1228 lines
52 KiB
Rust
1228 lines
52 KiB
Rust
mod pane_resizer;
|
|
mod tiled_pane_grid;
|
|
|
|
use crate::resize_pty;
|
|
use tiled_pane_grid::{split, TiledPaneGrid};
|
|
|
|
use crate::{
|
|
os_input_output::ServerOsApi,
|
|
output::Output,
|
|
panes::{ActivePanes, PaneId},
|
|
plugins::PluginInstruction,
|
|
tab::{Pane, MIN_TERMINAL_HEIGHT, MIN_TERMINAL_WIDTH},
|
|
thread_bus::ThreadSenders,
|
|
ui::boundaries::Boundaries,
|
|
ui::pane_contents_and_ui::PaneContentsAndUi,
|
|
ClientId,
|
|
};
|
|
use zellij_utils::{
|
|
data::{ModeInfo, Style},
|
|
errors::prelude::*,
|
|
input::{command::RunCommand, layout::SplitDirection},
|
|
pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport},
|
|
};
|
|
|
|
use std::{
|
|
cell::RefCell,
|
|
collections::{BTreeMap, HashMap, HashSet},
|
|
rc::Rc,
|
|
time::Instant,
|
|
};
|
|
|
|
fn pane_content_offset(position_and_size: &PaneGeom, viewport: &Viewport) -> (usize, usize) {
|
|
// (columns_offset, rows_offset)
|
|
// if the pane is not on the bottom or right edge on the screen, we need to reserve one space
|
|
// from its content to leave room for the boundary between it and the next pane (if it doesn't
|
|
// draw its own frame)
|
|
let columns_offset = if position_and_size.x + position_and_size.cols.as_usize() < viewport.cols
|
|
{
|
|
1
|
|
} else {
|
|
0
|
|
};
|
|
let rows_offset = if position_and_size.y + position_and_size.rows.as_usize() < viewport.rows {
|
|
1
|
|
} else {
|
|
0
|
|
};
|
|
(columns_offset, rows_offset)
|
|
}
|
|
|
|
pub struct TiledPanes {
|
|
pub panes: BTreeMap<PaneId, Box<dyn Pane>>,
|
|
display_area: Rc<RefCell<Size>>,
|
|
viewport: Rc<RefCell<Viewport>>,
|
|
connected_clients: Rc<RefCell<HashSet<ClientId>>>,
|
|
connected_clients_in_app: Rc<RefCell<HashSet<ClientId>>>,
|
|
mode_info: Rc<RefCell<HashMap<ClientId, ModeInfo>>>,
|
|
character_cell_size: Rc<RefCell<Option<SizeInPixels>>>,
|
|
default_mode_info: ModeInfo,
|
|
style: Style,
|
|
session_is_mirrored: bool,
|
|
active_panes: ActivePanes,
|
|
draw_pane_frames: bool,
|
|
panes_to_hide: HashSet<PaneId>,
|
|
fullscreen_is_active: bool,
|
|
os_api: Box<dyn ServerOsApi>,
|
|
senders: ThreadSenders,
|
|
window_title: Option<String>,
|
|
client_id_to_boundaries: HashMap<ClientId, Boundaries>,
|
|
}
|
|
|
|
impl TiledPanes {
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
display_area: Rc<RefCell<Size>>,
|
|
viewport: Rc<RefCell<Viewport>>,
|
|
connected_clients: Rc<RefCell<HashSet<ClientId>>>,
|
|
connected_clients_in_app: Rc<RefCell<HashSet<ClientId>>>,
|
|
mode_info: Rc<RefCell<HashMap<ClientId, ModeInfo>>>,
|
|
character_cell_size: Rc<RefCell<Option<SizeInPixels>>>,
|
|
session_is_mirrored: bool,
|
|
draw_pane_frames: bool,
|
|
default_mode_info: ModeInfo,
|
|
style: Style,
|
|
os_api: Box<dyn ServerOsApi>,
|
|
senders: ThreadSenders,
|
|
) -> Self {
|
|
TiledPanes {
|
|
panes: BTreeMap::new(),
|
|
display_area,
|
|
viewport,
|
|
connected_clients,
|
|
connected_clients_in_app,
|
|
mode_info,
|
|
character_cell_size,
|
|
default_mode_info,
|
|
style,
|
|
session_is_mirrored,
|
|
active_panes: ActivePanes::new(&os_api),
|
|
draw_pane_frames,
|
|
panes_to_hide: HashSet::new(),
|
|
fullscreen_is_active: false,
|
|
os_api,
|
|
senders,
|
|
window_title: None,
|
|
client_id_to_boundaries: HashMap::new(),
|
|
}
|
|
}
|
|
pub fn add_pane_with_existing_geom(&mut self, pane_id: PaneId, mut pane: Box<dyn Pane>) {
|
|
if self.draw_pane_frames {
|
|
pane.set_content_offset(Offset::frame(1));
|
|
}
|
|
self.panes.insert(pane_id, pane);
|
|
}
|
|
pub fn replace_active_pane(
|
|
&mut self,
|
|
pane: Box<dyn Pane>,
|
|
client_id: ClientId,
|
|
) -> Option<Box<dyn Pane>> {
|
|
let pane_id = pane.pid();
|
|
// remove the currently active pane
|
|
let previously_active_pane = self
|
|
.active_panes
|
|
.get(&client_id)
|
|
.copied()
|
|
.and_then(|active_pane_id| self.replace_pane(active_pane_id, pane));
|
|
|
|
// move clients from the previously active pane to the new pane we just inserted
|
|
if let Some(previously_active_pane) = previously_active_pane.as_ref() {
|
|
let previously_active_pane_id = previously_active_pane.pid();
|
|
self.move_clients_between_panes(previously_active_pane_id, pane_id);
|
|
}
|
|
previously_active_pane
|
|
}
|
|
pub fn replace_pane(
|
|
&mut self,
|
|
pane_id: PaneId,
|
|
mut with_pane: Box<dyn Pane>,
|
|
) -> Option<Box<dyn Pane>> {
|
|
let with_pane_id = with_pane.pid();
|
|
if self.draw_pane_frames && !with_pane.borderless() {
|
|
with_pane.set_content_offset(Offset::frame(1));
|
|
}
|
|
let removed_pane = self.panes.remove(&pane_id).map(|removed_pane| {
|
|
let with_pane_id = with_pane.pid();
|
|
let removed_pane_geom = removed_pane.position_and_size();
|
|
let removed_pane_geom_override = removed_pane.geom_override();
|
|
with_pane.set_geom(removed_pane_geom);
|
|
match removed_pane_geom_override {
|
|
Some(geom_override) => with_pane.set_geom_override(geom_override),
|
|
None => with_pane.reset_size_and_position_override(),
|
|
};
|
|
self.panes.insert(with_pane_id, with_pane);
|
|
removed_pane
|
|
});
|
|
|
|
// move clients from the previously active pane to the new pane we just inserted
|
|
self.move_clients_between_panes(pane_id, with_pane_id);
|
|
removed_pane
|
|
}
|
|
pub fn insert_pane(&mut self, pane_id: PaneId, mut pane: Box<dyn Pane>) {
|
|
let cursor_height_width_ratio = self.cursor_height_width_ratio();
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let pane_id_and_split_direction =
|
|
pane_grid.find_room_for_new_pane(cursor_height_width_ratio);
|
|
if let Some((pane_id_to_split, split_direction)) = pane_id_and_split_direction {
|
|
// this unwrap is safe because floating panes should not be visible if there are no floating panes
|
|
let pane_to_split = self.panes.get_mut(&pane_id_to_split).unwrap();
|
|
let size_of_both_panes = pane_to_split.position_and_size();
|
|
if let Some((first_geom, second_geom)) = split(split_direction, &size_of_both_panes) {
|
|
pane_to_split.set_geom(first_geom);
|
|
pane.set_geom(second_geom);
|
|
self.panes.insert(pane_id, pane);
|
|
self.relayout(!split_direction);
|
|
}
|
|
}
|
|
}
|
|
pub fn has_room_for_new_pane(&mut self) -> bool {
|
|
let cursor_height_width_ratio = self.cursor_height_width_ratio();
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
pane_grid
|
|
.find_room_for_new_pane(cursor_height_width_ratio)
|
|
.is_some()
|
|
}
|
|
pub fn fixed_pane_geoms(&self) -> Vec<Viewport> {
|
|
self.panes
|
|
.values()
|
|
.filter_map(|p| {
|
|
let geom = p.position_and_size();
|
|
if geom.cols.is_fixed() || geom.rows.is_fixed() {
|
|
Some(geom.into())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
pub fn first_selectable_pane_id(&self) -> Option<PaneId> {
|
|
self.panes
|
|
.iter()
|
|
.filter(|(_id, pane)| pane.selectable())
|
|
.map(|(id, _)| id.to_owned())
|
|
.next()
|
|
}
|
|
pub fn pane_ids(&self) -> impl Iterator<Item = &PaneId> {
|
|
self.panes.keys()
|
|
}
|
|
pub fn relayout(&mut self, direction: SplitDirection) {
|
|
let mut pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
match direction {
|
|
SplitDirection::Horizontal => {
|
|
pane_grid.layout(direction, (*self.display_area.borrow()).cols)
|
|
},
|
|
SplitDirection::Vertical => {
|
|
pane_grid.layout(direction, (*self.display_area.borrow()).rows)
|
|
},
|
|
}
|
|
.or_else(|e| Err(anyError::msg(e)))
|
|
.with_context(|| format!("{:?} relayout of tab failed", direction))
|
|
.non_fatal();
|
|
|
|
self.set_pane_frames(self.draw_pane_frames);
|
|
}
|
|
pub fn set_pane_frames(&mut self, draw_pane_frames: bool) {
|
|
self.draw_pane_frames = draw_pane_frames;
|
|
let viewport = *self.viewport.borrow();
|
|
for pane in self.panes.values_mut() {
|
|
if !pane.borderless() {
|
|
pane.set_frame(draw_pane_frames);
|
|
}
|
|
|
|
#[allow(clippy::if_same_then_else)]
|
|
if draw_pane_frames && !pane.borderless() {
|
|
// there's definitely a frame around this pane, offset its contents
|
|
pane.set_content_offset(Offset::frame(1));
|
|
} else if draw_pane_frames && pane.borderless() {
|
|
// there's no frame around this pane, and the tab isn't handling the boundaries
|
|
// between panes (they each draw their own frames as they please)
|
|
// this one doesn't - do not offset its content
|
|
pane.set_content_offset(Offset::default());
|
|
} else if !is_inside_viewport(&viewport, pane) {
|
|
// this pane is outside the viewport and has no border - it should not have an offset
|
|
pane.set_content_offset(Offset::default());
|
|
} else {
|
|
// no draw_pane_frames and this pane should have a separation to other panes
|
|
// according to its position in the viewport (eg. no separation if its at the
|
|
// viewport bottom) - offset its content accordingly
|
|
let position_and_size = pane.current_geom();
|
|
let (pane_columns_offset, pane_rows_offset) =
|
|
pane_content_offset(&position_and_size, &viewport);
|
|
pane.set_content_offset(Offset::shift(pane_rows_offset, pane_columns_offset));
|
|
}
|
|
|
|
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
|
}
|
|
self.reset_boundaries();
|
|
}
|
|
pub fn can_split_pane_horizontally(&mut self, client_id: ClientId) -> bool {
|
|
if let Some(active_pane_id) = &self.active_panes.get(&client_id) {
|
|
if let Some(active_pane) = self.panes.get_mut(active_pane_id) {
|
|
let full_pane_size = active_pane.position_and_size();
|
|
if full_pane_size.rows.as_usize() < MIN_TERMINAL_HEIGHT * 2 {
|
|
return false;
|
|
} else {
|
|
return split(SplitDirection::Horizontal, &full_pane_size).is_some();
|
|
}
|
|
}
|
|
}
|
|
false
|
|
}
|
|
pub fn can_split_pane_vertically(&mut self, client_id: ClientId) -> bool {
|
|
if let Some(active_pane_id) = &self.active_panes.get(&client_id) {
|
|
if let Some(active_pane) = self.panes.get_mut(active_pane_id) {
|
|
let full_pane_size = active_pane.position_and_size();
|
|
if full_pane_size.cols.as_usize() < MIN_TERMINAL_WIDTH * 2 {
|
|
return false;
|
|
}
|
|
return split(SplitDirection::Vertical, &full_pane_size).is_some();
|
|
}
|
|
}
|
|
false
|
|
}
|
|
pub fn split_pane_horizontally(
|
|
&mut self,
|
|
pid: PaneId,
|
|
mut new_pane: Box<dyn Pane>,
|
|
client_id: ClientId,
|
|
) {
|
|
let active_pane_id = &self.active_panes.get(&client_id).unwrap();
|
|
let active_pane = self.panes.get_mut(active_pane_id).unwrap();
|
|
let full_pane_size = active_pane.position_and_size();
|
|
if let Some((top_winsize, bottom_winsize)) =
|
|
split(SplitDirection::Horizontal, &full_pane_size)
|
|
{
|
|
active_pane.set_geom(top_winsize);
|
|
new_pane.set_geom(bottom_winsize);
|
|
self.panes.insert(pid, new_pane);
|
|
self.relayout(SplitDirection::Vertical);
|
|
}
|
|
}
|
|
pub fn split_pane_vertically(
|
|
&mut self,
|
|
pid: PaneId,
|
|
mut new_pane: Box<dyn Pane>,
|
|
client_id: ClientId,
|
|
) {
|
|
let active_pane_id = &self.active_panes.get(&client_id).unwrap();
|
|
let active_pane = self.panes.get_mut(active_pane_id).unwrap();
|
|
let full_pane_size = active_pane.position_and_size();
|
|
if let Some((left_winsize, right_winsize)) =
|
|
split(SplitDirection::Vertical, &full_pane_size)
|
|
{
|
|
active_pane.set_geom(left_winsize);
|
|
new_pane.set_geom(right_winsize);
|
|
self.panes.insert(pid, new_pane);
|
|
self.relayout(SplitDirection::Horizontal);
|
|
}
|
|
}
|
|
pub fn focus_pane(&mut self, pane_id: PaneId, client_id: ClientId) {
|
|
self.active_panes
|
|
.insert(client_id, pane_id, &mut self.panes);
|
|
if self.session_is_mirrored {
|
|
// move all clients
|
|
let connected_clients: Vec<ClientId> =
|
|
self.connected_clients.borrow().iter().copied().collect();
|
|
for client_id in connected_clients {
|
|
self.active_panes
|
|
.insert(client_id, pane_id, &mut self.panes);
|
|
}
|
|
}
|
|
self.reset_boundaries();
|
|
}
|
|
pub fn focus_pane_if_client_not_focused(&mut self, pane_id: PaneId, client_id: ClientId) {
|
|
if self.active_panes.get(&client_id).is_none() {
|
|
self.focus_pane(pane_id, client_id)
|
|
}
|
|
}
|
|
pub fn clear_active_panes(&mut self) {
|
|
self.active_panes.clear(&mut self.panes);
|
|
self.reset_boundaries();
|
|
}
|
|
pub fn first_active_pane_id(&self) -> Option<PaneId> {
|
|
self.connected_clients
|
|
.borrow()
|
|
.iter()
|
|
.next()
|
|
.and_then(|first_client_id| self.active_panes.get(first_client_id).copied())
|
|
}
|
|
pub fn focused_pane_id(&self, client_id: ClientId) -> Option<PaneId> {
|
|
self.active_panes.get(&client_id).copied()
|
|
}
|
|
// FIXME: Really not a fan of allowing this... Someone with more energy
|
|
// than me should clean this up someday...
|
|
#[allow(clippy::borrowed_box)]
|
|
pub fn get_pane(&self, pane_id: PaneId) -> Option<&Box<dyn Pane>> {
|
|
self.panes.get(&pane_id)
|
|
}
|
|
pub fn get_pane_mut(&mut self, pane_id: PaneId) -> Option<&mut Box<dyn Pane>> {
|
|
self.panes.get_mut(&pane_id)
|
|
}
|
|
pub fn get_active_pane_id(&self, client_id: ClientId) -> Option<PaneId> {
|
|
self.active_panes.get(&client_id).copied()
|
|
}
|
|
pub fn panes_contain(&self, pane_id: &PaneId) -> bool {
|
|
self.panes.contains_key(pane_id)
|
|
}
|
|
pub fn set_force_render(&mut self) {
|
|
for pane in self.panes.values_mut() {
|
|
pane.set_should_render(true);
|
|
pane.set_should_render_boundaries(true);
|
|
pane.render_full_viewport();
|
|
}
|
|
self.reset_boundaries();
|
|
}
|
|
pub fn has_active_panes(&self) -> bool {
|
|
!self.active_panes.is_empty()
|
|
}
|
|
pub fn has_panes(&self) -> bool {
|
|
!self.panes.is_empty()
|
|
}
|
|
pub fn render(&mut self, output: &mut Output, floating_panes_are_visible: bool) -> Result<()> {
|
|
let err_context = || "failed to render tiled panes";
|
|
|
|
let connected_clients: Vec<ClientId> =
|
|
{ self.connected_clients.borrow().iter().copied().collect() };
|
|
let multiple_users_exist_in_session = { self.connected_clients_in_app.borrow().len() > 1 };
|
|
let mut client_id_to_boundaries: HashMap<ClientId, Boundaries> = HashMap::new();
|
|
let active_panes = if self.session_is_mirrored || floating_panes_are_visible {
|
|
HashMap::new()
|
|
} else {
|
|
self.active_panes
|
|
.iter()
|
|
.filter(|(client_id, _pane_id)| connected_clients.contains(client_id))
|
|
.map(|(client_id, pane_id)| (*client_id, *pane_id))
|
|
.collect()
|
|
};
|
|
for (kind, pane) in self.panes.iter_mut() {
|
|
if !self.panes_to_hide.contains(&pane.pid()) {
|
|
let mut pane_contents_and_ui = PaneContentsAndUi::new(
|
|
pane,
|
|
output,
|
|
self.style,
|
|
&active_panes,
|
|
multiple_users_exist_in_session,
|
|
None,
|
|
);
|
|
for client_id in &connected_clients {
|
|
let client_mode = self
|
|
.mode_info
|
|
.borrow()
|
|
.get(client_id)
|
|
.unwrap_or(&self.default_mode_info)
|
|
.mode;
|
|
let err_context =
|
|
|| format!("failed to render tiled panes for client {client_id}");
|
|
if let PaneId::Plugin(..) = kind {
|
|
pane_contents_and_ui
|
|
.render_pane_contents_for_client(*client_id)
|
|
.with_context(err_context)?;
|
|
}
|
|
if self.draw_pane_frames {
|
|
pane_contents_and_ui
|
|
.render_pane_frame(*client_id, client_mode, self.session_is_mirrored)
|
|
.with_context(err_context)?;
|
|
} else {
|
|
let boundaries = client_id_to_boundaries
|
|
.entry(*client_id)
|
|
.or_insert_with(|| Boundaries::new(*self.viewport.borrow()));
|
|
pane_contents_and_ui.render_pane_boundaries(
|
|
*client_id,
|
|
client_mode,
|
|
boundaries,
|
|
self.session_is_mirrored,
|
|
);
|
|
}
|
|
pane_contents_and_ui.render_terminal_title_if_needed(
|
|
*client_id,
|
|
client_mode,
|
|
&mut self.window_title,
|
|
);
|
|
// this is done for panes that don't have their own cursor (eg. panes of
|
|
// another user)
|
|
pane_contents_and_ui
|
|
.render_fake_cursor_if_needed(*client_id)
|
|
.with_context(err_context)?;
|
|
}
|
|
if let PaneId::Terminal(..) = kind {
|
|
pane_contents_and_ui
|
|
.render_pane_contents_to_multiple_clients(connected_clients.iter().copied())
|
|
.with_context(err_context)?;
|
|
}
|
|
}
|
|
}
|
|
// render boundaries if needed
|
|
for (client_id, boundaries) in client_id_to_boundaries {
|
|
let boundaries_to_render = boundaries
|
|
.render(self.client_id_to_boundaries.get(&client_id))
|
|
.with_context(err_context)?;
|
|
self.client_id_to_boundaries.insert(client_id, boundaries);
|
|
output
|
|
.add_character_chunks_to_client(client_id, boundaries_to_render, None)
|
|
.with_context(err_context)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
pub fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
|
|
self.panes.iter()
|
|
}
|
|
pub fn resize(&mut self, new_screen_size: Size) {
|
|
// this is blocked out to appease the borrow checker
|
|
{
|
|
let mut display_area = self.display_area.borrow_mut();
|
|
let mut viewport = self.viewport.borrow_mut();
|
|
let Size { rows, cols } = new_screen_size;
|
|
let mut pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*display_area,
|
|
*viewport,
|
|
);
|
|
match pane_grid.layout(SplitDirection::Horizontal, cols) {
|
|
Ok(_) => {
|
|
let column_difference = cols as isize - display_area.cols as isize;
|
|
// FIXME: Should the viewport be an Offset?
|
|
viewport.cols = (viewport.cols as isize + column_difference) as usize;
|
|
display_area.cols = cols;
|
|
},
|
|
Err(e) => {
|
|
Err::<(), _>(anyError::msg(e))
|
|
.context("failed to resize tab horizontally")
|
|
.non_fatal();
|
|
},
|
|
};
|
|
match pane_grid.layout(SplitDirection::Vertical, rows) {
|
|
Ok(_) => {
|
|
let row_difference = rows as isize - display_area.rows as isize;
|
|
viewport.rows = (viewport.rows as isize + row_difference) as usize;
|
|
display_area.rows = rows;
|
|
},
|
|
Err(e) => {
|
|
Err::<(), _>(anyError::msg(e))
|
|
.context("failed to resize tab vertically")
|
|
.non_fatal();
|
|
},
|
|
};
|
|
}
|
|
self.set_pane_frames(self.draw_pane_frames);
|
|
}
|
|
pub fn resize_active_pane_left(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let mut pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
pane_grid.resize_pane_left(&active_pane_id);
|
|
for pane in self.panes.values_mut() {
|
|
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
|
}
|
|
self.reset_boundaries();
|
|
}
|
|
}
|
|
pub fn resize_active_pane_right(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let mut pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
pane_grid.resize_pane_right(&active_pane_id);
|
|
for pane in self.panes.values_mut() {
|
|
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
|
}
|
|
self.reset_boundaries();
|
|
}
|
|
}
|
|
pub fn resize_active_pane_up(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let mut pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
pane_grid.resize_pane_up(&active_pane_id);
|
|
for pane in self.panes.values_mut() {
|
|
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
|
}
|
|
self.reset_boundaries();
|
|
}
|
|
}
|
|
pub fn resize_active_pane_down(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let mut pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
pane_grid.resize_pane_down(&active_pane_id);
|
|
for pane in self.panes.values_mut() {
|
|
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
|
}
|
|
self.reset_boundaries();
|
|
}
|
|
}
|
|
pub fn resize_active_pane_increase(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let mut pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
pane_grid.resize_increase(&active_pane_id);
|
|
for pane in self.panes.values_mut() {
|
|
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
|
}
|
|
self.reset_boundaries();
|
|
}
|
|
}
|
|
pub fn resize_active_pane_decrease(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let mut pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
pane_grid.resize_decrease(&active_pane_id);
|
|
for pane in self.panes.values_mut() {
|
|
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
|
}
|
|
self.reset_boundaries();
|
|
}
|
|
}
|
|
pub fn focus_next_pane(&mut self, client_id: ClientId) {
|
|
let connected_clients: Vec<ClientId> =
|
|
{ self.connected_clients.borrow().iter().copied().collect() };
|
|
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_active_pane_id = pane_grid.next_selectable_pane_id(&active_pane_id);
|
|
for client_id in connected_clients {
|
|
self.active_panes
|
|
.insert(client_id, next_active_pane_id, &mut self.panes);
|
|
}
|
|
self.set_pane_active_at(next_active_pane_id);
|
|
self.reset_boundaries();
|
|
}
|
|
pub fn focus_previous_pane(&mut self, client_id: ClientId) {
|
|
let connected_clients: Vec<ClientId> =
|
|
{ self.connected_clients.borrow().iter().copied().collect() };
|
|
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_active_pane_id = pane_grid.previous_selectable_pane_id(&active_pane_id);
|
|
for client_id in connected_clients {
|
|
self.active_panes
|
|
.insert(client_id, next_active_pane_id, &mut self.panes);
|
|
}
|
|
self.set_pane_active_at(next_active_pane_id);
|
|
self.reset_boundaries();
|
|
}
|
|
fn set_pane_active_at(&mut self, pane_id: PaneId) {
|
|
if let Some(pane) = self.get_pane_mut(pane_id) {
|
|
pane.set_active_at(Instant::now());
|
|
}
|
|
}
|
|
pub fn cursor_height_width_ratio(&self) -> Option<usize> {
|
|
let character_cell_size = self.character_cell_size.borrow();
|
|
character_cell_size.map(|size_in_pixels| {
|
|
(size_in_pixels.height as f64 / size_in_pixels.width as f64).round() as usize
|
|
})
|
|
}
|
|
pub fn move_focus_left(&mut self, client_id: ClientId) -> bool {
|
|
match self.get_active_pane_id(client_id) {
|
|
Some(active_pane_id) => {
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_index = pane_grid.next_selectable_pane_id_to_the_left(&active_pane_id);
|
|
match next_index {
|
|
Some(p) => {
|
|
// render previously active pane so that its frame does not remain actively
|
|
// colored
|
|
let previously_active_pane = self
|
|
.panes
|
|
.get_mut(self.active_panes.get(&client_id).unwrap())
|
|
.unwrap();
|
|
|
|
previously_active_pane.set_should_render(true);
|
|
// we render the full viewport to remove any ui elements that might have been
|
|
// there before (eg. another user's cursor)
|
|
previously_active_pane.render_full_viewport();
|
|
|
|
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
|
next_active_pane.set_should_render(true);
|
|
// we render the full viewport to remove any ui elements that might have been
|
|
// there before (eg. another user's cursor)
|
|
next_active_pane.render_full_viewport();
|
|
|
|
self.focus_pane(p, client_id);
|
|
self.set_pane_active_at(p);
|
|
|
|
true
|
|
},
|
|
None => false,
|
|
}
|
|
},
|
|
None => false,
|
|
}
|
|
}
|
|
pub fn move_focus_down(&mut self, client_id: ClientId) -> bool {
|
|
match self.get_active_pane_id(client_id) {
|
|
Some(active_pane_id) => {
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_index = pane_grid.next_selectable_pane_id_below(&active_pane_id);
|
|
match next_index {
|
|
Some(p) => {
|
|
// render previously active pane so that its frame does not remain actively
|
|
// colored
|
|
let previously_active_pane = self
|
|
.panes
|
|
.get_mut(self.active_panes.get(&client_id).unwrap())
|
|
.unwrap();
|
|
|
|
previously_active_pane.set_should_render(true);
|
|
// we render the full viewport to remove any ui elements that might have been
|
|
// there before (eg. another user's cursor)
|
|
previously_active_pane.render_full_viewport();
|
|
|
|
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
|
next_active_pane.set_should_render(true);
|
|
// we render the full viewport to remove any ui elements that might have been
|
|
// there before (eg. another user's cursor)
|
|
next_active_pane.render_full_viewport();
|
|
|
|
self.focus_pane(p, client_id);
|
|
self.set_pane_active_at(p);
|
|
|
|
true
|
|
},
|
|
None => false,
|
|
}
|
|
},
|
|
None => false,
|
|
}
|
|
}
|
|
pub fn move_focus_up(&mut self, client_id: ClientId) -> bool {
|
|
match self.get_active_pane_id(client_id) {
|
|
Some(active_pane_id) => {
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_index = pane_grid.next_selectable_pane_id_above(&active_pane_id);
|
|
match next_index {
|
|
Some(p) => {
|
|
// render previously active pane so that its frame does not remain actively
|
|
// colored
|
|
let previously_active_pane = self
|
|
.panes
|
|
.get_mut(self.active_panes.get(&client_id).unwrap())
|
|
.unwrap();
|
|
|
|
previously_active_pane.set_should_render(true);
|
|
// we render the full viewport to remove any ui elements that might have been
|
|
// there before (eg. another user's cursor)
|
|
previously_active_pane.render_full_viewport();
|
|
|
|
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
|
next_active_pane.set_should_render(true);
|
|
// we render the full viewport to remove any ui elements that might have been
|
|
// there before (eg. another user's cursor)
|
|
next_active_pane.render_full_viewport();
|
|
|
|
self.focus_pane(p, client_id);
|
|
self.set_pane_active_at(p);
|
|
|
|
true
|
|
},
|
|
None => false,
|
|
}
|
|
},
|
|
None => false,
|
|
}
|
|
}
|
|
pub fn move_focus_right(&mut self, client_id: ClientId) -> bool {
|
|
match self.get_active_pane_id(client_id) {
|
|
Some(active_pane_id) => {
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_index = pane_grid.next_selectable_pane_id_to_the_right(&active_pane_id);
|
|
match next_index {
|
|
Some(p) => {
|
|
// render previously active pane so that its frame does not remain actively
|
|
// colored
|
|
let previously_active_pane = self
|
|
.panes
|
|
.get_mut(self.active_panes.get(&client_id).unwrap())
|
|
.unwrap();
|
|
|
|
previously_active_pane.set_should_render(true);
|
|
// we render the full viewport to remove any ui elements that might have been
|
|
// there before (eg. another user's cursor)
|
|
previously_active_pane.render_full_viewport();
|
|
|
|
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
|
next_active_pane.set_should_render(true);
|
|
// we render the full viewport to remove any ui elements that might have been
|
|
// there before (eg. another user's cursor)
|
|
next_active_pane.render_full_viewport();
|
|
|
|
self.focus_pane(p, client_id);
|
|
self.set_pane_active_at(p);
|
|
|
|
true
|
|
},
|
|
None => false,
|
|
}
|
|
},
|
|
None => false,
|
|
}
|
|
}
|
|
pub fn move_active_pane(&mut self, client_id: ClientId) {
|
|
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let new_position_id = pane_grid.next_selectable_pane_id(&active_pane_id);
|
|
let current_position = self.panes.get(&active_pane_id).unwrap();
|
|
let prev_geom = current_position.position_and_size();
|
|
let prev_geom_override = current_position.geom_override();
|
|
|
|
let new_position = self.panes.get_mut(&new_position_id).unwrap();
|
|
let next_geom = new_position.position_and_size();
|
|
let next_geom_override = new_position.geom_override();
|
|
new_position.set_geom(prev_geom);
|
|
if let Some(geom) = prev_geom_override {
|
|
new_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
|
new_position.set_should_render(true);
|
|
|
|
let current_position = self.panes.get_mut(&active_pane_id).unwrap();
|
|
current_position.set_geom(next_geom);
|
|
if let Some(geom) = next_geom_override {
|
|
current_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
|
current_position.set_should_render(true);
|
|
self.set_pane_frames(self.draw_pane_frames);
|
|
}
|
|
pub fn move_active_pane_down(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_index = pane_grid.next_selectable_pane_id_below(&active_pane_id);
|
|
if let Some(p) = next_index {
|
|
let active_pane_id = self.active_panes.get(&client_id).unwrap();
|
|
let current_position = self.panes.get(active_pane_id).unwrap();
|
|
let prev_geom = current_position.position_and_size();
|
|
let prev_geom_override = current_position.geom_override();
|
|
|
|
let new_position = self.panes.get_mut(&p).unwrap();
|
|
let next_geom = new_position.position_and_size();
|
|
let next_geom_override = new_position.geom_override();
|
|
new_position.set_geom(prev_geom);
|
|
if let Some(geom) = prev_geom_override {
|
|
new_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
|
new_position.set_should_render(true);
|
|
|
|
let current_position = self.panes.get_mut(active_pane_id).unwrap();
|
|
current_position.set_geom(next_geom);
|
|
if let Some(geom) = next_geom_override {
|
|
current_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
|
current_position.set_should_render(true);
|
|
self.set_pane_frames(self.draw_pane_frames);
|
|
}
|
|
}
|
|
}
|
|
pub fn move_active_pane_left(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_index = pane_grid.next_selectable_pane_id_to_the_left(&active_pane_id);
|
|
if let Some(p) = next_index {
|
|
let active_pane_id = self.active_panes.get(&client_id).unwrap();
|
|
let current_position = self.panes.get(active_pane_id).unwrap();
|
|
let prev_geom = current_position.position_and_size();
|
|
let prev_geom_override = current_position.geom_override();
|
|
|
|
let new_position = self.panes.get_mut(&p).unwrap();
|
|
let next_geom = new_position.position_and_size();
|
|
let next_geom_override = new_position.geom_override();
|
|
new_position.set_geom(prev_geom);
|
|
if let Some(geom) = prev_geom_override {
|
|
new_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
|
new_position.set_should_render(true);
|
|
|
|
let current_position = self.panes.get_mut(active_pane_id).unwrap();
|
|
current_position.set_geom(next_geom);
|
|
if let Some(geom) = next_geom_override {
|
|
current_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
|
current_position.set_should_render(true);
|
|
self.set_pane_frames(self.draw_pane_frames);
|
|
}
|
|
}
|
|
}
|
|
pub fn move_active_pane_right(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_index = pane_grid.next_selectable_pane_id_to_the_right(&active_pane_id);
|
|
if let Some(p) = next_index {
|
|
let active_pane_id = self.active_panes.get(&client_id).unwrap();
|
|
let current_position = self.panes.get(active_pane_id).unwrap();
|
|
let prev_geom = current_position.position_and_size();
|
|
let prev_geom_override = current_position.geom_override();
|
|
|
|
let new_position = self.panes.get_mut(&p).unwrap();
|
|
let next_geom = new_position.position_and_size();
|
|
let next_geom_override = new_position.geom_override();
|
|
new_position.set_geom(prev_geom);
|
|
if let Some(geom) = prev_geom_override {
|
|
new_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
|
new_position.set_should_render(true);
|
|
|
|
let current_position = self.panes.get_mut(active_pane_id).unwrap();
|
|
current_position.set_geom(next_geom);
|
|
if let Some(geom) = next_geom_override {
|
|
current_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
|
current_position.set_should_render(true);
|
|
self.set_pane_frames(self.draw_pane_frames);
|
|
}
|
|
}
|
|
}
|
|
pub fn move_active_pane_up(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
let pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
let next_index = pane_grid.next_selectable_pane_id_above(&active_pane_id);
|
|
if let Some(p) = next_index {
|
|
let active_pane_id = self.active_panes.get(&client_id).unwrap();
|
|
let current_position = self.panes.get(active_pane_id).unwrap();
|
|
let prev_geom = current_position.position_and_size();
|
|
let prev_geom_override = current_position.geom_override();
|
|
|
|
let new_position = self.panes.get_mut(&p).unwrap();
|
|
let next_geom = new_position.position_and_size();
|
|
let next_geom_override = new_position.geom_override();
|
|
new_position.set_geom(prev_geom);
|
|
if let Some(geom) = prev_geom_override {
|
|
new_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
|
new_position.set_should_render(true);
|
|
|
|
let current_position = self.panes.get_mut(active_pane_id).unwrap();
|
|
current_position.set_geom(next_geom);
|
|
if let Some(geom) = next_geom_override {
|
|
current_position.set_geom_override(geom);
|
|
}
|
|
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
|
current_position.set_should_render(true);
|
|
self.set_pane_frames(self.draw_pane_frames);
|
|
}
|
|
}
|
|
}
|
|
pub fn move_clients_out_of_pane(&mut self, pane_id: PaneId) {
|
|
let active_panes: Vec<(ClientId, PaneId)> = self
|
|
.active_panes
|
|
.iter()
|
|
.map(|(cid, pid)| (*cid, *pid))
|
|
.collect();
|
|
|
|
// find the most recently active pane
|
|
let mut next_active_pane_candidates: Vec<(&PaneId, &Box<dyn Pane>)> = self
|
|
.panes
|
|
.iter()
|
|
.filter(|(p_id, p)| !self.panes_to_hide.contains(p_id) && p.selectable())
|
|
.collect();
|
|
next_active_pane_candidates.sort_by(|(_pane_id_a, pane_a), (_pane_id_b, pane_b)| {
|
|
pane_a.active_at().cmp(&pane_b.active_at())
|
|
});
|
|
let next_active_pane_id = next_active_pane_candidates
|
|
.last()
|
|
.map(|(pane_id, _pane)| **pane_id);
|
|
|
|
match next_active_pane_id {
|
|
Some(next_active_pane) => {
|
|
for (client_id, active_pane_id) in active_panes {
|
|
if active_pane_id == pane_id {
|
|
self.active_panes
|
|
.insert(client_id, next_active_pane, &mut self.panes);
|
|
}
|
|
}
|
|
},
|
|
None => self.active_panes.clear(&mut self.panes),
|
|
}
|
|
}
|
|
pub fn extract_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
|
self.reset_boundaries();
|
|
self.panes.remove(&pane_id)
|
|
}
|
|
pub fn remove_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
|
let mut pane_grid = TiledPaneGrid::new(
|
|
&mut self.panes,
|
|
&self.panes_to_hide,
|
|
*self.display_area.borrow(),
|
|
*self.viewport.borrow(),
|
|
);
|
|
if pane_grid.fill_space_over_pane(pane_id) {
|
|
// successfully filled space over pane
|
|
let closed_pane = self.panes.remove(&pane_id);
|
|
self.move_clients_out_of_pane(pane_id);
|
|
self.set_pane_frames(self.draw_pane_frames); // recalculate pane frames and update size
|
|
closed_pane
|
|
} else {
|
|
self.panes.remove(&pane_id);
|
|
// this is a bit of a roundabout way to say: this is the last pane and so the tab
|
|
// should be destroyed
|
|
self.active_panes.clear(&mut self.panes);
|
|
None
|
|
}
|
|
}
|
|
pub fn hold_pane(
|
|
&mut self,
|
|
pane_id: PaneId,
|
|
exit_status: Option<i32>,
|
|
is_first_run: bool,
|
|
run_command: RunCommand,
|
|
) {
|
|
self.panes
|
|
.get_mut(&pane_id)
|
|
.map(|p| p.hold(exit_status, is_first_run, run_command));
|
|
}
|
|
pub fn panes_to_hide_contains(&self, pane_id: PaneId) -> bool {
|
|
self.panes_to_hide.contains(&pane_id)
|
|
}
|
|
pub fn fullscreen_is_active(&self) -> bool {
|
|
self.fullscreen_is_active
|
|
}
|
|
pub fn unset_fullscreen(&mut self) {
|
|
if self.fullscreen_is_active {
|
|
let first_client_id = {
|
|
let connected_clients = self.connected_clients.borrow();
|
|
*connected_clients.iter().next().unwrap()
|
|
};
|
|
let active_pane_id = self.get_active_pane_id(first_client_id).unwrap();
|
|
let panes_to_hide: Vec<_> = self.panes_to_hide.iter().copied().collect();
|
|
for pane_id in panes_to_hide {
|
|
let pane = self.get_pane_mut(pane_id).unwrap();
|
|
pane.set_should_render(true);
|
|
pane.set_should_render_boundaries(true);
|
|
}
|
|
let viewport_pane_ids: Vec<_> = self
|
|
.panes
|
|
.keys()
|
|
.copied()
|
|
.into_iter()
|
|
.filter(|id| {
|
|
!is_inside_viewport(&*self.viewport.borrow(), self.get_pane(*id).unwrap())
|
|
})
|
|
.collect();
|
|
for pid in viewport_pane_ids {
|
|
let viewport_pane = self.get_pane_mut(pid).unwrap();
|
|
viewport_pane.reset_size_and_position_override();
|
|
}
|
|
self.panes_to_hide.clear();
|
|
let active_terminal = self.get_pane_mut(active_pane_id).unwrap();
|
|
active_terminal.reset_size_and_position_override();
|
|
self.set_force_render();
|
|
let display_area = *self.display_area.borrow();
|
|
self.resize(display_area);
|
|
self.fullscreen_is_active = false;
|
|
}
|
|
}
|
|
pub fn toggle_active_pane_fullscreen(&mut self, client_id: ClientId) {
|
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
|
if self.fullscreen_is_active {
|
|
self.unset_fullscreen();
|
|
} else {
|
|
let pane_ids_to_hide = self.panes.iter().filter_map(|(&id, _pane)| {
|
|
if id != active_pane_id
|
|
&& is_inside_viewport(&*self.viewport.borrow(), self.get_pane(id).unwrap())
|
|
{
|
|
Some(id)
|
|
} else {
|
|
None
|
|
}
|
|
});
|
|
self.panes_to_hide = pane_ids_to_hide.collect();
|
|
if self.panes_to_hide.is_empty() {
|
|
// nothing to do, pane is already as fullscreen as it can be, let's bail
|
|
return;
|
|
} else {
|
|
// For all of the panes outside of the viewport staying on the fullscreen
|
|
// screen, switch them to using override positions as well so that the resize
|
|
// system doesn't get confused by viewport and old panes that no longer line up
|
|
let viewport_pane_ids: Vec<_> = self
|
|
.panes
|
|
.keys()
|
|
.copied()
|
|
.into_iter()
|
|
.filter(|id| {
|
|
!is_inside_viewport(
|
|
&*self.viewport.borrow(),
|
|
self.get_pane(*id).unwrap(),
|
|
)
|
|
})
|
|
.collect();
|
|
for pid in viewport_pane_ids {
|
|
let viewport_pane = self.get_pane_mut(pid).unwrap();
|
|
viewport_pane.set_geom_override(viewport_pane.position_and_size());
|
|
}
|
|
let viewport = { *self.viewport.borrow() };
|
|
let active_terminal = self.get_pane_mut(active_pane_id).unwrap();
|
|
let full_screen_geom = PaneGeom {
|
|
x: viewport.x,
|
|
y: viewport.y,
|
|
..Default::default()
|
|
};
|
|
active_terminal.set_geom_override(full_screen_geom);
|
|
}
|
|
let connected_client_list: Vec<ClientId> =
|
|
{ self.connected_clients.borrow().iter().copied().collect() };
|
|
for client_id in connected_client_list {
|
|
self.focus_pane(active_pane_id, client_id);
|
|
}
|
|
self.set_force_render();
|
|
let display_area = *self.display_area.borrow();
|
|
self.resize(display_area);
|
|
self.fullscreen_is_active = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn switch_next_pane_fullscreen(&mut self, client_id: ClientId) {
|
|
self.unset_fullscreen();
|
|
self.focus_next_pane(client_id);
|
|
self.toggle_active_pane_fullscreen(client_id);
|
|
}
|
|
|
|
pub fn switch_prev_pane_fullscreen(&mut self, client_id: ClientId) {
|
|
self.unset_fullscreen();
|
|
self.focus_previous_pane(client_id);
|
|
self.toggle_active_pane_fullscreen(client_id);
|
|
}
|
|
|
|
pub fn panes_to_hide_count(&self) -> usize {
|
|
self.panes_to_hide.len()
|
|
}
|
|
pub fn add_to_hidden_panels(&mut self, pid: PaneId) {
|
|
self.panes_to_hide.insert(pid);
|
|
}
|
|
pub fn remove_from_hidden_panels(&mut self, pid: PaneId) {
|
|
self.panes_to_hide.remove(&pid);
|
|
}
|
|
pub fn unfocus_all_panes(&mut self) {
|
|
self.active_panes.unfocus_all_panes(&mut self.panes);
|
|
}
|
|
pub fn focus_all_panes(&mut self) {
|
|
self.active_panes.focus_all_panes(&mut self.panes);
|
|
}
|
|
fn move_clients_between_panes(&mut self, from_pane_id: PaneId, to_pane_id: PaneId) {
|
|
let clients_in_pane: Vec<ClientId> = self
|
|
.active_panes
|
|
.iter()
|
|
.filter(|(_cid, pid)| **pid == from_pane_id)
|
|
.map(|(cid, _pid)| *cid)
|
|
.collect();
|
|
for client_id in clients_in_pane {
|
|
self.active_panes.remove(&client_id, &mut self.panes);
|
|
self.active_panes
|
|
.insert(client_id, to_pane_id, &mut self.panes);
|
|
}
|
|
}
|
|
fn reset_boundaries(&mut self) {
|
|
self.client_id_to_boundaries.clear();
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::borrowed_box)]
|
|
pub fn is_inside_viewport(viewport: &Viewport, pane: &Box<dyn Pane>) -> bool {
|
|
let pane_position_and_size = pane.current_geom();
|
|
pane_position_and_size.y >= viewport.y
|
|
&& pane_position_and_size.y + pane_position_and_size.rows.as_usize()
|
|
<= viewport.y + viewport.rows
|
|
}
|
|
|
|
pub fn pane_geom_is_inside_viewport(viewport: &Viewport, geom: &PaneGeom) -> bool {
|
|
geom.y >= viewport.y
|
|
&& geom.y + geom.rows.as_usize() <= viewport.y + viewport.rows
|
|
&& geom.x >= viewport.x
|
|
&& geom.x + geom.cols.as_usize() <= viewport.x + viewport.cols
|
|
}
|