1use crate::color::{sRGB, sRGB32};
5use crate::component::button::Button;
6use crate::component::domain_line::DomainLine;
7use crate::component::domain_point::DomainPoint;
8use crate::component::flexbox::FlexBox;
9use crate::component::gridbox::GridBox;
10use crate::component::image::Image;
11use crate::component::line::Line;
12use crate::component::listbox::ListBox;
13use crate::component::mouse_area::MouseArea;
14use crate::component::region::Region;
15use crate::component::scroll_area::ScrollArea;
16use crate::component::text::Text;
17use crate::component::textbox::TextBox;
18use crate::component::window::Window;
19use crate::component::{ChildOf, shape};
20use crate::layout::{fixed, flex, grid, list};
21use crate::persist::FnPersistStore;
22use crate::propbag::PropBag;
23use crate::{
24 APP_SOURCE_ID, AccessCell, DAbsPoint, DAbsRect, DPoint, DRect, DValue, DataID, FnPersist2,
25 InputResult, Logical, Pixel, Rect, Relative, ScopeID, Slot, SourceID, StateMachineChild,
26 UNSIZED_AXIS,
27};
28use guillotiere::euclid::Point2D;
29use mlua::UserData;
30use mlua::prelude::*;
31use std::collections::{HashMap, HashSet};
32use std::marker::PhantomData;
33use std::rc::Rc;
34use std::sync::Arc;
35use std::sync::atomic::{AtomicU64, Ordering};
36use wide::f32x4;
37use winit::application::ApplicationHandler;
38
39use winit::dpi;
40
41const SANDBOX: &[u8] = include_bytes!("./sandbox.lua");
42const FEATHER: &[u8] = include_bytes!("./feather.lua");
43
44struct NamedChunk<'a>(&'a [u8], &'a str);
45
46impl mlua::AsChunk for NamedChunk<'static> {
47 fn name(&self) -> Option<String> {
48 Some(self.1.into())
49 }
50
51 fn source<'a>(&self) -> std::io::Result<std::borrow::Cow<'a, [u8]>> {
52 Ok(std::borrow::Cow::Borrowed(self.0))
53 }
54}
55
56fn point_to_rect<T>(pt: Point2D<f32, T>) -> Rect<T> {
57 Rect::new(pt.x, pt.y, pt.x, pt.y)
58}
59
60#[derive(Clone, Debug)]
61struct LuaSourceID(Arc<SourceID>);
62
63impl std::fmt::Display for LuaSourceID {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 self.0.fmt(f)
66 }
67}
68
69struct LuaEnum<T>(T);
70
71impl<T: TryFrom<u8>> FromLua for LuaEnum<T>
72where
73 <T as TryFrom<u8>>::Error: std::error::Error + 'static + Sync + Send,
74{
75 fn from_lua(value: LuaValue, _: &mlua::Lua) -> LuaResult<Self> {
76 T::try_from(value.as_i32().ok_or(LuaError::RuntimeError(format!(
77 "Can't convert {} to enum",
78 value.type_name()
79 )))? as u8)
80 .map_err(|e| LuaError::ExternalError(std::sync::Arc::new(e)))
81 .map(|x| LuaEnum(x))
82 }
83}
84
85impl<T: Into<u8>> IntoLua for LuaEnum<T> {
86 fn into_lua(self, _: &Lua) -> LuaResult<LuaValue> {
87 Ok(LuaValue::Integer(self.0.into().into()))
88 }
89}
90
91fn get_key<V: FromLua>(t: &LuaTable, key: &str) -> LuaResult<Option<V>> {
92 if t.contains_key(key)? {
93 Ok(Some(t.get(key)?))
94 } else {
95 Ok(None)
96 }
97}
98
99fn get_or_default<V: FromLua + Default>(t: &LuaTable, key: &str) -> LuaResult<V> {
100 Ok(get_key(t, key)?.unwrap_or_default())
101}
102
103fn get_or<V: FromLua>(t: &LuaTable, key: &str, v: V) -> LuaResult<V> {
104 Ok(get_key(t, key)?.unwrap_or(v))
105}
106
107fn get_required<V: FromLua>(t: &LuaTable, key: &str) -> LuaResult<V> {
108 if !t.contains_key(key)? {
109 Err(LuaError::RuntimeError(format!(
110 "Missing required property: {key}"
111 )))
112 } else {
113 t.get(key)
114 }
115}
116
117fn get_array_or<T: num_traits::FromPrimitive + FromLua + Clone + Copy, const N: usize>(
118 lua: &Lua,
119 t: &LuaTable,
120 key: &str,
121 d: [T; N],
122) -> LuaResult<[T; N]> {
123 Ok(if t.contains_key(key)? {
124 let v = t.get::<LuaValue>(key)?;
125 if let Some(n) = v.as_number() {
126 let num = T::from_f64(n).ok_or(LuaError::RuntimeError(format!(
127 "Failed to convert {n}, is it out of range?"
128 )))?;
129 let test: [T; N] = [num; N];
130 test
131 } else if let Some(n) = v.as_integer() {
132 let num = T::from_i64(n).ok_or(LuaError::RuntimeError(format!(
133 "Failed to convert {n}, is it out of range?"
134 )))?;
135 let test: [T; N] = [num; N];
136 test
137 } else {
138 <[T; N] as FromLua>::from_lua(v, lua)?
139 }
140 } else {
141 d
142 })
143}
144
145fn get_arg_default<V: FromLua + Default>(
146 args: &mut HashSet<&'static str>,
147 t: &LuaTable,
148 key: &'static str,
149) -> LuaResult<V> {
150 args.insert(key);
151 get_or_default(t, key)
152}
153
154fn get_arg_or<V: FromLua>(
155 args: &mut HashSet<&'static str>,
156 t: &LuaTable,
157 key: &'static str,
158 v: V,
159) -> LuaResult<V> {
160 args.insert(key);
161 get_or(t, key, v)
162}
163
164fn get_arg_required<V: FromLua>(
165 args: &mut HashSet<&'static str>,
166 t: &LuaTable,
167 key: &'static str,
168) -> LuaResult<V> {
169 args.insert(key);
170 get_required(t, key)
171}
172
173fn get_array_arg<T: num_traits::FromPrimitive + FromLua + Clone + Copy, const N: usize>(
174 args: &mut HashSet<&'static str>,
175 lua: &Lua,
176 t: &LuaTable,
177 key: &'static str,
178 d: [T; N],
179) -> LuaResult<[T; N]> {
180 args.insert(key);
181 get_array_or(lua, t, key, d)
182}
183
184fn is_dvalue(t: &LuaTable) -> LuaResult<bool> {
185 if let Some(mt) = t.metatable() {
186 Ok(mt.get::<String>("name")? == "value_mt")
187 } else {
188 Ok(false)
189 }
190}
191
192#[allow(dead_code)]
193fn dump_value(k: LuaValue, v: LuaValue, i: usize) -> LuaResult<()> {
194 if let Some(t) = v.as_table() {
195 println!("{:i$}{k:?} -", "");
196 if let Some(mt) = t.metatable() {
197 println!("{:i$} mt - {}", "", mt.get::<String>("name")?);
198 mt.for_each(|k, v| dump_value(k, v, i + 4))?;
199 }
200
201 t.for_each(|k, v| dump_value(k, v, i + 2))?;
202 } else {
203 println!("{:i$}{k:?} : {v:?}", "");
204 }
205 Ok(())
206}
207
208fn get_kind(t: &LuaTable) -> LuaResult<String> {
209 if let Some(mt) = t.metatable() {
210 if !mt.contains_key("kind")? {
211 Err(LuaError::RuntimeError("Unknown metatable".into()))
212 } else {
213 mt.get("kind")
214 }
215 } else {
216 Err(LuaError::RuntimeError(
217 "Expected type to have metatable, but no metatable was found.".into(),
218 ))
219 }
220}
221
222fn get_name(t: &LuaTable) -> LuaResult<String> {
223 if let Some(mt) = t.metatable() {
224 if !mt.contains_key("name")? {
225 Err(LuaError::RuntimeError("nameless metatable".into()))
226 } else {
227 mt.get("name")
228 }
229 } else {
230 Err(LuaError::RuntimeError(
231 "Expected type to have metatable, but no metatable was found.".into(),
232 ))
233 }
234}
235
236fn expect_name(t: &LuaTable, expect: &str) -> LuaResult<()> {
237 let name = match get_name(t) {
238 Ok(s) => s,
239 Err(_) => "[unknown table type]".into(),
240 };
241
242 if name != expect {
243 Err(LuaError::RuntimeError(format!(
244 "Expected {expect} type, but found {name}",
245 )))
246 } else {
247 Ok(())
248 }
249}
250
251impl FromLua for DValue {
252 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
253 let t = value.as_table().ok_or(LuaError::RuntimeError(format!(
254 "Expected DValue, found {}",
255 value.type_name(),
256 )))?;
257 if !is_dvalue(t)? {
258 return Err(LuaError::RuntimeError(format!(
259 "Expected DValue, found {}",
260 get_name(t)?,
261 )))?;
262 }
263
264 Ok(DValue {
265 dp: get_or_default(t, "dp")?,
266 px: get_or_default(t, "px")?,
267 rel: get_or_default(t, "rel")?,
268 })
269 }
270}
271
272#[derive(Copy, Clone, Debug, Default, PartialEq)]
273struct LuaPoint<U>(Point2D<f32, U>);
274
275impl<U> LuaPoint<U> {
276 pub const fn nan() -> Self {
277 Self(Point2D::new(f32::NAN, f32::NAN))
278 }
279}
280
281impl<U> From<LuaPoint<U>> for Point2D<f32, U> {
282 fn from(val: LuaPoint<U>) -> Self {
283 val.0
284 }
285}
286
287trait LuaKind {
288 const KIND: &str;
289}
290impl LuaKind for crate::Pixel {
291 const KIND: &str = "px";
292}
293impl LuaKind for crate::Logical {
294 const KIND: &str = "dp";
295}
296impl LuaKind for crate::Relative {
297 const KIND: &str = "rel";
298}
299
300impl<U: LuaKind> FromLua for LuaPoint<U> {
301 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
302 if let Some(v) = value.as_number() {
303 return Ok(LuaPoint(Point2D::<f32, U>::splat(v as f32)));
304 }
305 if let Some(v) = value.as_integer() {
306 return Ok(LuaPoint(Point2D::<f32, U>::splat(v as f32)));
307 }
308
309 let t = value.as_table().ok_or(LuaError::RuntimeError(format!(
310 "Expected a number or an object, but found {}",
311 value.type_name()
312 )))?;
313 if is_dvalue(t)? {
314 const TYPES: [&str; 3] = ["dp", "px", "rel"];
315
316 for ty in TYPES {
317 if t.contains_key(ty)? != (U::KIND == ty) {
318 return Err(LuaError::RuntimeError(format!(
319 "Tried to build a {}point but found a value of kind {ty}",
320 U::KIND
321 )));
322 }
323 }
324 Ok(LuaPoint(Point2D::<f32, U>::splat(t.get(U::KIND)?)))
325 } else if let Some(mt) = t.metatable() {
326 expect_name(&mt, "point_mt")?;
327
328 if get_kind(t)? == U::KIND {
329 Ok(LuaPoint(Point2D::<f32, U>::new(t.get("x")?, t.get("y")?)))
330 } else {
331 Err(LuaError::RuntimeError(format!(
332 "Tried to build a {}point but found kind {}",
333 U::KIND,
334 get_kind(t)?
335 )))
336 }
337 } else {
338 Err(LuaError::RuntimeError(format!(
339 "Found unknown table kind {} while looking for a {}point",
340 get_kind(t)?,
341 U::KIND,
342 )))
343 }
344 }
345}
346
347impl<U: LuaKind> FromLua for Rect<U> {
348 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
349 let v = value.as_table().ok_or(LuaError::RuntimeError(format!(
350 "Expected a rect object, but found {}",
351 value.type_name()
352 )))?;
353
354 if get_kind(v)? != U::KIND {
355 return Err(LuaError::RuntimeError(format!(
356 "Trying to build a {}rect but found kind {}",
357 U::KIND,
358 get_kind(v)?
359 )));
360 }
361
362 Ok(Rect::<U> {
363 v: f32x4::new(if v.contains_key("x")? || v.contains_key("y")? {
364 let x = get_or_default(v, "x")?;
365 let y = get_or_default(v, "y")?;
366 [x, y, x, y]
367 } else {
368 [
369 get_or_default(v, "left")?,
370 get_or_default(v, "top")?,
371 get_or_default(v, "right")?,
372 get_or_default(v, "bottom")?,
373 ]
374 }),
375 _unit: PhantomData,
376 })
377 }
378}
379
380impl FromLua for DAbsRect {
381 fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
382 let v = value.as_table().ok_or(LuaError::RuntimeError(format!(
383 "Expected a rect, but found {}",
384 value.type_name()
385 )))?;
386 let name = get_name(v)?;
387 if name == "value_mt" {
388 if v.contains_key("rel")? {
389 return Err(LuaError::RuntimeError(
390 "DAbsRect cannot have a relative component!".to_string(),
391 ));
392 }
393 let v = DValue::from_lua(value, lua)?;
394 let px = Rect::splat(v.px);
395 let dp = Rect::splat(v.dp);
396 Ok(DAbsRect { dp, px })
397 } else if name == "pxrect_mt" {
398 Ok(Rect::<Pixel>::from_lua(value, lua)?.into())
399 } else if name == "dprect_mt" {
400 Ok(Rect::<Logical>::from_lua(value, lua)?.into())
401 } else if name == "pxpoint_mt" {
402 Ok(point_to_rect(LuaPoint::<Pixel>::from_lua(value, lua)?.0).into())
403 } else if name == "abspoint_mt" {
404 Ok(point_to_rect(LuaPoint::<Logical>::from_lua(value, lua)?.0).into())
405 } else if name == "area_mt" {
406 let px = get_or_default(v, "px")?;
407 let dp = get_or_default(v, "dp")?;
408 if v.contains_key("rel")? {
409 return Err(LuaError::RuntimeError(
410 "DAbsRect cannot have a relative component!".to_string(),
411 ));
412 }
413 Ok(DAbsRect { dp, px })
414 } else {
415 Err(LuaError::RuntimeError(format!(
416 "Expected an AbsRect or PxRect, but found {name}",
417 )))
418 }
419 }
420}
421
422impl FromLua for DRect {
423 fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
424 let v = value.as_table().ok_or(LuaError::RuntimeError(format!(
425 "Expected a rect, but found {}",
426 value.type_name()
427 )))?;
428 let name = get_name(v)?;
429 if name == "value_mt" {
430 let v = DValue::from_lua(value, lua)?;
431 let px = Rect::splat(v.px);
432 let dp = Rect::splat(v.dp);
433 let rel = Rect::splat(v.rel);
434 Ok(DRect { dp, px, rel })
435 } else if name == "pxrect_mt" {
436 Ok(Rect::<Pixel>::from_lua(value, lua)?.into())
437 } else if name == "dprect_mt" {
438 Ok(Rect::<Logical>::from_lua(value, lua)?.into())
439 } else if name == "relrect_mt" {
440 Ok(Rect::<Relative>::from_lua(value, lua)?.into())
441 } else if name == "pxpoint_mt" {
442 Ok(point_to_rect(LuaPoint::<Pixel>::from_lua(value, lua)?.0).into())
443 } else if name == "abspoint_mt" {
444 Ok(point_to_rect(LuaPoint::<Logical>::from_lua(value, lua)?.0).into())
445 } else if name == "relpoint_mt" {
446 Ok(point_to_rect(LuaPoint::<Relative>::from_lua(value, lua)?.0).into())
447 } else if name == "area_mt" {
448 let px = get_or_default(v, "px")?;
449 let dp = get_or_default(v, "dp")?;
450 let rel = get_or_default(v, "rel")?;
451 Ok(DRect { dp, px, rel })
452 } else {
453 Err(LuaError::RuntimeError(format!(
454 "Expected a rect, but found {name}",
455 )))
456 }
457 }
458}
459
460impl FromLua for DAbsPoint {
461 fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
462 let v = value.as_table().ok_or(LuaError::RuntimeError(format!(
463 "Expected a point, but found {}",
464 value.type_name()
465 )))?;
466 let name = get_name(v)?;
467 if name == "value_mt" {
468 if v.contains_key("rel")? {
469 return Err(LuaError::RuntimeError(
470 "DAbsPoint cannot have a relative component!".to_string(),
471 ));
472 }
473 let v = DValue::from_lua(value, lua)?;
474 let px = Point2D::splat(v.px);
475 let dp = Point2D::splat(v.dp);
476 Ok(DAbsPoint { dp, px })
477 } else if name == "pxpoint_mt" {
478 Ok(LuaPoint::<Pixel>::from_lua(value, lua)?.0.into())
479 } else if name == "abspoint_mt" {
480 Ok(LuaPoint::<Logical>::from_lua(value, lua)?.0.into())
481 } else if name == "coord_mt" {
482 let px = get_or_default::<LuaPoint<Pixel>>(v, "px")?.0;
483 let dp = get_or_default::<LuaPoint<Logical>>(v, "dp")?.0;
484 if v.contains_key("rel")? {
485 return Err(LuaError::RuntimeError(
486 "DAbsPoint cannot have a relative component!".to_string(),
487 ));
488 }
489 Ok(DAbsPoint { dp, px })
490 } else {
491 Err(LuaError::RuntimeError(format!(
492 "Expected either an abs or a px point, but found {name}",
493 )))
494 }
495 }
496}
497
498impl FromLua for DPoint {
499 fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
500 let v = value.as_table().ok_or(LuaError::RuntimeError(format!(
501 "Expected a point, but found {}",
502 value.type_name()
503 )))?;
504 let name = get_name(v)?;
505 if name == "value_mt" {
506 let v = DValue::from_lua(value, lua)?;
507 let px = Point2D::splat(v.px);
508 let dp = Point2D::splat(v.dp);
509 let rel = Point2D::splat(v.rel);
510 Ok(DPoint { dp, px, rel })
511 } else if name == "pxpoint_mt" {
512 Ok(LuaPoint::<Pixel>::from_lua(value, lua)?.0.into())
513 } else if name == "abspoint_mt" {
514 Ok(LuaPoint::<Logical>::from_lua(value, lua)?.0.into())
515 } else if name == "relpoint_mt" {
516 Ok(LuaPoint::<Relative>::from_lua(value, lua)?.0.into())
517 } else if name == "coord_mt" {
518 let px = get_or_default::<LuaPoint<Pixel>>(v, "px")?.0;
519 let dp = get_or_default::<LuaPoint<Logical>>(v, "dp")?.0;
520 let rel = get_or_default::<LuaPoint<Relative>>(v, "rel")?.0;
521 Ok(DPoint { dp, px, rel })
522 } else {
523 Err(LuaError::RuntimeError(format!(
524 "Expected a point, but found {name}",
525 )))
526 }
527 }
528}
529
530struct LimitPoint(DPoint);
531
532impl FromLua for LimitPoint {
533 fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
534 let v = value.as_table().ok_or(LuaError::RuntimeError(format!(
535 "Expected a point, but found {}",
536 value.type_name()
537 )))?;
538
539 let name = get_name(v)?;
542 if name == "pxpoint_mt" {
543 Ok(LimitPoint(DPoint {
544 rel: LuaPoint::nan().0,
545 dp: LuaPoint::nan().0,
546 px: LuaPoint::<Pixel>::from_lua(value, lua)?.0,
547 }))
548 } else if name == "abspoint_mt" {
549 Ok(LimitPoint(DPoint {
550 rel: LuaPoint::nan().0,
551 dp: LuaPoint::<Logical>::from_lua(value, lua)?.0,
552 px: LuaPoint::nan().0,
553 }))
554 } else if name == "relpoint_mt" {
555 Ok(LimitPoint(DPoint {
556 rel: LuaPoint::<Relative>::from_lua(value, lua)?.0,
557 dp: LuaPoint::nan().0,
558 px: LuaPoint::nan().0,
559 }))
560 } else if name == "coord_mt" {
561 let px = get_or::<LuaPoint<Pixel>>(v, "px", LuaPoint::nan())?.0;
562 let dp = get_or::<LuaPoint<Logical>>(v, "dp", LuaPoint::nan())?.0;
563 let rel = get_or::<LuaPoint<Relative>>(v, "rel", LuaPoint::nan())?.0;
564 Ok(LimitPoint(DPoint { dp, px, rel }))
565 } else {
566 Err(LuaError::RuntimeError(format!(
567 "Expected a point, but found {name}",
568 )))
569 }
570 }
571}
572
573impl FromLua for sRGB {
574 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
575 if let Some(i) = value.as_integer() {
576 Ok(sRGB32 { rgba: i as u32 }.as_f32())
577 } else if let Some(v) = value.as_table() {
578 if v.len()? == 4 {
579 Ok(sRGB::new(v.get(1)?, v.get(2)?, v.get(3)?, v.get(4)?))
580 } else {
581 Err(LuaError::RuntimeError(format!(
582 "A color must be an array of exactly 4 numbers, declared like {{ R, G, B, A }}, but only found {}",
583 v.len()?
584 )))
585 }
586 } else {
587 Err(LuaError::RuntimeError(format!(
588 "Expected a color but found {}",
589 value.type_name()
590 )))
591 }
592 }
593}
594
595struct LuaFontFamily(cosmic_text::FamilyOwned);
596
597impl Default for LuaFontFamily {
598 fn default() -> Self {
599 Self(cosmic_text::FamilyOwned::SansSerif)
600 }
601}
602
603impl FromLua for LuaFontFamily {
604 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
605 let name =
606 value
607 .as_string()
608 .and_then(|s| s.to_str().ok())
609 .ok_or(LuaError::RuntimeError(format!(
610 "Expected a string, but found {}",
611 value.type_name()
612 )))?;
613
614 Ok(LuaFontFamily(if name.eq_ignore_ascii_case("serif") {
615 cosmic_text::FamilyOwned::Serif
616 } else if name.eq_ignore_ascii_case("cursive") {
617 cosmic_text::FamilyOwned::Cursive
618 } else if name.eq_ignore_ascii_case("fantasy") {
619 cosmic_text::FamilyOwned::Fantasy
620 } else if name.eq_ignore_ascii_case("monospace") {
621 cosmic_text::FamilyOwned::Monospace
622 } else if name.eq_ignore_ascii_case("sansserif") || name.eq_ignore_ascii_case("sans-serif")
623 {
624 cosmic_text::FamilyOwned::SansSerif
625 } else {
626 cosmic_text::FamilyOwned::Name((*name).into())
627 }))
628 }
629}
630
631pub type ComponentBag = Box<dyn crate::component::Component<Props = PropBag>>;
632
633impl<U: ?Sized> crate::component::ComponentWrap<U> for ComponentBag
634where
635 for<'a> &'a U: std::convert::From<&'a PropBag>,
636{
637 fn layout(
638 &self,
639 manager: &mut crate::StateManager,
640 driver: &crate::graphics::Driver,
641 window: &Arc<SourceID>,
642 ) -> Box<dyn crate::layout::Layout<U>> {
643 use std::ops::Deref;
644 Box::new(Box::deref(self).layout(manager, driver, window))
645 }
646}
647
648impl StateMachineChild for ComponentBag {
649 fn id(&self) -> Arc<SourceID> {
650 use std::ops::Deref;
651 Box::deref(self).id()
652 }
653
654 fn init(
655 &self,
656 driver: &std::sync::Weak<crate::graphics::Driver>,
657 ) -> Result<Box<dyn crate::component::StateMachineWrapper>, crate::Error> {
658 use std::ops::Deref;
659 Box::deref(self).init(driver)
660 }
661
662 fn apply_children(
663 &self,
664 f: &mut dyn FnMut(&dyn StateMachineChild) -> eyre::Result<()>,
665 ) -> eyre::Result<()> {
666 use std::ops::Deref;
667 Box::deref(self).apply_children(f)
668 }
669}
670
671macro_rules! gen_from_lua {
672 ($type_name:ident) => {
673 impl mlua::FromLua for $type_name {
674 #[inline]
675 fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
676 match value {
677 ::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
678 _ => Err(::mlua::Error::FromLuaConversionError {
679 from: value.type_name(),
680 to: stringify!($type_name).to_string(),
681 message: None,
682 }),
683 }
684 }
685 }
686 };
687}
688
689#[derive(Clone)]
690struct LuaDomain(std::sync::Arc<crate::CrossReferenceDomain>);
691
692impl UserData for LuaDomain {}
693gen_from_lua!(LuaDomain);
694
695impl UserData for Window {}
696gen_from_lua!(Window);
697
698impl UserData for LuaSourceID {}
699gen_from_lua!(LuaSourceID);
700
701impl UserData for Slot {
702 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
703 methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| {
704 Ok(format!("Slot #{}: {}", this.1, this.0))
705 });
706 }
707}
708gen_from_lua!(Slot);
709
710impl UserData for ComponentBag {}
711gen_from_lua!(ComponentBag);
712
713pub struct LuaPersist<AppData> {
717 pub window: LuaFunction, pub id_enter: LuaFunction,
719 pub init: Result<LuaFunction, AppData>,
720 phantom: PhantomData<AppData>,
721}
722
723impl<AppData: Clone> FnPersistStore for LuaPersist<AppData> {
724 type Store = AppData;
725}
726
727impl<AppData: Clone + FromLua + IntoLua>
728 FnPersist2<&AppData, ScopeID<'static>, im::HashMap<Arc<SourceID>, Option<Window>>>
729 for LuaPersist<AppData>
730{
731 fn init(&self) -> Self::Store {
732 let r = match &self.init {
733 Ok(init) => init.call::<AppData>(()),
734 Err(data) => Ok(data.clone()),
735 };
736
737 match r {
738 Err(LuaError::RuntimeError(s)) => panic!("{}", s),
739 Err(e) => panic!("{e:?}"),
740 Ok(v) => v,
741 }
742 }
743 fn call(
744 &mut self,
745 _: Self::Store,
746 appdata: &AppData,
747 mut id: ScopeID<'static>,
748 ) -> (Self::Store, im::HashMap<Arc<SourceID>, Option<Window>>) {
749 let mut h = im::HashMap::new();
750
751 let (store, w) = self
752 .id_enter
753 .call::<(AppData, crate::component::window::Window)>((
754 LuaSourceID(id.id().clone()),
755 self.window.clone(),
756 appdata.clone(),
757 ))
758 .unwrap();
759 h.insert(w.id().clone(), Some(w));
760 (store, h)
761 }
762}
763
764enum LuaDualPoint {
765 Px(Point2D<f32, Pixel>),
766 Dp(Point2D<f32, Logical>),
767}
768
769impl FromLua for LuaDualPoint {
770 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
771 let t = value.as_table().ok_or(LuaError::RuntimeError(format!(
772 "Expected a 2D point, but found {}",
773 value.type_name()
774 )))?;
775
776 if is_dvalue(t)? {
777 if t.contains_key("dp")? && !t.contains_key("px")? {
778 Ok(LuaDualPoint::Dp(Point2D::<f32, Logical>::splat(
779 t.get("dp")?,
780 )))
781 } else if t.contains_key("px")? && !t.contains_key("dp")? {
782 Ok(LuaDualPoint::Px(Point2D::<f32, Pixel>::splat(t.get("px")?)))
783 } else {
784 Err(LuaError::RuntimeError("This property only accepts a point that is either abs or px, not relative or a combination.".to_string()))
785 }
786 } else if t.contains_key("dp")? && !t.contains_key("px")? {
787 Ok(LuaDualPoint::Dp(t.get::<LuaPoint<Logical>>("dp")?.0))
788 } else if t.contains_key("px")? && !t.contains_key("dp")? {
789 Ok(LuaDualPoint::Px(t.get::<LuaPoint<Pixel>>("px")?.0))
790 } else if get_kind(t)? == "px" {
791 Ok(LuaDualPoint::Px(Point2D::<f32, Pixel>::new(
792 t.get("x")?,
793 t.get("y")?,
794 )))
795 } else if get_kind(t)? == "dp" {
796 Ok(LuaDualPoint::Dp(Point2D::<f32, Logical>::new(
797 t.get("x")?,
798 t.get("y")?,
799 )))
800 } else {
801 Err(LuaError::RuntimeError("This property only accepts a point that is either abs or px, not relative or a combination.".to_string()))
802 }
803 }
804}
805
806fn load_prop<T: mlua::FromLua>(
807 f: fn(&mut PropBag, T) -> Option<T>,
808 bag: &mut PropBag,
809 props: &LuaTable,
810 name: &str,
811) -> LuaResult<()> {
812 if props.contains_key(name)? {
813 f(bag, props.get(name)?);
814 }
815 Ok(())
816}
817
818#[inline]
819fn replace_unsized<U>(rect: &mut Rect<U>) {
820 rect.v = f32x4::new(
821 rect.v
822 .to_array()
823 .map(|x| if x.is_nan() { UNSIZED_AXIS } else { x }),
824 );
825}
826
827#[inline]
828fn replace_unsized_drect(mut rect: DRect) -> DRect {
829 replace_unsized(&mut rect.dp);
830 replace_unsized(&mut rect.px);
831 replace_unsized(&mut rect.rel);
832 rect
833}
834
835#[inline]
836fn replace_limit<U>(p: &mut Point2D<f32, U>, bound: f32) {
837 if p.x.is_nan() {
838 p.x = bound
839 }
840 if p.y.is_nan() {
841 p.y = bound
842 }
843}
844
845#[inline]
846fn replace_limit_dpoint(mut p: DPoint, bound: f32) -> DPoint {
847 replace_limit(&mut p.dp, bound);
848 replace_limit(&mut p.px, bound);
849 replace_limit(&mut p.rel, bound);
850 p
851}
852
853impl FromLua for PropBag {
854 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
855 let props = value.as_table().ok_or(LuaError::RuntimeError(format!(
856 "Expected PropBag, got {}",
857 value.type_name(),
858 )))?;
859 let mut bag = PropBag::new();
860
861 load_prop(PropBag::set_wrap, &mut bag, props, "wrap")?;
862 load_prop(PropBag::set_zindex, &mut bag, props, "zindex")?;
863 load_prop(PropBag::set_order, &mut bag, props, "order")?;
864 load_prop(PropBag::set_grow, &mut bag, props, "grow")?;
865 load_prop(PropBag::set_shrink, &mut bag, props, "shrink")?;
866 load_prop(PropBag::set_basis, &mut bag, props, "basis")?;
867 load_prop(PropBag::set_padding, &mut bag, props, "padding")?;
868 load_prop(PropBag::set_margin, &mut bag, props, "margin")?;
869 load_prop(PropBag::set_anchor, &mut bag, props, "anchor")?;
870
871 if props.contains_key("area")? {
872 bag.set_area(replace_unsized_drect(props.get::<DRect>("area")?));
873 }
874
875 let mut limits: crate::DLimits = Default::default();
876 let mut rlimits: crate::Limits<Relative> = Default::default();
877
878 if props.contains_key("minsize")? {
879 let p = replace_limit_dpoint(props.get::<LimitPoint>("minsize")?.0, f32::NEG_INFINITY);
880 limits.dp.set_min(p.dp.to_vector().to_size());
881 limits.px.set_min(p.px.to_vector().to_size());
882 rlimits.set_min(p.rel.to_vector().to_size());
883 }
884
885 if props.contains_key("maxsize")? {
886 let p = replace_limit_dpoint(props.get::<LimitPoint>("maxsize")?.0, f32::INFINITY);
887 limits.dp.set_max(p.dp.to_vector().to_size());
888 limits.px.set_max(p.px.to_vector().to_size());
889 rlimits.set_max(p.rel.to_vector().to_size());
890 }
891
892 bag.set_limits(limits);
893 bag.set_rlimits(rlimits);
894
895 if props.contains_key("direction")? {
896 bag.set_direction(props.get::<LuaEnum<crate::RowDirection>>("direction")?.0);
897 }
898
899 if props.contains_key("justify")? {
900 bag.set_justify(props.get::<LuaEnum<flex::FlexJustify>>("justify")?.0);
901 }
902
903 if props.contains_key("align")? {
904 bag.set_align(props.get::<LuaEnum<flex::FlexJustify>>("align")?.0);
905 }
906
907 if props.contains_key("domain")? {
908 bag.set_domain(props.get::<LuaDomain>("domain")?.0);
909 }
910
911 if props.contains_key("obstacles")? {
912 bag.set_obstacles(props.get::<Vec<DAbsRect>>("obstacles")?.as_slice());
913 }
914
915 if props.contains_key("dim")? {
916 bag.set_dim(props.get::<LuaPoint<Pixel>>("dim")?.0.to_vector().to_size());
917 }
918
919 if props.contains_key("rows")? {
920 bag.set_rows(props.get::<Vec<DValue>>("rows")?.as_slice());
921 }
922
923 if props.contains_key("columns")? {
924 bag.set_columns(props.get::<Vec<DValue>>("columns")?.as_slice());
925 }
926
927 if props.contains_key("spacing")? {
928 bag.set_spacing(props.get::<DPoint>("spacing")?);
929 }
930
931 if props.contains_key("coord")? {
932 let coords = props.get::<Vec<usize>>("coord")?;
933 bag.set_coord((coords[0], coords[1]));
934 }
935
936 if props.contains_key("span")? {
937 let coords = props.get::<Vec<usize>>("span")?;
938 bag.set_span((coords[0], coords[1]));
939 }
940
941 Ok(bag)
942 }
943}
944
945fn prop_no_children(t: &LuaTable) -> LuaResult<PropBag> {
946 let count = t.len()?;
947 if count > 0 {
948 return Err(LuaError::RuntimeError(format!(
949 "Found {count} child components for a component that doesn't accept children!"
950 )));
951 }
952
953 let bag: PropBag = get_required(t, "props")?;
954 Ok(bag)
955}
956
957fn push_child<D: crate::layout::Desc + ?Sized>(
958 t: &LuaTable,
959 i: i64,
960 children: &mut im::Vector<Option<Box<ChildOf<D>>>>,
961) -> LuaResult<()>
962where
963 std::boxed::Box<dyn crate::component::Component<Props = PropBag>>:
964 crate::component::ComponentWrap<<D as crate::layout::Desc>::Child>,
965{
966 let v = t.get::<LuaValue>(i)?;
967 if v.is_nil() {
968 return Ok(());
969 } else if let Some(inner) = v.as_table()
970 && let Some(mt) = inner.metatable()
971 && let Ok(s) = mt.get::<String>("kind")
972 && s == "multicomponent_mt"
973 {
974 for j in 1..=inner.len()? {
975 push_child::<D>(inner, j, children)?;
976 }
977 } else {
978 let component: ComponentBag = t.get(i)?;
979 children.push_back(Some(Box::new(component)));
980 }
981
982 Ok(())
983}
984
985#[allow(clippy::type_complexity)]
986fn prop_children<D: crate::layout::Desc + ?Sized>(
987 t: &LuaTable,
988) -> LuaResult<(im::Vector<Option<Box<ChildOf<D>>>>, PropBag)>
989where
990 std::boxed::Box<dyn crate::component::Component<Props = PropBag>>:
991 crate::component::ComponentWrap<<D as crate::layout::Desc>::Child>,
992{
993 let mut children: im::Vector<Option<Box<ChildOf<D>>>> = im::Vector::new();
994
995 for i in 1..=t.len()? {
996 push_child::<D>(t, i, &mut children)?;
997 }
998
999 let bag: PropBag = get_required(t, "props")?;
1000 Ok((children, bag))
1001}
1002
1003fn check_args(name: &str, t: LuaTable, v: HashSet<&'static str>) -> LuaResult<()> {
1004 for pair in t.pairs() {
1005 let (k, _): (LuaValue, LuaValue) = pair?;
1006 if k.as_integer().is_none()
1007 && k.as_number().is_none()
1008 && let Some(s) = k.as_string()
1009 {
1010 let str = s.to_string_lossy();
1011 if !v.contains(str.as_str()) {
1012 return Err(LuaError::FromLuaConversionError {
1013 from: "[attributes table]",
1014 to: name.to_string(),
1015 message: Some(format!("Unexpected key {str} in attributes table")),
1016 });
1017 }
1018 }
1019 }
1020
1021 Ok(())
1022}
1023
1024fn create_button(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1025 let (children, bag) = prop_children::<dyn fixed::Prop>(&body)?;
1026
1027 let mut args = HashSet::from_iter(["props"]);
1028 let onclick = get_arg_required(&mut args, &body, "onclick")?;
1029 check_args("button", body, args)?;
1030
1031 let res: LuaResult<ComponentBag> = Ok(Box::new(Button::<PropBag>::new(
1032 id.0, bag, onclick, children,
1033 )));
1034 res
1035}
1036
1037fn create_domain_line(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1038 let bag = prop_no_children(&body)?;
1039
1040 let mut args = HashSet::from_iter(["props"]);
1041 let domain: LuaDomain = get_arg_required(&mut args, &body, "domain")?;
1042 let start: LuaSourceID = get_arg_required(&mut args, &body, "start")?;
1043 let end: LuaSourceID = get_arg_required(&mut args, &body, "end")?;
1044 let fill = get_arg_default(&mut args, &body, "fill")?;
1045 check_args("domain-line", body, args)?;
1046
1047 let res: LuaResult<ComponentBag> = Ok(Box::new(DomainLine::<PropBag> {
1048 id: id.0,
1049 domain: domain.0,
1050 start: start.0,
1051 end: end.0,
1052 props: bag.into(),
1053 fill,
1054 }));
1055 res
1056}
1057
1058fn create_domain_point(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1059 let bag = prop_no_children(&body)?;
1060
1061 let args = HashSet::from_iter(["props"]);
1062 check_args("domain-point", body, args)?;
1063
1064 let res: LuaResult<ComponentBag> = Ok(Box::new(DomainPoint::<PropBag>::new(id.0, bag)));
1065 res
1066}
1067
1068fn create_flexbox(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1069 let (children, bag) = prop_children::<dyn flex::Prop>(&body)?;
1070 let args = HashSet::from_iter(["props"]);
1071 check_args("flexbox", body, args)?;
1072
1073 let res: LuaResult<ComponentBag> = Ok(Box::new(FlexBox::<PropBag>::new(id.0, bag, children)));
1074 res
1075}
1076
1077fn create_gridbox(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1078 let (children, bag) = prop_children::<dyn grid::Prop>(&body)?;
1079 let args = HashSet::from_iter(["props"]);
1080 check_args("gridbox", body, args)?;
1081
1082 let res: LuaResult<ComponentBag> = Ok(Box::new(GridBox::<PropBag>::new(id.0, bag, children)));
1083 res
1084}
1085
1086fn create_image(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1087 let bag = prop_no_children(&body)?;
1088
1089 let mut args = HashSet::from_iter(["props"]);
1090 let resource: String = get_arg_required(&mut args, &body, "resource")?;
1091 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1092 let dynamic: bool = get_arg_default(&mut args, &body, "dynamic")?;
1093 check_args("image", body, args)?;
1094
1095 let location = std::path::PathBuf::from(resource);
1096
1097 let res: LuaResult<ComponentBag> = Ok(Box::new(Image::<PropBag>::new(
1098 id.0, bag, &location, size, dynamic,
1099 )));
1100 res
1101}
1102
1103fn create_line(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1104 let bag = prop_no_children(&body)?;
1105
1106 let mut args = HashSet::from_iter(["props"]);
1107 let start: LuaPoint<Pixel> = get_arg_required(&mut args, &body, "start")?;
1108 let end: LuaPoint<Pixel> = get_arg_required(&mut args, &body, "end")?;
1109 let fill = get_arg_default(&mut args, &body, "fill")?;
1110 check_args("line", body, args)?;
1111
1112 let res: LuaResult<ComponentBag> = Ok(Box::new(Line::<PropBag> {
1113 id: id.0,
1114 start: start.0,
1115 end: end.0,
1116 props: bag.into(),
1117 fill,
1118 }));
1119 res
1120}
1121
1122fn create_listbox(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1123 let (children, bag) = prop_children::<dyn list::Prop>(&body)?;
1124 let args = HashSet::from_iter(["props"]);
1125 check_args("listbox", body, args)?;
1126
1127 let res: LuaResult<ComponentBag> = Ok(Box::new(ListBox::<PropBag>::new(id.0, bag, children)));
1128 res
1129}
1130
1131fn create_mousearea(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1132 let bag = prop_no_children(&body)?;
1133
1134 let mut args = HashSet::from_iter(["props"]);
1135 let deadzone = get_arg_or(&mut args, &body, "deadzone", f32::INFINITY)?;
1136 let onclick = get_arg_default(&mut args, &body, "onclick")?;
1137 let ondblclick = get_arg_default(&mut args, &body, "ondblclick")?;
1138 let ondrag = get_arg_default(&mut args, &body, "ondrag")?;
1139 check_args("mousearea", body, args)?;
1140
1141 let res: LuaResult<ComponentBag> = Ok(Box::new(MouseArea::<PropBag>::new(
1142 id.0,
1143 bag,
1144 Some(deadzone),
1145 [onclick, ondblclick, ondrag, None, None, None],
1146 )));
1147 res
1148}
1149
1150fn create_region(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1151 let (children, bag) = prop_children::<dyn fixed::Prop>(&body)?;
1152 let mut args = HashSet::from_iter(["props"]);
1153 let color: Option<sRGB> = get_arg_default(&mut args, &body, "color")?;
1154 let rotation: Option<f32> = get_arg_default(&mut args, &body, "rotation")?;
1155 check_args("region", body, args)?;
1156
1157 Ok(Box::new(if color.is_some() || rotation.is_some() {
1158 Region::<PropBag>::new_layer(
1159 id.0,
1160 bag,
1161 color.unwrap_or(sRGB::white()).as_32bit(),
1162 rotation.unwrap_or_default(),
1163 children,
1164 )
1165 } else {
1166 Region::<PropBag>::new(id.0, bag, children)
1167 }))
1168}
1169
1170fn create_scrollarea(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1171 let (children, bag) = prop_children::<dyn fixed::Prop>(&body)?;
1172 let mut args = HashSet::from_iter(["props"]);
1173 let stepx = get_arg_default(&mut args, &body, "stepx")?;
1174 let stepy = get_arg_default(&mut args, &body, "stepy")?;
1175 let extension = get_arg_default(&mut args, &body, "extension")?;
1176 let onscroll = get_arg_default(&mut args, &body, "onscroll")?;
1177 check_args("scrollarea", body, args)?;
1178
1179 let res: LuaResult<ComponentBag> = Ok(Box::new(ScrollArea::<PropBag>::new(
1180 id.0,
1181 bag,
1182 (stepx, stepy),
1183 extension,
1184 children,
1185 [onscroll],
1186 )));
1187 res
1188}
1189
1190fn create_round_rect(lua: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1191 let bag = prop_no_children(&body)?;
1192
1193 let mut args: HashSet<&'static str> = HashSet::from_iter(["props"]);
1194 let border = get_arg_default(&mut args, &body, "border")?;
1195 let blur = get_arg_default(&mut args, &body, "blur")?;
1196 let fill = get_arg_default(&mut args, &body, "fill")?;
1197 let outline = get_arg_default(&mut args, &body, "outline")?;
1198 let corners = get_array_arg(&mut args, lua, &body, "corners", [0.0; 4])?;
1199 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1200 check_args("round_rect", body, args)?;
1201
1202 Ok(Box::new(shape::round_rect(
1203 id.0,
1204 bag,
1205 border,
1206 blur,
1207 wide::f32x4::new(corners),
1208 fill,
1209 outline,
1210 size,
1211 )))
1212}
1213
1214fn create_arc(lua: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1215 let bag = prop_no_children(&body)?;
1216
1217 let mut args: HashSet<&'static str> = HashSet::from_iter(["props"]);
1218 let border = get_arg_default(&mut args, &body, "border")?;
1219 let blur = get_arg_default(&mut args, &body, "blur")?;
1220 let fill = get_arg_default(&mut args, &body, "fill")?;
1221 let outline = get_arg_default(&mut args, &body, "outline")?;
1222 let angles = get_array_arg(&mut args, lua, &body, "angles", [0.0; 2])?;
1223 let inner_radius = get_arg_default(&mut args, &body, "innerRadius")?;
1224 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1225 check_args("arc", body, args)?;
1226
1227 Ok(Box::new(shape::arcs(
1228 id.0,
1229 bag,
1230 border,
1231 blur,
1232 inner_radius,
1233 angles,
1234 fill,
1235 outline,
1236 size,
1237 )))
1238}
1239
1240fn create_triangle(lua: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1241 let bag = prop_no_children(&body)?;
1242
1243 let mut args: HashSet<&'static str> = HashSet::from_iter(["props"]);
1244 let border = get_arg_default(&mut args, &body, "border")?;
1245 let blur = get_arg_default(&mut args, &body, "blur")?;
1246 let fill = get_arg_default(&mut args, &body, "fill")?;
1247 let outline = get_arg_default(&mut args, &body, "outline")?;
1248 let corners = get_array_arg(&mut args, lua, &body, "corners", [0.0; 3])?;
1249 let offset = get_arg_default(&mut args, &body, "offset")?;
1250 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1251 check_args("triangle", body, args)?;
1252
1253 Ok(Box::new(shape::triangle(
1254 id.0, bag, border, blur, corners, offset, fill, outline, size,
1255 )))
1256}
1257
1258fn create_circle(lua: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1259 let bag = prop_no_children(&body)?;
1260
1261 let mut args: HashSet<&'static str> = HashSet::from_iter(["props"]);
1262 let border = get_arg_default(&mut args, &body, "border")?;
1263 let blur = get_arg_default(&mut args, &body, "blur")?;
1264 let fill = get_arg_default(&mut args, &body, "fill")?;
1265 let outline = get_arg_default(&mut args, &body, "outline")?;
1266 let radii = get_array_arg(&mut args, lua, &body, "radii", [0.0; 2])?;
1267 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1268 check_args("circle", body, args)?;
1269
1270 Ok(Box::new(shape::circle(
1271 id.0, bag, border, blur, radii, fill, outline, size,
1272 )))
1273}
1274
1275const DEFAULT_LINE_HEIGHT: f32 = 1.2;
1278const DEFAULT_FONT_SIZE: f32 = 14.0;
1281
1282fn to_style(style: u8) -> LuaResult<cosmic_text::Style> {
1283 match style {
1284 0 => Ok(cosmic_text::Style::Normal),
1285 1 => Ok(cosmic_text::Style::Italic),
1286 2 => Ok(cosmic_text::Style::Oblique),
1287 _ => Err(LuaError::RuntimeError(format!(
1288 "{style} is not a valid style enum value!"
1289 ))),
1290 }
1291}
1292
1293fn to_wrap(wrap: u8) -> LuaResult<cosmic_text::Wrap> {
1294 match wrap {
1295 0 => Ok(cosmic_text::Wrap::None),
1296 1 => Ok(cosmic_text::Wrap::Glyph),
1297 2 => Ok(cosmic_text::Wrap::Word),
1298 3 => Ok(cosmic_text::Wrap::WordOrGlyph),
1299 _ => Err(LuaError::RuntimeError(format!(
1300 "{wrap} is not a valid wrap enum value!"
1301 ))),
1302 }
1303}
1304
1305fn to_align(align: Option<u8>) -> LuaResult<Option<cosmic_text::Align>> {
1306 if let Some(a) = align {
1307 Ok(Some(match a {
1308 0 => cosmic_text::Align::Left,
1309 1 => cosmic_text::Align::Right,
1310 2 => cosmic_text::Align::Center,
1311 3 => cosmic_text::Align::Justified,
1312 4 => cosmic_text::Align::End,
1313 _ => {
1314 return Err(LuaError::RuntimeError(format!(
1315 "{a} is not a valid align enum value!"
1316 )));
1317 }
1318 }))
1319 } else {
1320 Ok(None)
1321 }
1322}
1323
1324fn create_text(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1325 let bag = prop_no_children(&body)?;
1326
1327 let mut args = HashSet::from_iter(["props"]);
1328 let text = get_arg_required(&mut args, &body, "text")?;
1329 let font_size = get_arg_or(&mut args, &body, "fontsize", DEFAULT_FONT_SIZE)?;
1330 let line_height = get_arg_or(
1331 &mut args,
1332 &body,
1333 "lineheight",
1334 font_size * DEFAULT_LINE_HEIGHT,
1335 )?;
1336 let font: LuaFontFamily = get_arg_default(&mut args, &body, "font")?;
1337 let color = get_arg_required(&mut args, &body, "color")?;
1338 let weight: u16 = get_arg_or(&mut args, &body, "weight", cosmic_text::Weight::NORMAL.0)?;
1339 let style: u8 = get_arg_default(&mut args, &body, "style")?;
1340 let wrap: u8 = get_arg_default(&mut args, &body, "wrap")?;
1341 let align: Option<u8> = get_arg_default(&mut args, &body, "align")?;
1342 check_args("text", body, args)?;
1343
1344 let style = to_style(style)?;
1345 let wrap = to_wrap(wrap)?;
1346 let align = to_align(align)?;
1347
1348 Ok(Box::new(Text::<PropBag>::new(
1349 id.0,
1350 bag,
1351 font_size,
1352 line_height,
1353 text,
1354 font.0,
1355 color,
1356 cosmic_text::Weight(weight),
1357 style,
1358 wrap,
1359 align,
1360 )))
1361}
1362
1363fn create_textbox(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1364 let bag = prop_no_children(&body)?;
1365
1366 let mut args = HashSet::from_iter(["props"]);
1367 let font_size = get_arg_or(&mut args, &body, "fontsize", DEFAULT_FONT_SIZE)?;
1368 let line_height = get_arg_or(
1369 &mut args,
1370 &body,
1371 "lineheight",
1372 font_size * DEFAULT_LINE_HEIGHT,
1373 )?;
1374 let font: LuaFontFamily = get_arg_default(&mut args, &body, "font")?;
1375 let color = get_arg_required(&mut args, &body, "color")?;
1376 let weight: u16 = get_arg_or(&mut args, &body, "weight", cosmic_text::Weight::NORMAL.0)?;
1377 let style: u8 = get_arg_default(&mut args, &body, "style")?;
1378 let wrap: u8 = get_arg_default(&mut args, &body, "wrap")?;
1379 let align: Option<u8> = get_arg_default(&mut args, &body, "align")?;
1380 check_args("textbox", body, args)?;
1381
1382 let style = to_style(style)?;
1383 let wrap = to_wrap(wrap)?;
1384 let align = to_align(align)?;
1385
1386 Ok(Box::new(TextBox::<PropBag>::new(
1387 id.0,
1388 bag,
1389 font_size,
1390 line_height,
1391 font.0,
1392 color,
1393 cosmic_text::Weight(weight),
1394 style,
1395 wrap,
1396 align,
1397 )))
1398}
1399
1400fn create_id(_: &Lua, (parent, v): (Option<LuaSourceID>, LuaValue)) -> LuaResult<LuaSourceID> {
1401 if let Some(n) = v.as_integer() {
1402 if let Some(parent) = parent {
1403 Ok(LuaSourceID(parent.0.child(DataID::Int(n))))
1404 } else {
1405 #[cfg(debug_assertions)]
1406 panic!("NO PARENT! This means the layout was called incorrectly!");
1407 #[cfg(not(debug_assertions))]
1408 Err(LuaError::UserDataTypeMismatch)
1409 }
1410 } else if let Some(name) = v.as_string().map(|x| x.to_string_lossy()) {
1411 if let Some(parent) = parent {
1412 Ok(LuaSourceID(parent.0.child(DataID::Owned(name))))
1413 } else {
1414 #[cfg(debug_assertions)]
1415 panic!("NO PARENT! This means the layout was called incorrectly!");
1416 #[cfg(not(debug_assertions))]
1417 Err(LuaError::UserDataTypeMismatch)
1418 }
1419 } else {
1420 Err(LuaError::RuntimeError(format!(
1421 "Expected a number or string to construct an ID, but found {}",
1422 v.type_name()
1423 )))
1424 }
1425}
1426
1427fn replace_dualpoint(p: LuaDualPoint, bound: f32) -> dpi::Size {
1428 match p {
1429 LuaDualPoint::Px(mut point2_d) => {
1430 replace_limit(&mut point2_d, bound);
1431 dpi::Size::Physical(dpi::PhysicalSize::<u32>::new(
1432 point2_d.x.ceil() as u32,
1433 point2_d.y.ceil() as u32,
1434 ))
1435 }
1436 LuaDualPoint::Dp(mut point2_d) => {
1437 replace_limit(&mut point2_d, bound);
1438 dpi::Size::Logical(dpi::LogicalSize::<f64>::new(
1439 point2_d.x as f64,
1440 point2_d.y as f64,
1441 ))
1442 }
1443 }
1444}
1445
1446fn get_required_child<T: FromLua>(t: &LuaTable, idx: usize) -> LuaResult<T> {
1447 if !t.contains_key(idx)? {
1448 Err(LuaError::RuntimeError(format!(
1449 "Expected at least {idx} children, but found {}",
1450 t.raw_len()
1451 )))
1452 } else {
1453 t.get(idx)
1454 }
1455}
1456
1457fn create_window(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<Window> {
1458 let title: LuaString = get_required(&body, "title")?;
1459 let child: ComponentBag = get_required_child(&body, 1)?;
1460
1461 let mut attributes = winit::window::Window::default_attributes()
1462 .with_title(title.to_string_lossy())
1463 .with_resizable(get_or(&body, "resizeable", true)?)
1464 .with_maximized(get_or(&body, "maximized", false)?)
1465 .with_visible(get_or(&body, "visible", true)?)
1466 .with_transparent(get_or(&body, "transparent", false)?)
1467 .with_blur(get_or(&body, "blur", false)?)
1468 .with_decorations(get_or(&body, "decorated", true)?)
1469 .with_content_protected(get_or(&body, "protected", false)?)
1470 .with_active(get_or(&body, "focused", false)?);
1471
1472 if body.contains_key("icon")? {
1473 attributes.window_icon = Some(
1474 crate::resource::load_icon(&std::path::PathBuf::from(body.get::<String>("icon")?))
1475 .map_err(|e| LuaError::ExternalError(Arc::new(Box::new(e))))?,
1476 );
1477 }
1478
1479 if body.contains_key("minsize")? {
1480 attributes.min_inner_size = Some(replace_dualpoint(body.get("minsize")?, f32::NEG_INFINITY))
1481 }
1482
1483 if body.contains_key("maxsize")? {
1484 attributes.max_inner_size = Some(replace_dualpoint(body.get("maxsize")?, f32::INFINITY))
1485 }
1486
1487 if body.contains_key("size")? {
1488 attributes.inner_size = Some(replace_dualpoint(body.get("size")?, 0.0));
1489 }
1490
1491 Ok(Window::new(id.0, attributes, Box::new(child)))
1492}
1493
1494#[derive(PartialEq, Clone, Debug)]
1495pub struct LuaFragment {
1496 layout: LuaFunction,
1497 id_enter: LuaFunction,
1498}
1499
1500impl FromLua for LuaFragment {
1501 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
1502 let t = value.as_table().ok_or(LuaError::UserDataTypeMismatch)?;
1503 Ok(Self {
1504 layout: t.get("layout")?,
1505 id_enter: t.get("id_enter")?,
1506 })
1507 }
1508}
1509
1510impl IntoLua for LuaFragment {
1511 fn into_lua(self, lua: &Lua) -> LuaResult<LuaValue> {
1512 Ok(LuaValue::Table(lua.create_table_from([
1513 ("layout", self.layout),
1514 ("id_enter", self.id_enter),
1515 ])?))
1516 }
1517}
1518
1519impl LuaFragment {
1520 pub fn call<T: IntoLuaMulti>(&self, args: T, mut id: ScopeID<'_>) -> LuaResult<ComponentBag> {
1521 self.id_enter.call::<ComponentBag>((
1522 LuaSourceID(id.id().clone()),
1523 self.layout.clone(),
1524 args,
1525 ))
1526 }
1527}
1528
1529#[derive(Clone, Debug)]
1530pub struct LuaContext {
1531 feather: LuaTable,
1532 modules: LuaTable,
1533 id_enter: LuaFunction,
1534 require: LuaFunction,
1535 load_in_sandbox: LuaFunction,
1536 lua_handlers: LuaTable,
1537 handler_slots: LuaTable,
1538 handler_count: Rc<AtomicU64>,
1539}
1540
1541impl PartialEq for LuaContext {
1542 fn eq(&self, other: &Self) -> bool {
1543 self.feather == other.feather
1544 && self.modules == other.modules
1545 && self.id_enter == other.id_enter
1546 && self.require == other.require
1547 && self.load_in_sandbox == other.load_in_sandbox
1548 && self.lua_handlers == other.lua_handlers
1549 && self.handler_slots == other.handler_slots
1550 && self.handler_count.load(Ordering::Relaxed)
1551 == other.handler_count.load(Ordering::Relaxed)
1552 }
1553}
1554
1555impl LuaContext {
1556 pub fn new(lua: &Lua) -> LuaResult<Self> {
1557 let preload = lua.create_table()?;
1558
1559 preload.set("create_id", lua.create_function(create_id)?)?;
1560 preload.set("create_button", lua.create_function(create_button)?)?;
1561 preload.set(
1562 "create_domain_line",
1563 lua.create_function(create_domain_line)?,
1564 )?;
1565 preload.set(
1566 "create_domain_point",
1567 lua.create_function(create_domain_point)?,
1568 )?;
1569 preload.set("create_flexbox", lua.create_function(create_flexbox)?)?;
1570 preload.set("create_gridbox", lua.create_function(create_gridbox)?)?;
1571 preload.set("create_image", lua.create_function(create_image)?)?;
1572 preload.set("create_line", lua.create_function(create_line)?)?;
1573 preload.set("create_listbox", lua.create_function(create_listbox)?)?;
1574 preload.set("create_mousearea", lua.create_function(create_mousearea)?)?;
1575 preload.set("create_region", lua.create_function(create_region)?)?;
1577 preload.set("create_scrollarea", lua.create_function(create_scrollarea)?)?;
1578 preload.set("create_round_rect", lua.create_function(create_round_rect)?)?;
1579 preload.set("create_arc", lua.create_function(create_arc)?)?;
1580 preload.set("create_triangle", lua.create_function(create_triangle)?)?;
1581 preload.set("create_circle", lua.create_function(create_circle)?)?;
1582 preload.set("create_text", lua.create_function(create_text)?)?;
1583 preload.set("create_textbox", lua.create_function(create_textbox)?)?;
1584 preload.set("create_window", lua.create_function(create_window)?)?;
1585
1586 let preload_mt = lua.create_table()?;
1587 preload_mt.set("__index", lua.globals())?;
1588 preload.set_metatable(Some(preload_mt))?;
1589
1590 let feather: LuaTable = lua
1591 .load(NamedChunk(FEATHER, "feather"))
1592 .set_environment(preload.clone())
1593 .eval()?;
1594
1595 let id_enter = feather.get::<LuaTable>("ID")?.get::<LuaFunction>("enter")?;
1596 let lua_handlers = lua.create_table()?;
1597 let handlers = lua_handlers.clone();
1598 let handler_slots = lua.create_table()?;
1599 let slot_clone = handler_slots.clone();
1600 let handler_count = Rc::new(AtomicU64::new(0));
1601 let handler_count_clone = handler_count.clone();
1602
1603 feather.set(
1604 "add_handler",
1605 lua.create_function(move |_, (name, func): (LuaString, LuaFunction)| {
1606 let count = handler_count_clone.fetch_add(1, Ordering::Relaxed);
1607 handlers.set(&name, func)?;
1608 let slot = Slot(APP_SOURCE_ID.into(), count);
1609 slot_clone.set(name, slot.clone())?;
1610 Ok(slot)
1611 })?,
1612 )?;
1613
1614 let modules = lua.create_table()?;
1615 modules.set("feather", feather.clone())?;
1616 preload.set("injected_dep", &modules)?;
1617
1618 lua.load(NamedChunk(SANDBOX, "sandbox")).exec()?;
1619
1620 let require: LuaFunction = lua
1621 .load(
1622 r#"
1623package = {preload = {}, loaded = injected_dep}
1624return function(name) -- require stub for inside sandbox
1625 if not package.loaded[name] then
1626 if not package.preload[name] then
1627 error("Couldn't find package.preload for " .. name)
1628 end
1629 package.loaded[name] = package.preload[name]()
1630 end
1631 return package.loaded[name]
1632end
1633 "#,
1634 )
1635 .set_environment(preload)
1636 .eval()?;
1637
1638 lua.load(
1639 r#"
1640 jit.opt.start("maxtrace=10000")
1641 jit.opt.start("maxmcode=4096")
1642 jit.opt.start("recunroll=5")
1643 jit.opt.start("loopunroll=60")
1644
1645 --local create_module = sandbox_impl(true)
1646 create_module = function(bytes, name, interface)
1647 return load(bytes, name, "t", setmetatable(interface, {__index=_G}))
1648 end
1649
1650
1651 function load_in_sandbox(bytes, name, additional_interface)
1652 local r, err = create_module(bytes, name, additional_interface)
1653 if r == nil then
1654 error(err)
1655 end
1656
1657 return r()
1658 end
1659 "#,
1660 )
1661 .exec()?;
1662
1663 let load_in_sandbox: LuaFunction = lua.load("load_in_sandbox").eval()?;
1664
1665 Ok(Self {
1666 feather,
1667 id_enter,
1668 load_in_sandbox,
1669 require,
1670 modules,
1671 lua_handlers,
1672 handler_slots,
1673 handler_count,
1674 })
1675 }
1676
1677 pub fn add_module(&self, lua: &Lua, name: &str, module: &[u8]) -> LuaResult<()> {
1678 let interface = lua.create_table()?;
1679 interface.set("require", self.require.clone())?;
1680
1681 self.modules.set(
1682 name,
1683 self.load_in_sandbox
1684 .call::<LuaValue>((lua.create_string(module)?, name, interface))?,
1685 )
1686 }
1687
1688 pub fn add_function<A: FromLuaMulti, R: IntoLuaMulti>(
1689 &self,
1690 lua: &Lua,
1691 name: &str,
1692 f: impl Fn(&Lua, A) -> LuaResult<R> + mlua::MaybeSend + 'static,
1693 ) -> LuaResult<()> {
1694 self.modules.set(name, lua.create_function(f)?)
1695 }
1696
1697 pub fn add_function_mut<F, A: FromLuaMulti, R: IntoLuaMulti>(
1698 &self,
1699 lua: &Lua,
1700 name: &str,
1701 f: impl FnMut(&Lua, A) -> LuaResult<R> + mlua::MaybeSend + 'static,
1702 ) -> LuaResult<()> {
1703 self.modules.set(name, lua.create_function_mut(f)?)
1704 }
1705
1706 pub fn get_module(&self, name: &str) -> Option<LuaValue> {
1707 self.modules.get(name).ok().and_then(|x| match x {
1708 Some(LuaNil) => None,
1709 x => x,
1710 })
1711 }
1712
1713 pub fn reserve_handler(&self, name: &str) -> LuaResult<u64> {
1714 let index = self.handler_count.fetch_add(1, Ordering::Relaxed);
1715 self.handler_slots
1716 .set(name, Slot(APP_SOURCE_ID.into(), index))?;
1717 Ok(index)
1718 }
1719
1720 fn load_layout<R: FromLuaMulti>(&self, lua: &Lua, layout: &[u8]) -> LuaResult<R> {
1721 let interface = lua.create_table()?;
1722 interface.set("handlers", self.handler_slots.clone())?;
1723 interface.set("require", self.require.clone())?;
1724
1725 self.load_in_sandbox
1726 .call((lua.create_string(layout)?, "layout", interface))
1727 }
1728
1729 pub fn load_fragment(&self, lua: &Lua, layout: &[u8]) -> LuaResult<LuaFragment> {
1730 Ok(LuaFragment {
1731 layout: self.load_layout(lua, layout)?,
1732 id_enter: self.id_enter.clone(),
1733 })
1734 }
1735
1736 pub fn get_handlers<AppData: FromLua + IntoLua + Clone>(
1737 &self,
1738 mut reserved: HashMap<u64, crate::AppEvent<AppData>>,
1739 ) -> LuaResult<Vec<crate::AppEvent<AppData>>> {
1740 use std::mem::MaybeUninit;
1741
1742 let count = self.handler_count.load(Ordering::Relaxed) as usize;
1743 let mut handlers: Vec<crate::AppEvent<AppData>> = Vec::with_capacity(count);
1744 let spare = handlers.spare_capacity_mut();
1745
1746 for v in self.handler_slots.pairs::<LuaString, Slot>() {
1747 let (key, slot) = v?;
1748 if self.lua_handlers.contains_key(&key)? {
1749 let func: LuaFunction = self.lua_handlers.get(&key)?;
1750 spare[slot.1 as usize] = MaybeUninit::new(Box::new(
1751 move |pair: crate::DispatchPair, mut state: AccessCell<AppData>| {
1752 let (appdata, v): (AppData, LuaValue) =
1753 match func.call((pair.0, state.value.clone())) {
1754 Ok(v) => v,
1755 Err(e) => return InputResult::Error(e.into()),
1756 };
1757 *state = appdata;
1758 if v.as_boolean().unwrap_or(true) {
1759 InputResult::Consume(())
1760 } else {
1761 InputResult::Forward(())
1762 }
1763 },
1764 ));
1765 } else {
1766 spare[slot.1 as usize] = MaybeUninit::new(
1767 reserved
1768 .remove(&slot.1)
1769 .expect("Reserved slot had no entry in `reserved` map!"),
1770 )
1771 }
1772 }
1773
1774 unsafe {
1775 handlers.set_len(count);
1776 }
1777
1778 Ok(handlers)
1779 }
1780
1781 pub fn update_handler<AppData: FromLua + IntoLua + Clone + 'static>(
1782 &self,
1783 lua: &Lua,
1784 sender: std::sync::mpsc::Sender<crate::EventPair<AppData>>,
1785 count: AtomicU64,
1786 ) -> LuaResult<()> {
1787 let slot_clone = self.handler_slots.clone();
1788 self.feather.set(
1789 "add_handler",
1790 lua.create_function(move |_, (name, func): (LuaString, LuaFunction)| {
1791 let count = count.fetch_add(1, Ordering::Relaxed);
1792 sender
1793 .send((
1794 count,
1795 Box::new(
1796 move |pair: crate::DispatchPair, mut state: AccessCell<AppData>| {
1797 let (appdata, v): (AppData, LuaValue) =
1798 match func.call((pair.0, state.value.clone())) {
1799 Ok(v) => v,
1800 Err(e) => return InputResult::Error(e.into()),
1801 };
1802 *state = appdata;
1803 if v.as_boolean().unwrap_or(true) {
1804 InputResult::Consume(())
1805 } else {
1806 InputResult::Forward(())
1807 }
1808 },
1809 ),
1810 ))
1811 .unwrap();
1812 let slot = Slot(APP_SOURCE_ID.into(), count);
1813 slot_clone.set(name, slot.clone())?;
1814 Ok(slot)
1815 })?,
1816 )?;
1817
1818 Ok(())
1819 }
1820}
1821
1822pub struct LuaApp<AppData: Clone + FromLua + IntoLua, T: 'static>(
1823 crate::App<AppData, LuaPersist<AppData>, T>,
1824);
1825
1826impl<AppData: Clone + FromLua + IntoLua + PartialEq + 'static, T: 'static> LuaApp<AppData, T> {
1827 pub fn new(
1828 lua: &Lua,
1829 app_state: AppData,
1830 handlers: Vec<(String, crate::AppEvent<AppData>)>,
1831 layout: &[u8],
1832 ) -> eyre::Result<(Self, crate::EventLoop<T>)> {
1833 let ctx = LuaContext::new(lua)?;
1834
1835 let mut reserved = HashMap::new();
1836 for (k, v) in handlers.into_iter() {
1837 reserved.insert(ctx.reserve_handler(&k)?, v);
1838 }
1839
1840 let (window, init): (LuaFunction, Option<LuaFunction>) = ctx.load_layout(lua, layout)?;
1841
1842 let (app, event, sender, count) = crate::App::new(
1843 app_state.clone(),
1844 ctx.get_handlers(reserved)?,
1845 LuaPersist {
1846 window,
1847 id_enter: ctx.id_enter.clone(),
1848 init: if let Some(init) = init {
1849 Ok(init)
1850 } else {
1851 Err(app_state)
1852 },
1853 phantom: PhantomData,
1854 },
1855 None,
1856 None,
1857 )?;
1858
1859 ctx.update_handler(lua, sender, count)?;
1861 Ok((Self(app), event))
1862 }
1863}
1864
1865impl<AppData: Clone + FromLua + IntoLua + PartialEq + 'static, T: 'static> ApplicationHandler<T>
1866 for LuaApp<AppData, T>
1867{
1868 fn new_events(
1869 &mut self,
1870 event_loop: &winit::event_loop::ActiveEventLoop,
1871 cause: winit::event::StartCause,
1872 ) {
1873 <crate::App<AppData, LuaPersist<AppData>, T> as ApplicationHandler<T>>::new_events(
1874 &mut self.0,
1875 event_loop,
1876 cause,
1877 );
1878 }
1879
1880 fn user_event(&mut self, event_loop: &winit::event_loop::ActiveEventLoop, event: T) {
1881 self.0.user_event(event_loop, event);
1882 }
1883
1884 fn device_event(
1885 &mut self,
1886 event_loop: &winit::event_loop::ActiveEventLoop,
1887 device_id: winit::event::DeviceId,
1888 event: winit::event::DeviceEvent,
1889 ) {
1890 <crate::App<AppData, LuaPersist<AppData>, T> as ApplicationHandler<T>>::device_event(
1891 &mut self.0,
1892 event_loop,
1893 device_id,
1894 event,
1895 );
1896 }
1897
1898 fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1899 <crate::App<AppData, LuaPersist<AppData>, T> as ApplicationHandler<T>>::about_to_wait(
1900 &mut self.0,
1901 event_loop,
1902 );
1903 }
1904
1905 fn suspended(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1906 <crate::App<AppData, LuaPersist<AppData>, T> as ApplicationHandler<T>>::suspended(
1907 &mut self.0,
1908 event_loop,
1909 );
1910 }
1911
1912 fn exiting(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1913 <crate::App<AppData, LuaPersist<AppData>, T> as ApplicationHandler<T>>::exiting(
1914 &mut self.0,
1915 event_loop,
1916 );
1917 }
1918
1919 fn memory_warning(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1920 <crate::App<AppData, LuaPersist<AppData>, T> as ApplicationHandler<T>>::memory_warning(
1921 &mut self.0,
1922 event_loop,
1923 );
1924 }
1925
1926 fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1927 <crate::App<AppData, LuaPersist<AppData>, T> as ApplicationHandler<T>>::resumed(
1928 &mut self.0,
1929 event_loop,
1930 );
1931 }
1932
1933 fn window_event(
1934 &mut self,
1935 event_loop: &winit::event_loop::ActiveEventLoop,
1936 window_id: winit::window::WindowId,
1937 event: winit::event::WindowEvent,
1938 ) {
1939 <crate::App<AppData, LuaPersist<AppData>, T> as ApplicationHandler<T>>::window_event(
1940 &mut self.0,
1941 event_loop,
1942 window_id,
1943 event,
1944 )
1945 }
1946}