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