use std::collections::{HashMap, HashSet};
use std::io::{self, Write};
use crossterm::{cursor, queue, terminal};
use crate::app::lifecycle::extract_selected_text;
use crate::layout::{Layout, Rect};
use crate::pane::Pane;
use crate::render::{self, BorderCache};
use crate::settings::Settings;
pub(crate) fn make_inner(tw: u16, th: u16, show_status_bar: bool) -> Rect {
let sh = if show_status_bar { 1u16 } else { 0 };
Rect {
x: 1,
y: 1,
w: tw.saturating_sub(2),
h: th.saturating_sub(sh + 2),
}
}
pub(crate) fn zoomed_content_size(tw: u16, th: u16, show_status_bar: bool) -> (u16, u16) {
let sh = if show_status_bar { 1u16 } else { 0 };
(tw.saturating_sub(2), th.saturating_sub(sh + 2))
}
pub(crate) fn resize_zoomed_pane(
panes: &mut HashMap<usize, Pane>,
pane_id: usize,
tw: u16,
th: u16,
settings: &Settings,
) {
let (cols, rows) = zoomed_content_size(tw, th, settings.show_status_bar);
if let Some(pane) = panes.get_mut(&pane_id) {
pane.resize(cols, rows);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn render_frame(
stdout: &mut io::Stdout,
panes: &HashMap<usize, Pane>,
layout: &Layout,
active: usize,
settings: &Settings,
tw: u16,
th: u16,
dragging: bool,
border_cache: &BorderCache,
dirty_panes: &HashSet<usize>,
full_redraw: bool,
mode_label: &str,
selection: render::PaneSelection,
selection_chars: usize,
broadcast: bool,
) -> anyhow::Result<()> {
queue!(stdout, terminal::BeginSynchronizedUpdate)?;
render::render_panes(
stdout,
panes,
layout,
active,
settings.border_style,
settings.show_status_bar,
tw,
th,
dragging,
border_cache,
dirty_panes,
full_redraw,
selection,
broadcast,
&settings.theme,
)?;
if settings.show_status_bar && (!mode_label.is_empty() || selection_chars > 0) {
let pane_order = border_cache.pane_order();
let active_idx = pane_order.iter().position(|&id| id == active).unwrap_or(0);
let pane_name = panes.get(&active).and_then(|p| p.name()).unwrap_or("");
render::draw_status_bar_full(
stdout,
tw,
th,
active_idx,
pane_order.len(),
mode_label,
pane_name,
selection_chars,
&settings.theme,
)?;
}
if settings.visible {
settings.render_overlay(stdout, tw, th, broadcast)?;
queue!(stdout, cursor::Hide)?; }
queue!(stdout, terminal::EndSynchronizedUpdate)?;
stdout.flush()?;
Ok(())
}
pub(crate) fn collect_render_targets(
panes: &HashMap<usize, Pane>,
dirty_panes: &HashSet<usize>,
full_redraw: bool,
zoomed_pane: Option<usize>,
extra_pane: Option<usize>,
) -> Vec<usize> {
let mut targets = if let Some(pid) = zoomed_pane {
let mut out = Vec::with_capacity(1 + usize::from(extra_pane.is_some()));
if panes.contains_key(&pid) {
out.push(pid);
}
out
} else if full_redraw {
panes.keys().copied().collect::<Vec<_>>()
} else {
dirty_panes
.iter()
.copied()
.filter(|pid| panes.contains_key(pid))
.collect::<Vec<_>>()
};
if let Some(pid) = extra_pane {
if panes.contains_key(&pid) && !targets.contains(&pid) {
targets.push(pid);
}
}
targets
}
pub(crate) fn sync_render_targets(panes: &mut HashMap<usize, Pane>, targets: &[usize]) {
for pid in targets {
if let Some(pane) = panes.get_mut(pid) {
pane.sync_scrollback();
}
}
}
pub(crate) fn reset_render_targets(panes: &mut HashMap<usize, Pane>, targets: &[usize]) {
for pid in targets {
if let Some(pane) = panes.get_mut(pid) {
pane.reset_scrollback_view();
}
}
}
pub(crate) fn selection_char_count_from_synced(
panes: &HashMap<usize, Pane>,
selection: render::PaneSelection,
) -> usize {
selection
.and_then(|(pane_id, sr, sc, er, ec)| {
panes.get(&pane_id).map(|pane| {
let text = extract_selected_text(pane.screen(), pane_id, sr, sc, er, ec);
text.chars().count()
})
})
.unwrap_or(0)
}