Add Apple Pencil support on iOS (#4449)

With force, altitude, and azimuth data handling
This commit is contained in:
Jack
2026-01-28 13:24:38 -05:00
committed by GitHub
parent 4aac778d2a
commit b3c7635bba
2 changed files with 58 additions and 18 deletions

View File

@@ -15,8 +15,8 @@ use objc2_ui_kit::{
}; };
use tracing::debug; use tracing::debug;
use winit_core::event::{ use winit_core::event::{
ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource, TouchPhase, ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource,
WindowEvent, TabletToolAngle, TabletToolButton, TabletToolData, TabletToolKind, TouchPhase, WindowEvent,
}; };
use winit_core::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey}; use winit_core::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
@@ -461,19 +461,18 @@ impl WinitView {
let window = self.window().unwrap(); let window = self.window().unwrap();
let mut touch_events = Vec::new(); let mut touch_events = Vec::new();
for touch in touches { for touch in touches {
if let UITouchType::Pencil = touch.r#type() {
continue;
}
let logical_location = touch.locationInView(None); let logical_location = touch.locationInView(None);
let touch_type = touch.r#type(); let touch_type = touch.r#type();
let force = if let UITouchType::Pencil = touch_type { let force = if available!(ios = 9.0, tvos = 9.0, visionos = 1.0) {
None let use_force = match touch_type {
} else if available!(ios = 9.0, tvos = 9.0, visionos = 1.0) { UITouchType::Pencil => true,
let trait_collection = self.traitCollection(); _ => {
let touch_capability = trait_collection.forceTouchCapability(); let trait_collection = self.traitCollection();
// Both the OS _and_ the device need to be checked for force touch support. trait_collection.forceTouchCapability() == UIForceTouchCapability::Available
if touch_capability == UIForceTouchCapability::Available { },
};
if use_force {
let force = touch.force(); let force = touch.force();
let max_possible_force = touch.maximumPossibleForce(); let max_possible_force = touch.maximumPossibleForce();
Some(Force::Calibrated { Some(Force::Calibrated {
@@ -529,7 +528,7 @@ impl WinitView {
primary, primary,
position, position,
kind: if let UITouchType::Pencil = touch_type { kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown PointerKind::TabletTool(TabletToolKind::Pencil)
} else { } else {
PointerKind::Touch(finger_id) PointerKind::Touch(finger_id)
}, },
@@ -543,7 +542,12 @@ impl WinitView {
state: ElementState::Pressed, state: ElementState::Pressed,
position, position,
button: if let UITouchType::Pencil = touch_type { button: if let UITouchType::Pencil = touch_type {
ButtonSource::Unknown(0) let tool_data = self.tablet_tool_data_for_pencil(&touch);
ButtonSource::TabletTool {
kind: TabletToolKind::Pencil,
button: TabletToolButton::Contact,
data: tool_data,
}
} else { } else {
ButtonSource::Touch { finger_id, force } ButtonSource::Touch { finger_id, force }
}, },
@@ -552,7 +556,11 @@ impl WinitView {
}, },
UITouchPhase::Moved => { UITouchPhase::Moved => {
let (primary, source) = if let UITouchType::Pencil = touch_type { let (primary, source) = if let UITouchType::Pencil = touch_type {
(true, PointerSource::Unknown) let tool_data = self.tablet_tool_data_for_pencil(&touch);
(true, PointerSource::TabletTool {
kind: TabletToolKind::Pencil,
data: tool_data,
})
} else { } else {
(ivars.primary_finger.get().unwrap() == finger_id, PointerSource::Touch { (ivars.primary_finger.get().unwrap() == finger_id, PointerSource::Touch {
finger_id, finger_id,
@@ -592,7 +600,12 @@ impl WinitView {
state: ElementState::Released, state: ElementState::Released,
position, position,
button: if let UITouchType::Pencil = touch_type { button: if let UITouchType::Pencil = touch_type {
ButtonSource::Unknown(0) let tool_data = self.tablet_tool_data_for_pencil(&touch);
ButtonSource::TabletTool {
kind: TabletToolKind::Pencil,
button: TabletToolButton::Contact,
data: tool_data,
}
} else { } else {
ButtonSource::Touch { finger_id, force } ButtonSource::Touch { finger_id, force }
}, },
@@ -607,7 +620,7 @@ impl WinitView {
primary, primary,
position: Some(position), position: Some(position),
kind: if let UITouchType::Pencil = touch_type { kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown PointerKind::TabletTool(TabletToolKind::Pencil)
} else { } else {
PointerKind::Touch(finger_id) PointerKind::Touch(finger_id)
}, },
@@ -621,6 +634,32 @@ impl WinitView {
app_state::handle_nonuser_events(mtm, touch_events); app_state::handle_nonuser_events(mtm, touch_events);
} }
fn tablet_tool_data_for_pencil(&self, touch: &UITouch) -> TabletToolData {
let force = if available!(ios = 9.0, tvos = 9.0, visionos = 1.0) {
let force_val = touch.force();
let max_force = touch.maximumPossibleForce();
Some(Force::Calibrated { force: force_val as _, max_possible_force: max_force as _ })
} else {
None
};
let angle = if available!(ios = 9.1, tvos = 9.0, visionos = 1.0) {
let altitude = touch.altitudeAngle();
let azimuth = touch.azimuthAngleInView(Some(self));
Some(TabletToolAngle { altitude: altitude as _, azimuth: azimuth as _ })
} else {
None
};
TabletToolData {
force,
tangential_force: None, // iOS doesn't provide barrel pressure
twist: None, // iOS doesn't provide rotation/twist
tilt: None, // Will be calculated from angle if needed
angle,
}
}
fn handle_insert_text(&self, text: &NSString) { fn handle_insert_text(&self, text: &NSString) {
let window = self.window().unwrap(); let window = self.window().unwrap();
let window_id = window.id(); let window_id = window.id();

View File

@@ -43,6 +43,7 @@ changelog entry.
### Added ### Added
- Add `keyboard` support for OpenHarmony. - Add `keyboard` support for OpenHarmony.
- On iOS, add Apple Pencil support with force, altitude, and azimuth data.
### Changed ### Changed