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, Dirty, 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, LineHeight, RichAlign, RichParagraph,
16    ShapedText, TextSpan, 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    /// Dirty flags applied when a signal read while building this view changes.
54    ///
55    /// The default rebuilds the body and relayouts. Views that paint their own
56    /// content from tracked signals — notably [`Canvas`](crate::Canvas) — can
57    /// narrow this to a paint-only flag.
58    #[doc(hidden)]
59    fn signal_dirty(&self) -> Dirty {
60        Dirty::LAYOUT | Dirty::CHILDREN
61    }
62
63    /// Drops any cached render output.
64    ///
65    /// The runtime calls this when a signal read while building the view
66    /// changes, so the next build re-evaluates the source closure. Views that
67    /// cache content between frames override this to clear that cache.
68    #[doc(hidden)]
69    fn invalidate_cache(&self) {}
70
71    #[doc(hidden)]
72    fn has_visual_state(&self) -> bool {
73        false
74    }
75
76    #[doc(hidden)]
77    fn wants_hover_updates(&self) -> bool {
78        false
79    }
80
81    fn accessibility(&self) -> Option<crate::a11y::Accessible> {
82        None
83    }
84
85    #[doc(hidden)]
86    fn container(&self) -> Option<ContainerStyle> {
87        None
88    }
89
90    #[doc(hidden)]
91    fn grid(&self) -> Option<GridStyle> {
92        None
93    }
94
95    #[doc(hidden)]
96    fn dock(&self) -> Option<DockStyle> {
97        None
98    }
99
100    #[doc(hidden)]
101    fn wrap(&self) -> Option<WrapStyle> {
102        None
103    }
104
105    #[doc(hidden)]
106    fn masonry(&self) -> Option<MasonryStyle> {
107        None
108    }
109
110    #[doc(hidden)]
111    fn flex_weights(&self) -> Option<&[f32]> {
112        None
113    }
114
115    #[doc(hidden)]
116    fn grid_placement(&self) -> Option<GridPlacement> {
117        None
118    }
119
120    #[doc(hidden)]
121    fn grid_placements(&self) -> Option<Vec<GridPlacement>> {
122        None
123    }
124
125    #[doc(hidden)]
126    fn intrinsic_size(&self, _ctx: &mut LayoutCtx) -> Option<Size> {
127        None
128    }
129
130    /// Returns the first text baseline relative to the view's top edge.
131    ///
132    /// Used by [`sequoia_layout::Align::Baseline`] in horizontal containers.
133    #[doc(hidden)]
134    fn baseline_offset(&self, _ctx: &mut LayoutCtx, _width: f32) -> Option<f32> {
135        None
136    }
137
138    #[doc(hidden)]
139    fn contain_size(&self) -> bool {
140        false
141    }
142
143    fn arrange_override(
144        &self,
145        _rt: &Runtime,
146        _el: ElementId,
147        _rect: Rect,
148        _ctx: &mut LayoutCtx,
149    ) -> bool {
150        false
151    }
152
153    fn clip_self(&self) -> bool {
154        false
155    }
156
157    fn clip_radius(&self) -> f32 {
158        0.0
159    }
160
161    fn paint_opacity(&self) -> Option<f32> {
162        None
163    }
164
165    /// Allows the runtime to replay this element's painted subtree from cache
166    /// while its element revision and absolute paint context stay unchanged.
167    ///
168    /// Cache hits skip `paint_self` and `paint_foreground`, so their side
169    /// effects must not be required for later frames.
170    fn cache_layer(&self) -> bool {
171        false
172    }
173
174    fn paint_opacity_ctx(&self, _rt: &Runtime, _el: ElementId) -> Option<f32> {
175        self.paint_opacity()
176    }
177
178    fn paint_offset(&self) -> Vec2 {
179        Vec2::ZERO
180    }
181
182    fn paint_offset_ctx(&self, _rt: &Runtime, _el: ElementId) -> Vec2 {
183        self.paint_offset()
184    }
185
186    fn paint_scale(&self) -> f32 {
187        1.0
188    }
189
190    fn paint_scale_ctx(&self, _rt: &Runtime, _el: ElementId) -> f32 {
191        self.paint_scale()
192    }
193
194    fn paint_rotation(&self) -> f32 {
195        0.0
196    }
197
198    fn paint_rotation_ctx(&self, _rt: &Runtime, _el: ElementId) -> f32 {
199        self.paint_rotation()
200    }
201
202    fn foreground_filter(&self) -> Option<sequoia_style::Filter> {
203        None
204    }
205
206    #[doc(hidden)]
207    fn surface_target(&self) -> Option<Rc<dyn Any>> {
208        None
209    }
210
211    #[doc(hidden)]
212    fn surface_input(&self) -> Option<SurfaceInputBinding> {
213        None
214    }
215
216    #[doc(hidden)]
217    fn on_event(&self, _event: &Event, _ctx: &mut EventCtx) {}
218
219    #[doc(hidden)]
220    fn captures_pointer(&self) -> bool {
221        false
222    }
223
224    fn captures_pointer_ctx(&self, _rt: &Runtime, _el: ElementId) -> bool {
225        self.captures_pointer()
226    }
227
228    fn reuse_state(&self, _old: &Self) {}
229
230    fn hit_testable(&self) -> bool {
231        true
232    }
233
234    fn test_id(&self) -> Option<String> {
235        None
236    }
237
238    #[doc(hidden)]
239    fn focusable(&self) -> bool {
240        false
241    }
242
243    fn focus_trap(&self) -> bool {
244        false
245    }
246
247    fn accepts_tab(&self) -> bool {
248        false
249    }
250
251    /// Whether this view edits text when focused.
252    ///
253    /// Hosts use this to keep single-key accelerators (Delete, Backspace,
254    /// letter keys) from stealing input while a text field or text area has
255    /// focus. Command shortcuts such as Ctrl+S still fire.
256    fn accepts_text_input(&self) -> bool {
257        false
258    }
259
260    /// Caret rectangle in window coordinates, used to place the platform IME
261    /// candidate window. Widgets with a text caret return the box the caret
262    /// occupies; hosts fall back to the focused element's bounds when this is
263    /// `None`.
264    fn ime_caret_rect(&self) -> Option<Rect> {
265        None
266    }
267
268    #[doc(hidden)]
269    fn cursor_icon(&self, _state: VisualState) -> CursorIcon {
270        CursorIcon::Default
271    }
272
273    #[doc(hidden)]
274    fn paint_self(&self, _ctx: &mut PaintCtx) {}
275
276    fn paint_foreground(&self, _ctx: &mut PaintCtx) {}
277
278    fn accepts_drop(&self, _payload: &DragPayload) -> bool {
279        false
280    }
281
282    fn on_drag_over(&self, _pos: DropPosition, _ctx: &mut EventCtx) {}
283
284    fn on_drop(&self, _pos: DropPosition, _ctx: &mut EventCtx) {}
285
286    fn on_drag_start(&self, _ctx: &mut EventCtx) {}
287
288    fn on_drag_end(&self, _end: DragEnd, _ctx: &mut EventCtx) {}
289
290    #[doc(hidden)]
291    fn on_mount(&self) {}
292
293    #[doc(hidden)]
294    fn on_unmount(&self) {}
295}
296
297pub struct EventCtx<'a> {
298    pub phase: EventPhase,
299    pub element: ElementId,
300    pub state: VisualState,
301    pub hover_self: bool,
302    pub pos: Point,
303    pub rect: Rect,
304    pub environment: &'a Environment,
305    pub window: WindowId,
306    pub(crate) stopped: &'a Cell<bool>,
307    pub(crate) handled: &'a Cell<bool>,
308    pub(crate) capture_req: &'a Cell<Option<ElementId>>,
309    pub(crate) release_req: &'a Cell<bool>,
310    pub(crate) focus_req: &'a Cell<bool>,
311    pub(crate) text: &'a mut TextSystem,
312    pub(crate) runtime: &'a Runtime,
313}
314
315impl EventCtx<'_> {
316    pub fn local_pos(&self) -> Point {
317        Point::new(self.pos.x - self.rect.left(), self.pos.y - self.rect.top())
318    }
319
320    pub fn stop_propagation(&mut self) {
321        self.stopped.set(true);
322    }
323
324    pub fn handled(&mut self) {
325        self.handled.set(true);
326        self.stopped.set(true);
327    }
328
329    pub fn capture_pointer(&mut self) {
330        self.capture_req.set(Some(self.element));
331        self.release_req.set(false);
332    }
333
334    pub fn release_pointer(&mut self) {
335        self.release_req.set(true);
336        self.capture_req.set(None);
337    }
338
339    pub fn request_focus(&mut self) {
340        self.focus_req.set(true);
341    }
342
343    pub fn begin_drag<T: 'static>(&self, payload: T, options: DragOptions) {
344        self.runtime.begin_pending(
345            self.element,
346            DragPayload::new(payload),
347            self.pos,
348            self.rect,
349            options,
350        );
351    }
352
353    pub fn drag_session(&self) -> Option<DragSession> {
354        self.runtime.current_drag()
355    }
356
357    pub fn drop_position(&self, modifiers: Modifiers) -> DropPosition {
358        DropPosition::in_rect(self.pos, self.rect, modifiers)
359    }
360
361    pub fn cancel_drag(&mut self) {
362        self.runtime.cancel_drag(&mut *self.text);
363    }
364
365    pub fn pressure(&self) -> f32 {
366        self.runtime.pointer_pressure()
367    }
368
369    pub fn tool(&self) -> PenTool {
370        self.runtime.pointer_tool()
371    }
372
373    pub fn text_width(&mut self, text: &str, size: f32) -> f32 {
374        self.text.text_width(text, size)
375    }
376
377    pub fn text_width_weighted(&mut self, text: &str, size: f32, weight: FontWeight) -> f32 {
378        self.text.text_width_weighted(text, size, weight)
379    }
380
381    pub fn text_width_weighted_spaced(
382        &mut self,
383        text: &str,
384        size: f32,
385        weight: FontWeight,
386        spacing: LetterSpacing,
387    ) -> f32 {
388        self.text
389            .text_width_weighted_spaced(text, size, weight, spacing)
390    }
391
392    pub fn caret_positions(&mut self, text: &str, size: f32, weight: FontWeight) -> Vec<f32> {
393        self.text.caret_positions(text, size, weight)
394    }
395
396    pub fn caret_positions_spaced(
397        &mut self,
398        text: &str,
399        size: f32,
400        weight: FontWeight,
401        spacing: LetterSpacing,
402    ) -> Vec<f32> {
403        self.text
404            .caret_positions_spaced(text, size, weight, spacing)
405    }
406
407    pub fn char_index_from_x(
408        &mut self,
409        text: &str,
410        size: f32,
411        weight: FontWeight,
412        x: f32,
413    ) -> usize {
414        self.text.char_index_from_x(text, size, weight, x)
415    }
416
417    pub fn char_index_from_x_spaced(
418        &mut self,
419        text: &str,
420        size: f32,
421        weight: FontWeight,
422        x: f32,
423        spacing: LetterSpacing,
424    ) -> usize {
425        self.text
426            .char_index_from_x_spaced(text, size, weight, x, spacing)
427    }
428
429    pub fn shape_rich_full(
430        &mut self,
431        spans: &[TextSpan],
432        size: f32,
433        width: f32,
434        mode: WrapMode,
435        spacing: LetterSpacing,
436        align: RichAlign,
437    ) -> std::rc::Rc<RichParagraph> {
438        self.text
439            .shape_rich_full(spans, size, width, mode, spacing, align)
440    }
441
442    pub fn request_frame(&self) {
443        self.runtime.request_frame();
444    }
445
446    pub fn hide_overlay(&self, id: OverlayId) -> bool {
447        self.runtime.overlay_hide(id)
448    }
449
450    pub fn has_overlay(&self, id: OverlayId) -> bool {
451        self.runtime.overlay_exists(id)
452    }
453
454    pub fn overlay_rect(&self, id: OverlayId) -> Option<Rect> {
455        self.runtime.overlay_rect(id)
456    }
457
458    pub fn overlay_set_position(&self, id: OverlayId, x: f32, y: f32) -> bool {
459        self.runtime.overlay_set_position(id, x, y)
460    }
461
462    pub fn has_focus_within(&self) -> bool {
463        self.runtime.focus_in_subtree(self.element)
464    }
465
466    pub fn window_rect(&self) -> Rect {
467        self.runtime.window_rect()
468    }
469
470    pub fn window_size(&self) -> Size {
471        self.runtime.window_size_value()
472    }
473
474    pub fn scale(&self) -> f32 {
475        self.runtime.scale()
476    }
477
478    pub fn safe_insets(&self) -> sequoia_core::Edges {
479        self.runtime.safe_insets()
480    }
481
482    pub fn runtime(&self) -> &Runtime {
483        self.runtime
484    }
485
486    pub fn element(&self) -> ElementId {
487        self.element
488    }
489}
490
491#[derive(Clone, Copy, Debug, Default, PartialEq)]
492pub struct TextMetrics {
493    /// Width of the measured text. Zero for [`PaintCtx::text_metrics`], which
494    /// measures the font rather than a specific string.
495    pub width: f32,
496
497    /// Height of the measured text block, or the line-box height for
498    /// [`PaintCtx::text_metrics`].
499    pub height: f32,
500
501    /// Distance from the top of the line box to the baseline.
502    pub ascent: f32,
503
504    /// Distance from the baseline to the bottom of the line box.
505    pub descent: f32,
506
507    /// Extra leading outside ascent and descent. Sequoia line boxes are tight,
508    /// so this is currently always `0.0`.
509    pub line_gap: f32,
510}
511
512fn metrics_from_shaped(shaped: &ShapedText) -> TextMetrics {
513    let line_height = shaped.line_height();
514    let ascent = shaped.first_baseline();
515    TextMetrics {
516        width: shaped.width(),
517        height: shaped.height(),
518        ascent,
519        descent: (line_height - ascent).max(0.0),
520        line_gap: 0.0,
521    }
522}
523
524pub struct PaintCtx<'a> {
525    pub(crate) list: &'a mut RenderList,
526    pub(crate) text: &'a mut TextSystem,
527
528    pub environment: &'a Environment,
529
530    pub rect: Rect,
531
532    pub element: ElementId,
533
534    pub state: VisualState,
535
536    pub window: WindowId,
537
538    pub hovered_subtree: bool,
539    pub(crate) runtime: &'a Runtime,
540
541    pub(crate) shapes: &'a ShapeCache,
542
543    pub(crate) draw_offset: Vec2,
544
545    pub(crate) offset_stack: Vec<Vec2>,
546}
547
548impl PaintCtx<'_> {
549    pub fn theme(&self) -> Theme {
550        self.environment.get::<Theme>().unwrap_or_else(Theme::dark)
551    }
552
553    pub fn runtime(&self) -> &Runtime {
554        self.runtime
555    }
556
557    pub fn window_rect(&self) -> Rect {
558        self.runtime.window_rect()
559    }
560
561    pub fn window_size(&self) -> Size {
562        self.runtime.window_size_value()
563    }
564
565    pub fn scale(&self) -> f32 {
566        self.runtime.scale()
567    }
568
569    pub fn safe_insets(&self) -> sequoia_core::Edges {
570        self.runtime.safe_insets()
571    }
572
573    pub fn element(&self) -> ElementId {
574        self.element
575    }
576
577    /// Measures a specific string.
578    pub fn measure_text(&mut self, text: &str, size: f32) -> TextMetrics {
579        if text.is_empty() || !(size > 0.0) {
580            return TextMetrics::default();
581        }
582        let shaped = self.text.shape(text, size);
583        metrics_from_shaped(&shaped)
584    }
585
586    /// Returns vertical metrics for the default font at `size`.
587    ///
588    /// Use this instead of guessing a baseline offset: center a line box with
589    /// `rect.top() + (rect.height() - metrics.height) * 0.5`, then draw it at
590    /// that top edge with [`Self::draw_text_tl`].
591    pub fn text_metrics(&mut self, size: f32) -> TextMetrics {
592        if !(size > 0.0) {
593            return TextMetrics::default();
594        }
595        let shaped = self.text.shape("Mg", size);
596        TextMetrics {
597            width: 0.0,
598            ..metrics_from_shaped(&shaped)
599        }
600    }
601
602    /// Baseline placed in the vertical center of a line box whose top is
603    /// `rect_top` and whose height is `rect_h`.
604    ///
605    /// Prefer [`Self::text_metrics`] when the real font ascent is needed.
606    pub fn baseline_centered(rect_top: f32, rect_h: f32, size: f32) -> f32 {
607        rect_top + rect_h * 0.5 + size * 0.35
608    }
609
610    /// Approximate baseline for a line box whose top is `rect_top`.
611    ///
612    /// Prefer [`Self::text_metrics`] when the real font ascent is needed.
613    pub fn baseline_top(rect_top: f32, size: f32) -> f32 {
614        rect_top + size * 1.35 * 0.5 + size * 0.35
615    }
616
617    pub fn quad(&mut self, rect: Rect, color: Color, radius: f32) {
618        let rect = if rect.is_empty() {
619            Rect::from_xywh(rect.origin.x, rect.origin.y, 1.0, 1.0)
620        } else {
621            rect
622        };
623        self.list.push_quad(rect, color, radius);
624    }
625
626    pub fn bordered_quad(
627        &mut self,
628        rect: Rect,
629        background: Color,
630        radius: f32,
631        border_width: f32,
632        border_color: Color,
633    ) {
634        self.list
635            .push_bordered_quad(rect, background, radius, border_width, border_color);
636    }
637
638    pub fn paint_box(&mut self, style: BoxStyle) {
639        let rect = self.rect;
640        self.paint_box_in(style, rect);
641    }
642
643    pub fn paint_box_in(&mut self, style: BoxStyle, rect: Rect) {
644        style.paint(self.list, rect);
645    }
646
647    pub fn push_clip(&mut self, rect: Rect) {
648        self.list.push_clip(rect);
649    }
650    pub fn pop_clip(&mut self) {
651        self.list.pop_clip();
652    }
653    pub fn push_rounded_clip(&mut self, rect: Rect, radius: f32) {
654        self.list.push_rounded_clip(rect, radius);
655    }
656
657    pub fn list_clip_depth(&self) -> usize {
658        self.list.clip_depth()
659    }
660
661    pub fn push_backdrop(&mut self, rect: Rect, backdrop: sequoia_style::Backdrop) {
662        if backdrop.is_empty() {
663            return;
664        }
665        self.list.push_backdrop(
666            rect,
667            backdrop.radius,
668            backdrop.blur_px(),
669            backdrop.dim,
670            backdrop.noise,
671            backdrop.span_mask(),
672        );
673    }
674
675    /// Draws a single line with `origin` at the **text baseline**.
676    ///
677    /// The y coordinate is not a top edge: glyphs extend above it by the font
678    /// ascent. Use [`Self::draw_text_tl`] when positioning from the top-left of
679    /// a layout rectangle, or [`Self::text_metrics`] to center a line box.
680    pub fn draw_text(&mut self, text: &str, origin: (f32, f32), size: f32, color: Color) {
681        self.draw_text_weighted(text, origin, size, FontWeight::NORMAL, color);
682    }
683
684    /// Draws a single line with `top_left` at the top-left of its line box.
685    ///
686    /// This is the counterpart of [`Self::draw_text`], whose origin is the
687    /// baseline, and matches [`crate::Painter::text_tl`].
688    pub fn draw_text_tl(&mut self, text: &str, top_left: (f32, f32), size: f32, color: Color) {
689        if text.is_empty() || !(size > 0.0) {
690            return;
691        }
692        let ascent = self.text_metrics(size).ascent;
693        self.draw_text(text, (top_left.0, top_left.1 + ascent), size, color);
694    }
695
696    pub fn draw_text_weighted(
697        &mut self,
698        text: &str,
699        origin: (f32, f32),
700        size: f32,
701        weight: FontWeight,
702        color: Color,
703    ) {
704        let shaped = self.text.shape_weighted(text, size, weight);
705        self.text.draw(&shaped, origin, color, self.list);
706    }
707
708    pub fn draw_text_wrapped(
709        &mut self,
710        text: &str,
711        origin: (f32, f32),
712        size: f32,
713        weight: FontWeight,
714        width: f32,
715        mode: WrapMode,
716        color: Color,
717    ) {
718        self.draw_text_wrapped_spaced(text, origin, size, weight, width, mode, 0.0, color);
719    }
720
721    pub fn draw_text_wrapped_spaced(
722        &mut self,
723        text: &str,
724        origin: (f32, f32),
725        size: f32,
726        weight: FontWeight,
727        width: f32,
728        mode: WrapMode,
729        spacing: LetterSpacing,
730        color: Color,
731    ) {
732        let shaped = self
733            .text
734            .shape_wrapped_spaced(text, size, weight, width, mode, spacing);
735        self.text.draw(&shaped, origin, color, self.list);
736    }
737
738    pub fn draw_text_spaced(
739        &mut self,
740        text: &str,
741        origin: (f32, f32),
742        size: f32,
743        spacing: LetterSpacing,
744        color: Color,
745    ) {
746        self.draw_text_weighted_spaced(text, origin, size, FontWeight::NORMAL, spacing, color);
747    }
748
749    pub fn draw_text_weighted_spaced(
750        &mut self,
751        text: &str,
752        origin: (f32, f32),
753        size: f32,
754        weight: FontWeight,
755        spacing: LetterSpacing,
756        color: Color,
757    ) {
758        let shaped = self.text.shape_spaced(text, size, weight, spacing);
759        self.text.draw(&shaped, origin, color, self.list);
760    }
761
762    pub fn draw_text_styled_spaced(
763        &mut self,
764        text: &str,
765        origin: (f32, f32),
766        size: f32,
767        weight: FontWeight,
768        spacing: LetterSpacing,
769        family: Option<&str>,
770        style: FontStyle,
771        color: Color,
772    ) {
773        let shaped = self
774            .text
775            .shape_spaced_styled(text, size, weight, spacing, family, style);
776        self.text.draw(&shaped, origin, color, self.list);
777    }
778
779    pub fn draw_text_wrapped_styled_spaced(
780        &mut self,
781        text: &str,
782        origin: (f32, f32),
783        size: f32,
784        weight: FontWeight,
785        width: f32,
786        mode: WrapMode,
787        spacing: LetterSpacing,
788        family: Option<&str>,
789        style: FontStyle,
790        color: Color,
791    ) {
792        let shaped = self
793            .text
794            .shape_wrapped_styled_spaced(text, size, weight, width, mode, spacing, family, style);
795        self.text.draw(&shaped, origin, color, self.list);
796    }
797
798    #[allow(clippy::too_many_arguments)]
799    pub fn shape_wrapped_styled_spaced_lh(
800        &mut self,
801        text: &str,
802        size: f32,
803        weight: FontWeight,
804        width: f32,
805        mode: WrapMode,
806        spacing: LetterSpacing,
807        family: Option<&str>,
808        style: FontStyle,
809        line_height: LineHeight,
810    ) -> Rc<ShapedText> {
811        self.text.shape_wrapped_styled_spaced_lh(
812            text,
813            size,
814            weight,
815            width,
816            mode,
817            spacing,
818            family,
819            style,
820            line_height,
821        )
822    }
823
824    pub fn draw_shaped(&mut self, shaped: &ShapedText, origin: (f32, f32), color: Color) {
825        self.text.draw(shaped, origin, color, self.list);
826    }
827
828    #[allow(clippy::too_many_arguments)]
829    pub fn shape_rich_full_lh(
830        &mut self,
831        spans: &[TextSpan],
832        size: f32,
833        width: f32,
834        mode: WrapMode,
835        spacing: LetterSpacing,
836        align: RichAlign,
837        line_height: LineHeight,
838    ) -> Rc<RichParagraph> {
839        self.text
840            .shape_rich_full_lh(spans, size, width, mode, spacing, align, line_height)
841    }
842
843    #[allow(clippy::too_many_arguments)]
844    pub fn elide_lines_styled_spaced(
845        &mut self,
846        text: &str,
847        size: f32,
848        weight: FontWeight,
849        width: f32,
850        mode: WrapMode,
851        spacing: LetterSpacing,
852        family: Option<&str>,
853        style: FontStyle,
854        line_height: LineHeight,
855        max_lines: usize,
856    ) -> String {
857        self.text.elide_lines_styled_spaced(
858            text,
859            size,
860            weight,
861            width,
862            mode,
863            spacing,
864            family,
865            style,
866            line_height,
867            max_lines,
868        )
869    }
870
871    #[allow(clippy::too_many_arguments)]
872    pub fn elide_rich_lines(
873        &mut self,
874        spans: &[TextSpan],
875        size: f32,
876        width: f32,
877        mode: WrapMode,
878        spacing: LetterSpacing,
879        align: RichAlign,
880        line_height: LineHeight,
881        max_lines: usize,
882    ) -> Vec<TextSpan> {
883        self.text.elide_rich_lines(
884            spans,
885            size,
886            width,
887            mode,
888            spacing,
889            align,
890            line_height,
891            max_lines,
892        )
893    }
894
895    pub fn draw_rich(&mut self, rich: &RichParagraph, origin: (f32, f32), default_color: Color) {
896        self.text.draw_rich(rich, origin, default_color, self.list);
897    }
898
899    pub fn draw_rich_spans(
900        &mut self,
901        spans: &[TextSpan],
902        origin: (f32, f32),
903        size: f32,
904        width: f32,
905        mode: WrapMode,
906        spacing: LetterSpacing,
907        align: RichAlign,
908        default_color: Color,
909    ) {
910        let rich = self
911            .text
912            .shape_rich_full(spans, size, width, mode, spacing, align);
913        self.text.draw_rich(&rich, origin, default_color, self.list);
914    }
915
916    pub fn text_width(&mut self, text: &str, size: f32) -> f32 {
917        self.text.shape(text, size).width()
918    }
919
920    pub fn text_width_weighted(&mut self, text: &str, size: f32, weight: FontWeight) -> f32 {
921        self.text.shape_weighted(text, size, weight).width()
922    }
923
924    pub fn text_width_weighted_spaced(
925        &mut self,
926        text: &str,
927        size: f32,
928        weight: FontWeight,
929        spacing: LetterSpacing,
930    ) -> f32 {
931        self.text.shape_spaced(text, size, weight, spacing).width()
932    }
933
934    pub fn text_width_styled_spaced(
935        &mut self,
936        text: &str,
937        size: f32,
938        weight: FontWeight,
939        spacing: LetterSpacing,
940        family: Option<&str>,
941        style: FontStyle,
942    ) -> f32 {
943        self.text
944            .text_width_styled_spaced(text, size, weight, spacing, family, style)
945    }
946
947    pub fn elide_spaced(
948        &mut self,
949        text: &str,
950        size: f32,
951        weight: FontWeight,
952        max_width: f32,
953        mode: ElideMode,
954        spacing: LetterSpacing,
955    ) -> String {
956        self.text
957            .elide_spaced(text, size, weight, max_width, mode, spacing)
958    }
959
960    pub fn elide_styled_spaced(
961        &mut self,
962        text: &str,
963        size: f32,
964        weight: FontWeight,
965        max_width: f32,
966        mode: ElideMode,
967        spacing: LetterSpacing,
968        family: Option<&str>,
969        style: FontStyle,
970    ) -> String {
971        self.text
972            .elide_styled_spaced(text, size, weight, max_width, mode, spacing, family, style)
973    }
974
975    pub fn caret_positions(&mut self, text: &str, size: f32, weight: FontWeight) -> Vec<f32> {
976        self.text.caret_positions(text, size, weight)
977    }
978
979    pub fn caret_positions_spaced(
980        &mut self,
981        text: &str,
982        size: f32,
983        weight: FontWeight,
984        spacing: LetterSpacing,
985    ) -> Vec<f32> {
986        self.text
987            .caret_positions_spaced(text, size, weight, spacing)
988    }
989
990    pub fn shape_rich_full(
991        &mut self,
992        spans: &[TextSpan],
993        size: f32,
994        width: f32,
995        mode: WrapMode,
996        spacing: LetterSpacing,
997        align: RichAlign,
998    ) -> std::rc::Rc<RichParagraph> {
999        self.text
1000            .shape_rich_full(spans, size, width, mode, spacing, align)
1001    }
1002
1003    pub fn scale_factor(&self) -> f32 {
1004        self.text.scale_factor()
1005    }
1006
1007    pub fn request_frame(&self) {
1008        self.runtime.request_frame();
1009    }
1010
1011    pub fn live_color_exp(&self, prop: Prop, target: Color, rate: f32) -> Color {
1012        self.runtime
1013            .live_color_exp(self.element, prop, target, rate)
1014            .0
1015    }
1016
1017    pub fn draw_sprite(
1018        &mut self,
1019        key: u64,
1020        width: u32,
1021        height: u32,
1022        alpha: &[u8],
1023        rect: Rect,
1024        color: Color,
1025    ) {
1026        self.text
1027            .draw_sprite(key, width, height, alpha, rect, color, self.list);
1028    }
1029
1030    pub fn image(&mut self, rect: Rect, uv: [f32; 4], source: ImageSource) {
1031        self.list.push_image(rect, uv, source);
1032    }
1033
1034    pub fn show_overlay(&self, entry: OverlayEntry) -> OverlayId {
1035        self.runtime.overlay_show(entry.owned_by(self.element))
1036    }
1037
1038    pub fn hide_overlay(&self, id: OverlayId) -> bool {
1039        self.runtime.overlay_hide(id)
1040    }
1041
1042    pub fn has_overlay(&self, id: OverlayId) -> bool {
1043        self.runtime.overlay_exists(id)
1044    }
1045
1046    pub fn overlay_rect(&self, id: OverlayId) -> Option<Rect> {
1047        self.runtime.overlay_rect(id)
1048    }
1049
1050    pub fn drag_session(&self) -> Option<DragSession> {
1051        self.runtime.current_drag()
1052    }
1053
1054    pub fn is_drag_target(&self) -> bool {
1055        self.runtime.drag_target_contains(self.element)
1056    }
1057
1058    pub fn has_focus_within(&self) -> bool {
1059        self.runtime.focus_in_subtree(self.element)
1060    }
1061}
1062
1063pub trait CustomLayout {
1064    fn arrange(&self, rt: &Runtime, el: ElementId, rect: Rect, ctx: &mut LayoutCtx) -> bool;
1065}