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, 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}