Added Slider widget

This commit is contained in:
OmniaX-Dev 2026-04-23 00:46:10 +02:00
parent 327582c463
commit 19f64f6af3
10 changed files with 386 additions and 79 deletions

View file

@ -95,6 +95,7 @@ list(APPEND OGFX_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Button.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/RadioButton.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/ProgressBar.cpp ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/ProgressBar.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Slider.cpp
# render # render
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/render/BasicRenderer.cpp ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/render/BasicRenderer.cpp

View file

@ -235,3 +235,23 @@ const $accentColorDark = #6B0A1DFF
progressGradient = gradientV(#C21135FF - #820B23FF) progressGradient = gradientV(#C21135FF - #820B23FF)
} }
% =================== % ===================
% ====== Slider ======
(slider) {
(handle) {
color = $accentColor
size = vec2(10, 18)
}
(track) {
color = #500000FF
progressColor = #AA0000FF
width = 6.0
}
showStepTicks = true
stepTickHeight = 6.0
stepTickColor = $accentColor
padding = rect(5, 5, 5, 5)
}
% ===================

View file

@ -47,8 +47,8 @@ Implement following widgets:
***Radio Button ***Radio Button
***Grouping ***Grouping
***Progressbar ***Progressbar
Horizontal Slider ***Horizontal Slider
Vertical Slider ***Vertical Slider
ListBox ListBox
ComboBox ComboBox
Context Menu Context Menu

View file

@ -27,3 +27,4 @@
#include <ogfx/gui/widgets/Button.hpp> #include <ogfx/gui/widgets/Button.hpp>
#include <ogfx/gui/widgets/RadioButton.hpp> #include <ogfx/gui/widgets/RadioButton.hpp>
#include <ogfx/gui/widgets/ProgressBar.hpp> #include <ogfx/gui/widgets/ProgressBar.hpp>
#include <ogfx/gui/widgets/Slider.hpp>

View file

@ -69,9 +69,8 @@ namespace ogfx
void ProgressBar::setProgressNormalized(f32 normalized_value) void ProgressBar::setProgressNormalized(f32 normalized_value)
{ {
f32 current = m_progress.load(std::memory_order_relaxed); normalized_value = std::clamp(normalized_value, 0.0f, 1.0f);
f32 next = current; f32 next = m_min + normalized_value * (m_max - m_min);
next = current + std::clamp(normalized_value, 0.0f, 1.0f) * (m_max - m_min);
m_progress.store(next, std::memory_order_relaxed); m_progress.store(next, std::memory_order_relaxed);
} }

View file

@ -0,0 +1,170 @@
/*
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 "Slider.hpp"
#include "../../render/BasicRenderer.hpp"
namespace ogfx
{
namespace gui
{
namespace widgets
{
Slider& Slider::create(bool vertical, f32 min, f32 max, f32 step)
{
setPadding({ 0, 0, 0, 0 });
setTypeName("ogfx::gui::widgets::Slider");
disableChildren();
disableBackground();
disableBorder();
setContentOffset({ 0, 0 });
setStylesheetCategoryName("slider");
setMinValue(min);
setMaxValue(max);
setStep(step);
validate();
return *this;
}
void Slider::applyTheme(const ostd::Stylesheet& theme)
{
setHandleColor(getThemeValue<Color>(theme, "handle.color", getHandleColor()));
setHandleSize(getThemeValue<Vec2>(theme, "handle.size", getHandleSize()));
setTrackColor(getThemeValue<Color>(theme, "track.color", getTrackColor()));
setTrackProgressColor(getThemeValue<Color>(theme, "track.progressColor", getTrackProgressColor()));
setTrackWidth(getThemeValue<f32>(theme, "track.width", getTrackWidth()));
enableDrawTicks(getThemeValue<bool>(theme, "showStepTicks", isDrawTicksEnabled()));
setStepTickHeight(getThemeValue<f32>(theme, "stepTickHeight", getStepTickHeight()));
setStepTickColor(getThemeValue<Color>(theme, "stepTickColor", getStepTickColor()));
}
void Slider::onMouseReleased(const Event& event)
{
if (!isMouseInside())
return;
if (isMiddleClickShortcutEnabled() && event.mouse->button == MouseEventData::eButton::Middle)
{
set_value(snap_to_step((m_min + m_max) * 0.5f));
return;
}
set_value(mouse_position_to_value({ event.mouse->position_x, event.mouse->position_y }));
}
void Slider::onMouseDragged(const Event& event)
{
if (!isMouseInside())
return;
set_value(mouse_position_to_value({ event.mouse->position_x, event.mouse->position_y }));
}
void Slider::onDraw(ogfx::BasicRenderer2D& gfx)
{
const auto& bounds = getGlobalContentBounds();
f32 value = m_value;
f32 t = (value - m_min) / (m_max - m_min); // normalized [0, 1]
auto l_centerh = [&](f32 value) -> f32 {
return (geth() / 2.0f) - (value / 2.0f);
};
auto l_centerw = [&](f32 value) -> f32 {
return (getw() / 2.0f) - (value / 2.0f);
};
if (isHorizontal())
{
if (m_step > 0.0f && m_drawTicks)
{
int tickCount = (int)std::round((m_max - m_min) / m_step) + 1;
for (int i = 0; i < tickCount; i++)
{
f32 v = m_min + i * m_step;
v = std::min(v, m_max);
f32 tickT = (v - m_min) / (m_max - m_min);
f32 tickX = bounds.x + bounds.w * tickT;
gfx.fillRect({ tickX - 1, bounds.y + l_centerh(getStepTickHeight()) - getStepTickHeight(), 2, getStepTickHeight() }, getStepTickColor());
}
}
Vec2 correction { -2, 0 };
Vec2 trackPos = bounds.getPosition() + Vec2 { 0, l_centerh(getTrackWidth()) };
gfx.fillRect({ trackPos + correction, bounds.w + (correction.x * -2), getTrackWidth() }, getTrackColor());
gfx.fillRect({ trackPos + correction, (bounds.w + (correction.x * -2)) * t, getTrackWidth() }, getTrackProgressColor());
f32 handleX = bounds.x + bounds.w * t - (getHandleSize().x / 2.0f);
gfx.fillRect({ handleX, bounds.y + l_centerh(getHandleSize().y), getHandleSize() }, getHandleColor());
}
else
{
if (m_step > 0.0f && m_drawTicks)
{
int tickCount = (int)std::round((m_max - m_min) / m_step) + 1;
for (int i = 0; i < tickCount; i++)
{
f32 v = m_min + i * m_step;
v = std::min(v, m_max);
f32 tickT = (v - m_min) / (m_max - m_min);
f32 tickY = bounds.y + bounds.h * (1.0f - tickT);
gfx.fillRect({ bounds.x + l_centerw(getStepTickHeight()) - getStepTickHeight(), tickY - 1, getStepTickHeight(), 2 }, getStepTickColor());
}
}
Vec2 correction { 0, -2 };
Vec2 trackPos = bounds.getPosition() + Vec2 { l_centerw(getTrackWidth()), 0 };
gfx.fillRect({ trackPos + correction, getTrackWidth(), bounds.h + (correction.y * -2) }, getTrackColor());
f32 progressH = (bounds.h + (correction.y * -2)) * t;
gfx.fillRect({ trackPos.x + correction.x, trackPos.y + correction.y + (bounds.h + (correction.y * -2)) - progressH, getTrackWidth(), progressH }, getTrackProgressColor());
Vec2 swappedSize = getHandleSize().swap();
f32 handleY = bounds.y + bounds.h * (1.0f - t) - (swappedSize.y / 2.0f);
gfx.fillRect({ bounds.x + l_centerw(swappedSize.x), handleY, swappedSize }, getHandleColor());
}
}
void Slider::enableVertical(bool enable)
{
if (isVertical() && enable) return;
if (isHorizontal() && !enable) return;
m_vertical = enable;
setSize(getSize().swap());
}
f32 Slider::snap_to_step(f32 val)
{
if (m_step <= 0.0f) return val; // full precision
f32 snapped = std::round((val - m_min) / m_step) * m_step + m_min;
return std::clamp(snapped, m_min, m_max);
}
f32 Slider::mouse_position_to_value(Vec2 mousePosition)
{
const auto& bounds = getGlobalBounds();
f32 raw = isHorizontal() ? (mousePosition.x - bounds.x) / bounds.w : 1.0f - (mousePosition.y - bounds.y) / bounds.h;
raw = std::clamp(raw, 0.0f, 1.0f);
return m_min + raw * (m_max - m_min);
}
void Slider::set_value(f32 val)
{
if (m_value == val)
return;
f32 old = m_value;
m_value = snap_to_step(val);
if (callback_onValueChanged)
callback_onValueChanged(old, m_value);
}
}
}
}

View file

@ -0,0 +1,92 @@
/*
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 <ogfx/gui/widgets/Widget.hpp>
namespace ogfx
{
namespace gui
{
namespace widgets
{
class Slider : public Widget
{
public:
inline Slider(WindowCore& window, bool vertical = false, f32 min = 0.0f, f32 max = 1.0f, f32 step = 0.1f) : Widget({ 0, 0, 0, 0 }, window) { create(vertical, min, max, step); }
Slider& create(bool vertical, f32 min, f32 max, f32 step);
void applyTheme(const ostd::Stylesheet& theme) override;
void onMouseReleased(const Event& event) override;
void onMouseDragged(const Event& event) override;
void onDraw(ogfx::BasicRenderer2D& gfx) override;
inline f32 getValue(void) const { return m_value; }
inline void setValueChangedCallback(std::function<void(f32 oldValue, f32 newValue)> callback) { callback_onValueChanged = std::move(callback); }
void enableVertical(bool enable = true);
inline bool isVertical(void) const { return m_vertical; }
inline void disableVertical(void) { enableVertical(false); }
inline bool isHorizontal(void) const { return !isVertical(); }
inline void enableHorizontal(bool enable = true) { enableVertical(!enable); }
inline void disableHorizontal(void) { enableHorizontal(false); }
OSTD_BOOL_PARAM_GETSET_E(MiddleClickShortcut, m_middleClickShortcut);
OSTD_PARAM_GETSET(f32, MinValue, m_min);
OSTD_PARAM_GETSET(f32, MaxValue, m_max);
OSTD_PARAM_GETSET(f32, Step, m_step);
OSTD_PARAM_GETSET(f32, TrackWidth, m_trackWidth);
OSTD_PARAM_GETSET(Color, TrackColor, m_trackColor);
OSTD_PARAM_GETSET(Color, TrackProgressColor, m_trackProgressColor);
OSTD_BOOL_PARAM_GETSET_E(DrawTicks, m_drawTicks);
OSTD_PARAM_GETSET(f32, StepTickHeight, m_tickHeight);
OSTD_PARAM_GETSET(Color, StepTickColor, m_tickColor);
OSTD_PARAM_GETSET(Color, HandleColor, m_handleColor);
OSTD_PARAM_GETSET(Vec2, HandleSize, m_handleSize);
private:
f32 snap_to_step(f32 val);
f32 mouse_position_to_value(Vec2 mousePosition);
void set_value(f32 val);
private:
f32 m_value { 0.0f };
std::function<void(f32 oldValue, f32 newValue)> callback_onValueChanged { nullptr };
f32 m_min { 0.0f };
f32 m_max { 1.0f };
f32 m_step { 0.1f };
bool m_vertical { false };
bool m_middleClickShortcut { true };
f32 m_trackWidth { 6 };
Color m_trackColor { "#500000FF" };
Color m_trackProgressColor { "#AA0000FF" };
bool m_drawTicks { true };
f32 m_tickHeight { 4 };
Color m_tickColor { Colors::Crimson };
Color m_handleColor { Colors::Crimson };
Vec2 m_handleSize { 10, 18 };
};
}
}
}

View file

@ -83,6 +83,7 @@ namespace ostd
inline Vec2 div(f32 scalar) const { return { x / scalar, y / scalar }; } inline Vec2 div(f32 scalar) const { return { x / scalar, y / scalar }; }
inline Vec2 propx(f32 new_x) const { return { new_x, new_x * (y / x) }; } inline Vec2 propx(f32 new_x) const { return { new_x, new_x * (y / x) }; }
inline Vec2 propy(f32 new_y) const { return { new_y * (x / y), new_y }; } inline Vec2 propy(f32 new_y) const { return { new_y * (x / y), new_y }; }
inline Vec2 swap(void) { return { y, x }; }
inline Vec2 normalize(void) const { f32 m = _zp(mag()); return { x / m, y / m }; } inline Vec2 normalize(void) const { f32 m = _zp(mag()); return { x / m, y / m }; }
inline f32 dist(Vec2 v2) const { return std::sqrt((f32)((v2.x - x) * (v2.x - x)) + ((v2.y - y) * (v2.y - y))); } inline f32 dist(Vec2 v2) const { return std::sqrt((f32)((v2.x - x) * (v2.x - x)) + ((v2.y - y) * (v2.y - y))); }
@ -102,6 +103,7 @@ namespace ostd
inline Vec2& divm(const f32& scalar) { x /= scalar; y /= scalar; return *this; } inline Vec2& divm(const f32& scalar) { x /= scalar; y /= scalar; return *this; }
inline Vec2& propxm(const f32& new_x) { x = new_x; y = new_x * (y / x); return *this; } inline Vec2& propxm(const f32& new_x) { x = new_x; y = new_x * (y / x); return *this; }
inline Vec2& propym(const f32& new_y) { x = new_y * (x / y); y = new_y; return *this; } inline Vec2& propym(const f32& new_y) { x = new_y * (x / y); y = new_y; return *this; }
inline Vec2& swapm(void) { f32 tmp = x; x = y; y = tmp; return *this; }
inline Vec2& normalizem(void) { f32 m = _zp(mag()); x /= m; y /= m; return *this; } inline Vec2& normalizem(void) { f32 m = _zp(mag()); x /= m; y /= m; return *this; }
inline Vec2& setMag(const f32& mag) { return normalizem().mulm(mag); } inline Vec2& setMag(const f32& mag) { return normalizem().mulm(mag); }

View file

@ -1,73 +1,83 @@
/* /*
OmniaFramework - A collection of useful functionality OmniaFramework - A collection of useful functionality
Copyright (C) 2025 OmniaX-Dev 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 OmniaFramework is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
OmniaFramework is distributed in the hope that it will be useful, OmniaFramework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>. along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
#define WINDOWS_OS #define WINDOWS_OS
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT 0x0501 #define _WIN32_WINNT 0x0501
#elif defined(__APPLE__) #elif defined(__APPLE__)
#define MAC_OS #define MAC_OS
#else #else
#define LINUX_OS #define LINUX_OS
#endif #endif
//Basic constants //Basic constants
#if !defined(BUILD_NR) #if !defined(BUILD_NR)
#define BUILD_NR 0 #define BUILD_NR 0
#endif #endif
#if !defined(MAJ_V) #if !defined(MAJ_V)
#define MAJ_V 0 #define MAJ_V 0
#endif #endif
#if !defined(MIN_V) #if !defined(MIN_V)
#define MIN_V 0 #define MIN_V 0
#endif #endif
#if !defined(VERSION_STR) #if !defined(VERSION_STR)
#define VERSION_STR "0.0.0" #define VERSION_STR "0.0.0"
#endif #endif
#if !defined(NULL) #if !defined(NULL)
#define NULL 0 #define NULL 0
#endif #endif
#ifndef OX_DEBUG_BUILD #ifndef OX_DEBUG_BUILD
#define _DEBUG(n) #define _DEBUG(n)
#ifndef OX_RELEASE_BUILD #ifndef OX_RELEASE_BUILD
#define OX_RELEASE_BUILD #define OX_RELEASE_BUILD
#endif #endif
#else #else
#define _DEBUG(n) std::cout << (i32)n << "\n"; #define _DEBUG(n) std::cout << (i32)n << "\n";
#endif #endif
#define OSTD_BOOL_PARAM_GETSET_E(name, member) \ #define OSTD_BOOL_PARAM_GETSET_E(name, member) \
inline bool is##name##Enabled(void) const { return member; } \ inline bool is##name##Enabled(void) const { return member; } \
inline void enable##name(bool enable = true) { member = enable; } \ inline void enable##name(bool enable = true) { member = enable; } \
inline void disable##name(void) { enable##name(false); } inline void disable##name(void) { enable##name(false); }
#define OSTD_BOOL_PARAM_GETSET_I(name, member) \ #define OSTD_BOOL_PARAM_GETSET_I(name, member) \
inline bool is##name(void) const { return member; } \ inline bool is##name(void) const { return member; } \
inline void enable##name(bool enable = true) { member = enable; } \ inline void enable##name(bool enable = true) { member = enable; } \
inline void disable##name(void) { enable##name(false); } inline void disable##name(void) { enable##name(false); }
#define OSTD_PARAM_GETSET(type, name, member) \ #define OSTD_BOOL_PARAM_GETSET_E_NEG(name, member) \
inline type get##name(void) const { return member; } \ inline bool is##name##Enabled(void) const { return !member; } \
inline void set##name(const type& value) { member = value; } \ inline void enable##name(bool enable = true) { member = !enable; } \
inline void disable##name(void) { enable##name(false); }
#define OSTD_BOOL_PARAM_GETSET_I_NEG(name, member) \
inline bool is##name(void) const { return !member; } \
inline void enable##name(bool enable = true) { member = !enable; } \
inline void disable##name(void) { enable##name(false); }
#define OSTD_PARAM_GETSET(type, name, member) \
inline type get##name(void) const { return member; } \
inline void set##name(const type& value) { member = value; } \

View file

@ -105,6 +105,14 @@ class Window : public ogfx::gui::Window
m_panel2.setTooltipText("PANEL tooltip"); m_panel2.setTooltipText("PANEL tooltip");
m_prog.setSize(300, 30); m_prog.setSize(300, 30);
m_slide.setSize(300, 30);
m_slide.setMinValue(-1.0f);
// m_slide.enableVertical();
// m_slide.setStep(0);
m_slide.setValueChangedCallback([&](f32 oldVal, f32 newVal) -> void {
m_slideLbl.setText(String("").add(newVal, 2));
});
m_slideLbl.setText(String("").add(m_slide.getValue(), 2));
m_tabs.setSize(900, 700); m_tabs.setSize(900, 700);
auto& t1 = m_tabs.addTab("Tab1"); auto& t1 = m_tabs.addTab("Tab1");
@ -123,6 +131,8 @@ class Window : public ogfx::gui::Window
std::cout << sender.getText() << "\n\n"; std::cout << sender.getText() << "\n\n";
}); });
t1.addWidget(m_prog, { 30, 200 }); t1.addWidget(m_prog, { 30, 200 });
t1.addWidget(m_slide, { 30, 250 });
t1.addWidget(m_slideLbl, { 340, 240 });
t2.addWidget(m_panel2, { 500, 100 }); t2.addWidget(m_panel2, { 500, 100 });
m_panel3.addWidget(m_label4); m_panel3.addWidget(m_label4);
@ -147,7 +157,7 @@ class Window : public ogfx::gui::Window
while (prog < 100) while (prog < 100)
{ {
prog += 0.6f; prog += 0.6f;
ostd::Time::sleep(ostd::Random::getui32(10, 200)); ostd::Time::sleep(ostd::Random::getui32(10, 500));
this->m_prog.setProgress(prog); this->m_prog.setProgress(prog);
} }
return true; return true;
@ -188,6 +198,8 @@ class Window : public ogfx::gui::Window
TabPanel m_tabs { *this }; TabPanel m_tabs { *this };
RadioButtonGroup m_radioGroup; RadioButtonGroup m_radioGroup;
ProgressBar m_prog { *this, 0, 100 }; ProgressBar m_prog { *this, 0, 100 };
Slider m_slide { *this };
Label m_slideLbl { *this };
ostd::StepTimer m_timer; ostd::StepTimer m_timer;
ostd::AsyncJob<bool> m_progressJob; ostd::AsyncJob<bool> m_progressJob;