cadmus_core/view/calculator/
code_area.rs

1use super::{Line, LineOrigin};
2use crate::color::TEXT_NORMAL;
3use crate::context::Context;
4use crate::device::CURRENT_DEVICE;
5use crate::font::Fonts;
6use crate::framebuffer::{Framebuffer, UpdateMode};
7use crate::geom::{CycleDir, Dir, Rectangle};
8use crate::gesture::GestureEvent;
9use crate::input::{ButtonCode, ButtonStatus, DeviceEvent};
10use crate::unit::mm_to_px;
11use crate::view::{Bus, Event, Hub, Id, RenderQueue, View, ID_FEEDER};
12
13pub struct CodeArea {
14    id: Id,
15    rect: Rectangle,
16    children: Vec<Box<dyn View>>,
17    data: Vec<Line>,
18    font_size: f32,
19    margin_width: i32,
20}
21
22impl CodeArea {
23    pub fn new(rect: Rectangle, font_size: f32, margin_width: i32) -> CodeArea {
24        CodeArea {
25            id: ID_FEEDER.next(),
26            rect,
27            children: Vec::new(),
28            data: Vec::new(),
29            font_size,
30            margin_width,
31        }
32    }
33
34    pub fn append(
35        &mut self,
36        line: Line,
37        added_lines: i32,
38        screen_lines: i32,
39        context: &mut Context,
40    ) {
41        let dpi = CURRENT_DEVICE.dpi;
42        let font = &mut context.fonts.monospace.regular;
43        font.set_size((64.0 * self.font_size) as u32, dpi);
44        let line_height = font.ascender() - font.descender();
45        let margin_width_px = mm_to_px(self.margin_width as f32, dpi) as i32;
46        let min_y = self.rect.min.y + margin_width_px + screen_lines * line_height;
47
48        let rect = rect![
49            self.rect.min.x + margin_width_px,
50            min_y,
51            self.rect.max.x - margin_width_px,
52            min_y + added_lines * line_height
53        ];
54        self.data.push(line);
55        self.render(context.fb.as_mut(), rect, &mut context.fonts);
56        context.fb.update(&rect, UpdateMode::Gui).ok();
57    }
58
59    pub fn set_data(&mut self, data: Vec<Line>, context: &mut Context) {
60        self.data = data;
61        self.render(context.fb.as_mut(), self.rect, &mut context.fonts);
62        context.fb.update(&self.rect, UpdateMode::Gui).ok();
63    }
64
65    pub fn update(&mut self, font_size: f32, margin_width: i32) {
66        self.font_size = font_size;
67        self.margin_width = margin_width;
68    }
69}
70
71impl View for CodeArea {
72    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, bus, _rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
73    fn handle_event(
74        &mut self,
75        evt: &Event,
76        _hub: &Hub,
77        bus: &mut Bus,
78        _rq: &mut RenderQueue,
79        _context: &mut Context,
80    ) -> bool {
81        match *evt {
82            Event::Gesture(GestureEvent::Swipe {
83                dir, start, end, ..
84            }) if self.rect.includes(start) => {
85                match dir {
86                    Dir::South | Dir::North => bus.push_back(Event::Scroll(start.y - end.y)),
87                    Dir::West => bus.push_back(Event::Page(CycleDir::Next)),
88                    Dir::East => bus.push_back(Event::Page(CycleDir::Previous)),
89                }
90                true
91            }
92            Event::Device(DeviceEvent::Button {
93                code,
94                status: ButtonStatus::Pressed,
95                ..
96            }) => {
97                match code {
98                    ButtonCode::Backward => bus.push_back(Event::Page(CycleDir::Previous)),
99                    ButtonCode::Forward => bus.push_back(Event::Page(CycleDir::Next)),
100                    _ => (),
101                }
102                true
103            }
104            Event::Gesture(GestureEvent::Tap(center)) if self.rect.includes(center) => {
105                let middle_x = (self.rect.min.x + self.rect.max.x) / 2;
106                if center.x < middle_x {
107                    bus.push_back(Event::Page(CycleDir::Previous));
108                } else {
109                    bus.push_back(Event::Page(CycleDir::Next));
110                }
111                true
112            }
113            _ => false,
114        }
115    }
116
117    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, fonts), fields(rect = ?rect)))]
118    fn render(&self, fb: &mut dyn Framebuffer, rect: Rectangle, fonts: &mut Fonts) {
119        let dpi = CURRENT_DEVICE.dpi;
120
121        if let Some(irect) = self.rect.intersection(&rect) {
122            fb.draw_rectangle(&irect, TEXT_NORMAL[0]);
123        }
124
125        let font = &mut fonts.monospace.regular;
126        font.set_size((64.0 * self.font_size) as u32, dpi);
127        let line_height = font.ascender() - font.descender();
128        let char_width = font.plan(" ", None, None).width;
129        let padding = mm_to_px(self.margin_width as f32, dpi) as i32;
130
131        let mut x = self.rect.min.x + padding;
132        let mut y = self.rect.min.y + padding + font.ascender();
133
134        for line in &self.data {
135            let font = match line.origin {
136                LineOrigin::Input => &mut fonts.monospace.bold,
137                LineOrigin::Output => &mut fonts.monospace.regular,
138                LineOrigin::Error => &mut fonts.monospace.italic,
139            };
140
141            font.set_size((64.0 * self.font_size) as u32, dpi);
142
143            for c in line.content.chars() {
144                if x > self.rect.max.x - padding - char_width {
145                    y += line_height;
146                    x = self.rect.min.x + padding;
147                }
148                if y >= rect.min.y {
149                    let plan = font.plan(&c.to_string(), None, None);
150                    font.render(fb, TEXT_NORMAL[1], &plan, pt!(x, y));
151                }
152                x += char_width;
153            }
154
155            y += line_height;
156            x = self.rect.min.x + padding;
157        }
158    }
159
160    fn render_rect(&self, rect: &Rectangle) -> Rectangle {
161        rect.intersection(&self.rect).unwrap_or(self.rect)
162    }
163
164    fn rect(&self) -> &Rectangle {
165        &self.rect
166    }
167
168    fn rect_mut(&mut self) -> &mut Rectangle {
169        &mut self.rect
170    }
171
172    fn children(&self) -> &Vec<Box<dyn View>> {
173        &self.children
174    }
175
176    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
177        &mut self.children
178    }
179
180    fn id(&self) -> Id {
181        self.id
182    }
183}