hello_egui_utils/
lib.rs

1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
3#![warn(missing_docs)]
4
5/// Helper struct to easily align things with unknown sizes
6pub mod center;
7
8use egui::{Align, Label, Layout, Ui, UiBuilder, Vec2, WidgetText};
9
10pub use concat_idents::concat_idents;
11
12/// Returns the size of the text in the current ui (based on the max width of the ui)
13pub fn measure_text(ui: &mut Ui, text: impl Into<WidgetText>) -> Vec2 {
14    // There might be a more elegant way but this is enough for now
15    let res = Label::new(text).layout_in_ui(
16        &mut ui.new_child(
17            UiBuilder::new()
18                .max_rect(ui.available_rect_before_wrap())
19                .layout(Layout::left_to_right(Align::Center)),
20        ),
21    );
22
23    // There seem to be rounding errors in egui's text rendering
24    // so we add a little bit of padding
25    res.2.rect.size() + Vec2::new(0.1, 0.0)
26}
27
28/// Returns the approximate current scroll delta of the ui
29pub fn current_scroll_delta(ui: &Ui) -> Vec2 {
30    -ui.min_rect().min.to_vec2()
31}
32
33/// Spawns a tokio task
34#[cfg(all(feature = "tokio", not(target_arch = "wasm32")))]
35pub fn spawn(future: impl std::future::Future<Output = ()> + Send + 'static) {
36    tokio::task::spawn(future);
37}
38
39/// Spawns a wasm_bindgen_futures task
40#[cfg(all(feature = "async", target_arch = "wasm32"))]
41pub fn spawn(future: impl std::future::Future<Output = ()> + 'static) {
42    wasm_bindgen_futures::spawn_local(future);
43}
44
45#[cfg(all(feature = "async", not(feature = "tokio"), not(target_arch = "wasm32")))]
46compile_error!("You need to enable the `tokio` feature to use this crate on native. If you need a different async runtime, please open an issue (should be easy to add).");
47
48/// Matches the self reference of the callback fn
49#[macro_export]
50macro_rules! call_self_fn {
51    ($path:path, &mut $self:expr, ($($arg:expr,)*)) => {
52        $path($self, $($arg,)*)
53    };
54    ($path:path, mut $self:expr, ($($arg:expr,)*)) => {
55        $path($self, $($arg,)*)
56    };
57    ($path:path, &$self:expr, ($($arg:expr,)*)) => {
58        $path($self, $($arg,)*)
59    };
60    ($path:path, $self:expr, ($($arg:expr,)*)) => {
61        $path($self, $($arg,)*)
62    };
63    ($path:path, ($($arg:expr,)*)) => {
64        $path($($arg,)*)
65    };
66}
67
68/// This macro generates the async fn
69#[macro_export]
70macro_rules! async_fn_def {
71    (
72        $(#[$docs:meta])*
73        // Block that contains the async logic
74        $body:block,
75        // The name of the async fn
76        $name:ident,
77        // The path to the callback fn (e.g. Self::callback)
78        $callback_fn_path:path,
79        // The parameters of the async fn. The semicolon in front of the mutt is a hack to circumvent ambiguity
80        ($($(;$mutt:ident)? $arg:ident: $_type:ty,)*)
81        // The parameters of the call to the callback fn
82        ($($call_args:ident,)*)
83        // The generics of the async fn
84        ($($gen:tt)*),
85        // Return type
86        ($($return_type:tt)*),
87        // Self reference
88        ($($callback_body_self:tt)*),
89    ) => {
90        // We use concat_idents to generate the name for the async fn
91        $crate::concat_idents!(fn_name = $name, _async {
92            $(#[$docs])*
93            #[doc = concat!("This is the async version of `", stringify!($name), "`")]
94            #[allow(unused_mut)]
95            pub fn fn_name$($gen)*(
96                $($callback_body_self)*
97                $($($mutt)? $arg: $_type,)*
98            ) -> $($return_type)* {
99                let callback = $body;
100
101                // Construct the call to the callback fn
102                $crate::call_self_fn!{
103                    $callback_fn_path,
104                    $($callback_body_self)*
105                    ($($call_args,)*
106                    callback,)
107                }
108            }
109        });
110    };
111}
112
113/// This macro generates the callback fn
114#[macro_export]
115macro_rules! fn_def {
116    (
117        $(#[$docs:meta])*
118        // Block that contains the callback function body
119        $body:block,
120        // The name of the callback fn
121        $name:ident,
122        // The parameters of the callback fn
123        $($arg:ident: $_type:ty,)*
124        // The generics of the callback fn
125        ($($gen:tt)*),
126        // The return type
127        ($($return_type:tt)*),
128        // The self declaration
129        ($($callback_body_self:tt)*),
130    ) => {
131        $(#[$docs])*
132        pub fn $name $($gen)*(
133            $($callback_body_self)*
134            $($arg: $_type,)*
135        ) -> $($return_type)* {
136            $body
137        }
138    };
139}
140
141/// This macro generates the async fn and the callback fn
142#[macro_export]
143macro_rules! fnify {
144    (
145        $(#[$docs:meta])*
146        $name:ident,
147        body: $body:block,
148        parameters: ($($arg:ident: $_type:ty,)*),
149        async_body: $async_body:block,
150        async_parameters: ($(;$async_mutt:ident)? $($async_arg:ident: $async_type:ty,)*),
151        call_args: ($($call_args:ident,)*),
152        generics: ($($gen:tt)*),
153        async_generics: ($($async_gen:tt)*),
154        return_type: ($($return_type:tt)*),
155        call_prefix: ($($call_prefix:tt)*),
156        callback_body_self: ($($callback_body_self:tt)*),
157    ) => {
158        $crate::fn_def!(
159            $(#[$docs])*
160            $body,
161            $name,
162            $($arg: $_type,)*
163            ($($gen)*),
164            ($($return_type)*),
165            ($($callback_body_self)*),
166        );
167
168        #[cfg(feature = "async")]
169        $crate::async_fn_def!(
170            $(#[$docs])*
171            $async_body,
172            $name,
173            $($call_prefix)*$name,
174            ($(;$async_mutt)? $($async_arg: $async_type,)*)
175            ($($call_args,)*)
176            ($($async_gen)*),
177            ($($return_type)*),
178            ($($callback_body_self)*),
179        );
180    };
181}
182
183/// This macro generates a callback based and a async version of the function
184#[macro_export]
185macro_rules! asyncify {
186    (
187        $(#[$docs:meta])*
188        $name:ident,
189        $callback_name:ident: (impl FnMut($callback_type:ty, $($closure_arg_name:ident: $closure_arg:ty,)*) $($bounds:tt)*),
190        call_prefix: ($($call_prefix:tt)*),
191        generics: ($($gen:tt)*),
192        async_generics: ($($async_gen:tt)*),
193        parameters: ($($arg:ident: $_type:ty,)*),
194        future: $future:ty,
195        return_type: ($($return_type:tt)*),
196        body: |($($callback_body_self:tt)*)| $body:block,
197    ) => {
198        $crate::fnify!{
199            $(#[$docs])*
200            $name,
201            body: $body,
202            parameters: ($($arg: $_type,)* $callback_name: impl FnMut($($closure_arg,)* $callback_type) $($bounds)*,),
203            async_body: {
204                Box::new(move |$($closure_arg_name: $closure_arg,)* callback: $callback_type| {
205                    let fut = future_fn($($closure_arg_name,)*);
206                    $crate::spawn(async move {
207                        let res = fut.await;
208                        callback(res);
209                    })
210                })
211            },
212            async_parameters: ($($arg: $_type,)* ;mut future_fn: $future,),
213            call_args: ($($arg,)*),
214            generics: ($($gen)*),
215            async_generics: ($($async_gen)*),
216            return_type: ($($return_type)*),
217            call_prefix: ($($call_prefix)*),
218            callback_body_self: ($($callback_body_self)*),
219        }
220    };
221    (
222        $(#[$docs:meta])*
223        $name:ident,
224        $callback_name:ident: (impl FnOnce($callback_type:ty) $($bounds:tt)*),
225        call_prefix: ($($call_prefix:tt)*),
226        generics: ($($gen:tt)*),
227        async_generics: ($($async_gen:tt)*),
228        parameters: ($($arg:ident: $_type:ty,)*),
229        future: $future:ty,
230        return_type: ($($return_type:tt)*),
231        body: |($($callback_body_self:tt)*)| $body:block,
232    ) => {
233        $crate::fnify!{
234            $(#[$docs])*
235            $name,
236            body: $body,
237            parameters: ($($arg: $_type,)* $callback_name: impl FnOnce($callback_type) $($bounds)*,),
238            async_body: {
239                Box::new(move |callback: $callback_type| {
240                    let fut = future;
241                    $crate::spawn(async move {
242                        let res = fut.await;
243                        callback(res);
244                    })
245                })
246            },
247            async_parameters: ($($arg: $_type,)* ;mut future: $future,),
248            call_args: ($($arg,)*),
249            generics: ($($gen)*),
250            async_generics: ($($async_gen)*),
251            return_type: ($($return_type)*),
252            call_prefix: ($($call_prefix)*),
253            callback_body_self: ($($callback_body_self)*),
254        }
255    };
256}
257
258/// Type of the callback function
259#[cfg(target_arch = "wasm32")]
260pub type CallbackType<T> = Box<dyn FnOnce(T)>;
261/// Type of the callback function
262#[cfg(not(target_arch = "wasm32"))]
263pub type CallbackType<T> = Box<dyn FnOnce(T) + Send + Sync>;
264
265#[cfg(not(target_arch = "wasm32"))]
266mod sync {
267    /// Marker trait for types that can be sent between threads (this will be not be Send on wasm)
268    pub use Send as MaybeSend;
269    /// Marker trait for types that can be shared between threads (this will be not be Sync on wasm)
270    pub use Sync as MaybeSync;
271}
272#[cfg(target_arch = "wasm32")]
273mod unsync {
274    /// Marker trait for types that can be sent between threads (this will not be Send on wasm)
275    pub trait MaybeSend {}
276
277    impl<T> MaybeSend for T where T: ?Sized {}
278
279    /// Marker trait for types that can be shared between threads (this will not be Sync on wasm)
280    pub trait MaybeSync {}
281
282    impl<T> MaybeSync for T where T: ?Sized {}
283}
284
285#[cfg(not(target_arch = "wasm32"))]
286pub use sync::*;
287
288#[cfg(target_arch = "wasm32")]
289pub use unsync::*;