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