mirror of
https://github.com/zetaPRIME/libstarlight.git
synced 2025-06-25 21:22:46 +00:00
Application::GetTime(), InputManager improvements,
shiftkey and fast scroll for OSK, partial ThemeManager GC, and planning
This commit is contained in:
parent
7f27018808
commit
c6a5781cf8
@ -30,6 +30,7 @@ using starlight::Application;
|
||||
////////////////////
|
||||
|
||||
Application* Application::_currentApp = nullptr;
|
||||
unsigned long long Application::ftime = 0;
|
||||
|
||||
bool Application::Quit() {
|
||||
if (_currentApp == nullptr) return false;
|
||||
@ -124,6 +125,8 @@ void Application::_mainLoop() {
|
||||
}
|
||||
|
||||
// update step
|
||||
ftime = osGetTime();
|
||||
|
||||
InputManager::Update();
|
||||
Update();
|
||||
{ // update loop for forms, guarded from snap-outs
|
||||
|
@ -22,12 +22,14 @@ namespace starlight {
|
||||
////////////////////
|
||||
private:
|
||||
static Application* _currentApp;
|
||||
static unsigned long long ftime;
|
||||
|
||||
public:
|
||||
static bool Quit();
|
||||
static Config& GetConfig(const std::string& path);
|
||||
static std::string AppName();
|
||||
static inline Application* Current() { return _currentApp; }
|
||||
static inline unsigned long long GetTime() { return ftime; }
|
||||
|
||||
//////////////////////
|
||||
// INSTANCE MEMBERS //
|
||||
|
@ -43,6 +43,9 @@ enum class Keys : unsigned int {
|
||||
Right = DPadRight | CPadRight, ///< D-Pad Right or Circle Pad Right
|
||||
};
|
||||
|
||||
inline constexpr unsigned int operator*(Keys k) { return static_cast<unsigned int>(k); }
|
||||
inline constexpr Keys operator|(Keys k1, Keys k2) { return static_cast<Keys>(*k1 | *k2); }
|
||||
|
||||
namespace starlight {
|
||||
// forward declare this for OpenKeyboard
|
||||
namespace dialog {
|
||||
@ -94,11 +97,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 inline bool Held(Keys mask) { return Held(*mask); }
|
||||
static bool Pressed(unsigned int mask);
|
||||
static inline bool Pressed(Keys mask) { return Pressed(static_cast<unsigned int>(mask)); }
|
||||
static inline bool Pressed(Keys mask) { return Pressed(*mask); }
|
||||
static bool Released(unsigned int mask);
|
||||
static inline bool Released(Keys mask) { return Released(static_cast<unsigned int>(mask)); }
|
||||
static inline bool Released(Keys mask) { return Released(*mask); }
|
||||
|
||||
static Vector2 TouchPos();
|
||||
static Vector2 TouchDelta();
|
||||
|
@ -179,6 +179,14 @@ void ThemeManager::End() {
|
||||
|
||||
}
|
||||
|
||||
void ThemeManager::GC() {
|
||||
constexpr const int keepCycles = 5; // how many gc sweeps a drawable gets to stay loaded without being used
|
||||
// WIP
|
||||
for (auto& d : drawables) {
|
||||
if (++d.second.lastAccess > keepCycles) d.second.Unload();
|
||||
}
|
||||
}
|
||||
|
||||
ThemeRef<Drawable> ThemeManager::GetAsset(const std::string& name) {
|
||||
auto const& itr = drawables.find(name);
|
||||
if (itr == drawables.end()) {
|
||||
@ -195,10 +203,10 @@ ThemeRef<Font> ThemeManager::GetFont(const std::string& name) {
|
||||
|
||||
void ThemeManager::Fulfill(ThemeRefContainer<Drawable>& ref) {
|
||||
string path = ResolveAssetPath(ref.name);
|
||||
ref.ptr = LoadAsset(path);
|
||||
ref.ptr = LoadAsset(path, ref);
|
||||
}
|
||||
|
||||
shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) {
|
||||
shared_ptr<Drawable> ThemeManager::LoadAsset(string& path, ThemeRefContainer<Drawable>& ref) {
|
||||
static shared_ptr<Drawable> nulldrw = make_shared<starlight::gfx::DrawableTest>();
|
||||
|
||||
string ext = FindExtension(path);
|
||||
@ -225,9 +233,8 @@ shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) {
|
||||
// else if (type == "") { }
|
||||
else if (type == "link") {
|
||||
string npath = ResolveAssetPath(j["path"]);
|
||||
//return LoadAsset(npath);
|
||||
return GetAsset(npath).GetShared(); // I guess this works; may need to be altered for asynchronity if I do that later
|
||||
// (perhaps by--wait no, making it the same ThemeRefContainer would require a full rearchitecture of this part @.@)
|
||||
ref.redir = const_cast<ThemeRefContainer<Drawable>*>(GetAsset(npath).cptr); // link containers directly
|
||||
return nulldrw; // doesn't really matter what's inside, it'll never get used
|
||||
}
|
||||
return nulldrw;
|
||||
}
|
||||
|
@ -48,13 +48,15 @@ namespace starlight {
|
||||
static void Fulfill(gfx::ThemeRefContainer<gfx::Drawable>& ref);
|
||||
static void Fulfill(gfx::ThemeRefContainer<gfx::Font>& ref);
|
||||
|
||||
static std::shared_ptr<gfx::Drawable> LoadAsset(std::string& path);
|
||||
static std::shared_ptr<gfx::Drawable> LoadAsset(std::string& path, gfx::ThemeRefContainer<gfx::Drawable>& ref);
|
||||
public:
|
||||
ThemeManager() = delete; // "static" class
|
||||
|
||||
static void Init();
|
||||
static void End();
|
||||
|
||||
static void GC();
|
||||
|
||||
static gfx::ThemeRef<gfx::Drawable> GetAsset(const std::string& name);
|
||||
static gfx::ThemeRef<gfx::Font> GetFont(const std::string& name);
|
||||
|
||||
|
@ -29,7 +29,7 @@ namespace starlight {
|
||||
getdef = o.getdef;
|
||||
}
|
||||
|
||||
Optional<T>& operator=(const nullptr_t&) { p.reset(); }
|
||||
Optional<T>& operator=(const nullptr_t&) { p.reset(); return *this; }
|
||||
Optional<T>& operator=(const T& o) { // assign by type's assignment operator if passed a "value"
|
||||
if (!p) p = std::make_unique<T>();
|
||||
*p = o;
|
||||
|
@ -37,6 +37,10 @@ namespace {
|
||||
return tc;
|
||||
}
|
||||
|
||||
inline bool ShiftScroll(Keys k) {
|
||||
return InputManager::Pressed(k) || (InputManager::Held(Keys::L | Keys::R) && InputManager::Held(k));
|
||||
}
|
||||
|
||||
const constexpr float textHang = 4;
|
||||
}
|
||||
|
||||
@ -44,14 +48,13 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) {
|
||||
priority = 1000; // probably don't want all that much displaying above the keyboard
|
||||
handler->parent = this;
|
||||
|
||||
auto cover = std::make_shared<Image>(touchScreen->rect, "decorations/osk.background");
|
||||
auto cover = touchScreen->AddNew<Image>(touchScreen->rect, "decorations/osk.background");
|
||||
cover->blockTouch = true;
|
||||
touchScreen->Add(cover);
|
||||
|
||||
// wip
|
||||
// build keyboard
|
||||
|
||||
setContainer = std::make_shared<ui::UIContainer>(VRect::touchScreen);
|
||||
touchScreen->Add(setContainer);
|
||||
//setContainer = touchScreen->AddNew<ui::UIContainer>(VRect::touchScreen); // kept as a test case
|
||||
setContainer = touchScreen->AddNew<ui::UICanvas>(VRect::touchScreen); // but this is much more efficient
|
||||
|
||||
auto actSym = [this](Button& key){
|
||||
this->handler->InputSymbol(key.label);
|
||||
@ -74,18 +77,16 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) {
|
||||
bpen = bpstart + Vector2(linestart[line] * bs.x, bs.y * line);
|
||||
} else {
|
||||
// lower
|
||||
auto key = std::make_shared<Button>(VRect(bpen, bs));
|
||||
auto key = setContainer->AddNew<Button>(VRect(bpen, bs));
|
||||
if (c == ' ') key->rect.size.x *= 6;
|
||||
key->SetText(string(1, c));
|
||||
key->eOnTap = actSym;
|
||||
setContainer->Add(key);
|
||||
|
||||
// upper
|
||||
key = std::make_shared<Button>(VRect(bpen + Vector2(0, 1000), bs));
|
||||
key = setContainer->AddNew<Button>(VRect(bpen + Vector2(0, 1000), bs));
|
||||
if (C == ' ') key->rect.size.x *= 6;
|
||||
key->SetText(string(1, C));
|
||||
key->eOnTap = actSym;
|
||||
setContainer->Add(key);
|
||||
|
||||
// and after
|
||||
bpen.x += key->rect.size.x;
|
||||
@ -94,28 +95,30 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) {
|
||||
|
||||
// backspace
|
||||
bpen = bpstart + bs * Vector2(linestart[3] + 10, 3);
|
||||
auto key = std::make_shared<Button>(VRect(bpen, bs));
|
||||
auto key = touchScreen->AddNew<Button>(VRect(bpen, bs));
|
||||
key->rect.size.x *= 1.25;
|
||||
//key->SetText("< <");
|
||||
key->style.glyph = ThemeManager::GetAsset("glyphs/backspace.small");
|
||||
key->eOnTap = [this](auto& btn){ this->handler->Backspace(); this->OnKey(); };
|
||||
touchScreen->Add(key);
|
||||
|
||||
// enter
|
||||
bpen = bpstart + bs * Vector2(linestart[4] + 8, 4);
|
||||
key = std::make_shared<Button>(VRect(bpen, bs));
|
||||
key = touchScreen->AddNew<Button>(VRect(bpen, bs));
|
||||
key->rect.size.x *= 2.5;
|
||||
//key->SetText("Enter");
|
||||
key->style.glyph = ThemeManager::GetAsset("glyphs/enter.large");
|
||||
key->eOnTap = [this](auto& btn){ this->handler->Enter(); this->OnKey(); };
|
||||
touchScreen->Add(key);
|
||||
|
||||
previewSc = std::make_shared<ScrollField>(VRect(VRect::touchScreen.TopEdge(66)));
|
||||
touchScreen->Add(previewSc);
|
||||
// shift
|
||||
bpen = bpstart + bs * Vector2(linestart[0] + .25, 4);
|
||||
key = touchScreen->AddNew<Button>(VRect(bpen, bs));
|
||||
key->rect.size.x = bs.x * (linestart[4] - linestart[0] - .25);
|
||||
key->style.glyph = ThemeManager::GetAsset("glyphs/shift.large");
|
||||
key->eOnTap = [this](auto& btn){ this->shiftLock ^= true; };
|
||||
shiftKey = key;
|
||||
|
||||
preview = std::make_shared<DrawLayerProxy>(VRect::touchScreen.TopEdge(66).Expand(-2, 0), [this](auto& layer){ this->DrawPreview(layer); }, true);
|
||||
previewSc = touchScreen->AddNew<ScrollField>(VRect(VRect::touchScreen.TopEdge(66)));
|
||||
|
||||
preview = previewSc->AddNew<DrawLayerProxy>(VRect::touchScreen.TopEdge(66).Expand(-2, 0), [this](auto& layer){ this->DrawPreview(layer); }, true);
|
||||
preview->eOnTap = [this](auto& layer){ this->OnPreviewTap(layer); };
|
||||
previewSc->Add(preview);
|
||||
|
||||
RefreshPreview();
|
||||
}
|
||||
@ -126,46 +129,57 @@ void OSK::Update(bool focused) {
|
||||
return;
|
||||
}
|
||||
if (focused) {
|
||||
if (InputManager::Pressed(Keys::B)) handler->Done();
|
||||
if (InputManager::Pressed(Keys::B | Keys::Start)) handler->Done();
|
||||
if (handler->showPreview) {
|
||||
if (InputManager::Pressed(Keys::DPadLeft)) {
|
||||
auto& tc = PreviewTC();
|
||||
bool refresh = false;
|
||||
|
||||
if (ShiftScroll(Keys::DPadLeft)) {
|
||||
auto c = handler->GetCursor();
|
||||
if (c > 0) handler->SetCursor(c - 1);
|
||||
RefreshPreview();
|
||||
refresh = true;
|
||||
}
|
||||
if (InputManager::Pressed(Keys::DPadRight)) {
|
||||
if (ShiftScroll(Keys::DPadRight)) {
|
||||
handler->SetCursor(handler->GetCursor() + 1);
|
||||
RefreshPreview();
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
auto& tc = PreviewTC();
|
||||
if (InputManager::Pressed(Keys::DPadUp)) {
|
||||
if (ShiftScroll(Keys::DPadUp)) {
|
||||
Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor());
|
||||
pt.y -= tc.Measure("|").y * 0.5f;
|
||||
handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt));
|
||||
RefreshPreview();
|
||||
refresh = true;
|
||||
}
|
||||
if (InputManager::Pressed(Keys::DPadDown)) {
|
||||
if (ShiftScroll(Keys::DPadDown)) {
|
||||
Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor());
|
||||
pt.y += tc.Measure("|").y * 1.5f;
|
||||
handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt));
|
||||
RefreshPreview();
|
||||
refresh = true;
|
||||
}
|
||||
if (refresh) RefreshPreview();
|
||||
}
|
||||
|
||||
float& s = setContainer->scrollOffset.y;
|
||||
float ts = 0;
|
||||
if (InputManager::Held(Keys::L) || InputManager::Held(Keys::R)) {
|
||||
ts = 1000;
|
||||
}
|
||||
shiftLock = false;
|
||||
} else if (shiftLock) ts = 1000;
|
||||
if (s != ts) {
|
||||
s = ts;
|
||||
setContainer->MarkForRedraw();
|
||||
|
||||
if (ts > 0) {
|
||||
static TextConfig stc = ThemeManager::GetMetric<starlight::TextConfig>("/dialogs/OSK/keyHighlight");
|
||||
shiftKey->style.textConfig = stc;
|
||||
} else {
|
||||
shiftKey->style.textConfig = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OSK::OnKey() {
|
||||
shiftLock = false;
|
||||
RefreshPreview();
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "starlight/ui/Form.h"
|
||||
#include "starlight/ui/Button.h"
|
||||
#include "starlight/ui/ScrollField.h"
|
||||
#include "starlight/ui/DrawLayerProxy.h"
|
||||
|
||||
@ -19,7 +20,10 @@ namespace starlight {
|
||||
std::shared_ptr<ui::ScrollField> previewSc;
|
||||
std::shared_ptr<ui::DrawLayerProxy> preview;
|
||||
|
||||
std::shared_ptr<ui::Button> shiftKey;
|
||||
|
||||
//Vector2 cursorPos;
|
||||
bool shiftLock = false;
|
||||
|
||||
public:
|
||||
std::unique_ptr<osk::InputHandler> handler;
|
||||
|
@ -16,21 +16,29 @@ namespace starlight {
|
||||
protected:
|
||||
const std::string name;
|
||||
std::shared_ptr<T> ptr = nullptr;
|
||||
void Unload() {
|
||||
ThemeRefContainer* redir = nullptr;
|
||||
unsigned int lastAccess = 0; // how many gc sweeps since last use
|
||||
void Unload(bool full = false) {
|
||||
ptr.reset();
|
||||
if (full) redir = nullptr;
|
||||
}
|
||||
|
||||
ThemeRefContainer(std::string name, std::shared_ptr<T> ptr) : name(name), ptr(ptr) { }
|
||||
ThemeRefContainer(std::string name, T* ptr) : name(name), ptr(ptr) { }
|
||||
ThemeRefContainer(std::string name) : name(name) { }
|
||||
|
||||
inline std::shared_ptr<T>& _getptr() {
|
||||
lastAccess = 0;
|
||||
if (!redir && !ptr) ThemeManager::Fulfill(*this); // call thememanager to grab things
|
||||
if (redir) return redir->_getptr();
|
||||
return ptr;
|
||||
}
|
||||
inline std::shared_ptr<T>& getptr() const { return const_cast<ThemeRefContainer<T>&>(*this)._getptr(); }
|
||||
public:
|
||||
~ThemeRefContainer() { }
|
||||
|
||||
T* operator ->() const {
|
||||
if (ptr == nullptr) {
|
||||
ThemeManager::Fulfill(const_cast<ThemeRefContainer<T>&>(*this)); // call thememanager to grab things
|
||||
}
|
||||
return &*ptr;
|
||||
return &*(getptr());
|
||||
}
|
||||
|
||||
/*T& operator *() const {
|
||||
@ -44,7 +52,8 @@ namespace starlight {
|
||||
|
||||
template <class T>
|
||||
class ThemeRef {
|
||||
private:
|
||||
friend class starlight::ThemeManager;
|
||||
protected:
|
||||
const ThemeRefContainer<T>* cptr;
|
||||
public:
|
||||
ThemeRef() : cptr(nullptr) { }
|
||||
@ -52,7 +61,7 @@ namespace starlight {
|
||||
~ThemeRef() { }
|
||||
inline const ThemeRefContainer<T>& operator ->() const { return *cptr; }
|
||||
inline explicit operator bool() const { return cptr != nullptr; }
|
||||
inline std::shared_ptr<T> GetShared() const { return (*cptr).ptr; }
|
||||
inline std::shared_ptr<T> GetShared() const { return (*cptr).getptr(); }
|
||||
inline const std::string& GetName() const { return (*cptr).name; }
|
||||
};
|
||||
}
|
||||
|
@ -2,22 +2,26 @@
|
||||
|
||||
|
||||
roadmap to v0.5.1 {
|
||||
- add customization for Button (alternate idle/press images, optional glyph drawable) {
|
||||
- also add (optional) TextConfig
|
||||
- use that to spice up the OSK
|
||||
}
|
||||
figure out what to put on the left side of the keyboard (opposite backspace and enter)
|
||||
- fix the hang on osk when pressing (L|R)+up+left
|
||||
figure out what (else) to put on the left side of the keyboard (opposite backspace and enter)
|
||||
temporary drawable loading, local themeref, discard etc.
|
||||
^ both png and raw load
|
||||
maybe rgb565 for smdh icon loading?
|
||||
some examples (minesweeper?)
|
||||
proper thread dispatch?
|
||||
fix `, ' and " glyph spacing/offset
|
||||
adjust /\ some?
|
||||
proper thread dispatch? {
|
||||
Application main loop keeps a libctru TickCounter and keeps track of frame time;
|
||||
thread objects are held in a std::list, Application dispatches resume events and splice()s them to the end until frame time reaches some proportion of 1/60s
|
||||
thread gets a yield function that calls svcWaitSynchronization on its resume event
|
||||
...and some mechanism for allowing it to opt out of the rest of the cycle
|
||||
}
|
||||
} then consider these before 1.0 "gold" {
|
||||
make closing forms a bit less finicky (add them to a separate list and let the Application remove them from the list)
|
||||
garbage collection for not-recently-used theme assets {
|
||||
keep track of last-use in ThemeRefContainer
|
||||
- keep track of last-use in ThemeRefContainer
|
||||
have ThemeManager sweep gc every so often
|
||||
rework redirects (proxy drawable I guess...?)
|
||||
- rework redirects (proxy drawable I guess...?) or a pointer to another container
|
||||
}
|
||||
HANDLE CANVAS OVERRUNS FOR LABELS AND OSK PREVIEW {
|
||||
- well, it doesn't actually *crash* anymore... or at least nowhere near as fast
|
||||
@ -26,6 +30,7 @@ roadmap to v0.5.1 {
|
||||
}
|
||||
actual cursor image for OSK instead of just using a | glypyh
|
||||
input prompt dialog
|
||||
make the panel background not just the button image
|
||||
"shortcut" overloads for InputManager::OpenKeyboard
|
||||
language config and atlas support
|
||||
maybe implement some way of "knocking out" and replacing metrics during runtime for theme switching
|
||||
|
@ -44,7 +44,7 @@ void Core::Init() {
|
||||
auto label = std::make_shared<sl::ui::Label>(VRect(0,0,320,0));
|
||||
label->textConfig->justification = Vector2::half;
|
||||
label->autoSizeV = true;
|
||||
label->SetText("~libstarlight UI test~\n\nHello. I'm a label.\nI have multiple lines and can resize to fit my content. Did you know that miles per gallon is actually a measure of volume? " + std::to_string(sizeof(std::unique_ptr<sl::ui::Label>)));
|
||||
label->SetText("~libstarlight UI test~\n\nHello. I'm a label.\nI have multiple lines and can resize to fit my content. Did you know that miles per gallon is actually a measure of volume? " + std::to_string(sizeof(unsigned long long)));
|
||||
container->Add(label);
|
||||
|
||||
auto button = std::make_shared<sl::ui::Button>(VRect(64,80,128,32));
|
||||
|
BIN
themes/default/glyphs/shift.large.png
Normal file
BIN
themes/default/glyphs/shift.large.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 250 B |
BIN
themes/default/glyphs/shift.small.png
Normal file
BIN
themes/default/glyphs/shift.small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 234 B |
BIN
themes/default/glyphs/shift.xcf
Normal file
BIN
themes/default/glyphs/shift.xcf
Normal file
Binary file not shown.
@ -55,6 +55,10 @@
|
||||
"textColor" : "midGray",
|
||||
"borderColor" : "darkGray",
|
||||
"justification" : [0.5, 0.5]
|
||||
},
|
||||
"keyHighlight" : {
|
||||
"_inherit" : "/controls/button/text",
|
||||
"textColor" : [0.75, 0.825, 1]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user