aboutsummaryrefslogtreecommitdiffstats
path: root/src/widgets
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-08 23:54:38 +0200
committeruser <user@clank>2026-08-08 23:54:38 +0200
commit813d4b80658bfb717d93d539019d970b6fbbd1d0 (patch)
tree22849b81eb2a5aa35b9ab8fb964d40374047f294 /src/widgets
parentchore: bump version to v0.24.0 (diff)
downloadsl0pstatus-main.tar.gz
sl0pstatus-main.tar.xz
sl0pstatus-main.zip
sl0p: double-click a tab to rename itHEADmain
Double-clicking a tab in the bar switches to it and drops into RenameTab mode, matching the double-click-to-rename we added for panes in the sl0ppij zellij fork. Enter commits, Esc cancels, exactly as the rename keybind does. Widgets are held behind &self and cannot keep click state, so double-click detection lives centrally in ModuleConfig::handle_mouse_action and is passed to Widget::process_click as a flag. THE TRAP, and the reason this is not a two-line change: handle_mouse_action also runs for Mouse::Release. A single click therefore arrives as LeftClick followed by Release at the SAME position. Counting releases would make every single click register as a double click, and resetting on release would mean a real double click never registers at all, since the release falls between the two clicks. Only LeftClick participates; Release neither counts nor resets. A completed pair is consumed, so a triple click does not fire a second rename. switch_tab_to() is called before switch_to_input_mode(), because RenameTab acts on the ACTIVE tab - renaming without switching first would rename the wrong one. Threshold is 400ms, the same one zellij uses for text-selection double clicks, so the bar feels consistent with the panes. Detection is extracted as register_click_for_double_click() so it can be tested without constructing a ModuleConfig. Tests cover: a real double click, the release-in-between regression, too-slow clicks, clicks at different positions, and the triple-click case. Upstream tests still pass: 25 total (20 upstream + 5 new). Note: this repo's .cargo/config.toml targets wasm32-wasip1 and runs tests under wasmtime, which is not installed here. Tests are run with `cargo test --target x86_64-unknown-linux-gnu` instead.
Diffstat (limited to 'src/widgets')
-rw-r--r--src/widgets/command.rs2
-rw-r--r--src/widgets/datetime.rs2
-rw-r--r--src/widgets/mode.rs2
-rw-r--r--src/widgets/notification.rs2
-rw-r--r--src/widgets/pipe.rs2
-rw-r--r--src/widgets/session.rs2
-rw-r--r--src/widgets/swap_layout.rs2
-rw-r--r--src/widgets/tabs.rs11
-rw-r--r--src/widgets/widget.rs6
9 files changed, 21 insertions, 10 deletions
diff --git a/src/widgets/command.rs b/src/widgets/command.rs
index 245c5fc..55498c5 100644
--- a/src/widgets/command.rs
+++ b/src/widgets/command.rs
@@ -142,7 +142,7 @@ impl Widget for CommandWidget {
}
}
- fn process_click(&self, name: &str, _state: &ZellijState, _pos: usize) {
+ fn process_click(&self, name: &str, _state: &ZellijState, _pos: usize, _is_double_click: bool) {
let command_config = match self.config.get(name) {
Some(cc) => cc,
None => {
diff --git a/src/widgets/datetime.rs b/src/widgets/datetime.rs
index f500b5d..8e2a67e 100644
--- a/src/widgets/datetime.rs
+++ b/src/widgets/datetime.rs
@@ -105,5 +105,5 @@ impl Widget for DateTimeWidget {
})
}
- fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize) {}
+ fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize, _is_double_click: bool) {}
}
diff --git a/src/widgets/mode.rs b/src/widgets/mode.rs
index 8544cf9..f604291 100644
--- a/src/widgets/mode.rs
+++ b/src/widgets/mode.rs
@@ -139,7 +139,7 @@ impl Widget for ModeWidget {
})
}
- fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize) {}
+ fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize, _is_double_click: bool) {}
}
impl ModeWidget {
diff --git a/src/widgets/notification.rs b/src/widgets/notification.rs
index 458d12a..b892d4a 100644
--- a/src/widgets/notification.rs
+++ b/src/widgets/notification.rs
@@ -74,5 +74,5 @@ impl Widget for NotificationWidget {
output.to_owned()
}
- fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize) {}
+ fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize, _is_double_click: bool) {}
}
diff --git a/src/widgets/pipe.rs b/src/widgets/pipe.rs
index 2c5f724..2c41378 100644
--- a/src/widgets/pipe.rs
+++ b/src/widgets/pipe.rs
@@ -85,7 +85,7 @@ impl Widget for PipeWidget {
}
}
- fn process_click(&self, _name: &str, _state: &crate::config::ZellijState, _pos: usize) {}
+ fn process_click(&self, _name: &str, _state: &crate::config::ZellijState, _pos: usize, _is_double_click: bool) {}
}
fn render_dynamic_formatted_content(content: &str, config: &BTreeMap<String, String>) -> String {
diff --git a/src/widgets/session.rs b/src/widgets/session.rs
index 9e27f6d..107d5fa 100644
--- a/src/widgets/session.rs
+++ b/src/widgets/session.rs
@@ -18,5 +18,5 @@ impl Widget for SessionWidget {
}
}
- fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize) {}
+ fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize, _is_double_click: bool) {}
}
diff --git a/src/widgets/swap_layout.rs b/src/widgets/swap_layout.rs
index 8c9e036..ca58f97 100644
--- a/src/widgets/swap_layout.rs
+++ b/src/widgets/swap_layout.rs
@@ -66,7 +66,7 @@ impl Widget for SwapLayoutWidget {
output
}
- fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize) {
+ fn process_click(&self, _name: &str, _state: &ZellijState, _pos: usize, _is_double_click: bool) {
next_swap_layout()
}
}
diff --git a/src/widgets/tabs.rs b/src/widgets/tabs.rs
index b62f581..970449a 100644
--- a/src/widgets/tabs.rs
+++ b/src/widgets/tabs.rs
@@ -2,7 +2,7 @@ use std::{cmp, collections::BTreeMap};
use zellij_tile::{
prelude::{InputMode, ModeInfo, PaneInfo, PaneManifest, TabInfo},
- shim::switch_tab_to,
+ shim::{switch_tab_to, switch_to_input_mode},
};
use crate::{config::ZellijState, render::FormattedPart};
@@ -178,7 +178,7 @@ impl Widget for TabsWidget {
output
}
- fn process_click(&self, _name: &str, state: &ZellijState, pos: usize) {
+ fn process_click(&self, _name: &str, state: &ZellijState, pos: usize, is_double_click: bool) {
let mut offset = 0;
let mut counter = 0;
@@ -226,6 +226,13 @@ impl Widget for TabsWidget {
if pos > offset && pos < offset + content_len {
switch_tab_to(tab.position as u32 + 1);
+ if is_double_click {
+ // RenameTab acts on the ACTIVE tab, so the switch above has
+ // to happen first. Mirrors how the rename keybind works, so
+ // Enter commits and Esc cancels exactly as usual.
+ switch_to_input_mode(&InputMode::RenameTab);
+ }
+
break;
}
diff --git a/src/widgets/widget.rs b/src/widgets/widget.rs
index 7087cba..4745713 100644
--- a/src/widgets/widget.rs
+++ b/src/widgets/widget.rs
@@ -2,5 +2,9 @@ use crate::config::ZellijState;
pub trait Widget {
fn process(&self, name: &str, state: &ZellijState) -> String;
- fn process_click(&self, name: &str, state: &ZellijState, pos: usize);
+ /// `is_double_click` is true when this is the second click of a
+ /// double-click at the same position. Detected centrally in
+ /// ModuleConfig::handle_mouse_action, because widgets are held behind &self
+ /// and cannot keep click state of their own.
+ fn process_click(&self, name: &str, state: &ZellijState, pos: usize, is_double_click: bool);
}