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