Added caching for string dimensions in BasicRenderer2D

This commit is contained in:
OmniaX-Dev 2026-05-04 19:51:15 +02:00
parent 012f26f5f5
commit 424574a2e6
27 changed files with 143 additions and 61 deletions

View file

@ -28,6 +28,7 @@
***FIX: ToolBar::AddButton not consistent order ***FIX: ToolBar::AddButton not consistent order
***FIX: KeyPress crash on TreeView ***FIX: KeyPress crash on TreeView
***FIX: Animated icons not working in TreeView ***FIX: Animated icons not working in TreeView
***Check if possible, then change all WindowCore& refs to Window&
Implement cursors in Stylesheet Implement cursors in Stylesheet
FIX: Window getting exponentially bigger when Desktop scale changes FIX: Window getting exponentially bigger when Desktop scale changes
FIX: Refreshing scroll when content extent 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 Add ToolBar::addButton overload that takes a ogfx::Icon wrapper
Redesign theme overrides to be more robust Redesign theme overrides to be more robust
Add modifiers to Event.keyboard path Add modifiers to Event.keyboard path
Check if possible, then change all WindowCore& refs to Window&

View file

@ -304,7 +304,7 @@ namespace ogfx
push_panel(m_data.entries, pos, false); push_panel(m_data.entries, pos, false);
m_visible = true; m_visible = true;
m_animClock.startCount(ostd::eTimeUnits::Milliseconds); m_animClock.startCount(ostd::eTimeUnits::Milliseconds);
cast<Window&>(m_window).getFocusManager().clearFocus(); m_window.getFocusManager().clearFocus();
} }
void ContextMenu::hide(void) void ContextMenu::hide(void)

View file

@ -114,7 +114,7 @@ namespace ogfx
f32 animProgress { 0 }; // 0.0 = collapsed, 1.0 = fully open f32 animProgress { 0 }; // 0.0 = collapsed, 1.0 = fully open
}; };
public: public:
inline ContextMenu(WindowCore& window) : m_window(window) { create(); } inline ContextMenu(Window& window) : m_window(window) { create(); }
ContextMenu& create(void); ContextMenu& create(void);
void applyTheme(const ostd::Stylesheet& theme); void applyTheme(const ostd::Stylesheet& theme);
void draw(BasicRenderer2D& gfx); void draw(BasicRenderer2D& gfx);
@ -155,7 +155,7 @@ namespace ogfx
void relayout_panels(void); void relayout_panels(void);
private: private:
WindowCore& m_window; Window& m_window;
bool m_visible { false }; bool m_visible { false };
Instance m_data; Instance m_data;
f32 m_entryHeight { 0 }; f32 m_entryHeight { 0 };

View file

@ -149,6 +149,13 @@ namespace ogfx
{ {
if (isTabNavigationEnabled() && event.keyboard->keyCode == KeyCode::Tab) if (isTabNavigationEnabled() && event.keyboard->keyCode == KeyCode::Tab)
focusNext(); 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);
}
} }
} }
} }

View file

@ -36,7 +36,7 @@ namespace ogfx
setRootChild(); setRootChild();
setStylesheetCategoryName("toolbar"); setStylesheetCategoryName("toolbar");
setTypeName("ogfx::gui::ToolBar"); setTypeName("ogfx::gui::ToolBar");
auto& win = cast<Window&>(getWindow()); auto& win = getWindow();
w = win.getWindowWidth(); w = win.getWindowWidth();
h = m_height; h = m_height;
disableBorder(); disableBorder();
@ -69,7 +69,7 @@ namespace ogfx
void ToolBar::onWindowResized(const Event& event) void ToolBar::onWindowResized(const Event& event)
{ {
auto& win = cast<Window&>(getWindow()); auto& win = getWindow();
w = win.getWindowWidth(); w = win.getWindowWidth();
h = m_height; h = m_height;
f32 offset_y = 0; f32 offset_y = 0;
@ -82,7 +82,7 @@ namespace ogfx
void ToolBar::applyTheme(const ostd::Stylesheet& theme) void ToolBar::applyTheme(const ostd::Stylesheet& theme)
{ {
auto& win = cast<Window&>(getWindow()); auto& win = getWindow();
setHeight(getThemeValue<f32>(theme, "height", getHeight())); setHeight(getThemeValue<f32>(theme, "height", getHeight()));
setSize(win.getWindowWidth(), m_height); setSize(win.getWindowWidth(), m_height);
enableBottomBorder(isBorderEnabled()); enableBottomBorder(isBorderEnabled());

View file

@ -28,7 +28,7 @@ namespace ogfx
{ {
namespace gui 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)) if (!w->contains(event.mouse->position_x, event.mouse->position_y, true))
continue; continue;
event.mouse->mousePressedOnWidget = w; event.mouse->mousePressedOnWidget = w;
cast<Window&>(m_window).getFocusManager().requestFocus(*w); m_window.getFocusManager().requestFocus(*w);
w->__onMousePressed(event); w->__onMousePressed(event);
m_mousePressedOnWidget = w; m_mousePressedOnWidget = w;
if (event.isHandled() || w->m_stopEvents) if (event.isHandled() || w->m_stopEvents)
@ -219,14 +219,14 @@ namespace ogfx
void WidgetManager::onKeyPressed(const Event& event) void WidgetManager::onKeyPressed(const Event& event)
{ {
auto focused = cast<Window&>(m_window).getFocusManager().getFocused(); auto focused = m_window.getFocusManager().getFocused();
if (!focused || !focused->isVisible()) return; if (!focused || !focused->isVisible()) return;
focused->__onKeyPressed(event); focused->__onKeyPressed(event);
} }
void WidgetManager::onKeyReleased(const Event& event) void WidgetManager::onKeyReleased(const Event& event)
{ {
auto focus = cast<Window&>(m_window).getFocusManager(); auto focus = m_window.getFocusManager();
auto focused = focus.getFocused(); auto focused = focus.getFocused();
if (!focused || !focused->isVisible()) return; if (!focused || !focused->isVisible()) return;
focused->__onKeyReleased(event); focused->__onKeyReleased(event);
@ -234,7 +234,7 @@ namespace ogfx
void WidgetManager::onTextEntered(const Event& event) void WidgetManager::onTextEntered(const Event& event)
{ {
auto focused = cast<Window&>(m_window).getFocusManager().getFocused(); auto focused = m_window.getFocusManager().getFocused();
if (!focused || !focused->isVisible()) return; if (!focused || !focused->isVisible()) return;
focused->__onTextEntered(event); focused->__onTextEntered(event);
} }

View file

@ -29,15 +29,15 @@ namespace ostd
namespace ogfx namespace ogfx
{ {
class BasicRenderer2D; class BasicRenderer2D;
class WindowCore;
namespace gui namespace gui
{ {
class Event; class Event;
class Widget; class Widget;
class Window;
class WidgetManager class WidgetManager
{ {
public: public:
WidgetManager(WindowCore& window, Widget& owner); WidgetManager(Window& window, Widget& owner);
bool hasWidget(Widget& widget); bool hasWidget(Widget& widget);
bool addWidget(Widget& widget); bool addWidget(Widget& widget);
bool removeWidget(Widget& widget); bool removeWidget(Widget& widget);
@ -59,14 +59,14 @@ namespace ogfx
void onWindowFocusLost(const Event& event); void onWindowFocusLost(const Event& event);
inline i32 widgetCount(void) const { return m_widgetList.size(); } 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<Widget*>& getWidgets(void) const { return m_widgetList; } inline const stdvec<Widget*>& getWidgets(void) const { return m_widgetList; }
private: private:
void processDragAndDrop(Widget* widget, const Event& event); void processDragAndDrop(Widget* widget, const Event& event);
private: private:
WindowCore& m_window; Window& m_window;
Widget& m_owner; Widget& m_owner;
stdvec<Widget*> m_widgetList; stdvec<Widget*> m_widgetList;
Widget* m_mousePressedOnWidget { nullptr }; Widget* m_mousePressedOnWidget { nullptr };

View file

@ -56,8 +56,8 @@ namespace ogfx
class Button : public Widget class Button : public Widget
{ {
public: public:
inline Button(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } inline Button(Window& 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, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); }
Button& create(const String& text); Button& create(const String& text);
void onDraw(ogfx::BasicRenderer2D& gfx) override; void onDraw(ogfx::BasicRenderer2D& gfx) override;

View file

@ -30,8 +30,8 @@ namespace ogfx
class CheckBox : public Widget class CheckBox : public Widget
{ {
public: public:
inline CheckBox(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } inline CheckBox(Window& 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, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); }
CheckBox& create(const String& text); CheckBox& create(const String& text);
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;
void onDraw(ogfx::BasicRenderer2D& gfx) override; void onDraw(ogfx::BasicRenderer2D& gfx) override;

View file

@ -115,7 +115,7 @@ namespace ogfx
setThemeQualifier("active", true); setThemeQualifier("active", true);
m_dropDownShown = true; m_dropDownShown = true;
Vec2 anchor = getGlobalPosition() + Vec2 { 0, geth() }; Vec2 anchor = getGlobalPosition() + Vec2 { 0, geth() };
cast<Window&>(getWindow()).showContextMenu(m_menu, anchor); getWindow().showContextMenu(m_menu, anchor);
} }
} }

View file

@ -30,7 +30,7 @@ namespace ogfx
class ComboBox : public Widget class ComboBox : public Widget
{ {
public: 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); ComboBox& create(void);
i32 addMenuItem(const String& text); i32 addMenuItem(const String& text);
bool removeMenuItem(const String& text); bool removeMenuItem(const String& text);

View file

@ -47,7 +47,7 @@ namespace ogfx
m_titlebarHeight = getThemeValue<f32>(theme, "titlebarHeight", 30); m_titlebarHeight = getThemeValue<f32>(theme, "titlebarHeight", 30);
m_titlebarBorderWidth = getThemeValue<i32>(theme, "titlebarBorderWidth", 1); m_titlebarBorderWidth = getThemeValue<i32>(theme, "titlebarBorderWidth", 1);
m_titlebarFontSize = getThemeValue<i32>(theme, "titlebarFontSize", 26); m_titlebarFontSize = getThemeValue<i32>(theme, "titlebarFontSize", 26);
m_titleTextAlign = getThemeValue<i32>(theme, "titlebarTextAlign", cast<i32>(WindowCore::eTextAlign::Left)); m_titleTextAlign = getThemeValue<i32>(theme, "titlebarTextAlign", cast<i32>(Window::eTextAlign::Left));
setTitlebarType(getThemeValue<String>(theme, "titlebarType", TitleBarTypes::None)); setTitlebarType(getThemeValue<String>(theme, "titlebarType", TitleBarTypes::None));
} }
@ -137,7 +137,7 @@ namespace ogfx
{ {
gfx.outlinedRoundRect(titleBarBounds, m_titlebarColor, m_titlebarColor, { cast<f32>(getBorderRadius()), cast<f32>(getBorderRadius()), 0, 0 }, getBorderWidth()); gfx.outlinedRoundRect(titleBarBounds, m_titlebarColor, m_titlebarColor, { cast<f32>(getBorderRadius()), cast<f32>(getBorderRadius()), 0, 0 }, getBorderWidth());
gfx.drawLine({ getGlobalPosition() + Vec2 { 0, m_titlebarHeight - 2 }, getGlobalPosition() + Vec2 { getGlobalBounds().w, m_titlebarHeight - 2 } }, m_titlebarBorderColor, m_titlebarBorderWidth); gfx.drawLine({ getGlobalPosition() + Vec2 { 0, m_titlebarHeight - 2 }, getGlobalPosition() + Vec2 { getGlobalBounds().w, m_titlebarHeight - 2 } }, m_titlebarBorderColor, m_titlebarBorderWidth);
if (m_titleTextAlign == cast<i32>(WindowCore::eTextAlign::Center)) if (m_titleTextAlign == cast<i32>(Window::eTextAlign::Center))
gfx.drawCenteredString(m_title, titleBarBounds, m_titleColor, m_titlebarFontSize); gfx.drawCenteredString(m_title, titleBarBounds, m_titleColor, m_titlebarFontSize);
else else
gfx.drawString(m_title, titleBarBounds.getPosition() + Vec2 { 10, 5 }, m_titleColor, m_titlebarFontSize); gfx.drawString(m_title, titleBarBounds.getPosition() + Vec2 { 10, 5 }, m_titleColor, m_titlebarFontSize);
@ -145,7 +145,7 @@ namespace ogfx
} }
case TitleBarTypes::MinimalValue: case TitleBarTypes::MinimalValue:
{ {
if (m_titleTextAlign == cast<i32>(WindowCore::eTextAlign::Center)) if (m_titleTextAlign == cast<i32>(Window::eTextAlign::Center))
gfx.drawCenteredString(m_title, titleBarBounds, m_titleColor, m_titlebarFontSize); gfx.drawCenteredString(m_title, titleBarBounds, m_titleColor, m_titlebarFontSize);
else else
gfx.drawString(m_title, titleBarBounds.getPosition() + Vec2 { 10, 5 }, m_titleColor, m_titlebarFontSize); 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<Panel>& p) { return p->getTitle() == title; }); auto it = std::find_if(m_tabs.begin(), m_tabs.end(), [&](const std::unique_ptr<Panel>& p) { return p->getTitle() == title; });
if (it == m_tabs.end()) if (it == m_tabs.end())
return false; return false;
if (m_currentTab == it->get())
prepare_for_current_tab_removal();
m_tabs.erase(it); m_tabs.erase(it);
return true; return true;
} }
@ -289,6 +291,7 @@ namespace ogfx
auto curr = m_tabs[index].get(); auto curr = m_tabs[index].get();
if (!ignore_same_tab && curr == m_currentTab && m_currentTab != nullptr) if (!ignore_same_tab && curr == m_currentTab && m_currentTab != nullptr)
return false; return false;
getWindow().getFocusManager().clearFocus();
removeWidget(*m_currentTab); removeWidget(*m_currentTab);
m_currentTabIndex = index; m_currentTabIndex = index;
m_currentTab = curr; m_currentTab = curr;
@ -320,6 +323,7 @@ namespace ogfx
return; return;
if (m_tabs.size() < 2) if (m_tabs.size() < 2)
{ {
getWindow().getFocusManager().clearFocus();
m_currentTab = nullptr; m_currentTab = nullptr;
return; return;
} }
@ -328,6 +332,8 @@ namespace ogfx
if (it == m_tabs.end()) if (it == m_tabs.end())
return; // shouldn't happen but defensive return; // shouldn't happen but defensive
getWindow().getFocusManager().clearFocus();
if (it != m_tabs.begin()) if (it != m_tabs.begin())
m_currentTab = (it - 1)->get(); // tab to the left m_currentTab = (it - 1)->get(); // tab to the left
else else

View file

@ -41,7 +41,7 @@ namespace ogfx
inline static constexpr i32 MinimalValue = 2; inline static constexpr i32 MinimalValue = 2;
}; };
public: public:
inline Panel(WindowCore& window) : ScrollableWidget(window) { create(); } inline Panel(Window& window) : ScrollableWidget(window) { create(); }
Panel& create(void); Panel& create(void);
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;
void afterDraw(ogfx::BasicRenderer2D& gfx) override; void afterDraw(ogfx::BasicRenderer2D& gfx) override;
@ -78,7 +78,7 @@ namespace ogfx
class TabPanel : public Widget class TabPanel : public Widget
{ {
public: 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); TabPanel& create(void);
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;
void onMousePressed(const Event& event) override; void onMousePressed(const Event& event) override;

View file

@ -41,8 +41,8 @@ namespace ogfx
class Label : public Widget class Label : public Widget
{ {
public: public:
inline Label(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } inline Label(Window& 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, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); }
Label& create(const String& text); Label& create(const String& text);
void onDraw(ogfx::BasicRenderer2D& gfx) override; void onDraw(ogfx::BasicRenderer2D& gfx) override;
@ -94,8 +94,8 @@ namespace ogfx
class ImageLabel : public Widget class ImageLabel : public Widget
{ {
public: public:
inline ImageLabel(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); } inline ImageLabel(Window& 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, const String& filePath) : Widget({ 0, 0, 0, 0 }, window) { create(filePath); }
ImageLabel& create(const String& filePath); ImageLabel& create(const String& filePath);
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;

View file

@ -30,7 +30,7 @@ namespace ogfx
class ProgressBar : public Widget class ProgressBar : public Widget
{ {
public: 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); ProgressBar& create(void);
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;
void onDraw(ogfx::BasicRenderer2D& gfx) override; void onDraw(ogfx::BasicRenderer2D& gfx) override;

View file

@ -41,7 +41,7 @@ namespace ogfx
OSTD_PARAM_GETSET(Color, OuterCircleColor, m_outerCircleColor); OSTD_PARAM_GETSET(Color, OuterCircleColor, m_outerCircleColor);
private: 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); RadioButton& create(RadioButtonGroup& group, const String& text);
void __update_size(ogfx::BasicRenderer2D& gfx); void __update_size(ogfx::BasicRenderer2D& gfx);
void __set_selected(bool selected); void __set_selected(bool selected);

View file

@ -26,7 +26,7 @@ namespace ogfx
{ {
namespace gui namespace gui
{ {
RootWidget::RootWidget(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) RootWidget::RootWidget(Window& window) : Widget({ 0, 0, 0, 0 }, window)
{ {
setRootChild(); setRootChild();
disableFocus(); disableFocus();
@ -37,7 +37,7 @@ namespace ogfx
void RootWidget::onWindowResized(const Event& event) void RootWidget::onWindowResized(const Event& event)
{ {
Window& win = cast<Window&>(getWindow()); //TODO: Potentially unsage? Window& win = getWindow();
f32 offset_y = 0; f32 offset_y = 0;
if (win.m_menubar.isVisible()) if (win.m_menubar.isVisible())
offset_y += win.m_menubar.geth(); offset_y += win.m_menubar.geth();

View file

@ -29,7 +29,7 @@ namespace ogfx
class RootWidget : public Widget class RootWidget : public Widget
{ {
public: public:
RootWidget(WindowCore& window); RootWidget(Window& window);
void onWindowResized(const Event& event) override; void onWindowResized(const Event& event) override;
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;
void onDraw(ogfx::BasicRenderer2D& gfx) override; void onDraw(ogfx::BasicRenderer2D& gfx) override;

View file

@ -30,7 +30,7 @@ namespace ogfx
class VerticalScrollBar : public Widget class VerticalScrollBar : public Widget
{ {
public: 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); VerticalScrollBar& create(void);
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;
void afterDraw(ogfx::BasicRenderer2D& gfx) override; void afterDraw(ogfx::BasicRenderer2D& gfx) override;
@ -68,7 +68,7 @@ namespace ogfx
class HorizontalScrollbar : public Widget class HorizontalScrollbar : public Widget
{ {
public: 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); HorizontalScrollbar& create(void);
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;
void afterDraw(ogfx::BasicRenderer2D& gfx) override; void afterDraw(ogfx::BasicRenderer2D& gfx) override;
@ -106,7 +106,7 @@ namespace ogfx
class ScrollableWidget : public Widget class ScrollableWidget : public Widget
{ {
public: 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); ScrollableWidget& create(void);
virtual void onUpdate(void) override; virtual void onUpdate(void) override;
void drawScrollbars(ogfx::BasicRenderer2D& gfx); void drawScrollbars(ogfx::BasicRenderer2D& gfx);

View file

@ -29,7 +29,7 @@ namespace ogfx
class Slider : public Widget class Slider : public Widget
{ {
public: 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); Slider& create(bool vertical, f32 min, f32 max, f32 step);
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;
void onMouseReleased(const Event& event) override; void onMouseReleased(const Event& event) override;

View file

@ -86,7 +86,7 @@ namespace ogfx
public: public:
using ChevronDrawCallback = std::function<void(TreeView& sender, const Item& item, const Rectangle& bounds, BasicRenderer2D& gfx)>; using ChevronDrawCallback = std::function<void(TreeView& sender, const Item& item, const Rectangle& bounds, BasicRenderer2D& gfx)>;
inline TreeView(WindowCore& window) : ScrollableWidget(window) { create(); } inline TreeView(Window& window) : ScrollableWidget(window) { create(); }
TreeView& create(void); TreeView& create(void);
void applyTheme(const ostd::Stylesheet& theme) override; void applyTheme(const ostd::Stylesheet& theme) override;
void onDraw(ogfx::BasicRenderer2D& gfx) override; void onDraw(ogfx::BasicRenderer2D& gfx) override;

View file

@ -31,7 +31,7 @@ namespace ogfx
ostd::BaseObject* Widget::s_dragAndDropData { nullptr }; ostd::BaseObject* Widget::s_dragAndDropData { nullptr };
bool Widget::s_hasDragAndDropData { false }; 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; m_window = &window;
} }
@ -39,8 +39,8 @@ namespace ogfx
Widget::~Widget(void) Widget::~Widget(void)
{ {
if (m_window && m_tabIndex >= 0) if (m_window && m_tabIndex >= 0)
cast<Window&>(*m_window).getFocusManager().unregisterTabIndex(*this); m_window->getFocusManager().unregisterTabIndex(*this);
auto& fm = cast<Window&>(*m_window).getFocusManager(); auto& fm = m_window->getFocusManager();
if (fm.getFocused() == this) if (fm.getFocused() == this)
fm.clearFocus(); // you'd add this method fm.clearFocus(); // you'd add this method
} }
@ -240,7 +240,7 @@ namespace ogfx
bool Widget::setTabIndex(i32 index) bool Widget::setTabIndex(i32 index)
{ {
if (cast<Window&>(getWindow()).getFocusManager().registerTabIndex(*this, index)) if (getWindow().getFocusManager().registerTabIndex(*this, index))
{ {
m_tabIndex = index; m_tabIndex = index;
return true; return true;

View file

@ -36,6 +36,7 @@ namespace ogfx
{ {
namespace gui namespace gui
{ {
class Window;
class Widget : public ostd::BaseObject, public Rectangle class Widget : public ostd::BaseObject, public Rectangle
{ {
private: struct ThemeOverride private: struct ThemeOverride
@ -65,7 +66,7 @@ namespace ogfx
public: using EventCallback = std::function<void(const Event&)>; public: using EventCallback = std::function<void(const Event&)>;
public: public:
// ================================== MAIN ================================= // ================================== MAIN =================================
Widget(const Rectangle& bounds, WindowCore& window); Widget(const Rectangle& bounds, Window& window);
Widget(Widget&&) = default; Widget(Widget&&) = default;
Widget& operator=(Widget&&) = delete; Widget& operator=(Widget&&) = delete;
Widget(const Widget&) = delete; Widget(const Widget&) = delete;
@ -161,7 +162,7 @@ namespace ogfx
inline static void clearDragAndDropData(void) { s_dragAndDropData = nullptr; } inline static void clearDragAndDropData(void) { s_dragAndDropData = nullptr; }
inline static ostd::BaseObject* getDragAndDropData(void) { return s_dragAndDropData; } inline static ostd::BaseObject* getDragAndDropData(void) { return s_dragAndDropData; }
inline i32 getTabIndex(void) const { return m_tabIndex; } 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 Widget* getParent(void) { return m_parent; }
inline const Widget* getParent(void) const { return m_parent; } inline const Widget* getParent(void) const { return m_parent; }
inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; } inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; }
@ -235,7 +236,7 @@ namespace ogfx
private: private:
// ======= CORE ======= // ======= CORE =======
WindowCore* m_window { nullptr }; Window* m_window { nullptr };
Widget* m_parent { nullptr }; Widget* m_parent { nullptr };
WidgetManager m_widgets; WidgetManager m_widgets;
std::unique_ptr<Layout> m_layout; std::unique_ptr<Layout> m_layout;

View file

@ -232,28 +232,64 @@ namespace ogfx
Vec2 BasicRenderer2D::getStringDimensions(const String& message, i32 fontSize, TTF_Font* font) 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<Vec2> BasicRenderer2D::getStringDimensionsPerCharacter(const String& message, i32 fontSize, TTF_Font* font)
{
if (!isValid()) return { };
if (fontSize <= 0) fontSize = m_fontSize; if (fontSize <= 0) fontSize = m_fontSize;
if (!font) font = m_font; if (!font) font = m_font;
auto l_buildCacheKey = [&](void) -> String {
return String("").add(message).add(fontSize).add(reinterpret_cast<uintptr_t>(font));
};
String cacheKey = l_buildCacheKey();
auto it = m_strDimsCache.find(cacheKey);
if (it != m_strDimsCache.end())
{
m_cacheHitCount++;
return it->second;
}
i32 oldFontSize = getFontSize(); i32 oldFontSize = getFontSize();
setFontSize(fontSize); setFontSize(fontSize);
auto glyphs = m_fontGlyphAtlas.processString(message, font, fontSize); auto glyphs = m_fontGlyphAtlas.processString(message, font, fontSize);
if (glyphs.empty()) return { 0, 0 }; if (glyphs.empty()) return { };
f32 totalWidth = 0; stdvec<Vec2> dimensions;
dimensions.reserve(message.len());
f32 charWidth = 0;
for (size_t i = 0; i < glyphs.size(); i++) for (size_t i = 0; i < glyphs.size(); i++)
{ {
totalWidth += glyphs[i]->advance; charWidth += glyphs[i]->advance;
if (i > 0) if (i > 0)
{ {
i32 kern = 0; i32 kern = 0;
TTF_GetGlyphKerning(font, glyphs[i - 1]->codepoint, glyphs[i]->codepoint, &kern); 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); 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 ===================================================== // ===================================================== UTILS =====================================================

View file

@ -26,6 +26,7 @@
#include <ogfx/resources/Image.hpp> #include <ogfx/resources/Image.hpp>
#include <ostd/io/IOHandlers.hpp> #include <ostd/io/IOHandlers.hpp>
#include <ogfx/render/FontGlyphAtlas.hpp> #include <ogfx/render/FontGlyphAtlas.hpp>
#include <ostd/io/StaticHashMap.hpp>
namespace ogfx namespace ogfx
{ {
@ -70,8 +71,11 @@ namespace ogfx
i32 openFont(const ostd::UByte resource_data[], u32 data_size, i32 fontSize = 0); i32 openFont(const ostd::UByte resource_data[], u32 data_size, i32 fontSize = 0);
i32 setFontSize(i32 fontSize); i32 setFontSize(i32 fontSize);
Vec2 getStringDimensions(const String& message, i32 fontSize = 0, TTF_Font* font = nullptr); Vec2 getStringDimensions(const String& message, i32 fontSize = 0, TTF_Font* font = nullptr);
stdvec<Vec2> getStringDimensionsPerCharacter(const String& message, i32 fontSize = 0, TTF_Font* font = nullptr);
inline u32 getDrawCallCount(void) { return m_lastFrameDrawCallCount; } 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 bool hasOpenFont(void) { return m_fontOpen; }
inline TTF_Font* getSDLFont(void) { return m_font; } inline TTF_Font* getSDLFont(void) { return m_font; }
inline bool isValid(void) const { return m_initialized && m_fontOpen && (m_font != nullptr || m_fontFromMemory); } 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 }; SDL_Texture* m_lastUsedGlyphAtlasTex { nullptr };
SignalHandler m_sigHandler { *this }; SignalHandler m_sigHandler { *this };
ostd::StaticHashMap<String, stdvec<Vec2>> m_strDimsCache;
i32 m_cacheHitCount { 0 };
i32 m_cacheMissCount { 0 };
inline static constexpr i32 DefaultFontSize { 16 }; inline static constexpr i32 DefaultFontSize { 16 };
inline static constexpr i32 MaxStringDimsCacheSize { 512 };
}; };
} }

View file

@ -251,6 +251,17 @@ namespace ostd
m_entries.pop_back(); 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<u32>(i)].first] = i;
}
// ============================================================ // ============================================================
// Removal // Removal
// ============================================================ // ============================================================

View file

@ -24,7 +24,7 @@
#include <ogfx/ogfx.hpp> #include <ogfx/ogfx.hpp>
#include <ostd/utils/Async.hpp> #include <ostd/utils/Async.hpp>
ogfx::WindowCore::FileDialogFilterList filters = { ogfx::gui::Window::FileDialogFilterList filters = {
{ "Image files", { "png", "jpg", "jpeg", "bmp" } }, { "Image files", { "png", "jpg", "jpeg", "bmp" } },
{ "All files", { "*" } } { "All files", { "*" } }
}; };
@ -279,6 +279,14 @@ class TestWindow : public Window
m_drawCallsLbl.addThemeOverride("@.label.padding", ostd::Rectangle { 10, 5, 0, 0 }); m_drawCallsLbl.addThemeOverride("@.label.padding", ostd::Rectangle { 10, 5, 0, 0 });
getStatusBar().addWidget(m_drawCallsLbl, { 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] { m_progressJob.start([this] {
while (true) while (true)
{ {
@ -352,6 +360,8 @@ class TestWindow : public Window
{ {
// gfx.fillRect(m_tabs.getGlobalPureContentBounds(), { 0, 255, 0, 100 }); // gfx.fillRect(m_tabs.getGlobalPureContentBounds(), { 0, 255, 0, 100 });
m_drawCallsLbl.setText(String("DrawCalls: ").add(gfx.getDrawCallCount())); 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 void onFixedUpdate(void) override
@ -377,6 +387,8 @@ class TestWindow : public Window
Label m_slideLbl { *this }; Label m_slideLbl { *this };
TreeView m_list { *this }; TreeView m_list { *this };
Label m_drawCallsLbl { *this }; Label m_drawCallsLbl { *this };
Label m_cacheHitsLbl { *this };
Label m_cacheMissesLbl { *this };
ComboBox m_combo { *this }; ComboBox m_combo { *this };
enum MenuId : i32 { New = 1, Open, Save, SaveAs, Exit, CopyRaw, CopyFormatted }; 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.initialize(1200, 900, "OmniaFramework - Test Window");
window.setClearColor({ 0, 0, 0 }); window.setClearColor({ 0, 0, 0 });
window.setPosition({ 50, 50 }); window.setPosition({ 50, 50 });
// window.setWindowState(ogfx::WindowCore::eWindowState::Maximized); // window.setWindowState(ogfx::gui::Window::eWindowState::Maximized);
window.mainLoop(); window.mainLoop();
return 0; return 0;
} }