219 lines
5.9 KiB
Rust
219 lines
5.9 KiB
Rust
//! This example showcases an interactive `Canvas` for drawing Bézier curves.
|
|
use iced::{
|
|
button, Align, Button, Column, Container, Element, Length, Sandbox,
|
|
Settings, Text,
|
|
};
|
|
|
|
pub fn main() {
|
|
Example::run(Settings {
|
|
antialiasing: true,
|
|
..Settings::default()
|
|
});
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct Example {
|
|
bezier: bezier::State,
|
|
curves: Vec<bezier::Curve>,
|
|
button_state: button::State,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum Message {
|
|
AddCurve(bezier::Curve),
|
|
Clear,
|
|
}
|
|
|
|
impl Sandbox for Example {
|
|
type Message = Message;
|
|
|
|
fn new() -> Self {
|
|
Example::default()
|
|
}
|
|
|
|
fn title(&self) -> String {
|
|
String::from("Bezier tool - Iced")
|
|
}
|
|
|
|
fn update(&mut self, message: Message) {
|
|
match message {
|
|
Message::AddCurve(curve) => {
|
|
self.curves.push(curve);
|
|
self.bezier.request_redraw();
|
|
}
|
|
Message::Clear => {
|
|
self.bezier = bezier::State::default();
|
|
self.curves.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn view(&mut self) -> Element<Message> {
|
|
let content = Column::new()
|
|
.padding(20)
|
|
.spacing(20)
|
|
.align_items(Align::Center)
|
|
.push(
|
|
Text::new("Bezier tool example")
|
|
.width(Length::Shrink)
|
|
.size(50),
|
|
)
|
|
.push(self.bezier.view(&self.curves).map(Message::AddCurve))
|
|
.push(
|
|
Button::new(&mut self.button_state, Text::new("Clear"))
|
|
.padding(8)
|
|
.on_press(Message::Clear),
|
|
);
|
|
|
|
Container::new(content)
|
|
.width(Length::Fill)
|
|
.height(Length::Fill)
|
|
.center_x()
|
|
.center_y()
|
|
.into()
|
|
}
|
|
}
|
|
|
|
mod bezier {
|
|
use iced::{
|
|
canvas::{
|
|
self, Canvas, Drawable, Event, Frame, Geometry, Path, Stroke,
|
|
},
|
|
mouse, ButtonState, Element, Length, Point, Size,
|
|
};
|
|
|
|
#[derive(Default)]
|
|
pub struct State {
|
|
pending: Option<Pending>,
|
|
cursor_position: Point,
|
|
cache: canvas::Cache,
|
|
}
|
|
|
|
impl State {
|
|
pub fn view<'a>(
|
|
&'a mut self,
|
|
curves: &'a [Curve],
|
|
) -> Element<'a, Curve> {
|
|
Canvas::new(Bezier {
|
|
state: self,
|
|
curves,
|
|
})
|
|
.width(Length::Fill)
|
|
.height(Length::Fill)
|
|
.into()
|
|
}
|
|
|
|
pub fn request_redraw(&mut self) {
|
|
self.cache.clear()
|
|
}
|
|
}
|
|
|
|
struct Bezier<'a> {
|
|
state: &'a mut State,
|
|
curves: &'a [Curve],
|
|
}
|
|
|
|
impl<'a> canvas::State<Curve> for Bezier<'a> {
|
|
fn update(&mut self, event: Event, _bounds: Size) -> Option<Curve> {
|
|
match event {
|
|
Event::Mouse(mouse_event) => match mouse_event {
|
|
mouse::Event::CursorMoved { x, y } => {
|
|
self.state.cursor_position = Point::new(x, y);
|
|
|
|
None
|
|
}
|
|
mouse::Event::Input {
|
|
button: mouse::Button::Left,
|
|
state: ButtonState::Pressed,
|
|
} => match self.state.pending {
|
|
None => {
|
|
self.state.pending = Some(Pending::One {
|
|
from: self.state.cursor_position,
|
|
});
|
|
None
|
|
}
|
|
Some(Pending::One { from }) => {
|
|
self.state.pending = Some(Pending::Two {
|
|
from,
|
|
to: self.state.cursor_position,
|
|
});
|
|
|
|
None
|
|
}
|
|
Some(Pending::Two { from, to }) => {
|
|
self.state.pending = None;
|
|
|
|
Some(Curve {
|
|
from,
|
|
to,
|
|
control: self.state.cursor_position,
|
|
})
|
|
}
|
|
},
|
|
_ => None,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn draw(&self, bounds: Size) -> Vec<Geometry> {
|
|
let curves = self.state.cache.draw(bounds, &self.curves);
|
|
|
|
if let Some(pending) = &self.state.pending {
|
|
let pending_curve =
|
|
pending.draw(bounds, self.state.cursor_position);
|
|
|
|
vec![curves, pending_curve]
|
|
} else {
|
|
vec![curves]
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Curve {
|
|
from: Point,
|
|
to: Point,
|
|
control: Point,
|
|
}
|
|
|
|
impl Drawable for Curve {
|
|
fn draw(&self, frame: &mut Frame) {
|
|
let curve = Path::new(|p| {
|
|
p.move_to(self.from);
|
|
p.quadratic_curve_to(self.control, self.to);
|
|
});
|
|
|
|
frame.stroke(&curve, Stroke::default().with_width(2.0));
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum Pending {
|
|
One { from: Point },
|
|
Two { from: Point, to: Point },
|
|
}
|
|
|
|
impl Pending {
|
|
fn draw(&self, bounds: Size, cursor_position: Point) -> Geometry {
|
|
let mut frame = Frame::new(bounds);
|
|
|
|
match *self {
|
|
Pending::One { from } => {
|
|
let line = Path::line(from, cursor_position);
|
|
frame.stroke(&line, Stroke::default().with_width(2.0));
|
|
}
|
|
Pending::Two { from, to } => {
|
|
let curve = Curve {
|
|
from,
|
|
to,
|
|
control: cursor_position,
|
|
};
|
|
|
|
curve.draw(&mut frame);
|
|
}
|
|
};
|
|
|
|
frame.into_geometry()
|
|
}
|
|
}
|
|
}
|