Added Horizontal scrollbar

This commit is contained in:
OmniaX-Dev 2026-04-19 05:42:03 +02:00
parent 891e17948a
commit 510ce50684
9 changed files with 143 additions and 58 deletions

View file

@ -43,13 +43,12 @@ window.backgroundColor = rgba(160, 160, 160, 255)
(scrollbar) {
(thumb) {
color = rgb(140, 140, 140)
borderRadius = 16
borderRadius = 0.0
borderColor = rgb(150, 150, 150)
showBorder = true
}
(track) {
color = rgb(90, 90, 90)
borderRadii = rect(0, 0, 10, 0)
}
width = 18.0
}
@ -145,7 +144,7 @@ window.backgroundColor = rgba(160, 160, 160, 255)
}
(button:pressed) {
borderColor = $accentColorDark
textColor = #DDDDDDFF
textColor = #BBBBBBFF
backgroundGradient = gradientV(rgb(120, 120, 120) - rgb(160, 160, 160))
}
% ====================

View file

@ -35,7 +35,8 @@ namespace ogfx
disableDrawBox();
disableFocus();
enableStopEvents();
allowScroll(true);
allowVScroll(true);
allowHScroll(true);
m_vScrollbar.setMargin({ 0, getTitlebarHeight(), 0, 0 });
m_vScrollbar.enableManualDraw(true);
addWidget(m_vScrollbar);
@ -72,7 +73,6 @@ namespace ogfx
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);
m_basePadding = getPadding();
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);
@ -103,18 +103,34 @@ namespace ogfx
void Panel::onMouseScrolled(const Event& event)
{
if (!isScrollAllowed())
return;
if (std::abs(event.mouse->scrollAmount.y) > 0)
m_scrollVelocity.y += m_scrollSpeed * event.mouse->scrollAmount.y * 15.0f;
if (std::abs(event.mouse->scrollAmount.x) > 0)
m_scrollVelocity.x += m_scrollSpeed * event.mouse->scrollAmount.x * 15.0f;
if (m_smoothScrollTimer.isStopped())
m_smoothScrollTimer.restart();
event.handle();
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 Panel::setTitlebarType(const String& type)
@ -122,17 +138,17 @@ namespace ogfx
String t = type.new_toLower().trim();
if (t == TitleBarTypes::None)
{
setPadding(m_basePadding);
setContentOffset({ 0, 0 });
m_titlebarType = TitleBarTypes::NoneValue;
}
else if (t == TitleBarTypes::Minimal)
{
setPadding(m_basePadding + Rectangle { 0, m_titlebarHeight, 0, 0 });
setContentOffset({ 0, m_titlebarHeight });
m_titlebarType = TitleBarTypes::MinimalValue;
}
else if (t == TitleBarTypes::Full)
{
setPadding(m_basePadding + Rectangle { 0, m_titlebarHeight, 0, 0 });
setContentOffset({ 0, m_titlebarHeight });
m_titlebarType = TitleBarTypes::FullValue;
}
}
@ -180,12 +196,12 @@ namespace ogfx
bool Panel::needsVScroll(void) const
{
return isScrollAllowed() && getContentExtents().h > getContentBounds().h;
return isVScrollAllowed() && getContentExtents().h > getContentBounds().h;
}
bool Panel::needsHScroll(void) const
{
return isScrollAllowed() && getContentExtents().w > getContentBounds().w;
return isHScrollAllowed() && getContentExtents().w > getContentBounds().w;
}
void Panel::onWidgetAdded(Widget& child)
@ -196,6 +212,24 @@ namespace ogfx
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

@ -57,6 +57,8 @@ namespace ogfx
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; }
@ -81,12 +83,12 @@ namespace ogfx
i32 m_titlebarFontSize { 26 };
Color m_titlebarColor { Colors::Transparent };
Color m_titlebarBorderColor { Colors::Black };
Rectangle m_basePadding { 0, 0, 0, 0 };
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

@ -50,7 +50,6 @@ 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 });
m_trackBorderRadii = getThemeValue<Rectangle>(theme, "scrollbar.track.borderRadii", { 0, 0, 10, 0 });
}
void VerticalScrollBar::afterDraw(ogfx::BasicRenderer2D& gfx)
@ -66,7 +65,7 @@ namespace ogfx
if (!m_mousePressed)
return;
if (is_mouse_in_thumb({ event.mouse->position_x, event.mouse->position_y }))
if (isMouseInsideThumb({ event.mouse->position_x, event.mouse->position_y }))
m_mouseDragged = true;
if (m_mouseDragged)
@ -103,11 +102,16 @@ namespace ogfx
update_thumb();
}
bool VerticalScrollBar::isMouseInsideThumb(const Vec2& mouse_pos)
{
return m_thumbGlobalBounds.contains(mouse_pos, true);
}
void VerticalScrollBar::update_thumb(void)
{
x = getParent()->getSize().x - w;
y = 0;
h = getParent()->getGlobalContentBounds().h;
h = getParent()->getGlobalPureContentBounds().h;
auto ext = getParent()->getContentExtents();
auto cont = getParent()->getContentBounds();
@ -127,6 +131,8 @@ namespace ogfx
void VerticalScrollBar::set_thumb_y(f32 thumby)
{
if (!getParent()->needsVScroll())
return;
auto bounds = getGlobalBounds() - m_correctionOffset;
auto ext = getParent()->getContentExtents();
auto cont = getParent()->getContentBounds();
@ -139,11 +145,6 @@ namespace ogfx
getParent()->setScrollOffset(parentScrollOffset);
}
bool VerticalScrollBar::is_mouse_in_thumb(const Vec2& mouse_pos)
{
return m_thumbGlobalBounds.contains(mouse_pos, true);
}
@ -172,8 +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 });
m_trackBorderRadii = getThemeValue<Rectangle>(theme, "scrollbar.track.borderRadii", { 0, 0, 10, 10 });
}
}
void HorizontalScrollbar::afterDraw(ogfx::BasicRenderer2D& gfx)
{
@ -188,7 +188,7 @@ namespace ogfx
if (!m_mousePressed)
return;
if (is_mouse_in_thumb({ event.mouse->position_x, event.mouse->position_y }))
if (isMouseInsideThumb({ event.mouse->position_x, event.mouse->position_y }))
m_mouseDragged = true;
if (m_mouseDragged)
@ -225,11 +225,16 @@ namespace ogfx
update_thumb();
}
bool HorizontalScrollbar::isMouseInsideThumb(const Vec2& mouse_pos)
{
return m_thumbGlobalBounds.contains(mouse_pos, true);
}
void HorizontalScrollbar::update_thumb(void)
{
x = 0;
y = getParent()->getSize().y - h;
w = getParent()->getGlobalBounds().w;
w = getParent()->getGlobalBounds().w - getParent()->getVScrollbarSize();
auto ext = getParent()->getContentExtents();
auto cont = getParent()->getContentBounds();
@ -245,11 +250,13 @@ namespace ogfx
auto bounds = getGlobalBounds() - m_correctionOffset;
m_thumbGlobalBounds = { bounds.x + 2 + m_thumbX, bounds.y + gety(), m_thumbWidth, geth() - 4 };
std::cout << Rectangle::toString() << "\n";
m_thumbGlobalBounds = { bounds.x + getx() + m_thumbX, bounds.y + 2, m_thumbWidth, geth() - 4 };
}
void HorizontalScrollbar::set_thumb_x(f32 thumbx)
{
if (!getParent()->needsHScroll())
return;
auto bounds = getGlobalBounds() - m_correctionOffset;
auto ext = getParent()->getContentExtents();
auto cont = getParent()->getContentBounds();
@ -261,11 +268,6 @@ namespace ogfx
parentScrollOffset.x = maxScroll * scrollProgress;
getParent()->setScrollOffset(parentScrollOffset);
}
bool HorizontalScrollbar::is_mouse_in_thumb(const Vec2& mouse_pos)
{
return m_thumbGlobalBounds.contains(mouse_pos, true);
}
}
}
}

View file

@ -39,6 +39,7 @@ namespace ogfx
void onMousePressed(const Event& event) override;
void onMouseReleased(const Event& event) override;
void onUpdate(void) override;
bool isMouseInsideThumb(const Vec2& mouse_pos);
inline void setx(f32 xx) override { }
inline void sety(f32 yy) override { }
@ -48,7 +49,6 @@ namespace ogfx
private:
void update_thumb(void);
void set_thumb_y(f32 thumby);
bool is_mouse_in_thumb(const Vec2& mouse_pos);
private:
f32 m_thumbHeight { 0 };
@ -77,6 +77,7 @@ namespace ogfx
void onMousePressed(const Event& event) override;
void onMouseReleased(const Event& event) override;
void onUpdate(void) override;
bool isMouseInsideThumb(const Vec2& mouse_pos);
inline void setx(f32 xx) override { }
inline void sety(f32 yy) override { }
@ -86,7 +87,6 @@ namespace ogfx
private:
void update_thumb(void);
void set_thumb_x(f32 thumbx);
bool is_mouse_in_thumb(const Vec2& mouse_pos);
private:
f32 m_thumbWidth { 0 };
@ -97,7 +97,7 @@ namespace ogfx
f32 m_dragGrabOffset { 0 };
Rectangle m_correctionOffset { 0, 0, 0, 0 };
Rectangle m_trackBorderRadii { 0, 0, 10, 10 };
Rectangle m_trackBorderRadii { 0, 0, 0, 10 };
f32 m_thumbBorderRadius { 16 };
Color m_trackColor { 70, 70, 70 };
Color m_thumbColor { 120, 120, 120 };

View file

@ -64,6 +64,7 @@ namespace ogfx
if (!isIgnoreScrollAllowed())
{
glob += m_parent->getPadding().getPosition();
glob += m_parent->getContentOffset();
glob += m_parent->getScrollOffset();
}
}
@ -84,15 +85,24 @@ namespace ogfx
Rectangle Widget::getContentBounds(void) const
{
auto pad = getPadding();
return { pad.getPosition(), (getSize() - (pad.getSize() * 2)) };
return { Vec2 { m_contentOffset.x, m_contentOffset.y } + pad.getPosition(), (getSize() - m_contentOffset - (pad.getSize() * 2)) };
}
Rectangle Widget::getPureContentBounds(void) const
{
return { { m_contentOffset.x, m_contentOffset.y }, (getSize() - m_contentOffset) };
}
Rectangle Widget::getGlobalContentBounds(void) const
{
auto pad = getPadding();
return { getGlobalContentPosition(), getContentBounds().getSize() };
}
Rectangle Widget::getGlobalPureContentBounds(void) const
{
return { getGlobalPosition() + getContentOffset(), getPureContentBounds().getSize() };
}
Rectangle Widget::getContentExtents(void) const
{
f32 maxX = 0, maxY = 0;
@ -218,8 +228,12 @@ namespace ogfx
gfx.fillRoundRect({ getGlobalPosition(), getSize() }, m_backgroundColor, m_borderRadius);
}
onDraw(gfx);
// gfx.fillRect(getGlobalPureContentBounds(), { 0, 255, 0, 120 });
gfx.pushClippingRect(getGlobalPureContentBounds(), true);
if (hasChildren())
m_widgets.draw(gfx);
gfx.popClippingRect();
if (m_showBorder)
gfx.drawRoundRect({ getGlobalPosition(), getSize() }, m_borderColor, m_borderRadius, m_borderWidth);
afterDraw(gfx);

View file

@ -47,7 +47,9 @@ namespace ogfx
virtual Vec2 getGlobalContentPosition(void) const;
virtual Rectangle getGlobalBounds(void) const;
virtual Rectangle getContentBounds(void) const;
virtual Rectangle getPureContentBounds(void) const;
virtual Rectangle getGlobalContentBounds(void) const;
virtual Rectangle getGlobalPureContentBounds(void) const;
virtual Rectangle getContentExtents(void) const;
using Rectangle::contains;
bool contains(Vec2 p, bool includeBounds = false) const override;
@ -58,6 +60,8 @@ namespace ogfx
inline virtual void addScrollOffset(const Vec2& offset) { }
inline virtual bool needsVScroll(void) const { return false; }
inline virtual bool needsHScroll(void) const { return false; }
inline virtual f32 getVScrollbarSize(void) const { return 0; }
inline virtual f32 getHScrollbarSize(void) const { return 0; }
void addThemeOverride(const String& fullKey, ostd::Stylesheet::TypeVariant value);
void reloadTheme(bool propagate = false);
void setThemeQualifier(const String& qualifier, bool value = true);
@ -137,8 +141,10 @@ namespace ogfx
inline bool isChildrenEnabled(void) const { return m_allowChildren; }
inline void setPadding(const Rectangle& pad) { m_padding = pad; }
inline void setMargin(const Rectangle& margin) { m_margin = margin; }
inline void setContentOffset(const Vec2& offset) { m_contentOffset = offset; }
inline Rectangle getPadding(void) const { return m_padding; }
inline Rectangle getMargin(void) const { return m_margin; }
inline Vec2 getContentOffset(void) const { return m_contentOffset; }
inline bool isMouseInside(void) const { return m_mouseInside; }
inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; }
inline const stdvec<String>& getThemeIDList(void) const { return m_themeIDList; }
@ -170,8 +176,10 @@ 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; }
inline void allowVScroll(bool allow = true) { m_vScrollEnabled = allow; }
inline bool isVScrollAllowed(void) const { return m_vScrollEnabled; }
inline void allowHScroll(bool allow = true) { m_hScrollEnabled = allow; }
inline bool isHScrollAllowed(void) const { return m_hScrollEnabled; }
inline void allowIgnoreScroll(bool allow = true) { m_ignoreScroll = allow; }
inline bool isIgnoreScrollAllowed(void) const { return m_ignoreScroll; }
@ -233,10 +241,11 @@ namespace ogfx
bool m_clipContents { true };
bool m_acceptDragAndDrop { false };
bool m_visible { true };
bool m_allowScroll { false };
bool m_ignoreScroll { false};
bool m_manualDraw { false };
bool m_topMost { false };
bool m_vScrollEnabled { false };
bool m_hScrollEnabled { false };
MouseEventData::eButton m_pressedButton { MouseEventData::eButton::None };
stdvec<String> m_themeIDList;
@ -254,6 +263,7 @@ namespace ogfx
Rectangle m_padding { 0, 0, 0, 0 };
Rectangle m_margin { 0, 0, 0, 0 };
Vec2 m_contentOffset { 0, 0 };
static ostd::BaseObject* s_dragAndDropData;
static bool s_hasDragAndDropData;

View file

@ -102,7 +102,10 @@ namespace ogfx
finalRect = m_clipStack.back().getIntersection(rect, false);
if (!m_clipStack.empty() && m_clipStack.back() == finalRect)
{
m_clipStack.push_back(finalRect);
return;
}
flushBatch();
m_clipStack.push_back(finalRect);
@ -121,22 +124,26 @@ namespace ogfx
if (!m_initialized) return;
if (m_clipStack.empty()) return;
Rectangle popped = m_clipStack.back();
m_clipStack.pop_back();
flushBatch();
if (m_clipStack.empty())
{
flushBatch();
SDL_SetRenderClipRect(m_window->getSDLRenderer(), nullptr);
return;
}
const auto& rect = m_clipStack.back();
const auto& prev = m_clipStack.back();
if (prev == popped)
return;
flushBatch();
SDL_Rect r;
r.x = (i32)std::round(rect.x);
r.y = (i32)std::round(rect.y);
r.w = (i32)std::round(rect.w);
r.h = (i32)std::round(rect.h);
r.x = (i32)std::round(prev.x);
r.y = (i32)std::round(prev.y);
r.w = (i32)std::round(prev.w);
r.h = (i32)std::round(prev.h);
SDL_SetRenderClipRect(m_window->getSDLRenderer(), &r);
}

View file

@ -71,13 +71,27 @@ class Window : public ogfx::gui::Window
m_label2.setText("Label2");
m_label3.setText("Label3");
m_label4.setText("Label4");
m_label5.setText("Label5");
m_panel1.setSize(300, 140);
m_panel1.allowScroll(false);
m_panel1.setSize(300, 300);
m_panel1.allowVScroll(false);
m_panel1.allowHScroll(false);
m_panel1.setTitle("Panel 1");
m_panel3.setSize(150, 150);
m_panel3.allowVScroll(false);
m_panel3.allowHScroll(false);
m_panel3.setTitle("Panel 3");
m_panel2.setSize(600, 400);
m_panel2.setTitle("Panel 2");
m_panel3.addWidget(m_label4);
m_panel1.addWidget(m_label2);
m_panel1.addWidget(m_panel3, { 100, 50 });
m_panel1.addWidget(m_label5, { 0, 60 });
m_panel2.addWidget(m_label3);
m_panel2.addWidget(m_panel1, { 400, 50 });
@ -104,7 +118,7 @@ class Window : public ogfx::gui::Window
void onRedraw(ogfx::BasicRenderer2D& gfx) override
{
gfx.drawAnimation(m_anim, { 200, 200 });
// gfx.fillRect(m_panel2.getGlobalContentBounds(), { 0, 255, 0, 120 });
// gfx.fillRect(m_panel2.getGlobalPureContentBounds(), { 0, 255, 0, 120 });
}
void onFixedUpdate(void) override
@ -116,8 +130,11 @@ class Window : public ogfx::gui::Window
Label m_label1 { *this };
Label m_label2 { *this };
Label m_label3 { *this };
Label m_label4 { *this };
Label m_label5 { *this };
Panel m_panel1 { *this };
Panel m_panel2 { *this };
Panel m_panel3 { *this };
CheckBox m_check1 { *this };
Button m_btn1 { *this };