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;
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

View File

@ -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 //

View File

@ -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();

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) {
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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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; }
};
}

View File

@ -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

View File

@ -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));

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",
"borderColor" : "darkGray",
"justification" : [0.5, 0.5]
},
"keyHighlight" : {
"_inherit" : "/controls/button/text",
"textColor" : [0.75, 0.825, 1]
}
}
},