Fixed color math, and ported AutoLayout
This commit is contained in:
parent
3d93b04455
commit
86980d7329
6
appkit/src/collection_view/traits.rs
Normal file
6
appkit/src/collection_view/traits.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
//! Traits used for `CollectionView` controllers. More or less maps to the iOS/macOS routines,
|
||||||
|
//! mapping over the differences between them where appropriate.
|
||||||
|
|
||||||
|
pub trait CollectionViewController {
|
||||||
|
|
||||||
|
}
|
|
@ -30,15 +30,32 @@ impl Color {
|
||||||
/// green, blue and alpha channels in that order, and all values will be
|
/// green, blue and alpha channels in that order, and all values will be
|
||||||
/// clamped to the 0.0 ... 1.0 range.
|
/// clamped to the 0.0 ... 1.0 range.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new<T: Into<f32>>(red: T, green: T, blue: T, alpha: T) -> Self {
|
pub fn from_floats(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
|
||||||
Self::from_u8s(
|
Self::new(
|
||||||
clamp_unit_f32(red.into()),
|
clamp_unit_f32(red),
|
||||||
clamp_unit_f32(green.into()),
|
clamp_unit_f32(green),
|
||||||
clamp_unit_f32(blue.into()),
|
clamp_unit_f32(blue),
|
||||||
clamp_unit_f32(alpha.into()),
|
clamp_unit_f32(alpha),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a transparent color.
|
||||||
|
#[inline]
|
||||||
|
pub fn transparent() -> Self {
|
||||||
|
Self::new(0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same thing, but with `u8` values instead of floats in the 0 to 1 range.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
|
||||||
|
Color {
|
||||||
|
red: red,
|
||||||
|
green: green,
|
||||||
|
blue: blue,
|
||||||
|
alpha: alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Maps to NS/UIColor.
|
/// Maps to NS/UIColor.
|
||||||
pub fn into_platform_specific_color(&self) -> id {
|
pub fn into_platform_specific_color(&self) -> id {
|
||||||
let red = self.red as CGFloat / 255.0;
|
let red = self.red as CGFloat / 255.0;
|
||||||
|
@ -51,23 +68,6 @@ impl Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a transparent color.
|
|
||||||
#[inline]
|
|
||||||
pub fn transparent() -> Self {
|
|
||||||
Self::new(0., 0., 0., 0.)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same thing, but with `u8` values instead of floats in the 0 to 1 range.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_u8s(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
|
|
||||||
Color {
|
|
||||||
red: red,
|
|
||||||
green: green,
|
|
||||||
blue: blue,
|
|
||||||
alpha: alpha,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the red channel in a floating point number form, from 0 to 1.
|
/// Returns the red channel in a floating point number form, from 0 to 1.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn red_f32(&self) -> f32 {
|
pub fn red_f32(&self) -> f32 {
|
||||||
|
@ -91,18 +91,45 @@ impl Color {
|
||||||
pub fn alpha_f32(&self) -> f32 {
|
pub fn alpha_f32(&self) -> f32 {
|
||||||
self.alpha as f32 / 255.0
|
self.alpha as f32 / 255.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a <color> value, per CSS Color Module Level 3.
|
||||||
|
///
|
||||||
|
/// FIXME(#2) Deprecated CSS2 System Colors are not supported yet.
|
||||||
|
#[cfg(feature="parser")]
|
||||||
|
pub fn parse_with<'i, 't, ComponentParser>(
|
||||||
|
component_parser: &ComponentParser,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Color, ParseError<'i, ComponentParser::Error>>
|
||||||
|
where
|
||||||
|
ComponentParser: ColorComponentParser<'i>,
|
||||||
|
{
|
||||||
|
// FIXME: remove clone() when lifetimes are non-lexical
|
||||||
|
let location = input.current_source_location();
|
||||||
|
let token = input.next()?.clone();
|
||||||
|
match token {
|
||||||
|
Token::Hash(ref value) | Token::IDHash(ref value) => {
|
||||||
|
Color::parse_hash(value.as_bytes())
|
||||||
|
}
|
||||||
|
Token::Ident(ref value) => parse_color_keyword(&*value),
|
||||||
|
Token::Function(ref name) => {
|
||||||
|
return input.parse_nested_block(|arguments| {
|
||||||
|
parse_color_function(component_parser, &*name, arguments)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
.map_err(|()| location.new_unexpected_token_error(token))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A less-verbose way of specifying a generic color, without alpha.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rgb<T: Into<f32>>(red: T, green: T, blue: T) -> Color {
|
pub fn rgb(red: u8, green: u8, blue: u8) -> Color {
|
||||||
rgba(red.into(), green.into(), blue.into(), 1.)
|
rgba(red, green, blue, 255)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A less-verbose way of specifying a generic color, with alpha.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rgba<T: Into<f32>>(red: T, green: T, blue: T, alpha: T) -> Color {
|
pub fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
|
||||||
Color::new(red.into(), green.into(), blue.into(), alpha.into())
|
Color::new(red, green, blue, alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clamp_unit_f32(val: f32) -> u8 {
|
fn clamp_unit_f32(val: f32) -> u8 {
|
||||||
|
|
77
appkit/src/layout/constraint.rs
Normal file
77
appkit/src/layout/constraint.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//! A wrapper for `NSLayoutConstraint` that's more general in nature. You can think of this as an
|
||||||
|
//! escape hatch, if you need it (we use it for things like width and height, which aren't handled
|
||||||
|
//! by an axis).
|
||||||
|
|
||||||
|
use cocoa::base::id;
|
||||||
|
use core_graphics::base::CGFloat;
|
||||||
|
|
||||||
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
/// A wrapper for `NSLayoutConstraint`. This both acts as a central path through which to activate
|
||||||
|
/// constraints, as well as a wrapper for layout constraints that are not axis bound (e.g, width or
|
||||||
|
/// height).
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct LayoutConstraint {
|
||||||
|
/// A shared pointer to the underlying view. Provided your view isn't dropped, this will always
|
||||||
|
/// be valid.
|
||||||
|
pub constraint: ShareId<Object>,
|
||||||
|
|
||||||
|
/// The offset used in computing this constraint.
|
||||||
|
pub offset: f64,
|
||||||
|
|
||||||
|
/// The multiplier used in computing this constraint.
|
||||||
|
pub multiplier: f64,
|
||||||
|
|
||||||
|
/// The priority used in computing this constraint.
|
||||||
|
pub priority: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutConstraint {
|
||||||
|
/// An internal method for wrapping existing constraints.
|
||||||
|
pub(crate) fn new(object: id) -> Self {
|
||||||
|
LayoutConstraint {
|
||||||
|
constraint: unsafe { ShareId::from_ptr(object) },
|
||||||
|
offset: 0.0,
|
||||||
|
multiplier: 0.0,
|
||||||
|
priority: 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the offset for this constraint.
|
||||||
|
pub fn offset<F: Into<f64>>(self, offset: F) -> Self {
|
||||||
|
let offset: f64 = offset.into();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let o = offset as CGFloat;
|
||||||
|
let _: () = msg_send![&*self.constraint, setConstant:o];
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutConstraint {
|
||||||
|
constraint: self.constraint,
|
||||||
|
offset: offset,
|
||||||
|
multiplier: self.multiplier,
|
||||||
|
priority: self.priority
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call this with your batch of constraints to activate them.
|
||||||
|
// If you're astute, you'll note that, yes... this is kind of hacking around some
|
||||||
|
// borrowing rules with how objc_id::Id/objc_id::ShareId works. In this case, to
|
||||||
|
// support the way autolayout constraints work over in the cocoa runtime, we need to be
|
||||||
|
// able to clone these and pass them around... while also getting certain references to
|
||||||
|
// them.
|
||||||
|
//
|
||||||
|
// I regret nothing, lol. If you have a better solution I'm all ears.
|
||||||
|
pub fn activate(constraints: &[LayoutConstraint]) {
|
||||||
|
unsafe {
|
||||||
|
let ids: Vec<&Object> = constraints.into_iter().map(|constraint| {
|
||||||
|
&*constraint.constraint
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let constraints: id = msg_send![class!(NSArray), arrayWithObjects:ids.as_ptr() count:ids.len()];
|
||||||
|
let _: () = msg_send![class!(NSLayoutConstraint), activateConstraints:constraints];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
appkit/src/layout/dimension.rs
Normal file
70
appkit/src/layout/dimension.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
//! A wrapper for `NSLayoutAnchorDimension`, which is typically used to handle `width` and `height`
|
||||||
|
//! values for how a given view should layout.
|
||||||
|
|
||||||
|
use cocoa::base::id;
|
||||||
|
use core_graphics::base::CGFloat;
|
||||||
|
|
||||||
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
use crate::layout::constraint::LayoutConstraint;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct LayoutAnchorDimension(pub Option<ShareId<Object>>);
|
||||||
|
|
||||||
|
impl LayoutAnchorDimension {
|
||||||
|
/// An internal method for wrapping existing anchors.
|
||||||
|
pub(crate) fn new(object: id) -> Self {
|
||||||
|
LayoutAnchorDimension(Some(unsafe {
|
||||||
|
ShareId::from_ptr(object)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a constraint equal to a constant value.
|
||||||
|
pub fn constraint_equal_to_constant(&self, constant: f64) -> LayoutConstraint {
|
||||||
|
match &self.0 {
|
||||||
|
Some(from) => LayoutConstraint::new(unsafe {
|
||||||
|
let value = constant as CGFloat;
|
||||||
|
msg_send![*from, constraintEqualToConstant:value]
|
||||||
|
}),
|
||||||
|
|
||||||
|
_ => { panic!("Attempted to create constraints with an uninitialized anchor!"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a constraint equal to another dimension anchor.
|
||||||
|
pub fn constraint_equal_to(&self, anchor_to: &LayoutAnchorDimension) -> LayoutConstraint {
|
||||||
|
match (&self.0, &anchor_to.0) {
|
||||||
|
(Some(from), Some(to)) => LayoutConstraint::new(unsafe {
|
||||||
|
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.
|
||||||
|
pub fn constraint_greater_than_or_equal_to(&self, anchor_to: &LayoutAnchorDimension) -> LayoutConstraint {
|
||||||
|
match (&self.0, &anchor_to.0) {
|
||||||
|
(Some(from), Some(to)) => LayoutConstraint::new(unsafe {
|
||||||
|
msg_send![*from, constraintGreaterThanOrEqualToAnchor:&*to]
|
||||||
|
}),
|
||||||
|
|
||||||
|
_ => { panic!("Attempted to create constraints with an uninitialized 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 {
|
||||||
|
match (&self.0, &anchor_to.0) {
|
||||||
|
(Some(from), Some(to)) => LayoutConstraint::new(unsafe {
|
||||||
|
msg_send![*from, constraintLessThanOrEqualToAnchor:&*to]
|
||||||
|
}),
|
||||||
|
|
||||||
|
_ => { panic!("Attempted to create constraints with an uninitialized anchor!"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
appkit/src/layout/horizontal.rs
Normal file
58
appkit/src/layout/horizontal.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
//! 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 cocoa::base::id;
|
||||||
|
|
||||||
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
use crate::layout::constraint::LayoutConstraint;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct LayoutAnchorX(pub Option<ShareId<Object>>);
|
||||||
|
|
||||||
|
impl LayoutAnchorX {
|
||||||
|
/// An internal method for wrapping existing anchors.
|
||||||
|
pub(crate) fn new(object: id) -> Self {
|
||||||
|
LayoutAnchorX(Some(unsafe {
|
||||||
|
ShareId::from_ptr(object)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a constraint equal to another horizontal anchor.
|
||||||
|
pub fn constraint_equal_to(&self, anchor_to: &LayoutAnchorX) -> LayoutConstraint {
|
||||||
|
match (&self.0, &anchor_to.0) {
|
||||||
|
(Some(from), Some(to)) => LayoutConstraint::new(unsafe {
|
||||||
|
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.
|
||||||
|
pub fn constraint_greater_than_or_equal_to(&self, anchor_to: &LayoutAnchorX) -> LayoutConstraint {
|
||||||
|
match (&self.0, &anchor_to.0) {
|
||||||
|
(Some(from), Some(to)) => LayoutConstraint::new(unsafe {
|
||||||
|
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.
|
||||||
|
pub fn constraint_less_than_or_equal_to(&self, anchor_to: &LayoutAnchorX) -> LayoutConstraint {
|
||||||
|
match (&self.0, &anchor_to.0) {
|
||||||
|
(Some(from), Some(to)) => LayoutConstraint::new(unsafe {
|
||||||
|
msg_send![*from, constraintLessThanOrEqualToAnchor:&*to]
|
||||||
|
}),
|
||||||
|
|
||||||
|
_ => { panic!("Attempted to create horizontal constraints with an uninitialized anchor!"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
//! A wrapper for `NSLayoutConstraint`, enabling AutoLayout across views. This does a few things
|
||||||
|
//! that might seem weird, but are generally good and rely on the idea that this is all written
|
||||||
|
//! once and used often.
|
||||||
|
//!
|
||||||
|
//! Notably: there are 3 structs for wrapping layout constraints; in practice, you likely don't need to
|
||||||
|
//! care. This is because we want to detect at compile time invalid layout items - i.e, you should
|
||||||
|
//! not be able to attach a left-axis to a top-axis. In Rust this is a bit tricky, but by using
|
||||||
|
//! some `impl Trait`'s in the right places we can mostly hide this detail away.
|
||||||
|
|
||||||
|
pub mod traits;
|
||||||
|
pub use traits::Layout;
|
||||||
|
|
||||||
|
pub mod constraint;
|
||||||
|
pub use constraint::LayoutConstraint;
|
||||||
|
|
||||||
|
pub mod dimension;
|
||||||
|
pub use dimension::LayoutAnchorDimension;
|
||||||
|
|
||||||
|
pub mod horizontal;
|
||||||
|
pub use horizontal::LayoutAnchorX;
|
||||||
|
|
||||||
|
pub mod vertical;
|
||||||
|
pub use vertical::LayoutAnchorY;
|
16
appkit/src/layout/traits.rs
Normal file
16
appkit/src/layout/traits.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//! Various traits related to controllers opting in to autolayout routines and support for view
|
||||||
|
//! heirarchies.
|
||||||
|
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
/// A trait that view wrappers must conform to. Enables managing the subview tree.
|
||||||
|
pub trait Layout {
|
||||||
|
/// 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
|
||||||
|
/// this shouldn't affect your code too much (if at all).
|
||||||
|
fn get_backing_node(&self) -> Option<ShareId<Object>>;
|
||||||
|
|
||||||
|
/// This trait should implement adding a view to the subview tree for a given view.
|
||||||
|
fn add_subview<V: Layout>(&self, _view: &V);
|
||||||
|
}
|
58
appkit/src/layout/vertical.rs
Normal file
58
appkit/src/layout/vertical.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
//! 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 cocoa::base::id;
|
||||||
|
|
||||||
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
use crate::layout::constraint::LayoutConstraint;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct LayoutAnchorY(pub Option<ShareId<Object>>);
|
||||||
|
|
||||||
|
impl LayoutAnchorY {
|
||||||
|
/// An internal method for wrapping existing constraints.
|
||||||
|
pub(crate) fn new(object: id) -> Self {
|
||||||
|
LayoutAnchorY(Some(unsafe {
|
||||||
|
ShareId::from_ptr(object)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a constraint equal to another vertical anchor.
|
||||||
|
pub fn constraint_equal_to(&self, anchor_to: &LayoutAnchorY) -> LayoutConstraint {
|
||||||
|
match (&self.0, &anchor_to.0) {
|
||||||
|
(Some(from), Some(to)) => LayoutConstraint::new(unsafe {
|
||||||
|
msg_send![*from, constraintEqualToAnchor:&*to]
|
||||||
|
}),
|
||||||
|
|
||||||
|
_ => { panic!("Attempted to create vertical constraints with an uninitialized 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 {
|
||||||
|
match (&self.0, &anchor_to.0) {
|
||||||
|
(Some(from), Some(to)) => LayoutConstraint::new(unsafe {
|
||||||
|
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.
|
||||||
|
pub fn constraint_less_than_or_equal_to(&self, anchor_to: &LayoutAnchorY) -> LayoutConstraint {
|
||||||
|
match (&self.0, &anchor_to.0) {
|
||||||
|
(Some(from), Some(to)) => LayoutConstraint::new(unsafe {
|
||||||
|
msg_send![*from, constraintLessThanOrEqualToAnchor:&*to]
|
||||||
|
}),
|
||||||
|
|
||||||
|
_ => { panic!("Attempted to create vertical constraints with an uninitialized anchor!"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ pub mod error;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod filesystem;
|
pub mod filesystem;
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
|
pub mod layout;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
pub mod networking;
|
pub mod networking;
|
||||||
pub mod notifications;
|
pub mod notifications;
|
||||||
|
@ -47,6 +48,8 @@ pub use url;
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::app::{App, AppDelegate, Dispatcher};
|
pub use crate::app::{App, AppDelegate, Dispatcher};
|
||||||
|
|
||||||
|
pub use crate::layout::LayoutConstraint;
|
||||||
|
|
||||||
pub use crate::menu::{Menu, MenuItem};
|
pub use crate::menu::{Menu, MenuItem};
|
||||||
pub use crate::notifications::{Notification, NotificationCenter, NotificationAuthOption};
|
pub use crate::notifications::{Notification, NotificationCenter, NotificationAuthOption};
|
||||||
pub use crate::toolbar::{Toolbar, ToolbarController, ToolbarHandle};
|
pub use crate::toolbar::{Toolbar, ToolbarController, ToolbarHandle};
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
//! Various traits used for Views.
|
//! Various traits used for Views.
|
||||||
|
|
||||||
use objc::runtime::Object;
|
|
||||||
|
|
||||||
use objc_id::ShareId;
|
|
||||||
|
|
||||||
use crate::dragdrop::{DragInfo, DragOperation};
|
use crate::dragdrop::{DragInfo, DragOperation};
|
||||||
use crate::view::ViewHandle;
|
use crate::view::ViewHandle;
|
||||||
|
|
||||||
pub trait Node {
|
|
||||||
fn get_backing_node(&self) -> Option<ShareId<Object>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ViewController {
|
pub trait ViewController {
|
||||||
/// Where possible, we try to respect the lazy-ish-loading of macOS/iOS systems. This hook
|
/// Where possible, we try to respect the lazy-ish-loading of macOS/iOS systems. This hook
|
||||||
/// notifies you when the `View` has actually loaded into memory, and you're free to do things
|
/// notifies you when the `View` has actually loaded into memory, and you're free to do things
|
||||||
|
|
|
@ -12,22 +12,68 @@ use objc::{msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::constants::{BACKGROUND_COLOR, VIEW_CONTROLLER_PTR};
|
use crate::constants::{BACKGROUND_COLOR, VIEW_CONTROLLER_PTR};
|
||||||
|
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||||
use crate::pasteboard::PasteboardType;
|
use crate::pasteboard::PasteboardType;
|
||||||
use crate::view::traits::{Node, ViewController};
|
|
||||||
use crate::view::controller::register_controller_class;
|
use crate::view::controller::register_controller_class;
|
||||||
|
use crate::view::traits::ViewController;
|
||||||
|
|
||||||
/// A clone-able handler to a `ViewController` reference in the Objective C runtime. We use this
|
/// A clone-able handler to a `ViewController` reference in the Objective C runtime. We use this
|
||||||
/// instead of a stock `View` for easier recordkeeping, since it'll need to hold the `View` on that
|
/// instead of a stock `View` for easier recordkeeping, since it'll need to hold the `View` on that
|
||||||
/// side anyway.
|
/// side anyway.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct ViewHandle(Option<ShareId<Object>>);
|
pub struct ViewHandle {
|
||||||
|
/// A pointer to the Objective-C runtime view controller.
|
||||||
|
pub objc: Option<ShareId<Object>>,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime top layout constraint.
|
||||||
|
pub top: LayoutAnchorY,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime leading layout constraint.
|
||||||
|
pub leading: LayoutAnchorX,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime trailing layout constraint.
|
||||||
|
pub trailing: LayoutAnchorX,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime bottom layout constraint.
|
||||||
|
pub bottom: LayoutAnchorY,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime width layout constraint.
|
||||||
|
pub width: LayoutAnchorDimension,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime height layout constraint.
|
||||||
|
pub height: LayoutAnchorDimension,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime center X layout constraint.
|
||||||
|
pub center_x: LayoutAnchorX,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime center Y layout constraint.
|
||||||
|
pub center_y: LayoutAnchorY
|
||||||
|
}
|
||||||
|
|
||||||
impl ViewHandle {
|
impl ViewHandle {
|
||||||
|
pub(crate) fn new(object: ShareId<Object>) -> Self {
|
||||||
|
let view: id = unsafe {
|
||||||
|
msg_send![&*object, view]
|
||||||
|
};
|
||||||
|
|
||||||
|
ViewHandle {
|
||||||
|
objc: Some(object),
|
||||||
|
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
|
||||||
|
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }),
|
||||||
|
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }),
|
||||||
|
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }),
|
||||||
|
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }),
|
||||||
|
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }),
|
||||||
|
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }),
|
||||||
|
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Call this to set the background color for the backing layer.
|
/// Call this to set the background color for the backing layer.
|
||||||
pub fn set_background_color(&self, color: Color) {
|
pub fn set_background_color(&self, color: Color) {
|
||||||
if let Some(controller) = &self.0 {
|
if let Some(objc) = &self.objc {
|
||||||
unsafe {
|
unsafe {
|
||||||
let view: id = msg_send![*controller, view];
|
let view: id = msg_send![*objc, view];
|
||||||
(*view).set_ivar(BACKGROUND_COLOR, color.into_platform_specific_color());
|
(*view).set_ivar(BACKGROUND_COLOR, color.into_platform_specific_color());
|
||||||
let _: () = msg_send![view, setNeedsDisplay:YES];
|
let _: () = msg_send![view, setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
@ -36,17 +82,31 @@ impl ViewHandle {
|
||||||
|
|
||||||
/// Register this view for drag and drop operations.
|
/// Register this view for drag and drop operations.
|
||||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||||
if let Some(controller) = &self.0 {
|
if let Some(objc) = &self.objc {
|
||||||
unsafe {
|
unsafe {
|
||||||
let types = NSArray::arrayWithObjects(nil, &types.iter().map(|t| {
|
let types = NSArray::arrayWithObjects(nil, &types.iter().map(|t| {
|
||||||
t.to_nsstring()
|
t.to_nsstring()
|
||||||
}).collect::<Vec<id>>());
|
}).collect::<Vec<id>>());
|
||||||
|
|
||||||
let view: id = msg_send![*controller, view];
|
let view: id = msg_send![*objc, view];
|
||||||
let _: () = msg_send![view, registerForDraggedTypes:types];
|
let _: () = msg_send![view, registerForDraggedTypes:types];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_subview<T: Layout>(&self, subview: &T) {
|
||||||
|
if let Some(this) = &self.objc {
|
||||||
|
if let Some(subview_controller) = subview.get_backing_node() {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![*this, addChildViewController:&*subview_controller];
|
||||||
|
|
||||||
|
let subview: id = msg_send![&*subview_controller, view];
|
||||||
|
let view: id = msg_send![*this, view];
|
||||||
|
let _: () = msg_send![view, addSubview:subview];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `View` wraps two different controllers - one on the Objective-C/Cocoa side, which forwards
|
/// A `View` wraps two different controllers - one on the Objective-C/Cocoa side, which forwards
|
||||||
|
@ -80,14 +140,16 @@ impl<T> View<T> where T: ViewController + 'static {
|
||||||
ShareId::from_ptr(view_controller)
|
ShareId::from_ptr(view_controller)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let handle = ViewHandle::new(inner);
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut vc = controller.borrow_mut();
|
let mut vc = controller.borrow_mut();
|
||||||
(*vc).did_load(ViewHandle(Some(inner.clone())));
|
(*vc).did_load(handle.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
View {
|
View {
|
||||||
internal_callback_ptr: internal_callback_ptr,
|
internal_callback_ptr: internal_callback_ptr,
|
||||||
objc_controller: ViewHandle(Some(inner)),
|
objc_controller: handle,
|
||||||
controller: controller
|
controller: controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,12 +161,24 @@ impl<T> View<T> where T: ViewController + 'static {
|
||||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||||
self.objc_controller.register_for_dragged_types(types);
|
self.objc_controller.register_for_dragged_types(types);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> &LayoutAnchorDimension {
|
||||||
|
&self.objc_controller.width
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Node for View<T> {
|
pub fn height(&self) -> &LayoutAnchorDimension {
|
||||||
|
&self.objc_controller.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Layout for View<T> {
|
||||||
/// Returns the Objective-C object used for handling the view heirarchy.
|
/// Returns the Objective-C object used for handling the view heirarchy.
|
||||||
fn get_backing_node(&self) -> Option<ShareId<Object>> {
|
fn get_backing_node(&self) -> Option<ShareId<Object>> {
|
||||||
self.objc_controller.0.clone()
|
self.objc_controller.objc.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_subview<V: Layout>(&self, subview: &V) {
|
||||||
|
self.objc_controller.add_subview(subview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ impl Default for WindowConfig {
|
||||||
let dimensions = NSRect::new(NSPoint::new(0., 0.), NSSize::new(800., 600.));
|
let dimensions = NSRect::new(NSPoint::new(0., 0.), NSSize::new(800., 600.));
|
||||||
|
|
||||||
let style = WindowStyle::Resizable | WindowStyle::Miniaturizable | WindowStyle::UnifiedTitleAndToolbar |
|
let style = WindowStyle::Resizable | WindowStyle::Miniaturizable | WindowStyle::UnifiedTitleAndToolbar |
|
||||||
WindowStyle::Closable | WindowStyle::Titled | WindowStyle::FullSizeContentView;
|
WindowStyle::Closable | WindowStyle::Titled;
|
||||||
|
|
||||||
let alloc: id = msg_send![class!(NSWindow), alloc];
|
let alloc: id = msg_send![class!(NSWindow), alloc];
|
||||||
let window: id = msg_send![alloc, initWithContentRect:dimensions styleMask:style backing:2 as NSUInteger defer:YES];
|
let window: id = msg_send![alloc, initWithContentRect:dimensions styleMask:style backing:2 as NSUInteger defer:YES];
|
||||||
|
|
|
@ -11,7 +11,7 @@ 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::view::traits::Node;
|
use crate::layout::traits::Layout;
|
||||||
|
|
||||||
use crate::toolbar::{Toolbar, ToolbarController};
|
use crate::toolbar::{Toolbar, ToolbarController};
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ impl WindowHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used for setting the content view controller for this window.
|
/// Used for setting the content view controller for this window.
|
||||||
pub fn set_content_view_controller<T: Node + 'static>(&self, view_controller: &T) {
|
pub fn set_content_view_controller<T: Layout + 'static>(&self, view_controller: &T) {
|
||||||
if let Some(controller) = &self.0 {
|
if let Some(controller) = &self.0 {
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(vc) = view_controller.get_backing_node() {
|
if let Some(vc) = view_controller.get_backing_node() {
|
||||||
|
|
|
@ -10,8 +10,8 @@ use objc::{msg_send, sel, sel_impl};
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::constants::WINDOW_CONTROLLER_PTR;
|
use crate::constants::WINDOW_CONTROLLER_PTR;
|
||||||
|
use crate::layout::traits::Layout;
|
||||||
use crate::toolbar::{Toolbar, ToolbarController};
|
use crate::toolbar::{Toolbar, ToolbarController};
|
||||||
use crate::view::traits::Node;
|
|
||||||
use crate::window::handle::WindowHandle;
|
use crate::window::handle::WindowHandle;
|
||||||
use crate::window::traits::WindowController;
|
use crate::window::traits::WindowController;
|
||||||
use crate::window::controller::register_window_controller_class;
|
use crate::window::controller::register_window_controller_class;
|
||||||
|
@ -98,7 +98,7 @@ impl<T> Window<T> where T: WindowController + 'static {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the content view controller for the window.
|
/// Sets the content view controller for the window.
|
||||||
pub fn set_content_view_controller<VC: Node + 'static>(&self, view_controller: &VC) {
|
pub fn set_content_view_controller<VC: Layout + 'static>(&self, view_controller: &VC) {
|
||||||
self.objc_controller.set_content_view_controller(view_controller);
|
self.objc_controller.set_content_view_controller(view_controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue