azul_core/dom.rs
1//! Defines the core Document Object Model (DOM) structures.
2//!
3//! This module is responsible for representing the UI as a tree of nodes,
4//! similar to the HTML DOM. It includes definitions for node types, event handling,
5//! accessibility, and the main `Dom` and `CompactDom` structures.
6
7#[cfg(not(feature = "std"))]
8use alloc::string::ToString;
9use alloc::{boxed::Box, collections::btree_map::BTreeMap, string::String, vec::Vec};
10use core::{
11 fmt,
12 hash::{Hash, Hasher},
13 iter::FromIterator,
14 mem,
15 sync::atomic::{AtomicUsize, Ordering},
16};
17
18use azul_css::{
19 css::{Css, NodeTypeTag},
20 format_rust_code::GetHash,
21 props::{
22 basic::{FloatValue, FontRef},
23 layout::{LayoutDisplay, LayoutFloat, LayoutPosition},
24 property::CssProperty,
25 },
26 AzString, OptionString,
27};
28
29// Re-export event filters from events module (moved in Phase 3.5)
30pub use crate::events::{
31 ApplicationEventFilter, ComponentEventFilter, EventFilter, FocusEventFilter, HoverEventFilter,
32 NotEventFilter, WindowEventFilter,
33};
34pub use crate::id::{Node, NodeHierarchy, NodeId};
35use crate::{
36 callbacks::{
37 CoreCallback, CoreCallbackData, CoreCallbackDataVec, CoreCallbackType, IFrameCallback,
38 IFrameCallbackType,
39 },
40 geom::LogicalPosition,
41 id::{NodeDataContainer, NodeDataContainerRef, NodeDataContainerRefMut},
42 menu::Menu,
43 prop_cache::{CssPropertyCache, CssPropertyCachePtr},
44 refany::{OptionRefAny, RefAny},
45 resources::{
46 image_ref_get_hash, CoreImageCallback, ImageMask, ImageRef, ImageRefHash, RendererResources,
47 },
48 styled_dom::{
49 CompactDom, NodeHierarchyItemId, StyleFontFamilyHash, StyledDom, StyledNode,
50 StyledNodeState,
51 },
52 window::OptionVirtualKeyCodeCombo,
53};
54pub use azul_css::dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec};
55
56static TAG_ID: AtomicUsize = AtomicUsize::new(1);
57
58/// Strongly-typed input element types for HTML `<input>` elements.
59#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
60#[repr(C)]
61pub enum InputType {
62 /// Text input (default)
63 Text,
64 /// Button
65 Button,
66 /// Checkbox
67 Checkbox,
68 /// Color picker
69 Color,
70 /// Date picker
71 Date,
72 /// Date and time picker
73 Datetime,
74 /// Date and time picker (local)
75 DatetimeLocal,
76 /// Email address input
77 Email,
78 /// File upload
79 File,
80 /// Hidden input
81 Hidden,
82 /// Image button
83 Image,
84 /// Month picker
85 Month,
86 /// Number input
87 Number,
88 /// Password input
89 Password,
90 /// Radio button
91 Radio,
92 /// Range slider
93 Range,
94 /// Reset button
95 Reset,
96 /// Search input
97 Search,
98 /// Submit button
99 Submit,
100 /// Telephone number input
101 Tel,
102 /// Time picker
103 Time,
104 /// URL input
105 Url,
106 /// Week picker
107 Week,
108}
109
110impl InputType {
111 /// Returns the HTML attribute value for this input type
112 pub const fn as_str(&self) -> &'static str {
113 match self {
114 InputType::Text => "text",
115 InputType::Button => "button",
116 InputType::Checkbox => "checkbox",
117 InputType::Color => "color",
118 InputType::Date => "date",
119 InputType::Datetime => "datetime",
120 InputType::DatetimeLocal => "datetime-local",
121 InputType::Email => "email",
122 InputType::File => "file",
123 InputType::Hidden => "hidden",
124 InputType::Image => "image",
125 InputType::Month => "month",
126 InputType::Number => "number",
127 InputType::Password => "password",
128 InputType::Radio => "radio",
129 InputType::Range => "range",
130 InputType::Reset => "reset",
131 InputType::Search => "search",
132 InputType::Submit => "submit",
133 InputType::Tel => "tel",
134 InputType::Time => "time",
135 InputType::Url => "url",
136 InputType::Week => "week",
137 }
138 }
139}
140
141#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
142#[repr(C)]
143pub struct TagId {
144 pub inner: u64,
145}
146
147impl ::core::fmt::Display for TagId {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 f.debug_struct("TagId").field("inner", &self.inner).finish()
150 }
151}
152
153impl_option!(
154 TagId,
155 OptionTagId,
156 [Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash]
157);
158
159impl TagId {
160 pub const fn into_crate_internal(&self) -> TagId {
161 TagId { inner: self.inner }
162 }
163 pub const fn from_crate_internal(t: TagId) -> Self {
164 TagId { inner: t.inner }
165 }
166
167 /// Creates a new, unique hit-testing tag ID.
168 pub fn unique() -> Self {
169 TagId {
170 inner: TAG_ID.fetch_add(1, Ordering::SeqCst) as u64,
171 }
172 }
173
174 /// Resets the counter (usually done after each frame) so that we can
175 /// track hit-testing Tag IDs of subsequent frames.
176 pub fn reset() {
177 TAG_ID.swap(1, Ordering::SeqCst);
178 }
179}
180
181/// Same as the `TagId`, but only for scrollable nodes.
182/// This provides a typed distinction for tags associated with scrolling containers.
183#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
184#[repr(C)]
185pub struct ScrollTagId {
186 pub inner: TagId,
187}
188
189impl ::core::fmt::Display for ScrollTagId {
190 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191 f.debug_struct("ScrollTagId")
192 .field("inner", &self.inner)
193 .finish()
194 }
195}
196
197impl ::core::fmt::Debug for ScrollTagId {
198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 write!(f, "{}", self)
200 }
201}
202
203impl ScrollTagId {
204 /// Creates a new, unique scroll tag ID. Note that this should not
205 /// be used for identifying nodes, use the `DomNodeHash` instead.
206 pub fn unique() -> ScrollTagId {
207 ScrollTagId {
208 inner: TagId::unique(),
209 }
210 }
211}
212
213/// Orientation of a scrollbar.
214#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
215#[repr(C)]
216pub enum ScrollbarOrientation {
217 Horizontal,
218 Vertical,
219}
220
221/// Calculated hash of a DOM node, used for identifying identical DOM
222/// nodes across frames for efficient diffing and state preservation.
223#[derive(Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
224#[repr(C)]
225pub struct DomNodeHash {
226 pub inner: u64,
227}
228
229impl ::core::fmt::Debug for DomNodeHash {
230 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
231 write!(f, "DomNodeHash({})", self.inner)
232 }
233}
234
235/// List of core DOM node types built into `azul`.
236/// This enum defines the building blocks of the UI, similar to HTML tags.
237#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
238#[repr(C, u8)]
239pub enum NodeType {
240 // Root and container elements
241 /// Root HTML element.
242 Html,
243 /// Document head (metadata container).
244 Head,
245 /// Root element of the document body.
246 Body,
247 /// Generic block-level container.
248 Div,
249 /// Paragraph.
250 P,
251 /// Article content.
252 Article,
253 /// Section of a document.
254 Section,
255 /// Navigation links.
256 Nav,
257 /// Sidebar/tangential content.
258 Aside,
259 /// Header section.
260 Header,
261 /// Footer section.
262 Footer,
263 /// Main content.
264 Main,
265 /// Figure with optional caption.
266 Figure,
267 /// Caption for figure element.
268 FigCaption,
269 /// Headings.
270 H1,
271 H2,
272 H3,
273 H4,
274 H5,
275 H6,
276 /// Line break.
277 Br,
278 /// Horizontal rule.
279 Hr,
280 /// Preformatted text.
281 Pre,
282 /// Block quote.
283 BlockQuote,
284 /// Address.
285 Address,
286 /// Details disclosure widget.
287 Details,
288 /// Summary for details element.
289 Summary,
290 /// Dialog box or window.
291 Dialog,
292
293 // List elements
294 /// Unordered list.
295 Ul,
296 /// Ordered list.
297 Ol,
298 /// List item.
299 Li,
300 /// Definition list.
301 Dl,
302 /// Definition term.
303 Dt,
304 /// Definition description.
305 Dd,
306 /// Menu list.
307 Menu,
308 /// Menu item.
309 MenuItem,
310 /// Directory list (deprecated).
311 Dir,
312
313 // Table elements
314 /// Table container.
315 Table,
316 /// Table caption.
317 Caption,
318 /// Table header.
319 THead,
320 /// Table body.
321 TBody,
322 /// Table footer.
323 TFoot,
324 /// Table row.
325 Tr,
326 /// Table header cell.
327 Th,
328 /// Table data cell.
329 Td,
330 /// Table column group.
331 ColGroup,
332 /// Table column.
333 Col,
334
335 // Form elements
336 /// Form container.
337 Form,
338 /// Form fieldset.
339 FieldSet,
340 /// Fieldset legend.
341 Legend,
342 /// Label for form controls.
343 Label,
344 /// Input control.
345 Input,
346 /// Button control.
347 Button,
348 /// Select dropdown.
349 Select,
350 /// Option group.
351 OptGroup,
352 /// Select option.
353 SelectOption,
354 /// Multiline text input.
355 TextArea,
356 /// Form output element.
357 Output,
358 /// Progress indicator.
359 Progress,
360 /// Scalar measurement within a known range.
361 Meter,
362 /// List of predefined options for input.
363 DataList,
364
365 // Inline elements
366 /// Generic inline container.
367 Span,
368 /// Anchor/hyperlink.
369 A,
370 /// Emphasized text.
371 Em,
372 /// Strongly emphasized text.
373 Strong,
374 /// Bold text (deprecated - use `Dom::create_strong()` for semantic importance).
375 B,
376 /// Italic text (deprecated - use `Dom::create_em()` for emphasis or `Dom::create_cite()` for citations).
377 I,
378 /// Underline text.
379 U,
380 /// Strikethrough text.
381 S,
382 /// Marked/highlighted text.
383 Mark,
384 /// Deleted text.
385 Del,
386 /// Inserted text.
387 Ins,
388 /// Code.
389 Code,
390 /// Sample output.
391 Samp,
392 /// Keyboard input.
393 Kbd,
394 /// Variable.
395 Var,
396 /// Citation.
397 Cite,
398 /// Defining instance of a term.
399 Dfn,
400 /// Abbreviation.
401 Abbr,
402 /// Acronym.
403 Acronym,
404 /// Inline quotation.
405 Q,
406 /// Date/time.
407 Time,
408 /// Subscript.
409 Sub,
410 /// Superscript.
411 Sup,
412 /// Small text (deprecated - use CSS `font-size` instead).
413 Small,
414 /// Big text (deprecated - use CSS `font-size` instead).
415 Big,
416 /// Bi-directional override.
417 Bdo,
418 /// Bi-directional isolate.
419 Bdi,
420 /// Word break opportunity.
421 Wbr,
422 /// Ruby annotation.
423 Ruby,
424 /// Ruby text.
425 Rt,
426 /// Ruby text container.
427 Rtc,
428 /// Ruby parenthesis.
429 Rp,
430 /// Machine-readable data.
431 Data,
432
433 // Embedded content
434 /// Canvas for graphics.
435 Canvas,
436 /// Embedded object.
437 Object,
438 /// Embedded object parameter.
439 Param,
440 /// External resource embed.
441 Embed,
442 /// Audio content.
443 Audio,
444 /// Video content.
445 Video,
446 /// Media source.
447 Source,
448 /// Text track for media.
449 Track,
450 /// Image map.
451 Map,
452 /// Image map area.
453 Area,
454 /// SVG graphics.
455 Svg,
456
457 // Metadata elements
458 /// Document title.
459 Title,
460 /// Metadata.
461 Meta,
462 /// External resource link.
463 Link,
464 /// Embedded or referenced script.
465 Script,
466 /// Style information.
467 Style,
468 /// Base URL for relative URLs.
469 Base,
470
471 // Pseudo-elements (transformed into real elements)
472 /// ::before pseudo-element.
473 Before,
474 /// ::after pseudo-element.
475 After,
476 /// ::marker pseudo-element.
477 Marker,
478 /// ::placeholder pseudo-element.
479 Placeholder,
480
481 // Special content types
482 /// Text content, ::text
483 Text(AzString),
484 /// Image element, ::image
485 Image(ImageRef),
486 /// IFrame (embedded content)
487 IFrame(IFrameNode),
488 /// Icon element - resolved to actual content by IconProvider
489 /// The string is the icon name (e.g., "home", "settings", "search")
490 Icon(AzString),
491}
492
493impl NodeType {
494 fn into_library_owned_nodetype(&self) -> Self {
495 use self::NodeType::*;
496 match self {
497 Html => Html,
498 Head => Head,
499 Body => Body,
500 Div => Div,
501 P => P,
502 Article => Article,
503 Section => Section,
504 Nav => Nav,
505 Aside => Aside,
506 Header => Header,
507 Footer => Footer,
508 Main => Main,
509 Figure => Figure,
510 FigCaption => FigCaption,
511 H1 => H1,
512 H2 => H2,
513 H3 => H3,
514 H4 => H4,
515 H5 => H5,
516 H6 => H6,
517 Br => Br,
518 Hr => Hr,
519 Pre => Pre,
520 BlockQuote => BlockQuote,
521 Address => Address,
522 Details => Details,
523 Summary => Summary,
524 Dialog => Dialog,
525 Ul => Ul,
526 Ol => Ol,
527 Li => Li,
528 Dl => Dl,
529 Dt => Dt,
530 Dd => Dd,
531 Menu => Menu,
532 MenuItem => MenuItem,
533 Dir => Dir,
534 Table => Table,
535 Caption => Caption,
536 THead => THead,
537 TBody => TBody,
538 TFoot => TFoot,
539 Tr => Tr,
540 Th => Th,
541 Td => Td,
542 ColGroup => ColGroup,
543 Col => Col,
544 Form => Form,
545 FieldSet => FieldSet,
546 Legend => Legend,
547 Label => Label,
548 Input => Input,
549 Button => Button,
550 Select => Select,
551 OptGroup => OptGroup,
552 SelectOption => SelectOption,
553 TextArea => TextArea,
554 Output => Output,
555 Progress => Progress,
556 Meter => Meter,
557 DataList => DataList,
558 Span => Span,
559 A => A,
560 Em => Em,
561 Strong => Strong,
562 B => B,
563 I => I,
564 U => U,
565 S => S,
566 Mark => Mark,
567 Del => Del,
568 Ins => Ins,
569 Code => Code,
570 Samp => Samp,
571 Kbd => Kbd,
572 Var => Var,
573 Cite => Cite,
574 Dfn => Dfn,
575 Abbr => Abbr,
576 Acronym => Acronym,
577 Q => Q,
578 Time => Time,
579 Sub => Sub,
580 Sup => Sup,
581 Small => Small,
582 Big => Big,
583 Bdo => Bdo,
584 Bdi => Bdi,
585 Wbr => Wbr,
586 Ruby => Ruby,
587 Rt => Rt,
588 Rtc => Rtc,
589 Rp => Rp,
590 Data => Data,
591 Canvas => Canvas,
592 Object => Object,
593 Param => Param,
594 Embed => Embed,
595 Audio => Audio,
596 Video => Video,
597 Source => Source,
598 Track => Track,
599 Map => Map,
600 Area => Area,
601 Svg => Svg,
602 Title => Title,
603 Meta => Meta,
604 Link => Link,
605 Script => Script,
606 Style => Style,
607 Base => Base,
608 Before => Before,
609 After => After,
610 Marker => Marker,
611 Placeholder => Placeholder,
612
613 Text(s) => Text(s.clone_self()),
614 Image(i) => Image(i.clone()), // note: shallow clone
615 IFrame(i) => IFrame(IFrameNode {
616 callback: i.callback.clone(),
617 refany: i.refany.clone(),
618 }),
619 Icon(s) => Icon(s.clone_self()),
620 }
621 }
622
623 pub fn format(&self) -> Option<String> {
624 use self::NodeType::*;
625 match self {
626 Text(s) => Some(format!("{}", s)),
627 Image(id) => Some(format!("image({:?})", id)),
628 IFrame(i) => Some(format!("iframe({:?})", i)),
629 Icon(s) => Some(format!("icon({})", s)),
630 _ => None,
631 }
632 }
633
634 /// Returns the NodeTypeTag for CSS selector matching.
635 pub fn get_path(&self) -> NodeTypeTag {
636 match self {
637 Self::Html => NodeTypeTag::Html,
638 Self::Head => NodeTypeTag::Head,
639 Self::Body => NodeTypeTag::Body,
640 Self::Div => NodeTypeTag::Div,
641 Self::P => NodeTypeTag::P,
642 Self::Article => NodeTypeTag::Article,
643 Self::Section => NodeTypeTag::Section,
644 Self::Nav => NodeTypeTag::Nav,
645 Self::Aside => NodeTypeTag::Aside,
646 Self::Header => NodeTypeTag::Header,
647 Self::Footer => NodeTypeTag::Footer,
648 Self::Main => NodeTypeTag::Main,
649 Self::Figure => NodeTypeTag::Figure,
650 Self::FigCaption => NodeTypeTag::FigCaption,
651 Self::H1 => NodeTypeTag::H1,
652 Self::H2 => NodeTypeTag::H2,
653 Self::H3 => NodeTypeTag::H3,
654 Self::H4 => NodeTypeTag::H4,
655 Self::H5 => NodeTypeTag::H5,
656 Self::H6 => NodeTypeTag::H6,
657 Self::Br => NodeTypeTag::Br,
658 Self::Hr => NodeTypeTag::Hr,
659 Self::Pre => NodeTypeTag::Pre,
660 Self::BlockQuote => NodeTypeTag::BlockQuote,
661 Self::Address => NodeTypeTag::Address,
662 Self::Details => NodeTypeTag::Details,
663 Self::Summary => NodeTypeTag::Summary,
664 Self::Dialog => NodeTypeTag::Dialog,
665 Self::Ul => NodeTypeTag::Ul,
666 Self::Ol => NodeTypeTag::Ol,
667 Self::Li => NodeTypeTag::Li,
668 Self::Dl => NodeTypeTag::Dl,
669 Self::Dt => NodeTypeTag::Dt,
670 Self::Dd => NodeTypeTag::Dd,
671 Self::Menu => NodeTypeTag::Menu,
672 Self::MenuItem => NodeTypeTag::MenuItem,
673 Self::Dir => NodeTypeTag::Dir,
674 Self::Table => NodeTypeTag::Table,
675 Self::Caption => NodeTypeTag::Caption,
676 Self::THead => NodeTypeTag::THead,
677 Self::TBody => NodeTypeTag::TBody,
678 Self::TFoot => NodeTypeTag::TFoot,
679 Self::Tr => NodeTypeTag::Tr,
680 Self::Th => NodeTypeTag::Th,
681 Self::Td => NodeTypeTag::Td,
682 Self::ColGroup => NodeTypeTag::ColGroup,
683 Self::Col => NodeTypeTag::Col,
684 Self::Form => NodeTypeTag::Form,
685 Self::FieldSet => NodeTypeTag::FieldSet,
686 Self::Legend => NodeTypeTag::Legend,
687 Self::Label => NodeTypeTag::Label,
688 Self::Input => NodeTypeTag::Input,
689 Self::Button => NodeTypeTag::Button,
690 Self::Select => NodeTypeTag::Select,
691 Self::OptGroup => NodeTypeTag::OptGroup,
692 Self::SelectOption => NodeTypeTag::SelectOption,
693 Self::TextArea => NodeTypeTag::TextArea,
694 Self::Output => NodeTypeTag::Output,
695 Self::Progress => NodeTypeTag::Progress,
696 Self::Meter => NodeTypeTag::Meter,
697 Self::DataList => NodeTypeTag::DataList,
698 Self::Span => NodeTypeTag::Span,
699 Self::A => NodeTypeTag::A,
700 Self::Em => NodeTypeTag::Em,
701 Self::Strong => NodeTypeTag::Strong,
702 Self::B => NodeTypeTag::B,
703 Self::I => NodeTypeTag::I,
704 Self::U => NodeTypeTag::U,
705 Self::S => NodeTypeTag::S,
706 Self::Mark => NodeTypeTag::Mark,
707 Self::Del => NodeTypeTag::Del,
708 Self::Ins => NodeTypeTag::Ins,
709 Self::Code => NodeTypeTag::Code,
710 Self::Samp => NodeTypeTag::Samp,
711 Self::Kbd => NodeTypeTag::Kbd,
712 Self::Var => NodeTypeTag::Var,
713 Self::Cite => NodeTypeTag::Cite,
714 Self::Dfn => NodeTypeTag::Dfn,
715 Self::Abbr => NodeTypeTag::Abbr,
716 Self::Acronym => NodeTypeTag::Acronym,
717 Self::Q => NodeTypeTag::Q,
718 Self::Time => NodeTypeTag::Time,
719 Self::Sub => NodeTypeTag::Sub,
720 Self::Sup => NodeTypeTag::Sup,
721 Self::Small => NodeTypeTag::Small,
722 Self::Big => NodeTypeTag::Big,
723 Self::Bdo => NodeTypeTag::Bdo,
724 Self::Bdi => NodeTypeTag::Bdi,
725 Self::Wbr => NodeTypeTag::Wbr,
726 Self::Ruby => NodeTypeTag::Ruby,
727 Self::Rt => NodeTypeTag::Rt,
728 Self::Rtc => NodeTypeTag::Rtc,
729 Self::Rp => NodeTypeTag::Rp,
730 Self::Data => NodeTypeTag::Data,
731 Self::Canvas => NodeTypeTag::Canvas,
732 Self::Object => NodeTypeTag::Object,
733 Self::Param => NodeTypeTag::Param,
734 Self::Embed => NodeTypeTag::Embed,
735 Self::Audio => NodeTypeTag::Audio,
736 Self::Video => NodeTypeTag::Video,
737 Self::Source => NodeTypeTag::Source,
738 Self::Track => NodeTypeTag::Track,
739 Self::Map => NodeTypeTag::Map,
740 Self::Area => NodeTypeTag::Area,
741 Self::Svg => NodeTypeTag::Svg,
742 Self::Title => NodeTypeTag::Title,
743 Self::Meta => NodeTypeTag::Meta,
744 Self::Link => NodeTypeTag::Link,
745 Self::Script => NodeTypeTag::Script,
746 Self::Style => NodeTypeTag::Style,
747 Self::Base => NodeTypeTag::Base,
748 Self::Text(_) => NodeTypeTag::Text,
749 Self::Image(_) => NodeTypeTag::Img,
750 Self::IFrame(_) => NodeTypeTag::IFrame,
751 Self::Icon(_) => NodeTypeTag::Icon,
752 Self::Before => NodeTypeTag::Before,
753 Self::After => NodeTypeTag::After,
754 Self::Marker => NodeTypeTag::Marker,
755 Self::Placeholder => NodeTypeTag::Placeholder,
756 }
757 }
758
759 /// Returns whether this node type is a semantic HTML element that should
760 /// automatically generate an accessibility tree node.
761 ///
762 /// These are elements with inherent semantic meaning that assistive
763 /// technologies should be aware of, even without explicit ARIA attributes.
764 pub const fn is_semantic_for_accessibility(&self) -> bool {
765 matches!(
766 self,
767 Self::Button
768 | Self::Input
769 | Self::TextArea
770 | Self::Select
771 | Self::A
772 | Self::H1
773 | Self::H2
774 | Self::H3
775 | Self::H4
776 | Self::H5
777 | Self::H6
778 | Self::Article
779 | Self::Section
780 | Self::Nav
781 | Self::Main
782 | Self::Header
783 | Self::Footer
784 | Self::Aside
785 )
786 }
787}
788
789/// Represents the CSS formatting context for an element
790#[derive(Clone, PartialEq)]
791pub enum FormattingContext {
792 /// Block-level formatting context
793 Block {
794 /// Whether this element establishes a new block formatting context
795 establishes_new_context: bool,
796 },
797 /// Inline-level formatting context
798 Inline,
799 /// Inline-block (participates in an IFC but creates a BFC)
800 InlineBlock,
801 /// Flex formatting context
802 Flex,
803 /// Float (left or right)
804 Float(LayoutFloat),
805 /// Absolutely positioned (out of flow)
806 OutOfFlow(LayoutPosition),
807 /// Table formatting context (container)
808 Table,
809 /// Table row group formatting context (thead, tbody, tfoot)
810 TableRowGroup,
811 /// Table row formatting context
812 TableRow,
813 /// Table cell formatting context (td, th)
814 TableCell,
815 /// Table column group formatting context
816 TableColumnGroup,
817 /// Table caption formatting context
818 TableCaption,
819 /// Grid formatting context
820 Grid,
821 /// No formatting context (display: none)
822 None,
823}
824
825impl fmt::Debug for FormattingContext {
826 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
827 match self {
828 FormattingContext::Block {
829 establishes_new_context,
830 } => write!(
831 f,
832 "Block {{ establishes_new_context: {establishes_new_context:?} }}"
833 ),
834 FormattingContext::Inline => write!(f, "Inline"),
835 FormattingContext::InlineBlock => write!(f, "InlineBlock"),
836 FormattingContext::Flex => write!(f, "Flex"),
837 FormattingContext::Float(layout_float) => write!(f, "Float({layout_float:?})"),
838 FormattingContext::OutOfFlow(layout_position) => {
839 write!(f, "OutOfFlow({layout_position:?})")
840 }
841 FormattingContext::Grid => write!(f, "Grid"),
842 FormattingContext::None => write!(f, "None"),
843 FormattingContext::Table => write!(f, "Table"),
844 FormattingContext::TableRowGroup => write!(f, "TableRowGroup"),
845 FormattingContext::TableRow => write!(f, "TableRow"),
846 FormattingContext::TableCell => write!(f, "TableCell"),
847 FormattingContext::TableColumnGroup => write!(f, "TableColumnGroup"),
848 FormattingContext::TableCaption => write!(f, "TableCaption"),
849 }
850 }
851}
852
853impl Default for FormattingContext {
854 fn default() -> Self {
855 FormattingContext::Block {
856 establishes_new_context: false,
857 }
858 }
859}
860
861/// Defines the type of event that can trigger a callback action.
862#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
863#[repr(C)]
864pub enum On {
865 /// Mouse cursor is hovering over the element.
866 MouseOver,
867 /// Mouse cursor has is over element and is pressed
868 /// (not good for "click" events - use `MouseUp` instead).
869 MouseDown,
870 /// (Specialization of `MouseDown`). Fires only if the left mouse button
871 /// has been pressed while cursor was over the element.
872 LeftMouseDown,
873 /// (Specialization of `MouseDown`). Fires only if the middle mouse button
874 /// has been pressed while cursor was over the element.
875 MiddleMouseDown,
876 /// (Specialization of `MouseDown`). Fires only if the right mouse button
877 /// has been pressed while cursor was over the element.
878 RightMouseDown,
879 /// Mouse button has been released while cursor was over the element.
880 MouseUp,
881 /// (Specialization of `MouseUp`). Fires only if the left mouse button has
882 /// been released while cursor was over the element.
883 LeftMouseUp,
884 /// (Specialization of `MouseUp`). Fires only if the middle mouse button has
885 /// been released while cursor was over the element.
886 MiddleMouseUp,
887 /// (Specialization of `MouseUp`). Fires only if the right mouse button has
888 /// been released while cursor was over the element.
889 RightMouseUp,
890 /// Mouse cursor has entered the element.
891 MouseEnter,
892 /// Mouse cursor has left the element.
893 MouseLeave,
894 /// Mousewheel / touchpad scrolling.
895 Scroll,
896 /// The window received a unicode character (also respects the system locale).
897 /// Check `keyboard_state.current_char` to get the current pressed character.
898 TextInput,
899 /// A **virtual keycode** was pressed. Note: This is only the virtual keycode,
900 /// not the actual char. If you want to get the character, use `TextInput` instead.
901 /// A virtual key does not have to map to a printable character.
902 ///
903 /// You can get all currently pressed virtual keycodes in the
904 /// `keyboard_state.current_virtual_keycodes` and / or just the last keycode in the
905 /// `keyboard_state.latest_virtual_keycode`.
906 VirtualKeyDown,
907 /// A **virtual keycode** was release. See `VirtualKeyDown` for more info.
908 VirtualKeyUp,
909 /// A file has been dropped on the element.
910 HoveredFile,
911 /// A file is being hovered on the element.
912 DroppedFile,
913 /// A file was hovered, but has exited the window.
914 HoveredFileCancelled,
915 /// Equivalent to `onfocus`.
916 FocusReceived,
917 /// Equivalent to `onblur`.
918 FocusLost,
919
920 // Accessibility-specific events
921 /// Default action triggered by screen reader (usually same as click/activate)
922 Default,
923 /// Element should collapse (e.g., accordion panel, tree node)
924 Collapse,
925 /// Element should expand (e.g., accordion panel, tree node)
926 Expand,
927 /// Increment value (e.g., number input, slider)
928 Increment,
929 /// Decrement value (e.g., number input, slider)
930 Decrement,
931}
932
933// NOTE: EventFilter types moved to core/src/events.rs (Phase 3.5)
934//
935// The following types are now defined in events.rs and re-exported above:
936// - EventFilter
937// - HoverEventFilter
938// - FocusEventFilter
939// - WindowEventFilter
940// - NotEventFilter
941// - ComponentEventFilter
942// - ApplicationEventFilter
943//
944// This consolidates all event-related logic in one place.
945
946/// Contains the necessary information to render an embedded `IFrame` node.
947#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
948#[repr(C)]
949pub struct IFrameNode {
950 /// The callback function that returns the DOM for the iframe's content.
951 pub callback: IFrameCallback,
952 /// The application data passed to the iframe's layout callback.
953 pub refany: RefAny,
954}
955
956/// An enum that holds either a CSS ID or a class name as a string.
957#[repr(C, u8)]
958#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
959pub enum IdOrClass {
960 Id(AzString),
961 Class(AzString),
962}
963
964impl_vec!(
965 IdOrClass,
966 IdOrClassVec,
967 IdOrClassVecDestructor,
968 IdOrClassVecDestructorType
969);
970impl_vec_debug!(IdOrClass, IdOrClassVec);
971impl_vec_partialord!(IdOrClass, IdOrClassVec);
972impl_vec_ord!(IdOrClass, IdOrClassVec);
973impl_vec_clone!(IdOrClass, IdOrClassVec, IdOrClassVecDestructor);
974impl_vec_partialeq!(IdOrClass, IdOrClassVec);
975impl_vec_eq!(IdOrClass, IdOrClassVec);
976impl_vec_hash!(IdOrClass, IdOrClassVec);
977
978impl IdOrClass {
979 pub fn as_id(&self) -> Option<&str> {
980 match self {
981 IdOrClass::Id(s) => Some(s.as_str()),
982 IdOrClass::Class(_) => None,
983 }
984 }
985 pub fn as_class(&self) -> Option<&str> {
986 match self {
987 IdOrClass::Class(s) => Some(s.as_str()),
988 IdOrClass::Id(_) => None,
989 }
990 }
991}
992
993/// Name-value pair for custom attributes (data-*, aria-*, etc.)
994#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
995#[repr(C)]
996pub struct AttributeNameValue {
997 pub attr_name: AzString,
998 pub value: AzString,
999}
1000
1001/// Strongly-typed HTML attribute with type-safe values.
1002///
1003/// This enum provides a type-safe way to represent HTML attributes, ensuring that
1004/// values are validated at compile-time and properly converted to their string
1005/// representations at runtime.
1006#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1007#[repr(C, u8)]
1008pub enum AttributeType {
1009 /// Element ID attribute (`id="..."`)
1010 Id(AzString),
1011 /// CSS class attribute (`class="..."`)
1012 Class(AzString),
1013 /// Accessible name/label (`aria-label="..."`)
1014 AriaLabel(AzString),
1015 /// Element that labels this one (`aria-labelledby="..."`)
1016 AriaLabelledBy(AzString),
1017 /// Element that describes this one (`aria-describedby="..."`)
1018 AriaDescribedBy(AzString),
1019 /// Role for accessibility (`role="..."`)
1020 AriaRole(AzString),
1021 /// Current state of an element (`aria-checked`, `aria-selected`, etc.)
1022 AriaState(AttributeNameValue),
1023 /// ARIA property (`aria-*`)
1024 AriaProperty(AttributeNameValue),
1025
1026 /// Hyperlink target URL (`href="..."`)
1027 Href(AzString),
1028 /// Link relationship (`rel="..."`)
1029 Rel(AzString),
1030 /// Link target frame (`target="..."`)
1031 Target(AzString),
1032
1033 /// Image source URL (`src="..."`)
1034 Src(AzString),
1035 /// Alternative text for images (`alt="..."`)
1036 Alt(AzString),
1037 /// Image title (tooltip) (`title="..."`)
1038 Title(AzString),
1039
1040 /// Form input name (`name="..."`)
1041 Name(AzString),
1042 /// Form input value (`value="..."`)
1043 Value(AzString),
1044 /// Input type (`type="text|password|email|..."`)
1045 InputType(AzString),
1046 /// Placeholder text (`placeholder="..."`)
1047 Placeholder(AzString),
1048 /// Input is required (`required`)
1049 Required,
1050 /// Input is disabled (`disabled`)
1051 Disabled,
1052 /// Input is readonly (`readonly`)
1053 Readonly,
1054 /// Input is checked (checkbox/radio) (`checked`)
1055 Checked,
1056 /// Input is selected (option) (`selected`)
1057 Selected,
1058 /// Maximum value for number inputs (`max="..."`)
1059 Max(AzString),
1060 /// Minimum value for number inputs (`min="..."`)
1061 Min(AzString),
1062 /// Step value for number inputs (`step="..."`)
1063 Step(AzString),
1064 /// Input pattern for validation (`pattern="..."`)
1065 Pattern(AzString),
1066 /// Minimum length (`minlength="..."`)
1067 MinLength(i32),
1068 /// Maximum length (`maxlength="..."`)
1069 MaxLength(i32),
1070 /// Autocomplete behavior (`autocomplete="on|off|..."`)
1071 Autocomplete(AzString),
1072
1073 /// Table header scope (`scope="row|col|rowgroup|colgroup"`)
1074 Scope(AzString),
1075 /// Number of columns to span (`colspan="..."`)
1076 ColSpan(i32),
1077 /// Number of rows to span (`rowspan="..."`)
1078 RowSpan(i32),
1079
1080 /// Tab index for keyboard navigation (`tabindex="..."`)
1081 TabIndex(i32),
1082 /// Element can receive focus (`tabindex="0"` equivalent)
1083 Focusable,
1084
1085 /// Language code (`lang="..."`)
1086 Lang(AzString),
1087 /// Text direction (`dir="ltr|rtl|auto"`)
1088 Dir(AzString),
1089
1090 /// Content is editable (`contenteditable="true|false"`)
1091 ContentEditable(bool),
1092 /// Element is draggable (`draggable="true|false"`)
1093 Draggable(bool),
1094 /// Element is hidden (`hidden`)
1095 Hidden,
1096
1097 /// Generic data attribute (`data-*="..."`)
1098 Data(AttributeNameValue),
1099 /// Generic custom attribute (for future extensibility)
1100 Custom(AttributeNameValue),
1101}
1102
1103impl_vec!(
1104 AttributeType,
1105 AttributeVec,
1106 AttributeVecDestructor,
1107 AttributeVecDestructorType
1108);
1109impl_vec_debug!(AttributeType, AttributeVec);
1110impl_vec_partialord!(AttributeType, AttributeVec);
1111impl_vec_ord!(AttributeType, AttributeVec);
1112impl_vec_clone!(AttributeType, AttributeVec, AttributeVecDestructor);
1113impl_vec_partialeq!(AttributeType, AttributeVec);
1114impl_vec_eq!(AttributeType, AttributeVec);
1115impl_vec_hash!(AttributeType, AttributeVec);
1116
1117impl AttributeType {
1118 /// Get the attribute name (e.g., "href", "aria-label", "data-foo")
1119 pub fn name(&self) -> &str {
1120 match self {
1121 AttributeType::Id(_) => "id",
1122 AttributeType::Class(_) => "class",
1123 AttributeType::AriaLabel(_) => "aria-label",
1124 AttributeType::AriaLabelledBy(_) => "aria-labelledby",
1125 AttributeType::AriaDescribedBy(_) => "aria-describedby",
1126 AttributeType::AriaRole(_) => "role",
1127 AttributeType::AriaState(nv) => nv.attr_name.as_str(),
1128 AttributeType::AriaProperty(nv) => nv.attr_name.as_str(),
1129 AttributeType::Href(_) => "href",
1130 AttributeType::Rel(_) => "rel",
1131 AttributeType::Target(_) => "target",
1132 AttributeType::Src(_) => "src",
1133 AttributeType::Alt(_) => "alt",
1134 AttributeType::Title(_) => "title",
1135 AttributeType::Name(_) => "name",
1136 AttributeType::Value(_) => "value",
1137 AttributeType::InputType(_) => "type",
1138 AttributeType::Placeholder(_) => "placeholder",
1139 AttributeType::Required => "required",
1140 AttributeType::Disabled => "disabled",
1141 AttributeType::Readonly => "readonly",
1142 AttributeType::Checked => "checked",
1143 AttributeType::Selected => "selected",
1144 AttributeType::Max(_) => "max",
1145 AttributeType::Min(_) => "min",
1146 AttributeType::Step(_) => "step",
1147 AttributeType::Pattern(_) => "pattern",
1148 AttributeType::MinLength(_) => "minlength",
1149 AttributeType::MaxLength(_) => "maxlength",
1150 AttributeType::Autocomplete(_) => "autocomplete",
1151 AttributeType::Scope(_) => "scope",
1152 AttributeType::ColSpan(_) => "colspan",
1153 AttributeType::RowSpan(_) => "rowspan",
1154 AttributeType::TabIndex(_) => "tabindex",
1155 AttributeType::Focusable => "tabindex",
1156 AttributeType::Lang(_) => "lang",
1157 AttributeType::Dir(_) => "dir",
1158 AttributeType::ContentEditable(_) => "contenteditable",
1159 AttributeType::Draggable(_) => "draggable",
1160 AttributeType::Hidden => "hidden",
1161 AttributeType::Data(nv) => nv.attr_name.as_str(),
1162 AttributeType::Custom(nv) => nv.attr_name.as_str(),
1163 }
1164 }
1165
1166 /// Get the attribute value as a string
1167 pub fn value(&self) -> AzString {
1168 match self {
1169 AttributeType::Id(v)
1170 | AttributeType::Class(v)
1171 | AttributeType::AriaLabel(v)
1172 | AttributeType::AriaLabelledBy(v)
1173 | AttributeType::AriaDescribedBy(v)
1174 | AttributeType::AriaRole(v)
1175 | AttributeType::Href(v)
1176 | AttributeType::Rel(v)
1177 | AttributeType::Target(v)
1178 | AttributeType::Src(v)
1179 | AttributeType::Alt(v)
1180 | AttributeType::Title(v)
1181 | AttributeType::Name(v)
1182 | AttributeType::Value(v)
1183 | AttributeType::InputType(v)
1184 | AttributeType::Placeholder(v)
1185 | AttributeType::Max(v)
1186 | AttributeType::Min(v)
1187 | AttributeType::Step(v)
1188 | AttributeType::Pattern(v)
1189 | AttributeType::Autocomplete(v)
1190 | AttributeType::Scope(v)
1191 | AttributeType::Lang(v)
1192 | AttributeType::Dir(v) => v.clone(),
1193
1194 AttributeType::AriaState(nv)
1195 | AttributeType::AriaProperty(nv)
1196 | AttributeType::Data(nv)
1197 | AttributeType::Custom(nv) => nv.value.clone(),
1198
1199 AttributeType::MinLength(n)
1200 | AttributeType::MaxLength(n)
1201 | AttributeType::ColSpan(n)
1202 | AttributeType::RowSpan(n)
1203 | AttributeType::TabIndex(n) => n.to_string().into(),
1204
1205 AttributeType::Focusable => "0".into(),
1206 AttributeType::ContentEditable(b) | AttributeType::Draggable(b) => {
1207 if *b {
1208 "true".into()
1209 } else {
1210 "false".into()
1211 }
1212 }
1213
1214 AttributeType::Required
1215 | AttributeType::Disabled
1216 | AttributeType::Readonly
1217 | AttributeType::Checked
1218 | AttributeType::Selected
1219 | AttributeType::Hidden => "".into(), // Boolean attributes
1220 }
1221 }
1222
1223 /// Check if this is a boolean attribute (present = true, absent = false)
1224 pub fn is_boolean(&self) -> bool {
1225 matches!(
1226 self,
1227 AttributeType::Required
1228 | AttributeType::Disabled
1229 | AttributeType::Readonly
1230 | AttributeType::Checked
1231 | AttributeType::Selected
1232 | AttributeType::Hidden
1233 )
1234 }
1235}
1236
1237/// Compact accessibility information for common use cases.
1238///
1239/// This is a lighter-weight alternative to `AccessibilityInfo` for cases where
1240/// only basic accessibility properties are needed. Developers must explicitly
1241/// pass `None` if they choose not to provide accessibility information.
1242#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1243#[repr(C)]
1244pub struct SmallAriaInfo {
1245 /// Accessible label/name
1246 pub label: OptionString,
1247 /// Element's role (button, link, etc.)
1248 pub role: OptionAccessibilityRole,
1249 /// Additional description
1250 pub description: OptionString,
1251}
1252
1253impl_option!(
1254 SmallAriaInfo,
1255 OptionSmallAriaInfo,
1256 copy = false,
1257 [Debug, Clone, PartialEq, Eq, Hash]
1258);
1259
1260impl SmallAriaInfo {
1261 pub fn label<S: Into<AzString>>(text: S) -> Self {
1262 Self {
1263 label: OptionString::Some(text.into()),
1264 role: OptionAccessibilityRole::None,
1265 description: OptionString::None,
1266 }
1267 }
1268
1269 pub fn with_role(mut self, role: AccessibilityRole) -> Self {
1270 self.role = OptionAccessibilityRole::Some(role);
1271 self
1272 }
1273
1274 pub fn with_description<S: Into<AzString>>(mut self, desc: S) -> Self {
1275 self.description = OptionString::Some(desc.into());
1276 self
1277 }
1278
1279 /// Convert to full `AccessibilityInfo`
1280 pub fn to_full_info(&self) -> AccessibilityInfo {
1281 AccessibilityInfo {
1282 accessibility_name: self.label.clone(),
1283 accessibility_value: OptionString::None,
1284 role: match self.role {
1285 OptionAccessibilityRole::Some(r) => r,
1286 OptionAccessibilityRole::None => AccessibilityRole::Unknown,
1287 },
1288 states: Vec::new().into(),
1289 accelerator: OptionVirtualKeyCodeCombo::None,
1290 default_action: OptionString::None,
1291 supported_actions: Vec::new().into(),
1292 is_live_region: false,
1293 labelled_by: OptionDomNodeId::None,
1294 described_by: OptionDomNodeId::None,
1295 }
1296 }
1297}
1298
1299/// Represents all data associated with a single DOM node, such as its type,
1300/// classes, IDs, callbacks, and inline styles.
1301#[repr(C)]
1302#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
1303pub struct NodeData {
1304 /// `div`, `p`, `img`, etc.
1305 pub node_type: NodeType,
1306 /// `data-*` attributes for this node, useful to store UI-related data on the node itself.
1307 pub dataset: OptionRefAny,
1308 /// Stores all ids and classes as one vec - size optimization since
1309 /// most nodes don't have any classes or IDs.
1310 pub ids_and_classes: IdOrClassVec,
1311 /// Strongly-typed HTML attributes (aria-*, href, alt, etc.)
1312 pub attributes: AttributeVec,
1313 /// Callbacks attached to this node:
1314 ///
1315 /// `On::MouseUp` -> `Callback(my_button_click_handler)`
1316 pub callbacks: CoreCallbackDataVec,
1317 /// Conditional CSS properties with dynamic selectors.
1318 /// These are evaluated at runtime based on OS, viewport, container, theme, and pseudo-state.
1319 /// Uses "last wins" semantics - properties are evaluated in order, last match wins.
1320 pub css_props: CssPropertyWithConditionsVec,
1321 /// Tab index (commonly used property).
1322 pub tab_index: OptionTabIndex,
1323 /// Whether this node is contenteditable (accepts text input).
1324 /// Equivalent to HTML `contenteditable="true"` attribute.
1325 pub contenteditable: bool,
1326 /// Stores "extra", not commonly used data of the node: accessibility, clip-mask, tab-index,
1327 /// etc.
1328 ///
1329 /// SHOULD NOT EXPOSED IN THE API - necessary to retroactively add functionality
1330 /// to the node without breaking the ABI.
1331 extra: Option<Box<NodeDataExt>>,
1332}
1333
1334impl Hash for NodeData {
1335 fn hash<H: Hasher>(&self, state: &mut H) {
1336 self.node_type.hash(state);
1337 self.dataset.hash(state);
1338 self.ids_and_classes.as_ref().hash(state);
1339 self.attributes.as_ref().hash(state);
1340 self.contenteditable.hash(state);
1341
1342 // NOTE: callbacks are NOT hashed regularly, otherwise
1343 // they'd cause inconsistencies because of the scroll callback
1344 for callback in self.callbacks.as_ref().iter() {
1345 callback.event.hash(state);
1346 callback.callback.hash(state);
1347 callback.refany.get_type_id().hash(state);
1348 }
1349
1350 // Hash CSS props (conditional CSS with dynamic selectors)
1351 for prop in self.css_props.as_ref().iter() {
1352 // Hash property type as a simple discriminant
1353 core::mem::discriminant(&prop.property).hash(state);
1354 }
1355 if let Some(ext) = self.extra.as_ref() {
1356 if let Some(c) = ext.clip_mask.as_ref() {
1357 c.hash(state);
1358 }
1359 // Note: AccessibilityInfo doesn't implement Hash (has non-hashable fields)
1360 // Skipping accessibility field in hash
1361 if let Some(c) = ext.menu_bar.as_ref() {
1362 c.hash(state);
1363 }
1364 if let Some(c) = ext.context_menu.as_ref() {
1365 c.hash(state);
1366 }
1367 }
1368 }
1369}
1370
1371/// NOTE: NOT EXPOSED IN THE API! Stores extra,
1372/// not commonly used information for the NodeData.
1373/// This helps keep the primary `NodeData` struct smaller for common cases.
1374#[repr(C)]
1375#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1376pub struct NodeDataExt {
1377 /// Optional clip mask for this DOM node.
1378 pub clip_mask: Option<ImageMask>,
1379 /// Optional extra accessibility information about this DOM node (MSAA, AT-SPI, UA).
1380 pub accessibility: Option<Box<AccessibilityInfo>>,
1381 /// Menu bar that should be displayed at the top of this nodes rect.
1382 pub menu_bar: Option<Box<Menu>>,
1383 /// Context menu that should be opened when the item is left-clicked.
1384 pub context_menu: Option<Box<Menu>>,
1385 /// Whether this node is an anonymous box (generated for table layout).
1386 /// Anonymous boxes are not part of the original DOM tree and are created
1387 /// by the layout engine to satisfy table layout requirements (e.g., wrapping
1388 /// non-table children of table elements in anonymous table-row/table-cell boxes).
1389 pub is_anonymous: bool,
1390 /// Stable key for reconciliation. If provided, allows the framework to track
1391 /// this node across frames even if its position in the array changes.
1392 /// This is crucial for correct lifecycle events when lists are reordered.
1393 pub key: Option<u64>,
1394 /// Callback to merge dataset state from a previous frame's node into the current node.
1395 /// This enables heavy resource preservation (video decoders, GL textures) across frames.
1396 pub dataset_merge_callback: Option<DatasetMergeCallback>,
1397 // ... insert further API extensions here...
1398}
1399
1400/// A callback function used to merge the state of an old dataset into a new one.
1401///
1402/// This enables components with heavy internal state (video players, WebGL contexts)
1403/// to preserve their resources across frames, while the DOM tree is recreated.
1404///
1405/// The callback receives both the old and new datasets as `RefAny` (cheap shallow clones)
1406/// and returns the dataset that should be used for the new node.
1407///
1408/// # Example
1409///
1410/// ```rust,ignore
1411/// fn merge_video_state(new_data: RefAny, old_data: RefAny) -> RefAny {
1412/// // Transfer heavy resources from old to new
1413/// if let (Some(mut new), Some(old)) = (
1414/// new_data.downcast_mut::<VideoState>(),
1415/// old_data.downcast_ref::<VideoState>()
1416/// ) {
1417/// new.decoder = old.decoder.take();
1418/// new.gl_texture = old.gl_texture.take();
1419/// }
1420/// new_data // Return the merged state
1421/// }
1422/// ```
1423#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1424#[repr(C)]
1425pub struct DatasetMergeCallback {
1426 /// The function pointer that performs the merge.
1427 /// Signature: `fn(new_data: RefAny, old_data: RefAny) -> RefAny`
1428 pub cb: DatasetMergeCallbackType,
1429 /// Optional callable for FFI language bindings (Python, etc.)
1430 /// When set, the FFI layer can invoke this instead of `cb`.
1431 pub callable: OptionRefAny,
1432}
1433
1434impl core::fmt::Debug for DatasetMergeCallback {
1435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1436 f.debug_struct("DatasetMergeCallback")
1437 .field("cb", &(self.cb as usize))
1438 .field("callable", &self.callable)
1439 .finish()
1440 }
1441}
1442
1443/// Allow creating DatasetMergeCallback from a raw function pointer.
1444/// This enables the `Into<DatasetMergeCallback>` pattern for Python bindings.
1445impl From<DatasetMergeCallbackType> for DatasetMergeCallback {
1446 fn from(cb: DatasetMergeCallbackType) -> Self {
1447 DatasetMergeCallback {
1448 cb,
1449 callable: OptionRefAny::None,
1450 }
1451 }
1452}
1453
1454impl_option!(
1455 DatasetMergeCallback,
1456 OptionDatasetMergeCallback,
1457 copy = false,
1458 [Debug, Clone]
1459);
1460
1461/// Function pointer type for dataset merge callbacks.
1462///
1463/// Arguments:
1464/// - `new_data`: The new node's dataset (shallow clone, cheap)
1465/// - `old_data`: The old node's dataset (shallow clone, cheap)
1466///
1467/// Returns:
1468/// - The `RefAny` that should be used as the dataset for the new node
1469pub type DatasetMergeCallbackType = extern "C" fn(RefAny, RefAny) -> RefAny;
1470
1471/// Holds information about a UI element for accessibility purposes (e.g., screen readers).
1472/// This is a wrapper for platform-specific accessibility APIs like MSAA.
1473#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1474#[repr(C)]
1475pub struct AccessibilityInfo {
1476 /// Get the "name" of the `IAccessible`, for example the
1477 /// name of a button, checkbox or menu item. Try to use unique names
1478 /// for each item in a dialog so that voice dictation software doesn't
1479 /// have to deal with extra ambiguity.
1480 pub accessibility_name: OptionString,
1481 /// Get the "value" of the `IAccessible`, for example a number in a slider,
1482 /// a URL for a link, the text a user entered in a field.
1483 pub accessibility_value: OptionString,
1484 /// Get an enumerated value representing what this IAccessible is used for,
1485 /// for example is it a link, static text, editable text, a checkbox, or a table cell, etc.
1486 pub role: AccessibilityRole,
1487 /// Possible on/off states, such as focused, focusable, selected, selectable,
1488 /// visible, protected (for passwords), checked, etc.
1489 pub states: AccessibilityStateVec,
1490 /// Optional keyboard accelerator.
1491 pub accelerator: OptionVirtualKeyCodeCombo,
1492 /// Optional "default action" description. Only used when there is at least
1493 /// one `ComponentEventFilter::DefaultAction` callback present on this node.
1494 pub default_action: OptionString,
1495 /// A list of actions the user can perform on this element.
1496 /// Maps to accesskit's Action enum.
1497 pub supported_actions: AccessibilityActionVec,
1498 /// For live regions that update automatically (e.g., chat messages, timers).
1499 /// Maps to accesskit's `Live` property.
1500 pub is_live_region: bool,
1501 /// ID of another node that labels this one (for `aria-labelledby`).
1502 pub labelled_by: OptionDomNodeId,
1503 /// ID of another node that describes this one (for `aria-describedby`).
1504 pub described_by: OptionDomNodeId,
1505}
1506
1507/// Actions that can be performed on an accessible element.
1508/// This is a simplified version of accesskit::Action to avoid direct dependency in core.
1509#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1510#[repr(C, u8)]
1511pub enum AccessibilityAction {
1512 /// The default action for the element (usually a click).
1513 Default,
1514 /// Set focus to this element.
1515 Focus,
1516 /// Remove focus from this element.
1517 Blur,
1518 /// Collapse an expandable element (e.g., tree node, accordion).
1519 Collapse,
1520 /// Expand a collapsible element (e.g., tree node, accordion).
1521 Expand,
1522 /// Scroll this element into view.
1523 ScrollIntoView,
1524 /// Increment a numeric value (e.g., slider, spinner).
1525 Increment,
1526 /// Decrement a numeric value (e.g., slider, spinner).
1527 Decrement,
1528 /// Show a context menu.
1529 ShowContextMenu,
1530 /// Hide a tooltip.
1531 HideTooltip,
1532 /// Show a tooltip.
1533 ShowTooltip,
1534 /// Scroll up.
1535 ScrollUp,
1536 /// Scroll down.
1537 ScrollDown,
1538 /// Scroll left.
1539 ScrollLeft,
1540 /// Scroll right.
1541 ScrollRight,
1542 /// Replace selected text with new text.
1543 ReplaceSelectedText(AzString),
1544 /// Scroll to a specific point.
1545 ScrollToPoint(LogicalPosition),
1546 /// Set scroll offset.
1547 SetScrollOffset(LogicalPosition),
1548 /// Set text selection.
1549 SetTextSelection(TextSelectionStartEnd),
1550 /// Set sequential focus navigation starting point.
1551 SetSequentialFocusNavigationStartingPoint,
1552 /// Set the value of a control.
1553 SetValue(AzString),
1554 /// Set numeric value of a control.
1555 SetNumericValue(FloatValue),
1556 /// Custom action with ID.
1557 CustomAction(i32),
1558}
1559
1560#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1561#[repr(C)]
1562pub struct TextSelectionStartEnd {
1563 pub selection_start: usize,
1564 pub selection_end: usize,
1565}
1566
1567impl_vec![
1568 AccessibilityAction,
1569 AccessibilityActionVec,
1570 AccessibilityActionVecDestructor,
1571 AccessibilityActionVecDestructorType
1572];
1573impl_vec_debug!(AccessibilityAction, AccessibilityActionVec);
1574impl_vec_clone!(
1575 AccessibilityAction,
1576 AccessibilityActionVec,
1577 AccessibilityActionVecDestructor
1578);
1579impl_vec_partialeq!(AccessibilityAction, AccessibilityActionVec);
1580impl_vec_eq!(AccessibilityAction, AccessibilityActionVec);
1581impl_vec_partialord!(AccessibilityAction, AccessibilityActionVec);
1582impl_vec_ord!(AccessibilityAction, AccessibilityActionVec);
1583impl_vec_hash!(AccessibilityAction, AccessibilityActionVec);
1584
1585impl_option![
1586 AccessibilityAction,
1587 OptionAccessibilityAction,
1588 copy = false,
1589 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
1590];
1591
1592impl_option!(
1593 AccessibilityInfo,
1594 OptionAccessibilityInfo,
1595 copy = false,
1596 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
1597);
1598
1599/// Defines the element's purpose for accessibility APIs, informing assistive technologies
1600/// like screen readers about the function of a UI element. Each variant corresponds to a
1601/// standard control type or UI structure.
1602///
1603/// For more details, see the [MSDN Role Constants page](https://docs.microsoft.com/en-us/windows/winauto/object-roles).
1604#[repr(C)]
1605#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1606pub enum AccessibilityRole {
1607 /// Represents the title or caption bar of a window.
1608 /// - **Purpose**: To identify the title bar containing the window title and system commands.
1609 /// - **When to use**: This role is typically inserted by the operating system for standard
1610 /// windows.
1611 /// - **Example**: The bar at the top of an application window displaying its name and the
1612 /// minimize, maximize, and close buttons.
1613 TitleBar,
1614
1615 /// Represents a menu bar at the top of a window.
1616 /// - **Purpose**: To contain a set of top-level menus for an application.
1617 /// - **When to use**: For the main menu bar of an application, such as one containing "File,"
1618 /// "Edit," and "View."
1619 /// - **Example**: The "File", "Edit", "View" menu bar at the top of a text editor.
1620 MenuBar,
1621
1622 /// Represents a vertical or horizontal scroll bar.
1623 /// - **Purpose**: To enable scrolling through content that is larger than the visible area.
1624 /// - **When to use**: For any scrollable region of content.
1625 /// - **Example**: The bar on the side of a web page that allows the user to scroll up and
1626 /// down.
1627 ScrollBar,
1628
1629 /// Represents a handle or grip used for moving or resizing.
1630 /// - **Purpose**: To provide a user interface element for manipulating another element's size
1631 /// or position.
1632 /// - **When to use**: For handles that allow resizing of windows, panes, or other objects.
1633 /// - **Example**: The small textured area in the bottom-right corner of a window that can be
1634 /// dragged to resize it.
1635 Grip,
1636
1637 /// Represents a system sound indicating an event.
1638 /// - **Purpose**: To associate a sound with a UI event, providing an auditory cue.
1639 /// - **When to use**: When a sound is the primary representation of an event.
1640 /// - **Example**: A system notification sound that plays when a new message arrives.
1641 Sound,
1642
1643 /// Represents the system's mouse pointer or other pointing device.
1644 /// - **Purpose**: To indicate the screen position of the user's pointing device.
1645 /// - **When to use**: This role is managed by the operating system.
1646 /// - **Example**: The arrow that moves on the screen as you move the mouse.
1647 Cursor,
1648
1649 /// Represents the text insertion point indicator.
1650 /// - **Purpose**: To show the current text entry or editing position.
1651 /// - **When to use**: This role is typically managed by the operating system for text input
1652 /// fields.
1653 /// - **Example**: The blinking vertical line in a text box that shows where the next character
1654 /// will be typed.
1655 Caret,
1656
1657 /// Represents an alert or notification.
1658 /// - **Purpose**: To convey an important, non-modal message to the user.
1659 /// - **When to use**: For non-intrusive notifications that do not require immediate user
1660 /// interaction.
1661 /// - **Example**: A small, temporary "toast" notification that appears to confirm an action,
1662 /// like "Email sent."
1663 Alert,
1664
1665 /// Represents a window frame.
1666 /// - **Purpose**: To serve as the container for other objects like a title bar and client
1667 /// area.
1668 /// - **When to use**: This is a fundamental role, typically managed by the windowing system.
1669 /// - **Example**: The main window of any application, which contains all other UI elements.
1670 Window,
1671
1672 /// Represents a window's client area, where the main content is displayed.
1673 /// - **Purpose**: To define the primary content area of a window.
1674 /// - **When to use**: For the main content region of a window. It's often the default role for
1675 /// a custom control container.
1676 /// - **Example**: The area of a web browser where the web page content is rendered.
1677 Client,
1678
1679 /// Represents a pop-up menu.
1680 /// - **Purpose**: To display a list of `MenuItem` objects that appears when a user performs an
1681 /// action.
1682 /// - **When to use**: For context menus (right-click menus) or drop-down menus.
1683 /// - **Example**: The menu that appears when you right-click on a file in a file explorer.
1684 MenuPopup,
1685
1686 /// Represents an individual item within a menu.
1687 /// - **Purpose**: To represent a single command, option, or separator within a menu.
1688 /// - **When to use**: For individual options inside a `MenuBar` or `MenuPopup`.
1689 /// - **Example**: The "Save" option within the "File" menu.
1690 MenuItem,
1691
1692 /// Represents a small pop-up window that provides information.
1693 /// - **Purpose**: To offer brief, contextual help or information about a UI element.
1694 /// - **When to use**: For informational pop-ups that appear on mouse hover.
1695 /// - **Example**: The small box of text that appears when you hover over a button in a
1696 /// toolbar.
1697 Tooltip,
1698
1699 /// Represents the main window of an application.
1700 /// - **Purpose**: To identify the top-level window of an application.
1701 /// - **When to use**: For the primary window that represents the application itself.
1702 /// - **Example**: The main window of a calculator or notepad application.
1703 Application,
1704
1705 /// Represents a document window within an application.
1706 /// - **Purpose**: To represent a contained document, typically in a Multiple Document
1707 /// Interface (MDI) application.
1708 /// - **When to use**: For individual document windows inside a larger application shell.
1709 /// - **Example**: In a photo editor that allows multiple images to be open in separate
1710 /// windows, each image window would be a `Document`.
1711 Document,
1712
1713 /// Represents a pane or a distinct section of a window.
1714 /// - **Purpose**: To divide a window into visually and functionally distinct areas.
1715 /// - **When to use**: For sub-regions of a window, like a navigation pane, preview pane, or
1716 /// sidebar.
1717 /// - **Example**: The preview pane in an email client that shows the content of the selected
1718 /// email.
1719 Pane,
1720
1721 /// Represents a graphical chart or graph.
1722 /// - **Purpose**: To display data visually in a chart format.
1723 /// - **When to use**: For any type of chart, such as a bar chart, line chart, or pie chart.
1724 /// - **Example**: A bar chart displaying monthly sales figures.
1725 Chart,
1726
1727 /// Represents a dialog box or message box.
1728 /// - **Purpose**: To create a secondary window that requires user interaction before returning
1729 /// to the main application.
1730 /// - **When to use**: For modal or non-modal windows that prompt the user for information or a
1731 /// response.
1732 /// - **Example**: The "Open File" or "Print" dialog in most applications.
1733 Dialog,
1734
1735 /// Represents a window's border.
1736 /// - **Purpose**: To identify the border of a window, which is often used for resizing.
1737 /// - **When to use**: This role is typically managed by the windowing system.
1738 /// - **Example**: The decorative and functional frame around a window.
1739 Border,
1740
1741 /// Represents a group of related controls.
1742 /// - **Purpose**: To logically group other objects that share a common purpose.
1743 /// - **When to use**: For grouping controls like a set of radio buttons or a fieldset with a
1744 /// legend.
1745 /// - **Example**: A "Settings" group box in a dialog that contains several related checkboxes.
1746 Grouping,
1747
1748 /// Represents a visual separator.
1749 /// - **Purpose**: To visually divide a space or a group of controls.
1750 /// - **When to use**: For visual separators in menus, toolbars, or between panes.
1751 /// - **Example**: The horizontal line in a menu that separates groups of related menu items.
1752 Separator,
1753
1754 /// Represents a toolbar containing a group of controls.
1755 /// - **Purpose**: To group controls, typically buttons, for quick access to frequently used
1756 /// functions.
1757 /// - **When to use**: For a bar of buttons or other controls, usually at the top of a window
1758 /// or pane.
1759 /// - **Example**: The toolbar at the top of a word processor with buttons for "Bold,"
1760 /// "Italic," and "Underline."
1761 Toolbar,
1762
1763 /// Represents a status bar for displaying information.
1764 /// - **Purpose**: To display status information about the current state of the application.
1765 /// - **When to use**: For a bar, typically at the bottom of a window, that displays messages.
1766 /// - **Example**: The bar at the bottom of a web browser that shows the loading status of a
1767 /// page.
1768 StatusBar,
1769
1770 /// Represents a data table.
1771 /// - **Purpose**: To present data in a two-dimensional grid of rows and columns.
1772 /// - **When to use**: For grid-like data presentation.
1773 /// - **Example**: A spreadsheet or a table of data in a database application.
1774 Table,
1775
1776 /// Represents a column header in a table.
1777 /// - **Purpose**: To provide a label for a column of data.
1778 /// - **When to use**: For the headers of columns in a `Table`.
1779 /// - **Example**: The header row in a spreadsheet with labels like "Name," "Date," and
1780 /// "Amount."
1781 ColumnHeader,
1782
1783 /// Represents a row header in a table.
1784 /// - **Purpose**: To provide a label for a row of data.
1785 /// - **When to use**: For the headers of rows in a `Table`.
1786 /// - **Example**: The numbered rows on the left side of a spreadsheet.
1787 RowHeader,
1788
1789 /// Represents a full column of cells in a table.
1790 /// - **Purpose**: To represent an entire column as a single accessible object.
1791 /// - **When to use**: When it is useful to interact with a column as a whole.
1792 /// - **Example**: The "Amount" column in a financial data table.
1793 Column,
1794
1795 /// Represents a full row of cells in a table.
1796 /// - **Purpose**: To represent an entire row as a single accessible object.
1797 /// - **When to use**: When it is useful to interact with a row as a whole.
1798 /// - **Example**: A row representing a single customer's information in a customer list.
1799 Row,
1800
1801 /// Represents a single cell within a table.
1802 /// - **Purpose**: To represent a single data point or control within a `Table`.
1803 /// - **When to use**: For individual cells in a grid or table.
1804 /// - **Example**: A single cell in a spreadsheet containing a specific value.
1805 Cell,
1806
1807 /// Represents a hyperlink to a resource.
1808 /// - **Purpose**: To provide a navigational link to another document or location.
1809 /// - **When to use**: For text or images that, when clicked, navigate to another resource.
1810 /// - **Example**: A clickable link on a web page.
1811 Link,
1812
1813 /// Represents a help balloon or pop-up.
1814 /// - **Purpose**: To provide more detailed help information than a standard tooltip.
1815 /// - **When to use**: For a pop-up that offers extended help text, often initiated by a help
1816 /// button.
1817 /// - **Example**: A pop-up balloon with a paragraph of help text that appears when a user
1818 /// clicks a help icon.
1819 HelpBalloon,
1820
1821 /// Represents an animated, character-like graphic object.
1822 /// - **Purpose**: To provide an animated agent for user assistance or entertainment.
1823 /// - **When to use**: For animated characters or avatars that provide help or guidance.
1824 /// - **Example**: An animated paperclip that offers tips in a word processor (e.g.,
1825 /// Microsoft's Clippy).
1826 Character,
1827
1828 /// Represents a list of items.
1829 /// - **Purpose**: To contain a set of `ListItem` objects.
1830 /// - **When to use**: For list boxes or similar controls that present a list of selectable
1831 /// items.
1832 /// - **Example**: The list of files in a file selection dialog.
1833 List,
1834
1835 /// Represents an individual item within a list.
1836 /// - **Purpose**: To represent a single, selectable item within a `List`.
1837 /// - **When to use**: For each individual item in a list box or combo box.
1838 /// - **Example**: A single file name in a list of files.
1839 ListItem,
1840
1841 /// Represents an outline or tree structure.
1842 /// - **Purpose**: To display a hierarchical view of data.
1843 /// - **When to use**: For tree-view controls that show nested items.
1844 /// - **Example**: A file explorer's folder tree view.
1845 Outline,
1846
1847 /// Represents an individual item within an outline or tree.
1848 /// - **Purpose**: To represent a single node (which can be a leaf or a branch) in an
1849 /// `Outline`.
1850 /// - **When to use**: For each node in a tree view.
1851 /// - **Example**: A single folder in a file explorer's tree view.
1852 OutlineItem,
1853
1854 /// Represents a single tab in a tabbed interface.
1855 /// - **Purpose**: To provide a control for switching between different `PropertyPage` views.
1856 /// - **When to use**: For the individual tabs that the user can click to switch pages.
1857 /// - **Example**: The "General" and "Security" tabs in a file properties dialog.
1858 PageTab,
1859
1860 /// Represents the content of a page in a property sheet.
1861 /// - **Purpose**: To serve as a container for the controls displayed when a `PageTab` is
1862 /// selected.
1863 /// - **When to use**: For the content area associated with a specific tab.
1864 /// - **Example**: The set of options displayed when the "Security" tab is active.
1865 PropertyPage,
1866
1867 /// Represents a visual indicator, like a slider thumb.
1868 /// - **Purpose**: To visually indicate the current value or position of another control.
1869 /// - **When to use**: For a sub-element that indicates status, like the thumb of a scrollbar.
1870 /// - **Example**: The draggable thumb of a scrollbar that indicates the current scroll
1871 /// position.
1872 Indicator,
1873
1874 /// Represents a picture or graphical image.
1875 /// - **Purpose**: To display a non-interactive image.
1876 /// - **When to use**: For images and icons that are purely decorative or informational.
1877 /// - **Example**: A company logo displayed in an application's "About" dialog.
1878 Graphic,
1879
1880 /// Represents read-only text.
1881 /// - **Purpose**: To provide a non-editable text label for another control or for displaying
1882 /// information.
1883 /// - **When to use**: For text that the user cannot edit.
1884 /// - **Example**: The label "Username:" next to a text input field.
1885 StaticText,
1886
1887 /// Represents editable text or a text area.
1888 /// - **Purpose**: To allow for user text input or selection.
1889 /// - **When to use**: For text input fields where the user can type.
1890 /// - **Example**: A text box for entering a username or password.
1891 Text,
1892
1893 /// Represents a standard push button.
1894 /// - **Purpose**: To initiate an immediate action.
1895 /// - **When to use**: For standard buttons that perform an action when clicked.
1896 /// - **Example**: An "OK" or "Cancel" button in a dialog.
1897 PushButton,
1898
1899 /// Represents a check box control.
1900 /// - **Purpose**: To allow the user to make a binary choice (checked or unchecked).
1901 /// - **When to use**: For options that can be toggled on or off independently.
1902 /// - **Example**: A "Remember me" checkbox on a login form.
1903 CheckButton,
1904
1905 /// Represents a radio button.
1906 /// - **Purpose**: To allow the user to select one option from a mutually exclusive group.
1907 /// - **When to use**: For a choice where only one option from a `Grouping` can be selected.
1908 /// - **Example**: "Male" and "Female" radio buttons for selecting gender.
1909 RadioButton,
1910
1911 /// Represents a combination of a text field and a drop-down list.
1912 /// - **Purpose**: To allow the user to either type a value or select one from a list.
1913 /// - **When to use**: For controls that offer a list of suggestions but also allow custom
1914 /// input.
1915 /// - **Example**: A font selector that allows you to type a font name or choose one from a
1916 /// list.
1917 ComboBox,
1918
1919 /// Represents a drop-down list box.
1920 /// - **Purpose**: To allow the user to select an item from a non-editable list that drops
1921 /// down.
1922 /// - **When to use**: For selecting a single item from a predefined list of options.
1923 /// - **Example**: A country selection drop-down menu.
1924 DropList,
1925
1926 /// Represents a progress bar.
1927 /// - **Purpose**: To indicate the progress of a lengthy operation.
1928 /// - **When to use**: To provide feedback for tasks like file downloads or installations.
1929 /// - **Example**: The bar that fills up to show the progress of a file copy operation.
1930 ProgressBar,
1931
1932 /// Represents a dial or knob.
1933 /// - **Purpose**: To allow selecting a value from a continuous or discrete range, often
1934 /// circularly.
1935 /// - **When to use**: For controls that resemble real-world dials, like a volume knob.
1936 /// - **Example**: A volume control knob in a media player application.
1937 Dial,
1938
1939 /// Represents a control for entering a keyboard shortcut.
1940 /// - **Purpose**: To capture a key combination from the user.
1941 /// - **When to use**: In settings where users can define their own keyboard shortcuts.
1942 /// - **Example**: A text field in a settings dialog where a user can press a key combination
1943 /// to assign it to a command.
1944 HotkeyField,
1945
1946 /// Represents a slider for selecting a value within a range.
1947 /// - **Purpose**: To allow the user to adjust a setting along a continuous or discrete range.
1948 /// - **When to use**: For adjusting values like volume, brightness, or zoom level.
1949 /// - **Example**: A slider to control the volume of a video.
1950 Slider,
1951
1952 /// Represents a spin button (up/down arrows) for incrementing or decrementing a value.
1953 /// - **Purpose**: To provide fine-tuned adjustment of a value, typically numeric.
1954 /// - **When to use**: For controls that allow stepping through a range of values.
1955 /// - **Example**: The up and down arrows next to a number input for setting the font size.
1956 SpinButton,
1957
1958 /// Represents a diagram or flowchart.
1959 /// - **Purpose**: To represent data or relationships in a schematic form.
1960 /// - **When to use**: For visual representations of structures that are not charts, like a
1961 /// database schema diagram.
1962 /// - **Example**: A flowchart illustrating a business process.
1963 Diagram,
1964
1965 /// Represents an animation control.
1966 /// - **Purpose**: To display a sequence of images or indicate an ongoing process.
1967 /// - **When to use**: For animations that show that an operation is in progress.
1968 /// - **Example**: The animation that plays while files are being copied.
1969 Animation,
1970
1971 /// Represents a mathematical equation.
1972 /// - **Purpose**: To display a mathematical formula in the correct format.
1973 /// - **When to use**: For displaying mathematical equations.
1974 /// - **Example**: A rendered mathematical equation in a scientific document editor.
1975 Equation,
1976
1977 /// Represents a button that drops down a list of items.
1978 /// - **Purpose**: To combine a default action button with a list of alternative actions.
1979 /// - **When to use**: For buttons that have a primary action and a secondary list of options.
1980 /// - **Example**: A "Send" button with a dropdown arrow that reveals "Send and Archive."
1981 ButtonDropdown,
1982
1983 /// Represents a button that drops down a full menu.
1984 /// - **Purpose**: To provide a button that opens a menu of choices rather than performing a
1985 /// single action.
1986 /// - **When to use**: When a button's primary purpose is to reveal a menu.
1987 /// - **Example**: A "Tools" button that opens a menu with various tool options.
1988 ButtonMenu,
1989
1990 /// Represents a button that drops down a grid for selection.
1991 /// - **Purpose**: To allow selection from a two-dimensional grid of options.
1992 /// - **When to use**: For buttons that open a grid-based selection UI.
1993 /// - **Example**: A color picker button that opens a grid of color swatches.
1994 ButtonDropdownGrid,
1995
1996 /// Represents blank space between other objects.
1997 /// - **Purpose**: To represent significant empty areas in a UI that are part of the layout.
1998 /// - **When to use**: Sparingly, to signify that a large area is intentionally blank.
1999 /// - **Example**: A large empty panel in a complex layout might use this role.
2000 Whitespace,
2001
2002 /// Represents the container for a set of tabs.
2003 /// - **Purpose**: To group a set of `PageTab` elements.
2004 /// - **When to use**: To act as the parent container for a row or column of tabs.
2005 /// - **Example**: The entire row of tabs at the top of a properties dialog.
2006 PageTabList,
2007
2008 /// Represents a clock control.
2009 /// - **Purpose**: To display the current time.
2010 /// - **When to use**: For any UI element that displays time.
2011 /// - **Example**: The clock in the system tray of the operating system.
2012 Clock,
2013
2014 /// Represents a button with two parts: a default action and a dropdown.
2015 /// - **Purpose**: To combine a frequently used action with a set of related, less-used
2016 /// actions.
2017 /// - **When to use**: When a button has a default action and other related actions available
2018 /// in a dropdown.
2019 /// - **Example**: A "Save" split button where the primary part saves, and the dropdown offers
2020 /// "Save As."
2021 SplitButton,
2022
2023 /// Represents a control for entering an IP address.
2024 /// - **Purpose**: To provide a specialized input field for IP addresses, often with formatting
2025 /// and validation.
2026 /// - **When to use**: For dedicated IP address input fields.
2027 /// - **Example**: A network configuration dialog with a field for entering a static IP
2028 /// address.
2029 IpAddress,
2030
2031 /// Represents an element with no specific role.
2032 /// - **Purpose**: To indicate an element that has no semantic meaning for accessibility.
2033 /// - **When to use**: Should be used sparingly for purely decorative elements that should be
2034 /// ignored by assistive technologies.
2035 /// - **Example**: A decorative graphical flourish that has no function or information to
2036 /// convey.
2037 Nothing,
2038
2039 /// Unknown or unspecified role.
2040 /// - **Purpose**: Default fallback when no specific role is assigned.
2041 /// - **When to use**: As a default value or when role information is unavailable.
2042 Unknown,
2043}
2044
2045impl_option!(
2046 AccessibilityRole,
2047 OptionAccessibilityRole,
2048 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
2049);
2050
2051/// Defines the current state of an element for accessibility APIs (e.g., focused, checked).
2052/// These states provide dynamic information to assistive technologies about the element's
2053/// condition.
2054///
2055/// See the [MSDN State Constants page](https://docs.microsoft.com/en-us/windows/win32/winauto/object-state-constants) for more details.
2056#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2057#[repr(C)]
2058pub enum AccessibilityState {
2059 /// The element is unavailable and cannot be interacted with.
2060 /// - **Purpose**: To indicate that a control is disabled or grayed out.
2061 /// - **When to use**: For disabled buttons, non-interactive menu items, or any control that is
2062 /// temporarily non-functional.
2063 /// - **Example**: A "Save" button that is disabled until the user makes changes to a document.
2064 Unavailable,
2065
2066 /// The element is selected.
2067 /// - **Purpose**: To indicate that an item is currently chosen or highlighted. This is
2068 /// distinct from having focus.
2069 /// - **When to use**: For selected items in a list, highlighted text, or the currently active
2070 /// tab in a tab list.
2071 /// - **Example**: A file highlighted in a file explorer, or multiple selected emails in an
2072 /// inbox.
2073 Selected,
2074
2075 /// The element has the keyboard focus.
2076 /// - **Purpose**: To identify the single element that will receive keyboard input.
2077 /// - **When to use**: For the control that is currently active and ready to be manipulated by
2078 /// the keyboard.
2079 /// - **Example**: A text box with a blinking cursor, or a button with a dotted outline around
2080 /// it.
2081 Focused,
2082
2083 /// The element is checked, toggled, or in a mixed state.
2084 /// - **Purpose**: To represent the state of controls like checkboxes, radio buttons, and
2085 /// toggle buttons.
2086 /// - **When to use**: For checkboxes that are ticked, selected radio buttons, or toggle
2087 /// buttons that are "on."
2088 /// - **Example**: A checked "I agree" checkbox, a selected "Yes" radio button, or an active
2089 /// "Bold" button in a toolbar.
2090 Checked,
2091
2092 /// The element's content cannot be edited by the user.
2093 /// - **Purpose**: To indicate that the element's value can be viewed and copied, but not
2094 /// modified.
2095 /// - **When to use**: For display-only text fields or documents.
2096 /// - **Example**: A text box displaying a license agreement that the user can scroll through
2097 /// but cannot edit.
2098 Readonly,
2099
2100 /// The element is the default action in a dialog or form.
2101 /// - **Purpose**: To identify the button that will be activated if the user presses the Enter
2102 /// key.
2103 /// - **When to use**: For the primary confirmation button in a dialog.
2104 /// - **Example**: The "OK" button in a dialog box, which often has a thicker or colored
2105 /// border.
2106 Default,
2107
2108 /// The element is expanded, showing its child items.
2109 /// - **Purpose**: To indicate that a collapsible element is currently open and its contents
2110 /// are visible.
2111 /// - **When to use**: For tree view nodes, combo boxes with their lists open, or expanded
2112 /// accordion panels.
2113 /// - **Example**: A folder in a file explorer's tree view that has been clicked to show its
2114 /// subfolders.
2115 Expanded,
2116
2117 /// The element is collapsed, hiding its child items.
2118 /// - **Purpose**: To indicate that a collapsible element is closed and its contents are
2119 /// hidden.
2120 /// - **When to use**: The counterpart to `Expanded` for any collapsible UI element.
2121 /// - **Example**: A closed folder in a file explorer's tree view, hiding its contents.
2122 Collapsed,
2123
2124 /// The element is busy and cannot respond to user interaction.
2125 /// - **Purpose**: To indicate that the element or application is performing an operation and
2126 /// is temporarily unresponsive.
2127 /// - **When to use**: When an application is loading, processing refany, or otherwise occupied.
2128 /// - **Example**: A window that is grayed out and shows a spinning cursor while saving a large
2129 /// file.
2130 Busy,
2131
2132 /// The element is not currently visible on the screen.
2133 /// - **Purpose**: To indicate that an element exists but is currently scrolled out of the
2134 /// visible area.
2135 /// - **When to use**: For items in a long list or a large document that are not within the
2136 /// current viewport.
2137 /// - **Example**: A list item in a long dropdown that you would have to scroll down to see.
2138 Offscreen,
2139
2140 /// The element can accept keyboard focus.
2141 /// - **Purpose**: To indicate that the user can navigate to this element using the keyboard
2142 /// (e.g., with the Tab key).
2143 /// - **When to use**: On all interactive elements like buttons, links, and input fields,
2144 /// whether they currently have focus or not.
2145 /// - **Example**: A button that can receive focus, even if it is not the currently focused
2146 /// element.
2147 Focusable,
2148
2149 /// The element is a container whose children can be selected.
2150 /// - **Purpose**: To indicate that the element contains items that can be chosen.
2151 /// - **When to use**: On container controls like list boxes, tree views, or text spans where
2152 /// text can be highlighted.
2153 /// - **Example**: A list box control is `Selectable`, while its individual list items have the
2154 /// `Selected` state when chosen.
2155 Selectable,
2156
2157 /// The element is a hyperlink.
2158 /// - **Purpose**: To identify an object that navigates to another resource or location when
2159 /// activated.
2160 /// - **When to use**: On any object that functions as a hyperlink.
2161 /// - **Example**: Text or an image that, when clicked, opens a web page.
2162 Linked,
2163
2164 /// The element is a hyperlink that has been visited.
2165 /// - **Purpose**: To indicate that a hyperlink has already been followed by the user.
2166 /// - **When to use**: On a `Linked` object that the user has previously activated.
2167 /// - **Example**: A hyperlink on a web page that has changed color to show it has been
2168 /// visited.
2169 Traversed,
2170
2171 /// The element allows multiple of its children to be selected at once.
2172 /// - **Purpose**: To indicate that a container control supports multi-selection.
2173 /// - **When to use**: On container controls like list boxes or file explorers that support
2174 /// multiple selections (e.g., with Ctrl-click).
2175 /// - **Example**: A file list that allows the user to select several files at once for a copy
2176 /// operation.
2177 Multiselectable,
2178
2179 /// The element contains protected content that should not be read aloud.
2180 /// - **Purpose**: To prevent assistive technologies from speaking the content of a sensitive
2181 /// field.
2182 /// - **When to use**: Primarily for password input fields.
2183 /// - **Example**: A password text box where typed characters are masked with asterisks or
2184 /// dots.
2185 Protected,
2186}
2187
2188impl_vec!(
2189 AccessibilityState,
2190 AccessibilityStateVec,
2191 AccessibilityStateVecDestructor,
2192 AccessibilityStateVecDestructorType
2193);
2194impl_vec_clone!(
2195 AccessibilityState,
2196 AccessibilityStateVec,
2197 AccessibilityStateVecDestructor
2198);
2199impl_vec_debug!(AccessibilityState, AccessibilityStateVec);
2200impl_vec_partialeq!(AccessibilityState, AccessibilityStateVec);
2201impl_vec_partialord!(AccessibilityState, AccessibilityStateVec);
2202impl_vec_eq!(AccessibilityState, AccessibilityStateVec);
2203impl_vec_ord!(AccessibilityState, AccessibilityStateVec);
2204impl_vec_hash!(AccessibilityState, AccessibilityStateVec);
2205
2206impl Clone for NodeData {
2207 #[inline]
2208 fn clone(&self) -> Self {
2209 Self {
2210 node_type: self.node_type.into_library_owned_nodetype(),
2211 dataset: match &self.dataset {
2212 OptionRefAny::None => OptionRefAny::None,
2213 OptionRefAny::Some(s) => OptionRefAny::Some(s.clone()),
2214 },
2215 ids_and_classes: self.ids_and_classes.clone(), /* do not clone the IDs and classes if
2216 * they are &'static */
2217 attributes: self.attributes.clone(),
2218 css_props: self.css_props.clone(),
2219 callbacks: self.callbacks.clone(),
2220 tab_index: self.tab_index,
2221 contenteditable: self.contenteditable,
2222 extra: self.extra.clone(),
2223 }
2224 }
2225}
2226
2227// Clone, PartialEq, Eq, Hash, PartialOrd, Ord
2228impl_vec!(
2229 NodeData,
2230 NodeDataVec,
2231 NodeDataVecDestructor,
2232 NodeDataVecDestructorType
2233);
2234impl_vec_clone!(NodeData, NodeDataVec, NodeDataVecDestructor);
2235impl_vec_mut!(NodeData, NodeDataVec);
2236impl_vec_debug!(NodeData, NodeDataVec);
2237impl_vec_partialord!(NodeData, NodeDataVec);
2238impl_vec_ord!(NodeData, NodeDataVec);
2239impl_vec_partialeq!(NodeData, NodeDataVec);
2240impl_vec_eq!(NodeData, NodeDataVec);
2241impl_vec_hash!(NodeData, NodeDataVec);
2242
2243impl NodeDataVec {
2244 #[inline]
2245 pub fn as_container<'a>(&'a self) -> NodeDataContainerRef<'a, NodeData> {
2246 NodeDataContainerRef {
2247 internal: self.as_ref(),
2248 }
2249 }
2250 #[inline]
2251 pub fn as_container_mut<'a>(&'a mut self) -> NodeDataContainerRefMut<'a, NodeData> {
2252 NodeDataContainerRefMut {
2253 internal: self.as_mut(),
2254 }
2255 }
2256}
2257
2258unsafe impl Send for NodeData {}
2259
2260/// Determines the behavior of an element in sequential focus navigation
2261// (e.g., using the Tab key).
2262#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2263#[repr(C, u8)]
2264pub enum TabIndex {
2265 /// Automatic tab index, similar to simply setting `focusable = "true"` or `tabindex = 0`
2266 /// (both have the effect of making the element focusable).
2267 ///
2268 /// Sidenote: See https://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribute
2269 /// for interesting notes on tabindex and accessibility
2270 Auto,
2271 /// Set the tab index in relation to its parent element. I.e. if you have a list of elements,
2272 /// the focusing order is restricted to the current parent.
2273 ///
2274 /// When pressing tab repeatedly, the focusing order will be
2275 /// determined by OverrideInParent elements taking precedence among global order.
2276 OverrideInParent(u32),
2277 /// Elements can be focused in callbacks, but are not accessible via
2278 /// keyboard / tab navigation (-1).
2279 NoKeyboardFocus,
2280}
2281
2282impl_option!(
2283 TabIndex,
2284 OptionTabIndex,
2285 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
2286);
2287
2288impl TabIndex {
2289 /// Returns the HTML-compatible number of the `tabindex` element.
2290 pub fn get_index(&self) -> isize {
2291 use self::TabIndex::*;
2292 match self {
2293 Auto => 0,
2294 OverrideInParent(x) => *x as isize,
2295 NoKeyboardFocus => -1,
2296 }
2297 }
2298}
2299
2300impl Default for TabIndex {
2301 fn default() -> Self {
2302 TabIndex::Auto
2303 }
2304}
2305
2306impl Default for NodeData {
2307 fn default() -> Self {
2308 NodeData::create_node(NodeType::Div)
2309 }
2310}
2311
2312impl fmt::Display for NodeData {
2313 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2314 let html_type = self.node_type.get_path();
2315 let attributes_string = node_data_to_string(&self);
2316
2317 match self.node_type.format() {
2318 Some(content) => write!(
2319 f,
2320 "<{}{}>{}</{}>",
2321 html_type, attributes_string, content, html_type
2322 ),
2323 None => write!(f, "<{}{}/>", html_type, attributes_string),
2324 }
2325 }
2326}
2327
2328fn node_data_to_string(node_data: &NodeData) -> String {
2329 let mut id_string = String::new();
2330 let ids = node_data
2331 .ids_and_classes
2332 .as_ref()
2333 .iter()
2334 .filter_map(|s| s.as_id())
2335 .collect::<Vec<_>>()
2336 .join(" ");
2337
2338 if !ids.is_empty() {
2339 id_string = format!(" id=\"{}\" ", ids);
2340 }
2341
2342 let mut class_string = String::new();
2343 let classes = node_data
2344 .ids_and_classes
2345 .as_ref()
2346 .iter()
2347 .filter_map(|s| s.as_class())
2348 .collect::<Vec<_>>()
2349 .join(" ");
2350
2351 if !classes.is_empty() {
2352 class_string = format!(" class=\"{}\" ", classes);
2353 }
2354
2355 let mut tabindex_string = String::new();
2356 if let Some(tab_index) = node_data.get_tab_index() {
2357 tabindex_string = format!(" tabindex=\"{}\" ", tab_index.get_index());
2358 };
2359
2360 format!("{}{}{}", id_string, class_string, tabindex_string)
2361}
2362
2363impl NodeData {
2364 /// Creates a new `NodeData` instance from a given `NodeType`.
2365 #[inline]
2366 pub const fn create_node(node_type: NodeType) -> Self {
2367 Self {
2368 node_type,
2369 dataset: OptionRefAny::None,
2370 ids_and_classes: IdOrClassVec::from_const_slice(&[]),
2371 attributes: AttributeVec::from_const_slice(&[]),
2372 callbacks: CoreCallbackDataVec::from_const_slice(&[]),
2373 css_props: CssPropertyWithConditionsVec::from_const_slice(&[]),
2374 tab_index: OptionTabIndex::None,
2375 contenteditable: false,
2376 extra: None,
2377 }
2378 }
2379
2380 /// Shorthand for `NodeData::create_node(NodeType::Body)`.
2381 #[inline(always)]
2382 pub const fn create_body() -> Self {
2383 Self::create_node(NodeType::Body)
2384 }
2385
2386 /// Shorthand for `NodeData::create_node(NodeType::Div)`.
2387 #[inline(always)]
2388 pub const fn create_div() -> Self {
2389 Self::create_node(NodeType::Div)
2390 }
2391
2392 /// Shorthand for `NodeData::create_node(NodeType::Br)`.
2393 #[inline(always)]
2394 pub const fn create_br() -> Self {
2395 Self::create_node(NodeType::Br)
2396 }
2397
2398 /// Shorthand for `NodeData::create_node(NodeType::Text(value.into()))`.
2399 #[inline(always)]
2400 pub fn create_text<S: Into<AzString>>(value: S) -> Self {
2401 Self::create_node(NodeType::Text(value.into()))
2402 }
2403
2404 /// Shorthand for `NodeData::create_node(NodeType::Image(image_id))`.
2405 #[inline(always)]
2406 pub fn create_image(image: ImageRef) -> Self {
2407 Self::create_node(NodeType::Image(image))
2408 }
2409
2410 #[inline(always)]
2411 pub fn create_iframe(data: RefAny, callback: impl Into<IFrameCallback>) -> Self {
2412 Self::create_node(NodeType::IFrame(IFrameNode {
2413 callback: callback.into(),
2414 refany: data,
2415 }))
2416 }
2417
2418 /// Checks whether this node is of the given node type (div, image, text).
2419 #[inline]
2420 pub fn is_node_type(&self, searched_type: NodeType) -> bool {
2421 self.node_type == searched_type
2422 }
2423
2424 /// Checks whether this node has the searched ID attached.
2425 pub fn has_id(&self, id: &str) -> bool {
2426 self.ids_and_classes
2427 .iter()
2428 .any(|id_or_class| id_or_class.as_id() == Some(id))
2429 }
2430
2431 /// Checks whether this node has the searched class attached.
2432 pub fn has_class(&self, class: &str) -> bool {
2433 self.ids_and_classes
2434 .iter()
2435 .any(|id_or_class| id_or_class.as_class() == Some(class))
2436 }
2437
2438 pub fn has_context_menu(&self) -> bool {
2439 self.extra
2440 .as_ref()
2441 .map(|m| m.context_menu.is_some())
2442 .unwrap_or(false)
2443 }
2444
2445 pub fn is_text_node(&self) -> bool {
2446 match self.node_type {
2447 NodeType::Text(_) => true,
2448 _ => false,
2449 }
2450 }
2451
2452 pub fn is_iframe_node(&self) -> bool {
2453 match self.node_type {
2454 NodeType::IFrame(_) => true,
2455 _ => false,
2456 }
2457 }
2458
2459 // NOTE: Getters are used here in order to allow changing the memory allocator for the NodeData
2460 // in the future (which is why the fields are all private).
2461
2462 #[inline(always)]
2463 pub const fn get_node_type(&self) -> &NodeType {
2464 &self.node_type
2465 }
2466 #[inline(always)]
2467 pub fn get_dataset_mut(&mut self) -> &mut OptionRefAny {
2468 &mut self.dataset
2469 }
2470 #[inline(always)]
2471 pub const fn get_dataset(&self) -> &OptionRefAny {
2472 &self.dataset
2473 }
2474 #[inline(always)]
2475 pub const fn get_ids_and_classes(&self) -> &IdOrClassVec {
2476 &self.ids_and_classes
2477 }
2478 #[inline(always)]
2479 pub const fn get_callbacks(&self) -> &CoreCallbackDataVec {
2480 &self.callbacks
2481 }
2482 #[inline(always)]
2483 pub const fn get_css_props(&self) -> &CssPropertyWithConditionsVec {
2484 &self.css_props
2485 }
2486
2487 #[inline]
2488 pub fn get_clip_mask(&self) -> Option<&ImageMask> {
2489 self.extra.as_ref().and_then(|e| e.clip_mask.as_ref())
2490 }
2491 #[inline]
2492 pub fn get_tab_index(&self) -> Option<&TabIndex> {
2493 self.tab_index.as_ref()
2494 }
2495 #[inline]
2496 pub fn get_accessibility_info(&self) -> Option<&Box<AccessibilityInfo>> {
2497 self.extra.as_ref().and_then(|e| e.accessibility.as_ref())
2498 }
2499 #[inline]
2500 pub fn get_menu_bar(&self) -> Option<&Box<Menu>> {
2501 self.extra.as_ref().and_then(|e| e.menu_bar.as_ref())
2502 }
2503 #[inline]
2504 pub fn get_context_menu(&self) -> Option<&Box<Menu>> {
2505 self.extra.as_ref().and_then(|e| e.context_menu.as_ref())
2506 }
2507
2508 /// Returns whether this node is an anonymous box generated for table layout.
2509 #[inline]
2510 pub fn is_anonymous(&self) -> bool {
2511 self.extra.as_ref().map(|e| e.is_anonymous).unwrap_or(false)
2512 }
2513
2514 #[inline(always)]
2515 pub fn set_node_type(&mut self, node_type: NodeType) {
2516 self.node_type = node_type;
2517 }
2518 #[inline(always)]
2519 pub fn set_dataset(&mut self, data: OptionRefAny) {
2520 self.dataset = data;
2521 }
2522 #[inline(always)]
2523 pub fn set_ids_and_classes(&mut self, ids_and_classes: IdOrClassVec) {
2524 self.ids_and_classes = ids_and_classes;
2525 }
2526 #[inline(always)]
2527 pub fn set_callbacks(&mut self, callbacks: CoreCallbackDataVec) {
2528 self.callbacks = callbacks;
2529 }
2530 #[inline(always)]
2531 pub fn set_css_props(&mut self, css_props: CssPropertyWithConditionsVec) {
2532 self.css_props = css_props;
2533 }
2534 #[inline]
2535 pub fn set_clip_mask(&mut self, clip_mask: ImageMask) {
2536 self.extra
2537 .get_or_insert_with(|| Box::new(NodeDataExt::default()))
2538 .clip_mask = Some(clip_mask);
2539 }
2540 #[inline]
2541 pub fn set_tab_index(&mut self, tab_index: TabIndex) {
2542 self.tab_index = Some(tab_index).into();
2543 }
2544 #[inline]
2545 pub fn set_contenteditable(&mut self, contenteditable: bool) {
2546 self.contenteditable = contenteditable;
2547 }
2548 #[inline]
2549 pub fn is_contenteditable(&self) -> bool {
2550 self.contenteditable
2551 }
2552 #[inline]
2553 pub fn set_accessibility_info(&mut self, accessibility_info: AccessibilityInfo) {
2554 self.extra
2555 .get_or_insert_with(|| Box::new(NodeDataExt::default()))
2556 .accessibility = Some(Box::new(accessibility_info));
2557 }
2558
2559 /// Marks this node as an anonymous box (generated for table layout).
2560 #[inline]
2561 pub fn set_anonymous(&mut self, is_anonymous: bool) {
2562 self.extra
2563 .get_or_insert_with(|| Box::new(NodeDataExt::default()))
2564 .is_anonymous = is_anonymous;
2565 }
2566 #[inline]
2567 pub fn set_menu_bar(&mut self, menu_bar: Menu) {
2568 self.extra
2569 .get_or_insert_with(|| Box::new(NodeDataExt::default()))
2570 .menu_bar = Some(Box::new(menu_bar));
2571 }
2572 #[inline]
2573 pub fn set_context_menu(&mut self, context_menu: Menu) {
2574 self.extra
2575 .get_or_insert_with(|| Box::new(NodeDataExt::default()))
2576 .context_menu = Some(Box::new(context_menu));
2577 }
2578
2579 /// Sets a stable key for this node used in reconciliation.
2580 ///
2581 /// This key is used to track node identity across DOM updates, enabling
2582 /// the framework to distinguish between "moving" a node and "destroying/creating" one.
2583 /// This is crucial for correct lifecycle events when lists are reordered.
2584 ///
2585 /// # Example
2586 /// ```rust
2587 /// node_data.set_key("user-123");
2588 /// ```
2589 #[inline]
2590 pub fn set_key<K: core::hash::Hash>(&mut self, key: K) {
2591 use highway::{HighwayHash, HighwayHasher, Key};
2592 let mut hasher = HighwayHasher::new(Key([0; 4]));
2593 key.hash(&mut hasher);
2594 self.extra
2595 .get_or_insert_with(|| Box::new(NodeDataExt::default()))
2596 .key = Some(hasher.finalize64());
2597 }
2598
2599 /// Gets the key for this node, if set.
2600 #[inline]
2601 pub fn get_key(&self) -> Option<u64> {
2602 self.extra.as_ref().and_then(|ext| ext.key)
2603 }
2604
2605 /// Sets a dataset merge callback for this node.
2606 ///
2607 /// The merge callback is invoked during reconciliation when a node from the
2608 /// previous frame is matched with a node in the new frame. It allows heavy
2609 /// resources (video decoders, GL textures, network connections) to be
2610 /// transferred from the old node to the new node instead of being destroyed.
2611 ///
2612 /// # Type Safety
2613 ///
2614 /// The callback stores the `TypeId` of `T`. During execution, both the old
2615 /// and new datasets must match this type, otherwise the merge is skipped.
2616 ///
2617 /// # Example
2618 /// ```rust
2619 /// struct VideoPlayer {
2620 /// url: String,
2621 /// decoder: Option<DecoderHandle>,
2622 /// }
2623 ///
2624 /// extern "C" fn merge_video(new_data: RefAny, old_data: RefAny) -> RefAny {
2625 /// // Transfer the heavy decoder handle from old to new
2626 /// if let (Some(mut new), Some(old)) = (
2627 /// new_data.downcast_mut::<VideoPlayer>(),
2628 /// old_data.downcast_ref::<VideoPlayer>()
2629 /// ) {
2630 /// new.decoder = old.decoder.take();
2631 /// }
2632 /// new_data
2633 /// }
2634 ///
2635 /// node_data.set_merge_callback(merge_video);
2636 /// ```
2637 #[inline]
2638 pub fn set_merge_callback<C: Into<DatasetMergeCallback>>(&mut self, callback: C) {
2639 self.extra
2640 .get_or_insert_with(|| Box::new(NodeDataExt::default()))
2641 .dataset_merge_callback = Some(callback.into());
2642 }
2643
2644 /// Gets the merge callback for this node, if set.
2645 #[inline]
2646 pub fn get_merge_callback(&self) -> Option<DatasetMergeCallback> {
2647 self.extra.as_ref().and_then(|ext| ext.dataset_merge_callback.clone())
2648 }
2649
2650 #[inline]
2651 pub fn with_menu_bar(mut self, menu_bar: Menu) -> Self {
2652 self.set_menu_bar(menu_bar);
2653 self
2654 }
2655
2656 #[inline]
2657 pub fn with_context_menu(mut self, context_menu: Menu) -> Self {
2658 self.set_context_menu(context_menu);
2659 self
2660 }
2661
2662 #[inline]
2663 pub fn add_callback<C: Into<CoreCallback>>(
2664 &mut self,
2665 event: EventFilter,
2666 data: RefAny,
2667 callback: C,
2668 ) {
2669 let callback = callback.into();
2670 let mut v: CoreCallbackDataVec = Vec::new().into();
2671 mem::swap(&mut v, &mut self.callbacks);
2672 let mut v = v.into_library_owned_vec();
2673 v.push(CoreCallbackData {
2674 event,
2675 refany: data,
2676 callback,
2677 });
2678 self.callbacks = v.into();
2679 }
2680
2681 #[inline]
2682 pub fn add_id(&mut self, s: AzString) {
2683 let mut v: IdOrClassVec = Vec::new().into();
2684 mem::swap(&mut v, &mut self.ids_and_classes);
2685 let mut v = v.into_library_owned_vec();
2686 v.push(IdOrClass::Id(s));
2687 self.ids_and_classes = v.into();
2688 }
2689 #[inline]
2690 pub fn add_class(&mut self, s: AzString) {
2691 let mut v: IdOrClassVec = Vec::new().into();
2692 mem::swap(&mut v, &mut self.ids_and_classes);
2693 let mut v = v.into_library_owned_vec();
2694 v.push(IdOrClass::Class(s));
2695 self.ids_and_classes = v.into();
2696 }
2697
2698 /// Add an unconditional CSS property (always applies)
2699 #[inline]
2700 pub fn add_css_property(&mut self, p: CssProperty) {
2701 use azul_css::dynamic_selector::CssPropertyWithConditions;
2702 let mut v: CssPropertyWithConditionsVec = Vec::new().into();
2703 mem::swap(&mut v, &mut self.css_props);
2704 let mut v = v.into_library_owned_vec();
2705 v.push(CssPropertyWithConditions::simple(p));
2706 self.css_props = v.into();
2707 }
2708
2709 /// Add a CSS property that applies only on hover
2710 #[inline]
2711 pub fn add_hover_css_property(&mut self, p: CssProperty) {
2712 use azul_css::dynamic_selector::{
2713 CssPropertyWithConditions, DynamicSelector, PseudoStateType,
2714 };
2715 let mut v: CssPropertyWithConditionsVec = Vec::new().into();
2716 mem::swap(&mut v, &mut self.css_props);
2717 let mut v = v.into_library_owned_vec();
2718 v.push(CssPropertyWithConditions::with_condition(
2719 p,
2720 DynamicSelector::PseudoState(PseudoStateType::Hover),
2721 ));
2722 self.css_props = v.into();
2723 }
2724
2725 /// Add a CSS property that applies only when active (clicked)
2726 #[inline]
2727 pub fn add_active_css_property(&mut self, p: CssProperty) {
2728 use azul_css::dynamic_selector::{
2729 CssPropertyWithConditions, DynamicSelector, PseudoStateType,
2730 };
2731 let mut v: CssPropertyWithConditionsVec = Vec::new().into();
2732 mem::swap(&mut v, &mut self.css_props);
2733 let mut v = v.into_library_owned_vec();
2734 v.push(CssPropertyWithConditions::with_condition(
2735 p,
2736 DynamicSelector::PseudoState(PseudoStateType::Active),
2737 ));
2738 self.css_props = v.into();
2739 }
2740
2741 /// Add a CSS property that applies only when focused
2742 #[inline]
2743 pub fn add_focus_css_property(&mut self, p: CssProperty) {
2744 use azul_css::dynamic_selector::{
2745 CssPropertyWithConditions, DynamicSelector, PseudoStateType,
2746 };
2747 let mut v: CssPropertyWithConditionsVec = Vec::new().into();
2748 mem::swap(&mut v, &mut self.css_props);
2749 let mut v = v.into_library_owned_vec();
2750 v.push(CssPropertyWithConditions::with_condition(
2751 p,
2752 DynamicSelector::PseudoState(PseudoStateType::Focus),
2753 ));
2754 self.css_props = v.into();
2755 }
2756
2757 /// Calculates a deterministic node hash for this node.
2758 pub fn calculate_node_data_hash(&self) -> DomNodeHash {
2759 use highway::{HighwayHash, HighwayHasher, Key};
2760 let mut hasher = HighwayHasher::new(Key([0; 4]));
2761 self.hash(&mut hasher);
2762 let h = hasher.finalize64();
2763 DomNodeHash { inner: h }
2764 }
2765
2766 /// Calculates a structural hash for DOM reconciliation that ignores text content.
2767 ///
2768 /// This hash is used for matching nodes across DOM frames where the text content
2769 /// may have changed (e.g., contenteditable text being edited). It hashes:
2770 /// - Node type discriminant (but NOT the text content for Text nodes)
2771 /// - IDs and classes
2772 /// - Attributes (but NOT contenteditable state which may change with focus)
2773 /// - Callback events and types
2774 ///
2775 /// This allows a Text("Hello") node to match Text("Hello World") during reconciliation,
2776 /// preserving cursor position and selection state.
2777 pub fn calculate_structural_hash(&self) -> DomNodeHash {
2778 use highway::{HighwayHash, HighwayHasher, Key};
2779 use core::hash::Hasher as StdHasher;
2780
2781 let mut hasher = HighwayHasher::new(Key([0; 4]));
2782
2783 // Hash node type discriminant only, not content
2784 // This means Text("A") and Text("B") have the same structural hash
2785 core::mem::discriminant(&self.node_type).hash(&mut hasher);
2786
2787 // For IFrame nodes, hash the callback to distinguish different iframes
2788 if let NodeType::IFrame(ref iframe) = self.node_type {
2789 // Hash iframe callback (it implements Hash)
2790 iframe.hash(&mut hasher);
2791 }
2792
2793 // For Image nodes, hash the image reference to distinguish different images
2794 if let NodeType::Image(ref img_ref) = self.node_type {
2795 img_ref.hash(&mut hasher);
2796 }
2797
2798 // Hash IDs and classes - these are structural and shouldn't change
2799 self.ids_and_classes.as_ref().hash(&mut hasher);
2800
2801 // Hash attributes - but skip contenteditable since that might change
2802 for attr in self.attributes.as_ref().iter() {
2803 // Skip ContentEditable attribute - it's state, not structure
2804 if !matches!(attr, AttributeType::ContentEditable(_)) {
2805 attr.hash(&mut hasher);
2806 }
2807 }
2808
2809 // Hash callback events (not the actual callback function pointers)
2810 for callback in self.callbacks.as_ref().iter() {
2811 callback.event.hash(&mut hasher);
2812 }
2813
2814 let h = hasher.finalize64();
2815 DomNodeHash { inner: h }
2816 }
2817
2818 #[inline(always)]
2819 pub fn with_tab_index(mut self, tab_index: TabIndex) -> Self {
2820 self.set_tab_index(tab_index);
2821 self
2822 }
2823 #[inline(always)]
2824 pub fn with_contenteditable(mut self, contenteditable: bool) -> Self {
2825 self.set_contenteditable(contenteditable);
2826 self
2827 }
2828 #[inline(always)]
2829 pub fn with_node_type(mut self, node_type: NodeType) -> Self {
2830 self.set_node_type(node_type);
2831 self
2832 }
2833 #[inline(always)]
2834 pub fn with_callback<C: Into<CoreCallback>>(
2835 mut self,
2836 event: EventFilter,
2837 data: RefAny,
2838 callback: C,
2839 ) -> Self {
2840 self.add_callback(event, data, callback);
2841 self
2842 }
2843 #[inline(always)]
2844 pub fn with_dataset(mut self, data: OptionRefAny) -> Self {
2845 self.dataset = data;
2846 self
2847 }
2848 #[inline(always)]
2849 pub fn with_ids_and_classes(mut self, ids_and_classes: IdOrClassVec) -> Self {
2850 self.ids_and_classes = ids_and_classes;
2851 self
2852 }
2853 #[inline(always)]
2854 pub fn with_callbacks(mut self, callbacks: CoreCallbackDataVec) -> Self {
2855 self.callbacks = callbacks;
2856 self
2857 }
2858 #[inline(always)]
2859 pub fn with_css_props(mut self, css_props: CssPropertyWithConditionsVec) -> Self {
2860 self.css_props = css_props;
2861 self
2862 }
2863
2864 /// Assigns a stable key to this node for reconciliation.
2865 ///
2866 /// This is crucial for performance and correct state preservation when
2867 /// lists of items change order or items are inserted/removed. Without keys,
2868 /// the reconciliation algorithm falls back to hash-based matching.
2869 ///
2870 /// # Example
2871 /// ```rust
2872 /// NodeData::create_div()
2873 /// .with_key("user-avatar-123")
2874 /// ```
2875 #[inline]
2876 pub fn with_key<K: core::hash::Hash>(mut self, key: K) -> Self {
2877 self.set_key(key);
2878 self
2879 }
2880
2881 /// Registers a callback to merge dataset state from the previous frame.
2882 ///
2883 /// This is used for components that maintain heavy internal state (video players,
2884 /// WebGL contexts, network connections) that should not be destroyed and recreated
2885 /// on every render frame.
2886 ///
2887 /// The callback receives both datasets as `RefAny` (cheap shallow clones) and
2888 /// returns the `RefAny` that should be used for the new node.
2889 ///
2890 /// # Example
2891 /// ```rust
2892 /// struct VideoPlayer {
2893 /// url: String,
2894 /// decoder_handle: Option<DecoderHandle>,
2895 /// }
2896 ///
2897 /// extern "C" fn merge_video(new_data: RefAny, old_data: RefAny) -> RefAny {
2898 /// if let (Some(mut new), Some(old)) = (
2899 /// new_data.downcast_mut::<VideoPlayer>(),
2900 /// old_data.downcast_ref::<VideoPlayer>()
2901 /// ) {
2902 /// new.decoder_handle = old.decoder_handle.take();
2903 /// }
2904 /// new_data
2905 /// }
2906 ///
2907 /// NodeData::create_div()
2908 /// .with_dataset(RefAny::new(VideoPlayer::new("movie.mp4")).into())
2909 /// .with_merge_callback(merge_video)
2910 /// ```
2911 #[inline]
2912 pub fn with_merge_callback<C: Into<DatasetMergeCallback>>(mut self, callback: C) -> Self {
2913 self.set_merge_callback(callback);
2914 self
2915 }
2916
2917 /// Parse CSS from a string and add as unconditional properties
2918 pub fn set_inline_style(&mut self, style: &str) {
2919 use azul_css::dynamic_selector::CssPropertyWithConditions;
2920 let parsed = CssPropertyWithConditionsVec::parse_normal(style);
2921 let mut current = Vec::new().into();
2922 mem::swap(&mut current, &mut self.css_props);
2923 let mut v = current.into_library_owned_vec();
2924 v.extend(parsed.into_library_owned_vec());
2925 self.css_props = v.into();
2926 }
2927
2928 /// Builder method for setting inline CSS styles for the normal state
2929 pub fn with_inline_style(mut self, style: &str) -> Self {
2930 self.set_inline_style(style);
2931 self
2932 }
2933
2934 /// Sets inline CSS styles for the hover state, parsing from a CSS string
2935 pub fn set_inline_hover_style(&mut self, style: &str) {
2936 let parsed = CssPropertyWithConditionsVec::parse_hover(style);
2937 let mut current = Vec::new().into();
2938 mem::swap(&mut current, &mut self.css_props);
2939 let mut v = current.into_library_owned_vec();
2940 v.extend(parsed.into_library_owned_vec());
2941 self.css_props = v.into();
2942 }
2943
2944 /// Builder method for setting inline CSS styles for the hover state
2945 pub fn with_inline_hover_style(mut self, style: &str) -> Self {
2946 self.set_inline_hover_style(style);
2947 self
2948 }
2949
2950 /// Sets inline CSS styles for the active state, parsing from a CSS string
2951 pub fn set_inline_active_style(&mut self, style: &str) {
2952 let parsed = CssPropertyWithConditionsVec::parse_active(style);
2953 let mut current = Vec::new().into();
2954 mem::swap(&mut current, &mut self.css_props);
2955 let mut v = current.into_library_owned_vec();
2956 v.extend(parsed.into_library_owned_vec());
2957 self.css_props = v.into();
2958 }
2959
2960 /// Builder method for setting inline CSS styles for the active state
2961 pub fn with_inline_active_style(mut self, style: &str) -> Self {
2962 self.set_inline_active_style(style);
2963 self
2964 }
2965
2966 /// Sets inline CSS styles for the focus state, parsing from a CSS string
2967 pub fn set_inline_focus_style(&mut self, style: &str) {
2968 let parsed = CssPropertyWithConditionsVec::parse_focus(style);
2969 let mut current = Vec::new().into();
2970 mem::swap(&mut current, &mut self.css_props);
2971 let mut v = current.into_library_owned_vec();
2972 v.extend(parsed.into_library_owned_vec());
2973 self.css_props = v.into();
2974 }
2975
2976 /// Builder method for setting inline CSS styles for the focus state
2977 pub fn with_inline_focus_style(mut self, style: &str) -> Self {
2978 self.set_inline_focus_style(style);
2979 self
2980 }
2981
2982 #[inline(always)]
2983 pub fn swap_with_default(&mut self) -> Self {
2984 let mut s = NodeData::create_div();
2985 mem::swap(&mut s, self);
2986 s
2987 }
2988
2989 #[inline]
2990 pub fn copy_special(&self) -> Self {
2991 Self {
2992 node_type: self.node_type.into_library_owned_nodetype(),
2993 dataset: match &self.dataset {
2994 OptionRefAny::None => OptionRefAny::None,
2995 OptionRefAny::Some(s) => OptionRefAny::Some(s.clone()),
2996 },
2997 ids_and_classes: self.ids_and_classes.clone(), /* do not clone the IDs and classes if
2998 * they are &'static */
2999 attributes: self.attributes.clone(),
3000 css_props: self.css_props.clone(),
3001 callbacks: self.callbacks.clone(),
3002 tab_index: self.tab_index,
3003 contenteditable: self.contenteditable,
3004 extra: self.extra.clone(),
3005 }
3006 }
3007
3008 pub fn is_focusable(&self) -> bool {
3009 // TODO: do some better analysis of next / first / item
3010 self.get_tab_index().is_some()
3011 || self
3012 .get_callbacks()
3013 .iter()
3014 .any(|cb| cb.event.is_focus_callback())
3015 }
3016
3017 /// Returns true if this element has "activation behavior" per HTML5 spec.
3018 ///
3019 /// Elements with activation behavior can be activated via Enter or Space key
3020 /// when focused, which generates a synthetic click event.
3021 ///
3022 /// Per HTML5 spec, elements with activation behavior include:
3023 /// - Button elements
3024 /// - Input elements (submit, button, reset, checkbox, radio)
3025 /// - Anchor elements with href
3026 /// - Any element with a click callback (implicit activation)
3027 ///
3028 /// See: https://html.spec.whatwg.org/multipage/interaction.html#activation-behavior
3029 pub fn has_activation_behavior(&self) -> bool {
3030 use crate::events::{EventFilter, HoverEventFilter};
3031
3032 // Check for click callback (most common case for Azul)
3033 // In Azul, "click" is typically LeftMouseUp
3034 let has_click_callback = self
3035 .get_callbacks()
3036 .iter()
3037 .any(|cb| matches!(
3038 cb.event,
3039 EventFilter::Hover(HoverEventFilter::MouseUp)
3040 | EventFilter::Hover(HoverEventFilter::LeftMouseUp)
3041 ));
3042
3043 if has_click_callback {
3044 return true;
3045 }
3046
3047 // Check accessibility role for button-like elements
3048 if let Some(ref ext) = self.extra {
3049 if let Some(ref accessibility) = ext.accessibility {
3050 use crate::dom::AccessibilityRole;
3051 match accessibility.role {
3052 AccessibilityRole::PushButton // Button
3053 | AccessibilityRole::Link
3054 | AccessibilityRole::CheckButton // Checkbox
3055 | AccessibilityRole::RadioButton // Radio
3056 | AccessibilityRole::MenuItem
3057 | AccessibilityRole::PageTab // Tab
3058 => return true,
3059 _ => {}
3060 }
3061 }
3062 }
3063
3064 false
3065 }
3066
3067 /// Returns true if this element is currently activatable.
3068 ///
3069 /// An element is activatable if it has activation behavior AND is not disabled.
3070 /// This checks for common disability patterns (aria-disabled, disabled attribute).
3071 pub fn is_activatable(&self) -> bool {
3072 if !self.has_activation_behavior() {
3073 return false;
3074 }
3075
3076 // Check for disabled state in accessibility info
3077 if let Some(ref ext) = self.extra {
3078 if let Some(ref accessibility) = ext.accessibility {
3079 // Check if explicitly marked as unavailable
3080 if accessibility
3081 .states
3082 .as_ref()
3083 .iter()
3084 .any(|s| matches!(s, AccessibilityState::Unavailable))
3085 {
3086 return false;
3087 }
3088 }
3089 }
3090
3091 // Not disabled, so activatable
3092 true
3093 }
3094
3095 /// Returns the tab index for this element.
3096 ///
3097 /// Tab index determines keyboard navigation order:
3098 /// - `None`: Not in tab order (unless naturally focusable)
3099 /// - `Some(-1)`: Focusable programmatically but not via Tab
3100 /// - `Some(0)`: In natural tab order
3101 /// - `Some(n > 0)`: In tab order with priority n (higher = later)
3102 pub fn get_effective_tabindex(&self) -> Option<i32> {
3103 match self.tab_index {
3104 OptionTabIndex::None => {
3105 // Check if naturally focusable (has focus callback)
3106 if self.get_callbacks().iter().any(|cb| cb.event.is_focus_callback()) {
3107 Some(0)
3108 } else {
3109 None
3110 }
3111 }
3112 OptionTabIndex::Some(tab_idx) => {
3113 match tab_idx {
3114 TabIndex::Auto => Some(0),
3115 TabIndex::OverrideInParent(n) => Some(n as i32),
3116 TabIndex::NoKeyboardFocus => Some(-1),
3117 }
3118 }
3119 }
3120 }
3121
3122 pub fn get_iframe_node(&mut self) -> Option<&mut IFrameNode> {
3123 match &mut self.node_type {
3124 NodeType::IFrame(i) => Some(i),
3125 _ => None,
3126 }
3127 }
3128
3129 pub fn get_render_image_callback_node<'a>(
3130 &'a mut self,
3131 ) -> Option<(&'a mut CoreImageCallback, ImageRefHash)> {
3132 match &mut self.node_type {
3133 NodeType::Image(img) => {
3134 let hash = image_ref_get_hash(&img);
3135 img.get_image_callback_mut().map(|r| (r, hash))
3136 }
3137 _ => None,
3138 }
3139 }
3140
3141 pub fn debug_print_start(
3142 &self,
3143 css_cache: &CssPropertyCache,
3144 node_id: &NodeId,
3145 node_state: &StyledNodeState,
3146 ) -> String {
3147 let html_type = self.node_type.get_path();
3148 let attributes_string = node_data_to_string(&self);
3149 let style = css_cache.get_computed_css_style_string(&self, node_id, node_state);
3150 format!(
3151 "<{} data-az-node-id=\"{}\" {} {style}>",
3152 html_type,
3153 node_id.index(),
3154 attributes_string,
3155 style = if style.trim().is_empty() {
3156 String::new()
3157 } else {
3158 format!("style=\"{style}\"")
3159 }
3160 )
3161 }
3162
3163 pub fn debug_print_end(&self) -> String {
3164 let html_type = self.node_type.get_path();
3165 format!("</{}>", html_type)
3166 }
3167}
3168
3169/// A unique, runtime-generated identifier for a single `Dom` instance.
3170#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
3171#[repr(C)]
3172pub struct DomId {
3173 pub inner: usize,
3174}
3175
3176impl fmt::Display for DomId {
3177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3178 write!(f, "{}", self.inner)
3179 }
3180}
3181
3182impl DomId {
3183 pub const ROOT_ID: DomId = DomId { inner: 0 };
3184}
3185
3186impl Default for DomId {
3187 fn default() -> DomId {
3188 DomId::ROOT_ID
3189 }
3190}
3191
3192impl_option!(
3193 DomId,
3194 OptionDomId,
3195 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
3196);
3197
3198impl_vec!(DomId, DomIdVec, DomIdVecDestructor, DomIdVecDestructorType);
3199impl_vec_debug!(DomId, DomIdVec);
3200impl_vec_clone!(DomId, DomIdVec, DomIdVecDestructor);
3201impl_vec_partialeq!(DomId, DomIdVec);
3202impl_vec_partialord!(DomId, DomIdVec);
3203
3204/// A UUID for a DOM node within a `LayoutWindow`.
3205#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3206#[repr(C)]
3207pub struct DomNodeId {
3208 /// The ID of the `Dom` this node belongs to.
3209 pub dom: DomId,
3210 /// The hierarchical ID of the node within its `Dom`.
3211 pub node: NodeHierarchyItemId,
3212}
3213
3214impl_option!(
3215 DomNodeId,
3216 OptionDomNodeId,
3217 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
3218);
3219
3220impl DomNodeId {
3221 pub const ROOT: DomNodeId = DomNodeId {
3222 dom: DomId::ROOT_ID,
3223 node: NodeHierarchyItemId::NONE,
3224 };
3225}
3226
3227/// The document model, similar to HTML. This is a create-only structure, you don't actually read
3228/// anything back from it. It's designed for ease of construction.
3229#[repr(C)]
3230#[derive(PartialEq, Clone, Eq, Hash, PartialOrd, Ord)]
3231pub struct Dom {
3232 /// The data for the root node of this DOM (or sub-DOM).
3233 pub root: NodeData,
3234 /// The children of this DOM node.
3235 pub children: DomVec,
3236 // Tracks the number of sub-children of the current children, so that
3237 // the `Dom` can be converted into a `CompactDom`.
3238 pub estimated_total_children: usize,
3239}
3240
3241impl_option!(
3242 Dom,
3243 OptionDom,
3244 copy = false,
3245 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
3246);
3247
3248impl_vec!(Dom, DomVec, DomVecDestructor, DomVecDestructorType);
3249impl_vec_clone!(Dom, DomVec, DomVecDestructor);
3250impl_vec_mut!(Dom, DomVec);
3251impl_vec_debug!(Dom, DomVec);
3252impl_vec_partialord!(Dom, DomVec);
3253impl_vec_ord!(Dom, DomVec);
3254impl_vec_partialeq!(Dom, DomVec);
3255impl_vec_eq!(Dom, DomVec);
3256impl_vec_hash!(Dom, DomVec);
3257
3258impl Dom {
3259 // ----- DOM CONSTRUCTORS
3260
3261 /// Creates an empty DOM with a give `NodeType`. Note: This is a `const fn` and
3262 /// doesn't allocate, it only allocates once you add at least one child node.
3263 #[inline(always)]
3264 pub fn create_node(node_type: NodeType) -> Self {
3265 Self {
3266 root: NodeData::create_node(node_type),
3267 children: Vec::new().into(),
3268 estimated_total_children: 0,
3269 }
3270 }
3271 #[inline(always)]
3272 pub fn from_data(node_data: NodeData) -> Self {
3273 Self {
3274 root: node_data,
3275 children: Vec::new().into(),
3276 estimated_total_children: 0,
3277 }
3278 }
3279
3280 // Document Structure Elements
3281
3282 /// Creates the root HTML element.
3283 ///
3284 /// **Accessibility**: The `<html>` element is the root of an HTML document and should have a
3285 /// `lang` attribute.
3286 #[inline(always)]
3287 pub const fn create_html() -> Self {
3288 Self {
3289 root: NodeData::create_node(NodeType::Html),
3290 children: DomVec::from_const_slice(&[]),
3291 estimated_total_children: 0,
3292 }
3293 }
3294
3295 /// Creates the document head element.
3296 ///
3297 /// **Accessibility**: The `<head>` contains metadata. Use `<title>` for page titles.
3298 #[inline(always)]
3299 pub const fn create_head() -> Self {
3300 Self {
3301 root: NodeData::create_node(NodeType::Head),
3302 children: DomVec::from_const_slice(&[]),
3303 estimated_total_children: 0,
3304 }
3305 }
3306
3307 #[inline(always)]
3308 pub const fn create_body() -> Self {
3309 Self {
3310 root: NodeData::create_node(NodeType::Body),
3311 children: DomVec::from_const_slice(&[]),
3312 estimated_total_children: 0,
3313 }
3314 }
3315
3316 /// Creates a generic block-level container.
3317 ///
3318 /// **Accessibility**: Prefer semantic elements like `<article>`, `<section>`, `<nav>` when
3319 /// applicable.
3320 #[inline(always)]
3321 pub const fn create_div() -> Self {
3322 Self {
3323 root: NodeData::create_node(NodeType::Div),
3324 children: DomVec::from_const_slice(&[]),
3325 estimated_total_children: 0,
3326 }
3327 }
3328
3329 // Semantic Structure Elements
3330
3331 /// Creates an article element.
3332 ///
3333 /// **Accessibility**: Represents self-contained content that could be distributed
3334 /// independently. Screen readers can navigate by articles. Consider adding aria-label for
3335 /// multiple articles.
3336 #[inline(always)]
3337 pub const fn create_article() -> Self {
3338 Self {
3339 root: NodeData::create_node(NodeType::Article),
3340 children: DomVec::from_const_slice(&[]),
3341 estimated_total_children: 0,
3342 }
3343 }
3344
3345 /// Creates a section element.
3346 ///
3347 /// **Accessibility**: Represents a thematic grouping of content with a heading.
3348 /// Should typically have a heading (h1-h6) as a child. Consider aria-labelledby.
3349 #[inline(always)]
3350 pub const fn create_section() -> Self {
3351 Self {
3352 root: NodeData::create_node(NodeType::Section),
3353 children: DomVec::from_const_slice(&[]),
3354 estimated_total_children: 0,
3355 }
3356 }
3357
3358 /// Creates a navigation element.
3359 ///
3360 /// **Accessibility**: Represents navigation links. Screen readers can jump to navigation.
3361 /// Use aria-label to distinguish multiple nav elements (e.g., "Main navigation", "Footer
3362 /// links").
3363 #[inline(always)]
3364 pub const fn create_nav() -> Self {
3365 Self {
3366 root: NodeData::create_node(NodeType::Nav),
3367 children: DomVec::from_const_slice(&[]),
3368 estimated_total_children: 0,
3369 }
3370 }
3371
3372 /// Creates an aside element.
3373 ///
3374 /// **Accessibility**: Represents content tangentially related to main content (sidebars,
3375 /// callouts). Screen readers announce this as complementary content.
3376 #[inline(always)]
3377 pub const fn create_aside() -> Self {
3378 Self {
3379 root: NodeData::create_node(NodeType::Aside),
3380 children: DomVec::from_const_slice(&[]),
3381 estimated_total_children: 0,
3382 }
3383 }
3384
3385 /// Creates a header element.
3386 ///
3387 /// **Accessibility**: Represents introductory content or navigational aids.
3388 /// Can be used for page headers or section headers.
3389 #[inline(always)]
3390 pub const fn create_header() -> Self {
3391 Self {
3392 root: NodeData::create_node(NodeType::Header),
3393 children: DomVec::from_const_slice(&[]),
3394 estimated_total_children: 0,
3395 }
3396 }
3397
3398 /// Creates a footer element.
3399 ///
3400 /// **Accessibility**: Represents footer for nearest section or page.
3401 /// Typically contains copyright, author info, or related links.
3402 #[inline(always)]
3403 pub const fn create_footer() -> Self {
3404 Self {
3405 root: NodeData::create_node(NodeType::Footer),
3406 children: DomVec::from_const_slice(&[]),
3407 estimated_total_children: 0,
3408 }
3409 }
3410
3411 /// Creates a main content element.
3412 ///
3413 /// **Accessibility**: Represents the dominant content. There should be only ONE main per page.
3414 /// Screen readers can jump directly to main content. Do not nest inside
3415 /// article/aside/footer/header/nav.
3416 #[inline(always)]
3417 pub const fn create_main() -> Self {
3418 Self {
3419 root: NodeData::create_node(NodeType::Main),
3420 children: DomVec::from_const_slice(&[]),
3421 estimated_total_children: 0,
3422 }
3423 }
3424
3425 /// Creates a figure element.
3426 ///
3427 /// **Accessibility**: Represents self-contained content like diagrams, photos, code listings.
3428 /// Use with `<figcaption>` to provide a caption. Screen readers associate caption with figure.
3429 #[inline(always)]
3430 pub const fn create_figure() -> Self {
3431 Self {
3432 root: NodeData::create_node(NodeType::Figure),
3433 children: DomVec::from_const_slice(&[]),
3434 estimated_total_children: 0,
3435 }
3436 }
3437
3438 /// Creates a figure caption element.
3439 ///
3440 /// **Accessibility**: Provides a caption for `<figure>`. Screen readers announce this as the
3441 /// figure description.
3442 #[inline(always)]
3443 pub const fn create_figcaption() -> Self {
3444 Self {
3445 root: NodeData::create_node(NodeType::FigCaption),
3446 children: DomVec::from_const_slice(&[]),
3447 estimated_total_children: 0,
3448 }
3449 }
3450
3451 // Interactive Elements
3452
3453 /// Creates a details disclosure element.
3454 ///
3455 /// **Accessibility**: Creates a disclosure widget. Screen readers announce expanded/collapsed
3456 /// state. Must contain a `<summary>` element. Keyboard accessible by default.
3457 #[inline(always)]
3458 pub const fn create_details() -> Self {
3459 Self {
3460 root: NodeData::create_node(NodeType::Details),
3461 children: DomVec::from_const_slice(&[]),
3462 estimated_total_children: 0,
3463 }
3464 }
3465
3466 /// Creates a summary element for details.
3467 ///
3468 /// **Accessibility**: The visible heading/label for `<details>`.
3469 /// Must be the first child of details. Keyboard accessible (Enter/Space to toggle).
3470 #[inline]
3471 pub fn summary<S: Into<AzString>>(text: S) -> Self {
3472 Self {
3473 root: NodeData::create_node(NodeType::Summary),
3474 children: DomVec::from_const_slice(&[]),
3475 estimated_total_children: 0,
3476 }
3477 .with_child(Self::create_text(text))
3478 }
3479
3480 /// Creates a dialog element.
3481 ///
3482 /// **Accessibility**: Represents a modal or non-modal dialog.
3483 /// When opened as modal, focus is trapped. Use aria-label or aria-labelledby.
3484 /// Escape key should close modal dialogs.
3485 #[inline(always)]
3486 pub const fn create_dialog() -> Self {
3487 Self {
3488 root: NodeData::create_node(NodeType::Dialog),
3489 children: DomVec::from_const_slice(&[]),
3490 estimated_total_children: 0,
3491 }
3492 }
3493
3494 // Basic Structural Elements
3495
3496 #[inline(always)]
3497 pub const fn create_br() -> Self {
3498 Self {
3499 root: NodeData::create_node(NodeType::Br),
3500 children: DomVec::from_const_slice(&[]),
3501 estimated_total_children: 0,
3502 }
3503 }
3504 #[inline(always)]
3505 pub fn create_text<S: Into<AzString>>(value: S) -> Self {
3506 Self::create_node(NodeType::Text(value.into()))
3507 }
3508 #[inline(always)]
3509 pub fn create_image(image: ImageRef) -> Self {
3510 Self::create_node(NodeType::Image(image))
3511 }
3512 /// Creates an icon node with the given icon name.
3513 ///
3514 /// The icon name should match names from the icon provider (e.g., "home", "settings", "search").
3515 /// Icons are resolved to actual content (font glyph, image, etc.) during StyledDom creation
3516 /// based on the configured IconProvider.
3517 ///
3518 /// # Example
3519 /// ```rust,ignore
3520 /// Dom::create_icon("home")
3521 /// .with_class("nav-icon")
3522 /// ```
3523 #[inline(always)]
3524 pub fn create_icon<S: Into<AzString>>(icon_name: S) -> Self {
3525 Self::create_node(NodeType::Icon(icon_name.into()))
3526 }
3527
3528 #[inline(always)]
3529 pub fn create_iframe(data: RefAny, callback: impl Into<IFrameCallback>) -> Self {
3530 Self::create_node(NodeType::IFrame(IFrameNode {
3531 callback: callback.into(),
3532 refany: data,
3533 }))
3534 }
3535
3536 // Semantic HTML Elements with Accessibility Guidance
3537
3538 /// Creates a paragraph element.
3539 ///
3540 /// **Accessibility**: Paragraphs provide semantic structure for screen readers.
3541 #[inline(always)]
3542 pub const fn create_p() -> Self {
3543 Self {
3544 root: NodeData::create_node(NodeType::P),
3545 children: DomVec::from_const_slice(&[]),
3546 estimated_total_children: 0,
3547 }
3548 }
3549
3550 /// Creates a heading level 1 element.
3551 ///
3552 /// **Accessibility**: Use `h1` for the main page title. There should typically be only one `h1`
3553 /// per page.
3554 ///
3555 /// **Parameters:**
3556 /// - `text`: Heading text
3557 #[inline]
3558 pub fn h1<S: Into<AzString>>(text: S) -> Self {
3559 Self {
3560 root: NodeData::create_node(NodeType::H1),
3561 children: DomVec::from_const_slice(&[]),
3562 estimated_total_children: 0,
3563 }
3564 .with_child(Self::create_text(text))
3565 }
3566
3567 /// Creates a heading level 2 element.
3568 ///
3569 /// **Accessibility**: Use `h2` for major section headings under `h1`.
3570 ///
3571 /// **Parameters:**
3572 /// - `text`: Heading text
3573 #[inline]
3574 pub fn h2<S: Into<AzString>>(text: S) -> Self {
3575 Self {
3576 root: NodeData::create_node(NodeType::H2),
3577 children: DomVec::from_const_slice(&[]),
3578 estimated_total_children: 0,
3579 }
3580 .with_child(Self::create_text(text))
3581 }
3582
3583 /// Creates a heading level 3 element.
3584 ///
3585 /// **Accessibility**: Use `h3` for subsections under `h2`.
3586 ///
3587 /// **Parameters:**
3588 /// - `text`: Heading text
3589 #[inline]
3590 pub fn h3<S: Into<AzString>>(text: S) -> Self {
3591 Self {
3592 root: NodeData::create_node(NodeType::H3),
3593 children: DomVec::from_const_slice(&[]),
3594 estimated_total_children: 0,
3595 }
3596 .with_child(Self::create_text(text))
3597 }
3598
3599 /// Creates a heading level 4 element.
3600 ///
3601 /// **Parameters:**
3602 /// - `text`: Heading text
3603 #[inline]
3604 pub fn h4<S: Into<AzString>>(text: S) -> Self {
3605 Self {
3606 root: NodeData::create_node(NodeType::H4),
3607 children: DomVec::from_const_slice(&[]),
3608 estimated_total_children: 0,
3609 }
3610 .with_child(Self::create_text(text))
3611 }
3612
3613 /// Creates a heading level 5 element.
3614 ///
3615 /// **Parameters:**
3616 /// - `text`: Heading text
3617 #[inline]
3618 pub fn h5<S: Into<AzString>>(text: S) -> Self {
3619 Self {
3620 root: NodeData::create_node(NodeType::H5),
3621 children: DomVec::from_const_slice(&[]),
3622 estimated_total_children: 0,
3623 }
3624 .with_child(Self::create_text(text))
3625 }
3626
3627 /// Creates a heading level 6 element.
3628 ///
3629 /// **Parameters:**
3630 /// - `text`: Heading text
3631 #[inline]
3632 pub fn h6<S: Into<AzString>>(text: S) -> Self {
3633 Self {
3634 root: NodeData::create_node(NodeType::H6),
3635 children: DomVec::from_const_slice(&[]),
3636 estimated_total_children: 0,
3637 }
3638 .with_child(Self::create_text(text))
3639 }
3640
3641 /// Creates a generic inline container (span).
3642 ///
3643 /// **Accessibility**: Prefer semantic elements like `strong`, `em`, `code`, etc. when
3644 /// applicable.
3645 ///
3646 /// **Parameters:**
3647 /// - `text`: Span content
3648 #[inline]
3649 pub fn span<S: Into<AzString>>(text: S) -> Self {
3650 Self {
3651 root: NodeData::create_node(NodeType::Span),
3652 children: DomVec::from_const_slice(&[]),
3653 estimated_total_children: 0,
3654 }
3655 .with_child(Self::create_text(text))
3656 }
3657
3658 /// Creates a strongly emphasized text element (strong importance).
3659 ///
3660 /// **Accessibility**: Use `strong` instead of `b` for semantic meaning. Screen readers can
3661 /// convey the importance. Use for text that has strong importance, seriousness, or urgency.
3662 ///
3663 /// **Parameters:**
3664 /// - `text`: Text to emphasize
3665 #[inline]
3666 pub fn strong<S: Into<AzString>>(text: S) -> Self {
3667 Self {
3668 root: NodeData::create_node(NodeType::Strong),
3669 children: DomVec::from_const_slice(&[]),
3670 estimated_total_children: 0,
3671 }
3672 .with_child(Self::create_text(text))
3673 }
3674
3675 /// Creates an emphasized text element (stress emphasis).
3676 ///
3677 /// **Accessibility**: Use `em` instead of `i` for semantic meaning. Screen readers can
3678 /// convey the emphasis. Use for text that has stress emphasis.
3679 ///
3680 /// **Parameters:**
3681 /// - `text`: Text to emphasize
3682 #[inline]
3683 pub fn em<S: Into<AzString>>(text: S) -> Self {
3684 Self {
3685 root: NodeData::create_node(NodeType::Em),
3686 children: DomVec::from_const_slice(&[]),
3687 estimated_total_children: 0,
3688 }
3689 .with_child(Self::create_text(text))
3690 }
3691
3692 /// Creates a code/computer code element.
3693 ///
3694 /// **Accessibility**: Represents a fragment of computer code. Screen readers can identify
3695 /// this as code content.
3696 ///
3697 /// **Parameters:**
3698 /// - `code`: Code content
3699 #[inline]
3700 pub fn code<S: Into<AzString>>(code: S) -> Self {
3701 Self::create_node(NodeType::Code).with_child(Self::create_text(code))
3702 }
3703
3704 /// Creates a preformatted text element.
3705 ///
3706 /// **Accessibility**: Preserves whitespace and line breaks. Useful for code blocks or
3707 /// ASCII art. Screen readers will read the content as-is.
3708 ///
3709 /// **Parameters:**
3710 /// - `text`: Preformatted content
3711 #[inline]
3712 pub fn pre<S: Into<AzString>>(text: S) -> Self {
3713 Self::create_node(NodeType::Pre).with_child(Self::create_text(text))
3714 }
3715
3716 /// Creates a blockquote element.
3717 ///
3718 /// **Accessibility**: Represents a section quoted from another source. Screen readers
3719 /// can identify quoted content. Consider adding a `cite` attribute.
3720 ///
3721 /// **Parameters:**
3722 /// - `text`: Quote content
3723 #[inline]
3724 pub fn blockquote<S: Into<AzString>>(text: S) -> Self {
3725 Self::create_node(NodeType::BlockQuote).with_child(Self::create_text(text))
3726 }
3727
3728 /// Creates a citation element.
3729 ///
3730 /// **Accessibility**: Represents a reference to a creative work. Screen readers can
3731 /// identify citations.
3732 ///
3733 /// **Parameters:**
3734 /// - `text`: Citation text
3735 #[inline]
3736 pub fn cite<S: Into<AzString>>(text: S) -> Self {
3737 Self::create_node(NodeType::Cite).with_child(Self::create_text(text))
3738 }
3739
3740 /// Creates an abbreviation element.
3741 ///
3742 /// **Accessibility**: Represents an abbreviation or acronym. Use with a `title` attribute
3743 /// to provide the full expansion for screen readers.
3744 ///
3745 /// **Parameters:**
3746 /// - `abbr_text`: Abbreviated text
3747 /// - `title`: Full expansion
3748 #[inline]
3749 pub fn create_abbr(abbr_text: AzString, title: AzString) -> Self {
3750 Self::create_node(NodeType::Abbr)
3751 .with_attribute(AttributeType::Title(title))
3752 .with_child(Self::create_text(abbr_text))
3753 }
3754
3755 /// Creates a keyboard input element.
3756 ///
3757 /// **Accessibility**: Represents keyboard input or key combinations. Screen readers can
3758 /// identify keyboard instructions.
3759 ///
3760 /// **Parameters:**
3761 /// - `text`: Keyboard instruction
3762 #[inline]
3763 pub fn kbd<S: Into<AzString>>(text: S) -> Self {
3764 Self::create_node(NodeType::Kbd).with_child(Self::create_text(text))
3765 }
3766
3767 /// Creates a sample output element.
3768 ///
3769 /// **Accessibility**: Represents sample output from a program or computing system.
3770 ///
3771 /// **Parameters:**
3772 /// - `text`: Sample text
3773 #[inline]
3774 pub fn samp<S: Into<AzString>>(text: S) -> Self {
3775 Self::create_node(NodeType::Samp).with_child(Self::create_text(text))
3776 }
3777
3778 /// Creates a variable element.
3779 ///
3780 /// **Accessibility**: Represents a variable in mathematical expressions or programming.
3781 ///
3782 /// **Parameters:**
3783 /// - `text`: Variable name
3784 #[inline]
3785 pub fn var<S: Into<AzString>>(text: S) -> Self {
3786 Self::create_node(NodeType::Var).with_child(Self::create_text(text))
3787 }
3788
3789 /// Creates a subscript element.
3790 ///
3791 /// **Accessibility**: Screen readers may announce subscript formatting.
3792 ///
3793 /// **Parameters:**
3794 /// - `text`: Subscript content
3795 #[inline]
3796 pub fn sub<S: Into<AzString>>(text: S) -> Self {
3797 Self::create_node(NodeType::Sub).with_child(Self::create_text(text))
3798 }
3799
3800 /// Creates a superscript element.
3801 ///
3802 /// **Accessibility**: Screen readers may announce superscript formatting.
3803 ///
3804 /// **Parameters:**
3805 /// - `text`: Superscript content
3806 #[inline]
3807 pub fn sup<S: Into<AzString>>(text: S) -> Self {
3808 Self::create_node(NodeType::Sup).with_child(Self::create_text(text))
3809 }
3810
3811 /// Creates an underline text element.
3812 ///
3813 /// **Accessibility**: Screen readers typically don't announce underline formatting.
3814 /// Use semantic elements when possible (e.g., `<em>` for emphasis).
3815 #[inline]
3816 pub fn u<S: Into<AzString>>(text: S) -> Self {
3817 Self::create_node(NodeType::U).with_child(Self::create_text(text))
3818 }
3819
3820 /// Creates a strikethrough text element.
3821 ///
3822 /// **Accessibility**: Represents text that is no longer accurate or relevant.
3823 /// Consider using `<del>` for deleted content with datetime attribute.
3824 #[inline]
3825 pub fn s<S: Into<AzString>>(text: S) -> Self {
3826 Self::create_node(NodeType::S).with_child(Self::create_text(text))
3827 }
3828
3829 /// Creates a marked/highlighted text element.
3830 ///
3831 /// **Accessibility**: Represents text marked for reference or notation purposes.
3832 /// Screen readers may announce this as "highlighted".
3833 #[inline]
3834 pub fn mark<S: Into<AzString>>(text: S) -> Self {
3835 Self::create_node(NodeType::Mark).with_child(Self::create_text(text))
3836 }
3837
3838 /// Creates a deleted text element.
3839 ///
3840 /// **Accessibility**: Represents deleted content in document edits.
3841 /// Use with `datetime` and `cite` attributes for edit tracking.
3842 #[inline]
3843 pub fn del<S: Into<AzString>>(text: S) -> Self {
3844 Self::create_node(NodeType::Del).with_child(Self::create_text(text))
3845 }
3846
3847 /// Creates an inserted text element.
3848 ///
3849 /// **Accessibility**: Represents inserted content in document edits.
3850 /// Use with `datetime` and `cite` attributes for edit tracking.
3851 #[inline]
3852 pub fn ins<S: Into<AzString>>(text: S) -> Self {
3853 Self::create_node(NodeType::Ins).with_child(Self::create_text(text))
3854 }
3855
3856 /// Creates a definition element.
3857 ///
3858 /// **Accessibility**: Represents the defining instance of a term.
3859 /// Often used within a definition list or with `<abbr>`.
3860 #[inline]
3861 pub fn dfn<S: Into<AzString>>(text: S) -> Self {
3862 Self::create_node(NodeType::Dfn).with_child(Self::create_text(text))
3863 }
3864
3865 /// Creates a time element.
3866 ///
3867 /// **Accessibility**: Represents a specific time or date.
3868 /// Use `datetime` attribute for machine-readable format.
3869 ///
3870 /// **Parameters:**
3871 /// - `text`: Human-readable time/date
3872 /// - `datetime`: Optional machine-readable datetime
3873 #[inline]
3874 pub fn create_time(text: AzString, datetime: OptionString) -> Self {
3875 let mut element = Self::create_node(NodeType::Time).with_child(Self::create_text(text));
3876 if let OptionString::Some(dt) = datetime {
3877 element = element.with_attribute(AttributeType::Custom(AttributeNameValue {
3878 attr_name: "datetime".into(),
3879 value: dt,
3880 }));
3881 }
3882 element
3883 }
3884
3885 /// Creates a bi-directional override element.
3886 ///
3887 /// **Accessibility**: Overrides text direction. Use `dir` attribute (ltr/rtl).
3888 #[inline]
3889 pub fn bdo<S: Into<AzString>>(text: S) -> Self {
3890 Self::create_node(NodeType::Bdo).with_child(Self::create_text(text))
3891 }
3892
3893 /// Creates an anchor/hyperlink element.
3894 ///
3895 /// **Accessibility**: Always provide meaningful link text. Avoid "click here" or "read more".
3896 /// Screen readers often navigate by links, so descriptive text is crucial.
3897 ///
3898 /// **Parameters:**
3899 /// - `href`: Link destination URL
3900 /// - `label`: Link text (pass `None` for image-only links with alt text)
3901 #[inline]
3902 pub fn create_a(href: AzString, label: OptionString) -> Self {
3903 let mut link = Self::create_node(NodeType::A).with_attribute(AttributeType::Href(href));
3904 if let OptionString::Some(text) = label {
3905 link = link.with_child(Self::create_text(text));
3906 }
3907 link
3908 }
3909
3910 /// Creates a button element.
3911 ///
3912 /// **Accessibility**: Buttons are keyboard accessible by default. Always provide clear
3913 /// button text or an `aria-label` for icon-only buttons.
3914 ///
3915 /// **Parameters:**
3916 /// - `text`: Button label text
3917 #[inline]
3918 pub fn create_button(text: AzString) -> Self {
3919 Self::create_node(NodeType::Button).with_child(Self::create_text(text))
3920 }
3921
3922 /// Creates a label element for form controls.
3923 ///
3924 /// **Accessibility**: Always associate labels with form controls using `for` attribute
3925 /// or by wrapping the control. This is critical for screen reader users.
3926 ///
3927 /// **Parameters:**
3928 /// - `for_id`: ID of the associated form control
3929 /// - `text`: Label text
3930 #[inline]
3931 pub fn create_label(for_id: AzString, text: AzString) -> Self {
3932 Self::create_node(NodeType::Label)
3933 .with_attribute(AttributeType::Custom(AttributeNameValue {
3934 attr_name: "for".into(),
3935 value: for_id,
3936 }))
3937 .with_child(Self::create_text(text))
3938 }
3939
3940 /// Creates an input element.
3941 ///
3942 /// **Accessibility**: Always provide a label or `aria-label`. Set appropriate `type`
3943 /// and `aria-` attributes for the input's purpose.
3944 ///
3945 /// **Parameters:**
3946 /// - `input_type`: Input type (text, password, email, etc.)
3947 /// - `name`: Form field name
3948 /// - `label`: Accessibility label (required)
3949 #[inline]
3950 pub fn create_input(input_type: AzString, name: AzString, label: AzString) -> Self {
3951 Self::create_node(NodeType::Input)
3952 .with_attribute(AttributeType::InputType(input_type))
3953 .with_attribute(AttributeType::Name(name))
3954 .with_attribute(AttributeType::AriaLabel(label))
3955 }
3956
3957 /// Creates a textarea element.
3958 ///
3959 /// **Accessibility**: Always provide a label or `aria-label`. Consider `aria-describedby`
3960 /// for additional instructions.
3961 ///
3962 /// **Parameters:**
3963 /// - `name`: Form field name
3964 /// - `label`: Accessibility label (required)
3965 #[inline]
3966 pub fn create_textarea(name: AzString, label: AzString) -> Self {
3967 Self::create_node(NodeType::TextArea)
3968 .with_attribute(AttributeType::Name(name))
3969 .with_attribute(AttributeType::AriaLabel(label))
3970 }
3971
3972 /// Creates a select dropdown element.
3973 ///
3974 /// **Accessibility**: Always provide a label. Group related options with `optgroup`.
3975 ///
3976 /// **Parameters:**
3977 /// - `name`: Form field name
3978 /// - `label`: Accessibility label (required)
3979 #[inline]
3980 pub fn create_select(name: AzString, label: AzString) -> Self {
3981 Self::create_node(NodeType::Select)
3982 .with_attribute(AttributeType::Name(name))
3983 .with_attribute(AttributeType::AriaLabel(label))
3984 }
3985
3986 /// Creates an option element for select dropdowns.
3987 ///
3988 /// **Parameters:**
3989 /// - `value`: Option value
3990 /// - `text`: Display text
3991 #[inline]
3992 pub fn create_option(value: AzString, text: AzString) -> Self {
3993 Self::create_node(NodeType::SelectOption)
3994 .with_attribute(AttributeType::Value(value))
3995 .with_child(Self::create_text(text))
3996 }
3997
3998 /// Creates an unordered list element.
3999 ///
4000 /// **Accessibility**: Screen readers announce lists and item counts, helping users
4001 /// understand content structure.
4002 #[inline(always)]
4003 pub fn create_ul() -> Self {
4004 Self::create_node(NodeType::Ul)
4005 }
4006
4007 /// Creates an ordered list element.
4008 ///
4009 /// **Accessibility**: Screen readers announce lists and item counts, helping users
4010 /// understand content structure and numbering.
4011 #[inline(always)]
4012 pub fn create_ol() -> Self {
4013 Self::create_node(NodeType::Ol)
4014 }
4015
4016 /// Creates a list item element.
4017 ///
4018 /// **Accessibility**: Must be a child of `ul`, `ol`, or `menu`. Screen readers announce
4019 /// list item position (e.g., "2 of 5").
4020 #[inline(always)]
4021 pub fn create_li() -> Self {
4022 Self::create_node(NodeType::Li)
4023 }
4024
4025 /// Creates a table element.
4026 ///
4027 /// **Accessibility**: Use proper table structure with `thead`, `tbody`, `th`, and `td`.
4028 /// Provide a `caption` for table purpose. Use `scope` attribute on header cells.
4029 #[inline(always)]
4030 pub fn create_table() -> Self {
4031 Self::create_node(NodeType::Table)
4032 }
4033
4034 /// Creates a table caption element.
4035 ///
4036 /// **Accessibility**: Describes the purpose of the table. Screen readers announce this first.
4037 #[inline(always)]
4038 pub fn create_caption() -> Self {
4039 Self::create_node(NodeType::Caption)
4040 }
4041
4042 /// Creates a table header element.
4043 ///
4044 /// **Accessibility**: Groups header rows. Screen readers can navigate table structure.
4045 #[inline(always)]
4046 pub fn create_thead() -> Self {
4047 Self::create_node(NodeType::THead)
4048 }
4049
4050 /// Creates a table body element.
4051 ///
4052 /// **Accessibility**: Groups body rows. Screen readers can navigate table structure.
4053 #[inline(always)]
4054 pub fn create_tbody() -> Self {
4055 Self::create_node(NodeType::TBody)
4056 }
4057
4058 /// Creates a table footer element.
4059 ///
4060 /// **Accessibility**: Groups footer rows. Screen readers can navigate table structure.
4061 #[inline(always)]
4062 pub fn create_tfoot() -> Self {
4063 Self::create_node(NodeType::TFoot)
4064 }
4065
4066 /// Creates a table row element.
4067 #[inline(always)]
4068 pub fn create_tr() -> Self {
4069 Self::create_node(NodeType::Tr)
4070 }
4071
4072 /// Creates a table header cell element.
4073 ///
4074 /// **Accessibility**: Use `scope` attribute ("col" or "row") to associate headers with
4075 /// data cells. Screen readers use this to announce cell context.
4076 #[inline(always)]
4077 pub fn create_th() -> Self {
4078 Self::create_node(NodeType::Th)
4079 }
4080
4081 /// Creates a table data cell element.
4082 #[inline(always)]
4083 pub fn create_td() -> Self {
4084 Self::create_node(NodeType::Td)
4085 }
4086
4087 /// Creates a form element.
4088 ///
4089 /// **Accessibility**: Group related form controls with `fieldset` and `legend`.
4090 /// Provide clear labels for all inputs. Consider `aria-describedby` for instructions.
4091 #[inline(always)]
4092 pub fn create_form() -> Self {
4093 Self::create_node(NodeType::Form)
4094 }
4095
4096 /// Creates a fieldset element for grouping form controls.
4097 ///
4098 /// **Accessibility**: Groups related form controls. Always include a `legend` as the
4099 /// first child to describe the group. Screen readers announce the legend when entering
4100 /// the fieldset.
4101 #[inline(always)]
4102 pub fn create_fieldset() -> Self {
4103 Self::create_node(NodeType::FieldSet)
4104 }
4105
4106 /// Creates a legend element for fieldsets.
4107 ///
4108 /// **Accessibility**: Describes the purpose of a fieldset. Must be the first child of
4109 /// a fieldset. Screen readers announce this when entering the fieldset.
4110 #[inline(always)]
4111 pub fn create_legend() -> Self {
4112 Self::create_node(NodeType::Legend)
4113 }
4114
4115 /// Creates a horizontal rule element.
4116 ///
4117 /// **Accessibility**: Represents a thematic break. Screen readers may announce this as
4118 /// a separator. Consider using CSS borders for purely decorative lines.
4119 #[inline(always)]
4120 pub fn create_hr() -> Self {
4121 Self::create_node(NodeType::Hr)
4122 }
4123
4124 // Additional Element Constructors
4125
4126 /// Creates an address element.
4127 ///
4128 /// **Accessibility**: Represents contact information. Screen readers identify this
4129 /// as address content.
4130 #[inline(always)]
4131 pub const fn create_address() -> Self {
4132 Self {
4133 root: NodeData::create_node(NodeType::Address),
4134 children: DomVec::from_const_slice(&[]),
4135 estimated_total_children: 0,
4136 }
4137 }
4138
4139 /// Creates a definition list element.
4140 ///
4141 /// **Accessibility**: Screen readers announce definition lists and their structure.
4142 #[inline(always)]
4143 pub const fn create_dl() -> Self {
4144 Self {
4145 root: NodeData::create_node(NodeType::Dl),
4146 children: DomVec::from_const_slice(&[]),
4147 estimated_total_children: 0,
4148 }
4149 }
4150
4151 /// Creates a definition term element.
4152 ///
4153 /// **Accessibility**: Must be a child of `dl`. Represents the term being defined.
4154 #[inline(always)]
4155 pub const fn create_dt() -> Self {
4156 Self {
4157 root: NodeData::create_node(NodeType::Dt),
4158 children: DomVec::from_const_slice(&[]),
4159 estimated_total_children: 0,
4160 }
4161 }
4162
4163 /// Creates a definition description element.
4164 ///
4165 /// **Accessibility**: Must be a child of `dl`. Provides the definition for the term.
4166 #[inline(always)]
4167 pub const fn create_dd() -> Self {
4168 Self {
4169 root: NodeData::create_node(NodeType::Dd),
4170 children: DomVec::from_const_slice(&[]),
4171 estimated_total_children: 0,
4172 }
4173 }
4174
4175 /// Creates a table column group element.
4176 #[inline(always)]
4177 pub const fn create_colgroup() -> Self {
4178 Self {
4179 root: NodeData::create_node(NodeType::ColGroup),
4180 children: DomVec::from_const_slice(&[]),
4181 estimated_total_children: 0,
4182 }
4183 }
4184
4185 /// Creates a table column element.
4186 #[inline]
4187 pub fn create_col(span: i32) -> Self {
4188 Self::create_node(NodeType::Col).with_attribute(AttributeType::ColSpan(span))
4189 }
4190
4191 /// Creates an optgroup element for grouping select options.
4192 ///
4193 /// **Parameters:**
4194 /// - `label`: Label for the option group
4195 #[inline]
4196 pub fn create_optgroup(label: AzString) -> Self {
4197 Self::create_node(NodeType::OptGroup).with_attribute(AttributeType::AriaLabel(label))
4198 }
4199
4200 /// Creates a quotation element.
4201 ///
4202 /// **Accessibility**: Represents an inline quotation.
4203 #[inline(always)]
4204 pub const fn create_q() -> Self {
4205 Self {
4206 root: NodeData::create_node(NodeType::Q),
4207 children: DomVec::from_const_slice(&[]),
4208 estimated_total_children: 0,
4209 }
4210 }
4211
4212 /// Creates an acronym element.
4213 ///
4214 /// **Note**: Deprecated in HTML5. Consider using `abbr()` instead.
4215 #[inline(always)]
4216 pub const fn acronym() -> Self {
4217 Self {
4218 root: NodeData::create_node(NodeType::Acronym),
4219 children: DomVec::from_const_slice(&[]),
4220 estimated_total_children: 0,
4221 }
4222 }
4223
4224 /// Creates a menu element.
4225 ///
4226 /// **Accessibility**: Represents a list of commands. Similar to `<ul>` but semantic for
4227 /// toolbars/menus.
4228 #[inline(always)]
4229 pub const fn create_menu() -> Self {
4230 Self {
4231 root: NodeData::create_node(NodeType::Menu),
4232 children: DomVec::from_const_slice(&[]),
4233 estimated_total_children: 0,
4234 }
4235 }
4236
4237 /// Creates a menu item element.
4238 ///
4239 /// **Accessibility**: Represents a command in a menu. Use with appropriate role/aria
4240 /// attributes.
4241 #[inline(always)]
4242 pub const fn menuitem() -> Self {
4243 Self {
4244 root: NodeData::create_node(NodeType::MenuItem),
4245 children: DomVec::from_const_slice(&[]),
4246 estimated_total_children: 0,
4247 }
4248 }
4249
4250 /// Creates an output element.
4251 ///
4252 /// **Accessibility**: Represents the result of a calculation or user action.
4253 /// Use `for` attribute to associate with input elements. Screen readers announce updates.
4254 #[inline(always)]
4255 pub const fn create_output() -> Self {
4256 Self {
4257 root: NodeData::create_node(NodeType::Output),
4258 children: DomVec::from_const_slice(&[]),
4259 estimated_total_children: 0,
4260 }
4261 }
4262
4263 /// Creates a progress indicator element.
4264 ///
4265 /// **Accessibility**: Represents task progress. Use `value` and `max` attributes.
4266 /// Screen readers announce progress percentage. Use aria-label to describe the task.
4267 #[inline]
4268 pub fn create_progress(value: f32, max: f32) -> Self {
4269 Self::create_node(NodeType::Progress)
4270 .with_attribute(AttributeType::Custom(AttributeNameValue {
4271 attr_name: "value".into(),
4272 value: value.to_string().into(),
4273 }))
4274 .with_attribute(AttributeType::Custom(AttributeNameValue {
4275 attr_name: "max".into(),
4276 value: max.to_string().into(),
4277 }))
4278 }
4279
4280 /// Creates a meter gauge element.
4281 ///
4282 /// **Accessibility**: Represents a scalar measurement within a known range.
4283 /// Use `value`, `min`, `max`, `low`, `high`, `optimum` attributes.
4284 /// Screen readers announce the measurement. Provide aria-label for context.
4285 #[inline]
4286 pub fn create_meter(value: f32, min: f32, max: f32) -> Self {
4287 Self::create_node(NodeType::Meter)
4288 .with_attribute(AttributeType::Custom(AttributeNameValue {
4289 attr_name: "value".into(),
4290 value: value.to_string().into(),
4291 }))
4292 .with_attribute(AttributeType::Custom(AttributeNameValue {
4293 attr_name: "min".into(),
4294 value: min.to_string().into(),
4295 }))
4296 .with_attribute(AttributeType::Custom(AttributeNameValue {
4297 attr_name: "max".into(),
4298 value: max.to_string().into(),
4299 }))
4300 }
4301
4302 /// Creates a datalist element for input suggestions.
4303 ///
4304 /// **Accessibility**: Provides autocomplete options for inputs.
4305 /// Associate with input using `list` attribute. Screen readers announce available options.
4306 #[inline(always)]
4307 pub const fn create_datalist() -> Self {
4308 Self {
4309 root: NodeData::create_node(NodeType::DataList),
4310 children: DomVec::from_const_slice(&[]),
4311 estimated_total_children: 0,
4312 }
4313 }
4314
4315 // Embedded Content Elements
4316
4317 /// Creates a canvas element for graphics.
4318 ///
4319 /// **Accessibility**: Canvas content is not accessible by default.
4320 /// Always provide fallback content as children and/or detailed aria-label.
4321 /// Consider using SVG for accessible graphics when possible.
4322 #[inline(always)]
4323 pub const fn create_canvas() -> Self {
4324 Self {
4325 root: NodeData::create_node(NodeType::Canvas),
4326 children: DomVec::from_const_slice(&[]),
4327 estimated_total_children: 0,
4328 }
4329 }
4330
4331 /// Creates an object element for embedded content.
4332 ///
4333 /// **Accessibility**: Provide fallback content as children. Use aria-label to describe content.
4334 #[inline(always)]
4335 pub const fn create_object() -> Self {
4336 Self {
4337 root: NodeData::create_node(NodeType::Object),
4338 children: DomVec::from_const_slice(&[]),
4339 estimated_total_children: 0,
4340 }
4341 }
4342
4343 /// Creates a param element for object parameters.
4344 ///
4345 /// **Parameters:**
4346 /// - `name`: Parameter name
4347 /// - `value`: Parameter value
4348 #[inline]
4349 pub fn create_param(name: AzString, value: AzString) -> Self {
4350 Self::create_node(NodeType::Param)
4351 .with_attribute(AttributeType::Name(name))
4352 .with_attribute(AttributeType::Value(value))
4353 }
4354
4355 /// Creates an embed element.
4356 ///
4357 /// **Accessibility**: Provide alternative content or link. Use aria-label to describe embedded
4358 /// content.
4359 #[inline(always)]
4360 pub const fn create_embed() -> Self {
4361 Self {
4362 root: NodeData::create_node(NodeType::Embed),
4363 children: DomVec::from_const_slice(&[]),
4364 estimated_total_children: 0,
4365 }
4366 }
4367
4368 /// Creates an audio element.
4369 ///
4370 /// **Accessibility**: Always provide controls. Use `<track>` for captions/subtitles.
4371 /// Provide fallback text for unsupported browsers.
4372 #[inline(always)]
4373 pub const fn create_audio() -> Self {
4374 Self {
4375 root: NodeData::create_node(NodeType::Audio),
4376 children: DomVec::from_const_slice(&[]),
4377 estimated_total_children: 0,
4378 }
4379 }
4380
4381 /// Creates a video element.
4382 ///
4383 /// **Accessibility**: Always provide controls. Use `<track>` for
4384 /// captions/subtitles/descriptions. Provide fallback text. Consider providing transcript.
4385 #[inline(always)]
4386 pub const fn create_video() -> Self {
4387 Self {
4388 root: NodeData::create_node(NodeType::Video),
4389 children: DomVec::from_const_slice(&[]),
4390 estimated_total_children: 0,
4391 }
4392 }
4393
4394 /// Creates a source element for media.
4395 ///
4396 /// **Parameters:**
4397 /// - `src`: Media source URL
4398 /// - `media_type`: MIME type (e.g., "video/mp4", "audio/ogg")
4399 #[inline]
4400 pub fn create_source(src: AzString, media_type: AzString) -> Self {
4401 Self::create_node(NodeType::Source)
4402 .with_attribute(AttributeType::Src(src))
4403 .with_attribute(AttributeType::Custom(AttributeNameValue {
4404 attr_name: "type".into(),
4405 value: media_type,
4406 }))
4407 }
4408
4409 /// Creates a track element for media captions/subtitles.
4410 ///
4411 /// **Accessibility**: Essential for deaf/hard-of-hearing users and non-native speakers.
4412 /// Use `kind` (subtitles/captions/descriptions), `srclang`, and `label` attributes.
4413 ///
4414 /// **Parameters:**
4415 /// - `src`: Track file URL (WebVTT format)
4416 /// - `kind`: Track kind ("subtitles", "captions", "descriptions", "chapters", "metadata")
4417 #[inline]
4418 pub fn create_track(src: AzString, kind: AzString) -> Self {
4419 Self::create_node(NodeType::Track)
4420 .with_attribute(AttributeType::Src(src))
4421 .with_attribute(AttributeType::Custom(AttributeNameValue {
4422 attr_name: "kind".into(),
4423 value: kind,
4424 }))
4425 }
4426
4427 /// Creates a map element for image maps.
4428 ///
4429 /// **Accessibility**: Provide text alternatives. Ensure all areas have alt text.
4430 #[inline(always)]
4431 pub const fn create_map() -> Self {
4432 Self {
4433 root: NodeData::create_node(NodeType::Map),
4434 children: DomVec::from_const_slice(&[]),
4435 estimated_total_children: 0,
4436 }
4437 }
4438
4439 /// Creates an area element for image map regions.
4440 ///
4441 /// **Accessibility**: Always provide `alt` text describing the region/link purpose.
4442 /// Keyboard users should be able to navigate areas.
4443 #[inline(always)]
4444 pub const fn create_area() -> Self {
4445 Self {
4446 root: NodeData::create_node(NodeType::Area),
4447 children: DomVec::from_const_slice(&[]),
4448 estimated_total_children: 0,
4449 }
4450 }
4451
4452 // Metadata Elements
4453
4454 /// Creates a title element for document title.
4455 ///
4456 /// **Accessibility**: Required for all pages. Screen readers announce this first.
4457 /// Should be unique and descriptive. Keep under 60 characters.
4458 #[inline]
4459 pub fn title<S: Into<AzString>>(text: S) -> Self {
4460 Self::create_node(NodeType::Title).with_child(Self::create_text(text))
4461 }
4462
4463 /// Creates a meta element.
4464 ///
4465 /// **Accessibility**: Use for charset, viewport, description. Crucial for proper text display.
4466 #[inline(always)]
4467 pub const fn meta() -> Self {
4468 Self {
4469 root: NodeData::create_node(NodeType::Meta),
4470 children: DomVec::from_const_slice(&[]),
4471 estimated_total_children: 0,
4472 }
4473 }
4474
4475 /// Creates a link element for external resources.
4476 ///
4477 /// **Accessibility**: Use for stylesheets, icons, alternate versions.
4478 /// Provide meaningful `title` attribute for alternate stylesheets.
4479 #[inline(always)]
4480 pub const fn create_link() -> Self {
4481 Self {
4482 root: NodeData::create_node(NodeType::Link),
4483 children: DomVec::from_const_slice(&[]),
4484 estimated_total_children: 0,
4485 }
4486 }
4487
4488 /// Creates a script element.
4489 ///
4490 /// **Accessibility**: Ensure scripted content is accessible.
4491 /// Provide noscript fallbacks for critical functionality.
4492 #[inline(always)]
4493 pub const fn create_script() -> Self {
4494 Self {
4495 root: NodeData::create_node(NodeType::Script),
4496 children: DomVec::from_const_slice(&[]),
4497 estimated_total_children: 0,
4498 }
4499 }
4500
4501 /// Creates a style element for embedded CSS.
4502 ///
4503 /// **Note**: In Azul, use the `.style()` method instead for styling.
4504 /// This creates a `<style>` HTML element for embedded stylesheets.
4505 #[inline(always)]
4506 pub const fn style_element() -> Self {
4507 Self {
4508 root: NodeData::create_node(NodeType::Style),
4509 children: DomVec::from_const_slice(&[]),
4510 estimated_total_children: 0,
4511 }
4512 }
4513
4514 /// Creates a base element for document base URL.
4515 ///
4516 /// **Parameters:**
4517 /// - `href`: Base URL for relative URLs in the document
4518 #[inline]
4519 pub fn base(href: AzString) -> Self {
4520 Self::create_node(NodeType::Base).with_attribute(AttributeType::Href(href))
4521 }
4522
4523 // Advanced Constructors with Parameters
4524
4525 /// Creates a table header cell with scope.
4526 ///
4527 /// **Parameters:**
4528 /// - `scope`: "col", "row", "colgroup", or "rowgroup"
4529 /// - `text`: Header text
4530 ///
4531 /// **Accessibility**: The scope attribute is crucial for associating headers with data cells.
4532 #[inline]
4533 pub fn th_with_scope(scope: AzString, text: AzString) -> Self {
4534 Self::create_node(NodeType::Th)
4535 .with_attribute(AttributeType::Scope(scope))
4536 .with_child(Self::create_text(text))
4537 }
4538
4539 /// Creates a table data cell with text.
4540 ///
4541 /// **Parameters:**
4542 /// - `text`: Cell content
4543 #[inline]
4544 pub fn td_with_text<S: Into<AzString>>(text: S) -> Self {
4545 Self::create_td().with_child(Self::create_text(text))
4546 }
4547
4548 /// Creates a table header cell with text.
4549 ///
4550 /// **Parameters:**
4551 /// - `text`: Header text
4552 #[inline]
4553 pub fn th_with_text<S: Into<AzString>>(text: S) -> Self {
4554 Self::create_th().with_child(Self::create_text(text))
4555 }
4556
4557 /// Creates a list item with text.
4558 ///
4559 /// **Parameters:**
4560 /// - `text`: List item content
4561 #[inline]
4562 pub fn li_with_text<S: Into<AzString>>(text: S) -> Self {
4563 Self::create_li().with_child(Self::create_text(text))
4564 }
4565
4566 /// Creates a paragraph with text.
4567 ///
4568 /// **Parameters:**
4569 /// - `text`: Paragraph content
4570 #[inline]
4571 pub fn p_with_text<S: Into<AzString>>(text: S) -> Self {
4572 Self::create_p().with_child(Self::create_text(text))
4573 }
4574
4575 // Accessibility-Aware Constructors
4576 // These constructors require explicit accessibility information.
4577
4578 /// Creates a button with text content and accessibility information.
4579 ///
4580 /// **Parameters:**
4581 /// - `text`: The visible button text
4582 /// - `aria`: Accessibility information (role, description, etc.)
4583 #[inline]
4584 pub fn button_with_aria<S: Into<AzString>>(text: S, aria: SmallAriaInfo) -> Self {
4585 let mut btn = Self::create_button(text.into());
4586 btn.root.set_accessibility_info(aria.to_full_info());
4587 btn
4588 }
4589
4590 /// Creates a link (anchor) with href, text, and accessibility information.
4591 ///
4592 /// **Parameters:**
4593 /// - `href`: The link destination
4594 /// - `text`: The visible link text
4595 /// - `aria`: Accessibility information (expanded description, etc.)
4596 #[inline]
4597 pub fn link_with_aria<S1: Into<AzString>, S2: Into<AzString>>(
4598 href: S1,
4599 text: S2,
4600 aria: SmallAriaInfo,
4601 ) -> Self {
4602 let mut link = Self::create_a(href.into(), OptionString::Some(text.into()));
4603 link.root.set_accessibility_info(aria.to_full_info());
4604 link
4605 }
4606
4607 /// Creates an input element with type, name, and accessibility information.
4608 ///
4609 /// **Parameters:**
4610 /// - `input_type`: The input type (text, password, email, etc.)
4611 /// - `name`: The form field name
4612 /// - `label`: Base accessibility label
4613 /// - `aria`: Additional accessibility information (description, etc.)
4614 #[inline]
4615 pub fn input_with_aria<S1: Into<AzString>, S2: Into<AzString>, S3: Into<AzString>>(
4616 input_type: S1,
4617 name: S2,
4618 label: S3,
4619 aria: SmallAriaInfo,
4620 ) -> Self {
4621 let mut input = Self::create_input(input_type.into(), name.into(), label.into());
4622 input.root.set_accessibility_info(aria.to_full_info());
4623 input
4624 }
4625
4626 /// Creates a textarea with name and accessibility information.
4627 ///
4628 /// **Parameters:**
4629 /// - `name`: The form field name
4630 /// - `label`: Base accessibility label
4631 /// - `aria`: Additional accessibility information (description, etc.)
4632 #[inline]
4633 pub fn textarea_with_aria<S1: Into<AzString>, S2: Into<AzString>>(
4634 name: S1,
4635 label: S2,
4636 aria: SmallAriaInfo,
4637 ) -> Self {
4638 let mut textarea = Self::create_textarea(name.into(), label.into());
4639 textarea.root.set_accessibility_info(aria.to_full_info());
4640 textarea
4641 }
4642
4643 /// Creates a select dropdown with name and accessibility information.
4644 ///
4645 /// **Parameters:**
4646 /// - `name`: The form field name
4647 /// - `label`: Base accessibility label
4648 /// - `aria`: Additional accessibility information (description, etc.)
4649 #[inline]
4650 pub fn select_with_aria<S1: Into<AzString>, S2: Into<AzString>>(
4651 name: S1,
4652 label: S2,
4653 aria: SmallAriaInfo,
4654 ) -> Self {
4655 let mut select = Self::create_select(name.into(), label.into());
4656 select.root.set_accessibility_info(aria.to_full_info());
4657 select
4658 }
4659
4660 /// Creates a table with caption and accessibility information.
4661 ///
4662 /// **Parameters:**
4663 /// - `caption`: Table caption (visible title)
4664 /// - `aria`: Accessibility information describing table purpose
4665 #[inline]
4666 pub fn table_with_aria<S: Into<AzString>>(caption: S, aria: SmallAriaInfo) -> Self {
4667 let mut table = Self::create_table()
4668 .with_child(Self::create_caption().with_child(Self::create_text(caption)));
4669 table.root.set_accessibility_info(aria.to_full_info());
4670 table
4671 }
4672
4673 /// Creates a label for a form control with additional accessibility information.
4674 ///
4675 /// **Parameters:**
4676 /// - `for_id`: The ID of the associated form control
4677 /// - `text`: The visible label text
4678 /// - `aria`: Additional accessibility information (description, etc.)
4679 #[inline]
4680 pub fn label_with_aria<S1: Into<AzString>, S2: Into<AzString>>(
4681 for_id: S1,
4682 text: S2,
4683 aria: SmallAriaInfo,
4684 ) -> Self {
4685 let mut label = Self::create_label(for_id.into(), text.into());
4686 label.root.set_accessibility_info(aria.to_full_info());
4687 label
4688 }
4689
4690 /// Parse XML/XHTML string into a DOM
4691 ///
4692 /// This is a simple wrapper that parses XML and converts it to a DOM.
4693 /// For now, it just creates a text node with the content since full XML parsing
4694 /// requires the xml feature and more complex parsing logic.
4695 #[cfg(feature = "xml")]
4696 pub fn from_xml<S: AsRef<str>>(xml_str: S) -> Self {
4697 // TODO: Implement full XML parsing
4698 // For now, just create a text node showing that XML was loaded
4699 Self::create_text(format!(
4700 "XML content loaded ({} bytes)",
4701 xml_str.as_ref().len()
4702 ))
4703 }
4704
4705 /// Parse XML/XHTML string into a DOM (fallback without xml feature)
4706 #[cfg(not(feature = "xml"))]
4707 pub fn from_xml<S: AsRef<str>>(xml_str: S) -> Self {
4708 Self::create_text(format!(
4709 "XML parsing requires 'xml' feature ({} bytes)",
4710 xml_str.as_ref().len()
4711 ))
4712 }
4713
4714 // Swaps `self` with a default DOM, necessary for builder methods
4715 #[inline(always)]
4716 pub fn swap_with_default(&mut self) -> Self {
4717 let mut s = Self {
4718 root: NodeData::create_div(),
4719 children: DomVec::from_const_slice(&[]),
4720 estimated_total_children: 0,
4721 };
4722 mem::swap(&mut s, self);
4723 s
4724 }
4725
4726 #[inline]
4727 pub fn add_child(&mut self, child: Dom) {
4728 let estimated = child.estimated_total_children;
4729 let mut v: DomVec = Vec::new().into();
4730 mem::swap(&mut v, &mut self.children);
4731 let mut v = v.into_library_owned_vec();
4732 v.push(child);
4733 self.children = v.into();
4734 self.estimated_total_children += estimated + 1;
4735 }
4736
4737 #[inline(always)]
4738 pub fn set_children(&mut self, children: DomVec) {
4739 let children_estimated = children
4740 .iter()
4741 .map(|s| s.estimated_total_children + 1)
4742 .sum();
4743 self.children = children;
4744 self.estimated_total_children = children_estimated;
4745 }
4746
4747 pub fn copy_except_for_root(&mut self) -> Self {
4748 Self {
4749 root: self.root.copy_special(),
4750 children: self.children.clone(),
4751 estimated_total_children: self.estimated_total_children,
4752 }
4753 }
4754 pub fn node_count(&self) -> usize {
4755 self.estimated_total_children + 1
4756 }
4757
4758 pub fn style(&mut self, css: azul_css::css::Css) -> StyledDom {
4759 StyledDom::create(self, css)
4760 }
4761 #[inline(always)]
4762 pub fn with_children(mut self, children: DomVec) -> Self {
4763 self.set_children(children);
4764 self
4765 }
4766 #[inline(always)]
4767 pub fn with_child(mut self, child: Self) -> Self {
4768 self.add_child(child);
4769 self
4770 }
4771 #[inline(always)]
4772 pub fn with_node_type(mut self, node_type: NodeType) -> Self {
4773 self.root.set_node_type(node_type);
4774 self
4775 }
4776 #[inline(always)]
4777 pub fn with_id(mut self, id: AzString) -> Self {
4778 self.root.add_id(id);
4779 self
4780 }
4781 #[inline(always)]
4782 pub fn with_class(mut self, class: AzString) -> Self {
4783 self.root.add_class(class);
4784 self
4785 }
4786 #[inline(always)]
4787 pub fn with_callback<C: Into<CoreCallback>>(
4788 mut self,
4789 event: EventFilter,
4790 data: RefAny,
4791 callback: C,
4792 ) -> Self {
4793 self.root.add_callback(event, data, callback);
4794 self
4795 }
4796 #[inline(always)]
4797 pub fn with_css_property(mut self, prop: CssProperty) -> Self {
4798 self.root.add_css_property(prop);
4799 self
4800 }
4801 #[inline(always)]
4802 pub fn with_hover_css_property(mut self, prop: CssProperty) -> Self {
4803 self.root.add_hover_css_property(prop);
4804 self
4805 }
4806 #[inline(always)]
4807 pub fn with_active_css_property(mut self, prop: CssProperty) -> Self {
4808 self.root.add_active_css_property(prop);
4809 self
4810 }
4811 #[inline(always)]
4812 pub fn with_focus_css_property(mut self, prop: CssProperty) -> Self {
4813 self.root.add_focus_css_property(prop);
4814 self
4815 }
4816 #[inline(always)]
4817 pub fn with_tab_index(mut self, tab_index: TabIndex) -> Self {
4818 self.root.set_tab_index(tab_index);
4819 self
4820 }
4821 #[inline(always)]
4822 pub fn with_contenteditable(mut self, contenteditable: bool) -> Self {
4823 self.root.set_contenteditable(contenteditable);
4824 self
4825 }
4826 #[inline(always)]
4827 pub fn with_dataset(mut self, data: OptionRefAny) -> Self {
4828 self.root.dataset = data;
4829 self
4830 }
4831 #[inline(always)]
4832 pub fn with_ids_and_classes(mut self, ids_and_classes: IdOrClassVec) -> Self {
4833 self.root.ids_and_classes = ids_and_classes;
4834 self
4835 }
4836
4837 /// Adds an attribute to this DOM element.
4838 #[inline(always)]
4839 pub fn with_attribute(mut self, attr: AttributeType) -> Self {
4840 let mut attrs = self.root.attributes.clone();
4841 let mut v = attrs.into_library_owned_vec();
4842 v.push(attr);
4843 self.root.attributes = v.into();
4844 self
4845 }
4846
4847 /// Adds multiple attributes to this DOM element.
4848 #[inline(always)]
4849 pub fn with_attributes(mut self, attributes: AttributeVec) -> Self {
4850 self.root.attributes = attributes;
4851 self
4852 }
4853
4854 #[inline(always)]
4855 pub fn with_callbacks(mut self, callbacks: CoreCallbackDataVec) -> Self {
4856 self.root.callbacks = callbacks;
4857 self
4858 }
4859 #[inline(always)]
4860 pub fn with_css_props(mut self, css_props: CssPropertyWithConditionsVec) -> Self {
4861 self.root.css_props = css_props;
4862 self
4863 }
4864
4865 /// Assigns a stable key to the root node of this DOM for reconciliation.
4866 ///
4867 /// This is crucial for performance and correct state preservation when
4868 /// lists of items change order or items are inserted/removed.
4869 ///
4870 /// # Example
4871 /// ```rust
4872 /// Dom::create_div()
4873 /// .with_key("user-avatar-123")
4874 /// ```
4875 #[inline]
4876 pub fn with_key<K: core::hash::Hash>(mut self, key: K) -> Self {
4877 self.root.set_key(key);
4878 self
4879 }
4880
4881 /// Registers a callback to merge dataset state from the previous frame.
4882 ///
4883 /// This is used for components that maintain heavy internal state (video players,
4884 /// WebGL contexts, network connections) that should not be destroyed and recreated
4885 /// on every render frame.
4886 ///
4887 /// The callback receives both datasets as `RefAny` (cheap shallow clones) and
4888 /// returns the `RefAny` that should be used for the new node.
4889 #[inline]
4890 pub fn with_merge_callback<C: Into<DatasetMergeCallback>>(mut self, callback: C) -> Self {
4891 self.root.set_merge_callback(callback);
4892 self
4893 }
4894
4895 pub fn set_inline_style(&mut self, style: &str) {
4896 self.root.set_inline_style(style);
4897 }
4898
4899 pub fn with_inline_style(mut self, style: &str) -> Self {
4900 self.set_inline_style(style);
4901 self
4902 }
4903
4904 /// Sets inline CSS styles for the hover state on the root node
4905 pub fn set_inline_hover_style(&mut self, style: &str) {
4906 self.root.set_inline_hover_style(style);
4907 }
4908
4909 /// Builder method for setting inline CSS styles for the hover state
4910 pub fn with_inline_hover_style(mut self, style: &str) -> Self {
4911 self.set_inline_hover_style(style);
4912 self
4913 }
4914
4915 /// Sets inline CSS styles for the active state on the root node
4916 pub fn set_inline_active_style(&mut self, style: &str) {
4917 self.root.set_inline_active_style(style);
4918 }
4919
4920 /// Builder method for setting inline CSS styles for the active state
4921 pub fn with_inline_active_style(mut self, style: &str) -> Self {
4922 self.set_inline_active_style(style);
4923 self
4924 }
4925
4926 /// Sets inline CSS styles for the focus state on the root node
4927 pub fn set_inline_focus_style(&mut self, style: &str) {
4928 self.root.set_inline_focus_style(style);
4929 }
4930
4931 /// Builder method for setting inline CSS styles for the focus state
4932 pub fn with_inline_focus_style(mut self, style: &str) -> Self {
4933 self.set_inline_focus_style(style);
4934 self
4935 }
4936
4937 /// Sets the context menu for the root node
4938 #[inline]
4939 pub fn set_context_menu(&mut self, context_menu: Menu) {
4940 self.root.set_context_menu(context_menu);
4941 }
4942
4943 #[inline]
4944 pub fn with_context_menu(mut self, context_menu: Menu) -> Self {
4945 self.set_context_menu(context_menu);
4946 self
4947 }
4948
4949 /// Sets the menu bar for the root node
4950 #[inline]
4951 pub fn set_menu_bar(&mut self, menu_bar: Menu) {
4952 self.root.set_menu_bar(menu_bar);
4953 }
4954
4955 #[inline]
4956 pub fn with_menu_bar(mut self, menu_bar: Menu) -> Self {
4957 self.set_menu_bar(menu_bar);
4958 self
4959 }
4960
4961 #[inline]
4962 pub fn with_clip_mask(mut self, clip_mask: ImageMask) -> Self {
4963 self.root.set_clip_mask(clip_mask);
4964 self
4965 }
4966
4967 #[inline]
4968 pub fn with_accessibility_info(mut self, accessibility_info: AccessibilityInfo) -> Self {
4969 self.root.set_accessibility_info(accessibility_info);
4970 self
4971 }
4972
4973 pub fn fixup_children_estimated(&mut self) -> usize {
4974 if self.children.is_empty() {
4975 self.estimated_total_children = 0;
4976 } else {
4977 self.estimated_total_children = self
4978 .children
4979 .iter_mut()
4980 .map(|s| s.fixup_children_estimated() + 1)
4981 .sum();
4982 }
4983 return self.estimated_total_children;
4984 }
4985}
4986
4987impl core::iter::FromIterator<Dom> for Dom {
4988 fn from_iter<I: IntoIterator<Item = Dom>>(iter: I) -> Self {
4989 let mut estimated_total_children = 0;
4990 let children = iter
4991 .into_iter()
4992 .map(|c| {
4993 estimated_total_children += c.estimated_total_children + 1;
4994 c
4995 })
4996 .collect::<Vec<Dom>>();
4997
4998 Dom {
4999 root: NodeData::create_div(),
5000 children: children.into(),
5001 estimated_total_children,
5002 }
5003 }
5004}
5005
5006impl fmt::Debug for Dom {
5007 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5008 fn print_dom(d: &Dom, f: &mut fmt::Formatter) -> fmt::Result {
5009 write!(f, "Dom {{\r\n")?;
5010 write!(f, "\troot: {:#?}\r\n", d.root)?;
5011 write!(
5012 f,
5013 "\testimated_total_children: {:#?}\r\n",
5014 d.estimated_total_children
5015 )?;
5016 write!(f, "\tchildren: [\r\n")?;
5017 for c in d.children.iter() {
5018 print_dom(c, f)?;
5019 }
5020 write!(f, "\t]\r\n")?;
5021 write!(f, "}}\r\n")?;
5022 Ok(())
5023 }
5024
5025 print_dom(self, f)
5026 }
5027}