Skip to main content

sequoia_view/
view.rs

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}