From 5ee02e4d655b2dc38175806435fe0eb8487c61f4 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 16 Jun 2024 23:32:01 -0700 Subject: [PATCH 01/66] Attempt at float pane --- config/src/keyassignment.rs | 1 + mux/src/domain.rs | 28 +++++++ mux/src/lib.rs | 60 +++++++++++++ mux/src/tab.rs | 97 ++++++++++++++++++++++ wezterm-gui/src/commands.rs | 30 +++++++ wezterm-gui/src/spawn.rs | 26 ++++++ wezterm-gui/src/termwindow/mod.rs | 7 ++ wezterm-gui/src/termwindow/render/paint.rs | 12 ++- 8 files changed, 260 insertions(+), 1 deletion(-) diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index ed284a5ec98..939f281fca8 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -549,6 +549,7 @@ pub enum KeyAssignment { SpawnCommandInNewWindow(SpawnCommand), SplitHorizontal(SpawnCommand), SplitVertical(SpawnCommand), + FloatPane(SpawnCommand), ShowLauncher, ShowLauncherArgs(LauncherActionArgs), ClearScrollback(ScrollbackEraseMode), diff --git a/mux/src/domain.rs b/mux/src/domain.rs index 3cb3767d5c3..98d57dc42a8 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -71,6 +71,34 @@ pub trait Domain: Downcast + Send + Sync { Ok(tab) } + async fn add_float_pane( + &self, + tab: TabId, + pane_id: PaneId, + command_builder: Option, + command_dir: Option + ) -> anyhow::Result> { + let mux = Mux::get(); + let tab = match mux.get_tab(tab) { + Some(t) => t, + None => anyhow::bail!("Invalid tab id {}", tab), + }; + + let pane_index = match tab + .iter_panes_ignoring_zoom() + .iter() + .find(|p| p.pane.pane_id() == pane_id) + { + Some(p) => p.index, + None => anyhow::bail!("invalid pane id {}", pane_id), + }; + + let float_size = tab.compute_float_size(); + let pane = self.spawn_pane(float_size, command_builder, command_dir) .await?; + tab.insert_float(pane_index, float_size, Arc::clone(&pane))?; + Ok(pane) + } + async fn split_pane( &self, source: SplitSource, diff --git a/mux/src/lib.rs b/mux/src/lib.rs index f46e5ee0896..9024e5bce1a 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -812,6 +812,15 @@ impl Mux { } } + fn remove_float_pane_internal(&self, pane_id: PaneId) { + log::debug!("removing float pane {}", pane_id); + if let Some(pane) = self.panes.write().remove(&pane_id).clone() { + log::debug!("killing float pane {}", pane_id); + pane.kill(); + self.notify(MuxNotification::PaneRemoved(pane_id)); + } + } + fn remove_tab_internal(&self, tab_id: TabId) -> Option> { log::debug!("remove_tab_internal tab {}", tab_id); @@ -876,6 +885,11 @@ impl Mux { self.prune_dead_windows(); } + pub fn remove_float_pane(&self, pane_id: PaneId) { + self.remove_float_pane_internal(pane_id); + self.prune_dead_windows(); + } + pub fn remove_tab(&self, tab_id: TabId) -> Option> { let tab = self.remove_tab_internal(tab_id); self.prune_dead_windows(); @@ -1169,6 +1183,52 @@ impl Mux { }) } + pub async fn float_pane( + &self, + // TODO: disambiguate with TabId + pane_id: PaneId, + command_builder: Option, + command: Option, + domain: config::keyassignment::SpawnTabDomain, + ) -> anyhow::Result<(Arc, TerminalSize)> { + let (_pane_domain_id, window_id, tab_id) = self + .resolve_pane_id(pane_id) + .ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?; + + let domain = self + .resolve_spawn_tab_domain(Some(pane_id), &domain) + .context("resolve_spawn_tab_domain")?; + + if domain.state() == DomainState::Detached { + domain.attach(Some(window_id)).await?; + } + + let current_pane = self + .get_pane(pane_id) + .ok_or_else(|| anyhow!("pane_id {} is invalid", pane_id))?; + let term_config = current_pane.get_config(); + + let pane = domain.add_float_pane(tab_id, pane_id, command_builder, command).await?; + + if let Some(config) = term_config { + pane.set_config(config); + } + + // FIXME: clipboard + + let dims = pane.get_dimensions(); + + let size = TerminalSize { + cols: dims.cols, + rows: dims.viewport_rows, + pixel_height: 0, // FIXME: split pane pixel dimensions + pixel_width: 0, + dpi: dims.dpi, + }; + + Ok((pane, size)) + } + pub async fn split_pane( &self, // TODO: disambiguate with TabId diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 8eec504fb5f..1984daef99f 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -45,6 +45,7 @@ struct TabInner { active: usize, zoomed: Option>, title: String, + float_pane: Option>, recency: Recency, } @@ -76,6 +77,7 @@ pub struct PositionedPane { pub pixel_height: usize, /// The pane instance pub pane: Arc, + pub is_float: bool, } impl std::fmt::Debug for PositionedPane { @@ -733,6 +735,12 @@ impl Tab { self.inner.lock().compute_split_size(pane_index, request) } + pub fn compute_float_size( + &self, + ) -> TerminalSize { + self.inner.lock().compute_float_size() + } + /// Split the pane that has pane_index in the given direction and assign /// the right/bottom pane of the newly created split to the provided Pane /// instance. Returns the resultant index of the newly inserted pane. @@ -748,6 +756,17 @@ impl Tab { .split_and_insert(pane_index, request, pane) } + pub fn insert_float( + &self, + pane_index: usize, + float_size: TerminalSize, + pane: Arc, + ) -> anyhow::Result { + self.inner + .lock() + .add_float_pane(pane_index, float_size, pane) + } + pub fn get_zoomed_pane(&self) -> Option> { self.inner.lock().get_zoomed_pane() } @@ -764,6 +783,7 @@ impl TabInner { zoomed: None, title: String::new(), recency: Recency::default(), + float_pane: None, } } @@ -1017,6 +1037,7 @@ impl TabInner { height: size.rows.into(), pixel_height: size.pixel_height.into(), pane: Arc::clone(zoomed), + is_float: false }); return panes; } @@ -1027,6 +1048,29 @@ impl TabInner { let root_size = self.size; let mut cursor = self.pane.take().unwrap().cursor(); + if let Some(float_pane) = self.float_pane.as_ref() { + let top = (root_size.rows as f32 * 0.10).floor() as usize; + let left = (root_size.cols as f32 * 0.10).floor() as usize; + let cols = (root_size.cols as f32 * 0.80).floor() as usize; + let rows = (root_size.rows as f32 * 0.80).floor() as usize; + let pixel_width = (cols as f32 * root_size.pixel_width as f32 * 0.80).floor() as usize; + let pixel_height = (cols as f32 * root_size.pixel_height as f32 * 0.80).floor() as usize; + + panes.push(PositionedPane { + index: 0, + is_active: true, + is_zoomed: zoomed_id == Some(float_pane.pane_id()), + left, + top, + width: cols, + height: rows, + pixel_width, + pixel_height, + pane: Arc::clone(float_pane), + is_float: true + }); + } + loop { if cursor.is_leaf() { let index = panes.len(); @@ -1063,6 +1107,7 @@ impl TabInner { pixel_width: dims.pixel_width as _, pixel_height: dims.pixel_height as _, pane, + is_float: false }); } @@ -1075,6 +1120,7 @@ impl TabInner { } } + panes } @@ -1696,6 +1742,15 @@ impl TabInner { self.active = active_idx.saturating_sub(removed_indices.len()); } + if let Some(float_pane) = &self.float_pane { + if float_pane.is_dead() { + let mux = Mux::get(); + let pane_id = float_pane.pane_id(); + self.float_pane = None; + mux.remove_float_pane(pane_id); + } + } + if !dead_panes.is_empty() && kill { let to_kill: Vec<_> = dead_panes.iter().map(|p| p.pane_id()).collect(); promise::spawn::spawn_into_main_thread(async move { @@ -1861,6 +1916,23 @@ impl TabInner { None } + fn compute_float_size( + &mut self + ) -> TerminalSize { + let cell_dims = self.cell_dimensions(); + let size = self.size; + let width = (size.cols as f32 * 0.80).floor() as usize; + let height = (size.rows as f32 * 0.80).floor() as usize; + + TerminalSize { + rows: height as _, + cols: width as _, + pixel_height: cell_dims.pixel_height * height, + pixel_width: cell_dims.pixel_width * width, + dpi: cell_dims.dpi, + } + } + fn compute_split_size( &mut self, pane_index: usize, @@ -1952,6 +2024,31 @@ impl TabInner { }) } + fn add_float_pane( + &mut self, + pane_index: usize, + float_size: TerminalSize, + pane: Arc, + ) -> anyhow::Result { + if self.zoomed.is_some() { + anyhow::bail!("cannot add float while zoomed"); + } + + if self.float_pane.is_some() { + anyhow::bail!("cannot add float while another float is active") + } + + self.float_pane = Some(Arc::clone(&pane)); + { + pane.resize(float_size)?; + } + + log::debug!("split info after split: {:#?}", self.iter_splits()); + log::debug!("pane info after split: {:#?}", self.iter_panes()); + + Ok(pane_index + 1) + } + fn split_and_insert( &mut self, pane_index: usize, diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 321925e7dfa..3a925bf82b1 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1445,6 +1445,23 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + brief: label_string(action, "Create a float pane".to_string()).into(), + doc: "Create a float pane" + .into(), + keys: vec![( + Modifiers::CTRL + .union(Modifiers::ALT) + .union(Modifiers::SHIFT), + "p".into(), + )], + args: &[ArgType::ActivePane], + menubar: &["Shell"], + icon: Some("cod_split_vertical"), + }, SplitVertical(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, .. @@ -1501,6 +1518,15 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + brief: label_string(action, "Create a float pane".to_string()).into(), + doc: "Create a float pane" + .into(), + keys: vec![], + args: &[ArgType::ActivePane], + menubar: &[], + icon: Some("cod_split_vertical"), + }, AdjustPaneSize(PaneDirection::Left, amount) => CommandDef { brief: format!("Resize Pane {amount} cell(s) to the Left").into(), doc: "Adjusts the closest split divider to the left".into(), @@ -2025,6 +2051,10 @@ fn compute_default_actions() -> Vec { // ----------------- Shell SpawnTab(SpawnTabDomain::CurrentPaneDomain), SpawnWindow, + FloatPane(SpawnCommand { + domain: SpawnTabDomain::CurrentPaneDomain, + ..Default::default() + }), SplitVertical(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() diff --git a/wezterm-gui/src/spawn.rs b/wezterm-gui/src/spawn.rs index 31ba3268603..eba1c3746cc 100644 --- a/wezterm-gui/src/spawn.rs +++ b/wezterm-gui/src/spawn.rs @@ -15,6 +15,7 @@ pub enum SpawnWhere { NewWindow, NewTab, SplitPane(SplitRequest), + Float, } pub fn spawn_command_impl( @@ -113,6 +114,31 @@ pub async fn spawn_command_internal( bail!("there is no active tab while splitting pane!?"); } } + SpawnWhere::Float => { + let src_window_id = match src_window_id { + Some(id) => id, + None => anyhow::bail!("no src window when float a pane?"), + }; + if let Some(tab) = mux.get_active_tab_for_window(src_window_id) { + let pane = tab + .get_active_pane() + .ok_or_else(|| anyhow!("tab to have a pane"))?; + + log::trace!("doing float_pane"); + let (pane, _size) = mux + .float_pane( + pane.pane_id(), + cmd_builder, + cwd, + spawn.domain, + ) + .await + .context("float_pane")?; + pane.set_config(term_config); + } else { + bail!("there is no active tab while floating pane!?"); + } + } _ => { let (_tab, pane, window_id) = mux .spawn_tab_or_window( diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 71ece24c2df..2014abc03c7 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -2591,6 +2591,12 @@ impl TermWindow { }), ); } + FloatPane(spawn) => { + log::trace!("FloatPane {:?}", spawn); + self.spawn_command( + spawn, + SpawnWhere::Float); + } ToggleFullScreen => { self.window.as_ref().unwrap().toggle_fullscreen(); } @@ -3419,6 +3425,7 @@ impl TermWindow { pixel_width: size.cols as usize * self.render_metrics.cell_size.width as usize, pixel_height: size.rows as usize * self.render_metrics.cell_size.height as usize, pane, + is_float: false }] } else { let mut panes = tab.iter_panes(); diff --git a/wezterm-gui/src/termwindow/render/paint.rs b/wezterm-gui/src/termwindow/render/paint.rs index 5218d2d77ed..8211e0390e2 100644 --- a/wezterm-gui/src/termwindow/render/paint.rs +++ b/wezterm-gui/src/termwindow/render/paint.rs @@ -180,6 +180,12 @@ impl crate::TermWindow { .layer_for_zindex(0) .context("layer_for_zindex(0)")?; let mut layers = layer.quad_allocator(); + + let float_layer = gl_state + .layer_for_zindex(2) + .context("layer_for_zindex(2)")?; + let mut float_layers = float_layer.quad_allocator(); + log::trace!("quad map elapsed {:?}", start.elapsed()); metrics::histogram!("quad.map").record(start.elapsed()); @@ -254,7 +260,11 @@ impl crate::TermWindow { mux::Mux::get().record_focus_for_current_identity(pos.pane.pane_id()); } } - self.paint_pane(&pos, &mut layers).context("paint_pane")?; + if pos.is_float { + self.paint_pane(&pos, &mut float_layers).context("paint_pane")?; + } else { + self.paint_pane(&pos, &mut layers).context("paint_pane")?; + } } if let Some(pane) = self.get_active_pane_or_overlay() { From 518e0c831874b32d823af66c0c6a3b16151f96d1 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Mon, 17 Jun 2024 18:59:28 -0700 Subject: [PATCH 02/66] Try to prevent changes when a float is being shown --- mux/src/tab.rs | 14 ++++++++++---- wezterm-gui/src/termwindow/mod.rs | 14 ++++++++++---- wezterm-gui/src/termwindow/mouseevent.rs | 5 +++++ wezterm-gui/src/termwindow/palette.rs | 15 ++++++++++++++- wezterm-gui/src/termwindow/spawn.rs | 9 +++++++++ 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 1984daef99f..e96252ffd79 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -691,6 +691,10 @@ impl Tab { self.inner.lock().is_dead() } + pub fn is_float_active(&self) -> bool { + self.inner.lock().is_float_active() + } + pub fn get_active_pane(&self) -> Option> { self.inner.lock().get_active_pane() } @@ -1059,7 +1063,7 @@ impl TabInner { panes.push(PositionedPane { index: 0, is_active: true, - is_zoomed: zoomed_id == Some(float_pane.pane_id()), + is_zoomed: false, left, top, width: cols, @@ -1787,6 +1791,10 @@ impl TabInner { dead_count == panes.len() } + fn is_float_active(&self) -> bool { + return self.float_pane.is_some(); + } + fn get_active_pane(&mut self) -> Option> { if let Some(zoomed) = self.zoomed.as_ref() { return Some(Arc::clone(zoomed)); @@ -2041,11 +2049,9 @@ impl TabInner { self.float_pane = Some(Arc::clone(&pane)); { pane.resize(float_size)?; + self.set_active_idx(0); } - log::debug!("split info after split: {:#?}", self.iter_splits()); - log::debug!("pane info after split: {:#?}", self.iter_panes()); - Ok(pane_index + 1) } diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 2014abc03c7..9f2de5f0f1c 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -541,10 +541,6 @@ impl TermWindow { // force cursor to be repainted window.invalidate(); - if let Some(pane) = self.get_active_pane_or_overlay() { - pane.focus_changed(focused); - } - self.update_title(); self.emit_window_event("window-focus-changed", None); } @@ -3334,6 +3330,16 @@ impl TermWindow { } } + pub fn is_float_active(&self) -> bool { + let mux = Mux::get(); + let tab = match mux.get_active_tab_for_window(self.mux_window_id) { + Some(tab) => tab, + None => return false, + }; + + return tab.is_float_active(); + } + fn get_splits(&mut self) -> Vec { let mux = Mux::get(); let tab = match mux.get_active_tab_for_window(self.mux_window_id) { diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index 65289d44876..f70db22ceac 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -667,6 +667,11 @@ impl super::TermWindow { Some(MouseCapture::TerminalPane(_)) ); + //is there a better place to put this? + if self.is_float_active() { + return; + } + for pos in self.get_panes_to_render() { if !is_already_captured && row >= pos.top as i64 diff --git a/wezterm-gui/src/termwindow/palette.rs b/wezterm-gui/src/termwindow/palette.rs index 0522c130b59..2a53bb34ff9 100644 --- a/wezterm-gui/src/termwindow/palette.rs +++ b/wezterm-gui/src/termwindow/palette.rs @@ -92,6 +92,7 @@ fn build_commands( gui_window: GuiWin, pane: Option, filter_copy_mode: bool, + is_float_active: bool ) -> Vec { let mut commands = CommandDef::actions_for_palette_and_menubar(&config::configuration()); @@ -139,6 +140,18 @@ fn build_commands( } }); + //is there a better way to do this? + commands.retain(|cmd| { + if is_float_active { + !matches!(cmd.action, KeyAssignment::PaneSelect(_)) + && !matches!(cmd.action, KeyAssignment::SplitPane(_)) + && !matches!(cmd.action, KeyAssignment::SplitHorizontal(_)) + && !matches!(cmd.action, KeyAssignment::SplitVertical(_)) + } else { + true + } + }); + let mut scores: HashMap<&str, f64> = HashMap::new(); let recents = load_recents(); if let Ok(recents) = &recents { @@ -233,7 +246,7 @@ impl CommandPalette { .get_active_pane_or_overlay() .map(|pane| MuxPane(pane.pane_id())); - let commands = build_commands(GuiWin::new(term_window), mux_pane, filter_copy_mode); + let commands = build_commands(GuiWin::new(term_window), mux_pane, filter_copy_mode, term_window.is_float_active()); Self { element: RefCell::new(None), diff --git a/wezterm-gui/src/termwindow/spawn.rs b/wezterm-gui/src/termwindow/spawn.rs index 1395afc30e1..d5ffb1ef115 100644 --- a/wezterm-gui/src/termwindow/spawn.rs +++ b/wezterm-gui/src/termwindow/spawn.rs @@ -5,6 +5,15 @@ use std::sync::Arc; impl super::TermWindow { pub fn spawn_command(&self, spawn: &SpawnCommand, spawn_where: SpawnWhere) { + if self.is_float_active() { + match spawn_where{ + SpawnWhere::SplitPane(_) => { + return; + } + _ => {} + } + } + let size = if spawn_where == SpawnWhere::NewWindow { self.config.initial_size( self.dimensions.dpi as u32, From be7d66900fddc4054e20499f442badf435d09a38 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 18 Jun 2024 19:59:10 -0700 Subject: [PATCH 03/66] Trying to get cli float-pane to work --- codec/src/lib.rs | 9 ++++ config/src/config.rs | 3 ++ mux/src/tab.rs | 36 ++++++++++++---- wezterm-client/src/client.rs | 1 + wezterm-client/src/domain.rs | 37 +++++++++++++++- wezterm-mux-server-impl/src/sessionhandler.rs | 36 ++++++++++++++++ wezterm/src/cli/mod.rs | 9 ++++ wezterm/src/cli/split_pane.rs | 43 +++++++++++++++++++ 8 files changed, 165 insertions(+), 9 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 1f200d877a5..6ce72ccda14 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -502,6 +502,7 @@ pdu! { GetPaneDirection: 60, GetPaneDirectionResponse: 61, AdjustPaneSize: 62, + FloatPane: 63, } impl Pdu { @@ -649,6 +650,14 @@ pub struct ListPanesResponse { pub window_titles: HashMap, } +#[derive(Deserialize, Serialize, PartialEq, Debug)] +pub struct FloatPane { + pub pane_id: PaneId, + pub command: Option, + pub command_dir: Option, + pub domain: config::keyassignment::SpawnTabDomain, +} + #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct SplitPane { pub pane_id: PaneId, diff --git a/config/src/config.rs b/config/src/config.rs index 5b099edc7ad..5629332f8fb 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -515,6 +515,9 @@ pub struct Config { #[dynamic(default)] pub window_padding: WindowPadding, + #[dynamic(default)] + pub float_pane_padding: WindowPadding, + /// Specifies the path to a background image attachment file. /// The file can be any image format that the rust `image` /// crate is able to identify and load. diff --git a/mux/src/tab.rs b/mux/src/tab.rs index e96252ffd79..ed33916d745 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1053,19 +1053,39 @@ impl TabInner { let mut cursor = self.pane.take().unwrap().cursor(); if let Some(float_pane) = self.float_pane.as_ref() { - let top = (root_size.rows as f32 * 0.10).floor() as usize; - let left = (root_size.cols as f32 * 0.10).floor() as usize; - let cols = (root_size.cols as f32 * 0.80).floor() as usize; - let rows = (root_size.rows as f32 * 0.80).floor() as usize; - let pixel_width = (cols as f32 * root_size.pixel_width as f32 * 0.80).floor() as usize; - let pixel_height = (cols as f32 * root_size.pixel_height as f32 * 0.80).floor() as usize; + let cell_width = root_size.pixel_width as f32 / root_size.cols as f32; + let cell_height = root_size.pixel_height as f32 / root_size.rows as f32; + let h_context = config::DimensionContext { + dpi: root_size.dpi as f32, + pixel_max: root_size.pixel_width as f32, + pixel_cell: cell_width, + }; + + let v_context = config::DimensionContext { + dpi: root_size.dpi as f32, + pixel_max: root_size.pixel_height as f32, + pixel_cell: cell_height, + }; + + let float_padding = configuration().float_pane_padding; + let top = float_padding.top.evaluate_as_pixels(v_context) as usize; + let bottom_padding = float_padding.bottom.evaluate_as_pixels(v_context) as usize; + let left = float_padding.left.evaluate_as_pixels(h_context) as usize; + let right_padding = float_padding.right.evaluate_as_pixels(h_context) as usize; + let pixel_width = root_size.pixel_width - right_padding - left; + let pixel_height = root_size.pixel_height - bottom_padding - top; + let cols = (pixel_width as f32 / cell_width) as usize + 1; + let rows = (pixel_height as f32 / cell_height) as usize; + + let left_cols = (left as f32 / cell_width) as usize; + let top_rows = (top as f32 / cell_height) as usize; panes.push(PositionedPane { index: 0, is_active: true, is_zoomed: false, - left, - top, + left: left_cols, + top: top_rows, width: cols, height: rows, pixel_width, diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index 0b07d6d95bc..518bd9e0ce8 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -1342,6 +1342,7 @@ impl Client { rpc!(list_panes, ListPanes = (), ListPanesResponse); rpc!(spawn_v2, SpawnV2, SpawnResponse); rpc!(split_pane, SplitPane, SpawnResponse); + rpc!(add_float_pane, FloatPane, SpawnResponse); rpc!( move_pane_to_new_tab, MovePaneToNewTab, diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 7efb1d231c0..27c725248ea 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -2,7 +2,7 @@ use crate::client::Client; use crate::pane::ClientPane; use anyhow::{anyhow, bail}; use async_trait::async_trait; -use codec::{ListPanesResponse, SpawnV2, SplitPane}; +use codec::{FloatPane, ListPanesResponse, SpawnV2, SplitPane}; use config::keyassignment::SpawnTabDomain; use config::{SshDomain, TlsDomainClient, UnixDomain}; use mux::connui::{ConnectionUI, ConnectionUIParams}; @@ -928,6 +928,41 @@ impl Domain for ClientDomain { Ok(pane) } + async fn add_float_pane( + &self, + tab_id: TabId, + pane_id: PaneId, + command: Option, + command_dir: Option + ) -> anyhow::Result> { + let inner = self + .inner() + .ok_or_else(|| anyhow!("domain is not attached"))?; + + let mux = Mux::get(); + + let result = inner + .client + .add_float_pane(FloatPane{ + pane_id, + command, + command_dir, + domain: SpawnTabDomain::CurrentPaneDomain + }).await?; + + let pane: Arc = Arc::new(ClientPane::new( + &inner, + result.tab_id, + result.pane_id, + result.size, + "wezterm", + )); + + mux.add_pane(&pane)?; + + Ok(pane) + } + async fn attach(&self, window_id: Option) -> anyhow::Result<()> { if self.state() == DomainState::Attached { // Already attached diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index a8a497cdf32..c6555730d44 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -724,6 +724,14 @@ impl SessionHandler { .detach(); } + Pdu::FloatPane(float) => { + let client_id = self.client_id.clone(); + spawn_into_main_thread(async move { + schedule_float_pane(float, send_response, client_id); + }) + .detach(); + } + Pdu::MovePaneToNewTab(request) => { let client_id = self.client_id.clone(); spawn_into_main_thread(async move { @@ -1068,6 +1076,34 @@ async fn split_pane(split: SplitPane, client_id: Option>) -> anyho })) } +fn schedule_float_pane(float: FloatPane, send_response: SND, client_id: Option>) + where + SND: Fn(anyhow::Result) + 'static, +{ + promise::spawn::spawn(async move { send_response(float_pane(float, client_id).await) }) + .detach(); +} + +async fn float_pane(float_pane: FloatPane, client_id: Option>) -> anyhow::Result { + let mux = Mux::get(); + let _identity = mux.with_identity(client_id); + + let (_pane_domain_id, window_id, tab_id) = mux + .resolve_pane_id(float_pane.pane_id) + .ok_or_else(|| anyhow!("pane_id {} invalid", float_pane.pane_id))?; + + let (pane, size) = mux + .float_pane(float_pane.pane_id, float_pane.command, float_pane.command_dir, float_pane.domain) + .await?; + + Ok::(Pdu::SpawnResponse(SpawnResponse { + pane_id: pane.pane_id(), + tab_id, + window_id, + size, + })) +} + async fn domain_spawn_v2(spawn: SpawnV2, client_id: Option>) -> anyhow::Result { let mux = Mux::get(); let _identity = mux.with_identity(client_id); diff --git a/wezterm/src/cli/mod.rs b/wezterm/src/cli/mod.rs index b9325957b1e..a72aaa6afd1 100644 --- a/wezterm/src/cli/mod.rs +++ b/wezterm/src/cli/mod.rs @@ -103,6 +103,14 @@ Outputs the pane-id for the newly created pane on success" )] SplitPane(split_pane::SplitPane), + #[command( + name = "float-pane", + rename_all = "kebab", + trailing_var_arg = true, + about = "spawn a float pane" + )] + FloatPane(split_pane::FloatPane), + #[command( name = "spawn", trailing_var_arg = true, @@ -184,6 +192,7 @@ async fn run_cli_async(opts: &crate::Opt, cli: CliCommand) -> anyhow::Result<()> CliSubCommand::List(cmd) => cmd.run(client).await, CliSubCommand::MovePaneToNewTab(cmd) => cmd.run(client).await, CliSubCommand::SplitPane(cmd) => cmd.run(client).await, + CliSubCommand::FloatPane(cmd) => cmd.run(client).await, CliSubCommand::SendText(cmd) => cmd.run(client).await, CliSubCommand::GetText(cmd) => cmd.run(client).await, CliSubCommand::SpawnCommand(cmd) => cmd.run(client, &crate::init_config(opts)?).await, diff --git a/wezterm/src/cli/split_pane.rs b/wezterm/src/cli/split_pane.rs index e51f5082039..e763bd1b04f 100644 --- a/wezterm/src/cli/split_pane.rs +++ b/wezterm/src/cli/split_pane.rs @@ -113,3 +113,46 @@ impl SplitPane { Ok(()) } } + +#[derive(Debug, Parser, Clone)] +pub struct FloatPane { + /// Specify the pane that should be split. + /// The default is to use the current pane based on the + /// environment variable WEZTERM_PANE. + #[arg(long)] + pane_id: Option, + + /// Specify the current working directory for the initially + /// spawned program + #[arg(long, value_parser, value_hint=ValueHint::DirPath)] + cwd: Option, + + /// Instead of executing your shell, run PROG. + /// For example: `wezterm cli split-pane -- bash -l` will spawn bash + /// as if it were a login shell. + #[arg(value_parser, value_hint=ValueHint::CommandWithArguments, num_args=1..)] + prog: Vec, +} + +impl FloatPane { + pub async fn run(self, client: Client) -> anyhow::Result<()> { + let pane_id = client.resolve_pane_id(self.pane_id).await?; + let spawned = client + .add_float_pane(codec::FloatPane { + pane_id, + domain: config::keyassignment::SpawnTabDomain::CurrentPaneDomain, + command: if self.prog.is_empty() { + None + } else { + let builder = CommandBuilder::from_argv(self.prog); + Some(builder) + }, + command_dir: resolve_relative_cwd(self.cwd)? + }) + .await?; + + log::debug!("{:?}", spawned); + println!("{}", spawned.pane_id); + Ok(()) + } +} From 6da5f03216eb907560d9b8b101fa21a23bdbb517 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 18 Jun 2024 22:17:34 -0700 Subject: [PATCH 04/66] Reverted changes to allow float pane size configuration --- mux/src/tab.rs | 36 ++++++++---------------------------- wezterm-client/src/domain.rs | 2 +- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index ed33916d745..e96252ffd79 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1053,39 +1053,19 @@ impl TabInner { let mut cursor = self.pane.take().unwrap().cursor(); if let Some(float_pane) = self.float_pane.as_ref() { - let cell_width = root_size.pixel_width as f32 / root_size.cols as f32; - let cell_height = root_size.pixel_height as f32 / root_size.rows as f32; - let h_context = config::DimensionContext { - dpi: root_size.dpi as f32, - pixel_max: root_size.pixel_width as f32, - pixel_cell: cell_width, - }; - - let v_context = config::DimensionContext { - dpi: root_size.dpi as f32, - pixel_max: root_size.pixel_height as f32, - pixel_cell: cell_height, - }; - - let float_padding = configuration().float_pane_padding; - let top = float_padding.top.evaluate_as_pixels(v_context) as usize; - let bottom_padding = float_padding.bottom.evaluate_as_pixels(v_context) as usize; - let left = float_padding.left.evaluate_as_pixels(h_context) as usize; - let right_padding = float_padding.right.evaluate_as_pixels(h_context) as usize; - let pixel_width = root_size.pixel_width - right_padding - left; - let pixel_height = root_size.pixel_height - bottom_padding - top; - let cols = (pixel_width as f32 / cell_width) as usize + 1; - let rows = (pixel_height as f32 / cell_height) as usize; - - let left_cols = (left as f32 / cell_width) as usize; - let top_rows = (top as f32 / cell_height) as usize; + let top = (root_size.rows as f32 * 0.10).floor() as usize; + let left = (root_size.cols as f32 * 0.10).floor() as usize; + let cols = (root_size.cols as f32 * 0.80).floor() as usize; + let rows = (root_size.rows as f32 * 0.80).floor() as usize; + let pixel_width = (cols as f32 * root_size.pixel_width as f32 * 0.80).floor() as usize; + let pixel_height = (cols as f32 * root_size.pixel_height as f32 * 0.80).floor() as usize; panes.push(PositionedPane { index: 0, is_active: true, is_zoomed: false, - left: left_cols, - top: top_rows, + left, + top, width: cols, height: rows, pixel_width, diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 27c725248ea..8d6a116d158 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -930,7 +930,7 @@ impl Domain for ClientDomain { async fn add_float_pane( &self, - tab_id: TabId, + _tab_id: TabId, pane_id: PaneId, command: Option, command_dir: Option From 83b7974b7399e9893030adb225cf40d8edd14209 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 19 Jun 2024 20:29:50 -0700 Subject: [PATCH 05/66] Experiment with float borders --- mux/src/domain.rs | 6 +- mux/src/lib.rs | 2 +- mux/src/tab.rs | 76 +++++++++++++--------- wezterm-gui/src/termwindow/mod.rs | 16 +++-- wezterm-gui/src/termwindow/render/paint.rs | 7 ++ wezterm-gui/src/termwindow/render/split.rs | 66 ++++++++++++++++++- 6 files changed, 134 insertions(+), 39 deletions(-) diff --git a/mux/src/domain.rs b/mux/src/domain.rs index 98d57dc42a8..98bf4fe755a 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -93,9 +93,9 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("invalid pane id {}", pane_id), }; - let float_size = tab.compute_float_size(); - let pane = self.spawn_pane(float_size, command_builder, command_dir) .await?; - tab.insert_float(pane_index, float_size, Arc::clone(&pane))?; + let float_pos = tab.get_float_pos(); + let pane = self.spawn_pane(float_pos.size, command_builder, command_dir) .await?; + tab.insert_float(pane_index, float_pos.size, Arc::clone(&pane))?; Ok(pane) } diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 9024e5bce1a..e2235a0d613 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1,7 +1,7 @@ use crate::client::{ClientId, ClientInfo}; use crate::pane::{CachePolicy, Pane, PaneId}; use crate::ssh_agent::AgentProxy; -use crate::tab::{SplitRequest, Tab, TabId}; +use crate::tab::{PositionedPane, SplitRequest, Tab, TabId}; use crate::window::{Window, WindowId}; use anyhow::{anyhow, Context, Error}; use config::keyassignment::SpawnTabDomain; diff --git a/mux/src/tab.rs b/mux/src/tab.rs index e96252ffd79..c227adfdf7f 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; use std::convert::TryInto; use std::sync::Arc; use url::Url; -use wezterm_term::{StableRowIndex, TerminalSize}; +use wezterm_term::{Position, StableRowIndex, TerminalSize}; pub type Tree = bintree::Tree, SplitDirectionAndSize>; pub type Cursor = bintree::Cursor, SplitDirectionAndSize>; @@ -207,6 +207,17 @@ pub struct PositionedSplit { pub size: usize, } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct PositionedFloat { + /// The offset from the top left corner of the containing tab to the top + /// left corner of this split, in cells. + pub left: usize, + /// The offset from the top left corner of the containing tab to the top + /// left corner of this split, in cells. + pub top: usize, + pub size: TerminalSize +} + fn is_pane(pane: &Arc, other: &Option<&Arc>) -> bool { if let Some(other) = other { other.pane_id() == pane.pane_id() @@ -739,10 +750,10 @@ impl Tab { self.inner.lock().compute_split_size(pane_index, request) } - pub fn compute_float_size( + pub fn get_float_pos( &self, - ) -> TerminalSize { - self.inner.lock().compute_float_size() + ) -> PositionedFloat { + self.inner.lock().get_float_pos() } /// Split the pane that has pane_index in the given direction and assign @@ -1053,23 +1064,18 @@ impl TabInner { let mut cursor = self.pane.take().unwrap().cursor(); if let Some(float_pane) = self.float_pane.as_ref() { - let top = (root_size.rows as f32 * 0.10).floor() as usize; - let left = (root_size.cols as f32 * 0.10).floor() as usize; - let cols = (root_size.cols as f32 * 0.80).floor() as usize; - let rows = (root_size.rows as f32 * 0.80).floor() as usize; - let pixel_width = (cols as f32 * root_size.pixel_width as f32 * 0.80).floor() as usize; - let pixel_height = (cols as f32 * root_size.pixel_height as f32 * 0.80).floor() as usize; + let float_pos = self.get_float_pos(); panes.push(PositionedPane { index: 0, is_active: true, is_zoomed: false, - left, - top, - width: cols, - height: rows, - pixel_width, - pixel_height, + left: float_pos.left, + top: float_pos.top, + width: float_pos.size.cols, + height: float_pos.size.rows, + pixel_width: float_pos.size.pixel_width, + pixel_height: float_pos.size.pixel_height, pane: Arc::clone(float_pane), is_float: true }); @@ -1924,21 +1930,31 @@ impl TabInner { None } - fn compute_float_size( - &mut self - ) -> TerminalSize { - let cell_dims = self.cell_dimensions(); - let size = self.size; - let width = (size.cols as f32 * 0.80).floor() as usize; - let height = (size.rows as f32 * 0.80).floor() as usize; + fn get_float_pos(&self) -> PositionedFloat { + let root_size = self.size; + let padding_percent = 0.10; + let content_percent = 1.0 - (padding_percent * 2.0); + let cell_width = root_size.pixel_width / root_size.cols; + let cell_height = root_size.pixel_height / root_size.rows; + let width = (root_size.cols as f32 * content_percent) as usize; + let height = (root_size.rows as f32 * content_percent) as usize; + + let top = (root_size.rows as f32 * padding_percent) as usize; + let left = (root_size.cols as f32 * padding_percent) as usize + 2; + + let size = TerminalSize{ + rows: height, + cols: width, + pixel_width: cell_width * width, + pixel_height: cell_height * height, + dpi: root_size.dpi, + }; - TerminalSize { - rows: height as _, - cols: width as _, - pixel_height: cell_dims.pixel_height * height, - pixel_width: cell_dims.pixel_width * width, - dpi: cell_dims.dpi, - } + PositionedFloat{ + left, + top, + size + } } fn compute_split_size( diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 9f2de5f0f1c..eca3843b99f 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -44,10 +44,7 @@ use mux::pane::{ CachePolicy, CloseReason, Pane, PaneId, Pattern as MuxPattern, PerformAssignmentResult, }; use mux::renderable::RenderableDimensions; -use mux::tab::{ - PositionedPane, PositionedSplit, SplitDirection, SplitRequest, SplitSize as MuxSplitSize, Tab, - TabId, -}; +use mux::tab::{PositionedFloat, PositionedPane, PositionedSplit, SplitDirection, SplitRequest, SplitSize as MuxSplitSize, Tab, TabId}; use mux::window::WindowId as MuxWindowId; use mux::{Mux, MuxNotification}; use mux_lua::MuxPane; @@ -3356,6 +3353,17 @@ impl TermWindow { } } + fn get_float_pos(&mut self) -> Option { + let mux = Mux::get(); + let tab = match mux.get_active_tab_for_window(self.mux_window_id) { + Some(tab) => tab, + None => return None, + }; + + let float_pos = tab.get_float_pos(); + Some(float_pos) + } + fn pos_pane_to_pane_info(pos: &PositionedPane) -> PaneInformation { PaneInformation { pane_id: pos.pane.pane_id(), diff --git a/wezterm-gui/src/termwindow/render/paint.rs b/wezterm-gui/src/termwindow/render/paint.rs index 8211e0390e2..44d44493910 100644 --- a/wezterm-gui/src/termwindow/render/paint.rs +++ b/wezterm-gui/src/termwindow/render/paint.rs @@ -275,6 +275,13 @@ impl crate::TermWindow { } } + if let Some(pane) = self.get_active_pane_or_overlay() { + if self.is_float_active() { + let float = self.get_float_pos(); + self.paint_float(float.unwrap(), &mut layers).context("paint_float")?; + } + } + if self.show_tab_bar { self.paint_tab_bar(&mut layers).context("paint_tab_bar")?; } diff --git a/wezterm-gui/src/termwindow/render/split.rs b/wezterm-gui/src/termwindow/render/split.rs index 745530161cf..8264a238be9 100644 --- a/wezterm-gui/src/termwindow/render/split.rs +++ b/wezterm-gui/src/termwindow/render/split.rs @@ -1,7 +1,7 @@ use crate::termwindow::render::TripleLayerQuadAllocator; use crate::termwindow::{UIItem, UIItemType}; use mux::pane::Pane; -use mux::tab::{PositionedSplit, SplitDirection}; +use mux::tab::{PositionedFloat, PositionedSplit, SplitDirection}; use std::sync::Arc; impl crate::TermWindow { @@ -78,4 +78,68 @@ impl crate::TermWindow { Ok(()) } + + pub fn paint_float( + &mut self, + pos: PositionedFloat, + layers: &mut TripleLayerQuadAllocator, + ) -> anyhow::Result<()> { + let foreground = self.palette().foreground; + + let cell_height = self.render_metrics.cell_size.height; + let cell_width = self.render_metrics.cell_size.width; + let half_cell_height = cell_height as f32 / 2.0; + let half_cell_width = cell_width as f32 / 2.0; + let pos_y = (pos.top as f32 * self.render_metrics.cell_size.height as f32) - self.render_metrics.underline_height as f32 - half_cell_height; + let pos_x = (pos.left as f32 * self.render_metrics.cell_size.width as f32) - self.render_metrics.underline_height as f32 - half_cell_width; + let pixel_height = pos.size.pixel_height as f32 + self.render_metrics.underline_height as f32 + cell_height as f32; + let pixel_width = pos.size.pixel_width as f32 + self.render_metrics.underline_height as f32 + cell_width as f32; + + self.filled_rectangle( + layers, + 2, + euclid::rect( + pos_x, + pos_y, + self.render_metrics.underline_height as f32, + pixel_height as f32, + ), + foreground.to_linear(), + )?; + self.filled_rectangle( + layers, + 2, + euclid::rect( + pos_x, + pos_y, + pixel_width, + self.render_metrics.underline_height as f32, + ), + foreground.to_linear(), + )?; + self.filled_rectangle( + layers, + 2, + euclid::rect( + pos_x, + pos_y + pixel_height, + pixel_width, + self.render_metrics.underline_height as f32, + ), + foreground.to_linear(), + )?; + self.filled_rectangle( + layers, + 2, + euclid::rect( + pos_x + pixel_width, + pos_y, + self.render_metrics.underline_height as f32, + pixel_height as f32, + ), + foreground.to_linear(), + )?; + + Ok(()) + } } From 44c8a21d1b58b703edfbd436b2ad278b27120333 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Thu, 20 Jun 2024 23:13:59 -0700 Subject: [PATCH 06/66] float padding config --- config/src/config.rs | 11 +++++- mux/src/tab.rs | 43 +++++++++++++++------- wezterm-gui/src/termwindow/render/split.rs | 9 +++-- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/config/src/config.rs b/config/src/config.rs index 5629332f8fb..0892dd179db 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -515,7 +515,7 @@ pub struct Config { #[dynamic(default)] pub window_padding: WindowPadding, - #[dynamic(default)] + #[dynamic(default = "default_float_pane_padding")] pub float_pane_padding: WindowPadding, /// Specifies the path to a background image attachment file. @@ -1576,6 +1576,15 @@ fn default_integrated_title_buttons() -> Vec { vec![Hide, Maximize, Close] } +fn default_float_pane_padding() -> WindowPadding { + WindowPadding{ + left: Dimension::Percent(0.20), + top: Dimension::Percent(0.20), + right: Dimension::Percent(0.20), + bottom: Dimension::Percent(0.20), + } +} + fn default_char_select_font_size() -> f64 { 18.0 } diff --git a/mux/src/tab.rs b/mux/src/tab.rs index c227adfdf7f..9b4271fcc7e 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1932,27 +1932,42 @@ impl TabInner { fn get_float_pos(&self) -> PositionedFloat { let root_size = self.size; - let padding_percent = 0.10; - let content_percent = 1.0 - (padding_percent * 2.0); - let cell_width = root_size.pixel_width / root_size.cols; - let cell_height = root_size.pixel_height / root_size.rows; - let width = (root_size.cols as f32 * content_percent) as usize; - let height = (root_size.rows as f32 * content_percent) as usize; - let top = (root_size.rows as f32 * padding_percent) as usize; - let left = (root_size.cols as f32 * padding_percent) as usize + 2; + let cell_width = root_size.pixel_width as f32 / root_size.cols as f32; + let cell_height = root_size.pixel_height as f32 / root_size.rows as f32; + let h_context = config::DimensionContext { + dpi: root_size.dpi as f32, + pixel_max: root_size.pixel_width as f32, + pixel_cell: cell_width, + }; + + let v_context = config::DimensionContext { + dpi: root_size.dpi as f32, + pixel_max: root_size.pixel_height as f32, + pixel_cell: cell_height, + }; + + let float_padding = configuration().float_pane_padding; + let top_padding_pixels = float_padding.top.evaluate_as_pixels(v_context) as usize; + let bottom_padding_pixels = float_padding.bottom.evaluate_as_pixels(v_context) as usize; + let left_padding_pixels = float_padding.left.evaluate_as_pixels(h_context) as usize; + let right_padding_pixels = float_padding.right.evaluate_as_pixels(h_context) as usize; + let pixel_width = root_size.pixel_width - right_padding_pixels - left_padding_pixels; + let pixel_height = root_size.pixel_height - bottom_padding_pixels - top_padding_pixels; + let cols = (pixel_width as f32 / cell_width) as usize; + let rows = (pixel_height as f32 / cell_height) as usize; let size = TerminalSize{ - rows: height, - cols: width, - pixel_width: cell_width * width, - pixel_height: cell_height * height, + rows, + cols, + pixel_width, + pixel_height, dpi: root_size.dpi, }; PositionedFloat{ - left, - top, + left: (left_padding_pixels as f32 / cell_width).ceil() as usize, + top: (top_padding_pixels as f32 / cell_height).ceil() as usize, size } } diff --git a/wezterm-gui/src/termwindow/render/split.rs b/wezterm-gui/src/termwindow/render/split.rs index 8264a238be9..33176ccb8ef 100644 --- a/wezterm-gui/src/termwindow/render/split.rs +++ b/wezterm-gui/src/termwindow/render/split.rs @@ -84,16 +84,17 @@ impl crate::TermWindow { pos: PositionedFloat, layers: &mut TripleLayerQuadAllocator, ) -> anyhow::Result<()> { + let (padding_left, padding_top) = self.padding_left_top(); let foreground = self.palette().foreground; let cell_height = self.render_metrics.cell_size.height; let cell_width = self.render_metrics.cell_size.width; let half_cell_height = cell_height as f32 / 2.0; let half_cell_width = cell_width as f32 / 2.0; - let pos_y = (pos.top as f32 * self.render_metrics.cell_size.height as f32) - self.render_metrics.underline_height as f32 - half_cell_height; - let pos_x = (pos.left as f32 * self.render_metrics.cell_size.width as f32) - self.render_metrics.underline_height as f32 - half_cell_width; - let pixel_height = pos.size.pixel_height as f32 + self.render_metrics.underline_height as f32 + cell_height as f32; - let pixel_width = pos.size.pixel_width as f32 + self.render_metrics.underline_height as f32 + cell_width as f32; + let pos_y = (pos.top as f32 * self.render_metrics.cell_size.height as f32) - self.render_metrics.underline_height as f32 - half_cell_height + padding_top; + let pos_x = (pos.left as f32 * self.render_metrics.cell_size.width as f32) - self.render_metrics.underline_height as f32 - half_cell_width + padding_left; + let pixel_height = (pos.size.rows as f32 * cell_height as f32) + self.render_metrics.underline_height as f32 + cell_height as f32; + let pixel_width = (pos.size.cols as f32 * cell_width as f32) + self.render_metrics.underline_height as f32 + cell_width as f32; self.filled_rectangle( layers, From 3bcf22923143c1f1fe33181031964b3410025409 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 23 Jun 2024 07:22:30 -0700 Subject: [PATCH 07/66] Remove float pane from tab.panes --- mux/src/tab.rs | 48 ++++++++++++++-------- wezterm-gui/src/spawn.rs | 7 ++++ wezterm-gui/src/termwindow/mod.rs | 26 ++++++++++++ wezterm-gui/src/termwindow/palette.rs | 15 +------ wezterm-gui/src/termwindow/render/paint.rs | 20 ++++----- wezterm-gui/src/termwindow/render/split.rs | 2 +- wezterm-gui/src/termwindow/spawn.rs | 9 ---- 7 files changed, 73 insertions(+), 54 deletions(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 9b4271fcc7e..6a95d4d2c58 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -590,6 +590,10 @@ impl Tab { self.inner.lock().iter_panes() } + pub fn get_float_pane(&self) -> Option { + self.inner.lock().get_float_pane() + } + pub fn iter_panes_ignoring_zoom(&self) -> Vec { self.inner.lock().iter_panes_ignoring_zoom() } @@ -966,6 +970,28 @@ impl TabInner { self.iter_panes_impl(true) } + fn get_float_pane(&self) -> Option { + let float_pos = self.get_float_pos(); + + if let Some(float_pane) = self.float_pane.as_ref() { + Some(PositionedPane { + index: 0, + is_active: true, + is_zoomed: false, + left: float_pos.left, + top: float_pos.top, + width: float_pos.size.cols, + height: float_pos.size.rows, + pixel_width: float_pos.size.pixel_width, + pixel_height: float_pos.size.pixel_height, + pane: Arc::clone(float_pane), + is_float: true + }) + } else { + None + } + } + /// Like iter_panes, except that it will include all panes, regardless of /// whether one of them is currently zoomed. fn iter_panes_ignoring_zoom(&mut self) -> Vec { @@ -1063,24 +1089,6 @@ impl TabInner { let root_size = self.size; let mut cursor = self.pane.take().unwrap().cursor(); - if let Some(float_pane) = self.float_pane.as_ref() { - let float_pos = self.get_float_pos(); - - panes.push(PositionedPane { - index: 0, - is_active: true, - is_zoomed: false, - left: float_pos.left, - top: float_pos.top, - width: float_pos.size.cols, - height: float_pos.size.rows, - pixel_width: float_pos.size.pixel_width, - pixel_height: float_pos.size.pixel_height, - pane: Arc::clone(float_pane), - is_float: true - }); - } - loop { if cursor.is_leaf() { let index = panes.len(); @@ -1806,6 +1814,10 @@ impl TabInner { return Some(Arc::clone(zoomed)); } + if let Some(float_pane) = self.float_pane.as_ref() { + return Some(Arc::clone(float_pane)); + } + self.iter_panes_ignoring_zoom() .iter() .nth(self.active) diff --git a/wezterm-gui/src/spawn.rs b/wezterm-gui/src/spawn.rs index eba1c3746cc..45a27b90b19 100644 --- a/wezterm-gui/src/spawn.rs +++ b/wezterm-gui/src/spawn.rs @@ -91,6 +91,10 @@ pub async fn spawn_command_internal( None => anyhow::bail!("no src window when splitting a pane?"), }; if let Some(tab) = mux.get_active_tab_for_window(src_window_id) { + if tab.is_float_active() { + bail!("cannot open split when float is active"); + } + let pane = tab .get_active_pane() .ok_or_else(|| anyhow!("tab to have a pane"))?; @@ -120,6 +124,9 @@ pub async fn spawn_command_internal( None => anyhow::bail!("no src window when float a pane?"), }; if let Some(tab) = mux.get_active_tab_for_window(src_window_id) { + if tab.is_float_active() { + bail!("cannot open split when float is active"); + } let pane = tab .get_active_pane() .ok_or_else(|| anyhow!("tab to have a pane"))?; diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index eca3843b99f..c5c3ef643ba 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -3353,6 +3353,22 @@ impl TermWindow { } } + fn get_float_pane(&mut self) -> Option> { + let mux = Mux::get(); + let tab = match mux.get_active_tab_for_window(self.mux_window_id) { + Some(tab) => tab, + None => return None, + }; + + let tab_id = tab.tab_id(); + + if self.tab_state(tab_id).overlay.is_some() { + None + } else { + self.get_float_pane() + } + } + fn get_float_pos(&mut self) -> Option { let mux = Mux::get(); let tab = match mux.get_active_tab_for_window(self.mux_window_id) { @@ -3462,6 +3478,16 @@ impl TermWindow { self.get_pos_panes_for_tab(&tab) } + fn get_float_pane_to_render(&self) -> Option { + let mux = Mux::get(); + let tab = match mux.get_active_tab_for_window(self.mux_window_id) { + Some(tab) => tab, + None => return None, + }; + + tab.get_float_pane() + } + /// if pane_id.is_none(), removes any overlay for the specified tab. /// Otherwise: if the overlay is the specified pane for that tab, remove it. fn cancel_overlay_for_tab(&mut self, tab_id: TabId, pane_id: Option) { diff --git a/wezterm-gui/src/termwindow/palette.rs b/wezterm-gui/src/termwindow/palette.rs index 2a53bb34ff9..0522c130b59 100644 --- a/wezterm-gui/src/termwindow/palette.rs +++ b/wezterm-gui/src/termwindow/palette.rs @@ -92,7 +92,6 @@ fn build_commands( gui_window: GuiWin, pane: Option, filter_copy_mode: bool, - is_float_active: bool ) -> Vec { let mut commands = CommandDef::actions_for_palette_and_menubar(&config::configuration()); @@ -140,18 +139,6 @@ fn build_commands( } }); - //is there a better way to do this? - commands.retain(|cmd| { - if is_float_active { - !matches!(cmd.action, KeyAssignment::PaneSelect(_)) - && !matches!(cmd.action, KeyAssignment::SplitPane(_)) - && !matches!(cmd.action, KeyAssignment::SplitHorizontal(_)) - && !matches!(cmd.action, KeyAssignment::SplitVertical(_)) - } else { - true - } - }); - let mut scores: HashMap<&str, f64> = HashMap::new(); let recents = load_recents(); if let Ok(recents) = &recents { @@ -246,7 +233,7 @@ impl CommandPalette { .get_active_pane_or_overlay() .map(|pane| MuxPane(pane.pane_id())); - let commands = build_commands(GuiWin::new(term_window), mux_pane, filter_copy_mode, term_window.is_float_active()); + let commands = build_commands(GuiWin::new(term_window), mux_pane, filter_copy_mode); Self { element: RefCell::new(None), diff --git a/wezterm-gui/src/termwindow/render/paint.rs b/wezterm-gui/src/termwindow/render/paint.rs index 44d44493910..56bbb0108ff 100644 --- a/wezterm-gui/src/termwindow/render/paint.rs +++ b/wezterm-gui/src/termwindow/render/paint.rs @@ -260,11 +260,14 @@ impl crate::TermWindow { mux::Mux::get().record_focus_for_current_identity(pos.pane.pane_id()); } } - if pos.is_float { - self.paint_pane(&pos, &mut float_layers).context("paint_pane")?; - } else { - self.paint_pane(&pos, &mut layers).context("paint_pane")?; - } + self.paint_pane(&pos, &mut layers).context("paint_pane")?; + } + + if let Some(float_pane) = self.get_float_pane_to_render() { + float_pane.pane.advise_focus(); + self.paint_pane(&float_pane, &mut float_layers).context("paint_pane")?; + let float = self.get_float_pos(); + self.paint_float_border(float.unwrap(), &mut layers).context("paint_float")?; } if let Some(pane) = self.get_active_pane_or_overlay() { @@ -275,13 +278,6 @@ impl crate::TermWindow { } } - if let Some(pane) = self.get_active_pane_or_overlay() { - if self.is_float_active() { - let float = self.get_float_pos(); - self.paint_float(float.unwrap(), &mut layers).context("paint_float")?; - } - } - if self.show_tab_bar { self.paint_tab_bar(&mut layers).context("paint_tab_bar")?; } diff --git a/wezterm-gui/src/termwindow/render/split.rs b/wezterm-gui/src/termwindow/render/split.rs index 33176ccb8ef..5dc82099304 100644 --- a/wezterm-gui/src/termwindow/render/split.rs +++ b/wezterm-gui/src/termwindow/render/split.rs @@ -79,7 +79,7 @@ impl crate::TermWindow { Ok(()) } - pub fn paint_float( + pub fn paint_float_border( &mut self, pos: PositionedFloat, layers: &mut TripleLayerQuadAllocator, diff --git a/wezterm-gui/src/termwindow/spawn.rs b/wezterm-gui/src/termwindow/spawn.rs index d5ffb1ef115..1395afc30e1 100644 --- a/wezterm-gui/src/termwindow/spawn.rs +++ b/wezterm-gui/src/termwindow/spawn.rs @@ -5,15 +5,6 @@ use std::sync::Arc; impl super::TermWindow { pub fn spawn_command(&self, spawn: &SpawnCommand, spawn_where: SpawnWhere) { - if self.is_float_active() { - match spawn_where{ - SpawnWhere::SplitPane(_) => { - return; - } - _ => {} - } - } - let size = if spawn_where == SpawnWhere::NewWindow { self.config.initial_size( self.dimensions.dpi as u32, From f9de55fa0141b89ef4a63f038a351070adf1e7ac Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 23 Jun 2024 07:51:51 -0700 Subject: [PATCH 08/66] Removed pane_id where not needed --- mux/src/domain.rs | 17 ++---- mux/src/tab.rs | 60 +++++++++------------- wezterm-gui/src/termwindow/mod.rs | 29 +---------- wezterm-gui/src/termwindow/render/paint.rs | 7 ++- wezterm-gui/src/termwindow/render/split.rs | 8 +-- 5 files changed, 35 insertions(+), 86 deletions(-) diff --git a/mux/src/domain.rs b/mux/src/domain.rs index 98bf4fe755a..e7e5a3e700f 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -74,7 +74,7 @@ pub trait Domain: Downcast + Send + Sync { async fn add_float_pane( &self, tab: TabId, - pane_id: PaneId, + _pane_id: PaneId, command_builder: Option, command_dir: Option ) -> anyhow::Result> { @@ -84,18 +84,9 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("Invalid tab id {}", tab), }; - let pane_index = match tab - .iter_panes_ignoring_zoom() - .iter() - .find(|p| p.pane.pane_id() == pane_id) - { - Some(p) => p.index, - None => anyhow::bail!("invalid pane id {}", pane_id), - }; - - let float_pos = tab.get_float_pos(); - let pane = self.spawn_pane(float_pos.size, command_builder, command_dir) .await?; - tab.insert_float(pane_index, float_pos.size, Arc::clone(&pane))?; + let float_size = tab.get_float_size(); + let pane = self.spawn_pane(float_size, command_builder, command_dir) .await?; + tab.insert_float(float_size, Arc::clone(&pane))?; Ok(pane) } diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 6a95d4d2c58..616b062ad4a 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -207,17 +207,6 @@ pub struct PositionedSplit { pub size: usize, } -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub struct PositionedFloat { - /// The offset from the top left corner of the containing tab to the top - /// left corner of this split, in cells. - pub left: usize, - /// The offset from the top left corner of the containing tab to the top - /// left corner of this split, in cells. - pub top: usize, - pub size: TerminalSize -} - fn is_pane(pane: &Arc, other: &Option<&Arc>) -> bool { if let Some(other) = other { other.pane_id() == pane.pane_id() @@ -754,10 +743,10 @@ impl Tab { self.inner.lock().compute_split_size(pane_index, request) } - pub fn get_float_pos( + pub fn get_float_size( &self, - ) -> PositionedFloat { - self.inner.lock().get_float_pos() + ) -> TerminalSize { + self.inner.lock().get_float_size() } /// Split the pane that has pane_index in the given direction and assign @@ -777,13 +766,12 @@ impl Tab { pub fn insert_float( &self, - pane_index: usize, float_size: TerminalSize, pane: Arc, - ) -> anyhow::Result { + ) -> anyhow::Result<()> { self.inner .lock() - .add_float_pane(pane_index, float_size, pane) + .add_float_pane(float_size, pane) } pub fn get_zoomed_pane(&self) -> Option> { @@ -971,19 +959,25 @@ impl TabInner { } fn get_float_pane(&self) -> Option { - let float_pos = self.get_float_pos(); - if let Some(float_pane) = self.float_pane.as_ref() { + let root_size = self.size; + let size = self.get_float_size(); + + let cell_height = root_size.pixel_height / root_size.rows; + let cell_width = root_size.pixel_width / root_size.cols; + let left = ((root_size.pixel_width - size.pixel_width) / 2) / cell_width; + let top = (root_size.pixel_height - size.pixel_height) / 2 / cell_height; + Some(PositionedPane { index: 0, is_active: true, is_zoomed: false, - left: float_pos.left, - top: float_pos.top, - width: float_pos.size.cols, - height: float_pos.size.rows, - pixel_width: float_pos.size.pixel_width, - pixel_height: float_pos.size.pixel_height, + left, + top, + width: size.cols, + height: size.rows, + pixel_width: size.pixel_width, + pixel_height: size.pixel_height, pane: Arc::clone(float_pane), is_float: true }) @@ -1942,7 +1936,7 @@ impl TabInner { None } - fn get_float_pos(&self) -> PositionedFloat { + fn get_float_size(&self) -> TerminalSize { let root_size = self.size; let cell_width = root_size.pixel_width as f32 / root_size.cols as f32; @@ -1969,18 +1963,12 @@ impl TabInner { let cols = (pixel_width as f32 / cell_width) as usize; let rows = (pixel_height as f32 / cell_height) as usize; - let size = TerminalSize{ + TerminalSize{ rows, cols, pixel_width, pixel_height, dpi: root_size.dpi, - }; - - PositionedFloat{ - left: (left_padding_pixels as f32 / cell_width).ceil() as usize, - top: (top_padding_pixels as f32 / cell_height).ceil() as usize, - size } } @@ -2077,10 +2065,9 @@ impl TabInner { fn add_float_pane( &mut self, - pane_index: usize, float_size: TerminalSize, pane: Arc, - ) -> anyhow::Result { + ) -> anyhow::Result<()> { if self.zoomed.is_some() { anyhow::bail!("cannot add float while zoomed"); } @@ -2092,10 +2079,9 @@ impl TabInner { self.float_pane = Some(Arc::clone(&pane)); { pane.resize(float_size)?; - self.set_active_idx(0); } - Ok(pane_index + 1) + Ok(()) } fn split_and_insert( diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index c5c3ef643ba..39cec992af9 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -44,7 +44,7 @@ use mux::pane::{ CachePolicy, CloseReason, Pane, PaneId, Pattern as MuxPattern, PerformAssignmentResult, }; use mux::renderable::RenderableDimensions; -use mux::tab::{PositionedFloat, PositionedPane, PositionedSplit, SplitDirection, SplitRequest, SplitSize as MuxSplitSize, Tab, TabId}; +use mux::tab::{PositionedPane, PositionedSplit, SplitDirection, SplitRequest, SplitSize as MuxSplitSize, Tab, TabId}; use mux::window::WindowId as MuxWindowId; use mux::{Mux, MuxNotification}; use mux_lua::MuxPane; @@ -3353,33 +3353,6 @@ impl TermWindow { } } - fn get_float_pane(&mut self) -> Option> { - let mux = Mux::get(); - let tab = match mux.get_active_tab_for_window(self.mux_window_id) { - Some(tab) => tab, - None => return None, - }; - - let tab_id = tab.tab_id(); - - if self.tab_state(tab_id).overlay.is_some() { - None - } else { - self.get_float_pane() - } - } - - fn get_float_pos(&mut self) -> Option { - let mux = Mux::get(); - let tab = match mux.get_active_tab_for_window(self.mux_window_id) { - Some(tab) => tab, - None => return None, - }; - - let float_pos = tab.get_float_pos(); - Some(float_pos) - } - fn pos_pane_to_pane_info(pos: &PositionedPane) -> PaneInformation { PaneInformation { pane_id: pos.pane.pane_id(), diff --git a/wezterm-gui/src/termwindow/render/paint.rs b/wezterm-gui/src/termwindow/render/paint.rs index 56bbb0108ff..7c94cd514c0 100644 --- a/wezterm-gui/src/termwindow/render/paint.rs +++ b/wezterm-gui/src/termwindow/render/paint.rs @@ -184,7 +184,6 @@ impl crate::TermWindow { let float_layer = gl_state .layer_for_zindex(2) .context("layer_for_zindex(2)")?; - let mut float_layers = float_layer.quad_allocator(); log::trace!("quad map elapsed {:?}", start.elapsed()); metrics::histogram!("quad.map").record(start.elapsed()); @@ -264,10 +263,10 @@ impl crate::TermWindow { } if let Some(float_pane) = self.get_float_pane_to_render() { - float_pane.pane.advise_focus(); + let mut float_layers = float_layer.quad_allocator(); + self.paint_pane(&float_pane, &mut float_layers).context("paint_pane")?; - let float = self.get_float_pos(); - self.paint_float_border(float.unwrap(), &mut layers).context("paint_float")?; + self.paint_float_border(float_pane, &mut float_layers).context("paint_float_border")?; } if let Some(pane) = self.get_active_pane_or_overlay() { diff --git a/wezterm-gui/src/termwindow/render/split.rs b/wezterm-gui/src/termwindow/render/split.rs index 5dc82099304..0efbbf202fb 100644 --- a/wezterm-gui/src/termwindow/render/split.rs +++ b/wezterm-gui/src/termwindow/render/split.rs @@ -1,7 +1,7 @@ use crate::termwindow::render::TripleLayerQuadAllocator; use crate::termwindow::{UIItem, UIItemType}; use mux::pane::Pane; -use mux::tab::{PositionedFloat, PositionedSplit, SplitDirection}; +use mux::tab::{PositionedPane, PositionedSplit, SplitDirection}; use std::sync::Arc; impl crate::TermWindow { @@ -81,7 +81,7 @@ impl crate::TermWindow { pub fn paint_float_border( &mut self, - pos: PositionedFloat, + pos: PositionedPane, layers: &mut TripleLayerQuadAllocator, ) -> anyhow::Result<()> { let (padding_left, padding_top) = self.padding_left_top(); @@ -93,8 +93,8 @@ impl crate::TermWindow { let half_cell_width = cell_width as f32 / 2.0; let pos_y = (pos.top as f32 * self.render_metrics.cell_size.height as f32) - self.render_metrics.underline_height as f32 - half_cell_height + padding_top; let pos_x = (pos.left as f32 * self.render_metrics.cell_size.width as f32) - self.render_metrics.underline_height as f32 - half_cell_width + padding_left; - let pixel_height = (pos.size.rows as f32 * cell_height as f32) + self.render_metrics.underline_height as f32 + cell_height as f32; - let pixel_width = (pos.size.cols as f32 * cell_width as f32) + self.render_metrics.underline_height as f32 + cell_width as f32; + let pixel_height = pos.pixel_height as f32 + self.render_metrics.underline_height as f32 + cell_height as f32; + let pixel_width = pos.pixel_width as f32 + self.render_metrics.underline_height as f32 + cell_width as f32; self.filled_rectangle( layers, From 543af2bcbc6cfede3ef789ae115fc7eea090c6ba Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Mon, 1 Jul 2024 23:54:51 -0700 Subject: [PATCH 09/66] More accurate float borders --- config/src/color.rs | 32 ++++++ config/src/config.rs | 11 +- wezterm-gui/src/termwindow/render/borders.rs | 68 ++++++++++++- wezterm-gui/src/termwindow/render/pane.rs | 11 +- wezterm-gui/src/termwindow/render/split.rs | 100 +++++++++++++++---- 5 files changed, 188 insertions(+), 34 deletions(-) diff --git a/config/src/color.rs b/config/src/color.rs index ad16cf1cc94..ab993d691a2 100644 --- a/config/src/color.rs +++ b/config/src/color.rs @@ -598,6 +598,23 @@ pub struct WindowFrameConfig { pub border_bottom_color: Option, } +#[derive(Debug, Clone, FromDynamic, ToDynamic)] +pub struct FloatBorderConfig { + #[dynamic(try_from = "crate::units::PixelUnit", default = "default_zero_pixel")] + pub left_width: Dimension, + #[dynamic(try_from = "crate::units::PixelUnit", default = "default_zero_pixel")] + pub right_width: Dimension, + #[dynamic(try_from = "crate::units::PixelUnit", default = "default_zero_pixel")] + pub top_height: Dimension, + #[dynamic(try_from = "crate::units::PixelUnit", default = "default_zero_pixel")] + pub bottom_height: Dimension, + + pub left_color: Option, + pub right_color: Option, + pub top_color: Option, + pub bottom_color: Option, +} + const fn default_zero_pixel() -> Dimension { Dimension::Pixels(0.) } @@ -629,6 +646,21 @@ impl Default for WindowFrameConfig { } } +impl Default for FloatBorderConfig { + fn default() -> Self { + Self { + left_width: default_zero_pixel(), + right_width: default_zero_pixel(), + top_height: default_zero_pixel(), + bottom_height: default_zero_pixel(), + left_color: None, + right_color: None, + top_color: None, + bottom_color: None, + } + } +} + fn default_inactive_titlebar_bg() -> RgbaColor { RgbColor::new_8bpc(0x33, 0x33, 0x33).into() } diff --git a/config/src/config.rs b/config/src/config.rs index 0892dd179db..5a420745795 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -20,13 +20,7 @@ use crate::tls::{TlsDomainClient, TlsDomainServer}; use crate::units::Dimension; use crate::unix::UnixDomain; use crate::wsl::WslDomain; -use crate::{ - default_config_with_overrides_applied, default_one_point_oh, default_one_point_oh_f64, - default_true, default_win32_acrylic_accent_color, GpuInfo, IntegratedTitleButtonColor, - KeyMapPreference, LoadedConfig, MouseEventTriggerMods, RgbaColor, SerialDomain, SystemBackdrop, - WebGpuPowerPreference, CONFIG_DIRS, CONFIG_FILE_OVERRIDE, CONFIG_OVERRIDES, CONFIG_SKIP, - HOME_DIR, -}; +use crate::{default_config_with_overrides_applied, default_one_point_oh, default_one_point_oh_f64, default_true, default_win32_acrylic_accent_color, GpuInfo, IntegratedTitleButtonColor, KeyMapPreference, LoadedConfig, MouseEventTriggerMods, RgbaColor, SerialDomain, SystemBackdrop, WebGpuPowerPreference, CONFIG_DIRS, CONFIG_FILE_OVERRIDE, CONFIG_OVERRIDES, CONFIG_SKIP, HOME_DIR, FloatBorderConfig}; use anyhow::Context; use luahelper::impl_lua_conversion_dynamic; use mlua::FromLua; @@ -142,6 +136,9 @@ pub struct Config { #[dynamic(default)] pub window_frame: WindowFrameConfig, + #[dynamic(default)] + pub float_pane_border: FloatBorderConfig, + #[dynamic(default = "default_char_select_font_size")] pub char_select_font_size: f64, diff --git a/wezterm-gui/src/termwindow/render/borders.rs b/wezterm-gui/src/termwindow/render/borders.rs index 8d7c43cb6bb..abe47340983 100644 --- a/wezterm-gui/src/termwindow/render/borders.rs +++ b/wezterm-gui/src/termwindow/render/borders.rs @@ -1,7 +1,7 @@ use crate::quad::TripleLayerQuadAllocator; use crate::utilsprites::RenderMetrics; use ::window::ULength; -use config::{ConfigHandle, DimensionContext}; +use config::{ConfigHandle, DimensionContext, FloatBorderConfig}; impl crate::TermWindow { pub fn paint_window_borders( @@ -137,6 +137,72 @@ impl crate::TermWindow { border } + pub fn get_os_border_impl2( + os_parameters: &Option, + config: &ConfigHandle, + dimensions: &crate::Dimensions, + render_metrics: &RenderMetrics, + border_config: &FloatBorderConfig + ) -> window::parameters::Border { + let mut border = os_parameters + .as_ref() + .and_then(|p| p.border_dimensions.clone()) + .unwrap_or_default(); + + border.left += ULength::new( + border_config + .left_width + .evaluate_as_pixels(DimensionContext { + dpi: dimensions.dpi as f32, + pixel_max: dimensions.pixel_width as f32, + pixel_cell: render_metrics.cell_size.width as f32, + }) + .ceil() as usize, + ); + border.right += ULength::new( + border_config + .right_width + .evaluate_as_pixels(DimensionContext { + dpi: dimensions.dpi as f32, + pixel_max: dimensions.pixel_width as f32, + pixel_cell: render_metrics.cell_size.width as f32, + }) + .ceil() as usize, + ); + border.top += ULength::new( + border_config + .top_height + .evaluate_as_pixels(DimensionContext { + dpi: dimensions.dpi as f32, + pixel_max: dimensions.pixel_height as f32, + pixel_cell: render_metrics.cell_size.height as f32, + }) + .ceil() as usize, + ); + border.bottom += ULength::new( + border_config + .bottom_height + .evaluate_as_pixels(DimensionContext { + dpi: dimensions.dpi as f32, + pixel_max: dimensions.pixel_height as f32, + pixel_cell: render_metrics.cell_size.height as f32, + }) + .ceil() as usize, + ); + + border + } + + pub fn get_float_border(&self) -> window::parameters::Border { + Self::get_os_border_impl2( + &self.os_parameters, + &self.config, + &self.dimensions, + &self.render_metrics, + &self.config.float_pane_border + ) + } + pub fn get_os_border(&self) -> window::parameters::Border { Self::get_os_border_impl( &self.os_parameters, diff --git a/wezterm-gui/src/termwindow/render/pane.rs b/wezterm-gui/src/termwindow/render/pane.rs index a7c5f797fb2..76299c38d91 100644 --- a/wezterm-gui/src/termwindow/render/pane.rs +++ b/wezterm-gui/src/termwindow/render/pane.rs @@ -96,7 +96,7 @@ impl crate::TermWindow { let window_is_transparent = !self.window_background.is_empty() || config.window_background_opacity != 1.0; - let default_bg = palette + let mut default_bg = palette .resolve_bg(ColorAttribute::Default) .to_linear() .mul_alpha(if window_is_transparent { @@ -159,10 +159,11 @@ impl crate::TermWindow { layers, 0, background_rect, - palette - .background - .to_linear() - .mul_alpha(config.window_background_opacity), + default_bg, + //palette + // .foreground + // .to_linear() + // .mul_alpha(config.window_background_opacity), ) .context("filled_rectangle")?; quad.set_hsv(if pos.is_active { diff --git a/wezterm-gui/src/termwindow/render/split.rs b/wezterm-gui/src/termwindow/render/split.rs index 0efbbf202fb..f97381b5693 100644 --- a/wezterm-gui/src/termwindow/render/split.rs +++ b/wezterm-gui/src/termwindow/render/split.rs @@ -3,6 +3,7 @@ use crate::termwindow::{UIItem, UIItemType}; use mux::pane::Pane; use mux::tab::{PositionedPane, PositionedSplit, SplitDirection}; use std::sync::Arc; +use window::PixelUnit; impl crate::TermWindow { pub fn paint_split( @@ -85,16 +86,73 @@ impl crate::TermWindow { layers: &mut TripleLayerQuadAllocator, ) -> anyhow::Result<()> { let (padding_left, padding_top) = self.padding_left_top(); - let foreground = self.palette().foreground; + let config = self.config.float_pane_border.clone(); + let float_border = self.get_float_border(); - let cell_height = self.render_metrics.cell_size.height; - let cell_width = self.render_metrics.cell_size.width; - let half_cell_height = cell_height as f32 / 2.0; - let half_cell_width = cell_width as f32 / 2.0; - let pos_y = (pos.top as f32 * self.render_metrics.cell_size.height as f32) - self.render_metrics.underline_height as f32 - half_cell_height + padding_top; - let pos_x = (pos.left as f32 * self.render_metrics.cell_size.width as f32) - self.render_metrics.underline_height as f32 - half_cell_width + padding_left; - let pixel_height = pos.pixel_height as f32 + self.render_metrics.underline_height as f32 + cell_height as f32; - let pixel_width = pos.pixel_width as f32 + self.render_metrics.underline_height as f32 + cell_width as f32; + let os_border = self.get_os_border(); + let tab_bar_height = if self.show_tab_bar { + self.tab_bar_pixel_height()? + } else { + 0. + }; + + let (top_bar_height, bottom_bar_height) = if self.config.tab_bar_at_bottom { + (0.0, tab_bar_height) + } else { + (tab_bar_height, 0.0) + }; + let top_pixel_y = top_bar_height + padding_top + os_border.top.get() as f32; + let cell_height = self.render_metrics.cell_size.height as f32; + let cell_width = self.render_metrics.cell_size.width as f32; + + let background_rect: euclid::Rect = { + // We want to fill out to the edges of the splits + let (x, width_delta) = if pos.left == 0 { + ( + 0., + padding_left + os_border.left.get() as f32 + (cell_width / 2.0), + ) + } else { + ( + padding_left + os_border.left.get() as f32 - (cell_width / 2.0) + + (pos.left as f32 * cell_width), + cell_width, + ) + }; + + let (y, height_delta) = if pos.top == 0 { + ( + (top_pixel_y - padding_top), + padding_top + (cell_height / 2.0), + ) + } else { + ( + top_pixel_y + (pos.top as f32 * cell_height) - (cell_height / 2.0), + cell_height, + ) + }; + euclid::rect( + x, + y, + // Go all the way to the right edge if we're right-most + if pos.left + pos.width >= self.terminal_size.cols as usize { + self.dimensions.pixel_width as f32 - x + } else { + (pos.width as f32 * cell_width) + width_delta + }, + // Go all the way to the bottom if we're bottom-most + if pos.top + pos.height >= self.terminal_size.rows as usize { + self.dimensions.pixel_height as f32 - y + } else { + (pos.height as f32 * cell_height) + height_delta as f32 + }, + ) + }; + + let pos_y = background_rect.origin.y - float_border.top.get() as f32; + let pos_x = background_rect.origin.x - float_border.left.get() as f32; + let pixel_width = background_rect.size.width + float_border.left.get() as f32; + let pixel_height = background_rect.size.height + float_border.top.get() as f32; self.filled_rectangle( layers, @@ -102,32 +160,32 @@ impl crate::TermWindow { euclid::rect( pos_x, pos_y, - self.render_metrics.underline_height as f32, - pixel_height as f32, + float_border.left.get() as f32, + pixel_height + float_border.top.get() as f32, ), - foreground.to_linear(), + config.left_color.map(|c| c.to_linear()).unwrap_or(os_border.color), )?; self.filled_rectangle( layers, 2, euclid::rect( - pos_x, + pos_x + float_border.left.get() as f32, pos_y, pixel_width, - self.render_metrics.underline_height as f32, + float_border.top.get() as f32, ), - foreground.to_linear(), + config.top_color.map(|c| c.to_linear()).unwrap_or(os_border.color), )?; self.filled_rectangle( layers, 2, euclid::rect( - pos_x, + pos_x + float_border.left.get() as f32, pos_y + pixel_height, pixel_width, - self.render_metrics.underline_height as f32, + float_border.bottom.get() as f32, ), - foreground.to_linear(), + config.bottom_color.map(|c| c.to_linear()).unwrap_or(os_border.color), )?; self.filled_rectangle( layers, @@ -135,10 +193,10 @@ impl crate::TermWindow { euclid::rect( pos_x + pixel_width, pos_y, - self.render_metrics.underline_height as f32, - pixel_height as f32, + float_border.right.get() as f32, + pixel_height + float_border.top.get() as f32, ), - foreground.to_linear(), + config.right_color.map(|c| c.to_linear()).unwrap_or(os_border.color), )?; Ok(()) From 275567aaba813290ea6834fe65af2ac7ebf0a483 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 2 Jul 2024 17:25:22 -0700 Subject: [PATCH 10/66] Handle Resize --- mux/src/tab.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 616b062ad4a..b5a139d55e8 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1235,6 +1235,10 @@ impl TabInner { // And then resize the individual panes to match apply_sizes_from_splits(self.pane.as_mut().unwrap(), &size); + if let Some(float_pane) = &self.float_pane { + let float_size = self.get_float_size(); + float_pane.resize(float_size); + } } Mux::try_get().map(|mux| mux.notify(MuxNotification::TabResized(self.id))); From 203e36236a7fd90077b6fa9ab2b10168163b67d3 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 2 Jul 2024 17:25:44 -0700 Subject: [PATCH 11/66] Better mouse handling --- wezterm-gui/src/termwindow/mouseevent.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index f70db22ceac..20917bf386d 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -667,12 +667,18 @@ impl super::TermWindow { Some(MouseCapture::TerminalPane(_)) ); - //is there a better place to put this? - if self.is_float_active() { - return; - } + let panes = if let Some(float_pane) = self.get_float_pane_to_render() { + if position.column >= float_pane.left && position.column <= float_pane.left + float_pane.width + && position.row as usize >= float_pane.top && position.row as usize <= float_pane.top + float_pane.height { + vec![float_pane] + } else { + return + } + } else { + self.get_panes_to_render() + }; - for pos in self.get_panes_to_render() { + for pos in panes { if !is_already_captured && row >= pos.top as i64 && row <= (pos.top + pos.height) as i64 From a3e74442a04f65dec0e4f734c25277d507fb519a Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 2 Jul 2024 19:56:05 -0700 Subject: [PATCH 12/66] Clean up --- config/src/config.rs | 8 +- mux/src/lib.rs | 2 +- mux/src/tab.rs | 15 +- wezterm-gui/src/termwindow/render/borders.rs | 146 +++++++++++++++++-- wezterm-gui/src/termwindow/render/paint.rs | 15 +- wezterm-gui/src/termwindow/render/pane.rs | 11 +- wezterm-gui/src/termwindow/render/split.rs | 125 +--------------- 7 files changed, 163 insertions(+), 159 deletions(-) diff --git a/config/src/config.rs b/config/src/config.rs index 5a420745795..4a8c59af05f 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -20,7 +20,13 @@ use crate::tls::{TlsDomainClient, TlsDomainServer}; use crate::units::Dimension; use crate::unix::UnixDomain; use crate::wsl::WslDomain; -use crate::{default_config_with_overrides_applied, default_one_point_oh, default_one_point_oh_f64, default_true, default_win32_acrylic_accent_color, GpuInfo, IntegratedTitleButtonColor, KeyMapPreference, LoadedConfig, MouseEventTriggerMods, RgbaColor, SerialDomain, SystemBackdrop, WebGpuPowerPreference, CONFIG_DIRS, CONFIG_FILE_OVERRIDE, CONFIG_OVERRIDES, CONFIG_SKIP, HOME_DIR, FloatBorderConfig}; +use crate::{ + default_config_with_overrides_applied, default_one_point_oh, default_one_point_oh_f64, + default_true, default_win32_acrylic_accent_color, GpuInfo, IntegratedTitleButtonColor, + KeyMapPreference, LoadedConfig, MouseEventTriggerMods, RgbaColor, SerialDomain, SystemBackdrop, + WebGpuPowerPreference, CONFIG_DIRS, CONFIG_FILE_OVERRIDE, CONFIG_OVERRIDES, CONFIG_SKIP, + HOME_DIR,FloatBorderConfig +}; use anyhow::Context; use luahelper::impl_lua_conversion_dynamic; use mlua::FromLua; diff --git a/mux/src/lib.rs b/mux/src/lib.rs index e2235a0d613..9024e5bce1a 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1,7 +1,7 @@ use crate::client::{ClientId, ClientInfo}; use crate::pane::{CachePolicy, Pane, PaneId}; use crate::ssh_agent::AgentProxy; -use crate::tab::{PositionedPane, SplitRequest, Tab, TabId}; +use crate::tab::{SplitRequest, Tab, TabId}; use crate::window::{Window, WindowId}; use anyhow::{anyhow, Context, Error}; use config::keyassignment::SpawnTabDomain; diff --git a/mux/src/tab.rs b/mux/src/tab.rs index b5a139d55e8..2280cb5cb37 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; use std::convert::TryInto; use std::sync::Arc; use url::Url; -use wezterm_term::{Position, StableRowIndex, TerminalSize}; +use wezterm_term::{StableRowIndex, TerminalSize}; pub type Tree = bintree::Tree, SplitDirectionAndSize>; pub type Cursor = bintree::Cursor, SplitDirectionAndSize>; @@ -1078,6 +1078,10 @@ impl TabInner { } } + if let Some(float_pane) = self.get_float_pane() { + panes.push(float_pane); + } + let active_idx = self.active; let zoomed_id = self.zoomed.as_ref().map(|p| p.pane_id()); let root_size = self.size; @@ -1200,6 +1204,11 @@ impl TabInner { return; } + if let Some(float_pane) = &self.float_pane { + let float_size = self.get_float_size(); + float_pane.resize(float_size).ok(); + } + if let Some(zoomed) = &self.zoomed { self.size = size; zoomed.resize(size).ok(); @@ -1235,10 +1244,6 @@ impl TabInner { // And then resize the individual panes to match apply_sizes_from_splits(self.pane.as_mut().unwrap(), &size); - if let Some(float_pane) = &self.float_pane { - let float_size = self.get_float_size(); - float_pane.resize(float_size); - } } Mux::try_get().map(|mux| mux.notify(MuxNotification::TabResized(self.id))); diff --git a/wezterm-gui/src/termwindow/render/borders.rs b/wezterm-gui/src/termwindow/render/borders.rs index abe47340983..95809701388 100644 --- a/wezterm-gui/src/termwindow/render/borders.rs +++ b/wezterm-gui/src/termwindow/render/borders.rs @@ -1,7 +1,9 @@ use crate::quad::TripleLayerQuadAllocator; use crate::utilsprites::RenderMetrics; use ::window::ULength; -use config::{ConfigHandle, DimensionContext, FloatBorderConfig}; +use config::{ConfigHandle, DimensionContext, FloatBorderConfig, PixelUnit}; +use mux::tab::PositionedPane; +use window::parameters::Border; impl crate::TermWindow { pub fn paint_window_borders( @@ -78,6 +80,129 @@ impl crate::TermWindow { Ok(()) } + pub fn paint_float_border( + &mut self, + pos: PositionedPane, + layers: &mut TripleLayerQuadAllocator, + ) -> anyhow::Result<()> { + let (padding_left, padding_top) = self.padding_left_top(); + let config = self.config.float_pane_border.clone(); + let float_border = self.get_float_border(); + + let os_border = self.get_os_border(); + let tab_bar_height = if self.show_tab_bar { + self.tab_bar_pixel_height()? + } else { + 0. + }; + + let (top_bar_height, _bottom_bar_height) = if self.config.tab_bar_at_bottom { + (0.0, tab_bar_height) + } else { + (tab_bar_height, 0.0) + }; + let top_pixel_y = top_bar_height + padding_top + os_border.top.get() as f32; + let cell_height = self.render_metrics.cell_size.height as f32; + let cell_width = self.render_metrics.cell_size.width as f32; + + //refactor with logic from paint_pane? + let background_rect: euclid::Rect = { + // We want to fill out to the edges of the splits + let (x, width_delta) = if pos.left == 0 { + ( + 0., + padding_left + os_border.left.get() as f32 + (cell_width / 2.0), + ) + } else { + ( + padding_left + os_border.left.get() as f32 - (cell_width / 2.0) + + (pos.left as f32 * cell_width), + cell_width, + ) + }; + + let (y, height_delta) = if pos.top == 0 { + ( + (top_pixel_y - padding_top), + padding_top + (cell_height / 2.0), + ) + } else { + ( + top_pixel_y + (pos.top as f32 * cell_height) - (cell_height / 2.0), + cell_height, + ) + }; + euclid::rect( + x, + y, + // Go all the way to the right edge if we're right-most + if pos.left + pos.width >= self.terminal_size.cols as usize { + self.dimensions.pixel_width as f32 - x + } else { + (pos.width as f32 * cell_width) + width_delta + }, + // Go all the way to the bottom if we're bottom-most + if pos.top + pos.height >= self.terminal_size.rows as usize { + self.dimensions.pixel_height as f32 - y + } else { + (pos.height as f32 * cell_height) + height_delta as f32 + }, + ) + }; + + let pos_y = background_rect.origin.y - float_border.top.get() as f32; + let pos_x = background_rect.origin.x - float_border.left.get() as f32; + let pixel_width = background_rect.size.width + float_border.left.get() as f32; + let pixel_height = background_rect.size.height + float_border.top.get() as f32; + + self.filled_rectangle( + layers, + 2, + euclid::rect( + pos_x, + pos_y, + float_border.left.get() as f32, + pixel_height + float_border.top.get() as f32, + ), + config.left_color.map(|c| c.to_linear()).unwrap_or(os_border.color), + )?; + self.filled_rectangle( + layers, + 2, + euclid::rect( + pos_x + float_border.left.get() as f32, + pos_y, + pixel_width, + float_border.top.get() as f32, + ), + config.top_color.map(|c| c.to_linear()).unwrap_or(os_border.color), + )?; + self.filled_rectangle( + layers, + 2, + euclid::rect( + pos_x + float_border.left.get() as f32, + pos_y + pixel_height, + pixel_width, + float_border.bottom.get() as f32, + ), + config.bottom_color.map(|c| c.to_linear()).unwrap_or(os_border.color), + )?; + self.filled_rectangle( + layers, + 2, + euclid::rect( + pos_x + pixel_width, + pos_y, + float_border.right.get() as f32, + pixel_height + float_border.top.get() as f32, + ), + config.right_color.map(|c| c.to_linear()).unwrap_or(os_border.color), + )?; + + Ok(()) + } + pub fn get_os_border_impl( os_parameters: &Option, config: &ConfigHandle, @@ -137,18 +262,13 @@ impl crate::TermWindow { border } - pub fn get_os_border_impl2( - os_parameters: &Option, - config: &ConfigHandle, + //refactor with get_os_border_impl? + fn get_float_border_impl( dimensions: &crate::Dimensions, render_metrics: &RenderMetrics, border_config: &FloatBorderConfig - ) -> window::parameters::Border { - let mut border = os_parameters - .as_ref() - .and_then(|p| p.border_dimensions.clone()) - .unwrap_or_default(); - + ) -> Border { + let mut border= Border::default(); border.left += ULength::new( border_config .left_width @@ -193,10 +313,8 @@ impl crate::TermWindow { border } - pub fn get_float_border(&self) -> window::parameters::Border { - Self::get_os_border_impl2( - &self.os_parameters, - &self.config, + fn get_float_border(&self) -> Border { + Self::get_float_border_impl( &self.dimensions, &self.render_metrics, &self.config.float_pane_border diff --git a/wezterm-gui/src/termwindow/render/paint.rs b/wezterm-gui/src/termwindow/render/paint.rs index 7c94cd514c0..1b037af284a 100644 --- a/wezterm-gui/src/termwindow/render/paint.rs +++ b/wezterm-gui/src/termwindow/render/paint.rs @@ -251,6 +251,7 @@ impl crate::TermWindow { .context("filled_rectangle for window background")?; } + let mut float_layers = float_layer.quad_allocator(); for pos in panes { if pos.is_active { self.update_text_cursor(&pos); @@ -259,14 +260,12 @@ impl crate::TermWindow { mux::Mux::get().record_focus_for_current_identity(pos.pane.pane_id()); } } - self.paint_pane(&pos, &mut layers).context("paint_pane")?; - } - - if let Some(float_pane) = self.get_float_pane_to_render() { - let mut float_layers = float_layer.quad_allocator(); - - self.paint_pane(&float_pane, &mut float_layers).context("paint_pane")?; - self.paint_float_border(float_pane, &mut float_layers).context("paint_float_border")?; + if pos.is_float { + self.paint_pane(&pos, &mut float_layers).context("paint_pane")?; + self.paint_float_border(pos, &mut float_layers).context("paint_float_border")?; + } else { + self.paint_pane(&pos, &mut layers).context("paint_pane")?; + } } if let Some(pane) = self.get_active_pane_or_overlay() { diff --git a/wezterm-gui/src/termwindow/render/pane.rs b/wezterm-gui/src/termwindow/render/pane.rs index 76299c38d91..a7c5f797fb2 100644 --- a/wezterm-gui/src/termwindow/render/pane.rs +++ b/wezterm-gui/src/termwindow/render/pane.rs @@ -96,7 +96,7 @@ impl crate::TermWindow { let window_is_transparent = !self.window_background.is_empty() || config.window_background_opacity != 1.0; - let mut default_bg = palette + let default_bg = palette .resolve_bg(ColorAttribute::Default) .to_linear() .mul_alpha(if window_is_transparent { @@ -159,11 +159,10 @@ impl crate::TermWindow { layers, 0, background_rect, - default_bg, - //palette - // .foreground - // .to_linear() - // .mul_alpha(config.window_background_opacity), + palette + .background + .to_linear() + .mul_alpha(config.window_background_opacity), ) .context("filled_rectangle")?; quad.set_hsv(if pos.is_active { diff --git a/wezterm-gui/src/termwindow/render/split.rs b/wezterm-gui/src/termwindow/render/split.rs index f97381b5693..745530161cf 100644 --- a/wezterm-gui/src/termwindow/render/split.rs +++ b/wezterm-gui/src/termwindow/render/split.rs @@ -1,9 +1,8 @@ use crate::termwindow::render::TripleLayerQuadAllocator; use crate::termwindow::{UIItem, UIItemType}; use mux::pane::Pane; -use mux::tab::{PositionedPane, PositionedSplit, SplitDirection}; +use mux::tab::{PositionedSplit, SplitDirection}; use std::sync::Arc; -use window::PixelUnit; impl crate::TermWindow { pub fn paint_split( @@ -79,126 +78,4 @@ impl crate::TermWindow { Ok(()) } - - pub fn paint_float_border( - &mut self, - pos: PositionedPane, - layers: &mut TripleLayerQuadAllocator, - ) -> anyhow::Result<()> { - let (padding_left, padding_top) = self.padding_left_top(); - let config = self.config.float_pane_border.clone(); - let float_border = self.get_float_border(); - - let os_border = self.get_os_border(); - let tab_bar_height = if self.show_tab_bar { - self.tab_bar_pixel_height()? - } else { - 0. - }; - - let (top_bar_height, bottom_bar_height) = if self.config.tab_bar_at_bottom { - (0.0, tab_bar_height) - } else { - (tab_bar_height, 0.0) - }; - let top_pixel_y = top_bar_height + padding_top + os_border.top.get() as f32; - let cell_height = self.render_metrics.cell_size.height as f32; - let cell_width = self.render_metrics.cell_size.width as f32; - - let background_rect: euclid::Rect = { - // We want to fill out to the edges of the splits - let (x, width_delta) = if pos.left == 0 { - ( - 0., - padding_left + os_border.left.get() as f32 + (cell_width / 2.0), - ) - } else { - ( - padding_left + os_border.left.get() as f32 - (cell_width / 2.0) - + (pos.left as f32 * cell_width), - cell_width, - ) - }; - - let (y, height_delta) = if pos.top == 0 { - ( - (top_pixel_y - padding_top), - padding_top + (cell_height / 2.0), - ) - } else { - ( - top_pixel_y + (pos.top as f32 * cell_height) - (cell_height / 2.0), - cell_height, - ) - }; - euclid::rect( - x, - y, - // Go all the way to the right edge if we're right-most - if pos.left + pos.width >= self.terminal_size.cols as usize { - self.dimensions.pixel_width as f32 - x - } else { - (pos.width as f32 * cell_width) + width_delta - }, - // Go all the way to the bottom if we're bottom-most - if pos.top + pos.height >= self.terminal_size.rows as usize { - self.dimensions.pixel_height as f32 - y - } else { - (pos.height as f32 * cell_height) + height_delta as f32 - }, - ) - }; - - let pos_y = background_rect.origin.y - float_border.top.get() as f32; - let pos_x = background_rect.origin.x - float_border.left.get() as f32; - let pixel_width = background_rect.size.width + float_border.left.get() as f32; - let pixel_height = background_rect.size.height + float_border.top.get() as f32; - - self.filled_rectangle( - layers, - 2, - euclid::rect( - pos_x, - pos_y, - float_border.left.get() as f32, - pixel_height + float_border.top.get() as f32, - ), - config.left_color.map(|c| c.to_linear()).unwrap_or(os_border.color), - )?; - self.filled_rectangle( - layers, - 2, - euclid::rect( - pos_x + float_border.left.get() as f32, - pos_y, - pixel_width, - float_border.top.get() as f32, - ), - config.top_color.map(|c| c.to_linear()).unwrap_or(os_border.color), - )?; - self.filled_rectangle( - layers, - 2, - euclid::rect( - pos_x + float_border.left.get() as f32, - pos_y + pixel_height, - pixel_width, - float_border.bottom.get() as f32, - ), - config.bottom_color.map(|c| c.to_linear()).unwrap_or(os_border.color), - )?; - self.filled_rectangle( - layers, - 2, - euclid::rect( - pos_x + pixel_width, - pos_y, - float_border.right.get() as f32, - pixel_height + float_border.top.get() as f32, - ), - config.right_color.map(|c| c.to_linear()).unwrap_or(os_border.color), - )?; - - Ok(()) - } } From e8ee2a8d2f3eed8eaec72809348f94536918db2b Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 13 Jul 2024 07:44:33 -0700 Subject: [PATCH 13/66] Update float-pane to use cwd instead of home directory --- mux/src/lib.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 9024e5bce1a..20554a67a96 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1188,7 +1188,7 @@ impl Mux { // TODO: disambiguate with TabId pane_id: PaneId, command_builder: Option, - command: Option, + command_dir: Option, domain: config::keyassignment::SpawnTabDomain, ) -> anyhow::Result<(Arc, TerminalSize)> { let (_pane_domain_id, window_id, tab_id) = self @@ -1208,7 +1208,18 @@ impl Mux { .ok_or_else(|| anyhow!("pane_id {} is invalid", pane_id))?; let term_config = current_pane.get_config(); - let pane = domain.add_float_pane(tab_id, pane_id, command_builder, command).await?; + let command_dir = if !command_dir.is_some() { + self.resolve_cwd( + command_dir, + Some(Arc::clone(¤t_pane)), + domain.domain_id(), + CachePolicy::FetchImmediate, + ) + } else { + command_dir + }; + + let pane = domain.add_float_pane(tab_id, pane_id, command_builder, command_dir).await?; if let Some(config) = term_config { pane.set_config(config); From 128a1caaff729ac13493266e5fe5f1552575b5f1 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 13 Jul 2024 18:37:47 -0700 Subject: [PATCH 14/66] prevent running pane manipulation actions when float pane is active --- wezterm-gui/src/spawn.rs | 7 ------- wezterm-gui/src/termwindow/mod.rs | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/wezterm-gui/src/spawn.rs b/wezterm-gui/src/spawn.rs index 45a27b90b19..eba1c3746cc 100644 --- a/wezterm-gui/src/spawn.rs +++ b/wezterm-gui/src/spawn.rs @@ -91,10 +91,6 @@ pub async fn spawn_command_internal( None => anyhow::bail!("no src window when splitting a pane?"), }; if let Some(tab) = mux.get_active_tab_for_window(src_window_id) { - if tab.is_float_active() { - bail!("cannot open split when float is active"); - } - let pane = tab .get_active_pane() .ok_or_else(|| anyhow!("tab to have a pane"))?; @@ -124,9 +120,6 @@ pub async fn spawn_command_internal( None => anyhow::bail!("no src window when float a pane?"), }; if let Some(tab) = mux.get_active_tab_for_window(src_window_id) { - if tab.is_float_active() { - bail!("cannot open split when float is active"); - } let pane = tab .get_active_pane() .ok_or_else(|| anyhow!("tab to have a pane"))?; diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 39cec992af9..bfd827e6739 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -2509,6 +2509,21 @@ impl TermWindow { result => return Ok(result), } + if self.is_float_active() { + match assignment { + ActivatePaneByIndex(..) | + ActivatePaneDirection(..) | + SplitPane(..) | + SplitVertical(..) | + SplitHorizontal(..) | + SpawnTab(..) | + PaneSelect(..) => { + return Ok(PerformAssignmentResult::Handled); + }, + _ => {} + } + } + let window = self.window.as_ref().map(|w| w.clone()); match assignment { From adba9f209945d09d22d6b27c42ccbbd545651e2b Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Thu, 8 Aug 2024 18:26:05 -0700 Subject: [PATCH 15/66] Updated float pane icons --- wezterm-gui/src/commands.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 3a925bf82b1..5f6ea8d47e0 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1460,7 +1460,7 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option Option CommandDef { brief: format!("Resize Pane {amount} cell(s) to the Left").into(), From cd0ee5fcb03a5c430ffa77209c89ba1546225c5d Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 21 Sep 2024 16:41:49 -0700 Subject: [PATCH 16/66] Floating pane: address initial feedback --- mux/src/domain.rs | 2 +- mux/src/tab.rs | 14 ++++++-------- wezterm-gui/src/commands.rs | 8 ++++---- wezterm-gui/src/termwindow/mod.rs | 4 ++++ wezterm-gui/src/termwindow/mouseevent.rs | 17 ++++++++++++----- wezterm/src/cli/mod.rs | 2 +- wezterm/src/cli/split_pane.rs | 4 ++-- 7 files changed, 30 insertions(+), 21 deletions(-) diff --git a/mux/src/domain.rs b/mux/src/domain.rs index e7e5a3e700f..47184c08ec2 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -84,7 +84,7 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("Invalid tab id {}", tab), }; - let float_size = tab.get_float_size(); + let float_size = tab.compute_float_size(); let pane = self.spawn_pane(float_size, command_builder, command_dir) .await?; tab.insert_float(float_size, Arc::clone(&pane))?; Ok(pane) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 2280cb5cb37..2ef47c81df0 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -743,10 +743,10 @@ impl Tab { self.inner.lock().compute_split_size(pane_index, request) } - pub fn get_float_size( + pub fn compute_float_size( &self, ) -> TerminalSize { - self.inner.lock().get_float_size() + self.inner.lock().compute_float_size() } /// Split the pane that has pane_index in the given direction and assign @@ -961,7 +961,7 @@ impl TabInner { fn get_float_pane(&self) -> Option { if let Some(float_pane) = self.float_pane.as_ref() { let root_size = self.size; - let size = self.get_float_size(); + let size = self.compute_float_size(); let cell_height = root_size.pixel_height / root_size.rows; let cell_width = root_size.pixel_width / root_size.cols; @@ -1205,7 +1205,7 @@ impl TabInner { } if let Some(float_pane) = &self.float_pane { - let float_size = self.get_float_size(); + let float_size = self.compute_float_size(); float_pane.resize(float_size).ok(); } @@ -1945,7 +1945,7 @@ impl TabInner { None } - fn get_float_size(&self) -> TerminalSize { + fn compute_float_size(&self) -> TerminalSize { let root_size = self.size; let cell_width = root_size.pixel_width as f32 / root_size.cols as f32; @@ -2086,10 +2086,8 @@ impl TabInner { } self.float_pane = Some(Arc::clone(&pane)); - { - pane.resize(float_size)?; - } + pane.resize(float_size)?; Ok(()) } diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 5f6ea8d47e0..8bdad3b4f04 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1449,8 +1449,8 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { - brief: label_string(action, "Create a float pane".to_string()).into(), - doc: "Create a float pane" + brief: label_string(action, "Create a floating pane".to_string()).into(), + doc: "Create a floating pane" .into(), keys: vec![( Modifiers::CTRL @@ -1519,8 +1519,8 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { - brief: label_string(action, "Create a float pane".to_string()).into(), - doc: "Create a float pane" + brief: label_string(action, "Create a floating pane".to_string()).into(), + doc: "Create a floating pane" .into(), keys: vec![], args: &[ArgType::ActivePane], diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index bfd827e6739..9091113c1bd 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -538,6 +538,10 @@ impl TermWindow { // force cursor to be repainted window.invalidate(); + if let Some(pane) = self.get_active_pane_or_overlay() { + pane.focus_changed(focused); + } + self.update_title(); self.emit_window_event("window-focus-changed", None); } diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index 20917bf386d..92463badab3 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -668,12 +668,19 @@ impl super::TermWindow { ); let panes = if let Some(float_pane) = self.get_float_pane_to_render() { - if position.column >= float_pane.left && position.column <= float_pane.left + float_pane.width - && position.row as usize >= float_pane.top && position.row as usize <= float_pane.top + float_pane.height { - vec![float_pane] - } else { - return + let mouse_in_float = position.column >= float_pane.left && + position.column <= float_pane.left + float_pane.width && + position.row as usize >= float_pane.top && + position.row as usize <= float_pane.top + float_pane.height; + + if !mouse_in_float { + // Mouse events are not dispatched to the other panes when + // a floating pane is active, this is to prevent users from selecting one of the + // panes that the float is on top of and encountering some weird behavior, ex. + // closing the last non-floating pane while the floating pane is active. + return; } + vec![float_pane] } else { self.get_panes_to_render() }; diff --git a/wezterm/src/cli/mod.rs b/wezterm/src/cli/mod.rs index a72aaa6afd1..dcaa92cf788 100644 --- a/wezterm/src/cli/mod.rs +++ b/wezterm/src/cli/mod.rs @@ -107,7 +107,7 @@ Outputs the pane-id for the newly created pane on success" name = "float-pane", rename_all = "kebab", trailing_var_arg = true, - about = "spawn a float pane" + about = "spawn a floating pane" )] FloatPane(split_pane::FloatPane), diff --git a/wezterm/src/cli/split_pane.rs b/wezterm/src/cli/split_pane.rs index e763bd1b04f..0424204c2a8 100644 --- a/wezterm/src/cli/split_pane.rs +++ b/wezterm/src/cli/split_pane.rs @@ -116,7 +116,7 @@ impl SplitPane { #[derive(Debug, Parser, Clone)] pub struct FloatPane { - /// Specify the pane that should be split. + /// Specify the pane that should be floating. /// The default is to use the current pane based on the /// environment variable WEZTERM_PANE. #[arg(long)] @@ -128,7 +128,7 @@ pub struct FloatPane { cwd: Option, /// Instead of executing your shell, run PROG. - /// For example: `wezterm cli split-pane -- bash -l` will spawn bash + /// For example: `wezterm cli float-pane -- bash -l` will spawn bash /// as if it were a login shell. #[arg(value_parser, value_hint=ValueHint::CommandWithArguments, num_args=1..)] prog: Vec, From e567786d4f3a86d480eefe76143b50544d2e85c1 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 22 Sep 2024 10:18:13 -0700 Subject: [PATCH 17/66] Fix for not creating float pane in multiplexer --- wezterm-client/src/domain.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 8d6a116d158..953ec72ba73 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -941,10 +941,20 @@ impl Domain for ClientDomain { let mux = Mux::get(); + let tab = mux + .get_tab(_tab_id) + .ok_or_else(|| anyhow!("tab_id {} is invalid", _tab_id))?; + let local_pane = mux + .get_pane(pane_id) + .ok_or_else(|| anyhow!("pane_id {} is invalid", pane_id))?; + let client_pane = local_pane + .downcast_ref::() + .ok_or_else(|| anyhow!("pane_id {} is not a ClientPane", pane_id))?; + let result = inner .client .add_float_pane(FloatPane{ - pane_id, + pane_id: client_pane.remote_pane_id, command, command_dir, domain: SpawnTabDomain::CurrentPaneDomain @@ -958,6 +968,8 @@ impl Domain for ClientDomain { "wezterm", )); + tab.insert_float(result.size, Arc::clone(&pane)).ok(); + mux.add_pane(&pane)?; Ok(pane) From 55b9d8a2cfcfd9de98ab7d03adf72c94de7fc104 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 22 Sep 2024 17:41:15 -0700 Subject: [PATCH 18/66] Support for floating panes in cli list-panes Open questions - Is (PaneNode, PaneNode) ok? Or should it be a type? - I think the tuple matches what Zellij has. - Does the output need anything to indicate that the pane is a float? - I don't think Zellij supports list panes (It looked like there is a PR) - It looked like tmux popups are not persistent so I don't think it is applicable here --- codec/src/lib.rs | 2 +- mux/src/tab.rs | 51 +++++++++++++++---- wezterm-client/src/domain.rs | 6 +-- wezterm-gui/src/main.rs | 2 +- wezterm-mux-server-impl/src/sessionhandler.rs | 2 +- wezterm/src/cli/activate_tab.rs | 2 +- wezterm/src/cli/list.rs | 16 +++++- wezterm/src/cli/move_pane_to_new_tab.rs | 2 +- wezterm/src/cli/rename_workspace.rs | 2 +- wezterm/src/cli/set_tab_title.rs | 2 +- wezterm/src/cli/set_window_title.rs | 2 +- wezterm/src/cli/spawn_command.rs | 2 +- wezterm/src/cli/zoom_pane.rs | 2 +- 13 files changed, 70 insertions(+), 23 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 6ce72ccda14..86a2a2d0251 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -645,7 +645,7 @@ pub struct ListPanes {} #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct ListPanesResponse { - pub tabs: Vec, + pub tabs: Vec<(PaneNode, PaneNode)>, pub tab_titles: Vec, pub window_titles: HashMap, } diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 2ef47c81df0..2f3ea168ce5 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -553,7 +553,7 @@ impl Tab { self.inner.lock().sync_with_pane_tree(size, root, make_pane) } - pub fn codec_pane_tree(&self) -> PaneNode { + pub fn codec_pane_tree(&self) -> (PaneNode, PaneNode) { self.inner.lock().codec_pane_tree() } @@ -845,14 +845,14 @@ impl TabInner { assert!(self.pane.is_some()); } - fn codec_pane_tree(&mut self) -> PaneNode { + fn codec_pane_tree(&mut self) -> (PaneNode, PaneNode) { let mux = Mux::get(); let tab_id = self.id; let window_id = match mux.window_containing_tab(tab_id) { Some(w) => w, None => { log::error!("no window contains tab {}", tab_id); - return PaneNode::Empty; + return (PaneNode::Empty, PaneNode::Empty); } }; @@ -863,14 +863,47 @@ impl TabInner { Some(ws) => ws, None => { log::error!("window id {} doesn't have a window!?", window_id); - return PaneNode::Empty; + return (PaneNode::Empty, PaneNode::Empty); } }; let active = self.get_active_pane(); let zoomed = self.zoomed.as_ref(); + let float_pane = self.float_pane.as_ref(); + + let codec_float_pane = if let Some(float_pane) = float_pane { + let dims = float_pane.get_dimensions(); + let working_dir = float_pane.get_current_working_dir(CachePolicy::AllowStale); + let cursor_pos = float_pane.get_cursor_position(); + + PaneNode::Leaf(PaneEntry { + window_id, + tab_id, + pane_id: float_pane.pane_id(), + title: float_pane.get_title(), + size: TerminalSize { + cols: dims.cols, + rows: dims.viewport_rows, + pixel_height: dims.pixel_height, + pixel_width: dims.pixel_width, + dpi: dims.dpi, + }, + working_dir: working_dir.map(Into::into), + is_active_pane: true, + is_zoomed_pane: false, + workspace: workspace.to_string(), + cursor_pos, + physical_top: 0, + top_row: 0, + left_col: 0, + tty_name: float_pane.tty_name(), + }) + } else { + PaneNode::Empty + }; + if let Some(root) = self.pane.as_ref() { - pane_tree( + (pane_tree( root, tab_id, window_id, @@ -879,9 +912,9 @@ impl TabInner { &workspace, 0, 0, - ) + ), codec_float_pane) } else { - PaneNode::Empty + (PaneNode::Empty, codec_float_pane) } } @@ -2241,7 +2274,7 @@ pub enum PaneNode { right: Box, node: SplitDirectionAndSize, }, - Leaf(PaneEntry), + Leaf(PaneEntry) } impl PaneNode { @@ -2253,7 +2286,7 @@ impl PaneNode { right: Box::new((*right).into_tree()), data: Some(node), }, - PaneNode::Leaf(e) => bintree::Tree::Leaf(e), + PaneNode::Leaf(e) => bintree::Tree::Leaf(e) } } diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 953ec72ba73..990814600cd 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -538,12 +538,12 @@ impl ClientDomain { .collect(); for (tabroot, tab_title) in panes.tabs.into_iter().zip(panes.tab_titles.iter()) { - let root_size = match tabroot.root_size() { + let root_size = match tabroot.0.root_size() { Some(size) => size, None => continue, }; - if let Some((remote_window_id, remote_tab_id)) = tabroot.window_and_tab_ids() { + if let Some((remote_window_id, remote_tab_id)) = tabroot.0.window_and_tab_ids() { let tab; remote_windows_to_forget.remove(&remote_window_id); @@ -577,7 +577,7 @@ impl ClientDomain { log::debug!("domain: {} tree: {:#?}", inner.local_domain_id, tabroot); let mut workspace = None; - tab.sync_with_pane_tree(root_size, tabroot, |entry| { + tab.sync_with_pane_tree(root_size, tabroot.0, |entry| { workspace.replace(entry.workspace.clone()); remote_panes_to_forget.remove(&entry.pane_id); if let Some(pane_id) = inner.remote_to_local_pane_id(entry.pane_id) { diff --git a/wezterm-gui/src/main.rs b/wezterm-gui/src/main.rs index f3b7688acdc..13f79915d85 100644 --- a/wezterm-gui/src/main.rs +++ b/wezterm-gui/src/main.rs @@ -576,7 +576,7 @@ impl Publish { let mut window_id = None; 'outer: for tabroot in panes.tabs { - let mut cursor = tabroot.into_tree().cursor(); + let mut cursor = tabroot.0.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index c6555730d44..06f25f3f19e 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -6,7 +6,7 @@ use mux::client::ClientId; use mux::domain::SplitSource; use mux::pane::{CachePolicy, Pane, PaneId}; use mux::renderable::{RenderableDimensions, StableCursorPosition}; -use mux::tab::TabId; +use mux::tab::{PaneNode, TabId}; use mux::{Mux, MuxNotification}; use promise::spawn::spawn_into_main_thread; use std::collections::HashMap; diff --git a/wezterm/src/cli/activate_tab.rs b/wezterm/src/cli/activate_tab.rs index 7cb32b34811..18f2206f0b7 100644 --- a/wezterm/src/cli/activate_tab.rs +++ b/wezterm/src/cli/activate_tab.rs @@ -54,7 +54,7 @@ impl ActivateTab { let mut window_by_tab_id = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.into_tree().cursor(); + let mut cursor = tabroot.0.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/list.rs b/wezterm/src/cli/list.rs index 35ab4fc2181..eec31d948f2 100644 --- a/wezterm/src/cli/list.rs +++ b/wezterm/src/cli/list.rs @@ -1,6 +1,7 @@ use crate::cli::CliOutputFormatKind; use clap::Parser; use serde::Serializer as _; +use mux::tab::PaneNode; use tabout::{tabulate_output, Alignment, Column}; use wezterm_client::client::Client; use wezterm_term::TerminalSize; @@ -21,7 +22,7 @@ impl ListCommand { let panes = client.list_panes().await?; for (tabroot, tab_title) in panes.tabs.into_iter().zip(panes.tab_titles.iter()) { - let mut cursor = tabroot.into_tree().cursor(); + let mut cursor = tabroot.0.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { @@ -41,6 +42,19 @@ impl ListCommand { Err(_) => break, } } + + if let PaneNode::Leaf(entry) = &tabroot.1 { + let window_title = panes + .window_titles + .get(&entry.window_id) + .map(|s| s.as_str()) + .unwrap_or(""); + output_items.push(CliListResultItem::from( + entry.clone(), + tab_title, + window_title, + )); + } } match self.format { CliOutputFormatKind::Json => { diff --git a/wezterm/src/cli/move_pane_to_new_tab.rs b/wezterm/src/cli/move_pane_to_new_tab.rs index 61ae5da632c..119e9c13d16 100644 --- a/wezterm/src/cli/move_pane_to_new_tab.rs +++ b/wezterm/src/cli/move_pane_to_new_tab.rs @@ -41,7 +41,7 @@ impl MovePaneToNewTab { let panes = client.list_panes().await?; let mut window_id = None; 'outer_move: for tabroot in panes.tabs { - let mut cursor = tabroot.into_tree().cursor(); + let mut cursor = tabroot.0.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/rename_workspace.rs b/wezterm/src/cli/rename_workspace.rs index 03c3018fa5d..c19a224b19f 100644 --- a/wezterm/src/cli/rename_workspace.rs +++ b/wezterm/src/cli/rename_workspace.rs @@ -29,7 +29,7 @@ impl RenameWorkspace { let mut pane_id_to_workspace = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.into_tree().cursor(); + let mut cursor = tabroot.0.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/set_tab_title.rs b/wezterm/src/cli/set_tab_title.rs index 6cf231222e5..440b855e2a4 100644 --- a/wezterm/src/cli/set_tab_title.rs +++ b/wezterm/src/cli/set_tab_title.rs @@ -28,7 +28,7 @@ impl SetTabTitle { let mut pane_id_to_tab_id = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.into_tree().cursor(); + let mut cursor = tabroot.0.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/set_window_title.rs b/wezterm/src/cli/set_window_title.rs index 089c87a2c13..f378cc07672 100644 --- a/wezterm/src/cli/set_window_title.rs +++ b/wezterm/src/cli/set_window_title.rs @@ -29,7 +29,7 @@ impl SetWindowTitle { let mut pane_id_to_window_id = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.into_tree().cursor(); + let mut cursor = tabroot.0.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/spawn_command.rs b/wezterm/src/cli/spawn_command.rs index 0e6e490cad5..f36c050ca68 100644 --- a/wezterm/src/cli/spawn_command.rs +++ b/wezterm/src/cli/spawn_command.rs @@ -63,7 +63,7 @@ impl SpawnCommand { let panes = client.list_panes().await?; let mut window_id = None; 'outer: for tabroot in panes.tabs { - let mut cursor = tabroot.into_tree().cursor(); + let mut cursor = tabroot.0.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/zoom_pane.rs b/wezterm/src/cli/zoom_pane.rs index e8b8f755872..ab3d5737ac0 100644 --- a/wezterm/src/cli/zoom_pane.rs +++ b/wezterm/src/cli/zoom_pane.rs @@ -37,7 +37,7 @@ impl ZoomPane { let mut tab_id_to_active_zoomed_pane_id = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.into_tree().cursor(); + let mut cursor = tabroot.0.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { From 7b20a181c129634916f9b48f88eac049f1fba7a8 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 22 Sep 2024 18:42:40 -0700 Subject: [PATCH 19/66] Re-attach the floating pane --- mux/src/tab.rs | 11 ++++++++--- wezterm-client/src/domain.rs | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 2f3ea168ce5..6a4c38af6c8 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -546,7 +546,7 @@ impl Tab { /// PaneEntry, or to create a new Pane from that entry. /// make_pane is expected to add the pane to the mux if it creates /// a new pane, otherwise the pane won't poll/update in the GUI. - pub fn sync_with_pane_tree(&self, size: TerminalSize, root: PaneNode, make_pane: F) + pub fn sync_with_pane_tree(&self, size: TerminalSize, root: (PaneNode, PaneNode), make_pane: F) where F: FnMut(PaneEntry) -> Arc, { @@ -794,7 +794,7 @@ impl TabInner { } } - fn sync_with_pane_tree(&mut self, size: TerminalSize, root: PaneNode, mut make_pane: F) + fn sync_with_pane_tree(&mut self, size: TerminalSize, root: (PaneNode, PaneNode), mut make_pane: F) where F: FnMut(PaneEntry) -> Arc, { @@ -803,7 +803,7 @@ impl TabInner { log::debug!("sync_with_pane_tree with size {:?}", size); - let t = build_from_pane_tree(root.into_tree(), &mut active, &mut zoomed, &mut make_pane); + let t = build_from_pane_tree(root.0.into_tree(), &mut active, &mut zoomed, &mut make_pane); let mut cursor = t.cursor(); self.active = 0; @@ -834,6 +834,11 @@ impl TabInner { self.zoomed = zoomed; self.size = size; + if let PaneNode::Leaf(entry) = root.1 { + let float_pane = make_pane(entry); + self.float_pane.replace(float_pane); + } + self.resize(size); log::debug!( diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 990814600cd..ff1439ed546 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -577,7 +577,7 @@ impl ClientDomain { log::debug!("domain: {} tree: {:#?}", inner.local_domain_id, tabroot); let mut workspace = None; - tab.sync_with_pane_tree(root_size, tabroot.0, |entry| { + tab.sync_with_pane_tree(root_size, tabroot, |entry| { workspace.replace(entry.workspace.clone()); remote_panes_to_forget.remove(&entry.pane_id); if let Some(pane_id) = inner.remote_to_local_pane_id(entry.pane_id) { From 29d39888a0352d085b8fb7c2f1d112a8e13e60f4 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Mon, 23 Sep 2024 18:53:18 -0700 Subject: [PATCH 20/66] Fix for floating pane not being removed from all connected wezterms for a single mux --- mux/src/tab.rs | 1 + wezterm-gui/src/termwindow/mod.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 6a4c38af6c8..9430cd84867 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1803,6 +1803,7 @@ impl TabInner { if let Some(float_pane) = &self.float_pane { if float_pane.is_dead() { + dead_panes.push(Arc::clone(float_pane)); let mux = Mux::get(); let pane_id = float_pane.pane_id(); self.float_pane = None; diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 9091113c1bd..80f34b1a5d8 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -1416,6 +1416,13 @@ impl TermWindow { return tab_overlay.pane_id() == pane_id; } + if let Some(float_pane) = tab.get_float_pane(){ + if(float_pane.pane.pane_id() == pane_id) + { + return true; + } + } + tab.contains_pane(pane_id) } From 5ad7fe60127aafd908f1ce022102e412a0e66cd5 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Mon, 23 Sep 2024 23:10:25 -0700 Subject: [PATCH 21/66] Refactored remove_float_pane --- mux/src/lib.rs | 14 -------------- mux/src/tab.rs | 3 --- 2 files changed, 17 deletions(-) diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 20554a67a96..75856fb2fd7 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -812,15 +812,6 @@ impl Mux { } } - fn remove_float_pane_internal(&self, pane_id: PaneId) { - log::debug!("removing float pane {}", pane_id); - if let Some(pane) = self.panes.write().remove(&pane_id).clone() { - log::debug!("killing float pane {}", pane_id); - pane.kill(); - self.notify(MuxNotification::PaneRemoved(pane_id)); - } - } - fn remove_tab_internal(&self, tab_id: TabId) -> Option> { log::debug!("remove_tab_internal tab {}", tab_id); @@ -885,11 +876,6 @@ impl Mux { self.prune_dead_windows(); } - pub fn remove_float_pane(&self, pane_id: PaneId) { - self.remove_float_pane_internal(pane_id); - self.prune_dead_windows(); - } - pub fn remove_tab(&self, tab_id: TabId) -> Option> { let tab = self.remove_tab_internal(tab_id); self.prune_dead_windows(); diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 9430cd84867..d651e86549b 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1804,10 +1804,7 @@ impl TabInner { if let Some(float_pane) = &self.float_pane { if float_pane.is_dead() { dead_panes.push(Arc::clone(float_pane)); - let mux = Mux::get(); - let pane_id = float_pane.pane_id(); self.float_pane = None; - mux.remove_float_pane(pane_id); } } From b2e2b79c3d01c26fbb420006269baa8ab7890dbb Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 24 Sep 2024 18:24:24 -0700 Subject: [PATCH 22/66] Fix for floating pane not working with rotate_clockwise and rotate_counter_clockwise --- mux/src/tab.rs | 4 ---- wezterm-gui/src/termwindow/render/paint.rs | 12 ++++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index d651e86549b..a55f4b145e2 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1116,10 +1116,6 @@ impl TabInner { } } - if let Some(float_pane) = self.get_float_pane() { - panes.push(float_pane); - } - let active_idx = self.active; let zoomed_id = self.zoomed.as_ref().map(|p| p.pane_id()); let root_size = self.size; diff --git a/wezterm-gui/src/termwindow/render/paint.rs b/wezterm-gui/src/termwindow/render/paint.rs index 1b037af284a..442b26dc30b 100644 --- a/wezterm-gui/src/termwindow/render/paint.rs +++ b/wezterm-gui/src/termwindow/render/paint.rs @@ -260,12 +260,12 @@ impl crate::TermWindow { mux::Mux::get().record_focus_for_current_identity(pos.pane.pane_id()); } } - if pos.is_float { - self.paint_pane(&pos, &mut float_layers).context("paint_pane")?; - self.paint_float_border(pos, &mut float_layers).context("paint_float_border")?; - } else { - self.paint_pane(&pos, &mut layers).context("paint_pane")?; - } + self.paint_pane(&pos, &mut layers).context("paint_pane")?; + } + + if let Some(float_pane) = self.get_float_pane_to_render(){ + self.paint_pane(&float_pane, &mut float_layers).context("paint_pane")?; + self.paint_float_border(float_pane, &mut float_layers).context("paint_float_border")?; } if let Some(pane) = self.get_active_pane_or_overlay() { From 7fa4ccacfeefe030faec44abb5e9a8adea79f47d Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 25 Sep 2024 17:59:21 -0700 Subject: [PATCH 23/66] Refactor compute_background_rect --- wezterm-gui/src/termwindow/render/borders.rs | 99 +++++++++++--------- wezterm-gui/src/termwindow/render/pane.rs | 45 +-------- 2 files changed, 57 insertions(+), 87 deletions(-) diff --git a/wezterm-gui/src/termwindow/render/borders.rs b/wezterm-gui/src/termwindow/render/borders.rs index 95809701388..3515c20a19d 100644 --- a/wezterm-gui/src/termwindow/render/borders.rs +++ b/wezterm-gui/src/termwindow/render/borders.rs @@ -80,6 +80,59 @@ impl crate::TermWindow { Ok(()) } + pub fn compute_background_rect( + &self, + pos: &PositionedPane, + padding_left: f32, + padding_top: f32, + border: &Border, + top_pixel_y: f32, + cell_width: f32, + cell_height: f32, + ) -> euclid::Rect { + let (x, width_delta) = if pos.left == 0 { + ( + 0., + padding_left + border.left.get() as f32 + (cell_width / 2.0), + ) + } else { + ( + padding_left + border.left.get() as f32 - (cell_width / 2.0) + + (pos.left as f32 * cell_width), + cell_width, + ) + }; + + let (y, height_delta) = if pos.top == 0 { + ( + (top_pixel_y - padding_top), + padding_top + (cell_height / 2.0), + ) + } else { + ( + top_pixel_y + (pos.top as f32 * cell_height) - (cell_height / 2.0), + cell_height, + ) + }; + + euclid::rect( + x, + y, + // Width calculation + if pos.left + pos.width >= self.terminal_size.cols as usize { + self.dimensions.pixel_width as f32 - x + } else { + (pos.width as f32 * cell_width) + width_delta + }, + // Height calculation + if pos.top + pos.height >= self.terminal_size.rows as usize { + self.dimensions.pixel_height as f32 - y + } else { + (pos.height as f32 * cell_height) + height_delta + }, + ) + } + pub fn paint_float_border( &mut self, pos: PositionedPane, @@ -105,50 +158,8 @@ impl crate::TermWindow { let cell_height = self.render_metrics.cell_size.height as f32; let cell_width = self.render_metrics.cell_size.width as f32; - //refactor with logic from paint_pane? - let background_rect: euclid::Rect = { - // We want to fill out to the edges of the splits - let (x, width_delta) = if pos.left == 0 { - ( - 0., - padding_left + os_border.left.get() as f32 + (cell_width / 2.0), - ) - } else { - ( - padding_left + os_border.left.get() as f32 - (cell_width / 2.0) - + (pos.left as f32 * cell_width), - cell_width, - ) - }; - - let (y, height_delta) = if pos.top == 0 { - ( - (top_pixel_y - padding_top), - padding_top + (cell_height / 2.0), - ) - } else { - ( - top_pixel_y + (pos.top as f32 * cell_height) - (cell_height / 2.0), - cell_height, - ) - }; - euclid::rect( - x, - y, - // Go all the way to the right edge if we're right-most - if pos.left + pos.width >= self.terminal_size.cols as usize { - self.dimensions.pixel_width as f32 - x - } else { - (pos.width as f32 * cell_width) + width_delta - }, - // Go all the way to the bottom if we're bottom-most - if pos.top + pos.height >= self.terminal_size.rows as usize { - self.dimensions.pixel_height as f32 - y - } else { - (pos.height as f32 * cell_height) + height_delta as f32 - }, - ) - }; + let background_rect = self.compute_background_rect(&pos, + padding_left, padding_top, &os_border, top_pixel_y, cell_width, cell_height); let pos_y = background_rect.origin.y - float_border.top.get() as f32; let pos_x = background_rect.origin.x - float_border.left.get() as f32; diff --git a/wezterm-gui/src/termwindow/render/pane.rs b/wezterm-gui/src/termwindow/render/pane.rs index a7c5f797fb2..bc76615ac95 100644 --- a/wezterm-gui/src/termwindow/render/pane.rs +++ b/wezterm-gui/src/termwindow/render/pane.rs @@ -107,49 +107,8 @@ impl crate::TermWindow { let cell_width = self.render_metrics.cell_size.width as f32; let cell_height = self.render_metrics.cell_size.height as f32; - let background_rect = { - // We want to fill out to the edges of the splits - let (x, width_delta) = if pos.left == 0 { - ( - 0., - padding_left + border.left.get() as f32 + (cell_width / 2.0), - ) - } else { - ( - padding_left + border.left.get() as f32 - (cell_width / 2.0) - + (pos.left as f32 * cell_width), - cell_width, - ) - }; - - let (y, height_delta) = if pos.top == 0 { - ( - (top_pixel_y - padding_top), - padding_top + (cell_height / 2.0), - ) - } else { - ( - top_pixel_y + (pos.top as f32 * cell_height) - (cell_height / 2.0), - cell_height, - ) - }; - euclid::rect( - x, - y, - // Go all the way to the right edge if we're right-most - if pos.left + pos.width >= self.terminal_size.cols as usize { - self.dimensions.pixel_width as f32 - x - } else { - (pos.width as f32 * cell_width) + width_delta - }, - // Go all the way to the bottom if we're bottom-most - if pos.top + pos.height >= self.terminal_size.rows as usize { - self.dimensions.pixel_height as f32 - y - } else { - (pos.height as f32 * cell_height) + height_delta as f32 - }, - ) - }; + let background_rect = self.compute_background_rect(&pos, padding_top, + padding_left, &border, top_pixel_y, cell_width, cell_height); if self.window_background.is_empty() { // Per-pane, palette-specified background From d771fb772067e0fdfc0e26f101089aed3baf8fab Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 25 Sep 2024 18:03:26 -0700 Subject: [PATCH 24/66] bump codec version --- codec/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 86a2a2d0251..822f5a02c3a 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -441,7 +441,7 @@ macro_rules! pdu { /// The overall version of the codec. /// This must be bumped when backwards incompatible changes /// are made to the types and protocol. -pub const CODEC_VERSION: usize = 43; +pub const CODEC_VERSION: usize = 44; // Defines the Pdu enum. // Each struct has an explicit identifying number. From 20fbe7a99f718b16cae475cd6aa5dd4258e4f3c2 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Thu, 26 Sep 2024 00:04:58 -0700 Subject: [PATCH 25/66] Fix for resize not being applied to floating pane when updating window size. --- mux/src/tab.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index a55f4b145e2..659316cc9c7 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1239,6 +1239,7 @@ impl TabInner { } if let Some(float_pane) = &self.float_pane { + self.size = size; let float_size = self.compute_float_size(); float_pane.resize(float_size).ok(); } From e240f6763bb7b8c8bda21fe55d1b4a952de28196 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 28 Sep 2024 21:54:43 -0700 Subject: [PATCH 26/66] Attempt at being able to hide the floating pane --- codec/src/lib.rs | 11 ++- config/src/keyassignment.rs | 1 + mux/src/lib.rs | 20 +++++ mux/src/tab.rs | 83 ++++++++++++++----- wezterm-client/src/client.rs | 23 +++++ wezterm-client/src/domain.rs | 27 ++++++ wezterm-gui/src/commands.rs | 9 ++ wezterm-gui/src/frontend.rs | 1 + wezterm-gui/src/termwindow/mod.rs | 18 +++- wezterm-gui/src/termwindow/mouseevent.rs | 7 +- wezterm-mux-server-impl/src/dispatch.rs | 14 ++++ wezterm-mux-server-impl/src/sessionhandler.rs | 19 +++++ wezterm/src/cli/list.rs | 2 +- 13 files changed, 208 insertions(+), 27 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 822f5a02c3a..24c82d749df 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -16,7 +16,7 @@ use config::keyassignment::{PaneDirection, ScrollbackEraseMode}; use mux::client::{ClientId, ClientInfo}; use mux::pane::PaneId; use mux::renderable::{RenderableDimensions, StableCursorPosition}; -use mux::tab::{PaneNode, SerdeUrl, SplitRequest, TabId}; +use mux::tab::{PaneNode, SerdeUrl, SplitRequest, Tab, TabId}; use mux::window::WindowId; use portable_pty::CommandBuilder; use rangeset::*; @@ -503,6 +503,7 @@ pdu! { GetPaneDirectionResponse: 61, AdjustPaneSize: 62, FloatPane: 63, + FloatPaneVisibilityChanged: 64 } impl Pdu { @@ -645,7 +646,7 @@ pub struct ListPanes {} #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct ListPanesResponse { - pub tabs: Vec<(PaneNode, PaneNode)>, + pub tabs: Vec<(PaneNode, (PaneNode, bool))>, pub tab_titles: Vec, pub window_titles: HashMap, } @@ -658,6 +659,12 @@ pub struct FloatPane { pub domain: config::keyassignment::SpawnTabDomain, } +#[derive(Deserialize, Serialize, PartialEq, Debug)] +pub struct FloatPaneVisibilityChanged { + pub tab_id: TabId, + pub visible: bool +} + #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct SplitPane { pub pane_id: PaneId, diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index 939f281fca8..91ca4f29092 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -550,6 +550,7 @@ pub enum KeyAssignment { SplitHorizontal(SpawnCommand), SplitVertical(SpawnCommand), FloatPane(SpawnCommand), + ToggleFloatingPane, ShowLauncher, ShowLauncherArgs(LauncherActionArgs), ClearScrollback(ScrollbackEraseMode), diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 75856fb2fd7..9fec7c951f6 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -93,6 +93,10 @@ pub enum MuxNotification { old_workspace: String, new_workspace: String, }, + FloatPaneVisibilityChanged{ + tab_id: TabId, + visible: bool, + } } static SUB_ID: AtomicUsize = AtomicUsize::new(0); @@ -525,6 +529,16 @@ impl Mux { } } + pub fn set_float_pane_visibility(&self, tab_id: TabId, visible: bool) -> anyhow::Result<()> { + let tab = self + .get_tab(tab_id) + .ok_or_else(|| anyhow::anyhow!("tab {tab_id} not found"))?; + + tab.set_float_pane_visibility(visible); + + Ok(()) + } + /// Called by PaneFocused event handlers to reconcile a remote /// pane focus event and apply its effects locally pub fn focus_pane_and_containing_tab(&self, pane_id: PaneId) -> anyhow::Result<()> { @@ -1056,6 +1070,12 @@ impl Mux { pub fn resolve_pane_id(&self, pane_id: PaneId) -> Option<(DomainId, WindowId, TabId)> { let mut ids = None; for tab in self.tabs.read().values() { + if let Some(float_pane) = tab.get_float_pane() { + if pane_id == float_pane.pane.pane_id() { + ids = Some((tab.tab_id(), float_pane.pane.domain_id())); + break; + } + } for p in tab.iter_panes_ignoring_zoom() { if p.pane.pane_id() == pane_id { ids = Some((tab.tab_id(), p.pane.domain_id())); diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 659316cc9c7..b42ecc5bb0a 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -46,6 +46,7 @@ struct TabInner { zoomed: Option>, title: String, float_pane: Option>, + float_pane_visible: bool, recency: Recency, } @@ -76,8 +77,7 @@ pub struct PositionedPane { pub height: usize, pub pixel_height: usize, /// The pane instance - pub pane: Arc, - pub is_float: bool, + pub pane: Arc } impl std::fmt::Debug for PositionedPane { @@ -546,14 +546,14 @@ impl Tab { /// PaneEntry, or to create a new Pane from that entry. /// make_pane is expected to add the pane to the mux if it creates /// a new pane, otherwise the pane won't poll/update in the GUI. - pub fn sync_with_pane_tree(&self, size: TerminalSize, root: (PaneNode, PaneNode), make_pane: F) + pub fn sync_with_pane_tree(&self, size: TerminalSize, root: (PaneNode, (PaneNode, bool)), make_pane: F) where F: FnMut(PaneEntry) -> Arc, { self.inner.lock().sync_with_pane_tree(size, root, make_pane) } - pub fn codec_pane_tree(&self) -> (PaneNode, PaneNode) { + pub fn codec_pane_tree(&self) -> (PaneNode, (PaneNode, bool)) { self.inner.lock().codec_pane_tree() } @@ -579,6 +579,10 @@ impl Tab { self.inner.lock().iter_panes() } + pub fn float_pane_is_visible(&self) -> bool { + self.inner.lock().float_pane_is_visible() + } + pub fn get_float_pane(&self) -> Option { self.inner.lock().get_float_pane() } @@ -712,6 +716,10 @@ impl Tab { self.inner.lock().set_active_pane(pane) } + pub fn set_float_pane_visibility(&self, visible: bool) { + self.inner.lock().set_float_pane_visibility(visible); + } + pub fn set_active_idx(&self, pane_index: usize) { self.inner.lock().set_active_idx(pane_index) } @@ -764,6 +772,12 @@ impl Tab { .split_and_insert(pane_index, request, pane) } + pub fn toggle_float(&self) { + self.inner + .lock() + .toggle_float_pane(); + } + pub fn insert_float( &self, float_size: TerminalSize, @@ -791,10 +805,11 @@ impl TabInner { title: String::new(), recency: Recency::default(), float_pane: None, + float_pane_visible: false } } - fn sync_with_pane_tree(&mut self, size: TerminalSize, root: (PaneNode, PaneNode), mut make_pane: F) + fn sync_with_pane_tree(&mut self, size: TerminalSize, root: (PaneNode, (PaneNode, bool)), mut make_pane: F) where F: FnMut(PaneEntry) -> Arc, { @@ -834,9 +849,10 @@ impl TabInner { self.zoomed = zoomed; self.size = size; - if let PaneNode::Leaf(entry) = root.1 { + if let PaneNode::Leaf(entry) = root.1.0 { let float_pane = make_pane(entry); self.float_pane.replace(float_pane); + self.float_pane_visible = root.1.1; } self.resize(size); @@ -850,14 +866,14 @@ impl TabInner { assert!(self.pane.is_some()); } - fn codec_pane_tree(&mut self) -> (PaneNode, PaneNode) { + fn codec_pane_tree(&mut self) -> (PaneNode, (PaneNode, bool)) { let mux = Mux::get(); let tab_id = self.id; let window_id = match mux.window_containing_tab(tab_id) { Some(w) => w, None => { log::error!("no window contains tab {}", tab_id); - return (PaneNode::Empty, PaneNode::Empty); + return (PaneNode::Empty, (PaneNode::Empty, false)); } }; @@ -868,7 +884,7 @@ impl TabInner { Some(ws) => ws, None => { log::error!("window id {} doesn't have a window!?", window_id); - return (PaneNode::Empty, PaneNode::Empty); + return (PaneNode::Empty, (PaneNode::Empty, false)); } }; @@ -881,7 +897,7 @@ impl TabInner { let working_dir = float_pane.get_current_working_dir(CachePolicy::AllowStale); let cursor_pos = float_pane.get_cursor_position(); - PaneNode::Leaf(PaneEntry { + (PaneNode::Leaf(PaneEntry { window_id, tab_id, pane_id: float_pane.pane_id(), @@ -902,9 +918,9 @@ impl TabInner { top_row: 0, left_col: 0, tty_name: float_pane.tty_name(), - }) + }), self.float_pane_visible) } else { - PaneNode::Empty + (PaneNode::Empty, false) }; if let Some(root) = self.pane.as_ref() { @@ -996,6 +1012,10 @@ impl TabInner { self.iter_panes_impl(true) } + fn float_pane_is_visible(&self) -> bool { + self.float_pane_visible + } + fn get_float_pane(&self) -> Option { if let Some(float_pane) = self.float_pane.as_ref() { let root_size = self.size; @@ -1016,8 +1036,7 @@ impl TabInner { height: size.rows, pixel_width: size.pixel_width, pixel_height: size.pixel_height, - pane: Arc::clone(float_pane), - is_float: true + pane: Arc::clone(float_pane) }) } else { None @@ -1109,8 +1128,7 @@ impl TabInner { pixel_width: size.pixel_width.into(), height: size.rows.into(), pixel_height: size.pixel_height.into(), - pane: Arc::clone(zoomed), - is_float: false + pane: Arc::clone(zoomed) }); return panes; } @@ -1156,8 +1174,7 @@ impl TabInner { height: dims.rows as _, pixel_width: dims.pixel_width as _, pixel_height: dims.pixel_height as _, - pane, - is_float: false + pane }); } @@ -1842,7 +1859,7 @@ impl TabInner { } fn is_float_active(&self) -> bool { - return self.float_pane.is_some(); + self.float_pane.is_some() && self.float_pane_visible } fn get_active_pane(&mut self) -> Option> { @@ -1850,8 +1867,10 @@ impl TabInner { return Some(Arc::clone(zoomed)); } - if let Some(float_pane) = self.float_pane.as_ref() { - return Some(Arc::clone(float_pane)); + if self.float_pane_visible { + if let Some(float_pane) = self.float_pane.as_ref() { + return Some(Arc::clone(float_pane)); + } } self.iter_panes_ignoring_zoom() @@ -1884,6 +1903,17 @@ impl TabInner { } } + fn set_float_pane_visibility(&mut self, visible: bool) { + if visible != self.float_pane_visible { + self.float_pane_visible = visible; + let mux = Mux::get(); + mux.notify(MuxNotification::FloatPaneVisibilityChanged{ + tab_id: self.id, + visible, + }); + } + } + fn advise_focus_change(&mut self, prior: Option>) { let mux = Mux::get(); let current = self.get_active_pane(); @@ -2105,6 +2135,16 @@ impl TabInner { }) } + fn toggle_float_pane(&mut self) { + if self.float_pane.is_some() { + if self.float_pane_visible { + self.set_float_pane_visibility(false); + } else { + self.set_float_pane_visibility(true); + } + } + } + fn add_float_pane( &mut self, float_size: TerminalSize, @@ -2119,6 +2159,7 @@ impl TabInner { } self.float_pane = Some(Arc::clone(&pane)); + self.float_pane_visible = true; pane.resize(float_size)?; Ok(()) diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index 518bd9e0ce8..5e96978428d 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -33,6 +33,7 @@ use std::path::{Path, PathBuf}; use std::thread; use std::time::Duration; use thiserror::Error; +use codec::FloatPaneVisibilityChanged; use wezterm_uds::UnixStream; #[derive(Error, Debug)] @@ -297,6 +298,27 @@ fn process_unilateral( .detach(); return Ok(()); } + Pdu::FloatPaneVisibilityChanged(FloatPaneVisibilityChanged { tab_id, visible }) => { + let tab_id = *tab_id; + let visible = visible.clone(); + promise::spawn::spawn_into_main_thread(async move { + let mux = Mux::try_get().ok_or_else(|| anyhow!("no more mux"))?; + let client_domain = mux + .get_domain(local_domain_id) + .ok_or_else(|| anyhow!("no such domain {}", local_domain_id))?; + let client_domain = + client_domain + .downcast_ref::() + .ok_or_else(|| { + anyhow!("domain {} is not a ClientDomain instance", local_domain_id) + })?; + + client_domain.set_float_pane_visibility(tab_id, visible); + anyhow::Result::<()>::Ok(()) + }) + .detach(); + return Ok(()); + } Pdu::TabResized(_) | Pdu::TabAddedToWindow(_) => { log::trace!("resync due to {:?}", decoded.pdu); promise::spawn::spawn_into_main_thread(async move { @@ -1343,6 +1365,7 @@ impl Client { rpc!(spawn_v2, SpawnV2, SpawnResponse); rpc!(split_pane, SplitPane, SpawnResponse); rpc!(add_float_pane, FloatPane, SpawnResponse); + rpc!(set_float_pane_visibility, FloatPaneVisibilityChanged, UnitResponse); rpc!( move_pane_to_new_tab, MovePaneToNewTab, diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index ff1439ed546..5835956577a 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -15,6 +15,7 @@ use portable_pty::CommandBuilder; use promise::spawn::spawn_into_new_thread; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, Mutex}; +use mux::MuxNotification::FloatPaneVisibilityChanged; use wezterm_term::TerminalSize; pub struct ClientInner { @@ -354,6 +355,22 @@ fn mux_notify_client_domain(local_domain_id: DomainId, notif: MuxNotification) - } } } + MuxNotification::FloatPaneVisibilityChanged { tab_id, visible } => { + if let Some(remote_tab_id) = client_domain.local_to_remote_tab_id(tab_id) { + if let Some(inner) = client_domain.inner() { + promise::spawn::spawn(async move { + inner + .client + .set_float_pane_visibility(codec::FloatPaneVisibilityChanged { + tab_id: remote_tab_id, + visible, + }) + .await + }) + .detach(); + } + } + } MuxNotification::WindowTitleChanged { window_id, title: _, @@ -501,6 +518,16 @@ impl ClientDomain { } } + pub fn set_float_pane_visibility(&self, remote_tab_id: TabId, visible: bool) { + if let Some(inner) = self.inner() { + if let Some(local_tab_id) = inner.remote_to_local_tab_id(remote_tab_id) { + if let Some(tab) = Mux::get().get_tab(local_tab_id) { + tab.set_float_pane_visibility(visible); + } + } + } + } + fn process_pane_list( inner: Arc, panes: ListPanesResponse, diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 8bdad3b4f04..46d1675daeb 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1621,6 +1621,14 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + brief: "Toggle floating pane".into(), + doc: "Toggles the visibility state for the current pane".into(), + keys: vec![(Modifiers::CTRL.union(Modifiers::SHIFT), "e".into())], + args: &[ArgType::ActivePane], + menubar: &["Window"], + icon: Some("md_fullscreen"), + }, ActivateLastTab => CommandDef { brief: "Activate the last active tab".into(), doc: "If there was no prior active tab, has no effect.".into(), @@ -2160,6 +2168,7 @@ fn compute_default_actions() -> Vec { ActivatePaneDirection(PaneDirection::Up), ActivatePaneDirection(PaneDirection::Down), TogglePaneZoomState, + ToggleFloatingPane, ActivateLastTab, ShowLauncher, ShowTabNavigator, diff --git a/wezterm-gui/src/frontend.rs b/wezterm-gui/src/frontend.rs index 66c61e72acb..b9bab5d24e7 100644 --- a/wezterm-gui/src/frontend.rs +++ b/wezterm-gui/src/frontend.rs @@ -94,6 +94,7 @@ impl GuiFrontEnd { MuxNotification::WindowInvalidated(_) => {} MuxNotification::PaneOutput(_) => {} MuxNotification::PaneAdded(_) => {} + MuxNotification::FloatPaneVisibilityChanged { .. } => { } MuxNotification::Alert { pane_id, alert: diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 80f34b1a5d8..439bc7d761a 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -1289,6 +1289,7 @@ impl TermWindow { // Also handled by clientpane self.update_title_post_status(); } + MuxNotification::FloatPaneVisibilityChanged { .. } => { } MuxNotification::TabResized(_) => { // Also handled by wezterm-client self.update_title_post_status(); @@ -1502,6 +1503,8 @@ impl TermWindow { return true; } } + MuxNotification::FloatPaneVisibilityChanged { .. } => { + } MuxNotification::Alert { alert: Alert::ToastNotification { .. }, .. @@ -2929,6 +2932,14 @@ impl TermWindow { }; tab.toggle_zoom(); } + ToggleFloatingPane => { + let mux = Mux::get(); + let tab = match mux.get_active_tab_for_window(self.mux_window_id) { + Some(tab) => tab, + None => return Ok(PerformAssignmentResult::Handled), + }; + tab.toggle_float(); + } SetPaneZoomState(zoomed) => { let mux = Mux::get(); let tab = match mux.get_active_tab_for_window(self.mux_window_id) { @@ -3453,8 +3464,7 @@ impl TermWindow { height: size.rows as _, pixel_width: size.cols as usize * self.render_metrics.cell_size.width as usize, pixel_height: size.rows as usize * self.render_metrics.cell_size.height as usize, - pane, - is_float: false + pane }] } else { let mut panes = tab.iter_panes(); @@ -3484,6 +3494,10 @@ impl TermWindow { None => return None, }; + if !tab.float_pane_is_visible() { + return None; + } + tab.get_float_pane() } diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index 92463badab3..bdc4476179c 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -9,7 +9,7 @@ use ::window::{ use config::keyassignment::{KeyAssignment, MouseEventTrigger, SpawnTabDomain}; use config::MouseEventAltScreen; use mux::pane::{Pane, WithPaneLines}; -use mux::tab::SplitDirection; +use mux::tab::{SplitDirection, Tab}; use mux::Mux; use mux_lua::MuxPane; use std::convert::TryInto; @@ -674,6 +674,11 @@ impl super::TermWindow { position.row as usize <= float_pane.top + float_pane.height; if !mouse_in_float { + let mux = Mux::get(); + if let Some(tab) = mux.get_active_tab_for_window(self.mux_window_id){ + mux.set_float_pane_visibility(tab.tab_id(), false).ok(); + } + // Mouse events are not dispatched to the other panes when // a floating pane is active, this is to prevent users from selecting one of the // panes that the float is on top of and encountering some weird behavior, ex. diff --git a/wezterm-mux-server-impl/src/dispatch.rs b/wezterm-mux-server-impl/src/dispatch.rs index c3728f1c2dc..8f981b4605f 100644 --- a/wezterm-mux-server-impl/src/dispatch.rs +++ b/wezterm-mux-server-impl/src/dispatch.rs @@ -3,6 +3,7 @@ use anyhow::Context; use async_ossl::AsyncSslStream; use codec::{DecodedPdu, Pdu}; use futures::FutureExt; +use log::log; use mux::{Mux, MuxNotification}; use smol::prelude::*; use smol::Async; @@ -82,6 +83,7 @@ where return Err(err).context("reading Pdu from client"); } }; + handler.process_one(decoded); } Ok(Item::WritePdu(decoded)) => { @@ -204,6 +206,18 @@ where } Ok(Item::Notif(MuxNotification::ActiveWorkspaceChanged(_))) => {} Ok(Item::Notif(MuxNotification::Empty)) => {} + Ok(Item::Notif(MuxNotification::FloatPaneVisibilityChanged{ + tab_id, + visible + })) => { + Pdu::FloatPaneVisibilityChanged(codec::FloatPaneVisibilityChanged { + tab_id, + visible + }) + .encode_async(&mut stream, 0) + .await?; + stream.flush().await.context("flushing PDU to client")?; + } Err(err) => { log::error!("process_async Err {}", err); return Ok(()); diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index 06f25f3f19e..7336223f789 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -732,6 +732,25 @@ impl SessionHandler { .detach(); } + Pdu::FloatPaneVisibilityChanged(FloatPaneVisibilityChanged{ tab_id, visible }) => { + let client_id = self.client_id.clone(); + spawn_into_main_thread(async move { + catch( + move || { + let mux = Mux::get(); + let _identity = mux.with_identity(client_id); + + let tab = mux + .get_tab(tab_id) + .ok_or_else(|| anyhow!("no such tab {}", tab_id))?; + tab.set_float_pane_visibility(visible); + Ok(Pdu::UnitResponse(UnitResponse {})) + }, + send_response + ); + }).detach() + } + Pdu::MovePaneToNewTab(request) => { let client_id = self.client_id.clone(); spawn_into_main_thread(async move { diff --git a/wezterm/src/cli/list.rs b/wezterm/src/cli/list.rs index eec31d948f2..d0b4d825a7d 100644 --- a/wezterm/src/cli/list.rs +++ b/wezterm/src/cli/list.rs @@ -43,7 +43,7 @@ impl ListCommand { } } - if let PaneNode::Leaf(entry) = &tabroot.1 { + if let PaneNode::Leaf(entry) = &tabroot.1.0 { let window_title = panes .window_titles .get(&entry.window_id) From 0d05175baa4aa93119133eba157dad3502d2da74 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 4 Oct 2024 21:29:36 -0700 Subject: [PATCH 27/66] Add is_floating to cli list --- docs/config/lua/MuxTab/panes_with_info.md | 1 + docs/config/lua/PaneInformation.md | 1 + mux/src/tab.rs | 8 ++++++++ wezterm-gui/src/termwindow/mod.rs | 1 + wezterm/src/cli/list.rs | 3 +++ 5 files changed, 14 insertions(+) diff --git a/docs/config/lua/MuxTab/panes_with_info.md b/docs/config/lua/MuxTab/panes_with_info.md index f50676bb442..dda9315c7e6 100644 --- a/docs/config/lua/MuxTab/panes_with_info.md +++ b/docs/config/lua/MuxTab/panes_with_info.md @@ -10,6 +10,7 @@ Each element is a lua table with the following fields: * `index` - the topological pane index * `is_active` - a boolean indicating whether this is the active pane withing the tab * `is_zoomed` - a boolean indicating whether this pane is zoomed +* `is_floating` - a boolean indicating whether this pane is floating * `left` - The offset from the top left corner of the containing tab to the top left corner of this pane, in cells. * `top` - The offset from the top left corner of the containing tab to the top left corner of this pane, in cells. * `width` - The width of this pane in cells diff --git a/docs/config/lua/PaneInformation.md b/docs/config/lua/PaneInformation.md index a2c417201d2..50a6b134d90 100644 --- a/docs/config/lua/PaneInformation.md +++ b/docs/config/lua/PaneInformation.md @@ -11,6 +11,7 @@ The `PaneInformation` struct contains the following fields: * `pane_index` - the logical position of the pane within its containing layout * `is_active` - is true if the pane is the active pane within its containing tab * `is_zoomed` - is true if the pane is in the zoomed state +* `is_floating` - is true if the pane is in the floating state * `left` - the cell x coordinate of the left edge of the pane * `top` - the cell y coordinate of the top edge of the pane * `width` - the width of the pane in cells diff --git a/mux/src/tab.rs b/mux/src/tab.rs index b42ecc5bb0a..5c1c310c8c8 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -64,6 +64,8 @@ pub struct PositionedPane { pub is_active: bool, /// true if this pane is zoomed pub is_zoomed: bool, + /// true if this pane is floating + pub is_floating: bool, /// The offset from the top left corner of the containing tab to the top /// left corner of this pane, in cells. pub left: usize, @@ -266,6 +268,7 @@ fn pane_tree( title: pane.get_title(), is_active_pane: is_pane(pane, &active), is_zoomed_pane: is_pane(pane, &zoomed), + is_floating_pane: false, size: TerminalSize { cols: dims.cols, rows: dims.viewport_rows, @@ -912,6 +915,7 @@ impl TabInner { working_dir: working_dir.map(Into::into), is_active_pane: true, is_zoomed_pane: false, + is_floating_pane: true, workspace: workspace.to_string(), cursor_pos, physical_top: 0, @@ -1030,6 +1034,7 @@ impl TabInner { index: 0, is_active: true, is_zoomed: false, + is_floating: true, left, top, width: size.cols, @@ -1122,6 +1127,7 @@ impl TabInner { index: 0, is_active: true, is_zoomed: true, + is_floating: false, left: 0, top: 0, width: size.cols.into(), @@ -1168,6 +1174,7 @@ impl TabInner { index, is_active: index == active_idx, is_zoomed: zoomed_id == Some(pane.pane_id()), + is_floating: false, left, top, width: dims.cols as _, @@ -2363,6 +2370,7 @@ pub struct PaneEntry { pub working_dir: Option, pub is_active_pane: bool, pub is_zoomed_pane: bool, + pub is_floating_pane: bool, pub workspace: String, pub cursor_pos: StableCursorPosition, pub physical_top: StableRowIndex, diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 439bc7d761a..05f7cdfbdb6 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -3458,6 +3458,7 @@ impl TermWindow { index: 0, is_active: true, is_zoomed: false, + is_floating: false, left: 0, top: 0, width: size.cols as _, diff --git a/wezterm/src/cli/list.rs b/wezterm/src/cli/list.rs index d0b4d825a7d..8d0598ceb7b 100644 --- a/wezterm/src/cli/list.rs +++ b/wezterm/src/cli/list.rs @@ -152,6 +152,7 @@ struct CliListResultItem { window_title: String, is_active: bool, is_zoomed: bool, + is_floating: bool, tty_name: Option, } @@ -170,6 +171,7 @@ impl CliListResultItem { top_row, is_active_pane, is_zoomed_pane, + is_floating_pane, tty_name, size: TerminalSize { @@ -210,6 +212,7 @@ impl CliListResultItem { window_title: window_title.to_string(), is_active: is_active_pane, is_zoomed: is_zoomed_pane, + is_floating: is_floating_pane, tty_name, } } From 9534be7caa41c67f7d88148bc6433f20890c64de Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 4 Oct 2024 21:33:27 -0700 Subject: [PATCH 28/66] Update to only hide the floating pane if clicked outside of floating pane. --- wezterm-gui/src/termwindow/mouseevent.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index bdc4476179c..5fe4a0526bd 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -22,6 +22,7 @@ use termwiz::surface::Line; use wezterm_dynamic::ToDynamic; use wezterm_term::input::{MouseButton, MouseEventKind as TMEK}; use wezterm_term::{ClickPosition, LastMouseClick, StableRowIndex}; +use window::MouseEventKind; impl super::TermWindow { fn resolve_ui_item(&self, event: &MouseEvent) -> Option { @@ -676,7 +677,13 @@ impl super::TermWindow { if !mouse_in_float { let mux = Mux::get(); if let Some(tab) = mux.get_active_tab_for_window(self.mux_window_id){ - mux.set_float_pane_visibility(tab.tab_id(), false).ok(); + //Hide floating pane if mouse is clicked outside the floating pane + match &event.kind { + WMEK::Press(_) => { + mux.set_float_pane_visibility(tab.tab_id(), false).ok(); + } + _ => {} + } } // Mouse events are not dispatched to the other panes when From 2fb7f8bc449580e0e042711bc5d9bac23408f8f3 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 4 Oct 2024 21:55:38 -0700 Subject: [PATCH 29/66] Update so that cli active-pane --pane-id works floating pane. Note if the floating pane is hidden it won't show if the terminal window isn't focused (it will aprear when the terminal window gets focus), I think this matches the behavior of splits but is more noticeable with the floating pane becoming visible. --- mux/src/tab.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 5c1c310c8c8..63ca677ccd3 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1891,6 +1891,13 @@ impl TabInner { } fn set_active_pane(&mut self, pane: &Arc) { + if let Some(float_pane) = self.float_pane.as_ref() { + if float_pane.pane_id() == pane.pane_id() { + self.set_float_pane_visibility(true); + return; + } + } + if self.zoomed.is_some() { if !configuration().unzoom_on_switch_pane { return; From d5a0072031ae9d5a63427b1f46817df6c0060d3c Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 5 Oct 2024 22:25:28 -0700 Subject: [PATCH 30/66] Bump codec version --- codec/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 24c82d749df..65fb1d73373 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -441,7 +441,7 @@ macro_rules! pdu { /// The overall version of the codec. /// This must be bumped when backwards incompatible changes /// are made to the types and protocol. -pub const CODEC_VERSION: usize = 44; +pub const CODEC_VERSION: usize = 45; // Defines the Pdu enum. // Each struct has an explicit identifying number. From 2e83b117ca391491280cb472343cf64aa08851ef Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 5 Oct 2024 23:22:43 -0700 Subject: [PATCH 31/66] Update so that cli activate-pane will show/hide the floating pane when the terminal window is not focused. --- mux/src/tab.rs | 7 +++++++ wezterm-mux-server-impl/src/sessionhandler.rs | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 63ca677ccd3..e839ac511e6 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1898,6 +1898,10 @@ impl TabInner { } } + if self.float_pane_visible { + self.set_float_pane_visibility(false); + } + if self.zoomed.is_some() { if !configuration().unzoom_on_switch_pane { return; @@ -1925,6 +1929,9 @@ impl TabInner { tab_id: self.id, visible, }); + if let Some(window_id) = mux.window_containing_tab(self.id) { + mux.notify(MuxNotification::WindowInvalidated(window_id)); + } } } diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index 7336223f789..b57cb075a38 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -729,7 +729,7 @@ impl SessionHandler { spawn_into_main_thread(async move { schedule_float_pane(float, send_response, client_id); }) - .detach(); + .detach(); } Pdu::FloatPaneVisibilityChanged(FloatPaneVisibilityChanged{ tab_id, visible }) => { @@ -744,6 +744,7 @@ impl SessionHandler { .get_tab(tab_id) .ok_or_else(|| anyhow!("no such tab {}", tab_id))?; tab.set_float_pane_visibility(visible); + mux.notify(mux::MuxNotification::FloatPaneVisibilityChanged{tab_id, visible}); Ok(Pdu::UnitResponse(UnitResponse {})) }, send_response From 9cb644392b66762caecac6d08a0600680c8b89bb Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 6 Oct 2024 00:19:49 -0700 Subject: [PATCH 32/66] Fix for cli list floating_pane.is_active always being true --- mux/src/tab.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index e839ac511e6..f0964d8abcb 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -913,7 +913,7 @@ impl TabInner { dpi: dims.dpi, }, working_dir: working_dir.map(Into::into), - is_active_pane: true, + is_active_pane: self.float_pane_visible, is_zoomed_pane: false, is_floating_pane: true, workspace: workspace.to_string(), From ab7acd6cbe1b0f34c58a0142f31349572a5a51be Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 6 Oct 2024 00:20:45 -0700 Subject: [PATCH 33/66] Revert "Update so that cli activate-pane will show/hide the floating pane when" This reverts commit 2e83b117ca391491280cb472343cf64aa08851ef. --- mux/src/tab.rs | 7 ------- wezterm-mux-server-impl/src/sessionhandler.rs | 3 +-- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index f0964d8abcb..b981a0cd6be 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1898,10 +1898,6 @@ impl TabInner { } } - if self.float_pane_visible { - self.set_float_pane_visibility(false); - } - if self.zoomed.is_some() { if !configuration().unzoom_on_switch_pane { return; @@ -1929,9 +1925,6 @@ impl TabInner { tab_id: self.id, visible, }); - if let Some(window_id) = mux.window_containing_tab(self.id) { - mux.notify(MuxNotification::WindowInvalidated(window_id)); - } } } diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index b57cb075a38..7336223f789 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -729,7 +729,7 @@ impl SessionHandler { spawn_into_main_thread(async move { schedule_float_pane(float, send_response, client_id); }) - .detach(); + .detach(); } Pdu::FloatPaneVisibilityChanged(FloatPaneVisibilityChanged{ tab_id, visible }) => { @@ -744,7 +744,6 @@ impl SessionHandler { .get_tab(tab_id) .ok_or_else(|| anyhow!("no such tab {}", tab_id))?; tab.set_float_pane_visibility(visible); - mux.notify(mux::MuxNotification::FloatPaneVisibilityChanged{tab_id, visible}); Ok(Pdu::UnitResponse(UnitResponse {})) }, send_response From c534829463ddba3bc5fd4972347e319da674ec5e Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 6 Oct 2024 01:07:43 -0700 Subject: [PATCH 34/66] Fix for activate-pane --pane-id (selecting non floating pane when floating pane is active) --- mux/src/tab.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index b981a0cd6be..ac59d634d4c 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1898,6 +1898,10 @@ impl TabInner { } } + if self.float_pane_visible { + self.set_float_pane_visibility(false); + } + if self.zoomed.is_some() { if !configuration().unzoom_on_switch_pane { return; From 7852213900c211f5f0843d230fe2f2cfbf85939b Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 6 Oct 2024 10:23:16 -0700 Subject: [PATCH 35/66] Update so that cli activate-pane will show/hide the floating pane when the terminal window is not focused --- mux/src/tab.rs | 4 ++++ wezterm-gui/src/termwindow/render/paint.rs | 14 +++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index ac59d634d4c..772a03164c7 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1929,6 +1929,10 @@ impl TabInner { tab_id: self.id, visible, }); + if let Some(window_id) = mux.window_containing_tab(self.id) + { + mux.notify(MuxNotification::WindowInvalidated(window_id)); + } } } diff --git a/wezterm-gui/src/termwindow/render/paint.rs b/wezterm-gui/src/termwindow/render/paint.rs index 442b26dc30b..1e0c7b275ab 100644 --- a/wezterm-gui/src/termwindow/render/paint.rs +++ b/wezterm-gui/src/termwindow/render/paint.rs @@ -253,11 +253,15 @@ impl crate::TermWindow { let mut float_layers = float_layer.quad_allocator(); for pos in panes { - if pos.is_active { - self.update_text_cursor(&pos); - if focused { - pos.pane.advise_focus(); - mux::Mux::get().record_focus_for_current_identity(pos.pane.pane_id()); + //This feels like technical debt, the floating panes should probably update the normal + //panes that they are not active or something similar + if !self.is_float_active() { + if pos.is_active { + self.update_text_cursor(&pos); + if focused { + pos.pane.advise_focus(); + mux::Mux::get().record_focus_for_current_identity(pos.pane.pane_id()); + } } } self.paint_pane(&pos, &mut layers).context("paint_pane")?; From cfe8edf410c44063d645f9bc86a5378bf4619301 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 6 Oct 2024 21:53:13 -0700 Subject: [PATCH 36/66] Kill the floating pane if it is the last remaining pane in the tab (I think this matches Zellij) --- mux/src/tab.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 772a03164c7..83342b201ab 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1685,7 +1685,7 @@ impl TabInner { fn prune_dead_panes(&mut self) -> bool { let mux = Mux::get(); - !self + let result = !self .remove_pane_if( |_, pane| { // If the pane is no longer known to the mux, then its liveness @@ -1704,7 +1704,15 @@ impl TabInner { }, true, ) - .is_empty() + .is_empty(); + + if self.iter_panes().iter().len() == 0 { + if let Some(float_pane) = &self.float_pane { + self.kill_pane(float_pane.pane_id()); + } + } + + result } fn kill_pane(&mut self, pane_id: PaneId) -> bool { @@ -1823,7 +1831,7 @@ impl TabInner { } if let Some(float_pane) = &self.float_pane { - if float_pane.is_dead() { + if float_pane.is_dead() || f(0, &float_pane) { dead_panes.push(Arc::clone(float_pane)); self.float_pane = None; } From bb4b59d578d62cfb0ac6914f18bfe2a9643426cd Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Mon, 7 Oct 2024 18:04:31 -0700 Subject: [PATCH 37/66] Update toggle floating pane to create floating pane if it doesn't exist --- mux/src/tab.rs | 10 ++++++++++ wezterm-gui/src/termwindow/mod.rs | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 83342b201ab..047e60aa1f9 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -775,6 +775,12 @@ impl Tab { .split_and_insert(pane_index, request, pane) } + pub fn has_floating_pane(&self) -> bool { + self.inner + .lock() + .has_floating_pane() + } + pub fn toggle_float(&self) { self.inner .lock() @@ -2165,6 +2171,10 @@ impl TabInner { }) } + fn has_floating_pane(&mut self) -> bool { + self.float_pane.is_some() + } + fn toggle_float_pane(&mut self) { if self.float_pane.is_some() { if self.float_pane_visible { diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 05f7cdfbdb6..bcb3e9324e8 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -2938,7 +2938,16 @@ impl TermWindow { Some(tab) => tab, None => return Ok(PerformAssignmentResult::Handled), }; - tab.toggle_float(); + if !tab.has_floating_pane() { + self.spawn_command( + &SpawnCommand { + domain: config::keyassignment::SpawnTabDomain::CurrentPaneDomain, + ..Default::default() + }, + SpawnWhere::Float); + } else { + tab.toggle_float(); + } } SetPaneZoomState(zoomed) => { let mux = Mux::get(); From 2bf08bbbc538552cc3da4320a1069c75486ba376 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 13 Jul 2024 22:37:05 -0700 Subject: [PATCH 38/66] Attempt at being able to move a float pane to a split pane --- codec/src/lib.rs | 11 +++- config/src/keyassignment.rs | 2 + mux/src/domain.rs | 37 +++++++++++- mux/src/lib.rs | 38 +++++++++++- mux/src/tab.rs | 13 ++++ wezterm-client/src/client.rs | 1 + wezterm-client/src/domain.rs | 34 ++++++++++- wezterm-gui/src/commands.rs | 60 +++++++++++++++++++ wezterm-gui/src/spawn.rs | 1 + wezterm-gui/src/termwindow/mod.rs | 42 ++++++++++++- wezterm-mux-server-impl/src/sessionhandler.rs | 37 +++++++++++- 11 files changed, 266 insertions(+), 10 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 65fb1d73373..2b9b91ae2f7 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -16,7 +16,7 @@ use config::keyassignment::{PaneDirection, ScrollbackEraseMode}; use mux::client::{ClientId, ClientInfo}; use mux::pane::PaneId; use mux::renderable::{RenderableDimensions, StableCursorPosition}; -use mux::tab::{PaneNode, SerdeUrl, SplitRequest, Tab, TabId}; +use mux::tab::{PaneNode, SerdeUrl, SplitDirection, SplitRequest, Tab, TabId}; use mux::window::WindowId; use portable_pty::CommandBuilder; use rangeset::*; @@ -503,7 +503,8 @@ pdu! { GetPaneDirectionResponse: 61, AdjustPaneSize: 62, FloatPane: 63, - FloatPaneVisibilityChanged: 64 + FloatPaneVisibilityChanged: 64, + MoveFloatPaneToSplit: 65, } impl Pdu { @@ -677,6 +678,12 @@ pub struct SplitPane { pub move_pane_id: Option, } +#[derive(Deserialize, Serialize, PartialEq, Debug)] +pub struct MoveFloatPaneToSplit { + pub pane_id: PaneId, + pub split_direction: SplitDirection, +} + #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct MovePaneToNewTab { pub pane_id: PaneId, diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index 91ca4f29092..82f6115556d 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -617,6 +617,8 @@ pub enum KeyAssignment { ActivateWindowRelativeNoWrap(isize), PromptInputLine(PromptInputLine), InputSelector(InputSelector), + MoveFloatToHorizontalSplit(SpawnCommand), + MoveFloatToVerticalSplit(SpawnCommand) } impl_lua_conversion_dynamic!(KeyAssignment); diff --git a/mux/src/domain.rs b/mux/src/domain.rs index 47184c08ec2..1ac83a286ed 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -7,7 +7,7 @@ use crate::localpane::LocalPane; use crate::pane::{alloc_pane_id, Pane, PaneId}; -use crate::tab::{SplitRequest, Tab, TabId}; +use crate::tab::{SplitDirection, SplitRequest, Tab, TabId}; use crate::window::WindowId; use crate::Mux; use anyhow::{bail, Context, Error}; @@ -90,6 +90,41 @@ pub trait Domain: Downcast + Send + Sync { Ok(pane) } + async fn move_floating_pane_to_split( + &self, + tab: TabId, + direction: SplitDirection, + ) -> anyhow::Result> { + let mux = Mux::get(); + let tab = match mux.get_tab(tab) { + Some(t) => t, + None => anyhow::bail!("Invalid tab id {}", tab), + }; + + let pane = tab.get_float_pane().unwrap().pane; + tab.clear_float_pane(); + let pane_id = tab.get_active_pane().unwrap().pane_id(); + + let pane_index = match tab + .iter_panes_ignoring_zoom() + .iter() + .find(|p| p.pane.pane_id() == pane_id) + { + Some(p) => p.index, + None => anyhow::bail!("invalid pane id {}", pane_id), + }; + + let split_request = SplitRequest { + direction, + target_is_second: true, + top_level: false, + size: Default::default(), + }; + + tab.split_and_insert(pane_index, split_request, Arc::clone(&pane))?; + Ok(pane) + } + async fn split_pane( &self, source: SplitSource, diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 9fec7c951f6..8fe972df2e3 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1,7 +1,7 @@ use crate::client::{ClientId, ClientInfo}; use crate::pane::{CachePolicy, Pane, PaneId}; use crate::ssh_agent::AgentProxy; -use crate::tab::{SplitRequest, Tab, TabId}; +use crate::tab::{SplitDirection, SplitRequest, Tab, TabId}; use crate::window::{Window, WindowId}; use anyhow::{anyhow, Context, Error}; use config::keyassignment::SpawnTabDomain; @@ -1246,6 +1246,42 @@ impl Mux { Ok((pane, size)) } + pub async fn move_floating_pane_to_split( + &self, + // TODO: disambiguate with TabId + pane_id: PaneId, + direction: SplitDirection, + ) -> anyhow::Result<(Arc, TerminalSize)> { + let (pane_domain_id, window_id, tab_id) = self + .resolve_pane_id(pane_id) + .ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?; + + let domain = self + .get_domain(pane_domain_id) + //TODO::Update this + .context("resolve_spawn_tab_domain")?; + + if domain.state() == DomainState::Detached { + domain.attach(Some(window_id)).await?; + } + + let pane = domain.move_floating_pane_to_split(tab_id, direction).await?; + + //// FIXME: clipboard + + let dims = pane.get_dimensions(); + + let size = TerminalSize { + cols: dims.cols, + rows: dims.viewport_rows, + pixel_height: 0, // FIXME: split pane pixel dimensions + pixel_width: 0, + dpi: dims.dpi, + }; + + Ok((pane, size)) + } + pub async fn split_pane( &self, // TODO: disambiguate with TabId diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 047e60aa1f9..995a9ba294e 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -760,6 +760,12 @@ impl Tab { self.inner.lock().compute_float_size() } + pub fn clear_float_pane( + & self, + ) { + self.inner.lock().clear_float_pane(); + } + /// Split the pane that has pane_index in the given direction and assign /// the right/bottom pane of the newly created split to the provided Pane /// instance. Returns the resultant index of the newly inserted pane. @@ -862,6 +868,9 @@ impl TabInner { let float_pane = make_pane(entry); self.float_pane.replace(float_pane); self.float_pane_visible = root.1.1; + } else if let PaneNode::Empty = root.1.0 { + self.float_pane = None; + self.float_pane_visible = false; } self.resize(size); @@ -2044,6 +2053,10 @@ impl TabInner { None } + fn clear_float_pane(& mut self) { + self.float_pane = None; + } + fn compute_float_size(&self) -> TerminalSize { let root_size = self.size; diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index 5e96978428d..959370b4463 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -1366,6 +1366,7 @@ impl Client { rpc!(split_pane, SplitPane, SpawnResponse); rpc!(add_float_pane, FloatPane, SpawnResponse); rpc!(set_float_pane_visibility, FloatPaneVisibilityChanged, UnitResponse); + rpc!(move_floating_pane_to_split, MoveFloatPaneToSplit, UnitResponse); rpc!( move_pane_to_new_tab, MovePaneToNewTab, diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 5835956577a..f48926a9fbd 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -8,7 +8,7 @@ use config::{SshDomain, TlsDomainClient, UnixDomain}; use mux::connui::{ConnectionUI, ConnectionUIParams}; use mux::domain::{alloc_domain_id, Domain, DomainId, DomainState, SplitSource}; use mux::pane::{Pane, PaneId}; -use mux::tab::{SplitRequest, Tab, TabId}; +use mux::tab::{SplitDirection, SplitRequest, Tab, TabId}; use mux::window::WindowId; use mux::{Mux, MuxNotification}; use portable_pty::CommandBuilder; @@ -787,6 +787,38 @@ impl Domain for ClientDomain { anyhow::bail!("spawn_pane not implemented for ClientDomain") } + /// Forward the request to the remote; we need to translate the local ids + /// to those that match the remote for the request, resync the changed + /// structure, and then translate the results back to local + async fn move_floating_pane_to_split( + &self, + pane_id: PaneId, + split_direction: SplitDirection, + ) -> anyhow::Result> { + let inner = self + .inner() + .ok_or_else(|| anyhow!("domain is not attached"))?; + + let local_pane = Mux::get() + .get_pane(pane_id) + .ok_or_else(|| anyhow!("pane_id {} is invalid", pane_id))?; + let pane = local_pane + .downcast_ref::() + .ok_or_else(|| anyhow!("pane_id {} is not a ClientPane", pane_id))?; + + let result = inner + .client + .move_floating_pane_to_split(codec::MoveFloatPaneToSplit { + pane_id: pane.remote_pane_id, + split_direction, + }) + .await?; + + self.resync().await?; + + Ok(local_pane) + } + /// Forward the request to the remote; we need to translate the local ids /// to those that match the remote for the request, resync the changed /// structure, and then translate the results back to local diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 46d1675daeb..fdae3efce39 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1462,6 +1462,40 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + brief: label_string(action, "Move float pane to horizontal split".to_string()).into(), + doc: "Move float pane to horizontal split" + .into(), + keys: vec![( + Modifiers::CTRL + .union(Modifiers::ALT) + .union(Modifiers::SHIFT), + "p".into(), + )], + args: &[ArgType::ActivePane], + menubar: &["Shell"], + icon: Some("cod_split_vertical"), + }, + MoveFloatToVerticalSplit(SpawnCommand { + domain: SpawnTabDomain::CurrentPaneDomain, + .. + }) => CommandDef { + brief: label_string(action, "Move float pane to vertical split".to_string()).into(), + doc: "Move float pane to vertical split" + .into(), + keys: vec![( + Modifiers::CTRL + .union(Modifiers::ALT) + .union(Modifiers::SHIFT), + "p".into(), + )], + args: &[ArgType::ActivePane], + menubar: &["Shell"], + icon: Some("cod_split_vertical"), + }, SplitVertical(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, .. @@ -1527,6 +1561,24 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + brief: label_string(action, "Move float to horizontal split (Top/Bottom)".to_string()).into(), + doc: "Move float to horizontal split (Top/Bottom)" + .into(), + keys: vec![], + args: &[ArgType::ActivePane], + menubar: &[], + icon: Some("cod_split_vertical"), + }, + MoveFloatToVerticalSplit(_) => CommandDef { + brief: label_string(action, "Move float to vertical split (Top/Bottom)".to_string()).into(), + doc: "Move float to vertical split (Top/Bottom)" + .into(), + keys: vec![], + args: &[ArgType::ActivePane], + menubar: &[], + icon: Some("cod_split_vertical"), + }, AdjustPaneSize(PaneDirection::Left, amount) => CommandDef { brief: format!("Resize Pane {amount} cell(s) to the Left").into(), doc: "Adjusts the closest split divider to the left".into(), @@ -2063,6 +2115,14 @@ fn compute_default_actions() -> Vec { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() }), + MoveFloatToHorizontalSplit(SpawnCommand { + domain: SpawnTabDomain::CurrentPaneDomain, + ..Default::default() + }), + MoveFloatToVerticalSplit(SpawnCommand { + domain: SpawnTabDomain::CurrentPaneDomain, + ..Default::default() + }), SplitVertical(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() diff --git a/wezterm-gui/src/spawn.rs b/wezterm-gui/src/spawn.rs index eba1c3746cc..f9777f0e28b 100644 --- a/wezterm-gui/src/spawn.rs +++ b/wezterm-gui/src/spawn.rs @@ -110,6 +110,7 @@ pub async fn spawn_command_internal( .await .context("split_pane")?; pane.set_config(term_config); + } else { bail!("there is no active tab while splitting pane!?"); } diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index bcb3e9324e8..127572bd1bc 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -2528,8 +2528,6 @@ impl TermWindow { ActivatePaneByIndex(..) | ActivatePaneDirection(..) | SplitPane(..) | - SplitVertical(..) | - SplitHorizontal(..) | SpawnTab(..) | PaneSelect(..) => { return Ok(PerformAssignmentResult::Handled); @@ -3116,6 +3114,46 @@ impl TermWindow { } PromptInputLine(args) => self.show_prompt_input_line(args), InputSelector(args) => self.show_input_selector(args), + MoveFloatToHorizontalSplit (spawn) => { + if !self.is_float_active() { + return Ok(PerformAssignmentResult::Handled); + } + + let domain = spawn.domain.clone(); + let mux_window_id = self.mux_window_id; + promise::spawn::spawn(async move { + let mux = Mux::get(); + let tab = match mux.get_active_tab_for_window(mux_window_id) { + Some(tab) => tab, + None => anyhow::bail!("no active tab!?"), + }; + let pane = tab + .get_active_pane() + .ok_or_else(|| anyhow!("tab to have a pane"))?; + mux.move_floating_pane_to_split(pane.pane_id(), SplitDirection::Horizontal).await?; + Result::<(), anyhow::Error>::Ok(()) + }).detach(); + } + MoveFloatToVerticalSplit(spawn) => { + if !self.is_float_active() { + return Ok(PerformAssignmentResult::Handled); + } + + let domain = spawn.domain.clone(); + let mux_window_id = self.mux_window_id; + promise::spawn::spawn(async move { + let mux = Mux::get(); + let tab = match mux.get_active_tab_for_window(mux_window_id) { + Some(tab) => tab, + None => anyhow::bail!("no active tab!?"), + }; + let pane = tab + .get_active_pane() + .ok_or_else(|| anyhow!("tab to have a pane"))?; + mux.move_floating_pane_to_split(pane.pane_id(), SplitDirection::Vertical).await?; + Result::<(), anyhow::Error>::Ok(()) + }).detach(); + } }; Ok(PerformAssignmentResult::Handled) } diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index 7336223f789..fd6bbf42128 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -3,10 +3,10 @@ use anyhow::{anyhow, Context}; use codec::*; use config::TermConfig; use mux::client::ClientId; -use mux::domain::SplitSource; +use mux::domain::{DomainId, SplitSource}; use mux::pane::{CachePolicy, Pane, PaneId}; use mux::renderable::{RenderableDimensions, StableCursorPosition}; -use mux::tab::{PaneNode, TabId}; +use mux::tab::{PaneNode, SplitDirection, TabId}; use mux::{Mux, MuxNotification}; use promise::spawn::spawn_into_main_thread; use std::collections::HashMap; @@ -729,7 +729,15 @@ impl SessionHandler { spawn_into_main_thread(async move { schedule_float_pane(float, send_response, client_id); }) - .detach(); + .detach(); + } + + Pdu::MoveFloatPaneToSplit(request) => { + let client_id = self.client_id.clone(); + spawn_into_main_thread(async move { + schedule_move_floating_pane_to_split(request.pane_id, request.split_direction, client_id, send_response); + }) + .detach(); } Pdu::FloatPaneVisibilityChanged(FloatPaneVisibilityChanged{ tab_id, visible }) => { @@ -1123,6 +1131,29 @@ async fn float_pane(float_pane: FloatPane, client_id: Option>) -> })) } +fn schedule_move_floating_pane_to_split( + pane_id: PaneId, + split_direction: SplitDirection, + client_id: Option>, + send_response: SND) +where + SND: Fn(anyhow::Result) + 'static, +{ + promise::spawn::spawn(async move { send_response(move_floating_pane_to_split(pane_id, split_direction, client_id).await) }) + .detach(); +} + +async fn move_floating_pane_to_split(pane_id: PaneId, split_direction: SplitDirection, client_id: Option>) -> anyhow::Result { + let mux = Mux::get(); + let _identity = mux.with_identity(client_id); + + let (pane, size) = mux + .move_floating_pane_to_split(pane_id, split_direction) + .await?; + + Ok::(Pdu::UnitResponse(UnitResponse{})) +} + async fn domain_spawn_v2(spawn: SpawnV2, client_id: Option>) -> anyhow::Result { let mux = Mux::get(); let _identity = mux.with_identity(client_id); From 09d1b6e6bbe1af4b7af024dc44c204162d3d2abc Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 9 Oct 2024 21:26:56 -0700 Subject: [PATCH 39/66] Ability to move existing pane to a floating pane --- codec/src/lib.rs | 6 ++++ config/src/keyassignment.rs | 3 +- mux/src/domain.rs | 7 +++++ mux/src/lib.rs | 28 +++++++++++++++++ mux/src/tab.rs | 9 ++++++ wezterm-client/src/client.rs | 1 + wezterm-client/src/domain.rs | 27 +++++++++++++++++ wezterm-gui/src/commands.rs | 24 +++++++++++++++ wezterm-gui/src/termwindow/mod.rs | 22 ++++++++++++-- wezterm-mux-server-impl/src/sessionhandler.rs | 30 +++++++++++++++++++ 10 files changed, 154 insertions(+), 3 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 2b9b91ae2f7..6e3d3d293de 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -505,6 +505,7 @@ pdu! { FloatPane: 63, FloatPaneVisibilityChanged: 64, MoveFloatPaneToSplit: 65, + MovePaneToFloatingPane: 66, } impl Pdu { @@ -678,6 +679,11 @@ pub struct SplitPane { pub move_pane_id: Option, } +#[derive(Deserialize, Serialize, PartialEq, Debug)] +pub struct MovePaneToFloatingPane { + pub pane_id: PaneId, +} + #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct MoveFloatPaneToSplit { pub pane_id: PaneId, diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index 82f6115556d..ec030cadc62 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -618,7 +618,8 @@ pub enum KeyAssignment { PromptInputLine(PromptInputLine), InputSelector(InputSelector), MoveFloatToHorizontalSplit(SpawnCommand), - MoveFloatToVerticalSplit(SpawnCommand) + MoveFloatToVerticalSplit(SpawnCommand), + MovePaneToFloatingPane } impl_lua_conversion_dynamic!(KeyAssignment); diff --git a/mux/src/domain.rs b/mux/src/domain.rs index 1ac83a286ed..56944365ecb 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -205,6 +205,13 @@ pub trait Domain: Downcast + Send + Sync { Ok(None) } + async fn move_pane_to_floating_pane( + &self, + _pane_id: PaneId, + ) -> anyhow::Result<()> { + Ok(()) + } + /// Returns false if the `spawn` method will never succeed. /// There are some internal placeholder domains that are /// pre-created with local UI that we do not want to allow diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 8fe972df2e3..c2efc983d80 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1343,6 +1343,34 @@ impl Mux { Ok((pane, size)) } + pub async fn move_pane_to_floating_pane( + &self, + pane_id: PaneId, + ) -> anyhow::Result<()> { + let (domain_id, _src_window, src_tab) = self + .resolve_pane_id(pane_id) + .ok_or_else(|| anyhow::anyhow!("pane {} not found", pane_id))?; + + let domain = self + .get_domain(domain_id) + .ok_or_else(|| anyhow::anyhow!("domain {domain_id} of pane {pane_id} not found"))?; + + domain.move_pane_to_floating_pane(pane_id) .await?; + + let tab = match self.get_tab(src_tab) { + Some(t) => t, + None => anyhow::bail!("Invalid tab id {}", src_tab), + }; + + let pane = tab + .remove_pane(pane_id) + .ok_or_else(|| anyhow::anyhow!("pane {} wasn't in its containing tab!?", pane_id))?; + + tab.set_floating_pane(&pane); + + Ok(()) + } + pub async fn move_pane_to_new_tab( &self, pane_id: PaneId, diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 995a9ba294e..d1518ffae16 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -723,6 +723,10 @@ impl Tab { self.inner.lock().set_float_pane_visibility(visible); } + pub fn set_floating_pane(&self, pane: &Arc) { + self.inner.lock().set_floating_pane(pane); + } + pub fn set_active_idx(&self, pane_index: usize) { self.inner.lock().set_active_idx(pane_index) } @@ -1944,6 +1948,11 @@ impl TabInner { } } + fn set_floating_pane(&mut self, pane: &Arc) { + self.float_pane = Some(Arc::clone(&pane)); + self.set_float_pane_visibility(true); + } + fn set_float_pane_visibility(&mut self, visible: bool) { if visible != self.float_pane_visible { self.float_pane_visible = visible; diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index 959370b4463..ee756f2133e 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -1367,6 +1367,7 @@ impl Client { rpc!(add_float_pane, FloatPane, SpawnResponse); rpc!(set_float_pane_visibility, FloatPaneVisibilityChanged, UnitResponse); rpc!(move_floating_pane_to_split, MoveFloatPaneToSplit, UnitResponse); + rpc!(move_pane_to_floating_pane, MovePaneToFloatingPane, UnitResponse); rpc!( move_pane_to_new_tab, MovePaneToNewTab, diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index f48926a9fbd..25d1e519887 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -787,6 +787,33 @@ impl Domain for ClientDomain { anyhow::bail!("spawn_pane not implemented for ClientDomain") } + async fn move_pane_to_floating_pane( + &self, + pane_id: PaneId, + ) -> anyhow::Result<()> { + let inner = self + .inner() + .ok_or_else(|| anyhow!("domain is not attached"))?; + + let local_pane = Mux::get() + .get_pane(pane_id) + .ok_or_else(|| anyhow!("pane_id {} is invalid", pane_id))?; + let pane = local_pane + .downcast_ref::() + .ok_or_else(|| anyhow!("pane_id {} is not a ClientPane", pane_id))?; + + let result = inner + .client + .move_pane_to_floating_pane(codec::MovePaneToFloatingPane { + pane_id: pane.remote_pane_id, + }) + .await?; + + self.resync().await?; + + Ok(()) + } + /// Forward the request to the remote; we need to translate the local ids /// to those that match the remote for the request, resync the changed /// structure, and then translate the results back to local diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index fdae3efce39..41172b5201b 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1462,6 +1462,20 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + brief: label_string(action, "Move pane to floating pane".to_string()).into(), + doc: "Move pane to floating pane" + .into(), + keys: vec![( + Modifiers::CTRL + .union(Modifiers::ALT) + .union(Modifiers::SHIFT), + "u".into(), + )], + args: &[ArgType::ActivePane], + menubar: &["Shell"], + icon: Some("cod_split_vertical"), + }, MoveFloatToHorizontalSplit(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, .. @@ -1561,6 +1575,15 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + brief: label_string(action, "Move pane to floating pane".to_string()).into(), + doc: "Move pane to floating pane" + .into(), + keys: vec![], + args: &[ArgType::ActivePane], + menubar: &[], + icon: Some("cod_split_vertical"), + }, MoveFloatToHorizontalSplit(_) => CommandDef { brief: label_string(action, "Move float to horizontal split (Top/Bottom)".to_string()).into(), doc: "Move float to horizontal split (Top/Bottom)" @@ -2115,6 +2138,7 @@ fn compute_default_actions() -> Vec { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() }), + MovePaneToFloatingPane, MoveFloatToHorizontalSplit(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 127572bd1bc..4a7c2c7abe7 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -3114,12 +3114,31 @@ impl TermWindow { } PromptInputLine(args) => self.show_prompt_input_line(args), InputSelector(args) => self.show_input_selector(args), + MovePaneToFloatingPane => { + //This should also error out if the tab only has 1 pane + if self.is_float_active() { + return Ok(PerformAssignmentResult::Handled); + } + + let mux_window_id = self.mux_window_id; + promise::spawn::spawn(async move { + let mux = Mux::get(); + let tab = match mux.get_active_tab_for_window(mux_window_id) { + Some(tab) => tab, + None => anyhow::bail!("no active tab!?"), + }; + let pane = tab + .get_active_pane() + .ok_or_else(|| anyhow!("tab to have a pane"))?; + mux.move_pane_to_floating_pane(pane.pane_id()).await?; + Result::<(), anyhow::Error>::Ok(()) + }).detach(); + } MoveFloatToHorizontalSplit (spawn) => { if !self.is_float_active() { return Ok(PerformAssignmentResult::Handled); } - let domain = spawn.domain.clone(); let mux_window_id = self.mux_window_id; promise::spawn::spawn(async move { let mux = Mux::get(); @@ -3139,7 +3158,6 @@ impl TermWindow { return Ok(PerformAssignmentResult::Handled); } - let domain = spawn.domain.clone(); let mux_window_id = self.mux_window_id; promise::spawn::spawn(async move { let mux = Mux::get(); diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index fd6bbf42128..404bc5dc8b0 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -732,6 +732,14 @@ impl SessionHandler { .detach(); } + Pdu::MovePaneToFloatingPane(request) => { + let client_id = self.client_id.clone(); + spawn_into_main_thread(async move { + schedule_move_pane_to_floating_pane(request.pane_id, client_id, send_response); + }) + .detach(); + } + Pdu::MoveFloatPaneToSplit(request) => { let client_id = self.client_id.clone(); spawn_into_main_thread(async move { @@ -1131,6 +1139,28 @@ async fn float_pane(float_pane: FloatPane, client_id: Option>) -> })) } +fn schedule_move_pane_to_floating_pane( + pane_id: PaneId, + client_id: Option>, + send_response: SND) +where + SND: Fn(anyhow::Result) + 'static, +{ + promise::spawn::spawn(async move { send_response(crate::sessionhandler::move_pane_to_floating_pane(pane_id, client_id).await) }) + .detach(); +} + +async fn move_pane_to_floating_pane(pane_id: PaneId, client_id: Option>) -> anyhow::Result { + let mux = Mux::get(); + let _identity = mux.with_identity(client_id); + + mux + .move_pane_to_floating_pane(pane_id) + .await?; + + Ok::(Pdu::UnitResponse(UnitResponse{})) +} + fn schedule_move_floating_pane_to_split( pane_id: PaneId, split_direction: SplitDirection, From 1da1d9f37714264eba10852e2b592c812ead45eb Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 9 Oct 2024 21:35:58 -0700 Subject: [PATCH 40/66] Some clean up for move floating pane to split --- codec/src/lib.rs | 4 +- config/src/keyassignment.rs | 5 +- mux/src/domain.rs | 46 ++++++----- mux/src/lib.rs | 19 +---- wezterm-client/src/client.rs | 2 +- wezterm-client/src/domain.rs | 8 +- wezterm-gui/src/commands.rs | 52 ++++-------- wezterm-gui/src/termwindow/mod.rs | 80 ++++++------------- wezterm-mux-server-impl/src/sessionhandler.rs | 4 +- 9 files changed, 76 insertions(+), 144 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 6e3d3d293de..2b4b4983701 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -504,7 +504,7 @@ pdu! { AdjustPaneSize: 62, FloatPane: 63, FloatPaneVisibilityChanged: 64, - MoveFloatPaneToSplit: 65, + MoveFloatingPaneToSplit: 65, MovePaneToFloatingPane: 66, } @@ -685,7 +685,7 @@ pub struct MovePaneToFloatingPane { } #[derive(Deserialize, Serialize, PartialEq, Debug)] -pub struct MoveFloatPaneToSplit { +pub struct MoveFloatingPaneToSplit { pub pane_id: PaneId, pub split_direction: SplitDirection, } diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index ec030cadc62..bff6966201e 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -617,9 +617,8 @@ pub enum KeyAssignment { ActivateWindowRelativeNoWrap(isize), PromptInputLine(PromptInputLine), InputSelector(InputSelector), - MoveFloatToHorizontalSplit(SpawnCommand), - MoveFloatToVerticalSplit(SpawnCommand), - MovePaneToFloatingPane + MoveFloatingPaneToHorizontalSplit(SpawnCommand), + MoveFloatingPantToVerticalSplit(SpawnCommand), } impl_lua_conversion_dynamic!(KeyAssignment); diff --git a/mux/src/domain.rs b/mux/src/domain.rs index 56944365ecb..ba86626dab2 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -94,35 +94,37 @@ pub trait Domain: Downcast + Send + Sync { &self, tab: TabId, direction: SplitDirection, - ) -> anyhow::Result> { + ) -> anyhow::Result<()> { let mux = Mux::get(); let tab = match mux.get_tab(tab) { Some(t) => t, None => anyhow::bail!("Invalid tab id {}", tab), }; - let pane = tab.get_float_pane().unwrap().pane; - tab.clear_float_pane(); - let pane_id = tab.get_active_pane().unwrap().pane_id(); - - let pane_index = match tab - .iter_panes_ignoring_zoom() - .iter() - .find(|p| p.pane.pane_id() == pane_id) - { - Some(p) => p.index, - None => anyhow::bail!("invalid pane id {}", pane_id), - }; - - let split_request = SplitRequest { - direction, - target_is_second: true, - top_level: false, - size: Default::default(), - }; + if let Some(float_pane) = tab.get_float_pane() { + tab.clear_float_pane(); + if let Some(active_non_floating_pane) = tab.get_active_pane() { + let pane_id = active_non_floating_pane.pane_id(); + + let pane_index = match tab + .iter_panes_ignoring_zoom() + .iter() + .find(|p| p.pane.pane_id() == pane_id) + { + Some(p) => p.index, + None => anyhow::bail!("invalid pane id {}", pane_id), + }; - tab.split_and_insert(pane_index, split_request, Arc::clone(&pane))?; - Ok(pane) + let split_request = SplitRequest { + direction, + target_is_second: true, + top_level: false, + size: Default::default(), + }; + tab.split_and_insert(pane_index, split_request, Arc::clone(&float_pane.pane))?; + } + } + Ok(()) } async fn split_pane( diff --git a/mux/src/lib.rs b/mux/src/lib.rs index c2efc983d80..7c7cf7d1f40 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1248,10 +1248,9 @@ impl Mux { pub async fn move_floating_pane_to_split( &self, - // TODO: disambiguate with TabId pane_id: PaneId, direction: SplitDirection, - ) -> anyhow::Result<(Arc, TerminalSize)> { + ) -> anyhow::Result<()> { let (pane_domain_id, window_id, tab_id) = self .resolve_pane_id(pane_id) .ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?; @@ -1265,21 +1264,9 @@ impl Mux { domain.attach(Some(window_id)).await?; } - let pane = domain.move_floating_pane_to_split(tab_id, direction).await?; - - //// FIXME: clipboard - - let dims = pane.get_dimensions(); - - let size = TerminalSize { - cols: dims.cols, - rows: dims.viewport_rows, - pixel_height: 0, // FIXME: split pane pixel dimensions - pixel_width: 0, - dpi: dims.dpi, - }; + domain.move_floating_pane_to_split(tab_id, direction).await?; - Ok((pane, size)) + Ok(()) } pub async fn split_pane( diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index ee756f2133e..cb205ce68de 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -1366,7 +1366,7 @@ impl Client { rpc!(split_pane, SplitPane, SpawnResponse); rpc!(add_float_pane, FloatPane, SpawnResponse); rpc!(set_float_pane_visibility, FloatPaneVisibilityChanged, UnitResponse); - rpc!(move_floating_pane_to_split, MoveFloatPaneToSplit, UnitResponse); + rpc!(move_floating_pane_to_split, MoveFloatingPaneToSplit, UnitResponse); rpc!(move_pane_to_floating_pane, MovePaneToFloatingPane, UnitResponse); rpc!( move_pane_to_new_tab, diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 25d1e519887..0c6175a5a8f 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -821,7 +821,7 @@ impl Domain for ClientDomain { &self, pane_id: PaneId, split_direction: SplitDirection, - ) -> anyhow::Result> { + ) -> anyhow::Result<()> { let inner = self .inner() .ok_or_else(|| anyhow!("domain is not attached"))?; @@ -833,9 +833,9 @@ impl Domain for ClientDomain { .downcast_ref::() .ok_or_else(|| anyhow!("pane_id {} is not a ClientPane", pane_id))?; - let result = inner + inner .client - .move_floating_pane_to_split(codec::MoveFloatPaneToSplit { + .move_floating_pane_to_split(codec::MoveFloatingPaneToSplit { pane_id: pane.remote_pane_id, split_direction, }) @@ -843,7 +843,7 @@ impl Domain for ClientDomain { self.resync().await?; - Ok(local_pane) + Ok(()) } /// Forward the request to the remote; we need to translate the local ids diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 41172b5201b..770c00e6dd9 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1462,26 +1462,12 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { - brief: label_string(action, "Move pane to floating pane".to_string()).into(), - doc: "Move pane to floating pane" - .into(), - keys: vec![( - Modifiers::CTRL - .union(Modifiers::ALT) - .union(Modifiers::SHIFT), - "u".into(), - )], - args: &[ArgType::ActivePane], - menubar: &["Shell"], - icon: Some("cod_split_vertical"), - }, - MoveFloatToHorizontalSplit(SpawnCommand { + MoveFloatingPaneToHorizontalSplit(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, .. }) => CommandDef { - brief: label_string(action, "Move float pane to horizontal split".to_string()).into(), - doc: "Move float pane to horizontal split" + brief: label_string(action, "Move floating pane to horizontal split".to_string()).into(), + doc: "Move floating pane to horizontal split" .into(), keys: vec![( Modifiers::CTRL @@ -1493,12 +1479,12 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { - brief: label_string(action, "Move float pane to vertical split".to_string()).into(), - doc: "Move float pane to vertical split" + brief: label_string(action, "Move floating pane to vertical split".to_string()).into(), + doc: "Move floating pane to vertical split" .into(), keys: vec![( Modifiers::CTRL @@ -1575,27 +1561,18 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { - brief: label_string(action, "Move pane to floating pane".to_string()).into(), - doc: "Move pane to floating pane" - .into(), - keys: vec![], - args: &[ArgType::ActivePane], - menubar: &[], - icon: Some("cod_split_vertical"), - }, - MoveFloatToHorizontalSplit(_) => CommandDef { - brief: label_string(action, "Move float to horizontal split (Top/Bottom)".to_string()).into(), - doc: "Move float to horizontal split (Top/Bottom)" + MoveFloatingPaneToHorizontalSplit(_) => CommandDef { + brief: label_string(action, "Move floating pane to horizontal split (Top/Bottom)".to_string()).into(), + doc: "Move floating pane to horizontal split (Top/Bottom)" .into(), keys: vec![], args: &[ArgType::ActivePane], menubar: &[], icon: Some("cod_split_vertical"), }, - MoveFloatToVerticalSplit(_) => CommandDef { - brief: label_string(action, "Move float to vertical split (Top/Bottom)".to_string()).into(), - doc: "Move float to vertical split (Top/Bottom)" + MoveFloatingPantToVerticalSplit(_) => CommandDef { + brief: label_string(action, "Move floating pane to vertical split (Top/Bottom)".to_string()).into(), + doc: "Move floating pane to vertical split (Top/Bottom)" .into(), keys: vec![], args: &[ArgType::ActivePane], @@ -2138,12 +2115,11 @@ fn compute_default_actions() -> Vec { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() }), - MovePaneToFloatingPane, - MoveFloatToHorizontalSplit(SpawnCommand { + MoveFloatingPaneToHorizontalSplit(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() }), - MoveFloatToVerticalSplit(SpawnCommand { + MoveFloatingPantToVerticalSplit(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() }), diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 4a7c2c7abe7..73a1e9d4ae2 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -3114,68 +3114,36 @@ impl TermWindow { } PromptInputLine(args) => self.show_prompt_input_line(args), InputSelector(args) => self.show_input_selector(args), - MovePaneToFloatingPane => { - //This should also error out if the tab only has 1 pane - if self.is_float_active() { - return Ok(PerformAssignmentResult::Handled); - } - - let mux_window_id = self.mux_window_id; - promise::spawn::spawn(async move { - let mux = Mux::get(); - let tab = match mux.get_active_tab_for_window(mux_window_id) { - Some(tab) => tab, - None => anyhow::bail!("no active tab!?"), - }; - let pane = tab - .get_active_pane() - .ok_or_else(|| anyhow!("tab to have a pane"))?; - mux.move_pane_to_floating_pane(pane.pane_id()).await?; - Result::<(), anyhow::Error>::Ok(()) - }).detach(); - } - MoveFloatToHorizontalSplit (spawn) => { - if !self.is_float_active() { - return Ok(PerformAssignmentResult::Handled); - } - - let mux_window_id = self.mux_window_id; - promise::spawn::spawn(async move { - let mux = Mux::get(); - let tab = match mux.get_active_tab_for_window(mux_window_id) { - Some(tab) => tab, - None => anyhow::bail!("no active tab!?"), - }; - let pane = tab - .get_active_pane() - .ok_or_else(|| anyhow!("tab to have a pane"))?; - mux.move_floating_pane_to_split(pane.pane_id(), SplitDirection::Horizontal).await?; - Result::<(), anyhow::Error>::Ok(()) - }).detach(); + MoveFloatingPaneToHorizontalSplit(spawn) => { + self.move_floating_pane_to_split(SplitDirection::Horizontal); } - MoveFloatToVerticalSplit(spawn) => { - if !self.is_float_active() { - return Ok(PerformAssignmentResult::Handled); - } - - let mux_window_id = self.mux_window_id; - promise::spawn::spawn(async move { - let mux = Mux::get(); - let tab = match mux.get_active_tab_for_window(mux_window_id) { - Some(tab) => tab, - None => anyhow::bail!("no active tab!?"), - }; - let pane = tab - .get_active_pane() - .ok_or_else(|| anyhow!("tab to have a pane"))?; - mux.move_floating_pane_to_split(pane.pane_id(), SplitDirection::Vertical).await?; - Result::<(), anyhow::Error>::Ok(()) - }).detach(); + MoveFloatingPantToVerticalSplit(spawn) => { + self.move_floating_pane_to_split(SplitDirection::Vertical); } }; Ok(PerformAssignmentResult::Handled) } + fn move_floating_pane_to_split(&self, split_direction: SplitDirection) { + if !self.is_float_active() { + return; + } + + let mux_window_id = self.mux_window_id; + promise::spawn::spawn(async move { + let mux = Mux::get(); + let tab = match mux.get_active_tab_for_window(mux_window_id) { + Some(tab) => tab, + None => anyhow::bail!("no active tab!?"), + }; + let pane = tab + .get_active_pane() + .ok_or_else(|| anyhow!("tab to have a pane"))?; + mux.move_floating_pane_to_split(pane.pane_id(), split_direction).await?; + Result::<(), anyhow::Error>::Ok(()) + }).detach(); + } + fn do_open_link_at_mouse_cursor(&self, pane: &Arc) { // They clicked on a link, so let's open it! // We need to ensure that we spawn the `open` call outside of the context diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index 404bc5dc8b0..f934d4c7c2a 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -740,7 +740,7 @@ impl SessionHandler { .detach(); } - Pdu::MoveFloatPaneToSplit(request) => { + Pdu::MoveFloatingPaneToSplit(request) => { let client_id = self.client_id.clone(); spawn_into_main_thread(async move { schedule_move_floating_pane_to_split(request.pane_id, request.split_direction, client_id, send_response); @@ -1177,7 +1177,7 @@ async fn move_floating_pane_to_split(pane_id: PaneId, split_direction: SplitDire let mux = Mux::get(); let _identity = mux.with_identity(client_id); - let (pane, size) = mux + mux .move_floating_pane_to_split(pane_id, split_direction) .await?; From b0c3dd11a62ec3eed68361b170be5fba3f16341d Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 9 Oct 2024 22:41:46 -0700 Subject: [PATCH 41/66] Update move pane to floating pane to use selection UI --- config/src/keyassignment.rs | 1 + wezterm-client/src/domain.rs | 2 +- wezterm-gui/src/commands.rs | 16 ++++++++++++++++ wezterm-gui/src/termwindow/paneselect.rs | 15 +++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index bff6966201e..896cb0cfc38 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -337,6 +337,7 @@ pub enum PaneSelectMode { SwapWithActiveKeepFocus, MoveToNewTab, MoveToNewWindow, + MoveToFloatingPane } impl Default for PaneSelectMode { diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 0c6175a5a8f..409161b887f 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -802,7 +802,7 @@ impl Domain for ClientDomain { .downcast_ref::() .ok_or_else(|| anyhow!("pane_id {} is not a ClientPane", pane_id))?; - let result = inner + inner .client .move_pane_to_floating_pane(codec::MovePaneToFloatingPane { pane_id: pane.remote_pane_id, diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 770c00e6dd9..86ba4550c69 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -898,6 +898,17 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + brief: "Move a pane to floating pane".into(), + doc: "Activates the pane selection UI".into(), + keys: vec![], // FIXME: find a new assignment + args: &[ArgType::ActivePane], + menubar: &["Window"], + icon: Some("cod_multiple_windows"), + }, DecreaseFontSize => CommandDef { brief: "Decrease font size".into(), doc: "Scales the font size smaller by 10%".into(), @@ -2192,6 +2203,11 @@ fn compute_default_actions() -> Vec { mode: PaneSelectMode::MoveToNewWindow, show_pane_ids: false, }), + PaneSelect(PaneSelectArguments { + alphabet: String::new(), + mode: PaneSelectMode::MoveToFloatingPane, + show_pane_ids: false, + }), RotatePanes(RotationDirection::Clockwise), RotatePanes(RotationDirection::CounterClockwise), ActivateTab(0), diff --git a/wezterm-gui/src/termwindow/paneselect.rs b/wezterm-gui/src/termwindow/paneselect.rs index a0ca05c0cc1..11519e1e46c 100644 --- a/wezterm-gui/src/termwindow/paneselect.rs +++ b/wezterm-gui/src/termwindow/paneselect.rs @@ -213,6 +213,21 @@ impl PaneSelector { .detach(); } } + PaneSelectMode::MoveToFloatingPane => { + if let Some(pos) = panes.iter().find(|p| p.index == pane_index) { + let pane_id = pos.pane.pane_id(); + let window_id = term_window.mux_window_id; + promise::spawn::spawn(async move { + if let Err(err) = mux + .move_pane_to_floating_pane(pane_id) + .await + { + log::error!("failed to move_pane_to_floating_pane: {err:#}"); + } + }) + .detach(); + } + } } } From 3a242958e987d3a084444373dcce57297bff77cb Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Thu, 10 Oct 2024 18:01:35 -0700 Subject: [PATCH 42/66] More clean up of moving floating panes to horizontal and vertical splits --- wezterm-gui/src/commands.rs | 40 +++---------------------------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 86ba4550c69..2eb93fb26f3 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1473,40 +1473,6 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { - brief: label_string(action, "Move floating pane to horizontal split".to_string()).into(), - doc: "Move floating pane to horizontal split" - .into(), - keys: vec![( - Modifiers::CTRL - .union(Modifiers::ALT) - .union(Modifiers::SHIFT), - "p".into(), - )], - args: &[ArgType::ActivePane], - menubar: &["Shell"], - icon: Some("cod_split_vertical"), - }, - MoveFloatingPantToVerticalSplit(SpawnCommand { - domain: SpawnTabDomain::CurrentPaneDomain, - .. - }) => CommandDef { - brief: label_string(action, "Move floating pane to vertical split".to_string()).into(), - doc: "Move floating pane to vertical split" - .into(), - keys: vec![( - Modifiers::CTRL - .union(Modifiers::ALT) - .union(Modifiers::SHIFT), - "p".into(), - )], - args: &[ArgType::ActivePane], - menubar: &["Shell"], - icon: Some("cod_split_vertical"), - }, SplitVertical(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, .. @@ -1573,13 +1539,13 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { - brief: label_string(action, "Move floating pane to horizontal split (Top/Bottom)".to_string()).into(), - doc: "Move floating pane to horizontal split (Top/Bottom)" + brief: label_string(action, "Move floating pane to horizontal split (Left/Right)".to_string()).into(), + doc: "Move floating pane to horizontal split (Left/Right)" .into(), keys: vec![], args: &[ArgType::ActivePane], menubar: &[], - icon: Some("cod_split_vertical"), + icon: Some("cod_split_horizontal"), }, MoveFloatingPantToVerticalSplit(_) => CommandDef { brief: label_string(action, "Move floating pane to vertical split (Top/Bottom)".to_string()).into(), From 2c231f5d0fa030000e0ff60cdbaa3ba9419ddcb0 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Thu, 10 Oct 2024 18:41:03 -0700 Subject: [PATCH 43/66] Use floating_pane instead of float_pane --- mux/src/domain.rs | 6 +- mux/src/lib.rs | 2 +- mux/src/tab.rs | 96 +++++++++++++++---------------- wezterm-gui/src/termwindow/mod.rs | 6 +- 4 files changed, 55 insertions(+), 55 deletions(-) diff --git a/mux/src/domain.rs b/mux/src/domain.rs index ba86626dab2..10d9e1c03d1 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -84,7 +84,7 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("Invalid tab id {}", tab), }; - let float_size = tab.compute_float_size(); + let float_size = tab.compute_floating_pane_size(); let pane = self.spawn_pane(float_size, command_builder, command_dir) .await?; tab.insert_float(float_size, Arc::clone(&pane))?; Ok(pane) @@ -101,8 +101,8 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("Invalid tab id {}", tab), }; - if let Some(float_pane) = tab.get_float_pane() { - tab.clear_float_pane(); + if let Some(float_pane) = tab.get_floating_pane() { + tab.clear_floating_pane(); if let Some(active_non_floating_pane) = tab.get_active_pane() { let pane_id = active_non_floating_pane.pane_id(); diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 7c7cf7d1f40..e6a9437675d 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1070,7 +1070,7 @@ impl Mux { pub fn resolve_pane_id(&self, pane_id: PaneId) -> Option<(DomainId, WindowId, TabId)> { let mut ids = None; for tab in self.tabs.read().values() { - if let Some(float_pane) = tab.get_float_pane() { + if let Some(float_pane) = tab.get_floating_pane() { if pane_id == float_pane.pane.pane_id() { ids = Some((tab.tab_id(), float_pane.pane.domain_id())); break; diff --git a/mux/src/tab.rs b/mux/src/tab.rs index d1518ffae16..e4cca2e3118 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -45,8 +45,8 @@ struct TabInner { active: usize, zoomed: Option>, title: String, - float_pane: Option>, - float_pane_visible: bool, + floating_pane: Option>, + floating_pane_visible: bool, recency: Recency, } @@ -582,12 +582,12 @@ impl Tab { self.inner.lock().iter_panes() } - pub fn float_pane_is_visible(&self) -> bool { - self.inner.lock().float_pane_is_visible() + pub fn floating_pane_is_visible(&self) -> bool { + self.inner.lock().floating_pane_is_visible() } - pub fn get_float_pane(&self) -> Option { - self.inner.lock().get_float_pane() + pub fn get_floating_pane(&self) -> Option { + self.inner.lock().get_floating_pane() } pub fn iter_panes_ignoring_zoom(&self) -> Vec { @@ -758,16 +758,16 @@ impl Tab { self.inner.lock().compute_split_size(pane_index, request) } - pub fn compute_float_size( + pub fn compute_floating_pane_size( &self, ) -> TerminalSize { - self.inner.lock().compute_float_size() + self.inner.lock().compute_floating_pane_size() } - pub fn clear_float_pane( + pub fn clear_floating_pane( & self, ) { - self.inner.lock().clear_float_pane(); + self.inner.lock().clear_floating_pane(); } /// Split the pane that has pane_index in the given direction and assign @@ -804,7 +804,7 @@ impl Tab { ) -> anyhow::Result<()> { self.inner .lock() - .add_float_pane(float_size, pane) + .add_floating_pane(float_size, pane) } pub fn get_zoomed_pane(&self) -> Option> { @@ -823,8 +823,8 @@ impl TabInner { zoomed: None, title: String::new(), recency: Recency::default(), - float_pane: None, - float_pane_visible: false + floating_pane: None, + floating_pane_visible: false } } @@ -870,11 +870,11 @@ impl TabInner { if let PaneNode::Leaf(entry) = root.1.0 { let float_pane = make_pane(entry); - self.float_pane.replace(float_pane); - self.float_pane_visible = root.1.1; + self.floating_pane.replace(float_pane); + self.floating_pane_visible = root.1.1; } else if let PaneNode::Empty = root.1.0 { - self.float_pane = None; - self.float_pane_visible = false; + self.floating_pane = None; + self.floating_pane_visible = false; } self.resize(size); @@ -912,7 +912,7 @@ impl TabInner { let active = self.get_active_pane(); let zoomed = self.zoomed.as_ref(); - let float_pane = self.float_pane.as_ref(); + let float_pane = self.floating_pane.as_ref(); let codec_float_pane = if let Some(float_pane) = float_pane { let dims = float_pane.get_dimensions(); @@ -932,7 +932,7 @@ impl TabInner { dpi: dims.dpi, }, working_dir: working_dir.map(Into::into), - is_active_pane: self.float_pane_visible, + is_active_pane: self.floating_pane_visible, is_zoomed_pane: false, is_floating_pane: true, workspace: workspace.to_string(), @@ -941,7 +941,7 @@ impl TabInner { top_row: 0, left_col: 0, tty_name: float_pane.tty_name(), - }), self.float_pane_visible) + }), self.floating_pane_visible) } else { (PaneNode::Empty, false) }; @@ -1035,14 +1035,14 @@ impl TabInner { self.iter_panes_impl(true) } - fn float_pane_is_visible(&self) -> bool { - self.float_pane_visible + fn floating_pane_is_visible(&self) -> bool { + self.floating_pane_visible } - fn get_float_pane(&self) -> Option { - if let Some(float_pane) = self.float_pane.as_ref() { + fn get_floating_pane(&self) -> Option { + if let Some(float_pane) = self.floating_pane.as_ref() { let root_size = self.size; - let size = self.compute_float_size(); + let size = self.compute_floating_pane_size(); let cell_height = root_size.pixel_height / root_size.rows; let cell_width = root_size.pixel_width / root_size.cols; @@ -1281,9 +1281,9 @@ impl TabInner { return; } - if let Some(float_pane) = &self.float_pane { + if let Some(float_pane) = &self.floating_pane { self.size = size; - let float_size = self.compute_float_size(); + let float_size = self.compute_floating_pane_size(); float_pane.resize(float_size).ok(); } @@ -1726,7 +1726,7 @@ impl TabInner { .is_empty(); if self.iter_panes().iter().len() == 0 { - if let Some(float_pane) = &self.float_pane { + if let Some(float_pane) = &self.floating_pane { self.kill_pane(float_pane.pane_id()); } } @@ -1849,10 +1849,10 @@ impl TabInner { self.active = active_idx.saturating_sub(removed_indices.len()); } - if let Some(float_pane) = &self.float_pane { + if let Some(float_pane) = &self.floating_pane { if float_pane.is_dead() || f(0, &float_pane) { dead_panes.push(Arc::clone(float_pane)); - self.float_pane = None; + self.floating_pane = None; } } @@ -1893,7 +1893,7 @@ impl TabInner { } fn is_float_active(&self) -> bool { - self.float_pane.is_some() && self.float_pane_visible + self.floating_pane.is_some() && self.floating_pane_visible } fn get_active_pane(&mut self) -> Option> { @@ -1901,8 +1901,8 @@ impl TabInner { return Some(Arc::clone(zoomed)); } - if self.float_pane_visible { - if let Some(float_pane) = self.float_pane.as_ref() { + if self.floating_pane_visible { + if let Some(float_pane) = self.floating_pane.as_ref() { return Some(Arc::clone(float_pane)); } } @@ -1918,14 +1918,14 @@ impl TabInner { } fn set_active_pane(&mut self, pane: &Arc) { - if let Some(float_pane) = self.float_pane.as_ref() { + if let Some(float_pane) = self.floating_pane.as_ref() { if float_pane.pane_id() == pane.pane_id() { self.set_float_pane_visibility(true); return; } } - if self.float_pane_visible { + if self.floating_pane_visible { self.set_float_pane_visibility(false); } @@ -1949,13 +1949,13 @@ impl TabInner { } fn set_floating_pane(&mut self, pane: &Arc) { - self.float_pane = Some(Arc::clone(&pane)); + self.floating_pane = Some(Arc::clone(&pane)); self.set_float_pane_visibility(true); } fn set_float_pane_visibility(&mut self, visible: bool) { - if visible != self.float_pane_visible { - self.float_pane_visible = visible; + if visible != self.floating_pane_visible { + self.floating_pane_visible = visible; let mux = Mux::get(); mux.notify(MuxNotification::FloatPaneVisibilityChanged{ tab_id: self.id, @@ -2062,11 +2062,11 @@ impl TabInner { None } - fn clear_float_pane(& mut self) { - self.float_pane = None; + fn clear_floating_pane(& mut self) { + self.floating_pane = None; } - fn compute_float_size(&self) -> TerminalSize { + fn compute_floating_pane_size(&self) -> TerminalSize { let root_size = self.size; let cell_width = root_size.pixel_width as f32 / root_size.cols as f32; @@ -2194,12 +2194,12 @@ impl TabInner { } fn has_floating_pane(&mut self) -> bool { - self.float_pane.is_some() + self.floating_pane.is_some() } fn toggle_float_pane(&mut self) { - if self.float_pane.is_some() { - if self.float_pane_visible { + if self.floating_pane.is_some() { + if self.floating_pane_visible { self.set_float_pane_visibility(false); } else { self.set_float_pane_visibility(true); @@ -2207,7 +2207,7 @@ impl TabInner { } } - fn add_float_pane( + fn add_floating_pane( &mut self, float_size: TerminalSize, pane: Arc, @@ -2216,12 +2216,12 @@ impl TabInner { anyhow::bail!("cannot add float while zoomed"); } - if self.float_pane.is_some() { + if self.floating_pane.is_some() { anyhow::bail!("cannot add float while another float is active") } - self.float_pane = Some(Arc::clone(&pane)); - self.float_pane_visible = true; + self.floating_pane = Some(Arc::clone(&pane)); + self.floating_pane_visible = true; pane.resize(float_size)?; Ok(()) diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 73a1e9d4ae2..b6efd4bccfa 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -1417,7 +1417,7 @@ impl TermWindow { return tab_overlay.pane_id() == pane_id; } - if let Some(float_pane) = tab.get_float_pane(){ + if let Some(float_pane) = tab.get_floating_pane(){ if(float_pane.pane.pane_id() == pane_id) { return true; @@ -3528,11 +3528,11 @@ impl TermWindow { None => return None, }; - if !tab.float_pane_is_visible() { + if !tab.floating_pane_is_visible() { return None; } - tab.get_float_pane() + tab.get_floating_pane() } /// if pane_id.is_none(), removes any overlay for the specified tab. From f477cac5bf381e947e4a6b6b7fea85abe70a088e Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 11 Oct 2024 16:09:21 -0700 Subject: [PATCH 44/66] Basic support for multiple floating panes --- codec/src/lib.rs | 10 +- mux/src/lib.rs | 3 + mux/src/tab.rs | 225 +++++++++++------- wezterm-client/src/client.rs | 28 +++ wezterm-client/src/domain.rs | 25 ++ wezterm-gui/src/frontend.rs | 1 + wezterm-gui/src/termwindow/mod.rs | 5 +- wezterm-mux-server-impl/src/dispatch.rs | 10 + wezterm-mux-server-impl/src/sessionhandler.rs | 25 ++ wezterm/src/cli/list.rs | 24 +- 10 files changed, 259 insertions(+), 97 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 2b4b4983701..641e684faf9 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -14,7 +14,7 @@ use anyhow::{bail, Context as _, Error}; use config::keyassignment::{PaneDirection, ScrollbackEraseMode}; use mux::client::{ClientId, ClientInfo}; -use mux::pane::PaneId; +use mux::pane::{Pane, PaneId}; use mux::renderable::{RenderableDimensions, StableCursorPosition}; use mux::tab::{PaneNode, SerdeUrl, SplitDirection, SplitRequest, Tab, TabId}; use mux::window::WindowId; @@ -506,6 +506,7 @@ pdu! { FloatPaneVisibilityChanged: 64, MoveFloatingPaneToSplit: 65, MovePaneToFloatingPane: 66, + ActiveFloatingPaneChanged: 67, } impl Pdu { @@ -648,7 +649,7 @@ pub struct ListPanes {} #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct ListPanesResponse { - pub tabs: Vec<(PaneNode, (PaneNode, bool))>, + pub tabs: Vec<(PaneNode, (Vec, usize, bool))>, pub tab_titles: Vec, pub window_titles: HashMap, } @@ -667,6 +668,11 @@ pub struct FloatPaneVisibilityChanged { pub visible: bool } +#[derive(Deserialize, Serialize, PartialEq, Debug)] +pub struct ActiveFloatingPaneChanged { + pub pane_id: PaneId +} + #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct SplitPane { pub pane_id: PaneId, diff --git a/mux/src/lib.rs b/mux/src/lib.rs index e6a9437675d..4b9088df586 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -96,6 +96,9 @@ pub enum MuxNotification { FloatPaneVisibilityChanged{ tab_id: TabId, visible: bool, + }, + ActiveFloatingPaneChanged{ + pane_id: PaneId, } } diff --git a/mux/src/tab.rs b/mux/src/tab.rs index e4cca2e3118..ebff6d3387f 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -45,7 +45,8 @@ struct TabInner { active: usize, zoomed: Option>, title: String, - floating_pane: Option>, + floating_panes: Vec>, + active_floating_pane: usize, floating_pane_visible: bool, recency: Recency, } @@ -549,14 +550,14 @@ impl Tab { /// PaneEntry, or to create a new Pane from that entry. /// make_pane is expected to add the pane to the mux if it creates /// a new pane, otherwise the pane won't poll/update in the GUI. - pub fn sync_with_pane_tree(&self, size: TerminalSize, root: (PaneNode, (PaneNode, bool)), make_pane: F) + pub fn sync_with_pane_tree(&self, size: TerminalSize, root: (PaneNode, (Vec, usize, bool)), make_pane: F) where F: FnMut(PaneEntry) -> Arc, { self.inner.lock().sync_with_pane_tree(size, root, make_pane) } - pub fn codec_pane_tree(&self) -> (PaneNode, (PaneNode, bool)) { + pub fn codec_pane_tree(&self) -> (PaneNode, (Vec, usize, bool)) { self.inner.lock().codec_pane_tree() } @@ -703,7 +704,7 @@ impl Tab { } pub fn is_float_active(&self) -> bool { - self.inner.lock().is_float_active() + self.inner.lock().is_floating_pane_active() } pub fn get_active_pane(&self) -> Option> { @@ -720,11 +721,16 @@ impl Tab { } pub fn set_float_pane_visibility(&self, visible: bool) { - self.inner.lock().set_float_pane_visibility(visible); + self.inner.lock().set_float_pane_visibility(visible, true); } + pub fn set_active_floating_pane(&self, pane_id: PaneId) { + self.inner.lock().set_active_floating_pane(pane_id, true); + } + + //TODO: This should be something like add_floating_pane now pub fn set_floating_pane(&self, pane: &Arc) { - self.inner.lock().set_floating_pane(pane); + self.inner.lock().append_floating_pane(pane); } pub fn set_active_idx(&self, pane_index: usize) { @@ -764,6 +770,7 @@ impl Tab { self.inner.lock().compute_floating_pane_size() } + //TODO: This should be remove_floating_pane now pub fn clear_floating_pane( & self, ) { @@ -794,7 +801,7 @@ impl Tab { pub fn toggle_float(&self) { self.inner .lock() - .toggle_float_pane(); + .toggle_floating_pane(); } pub fn insert_float( @@ -823,12 +830,13 @@ impl TabInner { zoomed: None, title: String::new(), recency: Recency::default(), - floating_pane: None, + floating_panes: vec!(), + active_floating_pane: 0, floating_pane_visible: false } } - fn sync_with_pane_tree(&mut self, size: TerminalSize, root: (PaneNode, (PaneNode, bool)), mut make_pane: F) + fn sync_with_pane_tree(&mut self, size: TerminalSize, root: (PaneNode, (Vec, usize, bool)), mut make_pane: F) where F: FnMut(PaneEntry) -> Arc, { @@ -868,13 +876,15 @@ impl TabInner { self.zoomed = zoomed; self.size = size; - if let PaneNode::Leaf(entry) = root.1.0 { - let float_pane = make_pane(entry); - self.floating_pane.replace(float_pane); - self.floating_pane_visible = root.1.1; - } else if let PaneNode::Empty = root.1.0 { - self.floating_pane = None; - self.floating_pane_visible = false; + //TODO: we are in a lock this should be safe? is it needed? + self.floating_panes.clear(); + for pane_node in root.1.0 { + if let PaneNode::Leaf(entry) = pane_node { + let floating_pane = make_pane(entry); + self.active_floating_pane = root.1.1; + self.floating_pane_visible = root.1.2; + self.floating_panes.push(floating_pane); + } } self.resize(size); @@ -888,14 +898,14 @@ impl TabInner { assert!(self.pane.is_some()); } - fn codec_pane_tree(&mut self) -> (PaneNode, (PaneNode, bool)) { + fn codec_pane_tree(&mut self) -> (PaneNode, (Vec, usize, bool)) { let mux = Mux::get(); let tab_id = self.id; let window_id = match mux.window_containing_tab(tab_id) { Some(w) => w, None => { log::error!("no window contains tab {}", tab_id); - return (PaneNode::Empty, (PaneNode::Empty, false)); + return (PaneNode::Empty, (vec!(), self.active_floating_pane, false)); } }; @@ -906,24 +916,24 @@ impl TabInner { Some(ws) => ws, None => { log::error!("window id {} doesn't have a window!?", window_id); - return (PaneNode::Empty, (PaneNode::Empty, false)); + return (PaneNode::Empty, (vec!(), 0, false)); } }; let active = self.get_active_pane(); let zoomed = self.zoomed.as_ref(); - let float_pane = self.floating_pane.as_ref(); - let codec_float_pane = if let Some(float_pane) = float_pane { - let dims = float_pane.get_dimensions(); - let working_dir = float_pane.get_current_working_dir(CachePolicy::AllowStale); - let cursor_pos = float_pane.get_cursor_position(); + let mut codec_floating_panes = vec!(); + for floating_pane in &self.floating_panes { + let dims = floating_pane.get_dimensions(); + let working_dir = floating_pane.get_current_working_dir(CachePolicy::AllowStale); + let cursor_pos = floating_pane.get_cursor_position(); - (PaneNode::Leaf(PaneEntry { + let to_add = PaneNode::Leaf(PaneEntry { window_id, tab_id, - pane_id: float_pane.pane_id(), - title: float_pane.get_title(), + pane_id: floating_pane.pane_id(), + title: floating_pane.get_title(), size: TerminalSize { cols: dims.cols, rows: dims.viewport_rows, @@ -940,11 +950,10 @@ impl TabInner { physical_top: 0, top_row: 0, left_col: 0, - tty_name: float_pane.tty_name(), - }), self.floating_pane_visible) - } else { - (PaneNode::Empty, false) - }; + tty_name: floating_pane.tty_name(), + }); + codec_floating_panes.push(to_add); + } if let Some(root) = self.pane.as_ref() { (pane_tree( @@ -956,9 +965,9 @@ impl TabInner { &workspace, 0, 0, - ), codec_float_pane) + ), (codec_floating_panes, self.active_floating_pane, self.floating_pane_visible)) } else { - (PaneNode::Empty, codec_float_pane) + (PaneNode::Empty, (codec_floating_panes, self.active_floating_pane, self.floating_pane_visible)) } } @@ -1040,7 +1049,8 @@ impl TabInner { } fn get_floating_pane(&self) -> Option { - if let Some(float_pane) = self.floating_pane.as_ref() { + if self.floating_pane_visible && self.active_floating_pane < self.floating_panes.len() { + let floating_pane = &self.floating_panes[self.active_floating_pane]; let root_size = self.size; let size = self.compute_floating_pane_size(); @@ -1060,7 +1070,7 @@ impl TabInner { height: size.rows, pixel_width: size.pixel_width, pixel_height: size.pixel_height, - pane: Arc::clone(float_pane) + pane: Arc::clone(floating_pane) }) } else { None @@ -1281,10 +1291,9 @@ impl TabInner { return; } - if let Some(float_pane) = &self.floating_pane { - self.size = size; - let float_size = self.compute_floating_pane_size(); - float_pane.resize(float_size).ok(); + let float_size = self.compute_floating_pane_size(); + for floating_pane in &self.floating_panes { + floating_pane.resize(float_size).ok(); } if let Some(zoomed) = &self.zoomed { @@ -1704,7 +1713,10 @@ impl TabInner { fn prune_dead_panes(&mut self) -> bool { let mux = Mux::get(); - let result = !self + let previous_floating_pane_visibility = self.floating_pane_visible; + let previous_active_floating_pane = self.active_floating_pane; + //TODO: this may be important + let mut result = !self .remove_pane_if( |_, pane| { // If the pane is no longer known to the mux, then its liveness @@ -1725,12 +1737,23 @@ impl TabInner { ) .is_empty(); - if self.iter_panes().iter().len() == 0 { - if let Some(float_pane) = &self.floating_pane { - self.kill_pane(float_pane.pane_id()); + if self.iter_panes().iter().next().is_none() { + let pane_ids: Vec<_> = self + .floating_panes + .iter() + .map(|floating_pane| floating_pane.pane_id()) + .collect(); + + for pane_id in pane_ids { + self.kill_pane(pane_id); } } + if previous_floating_pane_visibility != self.floating_pane_visible || + previous_active_floating_pane != self.active_floating_pane { + result = true; + } + result } @@ -1849,13 +1872,35 @@ impl TabInner { self.active = active_idx.saturating_sub(removed_indices.len()); } - if let Some(float_pane) = &self.floating_pane { - if float_pane.is_dead() || f(0, &float_pane) { - dead_panes.push(Arc::clone(float_pane)); - self.floating_pane = None; + // Step 1: Collect indices of panes to remove and count how many are before the active pane + let mut indices_to_remove = Vec::new(); + let mut count_before_active = 0; + + for (i, floating_pane) in self.floating_panes.iter().enumerate() { + if floating_pane.is_dead() || f(0, floating_pane) { + dead_panes.push(Arc::clone(floating_pane)); + + if i < self.active_floating_pane { + count_before_active += 1; + } + + indices_to_remove.push(i); } } + let active_floating_index = self.active_floating_pane; + indices_to_remove.retain(|&idx| idx <= active_floating_index); + let new_active_floating_pane = active_floating_index.saturating_sub(indices_to_remove.len()); + self.set_active_floating_pane(new_active_floating_pane, false); + + for i in indices_to_remove { + self.floating_panes.remove(i); + } + + if self.floating_panes.is_empty() { + self.set_float_pane_visibility(false, false); + } + if !dead_panes.is_empty() && kill { let to_kill: Vec<_> = dead_panes.iter().map(|p| p.pane_id()).collect(); promise::spawn::spawn_into_main_thread(async move { @@ -1892,8 +1937,8 @@ impl TabInner { dead_count == panes.len() } - fn is_float_active(&self) -> bool { - self.floating_pane.is_some() && self.floating_pane_visible + fn is_floating_pane_active(&self) -> bool { + self.floating_panes.len() > 0 && self.floating_pane_visible } fn get_active_pane(&mut self) -> Option> { @@ -1901,10 +1946,8 @@ impl TabInner { return Some(Arc::clone(zoomed)); } - if self.floating_pane_visible { - if let Some(float_pane) = self.floating_pane.as_ref() { - return Some(Arc::clone(float_pane)); - } + if self.is_floating_pane_active() { + return Some(Arc::clone(&self.floating_panes[self.active_floating_pane])); } self.iter_panes_ignoring_zoom() @@ -1918,15 +1961,16 @@ impl TabInner { } fn set_active_pane(&mut self, pane: &Arc) { - if let Some(float_pane) = self.floating_pane.as_ref() { - if float_pane.pane_id() == pane.pane_id() { - self.set_float_pane_visibility(true); + for (i, floating_pane) in self.floating_panes.iter().enumerate() { + if floating_pane.pane_id() == pane.pane_id() { + self.set_active_floating_pane(i, true); + self.set_float_pane_visibility(true, true); return; } } if self.floating_pane_visible { - self.set_float_pane_visibility(false); + self.set_float_pane_visibility(false, true); } if self.zoomed.is_some() { @@ -1948,12 +1992,13 @@ impl TabInner { } } - fn set_floating_pane(&mut self, pane: &Arc) { - self.floating_pane = Some(Arc::clone(&pane)); - self.set_float_pane_visibility(true); + fn append_floating_pane(&mut self, pane: &Arc) { + self.floating_panes.push(Arc::clone(&pane)); + self.set_active_floating_pane(self.floating_panes.len() - 1, true); + self.set_float_pane_visibility(true, true); } - fn set_float_pane_visibility(&mut self, visible: bool) { + fn set_float_pane_visibility(&mut self, visible: bool, invalidate: bool) { if visible != self.floating_pane_visible { self.floating_pane_visible = visible; let mux = Mux::get(); @@ -1961,9 +2006,26 @@ impl TabInner { tab_id: self.id, visible, }); - if let Some(window_id) = mux.window_containing_tab(self.id) - { - mux.notify(MuxNotification::WindowInvalidated(window_id)); + if invalidate { + if let Some(window_id) = mux.window_containing_tab(self.id) + { + mux.notify(MuxNotification::WindowInvalidated(window_id)); + } + } + } + } + + fn set_active_floating_pane(&mut self, index: usize, invalidate: bool) { + if index != self.active_floating_pane && index < self.floating_panes.len() { + self.active_floating_pane = index; + let pane = &self.floating_panes[self.active_floating_pane]; + let mux = Mux::get(); + mux.notify(MuxNotification::ActiveFloatingPaneChanged {pane_id: pane.pane_id()}); + if invalidate { + if let Some(window_id) = mux.window_containing_tab(self.id) + { + mux.notify(MuxNotification::WindowInvalidated(window_id)); + } } } } @@ -2062,8 +2124,17 @@ impl TabInner { None } + //TODO: this should be updated to accept a pane fn clear_floating_pane(& mut self) { - self.floating_pane = None; + if self.active_floating_pane < self.floating_panes.len() { + self.floating_panes.remove(self.active_floating_pane); + } + if self.floating_panes.len() > 0 { + self.set_active_floating_pane(self.active_floating_pane - 1, true); + } else { + self.set_active_floating_pane(0, true); + self.set_float_pane_visibility(false, true); + } } fn compute_floating_pane_size(&self) -> TerminalSize { @@ -2193,18 +2264,12 @@ impl TabInner { }) } - fn has_floating_pane(&mut self) -> bool { - self.floating_pane.is_some() + fn has_floating_pane(&self) -> bool { + self.floating_panes.len() > 0 } - fn toggle_float_pane(&mut self) { - if self.floating_pane.is_some() { - if self.floating_pane_visible { - self.set_float_pane_visibility(false); - } else { - self.set_float_pane_visibility(true); - } - } + fn toggle_floating_pane(&mut self) { + self.set_float_pane_visibility(!self.is_floating_pane_active(), true); } fn add_floating_pane( @@ -2216,11 +2281,9 @@ impl TabInner { anyhow::bail!("cannot add float while zoomed"); } - if self.floating_pane.is_some() { - anyhow::bail!("cannot add float while another float is active") - } - - self.floating_pane = Some(Arc::clone(&pane)); + //TODO: I don't think I need to use Arc here + self.append_floating_pane(&Arc::clone(&pane)); + //TODO: I don't think this is needed (already set in append_floating_pane) self.floating_pane_visible = true; pane.resize(float_size)?; diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index cb205ce68de..bb4122fdb58 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -34,6 +34,7 @@ use std::thread; use std::time::Duration; use thiserror::Error; use codec::FloatPaneVisibilityChanged; +use codec::ActiveFloatingPaneChanged; use wezterm_uds::UnixStream; #[derive(Error, Debug)] @@ -314,6 +315,32 @@ fn process_unilateral( })?; client_domain.set_float_pane_visibility(tab_id, visible); + client_domain.resync().await; + anyhow::Result::<()>::Ok(()) + }) + .detach(); + return Ok(()); + } + Pdu::ActiveFloatingPaneChanged(ActiveFloatingPaneChanged { pane_id}) => { + let pane_id = *pane_id; + promise::spawn::spawn_into_main_thread(async move { + let mux = Mux::try_get().ok_or_else(|| anyhow!("no more mux"))?; + let client_domain = mux + .get_domain(local_domain_id) + .ok_or_else(|| anyhow!("no such domain {}", local_domain_id))?; + + let (_domain, window_id, tab_id) = mux.resolve_pane_id(pane_id) + .ok_or_else(|| anyhow::anyhow!("can't find {pane_id} in mux"))?; + + let client_domain = + client_domain + .downcast_ref::() + .ok_or_else(|| { + anyhow!("domain {} is not a ClientDomain instance", local_domain_id) + })?; + + client_domain.set_active_floating_pane(pane_id, tab_id); + client_domain.resync().await; anyhow::Result::<()>::Ok(()) }) .detach(); @@ -1366,6 +1393,7 @@ impl Client { rpc!(split_pane, SplitPane, SpawnResponse); rpc!(add_float_pane, FloatPane, SpawnResponse); rpc!(set_float_pane_visibility, FloatPaneVisibilityChanged, UnitResponse); + rpc!(set_active_floating_pane, ActiveFloatingPaneChanged, UnitResponse); rpc!(move_floating_pane_to_split, MoveFloatingPaneToSplit, UnitResponse); rpc!(move_pane_to_floating_pane, MovePaneToFloatingPane, UnitResponse); rpc!( diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 409161b887f..75de47dd9c9 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -27,6 +27,7 @@ pub struct ClientInner { remote_to_local_tab: Mutex>, remote_to_local_pane: Mutex>, pub focused_remote_pane_id: Mutex>, + pub focused_remove_floating_pane_id: Mutex>, } impl ClientInner { @@ -247,6 +248,7 @@ impl ClientInner { remote_to_local_tab: Mutex::new(HashMap::new()), remote_to_local_pane: Mutex::new(HashMap::new()), focused_remote_pane_id: Mutex::new(None), + focused_remove_floating_pane_id: Mutex::new(None) } } } @@ -371,6 +373,19 @@ fn mux_notify_client_domain(local_domain_id: DomainId, notif: MuxNotification) - } } } + MuxNotification::ActiveFloatingPaneChanged {pane_id} => { + if let Some(inner) = client_domain.inner() { + promise::spawn::spawn(async move { + inner + .client + .set_active_floating_pane(codec::ActiveFloatingPaneChanged { + pane_id: pane_id + }) + .await + }) + .detach(); + } + } MuxNotification::WindowTitleChanged { window_id, title: _, @@ -528,6 +543,16 @@ impl ClientDomain { } } + pub fn set_active_floating_pane(&self, pane_id: PaneId, remote_tab_id: TabId) { + if let Some(inner) = self.inner() { + if let Some(local_tab_id) = inner.remote_to_local_tab_id(remote_tab_id) { + if let Some(tab) = Mux::get().get_tab(local_tab_id) { + tab.set_active_floating_pane(pane_id); + } + } + } + } + fn process_pane_list( inner: Arc, panes: ListPanesResponse, diff --git a/wezterm-gui/src/frontend.rs b/wezterm-gui/src/frontend.rs index b9bab5d24e7..4f394383fe9 100644 --- a/wezterm-gui/src/frontend.rs +++ b/wezterm-gui/src/frontend.rs @@ -95,6 +95,7 @@ impl GuiFrontEnd { MuxNotification::PaneOutput(_) => {} MuxNotification::PaneAdded(_) => {} MuxNotification::FloatPaneVisibilityChanged { .. } => { } + MuxNotification::ActiveFloatingPaneChanged { .. } => { } MuxNotification::Alert { pane_id, alert: diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index b6efd4bccfa..ded2095674c 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -1290,6 +1290,7 @@ impl TermWindow { self.update_title_post_status(); } MuxNotification::FloatPaneVisibilityChanged { .. } => { } + MuxNotification::ActiveFloatingPaneChanged { .. } => { } MuxNotification::TabResized(_) => { // Also handled by wezterm-client self.update_title_post_status(); @@ -1503,8 +1504,8 @@ impl TermWindow { return true; } } - MuxNotification::FloatPaneVisibilityChanged { .. } => { - } + MuxNotification::FloatPaneVisibilityChanged { .. } => { } + MuxNotification::ActiveFloatingPaneChanged { .. } => { } MuxNotification::Alert { alert: Alert::ToastNotification { .. }, .. diff --git a/wezterm-mux-server-impl/src/dispatch.rs b/wezterm-mux-server-impl/src/dispatch.rs index 8f981b4605f..b94244e57e1 100644 --- a/wezterm-mux-server-impl/src/dispatch.rs +++ b/wezterm-mux-server-impl/src/dispatch.rs @@ -218,6 +218,16 @@ where .await?; stream.flush().await.context("flushing PDU to client")?; } + Ok(Item::Notif(MuxNotification::ActiveFloatingPaneChanged{ + pane_id + })) => { + Pdu::ActiveFloatingPaneChanged(codec::ActiveFloatingPaneChanged { + pane_id + }) + .encode_async(&mut stream, 0) + .await?; + stream.flush().await.context("flushing PDU to client")?; + } Err(err) => { log::error!("process_async Err {}", err); return Ok(()); diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index f934d4c7c2a..009c16ee967 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -14,6 +14,7 @@ use std::sync::{Arc, Mutex}; use std::time::Instant; use termwiz::surface::SequenceNo; use url::Url; +use mux::MuxNotification::ActiveFloatingPaneChanged; use wezterm_term::terminal::Alert; use wezterm_term::StableRowIndex; @@ -767,6 +768,30 @@ impl SessionHandler { }).detach() } + Pdu::ActiveFloatingPaneChanged(codec::ActiveFloatingPaneChanged{ pane_id}) => { + let client_id = self.client_id.clone(); + spawn_into_main_thread(async move { + catch( + move || { + let mux = Mux::get(); + let _identity = mux.with_identity(client_id); + + let (_domain_id, window_id, tab_id) = mux + .resolve_pane_id(pane_id) + .ok_or_else(|| anyhow::anyhow!("pane {pane_id} not found"))?; + + let tab = mux + .get_tab(tab_id) + .ok_or_else(|| anyhow!("no such tab {}", tab_id))?; + + tab.set_active_floating_pane(pane_id); + Ok(Pdu::UnitResponse(UnitResponse {})) + }, + send_response + ); + }).detach() + } + Pdu::MovePaneToNewTab(request) => { let client_id = self.client_id.clone(); spawn_into_main_thread(async move { diff --git a/wezterm/src/cli/list.rs b/wezterm/src/cli/list.rs index 8d0598ceb7b..a31f4e9003e 100644 --- a/wezterm/src/cli/list.rs +++ b/wezterm/src/cli/list.rs @@ -43,18 +43,18 @@ impl ListCommand { } } - if let PaneNode::Leaf(entry) = &tabroot.1.0 { - let window_title = panes - .window_titles - .get(&entry.window_id) - .map(|s| s.as_str()) - .unwrap_or(""); - output_items.push(CliListResultItem::from( - entry.clone(), - tab_title, - window_title, - )); - } + //if let PaneNode::Leaf(entry) = &tabroot.1.0 { + // let window_title = panes + // .window_titles + // .get(&entry.window_id) + // .map(|s| s.as_str()) + // .unwrap_or(""); + // output_items.push(CliListResultItem::from( + // entry.clone(), + // tab_title, + // window_title, + // )); + //} } match self.format { CliOutputFormatKind::Json => { From 691e8f6c23e91458621a5e96601f6d20eed6661e Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 11 Oct 2024 18:37:34 -0700 Subject: [PATCH 45/66] Support for move pervious, move next in floating panes --- codec/src/lib.rs | 3 +- mux/src/lib.rs | 3 +- mux/src/tab.rs | 21 +++++++++++--- wezterm-client/src/client.rs | 12 ++++---- wezterm-client/src/domain.rs | 29 ++++++++++--------- wezterm-gui/src/termwindow/mod.rs | 1 - wezterm-mux-server-impl/src/dispatch.rs | 6 ++-- wezterm-mux-server-impl/src/sessionhandler.rs | 16 +++++----- 8 files changed, 54 insertions(+), 37 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 641e684faf9..d322c64c8aa 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -670,7 +670,8 @@ pub struct FloatPaneVisibilityChanged { #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct ActiveFloatingPaneChanged { - pub pane_id: PaneId + pub index: usize, + pub tab_id: TabId, } #[derive(Deserialize, Serialize, PartialEq, Debug)] diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 4b9088df586..23c88daa6d5 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -98,7 +98,8 @@ pub enum MuxNotification { visible: bool, }, ActiveFloatingPaneChanged{ - pane_id: PaneId, + tab_id: TabId, + index: usize, } } diff --git a/mux/src/tab.rs b/mux/src/tab.rs index ebff6d3387f..51ff339accd 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -724,8 +724,8 @@ impl Tab { self.inner.lock().set_float_pane_visibility(visible, true); } - pub fn set_active_floating_pane(&self, pane_id: PaneId) { - self.inner.lock().set_active_floating_pane(pane_id, true); + pub fn set_active_floating_pane(&self, index: usize) { + self.inner.lock().set_active_floating_pane(index, true); } //TODO: This should be something like add_floating_pane now @@ -1597,6 +1597,20 @@ impl TabInner { } self.toggle_zoom(); } + + if self.floating_pane_visible && self.floating_panes.len() > 0 { + let len = self.floating_panes.len(); + + let new_index = match direction { + PaneDirection::Right => (self.active_floating_pane + 1) % len, + PaneDirection::Left => (self.active_floating_pane + len - 1) % len, + //Not sure what to do with other directions, leave as is for now + _ => self.active_floating_pane, + }; + + self.set_active_floating_pane(new_index, true); + } + if let Some(panel_idx) = self.get_pane_direction(direction, false) { self.set_active_idx(panel_idx); } @@ -2018,9 +2032,8 @@ impl TabInner { fn set_active_floating_pane(&mut self, index: usize, invalidate: bool) { if index != self.active_floating_pane && index < self.floating_panes.len() { self.active_floating_pane = index; - let pane = &self.floating_panes[self.active_floating_pane]; let mux = Mux::get(); - mux.notify(MuxNotification::ActiveFloatingPaneChanged {pane_id: pane.pane_id()}); + mux.notify(MuxNotification::ActiveFloatingPaneChanged {tab_id: self.id, index: index}); if invalidate { if let Some(window_id) = mux.window_containing_tab(self.id) { diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index bb4122fdb58..b9e97800a41 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -321,17 +321,15 @@ fn process_unilateral( .detach(); return Ok(()); } - Pdu::ActiveFloatingPaneChanged(ActiveFloatingPaneChanged { pane_id}) => { - let pane_id = *pane_id; + Pdu::ActiveFloatingPaneChanged(ActiveFloatingPaneChanged {index, tab_id}) => { + let tab_id = *tab_id; + let index = *index; promise::spawn::spawn_into_main_thread(async move { let mux = Mux::try_get().ok_or_else(|| anyhow!("no more mux"))?; let client_domain = mux .get_domain(local_domain_id) .ok_or_else(|| anyhow!("no such domain {}", local_domain_id))?; - let (_domain, window_id, tab_id) = mux.resolve_pane_id(pane_id) - .ok_or_else(|| anyhow::anyhow!("can't find {pane_id} in mux"))?; - let client_domain = client_domain .downcast_ref::() @@ -339,8 +337,10 @@ fn process_unilateral( anyhow!("domain {} is not a ClientDomain instance", local_domain_id) })?; - client_domain.set_active_floating_pane(pane_id, tab_id); + client_domain.set_active_floating_pane(index, tab_id); + //TODO: is resync the best way to do this? client_domain.resync().await; + anyhow::Result::<()>::Ok(()) }) .detach(); diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 75de47dd9c9..6adaa8184fe 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -373,17 +373,20 @@ fn mux_notify_client_domain(local_domain_id: DomainId, notif: MuxNotification) - } } } - MuxNotification::ActiveFloatingPaneChanged {pane_id} => { - if let Some(inner) = client_domain.inner() { - promise::spawn::spawn(async move { - inner - .client - .set_active_floating_pane(codec::ActiveFloatingPaneChanged { - pane_id: pane_id - }) - .await - }) - .detach(); + MuxNotification::ActiveFloatingPaneChanged {index, tab_id} => { + if let Some(remote_tab_id) = client_domain.local_to_remote_tab_id(tab_id) { + if let Some(inner) = client_domain.inner() { + promise::spawn::spawn(async move { + inner + .client + .set_active_floating_pane(codec::ActiveFloatingPaneChanged { + tab_id: remote_tab_id, + index + }) + .await + }) + .detach(); + } } } MuxNotification::WindowTitleChanged { @@ -543,11 +546,11 @@ impl ClientDomain { } } - pub fn set_active_floating_pane(&self, pane_id: PaneId, remote_tab_id: TabId) { + pub fn set_active_floating_pane(&self, index: usize, remote_tab_id: TabId) { if let Some(inner) = self.inner() { if let Some(local_tab_id) = inner.remote_to_local_tab_id(remote_tab_id) { if let Some(tab) = Mux::get().get_tab(local_tab_id) { - tab.set_active_floating_pane(pane_id); + tab.set_active_floating_pane(index); } } } diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index ded2095674c..d601a63cc0c 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -2527,7 +2527,6 @@ impl TermWindow { if self.is_float_active() { match assignment { ActivatePaneByIndex(..) | - ActivatePaneDirection(..) | SplitPane(..) | SpawnTab(..) | PaneSelect(..) => { diff --git a/wezterm-mux-server-impl/src/dispatch.rs b/wezterm-mux-server-impl/src/dispatch.rs index b94244e57e1..b2237c3033d 100644 --- a/wezterm-mux-server-impl/src/dispatch.rs +++ b/wezterm-mux-server-impl/src/dispatch.rs @@ -219,10 +219,12 @@ where stream.flush().await.context("flushing PDU to client")?; } Ok(Item::Notif(MuxNotification::ActiveFloatingPaneChanged{ - pane_id + index, + tab_id })) => { Pdu::ActiveFloatingPaneChanged(codec::ActiveFloatingPaneChanged { - pane_id + index, + tab_id, }) .encode_async(&mut stream, 0) .await?; diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index 009c16ee967..b2cf0b701ce 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -358,10 +358,12 @@ impl SessionHandler { let tab = mux .get_tab(tab_id) .ok_or_else(|| anyhow::anyhow!("tab {tab_id} not found"))?; - tab.set_active_pane(&pane); + if tab.is_float_active() { + tab.set_active_pane(&pane); - mux.record_focus_for_current_identity(pane_id); - mux.notify(mux::MuxNotification::PaneFocused(pane_id)); + mux.record_focus_for_current_identity(pane_id); + mux.notify(mux::MuxNotification::PaneFocused(pane_id)); + } Ok(Pdu::UnitResponse(UnitResponse {})) }, @@ -768,7 +770,7 @@ impl SessionHandler { }).detach() } - Pdu::ActiveFloatingPaneChanged(codec::ActiveFloatingPaneChanged{ pane_id}) => { + Pdu::ActiveFloatingPaneChanged(codec::ActiveFloatingPaneChanged{index, tab_id}) => { let client_id = self.client_id.clone(); spawn_into_main_thread(async move { catch( @@ -776,15 +778,11 @@ impl SessionHandler { let mux = Mux::get(); let _identity = mux.with_identity(client_id); - let (_domain_id, window_id, tab_id) = mux - .resolve_pane_id(pane_id) - .ok_or_else(|| anyhow::anyhow!("pane {pane_id} not found"))?; - let tab = mux .get_tab(tab_id) .ok_or_else(|| anyhow!("no such tab {}", tab_id))?; - tab.set_active_floating_pane(pane_id); + tab.set_active_floating_pane(index); Ok(Pdu::UnitResponse(UnitResponse {})) }, send_response From a68e7d339253297d29a6025749af7159aafa4f43 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 11 Oct 2024 21:40:43 -0700 Subject: [PATCH 46/66] Fix list and activate floating panes from cli --- mux/src/lib.rs | 9 +++---- mux/src/tab.rs | 14 ++++++++++ wezterm-mux-server-impl/src/sessionhandler.rs | 8 +++--- wezterm/src/cli/list.rs | 26 ++++++++++--------- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 23c88daa6d5..07c249ff7af 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1074,11 +1074,10 @@ impl Mux { pub fn resolve_pane_id(&self, pane_id: PaneId) -> Option<(DomainId, WindowId, TabId)> { let mut ids = None; for tab in self.tabs.read().values() { - if let Some(float_pane) = tab.get_floating_pane() { - if pane_id == float_pane.pane.pane_id() { - ids = Some((tab.tab_id(), float_pane.pane.domain_id())); - break; - } + //TODO: the 2 loops feels weird + if let Some(floating_pane) = tab.get_floating_pane_by_pane_id(pane_id){ + ids = Some((tab.tab_id(), floating_pane.domain_id())); + break; } for p in tab.iter_panes_ignoring_zoom() { if p.pane.pane_id() == pane_id { diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 51ff339accd..3f78dc37e50 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -587,6 +587,10 @@ impl Tab { self.inner.lock().floating_pane_is_visible() } + pub fn get_floating_pane_by_pane_id(&self, pane_id: PaneId) -> Option> { + self.inner.lock().get_floating_pane_by_pane_id(pane_id) + } + pub fn get_floating_pane(&self) -> Option { self.inner.lock().get_floating_pane() } @@ -1044,6 +1048,16 @@ impl TabInner { self.iter_panes_impl(true) } + fn get_floating_pane_by_pane_id(&self, pane_id: PaneId) -> Option> { + for pane in &self.floating_panes { + if pane.pane_id() == pane_id { + + return Some(Arc::clone(&pane)) + } + } + return None + } + fn floating_pane_is_visible(&self) -> bool { self.floating_pane_visible } diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index b2cf0b701ce..0f0766e7e8f 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -358,12 +358,10 @@ impl SessionHandler { let tab = mux .get_tab(tab_id) .ok_or_else(|| anyhow::anyhow!("tab {tab_id} not found"))?; - if tab.is_float_active() { - tab.set_active_pane(&pane); - mux.record_focus_for_current_identity(pane_id); - mux.notify(mux::MuxNotification::PaneFocused(pane_id)); - } + tab.set_active_pane(&pane); + mux.record_focus_for_current_identity(pane_id); + mux.notify(mux::MuxNotification::PaneFocused(pane_id)); Ok(Pdu::UnitResponse(UnitResponse {})) }, diff --git a/wezterm/src/cli/list.rs b/wezterm/src/cli/list.rs index a31f4e9003e..efe0262acb1 100644 --- a/wezterm/src/cli/list.rs +++ b/wezterm/src/cli/list.rs @@ -43,18 +43,20 @@ impl ListCommand { } } - //if let PaneNode::Leaf(entry) = &tabroot.1.0 { - // let window_title = panes - // .window_titles - // .get(&entry.window_id) - // .map(|s| s.as_str()) - // .unwrap_or(""); - // output_items.push(CliListResultItem::from( - // entry.clone(), - // tab_title, - // window_title, - // )); - //} + for floating_pane in &tabroot.1.0 { + if let PaneNode::Leaf(entry) = floating_pane { + let window_title = panes + .window_titles + .get(&entry.window_id) + .map(|s| s.as_str()) + .unwrap_or(""); + output_items.push(CliListResultItem::from( + entry.clone(), + tab_title, + window_title, + )); + } + } } match self.format { CliOutputFormatKind::Json => { From 84b439085ced4dff5c7c7910ea0569d53c4275a7 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 12 Oct 2024 03:05:41 -0700 Subject: [PATCH 47/66] Fixes for move floting pane to split and Move pane to floating pane --- mux/src/domain.rs | 9 +++++---- mux/src/lib.rs | 6 ++++-- mux/src/tab.rs | 26 ++++++++++++++------------ wezterm-client/src/domain.rs | 4 ++-- wezterm-gui/src/termwindow/mod.rs | 4 ++-- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/mux/src/domain.rs b/mux/src/domain.rs index 10d9e1c03d1..e07e81a07ba 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -101,8 +101,9 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("Invalid tab id {}", tab), }; - if let Some(float_pane) = tab.get_floating_pane() { - tab.clear_floating_pane(); + if let Some(float_pane) = tab.get_active_floating_pane() { + tab.remove_floating_pane(tab.get_active_floating_pane_index()); + tab.set_float_pane_visibility(false); if let Some(active_non_floating_pane) = tab.get_active_pane() { let pane_id = active_non_floating_pane.pane_id(); @@ -210,8 +211,8 @@ pub trait Domain: Downcast + Send + Sync { async fn move_pane_to_floating_pane( &self, _pane_id: PaneId, - ) -> anyhow::Result<()> { - Ok(()) + ) -> anyhow::Result { + Ok(false) } /// Returns false if the `spawn` method will never succeed. diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 07c249ff7af..52a596d9e44 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1345,7 +1345,9 @@ impl Mux { .get_domain(domain_id) .ok_or_else(|| anyhow::anyhow!("domain {domain_id} of pane {pane_id} not found"))?; - domain.move_pane_to_floating_pane(pane_id) .await?; + if domain.move_pane_to_floating_pane(pane_id).await?{ + return Ok(()) + } let tab = match self.get_tab(src_tab) { Some(t) => t, @@ -1356,7 +1358,7 @@ impl Mux { .remove_pane(pane_id) .ok_or_else(|| anyhow::anyhow!("pane {} wasn't in its containing tab!?", pane_id))?; - tab.set_floating_pane(&pane); + tab.append_floating_pane(&pane); Ok(()) } diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 3f78dc37e50..237c7410c9f 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -591,8 +591,8 @@ impl Tab { self.inner.lock().get_floating_pane_by_pane_id(pane_id) } - pub fn get_floating_pane(&self) -> Option { - self.inner.lock().get_floating_pane() + pub fn get_active_floating_pane(&self) -> Option { + self.inner.lock().get_active_floating_pane() } pub fn iter_panes_ignoring_zoom(&self) -> Vec { @@ -733,7 +733,7 @@ impl Tab { } //TODO: This should be something like add_floating_pane now - pub fn set_floating_pane(&self, pane: &Arc) { + pub fn append_floating_pane(&self, pane: &Arc) { self.inner.lock().append_floating_pane(pane); } @@ -774,11 +774,12 @@ impl Tab { self.inner.lock().compute_floating_pane_size() } - //TODO: This should be remove_floating_pane now - pub fn clear_floating_pane( - & self, - ) { - self.inner.lock().clear_floating_pane(); + pub fn get_active_floating_pane_index(&self) -> usize { + self.inner.lock().active_floating_pane + } + + pub fn remove_floating_pane(&self, index: usize ) { + self.inner.lock().remove_floating_pane(index); } /// Split the pane that has pane_index in the given direction and assign @@ -1062,7 +1063,7 @@ impl TabInner { self.floating_pane_visible } - fn get_floating_pane(&self) -> Option { + fn get_active_floating_pane(&self) -> Option { if self.floating_pane_visible && self.active_floating_pane < self.floating_panes.len() { let floating_pane = &self.floating_panes[self.active_floating_pane]; let root_size = self.size; @@ -2023,7 +2024,7 @@ impl TabInner { fn append_floating_pane(&mut self, pane: &Arc) { self.floating_panes.push(Arc::clone(&pane)); self.set_active_floating_pane(self.floating_panes.len() - 1, true); - self.set_float_pane_visibility(true, true); + self.set_float_pane_visibility(true, false); } fn set_float_pane_visibility(&mut self, visible: bool, invalidate: bool) { @@ -2152,10 +2153,11 @@ impl TabInner { } //TODO: this should be updated to accept a pane - fn clear_floating_pane(& mut self) { + fn remove_floating_pane(& mut self, index: usize) { if self.active_floating_pane < self.floating_panes.len() { - self.floating_panes.remove(self.active_floating_pane); + self.floating_panes.remove(index); } + //TODO: This seems wrong if self.floating_panes.len() > 0 { self.set_active_floating_pane(self.active_floating_pane - 1, true); } else { diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 6adaa8184fe..5541d70031a 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -818,7 +818,7 @@ impl Domain for ClientDomain { async fn move_pane_to_floating_pane( &self, pane_id: PaneId, - ) -> anyhow::Result<()> { + ) -> anyhow::Result { let inner = self .inner() .ok_or_else(|| anyhow!("domain is not attached"))?; @@ -839,7 +839,7 @@ impl Domain for ClientDomain { self.resync().await?; - Ok(()) + Ok(true) } /// Forward the request to the remote; we need to translate the local ids diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index d601a63cc0c..0d6d2ee6e6c 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -1418,7 +1418,7 @@ impl TermWindow { return tab_overlay.pane_id() == pane_id; } - if let Some(float_pane) = tab.get_floating_pane(){ + if let Some(float_pane) = tab.get_active_floating_pane(){ if(float_pane.pane.pane_id() == pane_id) { return true; @@ -3532,7 +3532,7 @@ impl TermWindow { return None; } - tab.get_floating_pane() + tab.get_active_floating_pane() } /// if pane_id.is_none(), removes any overlay for the specified tab. From 2ff6f967bf6b27c5895ad274b33d9166d266ea13 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 12 Oct 2024 10:57:48 -0700 Subject: [PATCH 48/66] Use floating pane instead float pane --- codec/src/lib.rs | 8 ++++---- config/src/keyassignment.rs | 2 +- mux/src/domain.rs | 8 ++++---- mux/src/lib.rs | 10 +++++----- mux/src/tab.rs | 20 +++++++++---------- wezterm-client/src/client.rs | 10 +++++----- wezterm-client/src/domain.rs | 16 +++++++-------- wezterm-gui/src/commands.rs | 6 +++--- wezterm-gui/src/frontend.rs | 2 +- wezterm-gui/src/spawn.rs | 6 +++--- wezterm-gui/src/termwindow/mod.rs | 14 ++++++------- wezterm-gui/src/termwindow/mouseevent.rs | 16 +++++++-------- wezterm-gui/src/termwindow/render/paint.rs | 6 +++--- wezterm-mux-server-impl/src/dispatch.rs | 4 ++-- wezterm-mux-server-impl/src/sessionhandler.rs | 20 +++++++++---------- wezterm/src/cli/mod.rs | 4 ++-- wezterm/src/cli/split_pane.rs | 6 +++--- 17 files changed, 79 insertions(+), 79 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index d322c64c8aa..90e1a692511 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -502,8 +502,8 @@ pdu! { GetPaneDirection: 60, GetPaneDirectionResponse: 61, AdjustPaneSize: 62, - FloatPane: 63, - FloatPaneVisibilityChanged: 64, + SpawnFloatingPane: 63, + FloatingPaneVisibilityChanged: 64, MoveFloatingPaneToSplit: 65, MovePaneToFloatingPane: 66, ActiveFloatingPaneChanged: 67, @@ -655,7 +655,7 @@ pub struct ListPanesResponse { } #[derive(Deserialize, Serialize, PartialEq, Debug)] -pub struct FloatPane { +pub struct SpawnFloatingPane { pub pane_id: PaneId, pub command: Option, pub command_dir: Option, @@ -663,7 +663,7 @@ pub struct FloatPane { } #[derive(Deserialize, Serialize, PartialEq, Debug)] -pub struct FloatPaneVisibilityChanged { +pub struct FloatingPaneVisibilityChanged { pub tab_id: TabId, pub visible: bool } diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index 896cb0cfc38..7d5a25b1f1a 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -550,7 +550,7 @@ pub enum KeyAssignment { SpawnCommandInNewWindow(SpawnCommand), SplitHorizontal(SpawnCommand), SplitVertical(SpawnCommand), - FloatPane(SpawnCommand), + SpawnFloatingPane(SpawnCommand), ToggleFloatingPane, ShowLauncher, ShowLauncherArgs(LauncherActionArgs), diff --git a/mux/src/domain.rs b/mux/src/domain.rs index e07e81a07ba..5e207db7b67 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -71,7 +71,7 @@ pub trait Domain: Downcast + Send + Sync { Ok(tab) } - async fn add_float_pane( + async fn add_floating_pane( &self, tab: TabId, _pane_id: PaneId, @@ -101,9 +101,9 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("Invalid tab id {}", tab), }; - if let Some(float_pane) = tab.get_active_floating_pane() { + if let Some(floating_pane) = tab.get_active_floating_pane() { tab.remove_floating_pane(tab.get_active_floating_pane_index()); - tab.set_float_pane_visibility(false); + tab.set_floating_pane_visibility(false); if let Some(active_non_floating_pane) = tab.get_active_pane() { let pane_id = active_non_floating_pane.pane_id(); @@ -122,7 +122,7 @@ pub trait Domain: Downcast + Send + Sync { top_level: false, size: Default::default(), }; - tab.split_and_insert(pane_index, split_request, Arc::clone(&float_pane.pane))?; + tab.split_and_insert(pane_index, split_request, Arc::clone(&floating_pane.pane))?; } } Ok(()) diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 52a596d9e44..d1efeb7d39b 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -93,7 +93,7 @@ pub enum MuxNotification { old_workspace: String, new_workspace: String, }, - FloatPaneVisibilityChanged{ + FloatingPaneVisibilityChanged { tab_id: TabId, visible: bool, }, @@ -533,12 +533,12 @@ impl Mux { } } - pub fn set_float_pane_visibility(&self, tab_id: TabId, visible: bool) -> anyhow::Result<()> { + pub fn set_floating_pane_visibility(&self, tab_id: TabId, visible: bool) -> anyhow::Result<()> { let tab = self .get_tab(tab_id) .ok_or_else(|| anyhow::anyhow!("tab {tab_id} not found"))?; - tab.set_float_pane_visibility(visible); + tab.set_floating_pane_visibility(visible); Ok(()) } @@ -1192,7 +1192,7 @@ impl Mux { }) } - pub async fn float_pane( + pub async fn spawn_floating_pane( &self, // TODO: disambiguate with TabId pane_id: PaneId, @@ -1228,7 +1228,7 @@ impl Mux { command_dir }; - let pane = domain.add_float_pane(tab_id, pane_id, command_builder, command_dir).await?; + let pane = domain.add_floating_pane(tab_id, pane_id, command_builder, command_dir).await?; if let Some(config) = term_config { pane.set_config(config); diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 237c7410c9f..ed7ae35b92f 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -724,8 +724,8 @@ impl Tab { self.inner.lock().set_active_pane(pane) } - pub fn set_float_pane_visibility(&self, visible: bool) { - self.inner.lock().set_float_pane_visibility(visible, true); + pub fn set_floating_pane_visibility(&self, visible: bool) { + self.inner.lock().set_floating_pane_visibility(visible, true); } pub fn set_active_floating_pane(&self, index: usize) { @@ -1927,7 +1927,7 @@ impl TabInner { } if self.floating_panes.is_empty() { - self.set_float_pane_visibility(false, false); + self.set_floating_pane_visibility(false, false); } if !dead_panes.is_empty() && kill { @@ -1993,13 +1993,13 @@ impl TabInner { for (i, floating_pane) in self.floating_panes.iter().enumerate() { if floating_pane.pane_id() == pane.pane_id() { self.set_active_floating_pane(i, true); - self.set_float_pane_visibility(true, true); + self.set_floating_pane_visibility(true, true); return; } } if self.floating_pane_visible { - self.set_float_pane_visibility(false, true); + self.set_floating_pane_visibility(false, true); } if self.zoomed.is_some() { @@ -2024,14 +2024,14 @@ impl TabInner { fn append_floating_pane(&mut self, pane: &Arc) { self.floating_panes.push(Arc::clone(&pane)); self.set_active_floating_pane(self.floating_panes.len() - 1, true); - self.set_float_pane_visibility(true, false); + self.set_floating_pane_visibility(true, false); } - fn set_float_pane_visibility(&mut self, visible: bool, invalidate: bool) { + fn set_floating_pane_visibility(&mut self, visible: bool, invalidate: bool) { if visible != self.floating_pane_visible { self.floating_pane_visible = visible; let mux = Mux::get(); - mux.notify(MuxNotification::FloatPaneVisibilityChanged{ + mux.notify(MuxNotification::FloatingPaneVisibilityChanged { tab_id: self.id, visible, }); @@ -2162,7 +2162,7 @@ impl TabInner { self.set_active_floating_pane(self.active_floating_pane - 1, true); } else { self.set_active_floating_pane(0, true); - self.set_float_pane_visibility(false, true); + self.set_floating_pane_visibility(false, true); } } @@ -2298,7 +2298,7 @@ impl TabInner { } fn toggle_floating_pane(&mut self) { - self.set_float_pane_visibility(!self.is_floating_pane_active(), true); + self.set_floating_pane_visibility(!self.is_floating_pane_active(), true); } fn add_floating_pane( diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index b9e97800a41..9bd96aecce4 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -33,7 +33,7 @@ use std::path::{Path, PathBuf}; use std::thread; use std::time::Duration; use thiserror::Error; -use codec::FloatPaneVisibilityChanged; +use codec::FloatingPaneVisibilityChanged; use codec::ActiveFloatingPaneChanged; use wezterm_uds::UnixStream; @@ -299,7 +299,7 @@ fn process_unilateral( .detach(); return Ok(()); } - Pdu::FloatPaneVisibilityChanged(FloatPaneVisibilityChanged { tab_id, visible }) => { + Pdu::FloatingPaneVisibilityChanged(FloatingPaneVisibilityChanged { tab_id, visible }) => { let tab_id = *tab_id; let visible = visible.clone(); promise::spawn::spawn_into_main_thread(async move { @@ -314,7 +314,7 @@ fn process_unilateral( anyhow!("domain {} is not a ClientDomain instance", local_domain_id) })?; - client_domain.set_float_pane_visibility(tab_id, visible); + client_domain.set_floating_pane_visibility(tab_id, visible); client_domain.resync().await; anyhow::Result::<()>::Ok(()) }) @@ -1391,8 +1391,8 @@ impl Client { rpc!(list_panes, ListPanes = (), ListPanesResponse); rpc!(spawn_v2, SpawnV2, SpawnResponse); rpc!(split_pane, SplitPane, SpawnResponse); - rpc!(add_float_pane, FloatPane, SpawnResponse); - rpc!(set_float_pane_visibility, FloatPaneVisibilityChanged, UnitResponse); + rpc!(add_floating_pane, SpawnFloatingPane, SpawnResponse); + rpc!(set_floating_pane_visibility, FloatingPaneVisibilityChanged, UnitResponse); rpc!(set_active_floating_pane, ActiveFloatingPaneChanged, UnitResponse); rpc!(move_floating_pane_to_split, MoveFloatingPaneToSplit, UnitResponse); rpc!(move_pane_to_floating_pane, MovePaneToFloatingPane, UnitResponse); diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 5541d70031a..13505b5eade 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -2,7 +2,7 @@ use crate::client::Client; use crate::pane::ClientPane; use anyhow::{anyhow, bail}; use async_trait::async_trait; -use codec::{FloatPane, ListPanesResponse, SpawnV2, SplitPane}; +use codec::{SpawnFloatingPane, ListPanesResponse, SpawnV2, SplitPane}; use config::keyassignment::SpawnTabDomain; use config::{SshDomain, TlsDomainClient, UnixDomain}; use mux::connui::{ConnectionUI, ConnectionUIParams}; @@ -15,7 +15,7 @@ use portable_pty::CommandBuilder; use promise::spawn::spawn_into_new_thread; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, Mutex}; -use mux::MuxNotification::FloatPaneVisibilityChanged; +use mux::MuxNotification::FloatingPaneVisibilityChanged; use wezterm_term::TerminalSize; pub struct ClientInner { @@ -357,13 +357,13 @@ fn mux_notify_client_domain(local_domain_id: DomainId, notif: MuxNotification) - } } } - MuxNotification::FloatPaneVisibilityChanged { tab_id, visible } => { + MuxNotification::FloatingPaneVisibilityChanged { tab_id, visible } => { if let Some(remote_tab_id) = client_domain.local_to_remote_tab_id(tab_id) { if let Some(inner) = client_domain.inner() { promise::spawn::spawn(async move { inner .client - .set_float_pane_visibility(codec::FloatPaneVisibilityChanged { + .set_floating_pane_visibility(codec::FloatingPaneVisibilityChanged { tab_id: remote_tab_id, visible, }) @@ -536,11 +536,11 @@ impl ClientDomain { } } - pub fn set_float_pane_visibility(&self, remote_tab_id: TabId, visible: bool) { + pub fn set_floating_pane_visibility(&self, remote_tab_id: TabId, visible: bool) { if let Some(inner) = self.inner() { if let Some(local_tab_id) = inner.remote_to_local_tab_id(remote_tab_id) { if let Some(tab) = Mux::get().get_tab(local_tab_id) { - tab.set_float_pane_visibility(visible); + tab.set_floating_pane_visibility(visible); } } } @@ -1042,7 +1042,7 @@ impl Domain for ClientDomain { Ok(pane) } - async fn add_float_pane( + async fn add_floating_pane( &self, _tab_id: TabId, pane_id: PaneId, @@ -1067,7 +1067,7 @@ impl Domain for ClientDomain { let result = inner .client - .add_float_pane(FloatPane{ + .add_floating_pane(SpawnFloatingPane { pane_id: client_pane.remote_pane_id, command, command_dir, diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 2eb93fb26f3..efe63b1f09c 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1456,7 +1456,7 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { @@ -1529,7 +1529,7 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + SpawnFloatingPane(_) => CommandDef { brief: label_string(action, "Create a floating pane".to_string()).into(), doc: "Create a floating pane" .into(), @@ -2088,7 +2088,7 @@ fn compute_default_actions() -> Vec { // ----------------- Shell SpawnTab(SpawnTabDomain::CurrentPaneDomain), SpawnWindow, - FloatPane(SpawnCommand { + SpawnFloatingPane(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() }), diff --git a/wezterm-gui/src/frontend.rs b/wezterm-gui/src/frontend.rs index 4f394383fe9..27f337d5a9e 100644 --- a/wezterm-gui/src/frontend.rs +++ b/wezterm-gui/src/frontend.rs @@ -94,7 +94,7 @@ impl GuiFrontEnd { MuxNotification::WindowInvalidated(_) => {} MuxNotification::PaneOutput(_) => {} MuxNotification::PaneAdded(_) => {} - MuxNotification::FloatPaneVisibilityChanged { .. } => { } + MuxNotification::FloatingPaneVisibilityChanged { .. } => { } MuxNotification::ActiveFloatingPaneChanged { .. } => { } MuxNotification::Alert { pane_id, diff --git a/wezterm-gui/src/spawn.rs b/wezterm-gui/src/spawn.rs index f9777f0e28b..6c64f8bc2f9 100644 --- a/wezterm-gui/src/spawn.rs +++ b/wezterm-gui/src/spawn.rs @@ -125,16 +125,16 @@ pub async fn spawn_command_internal( .get_active_pane() .ok_or_else(|| anyhow!("tab to have a pane"))?; - log::trace!("doing float_pane"); + log::trace!("doing spawn_floating_pane"); let (pane, _size) = mux - .float_pane( + .spawn_floating_pane( pane.pane_id(), cmd_builder, cwd, spawn.domain, ) .await - .context("float_pane")?; + .context("spawn_floating_pane")?; pane.set_config(term_config); } else { bail!("there is no active tab while floating pane!?"); diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 0d6d2ee6e6c..afa6cb9018c 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -1289,7 +1289,7 @@ impl TermWindow { // Also handled by clientpane self.update_title_post_status(); } - MuxNotification::FloatPaneVisibilityChanged { .. } => { } + MuxNotification::FloatingPaneVisibilityChanged { .. } => { } MuxNotification::ActiveFloatingPaneChanged { .. } => { } MuxNotification::TabResized(_) => { // Also handled by wezterm-client @@ -1418,8 +1418,8 @@ impl TermWindow { return tab_overlay.pane_id() == pane_id; } - if let Some(float_pane) = tab.get_active_floating_pane(){ - if(float_pane.pane.pane_id() == pane_id) + if let Some(floating_pane) = tab.get_active_floating_pane(){ + if(floating_pane.pane.pane_id() == pane_id) { return true; } @@ -1504,7 +1504,7 @@ impl TermWindow { return true; } } - MuxNotification::FloatPaneVisibilityChanged { .. } => { } + MuxNotification::FloatingPaneVisibilityChanged { .. } => { } MuxNotification::ActiveFloatingPaneChanged { .. } => { } MuxNotification::Alert { alert: Alert::ToastNotification { .. }, @@ -2611,8 +2611,8 @@ impl TermWindow { }), ); } - FloatPane(spawn) => { - log::trace!("FloatPane {:?}", spawn); + SpawnFloatingPane(spawn) => { + log::trace!("SpawnFloatingPane {:?}", spawn); self.spawn_command( spawn, SpawnWhere::Float); @@ -3521,7 +3521,7 @@ impl TermWindow { self.get_pos_panes_for_tab(&tab) } - fn get_float_pane_to_render(&self) -> Option { + fn get_floating_pane_to_render(&self) -> Option { let mux = Mux::get(); let tab = match mux.get_active_tab_for_window(self.mux_window_id) { Some(tab) => tab, diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index 5fe4a0526bd..b51d90a9343 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -668,19 +668,19 @@ impl super::TermWindow { Some(MouseCapture::TerminalPane(_)) ); - let panes = if let Some(float_pane) = self.get_float_pane_to_render() { - let mouse_in_float = position.column >= float_pane.left && - position.column <= float_pane.left + float_pane.width && - position.row as usize >= float_pane.top && - position.row as usize <= float_pane.top + float_pane.height; + let panes = if let Some(floating_pane) = self.get_floating_pane_to_render() { + let mouse_in_floating_pane = position.column >= floating_pane.left && + position.column <= floating_pane.left + floating_pane.width && + position.row as usize >= floating_pane.top && + position.row as usize <= floating_pane.top + floating_pane.height; - if !mouse_in_float { + if !mouse_in_floating_pane { let mux = Mux::get(); if let Some(tab) = mux.get_active_tab_for_window(self.mux_window_id){ //Hide floating pane if mouse is clicked outside the floating pane match &event.kind { WMEK::Press(_) => { - mux.set_float_pane_visibility(tab.tab_id(), false).ok(); + mux.set_floating_pane_visibility(tab.tab_id(), false).ok(); } _ => {} } @@ -692,7 +692,7 @@ impl super::TermWindow { // closing the last non-floating pane while the floating pane is active. return; } - vec![float_pane] + vec![floating_pane] } else { self.get_panes_to_render() }; diff --git a/wezterm-gui/src/termwindow/render/paint.rs b/wezterm-gui/src/termwindow/render/paint.rs index 1e0c7b275ab..0b15fc21ed6 100644 --- a/wezterm-gui/src/termwindow/render/paint.rs +++ b/wezterm-gui/src/termwindow/render/paint.rs @@ -267,9 +267,9 @@ impl crate::TermWindow { self.paint_pane(&pos, &mut layers).context("paint_pane")?; } - if let Some(float_pane) = self.get_float_pane_to_render(){ - self.paint_pane(&float_pane, &mut float_layers).context("paint_pane")?; - self.paint_float_border(float_pane, &mut float_layers).context("paint_float_border")?; + if let Some(floating_pane) = self.get_floating_pane_to_render(){ + self.paint_pane(&floating_pane, &mut float_layers).context("paint_pane")?; + self.paint_float_border(floating_pane, &mut float_layers).context("paint_float_border")?; } if let Some(pane) = self.get_active_pane_or_overlay() { diff --git a/wezterm-mux-server-impl/src/dispatch.rs b/wezterm-mux-server-impl/src/dispatch.rs index b2237c3033d..471103bce8f 100644 --- a/wezterm-mux-server-impl/src/dispatch.rs +++ b/wezterm-mux-server-impl/src/dispatch.rs @@ -206,11 +206,11 @@ where } Ok(Item::Notif(MuxNotification::ActiveWorkspaceChanged(_))) => {} Ok(Item::Notif(MuxNotification::Empty)) => {} - Ok(Item::Notif(MuxNotification::FloatPaneVisibilityChanged{ + Ok(Item::Notif(MuxNotification::FloatingPaneVisibilityChanged { tab_id, visible })) => { - Pdu::FloatPaneVisibilityChanged(codec::FloatPaneVisibilityChanged { + Pdu::FloatingPaneVisibilityChanged(codec::FloatingPaneVisibilityChanged { tab_id, visible }) diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index 0f0766e7e8f..06d2df70963 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -725,10 +725,10 @@ impl SessionHandler { .detach(); } - Pdu::FloatPane(float) => { + Pdu::SpawnFloatingPane(float) => { let client_id = self.client_id.clone(); spawn_into_main_thread(async move { - schedule_float_pane(float, send_response, client_id); + schedule_spawn_floating_pane(float, send_response, client_id); }) .detach(); } @@ -749,7 +749,7 @@ impl SessionHandler { .detach(); } - Pdu::FloatPaneVisibilityChanged(FloatPaneVisibilityChanged{ tab_id, visible }) => { + Pdu::FloatingPaneVisibilityChanged(FloatingPaneVisibilityChanged { tab_id, visible }) => { let client_id = self.client_id.clone(); spawn_into_main_thread(async move { catch( @@ -760,7 +760,7 @@ impl SessionHandler { let tab = mux .get_tab(tab_id) .ok_or_else(|| anyhow!("no such tab {}", tab_id))?; - tab.set_float_pane_visibility(visible); + tab.set_floating_pane_visibility(visible); Ok(Pdu::UnitResponse(UnitResponse {})) }, send_response @@ -1132,24 +1132,24 @@ async fn split_pane(split: SplitPane, client_id: Option>) -> anyho })) } -fn schedule_float_pane(float: FloatPane, send_response: SND, client_id: Option>) +fn schedule_spawn_floating_pane(float: SpawnFloatingPane, send_response: SND, client_id: Option>) where SND: Fn(anyhow::Result) + 'static, { - promise::spawn::spawn(async move { send_response(float_pane(float, client_id).await) }) + promise::spawn::spawn(async move { send_response(spawn_floating_pane(float, client_id).await) }) .detach(); } -async fn float_pane(float_pane: FloatPane, client_id: Option>) -> anyhow::Result { +async fn spawn_floating_pane(spawn_floating_pane: SpawnFloatingPane, client_id: Option>) -> anyhow::Result { let mux = Mux::get(); let _identity = mux.with_identity(client_id); let (_pane_domain_id, window_id, tab_id) = mux - .resolve_pane_id(float_pane.pane_id) - .ok_or_else(|| anyhow!("pane_id {} invalid", float_pane.pane_id))?; + .resolve_pane_id(spawn_floating_pane.pane_id) + .ok_or_else(|| anyhow!("pane_id {} invalid", spawn_floating_pane.pane_id))?; let (pane, size) = mux - .float_pane(float_pane.pane_id, float_pane.command, float_pane.command_dir, float_pane.domain) + .spawn_floating_pane(spawn_floating_pane.pane_id, spawn_floating_pane.command, spawn_floating_pane.command_dir, spawn_floating_pane.domain) .await?; Ok::(Pdu::SpawnResponse(SpawnResponse { diff --git a/wezterm/src/cli/mod.rs b/wezterm/src/cli/mod.rs index dcaa92cf788..c9495bec778 100644 --- a/wezterm/src/cli/mod.rs +++ b/wezterm/src/cli/mod.rs @@ -109,7 +109,7 @@ Outputs the pane-id for the newly created pane on success" trailing_var_arg = true, about = "spawn a floating pane" )] - FloatPane(split_pane::FloatPane), + SpawnFloatingPane(split_pane::FloatingPane), #[command( name = "spawn", @@ -192,7 +192,7 @@ async fn run_cli_async(opts: &crate::Opt, cli: CliCommand) -> anyhow::Result<()> CliSubCommand::List(cmd) => cmd.run(client).await, CliSubCommand::MovePaneToNewTab(cmd) => cmd.run(client).await, CliSubCommand::SplitPane(cmd) => cmd.run(client).await, - CliSubCommand::FloatPane(cmd) => cmd.run(client).await, + CliSubCommand::SpawnFloatingPane(cmd) => cmd.run(client).await, CliSubCommand::SendText(cmd) => cmd.run(client).await, CliSubCommand::GetText(cmd) => cmd.run(client).await, CliSubCommand::SpawnCommand(cmd) => cmd.run(client, &crate::init_config(opts)?).await, diff --git a/wezterm/src/cli/split_pane.rs b/wezterm/src/cli/split_pane.rs index 0424204c2a8..43d7c87d441 100644 --- a/wezterm/src/cli/split_pane.rs +++ b/wezterm/src/cli/split_pane.rs @@ -115,7 +115,7 @@ impl SplitPane { } #[derive(Debug, Parser, Clone)] -pub struct FloatPane { +pub struct FloatingPane { /// Specify the pane that should be floating. /// The default is to use the current pane based on the /// environment variable WEZTERM_PANE. @@ -134,11 +134,11 @@ pub struct FloatPane { prog: Vec, } -impl FloatPane { +impl FloatingPane { pub async fn run(self, client: Client) -> anyhow::Result<()> { let pane_id = client.resolve_pane_id(self.pane_id).await?; let spawned = client - .add_float_pane(codec::FloatPane { + .add_floating_pane(codec::SpawnFloatingPane { pane_id, domain: config::keyassignment::SpawnTabDomain::CurrentPaneDomain, command: if self.prog.is_empty() { From 2da46fea88148c6a8b4d9383f016826349ac1180 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 12 Oct 2024 13:47:01 -0700 Subject: [PATCH 49/66] Fixes for move floating pane to split where there are multiple floating panes. (Right now if there are multiples it will keep the floating pane visible) --- mux/src/domain.rs | 9 +++++++-- mux/src/tab.rs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mux/src/domain.rs b/mux/src/domain.rs index 5e207db7b67..f979aa71df7 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -103,8 +103,13 @@ pub trait Domain: Downcast + Send + Sync { if let Some(floating_pane) = tab.get_active_floating_pane() { tab.remove_floating_pane(tab.get_active_floating_pane_index()); - tab.set_floating_pane_visibility(false); - if let Some(active_non_floating_pane) = tab.get_active_pane() { + + //TODO: Figure out if all floating pane stuff should be removed from tab.get_active_pane + if let Some(active_non_floating_pane) = tab.iter_panes_ignoring_zoom() + .iter() + .nth(tab.get_active_idx()) + .map(|p| Arc::clone(&p.pane)) { + let pane_id = active_non_floating_pane.pane_id(); let pane_index = match tab diff --git a/mux/src/tab.rs b/mux/src/tab.rs index ed7ae35b92f..dee180f8ba2 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -2158,7 +2158,7 @@ impl TabInner { self.floating_panes.remove(index); } //TODO: This seems wrong - if self.floating_panes.len() > 0 { + if self.floating_panes.len() > 0 && self.active_floating_pane > 0 { self.set_active_floating_pane(self.active_floating_pane - 1, true); } else { self.set_active_floating_pane(0, true); From c116dea5952627263abe3adb0c2039a29026c638 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 12 Oct 2024 17:50:24 -0700 Subject: [PATCH 50/66] Update so zoom works with floating pane --- mux/src/tab.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index dee180f8ba2..868d7f79b8e 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -878,20 +878,24 @@ impl TabInner { } } self.pane.replace(cursor.tree()); - self.zoomed = zoomed; self.size = size; //TODO: we are in a lock this should be safe? is it needed? self.floating_panes.clear(); for pane_node in root.1.0 { if let PaneNode::Leaf(entry) = pane_node { + let is_zoomed = entry.is_zoomed_pane; let floating_pane = make_pane(entry); + if is_zoomed { + zoomed = Some(Arc::clone(&floating_pane)) + } self.active_floating_pane = root.1.1; self.floating_pane_visible = root.1.2; self.floating_panes.push(floating_pane); } } + self.zoomed = zoomed; self.resize(size); log::debug!( @@ -948,7 +952,7 @@ impl TabInner { }, working_dir: working_dir.map(Into::into), is_active_pane: self.floating_pane_visible, - is_zoomed_pane: false, + is_zoomed_pane: is_pane(floating_pane, &zoomed), is_floating_pane: true, workspace: workspace.to_string(), cursor_pos, @@ -1060,10 +1064,14 @@ impl TabInner { } fn floating_pane_is_visible(&self) -> bool { - self.floating_pane_visible + self.floating_pane_visible && !self.zoomed.is_some() } fn get_active_floating_pane(&self) -> Option { + if self.zoomed.is_some() { + return None + } + if self.floating_pane_visible && self.active_floating_pane < self.floating_panes.len() { let floating_pane = &self.floating_panes[self.active_floating_pane]; let root_size = self.size; From b3326266cf571752ef4ba5f9c70c6f16e83fcef3 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 12 Oct 2024 17:55:22 -0700 Subject: [PATCH 51/66] Update so that moving a floating pane to a split hides other floating panes. (This matches zellij) --- mux/src/domain.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/mux/src/domain.rs b/mux/src/domain.rs index f979aa71df7..571b7d06682 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -103,6 +103,7 @@ pub trait Domain: Downcast + Send + Sync { if let Some(floating_pane) = tab.get_active_floating_pane() { tab.remove_floating_pane(tab.get_active_floating_pane_index()); + tab.set_floating_pane_visibility(false); //TODO: Figure out if all floating pane stuff should be removed from tab.get_active_pane if let Some(active_non_floating_pane) = tab.iter_panes_ignoring_zoom() From 50634cf8a67c4b672202acea00a4a07d04fb501a Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 13 Oct 2024 05:48:53 -0700 Subject: [PATCH 52/66] Clean up floating pane code --- codec/src/lib.rs | 4 +- config/src/color.rs | 4 +- config/src/config.rs | 10 +- config/src/keyassignment.rs | 4 +- mux/src/domain.rs | 65 +++++----- mux/src/lib.rs | 8 +- mux/src/tab.rs | 112 +++++++++++------- wezterm-client/src/client.rs | 4 +- wezterm-client/src/domain.rs | 7 +- wezterm-gui/src/commands.rs | 14 +-- wezterm-gui/src/main.rs | 2 +- wezterm-gui/src/spawn.rs | 6 +- wezterm-gui/src/termwindow/mod.rs | 18 +-- wezterm-gui/src/termwindow/mouseevent.rs | 2 +- wezterm-gui/src/termwindow/paneselect.rs | 1 - wezterm-gui/src/termwindow/render/borders.rs | 42 +++---- wezterm-gui/src/termwindow/render/paint.rs | 8 +- wezterm-mux-server-impl/src/sessionhandler.rs | 1 - wezterm/src/cli/activate_tab.rs | 2 +- wezterm/src/cli/list.rs | 4 +- wezterm/src/cli/move_pane_to_new_tab.rs | 2 +- wezterm/src/cli/rename_workspace.rs | 2 +- wezterm/src/cli/set_tab_title.rs | 2 +- wezterm/src/cli/set_window_title.rs | 2 +- wezterm/src/cli/spawn_command.rs | 2 +- wezterm/src/cli/zoom_pane.rs | 2 +- 26 files changed, 172 insertions(+), 158 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 90e1a692511..771eb46332b 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -16,7 +16,7 @@ use config::keyassignment::{PaneDirection, ScrollbackEraseMode}; use mux::client::{ClientId, ClientInfo}; use mux::pane::{Pane, PaneId}; use mux::renderable::{RenderableDimensions, StableCursorPosition}; -use mux::tab::{PaneNode, SerdeUrl, SplitDirection, SplitRequest, Tab, TabId}; +use mux::tab::{PaneNode, SerdeUrl, SplitDirection, SplitRequest, Tab, TabEntry, TabId}; use mux::window::WindowId; use portable_pty::CommandBuilder; use rangeset::*; @@ -649,7 +649,7 @@ pub struct ListPanes {} #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct ListPanesResponse { - pub tabs: Vec<(PaneNode, (Vec, usize, bool))>, + pub tabs: Vec, pub tab_titles: Vec, pub window_titles: HashMap, } diff --git a/config/src/color.rs b/config/src/color.rs index ab993d691a2..d024846ba41 100644 --- a/config/src/color.rs +++ b/config/src/color.rs @@ -599,7 +599,7 @@ pub struct WindowFrameConfig { } #[derive(Debug, Clone, FromDynamic, ToDynamic)] -pub struct FloatBorderConfig { +pub struct FloatingPaneBorderConfig { #[dynamic(try_from = "crate::units::PixelUnit", default = "default_zero_pixel")] pub left_width: Dimension, #[dynamic(try_from = "crate::units::PixelUnit", default = "default_zero_pixel")] @@ -646,7 +646,7 @@ impl Default for WindowFrameConfig { } } -impl Default for FloatBorderConfig { +impl Default for FloatingPaneBorderConfig { fn default() -> Self { Self { left_width: default_zero_pixel(), diff --git a/config/src/config.rs b/config/src/config.rs index 4a8c59af05f..88778e97311 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -25,7 +25,7 @@ use crate::{ default_true, default_win32_acrylic_accent_color, GpuInfo, IntegratedTitleButtonColor, KeyMapPreference, LoadedConfig, MouseEventTriggerMods, RgbaColor, SerialDomain, SystemBackdrop, WebGpuPowerPreference, CONFIG_DIRS, CONFIG_FILE_OVERRIDE, CONFIG_OVERRIDES, CONFIG_SKIP, - HOME_DIR,FloatBorderConfig + HOME_DIR, FloatingPaneBorderConfig }; use anyhow::Context; use luahelper::impl_lua_conversion_dynamic; @@ -143,7 +143,7 @@ pub struct Config { pub window_frame: WindowFrameConfig, #[dynamic(default)] - pub float_pane_border: FloatBorderConfig, + pub floating_pane_border: FloatingPaneBorderConfig, #[dynamic(default = "default_char_select_font_size")] pub char_select_font_size: f64, @@ -518,8 +518,8 @@ pub struct Config { #[dynamic(default)] pub window_padding: WindowPadding, - #[dynamic(default = "default_float_pane_padding")] - pub float_pane_padding: WindowPadding, + #[dynamic(default = "default_floating_pane_padding")] + pub floating_pane_padding: WindowPadding, /// Specifies the path to a background image attachment file. /// The file can be any image format that the rust `image` @@ -1579,7 +1579,7 @@ fn default_integrated_title_buttons() -> Vec { vec![Hide, Maximize, Close] } -fn default_float_pane_padding() -> WindowPadding { +fn default_floating_pane_padding() -> WindowPadding { WindowPadding{ left: Dimension::Percent(0.20), top: Dimension::Percent(0.20), diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index 7d5a25b1f1a..8c7eccda74c 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -618,8 +618,8 @@ pub enum KeyAssignment { ActivateWindowRelativeNoWrap(isize), PromptInputLine(PromptInputLine), InputSelector(InputSelector), - MoveFloatingPaneToHorizontalSplit(SpawnCommand), - MoveFloatingPantToVerticalSplit(SpawnCommand), + MoveFloatingPaneToHorizontalSplit, + MoveFloatingPaneToVerticalSplit, } impl_lua_conversion_dynamic!(KeyAssignment); diff --git a/mux/src/domain.rs b/mux/src/domain.rs index 571b7d06682..fc718408159 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -84,9 +84,9 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("Invalid tab id {}", tab), }; - let float_size = tab.compute_floating_pane_size(); - let pane = self.spawn_pane(float_size, command_builder, command_dir) .await?; - tab.insert_float(float_size, Arc::clone(&pane))?; + let size = tab.compute_floating_pane_size(); + let pane = self.spawn_pane(size, command_builder, command_dir).await?; + tab.add_floating_pane(size, Arc::clone(&pane))?; Ok(pane) } @@ -101,36 +101,37 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("Invalid tab id {}", tab), }; - if let Some(floating_pane) = tab.get_active_floating_pane() { - tab.remove_floating_pane(tab.get_active_floating_pane_index()); - tab.set_floating_pane_visibility(false); - - //TODO: Figure out if all floating pane stuff should be removed from tab.get_active_pane - if let Some(active_non_floating_pane) = tab.iter_panes_ignoring_zoom() - .iter() - .nth(tab.get_active_idx()) - .map(|p| Arc::clone(&p.pane)) { - - let pane_id = active_non_floating_pane.pane_id(); - - let pane_index = match tab - .iter_panes_ignoring_zoom() - .iter() - .find(|p| p.pane.pane_id() == pane_id) - { - Some(p) => p.index, - None => anyhow::bail!("invalid pane id {}", pane_id), - }; + let floating_pane = tab.get_active_floating_pane() + .ok_or_else(|| anyhow::anyhow!("tab does not have active floating pane"))?; - let split_request = SplitRequest { - direction, - target_is_second: true, - top_level: false, - size: Default::default(), - }; - tab.split_and_insert(pane_index, split_request, Arc::clone(&floating_pane.pane))?; - } - } + tab.remove_floating_pane(tab.get_active_floating_pane_index()); + tab.set_floating_pane_visibility(false); + + //TODO: Figure out if all floating pane stuff should be removed from tab.get_active_pane + let active_non_floating_pane = tab.iter_panes_ignoring_zoom() + .iter() + .nth(tab.get_active_idx()) + .map(|p| Arc::clone(&p.pane)) + .ok_or_else(|| anyhow::anyhow!("tab does not have a active non floating pane"))?; + + let pane_id = active_non_floating_pane.pane_id(); + + let pane_index = match tab + .iter_panes_ignoring_zoom() + .iter() + .find(|p| p.pane.pane_id() == pane_id) + { + Some(p) => p.index, + None => anyhow::bail!("invalid pane id {}", pane_id), + }; + + let split_request = SplitRequest { + direction, + target_is_second: true, + top_level: false, + size: Default::default(), + }; + tab.split_and_insert(pane_index, split_request, Arc::clone(&floating_pane.pane))?; Ok(()) } diff --git a/mux/src/lib.rs b/mux/src/lib.rs index d1efeb7d39b..62c6cfadf38 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1074,7 +1074,6 @@ impl Mux { pub fn resolve_pane_id(&self, pane_id: PaneId) -> Option<(DomainId, WindowId, TabId)> { let mut ids = None; for tab in self.tabs.read().values() { - //TODO: the 2 loops feels weird if let Some(floating_pane) = tab.get_floating_pane_by_pane_id(pane_id){ ids = Some((tab.tab_id(), floating_pane.domain_id())); break; @@ -1194,7 +1193,6 @@ impl Mux { pub async fn spawn_floating_pane( &self, - // TODO: disambiguate with TabId pane_id: PaneId, command_builder: Option, command_dir: Option, @@ -1234,14 +1232,12 @@ impl Mux { pane.set_config(config); } - // FIXME: clipboard - let dims = pane.get_dimensions(); let size = TerminalSize { cols: dims.cols, rows: dims.viewport_rows, - pixel_height: 0, // FIXME: split pane pixel dimensions + pixel_height: 0, pixel_width: 0, dpi: dims.dpi, }; @@ -1358,7 +1354,7 @@ impl Mux { .remove_pane(pane_id) .ok_or_else(|| anyhow::anyhow!("pane {} wasn't in its containing tab!?", pane_id))?; - tab.append_floating_pane(&pane); + tab.append_floating_pane(pane); Ok(()) } diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 868d7f79b8e..16530d3f1f5 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -550,14 +550,14 @@ impl Tab { /// PaneEntry, or to create a new Pane from that entry. /// make_pane is expected to add the pane to the mux if it creates /// a new pane, otherwise the pane won't poll/update in the GUI. - pub fn sync_with_pane_tree(&self, size: TerminalSize, root: (PaneNode, (Vec, usize, bool)), make_pane: F) + pub fn sync_with_pane_tree(&self, size: TerminalSize, root: TabEntry, make_pane: F) where F: FnMut(PaneEntry) -> Arc, { self.inner.lock().sync_with_pane_tree(size, root, make_pane) } - pub fn codec_pane_tree(&self) -> (PaneNode, (Vec, usize, bool)) { + pub fn codec_pane_tree(&self) -> TabEntry { self.inner.lock().codec_pane_tree() } @@ -707,7 +707,7 @@ impl Tab { self.inner.lock().is_dead() } - pub fn is_float_active(&self) -> bool { + pub fn is_floating_pane_active(&self) -> bool { self.inner.lock().is_floating_pane_active() } @@ -733,7 +733,7 @@ impl Tab { } //TODO: This should be something like add_floating_pane now - pub fn append_floating_pane(&self, pane: &Arc) { + pub fn append_floating_pane(&self, pane: Arc) { self.inner.lock().append_floating_pane(pane); } @@ -809,14 +809,14 @@ impl Tab { .toggle_floating_pane(); } - pub fn insert_float( + pub fn add_floating_pane( &self, - float_size: TerminalSize, + size: TerminalSize, pane: Arc, ) -> anyhow::Result<()> { self.inner .lock() - .add_floating_pane(float_size, pane) + .add_floating_pane(size, pane) } pub fn get_zoomed_pane(&self) -> Option> { @@ -841,7 +841,7 @@ impl TabInner { } } - fn sync_with_pane_tree(&mut self, size: TerminalSize, root: (PaneNode, (Vec, usize, bool)), mut make_pane: F) + fn sync_with_pane_tree(&mut self, size: TerminalSize, root: TabEntry, mut make_pane: F) where F: FnMut(PaneEntry) -> Arc, { @@ -850,7 +850,7 @@ impl TabInner { log::debug!("sync_with_pane_tree with size {:?}", size); - let t = build_from_pane_tree(root.0.into_tree(), &mut active, &mut zoomed, &mut make_pane); + let t = build_from_pane_tree(root.panes.into_tree(), &mut active, &mut zoomed, &mut make_pane); let mut cursor = t.cursor(); self.active = 0; @@ -882,15 +882,15 @@ impl TabInner { //TODO: we are in a lock this should be safe? is it needed? self.floating_panes.clear(); - for pane_node in root.1.0 { + for pane_node in root.floating_panes { if let PaneNode::Leaf(entry) = pane_node { let is_zoomed = entry.is_zoomed_pane; let floating_pane = make_pane(entry); if is_zoomed { zoomed = Some(Arc::clone(&floating_pane)) } - self.active_floating_pane = root.1.1; - self.floating_pane_visible = root.1.2; + self.active_floating_pane = root.active_floating_pane_index; + self.floating_pane_visible = root.floating_pane_visible; self.floating_panes.push(floating_pane); } } @@ -907,14 +907,19 @@ impl TabInner { assert!(self.pane.is_some()); } - fn codec_pane_tree(&mut self) -> (PaneNode, (Vec, usize, bool)) { + fn codec_pane_tree(&mut self) -> TabEntry { let mux = Mux::get(); let tab_id = self.id; let window_id = match mux.window_containing_tab(tab_id) { Some(w) => w, None => { log::error!("no window contains tab {}", tab_id); - return (PaneNode::Empty, (vec!(), self.active_floating_pane, false)); + return TabEntry{ + panes: PaneNode::Empty, + floating_panes: vec!(), + active_floating_pane_index: self.active_floating_pane, + floating_pane_visible: self.floating_pane_visible + }; } }; @@ -925,7 +930,12 @@ impl TabInner { Some(ws) => ws, None => { log::error!("window id {} doesn't have a window!?", window_id); - return (PaneNode::Empty, (vec!(), 0, false)); + return TabEntry{ + panes: PaneNode::Empty, + floating_panes: vec!(), + active_floating_pane_index: self.active_floating_pane, + floating_pane_visible: self.floating_pane_visible + }; } }; @@ -951,6 +961,7 @@ impl TabInner { dpi: dims.dpi, }, working_dir: working_dir.map(Into::into), + //TODO: this seems wrong, there are other floating panes is_active_pane: self.floating_pane_visible, is_zoomed_pane: is_pane(floating_pane, &zoomed), is_floating_pane: true, @@ -965,18 +976,28 @@ impl TabInner { } if let Some(root) = self.pane.as_ref() { - (pane_tree( - root, - tab_id, - window_id, - active.as_ref(), - zoomed, - &workspace, - 0, - 0, - ), (codec_floating_panes, self.active_floating_pane, self.floating_pane_visible)) + TabEntry{ + panes: pane_tree( + root, + tab_id, + window_id, + active.as_ref(), + zoomed, + &workspace, + 0, + 0, + ), + floating_panes: codec_floating_panes, + active_floating_pane_index: self.active_floating_pane, + floating_pane_visible: self.floating_pane_visible + } } else { - (PaneNode::Empty, (codec_floating_panes, self.active_floating_pane, self.floating_pane_visible)) + TabEntry{ + panes: PaneNode::Empty, + floating_panes: codec_floating_panes, + active_floating_pane_index: self.active_floating_pane, + floating_pane_visible: self.floating_pane_visible + } } } @@ -1056,7 +1077,6 @@ impl TabInner { fn get_floating_pane_by_pane_id(&self, pane_id: PaneId) -> Option> { for pane in &self.floating_panes { if pane.pane_id() == pane_id { - return Some(Arc::clone(&pane)) } } @@ -1068,6 +1088,7 @@ impl TabInner { } fn get_active_floating_pane(&self) -> Option { + //TODO: is this right? A floating pane can be zoomed if self.zoomed.is_some() { return None } @@ -1314,9 +1335,9 @@ impl TabInner { return; } - let float_size = self.compute_floating_pane_size(); + let floating_pane_size = self.compute_floating_pane_size(); for floating_pane in &self.floating_panes { - floating_pane.resize(float_size).ok(); + floating_pane.resize(floating_pane_size).ok(); } if let Some(zoomed) = &self.zoomed { @@ -2029,8 +2050,8 @@ impl TabInner { } } - fn append_floating_pane(&mut self, pane: &Arc) { - self.floating_panes.push(Arc::clone(&pane)); + fn append_floating_pane(&mut self, pane: Arc) { + self.floating_panes.push(pane); self.set_active_floating_pane(self.floating_panes.len() - 1, true); self.set_floating_pane_visibility(true, false); } @@ -2191,11 +2212,11 @@ impl TabInner { pixel_cell: cell_height, }; - let float_padding = configuration().float_pane_padding; - let top_padding_pixels = float_padding.top.evaluate_as_pixels(v_context) as usize; - let bottom_padding_pixels = float_padding.bottom.evaluate_as_pixels(v_context) as usize; - let left_padding_pixels = float_padding.left.evaluate_as_pixels(h_context) as usize; - let right_padding_pixels = float_padding.right.evaluate_as_pixels(h_context) as usize; + let padding = configuration().floating_pane_padding; + let top_padding_pixels = padding.top.evaluate_as_pixels(v_context) as usize; + let bottom_padding_pixels = padding.bottom.evaluate_as_pixels(v_context) as usize; + let left_padding_pixels = padding.left.evaluate_as_pixels(h_context) as usize; + let right_padding_pixels = padding.right.evaluate_as_pixels(h_context) as usize; let pixel_width = root_size.pixel_width - right_padding_pixels - left_padding_pixels; let pixel_height = root_size.pixel_height - bottom_padding_pixels - top_padding_pixels; let cols = (pixel_width as f32 / cell_width) as usize; @@ -2311,19 +2332,16 @@ impl TabInner { fn add_floating_pane( &mut self, - float_size: TerminalSize, + size: TerminalSize, pane: Arc, ) -> anyhow::Result<()> { if self.zoomed.is_some() { - anyhow::bail!("cannot add float while zoomed"); + anyhow::bail!("cannot add floating pane while zoomed"); } - //TODO: I don't think I need to use Arc here - self.append_floating_pane(&Arc::clone(&pane)); - //TODO: I don't think this is needed (already set in append_floating_pane) - self.floating_pane_visible = true; + self.append_floating_pane(Arc::clone(&pane)); - pane.resize(float_size)?; + pane.resize(size)?; Ok(()) } @@ -2534,6 +2552,14 @@ pub struct PaneEntry { pub tty_name: Option, } +#[derive(Deserialize, Serialize, PartialEq, Debug)] +pub struct TabEntry { + pub panes: PaneNode, + pub floating_panes: Vec, + pub active_floating_pane_index: usize, + pub floating_pane_visible: bool, +} + #[derive(Deserialize, Clone, Serialize, PartialEq, Debug)] #[serde(try_from = "String", into = "String")] pub struct SerdeUrl { diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index 9bd96aecce4..8883abba4f0 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -315,7 +315,7 @@ fn process_unilateral( })?; client_domain.set_floating_pane_visibility(tab_id, visible); - client_domain.resync().await; + let _ = client_domain.resync().await; anyhow::Result::<()>::Ok(()) }) .detach(); @@ -339,7 +339,7 @@ fn process_unilateral( client_domain.set_active_floating_pane(index, tab_id); //TODO: is resync the best way to do this? - client_domain.resync().await; + let _ = client_domain.resync().await; anyhow::Result::<()>::Ok(()) }) diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 13505b5eade..bb0a7c4f0da 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -15,7 +15,6 @@ use portable_pty::CommandBuilder; use promise::spawn::spawn_into_new_thread; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, Mutex}; -use mux::MuxNotification::FloatingPaneVisibilityChanged; use wezterm_term::TerminalSize; pub struct ClientInner { @@ -593,12 +592,12 @@ impl ClientDomain { .collect(); for (tabroot, tab_title) in panes.tabs.into_iter().zip(panes.tab_titles.iter()) { - let root_size = match tabroot.0.root_size() { + let root_size = match tabroot.panes.root_size() { Some(size) => size, None => continue, }; - if let Some((remote_window_id, remote_tab_id)) = tabroot.0.window_and_tab_ids() { + if let Some((remote_window_id, remote_tab_id)) = tabroot.panes.window_and_tab_ids() { let tab; remote_windows_to_forget.remove(&remote_window_id); @@ -1082,7 +1081,7 @@ impl Domain for ClientDomain { "wezterm", )); - tab.insert_float(result.size, Arc::clone(&pane)).ok(); + tab.add_floating_pane(result.size, Arc::clone(&pane)).ok(); mux.add_pane(&pane)?; diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index efe63b1f09c..545db6d43c7 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1538,7 +1538,7 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + MoveFloatingPaneToHorizontalSplit => CommandDef { brief: label_string(action, "Move floating pane to horizontal split (Left/Right)".to_string()).into(), doc: "Move floating pane to horizontal split (Left/Right)" .into(), @@ -1547,7 +1547,7 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + MoveFloatingPaneToVerticalSplit => CommandDef { brief: label_string(action, "Move floating pane to vertical split (Top/Bottom)".to_string()).into(), doc: "Move floating pane to vertical split (Top/Bottom)" .into(), @@ -2092,14 +2092,8 @@ fn compute_default_actions() -> Vec { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() }), - MoveFloatingPaneToHorizontalSplit(SpawnCommand { - domain: SpawnTabDomain::CurrentPaneDomain, - ..Default::default() - }), - MoveFloatingPantToVerticalSplit(SpawnCommand { - domain: SpawnTabDomain::CurrentPaneDomain, - ..Default::default() - }), + MoveFloatingPaneToHorizontalSplit, + MoveFloatingPaneToVerticalSplit, SplitVertical(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() diff --git a/wezterm-gui/src/main.rs b/wezterm-gui/src/main.rs index 13f79915d85..8aaef974236 100644 --- a/wezterm-gui/src/main.rs +++ b/wezterm-gui/src/main.rs @@ -576,7 +576,7 @@ impl Publish { let mut window_id = None; 'outer: for tabroot in panes.tabs { - let mut cursor = tabroot.0.into_tree().cursor(); + let mut cursor = tabroot.panes.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm-gui/src/spawn.rs b/wezterm-gui/src/spawn.rs index 6c64f8bc2f9..25394e47c18 100644 --- a/wezterm-gui/src/spawn.rs +++ b/wezterm-gui/src/spawn.rs @@ -15,7 +15,7 @@ pub enum SpawnWhere { NewWindow, NewTab, SplitPane(SplitRequest), - Float, + FloatingPane, } pub fn spawn_command_impl( @@ -115,10 +115,10 @@ pub async fn spawn_command_internal( bail!("there is no active tab while splitting pane!?"); } } - SpawnWhere::Float => { + SpawnWhere::FloatingPane => { let src_window_id = match src_window_id { Some(id) => id, - None => anyhow::bail!("no src window when float a pane?"), + None => anyhow::bail!("no src window when spawning floating pane?"), }; if let Some(tab) = mux.get_active_tab_for_window(src_window_id) { let pane = tab diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index afa6cb9018c..2b3494b1cd0 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -1419,7 +1419,7 @@ impl TermWindow { } if let Some(floating_pane) = tab.get_active_floating_pane(){ - if(floating_pane.pane.pane_id() == pane_id) + if floating_pane.pane.pane_id() == pane_id { return true; } @@ -2524,7 +2524,7 @@ impl TermWindow { result => return Ok(result), } - if self.is_float_active() { + if self.is_floating_pane_active() { match assignment { ActivatePaneByIndex(..) | SplitPane(..) | @@ -2615,7 +2615,7 @@ impl TermWindow { log::trace!("SpawnFloatingPane {:?}", spawn); self.spawn_command( spawn, - SpawnWhere::Float); + SpawnWhere::FloatingPane); } ToggleFullScreen => { self.window.as_ref().unwrap().toggle_fullscreen(); @@ -2942,7 +2942,7 @@ impl TermWindow { domain: config::keyassignment::SpawnTabDomain::CurrentPaneDomain, ..Default::default() }, - SpawnWhere::Float); + SpawnWhere::FloatingPane); } else { tab.toggle_float(); } @@ -3114,10 +3114,10 @@ impl TermWindow { } PromptInputLine(args) => self.show_prompt_input_line(args), InputSelector(args) => self.show_input_selector(args), - MoveFloatingPaneToHorizontalSplit(spawn) => { + MoveFloatingPaneToHorizontalSplit => { self.move_floating_pane_to_split(SplitDirection::Horizontal); } - MoveFloatingPantToVerticalSplit(spawn) => { + MoveFloatingPaneToVerticalSplit => { self.move_floating_pane_to_split(SplitDirection::Vertical); } }; @@ -3125,7 +3125,7 @@ impl TermWindow { } fn move_floating_pane_to_split(&self, split_direction: SplitDirection) { - if !self.is_float_active() { + if !self.is_floating_pane_active() { return; } @@ -3397,14 +3397,14 @@ impl TermWindow { } } - pub fn is_float_active(&self) -> bool { + pub fn is_floating_pane_active(&self) -> bool { let mux = Mux::get(); let tab = match mux.get_active_tab_for_window(self.mux_window_id) { Some(tab) => tab, None => return false, }; - return tab.is_float_active(); + return tab.is_floating_pane_active(); } fn get_splits(&mut self) -> Vec { diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index b51d90a9343..cc8fe5c759b 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -688,7 +688,7 @@ impl super::TermWindow { // Mouse events are not dispatched to the other panes when // a floating pane is active, this is to prevent users from selecting one of the - // panes that the float is on top of and encountering some weird behavior, ex. + // panes that the floating pane is on top of and encountering some weird behavior, ex. // closing the last non-floating pane while the floating pane is active. return; } diff --git a/wezterm-gui/src/termwindow/paneselect.rs b/wezterm-gui/src/termwindow/paneselect.rs index 11519e1e46c..1e0ca104f59 100644 --- a/wezterm-gui/src/termwindow/paneselect.rs +++ b/wezterm-gui/src/termwindow/paneselect.rs @@ -216,7 +216,6 @@ impl PaneSelector { PaneSelectMode::MoveToFloatingPane => { if let Some(pos) = panes.iter().find(|p| p.index == pane_index) { let pane_id = pos.pane.pane_id(); - let window_id = term_window.mux_window_id; promise::spawn::spawn(async move { if let Err(err) = mux .move_pane_to_floating_pane(pane_id) diff --git a/wezterm-gui/src/termwindow/render/borders.rs b/wezterm-gui/src/termwindow/render/borders.rs index 3515c20a19d..334f6dcaa8e 100644 --- a/wezterm-gui/src/termwindow/render/borders.rs +++ b/wezterm-gui/src/termwindow/render/borders.rs @@ -1,7 +1,7 @@ use crate::quad::TripleLayerQuadAllocator; use crate::utilsprites::RenderMetrics; use ::window::ULength; -use config::{ConfigHandle, DimensionContext, FloatBorderConfig, PixelUnit}; +use config::{ConfigHandle, DimensionContext, FloatingPaneBorderConfig, PixelUnit}; use mux::tab::PositionedPane; use window::parameters::Border; @@ -133,14 +133,14 @@ impl crate::TermWindow { ) } - pub fn paint_float_border( + pub fn paint_floating_pane_border( &mut self, pos: PositionedPane, layers: &mut TripleLayerQuadAllocator, ) -> anyhow::Result<()> { let (padding_left, padding_top) = self.padding_left_top(); - let config = self.config.float_pane_border.clone(); - let float_border = self.get_float_border(); + let config = self.config.floating_pane_border.clone(); + let floating_pane_border = self.get_floating_pane_border(); let os_border = self.get_os_border(); let tab_bar_height = if self.show_tab_bar { @@ -161,10 +161,10 @@ impl crate::TermWindow { let background_rect = self.compute_background_rect(&pos, padding_left, padding_top, &os_border, top_pixel_y, cell_width, cell_height); - let pos_y = background_rect.origin.y - float_border.top.get() as f32; - let pos_x = background_rect.origin.x - float_border.left.get() as f32; - let pixel_width = background_rect.size.width + float_border.left.get() as f32; - let pixel_height = background_rect.size.height + float_border.top.get() as f32; + let pos_y = background_rect.origin.y - floating_pane_border.top.get() as f32; + let pos_x = background_rect.origin.x - floating_pane_border.left.get() as f32; + let pixel_width = background_rect.size.width + floating_pane_border.left.get() as f32; + let pixel_height = background_rect.size.height + floating_pane_border.top.get() as f32; self.filled_rectangle( layers, @@ -172,8 +172,8 @@ impl crate::TermWindow { euclid::rect( pos_x, pos_y, - float_border.left.get() as f32, - pixel_height + float_border.top.get() as f32, + floating_pane_border.left.get() as f32, + pixel_height + floating_pane_border.top.get() as f32, ), config.left_color.map(|c| c.to_linear()).unwrap_or(os_border.color), )?; @@ -181,10 +181,10 @@ impl crate::TermWindow { layers, 2, euclid::rect( - pos_x + float_border.left.get() as f32, + pos_x + floating_pane_border.left.get() as f32, pos_y, pixel_width, - float_border.top.get() as f32, + floating_pane_border.top.get() as f32, ), config.top_color.map(|c| c.to_linear()).unwrap_or(os_border.color), )?; @@ -192,10 +192,10 @@ impl crate::TermWindow { layers, 2, euclid::rect( - pos_x + float_border.left.get() as f32, + pos_x + floating_pane_border.left.get() as f32, pos_y + pixel_height, pixel_width, - float_border.bottom.get() as f32, + floating_pane_border.bottom.get() as f32, ), config.bottom_color.map(|c| c.to_linear()).unwrap_or(os_border.color), )?; @@ -205,8 +205,8 @@ impl crate::TermWindow { euclid::rect( pos_x + pixel_width, pos_y, - float_border.right.get() as f32, - pixel_height + float_border.top.get() as f32, + floating_pane_border.right.get() as f32, + pixel_height + floating_pane_border.top.get() as f32, ), config.right_color.map(|c| c.to_linear()).unwrap_or(os_border.color), )?; @@ -274,10 +274,10 @@ impl crate::TermWindow { } //refactor with get_os_border_impl? - fn get_float_border_impl( + fn get_floating_pane_border_impl( dimensions: &crate::Dimensions, render_metrics: &RenderMetrics, - border_config: &FloatBorderConfig + border_config: &FloatingPaneBorderConfig ) -> Border { let mut border= Border::default(); border.left += ULength::new( @@ -324,11 +324,11 @@ impl crate::TermWindow { border } - fn get_float_border(&self) -> Border { - Self::get_float_border_impl( + fn get_floating_pane_border(&self) -> Border { + Self::get_floating_pane_border_impl( &self.dimensions, &self.render_metrics, - &self.config.float_pane_border + &self.config.floating_pane_border ) } diff --git a/wezterm-gui/src/termwindow/render/paint.rs b/wezterm-gui/src/termwindow/render/paint.rs index 0b15fc21ed6..84f681e0a3e 100644 --- a/wezterm-gui/src/termwindow/render/paint.rs +++ b/wezterm-gui/src/termwindow/render/paint.rs @@ -251,11 +251,11 @@ impl crate::TermWindow { .context("filled_rectangle for window background")?; } - let mut float_layers = float_layer.quad_allocator(); + let mut floating_pane_layers = float_layer.quad_allocator(); for pos in panes { //This feels like technical debt, the floating panes should probably update the normal //panes that they are not active or something similar - if !self.is_float_active() { + if !self.is_floating_pane_active() { if pos.is_active { self.update_text_cursor(&pos); if focused { @@ -268,8 +268,8 @@ impl crate::TermWindow { } if let Some(floating_pane) = self.get_floating_pane_to_render(){ - self.paint_pane(&floating_pane, &mut float_layers).context("paint_pane")?; - self.paint_float_border(floating_pane, &mut float_layers).context("paint_float_border")?; + self.paint_pane(&floating_pane, &mut floating_pane_layers).context("paint_pane")?; + self.paint_floating_pane_border(floating_pane, &mut floating_pane_layers).context("paint_floating_pane_border")?; } if let Some(pane) = self.get_active_pane_or_overlay() { diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index 06d2df70963..dde88b7d9ad 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -14,7 +14,6 @@ use std::sync::{Arc, Mutex}; use std::time::Instant; use termwiz::surface::SequenceNo; use url::Url; -use mux::MuxNotification::ActiveFloatingPaneChanged; use wezterm_term::terminal::Alert; use wezterm_term::StableRowIndex; diff --git a/wezterm/src/cli/activate_tab.rs b/wezterm/src/cli/activate_tab.rs index 18f2206f0b7..ece5f9c3c0b 100644 --- a/wezterm/src/cli/activate_tab.rs +++ b/wezterm/src/cli/activate_tab.rs @@ -54,7 +54,7 @@ impl ActivateTab { let mut window_by_tab_id = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.0.into_tree().cursor(); + let mut cursor = tabroot.panes.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/list.rs b/wezterm/src/cli/list.rs index efe0262acb1..fd34abe1203 100644 --- a/wezterm/src/cli/list.rs +++ b/wezterm/src/cli/list.rs @@ -22,7 +22,7 @@ impl ListCommand { let panes = client.list_panes().await?; for (tabroot, tab_title) in panes.tabs.into_iter().zip(panes.tab_titles.iter()) { - let mut cursor = tabroot.0.into_tree().cursor(); + let mut cursor = tabroot.panes.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { @@ -43,7 +43,7 @@ impl ListCommand { } } - for floating_pane in &tabroot.1.0 { + for floating_pane in &tabroot.floating_panes { if let PaneNode::Leaf(entry) = floating_pane { let window_title = panes .window_titles diff --git a/wezterm/src/cli/move_pane_to_new_tab.rs b/wezterm/src/cli/move_pane_to_new_tab.rs index 119e9c13d16..e03faf6e952 100644 --- a/wezterm/src/cli/move_pane_to_new_tab.rs +++ b/wezterm/src/cli/move_pane_to_new_tab.rs @@ -41,7 +41,7 @@ impl MovePaneToNewTab { let panes = client.list_panes().await?; let mut window_id = None; 'outer_move: for tabroot in panes.tabs { - let mut cursor = tabroot.0.into_tree().cursor(); + let mut cursor = tabroot.panes.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/rename_workspace.rs b/wezterm/src/cli/rename_workspace.rs index c19a224b19f..170358852c8 100644 --- a/wezterm/src/cli/rename_workspace.rs +++ b/wezterm/src/cli/rename_workspace.rs @@ -29,7 +29,7 @@ impl RenameWorkspace { let mut pane_id_to_workspace = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.0.into_tree().cursor(); + let mut cursor = tabroot.panes.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/set_tab_title.rs b/wezterm/src/cli/set_tab_title.rs index 440b855e2a4..7efdbc6392c 100644 --- a/wezterm/src/cli/set_tab_title.rs +++ b/wezterm/src/cli/set_tab_title.rs @@ -28,7 +28,7 @@ impl SetTabTitle { let mut pane_id_to_tab_id = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.0.into_tree().cursor(); + let mut cursor = tabroot.panes.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/set_window_title.rs b/wezterm/src/cli/set_window_title.rs index f378cc07672..ca42173c4e5 100644 --- a/wezterm/src/cli/set_window_title.rs +++ b/wezterm/src/cli/set_window_title.rs @@ -29,7 +29,7 @@ impl SetWindowTitle { let mut pane_id_to_window_id = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.0.into_tree().cursor(); + let mut cursor = tabroot.panes.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/spawn_command.rs b/wezterm/src/cli/spawn_command.rs index f36c050ca68..b0e4a1c61aa 100644 --- a/wezterm/src/cli/spawn_command.rs +++ b/wezterm/src/cli/spawn_command.rs @@ -63,7 +63,7 @@ impl SpawnCommand { let panes = client.list_panes().await?; let mut window_id = None; 'outer: for tabroot in panes.tabs { - let mut cursor = tabroot.0.into_tree().cursor(); + let mut cursor = tabroot.panes.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { diff --git a/wezterm/src/cli/zoom_pane.rs b/wezterm/src/cli/zoom_pane.rs index ab3d5737ac0..2c9448af327 100644 --- a/wezterm/src/cli/zoom_pane.rs +++ b/wezterm/src/cli/zoom_pane.rs @@ -37,7 +37,7 @@ impl ZoomPane { let mut tab_id_to_active_zoomed_pane_id = HashMap::new(); for tabroot in panes.tabs { - let mut cursor = tabroot.0.into_tree().cursor(); + let mut cursor = tabroot.panes.into_tree().cursor(); loop { if let Some(entry) = cursor.leaf_mut() { From 3034fc62ce0999e44cc7594ed0b1ea5ea9728f4d Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 13 Oct 2024 15:32:57 -0700 Subject: [PATCH 53/66] Update that cli uses spwan instead of float-pane to create floating pane and command palete and keybindings use toggle floating pane instead of having a separate create floating pane command --- config/src/keyassignment.rs | 2 +- wezterm-gui/src/commands.rs | 17 +++++---- wezterm-gui/src/termwindow/mod.rs | 2 +- wezterm/src/cli/mod.rs | 11 +----- wezterm/src/cli/spawn_command.rs | 59 ++++++++++++++++++++----------- wezterm/src/cli/split_pane.rs | 43 ---------------------- 6 files changed, 50 insertions(+), 84 deletions(-) diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index b7662862cf2..895546bdcee 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -558,9 +558,9 @@ pub enum KeyAssignment { QuitApplication, SpawnCommandInNewTab(SpawnCommand), SpawnCommandInNewWindow(SpawnCommand), + SpawnCommandInNewFloatingPane(SpawnCommand), SplitHorizontal(SpawnCommand), SplitVertical(SpawnCommand), - SpawnFloatingPane(SpawnCommand), ToggleFloatingPane, ShowLauncher, ShowLauncherArgs(LauncherActionArgs), diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 545db6d43c7..a8144ebef98 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -1456,19 +1456,14 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { brief: label_string(action, "Create a floating pane".to_string()).into(), doc: "Create a floating pane" .into(), - keys: vec![( - Modifiers::CTRL - .union(Modifiers::ALT) - .union(Modifiers::SHIFT), - "p".into(), - )], + keys: vec![], args: &[ArgType::ActivePane], menubar: &["Shell"], icon: Some("cod_primitive_square"), @@ -1529,7 +1524,7 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { + SpawnCommandInNewFloatingPane(_) => CommandDef { brief: label_string(action, "Create a floating pane".to_string()).into(), doc: "Create a floating pane" .into(), @@ -1538,6 +1533,8 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { brief: label_string(action, "Move floating pane to horizontal split (Left/Right)".to_string()).into(), doc: "Move floating pane to horizontal split (Left/Right)" @@ -1547,6 +1544,8 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { brief: label_string(action, "Move floating pane to vertical split (Top/Bottom)".to_string()).into(), doc: "Move floating pane to vertical split (Top/Bottom)" @@ -2088,7 +2087,7 @@ fn compute_default_actions() -> Vec { // ----------------- Shell SpawnTab(SpawnTabDomain::CurrentPaneDomain), SpawnWindow, - SpawnFloatingPane(SpawnCommand { + SpawnCommandInNewFloatingPane(SpawnCommand { domain: SpawnTabDomain::CurrentPaneDomain, ..Default::default() }), diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 60a9482acdd..d99d301a20f 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -2618,7 +2618,7 @@ impl TermWindow { }), ); } - SpawnFloatingPane(spawn) => { + SpawnCommandInNewFloatingPane(spawn) => { log::trace!("SpawnFloatingPane {:?}", spawn); self.spawn_command( spawn, diff --git a/wezterm/src/cli/mod.rs b/wezterm/src/cli/mod.rs index c9495bec778..a00bd787c5f 100644 --- a/wezterm/src/cli/mod.rs +++ b/wezterm/src/cli/mod.rs @@ -103,18 +103,10 @@ Outputs the pane-id for the newly created pane on success" )] SplitPane(split_pane::SplitPane), - #[command( - name = "float-pane", - rename_all = "kebab", - trailing_var_arg = true, - about = "spawn a floating pane" - )] - SpawnFloatingPane(split_pane::FloatingPane), - #[command( name = "spawn", trailing_var_arg = true, - about = "Spawn a command into a new window or tab + about = "Spawn a command into a new window, tab or floating pane Outputs the pane-id for the newly created pane on success" )] SpawnCommand(spawn_command::SpawnCommand), @@ -192,7 +184,6 @@ async fn run_cli_async(opts: &crate::Opt, cli: CliCommand) -> anyhow::Result<()> CliSubCommand::List(cmd) => cmd.run(client).await, CliSubCommand::MovePaneToNewTab(cmd) => cmd.run(client).await, CliSubCommand::SplitPane(cmd) => cmd.run(client).await, - CliSubCommand::SpawnFloatingPane(cmd) => cmd.run(client).await, CliSubCommand::SendText(cmd) => cmd.run(client).await, CliSubCommand::GetText(cmd) => cmd.run(client).await, CliSubCommand::SpawnCommand(cmd) => cmd.run(client, &crate::init_config(opts)?).await, diff --git a/wezterm/src/cli/spawn_command.rs b/wezterm/src/cli/spawn_command.rs index b0e4a1c61aa..80f594f0d46 100644 --- a/wezterm/src/cli/spawn_command.rs +++ b/wezterm/src/cli/spawn_command.rs @@ -32,6 +32,9 @@ pub struct SpawnCommand { #[arg(long)] new_window: bool, + #[arg(long)] + floating_pane: bool, + /// Specify the current working directory for the initially /// spawned program #[arg(long, value_parser, value_hint=ValueHint::DirPath)] @@ -96,26 +99,42 @@ impl SpawnCommand { let size = config.initial_size(0, None); - let spawned = client - .spawn_v2(codec::SpawnV2 { - domain: self - .domain_name - .map_or(SpawnTabDomain::DefaultDomain, |name| { - SpawnTabDomain::DomainName(name) - }), - window_id, - command: if self.prog.is_empty() { - None - } else { - let builder = CommandBuilder::from_argv(self.prog); - Some(builder) - }, - command_dir: resolve_relative_cwd(self.cwd)?, - size, - workspace, - }) - .await?; - + let spawned = if self.floating_pane { + let pane_id = client.resolve_pane_id(self.pane_id).await?; + client + .add_floating_pane(codec::SpawnFloatingPane { + pane_id, + domain: config::keyassignment::SpawnTabDomain::CurrentPaneDomain, + command: if self.prog.is_empty() { + None + } else { + let builder = CommandBuilder::from_argv(self.prog); + Some(builder) + }, + command_dir: resolve_relative_cwd(self.cwd)? + }) + .await? + } else { + client + .spawn_v2(codec::SpawnV2 { + domain: self + .domain_name + .map_or(SpawnTabDomain::DefaultDomain, |name| { + SpawnTabDomain::DomainName(name) + }), + window_id, + command: if self.prog.is_empty() { + None + } else { + let builder = CommandBuilder::from_argv(self.prog); + Some(builder) + }, + command_dir: resolve_relative_cwd(self.cwd)?, + size, + workspace, + }) + .await? + }; log::debug!("{:?}", spawned); println!("{}", spawned.pane_id); Ok(()) diff --git a/wezterm/src/cli/split_pane.rs b/wezterm/src/cli/split_pane.rs index 43d7c87d441..e51f5082039 100644 --- a/wezterm/src/cli/split_pane.rs +++ b/wezterm/src/cli/split_pane.rs @@ -113,46 +113,3 @@ impl SplitPane { Ok(()) } } - -#[derive(Debug, Parser, Clone)] -pub struct FloatingPane { - /// Specify the pane that should be floating. - /// The default is to use the current pane based on the - /// environment variable WEZTERM_PANE. - #[arg(long)] - pane_id: Option, - - /// Specify the current working directory for the initially - /// spawned program - #[arg(long, value_parser, value_hint=ValueHint::DirPath)] - cwd: Option, - - /// Instead of executing your shell, run PROG. - /// For example: `wezterm cli float-pane -- bash -l` will spawn bash - /// as if it were a login shell. - #[arg(value_parser, value_hint=ValueHint::CommandWithArguments, num_args=1..)] - prog: Vec, -} - -impl FloatingPane { - pub async fn run(self, client: Client) -> anyhow::Result<()> { - let pane_id = client.resolve_pane_id(self.pane_id).await?; - let spawned = client - .add_floating_pane(codec::SpawnFloatingPane { - pane_id, - domain: config::keyassignment::SpawnTabDomain::CurrentPaneDomain, - command: if self.prog.is_empty() { - None - } else { - let builder = CommandBuilder::from_argv(self.prog); - Some(builder) - }, - command_dir: resolve_relative_cwd(self.cwd)? - }) - .await?; - - log::debug!("{:?}", spawned); - println!("{}", spawned.pane_id); - Ok(()) - } -} From e4898715e36411dd7d310cbb061c58a04fc9e6f4 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 13 Oct 2024 22:32:01 -0700 Subject: [PATCH 54/66] Refactored floating pane code in mux/src/(lib|tab).rs --- mux/src/domain.rs | 7 +- mux/src/lib.rs | 135 ++++++++----------- mux/src/tab.rs | 64 ++++----- wezterm-gui/src/termwindow/render/borders.rs | 105 ++++++--------- 4 files changed, 134 insertions(+), 177 deletions(-) diff --git a/mux/src/domain.rs b/mux/src/domain.rs index fc718408159..bdb9b9a674f 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -101,10 +101,8 @@ pub trait Domain: Downcast + Send + Sync { None => anyhow::bail!("Invalid tab id {}", tab), }; - let floating_pane = tab.get_active_floating_pane() - .ok_or_else(|| anyhow::anyhow!("tab does not have active floating pane"))?; + let floating_pane = tab.remove_floating_pane(tab.get_active_floating_pane_index())?; - tab.remove_floating_pane(tab.get_active_floating_pane_index()); tab.set_floating_pane_visibility(false); //TODO: Figure out if all floating pane stuff should be removed from tab.get_active_pane @@ -116,6 +114,7 @@ pub trait Domain: Downcast + Send + Sync { let pane_id = active_non_floating_pane.pane_id(); + //TODO: this is duplicated let pane_index = match tab .iter_panes_ignoring_zoom() .iter() @@ -131,7 +130,7 @@ pub trait Domain: Downcast + Send + Sync { top_level: false, size: Default::default(), }; - tab.split_and_insert(pane_index, split_request, Arc::clone(&floating_pane.pane))?; + tab.split_and_insert(pane_index, split_request, Arc::clone(&floating_pane))?; Ok(()) } diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 62c6cfadf38..8be21655be1 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -27,7 +27,7 @@ use std::time::{Duration, Instant}; use termwiz::escape::csi::{DecPrivateMode, DecPrivateModeCode, Device, Mode}; use termwiz::escape::{Action, CSI}; use thiserror::*; -use wezterm_term::{Clipboard, ClipboardSelection, DownloadHandler, TerminalSize}; +use wezterm_term::{Clipboard, ClipboardSelection, DownloadHandler, TerminalConfiguration, TerminalSize}; #[cfg(windows)] use winapi::um::winsock2::{SOL_SOCKET, SO_RCVBUF, SO_SNDBUF}; @@ -48,6 +48,7 @@ mod tmux_pty; pub mod window; use crate::activity::Activity; +use crate::renderable::RenderableDimensions; pub const DEFAULT_WORKSPACE: &str = "default"; @@ -1191,13 +1192,38 @@ impl Mux { }) } - pub async fn spawn_floating_pane( - &self, - pane_id: PaneId, - command_builder: Option, - command_dir: Option, - domain: config::keyassignment::SpawnTabDomain, - ) -> anyhow::Result<(Arc, TerminalSize)> { + fn apply_config_to_pane(pane: Arc, term_config: Option>) -> (TerminalSize) { + if let Some(config) = term_config { + pane.set_config(config); + } + + let dims = pane.get_dimensions(); + + let size = TerminalSize { + cols: dims.cols, + rows: dims.viewport_rows, + pixel_height: 0, + pixel_width: 0, + dpi: dims.dpi, + }; + + size + } + + fn resolve_domain_from_pane_id(&self, pane_id: PaneId) -> anyhow::Result<(Arc, WindowId, TabId)> { + let (domain_id, src_window, src_tab) = self + .resolve_pane_id(pane_id) + .ok_or_else(|| anyhow::anyhow!("pane {} not found", pane_id))?; + + let domain = self + .get_domain(domain_id) + .ok_or_else(|| anyhow::anyhow!("domain {domain_id} of pane {pane_id} not found"))?; + + Ok((domain, src_window, src_tab)) + } + + async fn resolve_spawn_tab_domain_from_pane_id(&self, pane_id: PaneId, domain: SpawnTabDomain) -> + anyhow::Result<(Arc, TabId, Arc, Option>)> { let (_pane_domain_id, window_id, tab_id) = self .resolve_pane_id(pane_id) .ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?; @@ -1213,7 +1239,19 @@ impl Mux { let current_pane = self .get_pane(pane_id) .ok_or_else(|| anyhow!("pane_id {} is invalid", pane_id))?; + let term_config = current_pane.get_config(); + Ok((domain, tab_id, current_pane, term_config)) + } + + pub async fn spawn_floating_pane( + &self, + pane_id: PaneId, + command_builder: Option, + command_dir: Option, + domain: config::keyassignment::SpawnTabDomain, + ) -> anyhow::Result<(Arc, TerminalSize)> { + let (domain, tab_id, current_pane, term_config) = self.resolve_spawn_tab_domain_from_pane_id(pane_id, domain).await?; let command_dir = if !command_dir.is_some() { self.resolve_cwd( @@ -1227,20 +1265,7 @@ impl Mux { }; let pane = domain.add_floating_pane(tab_id, pane_id, command_builder, command_dir).await?; - - if let Some(config) = term_config { - pane.set_config(config); - } - - let dims = pane.get_dimensions(); - - let size = TerminalSize { - cols: dims.cols, - rows: dims.viewport_rows, - pixel_height: 0, - pixel_width: 0, - dpi: dims.dpi, - }; + let size = Self::apply_config_to_pane(Arc::clone(&pane), term_config); Ok((pane, size)) } @@ -1250,21 +1275,12 @@ impl Mux { pane_id: PaneId, direction: SplitDirection, ) -> anyhow::Result<()> { - let (pane_domain_id, window_id, tab_id) = self - .resolve_pane_id(pane_id) - .ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?; - - let domain = self - .get_domain(pane_domain_id) - //TODO::Update this - .context("resolve_spawn_tab_domain")?; - - if domain.state() == DomainState::Detached { - domain.attach(Some(window_id)).await?; - } + let (domain, window_id, tab_id) = self.resolve_domain_from_pane_id(pane_id)?; domain.move_floating_pane_to_split(tab_id, direction).await?; + //TODO: why don't I have to do the other stuff in move_pane_to_floating_pane + Ok(()) } @@ -1276,22 +1292,8 @@ impl Mux { source: SplitSource, domain: config::keyassignment::SpawnTabDomain, ) -> anyhow::Result<(Arc, TerminalSize)> { - let (_pane_domain_id, window_id, tab_id) = self - .resolve_pane_id(pane_id) - .ok_or_else(|| anyhow!("pane_id {} invalid", pane_id))?; - - let domain = self - .resolve_spawn_tab_domain(Some(pane_id), &domain) - .context("resolve_spawn_tab_domain")?; - - if domain.state() == DomainState::Detached { - domain.attach(Some(window_id)).await?; - } - - let current_pane = self - .get_pane(pane_id) - .ok_or_else(|| anyhow!("pane_id {} is invalid", pane_id))?; - let term_config = current_pane.get_config(); + let (domain, tab_id, current_pane, term_config) = + self.resolve_spawn_tab_domain_from_pane_id(pane_id, domain).await?; let source = match source { SplitSource::Spawn { @@ -1310,21 +1312,7 @@ impl Mux { }; let pane = domain.split_pane(source, tab_id, pane_id, request).await?; - if let Some(config) = term_config { - pane.set_config(config); - } - - // FIXME: clipboard - - let dims = pane.get_dimensions(); - - let size = TerminalSize { - cols: dims.cols, - rows: dims.viewport_rows, - pixel_height: 0, // FIXME: split pane pixel dimensions - pixel_width: 0, - dpi: dims.dpi, - }; + let size = Self::apply_config_to_pane(Arc::clone(&pane), term_config); Ok((pane, size)) } @@ -1333,13 +1321,7 @@ impl Mux { &self, pane_id: PaneId, ) -> anyhow::Result<()> { - let (domain_id, _src_window, src_tab) = self - .resolve_pane_id(pane_id) - .ok_or_else(|| anyhow::anyhow!("pane {} not found", pane_id))?; - - let domain = self - .get_domain(domain_id) - .ok_or_else(|| anyhow::anyhow!("domain {domain_id} of pane {pane_id} not found"))?; + let (domain, _, src_tab) = self.resolve_domain_from_pane_id(pane_id)?; if domain.move_pane_to_floating_pane(pane_id).await?{ return Ok(()) @@ -1365,14 +1347,7 @@ impl Mux { window_id: Option, workspace_for_new_window: Option, ) -> anyhow::Result<(Arc, WindowId)> { - let (domain_id, _src_window, src_tab) = self - .resolve_pane_id(pane_id) - .ok_or_else(|| anyhow::anyhow!("pane {} not found", pane_id))?; - - let domain = self - .get_domain(domain_id) - .ok_or_else(|| anyhow::anyhow!("domain {domain_id} of pane {pane_id} not found"))?; - + let (domain, _, src_tab) = self.resolve_domain_from_pane_id(pane_id)?; if let Some((tab, window_id)) = domain .move_pane_to_new_tab(pane_id, window_id, workspace_for_new_window.clone()) .await? diff --git a/mux/src/tab.rs b/mux/src/tab.rs index fd4d88f2840..1f23dd6b3e6 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -732,7 +732,6 @@ impl Tab { self.inner.lock().set_active_floating_pane(index, true); } - //TODO: This should be something like add_floating_pane now pub fn append_floating_pane(&self, pane: Arc) { self.inner.lock().append_floating_pane(pane); } @@ -778,8 +777,8 @@ impl Tab { self.inner.lock().active_floating_pane } - pub fn remove_floating_pane(&self, index: usize ) { - self.inner.lock().remove_floating_pane(index); + pub fn remove_floating_pane(&self, index: usize) -> anyhow::Result> { + self.inner.lock().remove_floating_pane(index) } /// Split the pane that has pane_index in the given direction and assign @@ -880,6 +879,8 @@ impl TabInner { self.pane.replace(cursor.tree()); self.size = size; + self.active_floating_pane = root.active_floating_pane_index; + self.floating_pane_visible = root.floating_pane_visible; //TODO: we are in a lock this should be safe? is it needed? self.floating_panes.clear(); for pane_node in root.floating_panes { @@ -889,8 +890,6 @@ impl TabInner { if is_zoomed { zoomed = Some(Arc::clone(&floating_pane)) } - self.active_floating_pane = root.active_floating_pane_index; - self.floating_pane_visible = root.floating_pane_visible; self.floating_panes.push(floating_pane); } } @@ -943,7 +942,7 @@ impl TabInner { let zoomed = self.zoomed.as_ref(); let mut codec_floating_panes = vec!(); - for floating_pane in &self.floating_panes { + for (i, floating_pane) in self.floating_panes.iter().enumerate() { let dims = floating_pane.get_dimensions(); let working_dir = floating_pane.get_current_working_dir(CachePolicy::AllowStale); let cursor_pos = floating_pane.get_cursor_position(); @@ -961,8 +960,7 @@ impl TabInner { dpi: dims.dpi, }, working_dir: working_dir.map(Into::into), - //TODO: this seems wrong, there are other floating panes - is_active_pane: self.floating_pane_visible, + is_active_pane: i == self.active_floating_pane && self.floating_pane_visible, is_zoomed_pane: is_pane(floating_pane, &zoomed), is_floating_pane: true, workspace: workspace.to_string(), @@ -1088,7 +1086,8 @@ impl TabInner { } fn get_active_floating_pane(&self) -> Option { - //TODO: is this right? A floating pane can be zoomed + //Even if the zoomed pane is a floating pane. Not returning it since it is currently + //not visible as a floating pane, instead as a zoomed pane if self.zoomed.is_some() { return None } @@ -1773,8 +1772,7 @@ impl TabInner { let mux = Mux::get(); let previous_floating_pane_visibility = self.floating_pane_visible; let previous_active_floating_pane = self.active_floating_pane; - //TODO: this may be important - let mut result = !self + let mut invalidate = !self .remove_pane_if( |_, pane| { // If the pane is no longer known to the mux, then its liveness @@ -1809,10 +1807,10 @@ impl TabInner { if previous_floating_pane_visibility != self.floating_pane_visible || previous_active_floating_pane != self.active_floating_pane { - result = true; + invalidate = true; } - result + invalidate } fn kill_pane(&mut self, pane_id: PaneId) -> bool { @@ -1930,8 +1928,7 @@ impl TabInner { self.active = active_idx.saturating_sub(removed_indices.len()); } - // Step 1: Collect indices of panes to remove and count how many are before the active pane - let mut indices_to_remove = Vec::new(); + let mut floating_pane_indices_to_remove = Vec::new(); let mut count_before_active = 0; for (i, floating_pane) in self.floating_panes.iter().enumerate() { @@ -1942,16 +1939,16 @@ impl TabInner { count_before_active += 1; } - indices_to_remove.push(i); + floating_pane_indices_to_remove.push(i); } } let active_floating_index = self.active_floating_pane; - indices_to_remove.retain(|&idx| idx <= active_floating_index); - let new_active_floating_pane = active_floating_index.saturating_sub(indices_to_remove.len()); + floating_pane_indices_to_remove.retain(|&idx| idx <= active_floating_index); + let new_active_floating_pane = active_floating_index.saturating_sub(floating_pane_indices_to_remove.len()); self.set_active_floating_pane(new_active_floating_pane, false); - for i in indices_to_remove { + for i in floating_pane_indices_to_remove { self.floating_panes.remove(i); } @@ -2027,8 +2024,6 @@ impl TabInner { } } - //A pane that is not floating is being set active so we make - //sure that the floating pane is hidden if self.floating_pane_visible { self.set_floating_pane_visibility(false, true); } @@ -2188,18 +2183,27 @@ impl TabInner { None } - //TODO: this should be updated to accept a pane - fn remove_floating_pane(& mut self, index: usize) { - if self.active_floating_pane < self.floating_panes.len() { - self.floating_panes.remove(index); + fn remove_floating_pane(& mut self, index: usize) -> anyhow::Result> { + if index >= self.floating_panes.len() { + anyhow::bail!("floating_panes index {index} out or range") } - //TODO: This seems wrong - if self.floating_panes.len() > 0 && self.active_floating_pane > 0 { - self.set_active_floating_pane(self.active_floating_pane - 1, true); - } else { - self.set_active_floating_pane(0, true); + + let result = self.floating_panes.remove(index); + if self.floating_panes.is_empty() { self.set_floating_pane_visibility(false, true); + self.active_floating_pane = 0; + } else { + if self.active_floating_pane > index { + self.active_floating_pane -= 1; + } else if self.active_floating_pane == index { + if index >= self.floating_panes.len() { + self.active_floating_pane = self.floating_panes.len() - 1; + } else { + self.active_floating_pane = index; + } + } } + Ok(Arc::clone(&result)) } fn compute_floating_pane_size(&self) -> TerminalSize { diff --git a/wezterm-gui/src/termwindow/render/borders.rs b/wezterm-gui/src/termwindow/render/borders.rs index 334f6dcaa8e..5f73347a411 100644 --- a/wezterm-gui/src/termwindow/render/borders.rs +++ b/wezterm-gui/src/termwindow/render/borders.rs @@ -1,7 +1,7 @@ use crate::quad::TripleLayerQuadAllocator; use crate::utilsprites::RenderMetrics; use ::window::ULength; -use config::{ConfigHandle, DimensionContext, FloatingPaneBorderConfig, PixelUnit}; +use config::{ConfigHandle, Dimension, DimensionContext, FloatingPaneBorderConfig, PixelUnit}; use mux::tab::PositionedPane; use window::parameters::Border; @@ -214,21 +214,17 @@ impl crate::TermWindow { Ok(()) } - pub fn get_os_border_impl( - os_parameters: &Option, - config: &ConfigHandle, + fn apply_config_to_border( + border: &mut Border, + left_width: Dimension, + right_width: Dimension, + top_height: Dimension, + bottom_height: Dimension, dimensions: &crate::Dimensions, render_metrics: &RenderMetrics, - ) -> window::parameters::Border { - let mut border = os_parameters - .as_ref() - .and_then(|p| p.border_dimensions.clone()) - .unwrap_or_default(); - + ) { border.left += ULength::new( - config - .window_frame - .border_left_width + left_width .evaluate_as_pixels(DimensionContext { dpi: dimensions.dpi as f32, pixel_max: dimensions.pixel_width as f32, @@ -237,9 +233,7 @@ impl crate::TermWindow { .ceil() as usize, ); border.right += ULength::new( - config - .window_frame - .border_right_width + right_width .evaluate_as_pixels(DimensionContext { dpi: dimensions.dpi as f32, pixel_max: dimensions.pixel_width as f32, @@ -248,9 +242,7 @@ impl crate::TermWindow { .ceil() as usize, ); border.top += ULength::new( - config - .window_frame - .border_top_height + top_height .evaluate_as_pixels(DimensionContext { dpi: dimensions.dpi as f32, pixel_max: dimensions.pixel_height as f32, @@ -259,9 +251,7 @@ impl crate::TermWindow { .ceil() as usize, ); border.bottom += ULength::new( - config - .window_frame - .border_bottom_height + bottom_height .evaluate_as_pixels(DimensionContext { dpi: dimensions.dpi as f32, pixel_max: dimensions.pixel_height as f32, @@ -269,58 +259,47 @@ impl crate::TermWindow { }) .ceil() as usize, ); + } + + pub fn get_os_border_impl( + os_parameters: &Option, + config: &ConfigHandle, + dimensions: &crate::Dimensions, + render_metrics: &RenderMetrics, + ) -> window::parameters::Border { + let mut border = os_parameters + .as_ref() + .and_then(|p| p.border_dimensions.clone()) + .unwrap_or_default(); + + Self::apply_config_to_border( + &mut border, + config.window_frame.border_left_width, + config.window_frame.border_right_width, + config.window_frame.border_top_height, + config.window_frame.border_bottom_height, + dimensions, + render_metrics + ); border } - //refactor with get_os_border_impl? fn get_floating_pane_border_impl( dimensions: &crate::Dimensions, render_metrics: &RenderMetrics, border_config: &FloatingPaneBorderConfig ) -> Border { let mut border= Border::default(); - border.left += ULength::new( - border_config - .left_width - .evaluate_as_pixels(DimensionContext { - dpi: dimensions.dpi as f32, - pixel_max: dimensions.pixel_width as f32, - pixel_cell: render_metrics.cell_size.width as f32, - }) - .ceil() as usize, - ); - border.right += ULength::new( - border_config - .right_width - .evaluate_as_pixels(DimensionContext { - dpi: dimensions.dpi as f32, - pixel_max: dimensions.pixel_width as f32, - pixel_cell: render_metrics.cell_size.width as f32, - }) - .ceil() as usize, + Self::apply_config_to_border( + &mut border, + border_config.left_width, + border_config.right_width, + border_config.top_height, + border_config.bottom_height, + dimensions, + render_metrics ); - border.top += ULength::new( - border_config - .top_height - .evaluate_as_pixels(DimensionContext { - dpi: dimensions.dpi as f32, - pixel_max: dimensions.pixel_height as f32, - pixel_cell: render_metrics.cell_size.height as f32, - }) - .ceil() as usize, - ); - border.bottom += ULength::new( - border_config - .bottom_height - .evaluate_as_pixels(DimensionContext { - dpi: dimensions.dpi as f32, - pixel_max: dimensions.pixel_height as f32, - pixel_cell: render_metrics.cell_size.height as f32, - }) - .ceil() as usize, - ); - border } From 426f0e284d228db2dc9aa6cf3ecdec8f821faf55 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 15 Oct 2024 17:17:10 -0700 Subject: [PATCH 55/66] Try to improve floating pane defaults --- config/src/color.rs | 16 ++++++++-------- wezterm-client/src/domain.rs | 4 +--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/config/src/color.rs b/config/src/color.rs index d024846ba41..4b591ac2238 100644 --- a/config/src/color.rs +++ b/config/src/color.rs @@ -649,14 +649,14 @@ impl Default for WindowFrameConfig { impl Default for FloatingPaneBorderConfig { fn default() -> Self { Self { - left_width: default_zero_pixel(), - right_width: default_zero_pixel(), - top_height: default_zero_pixel(), - bottom_height: default_zero_pixel(), - left_color: None, - right_color: None, - top_color: None, - bottom_color: None, + left_width: Dimension::Pixels(3.), + right_width: Dimension::Pixels(3.), + top_height: Dimension::Pixels(3.), + bottom_height: Dimension::Pixels(3.), + left_color: Some(default_active_titlebar_bg()), + right_color: Some(default_active_titlebar_bg()), + top_color: Some(default_active_titlebar_bg()), + bottom_color: Some(default_active_titlebar_bg()), } } } diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index bb0a7c4f0da..8c3f5957502 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -841,9 +841,6 @@ impl Domain for ClientDomain { Ok(true) } - /// Forward the request to the remote; we need to translate the local ids - /// to those that match the remote for the request, resync the changed - /// structure, and then translate the results back to local async fn move_floating_pane_to_split( &self, pane_id: PaneId, @@ -856,6 +853,7 @@ impl Domain for ClientDomain { let local_pane = Mux::get() .get_pane(pane_id) .ok_or_else(|| anyhow!("pane_id {} is invalid", pane_id))?; + let pane = local_pane .downcast_ref::() .ok_or_else(|| anyhow!("pane_id {} is not a ClientPane", pane_id))?; From 0f4010d9b1dfd6f3a66a7d14ec97f56d90f66118 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Thu, 17 Oct 2024 18:00:36 -0700 Subject: [PATCH 56/66] Fix for overlays (quick-select, find, copy mode) not working with floating panes. --- wezterm-gui/src/termwindow/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index d99d301a20f..bc767fd3460 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -3539,7 +3539,14 @@ impl TermWindow { return None; } - tab.get_active_floating_pane() + let mut pos = tab.get_active_floating_pane(); + if let Some(ref mut pos) = pos { + if let Some(overlay) = self.pane_state(pos.pane.pane_id()).overlay.as_ref() { + pos.pane = Arc::clone(&overlay.pane); + } + } + + pos } /// if pane_id.is_none(), removes any overlay for the specified tab. From ad6439d4b81437f0b8f0116f97882bcee70db653 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Thu, 17 Oct 2024 18:47:04 -0700 Subject: [PATCH 57/66] Fix for tab level overlays being hidden by floating pane. --- wezterm-gui/src/termwindow/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index bc767fd3460..92db5535497 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -3535,6 +3535,15 @@ impl TermWindow { None => return None, }; + if self + .tab_state(tab.tab_id()) + .overlay + .as_ref() + .map(|overlay| overlay.pane.clone()) + { + return None; + } + if !tab.floating_pane_is_visible() { return None; } From 435d83899834c97d07905ceec963ba5f5c255d8f Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Thu, 17 Oct 2024 23:41:28 -0700 Subject: [PATCH 58/66] Update so that cli move-pane-to-new-tab works with floating panes --- mux/src/lib.rs | 12 ++++++++---- mux/src/tab.rs | 8 ++++---- wezterm-gui/src/termwindow/mod.rs | 1 + wezterm/src/cli/move_pane_to_new_tab.rs | 10 ++++++++++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 8be21655be1..baece0acd72 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1075,7 +1075,7 @@ impl Mux { pub fn resolve_pane_id(&self, pane_id: PaneId) -> Option<(DomainId, WindowId, TabId)> { let mut ids = None; for tab in self.tabs.read().values() { - if let Some(floating_pane) = tab.get_floating_pane_by_pane_id(pane_id){ + if let Some((index, floating_pane)) = tab.get_floating_pane_by_pane_id(pane_id){ ids = Some((tab.tab_id(), floating_pane.domain_id())); break; } @@ -1376,9 +1376,13 @@ impl Mux { (*window_builder, src_tab.get_size()) }; - let pane = src_tab - .remove_pane(pane_id) - .ok_or_else(|| anyhow::anyhow!("pane {} wasn't in its containing tab!?", pane_id))?; + let pane = if let Some((i, _)) = src_tab.get_floating_pane_by_pane_id(pane_id) { + src_tab.remove_floating_pane(i)? + } else { + src_tab + .remove_pane(pane_id) + .ok_or_else(|| anyhow::anyhow!("pane {} wasn't in its containing tab!?", pane_id))? + }; let tab = Arc::new(Tab::new(&size)); tab.assign_pane(&pane); diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 1f23dd6b3e6..737c792d2c5 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -587,7 +587,7 @@ impl Tab { self.inner.lock().floating_pane_is_visible() } - pub fn get_floating_pane_by_pane_id(&self, pane_id: PaneId) -> Option> { + pub fn get_floating_pane_by_pane_id(&self, pane_id: PaneId) -> Option<(usize, Arc)> { self.inner.lock().get_floating_pane_by_pane_id(pane_id) } @@ -1072,10 +1072,10 @@ impl TabInner { self.iter_panes_impl(true) } - fn get_floating_pane_by_pane_id(&self, pane_id: PaneId) -> Option> { - for pane in &self.floating_panes { + fn get_floating_pane_by_pane_id(&self, pane_id: PaneId) -> Option<(usize, Arc)> { + for (i, pane) in self.floating_panes.iter().enumerate() { if pane.pane_id() == pane_id { - return Some(Arc::clone(&pane)) + return Some((i, Arc::clone(&pane))) } } return None diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 92db5535497..c574bac8c12 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -3540,6 +3540,7 @@ impl TermWindow { .overlay .as_ref() .map(|overlay| overlay.pane.clone()) + .is_some() { return None; } diff --git a/wezterm/src/cli/move_pane_to_new_tab.rs b/wezterm/src/cli/move_pane_to_new_tab.rs index e03faf6e952..dddbeb664f4 100644 --- a/wezterm/src/cli/move_pane_to_new_tab.rs +++ b/wezterm/src/cli/move_pane_to_new_tab.rs @@ -1,5 +1,6 @@ use clap::Parser; use mux::pane::PaneId; +use mux::tab::PaneNode::Leaf; use mux::window::WindowId; use wezterm_client::client::Client; @@ -41,6 +42,15 @@ impl MovePaneToNewTab { let panes = client.list_panes().await?; let mut window_id = None; 'outer_move: for tabroot in panes.tabs { + for floating_pane in tabroot.floating_panes { + if let Leaf(floating_pane) = floating_pane { + if floating_pane.pane_id == pane_id { + window_id.replace(floating_pane.window_id); + break 'outer_move; + } + } + } + let mut cursor = tabroot.panes.into_tree().cursor(); loop { From 107b353aab9a06720be8aa03f584493d8ec174b1 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 18 Oct 2024 17:42:50 -0700 Subject: [PATCH 59/66] Update so that floating panes are included in tab pane counts --- mux/src/tab.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 737c792d2c5..046d9e2b0a0 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -1004,6 +1004,10 @@ impl TabInner { let mut count = 0; let mut cursor = self.pane.take().unwrap().cursor(); + for pane in &self.floating_panes { + count += 1; + } + loop { if cursor.is_leaf() { count += 1; From d14d0947f76787e828a1c528bda56d82814dd451 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 18 Oct 2024 23:10:47 -0700 Subject: [PATCH 60/66] Update move_floating_pane_to_split to match move_pane_to_new_tab --- mux/src/domain.rs | 40 ++---------------------------------- mux/src/lib.rs | 39 +++++++++++++++++++++++++++++++++-- wezterm-client/src/domain.rs | 4 ++-- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/mux/src/domain.rs b/mux/src/domain.rs index bdb9b9a674f..b2690566f84 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -94,44 +94,8 @@ pub trait Domain: Downcast + Send + Sync { &self, tab: TabId, direction: SplitDirection, - ) -> anyhow::Result<()> { - let mux = Mux::get(); - let tab = match mux.get_tab(tab) { - Some(t) => t, - None => anyhow::bail!("Invalid tab id {}", tab), - }; - - let floating_pane = tab.remove_floating_pane(tab.get_active_floating_pane_index())?; - - tab.set_floating_pane_visibility(false); - - //TODO: Figure out if all floating pane stuff should be removed from tab.get_active_pane - let active_non_floating_pane = tab.iter_panes_ignoring_zoom() - .iter() - .nth(tab.get_active_idx()) - .map(|p| Arc::clone(&p.pane)) - .ok_or_else(|| anyhow::anyhow!("tab does not have a active non floating pane"))?; - - let pane_id = active_non_floating_pane.pane_id(); - - //TODO: this is duplicated - let pane_index = match tab - .iter_panes_ignoring_zoom() - .iter() - .find(|p| p.pane.pane_id() == pane_id) - { - Some(p) => p.index, - None => anyhow::bail!("invalid pane id {}", pane_id), - }; - - let split_request = SplitRequest { - direction, - target_is_second: true, - top_level: false, - size: Default::default(), - }; - tab.split_and_insert(pane_index, split_request, Arc::clone(&floating_pane))?; - Ok(()) + ) -> anyhow::Result { + Ok(false) } async fn split_pane( diff --git a/mux/src/lib.rs b/mux/src/lib.rs index baece0acd72..4c389d59d54 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1277,9 +1277,44 @@ impl Mux { ) -> anyhow::Result<()> { let (domain, window_id, tab_id) = self.resolve_domain_from_pane_id(pane_id)?; - domain.move_floating_pane_to_split(tab_id, direction).await?; + if domain.move_floating_pane_to_split(tab_id, direction).await? { + return Ok(()) + } + + let mux = Mux::get(); + let tab = match mux.get_tab(tab_id) { + Some(t) => t, + None => anyhow::bail!("Invalid tab id {}", tab_id), + }; + + let floating_pane = tab.remove_floating_pane(tab.get_active_floating_pane_index())?; + + tab.set_floating_pane_visibility(false); - //TODO: why don't I have to do the other stuff in move_pane_to_floating_pane + let active_non_floating_pane = tab.iter_panes_ignoring_zoom() + .iter() + .nth(tab.get_active_idx()) + .map(|p| Arc::clone(&p.pane)) + .ok_or_else(|| anyhow::anyhow!("tab does not have a active non floating pane"))?; + + let pane_id = active_non_floating_pane.pane_id(); + + let pane_index = match tab + .iter_panes_ignoring_zoom() + .iter() + .find(|p| p.pane.pane_id() == pane_id) + { + Some(p) => p.index, + None => anyhow::bail!("invalid pane id {}", pane_id), + }; + + let split_request = SplitRequest { + direction, + target_is_second: true, + top_level: false, + size: Default::default(), + }; + tab.split_and_insert(pane_index, split_request, Arc::clone(&floating_pane))?; Ok(()) } diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 8c3f5957502..9760f81d69b 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -845,7 +845,7 @@ impl Domain for ClientDomain { &self, pane_id: PaneId, split_direction: SplitDirection, - ) -> anyhow::Result<()> { + ) -> anyhow::Result { let inner = self .inner() .ok_or_else(|| anyhow!("domain is not attached"))?; @@ -868,7 +868,7 @@ impl Domain for ClientDomain { self.resync().await?; - Ok(()) + Ok(true) } /// Forward the request to the remote; we need to translate the local ids From 32b6ca306a068438a6016872c7d2d529b368ca38 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 19 Oct 2024 13:50:26 -0700 Subject: [PATCH 61/66] Simplified move_floating_pane_to_split --- mux/src/lib.rs | 19 +------------------ mux/src/tab.rs | 1 - 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 4c389d59d54..34a08c03d4e 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -1291,30 +1291,13 @@ impl Mux { tab.set_floating_pane_visibility(false); - let active_non_floating_pane = tab.iter_panes_ignoring_zoom() - .iter() - .nth(tab.get_active_idx()) - .map(|p| Arc::clone(&p.pane)) - .ok_or_else(|| anyhow::anyhow!("tab does not have a active non floating pane"))?; - - let pane_id = active_non_floating_pane.pane_id(); - - let pane_index = match tab - .iter_panes_ignoring_zoom() - .iter() - .find(|p| p.pane.pane_id() == pane_id) - { - Some(p) => p.index, - None => anyhow::bail!("invalid pane id {}", pane_id), - }; - let split_request = SplitRequest { direction, target_is_second: true, top_level: false, size: Default::default(), }; - tab.split_and_insert(pane_index, split_request, Arc::clone(&floating_pane))?; + tab.split_and_insert(tab.get_active_idx(), split_request, Arc::clone(&floating_pane))?; Ok(()) } diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 046d9e2b0a0..ced014af66b 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -881,7 +881,6 @@ impl TabInner { self.active_floating_pane = root.active_floating_pane_index; self.floating_pane_visible = root.floating_pane_visible; - //TODO: we are in a lock this should be safe? is it needed? self.floating_panes.clear(); for pane_node in root.floating_panes { if let PaneNode::Leaf(entry) = pane_node { From aa195445c66ba0aaf1993fb6ccde25beabbce849 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 19 Oct 2024 14:49:19 -0700 Subject: [PATCH 62/66] Clean up floating pane code --- wezterm-client/src/domain.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/wezterm-client/src/domain.rs b/wezterm-client/src/domain.rs index 9760f81d69b..6059414a7cb 100644 --- a/wezterm-client/src/domain.rs +++ b/wezterm-client/src/domain.rs @@ -525,33 +525,32 @@ impl ClientDomain { } } - pub fn process_remote_tab_title_change(&self, remote_tab_id: TabId, title: String) { + fn get_local_tab(&self, remote_tab_id: TabId) -> Option> { if let Some(inner) = self.inner() { if let Some(local_tab_id) = inner.remote_to_local_tab_id(remote_tab_id) { if let Some(tab) = Mux::get().get_tab(local_tab_id) { - tab.set_title(&title); + return Some(tab) } } } + None + } + + pub fn process_remote_tab_title_change(&self, remote_tab_id: TabId, title: String) { + if let Some(tab) = self.get_local_tab(remote_tab_id) { + tab.set_title(&title); + } } pub fn set_floating_pane_visibility(&self, remote_tab_id: TabId, visible: bool) { - if let Some(inner) = self.inner() { - if let Some(local_tab_id) = inner.remote_to_local_tab_id(remote_tab_id) { - if let Some(tab) = Mux::get().get_tab(local_tab_id) { - tab.set_floating_pane_visibility(visible); - } - } + if let Some(tab) = self.get_local_tab(remote_tab_id) { + tab.set_floating_pane_visibility(visible); } } pub fn set_active_floating_pane(&self, index: usize, remote_tab_id: TabId) { - if let Some(inner) = self.inner() { - if let Some(local_tab_id) = inner.remote_to_local_tab_id(remote_tab_id) { - if let Some(tab) = Mux::get().get_tab(local_tab_id) { - tab.set_active_floating_pane(index); - } - } + if let Some(tab) = self.get_local_tab(remote_tab_id) { + tab.set_active_floating_pane(index); } } From 96916107d1f8ecc08ea4765922f061f29d3b572f Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 19 Oct 2024 15:45:53 -0700 Subject: [PATCH 63/66] Removed ctrl|shift|e default key binding for toggle floating pane. (Not sure if the this should have a default binding so removing it for now) --- wezterm-gui/src/commands.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index a8144ebef98..4c4cbcb9bb2 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -904,7 +904,7 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { brief: "Move a pane to floating pane".into(), doc: "Activates the pane selection UI".into(), - keys: vec![], // FIXME: find a new assignment + keys: vec![], args: &[ArgType::ActivePane], menubar: &["Window"], icon: Some("cod_multiple_windows"), @@ -1652,7 +1652,7 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option CommandDef { brief: "Toggle floating pane".into(), doc: "Toggles the visibility state for the current pane".into(), - keys: vec![(Modifiers::CTRL.union(Modifiers::SHIFT), "e".into())], + keys: vec![], args: &[ArgType::ActivePane], menubar: &["Window"], icon: Some("md_fullscreen"), From 181884cf865f1d085f3bd2a2f46dea818486ce15 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 19 Oct 2024 16:21:39 -0700 Subject: [PATCH 64/66] Update so that floating panes are hidden when a normal pane is activated by index --- wezterm-gui/src/termwindow/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index c574bac8c12..42b20ca08eb 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -2533,9 +2533,7 @@ impl TermWindow { if self.is_floating_pane_active() { match assignment { - ActivatePaneByIndex(..) | SplitPane(..) | - SpawnTab(..) | PaneSelect(..) => { return Ok(PerformAssignmentResult::Handled); }, @@ -2909,6 +2907,10 @@ impl TermWindow { let tab_id = tab.tab_id(); + if tab.floating_pane_is_visible() { + tab.set_floating_pane_visibility(false); + } + if self.tab_state(tab_id).overlay.is_none() { let panes = tab.iter_panes(); if panes.iter().position(|p| p.index == *index).is_some() { From 31afd6cb2c88bd80f18cbfc3f6dd14cf8516cd6e Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sat, 19 Oct 2024 17:37:53 -0700 Subject: [PATCH 65/66] Clarified floating pane mouse handling in comments --- wezterm-gui/src/termwindow/mouseevent.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index cc8fe5c759b..37ce4dd71a0 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -686,10 +686,9 @@ impl super::TermWindow { } } - // Mouse events are not dispatched to the other panes when - // a floating pane is active, this is to prevent users from selecting one of the - // panes that the floating pane is on top of and encountering some weird behavior, ex. - // closing the last non-floating pane while the floating pane is active. + // Non click mouse events are not dispatched to the other panes when a floating + // pane is active. This is to prevent users from triggering unexpected + // behavior on one of the non-floating panes. return; } vec![floating_pane] From 068a3210e8a6ce499196599e4683e950d2be3bdc Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 27 Oct 2024 21:35:26 -0700 Subject: [PATCH 66/66] Add scrollbar support for floating panes --- codec/src/lib.rs | 4 +- mux/src/domain.rs | 4 +- mux/src/lib.rs | 7 ++- mux/src/tab.rs | 20 ++++---- wezterm-gui/src/termwindow/mod.rs | 7 +++ wezterm-gui/src/termwindow/mouseevent.rs | 21 ++++++--- wezterm-gui/src/termwindow/render/borders.rs | 32 +++++++++---- wezterm-gui/src/termwindow/render/pane.rs | 46 +++++++++++-------- wezterm-mux-server-impl/src/dispatch.rs | 1 - wezterm-mux-server-impl/src/sessionhandler.rs | 4 +- 10 files changed, 89 insertions(+), 57 deletions(-) diff --git a/codec/src/lib.rs b/codec/src/lib.rs index e945d7ae9ee..6da0a0b2aac 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -14,9 +14,9 @@ use anyhow::{bail, Context as _, Error}; use config::keyassignment::{PaneDirection, ScrollbackEraseMode}; use mux::client::{ClientId, ClientInfo}; -use mux::pane::{Pane, PaneId}; +use mux::pane::{PaneId}; use mux::renderable::{RenderableDimensions, StableCursorPosition}; -use mux::tab::{PaneNode, SerdeUrl, SplitDirection, SplitRequest, Tab, TabEntry, TabId}; +use mux::tab::{SerdeUrl, SplitDirection, SplitRequest, TabEntry, TabId}; use mux::window::WindowId; use portable_pty::CommandBuilder; use rangeset::*; diff --git a/mux/src/domain.rs b/mux/src/domain.rs index b2690566f84..26d0ad9fdf7 100644 --- a/mux/src/domain.rs +++ b/mux/src/domain.rs @@ -92,8 +92,8 @@ pub trait Domain: Downcast + Send + Sync { async fn move_floating_pane_to_split( &self, - tab: TabId, - direction: SplitDirection, + _tab: TabId, + _direction: SplitDirection, ) -> anyhow::Result { Ok(false) } diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 34a08c03d4e..1973ac1a282 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -48,7 +48,6 @@ mod tmux_pty; pub mod window; use crate::activity::Activity; -use crate::renderable::RenderableDimensions; pub const DEFAULT_WORKSPACE: &str = "default"; @@ -1075,7 +1074,7 @@ impl Mux { pub fn resolve_pane_id(&self, pane_id: PaneId) -> Option<(DomainId, WindowId, TabId)> { let mut ids = None; for tab in self.tabs.read().values() { - if let Some((index, floating_pane)) = tab.get_floating_pane_by_pane_id(pane_id){ + if let Some((_, floating_pane)) = tab.get_floating_pane_by_pane_id(pane_id){ ids = Some((tab.tab_id(), floating_pane.domain_id())); break; } @@ -1192,7 +1191,7 @@ impl Mux { }) } - fn apply_config_to_pane(pane: Arc, term_config: Option>) -> (TerminalSize) { + fn apply_config_to_pane(pane: Arc, term_config: Option>) -> TerminalSize { if let Some(config) = term_config { pane.set_config(config); } @@ -1275,7 +1274,7 @@ impl Mux { pane_id: PaneId, direction: SplitDirection, ) -> anyhow::Result<()> { - let (domain, window_id, tab_id) = self.resolve_domain_from_pane_id(pane_id)?; + let (domain, _window_id, tab_id) = self.resolve_domain_from_pane_id(pane_id)?; if domain.move_floating_pane_to_split(tab_id, direction).await? { return Ok(()) diff --git a/mux/src/tab.rs b/mux/src/tab.rs index ced014af66b..5c2c4af8f80 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -770,7 +770,7 @@ impl Tab { pub fn compute_floating_pane_size( &self, ) -> TerminalSize { - self.inner.lock().compute_floating_pane_size() + self.inner.lock().compute_floating_pane_size2() } pub fn get_active_floating_pane_index(&self) -> usize { @@ -1003,7 +1003,7 @@ impl TabInner { let mut count = 0; let mut cursor = self.pane.take().unwrap().cursor(); - for pane in &self.floating_panes { + for _ in &self.floating_panes { count += 1; } @@ -1098,7 +1098,7 @@ impl TabInner { if self.floating_pane_visible && self.active_floating_pane < self.floating_panes.len() { let floating_pane = &self.floating_panes[self.active_floating_pane]; let root_size = self.size; - let size = self.compute_floating_pane_size(); + let size = self.compute_floating_pane_size(self.size); let cell_height = root_size.pixel_height / root_size.rows; let cell_width = root_size.pixel_width / root_size.cols; @@ -1337,7 +1337,7 @@ impl TabInner { return; } - let floating_pane_size = self.compute_floating_pane_size(); + let floating_pane_size = self.compute_floating_pane_size(size); for floating_pane in &self.floating_panes { floating_pane.resize(floating_pane_size).ok(); } @@ -1932,16 +1932,10 @@ impl TabInner { } let mut floating_pane_indices_to_remove = Vec::new(); - let mut count_before_active = 0; for (i, floating_pane) in self.floating_panes.iter().enumerate() { if floating_pane.is_dead() || f(0, floating_pane) { dead_panes.push(Arc::clone(floating_pane)); - - if i < self.active_floating_pane { - count_before_active += 1; - } - floating_pane_indices_to_remove.push(i); } } @@ -2209,9 +2203,11 @@ impl TabInner { Ok(Arc::clone(&result)) } - fn compute_floating_pane_size(&self) -> TerminalSize { - let root_size = self.size; + fn compute_floating_pane_size2(&self) -> TerminalSize { + self.compute_floating_pane_size(self.size) + } + fn compute_floating_pane_size(&self, root_size: TerminalSize) -> TerminalSize { let cell_width = root_size.pixel_width as f32 / root_size.cols as f32; let cell_height = root_size.pixel_height as f32 / root_size.rows as f32; let h_context = config::DimensionContext { diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 42b20ca08eb..7667f8741d5 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -3530,6 +3530,13 @@ impl TermWindow { self.get_pos_panes_for_tab(&tab) } + fn get_floating_pane_scroll_thumb_width(&self) -> usize { + if !self.show_scroll_bar { + return 0 + } + self.effective_right_padding(&self.config) + } + fn get_floating_pane_to_render(&self) -> Option { let mux = Mux::get(); let tab = match mux.get_active_tab_for_window(self.mux_window_id) { diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index 37ce4dd71a0..f9407a35703 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -9,7 +9,7 @@ use ::window::{ use config::keyassignment::{KeyAssignment, MouseEventTrigger, SpawnTabDomain}; use config::MouseEventAltScreen; use mux::pane::{Pane, WithPaneLines}; -use mux::tab::{SplitDirection, Tab}; +use mux::tab::{SplitDirection}; use mux::Mux; use mux_lua::MuxPane; use std::convert::TryInto; @@ -22,7 +22,6 @@ use termwiz::surface::Line; use wezterm_dynamic::ToDynamic; use wezterm_term::input::{MouseButton, MouseEventKind as TMEK}; use wezterm_term::{ClickPosition, LastMouseClick, StableRowIndex}; -use window::MouseEventKind; impl super::TermWindow { fn resolve_ui_item(&self, event: &MouseEvent) -> Option { @@ -308,7 +307,19 @@ impl super::TermWindow { }; let border = self.get_os_border(); - let y_offset = top_bar_height + border.top.get() as f32; + let (y_offset, max_thumb_height) = if let Some(floating_pane) = self.get_floating_pane_to_render(){ + let (padding_left, padding_top) = self.padding_left_top(); + let top_pixel_y = top_bar_height + padding_top + border.top.get() as f32; + let background_rect = self.compute_background_rect(&floating_pane, + padding_left, padding_top, &border, top_pixel_y); + (background_rect.origin.y, background_rect.height() as usize) + } else { + let y = top_bar_height + border.top.get() as f32; + let max_thumb_height = self.dimensions.pixel_height.saturating_sub( + y as usize + border.bottom.get() + bottom_bar_height as usize, + ); + (y, max_thumb_height) + }; let from_top = start_event.coords.y.saturating_sub(item.y as isize); let effective_thumb_top = event @@ -323,9 +334,7 @@ impl super::TermWindow { effective_thumb_top, &*pane, current_viewport, - self.dimensions.pixel_height.saturating_sub( - y_offset as usize + border.bottom.get() + bottom_bar_height as usize, - ), + max_thumb_height, self.min_scroll_bar_height() as usize, ); self.set_viewport(pane.pane_id(), Some(row), dims); diff --git a/wezterm-gui/src/termwindow/render/borders.rs b/wezterm-gui/src/termwindow/render/borders.rs index 5f73347a411..a80f93301e8 100644 --- a/wezterm-gui/src/termwindow/render/borders.rs +++ b/wezterm-gui/src/termwindow/render/borders.rs @@ -1,7 +1,7 @@ use crate::quad::TripleLayerQuadAllocator; use crate::utilsprites::RenderMetrics; use ::window::ULength; -use config::{ConfigHandle, Dimension, DimensionContext, FloatingPaneBorderConfig, PixelUnit}; +use config::{ConfigHandle, Dimension, DimensionContext, FloatingPaneBorderConfig}; use mux::tab::PositionedPane; use window::parameters::Border; @@ -80,6 +80,21 @@ impl crate::TermWindow { Ok(()) } + pub fn compute_background_rect_with_scrollbar( + &self, + pos: &PositionedPane, + padding_left: f32, + padding_top: f32, + border: &Border, + top_pixel_y: f32, + ) -> euclid::Rect { + let mut base_rect = self.compute_background_rect(pos, padding_left, padding_top, border, top_pixel_y); + if pos.is_floating { + base_rect.size.width = base_rect.size.width + self.get_floating_pane_scroll_thumb_width() as f32; + }; + base_rect + } + pub fn compute_background_rect( &self, pos: &PositionedPane, @@ -87,9 +102,10 @@ impl crate::TermWindow { padding_top: f32, border: &Border, top_pixel_y: f32, - cell_width: f32, - cell_height: f32, ) -> euclid::Rect { + let cell_width = self.render_metrics.cell_size.width as f32; + let cell_height = self.render_metrics.cell_size.height as f32; + let (x, width_delta) = if pos.left == 0 { ( 0., @@ -154,12 +170,10 @@ impl crate::TermWindow { } else { (tab_bar_height, 0.0) }; - let top_pixel_y = top_bar_height + padding_top + os_border.top.get() as f32; - let cell_height = self.render_metrics.cell_size.height as f32; - let cell_width = self.render_metrics.cell_size.width as f32; - let background_rect = self.compute_background_rect(&pos, - padding_left, padding_top, &os_border, top_pixel_y, cell_width, cell_height); + let top_pixel_y = top_bar_height + padding_top + os_border.top.get() as f32; + let background_rect = self.compute_background_rect_with_scrollbar(&pos, + padding_left, padding_top, &os_border, top_pixel_y); let pos_y = background_rect.origin.y - floating_pane_border.top.get() as f32; let pos_x = background_rect.origin.x - floating_pane_border.left.get() as f32; @@ -303,7 +317,7 @@ impl crate::TermWindow { border } - fn get_floating_pane_border(&self) -> Border { + pub fn get_floating_pane_border(&self) -> Border { Self::get_floating_pane_border_impl( &self.dimensions, &self.render_metrics, diff --git a/wezterm-gui/src/termwindow/render/pane.rs b/wezterm-gui/src/termwindow/render/pane.rs index 0619180e1e1..e979fceee55 100644 --- a/wezterm-gui/src/termwindow/render/pane.rs +++ b/wezterm-gui/src/termwindow/render/pane.rs @@ -9,7 +9,7 @@ use crate::termwindow::{ScrollHit, UIItem, UIItemType}; use ::window::bitmaps::TextureRect; use ::window::DeadKeyStatus; use anyhow::Context; -use config::VisualBellTarget; +use config::{VisualBellTarget}; use mux::pane::{PaneId, WithPaneLines}; use mux::renderable::{RenderableDimensions, StableCursorPosition}; use mux::tab::PositionedPane; @@ -105,10 +105,8 @@ impl crate::TermWindow { config.text_background_opacity }); - let cell_width = self.render_metrics.cell_size.width as f32; - let cell_height = self.render_metrics.cell_size.height as f32; - let background_rect = self.compute_background_rect(&pos, padding_top, - padding_left, &border, top_pixel_y, cell_width, cell_height); + let background_rect = self.compute_background_rect_with_scrollbar( + &pos, padding_top, padding_left, &border, top_pixel_y); if self.window_background.is_empty() { // Per-pane, palette-specified background @@ -185,51 +183,61 @@ impl crate::TermWindow { // do a per-pane scrollbar. That will require more extensive // changes to ScrollHit, mouse positioning, PositionedPane // and tab size calculation. - if pos.is_active && self.show_scroll_bar { - let thumb_y_offset = top_bar_height as usize + border.top.get(); + if pos.is_active && self.show_scroll_bar && (!self.is_floating_pane_active() || pos.is_floating) { + let padding = self.effective_right_padding(&config) as f32; + let (thumb_y_offset, temp_bottom, thumb_x) = if self.is_floating_pane_active() { + let pos_y = background_rect.origin.y; + let pixel_height = background_rect.size.height; + let x = background_rect.width()+ background_rect.origin.x - padding; + (pos_y as usize, pos_y as usize + pixel_height as usize, x as usize) + } else { + let win_y_offset = top_bar_height as usize; + let win_bottom = self.dimensions.pixel_height.saturating_sub( + win_y_offset + border.bottom.get() + bottom_bar_height as usize, + ); + let x = self.dimensions.pixel_width - padding as usize - border.right.get(); + (win_y_offset + border.top.get(), win_bottom, x) + }; let min_height = self.min_scroll_bar_height(); let info = ScrollHit::thumb( &*pos.pane, current_viewport, - self.dimensions.pixel_height.saturating_sub( - thumb_y_offset + border.bottom.get() + bottom_bar_height as usize, - ), + temp_bottom - thumb_y_offset, min_height as usize, ); + + let scroll_height = info.height; + let abs_thumb_top = thumb_y_offset + info.top; - let thumb_size = info.height; + let thumb_size = scroll_height; let color = palette.scrollbar_thumb.to_linear(); // Adjust the scrollbar thumb position let config = &self.config; let padding = self.effective_right_padding(&config) as f32; - let thumb_x = self.dimensions.pixel_width - padding as usize - border.right.get(); - // Register the scroll bar location self.ui_items.push(UIItem { x: thumb_x, width: padding as usize, y: thumb_y_offset, - height: info.top, + height: scroll_height, item_type: UIItemType::AboveScrollThumb, }); self.ui_items.push(UIItem { x: thumb_x, width: padding as usize, y: abs_thumb_top, - height: thumb_size, + height: scroll_height, item_type: UIItemType::ScrollThumb, }); self.ui_items.push(UIItem { x: thumb_x, width: padding as usize, y: abs_thumb_top + thumb_size, - height: self - .dimensions - .pixel_height + height: scroll_height .saturating_sub(abs_thumb_top + thumb_size), item_type: UIItemType::BelowScrollThumb, }); @@ -241,7 +249,7 @@ impl crate::TermWindow { thumb_x as f32, abs_thumb_top as f32, padding, - thumb_size as f32, + scroll_height as f32, ), color, ) diff --git a/wezterm-mux-server-impl/src/dispatch.rs b/wezterm-mux-server-impl/src/dispatch.rs index 471103bce8f..c4339c4171a 100644 --- a/wezterm-mux-server-impl/src/dispatch.rs +++ b/wezterm-mux-server-impl/src/dispatch.rs @@ -3,7 +3,6 @@ use anyhow::Context; use async_ossl::AsyncSslStream; use codec::{DecodedPdu, Pdu}; use futures::FutureExt; -use log::log; use mux::{Mux, MuxNotification}; use smol::prelude::*; use smol::Async; diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index fabef55e9ff..52569fd9169 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -3,10 +3,10 @@ use anyhow::{anyhow, Context}; use codec::*; use config::TermConfig; use mux::client::ClientId; -use mux::domain::{DomainId, SplitSource}; +use mux::domain::{SplitSource}; use mux::pane::{CachePolicy, Pane, PaneId}; use mux::renderable::{RenderableDimensions, StableCursorPosition}; -use mux::tab::{PaneNode, SplitDirection, TabId}; +use mux::tab::{SplitDirection, TabId}; use mux::{Mux, MuxNotification}; use promise::spawn::spawn_into_main_thread; use std::collections::HashMap;