|
|
@ -1,54 +1,67 @@ |
|
|
|
import pyray as rl |
|
|
|
import pyray as rl |
|
|
|
from cffi import FFI |
|
|
|
from enum import IntEnum |
|
|
|
|
|
|
|
|
|
|
|
MOUSE_WHEEL_SCROLL_SPEED = 30 |
|
|
|
MOUSE_WHEEL_SCROLL_SPEED = 30 |
|
|
|
INERTIA_FRICTION = 0.95 # The rate at which the inertia slows down |
|
|
|
INERTIA_FRICTION = 0.95 # The rate at which the inertia slows down |
|
|
|
MIN_VELOCITY = 0.1 # Minimum velocity before stopping the inertia |
|
|
|
MIN_VELOCITY = 0.1 # Minimum velocity before stopping the inertia |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScrollState(IntEnum): |
|
|
|
|
|
|
|
IDLE = 0 |
|
|
|
|
|
|
|
DRAGGING_CONTENT = 1 |
|
|
|
|
|
|
|
DRAGGING_SCROLLBAR = 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GuiScrollPanel: |
|
|
|
class GuiScrollPanel: |
|
|
|
def __init__(self, bounds: rl.Rectangle, content: rl.Rectangle, show_vertical_scroll_bar: bool = False): |
|
|
|
def __init__(self, show_vertical_scroll_bar: bool = False): |
|
|
|
self._dragging: bool = False |
|
|
|
self._scroll_state: ScrollState = ScrollState.IDLE |
|
|
|
self._last_mouse_y: float = 0.0 |
|
|
|
self._last_mouse_y: float = 0.0 |
|
|
|
self._bounds = bounds |
|
|
|
self._offset = rl.Vector2(0, 0) |
|
|
|
self._content = content |
|
|
|
|
|
|
|
self._scroll = rl.Vector2(0, 0) |
|
|
|
|
|
|
|
self._view = rl.Rectangle(0, 0, 0, 0) |
|
|
|
self._view = rl.Rectangle(0, 0, 0, 0) |
|
|
|
self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar |
|
|
|
self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar |
|
|
|
self._velocity_y = 0.0 # Velocity for inertia |
|
|
|
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() |
|
|
|
mouse_pos = rl.get_mouse_position() |
|
|
|
|
|
|
|
|
|
|
|
# Handle dragging logic |
|
|
|
# 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 rl.check_collision_point_rec(mouse_pos, bounds) and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): |
|
|
|
if not self._dragging: |
|
|
|
if self._scroll_state == ScrollState.IDLE: |
|
|
|
self._dragging = True |
|
|
|
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._last_mouse_y = mouse_pos.y |
|
|
|
self._velocity_y = 0.0 # Reset velocity when drag starts |
|
|
|
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): |
|
|
|
if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): |
|
|
|
delta_y = mouse_pos.y - self._last_mouse_y |
|
|
|
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._last_mouse_y = mouse_pos.y |
|
|
|
self._velocity_y = delta_y # Update velocity during drag |
|
|
|
self._velocity_y = delta_y # Update velocity during drag |
|
|
|
else: |
|
|
|
else: |
|
|
|
self._dragging = False |
|
|
|
self._scroll_state = ScrollState.IDLE |
|
|
|
|
|
|
|
|
|
|
|
# Handle mouse wheel scrolling |
|
|
|
# Handle mouse wheel scrolling |
|
|
|
wheel_move = rl.get_mouse_wheel_move() |
|
|
|
wheel_move = rl.get_mouse_wheel_move() |
|
|
|
if self._show_vertical_scroll_bar: |
|
|
|
if self._show_vertical_scroll_bar: |
|
|
|
self._scroll.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) |
|
|
|
self._offset.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) |
|
|
|
rl.gui_scroll_panel(self._bounds, FFI().NULL, self._content, self._scroll, self._view) |
|
|
|
rl.gui_scroll_panel(bounds, rl.ffi.NULL, content, self._offset, self._view) |
|
|
|
else: |
|
|
|
else: |
|
|
|
self._scroll.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED |
|
|
|
self._offset.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) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Apply inertia (continue scrolling after mouse release) |
|
|
|
# Apply inertia (continue scrolling after mouse release) |
|
|
|
if not self._dragging: |
|
|
|
if self._scroll_state == ScrollState.IDLE: |
|
|
|
self._scroll.y += self._velocity_y |
|
|
|
self._offset.y += self._velocity_y |
|
|
|
self._velocity_y *= INERTIA_FRICTION # Slow down velocity over time |
|
|
|
self._velocity_y *= INERTIA_FRICTION # Slow down velocity over time |
|
|
|
|
|
|
|
|
|
|
|
# Stop scrolling when velocity is low |
|
|
|
# Stop scrolling when velocity is low |
|
|
@ -56,7 +69,7 @@ class GuiScrollPanel: |
|
|
|
self._velocity_y = 0.0 |
|
|
|
self._velocity_y = 0.0 |
|
|
|
|
|
|
|
|
|
|
|
# Ensure scrolling doesn't go beyond bounds |
|
|
|
# Ensure scrolling doesn't go beyond bounds |
|
|
|
max_scroll_y = max(self._content.height - self._bounds.height, 0) |
|
|
|
max_scroll_y = max(content.height - bounds.height, 0) |
|
|
|
self._scroll.y = max(min(self._scroll.y, 0), -max_scroll_y) |
|
|
|
self._offset.y = max(min(self._offset.y, 0), -max_scroll_y) |
|
|
|
|
|
|
|
|
|
|
|
return self._scroll |
|
|
|
return self._offset |
|
|
|