srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/decoration.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-09-28 21:25:00 +0200
committersrdusr <[email protected]>2025-09-28 21:25:00 +0200
commitffdae252782d047a6b2d76c9bb4efeb92c391db3 (patch)
tree11ddf590e643e55cdb1e907c0f236dd077236e03 /crates/wayland/src/decoration.rs
parent58a368df5f7a5d579335d1cb68213baacba5bc63 (diff)
downloadsrdwm-ffdae252782d047a6b2d76c9bb4efeb92c391db3.tar.gz
srdwm-ffdae252782d047a6b2d76c9bb4efeb92c391db3.zip
Context/desktop menu polish: real hover tint, real separator line, Select All
Reported live: "looks weird and unpolished... need a lot more items". Compared directly against the exact AGS reference this project's own menu rebuild already targets rather than guessing: - Highlighted rows used a flat, fully-saturated fill instead of the reference's subtle 22%-accent-into-background wash. New decoration:: color::mix_rgb (channel-wise linear blend, generalizing brighten/ darken's fixed-target blends to an arbitrary second colour/ratio) lets render_context_menu reproduce that same ratio. - Every separator row was a label string of Unicode box-drawing characters rendered as text glyphs, which render inconsistently at small sizes - a label that's entirely U+2500 now draws a real 1px hairline instead; a label that mixes it with real text ("--- Move to Workspace ---", a deliberate section-header convention) still renders as text, unchanged. - "Select All" added to the bare-desktop menu, the one action every mainstream desktop's own menu offers that this one lacked. New tests needed real care: the panel's own rounded-corner distance field softens alpha within its radius of any canvas edge, not just the visible corners, so a naive full-row pixel scan against bg picked that up as a false positive on the first attempt - fixed by scanning only rows/columns confirmed (via a throwaway debug dump) to sit inside the panel's genuinely flat interior. Full workspace build/test/clippy clean, built and installed. Real submenus and per-row icons remain real, separate scope - this project's floating-menu UI has no nested-panel concept yet.
Diffstat (limited to 'crates/wayland/src/decoration.rs')
-rw-r--r--crates/wayland/src/decoration.rs42
1 files changed, 37 insertions, 5 deletions
diff --git a/crates/wayland/src/decoration.rs b/crates/wayland/src/decoration.rs
index 56852cb..ee3f34c 100644
--- a/crates/wayland/src/decoration.rs
+++ b/crates/wayland/src/decoration.rs
@@ -40,7 +40,7 @@ mod titlebar;
pub use border::{border_strips, render_border_bottom, render_border_top};
pub(crate) use border::{border_bottom_visible_rows, border_top_visible_rows};
pub(crate) use buttons::HOVER_GLYPH_DURATION;
-pub(crate) use color::rgb_to_bgra;
+pub(crate) use color::{mix_rgb, rgb_to_bgra};
pub(crate) use corners::{round_bottom_corners, round_top_corners};
pub(crate) use font::{blit_glyph, find_system_font, FONT_PIXELS, TEXT_LEFT_PADDING};
pub use shadow::{shadow_bitmap, shadow_rect};
@@ -84,13 +84,38 @@ pub(crate) const CORNER_RADIUS: u32 = 12;
/// polished" - the previous version drew edge-to-edge square rows with a
/// single hard 1px border around the whole menu, exactly what that
/// complaint (raised about the AGS dropdown, fixed there first) describes.
-/// Still no submenus/icons/separators - real gaps beyond this pass' own
-/// scope, not attempted blind.
+/// Still no submenus/icons - real gaps beyond this pass' own scope, not
+/// attempted blind.
+///
+/// A row whose label is *entirely* the box-drawing character `─`
+/// (`\u{2500}`, one or more, no other content) renders as a real thin
+/// divider line instead of text glyphs - a label that *mixes* `─` with
+/// real text (`"─── Move to Workspace ───"`, a deliberate section-header
+/// convention `core::ContextMenu` already uses) is untouched and still
+/// renders as text, since that dual purpose (divider *and* caption) is
+/// the actual design, not a plain separator. A pure divider is drawn as
+/// pixels rather than characters because Unicode box-drawing glyphs
+/// render inconsistently thin/dotted across fonts at small sizes - a
+/// real 1px anti-aliased line, inset from both edges and blended low-
+/// opacity against the panel (matching the AGS reference dropdown's own
+/// `separator.menu-sep`: `color-mix(in srgb, var(--fg) 12%, transparent)`),
+/// reads as an intentional divider rather than a run of stray dashes.
pub fn render_context_menu(width: u32, row_height: u32, items: &[(&str, bool)], bg: (u8, u8, u8), fg: (u8, u8, u8), highlight_bg: (u8, u8, u8), border: (u8, u8, u8)) -> Vec<u8> {
let _ = border; // No outline anywhere now - see this function's own doc comment. Kept as a parameter so callers/themes don't need updating for a look this function no longer draws.
const PANEL_RADIUS: f32 = 10.0;
const ROW_INSET: i32 = 4;
const ROW_RADIUS: f32 = 6.0;
+ // AGS reference's own measured/settled ratio (`popover box.menu-list
+ // button:hover`'s `color-mix(in srgb, var(--primary-bg) 22%, var(
+ // --widget-bg))`) - a subtle tinted wash, not a flat, fully-
+ // saturated fill of whatever colour the caller passes as `highlight_
+ // bg`. Applied here rather than changing what callers pass in, so
+ // every existing call site's own colour choice still means "the
+ // accent to tint toward", not "the literal pixel colour".
+ const HIGHLIGHT_MIX: f32 = 0.22;
+ // Matches the reference's own `separator.menu-sep` background --
+ // barely-there, a hairline rather than a visible bar.
+ const SEPARATOR_MIX: f32 = 0.12;
let (width, row_height) = (width.max(1) as usize, row_height.max(1) as usize);
// Exactly `row_height * items.len()`, same as before this pass --
@@ -107,9 +132,16 @@ pub fn render_context_menu(width: u32, row_height: u32, items: &[(&str, bool)],
// the menu (desktop/window content) rather than a hard-edged square.
fill_rounded_rect(&mut buf, width, height, 0, 0, width as i32, height as i32, PANEL_RADIUS, bg, bg);
+ let highlight_fill = mix_rgb(bg, highlight_bg, HIGHLIGHT_MIX);
+ let separator_color = mix_rgb(bg, fg, SEPARATOR_MIX);
let font = find_system_font();
for (i, (label, highlighted)) in items.iter().enumerate() {
let row_top = (i * row_height) as i32;
+ if !label.is_empty() && label.chars().all(|c| c == '\u{2500}') {
+ let y = row_top + row_height as i32 / 2;
+ fill_rounded_rect_over(&mut buf, width, height, ROW_INSET * 2, y, width as i32 - ROW_INSET * 2, y + 1, 0.0, separator_color);
+ continue;
+ }
// The background text actually sits on, for `blit_glyph`'s own
// blend-toward-a-known-solid-colour contract - the row's own
// highlight fill (already baked into `buf` by this point, above)
@@ -121,9 +153,9 @@ pub fn render_context_menu(width: u32, row_height: u32, items: &[(&str, bool)],
// comment) - used correctly, it would leave a visible dark
// fringe around every character's anti-aliased edge instead of a
// clean blend into the row's real colour.
- let row_bg = if *highlighted { highlight_bg } else { bg };
+ let row_bg = if *highlighted { highlight_fill } else { bg };
if *highlighted {
- fill_rounded_rect_over(&mut buf, width, height, ROW_INSET, row_top, width as i32 - ROW_INSET, row_top + row_height as i32, ROW_RADIUS, highlight_bg);
+ fill_rounded_rect_over(&mut buf, width, height, ROW_INSET, row_top, width as i32 - ROW_INSET, row_top + row_height as i32, ROW_RADIUS, highlight_fill);
}
if let Some(font) = &font {
let baseline = row_top as f32 + row_height as f32 * 0.72;