aboutsummaryrefslogtreecommitdiff
path: root/src/client/shell/composition.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-14 03:02:14 +0300
committerGitHub <noreply@github.com>2026-09-14 03:02:14 +0300
commit21c1f8cf856affb875b5ab4302225a63f4563bdd (patch)
treefc3fa607c4af3d9233f3ba7d905a4744ad25c215 /src/client/shell/composition.rs
parentadd3dd28f91db1a2b33e9582c60ac9c1809c1bf5 (diff)
fix: hide only kitty image placements overlapping overlays (#4081)
Diffstat (limited to 'src/client/shell/composition.rs')
-rw-r--r--src/client/shell/composition.rs49
1 files changed, 40 insertions, 9 deletions
diff --git a/src/client/shell/composition.rs b/src/client/shell/composition.rs
index 8e99059b..cee57d96 100644
--- a/src/client/shell/composition.rs
+++ b/src/client/shell/composition.rs
@@ -314,6 +314,7 @@ impl ClientShellState {
});
blit_pane_surface(&mut frame, &surface.frame, layout.pane_surface);
restore_mode_bar(&mut frame, mode_bar, mode_bar_cells.as_deref());
+ let mut occlusion = crate::kitty_graphics::surface::Occlusion::default();
let has_selection = self
.selection
.as_ref()
@@ -335,6 +336,7 @@ impl ClientShellState {
hit,
&self.config.palette,
false,
+ &mut occlusion,
);
}
let selection_is_stale_copy_projection = !copy_surface_coherent
@@ -346,6 +348,13 @@ impl ClientShellState {
.is_some_and(|selection| selection.pane_id == hit.pane_id)
});
if !selection_is_stale_copy_projection {
+ if let Some(selection) =
+ self.selection.as_ref().filter(|s| s.pane_id == hit.pane_id)
+ {
+ for rect in selection.visible_rects(hit.inner_rect, hit.scroll) {
+ occlusion.cover(rect);
+ }
+ }
crate::ui::render_selection_highlight(
self.selection.as_ref(),
&mut composed,
@@ -366,12 +375,13 @@ impl ClientShellState {
hit,
&self.config.palette,
true,
+ &mut occlusion,
);
}
}
frame.replace_from_ratatui_buffer_preserving_effects(&composed, cursor);
}
- self.render_link_hover(&mut frame);
+ self.render_link_hover(&mut frame, &mut occlusion);
if self.mode == ClientShellMode::Copy {
frame.cursor = None;
if let Some(copy_mode) = self.copy_mode.as_ref() {
@@ -390,6 +400,7 @@ impl ClientShellState {
let mut composed = frame.to_ratatui_buffer()?;
let x = hit.inner_rect.x + copy_mode.cursor.col;
let y = hit.inner_rect.y + viewport_row as u16;
+ occlusion.cover(Rect::new(x, y, 1, 1));
composed[(x, y)].set_style(
Style::default()
.fg(match self.config.palette.panel_bg {
@@ -433,17 +444,18 @@ impl ClientShellState {
diagnostic_area,
diagnostic,
&self.config.palette,
+ |rect| occlusion.cover(rect),
);
}
let lifecycle_offset = active_lifecycle.as_ref().map_or(0, |(label, status)| {
- let _ = endpoint_notices::render_lifecycle_banner(
+ occlusion.cover(endpoint_notices::render_lifecycle_banner(
&mut composed,
Rect::new(0, 0, cols, rows),
label,
*status,
u16::from(has_config_diagnostic) + layout.mobile_header.height,
&self.config.palette,
- );
+ ));
1
});
if let Some(notice) = self.visible_endpoint_notice.as_ref() {
@@ -484,6 +496,7 @@ impl ClientShellState {
)
};
}
+ occlusion.cover(self.hits.notification_toast);
frame.replace_from_ratatui_buffer_preserving_effects(&composed, cursor);
}
if let Some(feedback) = self.copy_feedback.as_ref() {
@@ -502,14 +515,14 @@ impl ClientShellState {
self.config.clipboard_toast_position,
self.hits.notification_toast,
);
- crate::ui::render_copy_feedback_buffer(
+ occlusion.cover(crate::ui::render_copy_feedback_buffer(
&mut composed,
feedback_area,
feedback,
offset,
self.config.clipboard_toast_position,
&self.config.palette,
- );
+ ));
frame.replace_from_ratatui_buffer_preserving_effects(&composed, cursor);
}
self.hits.popup = None;
@@ -519,6 +532,7 @@ impl ClientShellState {
if let Some(geometry) =
crate::popup_size::resolve_popup_geometry(width, height, layout.pane_surface)
{
+ occlusion.start_popup(geometry.outer);
let mut composed = frame.to_ratatui_buffer()?;
let block = ratatui::widgets::Block::default()
.borders(ratatui::widgets::Borders::ALL)
@@ -552,6 +566,7 @@ impl ClientShellState {
&& self.overlay.is_none()
{
let mut composed = frame.to_ratatui_buffer()?;
+ occlusion.cover(composed.area);
super::mobile::render_mobile_switcher(
&mut composed,
Rect::new(0, 0, cols, rows),
@@ -591,20 +606,27 @@ impl ClientShellState {
self.hits.popup = None;
}
restore_mode_bar(&mut frame, mode_bar, mode_bar_cells.as_deref());
+ if let Some(bar) = mode_bar {
+ occlusion.cover(bar);
+ }
if let Some(overlay) = self.overlay.as_ref() {
let mut composed = frame.to_ratatui_buffer()?;
let cursor = if let ClientShellOverlay::ContextMenu(menu) = overlay {
- self.hits.context_menu_rows =
+ let rendered =
render::render_context_menu(&mut composed, menu, &self.config.palette)?;
+ occlusion.cover(rendered.area);
+ self.hits.context_menu_rows = rendered.menu_rows;
None
} else if let ClientShellOverlay::GlobalMenu(menu) = overlay {
- self.hits.global_menu_rows = render::render_global_menu(
+ let rendered = render::render_global_menu(
&mut composed,
self.hits.global_launcher,
menu,
snapshot,
&self.config.palette,
)?;
+ occlusion.cover(rendered.area);
+ self.hits.global_menu_rows = rendered.menu_rows;
None
} else {
let rendered = render::render_client_overlay(
@@ -616,6 +638,7 @@ impl ClientShellState {
&self.config.keybinds,
&self.config.palette,
)?;
+ occlusion.cover(rendered.area);
self.hits.overlay_primary = rendered.primary;
self.hits.overlay_clear = rendered.clear;
self.hits.overlay_cancel = rendered.cancel;
@@ -662,7 +685,7 @@ impl ClientShellState {
self.hits.pane_splits.clear();
self.hits.popup = None;
}
- self.compose_graphics(&mut frame, layout);
+ self.compose_graphics(&mut frame, layout, &occlusion);
Some(frame)
}
}
@@ -685,6 +708,7 @@ fn render_client_copy_search_highlights(
hit: &PaneHit,
palette: &Palette,
current_only: bool,
+ occlusion: &mut crate::kitty_graphics::surface::Occlusion,
) {
let Some(copy_mode) = copy_mode.filter(|copy_mode| copy_mode.pane_id == hit.pane_id) else {
return;
@@ -726,7 +750,14 @@ fn render_client_copy_search_highlights(
} else {
hit.inner_rect.width.saturating_sub(1)
};
- for col in start_col..=end_col.min(hit.inner_rect.width.saturating_sub(1)) {
+ let end_col = end_col.min(hit.inner_rect.width.saturating_sub(1));
+ occlusion.cover(Rect::new(
+ hit.inner_rect.x.saturating_add(start_col),
+ hit.inner_rect.y.saturating_add(viewport_row),
+ end_col.saturating_add(1).saturating_sub(start_col),
+ 1,
+ ));
+ for col in start_col..=end_col {
buffer[(
hit.inner_rect.x.saturating_add(col),
hit.inner_rect.y.saturating_add(viewport_row),