22
33from __future__ import annotations
44
5- from typing import Any , ClassVar
5+ from typing import TYPE_CHECKING , Any , ClassVar
66
77from textual .app import App , ComposeResult
88from textual .binding import Binding
1818 make_preview_renderables ,
1919)
2020
21+ if TYPE_CHECKING :
22+ from textual import events
23+
2124_KIND_ICONS = {
2225 "group" : "📁" ,
2326 "ndarray" : "▦" ,
@@ -105,6 +108,26 @@ def action_select_cursor(self) -> None:
105108 return
106109 super ().action_select_cursor ()
107110
111+ def _wheel_step (self ) -> int :
112+ # Half the visible rows per tick; arrow keys remain the
113+ # single-step path (also for dim-mode index changes).
114+ return max (1 , self .row_count // 2 )
115+
116+ def on_mouse_scroll_down (self , event : events .MouseScrollDown ) -> None :
117+ # The grid holds exactly one viewport-sized page, so the default
118+ # scroll handler has nothing to scroll; move the cursor instead,
119+ # which pages at the edges just like the arrow keys.
120+ event .stop ()
121+ event .prevent_default ()
122+ for _ in range (self ._wheel_step ()):
123+ self .action_cursor_down ()
124+
125+ def on_mouse_scroll_up (self , event : events .MouseScrollUp ) -> None :
126+ event .stop ()
127+ event .prevent_default ()
128+ for _ in range (self ._wheel_step ()):
129+ self .action_cursor_up ()
130+
108131 def on_resize (self , event ) -> None :
109132 # The column/row windows are fitted to this table's size; re-check
110133 # whenever it changes (terminal resize, panel maximize, ...).
@@ -1231,7 +1254,7 @@ def action_refresh(self) -> None:
12311254 def _adjust_fixed_value (self , direction : int ) -> None :
12321255 """Adjust the fixed value of the active dimension (if it is fixed).
12331256
1234- In DIM mode the value wraps around at boundaries (0 ↔ max ).
1257+ The value clamps at the boundaries (no wrap-around ).
12351258 """
12361259 layout = self ._data_layout
12371260 if layout is None or self .table_page is None :
@@ -1243,19 +1266,9 @@ def _adjust_fixed_value(self, direction: int) -> None:
12431266 if total <= 0 :
12441267 return
12451268 current = layout .fixed_values [dim ]
1246- if self ._dim_mode and total > 1 :
1247- # Cycle: up at max → 0, down at 0 → max-1
1248- new_val = (current + direction ) % total
1249- else :
1250- # Clamp at boundaries (normal mode)
1251- if direction > 0 :
1252- if current >= total - 1 :
1253- return
1254- new_val = current + 1
1255- else :
1256- if current <= 0 :
1257- return
1258- new_val = current - 1
1269+ new_val = min (max (current + direction , 0 ), total - 1 )
1270+ if new_val == current :
1271+ return
12591272 new_fixed = dict (layout .fixed_values )
12601273 new_fixed [dim ] = new_val
12611274 self ._data_layout = layout .copy_with (fixed_values = new_fixed )
@@ -1335,7 +1348,7 @@ def _dim_adjust(self, direction: int) -> None:
13351348 self ._scroll_navigable_viewport (direction )
13361349
13371350 def _scroll_navigable_viewport (self , direction : int ) -> None :
1338- """Shift the viewport of a navigable dimension by one step (wraps )."""
1351+ """Shift the viewport of a navigable dimension by one step (clamps )."""
13391352 layout = self ._data_layout
13401353 if layout is None or self .table_page is None :
13411354 return
@@ -1348,13 +1361,19 @@ def _scroll_navigable_viewport(self, direction: int) -> None:
13481361 total = layout .shape [dim ]
13491362
13501363 if pos == 0 :
1351- # Row navigable dim — shift start by one row (wraps)
1352- new_start = (page ["start" ] + direction ) % total
1364+ # Row navigable dim — shift start by one row, keeping a full page
1365+ max_start = max (0 , total - self ._table_page_size ())
1366+ new_start = min (max (page ["start" ] + direction , 0 ), max_start )
1367+ if new_start == page ["start" ]:
1368+ return
13531369 self .table_buffer = None
13541370 data = self ._load_table_page (self .selected_path , new_start )
13551371 else :
1356- # Column navigable dim — shift col_start by one column (wraps)
1357- new_col = (page ["col_start" ] + direction ) % total
1372+ # Column navigable dim — shift col_start by one whole column
1373+ max_col = self ._fit_col_start_backward (total )
1374+ new_col = min (max (page ["col_start" ] + direction , 0 ), max_col )
1375+ if new_col == page ["col_start" ]:
1376+ return
13581377 self .grid_col_start = new_col
13591378 self .table_buffer = None
13601379 data = self ._load_table_page (self .selected_path , page ["start" ])
0 commit comments