//! Create a renderer from a [`Backend`]. use crate::backend::{self, Backend}; use crate::Primitive; use iced_core::image; use iced_core::layout; use iced_core::renderer; use iced_core::svg; use iced_core::text::{self, Text}; use iced_core::{ Background, Color, Element, Font, Point, Rectangle, Size, Vector, }; use std::borrow::Cow; use std::marker::PhantomData; /// A backend-agnostic renderer that supports all the built-in widgets. #[derive(Debug)] pub struct Renderer { backend: B, primitives: Vec>, theme: PhantomData, } impl Renderer { /// Creates a new [`Renderer`] from the given [`Backend`]. pub fn new(backend: B) -> Self { Self { backend, primitives: Vec::new(), theme: PhantomData, } } /// Returns a reference to the [`Backend`] of the [`Renderer`]. pub fn backend(&self) -> &B { &self.backend } /// Enqueues the given [`Primitive`] in the [`Renderer`] for drawing. pub fn draw_primitive(&mut self, primitive: Primitive) { self.primitives.push(primitive); } /// Runs the given closure with the [`Backend`] and the recorded primitives /// of the [`Renderer`]. pub fn with_primitives( &mut self, f: impl FnOnce(&mut B, &[Primitive]) -> O, ) -> O { f(&mut self.backend, &self.primitives) } /// Starts recording a new layer. pub fn start_layer(&mut self) -> Vec> { std::mem::take(&mut self.primitives) } /// Ends the recording of a layer. pub fn end_layer( &mut self, primitives: Vec>, bounds: Rectangle, ) { let layer = std::mem::replace(&mut self.primitives, primitives); self.primitives.push(Primitive::group(layer).clip(bounds)); } /// Starts recording a translation. pub fn start_translation(&mut self) -> Vec> { std::mem::take(&mut self.primitives) } /// Ends the recording of a translation. pub fn end_translation( &mut self, primitives: Vec>, translation: Vector, ) { let layer = std::mem::replace(&mut self.primitives, primitives); self.primitives .push(Primitive::group(layer).translate(translation)); } } impl iced_core::Renderer for Renderer { type Theme = T; fn layout( &mut self, element: &Element<'_, Message, Self>, limits: &layout::Limits, ) -> layout::Node { self.backend.trim_measurements(); element.as_widget().layout(self, limits) } fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { let current = self.start_layer(); f(self); self.end_layer(current, bounds); } fn with_translation( &mut self, translation: Vector, f: impl FnOnce(&mut Self), ) { let current = self.start_translation(); f(self); self.end_translation(current, translation); } fn fill_quad( &mut self, quad: renderer::Quad, background: impl Into, ) { self.primitives.push(Primitive::Quad { bounds: quad.bounds, background: background.into(), border_radius: quad.border_radius.into(), border_width: quad.border_width, border_color: quad.border_color, }); } fn clear(&mut self) { self.primitives.clear(); } } impl text::Renderer for Renderer where B: Backend + backend::Text, { type Font = Font; const ICON_FONT: Font = B::ICON_FONT; const CHECKMARK_ICON: char = B::CHECKMARK_ICON; const ARROW_DOWN_ICON: char = B::ARROW_DOWN_ICON; fn default_font(&self) -> Self::Font { self.backend().default_font() } fn default_size(&self) -> f32 { self.backend().default_size() } fn measure( &self, content: &str, size: f32, line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, ) -> Size { self.backend().measure( content, size, line_height, font, bounds, shaping, ) } fn hit_test( &self, content: &str, size: f32, line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, point: Point, nearest_only: bool, ) -> Option { self.backend().hit_test( content, size, line_height, font, bounds, shaping, point, nearest_only, ) } fn load_font(&mut self, bytes: Cow<'static, [u8]>) { self.backend.load_font(bytes); } fn fill_text(&mut self, text: Text<'_, Self::Font>) { self.primitives.push(Primitive::Text { content: text.content.to_string(), bounds: text.bounds, size: text.size, line_height: text.line_height, color: text.color, font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, shaping: text.shaping, }); } } impl image::Renderer for Renderer where B: Backend + backend::Image, { type Handle = image::Handle; fn dimensions(&self, handle: &image::Handle) -> Size { self.backend().dimensions(handle) } fn draw(&mut self, handle: image::Handle, bounds: Rectangle) { self.primitives.push(Primitive::Image { handle, bounds }) } } impl svg::Renderer for Renderer where B: Backend + backend::Svg, { fn dimensions(&self, handle: &svg::Handle) -> Size { self.backend().viewport_dimensions(handle) } fn draw( &mut self, handle: svg::Handle, color: Option, bounds: Rectangle, ) { self.primitives.push(Primitive::Svg { handle, color, bounds, }) } }