use {
super::*,
crate::{
browser::BrowserState,
cli::TriBool,
command::{Command, Sequence},
conf::Conf,
display::{Areas, Screen, W},
errors::ProgramError,
file_sum,
git,
kitty,
launchable::Launchable,
path::closest_dir,
skin::*,
stage::Stage,
syntactic::SyntaxTheme,
task_sync::{Dam, Either},
verb::Internal,
},
crossbeam::channel::{
Receiver,
Sender,
unbounded,
},
crokey::crossterm::event::Event,
std::{
io::Write,
path::PathBuf,
str::FromStr,
sync::{Arc, Mutex},
},
strict::NonEmptyVec,
termimad::EventSource,
};
/// The GUI
pub struct App {
/// dimensions of the screen
screen: Screen,
/// the panels of the application, at least one
panels: NonEmptyVec<Panel>,
/// index of the currently focused panel
active_panel_idx: usize,
/// whether the app is in the (uncancellable) process of quitting
quitting: bool,
/// what must be done after having closed the TUI
launch_at_end: Option<Launchable>,
/// a count of all panels created
created_panels_count: usize,
/// the panel dedicated to preview, if any
preview_panel: Option<PanelId>,
stage_panel: Option<PanelId>,
/// an optional copy of the root for the --server
shared_root: Option<Arc<Mutex<PathBuf>>>,
/// sender to the sequence channel
tx_seqs: Sender<Sequence>,
/// receiver to listen to the sequence channel
rx_seqs: Receiver<Sequence>,
/// counter incremented at every draw
drawing_count: usize,
}
impl App {
pub fn new(
con: &AppContext,
) -> Result<App, ProgramError> {
let screen = Screen::new(con)?;
let panel = Panel::new(
PanelId::from(0),
Box::new(
BrowserState::new(
con.initial_root.clone(),
con.initial_tree_options.clone(),
screen,
con,
&Dam::unlimited(),
)?
),
Areas::create(&mut Vec::new(), 0, screen, false),
con,
);
let (tx_seqs, rx_seqs) = unbounded::<Sequence>();
Ok(App {
screen,
active_panel_idx: 0,
panels: panel.into(),
quitting: false,
launch_at_end: None,
created_panels_count: 1,
preview_panel: None,
stage_panel: None,
shared_root: None,
tx_seqs,
rx_seqs,
drawing_count: 0,
})
}
fn panel_ref_to_idx(&self, panel_ref: PanelReference) -> Option<usize> {
match panel_ref {
PanelReference::Active => Some(self.active_panel_idx),
PanelReference::Leftest => Some(0),
PanelReference::Rightest => Some(self.panels.len().get() - 1),
PanelReference::Id(id) => self.panel_id_to_idx(id),
PanelReference::Preview => self.preview_panel.and_then(|id| self.panel_id_to_idx(id)),
}
}
/// return the current index of the panel with given id
fn panel_id_to_idx(&self, id: PanelId) -> Option<usize> {
self.panels.iter().position(|panel| panel.id == id)
}
fn state(&self) -> &dyn PanelState {
self.panels[self.active_panel_idx].state()
}
fn mut_state(&mut self) -> &mut dyn PanelState {
self.panels[self.active_panel_idx].mut_state()
}
fn panel(&self) -> &Panel {
&self.panels[self.active_panel_idx]
}
fn mut_panel(&mut self) -> &mut Panel {
unsafe {
self.panels
.as_mut_slice()
.get_unchecked_mut(self.active_panel_idx)
}
}
/// close the panel if it's not the last one
///
/// Return true when the panel has been removed (ie it wasn't the last one)
fn close_panel(&mut self, panel_idx: usize) -> bool {
let active_panel_id = self.panels[self.active_panel_idx].id;
if let Some(preview_id) = self.preview_panel {
if self.panels.has_len(2) && self.panels[panel_idx].id != preview_id {
// we don't want to stay with just the preview
return false;
}
}
if let Some(stage_id) = self.stage_panel {
if self.panels.has_len(2) && self.panels[panel_idx].id != stage_id {
// we don't want to stay with just the stage
return false;
}
}
if let Ok(removed_panel) = self.panels.remove(panel_idx) {
if self.preview_panel == Some(removed_panel.id) {
self.preview_panel = None;
}
if self.stage_panel == Some(removed_panel.id) {
self.stage_panel = None;
}
Areas::resize_all(
self.panels.as_mut_slice(),
self.screen,
self.preview_panel.is_some(),
);
self.active_panel_idx = self
.panels
.iter()
.