diff --git a/libstarlight/source/starlight/ThemeManager.cpp b/libstarlight/source/starlight/ThemeManager.cpp index e0281c9..e53c7ad 100644 --- a/libstarlight/source/starlight/ThemeManager.cpp +++ b/libstarlight/source/starlight/ThemeManager.cpp @@ -347,11 +347,11 @@ TextConfig::TextConfig(const std::string& fontName, Color text, Color border) { textColor = text; borderColor = border; } -void TextConfig::Print(Vector2 position, std::string& text, Vector2 justification) { +void TextConfig::Print(Vector2 position, const std::string& text, Vector2 justification) { if (!justification) justification = this->justification; font->Print(position, text, 1, textColor, justification, borderColor); } -void TextConfig::Print(VRect rect, std::string& text, Vector2 justification) { +void TextConfig::Print(VRect rect, const std::string& text, Vector2 justification) { if (!justification) justification = this->justification; font->Print(rect, text, 1, textColor, justification, borderColor); } diff --git a/libstarlight/source/starlight/ThemeManager.h b/libstarlight/source/starlight/ThemeManager.h index 2795d76..8c0ca7d 100644 --- a/libstarlight/source/starlight/ThemeManager.h +++ b/libstarlight/source/starlight/ThemeManager.h @@ -85,8 +85,8 @@ namespace starlight { TextConfig(const std::string& fontName, Color text, Color border = Color::transparent); ~TextConfig() = default; - void Print(Vector2 position, std::string& text, Vector2 justification = Vector2::invalid); - void Print(VRect rect, std::string& text, Vector2 justification = Vector2::invalid); + void Print(Vector2 position, const std::string& text, Vector2 justification = Vector2::invalid); + void Print(VRect rect, const std::string& text, Vector2 justification = Vector2::invalid); Vector2 Measure(const std::string& text, float maxWidth = 65536*64); diff --git a/libstarlight/source/starlight/datatypes/VRect.h b/libstarlight/source/starlight/datatypes/VRect.h index 58fa7ce..70f12fe 100644 --- a/libstarlight/source/starlight/datatypes/VRect.h +++ b/libstarlight/source/starlight/datatypes/VRect.h @@ -32,7 +32,7 @@ namespace starlight { VRect Intersect(const VRect & o) const; VRect Expand(const Vector2& amount, const Vector2& bias) const; VRect Expand(const Vector2& amount) const { return Expand(amount, Vector2::half); } - VRect Expand(float x, float y) const { return Expand(Vector2(x, y )); } + VRect Expand(float x, float y) const { return Expand(Vector2(x, y)); } VRect Include(const Vector2& point) const; diff --git a/libstarlight/source/starlight/dialog/OSK.cpp b/libstarlight/source/starlight/dialog/OSK.cpp index 8b6b713..93918c1 100644 --- a/libstarlight/source/starlight/dialog/OSK.cpp +++ b/libstarlight/source/starlight/dialog/OSK.cpp @@ -36,13 +36,15 @@ namespace { static auto tc = ThemeManager::GetMetric("/dialogs/OSK/preview"); return tc; } + + const constexpr float textHang = 4; } 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(touchScreen->rect.Expand(4), "decorations/dialog.modal-cover"); + auto cover = std::make_shared(touchScreen->rect, "decorations/osk.background"); cover->blockTouch = true; touchScreen->Add(cover); @@ -58,7 +60,7 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) { Vector2 bs(24, 32); Vector2 bpen; - Vector2 bpstart(160-bs.x*(12.5/2), 68); + Vector2 bpstart(160-bs.x*(12.5/2), 68+5); int line = -1; float linestart [] = {0, .5, .75, 1.25, 2.75-1}; string chr = "\n1234567890-=\nqwertyuiop[]\nasdfghjkl;\'\nzxcvbnm,./\n` \\"; @@ -106,9 +108,14 @@ OSK::OSK(osk::InputHandler* handler) : Form(true), handler(handler) { key->eOnTap = [this](auto& btn){ this->handler->Enter(); this->OnKey(); }; touchScreen->Add(key); - preview = std::make_shared(VRect::touchScreen.TopEdge(68).Expand(-2), [this](auto& layer){ this->DrawPreview(layer); }, true); + previewSc = std::make_shared(VRect(VRect::touchScreen.TopEdge(66))); + touchScreen->Add(previewSc); + + preview = std::make_shared(VRect::touchScreen.TopEdge(66).Expand(-2, 0), [this](auto& layer){ this->DrawPreview(layer); }, true); preview->eOnTap = [this](auto& layer){ this->OnPreviewTap(layer); }; - touchScreen->Add(preview); + previewSc->Add(preview); + + RefreshPreview(); } void OSK::Update(bool focused) { @@ -117,30 +124,29 @@ void OSK::Update(bool focused) { } if (focused) { if (InputManager::Pressed(Keys::B)) handler->Done(); - if (true || handler->showPreview) { + if (handler->showPreview) { if (InputManager::Pressed(Keys::DPadLeft)) { - handler->SetCursor(handler->GetCursor() - 1); - preview->Refresh(); + auto c = handler->GetCursor(); + if (c > 0) handler->SetCursor(c - 1); + RefreshPreview(); } if (InputManager::Pressed(Keys::DPadRight)) { handler->SetCursor(handler->GetCursor() + 1); - preview->Refresh(); + RefreshPreview(); } auto& tc = PreviewTC(); if (InputManager::Pressed(Keys::DPadUp)) { Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor()); - string msr = "|"; - pt.y -= tc.Measure(msr).y * 0.5f; + pt.y -= tc.Measure("|").y * 0.5f; handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt)); - preview->Refresh(); + RefreshPreview(); } if (InputManager::Pressed(Keys::DPadDown)) { Vector2 pt = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor()); - string msr = "|"; - pt.y += tc.Measure(msr).y * 1.5f; + pt.y += tc.Measure("|").y * 1.5f; handler->SetCursor(tc.GetCursorFromPoint(preview->rect, handler->GetPreviewText(), pt)); - preview->Refresh(); + RefreshPreview(); } } @@ -157,23 +163,47 @@ void OSK::Update(bool focused) { } void OSK::OnKey() { + RefreshPreview(); +} + +void OSK::RefreshPreview() { + auto& tc = PreviewTC(); + if (handler->showPreview) { + Vector2 sz = tc.Measure(handler->GetPreviewText(), preview->rect.size.x); + preview->Resize(Vector2(preview->rect.size.x, std::max(sz.y + textHang, previewSc->rect.size.y))); // I guess a magic four will do + + Vector2 cp = tc.GetCursorPosition(preview->rect, handler->GetPreviewText(), handler->GetCursor()); + Vector2 cs = tc.Measure("|") + Vector2(0, textHang); + previewSc->ScrollIntoView(VRect(cp, cs));//*/ + + } else { + preview->Resize(Vector2(preview->rect.size.x, 66)); + } preview->Refresh(); } void OSK::DrawPreview(DrawLayerProxy& layer) { - if (true || handler->showPreview) { + if (handler->showPreview) { auto& tc = PreviewTC(); tc.Print(layer.rect, handler->GetPreviewText(), Vector2::zero); Vector2 cp = tc.GetCursorPosition(layer.rect, handler->GetPreviewText(), handler->GetCursor()); string cc = "|"; - tc.Print(cp, cc, Vector2::zero); + tc.Print(cp, cc); + + //Vector2 cs = tc.Measure("|") + Vector2(0, textHang); + //previewSc->ScrollIntoView(VRect(cp, cs)); + } else { + static auto tc = ThemeManager::GetMetric("/dialogs/OSK/noPreview"); + tc.Print(layer.rect, "(no preview available)"); } } void OSK::OnPreviewTap(DrawLayerProxy& layer) { - Vector2 tpos = InputManager::TouchPos() - layer.ScreenRect().pos; - auto& tc = PreviewTC(); - handler->SetCursor(tc.GetCursorFromPoint(layer.rect, handler->GetPreviewText(), tpos)); - preview->Refresh(); + if (handler->showPreview) { + Vector2 tpos = InputManager::TouchPos() - layer.ScreenRect().pos; + auto& tc = PreviewTC(); + handler->SetCursor(tc.GetCursorFromPoint(layer.rect, handler->GetPreviewText(), tpos)); + RefreshPreview(); + } } diff --git a/libstarlight/source/starlight/dialog/OSK.h b/libstarlight/source/starlight/dialog/OSK.h index e2c88cc..deff676 100644 --- a/libstarlight/source/starlight/dialog/OSK.h +++ b/libstarlight/source/starlight/dialog/OSK.h @@ -6,6 +6,7 @@ #include #include "starlight/ui/Form.h" +#include "starlight/ui/ScrollField.h" #include "starlight/ui/DrawLayerProxy.h" #include "starlight/dialog/osk/InputHandler.h" @@ -15,7 +16,10 @@ namespace starlight { class OSK : public ui::Form, public ui::FormCreator { private: std::shared_ptr setContainer; + std::shared_ptr previewSc; std::shared_ptr preview; + + //Vector2 cursorPos; public: std::unique_ptr handler; @@ -27,6 +31,7 @@ namespace starlight { void OnKey(); + void RefreshPreview(); void DrawPreview(ui::DrawLayerProxy& layer); void OnPreviewTap(ui::DrawLayerProxy& layer); }; diff --git a/libstarlight/source/starlight/gfx/BitmapFont.cpp b/libstarlight/source/starlight/gfx/BitmapFont.cpp index 9ca001d..08a0d5f 100644 --- a/libstarlight/source/starlight/gfx/BitmapFont.cpp +++ b/libstarlight/source/starlight/gfx/BitmapFont.cpp @@ -64,7 +64,9 @@ BitmapFont::CharInfo& BitmapFont::Char(char c) { } Vector2 BitmapFont::MeasureTo(const std::string& msg, bool total, unsigned int end, float maxWidth) { + if (msg == "") return Vector2::zero; if (total) { + if (end == 0) return Vector2(0, lineHeight); Vector2 measure = Vector2::zero; ForChar(msg, [&, this](auto& s){ @@ -81,11 +83,14 @@ Vector2 BitmapFont::MeasureTo(const std::string& msg, bool total, unsigned int e Vector2 measure; bool found = false; - ForChar(msg, [&, this, total, end](auto& s){ + unsigned int et = end - 1; + if (end == 0) et = 0; + ForChar(msg, [&, this, total, end, et](auto& s){ measure = Vector2(s.lineAcc + s.cc->advX, lineHeight * s.lineNum); - if (s.i == end-1) { + if (s.i >= et) { found = true; if (s.i == s.lineEnd) measure = Vector2(0, lineHeight * (s.lineNum + 1)); // linebreak == next line + if (end == 0) measure = Vector2(0, lineHeight * s.lineNum); return true; } diff --git a/libstarlight/source/starlight/ui/DrawLayerProxy.cpp b/libstarlight/source/starlight/ui/DrawLayerProxy.cpp index 5ba4e6e..2c55bfb 100644 --- a/libstarlight/source/starlight/ui/DrawLayerProxy.cpp +++ b/libstarlight/source/starlight/ui/DrawLayerProxy.cpp @@ -29,6 +29,7 @@ void DrawLayerProxy::PreDraw() { void DrawLayerProxy::Draw() { if (canvas) { + auto rect = (this->rect + GFXManager::GetOffset()).IntSnap(); canvas->Draw(VRect(rect.pos, canvas->rect.size)); } else { if (eDraw) eDraw(*this); diff --git a/libstarlight/source/starlight/ui/Form.h b/libstarlight/source/starlight/ui/Form.h index c15d6ab..97cb6a3 100644 --- a/libstarlight/source/starlight/ui/Form.h +++ b/libstarlight/source/starlight/ui/Form.h @@ -58,7 +58,7 @@ namespace starlight { // public: - int priority = 0; + float priority = 0; unsigned int flags = 0; std::shared_ptr touchScreen = nullptr; diff --git a/libstarlight/source/starlight/ui/ScrollField.cpp b/libstarlight/source/starlight/ui/ScrollField.cpp index 977a23a..3b79da0 100644 --- a/libstarlight/source/starlight/ui/ScrollField.cpp +++ b/libstarlight/source/starlight/ui/ScrollField.cpp @@ -38,6 +38,18 @@ void ScrollField::Update() { scrollOffset.y = std::max(0.0f, std::min(scrollOffset.y, scrollMax.y)); } +void ScrollField::ScrollIntoView(Vector2 pt) { + Vector2 sm = pt - rect.size; + + scrollOffset.x = std::max(sm.x, std::min(scrollOffset.x, pt.x)); + scrollOffset.y = std::max(sm.y, std::min(scrollOffset.y, pt.y)); + + // hmm. doesn't play well with pending resizes + //scrollOffset.x = std::max(0.0f, std::min(scrollOffset.x, scrollMax.x)); + //scrollOffset.y = std::max(0.0f, std::min(scrollOffset.y, scrollMax.y)); +} +void ScrollField::ScrollIntoView(VRect box) { ScrollIntoView(box.BottomRight()); ScrollIntoView(box.pos); } + void ScrollField::OnProcessTouchEvent() { // stop when child element touched if (InputManager::Pressed(Keys::Touch)) scrollVel = Vector2::zero; } diff --git a/libstarlight/source/starlight/ui/ScrollField.h b/libstarlight/source/starlight/ui/ScrollField.h index 86f98a3..6a24a06 100644 --- a/libstarlight/source/starlight/ui/ScrollField.h +++ b/libstarlight/source/starlight/ui/ScrollField.h @@ -16,6 +16,9 @@ namespace starlight { void Update() override; + void ScrollIntoView(Vector2 pt); + void ScrollIntoView(VRect box); + // events void OnTouchOn() override; void OnTouchOff() override { } @@ -33,4 +36,3 @@ namespace starlight { }; } } - diff --git a/libstarlight/todo.txt b/libstarlight/todo.txt index 602ac55..b668d3d 100644 --- a/libstarlight/todo.txt +++ b/libstarlight/todo.txt @@ -3,20 +3,21 @@ roadmap to first release, in no particular order { finish implementing OSK! { - enable scrolling preview (and scroll to cursor where applicable) - polish! + - fix cursor-down + polish! { + - background + actual cursor image? + } InputManager::OpenKeyboard } + fix [ offset (-1 it) add generic backdrop assets (and form) - fix lowercase j running into things - adjust monospace line height (and maybe offset) to closer match the vwf of the same size - add license!! (MIT?) ADD README.MD PLS } then consider these before 1.0 "gold" { - should form priority be a float? + - should form priority be a float? make closing forms a bit less finicky (add them to a separate list and let the Application remove them from the list) add customization for Button (alternate idle/press images, optional glyph drawable) ^ use that to spice up the OSK diff --git a/testbed/source/Core.cpp b/testbed/source/Core.cpp index 027b2c8..3f906dc 100644 --- a/testbed/source/Core.cpp +++ b/testbed/source/Core.cpp @@ -78,6 +78,7 @@ void Core::Init() { auto tb = std::make_shared(VRect(0, 64, 320, 24).Expand(-16, 0)); tb->text = "TextBox testing in progress. ij ji lj jl"; + tb->multiLine = true; form->touchScreen->Add(tb); /*label->SetFont("default.16"); diff --git a/themes/default/decorations/osk.background.png b/themes/default/decorations/osk.background.png new file mode 100644 index 0000000..ac72c66 Binary files /dev/null and b/themes/default/decorations/osk.background.png differ diff --git a/themes/default/decorations/osk.background.xcf b/themes/default/decorations/osk.background.xcf new file mode 100644 index 0000000..690281a Binary files /dev/null and b/themes/default/decorations/osk.background.xcf differ diff --git a/themes/default/metrics.json b/themes/default/metrics.json index ebc38d2..48ad86e 100644 --- a/themes/default/metrics.json +++ b/themes/default/metrics.json @@ -45,6 +45,12 @@ "font" : "default.16", "textColor" : "white", "borderColor" : "black" + }, + "noPreview" : { + "font" : "default.16", + "textColor" : "midGray", + "borderColor" : "darkGray", + "justification" : [0.5, 0.5] } } },