Improved scroll area, especially for touch devices

This commit is contained in:
Yousef Khadadeh 2026-04-29 12:38:40 +03:00
parent 826efee7fb
commit 6a05026f85
12 changed files with 271 additions and 133 deletions

View file

@ -305,7 +305,7 @@ template <typename T> std::string vec_pretty(const T& a) {
std::stringstream ss;
ss << "[";
for(size_t i = 0; i < static_cast<size_t>(a.size()); i++) {
if(i == a.size() - 1)
if(i == static_cast<size_t>(a.size() - 1))
ss << a[i] << "]";
else
ss << a[i] << ", ";

View file

@ -30,7 +30,7 @@ namespace GUIStuff {
std::shared_ptr<Theme> theme(std::make_shared<Theme>());
theme->fillColor1 = {0.65f, 0.64f, 1.0f, 1.0f};
theme->fillColor2 = {0.6f, 0.6f, 0.785f, 1.0f};
theme->backColor0 = {0.07f, 0.07f, 0.07f, 1.0f};
theme->backColor0 = {0.00f, 0.00f, 0.00f, 1.0f};
theme->backColor1 = {0.156f, 0.156f, 0.18f, 1.0f};
theme->backColor2 = {0.24f, 0.24f, 0.29f, 1.0f};
theme->frontColor1 = {0.87f, 0.87f, 0.87f, 1.0f};

View file

@ -99,6 +99,12 @@ struct UpdateInputData {
InputManager* input = nullptr;
std::function<void()> layoutRun;
#ifdef __ANDROID__
bool isTouchDevice = true;
#else
bool isTouchDevice = false;
#endif
SkFont get_font(float fSize) const;
uint16_t fontSize = 16;

View file

@ -71,7 +71,7 @@ void GridScrollArea::layout(const Clay_ElementId& id, const Options& o) {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}
}
}) {
scroll_area_many_entries(gui, "scroll many area", opts);
scrollArea = scroll_area_many_entries(gui, "scroll many area", opts);
}
}

View file

@ -20,6 +20,8 @@ class GridScrollArea : public Element {
};
void layout(const Clay_ElementId& id, const Options& options);
ScrollArea* scrollArea = nullptr;
private:
Options options;
float rowWidth = 0.0f;

View file

@ -18,6 +18,7 @@ void LayoutElement::input_mouse_button_callback(const InputManager::MouseButtonC
void LayoutElement::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) {
if(c.mouseMotion) c.mouseMotion(this, motion);
if(c.onMotion) c.onMotion(this, motion);
}
void LayoutElement::input_mouse_wheel_callback(const InputManager::MouseWheelCallbackArgs& wheel) {
@ -39,6 +40,13 @@ void LayoutElement::input_finger_touch_callback(const InputManager::FingerTouchC
void LayoutElement::input_finger_motion_callback(const InputManager::FingerMotionCallbackArgs& motion) {
if(c.fingerMotion) c.fingerMotion(this, motion);
if(c.onMotion) {
c.onMotion(this, {
.deviceType = InputManager::MouseDeviceType::TOUCH,
.pos = motion.pos,
.move = motion.move
});
}
}
void LayoutElement::input_key_callback(const InputManager::KeyCallbackArgs& key) {

View file

@ -13,6 +13,7 @@ class LayoutElement : public Element {
std::function<void(LayoutElement* l, const InputManager::FingerTouchCallbackArgs& touch)> fingerTouch;
std::function<void(LayoutElement* l, const InputManager::FingerMotionCallbackArgs& motion)> fingerMotion;
std::function<void(LayoutElement* l, const InputManager::MouseButtonCallbackArgs& button)> onClick;
std::function<void(LayoutElement* l, const InputManager::MouseMotionCallbackArgs& motion)> onMotion;
std::function<void(LayoutElement* l, const InputManager::KeyCallbackArgs& key)> key;
};
void layout(const Clay_ElementId& id, const std::function<void(LayoutElement*, const Clay_ElementId&)>& layout, const Callbacks& c = {});

View file

@ -1,156 +1,212 @@
#include "ScrollArea.hpp"
#include "../GUIManager.hpp"
#include <Helpers/ConvertVec.hpp>
#include <Helpers/Logger.hpp>
#include "Helpers/MathExtras.hpp"
#include "LayoutElement.hpp"
namespace GUIStuff {
constexpr float TOUCH_SCROLLER_WIDTH = 24.0f;
constexpr float DESKTOP_SCROLLER_WIDTH = 12.0f;
constexpr float SCROLL_DAMPING = 0.15f; // Damping on the scroll area velocity when its scrolling on its own
constexpr float SCROLL_MINIMUM_MOTION = 0.15f; // When the scroll area is scrolling on its own, this is the minimum velocity required for it to continue
constexpr float SCROLL_MAX_DAMPING = 0.0001f; // Damping on the scrollAreaMotionMax variable
constexpr float SCROLL_MINIMUM_MOTION_TO_START_MOVE = 1.0f; // Right after releasing touch, this is the minimum velocity for the area to start scrolling on its own
ScrollArea::ScrollArea(GUIManager& gui): Element(gui) {}
void ScrollArea::layout(const Clay_ElementId& id, const Options& options) {
opts = options;
Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(id);
CLAY(id, {
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
.childAlignment = {.x = opts.xAlign, .y = opts.yAlign},
.layoutDirection = opts.layoutDirection
},
.clip = {.horizontal = opts.clipHorizontal, .vertical = opts.clipVertical, .childOffset = {.x = scrollOffset.x(), .y = scrollOffset.y()}}
}) {
Clay_ElementId localID = CLAY_ID_LOCAL("SCROLL_AREA");
Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(localID);
Clay_ElementData elemData = Clay_GetElementData(localID);
Vector2f bbPos = {0.0f, 0.0f};
if(scrollData.found) {
if(scrollData.found)
contentDimensions = {scrollData.contentDimensions.width, scrollData.contentDimensions.height};
containerDimensions = {scrollData.scrollContainerDimensions.width, scrollData.scrollContainerDimensions.height};
}
if(boundingBox.has_value()) {
auto& bb = boundingBox.value();
clamp_scroll();
if(elemData.found)
bbPos = {elemData.boundingBox.x, elemData.boundingBox.y};
clamp_scroll();
CLAY(localID, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
.childAlignment = {.x = opts.xAlign, .y = opts.yAlign},
.layoutDirection = opts.layoutDirection
},
.clip = {.horizontal = opts.clipHorizontal, .vertical = opts.clipVertical, .childOffset = {.x = scrollOffset.x(), .y = scrollOffset.y()}}
}) {
if(scrollData.contentDimensions.height > scrollData.scrollContainerDimensions.height) {
if(contentDimensions.y() > bb.height()) {
gui.in_dynamic_area([&] {
opts.innerContent({.contentDimensions = contentDimensions, .containerDimensions = containerDimensions, .scrollOffset = &scrollOffset});
opts.innerContent({.contentDimensions = contentDimensions, .containerDimensions = bb.dim(), .scrollOffset = &scrollOffset});
});
}
else
opts.innerContent({.contentDimensions = contentDimensions, .containerDimensions = containerDimensions, .scrollOffset = &scrollOffset});
opts.innerContent({.contentDimensions = contentDimensions, .containerDimensions = bb.dim(), .scrollOffset = &scrollOffset});
clamp_scroll();
}
if(scrollData.contentDimensions.height > scrollData.scrollContainerDimensions.height) {
float sAreaDim = containerDimensions.y();
float sAreaStart = bbPos.y();
float contDim = contentDimensions.y();
float scrollerSize = (sAreaDim / contDim) * sAreaDim;
float scrollPosMax = contDim - sAreaDim;
float scrollerPos = std::fabs(scrollOffset.y() / scrollPosMax);
float areaAboveScrollerSize = scrollerPos * (sAreaDim - scrollerSize);
gui.set_z_index(gui.get_z_index() + 1, [&] {
gui.element<LayoutElement>("scroll bar", [&] (LayoutElement*, const Clay_ElementId& lId) {
CLAY(lId, {
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(12), .height = CLAY_SIZING_GROW(0)},
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.backgroundColor = convert_vec4<Clay_Color>(gui.io.theme->backColor2)
}) {
SkColor4f scrollerColor;
if(isScrollbarHeldY)
scrollerColor = gui.io.theme->fillColor1;
else if(isScrollbarHoveredY)
scrollerColor = gui.io.theme->fillColor1;
else
scrollerColor = gui.io.theme->fillColor2;
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(areaAboveScrollerSize)}
}
}) {}
gui.element<LayoutElement>("scroller", [&] (LayoutElement*, const Clay_ElementId& lId2) {
CLAY(lId2, {
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(scrollerSize)}},
.backgroundColor = convert_vec4<Clay_Color>(scrollerColor),
.cornerRadius = CLAY_CORNER_RADIUS(3),
}) {}
}, LayoutElement::Callbacks{
.mouseMotion = [&] (LayoutElement* t, const InputManager::MouseMotionCallbackArgs& motion) {
if(isHoveringOverScrollerY != t->mouseHovering) {
isHoveringOverScrollerY = t->mouseHovering;
gui.set_to_layout();
}
}
});
CLAY_AUTO_ID({ .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}}) {}
}
}, LayoutElement::Callbacks{
.mouseButton = [&, scrollPosMax, sAreaStart, sAreaDim, scrollerSize, areaAboveScrollerSize](LayoutElement* t, const InputManager::MouseButtonCallbackArgs& button) {
if(button.button == InputManager::MouseButton::LEFT) {
if(button.down && t->mouseHovering) {
isScrollbarHeldY = true;
if(isHoveringOverScrollerY)
scrollerStartPosY = sAreaStart + areaAboveScrollerSize + scrollerSize * 0.5f;
else
scrollerStartPosY = std::clamp(button.pos.y(), sAreaStart + scrollerSize * 0.5f, sAreaStart + sAreaDim - scrollerSize * 0.5f);
mouseStartPosY = button.pos.y();
float newScrollPosFrac;
newScrollPosFrac = std::clamp((scrollerStartPosY - (mouseStartPosY - button.pos.y()) - sAreaStart - scrollerSize * 0.5f) / (sAreaDim - scrollerSize), 0.0f, 1.0f);
scrollOffset.y() = newScrollPosFrac * (-scrollPosMax);
clamp_scroll();
gui.set_to_layout();
}
else if(isScrollbarHeldY) {
isScrollbarHeldY = false;
gui.set_to_layout();
}
}
},
.mouseMotion = [&, scrollPosMax, sAreaStart, sAreaDim, scrollerSize](LayoutElement* t, const InputManager::MouseMotionCallbackArgs& motion) {
if(isScrollbarHoveredY != t->mouseHovering) {
isScrollbarHoveredY = t->mouseHovering;
gui.set_to_layout();
}
if(isScrollbarHeldY) {
float oldScrollOffset = scrollOffset.y();
float newScrollPosFrac;
newScrollPosFrac = std::clamp((scrollerStartPosY - (mouseStartPosY - motion.pos.y()) - sAreaStart - scrollerSize * 0.5f) / (sAreaDim - scrollerSize), 0.0f, 1.0f);
scrollOffset.y() = newScrollPosFrac * (-scrollPosMax);
clamp_scroll();
if(oldScrollOffset != scrollOffset.y())
gui.set_to_layout();
}
}
});
});
if(contentDimensions.y() > bb.height() && opts.showScrollbarY)
y_scroll_bar();
}
}
}
void ScrollArea::y_scroll_bar() {
float sAreaDim = boundingBox.value().height();
float sAreaStart = boundingBox.value().min.y();
float contDim = contentDimensions.y();
float scrollerSize = (sAreaDim / contDim) * sAreaDim;
float scrollPosMax = contDim - sAreaDim;
float scrollerPos = std::fabs(scrollOffset.y() / scrollPosMax);
float areaAboveScrollerSize = scrollerPos * (sAreaDim - scrollerSize);
gui.set_z_index(gui.get_z_index() + 1, [&] {
gui.element<LayoutElement>("y scroll bar", [&] (LayoutElement*, const Clay_ElementId& lId) {
CLAY(lId, {
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(gui.io.isTouchDevice ? TOUCH_SCROLLER_WIDTH : DESKTOP_SCROLLER_WIDTH), .height = CLAY_SIZING_GROW(0)},
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.floating = {
.zIndex = gui.get_z_index(),
.attachPoints = {
.element = CLAY_ATTACH_POINT_RIGHT_TOP,
.parent = CLAY_ATTACH_POINT_RIGHT_TOP
},
.attachTo = CLAY_ATTACH_TO_PARENT,
}
}) {
SkColor4f scrollerColor;
if(isScrollbarHeldY)
scrollerColor = gui.io.theme->fillColor1;
else if(isScrollbarHoveredY)
scrollerColor = gui.io.theme->fillColor1;
else
scrollerColor = gui.io.theme->fillColor2;
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(areaAboveScrollerSize)}
}
}) {}
scrollerY = gui.element<LayoutElement>("scroller", [&] (LayoutElement*, const Clay_ElementId& lId2) {
CLAY(lId2, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(scrollerSize)},
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP},
.layoutDirection = CLAY_LEFT_TO_RIGHT,
}
}) {
CLAY_AUTO_ID({
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}
}) {}
float scrollerWidthPercent = 0.0f;
bool highlighted = isHoveringOverScrollerY || isScrollbarHeldY;
if(gui.io.isTouchDevice)
scrollerWidthPercent = highlighted ? 0.6f : 0.2f;
else
scrollerWidthPercent = highlighted ? 1.0f : 0.5f;
CLAY_AUTO_ID({
.layout = {.sizing = {.width = CLAY_SIZING_PERCENT(scrollerWidthPercent), .height = CLAY_SIZING_GROW(0)}},
.backgroundColor = convert_vec4<Clay_Color>(highlighted ? gui.io.theme->fillColor1 : gui.io.theme->fillColor2),
.cornerRadius = CLAY_CORNER_RADIUS(3),
}) {}
}
});
CLAY_AUTO_ID({ .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}}) {}
}
}, LayoutElement::Callbacks{
.onClick = [&, scrollPosMax, sAreaStart, sAreaDim, scrollerSize, areaAboveScrollerSize](LayoutElement* t, const InputManager::MouseButtonCallbackArgs& button) {
bool newIsHoveringOverScrollerY = scrollerY->mouseHovering && (button.deviceType != InputManager::MouseDeviceType::TOUCH || button.down);
bool newIsScrollbarHoveredY = t->mouseHovering && (button.deviceType != InputManager::MouseDeviceType::TOUCH || button.down);
if(isHoveringOverScrollerY != newIsHoveringOverScrollerY) {
isHoveringOverScrollerY = newIsHoveringOverScrollerY;
gui.set_to_layout();
}
if(isScrollbarHoveredY != newIsScrollbarHoveredY) {
isScrollbarHoveredY = newIsScrollbarHoveredY;
gui.set_to_layout();
}
if(button.button == InputManager::MouseButton::LEFT) {
if(button.down && isScrollbarHoveredY) {
isScrollbarHeldY = true;
if(isHoveringOverScrollerY)
scrollerStartPosY = sAreaStart + areaAboveScrollerSize + scrollerSize * 0.5f;
else
scrollerStartPosY = std::clamp(button.pos.y(), sAreaStart + scrollerSize * 0.5f, sAreaStart + sAreaDim - scrollerSize * 0.5f);
mouseStartPosY = button.pos.y();
float newScrollPosFrac;
newScrollPosFrac = std::clamp((scrollerStartPosY - (mouseStartPosY - button.pos.y()) - sAreaStart - scrollerSize * 0.5f) / (sAreaDim - scrollerSize), 0.0f, 1.0f);
scrollOffset.y() = newScrollPosFrac * (-scrollPosMax);
clamp_scroll();
scrollAreaMotionMax = scrollAreaMotion = {0.0f, 0.0f};
gui.set_to_layout();
}
else if(isScrollbarHeldY) {
isScrollbarHeldY = false;
scrollAreaMotionMax = scrollAreaMotion = {0.0f, 0.0f};
gui.set_to_layout();
}
}
},
.onMotion = [&, scrollPosMax, sAreaStart, sAreaDim, scrollerSize](LayoutElement* t, const InputManager::MouseMotionCallbackArgs& motion) {
bool newIsHoveringOverScrollerY = scrollerY->mouseHovering && motion.deviceType != InputManager::MouseDeviceType::TOUCH;
bool newIsScrollbarHoveredY = t->mouseHovering && motion.deviceType != InputManager::MouseDeviceType::TOUCH;
if(isHoveringOverScrollerY != newIsHoveringOverScrollerY) {
isHoveringOverScrollerY = newIsHoveringOverScrollerY;
gui.set_to_layout();
}
if(isScrollbarHoveredY != newIsScrollbarHoveredY) {
isScrollbarHoveredY = newIsScrollbarHoveredY;
gui.set_to_layout();
}
if(isScrollbarHeldY) {
float oldScrollOffset = scrollOffset.y();
float newScrollPosFrac;
newScrollPosFrac = std::clamp((scrollerStartPosY - (mouseStartPosY - motion.pos.y()) - sAreaStart - scrollerSize * 0.5f) / (sAreaDim - scrollerSize), 0.0f, 1.0f);
scrollOffset.y() = newScrollPosFrac * (-scrollPosMax);
clamp_scroll();
scrollAreaMotionMax = scrollAreaMotion = {0.0f, 0.0f};
if(oldScrollOffset != scrollOffset.y())
gui.set_to_layout();
}
}
});
});
}
void ScrollArea::reset_scroll() {
scrollOffset = {0.0f, 0.0f};
scrollAreaMotion = scrollAreaMotionMax = scrollOffset = {0.0f, 0.0f};
}
void ScrollArea::clamp_scroll() {
scrollOffset.x() = std::clamp(scrollOffset.x(), -std::max(0.0f, contentDimensions.x() - containerDimensions.x()), 0.0f);
scrollOffset.y() = std::clamp(scrollOffset.y(), -std::max(0.0f, contentDimensions.y() - containerDimensions.y()), 0.0f);
if(boundingBox.has_value()) {
scrollOffset.x() = std::clamp(scrollOffset.x(), -std::max(0.0f, contentDimensions.x() - boundingBox.value().width()), 0.0f);
scrollOffset.y() = std::clamp(scrollOffset.y(), -std::max(0.0f, contentDimensions.y() - boundingBox.value().height()), 0.0f);
}
}
void ScrollArea::update() {
if((scrollAreaMotion.x() != 0.0f || scrollAreaMotion.y() != 0.0f) && !scrollAreaHeld) {
Vector2f oldScrollOffset = scrollOffset;
scrollOffset.x() += scrollAreaMotion.x();
scrollOffset.y() += scrollAreaMotion.y();
clamp_scroll();
scrollAreaMotion *= std::pow(SCROLL_DAMPING, gui.io.deltaTime);
if(std::fabs(scrollAreaMotion.y()) <= SCROLL_MINIMUM_MOTION)
scrollAreaMotion.y() = 0.0f;
if(std::fabs(scrollAreaMotion.x()) <= SCROLL_MINIMUM_MOTION)
scrollAreaMotion.x() = 0.0f;
if(oldScrollOffset != scrollOffset)
gui.set_to_layout();
else
scrollAreaMotion = {0.0f, 0.0f};
}
else if(scrollAreaHeld)
scrollAreaMotionMax *= std::pow(SCROLL_MAX_DAMPING, gui.io.deltaTime);
}
void ScrollArea::input_mouse_wheel_callback(const InputManager::MouseWheelCallbackArgs& wheel) {
@ -168,4 +224,45 @@ void ScrollArea::input_mouse_wheel_callback(const InputManager::MouseWheelCallba
}
}
void ScrollArea::input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) {
if(button.deviceType != InputManager::MouseDeviceType::TOUCH)
scrollAreaMotion = scrollAreaMotionMax = {0.0f, 0.0f};
}
void ScrollArea::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) {
if(motion.deviceType != InputManager::MouseDeviceType::TOUCH)
scrollAreaMotion = scrollAreaMotionMax = {0.0f, 0.0f};
}
void ScrollArea::input_finger_touch_callback(const InputManager::FingerTouchCallbackArgs& touch) {
bool oldScrollAreaHeld = scrollAreaHeld;
scrollAreaHeld = mouseHovering && touch.down;
if(scrollAreaHeld)
scrollAreaMotionMax = scrollAreaMotion = {0.0f, 0.0f};
else if(oldScrollAreaHeld) {
scrollAreaMotion = scrollAreaMotionMax;
if(std::fabs(scrollAreaMotion.x()) < SCROLL_MINIMUM_MOTION_TO_START_MOVE)
scrollAreaMotion.x() = 0.0f;
if(std::fabs(scrollAreaMotion.y()) < SCROLL_MINIMUM_MOTION_TO_START_MOVE)
scrollAreaMotion.y() = 0.0f;
}
}
void ScrollArea::input_finger_motion_callback(const InputManager::FingerMotionCallbackArgs& motion) {
if(scrollAreaHeld) {
scrollAreaMotion = {opts.scrollHorizontal ? motion.move.x() : 0.0f, opts.scrollVertical ? motion.move.y() : 0.0f};
if(std::fabs(scrollAreaMotion.x()) > std::fabs(scrollAreaMotionMax.x()))
scrollAreaMotionMax.x() = scrollAreaMotion.x();
if(std::fabs(scrollAreaMotion.y()) > std::fabs(scrollAreaMotionMax.y()))
scrollAreaMotionMax.y() = scrollAreaMotion.y();
Vector2f oldScrollOffset = scrollOffset;
scrollOffset.x() += scrollAreaMotion.x();
scrollOffset.y() += scrollAreaMotion.y();
clamp_scroll();
if(oldScrollOffset != scrollOffset)
gui.set_to_layout();
}
}
}

View file

@ -30,17 +30,28 @@ class ScrollArea : public Element {
void layout(const Clay_ElementId& id, const Options& options);
void reset_scroll();
virtual void update() override;
virtual void input_mouse_wheel_callback(const InputManager::MouseWheelCallbackArgs& wheel) override;
virtual void input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) override;
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
virtual void input_finger_touch_callback(const InputManager::FingerTouchCallbackArgs& touch) override;
virtual void input_finger_motion_callback(const InputManager::FingerMotionCallbackArgs& motion) override;
private:
Options opts;
bool scrollAreaHeld = false;
Vector2f scrollAreaMotion = {0.0f, 0.0f};
Vector2f scrollAreaMotionMax = {0.0f, 0.0f};
bool isScrollbarHeldY = false;
bool isScrollbarHoveredY = false;
bool isHoveringOverScrollerY = false;
Element* scrollerY = nullptr;
float scrollerStartPosY;
float mouseStartPosY;
void y_scroll_bar();
Vector2f contentDimensions = {0.0f, 0.0f};
Vector2f containerDimensions = {0.0f, 0.0f};
Vector2f scrollOffset = {0.0f, 0.0f};
void clamp_scroll();
};

View file

@ -710,6 +710,7 @@ void GUIManager::input_mouse_button_callback(InputManager::MouseButtonCallbackAr
void GUIManager::input_mouse_motion_callback(InputManager::MouseMotionCallbackArgs motion) {
if(motion.deviceType != InputManager::MouseDeviceType::TOUCH) {
motion.pos /= io.guiScaleMultiplier;
motion.move /= io.guiScaleMultiplier;
mouse_callback(motion.pos, [&motion] (ElementContainer* e) { e->elem->input_mouse_motion_callback(motion); });
}
}
@ -726,6 +727,7 @@ void GUIManager::input_finger_touch_callback(InputManager::FingerTouchCallbackAr
void GUIManager::input_finger_motion_callback(InputManager::FingerMotionCallbackArgs motion) {
motion.pos /= io.guiScaleMultiplier;
motion.move /= io.guiScaleMultiplier;
mouse_callback(motion.pos, [&motion] (ElementContainer* e) { e->elem->input_finger_motion_callback(motion); });
}

View file

@ -163,14 +163,13 @@ void FileSelectScreen::main_display() {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
},
}) {
file_view();
switch(selectedMenu) {
case SelectedMenu::FILES:
file_view();
if(!editMode)
create_file_button();
break;
case SelectedMenu::TRASH:
file_view();
break;
case SelectedMenu::SETTINGS:
break;
@ -548,15 +547,18 @@ void FileSelectScreen::main_menu() {
selectedMenu = SelectedMenu::FILES;
update_file_list(fileList, savePath, false);
mainMenuOpenAnim->animation_trigger_reverse();
if(fileViewScrollArea) fileViewScrollArea->reset_scroll();
});
icon_text_transparent_option_selected_button("Trash", "data/icons/trash.svg", "Trash", selectedMenu == SelectedMenu::TRASH, [&] {
selectedMenu = SelectedMenu::TRASH;
update_file_list(fileList, trashPath, true);
mainMenuOpenAnim->animation_trigger_reverse();
if(fileViewScrollArea) fileViewScrollArea->reset_scroll();
});
icon_text_transparent_option_selected_button("Settings", "data/icons/RemixIcon/settings-3-line.svg", "Settings", selectedMenu == SelectedMenu::SETTINGS, [&] {
selectedMenu = SelectedMenu::SETTINGS;
mainMenuOpenAnim->animation_trigger_reverse();
if(fileViewScrollArea) fileViewScrollArea->reset_scroll();
});
}
}
@ -566,6 +568,11 @@ void FileSelectScreen::main_menu() {
}
void FileSelectScreen::file_view() {
if(selectedMenu == SelectedMenu::SETTINGS) {
fileViewScrollArea = nullptr;
return;
}
auto& gui = main.g.gui;
std::filesystem::path folderPath = (selectedMenu == SelectedMenu::TRASH) ? trashPath : savePath;
@ -660,7 +667,7 @@ void FileSelectScreen::file_view() {
};
if(fileViewType == FileViewType::LIST) {
scroll_area_many_entries(gui, "File selector list", ScrollBarManyEntriesOptions{
fileViewScrollArea = scroll_area_many_entries(gui, "File selector list", ScrollBarManyEntriesOptions{
.entryHeight = 150.0f,
.entryCount = fileList.size(),
.clipHorizontal = true,
@ -689,7 +696,7 @@ void FileSelectScreen::file_view() {
default:
break;
}
gui.element<GridScrollArea>("File selector grid", GridScrollArea::Options{
GridScrollArea* gridArea = gui.element<GridScrollArea>("File selector grid", GridScrollArea::Options{
.entryWidth = entrySize.x(),
.childAlignmentX = CLAY_ALIGN_X_LEFT,
.entryHeight = entrySize.y(),
@ -699,6 +706,7 @@ void FileSelectScreen::file_view() {
fileButton(i, false, iconSize);
}
});
fileViewScrollArea = gridArea->scrollArea;
}
}

View file

@ -2,6 +2,7 @@
#include "Screen.hpp"
#include "nlohmann/json.hpp"
#include "../GUIStuff/GUIFloatAnimation.hpp"
#include "../GUIStuff/Elements/ScrollArea.hpp"
class FileSelectScreen : public Screen {
public:
@ -44,6 +45,8 @@ class FileSelectScreen : public Screen {
GUIStuff::GUIFloatAnimation* mainMenuOpenAnim = nullptr;
GUIStuff::GUIFloatAnimation* actionBarOpenAnim = nullptr;
GUIStuff::ScrollArea* fileViewScrollArea = nullptr;
void main_display();
void main_menu();
void file_view();