273 lines
8.9 KiB
C++
273 lines
8.9 KiB
C++
/*
|
|
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 "Scrollbar.hpp"
|
|
#include "../../render/BasicRenderer.hpp"
|
|
|
|
namespace ogfx
|
|
{
|
|
namespace gui
|
|
{
|
|
namespace widgets
|
|
{
|
|
VerticalScrollBar& VerticalScrollBar::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 VerticalScrollBar::applyTheme(const ostd::Stylesheet& theme)
|
|
{
|
|
w = 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 });
|
|
}
|
|
|
|
void VerticalScrollBar::afterDraw(ogfx::BasicRenderer2D& gfx)
|
|
{
|
|
if (!getParent()->needsVScroll())
|
|
return;
|
|
gfx.fillRoundRect(getGlobalBounds() - m_correctionOffset, m_trackColor, m_trackBorderRadii);
|
|
gfx.outlinedRoundRect(m_thumbGlobalBounds, m_thumbColor, m_thumbBorderColor, m_thumbBorderRadius, 1);
|
|
}
|
|
|
|
void VerticalScrollBar::onMouseDragged(const Event& event)
|
|
{
|
|
if (!m_mousePressed)
|
|
return;
|
|
|
|
if (isMouseInsideThumb({ event.mouse->position_x, event.mouse->position_y }))
|
|
m_mouseDragged = true;
|
|
|
|
if (m_mouseDragged)
|
|
{
|
|
auto bounds = getGlobalBounds() - m_correctionOffset;
|
|
f32 newThumbY = (event.mouse->position_y - bounds.y) - m_dragGrabOffset;
|
|
set_thumb_y(newThumbY);
|
|
}
|
|
}
|
|
|
|
void VerticalScrollBar::onMousePressed(const Event& event)
|
|
{
|
|
m_mousePressed = true;
|
|
m_dragGrabOffset = event.mouse->position_y - m_thumbGlobalBounds.y;
|
|
}
|
|
|
|
void VerticalScrollBar::onMouseReleased(const Event& event)
|
|
{
|
|
m_mousePressed = false;
|
|
if (event.mouse->mousePressedOnWidget != this)
|
|
return;
|
|
if (!m_mouseDragged)
|
|
{
|
|
auto bounds = getGlobalBounds() - m_correctionOffset;
|
|
f32 newThumbY = (event.mouse->position_y - bounds.y);
|
|
newThumbY -= m_thumbHeight / 2.0f;
|
|
set_thumb_y(newThumbY);
|
|
}
|
|
m_mouseDragged = false;
|
|
}
|
|
|
|
void VerticalScrollBar::onUpdate(void)
|
|
{
|
|
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()->getGlobalPureContentBounds().h;
|
|
|
|
auto ext = getParent()->getContentExtents();
|
|
auto cont = getParent()->getContentBounds();
|
|
auto scrollOfset = getParent()->getScrollOffset();
|
|
|
|
// Thumb size is proportional to visible/total ratio
|
|
f32 visibleRatio = cont.h / ext.h; // e.g. 0.4 means 40% visible
|
|
m_thumbHeight = std::max(20.0f, geth() * visibleRatio); // min 20px for usability
|
|
|
|
// Thumb position is proportional to scroll progress
|
|
f32 scrollProgress = -scrollOfset.y / (ext.h - cont.h); // 0 to 1
|
|
m_thumbY = gety() + ((geth() - m_thumbHeight) * scrollProgress);
|
|
|
|
auto bounds = getGlobalBounds() - m_correctionOffset;
|
|
m_thumbGlobalBounds = { bounds.x + 2, bounds.y + gety() + m_thumbY, getw() - 4, m_thumbHeight };
|
|
}
|
|
|
|
void VerticalScrollBar::set_thumb_y(f32 thumby)
|
|
{
|
|
if (!getParent()->needsVScroll())
|
|
return;
|
|
auto bounds = getGlobalBounds() - m_correctionOffset;
|
|
auto ext = getParent()->getContentExtents();
|
|
auto cont = getParent()->getContentBounds();
|
|
thumby = std::clamp(thumby, 0.0f, h - m_thumbHeight);
|
|
m_thumbY = thumby;
|
|
f32 scrollProgress = (bounds.y + gety() + m_thumbY - bounds.y) / (bounds.h - m_thumbHeight);
|
|
f32 maxScroll = -(ext.h - cont.h);
|
|
auto parentScrollOffset = getParent()->getScrollOffset();
|
|
parentScrollOffset.y = maxScroll * scrollProgress;
|
|
getParent()->setScrollOffset(parentScrollOffset);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 });
|
|
}
|
|
|
|
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 (isMouseInsideThumb({ 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();
|
|
}
|
|
|
|
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 - getParent()->getVScrollbarSize();
|
|
|
|
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 };
|
|
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();
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|