From 4d3af41ef6b224faec48116313abfa8a9605a400 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 29 Jul 2022 16:09:05 +0200 Subject: [PATCH] Add a few safe NSValue getters --- objc2/CHANGELOG_FOUNDATION.md | 2 + objc2/src/foundation/geometry.rs | 6 +-- objc2/src/foundation/value.rs | 93 ++++++++++++++++++++++++++++---- 3 files changed, 89 insertions(+), 12 deletions(-) diff --git a/objc2/CHANGELOG_FOUNDATION.md b/objc2/CHANGELOG_FOUNDATION.md index 7a86bccbd..c07d32e46 100644 --- a/objc2/CHANGELOG_FOUNDATION.md +++ b/objc2/CHANGELOG_FOUNDATION.md @@ -17,6 +17,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Add extra `Extend<&u8>` impl for `NSMutableData`. * Added function `NSValue::contains_encoding` for determining if the encoding of the `NSValue` matches the encoding of the given type. +* Added functions `get_range`, `get_point`, `get_size` and `get_rect` to + `NSValue` to help safely returning various types it will commonly contain. ### Changed * **BREAKING**: Moved from external crate `objc2_foundation` into diff --git a/objc2/src/foundation/geometry.rs b/objc2/src/foundation/geometry.rs index 878fe4943..e5476a853 100644 --- a/objc2/src/foundation/geometry.rs +++ b/objc2/src/foundation/geometry.rs @@ -25,9 +25,9 @@ pub type CGFloat = InnerFloat; not(all(target_os = "macos", target_pointer_width = "32")) ))] mod names { - pub(super) const POINT: &str = "_CGPoint"; - pub(super) const SIZE: &str = "_CGSize"; - pub(super) const RECT: &str = "_CGRect"; + pub(super) const POINT: &str = "CGPoint"; + pub(super) const SIZE: &str = "CGSize"; + pub(super) const RECT: &str = "CGRect"; } #[cfg(any( diff --git a/objc2/src/foundation/value.rs b/objc2/src/foundation/value.rs index 8821daa80..1888a6902 100644 --- a/objc2/src/foundation/value.rs +++ b/objc2/src/foundation/value.rs @@ -8,7 +8,7 @@ use core::str; use std::ffi::{CStr, CString}; use std::os::raw::c_char; -use super::{NSCopying, NSObject}; +use super::{NSCopying, NSObject, NSPoint, NSRange, NSRect, NSSize}; use crate::rc::{Id, Shared}; use crate::Encode; use crate::{extern_class, msg_send, msg_send_bool, msg_send_id}; @@ -129,6 +129,54 @@ impl NSValue { unsafe { value.assume_init() } } + pub fn get_range(&self) -> Option { + if self.contains_encoding::() { + // SAFETY: We just checked that this contains an NSRange + Some(unsafe { msg_send![self, rangeValue] }) + } else { + None + } + } + + pub fn get_point(&self) -> Option { + if self.contains_encoding::() { + // SAFETY: We just checked that this contains an NSPoint + #[cfg(any(feature = "gnustep-1-7", target_os = "macos"))] + let res = unsafe { msg_send![self, pointValue] }; + #[cfg(all(feature = "apple", not(target_os = "macos")))] + let res = unsafe { msg_send![self, CGPointValue] }; + Some(res) + } else { + None + } + } + + pub fn get_size(&self) -> Option { + if self.contains_encoding::() { + // SAFETY: We just checked that this contains an NSSize + #[cfg(any(feature = "gnustep-1-7", target_os = "macos"))] + let res = unsafe { msg_send![self, sizeValue] }; + #[cfg(all(feature = "apple", not(target_os = "macos")))] + let res = unsafe { msg_send![self, CGSizeValue] }; + Some(res) + } else { + None + } + } + + pub fn get_rect(&self) -> Option { + if self.contains_encoding::() { + // SAFETY: We just checked that this contains an NSRect + #[cfg(any(feature = "gnustep-1-7", target_os = "macos"))] + let res = unsafe { msg_send![self, rectValue] }; + #[cfg(all(feature = "apple", not(target_os = "macos")))] + let res = unsafe { msg_send![self, CGRectValue] }; + Some(res) + } else { + None + } + } + pub fn encoding(&self) -> Option<&str> { let result: Option> = unsafe { msg_send![self, objCType] }; result.map(|s| unsafe { CStr::from_ptr(s.as_ptr()) }.to_str().unwrap()) @@ -187,8 +235,6 @@ mod tests { use core::slice; use super::*; - use crate::foundation::NSRange; - use crate::msg_send; #[test] fn basic() { @@ -230,14 +276,43 @@ mod tests { } #[test] - fn test_value_nsrange() { - let val = NSValue::new(NSRange::from(1..2)); - assert!(val.contains_encoding::()); - let range: NSRange = unsafe { msg_send![&val, rangeValue] }; - assert_eq!(range, NSRange::from(1..2)); + fn nsrange() { + let range = NSRange::from(1..2); + let val = NSValue::new(range); + assert_eq!(val.get_range(), Some(range)); + assert_eq!(val.get_point(), None); + assert_eq!(val.get_size(), None); + assert_eq!(val.get_rect(), None); // NSValue -getValue is broken on GNUStep for some types #[cfg(not(feature = "gnustep-1-7"))] - assert_eq!(unsafe { val.get::() }, NSRange::from(1..2)); + assert_eq!(unsafe { val.get::() }, range); + } + + #[test] + fn nspoint() { + let point = NSPoint::new(1.0, 2.0); + let val = NSValue::new(point); + assert_eq!(val.get_point(), Some(point)); + #[cfg(not(feature = "gnustep-1-7"))] + assert_eq!(unsafe { val.get::() }, point); + } + + #[test] + fn nssize() { + let point = NSSize::new(1.0, 2.0); + let val = NSValue::new(point); + assert_eq!(val.get_size(), Some(point)); + #[cfg(not(feature = "gnustep-1-7"))] + assert_eq!(unsafe { val.get::() }, point); + } + + #[test] + fn nsrect() { + let rect = NSRect::new(NSPoint::new(1.0, 2.0), NSSize::new(3.0, 4.0)); + let val = NSValue::new(rect); + assert_eq!(val.get_rect(), Some(rect)); + #[cfg(not(feature = "gnustep-1-7"))] + assert_eq!(unsafe { val.get::() }, rect); } #[test]