1use raui_immediate::*;
2
3#[allow(ambiguous_glob_reexports)]
4pub mod prelude {
5 pub use crate::{
6 core::*,
7 core::{containers::*, interactive::*},
8 material::*,
9 material::{containers::*, interactive::*},
10 };
11}
12
13macro_rules! impl_list_components {
14 ($($name:ident),+ $(,)?) => {
15 $(
16 pub fn $name<R>(
17 props: impl Into<raui_core::props::Props>,
18 f: impl FnMut() -> R,
19 ) -> R {
20 use raui_core::prelude::*;
21 #[allow(unused_imports)]
22 use raui_material::prelude::*;
23 crate::list_component(make_widget!($name), props, f)
24 }
25 )+
26 };
27}
28
29macro_rules! impl_content_components {
30 ($content:literal : $($name:ident),+ $(,)?) => {
31 $(
32 pub fn $name<R>(
33 props: impl Into<raui_core::props::Props>,
34 f: impl FnMut() -> R,
35 ) -> R {
36 use raui_core::prelude::*;
37 #[allow(unused_imports)]
38 use raui_material::prelude::*;
39 crate::content_component(make_widget!($name), $content, props, f)
40 }
41 )+
42 };
43}
44
45macro_rules! impl_components {
46 ($($name:ident),+ $(,)?) => {
47 $(
48 pub fn $name(
49 props: impl Into<raui_core::props::Props>,
50 ) {
51 use raui_core::prelude::*;
52 #[allow(unused_imports)]
53 use raui_material::prelude::*;
54 crate::component(make_widget!($name), props)
55 }
56 )+
57 };
58}
59
60pub mod core {
61 pub use raui_core::widget::{
62 component::{image_box::ImageBoxProps, space_box::SpaceBoxProps, text_box::TextBoxProps},
63 unit::{
64 content::{ContentBoxItem, ContentBoxItemLayout},
65 flex::{FlexBoxItem, FlexBoxItemLayout},
66 grid::{GridBoxItem, GridBoxItemLayout},
67 image::{
68 ImageBoxAspectRatio, ImageBoxColor, ImageBoxFrame, ImageBoxImage,
69 ImageBoxImageScaling, ImageBoxMaterial, ImageBoxProcedural, ImageBoxSizeValue,
70 },
71 size::SizeBoxSizeValue,
72 text::{
73 TextBoxDirection, TextBoxFont, TextBoxHorizontalAlign, TextBoxSizeValue,
74 TextBoxVerticalAlign,
75 },
76 },
77 utils::*,
78 };
79
80 impl_components! {
81 image_box,
82 text_box,
83 space_box,
84 }
85
86 pub mod containers {
87 pub use raui_core::widget::component::containers::{
88 anchor_box::{AnchorNotifyProps, AnchorProps, PivotBoxProps},
89 content_box::ContentBoxProps,
90 context_box::ContextBoxProps,
91 flex_box::FlexBoxProps,
92 grid_box::GridBoxProps,
93 hidden_box::HiddenBoxProps,
94 horizontal_box::HorizontalBoxProps,
95 portal_box::PortalsContainer,
96 scroll_box::ScrollBoxOwner,
97 size_box::SizeBoxProps,
98 switch_box::SwitchBoxProps,
99 tabs_box::{TabPlateProps, TabsBoxProps, TabsBoxTabsLocation},
100 variant_box::VariantBoxProps,
101 vertical_box::VerticalBoxProps,
102 wrap_box::WrapBoxProps,
103 };
104
105 impl_content_components! {
106 "content":
107 anchor_box,
108 pivot_box,
109 context_box,
110 portals_context_box,
111 hidden_box,
112 portal_box,
113 size_box,
114 tooltip_box,
115 portals_tooltip_box,
116 wrap_box,
117 }
118
119 impl_list_components! {
120 content_box,
121 nav_content_box,
122 flex_box,
123 nav_flex_box,
124 grid_box,
125 nav_grid_box,
126 horizontal_box,
127 nav_horizontal_box,
128 nav_scroll_box,
129 nav_scroll_box_content,
130 nav_scroll_box_side_scrollbars,
131 switch_box,
132 nav_switch_box,
133 nav_tabs_box,
134 variant_box,
135 nav_vertical_box,
136 vertical_box,
137 }
138 }
139
140 pub mod interactive {
141 use raui_core::{
142 make_widget,
143 props::Props,
144 widget::component::interactive::{
145 options_view::OptionsViewProxy, slider_view::SliderViewProxy,
146 },
147 };
148 use raui_immediate::{begin, end, pop, push, use_state};
149 use std::str::FromStr;
150
151 pub use raui_core::widget::component::interactive::{
152 button::{ButtonNotifyProps, ButtonProps},
153 input_field::{
154 TextInput, TextInputControlNotifyProps, TextInputMode, TextInputNotifyProps,
155 TextInputProps, TextInputState,
156 },
157 navigation::{
158 NavContainerActive, NavDirection, NavItemActive, NavJump, NavJumpActive,
159 NavJumpLooped, NavJumpMapProps, NavJumpMode, NavScroll, NavTextChange,
160 NavTrackingActive, NavType,
161 },
162 scroll_view::{ScrollViewNotifyProps, ScrollViewRange, ScrollViewState},
163 };
164
165 #[derive(Debug, Default, Copy, Clone)]
166 pub struct ImmediateButton {
167 pub state: ButtonProps,
168 pub prev: ButtonProps,
169 }
170
171 impl ImmediateButton {
172 pub fn select_start(&self) -> bool {
173 !self.prev.selected && self.state.selected
174 }
175
176 pub fn select_stop(&self) -> bool {
177 self.prev.selected && !self.state.selected
178 }
179
180 pub fn select_changed(&self) -> bool {
181 self.prev.selected != self.state.selected
182 }
183
184 pub fn trigger_start(&self) -> bool {
185 !self.prev.trigger && self.state.trigger
186 }
187
188 pub fn trigger_stop(&self) -> bool {
189 self.prev.trigger && !self.state.trigger
190 }
191
192 pub fn trigger_changed(&self) -> bool {
193 self.prev.trigger != self.state.trigger
194 }
195
196 pub fn context_start(&self) -> bool {
197 !self.prev.context && self.state.context
198 }
199
200 pub fn context_stop(&self) -> bool {
201 self.prev.context && !self.state.context
202 }
203
204 pub fn context_changed(&self) -> bool {
205 self.prev.context != self.state.context
206 }
207 }
208
209 impl_content_components! {
210 "content":
211 navigation_barrier,
212 }
213
214 pub fn button(
215 props: impl Into<Props>,
216 mut f: impl FnMut(ImmediateButton),
217 ) -> ImmediateButton {
218 use crate::internal::*;
219 use raui_core::prelude::*;
220 let state = use_state(ImmediateButton::default);
221 let result = state.read().unwrap().to_owned();
222 begin();
223 f(result);
224 let node = end().pop().unwrap_or_default();
225 push(
226 make_widget!(immediate_button)
227 .with_props(ImmediateButtonProps { state: Some(state) })
228 .merge_props(props.into())
229 .named_slot("content", node),
230 );
231 result
232 }
233
234 pub fn text_input<T: ToString + FromStr + Send + Sync>(
235 value: &T,
236 props: impl Into<Props>,
237 mut f: impl FnMut(&str, TextInputState),
238 ) -> (Option<T>, TextInputState) {
239 use crate::internal::*;
240 use raui_core::prelude::*;
241 let content = use_state(|| value.to_string());
242 let props = props.into();
243 let TextInputProps { allow_new_line, .. } = props.read_cloned_or_default();
244 let text_state = use_state(TextInputState::default);
245 let text_result = text_state.read().unwrap().to_owned();
246 if !text_result.focused {
247 *content.write().unwrap() = value.to_string();
248 }
249 let result = content.read().unwrap().to_string();
250 begin();
251 f(&result, text_result);
252 let node = end().pop().unwrap_or_default();
253 push(
254 make_widget!(immediate_text_input)
255 .with_props(ImmediateTextInputProps {
256 state: Some(text_state),
257 })
258 .merge_props(props)
259 .with_props(TextInputProps {
260 allow_new_line,
261 text: Some(content.into()),
262 })
263 .named_slot("content", node),
264 );
265 (result.parse().ok(), text_result)
266 }
267
268 pub fn input_field<T: ToString + FromStr + Send + Sync>(
269 value: &T,
270 props: impl Into<Props>,
271 mut f: impl FnMut(&str, TextInputState, ImmediateButton),
272 ) -> (Option<T>, TextInputState, ImmediateButton) {
273 use crate::internal::*;
274 use raui_core::prelude::*;
275 let content = use_state(|| value.to_string());
276 let props = props.into();
277 let TextInputProps { allow_new_line, .. } = props.read_cloned_or_default();
278 let text_state = use_state(TextInputState::default);
279 let text_result = text_state.read().unwrap().to_owned();
280 let button_state = use_state(ImmediateButton::default);
281 let button_result = button_state.read().unwrap().to_owned();
282 if !text_result.focused {
283 *content.write().unwrap() = value.to_string();
284 }
285 let result = content.read().unwrap().to_string();
286 begin();
287 f(&result, text_result, button_result);
288 let node = end().pop().unwrap_or_default();
289 push(
290 make_widget!(immediate_input_field)
291 .with_props(ImmediateTextInputProps {
292 state: Some(text_state),
293 })
294 .with_props(ImmediateButtonProps {
295 state: Some(button_state),
296 })
297 .merge_props(props)
298 .with_props(TextInputProps {
299 allow_new_line,
300 text: Some(content.into()),
301 })
302 .named_slot("content", node),
303 );
304 (result.parse().ok(), text_result, button_result)
305 }
306
307 pub fn slider_view<T: SliderViewProxy + Clone + 'static>(
308 value: T,
309 props: impl Into<Props>,
310 mut f: impl FnMut(&T, ImmediateButton),
311 ) -> (T, ImmediateButton) {
312 use crate::internal::*;
313 use raui_core::prelude::*;
314 let content = use_state(|| value.to_owned());
315 let props = props.into();
316 let SliderViewProps {
317 from,
318 to,
319 direction,
320 ..
321 } = props.read_cloned_or_default();
322 let button_state = use_state(ImmediateButton::default);
323 let button_result = button_state.read().unwrap().to_owned();
324 let result = content.read().unwrap().to_owned();
325 begin();
326 f(&result, button_result);
327 let node = end().pop().unwrap_or_default();
328 push(
329 make_widget!(immediate_slider_view)
330 .with_props(ImmediateButtonProps {
331 state: Some(button_state),
332 })
333 .merge_props(props)
334 .with_props(SliderViewProps {
335 input: Some(content.into()),
336 from,
337 to,
338 direction,
339 })
340 .named_slot("content", node),
341 );
342 (result, button_result)
343 }
344
345 pub fn options_view<T: OptionsViewProxy + Clone + 'static>(
346 value: T,
347 props: impl Into<Props>,
348 mut f_items: impl FnMut(&T),
349 mut f_content: impl FnMut(),
350 ) -> T {
351 use raui_core::prelude::*;
352 let content = use_state(|| value.to_owned());
353 let props = props.into();
354 let result = content.read().unwrap().to_owned();
355 begin();
356 f_items(&result);
357 let nodes = end();
358 begin();
359 f_content();
360 let node = pop();
361 push(
362 make_widget!(raui_core::widget::component::interactive::options_view::options_view)
363 .merge_props(props)
364 .with_props(OptionsViewProps {
365 input: Some(content.into()),
366 })
367 .named_slot("content", node)
368 .listed_slots(nodes),
369 );
370 result
371 }
372 }
373}
374
375pub mod material {
376 pub use raui_material::theme;
377
378 pub use raui_material::component::{
379 icon_paper::{IconImage, IconPaperProps},
380 switch_paper::SwitchPaperProps,
381 text_paper::TextPaperProps,
382 };
383
384 impl_components! {
385 icon_paper,
386 switch_paper,
387 text_paper,
388 }
389
390 pub mod containers {
391 pub use raui_material::component::containers::{
392 context_paper::ContextPaperProps,
393 modal_paper::ModalPaperProps,
394 paper::{PaperContentLayoutProps, PaperProps},
395 scroll_paper::SideScrollbarsPaperProps,
396 tooltip_paper::TooltipPaperProps,
397 };
398
399 impl_list_components! {
400 context_paper,
401 flex_paper,
402 nav_flex_paper,
403 grid_paper,
404 nav_grid_paper,
405 horizontal_paper,
406 nav_horizontal_paper,
407 modal_paper,
408 paper,
409 scroll_paper,
410 scroll_paper_side_scrollbars,
411 text_tooltip_paper,
412 tooltip_paper,
413 vertical_paper,
414 nav_vertical_paper,
415 wrap_paper,
416 }
417 }
418
419 pub mod interactive {
420 use crate::core::interactive::ImmediateButton;
421 use raui_core::{
422 props::Props,
423 widget::component::interactive::{
424 input_field::TextInputState, slider_view::SliderViewProxy,
425 },
426 };
427 use raui_immediate::{begin, end, push, use_state};
428 use std::str::FromStr;
429
430 pub use raui_material::component::interactive::{
431 button_paper::ButtonPaperOverrideStyle, text_field_paper::TextFieldPaperProps,
432 };
433
434 pub fn button_paper(
435 props: impl Into<Props>,
436 mut f: impl FnMut(ImmediateButton),
437 ) -> ImmediateButton {
438 use crate::internal::*;
439 use raui_core::prelude::*;
440 let state = use_state(ImmediateButton::default);
441 let result = state.read().unwrap().to_owned();
442 begin();
443 f(result);
444 let node = end().pop().unwrap_or_default();
445 push(
446 make_widget!(immediate_button_paper)
447 .with_props(ImmediateButtonProps { state: Some(state) })
448 .merge_props(props.into())
449 .named_slot("content", node),
450 );
451 result
452 }
453
454 pub fn icon_button_paper(props: impl Into<Props>) -> ImmediateButton {
455 use crate::internal::*;
456 use raui_core::prelude::*;
457 let state = use_state(ImmediateButton::default);
458 let result = state.read().unwrap().to_owned();
459 push(
460 make_widget!(immediate_icon_button_paper)
461 .with_props(ImmediateButtonProps { state: Some(state) })
462 .merge_props(props.into()),
463 );
464 result
465 }
466
467 pub fn switch_button_paper(props: impl Into<Props>) -> ImmediateButton {
468 use crate::internal::*;
469 use raui_core::prelude::*;
470 let state = use_state(ImmediateButton::default);
471 let result = state.read().unwrap().to_owned();
472 push(
473 make_widget!(immediate_switch_button_paper)
474 .with_props(ImmediateButtonProps { state: Some(state) })
475 .merge_props(props.into()),
476 );
477 result
478 }
479
480 pub fn text_button_paper(props: impl Into<Props>) -> ImmediateButton {
481 use crate::internal::*;
482 use raui_core::prelude::*;
483 let state = use_state(ImmediateButton::default);
484 let result = state.read().unwrap().to_owned();
485 push(
486 make_widget!(immediate_text_button_paper)
487 .with_props(ImmediateButtonProps { state: Some(state) })
488 .merge_props(props.into()),
489 );
490 result
491 }
492
493 pub fn text_field_paper<T: ToString + FromStr + Send + Sync>(
494 value: &T,
495 props: impl Into<Props>,
496 ) -> (Option<T>, TextInputState, ImmediateButton) {
497 use crate::internal::*;
498 use raui_core::prelude::*;
499 let content = use_state(|| value.to_string());
500 let props = props.into();
501 let TextInputProps { allow_new_line, .. } =
502 props.read_cloned_or_default::<TextInputProps>();
503 let text_state = use_state(TextInputState::default);
504 let text_result = text_state.read().unwrap().to_owned();
505 let button_state = use_state(ImmediateButton::default);
506 let button_result = button_state.read().unwrap().to_owned();
507 if !text_result.focused {
508 *content.write().unwrap() = value.to_string();
509 }
510 let result = content.read().unwrap().to_string();
511 push(
512 make_widget!(immediate_text_field_paper)
513 .with_props(ImmediateTextInputProps {
514 state: Some(text_state),
515 })
516 .with_props(ImmediateButtonProps {
517 state: Some(button_state),
518 })
519 .merge_props(props)
520 .with_props(TextInputProps {
521 allow_new_line,
522 text: Some(content.into()),
523 }),
524 );
525 (result.parse().ok(), text_result, button_result)
526 }
527
528 pub fn slider_paper<T: SliderViewProxy + Clone + 'static>(
529 value: T,
530 props: impl Into<Props>,
531 mut f: impl FnMut(&T, ImmediateButton),
532 ) -> (T, ImmediateButton) {
533 use crate::internal::*;
534 use raui_core::prelude::*;
535 let content = use_state(|| value.to_owned());
536 let props = props.into();
537 let SliderViewProps {
538 from,
539 to,
540 direction,
541 ..
542 } = props.read_cloned_or_default();
543 let button_state = use_state(ImmediateButton::default);
544 let button_result = button_state.read().unwrap().to_owned();
545 let result = content.read().unwrap().to_owned();
546 begin();
547 f(&result, button_result);
548 let node = end().pop().unwrap_or_default();
549 push(
550 make_widget!(immediate_slider_paper)
551 .with_props(ImmediateButtonProps {
552 state: Some(button_state),
553 })
554 .merge_props(props)
555 .with_props(SliderViewProps {
556 input: Some(content.into()),
557 from,
558 to,
559 direction,
560 })
561 .named_slot("content", node),
562 );
563 (result, button_result)
564 }
565
566 pub fn numeric_slider_paper<T: SliderViewProxy + Clone + 'static>(
567 value: T,
568 props: impl Into<Props>,
569 ) -> (T, ImmediateButton) {
570 use crate::internal::*;
571 use raui_core::prelude::*;
572 let content = use_state(|| value.to_owned());
573 let props = props.into();
574 let SliderViewProps {
575 from,
576 to,
577 direction,
578 ..
579 } = props.read_cloned_or_default();
580 let button_state = use_state(ImmediateButton::default);
581 let button_result = button_state.read().unwrap().to_owned();
582 let result = content.read().unwrap().to_owned();
583 push(
584 make_widget!(immediate_numeric_slider_paper)
585 .with_props(ImmediateButtonProps {
586 state: Some(button_state),
587 })
588 .merge_props(props)
589 .with_props(SliderViewProps {
590 input: Some(content.into()),
591 from,
592 to,
593 direction,
594 }),
595 );
596 (result, button_result)
597 }
598 }
599}
600
601mod internal {
602 use super::core::interactive::ImmediateButton;
603 use raui_core::prelude::*;
604 use raui_material::prelude::*;
605 use serde::{Deserialize, Serialize};
606
607 #[derive(PropsData, Default, Clone, Serialize, Deserialize)]
608 #[props_data(raui_core::props::PropsData)]
609 pub struct ImmediateButtonProps {
610 #[serde(default, skip)]
611 pub state: Option<ManagedLazy<ImmediateButton>>,
612 }
613
614 impl std::fmt::Debug for ImmediateButtonProps {
615 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
616 f.debug_struct("ImmediateButtonProps")
617 .field(
618 "state",
619 &self
620 .state
621 .as_ref()
622 .and_then(|state| state.read())
623 .map(|state| *state),
624 )
625 .finish()
626 }
627 }
628
629 #[derive(PropsData, Default, Clone, Serialize, Deserialize)]
630 #[props_data(raui_core::props::PropsData)]
631 pub struct ImmediateTextInputProps {
632 #[serde(default, skip)]
633 pub state: Option<ManagedLazy<TextInputState>>,
634 }
635
636 impl std::fmt::Debug for ImmediateTextInputProps {
637 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
638 f.debug_struct("ImmediateTextInputProps")
639 .finish_non_exhaustive()
640 }
641 }
642
643 fn use_immediate_button(ctx: &mut WidgetContext) {
644 ctx.props.write(ButtonNotifyProps(ctx.id.to_owned().into()));
645
646 if let Ok(props) = ctx.props.read::<ImmediateButtonProps>() {
647 let state = props.state.as_ref().unwrap();
648 let mut state = state.write().unwrap();
649 state.prev = state.state;
650 }
651
652 ctx.life_cycle.change(|ctx| {
653 if let Ok(props) = ctx.props.read::<ImmediateButtonProps>() {
654 if let Some(state) = props.state.as_ref() {
655 if let Some(mut state) = state.write() {
656 for msg in ctx.messenger.messages {
657 if let Some(msg) = msg.as_any().downcast_ref::<ButtonNotifyMessage>() {
658 state.state = msg.state;
659 }
660 }
661 }
662 }
663 }
664 });
665 }
666
667 fn use_immediate_text_input(ctx: &mut WidgetContext) {
668 if let Ok(data) = ctx.state.read_cloned::<TextInputState>() {
669 if let Ok(props) = ctx.props.read::<ImmediateTextInputProps>() {
670 let state = props.state.as_ref().unwrap();
671 let mut state = state.write().unwrap();
672 *state = data;
673 }
674 }
675 }
676
677 #[pre_hooks(use_immediate_button)]
678 pub(crate) fn immediate_button(mut ctx: WidgetContext) -> WidgetNode {
679 button(ctx)
680 }
681
682 #[pre_hooks(use_immediate_text_input)]
683 pub(crate) fn immediate_text_input(mut ctx: WidgetContext) -> WidgetNode {
684 text_input(ctx)
685 }
686
687 #[pre_hooks(use_immediate_text_input, use_immediate_button)]
688 pub(crate) fn immediate_input_field(mut ctx: WidgetContext) -> WidgetNode {
689 input_field(ctx)
690 }
691
692 #[pre_hooks(use_immediate_button)]
693 pub(crate) fn immediate_slider_view(mut ctx: WidgetContext) -> WidgetNode {
694 slider_view(ctx)
695 }
696
697 pub(crate) fn immediate_button_paper(ctx: WidgetContext) -> WidgetNode {
698 button_paper_impl(make_widget!(immediate_button), ctx)
699 }
700
701 pub(crate) fn immediate_icon_button_paper(ctx: WidgetContext) -> WidgetNode {
702 icon_button_paper_impl(make_widget!(immediate_button_paper), ctx)
703 }
704
705 pub(crate) fn immediate_switch_button_paper(ctx: WidgetContext) -> WidgetNode {
706 switch_button_paper_impl(make_widget!(immediate_button_paper), ctx)
707 }
708
709 pub(crate) fn immediate_text_button_paper(ctx: WidgetContext) -> WidgetNode {
710 text_button_paper_impl(make_widget!(immediate_button_paper), ctx)
711 }
712
713 pub(crate) fn immediate_text_field_paper(ctx: WidgetContext) -> WidgetNode {
714 text_field_paper_impl(make_widget!(immediate_input_field), ctx)
715 }
716
717 pub(crate) fn immediate_slider_paper(ctx: WidgetContext) -> WidgetNode {
718 slider_paper_impl(make_widget!(immediate_slider_view), ctx)
719 }
720
721 pub(crate) fn immediate_numeric_slider_paper(ctx: WidgetContext) -> WidgetNode {
722 numeric_slider_paper_impl(make_widget!(immediate_slider_paper), ctx)
723 }
724}