mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Small refactor of DataTable, additional testing around message emission
This commit is contained in:
@@ -336,18 +336,12 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
|
||||
def watch_cursor_type(self, old: str, new: str) -> None:
|
||||
self._set_hover_cursor(False)
|
||||
row_index, column_index = self.cursor_cell
|
||||
|
||||
# Apply the highlighting to the newly relevant cells
|
||||
if new == "cell":
|
||||
self._highlight_cell(self.cursor_cell)
|
||||
elif new == "row":
|
||||
self._highlight_row(row_index)
|
||||
elif new == "column":
|
||||
self._highlight_column(column_index)
|
||||
if self.show_cursor:
|
||||
self._highlight_cursor()
|
||||
|
||||
# Refresh cells that were previously impacted by the cursor
|
||||
# but may no longer be.
|
||||
row_index, column_index = self.cursor_cell
|
||||
if old == "cell":
|
||||
self.refresh_cell(row_index, column_index)
|
||||
elif old == "row":
|
||||
@@ -357,6 +351,17 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
|
||||
self._scroll_cursor_into_view()
|
||||
|
||||
def _highlight_cursor(self) -> None:
|
||||
row_index, column_index = self.cursor_cell
|
||||
cursor_type = self.cursor_type
|
||||
# Apply the highlighting to the newly relevant cells
|
||||
if cursor_type == "cell":
|
||||
self._highlight_cell(self.cursor_cell)
|
||||
elif cursor_type == "row":
|
||||
self._highlight_row(row_index)
|
||||
elif cursor_type == "column":
|
||||
self._highlight_column(column_index)
|
||||
|
||||
def _update_dimensions(self, new_rows: Iterable[int]) -> None:
|
||||
"""Called to recalculate the virtual (scrollable) size."""
|
||||
for row_index in new_rows:
|
||||
@@ -492,6 +497,15 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
self.cursor_cell = self.cursor_cell
|
||||
self.check_idle()
|
||||
|
||||
# If a position has opened for the cursor to appear, where it previously
|
||||
# could not (e.g. when there's no data in the table), then a highlighted
|
||||
# event is emitted, since there's now a highlighted cell when there wasn't
|
||||
# before.
|
||||
cell_now_available = self.row_count == 1 and len(self.columns) > 0
|
||||
visible_cursor = self.show_cursor and self.cursor_type != "none"
|
||||
if cell_now_available and visible_cursor:
|
||||
self._highlight_cursor()
|
||||
|
||||
def add_rows(self, rows: Iterable[Iterable[CellType]]) -> None:
|
||||
"""Add a number of rows.
|
||||
|
||||
|
||||
@@ -1,11 +1,124 @@
|
||||
from textual.app import App
|
||||
from textual.coordinate import Coordinate
|
||||
from textual.message import Message
|
||||
from textual.widgets import DataTable
|
||||
|
||||
|
||||
class DataTableApp(App):
|
||||
messages = []
|
||||
messages_to_record = {
|
||||
"CellHighlighted",
|
||||
"CellSelected",
|
||||
"RowHighlighted",
|
||||
"RowSelected",
|
||||
"ColumnHighlighted",
|
||||
"ColumnSelected",
|
||||
}
|
||||
|
||||
def compose(self):
|
||||
yield DataTable()
|
||||
table = DataTable()
|
||||
table.focus()
|
||||
yield table
|
||||
|
||||
def record_data_table_event(self, message: Message) -> None:
|
||||
name = message.__class__.__name__
|
||||
if name in self.messages_to_record:
|
||||
self.messages.append(name)
|
||||
|
||||
async def _on_message(self, message: Message) -> None:
|
||||
await super()._on_message(message)
|
||||
self.record_data_table_event(message)
|
||||
|
||||
|
||||
async def test_datatable_message_emission():
|
||||
app = DataTableApp()
|
||||
messages = app.messages
|
||||
expected_messages = []
|
||||
async with app.run_test() as pilot:
|
||||
table = app.query_one(DataTable)
|
||||
|
||||
assert messages == expected_messages
|
||||
|
||||
table.add_columns("Column0", "Column1")
|
||||
table.add_rows([["0/0", "0/1"], ["1/0", "1/1"], ["2/0", "2/1"]])
|
||||
|
||||
# A CellHighlighted is emitted because there were no rows (and
|
||||
# therefore no highlighted cells), but then a row was added, and
|
||||
# so the cell at (0, 0) became highlighted.
|
||||
expected_messages.append("CellHighlighted")
|
||||
await pilot.pause(1 / 100)
|
||||
assert messages == expected_messages
|
||||
|
||||
# Pressing Enter when the cursor is on a cell emits a CellSelected
|
||||
await pilot.press("enter")
|
||||
expected_messages.append("CellSelected")
|
||||
await pilot.pause(1 / 100)
|
||||
assert messages == expected_messages
|
||||
|
||||
# Moving the cursor left and up when the cursor is at origin
|
||||
# emits no events, since the cursor doesn't move at all.
|
||||
await pilot.press("left", "up")
|
||||
await pilot.pause(1 / 100)
|
||||
assert messages == expected_messages
|
||||
|
||||
# ROW CURSOR
|
||||
# Switch over to the row cursor... should emit a `RowHighlighted`
|
||||
table.cursor_type = "row"
|
||||
expected_messages.append("RowHighlighted")
|
||||
await pilot.pause(1 / 100)
|
||||
assert messages == expected_messages
|
||||
|
||||
# Select the row...
|
||||
await pilot.press("enter")
|
||||
expected_messages.append("RowSelected")
|
||||
await pilot.pause(1 / 100)
|
||||
assert messages == expected_messages
|
||||
|
||||
# COLUMN CURSOR
|
||||
# Switching to the column cursor emits a `ColumnHighlighted`
|
||||
table.cursor_type = "column"
|
||||
expected_messages.append("ColumnHighlighted")
|
||||
await pilot.pause(1 / 100)
|
||||
assert messages == expected_messages
|
||||
|
||||
# Select the column...
|
||||
await pilot.press("enter")
|
||||
expected_messages.append("ColumnSelected")
|
||||
await pilot.pause(1 / 100)
|
||||
assert messages == expected_messages
|
||||
|
||||
# NONE CURSOR
|
||||
# No messages get emitted at all...
|
||||
table.cursor_type = "none"
|
||||
await pilot.press("up", "down", "left", "right", "enter")
|
||||
await pilot.pause(2 / 100)
|
||||
# No new messages since cursor not visible
|
||||
assert messages == expected_messages
|
||||
|
||||
# Edge case - if show_cursor is False, and the cursor type
|
||||
# is changed back to a visible type, then no messages should
|
||||
# be emitted since the cursor is still not visible.
|
||||
table.show_cursor = False
|
||||
table.cursor_type = "cell"
|
||||
await pilot.press("up", "down", "left", "right", "enter")
|
||||
await pilot.pause(2 / 100)
|
||||
# No new messages since show_cursor = False
|
||||
assert messages == expected_messages
|
||||
|
||||
# Now when show_cursor is set back to True, the appropriate
|
||||
# message should be emitted for highlighting the cell.
|
||||
table.show_cursor = True
|
||||
expected_messages.append("CellHighlighted")
|
||||
await pilot.pause(1 / 100)
|
||||
assert messages == expected_messages
|
||||
|
||||
# Likewise, if the cursor_type is "none", and we change the
|
||||
# show_cursor to True, then no events should be raised since
|
||||
# the cursor is still not visible to the user.
|
||||
table.cursor_type = "none"
|
||||
await pilot.press("up", "down", "left", "right", "enter")
|
||||
await pilot.pause(2 / 100)
|
||||
assert messages == expected_messages
|
||||
|
||||
|
||||
async def test_clear():
|
||||
|
||||
Reference in New Issue
Block a user