1 mod changes;
2 mod command;
3 mod commit;
4 mod commit_details;
5 mod commitlist;
6 mod create_branch;
7 mod diff;
8 mod externaleditor;
9 mod filetree;
10 mod help;
11 mod inspect_commit;
12 mod msg;
13 mod reset;
14 mod stashmsg;
15 mod tag_commit;
16 mod textinput;
17 mod utils;
18
19 use anyhow::Result;
20 use crossterm::event::Event;
21
22 pub use changes::ChangesComponent;
23 pub use command::{CommandInfo, CommandText};
24 pub use commit::CommitComponent;
25 pub use commit_details::CommitDetailsComponent;
26 pub use commitlist::CommitList;
27 pub use create_branch::CreateBranchComponent;
28 pub use diff::DiffComponent;
29 pub use externaleditor::ExternalEditorComponent;
30 pub use filetree::FileTreeComponent;
31 pub use help::HelpComponent;
32 pub use inspect_commit::InspectCommitComponent;
33 pub use msg::MsgComponent;
34 pub use reset::ResetComponent;
35 pub use stashmsg::StashMsgComponent;
36 pub use tag_commit::TagCommitComponent;
37 pub use textinput::TextInputComponent;
38 pub use utils::filetree::FileTreeItemKind;
39
40 use crate::ui::style::Theme;
41 use tui::{
42 backend::Backend,
43 layout::{Alignment, Rect},
44 widgets::{Block, BorderType, Borders, Paragraph, Text},
45 Frame,
46 };
47
48 /// creates accessors for a list of components
49 ///
50 /// allows generating code to make sure
51 /// we always enumerate all components in both getter functions
52 #[macro_export]
53 macro_rules! accessors {
54 ($self:ident, [$($element:ident),+]) => {
55 fn components(& $self) -> Vec<&dyn Component> {
56 vec![
57 $(&$self.$element,)+
58 ]
59 }
60
61 fn components_mut(&mut $self) -> Vec<&mut dyn Component> {
62 vec![
63 $(&mut $self.$element,)+
64 ]
65 }
66 };
67 }
68
69 /// returns `true` if event was consumed
event_pump( ev: Event, components: &mut [&mut dyn Component], ) -> Result<bool>70 pub fn event_pump(
71 ev: Event,
72 components: &mut [&mut dyn Component],
73 ) -> Result<bool> {
74 for c in components {
75 if c.event(ev)? {
76 return Ok(true);
77 }
78 }
79
80 Ok(false)
81 }
82
83 /// helper fn to simplify delegating command
84 /// gathering down into child components
85 /// see `event_pump`,`accessors`
command_pump( out: &mut Vec<CommandInfo>, force_all: bool, components: &[&dyn Component], )86 pub fn command_pump(
87 out: &mut Vec<CommandInfo>,
88 force_all: bool,
89 components: &[&dyn Component],
90 ) {
91 for c in components {
92 if c.commands(out, force_all) != CommandBlocking::PassingOn
93 && !force_all
94 {
95 break;
96 }
97 }
98 }
99
100 #[derive(Copy, Clone)]
101 pub enum ScrollType {
102 Up,
103 Down,
104 Home,
105 End,
106 PageUp,
107 PageDown,
108 }
109
110 #[derive(Copy, Clone)]
111 pub enum Direction {
112 Up,
113 Down,
114 }
115
116 ///
117 #[derive(PartialEq)]
118 pub enum CommandBlocking {
119 Blocking,
120 PassingOn,
121 }
122
123 ///
visibility_blocking<T: Component>( comp: &T, ) -> CommandBlocking124 pub fn visibility_blocking<T: Component>(
125 comp: &T,
126 ) -> CommandBlocking {
127 if comp.is_visible() {
128 CommandBlocking::Blocking
129 } else {
130 CommandBlocking::PassingOn
131 }
132 }
133
134 ///
135 pub trait DrawableComponent {
136 ///
draw<B: Backend>( &self, f: &mut Frame<B>, rect: Rect, ) -> Result<()>137 fn draw<B: Backend>(
138 &self,
139 f: &mut Frame<B>,
140 rect: Rect,
141 ) -> Result<()>;
142 }
143
144 /// base component trait
145 pub trait Component {
146 ///
commands( &self, out: &mut Vec<CommandInfo>, force_all: bool, ) -> CommandBlocking147 fn commands(
148 &self,
149 out: &mut Vec<CommandInfo>,
150 force_all: bool,
151 ) -> CommandBlocking;
152
153 /// returns true if event propagation needs to end (event was consumed)
event(&mut self, ev: Event) -> Result<bool>154 fn event(&mut self, ev: Event) -> Result<bool>;
155
156 ///
focused(&self) -> bool157 fn focused(&self) -> bool {
158 false
159 }
160 /// focus/unfocus this component depending on param
focus(&mut self, _focus: bool)161 fn focus(&mut self, _focus: bool) {}
162 ///
is_visible(&self) -> bool163 fn is_visible(&self) -> bool {
164 true
165 }
166 ///
hide(&mut self)167 fn hide(&mut self) {}
168 ///
show(&mut self) -> Result<()>169 fn show(&mut self) -> Result<()> {
170 Ok(())
171 }
172
173 ///
toggle_visible(&mut self) -> Result<()>174 fn toggle_visible(&mut self) -> Result<()> {
175 if self.is_visible() {
176 self.hide();
177 Ok(())
178 } else {
179 self.show()
180 }
181 }
182 }
183
dialog_paragraph<'a, 't, T>( title: &'a str, content: T, theme: &Theme, focused: bool, ) -> Paragraph<'a, 't, T> where T: Iterator<Item = &'t Text<'t>>,184 fn dialog_paragraph<'a, 't, T>(
185 title: &'a str,
186 content: T,
187 theme: &Theme,
188 focused: bool,
189 ) -> Paragraph<'a, 't, T>
190 where
191 T: Iterator<Item = &'t Text<'t>>,
192 {
193 Paragraph::new(content)
194 .block(
195 Block::default()
196 .title(title)
197 .borders(Borders::ALL)
198 .title_style(theme.title(focused))
199 .border_style(theme.block(focused)),
200 )
201 .alignment(Alignment::Left)
202 }
203
popup_paragraph<'a, 't, T>( title: &'a str, content: T, theme: &Theme, focused: bool, ) -> Paragraph<'a, 't, T> where T: Iterator<Item = &'t Text<'t>>,204 fn popup_paragraph<'a, 't, T>(
205 title: &'a str,
206 content: T,
207 theme: &Theme,
208 focused: bool,
209 ) -> Paragraph<'a, 't, T>
210 where
211 T: Iterator<Item = &'t Text<'t>>,
212 {
213 Paragraph::new(content)
214 .block(
215 Block::default()
216 .title(title)
217 .borders(Borders::ALL)
218 .border_type(BorderType::Thick)
219 .title_style(theme.title(focused))
220 .border_style(theme.block(focused)),
221 )
222 .alignment(Alignment::Left)
223 .wrap(true)
224 }
225