1
2use {
3 crate::{
4 build_manager::{
5 build_manager::*,
6 build_protocol::*,
7 },
8 makepad_platform::studio::JumpToFile,
9 app::{AppAction, AppData},
10 makepad_widgets::*,
11 makepad_code_editor::code_view::*,
12 },
13 std::{
14 env,
15 },
16};
17
18live_design!{
19 use link::shaders::*;
20 use link::widgets::*;
21 use link::theme::*;
22 use makepad_code_editor::code_view::CodeView;
23
24 Icon = <View> {
25 width: 10, height: 10
26 margin:{top:2, right: 10},
27 show_bg: true,
28 }
29
30 LogItem = <View> {
31 height: Fit, width: Fill
32 padding: <THEME_MSPACE_2> {} spacing: (THEME_SPACE_2)
34 align: { x: 0.0, y: 0.0 }
35 show_bg: true,
36 draw_bg: {
37 instance is_even: 0.0
38 instance selected: 0.0
39 instance hover: 0.0
40 fn pixel(self) -> vec4 {
41 return mix(
42 mix(
43 THEME_COLOR_BG_EVEN,
44 THEME_COLOR_BG_ODD,
45 self.is_even
46 ),
47 THEME_COLOR_OUTSET_ACTIVE,
48 self.selected
49 );
50 }
51 }
52 animator: {
53 ignore_missing: true,
54 hover = {
55 default: off
56 off = {
57 from: {all: Forward {duration: 0.1}}
58 apply: {
59 draw_bg: {hover: 0.0}
60 }
61 }
62 on = {
63 cursor: Hand
64 from: {all: Snap}
65 apply: {
66 draw_bg: {hover: 1.0}
67 },
68 }
69 }
70
71 select = {
72 default: off
73 off = {
74 from: {all: Snap}
75 apply: {
76 draw_bg: {selected: 0.0}
77 }
78 }
79 on = {
80 from: {all: Snap}
81 apply: {
82 draw_bg: {selected: 1.0}
83 }
84 }
85 }
86 }
87 flow = <TextFlow>{
88 width: Fill,
89 height: Fit
90 width: Fill,
91 height: Fit
92
93 code_view = <CodeView>{
94 editor:{
95 margin:{left:25}
96 }
97 }
98
99 fold_button = <FoldButton>{
100 animator:{
101 active={default:off}
102 }
103 }
104
105 wait_icon = <Icon> {
106 draw_bg: {
107 fn pixel(self) -> vec4 {
108 let sdf = Sdf2d::viewport(self.pos * self.rect_size)
109 sdf.circle(5., 5., 4.)
110 sdf.fill(THEME_COLOR_LABEL_OUTER)
111 sdf.move_to(3., 5.)
112 sdf.line_to(3., 5.)
113 sdf.move_to(5., 5.)
114 sdf.line_to(5., 5.)
115 sdf.move_to(7., 5.)
116 sdf.line_to(7., 5.)
117 sdf.stroke(#0, 0.8)
118 return sdf.result
119 }
120 }
121 },
122 log_icon = <Icon> {
123 draw_bg: {
124 fn pixel(self) -> vec4 {
125 let sdf = Sdf2d::viewport(self.pos * self.rect_size)
126 sdf.circle(5., 5., 4.);
127 sdf.fill(THEME_COLOR_LABEL_OUTER);
128 let sz = 1.;
129 sdf.move_to(5., 5.);
130 sdf.line_to(5., 5.);
131 sdf.stroke(#a, 0.8);
132 return sdf.result
133 }
134 }
135 }
136 error_icon = <Icon> {
137 draw_bg: {
138 fn pixel(self) -> vec4 {
139 let sdf = Sdf2d::viewport(self.pos * self.rect_size)
140 sdf.circle(5., 5., 4.5);
141 sdf.fill(THEME_COLOR_ERROR);
142 let sz = 1.5;
143 sdf.move_to(5. - sz, 5. - sz);
144 sdf.line_to(5. + sz, 5. + sz);
145 sdf.move_to(5. - sz, 5. + sz);
146 sdf.line_to(5. + sz, 5. - sz);
147 sdf.stroke(#0, 0.8)
148 return sdf.result
149 }
150 }
151 },
152 warning_icon = <Icon> {
153 draw_bg: {
154 fn pixel(self) -> vec4 {
155 let sdf = Sdf2d::viewport(self.pos * self.rect_size)
156 sdf.move_to(5., 1.);
157 sdf.line_to(9.25, 9.);
158 sdf.line_to(0.75, 9.);
159 sdf.close_path();
160 sdf.fill(THEME_COLOR_WARNING);
161 sdf.move_to(5., 3.5);
163 sdf.line_to(5., 5.25);
164 sdf.stroke(#0, 1.0);
165 sdf.move_to(5., 7.25);
166 sdf.line_to(5., 7.5);
167 sdf.stroke(#0, 1.0);
168 return sdf.result
169 }
170 }
171 }
172 panic_icon = <Icon> {
173 draw_bg: {
174 fn pixel(self) -> vec4 {
175 let sdf = Sdf2d::viewport(self.pos * self.rect_size)
176 sdf.move_to(5., 1.);
177 sdf.line_to(9., 9.);
178 sdf.line_to(1., 9.);
179 sdf.close_path();
180 sdf.fill(THEME_COLOR_PANIC);
181 let sz = 1.;
182 sdf.move_to(5. - sz, 6.25 - sz);
183 sdf.line_to(5. + sz, 6.25 + sz);
184 sdf.move_to(5. - sz, 6.25 + sz);
185 sdf.line_to(5. + sz, 6.25 - sz);
186 sdf.stroke(#0, 0.8);
187 return sdf.result
188 }
189 }
190 }
191 }
192 }
193
194 pub LogList = {{LogList}}{
195 height: Fill, width: Fill,
196 list = <PortalList> {
197 max_pull_down: 0,
198 capture_overload: false,
199 grab_key_focus: false
200 auto_tail: true
201 drag_scrolling: false
202 height: Fill, width: Fill,
203 flow: Down
204 LogItem = <LogItem> {
205 }
206 Empty = <LogItem> {
207 cursor: Default
208 width: Fill
209 height: 25,
210 body = <P> { margin: 0, text: "" }
211 }
212 }
213 }
214}
215
216#[derive(Clone, Debug, DefaultNone)]
217pub enum LogListAction {
218 JumpTo(JumpToFile),
219 None
220}
221
222#[derive(Live, LiveHook, Widget)]
223pub struct LogList{
224 #[deref] view:View
225}
226
227#[derive(Clone, Debug, PartialEq)]
228pub struct JumpToFileLink{item_id:usize}
229
230impl LogList{
231 fn draw_log(&mut self, cx: &mut Cx2d, list:&mut PortalList, build_manager:&mut BuildManager){
232 list.set_item_range(cx, 0, build_manager.log.len());
233 while let Some(item_id) = list.next_visible_item(cx) {
234 let is_even = item_id & 1 == 0;
235 fn map_level_to_icon(level: LogLevel) -> LiveId {
236 match level {
237 LogLevel::Warning => live_id!(warning_icon),
238 LogLevel::Error => live_id!(error_icon),
239 LogLevel::Log => live_id!(log_icon),
240 LogLevel::Wait => live_id!(wait_icon),
241 LogLevel::Panic => live_id!(panic_icon),
242 }
243 }
244 let mut location = String::new();
245 if let Some((build_id, log_item)) = build_manager.log.get(item_id as usize) {
246 let _binary = if build_manager.active.builds.len()>1 {
247 if let Some(build) = build_manager.active.builds.get(&build_id) {
248 &build.log_index
249 }
250 else {""}
251 }else {""};
252 let mut item = list.item(cx, item_id, live_id!(LogItem)).as_view();
253 item.apply_over(cx, live!{
254 draw_bg: {is_even: (if is_even {1.0} else {0.0})}
255 });
256 while let Some(step) = item.draw(cx, &mut Scope::empty()).step(){
257 if let Some(mut tf) = step.as_text_flow().borrow_mut(){
258 match log_item {
259 LogItem::Bare(msg) => {
260 tf.draw_item_counted(cx, map_level_to_icon(msg.level));
261 tf.draw_text(cx,&msg.line);
262 }
263 LogItem::Location(msg) => {
264 tf.draw_item_counted(cx, map_level_to_icon(msg.level));
265 let fold_button = if msg.explanation.is_some(){
266 tf.draw_item_counted_ref(cx, live_id!(fold_button)).as_fold_button()
267 }
268 else{
269 Default::default()
270 };
271 fmt_over!(location, "{}: {}:{}", msg.file_name, msg.start.line_index + 1, msg.start.byte_index + 1);
272 tf.draw_link(cx, live_id!(link), JumpToFileLink{item_id}, &location);
273
274 tf.draw_text(cx, &msg.message);
275 if let Some(explanation) = &msg.explanation{
276 let open = fold_button.open_float();
277 if open > 0.0{
278 cx.turtle_new_line();
279 let code = tf.item_counted(cx, live_id!(code_view));
280 code.set_text(cx, explanation);
281 code.as_code_view().borrow_mut().unwrap().editor.height_scale = open;
282 code.draw_all_unscoped(cx);
283 }
284 };
285 }
286 _ => {}
287 }
288 }
289 }
290 continue
291 }
292 let item = list.item(cx, item_id, live_id!(Empty)).as_view();
293 item.apply_over(cx, live!{draw_bg: {is_even: (if is_even {1.0} else {0.0})}});
294 item.draw_all(cx, &mut Scope::empty());
295 }
296 }
297}
298
299impl Widget for LogList {
300 fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk:Walk)->DrawStep{
301 while let Some(step) = self.view.draw_walk(cx, scope, walk).step(){
302 if let Some(mut list) = step.as_portal_list().borrow_mut(){
303 self.draw_log(cx, &mut *list, &mut scope.data.get_mut::<AppData>().unwrap().build_manager)
304 }
305 }
306 DrawStep::done()
307 }
308
309 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope){
310 let log_list = self.view.portal_list(id!(list));
311 self.view.handle_event(cx, event, scope);
312 let data = scope.data.get::<AppData>().unwrap();
313 if let Event::Actions(actions) = event{
314
315 if log_list.any_items_with_actions(&actions) {
316 for jtf in actions.filter_actions_data::<JumpToFileLink>(){
319 if let Some((_build_id, log_item)) = data.build_manager.log.get(jtf.item_id) {
321 match log_item {
322 LogItem::Location(msg) => {
323 cx.action(AppAction::JumpTo(JumpToFile{
324 file_name: msg.file_name.clone(),
325 line: msg.start.line_index as u32,
326 column: msg.start.byte_index as u32
327 }));
328 }
329 _ => ()
330 }
331 }
332 }
333 }
334 }
335 }
336}
337
338impl LogListRef{
339 pub fn reset_scroll(&self, cx:&mut Cx){
340 if let Some(inner) = self.borrow_mut() {
341 let log_list = inner.view.portal_list(id!(list));
342 log_list.set_first_id_and_scroll(0,0.0);
343 log_list.redraw(cx);
344 }
345 }
346}