Compare commits

...

6 Commits

Author SHA1 Message Date
Kirill Chibisov
4b3c0655bf Winit version 0.30.0 2024-04-27 19:00:38 +04:00
Joshua Pedrick
0812adc983 Add UIGestureRecognizerDelegate and PanGestureRecogniser (#3597)
- Allow all gestures simultaneously recognized.
- Add PanGestureRecogniser with min/max number of touches.
- Fix sending delta values relative to Update instead to match macOS.
- Fix rotation gesture units from iOS to be in degrees instead of radians.

Co-authored-by: Mads Marquart <mads@marquart.dk>
2024-04-27 19:00:38 +04:00
Mads Marquart
cd6ec19300 Don't set the background color when initializing with transparency (#3657)
Setting the background color changes how the window title bar appears,
which is something that the application should customize itself if it
wants this behaviour (and also, it wasn't set when calling
`set_transparent`, so the behaviour wasn't consistent).
2024-04-27 19:00:38 +04:00
growfrow
61bd8172bd chore: fix some typos in comments (#3635)
Signed-off-by: growfrow <growfrow@outlook.com>
2024-04-27 19:00:38 +04:00
Kirill Chibisov
c04c113e7e chore: ensure that .cargo config is not published
Just in case, so the correct changelog will be rendered when pulling
the crate from the crates.io as archive and trying to build it.
2024-04-27 19:00:38 +04:00
Marijn Suijten
ce32a3024e android: bump to ndk 0.9.0 and android-activity 0.6.0 2024-04-27 19:00:38 +04:00
20 changed files with 530 additions and 264 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "winit" name = "winit"
version = "0.29.15" version = "0.30.0"
authors = [ authors = [
"The winit contributors", "The winit contributors",
"Pierre Krieger <pierre.krieger1708@gmail.com>", "Pierre Krieger <pierre.krieger1708@gmail.com>",
@@ -14,6 +14,7 @@ rust-version.workspace = true
repository.workspace = true repository.workspace = true
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true
exclude = ["/.cargo"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = [ features = [
@@ -102,9 +103,8 @@ softbuffer = { version = "0.4.0", default-features = false, features = [
] } ] }
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.5.0" android-activity = "0.6.0"
ndk = { version = "0.8.0", default-features = false } ndk = { version = "0.9.0", default-features = false }
ndk-sys = "0.5.0"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
core-foundation = "0.9.3" core-foundation = "0.9.3"
@@ -137,7 +137,6 @@ features = [
"NSApplication", "NSApplication",
"NSBitmapImageRep", "NSBitmapImageRep",
"NSButton", "NSButton",
"NSColor",
"NSControl", "NSControl",
"NSCursor", "NSCursor",
"NSDragging", "NSDragging",

View File

@@ -8,7 +8,7 @@
```toml ```toml
[dependencies] [dependencies]
winit = "0.29.15" winit = "0.30.0"
``` ```
## [Documentation](https://docs.rs/winit) ## [Documentation](https://docs.rs/winit)

View File

@@ -159,6 +159,7 @@ impl Application {
window.recognize_doubletap_gesture(true); window.recognize_doubletap_gesture(true);
window.recognize_pinch_gesture(true); window.recognize_pinch_gesture(true);
window.recognize_rotation_gesture(true); window.recognize_rotation_gesture(true);
window.recognize_pan_gesture(true, 2, 2);
} }
let window_state = WindowState::new(self, window)?; let window_state = WindowState::new(self, window)?;
@@ -428,6 +429,11 @@ impl ApplicationHandler<UserEvent> for Application {
info!("Rotated clockwise {delta:.5} (now: {rotated:.5})"); info!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
} }
}, },
WindowEvent::PanGesture { delta, phase, .. } => {
window.panned.x += delta.x;
window.panned.y += delta.y;
info!("Panned ({delta:?})) (now: {:?}), {phase:?}", window.panned);
},
WindowEvent::DoubleTapGesture { .. } => { WindowEvent::DoubleTapGesture { .. } => {
info!("Smart zoom"); info!("Smart zoom");
}, },
@@ -502,6 +508,8 @@ struct WindowState {
zoom: f64, zoom: f64,
/// The amount of rotation of the window. /// The amount of rotation of the window.
rotated: f32, rotated: f32,
/// The amount of pan of the window.
panned: PhysicalPosition<f32>,
#[cfg(macos_platform)] #[cfg(macos_platform)]
option_as_alt: OptionAsAlt, option_as_alt: OptionAsAlt,
@@ -547,6 +555,7 @@ impl WindowState {
modifiers: Default::default(), modifiers: Default::default(),
occluded: Default::default(), occluded: Default::default(),
rotated: Default::default(), rotated: Default::default(),
panned: Default::default(),
zoom: Default::default(), zoom: Default::default(),
}; };

View File

@@ -5,7 +5,10 @@
// Put the current entry at the top of this page, for discoverability. // Put the current entry at the top of this page, for discoverability.
// See `.cargo/config.toml` for details about `unreleased_changelogs`. // See `.cargo/config.toml` for details about `unreleased_changelogs`.
#![cfg_attr(unreleased_changelogs, doc = include_str!("unreleased.md"))] #![cfg_attr(unreleased_changelogs, doc = include_str!("unreleased.md"))]
#![cfg_attr(not(unreleased_changelogs), doc = include_str!("v0.29.md"))] #![cfg_attr(not(unreleased_changelogs), doc = include_str!("v0.30.md"))]
#[doc = include_str!("v0.30.md")]
pub mod v0_30 {}
#[doc = include_str!("v0.29.md")] #[doc = include_str!("v0.29.md")]
pub mod v0_29 {} pub mod v0_29 {}

View File

@@ -39,222 +39,3 @@ The migration guide could reference other migration examples in the current
changelog entry. changelog entry.
## Unreleased ## Unreleased
### Added
- Add `OwnedDisplayHandle` type for allowing safe display handle usage outside of
trivial cases.
- Add `ApplicationHandler<T>` trait which mimics `Event<T>`.
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a
`CursorIcon` or `CustomCursor`.
- Add `Sync` implementation for `EventLoopProxy<T: Send>`.
- Add `Window::default_attributes` to get default `WindowAttributes`.
- Add `EventLoop::builder` to get `EventLoopBuilder` without export.
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated
cursors from other `CustomCursor`s.
- Add `{Active,}EventLoop::create_custom_cursor` to load custom cursor image sources.
- Add `ActiveEventLoop::create_window` and `EventLoop::create_window`.
- Add `CustomCursor` which could be set via `Window::set_cursor`, implemented on
Windows, macOS, X11, Wayland, and Web.
- On Web, add to toggle calling `Event.preventDefault()` on `Window`.
- On iOS, add `PinchGesture`, `DoubleTapGesture`, and `RotationGesture`
- On macOS, add services menu.
- On Windows, add `with_title_text_color`, and `with_corner_preference` on
`WindowAttributesExtWindows`.
- On Windows, implement resize increments.
- On Windows, add `AnyThread` API to access window handle off the main thread.
### Changed
- Bump MSRV from `1.65` to `1.70`.
- On Wayland, bump `sctk-adwaita` to `0.9.0`, which changed system library
crates. This change is a **cascading breaking change**, you must do breaking
change as well, even if you don't expose winit.
- Rename `TouchpadMagnify` to `PinchGesture`.
- Rename `SmartMagnify` to `DoubleTapGesture`.
- Rename `TouchpadRotate` to `RotationGesture`.
- Rename `EventLoopWindowTarget` to `ActiveEventLoop`.
- Rename `platform::x11::XWindowType` to `platform::x11::WindowType`.
- Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold
static data.
- Make `Debug` formatting of `WindowId` more concise.
- Move `dpi` types to its own crate, and re-export it from the root crate.
- Replace `log` with `tracing`, use `log` feature on `tracing` to restore old
behavior.
- `EventLoop::with_user_event` now returns `EventLoopBuilder`.
- On Web, return `HandleError::Unavailable` when a window handle is not available.
- On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
- On Web, remove queuing fullscreen request in absence of transient activation.
- On iOS, return `HandleError::Unavailable` when a window handle is not available.
- On macOS, return `HandleError::Unavailable` when a window handle is not available.
- On Windows, remove `WS_CAPTION`, `WS_BORDER`, and `WS_EX_WINDOWEDGE` styles
for child windows without decorations.
### Deprecated
- Deprecate `EventLoop::run`, use `EventLoop::run_app`.
- Deprecate `EventLoopExtRunOnDemand::run_on_demand`, use `EventLoop::run_app_on_demand`.
- Deprecate `EventLoopExtPumpEvents::pump_events`, use `EventLoopExtPumpEvents::pump_app_events`.
The new `app` APIs accept a newly added `ApplicationHandler<T>` instead of
`Fn`. The semantics are mostly the same, given that the capture list of the
closure is your new `State`. Consider the following code:
```rust,no_run
use winit::event::Event;
use winit::event_loop::EventLoop;
use winit::window::Window;
struct MyUserEvent;
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
let window = event_loop.create_window(Window::default_attributes()).unwrap();
let mut counter = 0;
let _ = event_loop.run(move |event, event_loop| {
match event {
Event::AboutToWait => {
window.request_redraw();
counter += 1;
}
Event::WindowEvent { window_id, event } => {
// Handle window event.
}
Event::UserEvent(event) => {
// Handle user event.
}
Event::DeviceEvent { device_id, event } => {
// Handle device event.
}
_ => (),
}
});
```
To migrate this code, you should move all the captured values into some
newtype `State` and implement `ApplicationHandler` for this type. Finally,
we move particular `match event` arms into methods on `ApplicationHandler`,
for example:
```rust,no_run
use winit::application::ApplicationHandler;
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
use winit::event_loop::{EventLoop, ActiveEventLoop};
use winit::window::{Window, WindowId};
struct MyUserEvent;
struct State {
window: Window,
counter: i32,
}
impl ApplicationHandler<MyUserEvent> for State {
fn user_event(&mut self, event_loop: &ActiveEventLoop, user_event: MyUserEvent) {
// Handle user event.
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
// Your application got resumed.
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
// Handle window event.
}
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
// Handle device event.
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
self.window.request_redraw();
self.counter += 1;
}
}
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
#[allow(deprecated)]
let window = event_loop.create_window(Window::default_attributes()).unwrap();
let mut state = State { window, counter: 0 };
let _ = event_loop.run_app(&mut state);
```
Please submit your feedback after migrating in [this issue](https://github.com/rust-windowing/winit/issues/3626).
- Deprecate `Window::set_cursor_icon`, use `Window::set_cursor`.
### Removed
- Remove `Window::new`, use `ActiveEventLoop::create_window` instead.
You now have to create your windows inside the actively running event loop
(usually the `new_events(cause: StartCause::Init)` or `resumed()` events),
and can no longer do it before the application has properly launched.
This change is done to fix many long-standing issues on iOS and macOS, and
will improve things on Wayland once fully implemented.
To ease migration, we provide the deprecated `EventLoop::create_window` that
will allow you to bypass this restriction in this release.
Using the migration example from above, you can change your code as follows:
```rust,no_run
use winit::application::ApplicationHandler;
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
use winit::event_loop::{EventLoop, ActiveEventLoop};
use winit::window::{Window, WindowId};
#[derive(Default)]
struct State {
// Use an `Option` to allow the window to not be available until the
// application is properly running.
window: Option<Window>,
counter: i32,
}
impl ApplicationHandler for State {
// This is a common indicator that you can create a window.
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
// `unwrap` is fine, the window will always be available when
// receiving a window event.
let window = self.window.as_ref().unwrap();
// Handle window event.
}
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
// Handle window event.
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
self.counter += 1;
}
}
}
let event_loop = EventLoop::new().unwrap();
let mut state = State::default();
let _ = event_loop.run_app(&mut state);
```
- Remove `Deref` implementation for `EventLoop` that gave `EventLoopWindowTarget`.
- Remove `WindowBuilder` in favor of `WindowAttributes`.
- Remove Generic parameter `T` from `ActiveEventLoop`.
- Remove `EventLoopBuilder::with_user_event`, use `EventLoop::with_user_event`.
- Remove Redundant `EventLoopError::AlreadyRunning`.
- Remove `WindowAttributes::fullscreen` and expose as field directly.
- On X11, remove `platform::x11::XNotSupported` export.
### Fixed
- On Web, fix setting cursor icon overriding cursor visibility.
- On Windows, fix cursor not confined to center of window when grabbed and hidden.
- On macOS, fix sequence of mouse events being out of order when dragging on the trackpad.
- On Wayland, fix decoration glitch on close with some compositors.
- On Android, fix a regression introduced in #2748 to allow volume key events to be received again.
- On Windows, don't return a valid window handle outside of the GUI thread.

224
src/changelog/v0.30.md Normal file
View File

@@ -0,0 +1,224 @@
## 0.30.0
### Added
- Add `OwnedDisplayHandle` type for allowing safe display handle usage outside of
trivial cases.
- Add `ApplicationHandler<T>` trait which mimics `Event<T>`.
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a
`CursorIcon` or `CustomCursor`.
- Add `Sync` implementation for `EventLoopProxy<T: Send>`.
- Add `Window::default_attributes` to get default `WindowAttributes`.
- Add `EventLoop::builder` to get `EventLoopBuilder` without export.
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated
cursors from other `CustomCursor`s.
- Add `{Active,}EventLoop::create_custom_cursor` to load custom cursor image sources.
- Add `ActiveEventLoop::create_window` and `EventLoop::create_window`.
- Add `CustomCursor` which could be set via `Window::set_cursor`, implemented on
Windows, macOS, X11, Wayland, and Web.
- On Web, add to toggle calling `Event.preventDefault()` on `Window`.
- On iOS, add `PinchGesture`, `DoubleTapGesture`, `PanGesture` and `RotationGesture`.
- on iOS, use `UIGestureRecognizerDelegate` for fine grained control of gesture recognizers.
- On macOS, add services menu.
- On Windows, add `with_title_text_color`, and `with_corner_preference` on
`WindowAttributesExtWindows`.
- On Windows, implement resize increments.
- On Windows, add `AnyThread` API to access window handle off the main thread.
### Changed
- Bump MSRV from `1.65` to `1.70`.
- On Wayland, bump `sctk-adwaita` to `0.9.0`, which changed system library
crates. This change is a **cascading breaking change**, you must do breaking
change as well, even if you don't expose winit.
- Rename `TouchpadMagnify` to `PinchGesture`.
- Rename `SmartMagnify` to `DoubleTapGesture`.
- Rename `TouchpadRotate` to `RotationGesture`.
- Rename `EventLoopWindowTarget` to `ActiveEventLoop`.
- Rename `platform::x11::XWindowType` to `platform::x11::WindowType`.
- Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold
static data.
- Make `Debug` formatting of `WindowId` more concise.
- Move `dpi` types to its own crate, and re-export it from the root crate.
- Replace `log` with `tracing`, use `log` feature on `tracing` to restore old
behavior.
- `EventLoop::with_user_event` now returns `EventLoopBuilder`.
- On Web, return `HandleError::Unavailable` when a window handle is not available.
- On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
- On Web, remove queuing fullscreen request in absence of transient activation.
- On iOS, return `HandleError::Unavailable` when a window handle is not available.
- On macOS, return `HandleError::Unavailable` when a window handle is not available.
- On Windows, remove `WS_CAPTION`, `WS_BORDER`, and `WS_EX_WINDOWEDGE` styles
for child windows without decorations.
- On Android, bump `ndk` to `0.9.0` and `android-activity` to `0.6.0`,
and remove unused direct dependency on `ndk-sys`.
### Deprecated
- Deprecate `EventLoop::run`, use `EventLoop::run_app`.
- Deprecate `EventLoopExtRunOnDemand::run_on_demand`, use `EventLoop::run_app_on_demand`.
- Deprecate `EventLoopExtPumpEvents::pump_events`, use `EventLoopExtPumpEvents::pump_app_events`.
The new `app` APIs accept a newly added `ApplicationHandler<T>` instead of
`Fn`. The semantics are mostly the same, given that the capture list of the
closure is your new `State`. Consider the following code:
```rust,no_run
use winit::event::Event;
use winit::event_loop::EventLoop;
use winit::window::Window;
struct MyUserEvent;
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
let window = event_loop.create_window(Window::default_attributes()).unwrap();
let mut counter = 0;
let _ = event_loop.run(move |event, event_loop| {
match event {
Event::AboutToWait => {
window.request_redraw();
counter += 1;
}
Event::WindowEvent { window_id, event } => {
// Handle window event.
}
Event::UserEvent(event) => {
// Handle user event.
}
Event::DeviceEvent { device_id, event } => {
// Handle device event.
}
_ => (),
}
});
```
To migrate this code, you should move all the captured values into some
newtype `State` and implement `ApplicationHandler` for this type. Finally,
we move particular `match event` arms into methods on `ApplicationHandler`,
for example:
```rust,no_run
use winit::application::ApplicationHandler;
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
use winit::event_loop::{EventLoop, ActiveEventLoop};
use winit::window::{Window, WindowId};
struct MyUserEvent;
struct State {
window: Window,
counter: i32,
}
impl ApplicationHandler<MyUserEvent> for State {
fn user_event(&mut self, event_loop: &ActiveEventLoop, user_event: MyUserEvent) {
// Handle user event.
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
// Your application got resumed.
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
// Handle window event.
}
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
// Handle device event.
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
self.window.request_redraw();
self.counter += 1;
}
}
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
#[allow(deprecated)]
let window = event_loop.create_window(Window::default_attributes()).unwrap();
let mut state = State { window, counter: 0 };
let _ = event_loop.run_app(&mut state);
```
Please submit your feedback after migrating in [this issue](https://github.com/rust-windowing/winit/issues/3626).
- Deprecate `Window::set_cursor_icon`, use `Window::set_cursor`.
### Removed
- Remove `Window::new`, use `ActiveEventLoop::create_window` instead.
You now have to create your windows inside the actively running event loop
(usually the `new_events(cause: StartCause::Init)` or `resumed()` events),
and can no longer do it before the application has properly launched.
This change is done to fix many long-standing issues on iOS and macOS, and
will improve things on Wayland once fully implemented.
To ease migration, we provide the deprecated `EventLoop::create_window` that
will allow you to bypass this restriction in this release.
Using the migration example from above, you can change your code as follows:
```rust,no_run
use winit::application::ApplicationHandler;
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
use winit::event_loop::{EventLoop, ActiveEventLoop};
use winit::window::{Window, WindowId};
#[derive(Default)]
struct State {
// Use an `Option` to allow the window to not be available until the
// application is properly running.
window: Option<Window>,
counter: i32,
}
impl ApplicationHandler for State {
// This is a common indicator that you can create a window.
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
// `unwrap` is fine, the window will always be available when
// receiving a window event.
let window = self.window.as_ref().unwrap();
// Handle window event.
}
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
// Handle window event.
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
self.counter += 1;
}
}
}
let event_loop = EventLoop::new().unwrap();
let mut state = State::default();
let _ = event_loop.run_app(&mut state);
```
- Remove `Deref` implementation for `EventLoop` that gave `EventLoopWindowTarget`.
- Remove `WindowBuilder` in favor of `WindowAttributes`.
- Remove Generic parameter `T` from `ActiveEventLoop`.
- Remove `EventLoopBuilder::with_user_event`, use `EventLoop::with_user_event`.
- Remove Redundant `EventLoopError::AlreadyRunning`.
- Remove `WindowAttributes::fullscreen` and expose as field directly.
- On X11, remove `platform::x11::XNotSupported` export.
### Fixed
- On Web, fix setting cursor icon overriding cursor visibility.
- On Windows, fix cursor not confined to center of window when grabbed and hidden.
- On macOS, fix sequence of mouse events being out of order when dragging on the trackpad.
- On Wayland, fix decoration glitch on close with some compositors.
- On Android, fix a regression introduced in #2748 to allow volume key events to be received again.
- On Windows, don't return a valid window handle outside of the GUI thread.
- On macOS, don't set the background color when initializing a window with transparency.

View File

@@ -293,6 +293,19 @@ pub enum WindowEvent {
phase: TouchPhase, phase: TouchPhase,
}, },
/// N-finger pan gesture
///
/// ## Platform-specific
///
/// - Only available on **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
PanGesture {
device_id: DeviceId,
/// Change in pixels of pan gesture from last update.
delta: PhysicalPosition<f32>,
phase: TouchPhase,
},
/// Double tap gesture. /// Double tap gesture.
/// ///
/// On a Mac, smart magnification is triggered by a double tap with two fingers /// On a Mac, smart magnification is triggered by a double tap with two fingers
@@ -322,7 +335,12 @@ pub enum WindowEvent {
/// ///
/// - Only available on **macOS** and **iOS**. /// - Only available on **macOS** and **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed. /// - On iOS, not recognized by default. It must be enabled when needed.
RotationGesture { device_id: DeviceId, delta: f32, phase: TouchPhase }, RotationGesture {
device_id: DeviceId,
/// change in rotation in degrees
delta: f32,
phase: TouchPhase,
},
/// Touchpad pressure event. /// Touchpad pressure event.
/// ///
@@ -993,6 +1011,7 @@ impl PartialEq for InnerSizeWriter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::dpi::PhysicalPosition;
use crate::event; use crate::event;
use std::collections::{BTreeSet, HashSet}; use std::collections::{BTreeSet, HashSet};
@@ -1055,6 +1074,11 @@ mod tests {
delta: 0.0, delta: 0.0,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
}); });
with_window_event(PanGesture {
device_id: did,
delta: PhysicalPosition::<f32>::new(0.0, 0.0),
phase: event::TouchPhase::Started,
});
with_window_event(TouchpadPressure { device_id: did, pressure: 0.0, stage: 0 }); with_window_event(TouchpadPressure { device_id: did, pressure: 0.0, stage: 0 });
with_window_event(AxisMotion { device_id: did, axis: 0, value: 0.0 }); with_window_event(AxisMotion { device_id: did, axis: 0, value: 0.0 });
with_window_event(Touch(event::Touch { with_window_event(Touch(event::Touch {

View File

@@ -19,6 +19,7 @@
//! //!
//! | winit | ndk-glue | //! | winit | ndk-glue |
//! | :---: | :--------------------------: | //! | :---: | :--------------------------: |
//! | 0.30 | `android-activity = "0.6"` |
//! | 0.29 | `android-activity = "0.5"` | //! | 0.29 | `android-activity = "0.5"` |
//! | 0.28 | `android-activity = "0.4"` | //! | 0.28 | `android-activity = "0.4"` |
//! | 0.27 | `ndk-glue = "0.7"` | //! | 0.27 | `ndk-glue = "0.7"` |
@@ -61,7 +62,7 @@
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building //! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building
//! with `cargo apk`, then the minimal changes would be: //! with `cargo apk`, then the minimal changes would be:
//! 1. Remove `ndk-glue` from your `Cargo.toml` //! 1. Remove `ndk-glue` from your `Cargo.toml`
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.15", //! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.0",
//! features = [ "android-native-activity" ] }` //! features = [ "android-native-activity" ] }`
//! 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc //! 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc
//! macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize //! macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize

View File

@@ -155,6 +155,21 @@ pub trait WindowExtIOS {
/// The default is to not recognize gestures. /// The default is to not recognize gestures.
fn recognize_pinch_gesture(&self, should_recognize: bool); fn recognize_pinch_gesture(&self, should_recognize: bool);
/// Sets whether the [`Window`] should recognize pan gestures.
///
/// The default is to not recognize gestures.
/// Installs [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer) onto view
///
/// Set the minimum number of touches required: [`minimumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-minimumnumberoftouches)
///
/// Set the maximum number of touches recognized: [`maximumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-maximumnumberoftouches)
fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
);
/// Sets whether the [`Window`] should recognize double tap gestures. /// Sets whether the [`Window`] should recognize double tap gestures.
/// ///
/// The default is to not recognize gestures. /// The default is to not recognize gestures.
@@ -204,6 +219,22 @@ impl WindowExtIOS for Window {
self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize)); self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
} }
#[inline]
fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
) {
self.window.maybe_queue_on_main(move |w| {
w.recognize_pan_gesture(
should_recognize,
minimum_number_of_touches,
maximum_number_of_touches,
)
});
}
#[inline] #[inline]
fn recognize_doubletap_gesture(&self, should_recognize: bool) { fn recognize_doubletap_gesture(&self, should_recognize: bool) {
self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize)); self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));

View File

@@ -1,9 +1,13 @@
use objc2::encode::{Encode, Encoding}; use objc2::encode::{Encode, Encoding};
use objc2::{extern_class, extern_methods, mutability, ClassType}; use objc2::rc::Id;
use objc2_foundation::{CGFloat, NSInteger, NSObject, NSUInteger}; use objc2::runtime::ProtocolObject;
use objc2::{extern_class, extern_methods, extern_protocol, mutability, ClassType, ProtocolType};
use objc2_foundation::{CGFloat, CGPoint, NSInteger, NSObject, NSObjectProtocol, NSUInteger};
use super::UIView;
// https://developer.apple.com/documentation/uikit/uigesturerecognizer
extern_class!( extern_class!(
/// [`UIGestureRecognizer`](https://developer.apple.com/documentation/uikit/uigesturerecognizer)
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIGestureRecognizer; pub(crate) struct UIGestureRecognizer;
@@ -17,6 +21,14 @@ extern_methods!(
unsafe impl UIGestureRecognizer { unsafe impl UIGestureRecognizer {
#[method(state)] #[method(state)]
pub fn state(&self) -> UIGestureRecognizerState; pub fn state(&self) -> UIGestureRecognizerState;
/// [`delegate`](https://developer.apple.com/documentation/uikit/uigesturerecognizer/1624207-delegate?language=objc)
/// @property(nullable, nonatomic, weak) id<UIGestureRecognizerDelegate> delegate;
#[method(setDelegate:)]
pub fn setDelegate(&self, delegate: &ProtocolObject<dyn UIGestureRecognizerDelegate>);
#[method_id(delegate)]
pub fn delegate(&self) -> Id<ProtocolObject<dyn UIGestureRecognizerDelegate>>;
} }
); );
@@ -24,7 +36,7 @@ unsafe impl Encode for UIGestureRecognizer {
const ENCODING: Encoding = Encoding::Object; const ENCODING: Encoding = Encoding::Object;
} }
// https://developer.apple.com/documentation/uikit/uigesturerecognizer/state // [`UIGestureRecognizerState`](https://developer.apple.com/documentation/uikit/uigesturerecognizer/state)
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIGestureRecognizerState(NSInteger); pub struct UIGestureRecognizerState(NSInteger);
@@ -43,7 +55,7 @@ impl UIGestureRecognizerState {
pub const Failed: Self = Self(5); pub const Failed: Self = Self(5);
} }
// https://developer.apple.com/documentation/uikit/uipinchgesturerecognizer // [`UIPinchGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipinchgesturerecognizer)
extern_class!( extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIPinchGestureRecognizer; pub(crate) struct UIPinchGestureRecognizer;
@@ -68,8 +80,8 @@ unsafe impl Encode for UIPinchGestureRecognizer {
const ENCODING: Encoding = Encoding::Object; const ENCODING: Encoding = Encoding::Object;
} }
// https://developer.apple.com/documentation/uikit/uirotationgesturerecognizer
extern_class!( extern_class!(
/// [`UIRotationGestureRecognizer`](https://developer.apple.com/documentation/uikit/uirotationgesturerecognizer)
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIRotationGestureRecognizer; pub(crate) struct UIRotationGestureRecognizer;
@@ -93,8 +105,8 @@ unsafe impl Encode for UIRotationGestureRecognizer {
const ENCODING: Encoding = Encoding::Object; const ENCODING: Encoding = Encoding::Object;
} }
// https://developer.apple.com/documentation/uikit/uitapgesturerecognizer
extern_class!( extern_class!(
/// [`UITapGestureRecognizer`](https://developer.apple.com/documentation/uikit/uitapgesturerecognizer)
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UITapGestureRecognizer; pub(crate) struct UITapGestureRecognizer;
@@ -117,3 +129,48 @@ extern_methods!(
unsafe impl Encode for UITapGestureRecognizer { unsafe impl Encode for UITapGestureRecognizer {
const ENCODING: Encoding = Encoding::Object; const ENCODING: Encoding = Encoding::Object;
} }
extern_class!(
/// [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer)
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIPanGestureRecognizer;
unsafe impl ClassType for UIPanGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIPanGestureRecognizer {
#[method(translationInView:)]
pub fn translationInView(&self, view: &UIView) -> CGPoint;
#[method(setTranslation:inView:)]
pub fn setTranslationInView(&self, translation: CGPoint, view: &UIView);
#[method(velocityInView:)]
pub fn velocityInView(&self, view: &UIView) -> CGPoint;
#[method(setMinimumNumberOfTouches:)]
pub fn setMinimumNumberOfTouches(&self, minimum_number_of_touches: NSUInteger);
#[method(minimumNumberOfTouches)]
pub fn minimumNumberOfTouches(&self) -> NSUInteger;
#[method(setMaximumNumberOfTouches:)]
pub fn setMaximumNumberOfTouches(&self, maximum_number_of_touches: NSUInteger);
#[method(maximumNumberOfTouches)]
pub fn maximumNumberOfTouches(&self) -> NSUInteger;
}
);
extern_protocol!(
/// (@protocol UIGestureRecognizerDelegate)[https://developer.apple.com/documentation/uikit/uigesturerecognizerdelegate?language=objc]
pub(crate) unsafe trait UIGestureRecognizerDelegate: NSObjectProtocol {}
unsafe impl ProtocolType for dyn UIGestureRecognizerDelegate {
const NAME: &'static str = "UIGestureRecognizerDelegate";
}
);

View File

@@ -27,8 +27,9 @@ pub(crate) use self::device::{UIDevice, UIUserInterfaceIdiom};
pub(crate) use self::event::UIEvent; pub(crate) use self::event::UIEvent;
pub(crate) use self::geometry::UIRectEdge; pub(crate) use self::geometry::UIRectEdge;
pub(crate) use self::gesture_recognizer::{ pub(crate) use self::gesture_recognizer::{
UIGestureRecognizer, UIGestureRecognizerState, UIPinchGestureRecognizer, UIGestureRecognizer, UIGestureRecognizerDelegate, UIGestureRecognizerState,
UIRotationGestureRecognizer, UITapGestureRecognizer, UIPanGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer,
UITapGestureRecognizer,
}; };
pub(crate) use self::responder::UIResponder; pub(crate) use self::responder::UIResponder;
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation}; pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};

View File

@@ -1,18 +1,19 @@
#![allow(clippy::unnecessary_cast)] #![allow(clippy::unnecessary_cast)]
use std::cell::RefCell; use std::cell::{Cell, RefCell};
use objc2::rc::Id; use objc2::rc::Id;
use objc2::runtime::AnyClass; use objc2::runtime::{AnyClass, NSObjectProtocol, ProtocolObject};
use objc2::{ use objc2::{
declare_class, extern_methods, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass, declare_class, extern_methods, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
}; };
use objc2_foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSSet}; use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet};
use super::app_state::{self, EventWrapper}; use super::app_state::{self, EventWrapper};
use super::uikit::{ use super::uikit::{
UIEvent, UIForceTouchCapability, UIGestureRecognizerState, UIPinchGestureRecognizer, UIEvent, UIForceTouchCapability, UIGestureRecognizer, UIGestureRecognizerDelegate,
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITouch, UITouchPhase, UIGestureRecognizerState, UIPanGestureRecognizer, UIPinchGestureRecognizer, UIResponder,
UITouchType, UITraitCollection, UIView, UIRotationGestureRecognizer, UITapGestureRecognizer, UITouch, UITouchPhase, UITouchType,
UITraitCollection, UIView,
}; };
use super::window::WinitUIWindow; use super::window::WinitUIWindow;
use crate::dpi::PhysicalPosition; use crate::dpi::PhysicalPosition;
@@ -24,6 +25,12 @@ pub struct WinitViewState {
pinch_gesture_recognizer: RefCell<Option<Id<UIPinchGestureRecognizer>>>, pinch_gesture_recognizer: RefCell<Option<Id<UIPinchGestureRecognizer>>>,
doubletap_gesture_recognizer: RefCell<Option<Id<UITapGestureRecognizer>>>, doubletap_gesture_recognizer: RefCell<Option<Id<UITapGestureRecognizer>>>,
rotation_gesture_recognizer: RefCell<Option<Id<UIRotationGestureRecognizer>>>, rotation_gesture_recognizer: RefCell<Option<Id<UIRotationGestureRecognizer>>>,
pan_gesture_recognizer: RefCell<Option<Id<UIPanGestureRecognizer>>>,
// for iOS delta references the start of the Gesture
rotation_last_delta: Cell<CGFloat>,
pinch_last_delta: Cell<CGFloat>,
pan_last_delta: Cell<CGPoint>,
} }
declare_class!( declare_class!(
@@ -165,12 +172,23 @@ declare_class!(
fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) { fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) {
let window = self.window().unwrap(); let window = self.window().unwrap();
let phase = match recognizer.state() { let (phase, delta) = match recognizer.state() {
UIGestureRecognizerState::Began => TouchPhase::Started, UIGestureRecognizerState::Began => {
UIGestureRecognizerState::Changed => TouchPhase::Moved, self.ivars().pinch_last_delta.set(recognizer.scale());
UIGestureRecognizerState::Ended => TouchPhase::Ended, (TouchPhase::Started, 0.0)
}
UIGestureRecognizerState::Changed => {
let last_scale: f64 = self.ivars().pinch_last_delta.replace(recognizer.scale());
(TouchPhase::Moved, recognizer.scale() - last_scale)
}
UIGestureRecognizerState::Ended => {
let last_scale: f64 = self.ivars().pinch_last_delta.replace(0.0);
(TouchPhase::Moved, recognizer.scale() - last_scale)
}
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => { UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
TouchPhase::Cancelled self.ivars().rotation_last_delta.set(0.0);
// Pass -delta so that action is reversed
(TouchPhase::Cancelled, -recognizer.scale())
} }
state => panic!("unexpected recognizer state: {:?}", state), state => panic!("unexpected recognizer state: {:?}", state),
}; };
@@ -179,7 +197,7 @@ declare_class!(
window_id: RootWindowId(window.id()), window_id: RootWindowId(window.id()),
event: WindowEvent::PinchGesture { event: WindowEvent::PinchGesture {
device_id: DEVICE_ID, device_id: DEVICE_ID,
delta: recognizer.velocity() as _, delta: delta as f64,
phase, phase,
}, },
}); });
@@ -209,23 +227,37 @@ declare_class!(
fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) { fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) {
let window = self.window().unwrap(); let window = self.window().unwrap();
let phase = match recognizer.state() { let (phase, delta) = match recognizer.state() {
UIGestureRecognizerState::Began => TouchPhase::Started, UIGestureRecognizerState::Began => {
UIGestureRecognizerState::Changed => TouchPhase::Moved, self.ivars().rotation_last_delta.set(0.0);
UIGestureRecognizerState::Ended => TouchPhase::Ended,
(TouchPhase::Started, 0.0)
}
UIGestureRecognizerState::Changed => {
let last_rotation = self.ivars().rotation_last_delta.replace(recognizer.rotation());
(TouchPhase::Moved, recognizer.rotation() - last_rotation)
}
UIGestureRecognizerState::Ended => {
let last_rotation = self.ivars().rotation_last_delta.replace(0.0);
(TouchPhase::Ended, recognizer.rotation() - last_rotation)
}
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => { UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
TouchPhase::Cancelled self.ivars().rotation_last_delta.set(0.0);
// Pass -delta so that action is reversed
(TouchPhase::Cancelled, -recognizer.rotation())
} }
state => panic!("unexpected recognizer state: {:?}", state), state => panic!("unexpected recognizer state: {:?}", state),
}; };
// Flip the velocity to match macOS. // Make delta negative to match macos, convert to degrees
let delta = -recognizer.velocity() as _;
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent { let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()), window_id: RootWindowId(window.id()),
event: WindowEvent::RotationGesture { event: WindowEvent::RotationGesture {
device_id: DEVICE_ID, device_id: DEVICE_ID,
delta, delta: -delta.to_degrees() as _,
phase, phase,
}, },
}); });
@@ -233,6 +265,66 @@ declare_class!(
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event); app_state::handle_nonuser_event(mtm, gesture_event);
} }
#[method(panGesture:)]
fn pan_gesture(&self, recognizer: &UIPanGestureRecognizer) {
let window = self.window().unwrap();
let translation = recognizer.translationInView(self);
let (phase, dx, dy) = match recognizer.state() {
UIGestureRecognizerState::Began => {
self.ivars().pan_last_delta.set(translation);
(TouchPhase::Started, 0.0, 0.0)
}
UIGestureRecognizerState::Changed => {
let last_pan: CGPoint = self.ivars().pan_last_delta.replace(translation);
let dx = translation.x - last_pan.x;
let dy = translation.y - last_pan.y;
(TouchPhase::Moved, dx, dy)
}
UIGestureRecognizerState::Ended => {
let last_pan: CGPoint = self.ivars().pan_last_delta.replace(CGPoint{x:0.0, y:0.0});
let dx = translation.x - last_pan.x;
let dy = translation.y - last_pan.y;
(TouchPhase::Ended, dx, dy)
}
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
let last_pan: CGPoint = self.ivars().pan_last_delta.replace(CGPoint{x:0.0, y:0.0});
// Pass -delta so that action is reversed
(TouchPhase::Cancelled, -last_pan.x, -last_pan.y)
}
state => panic!("unexpected recognizer state: {:?}", state),
};
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::PanGesture {
device_id: DEVICE_ID,
delta: PhysicalPosition::new(dx as _, dy as _),
phase,
},
});
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event);
}
}
unsafe impl NSObjectProtocol for WinitView {}
unsafe impl UIGestureRecognizerDelegate for WinitView {
#[method(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
fn should_recognize_simultaneously(&self, _gesture_recognizer: &UIGestureRecognizer, _other_gesture_recognizer: &UIGestureRecognizer) -> bool {
true
}
} }
); );
@@ -263,6 +355,11 @@ impl WinitView {
pinch_gesture_recognizer: RefCell::new(None), pinch_gesture_recognizer: RefCell::new(None),
doubletap_gesture_recognizer: RefCell::new(None), doubletap_gesture_recognizer: RefCell::new(None),
rotation_gesture_recognizer: RefCell::new(None), rotation_gesture_recognizer: RefCell::new(None),
pan_gesture_recognizer: RefCell::new(None),
rotation_last_delta: Cell::new(0.0),
pinch_last_delta: Cell::new(0.0),
pan_last_delta: Cell::new(CGPoint { x: 0.0, y: 0.0 }),
}); });
let this: Id<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] }; let this: Id<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
@@ -281,6 +378,7 @@ impl WinitView {
let pinch: Id<UIPinchGestureRecognizer> = unsafe { let pinch: Id<UIPinchGestureRecognizer> = unsafe {
msg_send_id![UIPinchGestureRecognizer::alloc(), initWithTarget: self, action: sel!(pinchGesture:)] msg_send_id![UIPinchGestureRecognizer::alloc(), initWithTarget: self, action: sel!(pinchGesture:)]
}; };
pinch.setDelegate(ProtocolObject::from_ref(self));
self.addGestureRecognizer(&pinch); self.addGestureRecognizer(&pinch);
self.ivars().pinch_gesture_recognizer.replace(Some(pinch)); self.ivars().pinch_gesture_recognizer.replace(Some(pinch));
} }
@@ -289,12 +387,35 @@ impl WinitView {
} }
} }
pub(crate) fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
) {
if should_recognize {
if self.ivars().pan_gesture_recognizer.borrow().is_none() {
let pan: Id<UIPanGestureRecognizer> = unsafe {
msg_send_id![UIPanGestureRecognizer::alloc(), initWithTarget: self, action: sel!(panGesture:)]
};
pan.setDelegate(ProtocolObject::from_ref(self));
pan.setMinimumNumberOfTouches(minimum_number_of_touches as _);
pan.setMaximumNumberOfTouches(maximum_number_of_touches as _);
self.addGestureRecognizer(&pan);
self.ivars().pan_gesture_recognizer.replace(Some(pan));
}
} else if let Some(recognizer) = self.ivars().pan_gesture_recognizer.take() {
self.removeGestureRecognizer(&recognizer);
}
}
pub(crate) fn recognize_doubletap_gesture(&self, should_recognize: bool) { pub(crate) fn recognize_doubletap_gesture(&self, should_recognize: bool) {
if should_recognize { if should_recognize {
if self.ivars().doubletap_gesture_recognizer.borrow().is_none() { if self.ivars().doubletap_gesture_recognizer.borrow().is_none() {
let tap: Id<UITapGestureRecognizer> = unsafe { let tap: Id<UITapGestureRecognizer> = unsafe {
msg_send_id![UITapGestureRecognizer::alloc(), initWithTarget: self, action: sel!(doubleTapGesture:)] msg_send_id![UITapGestureRecognizer::alloc(), initWithTarget: self, action: sel!(doubleTapGesture:)]
}; };
tap.setDelegate(ProtocolObject::from_ref(self));
tap.setNumberOfTapsRequired(2); tap.setNumberOfTapsRequired(2);
tap.setNumberOfTouchesRequired(1); tap.setNumberOfTouchesRequired(1);
self.addGestureRecognizer(&tap); self.addGestureRecognizer(&tap);
@@ -311,6 +432,7 @@ impl WinitView {
let rotation: Id<UIRotationGestureRecognizer> = unsafe { let rotation: Id<UIRotationGestureRecognizer> = unsafe {
msg_send_id![UIRotationGestureRecognizer::alloc(), initWithTarget: self, action: sel!(rotationGesture:)] msg_send_id![UIRotationGestureRecognizer::alloc(), initWithTarget: self, action: sel!(rotationGesture:)]
}; };
rotation.setDelegate(ProtocolObject::from_ref(self));
self.addGestureRecognizer(&rotation); self.addGestureRecognizer(&rotation);
self.ivars().rotation_gesture_recognizer.replace(Some(rotation)); self.ivars().rotation_gesture_recognizer.replace(Some(rotation));
} }

View File

@@ -619,6 +619,19 @@ impl Inner {
self.view.recognize_pinch_gesture(should_recognize); self.view.recognize_pinch_gesture(should_recognize);
} }
pub fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
) {
self.view.recognize_pan_gesture(
should_recognize,
minimum_number_of_touches,
maximum_number_of_touches,
);
}
pub fn recognize_doubletap_gesture(&self, should_recognize: bool) { pub fn recognize_doubletap_gesture(&self, should_recognize: bool) {
self.view.recognize_doubletap_gesture(should_recognize); self.view.recognize_doubletap_gesture(should_recognize);
} }

View File

@@ -72,7 +72,7 @@ pub struct Window {
/// Source to wake-up the event-loop for window requests. /// Source to wake-up the event-loop for window requests.
event_loop_awakener: calloop::ping::Ping, event_loop_awakener: calloop::ping::Ping,
/// The event sink to deliver sythetic events. /// The event sink to deliver synthetic events.
window_events_sink: Arc<Mutex<EventSink>>, window_events_sink: Arc<Mutex<EventSink>>,
} }

View File

@@ -1778,7 +1778,7 @@ impl EventProcessor {
None => return, None => return,
}; };
// Send the keys using the sythetic state to not alter the main state. // Send the keys using the synthetic state to not alter the main state.
let mut xkb_state = match XkbState::new_x11(xcb, keymap) { let mut xkb_state = match XkbState::new_x11(xcb, keymap) {
Some(xkb_state) => xkb_state, Some(xkb_state) => xkb_state,
None => return, None => return,

View File

@@ -180,7 +180,7 @@ impl XConnection {
// Position relative to root window. // Position relative to root window.
// With rare exceptions, this is the position of a nested window. Cases where the window // With rare exceptions, this is the position of a nested window. Cases where the window
// isn't nested are outlined in the comments throghout this function, but in addition to // isn't nested are outlined in the comments throughout this function, but in addition to
// that, fullscreen windows often aren't nested. // that, fullscreen windows often aren't nested.
let (inner_y_rel_root, child) = { let (inner_y_rel_root, child) = {
let coords = self let coords = self

View File

@@ -53,7 +53,7 @@ impl PanicInfo {
result result
} }
/// Overwrites the curret state if the current state is not panicking /// Overwrites the current state if the current state is not panicking
pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) { pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) {
if !self.is_panicking() { if !self.is_panicking() {
self.inner.set(Some(p)); self.inner.set(Some(p));

View File

@@ -12,7 +12,7 @@ use objc2::{
}; };
use objc2_app_kit::{ use objc2_app_kit::{
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSApplication, NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSApplication,
NSApplicationPresentationOptions, NSBackingStoreType, NSColor, NSDraggingDestination, NSApplicationPresentationOptions, NSBackingStoreType, NSDraggingDestination,
NSFilenamesPboardType, NSPasteboard, NSRequestUserAttentionType, NSScreen, NSView, NSFilenamesPboardType, NSPasteboard, NSRequestUserAttentionType, NSScreen, NSView,
NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel, NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel,
NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask, NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask,
@@ -605,7 +605,6 @@ fn new_window(attrs: &WindowAttributes, mtm: MainThreadMarker) -> Option<Id<Wini
if attrs.transparent { if attrs.transparent {
window.setOpaque(false); window.setOpaque(false);
window.setBackgroundColor(Some(unsafe { &NSColor::clearColor() }));
} }
// register for drag and drop operations. // register for drag and drop operations.

View File

@@ -1,5 +1,5 @@
// A poly-fill for `lazy_cell` // A poly-fill for `lazy_cell`
// Replace with std::sync::LazyLock when https://github.com/rust-lang/rust/issues/109736 is stablized. // Replace with std::sync::LazyLock when https://github.com/rust-lang/rust/issues/109736 is stabilized.
// This isn't used on every platform, which can come up as dead code warnings. // This isn't used on every platform, which can come up as dead code warnings.
#![allow(dead_code)] #![allow(dead_code)]

View File

@@ -941,6 +941,8 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **macOS:** If you're not drawing to the window yourself, you might have to set the
/// background color of the window to enable transparency.
/// - **Web / iOS / Android:** Unsupported. /// - **Web / iOS / Android:** Unsupported.
/// - **X11:** Can only be set while building the window, with /// - **X11:** Can only be set while building the window, with
/// [`WindowAttributes::with_transparent`]. /// [`WindowAttributes::with_transparent`].