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