rg3d_ui/
progress_bar.rs

1use crate::{
2    border::BorderBuilder,
3    brush::Brush,
4    canvas::CanvasBuilder,
5    core::{algebra::Vector2, color::Color, pool::Handle},
6    define_constructor,
7    message::{MessageDirection, UiMessage},
8    widget::{Widget, WidgetBuilder, WidgetMessage},
9    BuildContext, Control, NodeHandleMapping, UiNode, UserInterface,
10};
11use std::{
12    any::{Any, TypeId},
13    ops::{Deref, DerefMut},
14};
15
16#[derive(Debug, Clone, PartialEq)]
17pub enum ProgressBarMessage {
18    Progress(f32),
19}
20
21impl ProgressBarMessage {
22    define_constructor!(ProgressBarMessage:Progress => fn progress(f32), layout: false);
23}
24
25#[derive(Clone)]
26pub struct ProgressBar {
27    widget: Widget,
28    progress: f32,
29    indicator: Handle<UiNode>,
30    body: Handle<UiNode>,
31}
32
33crate::define_widget_deref!(ProgressBar);
34
35impl Control for ProgressBar {
36    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
37        if type_id == TypeId::of::<Self>() {
38            Some(self)
39        } else {
40            None
41        }
42    }
43
44    fn resolve(&mut self, node_map: &NodeHandleMapping) {
45        node_map.resolve(&mut self.indicator);
46        node_map.resolve(&mut self.body);
47    }
48
49    fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
50        let size = self.widget.arrange_override(ui, final_size);
51
52        ui.send_message(WidgetMessage::width(
53            self.indicator,
54            MessageDirection::ToWidget,
55            size.x * self.progress,
56        ));
57
58        ui.send_message(WidgetMessage::height(
59            self.indicator,
60            MessageDirection::ToWidget,
61            size.y,
62        ));
63
64        size
65    }
66
67    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
68        self.widget.handle_routed_message(ui, message);
69
70        if message.destination() == self.handle {
71            if let Some(&ProgressBarMessage::Progress(progress)) =
72                message.data::<ProgressBarMessage>()
73            {
74                if progress != self.progress {
75                    self.set_progress(progress);
76                    self.invalidate_layout();
77                }
78            }
79        }
80    }
81}
82
83impl ProgressBar {
84    pub fn set_progress(&mut self, progress: f32) {
85        self.progress = progress.min(1.0).max(0.0);
86    }
87
88    pub fn progress(&self) -> f32 {
89        self.progress
90    }
91}
92
93pub struct ProgressBarBuilder {
94    widget_builder: WidgetBuilder,
95    body: Option<Handle<UiNode>>,
96    indicator: Option<Handle<UiNode>>,
97    progress: f32,
98}
99
100impl ProgressBarBuilder {
101    pub fn new(widget_builder: WidgetBuilder) -> Self {
102        Self {
103            widget_builder,
104            body: None,
105            indicator: None,
106            progress: 0.0,
107        }
108    }
109
110    pub fn with_body(mut self, body: Handle<UiNode>) -> Self {
111        self.body = Some(body);
112        self
113    }
114
115    pub fn with_indicator(mut self, indicator: Handle<UiNode>) -> Self {
116        self.indicator = Some(indicator);
117        self
118    }
119
120    pub fn with_progress(mut self, progress: f32) -> Self {
121        self.progress = progress.min(1.0).max(0.0);
122        self
123    }
124
125    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
126        let body = self
127            .body
128            .unwrap_or_else(|| BorderBuilder::new(WidgetBuilder::new()).build(ctx));
129
130        let indicator = self.indicator.unwrap_or_else(|| {
131            BorderBuilder::new(
132                WidgetBuilder::new().with_background(Brush::Solid(Color::opaque(180, 180, 180))),
133            )
134            .build(ctx)
135        });
136
137        let canvas = CanvasBuilder::new(WidgetBuilder::new().with_child(indicator)).build(ctx);
138
139        ctx.link(canvas, body);
140
141        let progress_bar = ProgressBar {
142            widget: self.widget_builder.with_child(body).build(),
143            progress: self.progress,
144            indicator,
145            body,
146        };
147
148        ctx.add_node(UiNode::new(progress_bar))
149    }
150}