From 6ade2e808163deb996f18f43631f636a7a9eb7eb Mon Sep 17 00:00:00 2001 From: zetaPRIME Date: Tue, 28 Feb 2017 08:24:38 -0500 Subject: [PATCH] basis/framework of form system --- libstarlight/source/starlight/Application.cpp | 34 +++++++ libstarlight/source/starlight/Application.h | 10 ++ libstarlight/source/starlight/ui/Form.cpp | 77 +++++++++++++++ libstarlight/source/starlight/ui/Form.h | 95 +++++++++++++++++++ .../source/starlight/ui/UIContainer.cpp | 14 ++- .../source/starlight/ui/UIContainer.h | 3 +- libstarlight/todo.txt | 14 +++ themes/default/about.txt | 1 + 8 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 libstarlight/source/starlight/ui/Form.cpp create mode 100644 libstarlight/source/starlight/ui/Form.h create mode 100644 themes/default/about.txt diff --git a/libstarlight/source/starlight/Application.cpp b/libstarlight/source/starlight/Application.cpp index 9d86785..e3758cc 100644 --- a/libstarlight/source/starlight/Application.cpp +++ b/libstarlight/source/starlight/Application.cpp @@ -20,6 +20,9 @@ using starlight::gfx::RenderCore; using starlight::ui::TouchScreenCanvas; using starlight::ui::TopScreenCanvas; +using starlight::ui::Form; +using starlight::ui::FormFlags; + using starlight::Application; //////////////////// @@ -69,6 +72,8 @@ void Application::_init() { touchScreen = std::make_shared(); topScreen = std::make_shared(); + formTouchScreen = touchScreen.get(); + formTopScreen = topScreen.get(); Init(); } @@ -81,9 +86,38 @@ void Application::_end() { } void Application::_mainLoop() { + if (!forms.empty()) { + if (_sFormState) { + _sFormState = false; + + // sort open forms + forms.sort(Form::OrderedCompare); + + // reconstruct ui container heirarchy + bool otouch, otop; + formTouchScreen->RemoveAll(); + formTopScreen->RemoveAll(); + + for (auto it = forms.rbegin(); it != forms.rend(); ++it) { + if ((*it)->IsVisible()) { + if (!otouch) formTouchScreen->Add((*it)->touchScreen, true); + if (!otop) formTopScreen->Add((*it)->topScreen, true); + if ((*it)->GetFlag(FormFlags::canOcclude)) { + if ((*it)->GetFlag(FormFlags::occludeTouch)) otouch = true; + if ((*it)->GetFlag(FormFlags::occludeTop)) otop = true; + } + } + } + // + } + } + // update step InputManager::Update(); Update(); + for (auto it : forms) { // update loop for forms + it->Update(it == forms.back()); + } touchScreen->Update(); topScreen->Update(); PostUpdate(); diff --git a/libstarlight/source/starlight/Application.h b/libstarlight/source/starlight/Application.h index 31415c2..6a07719 100644 --- a/libstarlight/source/starlight/Application.h +++ b/libstarlight/source/starlight/Application.h @@ -11,6 +11,8 @@ #include "starlight/ui/TouchScreenCanvas.h" #include "starlight/ui/TopScreenCanvas.h" +#include "starlight/ui/Form.h" + #include "starlight/ConfigManager.h" namespace starlight { @@ -25,12 +27,14 @@ namespace starlight { static bool Quit(); static Config& GetConfig(const std::string& path); static std::string AppName(); + static inline Application* Current() { return _currentApp; } ////////////////////// // INSTANCE MEMBERS // ////////////////////// private: bool _appQuit = false; + bool _sFormState = false; void _init(); void _mainLoop(); void _end(); @@ -43,6 +47,10 @@ namespace starlight { std::shared_ptr touchScreen = nullptr; std::shared_ptr topScreen = nullptr; + std::list> forms; + ui::UIContainer* formTouchScreen = nullptr; + ui::UIContainer* formTopScreen = nullptr; + Application() = delete; Application(std::string id) : appId(id) { } virtual ~Application() = default; @@ -55,5 +63,7 @@ namespace starlight { virtual void Draw() { } virtual void PostDraw() { } virtual void End() { } + + inline void SignalFormState() { _sFormState = true; } }; } diff --git a/libstarlight/source/starlight/ui/Form.cpp b/libstarlight/source/starlight/ui/Form.cpp new file mode 100644 index 0000000..de82983 --- /dev/null +++ b/libstarlight/source/starlight/ui/Form.cpp @@ -0,0 +1,77 @@ +#include "Form.h" + +#include <3ds.h> + +#include "starlight/GFXManager.h" +#include "starlight/ConfigManager.h" +#include "starlight/ThemeManager.h" +#include "starlight/InputManager.h" +#include "starlight/gfx/RenderCore.h" + +#include "starlight/Application.h" + +using std::string; + +using starlight::GFXManager; +using starlight::ConfigManager; +using starlight::Config; +using starlight::ThemeManager; +using starlight::InputManager; +using starlight::gfx::RenderCore; + +using starlight::ui::TouchScreenCanvas; +using starlight::ui::TopScreenCanvas; + +using starlight::Application; + +using starlight::ui::Form; + + //////////////////// + // STATIC MEMBERS // +//////////////////// + +bool Form::OrderedCompare(std::shared_ptr
& f1, std::shared_ptr& f2) { // acts as < operator + if (!f1->IsVisible()) return true; + if (f1->priority < f2->priority) return true; + if (f1->priority > f2->priority) return false; + return f1->showCounter < f2->showCounter; +} + + ////////////////////// + // INSTANCE MEMBERS // +////////////////////// + +void Form::Open() { + 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(); +} + +void Form::Close() { + auto app = Application::Current(); + if (app == nullptr) return; + app->forms.remove(shared_from_this()); + SetFlag(FormFlags::open, false); + app->SignalFormState(); +} + +void Form::Show() { + auto app = Application::Current(); + if (app == nullptr) return; + showCounter = nextShowCounter++; + SetFlag(FormFlags::visible, true); + app->SignalFormState(); + OnShow(); +} + +void Form::Hide() { + auto app = Application::Current(); + if (app == nullptr) return; + if (!GetFlag(FormFlags::visible)) return; // don't signal if already hidden + SetFlag(FormFlags::visible, false); + app->SignalFormState(); + OnHide(); // todo: make this interceptable +} diff --git a/libstarlight/source/starlight/ui/Form.h b/libstarlight/source/starlight/ui/Form.h new file mode 100644 index 0000000..46be1db --- /dev/null +++ b/libstarlight/source/starlight/ui/Form.h @@ -0,0 +1,95 @@ +#pragma once +#include "starlight/_global.h" + +#include +#include +#include +#include + +#include "starlight/datatypes/Vector2.h" +#include "starlight/datatypes/VRect.h" + +#include "starlight/ui/UIContainer.h" + +// Form subclasses should also derive from FormCreator, with their own typename as the template argument +// class WhateverForm : public Form, public FormCreator + +namespace starlight { + namespace ui { + enum class FormFlags : unsigned int { + visible = 1 << 0, + canOcclude = 1 << 1, + occludeTouch = 1 << 2, + occludeTop = 1 << 3, + + open = 1 << 30 + }; + typedef std::underlying_type::type FormFlags_t; + + class Form; + template + class FormCreator { + public: + template + static std::shared_ptr New(Args... args) { + auto f = std::make_shared(args...); + // ... hmm. do I "make live" here, or have a distinct Open call to insert it into the Application's list? + return f; + } + }; + + class Form : public std::enable_shared_from_this { + //////////////////// + // STATIC MEMBERS // + //////////////////// + private: + static unsigned int nextShowCounter; // you're not going to end up showing forms 4.3 billion times in a single session + + public: + static bool OrderedCompare(std::shared_ptr& f1, std::shared_ptr& f2); + + ////////////////////// + // INSTANCE MEMBERS // + ////////////////////// + private: + unsigned int showCounter; + + protected: + // + + public: + int priority; + unsigned int flags; + + std::shared_ptr touchScreen = nullptr; + std::shared_ptr topScreen = nullptr; + + Form() { } + virtual ~Form() { } + + void Open(); + void Close(); + void Show(); + void Hide(); + + //virtual void Init() { } + virtual void Update(bool focused) { } + //virtual void PostUpdate(bool focused) { } + //virtual void Draw() { } + //virtual void PostDraw() { } + //virtual void End() { } + virtual void OnShow() { } + virtual void OnHide() { } + + // convenience + inline bool GetFlag(FormFlags f) { return flags & static_cast(f); } + inline void SetFlag(FormFlags f, bool b) { + auto ff = static_cast(f); + flags &= ~ff; if (b) flags |= ff; + } + + inline bool IsVisible() { return GetFlag(FormFlags::visible); } + + }; + } +} diff --git a/libstarlight/source/starlight/ui/UIContainer.cpp b/libstarlight/source/starlight/ui/UIContainer.cpp index 8653a50..954b54c 100644 --- a/libstarlight/source/starlight/ui/UIContainer.cpp +++ b/libstarlight/source/starlight/ui/UIContainer.cpp @@ -47,8 +47,9 @@ void UIContainer::_Dive(std::function& func, bool consumable, finished = func(this) && consumable; } -void UIContainer::Add(std::shared_ptr elem) { - children.push_back(elem); +void UIContainer::Add(std::shared_ptr elem, bool front) { + if (front) children.push_front(elem); + else children.push_back(elem); elem->parent = std::weak_ptr(std::static_pointer_cast(this->shared_from_this())); MarkForRedraw(); } @@ -66,6 +67,13 @@ void UIContainer::Remove(std::shared_ptr elem) { MarkForRedraw(); } +void UIContainer::RemoveAll() { + for (auto it : children) { + it->parent = std::weak_ptr(); // clear parent + } + children.clear(); +} + void UIContainer::Update() { for (auto& it : children) { it->Update(); } } @@ -81,5 +89,3 @@ void UIContainer::Draw() { for (auto& it : children) { if (it->rect.Overlaps(vr)) it->Draw(); } GFXManager::PopOffset(); } - - diff --git a/libstarlight/source/starlight/ui/UIContainer.h b/libstarlight/source/starlight/ui/UIContainer.h index 9dbeeaf..bd26a4f 100644 --- a/libstarlight/source/starlight/ui/UIContainer.h +++ b/libstarlight/source/starlight/ui/UIContainer.h @@ -34,9 +34,10 @@ namespace starlight { void Dive(std::function check, std::function func, bool consumable = true, bool frontFirst = true); void Dive(std::function func, bool consumable = true, bool frontFirst = true); - void Add(std::shared_ptr elem); + void Add(std::shared_ptr elem, bool front = false); //void Add(UIElement* elem); void Remove(std::shared_ptr elem); + void RemoveAll(); void Update() override; void PreDraw() override; diff --git a/libstarlight/todo.txt b/libstarlight/todo.txt index b11c13b..9abc123 100644 --- a/libstarlight/todo.txt +++ b/libstarlight/todo.txt @@ -1,6 +1,20 @@ +form system { + capabilities { + updates, recieves events etc. as if it were a stackable Application + priority level (modals etc.) + can be stacked, reordered etc. via order shown + can occlude independently on top and bottom screen, causing lower-ordered things on that screen to not even render + 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 +} + + today's agenda { FSHelper, recursive directory assertion etc. } then { diff --git a/themes/default/about.txt b/themes/default/about.txt new file mode 100644 index 0000000..8a1e8b5 --- /dev/null +++ b/themes/default/about.txt @@ -0,0 +1 @@ +loosely styled after Vertex - https://www.gnome-look.org/p/1013757/ \ No newline at end of file