zng_wgt_progress/
lib.rs
1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12zng_wgt::enable_widget_macros!();
13
14use zng_app::handler::FilterWidgetHandler;
15use zng_wgt::{base_color, prelude::*, visibility};
16use zng_wgt_container::{Container, child_out_bottom};
17use zng_wgt_fill::background_color;
18use zng_wgt_size_offset::{height, width, x};
19use zng_wgt_style::{Style, StyleMix, impl_style_fn, style_fn};
20
21pub use zng_task::Progress;
22
23#[widget($crate::ProgressView {
25 ($progress:expr) => {
26 progress = $progress;
27 };
28})]
29pub struct ProgressView(StyleMix<WidgetBase>);
30impl ProgressView {
31 fn widget_intrinsic(&mut self) {
32 self.style_intrinsic(STYLE_FN_VAR, property_id!(self::style_fn));
33 widget_set! {
34 self;
35 style_base_fn = style_fn!(|_| DefaultStyle!());
36 }
37 }
38}
39impl_style_fn!(ProgressView);
40
41context_var! {
42 pub static PROGRESS_VAR: Progress = Progress::indeterminate();
44}
45
46#[property(CONTEXT, default(PROGRESS_VAR), widget_impl(ProgressView))]
50pub fn progress(child: impl UiNode, progress: impl IntoVar<Progress>) -> impl UiNode {
51 with_context_var(child, PROGRESS_VAR, progress)
52}
53
54#[property(CONTEXT, default(false), widget_impl(ProgressView))]
56pub fn collapse_complete(child: impl UiNode, collapse: impl IntoVar<bool>) -> impl UiNode {
57 let collapse = collapse.into_var();
58 visibility(
59 child,
60 expr_var! {
61 if #{PROGRESS_VAR}.is_complete() && *#{collapse} {
62 Visibility::Collapsed
63 } else {
64 Visibility::Visible
65 }
66 },
67 )
68}
69
70#[property(EVENT, widget_impl(ProgressView))]
74pub fn on_progress(child: impl UiNode, mut handler: impl WidgetHandler<Progress>) -> impl UiNode {
75 enum State {
77 WaitInfo,
78 InfoInited,
79 Done,
80 }
81 let mut state = State::WaitInfo;
82
83 match_node(child, move |c, op| match op {
84 UiNodeOp::Init => {
85 WIDGET.sub_var(&PROGRESS_VAR);
86 state = State::WaitInfo;
87 }
88 UiNodeOp::Info { .. } => {
89 if let State::WaitInfo = &state {
90 state = State::InfoInited;
91 WIDGET.update();
92 }
93 }
94 UiNodeOp::Update { updates } => {
95 c.update(updates);
96
97 match state {
98 State::Done => {
99 if PROGRESS_VAR.is_new() {
100 PROGRESS_VAR.with(|u| handler.event(u));
101 } else {
102 handler.update();
103 }
104 }
105 State::InfoInited => {
106 PROGRESS_VAR.with(|u| handler.event(u));
107 state = State::Done;
108 }
109 State::WaitInfo => {}
110 }
111 }
112 _ => {}
113 })
114}
115
116#[property(EVENT, widget_impl(ProgressView))]
120pub fn on_complete(child: impl UiNode, handler: impl WidgetHandler<Progress>) -> impl UiNode {
121 let mut is_complete = false;
122 on_progress(
123 child,
124 FilterWidgetHandler::new(handler, move |u| {
125 let complete = u.is_complete();
126 if complete != is_complete {
127 is_complete = complete;
128 return is_complete;
129 }
130 false
131 }),
132 )
133}
134
135#[property(EVENT, widget_impl(ProgressView))]
139pub fn is_indeterminate(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
140 bind_state(child, PROGRESS_VAR.map(|p| p.is_indeterminate()), state)
141}
142
143#[widget($crate::DefaultStyle)]
145pub struct DefaultStyle(Style);
146impl DefaultStyle {
147 fn widget_intrinsic(&mut self) {
148 let indeterminate_x = var(Length::from(0));
149 let mut indeterminate_animation = None;
150 let indeterminate_width = 10.pct();
151 widget_set! {
152 self;
153 base_color = light_dark(rgb(0.82, 0.82, 0.82), rgb(0.18, 0.18, 0.18));
154
155 zng_wgt_container::child = Container! {
156 height = 5;
157 background_color = colors::BASE_COLOR_VAR.rgba();
158
159 clip_to_bounds = true;
160 child_align = Align::FILL_START;
161 child = zng_wgt::Wgt! {
162 background_color = colors::ACCENT_COLOR_VAR.rgba();
163
164 #[easing(200.ms())]
165 width = PROGRESS_VAR.map(|p| Length::from(p.fct()));
166
167 on_progress = hn!(indeterminate_x, |p: &Progress| {
168 if p.is_indeterminate() {
169 if indeterminate_animation.is_none() {
171 let h = indeterminate_x.sequence(move |i| {
172 use zng_var::animation::easing;
173 i.set_ease(
174 -indeterminate_width,
175 100.pct(),
176 1.5.secs(),
177 |t| easing::ease_out(easing::quad, t),
178 )
179 });
180 indeterminate_animation = Some(h);
181 }
182 } else {
183 indeterminate_animation = None;
184 }
185 });
186 when *#{PROGRESS_VAR.map(|p| p.is_indeterminate())} {
187 width = indeterminate_width;
188 x = indeterminate_x;
189 }
190 };
191 };
192
193 child_out_bottom = zng_wgt_text::Text! {
194 txt = PROGRESS_VAR.map(|p| p.msg());
195 zng_wgt::visibility = PROGRESS_VAR.map(|p| Visibility::from(!p.msg().is_empty()));
196 zng_wgt::align = Align::CENTER;
197 }, 6;
198 }
199 }
200}
201
202#[widget($crate::SimpleBarStyle)]
204pub struct SimpleBarStyle(DefaultStyle);
205impl SimpleBarStyle {
206 fn widget_intrinsic(&mut self) {
207 widget_set! {
208 self;
209 child_out_bottom = unset!;
210 }
211 }
212}