diff options
Diffstat (limited to 'examples/multitouch/src/main.rs')
-rw-r--r-- | examples/multitouch/src/main.rs | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs new file mode 100644 index 00000000..0345ceb7 --- /dev/null +++ b/examples/multitouch/src/main.rs @@ -0,0 +1,199 @@ +//! This example shows how to use touch events in `Canvas` to draw +//! a circle around each fingertip. This only works on touch-enabled +//! computers like Microsoft Surface. +use iced::widget::canvas::event; +use iced::widget::canvas::{self, Canvas, Cursor, Geometry, Stroke}; +use iced::{ + executor, touch, window, Application, Color, Command, Element, Length, + Point, Rectangle, Settings, Subscription, Theme, +}; + +use std::collections::HashMap; + +pub fn main() -> iced::Result { + env_logger::builder().format_timestamp(None).init(); + + Multitouch::run(Settings { + antialiasing: true, + window: window::Settings { + position: window::Position::Centered, + ..window::Settings::default() + }, + ..Settings::default() + }) +} + +struct Multitouch { + state: State, +} + +#[derive(Debug)] +struct State { + cache: canvas::Cache, + fingers: HashMap<touch::Finger, Point>, +} + +impl State { + fn new() -> Self { + Self { + cache: canvas::Cache::new(), + fingers: HashMap::new(), + } + } +} + +#[derive(Debug)] +enum Message { + FingerPressed { id: touch::Finger, position: Point }, + FingerLifted { id: touch::Finger }, +} + +impl Application for Multitouch { + type Executor = executor::Default; + type Message = Message; + type Theme = Theme; + type Flags = (); + + fn new(_flags: ()) -> (Self, Command<Message>) { + ( + Multitouch { + state: State::new(), + }, + Command::none(), + ) + } + + fn title(&self) -> String { + String::from("Multitouch - Iced") + } + + fn update(&mut self, message: Message) -> Command<Message> { + match message { + Message::FingerPressed { id, position } => { + self.state.fingers.insert(id, position); + self.state.cache.clear(); + } + Message::FingerLifted { id } => { + self.state.fingers.remove(&id); + self.state.cache.clear(); + } + } + + Command::none() + } + + fn subscription(&self) -> Subscription<Message> { + Subscription::none() + } + + fn view(&self) -> Element<Message> { + Canvas::new(&self.state) + .width(Length::Fill) + .height(Length::Fill) + .into() + } +} + +impl canvas::Program<Message> for State { + type State = (); + + fn update( + &self, + _state: &mut Self::State, + event: event::Event, + _bounds: Rectangle, + _cursor: Cursor, + ) -> (event::Status, Option<Message>) { + match event { + event::Event::Touch(touch_event) => match touch_event { + touch::Event::FingerPressed { id, position } + | touch::Event::FingerMoved { id, position } => ( + event::Status::Captured, + Some(Message::FingerPressed { id, position }), + ), + touch::Event::FingerLifted { id, .. } + | touch::Event::FingerLost { id, .. } => ( + event::Status::Captured, + Some(Message::FingerLifted { id }), + ), + }, + _ => (event::Status::Ignored, None), + } + } + + fn draw( + &self, + _state: &Self::State, + _theme: &Theme, + bounds: Rectangle, + _cursor: Cursor, + ) -> Vec<Geometry> { + let fingerweb = self.cache.draw(bounds.size(), |frame| { + if self.fingers.len() < 2 { + return; + } + + // Collect tuples of (id, point); + let mut zones: Vec<(u64, Point)> = + self.fingers.iter().map(|(id, pt)| (id.0, *pt)).collect(); + + // Sort by ID + zones.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); + + // Generate sorted list of points + let vpoints: Vec<(f64, f64)> = zones + .iter() + .map(|(_, p)| (f64::from(p.x), f64::from(p.y))) + .collect(); + + let diagram: voronator::VoronoiDiagram< + voronator::delaunator::Point, + > = voronator::VoronoiDiagram::from_tuple( + &(0.0, 0.0), + &(700.0, 700.0), + &vpoints, + ) + .expect("Generate Voronoi diagram"); + + for (cell, zone) in diagram.cells().iter().zip(zones) { + let mut builder = canvas::path::Builder::new(); + + for (index, p) in cell.points().iter().enumerate() { + let p = Point::new(p.x as f32, p.y as f32); + + match index { + 0 => builder.move_to(p), + _ => builder.line_to(p), + } + } + + let path = builder.build(); + + let color_r = (10 % zone.0) as f32 / 20.0; + let color_g = (10 % (zone.0 + 8)) as f32 / 20.0; + let color_b = (10 % (zone.0 + 3)) as f32 / 20.0; + + frame.fill( + &path, + Color { + r: color_r, + g: color_g, + b: color_b, + a: 1.0, + }, + ); + + frame.stroke( + &path, + Stroke { + color: Color::BLACK, + width: 3.0, + ..Stroke::default() + }, + ); + } + }); + + vec![fingerweb] + } +} |