diff options
| author | srdusr <[email protected]> | 2026-01-28 14:20:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2026-01-28 14:20:00 +0200 |
| commit | d16f7dab25bd9508271017bc6dc15024c30cbf71 (patch) | |
| tree | 86162f301d361cea5aced086e644707a7366c261 /crates/x11 | |
| parent | 393f886eb9e275c3b7a9e3ef396092ba71297dfb (diff) | |
| download | srdwm-d16f7dab25bd9508271017bc6dc15024c30cbf71.tar.gz srdwm-d16f7dab25bd9508271017bc6dc15024c30cbf71.zip | |
Redesign the titlebar right-click menu: real separators/headers, live customization
Reported live: "looks very ugly currently and some of it doesn't make
sense." Both were real. Every row, including a bare divider, took one
full TITLEBAR_HEIGHT slot, so a separator was a 1px hairline in the
middle of 32px of empty space; "Move to Workspace" faked a section
caption by embedding box-drawing characters directly in an ordinary
item's label, which rendered - and behaved, until the click-dispatch
site's own special case - exactly like a clickable row that did
nothing. Separately, "Floating" was always offered even though
Window::floating only affects the "tiling" layout: toggling it under
this project's own default "dynamic" layout visibly changes nothing,
reading as a broken control rather than an inapplicable one.
ContextMenu (crates/core/src/context_menu.rs) gained real Separator
(9px) and Header (22px, non-interactive, dimmed) row kinds with their
own small heights, replacing the label-hack outright. Both backends'
rendering now sum each row's own real height instead of assuming one
uniform value, so hit-testing and pixels can't disagree about where a
row is. Floating is omitted entirely outside the tiling layout.
New, in direct response to "allow customizing from there as well": a
Customize section with live Button Style / Button Side toggles. Each
flips the matching ThemeConfig field and immediately redraws every open
window's titlebar - not routed through srd set's own path, which is
scoped to windows created after the call for lack of a redraw hook it
can reach; a menu action that didn't visibly change the titlebar you
clicked would be its own "doesn't make sense" bug.
Full workspace build/test/clippy clean (242 core tests, +8; 152
wayland, net-even after rewriting the old label-hack tests).
Diffstat (limited to 'crates/x11')
| -rw-r--r-- | crates/x11/src/platform/actions.rs | 2 | ||||
| -rw-r--r-- | crates/x11/src/platform/context_menu.rs | 43 | ||||
| -rw-r--r-- | crates/x11/src/platform/events.rs | 2 |
3 files changed, 40 insertions, 7 deletions
diff --git a/crates/x11/src/platform/actions.rs b/crates/x11/src/platform/actions.rs index 9d31d01..4847399 100644 --- a/crates/x11/src/platform/actions.rs +++ b/crates/x11/src/platform/actions.rs @@ -35,7 +35,7 @@ impl X11Platform { Ok(()) } - fn redraw_all_decorations(&mut self) -> PlatformResult<()> { + pub(super) fn redraw_all_decorations(&mut self) -> PlatformResult<()> { let focused = self.wm.borrow().focused_id(); let ids: Vec<WindowId> = self.frames.keys().copied().collect(); for id in ids { diff --git a/crates/x11/src/platform/context_menu.rs b/crates/x11/src/platform/context_menu.rs index 9238f6a..e6b4b7f 100644 --- a/crates/x11/src/platform/context_menu.rs +++ b/crates/x11/src/platform/context_menu.rs @@ -80,7 +80,7 @@ impl X11Platform { pub(super) fn redraw_context_menu(&mut self) -> PlatformResult<()> { let Some((menu, popup)) = &self.context_menu else { return Ok(()) }; let popup = *popup; - let (width, row_height, total_height) = (menu.width, menu.row_height, menu.height()); + let (width, total_height) = (menu.width, menu.height()); let theme = self.wm.borrow().theme; let bg = rgb_to_pixel(theme.titlebar_bg); let fg = rgb_to_pixel(theme.titlebar_fg_focused); @@ -89,14 +89,27 @@ impl X11Platform { self.conn.poly_fill_rectangle(popup, self.gc, &[Rectangle { x: 0, y: 0, width: width as u16, height: total_height as u16 }]).map_err(err)?; self.conn.change_gc(self.gc, &ChangeGCAux::new().foreground(fg).font(self.font)).map_err(err)?; + // Per-row heights, not a uniform `row_height * i` - a `Separator`/ + // `Header` row is shorter than a real item (`ContextMenu::row_ + // height_for`), same variable-height model the Wayland backend's + // own `render_context_menu` now uses; `menu.row_y(i)` is exactly + // how that side computes each row's own top too, so the two + // backends can't drift onto different geometry for the same menu + // data. No dimmed-text treatment for a `Header` row here (this + // backend draws everything through one single-colour `GC`, no + // per-pixel blending the way the Wayland renderer's own `mix_rgb` + // has) - feature parity (non-interactive, correctly sized) over + // pixel parity, matching this backend's existing bar elsewhere. for (i, (label, action)) in menu.items.iter().enumerate() { - let row_y = i as i32 * row_height as i32; + let row_y = menu.row_y(i); + let row_height = menu.row_height_for(i) as i32; if matches!(action, MenuAction::Separator) { - let mid = row_y + row_height as i32 / 2; + let mid = row_y + row_height / 2; self.conn.poly_line(CoordMode::ORIGIN, popup, self.gc, &[Point { x: 8, y: mid as i16 }, Point { x: width as i16 - 8, y: mid as i16 }]).map_err(err)?; continue; } - self.conn.image_text8(popup, self.gc, 10, (row_y + 20) as i16, label.as_bytes()).map_err(err)?; + let baseline = row_y + row_height * 3 / 4; + self.conn.image_text8(popup, self.gc, 10, baseline as i16, label.as_bytes()).map_err(err)?; } self.conn.flush().map_err(err)?; Ok(()) @@ -144,8 +157,28 @@ impl X11Platform { MenuAction::MoveToWorkspace(workspace) => { self.wm.borrow_mut().move_window_to_workspace(window, workspace); } + // Same reasoning as the Wayland backend's own `redraw_every_ + // decoration` (`crates/wayland/src/state/menu.rs`): `srd set + // button_style`/`button_side` are deliberately scoped to + // "takes effect on windows created after this call" because + // `crates/platform` is backend-agnostic and has no redraw hook + // to call - this menu action runs as this backend's own + // code, already holding `&mut self`, so it can and should + // repaint every open titlebar immediately instead. + MenuAction::CycleButtonStyle => { + let mut wm = self.wm.borrow_mut(); + wm.theme.traffic_light_buttons = !wm.theme.traffic_light_buttons; + drop(wm); + self.redraw_all_decorations()?; + } + MenuAction::CycleButtonSide => { + let mut wm = self.wm.borrow_mut(); + wm.theme.buttons_left = !wm.theme.buttons_left; + drop(wm); + self.redraw_all_decorations()?; + } MenuAction::Close => self.request_close(window)?, - MenuAction::Separator => {} + MenuAction::Separator | MenuAction::Header => {} } self.conn.flush().map_err(err)?; Ok(()) diff --git a/crates/x11/src/platform/events.rs b/crates/x11/src/platform/events.rs index 10f96a9..545e8f3 100644 --- a/crates/x11/src/platform/events.rs +++ b/crates/x11/src/platform/events.rs @@ -89,7 +89,7 @@ impl X11Platform { (menu.row_at(x, y).map(|r| menu.items[r].1), menu.window) }; match row_action { - Some(srdwm_core::context_menu::MenuAction::Separator) => {} + Some(action) if !action.is_interactive() => {} Some(action) => { self.close_context_menu()?; self.run_context_menu_action(menu_window, action)?; |