Added ScrollableWidget class

This commit is contained in:
OmniaX-Dev 2026-04-19 06:50:42 +02:00
parent 510ce50684
commit 63d00adf96
5 changed files with 192 additions and 159 deletions

View file

@ -43,7 +43,7 @@ window.backgroundColor = rgba(160, 160, 160, 255)
(scrollbar) {
(thumb) {
color = rgb(140, 140, 140)
borderRadius = 0.0
borderRadius = 16.0
borderColor = rgb(150, 150, 150)
showBorder = true
}

View file

@ -35,28 +35,6 @@ namespace ogfx
disableDrawBox();
disableFocus();
enableStopEvents();
allowVScroll(true);
allowHScroll(true);
m_vScrollbar.setMargin({ 0, getTitlebarHeight(), 0, 0 });
m_vScrollbar.enableManualDraw(true);
addWidget(m_vScrollbar);
m_hScrollbar.setMargin({ 0, 0, 0, 0 });
m_hScrollbar.enableManualDraw(true);
addWidget(m_hScrollbar);
m_smoothScrollTimer.create(60.0f, [&](f64 dt) -> void {
f32 stepX = m_scrollVelocity.x * (1.0f - m_scrollSmoothFactor);
f32 stepY = m_scrollVelocity.y * (1.0f - m_scrollSmoothFactor);
addScrollOffset({ stepX, stepY });
m_scrollVelocity -= { stepX, stepY };
if (std::abs(m_scrollVelocity.x) < 0.5f) m_scrollVelocity.x = 0;
if (std::abs(m_scrollVelocity.y) < 0.5f) m_scrollVelocity.y = 0;
}, true);
m_smoothScrollTimer.setStopCondition([&](void) -> bool {
return m_scrollVelocity.x == 0 && m_scrollVelocity.y == 0;
});
validate();
return *this;
}
@ -71,8 +49,8 @@ namespace ogfx
setBorderColor(getThemeValue<Color>(theme, "panel.borderColor", Colors::Black));
setPadding(getThemeValue<Rectangle>(theme, "panel.padding", { 15, 15, 15, 15 }));
setMargin(getThemeValue<Rectangle>(theme, "panel.margin", { 0, 0, 0, 0 }));
m_scrollSpeed = getThemeValue<f32>(theme, "panel.scrollSpeed", 0.8f);
m_scrollSmoothFactor = std::clamp(getThemeValue<f32>(theme, "panel.scrollSmoothFactor", 0.7f), 0.0f, 1.0f);
setScrollSpeed(getThemeValue<f32>(theme, "panel.scrollSpeed", 0.8f));
setScrollSmoothFactor(getThemeValue<f32>(theme, "panel.scrollSmoothFactor", 0.7f));
m_titleColor = getThemeValue<Color>(theme, "panel.titleColor", Colors::Black);
m_titlebarColor = getThemeValue<Color>(theme, "panel.titlebarColor", Colors::Transparent);
m_titlebarBorderColor = getThemeValue<Color>(theme, "panel.titlebarBorderColor", Colors::Black);
@ -83,54 +61,10 @@ namespace ogfx
setTitlebarType(getThemeValue<String>(theme, "panel.titlebarType", TitleBarTypes::None));
}
void Panel::onDraw(ogfx::BasicRenderer2D& gfx)
{
// if (isScrollAllowed())
// gfx.fillRect(getContentExtents() + Rectangle { getGlobalPosition(), 0, 0 }, { 80, 0, 0, 30 });
}
void Panel::onUpdate(void)
{
m_smoothScrollTimer.update();
}
void Panel::afterDraw(ogfx::BasicRenderer2D& gfx)
{
draw_titlebar(gfx);
m_vScrollbar.__draw(gfx);
m_hScrollbar.__draw(gfx);
}
void Panel::onMouseScrolled(const Event& event)
{
if (isVScrollAllowed())
{
bool mouseInsideHScrollbar = m_hScrollbar.isMouseInsideThumb({ event.mouse->position_x, event.mouse->position_y });
if (std::abs(event.mouse->scrollAmount.y) > 0 && !mouseInsideHScrollbar)
{
m_scrollVelocity.y += m_scrollSpeed * event.mouse->scrollAmount.y * m_scrollSpeedMultiplier;
if (m_smoothScrollTimer.isStopped())
m_smoothScrollTimer.restart();
event.handle();
}
else if (std::abs(event.mouse->scrollAmount.y) > 0)
{
m_scrollVelocity.x += m_scrollSpeed * event.mouse->scrollAmount.y * m_scrollSpeedMultiplier;
if (m_smoothScrollTimer.isStopped())
m_smoothScrollTimer.restart();
event.handle();
}
}
if (isHScrollAllowed())
{
if (std::abs(event.mouse->scrollAmount.x) > 0)
{
m_scrollVelocity.x += m_scrollSpeed * event.mouse->scrollAmount.x * m_scrollSpeedMultiplier;
if (m_smoothScrollTimer.isStopped())
m_smoothScrollTimer.restart();
event.handle();
}
}
drawScrollbars(gfx);
}
void Panel::setTitlebarType(const String& type)
@ -139,16 +73,19 @@ namespace ogfx
if (t == TitleBarTypes::None)
{
setContentOffset({ 0, 0 });
updateScrollbarsSize();
m_titlebarType = TitleBarTypes::NoneValue;
}
else if (t == TitleBarTypes::Minimal)
{
setContentOffset({ 0, m_titlebarHeight });
updateScrollbarsSize();
m_titlebarType = TitleBarTypes::MinimalValue;
}
else if (t == TitleBarTypes::Full)
{
setContentOffset({ 0, m_titlebarHeight });
updateScrollbarsSize();
m_titlebarType = TitleBarTypes::FullValue;
}
}
@ -164,72 +101,6 @@ namespace ogfx
}
}
void Panel::setScrollOffset(const Vec2& offset)
{
auto ext = getContentExtents();
auto cont = getContentBounds();
f32 maxScrollY = -(ext.h - cont.h);
f32 maxScrollX = -(ext.w - cont.w);
m_scrollOffset = offset;
if (m_scrollOffset.y < maxScrollY) m_scrollOffset.y = maxScrollY;
if (m_scrollOffset.y > 0) m_scrollOffset.y = 0;
if (m_scrollOffset.x < maxScrollX) m_scrollOffset.x = maxScrollX;
if (m_scrollOffset.x > 0) m_scrollOffset.x = 0;
}
void Panel::addScrollOffset(const Vec2& offset)
{
auto ext = getContentExtents();
auto cont = getContentBounds();
f32 maxScrollY = -(ext.h - cont.h);
f32 maxScrollX = -(ext.w - cont.w);
m_scrollOffset += offset;
if (m_scrollOffset.y < maxScrollY) m_scrollOffset.y = maxScrollY;
if (m_scrollOffset.y > 0) m_scrollOffset.y = 0;
if (m_scrollOffset.x < maxScrollX) m_scrollOffset.x = maxScrollX;
if (m_scrollOffset.x > 0) m_scrollOffset.x = 0;
}
bool Panel::needsVScroll(void) const
{
return isVScrollAllowed() && getContentExtents().h > getContentBounds().h;
}
bool Panel::needsHScroll(void) const
{
return isHScrollAllowed() && getContentExtents().w > getContentBounds().w;
}
void Panel::onWidgetAdded(Widget& child)
{
removeWidget(m_vScrollbar);
removeWidget(m_hScrollbar);
addWidget(m_vScrollbar, { 0, 0 }, true);
addWidget(m_hScrollbar, { 0, 0 }, true);
}
f32 Panel::getVScrollbarSize(void) const
{
if (!isVScrollAllowed())
return 0;
if (!needsVScroll())
return 0;
return m_vScrollbar.getw();
}
f32 Panel::getHScrollbarSize(void) const
{
if (!isHScrollAllowed())
return 0;
if (!needsHScroll())
return 0;
return m_hScrollbar.geth();
}
void Panel::draw_titlebar(BasicRenderer2D& gfx)
{
f32 br = cast<f32>(getBorderWidth());

View file

@ -30,7 +30,7 @@ namespace ogfx
{
namespace widgets
{
class Panel : public Widget
class Panel : public ScrollableWidget
{
public: struct TitleBarTypes
{
@ -43,27 +43,16 @@ namespace ogfx
inline static constexpr i32 MinimalValue = 2;
};
public:
inline Panel(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); }
inline Panel(WindowCore& window) : ScrollableWidget(window) { create(); }
Panel& create(void);
void applyTheme(const ostd::Stylesheet& theme) override;
void onDraw(ogfx::BasicRenderer2D& gfx) override;
void onUpdate(void) override;
void afterDraw(ogfx::BasicRenderer2D& gfx) override;
void onMouseScrolled(const Event& event) override;
void setTitlebarType(const String& type);
String getTitlebarType(void) const;
void setScrollOffset(const Vec2& offset) override;
void addScrollOffset(const Vec2& offset) override;
bool needsVScroll(void) const override;
bool needsHScroll(void) const override;
void onWidgetAdded(Widget& child) override;
f32 getVScrollbarSize(void) const override;
f32 getHScrollbarSize(void) const override;
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; }
inline f32 getTitlebarHeight(void) const { return m_titlebarHeight; }
private:
@ -71,10 +60,6 @@ namespace ogfx
private:
String m_title { "Panel" };
Vec2 m_scrollOffset { 0, 0 };
VerticalScrollBar m_vScrollbar { getWindow() };
HorizontalScrollbar m_hScrollbar { getWindow() };
ostd::StepTimer m_smoothScrollTimer;
Color m_titleColor { Colors::Black };
i32 m_titlebarType = TitleBarTypes::NoneValue;
@ -84,11 +69,6 @@ namespace ogfx
Color m_titlebarColor { Colors::Transparent };
Color m_titlebarBorderColor { Colors::Black };
i32 m_titleTextAlign { 0 };
f32 m_scrollSpeed { 0.8f };
Vec2 m_scrollVelocity { 0.0f, 0.0f };
f32 m_scrollSmoothFactor { 0.7f };
f32 m_scrollSpeedMultiplier { 15.0f };
};
}
}

View file

@ -173,7 +173,7 @@ namespace ogfx
m_thumbBorderColor = getThemeValue<Color>(theme, "scrollbar.thumb.borderColor", { 150, 150, 150 });
m_thumbShowBorder = getThemeValue<bool>(theme, "scrollbar.thumb.showBorder", true);
m_trackColor = getThemeValue<Color>(theme, "scrollbar.track.color", { 70, 70, 70 });
}
}
void HorizontalScrollbar::afterDraw(ogfx::BasicRenderer2D& gfx)
{
@ -268,6 +268,154 @@ namespace ogfx
parentScrollOffset.x = maxScroll * scrollProgress;
getParent()->setScrollOffset(parentScrollOffset);
}
ScrollableWidget& ScrollableWidget::create(void)
{
setTypeName("ogfx::gui::widgets::ScrollableWidget");
allowVScroll(true);
allowHScroll(true);
m_vScrollbar.enableManualDraw(true);
addWidget(m_vScrollbar);
m_hScrollbar.setMargin({ 0, 0, 0, 0 });
m_hScrollbar.enableManualDraw(true);
addWidget(m_hScrollbar);
m_smoothScrollTimer.create(60.0f, [&](f64 dt) -> void {
f32 stepX = m_scrollVelocity.x * (1.0f - m_scrollSmoothFactor);
f32 stepY = m_scrollVelocity.y * (1.0f - m_scrollSmoothFactor);
addScrollOffset({ stepX, stepY });
m_scrollVelocity -= { stepX, stepY };
if (std::abs(m_scrollVelocity.x) < 0.5f) m_scrollVelocity.x = 0;
if (std::abs(m_scrollVelocity.y) < 0.5f) m_scrollVelocity.y = 0;
}, true);
m_smoothScrollTimer.setStopCondition([&](void) -> bool {
return m_scrollVelocity.x == 0 && m_scrollVelocity.y == 0;
});
updateScrollbarsSize();
validate();
return *this;
}
void ScrollableWidget::onUpdate(void)
{
m_smoothScrollTimer.update();
}
void ScrollableWidget::drawScrollbars(ogfx::BasicRenderer2D& gfx)
{
m_vScrollbar.__draw(gfx);
m_hScrollbar.__draw(gfx);
}
void ScrollableWidget::updateScrollbarsSize(void)
{
m_vScrollbar.setMargin({ 0, getPureContentBounds().y, 0, 0 });
}
void ScrollableWidget::onMouseScrolled(const Event& event)
{
if (isVScrollAllowed())
{
bool mouseInsideHScrollbar = m_hScrollbar.isMouseInsideThumb({ event.mouse->position_x, event.mouse->position_y });
if (std::abs(event.mouse->scrollAmount.y) > 0 && !mouseInsideHScrollbar)
{
m_scrollVelocity.y += m_scrollSpeed * event.mouse->scrollAmount.y * m_scrollSpeedMultiplier;
if (m_smoothScrollTimer.isStopped())
m_smoothScrollTimer.restart();
event.handle();
}
else if (std::abs(event.mouse->scrollAmount.y) > 0)
{
m_scrollVelocity.x += m_scrollSpeed * event.mouse->scrollAmount.y * m_scrollSpeedMultiplier;
if (m_smoothScrollTimer.isStopped())
m_smoothScrollTimer.restart();
event.handle();
}
}
if (isHScrollAllowed())
{
if (std::abs(event.mouse->scrollAmount.x) > 0)
{
m_scrollVelocity.x += m_scrollSpeed * event.mouse->scrollAmount.x * m_scrollSpeedMultiplier;
if (m_smoothScrollTimer.isStopped())
m_smoothScrollTimer.restart();
event.handle();
}
}
}
void ScrollableWidget::setScrollOffset(const Vec2& offset)
{
auto ext = getContentExtents();
auto cont = getContentBounds();
f32 maxScrollY = -(ext.h - cont.h);
f32 maxScrollX = -(ext.w - cont.w);
m_scrollOffset = offset;
if (m_scrollOffset.y < maxScrollY) m_scrollOffset.y = maxScrollY;
if (m_scrollOffset.y > 0) m_scrollOffset.y = 0;
if (m_scrollOffset.x < maxScrollX) m_scrollOffset.x = maxScrollX;
if (m_scrollOffset.x > 0) m_scrollOffset.x = 0;
}
void ScrollableWidget::addScrollOffset(const Vec2& offset)
{
auto ext = getContentExtents();
auto cont = getContentBounds();
f32 maxScrollY = -(ext.h - cont.h);
f32 maxScrollX = -(ext.w - cont.w);
m_scrollOffset += offset;
if (m_scrollOffset.y < maxScrollY) m_scrollOffset.y = maxScrollY;
if (m_scrollOffset.y > 0) m_scrollOffset.y = 0;
if (m_scrollOffset.x < maxScrollX) m_scrollOffset.x = maxScrollX;
if (m_scrollOffset.x > 0) m_scrollOffset.x = 0;
}
bool ScrollableWidget::needsVScroll(void) const
{
return isVScrollAllowed() && getContentExtents().h > getContentBounds().h;
}
bool ScrollableWidget::needsHScroll(void) const
{
return isHScrollAllowed() && getContentExtents().w > getContentBounds().w;
}
void ScrollableWidget::onWidgetAdded(Widget& child)
{
removeWidget(m_vScrollbar);
removeWidget(m_hScrollbar);
addWidget(m_vScrollbar, { 0, 0 }, true);
addWidget(m_hScrollbar, { 0, 0 }, true);
}
f32 ScrollableWidget::getVScrollbarSize(void) const
{
if (!isVScrollAllowed())
return 0;
if (!needsVScroll())
return 0;
return m_vScrollbar.getw();
}
f32 ScrollableWidget::getHScrollbarSize(void) const
{
if (!isHScrollAllowed())
return 0;
if (!needsHScroll())
return 0;
return m_hScrollbar.geth();
}
}
}
}

View file

@ -21,6 +21,7 @@
#pragma once
#include <ogfx/gui/widgets/Widget.hpp>
#include <ostd/utils/Time.hpp>
namespace ogfx
{
@ -104,6 +105,39 @@ namespace ogfx
Color m_thumbBorderColor { 150, 150, 150 };
bool m_thumbShowBorder { true };
};
class ScrollableWidget : public Widget
{
public:
inline ScrollableWidget(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); }
ScrollableWidget& create(void);
virtual void onUpdate(void) override;
void drawScrollbars(ogfx::BasicRenderer2D& gfx);
void updateScrollbarsSize(void);
virtual void onMouseScrolled(const Event& event) override;
virtual void setScrollOffset(const Vec2& offset) override;
virtual void addScrollOffset(const Vec2& offset) override;
virtual bool needsVScroll(void) const override;
virtual bool needsHScroll(void) const override;
virtual void onWidgetAdded(Widget& child) override;
virtual f32 getVScrollbarSize(void) const override;
virtual f32 getHScrollbarSize(void) const override;
inline Vec2 getScrollOffset(void) const override { return m_scrollOffset; }
inline void setScrollSpeed(f32 speed) { m_scrollSpeed = speed; }
inline f32 getScrollSpeed(void) const { return m_scrollSpeed; }
inline void setScrollSmoothFactor(f32 speed) { m_scrollSmoothFactor = std::clamp(speed, 0.0f, 1.0f); }
inline f32 getScrollSmoothFactor(void) const { return m_scrollSmoothFactor; }
private:
String m_title { "Panel" };
Vec2 m_scrollOffset { 0, 0 };
VerticalScrollBar m_vScrollbar { getWindow() };
HorizontalScrollbar m_hScrollbar { getWindow() };
ostd::StepTimer m_smoothScrollTimer;
f32 m_scrollSpeed { 0.8f };
Vec2 m_scrollVelocity { 0.0f, 0.0f };
f32 m_scrollSmoothFactor { 0.7f };
f32 m_scrollSpeedMultiplier { 15.0f };
};
}
}
}