1mod bottom_bar;
2mod code_area;
3mod input_bar;
4
5use self::bottom_bar::BottomBar;
6use self::code_area::CodeArea;
7use self::input_bar::InputBar;
8use crate::color::BLACK;
9use crate::context::Context;
10use crate::device::CURRENT_DEVICE;
11use crate::font::Fonts;
12use crate::framebuffer::{Framebuffer, UpdateMode};
13use crate::geom::{halves, CycleDir, Rectangle};
14use crate::gesture::GestureEvent;
15use crate::unit::{mm_to_px, scale_by_dpi};
16use crate::view::common::locate_by_id;
17use crate::view::common::{toggle_battery_menu, toggle_clock_menu, toggle_main_menu};
18use crate::view::filler::Filler;
19use crate::view::keyboard::Keyboard;
20use crate::view::menu::{Menu, MenuKind};
21use crate::view::top_bar::{TopBar, TopBarVariant};
22use crate::view::{Bus, Event, Hub, RenderData, RenderQueue, View};
23use crate::view::{EntryId, EntryKind, Id, ViewId, ID_FEEDER};
24use crate::view::{BIG_BAR_HEIGHT, SMALL_BAR_HEIGHT, THICKNESS_MEDIUM};
25use anyhow::{format_err, Error};
26use std::collections::VecDeque;
27use std::io::Write;
28use std::io::{BufRead, BufReader};
29use std::path::Path;
30use std::process::{Child, Command, Stdio};
31use std::thread;
32use tracing::error;
33
34const APP_DIR: &str = "bin/ivy";
35const APP_NAME: &str = "ivy";
36const LIB_NAME: &str = "lib.ivy";
37
38pub struct Calculator {
39 id: Id,
40 rect: Rectangle,
41 children: Vec<Box<dyn View>>,
42 process: Child,
43 data: VecDeque<Line>,
44 size: (usize, usize),
45 location: (usize, usize),
46 history: History,
47 margin_width: i32,
48 font_size: f32,
49}
50
51#[derive(Debug, Clone)]
52struct History {
53 cursor: usize,
54 size: usize,
55}
56
57#[derive(Debug, Clone)]
58pub struct Line {
59 origin: LineOrigin,
60 content: String,
61}
62
63#[derive(Debug, Copy, Clone, Eq, PartialEq)]
64pub enum LineOrigin {
65 Input,
66 Output,
67 Error,
68}
69
70impl Calculator {
71 pub fn new(
72 rect: Rectangle,
73 hub: &Hub,
74 rq: &mut RenderQueue,
75 context: &mut Context,
76 ) -> Result<Calculator, Error> {
77 let id = ID_FEEDER.next();
78 let path = Path::new(APP_DIR).join(APP_NAME).canonicalize()?;
79 let mut process = Command::new(path)
80 .current_dir(APP_DIR)
81 .stdin(Stdio::piped())
82 .stdout(Stdio::piped())
83 .stderr(Stdio::piped())
84 .spawn()?;
85 let stdout = process
86 .stdout
87 .take()
88 .ok_or_else(|| format_err!("can't take stdout"))?;
89 let stderr = process
90 .stderr
91 .take()
92 .ok_or_else(|| format_err!("can't take stderr"))?;
93
94 let hub2 = hub.clone();
95 thread::spawn(move || {
96 let reader = BufReader::new(stdout);
97 for line_res in reader.lines() {
98 if let Ok(line) = line_res {
99 hub2.send(Event::ProcessLine(LineOrigin::Output, line.clone()))
100 .ok();
101 } else {
102 break;
103 }
104 }
105 });
106
107 let hub3 = hub.clone();
108 thread::spawn(move || {
109 let reader = BufReader::new(stderr);
110 for line_res in reader.lines() {
111 if let Ok(line) = line_res {
112 hub3.send(Event::ProcessLine(LineOrigin::Error, line.clone()))
113 .ok();
114 } else {
115 break;
116 }
117 }
118 });
119
120 if Path::new(APP_DIR).join(LIB_NAME).exists() {
121 if let Some(stdin) = process.stdin.as_mut() {
122 writeln!(stdin, ")get '{}'", LIB_NAME).ok();
123 }
124 }
125
126 let mut children = Vec::new();
127 let dpi = CURRENT_DEVICE.dpi;
128 let (small_height, big_height) = (
129 scale_by_dpi(SMALL_BAR_HEIGHT, dpi) as i32,
130 scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32,
131 );
132 let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32;
133 let (small_thickness, big_thickness) = halves(thickness);
134 let side = small_height;
135
136 let font_size = context.settings.calculator.font_size;
137 let margin_width = context.settings.calculator.margin_width;
138 let history = History {
139 cursor: 0,
140 size: context.settings.calculator.history_size,
141 };
142
143 let top_bar = TopBar::new(
144 rect![
145 rect.min.x,
146 rect.min.y,
147 rect.max.x,
148 rect.min.y + side - small_thickness
149 ],
150 TopBarVariant::Back,
151 "Calculator".to_string(),
152 context,
153 );
154 children.push(Box::new(top_bar) as Box<dyn View>);
155
156 let separator = Filler::new(
157 rect![
158 rect.min.x,
159 rect.min.y + side - small_thickness,
160 rect.max.x,
161 rect.min.y + side + big_thickness
162 ],
163 BLACK,
164 );
165 children.push(Box::new(separator) as Box<dyn View>);
166
167 let mut kb_rect = rect![
168 rect.min.x,
169 rect.max.y - (small_height + 3 * big_height) as i32 + big_thickness,
170 rect.max.x,
171 rect.max.y - small_height - small_thickness
172 ];
173
174 let keyboard = Keyboard::new(&mut kb_rect, true, context);
175
176 let sp_rect = rect![
177 rect.min.x,
178 kb_rect.min.y - thickness,
179 rect.max.x,
180 kb_rect.min.y
181 ];
182
183 let input_bar = InputBar::new(
184 rect![
185 rect.min.x,
186 sp_rect.min.y - side + thickness,
187 rect.max.x,
188 sp_rect.min.y
189 ],
190 "",
191 "",
192 context,
193 );
194
195 let sp_rect2 = rect![
196 rect.min.x,
197 sp_rect.min.y - side,
198 rect.max.x,
199 sp_rect.min.y - side + thickness
200 ];
201
202 let code_area_rect = rect![
203 rect.min.x,
204 rect.min.y + side + big_thickness,
205 rect.max.x,
206 sp_rect2.min.y
207 ];
208 let code_area = CodeArea::new(code_area_rect, font_size, margin_width);
209 children.push(Box::new(code_area) as Box<dyn View>);
210
211 let separator = Filler::new(sp_rect2, BLACK);
212 children.push(Box::new(separator) as Box<dyn View>);
213
214 children.push(Box::new(input_bar) as Box<dyn View>);
215
216 let separator = Filler::new(sp_rect, BLACK);
217 children.push(Box::new(separator) as Box<dyn View>);
218
219 children.push(Box::new(keyboard) as Box<dyn View>);
220
221 let separator = Filler::new(
222 rect![
223 rect.min.x,
224 rect.max.y - side - small_thickness,
225 rect.max.x,
226 rect.max.y - side + big_thickness
227 ],
228 BLACK,
229 );
230 children.push(Box::new(separator) as Box<dyn View>);
231
232 let bottom_bar = BottomBar::new(
233 rect![
234 rect.min.x,
235 rect.max.y - side + big_thickness,
236 rect.max.x,
237 rect.max.y
238 ],
239 margin_width,
240 font_size,
241 );
242 children.push(Box::new(bottom_bar) as Box<dyn View>);
243
244 let font = &mut context.fonts.monospace.regular;
245 font.set_size((64.0 * font_size) as u32, dpi);
246 let char_width = font.plan(" ", None, None).width;
247 let line_height = font.ascender() - font.descender();
248 let margin_width_px = mm_to_px(margin_width as f32, dpi) as i32;
249 let columns_count = (code_area_rect.width() as i32 - 2 * margin_width_px) / char_width;
250 let lines_count = (code_area_rect.height() as i32 - 2 * margin_width_px) / line_height;
251
252 rq.add(RenderData::new(id, rect, UpdateMode::Full));
253 hub.send(Event::Focus(Some(ViewId::CalculatorInput))).ok();
254
255 Ok(Calculator {
256 id,
257 rect,
258 children,
259 process,
260 data: VecDeque::new(),
261 size: (lines_count as usize, columns_count as usize),
262 location: (0, 0),
263 history,
264 font_size,
265 margin_width,
266 })
267 }
268
269 fn append(&mut self, line: Line, context: &mut Context) {
270 let (lines_count, columns_count) = self.size;
271 let (mut current_line, mut current_column) = self.location;
272 let mut screen_lines = 0;
273
274 while screen_lines <= lines_count && current_line < self.data.len() {
275 screen_lines += (self.data[current_line].content[current_column..]
276 .chars()
277 .count() as f32
278 / columns_count as f32)
279 .ceil()
280 .max(1.0) as usize;
281 current_line += 1;
282 current_column = 0;
283 }
284
285 if screen_lines <= lines_count {
286 let added_lines = (line.content.chars().count() as f32 / columns_count as f32)
287 .ceil()
288 .max(1.0) as usize;
289 if screen_lines + added_lines > lines_count {
290 let filled_pages =
291 ((screen_lines + added_lines) as f32 / lines_count as f32).ceil() as usize;
292 let chars_count = columns_count * ((filled_pages - 1) * lines_count - screen_lines);
293 let current_column = line
294 .content
295 .char_indices()
296 .nth(chars_count)
297 .map_or(0, |v| v.0);
298 self.location = (self.data.len(), current_column);
299
300 if let Some(code_area) = self.children[2].downcast_mut::<CodeArea>() {
301 let last_line = Line {
302 content: line.content[current_column..].to_string(),
303 origin: line.origin,
304 };
305 code_area.set_data(vec![last_line], context);
306 }
307 } else {
308 if let Some(code_area) = self.children[2].downcast_mut::<CodeArea>() {
309 code_area.append(
310 line.clone(),
311 added_lines as i32,
312 screen_lines as i32,
313 context,
314 );
315 }
316 }
317 }
318
319 self.data.push_back(line);
320
321 if self.data.len() > self.history.size {
322 self.data.pop_front();
323 if self.location.0 == 0 {
324 self.location = (0, 0);
325 self.refresh(context);
326 } else {
327 self.location.0 -= 1;
328 }
329 }
330
331 self.history.cursor = self.data.len();
332 }
333
334 fn scroll(&mut self, mut delta_lines: i32, context: &mut Context) {
335 if delta_lines == 0 || self.data.is_empty() {
336 return;
337 }
338
339 let (_, columns_count) = self.size;
340 let (mut current_line, mut current_column) = self.location;
341
342 if delta_lines < 0 {
343 let lines_before = (self.data[current_line].content[..current_column]
344 .chars()
345 .count()
346 / columns_count) as i32;
347 delta_lines += lines_before;
348 if delta_lines < 0 && current_line > 0 {
349 current_line -= 1;
350 loop {
351 let lines_before = (self.data[current_line].content.chars().count() as f32
352 / columns_count as f32)
353 .ceil()
354 .max(1.0) as i32;
355 delta_lines += lines_before;
356 if delta_lines >= 0 || current_line == 0 {
357 break;
358 }
359 current_line -= 1;
360 }
361 }
362
363 let chars_count = delta_lines.max(0) as usize * columns_count;
364 let current_column = self.data[current_line]
365 .content
366 .char_indices()
367 .nth(chars_count)
368 .map_or(0, |v| v.0);
369 self.location = (current_line, current_column);
370 } else {
371 loop {
372 let lines_after = (self.data[current_line].content[current_column..]
373 .chars()
374 .count() as f32
375 / columns_count as f32)
376 .ceil()
377 .max(1.0) as i32;
378 delta_lines -= lines_after;
379 if delta_lines < 0 || current_line == self.data.len() - 1 {
380 break;
381 }
382 current_line += 1;
383 current_column = 0;
384 }
385
386 let chars_count = ((self.data[current_line].content[current_column..]
387 .chars()
388 .count() as f32
389 / columns_count as f32)
390 .ceil()
391 .max(1.0) as i32
392 + delta_lines.min(-1)) as usize
393 * columns_count;
394 current_column += self.data[current_line].content[current_column..]
395 .char_indices()
396 .nth(chars_count)
397 .map_or(0, |v| v.0);
398
399 self.location = (current_line, current_column);
400 }
401
402 self.refresh(context);
403 }
404
405 fn scroll_pixels(&mut self, dy: i32, context: &mut Context) {
406 let dpi = CURRENT_DEVICE.dpi;
407 let line_height = {
408 let font = &mut context.fonts.monospace.regular;
409 font.set_size((64.0 * self.font_size) as u32, dpi);
410 font.ascender() - font.descender()
411 };
412 let delta_lines = (dy as f32 / line_height as f32).round() as i32;
413
414 self.scroll(delta_lines, context);
415 }
416
417 fn scroll_page(&mut self, dir: CycleDir, context: &mut Context) {
418 let sgn = if dir == CycleDir::Previous { -1 } else { 1 };
419 let delta_lines = sgn * self.size.0 as i32;
420 self.scroll(delta_lines, context);
421 }
422
423 fn refresh(&mut self, context: &mut Context) {
424 let mut data = Vec::new();
425 let (mut current_line, mut current_column) = self.location;
426 let (lines_count, columns_count) = self.size;
427
428 let mut screen_lines = 0;
429
430 while screen_lines < lines_count && current_line < self.data.len() {
431 let mut line = Line {
432 content: self.data[current_line].content[current_column..].to_string(),
433 origin: self.data[current_line].origin,
434 };
435 screen_lines += (line.content.chars().count() as f32 / columns_count as f32)
436 .ceil()
437 .max(1.0) as usize;
438 if screen_lines > lines_count {
439 let delta = screen_lines - lines_count;
440 let chars_count = columns_count
441 * ((line.content.chars().count() as f32 / columns_count as f32)
442 .ceil()
443 .max(1.0) as usize
444 - delta);
445 let column_cut = line
446 .content
447 .char_indices()
448 .nth(chars_count)
449 .map_or(0, |v| v.0);
450 line.content = line.content[..column_cut].to_string();
451 }
452 data.push(line);
453 current_line += 1;
454 current_column = 0;
455 }
456
457 if let Some(code_area) = self.children[2].downcast_mut::<CodeArea>() {
458 code_area.set_data(data, context);
459 }
460 }
461
462 fn history_navigate(
463 &mut self,
464 dir: CycleDir,
465 honor_prefix: bool,
466 rq: &mut RenderQueue,
467 context: &mut Context,
468 ) {
469 let beginning = if honor_prefix {
470 self.children[4]
471 .downcast_ref::<InputBar>()
472 .unwrap()
473 .text_before_cursor()
474 } else {
475 ""
476 };
477
478 let cursor_opt = match dir {
479 CycleDir::Previous => self
480 .data
481 .iter()
482 .enumerate()
483 .rev()
484 .find(|(index, line)| {
485 *index < self.history.cursor
486 && line.origin == LineOrigin::Input
487 && line.content.starts_with(beginning)
488 })
489 .map(|(index, _)| index),
490 CycleDir::Next => self
491 .data
492 .iter()
493 .enumerate()
494 .find(|(index, line)| {
495 *index > self.history.cursor
496 && line.origin == LineOrigin::Input
497 && line.content.starts_with(beginning)
498 })
499 .map(|(index, _)| index),
500 };
501
502 if let Some(cursor) = cursor_opt {
503 let line = self.data[cursor].content.as_str();
504 if let Some(input_bar) = self.children[4].downcast_mut::<InputBar>() {
505 input_bar.set_text(line, !honor_prefix, rq, context);
506 }
507 self.history.cursor = cursor;
508 }
509 }
510
511 fn update_size(&mut self, rq: &mut RenderQueue, context: &mut Context) {
512 let dpi = CURRENT_DEVICE.dpi;
513 let font = &mut context.fonts.monospace.regular;
514 font.set_size((64.0 * self.font_size) as u32, dpi);
515 let char_width = font.plan(" ", None, None).width;
516 let line_height = font.ascender() - font.descender();
517 let margin_width_px = mm_to_px(self.margin_width as f32, dpi) as i32;
518 if let Some(code_area) = self.children[2].downcast_mut::<CodeArea>() {
519 let columns_count =
520 (code_area.rect().width() as i32 - 2 * margin_width_px) / char_width;
521 let lines_count =
522 (code_area.rect().height() as i32 - 2 * margin_width_px) / line_height;
523 self.size = (lines_count as usize, columns_count as usize);
524 code_area.update(self.font_size, self.margin_width);
525 }
526 if let Some(bottom_bar) = self.children[8].downcast_mut::<BottomBar>() {
527 bottom_bar.update_font_size(self.font_size, rq);
528 bottom_bar.update_margin_width(self.margin_width, rq);
529 }
530 }
531
532 fn set_font_size(&mut self, font_size: f32, rq: &mut RenderQueue, context: &mut Context) {
533 self.font_size = font_size;
534 self.update_size(rq, context);
535 self.refresh(context);
536 }
537
538 fn set_margin_width(&mut self, margin_width: i32, rq: &mut RenderQueue, context: &mut Context) {
539 self.margin_width = margin_width;
540 self.update_size(rq, context);
541 self.refresh(context);
542 }
543
544 fn toggle_margin_width_menu(
545 &mut self,
546 rect: Rectangle,
547 enable: Option<bool>,
548 rq: &mut RenderQueue,
549 context: &mut Context,
550 ) {
551 if let Some(index) = locate_by_id(self, ViewId::MarginWidthMenu) {
552 if let Some(true) = enable {
553 return;
554 }
555
556 rq.add(RenderData::expose(
557 *self.child(index).rect(),
558 UpdateMode::Gui,
559 ));
560 self.children.remove(index);
561 } else {
562 if let Some(false) = enable {
563 return;
564 }
565
566 let entries = (0..=10)
567 .map(|mw| {
568 EntryKind::RadioButton(
569 format!("{}", mw),
570 EntryId::SetMarginWidth(mw),
571 mw == self.margin_width,
572 )
573 })
574 .collect();
575 let margin_width_menu = Menu::new(
576 rect,
577 ViewId::MarginWidthMenu,
578 MenuKind::DropDown,
579 entries,
580 context,
581 );
582 rq.add(RenderData::new(
583 margin_width_menu.id(),
584 *margin_width_menu.rect(),
585 UpdateMode::Gui,
586 ));
587 self.children
588 .push(Box::new(margin_width_menu) as Box<dyn View>);
589 }
590 }
591
592 fn toggle_font_size_menu(
593 &mut self,
594 rect: Rectangle,
595 enable: Option<bool>,
596 rq: &mut RenderQueue,
597 context: &mut Context,
598 ) {
599 if let Some(index) = locate_by_id(self, ViewId::FontSizeMenu) {
600 if let Some(true) = enable {
601 return;
602 }
603
604 rq.add(RenderData::expose(
605 *self.child(index).rect(),
606 UpdateMode::Gui,
607 ));
608 self.children.remove(index);
609 } else {
610 if let Some(false) = enable {
611 return;
612 }
613
614 let entries = (0..=20)
615 .map(|v| {
616 let fs = 6.0 + v as f32 / 10.0;
617 EntryKind::RadioButton(
618 format!("{:.1}", fs),
619 EntryId::SetFontSize(v),
620 (fs - self.font_size).abs() < 0.05,
621 )
622 })
623 .collect();
624 let font_size_menu = Menu::new(
625 rect,
626 ViewId::FontSizeMenu,
627 MenuKind::DropDown,
628 entries,
629 context,
630 );
631 rq.add(RenderData::new(
632 font_size_menu.id(),
633 *font_size_menu.rect(),
634 UpdateMode::Gui,
635 ));
636 self.children
637 .push(Box::new(font_size_menu) as Box<dyn View>);
638 }
639 }
640
641 fn reseed(&mut self, rq: &mut RenderQueue, context: &mut Context) {
642 if let Some(top_bar) = self.child_mut(0).downcast_mut::<TopBar>() {
643 top_bar.reseed(rq, context);
644 }
645
646 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
647 }
648
649 fn quit(&mut self, context: &mut Context) {
650 unsafe { libc::kill(self.process.id() as libc::pid_t, libc::SIGTERM) };
651 self.process
652 .wait()
653 .map_err(|e| error!("Can't wait for child process: {:#}.", e))
654 .ok();
655 context.settings.calculator.font_size = self.font_size;
656 context.settings.calculator.margin_width = self.margin_width;
657 }
658}
659
660impl View for Calculator {
661 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, hub, _bus, rq, context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
662 fn handle_event(
663 &mut self,
664 evt: &Event,
665 hub: &Hub,
666 _bus: &mut Bus,
667 rq: &mut RenderQueue,
668 context: &mut Context,
669 ) -> bool {
670 match *evt {
671 Event::Submit(ViewId::CalculatorInput, ref line) => {
672 self.append(
673 Line {
674 origin: LineOrigin::Input,
675 content: line.to_string(),
676 },
677 context,
678 );
679 if let Some(input_bar) = self.children[4].downcast_mut::<InputBar>() {
680 input_bar.set_text("", true, rq, context);
681 }
682 if let Some(stdin) = self.process.stdin.as_mut() {
683 writeln!(stdin, "{}", line).ok();
684 }
685 true
686 }
687 Event::Scroll(dy) => {
688 self.scroll_pixels(dy, context);
689 true
690 }
691 Event::Page(dir) => {
692 self.scroll_page(dir, context);
693 true
694 }
695 Event::ProcessLine(origin, ref line) => {
696 self.append(
697 Line {
698 origin,
699 content: line.replace('\t', " "),
700 },
701 context,
702 );
703 true
704 }
705 Event::History(dir, honor_prefix) => {
706 self.history_navigate(dir, honor_prefix, rq, context);
707 true
708 }
709 Event::Select(EntryId::SetFontSize(v)) => {
710 let font_size = 6.0 + v as f32 / 10.0;
711 self.set_font_size(font_size, rq, context);
712 true
713 }
714 Event::Select(EntryId::SetMarginWidth(width)) => {
715 self.set_margin_width(width, rq, context);
716 true
717 }
718 Event::Gesture(GestureEvent::Rotate { quarter_turns, .. }) if quarter_turns != 0 => {
719 let (_, dir) = CURRENT_DEVICE.mirroring_scheme();
720 let n = (4 + (context.display.rotation - dir * quarter_turns)) % 4;
721 hub.send(Event::Select(EntryId::Rotate(n))).ok();
722 true
723 }
724 Event::Gesture(GestureEvent::HoldFingerShort(center, ..))
725 if self.rect.includes(center) =>
726 {
727 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Full));
728 true
729 }
730 Event::ToggleNear(ViewId::MainMenu, rect) => {
731 toggle_main_menu(self, rect, None, rq, context);
732 true
733 }
734 Event::ToggleNear(ViewId::BatteryMenu, rect) => {
735 toggle_battery_menu(self, rect, None, rq, context);
736 true
737 }
738 Event::ToggleNear(ViewId::ClockMenu, rect) => {
739 toggle_clock_menu(self, rect, None, rq, context);
740 true
741 }
742 Event::ToggleNear(ViewId::MarginWidthMenu, rect) => {
743 self.toggle_margin_width_menu(rect, None, rq, context);
744 true
745 }
746 Event::ToggleNear(ViewId::FontSizeMenu, rect) => {
747 self.toggle_font_size_menu(rect, None, rq, context);
748 true
749 }
750 Event::Back | Event::Select(EntryId::Quit) => {
751 self.quit(context);
752 hub.send(Event::Back).ok();
753 true
754 }
755 Event::Reseed => {
756 self.reseed(rq, context);
757 true
758 }
759 _ => false,
760 }
761 }
762
763 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _fb, _fonts, _rect), fields(rect = ?_rect)))]
764 fn render(&self, _fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {}
765
766 fn resize(&mut self, rect: Rectangle, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) {
767 let dpi = CURRENT_DEVICE.dpi;
768 let (small_height, big_height) = (
769 scale_by_dpi(SMALL_BAR_HEIGHT, dpi) as i32,
770 scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32,
771 );
772 let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32;
773 let (small_thickness, big_thickness) = halves(thickness);
774 let side = small_height;
775
776 self.children.retain(|child| !child.is::<Menu>());
777
778 let top_bar_rect = rect![
780 rect.min.x,
781 rect.min.y,
782 rect.max.x,
783 rect.min.y + side - small_thickness
784 ];
785 self.children[0].resize(top_bar_rect, hub, rq, context);
786
787 let separator_rect = rect![
788 rect.min.x,
789 rect.min.y + side - small_thickness,
790 rect.max.x,
791 rect.min.y + side + big_thickness
792 ];
793 self.children[1].resize(separator_rect, hub, rq, context);
794
795 let kb_rect = rect![
796 rect.min.x,
797 rect.max.y - (small_height + 3 * big_height) as i32 + big_thickness,
798 rect.max.x,
799 rect.max.y - small_height - small_thickness
800 ];
801 self.children[6].resize(kb_rect, hub, rq, context);
802 let kb_rect = *self.children[6].rect();
803
804 let sp_rect = rect![
805 rect.min.x,
806 kb_rect.min.y - thickness,
807 rect.max.x,
808 kb_rect.min.y
809 ];
810
811 let sp_rect2 = rect![
812 rect.min.x,
813 sp_rect.min.y - side,
814 rect.max.x,
815 sp_rect.min.y - side + thickness
816 ];
817
818 let input_bar_rect = rect![
819 rect.min.x,
820 sp_rect.min.y - side + thickness,
821 rect.max.x,
822 sp_rect.min.y
823 ];
824
825 let code_area_rect = rect![
826 rect.min.x,
827 rect.min.y + side + big_thickness,
828 rect.max.x,
829 sp_rect2.min.y
830 ];
831
832 self.children[2].resize(code_area_rect, hub, rq, context);
833 self.children[3].resize(sp_rect2, hub, rq, context);
834 self.children[4].resize(input_bar_rect, hub, rq, context);
835 self.children[5].resize(sp_rect, hub, rq, context);
836
837 let sp_rect = rect![
838 rect.min.x,
839 rect.max.y - side - small_thickness,
840 rect.max.x,
841 rect.max.y - side + big_thickness
842 ];
843
844 self.children[7].resize(sp_rect, hub, rq, context);
845
846 let bottom_bar_rect = rect![
847 rect.min.x,
848 rect.max.y - side + big_thickness,
849 rect.max.x,
850 rect.max.y
851 ];
852
853 self.children[8].resize(bottom_bar_rect, hub, rq, context);
854
855 for i in 9..self.children.len() {
856 self.children[i].resize(rect, hub, rq, context);
857 }
858
859 self.update_size(&mut RenderQueue::new(), context);
860 self.refresh(context);
861
862 self.rect = rect;
863 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Full));
864 }
865
866 fn rect(&self) -> &Rectangle {
867 &self.rect
868 }
869
870 fn rect_mut(&mut self) -> &mut Rectangle {
871 &mut self.rect
872 }
873
874 fn children(&self) -> &Vec<Box<dyn View>> {
875 &self.children
876 }
877
878 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
879 &mut self.children
880 }
881
882 fn id(&self) -> Id {
883 self.id
884 }
885}