Started work on horizontal scrollbar
This commit is contained in:
parent
d8475ecb48
commit
891e17948a
10 changed files with 195 additions and 17 deletions
|
|
@ -1 +1 @@
|
||||||
2069
|
2070
|
||||||
|
|
|
||||||
|
|
@ -463,7 +463,7 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
MouseEventData mmd = get_mouse_state(event);
|
MouseEventData mmd = get_mouse_state(event);
|
||||||
f32 deltaY = event.wheel.y;
|
f32 deltaY = event.wheel.y;
|
||||||
f32 deltaX = event.wheel.x;
|
f32 deltaX = m_invertHorizontalScroll ? -event.wheel.x : event.wheel.x;
|
||||||
if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
|
if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
|
||||||
deltaY = -deltaY;
|
deltaY = -deltaY;
|
||||||
if (deltaY < 0)
|
if (deltaY < 0)
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,7 @@ namespace ogfx
|
||||||
bool m_blockingEvents { false };
|
bool m_blockingEvents { false };
|
||||||
bool m_resizeable { true };
|
bool m_resizeable { true };
|
||||||
bool m_refreshScreen { true };
|
bool m_refreshScreen { true };
|
||||||
|
bool m_invertHorizontalScroll { true };
|
||||||
|
|
||||||
f32 m_systemScale { 1.0f };
|
f32 m_systemScale { 1.0f };
|
||||||
f32 m_userScale { 1.0f };
|
f32 m_userScale { 1.0f };
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,12 @@ namespace ogfx
|
||||||
disableFocus();
|
disableFocus();
|
||||||
enableStopEvents();
|
enableStopEvents();
|
||||||
allowScroll(true);
|
allowScroll(true);
|
||||||
m_scrollbar.setMargin({ 0, getTitlebarHeight(), 0, 0 });
|
m_vScrollbar.setMargin({ 0, getTitlebarHeight(), 0, 0 });
|
||||||
m_scrollbar.enableManualDraw(true);
|
m_vScrollbar.enableManualDraw(true);
|
||||||
addWidget(m_scrollbar);
|
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 {
|
m_smoothScrollTimer.create(60.0f, [&](f64 dt) -> void {
|
||||||
f32 stepX = m_scrollVelocity.x * (1.0f - m_scrollSmoothFactor);
|
f32 stepX = m_scrollVelocity.x * (1.0f - m_scrollSmoothFactor);
|
||||||
f32 stepY = m_scrollVelocity.y * (1.0f - m_scrollSmoothFactor);
|
f32 stepY = m_scrollVelocity.y * (1.0f - m_scrollSmoothFactor);
|
||||||
|
|
@ -94,7 +97,8 @@ namespace ogfx
|
||||||
void Panel::afterDraw(ogfx::BasicRenderer2D& gfx)
|
void Panel::afterDraw(ogfx::BasicRenderer2D& gfx)
|
||||||
{
|
{
|
||||||
draw_titlebar(gfx);
|
draw_titlebar(gfx);
|
||||||
m_scrollbar.__draw(gfx);
|
m_vScrollbar.__draw(gfx);
|
||||||
|
m_hScrollbar.__draw(gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::onMouseScrolled(const Event& event)
|
void Panel::onMouseScrolled(const Event& event)
|
||||||
|
|
@ -174,15 +178,22 @@ namespace ogfx
|
||||||
if (m_scrollOffset.x > 0) m_scrollOffset.x = 0;
|
if (m_scrollOffset.x > 0) m_scrollOffset.x = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Panel::needsScroll(void) const
|
bool Panel::needsVScroll(void) const
|
||||||
{
|
{
|
||||||
return isScrollAllowed() && getContentExtents().h > getContentBounds().h;
|
return isScrollAllowed() && getContentExtents().h > getContentBounds().h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Panel::needsHScroll(void) const
|
||||||
|
{
|
||||||
|
return isScrollAllowed() && getContentExtents().w > getContentBounds().w;
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::onWidgetAdded(Widget& child)
|
void Panel::onWidgetAdded(Widget& child)
|
||||||
{
|
{
|
||||||
removeWidget(m_scrollbar);
|
removeWidget(m_vScrollbar);
|
||||||
addWidget(m_scrollbar, { 0, 0 }, true);
|
removeWidget(m_hScrollbar);
|
||||||
|
addWidget(m_vScrollbar, { 0, 0 }, true);
|
||||||
|
addWidget(m_hScrollbar, { 0, 0 }, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::draw_titlebar(BasicRenderer2D& gfx)
|
void Panel::draw_titlebar(BasicRenderer2D& gfx)
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,8 @@ namespace ogfx
|
||||||
String getTitlebarType(void) const;
|
String getTitlebarType(void) const;
|
||||||
void setScrollOffset(const Vec2& offset) override;
|
void setScrollOffset(const Vec2& offset) override;
|
||||||
void addScrollOffset(const Vec2& offset) override;
|
void addScrollOffset(const Vec2& offset) override;
|
||||||
bool needsScroll(void) const override;
|
bool needsVScroll(void) const override;
|
||||||
|
bool needsHScroll(void) const override;
|
||||||
void onWidgetAdded(Widget& child) override;
|
void onWidgetAdded(Widget& child) override;
|
||||||
inline void setBackGroundColor(const Color& color) { m_backgroundColor = color; }
|
inline void setBackGroundColor(const Color& color) { m_backgroundColor = color; }
|
||||||
inline Color getBackgroundColor(void) { return m_backgroundColor; }
|
inline Color getBackgroundColor(void) { return m_backgroundColor; }
|
||||||
|
|
@ -69,7 +70,8 @@ namespace ogfx
|
||||||
private:
|
private:
|
||||||
String m_title { "Panel" };
|
String m_title { "Panel" };
|
||||||
Vec2 m_scrollOffset { 0, 0 };
|
Vec2 m_scrollOffset { 0, 0 };
|
||||||
VerticalScrollBar m_scrollbar { getWindow() };
|
VerticalScrollBar m_vScrollbar { getWindow() };
|
||||||
|
HorizontalScrollbar m_hScrollbar { getWindow() };
|
||||||
ostd::StepTimer m_smoothScrollTimer;
|
ostd::StepTimer m_smoothScrollTimer;
|
||||||
|
|
||||||
Color m_titleColor { Colors::Black };
|
Color m_titleColor { Colors::Black };
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
namespace widgets
|
namespace widgets
|
||||||
{
|
{
|
||||||
VerticalScrollBar& VerticalScrollBar::create(const String& text)
|
VerticalScrollBar& VerticalScrollBar::create(void)
|
||||||
{
|
{
|
||||||
setPadding({ 0, 0, 0, 0 });
|
setPadding({ 0, 0, 0, 0 });
|
||||||
setMargin({ 0, 0, 0, 0 });
|
setMargin({ 0, 0, 0, 0 });
|
||||||
|
|
@ -55,7 +55,7 @@ namespace ogfx
|
||||||
|
|
||||||
void VerticalScrollBar::afterDraw(ogfx::BasicRenderer2D& gfx)
|
void VerticalScrollBar::afterDraw(ogfx::BasicRenderer2D& gfx)
|
||||||
{
|
{
|
||||||
if (!getParent()->needsScroll())
|
if (!getParent()->needsVScroll())
|
||||||
return;
|
return;
|
||||||
gfx.fillRoundRect(getGlobalBounds() - m_correctionOffset, m_trackColor, m_trackBorderRadii);
|
gfx.fillRoundRect(getGlobalBounds() - m_correctionOffset, m_trackColor, m_trackBorderRadii);
|
||||||
gfx.outlinedRoundRect(m_thumbGlobalBounds, m_thumbColor, m_thumbBorderColor, m_thumbBorderRadius, 1);
|
gfx.outlinedRoundRect(m_thumbGlobalBounds, m_thumbColor, m_thumbBorderColor, m_thumbBorderRadius, 1);
|
||||||
|
|
@ -143,6 +143,129 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
return m_thumbGlobalBounds.contains(mouse_pos, true);
|
return m_thumbGlobalBounds.contains(mouse_pos, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HorizontalScrollbar& HorizontalScrollbar::create(void)
|
||||||
|
{
|
||||||
|
setPadding({ 0, 0, 0, 0 });
|
||||||
|
setMargin({ 0, 0, 0, 0 });
|
||||||
|
setTypeName("ogfx::gui::widgets::Label");
|
||||||
|
disableDrawBox();
|
||||||
|
disableChildren();
|
||||||
|
enableBackground(true);
|
||||||
|
enableBorder(false);
|
||||||
|
enableTopMost(true);
|
||||||
|
allowIgnoreScroll(true);
|
||||||
|
validate();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HorizontalScrollbar::applyTheme(const ostd::Stylesheet& theme)
|
||||||
|
{
|
||||||
|
h = getThemeValue<f32>(theme, "scrollbar.width", 15);
|
||||||
|
m_thumbColor = getThemeValue<Color>(theme, "scrollbar.thumb.color", { 120, 120, 120 });
|
||||||
|
m_thumbBorderRadius = getThemeValue<f32>(theme, "scrollbar.thumb.borderRadius", 16);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (!getParent()->needsHScroll())
|
||||||
|
return;
|
||||||
|
gfx.fillRoundRect(getGlobalBounds() - m_correctionOffset, m_trackColor, m_trackBorderRadii);
|
||||||
|
gfx.outlinedRoundRect(m_thumbGlobalBounds, m_thumbColor, m_thumbBorderColor, m_thumbBorderRadius, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HorizontalScrollbar::onMouseDragged(const Event& event)
|
||||||
|
{
|
||||||
|
if (!m_mousePressed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (is_mouse_in_thumb({ event.mouse->position_x, event.mouse->position_y }))
|
||||||
|
m_mouseDragged = true;
|
||||||
|
|
||||||
|
if (m_mouseDragged)
|
||||||
|
{
|
||||||
|
auto bounds = getGlobalBounds() - m_correctionOffset;
|
||||||
|
f32 newThumbX = (event.mouse->position_x - bounds.x) - m_dragGrabOffset;
|
||||||
|
set_thumb_x(newThumbX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HorizontalScrollbar::onMousePressed(const Event& event)
|
||||||
|
{
|
||||||
|
m_mousePressed = true;
|
||||||
|
m_dragGrabOffset = event.mouse->position_x - m_thumbGlobalBounds.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HorizontalScrollbar::onMouseReleased(const Event& event)
|
||||||
|
{
|
||||||
|
m_mousePressed = false;
|
||||||
|
if (event.mouse->mousePressedOnWidget != this)
|
||||||
|
return;
|
||||||
|
if (!m_mouseDragged)
|
||||||
|
{
|
||||||
|
auto bounds = getGlobalBounds() - m_correctionOffset;
|
||||||
|
f32 newThumbX = (event.mouse->position_x - bounds.x);
|
||||||
|
newThumbX -= m_thumbWidth / 2.0f;
|
||||||
|
set_thumb_x(newThumbX);
|
||||||
|
}
|
||||||
|
m_mouseDragged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HorizontalScrollbar::onUpdate(void)
|
||||||
|
{
|
||||||
|
update_thumb();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HorizontalScrollbar::update_thumb(void)
|
||||||
|
{
|
||||||
|
x = 0;
|
||||||
|
y = getParent()->getSize().y - h;
|
||||||
|
w = getParent()->getGlobalBounds().w;
|
||||||
|
|
||||||
|
auto ext = getParent()->getContentExtents();
|
||||||
|
auto cont = getParent()->getContentBounds();
|
||||||
|
auto scrollOfset = getParent()->getScrollOffset();
|
||||||
|
|
||||||
|
// Thumb size is proportional to visible/total ratio
|
||||||
|
f32 visibleRatio = cont.w / ext.w; // e.g. 0.4 means 40% visible
|
||||||
|
m_thumbWidth = std::max(20.0f, getw() * visibleRatio); // min 20px for usability
|
||||||
|
|
||||||
|
// Thumb position is proportional to scroll progress
|
||||||
|
f32 scrollProgress = -scrollOfset.x / (ext.w - cont.w); // 0 to 1
|
||||||
|
m_thumbX = getx() + ((getw() - m_thumbWidth) * scrollProgress);
|
||||||
|
|
||||||
|
auto bounds = getGlobalBounds() - m_correctionOffset;
|
||||||
|
m_thumbGlobalBounds = { bounds.x + 2 + m_thumbX, bounds.y + gety(), m_thumbWidth, geth() - 4 };
|
||||||
|
std::cout << Rectangle::toString() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void HorizontalScrollbar::set_thumb_x(f32 thumbx)
|
||||||
|
{
|
||||||
|
auto bounds = getGlobalBounds() - m_correctionOffset;
|
||||||
|
auto ext = getParent()->getContentExtents();
|
||||||
|
auto cont = getParent()->getContentBounds();
|
||||||
|
thumbx = std::clamp(thumbx, 0.0f, w - m_thumbWidth);
|
||||||
|
m_thumbX = thumbx;
|
||||||
|
f32 scrollProgress = (bounds.x + getx() + m_thumbX - bounds.x) / (bounds.w - m_thumbWidth);
|
||||||
|
f32 maxScroll = -(ext.w - cont.w);
|
||||||
|
auto parentScrollOffset = getParent()->getScrollOffset();
|
||||||
|
parentScrollOffset.x = maxScroll * scrollProgress;
|
||||||
|
getParent()->setScrollOffset(parentScrollOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HorizontalScrollbar::is_mouse_in_thumb(const Vec2& mouse_pos)
|
||||||
|
{
|
||||||
|
return m_thumbGlobalBounds.contains(mouse_pos, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,8 @@ namespace ogfx
|
||||||
class VerticalScrollBar : public Widget
|
class VerticalScrollBar : public Widget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline VerticalScrollBar(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); }
|
inline VerticalScrollBar(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); }
|
||||||
inline VerticalScrollBar(WindowCore& window, const String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); }
|
VerticalScrollBar& create(void);
|
||||||
VerticalScrollBar& create(const String& text);
|
|
||||||
void applyTheme(const ostd::Stylesheet& theme) override;
|
void applyTheme(const ostd::Stylesheet& theme) override;
|
||||||
void afterDraw(ogfx::BasicRenderer2D& gfx) override;
|
void afterDraw(ogfx::BasicRenderer2D& gfx) override;
|
||||||
void onMouseDragged(const Event& event) override;
|
void onMouseDragged(const Event& event) override;
|
||||||
|
|
@ -67,6 +66,44 @@ namespace ogfx
|
||||||
Color m_thumbBorderColor { 150, 150, 150 };
|
Color m_thumbBorderColor { 150, 150, 150 };
|
||||||
bool m_thumbShowBorder { true };
|
bool m_thumbShowBorder { true };
|
||||||
};
|
};
|
||||||
|
class HorizontalScrollbar : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline HorizontalScrollbar(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(); }
|
||||||
|
HorizontalScrollbar& create(void);
|
||||||
|
void applyTheme(const ostd::Stylesheet& theme) override;
|
||||||
|
void afterDraw(ogfx::BasicRenderer2D& gfx) override;
|
||||||
|
void onMouseDragged(const Event& event) override;
|
||||||
|
void onMousePressed(const Event& event) override;
|
||||||
|
void onMouseReleased(const Event& event) override;
|
||||||
|
void onUpdate(void) override;
|
||||||
|
|
||||||
|
inline void setx(f32 xx) override { }
|
||||||
|
inline void sety(f32 yy) override { }
|
||||||
|
inline void setw(f32 ww) override { }
|
||||||
|
inline void seth(f32 hh) override { }
|
||||||
|
|
||||||
|
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 };
|
||||||
|
f32 m_thumbX { 0 };
|
||||||
|
Rectangle m_thumbGlobalBounds { 0, 0, 0, 0 };
|
||||||
|
bool m_mousePressed { false };
|
||||||
|
bool m_mouseDragged { false };
|
||||||
|
f32 m_dragGrabOffset { 0 };
|
||||||
|
Rectangle m_correctionOffset { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
Rectangle m_trackBorderRadii { 0, 0, 10, 10 };
|
||||||
|
f32 m_thumbBorderRadius { 16 };
|
||||||
|
Color m_trackColor { 70, 70, 70 };
|
||||||
|
Color m_thumbColor { 120, 120, 120 };
|
||||||
|
Color m_thumbBorderColor { 150, 150, 150 };
|
||||||
|
bool m_thumbShowBorder { true };
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,7 @@ namespace ogfx
|
||||||
gfx.fillRect({ getGlobalPosition(), getSize() }, getDrawBoxColor());
|
gfx.fillRect({ getGlobalPosition(), getSize() }, getDrawBoxColor());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
beforeDraw(gfx);
|
||||||
if (m_showBackground && m_showBorder)
|
if (m_showBackground && m_showBorder)
|
||||||
gfx.outlinedRoundRect({ getGlobalPosition(), getSize() }, m_backgroundColor, m_borderColor, m_borderRadius, m_borderWidth);
|
gfx.outlinedRoundRect({ getGlobalPosition(), getSize() }, m_backgroundColor, m_borderColor, m_borderRadius, m_borderWidth);
|
||||||
else if (m_showBackground)
|
else if (m_showBackground)
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ namespace ogfx
|
||||||
inline virtual Vec2 getScrollOffset(void) const { return { 0, 0 }; }
|
inline virtual Vec2 getScrollOffset(void) const { return { 0, 0 }; }
|
||||||
inline virtual void setScrollOffset(const Vec2& offset) { }
|
inline virtual void setScrollOffset(const Vec2& offset) { }
|
||||||
inline virtual void addScrollOffset(const Vec2& offset) { }
|
inline virtual void addScrollOffset(const Vec2& offset) { }
|
||||||
inline virtual bool needsScroll(void) const { return false; }
|
inline virtual bool needsVScroll(void) const { return false; }
|
||||||
|
inline virtual bool needsHScroll(void) const { return false; }
|
||||||
void addThemeOverride(const String& fullKey, ostd::Stylesheet::TypeVariant value);
|
void addThemeOverride(const String& fullKey, ostd::Stylesheet::TypeVariant value);
|
||||||
void reloadTheme(bool propagate = false);
|
void reloadTheme(bool propagate = false);
|
||||||
void setThemeQualifier(const String& qualifier, bool value = true);
|
void setThemeQualifier(const String& qualifier, bool value = true);
|
||||||
|
|
@ -68,6 +69,7 @@ namespace ogfx
|
||||||
|
|
||||||
inline virtual void onDraw(ogfx::BasicRenderer2D& gfx) { }
|
inline virtual void onDraw(ogfx::BasicRenderer2D& gfx) { }
|
||||||
inline virtual void afterDraw(ogfx::BasicRenderer2D& gfx) { }
|
inline virtual void afterDraw(ogfx::BasicRenderer2D& gfx) { }
|
||||||
|
inline virtual void beforeDraw(ogfx::BasicRenderer2D& gfx) { }
|
||||||
inline virtual void onUpdate(void) { }
|
inline virtual void onUpdate(void) { }
|
||||||
inline virtual void onWidgetAdded(Widget& child) { }
|
inline virtual void onWidgetAdded(Widget& child) { }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ class Window : public ogfx::gui::Window
|
||||||
void onRedraw(ogfx::BasicRenderer2D& gfx) override
|
void onRedraw(ogfx::BasicRenderer2D& gfx) override
|
||||||
{
|
{
|
||||||
gfx.drawAnimation(m_anim, { 200, 200 });
|
gfx.drawAnimation(m_anim, { 200, 200 });
|
||||||
|
// gfx.fillRect(m_panel2.getGlobalContentBounds(), { 0, 255, 0, 120 });
|
||||||
}
|
}
|
||||||
|
|
||||||
void onFixedUpdate(void) override
|
void onFixedUpdate(void) override
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue