Introduce draw_with_bounds to canvas::Cache

Also:
  - Change `Rectangle::INFINITE` to have coordinates at `f32::NEG_INFINITY`
  - Change `Frame::with_clip` to _not_ adjust the coordinate system
  - Rename `Size::INFINITY` to `INFINITE`
This commit is contained in:
Héctor Ramón Jiménez 2025-08-17 22:31:58 +02:00
parent c1f7345ceb
commit f2aa570aac
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
13 changed files with 56 additions and 45 deletions

View file

@ -12,7 +12,7 @@ impl Limits {
/// No limits /// No limits
pub const NONE: Limits = Limits { pub const NONE: Limits = Limits {
min: Size::ZERO, min: Size::ZERO,
max: Size::INFINITY, max: Size::INFINITE,
}; };
/// Creates new [`Limits`] with the given minimum and maximum [`Size`]. /// Creates new [`Limits`] with the given minimum and maximum [`Size`].

View file

@ -34,8 +34,11 @@ where
} }
impl Rectangle<f32> { impl Rectangle<f32> {
/// A rectangle starting at [`Point::ORIGIN`] with infinite width and height. /// A rectangle starting at negative infinity and with infinite width and height.
pub const INFINITE: Self = Self::new(Point::ORIGIN, Size::INFINITY); pub const INFINITE: Self = Self::new(
Point::new(f32::NEG_INFINITY, f32::NEG_INFINITY),
Size::INFINITE,
);
/// Creates a new [`Rectangle`] with its top-left corner in the given /// Creates a new [`Rectangle`] with its top-left corner in the given
/// [`Point`] and with the provided [`Size`]. /// [`Point`] and with the provided [`Size`].

View file

@ -24,7 +24,7 @@ impl Size {
pub const UNIT: Size = Size::new(1., 1.); pub const UNIT: Size = Size::new(1., 1.);
/// A [`Size`] with infinite width and height. /// A [`Size`] with infinite width and height.
pub const INFINITY: Size = Size::new(f32::INFINITY, f32::INFINITY); pub const INFINITE: Size = Size::new(f32::INFINITY, f32::INFINITY);
/// Returns the minimum of each component of this size and another. /// Returns the minimum of each component of this size and another.
pub fn min(self, other: Self) -> Self { pub fn min(self, other: Self) -> Self {

View file

@ -20,7 +20,7 @@ pub use crate::core::{Image, Svg};
pub use crate::gradient::{self, Gradient}; pub use crate::gradient::{self, Gradient};
use crate::cache::Cached; use crate::cache::Cached;
use crate::core::{self, Size}; use crate::core::{self, Rectangle};
/// A renderer capable of drawing some [`Self::Geometry`]. /// A renderer capable of drawing some [`Self::Geometry`].
pub trait Renderer: core::Renderer { pub trait Renderer: core::Renderer {
@ -31,7 +31,7 @@ pub trait Renderer: core::Renderer {
type Frame: frame::Backend<Geometry = Self::Geometry>; type Frame: frame::Backend<Geometry = Self::Geometry>;
/// Creates a new [`Self::Frame`]. /// Creates a new [`Self::Frame`].
fn new_frame(&self, size: Size) -> Self::Frame; fn new_frame(&self, bounds: Rectangle) -> Self::Frame;
/// Draws the given [`Self::Geometry`]. /// Draws the given [`Self::Geometry`].
fn draw_geometry(&mut self, geometry: Self::Geometry); fn draw_geometry(&mut self, geometry: Self::Geometry);
@ -42,7 +42,7 @@ impl Renderer for () {
type Geometry = (); type Geometry = ();
type Frame = (); type Frame = ();
fn new_frame(&self, _size: Size) -> Self::Frame {} fn new_frame(&self, _bounds: Rectangle) -> Self::Frame {}
fn draw_geometry(&mut self, _geometry: Self::Geometry) {} fn draw_geometry(&mut self, _geometry: Self::Geometry) {}
} }

View file

@ -1,5 +1,5 @@
use crate::cache::{self, Cached}; use crate::cache::{self, Cached};
use crate::core::Size; use crate::core::{Rectangle, Size};
use crate::geometry::{self, Frame}; use crate::geometry::{self, Frame};
pub use cache::Group; pub use cache::Group;
@ -17,7 +17,7 @@ where
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Data<T> { struct Data<T> {
bounds: Size, bounds: Rectangle,
geometry: T, geometry: T,
} }
@ -53,7 +53,7 @@ where
/// [`Cache`]. /// [`Cache`].
/// ///
/// The closure will only be called when /// The closure will only be called when
/// - the bounds have changed since the previous draw call. /// - the size has changed since the previous draw call.
/// - the [`Cache`] is empty or has been explicitly cleared. /// - the [`Cache`] is empty or has been explicitly cleared.
/// ///
/// Otherwise, the previously stored geometry will be returned. The /// Otherwise, the previously stored geometry will be returned. The
@ -62,7 +62,21 @@ where
pub fn draw( pub fn draw(
&self, &self,
renderer: &Renderer, renderer: &Renderer,
bounds: Size, size: Size,
draw_fn: impl FnOnce(&mut Frame<Renderer>),
) -> Renderer::Geometry {
self.draw_with_bounds(renderer, Rectangle::with_size(size), draw_fn)
}
/// Draws geometry using the provided closure and stores it in the
/// [`Cache`].
///
/// Analogous to [`draw`](Self::draw), but takes a clipping [`Rectangle`] instead of
/// a [`Size`].
pub fn draw_with_bounds(
&self,
renderer: &Renderer,
bounds: Rectangle,
draw_fn: impl FnOnce(&mut Frame<Renderer>), draw_fn: impl FnOnce(&mut Frame<Renderer>),
) -> Renderer::Geometry { ) -> Renderer::Geometry {
use std::ops::Deref; use std::ops::Deref;
@ -82,7 +96,7 @@ where
} }
}; };
let mut frame = Frame::new(renderer, bounds); let mut frame = Frame::with_bounds(renderer, bounds);
draw_fn(&mut frame); draw_fn(&mut frame);
let geometry = frame.into_geometry().cache(self.raw.group(), previous); let geometry = frame.into_geometry().cache(self.raw.group(), previous);

View file

@ -16,9 +16,17 @@ where
Renderer: geometry::Renderer, Renderer: geometry::Renderer,
{ {
/// Creates a new [`Frame`] with the given dimensions. /// Creates a new [`Frame`] with the given dimensions.
///
/// Any geometry drawn outside of the dimensions will be clipped.
/// If you need further control, use [`with_bounds`](Self::with_bounds).
pub fn new(renderer: &Renderer, size: Size) -> Self { pub fn new(renderer: &Renderer, size: Size) -> Self {
Self::with_bounds(renderer, Rectangle::with_size(size))
}
/// Creates a new [`Frame`] with the given clip bounds.
pub fn with_bounds(renderer: &Renderer, bounds: Rectangle) -> Self {
Self { Self {
raw: renderer.new_frame(size), raw: renderer.new_frame(bounds),
} }
} }

View file

@ -409,13 +409,13 @@ mod geometry {
type Geometry = Geometry<A::Geometry, B::Geometry>; type Geometry = Geometry<A::Geometry, B::Geometry>;
type Frame = Frame<A::Frame, B::Frame>; type Frame = Frame<A::Frame, B::Frame>;
fn new_frame(&self, size: iced_graphics::core::Size) -> Self::Frame { fn new_frame(&self, bounds: Rectangle) -> Self::Frame {
match self { match self {
Self::Primary(renderer) => { Self::Primary(renderer) => {
Frame::Primary(renderer.new_frame(size)) Frame::Primary(renderer.new_frame(bounds))
} }
Self::Secondary(renderer) => { Self::Secondary(renderer) => {
Frame::Secondary(renderer.new_frame(size)) Frame::Secondary(renderer.new_frame(bounds))
} }
} }
} }

View file

@ -64,21 +64,14 @@ pub struct Frame {
} }
impl Frame { impl Frame {
pub fn new(size: Size) -> Self { pub fn new(bounds: Rectangle) -> Self {
Self::with_clip(Rectangle::with_size(size))
}
pub fn with_clip(clip_bounds: Rectangle) -> Self {
Self { Self {
clip_bounds, clip_bounds: bounds,
stack: Vec::new(), stack: Vec::new(),
primitives: Vec::new(), primitives: Vec::new(),
images: Vec::new(), images: Vec::new(),
text: Vec::new(), text: Vec::new(),
transform: tiny_skia::Transform::from_translate( transform: tiny_skia::Transform::identity(),
clip_bounds.x,
clip_bounds.y,
),
} }
} }
} }
@ -238,7 +231,7 @@ impl geometry::frame::Backend for Frame {
align_x: text.align_x, align_x: text.align_x,
align_y: text.align_y, align_y: text.align_y,
shaping: text.shaping, shaping: text.shaping,
clip_bounds: Rectangle::with_size(Size::INFINITY), clip_bounds: Rectangle::with_size(Size::INFINITE),
}); });
} else { } else {
text.draw_with(|path, color| self.fill(&path, color)); text.draw_with(|path, color| self.fill(&path, color));
@ -265,7 +258,7 @@ impl geometry::frame::Backend for Frame {
} }
fn draft(&mut self, clip_bounds: Rectangle) -> Self { fn draft(&mut self, clip_bounds: Rectangle) -> Self {
Self::with_clip(clip_bounds) Self::new(clip_bounds)
} }
fn paste(&mut self, frame: Self) { fn paste(&mut self, frame: Self) {

View file

@ -294,8 +294,8 @@ impl graphics::geometry::Renderer for Renderer {
type Geometry = Geometry; type Geometry = Geometry;
type Frame = geometry::Frame; type Frame = geometry::Frame;
fn new_frame(&self, size: core::Size) -> Self::Frame { fn new_frame(&self, bounds: Rectangle) -> Self::Frame {
geometry::Frame::new(size) geometry::Frame::new(bounds)
} }
fn draw_geometry(&mut self, geometry: Self::Geometry) { fn draw_geometry(&mut self, geometry: Self::Geometry) {

View file

@ -105,13 +105,8 @@ pub struct Frame {
} }
impl Frame { impl Frame {
/// Creates a new [`Frame`] with the given [`Size`].
pub fn new(size: Size) -> Frame {
Self::with_clip(Rectangle::with_size(size))
}
/// Creates a new [`Frame`] with the given clip bounds. /// Creates a new [`Frame`] with the given clip bounds.
pub fn with_clip(bounds: Rectangle) -> Frame { pub fn new(bounds: Rectangle) -> Frame {
Frame { Frame {
clip_bounds: bounds, clip_bounds: bounds,
buffers: BufferStack::new(), buffers: BufferStack::new(),
@ -120,9 +115,7 @@ impl Frame {
text: Vec::new(), text: Vec::new(),
transforms: Transforms { transforms: Transforms {
previous: Vec::new(), previous: Vec::new(),
current: Transform(lyon::math::Transform::translation( current: Transform(lyon::math::Transform::identity()),
bounds.x, bounds.y,
)),
}, },
fill_tessellator: tessellation::FillTessellator::new(), fill_tessellator: tessellation::FillTessellator::new(),
stroke_tessellator: tessellation::StrokeTessellator::new(), stroke_tessellator: tessellation::StrokeTessellator::new(),
@ -409,7 +402,7 @@ impl geometry::frame::Backend for Frame {
} }
fn draft(&mut self, clip_bounds: Rectangle) -> Frame { fn draft(&mut self, clip_bounds: Rectangle) -> Frame {
Frame::with_clip(clip_bounds) Frame::new(clip_bounds)
} }
fn paste(&mut self, frame: Frame) { fn paste(&mut self, frame: Frame) {

View file

@ -760,8 +760,8 @@ impl graphics::geometry::Renderer for Renderer {
type Geometry = Geometry; type Geometry = Geometry;
type Frame = geometry::Frame; type Frame = geometry::Frame;
fn new_frame(&self, size: core::Size) -> Self::Frame { fn new_frame(&self, bounds: Rectangle) -> Self::Frame {
geometry::Frame::new(size) geometry::Frame::new(bounds)
} }
fn draw_geometry(&mut self, geometry: Self::Geometry) { fn draw_geometry(&mut self, geometry: Self::Geometry) {

View file

@ -394,7 +394,7 @@ where
if self.snap_within_viewport { if self.snap_within_viewport {
viewport.size() viewport.size()
} else { } else {
Size::INFINITY Size::INFINITE
}, },
) )
.shrink(Padding::new(self.padding)), .shrink(Padding::new(self.padding)),
@ -508,7 +508,7 @@ where
&defaults, &defaults,
layout.children().next().unwrap(), layout.children().next().unwrap(),
cursor_position, cursor_position,
&Rectangle::with_size(Size::INFINITY), &Rectangle::with_size(Size::INFINITE),
); );
} }
} }

View file

@ -346,7 +346,7 @@ where
self.content = Renderer::Paragraph::with_spans(Text { self.content = Renderer::Paragraph::with_spans(Text {
content: &spans, content: &spans,
bounds: Size::INFINITY, bounds: Size::INFINITE,
size: preedit size: preedit
.text_size .text_size
.unwrap_or_else(|| renderer.default_size()), .unwrap_or_else(|| renderer.default_size()),