ratatui_toolkit/widgets/code_diff/extensions/file_tree/traits/
widget.rs1use ratatui::buffer::Buffer;
6use ratatui::layout::Rect;
7use ratatui::style::{Modifier, Style};
8use ratatui::text::{Line, Span};
9use ratatui::widgets::Widget;
10
11use crate::primitives::tree_view::{matches_filter, TreeViewRef};
12use crate::services::theme::AppTheme;
13use crate::widgets::code_diff::diff_file_tree::{DiffFileEntry, DiffFileTree};
14
15use super::render_entry::render_entry;
16
17fn entry_matches_filter(entry: &DiffFileEntry, filter: &Option<String>) -> bool {
21 matches_filter(&entry.name, filter)
22}
23
24fn render_filter_line(
26 filter_text: Option<&str>,
27 filter_mode: bool,
28 area: Rect,
29 buf: &mut Buffer,
30 theme: &AppTheme,
31) {
32 if area.height == 0 {
33 return;
34 }
35
36 let y = area.y + area.height - 1;
37
38 let filter_str = filter_text.unwrap_or("");
40 let cursor = if filter_mode { "_" } else { "" };
41
42 let line = Line::from(vec![
43 Span::styled(
44 "/ ",
45 Style::default()
46 .fg(theme.warning)
47 .add_modifier(Modifier::BOLD),
48 ),
49 Span::styled(filter_str, Style::default().fg(theme.text)),
50 Span::styled(
51 cursor,
52 Style::default()
53 .fg(theme.warning)
54 .add_modifier(Modifier::SLOW_BLINK),
55 ),
56 ]);
57
58 let bg_style = Style::default().bg(theme.background_panel);
60 for x in area.x..(area.x + area.width) {
61 buf[(x, y)].set_style(bg_style);
62 }
63
64 buf.set_line(area.x, y, &line, area.width);
65}
66
67impl Widget for DiffFileTree {
68 fn render(mut self, area: Rect, buf: &mut Buffer) {
73 if area.width == 0 || area.height == 0 {
74 return;
75 }
76
77 let focused = self.focused;
78 let theme = self.theme.clone();
79 let filter_mode = self.state.filter_mode;
80 let has_filter = self.state.filter.as_ref().map_or(false, |f| !f.is_empty());
81 let show_filter_line = filter_mode || has_filter;
82
83 let tree_area = if show_filter_line && area.height > 1 {
85 Rect {
86 height: area.height - 1,
87 ..area
88 }
89 } else {
90 area
91 };
92
93 let highlight_bg = theme.background_element;
94 let icon_style = Style::default().fg(theme.info);
95 let theme_for_render = theme.clone();
96
97 let tree_view = TreeViewRef::new(&self.nodes)
98 .icons("\u{F07B}", "\u{F07C}") .icon_style(icon_style)
100 .render_fn(move |entry, node_state| {
101 render_entry(entry, node_state, focused, &theme_for_render)
102 })
103 .filter_fn(entry_matches_filter)
104 .highlight_style(Style::default().bg(highlight_bg));
105
106 ratatui::widgets::StatefulWidget::render(tree_view, tree_area, buf, &mut self.state);
107
108 if show_filter_line && area.height > 1 {
110 render_filter_line(self.state.filter.as_deref(), filter_mode, area, buf, &theme);
111 }
112 }
113}
114
115impl Widget for &DiffFileTree {
116 fn render(self, area: Rect, buf: &mut Buffer) {
121 if area.width == 0 || area.height == 0 {
122 return;
123 }
124
125 let focused = self.focused;
126 let theme = self.theme.clone();
127 let filter_mode = self.state.filter_mode;
128 let has_filter = self.state.filter.as_ref().map_or(false, |f| !f.is_empty());
129 let show_filter_line = filter_mode || has_filter;
130 let mut state = self.state.clone();
131
132 let tree_area = if show_filter_line && area.height > 1 {
134 Rect {
135 height: area.height - 1,
136 ..area
137 }
138 } else {
139 area
140 };
141
142 let highlight_bg = theme.background_element;
143 let icon_style = Style::default().fg(theme.info);
144 let theme_for_render = theme.clone();
145
146 let tree_view = TreeViewRef::new(&self.nodes)
147 .icons("\u{F07B}", "\u{F07C}") .icon_style(icon_style)
149 .render_fn(move |entry, node_state| {
150 render_entry(entry, node_state, focused, &theme_for_render)
151 })
152 .filter_fn(entry_matches_filter)
153 .highlight_style(Style::default().bg(highlight_bg));
154
155 ratatui::widgets::StatefulWidget::render(tree_view, tree_area, buf, &mut state);
156
157 if show_filter_line && area.height > 1 {
159 render_filter_line(self.state.filter.as_deref(), filter_mode, area, buf, &theme);
160 }
161 }
162}