From f1111d06c2f62bcddb504623e06baddb46d0330a Mon Sep 17 00:00:00 2001 From: Dram27 Date: Tue, 12 May 2026 14:32:05 +0200 Subject: [PATCH] Scroll on settings --- .../gui/components/GuiElementContainer.cpp | 11 ++ .../gui/components/GuiElementContainer.h | 1 + src/client/gui/components/OptionsGroup.cpp | 121 +++++++++++++++++- src/client/gui/components/OptionsGroup.h | 17 +++ src/client/gui/screens/OptionsScreen.cpp | 7 + src/client/gui/screens/OptionsScreen.h | 1 + 6 files changed, 155 insertions(+), 3 deletions(-) diff --git a/src/client/gui/components/GuiElementContainer.cpp b/src/client/gui/components/GuiElementContainer.cpp index 9cfbb09..4fdaf22 100755 --- a/src/client/gui/components/GuiElementContainer.cpp +++ b/src/client/gui/components/GuiElementContainer.cpp @@ -35,6 +35,17 @@ void GuiElementContainer::removeChild( GuiElement* element ) { children.erase(it); } +bool GuiElementContainer::containsPointInChildren(int x, int y) const { + for (std::vector::const_iterator it = children.begin(); it != children.end(); ++it) { + GuiElement* child = *it; + if (child == NULL || !child->visible) continue; + if (child->pointInside(x, y)) return true; + const GuiElementContainer* container = dynamic_cast(child); + if (container != NULL && container->containsPointInChildren(x, y)) return true; + } + return false; +} + void GuiElementContainer::tick( Minecraft* minecraft ) { for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { (*it)->tick(minecraft); diff --git a/src/client/gui/components/GuiElementContainer.h b/src/client/gui/components/GuiElementContainer.h index d20f9d3..5e06110 100755 --- a/src/client/gui/components/GuiElementContainer.h +++ b/src/client/gui/components/GuiElementContainer.h @@ -13,6 +13,7 @@ public: virtual void setupPositions(); virtual void addChild(GuiElement* element); virtual void removeChild(GuiElement* element); + bool containsPointInChildren(int x, int y) const; virtual void tick( Minecraft* minecraft ); diff --git a/src/client/gui/components/OptionsGroup.cpp b/src/client/gui/components/OptionsGroup.cpp index 54212d5..b0d841d 100755 --- a/src/client/gui/components/OptionsGroup.cpp +++ b/src/client/gui/components/OptionsGroup.cpp @@ -6,14 +6,36 @@ #include "../../../locale/I18n.h" #include "TextOption.h" #include "KeyOption.h" +#include +#include "../Gui.h" +#include "../Screen.h" +#include "../../../platform/input/Mouse.h" +#include "../../../util/Mth.h" -OptionsGroup::OptionsGroup( std::string labelID ) { +OptionsGroup::OptionsGroup( std::string labelID ) +: contentHeight(0), + scrollOffsetY(0.0f), + maxScrollOffsetY(0.0f), + trackingScrollGesture(false), + scrollingGesture(false), + touchDispatched(false), + dragStartX(0), + dragStartY(0), + lastDragY(0), + touchStartX(0), + touchStartY(0) { label = I18n::get(labelID); } void OptionsGroup::setupPositions() { + const int labelHeight = 18; + const int bottomPadding = 36; + const float requestedScroll = scrollOffsetY; + const int scrollOffset = (int)requestedScroll; + int curY = y + labelHeight - scrollOffset; + const int contentStartY = y + labelHeight; + // First we write the header and then we add the items - int curY = y + 18; for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { (*it)->width = width - 5; @@ -22,16 +44,109 @@ void OptionsGroup::setupPositions() { (*it)->setupPositions(); curY += (*it)->height + 3; } - height = curY; + curY += bottomPadding; + contentHeight = std::max(0, curY - contentStartY + scrollOffset); + maxScrollOffsetY = std::max(0, contentHeight - (height - labelHeight)); + const float clampedScroll = Mth::clamp(requestedScroll, 0.0f, maxScrollOffsetY); + if (clampedScroll != requestedScroll) { + scrollOffsetY = clampedScroll; + setupPositions(); + } } void OptionsGroup::render( Minecraft* minecraft, int xm, int ym ) { float padX = 10.0f; float padY = 5.0f; + const int labelHeight = 18; minecraft->font->draw(label, (float)x + padX, (float)y + padY, 0xffffffff, false); + glEnable2(GL_SCISSOR_TEST); + glScissor( + Gui::GuiScale * x, + minecraft->height - Gui::GuiScale * (y + height), + Gui::GuiScale * width, + Gui::GuiScale * (height - labelHeight) + ); + super::render(minecraft, xm, ym); + glDisable2(GL_SCISSOR_TEST); +} + +void OptionsGroup::tick(Minecraft* minecraft) { + int xm = Mouse::getX(); + int ym = Mouse::getY(); + if (minecraft->screen != NULL) { + minecraft->screen->toGUICoordinate(xm, ym); + } + + bool leftDown = Mouse::isButtonDown(MouseAction::ACTION_LEFT); + + if (trackingScrollGesture && leftDown) { + int dy = ym - lastDragY; + int dx = xm - dragStartX; + if (!scrollingGesture) { + int totalDx = xm - dragStartX; + int totalDy = ym - dragStartY; + if (std::abs(totalDx) >= ScrollStartThreshold || std::abs(totalDy) >= ScrollStartThreshold) { + if (std::abs(totalDy) >= std::abs(totalDx)) { + scrollingGesture = true; + } else if (!touchDispatched) { + super::mouseClicked(minecraft, touchStartX, touchStartY, MouseAction::ACTION_LEFT); + touchDispatched = true; + } + } + } + if (scrollingGesture && dy != 0) { + scrollByPixels((float)dy); + } + lastDragY = ym; + } + super::tick(minecraft); +} + +void OptionsGroup::mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum) { + trackingScrollGesture = false; + scrollingGesture = false; + touchDispatched = false; + + if (buttonNum == MouseAction::ACTION_LEFT && pointInside(x, y)) { + trackingScrollGesture = true; + dragStartX = x; + dragStartY = y; + lastDragY = y; + touchStartX = x; + touchStartY = y; + return; + } + + super::mouseClicked(minecraft, x, y, buttonNum); +} + +void OptionsGroup::mouseReleased(Minecraft* minecraft, int x, int y, int buttonNum) { + bool wasScrolling = scrollingGesture; + bool wasTracking = trackingScrollGesture; + trackingScrollGesture = false; + scrollingGesture = false; + if (buttonNum == MouseAction::ACTION_LEFT && wasTracking && !touchDispatched && pointInside(touchStartX, touchStartY)) { + super::mouseClicked(minecraft, touchStartX, touchStartY, buttonNum); + touchDispatched = true; + } + + if (!wasScrolling) { + super::mouseReleased(minecraft, x, y, buttonNum); + } +} + +void OptionsGroup::scrollByPixels(float deltaY) { + if (deltaY == 0.0f || maxScrollOffsetY <= 0.0f) return; + + scrollOffsetY = Mth::clamp(scrollOffsetY - deltaY, 0.0f, maxScrollOffsetY); + setupPositions(); +} + +bool OptionsGroup::isScrollingGestureActive() const { + return trackingScrollGesture || scrollingGesture; } OptionsGroup& OptionsGroup::addOptionItem(OptionId optId, Minecraft* minecraft ) { diff --git a/src/client/gui/components/OptionsGroup.h b/src/client/gui/components/OptionsGroup.h index 24d28bd..6e65b70 100755 --- a/src/client/gui/components/OptionsGroup.h +++ b/src/client/gui/components/OptionsGroup.h @@ -17,7 +17,12 @@ public: OptionsGroup(std::string labelID); virtual void setupPositions(); virtual void render(Minecraft* minecraft, int xm, int ym); + virtual void tick(Minecraft* minecraft); + virtual void mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum); + virtual void mouseReleased(Minecraft* minecraft, int x, int y, int buttonNum); OptionsGroup& addOptionItem(OptionId optId, Minecraft* minecraft); + void scrollByPixels(float deltaY); + bool isScrollingGestureActive() const; protected: void createToggle(OptionId optId, Minecraft* minecraft); @@ -27,6 +32,18 @@ protected: void createKey(OptionId optId, Minecraft* minecraft); std::string label; + int contentHeight; + float scrollOffsetY; + float maxScrollOffsetY; + bool trackingScrollGesture; + bool scrollingGesture; + bool touchDispatched; + int dragStartX; + int dragStartY; + int lastDragY; + int touchStartX; + int touchStartY; + static const int ScrollStartThreshold = 5; }; #endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsGroup_H__*/ diff --git a/src/client/gui/screens/OptionsScreen.cpp b/src/client/gui/screens/OptionsScreen.cpp index d4eced8..5bed753 100755 --- a/src/client/gui/screens/OptionsScreen.cpp +++ b/src/client/gui/screens/OptionsScreen.cpp @@ -121,6 +121,7 @@ void OptionsScreen::setupPositions() { (*it)->x = categoryButtons[0]->width; (*it)->y = bHeader->height; (*it)->width = width - categoryButtons[0]->width; + (*it)->height = height - bHeader->height; (*it)->setupPositions(); } @@ -261,6 +262,12 @@ void OptionsScreen::mouseReleased(int x, int y, int buttonNum) { super::mouseReleased(x, y, buttonNum); } +void OptionsScreen::mouseWheel(int dx, int dy, int xm, int ym) { + if (currentOptionsGroup != NULL && currentOptionsGroup->pointInside(xm, ym) && dy != 0) { + currentOptionsGroup->scrollByPixels((float)dy * 18.0f); + } +} + void OptionsScreen::keyPressed(int eventKey) { if (currentOptionsGroup != NULL) currentOptionsGroup->keyPressed(minecraft, eventKey); diff --git a/src/client/gui/screens/OptionsScreen.h b/src/client/gui/screens/OptionsScreen.h index b02c7b8..dd1e73e 100755 --- a/src/client/gui/screens/OptionsScreen.h +++ b/src/client/gui/screens/OptionsScreen.h @@ -27,6 +27,7 @@ public: virtual void mouseClicked(int x, int y, int buttonNum); virtual void mouseReleased(int x, int y, int buttonNum); + virtual void mouseWheel(int dx, int dy, int xm, int ym); virtual void keyPressed(int eventKey); virtual void charPressed(char inputChar);