git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit2914b59908970d2f2c8177bac1db52d0f4a04e70
parent5d6ff01b67
authorLucas Galante <[email protected]>
date2026-07-29 10:12
feat: complementary shadow tint on focused carves; app-drivable rim flag

The focused well's shadow walls counter-tint with the complement of the
highlight accent (1 - rgb, at ~38%, alpha boosted like the highlight) —
the painter's warm-light/cool-shadow pairing, so the focused rim opposes
in hue as well as value. Untinted carves keep pure black shadows.

TreeList::focused goes pub: an app whose inline editors float over tree
rows (cce-data-editor) overrides it per input event with a focus-within
computation, since those editors take ctx focus while remaining visually
part of the tree pane.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/vk/shader2d.wgsl             | 14 +++++++++-----
 src/widget/container/treelist.rs |  9 ++++++---
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/src/vk/shader2d.wgsl b/src/vk/shader2d.wgsl
index 5031ab6..1f35dca 100644
--- a/src/vk/shader2d.wgsl
+++ b/src/vk/shader2d.wgsl
@@ -418,15 +418,19 @@ fn plate_shade(frag: vec2f, vcol: vec4f) -> vec4f {
     let host_d = min(hb.z - abs(frag.x - hb.x), hb.w - abs(frag.y - hb.y));
     let att = clamp(host_d / t, 0.0, 1.0) * wedge;
     let v = (diff / flat_shade - 1.0 + curv + spec) * strength * att;
+    // p_spec_tint.w = 1 marks a tinted carve (a focused well); plates leave w at 0.
+    let tw = rrect_clip.p_spec_tint.w;
     if (v >= 0.0) {
-        // p_spec_tint.w = 1 marks a tinted carve (a focused well): the white
-        // highlight screen mixes toward the tint color, slightly boosted so the
-        // accent reads at the rim's low alphas. Plates leave w at 0.
-        let tw = rrect_clip.p_spec_tint.w;
+        // Highlight: the white screen mixes toward the tint color, slightly
+        // boosted so the accent reads at the rim's low alphas.
         let hl = mix(vec3f(1.0), rrect_clip.p_spec_tint.rgb, tw);
         return vec4f(hl, min(v * (1.0 + 0.5 * tw), 1.0));
     }
-    return vec4f(0.0, 0.0, 0.0, min(-v, 1.0));
+    // Shadow: the complementary counter-tint (warm against a cool accent) at
+    // ~38%, so the focused rim's two sides oppose in hue as well as value —
+    // the painter's warm-light/cool-shadow trick. Untinted carves stay black.
+    let sh = (vec3f(1.0) - rrect_clip.p_spec_tint.rgb) * 0.38 * tw;
+    return vec4f(sh, min(-v * (1.0 + 0.5 * tw), 1.0));
 }
 
 struct VertexOutput {
diff --git a/src/widget/container/treelist.rs b/src/widget/container/treelist.rs
index 7eaa46b..d679622 100644
--- a/src/widget/container/treelist.rs
+++ b/src/widget/container/treelist.rs
@@ -179,9 +179,12 @@ pub struct TreeList {
     pub right_clicked_section: Option<String>,
     pub last_scroll_y: f32,
     pub scrollbar_activity_timer: f32,
-    /// Mirrors ctx focus (FocusIn/FocusOut + the grab in `mouse_body`) so `paint` —
-    /// which has no UiContext — can light the recess rim when the tree is focused.
-    focused: bool,
+    /// Lights the recess rim (`paint` has no UiContext, so this mirrors focus).
+    /// Driven by FocusIn/FocusOut by default; an app whose inline editors float
+    /// over the tree (cce-data-editor) overrides it per input event with its own
+    /// focus-within computation — those editors take ctx focus away from the
+    /// tree while still being, visually, part of the tree pane.
+    pub focused: bool,
     pub deleted_key_path: Option<String>,
     pub edit_box: crate::widget::Adapted<TextBox>,
     pub editing_key_idx: Option<usize>,