Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure styles applied in correct order in DataTable #2272

Merged
merged 3 commits into from
Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added `DataTable.remove_row` method https://github.com/Textualize/textual/pull/2253
- `Widget.scroll_to_center` now scrolls the widget to the center of the screen https://github.com/Textualize/textual/pull/2255

### 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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥪

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