Move DataTable cursor with page up/down, home, end (#2228)

* Add pageup and pagedown actions to DataTable, with no impls

* Pagedown moves DataTable cursor now

* Account for header height in pagedown action

* Page Up support in the DataTable

* Fix and off-by-1, ensure page up/down works on col cursor

* Add placeholder scroll home/end action handlers to datatable

* Add scroll home and scroll end

* Hide hover cursor when home or end is used

* Ensure home and end work correctly with all curosrs

* Testing home/end/pagedown/pageup cursor movement in DataTable

* Docstrings for new datatable actions

* Fix a broken unit test for the DataTable
This commit is contained in:
darrenburns
2023-04-06 21:22:31 +01:00
committed by GitHub
parent c3e56f1f19
commit 976bd2f5c2
2 changed files with 100 additions and 2 deletions

View File

@@ -203,6 +203,8 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
Binding("down", "cursor_down", "Cursor Down", show=False),
Binding("right", "cursor_right", "Cursor Right", show=False),
Binding("left", "cursor_left", "Cursor Left", show=False),
Binding("pageup", "page_up", "Page Up", show=False),
Binding("pagedown", "page_down", "Page Down", show=False),
]
"""
| Key(s) | Description |
@@ -247,7 +249,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
background: $surface ;
color: $text;
height: auto;
max-height: 100vh
max-height: 100vh;
}
DataTable > .datatable--header {
text-style: bold;
@@ -1976,6 +1978,76 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
self._scroll_cursor_into_view(animate=True)
event.stop()
def action_page_down(self) -> None:
"""Move the cursor one page down."""
self._set_hover_cursor(False)
cursor_type = self.cursor_type
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
height = self.size.height - self.header_height if self.show_header else 0
# Determine how many rows constitutes a "page"
offset = 0
rows_to_scroll = 0
row_index, column_index = self.cursor_coordinate
for ordered_row in self.ordered_rows[row_index:]:
offset += ordered_row.height
if offset > height:
break
rows_to_scroll += 1
self.cursor_coordinate = Coordinate(
row_index + rows_to_scroll - 1, column_index
)
self._scroll_cursor_into_view()
else:
super().action_page_down()
def action_page_up(self) -> None:
"""Move the cursor one page up."""
self._set_hover_cursor(False)
cursor_type = self.cursor_type
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
height = self.size.height - self.header_height if self.show_header else 0
# Determine how many rows constitutes a "page"
offset = 0
rows_to_scroll = 0
row_index, column_index = self.cursor_coordinate
for ordered_row in self.ordered_rows[: row_index + 1]:
offset += ordered_row.height
if offset > height:
break
rows_to_scroll += 1
self.cursor_coordinate = Coordinate(
row_index - rows_to_scroll + 1, column_index
)
self._scroll_cursor_into_view()
else:
super().action_page_up()
def action_scroll_home(self) -> None:
"""Scroll to the top of the data table."""
self._set_hover_cursor(False)
cursor_type = self.cursor_type
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
row_index, column_index = self.cursor_coordinate
self.cursor_coordinate = Coordinate(0, column_index)
self._scroll_cursor_into_view()
else:
super().action_scroll_home()
def action_scroll_end(self) -> None:
"""Scroll to the bottom of the data table."""
self._set_hover_cursor(False)
cursor_type = self.cursor_type
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
row_index, column_index = self.cursor_coordinate
self.cursor_coordinate = Coordinate(self.row_count - 1, column_index)
self._scroll_cursor_into_view()
else:
super().action_scroll_end()
def action_cursor_up(self) -> None:
self._set_hover_cursor(False)
cursor_type = self.cursor_type

View File

@@ -167,10 +167,36 @@ async def test_datatable_message_emission():
async def test_empty_table_interactions():
app = DataTableApp()
async with app.run_test() as pilot:
await pilot.press("enter", "up", "down", "left", "right")
await pilot.press(
"enter", "up", "down", "left", "right", "home", "end", "pagedown", "pageup"
)
assert app.message_names == []
async def test_cursor_movement_with_home_pagedown_etc():
app = DataTableApp()
async with app.run_test() as pilot:
table = app.query_one(DataTable)
table.add_columns("A", "B")
table.add_rows(ROWS)
await pilot.press("right", "pagedown")
await pilot.pause()
assert table.cursor_coordinate == Coordinate(2, 1)
await pilot.press("pageup")
await pilot.pause()
assert table.cursor_coordinate == Coordinate(0, 1)
await pilot.press("end")
await pilot.pause()
assert table.cursor_coordinate == Coordinate(2, 1)
await pilot.press("home")
await pilot.pause()
assert table.cursor_coordinate == Coordinate(0, 1)
async def test_add_rows():
app = DataTableApp()
async with app.run_test():