Port iced_tiny_skia to new layering architecture
This commit is contained in:
parent
2c6fd9ac14
commit
6ad5bb3597
28 changed files with 1948 additions and 1935 deletions
|
|
@ -1,36 +0,0 @@
|
|||
//! Write a graphics backend.
|
||||
use crate::core::image;
|
||||
use crate::core::svg;
|
||||
use crate::core::Size;
|
||||
use crate::{Compositor, Mesh, Renderer};
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// The graphics backend of a [`Renderer`].
|
||||
///
|
||||
/// [`Renderer`]: crate::Renderer
|
||||
pub trait Backend: Sized {
|
||||
/// The custom kind of primitives this [`Backend`] supports.
|
||||
type Primitive: TryFrom<Mesh, Error = &'static str>;
|
||||
|
||||
/// The default compositor of this [`Backend`].
|
||||
type Compositor: Compositor<Renderer = Renderer<Self>>;
|
||||
}
|
||||
|
||||
/// A graphics backend that supports text rendering.
|
||||
pub trait Text {
|
||||
/// Loads a font from its bytes.
|
||||
fn load_font(&mut self, font: Cow<'static, [u8]>);
|
||||
}
|
||||
|
||||
/// A graphics backend that supports image rendering.
|
||||
pub trait Image {
|
||||
/// Returns the dimensions of the provided image.
|
||||
fn dimensions(&self, handle: &image::Handle) -> Size<u32>;
|
||||
}
|
||||
|
||||
/// A graphics backend that supports SVG rendering.
|
||||
pub trait Svg {
|
||||
/// Returns the viewport dimensions of the provided SVG.
|
||||
fn viewport_dimensions(&self, handle: &svg::Handle) -> Size<u32>;
|
||||
}
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
use crate::Primitive;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A piece of data that can be cached.
|
||||
pub trait Cached: Sized {
|
||||
/// The type of cache produced.
|
||||
|
|
@ -18,20 +14,6 @@ pub trait Cached: Sized {
|
|||
fn cache(self, previous: Option<Self::Cache>) -> Self::Cache;
|
||||
}
|
||||
|
||||
impl<T> Cached for Primitive<T> {
|
||||
type Cache = Arc<Self>;
|
||||
|
||||
fn load(cache: &Arc<Self>) -> Self {
|
||||
Self::Cache {
|
||||
content: cache.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cache(self, _previous: Option<Arc<Self>>) -> Arc<Self> {
|
||||
Arc::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
impl Cached for () {
|
||||
type Cache = ();
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ use crate::futures::{MaybeSend, MaybeSync};
|
|||
use crate::{Error, Settings, Viewport};
|
||||
|
||||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||
use std::future::Future;
|
||||
use thiserror::Error;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::future::Future;
|
||||
|
||||
/// A graphics compositor that can draw to windows.
|
||||
pub trait Compositor: Sized {
|
||||
/// The iced renderer of the backend.
|
||||
|
|
@ -60,6 +62,14 @@ pub trait Compositor: Sized {
|
|||
/// Returns [`Information`] used by this [`Compositor`].
|
||||
fn fetch_information(&self) -> Information;
|
||||
|
||||
/// Loads a font from its bytes.
|
||||
fn load_font(&mut self, font: Cow<'static, [u8]>) {
|
||||
crate::text::font_system()
|
||||
.write()
|
||||
.expect("Write to font system")
|
||||
.load_font(font);
|
||||
}
|
||||
|
||||
/// Presents the [`Renderer`] primitives to the next frame of the given [`Surface`].
|
||||
///
|
||||
/// [`Renderer`]: Self::Renderer
|
||||
|
|
@ -168,6 +178,8 @@ impl Compositor for () {
|
|||
) {
|
||||
}
|
||||
|
||||
fn load_font(&mut self, _font: Cow<'static, [u8]>) {}
|
||||
|
||||
fn fetch_information(&self) -> Information {
|
||||
Information {
|
||||
adapter: String::from("Null Renderer"),
|
||||
|
|
|
|||
|
|
@ -1,257 +0,0 @@
|
|||
//! Track and compute the damage of graphical primitives.
|
||||
use crate::core::alignment;
|
||||
use crate::core::{Rectangle, Size};
|
||||
use crate::Primitive;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A type that has some damage bounds.
|
||||
pub trait Damage: PartialEq {
|
||||
/// Returns the bounds of the [`Damage`].
|
||||
fn bounds(&self) -> Rectangle;
|
||||
}
|
||||
|
||||
impl<T: Damage> Damage for Primitive<T> {
|
||||
fn bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Self::Text {
|
||||
bounds,
|
||||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
..
|
||||
} => {
|
||||
let mut bounds = *bounds;
|
||||
|
||||
bounds.x = match horizontal_alignment {
|
||||
alignment::Horizontal::Left => bounds.x,
|
||||
alignment::Horizontal::Center => {
|
||||
bounds.x - bounds.width / 2.0
|
||||
}
|
||||
alignment::Horizontal::Right => bounds.x - bounds.width,
|
||||
};
|
||||
|
||||
bounds.y = match vertical_alignment {
|
||||
alignment::Vertical::Top => bounds.y,
|
||||
alignment::Vertical::Center => {
|
||||
bounds.y - bounds.height / 2.0
|
||||
}
|
||||
alignment::Vertical::Bottom => bounds.y - bounds.height,
|
||||
};
|
||||
|
||||
bounds.expand(1.5)
|
||||
}
|
||||
Self::Paragraph {
|
||||
paragraph,
|
||||
position,
|
||||
..
|
||||
} => {
|
||||
let mut bounds =
|
||||
Rectangle::new(*position, paragraph.min_bounds);
|
||||
|
||||
bounds.x = match paragraph.horizontal_alignment {
|
||||
alignment::Horizontal::Left => bounds.x,
|
||||
alignment::Horizontal::Center => {
|
||||
bounds.x - bounds.width / 2.0
|
||||
}
|
||||
alignment::Horizontal::Right => bounds.x - bounds.width,
|
||||
};
|
||||
|
||||
bounds.y = match paragraph.vertical_alignment {
|
||||
alignment::Vertical::Top => bounds.y,
|
||||
alignment::Vertical::Center => {
|
||||
bounds.y - bounds.height / 2.0
|
||||
}
|
||||
alignment::Vertical::Bottom => bounds.y - bounds.height,
|
||||
};
|
||||
|
||||
bounds.expand(1.5)
|
||||
}
|
||||
Self::Editor {
|
||||
editor, position, ..
|
||||
} => {
|
||||
let bounds = Rectangle::new(*position, editor.bounds);
|
||||
|
||||
bounds.expand(1.5)
|
||||
}
|
||||
Self::RawText(raw) => {
|
||||
// TODO: Add `size` field to `raw` to compute more accurate
|
||||
// damage bounds (?)
|
||||
raw.clip_bounds.expand(1.5)
|
||||
}
|
||||
Self::Quad { bounds, shadow, .. } if shadow.color.a > 0.0 => {
|
||||
let bounds_with_shadow = Rectangle {
|
||||
x: bounds.x + shadow.offset.x.min(0.0) - shadow.blur_radius,
|
||||
y: bounds.y + shadow.offset.y.min(0.0) - shadow.blur_radius,
|
||||
width: bounds.width
|
||||
+ shadow.offset.x.abs()
|
||||
+ shadow.blur_radius * 2.0,
|
||||
height: bounds.height
|
||||
+ shadow.offset.y.abs()
|
||||
+ shadow.blur_radius * 2.0,
|
||||
};
|
||||
|
||||
bounds_with_shadow.expand(1.0)
|
||||
}
|
||||
Self::Quad { bounds, .. }
|
||||
| Self::Image { bounds, .. }
|
||||
| Self::Svg { bounds, .. } => bounds.expand(1.0),
|
||||
Self::Clip { bounds, .. } => bounds.expand(1.0),
|
||||
Self::Group { primitives } => primitives
|
||||
.iter()
|
||||
.map(Self::bounds)
|
||||
.fold(Rectangle::with_size(Size::ZERO), |a, b| {
|
||||
Rectangle::union(&a, &b)
|
||||
}),
|
||||
Self::Transform {
|
||||
transformation,
|
||||
content,
|
||||
} => content.bounds() * *transformation,
|
||||
Self::Cache { content } => content.bounds(),
|
||||
Self::Custom(custom) => custom.bounds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn regions<T: Damage>(a: &Primitive<T>, b: &Primitive<T>) -> Vec<Rectangle> {
|
||||
match (a, b) {
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: primitives_a,
|
||||
},
|
||||
Primitive::Group {
|
||||
primitives: primitives_b,
|
||||
},
|
||||
) => return list(primitives_a, primitives_b),
|
||||
(
|
||||
Primitive::Clip {
|
||||
bounds: bounds_a,
|
||||
content: content_a,
|
||||
..
|
||||
},
|
||||
Primitive::Clip {
|
||||
bounds: bounds_b,
|
||||
content: content_b,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if bounds_a == bounds_b {
|
||||
return regions(content_a, content_b)
|
||||
.into_iter()
|
||||
.filter_map(|r| r.intersection(&bounds_a.expand(1.0)))
|
||||
.collect();
|
||||
} else {
|
||||
return vec![bounds_a.expand(1.0), bounds_b.expand(1.0)];
|
||||
}
|
||||
}
|
||||
(
|
||||
Primitive::Transform {
|
||||
transformation: transformation_a,
|
||||
content: content_a,
|
||||
},
|
||||
Primitive::Transform {
|
||||
transformation: transformation_b,
|
||||
content: content_b,
|
||||
},
|
||||
) => {
|
||||
if transformation_a == transformation_b {
|
||||
return regions(content_a, content_b)
|
||||
.into_iter()
|
||||
.map(|r| r * *transformation_a)
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
(
|
||||
Primitive::Cache { content: content_a },
|
||||
Primitive::Cache { content: content_b },
|
||||
) => {
|
||||
if Arc::ptr_eq(content_a, content_b) {
|
||||
return vec![];
|
||||
}
|
||||
}
|
||||
_ if a == b => return vec![],
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let bounds_a = a.bounds();
|
||||
let bounds_b = b.bounds();
|
||||
|
||||
if bounds_a == bounds_b {
|
||||
vec![bounds_a]
|
||||
} else {
|
||||
vec![bounds_a, bounds_b]
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the damage regions between the two given lists of primitives.
|
||||
pub fn list<T: Damage>(
|
||||
previous: &[Primitive<T>],
|
||||
current: &[Primitive<T>],
|
||||
) -> Vec<Rectangle> {
|
||||
let damage = previous
|
||||
.iter()
|
||||
.zip(current)
|
||||
.flat_map(|(a, b)| regions(a, b));
|
||||
|
||||
if previous.len() == current.len() {
|
||||
damage.collect()
|
||||
} else {
|
||||
let (smaller, bigger) = if previous.len() < current.len() {
|
||||
(previous, current)
|
||||
} else {
|
||||
(current, previous)
|
||||
};
|
||||
|
||||
// Extend damage by the added/removed primitives
|
||||
damage
|
||||
.chain(bigger[smaller.len()..].iter().map(Damage::bounds))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Groups the given damage regions that are close together inside the given
|
||||
/// bounds.
|
||||
pub fn group(
|
||||
mut damage: Vec<Rectangle>,
|
||||
scale_factor: f32,
|
||||
bounds: Size<u32>,
|
||||
) -> Vec<Rectangle> {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
const AREA_THRESHOLD: f32 = 20_000.0;
|
||||
|
||||
let bounds = Rectangle {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
width: bounds.width as f32,
|
||||
height: bounds.height as f32,
|
||||
};
|
||||
|
||||
damage.sort_by(|a, b| {
|
||||
a.x.partial_cmp(&b.x)
|
||||
.unwrap_or(Ordering::Equal)
|
||||
.then_with(|| a.y.partial_cmp(&b.y).unwrap_or(Ordering::Equal))
|
||||
});
|
||||
|
||||
let mut output = Vec::new();
|
||||
let mut scaled = damage
|
||||
.into_iter()
|
||||
.filter_map(|region| (region * scale_factor).intersection(&bounds))
|
||||
.filter(|region| region.width >= 1.0 && region.height >= 1.0);
|
||||
|
||||
if let Some(mut current) = scaled.next() {
|
||||
for region in scaled {
|
||||
let union = current.union(®ion);
|
||||
|
||||
if union.area() - current.area() - region.area() <= AREA_THRESHOLD {
|
||||
current = union;
|
||||
} else {
|
||||
output.push(current);
|
||||
current = region;
|
||||
}
|
||||
}
|
||||
|
||||
output.push(current);
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
139
graphics/src/layer.rs
Normal file
139
graphics/src/layer.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
//! Draw and stack layers of graphical primitives.
|
||||
use crate::core::{Rectangle, Transformation};
|
||||
|
||||
/// A layer of graphical primitives.
|
||||
///
|
||||
/// Layers normally dictate a set of primitives that are
|
||||
/// rendered in a specific order.
|
||||
pub trait Layer: Default {
|
||||
/// Creates a new [`Layer`] with the given bounds.
|
||||
fn with_bounds(bounds: Rectangle) -> Self;
|
||||
|
||||
/// Flushes and settles any pending group of primitives in the [`Layer`].
|
||||
///
|
||||
/// This will be called when a [`Layer`] is finished. It allows layers to efficiently
|
||||
/// record primitives together and defer grouping until the end.
|
||||
fn flush(&mut self);
|
||||
|
||||
/// Resizes the [`Layer`] to the given bounds.
|
||||
fn resize(&mut self, bounds: Rectangle);
|
||||
|
||||
/// Clears all the layers contents and resets its bounds.
|
||||
fn reset(&mut self);
|
||||
}
|
||||
|
||||
/// A stack of layers used for drawing.
|
||||
#[derive(Debug)]
|
||||
pub struct Stack<T: Layer> {
|
||||
layers: Vec<T>,
|
||||
transformations: Vec<Transformation>,
|
||||
previous: Vec<usize>,
|
||||
current: usize,
|
||||
active_count: usize,
|
||||
}
|
||||
|
||||
impl<T: Layer> Stack<T> {
|
||||
/// Creates a new empty [`Stack`].
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
layers: vec![T::default()],
|
||||
transformations: vec![Transformation::IDENTITY],
|
||||
previous: vec![],
|
||||
current: 0,
|
||||
active_count: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the current [`Layer`] of the [`Stack`], together with
|
||||
/// the current [`Transformation`].
|
||||
#[inline]
|
||||
pub fn current_mut(&mut self) -> (&mut T, Transformation) {
|
||||
let transformation = self.transformation();
|
||||
|
||||
(&mut self.layers[self.current], transformation)
|
||||
}
|
||||
|
||||
/// Returns the current [`Transformation`] of the [`Stack`].
|
||||
#[inline]
|
||||
pub fn transformation(&self) -> Transformation {
|
||||
self.transformations.last().copied().unwrap()
|
||||
}
|
||||
|
||||
/// Pushes a new clipping region in the [`Stack`]; creating a new layer in the
|
||||
/// process.
|
||||
pub fn push_clip(&mut self, bounds: Rectangle) {
|
||||
self.previous.push(self.current);
|
||||
|
||||
self.current = self.active_count;
|
||||
self.active_count += 1;
|
||||
|
||||
let bounds = bounds * self.transformation();
|
||||
|
||||
if self.current == self.layers.len() {
|
||||
self.layers.push(T::with_bounds(bounds));
|
||||
} else {
|
||||
self.layers[self.current].resize(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
/// Pops the current clipping region from the [`Stack`] and restores the previous one.
|
||||
///
|
||||
/// The current layer will be recorded for drawing.
|
||||
pub fn pop_clip(&mut self) {
|
||||
self.flush();
|
||||
|
||||
self.current = self.previous.pop().unwrap();
|
||||
}
|
||||
|
||||
/// Pushes a new [`Transformation`] in the [`Stack`].
|
||||
///
|
||||
/// Future drawing operations will be affected by this new [`Transformation`] until
|
||||
/// it is popped using [`pop_transformation`].
|
||||
///
|
||||
/// [`pop_transformation`]: Self::pop_transformation
|
||||
pub fn push_transformation(&mut self, transformation: Transformation) {
|
||||
self.transformations
|
||||
.push(self.transformation() * transformation);
|
||||
}
|
||||
|
||||
/// Pops the current [`Transformation`] in the [`Stack`].
|
||||
pub fn pop_transformation(&mut self) {
|
||||
let _ = self.transformations.pop();
|
||||
}
|
||||
|
||||
/// Returns an iterator over mutable references to the layers in the [`Stack`].
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||
self.flush();
|
||||
|
||||
self.layers[..self.active_count].iter_mut()
|
||||
}
|
||||
|
||||
/// Returns an iterator over immutable references to the layers in the [`Stack`].
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||
self.layers[..self.active_count].iter()
|
||||
}
|
||||
|
||||
/// Flushes and settles any primitives in the current layer of the [`Stack`].
|
||||
pub fn flush(&mut self) {
|
||||
self.layers[self.current].flush();
|
||||
}
|
||||
|
||||
/// Clears the layers of the [`Stack`], allowing reuse.
|
||||
///
|
||||
/// This will normally keep layer allocations for future drawing operations.
|
||||
pub fn clear(&mut self) {
|
||||
for layer in self.layers[..self.active_count].iter_mut() {
|
||||
layer.reset();
|
||||
}
|
||||
|
||||
self.current = 0;
|
||||
self.active_count = 1;
|
||||
self.previous.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Layer> Default for Stack<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
@ -10,35 +10,29 @@
|
|||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
mod antialiasing;
|
||||
mod cached;
|
||||
mod primitive;
|
||||
mod settings;
|
||||
mod viewport;
|
||||
|
||||
pub mod backend;
|
||||
pub mod color;
|
||||
pub mod compositor;
|
||||
pub mod damage;
|
||||
pub mod error;
|
||||
pub mod gradient;
|
||||
pub mod image;
|
||||
pub mod layer;
|
||||
pub mod mesh;
|
||||
pub mod renderer;
|
||||
pub mod text;
|
||||
|
||||
#[cfg(feature = "geometry")]
|
||||
pub mod geometry;
|
||||
|
||||
pub use antialiasing::Antialiasing;
|
||||
pub use backend::Backend;
|
||||
pub use cached::Cached;
|
||||
pub use compositor::Compositor;
|
||||
pub use damage::Damage;
|
||||
pub use error::Error;
|
||||
pub use gradient::Gradient;
|
||||
pub use image::Image;
|
||||
pub use layer::Layer;
|
||||
pub use mesh::Mesh;
|
||||
pub use primitive::Primitive;
|
||||
pub use renderer::Renderer;
|
||||
pub use settings::Settings;
|
||||
pub use text::Text;
|
||||
pub use viewport::Viewport;
|
||||
|
|
|
|||
|
|
@ -1,160 +0,0 @@
|
|||
//! Draw using different graphical primitives.
|
||||
use crate::core::alignment;
|
||||
use crate::core::image;
|
||||
use crate::core::svg;
|
||||
use crate::core::text;
|
||||
use crate::core::{
|
||||
Background, Border, Color, Font, Pixels, Point, Rectangle, Shadow,
|
||||
Transformation, Vector,
|
||||
};
|
||||
use crate::text::editor;
|
||||
use crate::text::paragraph;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A rendering primitive.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Primitive<T> {
|
||||
/// A text primitive
|
||||
Text {
|
||||
/// The contents of the text.
|
||||
content: String,
|
||||
/// The bounds of the text.
|
||||
bounds: Rectangle,
|
||||
/// The color of the text.
|
||||
color: Color,
|
||||
/// The size of the text in logical pixels.
|
||||
size: Pixels,
|
||||
/// The line height of the text.
|
||||
line_height: text::LineHeight,
|
||||
/// The font of the text.
|
||||
font: Font,
|
||||
/// The horizontal alignment of the text.
|
||||
horizontal_alignment: alignment::Horizontal,
|
||||
/// The vertical alignment of the text.
|
||||
vertical_alignment: alignment::Vertical,
|
||||
/// The shaping strategy of the text.
|
||||
shaping: text::Shaping,
|
||||
/// The clip bounds of the text.
|
||||
clip_bounds: Rectangle,
|
||||
},
|
||||
/// A paragraph primitive
|
||||
Paragraph {
|
||||
/// The [`paragraph::Weak`] reference.
|
||||
paragraph: paragraph::Weak,
|
||||
/// The position of the paragraph.
|
||||
position: Point,
|
||||
/// The color of the paragraph.
|
||||
color: Color,
|
||||
/// The clip bounds of the paragraph.
|
||||
clip_bounds: Rectangle,
|
||||
},
|
||||
/// An editor primitive
|
||||
Editor {
|
||||
/// The [`editor::Weak`] reference.
|
||||
editor: editor::Weak,
|
||||
/// The position of the editor.
|
||||
position: Point,
|
||||
/// The color of the editor.
|
||||
color: Color,
|
||||
/// The clip bounds of the editor.
|
||||
clip_bounds: Rectangle,
|
||||
},
|
||||
/// A raw `cosmic-text` primitive
|
||||
RawText(crate::text::Raw),
|
||||
/// A quad primitive
|
||||
Quad {
|
||||
/// The bounds of the quad
|
||||
bounds: Rectangle,
|
||||
/// The background of the quad
|
||||
background: Background,
|
||||
/// The [`Border`] of the quad
|
||||
border: Border,
|
||||
/// The [`Shadow`] of the quad
|
||||
shadow: Shadow,
|
||||
},
|
||||
/// An image primitive
|
||||
Image {
|
||||
/// The handle of the image
|
||||
handle: image::Handle,
|
||||
/// The filter method of the image
|
||||
filter_method: image::FilterMethod,
|
||||
/// The bounds of the image
|
||||
bounds: Rectangle,
|
||||
},
|
||||
/// An SVG primitive
|
||||
Svg {
|
||||
/// The path of the SVG file
|
||||
handle: svg::Handle,
|
||||
|
||||
/// The [`Color`] filter
|
||||
color: Option<Color>,
|
||||
|
||||
/// The bounds of the viewport
|
||||
bounds: Rectangle,
|
||||
},
|
||||
/// A group of primitives
|
||||
Group {
|
||||
/// The primitives of the group
|
||||
primitives: Vec<Primitive<T>>,
|
||||
},
|
||||
/// A clip primitive
|
||||
Clip {
|
||||
/// The bounds of the clip
|
||||
bounds: Rectangle,
|
||||
/// The content of the clip
|
||||
content: Box<Primitive<T>>,
|
||||
},
|
||||
/// A primitive that applies a [`Transformation`]
|
||||
Transform {
|
||||
/// The [`Transformation`]
|
||||
transformation: Transformation,
|
||||
|
||||
/// The primitive to transform
|
||||
content: Box<Primitive<T>>,
|
||||
},
|
||||
/// A cached primitive.
|
||||
///
|
||||
/// This can be useful if you are implementing a widget where primitive
|
||||
/// generation is expensive.
|
||||
Cache {
|
||||
/// The cached primitive
|
||||
content: Arc<Primitive<T>>,
|
||||
},
|
||||
/// A backend-specific primitive.
|
||||
Custom(T),
|
||||
}
|
||||
|
||||
impl<T> Primitive<T> {
|
||||
/// Groups the current [`Primitive`].
|
||||
pub fn group(primitives: Vec<Self>) -> Self {
|
||||
Self::Group { primitives }
|
||||
}
|
||||
|
||||
/// Clips the current [`Primitive`].
|
||||
pub fn clip(self, bounds: Rectangle) -> Self {
|
||||
Self::Clip {
|
||||
bounds,
|
||||
content: Box::new(self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Translates the current [`Primitive`].
|
||||
pub fn translate(self, translation: Vector) -> Self {
|
||||
Self::Transform {
|
||||
transformation: Transformation::translate(
|
||||
translation.x,
|
||||
translation.y,
|
||||
),
|
||||
content: Box::new(self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms the current [`Primitive`].
|
||||
pub fn transform(self, transformation: Transformation) -> Self {
|
||||
Self::Transform {
|
||||
transformation,
|
||||
content: Box::new(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ impl<B: Backend> iced_core::Renderer for Renderer<B> {
|
|||
self.stack.push(std::mem::take(&mut self.primitives));
|
||||
}
|
||||
|
||||
fn end_layer(&mut self, bounds: Rectangle) {
|
||||
fn end_layer(&mut self) {
|
||||
let layer = std::mem::replace(
|
||||
&mut self.primitives,
|
||||
self.stack.pop().expect("a layer should be recording"),
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use std::borrow::Cow;
|
|||
use std::sync::{Arc, RwLock, Weak};
|
||||
|
||||
/// A text primitive.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Text {
|
||||
/// A paragraph.
|
||||
#[allow(missing_docs)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue