From 4674d0ae530efe9cd998b230b3ae87d61180a79d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 20 Feb 2025 13:15:21 +0800 Subject: [PATCH] python ui: fix scroll issues (#34600) fix scroll issues --- system/ui/lib/scroll_panel.py | 59 +++++++++++++++++++++-------------- system/ui/text.py | 4 +-- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index 0854ad954c..0dc757ff6f 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -1,54 +1,67 @@ import pyray as rl -from cffi import FFI +from enum import IntEnum MOUSE_WHEEL_SCROLL_SPEED = 30 INERTIA_FRICTION = 0.95 # The rate at which the inertia slows down MIN_VELOCITY = 0.1 # Minimum velocity before stopping the inertia +class ScrollState(IntEnum): + IDLE = 0 + DRAGGING_CONTENT = 1 + DRAGGING_SCROLLBAR = 2 + + class GuiScrollPanel: - def __init__(self, bounds: rl.Rectangle, content: rl.Rectangle, show_vertical_scroll_bar: bool = False): - self._dragging: bool = False + def __init__(self, show_vertical_scroll_bar: bool = False): + self._scroll_state: ScrollState = ScrollState.IDLE self._last_mouse_y: float = 0.0 - self._bounds = bounds - self._content = content - self._scroll = rl.Vector2(0, 0) + self._offset = rl.Vector2(0, 0) self._view = rl.Rectangle(0, 0, 0, 0) self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar self._velocity_y = 0.0 # Velocity for inertia - def handle_scroll(self) -> rl.Vector2: + def handle_scroll(self, bounds: rl.Rectangle, content: rl.Rectangle) -> rl.Vector2: mouse_pos = rl.get_mouse_position() # Handle dragging logic - if rl.check_collision_point_rec(mouse_pos, self._bounds) and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): - if not self._dragging: - self._dragging = True + if rl.check_collision_point_rec(mouse_pos, bounds) and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): + if self._scroll_state == ScrollState.IDLE: + self._scroll_state = ScrollState.DRAGGING_CONTENT + if self._show_vertical_scroll_bar: + scrollbar_width = rl.gui_get_style(rl.GuiControl.LISTVIEW, rl.GuiListViewProperty.SCROLLBAR_WIDTH) + scrollbar_x = bounds.x + bounds.width - scrollbar_width + if mouse_pos.x >= scrollbar_x: + self._scroll_state = ScrollState.DRAGGING_SCROLLBAR + self._last_mouse_y = mouse_pos.y self._velocity_y = 0.0 # Reset velocity when drag starts - if self._dragging: + if self._scroll_state != ScrollState.IDLE: if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): delta_y = mouse_pos.y - self._last_mouse_y - self._scroll.y += delta_y + + if self._scroll_state == ScrollState.DRAGGING_CONTENT: + self._offset.y += delta_y + else: + delta_y = -delta_y + self._last_mouse_y = mouse_pos.y self._velocity_y = delta_y # Update velocity during drag else: - self._dragging = False + self._scroll_state = ScrollState.IDLE # Handle mouse wheel scrolling wheel_move = rl.get_mouse_wheel_move() if self._show_vertical_scroll_bar: - self._scroll.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) - rl.gui_scroll_panel(self._bounds, FFI().NULL, self._content, self._scroll, self._view) + self._offset.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) + rl.gui_scroll_panel(bounds, rl.ffi.NULL, content, self._offset, self._view) else: - self._scroll.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED - max_scroll_y = self._content.height - self._bounds.height - self._scroll.y = max(min(self._scroll.y, 0), -max_scroll_y) + self._offset.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED # Apply inertia (continue scrolling after mouse release) - if not self._dragging: - self._scroll.y += self._velocity_y + if self._scroll_state == ScrollState.IDLE: + self._offset.y += self._velocity_y self._velocity_y *= INERTIA_FRICTION # Slow down velocity over time # Stop scrolling when velocity is low @@ -56,7 +69,7 @@ class GuiScrollPanel: self._velocity_y = 0.0 # Ensure scrolling doesn't go beyond bounds - max_scroll_y = max(self._content.height - self._bounds.height, 0) - self._scroll.y = max(min(self._scroll.y, 0), -max_scroll_y) + max_scroll_y = max(content.height - bounds.height, 0) + self._offset.y = max(min(self._offset.y, 0), -max_scroll_y) - return self._scroll + return self._offset diff --git a/system/ui/text.py b/system/ui/text.py index ab8c429287..77a0d267e4 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -42,10 +42,10 @@ def main(): textarea_rect = rl.Rectangle(MARGIN, MARGIN, gui_app.width - MARGIN * 2, gui_app.height - MARGIN * 2 - BUTTON_SIZE.y - SPACING) wrapped_lines = wrap_text(text_content, FONT_SIZE, textarea_rect.width - 20) content_rect = rl.Rectangle(0, 0, textarea_rect.width - 20, len(wrapped_lines) * LINE_HEIGHT) - scroll_panel = GuiScrollPanel(textarea_rect, content_rect, show_vertical_scroll_bar=True) + scroll_panel = GuiScrollPanel(show_vertical_scroll_bar=True) for _ in gui_app.render(): - scroll = scroll_panel.handle_scroll() + scroll = scroll_panel.handle_scroll(textarea_rect, content_rect) rl.begin_scissor_mode(int(textarea_rect.x), int(textarea_rect.y), int(textarea_rect.width), int(textarea_rect.height)) for i, line in enumerate(wrapped_lines):