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> {} 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 }
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 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 let fold_button = tf.draw_item_counted_ref(cx, live_id!(fold_button)).as_fold_button();
174 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 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 }
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 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}