/* 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" #include "../../ostd/io/Memory.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->isVisible()) 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); if (widget.m_topMost) return true; std::ranges::sort(m_widgetList, {}, [](Widget* w) { return w->m_zIndex; }); return true; } bool WidgetManager::removeWidget(Widget& widget) { STDVEC_REMOVE(m_widgetList, &widget); return true; } Widget* WidgetManager::focusNext(void) { if (m_widgetList.empty()) return nullptr; Widget* next = nullptr; Widget* smallest = nullptr; i32 currentTabIndex = (m_focused != nullptr ? m_focused->m_tabIndex : std::numeric_limits::max()); for (Widget* w : m_widgetList) { i32 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; if (!w->isVisible()) continue; if (w->isManualDrawEnabled()) 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; if (!w->isThemingEnabled()) continue; w->__applyTheme(theme, true); w->reloadTheme(); } } void WidgetManager::onMousePressed(const Event& event) { for (i32 i = m_widgetList.size() - 1; i >= 0; i--) { Widget* w = m_widgetList[i]; if (w == nullptr) continue; if (w->isInvalid()) continue; if (!w->isVisible()) continue; if (!w->contains(event.mouse->position_x, event.mouse->position_y, true)) continue; event.mouse->mousePressedOnWidget = w; 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 (i32 i = m_widgetList.size() - 1; i >= 0; i--) { Widget* w = m_widgetList[i]; if (w == nullptr) continue; if (w->isInvalid()) continue; if (!w->isVisible()) 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) { if (m_mousePressedOnWidget != nullptr && m_mousePressedOnWidget->m_pressedButton != MouseEventData::eButton::None && !m_mousePressedOnWidget->contains(event.mouse->position_x, event.mouse->position_y, true)) { m_mousePressedOnWidget->__onMouseDragged(event); } for (i32 i = m_widgetList.size() - 1; i >= 0; i--) { Widget* w = m_widgetList[i]; if (w == nullptr) continue; if (w->isInvalid()) continue; if (!w->isVisible()) 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 != MouseEventData::eButton::None) w->__onMouseDragged(event); else w->__onMouseMoved(event); } if (event.isHandled() || w->m_stopEvents) { bool mouseOut = false; for (i32 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::onMouseScrolled(const Event& event) { for (i32 i = m_widgetList.size() - 1; i >= 0; i--) { Widget* w = m_widgetList[i]; if (w == nullptr) continue; if (w->isInvalid()) continue; if (!w->isVisible()) continue; if (!w->contains(event.mouse->position_x, event.mouse->position_y, true)) continue; w->__onMouseScrolled(event); if (event.isHandled() || w->m_stopEvents) break; } } void WidgetManager::onMouseDragged(const Event& event) { for (i32 i = m_widgetList.size() - 1; i >= 0; i--) { Widget* w = m_widgetList[i]; if (w == nullptr) continue; if (w->isInvalid()) continue; if (!w->isVisible()) 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 || !m_focused->isVisible()) return; m_focused->__onKeyPressed(event); } void WidgetManager::onKeyReleased(const Event& event) { if (!m_focused || !m_focused->isVisible()) return; m_focused->__onKeyReleased(event); if (m_tabNavigationEnabled && event.keyboard->keyCode == KeyCode::Tab) focusNext(); } void WidgetManager::onTextEntered(const Event& event) { if (!m_focused || !m_focused->isVisible()) 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->isVisible()) return; if (!widget->isDragAndDropEnabled()) return; if (!widget->isMouseInside()) return; if (Widget::s_dragAndDropData != nullptr) { auto& const_cast_event = const_cast(event); const_cast_event.drop.textOrFilePath = ""; const_cast_event.drop.dropType = DropEventData::eDropType::InApp; const_cast_event.drop.userObject = Widget::s_dragAndDropData; widget->__onDragAndDrop(const_cast_event); Widget::clearDragAndDropData(); } } } }