Clean up AutoLayout wrapper.

- Anchors are now enums, which help in debugging constraint issues.

- Enum types that are acceptable for constraint generation are now
  matched at runtime, and provide better error messages if a constraint
  can't be satisfied and crashes the app.

- `left` and `right` anchors are now supported on all widgets, fixing an
  oversight from early iterations of the framework.

- Reduces `unsafe` usage by a decent amount when setting constraint
  pointers for widget types.
This commit is contained in:
Ryan McGrath 2021-03-16 18:21:31 -07:00
parent 696907aa73
commit 9446092169
No known key found for this signature in database
GPG key ID: DA6CBD9233593DEA
14 changed files with 650 additions and 283 deletions

View file

@ -38,9 +38,15 @@ pub struct Button {
/// A pointer to the Objective-C runtime leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint. /// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint. /// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
@ -77,14 +83,16 @@ impl Button {
Button { Button {
handler: None, handler: None,
image: None, image: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
} }
} }

View file

@ -44,16 +44,22 @@ fn allocate_view(registration_fn: fn() -> *const Class) -> id {
pub struct ImageView { pub struct ImageView {
/// A pointer to the Objective-C runtime view controller. /// A pointer to the Objective-C runtime view controller.
pub objc: ShareId<Object>, pub objc: ShareId<Object>,
/// A pointer to the Objective-C runtime top layout constraint. /// A pointer to the Objective-C runtime top layout constraint.
pub top: LayoutAnchorY, pub top: LayoutAnchorY,
/// A pointer to the Objective-C runtime leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint. /// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint. /// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
@ -82,14 +88,16 @@ impl ImageView {
let view = allocate_view(register_image_view_class); let view = allocate_view(register_image_view_class);
ImageView { ImageView {
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
} }
} }

View file

@ -89,16 +89,22 @@ pub struct TextField<T = ()> {
/// A pointer to the delegate for this view. /// A pointer to the delegate for this view.
pub delegate: Option<Box<T>>, pub delegate: Option<Box<T>>,
/// A pointer to the Objective-C runtime top layout constraint. /// A pointer to the Objective-C runtime top layout constraint.
pub top: LayoutAnchorY, pub top: LayoutAnchorY,
/// A pointer to the Objective-C runtime leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint. /// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint. /// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
@ -128,14 +134,16 @@ impl TextField {
TextField { TextField {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
} }
} }
@ -157,14 +165,16 @@ impl<T> TextField<T> where T: TextFieldDelegate + 'static {
let mut label = TextField { let mut label = TextField {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![label, topAnchor] }), top: LayoutAnchorY::top(label),
leading: LayoutAnchorX::new(unsafe { msg_send![label, leadingAnchor] }), left: LayoutAnchorX::left(label),
trailing: LayoutAnchorX::new(unsafe { msg_send![label, trailingAnchor] }), leading: LayoutAnchorX::leading(label),
bottom: LayoutAnchorY::new(unsafe { msg_send![label, bottomAnchor] }), right: LayoutAnchorX::right(label),
width: LayoutAnchorDimension::new(unsafe { msg_send![label, widthAnchor] }), trailing: LayoutAnchorX::trailing(label),
height: LayoutAnchorDimension::new(unsafe { msg_send![label, heightAnchor] }), bottom: LayoutAnchorY::bottom(label),
center_x: LayoutAnchorX::new(unsafe { msg_send![label, centerXAnchor] }), width: LayoutAnchorDimension::width(label),
center_y: LayoutAnchorY::new(unsafe { msg_send![label, centerYAnchor] }), height: LayoutAnchorDimension::height(label),
center_x: LayoutAnchorX::center(label),
center_y: LayoutAnchorY::center(label),
objc: unsafe { ShareId::from_ptr(label) }, objc: unsafe { ShareId::from_ptr(label) },
}; };
@ -184,7 +194,9 @@ impl<T> TextField<T> {
delegate: None, delegate: None,
top: self.top.clone(), top: self.top.clone(),
leading: self.leading.clone(), leading: self.leading.clone(),
left: self.left.clone(),
trailing: self.trailing.clone(), trailing: self.trailing.clone(),
right: self.right.clone(),
bottom: self.bottom.clone(), bottom: self.bottom.clone(),
width: self.width.clone(), width: self.width.clone(),
height: self.height.clone(), height: self.height.clone(),

View file

@ -1,6 +1,3 @@
//! A wrapper for `NSLayoutAnchorDimension`, which is typically used to handle `width` and `height`
//! values for how a given view should layout.
use core_graphics::base::CGFloat; use core_graphics::base::CGFloat;
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
@ -15,82 +12,128 @@ use super::attributes::{LayoutAttribute, LayoutRelation};
/// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a /// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a
/// factory/helper for creating `LayoutConstraint` objects based on your views. /// factory/helper for creating `LayoutConstraint` objects based on your views.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct LayoutAnchorDimension(pub Option<ShareId<Object>>); pub struct LayoutAnchorDimension2(pub Option<ShareId<Object>>);
/// A wrapper for `NSLayoutAnchorDimension`, which is typically used to handle `width` and `height`
/// values for how a given view should layout.
#[derive(Clone, Debug)]
pub enum LayoutAnchorDimension {
/// Represents an uninitialized anchor (e.g, for a view that's not created yet).
Uninitialized,
/// Represents a Width anchor.
Width(ShareId<Object>),
/// Represents a Height anchor.
Height(ShareId<Object>)
}
impl Default for LayoutAnchorDimension {
/// Returns an Uninitialized anchor dimension by default.
fn default() -> Self {
Self::Uninitialized
}
}
impl LayoutAnchorDimension { impl LayoutAnchorDimension {
/// An internal method for wrapping existing anchors. /// Given a view, returns an anchor for the width anchor.
pub(crate) fn new(object: id) -> Self { pub(crate) fn width(view: id) -> Self {
LayoutAnchorDimension(Some(unsafe { Self::Width(unsafe {
ShareId::from_ptr(object) ShareId::from_ptr(msg_send![view, widthAnchor])
})) })
}
/// Given a view, returns an anchor for the height anchor.
pub(crate) fn height(view: id) -> Self {
Self::Height(unsafe {
ShareId::from_ptr(msg_send![view, heightAnchor])
})
} }
/// Return a constraint equal to a constant value. /// Return a constraint equal to a constant value.
pub fn constraint_equal_to_constant(&self, constant: f64) -> LayoutConstraint { pub fn constraint_equal_to_constant(&self, constant: f64) -> LayoutConstraint {
match &self.0 { if let Self::Width(obj) | Self::Height(obj) = self {
Some(from) => LayoutConstraint::new(unsafe { return LayoutConstraint::new(unsafe {
let value = constant as CGFloat; let value = constant as CGFloat;
msg_send![*from, constraintEqualToConstant:value] msg_send![*obj, constraintEqualToConstant:value]
}), });
}
panic!("Attempted to create a constant constraint with an uninitialized anchor.");
}
_ => { panic!("Attempted to create constraints with an uninitialized anchor!"); } /// Return a constraint greater than or equal to a constant value.
pub fn constraint_greater_than_or_equal_to_constant(&self, constant: f64) -> LayoutConstraint {
if let Self::Width(obj) | Self::Height(obj) = self {
return LayoutConstraint::new(unsafe {
let value = constant as CGFloat;
msg_send![*obj, constraintGreaterThanOrEqualToConstant:value]
});
}
panic!("Attempted to create a constraint (>=) with an uninitialized anchor.");
}
/// Return a constraint greater than or equal to a constant value.
pub fn constraint_less_than_or_equal_to_constant(&self, constant: f64) -> LayoutConstraint {
if let Self::Width(obj) | Self::Height(obj) = self {
return LayoutConstraint::new(unsafe {
let value = constant as CGFloat;
msg_send![*obj, constraintLessThanOrEqualToConstant:value]
});
}
panic!("Attempted to create a constraint (<=) with an uninitialized anchor.");
}
/// Boilerplate for handling constraint construction and panic'ing with some more helpful
/// messages. The goal here is to make AutoLayout slightly easier to debug when things go
/// wrong.
fn constraint_with<F>(&self, anchor_to: &LayoutAnchorDimension, handler: F) -> LayoutConstraint
where
F: Fn(&ShareId<Object>, &ShareId<Object>) -> id
{
match (self, anchor_to) {
(Self::Width(from), Self::Width(to)) |
(Self::Width(from), Self::Height(to)) |
(Self::Height(from), Self::Width(to)) |
(Self::Height(from), Self::Height(to)) => {
LayoutConstraint::new(handler(from, to))
},
(Self::Uninitialized, Self::Uninitialized) => {
panic!("Attempted to create constraints with an uninitialized \"from\" and \"to\" dimension anchor.");
},
(Self::Uninitialized, _) => {
panic!("Attempted to create constraints with an uninitialized \"from\" dimension anchor.");
},
(_, Self::Uninitialized) => {
panic!("Attempted to create constraints with an uninitialized \"to\" dimension anchor.");
}
} }
} }
/// Return a constraint equal to another dimension anchor. /// Return a constraint equal to another dimension anchor.
pub fn constraint_equal_to(&self, anchor_to: &LayoutAnchorDimension) -> LayoutConstraint { pub fn constraint_equal_to(&self, anchor_to: &LayoutAnchorDimension) -> LayoutConstraint {
match (&self.0, &anchor_to.0) { self.constraint_with(anchor_to, |from, to| unsafe {
(Some(from), Some(to)) => LayoutConstraint::new(unsafe { msg_send![*from, constraintEqualToAnchor:&**to]
msg_send![*from, constraintEqualToAnchor:&**to] })
}),
_ => { panic!("Attempted to create constraints with an uninitialized anchor!"); }
}
} }
/// Return a constraint greater than or equal to another dimension anchor. /// Return a constraint greater than or equal to another dimension anchor.
pub fn constraint_greater_than_or_equal_to(&self, anchor_to: &LayoutAnchorDimension) -> LayoutConstraint { pub fn constraint_greater_than_or_equal_to(&self, anchor_to: &LayoutAnchorDimension) -> LayoutConstraint {
match (&self.0, &anchor_to.0) { self.constraint_with(anchor_to, |from, to| unsafe {
(Some(from), Some(to)) => LayoutConstraint::new(unsafe { msg_send![*from, constraintGreaterThanOrEqualToAnchor:&**to]
msg_send![*from, constraintGreaterThanOrEqualToAnchor:&**to] })
}),
_ => { panic!("Attempted to create constraints with an uninitialized anchor!"); }
}
}
/// Return a constraint greater than or equal to a constant value.
pub fn constraint_greater_than_or_equal_to_constant(&self, constant: f64) -> LayoutConstraint {
match &self.0 {
Some(from) => LayoutConstraint::new(unsafe {
let value = constant as CGFloat;
msg_send![*from, constraintGreaterThanOrEqualToConstant:value]
}),
_ => { panic!("Attempted to create constraints with an uninitialized anchor!"); }
}
} }
/// Return a constraint less than or equal to another dimension anchor. /// Return a constraint less than or equal to another dimension anchor.
pub fn constraint_less_than_or_equal_to(&self, anchor_to: &LayoutAnchorDimension) -> LayoutConstraint { pub fn constraint_less_than_or_equal_to(&self, anchor_to: &LayoutAnchorDimension) -> LayoutConstraint {
match (&self.0, &anchor_to.0) { self.constraint_with(anchor_to, |from, to| unsafe {
(Some(from), Some(to)) => LayoutConstraint::new(unsafe { msg_send![*from, constraintLessThanOrEqualToAnchor:&**to]
msg_send![*from, constraintLessThanOrEqualToAnchor:&**to] })
}),
_ => { panic!("Attempted to create constraints with an uninitialized anchor!"); }
}
}
/// Return a constraint greater than or equal to a constant value.
pub fn constraint_less_than_or_equal_to_constant(&self, constant: f64) -> LayoutConstraint {
match &self.0 {
Some(from) => LayoutConstraint::new(unsafe {
let value = constant as CGFloat;
msg_send![*from, constraintLessThanOrEqualToConstant:value]
}),
_ => { panic!("Attempted to create constraints with an uninitialized anchor!"); }
}
} }
} }

View file

@ -1,7 +1,3 @@
//! A wrapper for `NSLayoutAnchorX`, which is typically used to handle values for how a
//! given view should layout along the x-axis. Of note: the only thing that can't be protected
//! against is mixing/matching incorrect left/leading and right/trailing anchors. Be careful!
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
@ -9,49 +5,182 @@ use objc_id::ShareId;
use crate::foundation::id; use crate::foundation::id;
use crate::layout::constraint::LayoutConstraint; use crate::layout::constraint::LayoutConstraint;
/// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a /// A wrapper for `NSLayoutAnchorX`, used to handle values for how a given view should
/// factory/helper for creating `LayoutConstraint` objects based on your views. /// layout along the x-axis.
#[derive(Clone, Debug, Default)] ///
pub struct LayoutAnchorX(pub Option<ShareId<Object>>); /// Of note: mismatches of incorrect left/leading and right/trailing anchors are detected at
/// runtime, and will panic - this is by design, as your UI needs to work. Be careful!
#[derive(Clone, Debug)]
pub enum LayoutAnchorX {
/// Represents an uninitialized anchor (e.g, for a view that's not created yet).
Uninitialized,
/// Represents a leading anchor; side depends on system orientation.
Leading(ShareId<Object>),
/// Represents a left anchor.
Left(ShareId<Object>),
/// Represents a trailing anchor; side depends on system orientation.
Trailing(ShareId<Object>),
/// Represents a right anchor.
Right(ShareId<Object>),
/// Represents a center anchor on the X axis.
Center(ShareId<Object>)
}
impl Default for LayoutAnchorX {
/// Returns an uninitialized anchor by default.
fn default() -> Self {
Self::Uninitialized
}
}
impl LayoutAnchorX { impl LayoutAnchorX {
/// An internal method for wrapping existing anchors. /// Given a view, returns an anchor for the leading anchor.
pub(crate) fn new(object: id) -> Self { pub(crate) fn leading(view: id) -> Self {
LayoutAnchorX(Some(unsafe { Self::Leading(unsafe {
ShareId::from_ptr(object) ShareId::from_ptr(msg_send![view, leadingAnchor])
})) })
}
/// Given a view, returns an anchor for the left anchor.
pub(crate) fn left(view: id) -> Self {
Self::Left(unsafe {
ShareId::from_ptr(msg_send![view, leftAnchor])
})
}
/// Given a view, returns an anchor for the trailing anchor.
pub(crate) fn trailing(view: id) -> Self {
Self::Trailing(unsafe {
ShareId::from_ptr(msg_send![view, trailingAnchor])
})
}
/// Given a view, returns an anchor for the right anchor.
pub(crate) fn right(view: id) -> Self {
Self::Right(unsafe {
ShareId::from_ptr(msg_send![view, rightAnchor])
})
}
/// Given a view, returns an anchor for the right anchor.
pub(crate) fn center(view: id) -> Self {
Self::Center(unsafe {
ShareId::from_ptr(msg_send![view, centerXAnchor])
})
}
/// Boilerplate for handling constraint construction and panic'ing with some more helpful
/// messages. The goal here is to make AutoLayout slightly easier to debug when things go
/// wrong.
fn constraint_with<F>(&self, anchor_to: &LayoutAnchorX, handler: F) -> LayoutConstraint
where
F: Fn(&ShareId<Object>, &ShareId<Object>) -> id
{
match (self, anchor_to) {
// The anchors that can connect to each other. These blocks could be condensed, but are
// kept separate for readability reasons.
(Self::Leading(from), Self::Leading(to)) | (Self::Leading(from), Self::Trailing(to)) |
(Self::Leading(from), Self::Center(to)) => {
LayoutConstraint::new(handler(from, to))
},
(Self::Trailing(from), Self::Trailing(to)) | (Self::Trailing(from), Self::Leading(to)) |
(Self::Trailing(from), Self::Center(to)) => {
LayoutConstraint::new(handler(from, to))
},
(Self::Left(from), Self::Left(to)) | (Self::Left(from), Self::Right(to)) |
(Self::Left(from), Self::Center(to)) => {
LayoutConstraint::new(handler(from, to))
},
(Self::Right(from), Self::Right(to)) | (Self::Right(from), Self::Left(to)) |
(Self::Right(from), Self::Center(to)) => {
LayoutConstraint::new(handler(from, to))
},
(Self::Center(from), Self::Center(to)) | (Self::Center(from), Self::Leading(to)) |
(Self::Center(from), Self::Trailing(to)) | (Self::Center(from), Self::Left(to)) |
(Self::Center(from), Self::Right(to)) => {
LayoutConstraint::new(handler(from, to))
},
// These anchors explicitly cannot be attached to each other, as it results in
// undefined/unexpected layout behavior when a system has differing ltr/rtl setups.
(Self::Leading(_), Self::Left(_)) | (Self::Left(_), Self::Leading(_)) => {
panic!(r#"
Attempted to attach a "leading" constraint to a "left" constraint. This will
result in undefined behavior for LTR and RTL system settings, and Cacao blocks this.
Use either left/right or leading/trailing.
"#);
},
(Self::Leading(_), Self::Right(_)) | (Self::Right(_), Self::Leading(_)) => {
panic!(r#"
Attempted to attach a "leading" constraint to a "right" constraint. This will
result in undefined behavior for LTR and RTL system settings, and Cacao blocks this.
Use either left/right or leading/trailing.
"#);
},
(Self::Trailing(_), Self::Left(_)) | (Self::Left(_), Self::Trailing(_)) => {
panic!(r#"
Attempted to attach a "trailing" constraint to a "left" constraint. This will
result in undefined behavior for LTR and RTL system settings, and Cacao blocks this.
Use either left/right or leading/trailing.
"#);
},
(Self::Trailing(_), Self::Right(_)) | (Self::Right(_), Self::Trailing(_)) => {
panic!(r#"
Attempted to attach a "trailing" constraint to a "right" constraint. This will
result in undefined behavior for LTR and RTL system settings, and Cacao blocks this.
Use either left/right or leading/trailing.
"#);
},
// If anything is attempted with an uninitialized anchor, then block it.
(Self::Uninitialized, Self::Uninitialized) => {
panic!("Attempted to create constraints with an uninitialized \"from\" and \"to\" X anchor.");
},
(Self::Uninitialized, _) => {
panic!("Attempted to create constraints with an uninitialized \"from\" X anchor.");
},
(_, Self::Uninitialized) => {
panic!("Attempted to create constraints with an uninitialized \"to\" X anchor.");
}
}
} }
/// Return a constraint equal to another horizontal anchor. /// Return a constraint equal to another horizontal anchor.
pub fn constraint_equal_to(&self, anchor_to: &LayoutAnchorX) -> LayoutConstraint { pub fn constraint_equal_to(&self, anchor_to: &LayoutAnchorX) -> LayoutConstraint {
match (&self.0, &anchor_to.0) { self.constraint_with(anchor_to, |from, to| unsafe {
(Some(from), Some(to)) => LayoutConstraint::new(unsafe { msg_send![*from, constraintEqualToAnchor:&**to]
msg_send![*from, constraintEqualToAnchor:&**to] })
}),
_ => { panic!("Attempted to create horizontal constraints with an uninitialized anchor!"); }
}
} }
/// Return a constraint greater than or equal to another horizontal anchor. /// Return a constraint greater than or equal to another horizontal anchor.
pub fn constraint_greater_than_or_equal_to(&self, anchor_to: &LayoutAnchorX) -> LayoutConstraint { pub fn constraint_greater_than_or_equal_to(&self, anchor_to: &LayoutAnchorX) -> LayoutConstraint {
match (&self.0, &anchor_to.0) { self.constraint_with(anchor_to, |from, to| unsafe {
(Some(from), Some(to)) => LayoutConstraint::new(unsafe { msg_send![*from, constraintGreaterThanOrEqualToAnchor:&**to]
msg_send![*from, constraintGreaterThanOrEqualToAnchor:&**to] })
}),
_ => { panic!("Attempted to create horizontal constraints with an uninitialized anchor!"); }
}
} }
/// Return a constraint less than or equal to another horizontal anchor. /// Return a constraint less than or equal to another horizontal anchor.
pub fn constraint_less_than_or_equal_to(&self, anchor_to: &LayoutAnchorX) -> LayoutConstraint { pub fn constraint_less_than_or_equal_to(&self, anchor_to: &LayoutAnchorX) -> LayoutConstraint {
match (&self.0, &anchor_to.0) { self.constraint_with(anchor_to, |from, to| unsafe {
(Some(from), Some(to)) => LayoutConstraint::new(unsafe { msg_send![*from, constraintLessThanOrEqualToAnchor:&**to]
msg_send![*from, constraintLessThanOrEqualToAnchor:&**to] })
}),
_ => { panic!("Attempted to create horizontal constraints with an uninitialized anchor!"); }
}
} }
} }

View file

@ -1,16 +1,53 @@
//! Various traits related to controllers opting in to autolayout routines and support for view //! Various traits related to controllers opting in to autolayout routines and support for view
//! heirarchies. //! heirarchies.
use core_graphics::geometry::{CGRect, CGPoint, CGSize};
use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
use crate::foundation::{id, YES, NO};
use crate::geometry::Rect;
/// A trait that view wrappers must conform to. Enables managing the subview tree. /// A trait that view wrappers must conform to. Enables managing the subview tree.
#[allow(unused_variables)]
pub trait Layout { pub trait Layout {
/// Returns a reference to the backing Objective-C layer. This is optional, as we try to keep /// Returns a reference to the backing Objective-C layer. This is optional, as we try to keep
/// the general lazy-loading approach Cocoa has. This may change in the future, and in general /// the general lazy-loading approach Cocoa has. This may change in the future, and in general
/// this shouldn't affect your code too much (if at all). /// this shouldn't affect your code too much (if at all).
fn get_backing_node(&self) -> ShareId<Object>; fn get_backing_node(&self) -> ShareId<Object>;
/// This trait should implement adding a view to the subview tree for a given view. /// This trait method should implement adding a view to the subview tree for a given view.
fn add_subview<V: Layout>(&self, _view: &V); fn add_subview<V: Layout>(&self, view: &V);
/// 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<R: Into<CGRect>>(&self, rect: R) {
let backing_node = self.get_backing_node();
let frame: CGRect = rect.into();
unsafe {
let _: () = msg_send![&*backing_node, setFrame:frame];
}
}
/// Sets whether the view for this trait should translate autoresizing masks into layout
/// constraints.
///
/// Cacao defaults this to `false`; if you need to set frame-based layout pieces,
/// then you should set this to `true` (or use an appropriate initializer that does it for you).
fn set_translates_autoresizing_mask_into_constraints(&self, translates: bool) {
let backing_node = self.get_backing_node();
unsafe {
let _: () = msg_send![&*backing_node, setTranslatesAutoresizingMaskIntoConstraints:match translates {
true => YES,
false => NO
}];
}
}
} }

View file

@ -1,7 +1,3 @@
//! A wrapper for `NSLayoutAnchorX`, which is typically used to handle values for how a
//! given view should layout along the x-axis. Of note: the only thing that can't be protected
//! against is mixing/matching incorrect left/leading and right/trailing anchors. Be careful!
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
@ -9,50 +5,102 @@ use objc_id::ShareId;
use crate::foundation::id; use crate::foundation::id;
use crate::layout::constraint::LayoutConstraint; use crate::layout::constraint::LayoutConstraint;
/// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a /// A wrapper for `NSLayoutAnchorY`, used to handle values for how a given view should
/// factory/helper for creating `LayoutConstraint` objects based on your views. /// layout along the y-axis.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug)]
pub struct LayoutAnchorY(pub Option<ShareId<Object>>); pub enum LayoutAnchorY {
/// Represents an uninitialized anchor (e.g, for a view that's not created yet).
Uninitialized,
/// Represents a top anchor.
Top(ShareId<Object>),
/// Represents a bottom anchor.
Bottom(ShareId<Object>),
/// Represents a center anchor for the Y axis.
Center(ShareId<Object>)
}
impl Default for LayoutAnchorY {
fn default() -> Self {
Self::Uninitialized
}
}
impl LayoutAnchorY { impl LayoutAnchorY {
/// An internal method for wrapping existing constraints. /// Given a view, returns an anchor for the top anchor.
pub(crate) fn new(object: id) -> Self { pub(crate) fn top(view: id) -> Self {
LayoutAnchorY(Some(unsafe { Self::Top(unsafe {
ShareId::from_ptr(object) ShareId::from_ptr(msg_send![view, topAnchor])
})) })
}
/// Given a view, returns an anchor for the bottom anchor.
pub(crate) fn bottom(view: id) -> Self {
Self::Bottom(unsafe {
ShareId::from_ptr(msg_send![view, bottomAnchor])
})
}
/// Given a view, returns an anchor for the center Y anchor.
pub(crate) fn center(view: id) -> Self {
Self::Center(unsafe {
ShareId::from_ptr(msg_send![view, centerYAnchor])
})
}
/// Boilerplate for handling constraint construction and panic'ing with some more helpful
/// messages. The goal here is to make AutoLayout slightly easier to debug when things go
/// wrong.
fn constraint_with<F>(&self, anchor_to: &LayoutAnchorY, handler: F) -> LayoutConstraint
where
F: Fn(&ShareId<Object>, &ShareId<Object>) -> id
{
match (self, anchor_to) {
(Self::Top(from), Self::Top(to)) | (Self::Top(from), Self::Bottom(to)) |
(Self::Top(from), Self::Center(to)) |
(Self::Bottom(from), Self::Bottom(to)) | (Self::Bottom(from), Self::Top(to)) |
(Self::Bottom(from), Self::Center(to)) |
(Self::Center(from), Self::Center(to)) | (Self::Center(from), Self::Top(to)) |
(Self::Center(from), Self::Bottom(to)) => {
LayoutConstraint::new(handler(from, to))
},
(Self::Uninitialized, Self::Uninitialized) => {
panic!("Attempted to create constraints with uninitialized \"from\" and \"to\" y anchors.");
},
(Self::Uninitialized, _) => {
panic!("Attempted to create constraints with an uninitialized \"from\" y anchor.");
},
(_, Self::Uninitialized) => {
panic!("Attempted to create constraints with an uninitialized \"to\" y anchor.");
}
}
} }
/// Return a constraint equal to another vertical anchor. /// Return a constraint equal to another vertical anchor.
pub fn constraint_equal_to(&self, anchor_to: &LayoutAnchorY) -> LayoutConstraint { pub fn constraint_equal_to(&self, anchor_to: &LayoutAnchorY) -> LayoutConstraint {
match (&self.0, &anchor_to.0) { self.constraint_with(anchor_to, |from, to| unsafe {
(Some(from), Some(to)) => LayoutConstraint::new(unsafe { msg_send![*from, constraintEqualToAnchor:&**to]
let b: id = msg_send![*from, constraintEqualToAnchor:&**to]; })
b
}),
_ => { panic!("Attempted to create vertical constraints with an uninitialized anchor!"); }
}
} }
/// Return a constraint greater than or equal to another vertical anchor. /// Return a constraint greater than or equal to another vertical anchor.
pub fn constraint_greater_than_or_equal_to(&self, anchor_to: &LayoutAnchorY) -> LayoutConstraint { pub fn constraint_greater_than_or_equal_to(&self, anchor_to: &LayoutAnchorY) -> LayoutConstraint {
match (&self.0, &anchor_to.0) { self.constraint_with(anchor_to, |from, to| unsafe {
(Some(from), Some(to)) => LayoutConstraint::new(unsafe { msg_send![*from, constraintGreaterThanOrEqualToAnchor:&**to]
msg_send![*from, constraintGreaterThanOrEqualToAnchor:&**to] })
}),
_ => { panic!("Attempted to create vertical constraints with an uninitialized anchor!"); }
}
} }
/// Return a constraint less than or equal to another vertical anchor. /// Return a constraint less than or equal to another vertical anchor.
pub fn constraint_less_than_or_equal_to(&self, anchor_to: &LayoutAnchorY) -> LayoutConstraint { pub fn constraint_less_than_or_equal_to(&self, anchor_to: &LayoutAnchorY) -> LayoutConstraint {
match (&self.0, &anchor_to.0) { self.constraint_with(anchor_to, |from, to| unsafe {
(Some(from), Some(to)) => LayoutConstraint::new(unsafe { msg_send![*from, constraintLessThanOrEqualToAnchor:&**to]
msg_send![*from, constraintLessThanOrEqualToAnchor:&**to] })
}),
_ => { panic!("Attempted to create vertical constraints with an uninitialized anchor!"); }
}
} }
} }

View file

@ -200,9 +200,15 @@ pub struct ListView<T = ()> {
/// A pointer to the Objective-C runtime leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint. /// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint. /// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
@ -243,24 +249,27 @@ impl ListView {
}; };
// For macOS, we need to use the NSScrollView anchor points, not the NSTableView. // For macOS, we need to use the NSScrollView anchor points, not the NSTableView.
// @TODO: Fix this with proper mutable access.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let anchor_view = &*scrollview.objc; let anchor_view: id = unsafe { msg_send![&*scrollview.objc, self] };
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
let anchor_view = view; let anchor_view: id = view;
ListView { ListView {
cell_factory: CellFactory::new(), cell_factory: CellFactory::new(),
menu: PropertyNullable::default(), menu: PropertyNullable::default(),
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![anchor_view, topAnchor] }), top: LayoutAnchorY::top(anchor_view),
leading: LayoutAnchorX::new(unsafe { msg_send![anchor_view, leadingAnchor] }), left: LayoutAnchorX::left(anchor_view),
trailing: LayoutAnchorX::new(unsafe { msg_send![anchor_view, trailingAnchor] }), leading: LayoutAnchorX::leading(anchor_view),
bottom: LayoutAnchorY::new(unsafe { msg_send![anchor_view, bottomAnchor] }), right: LayoutAnchorX::right(anchor_view),
width: LayoutAnchorDimension::new(unsafe { msg_send![anchor_view, widthAnchor] }), trailing: LayoutAnchorX::trailing(anchor_view),
height: LayoutAnchorDimension::new(unsafe { msg_send![anchor_view, heightAnchor] }), bottom: LayoutAnchorY::bottom(anchor_view),
center_x: LayoutAnchorX::new(unsafe { msg_send![anchor_view, centerXAnchor] }), width: LayoutAnchorDimension::width(anchor_view),
center_y: LayoutAnchorY::new(unsafe { msg_send![anchor_view, centerYAnchor] }), height: LayoutAnchorDimension::height(anchor_view),
center_x: LayoutAnchorX::center(anchor_view),
center_y: LayoutAnchorY::center(anchor_view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@ -300,7 +309,7 @@ impl<T> ListView<T> where T: ListViewDelegate + 'static {
// For macOS, we need to use the NSScrollView anchor points, not the NSTableView. // For macOS, we need to use the NSScrollView anchor points, not the NSTableView.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let anchor_view = &*scrollview.objc; let anchor_view: id = unsafe { msg_send![&*scrollview.objc, self] };
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
let anchor_view = view; let anchor_view = view;
@ -309,14 +318,16 @@ impl<T> ListView<T> where T: ListViewDelegate + 'static {
cell_factory: cell, cell_factory: cell,
menu: PropertyNullable::default(), menu: PropertyNullable::default(),
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![anchor_view, topAnchor] }), top: LayoutAnchorY::top(anchor_view),
leading: LayoutAnchorX::new(unsafe { msg_send![anchor_view, leadingAnchor] }), left: LayoutAnchorX::left(anchor_view),
trailing: LayoutAnchorX::new(unsafe { msg_send![anchor_view, trailingAnchor] }), leading: LayoutAnchorX::leading(anchor_view),
bottom: LayoutAnchorY::new(unsafe { msg_send![anchor_view, bottomAnchor] }), right: LayoutAnchorX::right(anchor_view),
width: LayoutAnchorDimension::new(unsafe { msg_send![anchor_view, widthAnchor] }), trailing: LayoutAnchorX::trailing(anchor_view),
height: LayoutAnchorDimension::new(unsafe { msg_send![anchor_view, heightAnchor] }), bottom: LayoutAnchorY::bottom(anchor_view),
center_x: LayoutAnchorX::new(unsafe { msg_send![anchor_view, centerXAnchor] }), width: LayoutAnchorDimension::width(anchor_view),
center_y: LayoutAnchorY::new(unsafe { msg_send![anchor_view, centerYAnchor] }), height: LayoutAnchorDimension::height(anchor_view),
center_x: LayoutAnchorX::center(anchor_view),
center_y: LayoutAnchorY::center(anchor_view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@ -341,7 +352,9 @@ impl<T> ListView<T> {
delegate: None, delegate: None,
top: self.top.clone(), top: self.top.clone(),
leading: self.leading.clone(), leading: self.leading.clone(),
left: self.left.clone(),
trailing: self.trailing.clone(), trailing: self.trailing.clone(),
right: self.right.clone(),
bottom: self.bottom.clone(), bottom: self.bottom.clone(),
width: self.width.clone(), width: self.width.clone(),
height: self.height.clone(), height: self.height.clone(),

View file

@ -92,16 +92,22 @@ pub struct ListViewRow<T = ()> {
/// A pointer to the delegate for this view. /// A pointer to the delegate for this view.
pub delegate: Option<Box<T>>, pub delegate: Option<Box<T>>,
/// A pointer to the Objective-C runtime top layout constraint. /// A pointer to the Objective-C runtime top layout constraint.
pub top: LayoutAnchorY, pub top: LayoutAnchorY,
/// A pointer to the Objective-C runtime leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint. /// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint. /// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
@ -131,14 +137,16 @@ impl ListViewRow {
ListViewRow { ListViewRow {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })), objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })),
} }
} }
@ -166,14 +174,16 @@ impl<T> ListViewRow<T> where T: ViewDelegate + 'static {
let view = ListViewRow { let view = ListViewRow {
delegate: Some(delegate), delegate: Some(delegate),
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })), objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })),
}; };
@ -198,14 +208,16 @@ impl<T> ListViewRow<T> where T: ViewDelegate + 'static {
let mut view = ListViewRow { let mut view = ListViewRow {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })), objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })),
}; };
@ -226,7 +238,9 @@ impl<T> ListViewRow<T> where T: ViewDelegate + 'static {
delegate: None, delegate: None,
top: self.top.clone(), top: self.top.clone(),
leading: self.leading.clone(), leading: self.leading.clone(),
left: self.left.clone(),
trailing: self.trailing.clone(), trailing: self.trailing.clone(),
right: self.right.clone(),
bottom: self.bottom.clone(), bottom: self.bottom.clone(),
width: self.width.clone(), width: self.width.clone(),
height: self.height.clone(), height: self.height.clone(),
@ -253,7 +267,9 @@ impl<T> ListViewRow<T> {
delegate: None, delegate: None,
top: self.top.clone(), top: self.top.clone(),
leading: self.leading.clone(), leading: self.leading.clone(),
left: self.left.clone(),
trailing: self.trailing.clone(), trailing: self.trailing.clone(),
right: self.right.clone(),
bottom: self.bottom.clone(), bottom: self.bottom.clone(),
width: self.width.clone(), width: self.width.clone(),
height: self.height.clone(), height: self.height.clone(),

View file

@ -35,29 +35,35 @@ pub use enums::ProgressIndicatorStyle;
pub struct ProgressIndicator { pub struct ProgressIndicator {
/// A pointer to the Objective-C Object. /// A pointer to the Objective-C Object.
pub objc: ShareId<Object>, pub objc: ShareId<Object>,
/// A pointer to the Objective-C top layout constraint. /// A pointer to the Objective-C runtime top layout constraint.
pub top: LayoutAnchorY, pub top: LayoutAnchorY,
/// A pointer to the Objective-C leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C trailing layout constraint. /// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C bottom layout constraint. /// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
/// A pointer to the Objective-C width layout constraint. /// A pointer to the Objective-C runtime width layout constraint.
pub width: LayoutAnchorDimension, pub width: LayoutAnchorDimension,
/// A pointer to the Objective-C height layout constraint. /// A pointer to the Objective-C runtime height layout constraint.
pub height: LayoutAnchorDimension, pub height: LayoutAnchorDimension,
/// A pointer to the Objective-C center X layout constraint. /// A pointer to the Objective-C runtime center X layout constraint.
pub center_x: LayoutAnchorX, pub center_x: LayoutAnchorX,
/// A pointer to the Objective-C center Y layout constraint. /// A pointer to the Objective-C runtime center Y layout constraint.
pub center_y: LayoutAnchorY pub center_y: LayoutAnchorY
} }
@ -83,14 +89,16 @@ impl ProgressIndicator {
}; };
ProgressIndicator { ProgressIndicator {
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
} }
} }

View file

@ -94,16 +94,22 @@ pub struct ScrollView<T = ()> {
/// A pointer to the delegate for this view. /// A pointer to the delegate for this view.
pub delegate: Option<Box<T>>, pub delegate: Option<Box<T>>,
/// A pointer to the Objective-C runtime top layout constraint. /// A pointer to the Objective-C runtime top layout constraint.
pub top: LayoutAnchorY, pub top: LayoutAnchorY,
/// A pointer to the Objective-C runtime leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint. /// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint. /// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
@ -133,14 +139,16 @@ impl ScrollView {
ScrollView { ScrollView {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
} }
} }
@ -160,14 +168,16 @@ impl<T> ScrollView<T> where T: ScrollViewDelegate + 'static {
let mut view = ScrollView { let mut view = ScrollView {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
}; };
@ -187,7 +197,9 @@ impl<T> ScrollView<T> {
delegate: None, delegate: None,
top: self.top.clone(), top: self.top.clone(),
leading: self.leading.clone(), leading: self.leading.clone(),
left: self.left.clone(),
trailing: self.trailing.clone(), trailing: self.trailing.clone(),
right: self.right.clone(),
bottom: self.bottom.clone(), bottom: self.bottom.clone(),
width: self.width.clone(), width: self.width.clone(),
height: self.height.clone(), height: self.height.clone(),

View file

@ -28,9 +28,15 @@ pub struct Switch {
/// A pointer to the Objective-C runtime leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint. /// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint. /// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
@ -45,6 +51,7 @@ pub struct Switch {
/// A pointer to the Objective-C runtime center Y layout constraint. /// A pointer to the Objective-C runtime center Y layout constraint.
pub center_y: LayoutAnchorY pub center_y: LayoutAnchorY
} }
impl Switch { impl Switch {
@ -62,14 +69,16 @@ impl Switch {
Switch { Switch {
handler: None, handler: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
} }
} }

View file

@ -97,16 +97,22 @@ pub struct Label<T = ()> {
/// A pointer to the delegate for this view. /// A pointer to the delegate for this view.
pub delegate: Option<Box<T>>, pub delegate: Option<Box<T>>,
/// A pointer to the Objective-C runtime top layout constraint. /// A pointer to the Objective-C runtime top layout constraint.
pub top: LayoutAnchorY, pub top: LayoutAnchorY,
/// A pointer to the Objective-C runtime leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint. /// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint. /// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
@ -136,14 +142,16 @@ impl Label {
Label { Label {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
} }
} }
@ -165,14 +173,16 @@ impl<T> Label<T> where T: LabelDelegate + 'static {
let mut label = Label { let mut label = Label {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![label, topAnchor] }), top: LayoutAnchorY::top(label),
leading: LayoutAnchorX::new(unsafe { msg_send![label, leadingAnchor] }), left: LayoutAnchorX::left(label),
trailing: LayoutAnchorX::new(unsafe { msg_send![label, trailingAnchor] }), leading: LayoutAnchorX::leading(label),
bottom: LayoutAnchorY::new(unsafe { msg_send![label, bottomAnchor] }), right: LayoutAnchorX::right(label),
width: LayoutAnchorDimension::new(unsafe { msg_send![label, widthAnchor] }), trailing: LayoutAnchorX::trailing(label),
height: LayoutAnchorDimension::new(unsafe { msg_send![label, heightAnchor] }), bottom: LayoutAnchorY::bottom(label),
center_x: LayoutAnchorX::new(unsafe { msg_send![label, centerXAnchor] }), width: LayoutAnchorDimension::width(label),
center_y: LayoutAnchorY::new(unsafe { msg_send![label, centerYAnchor] }), height: LayoutAnchorDimension::height(label),
center_x: LayoutAnchorX::center(label),
center_y: LayoutAnchorY::center(label),
objc: unsafe { ShareId::from_ptr(label) }, objc: unsafe { ShareId::from_ptr(label) },
}; };
@ -192,7 +202,9 @@ impl<T> Label<T> {
delegate: None, delegate: None,
top: self.top.clone(), top: self.top.clone(),
leading: self.leading.clone(), leading: self.leading.clone(),
left: self.left.clone(),
trailing: self.trailing.clone(), trailing: self.trailing.clone(),
right: self.right.clone(),
bottom: self.bottom.clone(), bottom: self.bottom.clone(),
width: self.width.clone(), width: self.width.clone(),
height: self.height.clone(), height: self.height.clone(),

View file

@ -107,9 +107,15 @@ pub struct View<T = ()> {
/// A pointer to the Objective-C runtime leading layout constraint. /// A pointer to the Objective-C runtime leading layout constraint.
pub leading: LayoutAnchorX, pub leading: LayoutAnchorX,
/// A pointer to the Objective-C runtime left layout constraint.
pub left: LayoutAnchorX,
/// A pointer to the Objective-C runtime trailing layout constraint. /// A pointer to the Objective-C runtime trailing layout constraint.
pub trailing: LayoutAnchorX, pub trailing: LayoutAnchorX,
/// A pointer to the Objective-C runtime right layout constraint.
pub right: LayoutAnchorX,
/// A pointer to the Objective-C runtime bottom layout constraint. /// A pointer to the Objective-C runtime bottom layout constraint.
pub bottom: LayoutAnchorY, pub bottom: LayoutAnchorY,
@ -139,14 +145,16 @@ impl View {
View { View {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })), objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })),
} }
} }
@ -169,14 +177,16 @@ impl<T> View<T> where T: ViewDelegate + 'static {
let mut view = View { let mut view = View {
delegate: None, delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::top(view),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), left: LayoutAnchorX::left(view),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), leading: LayoutAnchorX::leading(view),
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }), right: LayoutAnchorX::right(view),
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }), trailing: LayoutAnchorX::trailing(view),
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), bottom: LayoutAnchorY::bottom(view),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), width: LayoutAnchorDimension::width(view),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), height: LayoutAnchorDimension::height(view),
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),
objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })), objc: Rc::new(RefCell::new(unsafe { Id::from_ptr(view) })),
}; };
@ -196,7 +206,9 @@ impl<T> View<T> {
delegate: None, delegate: None,
top: self.top.clone(), top: self.top.clone(),
leading: self.leading.clone(), leading: self.leading.clone(),
left: self.left.clone(),
trailing: self.trailing.clone(), trailing: self.trailing.clone(),
right: self.right.clone(),
bottom: self.bottom.clone(), bottom: self.bottom.clone(),
width: self.width.clone(), width: self.width.clone(),
height: self.height.clone(), height: self.height.clone(),