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

View File

@ -58,11 +58,11 @@ void InputManager::Update() {
touchLast = touchNow; touchLast = touchNow;
hidTouchRead(&tp); 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++; else touchTime++;
} }

View File

@ -10,38 +10,38 @@
// borrow this from ctrulib // borrow this from ctrulib
#ifndef BIT #ifndef BIT
#define BIT(n) (1U<<(n)) #define BIT(n) (1U<<(n))
enum { #endif
KEY_A = BIT(0), ///< A enum class Keys : unsigned int {
KEY_B = BIT(1), ///< B A = BIT(0), ///< A
KEY_SELECT = BIT(2), ///< Select B = BIT(1), ///< B
KEY_START = BIT(3), ///< Start SELECT = BIT(2), ///< Select
KEY_DRIGHT = BIT(4), ///< D-Pad Right START = BIT(3), ///< Start
KEY_DLEFT = BIT(5), ///< D-Pad Left DRIGHT = BIT(4), ///< D-Pad Right
KEY_DUP = BIT(6), ///< D-Pad Up DLEFT = BIT(5), ///< D-Pad Left
KEY_DDOWN = BIT(7), ///< D-Pad Down DUP = BIT(6), ///< D-Pad Up
KEY_R = BIT(8), ///< R DDOWN = BIT(7), ///< D-Pad Down
KEY_L = BIT(9), ///< L R = BIT(8), ///< R
KEY_X = BIT(10), ///< X L = BIT(9), ///< L
KEY_Y = BIT(11), ///< Y X = BIT(10), ///< X
KEY_ZL = BIT(14), ///< ZL (New 3DS only) Y = BIT(11), ///< Y
KEY_ZR = BIT(15), ///< ZR (New 3DS only) ZL = BIT(14), ///< ZL (New 3DS only)
KEY_TOUCH = BIT(20), ///< Touch (Not actually provided by HID) ZR = BIT(15), ///< ZR (New 3DS only)
KEY_CSTICK_RIGHT = BIT(24), ///< C-Stick Right (New 3DS only) TOUCH = BIT(20), ///< Touch (Not actually provided by HID)
KEY_CSTICK_LEFT = BIT(25), ///< C-Stick Left (New 3DS only) CSTICK_RIGHT = BIT(24), ///< C-Stick Right (New 3DS only)
KEY_CSTICK_UP = BIT(26), ///< C-Stick Up (New 3DS only) CSTICK_LEFT = BIT(25), ///< C-Stick Left (New 3DS only)
KEY_CSTICK_DOWN = BIT(27), ///< C-Stick Down (New 3DS only) CSTICK_UP = BIT(26), ///< C-Stick Up (New 3DS only)
KEY_CPAD_RIGHT = BIT(28), ///< Circle Pad Right CSTICK_DOWN = BIT(27), ///< C-Stick Down (New 3DS only)
KEY_CPAD_LEFT = BIT(29), ///< Circle Pad Left CPAD_RIGHT = BIT(28), ///< Circle Pad Right
KEY_CPAD_UP = BIT(30), ///< Circle Pad Up CPAD_LEFT = BIT(29), ///< Circle Pad Left
KEY_CPAD_DOWN = BIT(31), ///< Circle Pad Down CPAD_UP = BIT(30), ///< Circle Pad Up
CPAD_DOWN = BIT(31), ///< Circle Pad Down
// Generic catch-all directions // Generic catch-all directions
KEY_UP = KEY_DUP | KEY_CPAD_UP, ///< D-Pad Up or Circle Pad Up UP = DUP | CPAD_UP, ///< D-Pad Up or Circle Pad Up
KEY_DOWN = KEY_DDOWN | KEY_CPAD_DOWN, ///< D-Pad Down or Circle Pad Down DOWN = DDOWN | CPAD_DOWN, ///< D-Pad Down or Circle Pad Down
KEY_LEFT = KEY_DLEFT | KEY_CPAD_LEFT, ///< D-Pad Left or Circle Pad Left LEFT = DLEFT | CPAD_LEFT, ///< D-Pad Left or Circle Pad Left
KEY_RIGHT = KEY_DRIGHT | KEY_CPAD_RIGHT, ///< D-Pad Right or Circle Pad Right RIGHT = DRIGHT | CPAD_RIGHT, ///< D-Pad Right or Circle Pad Right
}; };
#endif
namespace starlight { namespace starlight {
class InputManager; class InputManager;
@ -87,8 +87,11 @@ namespace starlight {
static Vector2 CStick(); static Vector2 CStick();
static bool Held(unsigned int mask); 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 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 bool Released(unsigned int mask);
static inline bool Released(Keys mask) { return Released(static_cast<unsigned int>(mask)); }
static Vector2 TouchPos(); static Vector2 TouchPos();
static Vector2 TouchDelta(); 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 err(nth, wat) *((unsigned int*)0x00100000+(nth))=wat;
#define ded(wat) err(0,wat) #define ded(wat) err(0,wat)
Vector2 FontBMF::Measure(std::string& text, float scale, float maxWidth) { Vector2 FontBMF::Measure(std::string& text, float scale, float maxWidth) {
if (text == "") return Vector2::zero;
Vector2 v; Vector2 v;
PrintOp(Vector2(), text, scale, Color(), Vector2(), nullptr, maxWidth, &v, static_cast<DisplayList*>(nullptr)); PrintOp(Vector2(), text, scale, Color(), Vector2(), nullptr, maxWidth, &v, static_cast<DisplayList*>(nullptr));
return v; return v;
} }
void FontBMF::Print(Vector2 position, std::string& text, float scale, Color color, Vector2 justification, OptRef<Color> borderColor) { void FontBMF::Print(Vector2 position, std::string& text, float scale, Color color, Vector2 justification, OptRef<Color> borderColor) {
if (text == "") return;
if (GFXManager::PrepareForDrawing()) { if (GFXManager::PrepareForDrawing()) {
DisplayList dl = DisplayList(); DisplayList dl = DisplayList();
PrintOp(position, text, scale, color, justification, borderColor, 2147483647, static_cast<Vector2*>(nullptr), &dl); 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) { void FontBMF::Print(VRect rect, std::string& text, float scale, Color color, Vector2 justification, OptRef<Color> borderColor) {
if (text == "") return;
if (GFXManager::PrepareForDrawing()) { if (GFXManager::PrepareForDrawing()) {
if (borderColor && borderColor.get() != Color::transparent) rect = rect.Expand(-1, -1); if (borderColor && borderColor.get() != Color::transparent) rect = rect.Expand(-1, -1);
Vector2 pos = rect.pos + rect.size * justification; Vector2 pos = rect.pos + rect.size * justification;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -33,7 +33,7 @@ void TouchScreenCanvas::Update() {
// scan input // scan input
Vector2 tpos = InputManager::TouchPos(); Vector2 tpos = InputManager::TouchPos();
auto& drag = InputManager::GetDragHandle(); auto& drag = InputManager::GetDragHandle();
if (!drag.valid() && InputManager::Held(KEY_TOUCH)) { if (!drag.valid() && InputManager::Held(Keys::TOUCH)) {
Dive( Dive(
[&tpos](UIElement* e){ [&tpos](UIElement* e){
if (e->ScreenRect().Contains(tpos)) { if (e->ScreenRect().Contains(tpos)) {
@ -47,7 +47,7 @@ void TouchScreenCanvas::Update() {
}, true, true); }, true, true);
} else if (drag.valid()) { } else if (drag.valid()) {
UIElement* e = drag.get(); UIElement* e = drag.get();
if (InputManager::Held(KEY_TOUCH) && e != nullptr) { if (InputManager::Held(Keys::TOUCH) && e != nullptr) {
if (e->ScreenRect().Contains(tpos)) { if (e->ScreenRect().Contains(tpos)) {
touchedNow->insert({e, std::weak_ptr<UIElement>(e->shared_from_this())}); 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 { capabilities {
updates, recieves events etc. as if it were a stackable Application updates, recieves events etc. as if it were a stackable Application
priority level (modals etc.) 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 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 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. FSHelper, recursive directory assertion etc.
} then { } then {
change Label to move the rect instead of resizing to accomodate drops (add an offset thing to Font) 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) kill scroll velocity when hitting the edge (or rapid decel)
make scrollfield autofit a flag make scrollfield autofit a flag
make le scrollfield check threshold itself and only count directions it can actually scroll 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/Button.h"
#include "starlight/ui/Label.h" #include "starlight/ui/Label.h"
#include "starlight/dialog/MessageBox.h"
using starlight::Vector2; using starlight::Vector2;
using starlight::VRect; using starlight::VRect;
using starlight::Color; using starlight::Color;
@ -48,6 +50,18 @@ void Core::Init() {
auto button = std::make_shared<sl::ui::Button>(VRect(64,80,128,32)); auto button = std::make_shared<sl::ui::Button>(VRect(64,80,128,32));
button->SetText("I'm a button."); button->SetText("I'm a button.");
button->eOnTap = [label](auto& btn){ 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"); label->SetFont("default.16");
btn.SetText("I was pressed!"); btn.SetText("I was pressed!");
btn.eOnTap = [label](auto& btn){ 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)); auto label = std::make_shared<sl::ui::Label>(VRect(0,0,320,0));
label->autoSizeV = true; label->autoSizeV = true;
label->SetText("This is a form, coming in and nuking the non-form UI elements. Whoops."); 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->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() { 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