Scrolling layout

This commit is contained in:
IlyaShurupov 2024-10-17 16:46:49 +03:00 committed by Ilya Shurupov
parent c7496c7b42
commit 8c4ba37e59
14 changed files with 288 additions and 35 deletions

View file

@ -11,8 +11,10 @@ void BasicLayout::pickRect(bool vertical) {
auto current = getArea();
auto children = getChildrenEnclosure();
auto parent = getParentEnclosure();
RectF newArea = current;
children.shrinkFromCenter(-mLayoutMargin, true);
RectF newArea;
if (vertical) {
auto rangeY = pickRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true);
newArea = RectF(current.getRangeX(), rangeY);
@ -143,5 +145,5 @@ void BasicLayout::adjustLayout(bool vertical) {
RectF BasicLayout::getAvailableChildArea() const {
auto out = WidgetLayout::getAvailableChildArea();
return out.scaleFromCenter(mLayoutMargin, true);
return out.shrinkFromCenter(mLayoutMargin, true);
}

View file

@ -0,0 +1,53 @@
#include "ScrollableLayout.hpp"
// fixme : this dependency should be removed
#include "ScrollableWidget.hpp"
using namespace tp;
void ScrollableLayout::updateLayout(bool vertical) {
if (vertical) return;
// TODO : make better interface to get scroller and content widget
if (children().size() != 2) return;
auto scroller = dynamic_cast<ScrollableBarWidget*>(children().front());
auto content = children().back();
if (!scroller || !content) return;
updateWidgetRects(getArea(), content, scroller);
}
void ScrollableLayout::updateWidgetRects(const RectF& area, Widget* content, ScrollableBarWidget* scroller) const {
bool dir = scroller->getDirection();
// update content
// auto contentItems = content->getLayout()->getChildrenEnclosure();
// content->getLayout()->setArea(contentItems.shrinkFromCenter(-10, true));
content->getLayout()->pickRect(dir);
auto sizeFactor = (area.size[dir] / content->getLayout()->getArea().size[dir]);
auto splitFactor = 1 - ((sizeFactor > 1.f) ? 0 : mScrollerSize / area.size[!dir]);
auto holderArea = area.splitByFactorHL(splitFactor);
auto contentArea = content->getLayout()->getArea();
auto scrollerArea = area.splitByFactorHR(splitFactor);
scroller->updateSizeFactor(sizeFactor);
contentArea.pos[dir] = -scroller->getPosFactor() * contentArea.size[dir];
contentArea.size[!dir] = holderArea.size[!dir];
contentArea.pos[!dir] = 0;
scroller->getLayout()->setArea(scrollerArea);
content->getLayout()->setArea(contentArea);
}
RectF ScrollableLayout::getAvailableChildArea() const {
auto out = getArea();
// dont constrain on scroll axis
out.pos = -FLT_MAX / 4;
out.size = FLT_MAX / 2;
return out;
}

View file

@ -14,6 +14,9 @@ DebugManager tp::gDebugWidget;
bool DebugManager::update(RootWidget* rootWidget, EventHandler& events) {
mRootWidget = rootWidget;
events.setEnableKeyEvents(true);
if (events.isPressed(InputID::D)) mDebug = !mDebug;
if (mDebug) {
auto area = rootWidget->mRoot.getAreaT();
area.size -= { 400, 0 };
@ -53,12 +56,12 @@ bool DebugManager::update(RootWidget* rootWidget, EventHandler& events) {
void DebugManager::drawDebug(RootWidget* rootWidget, Canvas& canvas) {
mRootWidget = rootWidget;
ImGui::Checkbox("Draw debug", &mDebug);
if (!mDebug) {
return;
}
// ImGui::Checkbox("Draw debug", &mDebug);
ImGui::SameLine(); ImGui::Text("To Toggle processing press k");
auto& upd = rootWidget->mUpdateManager;

View file

@ -2,6 +2,7 @@
#include "DebugManager.hpp"
#include "DockLayout.hpp"
#include "ScrollableLayout.hpp"
#include <algorithm>
@ -91,6 +92,7 @@ int LayoutManager::getLayoutOrder(WidgetLayout* parent, WidgetLayout* child) con
auto policyChild = parent->getSizePolicy()[mVertical];
if (dynamic_cast<DockLayout*>(parent)) return -1;
// if (dynamic_cast<ScrollableLayout*>(parent)) return -1;
return sizePolicyDep[int(policyParent)][int(policyChild)];
}

View file

@ -206,6 +206,6 @@ void UpdateManager::lockFocus(tp::Widget* widget) {
}
void UpdateManager::freeFocus(tp::Widget* widget) {
DEBUG_ASSERT(mFocusLockWidget == widget)
// DEBUG_ASSERT(mFocusLockWidget == widget)
mFocusLockWidget = nullptr;
}

View file

@ -0,0 +1,87 @@
#include "ScrollableWidget.hpp"
using namespace tp;
void ScrollableBarWidget::process(const EventHandler& events) {
// all content is visible no need to process anything
if (mSizeFactor >= 1) {
mScrolling = false;
mPosFactor = mSizeFactor / 2.f;
freeFocus();
return;
}
updateHandleRect();
auto pointer = events.getPointer();
auto size = getArea().size;
mHandleHovered = getHandleRect().isInside(pointer);
if (getRelativeAreaT().isInside(pointer) && events.isPressed(InputID::MOUSE1)) {
if (!mHandleHovered) {
jumpTo((pointer / size)[mVertical]);
mStartPos = { 0, 0 };
} else {
mStartPos = pointer - getHandleRect().center();
}
mScrolling = true;
lockFocus();
}
if (mScrolling && events.isReleased(InputID::MOUSE1)) {
mScrolling = false;
freeFocus();
}
if (mScrolling) {
updateHandleRect();
auto dragDelta = (pointer - getHandleRect().center()) - mStartPos;
moveBy((dragDelta / size)[mVertical]);
}
}
void ScrollableBarWidget::jumpTo(halnf pos) {
mPosFactor = pos;
clamp();
}
void ScrollableBarWidget::moveBy(halnf delta) {
mPosFactor += delta;
clamp();
}
void ScrollableBarWidget::updateSizeFactor(halnf factor) {
mSizeFactor = factor;
clamp();
}
void ScrollableBarWidget::clamp() {
mSizeFactor = ::clamp(mSizeFactor, 0.f, 1.f);
mPosFactor = ::clamp(mPosFactor, mSizeFactor / 2, 1.f - mSizeFactor / 2);
}
void ScrollableBarWidget::draw(Canvas& canvas) {
updateHandleRect();
canvas.rect(getArea().relative(), mBGColor, mRounding);
auto& handleColor = mScrolling ? mHandleSlideColor : (mHandleHovered ? mHandleHoverColor : mHandleColor);
canvas.rect(getHandleRect(), handleColor, mRounding);
}
void ScrollableBarWidget::updateHandleRect() {
auto area = getArea().relative();
mHandleRect = area;
mHandleRect.size[mVertical] = area.size[mVertical] * mSizeFactor;
mHandleRect.pos[mVertical] = area.size[mVertical] * mPosFactor;
mHandleRect.pos[mVertical] -= mHandleRect.size[mVertical] / 2;
mHandleRect.shrinkFromCenter(mHandlePadding, true);
}
const RectF& ScrollableBarWidget::getHandleRect() const {
return mHandleRect;
}