From 014f6a4fbf77169966b64bad79189aeeb29d0f70 Mon Sep 17 00:00:00 2001 From: thunderballfists <130205658+thunderballfists@users.noreply.github.com> Date: Wed, 30 Sep 2026 18:03:24 -0500 Subject: [PATCH] Preserve control backgrounds in focus tints --- CHANGELOG.md | 1 + docs/controls.md | 2 +- docs/project-runtime.md | 15 +++++- tests/test_styles.py | 52 +++++++++++++++++++ .../darwin/showcase-first-modal-80x24.svg | 6 +-- .../darwin/showcase-reopened-modal-80x24.svg | 6 +-- .../darwin/showcase-shell-120x50.svg | 4 +- .../darwin/showcase-shell-80x24.svg | 4 +- .../linux/showcase-first-modal-80x24.svg | 6 +-- .../linux/showcase-reopened-modal-80x24.svg | 6 +-- .../linux/showcase-shell-120x50.svg | 4 +- .../linux/showcase-shell-80x24.svg | 4 +- textui/presets.py | 5 +- 13 files changed, 91 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b35905..15fea8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to TextUI are documented here. ### Fixed +- Focus cues now blend over native and author backgrounds instead of replacing them, so variant buttons retain their colors. Compact inputs, selects, and text areas also preserve their backgrounds; tint strengths remain 15% by default and 25% for compact editable controls. - Clicking a data-table column heading now sorts once per click. The table's click and mouse-move handlers also called `super()`, which Textual had already run, so every heading click was delivered twice and the ascending/descending toggle always ended on descending. A column-resize press no longer sorts either, and no longer swallows the next heading click when the drag is released. - Controller resize hooks now receive terminal dimensions even when the active screen has padding or a border. - Declared autofocus now waits for hidden ancestors to be displayed and runs again when controls are revealed after startup. Disabled controls and background screens do not take focus; anonymous modal autofocus registrations are released on dismissal. diff --git a/docs/controls.md b/docs/controls.md index 3473132..7150a35 100644 --- a/docs/controls.md +++ b/docs/controls.md @@ -27,7 +27,7 @@ Static tab views use native `TabbedContent`: ``` -Each pane needs an ID and title. `initial` must name one of those panes; without it, Textual selects the first. The `tab-activated` event provides `context.event.pane`. Tabs respond to native mouse and keyboard input. Add `autofocus="true"` to a focusable control when it should receive keyboard input after the document or a modal opens, or after a hidden control or its parent is shown with `display=True`. Focus waits for layout, skips disabled and hidden controls, and applies only to the active screen. In an inactive tab, autofocus waits for that tab to activate and does not override `initial`. TextUI gives focused native controls an accent background tint by default; application TCSS may override it. The [controls example](../examples/controls/app.ui) shows all four controls in a runnable project. +Each pane needs an ID and title. `initial` must name one of those panes; without it, Textual selects the first. The `tab-activated` event provides `context.event.pane`. Tabs respond to native mouse and keyboard input. Add `autofocus="true"` to a focusable control when it should receive keyboard input after the document or a modal opens, or after a hidden control or its parent is shown with `display=True`. Focus waits for layout, skips disabled and hidden controls, and applies only to the active screen. In an inactive tab, autofocus waits for that tab to activate and does not override `initial`. TextUI gives focused native controls an accent `background-tint` that preserves their own backgrounds and button variant colors; application TCSS may [adjust or disable the tint](project-runtime.md). The [controls example](../examples/controls/app.ui) shows all four controls in a runnable project. ## Choices, disclosure, and indicators diff --git a/docs/project-runtime.md b/docs/project-runtime.md index 3c57486..af26c01 100644 --- a/docs/project-runtime.md +++ b/docs/project-runtime.md @@ -12,7 +12,20 @@ Use ` + {markup} + + ''')) + app.theme = "textual-dark" + async with app.run_test(size=(40, 12)) as pilot: + control = app.document.get_by_id("control") + enabled = True + for compact in (True, False, True): + if compact != enabled: + enabled = app.document.toggle_style_preset("compact") + app.document.get_by_id("other").focus() + await pilot.pause() + resting = control.background_colors[1] + control.focus() + await pilot.pause() + focused = control.background_colors[1] + assert focused.b > max(focused.r, focused.g), (markup, compact, focused) + assert focused != resting diff --git a/tests/visual/__snapshots__/darwin/showcase-first-modal-80x24.svg b/tests/visual/__snapshots__/darwin/showcase-first-modal-80x24.svg index 017a243..83a7c45 100644 --- a/tests/visual/__snapshots__/darwin/showcase-first-modal-80x24.svg +++ b/tests/visual/__snapshots__/darwin/showcase-first-modal-80x24.svg @@ -37,10 +37,10 @@ .terminal-2523035515-r3 { fill: #c5c8c6 } .terminal-2523035515-r4 { fill: #e0e0e0 } .terminal-2523035515-r5 { fill: #646464;font-weight: bold } -.terminal-2523035515-r6 { fill: #201a14;font-weight: bold } +.terminal-2523035515-r6 { fill: #241e17;font-weight: bold } .terminal-2523035515-r7 { fill: #646464 } .terminal-2523035515-r8 { fill: #0178d4 } -.terminal-2523035515-r9 { fill: #3f3221;font-weight: bold } +.terminal-2523035515-r9 { fill: #3f321f;font-weight: bold } .terminal-2523035515-r10 { fill: #6c6668;font-weight: bold } @@ -127,7 +127,7 @@ - + ╭──────╮TextUIfeatureshowcase12:34:56╭───────────╮╔═══════ │ ☰ ││ Compact │║ Help  diff --git a/tests/visual/__snapshots__/darwin/showcase-reopened-modal-80x24.svg b/tests/visual/__snapshots__/darwin/showcase-reopened-modal-80x24.svg index 017a243..83a7c45 100644 --- a/tests/visual/__snapshots__/darwin/showcase-reopened-modal-80x24.svg +++ b/tests/visual/__snapshots__/darwin/showcase-reopened-modal-80x24.svg @@ -37,10 +37,10 @@ .terminal-2523035515-r3 { fill: #c5c8c6 } .terminal-2523035515-r4 { fill: #e0e0e0 } .terminal-2523035515-r5 { fill: #646464;font-weight: bold } -.terminal-2523035515-r6 { fill: #201a14;font-weight: bold } +.terminal-2523035515-r6 { fill: #241e17;font-weight: bold } .terminal-2523035515-r7 { fill: #646464 } .terminal-2523035515-r8 { fill: #0178d4 } -.terminal-2523035515-r9 { fill: #3f3221;font-weight: bold } +.terminal-2523035515-r9 { fill: #3f321f;font-weight: bold } .terminal-2523035515-r10 { fill: #6c6668;font-weight: bold } @@ -127,7 +127,7 @@ - + ╭──────╮TextUIfeatureshowcase12:34:56╭───────────╮╔═══════ │ ☰ ││ Compact │║ Help  diff --git a/tests/visual/__snapshots__/darwin/showcase-shell-120x50.svg b/tests/visual/__snapshots__/darwin/showcase-shell-120x50.svg index 99bfaea..81282ce 100644 --- a/tests/visual/__snapshots__/darwin/showcase-shell-120x50.svg +++ b/tests/visual/__snapshots__/darwin/showcase-shell-120x50.svg @@ -35,7 +35,7 @@ .terminal-3865339023-r1 { fill: #0178d4 } .terminal-3865339023-r2 { fill: #ffffff } .terminal-3865339023-r3 { fill: #c5c8c6 } -.terminal-3865339023-r4 { fill: #352817;font-weight: bold } +.terminal-3865339023-r4 { fill: #3f321f;font-weight: bold } .terminal-3865339023-r5 { fill: #e0e0e0;font-weight: bold } .terminal-3865339023-r6 { fill: #e0e0e0 } .terminal-3865339023-r7 { fill: #f5e5e9;font-weight: bold } @@ -202,7 +202,7 @@ - + ╔══════╗TextUIfeatureshowcase12:34:56╭───────────╮╭────────╮ ║ ☰ ║│ Compact ││ Help │ diff --git a/tests/visual/__snapshots__/darwin/showcase-shell-80x24.svg b/tests/visual/__snapshots__/darwin/showcase-shell-80x24.svg index c65f422..33620ed 100644 --- a/tests/visual/__snapshots__/darwin/showcase-shell-80x24.svg +++ b/tests/visual/__snapshots__/darwin/showcase-shell-80x24.svg @@ -35,7 +35,7 @@ .terminal-1566995877-r1 { fill: #0178d4 } .terminal-1566995877-r2 { fill: #ffffff } .terminal-1566995877-r3 { fill: #c5c8c6 } -.terminal-1566995877-r4 { fill: #352817;font-weight: bold } +.terminal-1566995877-r4 { fill: #3f321f;font-weight: bold } .terminal-1566995877-r5 { fill: #e0e0e0;font-weight: bold } .terminal-1566995877-r6 { fill: #e0e0e0 } .terminal-1566995877-r7 { fill: #f5e5e9;font-weight: bold } @@ -124,7 +124,7 @@ - + ╔══════╗TextUIfeatureshowcase12:34:56╭───────────╮╭─────── ║ ☰ ║│ Compact ││ Help  diff --git a/tests/visual/__snapshots__/linux/showcase-first-modal-80x24.svg b/tests/visual/__snapshots__/linux/showcase-first-modal-80x24.svg index 017a243..83a7c45 100644 --- a/tests/visual/__snapshots__/linux/showcase-first-modal-80x24.svg +++ b/tests/visual/__snapshots__/linux/showcase-first-modal-80x24.svg @@ -37,10 +37,10 @@ .terminal-2523035515-r3 { fill: #c5c8c6 } .terminal-2523035515-r4 { fill: #e0e0e0 } .terminal-2523035515-r5 { fill: #646464;font-weight: bold } -.terminal-2523035515-r6 { fill: #201a14;font-weight: bold } +.terminal-2523035515-r6 { fill: #241e17;font-weight: bold } .terminal-2523035515-r7 { fill: #646464 } .terminal-2523035515-r8 { fill: #0178d4 } -.terminal-2523035515-r9 { fill: #3f3221;font-weight: bold } +.terminal-2523035515-r9 { fill: #3f321f;font-weight: bold } .terminal-2523035515-r10 { fill: #6c6668;font-weight: bold } @@ -127,7 +127,7 @@ - + ╭──────╮TextUIfeatureshowcase12:34:56╭───────────╮╔═══════ │ ☰ ││ Compact │║ Help  diff --git a/tests/visual/__snapshots__/linux/showcase-reopened-modal-80x24.svg b/tests/visual/__snapshots__/linux/showcase-reopened-modal-80x24.svg index 017a243..83a7c45 100644 --- a/tests/visual/__snapshots__/linux/showcase-reopened-modal-80x24.svg +++ b/tests/visual/__snapshots__/linux/showcase-reopened-modal-80x24.svg @@ -37,10 +37,10 @@ .terminal-2523035515-r3 { fill: #c5c8c6 } .terminal-2523035515-r4 { fill: #e0e0e0 } .terminal-2523035515-r5 { fill: #646464;font-weight: bold } -.terminal-2523035515-r6 { fill: #201a14;font-weight: bold } +.terminal-2523035515-r6 { fill: #241e17;font-weight: bold } .terminal-2523035515-r7 { fill: #646464 } .terminal-2523035515-r8 { fill: #0178d4 } -.terminal-2523035515-r9 { fill: #3f3221;font-weight: bold } +.terminal-2523035515-r9 { fill: #3f321f;font-weight: bold } .terminal-2523035515-r10 { fill: #6c6668;font-weight: bold } @@ -127,7 +127,7 @@ - + ╭──────╮TextUIfeatureshowcase12:34:56╭───────────╮╔═══════ │ ☰ ││ Compact │║ Help  diff --git a/tests/visual/__snapshots__/linux/showcase-shell-120x50.svg b/tests/visual/__snapshots__/linux/showcase-shell-120x50.svg index 99bfaea..81282ce 100644 --- a/tests/visual/__snapshots__/linux/showcase-shell-120x50.svg +++ b/tests/visual/__snapshots__/linux/showcase-shell-120x50.svg @@ -35,7 +35,7 @@ .terminal-3865339023-r1 { fill: #0178d4 } .terminal-3865339023-r2 { fill: #ffffff } .terminal-3865339023-r3 { fill: #c5c8c6 } -.terminal-3865339023-r4 { fill: #352817;font-weight: bold } +.terminal-3865339023-r4 { fill: #3f321f;font-weight: bold } .terminal-3865339023-r5 { fill: #e0e0e0;font-weight: bold } .terminal-3865339023-r6 { fill: #e0e0e0 } .terminal-3865339023-r7 { fill: #f5e5e9;font-weight: bold } @@ -202,7 +202,7 @@ - + ╔══════╗TextUIfeatureshowcase12:34:56╭───────────╮╭────────╮ ║ ☰ ║│ Compact ││ Help │ diff --git a/tests/visual/__snapshots__/linux/showcase-shell-80x24.svg b/tests/visual/__snapshots__/linux/showcase-shell-80x24.svg index c65f422..33620ed 100644 --- a/tests/visual/__snapshots__/linux/showcase-shell-80x24.svg +++ b/tests/visual/__snapshots__/linux/showcase-shell-80x24.svg @@ -35,7 +35,7 @@ .terminal-1566995877-r1 { fill: #0178d4 } .terminal-1566995877-r2 { fill: #ffffff } .terminal-1566995877-r3 { fill: #c5c8c6 } -.terminal-1566995877-r4 { fill: #352817;font-weight: bold } +.terminal-1566995877-r4 { fill: #3f321f;font-weight: bold } .terminal-1566995877-r5 { fill: #e0e0e0;font-weight: bold } .terminal-1566995877-r6 { fill: #e0e0e0 } .terminal-1566995877-r7 { fill: #f5e5e9;font-weight: bold } @@ -124,7 +124,7 @@ - + ╔══════╗TextUIfeatureshowcase12:34:56╭───────────╮╭─────── ║ ☰ ║│ Compact ││ Help  diff --git a/textui/presets.py b/textui/presets.py index f928fed..dbdb2bc 100644 --- a/textui/presets.py +++ b/textui/presets.py @@ -20,15 +20,16 @@ } Input:focus, Select:focus, TextArea:focus { outline: none; - background: $accent 25%; + background-tint: $accent 25%; } """ FOCUS_TCSS = """ +/* Blend over native variant and author backgrounds instead of replacing them. */ Button:focus, Input:focus, Select:focus, TextArea:focus, Checkbox:focus, Switch:focus, RadioButton:focus, RadioSet:focus, DataTable:focus, Tree:focus, RuntimeList:focus { - background: $accent 15%; + background-tint: $accent 15%; } """