Some DataTable doc updates, rename Coord -> Coordinate and extract to module

This commit is contained in:
Darren Burns
2023-01-17 11:06:21 +00:00
parent f327f1d2af
commit 23eb13d12d
5 changed files with 110 additions and 91 deletions

View File

@@ -18,7 +18,7 @@ High-level features we plan on implementing.
* [ ] Integration with screen readers
* [x] Monochrome mode
* [ ] High contrast theme
* [ ] Color blind themes
* [ ] Color-blind themes
- [ ] Command interface
* [ ] Command menu
* [ ] Fuzzy search
@@ -35,7 +35,7 @@ High-level features we plan on implementing.
## Widgets
Widgets are key to making user friendly interfaces. The builtin widgets should cover many common (and some uncommon) use-cases. The following is a list of the widgets we have built or are planning to build.
Widgets are key to making user-friendly interfaces. The builtin widgets should cover many common (and some uncommon) use-cases. The following is a list of the widgets we have built or are planning to build.
- [x] Buttons
* [x] Error / warning variants
@@ -44,7 +44,7 @@ Widgets are key to making user friendly interfaces. The builtin widgets should c
- [ ] Content switcher
- [x] DataTable
* [x] Cell select
* [ ] Row / Column select
* [x] Row / Column select
* [ ] API to update cells / rows
* [ ] Lazy loading API
- [ ] Date picker
@@ -76,4 +76,3 @@ Widgets are key to making user friendly interfaces. The builtin widgets should c
* [ ] Indentation guides
* [ ] Smart features for various languages
* [ ] Syntax highlighting

View File

@@ -23,7 +23,7 @@ The example below populates a table with CSV data.
## Reactive Attributes
| Name | Type | Default | Description |
|-----------------|---------|---------------|---------------------------------------------------------|
|-----------------|--------------|--------------------|---------------------------------------------------------|
| `show_header` | `bool` | `True` | Show the table header |
| `fixed_rows` | `int` | `0` | Number of fixed rows (rows which do not scroll) |
| `fixed_columns` | `int` | `0` | Number of fixed columns (columns which do not scroll) |
@@ -31,9 +31,23 @@ The example below populates a table with CSV data.
| `header_height` | `int` | `1` | Height of header row |
| `show_cursor` | `bool` | `True` | Show the cursor |
| `cursor_type` | `str` | `"cell"` | One of `"cell"`, `"row"`, `"column"`, or `"none"` |
| `cursor_cell` | `Coord` | `Coord(0, 0)` | The coordinates of the cell the cursor is currently on |
| `hover_cell` | `Coord` | `Coord(0, 0)` | The coordinates of the cell the _mouse_ cursor is above |
| `cursor_cell` | `Coordinate` | `Coordinate(0, 0)` | The coordinates of the cell the cursor is currently on |
| `hover_cell` | `Coordinate` | `Coordinate(0, 0)` | The coordinates of the cell the _mouse_ cursor is above |
## Messages
### CellHighlighted
The `DataTable.CellHighlighted` message is emitted by the `DataTable` widget when the cursor moves
to highlight a new cell. It's also emitted when the cell cursor is re-enabled (by setting `show_cursor=True`),
and when the cursor type is changed to `"cell"`.
#### Attributes
| Attribute | Type | Description |
|--------------|----------------------------------------------|----------------------------------|
| `value` | `CellType` | The value contained in the cell. |
| `coordinate` | [Coordinate][textual.coordinates.Coordinate] | The coordinate of the cell. |
## See Also

View File

@@ -1774,8 +1774,8 @@ class App(Generic[ReturnType], DOMNode):
"""Get the widget under the given coordinates.
Args:
x (int): X Coord.
y (int): Y Coord.
x (int): X Coordinate.
y (int): Y Coordinate.
Returns:
tuple[Widget, Region]: The widget and the widget's screen region.

46
src/textual/coordinate.py Normal file
View File

@@ -0,0 +1,46 @@
from __future__ import annotations
from typing import NamedTuple
class Coordinate(NamedTuple):
"""An object representing a row/column coordinate."""
row: int
column: int
def left(self) -> Coordinate:
"""Get coordinate to the left.
Returns:
Coordinate: The coordinate.
"""
row, column = self
return Coordinate(row, column - 1)
def right(self) -> Coordinate:
"""Get coordinate to the right.
Returns:
Coordinate: The coordinate.
"""
row, column = self
return Coordinate(row, column + 1)
def up(self) -> Coordinate:
"""Get coordinate above.
Returns:
Coordinate: The coordinate.
"""
row, column = self
return Coordinate(row - 1, column)
def down(self) -> Coordinate:
"""Get coordinate below.
Returns:
Coordinate: The coordinate.
"""
row, column = self
return Coordinate(row + 1, column)

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from dataclasses import dataclass, field
from itertools import chain, zip_longest
from typing import ClassVar, Generic, Iterable, NamedTuple, TypeVar, cast
from typing import ClassVar, Generic, Iterable, TypeVar, cast
import rich.repr
from rich.console import RenderableType
@@ -14,6 +14,7 @@ from rich.text import Text, TextType
from .. import events, messages
from .._cache import LRUCache
from ..coordinate import Coordinate
from .._segment_tools import line_crop
from .._types import SegmentLines
from ..binding import Binding
@@ -78,49 +79,6 @@ class Row:
cell_renderables: list[RenderableType] = field(default_factory=list)
class Coord(NamedTuple):
"""An object to represent the coordinate of a cell within the data table."""
row: int
column: int
def left(self) -> Coord:
"""Get coordinate to the left.
Returns:
Coord: The coordinate.
"""
row, column = self
return Coord(row, column - 1)
def right(self) -> Coord:
"""Get coordinate to the right.
Returns:
Coord: The coordinate.
"""
row, column = self
return Coord(row, column + 1)
def up(self) -> Coord:
"""Get coordinate above.
Returns:
Coord: The coordinate.
"""
row, column = self
return Coord(row - 1, column)
def down(self) -> Coord:
"""Get coordinate below.
Returns:
Coord: The coordinate.
"""
row, column = self
return Coord(row + 1, column)
class DataTable(ScrollView, Generic[CellType], can_focus=True):
DEFAULT_CSS = """
App.-dark DataTable {
@@ -199,10 +157,10 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
show_cursor = Reactive(True)
cursor_type = Reactive(CELL)
cursor_cell: Reactive[Coord] = Reactive(
Coord(0, 0), repaint=False, always_update=True
cursor_cell: Reactive[Coordinate] = Reactive(
Coordinate(0, 0), repaint=False, always_update=True
)
hover_cell: Reactive[Coord] = Reactive(Coord(0, 0), repaint=False)
hover_cell: Reactive[Coordinate] = Reactive(Coordinate(0, 0), repaint=False)
def __init__(
self,
@@ -263,11 +221,11 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
def cursor_column(self) -> int:
return self.cursor_cell.column
def get_cell_value(self, coordinate: Coord) -> CellType:
def get_cell_value(self, coordinate: Coordinate) -> CellType:
"""Get the value from the cell at the given coordinate.
Args:
coordinate (Coord): The coordinate to retrieve the value from.
coordinate (Coordinate): The coordinate to retrieve the value from.
Returns:
CellType: The value of the cell.
@@ -310,11 +268,13 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
def watch_zebra_stripes(self, zebra_stripes: bool) -> None:
self._clear_caches()
def watch_hover_cell(self, old: Coord, value: Coord) -> None:
def watch_hover_cell(self, old: Coordinate, value: Coordinate) -> None:
self.refresh_cell(*old)
self.refresh_cell(*value)
def watch_cursor_cell(self, old_coordinate: Coord, new_coordinate: Coord) -> None:
def watch_cursor_cell(
self, old_coordinate: Coordinate, new_coordinate: Coordinate
) -> None:
if old_coordinate != new_coordinate:
if self.cursor_type == "cell":
self.refresh_cell(*old_coordinate)
@@ -326,7 +286,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
self.refresh_column(old_coordinate.column)
self._highlight_column(new_coordinate.column)
def _highlight_cell(self, coordinate: Coord) -> None:
def _highlight_cell(self, coordinate: Coordinate) -> None:
self.refresh_cell(*coordinate)
cell_value = self.get_cell_value(coordinate)
self.emit_no_wait(DataTable.CellHighlighted(self, cell_value, coordinate))
@@ -339,14 +299,14 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
self.refresh_column(column_index)
self.emit_no_wait(DataTable.ColumnHighlighted(self, column_index))
def validate_cursor_cell(self, value: Coord) -> Coord:
def validate_cursor_cell(self, value: Coordinate) -> Coordinate:
return self._clamp_cursor_cell(value)
def _clamp_cursor_cell(self, cursor_cell: Coord) -> Coord:
def _clamp_cursor_cell(self, cursor_cell: Coordinate) -> Coordinate:
row, column = cursor_cell
row = clamp(row, 0, self.row_count - 1)
column = clamp(column, self.fixed_columns, len(self.columns) - 1)
return Coord(row, column)
return Coordinate(row, column)
def watch_cursor_type(self, old: str, value: str) -> None:
self._set_hover_cursor(False)
@@ -637,8 +597,8 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
row_index: int,
line_no: int,
base_style: Style,
cursor_location: Coord,
hover_location: Coord,
cursor_location: Coordinate,
hover_location: Coordinate,
) -> tuple[SegmentLines, SegmentLines]:
"""Render a row in to lines for each cell.
@@ -669,8 +629,8 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
render_cell = self._render_cell
def _should_highlight(
cursor_location: Coord,
cell_location: Coord,
cursor_location: Coordinate,
cell_location: Coordinate,
cursor_type: CursorType,
) -> bool:
"""Determine whether we should highlight a cell given the location
@@ -694,7 +654,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
fixed_style += Style.from_meta({"fixed": True})
fixed_row = []
for column in self.columns[: self.fixed_columns]:
cell_location = Coord(row_index, column.index)
cell_location = Coordinate(row_index, column.index)
fixed_cell_lines = render_cell(
row_index,
column.index,
@@ -722,7 +682,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
scrollable_row = []
for column in self.columns:
cell_location = Coord(row_index, column.index)
cell_location = Coordinate(row_index, column.index)
cell_lines = render_cell(
row_index,
column.index,
@@ -827,7 +787,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
meta = event.style.meta
if meta and self.show_cursor and self.cursor_type != "none":
try:
self.hover_cell = Coord(meta["row"], meta["column"])
self.hover_cell = Coordinate(meta["row"], meta["column"])
except KeyError:
pass
@@ -872,7 +832,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
self._emit_selected_message()
meta = self.get_style_at(event.x, event.y).meta
if meta:
self.cursor_cell = Coord(meta["row"], meta["column"])
self.cursor_cell = Coordinate(meta["row"], meta["column"])
self._scroll_cursor_into_view(animate=True)
event.stop()
@@ -942,11 +902,11 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
Attributes:
sender (DataTable): The DataTable the cell was highlighted in.
value (CellType): The value in the highlighted cell.
coordinate (Coord): The coordinate of the highlighted cell.
coordinate (Coordinate): The coordinate of the highlighted cell.
"""
def __init__(
self, sender: DataTable, value: CellType, coordinate: Coord
self, sender: DataTable, value: CellType, coordinate: Coordinate
) -> None:
self.value = value
self.coordinate = coordinate
@@ -964,11 +924,11 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
Attributes:
sender (DataTable): The DataTable the cell was selected in.
value (CellType): The value in the cell that was selected.
coordinate (Coord): The coordinate of the cell that was selected.
coordinate (Coordinate): The coordinate of the cell that was selected.
"""
def __init__(
self, sender: DataTable, value: CellType, coordinate: Coord
self, sender: DataTable, value: CellType, coordinate: Coordinate
) -> None:
self.value = value
self.coordinate = coordinate