1use std::{any::Any, sync::Arc};
2
3use zng_app::{
4 render::{FrameBuilder, FrameUpdate},
5 update::{EventUpdate, WidgetUpdates},
6 widget::{
7 WIDGET, WidgetUpdateMode,
8 info::{WidgetInfoBuilder, WidgetLayout, WidgetMeasure},
9 node::{ArcNode, ArcNodeList, BoxedUiNode, BoxedUiNodeList, NilUiNode, UiNode, UiNodeList},
10 },
11};
12use zng_app_context::LocalContext;
13use zng_unit::PxSize;
14use zng_var::{BoxedVar, IntoValue, IntoVar, Var, VarValue};
15
16use crate::{HOT_RELOAD, HOT_RELOAD_EVENT};
17
18trait Arg: Any + Send {
19 fn clone_boxed(&self) -> Box<dyn Arg>;
20 fn into_any(self: Box<Self>) -> Box<dyn Any>;
21}
22impl<T: VarValue> Arg for BoxedVar<T> {
23 fn clone_boxed(&self) -> Box<dyn Arg> {
24 Box::new(self.clone())
25 }
26
27 fn into_any(self: Box<Self>) -> Box<dyn Any> {
28 self
29 }
30}
31#[derive(Clone)]
32struct ValueArg<T>(T);
33impl<T: Clone + Send + Any> Arg for ValueArg<T> {
34 fn clone_boxed(&self) -> Box<dyn Arg> {
35 Box::new(self.clone())
36 }
37
38 fn into_any(self: Box<Self>) -> Box<dyn Any> {
39 self
40 }
41}
42impl Arg for ArcNode<BoxedUiNode> {
43 fn clone_boxed(&self) -> Box<dyn Arg> {
44 Box::new(self.clone())
45 }
46
47 fn into_any(self: Box<Self>) -> Box<dyn Any> {
48 self
49 }
50}
51impl Arg for ArcNodeList<BoxedUiNodeList> {
52 fn clone_boxed(&self) -> Box<dyn Arg> {
53 Box::new(self.clone())
54 }
55
56 fn into_any(self: Box<Self>) -> Box<dyn Any> {
57 self
58 }
59}
60
61#[doc(hidden)]
63pub struct HotNodeArgs {
64 args: Vec<Box<dyn Arg>>,
65}
66impl HotNodeArgs {
67 pub fn with_capacity(capacity: usize) -> Self {
68 Self {
69 args: Vec::with_capacity(capacity),
70 }
71 }
72
73 pub fn push_var<T: VarValue>(&mut self, arg: impl IntoVar<T>) {
74 let arg = arg.into_var().boxed();
75 self.args.push(Box::new(arg));
76 }
77
78 pub fn push_value<T: VarValue>(&mut self, arg: impl IntoValue<T>) {
79 let arg = ValueArg(arg.into());
80 self.args.push(Box::new(arg))
81 }
82
83 pub fn push_ui_node(&mut self, arg: impl UiNode) {
84 let arg = ArcNode::new(arg.boxed());
85 self.args.push(Box::new(arg))
86 }
87
88 pub fn push_ui_node_list(&mut self, arg: impl UiNodeList) {
89 let arg = ArcNodeList::new(arg.boxed());
90 self.args.push(Box::new(arg))
91 }
92
93 pub fn push_clone<T: Clone + Send + Any>(&mut self, arg: T) {
94 let arg = ValueArg(arg);
95 self.args.push(Box::new(arg));
96 }
97
98 fn pop_downcast<T: Any>(&mut self) -> T {
99 *self.args.pop().unwrap().into_any().downcast().unwrap()
100 }
101
102 pub fn pop_var<T: VarValue>(&mut self) -> BoxedVar<T> {
103 self.pop_downcast()
104 }
105
106 pub fn pop_value<T: VarValue>(&mut self) -> T {
107 self.pop_downcast::<ValueArg<T>>().0
108 }
109
110 pub fn pop_ui_node(&mut self) -> BoxedUiNode {
111 self.pop_downcast::<ArcNode<BoxedUiNode>>().take_on_init().boxed()
112 }
113
114 pub fn pop_ui_node_list(&mut self) -> BoxedUiNodeList {
115 self.pop_downcast::<ArcNodeList<BoxedUiNodeList>>().take_on_init().boxed()
116 }
117
118 pub fn pop_clone<T: Clone + Send + Any>(&mut self) -> T {
119 self.pop_downcast::<ValueArg<T>>().0
120 }
121}
122impl Clone for HotNodeArgs {
123 fn clone(&self) -> Self {
124 let mut r = Self { args: vec![] };
125 r.clone_from(self);
126 r
127 }
128
129 fn clone_from(&mut self, source: &Self) {
130 self.args.clear();
131 self.args.reserve(source.args.len());
132 for a in &source.args {
133 self.args.push(a.clone_boxed());
134 }
135 }
136}
137
138#[doc(hidden)]
143pub struct HotNodeHost {
144 manifest_dir: &'static str,
145 name: &'static str,
146 args: HotNodeArgs,
147 fallback: fn(HotNodeArgs) -> HotNode,
148 instance: HotNode,
149}
150impl HotNodeHost {
151 pub fn new(manifest_dir: &'static str, name: &'static str, args: HotNodeArgs, fallback: fn(HotNodeArgs) -> HotNode) -> Self {
152 Self {
153 manifest_dir,
154 name,
155 args,
156 fallback,
157 instance: HotNode::new(NilUiNode),
158 }
159 }
160}
161impl UiNode for HotNodeHost {
162 fn init(&mut self) {
163 WIDGET.sub_event(&HOT_RELOAD_EVENT);
164
165 let mut ctx = LocalContext::capture();
166
167 self.instance = match HOT_RELOAD.lib(self.manifest_dir) {
168 Some(lib) => match lib.instantiate(self.name, &mut ctx, self.args.clone()) {
169 Some(ok) => {
170 tracing::debug!("loaded hot `{}` in `{}`", self.name, WIDGET.trace_id());
171 ok
172 }
173 None => {
174 tracing::error!("hot node `{}` not found in `{}` library", self.name, self.manifest_dir);
175 (self.fallback)(self.args.clone())
176 }
177 },
178 None => {
179 tracing::debug!("hot lib `{}` not loaded yet", self.manifest_dir);
180 (self.fallback)(self.args.clone())
181 }
182 };
183
184 self.instance.init(&mut ctx);
185 }
186
187 fn deinit(&mut self) {
188 let mut ctx = LocalContext::capture();
189 self.instance.deinit(&mut ctx);
190 self.instance = HotNode::new(NilUiNode);
191 }
192
193 fn info(&mut self, info: &mut WidgetInfoBuilder) {
194 let mut ctx = LocalContext::capture();
195 self.instance.info(&mut ctx, info);
196 }
197
198 fn event(&mut self, update: &EventUpdate) {
199 let mut ctx = LocalContext::capture();
200 self.instance.event(&mut ctx, update);
201
202 if let Some(args) = HOT_RELOAD_EVENT.on(update) {
203 if args.lib.manifest_dir() == self.manifest_dir {
204 WIDGET.reinit();
205 tracing::debug!("reinit `{}` to hot reload `{}`", WIDGET.trace_id(), self.name);
206 }
207 }
208 }
209
210 fn update(&mut self, updates: &WidgetUpdates) {
211 let mut ctx = LocalContext::capture();
212 self.instance.update(&mut ctx, updates);
213 }
214
215 fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
216 let mut ctx = LocalContext::capture();
217 self.instance.measure(&mut ctx, wm)
218 }
219
220 fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
221 let mut ctx = LocalContext::capture();
222 self.instance.layout(&mut ctx, wl)
223 }
224
225 fn render(&mut self, frame: &mut FrameBuilder) {
226 let mut ctx = LocalContext::capture();
227 self.instance.render(&mut ctx, frame)
228 }
229
230 fn render_update(&mut self, update: &mut FrameUpdate) {
231 let mut ctx = LocalContext::capture();
232 self.instance.render_update(&mut ctx, update)
233 }
234
235 fn is_widget(&self) -> bool {
236 let mut ctx = LocalContext::capture();
237 self.instance.is_widget(&mut ctx)
238 }
239
240 fn is_nil(&self) -> bool {
241 let mut ctx = LocalContext::capture();
242 self.instance.is_nil(&mut ctx)
243 }
244
245 fn with_context<R, F>(&mut self, update_mode: WidgetUpdateMode, f: F) -> Option<R>
246 where
247 F: FnOnce() -> R,
248 {
249 let mut ctx = LocalContext::capture();
250 let mut r = None;
251 let mut f = Some(f);
252 self.instance.with_context(&mut ctx, update_mode, &mut || {
253 r = Some(f.take().unwrap()());
254 });
255 r
256 }
257}
258
259#[doc(hidden)]
261pub struct HotNode {
262 child: BoxedUiNode,
263 api: HotNodeApi,
264 pub(crate) _lib: Option<Arc<libloading::Library>>,
266}
267impl HotNode {
268 pub fn new(node: impl UiNode) -> Self {
269 Self {
270 child: node.boxed(),
271 api: HotNodeApi::capture(),
272 _lib: None,
273 }
274 }
275
276 fn init(&mut self, ctx: &mut LocalContext) {
277 (self.api.init)(&mut self.child, ctx)
278 }
279
280 fn deinit(&mut self, ctx: &mut LocalContext) {
281 (self.api.deinit)(&mut self.child, ctx)
282 }
283
284 fn info(&mut self, ctx: &mut LocalContext, info: &mut WidgetInfoBuilder) {
285 (self.api.info)(&mut self.child, ctx, info)
286 }
287
288 fn event(&mut self, ctx: &mut LocalContext, update: &EventUpdate) {
289 (self.api.event)(&mut self.child, ctx, update)
290 }
291
292 fn update(&mut self, ctx: &mut LocalContext, updates: &WidgetUpdates) {
293 (self.api.update)(&mut self.child, ctx, updates)
294 }
295
296 fn measure(&mut self, ctx: &mut LocalContext, wm: &mut WidgetMeasure) -> PxSize {
297 (self.api.measure)(&mut self.child, ctx, wm)
298 }
299
300 fn layout(&mut self, ctx: &mut LocalContext, wl: &mut WidgetLayout) -> PxSize {
301 (self.api.layout)(&mut self.child, ctx, wl)
302 }
303
304 fn render(&mut self, ctx: &mut LocalContext, frame: &mut FrameBuilder) {
305 (self.api.render)(&mut self.child, ctx, frame)
306 }
307
308 fn render_update(&mut self, ctx: &mut LocalContext, update: &mut FrameUpdate) {
309 (self.api.render_update)(&mut self.child, ctx, update)
310 }
311
312 fn is_widget(&self, ctx: &mut LocalContext) -> bool {
313 (self.api.is_widget)(&self.child, ctx)
314 }
315
316 fn is_nil(&self, ctx: &mut LocalContext) -> bool {
317 (self.api.is_nil)(&self.child, ctx)
318 }
319
320 fn with_context(&mut self, ctx: &mut LocalContext, update_mode: WidgetUpdateMode, f: &mut dyn FnMut()) {
321 (self.api.with_context)(&mut self.child, ctx, update_mode, f)
322 }
323}
324
325struct HotNodeApi {
327 init: fn(&mut BoxedUiNode, &mut LocalContext),
328 deinit: fn(&mut BoxedUiNode, &mut LocalContext),
329 info: fn(&mut BoxedUiNode, &mut LocalContext, &mut WidgetInfoBuilder),
330 event: fn(&mut BoxedUiNode, &mut LocalContext, &EventUpdate),
331 update: fn(&mut BoxedUiNode, &mut LocalContext, &WidgetUpdates),
332 measure: fn(&mut BoxedUiNode, &mut LocalContext, &mut WidgetMeasure) -> PxSize,
333 layout: fn(&mut BoxedUiNode, &mut LocalContext, &mut WidgetLayout) -> PxSize,
334 render: fn(&mut BoxedUiNode, &mut LocalContext, &mut FrameBuilder),
335 render_update: fn(&mut BoxedUiNode, &mut LocalContext, &mut FrameUpdate),
336 is_widget: fn(&BoxedUiNode, &mut LocalContext) -> bool,
337 is_nil: fn(&BoxedUiNode, &mut LocalContext) -> bool,
338 with_context: fn(&mut BoxedUiNode, &mut LocalContext, WidgetUpdateMode, &mut dyn FnMut()),
339}
340impl HotNodeApi {
341 fn init(child: &mut BoxedUiNode, ctx: &mut LocalContext) {
342 ctx.with_context(|| child.init())
343 }
344
345 fn deinit(child: &mut BoxedUiNode, ctx: &mut LocalContext) {
346 ctx.with_context(|| child.deinit())
347 }
348
349 fn info(child: &mut BoxedUiNode, ctx: &mut LocalContext, info: &mut WidgetInfoBuilder) {
350 ctx.with_context(|| child.info(info))
351 }
352
353 fn event(child: &mut BoxedUiNode, ctx: &mut LocalContext, update: &EventUpdate) {
354 ctx.with_context(|| child.event(update))
355 }
356
357 fn update(child: &mut BoxedUiNode, ctx: &mut LocalContext, updates: &WidgetUpdates) {
358 ctx.with_context(|| child.update(updates))
359 }
360
361 fn measure(child: &mut BoxedUiNode, ctx: &mut LocalContext, wm: &mut WidgetMeasure) -> PxSize {
362 ctx.with_context(|| child.measure(wm))
363 }
364
365 fn layout(child: &mut BoxedUiNode, ctx: &mut LocalContext, wl: &mut WidgetLayout) -> PxSize {
366 ctx.with_context(|| child.layout(wl))
367 }
368
369 fn render(child: &mut BoxedUiNode, ctx: &mut LocalContext, frame: &mut FrameBuilder) {
370 ctx.with_context(|| child.render(frame))
371 }
372
373 fn render_update(child: &mut BoxedUiNode, ctx: &mut LocalContext, update: &mut FrameUpdate) {
374 ctx.with_context(|| child.render_update(update))
375 }
376
377 fn is_widget(child: &BoxedUiNode, ctx: &mut LocalContext) -> bool {
378 ctx.with_context(|| child.is_widget())
379 }
380
381 fn is_nil(child: &BoxedUiNode, ctx: &mut LocalContext) -> bool {
382 ctx.with_context(|| child.is_nil())
383 }
384
385 fn with_context(child: &mut BoxedUiNode, ctx: &mut LocalContext, update_mode: WidgetUpdateMode, f: &mut dyn FnMut()) {
386 ctx.with_context(|| {
387 child.with_context(update_mode, f);
388 })
389 }
390
391 fn capture() -> Self {
392 Self {
393 init: Self::init,
394 deinit: Self::deinit,
395 info: Self::info,
396 event: Self::event,
397 update: Self::update,
398 measure: Self::measure,
399 layout: Self::layout,
400 render: Self::render,
401 render_update: Self::render_update,
402 is_widget: Self::is_widget,
403 is_nil: Self::is_nil,
404 with_context: Self::with_context,
405 }
406 }
407}