iced-yoda/widget/src/canvas.rs

235 lines
6.3 KiB
Rust
Raw Normal View History

2020-05-19 17:15:44 +02:00
//! Draw 2D graphics for your users.
2020-11-12 01:24:59 +01:00
pub mod event;
2020-05-19 17:15:44 +02:00
mod program;
pub use event::Event;
pub use program::Program;
pub use crate::graphics::geometry::*;
pub use crate::renderer::geometry::*;
use crate::core;
use crate::core::layout::{self, Layout};
use crate::core::mouse;
use crate::core::renderer;
use crate::core::widget::tree::{self, Tree};
use crate::core::{
Clipboard, Element, Length, Rectangle, Shell, Size, Transformation, Widget,
};
use crate::graphics::geometry;
use std::marker::PhantomData;
2020-05-19 17:15:44 +02:00
/// A widget capable of drawing 2D graphics.
///
/// ## Drawing a simple circle
/// If you want to get a quick overview, here's how we can draw a simple circle:
///
/// ```no_run
2023-06-08 20:11:59 +02:00
/// # use iced_widget::canvas::{self, Canvas, Fill, Frame, Geometry, Path, Program};
/// # use iced_widget::core::{Color, Rectangle};
2023-06-08 20:11:59 +02:00
/// # use iced_widget::core::mouse;
/// # use iced_widget::{Renderer, Theme};
/// #
/// // First, we define the data we need for drawing
2020-05-19 17:15:44 +02:00
/// #[derive(Debug)]
/// struct Circle {
/// radius: f32,
/// }
///
/// // Then, we implement the `Program` trait
/// impl Program<()> for Circle {
/// type State = ();
///
2023-06-08 20:11:59 +02:00
/// fn draw(&self, _state: &(), renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: mouse::Cursor) -> Vec<Geometry>{
2020-05-19 17:15:44 +02:00
/// // We prepare a new `Frame`
/// let mut frame = Frame::new(renderer, bounds.size());
2020-05-19 17:15:44 +02:00
///
/// // We create a `Path` representing a simple circle
/// let circle = Path::circle(frame.center(), self.radius);
///
/// // And fill it with some color
/// frame.fill(&circle, Color::BLACK);
2020-05-19 17:15:44 +02:00
///
/// // Finally, we produce the geometry
/// vec![frame.into_geometry()]
/// }
/// }
///
/// // Finally, we simply use our `Circle` to create the `Canvas`!
/// let canvas = Canvas::new(Circle { radius: 50.0 });
/// ```
#[derive(Debug)]
pub struct Canvas<P, Message, Theme = crate::Theme, Renderer = crate::Renderer>
2022-06-07 05:24:43 +02:00
where
Renderer: geometry::Renderer,
P: Program<Message, Theme, Renderer>,
2022-06-07 05:24:43 +02:00
{
2020-05-19 17:15:44 +02:00
width: Length,
height: Length,
program: P,
message_: PhantomData<Message>,
theme_: PhantomData<Theme>,
renderer_: PhantomData<Renderer>,
2020-05-19 17:15:44 +02:00
}
impl<P, Message, Theme, Renderer> Canvas<P, Message, Theme, Renderer>
2022-06-07 05:24:43 +02:00
where
P: Program<Message, Theme, Renderer>,
Renderer: geometry::Renderer,
2022-06-07 05:24:43 +02:00
{
const DEFAULT_SIZE: f32 = 100.0;
2020-05-19 17:15:44 +02:00
/// Creates a new [`Canvas`].
pub fn new(program: P) -> Self {
Canvas {
width: Length::Fixed(Self::DEFAULT_SIZE),
height: Length::Fixed(Self::DEFAULT_SIZE),
2020-05-19 17:15:44 +02:00
program,
message_: PhantomData,
2022-06-07 05:24:43 +02:00
theme_: PhantomData,
renderer_: PhantomData,
2020-05-19 17:15:44 +02:00
}
}
/// Sets the width of the [`Canvas`].
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into();
2020-05-19 17:15:44 +02:00
self
}
/// Sets the height of the [`Canvas`].
pub fn height(mut self, height: impl Into<Length>) -> Self {
self.height = height.into();
2020-05-19 17:15:44 +02:00
self
}
}
impl<P, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Canvas<P, Message, Theme, Renderer>
2020-05-19 17:15:44 +02:00
where
Renderer: geometry::Renderer,
P: Program<Message, Theme, Renderer>,
2020-05-19 17:15:44 +02:00
{
fn tag(&self) -> tree::Tag {
struct Tag<T>(T);
tree::Tag::of::<Tag<P::State>>()
}
fn state(&self) -> tree::State {
tree::State::new(P::State::default())
}
fn size(&self) -> Size<Length> {
Size {
width: self.width,
height: self.height,
}
2020-05-19 17:15:44 +02:00
}
fn layout(
&self,
_tree: &mut Tree,
_renderer: &Renderer,
2020-05-19 17:15:44 +02:00
limits: &layout::Limits,
) -> layout::Node {
layout::atomic(limits, self.width, self.height)
2020-05-19 17:15:44 +02:00
}
fn on_event(
&mut self,
tree: &mut Tree,
event: core::Event,
2020-05-19 17:15:44 +02:00
layout: Layout<'_>,
2023-06-08 20:11:59 +02:00
cursor: mouse::Cursor,
_renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
2023-07-15 10:04:25 -07:00
_viewport: &Rectangle,
) -> event::Status {
2020-05-19 17:15:44 +02:00
let bounds = layout.bounds();
let canvas_event = match event {
core::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)),
core::Event::Touch(touch_event) => Some(Event::Touch(touch_event)),
core::Event::Keyboard(keyboard_event) => {
Some(Event::Keyboard(keyboard_event))
}
2020-05-19 17:15:44 +02:00
_ => None,
};
if let Some(canvas_event) = canvas_event {
let state = tree.state.downcast_mut::<P::State>();
2020-11-12 01:24:59 +01:00
let (event_status, message) =
self.program.update(state, canvas_event, bounds, cursor);
2020-11-12 01:24:59 +01:00
if let Some(message) = message {
shell.publish(message);
2020-05-19 17:15:44 +02:00
}
2020-11-12 01:24:59 +01:00
return event_status;
2020-05-19 17:15:44 +02:00
}
event::Status::Ignored
2020-05-19 17:15:44 +02:00
}
fn mouse_interaction(
&self,
tree: &Tree,
layout: Layout<'_>,
2023-06-08 20:11:59 +02:00
cursor: mouse::Cursor,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
let bounds = layout.bounds();
let state = tree.state.downcast_ref::<P::State>();
self.program.mouse_interaction(state, bounds, cursor)
}
2020-05-19 17:15:44 +02:00
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
2023-06-08 20:11:59 +02:00
cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
let bounds = layout.bounds();
if bounds.width < 1.0 || bounds.height < 1.0 {
return;
}
let state = tree.state.downcast_ref::<P::State>();
renderer.with_transformation(
Transformation::translate(bounds.x, bounds.y),
|renderer| {
renderer.draw(
self.program.draw(state, renderer, theme, bounds, cursor),
);
},
);
2020-05-19 17:15:44 +02:00
}
}
impl<'a, P, Message, Theme, Renderer> From<Canvas<P, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
2020-05-19 17:15:44 +02:00
where
Message: 'a,
Theme: 'a,
Renderer: 'a + geometry::Renderer,
P: 'a + Program<Message, Theme, Renderer>,
2020-05-19 17:15:44 +02:00
{
fn from(
canvas: Canvas<P, Message, Theme, Renderer>,
) -> Element<'a, Message, Theme, Renderer> {
2020-05-19 17:15:44 +02:00
Element::new(canvas)
}
}