Skip to main content

par_term/pane/manager/
focus.rs

1//! Focus and navigation operations for PaneManager
2//!
3//! Handles pane focus state, directional navigation, click-to-focus,
4//! and closing panes with automatic focus transfer.
5
6use super::PaneManager;
7use crate::pane::types::{NavigationDirection, Pane, PaneId};
8
9impl PaneManager {
10    /// Close a pane by ID
11    ///
12    /// Returns true if this was the last pane (tab should close)
13    pub fn close_pane(&mut self, id: PaneId) -> bool {
14        crate::debug_info!("PANE_CLOSE", "close_pane called for pane {}", id);
15
16        if let Some(root) = self.root.take() {
17            match Self::remove_pane(root, id) {
18                crate::pane::tmux_helpers::RemoveResult::Removed(new_root) => {
19                    self.root = new_root;
20
21                    // If we closed the focused pane, focus another
22                    if self.focused_pane_id == Some(id) {
23                        let new_focus = self
24                            .root
25                            .as_ref()
26                            .and_then(|r| r.all_pane_ids().first().copied());
27                        crate::debug_info!(
28                            "PANE_CLOSE",
29                            "Closed focused pane {}, new focus: {:?}",
30                            id,
31                            new_focus
32                        );
33                        self.focused_pane_id = new_focus;
34                    }
35
36                    // Recalculate bounds
37                    self.recalculate_bounds();
38
39                    // Log remaining panes after closure
40                    if let Some(ref root) = self.root {
41                        for pane_id in root.all_pane_ids() {
42                            if let Some(pane) = self.get_pane(pane_id) {
43                                crate::debug_info!(
44                                    "PANE_CLOSE",
45                                    "Remaining pane {} bounds=({:.0},{:.0} {:.0}x{:.0})",
46                                    pane.id,
47                                    pane.bounds.x,
48                                    pane.bounds.y,
49                                    pane.bounds.width,
50                                    pane.bounds.height
51                                );
52                            }
53                        }
54                    }
55
56                    crate::debug_info!("PANE_CLOSE", "Successfully closed pane {}", id);
57                }
58                crate::pane::tmux_helpers::RemoveResult::NotFound(root) => {
59                    crate::debug_info!("PANE_CLOSE", "Pane {} not found in tree", id);
60                    self.root = Some(root);
61                }
62            }
63        }
64
65        self.root.is_none()
66    }
67
68    /// Navigate to a pane in the given direction
69    pub fn navigate(&mut self, direction: NavigationDirection) {
70        if let Some(focused_id) = self.focused_pane_id
71            && let Some(ref root) = self.root
72            && let Some(new_id) = root.find_pane_in_direction(focused_id, direction)
73        {
74            self.focused_pane_id = Some(new_id);
75            log::debug!(
76                "Navigated {:?} from pane {} to pane {}",
77                direction,
78                focused_id,
79                new_id
80            );
81        }
82    }
83
84    /// Focus a specific pane by ID
85    pub fn focus_pane(&mut self, id: PaneId) {
86        if self
87            .root
88            .as_ref()
89            .is_some_and(|r| r.find_pane(id).is_some())
90        {
91            self.focused_pane_id = Some(id);
92        }
93    }
94
95    /// Focus the pane at a given pixel position
96    pub fn focus_pane_at(&mut self, x: f32, y: f32) -> Option<PaneId> {
97        if let Some(ref root) = self.root
98            && let Some(pane) = root.find_pane_at(x, y)
99        {
100            let id = pane.id;
101            self.focused_pane_id = Some(id);
102            return Some(id);
103        }
104        None
105    }
106
107    /// Get the currently focused pane
108    pub fn focused_pane(&self) -> Option<&Pane> {
109        self.focused_pane_id
110            .and_then(|id| self.root.as_ref()?.find_pane(id))
111    }
112
113    /// Get the currently focused pane mutably
114    pub fn focused_pane_mut(&mut self) -> Option<&mut Pane> {
115        let id = self.focused_pane_id?;
116        self.root.as_mut()?.find_pane_mut(id)
117    }
118
119    /// Get the focused pane ID
120    pub fn focused_pane_id(&self) -> Option<PaneId> {
121        self.focused_pane_id
122    }
123}