diff --git a/core/src/mouse/event.rs b/core/src/mouse/event.rs index 2f07b207..321b8399 100644 --- a/core/src/mouse/event.rs +++ b/core/src/mouse/event.rs @@ -1,3 +1,5 @@ +use crate::Point; + use super::Button; /// A mouse event. @@ -16,11 +18,8 @@ pub enum Event { /// The mouse cursor was moved CursorMoved { - /// The X coordinate of the mouse position - x: f32, - - /// The Y coordinate of the mouse position - y: f32, + /// The new position of the mouse cursor + position: Point, }, /// A mouse button was pressed. diff --git a/native/src/event.rs b/native/src/event.rs index 0e86171e..205bb797 100644 --- a/native/src/event.rs +++ b/native/src/event.rs @@ -1,5 +1,8 @@ //! Handle events of a user interface. -use crate::{keyboard, mouse, window}; +use crate::keyboard; +use crate::mouse; +use crate::touch; +use crate::window; /// A user interface event. /// @@ -17,6 +20,9 @@ pub enum Event { /// A window event Window(window::Event), + + /// A touch event + Touch(touch::Event), } /// The status of an [`Event`] after being processed. diff --git a/native/src/lib.rs b/native/src/lib.rs index f9a99c48..0890785b 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -41,6 +41,7 @@ pub mod overlay; pub mod program; pub mod renderer; pub mod subscription; +pub mod touch; pub mod widget; pub mod window; diff --git a/native/src/touch.rs b/native/src/touch.rs new file mode 100644 index 00000000..18120644 --- /dev/null +++ b/native/src/touch.rs @@ -0,0 +1,23 @@ +//! Build touch events. +use crate::Point; + +/// A touch interaction. +#[derive(Debug, Clone, Copy, PartialEq)] +#[allow(missing_docs)] +pub enum Event { + /// A touch interaction was started. + FingerPressed { id: Finger, position: Point }, + + /// An on-going touch interaction was moved. + FingerMoved { id: Finger, position: Point }, + + /// A touch interaction was ended. + FingerLifted { id: Finger, position: Point }, + + /// A touch interaction was canceled. + FingerLost { id: Finger, position: Point }, +} + +/// A unique identifier representing a finger on a touch interaction. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Finger(pub u64); diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index dca20e13..8e2450de 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -4,6 +4,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; +use crate::touch; use crate::{ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Widget, }; @@ -164,7 +165,8 @@ where _clipboard: Option<&dyn Clipboard>, ) -> event::Status { match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { if self.on_press.is_some() { let bounds = layout.bounds(); @@ -175,7 +177,8 @@ where } } } - Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => { + Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerLifted { .. }) => { if let Some(on_press) = self.on_press.clone() { let bounds = layout.bounds(); @@ -190,6 +193,9 @@ where } } } + Event::Touch(touch::Event::FingerLost { .. }) => { + self.state.is_pressed = false; + } _ => {} } diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 81420458..77a82fad 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -6,6 +6,7 @@ use crate::layout; use crate::mouse; use crate::row; use crate::text; +use crate::touch; use crate::{ Align, Clipboard, Element, Hasher, HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text, VerticalAlignment, Widget, @@ -154,7 +155,8 @@ where _clipboard: Option<&dyn Clipboard>, ) -> event::Status { match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { let mouse_over = layout.bounds().contains(cursor_position); if mouse_over { diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 4935569f..69952345 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -4,6 +4,7 @@ use crate::layout; use crate::mouse; use crate::row; use crate::text; +use crate::touch; use crate::{ Align, Clipboard, Element, Hasher, HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text, VerticalAlignment, Widget, @@ -160,7 +161,8 @@ where _clipboard: Option<&dyn Clipboard>, ) -> event::Status { match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { if layout.bounds().contains(cursor_position) { messages.push(self.on_click.clone()); diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index e23ab06a..18cdf169 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -4,6 +4,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::overlay; +use crate::touch; use crate::{ Align, Clipboard, Column, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector, Widget, @@ -229,6 +230,37 @@ where return event::Status::Captured; } + Event::Touch(event) => { + match event { + touch::Event::FingerPressed { .. } => { + self.state.scroll_box_touched_at = + Some(cursor_position); + } + touch::Event::FingerMoved { .. } => { + if let Some(scroll_box_touched_at) = + self.state.scroll_box_touched_at + { + let delta = + cursor_position.y - scroll_box_touched_at.y; + + self.state.scroll( + delta, + bounds, + content_bounds, + ); + + self.state.scroll_box_touched_at = + Some(cursor_position); + } + } + touch::Event::FingerLifted { .. } + | touch::Event::FingerLost { .. } => { + self.state.scroll_box_touched_at = None; + } + } + + return event::Status::Captured; + } _ => {} } } @@ -237,12 +269,15 @@ where match event { Event::Mouse(mouse::Event::ButtonReleased( mouse::Button::Left, - )) => { + )) + | Event::Touch(touch::Event::FingerLifted { .. }) + | Event::Touch(touch::Event::FingerLost { .. }) => { self.state.scroller_grabbed_at = None; return event::Status::Captured; } - Event::Mouse(mouse::Event::CursorMoved { .. }) => { + Event::Mouse(mouse::Event::CursorMoved { .. }) + | Event::Touch(touch::Event::FingerMoved { .. }) => { if let (Some(scrollbar), Some(scroller_grabbed_at)) = (scrollbar, self.state.scroller_grabbed_at) { @@ -264,7 +299,8 @@ where match event { Event::Mouse(mouse::Event::ButtonPressed( mouse::Button::Left, - )) => { + )) + | Event::Touch(touch::Event::FingerPressed { .. }) => { if let Some(scrollbar) = scrollbar { if let Some(scroller_grabbed_at) = scrollbar.grab_scroller(cursor_position) @@ -385,6 +421,7 @@ where #[derive(Debug, Clone, Copy, Default)] pub struct State { scroller_grabbed_at: Option, + scroll_box_touched_at: Option, offset: f32, } @@ -439,6 +476,11 @@ impl State { pub fn is_scroller_grabbed(&self) -> bool { self.scroller_grabbed_at.is_some() } + + /// Returns whether the scroll box is currently touched or not. + pub fn is_scroll_box_touched(&self) -> bool { + self.scroll_box_touched_at.is_some() + } } /// The scrollbar of a [`Scrollable`]. diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index ff39b816..010c6e53 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -4,6 +4,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; +use crate::touch; use crate::{ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; @@ -207,34 +208,35 @@ where }; match event { - Event::Mouse(mouse_event) => match mouse_event { - mouse::Event::ButtonPressed(mouse::Button::Left) => { - if layout.bounds().contains(cursor_position) { - change(); - self.state.is_dragging = true; + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { + if layout.bounds().contains(cursor_position) { + change(); + self.state.is_dragging = true; - return event::Status::Captured; - } + return event::Status::Captured; } - mouse::Event::ButtonReleased(mouse::Button::Left) => { - if self.state.is_dragging { - if let Some(on_release) = self.on_release.clone() { - messages.push(on_release); - } - self.state.is_dragging = false; + } + Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerLifted { .. }) + | Event::Touch(touch::Event::FingerLost { .. }) => { + if self.state.is_dragging { + if let Some(on_release) = self.on_release.clone() { + messages.push(on_release); + } + self.state.is_dragging = false; - return event::Status::Captured; - } + return event::Status::Captured; } - mouse::Event::CursorMoved { .. } => { - if self.state.is_dragging { - change(); + } + Event::Mouse(mouse::Event::CursorMoved { .. }) + | Event::Touch(touch::Event::FingerMoved { .. }) => { + if self.state.is_dragging { + change(); - return event::Status::Captured; - } + return event::Status::Captured; } - _ => {} - }, + } _ => {} } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 3e637e97..2fd9cec1 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -16,6 +16,7 @@ use crate::keyboard; use crate::layout; use crate::mouse::{self, click}; use crate::text; +use crate::touch; use crate::{ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; @@ -247,7 +248,8 @@ where clipboard: Option<&dyn Clipboard>, ) -> event::Status { match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { let is_clicked = layout.bounds().contains(cursor_position); self.state.is_focused = is_clicked; @@ -318,13 +320,16 @@ where return event::Status::Captured; } } - Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => { + Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerLifted { .. }) + | Event::Touch(touch::Event::FingerLost { .. }) => { self.state.is_dragging = false; } - Event::Mouse(mouse::Event::CursorMoved { x, .. }) => { + Event::Mouse(mouse::Event::CursorMoved { position }) + | Event::Touch(touch::Event::FingerMoved { position, .. }) => { if self.state.is_dragging { let text_layout = layout.children().next().unwrap(); - let target = x - text_layout.bounds().x; + let target = position.x - text_layout.bounds().x; if target > 0.0 { let value = if self.is_secure { diff --git a/winit/src/application/state.rs b/winit/src/application/state.rs index 58bc7ed6..46297370 100644 --- a/winit/src/application/state.rs +++ b/winit/src/application/state.rs @@ -2,7 +2,7 @@ use crate::conversion; use crate::{Application, Color, Debug, Mode, Point, Size, Viewport}; use std::marker::PhantomData; -use winit::event::WindowEvent; +use winit::event::{Touch, WindowEvent}; use winit::window::Window; /// The state of a windowed [`Application`]. @@ -128,7 +128,10 @@ impl State { self.viewport_version = self.viewport_version.wrapping_add(1); } - WindowEvent::CursorMoved { position, .. } => { + WindowEvent::CursorMoved { position, .. } + | WindowEvent::Touch(Touch { + location: position, .. + }) => { self.cursor_position = *position; } WindowEvent::CursorLeft { .. } => { diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 138bc64d..f073c474 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -2,10 +2,11 @@ //! //! [`winit`]: https://github.com/rust-windowing/winit //! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native -use crate::{ - keyboard::{self, KeyCode, Modifiers}, - mouse, window, Event, Mode, Point, -}; +use crate::keyboard; +use crate::mouse; +use crate::touch; +use crate::window; +use crate::{Event, Mode, Point}; /// Converts a winit window event into an iced event. pub fn window_event( @@ -36,8 +37,7 @@ pub fn window_event( let position = position.to_logical::(scale_factor); Some(Event::Mouse(mouse::Event::CursorMoved { - x: position.x as f32, - y: position.y as f32, + position: Point::new(position.x as f32, position.y as f32), })) } WindowEvent::CursorEntered { .. } => { @@ -118,6 +118,9 @@ pub fn window_event( WindowEvent::HoveredFileCancelled => { Some(Event::Window(window::Event::FilesHoveredLeft)) } + WindowEvent::Touch(touch) => { + Some(Event::Touch(touch_event(*touch, scale_factor))) + } _ => None, } } @@ -181,8 +184,10 @@ pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button { /// /// [`winit`]: https://github.com/rust-windowing/winit /// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native -pub fn modifiers(modifiers: winit::event::ModifiersState) -> Modifiers { - Modifiers { +pub fn modifiers( + modifiers: winit::event::ModifiersState, +) -> keyboard::Modifiers { + keyboard::Modifiers { shift: modifiers.shift(), control: modifiers.ctrl(), alt: modifiers.alt(), @@ -200,11 +205,46 @@ pub fn cursor_position( Point::new(logical_position.x, logical_position.y) } +/// Converts a `Touch` from [`winit`] to an [`iced_native`] touch event. +/// +/// [`winit`]: https://github.com/rust-windowing/winit +/// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native +pub fn touch_event( + touch: winit::event::Touch, + scale_factor: f64, +) -> touch::Event { + let id = touch::Finger(touch.id); + let position = { + let location = touch.location.to_logical::(scale_factor); + + Point::new(location.x as f32, location.y as f32) + }; + + match touch.phase { + winit::event::TouchPhase::Started => { + touch::Event::FingerPressed { id, position } + } + winit::event::TouchPhase::Moved => { + touch::Event::FingerMoved { id, position } + } + winit::event::TouchPhase::Ended => { + touch::Event::FingerLifted { id, position } + } + winit::event::TouchPhase::Cancelled => { + touch::Event::FingerLost { id, position } + } + } +} + /// Converts a `VirtualKeyCode` from [`winit`] to an [`iced_native`] key code. /// /// [`winit`]: https://github.com/rust-windowing/winit /// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native -pub fn key_code(virtual_keycode: winit::event::VirtualKeyCode) -> KeyCode { +pub fn key_code( + virtual_keycode: winit::event::VirtualKeyCode, +) -> keyboard::KeyCode { + use keyboard::KeyCode; + match virtual_keycode { winit::event::VirtualKeyCode::Key1 => KeyCode::Key1, winit::event::VirtualKeyCode::Key2 => KeyCode::Key2,