diff --git a/CHANGELOG.md b/CHANGELOG.md index ea6ad8631b..322a868b2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - The devtools console now confirms when CSS files have been successfully loaded after a previous error https://github.com/Textualize/textual/pull/2716 +- Added `cursor_foreground_priority` and `cursor_background_priority` to `DataTable` https://github.com/Textualize/textual/pull/2736 ### Fixed diff --git a/src/textual/widgets/_data_table.py b/src/textual/widgets/_data_table.py index 0f40552be0..840cb78928 100644 --- a/src/textual/widgets/_data_table.py +++ b/src/textual/widgets/_data_table.py @@ -579,6 +579,8 @@ def __init__( zebra_stripes: bool = False, header_height: int = 1, show_cursor: bool = True, + cursor_foreground_priority: Literal["renderable", "css"] = "css", + cursor_background_priority: Literal["renderable", "css"] = "renderable", name: str | None = None, id: str | None = None, classes: str | None = None, @@ -660,6 +662,12 @@ def __init__( """Apply zebra effect on row backgrounds (light, dark, light, dark, ...).""" self.show_cursor = show_cursor """Show/hide both the keyboard and hover cursor.""" + self.cursor_foreground_priority = cursor_foreground_priority + """Should we prioritize the cursor component class CSS foreground or the renderable foreground + in the event where a cell contains a renderable with a foreground color.""" + self.cursor_background_priority = cursor_background_priority + """Should we prioritize the cursor component class CSS background or the renderable background + in the event where a cell contains a renderable with a background color.""" @property def hover_row(self) -> int: @@ -1651,38 +1659,42 @@ def _render_cell( else: cell = row_cells[column_index] - get_component = self.get_component_styles + get_component = self.get_component_rich_style 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 + component_style += get_component("datatable--hover") 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 + component_style += get_component("datatable--header-hover") if cursor and show_cursor: - cursor_style = get_component("datatable--cursor").rich_style + cursor_style = get_component("datatable--cursor") component_style += cursor_style if is_header_cell or is_row_label_cell: - component_style += get_component( - "datatable--header-cursor" - ).rich_style + component_style += get_component("datatable--header-cursor") elif is_fixed_style_cell: - component_style += get_component( - "datatable--fixed-cursor" - ).rich_style + component_style += get_component("datatable--fixed-cursor") + + post_foreground = ( + Style.from_color(color=component_style.color) + if self.cursor_foreground_priority == "css" + else Style.null() + ) + post_background = ( + Style.from_color(bgcolor=component_style.bgcolor) + if self.cursor_background_priority == "css" + else Style.null() + ) lines = self.app.console.render_lines( Styled( Padding(cell, (0, 1)), - pre_style=base_style, - post_style=component_style, + pre_style=base_style + component_style, + post_style=post_foreground + post_background, ), self.app.console.options.update_dimensions(width, height), ) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 7252c099af..4f50440653 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -13568,136 +13568,137 @@ font-weight: 700; } - .terminal-490238722-matrix { + .terminal-1146140386-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-490238722-title { + .terminal-1146140386-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-490238722-r1 { fill: #dde6ed;font-weight: bold } - .terminal-490238722-r2 { fill: #dde6ed } - .terminal-490238722-r3 { fill: #c5c8c6 } - .terminal-490238722-r4 { fill: #0a180e;font-weight: bold;font-style: italic; } - .terminal-490238722-r5 { fill: #e1e2e3 } - .terminal-490238722-r6 { fill: #e1e1e1 } - .terminal-490238722-r7 { fill: #cc555a } + .terminal-1146140386-r1 { fill: #e1e1e1 } + .terminal-1146140386-r2 { fill: #c5c8c6 } + .terminal-1146140386-r3 { fill: #dde6ed;font-weight: bold } + .terminal-1146140386-r4 { fill: #dde6ed } + .terminal-1146140386-r5 { fill: #fea62b;font-weight: bold;font-style: italic; } + .terminal-1146140386-r6 { fill: #e1e2e3 } + .terminal-1146140386-r7 { fill: #cc555a } + .terminal-1146140386-r8 { fill: #cc555a;font-weight: bold;font-style: italic; } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - DataTableCursorStyles + DataTableCursorStyles - - - -  Movies          -  Severance       - Foundation - Dark - The Boys - The Last of Us - Lost in Space - Altered Carbon - - - - - - - - - - - - - - - + + + + Foreground is 'css', background is 'css': +  Movies      +  Severance   + Foundation + Dark + + Foreground is 'css', background is 'renderable': +  Movies      + Severance + Foundation + Dark + + Foreground is 'renderable', background is 'renderable': +  Movies      + Severance + Foundation + Dark + + Foreground is 'renderable', background is 'css': +  Movies      + Severance + Foundation + Dark diff --git a/tests/snapshot_tests/snapshot_apps/data_table_style_order.py b/tests/snapshot_tests/snapshot_apps/data_table_style_order.py index 06f9bf2ebc..40844053da 100644 --- a/tests/snapshot_tests/snapshot_apps/data_table_style_order.py +++ b/tests/snapshot_tests/snapshot_apps/data_table_style_order.py @@ -1,17 +1,26 @@ +from typing_extensions import Literal + from textual.app import App, ComposeResult -from textual.widgets import DataTable +from textual.widgets import DataTable, Label data = [ "Severance", "Foundation", "Dark", - "The Boys", - "The Last of Us", - "Lost in Space", - "Altered Carbon", ] +def make_datatable(foreground_priority: Literal["css", "renderable"], + background_priority: Literal["css", "renderable"]) -> DataTable: + table = DataTable(cursor_foreground_priority=foreground_priority, + cursor_background_priority=background_priority) + table.zebra_stripes = True + table.add_column("Movies") + for row in data: + table.add_row(f"[red on blue]{row}") + return table + + class DataTableCursorStyles(App): """Regression test snapshot app which ensures that styles are layered on top of each other correctly in the DataTable. @@ -20,18 +29,28 @@ class DataTableCursorStyles(App): on top.""" CSS = """ + DataTable {margin-bottom: 1;} DataTable > .datatable--cursor { - color: $text; + color: $secondary; background: $success; text-style: bold italic; } """ def compose(self) -> ComposeResult: - table = DataTable() - table.zebra_stripes = True - table.add_column("Movies") - for row in data: - table.add_row(f"[red]{row}") - table.focus() - yield table + priorities: list[tuple[Literal["css", "renderable"], Literal["css", "renderable"]]] = [ + ("css", "css"), + ("css", "renderable"), + ("renderable", "renderable"), + ("renderable", "css"), + ] + for foreground, background in priorities: + yield Label(f"Foreground is {foreground!r}, background is {background!r}:") + table = make_datatable(foreground, background) + yield table + + +app = DataTableCursorStyles() + +if __name__ == '__main__': + app.run()