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
16#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
18#[derive(Clone, Debug)]
19pub struct ProgressBarProps {
20 pub value: f64,
22 pub show_text: bool,
23 pub font_size: f64,
24 pub fill_color: Option<Color>,
25}
26
27impl Default for ProgressBarProps {
28 fn default() -> Self {
29 Self {
30 value: 0.0,
31 show_text: true,
32 font_size: 11.0,
33 fill_color: None,
34 }
35 }
36}
37
38pub struct ProgressBar {
40 bounds: Rect,
41 children: Vec<Box<dyn Widget>>, base: WidgetBase,
43 pub props: ProgressBarProps,
44 font: Arc<Font>,
45}
46
47impl ProgressBar {
48 pub fn new(value: f64, font: Arc<Font>) -> Self {
49 Self {
50 bounds: Rect::default(),
51 children: Vec::new(),
52 base: WidgetBase::new(),
53 props: ProgressBarProps {
54 value: value.clamp(0.0, 1.0),
55 ..ProgressBarProps::default()
56 },
57 font,
58 }
59 }
60
61 pub fn with_show_text(mut self, show: bool) -> Self {
62 self.props.show_text = show;
63 self
64 }
65 pub fn with_fill_color(mut self, color: Color) -> Self {
66 self.props.fill_color = Some(color);
67 self
68 }
69
70 pub fn with_margin(mut self, m: Insets) -> Self {
71 self.base.margin = m;
72 self
73 }
74 pub fn with_h_anchor(mut self, h: HAnchor) -> Self {
75 self.base.h_anchor = h;
76 self
77 }
78 pub fn with_v_anchor(mut self, v: VAnchor) -> Self {
79 self.base.v_anchor = v;
80 self
81 }
82 pub fn with_min_size(mut self, s: Size) -> Self {
83 self.base.min_size = s;
84 self
85 }
86 pub fn with_max_size(mut self, s: Size) -> Self {
87 self.base.max_size = s;
88 self
89 }
90
91 pub fn set_value(&mut self, v: f64) {
92 self.props.value = v.clamp(0.0, 1.0);
93 }
94
95 pub fn value(&self) -> f64 {
96 self.props.value
97 }
98}
99
100impl Widget for ProgressBar {
101 fn type_name(&self) -> &'static str {
102 "ProgressBar"
103 }
104 fn bounds(&self) -> Rect {
105 self.bounds
106 }
107 fn set_bounds(&mut self, b: Rect) {
108 self.bounds = b;
109 }
110 fn children(&self) -> &[Box<dyn Widget>] {
111 &self.children
112 }
113 fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
114 &mut self.children
115 }
116
117 #[cfg(feature = "reflect")]
118 fn as_reflect(&self) -> Option<&dyn bevy_reflect::Reflect> {
119 Some(&self.props)
120 }
121 #[cfg(feature = "reflect")]
122 fn as_reflect_mut(&mut self) -> Option<&mut dyn bevy_reflect::Reflect> {
123 Some(&mut self.props)
124 }
125
126 fn margin(&self) -> Insets {
127 self.base.margin
128 }
129 fn widget_base(&self) -> Option<&WidgetBase> {
130 Some(&self.base)
131 }
132 fn widget_base_mut(&mut self) -> Option<&mut WidgetBase> {
133 Some(&mut self.base)
134 }
135 fn h_anchor(&self) -> HAnchor {
136 self.base.h_anchor
137 }
138 fn v_anchor(&self) -> VAnchor {
139 self.base.v_anchor
140 }
141 fn min_size(&self) -> Size {
142 self.base.min_size
143 }
144 fn max_size(&self) -> Size {
145 self.base.max_size
146 }
147
148 fn layout(&mut self, available: Size) -> Size {
149 Size::new(available.width, WIDGET_H)
150 }
151
152 fn paint(&mut self, ctx: &mut dyn DrawCtx) {
153 let v = ctx.visuals();
154 let w = self.bounds.width;
155 let h = self.bounds.height;
156 let bar_y = (h - BAR_H) * 0.5;
157 let r = BAR_H * 0.5;
158
159 ctx.set_fill_color(v.track_bg);
161 ctx.begin_path();
162 ctx.rounded_rect(0.0, bar_y, w, BAR_H, r);
163 ctx.fill();
164
165 let fill_color = self.props.fill_color.unwrap_or(v.accent);
167 let fill_w = (w * self.props.value).max(0.0);
168 if fill_w >= 1.0 {
169 ctx.set_fill_color(fill_color);
170 ctx.begin_path();
171 ctx.rounded_rect(0.0, bar_y, fill_w, BAR_H, r);
172 ctx.fill();
173 }
174
175 if self.props.show_text {
177 let label = format!("{:.0}%", self.props.value * 100.0);
178 ctx.set_font(Arc::clone(&self.font));
179 ctx.set_font_size(self.props.font_size);
180 let mid = w * 0.5;
182 let text_color = if fill_w > mid {
183 Color::rgba(1.0, 1.0, 1.0, 0.9)
184 } else {
185 v.text_dim
186 };
187 ctx.set_fill_color(text_color);
188 if let Some(m) = ctx.measure_text(&label) {
189 let tx = (w - m.width) * 0.5;
190 let ty = bar_y + BAR_H * 0.5 - (m.ascent - m.descent) * 0.5;
191 ctx.fill_text(&label, tx, ty);
192 }
193 }
194 }
195
196 fn on_event(&mut self, _: &Event) -> EventResult {
197 EventResult::Ignored
198 }
199}