Skip to main content

dioxus_core/view/
traits.rs

1//! The core view abstraction: [`ViewTemplate`], [`View`], and the extension
2//! traits and helpers that turn a typed view into a [`VNode`].
3
4use crate::{DynamicValues, Template, VNode};
5use dioxus_core_template::{
6    TEMPLATE_STORAGE_DYNAMIC_CAP, TEMPLATE_STORAGE_OPS_CAP, TEMPLATE_STORAGE_STRING_CAP,
7    TemplateRawTree, TemplateStorage,
8};
9use std::marker::PhantomData;
10
11/// A type that contributes static template structure.
12pub trait ViewTemplate {
13    /// The static tree for this view type.
14    const TEMPLATE_TREE: &'static TemplateRawTree;
15}
16
17struct StaticViewTemplate<
18    V,
19    const OPS_CAP: usize,
20    const STRING_CAP: usize,
21    const DYNAMIC_CAP: usize,
22>(PhantomData<fn() -> V>);
23
24impl<V: ViewTemplate, const OPS_CAP: usize, const STRING_CAP: usize, const DYNAMIC_CAP: usize>
25    StaticViewTemplate<V, OPS_CAP, STRING_CAP, DYNAMIC_CAP>
26{
27    const TEMPLATE: &'static Template =
28        &TemplateStorage::<OPS_CAP, STRING_CAP, DYNAMIC_CAP>::build_from_tree(V::TEMPLATE_TREE)
29            .as_template();
30}
31
32impl ViewTemplate for () {
33    const TEMPLATE_TREE: &'static TemplateRawTree = &TemplateRawTree::Empty;
34}
35
36/// A typed view that can collect runtime dynamic nodes and attributes.
37pub trait View: ViewTemplate + Sized {
38    /// Push runtime dynamic nodes and attributes in template order.
39    #[inline]
40    fn push(self, _: &mut DynamicValues) {}
41}
42
43/// A typed view with a root key.
44pub struct KeyedView<V> {
45    key: Option<String>,
46    view: V,
47}
48
49impl<V: ViewTemplate> ViewTemplate for KeyedView<V> {
50    const TEMPLATE_TREE: &'static TemplateRawTree = V::TEMPLATE_TREE;
51}
52
53impl<V: View> View for KeyedView<V> {
54    #[inline]
55    fn push(self, dynamic: &mut DynamicValues) {
56        dynamic.set_key(self.key);
57        self.view.push(dynamic);
58    }
59}
60
61/// Extension methods for assigning a root key to typed views.
62pub trait ViewKeyExt: View {
63    /// Assign a root key to this view.
64    fn key(self, key: Option<String>) -> KeyedView<Self>;
65}
66
67impl<V: View> ViewKeyExt for V {
68    #[inline]
69    fn key(self, key: Option<String>) -> KeyedView<Self> {
70        KeyedView { key, view: self }
71    }
72}
73
74/// Extension methods for typed views.
75pub trait ViewExt: View {
76    /// Convert this view into a [`VNode`].
77    fn into_vnode(self) -> VNode;
78}
79
80impl<V: View> ViewExt for V {
81    #[inline]
82    fn into_vnode(self) -> VNode {
83        into_vnode_with_template(
84            self,
85            StaticViewTemplate::<
86                V,
87                TEMPLATE_STORAGE_OPS_CAP,
88                TEMPLATE_STORAGE_STRING_CAP,
89                TEMPLATE_STORAGE_DYNAMIC_CAP,
90            >::TEMPLATE,
91        )
92    }
93}
94
95/// Convert a view into a [`VNode`] using a prepared template.
96#[inline]
97fn into_vnode_with_template<V: View>(view: V, template: &Template) -> VNode {
98    let mut dynamic = DynamicValues::new();
99    view.push(&mut dynamic);
100    VNode::new(*template, dynamic)
101}
102
103/// Convert a view into a [`VNode`] using a debug-only lazy template cached per call site.
104///
105/// In dev builds the optimized template is lowered once at runtime from the view's
106/// [`ViewTemplate::TEMPLATE_TREE`] (skipping the per-`rsx!`-site const evaluation that dominates
107/// debug compile time) and cached in `cache`. Release builds use [`into_vnode_with_capacity`] and
108/// its const template instead.
109#[cfg(debug_assertions)]
110#[doc(hidden)]
111pub fn into_vnode_cached<V: View>(view: V, cache: &std::sync::OnceLock<Template>) -> VNode {
112    let template =
113        *cache.get_or_init(|| dioxus_core_template::build_runtime_template(V::TEMPLATE_TREE));
114    into_vnode_with_template(view, &template)
115}
116
117/// Convert a view into a [`VNode`] using template capacities resolved at the call site.
118pub fn into_vnode_with_capacity<
119    const OPS_CAP: usize,
120    const STRING_CAP: usize,
121    const DYNAMIC_CAP: usize,
122    V: View,
123>(
124    view: V,
125) -> VNode {
126    into_vnode_with_template(
127        view,
128        StaticViewTemplate::<V, OPS_CAP, STRING_CAP, DYNAMIC_CAP>::TEMPLATE,
129    )
130}
131
132impl View for () {}