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