1#![warn(missing_docs)]
25
26use crate::style::resource::StyleResourceExt;
27use crate::style::Style;
28use crate::{
29 border::BorderBuilder,
30 canvas::CanvasBuilder,
31 core::{
32 algebra::Vector2, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
33 visitor::prelude::*,
34 },
35 define_constructor,
36 message::{MessageDirection, UiMessage},
37 widget::{Widget, WidgetBuilder, WidgetMessage},
38 BuildContext, Control, UiNode, UserInterface,
39};
40use fyrox_core::uuid_provider;
41use fyrox_core::variable::InheritableVariable;
42use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
43use std::ops::{Deref, DerefMut};
44
45#[derive(Debug, Clone, PartialEq)]
47pub enum ProgressBarMessage {
48 Progress(f32),
50}
51
52impl ProgressBarMessage {
53 define_constructor!(
54 ProgressBarMessage:Progress => fn progress(f32), layout: false
56 );
57}
58
59#[derive(Default, Clone, Debug, Visit, Reflect, ComponentProvider)]
96pub struct ProgressBar {
97 pub widget: Widget,
99 pub progress: InheritableVariable<f32>,
101 pub indicator: InheritableVariable<Handle<UiNode>>,
103 pub body: InheritableVariable<Handle<UiNode>>,
105}
106
107impl ConstructorProvider<UiNode, UserInterface> for ProgressBar {
108 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
109 GraphNodeConstructor::new::<Self>()
110 .with_variant("Progress Bar", |ui| {
111 ProgressBarBuilder::new(WidgetBuilder::new().with_name("Progress Bar"))
112 .build(&mut ui.build_ctx())
113 .into()
114 })
115 .with_group("Visual")
116 }
117}
118
119crate::define_widget_deref!(ProgressBar);
120
121uuid_provider!(ProgressBar = "d6ebb853-d945-46bc-86db-4c8b5d5faf8e");
122
123impl Control for ProgressBar {
124 fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
125 let size = self.widget.arrange_override(ui, final_size);
126
127 ui.send_message(WidgetMessage::width(
128 *self.indicator,
129 MessageDirection::ToWidget,
130 size.x * *self.progress,
131 ));
132
133 ui.send_message(WidgetMessage::height(
134 *self.indicator,
135 MessageDirection::ToWidget,
136 size.y,
137 ));
138
139 size
140 }
141
142 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
143 self.widget.handle_routed_message(ui, message);
144
145 if message.destination() == self.handle {
146 if let Some(&ProgressBarMessage::Progress(progress)) =
147 message.data::<ProgressBarMessage>()
148 {
149 if progress != *self.progress {
150 self.set_progress(progress);
151 self.invalidate_layout();
152 }
153 }
154 }
155 }
156}
157
158impl ProgressBar {
159 fn set_progress(&mut self, progress: f32) {
160 self.progress
161 .set_value_and_mark_modified(progress.clamp(0.0, 1.0));
162 }
163}
164
165pub struct ProgressBarBuilder {
167 widget_builder: WidgetBuilder,
168 body: Option<Handle<UiNode>>,
169 indicator: Option<Handle<UiNode>>,
170 progress: f32,
171}
172
173impl ProgressBarBuilder {
174 pub fn new(widget_builder: WidgetBuilder) -> Self {
176 Self {
177 widget_builder,
178 body: None,
179 indicator: None,
180 progress: 0.0,
181 }
182 }
183
184 pub fn with_body(mut self, body: Handle<UiNode>) -> Self {
186 self.body = Some(body);
187 self
188 }
189
190 pub fn with_indicator(mut self, indicator: Handle<UiNode>) -> Self {
192 self.indicator = Some(indicator);
193 self
194 }
195
196 pub fn with_progress(mut self, progress: f32) -> Self {
198 self.progress = progress.clamp(0.0, 1.0);
199 self
200 }
201
202 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
204 let body = self
205 .body
206 .unwrap_or_else(|| BorderBuilder::new(WidgetBuilder::new()).build(ctx));
207
208 let indicator = self.indicator.unwrap_or_else(|| {
209 BorderBuilder::new(
210 WidgetBuilder::new().with_background(ctx.style.property(Style::BRUSH_BRIGHTEST)),
211 )
212 .build(ctx)
213 });
214
215 let canvas = CanvasBuilder::new(WidgetBuilder::new().with_child(indicator)).build(ctx);
216
217 ctx.link(canvas, body);
218
219 let progress_bar = ProgressBar {
220 widget: self.widget_builder.with_child(body).build(ctx),
221 progress: self.progress.into(),
222 indicator: indicator.into(),
223 body: body.into(),
224 };
225
226 ctx.add_node(UiNode::new(progress_bar))
227 }
228}
229
230#[cfg(test)]
231mod test {
232 use crate::progress_bar::ProgressBarBuilder;
233 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
234
235 #[test]
236 fn test_deletion() {
237 test_widget_deletion(|ctx| ProgressBarBuilder::new(WidgetBuilder::new()).build(ctx));
238 }
239}