1use std::any::Any;
2use std::cell::Cell;
3use std::rc::Rc;
4
5use sequoia_core::{Color, ImageSource, Point, Rect, RenderList, Size, Vec2, WindowId};
6use sequoia_draw::ShapeCache;
7use sequoia_input::{CursorIcon, Event, EventPhase, Modifiers, PenTool};
8use sequoia_layout::{
9 ContainerStyle, DockStyle, GridPlacement, GridStyle, LayoutCtx, LayoutStyle, WrapStyle,
10};
11use sequoia_reactive::Environment;
12use sequoia_style::{BoxStyle, Theme, VisualState};
13use sequoia_text::{
14 ElideMode, FontStyle, FontWeight, LetterSpacing, RichAlign, RichParagraph, TextSpan,
15 TextSystem, WrapMode,
16};
17
18use crate::any::AnyView;
19use crate::dnd::{DragEnd, DragOptions, DragPayload, DragSession, DropPosition};
20use crate::live::Prop;
21use crate::overlay::{OverlayEntry, OverlayId};
22use crate::runtime::{ElementId, Runtime};
23use crate::surface::SurfaceInputBinding;
24
25pub trait View {
26 fn body(&self) -> impl View + 'static;
27
28 #[doc(hidden)]
29 fn is_leaf(&self) -> bool {
30 false
31 }
32
33 #[doc(hidden)]
34 fn children_override(&self) -> Option<Vec<AnyView>> {
35 None
36 }
37
38 #[doc(hidden)]
39 fn keyed_children(&self) -> Option<Vec<(u64, AnyView)>> {
40 None
41 }
42
43 #[doc(hidden)]
44 fn layout_style(&self) -> LayoutStyle {
45 LayoutStyle::default()
46 }
47
48 fn layout_style_ctx(&self, _rt: &Runtime, _el: ElementId) -> LayoutStyle {
49 self.layout_style()
50 }
51
52 #[doc(hidden)]
53 fn has_visual_state(&self) -> bool {
54 false
55 }
56
57 #[doc(hidden)]
58 fn wants_hover_updates(&self) -> bool {
59 false
60 }
61
62 fn accessibility(&self) -> Option<crate::a11y::Accessible> {
63 None
64 }
65
66 #[doc(hidden)]
67 fn container(&self) -> Option<ContainerStyle> {
68 None
69 }
70
71 #[doc(hidden)]
72 fn grid(&self) -> Option<GridStyle> {
73 None
74 }
75
76 #[doc(hidden)]
77 fn dock(&self) -> Option<DockStyle> {
78 None
79 }
80
81 #[doc(hidden)]
82 fn wrap(&self) -> Option<WrapStyle> {
83 None
84 }
85
86 #[doc(hidden)]
87 fn flex_weights(&self) -> Option<&[f32]> {
88 None
89 }
90
91 #[doc(hidden)]
92 fn grid_placement(&self) -> Option<GridPlacement> {
93 None
94 }
95
96 #[doc(hidden)]
97 fn grid_placements(&self) -> Option<Vec<GridPlacement>> {
98 None
99 }
100
101 #[doc(hidden)]
102 fn intrinsic_size(&self, _ctx: &mut LayoutCtx) -> Option<Size> {
103 None
104 }
105
106 #[doc(hidden)]
107 fn contain_size(&self) -> bool {
108 false
109 }
110
111 fn arrange_override(
112 &self,
113 _rt: &Runtime,
114 _el: ElementId,
115 _rect: Rect,
116 _ctx: &mut LayoutCtx,
117 ) -> bool {
118 false
119 }
120
121 fn clip_self(&self) -> bool {
122 false
123 }
124
125 fn clip_radius(&self) -> f32 {
126 0.0
127 }
128
129 fn paint_opacity(&self) -> Option<f32> {
130 None
131 }
132
133 fn paint_opacity_ctx(&self, _rt: &Runtime, _el: ElementId) -> Option<f32> {
134 self.paint_opacity()
135 }
136
137 fn paint_offset(&self) -> Vec2 {
138 Vec2::ZERO
139 }
140
141 fn paint_offset_ctx(&self, _rt: &Runtime, _el: ElementId) -> Vec2 {
142 self.paint_offset()
143 }
144
145 fn paint_scale(&self) -> f32 {
146 1.0
147 }
148
149 fn paint_scale_ctx(&self, _rt: &Runtime, _el: ElementId) -> f32 {
150 self.paint_scale()
151 }
152
153 fn paint_rotation(&self) -> f32 {
154 0.0
155 }
156
157 fn paint_rotation_ctx(&self, _rt: &Runtime, _el: ElementId) -> f32 {
158 self.paint_rotation()
159 }
160
161 fn foreground_filter(&self) -> Option<sequoia_style::Filter> {
162 None
163 }
164
165 #[doc(hidden)]
166 fn surface_target(&self) -> Option<Rc<dyn Any>> {
167 None
168 }
169
170 #[doc(hidden)]
171 fn surface_input(&self) -> Option<SurfaceInputBinding> {
172 None
173 }
174
175 #[doc(hidden)]
176 fn on_event(&self, _event: &Event, _ctx: &mut EventCtx) {}
177
178 #[doc(hidden)]
179 fn captures_pointer(&self) -> bool {
180 false
181 }
182
183 fn captures_pointer_ctx(&self, _rt: &Runtime, _el: ElementId) -> bool {
184 self.captures_pointer()
185 }
186
187 fn reuse_state(&self, _old: &Self) {}
188
189 fn hit_testable(&self) -> bool {
190 true
191 }
192
193 fn test_id(&self) -> Option<String> {
194 None
195 }
196
197 #[doc(hidden)]
198 fn focusable(&self) -> bool {
199 false
200 }
201
202 fn focus_trap(&self) -> bool {
203 false
204 }
205
206 fn accepts_tab(&self) -> bool {
207 false
208 }
209
210 #[doc(hidden)]
211 fn cursor_icon(&self, _state: VisualState) -> CursorIcon {
212 CursorIcon::Default
213 }
214
215 #[doc(hidden)]
216 fn paint_self(&self, _ctx: &mut PaintCtx) {}
217
218 fn paint_foreground(&self, _ctx: &mut PaintCtx) {}
219
220 fn accepts_drop(&self, _payload: &DragPayload) -> bool {
221 false
222 }
223
224 fn on_drag_over(&self, _pos: DropPosition, _ctx: &mut EventCtx) {}
225
226 fn on_drop(&self, _pos: DropPosition, _ctx: &mut EventCtx) {}
227
228 fn on_drag_start(&self, _ctx: &mut EventCtx) {}
229
230 fn on_drag_end(&self, _end: DragEnd, _ctx: &mut EventCtx) {}
231
232 #[doc(hidden)]
233 fn on_mount(&self) {}
234
235 #[doc(hidden)]
236 fn on_unmount(&self) {}
237}
238
239pub struct EventCtx<'a> {
240 pub phase: EventPhase,
241 pub element: ElementId,
242 pub state: VisualState,
243 pub hover_self: bool,
244 pub pos: Point,
245 pub rect: Rect,
246 pub environment: &'a Environment,
247 pub window: WindowId,
248 pub(crate) stopped: &'a Cell<bool>,
249 pub(crate) handled: &'a Cell<bool>,
250 pub(crate) capture_req: &'a Cell<Option<ElementId>>,
251 pub(crate) release_req: &'a Cell<bool>,
252 pub(crate) focus_req: &'a Cell<bool>,
253 pub(crate) text: &'a mut TextSystem,
254 pub(crate) runtime: &'a Runtime,
255}
256
257impl EventCtx<'_> {
258 pub fn stop_propagation(&mut self) {
259 self.stopped.set(true);
260 }
261
262 pub fn handled(&mut self) {
263 self.handled.set(true);
264 self.stopped.set(true);
265 }
266
267 pub fn capture_pointer(&mut self) {
268 self.capture_req.set(Some(self.element));
269 self.release_req.set(false);
270 }
271
272 pub fn release_pointer(&mut self) {
273 self.release_req.set(true);
274 self.capture_req.set(None);
275 }
276
277 pub fn request_focus(&mut self) {
278 self.focus_req.set(true);
279 }
280
281 pub fn begin_drag<T: 'static>(&self, payload: T, options: DragOptions) {
282 self.runtime.begin_pending(
283 self.element,
284 DragPayload::new(payload),
285 self.pos,
286 self.rect,
287 options,
288 );
289 }
290
291 pub fn drag_session(&self) -> Option<DragSession> {
292 self.runtime.current_drag()
293 }
294
295 pub fn drop_position(&self, modifiers: Modifiers) -> DropPosition {
296 DropPosition::in_rect(self.pos, self.rect, modifiers)
297 }
298
299 pub fn cancel_drag(&mut self) {
300 self.runtime.cancel_drag(&mut *self.text);
301 }
302
303 pub fn pressure(&self) -> f32 {
304 self.runtime.pointer_pressure()
305 }
306
307 pub fn tool(&self) -> PenTool {
308 self.runtime.pointer_tool()
309 }
310
311 pub fn text_width(&mut self, text: &str, size: f32) -> f32 {
312 self.text.text_width(text, size)
313 }
314
315 pub fn text_width_weighted(&mut self, text: &str, size: f32, weight: FontWeight) -> f32 {
316 self.text.text_width_weighted(text, size, weight)
317 }
318
319 pub fn text_width_weighted_spaced(
320 &mut self,
321 text: &str,
322 size: f32,
323 weight: FontWeight,
324 spacing: LetterSpacing,
325 ) -> f32 {
326 self.text
327 .text_width_weighted_spaced(text, size, weight, spacing)
328 }
329
330 pub fn caret_positions(&mut self, text: &str, size: f32, weight: FontWeight) -> Vec<f32> {
331 self.text.caret_positions(text, size, weight)
332 }
333
334 pub fn caret_positions_spaced(
335 &mut self,
336 text: &str,
337 size: f32,
338 weight: FontWeight,
339 spacing: LetterSpacing,
340 ) -> Vec<f32> {
341 self.text
342 .caret_positions_spaced(text, size, weight, spacing)
343 }
344
345 pub fn char_index_from_x(
346 &mut self,
347 text: &str,
348 size: f32,
349 weight: FontWeight,
350 x: f32,
351 ) -> usize {
352 self.text.char_index_from_x(text, size, weight, x)
353 }
354
355 pub fn char_index_from_x_spaced(
356 &mut self,
357 text: &str,
358 size: f32,
359 weight: FontWeight,
360 x: f32,
361 spacing: LetterSpacing,
362 ) -> usize {
363 self.text
364 .char_index_from_x_spaced(text, size, weight, x, spacing)
365 }
366
367 pub fn shape_rich_full(
368 &mut self,
369 spans: &[TextSpan],
370 size: f32,
371 width: f32,
372 mode: WrapMode,
373 spacing: LetterSpacing,
374 align: RichAlign,
375 ) -> std::rc::Rc<RichParagraph> {
376 self.text
377 .shape_rich_full(spans, size, width, mode, spacing, align)
378 }
379
380 pub fn request_frame(&self) {
381 self.runtime.request_frame();
382 }
383
384 pub fn hide_overlay(&self, id: OverlayId) -> bool {
385 self.runtime.overlay_hide(id)
386 }
387
388 pub fn has_overlay(&self, id: OverlayId) -> bool {
389 self.runtime.overlay_exists(id)
390 }
391
392 pub fn overlay_rect(&self, id: OverlayId) -> Option<Rect> {
393 self.runtime.overlay_rect(id)
394 }
395
396 pub fn overlay_set_position(&self, id: OverlayId, x: f32, y: f32) -> bool {
397 self.runtime.overlay_set_position(id, x, y)
398 }
399
400 pub fn has_focus_within(&self) -> bool {
401 self.runtime.focus_in_subtree(self.element)
402 }
403
404 pub fn window_rect(&self) -> Rect {
405 self.runtime.window_rect()
406 }
407
408 pub fn window_size(&self) -> Size {
409 self.runtime.window_size_value()
410 }
411
412 pub fn scale(&self) -> f32 {
413 self.runtime.scale()
414 }
415
416 pub fn safe_insets(&self) -> sequoia_core::Edges {
417 self.runtime.safe_insets()
418 }
419
420 pub fn runtime(&self) -> &Runtime {
421 self.runtime
422 }
423
424 pub fn element(&self) -> ElementId {
425 self.element
426 }
427}
428
429pub struct PaintCtx<'a> {
430 pub(crate) list: &'a mut RenderList,
431 pub(crate) text: &'a mut TextSystem,
432
433 pub environment: &'a Environment,
434
435 pub rect: Rect,
436
437 pub element: ElementId,
438
439 pub state: VisualState,
440
441 pub window: WindowId,
442
443 pub hovered_subtree: bool,
444 pub(crate) runtime: &'a Runtime,
445
446 pub(crate) shapes: &'a ShapeCache,
447
448 pub(crate) draw_offset: Vec2,
449
450 pub(crate) offset_stack: Vec<Vec2>,
451}
452
453impl PaintCtx<'_> {
454 pub fn theme(&self) -> Theme {
455 self.environment.get::<Theme>().unwrap_or_else(Theme::dark)
456 }
457
458 pub fn runtime(&self) -> &Runtime {
459 self.runtime
460 }
461
462 pub fn window_rect(&self) -> Rect {
463 self.runtime.window_rect()
464 }
465
466 pub fn window_size(&self) -> Size {
467 self.runtime.window_size_value()
468 }
469
470 pub fn scale(&self) -> f32 {
471 self.runtime.scale()
472 }
473
474 pub fn safe_insets(&self) -> sequoia_core::Edges {
475 self.runtime.safe_insets()
476 }
477
478 pub fn element(&self) -> ElementId {
479 self.element
480 }
481
482 pub fn baseline_centered(rect_top: f32, rect_h: f32, size: f32) -> f32 {
483 rect_top + rect_h * 0.5 + size * 0.35
484 }
485
486 pub fn baseline_top(rect_top: f32, size: f32) -> f32 {
487 rect_top + size * 1.35 * 0.5 + size * 0.35
488 }
489
490 pub fn quad(&mut self, rect: Rect, color: Color, radius: f32) {
491 let rect = if rect.is_empty() {
492 Rect::from_xywh(rect.origin.x, rect.origin.y, 1.0, 1.0)
493 } else {
494 rect
495 };
496 self.list.push_quad(rect, color, radius);
497 }
498
499 pub fn bordered_quad(
500 &mut self,
501 rect: Rect,
502 background: Color,
503 radius: f32,
504 border_width: f32,
505 border_color: Color,
506 ) {
507 self.list
508 .push_bordered_quad(rect, background, radius, border_width, border_color);
509 }
510
511 pub fn paint_box(&mut self, style: BoxStyle) {
512 let rect = self.rect;
513 self.paint_box_in(style, rect);
514 }
515
516 pub fn paint_box_in(&mut self, style: BoxStyle, rect: Rect) {
517 style.paint(self.list, rect);
518 }
519
520 pub fn push_clip(&mut self, rect: Rect) {
521 self.list.push_clip(rect);
522 }
523 pub fn pop_clip(&mut self) {
524 self.list.pop_clip();
525 }
526 pub fn push_rounded_clip(&mut self, rect: Rect, radius: f32) {
527 self.list.push_rounded_clip(rect, radius);
528 }
529
530 pub fn list_clip_depth(&self) -> usize {
531 self.list.clip_depth()
532 }
533
534 pub fn push_backdrop(&mut self, rect: Rect, backdrop: sequoia_style::Backdrop) {
535 if backdrop.is_empty() {
536 return;
537 }
538 self.list.push_backdrop(
539 rect,
540 backdrop.radius,
541 backdrop.blur_px(),
542 backdrop.dim,
543 backdrop.noise,
544 backdrop.span_mask(),
545 );
546 }
547
548 pub fn draw_text(&mut self, text: &str, origin: (f32, f32), size: f32, color: Color) {
549 self.draw_text_weighted(text, origin, size, FontWeight::NORMAL, color);
550 }
551
552 pub fn draw_text_weighted(
553 &mut self,
554 text: &str,
555 origin: (f32, f32),
556 size: f32,
557 weight: FontWeight,
558 color: Color,
559 ) {
560 let shaped = self.text.shape_weighted(text, size, weight);
561 self.text.draw(&shaped, origin, color, self.list);
562 }
563
564 pub fn draw_text_wrapped(
565 &mut self,
566 text: &str,
567 origin: (f32, f32),
568 size: f32,
569 weight: FontWeight,
570 width: f32,
571 mode: WrapMode,
572 color: Color,
573 ) {
574 self.draw_text_wrapped_spaced(text, origin, size, weight, width, mode, 0.0, color);
575 }
576
577 pub fn draw_text_wrapped_spaced(
578 &mut self,
579 text: &str,
580 origin: (f32, f32),
581 size: f32,
582 weight: FontWeight,
583 width: f32,
584 mode: WrapMode,
585 spacing: LetterSpacing,
586 color: Color,
587 ) {
588 let shaped = self
589 .text
590 .shape_wrapped_spaced(text, size, weight, width, mode, spacing);
591 self.text.draw(&shaped, origin, color, self.list);
592 }
593
594 pub fn draw_text_spaced(
595 &mut self,
596 text: &str,
597 origin: (f32, f32),
598 size: f32,
599 spacing: LetterSpacing,
600 color: Color,
601 ) {
602 self.draw_text_weighted_spaced(text, origin, size, FontWeight::NORMAL, spacing, color);
603 }
604
605 pub fn draw_text_weighted_spaced(
606 &mut self,
607 text: &str,
608 origin: (f32, f32),
609 size: f32,
610 weight: FontWeight,
611 spacing: LetterSpacing,
612 color: Color,
613 ) {
614 let shaped = self.text.shape_spaced(text, size, weight, spacing);
615 self.text.draw(&shaped, origin, color, self.list);
616 }
617
618 pub fn draw_text_styled_spaced(
619 &mut self,
620 text: &str,
621 origin: (f32, f32),
622 size: f32,
623 weight: FontWeight,
624 spacing: LetterSpacing,
625 family: Option<&str>,
626 style: FontStyle,
627 color: Color,
628 ) {
629 let shaped = self
630 .text
631 .shape_spaced_styled(text, size, weight, spacing, family, style);
632 self.text.draw(&shaped, origin, color, self.list);
633 }
634
635 pub fn draw_text_wrapped_styled_spaced(
636 &mut self,
637 text: &str,
638 origin: (f32, f32),
639 size: f32,
640 weight: FontWeight,
641 width: f32,
642 mode: WrapMode,
643 spacing: LetterSpacing,
644 family: Option<&str>,
645 style: FontStyle,
646 color: Color,
647 ) {
648 let shaped = self
649 .text
650 .shape_wrapped_styled_spaced(text, size, weight, width, mode, spacing, family, style);
651 self.text.draw(&shaped, origin, color, self.list);
652 }
653
654 pub fn draw_rich(&mut self, rich: &RichParagraph, origin: (f32, f32), default_color: Color) {
655 self.text.draw_rich(rich, origin, default_color, self.list);
656 }
657
658 pub fn draw_rich_spans(
659 &mut self,
660 spans: &[TextSpan],
661 origin: (f32, f32),
662 size: f32,
663 width: f32,
664 mode: WrapMode,
665 spacing: LetterSpacing,
666 align: RichAlign,
667 default_color: Color,
668 ) {
669 let rich = self
670 .text
671 .shape_rich_full(spans, size, width, mode, spacing, align);
672 self.text.draw_rich(&rich, origin, default_color, self.list);
673 }
674
675 pub fn text_width(&mut self, text: &str, size: f32) -> f32 {
676 self.text.shape(text, size).width()
677 }
678
679 pub fn text_width_weighted(&mut self, text: &str, size: f32, weight: FontWeight) -> f32 {
680 self.text.shape_weighted(text, size, weight).width()
681 }
682
683 pub fn text_width_weighted_spaced(
684 &mut self,
685 text: &str,
686 size: f32,
687 weight: FontWeight,
688 spacing: LetterSpacing,
689 ) -> f32 {
690 self.text.shape_spaced(text, size, weight, spacing).width()
691 }
692
693 pub fn text_width_styled_spaced(
694 &mut self,
695 text: &str,
696 size: f32,
697 weight: FontWeight,
698 spacing: LetterSpacing,
699 family: Option<&str>,
700 style: FontStyle,
701 ) -> f32 {
702 self.text
703 .text_width_styled_spaced(text, size, weight, spacing, family, style)
704 }
705
706 pub fn elide_spaced(
707 &mut self,
708 text: &str,
709 size: f32,
710 weight: FontWeight,
711 max_width: f32,
712 mode: ElideMode,
713 spacing: LetterSpacing,
714 ) -> String {
715 self.text
716 .elide_spaced(text, size, weight, max_width, mode, spacing)
717 }
718
719 pub fn elide_styled_spaced(
720 &mut self,
721 text: &str,
722 size: f32,
723 weight: FontWeight,
724 max_width: f32,
725 mode: ElideMode,
726 spacing: LetterSpacing,
727 family: Option<&str>,
728 style: FontStyle,
729 ) -> String {
730 self.text
731 .elide_styled_spaced(text, size, weight, max_width, mode, spacing, family, style)
732 }
733
734 pub fn caret_positions(&mut self, text: &str, size: f32, weight: FontWeight) -> Vec<f32> {
735 self.text.caret_positions(text, size, weight)
736 }
737
738 pub fn caret_positions_spaced(
739 &mut self,
740 text: &str,
741 size: f32,
742 weight: FontWeight,
743 spacing: LetterSpacing,
744 ) -> Vec<f32> {
745 self.text
746 .caret_positions_spaced(text, size, weight, spacing)
747 }
748
749 pub fn shape_rich_full(
750 &mut self,
751 spans: &[TextSpan],
752 size: f32,
753 width: f32,
754 mode: WrapMode,
755 spacing: LetterSpacing,
756 align: RichAlign,
757 ) -> std::rc::Rc<RichParagraph> {
758 self.text
759 .shape_rich_full(spans, size, width, mode, spacing, align)
760 }
761
762 pub fn scale_factor(&self) -> f32 {
763 self.text.scale_factor()
764 }
765
766 pub fn request_frame(&self) {
767 self.runtime.request_frame();
768 }
769
770 pub fn live_color_exp(&self, prop: Prop, target: Color, rate: f32) -> Color {
771 self.runtime
772 .live_color_exp(self.element, prop, target, rate)
773 .0
774 }
775
776 pub fn draw_sprite(
777 &mut self,
778 key: u64,
779 width: u32,
780 height: u32,
781 alpha: &[u8],
782 rect: Rect,
783 color: Color,
784 ) {
785 self.text
786 .draw_sprite(key, width, height, alpha, rect, color, self.list);
787 }
788
789 pub fn image(&mut self, rect: Rect, uv: [f32; 4], source: ImageSource) {
790 self.list.push_image(rect, uv, source);
791 }
792
793 pub fn show_overlay(&self, entry: OverlayEntry) -> OverlayId {
794 self.runtime.overlay_show(entry)
795 }
796
797 pub fn hide_overlay(&self, id: OverlayId) -> bool {
798 self.runtime.overlay_hide(id)
799 }
800
801 pub fn has_overlay(&self, id: OverlayId) -> bool {
802 self.runtime.overlay_exists(id)
803 }
804
805 pub fn overlay_rect(&self, id: OverlayId) -> Option<Rect> {
806 self.runtime.overlay_rect(id)
807 }
808
809 pub fn drag_session(&self) -> Option<DragSession> {
810 self.runtime.current_drag()
811 }
812
813 pub fn is_drag_target(&self) -> bool {
814 self.runtime.drag_target_contains(self.element)
815 }
816
817 pub fn has_focus_within(&self) -> bool {
818 self.runtime.focus_in_subtree(self.element)
819 }
820}
821
822pub trait CustomLayout {
823 fn arrange(&self, rt: &Runtime, el: ElementId, rect: Rect, ctx: &mut LayoutCtx) -> bool;
824}