Ongoing push to a v0.1.
- Basic support for `AttributedString` type. - `Debug` implementations across the board to ease debugging issues. - Methods that take `Color` and `Font` types now accept an `AsRef` to make implementing less of a headache. - Cleanup of the `utils` module.
This commit is contained in:
parent
10c513edad
commit
bc54b49475
|
@ -14,16 +14,12 @@ use crate::image::Image;
|
||||||
use crate::foundation::{id, nil, BOOL, YES, NO, NSString, NSUInteger};
|
use crate::foundation::{id, nil, BOOL, YES, NO, NSString, NSUInteger};
|
||||||
use crate::invoker::TargetActionHandler;
|
use crate::invoker::TargetActionHandler;
|
||||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||||
use crate::text::Font;
|
use crate::text::{AttributedString, Font};
|
||||||
use crate::utils::load;
|
use crate::utils::load;
|
||||||
|
|
||||||
#[cfg(feature = "macos")]
|
#[cfg(feature = "macos")]
|
||||||
use crate::macos::FocusRingType;
|
use crate::macos::FocusRingType;
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
static NSForegroundColorAttributeName: id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper for `NSButton`. Holds (retains) pointers for the Objective-C runtime
|
/// A wrapper for `NSButton`. Holds (retains) pointers for the Objective-C runtime
|
||||||
/// where our `NSButton` lives.
|
/// where our `NSButton` lives.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -111,17 +107,13 @@ impl Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<C: AsRef<Color>>(&self, color: C) {
|
||||||
let bg = color.into_platform_specific_color();
|
let color: id = color.as_ref().into();
|
||||||
|
|
||||||
#[cfg(feature = "macos")]
|
#[cfg(feature = "macos")]
|
||||||
unsafe {
|
unsafe {
|
||||||
let cell: id = msg_send![&*self.objc, cell];
|
let cell: id = msg_send![&*self.objc, cell];
|
||||||
let _: () = msg_send![cell, setBackgroundColor:bg];
|
let _: () = msg_send![cell, setBackgroundColor:color];
|
||||||
/*let cg: id = msg_send![bg, CGColor];
|
|
||||||
let layer: id = msg_send![&*self.objc, layer];
|
|
||||||
let _: () = msg_send![layer, setBackgroundColor:cg];
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,20 +125,19 @@ impl Button {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text_color(&self, color: Color) {
|
/// Sets the text color for this button.
|
||||||
let bg = color.into_platform_specific_color();
|
///
|
||||||
|
/// On macOS, this is done by way of an `AttributedString` under the hood.
|
||||||
// @TODO: Clean this up, and look at just using `CFMutableAttributedString` instead
|
pub fn set_text_color<C: AsRef<Color>>(&self, color: C) {
|
||||||
// to avoid ObjC overhead.
|
#[cfg(feature = "macos")]
|
||||||
unsafe {
|
unsafe {
|
||||||
let alloc: id = msg_send![class!(NSMutableAttributedString), alloc];
|
let text: id = msg_send![&*self.objc, attributedTitle];
|
||||||
let s: id = msg_send![&*self.objc, attributedTitle];
|
let len: isize = msg_send![text, length];
|
||||||
let attributed_string: id = msg_send![alloc, initWithAttributedString:s];
|
|
||||||
let len: isize = msg_send![s, length];
|
|
||||||
let range = core_foundation::base::CFRange::init(0, len);
|
|
||||||
|
|
||||||
let _: () = msg_send![attributed_string, addAttribute:NSForegroundColorAttributeName value:bg range:range];
|
let mut attr_str = AttributedString::wrap(text);
|
||||||
let _: () = msg_send![&*self.objc, setAttributedTitle:attributed_string];
|
attr_str.set_text_color(color, 0..len);
|
||||||
|
|
||||||
|
let _: () = msg_send![&*self.objc, setAttributedTitle:&*attr_str];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,9 +154,11 @@ impl Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the font for this button.
|
/// Sets the font for this button.
|
||||||
pub fn set_font(&self, font: &Font) {
|
pub fn set_font<F: AsRef<Font>>(&self, font: F) {
|
||||||
|
let font = font.as_ref().clone();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![&*self.objc, setFont:&*font.objc];
|
let _: () = msg_send![&*self.objc, setFont:&*font];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,8 +190,8 @@ impl Layout for Button {
|
||||||
|
|
||||||
fn add_subview<V: Layout>(&self, _view: &V) {
|
fn add_subview<V: Layout>(&self, _view: &V) {
|
||||||
panic!(r#"
|
panic!(r#"
|
||||||
Tried to add a subview to a Button. This is not allowed in Cacao. If you think this should be supported,
|
Tried to add a subview to a Button. This is not allowed in Cacao.
|
||||||
open a discussion on the GitHub repo.
|
If you think this should be supported, open a discussion on the GitHub repo.
|
||||||
"#);
|
"#);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,8 +203,8 @@ impl Layout for &Button {
|
||||||
|
|
||||||
fn add_subview<V: Layout>(&self, _view: &V) {
|
fn add_subview<V: Layout>(&self, _view: &V) {
|
||||||
panic!(r#"
|
panic!(r#"
|
||||||
Tried to add a subview to a Button. This is not allowed in Cacao. If you think this should be supported,
|
Tried to add a subview to a Button. This is not allowed in Cacao.
|
||||||
open a discussion on the GitHub repo.
|
If you think this should be supported, open a discussion on the GitHub repo.
|
||||||
"#);
|
"#);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
170
src/color/mod.rs
170
src/color/mod.rs
|
@ -14,12 +14,14 @@
|
||||||
///
|
///
|
||||||
/// @TODO: bundle iOS/tvOS support.
|
/// @TODO: bundle iOS/tvOS support.
|
||||||
|
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use core_graphics::base::CGFloat;
|
use core_graphics::base::CGFloat;
|
||||||
use core_graphics::color::CGColor;
|
use core_graphics::color::CGColor;
|
||||||
|
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc_id::ShareId;
|
use objc_id::Id;
|
||||||
|
|
||||||
use crate::foundation::id;
|
use crate::foundation::id;
|
||||||
use crate::utils::os;
|
use crate::utils::os;
|
||||||
|
@ -37,7 +39,7 @@ use macos_dynamic_color::{
|
||||||
/// In the event that a new variant is introduced in later versions of
|
/// In the event that a new variant is introduced in later versions of
|
||||||
/// macOS or iOS, calls that use the dynamic color(s) from here will likely
|
/// macOS or iOS, calls that use the dynamic color(s) from here will likely
|
||||||
/// default to the `Light` theme.
|
/// default to the `Light` theme.
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum Theme {
|
pub enum Theme {
|
||||||
/// The "default" theme on a platform. On macOS, this is Aqua.
|
/// The "default" theme on a platform. On macOS, this is Aqua.
|
||||||
/// On iOS and tvOS, this is whatever you call the system defined theme.
|
/// On iOS and tvOS, this is whatever you call the system defined theme.
|
||||||
|
@ -48,7 +50,7 @@ pub enum Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the contrast level for a rendering context.
|
/// Represents the contrast level for a rendering context.
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum Contrast {
|
pub enum Contrast {
|
||||||
/// The default contrast level for the system.
|
/// The default contrast level for the system.
|
||||||
Normal,
|
Normal,
|
||||||
|
@ -60,7 +62,7 @@ pub enum Contrast {
|
||||||
/// A `Style` is passed to you when doing dynamic color calculations. You can opt to
|
/// A `Style` is passed to you when doing dynamic color calculations. You can opt to
|
||||||
/// provide different colors depending on the settings in here - notably, this is useful
|
/// provide different colors depending on the settings in here - notably, this is useful
|
||||||
/// for supporting dark mode and high contrast accessibility contexts.
|
/// for supporting dark mode and high contrast accessibility contexts.
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
/// Represents the current theme for where this color may render.
|
/// Represents the current theme for where this color may render.
|
||||||
pub theme: Theme,
|
pub theme: Theme,
|
||||||
|
@ -69,39 +71,22 @@ pub struct Style {
|
||||||
pub contrast: Contrast
|
pub contrast: Contrast
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Property(Rc<RefCell<Id<Object>>>);
|
|
||||||
|
|
||||||
impl Property {
|
|
||||||
pub fn new(obj: id) -> Self {
|
|
||||||
Property(Rc::new(RefCell::new(Id::from_ptr(obj))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ThreadSafeProperty(Arc<RwLock<Id<Object>>>);
|
|
||||||
|
|
||||||
impl Property {
|
|
||||||
pub fn new(obj: id) -> Self {
|
|
||||||
Property(Rc::new(RefCell::new(Id::from_ptr(obj))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// Represents a Color. You can create custom colors using the various
|
/// Represents a Color. You can create custom colors using the various
|
||||||
/// initializers, or opt to use a system-provided color. The system provided
|
/// initializers, or opt to use a system-provided color. The system provided
|
||||||
/// colors will automatically switch to the "correct" colors/shades depending on whether
|
/// colors will automatically switch to the "correct" colors/shades depending on whether
|
||||||
/// the user is in light or dark mode; to support this with custom colors, be sure
|
/// the user is in light or dark mode; to support this with custom colors, you can create a
|
||||||
/// to call the `.dark()` method after initializing.
|
/// `dynamic` color with a custom handler that determines a color depending on a variety of system
|
||||||
#[derive(Clone)]
|
/// settings.
|
||||||
|
///
|
||||||
|
/// This enum is thread-safe, so clone away as needed.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
/// Represents an `NSColor` on macOS, and a `UIColor` everywhere else. You typically
|
/// Represents an `NSColor` on macOS, and a `UIColor` everywhere else. You typically
|
||||||
/// don't create this variant yourself; use the initializers found on this enum.
|
/// don't create this variant yourself; use the initializers found on this enum.
|
||||||
///
|
///
|
||||||
/// If you need to do custom work not covered by this enum, you can drop to
|
/// If you need to do custom work not covered by this enum, you can drop to
|
||||||
/// the Objective-C level yourself and wrap your color in this.
|
/// the Objective-C level yourself and wrap your color in this.
|
||||||
Object(ShareId<Object>),
|
Custom(Arc<RwLock<Id<Object>>>),
|
||||||
|
|
||||||
/// The system-provided black. Harsh - you probably don't want to use this.
|
/// The system-provided black. Harsh - you probably don't want to use this.
|
||||||
SystemBlack,
|
SystemBlack,
|
||||||
|
@ -244,9 +229,13 @@ pub enum Color {
|
||||||
/// The un-adaptable color for text on a dark background.
|
/// The un-adaptable color for text on a dark background.
|
||||||
LightText,
|
LightText,
|
||||||
|
|
||||||
WindowBackgroundColor,
|
/// The background color for a given window in the system theme.
|
||||||
|
#[cfg(feature = "macos")]
|
||||||
|
MacOSWindowBackgroundColor,
|
||||||
|
|
||||||
UnderPageBackgroundColor
|
/// The background color that should appear under a page per the system theme.
|
||||||
|
#[cfg(feature = "macos")]
|
||||||
|
MacOSUnderPageBackgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
|
@ -264,10 +253,10 @@ impl Color {
|
||||||
#[cfg(feature = "ios")]
|
#[cfg(feature = "ios")]
|
||||||
let color = class!(UIColor);
|
let color = class!(UIColor);
|
||||||
|
|
||||||
Color::Object(unsafe {
|
Color::Custom(Arc::new(RwLock::new(unsafe {
|
||||||
#[cfg(feature = "macos")]
|
#[cfg(feature = "macos")]
|
||||||
ShareId::from_ptr(msg_send![color, colorWithCalibratedRed:r green:g blue:b alpha:a])
|
Id::from_ptr(msg_send![color, colorWithCalibratedRed:r green:g blue:b alpha:a])
|
||||||
})
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates and returns a color in the RGB space, with the alpha level
|
/// Creates and returns a color in the RGB space, with the alpha level
|
||||||
|
@ -290,10 +279,10 @@ impl Color {
|
||||||
#[cfg(feature = "ios")]
|
#[cfg(feature = "ios")]
|
||||||
let color = class!(UIColor);
|
let color = class!(UIColor);
|
||||||
|
|
||||||
Color::Object(unsafe {
|
Color::Custom(Arc::new(RwLock::new(unsafe {
|
||||||
#[cfg(feature = "macos")]
|
#[cfg(feature = "macos")]
|
||||||
ShareId::from_ptr(msg_send![color, colorWithCalibratedHue:h saturation:s brightness:b alpha:a])
|
Id::from_ptr(msg_send![color, colorWithCalibratedHue:h saturation:s brightness:b alpha:a])
|
||||||
})
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates and returns a color in the RGB space, with the alpha level
|
/// Creates and returns a color in the RGB space, with the alpha level
|
||||||
|
@ -311,10 +300,10 @@ impl Color {
|
||||||
#[cfg(feature = "ios")]
|
#[cfg(feature = "ios")]
|
||||||
let color = class!(UIColor);
|
let color = class!(UIColor);
|
||||||
|
|
||||||
Color::Object(unsafe {
|
Color::Custom(Arc::new(RwLock::new(unsafe {
|
||||||
#[cfg(feature = "macos")]
|
#[cfg(feature = "macos")]
|
||||||
ShareId::from_ptr(msg_send![color, colorWithCalibratedWhite:level alpha:alpha])
|
Id::from_ptr(msg_send![color, colorWithCalibratedWhite:level alpha:alpha])
|
||||||
})
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates and returns a white Color with the specified level or intensity, with the alpha
|
/// Creates and returns a white Color with the specified level or intensity, with the alpha
|
||||||
|
@ -327,7 +316,7 @@ impl Color {
|
||||||
///
|
///
|
||||||
/// This method is not an ideal one to use, but is offered as a convenience method for those
|
/// This method is not an ideal one to use, but is offered as a convenience method for those
|
||||||
/// coming from other environments where these are more common.
|
/// coming from other environments where these are more common.
|
||||||
pub fn hexa(hex: &str, alpha: u8) -> Self {
|
pub fn hexa(_hex: &str, _alpha: u8) -> Self {
|
||||||
Color::SystemRed
|
Color::SystemRed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,45 +340,53 @@ impl Color {
|
||||||
where
|
where
|
||||||
F: Fn(Style) -> Color + 'static
|
F: Fn(Style) -> Color + 'static
|
||||||
{
|
{
|
||||||
|
// It's *possible* that we shouldn't cache these up-front and let them be truly dynamically
|
||||||
|
// allocated, but this is fine for now (and more predictable, even if perhaps wrong). I'm
|
||||||
|
// not entirely clear on how expensive the dynamic allocation would be pre-10.15/11.0 and
|
||||||
|
// am happy to do this for now and let someone who needs true dynamic allocation look into
|
||||||
|
// it and PR it.
|
||||||
#[cfg(feature = "macos")]
|
#[cfg(feature = "macos")]
|
||||||
Color::Object(unsafe {
|
Color::Custom(Arc::new(RwLock::new(unsafe {
|
||||||
let color: id = msg_send![macos_dynamic_color::register_class(), new];
|
let color: id = msg_send![macos_dynamic_color::register_class(), new];
|
||||||
|
|
||||||
(&mut *color).set_ivar(AQUA_LIGHT_COLOR_NORMAL_CONTRAST, handler(Style {
|
(&mut *color).set_ivar(AQUA_LIGHT_COLOR_NORMAL_CONTRAST, {
|
||||||
theme: Theme::Light,
|
let color: id = handler(Style {
|
||||||
contrast: Contrast::Normal
|
theme: Theme::Light,
|
||||||
}).to_objc());
|
contrast: Contrast::Normal
|
||||||
|
}).into();
|
||||||
|
|
||||||
(&mut *color).set_ivar(AQUA_LIGHT_COLOR_HIGH_CONTRAST, handler(Style {
|
color
|
||||||
theme: Theme::Light,
|
});
|
||||||
contrast: Contrast::High
|
|
||||||
}).to_objc());
|
|
||||||
|
|
||||||
(&mut *color).set_ivar(AQUA_DARK_COLOR_NORMAL_CONTRAST, handler(Style {
|
(&mut *color).set_ivar(AQUA_LIGHT_COLOR_HIGH_CONTRAST, {
|
||||||
theme: Theme::Dark,
|
let color: id = handler(Style {
|
||||||
contrast: Contrast::Normal
|
theme: Theme::Light,
|
||||||
}).to_objc());
|
contrast: Contrast::High
|
||||||
|
}).into();
|
||||||
|
|
||||||
(&mut *color).set_ivar(AQUA_DARK_COLOR_HIGH_CONTRAST, handler(Style {
|
color
|
||||||
theme: Theme::Light,
|
});
|
||||||
contrast: Contrast::Normal
|
|
||||||
}).to_objc());
|
|
||||||
|
|
||||||
ShareId::from_ptr(color)
|
(&mut *color).set_ivar(AQUA_DARK_COLOR_NORMAL_CONTRAST, {
|
||||||
})
|
let color: id = handler(Style {
|
||||||
}
|
theme: Theme::Dark,
|
||||||
|
contrast: Contrast::Normal
|
||||||
|
}).into();
|
||||||
|
|
||||||
/// Returns a pointer that can be used for the Objective-C runtime.
|
color
|
||||||
///
|
});
|
||||||
/// This method is primarily for internal use, but is kept public for those who might need to
|
|
||||||
/// work with colors outside of what's available in this enum.
|
|
||||||
pub fn to_objc(&self) -> id {
|
|
||||||
unsafe { to_objc(self) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legacy.
|
(&mut *color).set_ivar(AQUA_DARK_COLOR_HIGH_CONTRAST, {
|
||||||
pub fn into_platform_specific_color(&self) -> id {
|
let color: id = handler(Style {
|
||||||
unsafe { to_objc(self) }
|
theme: Theme::Light,
|
||||||
|
contrast: Contrast::Normal
|
||||||
|
}).into();
|
||||||
|
|
||||||
|
color
|
||||||
|
});
|
||||||
|
|
||||||
|
Id::from_ptr(color)
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a CGColor, which can be used in Core Graphics calls as well as other areas.
|
/// Returns a CGColor, which can be used in Core Graphics calls as well as other areas.
|
||||||
|
@ -401,7 +398,7 @@ impl Color {
|
||||||
pub fn cg_color(&self) -> CGColor {
|
pub fn cg_color(&self) -> CGColor {
|
||||||
// @TODO: This should probably return a CGColorRef...
|
// @TODO: This should probably return a CGColorRef...
|
||||||
unsafe {
|
unsafe {
|
||||||
let objc = to_objc(self);
|
let objc: id = self.into();
|
||||||
msg_send![objc, CGColor]
|
msg_send![objc, CGColor]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,19 +412,33 @@ impl AsRef<Color> for Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Color> for id {
|
||||||
|
/// Consumes and returns the pointer to the underlying Color.
|
||||||
|
fn from(color: Color) -> Self {
|
||||||
|
unsafe { to_objc(&color) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Color> for id {
|
||||||
|
/// Consumes and returns the pointer to the underlying Color.
|
||||||
|
fn from(color: &Color) -> Self {
|
||||||
|
unsafe { to_objc(color) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles color fallback for system-provided colors.
|
/// Handles color fallback for system-provided colors.
|
||||||
macro_rules! system_color_with_fallback {
|
macro_rules! system_color_with_fallback {
|
||||||
($class:ident, $color:ident, $fallback:ident) => ({
|
($class:ident, $color:ident, $fallback:ident) => ({
|
||||||
#[cfg(feature = "macos")]
|
#[cfg(feature = "macos")]
|
||||||
{
|
{
|
||||||
#[cfg(feature = "color_fallbacks")]
|
#[cfg(feature = "color-fallbacks")]
|
||||||
if os::minimum_semversion(10, 10, 0) {
|
if os::minimum_semversion(10, 10, 0) {
|
||||||
msg_send![$class, $color]
|
msg_send![$class, $color]
|
||||||
} else {
|
} else {
|
||||||
msg_send![$class, $fallback]
|
msg_send![$class, $fallback]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "color_fallbacks"))]
|
#[cfg(not(feature = "color-fallbacks"))]
|
||||||
msg_send![$class, $color]
|
msg_send![$class, $color]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -450,7 +461,10 @@ unsafe fn to_objc(obj: &Color) -> id {
|
||||||
|
|
||||||
match obj {
|
match obj {
|
||||||
// Regardless of platform, we can just dereference this one.
|
// Regardless of platform, we can just dereference this one.
|
||||||
Color::Object(obj) => msg_send![&**obj, self],
|
Color::Custom(color) => {
|
||||||
|
let mut ptr = color.write().unwrap();
|
||||||
|
&mut **ptr
|
||||||
|
},
|
||||||
|
|
||||||
Color::SystemBlack => msg_send![color, blackColor],
|
Color::SystemBlack => msg_send![color, blackColor],
|
||||||
Color::SystemWhite => msg_send![color, whiteColor],
|
Color::SystemWhite => msg_send![color, whiteColor],
|
||||||
|
@ -488,7 +502,11 @@ unsafe fn to_objc(obj: &Color) -> id {
|
||||||
Color::Link => system_color_with_fallback!(color, linkColor, blueColor),
|
Color::Link => system_color_with_fallback!(color, linkColor, blueColor),
|
||||||
Color::DarkText => system_color_with_fallback!(color, darkTextColor, blackColor),
|
Color::DarkText => system_color_with_fallback!(color, darkTextColor, blackColor),
|
||||||
Color::LightText => system_color_with_fallback!(color, lightTextColor, whiteColor),
|
Color::LightText => system_color_with_fallback!(color, lightTextColor, whiteColor),
|
||||||
Color::WindowBackgroundColor => system_color_with_fallback!(color, windowBackgroundColor, clearColor),
|
|
||||||
Color::UnderPageBackgroundColor => system_color_with_fallback!(color, underPageBackgroundColor, clearColor),
|
#[cfg(feature = "macos")]
|
||||||
|
Color::MacOSWindowBackgroundColor => system_color_with_fallback!(color, windowBackgroundColor, clearColor),
|
||||||
|
|
||||||
|
#[cfg(feature = "macos")]
|
||||||
|
Color::MacOSUnderPageBackgroundColor => system_color_with_fallback!(color, underPageBackgroundColor, clearColor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::foundation::NSUInteger;
|
||||||
use crate::pasteboard::Pasteboard;
|
use crate::pasteboard::Pasteboard;
|
||||||
|
|
||||||
/// Represents operations that can happen for a given drag/drop scenario.
|
/// Represents operations that can happen for a given drag/drop scenario.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum DragOperation {
|
pub enum DragOperation {
|
||||||
/// No drag operations are allowed.
|
/// No drag operations are allowed.
|
||||||
None,
|
None,
|
||||||
|
@ -53,6 +54,7 @@ impl From<DragOperation> for NSUInteger {
|
||||||
|
|
||||||
/// A wrapper for `NSDraggingInfo`. As this is a protocol/type you should never create yourself,
|
/// A wrapper for `NSDraggingInfo`. As this is a protocol/type you should never create yourself,
|
||||||
/// this only provides getters - merely a Rust-y way to grab what you need.
|
/// this only provides getters - merely a Rust-y way to grab what you need.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct DragInfo {
|
pub struct DragInfo {
|
||||||
pub info: ShareId<Object>
|
pub info: ShareId<Object>
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ impl From<&EventModifierFlag> for NSUInteger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
KeyDown
|
KeyDown
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use crate::foundation::{NSInteger, NSUInteger};
|
use crate::foundation::{NSInteger, NSUInteger};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum ModalResponse {
|
pub enum ModalResponse {
|
||||||
Ok,
|
Ok,
|
||||||
Continue,
|
Continue,
|
||||||
|
@ -33,6 +34,7 @@ impl From<NSInteger> for ModalResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum SearchPathDomainMask {
|
pub enum SearchPathDomainMask {
|
||||||
User,
|
User,
|
||||||
Local,
|
Local,
|
||||||
|
@ -53,6 +55,7 @@ impl From<SearchPathDomainMask> for NSUInteger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum SearchPathDirectory {
|
pub enum SearchPathDirectory {
|
||||||
Applications,
|
Applications,
|
||||||
DemoApplications,
|
DemoApplications,
|
||||||
|
|
|
@ -13,6 +13,7 @@ use crate::foundation::{id, nil, NO, NSString, NSUInteger};
|
||||||
use crate::error::{Error as AppKitError};
|
use crate::error::{Error as AppKitError};
|
||||||
use crate::filesystem::enums::{SearchPathDirectory, SearchPathDomainMask};
|
use crate::filesystem::enums::{SearchPathDirectory, SearchPathDomainMask};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct FileManager {
|
pub struct FileManager {
|
||||||
pub manager: RwLock<Id<Object>>
|
pub manager: RwLock<Id<Object>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use objc_id::Id;
|
||||||
///
|
///
|
||||||
/// When this is dropped, we automatically send a `drain` message to the underlying pool. You can
|
/// When this is dropped, we automatically send a `drain` message to the underlying pool. You can
|
||||||
/// also call `drain()` yourself if you need to drain for whatever reason.
|
/// also call `drain()` yourself if you need to drain for whatever reason.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct AutoReleasePool(pub Id<Object>);
|
pub struct AutoReleasePool(pub Id<Object>);
|
||||||
|
|
||||||
impl AutoReleasePool {
|
impl AutoReleasePool {
|
||||||
|
|
|
@ -95,11 +95,10 @@ impl ImageView {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<C: AsRef<Color>>(&self, color: C) {
|
||||||
let bg = color.into_platform_specific_color();
|
let cg = color.as_ref().cg_color();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let cg: id = msg_send![bg, CGColor];
|
|
||||||
let layer: id = msg_send![&*self.objc, layer];
|
let layer: id = msg_send![&*self.objc, layer];
|
||||||
let _: () = msg_send![layer, setBackgroundColor:cg];
|
let _: () = msg_send![layer, setBackgroundColor:cg];
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,11 +204,11 @@ impl<T> TextField<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<C: AsRef<Color>>(&self, color: C) {
|
||||||
let bg = color.to_objc();
|
// @TODO: This is wrong.
|
||||||
|
let cg = color.as_ref().cg_color();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let cg: id = msg_send![bg, CGColor];
|
|
||||||
let layer: id = msg_send![&*self.objc, layer];
|
let layer: id = msg_send![&*self.objc, layer];
|
||||||
let _: () = msg_send![layer, setBackgroundColor:cg];
|
let _: () = msg_send![layer, setBackgroundColor:cg];
|
||||||
}
|
}
|
||||||
|
@ -230,9 +230,12 @@ impl<T> TextField<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_font(&self, font: &Font) {
|
/// Sets the font for this input.
|
||||||
|
pub fn set_font<F: AsRef<Font>>(&self, font: F) {
|
||||||
|
let font = font.as_ref();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![&*self.objc, setFont:&*font.objc];
|
let _: () = msg_send![&*self.objc, setFont:&*font];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//#![deny(missing_docs)]
|
//#![deny(missing_docs)]
|
||||||
//#![deny(missing_debug_implementations)]
|
#![deny(missing_debug_implementations)]
|
||||||
#![cfg_attr(debug_assertions, allow(dead_code, unused_imports))]
|
#![cfg_attr(debug_assertions, allow(dead_code, unused_imports))]
|
||||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
@ -74,6 +74,8 @@
|
||||||
//!
|
//!
|
||||||
//! - `cloudkit`: Links `CloudKit.framework` and provides some wrappers around CloudKit
|
//! - `cloudkit`: Links `CloudKit.framework` and provides some wrappers around CloudKit
|
||||||
//! functionality. Currently not feature complete.
|
//! functionality. Currently not feature complete.
|
||||||
|
//! - `color_fallbacks`: Provides fallback colors for older systems where `systemColor` types don't
|
||||||
|
//! exist. This feature is very uncommon and you probably don't need it.
|
||||||
//! - `quicklook`: Links `QuickLook.framework` and offers methods for generating preview images for
|
//! - `quicklook`: Links `QuickLook.framework` and offers methods for generating preview images for
|
||||||
//! files.
|
//! files.
|
||||||
//! - `user-notifications`: Links `UserNotifications.framework` and provides functionality for
|
//! - `user-notifications`: Links `UserNotifications.framework` and provides functionality for
|
||||||
|
|
|
@ -84,8 +84,8 @@ impl RowAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the background color of this action.
|
/// Sets the background color of this action.
|
||||||
pub fn set_background_color(&mut self, color: Color) {
|
pub fn set_background_color<C: AsRef<Color>>(&mut self, color: C) {
|
||||||
let color = color.to_objc();
|
let color: id = color.as_ref().into();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![&*self.0, setBackgroundColor:color];
|
let _: () = msg_send![&*self.0, setBackgroundColor:color];
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::foundation::{NSInteger, NSUInteger};
|
||||||
/// This enum represents the different stock animations possible
|
/// This enum represents the different stock animations possible
|
||||||
/// for ListView row operations. You can pass it to `insert_rows`
|
/// for ListView row operations. You can pass it to `insert_rows`
|
||||||
/// and `remove_rows` - reloads don't get animations.
|
/// and `remove_rows` - reloads don't get animations.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum RowAnimation {
|
pub enum RowAnimation {
|
||||||
/// No animation.
|
/// No animation.
|
||||||
None,
|
None,
|
||||||
|
@ -41,7 +42,7 @@ impl Into<NSUInteger> for RowAnimation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum RowEdge {
|
pub enum RowEdge {
|
||||||
Leading,
|
Leading,
|
||||||
Trailing
|
Trailing
|
||||||
|
|
|
@ -420,9 +420,9 @@ impl<T> ListView<T> {
|
||||||
/// Dequeue a reusable cell. If one is not in the queue, will create and cache one for reuse.
|
/// Dequeue a reusable cell. If one is not in the queue, will create and cache one for reuse.
|
||||||
pub fn dequeue<R: ViewDelegate + 'static>(&self, identifier: &'static str) -> ListViewRow<R> {
|
pub fn dequeue<R: ViewDelegate + 'static>(&self, identifier: &'static str) -> ListViewRow<R> {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
unsafe {
|
{
|
||||||
let key = NSString::new(identifier);
|
let key = NSString::new(identifier);
|
||||||
let cell: id = msg_send![&*self.objc, makeViewWithIdentifier:&*key owner:nil];
|
let cell: id = unsafe { msg_send![&*self.objc, makeViewWithIdentifier:&*key owner:nil] };
|
||||||
|
|
||||||
if cell != nil {
|
if cell != nil {
|
||||||
ListViewRow::from_cached(cell)
|
ListViewRow::from_cached(cell)
|
||||||
|
@ -436,13 +436,13 @@ impl<T> ListView<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<C: AsRef<Color>>(&self, color: C) {
|
||||||
let bg = color.into_platform_specific_color();
|
// @TODO: This is wrong.
|
||||||
|
let color = color.as_ref().cg_color();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let cg: id = msg_send![bg, CGColor];
|
|
||||||
let layer: id = msg_send![&*self.objc, layer];
|
let layer: id = msg_send![&*self.objc, layer];
|
||||||
let _: () = msg_send![layer, setBackgroundColor:cg];
|
let _: () = msg_send![layer, setBackgroundColor:color];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -274,11 +274,12 @@ impl<T> ListViewRow<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<C: AsRef<Color>>(&self, color: C) {
|
||||||
let mut objc = self.objc.borrow_mut();
|
let mut objc = self.objc.borrow_mut();
|
||||||
|
let color: id = color.as_ref().into();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
(&mut **objc).set_ivar(BACKGROUND_COLOR, color.as_ref().to_objc());
|
(&mut **objc).set_ivar(BACKGROUND_COLOR, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,9 @@
|
||||||
//! Certain lifecycle events are specific to certain platforms. Where this is the case, the
|
//! Certain lifecycle events are specific to certain platforms. Where this is the case, the
|
||||||
//! documentation makes every effort to note.
|
//! documentation makes every effort to note.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use objc_id::Id;
|
use objc_id::Id;
|
||||||
|
@ -51,7 +53,7 @@ mod class;
|
||||||
use class::register_app_class;
|
use class::register_app_class;
|
||||||
|
|
||||||
mod delegate;
|
mod delegate;
|
||||||
use delegate::{register_app_delegate_class};
|
use delegate::register_app_delegate_class;
|
||||||
|
|
||||||
mod enums;
|
mod enums;
|
||||||
pub use enums::*;
|
pub use enums::*;
|
||||||
|
@ -61,31 +63,6 @@ pub use traits::AppDelegate;
|
||||||
|
|
||||||
pub(crate) static APP_PTR: &str = "rstAppPtr";
|
pub(crate) static APP_PTR: &str = "rstAppPtr";
|
||||||
|
|
||||||
// Alright, so this... sucks.
|
|
||||||
//
|
|
||||||
// But let me explain.
|
|
||||||
//
|
|
||||||
// macOS only has one top level menu, and it's probably one of the old(est|er)
|
|
||||||
// parts of the system - there's still Carbon there, if you know where to look.
|
|
||||||
// We store our event handlers on the Rust side, and in most cases this works fine -
|
|
||||||
// we can enforce that the programmer should be retaining ownership and dropping
|
|
||||||
// when ready.
|
|
||||||
//
|
|
||||||
// We can't enforce this with NSMenu, as it takes ownership of the items and then
|
|
||||||
// lives in its own world. We want to mirror the same contract, somehow... so, again,
|
|
||||||
// we go back to the "one top level menu" bit.
|
|
||||||
//
|
|
||||||
// Yes, all this to say, this is a singleton that caches TargetActionHandler entries
|
|
||||||
// when menus are constructed. It's mostly 1:1 with the NSMenu, I think - and probably
|
|
||||||
// the only true singleton I want to see in this framework.
|
|
||||||
//
|
|
||||||
// Calls to App::set_menu() will reconfigure the contents of this Vec, so that's also
|
|
||||||
// the easiest way to remove things as necessary - just rebuild your menu. Trying to debug
|
|
||||||
// and/or predict NSMenu is a whole task that I'm not even sure is worth trying to do.
|
|
||||||
/*lazy_static! {
|
|
||||||
static ref MENU_ITEMS_HANDLER_CACHE: Arc<Mutex<Vec<TargetActionHandler>>> = Arc::new(Mutex::new(Vec::new()));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/// A handler to make some boilerplate less annoying.
|
/// A handler to make some boilerplate less annoying.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn shared_application<F: Fn(id)>(handler: F) {
|
fn shared_application<F: Fn(id)>(handler: F) {
|
||||||
|
@ -106,13 +83,26 @@ fn shared_application<F: Fn(id)>(handler: F) {
|
||||||
/// implement the `Dispatcher` trait to receive messages that you might dispatch from deeper in the
|
/// implement the `Dispatcher` trait to receive messages that you might dispatch from deeper in the
|
||||||
/// application.
|
/// application.
|
||||||
pub struct App<T = (), M = ()> {
|
pub struct App<T = (), M = ()> {
|
||||||
pub inner: Id<Object>,
|
pub objc: Id<Object>,
|
||||||
pub objc_delegate: Id<Object>,
|
pub objc_delegate: Id<Object>,
|
||||||
pub delegate: Box<T>,
|
pub delegate: Box<T>,
|
||||||
pub pool: AutoReleasePool,
|
pub pool: AutoReleasePool,
|
||||||
_message: std::marker::PhantomData<M>
|
_message: std::marker::PhantomData<M>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, M> fmt::Debug for App<T, M> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let delegate = format!("{:p}", self.delegate);
|
||||||
|
|
||||||
|
f.debug_struct("App")
|
||||||
|
.field("objc", &self.objc)
|
||||||
|
.field("objc_delegate", &self.objc_delegate)
|
||||||
|
.field("delegate", &delegate)
|
||||||
|
.field("pool", &self.pool)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> App<T> {
|
impl<T> App<T> {
|
||||||
/// Kicks off the NSRunLoop for the NSApplication instance. This blocks when called.
|
/// Kicks off the NSRunLoop for the NSApplication instance. This blocks when called.
|
||||||
/// If you're wondering where to go from here... you need an `AppDelegate` that implements
|
/// If you're wondering where to go from here... you need an `AppDelegate` that implements
|
||||||
|
@ -138,7 +128,7 @@ impl<T> App<T> where T: AppDelegate + 'static {
|
||||||
|
|
||||||
let pool = AutoReleasePool::new();
|
let pool = AutoReleasePool::new();
|
||||||
|
|
||||||
let inner = unsafe {
|
let objc = unsafe {
|
||||||
let app: id = msg_send![register_app_class(), sharedApplication];
|
let app: id = msg_send![register_app_class(), sharedApplication];
|
||||||
Id::from_ptr(app)
|
Id::from_ptr(app)
|
||||||
};
|
};
|
||||||
|
@ -150,15 +140,15 @@ impl<T> App<T> where T: AppDelegate + 'static {
|
||||||
let delegate: id = msg_send![delegate_class, new];
|
let delegate: id = msg_send![delegate_class, new];
|
||||||
let delegate_ptr: *const T = &*app_delegate;
|
let delegate_ptr: *const T = &*app_delegate;
|
||||||
(&mut *delegate).set_ivar(APP_PTR, delegate_ptr as usize);
|
(&mut *delegate).set_ivar(APP_PTR, delegate_ptr as usize);
|
||||||
let _: () = msg_send![&*inner, setDelegate:delegate];
|
let _: () = msg_send![&*objc, setDelegate:delegate];
|
||||||
Id::from_ptr(delegate)
|
Id::from_ptr(delegate)
|
||||||
};
|
};
|
||||||
|
|
||||||
App {
|
App {
|
||||||
objc_delegate: objc_delegate,
|
objc,
|
||||||
inner: inner,
|
objc_delegate,
|
||||||
delegate: app_delegate,
|
delegate: app_delegate,
|
||||||
pool: pool,
|
pool,
|
||||||
_message: std::marker::PhantomData
|
_message: std::marker::PhantomData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//! one level deep; this could change in the future but is fine for
|
//! one level deep; this could change in the future but is fine for
|
||||||
//! now.
|
//! now.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
use block::ConcreteBlock;
|
use block::ConcreteBlock;
|
||||||
|
@ -25,6 +26,16 @@ static BLOCK_PTR: &'static str = "cacaoMenuItemBlockPtr";
|
||||||
/// a better way to do this that doesn't require double-boxing, I'm all ears.
|
/// a better way to do this that doesn't require double-boxing, I'm all ears.
|
||||||
pub struct Action(Box<dyn Fn() + 'static>);
|
pub struct Action(Box<dyn Fn() + 'static>);
|
||||||
|
|
||||||
|
impl fmt::Debug for Action {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let ptr = format!("{:p}", self.0);
|
||||||
|
|
||||||
|
f.debug_struct("Action")
|
||||||
|
.field("fn", &ptr)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Internal method (shorthand) for generating `NSMenuItem` holders.
|
/// Internal method (shorthand) for generating `NSMenuItem` holders.
|
||||||
fn make_menu_item<S: AsRef<str>>(
|
fn make_menu_item<S: AsRef<str>>(
|
||||||
title: S,
|
title: S,
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
//!
|
//!
|
||||||
//! UNFORTUNATELY, this is a very old and janky API. So... yeah.
|
//! UNFORTUNATELY, this is a very old and janky API. So... yeah.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
@ -32,6 +34,7 @@ pub struct Toolbar<T = ()> {
|
||||||
/// The Objective-C runtime toolbar.
|
/// The Objective-C runtime toolbar.
|
||||||
pub objc: ShareId<Object>,
|
pub objc: ShareId<Object>,
|
||||||
|
|
||||||
|
/// A pointer to the underlying delegate.
|
||||||
pub objc_delegate: ShareId<Object>,
|
pub objc_delegate: ShareId<Object>,
|
||||||
|
|
||||||
/// The user supplied delegate.
|
/// The user supplied delegate.
|
||||||
|
@ -125,6 +128,22 @@ impl<T> Toolbar<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> fmt::Debug for Toolbar<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let delegate = match &self.delegate {
|
||||||
|
Some(d) => format!("Some({:p})", d),
|
||||||
|
None => "None".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
f.debug_struct("Toolbar")
|
||||||
|
.field("identifier", &self.identifier)
|
||||||
|
.field("objc", &self.objc)
|
||||||
|
.field("objc_delegate", &self.objc_delegate)
|
||||||
|
.field("delegate", &delegate)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Drop for Toolbar<T> {
|
impl<T> Drop for Toolbar<T> {
|
||||||
/// A bit of extra cleanup for the delegate system. If we have a non-`None` delegate, this is
|
/// A bit of extra cleanup for the delegate system. If we have a non-`None` delegate, this is
|
||||||
/// the OG Toolbar and should be cleaned up for any possible cyclical references.
|
/// the OG Toolbar and should be cleaned up for any possible cyclical references.
|
||||||
|
|
|
@ -27,9 +27,11 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc::{msg_send, sel, sel_impl};
|
use objc::{msg_send, sel, sel_impl};
|
||||||
use objc_id::ShareId;
|
use objc_id::Id;
|
||||||
|
|
||||||
use crate::foundation::{id, nil};
|
use crate::foundation::{id, nil};
|
||||||
use crate::utils::Controller;
|
use crate::utils::Controller;
|
||||||
|
@ -42,7 +44,7 @@ use class::register_window_controller_class;
|
||||||
/// provides some extra lifecycle methods.
|
/// provides some extra lifecycle methods.
|
||||||
pub struct WindowController<T> {
|
pub struct WindowController<T> {
|
||||||
/// A handler to the underlying `NSWindowController`.
|
/// A handler to the underlying `NSWindowController`.
|
||||||
pub objc: ShareId<Object>,
|
pub objc: Id<Object>,
|
||||||
|
|
||||||
/// The underlying `Window` that this controller wraps.
|
/// The underlying `Window` that this controller wraps.
|
||||||
pub window: Window<T>
|
pub window: Window<T>
|
||||||
|
@ -64,7 +66,7 @@ impl<T> WindowController<T> where T: WindowDelegate + 'static {
|
||||||
(&mut *controller).set_ivar(WINDOW_DELEGATE_PTR, ptr as usize);
|
(&mut *controller).set_ivar(WINDOW_DELEGATE_PTR, ptr as usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShareId::from_ptr(controller)
|
Id::from_ptr(controller)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(delegate) = &mut window.delegate {
|
if let Some(delegate) = &mut window.delegate {
|
||||||
|
@ -74,10 +76,7 @@ impl<T> WindowController<T> where T: WindowDelegate + 'static {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowController {
|
WindowController { objc, window }
|
||||||
objc: objc,
|
|
||||||
window: window
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a view, sets it as the content view controller for this window.
|
/// Given a view, sets it as the content view controller for this window.
|
||||||
|
@ -103,3 +102,11 @@ impl<T> WindowController<T> where T: WindowDelegate + 'static {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> fmt::Debug for WindowController<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("WindowController")
|
||||||
|
.field("objc", &self.objc)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use crate::foundation::{NSInteger, NSUInteger};
|
use crate::foundation::{NSInteger, NSUInteger};
|
||||||
|
|
||||||
/// Describes window styles that can be displayed.
|
/// Describes window styles that can be displayed.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum WindowStyle {
|
pub enum WindowStyle {
|
||||||
/// Window has no border. You generally do not want this.
|
/// Window has no border. You generally do not want this.
|
||||||
Borderless,
|
Borderless,
|
||||||
|
@ -80,6 +81,7 @@ impl From<&WindowStyle> for NSUInteger {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes whether a window shows a title or not.
|
/// Describes whether a window shows a title or not.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum TitleVisibility {
|
pub enum TitleVisibility {
|
||||||
/// Title is visible.
|
/// Title is visible.
|
||||||
Visible,
|
Visible,
|
||||||
|
|
|
@ -330,9 +330,11 @@ impl<T> Window<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the background color for the window. You generally don't want to do this often.
|
/// Sets the background color for the window. You generally don't want to do this often.
|
||||||
pub fn set_background_color(&self, color: Color) {
|
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||||
|
let color: id = color.as_ref().into();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![&*self.objc, setBackgroundColor:color.into_platform_specific_color()];
|
let _: () = msg_send![&*self.objc, setBackgroundColor:color];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ mod types;
|
||||||
pub use types::{PasteboardName, PasteboardType};
|
pub use types::{PasteboardName, PasteboardType};
|
||||||
|
|
||||||
/// Represents an `NSPasteboard`, enabling you to handle copy/paste/drag and drop.
|
/// Represents an `NSPasteboard`, enabling you to handle copy/paste/drag and drop.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Pasteboard(pub ShareId<Object>);
|
pub struct Pasteboard(pub ShareId<Object>);
|
||||||
|
|
||||||
impl Default for Pasteboard {
|
impl Default for Pasteboard {
|
||||||
|
|
|
@ -85,8 +85,8 @@ impl ProgressIndicator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProgressIndicator {
|
impl ProgressIndicator {
|
||||||
/// 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) {
|
||||||
let bg = color.into_platform_specific_color();
|
let bg = color.into_platform_specific_color();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -94,7 +94,7 @@ impl ProgressIndicator {
|
||||||
let layer: id = msg_send![&*self.objc, layer];
|
let layer: id = msg_send![&*self.objc, layer];
|
||||||
let _: () = msg_send![layer, setBackgroundColor:cg];
|
let _: () = msg_send![layer, setBackgroundColor:cg];
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/// Starts the animation for an indeterminate indicator.
|
/// Starts the animation for an indeterminate indicator.
|
||||||
pub fn start_animation(&self) {
|
pub fn start_animation(&self) {
|
||||||
|
|
|
@ -154,8 +154,6 @@ impl<T> ScrollView<T> where T: ScrollViewDelegate + 'static {
|
||||||
|
|
||||||
let view = allocate_view(register_scrollview_class_with_delegate::<T>);
|
let view = allocate_view(register_scrollview_class_with_delegate::<T>);
|
||||||
unsafe {
|
unsafe {
|
||||||
//let view: id = msg_send![register_view_class_with_delegate::<T>(), new];
|
|
||||||
//let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
|
||||||
let ptr: *const T = &*delegate;
|
let ptr: *const T = &*delegate;
|
||||||
(&mut *view).set_ivar(SCROLLVIEW_DELEGATE_PTR, ptr as usize);
|
(&mut *view).set_ivar(SCROLLVIEW_DELEGATE_PTR, ptr as usize);
|
||||||
};
|
};
|
||||||
|
@ -200,13 +198,13 @@ impl<T> ScrollView<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<C: AsRef<Color>>(&self, color: C) {
|
||||||
let bg = color.into_platform_specific_color();
|
// @TODO: This is wrong.
|
||||||
|
let color = color.as_ref().cg_color();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let cg: id = msg_send![bg, CGColor];
|
|
||||||
let layer: id = msg_send![&*self.objc, layer];
|
let layer: id = msg_send![&*self.objc, layer];
|
||||||
let _: () = msg_send![layer, setBackgroundColor:cg];
|
let _: () = msg_send![layer, setBackgroundColor:color];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
102
src/text/attributed_string.rs
Normal file
102
src/text/attributed_string.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
use std::{fmt, slice, str};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Deref, DerefMut, Range};
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
use core_foundation::base::CFRange;
|
||||||
|
|
||||||
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc_id::Id;
|
||||||
|
|
||||||
|
use crate::color::Color;
|
||||||
|
use crate::foundation::{id, to_bool, BOOL, YES, NO, NSString};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
static NSForegroundColorAttributeName: id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper around `NSMutableAttributedString`, which can be used for more complex text
|
||||||
|
/// rendering.
|
||||||
|
///
|
||||||
|
pub struct AttributedString(pub Id<Object>);
|
||||||
|
|
||||||
|
impl AttributedString {
|
||||||
|
/// Creates a blank AttributedString. Internally, this allocates an
|
||||||
|
/// `NSMutableAttributedString`, which is required for controls to make use of rich text.
|
||||||
|
pub fn new(value: &str) -> Self {
|
||||||
|
let text = NSString::no_copy(value);
|
||||||
|
|
||||||
|
Self(unsafe {
|
||||||
|
let alloc: id = msg_send![class!(NSMutableAttributedString), alloc];
|
||||||
|
Id::from_ptr(msg_send![alloc, initWithString:&*text])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a mutableCopy of a passed in `NSAttributedString` instance. This is mostly for
|
||||||
|
/// internal use, but kept available as part of the public API for the more adventurous types
|
||||||
|
/// who might need it.
|
||||||
|
pub fn wrap(value: id) -> Self {
|
||||||
|
Self(unsafe {
|
||||||
|
Id::from_ptr(msg_send![value, mutableCopy])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the text (foreground) color for the specified range.
|
||||||
|
pub fn set_text_color<C: AsRef<Color>>(&mut self, color: C, range: Range<isize>) {
|
||||||
|
let color: id = color.as_ref().into();
|
||||||
|
let range = CFRange::init(range.start, range.end);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![&*self.0, addAttribute:NSForegroundColorAttributeName
|
||||||
|
value:color
|
||||||
|
range:range
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AttributedString {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let string = NSString::from_retained(unsafe {
|
||||||
|
msg_send![&*self.0, string]
|
||||||
|
});
|
||||||
|
|
||||||
|
write!(f, "{}", string.to_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for AttributedString {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let string = NSString::from_retained(unsafe {
|
||||||
|
msg_send![&*self.0, string]
|
||||||
|
});
|
||||||
|
|
||||||
|
f.debug_struct("AttributedString")
|
||||||
|
.field("text", &string.to_str())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AttributedString> for id {
|
||||||
|
/// Consumes and returns the pointer to the underlying NSMutableAttributedString instance.
|
||||||
|
fn from(mut string: AttributedString) -> Self {
|
||||||
|
&mut *string.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for AttributedString {
|
||||||
|
type Target = Object;
|
||||||
|
|
||||||
|
/// Derefs to the underlying Objective-C Object.
|
||||||
|
fn deref(&self) -> &Object {
|
||||||
|
&*self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for AttributedString {
|
||||||
|
/// Derefs to the underlying Objective-C Object.
|
||||||
|
fn deref_mut(&mut self) -> &mut Object {
|
||||||
|
&mut *self.0
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,21 @@
|
||||||
|
|
||||||
use crate::foundation::{NSInteger, NSUInteger};
|
use crate::foundation::{NSInteger, NSUInteger};
|
||||||
|
|
||||||
|
/// Specifies how text should align for a supported control.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum TextAlign {
|
pub enum TextAlign {
|
||||||
|
/// Align text to the left.
|
||||||
Left,
|
Left,
|
||||||
|
|
||||||
|
/// Align text to the right.
|
||||||
Right,
|
Right,
|
||||||
|
|
||||||
|
/// Center-align text.
|
||||||
Center,
|
Center,
|
||||||
|
|
||||||
|
/// Justify text.
|
||||||
Justified,
|
Justified,
|
||||||
|
|
||||||
|
/// Natural.
|
||||||
Natural
|
Natural
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +32,7 @@ impl From<TextAlign> for NSInteger {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instructs text controls how to optimize line breaks.
|
/// Instructs text controls how to optimize line breaks.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum LineBreakMode {
|
pub enum LineBreakMode {
|
||||||
/// Wrap at word boundaries (the default)
|
/// Wrap at word boundaries (the default)
|
||||||
WrapWords,
|
WrapWords,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! Implements `Font`, a wrapper around `NSFont` on macOS and `UIFont` on iOS.
|
//! Implements `Font`, a wrapper around `NSFont` on macOS and `UIFont` on iOS.
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use core_graphics::base::CGFloat;
|
use core_graphics::base::CGFloat;
|
||||||
|
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
@ -8,37 +10,55 @@ use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use crate::foundation::{id, nil, YES, NO, NSArray, NSString};
|
use crate::foundation::{id, nil, YES, NO, NSArray, NSString};
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// A `Font` can be constructed and applied to supported controls to control things like text
|
||||||
pub struct Font {
|
/// appearance and size.
|
||||||
pub objc: ShareId<Object>
|
#[derive(Clone, Debug)]
|
||||||
}
|
pub struct Font(pub ShareId<Object>);
|
||||||
|
|
||||||
impl Default for Font {
|
impl Default for Font {
|
||||||
|
/// Returns the default `labelFont` on macOS.
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Font {
|
Font(unsafe {
|
||||||
objc: unsafe {
|
let cls = class!(NSFont);
|
||||||
let cls = class!(NSFont);
|
let default_size: id = msg_send![cls, labelFontSize];
|
||||||
let default_size: id = msg_send![cls, labelFontSize];
|
ShareId::from_ptr(msg_send![cls, labelFontOfSize:default_size])
|
||||||
ShareId::from_ptr(msg_send![cls, labelFontOfSize:default_size])
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Font {
|
impl Font {
|
||||||
pub fn system(size: CGFloat) -> Self {
|
/// Creates and returns a default system font at the specified size.
|
||||||
Font {
|
pub fn system(size: f64) -> Self {
|
||||||
objc: unsafe {
|
let size = size as CGFloat;
|
||||||
ShareId::from_ptr(msg_send![class!(NSFont), systemFontOfSize:size])
|
|
||||||
}
|
Font(unsafe {
|
||||||
}
|
ShareId::from_ptr(msg_send![class!(NSFont), systemFontOfSize:size])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bold_system(size: CGFloat) -> Self {
|
/// Creates and returns a default bold system font at the specified size.
|
||||||
Font {
|
pub fn bold_system(size: f64) -> Self {
|
||||||
objc: unsafe {
|
let size = size as CGFloat;
|
||||||
ShareId::from_ptr(msg_send![class!(NSFont), boldSystemFontOfSize:size])
|
|
||||||
}
|
Font(unsafe {
|
||||||
}
|
ShareId::from_ptr(msg_send![class!(NSFont), boldSystemFontOfSize:size])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Font {
|
||||||
|
type Target = Object;
|
||||||
|
|
||||||
|
/// Derefs to the underlying Objective-C Object.
|
||||||
|
fn deref(&self) -> &Object {
|
||||||
|
&*self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<Font> for Font {
|
||||||
|
/// Provided to make passing `Font` types around less of a headache.
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &Font {
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,19 +203,19 @@ impl<T> Label<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<C: AsRef<Color>>(&self, color: C) {
|
||||||
let bg = color.into_platform_specific_color();
|
// @TODO: This is wrong.
|
||||||
|
let color = color.as_ref().cg_color();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let cg: id = msg_send![bg, CGColor];
|
|
||||||
let layer: id = msg_send![&*self.objc, layer];
|
let layer: id = msg_send![&*self.objc, layer];
|
||||||
let _: () = msg_send![layer, setBackgroundColor:cg];
|
let _: () = msg_send![layer, setBackgroundColor:color];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call this to set the color of the text.
|
/// Call this to set the color of the text.
|
||||||
pub fn set_text_color(&self, color: Color) {
|
pub fn set_text_color<C: AsRef<Color>>(&self, color: C) {
|
||||||
let color = color.into_platform_specific_color();
|
let color: id = color.as_ref().into();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![&*self.objc, setTextColor:color];
|
let _: () = msg_send![&*self.objc, setTextColor:color];
|
||||||
|
@ -240,6 +240,7 @@ impl<T> Label<T> {
|
||||||
s.to_string()
|
s.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the text alignment for this label.
|
||||||
pub fn set_text_alignment(&self, alignment: TextAlign) {
|
pub fn set_text_alignment(&self, alignment: TextAlign) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let alignment: NSInteger = alignment.into();
|
let alignment: NSInteger = alignment.into();
|
||||||
|
@ -247,12 +248,18 @@ impl<T> Label<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_font(&self, font: &Font) {
|
/// Sets the font for this label.
|
||||||
|
pub fn set_font<F: AsRef<Font>>(&self, font: F) {
|
||||||
|
// This clone is here to ensure there's no oddities with retain counts on the underlying
|
||||||
|
// font object - it seems like it can be optimized away otherwise.
|
||||||
|
let font = font.as_ref().clone();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![&*self.objc, setFont:&*font.objc];
|
let _: () = msg_send![&*self.objc, setFont:&*font];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set whether this is hidden or not.
|
||||||
pub fn set_hidden(&self, hidden: bool) {
|
pub fn set_hidden(&self, hidden: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![&*self.objc, setHidden:match hidden {
|
let _: () = msg_send![&*self.objc, setHidden:match hidden {
|
||||||
|
@ -262,6 +269,7 @@ impl<T> Label<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the line break mode for this label.
|
||||||
pub fn set_line_break_mode(&self, mode: LineBreakMode) {
|
pub fn set_line_break_mode(&self, mode: LineBreakMode) {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
//! The `text` module encompasses various widgets for rendering and interacting
|
//! The `text` module encompasses various widgets for rendering and interacting
|
||||||
//! with text.
|
//! with text.
|
||||||
|
|
||||||
pub mod label;
|
mod attributed_string;
|
||||||
|
pub use attributed_string::AttributedString;
|
||||||
|
|
||||||
|
mod label;
|
||||||
pub use label::Label;
|
pub use label::Label;
|
||||||
|
|
||||||
pub mod enums;
|
mod enums;
|
||||||
pub use enums::{LineBreakMode, TextAlign};
|
pub use enums::{LineBreakMode, TextAlign};
|
||||||
|
|
||||||
pub mod font;
|
mod font;
|
||||||
pub use font::Font;
|
pub use font::Font;
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
//! A module wrapping `NSUserActivity`.
|
//! A module wrapping `NSUserActivity`.
|
||||||
|
//!
|
||||||
|
//! This is primarily used in handling app handoff between devices.
|
||||||
|
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::foundation::id;
|
use crate::foundation::id;
|
||||||
|
|
||||||
/// Represents an `NSUserActivity`, which acts as a lightweight method to capture the state of your
|
/// Represents an `NSUserActivity`, which acts as a lightweight method to capture
|
||||||
/// app.
|
/// the state of your app.
|
||||||
pub struct UserActivity {
|
#[derive(Debug)]
|
||||||
pub inner: ShareId<Object>
|
pub struct UserActivity(pub ShareId<Object>);
|
||||||
}
|
|
||||||
|
|
||||||
impl UserActivity {
|
impl UserActivity {
|
||||||
/// An internal method for wrapping a system-provided activity.
|
/// An internal method for wrapping a system-provided activity.
|
||||||
pub(crate) fn with_inner(object: id) -> Self {
|
pub(crate) fn with_inner(object: id) -> Self {
|
||||||
UserActivity {
|
UserActivity(unsafe {
|
||||||
inner: unsafe { ShareId::from_ptr(object) }
|
ShareId::from_ptr(object)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,36 +12,12 @@ use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::foundation::{id, BOOL, YES, NO};
|
use crate::foundation::{id, BOOL, YES, NO};
|
||||||
|
|
||||||
pub mod os {
|
pub mod os;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use os_info::Version;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref OS_VERSION: os_info::Info = os_info::get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// In rare cases we need to check whether something is a specific version of macOS. This is a
|
|
||||||
/// runtime check thhat returns a boolean indicating whether the current version is a minimum target.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_minimum_version(minimum_major: u64) -> bool {
|
|
||||||
match OS_VERSION.version() {
|
|
||||||
Version::Semantic(os_major, _, _) => { *os_major >= minimum_major },
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// In rare cases we need to check whether something is a specific version of macOS. This is a
|
|
||||||
/// runtime check thhat returns a boolean indicating whether the current version is a minimum target.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_minimum_semversion(major: u64, minor: u64, patch: u64) -> bool {
|
|
||||||
let target = Version::Semantic(major, minor, patch);
|
|
||||||
OS_VERSION.version() > &target
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A generic trait that's used throughout multiple different controls in this framework - acts as
|
/// A generic trait that's used throughout multiple different controls in this framework - acts as
|
||||||
/// a guard for whether something is a (View|etc)Controller. Only needs to return the backing node.
|
/// a guard for whether something is a (View|Window|etc)Controller.
|
||||||
pub trait Controller {
|
pub trait Controller {
|
||||||
|
/// Returns the underlying Objective-C object.
|
||||||
fn get_backing_node(&self) -> ShareId<Object>;
|
fn get_backing_node(&self) -> ShareId<Object>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +45,7 @@ pub fn load<'a, T>(this: &'a Object, ptr_name: &str) -> &'a T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Asynchronously execute a callback on the main thread via Grand Central Dispatch.
|
||||||
pub fn async_main_thread<F>(method: F)
|
pub fn async_main_thread<F>(method: F)
|
||||||
where
|
where
|
||||||
F: Fn() + Send + 'static
|
F: Fn() + Send + 'static
|
||||||
|
@ -77,6 +54,7 @@ where
|
||||||
queue.exec_async(method);
|
queue.exec_async(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Synchronously execute a callback on the main thread via Grand Central Dispatch.
|
||||||
pub fn sync_main_thread<F>(method: F)
|
pub fn sync_main_thread<F>(method: F)
|
||||||
where
|
where
|
||||||
F: Fn() + Send + 'static
|
F: Fn() + Send + 'static
|
||||||
|
@ -90,21 +68,27 @@ where
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
pub struct CGSize {
|
pub struct CGSize {
|
||||||
|
/// The width of this size.
|
||||||
pub width: CGFloat,
|
pub width: CGFloat,
|
||||||
|
|
||||||
|
/// The height of this size.
|
||||||
pub height: CGFloat,
|
pub height: CGFloat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CGSize {
|
impl CGSize {
|
||||||
|
/// Create and return a new `CGSize`.
|
||||||
pub fn new(width: CGFloat, height: CGFloat) -> Self {
|
pub fn new(width: CGFloat, height: CGFloat) -> Self {
|
||||||
CGSize { width, height }
|
CGSize { width, height }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create and return a `CGSizeZero` equivalent.
|
||||||
pub fn zero() -> Self {
|
pub fn zero() -> Self {
|
||||||
CGSize { width: 0., height: 0. }
|
CGSize { width: 0., height: 0. }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Encode for CGSize {
|
unsafe impl Encode for CGSize {
|
||||||
|
/// Adds support for CGSize Objective-C encoding.
|
||||||
fn encode() -> Encoding {
|
fn encode() -> Encoding {
|
||||||
let encoding = format!("{{CGSize={}{}}}",
|
let encoding = format!("{{CGSize={}{}}}",
|
||||||
CGFloat::encode().as_str(),
|
CGFloat::encode().as_str(),
|
27
src/utils/os.rs
Normal file
27
src/utils/os.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
//! Helper methods for OS version checking.
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use os_info::Version;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
/// A cached struct containing OS version for runtime checks.
|
||||||
|
pub static ref OS_VERSION: os_info::Info = os_info::get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In rare cases we need to check whether something is a specific version of macOS. This is a
|
||||||
|
/// runtime check thhat returns a boolean indicating whether the current version is a minimum target.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_minimum_version(minimum_major: u64) -> bool {
|
||||||
|
match OS_VERSION.version() {
|
||||||
|
Version::Semantic(os_major, _, _) => { *os_major >= minimum_major },
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In rare cases we need to check whether something is a specific version of macOS. This is a
|
||||||
|
/// runtime check thhat returns a boolean indicating whether the current version is a minimum target.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_minimum_semversion(major: u64, minor: u64, patch: u64) -> bool {
|
||||||
|
let target = Version::Semantic(major, minor, patch);
|
||||||
|
OS_VERSION.version() > &target
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ use objc::runtime::Object;
|
||||||
use objc::{msg_send, sel, sel_impl};
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use crate::foundation::id;
|
use crate::foundation::id;
|
||||||
use crate::layout::{Layout};
|
use crate::layout::Layout;
|
||||||
use crate::view::{VIEW_DELEGATE_PTR, View, ViewDelegate};
|
use crate::view::{VIEW_DELEGATE_PTR, View, ViewDelegate};
|
||||||
use crate::utils::Controller;
|
use crate::utils::Controller;
|
||||||
|
|
||||||
|
@ -19,13 +19,38 @@ mod ios;
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
use ios::register_view_controller_class;
|
use ios::register_view_controller_class;
|
||||||
|
|
||||||
|
/// A `ViewController` is a wrapper around `NSViewController` on macOS, and `UIViewController` on
|
||||||
|
/// iOS and tvOS.
|
||||||
|
///
|
||||||
|
/// This type is interchangeable with a standard `View<T>`, in that using this simply forwards
|
||||||
|
/// standard view controller lifecycle methods onto your `ViewDelegate`. You would use this if you
|
||||||
|
/// need to be notified of _when_ something is going to be used (e.g, for lifecycle event-based
|
||||||
|
/// cleanup routines, or something).
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// struct ContentViewDelegate;
|
||||||
|
///
|
||||||
|
/// impl ViewDelegate for ContentViewDelegate {
|
||||||
|
/// fn will_appear(&self, animated: bool) {
|
||||||
|
/// println!("This controller is about to appear!");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ViewController<T> {
|
pub struct ViewController<T> {
|
||||||
|
/// The underlying Objective-C pointer.
|
||||||
pub objc: ShareId<Object>,
|
pub objc: ShareId<Object>,
|
||||||
|
|
||||||
|
/// The underlying View that we manage.
|
||||||
pub view: View<T>
|
pub view: View<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ViewController<T> where T: ViewDelegate + 'static {
|
impl<T> ViewController<T>
|
||||||
|
where
|
||||||
|
T: ViewDelegate + 'static
|
||||||
|
{
|
||||||
|
/// Creates and returns a new `ViewController` with the provided `delegate`.
|
||||||
pub fn new(delegate: T) -> Self {
|
pub fn new(delegate: T) -> Self {
|
||||||
let class = register_view_controller_class::<T>(&delegate);
|
let class = register_view_controller_class::<T>(&delegate);
|
||||||
let view = View::with(delegate);
|
let view = View::with(delegate);
|
||||||
|
@ -43,10 +68,7 @@ impl<T> ViewController<T> where T: ViewDelegate + 'static {
|
||||||
ShareId::from_ptr(vc)
|
ShareId::from_ptr(vc)
|
||||||
};
|
};
|
||||||
|
|
||||||
ViewController {
|
ViewController { objc, view }
|
||||||
objc: objc,
|
|
||||||
view: view
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -209,9 +209,10 @@ impl<T> View<T> {
|
||||||
/// 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<C: AsRef<Color>>(&self, color: C) {
|
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||||
let mut objc = self.objc.borrow_mut();
|
let mut objc = self.objc.borrow_mut();
|
||||||
|
let color: id = color.as_ref().into();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
(&mut **objc).set_ivar(BACKGROUND_COLOR, color.as_ref().to_objc());
|
(&mut **objc).set_ivar(BACKGROUND_COLOR, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,12 @@
|
||||||
use crate::dragdrop::{DragInfo, DragOperation};
|
use crate::dragdrop::{DragInfo, DragOperation};
|
||||||
use crate::view::View;
|
use crate::view::View;
|
||||||
|
|
||||||
|
/// This trait can be used for implementing custom View behavior. You implement this trait on your
|
||||||
|
/// struct, and wrap your struct in a `View` or `ViewController`. The view or controller then
|
||||||
|
/// handles interfacing between your struct and system events.
|
||||||
|
///
|
||||||
|
/// It winds up feeling to subclassing, without the ability to subclass multiple levels deep and
|
||||||
|
/// get ultra confusing.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub trait ViewDelegate {
|
pub trait ViewDelegate {
|
||||||
/// Used to cache subclass creations on the Objective-C side.
|
/// Used to cache subclass creations on the Objective-C side.
|
||||||
|
@ -33,20 +39,24 @@ pub trait ViewDelegate {
|
||||||
/// Called when this has been removed from the view heirarchy.
|
/// Called when this has been removed from the view heirarchy.
|
||||||
fn did_disappear(&self, animated: bool) {}
|
fn did_disappear(&self, animated: bool) {}
|
||||||
|
|
||||||
/// Invoked when the dragged image enters destination bounds or frame; returns dragging operation to perform.
|
/// Invoked when the dragged image enters destination bounds or frame; returns dragging
|
||||||
|
/// operation to perform.
|
||||||
fn dragging_entered(&self, info: DragInfo) -> DragOperation { DragOperation::None }
|
fn dragging_entered(&self, info: DragInfo) -> DragOperation { DragOperation::None }
|
||||||
|
|
||||||
/// Invoked when the image is released, allowing the receiver to agree to or refuse drag operation.
|
/// Invoked when the image is released, allowing the receiver to agree to or refuse
|
||||||
|
/// drag operation.
|
||||||
fn prepare_for_drag_operation(&self, info: DragInfo) -> bool { false }
|
fn prepare_for_drag_operation(&self, info: DragInfo) -> bool { false }
|
||||||
|
|
||||||
/// Invoked after the released image has been removed from the screen, signaling the receiver to import the pasteboard data.
|
/// Invoked after the released image has been removed from the screen, signaling the
|
||||||
|
/// receiver to import the pasteboard data.
|
||||||
fn perform_drag_operation(&self, info: DragInfo) -> bool { false }
|
fn perform_drag_operation(&self, info: DragInfo) -> bool { false }
|
||||||
|
|
||||||
/// Invoked when the dragging operation is complete, signaling the receiver to perform any necessary clean-up.
|
/// Invoked when the dragging operation is complete, signaling the receiver to perform
|
||||||
|
/// any necessary clean-up.
|
||||||
fn conclude_drag_operation(&self, info: DragInfo) {}
|
fn conclude_drag_operation(&self, info: DragInfo) {}
|
||||||
|
|
||||||
/// Invoked when the dragged image exits the destination’s bounds rectangle (in the case of a view) or its frame
|
/// Invoked when the dragged image exits the destination’s bounds rectangle (in the case
|
||||||
/// rectangle (in the case of a window object).
|
/// of a view) or its frame rectangle (in the case of a window object).
|
||||||
fn dragging_exited(&self, info: DragInfo) {}
|
fn dragging_exited(&self, info: DragInfo) {}
|
||||||
|
|
||||||
//fn perform_key_equivalent(&self, event: Event) -> bool { false }
|
//fn perform_key_equivalent(&self, event: Event) -> bool { false }
|
||||||
|
|
Loading…
Reference in a new issue