From 424574a2e67c8ecbe408d42cbb242552230025c3 Mon Sep 17 00:00:00 2001 From: OmniaX-Dev Date: Mon, 4 May 2026 19:51:15 +0200 Subject: [PATCH] Added caching for string dimensions in BasicRenderer2D --- other/TODO.txt | 2 +- src/ogfx/gui/ContextMenu.cpp | 2 +- src/ogfx/gui/ContextMenu.hpp | 4 +-- src/ogfx/gui/FocusManager.cpp | 17 +++++++--- src/ogfx/gui/ToolBar.cpp | 6 ++-- src/ogfx/gui/WidgetManager.cpp | 10 +++--- src/ogfx/gui/WidgetManager.hpp | 8 ++--- src/ogfx/gui/widgets/Button.hpp | 4 +-- src/ogfx/gui/widgets/CheckBox.hpp | 4 +-- src/ogfx/gui/widgets/ComboBox.cpp | 2 +- src/ogfx/gui/widgets/ComboBox.hpp | 2 +- src/ogfx/gui/widgets/Containers.cpp | 12 +++++-- src/ogfx/gui/widgets/Containers.hpp | 4 +-- src/ogfx/gui/widgets/Label.hpp | 8 ++--- src/ogfx/gui/widgets/ProgressBar.hpp | 2 +- src/ogfx/gui/widgets/RadioButton.hpp | 2 +- src/ogfx/gui/widgets/RootWidget.cpp | 4 +-- src/ogfx/gui/widgets/RootWidget.hpp | 2 +- src/ogfx/gui/widgets/Scrollbar.hpp | 6 ++-- src/ogfx/gui/widgets/Slider.hpp | 2 +- src/ogfx/gui/widgets/TreeView.hpp | 2 +- src/ogfx/gui/widgets/Widget.cpp | 8 ++--- src/ogfx/gui/widgets/Widget.hpp | 7 ++-- src/ogfx/render/BasicRenderer.cpp | 48 ++++++++++++++++++++++++---- src/ogfx/render/BasicRenderer.hpp | 9 ++++++ src/ostd/io/StaticHashMap.hpp | 11 +++++++ src/test/GuiTest.cpp | 16 ++++++++-- 27 files changed, 143 insertions(+), 61 deletions(-) diff --git a/other/TODO.txt b/other/TODO.txt index b00aec0..b38a4e2 100644 --- a/other/TODO.txt +++ b/other/TODO.txt @@ -28,6 +28,7 @@ ***FIX: ToolBar::AddButton not consistent order ***FIX: KeyPress crash on TreeView ***FIX: Animated icons not working in TreeView +***Check if possible, then change all WindowCore& refs to Window& Implement cursors in Stylesheet FIX: Window getting exponentially bigger when Desktop scale changes FIX: Refreshing scroll when content extent changes @@ -55,7 +56,6 @@ FIX: ContextMenu rendering partly outside the screen when showing it close to th Add ToolBar::addButton overload that takes a ogfx::Icon wrapper Redesign theme overrides to be more robust Add modifiers to Event.keyboard path -Check if possible, then change all WindowCore& refs to Window& diff --git a/src/ogfx/gui/ContextMenu.cpp b/src/ogfx/gui/ContextMenu.cpp index 5447eba..43cf1cd 100644 --- a/src/ogfx/gui/ContextMenu.cpp +++ b/src/ogfx/gui/ContextMenu.cpp @@ -304,7 +304,7 @@ namespace ogfx push_panel(m_data.entries, pos, false); m_visible = true; m_animClock.startCount(ostd::eTimeUnits::Milliseconds); - cast(m_window).getFocusManager().clearFocus(); + m_window.getFocusManager().clearFocus(); } void ContextMenu::hide(void) diff --git a/src/ogfx/gui/ContextMenu.hpp b/src/ogfx/gui/ContextMenu.hpp index fe1ea97..5051247 100644 --- a/src/ogfx/gui/ContextMenu.hpp +++ b/src/ogfx/gui/ContextMenu.hpp @@ -114,7 +114,7 @@ namespace ogfx f32 animProgress { 0 }; // 0.0 = collapsed, 1.0 = fully open }; public: - inline ContextMenu(WindowCore& window) : m_window(window) { create(); } + inline ContextMenu(Window& window) : m_window(window) { create(); } ContextMenu& create(void); void applyTheme(const ostd::Stylesheet& theme); void draw(BasicRenderer2D& gfx); @@ -155,7 +155,7 @@ namespace ogfx void relayout_panels(void); private: - WindowCore& m_window; + Window& m_window; bool m_visible { false }; Instance m_data; f32 m_entryHeight { 0 }; diff --git a/src/ogfx/gui/FocusManager.cpp b/src/ogfx/gui/FocusManager.cpp index b44a411..9575dc3 100644 --- a/src/ogfx/gui/FocusManager.cpp +++ b/src/ogfx/gui/FocusManager.cpp @@ -138,17 +138,24 @@ namespace ogfx void FocusManager::clearFocus(void) { - if (m_focused) - { - m_focused->m_focused = false; - m_focused = nullptr; - } + if (m_focused) + { + m_focused->m_focused = false; + m_focused = nullptr; + } } void FocusManager::onKeyReleased(const Event& event) { if (isTabNavigationEnabled() && event.keyboard->keyCode == KeyCode::Tab) focusNext(); + else if (event.keyboard->keyCode == KeyCode::Space || event.keyboard->keyCode == KeyCode::Return || event.keyboard->keyCode == KeyCode::Return2 || event.keyboard->keyCode == KeyCode::KpEnter) + { + if (!m_focused) + return; + if (m_focused->callback_onActionPerformed) + m_focused->callback_onActionPerformed(event); + } } } } diff --git a/src/ogfx/gui/ToolBar.cpp b/src/ogfx/gui/ToolBar.cpp index 3d9c4d0..541eb5d 100644 --- a/src/ogfx/gui/ToolBar.cpp +++ b/src/ogfx/gui/ToolBar.cpp @@ -36,7 +36,7 @@ namespace ogfx setRootChild(); setStylesheetCategoryName("toolbar"); setTypeName("ogfx::gui::ToolBar"); - auto& win = cast(getWindow()); + auto& win = getWindow(); w = win.getWindowWidth(); h = m_height; disableBorder(); @@ -69,7 +69,7 @@ namespace ogfx void ToolBar::onWindowResized(const Event& event) { - auto& win = cast(getWindow()); + auto& win = getWindow(); w = win.getWindowWidth(); h = m_height; f32 offset_y = 0; @@ -82,7 +82,7 @@ namespace ogfx void ToolBar::applyTheme(const ostd::Stylesheet& theme) { - auto& win = cast(getWindow()); + auto& win = getWindow(); setHeight(getThemeValue(theme, "height", getHeight())); setSize(win.getWindowWidth(), m_height); enableBottomBorder(isBorderEnabled()); diff --git a/src/ogfx/gui/WidgetManager.cpp b/src/ogfx/gui/WidgetManager.cpp index bc49543..1714736 100644 --- a/src/ogfx/gui/WidgetManager.cpp +++ b/src/ogfx/gui/WidgetManager.cpp @@ -28,7 +28,7 @@ namespace ogfx { namespace gui { - WidgetManager::WidgetManager(WindowCore& window, Widget& owner) : m_window(window), m_owner(owner) + WidgetManager::WidgetManager(Window& window, Widget& owner) : m_window(window), m_owner(owner) { // } @@ -101,7 +101,7 @@ namespace ogfx if (!w->contains(event.mouse->position_x, event.mouse->position_y, true)) continue; event.mouse->mousePressedOnWidget = w; - cast(m_window).getFocusManager().requestFocus(*w); + m_window.getFocusManager().requestFocus(*w); w->__onMousePressed(event); m_mousePressedOnWidget = w; if (event.isHandled() || w->m_stopEvents) @@ -219,14 +219,14 @@ namespace ogfx void WidgetManager::onKeyPressed(const Event& event) { - auto focused = cast(m_window).getFocusManager().getFocused(); + auto focused = m_window.getFocusManager().getFocused(); if (!focused || !focused->isVisible()) return; focused->__onKeyPressed(event); } void WidgetManager::onKeyReleased(const Event& event) { - auto focus = cast(m_window).getFocusManager(); + auto focus = m_window.getFocusManager(); auto focused = focus.getFocused(); if (!focused || !focused->isVisible()) return; focused->__onKeyReleased(event); @@ -234,7 +234,7 @@ namespace ogfx void WidgetManager::onTextEntered(const Event& event) { - auto focused = cast(m_window).getFocusManager().getFocused(); + auto focused = m_window.getFocusManager().getFocused(); if (!focused || !focused->isVisible()) return; focused->__onTextEntered(event); } diff --git a/src/ogfx/gui/WidgetManager.hpp b/src/ogfx/gui/WidgetManager.hpp index f3946a8..377d5c4 100644 --- a/src/ogfx/gui/WidgetManager.hpp +++ b/src/ogfx/gui/WidgetManager.hpp @@ -29,15 +29,15 @@ namespace ostd namespace ogfx { class BasicRenderer2D; - class WindowCore; namespace gui { class Event; class Widget; + class Window; class WidgetManager { public: - WidgetManager(WindowCore& window, Widget& owner); + WidgetManager(Window& window, Widget& owner); bool hasWidget(Widget& widget); bool addWidget(Widget& widget); bool removeWidget(Widget& widget); @@ -59,14 +59,14 @@ namespace ogfx void onWindowFocusLost(const Event& event); inline i32 widgetCount(void) const { return m_widgetList.size(); } - inline WindowCore& getWindow(void) { return m_window; } + inline Window& getWindow(void) { return m_window; } inline const stdvec& getWidgets(void) const { return m_widgetList; } private: void processDragAndDrop(Widget* widget, const Event& event); private: - WindowCore& m_window; + Window& m_window; Widget& m_owner; stdvec m_widgetList; Widget* m_mousePressedOnWidget { nullptr }; diff --git a/src/ogfx/gui/widgets/Button.hpp b/src/ogfx/gui/widgets/Button.hpp index 97c051c..f3220be 100644 --- a/src/ogfx/gui/widgets/Button.hpp +++ b/src/ogfx/gui/widgets/Button.hpp @@ -56,8 +56,8 @@ namespace ogfx class Button : public Widget { public: - inline Button(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } - inline Button(WindowCore& window, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); } + inline Button(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } + inline Button(Window& window, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); } Button& create(const String& text); void onDraw(ogfx::BasicRenderer2D& gfx) override; diff --git a/src/ogfx/gui/widgets/CheckBox.hpp b/src/ogfx/gui/widgets/CheckBox.hpp index 9ce89f6..2a8d91f 100644 --- a/src/ogfx/gui/widgets/CheckBox.hpp +++ b/src/ogfx/gui/widgets/CheckBox.hpp @@ -30,8 +30,8 @@ namespace ogfx class CheckBox : public Widget { public: - inline CheckBox(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } - inline CheckBox(WindowCore& window, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); } + inline CheckBox(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } + inline CheckBox(Window& window, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); } CheckBox& create(const String& text); void applyTheme(const ostd::Stylesheet& theme) override; void onDraw(ogfx::BasicRenderer2D& gfx) override; diff --git a/src/ogfx/gui/widgets/ComboBox.cpp b/src/ogfx/gui/widgets/ComboBox.cpp index 0463dad..d70a831 100644 --- a/src/ogfx/gui/widgets/ComboBox.cpp +++ b/src/ogfx/gui/widgets/ComboBox.cpp @@ -115,7 +115,7 @@ namespace ogfx setThemeQualifier("active", true); m_dropDownShown = true; Vec2 anchor = getGlobalPosition() + Vec2 { 0, geth() }; - cast(getWindow()).showContextMenu(m_menu, anchor); + getWindow().showContextMenu(m_menu, anchor); } } diff --git a/src/ogfx/gui/widgets/ComboBox.hpp b/src/ogfx/gui/widgets/ComboBox.hpp index d9c0902..e5d2b8e 100644 --- a/src/ogfx/gui/widgets/ComboBox.hpp +++ b/src/ogfx/gui/widgets/ComboBox.hpp @@ -30,7 +30,7 @@ namespace ogfx class ComboBox : public Widget { public: - inline ComboBox(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } + inline ComboBox(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } ComboBox& create(void); i32 addMenuItem(const String& text); bool removeMenuItem(const String& text); diff --git a/src/ogfx/gui/widgets/Containers.cpp b/src/ogfx/gui/widgets/Containers.cpp index 5453100..a097836 100644 --- a/src/ogfx/gui/widgets/Containers.cpp +++ b/src/ogfx/gui/widgets/Containers.cpp @@ -47,7 +47,7 @@ namespace ogfx m_titlebarHeight = getThemeValue(theme, "titlebarHeight", 30); m_titlebarBorderWidth = getThemeValue(theme, "titlebarBorderWidth", 1); m_titlebarFontSize = getThemeValue(theme, "titlebarFontSize", 26); - m_titleTextAlign = getThemeValue(theme, "titlebarTextAlign", cast(WindowCore::eTextAlign::Left)); + m_titleTextAlign = getThemeValue(theme, "titlebarTextAlign", cast(Window::eTextAlign::Left)); setTitlebarType(getThemeValue(theme, "titlebarType", TitleBarTypes::None)); } @@ -137,7 +137,7 @@ namespace ogfx { gfx.outlinedRoundRect(titleBarBounds, m_titlebarColor, m_titlebarColor, { cast(getBorderRadius()), cast(getBorderRadius()), 0, 0 }, getBorderWidth()); gfx.drawLine({ getGlobalPosition() + Vec2 { 0, m_titlebarHeight - 2 }, getGlobalPosition() + Vec2 { getGlobalBounds().w, m_titlebarHeight - 2 } }, m_titlebarBorderColor, m_titlebarBorderWidth); - if (m_titleTextAlign == cast(WindowCore::eTextAlign::Center)) + if (m_titleTextAlign == cast(Window::eTextAlign::Center)) gfx.drawCenteredString(m_title, titleBarBounds, m_titleColor, m_titlebarFontSize); else gfx.drawString(m_title, titleBarBounds.getPosition() + Vec2 { 10, 5 }, m_titleColor, m_titlebarFontSize); @@ -145,7 +145,7 @@ namespace ogfx } case TitleBarTypes::MinimalValue: { - if (m_titleTextAlign == cast(WindowCore::eTextAlign::Center)) + if (m_titleTextAlign == cast(Window::eTextAlign::Center)) gfx.drawCenteredString(m_title, titleBarBounds, m_titleColor, m_titlebarFontSize); else gfx.drawString(m_title, titleBarBounds.getPosition() + Vec2 { 10, 5 }, m_titleColor, m_titlebarFontSize); @@ -268,6 +268,8 @@ namespace ogfx auto it = std::find_if(m_tabs.begin(), m_tabs.end(), [&](const std::unique_ptr& p) { return p->getTitle() == title; }); if (it == m_tabs.end()) return false; + if (m_currentTab == it->get()) + prepare_for_current_tab_removal(); m_tabs.erase(it); return true; } @@ -289,6 +291,7 @@ namespace ogfx auto curr = m_tabs[index].get(); if (!ignore_same_tab && curr == m_currentTab && m_currentTab != nullptr) return false; + getWindow().getFocusManager().clearFocus(); removeWidget(*m_currentTab); m_currentTabIndex = index; m_currentTab = curr; @@ -320,6 +323,7 @@ namespace ogfx return; if (m_tabs.size() < 2) { + getWindow().getFocusManager().clearFocus(); m_currentTab = nullptr; return; } @@ -328,6 +332,8 @@ namespace ogfx if (it == m_tabs.end()) return; // shouldn't happen but defensive + getWindow().getFocusManager().clearFocus(); + if (it != m_tabs.begin()) m_currentTab = (it - 1)->get(); // tab to the left else diff --git a/src/ogfx/gui/widgets/Containers.hpp b/src/ogfx/gui/widgets/Containers.hpp index 7e40d37..04358d3 100644 --- a/src/ogfx/gui/widgets/Containers.hpp +++ b/src/ogfx/gui/widgets/Containers.hpp @@ -41,7 +41,7 @@ namespace ogfx inline static constexpr i32 MinimalValue = 2; }; public: - inline Panel(WindowCore& window) : ScrollableWidget(window) { create(); } + inline Panel(Window& window) : ScrollableWidget(window) { create(); } Panel& create(void); void applyTheme(const ostd::Stylesheet& theme) override; void afterDraw(ogfx::BasicRenderer2D& gfx) override; @@ -78,7 +78,7 @@ namespace ogfx class TabPanel : public Widget { public: - inline TabPanel(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } + inline TabPanel(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } TabPanel& create(void); void applyTheme(const ostd::Stylesheet& theme) override; void onMousePressed(const Event& event) override; diff --git a/src/ogfx/gui/widgets/Label.hpp b/src/ogfx/gui/widgets/Label.hpp index 27289c7..ff854f2 100644 --- a/src/ogfx/gui/widgets/Label.hpp +++ b/src/ogfx/gui/widgets/Label.hpp @@ -41,8 +41,8 @@ namespace ogfx class Label : public Widget { public: - inline Label(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } - inline Label(WindowCore& window, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); } + inline Label(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } + inline Label(Window& window, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); } Label& create(const String& text); void onDraw(ogfx::BasicRenderer2D& gfx) override; @@ -94,8 +94,8 @@ namespace ogfx class ImageLabel : public Widget { public: - inline ImageLabel(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } - inline ImageLabel(WindowCore& window, const String& filePath) : Widget({ 0, 0, 0, 0 }, window) { create(filePath); } + inline ImageLabel(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } + inline ImageLabel(Window& window, const String& filePath) : Widget({ 0, 0, 0, 0 }, window) { create(filePath); } ImageLabel& create(const String& filePath); void applyTheme(const ostd::Stylesheet& theme) override; diff --git a/src/ogfx/gui/widgets/ProgressBar.hpp b/src/ogfx/gui/widgets/ProgressBar.hpp index 4313b43..e1960f8 100644 --- a/src/ogfx/gui/widgets/ProgressBar.hpp +++ b/src/ogfx/gui/widgets/ProgressBar.hpp @@ -30,7 +30,7 @@ namespace ogfx class ProgressBar : public Widget { public: - inline ProgressBar(WindowCore& window, f32 min = 0.0f, f32 max = 1.0f, f32 start = 0.0f) : Widget({ 0, 0, 0, 0 }, window), m_progress(std::clamp(start, min, max)), m_min(min), m_max(max) { create(); } + inline ProgressBar(Window& window, f32 min = 0.0f, f32 max = 1.0f, f32 start = 0.0f) : Widget({ 0, 0, 0, 0 }, window), m_progress(std::clamp(start, min, max)), m_min(min), m_max(max) { create(); } ProgressBar& create(void); void applyTheme(const ostd::Stylesheet& theme) override; void onDraw(ogfx::BasicRenderer2D& gfx) override; diff --git a/src/ogfx/gui/widgets/RadioButton.hpp b/src/ogfx/gui/widgets/RadioButton.hpp index ba219c5..2c7ac42 100644 --- a/src/ogfx/gui/widgets/RadioButton.hpp +++ b/src/ogfx/gui/widgets/RadioButton.hpp @@ -41,7 +41,7 @@ namespace ogfx OSTD_PARAM_GETSET(Color, OuterCircleColor, m_outerCircleColor); private: - inline RadioButton(WindowCore& window, RadioButtonGroup& group, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(group, text); } + inline RadioButton(Window& window, RadioButtonGroup& group, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(group, text); } RadioButton& create(RadioButtonGroup& group, const String& text); void __update_size(ogfx::BasicRenderer2D& gfx); void __set_selected(bool selected); diff --git a/src/ogfx/gui/widgets/RootWidget.cpp b/src/ogfx/gui/widgets/RootWidget.cpp index 4fed4de..5db1fc5 100644 --- a/src/ogfx/gui/widgets/RootWidget.cpp +++ b/src/ogfx/gui/widgets/RootWidget.cpp @@ -26,7 +26,7 @@ namespace ogfx { namespace gui { - RootWidget::RootWidget(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) + RootWidget::RootWidget(Window& window) : Widget({ 0, 0, 0, 0 }, window) { setRootChild(); disableFocus(); @@ -37,7 +37,7 @@ namespace ogfx void RootWidget::onWindowResized(const Event& event) { - Window& win = cast(getWindow()); //TODO: Potentially unsage? + Window& win = getWindow(); f32 offset_y = 0; if (win.m_menubar.isVisible()) offset_y += win.m_menubar.geth(); diff --git a/src/ogfx/gui/widgets/RootWidget.hpp b/src/ogfx/gui/widgets/RootWidget.hpp index f495ec1..16e8feb 100644 --- a/src/ogfx/gui/widgets/RootWidget.hpp +++ b/src/ogfx/gui/widgets/RootWidget.hpp @@ -29,7 +29,7 @@ namespace ogfx class RootWidget : public Widget { public: - RootWidget(WindowCore& window); + RootWidget(Window& window); void onWindowResized(const Event& event) override; void applyTheme(const ostd::Stylesheet& theme) override; void onDraw(ogfx::BasicRenderer2D& gfx) override; diff --git a/src/ogfx/gui/widgets/Scrollbar.hpp b/src/ogfx/gui/widgets/Scrollbar.hpp index a7e3d22..510dc84 100644 --- a/src/ogfx/gui/widgets/Scrollbar.hpp +++ b/src/ogfx/gui/widgets/Scrollbar.hpp @@ -30,7 +30,7 @@ namespace ogfx class VerticalScrollBar : public Widget { public: - inline VerticalScrollBar(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } + inline VerticalScrollBar(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } VerticalScrollBar& create(void); void applyTheme(const ostd::Stylesheet& theme) override; void afterDraw(ogfx::BasicRenderer2D& gfx) override; @@ -68,7 +68,7 @@ namespace ogfx class HorizontalScrollbar : public Widget { public: - inline HorizontalScrollbar(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } + inline HorizontalScrollbar(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } HorizontalScrollbar& create(void); void applyTheme(const ostd::Stylesheet& theme) override; void afterDraw(ogfx::BasicRenderer2D& gfx) override; @@ -106,7 +106,7 @@ namespace ogfx class ScrollableWidget : public Widget { public: - inline ScrollableWidget(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } + inline ScrollableWidget(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } ScrollableWidget& create(void); virtual void onUpdate(void) override; void drawScrollbars(ogfx::BasicRenderer2D& gfx); diff --git a/src/ogfx/gui/widgets/Slider.hpp b/src/ogfx/gui/widgets/Slider.hpp index 6ffc3c4..aaeed40 100644 --- a/src/ogfx/gui/widgets/Slider.hpp +++ b/src/ogfx/gui/widgets/Slider.hpp @@ -29,7 +29,7 @@ namespace ogfx class Slider : public Widget { public: - inline Slider(WindowCore& window, bool vertical = false, f32 min = 0.0f, f32 max = 1.0f, f32 step = 0.1f) : Widget({ 0, 0, 0, 0 }, window) { create(vertical, min, max, step); } + inline Slider(Window& window, bool vertical = false, f32 min = 0.0f, f32 max = 1.0f, f32 step = 0.1f) : Widget({ 0, 0, 0, 0 }, window) { create(vertical, min, max, step); } Slider& create(bool vertical, f32 min, f32 max, f32 step); void applyTheme(const ostd::Stylesheet& theme) override; void onMouseReleased(const Event& event) override; diff --git a/src/ogfx/gui/widgets/TreeView.hpp b/src/ogfx/gui/widgets/TreeView.hpp index ad8f399..8cf42d4 100644 --- a/src/ogfx/gui/widgets/TreeView.hpp +++ b/src/ogfx/gui/widgets/TreeView.hpp @@ -86,7 +86,7 @@ namespace ogfx public: using ChevronDrawCallback = std::function; - inline TreeView(WindowCore& window) : ScrollableWidget(window) { create(); } + inline TreeView(Window& window) : ScrollableWidget(window) { create(); } TreeView& create(void); void applyTheme(const ostd::Stylesheet& theme) override; void onDraw(ogfx::BasicRenderer2D& gfx) override; diff --git a/src/ogfx/gui/widgets/Widget.cpp b/src/ogfx/gui/widgets/Widget.cpp index 27b2953..7f469c1 100644 --- a/src/ogfx/gui/widgets/Widget.cpp +++ b/src/ogfx/gui/widgets/Widget.cpp @@ -31,7 +31,7 @@ namespace ogfx ostd::BaseObject* Widget::s_dragAndDropData { nullptr }; bool Widget::s_hasDragAndDropData { false }; - Widget::Widget(const Rectangle& bounds, WindowCore& window) : Rectangle(bounds), m_widgets(window, *this) + Widget::Widget(const Rectangle& bounds, Window& window) : Rectangle(bounds), m_widgets(window, *this) { m_window = &window; } @@ -39,8 +39,8 @@ namespace ogfx Widget::~Widget(void) { if (m_window && m_tabIndex >= 0) - cast(*m_window).getFocusManager().unregisterTabIndex(*this); - auto& fm = cast(*m_window).getFocusManager(); + m_window->getFocusManager().unregisterTabIndex(*this); + auto& fm = m_window->getFocusManager(); if (fm.getFocused() == this) fm.clearFocus(); // you'd add this method } @@ -240,7 +240,7 @@ namespace ogfx bool Widget::setTabIndex(i32 index) { - if (cast(getWindow()).getFocusManager().registerTabIndex(*this, index)) + if (getWindow().getFocusManager().registerTabIndex(*this, index)) { m_tabIndex = index; return true; diff --git a/src/ogfx/gui/widgets/Widget.hpp b/src/ogfx/gui/widgets/Widget.hpp index 4c58504..6a20e73 100644 --- a/src/ogfx/gui/widgets/Widget.hpp +++ b/src/ogfx/gui/widgets/Widget.hpp @@ -36,6 +36,7 @@ namespace ogfx { namespace gui { + class Window; class Widget : public ostd::BaseObject, public Rectangle { private: struct ThemeOverride @@ -65,7 +66,7 @@ namespace ogfx public: using EventCallback = std::function; public: // ================================== MAIN ================================= - Widget(const Rectangle& bounds, WindowCore& window); + Widget(const Rectangle& bounds, Window& window); Widget(Widget&&) = default; Widget& operator=(Widget&&) = delete; Widget(const Widget&) = delete; @@ -161,7 +162,7 @@ namespace ogfx inline static void clearDragAndDropData(void) { s_dragAndDropData = nullptr; } inline static ostd::BaseObject* getDragAndDropData(void) { return s_dragAndDropData; } inline i32 getTabIndex(void) const { return m_tabIndex; } - inline WindowCore& getWindow(void) { return *m_window; } + inline Window& getWindow(void) { return *m_window; } inline Widget* getParent(void) { return m_parent; } inline const Widget* getParent(void) const { return m_parent; } inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; } @@ -235,7 +236,7 @@ namespace ogfx private: // ======= CORE ======= - WindowCore* m_window { nullptr }; + Window* m_window { nullptr }; Widget* m_parent { nullptr }; WidgetManager m_widgets; std::unique_ptr m_layout; diff --git a/src/ogfx/render/BasicRenderer.cpp b/src/ogfx/render/BasicRenderer.cpp index 140bba0..606e71b 100644 --- a/src/ogfx/render/BasicRenderer.cpp +++ b/src/ogfx/render/BasicRenderer.cpp @@ -232,28 +232,64 @@ namespace ogfx Vec2 BasicRenderer2D::getStringDimensions(const String& message, i32 fontSize, TTF_Font* font) { - if (!isValid()) return { 0, 0 }; + auto dimensions = getStringDimensionsPerCharacter(message, fontSize, font); + if (dimensions.empty()) + return { 0, 0 }; + f32 totalWidth = 0; + for (const auto& d : dimensions) + totalWidth += d.x; + return { totalWidth, dimensions[0].y }; + } + + stdvec BasicRenderer2D::getStringDimensionsPerCharacter(const String& message, i32 fontSize, TTF_Font* font) + { + if (!isValid()) return { }; if (fontSize <= 0) fontSize = m_fontSize; if (!font) font = m_font; + + auto l_buildCacheKey = [&](void) -> String { + return String("").add(message).add(fontSize).add(reinterpret_cast(font)); + }; + + String cacheKey = l_buildCacheKey(); + + auto it = m_strDimsCache.find(cacheKey); + if (it != m_strDimsCache.end()) + { + m_cacheHitCount++; + return it->second; + } + i32 oldFontSize = getFontSize(); setFontSize(fontSize); auto glyphs = m_fontGlyphAtlas.processString(message, font, fontSize); - if (glyphs.empty()) return { 0, 0 }; + if (glyphs.empty()) return { }; - f32 totalWidth = 0; + stdvec dimensions; + dimensions.reserve(message.len()); + + f32 charWidth = 0; for (size_t i = 0; i < glyphs.size(); i++) { - totalWidth += glyphs[i]->advance; + charWidth += glyphs[i]->advance; if (i > 0) { i32 kern = 0; TTF_GetGlyphKerning(font, glyphs[i - 1]->codepoint, glyphs[i]->codepoint, &kern); - totalWidth += kern; + charWidth += kern; } + dimensions.push_back({ charWidth, glyphs[0]->size.y }); + charWidth = 0; } setFontSize(oldFontSize); - return { totalWidth, glyphs[0]->size.y }; + + if (m_strDimsCache.size() >= MaxStringDimsCacheSize) + m_strDimsCache.pop_back(); + m_strDimsCache.insert(cacheKey, dimensions); + + m_cacheMissCount++; + return dimensions; } // ===================================================== UTILS ===================================================== diff --git a/src/ogfx/render/BasicRenderer.hpp b/src/ogfx/render/BasicRenderer.hpp index b8bc615..088f9c6 100644 --- a/src/ogfx/render/BasicRenderer.hpp +++ b/src/ogfx/render/BasicRenderer.hpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace ogfx { @@ -70,8 +71,11 @@ namespace ogfx i32 openFont(const ostd::UByte resource_data[], u32 data_size, i32 fontSize = 0); i32 setFontSize(i32 fontSize); Vec2 getStringDimensions(const String& message, i32 fontSize = 0, TTF_Font* font = nullptr); + stdvec getStringDimensionsPerCharacter(const String& message, i32 fontSize = 0, TTF_Font* font = nullptr); inline u32 getDrawCallCount(void) { return m_lastFrameDrawCallCount; } + inline u32 getCacheHitCount(void) { return m_cacheHitCount; } + inline u32 getCacheMissCount(void) { return m_cacheMissCount; } inline bool hasOpenFont(void) { return m_fontOpen; } inline TTF_Font* getSDLFont(void) { return m_font; } inline bool isValid(void) const { return m_initialized && m_fontOpen && (m_font != nullptr || m_fontFromMemory); } @@ -164,6 +168,11 @@ namespace ogfx SDL_Texture* m_lastUsedGlyphAtlasTex { nullptr }; SignalHandler m_sigHandler { *this }; + ostd::StaticHashMap> m_strDimsCache; + i32 m_cacheHitCount { 0 }; + i32 m_cacheMissCount { 0 }; + inline static constexpr i32 DefaultFontSize { 16 }; + inline static constexpr i32 MaxStringDimsCacheSize { 512 }; }; } diff --git a/src/ostd/io/StaticHashMap.hpp b/src/ostd/io/StaticHashMap.hpp index 74143bb..a93fceb 100644 --- a/src/ostd/io/StaticHashMap.hpp +++ b/src/ostd/io/StaticHashMap.hpp @@ -251,6 +251,17 @@ namespace ostd m_entries.pop_back(); } + // Removes the first element + void pop_front(void) + { + if (m_entries.empty()) return; + m_index.erase(m_entries.front().first); + m_entries.pop_front(); + // Rebuild all remaining indices (everything shifted by 1) + for (i32 i = 0; i < size(); i++) + m_index[m_entries[cast(i)].first] = i; + } + // ============================================================ // Removal // ============================================================ diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp index 1ef8430..0b5edd6 100644 --- a/src/test/GuiTest.cpp +++ b/src/test/GuiTest.cpp @@ -24,7 +24,7 @@ #include #include -ogfx::WindowCore::FileDialogFilterList filters = { +ogfx::gui::Window::FileDialogFilterList filters = { { "Image files", { "png", "jpg", "jpeg", "bmp" } }, { "All files", { "*" } } }; @@ -279,6 +279,14 @@ class TestWindow : public Window m_drawCallsLbl.addThemeOverride("@.label.padding", ostd::Rectangle { 10, 5, 0, 0 }); getStatusBar().addWidget(m_drawCallsLbl, { 0, 0 }); + m_cacheHitsLbl.addThemeOverride("@.label.fontSize", 20); + m_cacheHitsLbl.addThemeOverride("@.label.padding", ostd::Rectangle { 10, 5, 0, 0 }); + getStatusBar().addWidget(m_cacheHitsLbl, { 200, 0 }); + + m_cacheMissesLbl.addThemeOverride("@.label.fontSize", 20); + m_cacheMissesLbl.addThemeOverride("@.label.padding", ostd::Rectangle { 10, 5, 0, 0 }); + getStatusBar().addWidget(m_cacheMissesLbl, { 400, 0 }); + m_progressJob.start([this] { while (true) { @@ -352,6 +360,8 @@ class TestWindow : public Window { // gfx.fillRect(m_tabs.getGlobalPureContentBounds(), { 0, 255, 0, 100 }); m_drawCallsLbl.setText(String("DrawCalls: ").add(gfx.getDrawCallCount())); + m_cacheHitsLbl.setText(String("Cache Hits: ").add(gfx.getCacheHitCount())); + m_cacheMissesLbl.setText(String("Cache Miss: ").add(gfx.getCacheMissCount())); } void onFixedUpdate(void) override @@ -377,6 +387,8 @@ class TestWindow : public Window Label m_slideLbl { *this }; TreeView m_list { *this }; Label m_drawCallsLbl { *this }; + Label m_cacheHitsLbl { *this }; + Label m_cacheMissesLbl { *this }; ComboBox m_combo { *this }; enum MenuId : i32 { New = 1, Open, Save, SaveAs, Exit, CopyRaw, CopyFormatted }; @@ -501,7 +513,7 @@ i32 main(i32 argc, char** argv) window.initialize(1200, 900, "OmniaFramework - Test Window"); window.setClearColor({ 0, 0, 0 }); window.setPosition({ 50, 50 }); - // window.setWindowState(ogfx::WindowCore::eWindowState::Maximized); + // window.setWindowState(ogfx::gui::Window::eWindowState::Maximized); window.mainLoop(); return 0; }