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)?;
541 if name == "pxpoint_mt" {
542 Ok(LimitPoint(DPoint {
543 rel: LuaPoint::nan().0,
544 dp: LuaPoint::nan().0,
545 px: LuaPoint::<Pixel>::from_lua(value, lua)?.0,
546 }))
547 } else if name == "abspoint_mt" {
548 Ok(LimitPoint(DPoint {
549 rel: LuaPoint::nan().0,
550 dp: LuaPoint::<Logical>::from_lua(value, lua)?.0,
551 px: LuaPoint::nan().0,
552 }))
553 } else if name == "relpoint_mt" {
554 Ok(LimitPoint(DPoint {
555 rel: LuaPoint::<Relative>::from_lua(value, lua)?.0,
556 dp: LuaPoint::nan().0,
557 px: LuaPoint::nan().0,
558 }))
559 } else if name == "coord_mt" {
560 let px = get_or::<LuaPoint<Pixel>>(v, "px", LuaPoint::nan())?.0;
561 let dp = get_or::<LuaPoint<Logical>>(v, "dp", LuaPoint::nan())?.0;
562 let rel = get_or::<LuaPoint<Relative>>(v, "rel", LuaPoint::nan())?.0;
563 Ok(LimitPoint(DPoint { dp, px, rel }))
564 } else {
565 Err(LuaError::RuntimeError(format!(
566 "Expected a point, but found {name}",
567 )))
568 }
569 }
570}
571
572impl FromLua for sRGB {
573 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
574 if let Some(i) = value.as_integer() {
575 Ok(sRGB32 { rgba: i as u32 }.as_f32())
576 } else if let Some(v) = value.as_table() {
577 if v.len()? == 4 {
578 Ok(sRGB::new(v.get(1)?, v.get(2)?, v.get(3)?, v.get(4)?))
579 } else {
580 Err(LuaError::RuntimeError(format!(
581 "A color must be an array of exactly 4 numbers, declared like {{ R, G, B, A }}, but only found {}",
582 v.len()?
583 )))
584 }
585 } else {
586 Err(LuaError::RuntimeError(format!(
587 "Expected a color but found {}",
588 value.type_name()
589 )))
590 }
591 }
592}
593
594struct LuaFontFamily(cosmic_text::FamilyOwned);
595
596impl Default for LuaFontFamily {
597 fn default() -> Self {
598 Self(cosmic_text::FamilyOwned::SansSerif)
599 }
600}
601
602impl FromLua for LuaFontFamily {
603 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
604 let name =
605 value
606 .as_string()
607 .and_then(|s| s.to_str().ok())
608 .ok_or(LuaError::RuntimeError(format!(
609 "Expected a string, but found {}",
610 value.type_name()
611 )))?;
612
613 Ok(LuaFontFamily(if name.eq_ignore_ascii_case("serif") {
614 cosmic_text::FamilyOwned::Serif
615 } else if name.eq_ignore_ascii_case("cursive") {
616 cosmic_text::FamilyOwned::Cursive
617 } else if name.eq_ignore_ascii_case("fantasy") {
618 cosmic_text::FamilyOwned::Fantasy
619 } else if name.eq_ignore_ascii_case("monospace") {
620 cosmic_text::FamilyOwned::Monospace
621 } else if name.eq_ignore_ascii_case("sansserif") || name.eq_ignore_ascii_case("sans-serif")
622 {
623 cosmic_text::FamilyOwned::SansSerif
624 } else {
625 cosmic_text::FamilyOwned::Name((*name).into())
626 }))
627 }
628}
629
630pub type ComponentBag = Box<dyn crate::component::Component<Props = PropBag>>;
631
632impl<U: ?Sized> crate::component::ComponentWrap<U> for ComponentBag
633where
634 for<'a> &'a U: std::convert::From<&'a PropBag>,
635{
636 fn layout(
637 &self,
638 manager: &mut crate::StateManager,
639 driver: &crate::graphics::Driver,
640 window: &Arc<SourceID>,
641 ) -> Box<dyn crate::layout::Layout<U> + 'static> {
642 use std::ops::Deref;
643 Box::new(Box::deref(self).layout(manager, driver, window))
644 }
645}
646
647impl StateMachineChild for ComponentBag {
648 fn id(&self) -> Arc<SourceID> {
649 use std::ops::Deref;
650 Box::deref(self).id()
651 }
652
653 fn init(
654 &self,
655 driver: &std::sync::Weak<crate::graphics::Driver>,
656 ) -> Result<Box<dyn crate::component::StateMachineWrapper>, crate::Error> {
657 use std::ops::Deref;
658 Box::deref(self).init(driver)
659 }
660
661 fn apply_children(
662 &self,
663 f: &mut dyn FnMut(&dyn StateMachineChild) -> eyre::Result<()>,
664 ) -> eyre::Result<()> {
665 use std::ops::Deref;
666 Box::deref(self).apply_children(f)
667 }
668}
669
670macro_rules! gen_from_lua {
671 ($type_name:ident) => {
672 impl mlua::FromLua for $type_name {
673 #[inline]
674 fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
675 match value {
676 ::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
677 _ => Err(::mlua::Error::FromLuaConversionError {
678 from: value.type_name(),
679 to: stringify!($type_name).to_string(),
680 message: None,
681 }),
682 }
683 }
684 }
685 };
686}
687
688#[derive(Clone)]
689struct LuaDomain(std::sync::Arc<crate::CrossReferenceDomain>);
690
691impl UserData for LuaDomain {}
692gen_from_lua!(LuaDomain);
693
694impl UserData for Window {}
695gen_from_lua!(Window);
696
697impl UserData for LuaSourceID {}
698gen_from_lua!(LuaSourceID);
699
700impl UserData for Slot {
701 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
702 methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| {
703 Ok(format!("Slot #{}: {}", this.1, this.0))
704 });
705 }
706}
707gen_from_lua!(Slot);
708
709impl UserData for ComponentBag {}
710gen_from_lua!(ComponentBag);
711
712pub struct LuaPersist<AppData> {
716 pub window: LuaFunction, pub id_enter: LuaFunction,
718 pub init: Result<LuaFunction, AppData>,
719 phantom: PhantomData<AppData>,
720}
721
722impl<AppData: Clone> FnPersistStore for LuaPersist<AppData> {
723 type Store = AppData;
724}
725
726impl<AppData: Clone + FromLua + IntoLua>
727 FnPersist2<AppData, ScopeID<'static>, im::HashMap<Arc<SourceID>, Option<Window>>>
728 for LuaPersist<AppData>
729{
730 fn init(&self) -> Self::Store {
731 let r = match &self.init {
732 Ok(init) => init.call::<AppData>(()),
733 Err(data) => Ok(data.clone()),
734 };
735
736 match r {
737 Err(LuaError::RuntimeError(s)) => panic!("{}", s),
738 Err(e) => panic!("{e:?}"),
739 Ok(v) => v,
740 }
741 }
742 fn call(
743 &mut self,
744 _: Self::Store,
745 appdata: AppData,
746 mut id: ScopeID<'static>,
747 ) -> (Self::Store, im::HashMap<Arc<SourceID>, Option<Window>>) {
748 let mut h = im::HashMap::new();
749
750 let (store, w) = self
751 .id_enter
752 .call::<(AppData, crate::component::window::Window)>((
753 LuaSourceID(id.id().clone()),
754 self.window.clone(),
755 appdata.clone(),
756 ))
757 .unwrap();
758 h.insert(w.id().clone(), Some(w));
759 (store, h)
760 }
761}
762
763enum LuaDualPoint {
764 Px(Point2D<f32, Pixel>),
765 Dp(Point2D<f32, Logical>),
766}
767
768impl FromLua for LuaDualPoint {
769 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
770 let t = value.as_table().ok_or(LuaError::RuntimeError(format!(
771 "Expected a 2D point, but found {}",
772 value.type_name()
773 )))?;
774
775 if is_dvalue(t)? {
776 if t.contains_key("dp")? && !t.contains_key("px")? {
777 Ok(LuaDualPoint::Dp(Point2D::<f32, Logical>::splat(
778 t.get("dp")?,
779 )))
780 } else if t.contains_key("px")? && !t.contains_key("dp")? {
781 Ok(LuaDualPoint::Px(Point2D::<f32, Pixel>::splat(t.get("px")?)))
782 } else {
783 Err(LuaError::RuntimeError("This property only accepts a point that is either abs or px, not relative or a combination.".to_string()))
784 }
785 } else if t.contains_key("dp")? && !t.contains_key("px")? {
786 Ok(LuaDualPoint::Dp(t.get::<LuaPoint<Logical>>("dp")?.0))
787 } else if t.contains_key("px")? && !t.contains_key("dp")? {
788 Ok(LuaDualPoint::Px(t.get::<LuaPoint<Pixel>>("px")?.0))
789 } else if get_kind(t)? == "px" {
790 Ok(LuaDualPoint::Px(Point2D::<f32, Pixel>::new(
791 t.get("x")?,
792 t.get("y")?,
793 )))
794 } else if get_kind(t)? == "dp" {
795 Ok(LuaDualPoint::Dp(Point2D::<f32, Logical>::new(
796 t.get("x")?,
797 t.get("y")?,
798 )))
799 } else {
800 Err(LuaError::RuntimeError("This property only accepts a point that is either abs or px, not relative or a combination.".to_string()))
801 }
802 }
803}
804
805fn load_prop<T: mlua::FromLua>(
806 f: fn(&mut PropBag, T) -> Option<T>,
807 bag: &mut PropBag,
808 props: &LuaTable,
809 name: &str,
810) -> LuaResult<()> {
811 if props.contains_key(name)? {
812 f(bag, props.get(name)?);
813 }
814 Ok(())
815}
816
817#[inline]
818fn replace_unsized<U>(rect: &mut Rect<U>) {
819 rect.v = f32x4::new(
820 rect.v
821 .to_array()
822 .map(|x| if x.is_nan() { UNSIZED_AXIS } else { x }),
823 );
824}
825
826#[inline]
827fn replace_unsized_drect(mut rect: DRect) -> DRect {
828 replace_unsized(&mut rect.dp);
829 replace_unsized(&mut rect.px);
830 replace_unsized(&mut rect.rel);
831 rect
832}
833
834#[inline]
835fn replace_limit<U>(p: &mut Point2D<f32, U>, bound: f32) {
836 if p.x.is_nan() {
837 p.x = bound
838 }
839 if p.y.is_nan() {
840 p.y = bound
841 }
842}
843
844#[inline]
845fn replace_limit_dpoint(mut p: DPoint, bound: f32) -> DPoint {
846 replace_limit(&mut p.dp, bound);
847 replace_limit(&mut p.px, bound);
848 replace_limit(&mut p.rel, bound);
849 p
850}
851
852impl FromLua for PropBag {
853 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
854 let props = value.as_table().ok_or(LuaError::RuntimeError(format!(
855 "Expected PropBag, got {}",
856 value.type_name(),
857 )))?;
858 let mut bag = PropBag::new();
859
860 load_prop(PropBag::set_wrap, &mut bag, props, "wrap")?;
861 load_prop(PropBag::set_zindex, &mut bag, props, "zindex")?;
862 load_prop(PropBag::set_order, &mut bag, props, "order")?;
863 load_prop(PropBag::set_grow, &mut bag, props, "grow")?;
864 load_prop(PropBag::set_shrink, &mut bag, props, "shrink")?;
865 load_prop(PropBag::set_basis, &mut bag, props, "basis")?;
866 load_prop(PropBag::set_padding, &mut bag, props, "padding")?;
867 load_prop(PropBag::set_margin, &mut bag, props, "margin")?;
868 load_prop(PropBag::set_anchor, &mut bag, props, "anchor")?;
869
870 if props.contains_key("area")? {
871 bag.set_area(replace_unsized_drect(props.get::<DRect>("area")?));
872 }
873
874 let mut limits: crate::DLimits = Default::default();
875 let mut rlimits: crate::Limits<Relative> = Default::default();
876
877 if props.contains_key("minsize")? {
878 let p = replace_limit_dpoint(props.get::<LimitPoint>("minsize")?.0, f32::NEG_INFINITY);
879 limits.dp.set_min(p.dp.to_vector().to_size());
880 limits.px.set_min(p.px.to_vector().to_size());
881 rlimits.set_min(p.rel.to_vector().to_size());
882 }
883
884 if props.contains_key("maxsize")? {
885 let p = replace_limit_dpoint(props.get::<LimitPoint>("maxsize")?.0, f32::INFINITY);
886 limits.dp.set_max(p.dp.to_vector().to_size());
887 limits.px.set_max(p.px.to_vector().to_size());
888 rlimits.set_max(p.rel.to_vector().to_size());
889 }
890
891 bag.set_limits(limits);
892 bag.set_rlimits(rlimits);
893
894 if props.contains_key("direction")? {
895 bag.set_direction(props.get::<LuaEnum<crate::RowDirection>>("direction")?.0);
896 }
897
898 if props.contains_key("justify")? {
899 bag.set_justify(props.get::<LuaEnum<flex::FlexJustify>>("justify")?.0);
900 }
901
902 if props.contains_key("align")? {
903 bag.set_align(props.get::<LuaEnum<flex::FlexJustify>>("align")?.0);
904 }
905
906 if props.contains_key("domain")? {
907 bag.set_domain(props.get::<LuaDomain>("domain")?.0);
908 }
909
910 if props.contains_key("obstacles")? {
911 bag.set_obstacles(props.get::<Vec<DAbsRect>>("obstacles")?.as_slice());
912 }
913
914 if props.contains_key("dim")? {
915 bag.set_dim(props.get::<LuaPoint<Pixel>>("dim")?.0.to_vector().to_size());
916 }
917
918 if props.contains_key("rows")? {
919 bag.set_rows(props.get::<Vec<DValue>>("rows")?.as_slice());
920 }
921
922 if props.contains_key("columns")? {
923 bag.set_columns(props.get::<Vec<DValue>>("columns")?.as_slice());
924 }
925
926 if props.contains_key("spacing")? {
927 bag.set_spacing(props.get::<DPoint>("spacing")?);
928 }
929
930 if props.contains_key("coord")? {
931 let coords = props.get::<Vec<usize>>("coord")?;
932 bag.set_coord((coords[0], coords[1]));
933 }
934
935 if props.contains_key("span")? {
936 let coords = props.get::<Vec<usize>>("span")?;
937 bag.set_span((coords[0], coords[1]));
938 }
939
940 Ok(bag)
941 }
942}
943
944fn prop_no_children(t: &LuaTable) -> LuaResult<PropBag> {
945 let count = t.len()?;
946 if count > 0 {
947 return Err(LuaError::RuntimeError(format!(
948 "Found {count} child components for a component that doesn't accept children!"
949 )));
950 }
951
952 let bag: PropBag = get_required(t, "props")?;
953 Ok(bag)
954}
955
956fn push_child<D: crate::layout::Desc + ?Sized>(
957 t: &LuaTable,
958 i: i64,
959 children: &mut im::Vector<Option<Box<ChildOf<D>>>>,
960) -> LuaResult<()>
961where
962 std::boxed::Box<dyn crate::component::Component<Props = PropBag>>:
963 crate::component::ComponentWrap<<D as crate::layout::Desc>::Child>,
964{
965 let v = t.get::<LuaValue>(i)?;
966 if v.is_nil() {
967 return Ok(());
968 } else if let Some(inner) = v.as_table()
969 && let Some(mt) = inner.metatable()
970 && let Ok(s) = mt.get::<String>("kind")
971 && s == "multicomponent_mt"
972 {
973 for j in 1..=inner.len()? {
974 push_child::<D>(inner, j, children)?;
975 }
976 } else {
977 let component: ComponentBag = t.get(i)?;
978 children.push_back(Some(Box::new(component)));
979 }
980
981 Ok(())
982}
983
984#[allow(clippy::type_complexity)]
985fn prop_children<D: crate::layout::Desc + ?Sized>(
986 t: &LuaTable,
987) -> LuaResult<(im::Vector<Option<Box<ChildOf<D>>>>, PropBag)>
988where
989 std::boxed::Box<dyn crate::component::Component<Props = PropBag>>:
990 crate::component::ComponentWrap<<D as crate::layout::Desc>::Child>,
991{
992 let mut children: im::Vector<Option<Box<ChildOf<D>>>> = im::Vector::new();
993
994 for i in 1..=t.len()? {
995 push_child::<D>(t, i, &mut children)?;
996 }
997
998 let bag: PropBag = get_required(t, "props")?;
999 Ok((children, bag))
1000}
1001
1002fn check_args(name: &str, t: LuaTable, v: HashSet<&'static str>) -> LuaResult<()> {
1003 for pair in t.pairs() {
1004 let (k, _): (LuaValue, LuaValue) = pair?;
1005 if k.as_integer().is_none()
1006 && k.as_number().is_none()
1007 && let Some(s) = k.as_string()
1008 {
1009 let str = s.to_string_lossy();
1010 if !v.contains(str.as_str()) {
1011 return Err(LuaError::FromLuaConversionError {
1012 from: "[attributes table]",
1013 to: name.to_string(),
1014 message: Some(format!("Unexpected key {str} in attributes table")),
1015 });
1016 }
1017 }
1018 }
1019
1020 Ok(())
1021}
1022
1023fn create_button(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1024 let (children, bag) = prop_children::<dyn fixed::Prop>(&body)?;
1025
1026 let mut args = HashSet::from_iter(["props"]);
1027 let onclick = get_arg_required(&mut args, &body, "onclick")?;
1028 check_args("button", body, args)?;
1029
1030 let res: LuaResult<ComponentBag> = Ok(Box::new(Button::<PropBag>::new(
1031 id.0, bag, onclick, children,
1032 )));
1033 res
1034}
1035
1036fn create_domain_line(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1037 let bag = prop_no_children(&body)?;
1038
1039 let mut args = HashSet::from_iter(["props"]);
1040 let domain: LuaDomain = get_arg_required(&mut args, &body, "domain")?;
1041 let start: LuaSourceID = get_arg_required(&mut args, &body, "start")?;
1042 let end: LuaSourceID = get_arg_required(&mut args, &body, "end")?;
1043 let fill = get_arg_default(&mut args, &body, "fill")?;
1044 check_args("domain-line", body, args)?;
1045
1046 let res: LuaResult<ComponentBag> = Ok(Box::new(DomainLine::<PropBag> {
1047 id: id.0,
1048 domain: domain.0,
1049 start: start.0,
1050 end: end.0,
1051 props: bag.into(),
1052 fill,
1053 }));
1054 res
1055}
1056
1057fn create_domain_point(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1058 let bag = prop_no_children(&body)?;
1059
1060 let args = HashSet::from_iter(["props"]);
1061 check_args("domain-point", body, args)?;
1062
1063 let res: LuaResult<ComponentBag> = Ok(Box::new(DomainPoint::<PropBag>::new(id.0, bag)));
1064 res
1065}
1066
1067fn create_flexbox(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1068 let (children, bag) = prop_children::<dyn flex::Prop>(&body)?;
1069 let args = HashSet::from_iter(["props"]);
1070 check_args("flexbox", body, args)?;
1071
1072 let res: LuaResult<ComponentBag> = Ok(Box::new(FlexBox::<PropBag>::new(id.0, bag, children)));
1073 res
1074}
1075
1076fn create_gridbox(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1077 let (children, bag) = prop_children::<dyn grid::Prop>(&body)?;
1078 let args = HashSet::from_iter(["props"]);
1079 check_args("gridbox", body, args)?;
1080
1081 let res: LuaResult<ComponentBag> = Ok(Box::new(GridBox::<PropBag>::new(id.0, bag, children)));
1082 res
1083}
1084
1085fn create_image(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1086 let bag = prop_no_children(&body)?;
1087
1088 let mut args = HashSet::from_iter(["props"]);
1089 let resource: String = get_arg_required(&mut args, &body, "resource")?;
1090 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1091 let dynamic: bool = get_arg_default(&mut args, &body, "dynamic")?;
1092 check_args("image", body, args)?;
1093
1094 let location = std::path::PathBuf::from(resource);
1095
1096 let res: LuaResult<ComponentBag> = Ok(Box::new(Image::<PropBag>::new(
1097 id.0, bag, &location, size, dynamic,
1098 )));
1099 res
1100}
1101
1102fn create_line(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1103 let bag = prop_no_children(&body)?;
1104
1105 let mut args = HashSet::from_iter(["props"]);
1106 let start: LuaPoint<Pixel> = get_arg_required(&mut args, &body, "start")?;
1107 let end: LuaPoint<Pixel> = get_arg_required(&mut args, &body, "end")?;
1108 let fill = get_arg_default(&mut args, &body, "fill")?;
1109 check_args("line", body, args)?;
1110
1111 let res: LuaResult<ComponentBag> = Ok(Box::new(Line::<PropBag> {
1112 id: id.0,
1113 start: start.0,
1114 end: end.0,
1115 props: bag.into(),
1116 fill,
1117 }));
1118 res
1119}
1120
1121fn create_listbox(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1122 let (children, bag) = prop_children::<dyn list::Prop>(&body)?;
1123 let args = HashSet::from_iter(["props"]);
1124 check_args("listbox", body, args)?;
1125
1126 let res: LuaResult<ComponentBag> = Ok(Box::new(ListBox::<PropBag>::new(id.0, bag, children)));
1127 res
1128}
1129
1130fn create_mousearea(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1131 let bag = prop_no_children(&body)?;
1132
1133 let mut args = HashSet::from_iter(["props"]);
1134 let deadzone = get_arg_or(&mut args, &body, "deadzone", f32::INFINITY)?;
1135 let onclick = get_arg_default(&mut args, &body, "onclick")?;
1136 let ondblclick = get_arg_default(&mut args, &body, "ondblclick")?;
1137 let ondrag = get_arg_default(&mut args, &body, "ondrag")?;
1138 check_args("mousearea", body, args)?;
1139
1140 let res: LuaResult<ComponentBag> = Ok(Box::new(MouseArea::<PropBag>::new(
1141 id.0,
1142 bag,
1143 Some(deadzone),
1144 [onclick, ondblclick, ondrag, None, None, None],
1145 )));
1146 res
1147}
1148
1149fn create_region(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1150 let (children, bag) = prop_children::<dyn fixed::Prop>(&body)?;
1151 let mut args = HashSet::from_iter(["props"]);
1152 let color: Option<sRGB> = get_arg_default(&mut args, &body, "color")?;
1153 let rotation: Option<f32> = get_arg_default(&mut args, &body, "rotation")?;
1154 check_args("region", body, args)?;
1155
1156 Ok(Box::new(if color.is_some() || rotation.is_some() {
1157 Region::<PropBag>::new_layer(
1158 id.0,
1159 bag,
1160 color.unwrap_or(sRGB::white()).as_32bit(),
1161 rotation.unwrap_or_default(),
1162 children,
1163 )
1164 } else {
1165 Region::<PropBag>::new(id.0, bag, children)
1166 }))
1167}
1168
1169fn create_scrollarea(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1170 let (children, bag) = prop_children::<dyn fixed::Prop>(&body)?;
1171 let mut args = HashSet::from_iter(["props"]);
1172 let stepx = get_arg_default(&mut args, &body, "stepx")?;
1173 let stepy = get_arg_default(&mut args, &body, "stepy")?;
1174 let extension = get_arg_default(&mut args, &body, "extension")?;
1175 let onscroll = get_arg_default(&mut args, &body, "onscroll")?;
1176 check_args("scrollarea", body, args)?;
1177
1178 let res: LuaResult<ComponentBag> = Ok(Box::new(ScrollArea::<PropBag>::new(
1179 id.0,
1180 bag,
1181 (stepx, stepy),
1182 extension,
1183 children,
1184 [onscroll],
1185 )));
1186 res
1187}
1188
1189fn create_round_rect(lua: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1190 let bag = prop_no_children(&body)?;
1191
1192 let mut args: HashSet<&'static str> = HashSet::from_iter(["props"]);
1193 let border = get_arg_default(&mut args, &body, "border")?;
1194 let blur = get_arg_default(&mut args, &body, "blur")?;
1195 let fill = get_arg_default(&mut args, &body, "fill")?;
1196 let outline = get_arg_default(&mut args, &body, "outline")?;
1197 let corners = get_array_arg(&mut args, lua, &body, "corners", [0.0; 4])?;
1198 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1199 check_args("round_rect", body, args)?;
1200
1201 Ok(Box::new(shape::round_rect(
1202 id.0,
1203 bag,
1204 border,
1205 blur,
1206 wide::f32x4::new(corners),
1207 fill,
1208 outline,
1209 size,
1210 )))
1211}
1212
1213fn create_arc(lua: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1214 let bag = prop_no_children(&body)?;
1215
1216 let mut args: HashSet<&'static str> = HashSet::from_iter(["props"]);
1217 let border = get_arg_default(&mut args, &body, "border")?;
1218 let blur = get_arg_default(&mut args, &body, "blur")?;
1219 let fill = get_arg_default(&mut args, &body, "fill")?;
1220 let outline = get_arg_default(&mut args, &body, "outline")?;
1221 let angles = get_array_arg(&mut args, lua, &body, "angles", [0.0; 2])?;
1222 let inner_radius = get_arg_default(&mut args, &body, "innerRadius")?;
1223 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1224 check_args("arc", body, args)?;
1225
1226 Ok(Box::new(shape::arcs(
1227 id.0,
1228 bag,
1229 border,
1230 blur,
1231 inner_radius,
1232 angles,
1233 fill,
1234 outline,
1235 size,
1236 )))
1237}
1238
1239fn create_triangle(lua: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1240 let bag = prop_no_children(&body)?;
1241
1242 let mut args: HashSet<&'static str> = HashSet::from_iter(["props"]);
1243 let border = get_arg_default(&mut args, &body, "border")?;
1244 let blur = get_arg_default(&mut args, &body, "blur")?;
1245 let fill = get_arg_default(&mut args, &body, "fill")?;
1246 let outline = get_arg_default(&mut args, &body, "outline")?;
1247 let corners = get_array_arg(&mut args, lua, &body, "corners", [0.0; 3])?;
1248 let offset = get_arg_default(&mut args, &body, "offset")?;
1249 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1250 check_args("triangle", body, args)?;
1251
1252 Ok(Box::new(shape::triangle(
1253 id.0, bag, border, blur, corners, offset, fill, outline, size,
1254 )))
1255}
1256
1257fn create_circle(lua: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1258 let bag = prop_no_children(&body)?;
1259
1260 let mut args: HashSet<&'static str> = HashSet::from_iter(["props"]);
1261 let border = get_arg_default(&mut args, &body, "border")?;
1262 let blur = get_arg_default(&mut args, &body, "blur")?;
1263 let fill = get_arg_default(&mut args, &body, "fill")?;
1264 let outline = get_arg_default(&mut args, &body, "outline")?;
1265 let radii = get_array_arg(&mut args, lua, &body, "radii", [0.0; 2])?;
1266 let size: DAbsPoint = get_arg_default(&mut args, &body, "size")?;
1267 check_args("circle", body, args)?;
1268
1269 Ok(Box::new(shape::circle(
1270 id.0, bag, border, blur, radii, fill, outline, size,
1271 )))
1272}
1273
1274const DEFAULT_LINE_HEIGHT: f32 = 1.2;
1276const DEFAULT_FONT_SIZE: f32 = 14.0;
1278
1279fn to_style(style: u8) -> LuaResult<cosmic_text::Style> {
1280 match style {
1281 0 => Ok(cosmic_text::Style::Normal),
1282 1 => Ok(cosmic_text::Style::Italic),
1283 2 => Ok(cosmic_text::Style::Oblique),
1284 _ => Err(LuaError::RuntimeError(format!(
1285 "{style} is not a valid style enum value!"
1286 ))),
1287 }
1288}
1289
1290fn to_wrap(wrap: u8) -> LuaResult<cosmic_text::Wrap> {
1291 match wrap {
1292 0 => Ok(cosmic_text::Wrap::None),
1293 1 => Ok(cosmic_text::Wrap::Glyph),
1294 2 => Ok(cosmic_text::Wrap::Word),
1295 3 => Ok(cosmic_text::Wrap::WordOrGlyph),
1296 _ => Err(LuaError::RuntimeError(format!(
1297 "{wrap} is not a valid wrap enum value!"
1298 ))),
1299 }
1300}
1301
1302fn to_align(align: Option<u8>) -> LuaResult<Option<cosmic_text::Align>> {
1303 if let Some(a) = align {
1304 Ok(Some(match a {
1305 0 => cosmic_text::Align::Left,
1306 1 => cosmic_text::Align::Right,
1307 2 => cosmic_text::Align::Center,
1308 3 => cosmic_text::Align::Justified,
1309 4 => cosmic_text::Align::End,
1310 _ => {
1311 return Err(LuaError::RuntimeError(format!(
1312 "{a} is not a valid align enum value!"
1313 )));
1314 }
1315 }))
1316 } else {
1317 Ok(None)
1318 }
1319}
1320
1321fn create_text(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1322 let bag = prop_no_children(&body)?;
1323
1324 let mut args = HashSet::from_iter(["props"]);
1325 let text = get_arg_required(&mut args, &body, "text")?;
1326 let font_size = get_arg_or(&mut args, &body, "fontsize", DEFAULT_FONT_SIZE)?;
1327 let line_height = get_arg_or(
1328 &mut args,
1329 &body,
1330 "lineheight",
1331 font_size * DEFAULT_LINE_HEIGHT,
1332 )?;
1333 let font: LuaFontFamily = get_arg_default(&mut args, &body, "font")?;
1334 let color = get_arg_required(&mut args, &body, "color")?;
1335 let weight: u16 = get_arg_or(&mut args, &body, "weight", cosmic_text::Weight::NORMAL.0)?;
1336 let style: u8 = get_arg_default(&mut args, &body, "style")?;
1337 let wrap: u8 = get_arg_default(&mut args, &body, "wrap")?;
1338 let align: Option<u8> = get_arg_default(&mut args, &body, "align")?;
1339 check_args("text", body, args)?;
1340
1341 let style = to_style(style)?;
1342 let wrap = to_wrap(wrap)?;
1343 let align = to_align(align)?;
1344
1345 Ok(Box::new(Text::<PropBag>::new(
1346 id.0,
1347 bag,
1348 font_size,
1349 line_height,
1350 text,
1351 font.0,
1352 color,
1353 cosmic_text::Weight(weight),
1354 style,
1355 wrap,
1356 align,
1357 )))
1358}
1359
1360fn create_textbox(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<ComponentBag> {
1361 let bag = prop_no_children(&body)?;
1362
1363 let mut args = HashSet::from_iter(["props"]);
1364 let font_size = get_arg_or(&mut args, &body, "fontsize", DEFAULT_FONT_SIZE)?;
1365 let line_height = get_arg_or(
1366 &mut args,
1367 &body,
1368 "lineheight",
1369 font_size * DEFAULT_LINE_HEIGHT,
1370 )?;
1371 let font: LuaFontFamily = get_arg_default(&mut args, &body, "font")?;
1372 let color = get_arg_required(&mut args, &body, "color")?;
1373 let weight: u16 = get_arg_or(&mut args, &body, "weight", cosmic_text::Weight::NORMAL.0)?;
1374 let style: u8 = get_arg_default(&mut args, &body, "style")?;
1375 let wrap: u8 = get_arg_default(&mut args, &body, "wrap")?;
1376 let align: Option<u8> = get_arg_default(&mut args, &body, "align")?;
1377 check_args("textbox", body, args)?;
1378
1379 let style = to_style(style)?;
1380 let wrap = to_wrap(wrap)?;
1381 let align = to_align(align)?;
1382
1383 Ok(Box::new(TextBox::<PropBag>::new(
1384 id.0,
1385 bag,
1386 font_size,
1387 line_height,
1388 font.0,
1389 color,
1390 cosmic_text::Weight(weight),
1391 style,
1392 wrap,
1393 align,
1394 )))
1395}
1396
1397fn create_id(_: &Lua, (parent, v): (Option<LuaSourceID>, LuaValue)) -> LuaResult<LuaSourceID> {
1398 if let Some(n) = v.as_integer() {
1399 if let Some(parent) = parent {
1400 Ok(LuaSourceID(parent.0.child(DataID::Int(n))))
1401 } else {
1402 #[cfg(debug_assertions)]
1403 panic!("NO PARENT! This means the layout was called incorrectly!");
1404 #[cfg(not(debug_assertions))]
1405 Err(LuaError::UserDataTypeMismatch)
1406 }
1407 } else if let Some(name) = v.as_string().map(|x| x.to_string_lossy()) {
1408 if let Some(parent) = parent {
1409 Ok(LuaSourceID(parent.0.child(DataID::Owned(name))))
1410 } else {
1411 #[cfg(debug_assertions)]
1412 panic!("NO PARENT! This means the layout was called incorrectly!");
1413 #[cfg(not(debug_assertions))]
1414 Err(LuaError::UserDataTypeMismatch)
1415 }
1416 } else {
1417 Err(LuaError::RuntimeError(format!(
1418 "Expected a number or string to construct an ID, but found {}",
1419 v.type_name()
1420 )))
1421 }
1422}
1423
1424fn replace_dualpoint(p: LuaDualPoint, bound: f32) -> dpi::Size {
1425 match p {
1426 LuaDualPoint::Px(mut point2_d) => {
1427 replace_limit(&mut point2_d, bound);
1428 dpi::Size::Physical(dpi::PhysicalSize::<u32>::new(
1429 point2_d.x.ceil() as u32,
1430 point2_d.y.ceil() as u32,
1431 ))
1432 }
1433 LuaDualPoint::Dp(mut point2_d) => {
1434 replace_limit(&mut point2_d, bound);
1435 dpi::Size::Logical(dpi::LogicalSize::<f64>::new(
1436 point2_d.x as f64,
1437 point2_d.y as f64,
1438 ))
1439 }
1440 }
1441}
1442
1443fn get_required_child<T: FromLua>(t: &LuaTable, idx: usize) -> LuaResult<T> {
1444 if !t.contains_key(idx)? {
1445 Err(LuaError::RuntimeError(format!(
1446 "Expected at least {idx} children, but found {}",
1447 t.raw_len()
1448 )))
1449 } else {
1450 t.get(idx)
1451 }
1452}
1453
1454fn create_window(_: &Lua, (id, body): (LuaSourceID, LuaTable)) -> LuaResult<Window> {
1455 let title: LuaString = get_required(&body, "title")?;
1456 let child: ComponentBag = get_required_child(&body, 1)?;
1457
1458 let mut attributes = winit::window::Window::default_attributes()
1459 .with_title(title.to_string_lossy())
1460 .with_resizable(get_or(&body, "resizeable", true)?)
1461 .with_maximized(get_or(&body, "maximized", false)?)
1462 .with_visible(get_or(&body, "visible", true)?)
1463 .with_transparent(get_or(&body, "transparent", false)?)
1464 .with_blur(get_or(&body, "blur", false)?)
1465 .with_decorations(get_or(&body, "decorated", true)?)
1466 .with_content_protected(get_or(&body, "protected", false)?)
1467 .with_active(get_or(&body, "focused", false)?);
1468
1469 if body.contains_key("icon")? {
1470 attributes.window_icon = Some(
1471 crate::resource::load_icon(&std::path::PathBuf::from(body.get::<String>("icon")?))
1472 .map_err(|e| LuaError::ExternalError(Arc::new(Box::new(e))))?,
1473 );
1474 }
1475
1476 if body.contains_key("minsize")? {
1477 attributes.min_inner_size = Some(replace_dualpoint(body.get("minsize")?, f32::NEG_INFINITY))
1478 }
1479
1480 if body.contains_key("maxsize")? {
1481 attributes.max_inner_size = Some(replace_dualpoint(body.get("maxsize")?, f32::INFINITY))
1482 }
1483
1484 if body.contains_key("size")? {
1485 attributes.inner_size = Some(replace_dualpoint(body.get("size")?, 0.0));
1486 }
1487
1488 Ok(Window::new(id.0, attributes, Box::new(child)))
1489}
1490
1491#[derive(PartialEq, Clone, Debug)]
1492pub struct LuaFragment {
1493 layout: LuaFunction,
1494 id_enter: LuaFunction,
1495}
1496
1497impl FromLua for LuaFragment {
1498 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
1499 let t = value.as_table().ok_or(LuaError::UserDataTypeMismatch)?;
1500 Ok(Self {
1501 layout: t.get("layout")?,
1502 id_enter: t.get("id_enter")?,
1503 })
1504 }
1505}
1506
1507impl IntoLua for LuaFragment {
1508 fn into_lua(self, lua: &Lua) -> LuaResult<LuaValue> {
1509 Ok(LuaValue::Table(lua.create_table_from([
1510 ("layout", self.layout),
1511 ("id_enter", self.id_enter),
1512 ])?))
1513 }
1514}
1515
1516impl LuaFragment {
1517 pub fn call<T: IntoLuaMulti>(&self, args: T, mut id: ScopeID<'_>) -> LuaResult<ComponentBag> {
1518 self.id_enter.call::<ComponentBag>((
1519 LuaSourceID(id.id().clone()),
1520 self.layout.clone(),
1521 args,
1522 ))
1523 }
1524}
1525
1526#[derive(Clone, Debug)]
1527pub struct LuaContext {
1528 feather: LuaTable,
1529 modules: LuaTable,
1530 id_enter: LuaFunction,
1531 require: LuaFunction,
1532 load_in_sandbox: LuaFunction,
1533 lua_handlers: LuaTable,
1534 handler_slots: LuaTable,
1535 handler_count: Rc<AtomicU64>,
1536}
1537
1538impl PartialEq for LuaContext {
1539 fn eq(&self, other: &Self) -> bool {
1540 self.feather == other.feather
1541 && self.modules == other.modules
1542 && self.id_enter == other.id_enter
1543 && self.require == other.require
1544 && self.load_in_sandbox == other.load_in_sandbox
1545 && self.lua_handlers == other.lua_handlers
1546 && self.handler_slots == other.handler_slots
1547 && self.handler_count.load(Ordering::Relaxed)
1548 == other.handler_count.load(Ordering::Relaxed)
1549 }
1550}
1551
1552impl LuaContext {
1553 pub fn new(lua: &Lua) -> LuaResult<Self> {
1554 let preload = lua.create_table()?;
1555
1556 preload.set("create_id", lua.create_function(create_id)?)?;
1557 preload.set("create_button", lua.create_function(create_button)?)?;
1558 preload.set(
1559 "create_domain_line",
1560 lua.create_function(create_domain_line)?,
1561 )?;
1562 preload.set(
1563 "create_domain_point",
1564 lua.create_function(create_domain_point)?,
1565 )?;
1566 preload.set("create_flexbox", lua.create_function(create_flexbox)?)?;
1567 preload.set("create_gridbox", lua.create_function(create_gridbox)?)?;
1568 preload.set("create_image", lua.create_function(create_image)?)?;
1569 preload.set("create_line", lua.create_function(create_line)?)?;
1570 preload.set("create_listbox", lua.create_function(create_listbox)?)?;
1571 preload.set("create_mousearea", lua.create_function(create_mousearea)?)?;
1572 preload.set("create_region", lua.create_function(create_region)?)?;
1574 preload.set("create_scrollarea", lua.create_function(create_scrollarea)?)?;
1575 preload.set("create_round_rect", lua.create_function(create_round_rect)?)?;
1576 preload.set("create_arc", lua.create_function(create_arc)?)?;
1577 preload.set("create_triangle", lua.create_function(create_triangle)?)?;
1578 preload.set("create_circle", lua.create_function(create_circle)?)?;
1579 preload.set("create_text", lua.create_function(create_text)?)?;
1580 preload.set("create_textbox", lua.create_function(create_textbox)?)?;
1581 preload.set("create_window", lua.create_function(create_window)?)?;
1582
1583 let preload_mt = lua.create_table()?;
1584 preload_mt.set("__index", lua.globals())?;
1585 preload.set_metatable(Some(preload_mt))?;
1586
1587 let feather: LuaTable = lua
1588 .load(NamedChunk(FEATHER, "feather"))
1589 .set_environment(preload.clone())
1590 .eval()?;
1591
1592 let id_enter = feather.get::<LuaTable>("ID")?.get::<LuaFunction>("enter")?;
1593 let lua_handlers = lua.create_table()?;
1594 let handlers = lua_handlers.clone();
1595 let handler_slots = lua.create_table()?;
1596 let slot_clone = handler_slots.clone();
1597 let handler_count = Rc::new(AtomicU64::new(0));
1598 let handler_count_clone = handler_count.clone();
1599
1600 feather.set(
1601 "add_handler",
1602 lua.create_function(move |_, (name, func): (LuaString, LuaFunction)| {
1603 let count = handler_count_clone.fetch_add(1, Ordering::Relaxed);
1604 handlers.set(&name, func)?;
1605 slot_clone.set(name, Slot(APP_SOURCE_ID.into(), count))?;
1606 Ok(LuaNil)
1607 })?,
1608 )?;
1609
1610 let modules = lua.create_table()?;
1611 preload.set("injected_dep", &modules)?;
1612
1613 lua.load(NamedChunk(SANDBOX, "sandbox")).exec()?;
1614
1615 let require: LuaFunction = lua
1616 .load(
1617 r#"
1618package = {preload = {}, loaded = injected_dep}
1619return function(name) -- require stub for inside sandbox
1620 if not package.loaded[name] then
1621 if not package.preload[name] then
1622 error("Couldn't find package.preload for " .. name)
1623 end
1624 package.loaded[name] = package.preload[name]()
1625 end
1626 return package.loaded[name]
1627end
1628 "#,
1629 )
1630 .set_environment(preload)
1631 .eval()?;
1632
1633 lua.load(
1634 r#"
1635 jit.opt.start("maxtrace=10000")
1636 jit.opt.start("maxmcode=4096")
1637 jit.opt.start("recunroll=5")
1638 jit.opt.start("loopunroll=60")
1639
1640 --local create_module = sandbox_impl(true)
1641 create_module = function(bytes, name, interface)
1642 return load(bytes, name, "t", setmetatable(interface, {__index=_G}))
1643 end
1644
1645
1646 function load_in_sandbox(bytes, name, additional_interface)
1647 local r, err = create_module(bytes, name, additional_interface)
1648 if r == nil then
1649 error(err)
1650 end
1651
1652 return r()
1653 end
1654 "#,
1655 )
1656 .exec()?;
1657
1658 let load_in_sandbox: LuaFunction = lua.load("load_in_sandbox").eval()?;
1659
1660 Ok(Self {
1661 feather,
1662 id_enter,
1663 load_in_sandbox,
1664 require,
1665 modules,
1666 lua_handlers,
1667 handler_slots,
1668 handler_count,
1669 })
1670 }
1671
1672 pub fn add_module(&self, lua: &Lua, name: &str, module: &[u8]) -> LuaResult<()> {
1673 let interface = lua.create_table()?;
1674 interface.set("f", self.feather.clone())?;
1675 interface.set("require", self.require.clone())?;
1676
1677 self.modules.set(
1678 name,
1679 self.load_in_sandbox
1680 .call::<LuaValue>((lua.create_string(module)?, name, interface))?,
1681 )
1682 }
1683
1684 pub fn get_module(&self, name: &str) -> Option<LuaValue> {
1685 self.modules.get(name).ok().and_then(|x| match x {
1686 Some(LuaNil) => None,
1687 x => x,
1688 })
1689 }
1690
1691 pub fn reserve_handler(&self, name: &str) -> LuaResult<u64> {
1692 let index = self.handler_count.fetch_add(1, Ordering::Relaxed);
1693 self.handler_slots
1694 .set(name, Slot(APP_SOURCE_ID.into(), index))?;
1695 Ok(index)
1696 }
1697
1698 fn load_layout<R: FromLuaMulti>(&self, lua: &Lua, layout: &[u8]) -> LuaResult<R> {
1699 let interface = lua.create_table()?;
1700 interface.set("handlers", self.handler_slots.clone())?;
1701 interface.set("f", self.feather.clone())?;
1702 interface.set("require", self.require.clone())?;
1703
1704 self.load_in_sandbox
1705 .call((lua.create_string(layout)?, "layout", interface))
1706 }
1707
1708 pub fn load_fragment(&self, lua: &Lua, layout: &[u8]) -> LuaResult<LuaFragment> {
1709 Ok(LuaFragment {
1710 layout: self.load_layout(lua, layout)?,
1711 id_enter: self.id_enter.clone(),
1712 })
1713 }
1714
1715 pub fn get_handlers<AppData: FromLua + IntoLua + Clone>(
1716 &self,
1717 mut reserved: HashMap<u64, crate::AppEvent<AppData>>,
1718 ) -> LuaResult<Vec<crate::AppEvent<AppData>>> {
1719 use std::mem::MaybeUninit;
1720
1721 let count = self.handler_count.load(Ordering::Relaxed) as usize;
1722 let mut handlers: Vec<crate::AppEvent<AppData>> = Vec::with_capacity(count);
1723 let spare = handlers.spare_capacity_mut();
1724
1725 for v in self.handler_slots.pairs::<LuaString, Slot>() {
1726 let (key, slot) = v?;
1727 if self.lua_handlers.contains_key(&key)? {
1728 let func: LuaFunction = self.lua_handlers.get(&key)?;
1729 spare[slot.1 as usize] = MaybeUninit::new(Box::new(
1730 move |pair: crate::DispatchPair, mut state: AccessCell<AppData>| {
1731 let (appdata, v): (AppData, LuaValue) =
1732 match func.call((pair.0, state.value.clone())) {
1733 Ok(v) => v,
1734 Err(e) => return InputResult::Error(e.into()),
1735 };
1736 *state = appdata;
1737 if v.as_boolean().unwrap_or(true) {
1738 InputResult::Consume(())
1739 } else {
1740 InputResult::Forward(())
1741 }
1742 },
1743 ));
1744 } else {
1745 spare[slot.1 as usize] = MaybeUninit::new(
1746 reserved
1747 .remove(&slot.1)
1748 .expect("Reserved slot had no entry in `reserved` map!"),
1749 )
1750 }
1751 }
1752
1753 unsafe {
1754 handlers.set_len(count);
1755 }
1756
1757 Ok(handlers)
1758 }
1759
1760 pub fn update_handler<AppData: FromLua + IntoLua + Clone + 'static>(
1761 &self,
1762 lua: &Lua,
1763 sender: std::sync::mpsc::Sender<crate::EventPair<AppData>>,
1764 count: AtomicU64,
1765 ) -> LuaResult<()> {
1766 let slot_clone = self.handler_slots.clone();
1767 self.feather.set(
1768 "add_handler",
1769 lua.create_function(move |_, (name, func): (LuaString, LuaFunction)| {
1770 let count = count.fetch_add(1, Ordering::Relaxed);
1771 sender
1772 .send((
1773 count,
1774 Box::new(
1775 move |pair: crate::DispatchPair, mut state: AccessCell<AppData>| {
1776 let (appdata, v): (AppData, LuaValue) =
1777 match func.call((pair.0, state.value.clone())) {
1778 Ok(v) => v,
1779 Err(e) => return InputResult::Error(e.into()),
1780 };
1781 *state = appdata;
1782 if v.as_boolean().unwrap_or(true) {
1783 InputResult::Consume(())
1784 } else {
1785 InputResult::Forward(())
1786 }
1787 },
1788 ),
1789 ))
1790 .unwrap();
1791 let slot = Slot(APP_SOURCE_ID.into(), count);
1792 slot_clone.set(name, slot.clone())?;
1793 Ok(slot)
1794 })?,
1795 )?;
1796
1797 Ok(())
1798 }
1799}
1800
1801pub struct LuaApp<AppData: Clone + FromLua + IntoLua>(crate::App<AppData, LuaPersist<AppData>>);
1802
1803impl<AppData: Clone + FromLua + IntoLua + PartialEq + 'static> LuaApp<AppData> {
1804 pub fn new<T>(
1805 lua: &Lua,
1806 app_state: AppData,
1807 handlers: Vec<(String, crate::AppEvent<AppData>)>,
1808 layout: &[u8],
1809 ) -> eyre::Result<(Self, crate::EventLoop<T>)> {
1810 let ctx = LuaContext::new(lua)?;
1811
1812 let mut reserved = HashMap::new();
1813 for (k, v) in handlers.into_iter() {
1814 reserved.insert(ctx.reserve_handler(&k)?, v);
1815 }
1816
1817 let (window, init): (LuaFunction, Option<LuaFunction>) = ctx.load_layout(lua, layout)?;
1818
1819 let (app, event, sender, count) = crate::App::new(
1820 app_state.clone(),
1821 ctx.get_handlers(reserved)?,
1822 LuaPersist {
1823 window,
1824 id_enter: ctx.id_enter.clone(),
1825 init: if let Some(init) = init {
1826 Ok(init)
1827 } else {
1828 Err(app_state)
1829 },
1830 phantom: PhantomData,
1831 },
1832 |_| (),
1833 )?;
1834
1835 ctx.update_handler(lua, sender, count)?;
1837 Ok((Self(app), event))
1838 }
1839}
1840
1841impl<AppData: Clone + FromLua + IntoLua + PartialEq + 'static, T: 'static> ApplicationHandler<T>
1842 for LuaApp<AppData>
1843{
1844 fn new_events(
1845 &mut self,
1846 event_loop: &winit::event_loop::ActiveEventLoop,
1847 cause: winit::event::StartCause,
1848 ) {
1849 <crate::App<AppData, LuaPersist<AppData>> as ApplicationHandler<T>>::new_events(
1850 &mut self.0,
1851 event_loop,
1852 cause,
1853 );
1854 }
1855
1856 fn user_event(&mut self, event_loop: &winit::event_loop::ActiveEventLoop, event: T) {
1857 self.0.user_event(event_loop, event);
1858 }
1859
1860 fn device_event(
1861 &mut self,
1862 event_loop: &winit::event_loop::ActiveEventLoop,
1863 device_id: winit::event::DeviceId,
1864 event: winit::event::DeviceEvent,
1865 ) {
1866 <crate::App<AppData, LuaPersist<AppData>> as ApplicationHandler<T>>::device_event(
1867 &mut self.0,
1868 event_loop,
1869 device_id,
1870 event,
1871 );
1872 }
1873
1874 fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1875 <crate::App<AppData, LuaPersist<AppData>> as ApplicationHandler<T>>::about_to_wait(
1876 &mut self.0,
1877 event_loop,
1878 );
1879 }
1880
1881 fn suspended(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1882 <crate::App<AppData, LuaPersist<AppData>> as ApplicationHandler<T>>::suspended(
1883 &mut self.0,
1884 event_loop,
1885 );
1886 }
1887
1888 fn exiting(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1889 <crate::App<AppData, LuaPersist<AppData>> as ApplicationHandler<T>>::exiting(
1890 &mut self.0,
1891 event_loop,
1892 );
1893 }
1894
1895 fn memory_warning(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1896 <crate::App<AppData, LuaPersist<AppData>> as ApplicationHandler<T>>::memory_warning(
1897 &mut self.0,
1898 event_loop,
1899 );
1900 }
1901
1902 fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
1903 <crate::App<AppData, LuaPersist<AppData>> as ApplicationHandler<T>>::resumed(
1904 &mut self.0,
1905 event_loop,
1906 );
1907 }
1908
1909 fn window_event(
1910 &mut self,
1911 event_loop: &winit::event_loop::ActiveEventLoop,
1912 window_id: winit::window::WindowId,
1913 event: winit::event::WindowEvent,
1914 ) {
1915 <crate::App<AppData, LuaPersist<AppData>> as ApplicationHandler<T>>::window_event(
1916 &mut self.0,
1917 event_loop,
1918 window_id,
1919 event,
1920 )
1921 }
1922}