makepad_studio/
search.rs

1
2use {
3    crate::{
4        file_system::file_system::FileSystem,
5        makepad_file_protocol::SearchItem,
6        makepad_platform::studio::JumpToFile,
7        app::{AppAction, AppData},
8        makepad_widgets::*,
9        makepad_code_editor::code_view::*,
10    },
11    std::{
12        env,
13    },
14};
15
16live_design!{
17    use link::shaders::*;
18    use link::widgets::*;
19    use link::theme::*;
20    use makepad_widgets::designer_theme::*;
21    use makepad_code_editor::code_view::CodeView;
22    
23    SearchResult = <View> {
24        height: Fit, width: Fill
25        padding: <THEME_MSPACE_2> {} // TODO: Fix. Changing this value to i.e. '0.' causes Makepad Studio to freeze when switching to the log tab.
26        spacing: (THEME_SPACE_2)
27        align: { x: 0.0, y: 0.0 }
28        show_bg: true,
29        draw_bg: {
30            instance is_even: 0.0
31            instance selected: 0.0
32            instance hover: 0.0
33            fn pixel(self) -> vec4 {
34                return mix(
35                    mix(
36                        THEME_COLOR_BG_EVEN,
37                        THEME_COLOR_BG_ODD,
38                        self.is_even
39                    ),
40                    THEME_COLOR_OUTSET_ACTIVE,
41                    self.selected
42                );
43            }
44        }
45        animator: {
46            ignore_missing: true,
47            hover = {
48                default: off
49                off = {
50                    from: {all: Forward {duration: 0.1}}
51                    apply: {
52                        draw_bg: {hover: 0.0}
53                    }
54                }
55                on = {
56                    cursor: Hand
57                    from: {all: Snap}
58                    apply: {
59                        draw_bg: {hover: 1.0}
60                    },
61                }
62            }
63            
64            select = {
65                default: off
66                off = {
67                    from: {all: Snap}
68                    apply: {
69                        draw_bg: {selected: 0.0}
70                    }
71                }
72                on = {
73                    from: {all: Snap}
74                    apply: {
75                        draw_bg: {selected: 1.0}
76                    }
77                }  
78            }
79        }
80        flow = <TextFlow>{
81            width: Fill,
82            height: Fit
83            width: Fill,
84            height: Fit
85            
86            code_view = <CodeView>{
87                editor:{
88                    word_wrap: false
89                    draw_bg: { color: (#0000) }
90                    margin:{left:15}
91                    draw_text: {
92                       // text_style: {font_size:7}
93                    }
94                }
95            }
96            
97            fold_button = <FoldButton>{
98                animator:{
99                    active={default:off}
100                }
101            }
102        }
103    }
104    
105    pub Search = {{Search}} <RectView> {
106        height: Fill, width: Fill,
107        //draw_bg: {color: (THEME_COLOR_BG_CONTAINER)}
108        flow: Down,
109        <DockToolbar> {
110            content = {
111                spacing: (THEME_SPACE_2)
112                align: { y: 0.5 }
113                search_input = <TextInputFlat> {
114                    width: Fill,
115                    empty_text: "Search",
116                }
117            }
118        }
119        list = <PortalList> {
120            capture_overload: false,
121            grab_key_focus: false
122            auto_tail: true
123            drag_scrolling: false
124            max_pull_down: 0,
125            height: Fill, width: Fill,
126            flow: Down
127            SearchResult = <SearchResult> {
128            }
129            Empty = <SearchResult> {
130                cursor: Default
131                width: Fill
132                height: 25,
133                body = <P> {  margin: 0, text: "" }
134            }
135        }
136    }
137}
138
139#[derive(Clone, Debug, DefaultNone)]
140pub enum SearchAction {
141    JumpTo(JumpToFile),
142    None
143}
144
145#[derive(Live, LiveHook, Widget)]
146pub struct Search{
147    #[deref] view:View
148}
149
150#[derive(Clone, Debug, PartialEq)]
151pub struct JumpToFileLink{item_id:usize}
152
153impl Search{
154    fn draw_results(&mut self, cx: &mut Cx2d, list:&mut PortalList, file_system:&mut FileSystem){
155        
156        list.set_item_range(cx, 0, file_system.search_results.len());
157        while let Some(item_id) = list.next_visible_item(cx) {
158            let is_even = item_id & 1 == 0;
159            let mut location = String::new();
160            if let Some(res) = file_system.search_results.get(item_id as usize) {
161                
162                let mut item = list.item(cx, item_id, live_id!(SearchResult)).as_view();
163                
164                item.apply_over(cx, live!{
165                    draw_bg: {is_even: (if is_even {1.0} else {0.0})}
166                });
167                
168                while let Some(step) = item.draw(cx, &mut Scope::empty()).step(){
169                    if let Some(mut tf) = step.as_text_flow().borrow_mut(){
170                        // alright what do we do
171                       //tf.draw_item_counted(cx, map_level_to_icon(msg.level));
172                        //let fold_button = if msg.explanation.is_some(){
173                        let fold_button = tf.draw_item_counted_ref(cx, live_id!(fold_button)).as_fold_button();
174                        //}
175                        //else{
176                        //    Default::default()
177                        //};
178                        // lets look up the file_name from file_id
179                        // and also lets use the result-span info to fetch the right
180                        // line and context
181                        
182                        fmt_over!(location, "{}: {}:{}", res.file_name, res.line + 1, res.column_byte + 1);
183                        
184                        tf.draw_link(cx, live_id!(link), JumpToFileLink{item_id}, &location);
185                        
186                        //tf.draw_text(cx, &res.result_line);
187                        
188                        let open = fold_button.open_float();
189                        cx.turtle_new_line();
190                        let code = tf.item_counted(cx, live_id!(code_view));
191                        code.set_text(cx, &res.result_line);
192                        if let Some(mut code_view) = code.as_code_view().borrow_mut(){
193                            code_view.lazy_init_session();
194                            let lines = code_view.session.as_ref().unwrap().document().as_text().as_lines().len();
195                            code_view.editor.height_scale = open.max(1.0 / (lines + 1) as f64);
196                        }
197                        code.draw_all_unscoped(cx);
198                        // lets check 
199                        /*if let Some(explanation) = &msg.explanation{
200                            
201                        };*/
202                    }
203                }
204                continue
205            }
206            let item = list.item(cx, item_id, live_id!(Empty)).as_view();
207            item.apply_over(cx, live!{draw_bg: {is_even: (if is_even {1.0} else {0.0})}});
208            item.draw_all(cx, &mut Scope::empty());
209        }
210    }
211}
212
213impl Widget for Search {
214    fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk:Walk)->DrawStep{
215        while let Some(step) = self.view.draw_walk(cx, scope, walk).step(){
216            if let Some(mut list) = step.as_portal_list().borrow_mut(){
217                self.draw_results(cx, &mut *list, &mut scope.data.get_mut::<AppData>().unwrap().file_system)
218            }
219        }
220        DrawStep::done()
221    }
222    
223    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope){
224        let search_results = self.view.portal_list(id!(list));
225        self.view.handle_event(cx, event, scope);
226        let data = scope.data.get_mut::<AppData>().unwrap();
227        if let Event::Actions(actions) = event{
228            if let Some(search) = self.view.text_input(id!(search_input)).changed(&actions){
229                let mut set = Vec::new();
230                for item in search.split("|"){
231                    if let Some(item) = item.strip_suffix("\\b"){
232                        if let Some(item) = item.strip_prefix("\\b"){
233                            set.push(SearchItem{
234                                needle: item.to_string(),
235                                prefixes: None,
236                                pre_word_boundary: true,
237                                post_word_boundary: true
238                            })
239                        }
240                        else{
241                            set.push(SearchItem{
242                                needle: item.to_string(),
243                                prefixes: None,
244                                pre_word_boundary: false,
245                                post_word_boundary: true
246                            })
247                        }
248                    }
249                    else if let Some(item) = item.strip_prefix("\\b"){
250                        set.push(SearchItem{
251                            needle: item.to_string(),
252                            prefixes: None,
253                            pre_word_boundary: true,
254                            post_word_boundary: false
255                        })
256                    }
257                    else{
258                        set.push(SearchItem{
259                            needle: item.to_string(),
260                            prefixes: None,
261                            pre_word_boundary: false,
262                            post_word_boundary: false
263                        })
264                    }
265                }
266                data.file_system.search_string(cx, set);
267            }
268            if search_results.any_items_with_actions(&actions) {
269                // alright lets figure out if someone clicked a link
270                // alright so how do we now filter which link was clicked
271                for jtf in actions.filter_actions_data::<JumpToFileLink>(){
272                    if let Some(res) = data.file_system.search_results.get(jtf.item_id) {
273                        cx.action(AppAction::JumpTo(JumpToFile{
274                            file_name: res.file_name.clone(), 
275                            line: res.line as u32,
276                            column: res.column_byte as u32
277                        }));
278                    }
279                }
280            }
281        }
282    }
283}
284
285impl SearchRef{
286}