diff --git a/Cargo.lock b/Cargo.lock index d0146dc91..cbf39375c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3366,6 +3366,7 @@ dependencies = [ "objc2-core-foundation", "objc2-core-location", "objc2-foundation", + "objc2-ui-kit", ] [[package]] diff --git a/framework-crates/objc2-xc-ui-automation/Cargo.modified.toml b/framework-crates/objc2-xc-ui-automation/Cargo.modified.toml new file mode 100644 index 000000000..c5537c54d --- /dev/null +++ b/framework-crates/objc2-xc-ui-automation/Cargo.modified.toml @@ -0,0 +1,15 @@ +[target.'cfg(target_os = "ios")'.dependencies] +objc2-ui-kit = { workspace = true, optional = true, features = ["UIOrientation"] } + +[features] +objc2-ui-kit = ["dep:objc2-ui-kit"] + +# Maybe, unsure if desirable: +default = [ + "std", + "block2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-location", + "objc2-ui-kit", +] diff --git a/framework-crates/objc2-xc-ui-automation/Cargo.toml b/framework-crates/objc2-xc-ui-automation/Cargo.toml index b3f692232..6674cf712 100644 --- a/framework-crates/objc2-xc-ui-automation/Cargo.toml +++ b/framework-crates/objc2-xc-ui-automation/Cargo.toml @@ -11,6 +11,8 @@ keywords = ["cocoa", "apple", "framework", "macos", "ios"] categories.workspace = true repository.workspace = true license.workspace = true +[target.'cfg(target_os = "ios")'.dependencies] +objc2-ui-kit = { workspace = true, optional = true, features = ["UIOrientation"] } [lints] workspace = true @@ -63,6 +65,7 @@ default = [ ] std = ["alloc"] alloc = [] +objc2-ui-kit = ["dep:objc2-ui-kit"] block2 = ["dep:block2"] objc2-app-kit = ["dep:objc2-app-kit"] objc2-core-foundation = ["dep:objc2-core-foundation"] diff --git a/framework-crates/objc2-xc-ui-automation/src/device_buttons.rs b/framework-crates/objc2-xc-ui-automation/src/device_buttons.rs new file mode 100644 index 000000000..786ca36f5 --- /dev/null +++ b/framework-crates/objc2-xc-ui-automation/src/device_buttons.rs @@ -0,0 +1,42 @@ +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +use crate::XCUIDevice; +use objc2::{extern_methods, Encode, Encoding, RefEncode}; + +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct XCUIDeviceButton(pub objc2_foundation::NSInteger); +impl XCUIDeviceButton { + #[doc(alias = "XCUIDeviceButtonHome")] + pub const Home: Self = Self(1); + #[doc(alias = "XCUIDeviceButtonVolumeUp")] + pub const VolumeUp: Self = Self(2); + #[doc(alias = "XCUIDeviceButtonVolumeDown")] + pub const VolumeDown: Self = Self(3); + #[cfg(not(any(target_os = "tvos", target_os = "visionos")))] + #[doc(alias = "XCUIDeviceButtonAction")] + pub const Action: Self = Self(4); + #[cfg(not(any(target_os = "tvos", target_os = "visionos", target_os = "watchos")))] + #[doc(alias = "XCUIDeviceButtonCamera")] + pub const Camera: Self = Self(5); +} + +unsafe impl Encode for XCUIDeviceButton { + const ENCODING: Encoding = objc2_foundation::NSInteger::ENCODING; +} + +unsafe impl RefEncode for XCUIDeviceButton { + const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING); +} +impl XCUIDevice { + extern_methods!( + #[unsafe(method(hasHardwareButton:))] + #[unsafe(method_family = none)] + pub fn hasHardwareButton(&self, button: XCUIDeviceButton) -> bool; + + /// Simulates the user pressing a physical button. + #[unsafe(method(pressButton:))] + #[unsafe(method_family = none)] + pub fn pressButton(&self, button: XCUIDeviceButton); + ); +} diff --git a/framework-crates/objc2-xc-ui-automation/src/lib.rs b/framework-crates/objc2-xc-ui-automation/src/lib.rs index bb8ea1b78..048000c19 100644 --- a/framework-crates/objc2-xc-ui-automation/src/lib.rs +++ b/framework-crates/objc2-xc-ui-automation/src/lib.rs @@ -20,6 +20,25 @@ mod generated; #[allow(unused_imports, unreachable_pub)] pub use self::generated::*; +// Everything but macOS. +#[cfg(not(any(target_os = "macos", target_env = "macabi")))] +mod device_buttons; + +#[cfg(target_os = "ios")] +mod siri; + +#[cfg(all(target_os = "ios", feature = "objc2-ui-kit"))] +mod orientation; + +#[cfg(not(target_os = "macos"))] +pub use device_buttons::*; + +#[cfg(all(target_os = "ios", feature = "objc2-ui-kit"))] +pub use orientation::*; + +#[cfg(target_os = "ios")] +pub use siri::*; + // Link to XCTest instead of XCUIAutomation, since the latter is only // available in newer Xcode versions. #[link(name = "XCTest", kind = "framework")] diff --git a/framework-crates/objc2-xc-ui-automation/src/orientation.rs b/framework-crates/objc2-xc-ui-automation/src/orientation.rs new file mode 100644 index 000000000..ba7d96cd0 --- /dev/null +++ b/framework-crates/objc2-xc-ui-automation/src/orientation.rs @@ -0,0 +1,20 @@ +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] + +use crate::XCUIDevice; +use objc2::extern_methods; +use objc2_ui_kit::UIDeviceOrientation; + +impl XCUIDevice { + extern_methods!( + /// The orientation of the device. + #[unsafe(method(orientation))] + #[unsafe(method_family = none)] + pub fn orientation(&self) -> UIDeviceOrientation; + + /// Setter for [`orientation`][Self::orientation]. + #[unsafe(method(setOrientation:))] + #[unsafe(method_family = none)] + pub fn setOrientation(&self, orientation: UIDeviceOrientation); + ); +} diff --git a/framework-crates/objc2-xc-ui-automation/src/siri.rs b/framework-crates/objc2-xc-ui-automation/src/siri.rs new file mode 100644 index 000000000..687464032 --- /dev/null +++ b/framework-crates/objc2-xc-ui-automation/src/siri.rs @@ -0,0 +1,61 @@ +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] + +use crate::XCUIDevice; +use objc2::{ + extern_class, extern_conformance, extern_methods, + rc::{Allocated, Retained}, + runtime::{NSObject, NSObjectProtocol}, + MainThreadMarker, MainThreadOnly, +}; +use objc2_foundation::NSString; + +extern_class!( + /// Represents a device's Siri interface and allows issuing textual queries + /// and producing element queries for UI shown by Siri. + /// + /// See also [Apple's documentation](https://developer.apple.com/documentation/xcuiautomation/xcuisiriservice?language=objc) + #[unsafe(super(NSObject))] + #[thread_kind = MainThreadOnly] + #[derive(Debug, PartialEq, Eq, Hash)] + pub struct XCUISiriService; +); +extern_conformance!( + unsafe impl NSObjectProtocol for XCUISiriService {} +); + +impl XCUISiriService { + extern_methods!( + #[unsafe(method(new))] + #[unsafe(method_family = new)] + pub fn new(mtm: MainThreadMarker) -> Retained; + + #[unsafe(method(init))] + #[unsafe(method_family = init)] + pub fn init(this: Allocated) -> Retained; + + /// Provides debugging information about the element representing the root of the Siri UI. + /// + /// See also: XCUIElement + #[unsafe(method(debugDescription))] + #[unsafe(method_family = none)] + pub fn debugDescription(&self) -> Retained; + + /// Presents the Siri UI, if it is not currently active, and accepts a string + /// which is then processed as if it were recognized speech. + /// + /// + /// Parameter `text`: The string to pass to Siri for processing. + #[unsafe(method(activateWithVoiceRecognitionText:))] + #[unsafe(method_family = none)] + pub fn activateWithVoiceRecognitionText(&self, text: &NSString); + ); +} +impl XCUIDevice { + extern_methods!( + /// Provides access to an object representing the Siri interface on the device. + #[unsafe(method(siriService))] + #[unsafe(method_family = none)] + pub fn siriService(&self) -> Retained; + ); +}