cadmus_core/view/home/
directory.rs1use crate::color::{BLACK, TEXT_BUMP_SMALL, WHITE};
2use crate::context::Context;
3use crate::device::CURRENT_DEVICE;
4use crate::font::{font_from_style, Fonts, NORMAL_STYLE};
5use crate::framebuffer::Framebuffer;
6use crate::geom::{BorderSpec, CornerSpec, Rectangle};
7use crate::gesture::GestureEvent;
8use crate::unit::scale_by_dpi;
9use crate::view::{Align, Bus, Event, Hub, Id, RenderQueue, View, ID_FEEDER};
10use crate::view::{BORDER_RADIUS_SMALL, THICKNESS_SMALL};
11use std::path::{Path, PathBuf};
12
13pub struct Directory {
14 id: Id,
15 rect: Rectangle,
16 children: Vec<Box<dyn View>>,
17 pub path: PathBuf,
18 selected: bool,
19 align: Align,
20 max_width: Option<i32>,
21}
22
23impl Directory {
24 pub fn new(
25 rect: Rectangle,
26 path: PathBuf,
27 selected: bool,
28 align: Align,
29 max_width: Option<i32>,
30 ) -> Directory {
31 Directory {
32 id: ID_FEEDER.next(),
33 rect,
34 children: Vec::new(),
35 path,
36 selected,
37 align,
38 max_width,
39 }
40 }
41
42 pub fn update_selected(&mut self, current_directory: &Path) -> bool {
43 let selected = current_directory.starts_with(&self.path);
44 self.selected = selected;
45 selected
46 }
47}
48
49impl View for Directory {
50 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, bus, _rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
51 fn handle_event(
52 &mut self,
53 evt: &Event,
54 _hub: &Hub,
55 bus: &mut Bus,
56 _rq: &mut RenderQueue,
57 _context: &mut Context,
58 ) -> bool {
59 match *evt {
60 Event::Gesture(GestureEvent::Tap(center)) if self.rect.includes(center) => {
61 bus.push_back(Event::ToggleSelectDirectory(self.path.clone()));
62 true
63 }
64 _ => false,
65 }
66 }
67 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, fonts, _rect), fields(rect = ?_rect)))]
68 fn render(&self, fb: &mut dyn Framebuffer, _rect: Rectangle, fonts: &mut Fonts) {
69 let dpi = CURRENT_DEVICE.dpi;
70 fb.draw_rectangle(&self.rect, TEXT_BUMP_SMALL[0]);
71 let font = font_from_style(fonts, &NORMAL_STYLE, dpi);
72 let x_height = font.x_heights.0 as i32;
73 let text = self.path.file_name().unwrap().to_string_lossy();
74 let plan = font.plan(text, self.max_width, None);
75
76 let dx = self.align.offset(plan.width, self.rect.width() as i32);
77 let dy = (self.rect.height() as i32 - x_height) / 2;
78
79 if self.selected {
80 let padding = font.em() as i32 / 2 - scale_by_dpi(3.0, dpi) as i32;
81 let small_x_height = font.x_heights.0 as i32;
82 let bg_width = plan.width + 2 * padding;
83 let bg_height = 3 * small_x_height;
84 let x_offset = dx - padding;
85 let y_offset = dy + x_height - 2 * small_x_height;
86 let pt = self.rect.min + pt!(x_offset, y_offset);
87 let bg_rect = rect![pt, pt + pt!(bg_width, bg_height)];
88 let border_radius = scale_by_dpi(BORDER_RADIUS_SMALL, dpi) as i32;
89 let border_thickness = scale_by_dpi(THICKNESS_SMALL, dpi) as u16;
90 fb.draw_rounded_rectangle_with_border(
91 &bg_rect,
92 &CornerSpec::Uniform(border_radius),
93 &BorderSpec {
94 thickness: border_thickness,
95 color: BLACK,
96 },
97 &WHITE,
98 );
99 }
100
101 let pt = pt!(self.rect.min.x + dx, self.rect.max.y - dy);
102 font.render(fb, TEXT_BUMP_SMALL[1], &plan, pt);
103 }
104
105 fn rect(&self) -> &Rectangle {
106 &self.rect
107 }
108
109 fn rect_mut(&mut self) -> &mut Rectangle {
110 &mut self.rect
111 }
112
113 fn children(&self) -> &Vec<Box<dyn View>> {
114 &self.children
115 }
116
117 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
118 &mut self.children
119 }
120
121 fn id(&self) -> Id {
122 self.id
123 }
124}