Skip to main content

makepad_gen_plugin/model/widget/handler/
all.rs

1use crate::{
2    compiler::{Context, WidgetPoll},
3    model::{
4        role::ForParent, widget::role::Role, AbsWidget, PropWidget, Widget, WidgetTemplate,
5        WidgetType,
6    },
7};
8
9use gen_analyzer::{
10    value::Bind, IdClass, Polls, Script, Style, StyleVisitor, SugarProps, Template,
11};
12use gen_utils::{common::Source, err_from_to, error::Error};
13use std::{
14    collections::HashMap,
15    sync::{Arc, RwLock},
16};
17
18pub type PropBinds = HashMap<String, Vec<PropWidget>>;
19/// 模版指针存储池
20pub type TemplatePtrs = Vec<WidgetTemplate>;
21
22pub fn all(
23    context: &mut Context,
24    source: Source,
25    template: Option<Template>,
26    script: Option<Script>,
27    style: Option<Style>,
28    is_entry: bool,
29    polls: Arc<RwLock<Polls>>,
30) -> Result<Widget, Error> {
31    // [初始化一些必要的池] ----------------------------------------------------------------------------------
32    // 用于存储脚本中可能会进行调用的Widget
33    let mut widget_poll: WidgetPoll = HashMap::new();
34    let mut template_ptrs: TemplatePtrs = vec![];
35    // [处理template] --------------------------------------------------------------------------------------
36    let template = if let Some(template) = template {
37        if let TemplateResult::Widget(template) = handle_template(
38            template,
39            style.as_ref(),
40            &mut template_ptrs,
41            &mut widget_poll,
42            &mut vec![],
43            0,
44            Role::Normal,
45        )? {
46            Some(template)
47        } else {
48            None
49        }
50    } else {
51        None
52    };
53    // [处理script] ----------------------------------------------------------------------------------------
54    let script = if let Some(script) = script {
55        Some(crate::script::Script::new(
56            script,
57            context,
58            polls,
59            &widget_poll,
60            &template_ptrs,
61            template.as_ref(),
62        )?)
63    } else {
64        if let Some(ident) = template.as_ref().map(|t| t.root_name()) {
65            Some(crate::script::Script::default(ident))
66        } else {
67            None
68        }
69    };
70
71    // [处理动态生成语法糖需要的代码] ----------------------------------------------------------------------
72    let template_ptrs = if template_ptrs.is_empty() {
73        None
74    } else {
75        Some(template_ptrs)
76    };
77
78    let mut widget = Widget {
79        source,
80        template,
81        template_ptrs,
82        script,
83        is_entry,
84        has_plugin: context.plugins.is_some(),
85    };
86
87    // 执行前需要执行default_script
88    let _ = widget.patch_or_default_script()?;
89    Ok(widget)
90}
91
92fn handle_template(
93    template: Template,
94    styles: Option<&Style>,
95    template_ptrs: &mut TemplatePtrs,
96    widget_poll: &mut WidgetPoll,
97    chain: &mut Vec<IdClass>,
98    index: usize,
99    father_role: Role,
100) -> Result<TemplateResult, Error> {
101    let is_static = template.is_static();
102    let is_define = template.is_component();
103    let Template {
104        id,
105        class,
106        as_prop,
107        name,
108        mut props,
109        callbacks,
110        inherits,
111        root,
112        children,
113        sugar_props,
114        parent,
115        binds,
116        ..
117    } = template;
118    // [绑定变量处理] ----------------------------------------------------------------------------------------
119    let mut bind_props = HashMap::new();
120    if let Some(binds) = binds.as_ref() {
121        for (k, v) in binds {
122            bind_props.insert(v.as_bind()?.ident(), k.name.to_string());
123        }
124    }
125    // [处理语法糖] -----------------------------------------------------------------------------------------
126    // [for] ------------------------------------------------------------------------------------------
127    let mut role = if let SugarProps::For(for_sign) = sugar_props {
128        if let Ok(Bind::For(bind)) = for_sign.as_bind() {
129            let mut parent: ForParent = parent.as_ref().unwrap().into();
130            parent.set_credential(father_role);
131            if let Some(id) = id.as_ref() {
132                Ok(Role::For {
133                    parent,
134                    creditial: bind,
135                    origin_pos: index,
136                    props: bind_props.clone(),
137                    children: vec![],
138                    id: id.to_string(),
139                    name: name.to_string(),
140                })
141            } else {
142                Err(err_from_to!(
143                    "GenUI Component" => "Makepad Widget, for widget need id!"
144                ))
145            }
146        } else {
147            Ok(Role::default())
148        }
149    } else {
150        Ok(Role::default())
151    }?;
152
153    let is_role_virtual = role.is_virtual();
154    // [处理inherits] --------------------------------------------------------------------------------------
155    if inherits.is_some() {
156        return Err(err_from_to!(
157            "GenUI Component" => "Makepad Widget, Static Widget has no inherits"
158        ));
159    }
160    // [当id存在时,说明有可能会进行脚本处理或有绑定变量] ----------------------------------------------------------
161    if let Some(id) = id.as_ref() {
162        let widget = AbsWidget::new(&name, props.clone());
163        widget_poll.insert(id.to_string(), widget);
164    }
165    // [处理callbacks] --------------------------------------------------------------------------------------
166    // 如果当前组件使用了as_prop,那么需要将绑定变量的值传递给父组件,并且当前组件不能调用自身的事件
167    if callbacks.is_some() {
168        if as_prop.is_some() {
169            return Err(err_from_to!(
170                "GenUI Component" => "Makepad Widget, as_prop widget can't have callback!"
171            ));
172        }
173        // 当组件有callback时,组件必须要有id,否则抛出异常
174        if id.is_none() {
175            return Err(err_from_to!(
176                "GenUI Component" => "Makepad Widget, callback widget need id!"
177            ));
178        }
179    }
180    // [处理节点, 属性, 子组件] ------------------------------------------------------------------------------
181    if let Some(styles) = styles.as_ref() {
182        let other_props = StyleVisitor::visit(styles, id.as_ref(), class.as_ref(), chain)?;
183        // 合并props
184        if !other_props.is_empty() {
185            if props.is_none() {
186                props = Some(HashMap::new());
187            }
188            if let Some(props) = props.as_mut() {
189                for p in other_props {
190                    props.extend(p);
191                }
192            }
193        }
194    }
195
196    let ty = if !is_define {
197        WidgetType::try_from((name, props, root))?
198    } else {
199        WidgetType::Define((name, props, root).try_into()?)
200    };
201
202    let children = if let Some(children) = children {
203        let mut w_children = vec![];
204        chain.push(IdClass {
205            id: id.clone(),
206            class: class.clone(),
207        });
208        for (index, child) in children.into_iter().enumerate() {
209            let w = handle_template(
210                child,
211                styles,
212                template_ptrs,
213                widget_poll,
214                chain,
215                index,
216                role.clone(),
217            )?;
218            match w {
219                TemplateResult::Widget(widget_template) => {
220                    w_children.push(widget_template);
221                }
222                TemplateResult::Role(child_role) => {
223                    role.push_child(child_role);
224                }
225            }
226        }
227        if w_children.is_empty() {
228            None
229        } else {
230            Some(w_children)
231        }
232    } else {
233        None
234    };
235
236    let binds = if bind_props.is_empty() {
237        None
238    } else {
239        Some(bind_props)
240    };
241
242    let widget = WidgetTemplate {
243        id,
244        is_root: root,
245        as_prop,
246        is_static,
247        ty,
248        children,
249        role,
250        binds,
251    };
252    if is_role_virtual {
253        let role = widget.role.clone();
254        template_ptrs.push(widget);
255        Ok(TemplateResult::Role(role))
256    } else {
257        Ok(TemplateResult::Widget(widget))
258    }
259}
260
261pub enum TemplateResult {
262    Widget(WidgetTemplate),
263    Role(Role),
264}