diff --git a/extra/DefaultTheme.oss b/extra/DefaultTheme.oss
index fb5fcc4..56c2f46 100644
--- a/extra/DefaultTheme.oss
+++ b/extra/DefaultTheme.oss
@@ -1,20 +1,20 @@
% ================== DefaultTheme.oss ==================
-% ---- Variables ----
+% ==== Variables ====
const $accentColor = #DC143CFF
const $textColor = #111111FF
const $accentColorLight = #F16A85FF
const $accentColorDark = #6B0A1DFF
-% -------------------
+% ===================
-% ------ Root Widget ------
+% ====== Root Widget ======
window.backgroundColor = rgba(160, 160, 160, 255)
-% -------------------------
+% =========================
-% ------ Panel ------
+% ====== Panel ======
(panel) {
backgroundColor = #AAAAAAFF
titleColor = $accentColorDark
@@ -30,14 +30,25 @@ window.backgroundColor = rgba(160, 160, 160, 255)
titlebarHeight = 18
titlebarFontSize = 18
titlebarTextAlign = text_left
+ % scrollSpeed = Vec2(0.8, 0.8)
padding = Rect(15, 15, 15, 15)
margin = Rect(0, 0, 0, 0)
}
-% -------------------
+% ===================
-% ------ CheckBox ------
+% ====== Scrollbar ======
+% (scrollbar) {
+% (handle) {
+
+% }
+% }
+% =======================
+
+
+
+% ====== CheckBox ======
(checkbox) {
checkBorderColor = $accentColorDark
checkBoxColor = $accentColor
@@ -67,11 +78,11 @@ window.backgroundColor = rgba(160, 160, 160, 255)
checkBorderColor = $accentColor
textColor = $accentColorDark
}
-% ----------------------
+% ======================
-% ------ Label ------
+% ====== Label ======
(label) {
textColor = $textColor
backgroundColor = color_transparent
@@ -84,4 +95,4 @@ window.backgroundColor = rgba(160, 160, 160, 255)
padding = Rect(15, 15, 15, 15)
margin = Rect(0, 0, 0, 0)
}
-% -------------------
+% ===================
diff --git a/src/ogfx/gui/Events.hpp b/src/ogfx/gui/Events.hpp
index 87f3738..734afc8 100644
--- a/src/ogfx/gui/Events.hpp
+++ b/src/ogfx/gui/Events.hpp
@@ -1,21 +1,21 @@
/*
- OmniaFramework - A collection of useful functionality
- Copyright (C) 2025 OmniaX-Dev
+ OmniaFramework - A collection of useful functionality
+ Copyright (C) 2025 OmniaX-Dev
- This file is part of OmniaFramework.
+ This file is part of OmniaFramework.
- OmniaFramework is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ OmniaFramework is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- OmniaFramework is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ OmniaFramework is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with OmniaFramework. If not, see .
+ You should have received a copy of the GNU General Public License
+ along with OmniaFramework. If not, see .
*/
#pragma once
@@ -102,7 +102,7 @@ namespace ogfx
{
public:
inline Event(WindowCore& _window) : window(_window), drop(window, DropEventData::eDropType::None) { }
- inline void handle(void) { m_handled = true; }
+ inline void handle(void) const { m_handled = true; }
inline bool isHandled(void) const { return m_handled; }
public:
@@ -114,7 +114,7 @@ namespace ogfx
u32 __original_signal_id { 0 };
private:
- bool m_handled { false };
+ mutable bool m_handled { false };
};
}
}
diff --git a/src/ogfx/gui/WidgetManager.cpp b/src/ogfx/gui/WidgetManager.cpp
index 5744387..facc208 100644
--- a/src/ogfx/gui/WidgetManager.cpp
+++ b/src/ogfx/gui/WidgetManager.cpp
@@ -130,6 +130,7 @@ namespace ogfx
if (w == nullptr) continue;
if (w->isInvalid()) continue;
w->__applyTheme(theme, true);
+ w->reloadTheme();
}
}
diff --git a/src/ogfx/gui/WidgetManager.hpp b/src/ogfx/gui/WidgetManager.hpp
index 325760b..81429a9 100644
--- a/src/ogfx/gui/WidgetManager.hpp
+++ b/src/ogfx/gui/WidgetManager.hpp
@@ -1,21 +1,21 @@
/*
- OmniaFramework - A collection of useful functionality
- Copyright (C) 2025 OmniaX-Dev
+ OmniaFramework - A collection of useful functionality
+ Copyright (C) 2025 OmniaX-Dev
- This file is part of OmniaFramework.
+ This file is part of OmniaFramework.
- OmniaFramework is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ OmniaFramework is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- OmniaFramework is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ OmniaFramework is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with OmniaFramework. If not, see .
+ You should have received a copy of the GNU General Public License
+ along with OmniaFramework. If not, see .
*/
#pragma once
@@ -61,6 +61,7 @@ namespace ogfx
inline i32 widgetCount(void) const { return m_widgetList.size(); }
inline WindowCore& getWindow(void) { return m_window; }
+ inline const stdvec& getWidgets(void) const { return m_widgetList; }
private:
void processDragAndDrop(Widget* widget, const Event& event);
diff --git a/src/ogfx/gui/Window.cpp b/src/ogfx/gui/Window.cpp
index d6d1c30..6568f7f 100644
--- a/src/ogfx/gui/Window.cpp
+++ b/src/ogfx/gui/Window.cpp
@@ -562,9 +562,9 @@ namespace ogfx
namespace gui
{
- void Window::addWidget(Widget& widget)
+ void Window::addWidget(Widget& widget, const Vec2& position)
{
- m_rootWidget.addChild(widget);
+ m_rootWidget.addWidget(widget, position);
setTheme(*m_guiTheme);
}
@@ -572,6 +572,7 @@ namespace ogfx
{
m_guiTheme = &theme;
m_rootWidget.__applyTheme(theme, true);
+ m_rootWidget.reloadTheme(true);
}
void Window::__on_window_init(i32 width, i32 height, const String& title)
diff --git a/src/ogfx/gui/Window.hpp b/src/ogfx/gui/Window.hpp
index c7ddd3c..eb5bd96 100644
--- a/src/ogfx/gui/Window.hpp
+++ b/src/ogfx/gui/Window.hpp
@@ -212,7 +212,7 @@ namespace ogfx
public:
inline Window(void) { }
inline Window(i32 width, i32 height, const String& title) { initialize(width, height, title); }
- void addWidget(Widget& widget);
+ void addWidget(Widget& widget, const Vec2& position = { 0, 0 });
void setTheme(const ostd::Stylesheet& theme) override;
inline virtual void onInitialize(void) { }
diff --git a/src/ogfx/gui/widgets/Containers.cpp b/src/ogfx/gui/widgets/Containers.cpp
index 279cc39..c38fac5 100644
--- a/src/ogfx/gui/widgets/Containers.cpp
+++ b/src/ogfx/gui/widgets/Containers.cpp
@@ -35,6 +35,7 @@ namespace ogfx
disableDrawBox();
disableFocus();
enableStopEvents();
+ allowScroll(true);
validate();
return *this;
}
@@ -49,6 +50,7 @@ namespace ogfx
setBorderColor(getThemeValue(theme, "panel.borderColor", Colors::Black));
setPadding(getThemeValue(theme, "panel.padding", { 15, 15, 15, 15 }));
setMargin(getThemeValue(theme, "panel.margin", { 0, 0, 0, 0 }));
+ m_scrollSpeed = getThemeValue(theme, "panel.scrollSpeed", { 0.0f, 0.8f });
m_basePadding = getPadding();
m_titleColor = getThemeValue(theme, "panel.titleColor", Colors::Black);
m_titlebarColor = getThemeValue(theme, "panel.titlebarColor", Colors::Transparent);
@@ -61,10 +63,38 @@ namespace ogfx
}
void Panel::onDraw(ogfx::BasicRenderer2D& gfx)
+ {
+ if (isScrollAllowed())
+ gfx.fillRect(getContentExtents() + Rectangle { getGlobalPosition(), 0, 0 }, { 80, 0, 0, 30 });
+ }
+
+ void Panel::afterDraw(ogfx::BasicRenderer2D& gfx)
{
draw_titlebar(gfx);
}
+ void Panel::onMouseScrolled(const Event& event)
+ {
+ if (!isScrollAllowed())
+ return;
+ auto ext = getContentExtents();
+ auto cont = getContentBounds();
+ f32 maxScroll = -(ext.h - cont.h);
+ if (event.mouse->scroll == MouseEventData::eScrollDirection::Down && m_scrollOffset.y > maxScroll)
+ {
+ m_scrollOffset -= (m_scrollSpeed * 15.0f);
+ if (m_scrollOffset.y < maxScroll)
+ m_scrollOffset.y = maxScroll;
+ }
+ else if (event.mouse->scroll == MouseEventData::eScrollDirection::Up && m_scrollOffset.y < 0)
+ {
+ m_scrollOffset += (m_scrollSpeed * 15.0f);
+ if (m_scrollOffset.y > 0)
+ m_scrollOffset.y = 0;
+ }
+ event.handle();
+ }
+
void Panel::setTitlebarType(const String& type)
{
String t = type.new_toLower().trim();
diff --git a/src/ogfx/gui/widgets/Containers.hpp b/src/ogfx/gui/widgets/Containers.hpp
index 8198fb0..4f04c45 100644
--- a/src/ogfx/gui/widgets/Containers.hpp
+++ b/src/ogfx/gui/widgets/Containers.hpp
@@ -45,18 +45,22 @@ namespace ogfx
Panel& create(void);
void applyTheme(const ostd::Stylesheet& theme) override;
void onDraw(ogfx::BasicRenderer2D& gfx) override;
+ void afterDraw(ogfx::BasicRenderer2D& gfx) override;
+ void onMouseScrolled(const Event& event) override;
void setTitlebarType(const String& type);
String getTitlebarType(void) const;
inline void setBackGroundColor(const Color& color) { m_backgroundColor = color; }
inline Color getBackgroundColor(void) { return m_backgroundColor; }
inline String getTitle(void) const { return m_title; }
inline void setTitle(const String& title) { m_title = title; }
+ inline Vec2 getScrollOffset(void) const override { return m_scrollOffset; }
private:
void draw_titlebar(BasicRenderer2D& gfx);
private:
String m_title { "Panel" };
+ Vec2 m_scrollOffset { 0, 0 };
Color m_titleColor { Colors::Black };
i32 m_titlebarType = TitleBarTypes::NoneValue;
@@ -67,6 +71,7 @@ namespace ogfx
Color m_titlebarBorderColor { Colors::Black };
Rectangle m_basePadding { 0, 0, 0, 0 };
i32 m_titleTextAlign { 0 };
+ Vec2 m_scrollSpeed { 0.8f, 0.8f };
};
}
}
diff --git a/src/ogfx/gui/widgets/Widget.cpp b/src/ogfx/gui/widgets/Widget.cpp
index f99b3bc..8597e22 100644
--- a/src/ogfx/gui/widgets/Widget.cpp
+++ b/src/ogfx/gui/widgets/Widget.cpp
@@ -36,10 +36,12 @@ namespace ogfx
m_window = &window;
}
- bool Widget::addChild(Widget& child)
+ bool Widget::addWidget(Widget& child, const Vec2& position)
{
if (!m_allowChildren)
return false;
+ if (position.x != 0 || position.y != 0)
+ child.setPosition(position);
child.reloadTheme();
return m_widgets.addWidget(child);
}
@@ -48,7 +50,7 @@ namespace ogfx
{
Vec2 glob = getPosition();
if (!m_rootChild && m_parent != nullptr)
- glob += m_parent->getGlobalPosition() + m_parent->getPadding().getPosition();
+ glob += m_parent->getGlobalPosition() + m_parent->getPadding().getPosition() + m_parent->getScrollOffset();
glob += m_margin.getPosition();
return glob;
}
@@ -75,6 +77,19 @@ namespace ogfx
return { getGlobalContentPosition(), getContentBounds().getSize() };
}
+ Rectangle Widget::getContentExtents(void) const
+ {
+ f32 maxX = 0, maxY = 0;
+ for (auto* child : m_widgets.getWidgets())
+ {
+ if (!child || child->isInvalid()) continue;
+ Vec2 localPos = getPadding().getPosition() + child->getPosition() + child->getMargin().getPosition() + child->getContentBounds().getPosition();
+ maxX = std::max(maxX, localPos.x + child->getw() + child->getMargin().w);
+ maxY = std::max(maxY, localPos.y + child->geth() + child->getMargin().h);
+ }
+ return { 0, 0, maxX, maxY };
+ }
+
bool Widget::contains(Vec2 p, bool includeBounds) const
{
return Rectangle(getGlobalPosition(), getSize()).contains(p, includeBounds);
@@ -91,7 +106,7 @@ namespace ogfx
m_themeOverrides.push_back({ fullKey, value });
}
- void Widget::reloadTheme(void)
+ void Widget::reloadTheme(bool propagate)
{
if (getWindow().theme() == nullptr)
return;
@@ -116,7 +131,7 @@ namespace ogfx
backup.push_back({ currentValuePtr, currentValue, rule.fullKey });
const_cast_theme.setFull(rule.fullKey, rule.value);
}
- __applyTheme(const_cast_theme, false);
+ __applyTheme(const_cast_theme, propagate);
for (auto&[ptr, val, key] : backup)
{
if (ptr == nullptr)
@@ -189,6 +204,7 @@ namespace ogfx
m_widgets.draw(gfx);
if (m_showBorder)
gfx.drawRoundRect({ getGlobalPosition(), getSize() }, m_borderColor, m_borderRadius, m_borderWidth);
+ afterDraw(gfx);
}
void Widget::__update(void)
diff --git a/src/ogfx/gui/widgets/Widget.hpp b/src/ogfx/gui/widgets/Widget.hpp
index 0b6ca31..8b7c215 100644
--- a/src/ogfx/gui/widgets/Widget.hpp
+++ b/src/ogfx/gui/widgets/Widget.hpp
@@ -41,18 +41,20 @@ namespace ogfx
public: using EventCallback = std::function;
public:
Widget(const Rectangle& bounds, WindowCore& window);
- bool addChild(Widget& child);
+ bool addWidget(Widget& child, const Vec2& position = { 0, 0 });
virtual Vec2 getGlobalPosition(void) const;
virtual Vec2 getGlobalContentPosition(void) const;
virtual Rectangle getGlobalBounds(void) const;
virtual Rectangle getContentBounds(void) const;
virtual Rectangle getGlobalContentBounds(void) const;
+ virtual Rectangle getContentExtents(void) const;
using Rectangle::contains;
bool contains(Vec2 p, bool includeBounds = false) const override;
void enable(bool enable = true);
virtual void applyTheme(const ostd::Stylesheet& theme) = 0;
+ inline virtual Vec2 getScrollOffset(void) const { return { 0, 0 }; }
void addThemeOverride(const String& fullKey, ostd::Stylesheet::TypeVariant value);
- void reloadTheme(void);
+ void reloadTheme(bool propagate = false);
void setThemeQualifier(const String& qualifier, bool value = true);
bool getThemeQualifier(const String& qualifier) const;
bool addThemeID(const String& id);
@@ -61,6 +63,7 @@ namespace ogfx
inline const ostd::Stylesheet::QualifierList& getThemeQualifierList(void) const { return m_qualifierList; }
inline virtual void onDraw(ogfx::BasicRenderer2D& gfx) { }
+ inline virtual void afterDraw(ogfx::BasicRenderer2D& gfx) { }
inline virtual void onUpdate(void) { }
inline virtual void onMousePressed(const Event& event) { }
@@ -156,6 +159,8 @@ namespace ogfx
inline bool isVisible(void) const { return m_visible; }
inline void show(void) { setVisible(true); }
inline void hide(void) { setVisible(false); }
+ inline void allowScroll(bool allow = true) { m_allowScroll = allow; }
+ inline bool isScrollAllowed(void) const { return m_allowScroll; }
template
inline T getThemeValue(const ostd::Stylesheet &theme, const String& key, const T& fallback)
@@ -215,6 +220,7 @@ namespace ogfx
bool m_clipContents { true };
bool m_acceptDragAndDrop { false };
bool m_visible { true };
+ bool m_allowScroll { false };
MouseEventData::eButton m_pressedButton { MouseEventData::eButton::None };
stdvec m_themeIDList;
diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp
index cb40723..60df6ad 100644
--- a/src/test/GuiTest.cpp
+++ b/src/test/GuiTest.cpp
@@ -31,115 +31,59 @@ class Window : public ogfx::gui::Window
inline Window(void) { }
inline void onInitialize(void) override
{
- m_img.loadFromFile("./img.png", m_gfx);
- ogfx::Animation::AnimationData ad;
- ad.backwards = false;
- ad.columns = 9;
- ad.rows = 4;
- ad.frame_width = 256;
- ad.frame_height = 256;
- ad.length = 36;
- ad.random = false;
- ad.still = false;
- ad.speed = 0;
- m_anim.create(ad);
- m_anim.setSpriteSheet(m_img);
-
- m_panel1.setSize(300, 140);
- m_panel1.setPosition(350, 50);
- m_panel1.setMousePressedCallback([&](const ogfx::gui::Event& event) -> void {
- pos = { event.mouse->position_x, event.mouse->position_y };
- });
- m_panel1.setMouseDraggedCallback([&](const ogfx::gui::Event& event) -> void {
- if (m_panel1.getPressedMouseButton() == ogfx::MouseEventData::eButton::None)
- return;
- ostd::Vec2 pos2 { event.mouse->position_x, event.mouse->position_y };
- m_panel1.addPos(pos2 - pos);
- pos = pos2;
- });
- m_panel1.setMouseScrolledCallback([&](const ogfx::gui::Event& event) -> void {
- std::cout << "SCROLLED IN THE PANEL LOL \n";
- });
+ // m_img.loadFromFile("./img.png", m_gfx);
+ // ogfx::Animation::AnimationData ad;
+ // ad.backwards = false;
+ // ad.columns = 9;
+ // ad.rows = 4;
+ // ad.frame_width = 256;
+ // ad.frame_height = 256;
+ // ad.length = 36;
+ // ad.random = false;
+ // ad.still = false;
+ // ad.speed = 0;
+ // m_anim.create(ad);
+ // m_anim.setSpriteSheet(m_img);
- m_panel2.setSize(600, 400);
- m_panel2.setPosition(170, 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!");
+ m_label1.setText("Show Panel2");
m_label1.setMousePressedCallback([&](const ogfx::gui::Event& event) -> void {
- std::cout << "PRESS!\n";
m_check1.setChecked(!m_check1.isChecked());
});
- m_label1.setMouseScrolledCallback([&](const ogfx::gui::Event& event) -> void {
- std::cout << "SCROLL!\n";
- });
- addWidget(m_label1);
- m_panel2.addChild(m_panel1);
- addWidget(m_panel2);
+ m_label1.addThemeOverride("@.label.showBackground", true);
+ m_label1.addThemeOverride("@.label.backgroundColor", Colors::Beige);
+ m_label1.addThemeOverride("@.label.showBorder", true);
+ m_label1.addThemeOverride("@.label.borderWidth", 2);
+ m_label1.addThemeOverride("@.label.borderColor", Colors::DarkMagenta);
+ m_label1.addThemeOverride("@:pressed.label.backgroundColor", Colors::Crimson);
+ m_label1.addThemeOverride("@:hover.label.backgroundColor", Colors::DarkRed);
+ m_label1.reloadTheme();
-
- m_check1.setPosition(30, 30);
m_check1.setText("Check this out!");
+ m_check1.setChecked(true);
m_check1.setStateChangedCallback([&](ogfx::gui::widgets::CheckBox& sender, bool state) -> void {
m_panel1.setVisible(state);
});
- addWidget(m_check1);
- m_label2.setPosition(0, 0);
- m_label2.setText("Ciccia Bella!");
- m_label2.connectSignal(ostd::BuiltinSignals::FileDragAndDropped);
- m_label2.addThemeID("testLabel");
- m_label2.addThemeID("testLabel2");
- m_label2.setSignalCallback([&](ostd::Signal& signal) -> void {
- if (signal.ID == ostd::BuiltinSignals::FileDragAndDropped)
- {
- auto& data = (ogfx::DropEventData&)signal.userData;
- if (ostd::FileSystem::fileExists(data.textOrFilePath))
- {
- m_theme.loadFromFile(data.textOrFilePath);
- m_rootWidget.reloadTheme();
- }
- std::cout << data.textOrFilePath << "\n";
- }
- });
- m_label2.setMouseScrolledCallback([&](const ogfx::gui::Event& event) -> void {
- std::cout << "BARBABARBA!\n";
- });
- m_label2.enableDragAndDrop();
- m_label2.setDragAndDropCallback([&](const ogfx::gui::Event& event) -> void {
- if (event.drop.userObject->getTypeName() == "ogfx::gui::widgets::Label")
- {
- std::cout << cast(*event.drop.userObject).getText() << "!\n";
- }
- });
- m_panel1.addChild(m_label2);
+ m_label2.setText("Label2");
+ m_label3.setText("Label3");
- m_label1.addThemeOverride("@:pressed.label.textColor", Colors::Crimson);
+ m_panel1.setSize(300, 140);
+ m_panel1.allowScroll(false);
+
+ m_panel2.setSize(600, 400);
+
+ m_panel1.addWidget(m_label2);
+
+ m_panel2.addWidget(m_label3);
+ m_panel2.addWidget(m_panel1, { 0, 50 });
+ m_panel2.addWidget(m_label1, { 0, 340 });
+
+ addWidget(m_check1, { 30, 30 });
+ addWidget(m_panel2, { 30, 100 });
m_theme.loadFromFile("./DefaultTheme.oss", true, getDefaultStylesheetVariableList());
-
- m_label2.addThemeOverride("@:hover.label.showBackground", true);
- m_label2.addThemeOverride("@:hover.label.backgroundColor", Colors::DarkGreen);
-
- m_label3.setPosition(0, 30);
- m_label3.setText("Bella!");
- m_label3.setMousePressedCallback([&](const ogfx::gui::Event& event) -> void {
- std::cout << "BALALLA!\n";
- });
- m_label3.addThemeID("label3");
- m_panel1.addChild(m_label3);
-
- wout().setFontSize(80);
-
-
setTheme(m_theme);
}
@@ -171,7 +115,6 @@ class Window : public ogfx::gui::Window
ogfx::gui::widgets::Panel m_panel2 { *this };
ogfx::gui::widgets::CheckBox m_check1 { *this };
ostd::Stylesheet m_theme;
- ostd::Vec2 pos { 0, 0 };
ogfx::Animation m_anim;
ogfx::Image m_img;
};