Rework the Layout trait implementation.
- Adds a new `ObjcProperty` type, which wraps our Objective-C objects in a Rust-backed Rc/Refcell combo. This in general makes understanding reference counting/storage more digestable when reading through the codebase and leans more on Rust to do its thing than the Objective-C runtime. - Most widgets that need to implement `Layout` now only need to provide a slot for running a handler with the underlying node. - Further documentation work ongoing.
This commit is contained in:
parent
5748e76a97
commit
2f9a5b5e67
|
@ -1,9 +1,12 @@
|
|||
//! Implements some stuff to handle dynamically setting the `NSBundle` identifier.
|
||||
//! This is not currently in use, but does have places where it's useful... and to be honest I'm
|
||||
//! kinda happy this is done as a swizzling implementation in pure Rust, which I couldn't find
|
||||
//! examples of anywhere else.
|
||||
//! Implements some functionality to handle dynamically setting the `NSBundle` identifier.
|
||||
//!
|
||||
//! Disregard until you can't, I guess.
|
||||
//!
|
||||
//
|
||||
// This is not currently in use, but does have places where it's useful... and to be honest I'm
|
||||
// kinda happy this is done as a swizzling implementation in pure Rust, which I couldn't find
|
||||
// examples of anywhere else.
|
||||
//
|
||||
// Disregard until you can't, I guess.
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::mem;
|
||||
|
|
94
src/button/enums.rs
Normal file
94
src/button/enums.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use crate::foundation::NSUInteger;
|
||||
|
||||
/// Represents a bezel style for a button. This is a macOS-specific control, and has no effect
|
||||
/// under iOS or tvOS.
|
||||
#[cfg(feature = "macos")]
|
||||
#[derive(Debug)]
|
||||
pub enum BezelStyle {
|
||||
/// A standard circular button.
|
||||
Circular,
|
||||
|
||||
/// A standard disclosure style button.
|
||||
Disclosure,
|
||||
|
||||
/// The standard looking "Help" (?) button.
|
||||
HelpButton,
|
||||
|
||||
/// An inline style, varies across OS's.
|
||||
Inline,
|
||||
|
||||
/// A recessed style, varies slightly across OS's.
|
||||
Recessed,
|
||||
|
||||
/// A regular square style, with no special styling.
|
||||
RegularSquare,
|
||||
|
||||
/// A standard rounded rectangle.
|
||||
RoundRect,
|
||||
|
||||
/// A standard rounded button.
|
||||
Rounded,
|
||||
|
||||
/// A standard rounded disclosure button.
|
||||
RoundedDisclosure,
|
||||
|
||||
/// A shadowless square styl.e
|
||||
ShadowlessSquare,
|
||||
|
||||
/// A small square style.
|
||||
SmallSquare,
|
||||
|
||||
/// A textured rounded style.
|
||||
TexturedRounded,
|
||||
|
||||
/// A textured square style.
|
||||
TexturedSquare,
|
||||
|
||||
/// Any style that's not known by this framework (e.g, if Apple
|
||||
/// introduces something new).
|
||||
Unknown(NSUInteger)
|
||||
}
|
||||
|
||||
#[cfg(feature = "macos")]
|
||||
impl From<BezelStyle> for NSUInteger {
|
||||
fn from(style: BezelStyle) -> Self {
|
||||
match style {
|
||||
BezelStyle::Circular => 7,
|
||||
BezelStyle::Disclosure => 5,
|
||||
BezelStyle::HelpButton => 9,
|
||||
BezelStyle::Inline => 15,
|
||||
BezelStyle::Recessed => 13,
|
||||
BezelStyle::RegularSquare => 2,
|
||||
BezelStyle::RoundRect => 12,
|
||||
BezelStyle::Rounded => 1,
|
||||
BezelStyle::RoundedDisclosure => 14,
|
||||
BezelStyle::ShadowlessSquare => 6,
|
||||
BezelStyle::SmallSquare => 10,
|
||||
BezelStyle::TexturedRounded => 11,
|
||||
BezelStyle::TexturedSquare => 8,
|
||||
BezelStyle::Unknown(i) => i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "macos")]
|
||||
impl From<NSUInteger> for BezelStyle {
|
||||
fn from(i: NSUInteger) -> Self {
|
||||
match i {
|
||||
7 => Self::Circular,
|
||||
5 => Self::Disclosure,
|
||||
9 => Self::HelpButton,
|
||||
15 => Self::Inline,
|
||||
13 => Self::Recessed,
|
||||
2 => Self::RegularSquare,
|
||||
12 => Self::RoundRect,
|
||||
1 => Self::Rounded,
|
||||
14 => Self::RoundedDisclosure,
|
||||
6 => Self::ShadowlessSquare,
|
||||
10 => Self::SmallSquare,
|
||||
11 => Self::TexturedRounded,
|
||||
8 => Self::TexturedSquare,
|
||||
i => Self::Unknown(i)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,29 @@
|
|||
//! A wrapper for NSButton. Currently the epitome of jank - if you're poking around here, expect
|
||||
//! that this will change at some point.
|
||||
//! Wraps `NSButton` on macOS, and `UIButton` on iOS and tvOS.
|
||||
//!
|
||||
//! You'd use this type to create a button that a user can interact with. Buttons can be configured
|
||||
//! a number of ways, and support setting a callback to fire when they're clicked or tapped.
|
||||
//!
|
||||
//! Some properties are platform-specific; see the documentation for further information.
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! let mut button = Button::new("My button title");
|
||||
//! button.set_text_equivalent("c");
|
||||
//!
|
||||
//! button.set_action(|| {
|
||||
//! println!("My button was clicked.");
|
||||
//! });
|
||||
//!
|
||||
//! // Make sure you don't let your Button drop for as long as you need it.
|
||||
//! my_view.add_subview(&button);
|
||||
//! ```
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Once;
|
||||
|
||||
use objc_id::ShareId;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use objc_id::Id;
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
@ -15,17 +34,36 @@ use crate::foundation::{id, nil, BOOL, YES, NO, NSString, NSUInteger};
|
|||
use crate::invoker::TargetActionHandler;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::text::{AttributedString, Font};
|
||||
use crate::utils::load;
|
||||
use crate::utils::{load, properties::ObjcProperty};
|
||||
|
||||
#[cfg(feature = "macos")]
|
||||
use crate::macos::FocusRingType;
|
||||
|
||||
/// A wrapper for `NSButton`. Holds (retains) pointers for the Objective-C runtime
|
||||
/// where our `NSButton` lives.
|
||||
mod enums;
|
||||
pub use enums::*;
|
||||
|
||||
/// Wraps `NSButton` on macOS, and `UIButton` on iOS and tvOS.
|
||||
///
|
||||
/// You'd use this type to create a button that a user can interact with. Buttons can be configured
|
||||
/// a number of ways, and support setting a callback to fire when they're clicked or tapped.
|
||||
///
|
||||
/// Some properties are platform-specific; see the documentation for further information.
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// let mut button = Button::new("My button title");
|
||||
/// button.set_text_equivalent("c");
|
||||
///
|
||||
/// button.set_action(|| {
|
||||
/// println!("My button was clicked.");
|
||||
/// });
|
||||
///
|
||||
/// // Make sure you don't let your Button drop for as long as you need it.
|
||||
/// my_view.add_subview(&button);
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Button {
|
||||
/// A handle for the underlying Objective-C object.
|
||||
pub objc: ShareId<Object>,
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// A reference to an image, if set. We keep a copy to avoid any ownership snafus.
|
||||
pub image: Option<Image>,
|
||||
|
@ -93,34 +131,34 @@ impl Button {
|
|||
height: LayoutAnchorDimension::height(view),
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an image on the underlying button.
|
||||
pub fn set_image(&mut self, image: Image) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setImage:&*image.0];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setImage:&*image.0];
|
||||
});
|
||||
|
||||
self.image = Some(image);
|
||||
}
|
||||
|
||||
/// Sets the bezel style for this button.
|
||||
/// Sets the bezel style for this button. Only supported on macOS.
|
||||
#[cfg(feature = "macos")]
|
||||
pub fn set_bezel_style(&self, bezel_style: BezelStyle) {
|
||||
let style: NSUInteger = bezel_style.into();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setBezelStyle:style];
|
||||
}
|
||||
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setBezelStyle:style];
|
||||
});
|
||||
}
|
||||
|
||||
/// Attaches a callback for button press events. Don't get too creative now...
|
||||
/// best just to message pass or something.
|
||||
pub fn set_action<F: Fn() + Send + Sync + 'static>(&mut self, action: F) {
|
||||
let handler = TargetActionHandler::new(&*self.objc, action);
|
||||
self.handler = Some(handler);
|
||||
//let handler = TargetActionHandler::new(&*self.objc, action);
|
||||
//self.handler = Some(handler);
|
||||
}
|
||||
|
||||
/// Call this to set the background color for the backing layer.
|
||||
|
@ -128,10 +166,10 @@ impl Button {
|
|||
let color: id = color.as_ref().into();
|
||||
|
||||
#[cfg(feature = "macos")]
|
||||
unsafe {
|
||||
let cell: id = msg_send![&*self.objc, cell];
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let cell: id = msg_send![obj, cell];
|
||||
let _: () = msg_send![cell, setBackgroundColor:color];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Set a key to be bound to this button. When the key is pressed, the action coupled to this
|
||||
|
@ -139,9 +177,9 @@ impl Button {
|
|||
pub fn set_key_equivalent(&self, key: &str) {
|
||||
let key = NSString::new(key);
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setKeyEquivalent:&*key];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setKeyEquivalent:&*key];
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the text color for this button.
|
||||
|
@ -149,94 +187,82 @@ impl Button {
|
|||
/// On macOS, this is done by way of an `AttributedString` under the hood.
|
||||
pub fn set_text_color<C: AsRef<Color>>(&self, color: C) {
|
||||
#[cfg(feature = "macos")]
|
||||
unsafe {
|
||||
let text: id = msg_send![&*self.objc, attributedTitle];
|
||||
self.objc.with_mut(move |obj| unsafe {
|
||||
let text: id = msg_send![obj, attributedTitle];
|
||||
let len: isize = msg_send![text, length];
|
||||
|
||||
let mut attr_str = AttributedString::wrap(text);
|
||||
attr_str.set_text_color(color, 0..len);
|
||||
attr_str.set_text_color(color.as_ref(), 0..len);
|
||||
|
||||
let _: () = msg_send![&*self.objc, setAttributedTitle:&*attr_str];
|
||||
}
|
||||
let _: () = msg_send![obj, setAttributedTitle:&*attr_str];
|
||||
});
|
||||
}
|
||||
|
||||
// @TODO: Figure out how to handle oddities like this.
|
||||
/// For buttons on macOS, one might need to disable the border. This does that.
|
||||
#[cfg(feature = "macos")]
|
||||
pub fn set_bordered(&self, is_bordered: bool) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setBordered:match is_bordered {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setBordered:match is_bordered {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the font for this button.
|
||||
pub fn set_font<F: AsRef<Font>>(&self, font: F) {
|
||||
let font = font.as_ref().clone();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setFont:&*font];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setFont:&*font];
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets how the control should draw a focus ring when a user is focused on it.
|
||||
///
|
||||
/// This is a macOS-only method.
|
||||
#[cfg(feature = "macos")]
|
||||
pub fn set_focus_ring_type(&self, focus_ring_type: FocusRingType) {
|
||||
let ring_type: NSUInteger = focus_ring_type.into();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setFocusRingType:ring_type];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setFocusRingType:ring_type];
|
||||
});
|
||||
}
|
||||
|
||||
/// Toggles the highlighted status of the button.
|
||||
pub fn set_highlighted(&self, highlight: bool) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, highlight:match highlight {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, highlight:match highlight {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for Button {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
self.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, _view: &V) {
|
||||
panic!(r#"
|
||||
Tried to add a subview to a Button. This is not allowed in Cacao.
|
||||
If you think this should be supported, open a discussion on the GitHub repo.
|
||||
"#);
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for &Button {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
self.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, _view: &V) {
|
||||
panic!(r#"
|
||||
Tried to add a subview to a Button. This is not allowed in Cacao.
|
||||
If you think this should be supported, open a discussion on the GitHub repo.
|
||||
"#);
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Drop for Button {
|
||||
/// Nils out references on the Objective-C side and removes this from the backing view.
|
||||
// Just to be sure, let's... nil these out. They should be weak references,
|
||||
// but I'd rather be paranoid and remove them later.
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setTarget:nil];
|
||||
let _: () = msg_send![&*self.objc, setAction:nil];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setTarget:nil];
|
||||
let _: () = msg_send![obj, setAction:nil];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,96 +280,3 @@ fn register_class() -> *const Class {
|
|||
|
||||
unsafe { VIEW_CLASS }
|
||||
}
|
||||
|
||||
/// Represents a bezel style for a button. This is a macOS-specific control, and has no effect
|
||||
/// under iOS or tvOS.
|
||||
#[cfg(feature = "macos")]
|
||||
#[derive(Debug)]
|
||||
pub enum BezelStyle {
|
||||
/// A standard circular button.
|
||||
Circular,
|
||||
|
||||
/// A standard disclosure style button.
|
||||
Disclosure,
|
||||
|
||||
/// The standard looking "Help" (?) button.
|
||||
HelpButton,
|
||||
|
||||
/// An inline style, varies across OS's.
|
||||
Inline,
|
||||
|
||||
/// A recessed style, varies slightly across OS's.
|
||||
Recessed,
|
||||
|
||||
/// A regular square style, with no special styling.
|
||||
RegularSquare,
|
||||
|
||||
/// A standard rounded rectangle.
|
||||
RoundRect,
|
||||
|
||||
/// A standard rounded button.
|
||||
Rounded,
|
||||
|
||||
/// A standard rounded disclosure button.
|
||||
RoundedDisclosure,
|
||||
|
||||
/// A shadowless square styl.e
|
||||
ShadowlessSquare,
|
||||
|
||||
/// A small square style.
|
||||
SmallSquare,
|
||||
|
||||
/// A textured rounded style.
|
||||
TexturedRounded,
|
||||
|
||||
/// A textured square style.
|
||||
TexturedSquare,
|
||||
|
||||
/// Any style that's not known by this framework (e.g, if Apple
|
||||
/// introduces something new).
|
||||
Unknown(NSUInteger)
|
||||
}
|
||||
|
||||
#[cfg(feature = "macos")]
|
||||
impl From<BezelStyle> for NSUInteger {
|
||||
fn from(style: BezelStyle) -> Self {
|
||||
match style {
|
||||
BezelStyle::Circular => 7,
|
||||
BezelStyle::Disclosure => 5,
|
||||
BezelStyle::HelpButton => 9,
|
||||
BezelStyle::Inline => 15,
|
||||
BezelStyle::Recessed => 13,
|
||||
BezelStyle::RegularSquare => 2,
|
||||
BezelStyle::RoundRect => 12,
|
||||
BezelStyle::Rounded => 1,
|
||||
BezelStyle::RoundedDisclosure => 14,
|
||||
BezelStyle::ShadowlessSquare => 6,
|
||||
BezelStyle::SmallSquare => 10,
|
||||
BezelStyle::TexturedRounded => 11,
|
||||
BezelStyle::TexturedSquare => 8,
|
||||
BezelStyle::Unknown(i) => i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "macos")]
|
||||
impl From<NSUInteger> for BezelStyle {
|
||||
fn from(i: NSUInteger) -> Self {
|
||||
match i {
|
||||
7 => Self::Circular,
|
||||
5 => Self::Disclosure,
|
||||
9 => Self::HelpButton,
|
||||
15 => Self::Inline,
|
||||
13 => Self::Recessed,
|
||||
2 => Self::RegularSquare,
|
||||
12 => Self::RoundRect,
|
||||
1 => Self::Rounded,
|
||||
14 => Self::RoundedDisclosure,
|
||||
6 => Self::ShadowlessSquare,
|
||||
10 => Self::SmallSquare,
|
||||
11 => Self::TexturedRounded,
|
||||
8 => Self::TexturedSquare,
|
||||
i => Self::Unknown(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ pub(crate) const AQUA_LIGHT_COLOR_HIGH_CONTRAST: &'static str = "AQUA_LIGHT_COLO
|
|||
pub(crate) const AQUA_DARK_COLOR_NORMAL_CONTRAST: &'static str = "AQUA_DARK_COLOR_NORMAL_CONTRAST";
|
||||
pub(crate) const AQUA_DARK_COLOR_HIGH_CONTRAST: &'static str = "AQUA_DARK_COLOR_HIGH_CONTRAST";
|
||||
|
||||
|
||||
extern "C" {
|
||||
static NSAppearanceNameAqua: id;
|
||||
static NSAppearanceNameAccessibilityHighContrastAqua: id;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
use core_graphics::geometry::{CGRect, CGPoint, CGSize};
|
||||
|
||||
/// A struct that represents a box - top, left, width and height.
|
||||
/// A struct that represents a box - top, left, width and height. You might use this for, say,
|
||||
/// setting the initial frame of a view.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Rect {
|
||||
/// Distance from the top, in points.
|
||||
|
|
|
@ -5,6 +5,7 @@ use objc::{msg_send, sel, sel_impl};
|
|||
use crate::foundation::{id, nil, YES, NO, NSArray, NSString};
|
||||
use crate::color::Color;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::utils::properties::ObjcProperty;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
@ -43,7 +44,7 @@ fn allocate_view(registration_fn: fn() -> *const Class) -> id {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct ImageView {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: ShareId<Object>,
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// A pointer to the Objective-C runtime top layout constraint.
|
||||
pub top: LayoutAnchorY,
|
||||
|
@ -98,47 +99,38 @@ impl ImageView {
|
|||
height: LayoutAnchorDimension::height(view),
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
}
|
||||
}
|
||||
|
||||
/// Call this to set the background color for the backing layer.
|
||||
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||
let cg = color.as_ref().cg_color();
|
||||
|
||||
unsafe {
|
||||
let layer: id = msg_send![&*self.objc, layer];
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let cg = color.as_ref().cg_color();
|
||||
let layer: id = msg_send![obj, layer];
|
||||
let _: () = msg_send![layer, setBackgroundColor:cg];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_image(&self, image: &Image) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setImage:&*image.0];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setImage:&*image.0];
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_hidden(&self, hidden: bool) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setHidden:match hidden {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setHidden:match hidden {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for ImageView {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
self.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, view: &V) {
|
||||
let backing_node = view.get_backing_node();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, addSubview:backing_node];
|
||||
}
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ use crate::color::Color;
|
|||
use crate::foundation::{id, nil, NSArray, NSInteger, NSString, NO, YES};
|
||||
use crate::layout::{Layout, LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
||||
use crate::text::{Font, TextAlign};
|
||||
use crate::utils::properties::ObjcProperty;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
@ -85,7 +86,7 @@ fn common_init(class: *const Class) -> id {
|
|||
#[derive(Debug)]
|
||||
pub struct TextField<T = ()> {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: ShareId<Object>,
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// A pointer to the delegate for this view.
|
||||
pub delegate: Option<Box<T>>,
|
||||
|
@ -145,7 +146,7 @@ impl TextField {
|
|||
height: LayoutAnchorDimension::height(view),
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +179,7 @@ where
|
|||
height: LayoutAnchorDimension::height(label),
|
||||
center_x: LayoutAnchorX::center(label),
|
||||
center_y: LayoutAnchorY::center(label),
|
||||
objc: unsafe { ShareId::from_ptr(label) },
|
||||
objc: ObjcProperty::retain(label),
|
||||
};
|
||||
|
||||
(&mut delegate).did_load(label.clone_as_handle());
|
||||
|
@ -211,61 +212,50 @@ impl<T> TextField<T> {
|
|||
|
||||
/// Grabs the value from the textfield and returns it as an owned String.
|
||||
pub fn get_value(&self) -> String {
|
||||
let value = NSString::retain(unsafe {
|
||||
msg_send![&*self.objc, stringValue]
|
||||
});
|
||||
|
||||
value.to_string()
|
||||
self.objc.get(|obj| unsafe {
|
||||
NSString::retain(msg_send![obj, stringValue]).to_string()
|
||||
})
|
||||
}
|
||||
|
||||
/// Call this to set the background color for the backing layer.
|
||||
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||
let cg = color.as_ref().cg_color();
|
||||
|
||||
unsafe {
|
||||
let layer: id = msg_send![&*self.objc, layer];
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let cg = color.as_ref().cg_color();
|
||||
let layer: id = msg_send![obj, layer];
|
||||
let _: () = msg_send![layer, setBackgroundColor: cg];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Call this to set the text for the label.
|
||||
pub fn set_text(&self, text: &str) {
|
||||
let s = NSString::new(text);
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setStringValue:&*s];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setStringValue:&*s];
|
||||
});
|
||||
}
|
||||
|
||||
/// The the text alignment style for this control.
|
||||
pub fn set_text_alignment(&self, alignment: TextAlign) {
|
||||
unsafe {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let alignment: NSInteger = alignment.into();
|
||||
let _: () = msg_send![&*self.objc, setAlignment: alignment];
|
||||
}
|
||||
let _: () = msg_send![obj, setAlignment: alignment];
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the font for this input.
|
||||
pub fn set_font<F: AsRef<Font>>(&self, font: F) {
|
||||
let font = font.as_ref().clone();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setFont:&*font];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setFont:&*font];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout for TextField<T> {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
self.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, view: &V) {
|
||||
let backing_node = view.get_backing_node();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, addSubview: backing_node];
|
||||
}
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,13 +267,13 @@ impl<T> Drop for TextField<T> {
|
|||
///
|
||||
/// There are, thankfully, no delegates we need to break here.
|
||||
fn drop(&mut self) {
|
||||
if self.delegate.is_some() {
|
||||
/*if self.delegate.is_some() {
|
||||
unsafe {
|
||||
let superview: id = msg_send![&*self.objc, superview];
|
||||
if superview != nil {
|
||||
let _: () = msg_send![&*self.objc, removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,25 +16,22 @@ 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) -> ShareId<Object>;
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F);
|
||||
|
||||
/// Adds another Layout-backed control or view as a subview of this view.
|
||||
fn add_subview<V: Layout>(&self, view: &V) {
|
||||
let backing_node = self.get_backing_node();
|
||||
let subview_node = view.get_backing_node();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*backing_node, addSubview:&*subview_node];
|
||||
}
|
||||
self.with_backing_node(|backing_node| {
|
||||
view.with_backing_node(|subview_node| unsafe {
|
||||
let _: () = msg_send![backing_node, addSubview:subview_node];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Removes a control or view from the superview.
|
||||
fn remove_from_superview(&self) {
|
||||
let backing_node = self.get_backing_node();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*backing_node, removeFromSuperview];
|
||||
}
|
||||
self.with_backing_node(|backing_node| unsafe {
|
||||
let _: () = msg_send![backing_node, removeFromSuperview];
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the `frame` for the view this trait is applied to.
|
||||
|
@ -43,12 +40,11 @@ pub trait Layout {
|
|||
/// `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];
|
||||
}
|
||||
|
||||
self.with_backing_node(move |backing_node| unsafe {
|
||||
let _: () = msg_send![backing_node, setFrame:frame];
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets whether the view for this trait should translate autoresizing masks into layout
|
||||
|
@ -57,13 +53,11 @@ pub trait Layout {
|
|||
/// 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 {
|
||||
self.with_backing_node(|backing_node| unsafe {
|
||||
let _: () = msg_send![backing_node, setTranslatesAutoresizingMaskIntoConstraints:match translates {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,10 +52,9 @@ extern fn view_for_column<T: ListViewDelegate>(
|
|||
//
|
||||
// @TODO: Finish investing the `Rc` approach, might be able to just take
|
||||
// ownership and rely on Rust being correct.
|
||||
let objc = item.objc.borrow();
|
||||
unsafe {
|
||||
msg_send![&**objc, self]
|
||||
}
|
||||
item.objc.get(|obj| unsafe {
|
||||
msg_send![obj, self]
|
||||
})
|
||||
}
|
||||
|
||||
extern fn will_display_cell<T: ListViewDelegate>(
|
||||
|
|
|
@ -54,6 +54,7 @@ use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension}
|
|||
use crate::pasteboard::PasteboardType;
|
||||
use crate::scrollview::ScrollView;
|
||||
use crate::utils::{os, CellFactory, CGSize};
|
||||
use crate::utils::properties::{ObjcProperty, PropertyNullable};
|
||||
use crate::view::ViewDelegate;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -126,55 +127,6 @@ fn common_init(class: *const Class) -> id {
|
|||
}
|
||||
}
|
||||
|
||||
use objc_id::Id;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ObjcProperty(Rc<RefCell<Id<Object>>>);
|
||||
|
||||
impl ObjcProperty {
|
||||
pub fn new(obj: id) -> Self {
|
||||
Self(Rc::new(RefCell::new(unsafe {
|
||||
Id::from_ptr(obj)
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn with<F>(&self, handler: F)
|
||||
where
|
||||
F: Fn(&Object)
|
||||
{
|
||||
let borrow = self.0.borrow();
|
||||
handler(&borrow);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PropertyNullable<T>(Rc<RefCell<Option<T>>>);
|
||||
|
||||
impl<T> PropertyNullable<T> {
|
||||
pub fn new(obj: T) -> Self {
|
||||
Self(Rc::new(RefCell::new(Some(obj))))
|
||||
}
|
||||
|
||||
pub fn clone(&self) -> Self {
|
||||
Self(Rc::clone(&self.0))
|
||||
}
|
||||
|
||||
pub fn with<F>(&self, handler: F)
|
||||
where
|
||||
F: Fn(&T)
|
||||
{
|
||||
let borrow = self.0.borrow();
|
||||
if let Some(s) = &*borrow {
|
||||
handler(s);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&self, obj: T) {
|
||||
let mut borrow = self.0.borrow_mut();
|
||||
*borrow = Some(obj);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ListView<T = ()> {
|
||||
/// Internal map of cell identifers/vendors. These are used for handling dynamic cell
|
||||
|
@ -184,7 +136,7 @@ pub struct ListView<T = ()> {
|
|||
menu: PropertyNullable<Vec<MenuItem>>,
|
||||
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: ShareId<Object>,
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// On macOS, we need to manage the NSScrollView ourselves. It's a bit
|
||||
/// more old school like that...
|
||||
|
@ -241,9 +193,9 @@ impl ListView {
|
|||
let scrollview = {
|
||||
let sview = ScrollView::new();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*sview.objc, setDocumentView:view];
|
||||
}
|
||||
sview.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setDocumentView:view];
|
||||
});
|
||||
|
||||
sview
|
||||
};
|
||||
|
@ -251,7 +203,9 @@ impl ListView {
|
|||
// For macOS, we need to use the NSScrollView anchor points, not the NSTableView.
|
||||
// @TODO: Fix this with proper mutable access.
|
||||
#[cfg(target_os = "macos")]
|
||||
let anchor_view: id = unsafe { msg_send![&*scrollview.objc, self] };
|
||||
let anchor_view: id = scrollview.objc.get(|obj| unsafe {
|
||||
msg_send![obj, self]
|
||||
});
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
let anchor_view: id = view;
|
||||
|
@ -270,7 +224,7 @@ impl ListView {
|
|||
height: LayoutAnchorDimension::height(anchor_view),
|
||||
center_x: LayoutAnchorX::center(anchor_view),
|
||||
center_y: LayoutAnchorY::center(anchor_view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
scrollview: scrollview
|
||||
|
@ -300,16 +254,18 @@ impl<T> ListView<T> where T: ListViewDelegate + 'static {
|
|||
let scrollview = {
|
||||
let sview = ScrollView::new();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*sview.objc, setDocumentView:view];
|
||||
}
|
||||
sview.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setDocumentView:view];
|
||||
});
|
||||
|
||||
sview
|
||||
};
|
||||
|
||||
// For macOS, we need to use the NSScrollView anchor points, not the NSTableView.
|
||||
#[cfg(target_os = "macos")]
|
||||
let anchor_view: id = unsafe { msg_send![&*scrollview.objc, self] };
|
||||
let anchor_view: id = scrollview.objc.get(|obj| unsafe {
|
||||
msg_send![obj, self]
|
||||
});
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
let anchor_view = view;
|
||||
|
@ -328,7 +284,7 @@ impl<T> ListView<T> where T: ListViewDelegate + 'static {
|
|||
height: LayoutAnchorDimension::height(anchor_view),
|
||||
center_x: LayoutAnchorX::center(anchor_view),
|
||||
center_y: LayoutAnchorY::center(anchor_view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
scrollview: scrollview
|
||||
|
@ -382,7 +338,9 @@ impl<T> ListView<T> {
|
|||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let key = NSString::new(identifier);
|
||||
let cell: id = unsafe { msg_send![&*self.objc, makeViewWithIdentifier:&*key owner:nil] };
|
||||
let cell: id = self.objc.get(|obj| unsafe {
|
||||
msg_send![obj, makeViewWithIdentifier:&*key owner:nil]
|
||||
});
|
||||
|
||||
if cell != nil {
|
||||
ListViewRow::from_cached(cell)
|
||||
|
@ -398,12 +356,11 @@ impl<T> ListView<T> {
|
|||
/// Call this to set the background color for the backing layer.
|
||||
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||
// @TODO: This is wrong.
|
||||
let color = color.as_ref().cg_color();
|
||||
|
||||
unsafe {
|
||||
let layer: id = msg_send![&*self.objc, layer];
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let color = color.as_ref().cg_color();
|
||||
let layer: id = msg_send![obj, layer];
|
||||
let _: () = msg_send![layer, setBackgroundColor:color];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the style for the underlying NSTableView. This property is only supported on macOS
|
||||
|
@ -411,9 +368,9 @@ impl<T> ListView<T> {
|
|||
#[cfg(feature = "macos")]
|
||||
pub fn set_style(&self, style: crate::foundation::NSInteger) {
|
||||
if os::is_minimum_version(11) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setStyle:style];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setStyle:style];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,19 +381,19 @@ impl<T> ListView<T> {
|
|||
/// view for navigation purposes.
|
||||
#[cfg(feature = "macos")]
|
||||
pub fn set_allows_empty_selection(&self, allows: bool) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setAllowsEmptySelection:match allows {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setAllowsEmptySelection:match allows {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Set the selection highlight style.
|
||||
pub fn set_selection_highlight_style(&self, style: crate::foundation::NSInteger) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setSelectionHighlightStyle:style];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setSelectionHighlightStyle:style];
|
||||
});
|
||||
}
|
||||
|
||||
/// Select the rows at the specified indexes, optionally adding to any existing selections.
|
||||
|
@ -448,10 +405,12 @@ impl<T> ListView<T> {
|
|||
let _: () = msg_send![index_set, addIndex:index];
|
||||
}
|
||||
|
||||
let _: () = msg_send![&*self.objc, selectRowIndexes:index_set byExtendingSelection:match extends_existing {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
self.objc.with_mut(|obj| {
|
||||
let _: () = msg_send![obj, selectRowIndexes:index_set byExtendingSelection:match extends_existing {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,14 +425,19 @@ impl<T> ListView<T> {
|
|||
/// });
|
||||
/// ```
|
||||
pub fn perform_batch_updates<F: Fn(ListView)>(&self, update: F) {
|
||||
// Note that we need to thread the `with_mut` calls carefully, to avoid deadlocking.
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, beginUpdates];
|
||||
{
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, beginUpdates];
|
||||
});
|
||||
|
||||
let handle = self.clone_as_handle();
|
||||
update(handle);
|
||||
|
||||
let _: () = msg_send![&*self.objc, endUpdates];
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, endUpdates];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,7 +461,9 @@ impl<T> ListView<T> {
|
|||
// We need to temporarily retain this; it can drop after the underlying NSTableView
|
||||
// has also retained it.
|
||||
let x = ShareId::from_ptr(index_set);
|
||||
let _: () = msg_send![&*self.objc, insertRowsAtIndexes:&*x withAnimation:animation_options];
|
||||
self.objc.with_mut(|obj| {
|
||||
let _: () = msg_send![obj, insertRowsAtIndexes:&*x withAnimation:animation_options];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,7 +482,10 @@ impl<T> ListView<T> {
|
|||
|
||||
let ye: id = msg_send![class!(NSIndexSet), indexSetWithIndex:0];
|
||||
let y = ShareId::from_ptr(ye);
|
||||
let _: () = msg_send![&*self.objc, reloadDataForRowIndexes:&*x columnIndexes:&*y];
|
||||
|
||||
self.objc.with_mut(|obj| {
|
||||
let _: () = msg_send![obj, reloadDataForRowIndexes:&*x columnIndexes:&*y];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,16 +509,18 @@ impl<T> ListView<T> {
|
|||
// We need to temporarily retain this; it can drop after the underlying NSTableView
|
||||
// has also retained it.
|
||||
let x = ShareId::from_ptr(index_set);
|
||||
let _: () = msg_send![&*self.objc, removeRowsAtIndexes:&*x withAnimation:animation_options];
|
||||
self.objc.with_mut(|obj| {
|
||||
let _: () = msg_send![obj, removeRowsAtIndexes:&*x withAnimation:animation_options];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an enforced row-height; if you need dynamic rows, you'll want to
|
||||
/// look at ListViewDelegate methods, or use AutoLayout.
|
||||
pub fn set_row_height(&self, height: CGFloat) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setRowHeight:height];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setRowHeight:height];
|
||||
});
|
||||
}
|
||||
|
||||
/// This defaults to true. If you're using manual heights, you may want to set this to `false`,
|
||||
|
@ -559,12 +530,12 @@ impl<T> ListView<T> {
|
|||
/// It can make some scrolling situations much smoother.
|
||||
pub fn set_uses_automatic_row_heights(&self, uses: bool) {
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setUsesAutomaticRowHeights:match uses {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setUsesAutomaticRowHeights:match uses {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// On macOS, this will instruct the underlying NSTableView to alternate
|
||||
|
@ -572,37 +543,37 @@ impl<T> ListView<T> {
|
|||
/// to hard-set a row height as well.
|
||||
pub fn set_uses_alternating_backgrounds(&self, uses: bool) {
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setUsesAlternatingRowBackgroundColors:match uses {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setUsesAlternatingRowBackgroundColors:match uses {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// End actions for a row. API subject to change.
|
||||
pub fn set_row_actions_visible(&self, visible: bool) {
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setRowActionsVisible:match visible {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setRowActionsVisible:match visible {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Register this view for drag and drop operations.
|
||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||
unsafe {
|
||||
let types: NSArray = types.into_iter().map(|t| {
|
||||
// This clone probably doesn't need to be here, but it should also be cheap as
|
||||
// this is just an enum... and this is not an oft called method.
|
||||
let x: NSString = (*t).into();
|
||||
x.into()
|
||||
}).collect::<Vec<id>>().into();
|
||||
let types: NSArray = types.into_iter().map(|t| {
|
||||
// This clone probably doesn't need to be here, but it should also be cheap as
|
||||
// this is just an enum... and this is not an oft called method.
|
||||
let x: NSString = (*t).into();
|
||||
x.into()
|
||||
}).collect::<Vec<id>>().into();
|
||||
|
||||
let _: () = msg_send![&*self.objc, registerForDraggedTypes:&*types];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, registerForDraggedTypes:&*types];
|
||||
});
|
||||
}
|
||||
|
||||
/// Reloads the underlying ListView. This is more expensive than handling insert/reload/remove
|
||||
|
@ -611,14 +582,14 @@ impl<T> ListView<T> {
|
|||
/// Calling this will reload (and redraw) your listview based on whatever the data source
|
||||
/// reports back.
|
||||
pub fn reload(&self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, reloadData];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, reloadData];
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the selected row.
|
||||
pub fn get_selected_row_index(&self) -> NSInteger {
|
||||
unsafe { msg_send![&*self.objc, selectedRow] }
|
||||
self.objc.get(|obj| unsafe { msg_send![obj, selectedRow] })
|
||||
}
|
||||
|
||||
/// Returns the currently clicked row. This is macOS-specific, and is generally used in context
|
||||
|
@ -643,32 +614,13 @@ impl<T> ListView<T> {
|
|||
/// }
|
||||
/// ```
|
||||
pub fn get_clicked_row_index(&self) -> NSInteger {
|
||||
unsafe { msg_send![&*self.objc, clickedRow] }
|
||||
self.objc.get(|obj| unsafe { msg_send![obj, clickedRow] })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout for ListView<T> {
|
||||
/// On macOS, this returns the NSScrollView, not the NSTableView.
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
#[cfg(target_os = "macos")]
|
||||
let val = self.scrollview.objc.clone();
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
let val = self.objc.clone();
|
||||
|
||||
val
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, view: &V) {
|
||||
let backing_node = view.get_backing_node();
|
||||
|
||||
unsafe {
|
||||
#[cfg(target_os = "macos")]
|
||||
let _: () = msg_send![&*self.scrollview.objc, addSubview:backing_node];
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
let _: () = msg_send![&*self.objc, addSubview:backing_node];
|
||||
}
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -680,13 +632,13 @@ impl<T> Drop for ListView<T> {
|
|||
///
|
||||
/// There are, thankfully, no delegates we need to break here.
|
||||
fn drop(&mut self) {
|
||||
if self.delegate.is_some() {
|
||||
/*if self.delegate.is_some() {
|
||||
unsafe {
|
||||
let superview: id = msg_send![&*self.objc, superview];
|
||||
if superview != nil {
|
||||
let _: () = msg_send![&*self.objc, removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ use crate::color::Color;
|
|||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::pasteboard::PasteboardType;
|
||||
use crate::view::ViewDelegate;
|
||||
use crate::utils::properties::ObjcProperty;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
@ -88,7 +89,7 @@ fn allocate_view(registration_fn: fn() -> *const Class) -> id {
|
|||
#[derive(Debug)]
|
||||
pub struct ListViewRow<T = ()> {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: Rc<RefCell<Id<Object>>>,
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// A pointer to the delegate for this view.
|
||||
pub delegate: Option<Box<T>>,
|
||||
|
@ -147,7 +148,7 @@ impl ListViewRow {
|
|||
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: ObjcProperty::retain(view),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +185,7 @@ impl<T> ListViewRow<T> where T: ViewDelegate + 'static {
|
|||
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: ObjcProperty::retain(view),
|
||||
};
|
||||
|
||||
view
|
||||
|
@ -218,7 +219,7 @@ impl<T> ListViewRow<T> where T: ViewDelegate + 'static {
|
|||
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: ObjcProperty::retain(view),
|
||||
};
|
||||
|
||||
(&mut delegate).did_load(view.clone_as_handle());
|
||||
|
@ -246,17 +247,11 @@ impl<T> ListViewRow<T> where T: ViewDelegate + 'static {
|
|||
height: self.height.clone(),
|
||||
center_x: self.center_x.clone(),
|
||||
center_y: self.center_y.clone(),
|
||||
objc: Rc::clone(&self.objc)
|
||||
objc: self.objc.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*impl<T> From<&ListViewRow<T>> for ShareId<Object> {
|
||||
fn from(row: &ListViewRow<T>) -> ShareId<Object> {
|
||||
row.objc.clone()
|
||||
}
|
||||
}*/
|
||||
|
||||
impl<T> ListViewRow<T> {
|
||||
/// An internal method that returns a clone of this object, sans references to the delegate or
|
||||
/// callback pointer. We use this in calling `did_load()` - implementing delegates get a way to
|
||||
|
@ -275,7 +270,7 @@ impl<T> ListViewRow<T> {
|
|||
height: self.height.clone(),
|
||||
center_x: self.center_x.clone(),
|
||||
center_y: self.center_y.clone(),
|
||||
objc: Rc::clone(&self.objc)
|
||||
objc: self.objc.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,56 +278,38 @@ impl<T> ListViewRow<T> {
|
|||
pub fn set_identifier(&self, identifier: &'static str) {
|
||||
let identifier = NSString::new(identifier);
|
||||
|
||||
let objc = self.objc.borrow();
|
||||
unsafe {
|
||||
let _: () = msg_send![&**objc, setIdentifier:&*identifier];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setIdentifier:&*identifier];
|
||||
});
|
||||
}
|
||||
|
||||
/// Call this to set the background color for the backing layer.
|
||||
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||
let mut objc = self.objc.borrow_mut();
|
||||
let color: id = color.as_ref().into();
|
||||
|
||||
unsafe {
|
||||
(&mut **objc).set_ivar(BACKGROUND_COLOR, color);
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
(&mut *obj).set_ivar(BACKGROUND_COLOR, color);
|
||||
});
|
||||
}
|
||||
|
||||
/// Register this view for drag and drop operations.
|
||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||
unsafe {
|
||||
let types: NSArray = types.into_iter().map(|t| {
|
||||
// This clone probably doesn't need to be here, but it should also be cheap as
|
||||
// this is just an enum... and this is not an oft called method.
|
||||
let x: NSString = (*t).into();
|
||||
x.into()
|
||||
}).collect::<Vec<id>>().into();
|
||||
let types: NSArray = types.into_iter().map(|t| {
|
||||
// This clone probably doesn't need to be here, but it should also be cheap as
|
||||
// this is just an enum... and this is not an oft called method.
|
||||
let x: NSString = (*t).into();
|
||||
x.into()
|
||||
}).collect::<Vec<id>>().into();
|
||||
|
||||
let objc = self.objc.borrow();
|
||||
let _: () = msg_send![&**objc, registerForDraggedTypes:&*types];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, registerForDraggedTypes:&*types];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout for ListViewRow<T> {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
let objc = self.objc.borrow();
|
||||
|
||||
unsafe {
|
||||
// @TODO: Need a better solution here.
|
||||
let x: id = msg_send![&**objc, self];
|
||||
ShareId::from_ptr(x)
|
||||
}
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, view: &V) {
|
||||
let backing_node = view.get_backing_node();
|
||||
|
||||
let objc = self.objc.borrow();
|
||||
unsafe {
|
||||
let _: () = msg_send![&**objc, addSubview:backing_node];
|
||||
}
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,9 +69,9 @@ impl ToolbarItem {
|
|||
pub fn set_button(&mut self, button: Button) {
|
||||
button.set_bezel_style(BezelStyle::TexturedRounded);
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setView:&*button.objc];
|
||||
}
|
||||
button.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![&*self.objc, setView:obj];
|
||||
});
|
||||
|
||||
self.button = Some(button);
|
||||
}
|
||||
|
|
|
@ -289,11 +289,9 @@ impl<T> Window<T> {
|
|||
|
||||
/// Given a view, sets it as the content view for this window.
|
||||
pub fn set_content_view<L: Layout + 'static>(&self, view: &L) {
|
||||
let backing_node = view.get_backing_node();
|
||||
|
||||
unsafe {
|
||||
view.with_backing_node(|backing_node| unsafe {
|
||||
let _: () = msg_send![&*self.objc, setContentView:&*backing_node];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Given a view, sets it as the content view controller for this window.
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
//! This module provides some basic wrappers for Pasteboard functionality. It's currently not an
|
||||
//! exhaustive clone, but feel free to pull request accordingly!
|
||||
|
||||
use crate::foundation::NSString;
|
||||
|
||||
/// Constants for the standard system pasteboard names.
|
||||
|
|
|
@ -20,6 +20,7 @@ use objc::{class, msg_send, sel, sel_impl};
|
|||
use crate::foundation::{id, nil, YES, NO, NSUInteger};
|
||||
use crate::color::Color;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::utils::properties::ObjcProperty;
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
mod ios;
|
||||
|
@ -34,7 +35,7 @@ pub use enums::ProgressIndicatorStyle;
|
|||
#[derive(Debug)]
|
||||
pub struct ProgressIndicator {
|
||||
/// A pointer to the Objective-C Object.
|
||||
pub objc: ShareId<Object>,
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// A pointer to the Objective-C runtime top layout constraint.
|
||||
pub top: LayoutAnchorY,
|
||||
|
@ -99,7 +100,7 @@ impl ProgressIndicator {
|
|||
height: LayoutAnchorDimension::height(view),
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,32 +108,33 @@ impl ProgressIndicator {
|
|||
impl ProgressIndicator {
|
||||
/// Starts the animation for an indeterminate indicator.
|
||||
pub fn start_animation(&self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, startAnimation:nil];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, startAnimation:nil];
|
||||
});
|
||||
}
|
||||
|
||||
/// Stops any animations that are currently happening on this indicator (e.g, if it's an
|
||||
/// indeterminate looping animation).
|
||||
pub fn stop_animation(&self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, stopAnimation:nil];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, stopAnimation:nil];
|
||||
});
|
||||
}
|
||||
|
||||
/// Increment the progress indicator by the amount specified.
|
||||
pub fn increment(&self, amount: f64) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, incrementBy:amount];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, incrementBy:amount];
|
||||
});
|
||||
}
|
||||
|
||||
/// Set the style for the progress indicator.
|
||||
pub fn set_style(&self, style: ProgressIndicatorStyle) {
|
||||
unsafe {
|
||||
let style = style as NSUInteger;
|
||||
let _: () = msg_send![&*self.objc, setStyle:style];
|
||||
}
|
||||
let style = style as NSUInteger;
|
||||
|
||||
self.objc.with_mut(move |obj| unsafe {
|
||||
let _: () = msg_send![obj, setStyle:style];
|
||||
});
|
||||
}
|
||||
|
||||
/// Set whether this is an indeterminate indicator or not. Indeterminate indicators are
|
||||
|
@ -140,12 +142,12 @@ impl ProgressIndicator {
|
|||
///
|
||||
/// Invert this to go back to a bar appearance.
|
||||
pub fn set_indeterminate(&self, is_indeterminate: bool) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setIndeterminate:match is_indeterminate {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setIndeterminate:match is_indeterminate {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the value of this progress indicator.
|
||||
|
@ -154,33 +156,25 @@ impl ProgressIndicator {
|
|||
pub fn set_value(&self, value: f64) {
|
||||
let value = value as CGFloat;
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setDoubleValue:value];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setDoubleValue:value];
|
||||
});
|
||||
}
|
||||
|
||||
/// Set whether this control is hidden or not.
|
||||
pub fn set_hidden(&self, hidden: bool) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setHidden:match hidden {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setHidden:match hidden {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for ProgressIndicator {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
self.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, view: &V) {
|
||||
let backing_node = view.get_backing_node();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, addSubview:backing_node];
|
||||
}
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,11 +187,11 @@ impl Drop for ProgressIndicator {
|
|||
///
|
||||
/// There are, thankfully, no delegates we need to break here.
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
/*unsafe {
|
||||
let superview: id = msg_send![&*self.objc, superview];
|
||||
if superview != nil {
|
||||
let _: () = msg_send![&*self.objc, removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ use crate::foundation::{id, nil, YES, NO, NSArray, NSString};
|
|||
use crate::color::Color;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::pasteboard::PasteboardType;
|
||||
use crate::utils::properties::ObjcProperty;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
@ -90,7 +91,7 @@ fn allocate_view(registration_fn: fn() -> *const Class) -> id {
|
|||
#[derive(Debug)]
|
||||
pub struct ScrollView<T = ()> {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: ShareId<Object>,
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// A pointer to the delegate for this view.
|
||||
pub delegate: Option<Box<T>>,
|
||||
|
@ -149,7 +150,7 @@ impl ScrollView {
|
|||
height: LayoutAnchorDimension::height(view),
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +179,7 @@ impl<T> ScrollView<T> where T: ScrollViewDelegate + 'static {
|
|||
height: LayoutAnchorDimension::height(view),
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
};
|
||||
|
||||
(&mut delegate).did_load(view.clone_as_handle());
|
||||
|
@ -212,26 +213,17 @@ impl<T> ScrollView<T> {
|
|||
/// Call this to set the background color for the backing layer.
|
||||
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||
// @TODO: This is wrong.
|
||||
let color = color.as_ref().cg_color();
|
||||
|
||||
unsafe {
|
||||
let layer: id = msg_send![&*self.objc, layer];
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let color = color.as_ref().cg_color();
|
||||
let layer: id = msg_send![obj, layer];
|
||||
let _: () = msg_send![layer, setBackgroundColor:color];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout for ScrollView<T> {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
self.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, view: &V) {
|
||||
let backing_node = view.get_backing_node();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, addSubview:backing_node];
|
||||
}
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,13 +235,13 @@ impl<T> Drop for ScrollView<T> {
|
|||
///
|
||||
/// There are, thankfully, no delegates we need to break here.
|
||||
fn drop(&mut self) {
|
||||
if self.delegate.is_some() {
|
||||
/*if self.delegate.is_some() {
|
||||
unsafe {
|
||||
let superview: id = msg_send![&*self.objc, superview];
|
||||
if superview != nil {
|
||||
let _: () = msg_send![&*self.objc, removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,14 +12,14 @@ use objc::{class, msg_send, sel, sel_impl};
|
|||
use crate::foundation::{id, nil, BOOL, YES, NO, NSString};
|
||||
use crate::invoker::TargetActionHandler;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::utils::load;
|
||||
use crate::utils::{load, properties::ObjcProperty};
|
||||
|
||||
/// A wrapper for `NSSwitch`. Holds (retains) pointers for the Objective-C runtime
|
||||
/// where our `NSSwitch` lives.
|
||||
#[derive(Debug)]
|
||||
pub struct Switch {
|
||||
/// A pointer to the underlying Objective-C Object.
|
||||
pub objc: ShareId<Object>,
|
||||
pub objc: ObjcProperty,
|
||||
handler: Option<TargetActionHandler>,
|
||||
|
||||
/// A pointer to the Objective-C runtime top layout constraint.
|
||||
|
@ -79,35 +79,35 @@ impl Switch {
|
|||
height: LayoutAnchorDimension::height(view),
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets whether this is checked on or off.
|
||||
pub fn set_checked(&mut self, checked: bool) {
|
||||
unsafe {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
// @TODO: The constants to use here changed back in 10.13ish, so... do we support that,
|
||||
// or just hide it?
|
||||
let _: () = msg_send![&*self.objc, setState:match checked {
|
||||
let _: () = msg_send![obj, setState:match checked {
|
||||
true => 1,
|
||||
false => 0
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Attaches a callback for button press events. Don't get too creative now...
|
||||
/// best just to message pass or something.
|
||||
pub fn set_action<F: Fn() + Send + Sync + 'static>(&mut self, action: F) {
|
||||
let handler = TargetActionHandler::new(&*self.objc, action);
|
||||
self.handler = Some(handler);
|
||||
//let handler = TargetActionHandler::new(&*self.objc, action);
|
||||
//self.handler = Some(handler);
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for Switch {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
self.objc.clone()
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
|
||||
|
||||
fn add_subview<V: Layout>(&self, _view: &V) {
|
||||
panic!(r#"
|
||||
Tried to add a subview to a Button. This is not allowed in Cacao. If you think this should be supported,
|
||||
|
@ -120,10 +120,10 @@ impl Drop for Switch {
|
|||
// Just to be sure, let's... nil these out. They should be weak references,
|
||||
// but I'd rather be paranoid and remove them later.
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setTarget:nil];
|
||||
let _: () = msg_send![&*self.objc, setAction:nil];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setTarget:nil];
|
||||
let _: () = msg_send![obj, setAction:nil];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ use crate::foundation::{id, nil, YES, NO, NSArray, NSInteger, NSUInteger, NSStri
|
|||
use crate::color::Color;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::text::{Font, TextAlign, LineBreakMode};
|
||||
use crate::utils::properties::ObjcProperty;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
@ -90,10 +91,51 @@ fn allocate_view(registration_fn: fn() -> *const Class) -> id {
|
|||
|
||||
/// A clone-able handler to an `NSTextField/UILabel` reference in the
|
||||
/// Objective-C runtime.
|
||||
/// Wraps `NSTextField` and `UILabel` across platforms, explicitly as a Label.
|
||||
/// In AppKit, `NSTextField` does double duty, and for clarity we just double
|
||||
/// the implementation.
|
||||
///
|
||||
/// Labels implement Autolayout, which enable you to specify how things should appear on the screen.
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use cacao::color::rgb;
|
||||
/// use cacao::layout::{Layout, LayoutConstraint};
|
||||
/// use cacao::view::Label;
|
||||
/// use cacao::window::{Window, WindowDelegate};
|
||||
///
|
||||
/// #[derive(Default)]
|
||||
/// struct AppWindow {
|
||||
/// content: Label,
|
||||
/// label: Label,
|
||||
/// window: Window
|
||||
/// }
|
||||
///
|
||||
/// impl WindowDelegate for AppWindow {
|
||||
/// fn did_load(&mut self, window: Window) {
|
||||
/// window.set_minimum_content_size(300., 300.);
|
||||
/// self.window = window;
|
||||
///
|
||||
/// self.label.set_background_color(rgb(224, 82, 99));
|
||||
/// self.label.set_text("LOL");
|
||||
/// self.content.add_subview(&self.red);
|
||||
///
|
||||
/// self.window.set_content_view(&self.content);
|
||||
///
|
||||
/// LayoutConstraint::activate(&[
|
||||
/// self.red.top.constraint_equal_to(&self.content.top).offset(16.),
|
||||
/// self.red.leading.constraint_equal_to(&self.content.leading).offset(16.),
|
||||
/// self.red.trailing.constraint_equal_to(&self.content.trailing).offset(-16.),
|
||||
/// self.red.bottom.constraint_equal_to(&self.content.bottom).offset(-16.),
|
||||
/// ]);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// For more information on Autolayout, view the module or check out the examples folder.
|
||||
#[derive(Debug)]
|
||||
pub struct Label<T = ()> {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: ShareId<Object>,
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// A pointer to the delegate for this view.
|
||||
pub delegate: Option<Box<T>>,
|
||||
|
@ -152,7 +194,7 @@ impl Label {
|
|||
height: LayoutAnchorDimension::height(view),
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
objc: ObjcProperty::retain(view),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +225,7 @@ impl<T> Label<T> where T: LabelDelegate + 'static {
|
|||
height: LayoutAnchorDimension::height(label),
|
||||
center_x: LayoutAnchorX::center(label),
|
||||
center_y: LayoutAnchorY::center(label),
|
||||
objc: unsafe { ShareId::from_ptr(label) },
|
||||
objc: ObjcProperty::retain(label),
|
||||
};
|
||||
|
||||
//(&mut delegate).did_load(label.clone_as_handle());
|
||||
|
@ -217,21 +259,21 @@ impl<T> Label<T> {
|
|||
/// Call this to set the background color for the backing layer.
|
||||
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||
// @TODO: This is wrong.
|
||||
let color = color.as_ref().cg_color();
|
||||
|
||||
unsafe {
|
||||
let layer: id = msg_send![&*self.objc, layer];
|
||||
// Needs to set ivar and such, akin to View.
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let color = color.as_ref().cg_color();
|
||||
let layer: id = msg_send![obj, layer];
|
||||
let _: () = msg_send![layer, setBackgroundColor:color];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Call this to set the color of the text.
|
||||
pub fn set_text_color<C: AsRef<Color>>(&self, color: C) {
|
||||
let color: id = color.as_ref().into();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setTextColor:color];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setTextColor:color];
|
||||
});
|
||||
}
|
||||
|
||||
/// Call this to set the text for the label.
|
||||
|
@ -239,26 +281,24 @@ impl<T> Label<T> {
|
|||
let text = text.as_ref();
|
||||
let s = NSString::new(text);
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setStringValue:&*s];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setStringValue:&*s];
|
||||
});
|
||||
}
|
||||
|
||||
/// Retrieve the text currently held in the label.
|
||||
pub fn get_text(&self) -> String {
|
||||
let s = NSString::retain(unsafe {
|
||||
msg_send![&*self.objc, stringValue]
|
||||
});
|
||||
|
||||
s.to_string()
|
||||
self.objc.get(|obj| unsafe {
|
||||
NSString::retain(msg_send![obj, stringValue]).to_string()
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the text alignment for this label.
|
||||
pub fn set_text_alignment(&self, alignment: TextAlign) {
|
||||
unsafe {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let alignment: NSInteger = alignment.into();
|
||||
let _: () = msg_send![&*self.objc, setAlignment:alignment];
|
||||
}
|
||||
let _: () = msg_send![obj, setAlignment:alignment];
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the font for this label.
|
||||
|
@ -267,51 +307,43 @@ impl<T> Label<T> {
|
|||
// font object - it seems like it can be optimized away otherwise.
|
||||
let font = font.as_ref().clone();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setFont:&*font];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setFont:&*font];
|
||||
});
|
||||
}
|
||||
|
||||
/// Set whether this is hidden or not.
|
||||
pub fn set_hidden(&self, hidden: bool) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setHidden:match hidden {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setHidden:match hidden {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the maximum number of lines.
|
||||
pub fn set_max_number_of_lines(&self, num: NSInteger) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setMaximumNumberOfLines:num];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setMaximumNumberOfLines:num];
|
||||
});
|
||||
}
|
||||
|
||||
/// Set the line break mode for this label.
|
||||
pub fn set_line_break_mode(&self, mode: LineBreakMode) {
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe {
|
||||
let cell: id = msg_send![&*self.objc, cell];
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let cell: id = msg_send![obj, cell];
|
||||
let mode = mode as NSUInteger;
|
||||
let _: () = msg_send![cell, setTruncatesLastVisibleLine:YES];
|
||||
let _: () = msg_send![cell, setLineBreakMode:mode];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout for Label<T> {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
self.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, view: &V) {
|
||||
let backing_node = view.get_backing_node();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, addSubview:backing_node];
|
||||
}
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,13 +355,13 @@ impl<T> Drop for Label<T> {
|
|||
///
|
||||
/// There are, thankfully, no delegates we need to break here.
|
||||
fn drop(&mut self) {
|
||||
if self.delegate.is_some() {
|
||||
/*if self.delegate.is_some() {
|
||||
unsafe {
|
||||
let superview: id = msg_send![&*self.objc, superview];
|
||||
if superview != nil {
|
||||
let _: () = msg_send![&*self.objc, removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ mod cell_factory;
|
|||
pub use cell_factory::CellFactory;
|
||||
|
||||
pub mod os;
|
||||
pub mod properties;
|
||||
|
||||
/// A generic trait that's used throughout multiple different controls in this framework - acts as
|
||||
/// a guard for whether something is a (View|Window|etc)Controller.
|
||||
|
|
74
src/utils/properties.rs
Normal file
74
src/utils/properties.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use objc_id::Id;
|
||||
use objc::runtime::Object;
|
||||
|
||||
use crate::foundation::id;
|
||||
|
||||
/// A wrapper for single-threaded `ObjcProperty` types.
|
||||
///
|
||||
/// An `ObjcProperty` is something that exists on the Objective-C side that we want to interact with, and
|
||||
/// support cloning with respect to our side and the general Rust rules. Thus, we do a layer of
|
||||
/// Rc/RefCell to shield things and make life easier.
|
||||
///
|
||||
/// It is possible we could remove the `Id` wrapper in here if we're just doing this ourselves, and
|
||||
/// is probably worth investigating at some point.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ObjcProperty(Rc<RefCell<Id<Object>>>);
|
||||
|
||||
impl ObjcProperty {
|
||||
/// Given an Objective-C object, retains it and wraps it as a `Property`.
|
||||
pub fn retain(obj: id) -> Self {
|
||||
ObjcProperty(Rc::new(RefCell::new(unsafe {
|
||||
Id::from_ptr(obj)
|
||||
})))
|
||||
}
|
||||
|
||||
/// Runs a handler with mutable access for the underlying Objective-C object.
|
||||
///
|
||||
/// Note that this is mutable access from the Rust side; we make every effort to ensure things are valid
|
||||
/// on the Objective-C side as well, but there be dragons.
|
||||
pub fn with_mut<F: Fn(id)>(&self, handler: F) {
|
||||
let mut obj = self.0.borrow_mut();
|
||||
handler(&mut **obj);
|
||||
}
|
||||
|
||||
/// Runs a handler with the underlying Objective-C type.
|
||||
///
|
||||
/// The handler can return whatever; this is primarily intended for dynamically calling getters
|
||||
/// on the underlying type.
|
||||
pub fn get<R, F: Fn(&Object) -> R>(&self, handler: F) -> R {
|
||||
let obj = self.0.borrow();
|
||||
handler(&**obj)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper for a single-threaded nullable `Property`.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PropertyNullable<T>(Rc<RefCell<Option<T>>>);
|
||||
|
||||
impl<T> PropertyNullable<T> {
|
||||
pub fn new(obj: T) -> Self {
|
||||
Self(Rc::new(RefCell::new(Some(obj))))
|
||||
}
|
||||
|
||||
pub fn clone(&self) -> Self {
|
||||
Self(Rc::clone(&self.0))
|
||||
}
|
||||
|
||||
pub fn with<F>(&self, handler: F)
|
||||
where
|
||||
F: Fn(&T)
|
||||
{
|
||||
let borrow = self.0.borrow();
|
||||
if let Some(s) = &*borrow {
|
||||
handler(s);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&self, obj: T) {
|
||||
let mut borrow = self.0.borrow_mut();
|
||||
*borrow = Some(obj);
|
||||
}
|
||||
}
|
|
@ -63,7 +63,9 @@ where
|
|||
(&mut *vc).set_ivar(VIEW_DELEGATE_PTR, ptr as usize);
|
||||
}
|
||||
|
||||
let _: () = msg_send![vc, setView:&*view.get_backing_node()];
|
||||
view.with_backing_node(|backing_node| {
|
||||
let _: () = msg_send![vc, setView:backing_node];
|
||||
});
|
||||
|
||||
ShareId::from_ptr(vc)
|
||||
};
|
||||
|
|
|
@ -49,9 +49,7 @@ use crate::foundation::{id, nil, YES, NO, NSArray, NSString};
|
|||
use crate::color::Color;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::pasteboard::PasteboardType;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use crate::utils::properties::ObjcProperty;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
@ -96,7 +94,7 @@ fn common_init(class: *const Class) -> id {
|
|||
#[derive(Debug)]
|
||||
pub struct View<T = ()> {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: Rc<RefCell<Id<Object>>>,
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// A pointer to the delegate for this view.
|
||||
pub delegate: Option<Box<T>>,
|
||||
|
@ -155,7 +153,7 @@ impl View {
|
|||
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: ObjcProperty::retain(view),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +185,7 @@ impl<T> View<T> where T: ViewDelegate + 'static {
|
|||
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: ObjcProperty::retain(view),
|
||||
};
|
||||
|
||||
(&mut delegate).did_load(view.clone_as_handle());
|
||||
|
@ -214,51 +212,35 @@ impl<T> View<T> {
|
|||
height: self.height.clone(),
|
||||
center_x: self.center_x.clone(),
|
||||
center_y: self.center_y.clone(),
|
||||
objc: Rc::clone(&self.objc)
|
||||
objc: self.objc.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Call this to set the background color for the backing layer.
|
||||
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||
let mut objc = self.objc.borrow_mut();
|
||||
let color: id = color.as_ref().into();
|
||||
|
||||
unsafe {
|
||||
(&mut **objc).set_ivar(BACKGROUND_COLOR, color);
|
||||
}
|
||||
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
(&mut *obj).set_ivar(BACKGROUND_COLOR, color);
|
||||
});
|
||||
}
|
||||
|
||||
/// Register this view for drag and drop operations.
|
||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||
unsafe {
|
||||
let types: NSArray = types.into_iter().map(|t| {
|
||||
let x: NSString = (*t).into();
|
||||
x.into()
|
||||
}).collect::<Vec<id>>().into();
|
||||
let types: NSArray = types.into_iter().map(|t| {
|
||||
let x: NSString = (*t).into();
|
||||
x.into()
|
||||
}).collect::<Vec<id>>().into();
|
||||
|
||||
let objc = self.objc.borrow();
|
||||
let _: () = msg_send![&**objc, registerForDraggedTypes:&*types];
|
||||
}
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, registerForDraggedTypes:&*types];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout for View<T> {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
let objc = self.objc.borrow();
|
||||
|
||||
unsafe {
|
||||
let x: id = msg_send![&**objc, self];
|
||||
ShareId::from_ptr(x)
|
||||
}
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, view: &V) {
|
||||
let backing_node = view.get_backing_node();
|
||||
|
||||
let objc = self.objc.borrow();
|
||||
unsafe {
|
||||
let _: () = msg_send![&**objc, addSubview:backing_node];
|
||||
}
|
||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue