fix(panes): various fixes for rendering stacked panes without pane frames (#4035)
* initial work * fix rendering issues with stacked panes without pane frames * make mouse clicking work * test: rendering stacked panes without frames * style(fmt): rustfmt * docs(changelog): update pr
This commit is contained in:
parent
9f900a7325
commit
b7cc3f3a62
13 changed files with 479 additions and 56 deletions
|
|
@ -38,6 +38,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
* chore(repo): update some dependencies (https://github.com/zellij-org/zellij/pull/4019)
|
* chore(repo): update some dependencies (https://github.com/zellij-org/zellij/pull/4019)
|
||||||
* fix(grid): reap sixel images on clear (https://github.com/zellij-org/zellij/pull/3982)
|
* fix(grid): reap sixel images on clear (https://github.com/zellij-org/zellij/pull/3982)
|
||||||
* chore(repo): remove compile warnings (https://github.com/zellij-org/zellij/pull/4026)
|
* chore(repo): remove compile warnings (https://github.com/zellij-org/zellij/pull/4026)
|
||||||
|
* fix(panes): properly render stacked panes when pane frames are disabled (https://github.com/zellij-org/zellij/pull/4035)
|
||||||
|
|
||||||
## [0.41.2] - 2024-11-19
|
## [0.41.2] - 2024-11-19
|
||||||
* fix(input): keypresses not being identified properly with kitty keyboard protocol in some terminals (https://github.com/zellij-org/zellij/pull/3725)
|
* fix(input): keypresses not being identified properly with kitty keyboard protocol in some terminals (https://github.com/zellij-org/zellij/pull/3725)
|
||||||
|
|
|
||||||
|
|
@ -412,13 +412,7 @@ impl Pane for PluginPane {
|
||||||
self.pane_name.clone()
|
self.pane_name.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut frame_geom = self.current_geom();
|
let frame_geom = self.current_geom();
|
||||||
if !frame_params.should_draw_pane_frames {
|
|
||||||
// in this case the width of the frame needs not include the pane corners
|
|
||||||
frame_geom
|
|
||||||
.cols
|
|
||||||
.set_inner(frame_geom.cols.as_usize().saturating_sub(1));
|
|
||||||
}
|
|
||||||
let is_pinned = frame_geom.is_pinned;
|
let is_pinned = frame_geom.is_pinned;
|
||||||
let mut frame = PaneFrame::new(
|
let mut frame = PaneFrame::new(
|
||||||
frame_geom.into(),
|
frame_geom.into(),
|
||||||
|
|
@ -613,6 +607,10 @@ impl Pane for PluginPane {
|
||||||
self.resize_grids();
|
self.resize_grids();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_content_offset(&self) -> Offset {
|
||||||
|
self.content_offset
|
||||||
|
}
|
||||||
|
|
||||||
fn store_pane_name(&mut self) {
|
fn store_pane_name(&mut self) {
|
||||||
if self.pane_name != self.prev_pane_name {
|
if self.pane_name != self.prev_pane_name {
|
||||||
self.prev_pane_name = self.pane_name.clone()
|
self.prev_pane_name = self.pane_name.clone()
|
||||||
|
|
|
||||||
|
|
@ -612,6 +612,10 @@ impl Pane for TerminalPane {
|
||||||
self.reflow_lines();
|
self.reflow_lines();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_content_offset(&self) -> Offset {
|
||||||
|
self.content_offset
|
||||||
|
}
|
||||||
|
|
||||||
fn store_pane_name(&mut self) {
|
fn store_pane_name(&mut self) {
|
||||||
if self.pane_name != self.prev_pane_name {
|
if self.pane_name != self.prev_pane_name {
|
||||||
self.prev_pane_name = self.pane_name.clone()
|
self.prev_pane_name = self.pane_name.clone()
|
||||||
|
|
|
||||||
|
|
@ -443,6 +443,11 @@ impl TiledPanes {
|
||||||
pub fn set_pane_frames(&mut self, draw_pane_frames: bool) {
|
pub fn set_pane_frames(&mut self, draw_pane_frames: bool) {
|
||||||
self.draw_pane_frames = draw_pane_frames;
|
self.draw_pane_frames = draw_pane_frames;
|
||||||
let viewport = *self.viewport.borrow();
|
let viewport = *self.viewport.borrow();
|
||||||
|
let position_and_sizes_of_stacks = {
|
||||||
|
StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide)
|
||||||
|
.positions_and_sizes_of_all_stacks()
|
||||||
|
.unwrap_or_else(|| Default::default())
|
||||||
|
};
|
||||||
for pane in self.panes.values_mut() {
|
for pane in self.panes.values_mut() {
|
||||||
if !pane.borderless() {
|
if !pane.borderless() {
|
||||||
pane.set_frame(draw_pane_frames);
|
pane.set_frame(draw_pane_frames);
|
||||||
|
|
@ -464,12 +469,27 @@ impl TiledPanes {
|
||||||
// no draw_pane_frames and this pane should have a separation to other panes
|
// 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
|
// according to its position in the viewport (eg. no separation if its at the
|
||||||
// viewport bottom) - offset its content accordingly
|
// viewport bottom) - offset its content accordingly
|
||||||
let position_and_size = pane.current_geom();
|
let mut position_and_size = pane.current_geom();
|
||||||
|
let is_stacked = position_and_size.is_stacked();
|
||||||
|
let is_flexible = !position_and_size.rows.is_fixed();
|
||||||
|
if let Some(position_and_size_of_stack) = position_and_size
|
||||||
|
.stacked
|
||||||
|
.and_then(|s_id| position_and_sizes_of_stacks.get(&s_id))
|
||||||
|
{
|
||||||
|
// we want to check the offset against the position_and_size of the whole
|
||||||
|
// stack rather than the pane, because the stack needs to have a consistent
|
||||||
|
// offset with itself
|
||||||
|
position_and_size = *position_and_size_of_stack;
|
||||||
|
};
|
||||||
let (pane_columns_offset, pane_rows_offset) =
|
let (pane_columns_offset, pane_rows_offset) =
|
||||||
pane_content_offset(&position_and_size, &viewport);
|
pane_content_offset(&position_and_size, &viewport);
|
||||||
if !draw_pane_frames && pane.current_geom().is_stacked() {
|
if is_stacked && is_flexible {
|
||||||
// stacked panes should always leave 1 top row for a title
|
// 1 to save room for the pane title
|
||||||
pane.set_content_offset(Offset::shift_right_and_top(pane_columns_offset, 1));
|
pane.set_content_offset(Offset::shift_right_top_and_bottom(
|
||||||
|
pane_columns_offset,
|
||||||
|
1,
|
||||||
|
pane_rows_offset,
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
pane.set_content_offset(Offset::shift(pane_rows_offset, pane_columns_offset));
|
pane.set_content_offset(Offset::shift(pane_rows_offset, pane_columns_offset));
|
||||||
}
|
}
|
||||||
|
|
@ -848,10 +868,14 @@ impl TiledPanes {
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
let (stacked_pane_ids_under_flexible_pane, stacked_pane_ids_over_flexible_pane) = {
|
let (stacked_pane_ids_under_flexible_pane, stacked_pane_ids_over_flexible_pane) = {
|
||||||
// TODO: do not recalculate this every time on render
|
|
||||||
StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide)
|
StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide)
|
||||||
.stacked_pane_ids_under_and_over_flexible_panes()
|
.stacked_pane_ids_under_and_over_flexible_panes()
|
||||||
.unwrap() // TODO: no unwrap
|
.with_context(err_context)?
|
||||||
|
};
|
||||||
|
let (stacked_pane_ids_on_top_of_stacks, stacked_pane_ids_on_bottom_of_stacks) = {
|
||||||
|
StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide)
|
||||||
|
.stacked_pane_ids_on_top_and_bottom_of_stacks()
|
||||||
|
.with_context(err_context)?
|
||||||
};
|
};
|
||||||
for (kind, pane) in self.panes.iter_mut() {
|
for (kind, pane) in self.panes.iter_mut() {
|
||||||
if !self.panes_to_hide.contains(&pane.pid()) {
|
if !self.panes_to_hide.contains(&pane.pid()) {
|
||||||
|
|
@ -859,8 +883,14 @@ impl TiledPanes {
|
||||||
stacked_pane_ids_under_flexible_pane.contains(&pane.pid());
|
stacked_pane_ids_under_flexible_pane.contains(&pane.pid());
|
||||||
let pane_is_stacked_over =
|
let pane_is_stacked_over =
|
||||||
stacked_pane_ids_over_flexible_pane.contains(&pane.pid());
|
stacked_pane_ids_over_flexible_pane.contains(&pane.pid());
|
||||||
|
let pane_is_on_top_of_stack =
|
||||||
|
stacked_pane_ids_on_top_of_stacks.contains(&pane.pid());
|
||||||
|
let pane_is_on_bottom_of_stack =
|
||||||
|
stacked_pane_ids_on_bottom_of_stacks.contains(&pane.pid());
|
||||||
let should_draw_pane_frames = self.draw_pane_frames;
|
let should_draw_pane_frames = self.draw_pane_frames;
|
||||||
let pane_is_stacked = pane.current_geom().is_stacked();
|
let pane_is_stacked = pane.current_geom().is_stacked();
|
||||||
|
let pane_is_one_liner_in_stack =
|
||||||
|
pane_is_stacked && pane.current_geom().rows.is_fixed();
|
||||||
let mut pane_contents_and_ui = PaneContentsAndUi::new(
|
let mut pane_contents_and_ui = PaneContentsAndUi::new(
|
||||||
pane,
|
pane,
|
||||||
output,
|
output,
|
||||||
|
|
@ -882,10 +912,12 @@ impl TiledPanes {
|
||||||
let err_context =
|
let err_context =
|
||||||
|| format!("failed to render tiled panes for client {client_id}");
|
|| format!("failed to render tiled panes for client {client_id}");
|
||||||
if let PaneId::Plugin(..) = kind {
|
if let PaneId::Plugin(..) = kind {
|
||||||
|
if !pane_is_one_liner_in_stack {
|
||||||
pane_contents_and_ui
|
pane_contents_and_ui
|
||||||
.render_pane_contents_for_client(*client_id)
|
.render_pane_contents_for_client(*client_id)
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let is_floating = false;
|
let is_floating = false;
|
||||||
if self.draw_pane_frames {
|
if self.draw_pane_frames {
|
||||||
pane_contents_and_ui
|
pane_contents_and_ui
|
||||||
|
|
@ -916,6 +948,8 @@ impl TiledPanes {
|
||||||
client_mode,
|
client_mode,
|
||||||
boundaries,
|
boundaries,
|
||||||
self.session_is_mirrored,
|
self.session_is_mirrored,
|
||||||
|
pane_is_on_top_of_stack,
|
||||||
|
pane_is_on_bottom_of_stack,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let boundaries = client_id_to_boundaries
|
let boundaries = client_id_to_boundaries
|
||||||
|
|
@ -926,6 +960,8 @@ impl TiledPanes {
|
||||||
client_mode,
|
client_mode,
|
||||||
boundaries,
|
boundaries,
|
||||||
self.session_is_mirrored,
|
self.session_is_mirrored,
|
||||||
|
pane_is_on_top_of_stack,
|
||||||
|
pane_is_on_bottom_of_stack,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pane_contents_and_ui.render_terminal_title_if_needed(
|
pane_contents_and_ui.render_terminal_title_if_needed(
|
||||||
|
|
@ -940,12 +976,16 @@ impl TiledPanes {
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
}
|
}
|
||||||
if let PaneId::Terminal(..) = kind {
|
if let PaneId::Terminal(..) = kind {
|
||||||
|
if !pane_is_one_liner_in_stack {
|
||||||
pane_contents_and_ui
|
pane_contents_and_ui
|
||||||
.render_pane_contents_to_multiple_clients(connected_clients.iter().copied())
|
.render_pane_contents_to_multiple_clients(
|
||||||
|
connected_clients.iter().copied(),
|
||||||
|
)
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// render boundaries if needed
|
// render boundaries if needed
|
||||||
for (client_id, boundaries) in client_id_to_boundaries {
|
for (client_id, boundaries) in client_id_to_boundaries {
|
||||||
let boundaries_to_render = boundaries
|
let boundaries_to_render = boundaries
|
||||||
|
|
@ -2426,6 +2466,12 @@ impl TiledPanes {
|
||||||
fn is_connected(&self, client_id: &ClientId) -> bool {
|
fn is_connected(&self, client_id: &ClientId) -> bool {
|
||||||
self.connected_clients.borrow().contains(&client_id)
|
self.connected_clients.borrow().contains(&client_id)
|
||||||
}
|
}
|
||||||
|
pub fn stacked_pane_ids_under_and_over_flexible_panes(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<(HashSet<PaneId>, HashSet<PaneId>)> {
|
||||||
|
StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide)
|
||||||
|
.stacked_pane_ids_under_and_over_flexible_panes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::borrowed_box)]
|
#[allow(clippy::borrowed_box)]
|
||||||
|
|
|
||||||
|
|
@ -416,6 +416,25 @@ impl<'a> StackedPanes<'a> {
|
||||||
stacked_pane_ids_over_flexible_panes,
|
stacked_pane_ids_over_flexible_panes,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
pub fn stacked_pane_ids_on_top_and_bottom_of_stacks(
|
||||||
|
&self,
|
||||||
|
) -> Result<(HashSet<PaneId>, HashSet<PaneId>)> {
|
||||||
|
let mut stacked_pane_ids_on_top_of_stacks = HashSet::new();
|
||||||
|
let mut stacked_pane_ids_on_bottom_of_stacks = HashSet::new();
|
||||||
|
let all_stacks = self.get_all_stacks()?;
|
||||||
|
for stack in all_stacks {
|
||||||
|
if let Some((first_pane_id, _pane)) = stack.iter().next() {
|
||||||
|
stacked_pane_ids_on_top_of_stacks.insert(*first_pane_id);
|
||||||
|
}
|
||||||
|
if let Some((last_pane_id, _pane)) = stack.iter().last() {
|
||||||
|
stacked_pane_ids_on_bottom_of_stacks.insert(*last_pane_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((
|
||||||
|
stacked_pane_ids_on_top_of_stacks,
|
||||||
|
stacked_pane_ids_on_bottom_of_stacks,
|
||||||
|
))
|
||||||
|
}
|
||||||
pub fn make_room_for_new_pane(&mut self) -> Result<PaneGeom> {
|
pub fn make_room_for_new_pane(&mut self) -> Result<PaneGeom> {
|
||||||
let err_context = || format!("Failed to add pane to stack");
|
let err_context = || format!("Failed to add pane to stack");
|
||||||
let all_stacks = self.get_all_stacks()?;
|
let all_stacks = self.get_all_stacks()?;
|
||||||
|
|
@ -804,6 +823,19 @@ impl<'a> StackedPanes<'a> {
|
||||||
}
|
}
|
||||||
highest_stack_id
|
highest_stack_id
|
||||||
}
|
}
|
||||||
|
pub fn positions_and_sizes_of_all_stacks(&self) -> Option<HashMap<usize, PaneGeom>> {
|
||||||
|
let panes = self.panes.borrow();
|
||||||
|
let mut positions_and_sizes_of_all_stacks = HashMap::new();
|
||||||
|
for pane in panes.values() {
|
||||||
|
if let Some(stack_id) = pane.current_geom().stacked {
|
||||||
|
if !positions_and_sizes_of_all_stacks.contains_key(&stack_id) {
|
||||||
|
positions_and_sizes_of_all_stacks
|
||||||
|
.insert(stack_id, self.position_and_size_of_stack(&pane.pid())?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(positions_and_sizes_of_all_stacks)
|
||||||
|
}
|
||||||
fn reset_stack_size(
|
fn reset_stack_size(
|
||||||
&self,
|
&self,
|
||||||
new_position_and_size_of_stack: &PaneGeom,
|
new_position_and_size_of_stack: &PaneGeom,
|
||||||
|
|
|
||||||
|
|
@ -3859,14 +3859,17 @@ pub(crate) fn screen_thread_main(
|
||||||
screen.unblock_input()?;
|
screen.unblock_input()?;
|
||||||
},
|
},
|
||||||
ScreenInstruction::MouseEvent(event, client_id) => {
|
ScreenInstruction::MouseEvent(event, client_id) => {
|
||||||
let mouse_effect = screen
|
match screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.and_then(|tab| tab.handle_mouse_event(&event, client_id))?;
|
.and_then(|tab| tab.handle_mouse_event(&event, client_id))
|
||||||
|
{
|
||||||
|
Ok(mouse_effect) => {
|
||||||
if mouse_effect.state_changed {
|
if mouse_effect.state_changed {
|
||||||
screen.log_and_report_session_state()?;
|
screen.log_and_report_session_state()?;
|
||||||
}
|
}
|
||||||
if !mouse_effect.leave_clipboard_message {
|
if !mouse_effect.leave_clipboard_message {
|
||||||
let _ = screen
|
let _ =
|
||||||
|
screen
|
||||||
.bus
|
.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_plugin(PluginInstruction::Update(vec![(
|
.send_to_plugin(PluginInstruction::Update(vec![(
|
||||||
|
|
@ -3875,7 +3878,12 @@ pub(crate) fn screen_thread_main(
|
||||||
Event::InputReceived,
|
Event::InputReceived,
|
||||||
)]));
|
)]));
|
||||||
}
|
}
|
||||||
screen.render(None)?;
|
screen.render(None).non_fatal();
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to process MouseEvent: {}", e);
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ScreenInstruction::Copy(client_id) => {
|
ScreenInstruction::Copy(client_id) => {
|
||||||
active_tab!(screen, client_id, |tab: &mut Tab| tab
|
active_tab!(screen, client_id, |tab: &mut Tab| tab
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,9 @@ pub trait Pane {
|
||||||
fn set_active_at(&mut self, instant: Instant);
|
fn set_active_at(&mut self, instant: Instant);
|
||||||
fn set_frame(&mut self, frame: bool);
|
fn set_frame(&mut self, frame: bool);
|
||||||
fn set_content_offset(&mut self, offset: Offset);
|
fn set_content_offset(&mut self, offset: Offset);
|
||||||
|
fn get_content_offset(&self) -> Offset {
|
||||||
|
Offset::default()
|
||||||
|
}
|
||||||
fn cursor_shape_csi(&self) -> String {
|
fn cursor_shape_csi(&self) -> String {
|
||||||
"\u{1b}[0 q".to_string() // default to non blinking block
|
"\u{1b}[0 q".to_string() // default to non blinking block
|
||||||
}
|
}
|
||||||
|
|
@ -328,9 +331,15 @@ pub trait Pane {
|
||||||
fn right_boundary_x_coords(&self) -> usize {
|
fn right_boundary_x_coords(&self) -> usize {
|
||||||
self.x() + self.cols()
|
self.x() + self.cols()
|
||||||
}
|
}
|
||||||
|
fn right_boundary_x_content_coords(&self) -> usize {
|
||||||
|
self.get_content_x() + self.get_content_columns()
|
||||||
|
}
|
||||||
fn bottom_boundary_y_coords(&self) -> usize {
|
fn bottom_boundary_y_coords(&self) -> usize {
|
||||||
self.y() + self.rows()
|
self.y() + self.rows()
|
||||||
}
|
}
|
||||||
|
fn bottom_boundary_y_content_coords(&self) -> usize {
|
||||||
|
self.get_content_y() + self.get_content_rows()
|
||||||
|
}
|
||||||
fn is_right_of(&self, other: &dyn Pane) -> bool {
|
fn is_right_of(&self, other: &dyn Pane) -> bool {
|
||||||
self.x() > other.x()
|
self.x() > other.x()
|
||||||
}
|
}
|
||||||
|
|
@ -3350,7 +3359,11 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pane_id_at(&self, point: &Position, search_selectable: bool) -> Result<Option<PaneId>> {
|
fn get_pane_id_at(
|
||||||
|
&mut self,
|
||||||
|
point: &Position,
|
||||||
|
search_selectable: bool,
|
||||||
|
) -> Result<Option<PaneId>> {
|
||||||
let err_context = || format!("failed to get id of pane at position {point:?}");
|
let err_context = || format!("failed to get id of pane at position {point:?}");
|
||||||
|
|
||||||
if self.tiled_panes.fullscreen_is_active()
|
if self.tiled_panes.fullscreen_is_active()
|
||||||
|
|
@ -3368,15 +3381,50 @@ impl Tab {
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
return Ok(self.tiled_panes.get_active_pane_id(first_client_id));
|
return Ok(self.tiled_panes.get_active_pane_id(first_client_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (stacked_pane_ids_under_flexible_pane, _stacked_pane_ids_over_flexible_pane) = {
|
||||||
|
self.tiled_panes
|
||||||
|
.stacked_pane_ids_under_and_over_flexible_panes()
|
||||||
|
.with_context(err_context)?
|
||||||
|
};
|
||||||
|
let pane_contains_point = |p: &Box<dyn Pane>,
|
||||||
|
point: &Position,
|
||||||
|
stacked_pane_ids_under_flexible_pane: &HashSet<PaneId>|
|
||||||
|
-> bool {
|
||||||
|
let is_flexible_in_stack =
|
||||||
|
p.current_geom().is_stacked() && !p.current_geom().rows.is_fixed();
|
||||||
|
let is_stacked_under = stacked_pane_ids_under_flexible_pane.contains(&p.pid());
|
||||||
|
let geom_to_compare_against = if is_stacked_under && !self.draw_pane_frames {
|
||||||
|
// these sort of panes are one-liner panes under a flexible pane in a stack when we
|
||||||
|
// don't draw pane frames - because the whole stack's content is offset to allow
|
||||||
|
// room for the boundary between panes, they are actually drawn 1 line above where
|
||||||
|
// they are
|
||||||
|
let mut geom = p.current_geom();
|
||||||
|
geom.y = geom.y.saturating_sub(p.get_content_offset().bottom);
|
||||||
|
geom
|
||||||
|
} else if is_flexible_in_stack && !self.draw_pane_frames {
|
||||||
|
// these sorts of panes are flexible panes inside a stack when we don't draw pane
|
||||||
|
// frames - because the whole stack's content is offset to give room for the
|
||||||
|
// boundary between panes, we need to take this offset into account when figuring
|
||||||
|
// out whether the position is inside them
|
||||||
|
let mut geom = p.current_geom();
|
||||||
|
geom.rows.decrease_inner(p.get_content_offset().bottom);
|
||||||
|
geom
|
||||||
|
} else {
|
||||||
|
p.current_geom()
|
||||||
|
};
|
||||||
|
geom_to_compare_against.contains(point)
|
||||||
|
};
|
||||||
|
|
||||||
if search_selectable {
|
if search_selectable {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_selectable_tiled_panes()
|
.get_selectable_tiled_panes()
|
||||||
.find(|(_, p)| p.contains(point))
|
.find(|(_, p)| pane_contains_point(p, point, &stacked_pane_ids_under_flexible_pane))
|
||||||
.map(|(&id, _)| id))
|
.map(|(&id, _)| id))
|
||||||
} else {
|
} else {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_tiled_panes()
|
.get_tiled_panes()
|
||||||
.find(|(_, p)| p.contains(point))
|
.find(|(_, p)| pane_contains_point(p, point, &stacked_pane_ids_under_flexible_pane))
|
||||||
.map(|(&id, _)| id))
|
.map(|(&id, _)| id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 1134
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ─ Pane #1 ────────────────────────────────────────────┼─ Pane #7 ───────────────────────────────────
|
||||||
|
01 (C): ─ Pane #9 ────────────────────────────────────────────┤
|
||||||
|
02 (C): ─ Pane #10 ───────────────────────────────────────────┤
|
||||||
|
03 (C): │
|
||||||
|
04 (C): │
|
||||||
|
05 (C): │
|
||||||
|
06 (C): │
|
||||||
|
07 (C): │
|
||||||
|
08 (C): │
|
||||||
|
09 (C): │
|
||||||
|
10 (C): │
|
||||||
|
11 (C): │
|
||||||
|
12 (C): │
|
||||||
|
13 (C): │
|
||||||
|
14 (C): │
|
||||||
|
15 (C): │
|
||||||
|
16 (C): │
|
||||||
|
17 (C): │
|
||||||
|
18 (C): ├─ Pane #8 ───────────────────────────────────
|
||||||
|
19 (C): ─────────────────────────────────────────────────┬────┴─────────────────────────────────────────────
|
||||||
|
20 (C): ─ Pane #2 ───────────────────────────────────────┼─ Pane #4 ────────────────────────────────────────
|
||||||
|
21 (C): ─ Pane #3 ───────────────────────────────────────┼─ Pane #5 ────────────────────────────────────────
|
||||||
|
22 (C): │
|
||||||
|
23 (C): │
|
||||||
|
24 (C): │
|
||||||
|
25 (C): │
|
||||||
|
26 (C): │
|
||||||
|
27 (C): │
|
||||||
|
28 (C): │
|
||||||
|
29 (C): │
|
||||||
|
30 (C): │
|
||||||
|
31 (C): │
|
||||||
|
32 (C): │
|
||||||
|
33 (C): │
|
||||||
|
34 (C): │
|
||||||
|
35 (C): │
|
||||||
|
36 (C): │
|
||||||
|
37 (C): │
|
||||||
|
38 (C): │
|
||||||
|
39 (C): ├─ Pane #6 ────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -201,7 +201,6 @@ impl MockPtyInstructionBus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to shared thingy with other test file
|
|
||||||
fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
|
fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
|
||||||
set_session_name("test".into());
|
set_session_name("test".into());
|
||||||
let index = 0;
|
let index = 0;
|
||||||
|
|
@ -270,6 +269,74 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
|
||||||
tab
|
tab
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_new_tab_without_pane_frames(size: Size, default_mode: ModeInfo) -> Tab {
|
||||||
|
set_session_name("test".into());
|
||||||
|
let index = 0;
|
||||||
|
let position = 0;
|
||||||
|
let name = String::new();
|
||||||
|
let os_api = Box::new(FakeInputOutput::default());
|
||||||
|
let senders = ThreadSenders::default().silently_fail_on_send();
|
||||||
|
let max_panes = None;
|
||||||
|
let mode_info = default_mode;
|
||||||
|
let style = Style::default();
|
||||||
|
let draw_pane_frames = false;
|
||||||
|
let auto_layout = true;
|
||||||
|
let client_id = 1;
|
||||||
|
let session_is_mirrored = true;
|
||||||
|
let mut connected_clients = HashSet::new();
|
||||||
|
connected_clients.insert(client_id);
|
||||||
|
let connected_clients = Rc::new(RefCell::new(connected_clients));
|
||||||
|
let character_cell_info = Rc::new(RefCell::new(None));
|
||||||
|
let stacked_resize = Rc::new(RefCell::new(true));
|
||||||
|
let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default()));
|
||||||
|
let copy_options = CopyOptions::default();
|
||||||
|
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
|
||||||
|
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
|
||||||
|
let debug = false;
|
||||||
|
let arrow_fonts = true;
|
||||||
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
|
let mut tab = Tab::new(
|
||||||
|
index,
|
||||||
|
position,
|
||||||
|
name,
|
||||||
|
size,
|
||||||
|
character_cell_info,
|
||||||
|
stacked_resize,
|
||||||
|
sixel_image_store,
|
||||||
|
os_api,
|
||||||
|
senders,
|
||||||
|
max_panes,
|
||||||
|
style,
|
||||||
|
mode_info,
|
||||||
|
draw_pane_frames,
|
||||||
|
auto_layout,
|
||||||
|
connected_clients,
|
||||||
|
session_is_mirrored,
|
||||||
|
Some(client_id),
|
||||||
|
copy_options,
|
||||||
|
terminal_emulator_colors,
|
||||||
|
terminal_emulator_color_codes,
|
||||||
|
(vec![], vec![]),
|
||||||
|
PathBuf::from("my_default_shell"),
|
||||||
|
debug,
|
||||||
|
arrow_fonts,
|
||||||
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
tab.apply_layout(
|
||||||
|
TiledPaneLayout::default(),
|
||||||
|
vec![],
|
||||||
|
vec![(1, None)],
|
||||||
|
vec![],
|
||||||
|
HashMap::new(),
|
||||||
|
client_id,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab
|
||||||
|
}
|
||||||
|
|
||||||
fn create_new_tab_with_swap_layouts(
|
fn create_new_tab_with_swap_layouts(
|
||||||
size: Size,
|
size: Size,
|
||||||
default_mode: ModeInfo,
|
default_mode: ModeInfo,
|
||||||
|
|
@ -980,6 +1047,96 @@ fn split_stack_horizontally() {
|
||||||
assert_snapshot!(snapshot);
|
assert_snapshot!(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn render_stacks_without_pane_frames() {
|
||||||
|
// this checks various cases and gotchas that have to do with rendering stacked panes when we
|
||||||
|
// don't draw frames around panes
|
||||||
|
let size = Size {
|
||||||
|
cols: 100,
|
||||||
|
rows: 40,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab_without_pane_frames(size, ModeInfo::default());
|
||||||
|
let mut output = Output::default();
|
||||||
|
for i in 2..4 {
|
||||||
|
let new_pane_id_1 = PaneId::Terminal(i);
|
||||||
|
tab.new_pane(
|
||||||
|
new_pane_id_1,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
Some(client_id),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
// the below resizes will end up stacking the panes
|
||||||
|
tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None))
|
||||||
|
.unwrap();
|
||||||
|
tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None))
|
||||||
|
.unwrap();
|
||||||
|
tab.vertical_split(PaneId::Terminal(4), None, client_id)
|
||||||
|
.unwrap();
|
||||||
|
for i in 5..7 {
|
||||||
|
let new_pane_id_1 = PaneId::Terminal(i);
|
||||||
|
tab.new_pane(
|
||||||
|
new_pane_id_1,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
Some(client_id),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
tab.focus_pane_with_id(PaneId::Terminal(1), false, client_id);
|
||||||
|
for i in 7..9 {
|
||||||
|
let new_pane_id_1 = PaneId::Terminal(i);
|
||||||
|
tab.new_pane(
|
||||||
|
new_pane_id_1,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
Some(client_id),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
let _ = tab.focus_pane_with_id(PaneId::Terminal(1), false, client_id);
|
||||||
|
for i in 9..11 {
|
||||||
|
let new_pane_id_1 = PaneId::Terminal(i);
|
||||||
|
tab.new_pane(
|
||||||
|
new_pane_id_1,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
Some(client_id),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
tab.resize(
|
||||||
|
client_id,
|
||||||
|
ResizeStrategy::new(Resize::Increase, Some(Direction::Right)),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let _ = tab.focus_pane_with_id(PaneId::Terminal(7), false, client_id);
|
||||||
|
let _ = tab.focus_pane_with_id(PaneId::Terminal(5), false, client_id);
|
||||||
|
|
||||||
|
tab.render(&mut output).unwrap();
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().unwrap().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_screen() {
|
fn dump_screen() {
|
||||||
let size = Size {
|
let size = Size {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use zellij_utils::pane_size::Viewport;
|
use zellij_utils::pane_size::{Offset, Viewport};
|
||||||
|
|
||||||
use crate::output::CharacterChunk;
|
use crate::output::CharacterChunk;
|
||||||
use crate::panes::terminal_character::{TerminalCharacter, EMPTY_TERMINAL_CHARACTER, RESET_STYLES};
|
use crate::panes::terminal_character::{TerminalCharacter, EMPTY_TERMINAL_CHARACTER, RESET_STYLES};
|
||||||
|
|
@ -444,25 +444,40 @@ impl Boundaries {
|
||||||
boundary_characters: HashMap::new(),
|
boundary_characters: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn add_rect(&mut self, rect: &dyn Pane, color: Option<PaletteColor>) {
|
pub fn add_rect(
|
||||||
|
&mut self,
|
||||||
|
rect: &dyn Pane,
|
||||||
|
color: Option<PaletteColor>,
|
||||||
|
pane_is_on_top_of_stack: bool,
|
||||||
|
pane_is_on_bottom_of_stack: bool,
|
||||||
|
pane_is_stacked_under: bool,
|
||||||
|
) {
|
||||||
let pane_is_stacked = rect.current_geom().is_stacked();
|
let pane_is_stacked = rect.current_geom().is_stacked();
|
||||||
|
let should_skip_top_boundary = pane_is_stacked && !pane_is_on_top_of_stack;
|
||||||
|
let should_skip_bottom_boundary = pane_is_stacked && !pane_is_on_bottom_of_stack;
|
||||||
|
let content_offset = rect.get_content_offset();
|
||||||
if !self.is_fully_inside_screen(rect) {
|
if !self.is_fully_inside_screen(rect) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if rect.x() > self.viewport.x {
|
if rect.x() > self.viewport.x {
|
||||||
// left boundary
|
// left boundary
|
||||||
let boundary_x_coords = rect.x() - 1;
|
let boundary_x_coords = rect.x() - 1;
|
||||||
let first_row_coordinates = self.rect_right_boundary_row_start(rect);
|
let first_row_coordinates =
|
||||||
|
self.rect_right_boundary_row_start(rect, pane_is_stacked_under, content_offset);
|
||||||
let last_row_coordinates = self.rect_right_boundary_row_end(rect);
|
let last_row_coordinates = self.rect_right_boundary_row_end(rect);
|
||||||
for row in first_row_coordinates..last_row_coordinates {
|
for row in first_row_coordinates..last_row_coordinates {
|
||||||
let coordinates = Coordinates::new(boundary_x_coords, row);
|
let coordinates = Coordinates::new(boundary_x_coords, row);
|
||||||
let symbol_to_add = if row == first_row_coordinates && row != self.viewport.y {
|
let symbol_to_add = if row == first_row_coordinates && row != self.viewport.y {
|
||||||
|
if pane_is_stacked {
|
||||||
|
BoundarySymbol::new(boundary_type::VERTICAL_RIGHT).color(color)
|
||||||
|
} else {
|
||||||
BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
|
BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
|
||||||
|
}
|
||||||
} else if row == first_row_coordinates && pane_is_stacked {
|
} else if row == first_row_coordinates && pane_is_stacked {
|
||||||
BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
|
BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
|
||||||
} else if row == last_row_coordinates - 1
|
} else if row == last_row_coordinates - 1
|
||||||
&& row != self.viewport.y + self.viewport.rows - 1
|
&& row != self.viewport.y + self.viewport.rows - 1
|
||||||
&& !pane_is_stacked
|
&& content_offset.bottom > 0
|
||||||
{
|
{
|
||||||
BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
|
BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -476,7 +491,7 @@ impl Boundaries {
|
||||||
self.boundary_characters.insert(coordinates, next_symbol);
|
self.boundary_characters.insert(coordinates, next_symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rect.y() > self.viewport.y && !pane_is_stacked {
|
if rect.y() > self.viewport.y && !should_skip_top_boundary {
|
||||||
// top boundary
|
// top boundary
|
||||||
let boundary_y_coords = rect.y() - 1;
|
let boundary_y_coords = rect.y() - 1;
|
||||||
let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
|
let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
|
||||||
|
|
@ -501,15 +516,22 @@ impl Boundaries {
|
||||||
if self.rect_right_boundary_is_before_screen_edge(rect) {
|
if self.rect_right_boundary_is_before_screen_edge(rect) {
|
||||||
// right boundary
|
// right boundary
|
||||||
let boundary_x_coords = rect.right_boundary_x_coords() - 1;
|
let boundary_x_coords = rect.right_boundary_x_coords() - 1;
|
||||||
let first_row_coordinates = self.rect_right_boundary_row_start(rect);
|
let first_row_coordinates =
|
||||||
|
self.rect_right_boundary_row_start(rect, pane_is_stacked_under, content_offset);
|
||||||
let last_row_coordinates = self.rect_right_boundary_row_end(rect);
|
let last_row_coordinates = self.rect_right_boundary_row_end(rect);
|
||||||
for row in first_row_coordinates..last_row_coordinates {
|
for row in first_row_coordinates..last_row_coordinates {
|
||||||
let coordinates = Coordinates::new(boundary_x_coords, row);
|
let coordinates = Coordinates::new(boundary_x_coords, row);
|
||||||
let symbol_to_add = if row == first_row_coordinates && row != self.viewport.y {
|
let symbol_to_add = if row == first_row_coordinates && pane_is_stacked {
|
||||||
|
BoundarySymbol::new(boundary_type::VERTICAL_LEFT).color(color)
|
||||||
|
} else if row == first_row_coordinates && row != self.viewport.y {
|
||||||
|
if pane_is_stacked {
|
||||||
|
BoundarySymbol::new(boundary_type::VERTICAL_LEFT).color(color)
|
||||||
|
} else {
|
||||||
BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
|
BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
|
||||||
|
}
|
||||||
} else if row == last_row_coordinates - 1
|
} else if row == last_row_coordinates - 1
|
||||||
&& row != self.viewport.y + self.viewport.rows - 1
|
&& row != self.viewport.y + self.viewport.rows - 1
|
||||||
&& !pane_is_stacked
|
&& content_offset.bottom > 0
|
||||||
{
|
{
|
||||||
BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
|
BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -523,7 +545,7 @@ impl Boundaries {
|
||||||
self.boundary_characters.insert(coordinates, next_symbol);
|
self.boundary_characters.insert(coordinates, next_symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.rect_bottom_boundary_is_before_screen_edge(rect) && !pane_is_stacked {
|
if self.rect_bottom_boundary_is_before_screen_edge(rect) && !should_skip_bottom_boundary {
|
||||||
// bottom boundary
|
// bottom boundary
|
||||||
let boundary_y_coords = rect.bottom_boundary_y_coords() - 1;
|
let boundary_y_coords = rect.bottom_boundary_y_coords() - 1;
|
||||||
let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
|
let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
|
||||||
|
|
@ -575,11 +597,28 @@ impl Boundaries {
|
||||||
fn rect_bottom_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
|
fn rect_bottom_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
|
||||||
rect.y() + rect.rows() < self.viewport.y + self.viewport.rows
|
rect.y() + rect.rows() < self.viewport.y + self.viewport.rows
|
||||||
}
|
}
|
||||||
fn rect_right_boundary_row_start(&self, rect: &dyn Pane) -> usize {
|
fn rect_right_boundary_row_start(
|
||||||
|
&self,
|
||||||
|
rect: &dyn Pane,
|
||||||
|
pane_is_stacked_under: bool,
|
||||||
|
content_offset: Offset,
|
||||||
|
) -> usize {
|
||||||
let pane_is_stacked = rect.current_geom().is_stacked();
|
let pane_is_stacked = rect.current_geom().is_stacked();
|
||||||
let horizontal_frame_offset = if pane_is_stacked { 0 } else { 1 };
|
let horizontal_frame_offset = if pane_is_stacked_under {
|
||||||
|
// these panes - panes that are in a stack below the flexible pane - need to have their
|
||||||
|
// content offset taken into account when rendering them (i.e. they are rendered
|
||||||
|
// one line above their actual y coordinates, since they are only 1 line)
|
||||||
|
// as opposed to panes that are in a stack above the flexible pane who do not because
|
||||||
|
// they are rendered in place (the content offset of the stack is "absorbed" by the
|
||||||
|
// flexible pane below them)
|
||||||
|
content_offset.bottom
|
||||||
|
} else if pane_is_stacked {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
if rect.y() > self.viewport.y {
|
if rect.y() > self.viewport.y {
|
||||||
rect.y() - horizontal_frame_offset
|
rect.y().saturating_sub(horizontal_frame_offset)
|
||||||
} else {
|
} else {
|
||||||
self.viewport.y
|
self.viewport.y
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::ui::boundaries::boundary_type;
|
||||||
use crate::ClientId;
|
use crate::ClientId;
|
||||||
use zellij_utils::data::{client_id_to_colors, PaletteColor, Style};
|
use zellij_utils::data::{client_id_to_colors, PaletteColor, Style};
|
||||||
use zellij_utils::errors::prelude::*;
|
use zellij_utils::errors::prelude::*;
|
||||||
use zellij_utils::pane_size::Viewport;
|
use zellij_utils::pane_size::{Offset, Viewport};
|
||||||
use zellij_utils::position::Position;
|
use zellij_utils::position::Position;
|
||||||
|
|
||||||
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||||
|
|
@ -62,6 +62,7 @@ pub struct FrameParams {
|
||||||
pub pane_is_stacked_over: bool,
|
pub pane_is_stacked_over: bool,
|
||||||
pub should_draw_pane_frames: bool,
|
pub should_draw_pane_frames: bool,
|
||||||
pub pane_is_floating: bool,
|
pub pane_is_floating: bool,
|
||||||
|
pub content_offset: Offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
#[derive(Default, PartialEq)]
|
||||||
|
|
@ -82,6 +83,7 @@ pub struct PaneFrame {
|
||||||
should_draw_pane_frames: bool,
|
should_draw_pane_frames: bool,
|
||||||
is_pinned: bool,
|
is_pinned: bool,
|
||||||
is_floating: bool,
|
is_floating: bool,
|
||||||
|
content_offset: Offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaneFrame {
|
impl PaneFrame {
|
||||||
|
|
@ -108,6 +110,7 @@ impl PaneFrame {
|
||||||
should_draw_pane_frames: frame_params.should_draw_pane_frames,
|
should_draw_pane_frames: frame_params.should_draw_pane_frames,
|
||||||
is_pinned: false,
|
is_pinned: false,
|
||||||
is_floating: frame_params.pane_is_floating,
|
is_floating: frame_params.pane_is_floating,
|
||||||
|
content_offset: frame_params.content_offset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_pinned(mut self, is_pinned: bool) -> Self {
|
pub fn is_pinned(mut self, is_pinned: bool) -> Self {
|
||||||
|
|
@ -776,11 +779,26 @@ impl PaneFrame {
|
||||||
// if this is a stacked pane with pane frames off (and it doesn't necessarily have only
|
// if this is a stacked pane with pane frames off (and it doesn't necessarily have only
|
||||||
// 1 row because it could also be a flexible stacked pane)
|
// 1 row because it could also be a flexible stacked pane)
|
||||||
// in this case we should always draw the pane title line, and only the title line
|
// in this case we should always draw the pane title line, and only the title line
|
||||||
let one_line_title = self.render_one_line_title().with_context(err_context)?;
|
let mut one_line_title = self.render_one_line_title().with_context(err_context)?;
|
||||||
|
|
||||||
|
if self.content_offset.right != 0 && !self.should_draw_pane_frames {
|
||||||
|
// here what happens is that the title should be offset to the right
|
||||||
|
// in order to give room to the boundaries between the panes to be drawn
|
||||||
|
one_line_title.pop();
|
||||||
|
}
|
||||||
|
let y_coords_of_title = if self.pane_is_stacked_under && !self.should_draw_pane_frames {
|
||||||
|
// we only want to use the bottom offset in this case because panes that are
|
||||||
|
// stacked above the flexible pane should actually appear exactly where they are on
|
||||||
|
// screen, the content offset being "absorbed" by the flexible pane below them
|
||||||
|
self.geom.y.saturating_sub(self.content_offset.bottom)
|
||||||
|
} else {
|
||||||
|
self.geom.y
|
||||||
|
};
|
||||||
|
|
||||||
character_chunks.push(CharacterChunk::new(
|
character_chunks.push(CharacterChunk::new(
|
||||||
one_line_title,
|
one_line_title,
|
||||||
self.geom.x,
|
self.geom.x,
|
||||||
self.geom.y,
|
y_coords_of_title,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
for row in 0..self.geom.rows {
|
for row in 0..self.geom.rows {
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,7 @@ impl<'a> PaneContentsAndUi<'a> {
|
||||||
pane_is_stacked_under: self.pane_is_stacked_under,
|
pane_is_stacked_under: self.pane_is_stacked_under,
|
||||||
should_draw_pane_frames: self.should_draw_pane_frames,
|
should_draw_pane_frames: self.should_draw_pane_frames,
|
||||||
pane_is_floating,
|
pane_is_floating,
|
||||||
|
content_offset: self.pane.get_content_offset(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FrameParams {
|
FrameParams {
|
||||||
|
|
@ -236,6 +237,7 @@ impl<'a> PaneContentsAndUi<'a> {
|
||||||
pane_is_stacked_under: self.pane_is_stacked_under,
|
pane_is_stacked_under: self.pane_is_stacked_under,
|
||||||
should_draw_pane_frames: self.should_draw_pane_frames,
|
should_draw_pane_frames: self.should_draw_pane_frames,
|
||||||
pane_is_floating,
|
pane_is_floating,
|
||||||
|
content_offset: self.pane.get_content_offset(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -260,9 +262,17 @@ impl<'a> PaneContentsAndUi<'a> {
|
||||||
client_mode: InputMode,
|
client_mode: InputMode,
|
||||||
boundaries: &mut Boundaries,
|
boundaries: &mut Boundaries,
|
||||||
session_is_mirrored: bool,
|
session_is_mirrored: bool,
|
||||||
|
pane_is_on_top_of_stack: bool,
|
||||||
|
pane_is_on_bottom_of_stack: bool,
|
||||||
) {
|
) {
|
||||||
let color = self.frame_color(client_id, client_mode, session_is_mirrored);
|
let color = self.frame_color(client_id, client_mode, session_is_mirrored);
|
||||||
boundaries.add_rect(self.pane.as_ref(), color);
|
boundaries.add_rect(
|
||||||
|
self.pane.as_ref(),
|
||||||
|
color,
|
||||||
|
pane_is_on_top_of_stack,
|
||||||
|
pane_is_on_bottom_of_stack,
|
||||||
|
self.pane_is_stacked_under,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
fn frame_color(
|
fn frame_color(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -400,6 +400,22 @@ impl Offset {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn shift_right(right: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
right,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shift_right_top_and_bottom(right: usize, top: usize, bottom: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
right,
|
||||||
|
top,
|
||||||
|
bottom,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: This should be top and left, not bottom and right, but `boundaries.rs` would need
|
// FIXME: This should be top and left, not bottom and right, but `boundaries.rs` would need
|
||||||
// some changing
|
// some changing
|
||||||
pub fn shift(bottom: usize, right: usize) -> Self {
|
pub fn shift(bottom: usize, right: usize) -> Self {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue