iced_renderer/
fallback.rs

1//! Compose existing renderers and create type-safe fallback strategies.
2use crate::core::image;
3use crate::core::renderer;
4use crate::core::svg;
5use crate::core::{
6    self, Background, Color, Font, Image, Pixels, Point, Rectangle, Size, Svg,
7    Transformation,
8};
9use crate::graphics::compositor;
10use crate::graphics::mesh;
11use crate::graphics::{self, Shell};
12
13use std::borrow::Cow;
14
15/// A renderer `A` with a fallback strategy `B`.
16///
17/// This type can be used to easily compose existing renderers and
18/// create custom, type-safe fallback strategies.
19#[derive(Debug)]
20pub enum Renderer<A, B> {
21    /// The primary rendering option.
22    Primary(A),
23    /// The secondary (or fallback) rendering option.
24    Secondary(B),
25}
26
27macro_rules! delegate {
28    ($renderer:expr, $name:ident, $body:expr) => {
29        match $renderer {
30            Self::Primary($name) => $body,
31            Self::Secondary($name) => $body,
32        }
33    };
34}
35
36impl<A, B> core::Renderer for Renderer<A, B>
37where
38    A: core::Renderer,
39    B: core::Renderer,
40{
41    fn fill_quad(
42        &mut self,
43        quad: renderer::Quad,
44        background: impl Into<Background>,
45    ) {
46        delegate!(self, renderer, renderer.fill_quad(quad, background.into()));
47    }
48
49    fn reset(&mut self, new_bounds: Rectangle) {
50        delegate!(self, renderer, renderer.reset(new_bounds));
51    }
52
53    fn start_layer(&mut self, bounds: Rectangle) {
54        delegate!(self, renderer, renderer.start_layer(bounds));
55    }
56
57    fn end_layer(&mut self) {
58        delegate!(self, renderer, renderer.end_layer());
59    }
60
61    fn start_transformation(&mut self, transformation: Transformation) {
62        delegate!(
63            self,
64            renderer,
65            renderer.start_transformation(transformation)
66        );
67    }
68
69    fn end_transformation(&mut self) {
70        delegate!(self, renderer, renderer.end_transformation());
71    }
72
73    fn allocate_image(
74        &mut self,
75        handle: &image::Handle,
76        callback: impl FnOnce(Result<image::Allocation, image::Error>)
77        + Send
78        + 'static,
79    ) {
80        delegate!(self, renderer, renderer.allocate_image(handle, callback));
81    }
82}
83
84impl<A, B> core::text::Renderer for Renderer<A, B>
85where
86    A: core::text::Renderer,
87    B: core::text::Renderer<
88            Font = A::Font,
89            Paragraph = A::Paragraph,
90            Editor = A::Editor,
91        >,
92{
93    type Font = A::Font;
94    type Paragraph = A::Paragraph;
95    type Editor = A::Editor;
96
97    const ICON_FONT: Self::Font = A::ICON_FONT;
98    const CHECKMARK_ICON: char = A::CHECKMARK_ICON;
99    const ARROW_DOWN_ICON: char = A::ARROW_DOWN_ICON;
100
101    fn default_font(&self) -> Self::Font {
102        delegate!(self, renderer, renderer.default_font())
103    }
104
105    fn default_size(&self) -> core::Pixels {
106        delegate!(self, renderer, renderer.default_size())
107    }
108
109    fn fill_paragraph(
110        &mut self,
111        text: &Self::Paragraph,
112        position: Point,
113        color: Color,
114        clip_bounds: Rectangle,
115    ) {
116        delegate!(
117            self,
118            renderer,
119            renderer.fill_paragraph(text, position, color, clip_bounds)
120        );
121    }
122
123    fn fill_editor(
124        &mut self,
125        editor: &Self::Editor,
126        position: Point,
127        color: Color,
128        clip_bounds: Rectangle,
129    ) {
130        delegate!(
131            self,
132            renderer,
133            renderer.fill_editor(editor, position, color, clip_bounds)
134        );
135    }
136
137    fn fill_text(
138        &mut self,
139        text: core::Text<String, Self::Font>,
140        position: Point,
141        color: Color,
142        clip_bounds: Rectangle,
143    ) {
144        delegate!(
145            self,
146            renderer,
147            renderer.fill_text(text, position, color, clip_bounds)
148        );
149    }
150}
151
152impl<A, B> image::Renderer for Renderer<A, B>
153where
154    A: image::Renderer,
155    B: image::Renderer<Handle = A::Handle>,
156{
157    type Handle = A::Handle;
158
159    fn load_image(
160        &self,
161        handle: &Self::Handle,
162    ) -> Result<image::Allocation, image::Error> {
163        delegate!(self, renderer, renderer.load_image(handle))
164    }
165
166    fn measure_image(&self, handle: &Self::Handle) -> Option<Size<u32>> {
167        delegate!(self, renderer, renderer.measure_image(handle))
168    }
169
170    fn draw_image(
171        &mut self,
172        image: Image<A::Handle>,
173        bounds: Rectangle,
174        clip_bounds: Rectangle,
175    ) {
176        delegate!(
177            self,
178            renderer,
179            renderer.draw_image(image, bounds, clip_bounds)
180        );
181    }
182}
183
184impl<A, B> svg::Renderer for Renderer<A, B>
185where
186    A: svg::Renderer,
187    B: svg::Renderer,
188{
189    fn measure_svg(&self, handle: &svg::Handle) -> Size<u32> {
190        delegate!(self, renderer, renderer.measure_svg(handle))
191    }
192
193    fn draw_svg(
194        &mut self,
195        svg: Svg,
196        bounds: Rectangle,
197        clip_bounds: Rectangle,
198    ) {
199        delegate!(self, renderer, renderer.draw_svg(svg, bounds, clip_bounds));
200    }
201}
202
203impl<A, B> mesh::Renderer for Renderer<A, B>
204where
205    A: mesh::Renderer,
206    B: mesh::Renderer,
207{
208    fn draw_mesh(&mut self, mesh: graphics::Mesh) {
209        delegate!(self, renderer, renderer.draw_mesh(mesh));
210    }
211}
212
213/// A compositor `A` with a fallback strategy `B`.
214///
215/// It works analogously to [`Renderer`].
216#[derive(Debug)]
217pub enum Compositor<A, B>
218where
219    A: graphics::Compositor,
220    B: graphics::Compositor,
221{
222    /// The primary compositing option.
223    Primary(A),
224    /// The secondary (or fallback) compositing option.
225    Secondary(B),
226}
227
228/// A surface `A` with a fallback strategy `B`.
229///
230/// It works analogously to [`Renderer`].
231#[derive(Debug)]
232pub enum Surface<A, B> {
233    /// The primary surface option.
234    Primary(A),
235    /// The secondary (or fallback) surface option.
236    Secondary(B),
237}
238
239impl<A, B> graphics::Compositor for Compositor<A, B>
240where
241    A: graphics::Compositor,
242    B: graphics::Compositor,
243{
244    type Renderer = Renderer<A::Renderer, B::Renderer>;
245    type Surface = Surface<A::Surface, B::Surface>;
246
247    async fn with_backend<W: compositor::Window + Clone>(
248        settings: graphics::Settings,
249        compatible_window: W,
250        shell: Shell,
251        backend: Option<&str>,
252    ) -> Result<Self, graphics::Error> {
253        use std::env;
254
255        let backends = backend
256            .map(str::to_owned)
257            .or_else(|| env::var("ICED_BACKEND").ok());
258
259        let mut candidates: Vec<_> = backends
260            .map(|backends| {
261                backends
262                    .split(',')
263                    .filter(|candidate| !candidate.is_empty())
264                    .map(str::to_owned)
265                    .map(Some)
266                    .collect()
267            })
268            .unwrap_or_default();
269
270        if candidates.is_empty() {
271            candidates.push(None);
272        }
273
274        let mut errors = vec![];
275
276        for backend in candidates.iter().map(Option::as_deref) {
277            match A::with_backend(
278                settings,
279                compatible_window.clone(),
280                shell.clone(),
281                backend,
282            )
283            .await
284            {
285                Ok(compositor) => return Ok(Self::Primary(compositor)),
286                Err(error) => {
287                    errors.push(error);
288                }
289            }
290
291            match B::with_backend(
292                settings,
293                compatible_window.clone(),
294                shell.clone(),
295                backend,
296            )
297            .await
298            {
299                Ok(compositor) => return Ok(Self::Secondary(compositor)),
300                Err(error) => {
301                    errors.push(error);
302                }
303            }
304        }
305
306        Err(graphics::Error::List(errors))
307    }
308
309    fn create_renderer(&self) -> Self::Renderer {
310        match self {
311            Self::Primary(compositor) => {
312                Renderer::Primary(compositor.create_renderer())
313            }
314            Self::Secondary(compositor) => {
315                Renderer::Secondary(compositor.create_renderer())
316            }
317        }
318    }
319
320    fn create_surface<W: compositor::Window + Clone>(
321        &mut self,
322        window: W,
323        width: u32,
324        height: u32,
325    ) -> Self::Surface {
326        match self {
327            Self::Primary(compositor) => Surface::Primary(
328                compositor.create_surface(window, width, height),
329            ),
330            Self::Secondary(compositor) => Surface::Secondary(
331                compositor.create_surface(window, width, height),
332            ),
333        }
334    }
335
336    fn configure_surface(
337        &mut self,
338        surface: &mut Self::Surface,
339        width: u32,
340        height: u32,
341    ) {
342        match (self, surface) {
343            (Self::Primary(compositor), Surface::Primary(surface)) => {
344                compositor.configure_surface(surface, width, height);
345            }
346            (Self::Secondary(compositor), Surface::Secondary(surface)) => {
347                compositor.configure_surface(surface, width, height);
348            }
349            _ => unreachable!(),
350        }
351    }
352
353    fn load_font(&mut self, font: Cow<'static, [u8]>) {
354        delegate!(self, compositor, compositor.load_font(font));
355    }
356
357    fn information(&self) -> compositor::Information {
358        delegate!(self, compositor, compositor.information())
359    }
360
361    fn present(
362        &mut self,
363        renderer: &mut Self::Renderer,
364        surface: &mut Self::Surface,
365        viewport: &graphics::Viewport,
366        background_color: Color,
367        on_pre_present: impl FnOnce(),
368    ) -> Result<(), compositor::SurfaceError> {
369        match (self, renderer, surface) {
370            (
371                Self::Primary(compositor),
372                Renderer::Primary(renderer),
373                Surface::Primary(surface),
374            ) => compositor.present(
375                renderer,
376                surface,
377                viewport,
378                background_color,
379                on_pre_present,
380            ),
381            (
382                Self::Secondary(compositor),
383                Renderer::Secondary(renderer),
384                Surface::Secondary(surface),
385            ) => compositor.present(
386                renderer,
387                surface,
388                viewport,
389                background_color,
390                on_pre_present,
391            ),
392            _ => unreachable!(),
393        }
394    }
395
396    fn screenshot(
397        &mut self,
398        renderer: &mut Self::Renderer,
399        viewport: &graphics::Viewport,
400        background_color: Color,
401    ) -> Vec<u8> {
402        match (self, renderer) {
403            (Self::Primary(compositor), Renderer::Primary(renderer)) => {
404                compositor.screenshot(renderer, viewport, background_color)
405            }
406            (Self::Secondary(compositor), Renderer::Secondary(renderer)) => {
407                compositor.screenshot(renderer, viewport, background_color)
408            }
409            _ => unreachable!(),
410        }
411    }
412}
413
414#[cfg(feature = "wgpu")]
415impl<A, B> iced_wgpu::primitive::Renderer for Renderer<A, B>
416where
417    A: iced_wgpu::primitive::Renderer,
418    B: core::Renderer,
419{
420    fn draw_primitive(
421        &mut self,
422        bounds: Rectangle,
423        primitive: impl iced_wgpu::Primitive,
424    ) {
425        match self {
426            Self::Primary(renderer) => {
427                renderer.draw_primitive(bounds, primitive);
428            }
429            Self::Secondary(_) => {
430                log::warn!(
431                    "Custom shader primitive is not supported with this renderer."
432                );
433            }
434        }
435    }
436}
437
438#[cfg(feature = "geometry")]
439mod geometry {
440    use super::Renderer;
441    use crate::core::{Point, Radians, Rectangle, Size, Svg, Vector};
442    use crate::graphics::cache::{self, Cached};
443    use crate::graphics::geometry::{self, Fill, Image, Path, Stroke, Text};
444
445    impl<A, B> geometry::Renderer for Renderer<A, B>
446    where
447        A: geometry::Renderer,
448        B: geometry::Renderer,
449    {
450        type Geometry = Geometry<A::Geometry, B::Geometry>;
451        type Frame = Frame<A::Frame, B::Frame>;
452
453        fn new_frame(&self, bounds: Rectangle) -> Self::Frame {
454            match self {
455                Self::Primary(renderer) => {
456                    Frame::Primary(renderer.new_frame(bounds))
457                }
458                Self::Secondary(renderer) => {
459                    Frame::Secondary(renderer.new_frame(bounds))
460                }
461            }
462        }
463
464        fn draw_geometry(&mut self, geometry: Self::Geometry) {
465            match (self, geometry) {
466                (Self::Primary(renderer), Geometry::Primary(geometry)) => {
467                    renderer.draw_geometry(geometry);
468                }
469                (Self::Secondary(renderer), Geometry::Secondary(geometry)) => {
470                    renderer.draw_geometry(geometry);
471                }
472                _ => unreachable!(),
473            }
474        }
475    }
476
477    #[derive(Debug, Clone)]
478    pub enum Geometry<A, B> {
479        Primary(A),
480        Secondary(B),
481    }
482
483    impl<A, B> Cached for Geometry<A, B>
484    where
485        A: Cached,
486        B: Cached,
487    {
488        type Cache = Geometry<A::Cache, B::Cache>;
489
490        fn load(cache: &Self::Cache) -> Self {
491            match cache {
492                Geometry::Primary(cache) => Self::Primary(A::load(cache)),
493                Geometry::Secondary(cache) => Self::Secondary(B::load(cache)),
494            }
495        }
496
497        fn cache(
498            self,
499            group: cache::Group,
500            previous: Option<Self::Cache>,
501        ) -> Self::Cache {
502            match (self, previous) {
503                (
504                    Self::Primary(geometry),
505                    Some(Geometry::Primary(previous)),
506                ) => Geometry::Primary(geometry.cache(group, Some(previous))),
507                (Self::Primary(geometry), None) => {
508                    Geometry::Primary(geometry.cache(group, None))
509                }
510                (
511                    Self::Secondary(geometry),
512                    Some(Geometry::Secondary(previous)),
513                ) => Geometry::Secondary(geometry.cache(group, Some(previous))),
514                (Self::Secondary(geometry), None) => {
515                    Geometry::Secondary(geometry.cache(group, None))
516                }
517                _ => unreachable!(),
518            }
519        }
520    }
521
522    #[derive(Debug)]
523    pub enum Frame<A, B> {
524        Primary(A),
525        Secondary(B),
526    }
527
528    impl<A, B> geometry::frame::Backend for Frame<A, B>
529    where
530        A: geometry::frame::Backend,
531        B: geometry::frame::Backend,
532    {
533        type Geometry = Geometry<A::Geometry, B::Geometry>;
534
535        fn width(&self) -> f32 {
536            delegate!(self, frame, frame.width())
537        }
538
539        fn height(&self) -> f32 {
540            delegate!(self, frame, frame.height())
541        }
542
543        fn size(&self) -> Size {
544            delegate!(self, frame, frame.size())
545        }
546
547        fn center(&self) -> Point {
548            delegate!(self, frame, frame.center())
549        }
550
551        fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
552            delegate!(self, frame, frame.fill(path, fill));
553        }
554
555        fn fill_rectangle(
556            &mut self,
557            top_left: Point,
558            size: Size,
559            fill: impl Into<Fill>,
560        ) {
561            delegate!(self, frame, frame.fill_rectangle(top_left, size, fill));
562        }
563
564        fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
565            delegate!(self, frame, frame.stroke(path, stroke));
566        }
567
568        fn stroke_rectangle<'a>(
569            &mut self,
570            top_left: Point,
571            size: Size,
572            stroke: impl Into<Stroke<'a>>,
573        ) {
574            delegate!(
575                self,
576                frame,
577                frame.stroke_rectangle(top_left, size, stroke)
578            );
579        }
580
581        fn stroke_text<'a>(
582            &mut self,
583            text: impl Into<Text>,
584            stroke: impl Into<Stroke<'a>>,
585        ) {
586            delegate!(self, frame, frame.stroke_text(text, stroke));
587        }
588
589        fn fill_text(&mut self, text: impl Into<Text>) {
590            delegate!(self, frame, frame.fill_text(text));
591        }
592
593        fn draw_image(&mut self, bounds: Rectangle, image: impl Into<Image>) {
594            delegate!(self, frame, frame.draw_image(bounds, image));
595        }
596
597        fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
598            delegate!(self, frame, frame.draw_svg(bounds, svg));
599        }
600
601        fn push_transform(&mut self) {
602            delegate!(self, frame, frame.push_transform());
603        }
604
605        fn pop_transform(&mut self) {
606            delegate!(self, frame, frame.pop_transform());
607        }
608
609        fn draft(&mut self, bounds: Rectangle) -> Self {
610            match self {
611                Self::Primary(frame) => Self::Primary(frame.draft(bounds)),
612                Self::Secondary(frame) => Self::Secondary(frame.draft(bounds)),
613            }
614        }
615
616        fn paste(&mut self, frame: Self) {
617            match (self, frame) {
618                (Self::Primary(target), Self::Primary(source)) => {
619                    target.paste(source);
620                }
621                (Self::Secondary(target), Self::Secondary(source)) => {
622                    target.paste(source);
623                }
624                _ => unreachable!(),
625            }
626        }
627
628        fn translate(&mut self, translation: Vector) {
629            delegate!(self, frame, frame.translate(translation));
630        }
631
632        fn rotate(&mut self, angle: impl Into<Radians>) {
633            delegate!(self, frame, frame.rotate(angle));
634        }
635
636        fn scale(&mut self, scale: impl Into<f32>) {
637            delegate!(self, frame, frame.scale(scale));
638        }
639
640        fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
641            delegate!(self, frame, frame.scale_nonuniform(scale));
642        }
643
644        fn into_geometry(self) -> Self::Geometry {
645            match self {
646                Frame::Primary(frame) => {
647                    Geometry::Primary(frame.into_geometry())
648                }
649                Frame::Secondary(frame) => {
650                    Geometry::Secondary(frame.into_geometry())
651                }
652            }
653        }
654    }
655}
656
657impl<A, B> renderer::Headless for Renderer<A, B>
658where
659    A: renderer::Headless,
660    B: renderer::Headless,
661{
662    async fn new(
663        default_font: Font,
664        default_text_size: Pixels,
665        backend: Option<&str>,
666    ) -> Option<Self> {
667        if let Some(renderer) =
668            A::new(default_font, default_text_size, backend).await
669        {
670            return Some(Self::Primary(renderer));
671        }
672
673        B::new(default_font, default_text_size, backend)
674            .await
675            .map(Self::Secondary)
676    }
677
678    fn name(&self) -> String {
679        delegate!(self, renderer, renderer.name())
680    }
681
682    fn screenshot(
683        &mut self,
684        size: Size<u32>,
685        scale_factor: f32,
686        background_color: Color,
687    ) -> Vec<u8> {
688        match self {
689            crate::fallback::Renderer::Primary(renderer) => {
690                renderer.screenshot(size, scale_factor, background_color)
691            }
692            crate::fallback::Renderer::Secondary(renderer) => {
693                renderer.screenshot(size, scale_factor, background_color)
694            }
695        }
696    }
697}
698
699impl<A, B> compositor::Default for Renderer<A, B>
700where
701    A: compositor::Default,
702    B: compositor::Default,
703{
704    type Compositor = Compositor<A::Compositor, B::Compositor>;
705}