1use crate::{BACK_CMD, CANCEL_CMD, FINISH_CMD, NEXT_CMD, PageArgs, Wizard};
2use zng_ext_font::FontWeight;
3use zng_ext_input::focus::TabIndex;
4use zng_wgt::{align, border, is_rtl, prelude::*};
5use zng_wgt_button::Button;
6use zng_wgt_container::{Container, padding};
7use zng_wgt_fill::{background, background_color};
8use zng_wgt_input::focus::tab_index;
9use zng_wgt_markdown::Markdown;
10use zng_wgt_scroll::{Scroll, ScrollMode};
11use zng_wgt_size_offset::width;
12use zng_wgt_stack::{Stack, StackDirection};
13use zng_wgt_style::style_fn;
14use zng_wgt_text::Text;
15
16context_var! {
17 pub static HEADER_FN_VAR: WidgetFn<HeaderFnArgs> = WidgetFn::new(default_header_fn);
19
20 pub static HEADER_BACKGROUND_FN_VAR: WidgetFn<()> = WidgetFn::nil();
22
23 pub static SIDE_FN_VAR: WidgetFn<SideFnArgs> = WidgetFn::new(default_side_fn);
25
26 pub static SIDE_BACKGROUND_FN_VAR: WidgetFn<()> = WidgetFn::nil();
28
29 pub static SIDE_EXTRA_FN_VAR: WidgetFn<PageArgs> = WidgetFn::nil();
32
33 pub static CONTENT_FN_VAR: WidgetFn<ContentFnArgs> = WidgetFn::new(default_content_fn);
35
36 pub static FOOTER_FN_VAR: WidgetFn<FooterFnArgs> = WidgetFn::new(default_footer_fn);
38
39 pub static FOOTER_EXTRA_FN_VAR: WidgetFn<PageArgs> = WidgetFn::nil();
41
42 pub static PANEL_FN_VAR: WidgetFn<PanelFnArgs> = WidgetFn::new(default_panel_fn);
44}
45
46#[non_exhaustive]
50pub struct HeaderFnArgs {
51 pub header: UiNode,
57
58 pub background: UiNode,
62
63 pub index: usize,
65 pub pages_len: usize,
67 pub titles: Vec<Var<Txt>>,
69 pub skips: Vec<Var<bool>>,
71}
72impl HeaderFnArgs {
73 pub fn is_first(&self) -> bool {
75 self.index == 0
76 }
77
78 pub fn is_last(&self) -> bool {
80 self.index == self.pages_len.saturating_sub(1)
81 }
82
83 pub fn wizard_id(&self) -> WidgetId {
85 WIDGET.id()
86 }
87}
88
89#[non_exhaustive]
93pub struct SideFnArgs {
94 pub side: UiNode,
106
107 pub side_extra: UiNode,
117
118 pub background: UiNode,
122
123 pub index: usize,
125 pub pages_len: usize,
127}
128impl SideFnArgs {
129 pub fn is_first(&self) -> bool {
131 self.index == 0
132 }
133
134 pub fn is_last(&self) -> bool {
136 self.index == self.pages_len.saturating_sub(1)
137 }
138
139 pub fn wizard_id(&self) -> WidgetId {
141 WIDGET.id()
142 }
143}
144
145#[non_exhaustive]
149pub struct ContentFnArgs {
150 pub content: UiNode,
156
157 pub content_fill: bool,
163
164 pub index: usize,
166 pub pages_len: usize,
168}
169impl ContentFnArgs {
170 pub fn is_first(&self) -> bool {
172 self.index == 0
173 }
174
175 pub fn is_last(&self) -> bool {
177 self.index == self.pages_len.saturating_sub(1)
178 }
179
180 pub fn wizard_id(&self) -> WidgetId {
182 WIDGET.id()
183 }
184}
185
186#[non_exhaustive]
190pub struct FooterFnArgs {
191 pub footer: UiNode,
196
197 pub footer_extra: UiNode,
207
208 pub index: usize,
210 pub pages_len: usize,
212}
213impl FooterFnArgs {
214 pub fn is_first(&self) -> bool {
216 self.index == 0
217 }
218
219 pub fn is_last(&self) -> bool {
221 self.index == self.pages_len.saturating_sub(1)
222 }
223
224 pub fn wizard_id(&self) -> WidgetId {
226 WIDGET.id()
227 }
228}
229
230pub struct PanelFnArgs {
238 pub header: UiNode,
244 pub side: UiNode,
250 pub content: UiNode,
256 pub footer: UiNode,
262}
263
264pub fn default_header_fn(args: HeaderFnArgs) -> UiNode {
268 Container! {
269 child = args.header;
270 padding = (10, 10, 10, 20);
271 border = {
272 widths: (0, 0, 1, 0),
273 sides: colors::GRAY.with_alpha(40.pct()),
274 };
275 background = args.background;
276 }
277}
278
279pub fn default_page_header(args: PageArgs) -> UiNode {
285 Stack! {
286 children = ui_vec![default_page_header_title(args.title), default_page_header_info(args.info),];
287 direction = StackDirection::top_to_bottom();
288 spacing = 5;
289 align = Align::START;
290 }
291}
292pub fn default_page_header_title(title: Var<Txt>) -> UiNode {
296 Text! {
297 txt = title;
298 font_weight = FontWeight::BOLD;
299 }
300}
301pub fn default_page_header_info(info: Var<Txt>) -> UiNode {
305 Markdown! {
306 txt = info;
307 }
308}
309
310pub fn default_side_fn(args: SideFnArgs) -> UiNode {
314 Stack! {
315 direction = StackDirection::top_to_bottom();
316 children = args.side;
317 children_align = Align::BOTTOM_START;
318 spacing = 5;
319 padding = 5;
320 width = 200;
321 background = args.background;
322 border = {
323 widths: (0, 1, 0, 0),
324 sides: colors::GRAY.with_alpha(40.pct()),
325 };
326 when #is_rtl {
327 border = {
328 widths: (0, 0, 0, 1),
329 sides: colors::GRAY.with_alpha(40.pct()),
330 };
331 }
332
333 zng_wgt_button::style_fn = style_fn!(|_| zng_wgt_button::LinkStyle!());
334 }
335}
336
337pub fn default_page_side(_: PageArgs) -> UiNode {
343 ui_vec![].into_node()
344}
345
346pub fn default_content_fn(args: ContentFnArgs) -> UiNode {
350 if args.content_fill {
351 Container! {
352 child = args.content;
353 background_color = light_dark(rgb(0.85, 0.85, 0.85), rgb(0.15, 0.15, 0.15));
354 }
355 } else {
356 Scroll! {
357 mode = ScrollMode::VERTICAL;
358 child = args.content;
359 child_align = Align::FILL;
360 padding = 20;
361 background_color = light_dark(rgb(0.85, 0.85, 0.85), rgb(0.15, 0.15, 0.15));
362 }
363 }
364}
365
366pub fn default_footer_fn(args: FooterFnArgs) -> UiNode {
370 Stack! {
371 children = args.footer.into_list();
372 direction = StackDirection::start_to_end();
373 spacing = 5;
374 padding = 5;
375 children_align = Align::END;
376 border = {
377 widths: (1, 0, 0, 0),
378 sides: colors::GRAY.with_alpha(40.pct()),
379 };
380 }
381}
382
383pub fn default_page_footer(args: PageArgs) -> UiNode {
389 let id = args.wizard_id();
390 if args.is_first() {
391 ui_vec![default_page_footer_next(id), default_page_footer_cancel(id)]
392 } else if args.is_last() {
393 ui_vec![default_page_footer_back(id), default_page_footer_finish(id)]
394 } else {
395 ui_vec![
396 default_page_footer_back(id),
397 default_page_footer_next(id),
398 default_page_footer_cancel(id)
399 ]
400 }
401 .into_node()
402}
403pub fn default_page_footer_back(wizard_id: WidgetId) -> UiNode {
405 Button! {
406 cmd = BACK_CMD.scoped(wizard_id);
407 tab_index = TabIndex::FIRST - 1;
408 }
409}
410pub fn default_page_footer_next(wizard_id: WidgetId) -> UiNode {
412 Button! {
413 cmd = NEXT_CMD.scoped(wizard_id);
414 tab_index = TabIndex::FIRST;
415 }
416}
417pub fn default_page_footer_finish(wizard_id: WidgetId) -> UiNode {
419 Button! {
420 cmd = FINISH_CMD.scoped(wizard_id);
421 tab_index = TabIndex::FIRST;
422 style_fn = style_fn!(|_| zng_wgt_button::PrimaryStyle!());
423 }
424}
425pub fn default_page_footer_cancel(wizard_id: WidgetId) -> UiNode {
427 Button! {
428 cmd = CANCEL_CMD.scoped(wizard_id);
429 tab_index = TabIndex::FIRST - 2;
430 }
431}
432
433pub fn default_panel_fn(args: PanelFnArgs) -> UiNode {
437 Container! {
438 child_bottom = args.footer;
439 child_start = args.side;
440 child_top = args.header;
441 child = args.content;
442 }
443}
444
445#[property(CONTEXT, default(HEADER_FN_VAR), widget_impl(Wizard))]
449pub fn header_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<HeaderFnArgs>>) -> UiNode {
450 with_context_var(child, HEADER_FN_VAR, wgt_fn)
451}
452
453#[property(CONTEXT, default(HEADER_BACKGROUND_FN_VAR), widget_impl(Wizard))]
457pub fn header_background_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<()>>) -> UiNode {
458 with_context_var(child, HEADER_BACKGROUND_FN_VAR, wgt_fn)
459}
460
461#[property(CONTEXT, default(SIDE_FN_VAR), widget_impl(Wizard))]
465pub fn side_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<SideFnArgs>>) -> UiNode {
466 with_context_var(child, SIDE_FN_VAR, wgt_fn)
467}
468
469#[property(CONTEXT, default(SIDE_BACKGROUND_FN_VAR), widget_impl(Wizard))]
473pub fn side_background_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<()>>) -> UiNode {
474 with_context_var(child, SIDE_BACKGROUND_FN_VAR, wgt_fn)
475}
476
477#[property(CONTEXT, default(SIDE_EXTRA_FN_VAR), widget_impl(Wizard))]
485pub fn side_extra_fn(child: impl IntoUiNode, side_extra_fn: impl IntoVar<WidgetFn<PageArgs>>) -> UiNode {
486 with_context_var(child, SIDE_EXTRA_FN_VAR, side_extra_fn)
487}
488
489#[property(CONTEXT, default(CONTENT_FN_VAR), widget_impl(Wizard))]
493pub fn content_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<ContentFnArgs>>) -> UiNode {
494 with_context_var(child, CONTENT_FN_VAR, wgt_fn)
495}
496
497#[property(CONTEXT, default(FOOTER_FN_VAR), widget_impl(Wizard))]
501pub fn footer_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<FooterFnArgs>>) -> UiNode {
502 with_context_var(child, FOOTER_FN_VAR, wgt_fn)
503}
504
505#[property(CONTEXT, default(FOOTER_EXTRA_FN_VAR), widget_impl(Wizard))]
509pub fn footer_extra_fn(child: impl IntoUiNode, footer_extra_fn: impl IntoVar<WidgetFn<PageArgs>>) -> UiNode {
510 with_context_var(child, FOOTER_EXTRA_FN_VAR, footer_extra_fn)
511}
512
513#[property(CONTEXT, default(PANEL_FN_VAR), widget_impl(Wizard))]
517pub fn panel_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<PanelFnArgs>>) -> UiNode {
518 with_context_var(child, PANEL_FN_VAR, wgt_fn)
519}