Application::GetTime(), InputManager improvements,

shiftkey and fast scroll for OSK, partial ThemeManager GC,
and planning
This commit is contained in:
zetaPRIME 2017-03-21 01:28:13 -04:00
parent 7f27018808
commit c6a5781cf8
15 changed files with 110 additions and 57 deletions

View File

@ -30,6 +30,7 @@ using starlight::Application;
//////////////////// ////////////////////
Application* Application::_currentApp = nullptr; Application* Application::_currentApp = nullptr;
unsigned long long Application::ftime = 0;
bool Application::Quit() { bool Application::Quit() {
if (_currentApp == nullptr) return false; if (_currentApp == nullptr) return false;
@ -124,6 +125,8 @@ void Application::_mainLoop() {
} }
// update step // update step
ftime = osGetTime();
InputManager::Update(); InputManager::Update();
Update(); Update();
{ // update loop for forms, guarded from snap-outs { // update loop for forms, guarded from snap-outs

View File

@ -22,12 +22,14 @@ namespace starlight {
//////////////////// ////////////////////
private: private:
static Application* _currentApp; static Application* _currentApp;
static unsigned long long ftime;
public: public:
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; } static inline Application* Current() { return _currentApp; }
static inline unsigned long long GetTime() { return ftime; }
////////////////////// //////////////////////
// INSTANCE MEMBERS // // INSTANCE MEMBERS //

View File

@ -43,6 +43,9 @@ enum class Keys : unsigned int {
Right = DPadRight | CPadRight, ///< D-Pad Right or Circle Pad Right 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 { namespace starlight {
// forward declare this for OpenKeyboard // forward declare this for OpenKeyboard
namespace dialog { namespace dialog {
@ -94,11 +97,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 inline bool Held(Keys mask) { return Held(*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 inline bool Pressed(Keys mask) { return Pressed(*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 inline bool Released(Keys mask) { return Released(*mask); }
static Vector2 TouchPos(); static Vector2 TouchPos();
static Vector2 TouchDelta(); static Vector2 TouchDelta();

View File

@ -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) { ThemeRef<Drawable> ThemeManager::GetAsset(const std::string& name) {
auto const& itr = drawables.find(name); auto const& itr = drawables.find(name);
if (itr == drawables.end()) { if (itr == drawables.end()) {
@ -195,10 +203,10 @@ ThemeRef<Font> ThemeManager::GetFont(const std::string& name) {
void ThemeManager::Fulfill(ThemeRefContainer<Drawable>& ref) { void ThemeManager::Fulfill(ThemeRefContainer<Drawable>& ref) {
string path = ResolveAssetPath(ref.name); 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>(); static shared_ptr<Drawable> nulldrw = make_shared<starlight::gfx::DrawableTest>();
string ext = FindExtension(path); string ext = FindExtension(path);
@ -225,9 +233,8 @@ shared_ptr<Drawable> ThemeManager::LoadAsset(string& path) {
// else if (type == "") { } // else if (type == "") { }
else if (type == "link") { else if (type == "link") {
string npath = ResolveAssetPath(j["path"]); string npath = ResolveAssetPath(j["path"]);
//return LoadAsset(npath); ref.redir = const_cast<ThemeRefContainer<Drawable>*>(GetAsset(npath).cptr); // link containers directly
return GetAsset(npath).GetShared(); // I guess this works; may need to be altered for asynchronity if I do that later return nulldrw; // doesn't really matter what's inside, it'll never get used
// (perhaps by--wait no, making it the same ThemeRefContainer would require a full rearchitecture of this part @.@)
} }
return nulldrw; return nulldrw;
} }

View File

@ -48,13 +48,15 @@ namespace starlight {
static void Fulfill(gfx::ThemeRefContainer<gfx::Drawable>& ref); static void Fulfill(gfx::ThemeRefContainer<gfx::Drawable>& ref);
static void Fulfill(gfx::ThemeRefContainer<gfx::Font>& 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: public:
ThemeManager() = delete; // "static" class ThemeManager() = delete; // "static" class
static void Init(); static void Init();
static void End(); static void End();
static void GC();
static gfx::ThemeRef<gfx::Drawable> GetAsset(const std::string& name); static gfx::ThemeRef<gfx::Drawable> GetAsset(const std::string& name);
static gfx::ThemeRef<gfx::Font> GetFont(const std::string& name); static gfx::ThemeRef<gfx::Font> GetFont(const std::string& name);

View File

@ -29,7 +29,7 @@ namespace starlight {
getdef = o.getdef; 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" Optional<T>& operator=(const T& o) { // assign by type's assignment operator if passed a "value"
if (!p) p = std::make_unique<T>(); if (!p) p = std::make_unique<T>();
*p = o; *p = o;

View File

@ -37,6 +37,10 @@ namespace {
return tc; 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; 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 priority = 1000; // probably don't want all that much displaying above the keyboard
handler->parent = this; 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; cover->blockTouch = true;
touchScreen->Add(cover);
// wip // build keyboard
setContainer = std::make_shared<ui::UIContainer>(VRect::touchScreen); //setContainer = touchScreen->AddNew<ui::UIContainer>(VRect::touchScreen); // kept as a test case
touchScreen->Add(setContainer); setContainer = touchScreen->AddNew<ui::UICanvas>(VRect::touchScreen); // but this is much more efficient
auto actSym = [this](Button& key){ auto actSym = [this](Button& key){
this->handler->InputSymbol(key.label); 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); bpen = bpstart + Vector2(linestart[line] * bs.x, bs.y * line);
} else { } else {
// lower // 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; if (c == ' ') key->rect.size.x *= 6;
key->SetText(string(1, c)); key->SetText(string(1, c));
key->eOnTap = actSym; key->eOnTap = actSym;
setContainer->Add(key);
// upper // 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; if (C == ' ') key->rect.size.x *= 6;
key->SetText(string(1, C)); key->SetText(string(1, C));
key->eOnTap = actSym; key->eOnTap = actSym;
setContainer->Add(key);
// and after // and after
bpen.x += key->rect.size.x; bpen.x += key->rect.size.x;
@ -94,28 +95,30 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) {
// backspace // backspace
bpen = bpstart + bs * Vector2(linestart[3] + 10, 3); 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->rect.size.x *= 1.25;
//key->SetText("< <");
key->style.glyph = ThemeManager::GetAsset("glyphs/backspace.small"); key->style.glyph = ThemeManager::GetAsset("glyphs/backspace.small");
key->eOnTap = [this](auto& btn){ this->handler->Backspace(); this->OnKey(); }; key->eOnTap = [this](auto& btn){ this->handler->Backspace(); this->OnKey(); };
touchScreen->Add(key);
// enter // enter
bpen = bpstart + bs * Vector2(linestart[4] + 8, 4); 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->rect.size.x *= 2.5;
//key->SetText("Enter");
key->style.glyph = ThemeManager::GetAsset("glyphs/enter.large"); key->style.glyph = ThemeManager::GetAsset("glyphs/enter.large");
key->eOnTap = [this](auto& btn){ this->handler->Enter(); this->OnKey(); }; key->eOnTap = [this](auto& btn){ this->handler->Enter(); this->OnKey(); };
touchScreen->Add(key);
previewSc = std::make_shared<ScrollField>(VRect(VRect::touchScreen.TopEdge(66))); // shift
touchScreen->Add(previewSc); 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); }; preview->eOnTap = [this](auto& layer){ this->OnPreviewTap(layer); };
previewSc->Add(preview);
RefreshPreview(); RefreshPreview();
} }
@ -126,46 +129,57 @@ void OSK::Update(bool focused) {
return; return;
} }
if (focused) { if (focused) {
if (InputManager::Pressed(Keys::B)) handler->Done(); if (InputManager::Pressed(Keys::B | Keys::Start)) handler->Done();
if (handler->showPreview) { if (handler->showPreview) {
if (InputManager::Pressed(Keys::DPadLeft)) { auto& tc = PreviewTC();
bool refresh = false;
if (ShiftScroll(Keys::DPadLeft)) {
auto c = handler->GetCursor(); auto c = handler->GetCursor();
if (c > 0) handler->SetCursor(c - 1); if (c > 0) handler->SetCursor(c - 1);
RefreshPreview(); refresh = true;
} }
if (InputManager::Pressed(Keys::DPadRight)) { if (ShiftScroll(Keys::DPadRight)) {
handler->SetCursor(handler->GetCursor() + 1); handler->SetCursor(handler->GetCursor() + 1);
RefreshPreview(); refresh = true;
} }
if (ShiftScroll(Keys::DPadUp)) {
auto& tc = PreviewTC();
if (InputManager::Pressed(Keys::DPadUp)) {
Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor()); Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor());
pt.y -= tc.Measure("|").y * 0.5f; pt.y -= tc.Measure("|").y * 0.5f;
handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt)); 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()); Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor());
pt.y += tc.Measure("|").y * 1.5f; pt.y += tc.Measure("|").y * 1.5f;
handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt)); handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt));
RefreshPreview(); refresh = true;
} }
if (refresh) RefreshPreview();
} }
float& s = setContainer->scrollOffset.y; float& s = setContainer->scrollOffset.y;
float ts = 0; float ts = 0;
if (InputManager::Held(Keys::L) || InputManager::Held(Keys::R)) { if (InputManager::Held(Keys::L) || InputManager::Held(Keys::R)) {
ts = 1000; ts = 1000;
} shiftLock = false;
} else if (shiftLock) ts = 1000;
if (s != ts) { if (s != ts) {
s = ts; s = ts;
setContainer->MarkForRedraw(); 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() { void OSK::OnKey() {
shiftLock = false;
RefreshPreview(); RefreshPreview();
} }

View File

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include "starlight/ui/Form.h" #include "starlight/ui/Form.h"
#include "starlight/ui/Button.h"
#include "starlight/ui/ScrollField.h" #include "starlight/ui/ScrollField.h"
#include "starlight/ui/DrawLayerProxy.h" #include "starlight/ui/DrawLayerProxy.h"
@ -19,7 +20,10 @@ namespace starlight {
std::shared_ptr<ui::ScrollField> previewSc; std::shared_ptr<ui::ScrollField> previewSc;
std::shared_ptr<ui::DrawLayerProxy> preview; std::shared_ptr<ui::DrawLayerProxy> preview;
std::shared_ptr<ui::Button> shiftKey;
//Vector2 cursorPos; //Vector2 cursorPos;
bool shiftLock = false;
public: public:
std::unique_ptr<osk::InputHandler> handler; std::unique_ptr<osk::InputHandler> handler;

View File

@ -16,21 +16,29 @@ namespace starlight {
protected: protected:
const std::string name; const std::string name;
std::shared_ptr<T> ptr = nullptr; 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(); ptr.reset();
if (full) redir = nullptr;
} }
ThemeRefContainer(std::string name, std::shared_ptr<T> ptr) : name(name), ptr(ptr) { } 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, T* ptr) : name(name), ptr(ptr) { }
ThemeRefContainer(std::string name) : name(name) { } 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: public:
~ThemeRefContainer() { } ~ThemeRefContainer() { }
T* operator ->() const { T* operator ->() const {
if (ptr == nullptr) { return &*(getptr());
ThemeManager::Fulfill(const_cast<ThemeRefContainer<T>&>(*this)); // call thememanager to grab things
}
return &*ptr;
} }
/*T& operator *() const { /*T& operator *() const {
@ -44,7 +52,8 @@ namespace starlight {
template <class T> template <class T>
class ThemeRef { class ThemeRef {
private: friend class starlight::ThemeManager;
protected:
const ThemeRefContainer<T>* cptr; const ThemeRefContainer<T>* cptr;
public: public:
ThemeRef() : cptr(nullptr) { } ThemeRef() : cptr(nullptr) { }
@ -52,7 +61,7 @@ namespace starlight {
~ThemeRef() { } ~ThemeRef() { }
inline const ThemeRefContainer<T>& operator ->() const { return *cptr; } inline const ThemeRefContainer<T>& operator ->() const { return *cptr; }
inline explicit operator bool() const { return cptr != nullptr; } 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; } inline const std::string& GetName() const { return (*cptr).name; }
}; };
} }

View File

@ -2,22 +2,26 @@
roadmap to v0.5.1 { roadmap to v0.5.1 {
- add customization for Button (alternate idle/press images, optional glyph drawable) { - fix the hang on osk when pressing (L|R)+up+left
- also add (optional) TextConfig figure out what (else) to put on the left side of the keyboard (opposite backspace and enter)
- use that to spice up the OSK
}
figure out what to put on the left side of the keyboard (opposite backspace and enter)
temporary drawable loading, local themeref, discard etc. temporary drawable loading, local themeref, discard etc.
^ both png and raw load ^ both png and raw load
maybe rgb565 for smdh icon loading? maybe rgb565 for smdh icon loading?
some examples (minesweeper?) 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" { } 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) 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 { 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 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 { HANDLE CANVAS OVERRUNS FOR LABELS AND OSK PREVIEW {
- well, it doesn't actually *crash* anymore... or at least nowhere near as fast - 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 actual cursor image for OSK instead of just using a | glypyh
input prompt dialog input prompt dialog
make the panel background not just the button image
"shortcut" overloads for InputManager::OpenKeyboard "shortcut" overloads for InputManager::OpenKeyboard
language config and atlas support language config and atlas support
maybe implement some way of "knocking out" and replacing metrics during runtime for theme switching maybe implement some way of "knocking out" and replacing metrics during runtime for theme switching

View File

@ -44,7 +44,7 @@ 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->textConfig->justification = Vector2::half; label->textConfig->justification = Vector2::half;
label->autoSizeV = true; 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); container->Add(label);
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));

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

View File

@ -55,6 +55,10 @@
"textColor" : "midGray", "textColor" : "midGray",
"borderColor" : "darkGray", "borderColor" : "darkGray",
"justification" : [0.5, 0.5] "justification" : [0.5, 0.5]
},
"keyHighlight" : {
"_inherit" : "/controls/button/text",
"textColor" : [0.75, 0.825, 1]
} }
} }
}, },