From a1be8a31c3e5a7d1e1adeb2972566d506792ac8f Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Wed, 19 Jul 2023 11:13:05 -0700 Subject: [PATCH 1/9] Combine retain definition for Foundation types and allow nullable --- src/appkit/app/delegate.rs | 2 +- src/appkit/event/mod.rs | 2 +- src/appkit/toolbar/class.rs | 2 +- src/defaults/mod.rs | 4 ++-- src/error.rs | 2 +- src/filesystem/manager.rs | 2 +- src/filesystem/save.rs | 2 +- src/filesystem/select.rs | 2 +- src/foundation/array.rs | 28 ++++++++++++++++------------ src/foundation/data.rs | 27 ++++++++++++++++----------- src/foundation/mod.rs | 21 +++++++++++++++++++++ src/foundation/number.rs | 28 ++++++++++++++++------------ src/foundation/string.rs | 35 ++++++++++++++++++----------------- src/foundation/urls/mod.rs | 35 ++++++++++++++++++----------------- src/input/appkit.rs | 2 +- src/input/mod.rs | 2 ++ src/input/uikit.rs | 2 +- src/networking/mod.rs | 2 +- src/pasteboard/mod.rs | 2 +- src/text/attributed_string.rs | 2 +- src/text/label/mod.rs | 2 ++ src/uikit/scene/session.rs | 2 +- src/webview/class.rs | 2 +- 23 files changed, 125 insertions(+), 85 deletions(-) diff --git a/src/appkit/app/delegate.rs b/src/appkit/app/delegate.rs index 0e90c2fe..5242d8b0 100644 --- a/src/appkit/app/delegate.rs +++ b/src/appkit/app/delegate.rs @@ -14,7 +14,7 @@ use crate::appkit::printing::PrintSettings; #[cfg(feature = "cloudkit")] use crate::cloudkit::share::CKShareMetaData; use crate::error::Error; -use crate::foundation::{id, load_or_register_class, nil, to_bool, NSArray, NSString, NSUInteger, BOOL, NO, YES}; +use crate::foundation::{id, load_or_register_class, nil, to_bool, NSArray, NSString, NSUInteger, Retainable, BOOL, NO, YES}; use crate::user_activity::UserActivity; /// A handy method for grabbing our `AppDelegate` from the pointer. This is different from our diff --git a/src/appkit/event/mod.rs b/src/appkit/event/mod.rs index 851639ff..020361a3 100644 --- a/src/appkit/event/mod.rs +++ b/src/appkit/event/mod.rs @@ -6,7 +6,7 @@ use objc::{class, msg_send, sel, sel_impl}; use objc_id::Id; use crate::events::EventType; -use crate::foundation::{id, nil, NSInteger, NSPoint, NSString}; +use crate::foundation::{id, nil, NSInteger, NSPoint, NSString, Retainable}; /// An EventMask describes the type of event. #[bitmask(u64)] diff --git a/src/appkit/toolbar/class.rs b/src/appkit/toolbar/class.rs index 719f799f..5f1f5f2d 100644 --- a/src/appkit/toolbar/class.rs +++ b/src/appkit/toolbar/class.rs @@ -7,7 +7,7 @@ use objc::runtime::{Class, Object, Sel}; use objc::{class, msg_send, sel, sel_impl}; use crate::appkit::toolbar::{ToolbarDelegate, TOOLBAR_PTR}; -use crate::foundation::{id, load_or_register_class, NSArray, NSString, BOOL}; +use crate::foundation::{id, load_or_register_class, NSArray, NSString, Retainable, BOOL}; use crate::utils::load; /// Retrieves and passes the allowed item identifiers for this toolbar. diff --git a/src/defaults/mod.rs b/src/defaults/mod.rs index 5e8d4910..4805c298 100644 --- a/src/defaults/mod.rs +++ b/src/defaults/mod.rs @@ -38,7 +38,7 @@ use objc::runtime::Object; use objc::{class, msg_send, sel, sel_impl}; use objc_id::Id; -use crate::foundation::{id, nil, to_bool, NSData, NSMutableDictionary, NSNumber, NSString, BOOL, NO, YES}; +use crate::foundation::{id, nil, to_bool, NSData, NSMutableDictionary, NSNumber, NSString, Retainable, BOOL, NO, YES}; mod value; pub use value::Value; @@ -200,7 +200,7 @@ impl UserDefaults { // // For context: https://nshipster.com/type-encodings/ if NSNumber::is(result) { - let number = NSNumber::wrap(result); + let number = NSNumber::from_retained(result); return match number.objc_type() { "c" => Some(Value::Bool(number.as_bool())), diff --git a/src/error.rs b/src/error.rs index 0620fd21..3687bfef 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,7 +9,7 @@ use std::fmt; use objc::{class, msg_send, sel, sel_impl}; -use crate::foundation::{id, nil, NSInteger, NSString}; +use crate::foundation::{id, nil, NSInteger, NSString, Retainable}; /// A wrapper around pieces of data extracted from `NSError`. This could be improved: right now, it /// allocates `String` instances when theoretically it could be avoided, and we might be erasing diff --git a/src/filesystem/manager.rs b/src/filesystem/manager.rs index 4a02878f..bd937b24 100644 --- a/src/filesystem/manager.rs +++ b/src/filesystem/manager.rs @@ -11,7 +11,7 @@ use url::Url; use crate::error::Error as AppKitError; use crate::filesystem::enums::{SearchPathDirectory, SearchPathDomainMask}; -use crate::foundation::{id, nil, NSString, NSUInteger, NO}; +use crate::foundation::{id, nil, NSString, NSUInteger, Retainable, NO}; /// A FileManager can be used for file operations (moving files, etc). /// diff --git a/src/filesystem/save.rs b/src/filesystem/save.rs index e76dff0b..58027f40 100644 --- a/src/filesystem/save.rs +++ b/src/filesystem/save.rs @@ -8,7 +8,7 @@ use objc::runtime::Object; use objc::{class, msg_send, sel, sel_impl}; use objc_id::ShareId; -use crate::foundation::{id, nil, NSInteger, NSString, NO, YES}; +use crate::foundation::{id, nil, NSInteger, NSString, Retainable, NO, YES}; #[derive(Debug)] pub struct FileSavePanel { diff --git a/src/filesystem/select.rs b/src/filesystem/select.rs index 56ff4a93..17a32c83 100644 --- a/src/filesystem/select.rs +++ b/src/filesystem/select.rs @@ -11,7 +11,7 @@ use objc::{class, msg_send, sel, sel_impl}; use objc_id::ShareId; use crate::filesystem::enums::ModalResponse; -use crate::foundation::{id, nil, NSInteger, NSString, NO, NSURL, YES}; +use crate::foundation::{id, nil, NSInteger, NSString, Retainable, NO, NSURL, YES}; #[cfg(feature = "appkit")] use crate::appkit::window::{Window, WindowDelegate}; diff --git a/src/foundation/array.rs b/src/foundation/array.rs index 800156c2..ea6df757 100644 --- a/src/foundation/array.rs +++ b/src/foundation/array.rs @@ -6,6 +6,8 @@ use objc_id::Id; use crate::foundation::id; +use super::Retainable; + /// A wrapper for `NSArray` that makes common operations in our framework a bit easier to handle /// and reason about. This also provides a central place to look at replacing with `CFArray` if /// ever deemed necessary (unlikely, given how much Apple has optimized the Foundation classes, but @@ -24,18 +26,6 @@ impl NSArray { }) } - /// In some cases, we're vended an `NSArray` by the system that we need to call retain on. - /// This handles that case. - pub fn retain(array: id) -> Self { - NSArray(unsafe { Id::from_ptr(array) }) - } - - /// In some cases, we're vended an `NSArray` by the system, and it's ideal to not retain that. - /// This handles that edge case. - pub fn from_retained(array: id) -> Self { - NSArray(unsafe { Id::from_retained_ptr(array) }) - } - /// Returns the `count` (`len()` equivalent) for the backing `NSArray`. pub fn count(&self) -> usize { unsafe { msg_send![&*self.0, count] } @@ -51,6 +41,20 @@ impl NSArray { } } +impl Retainable for NSArray { + /// In some cases, we're vended an `NSArray` by the system that we need to call retain on. + /// This handles that case. + fn retain(array: id) -> Self { + NSArray(unsafe { Id::from_ptr(array) }) + } + + /// In some cases, we're vended an `NSArray` by the system, and it's ideal to not retain that. + /// This handles that edge case. + fn from_retained(array: id) -> Self { + NSArray(unsafe { Id::from_retained_ptr(array) }) + } +} + #[derive(Debug)] pub struct NSArrayIterator<'a> { next_index: usize, diff --git a/src/foundation/data.rs b/src/foundation/data.rs index c8042865..b27dcc31 100644 --- a/src/foundation/data.rs +++ b/src/foundation/data.rs @@ -11,6 +11,8 @@ use objc_id::Id; use crate::foundation::{id, to_bool, NSUInteger, BOOL, NO, YES}; +use super::Retainable; + /// Wrapper for a retained `NSData` object. /// /// Supports constructing a new `NSData` from a `Vec`, wrapping and retaining an existing @@ -65,17 +67,6 @@ impl NSData { } } - /// Given a (presumably) `NSData`, wraps and retains it. - pub fn retain(data: id) -> Self { - NSData(unsafe { Id::from_ptr(data) }) - } - - /// If we're vended an NSData from a method (e.g, a push notification token) we might want to - /// wrap it while we figure out what to do with it. This does that. - pub fn from_retained(data: id) -> Self { - NSData(unsafe { Id::from_retained_ptr(data) }) - } - /// A helper method for determining if a given `NSObject` is an `NSData`. pub fn is(obj: id) -> bool { let result: BOOL = unsafe { msg_send![obj, isKindOfClass: class!(NSData)] }; @@ -127,6 +118,20 @@ impl NSData { } } +impl Retainable for NSData { + /// In some cases, we're vended an `NSData` by the system that we need to call retain on. + /// This handles that case. + fn retain(data: id) -> Self { + NSData(unsafe { Id::from_ptr(data) }) + } + + /// If we're vended an NSData from a method (e.g, a push notification token) we might want to + /// wrap it while we figure out what to do with it. This does that. + fn from_retained(data: id) -> Self { + NSData(unsafe { Id::from_retained_ptr(data) }) + } +} + impl From for id { /// Consumes and returns the underlying `NSData`. fn from(mut data: NSData) -> Self { diff --git a/src/foundation/mod.rs b/src/foundation/mod.rs index f73c0530..236679ed 100644 --- a/src/foundation/mod.rs +++ b/src/foundation/mod.rs @@ -63,6 +63,27 @@ pub fn to_bool(result: BOOL) -> bool { } } +pub trait Retainable { + /// In some cases, we're vended an `NSObject` by the system that we need to call retain on. + /// This handles that case. + fn retain(handle: id) -> Self; + + /// In some cases, we're vended an `NSObject` by the system, and it's ideal to not retain that. + /// This handles that edge case. + fn from_retained(handle: id) -> Self; + + fn retain_nullable(handle: id) -> Option + where + Self: Sized + { + if !handle.is_null() { + Some(Self::retain(handle)) + } else { + None + } + } +} + /// More or less maps over to Objective-C's `id` type, which... can really be anything. #[allow(non_camel_case_types)] pub type id = *mut runtime::Object; diff --git a/src/foundation/number.rs b/src/foundation/number.rs index 146762ac..795c50fd 100644 --- a/src/foundation/number.rs +++ b/src/foundation/number.rs @@ -7,6 +7,8 @@ use objc_id::Id; use crate::foundation::{id, to_bool, NSInteger, BOOL, NO, YES}; +use super::Retainable; + /// Wrapper for a `NSNumber` object. /// /// In general we strive to avoid using this in the codebase, but it's a requirement for moving @@ -15,18 +17,6 @@ use crate::foundation::{id, to_bool, NSInteger, BOOL, NO, YES}; pub struct NSNumber(pub Id); impl NSNumber { - /// If we're vended an NSNumber from a method (e.g, `NSUserDefaults` querying) we might want to - /// wrap (and retain) it while we figure out what to do with it. This does that. - pub fn retain(data: id) -> Self { - NSNumber(unsafe { Id::from_ptr(data) }) - } - - /// If we're vended an NSNumber from a method (e.g, `NSUserDefaults` querying) we might want to - /// wrap it while we figure out what to do with it. This does that. - pub fn wrap(data: id) -> Self { - NSNumber(unsafe { Id::from_retained_ptr(data) }) - } - /// Constructs a `numberWithBool` instance of `NSNumber` and retains it. pub fn bool(value: bool) -> Self { NSNumber(unsafe { @@ -97,6 +87,20 @@ impl NSNumber { } } +impl Retainable for NSNumber { + /// If we're vended an NSNumber from a method (e.g, `NSUserDefaults` querying) we might want to + /// wrap (and retain) it while we figure out what to do with it. This does that. + fn retain(data: id) -> Self { + NSNumber(unsafe { Id::from_ptr(data) }) + } + + /// If we're vended an NSNumber from a method (e.g, `NSUserDefaults` querying) we might want to + /// wrap it while we figure out what to do with it. This does that. + fn from_retained(data: id) -> Self { + NSNumber(unsafe { Id::from_retained_ptr(data) }) + } +} + impl From for id { /// Consumes and returns the underlying `NSNumber`. fn from(mut number: NSNumber) -> Self { diff --git a/src/foundation/string.rs b/src/foundation/string.rs index 543640f4..615f5677 100644 --- a/src/foundation/string.rs +++ b/src/foundation/string.rs @@ -9,6 +9,8 @@ use objc_id::Id; use crate::foundation::{id, to_bool, BOOL, NO, YES}; +use super::Retainable; + const UTF8_ENCODING: usize = 4; /// A wrapper for `NSString`. @@ -54,23 +56,6 @@ impl<'a> NSString<'a> { } } - /// In cases where we're vended an `NSString` by the system, this can be used to wrap and - /// retain it. - pub fn retain(object: id) -> Self { - NSString { - objc: unsafe { Id::from_ptr(object) }, - phantom: PhantomData - } - } - - /// In some cases, we want to wrap a system-provided NSString without retaining it. - pub fn from_retained(object: id) -> Self { - NSString { - objc: unsafe { Id::from_retained_ptr(object) }, - phantom: PhantomData - } - } - /// Utility method for checking whether an `NSObject` is an `NSString`. pub fn is(obj: id) -> bool { let result: BOOL = unsafe { msg_send![obj, isKindOfClass: class!(NSString)] }; @@ -107,6 +92,22 @@ impl<'a> NSString<'a> { } } +impl Retainable for NSString<'_> { + fn retain(object: id) -> Self { + NSString { + objc: unsafe { Id::from_ptr(object) }, + phantom: PhantomData + } + } + + fn from_retained(object: id) -> Self { + NSString { + objc: unsafe { Id::from_retained_ptr(object) }, + phantom: PhantomData + } + } +} + impl fmt::Display for NSString<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.to_str()) diff --git a/src/foundation/urls/mod.rs b/src/foundation/urls/mod.rs index 6af1374f..769ace4f 100644 --- a/src/foundation/urls/mod.rs +++ b/src/foundation/urls/mod.rs @@ -15,6 +15,8 @@ pub use bookmark_options::{NSURLBookmarkCreationOption, NSURLBookmarkResolutionO mod resource_keys; pub use resource_keys::{NSURLFileResource, NSURLResourceKey, NSUbiquitousItemDownloadingStatus}; +use super::Retainable; + /// Wraps `NSURL` for use throughout the framework. /// /// This type may also be returned to users in some callbacks (e.g, file manager/selectors) as it's @@ -36,23 +38,6 @@ pub struct NSURL<'a> { } impl<'a> NSURL<'a> { - /// In cases where we're vended an `NSURL` by the system, this can be used to wrap and - /// retain it. - pub fn retain(object: id) -> Self { - NSURL { - objc: unsafe { ShareId::from_ptr(object) }, - phantom: PhantomData - } - } - - /// In some cases, we want to wrap a system-provided NSURL without retaining it. - pub fn from_retained(object: id) -> Self { - NSURL { - objc: unsafe { ShareId::from_retained_ptr(object) }, - phantom: PhantomData - } - } - /// Creates and returns a URL object by calling through to `[NSURL URLWithString]`. pub fn with_str(url: &str) -> Self { let url = NSString::new(url); @@ -158,6 +143,22 @@ impl<'a> NSURL<'a> { } } +impl Retainable for NSURL<'_> { + fn retain(object: id) -> Self { + NSURL { + objc: unsafe { ShareId::from_ptr(object) }, + phantom: PhantomData + } + } + + fn from_retained(object: id) -> Self { + NSURL { + objc: unsafe { ShareId::from_retained_ptr(object) }, + phantom: PhantomData + } + } +} + /*impl From> for id { /// Consumes and returns the pointer to the underlying NSString instance. fn from(mut string: NSString) -> Self { diff --git a/src/input/appkit.rs b/src/input/appkit.rs index 66df7b63..a530b833 100644 --- a/src/input/appkit.rs +++ b/src/input/appkit.rs @@ -1,7 +1,7 @@ use objc::runtime::{Class, Object, Sel, BOOL}; use objc::{msg_send, sel, sel_impl}; -use crate::foundation::{id, load_or_register_class, NSString, NO, YES}; +use crate::foundation::{id, load_or_register_class, NSString, Retainable, NO, YES}; use crate::input::{TextFieldDelegate, TEXTFIELD_DELEGATE_PTR}; use crate::utils::load; diff --git a/src/input/mod.rs b/src/input/mod.rs index 4af8bb02..4c54f704 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -294,6 +294,8 @@ impl TextField { /// Grabs the value from the textfield and returns it as an owned String. #[cfg(feature = "appkit")] pub fn get_value(&self) -> String { + use crate::foundation::Retainable; + self.objc .get(|obj| unsafe { NSString::retain(msg_send![obj, stringValue]).to_string() }) } diff --git a/src/input/uikit.rs b/src/input/uikit.rs index 83f15a23..d50a19b3 100644 --- a/src/input/uikit.rs +++ b/src/input/uikit.rs @@ -5,7 +5,7 @@ use objc::runtime::{Class, Object, Sel, BOOL}; use objc::{class, msg_send, sel, sel_impl}; use objc_id::Id; -use crate::foundation::{id, load_or_register_class, NSString, NSUInteger, NO, YES}; +use crate::foundation::{id, load_or_register_class, NSString, NSUInteger, NO, YES, Retainable}; use crate::input::{TextFieldDelegate, TEXTFIELD_DELEGATE_PTR}; use crate::utils::load; diff --git a/src/networking/mod.rs b/src/networking/mod.rs index 42d05096..dc870308 100644 --- a/src/networking/mod.rs +++ b/src/networking/mod.rs @@ -8,7 +8,7 @@ use objc::runtime::Object; use objc::{msg_send, sel, sel_impl}; use objc_id::ShareId; -use crate::foundation::{id, NSString}; +use crate::foundation::{id, NSString, Retainable}; /// A wrapper around `NSURLRequest`. #[derive(Debug)] diff --git a/src/pasteboard/mod.rs b/src/pasteboard/mod.rs index 3847ff49..6168f2e3 100644 --- a/src/pasteboard/mod.rs +++ b/src/pasteboard/mod.rs @@ -21,7 +21,7 @@ use objc_id::ShareId; use url::Url; use crate::error::Error; -use crate::foundation::{id, nil, NSArray, NSString, NSURL}; +use crate::foundation::{id, nil, NSArray, NSString, Retainable, NSURL}; mod types; pub use types::{PasteboardName, PasteboardType}; diff --git a/src/text/attributed_string.rs b/src/text/attributed_string.rs index 4cfde00b..9d1be6cb 100644 --- a/src/text/attributed_string.rs +++ b/src/text/attributed_string.rs @@ -10,7 +10,7 @@ use objc::{class, msg_send, sel, sel_impl}; use objc_id::Id; use crate::color::Color; -use crate::foundation::{id, to_bool, NSString, BOOL, NO, YES}; +use crate::foundation::{id, to_bool, NSString, Retainable, BOOL, NO, YES}; use super::Font; diff --git a/src/text/label/mod.rs b/src/text/label/mod.rs index 04844a3d..1c971e2e 100644 --- a/src/text/label/mod.rs +++ b/src/text/label/mod.rs @@ -365,6 +365,8 @@ impl Label { /// Retrieve the text currently held in the label. #[cfg(feature = "appkit")] pub fn get_text(&self) -> String { + use crate::foundation::Retainable; + self.objc .get(|obj| unsafe { NSString::retain(msg_send![obj, stringValue]).to_string() }) } diff --git a/src/uikit/scene/session.rs b/src/uikit/scene/session.rs index bf16825a..789a1ffd 100644 --- a/src/uikit/scene/session.rs +++ b/src/uikit/scene/session.rs @@ -2,7 +2,7 @@ use objc::runtime::Object; use objc::{msg_send, sel, sel_impl}; use objc_id::Id; -use crate::foundation::{id, NSString}; +use crate::foundation::{id, NSString, Retainable}; use crate::uikit::scene::enums::SessionRole; #[derive(Debug)] diff --git a/src/webview/class.rs b/src/webview/class.rs index af1ef6a5..149a87de 100644 --- a/src/webview/class.rs +++ b/src/webview/class.rs @@ -12,7 +12,7 @@ use objc::declare::ClassDecl; use objc::runtime::{Class, Object, Sel, BOOL}; use objc::{class, msg_send, sel, sel_impl}; -use crate::foundation::{id, load_or_register_class, nil, NSArray, NSInteger, NSString, NO, YES}; +use crate::foundation::{id, load_or_register_class, nil, NSArray, NSInteger, NSString, NO, YES, Retainable}; use crate::webview::actions::{NavigationAction, NavigationResponse}; use crate::webview::{mimetype::MimeType, WebViewDelegate, WEBVIEW_DELEGATE_PTR}; //, OpenPanelParameters}; //use crate::webview::enums::{NavigationPolicy, NavigationResponsePolicy}; From 22dd4b7487763efae4093262ba91e61bfb403fcd Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Wed, 19 Jul 2023 13:39:30 -0700 Subject: [PATCH 2/9] NSRunningApplication implementation --- src/appkit/mod.rs | 1 + src/appkit/workspace/mod.rs | 24 +++ src/appkit/workspace/running_application.rs | 198 ++++++++++++++++++++ src/input/uikit.rs | 2 +- src/webview/class.rs | 2 +- 5 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 src/appkit/workspace/mod.rs create mode 100644 src/appkit/workspace/running_application.rs diff --git a/src/appkit/mod.rs b/src/appkit/mod.rs index eab6a638..638c1664 100644 --- a/src/appkit/mod.rs +++ b/src/appkit/mod.rs @@ -29,3 +29,4 @@ pub mod toolbar; pub mod window; pub mod haptics; +pub mod workspace; diff --git a/src/appkit/workspace/mod.rs b/src/appkit/workspace/mod.rs new file mode 100644 index 00000000..d8b234f8 --- /dev/null +++ b/src/appkit/workspace/mod.rs @@ -0,0 +1,24 @@ +use objc::{class, msg_send, sel, sel_impl}; + +use crate::foundation::{id, NSArray, Retainable}; + +use self::running_application::RunningApplication; + +pub mod running_application; + +#[derive(Debug)] +pub struct Workspace(id); + +impl Workspace { + pub fn shared_workspace() -> Self { + let workspace: id = unsafe { msg_send![class!(NSWorkspace), sharedWorkspace] }; + + Workspace(workspace) + } + + pub fn running_applications(&self) -> Vec { + let apps: id = unsafe { msg_send![self.0, runningApplications] }; + + NSArray::retain(apps).iter().map(|a| RunningApplication::new(a)).collect() + } +} diff --git a/src/appkit/workspace/running_application.rs b/src/appkit/workspace/running_application.rs new file mode 100644 index 00000000..22a6bd8e --- /dev/null +++ b/src/appkit/workspace/running_application.rs @@ -0,0 +1,198 @@ +use std::f32::consts::E; + +use bitmask_enum::bitmask; +use objc::{class, msg_send, sel, sel_impl}; + +use crate::{ + foundation::{id, NSArray, NSInteger, NSString, NSUInteger, Retainable, NSURL}, + image::Image +}; + +#[derive(Debug)] +pub struct RunningApplication(id); + +impl RunningApplication { + pub fn new(running_app: id) -> Self { + RunningApplication(running_app) + } + + /// Returns the running application with the given process identifier, or `None` if no application has that pid. + pub fn from_process_identifier(pid: usize) -> Option { + let id: id = unsafe { msg_send![class!(NSRunningApplication), runningApplicationWithProcessIdentifier:pid] }; + + if !id.is_null() { + Some(RunningApplication::new(id)) + } else { + None + } + } + + /// Returns an array of currently running applications with the specified bundle identifier. + pub fn from_bundle_identifier(identifier: &str) -> Vec { + let identifier = NSString::new(identifier); + let id: id = unsafe { msg_send![class!(NSRunningApplication), runningApplicationsWithBundleIdentifier:identifier] }; + + NSArray::retain(id).iter().map(|a| RunningApplication(a)).collect() + } + + /// Returns an NSRunningApplication representing this application. + pub fn current() -> Self { + let id: id = unsafe { msg_send![class!(NSRunningApplication), currentApplication] }; + + Self::new(id) + } + + // Activating applications + + /// Attempts to activate the application using the specified options. + pub fn activate_with_options(&self, options: ApplicationActivationOptions) -> bool { + let options = options.bits; + unsafe { msg_send![self.0, activateWithOptions:options] } + } + + /// Indicates whether the application is currently frontmost. + pub fn active(&self) -> bool { + unsafe { msg_send![self.0, isActive] } + } + + pub fn activation_policy(&self) -> ApplicationActivationPolicy { + let policy: NSUInteger = unsafe { msg_send![self.0, activationPolicy] }; + + match policy { + 0 => ApplicationActivationPolicy::Regular, + 1 => ApplicationActivationPolicy::Accessory, + 2 => ApplicationActivationPolicy::Prohibited, + + _ => ApplicationActivationPolicy::Regular + } + } + + // Hiding and unhiding applications + + /// Attempts to hide or the application. + pub fn hide(&self) { + unsafe { msg_send![self.0, hide] } + } + + /// Attempts to unhide or the application. + pub fn unhide(&self) { + unsafe { msg_send![self.0, unhide] } + } + + /// Indicates whether the application is currently hidden. + pub fn hidden(&self) -> bool { + unsafe { msg_send![self.0, isHidden] } + } + + // Application information + + /// Indicates the localized name of the application. + pub fn localized_name(&self) -> Option { + NSString::retain_nullable(unsafe { msg_send![self.0, localizedName] }).and_then(|s| Some(s.to_string())) + } + + /// Returns the icon for the receiver’s application. + pub fn icon(&self) -> Option { + let id: id = unsafe { msg_send![self.0, icon] }; + + if !id.is_null() { + Some(Image::with(id)) + } else { + None + } + } + + /// Indicates the `CFBundleIdentifier` of the application. + pub fn bundle_identifier(&self) -> Option { + NSString::retain_nullable(unsafe { msg_send![self.0, bundleIdentifier] }).and_then(|s| Some(s.to_string())) + } + + /// Indicates the URL to the application's bundle. + pub fn bundle_url(&self) -> Option { + NSURL::retain_nullable(unsafe { msg_send![self.0, bundleURL] }) + } + + /// Indicates the executing processor architecture for the application. + /// + /// The returned value will be one of the constants in Mach-O Architecture in `NSBundle`. + pub fn executable_architecture(&self) -> usize { + let arch: NSInteger = unsafe { msg_send![self.0, executableArchitecture] }; + + arch as usize + } + + /// Indicates the URL to the application's executable. + pub fn executable_url(&self) -> Option { + NSURL::retain_nullable(unsafe { msg_send![self.0, executableURL] }) + } + + /// Indicates the date when the application was launched. + pub fn launch_date(&self) -> Option { + unimplemented!("No NSDate implementation") + } + + /// Indicates whether the receiver’s process has finished launching. + pub fn finished_launching(&self) -> bool { + unsafe { msg_send![self.0, isFinishedLaunching] } + } + + /// Indicates the process identifier (pid) of the application. + pub fn process_identifier(&self) -> usize { + unsafe { msg_send![self.0, processIdentifier] } + } + + /// Returns whether the application owns the current menu bar. + pub fn owns_menu_bar(&self) -> bool { + unsafe { msg_send![self.0, ownsMenuBar] } + } + + // Terminating applications + + /// Attempts to force the receiver to quit. + pub fn force_terminate(&self) { + unsafe { msg_send![self.0, forceTerminate] } + } + + /// Attempts to quit the receiver normally. + pub fn terminate(&self) { + unsafe { msg_send![self.0, terminate] } + } + + /// Indicates that the receiver’s application has terminated. + pub fn terminated(&self) -> bool { + unsafe { msg_send![self.0, isTerminated] } + } + + /// Terminates invisibly running applications as if triggered by system memory pressure. + pub fn terminate_automatically_terminable_applications() { + unsafe { msg_send![class!(NSRunningApplication), terminateAutomaticallyTerminableApplications] } + } +} + +#[bitmask(u32)] +/// Flags are for `activate_with_options` +pub enum ApplicationActivationOptions { + /// This enum option doesn't actually exist, but it provides a convenient way for Rust to set no values + None = 0, + + /// By default, activation brings only the main and key windows forward. + /// If you specify `ActivateAllWindows`, all of the application's windows are brought forward. + ActivateAllWindows = 1 << 0, + + /// The application is activated regardless of the currently active app. + ActivateIgnoringOtherApps = 1 << 1 +} + +/// Activation policies (used by `activationPolicy`) that control whether and how an app may be activated. +#[derive(Debug)] +pub enum ApplicationActivationPolicy { + /// The application is an ordinary app that appears in the Dock and may have a user interface. + Regular = 0, + + /// The application doesn’t appear in the Dock and doesn’t have a menu bar, but it may be activated + /// programmatically or by clicking on one of its windows. + Accessory = 1, + + /// The application doesn’t appear in the Dock and may not create windows or be activated. + Prohibited = 2 +} diff --git a/src/input/uikit.rs b/src/input/uikit.rs index d50a19b3..10c51903 100644 --- a/src/input/uikit.rs +++ b/src/input/uikit.rs @@ -5,7 +5,7 @@ use objc::runtime::{Class, Object, Sel, BOOL}; use objc::{class, msg_send, sel, sel_impl}; use objc_id::Id; -use crate::foundation::{id, load_or_register_class, NSString, NSUInteger, NO, YES, Retainable}; +use crate::foundation::{id, load_or_register_class, NSString, NSUInteger, Retainable, NO, YES}; use crate::input::{TextFieldDelegate, TEXTFIELD_DELEGATE_PTR}; use crate::utils::load; diff --git a/src/webview/class.rs b/src/webview/class.rs index 149a87de..f894abd7 100644 --- a/src/webview/class.rs +++ b/src/webview/class.rs @@ -12,7 +12,7 @@ use objc::declare::ClassDecl; use objc::runtime::{Class, Object, Sel, BOOL}; use objc::{class, msg_send, sel, sel_impl}; -use crate::foundation::{id, load_or_register_class, nil, NSArray, NSInteger, NSString, NO, YES, Retainable}; +use crate::foundation::{id, load_or_register_class, nil, NSArray, NSInteger, NSString, Retainable, NO, YES}; use crate::webview::actions::{NavigationAction, NavigationResponse}; use crate::webview::{mimetype::MimeType, WebViewDelegate, WEBVIEW_DELEGATE_PTR}; //, OpenPanelParameters}; //use crate::webview::enums::{NavigationPolicy, NavigationResponsePolicy}; From 0c8929fb596f396d9c73edb87212fd5e184db5fa Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 20 Jul 2023 11:52:15 -0700 Subject: [PATCH 3/9] Implementation of much of NSWorkspace --- src/appkit/workspace/mod.rs | 281 +++++++++++++++++++- src/appkit/workspace/running_application.rs | 2 +- src/foundation/array.rs | 2 +- src/foundation/dictionary.rs | 12 +- 4 files changed, 289 insertions(+), 8 deletions(-) diff --git a/src/appkit/workspace/mod.rs b/src/appkit/workspace/mod.rs index d8b234f8..df9e92ba 100644 --- a/src/appkit/workspace/mod.rs +++ b/src/appkit/workspace/mod.rs @@ -1,6 +1,13 @@ -use objc::{class, msg_send, sel, sel_impl}; +use std::collections::HashMap; -use crate::foundation::{id, NSArray, Retainable}; +use block::ConcreteBlock; +use objc::{class, msg_send, runtime::Object, sel, sel_impl}; + +use crate::{ + color::Color, + error::Error, + foundation::{id, nil, NSArray, NSInteger, NSMutableDictionary, NSString, Retainable, NSURL} +}; use self::running_application::RunningApplication; @@ -9,16 +16,284 @@ pub mod running_application; #[derive(Debug)] pub struct Workspace(id); +#[derive(Debug)] +pub struct GetFileSystemInfoForPathResponse { + pub removable: bool, + pub writable: bool, + pub unmountable: bool, + pub description: String, + pub file_system_type: String +} + impl Workspace { - pub fn shared_workspace() -> Self { + /// The shared workspace object. + pub fn shared() -> Self { let workspace: id = unsafe { msg_send![class!(NSWorkspace), sharedWorkspace] }; Workspace(workspace) } + // Accessing the Workspace Notification Center + + /// The notification center for workspace notifications. + pub fn notification_center() -> Self { + unimplemented!("Missing NSNotificationCenter implementation") + } + + // Opening URLs + + /// Opens a URL asynchronously using the provided options. + /// + /// Maps to `openURL:configuration:completionHandler:` + pub fn open_url_with_completion(&self, _url: &str, _configuration: &str, _completion_handler: F) + where + F: FnOnce() -> () + { + unimplemented!("Missing NSWorkspaceOpenConfiguration implementation. Only >=10.15"); + } + + /// Opens one or more URLs asynchronously in the specified app using the provided options. + pub fn open_url_with_application_with_completion( + &self, + _url: &str, + _application_url: &str, + _configuration: &str, + _completion_handler: F + ) where + F: FnOnce() -> () + { + unimplemented!("Missing NSWorkspaceOpenConfiguration implementation. Only >=10.15"); + } + + /// Opens the location at the specified URL. + pub fn open_url(&self, url: &str) -> bool { + let url = NSString::new(url); + + unsafe { + let url: id = msg_send![class!(NSURL), URLWithString:url]; + msg_send![self.0, openURL: url] + } + } + + // Launching and Hiding Apps + + /// Launches the app at the specified URL and asynchronously reports back on the app's status. + pub fn open_application_at_url(&self, _application_url: &str, _configuration: &str, _completion_handler: F) + where + F: FnOnce() -> () + { + unimplemented!("Missing NSWorkspaceOpenConfiguration implementation. Only >=10.15"); + } + + /// Hides all applications other than the sender. + /// + /// Must be called on the main thread + pub fn hide_other_applications(&self) { + unsafe { msg_send![self.0, hideOtherApplications] } + } + + /// Duplicates the specified URLS asynchronously in the same manner as the Finder. + pub fn duplicate_urls(&self, _urls: Vec<&str>, _completion_handler: Option) + where + F: Fn(HashMap, Error) -> () + Send + Sync + 'static + { + unimplemented!("Missing ability to create NSArrays of NSURL"); + // let urls = urls + // .iter() + // .map(|u| { + // let mut url = NSURL::with_str(u); + // &mut *url as id + // }) + // .collect::>(); + // let urls = NSArray::new(&urls); + + // if let Some(completion_handler) = completion_handler { + // let block = ConcreteBlock::new(move |new_urls, error| { + // let new_urls = NSMutableDictionary::retain(new_urls); + // let new_urls = new_urls.into_hashmap(|u| NSURL::retain(u).absolute_string()); + + // let error = Error::new(error); + + // completion_handler(new_urls, error); + // }); + + // let block = block.copy(); + + // unsafe { msg_send![self.0, duplicateURLs: urls completionHandler: block] } + // } else { + // unsafe { msg_send![self.0, duplicateURLs: urls completionHandler: nil] } + // } + } + + /// Moves the specified URLs to the trash in the same manner as the Finder. + pub fn recycle_urls(&self, _urls: Vec<&str>, _completion_handler: Option) + where + F: Fn(HashMap, Error) -> () + Send + Sync + 'static + { + unimplemented!("Missing ability to create NSArrays of NSURL"); + } + + /// Activates the Finder, and opens one or more windows selecting the specified files. + pub fn active_file_viewer_selecting_urls(&self, _urls: Vec<&str>) { + unimplemented!("Missing ability to create NSArrays of NSURL"); + } + + /// Selects the file at the specified path. Corresponds to `selectFile:inFileViewerRootedAtPath:` + pub fn select_file(&self, file_path: &str, file_viewer_root_path: &str) -> bool { + let file_path = NSString::new(file_path); + let root_path = NSString::new(file_viewer_root_path); + + unsafe { msg_send![self.0, selectFile: file_path inFileViewerRootedAtPath: root_path] } + } + + // Manipulating Uniform Type Identifier Information + + /// Returns the URL for the app with the specified identifier. + pub fn url_for_application(&self, bundle_identifier: &str) -> Option { + let bundle_identifier = NSString::new(bundle_identifier); + + let url: id = unsafe { msg_send![self.0, URLForApplicationWithBundleIdentifier: bundle_identifier] }; + NSURL::retain_nullable(url) + } + + // Requesting Information + + /// Returns the URL to the default app that would be opened. + pub fn url_for_application_to_open(&self, url: &str) -> Option { + let url = NSString::new(url); + + let url: id = unsafe { msg_send![self.0, URLForApplicationToOpenURL: url] }; + NSURL::retain_nullable(url) + } + + /// Returns information about the file system at the specified path. + pub fn get_fs_info(&self, path: &str) -> Option { + let path = NSString::new(path); + + let mut removable = Box::new(false); + let mut writable = Box::new(false); + let mut unmountable = Box::new(false); + let description = NSString::new(""); + let file_system_type = NSString::new(""); + + let removable_ptr = removable.as_mut() as *mut bool; + let writable_ptr = writable.as_mut() as *mut bool; + let unmountable_ptr = unmountable.as_mut() as *mut bool; + + let returned_data: bool = unsafe { + msg_send![self.0, getFileSystemInfoForPath: + path isRemovable: removable_ptr + isWritable: writable_ptr + isUnmountable: unmountable_ptr + description: &description + type: &file_system_type + ] + }; + + if returned_data { + Some(GetFileSystemInfoForPathResponse { + removable: *removable, + writable: *writable, + unmountable: *unmountable, + description: description.to_string(), + file_system_type: file_system_type.to_string() + }) + } else { + None + } + } + + /// Determines whether the specified path is a file package. + pub fn is_path_file_package(&self, path: &str) -> bool { + let path = NSString::new(path); + + unsafe { msg_send![self.0, isFilePackageAtPath: path] } + } + + /// Returns the frontmost app, which is the app that receives key events. + pub fn frontmost_application(&self) -> Option { + let id: id = unsafe { msg_send![self.0, frontmostApplication] }; + + if !id.is_null() { + Some(RunningApplication::new(id)) + } else { + None + } + } + + /// Returns an array of running apps. pub fn running_applications(&self) -> Vec { let apps: id = unsafe { msg_send![self.0, runningApplications] }; NSArray::retain(apps).iter().map(|a| RunningApplication::new(a)).collect() } + + /// Returns the app that owns the currently displayed menu bar. + pub fn menu_bar_owning_application(&self) -> Option { + let id: id = unsafe { msg_send![self.0, menuBarOwningApplication] }; + + if !id.is_null() { + Some(RunningApplication::new(id)) + } else { + None + } + } + + // Managing Icons + + // TODO: Need icon methods + + // Unmounting a Device + + // TODO: Need unmount methods + + // Managing the Desktop Image + + // TODO: Need desktop image methods + + // Performing Finder Spotlight Searches + + /// Displays a Spotlight search results window in Finder for the specified query string. + pub fn show_finder_results_for_query(&self, query: &str) -> bool { + let query = NSString::new(query); + unsafe { msg_send![self.0, showSearchResultsForQueryString: query] } + } + + // Finder File Labels + + /// The array of file labels, returned as strings. + pub fn file_labels(&self) -> Vec { + let id: id = unsafe { msg_send![self.0, fileLabels] }; + NSArray::retain(id).iter().map(|s| NSString::retain(s).to_string()).collect() + } + + /// The array of colors for the file labels. + pub fn file_label_colors(&self) -> Vec { + unimplemented!("No NSColor constructor from a pointer") + } + + // Tracking Changes to the File System + + /// Informs the workspace object that the file system changed at the specified path. + pub fn note_fs_changed(&self, path: &str) { + let path = NSString::new(path); + unsafe { msg_send![self.0, noteFileSystemChanged: path] } + } + + // Requesting Additional Time Before Logout + + /// Requests the system wait for the specified amount of time before turning off the power or logging out the user. + pub fn extend_power_off(&self, milliseconds: i64) { + let milliseconds = milliseconds as NSInteger; + unsafe { msg_send![self.0, extendPowerOffBy: milliseconds] } + } + + // Suppporting Accessibility + + // TODO: Need accessibility getter/setter methods + + // Performing Priviledged Operations + + // TODO: Needs priviledged op methods - This returns an opaque object that is passed to NSFileManager, + // so probably not terribly important } diff --git a/src/appkit/workspace/running_application.rs b/src/appkit/workspace/running_application.rs index 22a6bd8e..58809144 100644 --- a/src/appkit/workspace/running_application.rs +++ b/src/appkit/workspace/running_application.rs @@ -128,7 +128,7 @@ impl RunningApplication { /// Indicates the date when the application was launched. pub fn launch_date(&self) -> Option { - unimplemented!("No NSDate implementation") + unimplemented!("Missing NSDate implementation") } /// Indicates whether the receiver’s process has finished launching. diff --git a/src/foundation/array.rs b/src/foundation/array.rs index ea6df757..e1db2a5d 100644 --- a/src/foundation/array.rs +++ b/src/foundation/array.rs @@ -13,7 +13,7 @@ use super::Retainable; /// ever deemed necessary (unlikely, given how much Apple has optimized the Foundation classes, but /// still...). #[derive(Debug)] -pub struct NSArray(pub Id); +pub struct NSArray(Id); impl NSArray { /// Given a set of `Object`s, creates and retains an `NSArray` that holds them. diff --git a/src/foundation/dictionary.rs b/src/foundation/dictionary.rs index 58909bf6..377a05a6 100644 --- a/src/foundation/dictionary.rs +++ b/src/foundation/dictionary.rs @@ -38,9 +38,15 @@ impl NSMutableDictionary { } } - /// Consumes and returns the underlying `NSMutableDictionary`. - pub fn into_inner(mut self) -> id { - &mut *self.0 +} + +impl Retainable for NSMutableDictionary { + fn retain(object: id) -> Self { + unsafe { NSMutableDictionary(Id::from_ptr(object)) } + } + + fn from_retained(object: id) -> Self { + unsafe { NSMutableDictionary(Id::from_retained_ptr(object)) } } } From 8c4fe06fa68ac30597502975173cf062cb2a0881 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 21 Jul 2023 10:59:03 -0700 Subject: [PATCH 4/9] Convenience methods for dictionary --- src/appkit/workspace/mod.rs | 46 +++++--- src/appkit/workspace/running_application.rs | 33 +++--- src/foundation/dictionary.rs | 117 +++++++++++++++++++- 3 files changed, 165 insertions(+), 31 deletions(-) diff --git a/src/appkit/workspace/mod.rs b/src/appkit/workspace/mod.rs index df9e92ba..57e6d600 100644 --- a/src/appkit/workspace/mod.rs +++ b/src/appkit/workspace/mod.rs @@ -2,11 +2,13 @@ use std::collections::HashMap; use block::ConcreteBlock; use objc::{class, msg_send, runtime::Object, sel, sel_impl}; +use objc_id::Id; use crate::{ color::Color, error::Error, - foundation::{id, nil, NSArray, NSInteger, NSMutableDictionary, NSString, Retainable, NSURL} + foundation::{id, nil, NSArray, NSInteger, NSMutableDictionary, NSString, Retainable, NSURL}, + notification_center::NotificationCenter, }; use self::running_application::RunningApplication; @@ -14,7 +16,7 @@ use self::running_application::RunningApplication; pub mod running_application; #[derive(Debug)] -pub struct Workspace(id); +pub struct Workspace(Id); #[derive(Debug)] pub struct GetFileSystemInfoForPathResponse { @@ -22,7 +24,7 @@ pub struct GetFileSystemInfoForPathResponse { pub writable: bool, pub unmountable: bool, pub description: String, - pub file_system_type: String + pub file_system_type: String, } impl Workspace { @@ -30,14 +32,16 @@ impl Workspace { pub fn shared() -> Self { let workspace: id = unsafe { msg_send![class!(NSWorkspace), sharedWorkspace] }; - Workspace(workspace) + Workspace::retain(workspace) } // Accessing the Workspace Notification Center /// The notification center for workspace notifications. - pub fn notification_center() -> Self { - unimplemented!("Missing NSNotificationCenter implementation") + pub fn notification_center(&self) -> NotificationCenter { + let notification: id = unsafe { msg_send![self.0, notificationCenter] }; + + NotificationCenter::retain(notification) } // Opening URLs @@ -47,7 +51,7 @@ impl Workspace { /// Maps to `openURL:configuration:completionHandler:` pub fn open_url_with_completion(&self, _url: &str, _configuration: &str, _completion_handler: F) where - F: FnOnce() -> () + F: FnOnce() -> (), { unimplemented!("Missing NSWorkspaceOpenConfiguration implementation. Only >=10.15"); } @@ -58,9 +62,9 @@ impl Workspace { _url: &str, _application_url: &str, _configuration: &str, - _completion_handler: F + _completion_handler: F, ) where - F: FnOnce() -> () + F: FnOnce() -> (), { unimplemented!("Missing NSWorkspaceOpenConfiguration implementation. Only >=10.15"); } @@ -80,7 +84,7 @@ impl Workspace { /// Launches the app at the specified URL and asynchronously reports back on the app's status. pub fn open_application_at_url(&self, _application_url: &str, _configuration: &str, _completion_handler: F) where - F: FnOnce() -> () + F: FnOnce() -> (), { unimplemented!("Missing NSWorkspaceOpenConfiguration implementation. Only >=10.15"); } @@ -95,7 +99,7 @@ impl Workspace { /// Duplicates the specified URLS asynchronously in the same manner as the Finder. pub fn duplicate_urls(&self, _urls: Vec<&str>, _completion_handler: Option) where - F: Fn(HashMap, Error) -> () + Send + Sync + 'static + F: Fn(HashMap, Error) -> () + Send + Sync + 'static, { unimplemented!("Missing ability to create NSArrays of NSURL"); // let urls = urls @@ -128,7 +132,7 @@ impl Workspace { /// Moves the specified URLs to the trash in the same manner as the Finder. pub fn recycle_urls(&self, _urls: Vec<&str>, _completion_handler: Option) where - F: Fn(HashMap, Error) -> () + Send + Sync + 'static + F: Fn(HashMap, Error) -> () + Send + Sync + 'static, { unimplemented!("Missing ability to create NSArrays of NSURL"); } @@ -196,7 +200,7 @@ impl Workspace { writable: *writable, unmountable: *unmountable, description: description.to_string(), - file_system_type: file_system_type.to_string() + file_system_type: file_system_type.to_string(), }) } else { None @@ -215,7 +219,7 @@ impl Workspace { let id: id = unsafe { msg_send![self.0, frontmostApplication] }; if !id.is_null() { - Some(RunningApplication::new(id)) + Some(RunningApplication::retain(id)) } else { None } @@ -225,7 +229,7 @@ impl Workspace { pub fn running_applications(&self) -> Vec { let apps: id = unsafe { msg_send![self.0, runningApplications] }; - NSArray::retain(apps).iter().map(|a| RunningApplication::new(a)).collect() + NSArray::retain(apps).iter().map(|a| RunningApplication::retain(a)).collect() } /// Returns the app that owns the currently displayed menu bar. @@ -233,7 +237,7 @@ impl Workspace { let id: id = unsafe { msg_send![self.0, menuBarOwningApplication] }; if !id.is_null() { - Some(RunningApplication::new(id)) + Some(RunningApplication::retain(id)) } else { None } @@ -297,3 +301,13 @@ impl Workspace { // TODO: Needs priviledged op methods - This returns an opaque object that is passed to NSFileManager, // so probably not terribly important } + +impl Retainable for Workspace { + fn retain(handle: id) -> Self { + Workspace(unsafe { Id::from_ptr(handle) }) + } + + fn from_retained(handle: id) -> Self { + Workspace(unsafe { Id::from_retained_ptr(handle) }) + } +} diff --git a/src/appkit/workspace/running_application.rs b/src/appkit/workspace/running_application.rs index 58809144..e7336dad 100644 --- a/src/appkit/workspace/running_application.rs +++ b/src/appkit/workspace/running_application.rs @@ -1,27 +1,24 @@ use std::f32::consts::E; use bitmask_enum::bitmask; -use objc::{class, msg_send, sel, sel_impl}; +use objc::{class, msg_send, runtime::Object, sel, sel_impl}; +use objc_id::Id; use crate::{ foundation::{id, NSArray, NSInteger, NSString, NSUInteger, Retainable, NSURL}, - image::Image + image::Image, }; #[derive(Debug)] -pub struct RunningApplication(id); +pub struct RunningApplication(Id); impl RunningApplication { - pub fn new(running_app: id) -> Self { - RunningApplication(running_app) - } - /// Returns the running application with the given process identifier, or `None` if no application has that pid. pub fn from_process_identifier(pid: usize) -> Option { let id: id = unsafe { msg_send![class!(NSRunningApplication), runningApplicationWithProcessIdentifier:pid] }; if !id.is_null() { - Some(RunningApplication::new(id)) + Some(RunningApplication::retain(id)) } else { None } @@ -32,14 +29,14 @@ impl RunningApplication { let identifier = NSString::new(identifier); let id: id = unsafe { msg_send![class!(NSRunningApplication), runningApplicationsWithBundleIdentifier:identifier] }; - NSArray::retain(id).iter().map(|a| RunningApplication(a)).collect() + NSArray::retain(id).iter().map(|a| RunningApplication::retain(a)).collect() } /// Returns an NSRunningApplication representing this application. pub fn current() -> Self { let id: id = unsafe { msg_send![class!(NSRunningApplication), currentApplication] }; - Self::new(id) + Self::retain(id) } // Activating applications @@ -63,7 +60,7 @@ impl RunningApplication { 1 => ApplicationActivationPolicy::Accessory, 2 => ApplicationActivationPolicy::Prohibited, - _ => ApplicationActivationPolicy::Regular + _ => ApplicationActivationPolicy::Regular, } } @@ -169,6 +166,16 @@ impl RunningApplication { } } +impl Retainable for RunningApplication { + fn retain(app: id) -> Self { + RunningApplication(unsafe { Id::from_ptr(app) }) + } + + fn from_retained(app: id) -> Self { + RunningApplication(unsafe { Id::from_retained_ptr(app) }) + } +} + #[bitmask(u32)] /// Flags are for `activate_with_options` pub enum ApplicationActivationOptions { @@ -180,7 +187,7 @@ pub enum ApplicationActivationOptions { ActivateAllWindows = 1 << 0, /// The application is activated regardless of the currently active app. - ActivateIgnoringOtherApps = 1 << 1 + ActivateIgnoringOtherApps = 1 << 1, } /// Activation policies (used by `activationPolicy`) that control whether and how an app may be activated. @@ -194,5 +201,5 @@ pub enum ApplicationActivationPolicy { Accessory = 1, /// The application doesn’t appear in the Dock and may not create windows or be activated. - Prohibited = 2 + Prohibited = 2, } diff --git a/src/foundation/dictionary.rs b/src/foundation/dictionary.rs index 377a05a6..4e3b3413 100644 --- a/src/foundation/dictionary.rs +++ b/src/foundation/dictionary.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use objc::runtime::Object; @@ -7,9 +8,11 @@ use objc_id::Id; use crate::foundation::{id, NSString}; +use super::{NSArray, Retainable}; + /// A wrapper for `NSMutableDictionary`. #[derive(Debug)] -pub struct NSMutableDictionary(pub Id); +pub struct NSMutableDictionary(Id); impl Default for NSMutableDictionary { /// Returns a blank NSMutableDictionary. @@ -29,7 +32,12 @@ impl NSMutableDictionary { NSMutableDictionary(unsafe { Id::from_ptr(msg_send![class!(NSMutableDictionary), new]) }) } - /// Inserts an object into the backing NSMutablyDictionary. + /// Consumes and returns the underlying `NSMutableDictionary`. + pub fn into_inner(mut self) -> id { + &mut *self.0 + } + + /// Inserts an object into the backing NSMutableDictionary. Corresponds to setObject:forKey: /// /// This intentionally requires `NSString` be allocated ahead of time. pub fn insert(&mut self, key: NSString, object: id) { @@ -38,6 +46,96 @@ impl NSMutableDictionary { } } + /// Returns the value associated with a given key. Corresponds to objectForKey: + pub fn get(&self, key: &str) -> Option { + let key = NSString::new(key); + let id: id = unsafe { msg_send![self.0, objectForKey: key] }; + + if !id.is_null() { + Some(id) + } else { + None + } + } + + /// A new array containing the dictionary’s keys, or an empty array if the dictionary has no entries. Corresponds to allKeys + /// + /// **NOTE:** This only works with string keys + pub fn keys(&self) -> Vec { + let keys = NSArray::retain(unsafe { msg_send![self.0, allKeys] }); + keys.iter().map(|s| NSString::retain(s).to_string()).collect() + } + + /// Converts the dictionary into a hashmap, passing each item through a transform function. + /// + /// **NOTE:** This only works with string keys + pub fn into_hashmap(&self, item_transform: F) -> HashMap + where + F: Fn(&String, id) -> T, + { + let mut map = HashMap::new(); + + let keys = self.keys(); + + println!("{keys:?}"); + + for key in keys { + let item_id = self.get(&key); + + if let Some(item_id) = item_id { + let item = item_transform(&key, item_id); + + map.insert(key, item); + } else { + // TODO: Should there be an assertion here for runtime failure? + continue; + } + } + + map + } + + /// Returns an iterator over the `NSMutableDictionary`. + /// + /// **NOTE:** This only works with string keys + pub fn iter<'a>(&'a self) -> NSMutableDictionaryIterator<'a> { + let keys = self.keys(); + + NSMutableDictionaryIterator { + next_index: 0, + count: keys.len(), + keys, + dict: self, + } + } +} + +#[derive(Debug)] +pub struct NSMutableDictionaryIterator<'a> { + next_index: usize, + count: usize, + keys: Vec, + + dict: &'a NSMutableDictionary, +} + +impl Iterator for NSMutableDictionaryIterator<'_> { + type Item = (String, id); + + fn next(&mut self) -> Option { + if self.next_index < self.count { + // This could be optimized to not clone + let key = self.keys[self.next_index].clone(); + + let value = self.dict.get(&key).expect("Could not find key"); + + self.next_index += 1; + + Some((key, value)) + } else { + None + } + } } impl Retainable for NSMutableDictionary { @@ -65,3 +163,18 @@ impl DerefMut for NSMutableDictionary { &mut *self.0 } } + +impl From<&HashMap> for NSMutableDictionary { + fn from(value: &HashMap) -> Self { + let mut dictionary = Self::new(); + + for (key, value) in value.iter() { + let key = NSString::new(key); + let mut value = NSString::new(value); + + dictionary.insert(key, &mut *value); + } + + dictionary + } +} From f6b5d7211463b2be26e251e0dcd4e2a0ded470d7 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 21 Jul 2023 11:20:13 -0700 Subject: [PATCH 5/9] NSNotification and basic NSNotificationCenter --- Cargo.toml | 2 + src/notification_center/mod.rs | 72 ++++++++++++++++++++----- src/notification_center/name.rs | 61 +++++++++++++++++++-- src/notification_center/notification.rs | 68 +++++++++++++++++++++++ 4 files changed, 186 insertions(+), 17 deletions(-) create mode 100644 src/notification_center/notification.rs diff --git a/Cargo.toml b/Cargo.toml index 3398f75e..f83d1e53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,8 @@ objc_id = "0.1.1" os_info = "3.0.1" url = "2.1.1" uuid = { version = "1.1", features = ["v4"], optional = true } +strum = "0.25" +strum_macros = "0.25" [dev-dependencies] eval = "0.4" diff --git a/src/notification_center/mod.rs b/src/notification_center/mod.rs index c62ed586..d96f50c4 100644 --- a/src/notification_center/mod.rs +++ b/src/notification_center/mod.rs @@ -15,17 +15,24 @@ //use std::sync::Mutex; //use std::collections::HashMap; +use block::ConcreteBlock; //use lazy_static::lazy_static; -//use objc::{class, msg_send, sel, sel_impl}; -//use objc::runtime::Object; -//use objc_id::ShareId; +use objc::runtime::Object; +use objc::{class, msg_send, sel, sel_impl}; +use objc_id::{Id, ShareId}; mod name; pub use name::NotificationName; +mod notification; + mod traits; pub use traits::Dispatcher; +use crate::foundation::{id, nil, NSString, Retainable}; + +use self::notification::Notification; + /*lazy_static! { pub static ref DefaultNotificationCenter: NotificationCenter = { NotificationCenter { @@ -40,23 +47,62 @@ pub use traits::Dispatcher; // Wraps a reference to an `NSNotificationCenter` instance. Currently this only supports the // default center; in the future it should aim to support custom variants. -//#[derive(Debug)] -//pub struct NotificationCenter { -// pub objc: ShareId, -//pub subscribers: Mutex>> -//} +#[derive(Debug)] +pub struct NotificationCenter { + pub objc: ShareId, + // pub subscribers: Mutex>> +} -/*impl Default for NotificationCenter { +impl Default for NotificationCenter { /// Returns a wrapper over `[NSNotificationCenter defaultCenter]`. From here you can handle /// observing, removing, and posting notifications. fn default() -> Self { NotificationCenter { - objc: unsafe { - ShareId::from_ptr(msg_send![class!(NSNotificationCenter), defaultCenter]) - } + objc: unsafe { ShareId::from_ptr(msg_send![class!(NSNotificationCenter), defaultCenter]) }, } } -}*/ +} + +impl NotificationCenter { + /// Adds an entry to the notification center to receive notifications that passed to the provided block. + /// Corresponds to `addObserverForName:object:queue:usingBlock:` + /// + /// TODO: Missing `object` and `queue` properties, so this receives all notifications matching the name, and + /// on the same thread as the notification was posted + pub fn observe () + Send + Sync + 'static>(&self, name: Option, block: F) -> id { + let block = ConcreteBlock::new(move |ctx| { + let notification = Notification::retain(ctx); + block(notification); + }); + let block = block.copy(); + + if let Some(name) = name { + let name: NSString = name.into(); + + let id: id = unsafe { msg_send![self.objc, addObserverForName: name object: nil queue: nil usingBlock: block] }; + + id + } else { + let id: id = unsafe { msg_send![self.objc, addObserverForName: nil object: nil queue: nil usingBlock: block] }; + + id + } + } +} + +impl Retainable for NotificationCenter { + fn retain(handle: id) -> Self { + NotificationCenter { + objc: unsafe { Id::from_ptr(handle) }, + } + } + + fn from_retained(handle: id) -> Self { + NotificationCenter { + objc: unsafe { Id::from_retained_ptr(handle) }, + } + } +} /*impl NotificationCenter { pub fn observe(&self, name: &str, handler: &T) { diff --git a/src/notification_center/name.rs b/src/notification_center/name.rs index b2613ab7..8d704bbd 100644 --- a/src/notification_center/name.rs +++ b/src/notification_center/name.rs @@ -1,11 +1,17 @@ +use std::{convert::TryInto, fmt::Display}; + +use strum_macros::{AsRefStr, EnumString}; + #[allow(non_camel_case_types)] use crate::foundation::NSString; +static NOTIFICATION_SUFFIX: &'static str = "Notification"; + /// An enum that wraps NSNotificationName. /// /// Since this framework utilizes Objective-C, these are ultimately backed by `NSString`... but we /// want them to be a bit more type-friendly and autocomplete-able. -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug, AsRefStr, EnumString)] pub enum NotificationName { /// Posted when the audio engine config changes. /// @@ -1310,13 +1316,60 @@ pub enum NotificationName { SKStorefrontCountryCodeDidChange, /// - WKAccessibilityReduceMotionStatusDidChange + WKAccessibilityReduceMotionStatusDidChange, + + /// A custom or unknown NSNotificationName + #[strum(default)] + Custom(String), } -impl From for NSString<'_> { +impl From for String { fn from(name: NotificationName) -> Self { + let full_name = format!("{}{NOTIFICATION_SUFFIX}", name.as_ref()); + match name { - _ => NSString::no_copy("") + // Custom does not have a `*Notification` suffix added + NotificationName::Custom(value) => value, + _ => full_name, } } } + +impl From for NSString<'_> { + fn from(name: NotificationName) -> Self { + let name: String = name.into(); + NSString::new(&name) + } +} + +impl From> for NotificationName { + fn from(value: NSString<'_>) -> Self { + value.to_string().into() + } +} + +impl From for NotificationName { + fn from(value: String) -> Self { + if let Some(value) = value.strip_suffix(NOTIFICATION_SUFFIX) { + // Has notification suffix, find enum + let variant = TryInto::::try_into(value); + + if let Ok(variant) = variant { + variant + } else { + // Could not match name to any enum. Return custom + NotificationName::Custom(value.into()) + } + } else { + // No notification suffix, must be custom + NotificationName::Custom(value.into()) + } + } +} + +impl Display for NotificationName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let string: String = self.clone().into(); + write!(f, "{}", string) + } +} diff --git a/src/notification_center/notification.rs b/src/notification_center/notification.rs new file mode 100644 index 00000000..3b996b19 --- /dev/null +++ b/src/notification_center/notification.rs @@ -0,0 +1,68 @@ +use std::collections::HashMap; + +use objc::{class, msg_send, runtime::Object, sel, sel_impl}; +use objc_id::Id; + +use crate::foundation::{id, nil, NSMutableDictionary, NSString, Retainable}; + +use super::NotificationName; + +#[derive(Debug)] +pub struct Notification(Id); + +impl Notification { + /// Returns a notification object with a specified name, object, and user information. Corresponds to notificationWithName:object:userInfo: + /// + /// Due to Rust typing limitations, `user_info` is only over `String` keys and values + pub fn new(name: NotificationName, object: Option, user_info: Option<&HashMap>) -> Self { + let name: NSString = name.into(); + + let user_info = user_info.and_then(|user_info| { + let user_info = NSMutableDictionary::from(user_info); + + Some(user_info) + }); + + let id: id = match (object, user_info) { + (None, None) => unsafe { msg_send![class!(NSNotification), notificationWithName: name object: nil] }, + (None, Some(user_info)) => unsafe { + msg_send![class!(NSNotification), notificationWithName: name object: nil userInfo: user_info] + }, + (Some(object), None) => unsafe { msg_send![class!(NSNotification), notificationWithName: name object: object] }, + (Some(object), Some(user_info)) => unsafe { + msg_send![class!(NSNotification), notificationWithName: name object: object userInfo: user_info] + }, + }; + + Notification::retain(id) + } + + /// The name of the notification. + pub fn name(&self) -> NotificationName { + let name = NSString::retain(unsafe { msg_send![self.0, name] }); + + name.into() + } + + /// The object associated with the notification. + pub fn object(&self) -> id { + unsafe { msg_send![self.0, object] } + } + + /// The user information dictionary associated with the notification. + /// + /// Due to complexity in possible key-value pairs, this returns the entire dictionary to consumers + pub fn user_info(&self) -> NSMutableDictionary where { + NSMutableDictionary::retain(unsafe { msg_send![self.0, userInfo] }) + } +} + +impl Retainable for Notification { + fn retain(handle: id) -> Self { + Notification(unsafe { Id::from_ptr(handle) }) + } + + fn from_retained(handle: id) -> Self { + Notification(unsafe { Id::from_retained_ptr(handle) }) + } +} From f787a81a67473f03af1c259d492bed4c6de997ac Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 21 Jul 2023 12:42:27 -0700 Subject: [PATCH 6/9] Observer removal and notification posting --- src/notification_center/mod.rs | 85 ++++++++++++------------- src/notification_center/notification.rs | 2 +- src/notification_center/traits.rs | 2 + 3 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/notification_center/mod.rs b/src/notification_center/mod.rs index d96f50c4..f9004621 100644 --- a/src/notification_center/mod.rs +++ b/src/notification_center/mod.rs @@ -25,41 +25,23 @@ mod name; pub use name::NotificationName; mod notification; +pub use notification::Notification; mod traits; pub use traits::Dispatcher; use crate::foundation::{id, nil, NSString, Retainable}; -use self::notification::Notification; - -/*lazy_static! { - pub static ref DefaultNotificationCenter: NotificationCenter = { - NotificationCenter { - objc: unsafe { - ShareId::from_ptr(msg_send![class!(NSNotificationCenter), defaultCenter]) - }, - - subscribers: Mutex::new(HashMap::new()) - } - }; -}*/ - // Wraps a reference to an `NSNotificationCenter` instance. Currently this only supports the // default center; in the future it should aim to support custom variants. #[derive(Debug)] -pub struct NotificationCenter { - pub objc: ShareId, - // pub subscribers: Mutex>> -} +pub struct NotificationCenter(ShareId); impl Default for NotificationCenter { /// Returns a wrapper over `[NSNotificationCenter defaultCenter]`. From here you can handle /// observing, removing, and posting notifications. fn default() -> Self { - NotificationCenter { - objc: unsafe { ShareId::from_ptr(msg_send![class!(NSNotificationCenter), defaultCenter]) }, - } + NotificationCenter(unsafe { ShareId::from_ptr(msg_send![class!(NSNotificationCenter), defaultCenter]) }) } } @@ -69,51 +51,68 @@ impl NotificationCenter { /// /// TODO: Missing `object` and `queue` properties, so this receives all notifications matching the name, and /// on the same thread as the notification was posted - pub fn observe () + Send + Sync + 'static>(&self, name: Option, block: F) -> id { + pub fn observe () + Send + Sync + 'static>( + &self, + name: Option, + block: F, + ) -> NotificationObserver { let block = ConcreteBlock::new(move |ctx| { let notification = Notification::retain(ctx); block(notification); }); let block = block.copy(); - if let Some(name) = name { + let id: id = if let Some(name) = name { let name: NSString = name.into(); - let id: id = unsafe { msg_send![self.objc, addObserverForName: name object: nil queue: nil usingBlock: block] }; - - id + unsafe { msg_send![self.0, addObserverForName: name object: nil queue: nil usingBlock: block] } } else { - let id: id = unsafe { msg_send![self.objc, addObserverForName: nil object: nil queue: nil usingBlock: block] }; + unsafe { msg_send![self.0, addObserverForName: nil object: nil queue: nil usingBlock: block] } + }; - id - } + NotificationObserver::new(id, self) + } + + /// Posts a given notification to the notification center. Corresponds to `postNotification:` + pub fn post(&self, notification: Notification) { + unsafe { msg_send![self.0, postNotification: notification.0] } } } impl Retainable for NotificationCenter { fn retain(handle: id) -> Self { - NotificationCenter { - objc: unsafe { Id::from_ptr(handle) }, - } + NotificationCenter(unsafe { Id::from_ptr(handle) }) } fn from_retained(handle: id) -> Self { - NotificationCenter { - objc: unsafe { Id::from_retained_ptr(handle) }, - } + NotificationCenter(unsafe { Id::from_retained_ptr(handle) }) } } -/*impl NotificationCenter { - pub fn observe(&self, name: &str, handler: &T) { - - } - - pub fn remove(&self, name: &str, handler: &T) { +#[derive(Debug)] +pub struct NotificationObserver { + objc: ShareId, + notification_center: ShareId, +} +impl NotificationObserver { + fn new(observer: id, notification_center: &NotificationCenter) -> Self { + NotificationObserver { + objc: unsafe { ShareId::from_ptr(observer) }, + notification_center: notification_center.0.clone(), + } } - pub fn post(&self, name: &str) { + /// Removes matching entries from the notification center's dispatch table. Corresponds to removeObserver:name:object: + /// + /// TODO: Missing object property + pub fn remove(self, name: Option) { + if let Some(name) = name { + let name: NSString = name.into(); + unsafe { msg_send![self.notification_center, removeObserver: &*self.objc name: name object: nil] } + } else { + unsafe { msg_send![self.notification_center, removeObserver: &*self.objc name: nil object: nil] } + } } -}*/ +} diff --git a/src/notification_center/notification.rs b/src/notification_center/notification.rs index 3b996b19..4d55d03b 100644 --- a/src/notification_center/notification.rs +++ b/src/notification_center/notification.rs @@ -8,7 +8,7 @@ use crate::foundation::{id, nil, NSMutableDictionary, NSString, Retainable}; use super::NotificationName; #[derive(Debug)] -pub struct Notification(Id); +pub struct Notification(pub(crate) Id); impl Notification { /// Returns a notification object with a specified name, object, and user information. Corresponds to notificationWithName:object:userInfo: diff --git a/src/notification_center/traits.rs b/src/notification_center/traits.rs index 7e00dfa4..4ae938a6 100644 --- a/src/notification_center/traits.rs +++ b/src/notification_center/traits.rs @@ -1,3 +1,5 @@ +// TODO: This should be moved elsewhere + /// A trait for handling dispatched messages on the AppDelegate. /// /// You can use this for a jank message dispatching mechanism. It has no guarantees concerning From 1d2721e23a3446f25f015c36b53c93fe461817d6 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 21 Jul 2023 12:45:18 -0700 Subject: [PATCH 7/9] Removed accidentally committed print --- src/foundation/dictionary.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/foundation/dictionary.rs b/src/foundation/dictionary.rs index 4e3b3413..92a43ddc 100644 --- a/src/foundation/dictionary.rs +++ b/src/foundation/dictionary.rs @@ -77,8 +77,6 @@ impl NSMutableDictionary { let keys = self.keys(); - println!("{keys:?}"); - for key in keys { let item_id = self.get(&key); From cf2f07c5fabb37affe77ac0ca0b212edebdeb227 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 21 Jul 2023 13:05:21 -0700 Subject: [PATCH 8/9] Forgot formatting --- src/appkit/workspace/mod.rs | 18 +++++++++--------- src/appkit/workspace/running_application.rs | 8 ++++---- src/foundation/dictionary.rs | 6 +++--- src/notification_center/mod.rs | 6 +++--- src/notification_center/name.rs | 4 ++-- src/notification_center/notification.rs | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/appkit/workspace/mod.rs b/src/appkit/workspace/mod.rs index 57e6d600..e4c48576 100644 --- a/src/appkit/workspace/mod.rs +++ b/src/appkit/workspace/mod.rs @@ -8,7 +8,7 @@ use crate::{ color::Color, error::Error, foundation::{id, nil, NSArray, NSInteger, NSMutableDictionary, NSString, Retainable, NSURL}, - notification_center::NotificationCenter, + notification_center::NotificationCenter }; use self::running_application::RunningApplication; @@ -24,7 +24,7 @@ pub struct GetFileSystemInfoForPathResponse { pub writable: bool, pub unmountable: bool, pub description: String, - pub file_system_type: String, + pub file_system_type: String } impl Workspace { @@ -51,7 +51,7 @@ impl Workspace { /// Maps to `openURL:configuration:completionHandler:` pub fn open_url_with_completion(&self, _url: &str, _configuration: &str, _completion_handler: F) where - F: FnOnce() -> (), + F: FnOnce() -> () { unimplemented!("Missing NSWorkspaceOpenConfiguration implementation. Only >=10.15"); } @@ -62,9 +62,9 @@ impl Workspace { _url: &str, _application_url: &str, _configuration: &str, - _completion_handler: F, + _completion_handler: F ) where - F: FnOnce() -> (), + F: FnOnce() -> () { unimplemented!("Missing NSWorkspaceOpenConfiguration implementation. Only >=10.15"); } @@ -84,7 +84,7 @@ impl Workspace { /// Launches the app at the specified URL and asynchronously reports back on the app's status. pub fn open_application_at_url(&self, _application_url: &str, _configuration: &str, _completion_handler: F) where - F: FnOnce() -> (), + F: FnOnce() -> () { unimplemented!("Missing NSWorkspaceOpenConfiguration implementation. Only >=10.15"); } @@ -99,7 +99,7 @@ impl Workspace { /// Duplicates the specified URLS asynchronously in the same manner as the Finder. pub fn duplicate_urls(&self, _urls: Vec<&str>, _completion_handler: Option) where - F: Fn(HashMap, Error) -> () + Send + Sync + 'static, + F: Fn(HashMap, Error) -> () + Send + Sync + 'static { unimplemented!("Missing ability to create NSArrays of NSURL"); // let urls = urls @@ -132,7 +132,7 @@ impl Workspace { /// Moves the specified URLs to the trash in the same manner as the Finder. pub fn recycle_urls(&self, _urls: Vec<&str>, _completion_handler: Option) where - F: Fn(HashMap, Error) -> () + Send + Sync + 'static, + F: Fn(HashMap, Error) -> () + Send + Sync + 'static { unimplemented!("Missing ability to create NSArrays of NSURL"); } @@ -200,7 +200,7 @@ impl Workspace { writable: *writable, unmountable: *unmountable, description: description.to_string(), - file_system_type: file_system_type.to_string(), + file_system_type: file_system_type.to_string() }) } else { None diff --git a/src/appkit/workspace/running_application.rs b/src/appkit/workspace/running_application.rs index e7336dad..c91c2b6a 100644 --- a/src/appkit/workspace/running_application.rs +++ b/src/appkit/workspace/running_application.rs @@ -6,7 +6,7 @@ use objc_id::Id; use crate::{ foundation::{id, NSArray, NSInteger, NSString, NSUInteger, Retainable, NSURL}, - image::Image, + image::Image }; #[derive(Debug)] @@ -60,7 +60,7 @@ impl RunningApplication { 1 => ApplicationActivationPolicy::Accessory, 2 => ApplicationActivationPolicy::Prohibited, - _ => ApplicationActivationPolicy::Regular, + _ => ApplicationActivationPolicy::Regular } } @@ -187,7 +187,7 @@ pub enum ApplicationActivationOptions { ActivateAllWindows = 1 << 0, /// The application is activated regardless of the currently active app. - ActivateIgnoringOtherApps = 1 << 1, + ActivateIgnoringOtherApps = 1 << 1 } /// Activation policies (used by `activationPolicy`) that control whether and how an app may be activated. @@ -201,5 +201,5 @@ pub enum ApplicationActivationPolicy { Accessory = 1, /// The application doesn’t appear in the Dock and may not create windows or be activated. - Prohibited = 2, + Prohibited = 2 } diff --git a/src/foundation/dictionary.rs b/src/foundation/dictionary.rs index 92a43ddc..3779da4f 100644 --- a/src/foundation/dictionary.rs +++ b/src/foundation/dictionary.rs @@ -71,7 +71,7 @@ impl NSMutableDictionary { /// **NOTE:** This only works with string keys pub fn into_hashmap(&self, item_transform: F) -> HashMap where - F: Fn(&String, id) -> T, + F: Fn(&String, id) -> T { let mut map = HashMap::new(); @@ -103,7 +103,7 @@ impl NSMutableDictionary { next_index: 0, count: keys.len(), keys, - dict: self, + dict: self } } } @@ -114,7 +114,7 @@ pub struct NSMutableDictionaryIterator<'a> { count: usize, keys: Vec, - dict: &'a NSMutableDictionary, + dict: &'a NSMutableDictionary } impl Iterator for NSMutableDictionaryIterator<'_> { diff --git a/src/notification_center/mod.rs b/src/notification_center/mod.rs index f9004621..6ba99804 100644 --- a/src/notification_center/mod.rs +++ b/src/notification_center/mod.rs @@ -54,7 +54,7 @@ impl NotificationCenter { pub fn observe () + Send + Sync + 'static>( &self, name: Option, - block: F, + block: F ) -> NotificationObserver { let block = ConcreteBlock::new(move |ctx| { let notification = Notification::retain(ctx); @@ -92,14 +92,14 @@ impl Retainable for NotificationCenter { #[derive(Debug)] pub struct NotificationObserver { objc: ShareId, - notification_center: ShareId, + notification_center: ShareId } impl NotificationObserver { fn new(observer: id, notification_center: &NotificationCenter) -> Self { NotificationObserver { objc: unsafe { ShareId::from_ptr(observer) }, - notification_center: notification_center.0.clone(), + notification_center: notification_center.0.clone() } } diff --git a/src/notification_center/name.rs b/src/notification_center/name.rs index 8d704bbd..f9363230 100644 --- a/src/notification_center/name.rs +++ b/src/notification_center/name.rs @@ -1320,7 +1320,7 @@ pub enum NotificationName { /// A custom or unknown NSNotificationName #[strum(default)] - Custom(String), + Custom(String) } impl From for String { @@ -1330,7 +1330,7 @@ impl From for String { match name { // Custom does not have a `*Notification` suffix added NotificationName::Custom(value) => value, - _ => full_name, + _ => full_name } } } diff --git a/src/notification_center/notification.rs b/src/notification_center/notification.rs index 4d55d03b..223ea0f0 100644 --- a/src/notification_center/notification.rs +++ b/src/notification_center/notification.rs @@ -31,7 +31,7 @@ impl Notification { (Some(object), None) => unsafe { msg_send![class!(NSNotification), notificationWithName: name object: object] }, (Some(object), Some(user_info)) => unsafe { msg_send![class!(NSNotification), notificationWithName: name object: object userInfo: user_info] - }, + } }; Notification::retain(id) From 9f727eaef15d6eefc5ef97a2fedb3b9c3f424b59 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 21 Jul 2023 13:13:13 -0700 Subject: [PATCH 9/9] Fixed bad use --- src/input/mod.rs | 4 +--- src/text/label/mod.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 4c54f704..98c53a96 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -49,7 +49,7 @@ use objc_id::ShareId; use crate::color::Color; use crate::control::Control; -use crate::foundation::{id, nil, NSArray, NSInteger, NSString, NO, YES}; +use crate::foundation::{id, nil, NSArray, NSInteger, NSString, Retainable, NO, YES}; use crate::layout::Layout; use crate::objc_access::ObjcAccess; use crate::text::{Font, TextAlign}; @@ -294,8 +294,6 @@ impl TextField { /// Grabs the value from the textfield and returns it as an owned String. #[cfg(feature = "appkit")] pub fn get_value(&self) -> String { - use crate::foundation::Retainable; - self.objc .get(|obj| unsafe { NSString::retain(msg_send![obj, stringValue]).to_string() }) } diff --git a/src/text/label/mod.rs b/src/text/label/mod.rs index 1c971e2e..8ce2f942 100644 --- a/src/text/label/mod.rs +++ b/src/text/label/mod.rs @@ -48,7 +48,7 @@ use objc::{msg_send, sel, sel_impl}; use objc_id::ShareId; use crate::color::Color; -use crate::foundation::{id, nil, NSArray, NSInteger, NSString, NSUInteger, NO, YES}; +use crate::foundation::{id, nil, NSArray, NSInteger, NSString, NSUInteger, Retainable, NO, YES}; use crate::layer::Layer; use crate::layout::Layout; use crate::objc_access::ObjcAccess; @@ -365,8 +365,6 @@ impl Label { /// Retrieve the text currently held in the label. #[cfg(feature = "appkit")] pub fn get_text(&self) -> String { - use crate::foundation::Retainable; - self.objc .get(|obj| unsafe { NSString::retain(msg_send![obj, stringValue]).to_string() }) }