mod creation;
mod focus;
mod layout;
mod session;
mod tmux_convert;
mod tmux_layout;
mod tmux_update;
use crate::config::{Config, PaneBackgroundConfig};
use crate::pane::types::{Pane, PaneBounds, PaneId, PaneNode};
use crate::terminal::TerminalManager;
use anyhow::Result;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use tokio::runtime::Runtime;
use tokio::sync::RwLock;
pub enum ExtractResult {
Extracted {
pane: Pane,
remaining: Option<PaneNode>,
},
OnlyPane(Pane),
NotFound,
}
enum ExtractInternal {
Extracted { pane: Pane, remaining: PaneNode },
OnlyPane(Pane),
NotFound(PaneNode),
}
pub struct PaneManager {
pub(super) root: Option<PaneNode>,
pub(super) focused_pane_id: Option<PaneId>,
pub(super) next_pane_id: PaneId,
pub(super) divider_width: f32,
pub(super) divider_hit_width: f32,
pub(super) total_bounds: PaneBounds,
}
impl PaneManager {
pub fn new() -> Self {
Self {
root: None,
focused_pane_id: None,
next_pane_id: 1,
divider_width: 1.0, divider_hit_width: 8.0, total_bounds: PaneBounds::default(),
}
}
pub fn new_with_existing_terminal(
terminal: Arc<RwLock<TerminalManager>>,
working_directory: Option<String>,
is_active: Arc<AtomicBool>,
) -> Self {
let mut manager = Self::new();
let primary_pane_id = manager.next_pane_id;
manager.next_pane_id += 1;
let pane =
Pane::new_wrapping_terminal(primary_pane_id, terminal, working_directory, is_active);
manager.root = Some(PaneNode::leaf(pane));
manager.focused_pane_id = Some(primary_pane_id);
manager
}
pub fn new_with_pane(pane: Pane) -> Self {
let pane_id = pane.id;
let mut manager = Self::new();
manager.next_pane_id = pane_id + 1;
manager.root = Some(PaneNode::leaf(pane));
manager.focused_pane_id = Some(pane_id);
manager
}
pub fn with_initial_pane(
config: &Config,
runtime: Arc<Runtime>,
working_directory: Option<String>,
) -> Result<Self> {
let mut manager = Self::new();
manager.divider_width = config.pane_divider_width.unwrap_or(1.0);
manager.divider_hit_width = config.pane_divider_hit_width;
manager.create_initial_pane(config, runtime, working_directory)?;
Ok(manager)
}
pub fn next_pane_id(&self) -> PaneId {
self.next_pane_id
}
pub fn get_pane(&self, id: PaneId) -> Option<&Pane> {
self.root.as_ref()?.find_pane(id)
}
pub fn get_pane_mut(&mut self, id: PaneId) -> Option<&mut Pane> {
self.root.as_mut()?.find_pane_mut(id)
}
pub fn all_panes(&self) -> Vec<&Pane> {
self.root
.as_ref()
.map(|r| r.all_panes())
.unwrap_or_default()
}
pub fn all_panes_mut(&mut self) -> Vec<&mut Pane> {
self.root
.as_mut()
.map(|r| r.all_panes_mut())
.unwrap_or_default()
}
pub fn collect_pane_backgrounds(&self) -> Vec<PaneBackgroundConfig> {
self.all_panes()
.iter()
.enumerate()
.filter_map(|(index, pane)| {
pane.background
.image_path
.as_ref()
.map(|path| PaneBackgroundConfig {
index,
image: path.clone(),
mode: pane.background.mode,
opacity: pane.background.opacity,
darken: pane.background.darken,
})
})
.collect()
}
pub fn pane_count(&self) -> usize {
self.root.as_ref().map(|r| r.pane_count()).unwrap_or(0)
}
pub fn has_multiple_panes(&self) -> bool {
self.pane_count() > 1
}
pub fn root(&self) -> Option<&PaneNode> {
self.root.as_ref()
}
pub fn root_mut(&mut self) -> Option<&mut PaneNode> {
self.root.as_mut()
}
pub fn insert_subtree_at(
&mut self,
target_pane_id: PaneId,
subtree: PaneNode,
direction: crate::pane::types::SplitDirection,
ratio: f32,
) -> bool {
if let Some(root) = self.root.take() {
match Self::insert_subtree_at_node(root, target_pane_id, subtree, direction, ratio) {
Ok(new_root) => {
self.root = Some(new_root);
self.recalculate_bounds();
return true;
}
Err((original_root, _subtree)) => {
self.root = Some(original_root);
}
}
}
false
}
pub fn extract_pane(&mut self, target_id: PaneId) -> ExtractResult {
if let Some(root) = self.root.take() {
match Self::extract_pane_from_node(root, target_id) {
ExtractInternal::Extracted { pane, remaining } => {
self.root = Some(remaining);
if let Some(id) = self.focused_pane_id
&& id == target_id
{
self.focused_pane_id = self.root.as_ref().and_then(|r| r.first_pane_id());
}
ExtractResult::Extracted {
pane,
remaining: self.root.take(),
}
}
ExtractInternal::OnlyPane(pane) => {
self.root = None;
self.focused_pane_id = None;
ExtractResult::OnlyPane(pane)
}
ExtractInternal::NotFound(node) => {
self.root = Some(node);
ExtractResult::NotFound
}
}
} else {
ExtractResult::NotFound
}
}
pub fn take_root(&mut self) -> Option<PaneNode> {
self.focused_pane_id = None;
self.root.take()
}
pub fn set_root(&mut self, node: PaneNode) {
self.root = Some(node);
self.recalculate_bounds();
}
}
impl Default for PaneManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pane_manager_new() {
let manager = PaneManager::new();
assert!(manager.root.is_none());
assert_eq!(manager.pane_count(), 0);
assert!(!manager.has_multiple_panes());
}
}