From 876ff3ff5d36e64163bdd5a6e336283f8bd1e4d7 Mon Sep 17 00:00:00 2001 From: Isaac Leonard Date: Sun, 10 Sep 2023 15:12:05 +1000 Subject: [PATCH 1/6] Add trait to get layout anchors for a view, implement it on common components and add some functions that make use of it to simplify creating layout constraints --- src/layout/has_layout_impl.rs | 116 ++++++++++++++++++++++++++++++++++ src/layout/mod.rs | 2 + src/layout/traits.rs | 10 +++ 3 files changed, 128 insertions(+) create mode 100644 src/layout/has_layout_impl.rs diff --git a/src/layout/has_layout_impl.rs b/src/layout/has_layout_impl.rs new file mode 100644 index 00000000..99ef5c3f --- /dev/null +++ b/src/layout/has_layout_impl.rs @@ -0,0 +1,116 @@ +use super::traits::HasLayout; +use crate::{ + button::Button, + input::TextField, + layout::{LayoutAnchorX, LayoutAnchorY, LayoutConstraint, SafeAreaLayoutGuide}, + listview::ListView, + text::Label, + view::View, +}; + +/// Takes a list of views, a parent view that contains them and returns layout constraints that will position them from top to bottom separated by the specified padding. +/// The padding is also applied to the sides of each view. +pub fn top_to_bottom(views: Vec<&dyn HasLayout>, parent: &impl HasLayout, padding: f32) -> Vec { + let (top, bottom) = if let (Some(first), Some(last)) = (views.first(), views.last()) { + ( + first.get_top().constraint_equal_to(parent.get_top()).offset(padding), + last.get_bottom().constraint_equal_to(parent.get_bottom()).offset(padding), + ) + } else { + // No views were passed + return Vec::new(); + }; + let adjoining_constraints = views + .windows(2) + .map(|views| views[0].get_bottom().constraint_equal_to(views[1].get_top())); + let side_constraints = views + .iter() + .map(|view| [view.get_leading().constraint_equal_to(parent.get_leading()).offset(padding)]) + .flatten(); + vec![top, bottom] + .into_iter() + .chain(adjoining_constraints) + .chain(side_constraints) + .collect() +} + +/// Returns a list of layout constraints that makes the given view fill the given safe area +pub fn fill_safe_area(view: &impl HasLayout, safe_area: &SafeAreaLayoutGuide) -> Vec { + vec![ + view.get_top().constraint_equal_to(&safe_area.top), + view.get_bottom().constraint_equal_to(&safe_area.bottom), + view.get_leading().constraint_equal_to(&safe_area.leading), + view.get_trailing().constraint_equal_to(&safe_area.trailing), + ] +} + +impl HasLayout for Label { + fn get_top(&self) -> &LayoutAnchorY { + &self.top + } + fn get_bottom(&self) -> &LayoutAnchorY { + &self.bottom + } + fn get_leading(&self) -> &LayoutAnchorX { + &self.leading + } + fn get_trailing(&self) -> &LayoutAnchorX { + &self.trailing + } +} +impl HasLayout for TextField { + fn get_top(&self) -> &LayoutAnchorY { + &self.top + } + fn get_bottom(&self) -> &LayoutAnchorY { + &self.bottom + } + fn get_leading(&self) -> &LayoutAnchorX { + &self.leading + } + fn get_trailing(&self) -> &LayoutAnchorX { + &self.trailing + } +} +impl HasLayout for Button { + fn get_top(&self) -> &LayoutAnchorY { + &self.top + } + fn get_bottom(&self) -> &LayoutAnchorY { + &self.bottom + } + fn get_leading(&self) -> &LayoutAnchorX { + &self.leading + } + fn get_trailing(&self) -> &LayoutAnchorX { + &self.trailing + } +} +impl HasLayout for View { + fn get_top(&self) -> &LayoutAnchorY { + &self.top + } + fn get_bottom(&self) -> &LayoutAnchorY { + &self.bottom + } + fn get_leading(&self) -> &LayoutAnchorX { + &self.leading + } + fn get_trailing(&self) -> &LayoutAnchorX { + &self.trailing + } +} +impl HasLayout for ListView { + fn get_top(&self) -> &LayoutAnchorY { + &self.top + } + fn get_bottom(&self) -> &LayoutAnchorY { + &self.bottom + } + fn get_leading(&self) -> &LayoutAnchorX { + &self.leading + } + fn get_trailing(&self) -> &LayoutAnchorX { + &self.trailing + } +} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 38bafee4..85498bfe 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -47,3 +47,5 @@ mod safe_guide; #[cfg(feature = "autolayout")] pub use safe_guide::SafeAreaLayoutGuide; +#[cfg(feature = "autolayout")] +mod has_layout_impl; diff --git a/src/layout/traits.rs b/src/layout/traits.rs index 6249b175..4ac0964c 100644 --- a/src/layout/traits.rs +++ b/src/layout/traits.rs @@ -15,6 +15,8 @@ use crate::objc_access::ObjcAccess; #[cfg(feature = "appkit")] use crate::pasteboard::PasteboardType; +use super::{LayoutAnchorX, LayoutAnchorY}; + /// A trait that view wrappers must conform to. Enables managing the subview tree. #[allow(unused_variables)] pub trait Layout: ObjcAccess { @@ -173,3 +175,11 @@ pub trait Layout: ObjcAccess { }); } } + +/// A trait to access a views layout anchors +pub trait HasLayout { + fn get_top(&self) -> &LayoutAnchorY; + fn get_bottom(&self) -> &LayoutAnchorY; + fn get_leading(&self) -> &LayoutAnchorX; + fn get_trailing(&self) -> &LayoutAnchorX; +} From 8867b6c8c6e0b1d37cb0540b2191b5d4b4cb7c73 Mon Sep 17 00:00:00 2001 From: Isaac Leonard Date: Sat, 16 Sep 2023 10:16:43 +1000 Subject: [PATCH 2/6] Move get_top and related methods into the layout trait --- src/layout/has_layout_impl.rs | 116 ---------------------------------- src/layout/mod.rs | 2 - src/layout/traits.rs | 25 ++++++-- 3 files changed, 18 insertions(+), 125 deletions(-) delete mode 100644 src/layout/has_layout_impl.rs diff --git a/src/layout/has_layout_impl.rs b/src/layout/has_layout_impl.rs deleted file mode 100644 index 99ef5c3f..00000000 --- a/src/layout/has_layout_impl.rs +++ /dev/null @@ -1,116 +0,0 @@ -use super::traits::HasLayout; -use crate::{ - button::Button, - input::TextField, - layout::{LayoutAnchorX, LayoutAnchorY, LayoutConstraint, SafeAreaLayoutGuide}, - listview::ListView, - text::Label, - view::View, -}; - -/// Takes a list of views, a parent view that contains them and returns layout constraints that will position them from top to bottom separated by the specified padding. -/// The padding is also applied to the sides of each view. -pub fn top_to_bottom(views: Vec<&dyn HasLayout>, parent: &impl HasLayout, padding: f32) -> Vec { - let (top, bottom) = if let (Some(first), Some(last)) = (views.first(), views.last()) { - ( - first.get_top().constraint_equal_to(parent.get_top()).offset(padding), - last.get_bottom().constraint_equal_to(parent.get_bottom()).offset(padding), - ) - } else { - // No views were passed - return Vec::new(); - }; - let adjoining_constraints = views - .windows(2) - .map(|views| views[0].get_bottom().constraint_equal_to(views[1].get_top())); - let side_constraints = views - .iter() - .map(|view| [view.get_leading().constraint_equal_to(parent.get_leading()).offset(padding)]) - .flatten(); - vec![top, bottom] - .into_iter() - .chain(adjoining_constraints) - .chain(side_constraints) - .collect() -} - -/// Returns a list of layout constraints that makes the given view fill the given safe area -pub fn fill_safe_area(view: &impl HasLayout, safe_area: &SafeAreaLayoutGuide) -> Vec { - vec![ - view.get_top().constraint_equal_to(&safe_area.top), - view.get_bottom().constraint_equal_to(&safe_area.bottom), - view.get_leading().constraint_equal_to(&safe_area.leading), - view.get_trailing().constraint_equal_to(&safe_area.trailing), - ] -} - -impl HasLayout for Label { - fn get_top(&self) -> &LayoutAnchorY { - &self.top - } - fn get_bottom(&self) -> &LayoutAnchorY { - &self.bottom - } - fn get_leading(&self) -> &LayoutAnchorX { - &self.leading - } - fn get_trailing(&self) -> &LayoutAnchorX { - &self.trailing - } -} -impl HasLayout for TextField { - fn get_top(&self) -> &LayoutAnchorY { - &self.top - } - fn get_bottom(&self) -> &LayoutAnchorY { - &self.bottom - } - fn get_leading(&self) -> &LayoutAnchorX { - &self.leading - } - fn get_trailing(&self) -> &LayoutAnchorX { - &self.trailing - } -} -impl HasLayout for Button { - fn get_top(&self) -> &LayoutAnchorY { - &self.top - } - fn get_bottom(&self) -> &LayoutAnchorY { - &self.bottom - } - fn get_leading(&self) -> &LayoutAnchorX { - &self.leading - } - fn get_trailing(&self) -> &LayoutAnchorX { - &self.trailing - } -} -impl HasLayout for View { - fn get_top(&self) -> &LayoutAnchorY { - &self.top - } - fn get_bottom(&self) -> &LayoutAnchorY { - &self.bottom - } - fn get_leading(&self) -> &LayoutAnchorX { - &self.leading - } - fn get_trailing(&self) -> &LayoutAnchorX { - &self.trailing - } -} -impl HasLayout for ListView { - fn get_top(&self) -> &LayoutAnchorY { - &self.top - } - fn get_bottom(&self) -> &LayoutAnchorY { - &self.bottom - } - fn get_leading(&self) -> &LayoutAnchorX { - &self.leading - } - fn get_trailing(&self) -> &LayoutAnchorX { - &self.trailing - } -} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 85498bfe..38bafee4 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -47,5 +47,3 @@ mod safe_guide; #[cfg(feature = "autolayout")] pub use safe_guide::SafeAreaLayoutGuide; -#[cfg(feature = "autolayout")] -mod has_layout_impl; diff --git a/src/layout/traits.rs b/src/layout/traits.rs index 4ac0964c..d1488667 100644 --- a/src/layout/traits.rs +++ b/src/layout/traits.rs @@ -1,6 +1,8 @@ //! Various traits related to controllers opting in to autolayout routines and support for view //! heirarchies. +use objc::msg_send_id; + use core_graphics::base::CGFloat; use core_graphics::geometry::{CGPoint, CGRect, CGSize}; @@ -174,12 +176,21 @@ pub trait Layout: ObjcAccess { let _: () = msg_send![obj, setAlphaValue: value]; }); } -} -/// A trait to access a views layout anchors -pub trait HasLayout { - fn get_top(&self) -> &LayoutAnchorY; - fn get_bottom(&self) -> &LayoutAnchorY; - fn get_leading(&self) -> &LayoutAnchorX; - fn get_trailing(&self) -> &LayoutAnchorX; + #[cfg(feature = "appkit")] + fn get_top(&self) -> LayoutAnchorY { + self.get_from_backing_obj(|id| LayoutAnchorY::Top(unsafe { msg_send_id![id, topAnchor] })) + } + #[cfg(feature = "appkit")] + fn get_bottom(&self) -> LayoutAnchorY { + self.get_from_backing_obj(|id| LayoutAnchorY::Bottom(unsafe { msg_send_id![id, bottomAnchor] })) + } + #[cfg(feature = "appkit")] + fn get_leading(&self) -> LayoutAnchorX { + self.get_from_backing_obj(|id| LayoutAnchorX::Leading(unsafe { msg_send_id![id, leadingAnchor] })) + } + #[cfg(feature = "appkit")] + fn get_trailing(&self) -> LayoutAnchorX { + self.get_from_backing_obj(|id| LayoutAnchorX::Trailing(unsafe { msg_send_id![id, trailingAnchor] })) + } } From fcef04d6ff74b01a5d7fbcd749e51b97084baa7a Mon Sep 17 00:00:00 2001 From: Isaac Leonard Date: Sat, 16 Sep 2023 11:01:42 +1000 Subject: [PATCH 3/6] Add method to get access to Objc as an Id without having to pass a function Acked-by: Isaac Leonard --- src/utils/properties.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utils/properties.rs b/src/utils/properties.rs index 6ccb8eaa..da53c0c7 100644 --- a/src/utils/properties.rs +++ b/src/utils/properties.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::cell::{Ref, RefCell}; use std::rc::Rc; use objc::rc::{Id, Owned}; @@ -40,6 +40,10 @@ impl ObjcProperty { let obj = self.0.borrow(); handler(&**obj) } + + pub fn get_ref(&self) -> Ref<'_, Id> { + self.0.borrow() + } } /// A wrapper for a single-threaded nullable `Property`. @@ -57,7 +61,7 @@ impl PropertyNullable { pub fn with(&self, handler: F) where - F: Fn(&T) + F: Fn(&T), { let borrow = self.0.borrow(); if let Some(s) = &*borrow { From 9448238794ea02e67874b1b3f33f2ff91546fe10 Mon Sep 17 00:00:00 2001 From: Isaac Leonard Date: Sat, 16 Sep 2023 11:55:11 +1000 Subject: [PATCH 4/6] Remove generic parameter from with_backing_obj_mut method on the ObjcAccess trait --- src/appkit/segmentedcontrol.rs | 4 ++-- src/appkit/window/mod.rs | 2 +- src/button/mod.rs | 4 ++-- src/control/mod.rs | 4 ++-- src/image/mod.rs | 2 +- src/input/mod.rs | 2 +- src/layout/traits.rs | 24 ++++++++++++------------ src/listview/mod.rs | 2 +- src/listview/row/mod.rs | 2 +- src/objc_access.rs | 2 +- src/progress/mod.rs | 2 +- src/scrollview/mod.rs | 2 +- src/select/mod.rs | 4 ++-- src/switch.rs | 2 +- src/text/label/mod.rs | 2 +- src/utils/properties.rs | 2 +- src/view/controller/mod.rs | 2 +- src/view/mod.rs | 2 +- src/view/popover/mod.rs | 2 +- 19 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/appkit/segmentedcontrol.rs b/src/appkit/segmentedcontrol.rs index a6a8468c..5f2ee04d 100644 --- a/src/appkit/segmentedcontrol.rs +++ b/src/appkit/segmentedcontrol.rs @@ -293,7 +293,7 @@ impl SegmentedControl { } impl ObjcAccess for SegmentedControl { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } @@ -306,7 +306,7 @@ impl Layout for SegmentedControl {} impl Control for SegmentedControl {} impl ObjcAccess for &SegmentedControl { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/appkit/window/mod.rs b/src/appkit/window/mod.rs index c479c79b..ee3cf2e7 100644 --- a/src/appkit/window/mod.rs +++ b/src/appkit/window/mod.rs @@ -330,7 +330,7 @@ impl Window { } /// Given a view, sets it as the content view for this window. pub fn set_content_view(&self, view: &L) { - view.with_backing_obj_mut(|backing_node| unsafe { + view.with_backing_obj_mut(&|backing_node| unsafe { let _: () = msg_send![&*self.objc, setContentView:&*backing_node]; }); } diff --git a/src/button/mod.rs b/src/button/mod.rs index 5fd80cb0..f0f3277b 100644 --- a/src/button/mod.rs +++ b/src/button/mod.rs @@ -311,7 +311,7 @@ impl Button { } impl ObjcAccess for Button { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } @@ -325,7 +325,7 @@ impl Layout for Button {} impl Control for Button {} impl ObjcAccess for &Button { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/control/mod.rs b/src/control/mod.rs index 8691011c..a957cb47 100644 --- a/src/control/mod.rs +++ b/src/control/mod.rs @@ -27,7 +27,7 @@ pub enum ControlSize { pub trait Control: ObjcAccess { /// Whether this control is enabled or not. fn set_enabled(&self, is_enabled: bool) { - self.with_backing_obj_mut(|obj| unsafe { + self.with_backing_obj_mut(&|obj| unsafe { let _: () = msg_send![obj, setEnabled:match is_enabled { true => YES, false => NO @@ -48,7 +48,7 @@ pub trait Control: ObjcAccess { } }; - self.with_backing_obj_mut(|obj| unsafe { + self.with_backing_obj_mut(&|obj| unsafe { let _: () = msg_send![obj, setControlSize: control_size]; }); } diff --git a/src/image/mod.rs b/src/image/mod.rs index 8b567235..2a679517 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -174,7 +174,7 @@ impl ImageView { } impl ObjcAccess for ImageView { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/input/mod.rs b/src/input/mod.rs index 3e1759e7..632ee798 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -389,7 +389,7 @@ impl TextField { } impl ObjcAccess for TextField { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/layout/traits.rs b/src/layout/traits.rs index d1488667..ce8917a9 100644 --- a/src/layout/traits.rs +++ b/src/layout/traits.rs @@ -28,7 +28,7 @@ pub trait Layout: ObjcAccess { /// pass from the system will redraw it accordingly, and set the underlying value back to /// `false`. fn set_needs_display(&self, needs_display: bool) { - self.with_backing_obj_mut(|obj| unsafe { + self.with_backing_obj_mut(&|obj| unsafe { let _: () = msg_send![obj, setNeedsDisplay:match needs_display { true => YES, false => NO @@ -38,8 +38,8 @@ pub trait Layout: ObjcAccess { /// Adds another Layout-backed control or view as a subview of this view. fn add_subview(&self, view: &V) { - self.with_backing_obj_mut(|backing_node| { - view.with_backing_obj_mut(|subview_node| unsafe { + self.with_backing_obj_mut(&|backing_node| { + view.with_backing_obj_mut(&|subview_node| unsafe { let _: () = msg_send![backing_node, addSubview: subview_node]; }); }); @@ -47,7 +47,7 @@ pub trait Layout: ObjcAccess { /// Removes a control or view from the superview. fn remove_from_superview(&self) { - self.with_backing_obj_mut(|backing_node| unsafe { + self.with_backing_obj_mut(&|backing_node| unsafe { let _: () = msg_send![backing_node, removeFromSuperview]; }); } @@ -60,7 +60,7 @@ pub trait Layout: ObjcAccess { fn set_frame>(&self, rect: R) { let frame: CGRect = rect.into(); - self.with_backing_obj_mut(move |backing_node| unsafe { + self.with_backing_obj_mut(&move |backing_node| unsafe { let _: () = msg_send![backing_node, setFrame: frame]; }); } @@ -72,7 +72,7 @@ pub trait Layout: ObjcAccess { /// then you should set this to `true` (or use an appropriate initializer that does it for you). #[cfg(feature = "autolayout")] fn set_translates_autoresizing_mask_into_constraints(&self, translates: bool) { - self.with_backing_obj_mut(|backing_node| unsafe { + self.with_backing_obj_mut(&|backing_node| unsafe { let _: () = msg_send![backing_node, setTranslatesAutoresizingMaskIntoConstraints:match translates { true => YES, false => NO @@ -84,7 +84,7 @@ pub trait Layout: ObjcAccess { /// /// When hidden, widgets don't receive events and is not visible. fn set_hidden(&self, hide: bool) { - self.with_backing_obj_mut(|obj| unsafe { + self.with_backing_obj_mut(&|obj| unsafe { let _: () = msg_send![obj, setHidden:match hide { true => YES, false => NO @@ -122,7 +122,7 @@ pub trait Layout: ObjcAccess { .collect::>() .into(); - self.with_backing_obj_mut(|obj| unsafe { + self.with_backing_obj_mut(&|obj| unsafe { let _: () = msg_send![obj, registerForDraggedTypes:&*types]; }); } @@ -133,7 +133,7 @@ pub trait Layout: ObjcAccess { /// currently to avoid compile issues. #[cfg(feature = "appkit")] fn unregister_dragged_types(&self) { - self.with_backing_obj_mut(|obj| unsafe { + self.with_backing_obj_mut(&|obj| unsafe { let _: () = msg_send![obj, unregisterDraggedTypes]; }); } @@ -144,7 +144,7 @@ pub trait Layout: ObjcAccess { /// can be helpful - but always test! #[cfg(feature = "appkit")] fn set_posts_frame_change_notifications(&self, posts: bool) { - self.with_backing_obj_mut(|obj| unsafe { + self.with_backing_obj_mut(&|obj| unsafe { let _: () = msg_send![obj, setPostsFrameChangedNotifications:match posts { true => YES, false => NO @@ -158,7 +158,7 @@ pub trait Layout: ObjcAccess { /// can be helpful - but always test! #[cfg(feature = "appkit")] fn set_posts_bounds_change_notifications(&self, posts: bool) { - self.with_backing_obj_mut(|obj| unsafe { + self.with_backing_obj_mut(&|obj| unsafe { let _: () = msg_send![obj, setPostsBoundsChangedNotifications:match posts { true => YES, false => NO @@ -172,7 +172,7 @@ pub trait Layout: ObjcAccess { fn set_alpha(&self, value: f64) { let value: CGFloat = value.into(); - self.with_backing_obj_mut(|obj| unsafe { + self.with_backing_obj_mut(&|obj| unsafe { let _: () = msg_send![obj, setAlphaValue: value]; }); } diff --git a/src/listview/mod.rs b/src/listview/mod.rs index 7f239be1..b1842245 100644 --- a/src/listview/mod.rs +++ b/src/listview/mod.rs @@ -750,7 +750,7 @@ impl ListView { } impl ObjcAccess for ListView { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { // In AppKit, we need to provide the scrollview for layout purposes - iOS and tvOS will know // what to do normally. #[cfg(feature = "appkit")] diff --git a/src/listview/row/mod.rs b/src/listview/row/mod.rs index ef6342d8..081b6423 100644 --- a/src/listview/row/mod.rs +++ b/src/listview/row/mod.rs @@ -449,7 +449,7 @@ impl ListViewRow { } impl ObjcAccess for ListViewRow { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/objc_access.rs b/src/objc_access.rs index a24055e4..0acef227 100644 --- a/src/objc_access.rs +++ b/src/objc_access.rs @@ -13,7 +13,7 @@ use crate::foundation::id; pub trait ObjcAccess { /// Used for mutably interacting with the underlying Objective-C instance. /// Setters should use this. - fn with_backing_obj_mut(&self, handler: F); + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)); /// Used for checking backing properties of the underlying Objective-C instance, without /// needing a mutable borrow. diff --git a/src/progress/mod.rs b/src/progress/mod.rs index 9de32950..a9e665df 100644 --- a/src/progress/mod.rs +++ b/src/progress/mod.rs @@ -207,7 +207,7 @@ impl ProgressIndicator { } impl ObjcAccess for ProgressIndicator { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/scrollview/mod.rs b/src/scrollview/mod.rs index e945c14d..91d932c6 100644 --- a/src/scrollview/mod.rs +++ b/src/scrollview/mod.rs @@ -306,7 +306,7 @@ impl ScrollView { } impl ObjcAccess for ScrollView { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/select/mod.rs b/src/select/mod.rs index 02b1e0ed..53094a36 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -203,7 +203,7 @@ impl Select { } impl ObjcAccess for Select { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } @@ -226,7 +226,7 @@ impl Layout for Select { impl Control for Select {} impl ObjcAccess for &Select { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/switch.rs b/src/switch.rs index 601ac901..c3ae25e6 100644 --- a/src/switch.rs +++ b/src/switch.rs @@ -139,7 +139,7 @@ impl Switch { } impl ObjcAccess for Switch { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/text/label/mod.rs b/src/text/label/mod.rs index 23cb7de8..d41411fa 100644 --- a/src/text/label/mod.rs +++ b/src/text/label/mod.rs @@ -439,7 +439,7 @@ impl Label { } impl ObjcAccess for Label { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/utils/properties.rs b/src/utils/properties.rs index da53c0c7..3cfb0467 100644 --- a/src/utils/properties.rs +++ b/src/utils/properties.rs @@ -61,7 +61,7 @@ impl PropertyNullable { pub fn with(&self, handler: F) where - F: Fn(&T), + F: Fn(&T) { let borrow = self.0.borrow(); if let Some(s) = &*borrow { diff --git a/src/view/controller/mod.rs b/src/view/controller/mod.rs index 453e76d9..7bf96297 100644 --- a/src/view/controller/mod.rs +++ b/src/view/controller/mod.rs @@ -60,7 +60,7 @@ where vc.set_ivar(VIEW_DELEGATE_PTR, ptr as usize); } - view.with_backing_obj_mut(|backing_node| { + view.with_backing_obj_mut(&|backing_node| { let _: () = msg_send![&vc, setView: backing_node]; }); diff --git a/src/view/mod.rs b/src/view/mod.rs index c0e1a334..14b4ebed 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -350,7 +350,7 @@ impl View { } impl ObjcAccess for View { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } diff --git a/src/view/popover/mod.rs b/src/view/popover/mod.rs index 1beeb129..e4f4abb5 100644 --- a/src/view/popover/mod.rs +++ b/src/view/popover/mod.rs @@ -81,7 +81,7 @@ impl Popover { pub fn show_popover(&self, relative_to: Rect, view: &V, edge: Edge) { let rect: CGRect = relative_to.into(); unsafe { - view.with_backing_obj_mut(|obj| { + view.with_backing_obj_mut(&|obj| { let _: () = msg_send![&*self.objc, showRelativeToRect:rect ofView: &*obj preferredEdge: edge as u32]; }); } From d20013ce9bc0f406861ca8f47560b709ebaf9b71 Mon Sep 17 00:00:00 2001 From: Isaac Leonard Date: Sat, 16 Sep 2023 13:03:06 +1000 Subject: [PATCH 5/6] Make ObjcAccess an object safe trait, hopefully Note the api has been reworked. The get_from_backing_obj() method has been changed to get_backing_obj and now returns a Ref to the Object rather then calling a callback with it. This is because there was no way to make the callback method work without generics but we can't have generics if we want to make it object safe --- src/appkit/segmentedcontrol.rs | 22 +++++++++++----------- src/button/mod.rs | 12 +++++++----- src/image/mod.rs | 8 +++++--- src/input/mod.rs | 8 +++++--- src/layout/traits.rs | 18 ++++++++++++------ src/listview/mod.rs | 6 +++--- src/listview/row/mod.rs | 20 ++++++++++---------- src/objc_access.rs | 9 +++++++-- src/progress/mod.rs | 8 +++++--- src/scrollview/mod.rs | 8 +++++--- src/select/mod.rs | 16 +++++++++------- src/switch.rs | 8 +++++--- src/text/label/mod.rs | 16 +++++++++------- src/view/mod.rs | 19 +++++++++++-------- src/webview/mod.rs | 16 ++++++++-------- 15 files changed, 112 insertions(+), 82 deletions(-) diff --git a/src/appkit/segmentedcontrol.rs b/src/appkit/segmentedcontrol.rs index 5f2ee04d..a7fa2303 100644 --- a/src/appkit/segmentedcontrol.rs +++ b/src/appkit/segmentedcontrol.rs @@ -3,11 +3,11 @@ use std::fmt; use std::sync::Once; -use std::cell::RefCell; +use std::cell::{Ref, RefCell}; use std::rc::Rc; use objc::declare::ClassDecl; -use objc::rc::{Id, Shared}; +use objc::rc::{Id, Owned, Shared}; use objc::runtime::{Class, Object, Sel}; use objc::{class, msg_send, msg_send_id, sel}; @@ -98,7 +98,7 @@ pub struct SegmentedControl { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY + pub center_y: LayoutAnchorY, } #[derive(Debug)] @@ -106,7 +106,7 @@ pub struct SegmentedControl { pub enum TrackingMode { SelectOne = 0, SelectMany = 1, - SelectMomentary = 2 + SelectMomentary = 2, } impl SegmentedControl { @@ -163,7 +163,7 @@ impl SegmentedControl { #[cfg(feature = "autolayout")] center_y: LayoutAnchorY::center(view), - objc: ObjcProperty::retain(view) + objc: ObjcProperty::retain(view), } } @@ -216,14 +216,14 @@ impl SegmentedControl { /// button will fire. pub fn set_key_equivalent<'a, K>(&self, key: K) where - K: Into> + K: Into>, { let key: Key<'a> = key.into(); self.objc.with_mut(|obj| { let keychar = match key { Key::Char(s) => NSString::new(s), - Key::Delete => NSString::new("\u{08}") + Key::Delete => NSString::new("\u{08}"), }; unsafe { @@ -297,8 +297,8 @@ impl ObjcAccess for SegmentedControl { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } @@ -310,8 +310,8 @@ impl ObjcAccess for &SegmentedControl { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/button/mod.rs b/src/button/mod.rs index f0f3277b..c6767f13 100644 --- a/src/button/mod.rs +++ b/src/button/mod.rs @@ -21,7 +21,9 @@ //! my_view.add_subview(&button); //! ``` -use objc::rc::{Id, Shared}; +use std::cell::Ref; + +use objc::rc::{Id, Owned, Shared}; use objc::runtime::{Class, Object}; use objc::{msg_send, msg_send_id, sel}; @@ -315,8 +317,8 @@ impl ObjcAccess for Button { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } @@ -329,8 +331,8 @@ impl ObjcAccess for &Button { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/image/mod.rs b/src/image/mod.rs index 2a679517..be587910 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -1,6 +1,8 @@ +use std::cell::Ref; + use core_foundation::base::TCFType; -use objc::rc::{Id, Shared}; +use objc::rc::{Id, Owned, Shared}; use objc::runtime::{Class, Object}; use objc::{msg_send, msg_send_id, sel}; @@ -178,8 +180,8 @@ impl ObjcAccess for ImageView { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/input/mod.rs b/src/input/mod.rs index 632ee798..0751ea41 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -43,9 +43,11 @@ //! //! For more information on Autolayout, view the module or check out the examples folder. +use std::cell::Ref; + use core_foundation::base::TCFType; -use objc::rc::{Id, Shared}; +use objc::rc::{Id, Owned, Shared}; use objc::runtime::{Class, Object}; use objc::{class, msg_send, sel}; @@ -393,8 +395,8 @@ impl ObjcAccess for TextField { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/layout/traits.rs b/src/layout/traits.rs index ce8917a9..3fbddb3a 100644 --- a/src/layout/traits.rs +++ b/src/layout/traits.rs @@ -97,13 +97,15 @@ pub trait Layout: ObjcAccess { /// Note that this can report `false` if an ancestor widget is hidden, thus hiding this - to check in /// that case, you may want `is_hidden_or_ancestor_is_hidden()`. fn is_hidden(&self) -> bool { - self.get_from_backing_obj(|obj| to_bool(unsafe { msg_send![obj, isHidden] })) + let obj = self.get_backing_obj(); + to_bool(unsafe { msg_send![&**obj, isHidden] }) } /// Returns whether this is hidden, *or* whether an ancestor view is hidden. #[cfg(feature = "appkit")] fn is_hidden_or_ancestor_is_hidden(&self) -> bool { - self.get_from_backing_obj(|obj| to_bool(unsafe { msg_send![obj, isHiddenOrHasHiddenAncestor] })) + let obj = self.get_backing_obj(); + to_bool(unsafe { msg_send![&**obj, isHiddenOrHasHiddenAncestor] }) } /// Register this view for drag and drop operations. @@ -179,18 +181,22 @@ pub trait Layout: ObjcAccess { #[cfg(feature = "appkit")] fn get_top(&self) -> LayoutAnchorY { - self.get_from_backing_obj(|id| LayoutAnchorY::Top(unsafe { msg_send_id![id, topAnchor] })) + let id = self.get_backing_obj(); + LayoutAnchorY::Top(unsafe { msg_send_id![&**id, topAnchor] }) } #[cfg(feature = "appkit")] fn get_bottom(&self) -> LayoutAnchorY { - self.get_from_backing_obj(|id| LayoutAnchorY::Bottom(unsafe { msg_send_id![id, bottomAnchor] })) + let id = self.get_backing_obj(); + LayoutAnchorY::Bottom(unsafe { msg_send_id![&**id, bottomAnchor] }) } #[cfg(feature = "appkit")] fn get_leading(&self) -> LayoutAnchorX { - self.get_from_backing_obj(|id| LayoutAnchorX::Leading(unsafe { msg_send_id![id, leadingAnchor] })) + let id = self.get_backing_obj(); + LayoutAnchorX::Leading(unsafe { msg_send_id![&**id, leadingAnchor] }) } #[cfg(feature = "appkit")] fn get_trailing(&self) -> LayoutAnchorX { - self.get_from_backing_obj(|id| LayoutAnchorX::Trailing(unsafe { msg_send_id![id, trailingAnchor] })) + let id = self.get_backing_obj(); + LayoutAnchorX::Trailing(unsafe { msg_send_id![&**id, trailingAnchor] }) } } diff --git a/src/listview/mod.rs b/src/listview/mod.rs index b1842245..9844e963 100644 --- a/src/listview/mod.rs +++ b/src/listview/mod.rs @@ -96,7 +96,7 @@ pub(crate) static LISTVIEW_DELEGATE_PTR: &str = "rstListViewDelegatePtr"; use std::any::Any; use std::sync::{Arc, RwLock}; -use std::cell::RefCell; +use std::cell::{Ref, RefCell}; use std::rc::Rc; /// A helper method for instantiating view classes and applying default settings to them. @@ -757,14 +757,14 @@ impl ObjcAccess for ListView { self.scrollview.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { + fn get_backing_obj(&self) -> Ref<'_, Id> { // In AppKit, we need to provide the scrollview for layout purposes - iOS and tvOS will know // what to do normally. // // @TODO: Review this, as property access isn't really used in the same place as layout // stuff... hmm... #[cfg(feature = "appkit")] - self.scrollview.objc.get(handler) + self.scrollview.objc.get_ref() } } diff --git a/src/listview/row/mod.rs b/src/listview/row/mod.rs index 081b6423..6dde48aa 100644 --- a/src/listview/row/mod.rs +++ b/src/listview/row/mod.rs @@ -42,7 +42,7 @@ //! //! For more information on Autolayout, view the module or check out the examples folder. -use std::cell::RefCell; +use std::cell::{Ref, RefCell}; use std::rc::Rc; use objc::rc::{Id, Owned, Shared}; @@ -148,7 +148,7 @@ pub struct ListViewRow { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY + pub center_y: LayoutAnchorY, } impl Default for ListViewRow { @@ -199,14 +199,14 @@ impl ListViewRow { center_x: LayoutAnchorX::center(view), #[cfg(feature = "autolayout")] - center_y: LayoutAnchorY::center(view) + center_y: LayoutAnchorY::center(view), } } } impl ListViewRow where - T: ViewDelegate + 'static + T: ViewDelegate + 'static, { /// When we're able to retrieve a reusable view cell from the backing table view, we can check /// for the pointer and attempt to reconstruct the ListViewRow that corresponds to this. @@ -264,7 +264,7 @@ where center_x: LayoutAnchorX::center(view), #[cfg(feature = "autolayout")] - center_y: LayoutAnchorY::center(view) + center_y: LayoutAnchorY::center(view), }; view @@ -321,7 +321,7 @@ where center_x: LayoutAnchorX::center(view), #[cfg(feature = "autolayout")] - center_y: LayoutAnchorY::center(view) + center_y: LayoutAnchorY::center(view), }; (&mut delegate).did_load(view.clone_as_handle()); @@ -374,7 +374,7 @@ where center_x: self.center_x.clone(), #[cfg(feature = "autolayout")] - center_y: self.center_y.clone() + center_y: self.center_y.clone(), } } } @@ -424,7 +424,7 @@ impl ListViewRow { center_x: self.center_x.clone(), #[cfg(feature = "autolayout")] - center_y: self.center_y.clone() + center_y: self.center_y.clone(), } } @@ -453,8 +453,8 @@ impl ObjcAccess for ListViewRow { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/objc_access.rs b/src/objc_access.rs index 0acef227..bb02eba4 100644 --- a/src/objc_access.rs +++ b/src/objc_access.rs @@ -2,7 +2,12 @@ //! defined on here provide access handlers for common properties that the sub-traits need to //! enable modifying. -use objc::runtime::Object; +use std::cell::Ref; + +use objc::{ + rc::{Id, Owned}, + runtime::Object +}; use crate::foundation::id; @@ -19,5 +24,5 @@ pub trait ObjcAccess { /// needing a mutable borrow. /// /// Getters should use this. - fn get_from_backing_obj R, R>(&self, handler: F) -> R; + fn get_backing_obj(&self) -> Ref<'_, Id>; } diff --git a/src/progress/mod.rs b/src/progress/mod.rs index a9e665df..cdc7003d 100644 --- a/src/progress/mod.rs +++ b/src/progress/mod.rs @@ -15,9 +15,11 @@ //! my_view.add_subview(&indicator); //! ``` +use std::cell::Ref; + use core_graphics::base::CGFloat; -use objc::rc::{Id, Shared}; +use objc::rc::{Id, Owned, Shared}; use objc::runtime::{Class, Object}; use objc::{class, msg_send, sel}; @@ -211,8 +213,8 @@ impl ObjcAccess for ProgressIndicator { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/scrollview/mod.rs b/src/scrollview/mod.rs index 91d932c6..d198dbf5 100644 --- a/src/scrollview/mod.rs +++ b/src/scrollview/mod.rs @@ -42,9 +42,11 @@ //! //! For more information on Autolayout, view the module or check out the examples folder. +use std::cell::Ref; + use core_foundation::base::TCFType; -use objc::rc::{Id, Shared}; +use objc::rc::{Id, Owned, Shared}; use objc::runtime::{Class, Object}; use objc::{msg_send, sel}; @@ -310,8 +312,8 @@ impl ObjcAccess for ScrollView { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/select/mod.rs b/src/select/mod.rs index 53094a36..e78a587e 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -1,7 +1,9 @@ //! Implements a Select-style dropdown. By default this uses NSPopupSelect on macOS. +use std::cell::Ref; + use core_graphics::geometry::CGRect; -use objc::rc::{Id, Shared}; +use objc::rc::{Id, Owned, Shared}; use objc::runtime::{Class, Object}; use objc::{msg_send, msg_send_id, sel}; @@ -78,7 +80,7 @@ pub struct Select { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY + pub center_y: LayoutAnchorY, } impl Select { @@ -130,7 +132,7 @@ impl Select { #[cfg(feature = "autolayout")] center_y: LayoutAnchorY::center(view), - objc: ObjcProperty::retain(view) + objc: ObjcProperty::retain(view), } } @@ -207,8 +209,8 @@ impl ObjcAccess for Select { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } @@ -230,8 +232,8 @@ impl ObjcAccess for &Select { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/switch.rs b/src/switch.rs index c3ae25e6..03e48a3d 100644 --- a/src/switch.rs +++ b/src/switch.rs @@ -1,7 +1,9 @@ //! A wrapper for NSSwitch. Currently the epitome of jank - if you're poking around here, expect //! that this will change at some point. -use objc::rc::{Id, Shared}; +use std::cell::Ref; + +use objc::rc::{Id, Owned, Shared}; use objc::runtime::{Class, Object}; use objc::{msg_send, msg_send_id, sel}; @@ -143,8 +145,8 @@ impl ObjcAccess for Switch { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/text/label/mod.rs b/src/text/label/mod.rs index d41411fa..b82536a0 100644 --- a/src/text/label/mod.rs +++ b/src/text/label/mod.rs @@ -43,9 +43,11 @@ //! //! For more information on Autolayout, view the module or check out the examples folder. +use std::cell::Ref; + use core_foundation::base::TCFType; -use objc::rc::{Id, Shared}; +use objc::rc::{Id, Owned, Shared}; use objc::runtime::{Class, Object}; use objc::{msg_send, msg_send_id, sel}; @@ -201,7 +203,7 @@ pub struct Label { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY + pub center_y: LayoutAnchorY, } impl Default for Label { @@ -253,14 +255,14 @@ impl Label { layer: Layer::from_id(unsafe { msg_send_id![view, layer] }), - objc: ObjcProperty::retain(view) + objc: ObjcProperty::retain(view), } } } impl Label where - T: LabelDelegate + 'static + T: LabelDelegate + 'static, { /// Initializes a new Label with a given `LabelDelegate`. This enables you to respond to events /// and customize the view as a module, similar to class-based systems. @@ -317,7 +319,7 @@ impl Label { layer: self.layer.clone(), - objc: self.objc.clone() + objc: self.objc.clone(), } } @@ -443,8 +445,8 @@ impl ObjcAccess for Label { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } diff --git a/src/view/mod.rs b/src/view/mod.rs index 14b4ebed..80064919 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -42,6 +42,9 @@ //! //! For more information on Autolayout, view the module or check out the examples folder. +use std::cell::Ref; + +use objc::rc::{Id, Owned}; use objc::runtime::{Class, Object}; use objc::{msg_send, msg_send_id, sel}; @@ -155,7 +158,7 @@ pub struct View { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY + pub center_y: LayoutAnchorY, } impl Default for View { @@ -221,7 +224,7 @@ impl View { #[cfg(all(feature = "appkit", target_os = "macos"))] animator: ViewAnimatorProxy::new(view), - objc: ObjcProperty::retain(view) + objc: ObjcProperty::retain(view), } } @@ -233,7 +236,7 @@ impl View { impl View where - T: ViewDelegate + 'static + T: ViewDelegate + 'static, { /// Initializes a new View with a given `ViewDelegate`. This enables you to respond to events /// and customize the view as a module, similar to class-based systems. @@ -302,7 +305,7 @@ impl View { center_x: self.center_x.clone(), #[cfg(feature = "autolayout")] - center_y: self.center_y.clone() + center_y: self.center_y.clone(), } } @@ -354,8 +357,8 @@ impl ObjcAccess for View { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } @@ -385,7 +388,7 @@ pub enum LayerContentsRedrawPolicy { OnSetNeedsDisplay, DuringViewResize, BeforeViewResize, - Crossfade + Crossfade, } #[cfg(feature = "appkit")] @@ -397,7 +400,7 @@ impl LayerContentsRedrawPolicy { Self::OnSetNeedsDisplay => 1, Self::DuringViewResize => 2, Self::BeforeViewResize => 3, - Self::Crossfade => 4 + Self::Crossfade => 4, } } } diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 8cbae491..f63eba20 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -14,10 +14,10 @@ //! platform. use core_graphics::geometry::CGRect; - use objc::rc::{Id, Owned, Shared}; use objc::runtime::Object; use objc::{class, msg_send, msg_send_id, sel}; +use std::cell::Ref; use crate::foundation::{id, nil, NSString, NO, YES}; use crate::geometry::Rect; @@ -151,7 +151,7 @@ pub struct WebView { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY + pub center_y: LayoutAnchorY, } impl Default for WebView { @@ -211,7 +211,7 @@ impl WebView { layer: Layer::from_id(unsafe { msg_send_id![view, layer] }), - objc: ObjcProperty::retain(view) + objc: ObjcProperty::retain(view), } } @@ -224,7 +224,7 @@ impl WebView { impl WebView where - T: WebViewDelegate + 'static + T: WebViewDelegate + 'static, { /// Initializes a new WebView with a given `WebViewDelegate`. This enables you to respond to events /// and customize the view as a module, similar to class-based systems. @@ -289,7 +289,7 @@ impl WebView { center_x: self.center_x.clone(), #[cfg(feature = "autolayout")] - center_y: self.center_y.clone() + center_y: self.center_y.clone(), } } @@ -333,12 +333,12 @@ impl WebView { } impl ObjcAccess for WebView { - fn with_backing_obj_mut(&self, handler: F) { + fn with_backing_obj_mut(&self, handler: &dyn Fn(id)) { self.objc.with_mut(handler); } - fn get_from_backing_obj R, R>(&self, handler: F) -> R { - self.objc.get(handler) + fn get_backing_obj(&self) -> Ref<'_, Id> { + self.objc.get_ref() } } From 47af12517333e6d9c75a2f956c8d2c17e6854335 Mon Sep 17 00:00:00 2001 From: Isaac Leonard Date: Sat, 16 Sep 2023 13:31:28 +1000 Subject: [PATCH 6/6] Make Layout Object safe, pull the set_frame method into its own trait --- examples/frame_layout.rs | 2 +- src/appkit/segmentedcontrol.rs | 10 +++++----- src/layout/mod.rs | 2 +- src/layout/traits.rs | 32 ++++++++++++++++++-------------- src/listview/row/mod.rs | 14 +++++++------- src/select/mod.rs | 8 ++++---- src/switch.rs | 2 +- src/text/label/mod.rs | 8 ++++---- src/view/mod.rs | 12 ++++++------ src/webview/mod.rs | 10 +++++----- 10 files changed, 52 insertions(+), 48 deletions(-) diff --git a/examples/frame_layout.rs b/examples/frame_layout.rs index 927ae861..c8091668 100644 --- a/examples/frame_layout.rs +++ b/examples/frame_layout.rs @@ -3,7 +3,7 @@ use cacao::color::Color; use cacao::geometry::Rect; -use cacao::layout::Layout; +use cacao::layout::{Frame, Layout}; use cacao::view::View; use cacao::appkit::menu::{Menu, MenuItem}; diff --git a/src/appkit/segmentedcontrol.rs b/src/appkit/segmentedcontrol.rs index a7fa2303..0ea33f4a 100644 --- a/src/appkit/segmentedcontrol.rs +++ b/src/appkit/segmentedcontrol.rs @@ -98,7 +98,7 @@ pub struct SegmentedControl { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY, + pub center_y: LayoutAnchorY } #[derive(Debug)] @@ -106,7 +106,7 @@ pub struct SegmentedControl { pub enum TrackingMode { SelectOne = 0, SelectMany = 1, - SelectMomentary = 2, + SelectMomentary = 2 } impl SegmentedControl { @@ -163,7 +163,7 @@ impl SegmentedControl { #[cfg(feature = "autolayout")] center_y: LayoutAnchorY::center(view), - objc: ObjcProperty::retain(view), + objc: ObjcProperty::retain(view) } } @@ -216,14 +216,14 @@ impl SegmentedControl { /// button will fire. pub fn set_key_equivalent<'a, K>(&self, key: K) where - K: Into>, + K: Into> { let key: Key<'a> = key.into(); self.objc.with_mut(|obj| { let keychar = match key { Key::Char(s) => NSString::new(s), - Key::Delete => NSString::new("\u{08}"), + Key::Delete => NSString::new("\u{08}") }; unsafe { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 38bafee4..14bc0a71 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -4,7 +4,7 @@ //! more complicated views that need to deal with differing screen sizes. mod traits; -pub use traits::Layout; +pub use traits::{Frame, Layout}; #[cfg(all(feature = "appkit", target_os = "macos"))] mod animator; diff --git a/src/layout/traits.rs b/src/layout/traits.rs index 3fbddb3a..97e74c73 100644 --- a/src/layout/traits.rs +++ b/src/layout/traits.rs @@ -37,7 +37,7 @@ pub trait Layout: ObjcAccess { } /// Adds another Layout-backed control or view as a subview of this view. - fn add_subview(&self, view: &V) { + fn add_subview(&self, view: &dyn Layout) { self.with_backing_obj_mut(&|backing_node| { view.with_backing_obj_mut(&|subview_node| unsafe { let _: () = msg_send![backing_node, addSubview: subview_node]; @@ -52,19 +52,6 @@ pub trait Layout: ObjcAccess { }); } - /// Sets the `frame` for the view this trait is applied to. - /// - /// Note that Cacao, by default, opts into autolayout - you need to call - /// `set_translates_autoresizing_mask_into_constraints` to enable frame-based layout calls (or - /// use an appropriate initializer for a given view type). - fn set_frame>(&self, rect: R) { - let frame: CGRect = rect.into(); - - self.with_backing_obj_mut(&move |backing_node| unsafe { - let _: () = msg_send![backing_node, setFrame: frame]; - }); - } - /// Sets whether the view for this trait should translate autoresizing masks into layout /// constraints. /// @@ -200,3 +187,20 @@ pub trait Layout: ObjcAccess { LayoutAnchorX::Trailing(unsafe { msg_send_id![&**id, trailingAnchor] }) } } + +pub trait Frame: Layout { + /// Sets the `frame` for the view this trait is applied to. + /// + /// Note that Cacao, by default, opts into autolayout - you need to call + /// `set_translates_autoresizing_mask_into_constraints` to enable frame-based layout calls (or + /// use an appropriate initializer for a given view type). + fn set_frame>(&self, rect: R) { + let frame: CGRect = rect.into(); + + self.with_backing_obj_mut(&move |backing_node| unsafe { + let _: () = msg_send![backing_node, setFrame: frame]; + }); + } +} + +impl Frame for T {} diff --git a/src/listview/row/mod.rs b/src/listview/row/mod.rs index 6dde48aa..32f690a3 100644 --- a/src/listview/row/mod.rs +++ b/src/listview/row/mod.rs @@ -148,7 +148,7 @@ pub struct ListViewRow { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY, + pub center_y: LayoutAnchorY } impl Default for ListViewRow { @@ -199,14 +199,14 @@ impl ListViewRow { center_x: LayoutAnchorX::center(view), #[cfg(feature = "autolayout")] - center_y: LayoutAnchorY::center(view), + center_y: LayoutAnchorY::center(view) } } } impl ListViewRow where - T: ViewDelegate + 'static, + T: ViewDelegate + 'static { /// When we're able to retrieve a reusable view cell from the backing table view, we can check /// for the pointer and attempt to reconstruct the ListViewRow that corresponds to this. @@ -264,7 +264,7 @@ where center_x: LayoutAnchorX::center(view), #[cfg(feature = "autolayout")] - center_y: LayoutAnchorY::center(view), + center_y: LayoutAnchorY::center(view) }; view @@ -321,7 +321,7 @@ where center_x: LayoutAnchorX::center(view), #[cfg(feature = "autolayout")] - center_y: LayoutAnchorY::center(view), + center_y: LayoutAnchorY::center(view) }; (&mut delegate).did_load(view.clone_as_handle()); @@ -374,7 +374,7 @@ where center_x: self.center_x.clone(), #[cfg(feature = "autolayout")] - center_y: self.center_y.clone(), + center_y: self.center_y.clone() } } } @@ -424,7 +424,7 @@ impl ListViewRow { center_x: self.center_x.clone(), #[cfg(feature = "autolayout")] - center_y: self.center_y.clone(), + center_y: self.center_y.clone() } } diff --git a/src/select/mod.rs b/src/select/mod.rs index e78a587e..1be2cc41 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -80,7 +80,7 @@ pub struct Select { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY, + pub center_y: LayoutAnchorY } impl Select { @@ -132,7 +132,7 @@ impl Select { #[cfg(feature = "autolayout")] center_y: LayoutAnchorY::center(view), - objc: ObjcProperty::retain(view), + objc: ObjcProperty::retain(view) } } @@ -215,7 +215,7 @@ impl ObjcAccess for Select { } impl Layout for Select { - fn add_subview(&self, _view: &V) { + fn add_subview(&self, _view: &dyn Layout) { panic!( r#" Tried to add a subview to a Select. This is not allowed in Cacao. If you think this should be supported, @@ -238,7 +238,7 @@ impl ObjcAccess for &Select { } impl Layout for &Select { - fn add_subview(&self, _view: &V) { + fn add_subview(&self, _view: &dyn Layout) { panic!( r#" Tried to add a subview to a Select. This is not allowed in Cacao. If you think this should be supported, diff --git a/src/switch.rs b/src/switch.rs index 03e48a3d..cfe1edf6 100644 --- a/src/switch.rs +++ b/src/switch.rs @@ -151,7 +151,7 @@ impl ObjcAccess for Switch { } impl Layout for Switch { - fn add_subview(&self, _view: &V) { + fn add_subview(&self, _view: &dyn Layout) { panic!( r#" Tried to add a subview to a Switch. This is not allowed in Cacao. If you think this should be supported, diff --git a/src/text/label/mod.rs b/src/text/label/mod.rs index b82536a0..93e91691 100644 --- a/src/text/label/mod.rs +++ b/src/text/label/mod.rs @@ -203,7 +203,7 @@ pub struct Label { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY, + pub center_y: LayoutAnchorY } impl Default for Label { @@ -255,14 +255,14 @@ impl Label { layer: Layer::from_id(unsafe { msg_send_id![view, layer] }), - objc: ObjcProperty::retain(view), + objc: ObjcProperty::retain(view) } } } impl Label where - T: LabelDelegate + 'static, + T: LabelDelegate + 'static { /// Initializes a new Label with a given `LabelDelegate`. This enables you to respond to events /// and customize the view as a module, similar to class-based systems. @@ -319,7 +319,7 @@ impl Label { layer: self.layer.clone(), - objc: self.objc.clone(), + objc: self.objc.clone() } } diff --git a/src/view/mod.rs b/src/view/mod.rs index 80064919..98043beb 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -158,7 +158,7 @@ pub struct View { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY, + pub center_y: LayoutAnchorY } impl Default for View { @@ -224,7 +224,7 @@ impl View { #[cfg(all(feature = "appkit", target_os = "macos"))] animator: ViewAnimatorProxy::new(view), - objc: ObjcProperty::retain(view), + objc: ObjcProperty::retain(view) } } @@ -236,7 +236,7 @@ impl View { impl View where - T: ViewDelegate + 'static, + T: ViewDelegate + 'static { /// Initializes a new View with a given `ViewDelegate`. This enables you to respond to events /// and customize the view as a module, similar to class-based systems. @@ -305,7 +305,7 @@ impl View { center_x: self.center_x.clone(), #[cfg(feature = "autolayout")] - center_y: self.center_y.clone(), + center_y: self.center_y.clone() } } @@ -388,7 +388,7 @@ pub enum LayerContentsRedrawPolicy { OnSetNeedsDisplay, DuringViewResize, BeforeViewResize, - Crossfade, + Crossfade } #[cfg(feature = "appkit")] @@ -400,7 +400,7 @@ impl LayerContentsRedrawPolicy { Self::OnSetNeedsDisplay => 1, Self::DuringViewResize => 2, Self::BeforeViewResize => 3, - Self::Crossfade => 4, + Self::Crossfade => 4 } } } diff --git a/src/webview/mod.rs b/src/webview/mod.rs index f63eba20..a0cef291 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -151,7 +151,7 @@ pub struct WebView { /// A pointer to the Objective-C runtime center Y layout constraint. #[cfg(feature = "autolayout")] - pub center_y: LayoutAnchorY, + pub center_y: LayoutAnchorY } impl Default for WebView { @@ -211,7 +211,7 @@ impl WebView { layer: Layer::from_id(unsafe { msg_send_id![view, layer] }), - objc: ObjcProperty::retain(view), + objc: ObjcProperty::retain(view) } } @@ -224,7 +224,7 @@ impl WebView { impl WebView where - T: WebViewDelegate + 'static, + T: WebViewDelegate + 'static { /// Initializes a new WebView with a given `WebViewDelegate`. This enables you to respond to events /// and customize the view as a module, similar to class-based systems. @@ -289,7 +289,7 @@ impl WebView { center_x: self.center_x.clone(), #[cfg(feature = "autolayout")] - center_y: self.center_y.clone(), + center_y: self.center_y.clone() } } @@ -345,7 +345,7 @@ impl ObjcAccess for WebView { impl Layout for WebView { /// Currently, this is a noop. Theoretically there is reason to support this, but in practice /// I've never seen it needed... but am open to discussion. - fn add_subview(&self, _: &V) {} + fn add_subview(&self, _: &dyn Layout) {} } impl std::fmt::Debug for WebView {