mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Add DataTable.move_cursor (#2479)
* Add 'DataTable.move_cursor'. Related issues: #2472. * Fix #2471. * Simplify cursor changes. * Address review feedback. Related comments: https://github.com/Textualize/textual/pull/2479\#discussion_r1185016002
This commit is contained in:
committed by
GitHub
parent
14850d54a3
commit
819b2f1eb3
@@ -19,6 +19,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- run_worker exclusive parameter is now `False` by default https://github.com/Textualize/textual/pull/2470
|
- run_worker exclusive parameter is now `False` by default https://github.com/Textualize/textual/pull/2470
|
||||||
- Added `always_update` as an optional argument for `reactive.var`
|
- Added `always_update` as an optional argument for `reactive.var`
|
||||||
|
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Method `DataTable.move_cursor` https://github.com/Textualize/textual/issues/2472
|
||||||
|
|
||||||
## [0.23.0] - 2023-05-03
|
## [0.23.0] - 2023-05-03
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -311,6 +311,11 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
cursor_coordinate: Reactive[Coordinate] = Reactive(
|
cursor_coordinate: Reactive[Coordinate] = Reactive(
|
||||||
Coordinate(0, 0), repaint=False, always_update=True
|
Coordinate(0, 0), repaint=False, always_update=True
|
||||||
)
|
)
|
||||||
|
"""Current cursor [`Coordinate`][textual.coordinate.Coordinate].
|
||||||
|
|
||||||
|
This can be set programmatically or changed via the method
|
||||||
|
[`move_cursor`][textual.widgets.DataTable.move_cursor].
|
||||||
|
"""
|
||||||
hover_coordinate: Reactive[Coordinate] = Reactive(
|
hover_coordinate: Reactive[Coordinate] = Reactive(
|
||||||
Coordinate(0, 0), repaint=False, always_update=True
|
Coordinate(0, 0), repaint=False, always_update=True
|
||||||
)
|
)
|
||||||
@@ -953,7 +958,41 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
elif self.cursor_type == "column":
|
elif self.cursor_type == "column":
|
||||||
self.refresh_column(old_coordinate.column)
|
self.refresh_column(old_coordinate.column)
|
||||||
self._highlight_column(new_coordinate.column)
|
self._highlight_column(new_coordinate.column)
|
||||||
self._scroll_cursor_into_view()
|
# If the coordinate was changed via `move_cursor`, give priority to its
|
||||||
|
# scrolling because it may be animated.
|
||||||
|
self.call_next(self._scroll_cursor_into_view)
|
||||||
|
|
||||||
|
def move_cursor(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
row: int | None = None,
|
||||||
|
column: int | None = None,
|
||||||
|
animate: bool = False,
|
||||||
|
) -> None:
|
||||||
|
"""Move the cursor to the given position.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```py
|
||||||
|
datatable = app.query_one(DataTable)
|
||||||
|
datatable.move_cursor(row=4, column=6)
|
||||||
|
# datatable.cursor_coordinate == Coordinate(4, 6)
|
||||||
|
datatable.move_cursor(row=3)
|
||||||
|
# datatable.cursor_coordinate == Coordinate(3, 6)
|
||||||
|
```
|
||||||
|
|
||||||
|
Args:
|
||||||
|
row: The new row to move the cursor to.
|
||||||
|
column: The new column to move the cursor to.
|
||||||
|
animate: Whether to animate the change of coordinates.
|
||||||
|
"""
|
||||||
|
cursor_row, cursor_column = self.cursor_coordinate
|
||||||
|
if row is not None:
|
||||||
|
cursor_row = row
|
||||||
|
if column is not None:
|
||||||
|
cursor_column = column
|
||||||
|
destination = Coordinate(cursor_row, cursor_column)
|
||||||
|
self.cursor_coordinate = destination
|
||||||
|
self._scroll_cursor_into_view(animate=animate)
|
||||||
|
|
||||||
def _highlight_coordinate(self, coordinate: Coordinate) -> None:
|
def _highlight_coordinate(self, coordinate: Coordinate) -> None:
|
||||||
"""Apply highlighting to the cell at the coordinate, and post event."""
|
"""Apply highlighting to the cell at the coordinate, and post event."""
|
||||||
@@ -2055,7 +2094,6 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
self.cursor_coordinate = Coordinate(
|
self.cursor_coordinate = Coordinate(
|
||||||
row_index + rows_to_scroll - 1, column_index
|
row_index + rows_to_scroll - 1, column_index
|
||||||
)
|
)
|
||||||
self._scroll_cursor_into_view()
|
|
||||||
else:
|
else:
|
||||||
super().action_page_down()
|
super().action_page_down()
|
||||||
|
|
||||||
@@ -2079,7 +2117,6 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
self.cursor_coordinate = Coordinate(
|
self.cursor_coordinate = Coordinate(
|
||||||
row_index - rows_to_scroll + 1, column_index
|
row_index - rows_to_scroll + 1, column_index
|
||||||
)
|
)
|
||||||
self._scroll_cursor_into_view()
|
|
||||||
else:
|
else:
|
||||||
super().action_page_up()
|
super().action_page_up()
|
||||||
|
|
||||||
@@ -2090,7 +2127,6 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
|
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
|
||||||
row_index, column_index = self.cursor_coordinate
|
row_index, column_index = self.cursor_coordinate
|
||||||
self.cursor_coordinate = Coordinate(0, column_index)
|
self.cursor_coordinate = Coordinate(0, column_index)
|
||||||
self._scroll_cursor_into_view()
|
|
||||||
else:
|
else:
|
||||||
super().action_scroll_home()
|
super().action_scroll_home()
|
||||||
|
|
||||||
@@ -2101,7 +2137,6 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
|
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
|
||||||
row_index, column_index = self.cursor_coordinate
|
row_index, column_index = self.cursor_coordinate
|
||||||
self.cursor_coordinate = Coordinate(self.row_count - 1, column_index)
|
self.cursor_coordinate = Coordinate(self.row_count - 1, column_index)
|
||||||
self._scroll_cursor_into_view()
|
|
||||||
else:
|
else:
|
||||||
super().action_scroll_end()
|
super().action_scroll_end()
|
||||||
|
|
||||||
@@ -2110,7 +2145,6 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
cursor_type = self.cursor_type
|
cursor_type = self.cursor_type
|
||||||
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
|
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
|
||||||
self.cursor_coordinate = self.cursor_coordinate.up()
|
self.cursor_coordinate = self.cursor_coordinate.up()
|
||||||
self._scroll_cursor_into_view()
|
|
||||||
else:
|
else:
|
||||||
# If the cursor doesn't move up (e.g. column cursor can't go up),
|
# If the cursor doesn't move up (e.g. column cursor can't go up),
|
||||||
# then ensure that we instead scroll the DataTable.
|
# then ensure that we instead scroll the DataTable.
|
||||||
@@ -2121,7 +2155,6 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
cursor_type = self.cursor_type
|
cursor_type = self.cursor_type
|
||||||
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
|
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
|
||||||
self.cursor_coordinate = self.cursor_coordinate.down()
|
self.cursor_coordinate = self.cursor_coordinate.down()
|
||||||
self._scroll_cursor_into_view()
|
|
||||||
else:
|
else:
|
||||||
super().action_scroll_down()
|
super().action_scroll_down()
|
||||||
|
|
||||||
|
|||||||
@@ -996,23 +996,34 @@ def test_key_string_lookup():
|
|||||||
async def test_scrolling_cursor_into_view():
|
async def test_scrolling_cursor_into_view():
|
||||||
"""Regression test for https://github.com/Textualize/textual/issues/2459"""
|
"""Regression test for https://github.com/Textualize/textual/issues/2459"""
|
||||||
|
|
||||||
class TableApp(App):
|
class ScrollingApp(DataTableApp):
|
||||||
CSS = "DataTable { height: 100%; }"
|
CSS = "DataTable { height: 100%; }"
|
||||||
|
|
||||||
def compose(self):
|
|
||||||
yield DataTable()
|
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
|
||||||
table = self.query_one(DataTable)
|
|
||||||
table.add_column("n")
|
|
||||||
table.add_rows([(n,) for n in range(300)])
|
|
||||||
|
|
||||||
def key_c(self):
|
def key_c(self):
|
||||||
self.query_one(DataTable).cursor_coordinate = Coordinate(200, 0)
|
self.query_one(DataTable).cursor_coordinate = Coordinate(200, 0)
|
||||||
|
|
||||||
app = TableApp()
|
app = ScrollingApp()
|
||||||
|
|
||||||
async with app.run_test() as pilot:
|
async with app.run_test() as pilot:
|
||||||
|
table = app.query_one(DataTable)
|
||||||
|
table.add_column("n")
|
||||||
|
table.add_rows([(n,) for n in range(300)])
|
||||||
|
|
||||||
await pilot.press("c")
|
await pilot.press("c")
|
||||||
await pilot.pause()
|
assert table.scroll_y > 100
|
||||||
assert app.query_one(DataTable).scroll_y > 100
|
|
||||||
|
|
||||||
|
async def test_move_cursor():
|
||||||
|
app = DataTableApp()
|
||||||
|
|
||||||
|
async with app.run_test():
|
||||||
|
table = app.query_one(DataTable)
|
||||||
|
table.add_columns(*"These are some columns in your nice table".split())
|
||||||
|
table.add_rows(["These are some columns in your nice table".split()] * 10)
|
||||||
|
|
||||||
|
table.move_cursor(row=4, column=6)
|
||||||
|
assert table.cursor_coordinate == Coordinate(4, 6)
|
||||||
|
table.move_cursor(row=3)
|
||||||
|
assert table.cursor_coordinate == Coordinate(3, 6)
|
||||||
|
table.move_cursor(column=3)
|
||||||
|
assert table.cursor_coordinate == Coordinate(3, 3)
|
||||||
|
|||||||
Reference in New Issue
Block a user