diff options
| author | srdusr <[email protected]> | 2025-09-28 21:25:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-09-28 21:25:00 +0200 |
| commit | ffdae252782d047a6b2d76c9bb4efeb92c391db3 (patch) | |
| tree | 11ddf590e643e55cdb1e907c0f236dd077236e03 /crates/wayland/src/decoration.rs | |
| parent | 58a368df5f7a5d579335d1cb68213baacba5bc63 (diff) | |
| download | srdwm-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.rs | 42 |
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; |