diff --git a/include/Helpers/MathExtras.hpp b/include/Helpers/MathExtras.hpp index 0767448..506451c 100644 --- a/include/Helpers/MathExtras.hpp +++ b/include/Helpers/MathExtras.hpp @@ -305,7 +305,7 @@ template std::string vec_pretty(const T& a) { std::stringstream ss; ss << "["; for(size_t i = 0; i < static_cast(a.size()); i++) { - if(i == a.size() - 1) + if(i == static_cast(a.size() - 1)) ss << a[i] << "]"; else ss << a[i] << ", "; diff --git a/src/GUIStuff/Elements/GUIStuffHelpers.cpp b/src/GUIStuff/Elements/GUIStuffHelpers.cpp index e1c85a0..202fe01 100644 --- a/src/GUIStuff/Elements/GUIStuffHelpers.cpp +++ b/src/GUIStuff/Elements/GUIStuffHelpers.cpp @@ -30,7 +30,7 @@ namespace GUIStuff { std::shared_ptr theme(std::make_shared()); 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}; diff --git a/src/GUIStuff/Elements/GUIStuffHelpers.hpp b/src/GUIStuff/Elements/GUIStuffHelpers.hpp index 57ae07b..f5b7e3e 100644 --- a/src/GUIStuff/Elements/GUIStuffHelpers.hpp +++ b/src/GUIStuff/Elements/GUIStuffHelpers.hpp @@ -99,6 +99,12 @@ struct UpdateInputData { InputManager* input = nullptr; std::function layoutRun; +#ifdef __ANDROID__ + bool isTouchDevice = true; +#else + bool isTouchDevice = false; +#endif + SkFont get_font(float fSize) const; uint16_t fontSize = 16; diff --git a/src/GUIStuff/Elements/GridScrollArea.cpp b/src/GUIStuff/Elements/GridScrollArea.cpp index 3890ed9..a0e04b8 100644 --- a/src/GUIStuff/Elements/GridScrollArea.cpp +++ b/src/GUIStuff/Elements/GridScrollArea.cpp @@ -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); } } diff --git a/src/GUIStuff/Elements/GridScrollArea.hpp b/src/GUIStuff/Elements/GridScrollArea.hpp index 2295901..1a45712 100644 --- a/src/GUIStuff/Elements/GridScrollArea.hpp +++ b/src/GUIStuff/Elements/GridScrollArea.hpp @@ -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; diff --git a/src/GUIStuff/Elements/LayoutElement.cpp b/src/GUIStuff/Elements/LayoutElement.cpp index 54adbe1..a4852d4 100644 --- a/src/GUIStuff/Elements/LayoutElement.cpp +++ b/src/GUIStuff/Elements/LayoutElement.cpp @@ -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) { diff --git a/src/GUIStuff/Elements/LayoutElement.hpp b/src/GUIStuff/Elements/LayoutElement.hpp index 9f9e46c..a9b8798 100644 --- a/src/GUIStuff/Elements/LayoutElement.hpp +++ b/src/GUIStuff/Elements/LayoutElement.hpp @@ -13,6 +13,7 @@ class LayoutElement : public Element { std::function fingerTouch; std::function fingerMotion; std::function onClick; + std::function onMotion; std::function key; }; void layout(const Clay_ElementId& id, const std::function& layout, const Callbacks& c = {}); diff --git a/src/GUIStuff/Elements/ScrollArea.cpp b/src/GUIStuff/Elements/ScrollArea.cpp index 9a4308f..d541f27 100644 --- a/src/GUIStuff/Elements/ScrollArea.cpp +++ b/src/GUIStuff/Elements/ScrollArea.cpp @@ -1,156 +1,212 @@ #include "ScrollArea.hpp" #include "../GUIManager.hpp" +#include +#include +#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("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(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("scroller", [&] (LayoutElement*, const Clay_ElementId& lId2) { - CLAY(lId2, { - .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(scrollerSize)}}, - .backgroundColor = convert_vec4(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("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("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(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(); + } +} + } diff --git a/src/GUIStuff/Elements/ScrollArea.hpp b/src/GUIStuff/Elements/ScrollArea.hpp index 7747152..d760065 100644 --- a/src/GUIStuff/Elements/ScrollArea.hpp +++ b/src/GUIStuff/Elements/ScrollArea.hpp @@ -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(); }; diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index 845da4b..c7965b3 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -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); }); } diff --git a/src/Screens/FileSelectScreen.cpp b/src/Screens/FileSelectScreen.cpp index 85cdc86..9a86274 100644 --- a/src/Screens/FileSelectScreen.cpp +++ b/src/Screens/FileSelectScreen.cpp @@ -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("File selector grid", GridScrollArea::Options{ + GridScrollArea* gridArea = gui.element("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; } } diff --git a/src/Screens/FileSelectScreen.hpp b/src/Screens/FileSelectScreen.hpp index 397ac5a..66deb18 100644 --- a/src/Screens/FileSelectScreen.hpp +++ b/src/Screens/FileSelectScreen.hpp @@ -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();