mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Some DataTable doc updates, rename Coord -> Coordinate and extract to module
This commit is contained in:
@@ -6,7 +6,7 @@ hide:
|
|||||||
|
|
||||||
# Roadmap
|
# Roadmap
|
||||||
|
|
||||||
We ([textualize.io](https://www.textualize.io/)) are actively building and maintaining Textual.
|
We ([textualize.io](https://www.textualize.io/)) are actively building and maintaining Textual.
|
||||||
|
|
||||||
We have many new features in the pipeline. This page will keep track of that work.
|
We have many new features in the pipeline. This page will keep track of that work.
|
||||||
|
|
||||||
@@ -18,24 +18,24 @@ High-level features we plan on implementing.
|
|||||||
* [ ] Integration with screen readers
|
* [ ] Integration with screen readers
|
||||||
* [x] Monochrome mode
|
* [x] Monochrome mode
|
||||||
* [ ] High contrast theme
|
* [ ] High contrast theme
|
||||||
* [ ] Color blind themes
|
* [ ] Color-blind themes
|
||||||
- [ ] Command interface
|
- [ ] Command interface
|
||||||
* [ ] Command menu
|
* [ ] Command menu
|
||||||
* [ ] Fuzzy search
|
* [ ] Fuzzy search
|
||||||
- [ ] Configuration (.toml based extensible configuration format)
|
- [ ] Configuration (.toml based extensible configuration format)
|
||||||
- [x] Console
|
- [x] Console
|
||||||
- [ ] Devtools
|
- [ ] Devtools
|
||||||
* [ ] Integrated log
|
* [ ] Integrated log
|
||||||
* [ ] DOM tree view
|
* [ ] DOM tree view
|
||||||
* [ ] REPL
|
* [ ] REPL
|
||||||
- [ ] Reactive state abstraction
|
- [ ] Reactive state abstraction
|
||||||
- [x] Themes
|
- [x] Themes
|
||||||
* [ ] Customize via config
|
* [ ] Customize via config
|
||||||
* [ ] Builtin theme editor
|
* [ ] Builtin theme editor
|
||||||
|
|
||||||
## Widgets
|
## 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] Buttons
|
||||||
* [x] Error / warning variants
|
* [x] Error / warning variants
|
||||||
@@ -44,8 +44,8 @@ Widgets are key to making user friendly interfaces. The builtin widgets should c
|
|||||||
- [ ] Content switcher
|
- [ ] Content switcher
|
||||||
- [x] DataTable
|
- [x] DataTable
|
||||||
* [x] Cell select
|
* [x] Cell select
|
||||||
* [ ] Row / Column select
|
* [x] Row / Column select
|
||||||
* [ ] API to update cells / rows
|
* [ ] API to update cells / rows
|
||||||
* [ ] Lazy loading API
|
* [ ] Lazy loading API
|
||||||
- [ ] Date picker
|
- [ ] Date picker
|
||||||
- [ ] Drop-down menus
|
- [ ] Drop-down menus
|
||||||
@@ -76,4 +76,3 @@ Widgets are key to making user friendly interfaces. The builtin widgets should c
|
|||||||
* [ ] Indentation guides
|
* [ ] Indentation guides
|
||||||
* [ ] Smart features for various languages
|
* [ ] Smart features for various languages
|
||||||
* [ ] Syntax highlighting
|
* [ ] Syntax highlighting
|
||||||
|
|
||||||
|
|||||||
@@ -22,18 +22,32 @@ The example below populates a table with CSV data.
|
|||||||
|
|
||||||
## Reactive Attributes
|
## Reactive Attributes
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|-----------------|---------|---------------|---------------------------------------------------------|
|
|-----------------|--------------|--------------------|---------------------------------------------------------|
|
||||||
| `show_header` | `bool` | `True` | Show the table header |
|
| `show_header` | `bool` | `True` | Show the table header |
|
||||||
| `fixed_rows` | `int` | `0` | Number of fixed rows (rows which do not scroll) |
|
| `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) |
|
| `fixed_columns` | `int` | `0` | Number of fixed columns (columns which do not scroll) |
|
||||||
| `zebra_stripes` | `bool` | `False` | Display alternating colors on rows |
|
| `zebra_stripes` | `bool` | `False` | Display alternating colors on rows |
|
||||||
| `header_height` | `int` | `1` | Height of header row |
|
| `header_height` | `int` | `1` | Height of header row |
|
||||||
| `show_cursor` | `bool` | `True` | Show the cursor |
|
| `show_cursor` | `bool` | `True` | Show the cursor |
|
||||||
| `cursor_type` | `str` | `"cell"` | One of `"cell"`, `"row"`, `"column"`, or `"none"` |
|
| `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 |
|
| `cursor_cell` | `Coordinate` | `Coordinate(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 |
|
| `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
|
## See Also
|
||||||
|
|
||||||
|
|||||||
@@ -1774,8 +1774,8 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
"""Get the widget under the given coordinates.
|
"""Get the widget under the given coordinates.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
x (int): X Coord.
|
x (int): X Coordinate.
|
||||||
y (int): Y Coord.
|
y (int): Y Coordinate.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple[Widget, Region]: The widget and the widget's screen region.
|
tuple[Widget, Region]: The widget and the widget's screen region.
|
||||||
|
|||||||
46
src/textual/coordinate.py
Normal file
46
src/textual/coordinate.py
Normal 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)
|
||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from itertools import chain, zip_longest
|
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
|
import rich.repr
|
||||||
from rich.console import RenderableType
|
from rich.console import RenderableType
|
||||||
@@ -14,6 +14,7 @@ from rich.text import Text, TextType
|
|||||||
|
|
||||||
from .. import events, messages
|
from .. import events, messages
|
||||||
from .._cache import LRUCache
|
from .._cache import LRUCache
|
||||||
|
from ..coordinate import Coordinate
|
||||||
from .._segment_tools import line_crop
|
from .._segment_tools import line_crop
|
||||||
from .._types import SegmentLines
|
from .._types import SegmentLines
|
||||||
from ..binding import Binding
|
from ..binding import Binding
|
||||||
@@ -78,49 +79,6 @@ class Row:
|
|||||||
cell_renderables: list[RenderableType] = field(default_factory=list)
|
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):
|
class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||||
DEFAULT_CSS = """
|
DEFAULT_CSS = """
|
||||||
App.-dark DataTable {
|
App.-dark DataTable {
|
||||||
@@ -199,10 +157,10 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
show_cursor = Reactive(True)
|
show_cursor = Reactive(True)
|
||||||
cursor_type = Reactive(CELL)
|
cursor_type = Reactive(CELL)
|
||||||
|
|
||||||
cursor_cell: Reactive[Coord] = Reactive(
|
cursor_cell: Reactive[Coordinate] = Reactive(
|
||||||
Coord(0, 0), repaint=False, always_update=True
|
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__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -263,11 +221,11 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
def cursor_column(self) -> int:
|
def cursor_column(self) -> int:
|
||||||
return self.cursor_cell.column
|
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.
|
"""Get the value from the cell at the given coordinate.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
coordinate (Coord): The coordinate to retrieve the value from.
|
coordinate (Coordinate): The coordinate to retrieve the value from.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
CellType: The value of the cell.
|
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:
|
def watch_zebra_stripes(self, zebra_stripes: bool) -> None:
|
||||||
self._clear_caches()
|
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(*old)
|
||||||
self.refresh_cell(*value)
|
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 old_coordinate != new_coordinate:
|
||||||
if self.cursor_type == "cell":
|
if self.cursor_type == "cell":
|
||||||
self.refresh_cell(*old_coordinate)
|
self.refresh_cell(*old_coordinate)
|
||||||
@@ -326,7 +286,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
self.refresh_column(old_coordinate.column)
|
self.refresh_column(old_coordinate.column)
|
||||||
self._highlight_column(new_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)
|
self.refresh_cell(*coordinate)
|
||||||
cell_value = self.get_cell_value(coordinate)
|
cell_value = self.get_cell_value(coordinate)
|
||||||
self.emit_no_wait(DataTable.CellHighlighted(self, 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.refresh_column(column_index)
|
||||||
self.emit_no_wait(DataTable.ColumnHighlighted(self, 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)
|
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, column = cursor_cell
|
||||||
row = clamp(row, 0, self.row_count - 1)
|
row = clamp(row, 0, self.row_count - 1)
|
||||||
column = clamp(column, self.fixed_columns, len(self.columns) - 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:
|
def watch_cursor_type(self, old: str, value: str) -> None:
|
||||||
self._set_hover_cursor(False)
|
self._set_hover_cursor(False)
|
||||||
@@ -637,8 +597,8 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
row_index: int,
|
row_index: int,
|
||||||
line_no: int,
|
line_no: int,
|
||||||
base_style: Style,
|
base_style: Style,
|
||||||
cursor_location: Coord,
|
cursor_location: Coordinate,
|
||||||
hover_location: Coord,
|
hover_location: Coordinate,
|
||||||
) -> tuple[SegmentLines, SegmentLines]:
|
) -> tuple[SegmentLines, SegmentLines]:
|
||||||
"""Render a row in to lines for each cell.
|
"""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
|
render_cell = self._render_cell
|
||||||
|
|
||||||
def _should_highlight(
|
def _should_highlight(
|
||||||
cursor_location: Coord,
|
cursor_location: Coordinate,
|
||||||
cell_location: Coord,
|
cell_location: Coordinate,
|
||||||
cursor_type: CursorType,
|
cursor_type: CursorType,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Determine whether we should highlight a cell given the location
|
"""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_style += Style.from_meta({"fixed": True})
|
||||||
fixed_row = []
|
fixed_row = []
|
||||||
for column in self.columns[: self.fixed_columns]:
|
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(
|
fixed_cell_lines = render_cell(
|
||||||
row_index,
|
row_index,
|
||||||
column.index,
|
column.index,
|
||||||
@@ -722,7 +682,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
|
|
||||||
scrollable_row = []
|
scrollable_row = []
|
||||||
for column in self.columns:
|
for column in self.columns:
|
||||||
cell_location = Coord(row_index, column.index)
|
cell_location = Coordinate(row_index, column.index)
|
||||||
cell_lines = render_cell(
|
cell_lines = render_cell(
|
||||||
row_index,
|
row_index,
|
||||||
column.index,
|
column.index,
|
||||||
@@ -827,7 +787,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
meta = event.style.meta
|
meta = event.style.meta
|
||||||
if meta and self.show_cursor and self.cursor_type != "none":
|
if meta and self.show_cursor and self.cursor_type != "none":
|
||||||
try:
|
try:
|
||||||
self.hover_cell = Coord(meta["row"], meta["column"])
|
self.hover_cell = Coordinate(meta["row"], meta["column"])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -872,7 +832,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
self._emit_selected_message()
|
self._emit_selected_message()
|
||||||
meta = self.get_style_at(event.x, event.y).meta
|
meta = self.get_style_at(event.x, event.y).meta
|
||||||
if 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)
|
self._scroll_cursor_into_view(animate=True)
|
||||||
event.stop()
|
event.stop()
|
||||||
|
|
||||||
@@ -942,11 +902,11 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
Attributes:
|
Attributes:
|
||||||
sender (DataTable): The DataTable the cell was highlighted in.
|
sender (DataTable): The DataTable the cell was highlighted in.
|
||||||
value (CellType): The value in the highlighted cell.
|
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__(
|
def __init__(
|
||||||
self, sender: DataTable, value: CellType, coordinate: Coord
|
self, sender: DataTable, value: CellType, coordinate: Coordinate
|
||||||
) -> None:
|
) -> None:
|
||||||
self.value = value
|
self.value = value
|
||||||
self.coordinate = coordinate
|
self.coordinate = coordinate
|
||||||
@@ -964,11 +924,11 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
Attributes:
|
Attributes:
|
||||||
sender (DataTable): The DataTable the cell was selected in.
|
sender (DataTable): The DataTable the cell was selected in.
|
||||||
value (CellType): The value in the cell that was selected.
|
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__(
|
def __init__(
|
||||||
self, sender: DataTable, value: CellType, coordinate: Coord
|
self, sender: DataTable, value: CellType, coordinate: Coordinate
|
||||||
) -> None:
|
) -> None:
|
||||||
self.value = value
|
self.value = value
|
||||||
self.coordinate = coordinate
|
self.coordinate = coordinate
|
||||||
|
|||||||
Reference in New Issue
Block a user