1use std::{
2 borrow::Cow,
3 hash::{
4 Hash,
5 Hasher,
6 },
7};
8
9use paste::paste;
10use rustc_hash::{
11 FxHashMap,
12 FxHasher,
13};
14use torin::{
15 content::Content,
16 gaps::Gaps,
17 prelude::{
18 Alignment,
19 Direction,
20 Length,
21 Position,
22 VisibleSize,
23 },
24 size::{
25 Size,
26 SizeFn,
27 SizeFnContext,
28 },
29};
30
31use crate::{
32 data::{
33 AccessibilityData,
34 LayoutData,
35 TextStyleData,
36 },
37 diff_key::DiffKey,
38 element::{
39 Element,
40 EventHandlerType,
41 },
42 elements::image::{
43 AspectRatio,
44 ImageCover,
45 ImageData,
46 SamplingMode,
47 },
48 event_handler::EventHandler,
49 events::{
50 data::{
51 Event,
52 KeyboardEventData,
53 MouseEventData,
54 SizedEventData,
55 WheelEventData,
56 },
57 name::EventName,
58 },
59 layers::Layer,
60 prelude::*,
61 style::{
62 font_size::FontSize,
63 font_slant::FontSlant,
64 font_weight::FontWeight,
65 font_width::FontWidth,
66 text_height::TextHeightBehavior,
67 text_overflow::TextOverflow,
68 text_shadow::TextShadow,
69 },
70};
71
72pub trait SizeExt {
73 fn auto() -> Size;
74 fn fill() -> Size;
75 fn fill_minimum() -> Size;
76 fn percent(percent: impl Into<f32>) -> Size;
77 fn px(px: impl Into<f32>) -> Size;
78 fn window_percent(percent: impl Into<f32>) -> Size;
79 fn flex(flex: impl Into<f32>) -> Size;
80 fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size;
81 fn func_data<D: Hash>(
82 func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
83 data: &D,
84 ) -> Size;
85}
86
87impl SizeExt for Size {
88 fn auto() -> Size {
89 Size::Inner
90 }
91
92 fn fill() -> Size {
93 Size::Fill
94 }
95
96 fn fill_minimum() -> Size {
97 Size::FillMinimum
98 }
99
100 fn percent(percent: impl Into<f32>) -> Size {
101 Size::Percentage(Length::new(percent.into()))
102 }
103
104 fn px(px: impl Into<f32>) -> Size {
105 Size::Pixels(Length::new(px.into()))
106 }
107
108 fn window_percent(percent: impl Into<f32>) -> Size {
109 Size::RootPercentage(Length::new(percent.into()))
110 }
111
112 fn flex(flex: impl Into<f32>) -> Size {
113 Size::Flex(Length::new(flex.into()))
114 }
115
116 fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size {
117 Self::Fn(Box::new(SizeFn::new(func)))
118 }
119
120 fn func_data<D: Hash>(
121 func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
122 data: &D,
123 ) -> Size {
124 Self::Fn(Box::new(SizeFn::new_data(func, data)))
125 }
126}
127
128pub trait DirectionExt {
129 fn vertical() -> Direction;
130 fn horizontal() -> Direction;
131}
132
133impl DirectionExt for Direction {
134 fn vertical() -> Direction {
135 Direction::Vertical
136 }
137 fn horizontal() -> Direction {
138 Direction::Horizontal
139 }
140}
141
142pub trait AlignmentExt {
143 fn start() -> Alignment;
144 fn center() -> Alignment;
145 fn end() -> Alignment;
146 fn space_between() -> Alignment;
147 fn space_evenly() -> Alignment;
148 fn space_around() -> Alignment;
149}
150
151impl AlignmentExt for Alignment {
152 fn start() -> Alignment {
153 Alignment::Start
154 }
155
156 fn center() -> Alignment {
157 Alignment::Center
158 }
159
160 fn end() -> Alignment {
161 Alignment::End
162 }
163
164 fn space_between() -> Alignment {
165 Alignment::SpaceBetween
166 }
167
168 fn space_evenly() -> Alignment {
169 Alignment::SpaceEvenly
170 }
171
172 fn space_around() -> Alignment {
173 Alignment::SpaceAround
174 }
175}
176
177pub trait ContentExt {
178 fn normal() -> Content;
179 fn fit() -> Content;
180 fn flex() -> Content;
181 fn wrap() -> Content;
182}
183
184impl ContentExt for Content {
185 fn normal() -> Content {
186 Content::Normal
187 }
188
189 fn fit() -> Content {
190 Content::Fit
191 }
192
193 fn flex() -> Content {
194 Content::Flex
195 }
196
197 fn wrap() -> Content {
198 Content::Wrap
199 }
200}
201
202pub trait VisibleSizeExt {
203 fn full() -> VisibleSize;
204 fn inner_percent(value: impl Into<f32>) -> VisibleSize;
205}
206
207impl VisibleSizeExt for VisibleSize {
208 fn full() -> VisibleSize {
209 VisibleSize::Full
210 }
211
212 fn inner_percent(value: impl Into<f32>) -> VisibleSize {
213 VisibleSize::InnerPercentage(Length::new(value.into()))
214 }
215}
216
217pub trait ChildrenExt: Sized {
218 fn get_children(&mut self) -> &mut Vec<Element>;
219
220 fn children(mut self, children: impl IntoIterator<Item = Element>) -> Self
221 {
222 self.get_children().extend(children);
223 self
224 }
225
226 fn maybe_child<C: IntoElement>(mut self, child: Option<C>) -> Self {
227 if let Some(child) = child {
228 self.get_children().push(child.into_element());
229 }
230 self
231 }
232
233 fn child<C: IntoElement>(mut self, child: C) -> Self {
234 self.get_children().push(child.into_element());
235 self
236 }
237}
238
239pub trait KeyExt: Sized {
240 fn write_key(&mut self) -> &mut DiffKey;
241
242 fn key(mut self, key: impl Hash) -> Self {
243 let mut hasher = FxHasher::default();
244 key.hash(&mut hasher);
245 *self.write_key() = DiffKey::U64(hasher.finish());
246 self
247 }
248}
249
250pub trait ListExt {
251 fn with(self, other: Self) -> Self;
252}
253
254impl<T> ListExt for Vec<T> {
255 fn with(mut self, other: Self) -> Self {
256 self.extend(other);
257 self
258 }
259}
260
261macro_rules! event_handlers {
262 (
263 $handler_variant:ident, $event_data:ty;
264 $(
265 $name:ident => $event_variant:expr ;
266 )*
267 ) => {
268 paste! {
269 $(
270 fn [<on_$name>](mut self, [<on_$name>]: impl Into<EventHandler<Event<$event_data>>>) -> Self {
271 self.get_event_handlers()
272 .insert($event_variant, EventHandlerType::$handler_variant([<on_$name>].into()));
273 self
274 }
275 )*
276 }
277 };
278}
279
280pub trait EventHandlersExt: Sized + LayoutExt {
281 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType>;
282
283 event_handlers! {
284 Mouse,
285 MouseEventData;
286
287 mouse_down => EventName::MouseDown;
288 mouse_up => EventName::MouseUp;
289 mouse_move => EventName::MouseMove;
290
291 global_mouse_up => EventName::GlobalMouseUp;
292 global_mouse_down => EventName::GlobalMouseDown;
293 global_mouse_move => EventName::GlobalMouseMove;
294
295 capture_global_mouse_move => EventName::CaptureGlobalMouseMove;
296 capture_global_mouse_up => EventName::CaptureGlobalMouseUp;
297 }
298
299 event_handlers! {
300 Keyboard,
301 KeyboardEventData;
302
303 key_down => EventName::KeyDown;
304 key_up => EventName::KeyUp;
305
306 global_key_down => EventName::GlobalKeyDown;
307 global_key_up => EventName::GlobalKeyUp;
308 }
309
310 event_handlers! {
311 Wheel,
312 WheelEventData;
313
314 wheel => EventName::Wheel;
315 }
316
317 event_handlers! {
318 Touch,
319 TouchEventData;
320
321 touch_cancel => EventName::TouchCancel;
322 touch_start => EventName::TouchStart;
323 touch_move => EventName::TouchMove;
324 touch_end => EventName::TouchEnd;
325 }
326
327 event_handlers! {
328 Pointer,
329 PointerEventData;
330
331 pointer_press => EventName::PointerPress;
332 pointer_down => EventName::PointerDown;
333 pointer_enter => EventName::PointerEnter;
334 pointer_leave => EventName::PointerLeave;
335 }
336
337 event_handlers! {
338 File,
339 FileEventData;
340
341 file_drop => EventName::FileDrop;
342 global_file_hover => EventName::GlobalFileHover;
343 global_file_hover_cancelled => EventName::GlobalFileHoverCancelled;
344 }
345
346 event_handlers! {
347 ImePreedit,
348 ImePreeditEventData;
349
350 ime_preedit => EventName::ImePreedit;
351 }
352
353 fn on_sized(mut self, on_sized: impl Into<EventHandler<Event<SizedEventData>>>) -> Self {
354 self.get_event_handlers()
355 .insert(EventName::Sized, EventHandlerType::Sized(on_sized.into()));
356 self.get_layout().layout.has_layout_references = true;
357 self
358 }
359
360 fn on_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
367 let on_press = on_press.into();
368 self.on_pointer_press({
369 let on_press = on_press.clone();
370 move |e: Event<PointerEventData>| {
371 let event = e.try_map(|d| match d {
372 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
373 Some(PressEventData::Mouse(m))
374 }
375 PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
376 _ => None,
377 });
378 if let Some(event) = event {
379 on_press.call(event);
380 }
381 }
382 })
383 .on_key_down({
384 let on_press = on_press.clone();
385 move |e: Event<KeyboardEventData>| {
386 if Focus::is_pressed(&e) {
387 on_press.call(e.map(PressEventData::Keyboard))
388 }
389 }
390 })
391 }
392
393 fn on_secondary_press(
397 self,
398 on_pointer_press: impl Into<EventHandler<Event<PressEventData>>>,
399 ) -> Self {
400 let on_pointer_press = on_pointer_press.into();
401 self.on_pointer_press({
402 let on_pointer_press = on_pointer_press.clone();
403 move |e: Event<PointerEventData>| {
404 let event = e.try_map(|d| match d {
405 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Right) => {
406 Some(PressEventData::Mouse(m))
407 }
408 _ => None,
409 });
410 if let Some(event) = event {
411 on_pointer_press.call(event);
412 }
413 }
414 })
415 }
416
417 fn on_all_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
422 let on_press = on_press.into();
423 self.on_pointer_press({
424 let on_press = on_press.clone();
425 move |e: Event<PointerEventData>| {
426 let event = e.try_map(|d| match d {
427 PointerEventData::Mouse(m) => Some(PressEventData::Mouse(m)),
428 PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
429 });
430 if let Some(event) = event {
431 on_press.call(event);
432 }
433 }
434 })
435 .on_key_down({
436 let on_press = on_press.clone();
437 move |e: Event<KeyboardEventData>| {
438 if Focus::is_pressed(&e) {
439 on_press.call(e.map(PressEventData::Keyboard))
440 }
441 }
442 })
443 }
444}
445
446#[derive(Debug, Clone, PartialEq)]
447pub enum PressEventData {
448 Mouse(MouseEventData),
449 Keyboard(KeyboardEventData),
450 Touch(TouchEventData),
451}
452
453pub trait ContainerWithContentExt
454where
455 Self: LayoutExt,
456{
457 fn direction(mut self, direction: Direction) -> Self {
458 self.get_layout().layout.direction = direction;
459 self
460 }
461 fn main_align(mut self, main_align: Alignment) -> Self {
462 self.get_layout().layout.main_alignment = main_align;
463 self
464 }
465
466 fn cross_align(mut self, cross_align: Alignment) -> Self {
467 self.get_layout().layout.cross_alignment = cross_align;
468 self
469 }
470
471 fn spacing(mut self, spacing: impl Into<f32>) -> Self {
472 self.get_layout().layout.spacing = Length::new(spacing.into());
473 self
474 }
475
476 fn content(mut self, content: Content) -> Self {
477 self.get_layout().layout.content = content;
478 self
479 }
480 fn center(mut self) -> Self {
481 self.get_layout().layout.main_alignment = Alignment::Center;
482 self.get_layout().layout.cross_alignment = Alignment::Center;
483
484 self
485 }
486
487 fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
488 self.get_layout().layout.offset_x = Length::new(offset_x.into());
489 self
490 }
491
492 fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
493 self.get_layout().layout.offset_y = Length::new(offset_y.into());
494 self
495 }
496
497 fn vertical(mut self) -> Self {
498 self.get_layout().layout.direction = Direction::vertical();
499 self
500 }
501
502 fn horizontal(mut self) -> Self {
503 self.get_layout().layout.direction = Direction::horizontal();
504 self
505 }
506}
507
508pub trait ContainerSizeExt
509where
510 Self: LayoutExt,
511{
512 fn width(mut self, width: impl Into<Size>) -> Self {
513 self.get_layout().layout.width = width.into();
514 self
515 }
516
517 fn height(mut self, height: impl Into<Size>) -> Self {
518 self.get_layout().layout.height = height.into();
519 self
520 }
521
522 fn expanded(mut self) -> Self {
524 self.get_layout().layout.width = Size::fill();
525 self.get_layout().layout.height = Size::fill();
526 self
527 }
528}
529
530impl<T: ContainerExt> ContainerSizeExt for T {}
531
532pub trait ContainerExt
533where
534 Self: LayoutExt,
535{
536 fn position(mut self, position: impl Into<Position>) -> Self {
537 self.get_layout().layout.position = position.into();
538 self
539 }
540
541 fn padding(mut self, padding: impl Into<Gaps>) -> Self {
542 self.get_layout().layout.padding = padding.into();
543 self
544 }
545
546 fn margin(mut self, margin: impl Into<Gaps>) -> Self {
547 self.get_layout().layout.margin = margin.into();
548 self
549 }
550
551 fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
552 self.get_layout().layout.minimum_width = minimum_width.into();
553 self
554 }
555
556 fn min_height(mut self, minimum_height: impl Into<Size>) -> Self {
557 self.get_layout().layout.minimum_height = minimum_height.into();
558 self
559 }
560
561 fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
562 self.get_layout().layout.maximum_width = maximum_width.into();
563 self
564 }
565
566 fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
567 self.get_layout().layout.maximum_height = maximum_height.into();
568 self
569 }
570
571 fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
572 self.get_layout().layout.visible_width = visible_width.into();
573 self
574 }
575
576 fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
577 self.get_layout().layout.visible_height = visible_height.into();
578 self
579 }
580}
581
582pub trait LayoutExt
583where
584 Self: Sized,
585{
586 fn get_layout(&mut self) -> &mut LayoutData;
587
588 fn layout(mut self, layout: LayoutData) -> Self {
589 *self.get_layout() = layout;
590 self
591 }
592}
593
594pub trait ImageExt
595where
596 Self: LayoutExt,
597{
598 fn width(mut self, width: Size) -> Self {
599 self.get_layout().layout.width = width;
600 self
601 }
602
603 fn height(mut self, height: Size) -> Self {
604 self.get_layout().layout.height = height;
605 self
606 }
607
608 fn get_image_data(&mut self) -> &mut ImageData;
609
610 fn image_data(mut self, image_data: ImageData) -> Self {
611 *self.get_image_data() = image_data;
612 self
613 }
614
615 fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
616 self.get_image_data().sampling_mode = sampling_mode;
617 self
618 }
619
620 fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
621 self.get_image_data().aspect_ratio = aspect_ratio;
622 self
623 }
624
625 fn image_cover(mut self, image_cover: ImageCover) -> Self {
626 self.get_image_data().image_cover = image_cover;
627 self
628 }
629}
630
631pub trait AccessibilityExt: Sized {
632 fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
633
634 fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
635 *self.get_accessibility_data() = accessibility;
636 self
637 }
638
639 fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
640 self.get_accessibility_data().a11y_id = a11y_id.into();
641 self
642 }
643
644 fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
645 self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
646 self
647 }
648
649 fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
650 self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
651 self
652 }
653
654 fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
655 self.get_accessibility_data()
656 .builder
657 .set_member_of(a11y_member_of.into());
658 self
659 }
660
661 fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
662 self.get_accessibility_data()
663 .builder
664 .set_role(a11y_role.into());
665 self
666 }
667
668 fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
669 self.get_accessibility_data().builder.set_label(value);
670 self
671 }
672
673 fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
674 with(&mut self.get_accessibility_data().builder);
675 self
676 }
677}
678
679pub trait TextStyleExt
680where
681 Self: Sized,
682{
683 fn get_text_style_data(&mut self) -> &mut TextStyleData;
684
685 fn color(mut self, color: impl Into<Color>) -> Self {
686 self.get_text_style_data().color = Some(color.into());
687 self
688 }
689
690 fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
691 self.get_text_style_data().text_align = Some(text_align.into());
692 self
693 }
694
695 fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
696 self.get_text_style_data().font_size = Some(font_size.into());
697 self
698 }
699
700 fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
701 self.get_text_style_data()
702 .font_families
703 .push(font_family.into());
704 self
705 }
706
707 fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
708 self.get_text_style_data().font_slant = Some(font_slant.into());
709 self
710 }
711
712 fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
713 self.get_text_style_data().font_weight = Some(font_weight.into());
714 self
715 }
716
717 fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
718 self.get_text_style_data().font_width = Some(font_width.into());
719 self
720 }
721
722 fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
723 self.get_text_style_data().text_height = Some(text_height.into());
724 self
725 }
726
727 fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
728 self.get_text_style_data().text_overflow = Some(text_overflow.into());
729 self
730 }
731
732 fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
733 self.get_text_style_data()
734 .text_shadows
735 .push(text_shadow.into());
736 self
737 }
738}
739
740pub trait StyleExt
741where
742 Self: Sized,
743{
744 fn get_style(&mut self) -> &mut StyleState;
745
746 fn background<S: Into<Color>>(mut self, background: S) -> Self {
747 self.get_style().background = Fill::Color(background.into());
748 self
749 }
750
751 fn background_conic_gradient<S: Into<ConicGradient>>(mut self, background: S) -> Self {
752 self.get_style().background = Fill::ConicGradient(Box::new(background.into()));
753 self
754 }
755
756 fn background_linear_gradient<S: Into<LinearGradient>>(mut self, background: S) -> Self {
757 self.get_style().background = Fill::LinearGradient(Box::new(background.into()));
758 self
759 }
760
761 fn background_radial_gradient<S: Into<RadialGradient>>(mut self, background: S) -> Self {
762 self.get_style().background = Fill::RadialGradient(Box::new(background.into()));
763 self
764 }
765
766 fn border(mut self, border: impl Into<Option<Border>>) -> Self {
767 if let Some(border) = border.into() {
768 self.get_style().borders.push(border);
769 }
770 self
771 }
772
773 fn shadow(mut self, shadow: impl Into<Shadow>) -> Self {
774 self.get_style().shadows.push(shadow.into());
775 self
776 }
777
778 fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
779 self.get_style().corner_radius = corner_radius.into();
780 self
781 }
782}
783
784impl<T: StyleExt> CornerRadiusExt for T {
785 fn with_corner_radius(mut self, corner_radius: f32) -> Self {
786 self.get_style().corner_radius = CornerRadius::new_all(corner_radius);
787 self
788 }
789}
790
791pub trait CornerRadiusExt: Sized {
792 fn with_corner_radius(self, corner_radius: f32) -> Self;
793
794 fn rounded_none(self) -> Self {
796 self.with_corner_radius(0.)
797 }
798
799 fn rounded(self) -> Self {
801 self.with_corner_radius(6.)
802 }
803
804 fn rounded_sm(self) -> Self {
806 self.with_corner_radius(4.)
807 }
808
809 fn rounded_md(self) -> Self {
811 self.with_corner_radius(6.)
812 }
813
814 fn rounded_lg(self) -> Self {
816 self.with_corner_radius(8.)
817 }
818
819 fn rounded_xl(self) -> Self {
821 self.with_corner_radius(12.)
822 }
823
824 fn rounded_2xl(self) -> Self {
826 self.with_corner_radius(16.)
827 }
828
829 fn rounded_3xl(self) -> Self {
831 self.with_corner_radius(24.)
832 }
833
834 fn rounded_4xl(self) -> Self {
836 self.with_corner_radius(32.)
837 }
838
839 fn rounded_full(self) -> Self {
841 self.with_corner_radius(99.)
842 }
843}
844
845pub trait MaybeExt
846where
847 Self: Sized,
848{
849 fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
850 if bool.into() { then(self) } else { self }
851 }
852
853 fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
854 if let Some(data) = data {
855 then(self, data)
856 } else {
857 self
858 }
859 }
860}
861
862pub trait LayerExt
863where
864 Self: Sized,
865{
866 fn get_layer(&mut self) -> &mut Layer;
867
868 fn layer(mut self, layer: impl Into<Layer>) -> Self {
869 *self.get_layer() = layer.into();
870 self
871 }
872}
873
874pub trait ScrollableExt
875where
876 Self: Sized,
877{
878 fn get_effect(&mut self) -> &mut EffectData;
879
880 fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
881 self.get_effect().scrollable = scrollable.into();
882 self
883 }
884}
885
886pub trait InteractiveExt
887where
888 Self: Sized,
889{
890 fn get_effect(&mut self) -> &mut EffectData;
891
892 fn interactive(mut self, interactive: impl Into<Interactive>) -> Self {
893 self.get_effect().interactive = interactive.into();
894 self
895 }
896}