mirror of
https://github.com/wez/wezterm.git
synced 2024-08-16 09:40:34 +03:00
Fix interaction between pane swapping / rotating and client domains.
This should address #4200. Currently pane swapping and rotation only works correctly on local domains since none of the state is propagated to the remote mux server. This patch adds a couple of RPCs for rotating panes and for swapping an active pane with the pane at a given index. Additionally, it renames the corresponding methods on the `mux::tab` module, prefixing them with `local_`, adds `remote_` versions of them to Domains and adds a convenience method on `mux`, mirroring the pattern used for `move_pane_to_new_tab`, which dispatches to the relevant method. This incidentally fixes a typo in the Lua API which was previously always rotating panes in a single direction.
This commit is contained in:
parent
b8f94c474c
commit
0f365952a1
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3214,6 +3214,7 @@ dependencies = [
|
||||
"mux",
|
||||
"parking_lot 0.12.2",
|
||||
"portable-pty",
|
||||
"promise",
|
||||
"smol",
|
||||
"termwiz",
|
||||
"termwiz-funcs",
|
||||
|
@ -12,7 +12,7 @@
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::range_plus_one))]
|
||||
|
||||
use anyhow::{bail, Context as _, Error};
|
||||
use config::keyassignment::{PaneDirection, ScrollbackEraseMode};
|
||||
use config::keyassignment::{PaneDirection, RotationDirection, ScrollbackEraseMode};
|
||||
use mux::client::{ClientId, ClientInfo};
|
||||
use mux::pane::PaneId;
|
||||
use mux::renderable::{RenderableDimensions, StableCursorPosition};
|
||||
@ -493,7 +493,7 @@ pdu! {
|
||||
GetPaneRenderableDimensions: 51,
|
||||
GetPaneRenderableDimensionsResponse: 52,
|
||||
PaneFocused: 53,
|
||||
TabResized: 54,
|
||||
TabReflowed: 54,
|
||||
TabAddedToWindow: 55,
|
||||
TabTitleChanged: 56,
|
||||
WindowTitleChanged: 57,
|
||||
@ -502,6 +502,8 @@ pdu! {
|
||||
GetPaneDirection: 60,
|
||||
GetPaneDirectionResponse: 61,
|
||||
AdjustPaneSize: 62,
|
||||
RotatePanes: 63,
|
||||
SwapActivePaneWithIndex: 64,
|
||||
}
|
||||
|
||||
impl Pdu {
|
||||
@ -803,7 +805,7 @@ pub struct TabAddedToWindow {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct TabResized {
|
||||
pub struct TabReflowed {
|
||||
pub tab_id: TabId,
|
||||
}
|
||||
|
||||
@ -887,6 +889,19 @@ pub struct ActivatePaneDirection {
|
||||
pub direction: PaneDirection,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct RotatePanes {
|
||||
pub pane_id: PaneId,
|
||||
pub direction: RotationDirection,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct SwapActivePaneWithIndex {
|
||||
pub active_pane_id: PaneId,
|
||||
pub with_pane_index: usize,
|
||||
pub keep_focus: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct GetPaneRenderChanges {
|
||||
pub pane_id: PaneId,
|
||||
|
@ -641,7 +641,7 @@ impl Default for SplitSize {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, FromDynamic, ToDynamic)]
|
||||
pub enum RotationDirection {
|
||||
Clockwise,
|
||||
CounterClockwise,
|
||||
|
@ -16,6 +16,7 @@ log = "0.4"
|
||||
luahelper = { path = "../../luahelper" }
|
||||
parking_lot = "0.12"
|
||||
portable-pty = { path = "../../pty" }
|
||||
promise = { path = "../../promise" }
|
||||
smol = "2.0"
|
||||
termwiz = { path = "../../termwiz" }
|
||||
termwiz-funcs = { path = "../termwiz-funcs" }
|
||||
|
@ -1,4 +1,4 @@
|
||||
use config::keyassignment::PaneDirection;
|
||||
use config::keyassignment::{PaneDirection, RotationDirection};
|
||||
|
||||
use super::*;
|
||||
use luahelper::mlua::Value;
|
||||
@ -113,14 +113,34 @@ impl UserData for MuxTab {
|
||||
methods.add_method("rotate_counter_clockwise", |_, this, _: ()| {
|
||||
let mux = get_mux()?;
|
||||
let tab = this.resolve(&mux)?;
|
||||
tab.rotate_counter_clockwise();
|
||||
|
||||
let tab_id = tab.tab_id();
|
||||
let direction = RotationDirection::CounterClockwise;
|
||||
promise::spawn::spawn(async move {
|
||||
let mux = Mux::get();
|
||||
if let Err(err) = mux.rotate_panes(tab_id, direction).await {
|
||||
log::error!("Unable to rotate panes: {:#}", err);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
methods.add_method("rotate_clockwise", |_, this, _: ()| {
|
||||
let mux = get_mux()?;
|
||||
let tab = this.resolve(&mux)?;
|
||||
tab.rotate_counter_clockwise();
|
||||
|
||||
let tab_id = tab.tab_id();
|
||||
let direction = RotationDirection::CounterClockwise;
|
||||
promise::spawn::spawn(async move {
|
||||
let mux = Mux::get();
|
||||
if let Err(err) = mux.rotate_panes(tab_id, direction).await {
|
||||
log::error!("Unable to rotate panes: {:#}", err);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
|
@ -12,7 +12,7 @@ use crate::window::WindowId;
|
||||
use crate::Mux;
|
||||
use anyhow::{bail, Context, Error};
|
||||
use async_trait::async_trait;
|
||||
use config::keyassignment::{SpawnCommand, SpawnTabDomain};
|
||||
use config::keyassignment::{RotationDirection, SpawnCommand, SpawnTabDomain};
|
||||
use config::{configuration, ExecDomain, SerialDomain, ValueOrFunc, WslDomain};
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use parking_lot::Mutex;
|
||||
@ -142,7 +142,7 @@ pub trait Domain: Downcast + Send + Sync {
|
||||
/// is being moved to give the domain a chance to handle the movement.
|
||||
/// If this method returns Ok(None), then the mux will handle the
|
||||
/// movement itself by mutating its local Tabs and Windows.
|
||||
async fn move_pane_to_new_tab(
|
||||
async fn remote_move_pane_to_new_tab(
|
||||
&self,
|
||||
_pane_id: PaneId,
|
||||
_window_id: Option<WindowId>,
|
||||
@ -151,6 +151,31 @@ pub trait Domain: Downcast + Send + Sync {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// The mux will call this method on the domain of the panes that are being
|
||||
/// rotated to give the domain a chance to handle the movement. If this
|
||||
/// method returns Ok(false), then the mux will handle the movement itself
|
||||
/// by mutating its local Tabs and Windows.
|
||||
async fn remote_rotate_panes(
|
||||
&self,
|
||||
_pane_id: PaneId,
|
||||
_direction: RotationDirection,
|
||||
) -> anyhow::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// The mux will call this method on the domain of the pane that is being
|
||||
/// swapped to give the domain a chance to handle the movement. If this
|
||||
/// method returns Ok(false), then the mux will handle the movement itself
|
||||
/// by mutating its local Tabs and Windows.
|
||||
async fn remote_swap_active_pane_with_index(
|
||||
&self,
|
||||
_active_pane_id: PaneId,
|
||||
_with_pane_index: usize,
|
||||
_keep_focus: bool,
|
||||
) -> anyhow::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
@ -4,7 +4,7 @@ use crate::ssh_agent::AgentProxy;
|
||||
use crate::tab::{SplitRequest, Tab, TabId};
|
||||
use crate::window::{Window, WindowId};
|
||||
use anyhow::{anyhow, Context, Error};
|
||||
use config::keyassignment::SpawnTabDomain;
|
||||
use config::keyassignment::{RotationDirection, SpawnTabDomain};
|
||||
use config::{configuration, ExitBehavior, GuiPosition};
|
||||
use domain::{Domain, DomainId, DomainState, SplitSource};
|
||||
use filedescriptor::{poll, pollfd, socketpair, AsRawSocketDescriptor, FileDescriptor, POLLIN};
|
||||
@ -80,7 +80,7 @@ pub enum MuxNotification {
|
||||
window_id: WindowId,
|
||||
},
|
||||
PaneFocused(PaneId),
|
||||
TabResized(TabId),
|
||||
TabReflowed(TabId),
|
||||
TabTitleChanged {
|
||||
tab_id: TabId,
|
||||
title: String,
|
||||
@ -1245,7 +1245,7 @@ impl Mux {
|
||||
.ok_or_else(|| anyhow::anyhow!("domain {domain_id} of pane {pane_id} not found"))?;
|
||||
|
||||
if let Some((tab, window_id)) = domain
|
||||
.move_pane_to_new_tab(pane_id, window_id, workspace_for_new_window.clone())
|
||||
.remote_move_pane_to_new_tab(pane_id, window_id, workspace_for_new_window.clone())
|
||||
.await?
|
||||
{
|
||||
return Ok((tab, window_id));
|
||||
@ -1289,6 +1289,71 @@ impl Mux {
|
||||
Ok((tab, window_id))
|
||||
}
|
||||
|
||||
pub async fn rotate_panes(
|
||||
&self,
|
||||
tab_id: TabId,
|
||||
direction: RotationDirection,
|
||||
) -> anyhow::Result<()> {
|
||||
let tab = match self.get_tab(tab_id) {
|
||||
Some(tab) => tab,
|
||||
None => anyhow::bail!("Invalid tab id {}", tab_id),
|
||||
};
|
||||
|
||||
// This makes the assumption that a tab contains only panes from a single local domain,
|
||||
// though that is also an assumption that ClientDomain makes when syncing tab panes.
|
||||
let tab_panes = tab.iter_panes();
|
||||
let pos_pane = match tab_panes.iter().nth(0) {
|
||||
Some(pos_pane) => pos_pane,
|
||||
None => anyhow::bail!("Tab contains no panes: {}", tab_id),
|
||||
};
|
||||
|
||||
let pane_id = pos_pane.pane.pane_id();
|
||||
let domain_id = pos_pane.pane.domain_id();
|
||||
|
||||
let domain = self
|
||||
.get_domain(domain_id)
|
||||
.ok_or_else(|| anyhow::anyhow!("domain {domain_id} of tab {tab_id} not found"))?;
|
||||
|
||||
if domain.remote_rotate_panes(pane_id, direction).await? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match direction {
|
||||
RotationDirection::Clockwise => tab.local_rotate_clockwise(),
|
||||
RotationDirection::CounterClockwise => tab.local_rotate_counter_clockwise(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn swap_active_pane_with_index(
|
||||
&self,
|
||||
active_pane_id: PaneId,
|
||||
with_pane_index: usize,
|
||||
keep_focus: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let (domain_id, _window_id, tab_id) = self
|
||||
.resolve_pane_id(active_pane_id)
|
||||
.ok_or_else(|| anyhow::anyhow!("pane {} not found", active_pane_id))?;
|
||||
|
||||
let domain = self.get_domain(domain_id).ok_or_else(|| {
|
||||
anyhow::anyhow!("domain {domain_id} of pane {active_pane_id} not found")
|
||||
})?;
|
||||
|
||||
if domain
|
||||
.remote_swap_active_pane_with_index(active_pane_id, with_pane_index, keep_focus)
|
||||
.await?
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let tab = match self.get_tab(tab_id) {
|
||||
Some(tab) => tab,
|
||||
None => anyhow::bail!("Invalid tab id {}", tab_id),
|
||||
};
|
||||
|
||||
tab.local_swap_active_with_index(with_pane_index, keep_focus);
|
||||
Ok(())
|
||||
}
|
||||
pub async fn spawn_tab_or_window(
|
||||
&self,
|
||||
window_id: Option<WindowId>,
|
||||
|
@ -581,12 +581,12 @@ impl Tab {
|
||||
self.inner.lock().iter_panes_ignoring_zoom()
|
||||
}
|
||||
|
||||
pub fn rotate_counter_clockwise(&self) {
|
||||
self.inner.lock().rotate_counter_clockwise()
|
||||
pub fn local_rotate_counter_clockwise(&self) {
|
||||
self.inner.lock().local_rotate_counter_clockwise()
|
||||
}
|
||||
|
||||
pub fn rotate_clockwise(&self) {
|
||||
self.inner.lock().rotate_clockwise()
|
||||
pub fn local_rotate_clockwise(&self) {
|
||||
self.inner.lock().local_rotate_clockwise()
|
||||
}
|
||||
|
||||
pub fn iter_splits(&self) -> Vec<PositionedSplit> {
|
||||
@ -714,10 +714,10 @@ impl Tab {
|
||||
}
|
||||
|
||||
/// Swap the active pane with the specified pane_index
|
||||
pub fn swap_active_with_index(&self, pane_index: usize, keep_focus: bool) -> Option<()> {
|
||||
pub fn local_swap_active_with_index(&self, pane_index: usize, keep_focus: bool) -> Option<()> {
|
||||
self.inner
|
||||
.lock()
|
||||
.swap_active_with_index(pane_index, keep_focus)
|
||||
.local_swap_active_with_index(pane_index, keep_focus)
|
||||
}
|
||||
|
||||
/// Computes the size of the pane that would result if the specified
|
||||
@ -908,7 +908,7 @@ impl TabInner {
|
||||
self.zoomed.replace(pane);
|
||||
}
|
||||
}
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabResized(self.id)));
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabReflowed(self.id)));
|
||||
}
|
||||
|
||||
fn contains_pane(&self, pane: PaneId) -> bool {
|
||||
@ -937,7 +937,7 @@ impl TabInner {
|
||||
self.iter_panes_impl(false)
|
||||
}
|
||||
|
||||
fn rotate_counter_clockwise(&mut self) {
|
||||
fn local_rotate_counter_clockwise(&mut self) {
|
||||
let panes = self.iter_panes_ignoring_zoom();
|
||||
if panes.is_empty() {
|
||||
// Shouldn't happen, but we check for this here so that the
|
||||
@ -966,9 +966,10 @@ impl TabInner {
|
||||
}
|
||||
}
|
||||
}
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabReflowed(self.id)));
|
||||
}
|
||||
|
||||
fn rotate_clockwise(&mut self) {
|
||||
fn local_rotate_clockwise(&mut self) {
|
||||
let panes = self.iter_panes_ignoring_zoom();
|
||||
if panes.is_empty() {
|
||||
// Shouldn't happen, but we check for this here so that the
|
||||
@ -997,7 +998,7 @@ impl TabInner {
|
||||
}
|
||||
}
|
||||
}
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabResized(self.id)));
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabReflowed(self.id)));
|
||||
}
|
||||
|
||||
fn iter_panes_impl(&mut self, respect_zoom_state: bool) -> Vec<PositionedPane> {
|
||||
@ -1179,7 +1180,7 @@ impl TabInner {
|
||||
apply_sizes_from_splits(self.pane.as_mut().unwrap(), &size);
|
||||
}
|
||||
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabResized(self.id)));
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabReflowed(self.id)));
|
||||
}
|
||||
|
||||
fn apply_pane_size(&mut self, pane_size: TerminalSize, cursor: &mut Cursor) {
|
||||
@ -1255,7 +1256,7 @@ impl TabInner {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabResized(self.id)));
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabReflowed(self.id)));
|
||||
}
|
||||
|
||||
fn resize_split_by(&mut self, split_index: usize, delta: isize) {
|
||||
@ -1288,7 +1289,7 @@ impl TabInner {
|
||||
// Now cursor is looking at the split
|
||||
self.adjust_node_at_cursor(&mut cursor, delta);
|
||||
self.cascade_size_from_cursor(cursor);
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabResized(self.id)));
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabReflowed(self.id)));
|
||||
}
|
||||
|
||||
fn adjust_node_at_cursor(&mut self, cursor: &mut Cursor, delta: isize) {
|
||||
@ -1371,7 +1372,7 @@ impl TabInner {
|
||||
}
|
||||
}
|
||||
}
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabResized(self.id)));
|
||||
Mux::try_get().map(|mux| mux.notify(MuxNotification::TabReflowed(self.id)));
|
||||
}
|
||||
|
||||
fn adjust_pane_size(&mut self, direction: PaneDirection, amount: usize) {
|
||||
@ -1807,7 +1808,7 @@ impl TabInner {
|
||||
cell_dimensions(&self.size)
|
||||
}
|
||||
|
||||
fn swap_active_with_index(&mut self, pane_index: usize, keep_focus: bool) -> Option<()> {
|
||||
fn local_swap_active_with_index(&mut self, pane_index: usize, keep_focus: bool) -> Option<()> {
|
||||
let active_idx = self.get_active_idx();
|
||||
let mut pane = self.get_active_pane()?;
|
||||
log::trace!(
|
||||
|
@ -297,7 +297,7 @@ fn process_unilateral(
|
||||
.detach();
|
||||
return Ok(());
|
||||
}
|
||||
Pdu::TabResized(_) | Pdu::TabAddedToWindow(_) => {
|
||||
Pdu::TabReflowed(_) | Pdu::TabAddedToWindow(_) => {
|
||||
log::trace!("resync due to {:?}", decoded.pdu);
|
||||
promise::spawn::spawn_into_main_thread(async move {
|
||||
let mux = Mux::try_get().ok_or_else(|| anyhow!("no more mux"))?;
|
||||
@ -1354,6 +1354,12 @@ impl Client {
|
||||
rpc!(resize, Resize, UnitResponse);
|
||||
rpc!(set_zoomed, SetPaneZoomed, UnitResponse);
|
||||
rpc!(activate_pane_direction, ActivatePaneDirection, UnitResponse);
|
||||
rpc!(
|
||||
swap_active_pane_with_index,
|
||||
SwapActivePaneWithIndex,
|
||||
UnitResponse
|
||||
);
|
||||
rpc!(rotate_panes, RotatePanes, UnitResponse);
|
||||
rpc!(
|
||||
get_pane_render_changes,
|
||||
GetPaneRenderChanges,
|
||||
|
@ -3,7 +3,7 @@ use crate::pane::ClientPane;
|
||||
use anyhow::{anyhow, bail};
|
||||
use async_trait::async_trait;
|
||||
use codec::{ListPanesResponse, SpawnV2, SplitPane};
|
||||
use config::keyassignment::SpawnTabDomain;
|
||||
use config::keyassignment::{RotationDirection, SpawnTabDomain};
|
||||
use config::{SshDomain, TlsDomainClient, UnixDomain};
|
||||
use mux::connui::{ConnectionUI, ConnectionUIParams};
|
||||
use mux::domain::{alloc_domain_id, Domain, DomainId, DomainState, SplitSource};
|
||||
@ -763,7 +763,7 @@ impl Domain 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_pane_to_new_tab(
|
||||
async fn remote_move_pane_to_new_tab(
|
||||
&self,
|
||||
pane_id: PaneId,
|
||||
window_id: Option<WindowId>,
|
||||
@ -814,6 +814,64 @@ impl Domain for ClientDomain {
|
||||
Ok(Some((tab, local_win_id)))
|
||||
}
|
||||
|
||||
async fn remote_rotate_panes(
|
||||
&self,
|
||||
pane_id: PaneId,
|
||||
direction: RotationDirection,
|
||||
) -> anyhow::Result<bool> {
|
||||
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::<ClientPane>()
|
||||
.ok_or_else(|| anyhow!("pane_id {} is not a ClientPane", pane_id))?;
|
||||
|
||||
inner
|
||||
.client
|
||||
.rotate_panes(codec::RotatePanes {
|
||||
pane_id: pane.remote_pane_id,
|
||||
direction,
|
||||
})
|
||||
.await?;
|
||||
|
||||
self.resync().await?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn remote_swap_active_pane_with_index(
|
||||
&self,
|
||||
active_pane_id: PaneId,
|
||||
with_pane_index: usize,
|
||||
keep_focus: bool,
|
||||
) -> anyhow::Result<bool> {
|
||||
let inner = self
|
||||
.inner()
|
||||
.ok_or_else(|| anyhow!("domain is not attached"))?;
|
||||
|
||||
let local_pane = Mux::get()
|
||||
.get_pane(active_pane_id)
|
||||
.ok_or_else(|| anyhow!("pane_id {} is invalid", active_pane_id))?;
|
||||
let pane = local_pane
|
||||
.downcast_ref::<ClientPane>()
|
||||
.ok_or_else(|| anyhow!("pane_id {} is not a ClientPane", active_pane_id))?;
|
||||
|
||||
inner
|
||||
.client
|
||||
.swap_active_pane_with_index(codec::SwapActivePaneWithIndex {
|
||||
active_pane_id: pane.remote_pane_id,
|
||||
with_pane_index,
|
||||
keep_focus,
|
||||
})
|
||||
.await?;
|
||||
|
||||
self.resync().await?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn spawn(
|
||||
&self,
|
||||
size: TerminalSize,
|
||||
|
@ -88,7 +88,7 @@ impl GuiFrontEnd {
|
||||
}
|
||||
MuxNotification::TabTitleChanged { .. } => {}
|
||||
MuxNotification::WindowTitleChanged { .. } => {}
|
||||
MuxNotification::TabResized(_) => {}
|
||||
MuxNotification::TabReflowed(_) => {}
|
||||
MuxNotification::TabAddedToWindow { .. } => {}
|
||||
MuxNotification::PaneRemoved(_) => {}
|
||||
MuxNotification::WindowInvalidated(_) => {}
|
||||
|
@ -30,8 +30,8 @@ use ::wezterm_term::input::{ClickPosition, MouseButton as TMB};
|
||||
use ::window::*;
|
||||
use anyhow::{anyhow, ensure, Context};
|
||||
use config::keyassignment::{
|
||||
KeyAssignment, PaneDirection, Pattern, PromptInputLine, QuickSelectArguments,
|
||||
RotationDirection, SpawnCommand, SplitSize,
|
||||
KeyAssignment, PaneDirection, Pattern, PromptInputLine, QuickSelectArguments, SpawnCommand,
|
||||
SplitSize,
|
||||
};
|
||||
use config::window::WindowLevel;
|
||||
use config::{
|
||||
@ -1292,7 +1292,7 @@ impl TermWindow {
|
||||
// Also handled by clientpane
|
||||
self.update_title_post_status();
|
||||
}
|
||||
MuxNotification::TabResized(_) => {
|
||||
MuxNotification::TabReflowed(_) => {
|
||||
// Also handled by wezterm-client
|
||||
self.update_title_post_status();
|
||||
}
|
||||
@ -1489,7 +1489,7 @@ impl TermWindow {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
MuxNotification::TabResized(tab_id)
|
||||
MuxNotification::TabReflowed(tab_id)
|
||||
| MuxNotification::TabTitleChanged { tab_id, .. } => {
|
||||
let mux = Mux::get();
|
||||
if mux.window_containing_tab(tab_id) == Some(mux_window_id) {
|
||||
@ -3013,10 +3013,15 @@ impl TermWindow {
|
||||
Some(tab) => tab,
|
||||
None => return Ok(PerformAssignmentResult::Handled),
|
||||
};
|
||||
match direction {
|
||||
RotationDirection::Clockwise => tab.rotate_clockwise(),
|
||||
RotationDirection::CounterClockwise => tab.rotate_counter_clockwise(),
|
||||
}
|
||||
let tab_id = tab.tab_id();
|
||||
let direction = *direction;
|
||||
promise::spawn::spawn(async move {
|
||||
let mux = Mux::get();
|
||||
if let Err(err) = mux.rotate_panes(tab_id, direction).await {
|
||||
log::error!("Unable to rotate panes: {:#}", err);
|
||||
}
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
SplitPane(split) => {
|
||||
log::trace!("SplitPane {:?}", split);
|
||||
|
@ -180,17 +180,26 @@ impl PaneSelector {
|
||||
}
|
||||
}
|
||||
PaneSelectMode::SwapWithActiveKeepFocus | PaneSelectMode::SwapWithActive => {
|
||||
tab.swap_active_with_index(
|
||||
pane_index,
|
||||
self.mode == PaneSelectMode::SwapWithActiveKeepFocus,
|
||||
);
|
||||
if let Some(active_pane) = tab.get_active_pane() {
|
||||
let active_pane_id = active_pane.pane_id();
|
||||
let keep_focus = self.mode == PaneSelectMode::SwapWithActiveKeepFocus;
|
||||
promise::spawn::spawn(async move {
|
||||
if let Err(err) = mux
|
||||
.swap_active_pane_with_index(active_pane_id, pane_index, keep_focus)
|
||||
.await
|
||||
{
|
||||
log::error!("failed to swap_active_pane_with_index: {err:#}");
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
PaneSelectMode::MoveToNewWindow => {
|
||||
if let Some(pos) = panes.iter().find(|p| p.index == pane_index) {
|
||||
let pane_id = pos.pane.pane_id();
|
||||
promise::spawn::spawn(async move {
|
||||
if let Err(err) = mux.move_pane_to_new_tab(pane_id, None, None).await {
|
||||
log::error!("failed to move_pane_to_new_tab: {err:#}");
|
||||
log::error!("failed to move_pane_to_new_window: {err:#}");
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
@ -172,8 +172,8 @@ where
|
||||
.await?;
|
||||
stream.flush().await.context("flushing PDU to client")?;
|
||||
}
|
||||
Ok(Item::Notif(MuxNotification::TabResized(tab_id))) => {
|
||||
Pdu::TabResized(codec::TabResized { tab_id })
|
||||
Ok(Item::Notif(MuxNotification::TabReflowed(tab_id))) => {
|
||||
Pdu::TabReflowed(codec::TabReflowed { tab_id })
|
||||
.encode_async(&mut stream, 0)
|
||||
.await?;
|
||||
stream.flush().await.context("flushing PDU to client")?;
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::PKI;
|
||||
use anyhow::{anyhow, Context};
|
||||
use codec::*;
|
||||
use config::keyassignment::RotationDirection;
|
||||
use config::TermConfig;
|
||||
use mux::client::ClientId;
|
||||
use mux::domain::SplitSource;
|
||||
@ -629,6 +630,57 @@ impl SessionHandler {
|
||||
.detach();
|
||||
}
|
||||
|
||||
Pdu::SwapActivePaneWithIndex(SwapActivePaneWithIndex {
|
||||
active_pane_id,
|
||||
with_pane_index,
|
||||
keep_focus,
|
||||
}) => {
|
||||
spawn_into_main_thread(async move {
|
||||
catch(
|
||||
move || {
|
||||
let mux = Mux::get();
|
||||
let (_domain_id, _window_id, tab_id) = mux
|
||||
.resolve_pane_id(active_pane_id)
|
||||
.ok_or_else(|| anyhow!("no such pane {}", active_pane_id))?;
|
||||
let tab = mux
|
||||
.get_tab(tab_id)
|
||||
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?;
|
||||
tab.local_swap_active_with_index(with_pane_index, keep_focus);
|
||||
Ok(Pdu::UnitResponse(UnitResponse {}))
|
||||
},
|
||||
send_response,
|
||||
)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
Pdu::RotatePanes(RotatePanes { pane_id, direction }) => {
|
||||
spawn_into_main_thread(async move {
|
||||
catch(
|
||||
move || {
|
||||
let mux = Mux::get();
|
||||
let (_domain_id, _window_id, tab_id) = mux
|
||||
.resolve_pane_id(pane_id)
|
||||
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
||||
let tab = mux
|
||||
.get_tab(tab_id)
|
||||
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?;
|
||||
|
||||
match direction {
|
||||
RotationDirection::Clockwise => tab.local_rotate_clockwise(),
|
||||
RotationDirection::CounterClockwise => {
|
||||
tab.local_rotate_counter_clockwise()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Pdu::UnitResponse(UnitResponse {}))
|
||||
},
|
||||
send_response,
|
||||
)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
Pdu::Resize(Resize {
|
||||
containing_tab_id,
|
||||
pane_id,
|
||||
@ -1004,7 +1056,7 @@ impl SessionHandler {
|
||||
| Pdu::GetClientListResponse { .. }
|
||||
| Pdu::PaneRemoved { .. }
|
||||
| Pdu::PaneFocused { .. }
|
||||
| Pdu::TabResized { .. }
|
||||
| Pdu::TabReflowed { .. }
|
||||
| Pdu::GetImageCellResponse { .. }
|
||||
| Pdu::MovePaneToNewTabResponse { .. }
|
||||
| Pdu::TabAddedToWindow { .. }
|
||||
|
Loading…
Reference in New Issue
Block a user