cadmus_core/view/calculator/
mod.rs

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        // Top bar.
779        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}