Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/modals.rs
1use std::sync::atomic::{AtomicUsize, Ordering};
2 
3use crate::{AppContext, View, ViewContext};
4 
5/// The data for displaying a platform-native alert dialog (modal).
6pub struct AlertDialog {
7 /// The primary text.
8 pub message_text: String,
9 /// Smaller, more detailed text.
10 pub info_text: String,
11 /// Each item is a button, and the String is the text for the button.
12 pub buttons: Vec<String>,
13}
14 
15impl AlertDialog {
16 pub fn new(
17 message_text: impl Into<String>,
18 info_text: impl Into<String>,
19 buttons: &[&str],
20 ) -> Self {
21 Self {
22 message_text: message_text.into(),
23 info_text: info_text.into(),
24 buttons: buttons.iter().map(|s| String::from(*s)).collect(),
25 }
26 }
27}
28 
29/// This is for requesting an [`AlertDialog`] on a [`AppContext`] or [`ViewContext`].
30/// Unlike [`AlertDialog`], this data structure co-locates the handlers for each button with the
31/// button text.
32pub struct AlertDialogWithCallbacks<F> {
33 /// The primary text.
34 pub message_text: String,
35 /// Smaller, more detailed text.
36 pub info_text: String,
37 /// Each item is a button, both its text and "on click" callback.
38 pub button_data: Vec<ModalButton<F>>,
39 /// Callback to run if the user clicks the "don't ask again" checkbox. This should prevent that
40 /// type of modal in the future.
41 pub on_disable: F,
42}
43 
44/// Wraps the button text with its click handler.
45pub struct ModalButton<F> {
46 /// The text on the button.
47 pub title: String,
48 /// The click handler for this button.
49 pub on_click: F,
50}
51 
52/// The signature of the callback when requesting a modal from a [`AppContext`].
53pub type AppModalCallback = Box<dyn FnOnce(&mut AppContext)>;
54/// The signature of the callback when requesting a modal from a [`ViewContext`].
55pub type ViewModalCallback<T> = Box<dyn FnOnce(&mut T, &mut ViewContext<T>)>;
56 
57impl ModalButton<AppModalCallback> {
58 /// This constructor is for modals when you have a [`AppContext`]. If you're requesting
59 /// a modal from a View and have access to a [`ViewContext`], use the [`ModalButton::for_view`]
60 /// method instead!
61 pub fn for_app<S, F>(title: S, on_click: F) -> Self
62 where
63 S: Into<String>,
64 F: FnOnce(&mut AppContext) + 'static,
65 {
66 Self {
67 title: title.into(),
68 on_click: Box::new(on_click),
69 }
70 }
71}
72 
73impl AlertDialogWithCallbacks<AppModalCallback> {
74 /// This constructor is for modals when you have a [`AppContext`]. If you're requesting
75 /// a modal from a View and have access to a [`ViewContext`], use the
76 /// [`AlertDialogWithCallbacks::for_view`] method instead!
77 pub fn for_app<F>(
78 message_text: impl Into<String>,
79 info_text: impl Into<String>,
80 button_data: Vec<ModalButton<AppModalCallback>>,
81 on_disable: F,
82 ) -> Self
83 where
84 F: FnOnce(&mut AppContext) + 'static,
85 {
86 Self {
87 message_text: message_text.into(),
88 info_text: info_text.into(),
89 button_data,
90 on_disable: Box::new(on_disable),
91 }
92 }
93}
94 
95impl<V: View> ModalButton<ViewModalCallback<V>> {
96 /// This constructor is for modals when you have a [`ViewContext`]. If you're requesting a modal
97 /// from a [`AppContext`], use the [`ModalButton::for_app`] method instead!
98 pub fn for_view<S, F>(title: S, on_click: F) -> Self
99 where
100 S: Into<String>,
101 F: FnOnce(&mut V, &mut ViewContext<V>) + 'static,
102 {
103 Self {
104 title: title.into(),
105 on_click: Box::new(on_click),
106 }
107 }
108}
109 
110impl<V: View> AlertDialogWithCallbacks<ViewModalCallback<V>> {
111 /// This constructor is for modals when you have a [`ViewContext`]. If you're requesting a modal
112 /// from a [`AppContext`], use the [`AlertDialogWithCallbacks::for_app`] method instead!
113 pub fn for_view<F>(
114 message_text: impl Into<String>,
115 info_text: impl Into<String>,
116 button_data: Vec<ModalButton<ViewModalCallback<V>>>,
117 on_disable: F,
118 ) -> Self
119 where
120 F: FnOnce(&mut V, &mut ViewContext<V>) + 'static,
121 {
122 Self {
123 message_text: message_text.into(),
124 info_text: info_text.into(),
125 button_data,
126 on_disable: Box::new(on_disable),
127 }
128 }
129}
130 
131/// This holds the data necessary for dispatching the response from a platform-native modal.
132pub(super) struct PlatformModalResponseData {
133 /// A list of callbacks for each button on the modal.
134 /// The first callback (at index 0) is the callback for the first button being pressed, etc.
135 pub button_callbacks: Vec<AppModalCallback>,
136 /// The callback for if the "Don't ask again" checkbox is clicked.
137 pub disable_callback: AppModalCallback,
138}
139 
140/// A globally unique, incrementing integer to ID platform native modals.
141#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
142#[repr(transparent)]
143pub struct ModalId(usize);
144 
145static NEXT_MODAL_ID: AtomicUsize = AtomicUsize::new(0);
146 
147impl ModalId {
148 /// Constructs a new globally unique modal ID.
149 #[allow(clippy::new_without_default)]
150 pub fn new() -> Self {
151 let raw = NEXT_MODAL_ID.fetch_add(1, Ordering::Relaxed);
152 Self(raw)
153 }
154}
155