diff options
| author | user <user@clank> | 2026-08-12 15:50:04 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-12 15:50:04 +0200 |
| commit | 266147d1d5ecadafda6a4893a60019c80f3c2c85 (patch) | |
| tree | 761be672fca76155d407ffda8be4a0c99033fc62 /zellij-server/src/tab/mouse_handler.rs | |
| parent | sl0p: document the pane status bar protocol (diff) | |
| download | sl0ppij-266147d1d5ecadafda6a4893a60019c80f3c2c85.tar.gz sl0ppij-266147d1d5ecadafda6a4893a60019c80f3c2c85.tar.xz sl0ppij-266147d1d5ecadafda6a4893a60019c80f3c2c85.zip | |
sl0p: highlight the status bar button under the pointer
A click target with no hover feedback gives you no way to tell what you are about
to hit, which matters more here than elsewhere because the buttons sit in a frame
row that is only one cell tall.
The hovered button is inverted rather than merely recoloured: swapping
fore/background reads regardless of which colours a theme uses, and makes "the
pointer is on THIS one" unmistakable next to its unhovered neighbours.
Tracked per client, because hover belongs to a pointer rather than to a pane --
with two users attached, one pointer must not light up the other's button. Stored
on the pane keyed by client id, which the frame renderer already has.
Updated before the action dispatch rather than in the hover action, because
motion over the ACTIVE pane is dispatched as SendToTerminal, not UpdateHover --
and the pane you are about to click a button in is usually the one you are
already in. Every other pane's hover for that client is cleared on each motion,
so moving between panes cannot leave a button lit behind.
Diffstat (limited to 'zellij-server/src/tab/mouse_handler.rs')
| -rw-r--r-- | zellij-server/src/tab/mouse_handler.rs | 43 |
1 files changed, 42 insertions, 1 deletions
diff --git a/zellij-server/src/tab/mouse_handler.rs b/zellij-server/src/tab/mouse_handler.rs index ab8b2383..cb1b79f6 100644 --- a/zellij-server/src/tab/mouse_handler.rs +++ b/zellij-server/src/tab/mouse_handler.rs @@ -410,9 +410,50 @@ impl MouseHandler { if let Some(effect) = Self::intercept_status_bar_button_click(tab, event)? { return Ok(effect); } + // Not an intercept: hover state is updated and the event still goes on to + // its normal handling. + let hover_changed = Self::update_status_bar_hover(tab, event, client_id)?; let context = Self::gather_mouse_event_context(tab, event, client_id)?; let action = Self::determine_mouse_action(event, &context)?; - Self::execute_mouse_action(tab, action, event, client_id) + let mut effect = Self::execute_mouse_action(tab, action, event, client_id)?; + if hover_changed { + effect.state_changed = true; + } + Ok(effect) + } + + /// Track which status bar button the pointer is over, so it can be drawn + /// highlighted. + /// + /// Done here rather than in the hover action, because motion over the ACTIVE + /// pane is dispatched as SendToTerminal, not UpdateHover -- and the pane you + /// are about to click a button in is usually the one you are already in. + /// + /// Returns whether anything changed, so the caller can force a render. + fn update_status_bar_hover( + tab: &mut Tab, + event: &MouseEvent, + client_id: ClientId, + ) -> Result<bool> { + if event.event_type != MouseEventType::Motion { + return Ok(false); + } + let hovered = Self::get_pane_at(tab, &event.position, false)? + .and_then(|pane| pane.status_bar_button_at(&event.position).map(|id| (pane.pid(), id))); + let mut changed = false; + // clear every other pane's hover for this client, so moving between panes + // cannot leave a button lit behind us + let pane_ids: Vec<PaneId> = tab.get_all_pane_ids(); + for pane_id in pane_ids { + let wanted = match &hovered { + Some((hovered_id, button_id)) if *hovered_id == pane_id => Some(button_id.clone()), + _ => None, + }; + if let Some(pane) = tab.get_pane_with_id_mut(pane_id) { + changed |= pane.set_hovered_status_bar_button(client_id, wanted); + } + } + Ok(changed) } /// A click on a pane's status bar button reports it to the program in that |
