Improved scroll area, especially for touch devices
This commit is contained in:
parent
826efee7fb
commit
6a05026f85
12 changed files with 271 additions and 133 deletions
|
|
@ -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] << ", ";
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 = {});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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); });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue