basis/framework of form system

This commit is contained in:
zetaPRIME 2017-02-28 08:24:38 -05:00
parent 942732522b
commit 6ade2e8081
8 changed files with 243 additions and 5 deletions

View File

@ -20,6 +20,9 @@ using starlight::gfx::RenderCore;
using starlight::ui::TouchScreenCanvas; using starlight::ui::TouchScreenCanvas;
using starlight::ui::TopScreenCanvas; using starlight::ui::TopScreenCanvas;
using starlight::ui::Form;
using starlight::ui::FormFlags;
using starlight::Application; using starlight::Application;
//////////////////// ////////////////////
@ -69,6 +72,8 @@ void Application::_init() {
touchScreen = std::make_shared<TouchScreenCanvas>(); touchScreen = std::make_shared<TouchScreenCanvas>();
topScreen = std::make_shared<TopScreenCanvas>(); topScreen = std::make_shared<TopScreenCanvas>();
formTouchScreen = touchScreen.get();
formTopScreen = topScreen.get();
Init(); Init();
} }
@ -81,9 +86,38 @@ void Application::_end() {
} }
void Application::_mainLoop() { 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 // update step
InputManager::Update(); InputManager::Update();
Update(); Update();
for (auto it : forms) { // update loop for forms
it->Update(it == forms.back());
}
touchScreen->Update(); touchScreen->Update();
topScreen->Update(); topScreen->Update();
PostUpdate(); PostUpdate();

View File

@ -11,6 +11,8 @@
#include "starlight/ui/TouchScreenCanvas.h" #include "starlight/ui/TouchScreenCanvas.h"
#include "starlight/ui/TopScreenCanvas.h" #include "starlight/ui/TopScreenCanvas.h"
#include "starlight/ui/Form.h"
#include "starlight/ConfigManager.h" #include "starlight/ConfigManager.h"
namespace starlight { namespace starlight {
@ -25,12 +27,14 @@ namespace starlight {
static bool Quit(); static bool Quit();
static Config& GetConfig(const std::string& path); static Config& GetConfig(const std::string& path);
static std::string AppName(); static std::string AppName();
static inline Application* Current() { return _currentApp; }
////////////////////// //////////////////////
// INSTANCE MEMBERS // // INSTANCE MEMBERS //
////////////////////// //////////////////////
private: private:
bool _appQuit = false; bool _appQuit = false;
bool _sFormState = false;
void _init(); void _init();
void _mainLoop(); void _mainLoop();
void _end(); void _end();
@ -43,6 +47,10 @@ namespace starlight {
std::shared_ptr<ui::TouchScreenCanvas> touchScreen = nullptr; std::shared_ptr<ui::TouchScreenCanvas> touchScreen = nullptr;
std::shared_ptr<ui::TopScreenCanvas> topScreen = nullptr; std::shared_ptr<ui::TopScreenCanvas> topScreen = nullptr;
std::list<std::shared_ptr<ui::Form>> forms;
ui::UIContainer* formTouchScreen = nullptr;
ui::UIContainer* formTopScreen = nullptr;
Application() = delete; Application() = delete;
Application(std::string id) : appId(id) { } Application(std::string id) : appId(id) { }
virtual ~Application() = default; virtual ~Application() = default;
@ -55,5 +63,7 @@ namespace starlight {
virtual void Draw() { } virtual void Draw() { }
virtual void PostDraw() { } virtual void PostDraw() { }
virtual void End() { } virtual void End() { }
inline void SignalFormState() { _sFormState = true; }
}; };
} }

View File

@ -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<Form>& f1, std::shared_ptr<Form>& 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
}

View File

@ -0,0 +1,95 @@
#pragma once
#include "starlight/_global.h"
#include <memory>
#include <list>
#include <functional>
#include <type_traits>
#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<WhateverForm>
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<FormFlags>::type FormFlags_t;
class Form;
template<class F>
class FormCreator {
public:
template<typename ... Args>
static std::shared_ptr<F> New(Args... args) {
auto f = std::make_shared<F>(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<Form> {
////////////////////
// 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<Form>& f1, std::shared_ptr<Form>& f2);
//////////////////////
// INSTANCE MEMBERS //
//////////////////////
private:
unsigned int showCounter;
protected:
//
public:
int priority;
unsigned int flags;
std::shared_ptr<UIContainer> touchScreen = nullptr;
std::shared_ptr<UIContainer> 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<unsigned int>(f); }
inline void SetFlag(FormFlags f, bool b) {
auto ff = static_cast<unsigned int>(f);
flags &= ~ff; if (b) flags |= ff;
}
inline bool IsVisible() { return GetFlag(FormFlags::visible); }
};
}
}

View File

@ -47,8 +47,9 @@ void UIContainer::_Dive(std::function<bool(UIElement*)>& func, bool consumable,
finished = func(this) && consumable; finished = func(this) && consumable;
} }
void UIContainer::Add(std::shared_ptr<UIElement> elem) { void UIContainer::Add(std::shared_ptr<UIElement> elem, bool front) {
children.push_back(elem); if (front) children.push_front(elem);
else children.push_back(elem);
elem->parent = std::weak_ptr<UIContainer>(std::static_pointer_cast<UIContainer>(this->shared_from_this())); elem->parent = std::weak_ptr<UIContainer>(std::static_pointer_cast<UIContainer>(this->shared_from_this()));
MarkForRedraw(); MarkForRedraw();
} }
@ -66,6 +67,13 @@ void UIContainer::Remove(std::shared_ptr<UIElement> elem) {
MarkForRedraw(); MarkForRedraw();
} }
void UIContainer::RemoveAll() {
for (auto it : children) {
it->parent = std::weak_ptr<UIContainer>(); // clear parent
}
children.clear();
}
void UIContainer::Update() { void UIContainer::Update() {
for (auto& it : children) { it->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(); } for (auto& it : children) { if (it->rect.Overlaps(vr)) it->Draw(); }
GFXManager::PopOffset(); GFXManager::PopOffset();
} }

View File

@ -34,9 +34,10 @@ namespace starlight {
void Dive(std::function<bool(UIElement*)> check, std::function<bool(UIElement*)> func, bool consumable = true, bool frontFirst = true); void Dive(std::function<bool(UIElement*)> check, std::function<bool(UIElement*)> func, bool consumable = true, bool frontFirst = true);
void Dive(std::function<bool(UIElement*)> func, bool consumable = true, bool frontFirst = true); void Dive(std::function<bool(UIElement*)> func, bool consumable = true, bool frontFirst = true);
void Add(std::shared_ptr<UIElement> elem); void Add(std::shared_ptr<UIElement> elem, bool front = false);
//void Add(UIElement* elem); //void Add(UIElement* elem);
void Remove(std::shared_ptr<UIElement> elem); void Remove(std::shared_ptr<UIElement> elem);
void RemoveAll();
void Update() override; void Update() override;
void PreDraw() override; void PreDraw() override;

View File

@ -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 { today's agenda {
FSHelper, recursive directory assertion etc. FSHelper, recursive directory assertion etc.
} then { } then {

1
themes/default/about.txt Normal file
View File

@ -0,0 +1 @@
loosely styled after Vertex - https://www.gnome-look.org/p/1013757/