zellij/zellij-server/src/tab/layout_applier.rs
Aram Drevekenin b3b0ddbab8
fix(layouts): do not relayout twice on auto_layout (#2202)
* fix(layouts): do not relayout twice on auto_layout

* style(fmt): rustfmt
2023-02-27 19:15:45 +01:00

723 lines
30 KiB
Rust

use zellij_utils::errors::prelude::*;
use crate::resize_pty;
use crate::tab::{get_next_terminal_position, HoldForCommand, Pane};
use crate::{
os_input_output::ServerOsApi,
panes::sixel::SixelImageStore,
panes::{FloatingPanes, TiledPanes},
panes::{LinkHandler, PaneId, PluginPane, TerminalPane},
plugins::PluginInstruction,
pty::PtyInstruction,
thread_bus::ThreadSenders,
ClientId,
};
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
use std::rc::Rc;
use zellij_utils::{
data::{Palette, Style},
input::layout::{FloatingPaneLayout, Run, RunPluginLocation, TiledPaneLayout},
pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport},
};
pub struct LayoutApplier<'a> {
viewport: Rc<RefCell<Viewport>>, // includes all non-UI panes
senders: ThreadSenders,
sixel_image_store: Rc<RefCell<SixelImageStore>>,
link_handler: Rc<RefCell<LinkHandler>>,
terminal_emulator_colors: Rc<RefCell<Palette>>,
terminal_emulator_color_codes: Rc<RefCell<HashMap<usize, String>>>,
character_cell_size: Rc<RefCell<Option<SizeInPixels>>>,
style: Style,
display_area: Rc<RefCell<Size>>, // includes all panes (including eg. the status bar and tab bar in the default layout)
tiled_panes: &'a mut TiledPanes,
floating_panes: &'a mut FloatingPanes,
draw_pane_frames: bool,
focus_pane_id: &'a mut Option<PaneId>,
os_api: Box<dyn ServerOsApi>,
}
impl<'a> LayoutApplier<'a> {
pub fn new(
viewport: &Rc<RefCell<Viewport>>,
senders: &ThreadSenders,
sixel_image_store: &Rc<RefCell<SixelImageStore>>,
link_handler: &Rc<RefCell<LinkHandler>>,
terminal_emulator_colors: &Rc<RefCell<Palette>>,
terminal_emulator_color_codes: &Rc<RefCell<HashMap<usize, String>>>,
character_cell_size: &Rc<RefCell<Option<SizeInPixels>>>,
style: &Style,
display_area: &Rc<RefCell<Size>>, // includes all panes (including eg. the status bar and tab bar in the default layout)
tiled_panes: &'a mut TiledPanes,
floating_panes: &'a mut FloatingPanes,
draw_pane_frames: bool,
focus_pane_id: &'a mut Option<PaneId>,
os_api: &Box<dyn ServerOsApi>,
) -> Self {
let viewport = viewport.clone();
let senders = senders.clone();
let sixel_image_store = sixel_image_store.clone();
let link_handler = link_handler.clone();
let terminal_emulator_colors = terminal_emulator_colors.clone();
let terminal_emulator_color_codes = terminal_emulator_color_codes.clone();
let character_cell_size = character_cell_size.clone();
let style = style.clone();
let display_area = display_area.clone();
let os_api = os_api.clone();
LayoutApplier {
viewport,
senders,
sixel_image_store,
link_handler,
terminal_emulator_colors,
terminal_emulator_color_codes,
character_cell_size,
style,
display_area,
tiled_panes,
floating_panes,
draw_pane_frames,
focus_pane_id,
os_api,
}
}
pub fn apply_layout(
&mut self,
layout: TiledPaneLayout,
floating_panes_layout: Vec<FloatingPaneLayout>,
new_terminal_ids: Vec<(u32, HoldForCommand)>,
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
mut new_plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
client_id: ClientId,
) -> Result<bool> {
// true => layout has floating panes
let layout_name = layout.name.clone();
self.apply_tiled_panes_layout(layout, new_terminal_ids, &mut new_plugin_ids, client_id)?;
let layout_has_floating_panes = self.apply_floating_panes_layout(
floating_panes_layout,
new_floating_terminal_ids,
&mut new_plugin_ids,
layout_name,
)?;
return Ok(layout_has_floating_panes);
}
pub fn apply_tiled_panes_layout_to_existing_panes(
&mut self,
layout: &TiledPaneLayout,
refocus_pane: bool,
client_id: Option<ClientId>,
) -> Result<()> {
let err_context = || format!("failed to apply tiled panes layout");
let free_space = self.total_space_for_tiled_panes();
let tiled_panes_count = self.tiled_panes.visible_panes_count();
match layout.position_panes_in_space(&free_space, Some(tiled_panes_count)) {
Ok(positions_in_layout) => {
let currently_focused_pane_id =
client_id.and_then(|client_id| self.tiled_panes.focused_pane_id(client_id));
let mut existing_tab_state =
ExistingTabState::new(self.tiled_panes.drain(), currently_focused_pane_id);
let mut pane_focuser = PaneFocuser::new(refocus_pane);
for (layout, position_and_size) in positions_in_layout {
if let Some(mut pane) = existing_tab_state.find_and_extract_pane(
&layout.run,
&position_and_size,
layout.focus.unwrap_or(false),
true,
) {
self.apply_layout_properties_to_pane(
&mut pane,
&layout,
Some(position_and_size),
);
pane_focuser.set_pane_id_in_focused_location(layout.focus, &pane);
resize_pty!(pane, self.os_api, self.senders)?;
self.tiled_panes
.add_pane_with_existing_geom(pane.pid(), pane);
}
}
let remaining_pane_ids: Vec<PaneId> = existing_tab_state.pane_ids();
for pane_id in remaining_pane_ids {
if let Some(mut pane) = existing_tab_state.remove_pane(&pane_id) {
self.apply_layout_properties_to_pane(&mut pane, &layout, None);
self.tiled_panes.insert_pane(pane.pid(), pane);
}
}
pane_focuser.focus_tiled_pane(&mut self.tiled_panes);
},
Err(e) => {
Err::<(), _>(anyError::msg(e))
.with_context(err_context)
.non_fatal(); // TODO: propagate this to the user
},
};
Ok(())
}
fn apply_tiled_panes_layout(
&mut self,
layout: TiledPaneLayout,
new_terminal_ids: Vec<(u32, HoldForCommand)>,
new_plugin_ids: &mut HashMap<RunPluginLocation, Vec<u32>>,
client_id: ClientId,
) -> Result<()> {
let err_context = || format!("failed to apply tiled panes layout");
let free_space = self.total_space_for_tiled_panes();
match layout.position_panes_in_space(&free_space, None) {
Ok(positions_in_layout) => {
let positions_and_size = positions_in_layout.iter();
let mut new_terminal_ids = new_terminal_ids.iter();
let mut focus_pane_id: Option<PaneId> = None;
let mut set_focus_pane_id = |layout: &TiledPaneLayout, pane_id: PaneId| {
if layout.focus.unwrap_or(false) && focus_pane_id.is_none() {
focus_pane_id = Some(pane_id);
}
};
for (layout, position_and_size) in positions_and_size {
// A plugin pane
if let Some(Run::Plugin(run)) = layout.run.clone() {
let pane_title = run.location.to_string();
let pid = new_plugin_ids
.get_mut(&run.location)
.and_then(|ids| ids.pop())
.with_context(err_context)?;
let mut new_plugin = PluginPane::new(
pid,
*position_and_size,
self.senders
.to_plugin
.as_ref()
.with_context(err_context)?
.clone(),
pane_title,
layout.name.clone().unwrap_or_default(),
self.sixel_image_store.clone(),
self.terminal_emulator_colors.clone(),
self.terminal_emulator_color_codes.clone(),
self.link_handler.clone(),
self.character_cell_size.clone(),
self.style,
layout.run.clone(),
);
new_plugin.set_borderless(layout.borderless);
self.tiled_panes
.add_pane_with_existing_geom(PaneId::Plugin(pid), Box::new(new_plugin));
set_focus_pane_id(layout, PaneId::Plugin(pid));
} else {
// there are still panes left to fill, use the pids we received in this method
if let Some((pid, hold_for_command)) = new_terminal_ids.next() {
let next_terminal_position =
get_next_terminal_position(&self.tiled_panes, &self.floating_panes);
let initial_title = match &layout.run {
Some(Run::Command(run_command)) => Some(run_command.to_string()),
_ => None,
};
let mut new_pane = TerminalPane::new(
*pid,
*position_and_size,
self.style,
next_terminal_position,
layout.name.clone().unwrap_or_default(),
self.link_handler.clone(),
self.character_cell_size.clone(),
self.sixel_image_store.clone(),
self.terminal_emulator_colors.clone(),
self.terminal_emulator_color_codes.clone(),
initial_title,
layout.run.clone(),
);
new_pane.set_borderless(layout.borderless);
if let Some(held_command) = hold_for_command {
new_pane.hold(None, true, held_command.clone());
}
self.tiled_panes.add_pane_with_existing_geom(
PaneId::Terminal(*pid),
Box::new(new_pane),
);
set_focus_pane_id(layout, PaneId::Terminal(*pid));
}
}
}
for (unused_pid, _) in new_terminal_ids {
self.senders
.send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(*unused_pid)))
.with_context(err_context)?;
}
self.adjust_viewport().with_context(err_context)?;
self.set_focused_tiled_pane(focus_pane_id, client_id);
},
Err(e) => {
for (unused_pid, _) in new_terminal_ids {
self.senders
.send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(unused_pid)))
.with_context(err_context)?;
}
Err::<(), _>(anyError::msg(e))
.with_context(err_context)
.non_fatal(); // TODO: propagate this to the user
},
};
Ok(())
}
fn apply_floating_panes_layout(
&mut self,
floating_panes_layout: Vec<FloatingPaneLayout>,
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
new_plugin_ids: &mut HashMap<RunPluginLocation, Vec<u32>>,
layout_name: Option<String>,
) -> Result<bool> {
// true => has floating panes
let err_context = || format!("Failed to apply_floating_panes_layout");
let mut layout_has_floating_panes = false;
let floating_panes_layout = floating_panes_layout.iter();
let mut focused_floating_pane = None;
let mut new_floating_terminal_ids = new_floating_terminal_ids.iter();
for floating_pane_layout in floating_panes_layout {
layout_has_floating_panes = true;
if let Some(Run::Plugin(run)) = floating_pane_layout.run.clone() {
let position_and_size = self
.floating_panes
.position_floating_pane_layout(&floating_pane_layout);
let pane_title = run.location.to_string();
let pid = new_plugin_ids
.get_mut(&run.location)
.and_then(|ids| ids.pop())
.with_context(err_context)?;
let mut new_pane = PluginPane::new(
pid,
position_and_size,
self.senders
.to_plugin
.as_ref()
.with_context(err_context)?
.clone(),
pane_title,
layout_name.clone().unwrap_or_default(),
self.sixel_image_store.clone(),
self.terminal_emulator_colors.clone(),
self.terminal_emulator_color_codes.clone(),
self.link_handler.clone(),
self.character_cell_size.clone(),
self.style,
floating_pane_layout.run.clone(),
);
new_pane.set_borderless(false);
new_pane.set_content_offset(Offset::frame(1));
resize_pty!(new_pane, self.os_api, self.senders)?;
self.floating_panes
.add_pane(PaneId::Plugin(pid), Box::new(new_pane));
if floating_pane_layout.focus.unwrap_or(false) {
focused_floating_pane = Some(PaneId::Plugin(pid));
}
} else if let Some((pid, hold_for_command)) = new_floating_terminal_ids.next() {
let position_and_size = self
.floating_panes
.position_floating_pane_layout(&floating_pane_layout);
let next_terminal_position =
get_next_terminal_position(&self.tiled_panes, &self.floating_panes);
let initial_title = match &floating_pane_layout.run {
Some(Run::Command(run_command)) => Some(run_command.to_string()),
_ => None,
};
let mut new_pane = TerminalPane::new(
*pid,
position_and_size,
self.style,
next_terminal_position,
floating_pane_layout.name.clone().unwrap_or_default(),
self.link_handler.clone(),
self.character_cell_size.clone(),
self.sixel_image_store.clone(),
self.terminal_emulator_colors.clone(),
self.terminal_emulator_color_codes.clone(),
initial_title,
floating_pane_layout.run.clone(),
);
new_pane.set_borderless(false);
new_pane.set_content_offset(Offset::frame(1));
if let Some(held_command) = hold_for_command {
new_pane.hold(None, true, held_command.clone());
}
resize_pty!(new_pane, self.os_api, self.senders)?;
self.floating_panes
.add_pane(PaneId::Terminal(*pid), Box::new(new_pane));
if floating_pane_layout.focus.unwrap_or(false) {
focused_floating_pane = Some(PaneId::Terminal(*pid));
}
}
}
if let Some(focused_floating_pane) = focused_floating_pane {
self.floating_panes
.focus_pane_for_all_clients(focused_floating_pane);
}
if layout_has_floating_panes {
Ok(true)
} else {
Ok(false)
}
}
pub fn apply_floating_panes_layout_to_existing_panes(
&mut self,
floating_panes_layout: &Vec<FloatingPaneLayout>,
refocus_pane: bool,
client_id: Option<ClientId>,
) -> Result<bool> {
// true => has floating panes
let mut layout_has_floating_panes = false;
let layout_has_focused_pane = floating_panes_layout
.iter()
.find(|f| f.focus.map(|f| f).unwrap_or(false))
.is_some();
let floating_panes_layout = floating_panes_layout.iter();
let currently_focused_pane_id = self
.floating_panes
.active_pane_id_or_focused_pane_id(client_id);
let mut existing_tab_state =
ExistingTabState::new(self.floating_panes.drain(), currently_focused_pane_id);
let mut pane_focuser = PaneFocuser::new(refocus_pane);
for floating_pane_layout in floating_panes_layout {
let position_and_size = self
.floating_panes
.position_floating_pane_layout(&floating_pane_layout);
let is_focused = floating_pane_layout.focus.unwrap_or(false);
if let Some(mut pane) = existing_tab_state.find_and_extract_pane(
&floating_pane_layout.run,
&position_and_size,
is_focused,
false,
) {
layout_has_floating_panes = true;
self.apply_floating_pane_layout_properties_to_pane(
&mut pane,
Some(&floating_pane_layout),
position_and_size,
);
let pane_is_focused = floating_pane_layout
.focus
.or(Some(!layout_has_focused_pane));
pane_focuser.set_pane_id_in_focused_location(pane_is_focused, &pane);
resize_pty!(pane, self.os_api, self.senders)?;
self.floating_panes.add_pane(pane.pid(), pane);
}
}
let remaining_pane_ids: Vec<PaneId> = existing_tab_state.pane_ids();
for pane_id in remaining_pane_ids {
match self.floating_panes.find_room_for_new_pane() {
Some(position_and_size) => {
if let Some(mut pane) = existing_tab_state.remove_pane(&pane_id) {
layout_has_floating_panes = true;
self.apply_floating_pane_layout_properties_to_pane(
&mut pane,
None,
position_and_size,
);
pane_focuser
.set_pane_id_in_focused_location(Some(!layout_has_focused_pane), &pane);
resize_pty!(pane, self.os_api, self.senders)?;
self.floating_panes.add_pane(pane.pid(), pane);
}
},
None => {
log::error!("could not find room for pane!")
},
}
}
if layout_has_floating_panes {
pane_focuser.focus_floating_pane(&mut self.floating_panes, &mut self.os_api);
Ok(true)
} else {
Ok(false)
}
}
fn resize_whole_tab(&mut self, new_screen_size: Size) -> Result<()> {
let err_context = || {
format!(
"failed to resize whole tab to new screen size {:?}",
new_screen_size
)
};
self.floating_panes.resize(new_screen_size);
// we need to do this explicitly because floating_panes.resize does not do this
self.floating_panes
.resize_pty_all_panes(&mut self.os_api)
.with_context(err_context)?;
self.tiled_panes.resize(new_screen_size);
Ok(())
}
fn offset_viewport(&mut self, position_and_size: &Viewport) {
let mut viewport = self.viewport.borrow_mut();
if position_and_size.x == viewport.x
&& position_and_size.x + position_and_size.cols == viewport.x + viewport.cols
{
if position_and_size.y == viewport.y {
viewport.y += position_and_size.rows;
viewport.rows -= position_and_size.rows;
} else if position_and_size.y + position_and_size.rows == viewport.y + viewport.rows {
viewport.rows -= position_and_size.rows;
}
}
if position_and_size.y == viewport.y
&& position_and_size.y + position_and_size.rows == viewport.y + viewport.rows
{
if position_and_size.x == viewport.x {
viewport.x += position_and_size.cols;
viewport.cols -= position_and_size.cols;
} else if position_and_size.x + position_and_size.cols == viewport.x + viewport.cols {
viewport.cols -= position_and_size.cols;
}
}
}
fn adjust_viewport(&mut self) -> Result<()> {
// here we offset the viewport after applying a tiled panes layout
// from borderless panes that are on the edges of the
// screen, this is so that when we don't have pane boundaries (eg. when they were
// disabled by the user) boundaries won't be drawn around these panes
// geometrically, we can only do this with panes that are on the edges of the
// screen - so it's mostly a best-effort thing
let err_context = "failed to adjust viewport";
let display_area = {
let display_area = self.display_area.borrow();
*display_area
};
self.resize_whole_tab(display_area).context(err_context)?;
let boundary_geoms = self.tiled_panes.borderless_pane_geoms();
for geom in boundary_geoms {
self.offset_viewport(&geom)
}
self.tiled_panes.set_pane_frames(self.draw_pane_frames);
Ok(())
}
fn set_focused_tiled_pane(&mut self, focus_pane_id: Option<PaneId>, client_id: ClientId) {
if let Some(pane_id) = focus_pane_id {
*self.focus_pane_id = Some(pane_id);
self.tiled_panes.focus_pane(pane_id, client_id);
} else {
let next_selectable_pane_id = self.tiled_panes.first_selectable_pane_id();
match next_selectable_pane_id {
Some(active_pane_id) => {
self.tiled_panes.focus_pane(active_pane_id, client_id);
},
None => {
self.tiled_panes.clear_active_panes();
},
}
}
}
fn apply_layout_properties_to_pane(
&self,
pane: &mut Box<dyn Pane>,
layout: &TiledPaneLayout,
position_and_size: Option<PaneGeom>,
) {
if let Some(position_and_size) = position_and_size {
pane.set_geom(position_and_size);
}
pane.set_borderless(layout.borderless);
if let Some(pane_title) = layout.name.as_ref() {
pane.set_title(pane_title.into());
}
}
fn apply_floating_pane_layout_properties_to_pane(
&self,
pane: &mut Box<dyn Pane>,
floating_pane_layout: Option<&FloatingPaneLayout>,
position_and_size: PaneGeom,
) {
pane.set_geom(position_and_size);
pane.set_borderless(false);
if let Some(pane_title) = floating_pane_layout.and_then(|f| f.name.clone()) {
pane.set_title(pane_title);
}
pane.set_content_offset(Offset::frame(1));
}
fn total_space_for_tiled_panes(&self) -> PaneGeom {
// for tiled panes we need to take the display area rather than the viewport because the
// viewport can potentially also be changed
let (display_area_cols, display_area_rows) = {
let display_area = self.display_area.borrow();
(display_area.cols, display_area.rows)
};
let mut free_space = PaneGeom::default();
free_space.cols.set_inner(display_area_cols);
free_space.rows.set_inner(display_area_rows);
free_space
}
}
struct ExistingTabState {
existing_panes: BTreeMap<PaneId, Box<dyn Pane>>,
currently_focused_pane_id: Option<PaneId>,
}
impl ExistingTabState {
pub fn new(
existing_panes: BTreeMap<PaneId, Box<dyn Pane>>,
currently_focused_pane_id: Option<PaneId>,
) -> Self {
ExistingTabState {
existing_panes,
currently_focused_pane_id,
}
}
pub fn find_and_extract_pane(
&mut self,
run: &Option<Run>,
position_and_size: &PaneGeom,
is_focused: bool,
default_to_closest_position: bool,
) -> Option<Box<dyn Pane>> {
let candidates = self.pane_candidates(run, position_and_size, default_to_closest_position);
if let Some(current_pane_id_with_same_contents) =
self.find_pane_id_with_same_contents(&candidates, run)
{
return self
.existing_panes
.remove(&current_pane_id_with_same_contents);
} else if let Some(currently_focused_pane_id) =
self.find_focused_pane_id(is_focused, &candidates)
{
return self.existing_panes.remove(&currently_focused_pane_id);
} else if let Some(same_position_candidate_id) = candidates
.iter()
.find(|(_, p)| p.position_and_size() == *position_and_size)
.map(|(pid, _p)| *pid)
.copied()
{
return self.existing_panes.remove(&same_position_candidate_id);
} else if let Some(first_candidate) =
candidates.iter().next().map(|(pid, _p)| *pid).copied()
{
return self.existing_panes.remove(&first_candidate);
}
None
}
pub fn pane_ids(&self) -> Vec<PaneId> {
self.existing_panes.keys().copied().collect()
}
pub fn remove_pane(&mut self, pane_id: &PaneId) -> Option<Box<dyn Pane>> {
self.existing_panes.remove(pane_id)
}
fn pane_candidates(
&self,
run: &Option<Run>,
position_and_size: &PaneGeom,
default_to_closest_position: bool,
) -> Vec<(&PaneId, &Box<dyn Pane>)> {
let mut candidates: Vec<_> = self.existing_panes.iter().collect();
candidates.sort_by(|(a_id, a), (b_id, b)| {
let a_invoked_with = a.invoked_with();
let b_invoked_with = b.invoked_with();
if Run::is_same_category(run, a_invoked_with)
&& !Run::is_same_category(run, b_invoked_with)
{
std::cmp::Ordering::Less
} else if Run::is_same_category(run, b_invoked_with)
&& !Run::is_same_category(run, a_invoked_with)
{
std::cmp::Ordering::Greater
} else if Run::is_terminal(a_invoked_with) && !Run::is_terminal(b_invoked_with) {
// we place terminals before everything else because when we can't find
// an exact match, we need to prefer terminals are more often than not
// we'd be doing the right thing here
std::cmp::Ordering::Less
} else if Run::is_terminal(b_invoked_with) && !Run::is_terminal(a_invoked_with) {
std::cmp::Ordering::Greater
} else {
// try to find the closest pane
if default_to_closest_position {
let abs = |a, b| (a as isize - b as isize).abs();
let a_x_distance = abs(a.position_and_size().x, position_and_size.x);
let a_y_distance = abs(a.position_and_size().y, position_and_size.y);
let b_x_distance = abs(b.position_and_size().x, position_and_size.x);
let b_y_distance = abs(b.position_and_size().y, position_and_size.y);
(a_x_distance + a_y_distance).cmp(&(b_x_distance + b_y_distance))
} else {
a_id.cmp(&b_id) // just so it's a stable sort
}
}
});
candidates
}
fn find_focused_pane_id(
&self,
is_focused: bool,
candidates: &Vec<(&PaneId, &Box<dyn Pane>)>,
) -> Option<PaneId> {
if is_focused {
candidates
.iter()
.find(|(pid, _p)| Some(**pid) == self.currently_focused_pane_id)
.map(|(pid, _p)| *pid)
.copied()
} else {
None
}
}
fn find_pane_id_with_same_contents(
&self,
candidates: &Vec<(&PaneId, &Box<dyn Pane>)>,
run: &Option<Run>,
) -> Option<PaneId> {
candidates
.iter()
.find(|(_pid, p)| p.invoked_with() == run)
.map(|(pid, _p)| *pid)
.copied()
}
}
#[derive(Default, Debug)]
struct PaneFocuser {
refocus_pane: bool,
pane_id_in_focused_location: Option<PaneId>,
}
impl PaneFocuser {
pub fn new(refocus_pane: bool) -> Self {
PaneFocuser {
refocus_pane,
..Default::default()
}
}
pub fn set_pane_id_in_focused_location(
&mut self,
is_focused: Option<bool>,
pane: &Box<dyn Pane>,
) {
if is_focused.unwrap_or(false) && pane.selectable() {
self.pane_id_in_focused_location = Some(pane.pid());
}
}
pub fn focus_tiled_pane(&self, tiled_panes: &mut TiledPanes) {
match self.pane_id_in_focused_location {
Some(pane_id_in_focused_location) => {
if self.refocus_pane {
tiled_panes.reapply_pane_focus();
tiled_panes.switch_active_pane_with(pane_id_in_focused_location);
} else {
tiled_panes.reapply_pane_focus();
}
},
None => {
tiled_panes.reapply_pane_focus();
},
}
}
pub fn focus_floating_pane(
&self,
floating_panes: &mut FloatingPanes,
os_api: &mut Box<dyn ServerOsApi>,
) {
floating_panes.reapply_pane_focus();
if let Some(pane_id_in_focused_location) = self.pane_id_in_focused_location {
if self.refocus_pane {
floating_panes.switch_active_pane_with(os_api, pane_id_in_focused_location);
}
}
}
}