Remove Layer trait and simplify Canvas
This commit is contained in:
parent
bb424e54c5
commit
592cc68506
9 changed files with 181 additions and 174 deletions
|
|
@ -12,7 +12,7 @@ pub fn main() {
|
|||
|
||||
struct Clock {
|
||||
now: LocalTime,
|
||||
clock: canvas::layer::Cache<LocalTime>,
|
||||
clock: canvas::Cache,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -59,7 +59,7 @@ impl Application for Clock {
|
|||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let canvas = Canvas::new(&mut self.clock, &self.now)
|
||||
let canvas = Canvas::new(self.clock.with(&self.now))
|
||||
.width(Length::Units(400))
|
||||
.height(Length::Units(400));
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ pub fn main() {
|
|||
|
||||
struct SolarSystem {
|
||||
state: State,
|
||||
now: Instant,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -40,7 +39,6 @@ impl Application for SolarSystem {
|
|||
(
|
||||
SolarSystem {
|
||||
state: State::new(),
|
||||
now: Instant::now(),
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
|
|
@ -53,8 +51,7 @@ impl Application for SolarSystem {
|
|||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Tick(instant) => {
|
||||
self.now = instant;
|
||||
self.state.clear();
|
||||
self.state.update(instant);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +64,7 @@ impl Application for SolarSystem {
|
|||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let canvas = Canvas::new(&mut self.state, &self.now)
|
||||
let canvas = Canvas::new(&mut self.state)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
|
|
@ -82,9 +79,11 @@ impl Application for SolarSystem {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct State {
|
||||
cache: canvas::layer::Cache,
|
||||
space_cache: canvas::Cache,
|
||||
system_cache: canvas::Cache,
|
||||
cursor_position: Point,
|
||||
start: Instant,
|
||||
now: Instant,
|
||||
stars: Vec<(Point, f32)>,
|
||||
}
|
||||
|
||||
|
|
@ -94,43 +93,52 @@ impl State {
|
|||
let (width, height) = window::Settings::default().size;
|
||||
|
||||
State {
|
||||
cache: Default::default(),
|
||||
space_cache: Default::default(),
|
||||
system_cache: Default::default(),
|
||||
cursor_position: Point::ORIGIN,
|
||||
start: now,
|
||||
stars: {
|
||||
use rand::Rng;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
(0..100)
|
||||
.map(|_| {
|
||||
(
|
||||
Point::new(
|
||||
rng.gen_range(0.0, width as f32),
|
||||
rng.gen_range(0.0, height as f32),
|
||||
),
|
||||
rng.gen_range(0.5, 1.0),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
now,
|
||||
stars: Self::generate_stars(width, height),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.cache.clear();
|
||||
pub fn space(&self) -> Space<'_> {
|
||||
Space { stars: &self.stars }
|
||||
}
|
||||
|
||||
pub fn system(&self) -> System {
|
||||
System {
|
||||
start: self.start,
|
||||
now: self.now,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, now: Instant) {
|
||||
self.now = now;
|
||||
self.system_cache.clear();
|
||||
}
|
||||
|
||||
fn generate_stars(width: u32, height: u32) -> Vec<(Point, f32)> {
|
||||
use rand::Rng;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
(0..100)
|
||||
.map(|_| {
|
||||
(
|
||||
Point::new(
|
||||
rng.gen_range(0.0, width as f32),
|
||||
rng.gen_range(0.0, height as f32),
|
||||
),
|
||||
rng.gen_range(0.5, 1.0),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl canvas::Program for State {
|
||||
type Input = Instant;
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
event: canvas::Event,
|
||||
_bounds: Size,
|
||||
_input: &Instant,
|
||||
) {
|
||||
impl canvas::State for State {
|
||||
fn update(&mut self, event: canvas::Event, _bounds: Size) {
|
||||
match event {
|
||||
canvas::Event::Mouse(mouse_event) => match mouse_event {
|
||||
mouse::Event::CursorMoved { x, y } => {
|
||||
|
|
@ -141,47 +149,29 @@ impl canvas::Program for State {
|
|||
state: input::ButtonState::Released,
|
||||
} => {
|
||||
self.stars.push((self.cursor_position, 2.0));
|
||||
self.space_cache.clear();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn layers<'a>(
|
||||
&'a self,
|
||||
now: &'a Instant,
|
||||
) -> Vec<Box<dyn canvas::Layer + 'a>> {
|
||||
let system = System {
|
||||
stars: &self.stars,
|
||||
start: &self.start,
|
||||
now,
|
||||
};
|
||||
|
||||
vec![Box::new(self.cache.with(system))]
|
||||
fn draw(&self, bounds: Size) -> Vec<canvas::Geometry> {
|
||||
vec![
|
||||
self.space_cache.draw(bounds, self.space()),
|
||||
self.system_cache.draw(bounds, self.system()),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct System<'a> {
|
||||
struct Space<'a> {
|
||||
stars: &'a [(Point, f32)],
|
||||
start: &'a Instant,
|
||||
now: &'a Instant,
|
||||
}
|
||||
|
||||
impl System<'_> {
|
||||
const SUN_RADIUS: f32 = 70.0;
|
||||
const ORBIT_RADIUS: f32 = 150.0;
|
||||
const EARTH_RADIUS: f32 = 12.0;
|
||||
const MOON_RADIUS: f32 = 4.0;
|
||||
const MOON_DISTANCE: f32 = 28.0;
|
||||
}
|
||||
|
||||
impl<'a> canvas::Drawable for System<'a> {
|
||||
impl canvas::Drawable for Space<'_> {
|
||||
fn draw(&self, frame: &mut canvas::Frame) {
|
||||
use canvas::{Path, Stroke};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
let center = frame.center();
|
||||
use canvas::Path;
|
||||
|
||||
let space = Path::rectangle(Point::new(0.0, 0.0), frame.size());
|
||||
|
||||
|
|
@ -191,11 +181,35 @@ impl<'a> canvas::Drawable for System<'a> {
|
|||
}
|
||||
});
|
||||
|
||||
frame.fill(&space, Color::BLACK);
|
||||
frame.fill(&stars, Color::WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct System {
|
||||
start: Instant,
|
||||
now: Instant,
|
||||
}
|
||||
|
||||
impl System {
|
||||
const SUN_RADIUS: f32 = 70.0;
|
||||
const ORBIT_RADIUS: f32 = 150.0;
|
||||
const EARTH_RADIUS: f32 = 12.0;
|
||||
const MOON_RADIUS: f32 = 4.0;
|
||||
const MOON_DISTANCE: f32 = 28.0;
|
||||
}
|
||||
|
||||
impl canvas::Drawable for System {
|
||||
fn draw(&self, frame: &mut canvas::Frame) {
|
||||
use canvas::{Path, Stroke};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
let center = frame.center();
|
||||
|
||||
let sun = Path::circle(center, Self::SUN_RADIUS);
|
||||
let orbit = Path::circle(center, Self::ORBIT_RADIUS);
|
||||
|
||||
frame.fill(&space, Color::BLACK);
|
||||
frame.fill(&stars, Color::WHITE);
|
||||
frame.fill(&sun, Color::from_rgb8(0xF9, 0xD7, 0x1C));
|
||||
frame.stroke(
|
||||
&orbit,
|
||||
|
|
@ -206,7 +220,7 @@ impl<'a> canvas::Drawable for System<'a> {
|
|||
},
|
||||
);
|
||||
|
||||
let elapsed = *self.now - *self.start;
|
||||
let elapsed = self.now - self.start;
|
||||
let elapsed_seconds = elapsed.as_secs() as f32;
|
||||
let elapsed_millis = elapsed.subsec_millis() as f32;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,24 +14,26 @@ use iced_native::{
|
|||
};
|
||||
use std::hash::Hash;
|
||||
|
||||
pub mod layer;
|
||||
pub mod path;
|
||||
|
||||
mod cache;
|
||||
mod drawable;
|
||||
mod event;
|
||||
mod fill;
|
||||
mod frame;
|
||||
mod program;
|
||||
mod geometry;
|
||||
mod state;
|
||||
mod stroke;
|
||||
mod text;
|
||||
|
||||
pub use cache::Cache;
|
||||
pub use drawable::Drawable;
|
||||
pub use event::Event;
|
||||
pub use fill::Fill;
|
||||
pub use frame::Frame;
|
||||
pub use layer::Layer;
|
||||
pub use geometry::Geometry;
|
||||
pub use path::Path;
|
||||
pub use program::Program;
|
||||
pub use state::State;
|
||||
pub use stroke::{LineCap, LineJoin, Stroke};
|
||||
pub use text::Text;
|
||||
|
||||
|
|
@ -65,7 +67,7 @@ pub use text::Text;
|
|||
/// # pub use iced_wgpu::canvas;
|
||||
/// # pub use iced_native::Color;
|
||||
/// # }
|
||||
/// use iced::canvas::{self, layer, Canvas, Drawable, Fill, Frame, Path};
|
||||
/// use iced::canvas::{self, Cache, Canvas, Drawable, Fill, Frame, Path};
|
||||
/// use iced::Color;
|
||||
///
|
||||
/// // First, we define the data we need for drawing
|
||||
|
|
@ -86,31 +88,29 @@ pub use text::Text;
|
|||
/// }
|
||||
///
|
||||
/// // We can use a `Cache` to avoid unnecessary re-tessellation
|
||||
/// let mut cache: layer::Cache<Circle> = layer::Cache::new();
|
||||
/// let cache = Cache::new();
|
||||
///
|
||||
/// // Finally, we simply use our `Cache` to create the `Canvas`!
|
||||
/// let canvas = Canvas::new(&mut cache, &Circle { radius: 50.0 });
|
||||
/// let canvas = Canvas::new(cache.with(Circle { radius: 50.0 }));
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Canvas<'a, P: Program> {
|
||||
pub struct Canvas<S: State> {
|
||||
width: Length,
|
||||
height: Length,
|
||||
program: &'a mut P,
|
||||
input: &'a P::Input,
|
||||
state: S,
|
||||
}
|
||||
|
||||
impl<'a, P: Program> Canvas<'a, P> {
|
||||
impl<S: State> Canvas<S> {
|
||||
const DEFAULT_SIZE: u16 = 100;
|
||||
|
||||
/// Creates a new [`Canvas`] with no layers.
|
||||
///
|
||||
/// [`Canvas`]: struct.Canvas.html
|
||||
pub fn new(program: &'a mut P, input: &'a P::Input) -> Self {
|
||||
pub fn new(state: S) -> Self {
|
||||
Canvas {
|
||||
width: Length::Units(Self::DEFAULT_SIZE),
|
||||
height: Length::Units(Self::DEFAULT_SIZE),
|
||||
program,
|
||||
input,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +131,7 @@ impl<'a, P: Program> Canvas<'a, P> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, P: Program> Widget<Message, Renderer> for Canvas<'a, P> {
|
||||
impl<Message, S: State> Widget<Message, Renderer> for Canvas<S> {
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
|
@ -178,7 +178,7 @@ impl<'a, Message, P: Program> Widget<Message, Renderer> for Canvas<'a, P> {
|
|||
};
|
||||
|
||||
if let Some(canvas_event) = canvas_event {
|
||||
self.program.update(canvas_event, bounds.size(), self.input)
|
||||
self.state.update(canvas_event, bounds.size())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,12 +196,12 @@ impl<'a, Message, P: Program> Widget<Message, Renderer> for Canvas<'a, P> {
|
|||
(
|
||||
Primitive::Group {
|
||||
primitives: self
|
||||
.program
|
||||
.layers(self.input)
|
||||
.iter()
|
||||
.map(|layer| Primitive::Cached {
|
||||
.state
|
||||
.draw(size)
|
||||
.into_iter()
|
||||
.map(|geometry| Primitive::Cached {
|
||||
origin,
|
||||
cache: layer.draw(size),
|
||||
cache: geometry.into_primitive(),
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
|
|
@ -218,12 +218,12 @@ impl<'a, Message, P: Program> Widget<Message, Renderer> for Canvas<'a, P> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, P: Program> From<Canvas<'a, P>>
|
||||
impl<'a, Message, S: State + 'a> From<Canvas<S>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Message: 'static,
|
||||
{
|
||||
fn from(canvas: Canvas<'a, P>) -> Element<'a, Message, Renderer> {
|
||||
fn from(canvas: Canvas<S>) -> Element<'a, Message, Renderer> {
|
||||
Element::new(canvas)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::{
|
||||
canvas::{Drawable, Frame, Layer},
|
||||
canvas::{Drawable, Frame, Geometry},
|
||||
Primitive,
|
||||
};
|
||||
|
||||
use iced_native::Size;
|
||||
use std::{borrow::Borrow, cell::RefCell, marker::PhantomData, sync::Arc};
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
|
||||
enum State {
|
||||
Empty,
|
||||
|
|
@ -48,61 +48,51 @@ impl Cache {
|
|||
*self.state.borrow_mut() = State::Empty;
|
||||
}
|
||||
|
||||
/// Binds the [`Cache`] with some data, producing a [`Layer`] that can be
|
||||
/// added to a [`Canvas`].
|
||||
///
|
||||
/// [`Cache`]: struct.Cache.html
|
||||
/// [`Layer`]: ../trait.Layer.html
|
||||
/// [`Canvas`]: ../../struct.Canvas.html
|
||||
pub fn with<'a, T>(
|
||||
&'a self,
|
||||
input: impl Borrow<T> + std::fmt::Debug + 'a,
|
||||
) -> impl Layer + 'a
|
||||
pub fn draw<T>(&self, new_bounds: Size, input: T) -> Geometry
|
||||
where
|
||||
T: Drawable + std::fmt::Debug + 'a,
|
||||
T: Drawable + std::fmt::Debug,
|
||||
{
|
||||
Bind {
|
||||
cache: self,
|
||||
input: input,
|
||||
drawable: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Bind<'a, T: Drawable, I: Borrow<T> + 'a> {
|
||||
cache: &'a Cache,
|
||||
input: I,
|
||||
drawable: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T, I> Layer for Bind<'a, T, I>
|
||||
where
|
||||
T: Drawable + std::fmt::Debug,
|
||||
I: Borrow<T> + std::fmt::Debug + 'a,
|
||||
{
|
||||
fn draw(&self, current_bounds: Size) -> Arc<Primitive> {
|
||||
use std::ops::Deref;
|
||||
|
||||
if let State::Filled { bounds, primitive } =
|
||||
self.cache.state.borrow().deref()
|
||||
if let State::Filled { bounds, primitive } = self.state.borrow().deref()
|
||||
{
|
||||
if *bounds == current_bounds {
|
||||
return primitive.clone();
|
||||
if *bounds == new_bounds {
|
||||
return Geometry::from_primitive(primitive.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut frame = Frame::new(current_bounds.width, current_bounds.height);
|
||||
self.input.borrow().draw(&mut frame);
|
||||
let mut frame = Frame::new(new_bounds.width, new_bounds.height);
|
||||
input.draw(&mut frame);
|
||||
|
||||
let primitive = Arc::new(frame.into_primitive());
|
||||
|
||||
*self.cache.state.borrow_mut() = State::Filled {
|
||||
bounds: current_bounds,
|
||||
*self.state.borrow_mut() = State::Filled {
|
||||
bounds: new_bounds,
|
||||
primitive: primitive.clone(),
|
||||
};
|
||||
|
||||
primitive
|
||||
Geometry::from_primitive(primitive)
|
||||
}
|
||||
|
||||
pub fn with<'a, T>(&'a self, input: T) -> impl crate::canvas::State + 'a
|
||||
where
|
||||
T: Drawable + std::fmt::Debug + 'a,
|
||||
{
|
||||
Bind { cache: self, input }
|
||||
}
|
||||
}
|
||||
|
||||
struct Bind<'a, T> {
|
||||
cache: &'a Cache,
|
||||
input: T,
|
||||
}
|
||||
|
||||
impl<'a, T> crate::canvas::State for Bind<'a, T>
|
||||
where
|
||||
T: Drawable + std::fmt::Debug + 'a,
|
||||
{
|
||||
fn draw(&self, bounds: Size) -> Vec<Geometry> {
|
||||
vec![self.cache.draw(bounds, &self.input)]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -16,3 +16,12 @@ impl<'a> Drawable for dyn Fn(&mut Frame) + 'a {
|
|||
self(frame)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drawable for &T
|
||||
where
|
||||
T: Drawable,
|
||||
{
|
||||
fn draw(&self, frame: &mut Frame) {
|
||||
T::draw(self, frame)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
wgpu/src/widget/canvas/geometry.rs
Normal file
15
wgpu/src/widget/canvas/geometry.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
use crate::Primitive;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Geometry(Arc<Primitive>);
|
||||
|
||||
impl Geometry {
|
||||
pub(crate) fn from_primitive(primitive: Arc<Primitive>) -> Self {
|
||||
Self(primitive)
|
||||
}
|
||||
|
||||
pub(crate) fn into_primitive(self) -> Arc<Primitive> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
//! Produce, store, and reuse geometry.
|
||||
mod cache;
|
||||
|
||||
pub use cache::Cache;
|
||||
|
||||
use crate::Primitive;
|
||||
use iced_native::Size;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A layer that can be presented at a [`Canvas`].
|
||||
///
|
||||
/// [`Canvas`]: ../struct.Canvas.html
|
||||
pub trait Layer: std::fmt::Debug {
|
||||
/// Draws the [`Layer`] in the given bounds and produces a [`Primitive`] as
|
||||
/// a result.
|
||||
///
|
||||
/// The [`Layer`] may choose to store the produced [`Primitive`] locally and
|
||||
/// only recompute it when the bounds change, its contents change, or is
|
||||
/// otherwise explicitly cleared by other means.
|
||||
///
|
||||
/// [`Layer`]: trait.Layer.html
|
||||
/// [`Primitive`]: ../../../enum.Primitive.html
|
||||
fn draw(&self, bounds: Size) -> Arc<Primitive>;
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
use crate::canvas::{Event, Layer, Size};
|
||||
|
||||
pub trait Program {
|
||||
type Input;
|
||||
|
||||
fn layers<'a>(&'a self, input: &'a Self::Input)
|
||||
-> Vec<Box<dyn Layer + 'a>>;
|
||||
|
||||
fn update<'a>(
|
||||
&'a mut self,
|
||||
_event: Event,
|
||||
_bounds: Size,
|
||||
_input: &'a Self::Input,
|
||||
) {
|
||||
}
|
||||
}
|
||||
20
wgpu/src/widget/canvas/state.rs
Normal file
20
wgpu/src/widget/canvas/state.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
use crate::canvas::{Event, Geometry, Size};
|
||||
|
||||
pub trait State {
|
||||
fn update(&mut self, _event: Event, _bounds: Size) {}
|
||||
|
||||
fn draw(&self, bounds: Size) -> Vec<Geometry>;
|
||||
}
|
||||
|
||||
impl<T> State for &mut T
|
||||
where
|
||||
T: State,
|
||||
{
|
||||
fn update(&mut self, event: Event, bounds: Size) {
|
||||
T::update(self, event, bounds);
|
||||
}
|
||||
|
||||
fn draw(&self, bounds: Size) -> Vec<Geometry> {
|
||||
T::draw(self, bounds)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue