Skip to content

Commit

Permalink
Ensure styles applied in correct order in DataTable (#2272)
Browse files Browse the repository at this point in the history
* Ensure styles applied in correct order in DataTable

* Add snapshot regression test for datatable style ordering

* Update CHANGELOG
  • Loading branch information
darrenburns authored Apr 12, 2023
1 parent 03fa641 commit 6369c37
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 24 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added `TabActivated` message to `TabbedContent` https://github.com/Textualize/textual/pull/2260


### Fixed

- Fixed order styles are applied in DataTable - allows combining of renderable styles and component classes https://github.com/Textualize/textual/pull/2272

## [0.19.1] - 2023-04-10

### Fixed
Expand Down
55 changes: 55 additions & 0 deletions src/textual/renderables/styled.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from typing import TYPE_CHECKING

from rich.measure import Measurement
from rich.segment import Segment

if TYPE_CHECKING:
from rich.console import (
Console,
ConsoleOptions,
RenderableType,
RenderResult,
StyleType,
)


class Styled:
"""A renderable which allows you to apply a style before and after another renderable.
This can be used to layer styles on top of each other, like a style sandwich. This is used,
for example, in the DataTable to layer default CSS styles + user renderables (e.g. Text objects
stored in the cells of the table) + CSS component styles on top of each other."""

def __init__(
self,
renderable: "RenderableType",
pre_style: "StyleType",
post_style: "StyleType",
) -> None:
"""Construct a Styled.
Args:
renderable (RenderableType): Any renderable.
pre_style (StyleType): A style to apply across the entire renderable.
Will be applied before the styles from the renderable itself.
post_style (StyleType): A style to apply across the entire renderable.
Will be applied after the styles from the renderable itself.
"""
self.renderable = renderable
self.pre_style = pre_style
self.post_style = post_style

def __rich_console__(
self, console: "Console", options: "ConsoleOptions"
) -> "RenderResult":
pre_style = console.get_style(self.pre_style)
post_style = console.get_style(self.post_style)
rendered_segments = console.render(self.renderable, options)
segments = Segment.apply_style(
rendered_segments, style=pre_style, post_style=post_style
)
return segments

def __rich_measure__(
self, console: "Console", options: "ConsoleOptions"
) -> Measurement:
return Measurement.get(console, options, self.renderable)
61 changes: 37 additions & 24 deletions src/textual/widgets/_data_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from ..message import Message
from ..reactive import Reactive
from ..render import measure
from ..renderables.styled import Styled
from ..scroll_view import ScrollView
from ..strip import Strip
from ..widget import PseudoClasses
Expand Down Expand Up @@ -1554,7 +1555,7 @@ def _render_cell(
self,
row_index: int,
column_index: int,
style: Style,
base_style: Style,
width: int,
cursor: bool = False,
hover: bool = False,
Expand All @@ -1564,7 +1565,7 @@ def _render_cell(
Args:
row_index: Index of the row.
column_index: Index of the column.
style: Style to apply.
base_style: Style to apply.
width: Width of the cell.
cursor: Is this cell affected by cursor highlighting?
hover: Is this cell affected by hover cursor highlighting?
Expand All @@ -1581,24 +1582,6 @@ def _render_cell(
and (row_index < self.fixed_rows or column_index < self.fixed_columns)
)

get_component = self.get_component_styles
show_cursor = self.show_cursor

if hover and show_cursor and self._show_hover_cursor:
style += get_component("datatable--hover").rich_style
if is_header_cell or is_row_label_cell:
# Apply subtle variation in style for the header/label (blue background by
# default) rows and columns affected by the cursor, to ensure we can
# still differentiate between the labels and the data.
style += get_component("datatable--header-hover").rich_style

if cursor and show_cursor:
style += get_component("datatable--cursor").rich_style
if is_header_cell or is_row_label_cell:
style += get_component("datatable--header-cursor").rich_style
elif is_fixed_style_cell:
style += get_component("datatable--fixed-cursor").rich_style

if is_header_cell:
row_key = self._header_row_key
else:
Expand All @@ -1608,15 +1591,15 @@ def _render_cell(
cell_cache_key = (
row_key,
column_key,
style,
base_style,
cursor,
hover,
self._update_count,
self._pseudo_class_state,
)

if cell_cache_key not in self._cell_render_cache:
style += Style.from_meta({"row": row_index, "column": column_index})
base_style += Style.from_meta({"row": row_index, "column": column_index})
height = self.header_height if is_header_cell else self.rows[row_key].height
row_label, row_cells = self._get_row_renderables(row_index)

Expand All @@ -1625,10 +1608,40 @@ def _render_cell(
else:
cell = row_cells[column_index]

get_component = self.get_component_styles
show_cursor = self.show_cursor

component_style = Style()

if hover and show_cursor and self._show_hover_cursor:
component_style += get_component("datatable--hover").rich_style
if is_header_cell or is_row_label_cell:
# Apply subtle variation in style for the header/label (blue background by
# default) rows and columns affected by the cursor, to ensure we can
# still differentiate between the labels and the data.
component_style += get_component(
"datatable--header-hover"
).rich_style

if cursor and show_cursor:
cursor_style = get_component("datatable--cursor").rich_style
component_style += cursor_style
if is_header_cell or is_row_label_cell:
component_style += get_component(
"datatable--header-cursor"
).rich_style
elif is_fixed_style_cell:
component_style += get_component(
"datatable--fixed-cursor"
).rich_style

lines = self.app.console.render_lines(
Padding(cell, (0, 1)),
Styled(
Padding(cell, (0, 1)),
pre_style=base_style,
post_style=component_style,
),
self.app.console.options.update_dimensions(width, height),
style=style,
)

self._cell_render_cache[cell_cache_key] = lines
Expand Down
Loading

0 comments on commit 6369c37

Please sign in to comment.