cadmus_core/view/calculator/
code_area.rs1use 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}