1313 UILayout ,
1414 UIMouseDragEvent ,
1515 UIMouseEvent ,
16+ UIMouseMovementEvent ,
17+ UIMousePressEvent ,
18+ UIMouseReleaseEvent ,
1619 UIMouseScrollEvent ,
1720 UIWidget ,
1821 bind ,
@@ -24,10 +27,12 @@ class UIScrollBar(UIWidget):
2427 """Scroll bar for a UIScrollLayout.
2528
2629 Indicating the current view position of the scroll area.
27-
28- Does not support mouse interaction yet.
30+ Supports mouse dragging to scroll the content.
2931 """
3032
33+ _thumb_hover = Property (False )
34+ _dragging = Property (False )
35+
3136 def __init__ (self , scroll_area : UIScrollArea , vertical : bool = True ):
3237 size_hint = (0.05 , 1 ) if vertical else (1 , 0.05 )
3338
@@ -37,17 +42,65 @@ def __init__(self, scroll_area: UIScrollArea, vertical: bool = True):
3742 self .with_border (color = arcade .uicolor .GRAY_CONCRETE )
3843 self .vertical = vertical
3944
45+ self ._scroll_bar_size = 20
46+
47+ bind (self , "_thumb_hover" , self .trigger_render )
48+ bind (self , "_dragging" , self .trigger_render )
49+ bind (scroll_area , "scroll_x" , self .trigger_full_render )
4050 bind (scroll_area , "scroll_y" , self .trigger_full_render )
4151 bind (scroll_area , "content_height" , self .trigger_full_render )
52+ bind (scroll_area , "content_width" , self .trigger_full_render )
4253
43- def do_render (self , surface : Surface ):
44- """Render the scroll bar."""
45- self .prepare_render (surface )
54+ def on_event (self , event : UIEvent ) -> Optional [bool ]:
55+ # detect if event is mouse down and inside the scroll thumb
56+ # if so, start dragging the thumb
57+ thumb_rect_relative = self ._thumb_rect ()
58+ thumb_rect = thumb_rect_relative .move (* self .rect .bottom_left )
59+
60+ if isinstance (event , UIMouseMovementEvent ):
61+ self ._thumb_hover = thumb_rect .point_in_rect (event .pos )
62+
63+ if isinstance (event , UIMousePressEvent ) and thumb_rect .point_in_rect (event .pos ):
64+ self ._dragging = True
65+ return True
66+
67+ # detect if event is mouse drag and thumb is being dragged
68+ # if so, update the scroll position
69+ if isinstance (event , UIMouseDragEvent ) and self ._dragging :
70+ sx , sy = event .pos - self .rect .bottom_left
71+ sx -= self ._scroll_bar_size / 2
72+ sy -= self ._scroll_bar_size / 2
73+
74+ scroll_area = self .scroll_area
75+
76+ if self .vertical :
77+ available_track_size = self .content_height - self ._scroll_bar_size
78+ target_progress = 1 - sy / available_track_size
79+ target_progress = max (0 , min (1 , target_progress ))
80+
81+ scroll_range = scroll_area .surface .height - scroll_area .content_height
82+ scroll_area .scroll_y = - target_progress * scroll_range
83+ else :
84+ available_track_size = self .content_width - self ._scroll_bar_size
85+ target_progress = sx / available_track_size
86+ target_progress = max (0 , min (1 , target_progress ))
87+ scroll_range = scroll_area .surface .width - scroll_area .content_width
88+ scroll_area .scroll_x = - target_progress * scroll_range
4689
47- # calc position and size of the scroll bar
90+ return True
91+
92+ # detect if event is mouse up and thumb is being dragged
93+ # if so, stop dragging the thumb
94+ if isinstance (event , UIMouseReleaseEvent ) and self ._dragging :
95+ self ._dragging = False
96+ return True
97+
98+ return EVENT_UNHANDLED
99+
100+ def _thumb_rect (self ):
101+ """Calculate the rect of the thumb."""
48102 scroll_area = self .scroll_area
49103
50- # calculate the scroll bar position
51104 scroll_value = scroll_area .scroll_y if self .vertical else scroll_area .scroll_x
52105 scroll_range = (
53106 scroll_area .surface .height - scroll_area .content_height
@@ -57,29 +110,35 @@ def do_render(self, surface: Surface):
57110
58111 scroll_progress = - scroll_value / scroll_range
59112
60- scroll_bar_size = 20
61113 content_size = self .content_height if self .vertical else self .content_width
62- available_track_size = content_size - scroll_bar_size
114+ available_track_size = content_size - self . _scroll_bar_size
63115
64116 if self .vertical :
65- scroll_bar_y = scroll_bar_size / 2 + available_track_size * (1 - scroll_progress )
117+ scroll_bar_y = self . _scroll_bar_size / 2 + available_track_size * (1 - scroll_progress )
66118 scroll_bar_x = self .content_width / 2
67-
68- # draw the scroll bar
69- arcade .draw_rect_filled (
70- XYWH (scroll_bar_x , scroll_bar_y , self .content_height , scroll_bar_size ),
71- color = arcade .uicolor .GRAY_ASBESTOS ,
72- )
119+ return XYWH (scroll_bar_x , scroll_bar_y , self .content_width , self ._scroll_bar_size )
73120
74121 else :
75- scroll_bar_x = scroll_bar_size / 2 + available_track_size * scroll_progress
122+ scroll_bar_x = self . _scroll_bar_size / 2 + available_track_size * scroll_progress
76123 scroll_bar_y = self .content_height / 2
124+ return XYWH (scroll_bar_x , scroll_bar_y , self ._scroll_bar_size , self .content_height )
77125
78- # draw the scroll bar
79- arcade .draw_rect_filled (
80- XYWH (scroll_bar_x , scroll_bar_y , scroll_bar_size , self .content_width ),
81- color = arcade .uicolor .GRAY_ASBESTOS ,
82- )
126+ def do_render (self , surface : Surface ):
127+ """Render the scroll bar."""
128+ self .prepare_render (surface )
129+
130+ if self ._dragging :
131+ thumb_color = arcade .uicolor .DARK_BLUE_WET_ASPHALT
132+ elif self ._thumb_hover :
133+ thumb_color = arcade .uicolor .GRAY_CONCRETE
134+ else :
135+ thumb_color = arcade .uicolor .GRAY_ASBESTOS
136+
137+ # draw the thumb
138+ arcade .draw_rect_filled (
139+ rect = self ._thumb_rect (),
140+ color = thumb_color ,
141+ )
83142
84143
85144class UIScrollArea (UILayout ):
0 commit comments