1use crate::callback::{Callback, KeyHandler};
4use crate::core::event::{KeyCode, KeyEvent, KeyMods};
5use crate::style::{Length, Style, StyleSlot};
6use crate::utils::gradient::ColorGradient;
7use crate::widgets::ScrollKeymap;
8use crate::widgets::{FocusAccordion, ListItem};
9use std::sync::Arc;
10
11#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13pub struct TreePath(pub(crate) Arc<[usize]>);
14
15impl TreePath {
16 pub fn segments(&self) -> &[usize] {
18 &self.0
19 }
20}
21
22impl From<Vec<usize>> for TreePath {
23 fn from(value: Vec<usize>) -> Self {
24 Self(value.into())
25 }
26}
27
28impl AsRef<[usize]> for TreePath {
29 fn as_ref(&self) -> &[usize] {
30 &self.0
31 }
32}
33
34#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct TreeEvent {
37 pub index: usize,
39 pub path: TreePath,
41}
42
43#[derive(Clone, Debug, PartialEq, Eq)]
45pub struct TreeToggleEvent {
46 pub index: usize,
48 pub path: TreePath,
50 pub expanded: bool,
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
56pub struct TreeKeymap(u8);
57
58impl TreeKeymap {
59 pub const NONE: Self = Self(0);
61 pub const ARROWS: Self = Self(1 << 0);
63 pub const VIM: Self = Self(1 << 1);
65 pub const TOGGLE: Self = Self(1 << 2);
67 pub const DEFAULT: Self = Self(Self::ARROWS.0 | Self::VIM.0 | Self::TOGGLE.0);
69
70 pub fn contains(self, other: Self) -> bool {
72 (self.0 & other.0) == other.0
73 }
74}
75
76impl std::ops::BitOr for TreeKeymap {
77 type Output = Self;
78
79 fn bitor(self, rhs: Self) -> Self {
80 Self(self.0 | rhs.0)
81 }
82}
83
84impl std::ops::BitOrAssign for TreeKeymap {
85 fn bitor_assign(&mut self, rhs: Self) {
86 self.0 |= rhs.0;
87 }
88}
89
90impl std::ops::BitAnd for TreeKeymap {
91 type Output = Self;
92
93 fn bitand(self, rhs: Self) -> Self {
94 Self(self.0 & rhs.0)
95 }
96}
97
98impl std::ops::BitAndAssign for TreeKeymap {
99 fn bitand_assign(&mut self, rhs: Self) {
100 self.0 &= rhs.0;
101 }
102}
103
104impl std::ops::Not for TreeKeymap {
105 type Output = Self;
106
107 fn not(self) -> Self {
108 Self(!self.0)
109 }
110}
111
112impl Default for TreeKeymap {
113 fn default() -> Self {
114 Self::DEFAULT
115 }
116}
117
118#[derive(Clone, Copy, Debug, PartialEq, Eq)]
119pub(crate) enum TreeAction {
120 Expand,
121 Collapse,
122 Toggle,
123}
124
125pub(crate) fn tree_action_from_key(key: &KeyEvent, keymap: TreeKeymap) -> Option<TreeAction> {
126 if key.mods != KeyMods::NONE {
127 return None;
128 }
129 match key.code {
130 KeyCode::Left if keymap.contains(TreeKeymap::ARROWS) => Some(TreeAction::Collapse),
131 KeyCode::Right if keymap.contains(TreeKeymap::ARROWS) => Some(TreeAction::Expand),
132 KeyCode::Char('h') if keymap.contains(TreeKeymap::VIM) => Some(TreeAction::Collapse),
133 KeyCode::Char('l') if keymap.contains(TreeKeymap::VIM) => Some(TreeAction::Expand),
134 KeyCode::Char(' ') if keymap.contains(TreeKeymap::TOGGLE) => Some(TreeAction::Toggle),
135 _ => None,
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 fn key(code: KeyCode, mods: KeyMods) -> KeyEvent {
144 KeyEvent { code, mods }
145 }
146
147 #[test]
148 fn tree_navigation_ignores_modified_keys() {
149 let keymap = TreeKeymap::DEFAULT;
150 assert_eq!(
151 tree_action_from_key(&key(KeyCode::Left, KeyMods::NONE), keymap),
152 Some(TreeAction::Collapse)
153 );
154 assert_eq!(
155 tree_action_from_key(
156 &key(
157 KeyCode::Left,
158 KeyMods {
159 ctrl: true,
160 ..KeyMods::NONE
161 }
162 ),
163 keymap
164 ),
165 None
166 );
167 }
168}
169
170#[derive(Clone, Debug, PartialEq, Eq)]
172pub struct TreeNode {
173 pub(crate) item: ListItem,
174 pub(crate) children: Vec<TreeNode>,
175 pub(crate) expanded: bool,
176 pub(crate) expandable: bool,
177 pub(crate) indent: u16,
178 pub(crate) leading_guide_fill_cells: u16,
179}
180
181impl TreeNode {
182 pub fn new(item: impl Into<ListItem>) -> Self {
184 Self {
185 item: item.into(),
186 children: Vec::new(),
187 expanded: false,
188 expandable: false,
189 indent: 2,
190 leading_guide_fill_cells: 0,
191 }
192 }
193
194 pub fn child(mut self, child: TreeNode) -> Self {
196 self.children.push(child);
197 self
198 }
199
200 pub fn children(mut self, children: impl IntoIterator<Item = TreeNode>) -> Self {
203 self.children = children.into_iter().collect();
204 self
205 }
206
207 pub fn expanded(mut self, expanded: bool) -> Self {
209 self.expanded = expanded;
210 self
211 }
212
213 pub fn expandable(mut self, expandable: bool) -> Self {
215 self.expandable = expandable;
216 self
217 }
218
219 pub(crate) fn is_expandable(&self) -> bool {
220 self.expandable || !self.children.is_empty()
221 }
222
223 pub fn indent(mut self, indent: u16) -> Self {
225 self.indent = indent;
226 self
227 }
228
229 pub(crate) fn leading_guide_fill_cells(mut self, cells: u16) -> Self {
230 self.leading_guide_fill_cells = cells;
231 self
232 }
233}
234
235#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
237pub enum IndentStyle {
238 #[default]
240 None,
241 Line,
243 Short,
245 ShortRounded,
247 Long,
249 LongRounded,
251}
252
253#[derive(Clone, PartialEq)]
254pub(crate) struct TreeProps {
255 pub root: TreeNode,
256 pub selected: Option<usize>,
257 pub clear_selection: bool,
258 pub force_scroll_to_selected: bool,
259 pub gap: u16,
260 pub icon_gap: u16,
261 pub show_icons: bool,
262 pub expanded_icon: Arc<str>,
263 pub collapsed_icon: Arc<str>,
264 pub leaf_icon: Option<Arc<str>>,
265 pub icon_style: Style,
266 pub width: Length,
267 pub height: Length,
268 pub style: Style,
269 pub hover_style: StyleSlot,
270 pub item_hover_style: StyleSlot,
271 pub selection_style: StyleSlot,
272 pub unfocused_selection_style: StyleSlot,
273 pub selection_symbol: Option<Arc<str>>,
274 pub selection_symbol_style: Option<Style>,
275 pub unfocused_selection_symbol_style: Option<Style>,
276 pub scrollbar: bool,
277 pub scrollbar_config: crate::style::ScrollbarConfig,
278 pub scroll_keys: ScrollKeymap,
279 pub scroll_wheel: bool,
280 pub show_scroll_indicators: bool,
281 pub scroll_indicator_style: Style,
282 pub empty_text: Option<Arc<str>>,
283 pub empty_text_style: Style,
284 pub activate_on_click: bool,
285 pub focusable: bool,
286 pub tab_stop: bool,
287 pub on_focus: Option<Callback<()>>,
288 pub on_blur: Option<Callback<()>>,
289 pub on_select: Option<Callback<TreeEvent>>,
290 pub on_activate: Option<Callback<TreeEvent>>,
291 pub on_toggle: Option<Callback<TreeToggleEvent>>,
292 pub keymap: TreeKeymap,
293 pub focus_policy: Option<FocusAccordion>,
294 pub focus_key: Option<Arc<str>>,
295 pub indent_style: IndentStyle,
296 pub indent_guide_style: Style,
297 pub indent_gradient: Option<ColorGradient>,
298 pub(crate) solid_indent_connector_gap: bool,
299 pub selection_full_width: bool,
300 pub unselected_symbol: Option<Arc<str>>,
301 pub key_interceptor: Option<KeyHandler>,
302 pub indent_guide_start_depth: usize,
303}