cadmus_core/view/
progress_bar.rs1use super::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ID_FEEDER};
2use crate::color::{BLACK, PROGRESS_EMPTY, PROGRESS_FULL, WHITE};
3use crate::context::Context;
4use crate::device::CURRENT_DEVICE;
5use crate::font::Fonts;
6use crate::framebuffer::{Framebuffer, UpdateMode};
7use crate::geom::{halves, BorderSpec, CornerSpec, Rectangle};
8use crate::unit::scale_by_dpi;
9
10const PROGRESS_HEIGHT: f32 = 7.0;
11const BORDER_THICKNESS: f32 = 1.0;
12
13pub struct ProgressBar {
20 id: Id,
21 rect: Rectangle,
22 children: Vec<Box<dyn View>>,
23 percent: u8,
24}
25
26impl ProgressBar {
27 pub fn new(rect: Rectangle, percent: u8) -> ProgressBar {
29 ProgressBar {
30 id: ID_FEEDER.next(),
31 rect,
32 children: Vec::new(),
33 percent: percent.min(100),
34 }
35 }
36
37 pub fn update(&mut self, percent: u8, rq: &mut RenderQueue) {
39 let clamped = percent.min(100);
40
41 if self.percent != clamped {
42 self.percent = clamped;
43 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
44 }
45 }
46}
47
48impl View for ProgressBar {
49 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, _bus, _rq, _context), fields(event = ?_evt), ret(level = tracing::Level::TRACE)))]
50 fn handle_event(
51 &mut self,
52 _evt: &Event,
53 _hub: &Hub,
54 _bus: &mut Bus,
55 _rq: &mut RenderQueue,
56 _context: &mut Context,
57 ) -> bool {
58 false
59 }
60
61 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, _fonts, _rect), fields(rect = ?_rect)))]
62 fn render(&self, fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {
63 let dpi = CURRENT_DEVICE.dpi;
64 let progress_height = scale_by_dpi(PROGRESS_HEIGHT, dpi) as i32;
65 let border_thickness = scale_by_dpi(BORDER_THICKNESS, dpi) as u16;
66
67 fb.draw_rectangle(&self.rect, WHITE);
68
69 let (small_half, _big_half) = halves(progress_height);
70 let (small_padding, big_padding) = halves(self.rect.height() as i32 - progress_height);
71
72 let track_rect = rect![
73 self.rect.min.x,
74 self.rect.min.y + small_padding,
75 self.rect.max.x,
76 self.rect.max.y - big_padding
77 ];
78
79 let fill_x =
80 self.rect.min.x + (self.rect.width() as f32 * self.percent as f32 / 100.0) as i32;
81
82 fb.draw_rounded_rectangle_with_border(
83 &track_rect,
84 &CornerSpec::Uniform(small_half),
85 &BorderSpec {
86 thickness: border_thickness,
87 color: BLACK,
88 },
89 &|x, _| {
90 if x < fill_x {
91 PROGRESS_FULL
92 } else {
93 PROGRESS_EMPTY
94 }
95 },
96 );
97 }
98
99 fn rect(&self) -> &Rectangle {
100 &self.rect
101 }
102
103 fn rect_mut(&mut self) -> &mut Rectangle {
104 &mut self.rect
105 }
106
107 fn children(&self) -> &Vec<Box<dyn View>> {
108 &self.children
109 }
110
111 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
112 &mut self.children
113 }
114
115 fn id(&self) -> Id {
116 self.id
117 }
118}