cadmus_core/view/
named_input.rs

1use super::common::shift;
2use super::input_field::InputField;
3use super::label::Label;
4use super::{Align, Bus, Event, Hub, Id, RenderQueue, View, ViewId, ID_FEEDER};
5use super::{BORDER_RADIUS_MEDIUM, THICKNESS_LARGE};
6use crate::color::{BLACK, WHITE};
7use crate::context::Context;
8use crate::device::CURRENT_DEVICE;
9use crate::font::{font_from_style, Fonts, NORMAL_STYLE};
10use crate::framebuffer::Framebuffer;
11use crate::geom::{big_half, halves, BorderSpec, CornerSpec, Rectangle};
12use crate::gesture::GestureEvent;
13use crate::unit::scale_by_dpi;
14
15pub struct NamedInput {
16    id: Id,
17    rect: Rectangle,
18    children: Vec<Box<dyn View>>,
19    view_id: ViewId,
20}
21
22impl NamedInput {
23    pub fn new(
24        text: String,
25        view_id: ViewId,
26        input_id: ViewId,
27        input_size: usize,
28        context: &mut Context,
29    ) -> NamedInput {
30        let id = ID_FEEDER.next();
31        let dpi = CURRENT_DEVICE.dpi;
32        let (width, height) = context.display.dims;
33
34        let input_size = input_size.max(3);
35        let mut children = Vec::new();
36        let font = font_from_style(&mut context.fonts, &NORMAL_STYLE, dpi);
37        let x_height = font.x_heights.0 as i32;
38        let padding = font.em() as i32;
39
40        let mut label_width = font.plan(&text, None, None).width;
41        let mut input_width = font.plan(&"0".repeat(input_size), None, None).width;
42        let mut total_width = 5 * padding + label_width + input_width;
43        let delta = width as i32 - total_width;
44
45        if delta < 0 {
46            let label_ratio = label_width as f32 / (label_width + input_width) as f32;
47            let label_delta = (delta as f32 * label_ratio) as i32;
48            let input_delta = delta - label_delta;
49            label_width = (label_width + label_delta).abs();
50            input_width = (input_width + input_delta).abs();
51            total_width += delta;
52        }
53
54        let (small_half_width, big_half_width) = halves(total_width);
55        let big_half_padding = big_half(padding);
56
57        let anchor = pt!(width as i32 / 2, height as i32 / 3);
58        let x_min = anchor.x - small_half_width;
59        let x_max = anchor.x + big_half_width;
60        let y_min = anchor.y - 4 * x_height;
61        let y_max = anchor.y + 4 * x_height;
62
63        let label = Label::new(
64            rect![
65                x_min + big_half_padding,
66                y_min + x_height,
67                x_min + big_half_padding + padding + label_width,
68                y_max - x_height
69            ],
70            text,
71            Align::Center,
72        );
73        children.push(Box::new(label) as Box<dyn View>);
74
75        let input_field = InputField::new(
76            rect![
77                x_max - 3 * padding - input_width,
78                y_min + 2 * x_height,
79                x_max - padding,
80                y_max - 2 * x_height
81            ],
82            input_id,
83        );
84        children.push(Box::new(input_field) as Box<dyn View>);
85
86        let rect = rect![x_min, y_min, x_max, y_max];
87
88        NamedInput {
89            id,
90            rect,
91            children,
92            view_id,
93        }
94    }
95
96    pub fn set_text(&mut self, text: &str, rq: &mut RenderQueue, context: &mut Context) {
97        if let Some(input_field) = self.children[1].downcast_mut::<InputField>() {
98            input_field.set_text(text, true, rq, context);
99        }
100    }
101}
102
103impl View for NamedInput {
104    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, bus, _rq, context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
105    fn handle_event(
106        &mut self,
107        evt: &Event,
108        _hub: &Hub,
109        bus: &mut Bus,
110        _rq: &mut RenderQueue,
111        context: &mut Context,
112    ) -> bool {
113        match *evt {
114            Event::Submit(..) => {
115                bus.push_back(Event::Close(self.view_id));
116                false
117            }
118            Event::Gesture(GestureEvent::Tap(center))
119            | Event::Gesture(GestureEvent::HoldFingerShort(center, _)) => {
120                if !self.rect.includes(center) && !context.kb_rect.includes(center) {
121                    if !context.kb_rect.is_empty() {
122                        bus.push_back(Event::Close(self.view_id));
123                    }
124                    true
125                } else {
126                    self.rect.includes(center)
127                }
128            }
129            Event::Gesture(..) => true,
130            _ => false,
131        }
132    }
133
134    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, _fonts, _rect), fields(rect = ?_rect)))]
135    fn render(&self, fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {
136        let dpi = CURRENT_DEVICE.dpi;
137        let border_radius = scale_by_dpi(BORDER_RADIUS_MEDIUM, dpi) as i32;
138        let border_thickness = scale_by_dpi(THICKNESS_LARGE, dpi) as u16;
139        fb.draw_rounded_rectangle_with_border(
140            &self.rect,
141            &CornerSpec::Uniform(border_radius),
142            &BorderSpec {
143                thickness: border_thickness,
144                color: BLACK,
145            },
146            &WHITE,
147        );
148    }
149
150    fn resize(
151        &mut self,
152        _rect: Rectangle,
153        _hub: &Hub,
154        _rq: &mut RenderQueue,
155        context: &mut Context,
156    ) {
157        let (width, height) = context.display.dims;
158        let dx = (width as i32 - height as i32) / 2;
159        let dy = (height as i32 - width as i32) / 3;
160        let delta = pt!(dx, dy);
161        shift(self, delta);
162    }
163
164    fn is_background(&self) -> bool {
165        true
166    }
167
168    fn view_id(&self) -> Option<ViewId> {
169        Some(self.view_id)
170    }
171
172    fn rect(&self) -> &Rectangle {
173        &self.rect
174    }
175
176    fn rect_mut(&mut self) -> &mut Rectangle {
177        &mut self.rect
178    }
179
180    fn children(&self) -> &Vec<Box<dyn View>> {
181        &self.children
182    }
183
184    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
185        &mut self.children
186    }
187
188    fn id(&self) -> Id {
189        self.id
190    }
191}