agg_gui/widgets/
progress_bar.rs1use std::sync::Arc;
4
5use crate::color::Color;
6use crate::draw_ctx::DrawCtx;
7use crate::event::{Event, EventResult};
8use crate::geometry::{Rect, Size};
9use crate::layout_props::{HAnchor, Insets, VAnchor, WidgetBase};
10use crate::text::Font;
11use crate::widget::Widget;
12
13const BAR_H: f64 = 18.0;
14const WIDGET_H: f64 = 24.0;
15
16pub struct ProgressBar {
18 bounds: Rect,
19 children: Vec<Box<dyn Widget>>, base: WidgetBase,
21 value: f64,
22 show_text: bool,
23 font: Arc<Font>,
24 font_size: f64,
25 fill_color: Option<Color>,
26}
27
28impl ProgressBar {
29 pub fn new(value: f64, font: Arc<Font>) -> Self {
30 Self {
31 bounds: Rect::default(),
32 children: Vec::new(),
33 base: WidgetBase::new(),
34 value: value.clamp(0.0, 1.0),
35 show_text: true,
36 font,
37 font_size: 11.0,
38 fill_color: None,
39 }
40 }
41
42 pub fn with_show_text(mut self, show: bool) -> Self {
43 self.show_text = show;
44 self
45 }
46 pub fn with_fill_color(mut self, color: Color) -> Self {
47 self.fill_color = Some(color);
48 self
49 }
50
51 pub fn with_margin(mut self, m: Insets) -> Self {
52 self.base.margin = m;
53 self
54 }
55 pub fn with_h_anchor(mut self, h: HAnchor) -> Self {
56 self.base.h_anchor = h;
57 self
58 }
59 pub fn with_v_anchor(mut self, v: VAnchor) -> Self {
60 self.base.v_anchor = v;
61 self
62 }
63 pub fn with_min_size(mut self, s: Size) -> Self {
64 self.base.min_size = s;
65 self
66 }
67 pub fn with_max_size(mut self, s: Size) -> Self {
68 self.base.max_size = s;
69 self
70 }
71
72 pub fn set_value(&mut self, v: f64) {
73 self.value = v.clamp(0.0, 1.0);
74 }
75
76 pub fn value(&self) -> f64 {
77 self.value
78 }
79}
80
81impl Widget for ProgressBar {
82 fn type_name(&self) -> &'static str {
83 "ProgressBar"
84 }
85 fn bounds(&self) -> Rect {
86 self.bounds
87 }
88 fn set_bounds(&mut self, b: Rect) {
89 self.bounds = b;
90 }
91 fn children(&self) -> &[Box<dyn Widget>] {
92 &self.children
93 }
94 fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
95 &mut self.children
96 }
97
98 fn margin(&self) -> Insets {
99 self.base.margin
100 }
101 fn h_anchor(&self) -> HAnchor {
102 self.base.h_anchor
103 }
104 fn v_anchor(&self) -> VAnchor {
105 self.base.v_anchor
106 }
107 fn min_size(&self) -> Size {
108 self.base.min_size
109 }
110 fn max_size(&self) -> Size {
111 self.base.max_size
112 }
113
114 fn layout(&mut self, available: Size) -> Size {
115 Size::new(available.width, WIDGET_H)
116 }
117
118 fn paint(&mut self, ctx: &mut dyn DrawCtx) {
119 let v = ctx.visuals();
120 let w = self.bounds.width;
121 let h = self.bounds.height;
122 let bar_y = (h - BAR_H) * 0.5;
123 let r = BAR_H * 0.5;
124
125 ctx.set_fill_color(v.track_bg);
127 ctx.begin_path();
128 ctx.rounded_rect(0.0, bar_y, w, BAR_H, r);
129 ctx.fill();
130
131 let fill_color = self.fill_color.unwrap_or(v.accent);
133 let fill_w = (w * self.value).max(0.0);
134 if fill_w >= 1.0 {
135 ctx.set_fill_color(fill_color);
136 ctx.begin_path();
137 ctx.rounded_rect(0.0, bar_y, fill_w, BAR_H, r);
138 ctx.fill();
139 }
140
141 if self.show_text {
143 let label = format!("{:.0}%", self.value * 100.0);
144 ctx.set_font(Arc::clone(&self.font));
145 ctx.set_font_size(self.font_size);
146 let mid = w * 0.5;
148 let text_color = if fill_w > mid {
149 Color::rgba(1.0, 1.0, 1.0, 0.9)
150 } else {
151 v.text_dim
152 };
153 ctx.set_fill_color(text_color);
154 if let Some(m) = ctx.measure_text(&label) {
155 let tx = (w - m.width) * 0.5;
156 let ty = bar_y + BAR_H * 0.5 - (m.ascent - m.descent) * 0.5;
157 ctx.fill_text(&label, tx, ty);
158 }
159 }
160 }
161
162 fn on_event(&mut self, _: &Event) -> EventResult {
163 EventResult::Ignored
164 }
165}