diff --git a/.clangd b/.clangd
index 32169b9..122ab09 100644
--- a/.clangd
+++ b/.clangd
@@ -1,3 +1,2 @@
CompileFlags:
- CompilationDatabase: bin
- Add: ["--target=x86_64-w64-mingw32"]
\ No newline at end of file
+ CompilationDatabase: bin
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d5075b2..585e2c7 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -89,11 +89,13 @@ 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/WidgetManager.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
+ ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/RootWidget.cpp
# render
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/render/BasicRenderer.cpp
diff --git a/extra/testTheme.txt b/extra/testTheme.txt
index a5f6f74..9b0c7be 100644
--- a/extra/testTheme.txt
+++ b/extra/testTheme.txt
@@ -42,11 +42,6 @@ window.backgroundColor = Color(rgba(120, 120, 120, 255))
borderColor = Color(#208080FF)
}
-
-% (@testLabel2 label:pressed [mouseButton=left or mouseButton=right]) {
-% ...
-
-
(@testLabel2 label:pressed) {
showBackground = true
}
diff --git a/other/TODO.txt b/other/TODO.txt
index 17d8377..3bf1c68 100644
--- a/other/TODO.txt
+++ b/other/TODO.txt
@@ -8,7 +8,6 @@ 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:
diff --git a/other/build.nr b/other/build.nr
index a9a6e2f..6310783 100644
--- a/other/build.nr
+++ b/other/build.nr
@@ -1 +1 @@
-2049
+2050
diff --git a/src/ogfx/gui/Events.hpp b/src/ogfx/gui/Events.hpp
index ce6b7d0..a55a53f 100644
--- a/src/ogfx/gui/Events.hpp
+++ b/src/ogfx/gui/Events.hpp
@@ -78,12 +78,28 @@ namespace ogfx
eKeyEvent eventType;
WindowCore& parentWindow;
};
+ class DropEventData : public ostd::BaseObject
+ {
+ public: enum class eDropType { None = 0, File, Text, InApp };
+ public:
+ inline DropEventData(WindowCore& parent, eDropType type) : parentWindow(parent), dropType(type)
+ {
+ setTypeName("ogfx::DropEventData");
+ validate();
+ }
+
+ public:
+ eDropType dropType;
+ WindowCore& parentWindow;
+ ostd::String textOrFilePath { "" };
+ ostd::BaseObject* userObject { nullptr };
+ };
namespace gui
{
class Event
{
public:
- inline Event(WindowCore& _window) : window(_window) { }
+ inline Event(WindowCore& _window) : window(_window), drop(window, DropEventData::eDropType::None) { }
inline void handle(void) { m_handled = true; }
inline bool isHandled(void) const { return m_handled; }
@@ -92,6 +108,8 @@ namespace ogfx
WindowResizedData* windowResized { nullptr };
MouseEventData* mouse { nullptr };
KeyEventData* keyboard { nullptr };
+ DropEventData drop;
+ uint32_t __original_signal_id { 0 };
private:
bool m_handled { false };
diff --git a/src/ogfx/gui/WidgetManager.cpp b/src/ogfx/gui/WidgetManager.cpp
new file mode 100644
index 0000000..f224167
--- /dev/null
+++ b/src/ogfx/gui/WidgetManager.cpp
@@ -0,0 +1,317 @@
+/*
+ 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 "WidgetManager.hpp"
+#include "widgets/Widget.hpp"
+#include "../render/BasicRenderer.hpp"
+#include "../utils/Keycodes.hpp"
+
+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));
+ w->setThemeQualifier("focused", false);
+ }
+ else
+ w->m_focused = false;
+ }
+ widget.m_focused = true;
+ widget.onFocusGained(Event(m_window));
+ widget.setThemeQualifier("focused", true);
+ 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;
+ gfx.pushClippingRect({ w->getGlobalPosition(), w->getSize() }, true);
+ w->__draw(gfx);
+ gfx.popClippingRect();
+ }
+ }
+
+ void WidgetManager::update(void)
+ {
+ for (auto& w : m_widgetList)
+ {
+ if (w == nullptr) continue;
+ if (w->isInvalid()) continue;
+ w->__update();
+ }
+ }
+
+ void WidgetManager::onThemeApplied(const ostd::Stylesheet& 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() || w->m_stopEvents)
+ break;
+ }
+ }
+
+ void WidgetManager::onMouseReleased(const Event& event)
+ {
+ if (m_mousePressedOnWidget != nullptr)
+ {
+ event.mouse->mousePressedOnWidget = m_mousePressedOnWidget;
+ m_mousePressedOnWidget->__onMouseReleased(event);
+ // processDragAndDrop(m_mousePressedOnWidget, 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 == m_mousePressedOnWidget) continue;
+ event.mouse->mousePressedOnWidget = m_mousePressedOnWidget;
+ w->__onMouseReleased(event);
+ processDragAndDrop(w, event);
+ // requestFocus(*w);
+ if (w->isMouseInside() && w->m_stopEvents)
+ break;
+ }
+ m_mousePressedOnWidget = nullptr;
+ }
+
+ 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))
+ {
+ if (w->m_mouseInside)
+ {
+ w->m_mouseInside = false;
+ w->__onMouseExited(event);
+ }
+ continue;
+ }
+ if (!w->m_mouseInside)
+ {
+ w->m_mouseInside = true;
+ w->__onMouseEntered(event);
+ }
+ else
+ {
+ if (w->m_pressedButton != ogfx::MouseEventData::eButton::None)
+ w->__onMouseDragged(event);
+ else
+ w->__onMouseMoved(event);
+ }
+ 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;
+ }
+ }
+ }
+
+ 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 == KeyCode::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;
+ }
+ }
+
+ void WidgetManager::processDragAndDrop(Widget* widget, const Event& event)
+ {
+ if (widget == nullptr) return;
+ if (!widget->isDragAndDropEnabled()) return;
+ if (!widget->isMouseInside()) return;
+ if (Widget::s_dragAndDropData != nullptr)
+ {
+ widget->__onDragAndDrop(event);
+ Widget::clearDragAndDropData();
+ }
+
+ auto& const_cast_event = const_cast(event);
+ // if (event.__original_signal_id == ostd::BuiltinSignals::TextDragAndDropped)
+ }
+ }
+}
diff --git a/src/ogfx/gui/WidgetManager.hpp b/src/ogfx/gui/WidgetManager.hpp
new file mode 100644
index 0000000..1e841f1
--- /dev/null
+++ b/src/ogfx/gui/WidgetManager.hpp
@@ -0,0 +1,76 @@
+/*
+ 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 ostd
+{
+ class Stylesheet;
+}
+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:
+ void processDragAndDrop(Widget* widget, const Event& event);
+
+ private:
+ WindowCore& m_window;
+ Widget& m_owner;
+ std::vector m_widgetList;
+ Widget* m_focused { nullptr };
+ bool m_tabNavigationEnabled { true };
+ Widget* m_mousePressedOnWidget { nullptr };
+ };
+ }
+}
diff --git a/src/ogfx/gui/Window.cpp b/src/ogfx/gui/Window.cpp
index a07dca5..e65c8c6 100644
--- a/src/ogfx/gui/Window.cpp
+++ b/src/ogfx/gui/Window.cpp
@@ -21,6 +21,7 @@
#include "Window.hpp"
#include "../../ostd/utils/Time.hpp"
#include "utils/Keycodes.hpp"
+#include
namespace ogfx
{
@@ -336,6 +337,18 @@ namespace ogfx
{
close();
}
+ else if (event.type == SDL_EVENT_DROP_FILE)
+ {
+ DropEventData ded(*this, DropEventData::eDropType::File);
+ ded.textOrFilePath = event.drop.data;
+ ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::FileDragAndDropped, ostd::Signal::Priority::RealTime, ded);
+ }
+ else if (event.type == SDL_EVENT_DROP_TEXT)
+ {
+ DropEventData ded(*this, DropEventData::eDropType::Text);
+ ded.textOrFilePath = event.drop.data;
+ ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::FileDragAndDropped, ostd::Signal::Priority::RealTime, ded);
+ }
else if (event.type == SDL_EVENT_WINDOW_FOCUS_GAINED)
{
ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::WindowFocused, ostd::Signal::Priority::RealTime, *this);
@@ -497,46 +510,74 @@ namespace ogfx
void Window::__on_signal(ostd::Signal& signal)
{
Event evt(*this);
+ evt.__original_signal_id = ostd::BuiltinSignals::NoSignal;
if (signal.ID == ostd::BuiltinSignals::WindowClosed)
{
+ evt.__original_signal_id = ostd::BuiltinSignals::WindowClosed;
m_rootWidget.__onWindowClosed(evt);
}
else if (signal.ID == ostd::BuiltinSignals::WindowFocused)
{
- m_rootWidget.__onWIndowFocused(evt);
+ evt.__original_signal_id = ostd::BuiltinSignals::WindowFocused;
+ m_rootWidget.__onWindowFocused(evt);
}
else if (signal.ID == ostd::BuiltinSignals::WindowLostFocus)
{
+ evt.__original_signal_id = ostd::BuiltinSignals::WindowLostFocus;
m_rootWidget.__onWindowFocusLost(evt);
}
+ else if (signal.ID == ostd::BuiltinSignals::FileDragAndDropped)
+ {
+ auto& ud = (ogfx::DropEventData&)signal.userData;
+ evt.drop.dropType = ud.dropType;
+ evt.drop.userObject = ud.userObject;
+ evt.drop.textOrFilePath = ud.textOrFilePath;
+ evt.__original_signal_id = ostd::BuiltinSignals::FileDragAndDropped;
+ m_rootWidget.__onDragAndDrop(evt);
+ }
+ else if (signal.ID == ostd::BuiltinSignals::TextDragAndDropped)
+ {
+ auto& ud = (ogfx::DropEventData&)signal.userData;
+ evt.drop.dropType = ud.dropType;
+ evt.drop.userObject = ud.userObject;
+ evt.drop.textOrFilePath = ud.textOrFilePath;
+ evt.__original_signal_id = ostd::BuiltinSignals::TextDragAndDropped;
+ m_rootWidget.__onDragAndDrop(evt);
+ }
else if (signal.ID == ostd::BuiltinSignals::WindowResized)
{
evt.windowResized = &(ogfx::WindowResizedData&)signal.userData;
+ evt.__original_signal_id = ostd::BuiltinSignals::WindowResized;
m_rootWidget.__onWindowResized(evt);
}
else if (signal.ID == ostd::BuiltinSignals::MouseMoved)
{
evt.mouse = &(ogfx::MouseEventData&)signal.userData;
+ evt.__original_signal_id = ostd::BuiltinSignals::MouseMoved;
m_rootWidget.__onMouseMoved(evt);
}
else if (signal.ID == ostd::BuiltinSignals::MousePressed)
{
evt.mouse = &(ogfx::MouseEventData&)signal.userData;
+ evt.__original_signal_id = ostd::BuiltinSignals::MousePressed;
m_rootWidget.__onMousePressed(evt);
}
else if (signal.ID == ostd::BuiltinSignals::MouseReleased)
{
evt.mouse = &(ogfx::MouseEventData&)signal.userData;
+ evt.__original_signal_id = ostd::BuiltinSignals::MouseReleased;
m_rootWidget.__onMouseReleased(evt);
}
else if (signal.ID == ostd::BuiltinSignals::KeyPressed)
{
evt.keyboard = &(ogfx::KeyEventData&)signal.userData;
+ evt.__original_signal_id = ostd::BuiltinSignals::KeyPressed;
m_rootWidget.__onKeyPressed(evt);
}
else if (signal.ID == ostd::BuiltinSignals::KeyReleased)
{
evt.keyboard = &(ogfx::KeyEventData&)signal.userData;
+ evt.__original_signal_id = ostd::BuiltinSignals::KeyReleased;
m_rootWidget.__onKeyReleased(evt);
if (evt.keyboard->keyCode == KeyCode::Escape)
close();
@@ -544,6 +585,7 @@ namespace ogfx
else if (signal.ID == ostd::BuiltinSignals::TextEntered)
{
evt.keyboard = &(ogfx::KeyEventData&)signal.userData;
+ evt.__original_signal_id = ostd::BuiltinSignals::TextEntered;
m_rootWidget.__onTextEntered(evt);
}
onSignal(signal);
diff --git a/src/ogfx/gui/Window.hpp b/src/ogfx/gui/Window.hpp
index 24212dd..049ed2b 100644
--- a/src/ogfx/gui/Window.hpp
+++ b/src/ogfx/gui/Window.hpp
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
#include
#include
diff --git a/src/ogfx/gui/widgets/RootWidget.cpp b/src/ogfx/gui/widgets/RootWidget.cpp
new file mode 100644
index 0000000..098ca12
--- /dev/null
+++ b/src/ogfx/gui/widgets/RootWidget.cpp
@@ -0,0 +1,55 @@
+/*
+ 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 "RootWidget.hpp"
+#include "../../render/BasicRenderer.hpp"
+#include "../Window.hpp"
+
+namespace ogfx
+{
+ namespace gui
+ {
+ namespace widgets
+ {
+ RootWidget::RootWidget(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window)
+ {
+ disableDrawBox();
+ m_rootChild = true;
+ setSize(static_cast(window.getWindowWidth()), static_cast(window.getWindowHeight()));
+ 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 ostd::Stylesheet& theme)
+ {
+ m_color = getThemeValue(theme, "window.backgroundColor", getWindow().getClearColor());
+ }
+
+ void RootWidget::onDraw(ogfx::BasicRenderer2D& gfx)
+ {
+ gfx.fillRect({ 0, 0, static_cast(getWindow().getWindowWidth()), static_cast(getWindow().getWindowHeight()) }, m_color);
+ }
+ }
+ }
+}
diff --git a/src/ogfx/gui/widgets/RootWidget.hpp b/src/ogfx/gui/widgets/RootWidget.hpp
new file mode 100644
index 0000000..06a91b0
--- /dev/null
+++ b/src/ogfx/gui/widgets/RootWidget.hpp
@@ -0,0 +1,44 @@
+/*
+ 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 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/gui/widgets/Widget.cpp b/src/ogfx/gui/widgets/Widget.cpp
index 1a4c790..4c4ec3c 100644
--- a/src/ogfx/gui/widgets/Widget.cpp
+++ b/src/ogfx/gui/widgets/Widget.cpp
@@ -21,7 +21,6 @@
#include "Widget.hpp"
#include "gui/Events.hpp"
#include "io/Memory.hpp"
-#include "utils/Keycodes.hpp"
#include "../..//render/BasicRenderer.hpp"
#include "../Window.hpp"
@@ -29,273 +28,6 @@ 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));
- w->setThemeQualifier("focused", true);
- }
- else
- w->m_focused = false;
- }
- widget.m_focused = true;
- widget.onFocusGained(Event(m_window));
- widget.setThemeQualifier("focused", true);
- 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;
- gfx.pushClippingRect({ w->getGlobalPosition(), w->getSize() }, true);
- w->__draw(gfx);
- gfx.popClippingRect();
- }
- }
-
- void WidgetManager::update(void)
- {
- for (auto& w : m_widgetList)
- {
- if (w == nullptr) continue;
- if (w->isInvalid()) continue;
- w->__update();
- }
- }
-
- void WidgetManager::onThemeApplied(const ostd::Stylesheet& 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() || w->m_stopEvents)
- 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))
- {
- if (w->m_mouseInside)
- {
- w->m_mouseInside = false;
- w->__onMouseExited(event);
- }
- continue;
- }
- if (!w->m_mouseInside)
- {
- w->m_mouseInside = true;
- w->__onMouseEntered(event);
- }
- else
- {
- if (w->m_pressedButton != ogfx::MouseEventData::eButton::None)
- w->__onMouseDragged(event);
- else
- w->__onMouseMoved(event);
- }
- 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;
- }
- }
- }
-
- 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 == KeyCode::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;
@@ -403,7 +135,7 @@ namespace ogfx
}
}
- bool Widget::getThemeQualifier(const ostd::String& qualifier)
+ bool Widget::getThemeQualifier(const ostd::String& qualifier) const
{
for (auto& [name, state] : m_qualifierList)
{
@@ -473,6 +205,18 @@ namespace ogfx
}
}
+ void Widget::__onDragAndDrop(const Event& event)
+ {
+ // if (hasChildren())
+ // m_widgets.onMouseReleased(event);
+ if (!event.isHandled())
+ {
+ if (callback_onDragAndDrop)
+ callback_onDragAndDrop(event);
+ onDragAndDrop(event);
+ }
+ }
+
void Widget::__onMouseMoved(const Event& event)
{
if (hasChildren())
@@ -574,7 +318,7 @@ namespace ogfx
}
}
- void Widget::__onWIndowFocused(const Event& event)
+ void Widget::__onWindowFocused(const Event& event)
{
if (hasChildren())
m_widgets.onWindowFocused(event);
@@ -604,33 +348,5 @@ namespace ogfx
m_widgets.onThemeApplied(theme);
applyTheme(theme);
}
-
-
-
- namespace widgets
- {
- RootWidget::RootWidget(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window)
- {
- disableDrawBox();
- m_rootChild = true;
- setSize(static_cast(window.getWindowWidth()), static_cast(window.getWindowHeight()));
- 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 ostd::Stylesheet& theme)
- {
- m_color = getThemeValue(theme, "window.backgroundColor", getWindow().getClearColor());
- }
-
- void RootWidget::onDraw(ogfx::BasicRenderer2D& gfx)
- {
- gfx.fillRect({ 0, 0, static_cast(getWindow().getWindowWidth()), static_cast(getWindow().getWindowHeight()) }, m_color);
- }
- }
}
}
diff --git a/src/ogfx/gui/widgets/Widget.hpp b/src/ogfx/gui/widgets/Widget.hpp
index 1454752..b18a9a3 100644
--- a/src/ogfx/gui/widgets/Widget.hpp
+++ b/src/ogfx/gui/widgets/Widget.hpp
@@ -21,56 +21,16 @@
#pragma once
#include
+#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
@@ -95,7 +55,7 @@ namespace ogfx
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 getThemeQualifier(const ostd::String& qualifier) const;
bool addThemeID(const ostd::String& id);
bool removeThemeID(const ostd::String& id);
inline const ostd::Stylesheet::QualifierList& getThemeQualifierList(void) const { return m_qualifierList; }
@@ -106,6 +66,7 @@ namespace ogfx
inline virtual void onMousePressed(const Event& event) { }
inline virtual void onMouseReleased(const Event& event) { }
inline virtual void onMouseMoved(const Event& event) { }
+ inline virtual void onDragAndDrop(const Event& event) { }
inline virtual void onMouseEntered(const Event& event) { }
inline virtual void onMouseExited(const Event& event) { }
inline virtual void onMouseDragged(const Event& event) { }
@@ -123,6 +84,7 @@ namespace ogfx
void __update(void);
void __onMousePressed(const Event& event);
void __onMouseReleased(const Event& event);
+ void __onDragAndDrop(const Event& event);
void __onMouseMoved(const Event& event);
void __onMouseEntered(const Event& event);
void __onMouseExited(const Event& event);
@@ -132,13 +94,14 @@ namespace ogfx
void __onTextEntered(const Event& event);
void __onWindowClosed(const Event& event);
void __onWindowResized(const Event& event);
- void __onWIndowFocused(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 setDragAndDropCallback(EventCallback callback) { callback_onDragAndDrop = 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; }
@@ -147,7 +110,7 @@ namespace ogfx
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 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; }
@@ -168,8 +131,11 @@ namespace ogfx
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 disable(void) { enable(false); }
inline void enableStopEvents(bool enable = true) { m_stopEvents = enable; }
+ inline bool isDragAndDropEnabled(void) const { return m_acceptDragAndDrop; }
+ inline void enableDragAndDrop(bool enable = true) { m_acceptDragAndDrop = enable; }
+ inline void disableDragAndDrop(void) { enableDragAndDrop(false); }
template
inline T getThemeValue(const ostd::Stylesheet &theme, const ostd::String& key, const T& fallback)
@@ -177,6 +143,10 @@ namespace ogfx
return theme.get(key, fallback, getThemeIDList(), getThemeQualifierList());
}
+ inline static void setDragAndDropData(ostd::BaseObject& data) { s_dragAndDropData = &data; }
+ inline static void clearDragAndDropData(void) { s_dragAndDropData = nullptr; }
+ inline static ostd::BaseObject* getDragAndDropData(void) { return s_dragAndDropData; }
+
protected:
inline void disableChildren(void) { m_allowChildren = false; }
inline void disableDrawBox(void) { m_drawBox = false; }
@@ -194,6 +164,7 @@ namespace ogfx
EventCallback callback_onMousePressed { nullptr };
EventCallback callback_onMouseReleased { nullptr };
+ EventCallback callback_onDragAndDrop { nullptr };
EventCallback callback_onMouseMoved { nullptr };
EventCallback callback_onMouseEntered { nullptr };
EventCallback callback_onMouseExited { nullptr };
@@ -219,6 +190,7 @@ namespace ogfx
bool m_enabled { true };
bool m_stopEvents { true };
bool m_clipContents { true };
+ bool m_acceptDragAndDrop { false };
MouseEventData::eButton m_pressedButton { MouseEventData::eButton::None };
std::vector m_themeIDList;
@@ -236,21 +208,9 @@ namespace ogfx
ostd::Rectangle m_padding { 0, 0, 0, 0 };
+ inline static ostd::BaseObject* s_dragAndDropData { nullptr };
+
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/ostd/utils/Logic.cpp b/src/ostd/utils/Logic.cpp
index a2980da..a95a783 100755
--- a/src/ostd/utils/Logic.cpp
+++ b/src/ostd/utils/Logic.cpp
@@ -1,3 +1,23 @@
+/*
+ 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 "Logic.hpp"
#include "../string/String.hpp"
#ifndef __APPLE__
diff --git a/src/ostd/utils/Signals.hpp b/src/ostd/utils/Signals.hpp
index b640792..9455875 100755
--- a/src/ostd/utils/Signals.hpp
+++ b/src/ostd/utils/Signals.hpp
@@ -41,6 +41,8 @@ namespace ostd
inline static constexpr uint32_t TextEntered = 0x0007;
inline static constexpr uint32_t OnGuiEvent = 0x2001;
+ inline static constexpr uint32_t FileDragAndDropped = 0x2002;
+ inline static constexpr uint32_t TextDragAndDropped = 0x2003;
inline static constexpr uint32_t BeforeSDLShutdown = 0x3001;
diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp
index 4e9fd5a..6652919 100644
--- a/src/test/GuiTest.cpp
+++ b/src/test/GuiTest.cpp
@@ -30,7 +30,7 @@ class Window : public ogfx::gui::Window
inline void onInitialize(void) override
{
m_panel1.setSize(300, 140);
- m_panel1.setPosition(200, 150);
+ m_panel1.setPosition(350, 50);
m_panel1.setMousePressedCallback([&](const ogfx::gui::Event& event) -> void {
pos = { event.mouse->position_x, event.mouse->position_y };
});
@@ -43,6 +43,13 @@ class Window : public ogfx::gui::Window
m_panel2.setSize(300, 140);
m_panel2.setPosition(130, 200);
+ m_panel2.setMousePressedCallback([&](const ogfx::gui::Event& event) -> void {
+ ogfx::gui::Widget::setDragAndDropData(m_label3);
+ setCursor(eCursor::Move);
+ });
+ m_panel2.setMouseReleasedCallback([&](const ogfx::gui::Event& event) -> void {
+ setCursor(eCursor::Default);
+ });
m_label1.setPosition(100, 200);
m_label1.setText("Hello World!");
@@ -57,6 +64,14 @@ class Window : public ogfx::gui::Window
m_label2.setText("Ciccia Bella!");
m_label2.addThemeID("testLabel");
m_label2.addThemeID("testLabel2");
+ m_label2.enableDragAndDrop();
+ m_label2.setDragAndDropCallback([&](const ogfx::gui::Event& event) -> void {
+ std::cout << "DROP\n";
+ if (auto data = ogfx::gui::Widget::getDragAndDropData())
+ {
+ std::cout << static_cast(*data).getText() << "!\n";
+ }
+ });
m_panel1.addChild(m_label2);
m_label1.addThemeOverride("@:pressed.label.textColor", ostd::Colors::Crimson);