import pyray as rl 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, show_vertical_scroll_bar: bool = False): self._scroll_state: ScrollState = ScrollState.IDLE self._last_mouse_y: float = 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, 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, 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._scroll_state != ScrollState.IDLE: if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): delta_y = mouse_pos.y - self._last_mouse_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._scroll_state = ScrollState.IDLE # Handle mouse wheel scrolling wheel_move = rl.get_mouse_wheel_move() if self._show_vertical_scroll_bar: 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._offset.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED # Apply inertia (continue scrolling after mouse release) 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 if abs(self._velocity_y) < MIN_VELOCITY: self._velocity_y = 0.0 # Ensure scrolling doesn't go beyond bounds max_scroll_y = max(content.height - bounds.height, 0) self._offset.y = max(min(self._offset.y, 0), -max_scroll_y) return self._offset