bunch of form and UI fixes, modal dialogs!

This commit is contained in:
zetaPRIME 2017-03-01 07:06:41 -05:00
parent 10ac977cb1
commit 67ccd0a055
16 changed files with 214 additions and 54 deletions

View File

@ -81,6 +81,8 @@ void Application::_init() {
void Application::_end() {
End();
forms.clear(); // not sure why, but not doing this results in a data abort if any forms are active
RenderCore::Close();
ConfigManager::End();
}
@ -115,8 +117,13 @@ void Application::_mainLoop() {
// update step
InputManager::Update();
Update();
for (auto it : forms) { // update loop for forms
it->Update(it == forms.back());
{ // update loop for forms, guarded from snap-outs
auto it = forms.begin();
while (it != forms.end()) {
auto next = std::next(it);
(*it)->Update(*it == forms.back());
it = next;
}
}
touchScreen->Update();
topScreen->Update();

View File

@ -58,11 +58,11 @@ void InputManager::Update() {
touchLast = touchNow;
hidTouchRead(&tp);
if (Held(KEY_TOUCH)) touchNow = Vector2(tp.px, tp.py);
if (Held(Keys::TOUCH)) touchNow = Vector2(tp.px, tp.py);
if (Pressed(KEY_TOUCH)) touchStart = touchLast = touchNow;
if (Pressed(Keys::TOUCH)) touchStart = touchLast = touchNow;
if (!Held(KEY_TOUCH) && !Released(KEY_TOUCH)) touchTime = 0;
if (!Held(Keys::TOUCH) && !Released(Keys::TOUCH)) touchTime = 0;
else touchTime++;
}
@ -119,4 +119,4 @@ void DragHandle::Release() {
rptr = nullptr;
wptr = std::shared_ptr<UIElement>(nullptr);
e->OnDragRelease();
}
}

View File

@ -10,38 +10,38 @@
// borrow this from ctrulib
#ifndef BIT
#define BIT(n) (1U<<(n))
enum {
KEY_A = BIT(0), ///< A
KEY_B = BIT(1), ///< B
KEY_SELECT = BIT(2), ///< Select
KEY_START = BIT(3), ///< Start
KEY_DRIGHT = BIT(4), ///< D-Pad Right
KEY_DLEFT = BIT(5), ///< D-Pad Left
KEY_DUP = BIT(6), ///< D-Pad Up
KEY_DDOWN = BIT(7), ///< D-Pad Down
KEY_R = BIT(8), ///< R
KEY_L = BIT(9), ///< L
KEY_X = BIT(10), ///< X
KEY_Y = BIT(11), ///< Y
KEY_ZL = BIT(14), ///< ZL (New 3DS only)
KEY_ZR = BIT(15), ///< ZR (New 3DS only)
KEY_TOUCH = BIT(20), ///< Touch (Not actually provided by HID)
KEY_CSTICK_RIGHT = BIT(24), ///< C-Stick Right (New 3DS only)
KEY_CSTICK_LEFT = BIT(25), ///< C-Stick Left (New 3DS only)
KEY_CSTICK_UP = BIT(26), ///< C-Stick Up (New 3DS only)
KEY_CSTICK_DOWN = BIT(27), ///< C-Stick Down (New 3DS only)
KEY_CPAD_RIGHT = BIT(28), ///< Circle Pad Right
KEY_CPAD_LEFT = BIT(29), ///< Circle Pad Left
KEY_CPAD_UP = BIT(30), ///< Circle Pad Up
KEY_CPAD_DOWN = BIT(31), ///< Circle Pad Down
#endif
enum class Keys : unsigned int {
A = BIT(0), ///< A
B = BIT(1), ///< B
SELECT = BIT(2), ///< Select
START = BIT(3), ///< Start
DRIGHT = BIT(4), ///< D-Pad Right
DLEFT = BIT(5), ///< D-Pad Left
DUP = BIT(6), ///< D-Pad Up
DDOWN = BIT(7), ///< D-Pad Down
R = BIT(8), ///< R
L = BIT(9), ///< L
X = BIT(10), ///< X
Y = BIT(11), ///< Y
ZL = BIT(14), ///< ZL (New 3DS only)
ZR = BIT(15), ///< ZR (New 3DS only)
TOUCH = BIT(20), ///< Touch (Not actually provided by HID)
CSTICK_RIGHT = BIT(24), ///< C-Stick Right (New 3DS only)
CSTICK_LEFT = BIT(25), ///< C-Stick Left (New 3DS only)
CSTICK_UP = BIT(26), ///< C-Stick Up (New 3DS only)
CSTICK_DOWN = BIT(27), ///< C-Stick Down (New 3DS only)
CPAD_RIGHT = BIT(28), ///< Circle Pad Right
CPAD_LEFT = BIT(29), ///< Circle Pad Left
CPAD_UP = BIT(30), ///< Circle Pad Up
CPAD_DOWN = BIT(31), ///< Circle Pad Down
// Generic catch-all directions
KEY_UP = KEY_DUP | KEY_CPAD_UP, ///< D-Pad Up or Circle Pad Up
KEY_DOWN = KEY_DDOWN | KEY_CPAD_DOWN, ///< D-Pad Down or Circle Pad Down
KEY_LEFT = KEY_DLEFT | KEY_CPAD_LEFT, ///< D-Pad Left or Circle Pad Left
KEY_RIGHT = KEY_DRIGHT | KEY_CPAD_RIGHT, ///< D-Pad Right or Circle Pad Right
UP = DUP | CPAD_UP, ///< D-Pad Up or Circle Pad Up
DOWN = DDOWN | CPAD_DOWN, ///< D-Pad Down or Circle Pad Down
LEFT = DLEFT | CPAD_LEFT, ///< D-Pad Left or Circle Pad Left
RIGHT = DRIGHT | CPAD_RIGHT, ///< D-Pad Right or Circle Pad Right
};
#endif
namespace starlight {
class InputManager;
@ -87,8 +87,11 @@ namespace starlight {
static Vector2 CStick();
static bool Held(unsigned int mask);
static inline bool Held(Keys mask) { return Held(static_cast<unsigned int>(mask)); }
static bool Pressed(unsigned int mask);
static inline bool Pressed(Keys mask) { return Pressed(static_cast<unsigned int>(mask)); }
static bool Released(unsigned int mask);
static inline bool Released(Keys mask) { return Released(static_cast<unsigned int>(mask)); }
static Vector2 TouchPos();
static Vector2 TouchDelta();

View File

@ -0,0 +1,92 @@
#include "MessageBox.h"
#include "starlight/InputManager.h"
#include "starlight/ui/Image.h"
#include "starlight/ui/Button.h"
#include "starlight/ui/Label.h"
#include "starlight/ui/ScrollField.h"
using starlight::InputManager;
using starlight::ui::Image;
using starlight::ui::Button;
using starlight::ui::Label;
using starlight::ui::ScrollField;
using starlight::ui::Form;
using starlight::dialog::MessageBox;
MessageBox::MessageBox(Mode m, const std::string& msg, std::function<void(int)> onSelect) : Form(true) {
priority = 10;
eOnSelect = onSelect;
VRect boxArea = VRect(160, 120, 0, 0).Expand(Vector2(300, 200)*.5);
auto bg = std::make_shared<Image>(boxArea, "decorations/panel.bg");
touchScreen->Add(bg);
auto scroll = std::make_shared<ScrollField>(boxArea.Expand(-8, -8).TopEdge(200-16-8-32));
touchScreen->Add(scroll);
auto label = std::make_shared<Label>(VRect(0, 0, 300-16, 0));
label->autoSizeV = true;
label->SetFont("default.16");
label->SetText(msg);
scroll->Add(label);
VRect buttonArea = boxArea.Expand(-8, -8).BottomEdge(32);
switch (m) {
case Ok: { // one button
numButtons = 1;
auto b = std::make_shared<Button>(buttonArea);
b->SetText("OK");
b->eOnTap = [this](auto& b){ this->OnSelect(0); };
touchScreen->Add(b);
} break;
case OkCancel:
case YesNo: { // two buttons
numButtons = 2;
auto b1 = std::make_shared<Button>(buttonArea.LeftEdge(buttonArea.size.x / 2 - 4));
b1->eOnTap = [this](auto& b){ this->OnSelect(0); };
touchScreen->Add(b1);
auto b2 = std::make_shared<Button>(buttonArea.RightEdge(buttonArea.size.x / 2 - 4));
b2->eOnTap = [this](auto& b){ this->OnSelect(1); };
touchScreen->Add(b2);
switch(m) { // labeling
case OkCancel: {
b1->SetText("OK");
b2->SetText("Cancel");
} break;
case YesNo: {
b1->SetText("Yes");
b2->SetText("No");
} break;
default: {
b1->SetText("You forgot to");
b2->SetText("implement this");
} break;
}
}
default: break;
}
}
void MessageBox::Update(bool focused) {
if (focused) {
if (InputManager::Pressed(Keys::A)) OnSelect(0);
else if (InputManager::Pressed(Keys::B) && numButtons > 1) OnSelect(numButtons - 1);
}
}
void MessageBox::OnSelect(int buttonId) {
if (eOnSelect) eOnSelect(buttonId);
Close();
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "starlight/_global.h"
#include <string>
#include <functional>
#include "starlight/ui/Form.h"
namespace starlight {
namespace dialog {
class MessageBox : public ui::Form, public ui::FormCreator<MessageBox> {
private:
//
public:
enum Mode {
Ok, OkCancel, YesNo
};
std::function<void(int)> eOnSelect;
int numButtons = 0;
MessageBox(Mode m, const std::string& msg, std::function<void(int)> onSelect = {});
~MessageBox() override { };
void Update(bool focused) override;
void OnSelect(int buttonId);
};
}
}

View File

@ -17,12 +17,14 @@ using starlight::gfx::FontBMF;
#define err(nth, wat) *((unsigned int*)0x00100000+(nth))=wat;
#define ded(wat) err(0,wat)
Vector2 FontBMF::Measure(std::string& text, float scale, float maxWidth) {
if (text == "") return Vector2::zero;
Vector2 v;
PrintOp(Vector2(), text, scale, Color(), Vector2(), nullptr, maxWidth, &v, static_cast<DisplayList*>(nullptr));
return v;
}
void FontBMF::Print(Vector2 position, std::string& text, float scale, Color color, Vector2 justification, OptRef<Color> borderColor) {
if (text == "") return;
if (GFXManager::PrepareForDrawing()) {
DisplayList dl = DisplayList();
PrintOp(position, text, scale, color, justification, borderColor, 2147483647, static_cast<Vector2*>(nullptr), &dl);
@ -36,6 +38,7 @@ void FontBMF::Print(Vector2 position, std::string& text, float scale, Color colo
}
void FontBMF::Print(VRect rect, std::string& text, float scale, Color color, Vector2 justification, OptRef<Color> borderColor) {
if (text == "") return;
if (GFXManager::PrepareForDrawing()) {
if (borderColor && borderColor.get() != Color::transparent) rect = rect.Expand(-1, -1);
Vector2 pos = rect.pos + rect.size * justification;

View File

@ -38,7 +38,7 @@ void Button::Draw() {
}
void Button::OnTouchOn() {
if (InputManager::Pressed(KEY_TOUCH)) {
if (InputManager::Pressed(Keys::TOUCH)) {
InputManager::GetDragHandle().Grab(this);
MarkForRedraw();
}
@ -60,7 +60,7 @@ void Button::OnDragHold() {
}
void Button::OnDragRelease() {
if (InputManager::Released(KEY_TOUCH)) {
if (InputManager::Released(Keys::TOUCH)) {
if (eOnTap) eOnTap(*this);
}
MarkForRedraw();

View File

@ -13,7 +13,7 @@ namespace starlight {
//
public:
std::string label;
std::string label = "";
std::function<void(Button&)> eOnTap;
Button(VRect rect) { this->rect = rect; }
@ -35,4 +35,3 @@ namespace starlight {
};
}
}

View File

@ -47,16 +47,18 @@ Form::Form(bool useDefaults) {
if (useDefaults) {
touchScreen = std::make_shared<UIContainer>(VRect(0, 0, 320, 240));
topScreen = std::make_shared<UIContainer>(VRect(0, 0, 400, 240));
SetFlag(FormFlags::canOcclude, true);
}
}
void Form::Open() {
void Form::Open(bool showImmediately) {
auto app = Application::Current();
if (app == nullptr) return;
if (GetFlag(FormFlags::open)) return;
app->forms.push_back(shared_from_this());
SetFlag(FormFlags::open, true);
app->SignalFormState();
if (showImmediately) Show();
}
void Form::Close() {

View File

@ -68,7 +68,7 @@ namespace starlight {
Form(bool useDefaults);
virtual ~Form() { }
void Open();
void Open(bool showImmediately = true);
void Close();
void Show();
void Hide();

View File

@ -39,11 +39,11 @@ void ScrollField::Update() {
}
void ScrollField::OnProcessTouchEvent() { // stop when child element touched
if (InputManager::Pressed(KEY_TOUCH)) scrollVel = Vector2::zero;
if (InputManager::Pressed(Keys::TOUCH)) scrollVel = Vector2::zero;
}
void ScrollField::OnTouchOn() {
if (InputManager::Pressed(KEY_TOUCH)) {
if (InputManager::Pressed(Keys::TOUCH)) {
InputManager::GetDragHandle().Grab(this);
}
}
@ -67,9 +67,8 @@ void ScrollField::OnDragHold() {
}
void ScrollField::OnDragRelease() {
if (InputManager::Released(KEY_TOUCH)) {
if (InputManager::Released(Keys::TOUCH)) {
if (scrollPreVel.Length() < InputManager::flingThreshold) scrollPreVel = Vector2::zero;
scrollVel = scrollPreVel;
}
}

View File

@ -33,7 +33,7 @@ void TouchScreenCanvas::Update() {
// scan input
Vector2 tpos = InputManager::TouchPos();
auto& drag = InputManager::GetDragHandle();
if (!drag.valid() && InputManager::Held(KEY_TOUCH)) {
if (!drag.valid() && InputManager::Held(Keys::TOUCH)) {
Dive(
[&tpos](UIElement* e){
if (e->ScreenRect().Contains(tpos)) {
@ -47,7 +47,7 @@ void TouchScreenCanvas::Update() {
}, true, true);
} else if (drag.valid()) {
UIElement* e = drag.get();
if (InputManager::Held(KEY_TOUCH) && e != nullptr) {
if (InputManager::Held(Keys::TOUCH) && e != nullptr) {
if (e->ScreenRect().Contains(tpos)) {
touchedNow->insert({e, std::weak_ptr<UIElement>(e->shared_from_this())});
}

View File

@ -1,7 +1,7 @@
form system {
form system { framework done apart from some small api changes
capabilities {
updates, recieves events etc. as if it were a stackable Application
priority level (modals etc.)
@ -10,7 +10,6 @@ form system {
can tell if it's the focused (topmost) form and use that to determine whether to accept button input
}
hmm. implement as direct replacement of Application UI contents, or implement as its own container...?
meaningful state change signals current Application to resort and rebuild draw list during next update
}
@ -19,7 +18,6 @@ today's agenda {
FSHelper, recursive directory assertion etc.
} then {
change Label to move the rect instead of resizing to accomodate drops (add an offset thing to Font)
figure out how I want to do separate forms???
kill scroll velocity when hitting the edge (or rapid decel)
make scrollfield autofit a flag
make le scrollfield check threshold itself and only count directions it can actually scroll

View File

@ -17,6 +17,8 @@
#include "starlight/ui/Button.h"
#include "starlight/ui/Label.h"
#include "starlight/dialog/MessageBox.h"
using starlight::Vector2;
using starlight::VRect;
using starlight::Color;
@ -48,6 +50,18 @@ void Core::Init() {
auto button = std::make_shared<sl::ui::Button>(VRect(64,80,128,32));
button->SetText("I'm a button.");
button->eOnTap = [label](auto& btn){
auto form = std::make_shared<sl::ui::Form>(true);
auto label = std::make_shared<sl::ui::Label>(VRect(0,0,320,0));
label->autoSizeV = true;
label->SetText("This is a form, coming in and nuking the non-form UI elements. Whoops.");
form->touchScreen->Add(label);
auto xbtn = std::make_shared<sl::ui::Button>(VRect(0,0,32,32));
xbtn->SetText(" ");
form->touchScreen->Add(xbtn);
form->Open();
auto msg = sl::dialog::MessageBox::New(sl::dialog::MessageBox::OkCancel, "This is a modal dialog!\n\n\n\nScrolly\n\n\n\nscrolly\n\n\n\nscrolly\n\n\n\nscrolly\n\n\n\nscroll!");
msg->Open();
label->SetFont("default.16");
btn.SetText("I was pressed!");
btn.eOnTap = [label](auto& btn){
@ -62,10 +76,18 @@ void Core::Init() {
auto label = std::make_shared<sl::ui::Label>(VRect(0,0,320,0));
label->autoSizeV = true;
label->SetText("This is a form, coming in and nuking the non-form UI elements. Whoops.");
form->topScreen->Add(label);
form->touchScreen->Add(label);
form->Open();
form->Show();
if (!form->GetFlag(sl::ui::FormFlags::open)) btn.SetText("fuck.");
form = std::make_shared<sl::ui::Form>(true);
auto xbtn = std::make_shared<sl::ui::Button>(VRect(32,32,128,32));
form->touchScreen->Add(xbtn);
form->Open();
xbtn->eOnTap = [form](auto& btn){
//form->Close();
auto msg = sl::dialog::MessageBox::New(sl::dialog::MessageBox::Ok, "This is a modal dialog!\n\n\n\nScrolly\n\n\n\nscrolly\n\n\n\nscrolly\n\n\n\nscrolly\n\n\n\nscroll!");
msg->Open();
};
};
};
};
@ -103,5 +125,5 @@ void Core::End() {
}
void Core::Update() {
if (InputManager::Held(KEY_Y) || InputManager::Pressed(KEY_START)) Application::Quit();
if (InputManager::Held(Keys::Y) || InputManager::Pressed(Keys::START)) Application::Quit();
}

View File

@ -0,0 +1,4 @@
{
"assetType" : "ninepatch",
"margin" : [3, 3]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B