diff --git a/CMakeLists.txt b/CMakeLists.txt
index bf793f7..a33ba72 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -89,7 +89,7 @@ list(APPEND OGFX_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/RawTextInput.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/Window.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/WindowOutputHandler.cpp
- ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/Widget.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/Widgets.cpp
# render
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/render/BasicRenderer.cpp
diff --git a/other/TODO.txt b/other/TODO.txt
index 37be370..8144f94 100644
--- a/other/TODO.txt
+++ b/other/TODO.txt
@@ -1 +1,5 @@
-Add Color constants to ostd::Color
\ No newline at end of file
+***Add Color constants to ostd::Color
+Add MouseEntered/MouseExited callbacks
+Fix mouse detection currently using local coordinates
+implement JSON theme loading
+Implemenmt MouseDragged callback
diff --git a/src/ogfx/gui/Events.hpp b/src/ogfx/gui/Events.hpp
index b2040d9..ce6b7d0 100644
--- a/src/ogfx/gui/Events.hpp
+++ b/src/ogfx/gui/Events.hpp
@@ -25,6 +25,10 @@
namespace ogfx
{
class WindowCore;
+ namespace gui
+ {
+ class Widget;
+ }
class WindowResizedData : public ostd::BaseObject
{
public:
@@ -55,6 +59,7 @@ namespace ogfx
float position_x;
float position_y;
eButton button;
+ gui::Widget* mousePressedOnWidget { nullptr };
WindowCore& parentWindow;
};
class KeyEventData : public ostd::BaseObject
@@ -73,4 +78,23 @@ namespace ogfx
eKeyEvent eventType;
WindowCore& parentWindow;
};
+ namespace gui
+ {
+ class Event
+ {
+ public:
+ inline Event(WindowCore& _window) : window(_window) { }
+ inline void handle(void) { m_handled = true; }
+ inline bool isHandled(void) const { return m_handled; }
+
+ public:
+ WindowCore& window;
+ WindowResizedData* windowResized { nullptr };
+ MouseEventData* mouse { nullptr };
+ KeyEventData* keyboard { nullptr };
+
+ private:
+ bool m_handled { false };
+ };
+ }
}
diff --git a/src/ogfx/gui/Themes.hpp b/src/ogfx/gui/Themes.hpp
new file mode 100644
index 0000000..c4a9b96
--- /dev/null
+++ b/src/ogfx/gui/Themes.hpp
@@ -0,0 +1,90 @@
+/*
+ OmniaFramework - A collection of useful functionality
+ Copyright (C) 2025 OmniaX-Dev
+
+ This file is part of OmniaFramework.
+
+ OmniaFramework is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OmniaFramework is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OmniaFramework. If not, see .
+*/
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace ogfx
+{
+ namespace gui
+ {
+ struct Theme
+ {
+ public: using ThemeValue = std::variant;
+ public:
+ inline Theme(bool blank = false)
+ {
+ if (blank) return;
+
+ set("window.backgroundColor", ostd::Colors::Black);
+
+ set("label.textColor", ostd::Colors::White);
+ set("label.backgroundColor", ostd::Colors::Transparent);
+ set("label.borderColor", ostd::Colors::White);
+ set("label.fontSize", 20);
+ set("label.borderRadius", 20);
+ set("label.borderWidth", 2);
+ set("label.showBackground", false);
+ set("label.showBorder", false);
+ set("label.padding", ostd::Rectangle { 5, 5, 5, 5 });
+ }
+
+ inline Theme& blank(void)
+ {
+ m_values.clear();
+ return *this;
+ }
+
+ inline Theme& loadFromJsonFile(const ostd::String& jsonFilePath)
+ {
+ return *this; //TODO: Implement
+ }
+
+ inline void set(const std::string& key, ThemeValue value)
+ {
+ m_values[key] = std::move(value);
+ }
+
+ inline const ThemeValue* __get(const ostd::String& key) const
+ {
+ auto it = m_values.find(key);
+ return it != m_values.end() ? &it->second : nullptr;
+ }
+
+ template
+ inline T get(const ostd::String& key, const T& fallback) const
+ {
+ if (auto v = __get(key))
+ {
+ if (auto p = std::get_if(v))
+ return *p;
+ }
+ return fallback;
+ }
+
+ private:
+ std::unordered_map m_values;
+ };
+ }
+}
diff --git a/src/ogfx/gui/Widget.cpp b/src/ogfx/gui/Widget.cpp
deleted file mode 100644
index f6ae4e3..0000000
--- a/src/ogfx/gui/Widget.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- OmniaFramework - A collection of useful functionality
- Copyright (C) 2025 OmniaX-Dev
-
- This file is part of OmniaFramework.
-
- OmniaFramework is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- OmniaFramework is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with OmniaFramework. If not, see .
-*/
-
-#include "Widget.hpp"
-
-namespace ogfx
-{
- namespace gui
- {
-
- }
-}
diff --git a/src/ogfx/gui/Widget.hpp b/src/ogfx/gui/Widget.hpp
deleted file mode 100644
index 38b8d75..0000000
--- a/src/ogfx/gui/Widget.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- OmniaFramework - A collection of useful functionality
- Copyright (C) 2025 OmniaX-Dev
-
- This file is part of OmniaFramework.
-
- OmniaFramework is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- OmniaFramework is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with OmniaFramework. If not, see .
-*/
-
-#pragma once
-
-#include
-#include
-
-namespace ogfx
-{
- namespace gui
- {
- class Widget : public ostd::BaseObject, public ostd::Rectangle
- {
- public:
- private:
- };
- }
-}
diff --git a/src/ogfx/gui/Widgets.cpp b/src/ogfx/gui/Widgets.cpp
new file mode 100644
index 0000000..812f01b
--- /dev/null
+++ b/src/ogfx/gui/Widgets.cpp
@@ -0,0 +1,526 @@
+/*
+ OmniaFramework - A collection of useful functionality
+ Copyright (C) 2025 OmniaX-Dev
+
+ This file is part of OmniaFramework.
+
+ OmniaFramework is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OmniaFramework is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OmniaFramework. If not, see .
+*/
+
+#include "Widgets.hpp"
+#include
+#include
+
+namespace ogfx
+{
+ namespace gui
+ {
+ WidgetManager::WidgetManager(WindowCore& window, Widget& owner) : m_window(window), m_owner(owner)
+ {
+ //
+ }
+
+ bool WidgetManager::hasWidget(Widget& widget)
+ {
+ bool found = std::ranges::find(m_widgetList, &widget) != m_widgetList.end();
+ return found && widget.isValid();
+ }
+
+ bool WidgetManager::requestFocus(Widget& widget)
+ {
+ if (!hasWidget(widget)) return false;
+ if (&widget == m_focused) return true;
+ for (auto& w : m_widgetList)
+ {
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ if (w->m_focused)
+ {
+ w->m_focused = false;
+ w->onFocusLost(Event(m_window));
+ }
+ else
+ w->m_focused = false;
+ }
+ widget.m_focused = true;
+ widget.onFocusGained(Event(m_window));
+ m_focused = &widget;
+ return true;
+ }
+
+ bool WidgetManager::addWidget(Widget& widget)
+ {
+ if (hasWidget(widget)) return false;
+ widget.m_parent = &m_owner;
+ m_widgetList.push_back(&widget);
+ std::ranges::sort(m_widgetList, {}, [](Widget* w) {
+ return w->m_zIndex;
+ });
+ return true;
+ }
+
+ Widget* WidgetManager::focusNext(void)
+ {
+ if (m_widgetList.empty())
+ return nullptr;
+
+ Widget* next = nullptr;
+ Widget* smallest = nullptr;
+
+ int32_t currentTabIndex = (m_focused != nullptr ? m_focused->m_tabIndex : std::numeric_limits::max());
+ for (Widget* w : m_widgetList)
+ {
+ int tab_i = w->m_tabIndex;
+ if (tab_i < 0) continue;
+ if (!smallest || tab_i < smallest->m_tabIndex)
+ smallest = w;
+ if (tab_i > currentTabIndex)
+ {
+ if (!next || tab_i < next->m_tabIndex)
+ next = w;
+ }
+ }
+ Widget* w = next ? next : smallest;
+ requestFocus(*w);
+ return w;
+ }
+
+ void WidgetManager::draw(ogfx::BasicRenderer2D& gfx)
+ {
+ for (auto& w : m_widgetList)
+ {
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ w->__draw(gfx);
+ }
+ }
+
+ void WidgetManager::update(void)
+ {
+ for (auto& w : m_widgetList)
+ {
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ w->__update();
+ }
+ }
+
+ void WidgetManager::onThemeApplied(const Theme& theme)
+ {
+ for (auto& w : m_widgetList)
+ {
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ w->__applyTheme(theme, true);
+ }
+ }
+
+ void WidgetManager::onMousePressed(const Event& event)
+ {
+ for (int32_t i = m_widgetList.size() - 1; i >= 0; i--)
+ {
+ Widget* w = m_widgetList[i];
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ if (!w->contains(event.mouse->position_x, event.mouse->position_y, true))
+ continue;
+ w->__onMousePressed(event);
+ m_mousePressedOnWidget = w;
+ if (event.isHandled())
+ break;
+ }
+ }
+
+ void WidgetManager::onMouseReleased(const Event& event)
+ {
+ for (int32_t i = m_widgetList.size() - 1; i >= 0; i--)
+ {
+ Widget* w = m_widgetList[i];
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ event.mouse->mousePressedOnWidget = m_mousePressedOnWidget;
+ w->__onMouseReleased(event);
+ requestFocus(*w);
+ m_mousePressedOnWidget = nullptr;
+ if (event.isHandled())
+ break;
+ }
+ }
+
+ void WidgetManager::onMouseMoved(const Event& event)
+ {
+ for (int32_t i = m_widgetList.size() - 1; i >= 0; i--)
+ {
+ Widget* w = m_widgetList[i];
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ if (!w->contains(event.mouse->position_x, event.mouse->position_y, true))
+ continue;
+ w->__onMouseMoved(event);
+ if (event.isHandled())
+ break;
+ }
+ }
+
+ void WidgetManager::onMouseDragged(const Event& event)
+ {
+ for (int32_t i = m_widgetList.size() - 1; i >= 0; i--)
+ {
+ Widget* w = m_widgetList[i];
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ if (!w->contains(event.mouse->position_x, event.mouse->position_y, true))
+ continue;
+ w->__onMouseDragged(event);
+ if (event.isHandled())
+ break;
+ }
+ }
+
+ void WidgetManager::onKeyPressed(const Event& event)
+ {
+ if (!m_focused) return;
+ m_focused->__onKeyPressed(event);
+ }
+
+ void WidgetManager::onKeyReleased(const Event& event)
+ {
+ if (!m_focused) return;
+ m_focused->__onKeyReleased(event);
+ if (m_tabNavigationEnabled && event.keyboard->keyCode == SDLK_TAB)
+ focusNext();
+ }
+
+ void WidgetManager::onTextEntered(const Event& event)
+ {
+ if (!m_focused) return;
+ m_focused->__onTextEntered(event);
+ }
+
+ void WidgetManager::onWindowClosed(const Event& event)
+ {
+ for (auto& w : m_widgetList)
+ {
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ w->__onWindowClosed(event);
+ if (event.isHandled())
+ break;
+ }
+ }
+
+ void WidgetManager::onWindowResized(const Event& event)
+ {
+ for (auto& w : m_widgetList)
+ {
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ w->__onWindowResized(event);
+ if (event.isHandled())
+ break;
+ }
+ }
+
+ void WidgetManager::onWindowFocused(const Event& event)
+ {
+ for (auto& w : m_widgetList)
+ {
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ w->__onWIndowFocused(event);
+ if (event.isHandled())
+ break;
+ }
+ }
+
+ void WidgetManager::onWindowFocusLost(const Event& event)
+ {
+ for (auto& w : m_widgetList)
+ {
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ w->__onWindowFocusLost(event);
+ if (event.isHandled())
+ break;
+ }
+ }
+
+
+
+
+ Widget::Widget(const ostd::Rectangle& bounds, WindowCore& window) : Rectangle(bounds), m_widgets(window, *this)
+ {
+ m_window = &window;
+ }
+
+ bool Widget::addChild(Widget& child)
+ {
+ if (!m_allowChildren)
+ return false;
+ return m_widgets.addWidget(child);
+ }
+
+ ostd::Vec2 Widget::getGlobalPosition(void) const
+ {
+ ostd::Vec2 glob = getPosition();
+ if (!m_rootChild && m_parent != nullptr)
+ glob += m_parent->getGlobalPosition();
+ return glob;
+ }
+
+ void Widget::applyThemeValue(Theme& theme, const ostd::String& key, Theme::ThemeValue value, bool propagate)
+ {
+ auto currentValue = theme.__get(key);
+ if (currentValue == nullptr) return;
+ theme.set(key, value);
+ __applyTheme(theme, propagate);
+ theme.set(key, *currentValue);
+ }
+
+ void Widget::__draw(ogfx::BasicRenderer2D& gfx)
+ {
+ if (isDrawBoxEnabled())
+ gfx.fillRect({ getGlobalPosition(), getSize() }, getDrawBoxColor());
+ onDraw(gfx);
+ if (hasChildren())
+ m_widgets.draw(gfx);
+ }
+
+ void Widget::__update(void)
+ {
+ onUpdate();
+ if (hasChildren())
+ m_widgets.update();
+ }
+
+ void Widget::__onMousePressed(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onMousePressed(event);
+ if (!event.isHandled())
+ {
+ if (callback_onMousePressed)
+ callback_onMousePressed(event);
+ onMousePressed(event);
+ }
+ }
+
+ void Widget::__onMouseReleased(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onMouseReleased(event);
+ if (!event.isHandled())
+ {
+ if (callback_onMouseReleased)
+ callback_onMouseReleased(event);
+ onMouseReleased(event);
+ }
+ }
+
+ void Widget::__onMouseMoved(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onMouseMoved(event);
+ if (!event.isHandled())
+ {
+ if (callback_onMouseMoved)
+ callback_onMouseMoved(event);
+ onMouseMoved(event);
+ }
+ }
+
+ void Widget::__onMouseDragged(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onMouseDragged(event);
+ if (!event.isHandled())
+ {
+ if (callback_onMouseDragged)
+ callback_onMouseDragged(event);
+ onMouseDragged(event);
+ }
+ }
+
+ void Widget::__onKeyPressed(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onKeyPressed(event);
+ if (!event.isHandled())
+ {
+ if (callback_onKeyPressed)
+ callback_onKeyPressed(event);
+ onKeyPressed(event);
+ }
+ }
+
+ void Widget::__onKeyReleased(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onKeyReleased(event);
+ if (!event.isHandled())
+ {
+ if (callback_onKeyReleased)
+ callback_onKeyReleased(event);
+ onKeyReleased(event);
+ }
+ }
+
+ void Widget::__onTextEntered(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onTextEntered(event);
+ if (!event.isHandled())
+ {
+ if (callback_onTextEntered)
+ callback_onTextEntered(event);
+ onTextEntered(event);
+ }
+ }
+
+ void Widget::__onWindowClosed(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onWindowClosed(event);
+ if (!event.isHandled())
+ {
+ if (callback_onWindowClosed)
+ callback_onWindowClosed(event);
+ onWindowClosed(event);
+ }
+ }
+
+ void Widget::__onWindowResized(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onWindowResized(event);
+ if (!event.isHandled())
+ {
+ if (callback_onWindowResized)
+ callback_onWindowResized(event);
+ onWindowResized(event);
+ }
+ }
+
+ void Widget::__onWIndowFocused(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onWindowFocused(event);
+ if (!event.isHandled())
+ {
+ if (callback_onWindowFocused)
+ callback_onWindowFocused(event);
+ onWindowFocused(event);
+ }
+ }
+
+ void Widget::__onWindowFocusLost(const Event& event)
+ {
+ if (hasChildren())
+ m_widgets.onWindowFocusLost(event);
+ if (!event.isHandled())
+ {
+ if (callback_onWindowFocusLost)
+ callback_onWindowFocusLost(event);
+ onWindowFocusLost(event);
+ }
+ }
+
+ void Widget::__applyTheme(const Theme& theme, bool propagate)
+ {
+ if (propagate && hasChildren())
+ m_widgets.onThemeApplied(theme);
+ applyTheme(theme);
+ }
+
+
+
+ namespace widgets
+ {
+ RootWidget::RootWidget(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window)
+ {
+ m_rootChild = true;
+ setSize(static_cast(window.getWindowWidth()), static_cast(window.getWindowHeight()));
+ enableDrawBox();
+ setDrawBoxColor(window.getClearColor());
+ setTypeName("ogfx::gui::widgets::RootWidget");
+ }
+
+ void RootWidget::onWindowResized(const Event& event)
+ {
+ setSize(static_cast(event.windowResized->new_width), static_cast(event.windowResized->new_height));
+ }
+
+ void RootWidget::applyTheme(const Theme& theme)
+ {
+ setDrawBoxColor(theme.get("window.backgroundColor", getWindow().getClearColor()));
+ }
+
+
+
+
+ Label& Label::create(const ostd::String& text)
+ {
+ setText(text);
+ setPadding({ 5, 5, 5, 5 });
+ setTypeName("ogfx::gui::widgets::Label");
+ disableDrawBox();
+ enableBackground(false);
+ validate();
+ return *this;
+ }
+
+ void Label::applyTheme(const Theme& theme)
+ {
+ setColor(theme.get("label.textColor", ostd::Colors::White));
+ setBackGroundColor(theme.get("label.backgroundColor", ostd::Colors::Transparent));
+ setFontSize(theme.get("label.fontSize", 20));
+ m_borderRadius = theme.get("label.borderRadius", 10);
+ m_borderWidth = theme.get("label.borderWidth", 2);
+ m_showBorder = theme.get("label.showBorder", false);
+ m_borderColor = theme.get("label.borderColor", ostd::Colors::White);
+ enableBackground(theme.get("label.showBackground", false));
+ setPadding(theme.get("label.padding", { 5, 5, 5, 5 }));
+ }
+
+ void Label::onDraw(ogfx::BasicRenderer2D& gfx)
+ {
+ if (m_textChanged)
+ __update_size(gfx);
+ if (m_showBackground)
+ gfx.fillRoundRect({ getGlobalPosition(), getSize() }, m_backgroundColor, m_borderRadius);
+ if (m_showBorder)
+ gfx.drawRoundRect({ getGlobalPosition(), getSize() }, m_borderColor, m_borderRadius, m_borderWidth);
+ gfx.drawString(getText(), getGlobalPosition() + ostd::Vec2 { getPadding().left(), getPadding().top() }, getColor());
+ }
+
+ void Label::setText(const ostd::String& text)
+ {
+ m_text = text;
+ m_textChanged = true;
+ }
+
+ void Label::__update_size(ogfx::BasicRenderer2D& gfx)
+ {
+ auto size = gfx.getStringSize(getText(), getFontSize());
+ size.x += getPadding().left();
+ size.x += getPadding().right();
+ size.y += getPadding().top();
+ size.y += getPadding().bottom();
+ setSize({ static_cast(size.x), static_cast(size.y) });
+ m_textChanged = false;
+ }
+
+ }
+ }
+}
diff --git a/src/ogfx/gui/Widgets.hpp b/src/ogfx/gui/Widgets.hpp
new file mode 100644
index 0000000..a476a41
--- /dev/null
+++ b/src/ogfx/gui/Widgets.hpp
@@ -0,0 +1,226 @@
+/*
+ OmniaFramework - A collection of useful functionality
+ Copyright (C) 2025 OmniaX-Dev
+
+ This file is part of OmniaFramework.
+
+ OmniaFramework is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OmniaFramework is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OmniaFramework. If not, see .
+*/
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+namespace ogfx
+{
+ class BasicRenderer2D;
+ class WindowCore;
+ namespace gui
+ {
+ class Event;
+ class Widget;
+ class WidgetManager
+ {
+ public:
+ WidgetManager(WindowCore& window, Widget& owner);
+ bool hasWidget(Widget& widget);
+ bool requestFocus(Widget& widget);
+ bool addWidget(Widget& widget);
+ Widget* focusNext(void);
+
+ void draw(ogfx::BasicRenderer2D& gfx);
+ void update(void);
+ void onThemeApplied(const Theme& theme);
+ void onMousePressed(const Event& event);
+ void onMouseReleased(const Event& event);
+ void onMouseMoved(const Event& event);
+ void onMouseDragged(const Event& event);
+ void onKeyPressed(const Event& event);
+ void onKeyReleased(const Event& event);
+ void onTextEntered(const Event& event);
+ void onWindowClosed(const Event& event);
+ void onWindowResized(const Event& event);
+ void onWindowFocused(const Event& event);
+ void onWindowFocusLost(const Event& event);
+
+ inline int32_t widgetCount(void) const { return m_widgetList.size(); }
+ inline WindowCore& getWindow(void) { return m_window; }
+
+ private:
+ WindowCore& m_window;
+ Widget& m_owner;
+ std::vector m_widgetList;
+ Widget* m_focused { nullptr };
+ bool m_tabNavigationEnabled { true };
+ Widget* m_mousePressedOnWidget { nullptr };
+ };
+ class Widget : public ostd::BaseObject, public ostd::Rectangle
+ {
+ public: using EventCallback = std::function;
+ public:
+ Widget(const ostd::Rectangle& bounds, WindowCore& window);
+ bool addChild(Widget& child);
+ ostd::Vec2 getGlobalPosition(void) const;
+ virtual void applyTheme(const Theme& theme) = 0;
+ void applyThemeValue(Theme& theme, const ostd::String& key, Theme::ThemeValue value, bool propagate);
+
+ inline virtual void onDraw(ogfx::BasicRenderer2D& gfx) { }
+ inline virtual void onUpdate(void) { }
+
+ inline virtual void onMousePressed(const Event& event) { }
+ inline virtual void onMouseReleased(const Event& event) { }
+ inline virtual void onMouseMoved(const Event& event) { }
+ inline virtual void onMouseDragged(const Event& event) { }
+ inline virtual void onKeyPressed(const Event& event) { }
+ inline virtual void onKeyReleased(const Event& event) { }
+ inline virtual void onTextEntered(const Event& event) { }
+ inline virtual void onFocusGained(const Event& event) { }
+ inline virtual void onFocusLost(const Event& event) { }
+ inline virtual void onWindowClosed(const Event& event) { }
+ inline virtual void onWindowResized(const Event& event) { }
+ inline virtual void onWindowFocused(const Event& event) { }
+ inline virtual void onWindowFocusLost(const Event& event) { }
+
+ void __draw(ogfx::BasicRenderer2D& gfx);
+ void __update(void);
+ void __onMousePressed(const Event& event);
+ void __onMouseReleased(const Event& event);
+ void __onMouseMoved(const Event& event);
+ void __onMouseDragged(const Event& event);
+ void __onKeyPressed(const Event& event);
+ void __onKeyReleased(const Event& event);
+ void __onTextEntered(const Event& event);
+ void __onWindowClosed(const Event& event);
+ void __onWindowResized(const Event& event);
+ void __onWIndowFocused(const Event& event);
+ void __onWindowFocusLost(const Event& event);
+ void __applyTheme(const Theme& theme, bool propagate);
+
+ inline virtual void setMousePressedCallback(EventCallback callback) { callback_onMousePressed = callback; }
+ inline virtual void setMouseReleasedCallback(EventCallback callback) { callback_onMouseReleased = callback; }
+ inline virtual void setMouseMovedCallback(EventCallback callback) { callback_onMouseMoved = callback; }
+ inline virtual void setMouseDraggedCallback(EventCallback callback) { callback_onMouseDragged = callback; }
+ inline virtual void setKeyPressedCallback(EventCallback callback) { callback_onKeyPressed = callback; }
+ inline virtual void setKeyReleasedCallback(EventCallback callback) { callback_onKeyReleased = callback; }
+ inline virtual void setTextEnteredCallback(EventCallback callback) { callback_onTextEntered = callback; }
+ inline virtual void setWindowClosedCallback(EventCallback callback) { callback_onWindowClosed = callback; }
+ inline virtual void setWindowResizedCallback(EventCallback callback) { callback_onWindowResized = callback; }
+ inline virtual void setWIndowFocusedCallback(EventCallback callback) { callback_onWindowFocused = callback; }
+ inline virtual void setWindowFocusLostCallback(EventCallback callback) { callback_onWindowFocusLost = callback; }
+
+ inline bool hasChildren(void) const { return m_allowChildren && m_widgets.widgetCount() > 0; }
+ inline virtual bool isInvalid(void) const override { return ostd::BaseObject::isInvalid() || (m_parent == nullptr && !m_rootChild); }
+ inline void setTabIndex(int32_t tabIndex) { m_tabIndex = tabIndex; }
+ inline int32_t getTabIndex(void) const { return m_tabIndex; }
+ inline bool isFocused(void) const { return m_focused; }
+ inline WindowCore& getWindow(void) { return *m_window; }
+ inline Widget* getParent(void) { return m_parent; }
+ inline int32_t getZIndex(void) const { return m_zIndex; }
+ inline bool isChildrenEnabled(void) const { return m_allowChildren; }
+ inline void setPadding(const ostd::Rectangle& pad) { m_padding = pad; }
+ inline Rectangle getPadding(void) { return m_padding; }
+
+ protected:
+ inline void disableChildren(void) { m_allowChildren = false; }
+ inline void disableDrawBox(void) { m_drawBox = false; }
+ inline void enableDrawBox(void) { m_drawBox = true; }
+ inline bool isDrawBoxEnabled(void) const { return m_drawBox; }
+ inline void setDrawBoxColor(const ostd::Color& color) { m_drawBoxColor = color; }
+ inline ostd::Color getDrawBoxColor(void) { return m_drawBoxColor; }
+
+ protected:
+ bool m_rootChild { false };
+
+ EventCallback callback_onMousePressed { nullptr };
+ EventCallback callback_onMouseReleased { nullptr };
+ EventCallback callback_onMouseMoved { nullptr };
+ EventCallback callback_onMouseDragged { nullptr };
+ EventCallback callback_onKeyPressed { nullptr };
+ EventCallback callback_onKeyReleased { nullptr };
+ EventCallback callback_onTextEntered { nullptr };
+ EventCallback callback_onWindowClosed { nullptr };
+ EventCallback callback_onWindowResized { nullptr };
+ EventCallback callback_onWindowFocused { nullptr };
+ EventCallback callback_onWindowFocusLost { nullptr };
+
+ private:
+ WindowCore* m_window { nullptr };
+ Widget* m_parent { nullptr };
+ bool m_focused { false };
+ int32_t m_tabIndex { -1 };
+ int32_t m_zIndex { -1 };
+ WidgetManager m_widgets;
+ bool m_allowChildren { true };
+
+ bool m_drawBox { true };
+ ostd::Color m_drawBoxColor { 255, 0, 0 };
+
+ ostd::Rectangle m_padding { 0, 0, 0, 0 };
+
+ friend class WidgetManager;
+ };
+ namespace widgets
+ {
+ class RootWidget : public Widget
+ {
+ public:
+ RootWidget(WindowCore& window);
+ void onWindowResized(const Event& event) override;
+ void applyTheme(const Theme& theme) override;
+
+ private:
+ };
+ class Label : public Widget
+ {
+ public:
+ inline Label(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); }
+ inline Label(WindowCore& window, const ostd::String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); }
+ Label& create(const ostd::String& text);
+ void applyTheme(const Theme& theme) override;
+ void onDraw(ogfx::BasicRenderer2D& gfx) override;
+ void setText(const ostd::String& text);
+ inline ostd::String getText(void) const { return m_text; }
+ inline ostd::Color getColor(void) const { return m_color; }
+ inline void setColor(const ostd::Color& color) { m_color = color; }
+ inline int32_t getFontSize(void) const { return m_fontSize; }
+ inline void setFontSize(int32_t fontSize) { m_fontSize = fontSize; }
+ inline void setBackGroundColor(const ostd::Color& color) { m_backgroundColor = color; }
+ inline ostd::Color getBackgroundColor(const ostd::Color& color) { return m_backgroundColor; }
+ inline void enableBackground(bool enable = true) { m_showBackground = enable; }
+ inline bool isBackgoundEnabled(void) const { return m_showBackground; }
+
+ private:
+ void __update_size(ogfx::BasicRenderer2D& gfx);
+
+ private:
+ ostd::String m_text { "" };
+ bool m_textChanged { false };
+ int32_t m_fontSize { 20 };
+ ostd::Color m_color { 255, 255, 255 };
+ int32_t m_borderRadius { 10 };
+ int32_t m_borderWidth { 2 };
+ bool m_showBorder { false };
+ bool m_showBackground { false };
+ ostd::Color m_backgroundColor { ostd::Colors::Transparent };
+ ostd::Color m_borderColor { 255, 255, 255 };
+
+ private:
+ };
+ }
+ }
+}
diff --git a/src/ogfx/gui/Window.cpp b/src/ogfx/gui/Window.cpp
index 933fa1e..334175f 100644
--- a/src/ogfx/gui/Window.cpp
+++ b/src/ogfx/gui/Window.cpp
@@ -20,6 +20,7 @@
#include "Window.hpp"
#include "../../ostd/utils/Time.hpp"
+#include
namespace ogfx
{
@@ -74,9 +75,12 @@ namespace ogfx
connectSignal(ostd::tBuiltinSignals::MousePressed);
connectSignal(ostd::tBuiltinSignals::MouseReleased);
connectSignal(ostd::tBuiltinSignals::MouseMoved);
+ connectSignal(ostd::tBuiltinSignals::MouseMoved);
connectSignal(ostd::tBuiltinSignals::OnGuiEvent);
connectSignal(ostd::tBuiltinSignals::WindowClosed);
connectSignal(ostd::tBuiltinSignals::WindowResized);
+ connectSignal(ostd::tBuiltinSignals::WindowFocused);
+ connectSignal(ostd::tBuiltinSignals::WindowLostFocus);
__on_window_init(width, height, title);
}
@@ -160,6 +164,11 @@ namespace ogfx
SDL_PushEvent(&e);
}
+ void WindowCore::handleSignal(ostd::tSignal& signal)
+ {
+ __on_signal(signal);
+ }
+
MouseEventData WindowCore::get_mouse_state(void)
{
float mx = 0, my = 0;
@@ -216,6 +225,14 @@ namespace ogfx
{
close();
}
+ else if (event.type == SDL_EVENT_WINDOW_FOCUS_GAINED)
+ {
+ ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowFocused, ostd::tSignalPriority::RealTime, *this);
+ }
+ else if (event.type == SDL_EVENT_WINDOW_FOCUS_LOST)
+ {
+ ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowLostFocus, ostd::tSignalPriority::RealTime, *this);
+ }
else if (event.type == SDL_EVENT_WINDOW_RESIZED)
{
WindowResizedData wrd(*this, m_windowWidth, m_windowHeight, 0, 0);
@@ -267,6 +284,7 @@ namespace ogfx
+
void GraphicsWindow::__on_window_init(int32_t width, int32_t height, const ostd::String& title)
{
SDL_SetWindowResizable(m_window, false);
@@ -305,6 +323,11 @@ namespace ogfx
m_fpsUpdateTimer.update();
}
+ void GraphicsWindow::__on_signal(ostd::tSignal& signal)
+ {
+ onSignal(signal);
+ }
+
void GraphicsWindow::__on_event(SDL_Event& event)
{
onSDLEvent(event);
@@ -320,11 +343,24 @@ namespace ogfx
namespace gui
{
+ void Window::addWidget(Widget& widget)
+ {
+ m_rootWidget.addChild(widget);
+ setTheme(*m_guiTheme);
+ }
+
+ void Window::setTheme(const gui::Theme& theme)
+ {
+ m_guiTheme = &theme;
+ m_rootWidget.__applyTheme(theme, true);
+ }
+
void Window::__on_window_init(int32_t width, int32_t height, const ostd::String& title)
{
enableBlockingEvents();
setTypeName("ogfx::gui::Window");
m_gfx.init(*this);
+ loadDefaultTHeme();
onInitialize();
}
@@ -339,19 +375,70 @@ namespace ogfx
{
handle_events();
before_render();
+ m_rootWidget.__update();
+ m_rootWidget.__draw(m_gfx);
onRedraw(m_gfx);
- m_gfx.drawString("Hello World", { 100, 100 }, { 255, 0, 0 });
after_render();
}
}
- void Window::__on_event(SDL_Event& event)
+ void Window::__on_signal(ostd::tSignal& signal)
{
- if (event.type == SDL_EVENT_KEY_UP)
+ Event evt(*this);
+ if (signal.ID == ostd::tBuiltinSignals::WindowClosed)
{
- if (event.key.key == SDLK_ESCAPE)
+ m_rootWidget.__onWindowClosed(evt);
+ }
+ else if (signal.ID == ostd::tBuiltinSignals::WindowFocused)
+ {
+ m_rootWidget.__onWIndowFocused(evt);
+ }
+ else if (signal.ID == ostd::tBuiltinSignals::WindowLostFocus)
+ {
+ m_rootWidget.__onWindowFocusLost(evt);
+ }
+ else if (signal.ID == ostd::tBuiltinSignals::WindowResized)
+ {
+ evt.windowResized = &(ogfx::WindowResizedData&)signal.userData;
+ m_rootWidget.__onWindowResized(evt);
+ }
+ else if (signal.ID == ostd::tBuiltinSignals::MouseMoved)
+ {
+ evt.mouse = &(ogfx::MouseEventData&)signal.userData;
+ m_rootWidget.__onMouseMoved(evt);
+ }
+ else if (signal.ID == ostd::tBuiltinSignals::MousePressed)
+ {
+ evt.mouse = &(ogfx::MouseEventData&)signal.userData;
+ m_rootWidget.__onMousePressed(evt);
+ }
+ else if (signal.ID == ostd::tBuiltinSignals::MouseReleased)
+ {
+ evt.mouse = &(ogfx::MouseEventData&)signal.userData;
+ m_rootWidget.__onMouseReleased(evt);
+ }
+ else if (signal.ID == ostd::tBuiltinSignals::KeyPressed)
+ {
+ evt.keyboard = &(ogfx::KeyEventData&)signal.userData;
+ m_rootWidget.__onKeyPressed(evt);
+ }
+ else if (signal.ID == ostd::tBuiltinSignals::KeyReleased)
+ {
+ evt.keyboard = &(ogfx::KeyEventData&)signal.userData;
+ m_rootWidget.__onKeyReleased(evt);
+ if (evt.keyboard->keyCode == SDLK_ESCAPE)
close();
}
+ else if (signal.ID == ostd::tBuiltinSignals::TextEntered)
+ {
+ evt.keyboard = &(ogfx::KeyEventData&)signal.userData;
+ m_rootWidget.__onTextEntered(evt);
+ }
+ onSignal(signal);
+ }
+
+ void Window::__on_event(SDL_Event& event)
+ {
onSDLEvent(event);
}
diff --git a/src/ogfx/gui/Window.hpp b/src/ogfx/gui/Window.hpp
index 5a00df2..47c1375 100644
--- a/src/ogfx/gui/Window.hpp
+++ b/src/ogfx/gui/Window.hpp
@@ -25,8 +25,10 @@
#include
#include
#include
+#include
#include
#include
+#include
namespace ogfx
{
@@ -47,6 +49,7 @@ namespace ogfx
void setIcon(const ostd::String& iconFilePath);
void setBlockingEventsRefreshFPS(uint32_t fps);
void requestRedraw(void);
+ void handleSignal(ostd::tSignal& signal) override;
inline bool isInitialized(void) const { return m_initialized; }
inline bool isRunning(void) const { return m_running; }
@@ -74,6 +77,7 @@ namespace ogfx
inline virtual void __on_window_init(int32_t width, int32_t height, const ostd::String& title) { }
inline virtual void __on_window_destroy(void) { }
inline virtual void __on_window_close(void) { }
+ inline virtual void __on_signal(ostd::tSignal& signal) { }
inline virtual void __main_loop(void) = 0;
private:
@@ -106,6 +110,7 @@ namespace ogfx
public:
inline static constexpr int32_t MaxBlockingEventsFPS { 240 };
inline static constexpr int32_t DefaultBlockingEventsFPS { 30 };
+ inline static const gui::Theme DefaultTheme;
};
class GraphicsWindow : public WindowCore
{
@@ -121,6 +126,7 @@ namespace ogfx
inline virtual void onDestroy(void) { }
inline virtual void onClose(void) { }
inline virtual void onSDLEvent(SDL_Event& event) { }
+ inline virtual void onSignal(ostd::tSignal& signal) { }
inline int32_t getFPS(void) const { return m_fps; }
@@ -130,6 +136,7 @@ namespace ogfx
void __on_window_destroy(void) override;
void __on_window_close(void) override;
void __main_loop(void) override;
+ void __on_signal(ostd::tSignal& signal) override;
private:
int32_t m_fps { 0 };
@@ -147,12 +154,18 @@ namespace ogfx
public:
inline Window(void) { }
inline Window(int32_t width, int32_t height, const ostd::String& title) { initialize(width, height, title); }
+ void addWidget(Widget& widget);
inline virtual void onInitialize(void) { }
inline virtual void onDestroy(void) { }
inline virtual void onClose(void) { }
inline virtual void onSDLEvent(SDL_Event& event) { }
inline virtual void onRedraw(BasicRenderer2D& gfx) { }
+ inline virtual void onSignal(ostd::tSignal& signal) { }
+
+ inline const gui::Theme& theme(void) const { return *m_guiTheme; }
+ void setTheme(const gui::Theme& theme);
+ inline void loadDefaultTHeme(void) { setTheme(DefaultTheme); }
protected:
void __on_window_init(int32_t width, int32_t height, const ostd::String& title) override;
@@ -160,9 +173,14 @@ namespace ogfx
void __on_window_destroy(void) override;
void __on_window_close(void) override;
void __main_loop(void) override;
+ void __on_signal(ostd::tSignal& signal) override;
private:
BasicRenderer2D m_gfx;
+ widgets::RootWidget m_rootWidget { *this };
+ const gui::Theme* m_guiTheme { nullptr };
+
+ inline static const gui::Theme DefaultTheme;
};
}
}
diff --git a/src/ostd/data/Color.hpp b/src/ostd/data/Color.hpp
index 218f767..a9c591e 100755
--- a/src/ostd/data/Color.hpp
+++ b/src/ostd/data/Color.hpp
@@ -78,5 +78,62 @@ namespace ostd
uint8_t g;
uint8_t b;
uint8_t a;
+
+ public:
+ };
+ struct Colors
+ {
+ inline static const Color Transparent { 0, 0, 0, 0 };
+ inline static const Color Black { 0, 0, 0, 255 };
+ inline static const Color White { 255, 255, 255, 255 };
+ inline static const Color Gray { 128, 128, 128, 255 };
+ inline static const Color LightGray { 192, 192, 192, 255 };
+ inline static const Color DarkGray { 64, 64, 64, 255 };
+
+ inline static const Color Red { 255, 0, 0, 255 };
+ inline static const Color Green { 0, 255, 0, 255 };
+ inline static const Color Blue { 0, 0, 255, 255 };
+
+ inline static const Color Yellow { 255, 255, 0, 255 };
+ inline static const Color Cyan { 0, 255, 255, 255 };
+ inline static const Color Magenta { 255, 0, 255, 255 };
+
+ inline static const Color Orange { 255, 165, 0, 255 };
+ inline static const Color Purple { 128, 0, 128, 255 };
+ inline static const Color Brown { 139, 69, 19, 255 };
+ inline static const Color Pink { 255, 192, 203, 255 };
+ inline static const Color Lime { 50, 205, 50, 255 };
+ inline static const Color Olive { 128, 128, 0, 255 };
+ inline static const Color Teal { 0, 128, 128, 255 };
+ inline static const Color Navy { 0, 0, 128, 255 };
+ inline static const Color Maroon { 128, 0, 0, 255 };
+ inline static const Color Indigo { 75, 0, 130, 255 };
+ inline static const Color Gold { 255, 215, 0, 255 };
+ inline static const Color Silver { 192, 192, 192, 255 };
+ inline static const Color Beige { 245, 245, 220, 255 };
+ inline static const Color Coral { 255, 127, 80, 255 };
+ inline static const Color Salmon { 250, 128, 114, 255 };
+ inline static const Color Chocolate { 210, 105, 30, 255 };
+ inline static const Color Khaki { 240, 230, 140, 255 };
+ inline static const Color Lavender { 230, 230, 250, 255 };
+ inline static const Color Mint { 189, 252, 201, 255 };
+ inline static const Color SkyBlue { 135, 206, 235, 255 };
+ inline static const Color RoyalBlue { 65, 105, 225, 255 };
+ inline static const Color DeepSkyBlue { 0, 191, 255, 255 };
+ inline static const Color Turquoise { 64, 224, 208, 255 };
+ inline static const Color Aquamarine { 127, 255, 212, 255 };
+ inline static const Color ForestGreen { 34, 139, 34, 255 };
+ inline static const Color SeaGreen { 46, 139, 87, 255 };
+ inline static const Color SpringGreen { 0, 255, 127, 255 };
+ inline static const Color Firebrick { 178, 34, 34, 255 };
+ inline static const Color Crimson { 220, 20, 60, 255 };
+ inline static const Color Tomato { 255, 99, 71, 255 };
+ inline static const Color DarkOrange { 255, 140, 0, 255 };
+ inline static const Color DarkRed { 139, 0, 0, 255 };
+ inline static const Color DarkBlue { 0, 0, 139, 255 };
+ inline static const Color DarkGreen { 0, 100, 0, 255 };
+ inline static const Color DarkCyan { 0, 139, 139, 255 };
+ inline static const Color DarkMagenta { 139, 0, 139, 255 };
+ inline static const Color DarkYellow { 204, 204, 0, 255 };
};
}
diff --git a/src/ostd/math/Geometry.hpp b/src/ostd/math/Geometry.hpp
index 7e15383..09740ea 100755
--- a/src/ostd/math/Geometry.hpp
+++ b/src/ostd/math/Geometry.hpp
@@ -345,6 +345,11 @@ namespace ostd
inline virtual Vec2 bottomLeft(void) const { return Vec2(getx(), gety() + geth()); }
inline virtual Vec2 bottomRight(void) const { return Vec2(getx() + getw(), gety() + geth()); }
+ inline virtual float left(void) const { return getx(); }
+ inline virtual float right(void) const { return getw(); }
+ inline virtual float top(void) const { return gety(); }
+ inline virtual float bottom(void) const { return geth(); }
+
inline virtual bool intersects(Rectangle rect, bool includeBounds = true) const
{
if (includeBounds)
@@ -376,9 +381,9 @@ namespace ostd
inline virtual bool contains(Vec2 p, bool includeBounds = false) const
{
if (includeBounds)
- return p.x >= x && p.y >= y & p.x <= x + w && p.y <= y + h;
+ return p.x >= x && p.y >= y && p.x <= x + w && p.y <= y + h;
else
- return p.x > x && p.y > y & p.x < x + w && p.y < y + h;
+ return p.x > x && p.y > y && p.x < x + w && p.y < y + h;
}
inline virtual bool contains(float xx, float yy, bool includeBounds = false) const { return contains({ xx, yy }); }
diff --git a/src/ostd/utils/Signals.cpp b/src/ostd/utils/Signals.cpp
index 888a2ae..76a679c 100755
--- a/src/ostd/utils/Signals.cpp
+++ b/src/ostd/utils/Signals.cpp
@@ -27,6 +27,10 @@ namespace ostd
SignalHandler::m_windowResizedRecievers.reserve(SignalHandler::__SIGNAL_BUFFER_START_SIZE);
SignalHandler::m_windowClosedRecievers.clear();
SignalHandler::m_windowClosedRecievers.reserve(SignalHandler::__SIGNAL_BUFFER_START_SIZE);
+ SignalHandler::m_windowFocusedRecievers.clear();
+ SignalHandler::m_windowFocusedRecievers.reserve(SignalHandler::__SIGNAL_BUFFER_START_SIZE);
+ SignalHandler::m_windowLostFocusRecievers.clear();
+ SignalHandler::m_windowLostFocusRecievers.reserve(SignalHandler::__SIGNAL_BUFFER_START_SIZE);
SignalHandler::m_delegatedSignals.clear();
SignalHandler::m_delegatedSignals.reserve(SignalHandler::__DELEGATED_SIGNALS_BUFFER_START_SIZE);
SignalHandler::m_onGuiEventRecievers.clear();
@@ -71,6 +75,10 @@ namespace ostd
sig_list = &m_windowResizedRecievers;
else if (signal_id == tBuiltinSignals::WindowClosed)
sig_list = &m_windowClosedRecievers;
+ else if (signal_id == tBuiltinSignals::WindowLostFocus)
+ sig_list = &m_windowLostFocusRecievers;
+ else if (signal_id == tBuiltinSignals::WindowFocused)
+ sig_list = &m_windowFocusedRecievers;
else if (signal_id == tBuiltinSignals::OnGuiEvent)
sig_list = &m_onGuiEventRecievers;
else if (signal_id == tBuiltinSignals::BeforeSDLShutdown)
@@ -112,6 +120,10 @@ namespace ostd
m_windowResizedRecievers.push_back({ &object, signal_id });
else if (signal_id == tBuiltinSignals::WindowClosed)
m_windowClosedRecievers.push_back({ &object, signal_id });
+ else if (signal_id == tBuiltinSignals::WindowLostFocus)
+ m_windowLostFocusRecievers.push_back({ &object, signal_id });
+ else if (signal_id == tBuiltinSignals::WindowFocused)
+ m_windowFocusedRecievers.push_back({ &object, signal_id });
else if (signal_id == tBuiltinSignals::OnGuiEvent)
m_onGuiEventRecievers.push_back({ &object, signal_id });
else if (signal_id == tBuiltinSignals::BeforeSDLShutdown)
diff --git a/src/ostd/utils/Signals.hpp b/src/ostd/utils/Signals.hpp
index 8bc74aa..2b37b0f 100755
--- a/src/ostd/utils/Signals.hpp
+++ b/src/ostd/utils/Signals.hpp
@@ -49,6 +49,8 @@ namespace ostd
inline static constexpr uint32_t WindowResized = 0x1001;
inline static constexpr uint32_t WindowClosed = 0x1002;
+ inline static constexpr uint32_t WindowFocused = 0x1003;
+ inline static constexpr uint32_t WindowLostFocus = 0x1004;
/*********************/
inline static constexpr uint32_t CustomSignalBase = 0xFF0000;
@@ -107,6 +109,8 @@ namespace ostd
inline static std::vector m_textEnteredRecievers;
inline static std::vector m_windowResizedRecievers;
inline static std::vector m_windowClosedRecievers;
+ inline static std::vector m_windowFocusedRecievers;
+ inline static std::vector m_windowLostFocusRecievers;
inline static std::vector m_onGuiEventRecievers;
inline static std::vector m_beforeSDLShutdownRecievers;
/************************************/
diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp
index 4087ab4..1c9f929 100644
--- a/src/test/GuiTest.cpp
+++ b/src/test/GuiTest.cpp
@@ -48,9 +48,25 @@ class Window : public ogfx::gui::Window
inline Window(void) { }
inline void onInitialize(void) override
{
+ m_label1.setPosition(100, 200);
+ m_label1.setText("Hello World!");
+ m_label1.setMouseMovedCallback([&](const ogfx::gui::Event& event) -> void {
+ m_label1.applyThemeValue(m_theme, "label.backgroundColor", ostd::Colors::DarkBlue, false);
+ });
+ addWidget(m_label1);
+
+ m_label2.setPosition(100, 400);
+ m_label2.setText("Ciccia Bella!");
+ addWidget(m_label2);
+
+ m_theme.set("label.textColor", ostd::Colors::White);
+ m_theme.set("label.backgroundColor", ostd::Colors::DarkRed);
+ m_theme.set("label.showBackground", true);
+ m_theme.set("label.borderRadius", 0);
+ setTheme(m_theme);
}
- inline void handleSignal(ostd::tSignal& signal) override
+ inline void onSignal(ostd::tSignal& signal) override
{
if (signal.ID == ostd::tBuiltinSignals::KeyReleased)
{
@@ -62,10 +78,13 @@ class Window : public ogfx::gui::Window
void onRedraw(ogfx::BasicRenderer2D& gfx) override
{
- wout().fg(ostd::Color { 255, 0, 0 }).p("Hello ").fg(ostd::Color { 0, 0, 255 }).p("World");
+ // wout().fg(ostd::Color { 255, 0, 0 }).p("Hello ").fg(ostd::Color { 0, 0, 255 }).p("World");
}
private:
+ ogfx::gui::widgets::Label m_label1 { *this };
+ ogfx::gui::widgets::Label m_label2 { *this };
+ ogfx::gui::Theme m_theme = ogfx::gui::Theme();
};
int main(int argc, char** argv)