Skip to content

Commit a26fb12

Browse files
committed
compositor: window header tweaks
1 parent d4c1cd5 commit a26fb12

1 file changed

Lines changed: 81 additions & 49 deletions

File tree

src/backend/render/window_header.rs

Lines changed: 81 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -649,40 +649,55 @@ fn draw_buttons(
649649

650650
/// Pick the (background, icon, scale) visual triple for a button
651651
/// based on its interaction state. Matches the canonical
652-
/// `WindowControls.svelte` in `sdk/ui-kit` field-for-field:
652+
/// `WindowControls.svelte` shipped in `app-settings` (which is
653+
/// the visible reference the user compares against side-by-side
654+
/// with the compositor-rendered header) **field-for-field**:
653655
///
654656
/// ```css
655-
/// .control-btn { opacity: 0.7 }
656-
/// .control-btn:hover { opacity: 1; transform: scale(1.1);
657-
/// background: color-mix(fg 10%, transparent); }
658-
/// .control-btn:active { transform: scale(0.9) }
659-
/// .close-btn:hover { background: var(--destructive);
660-
/// color: #ffffff }
657+
/// .control-btn { opacity: 0.7;
658+
/// transition: opacity var(--duration-fast)
659+
/// var(--easing-default); }
660+
/// .control-btn:hover { opacity: 1; } /* NOTHING ELSE */
661+
/// .close-btn:hover { background: var(--destructive);
662+
/// color: #ffffff; }
661663
/// ```
662664
///
663-
/// The `opacity` is applied to both bg alpha AND the icon colour
664-
/// so the whole button dims together. When the parent window is
665-
/// inactive we dim further (`BUTTON_IDLE_OPACITY_INACTIVE`) to make
666-
/// focus state obvious — WindowControls.svelte doesn't handle this
667-
/// case because it's rendered inside the app's own window where
668-
/// focus state isn't ambiguous.
665+
/// Notably, the canonical decoration buttons have:
666+
/// * NO hover-bg tint on non-close buttons (only opacity changes).
667+
/// * NO scale on hover.
668+
/// * NO scale on press.
669+
///
670+
/// An older variant of `WindowControls.svelte` in `desktop-shell`
671+
/// has scale(1.1) on hover, scale(0.9) on press, and a 10 %
672+
/// foreground bg-tint on hover — those were a brief experiment
673+
/// that never made it back into the canonical version. The
674+
/// compositor used to mirror that experimental variant; this
675+
/// function now matches the pared-back canonical look so the
676+
/// app-settings decorations and the compositor-rendered Kitty
677+
/// decorations animate identically.
678+
///
679+
/// Inactive-window dimming (`BUTTON_IDLE_OPACITY_INACTIVE`) stays
680+
/// — that's a compositor-specific extension because
681+
/// WindowControls.svelte never lives outside its own focused
682+
/// app window.
669683
fn button_visual(
670684
button: HeaderButton,
671685
state: &HeaderVisualState,
672686
theme: &LunarisTheme,
673687
) -> (Rgba, Rgba, f32) {
674688
let is_close = button == HeaderButton::Close;
675689

676-
let (hovered, pressed) = match state.interaction {
690+
let (hovered, _pressed) = match state.interaction {
677691
ButtonInteraction::Hover(b) if b == button => (true, false),
678692
ButtonInteraction::Pressed(b) if b == button => (true, true),
679693
_ => (false, false),
680694
};
681695

682-
// Effective "button opacity" — WindowControls.svelte:
683-
// idle → 0.7 (or lower when window unfocused)
684-
// hover → 1.0 (on focused window)
685-
// hover on unfocused window → bump back to focused-idle
696+
// Effective "button opacity":
697+
// activated + idle → 0.7 (matches WindowControls.svelte)
698+
// activated + hover → 1.0
699+
// inactive + idle → 0.4 (compositor-only extension)
700+
// inactive + hover → 0.7
686701
let button_opacity = if hovered {
687702
if state.activated { 1.0 } else { BUTTON_IDLE_OPACITY }
688703
} else if state.activated {
@@ -691,26 +706,14 @@ fn button_visual(
691706
BUTTON_IDLE_OPACITY_INACTIVE
692707
};
693708

694-
// Background — pre-opacity. Close-hover has special treatment.
695-
let bg_raw = if hovered {
696-
if is_close {
697-
// `.close-btn:hover { background: var(--destructive) }`
698-
// — FULL OPAQUE destructive (no alpha mix). This is a
699-
// clear "you are about to close" signal, not a soft
700-
// tint. The old renderer mixed at 80 % alpha which
701-
// came from a stale version of WindowHeader.svelte;
702-
// the canonical WindowControls is full-opaque.
703-
theme.error
704-
} else {
705-
// `.control-btn:hover { background:
706-
// color-mix(in srgb, var(--foreground) 10%, transparent) }`
707-
mix(theme.fg_primary, TRANSPARENT, 0.10)
708-
}
709+
// Background — pre-opacity. Non-close hover stays transparent
710+
// (no tint). Close-hover lights up the full destructive
711+
// colour, no alpha attenuation other than `button_opacity`.
712+
let bg_raw = if hovered && is_close {
713+
theme.error
709714
} else {
710715
TRANSPARENT
711716
};
712-
// Apply button_opacity to the BG alpha so the whole button
713-
// dims uniformly with its icon.
714717
let mut bg = bg_raw;
715718
bg[3] *= button_opacity;
716719

@@ -719,19 +722,16 @@ fn button_visual(
719722
let icon_raw = if hovered && is_close {
720723
[1.0, 1.0, 1.0, 1.0]
721724
} else {
722-
// Use fg_primary at full RGB; opacity handles the dimming.
723725
theme.fg_primary
724726
};
725727
let mut icon_color = icon_raw;
726728
icon_color[3] *= button_opacity;
727729

728-
let scale = if pressed {
729-
0.9
730-
} else if hovered {
731-
1.1
732-
} else {
733-
1.0
734-
};
730+
// Scale = 1.0 always. The canonical `WindowControls.svelte`
731+
// does NOT animate scale; only opacity. Keep this as a triple
732+
// return so callers stay shape-stable if a future variant
733+
// wants to bring scale back.
734+
let scale = 1.0;
735735

736736
(bg, icon_color, scale)
737737
}
@@ -974,7 +974,9 @@ mod tests {
974974
assert!((bg[3] - 1.0).abs() < 0.001, "A should be full, got {}", bg[3]);
975975
// Icon pure white at full alpha on activated window.
976976
assert_eq!(icon, [1.0, 1.0, 1.0, 1.0]);
977-
assert!((scale - 1.1).abs() < 0.001);
977+
// Canonical app-settings WindowControls has NO scale on
978+
// hover or active. Stay at 1.0.
979+
assert!((scale - 1.0).abs() < 0.001, "no hover scale, got {}", scale);
978980
}
979981

980982
#[test]
@@ -997,14 +999,44 @@ mod tests {
997999
}
9981000

9991001
#[test]
1000-
fn button_visual_pressed_shrinks() {
1001-
let state = HeaderVisualState {
1002+
fn button_visual_no_scale_animation() {
1003+
// Canonical WindowControls has no transform animation —
1004+
// verify all states stay at scale 1.0 so the compositor
1005+
// doesn't reintroduce the bouncy scale-on-press feel.
1006+
let theme = LunarisTheme::lunaris_dark();
1007+
1008+
let idle = stub_state(800, true);
1009+
let (_, _, s_idle) = button_visual(HeaderButton::Minimize, &idle, &theme);
1010+
assert!((s_idle - 1.0).abs() < 0.001);
1011+
1012+
let hover = HeaderVisualState {
1013+
interaction: ButtonInteraction::Hover(HeaderButton::Minimize),
1014+
..idle.clone()
1015+
};
1016+
let (_, _, s_hover) = button_visual(HeaderButton::Minimize, &hover, &theme);
1017+
assert!((s_hover - 1.0).abs() < 0.001);
1018+
1019+
let pressed = HeaderVisualState {
10021020
interaction: ButtonInteraction::Pressed(HeaderButton::Minimize),
1021+
..idle
1022+
};
1023+
let (_, _, s_press) = button_visual(HeaderButton::Minimize, &pressed, &theme);
1024+
assert!((s_press - 1.0).abs() < 0.001);
1025+
}
1026+
1027+
#[test]
1028+
fn button_visual_no_hover_bg_for_non_close() {
1029+
// Non-close hover keeps bg transparent — only opacity
1030+
// changes. This is the visible difference vs the older
1031+
// desktop-shell variant that mixed a 10 % foreground tint.
1032+
let theme = LunarisTheme::lunaris_dark();
1033+
let hover = HeaderVisualState {
1034+
interaction: ButtonInteraction::Hover(HeaderButton::Minimize),
10031035
..stub_state(800, true)
10041036
};
1005-
let theme = LunarisTheme::panda();
1006-
let (_, _, scale) = button_visual(HeaderButton::Minimize, &state, &theme);
1007-
assert!((scale - 0.9).abs() < 0.001);
1037+
let (bg, _, _) = button_visual(HeaderButton::Minimize, &hover, &theme);
1038+
// Pre-multiplied alpha == 0 → background fully transparent.
1039+
assert!(bg[3] < 1e-5, "non-close hover should not paint bg, got alpha {}", bg[3]);
10081040
}
10091041

10101042
#[test]

0 commit comments

Comments
 (0)