From 327582c463dd359be0e1decde734e7549d39b3ec Mon Sep 17 00:00:00 2001 From: OmniaX-Dev Date: Wed, 22 Apr 2026 18:34:47 +0200 Subject: [PATCH] Added ProgressBar widget --- CMakeLists.txt | 3 +- extra/DefaultTheme.oss | 16 +++++ other/TODO.txt | 4 +- src/ogfx/gui/Widgets.hpp | 1 + src/ogfx/gui/widgets/CheckBox.hpp | 2 +- src/ogfx/gui/widgets/ProgressBar.cpp | 98 ++++++++++++++++++++++++++++ src/ogfx/gui/widgets/ProgressBar.hpp | 64 ++++++++++++++++++ src/ogfx/gui/widgets/RadioButton.cpp | 5 ++ src/ogfx/gui/widgets/RadioButton.hpp | 2 + src/ogfx/gui/widgets/Widget.hpp | 2 +- src/test/GuiTest.cpp | 23 +++++++ 11 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 src/ogfx/gui/widgets/ProgressBar.cpp create mode 100644 src/ogfx/gui/widgets/ProgressBar.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ef852ab..aff6777 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ list(APPEND OGFX_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Scrollbar.cpp ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Button.cpp ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/RadioButton.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/ProgressBar.cpp # render ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/render/BasicRenderer.cpp @@ -152,7 +153,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(${OMNIA_STD_LIB} PUBLIC OX_DEBUG_BUILD) target_compile_definitions(${OMNIA_GFX_LIB} PUBLIC OX_DEBUG_BUILD) elseif(CMAKE_BUILD_TYPE STREQUAL "Release") - add_compile_options(-MMD -MP -Wall) + add_compile_options(-MMD -MP -Wall -latomic) target_compile_definitions(${OMNIA_STD_LIB} PUBLIC OX_RELEASE_BUILD) target_compile_definitions(${OMNIA_GFX_LIB} PUBLIC OX_RELEASE_BUILD) endif() diff --git a/extra/DefaultTheme.oss b/extra/DefaultTheme.oss index acb703a..ab1d208 100644 --- a/extra/DefaultTheme.oss +++ b/extra/DefaultTheme.oss @@ -219,3 +219,19 @@ const $accentColorDark = #6B0A1DFF (image) { } % =================== + + + +% ====== Image ====== +(progressbar) { + backgroundColor = #400000FF + progressColor = #AA0000FF + borderColor = $accentColor + showDecimal = false + showText = true + useBackgroundGradient = true + backgroundGradient = gradientV(#400A1DFF - $accentColorDark) + useProgressGradient = true + progressGradient = gradientV(#C21135FF - #820B23FF) +} +% =================== diff --git a/other/TODO.txt b/other/TODO.txt index 3a3354a..dc5a5a2 100644 --- a/other/TODO.txt +++ b/other/TODO.txt @@ -24,7 +24,7 @@ Add theme caching Implement cursors in Stylesheet FIX: Window getting exponentially bigger when Desktop scale changes FIX: Refreshing scroll when content extent changes -Add Dark Mode Default theme +Add Dark Mode Default theme, and see if I can query OS theme mode to saelect the right theme FIX: Float values in stylesheet needing to have decimal point to be read as float Make parsing of vec2 and rect in stylesheet, consistent with other value functions like gradients and animations Add buttons to scroll tabs in TabPanel @@ -46,7 +46,7 @@ Implement following widgets: ***Tooltips ***Radio Button ***Grouping - Progressbar + ***Progressbar Horizontal Slider Vertical Slider ListBox diff --git a/src/ogfx/gui/Widgets.hpp b/src/ogfx/gui/Widgets.hpp index 194da34..8446102 100644 --- a/src/ogfx/gui/Widgets.hpp +++ b/src/ogfx/gui/Widgets.hpp @@ -26,3 +26,4 @@ #include #include #include +#include diff --git a/src/ogfx/gui/widgets/CheckBox.hpp b/src/ogfx/gui/widgets/CheckBox.hpp index 1b05fe0..1baa772 100644 --- a/src/ogfx/gui/widgets/CheckBox.hpp +++ b/src/ogfx/gui/widgets/CheckBox.hpp @@ -47,7 +47,7 @@ namespace ogfx inline Color getCheckBoxColor(void) const { return m_checkBoxColor; } inline void setCheckBoxColor(const Color& color) { m_checkBoxColor = color; } inline bool isChecked(void) const { return m_checked; } - inline void setStateChangedCallback(std::function callback) { callback_onStateChanged = callback; } + inline void setStateChangedCallback(std::function callback) { callback_onStateChanged = std::move(callback); } private: void __update_size(ogfx::BasicRenderer2D& gfx); diff --git a/src/ogfx/gui/widgets/ProgressBar.cpp b/src/ogfx/gui/widgets/ProgressBar.cpp new file mode 100644 index 0000000..14796ef --- /dev/null +++ b/src/ogfx/gui/widgets/ProgressBar.cpp @@ -0,0 +1,98 @@ +/* + 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 "ProgressBar.hpp" +#include "../../render/BasicRenderer.hpp" + +namespace ogfx +{ + namespace gui + { + namespace widgets + { + ProgressBar& ProgressBar::create(void) + { + setPadding({ 0, 0, 0, 0 }); + setTypeName("ogfx::gui::widgets::ProgressBar"); + disableChildren(); + enableBackground(); + enableBorder(); + setStylesheetCategoryName("progressbar"); + validate(); + return *this; + } + + void ProgressBar::applyTheme(const ostd::Stylesheet& theme) + { + setProgressColor(getThemeValue(theme, "progressColor", getProgressColor())); + enableShowDecimal(getThemeValue(theme, "showDecimal", isShowDecimalEnabled())); + enableShowText(getThemeValue(theme, "showText", isShowTextEnabled())); + enableProgressGradient(getThemeValue(theme, "useProgressGradient", isProgressGradientEnabled())); + setProgressGradient(getThemeValue(theme, "progressGradient", getProgressGradient())); + } + + void ProgressBar::onDraw(ogfx::BasicRenderer2D& gfx) + { + f32 prog = getProgressNormalized(); + f32 progressw = (prog * getw()); + if (isProgressGradientEnabled()) + gfx.fillGradientRect({ getGlobalPosition(), { progressw, geth() } }, m_progressGradient); + else + gfx.fillRect({ getGlobalPosition(), { progressw, geth() } }, getProgressColor()); + if (isShowTextEnabled()) + { + String text = ""; + if (isShowDecimalEnabled()) + text.add(prog * 100, 2).add("%"); + else + text.add(cast(std::round(prog * 100))).add("%"); + gfx.drawCenteredString(text, getGlobalBounds(), getTextColor(), getFontSize()); + } + } + + void ProgressBar::setProgressNormalized(f32 normalized_value) + { + f32 current = m_progress.load(std::memory_order_relaxed); + f32 next = current; + next = current + std::clamp(normalized_value, 0.0f, 1.0f) * (m_max - m_min); + m_progress.store(next, std::memory_order_relaxed); + } + + void ProgressBar::setProgress(f32 value) + { + f32 current = m_progress.load(std::memory_order_relaxed); + f32 next = current; + next = std::clamp(value, m_min, m_max); + m_progress.store(next, std::memory_order_relaxed); + } + + f32 ProgressBar::getProgress(void) const + { + return m_progress.load(std::memory_order_relaxed); + } + + f32 ProgressBar::getProgressNormalized(void) const + { + f32 s = m_progress.load(std::memory_order_relaxed); + return (s - m_min) / (m_max - m_min); + } + } + } +} diff --git a/src/ogfx/gui/widgets/ProgressBar.hpp b/src/ogfx/gui/widgets/ProgressBar.hpp new file mode 100644 index 0000000..f46b457 --- /dev/null +++ b/src/ogfx/gui/widgets/ProgressBar.hpp @@ -0,0 +1,64 @@ +/* + OmniaFramework - A collection of useful functionality + Copyright (C) 2025 OmniaX-Dev + + This file is part of OmniaFramework. + + OmniaFramework is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OmniaFramework is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OmniaFramework. If not, see . +*/ + +#pragma once + +#include +#include + +namespace ogfx +{ + namespace gui + { + namespace widgets + { + class ProgressBar : public Widget + { + public: + inline ProgressBar(WindowCore& window, f32 min = 0.0f, f32 max = 1.0f, f32 start = 0.0f) : Widget({ 0, 0, 0, 0 }, window), m_progress(std::clamp(start, min, max)), m_min(min), m_max(max) { create(); } + ProgressBar& create(void); + void applyTheme(const ostd::Stylesheet& theme) override; + void onDraw(ogfx::BasicRenderer2D& gfx) override; + void setProgressNormalized(f32 normalized_value); + void setProgress(f32 value); + f32 getProgress(void) const; + f32 getProgressNormalized(void) const; + OSTD_PARAM_GETSET(Color, ProgressColor, m_progressColor); + OSTD_BOOL_PARAM_GETSET_E(ShowDecimal, m_showDecimal); + OSTD_BOOL_PARAM_GETSET_E(ShowText, m_showText); + OSTD_BOOL_PARAM_GETSET_E(ProgressGradient, m_useProgressGradient); + OSTD_PARAM_GETSET(ColorGradient, ProgressGradient, m_progressGradient); + + private: + Color m_progressColor { 255, 255, 255 }; + f32 m_min { 0.0f }; + f32 m_max { 1.0f }; + std::atomic m_progress; + bool m_showDecimal { false }; + bool m_showText { true }; + bool m_useProgressGradient { true }; + ColorGradient m_progressGradient { + { Colors::Crimson.darkened(0.1f), Colors::Crimson.darkened(0.35f) }, + { 1.0f } + }; + }; + } + } +} diff --git a/src/ogfx/gui/widgets/RadioButton.cpp b/src/ogfx/gui/widgets/RadioButton.cpp index 1e6653f..33e7ad5 100644 --- a/src/ogfx/gui/widgets/RadioButton.cpp +++ b/src/ogfx/gui/widgets/RadioButton.cpp @@ -117,9 +117,14 @@ namespace ogfx { if (is_button_present(sender)) { + if (m_selected != nullptr && m_selected == &sender) + return; unselect_all(); + auto previous = m_selected; m_selected = &sender; m_selected->__set_selected(true); + if (previous != nullptr && callback_onSelectionChanged != nullptr) + callback_onSelectionChanged(*previous, sender); return; } } diff --git a/src/ogfx/gui/widgets/RadioButton.hpp b/src/ogfx/gui/widgets/RadioButton.hpp index ba74910..4e9b5ca 100644 --- a/src/ogfx/gui/widgets/RadioButton.hpp +++ b/src/ogfx/gui/widgets/RadioButton.hpp @@ -66,6 +66,7 @@ namespace ogfx public: RadioButtonGroup(void); RadioButton& addButton(Widget& parent, const String& text, const Vec2& position = { 0, 0 }); + inline void setSelectionChangedCallback(std::function callback) { callback_onSelectionChanged = std::move(callback); } private: void set_selected(RadioButton& sender); @@ -75,6 +76,7 @@ namespace ogfx private: stdvec> m_buttons; RadioButton* m_selected { nullptr }; + std::function callback_onSelectionChanged { nullptr }; friend class RadioButton; }; diff --git a/src/ogfx/gui/widgets/Widget.hpp b/src/ogfx/gui/widgets/Widget.hpp index 0e856ed..0a5e5f7 100644 --- a/src/ogfx/gui/widgets/Widget.hpp +++ b/src/ogfx/gui/widgets/Widget.hpp @@ -228,7 +228,7 @@ namespace ogfx Rectangle m_padding { 0, 0, 0, 0 }; Rectangle m_margin { 0, 0, 0, 0 }; Vec2 m_contentOffset { 0, 0 }; - i32 m_borderRadius { 10 }; + i32 m_borderRadius { 0 }; i32 m_borderWidth { 2 }; Color m_borderColor { 255, 255, 255 }; bool m_showBorder { false }; diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp index 2cc07a2..d3c8b2b 100644 --- a/src/test/GuiTest.cpp +++ b/src/test/GuiTest.cpp @@ -22,6 +22,7 @@ #include #include #include +#include ogfx::WindowCore::FileDialogFilterList filters = { { "Image files", { "png", "jpg", "jpeg", "bmp" } }, @@ -103,6 +104,8 @@ class Window : public ogfx::gui::Window m_panel2.enableTooltip(); m_panel2.setTooltipText("PANEL tooltip"); + m_prog.setSize(300, 30); + m_tabs.setSize(900, 700); auto& t1 = m_tabs.addTab("Tab1"); auto& t2 = m_tabs.addTab("Tab2 Test"); @@ -115,6 +118,11 @@ class Window : public ogfx::gui::Window m_radioGroup.addButton(t1, "Radio this out!", { 30, 80 }); m_radioGroup.addButton(t1, "Radio Opt. 2", { 30, 110 }); m_radioGroup.addButton(t1, "Radio 3", { 30, 140 }); + m_radioGroup.setSelectionChangedCallback([&](RadioButton& previous, RadioButton& sender) -> void { + std::cout << previous.getText() << "\n"; + std::cout << sender.getText() << "\n\n"; + }); + t1.addWidget(m_prog, { 30, 200 }); t2.addWidget(m_panel2, { 500, 100 }); m_panel3.addWidget(m_label4); @@ -133,6 +141,17 @@ class Window : public ogfx::gui::Window m_theme.loadFromFile("./DefaultTheme.oss", true, getDefaultStylesheetVariableList()); setTheme(m_theme); + + m_progressJob.start([this] { + f32 prog = 0.0f; + while (prog < 100) + { + prog += 0.6f; + ostd::Time::sleep(ostd::Random::getui32(10, 200)); + this->m_prog.setProgress(prog); + } + return true; + }); } inline void onSignal(ostd::Signal& signal) override @@ -168,6 +187,10 @@ class Window : public ogfx::gui::Window ImageLabel m_img { *this }; TabPanel m_tabs { *this }; RadioButtonGroup m_radioGroup; + ProgressBar m_prog { *this, 0, 100 }; + + ostd::StepTimer m_timer; + ostd::AsyncJob m_progressJob; ostd::Stylesheet m_theme; };