azul_core/
macros.rs

1 #![allow(unused_macros)]
2
3/// Implements functions for `CallbackInfo` and `Info`,
4/// to prevent duplicating the functions
5#[macro_export]
6macro_rules! impl_task_api {() => (
7    /// Insert a timer into the list of active timers.
8    /// Replaces the existing timer if called with the same TimerId.
9    pub fn add_timer(&mut self, id: TimerId, timer: Timer) {
10        self.timers.insert(id, timer);
11    }
12
13    /// Returns if a timer with the given ID is currently running
14    pub fn has_timer(&self, timer_id: &TimerId) -> bool {
15        self.get_timer(timer_id).is_some()
16    }
17
18    /// Returns a reference to an existing timer (if the `TimerId` is valid)
19    pub fn get_timer(&self, timer_id: &TimerId) -> Option<&Timer> {
20        self.timers.get(&timer_id)
21    }
22
23    /// Deletes a timer and returns it (if the `TimerId` is valid)
24    pub fn delete_timer(&mut self, timer_id: &TimerId) -> Option<Timer> {
25        self.timers.remove(timer_id)
26    }
27
28    /// Adds a (thread-safe) `Task` to the app that runs on a different thread
29    pub fn add_task(&mut self, task: Task) {
30        self.tasks.push(task);
31    }
32)}
33
34/// Implement the `From` trait for any type.
35/// Example usage:
36/// ```
37/// enum MyError<'a> {
38///     Bar(BarError<'a>)
39///     Foo(FooError<'a>)
40/// }
41///
42/// impl_from!(BarError<'a>, Error::Bar);
43/// impl_from!(BarError<'a>, Error::Bar);
44///
45/// ```
46#[macro_export]
47macro_rules! impl_from {
48    // From a type with a lifetime to a type which also has a lifetime
49    ($a:ident<$c:lifetime>, $b:ident::$enum_type:ident) => {
50        impl<$c> From<$a<$c>> for $b<$c> {
51            fn from(e: $a<$c>) -> Self {
52                $b::$enum_type(e)
53            }
54        }
55    };
56
57    // From a type without a lifetime to a type which also does not have a lifetime
58    ($a:ident, $b:ident::$enum_type:ident) => {
59        impl From<$a> for $b {
60            fn from(e: $a) -> Self {
61                $b::$enum_type(e)
62            }
63        }
64    };
65}
66
67/// Implement `Display` for an enum.
68///
69/// Example usage:
70/// ```
71/// enum Foo<'a> {
72///     Bar(&'a str)
73///     Baz(i32)
74/// }
75///
76/// impl_display!{ Foo<'a>, {
77///     Bar(s) => s,
78///     Baz(i) => format!("{}", i)
79/// }}
80/// ```
81#[macro_export]
82macro_rules! impl_display {
83    // For a type with a lifetime
84    ($enum:ident<$lt:lifetime>, {$($variant:pat => $fmt_string:expr),+$(,)* }) => {
85
86        impl<$lt> ::std::fmt::Display for $enum<$lt> {
87            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
88                use self::$enum::*;
89                match &self {
90                    $(
91                        $variant => write!(f, "{}", $fmt_string),
92                    )+
93                }
94            }
95        }
96
97    };
98
99    // For a type without a lifetime
100    ($enum:ident, {$($variant:pat => $fmt_string:expr),+$(,)* }) => {
101
102        impl ::std::fmt::Display for $enum {
103            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
104                use self::$enum::*;
105                match &self {
106                    $(
107                        $variant => write!(f, "{}", $fmt_string),
108                    )+
109                }
110            }
111        }
112
113    };
114}
115
116#[macro_export]
117macro_rules! impl_image_api {($struct_field:ident) => (
118
119    /// See [`AppResources::get_loaded_font_ids`]
120    ///
121    /// [`AppResources::get_loaded_font_ids`]: ../app_resources/struct.AppResources.html#method.get_loaded_font_ids
122    pub fn get_loaded_font_ids(&self) -> Vec<FontId> {
123        self.$struct_field.get_loaded_font_ids()
124    }
125
126    /// See [`AppResources::get_loaded_image_ids`]
127    ///
128    /// [`AppResources::get_loaded_image_ids`]: ../app_resources/struct.AppResources.html#method.get_loaded_image_ids
129    pub fn get_loaded_image_ids(&self) -> Vec<ImageId> {
130        self.$struct_field.get_loaded_image_ids()
131    }
132
133    /// See [`AppResources::get_loaded_css_image_ids`]
134    ///
135    /// [`AppResources::get_loaded_css_image_ids`]: ../app_resources/struct.AppResources.html#method.get_loaded_css_image_ids
136    pub fn get_loaded_css_image_ids(&self) -> Vec<CssImageId> {
137        self.$struct_field.get_loaded_css_image_ids()
138    }
139
140    /// See [`AppResources::get_loaded_css_font_ids`]
141    ///
142    /// [`AppResources::get_loaded_css_font_ids`]: ../app_resources/struct.AppResources.html#method.get_loaded_css_font_ids
143    pub fn get_loaded_css_font_ids(&self) -> Vec<CssImageId> {
144        self.$struct_field.get_loaded_css_font_ids()
145    }
146
147    /// See [`AppResources::get_loaded_text_ids`]
148    ///
149    /// [`AppResources::get_loaded_text_ids`]: ../app_resources/struct.AppResources.html#method.get_loaded_text_ids
150    pub fn get_loaded_text_ids(&self) -> Vec<TextId> {
151        self.$struct_field.get_loaded_text_ids()
152    }
153
154    // -- ImageId cache
155
156    /// See [`AppResources::add_image`]
157    ///
158    /// [`AppResources::add_image`]: ../app_resources/struct.AppResources.html#method.add_image
159    pub fn add_image_source(&mut self, image_id: ImageId, image_source: ImageSource) {
160        self.$struct_field.add_image_source(image_id, image_source)
161    }
162
163    /// See [`AppResources::has_image`]
164    ///
165    /// [`AppResources::has_image`]: ../app_resources/struct.AppResources.html#method.has_image
166    pub fn has_image_source(&self, image_id: &ImageId) -> bool {
167        self.$struct_field.has_image_source(image_id)
168    }
169
170    /// Given an `ImageId`, returns the bytes for that image or `None`, if the `ImageId` is invalid.
171    ///
172    /// See [`AppResources::get_image_bytes`]
173    ///
174    /// [`AppResources::get_image_bytes`]: ../app_resources/struct.AppResources.html#method.get_image_bytes
175    pub fn get_image_info(&self, pipeline_id: &PipelineId, image_id: &ImageId) -> Option<&ImageInfo> {
176        self.$struct_field.get_image_info(pipeline_id, image_id)
177    }
178
179    /// See [`AppResources::delete_image`]
180    ///
181    /// [`AppResources::delete_image`]: ../app_resources/struct.AppResources.html#method.delete_image
182    pub fn delete_image_source(&mut self, image_id: &ImageId) {
183        self.$struct_field.delete_image_source(image_id)
184    }
185
186    /// See [`AppResources::add_css_image_id`]
187    ///
188    /// [`AppResources::add_css_image_id`]: ../app_resources/struct.AppResources.html#method.add_css_image_id
189    pub fn add_css_image_id<S: Into<String>>(&mut self, css_id: S) -> ImageId {
190        self.$struct_field.add_css_image_id(css_id)
191    }
192
193    /// See [`AppResources::has_css_image_id`]
194    ///
195    /// [`AppResources::has_css_image_id`]: ../app_resources/struct.AppResources.html#method.has_css_image_id
196    pub fn has_css_image_id(&self, css_id: &str) -> bool {
197        self.$struct_field.has_css_image_id(css_id)
198    }
199
200    /// See [`AppResources::get_css_image_id`]
201    ///
202    /// [`AppResources::get_css_image_id`]: ../app_resources/struct.AppResources.html#method.get_css_image_id
203    pub fn get_css_image_id(&self, css_id: &str) -> Option<&ImageId> {
204        self.$struct_field.get_css_image_id(css_id)
205    }
206
207    /// See [`AppResources::delete_css_image_id`]
208    ///
209    /// [`AppResources::delete_css_image_id`]: ../app_resources/struct.AppResources.html#method.delete_css_image_id
210    pub fn delete_css_image_id(&mut self, css_id: &str) -> Option<ImageId> {
211        self.$struct_field.delete_css_image_id(css_id)
212    }
213
214    /// See [`AppResources::add_css_font_id`]
215    ///
216    /// [`AppResources::add_css_font_id`]: ../app_resources/struct.AppResources.html#method.add_css_font_id
217    pub fn add_css_font_id<S: Into<String>>(&mut self, css_id: S) -> FontId {
218        self.$struct_field.add_css_font_id(css_id)
219    }
220
221    /// See [`AppResources::has_css_font_id`]
222    ///
223    /// [`AppResources::has_css_font_id`]: ../app_resources/struct.AppResources.html#method.has_css_font_id
224    pub fn has_css_font_id(&self, css_id: &str) -> bool {
225        self.$struct_field.has_css_font_id(css_id)
226    }
227
228    /// See [`AppResources::get_css_font_id`]
229    ///
230    /// [`AppResources::get_css_font_id`]: ../app_resources/struct.AppResources.html#method.get_css_font_id
231    pub fn get_css_font_id(&self, css_id: &str) -> Option<&FontId> {
232        self.$struct_field.get_css_font_id(css_id)
233    }
234
235    /// See [`AppResources::delete_css_font_id`]
236    ///
237    /// [`AppResources::delete_css_font_id`]: ../app_resources/struct.AppResources.html#method.delete_css_font_id
238    pub fn delete_css_font_id(&mut self, css_id: &str) -> Option<FontId> {
239        self.$struct_field.delete_css_font_id(css_id)
240    }
241
242)}
243
244#[macro_export]
245macro_rules! impl_font_api {($struct_field:ident) => (
246
247    /// See [`AppResources::add_font`]
248    ///
249    /// [`AppResources::add_font`]: ../app_resources/struct.AppResources.html#method.add_font
250    pub fn add_font_source(&mut self, font_id: FontId, font_source: FontSource) {
251        self.$struct_field.add_font_source(font_id, font_source)
252    }
253
254    /// See [`AppResources::has_font`]
255    ///
256    /// [`AppResources::has_font`]: ../app_resources/struct.AppResources.html#method.has_font
257    pub fn has_font_source(&self, font_id: &FontId) -> bool {
258        self.$struct_field.has_font_source(font_id)
259    }
260
261    /// See [`AppResources::delete_font`]
262    ///
263    /// [`AppResources::delete_font`]: ../app_resources/struct.AppResources.html#method.delete_font
264    pub fn delete_font_source(&mut self, font_id: &FontId) {
265        self.$struct_field.delete_font_source(font_id)
266    }
267
268    pub fn get_loaded_font(&self, pipeline_id: &PipelineId, font_id: &ImmediateFontId) -> Option<&LoadedFont> {
269        self.$struct_field.get_loaded_font(pipeline_id, font_id)
270    }
271)}
272
273#[macro_export]
274macro_rules! impl_text_api {($struct_field:ident) => (
275
276    /// Adds a string to the internal text cache, but only store it as a string,
277    /// without caching the layout of the string.
278    ///
279    /// See [`AppResources::add_text`].
280    ///
281    /// [`AppResources::add_text`]: ../app_resources/struct.AppResources.html#method.add_text
282    pub fn add_text(&mut self, text: &str) -> TextId {
283        self.$struct_field.add_text(text)
284    }
285
286    /// Removes a string from both the string cache and the layouted text cache
287    ///
288    /// See [`AppResources::delete_text`].
289    ///
290    /// [`AppResources::delete_text`]: ../app_resources/struct.AppResources.html#method.delete_text
291    pub fn delete_text(&mut self, id: TextId) {
292        self.$struct_field.delete_text(id)
293    }
294
295    /// Empties the entire internal text cache, invalidating all `TextId`s.
296    /// If the given TextId is used after this call, the text will not render in the UI.
297    /// Use with care.
298    ///
299    /// See [`AppResources::clear_all_texts`].
300    ///
301    /// [`AppResources::clear_all_texts`]: ../app_resources/struct.AppResources.html#method.clear_all_texts
302    pub fn clear_all_texts(&mut self) {
303        self.$struct_field.clear_all_texts()
304    }
305
306)}
307
308#[macro_export]
309macro_rules! impl_timer_api {($struct_field:ident) => (
310
311    /// See [`AppState::add_timer`]
312    ///
313    /// [`AppState::add_timer`]: ../app_state/struct.AppState.html#method.add_timer
314    pub fn add_timer(&mut self, timer_id: TimerId, timer: Timer) {
315        self.$struct_field.add_timer(timer_id, timer)
316    }
317
318    /// See [`AppState::has_timer`]
319    ///
320    /// [`AppState::has_timer`]: ../app_state/struct.AppState.html#method.has_timer
321    pub fn has_timer(&self, timer_id: &TimerId) -> bool {
322        self.$struct_field.has_timer(timer_id)
323    }
324
325    /// See [`AppState::get_timer`]
326    ///
327    /// [`AppState::get_timer`]: ../app_state/struct.AppState.html#method.get_timer
328    pub fn get_timer(&self, timer_id: &TimerId) -> Option<Timer> {
329        self.$struct_field.get_timer(timer_id)
330    }
331
332    /// See [`AppState::delete_timer`]
333    ///
334    /// [`AppState::delete_timer`]: ../app_state/struct.AppState.html#method.delete_timer
335    pub fn delete_timer(&mut self, timer_id: &TimerId) -> Option<Timer> {
336        self.$struct_field.delete_timer(timer_id)
337    }
338
339)}
340
341/// Implements functions for `CallbackInfo` and `Info`,
342/// to prevent duplicating the functions
343macro_rules! impl_callback_info_api {() => (
344
345    pub fn window_state(&self) -> &FullWindowState {
346        self.current_window_state
347    }
348
349    pub fn window_state_mut(&mut self) -> &mut WindowState {
350        self.modifiable_window_state
351    }
352
353    pub fn get_keyboard_state(&self) -> &KeyboardState {
354        self.window_state().get_keyboard_state()
355    }
356
357    pub fn get_mouse_state(&self) -> &MouseState {
358        self.window_state().get_mouse_state()
359    }
360
361    /// Returns the bounds (width / height / position / margins / border) for any given NodeId,
362    /// useful for calculating scroll positions / offsets
363    pub fn get_bounds(&self, (dom_id, node_id): &(DomId, NodeId)) -> Option<&PositionedRectangle> {
364        self.layout_result.get(&dom_id)?.rects.get(*node_id)
365    }
366
367    /// If the node is a text node, return the text of the node
368    pub fn get_words(&self, (dom_id, node_id): &(DomId, NodeId)) -> Option<&Words> {
369        self.layout_result.get(&dom_id)?.word_cache.get(&node_id)
370    }
371
372    /// If the node is a text node, return the shaped glyphs (on a per-word basis, unpositioned)
373    pub fn get_scaled_words(&self, (dom_id, node_id): &(DomId, NodeId)) -> Option<&ScaledWords> {
374        self.layout_result.get(&dom_id).as_ref().and_then(|lr| lr.scaled_words.get(&node_id).as_ref().map(|sw| &sw.0))
375    }
376
377    /// If the node is a text node, return the shaped glyphs (on a per-word basis, unpositioned)
378    pub fn get_word_positions(&self, (dom_id, node_id): &(DomId, NodeId)) -> Option<&WordPositions> {
379        self.layout_result.get(&dom_id).as_ref().and_then(|lr| lr.positioned_word_cache.get(&node_id).as_ref().map(|sw| &sw.0))
380    }
381
382    pub fn get_layouted_glyphs(&self, (dom_id, node_id): &(DomId, NodeId)) -> Option<&LayoutedGlyphs> {
383        self.layout_result.get(&dom_id)?.layouted_glyph_cache.get(&node_id)
384    }
385
386    /// Returns information about the current scroll position of a node, such as the
387    /// size of the scroll frame, the position of the scroll in the parent (how far the node has been scrolled),
388    /// as well as the size of the parent node (so that things like "scroll to left edge", etc. are easy to calculate).
389    pub fn get_current_scroll_position(&self, (dom_id, node_id): &(DomId, NodeId)) -> Option<ScrollPosition> {
390        self.current_scroll_states.get(&dom_id)?.get(node_id).cloned()
391    }
392
393    /// For any node ID, returns what the position in its parent it is, plus the parent itself.
394    /// Returns `None` on the root ID (because the root has no parent, therefore it's the 1st item)
395    ///
396    /// Note: Index is 0-based (first item has the index of 0)
397    pub fn get_index_in_parent(&self, node_id: &(DomId, NodeId)) -> Option<(usize, (DomId, NodeId))> {
398        let node_layout = &self.ui_state[&node_id.0].dom.arena.node_hierarchy;
399
400        if node_id.1.index() > node_layout.len() {
401            return None; // node_id out of range
402        }
403
404        let parent_node = self.get_parent_node_id(node_id)?;
405        Some((node_layout.get_index_in_parent(node_id.1), parent_node))
406    }
407
408    // Functions that are may be called from the user callback
409    // - the `CallbackInfo` contains a `&mut UiState`, which can be
410    // used to query DOM information when the callbacks are run
411
412    /// Returns the hierarchy of the given node ID
413    pub fn get_node(&self, (dom_id, node_id): &(DomId, NodeId)) -> Option<&Node> {
414        self.ui_state[dom_id].dom.arena.node_hierarchy.internal.get(node_id.index())
415    }
416
417    /// Returns the parent of the given `NodeId` or None if the target is the root node.
418    pub fn get_parent_node_id(&self, node_id: &(DomId, NodeId)) -> Option<(DomId, NodeId)> {
419        let new_node_id = self.get_node(node_id)?.parent?;
420        Some((node_id.0.clone(), new_node_id))
421    }
422
423    /// Returns the node hierarchy (DOM tree order)
424    pub fn get_node_hierarchy(&self) -> &NodeHierarchy {
425        &self.ui_state[&self.hit_dom_node.0].dom.arena.node_hierarchy
426    }
427
428    /// Returns the node content of a specific node
429    pub fn get_node_content(&self, (dom_id, node_id): &(DomId, NodeId)) -> Option<&NodeData> {
430        self.ui_state[dom_id].dom.arena.node_data.internal.get(node_id.index())
431    }
432
433    /// Returns the index of the target NodeId (the target that received the event)
434    /// in the targets parent or None if the target is the root node
435    pub fn target_index_in_parent(&self) -> Option<usize> {
436        let (index, _) = self.get_index_in_parent(&self.hit_dom_node)?;
437        Some(index)
438    }
439
440    /// Returns the parent of the current target or None if the target is the root node.
441    pub fn target_parent_node_id(&self) -> Option<(DomId, NodeId)> {
442        self.get_parent_node_id(&self.hit_dom_node)
443    }
444
445    /// Checks whether the target of the CallbackInfo has a certain node type
446    pub fn target_is_node_type(&self, node_type: NodeType) -> bool {
447        if let Some(self_node) = self.get_node_content(&self.hit_dom_node) {
448            self_node.is_node_type(node_type)
449        } else {
450            false
451        }
452    }
453
454    /// Checks whether the target of the CallbackInfo has a certain ID
455    pub fn target_has_id(&self, id: &str) -> bool {
456        if let Some(self_node) = self.get_node_content(&self.hit_dom_node) {
457            self_node.has_id(id)
458        } else {
459            false
460        }
461    }
462
463    /// Checks whether the target of the CallbackInfo has a certain class
464    pub fn target_has_class(&self, class: &str) -> bool {
465        if let Some(self_node) = self.get_node_content(&self.hit_dom_node) {
466            self_node.has_class(class)
467        } else {
468            false
469        }
470    }
471
472    /// Traverses up the hierarchy, checks whether any parent has a certain ID,
473    /// the returns that parent
474    pub fn any_parent_has_id(&self, id: &str) -> Option<(DomId, NodeId)> {
475        self.parent_nodes().find(|parent_id| {
476            if let Some(self_node) = self.get_node_content(parent_id) {
477                self_node.has_id(id)
478            } else {
479                false
480            }
481        })
482    }
483
484    /// Traverses up the hierarchy, checks whether any parent has a certain class
485    pub fn any_parent_has_class(&self, class: &str) -> Option<(DomId, NodeId)> {
486        self.parent_nodes().find(|parent_id| {
487            if let Some(self_node) = self.get_node_content(parent_id) {
488                self_node.has_class(class)
489            } else {
490                false
491            }
492        })
493    }
494
495    /// Scrolls a node to a certain position
496    pub fn scroll_node(&mut self, (dom_id, node_id): &(DomId, NodeId), scroll_location: LayoutPoint) {
497        self.nodes_scrolled_in_callback
498            .entry(dom_id.clone())
499            .or_insert_with(|| BTreeMap::default())
500            .insert(*node_id, scroll_location);
501    }
502
503    /// Scrolls a node to a certain position
504    pub fn scroll_target(&mut self, scroll_location: LayoutPoint) {
505        let target = self.hit_dom_node.clone(); // borrowing issue
506        self.scroll_node(&target, scroll_location);
507    }
508
509    /// Set the focus_target to a certain div by parsing a string.
510    /// Note that the parsing of the string can fail, therefore the Result
511    #[cfg(feature = "css_parser")]
512    pub fn set_focus_from_css<'c>(&mut self, input: &'c str) -> Result<(), CssPathParseError<'c>> {
513        use azul_css_parser::parse_css_path;
514        let path = parse_css_path(input)?;
515        *self.focus_target = Some(FocusTarget::Path((self.hit_dom_node.0.clone(), path)));
516        Ok(())
517    }
518
519    /// Creates an iterator that starts at the current DOM node and continouusly
520    /// returns the parent `(DomId, NodeId)`, until the iterator gets to the root DOM node.
521    pub fn parent_nodes<'c>(&'c self) -> ParentNodesIterator<'c> {
522        ParentNodesIterator {
523            ui_state: &self.ui_state,
524            current_item: self.hit_dom_node.clone(),
525        }
526    }
527
528    /// Sets the focus_target by using an already-parsed `CssPath`.
529    pub fn set_focus_from_path(&mut self, path: CssPath) {
530        *self.focus_target = Some(FocusTarget::Path((self.hit_dom_node.0.clone(), path)))
531    }
532
533    /// Set the focus_target of the window to a specific div using a `NodeId`.
534    ///
535    /// Note that this ID will be dependent on the position in the DOM and therefore
536    /// the next frames UI must be the exact same as the current one, otherwise
537    /// the focus_target will be cleared or shifted (depending on apps setting).
538    pub fn set_focus_from_node_id(&mut self, id: (DomId, NodeId)) {
539        *self.focus_target = Some(FocusTarget::Id(id));
540    }
541
542    /// Clears the focus_target for the next frame.
543    pub fn clear_focus(&mut self) {
544        *self.focus_target = Some(FocusTarget::NoFocus);
545    }
546)}
547