diff options
author | 2022-10-07 16:58:45 -0700 | |
---|---|---|
committer | 2022-10-07 16:58:45 -0700 | |
commit | aabc4e87b51af8025910681dc427260254877e4c (patch) | |
tree | 44dc50e04e58c15871cc56d22221dc43685d1d58 | |
parent | 7a124476b1f609019bb0fdfa1254236af2d2a49e (diff) | |
parent | 77c838011fe6f8f567389d5994584a1a1b8420c5 (diff) | |
download | iced-aabc4e87b51af8025910681dc427260254877e4c.tar.gz iced-aabc4e87b51af8025910681dc427260254877e4c.tar.bz2 iced-aabc4e87b51af8025910681dc427260254877e4c.zip |
Merge branch 'master' into fear/linear-gradients
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | examples/game_of_life/src/main.rs | 14 | ||||
-rw-r--r-- | examples/multitouch/Cargo.toml | 12 | ||||
-rw-r--r-- | examples/multitouch/src/main.rs | 199 | ||||
-rw-r--r-- | graphics/src/widget/canvas.rs | 3 | ||||
-rw-r--r-- | graphics/src/widget/canvas/event.rs | 4 | ||||
-rw-r--r-- | native/src/widget/radio.rs | 4 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/touch.rs | 2 | ||||
-rw-r--r-- | style/src/radio.rs | 4 | ||||
-rw-r--r-- | style/src/theme.rs | 14 | ||||
-rw-r--r-- | style/src/toggler.rs | 2 | ||||
-rw-r--r-- | wgpu/README.md | 7 |
14 files changed, 257 insertions, 12 deletions
@@ -72,6 +72,7 @@ members = [ "examples/integration_opengl", "examples/integration_wgpu", "examples/modern_art", + "examples/multitouch", "examples/pane_grid", "examples/pick_list", "examples/pokedex", @@ -8,7 +8,7 @@ [](https://crates.io/crates/iced) [](https://github.com/iced-rs/iced/blob/master/LICENSE) [](https://crates.io/crates/iced) -[](https://github.com/iced-rs/iced/actions) +[](https://github.com/iced-rs/iced/actions) [](https://discord.gg/3xZJ65GAhd) A cross-platform GUI library for Rust focused on simplicity and type-safety. diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index a2030275..2a8b3721 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -204,6 +204,7 @@ fn view_controls<'a>( mod grid { use crate::Preset; + use iced::touch; use iced::widget::canvas; use iced::widget::canvas::event::{self, Event}; use iced::widget::canvas::{ @@ -423,6 +424,19 @@ mod grid { }; match event { + Event::Touch(touch::Event::FingerMoved { .. }) => { + let message = { + *interaction = if is_populated { + Interaction::Erasing + } else { + Interaction::Drawing + }; + + populate.or(unpopulate) + }; + + (event::Status::Captured, message) + } Event::Mouse(mouse_event) => match mouse_event { mouse::Event::ButtonPressed(button) => { let message = match button { diff --git a/examples/multitouch/Cargo.toml b/examples/multitouch/Cargo.toml new file mode 100644 index 00000000..f7c8c145 --- /dev/null +++ b/examples/multitouch/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "multitouch" +version = "0.1.0" +authors = ["Artur Sapek <artur@kraken.com>"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../..", features = ["canvas", "tokio", "debug"] } +tokio = { version = "1.0", features = ["sync"] } +env_logger = "0.9" +voronator = "0.2" 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] + } +} diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index 36c17906..ea2efcc1 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -170,6 +170,9 @@ where iced_native::Event::Mouse(mouse_event) => { Some(Event::Mouse(mouse_event)) } + iced_native::Event::Touch(touch_event) => { + Some(Event::Touch(touch_event)) + } iced_native::Event::Keyboard(keyboard_event) => { Some(Event::Keyboard(keyboard_event)) } diff --git a/graphics/src/widget/canvas/event.rs b/graphics/src/widget/canvas/event.rs index 5bf6f7a6..7c733a4d 100644 --- a/graphics/src/widget/canvas/event.rs +++ b/graphics/src/widget/canvas/event.rs @@ -1,6 +1,7 @@ //! Handle events of a canvas. use iced_native::keyboard; use iced_native::mouse; +use iced_native::touch; pub use iced_native::event::Status; @@ -12,6 +13,9 @@ pub enum Event { /// A mouse event. Mouse(mouse::Event), + /// A touch event. + Touch(touch::Event), + /// A keyboard event. Keyboard(keyboard::Event), } diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index c9152d05..cb83f745 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -230,9 +230,9 @@ where let mut children = layout.children(); let custom_style = if is_mouse_over { - theme.hovered(self.style) + theme.hovered(self.style, self.is_selected) } else { - theme.active(self.style) + theme.active(self.style, self.is_selected) }; { @@ -177,6 +177,7 @@ pub mod mouse; pub mod overlay; pub mod settings; pub mod time; +pub mod touch; pub mod widget; pub mod window; diff --git a/src/touch.rs b/src/touch.rs new file mode 100644 index 00000000..0b77c386 --- /dev/null +++ b/src/touch.rs @@ -0,0 +1,2 @@ +//! Listen and react to touch events. +pub use crate::runtime::touch::{Event, Finger}; diff --git a/style/src/radio.rs b/style/src/radio.rs index a4d4a83b..d14ea33e 100644 --- a/style/src/radio.rs +++ b/style/src/radio.rs @@ -15,7 +15,7 @@ pub struct Appearance { pub trait StyleSheet { type Style: Default + Copy; - fn active(&self, style: Self::Style) -> Appearance; + fn active(&self, style: Self::Style, is_selected: bool) -> Appearance; - fn hovered(&self, style: Self::Style) -> Appearance; + fn hovered(&self, style: Self::Style, is_selected: bool) -> Appearance; } diff --git a/style/src/theme.rs b/style/src/theme.rs index 9e9abfa0..ea538c3a 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -415,7 +415,11 @@ impl pick_list::StyleSheet for Theme { impl radio::StyleSheet for Theme { type Style = (); - fn active(&self, _style: Self::Style) -> radio::Appearance { + fn active( + &self, + _style: Self::Style, + _is_selected: bool, + ) -> radio::Appearance { let palette = self.extended_palette(); radio::Appearance { @@ -427,8 +431,12 @@ impl radio::StyleSheet for Theme { } } - fn hovered(&self, style: Self::Style) -> radio::Appearance { - let active = self.active(style); + fn hovered( + &self, + style: Self::Style, + is_selected: bool, + ) -> radio::Appearance { + let active = self.active(style, is_selected); let palette = self.extended_palette(); radio::Appearance { diff --git a/style/src/toggler.rs b/style/src/toggler.rs index 4ee7db46..0acf8e97 100644 --- a/style/src/toggler.rs +++ b/style/src/toggler.rs @@ -2,7 +2,7 @@ use iced_core::Color; /// The appearance of a toggler. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct Appearance { pub background: Color, pub background_border: Option<Color>, diff --git a/wgpu/README.md b/wgpu/README.md index 50440baf..016af179 100644 --- a/wgpu/README.md +++ b/wgpu/README.md @@ -4,9 +4,9 @@ [](https://github.com/iced-rs/iced/blob/master/LICENSE) [](https://discord.gg/3xZJ65GAhd) -`iced_wgpu` is a [`wgpu`] renderer for [`iced_native`]. For now, it is the default renderer of Iced in native platforms. +`iced_wgpu` is a [`wgpu`] renderer for [`iced_native`]. For now, it is the default renderer of Iced on [native platforms]. -[`wgpu`] supports most modern graphics backends: Vulkan, Metal, DX11, and DX12 (OpenGL and WebGL are still WIP). Additionally, it will support the incoming [WebGPU API]. +[`wgpu`] supports most modern graphics backends: Vulkan, Metal, and DX12 (OpenGL and WebGL are still WIP). Additionally, it will support the incoming [WebGPU API]. Currently, `iced_wgpu` supports the following primitives: - Text, which is rendered using [`wgpu_glyph`]. No shaping at all. @@ -22,6 +22,7 @@ Currently, `iced_wgpu` supports the following primitives: [documentation]: https://docs.rs/iced_wgpu [`iced_native`]: ../native [`wgpu`]: https://github.com/gfx-rs/wgpu +[native platforms]: https://github.com/gfx-rs/wgpu#supported-platforms [WebGPU API]: https://gpuweb.github.io/gpuweb/ [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph @@ -39,7 +40,7 @@ you want to learn about a specific release, check out [the release list]. ## Current limitations -The current implementation is quite naive, it uses: +The current implementation is quite naive; it uses: - A different pipeline/shader for each primitive - A very simplistic layer model: every `Clip` primitive will generate new layers |