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