mirror of
https://github.com/zetaPRIME/libstarlight.git
synced 2025-06-25 21:22:46 +00:00
basis/framework of form system
This commit is contained in:
parent
942732522b
commit
6ade2e8081
@ -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<TouchScreenCanvas>();
|
||||
topScreen = std::make_shared<TopScreenCanvas>();
|
||||
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();
|
||||
|
@ -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<ui::TouchScreenCanvas> touchScreen = 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(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; }
|
||||
};
|
||||
}
|
||||
|
77
libstarlight/source/starlight/ui/Form.cpp
Normal file
77
libstarlight/source/starlight/ui/Form.cpp
Normal 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
|
||||
}
|
95
libstarlight/source/starlight/ui/Form.h
Normal file
95
libstarlight/source/starlight/ui/Form.h
Normal 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); }
|
||||
|
||||
};
|
||||
}
|
||||
}
|
@ -47,8 +47,9 @@ void UIContainer::_Dive(std::function<bool(UIElement*)>& func, bool consumable,
|
||||
finished = func(this) && consumable;
|
||||
}
|
||||
|
||||
void UIContainer::Add(std::shared_ptr<UIElement> elem) {
|
||||
children.push_back(elem);
|
||||
void UIContainer::Add(std::shared_ptr<UIElement> elem, bool front) {
|
||||
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()));
|
||||
MarkForRedraw();
|
||||
}
|
||||
@ -66,6 +67,13 @@ void UIContainer::Remove(std::shared_ptr<UIElement> elem) {
|
||||
MarkForRedraw();
|
||||
}
|
||||
|
||||
void UIContainer::RemoveAll() {
|
||||
for (auto it : children) {
|
||||
it->parent = std::weak_ptr<UIContainer>(); // 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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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*)> 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 Remove(std::shared_ptr<UIElement> elem);
|
||||
void RemoveAll();
|
||||
|
||||
void Update() override;
|
||||
void PreDraw() override;
|
||||
|
@ -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 {
|
||||
|
1
themes/default/about.txt
Normal file
1
themes/default/about.txt
Normal file
@ -0,0 +1 @@
|
||||
loosely styled after Vertex - https://www.gnome-look.org/p/1013757/
|
Loading…
x
Reference in New Issue
Block a user