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