From 260c7054bcbb5d79e5a3aa543d3265b0ed895024 Mon Sep 17 00:00:00 2001 From: OmniaX-Dev Date: Mon, 6 Apr 2026 06:48:33 +0200 Subject: [PATCH] Added Basic Panel widget --- .clangd | 3 +- CMakeLists.txt | 6 +- extra/testTheme.txt | 18 +- other/TODO.txt | 27 ++ other/build.nr | 2 +- other/create_release.sh | 2 + src/ogfx/gui/Widgets.hpp | 307 +----------------- src/ogfx/gui/Window.hpp | 2 +- src/ogfx/gui/widgets/Containers.cpp | 61 ++++ src/ogfx/gui/widgets/Containers.hpp | 47 +++ src/ogfx/gui/widgets/Label.cpp | 84 +++++ src/ogfx/gui/widgets/Label.hpp | 66 ++++ .../gui/{Widgets.cpp => widgets/Widget.cpp} | 137 +++----- src/ogfx/gui/widgets/Widget.hpp | 256 +++++++++++++++ src/ogfx/ogfx.hpp | 1 + src/ogfx/render/BasicRenderer.cpp | 63 +++- src/ogfx/render/BasicRenderer.hpp | 9 +- src/ostd/math/Geometry.hpp | 17 + src/test/GuiTest.cpp | 40 +-- 19 files changed, 722 insertions(+), 426 deletions(-) create mode 100644 src/ogfx/gui/widgets/Containers.cpp create mode 100644 src/ogfx/gui/widgets/Containers.hpp create mode 100644 src/ogfx/gui/widgets/Label.cpp create mode 100644 src/ogfx/gui/widgets/Label.hpp rename src/ogfx/gui/{Widgets.cpp => widgets/Widget.cpp} (80%) create mode 100644 src/ogfx/gui/widgets/Widget.hpp diff --git a/.clangd b/.clangd index 122ab09..32169b9 100644 --- a/.clangd +++ b/.clangd @@ -1,2 +1,3 @@ CompileFlags: - CompilationDatabase: bin \ No newline at end of file + CompilationDatabase: bin + Add: ["--target=x86_64-w64-mingw32"] \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ac8ecb2..d5075b2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,11 @@ 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/Widgets.cpp + + # gui/widgets + ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Widget.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Containers.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Label.cpp # render ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/render/BasicRenderer.cpp diff --git a/extra/testTheme.txt b/extra/testTheme.txt index a2d78c1..a5f6f74 100644 --- a/extra/testTheme.txt +++ b/extra/testTheme.txt @@ -21,9 +21,9 @@ window.backgroundColor = Color(rgba(120, 120, 120, 255)) (panel) { backgroundColor = Color(#AAAAAAFF) - borderColor = Color(#000000FF) + borderColor = Color(#00FF00FF) borderRadius = 8 - borderWidth = 2 + borderWidth = 4 showBorder = true padding = Rect(15, 15, 15, 15) } @@ -42,6 +42,11 @@ window.backgroundColor = Color(rgba(120, 120, 120, 255)) borderColor = Color(#208080FF) } + +% (@testLabel2 label:pressed [mouseButton=left or mouseButton=right]) { +% ... + + (@testLabel2 label:pressed) { showBackground = true } @@ -70,4 +75,13 @@ window.backgroundColor = Color(rgba(120, 120, 120, 255)) %borderColor = Color(#000000FF) } +(@label3 label) { + textColor = $color2 + backgroundColor = Color(#000000FF) + fontSize = 20 + borderColor = Color(#FF0000FF) + borderWidth = 4 + borderRadius = 15 +} + % =================================================== diff --git a/other/TODO.txt b/other/TODO.txt index f6c4ea1..17d8377 100644 --- a/other/TODO.txt +++ b/other/TODO.txt @@ -3,3 +3,30 @@ ***Fix mouse detection currently using local coordinates ***implement file theme loading ***Implemenmt MouseDragged callback +***Add scissoring to only draw inside the bounds of containers +Add theme caching +Implement mouse scroll callback +Implement a way to determine on what widget (in the hierarchy chain) and event must stop +Implement margin for widgets +Implement conditional statements for stylesheet rules + + +Implement following widgets: + ***Label + Button + Image/Icon + Panel / Container + Title Label + Checkbox + Radio Button + Grouping + Text Input + Horizontal Slider + Vertical Slider + Image / Icon + ScrollView + ListBox + ComboBox + TreeView + Save/Open File Dialogs (and folder dialogs) + Tab Panel diff --git a/other/build.nr b/other/build.nr index e83bd43..a9a6e2f 100644 --- a/other/build.nr +++ b/other/build.nr @@ -1 +1 @@ -2046 +2049 diff --git a/other/create_release.sh b/other/create_release.sh index 1cb7819..0a0aa33 100755 --- a/other/create_release.sh +++ b/other/create_release.sh @@ -58,11 +58,13 @@ cp -r ../src/ostd/vendor/nlohmann $RELEASE_DIR/include/ostd/vendor/ # OGFX mkdir -p $RELEASE_DIR/include/ogfx/vendor/sdl3_gfx mkdir -p $RELEASE_DIR/include/ogfx/gui +mkdir -p $RELEASE_DIR/include/ogfx/gui/widgets mkdir -p $RELEASE_DIR/include/ogfx/render mkdir -p $RELEASE_DIR/include/ogfx/resources mkdir -p $RELEASE_DIR/include/ogfx/utils find ../src/ogfx -maxdepth 1 -name "*.h*" -exec cp {} $RELEASE_DIR/include/ogfx \; find ../src/ogfx/gui -maxdepth 1 -name "*.h*" -exec cp {} $RELEASE_DIR/include/ogfx/gui \; +find ../src/ogfx/gui/widgets -maxdepth 1 -name "*.h*" -exec cp {} $RELEASE_DIR/include/ogfx/gui/widgets \; find ../src/ogfx/render -maxdepth 1 -name "*.h*" -exec cp {} $RELEASE_DIR/include/ogfx/render \; find ../src/ogfx/resources -maxdepth 1 -name "*.h*" -exec cp {} $RELEASE_DIR/include/ogfx/resources \; find ../src/ogfx/utils -maxdepth 1 -name "*.h*" -exec cp {} $RELEASE_DIR/include/ogfx/utils \; diff --git a/src/ogfx/gui/Widgets.hpp b/src/ogfx/gui/Widgets.hpp index bf10646..d0912eb 100644 --- a/src/ogfx/gui/Widgets.hpp +++ b/src/ogfx/gui/Widgets.hpp @@ -1,301 +1,24 @@ /* - OmniaFramework - A collection of useful functionality - Copyright (C) 2025 OmniaX-Dev + OmniaFramework - A collection of useful functionality + Copyright (C) 2025 OmniaX-Dev - This file is part of OmniaFramework. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with OmniaFramework. If not, see . */ #pragma once -#include "gui/Events.hpp" -#include -#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 ostd::Stylesheet& 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 - { - private: struct ThemeOverride - { - ostd::String fullKey; - ostd::Stylesheet::TypeVariant value; - bool propagate; - }; - public: using EventCallback = std::function; - public: - Widget(const ostd::Rectangle& bounds, WindowCore& window); - bool addChild(Widget& child); - ostd::Vec2 getGlobalPosition(void) const; - using ostd::Rectangle::contains; - bool contains(ostd::Vec2 p, bool includeBounds = false) const override; - void enable(bool enable = true); - virtual void applyTheme(const ostd::Stylesheet& theme) = 0; - void addThemeOverride(const ostd::String& fullKey, ostd::Stylesheet::TypeVariant value, bool propagate = true); - void reloadTheme(void); - void setThemeQualifier(const ostd::String& qualifier, bool value = true); - bool getThemeQualifier(const ostd::String& qualifier); - bool addThemeID(const ostd::String& id); - bool removeThemeID(const ostd::String& id); - inline const ostd::Stylesheet::QualifierList& getThemeQualifierList(void) const { return m_qualifierList; } - - 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 onMouseEntered(const Event& event) { } - inline virtual void onMouseExited(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 __onMouseEntered(const Event& event); - void __onMouseExited(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 ostd::Stylesheet& 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 setMouseEnteredCallback(EventCallback callback) { callback_onMouseEntered = callback; } - inline virtual void setMouseExitedCallback(EventCallback callback) { callback_onMouseExited = 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; } - inline bool isMouseInside(void) const { return m_mouseInside; } - inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; } - inline const std::vector& getThemeIDList(void) const { return m_themeIDList; } - inline bool isEnabled(void) const { return m_enabled; } - inline bool isFocusEnabled(void) const { return m_allowFocus; } - inline void enableFocus(bool enable = true) { m_allowFocus = enable; } - inline void disableFocus(void) { enableFocus(false); } - inline void disabble(void) { enable(false); } - inline void enableStopEvents(bool enable = true) { m_stopEvents = enable; } - - template - inline T getThemeValue(const ostd::Stylesheet &theme, const ostd::String& key, const T& fallback) - { - return theme.get(key, fallback, getThemeIDList(), getThemeQualifierList()); - } - - 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_onMouseEntered { nullptr }; - EventCallback callback_onMouseExited { 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_mouseInside { false }; - bool m_allowFocus { true }; - bool m_enabled { true }; - bool m_stopEvents { false }; - MouseEventData::eButton m_pressedButton { MouseEventData::eButton::None }; - - std::vector m_themeIDList; - ostd::Stylesheet::QualifierList m_qualifierList { - { "disabled", false }, - { "pressed", false }, - { "hover", false }, - { "focused", false }, - { "active", false } - }; - std::vector m_themeOverrides; - - 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 ostd::Stylesheet& theme) override; - void onDraw(ogfx::BasicRenderer2D& gfx) override; - - private: - ostd::Color m_color { ostd::Colors::Transparent }; - }; - 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 ostd::Stylesheet& 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(void) { 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: - }; - class Panel : public Widget - { - public: - inline Panel(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } - Panel& create(void); - void applyTheme(const ostd::Stylesheet& theme) override; - void onDraw(ogfx::BasicRenderer2D& gfx) override; - inline void setBackGroundColor(const ostd::Color& color) { m_backgroundColor = color; } - inline ostd::Color getBackgroundColor(void) { return m_backgroundColor; } - - private: - ostd::Color m_backgroundColor { 150, 150, 150 }; - ostd::Color m_borderColor { 0, 0, 0 }; - int32_t m_borderRadius { 10 }; - int32_t m_borderWidth { 2 }; - bool m_showBorder { false }; - - }; - } - } -} +#include +#include diff --git a/src/ogfx/gui/Window.hpp b/src/ogfx/gui/Window.hpp index b01524f..24212dd 100644 --- a/src/ogfx/gui/Window.hpp +++ b/src/ogfx/gui/Window.hpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/ogfx/gui/widgets/Containers.cpp b/src/ogfx/gui/widgets/Containers.cpp new file mode 100644 index 0000000..5f729c9 --- /dev/null +++ b/src/ogfx/gui/widgets/Containers.cpp @@ -0,0 +1,61 @@ +/* + 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 "Containers.hpp" +#include "../../render/BasicRenderer.hpp" + +namespace ogfx +{ + namespace gui + { + namespace widgets + { + Panel& Panel::create(void) + { + setPadding({ 5, 5, 5, 5 }); + setTypeName("ogfx::gui::widgets::Panel"); + disableDrawBox(); + disableFocus(); + enableStopEvents(); + validate(); + return *this; + } + + void Panel::applyTheme(const ostd::Stylesheet& theme) + { + setBackGroundColor(getThemeValue(theme, "panel.backgroundColor", ostd::Colors::Gray)); + m_borderRadius = getThemeValue(theme, "panel.borderRadius", 8); + m_borderWidth = getThemeValue(theme, "panel.borderWidth", 2); + m_showBorder = getThemeValue(theme, "panel.showBorder", true); + m_borderColor = getThemeValue(theme, "panel.borderColor", ostd::Colors::Black); + setPadding(getThemeValue(theme, "panel.padding", { 15, 15, 15, 15 })); + } + + void Panel::onDraw(ogfx::BasicRenderer2D& gfx) + { + if (m_showBorder) + gfx.outlinedRoundRect({ getGlobalPosition(), getSize() }, getBackgroundColor(), m_borderColor, m_borderRadius, m_borderWidth); + else + gfx.fillRoundRect({ getGlobalPosition(), getSize() }, getBackgroundColor(), m_borderRadius); + gfx.drawRect(getGlobalContentBounds(), ostd::Colors::Aquamarine, 6); + } + } + } +} diff --git a/src/ogfx/gui/widgets/Containers.hpp b/src/ogfx/gui/widgets/Containers.hpp new file mode 100644 index 0000000..de68bbe --- /dev/null +++ b/src/ogfx/gui/widgets/Containers.hpp @@ -0,0 +1,47 @@ +/* + 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 + +namespace ogfx +{ + namespace gui + { + namespace widgets + { + class Panel : public Widget + { + public: + inline Panel(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); } + Panel& create(void); + void applyTheme(const ostd::Stylesheet& theme) override; + void onDraw(ogfx::BasicRenderer2D& gfx) override; + inline void setBackGroundColor(const ostd::Color& color) { m_backgroundColor = color; } + inline ostd::Color getBackgroundColor(void) { return m_backgroundColor; } + + private: + ostd::Color m_backgroundColor { 150, 150, 150 }; + + }; + } + } +} diff --git a/src/ogfx/gui/widgets/Label.cpp b/src/ogfx/gui/widgets/Label.cpp new file mode 100644 index 0000000..b52399d --- /dev/null +++ b/src/ogfx/gui/widgets/Label.cpp @@ -0,0 +1,84 @@ +/* + 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 "Label.hpp" +#include "../../render/BasicRenderer.hpp" + +namespace ogfx +{ + namespace gui + { + namespace widgets + { + Label& Label::create(const ostd::String& text) + { + setText(text); + setPadding({ 5, 5, 5, 5 }); + setTypeName("ogfx::gui::widgets::Label"); + disableDrawBox(); + disableChildren(); + enableBackground(false); + validate(); + return *this; + } + + void Label::applyTheme(const ostd::Stylesheet& theme) + { + setColor(getThemeValue(theme, "label.textColor", ostd::Colors::White)); + setBackGroundColor(getThemeValue(theme, "label.backgroundColor", ostd::Colors::Transparent)); + setFontSize(getThemeValue(theme, "label.fontSize", 20)); + m_borderRadius = getThemeValue(theme, "label.borderRadius", 10); + m_borderWidth = getThemeValue(theme, "label.borderWidth", 2); + m_showBorder = getThemeValue(theme, "label.showBorder", false); + m_borderColor = getThemeValue(theme, "label.borderColor", ostd::Colors::White); + enableBackground(getThemeValue(theme, "label.showBackground", false)); + setPadding(getThemeValue(theme, "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(), getFontSize()); + } + + 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/Label.hpp b/src/ogfx/gui/widgets/Label.hpp new file mode 100644 index 0000000..f240b4c --- /dev/null +++ b/src/ogfx/gui/widgets/Label.hpp @@ -0,0 +1,66 @@ +/* + 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 + +namespace ogfx +{ + namespace gui + { + namespace widgets + { + + 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 ostd::Stylesheet& 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(void) { 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 }; + bool m_showBackground { false }; + ostd::Color m_backgroundColor { ostd::Colors::Transparent }; + + private: + }; + } + } +} diff --git a/src/ogfx/gui/Widgets.cpp b/src/ogfx/gui/widgets/Widget.cpp similarity index 80% rename from src/ogfx/gui/Widgets.cpp rename to src/ogfx/gui/widgets/Widget.cpp index a2e12df..1a4c790 100644 --- a/src/ogfx/gui/Widgets.cpp +++ b/src/ogfx/gui/widgets/Widget.cpp @@ -18,12 +18,12 @@ along with OmniaFramework. If not, see . */ -#include "Widgets.hpp" +#include "Widget.hpp" #include "gui/Events.hpp" #include "io/Memory.hpp" #include "utils/Keycodes.hpp" -#include -#include +#include "../..//render/BasicRenderer.hpp" +#include "../Window.hpp" namespace ogfx { @@ -107,7 +107,9 @@ namespace ogfx { if (w == nullptr) continue; if (w->isInvalid()) continue; + gfx.pushClippingRect({ w->getGlobalPosition(), w->getSize() }, true); w->__draw(gfx); + gfx.popClippingRect(); } } @@ -142,7 +144,7 @@ namespace ogfx continue; w->__onMousePressed(event); m_mousePressedOnWidget = w; - if (event.isHandled()) + if (event.isHandled() || w->m_stopEvents) break; } } @@ -191,8 +193,20 @@ namespace ogfx else w->__onMouseMoved(event); } - if (event.isHandled()) + if (event.isHandled() || w->m_stopEvents) + { + bool mouseOut = false; + for (int32_t j = i - 1; j >= 0; j--) + { + Widget* ww = m_widgetList[j]; + if (ww->m_mouseInside) + { + ww->m_mouseInside = false; + ww->__onMouseExited(event); + } + } break; + } } } @@ -291,6 +305,7 @@ namespace ogfx { if (!m_allowChildren) return false; + child.reloadTheme(); return m_widgets.addWidget(child); } @@ -298,10 +313,32 @@ namespace ogfx { ostd::Vec2 glob = getPosition(); if (!m_rootChild && m_parent != nullptr) - glob += m_parent->getGlobalPosition(); + glob += m_parent->getGlobalPosition() + m_parent->getPadding().getPosition(); return glob; } + ostd::Vec2 Widget::getGlobalContentPosition(void) const + { + return getGlobalPosition() + getContentBounds().getPosition(); + } + + ostd::Rectangle Widget::getGlobalBounds(void) const + { + return { getGlobalPosition(), getSize() }; + } + + ostd::Rectangle Widget::getContentBounds(void) const + { + auto pad = getPadding(); + return { pad.getPosition(), (getSize() - (pad.getSize() * 2)) }; + } + + ostd::Rectangle Widget::getGlobalContentBounds(void) const + { + auto pad = getPadding(); + return { getGlobalContentPosition(), getContentBounds().getSize() }; + } + bool Widget::contains(ostd::Vec2 p, bool includeBounds) const { return ostd::Rectangle(getGlobalPosition(), getSize()).contains(p, includeBounds); @@ -594,94 +631,6 @@ namespace ogfx { gfx.fillRect({ 0, 0, static_cast(getWindow().getWindowWidth()), static_cast(getWindow().getWindowHeight()) }, m_color); } - - - - - Label& Label::create(const ostd::String& text) - { - setText(text); - setPadding({ 5, 5, 5, 5 }); - setTypeName("ogfx::gui::widgets::Label"); - disableDrawBox(); - disableChildren(); - enableBackground(false); - validate(); - return *this; - } - - void Label::applyTheme(const ostd::Stylesheet& theme) - { - setColor(getThemeValue(theme, "label.textColor", ostd::Colors::White)); - setBackGroundColor(getThemeValue(theme, "label.backgroundColor", ostd::Colors::Transparent)); - setFontSize(getThemeValue(theme, "label.fontSize", 20)); - m_borderRadius = getThemeValue(theme, "label.borderRadius", 10); - m_borderWidth = getThemeValue(theme, "label.borderWidth", 2); - m_showBorder = getThemeValue(theme, "label.showBorder", false); - m_borderColor = getThemeValue(theme, "label.borderColor", ostd::Colors::White); - enableBackground(getThemeValue(theme, "label.showBackground", false)); - setPadding(getThemeValue(theme, "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(), getFontSize()); - } - - 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; - } - - - - - Panel& Panel::create(void) - { - setPadding({ 5, 5, 5, 5 }); - setTypeName("ogfx::gui::widgets::Panel"); - disableDrawBox(); - disableFocus(); - enableStopEvents(); - validate(); - return *this; - } - - void Panel::applyTheme(const ostd::Stylesheet& theme) - { - setBackGroundColor(getThemeValue(theme, "panel.backgroundColor", ostd::Colors::Gray)); - m_borderRadius = getThemeValue(theme, "panel.borderRadius", 8); - m_borderWidth = getThemeValue(theme, "panel.borderWidth", 2); - m_showBorder = getThemeValue(theme, "panel.showBorder", true); - m_borderColor = getThemeValue(theme, "panel.borderColor", ostd::Colors::Black); - setPadding(getThemeValue(theme, "panel.padding", { 15, 15, 15, 15 })); - } - - void Panel::onDraw(ogfx::BasicRenderer2D& gfx) - { - if (m_showBorder) - gfx.outlinedRoundRect({ getGlobalPosition(), getSize() }, getBackgroundColor(), m_borderColor, m_borderRadius, m_borderWidth); - else - gfx.fillRoundRect({ getGlobalPosition(), getSize() }, getBackgroundColor(), m_borderRadius); - } } } } diff --git a/src/ogfx/gui/widgets/Widget.hpp b/src/ogfx/gui/widgets/Widget.hpp new file mode 100644 index 0000000..1454752 --- /dev/null +++ b/src/ogfx/gui/widgets/Widget.hpp @@ -0,0 +1,256 @@ +/* + 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 +#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 ostd::Stylesheet& 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 + { + private: struct ThemeOverride + { + ostd::String fullKey; + ostd::Stylesheet::TypeVariant value; + bool propagate; + }; + public: using EventCallback = std::function; + public: + Widget(const ostd::Rectangle& bounds, WindowCore& window); + bool addChild(Widget& child); + ostd::Vec2 getGlobalPosition(void) const; + ostd::Vec2 getGlobalContentPosition(void) const; + ostd::Rectangle getGlobalBounds(void) const; + ostd::Rectangle getContentBounds(void) const; + ostd::Rectangle getGlobalContentBounds(void) const; + using ostd::Rectangle::contains; + bool contains(ostd::Vec2 p, bool includeBounds = false) const override; + void enable(bool enable = true); + virtual void applyTheme(const ostd::Stylesheet& theme) = 0; + void addThemeOverride(const ostd::String& fullKey, ostd::Stylesheet::TypeVariant value, bool propagate = true); + void reloadTheme(void); + void setThemeQualifier(const ostd::String& qualifier, bool value = true); + bool getThemeQualifier(const ostd::String& qualifier); + bool addThemeID(const ostd::String& id); + bool removeThemeID(const ostd::String& id); + inline const ostd::Stylesheet::QualifierList& getThemeQualifierList(void) const { return m_qualifierList; } + + 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 onMouseEntered(const Event& event) { } + inline virtual void onMouseExited(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 __onMouseEntered(const Event& event); + void __onMouseExited(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 ostd::Stylesheet& 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 setMouseEnteredCallback(EventCallback callback) { callback_onMouseEntered = callback; } + inline virtual void setMouseExitedCallback(EventCallback callback) { callback_onMouseExited = 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) const { return m_padding; } + inline bool isMouseInside(void) const { return m_mouseInside; } + inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; } + inline const std::vector& getThemeIDList(void) const { return m_themeIDList; } + inline bool isEnabled(void) const { return m_enabled; } + inline bool isFocusEnabled(void) const { return m_allowFocus; } + inline void enableFocus(bool enable = true) { m_allowFocus = enable; } + inline void disableFocus(void) { enableFocus(false); } + inline void disabble(void) { enable(false); } + inline void enableStopEvents(bool enable = true) { m_stopEvents = enable; } + + template + inline T getThemeValue(const ostd::Stylesheet &theme, const ostd::String& key, const T& fallback) + { + return theme.get(key, fallback, getThemeIDList(), getThemeQualifierList()); + } + + 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 }; + int32_t m_borderRadius { 10 }; + int32_t m_borderWidth { 2 }; + ostd::Color m_borderColor { 255, 255, 255 }; + bool m_showBorder { false }; + + EventCallback callback_onMousePressed { nullptr }; + EventCallback callback_onMouseReleased { nullptr }; + EventCallback callback_onMouseMoved { nullptr }; + EventCallback callback_onMouseEntered { nullptr }; + EventCallback callback_onMouseExited { 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_mouseInside { false }; + bool m_allowFocus { true }; + bool m_enabled { true }; + bool m_stopEvents { true }; + bool m_clipContents { true }; + MouseEventData::eButton m_pressedButton { MouseEventData::eButton::None }; + + std::vector m_themeIDList; + ostd::Stylesheet::QualifierList m_qualifierList { + { "disabled", false }, + { "pressed", false }, + { "hover", false }, + { "focused", false }, + { "active", false } + }; + std::vector m_themeOverrides; + + 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 ostd::Stylesheet& theme) override; + void onDraw(ogfx::BasicRenderer2D& gfx) override; + + private: + ostd::Color m_color { ostd::Colors::Transparent }; + }; + } + } +} diff --git a/src/ogfx/ogfx.hpp b/src/ogfx/ogfx.hpp index d6d6612..f483be8 100644 --- a/src/ogfx/ogfx.hpp +++ b/src/ogfx/ogfx.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/src/ogfx/render/BasicRenderer.cpp b/src/ogfx/render/BasicRenderer.cpp index b78077f..508ece1 100644 --- a/src/ogfx/render/BasicRenderer.cpp +++ b/src/ogfx/render/BasicRenderer.cpp @@ -31,6 +31,50 @@ namespace ogfx setDefaultFont(); } + void BasicRenderer2D::pushClippingRect(const ostd::Rectangle& rect, bool additive) + { + if (!m_initialized) return; + + ostd::Rectangle finalRect = rect; + + if (additive && !m_clipStack.empty()) + finalRect = m_clipStack.back().getIntersection(rect, false); + + m_clipStack.push_back(finalRect); + + SDL_Rect r; + r.x = (int)std::round(finalRect.x); + r.y = (int)std::round(finalRect.y); + r.w = (int)std::round(finalRect.w); + r.h = (int)std::round(finalRect.h); + + SDL_SetRenderClipRect(m_window->getSDLRenderer(), &r); + } + + void BasicRenderer2D::popClippingRect(void) + { + if (!m_initialized) return; + if (m_clipStack.empty()) return; + + m_clipStack.pop_back(); + + if (m_clipStack.empty()) + { + SDL_SetRenderClipRect(m_window->getSDLRenderer(), nullptr); + return; + } + + const auto& rect = m_clipStack.back(); + + SDL_Rect r; + r.x = (int)std::round(rect.x); + r.y = (int)std::round(rect.y); + r.w = (int)std::round(rect.w); + r.h = (int)std::round(rect.h); + + SDL_SetRenderClipRect(m_window->getSDLRenderer(), &r); + } + void BasicRenderer2D::setFont(const ostd::String& fontFilePath) { if (!m_initialized) return; @@ -172,24 +216,33 @@ namespace ogfx filledCircleRGBA(m_window->getSDLRenderer(), (int32_t)std::round(center.x), (int32_t)std::round(center.y), radius, color.r, color.g, color.b, color.a); } - void BasicRenderer2D::outlinedRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness) + void BasicRenderer2D::outlinedRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness, bool outlineInward) { if (!m_initialized) return; fillRect(rect, fillColor); - drawRect(rect, outlineColor, outlineThickness); + float t = static_cast(outlineThickness); + ostd::Rectangle offset = { 0, 0, 0, 0 }; + if (!outlineInward) + offset = { -t, -t, t * 2, t * 2 }; + drawRect(rect + offset , outlineColor, outlineThickness); } - void BasicRenderer2D::outlinedRoundRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t radius, int32_t outlineThickness) + void BasicRenderer2D::outlinedRoundRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t radius, int32_t outlineThickness, bool outlineInward) { if (!m_initialized) return; fillRoundRect(rect, fillColor, radius); - drawRoundRect(rect, outlineColor, radius, outlineThickness); + float t = static_cast(outlineThickness); + ostd::Rectangle offset = { 0, 0, 0, 0 }; + if (!outlineInward) + offset = { -t, -t, t * 2, t * 2 }; + drawRoundRect(rect + offset, outlineColor, radius, outlineThickness); } - void BasicRenderer2D::outlinedCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness) + void BasicRenderer2D::outlinedCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness, bool outlineInward) { if (!m_initialized) return; fillCircle(center, radius, fillColor); + radius = (outlineInward ? radius : radius + outlineThickness); drawCircle(center, radius, outlineColor, outlineThickness); } diff --git a/src/ogfx/render/BasicRenderer.hpp b/src/ogfx/render/BasicRenderer.hpp index 297c64f..ceb381f 100644 --- a/src/ogfx/render/BasicRenderer.hpp +++ b/src/ogfx/render/BasicRenderer.hpp @@ -38,6 +38,8 @@ namespace ogfx inline ostd::IPoint getStringSize(const ostd::String str, int32_t fontSize = 0) { return m_ttfr.getStringDimensions(str, fontSize); } inline WindowCore& getWindow(void) { return *m_window; } inline bool isInitialized(void) { return m_initialized; } + void pushClippingRect(const ostd::Rectangle& rect, bool additive = false); + void popClippingRect(void); void setFont(const ostd::String& fontFilePath); void setDefaultFont(void); void setFontSize(int32_t fontSize); @@ -58,13 +60,14 @@ namespace ogfx void fillRoundRect(const ostd::Rectangle& rect, const ostd::Color& color, int32_t radius); void fillCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& color); - void outlinedRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness = 1); - void outlinedRoundRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t radius, int32_t outlineThickness = 1); - void outlinedCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness = 1); + void outlinedRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness = 1, bool outlineInward = true); + void outlinedRoundRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t radius, int32_t outlineThickness = 1, bool outlineInward = true); + void outlinedCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness = 1, bool outlineInward = true); private: TTFRenderer m_ttfr; WindowCore* m_window { nullptr }; bool m_initialized { false }; + std::vector m_clipStack; }; } diff --git a/src/ostd/math/Geometry.hpp b/src/ostd/math/Geometry.hpp index 8519cf2..3e9640e 100755 --- a/src/ostd/math/Geometry.hpp +++ b/src/ostd/math/Geometry.hpp @@ -268,6 +268,23 @@ namespace ostd inline Rectangle(Vec2 position, Vec2 size) { x = position.x; y = position.y; w = size.x; h = size.y; } virtual ~Rectangle(void) = default; + inline bool operator==(const Rectangle& op2) const { return x == op2.x && y == op2.y && w == op2.w && h == op2.h; } + inline bool operator!=(const Rectangle& op2) const { return !(*this == op2); } + inline Rectangle operator+(const Rectangle& op2) const { return { x + op2.x, y + op2.y, w + op2.w, h + op2.h }; } + inline Rectangle operator-(const Rectangle& op2) const { return { x - op2.x, y - op2.y, w - op2.w, h - op2.h }; } + inline Rectangle operator+(float s) const { return { x + s, y + s, w + s, h + s }; } + inline Rectangle operator-(float s) const { return { x - s, y - s, w - s, h - s }; } + inline Rectangle operator*(float s) const { return { x * s, y * s, w * s, h * s }; } + inline Rectangle operator/(float s) const { return { x / s, y / s, w / s, h / s }; } + inline Rectangle& operator=(const Rectangle& r) { x = r.x; y = r.y; w = r.w; h = r.h; return *this; } + inline Rectangle& operator=(float s) { x = s; y = s; w = s; h = s; return *this; } + inline Rectangle& operator+=(const Rectangle& op2) { x += op2.x; y += op2.y; w += op2.w; h += op2.h; return *this; } + inline Rectangle& operator-=(const Rectangle& op2) { x -= op2.x; y -= op2.y; w -= op2.w; h -= op2.h; return *this; } + inline Rectangle& operator+=(float s) { x += s; y += s; w += s; h += s; return *this; } + inline Rectangle& operator-=(float s) { x -= s; y -= s; w -= s; h -= s; return *this; } + inline Rectangle& operator*=(float s) { x *= s; y *= s; w *= s; h *= s; return *this; } + inline Rectangle& operator/=(float s) { x /= s; y /= s; w /= s; h /= s; return *this; } + inline virtual float getx(void) const { return x; } inline virtual float gety(void) const { return y; } inline virtual float getw(void) const { return w; } diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp index 0f529de..4e9fd5a 100644 --- a/src/test/GuiTest.cpp +++ b/src/test/GuiTest.cpp @@ -18,26 +18,6 @@ along with OmniaFramework. If not, see . */ - - - -/* - * --- Label - * Button - * Panel / Container - * Checkbox - * Radio Button (Group) - * Text Input - * Horizontal Slider - * Image / Icon - * ScrollView - * ListBox - * ComboBox - * TreeView - * Save/Open File Dialogs (and folder dialogs) - * Tab Panel - */ - #include #include @@ -60,6 +40,10 @@ class Window : public ogfx::gui::Window pos = pos2; }); + + m_panel2.setSize(300, 140); + m_panel2.setPosition(130, 200); + m_label1.setPosition(100, 200); m_label1.setText("Hello World!"); m_label1.setMousePressedCallback([&](const ogfx::gui::Event& event) -> void { @@ -67,9 +51,9 @@ class Window : public ogfx::gui::Window }); addWidget(m_label1); addWidget(m_panel1); - // m_label1.setThemeQualifier("disabled"); + addWidget(m_panel2); - m_label2.setPosition(10, 10); + m_label2.setPosition(0, 0); m_label2.setText("Ciccia Bella!"); m_label2.addThemeID("testLabel"); m_label2.addThemeID("testLabel2"); @@ -78,14 +62,16 @@ class Window : public ogfx::gui::Window m_label1.addThemeOverride("@:pressed.label.textColor", ostd::Colors::Crimson); m_theme.loadFromFile("./testTheme.txt"); - setTheme(m_theme); m_label2.addThemeOverride("@:hover.label.showBackground", true); m_label2.addThemeOverride("@:hover.label.backgroundColor", ostd::Colors::DarkGreen); - m_theme.debugPrint(); - m_label2.reloadTheme(); - m_theme.debugPrint(); + m_label3.setPosition(0, 30); + m_label3.setText("Bella!"); + m_label3.addThemeID("label3"); + m_panel1.addChild(m_label3); + + setTheme(m_theme); } inline void onSignal(ostd::Signal& signal) override @@ -106,7 +92,9 @@ class Window : public ogfx::gui::Window private: ogfx::gui::widgets::Label m_label1 { *this }; ogfx::gui::widgets::Label m_label2 { *this }; + ogfx::gui::widgets::Label m_label3 { *this }; ogfx::gui::widgets::Panel m_panel1 { *this }; + ogfx::gui::widgets::Panel m_panel2 { *this }; ostd::Stylesheet m_theme; ostd::Vec2 pos { 0, 0 }; };