iOS works again, lol.

- Corrects `feature` -> `target_os` checks.
- Updates the old iOS scene delegate pieces to use the new class
  structure.
- Bundles in an iOS demo app.
- Blocks off most things that should not even attempt to compile for
  iOS.
This commit is contained in:
Ryan McGrath 2021-04-15 17:13:59 -07:00
parent 5f2f1f21b0
commit f558f8e24d
No known key found for this signature in database
GPG key ID: DA6CBD9233593DEA
22 changed files with 272 additions and 114 deletions

View file

@ -31,12 +31,17 @@ url = "2.1.1"
eval = "0.4" eval = "0.4"
[features] [features]
default = ["macos"] default = []
cloudkit = [] cloudkit = []
ios = []
macos = []
color_fallbacks = [] color_fallbacks = []
quicklook = [] quicklook = []
user-notifications = ["uuid"] user-notifications = ["uuid"]
webview = [] webview = []
webview-downloading-macos = [] webview-downloading-macos = []
[package.metadata.bundle.example.ios-beta]
name = "ios-beta"
identifier = "com.cacao.ios-test"
category = "Developer Tool"
short_description = "An example Cacao iOS app."
long_description = "An example Cacao iOS app."

96
examples/ios-beta/main.rs Normal file
View file

@ -0,0 +1,96 @@
use std::sync::RwLock;
use cacao::ios::{
App, AppDelegate, Scene, SceneConfig, SceneSession,
SceneConnectionOptions, WindowSceneDelegate, Window
};
use cacao::color::Color;
use cacao::layout::{Layout, LayoutConstraint};
use cacao::view::{View, ViewController, ViewDelegate};
#[derive(Default)]
struct TestApp;
impl AppDelegate for TestApp {
fn config_for_scene_session(&self, session: SceneSession, _options: SceneConnectionOptions) -> SceneConfig {
SceneConfig::new("Default Configuration", session.role())
}
}
#[derive(Default)]
pub struct RootView {
pub red: View,
pub green: View,
pub blue: View
}
impl ViewDelegate for RootView {
const NAME: &'static str = "RootView";
fn did_load(&mut self, view: View) {
self.red.set_background_color(Color::SystemRed);
self.red.layer.set_corner_radius(16.);
view.add_subview(&self.red);
self.green.set_background_color(Color::SystemGreen);
view.add_subview(&self.green);
self.blue.set_background_color(Color::SystemBlue);
view.add_subview(&self.blue);
LayoutConstraint::activate(&[
self.red.top.constraint_equal_to(&view.top).offset(16.),
self.red.leading.constraint_equal_to(&view.leading).offset(16.),
self.red.trailing.constraint_equal_to(&view.trailing).offset(-16.),
self.red.height.constraint_equal_to_constant(100.),
self.green.top.constraint_equal_to(&self.red.bottom).offset(16.),
self.green.leading.constraint_equal_to(&view.leading).offset(16.),
self.green.trailing.constraint_equal_to(&view.trailing).offset(-16.),
self.green.height.constraint_equal_to_constant(120.),
self.blue.top.constraint_equal_to(&self.green.bottom).offset(16.),
self.blue.leading.constraint_equal_to(&view.leading).offset(16.),
self.blue.trailing.constraint_equal_to(&view.trailing).offset(-16.),
self.blue.bottom.constraint_equal_to(&view.bottom).offset(-16.)
]);
}
}
#[derive(Default)]
pub struct WindowScene {
pub window: RwLock<Option<Window>>,
pub root_view_controller: RwLock<Option<ViewController<RootView>>>
}
impl WindowSceneDelegate for WindowScene {
fn will_connect(
&self,
scene: Scene,
session: SceneSession,
options: SceneConnectionOptions
) {
let bounds = scene.get_bounds();
let mut window = Window::new(bounds);
window.set_window_scene(scene);
let root_view_controller = ViewController::new(RootView::default());
window.set_root_view_controller(&root_view_controller);
window.show();
{
let mut w = self.window.write().unwrap();
*w = Some(window);
let mut vc = self.root_view_controller.write().unwrap();
*vc = Some(root_view_controller);
}
}
}
fn main() {
App::new(TestApp::default(), || {
Box::new(WindowScene::default())
}).run();
}

View file

@ -0,0 +1,14 @@
# Cacao iOS Support
This, unlike the macOS side of things, is much more alpha-quality. It does work, though - and this example will likely end up being a "kitchen sink" to figure things out with.
## To run
Since this needs to run in an iOS simulator or on a device, you can't run it like a typical example. Follow the instructions below to give it a go:
- Start a simulator (Simulator.app).
- `cargo install cargo-bundle`
- `cargo bundle --example ios-beta --target x86_64-apple-ios`
- `xcrun simctl install booted target/x86_64-apple-ios/debug/examples/bundle/ios/cacao-ios-test.app`
- `xcrun simctl launch --console booted com.cacao.ios-test`
## Current Support
Not much, but the basics of the scene delegate system work, along with view support, colors, and layout. Play around!

View file

@ -2,7 +2,7 @@ use crate::foundation::NSUInteger;
/// Represents a bezel style for a button. This is a macOS-specific control, and has no effect /// Represents a bezel style for a button. This is a macOS-specific control, and has no effect
/// under iOS or tvOS. /// under iOS or tvOS.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
#[derive(Debug)] #[derive(Debug)]
pub enum BezelStyle { pub enum BezelStyle {
/// A standard circular button. /// A standard circular button.
@ -49,7 +49,7 @@ pub enum BezelStyle {
Unknown(NSUInteger) Unknown(NSUInteger)
} }
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
impl From<BezelStyle> for NSUInteger { impl From<BezelStyle> for NSUInteger {
fn from(style: BezelStyle) -> Self { fn from(style: BezelStyle) -> Self {
match style { match style {
@ -71,7 +71,7 @@ impl From<BezelStyle> for NSUInteger {
} }
} }
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
impl From<NSUInteger> for BezelStyle { impl From<NSUInteger> for BezelStyle {
fn from(i: NSUInteger) -> Self { fn from(i: NSUInteger) -> Self {
match i { match i {

View file

@ -36,7 +36,7 @@ use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension}
use crate::text::{AttributedString, Font}; use crate::text::{AttributedString, Font};
use crate::utils::{load, properties::ObjcProperty}; use crate::utils::{load, properties::ObjcProperty};
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
use crate::macos::FocusRingType; use crate::macos::FocusRingType;
mod enums; mod enums;
@ -145,7 +145,7 @@ impl Button {
} }
/// Sets the bezel style for this button. Only supported on macOS. /// Sets the bezel style for this button. Only supported on macOS.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
pub fn set_bezel_style(&self, bezel_style: BezelStyle) { pub fn set_bezel_style(&self, bezel_style: BezelStyle) {
let style: NSUInteger = bezel_style.into(); let style: NSUInteger = bezel_style.into();
@ -167,7 +167,7 @@ impl Button {
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) { pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
let color: id = color.as_ref().into(); let color: id = color.as_ref().into();
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
self.objc.with_mut(|obj| unsafe { self.objc.with_mut(|obj| unsafe {
let cell: id = msg_send![obj, cell]; let cell: id = msg_send![obj, cell];
let _: () = msg_send![cell, setBackgroundColor:color]; let _: () = msg_send![cell, setBackgroundColor:color];
@ -188,7 +188,7 @@ impl Button {
/// ///
/// On macOS, this is done by way of an `AttributedString` under the hood. /// On macOS, this is done by way of an `AttributedString` under the hood.
pub fn set_text_color<C: AsRef<Color>>(&self, color: C) { pub fn set_text_color<C: AsRef<Color>>(&self, color: C) {
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
self.objc.with_mut(move |obj| unsafe { self.objc.with_mut(move |obj| unsafe {
let text: id = msg_send![obj, attributedTitle]; let text: id = msg_send![obj, attributedTitle];
let len: isize = msg_send![text, length]; let len: isize = msg_send![text, length];
@ -202,7 +202,7 @@ impl Button {
// @TODO: Figure out how to handle oddities like this. // @TODO: Figure out how to handle oddities like this.
/// For buttons on macOS, one might need to disable the border. This does that. /// For buttons on macOS, one might need to disable the border. This does that.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
pub fn set_bordered(&self, is_bordered: bool) { pub fn set_bordered(&self, is_bordered: bool) {
self.objc.with_mut(|obj| unsafe { self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![obj, setBordered:match is_bordered { let _: () = msg_send![obj, setBordered:match is_bordered {
@ -224,7 +224,7 @@ impl Button {
/// Sets how the control should draw a focus ring when a user is focused on it. /// Sets how the control should draw a focus ring when a user is focused on it.
/// ///
/// This is a macOS-only method. /// This is a macOS-only method.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
pub fn set_focus_ring_type(&self, focus_ring_type: FocusRingType) { pub fn set_focus_ring_type(&self, focus_ring_type: FocusRingType) {
let ring_type: NSUInteger = focus_ring_type.into(); let ring_type: NSUInteger = focus_ring_type.into();

View file

@ -10,7 +10,7 @@
/// ///
/// The goal here is to make sure that this can't reasonably break on OS's, as `Color` is kind of /// The goal here is to make sure that this can't reasonably break on OS's, as `Color` is kind of
/// an important piece. It's not on the framework to make your app look good, though. To enable /// an important piece. It's not on the framework to make your app look good, though. To enable
/// fallbacks, specify the `color_fallbacks` feature in your `Cargo.toml`. /// fallbacks, specify the `color_fallbacks` target_os in your `Cargo.toml`.
/// ///
/// @TODO: bundle iOS/tvOS support. /// @TODO: bundle iOS/tvOS support.
@ -26,10 +26,10 @@ use objc_id::Id;
use crate::foundation::id; use crate::foundation::id;
use crate::utils::os; use crate::utils::os;
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
mod macos_dynamic_color; mod macos_dynamic_color;
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
use macos_dynamic_color::{ use macos_dynamic_color::{
AQUA_LIGHT_COLOR_NORMAL_CONTRAST, AQUA_LIGHT_COLOR_HIGH_CONTRAST, AQUA_LIGHT_COLOR_NORMAL_CONTRAST, AQUA_LIGHT_COLOR_HIGH_CONTRAST,
AQUA_DARK_COLOR_NORMAL_CONTRAST, AQUA_DARK_COLOR_HIGH_CONTRAST AQUA_DARK_COLOR_NORMAL_CONTRAST, AQUA_DARK_COLOR_HIGH_CONTRAST
@ -230,11 +230,11 @@ pub enum Color {
LightText, LightText,
/// The background color for a given window in the system theme. /// The background color for a given window in the system theme.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
MacOSWindowBackgroundColor, MacOSWindowBackgroundColor,
/// The background color that should appear under a page per the system theme. /// The background color that should appear under a page per the system theme.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
MacOSUnderPageBackgroundColor MacOSUnderPageBackgroundColor
} }
@ -247,15 +247,12 @@ impl Color {
let b = blue as CGFloat / 255.0; let b = blue as CGFloat / 255.0;
let a = alpha as CGFloat / 255.0; let a = alpha as CGFloat / 255.0;
#[cfg(feature = "macos")]
let color = class!(NSColor);
#[cfg(feature = "ios")]
let color = class!(UIColor);
Color::Custom(Arc::new(RwLock::new(unsafe { Color::Custom(Arc::new(RwLock::new(unsafe {
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
Id::from_ptr(msg_send![color, colorWithCalibratedRed:r green:g blue:b alpha:a]) { Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedRed:r green:g blue:b alpha:a]) }
#[cfg(target_os = "ios")]
{ Id::from_ptr(msg_send![class!(UIColor), colorWithRed:r green:g blue:b alpha:a]) }
}))) })))
} }
@ -273,15 +270,12 @@ impl Color {
let b = brightness as CGFloat / 255.0; let b = brightness as CGFloat / 255.0;
let a = alpha as CGFloat / 255.0; let a = alpha as CGFloat / 255.0;
#[cfg(feature = "macos")]
let color = class!(NSColor);
#[cfg(feature = "ios")]
let color = class!(UIColor);
Color::Custom(Arc::new(RwLock::new(unsafe { Color::Custom(Arc::new(RwLock::new(unsafe {
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
Id::from_ptr(msg_send![color, colorWithCalibratedHue:h saturation:s brightness:b alpha:a]) { Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedHue:h saturation:s brightness:b alpha:a]) }
#[cfg(target_os = "ios")]
{ Id::from_ptr(msg_send![class!(UIColor), colorWithHue:h saturation:s brightness:b alpha:a]) }
}))) })))
} }
@ -294,15 +288,12 @@ impl Color {
/// Creates and returns a white color with the specified level or intensity, along with the /// Creates and returns a white color with the specified level or intensity, along with the
/// specified alpha. /// specified alpha.
pub fn white_alpha(level: CGFloat, alpha: CGFloat) -> Self { pub fn white_alpha(level: CGFloat, alpha: CGFloat) -> Self {
#[cfg(feature = "macos")]
let color = class!(NSColor);
#[cfg(feature = "ios")]
let color = class!(UIColor);
Color::Custom(Arc::new(RwLock::new(unsafe { Color::Custom(Arc::new(RwLock::new(unsafe {
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
Id::from_ptr(msg_send![color, colorWithCalibratedWhite:level alpha:alpha]) { Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedWhite:level alpha:alpha]) }
#[cfg(target_os = "ios")]
{ Id::from_ptr(msg_send![class!(UIColor), colorWithWhite:level alpha:alpha]) }
}))) })))
} }
@ -336,6 +327,7 @@ impl Color {
/// "default" or "light" color. /// "default" or "light" color.
/// ///
/// Returning a dynamic color in your handler is unsupported and may panic. /// Returning a dynamic color in your handler is unsupported and may panic.
#[cfg(target_os = "macos")]
pub fn dynamic<F>(handler: F) -> Self pub fn dynamic<F>(handler: F) -> Self
where where
F: Fn(Style) -> Color + 'static F: Fn(Style) -> Color + 'static
@ -345,7 +337,7 @@ impl Color {
// not entirely clear on how expensive the dynamic allocation would be pre-10.15/11.0 and // 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 // am happy to do this for now and let someone who needs true dynamic allocation look into
// it and PR it. // it and PR it.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
Color::Custom(Arc::new(RwLock::new(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];
@ -429,7 +421,7 @@ impl From<&Color> for id {
/// 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(target_os = "macos")]
{ {
#[cfg(feature = "color-fallbacks")] #[cfg(feature = "color-fallbacks")]
if os::minimum_semversion(10, 10, 0) { if os::minimum_semversion(10, 10, 0) {
@ -441,6 +433,11 @@ macro_rules! system_color_with_fallback {
#[cfg(not(feature = "color-fallbacks"))] #[cfg(not(feature = "color-fallbacks"))]
msg_send![$class, $color] msg_send![$class, $color]
} }
#[cfg(target_os = "ios")]
{
msg_send![$class, $color]
}
}) })
} }
@ -453,10 +450,10 @@ macro_rules! system_color_with_fallback {
/// The goal here is to make sure that this can't reasonably break on OS's, as `Color` is kind of /// The goal here is to make sure that this can't reasonably break on OS's, as `Color` is kind of
/// an important piece. It's not on the framework to make your app look good, though. /// an important piece. It's not on the framework to make your app look good, though.
unsafe fn to_objc(obj: &Color) -> id { unsafe fn to_objc(obj: &Color) -> id {
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
let color = class!(NSColor); let color = class!(NSColor);
#[cfg(feature = "ios")] #[cfg(target_os = "ios")]
let color = class!(UIColor); let color = class!(UIColor);
match obj { match obj {
@ -503,10 +500,10 @@ unsafe fn to_objc(obj: &Color) -> id {
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),
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
Color::MacOSWindowBackgroundColor => system_color_with_fallback!(color, windowBackgroundColor, clearColor), Color::MacOSWindowBackgroundColor => system_color_with_fallback!(color, windowBackgroundColor, clearColor),
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
Color::MacOSUnderPageBackgroundColor => system_color_with_fallback!(color, underPageBackgroundColor, clearColor), Color::MacOSUnderPageBackgroundColor => system_color_with_fallback!(color, underPageBackgroundColor, clearColor),
} }
} }

View file

@ -9,7 +9,7 @@
/// ///
/// You can opt to include vector assets in your bundle, or draw icons with `Image::draw` by /// You can opt to include vector assets in your bundle, or draw icons with `Image::draw` by
/// converting Core Graphics calls (e.g, PaintCode can work well for this). /// converting Core Graphics calls (e.g, PaintCode can work well for this).
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
#[derive(Debug)] #[derive(Debug)]
pub enum MacSystemIcon { pub enum MacSystemIcon {
/// A standard "General" preferences icon. This is intended for usage in Preferences toolbars. /// A standard "General" preferences icon. This is intended for usage in Preferences toolbars.
@ -26,7 +26,7 @@ pub enum MacSystemIcon {
Add Add
} }
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
impl MacSystemIcon { impl MacSystemIcon {
/// Maps system icons to their pre-11.0 framework identifiers. /// Maps system icons to their pre-11.0 framework identifiers.
pub fn to_str(&self) -> &'static str { pub fn to_str(&self) -> &'static str {

View file

@ -133,7 +133,7 @@ impl Image {
/// Returns a stock system icon. These are guaranteed to exist across all versions of macOS /// Returns a stock system icon. These are guaranteed to exist across all versions of macOS
/// supported. /// supported.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
pub fn system_icon(icon: MacSystemIcon, accessibility_description: &str) -> Self { pub fn system_icon(icon: MacSystemIcon, accessibility_description: &str) -> Self {
Image(unsafe { Image(unsafe {
ShareId::from_ptr(match os::is_minimum_version(11) { ShareId::from_ptr(match os::is_minimum_version(11) {
@ -166,7 +166,7 @@ impl Image {
}, },
false => { false => {
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
panic!("SFSymbols are only supported on macOS 11.0 and up."); panic!("SFSymbols are only supported on macOS 11.0 and up.");
} }
}) })

View file

@ -49,7 +49,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::*;
@ -95,6 +95,14 @@ pub struct App<
_w: std::marker::PhantomData<W> _w: std::marker::PhantomData<W>
} }
// Temporary. ;P
impl<W, T, F> std::fmt::Debug for App<W, T, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("App<W, T, F>")
.finish()
}
}
impl<T, W, F> App<T, W, F> impl<T, W, F> App<T, W, F>
where where
T: AppDelegate + 'static, T: AppDelegate + 'static,
@ -134,8 +142,8 @@ where
App { App {
delegate: app_delegate, delegate: app_delegate,
vendor: vendor, vendor,
pool: pool, pool,
_w: std::marker::PhantomData _w: std::marker::PhantomData
} }
} }
@ -145,14 +153,24 @@ impl<T, W, F> App<T, W, F> {
/// Handles calling through to `UIApplicationMain()`, ensuring that it's using our custom /// Handles calling through to `UIApplicationMain()`, ensuring that it's using our custom
/// `UIApplication` and `UIApplicationDelegate` classes. /// `UIApplication` and `UIApplicationDelegate` classes.
pub fn run(&self) { pub fn run(&self) {
let args = std::env::args().map(|arg| CString::new(arg).unwrap() ).collect::<Vec<CString>>(); let args = std::env::args().map(|arg| {
let c_args = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<*const c_char>>(); CString::new(arg).unwrap()
}).collect::<Vec<CString>>();
let s = NSString::no_copy("RSTApplication"); let c_args = args.iter().map(|arg| {
let s2 = NSString::no_copy("RSTAppDelegate"); arg.as_ptr()
}).collect::<Vec<*const c_char>>();
let mut s = NSString::new("RSTApplication");
let mut s2 = NSString::new("RSTAppDelegate");
unsafe { unsafe {
UIApplicationMain(c_args.len() as c_int, c_args.as_ptr(), &*s, &*s2); UIApplicationMain(
c_args.len() as c_int,
c_args.as_ptr(),
s.into(),
s2.into()
);
} }
self.pool.drain(); self.pool.drain();

View file

@ -1,7 +1,3 @@
//! This module implements forwarding methods for standard `UIApplicationDelegate` calls. It also
//! creates a custom `UIApplication` subclass that currently does nothing; this is meant as a hook
//! for potential future use.
use std::ffi::c_void; use std::ffi::c_void;
use std::sync::Once; use std::sync::Once;
use std::unreachable; use std::unreachable;
@ -39,7 +35,7 @@ extern fn init<
let factory: &F = &*scene_delegate_vendor; let factory: &F = &*scene_delegate_vendor;
let scene_delegate = factory(); let scene_delegate = factory();
let scene_delegate_ptr = Box::into_raw(scene_delegate); let scene_delegate_ptr = Box::into_raw(scene_delegate);
println!("scene ptr: {:p}", scene_delegate_ptr); //println!("scene ptr: {:p}", scene_delegate_ptr);
this.set_ivar(WINDOW_SCENE_PTR, scene_delegate_ptr as usize); this.set_ivar(WINDOW_SCENE_PTR, scene_delegate_ptr as usize);
this this

View file

@ -15,11 +15,11 @@ pub enum SessionRole {
impl From<SessionRole> for NSString<'_> { impl From<SessionRole> for NSString<'_> {
fn from(role: SessionRole) -> Self { fn from(role: SessionRole) -> Self {
NSString::new(match role { match role {
SessionRole::Application => NSString::no_copy("UIWindowSceneSessionRoleApplication"), SessionRole::Application => NSString::no_copy("UIWindowSceneSessionRoleApplication"),
SessionRole::ExternalDisplay => NSString::no_copy("UIWindowSceneSessionRoleExternalDisplay"), SessionRole::ExternalDisplay => NSString::no_copy("UIWindowSceneSessionRoleExternalDisplay"),
//SessionRole::CarPlayApplication => "" //SessionRole::CarPlayApplication => ""
}) }
} }
} }

View file

@ -16,7 +16,7 @@ impl SceneSession {
} }
pub fn role(&self) -> SessionRole { pub fn role(&self) -> SessionRole {
NSString::wrap(unsafe { NSString::from_retained(unsafe {
msg_send![&*self.0, role] msg_send![&*self.0, role]
}).into() }).into()
} }

View file

@ -9,6 +9,8 @@ use objc_id::ShareId;
use crate::foundation::{id, nil, to_bool, YES, NO, NSArray, NSString}; use crate::foundation::{id, nil, to_bool, YES, NO, NSArray, NSString};
use crate::geometry::Rect; use crate::geometry::Rect;
#[cfg(target_os = "macos")]
use crate::pasteboard::PasteboardType; use crate::pasteboard::PasteboardType;
/// A trait that view wrappers must conform to. Enables managing the subview tree. /// A trait that view wrappers must conform to. Enables managing the subview tree.
@ -103,6 +105,7 @@ pub trait Layout {
} }
/// Returns whether this is hidden, *or* whether an ancestor view is hidden. /// Returns whether this is hidden, *or* whether an ancestor view is hidden.
#[cfg(target_os = "macos")]
fn is_hidden_or_ancestor_is_hidden(&self) -> bool { fn is_hidden_or_ancestor_is_hidden(&self) -> bool {
self.get_from_backing_node(|obj| { self.get_from_backing_node(|obj| {
to_bool(unsafe { to_bool(unsafe {
@ -112,6 +115,7 @@ pub trait Layout {
} }
/// Register this view for drag and drop operations. /// Register this view for drag and drop operations.
#[cfg(target_os = "macos")]
fn register_for_dragged_types(&self, types: &[PasteboardType]) { fn register_for_dragged_types(&self, types: &[PasteboardType]) {
let types: NSArray = types.into_iter().map(|t| { let types: NSArray = types.into_iter().map(|t| {
let x: NSString = (*t).into(); let x: NSString = (*t).into();
@ -124,6 +128,7 @@ pub trait Layout {
} }
/// Unregisters this as a target for drag and drop operations. /// Unregisters this as a target for drag and drop operations.
#[cfg(target_os = "macos")]
fn unregister_dragged_types(&self) { fn unregister_dragged_types(&self) {
self.with_backing_node(|obj| unsafe { self.with_backing_node(|obj| unsafe {
let _: () = msg_send![obj, unregisterDraggedTypes]; let _: () = msg_send![obj, unregisterDraggedTypes];
@ -134,6 +139,7 @@ pub trait Layout {
/// ///
/// If you have a high performance tableview or collectionview that has issues, disabling these /// If you have a high performance tableview or collectionview that has issues, disabling these
/// can be helpful - but always test! /// can be helpful - but always test!
#[cfg(target_os = "macos")]
fn set_posts_frame_change_notifications(&self, posts: bool) { fn set_posts_frame_change_notifications(&self, posts: bool) {
self.with_backing_node(|obj| unsafe { self.with_backing_node(|obj| unsafe {
let _: () = msg_send![obj, setPostsFrameChangedNotifications:match posts { let _: () = msg_send![obj, setPostsFrameChangedNotifications:match posts {
@ -147,6 +153,7 @@ pub trait Layout {
/// ///
/// If you have a high performance tableview or collectionview that has issues, disabling these /// If you have a high performance tableview or collectionview that has issues, disabling these
/// can be helpful - but always test! /// can be helpful - but always test!
#[cfg(target_os = "macos")]
fn set_posts_bounds_change_notifications(&self, posts: bool) { fn set_posts_bounds_change_notifications(&self, posts: bool) {
self.with_backing_node(|obj| unsafe { self.with_backing_node(|obj| unsafe {
let _: () = msg_send![obj, setPostsBoundsChangedNotifications:match posts { let _: () = msg_send![obj, setPostsBoundsChangedNotifications:match posts {

View file

@ -96,14 +96,15 @@ pub use objc;
pub use url; pub use url;
pub use lazy_static; pub use lazy_static;
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(feature = "macos")))] #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
pub mod macos; pub mod macos;
#[cfg(feature = "ios")] #[cfg(target_os = "ios")]
#[cfg_attr(docsrs, doc(cfg(feature = "ios")))] #[cfg_attr(docsrs, doc(cfg(target_os = "ios")))]
pub mod ios; pub mod ios;
#[cfg(target_os = "macos")]
pub mod button; pub mod button;
#[cfg(any(feature = "cloudkit", doc))] #[cfg(any(feature = "cloudkit", doc))]
@ -111,25 +112,51 @@ pub mod button;
pub mod cloudkit; pub mod cloudkit;
pub mod color; pub mod color;
#[cfg(target_os = "macos")]
pub mod dragdrop; pub mod dragdrop;
pub mod error; pub mod error;
#[cfg(target_os = "macos")]
pub mod events; pub mod events;
pub mod defaults; pub mod defaults;
#[cfg(target_os = "macos")]
pub mod filesystem; pub mod filesystem;
pub mod foundation; pub mod foundation;
pub mod geometry; pub mod geometry;
#[cfg(target_os = "macos")]
pub mod image; pub mod image;
#[cfg(target_os = "macos")]
pub mod input; pub mod input;
pub mod layer; pub mod layer;
pub(crate) mod invoker; pub(crate) mod invoker;
pub mod layout; pub mod layout;
#[cfg(target_os = "macos")]
pub mod listview; pub mod listview;
pub mod networking; pub mod networking;
pub mod notification_center; pub mod notification_center;
#[cfg(target_os = "macos")]
pub mod pasteboard; pub mod pasteboard;
#[cfg(target_os = "macos")]
pub mod progress; pub mod progress;
#[cfg(target_os = "macos")]
pub mod scrollview; pub mod scrollview;
#[cfg(target_os = "macos")]
pub mod switch; pub mod switch;
#[cfg(target_os = "macos")]
pub mod text; pub mod text;
#[cfg(feature = "quicklook")] #[cfg(feature = "quicklook")]

View file

@ -364,7 +364,7 @@ impl<T> ListView<T> {
/// Sets the style for the underlying NSTableView. This property is only supported on macOS /// Sets the style for the underlying NSTableView. This property is only supported on macOS
/// 11.0+, and will always be `FullWidth` on anything older. /// 11.0+, and will always be `FullWidth` on anything older.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
pub fn set_style(&self, style: crate::foundation::NSInteger) { pub fn set_style(&self, style: crate::foundation::NSInteger) {
if os::is_minimum_version(11) { if os::is_minimum_version(11) {
self.objc.with_mut(|obj| unsafe { self.objc.with_mut(|obj| unsafe {
@ -378,7 +378,7 @@ impl<T> ListView<T> {
/// This defaults to `true`, but some macOS pieces (e.g, a sidebar) may want this set to /// This defaults to `true`, but some macOS pieces (e.g, a sidebar) may want this set to
/// `false`. This can be particularly useful when implementing a Source List style sidebar /// `false`. This can be particularly useful when implementing a Source List style sidebar
/// view for navigation purposes. /// view for navigation purposes.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
pub fn set_allows_empty_selection(&self, allows: bool) { pub fn set_allows_empty_selection(&self, allows: bool) {
self.objc.with_mut(|obj| unsafe { self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![obj, setAllowsEmptySelection:match allows { let _: () = msg_send![obj, setAllowsEmptySelection:match allows {

View file

@ -3,7 +3,7 @@
use crate::foundation::{NSUInteger}; use crate::foundation::{NSUInteger};
/// Used to set whether and/or how a view or cell draws a focus ring. /// Used to set whether and/or how a view or cell draws a focus ring.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
#[derive(Debug)] #[derive(Debug)]
pub enum FocusRingType { pub enum FocusRingType {
/// Whatever the default is. /// Whatever the default is.
@ -20,7 +20,7 @@ pub enum FocusRingType {
Unknown(NSUInteger) Unknown(NSUInteger)
} }
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
impl From<FocusRingType> for NSUInteger { impl From<FocusRingType> for NSUInteger {
fn from(ring_type: FocusRingType) -> Self { fn from(ring_type: FocusRingType) -> Self {
match ring_type { match ring_type {

View file

@ -79,11 +79,11 @@ impl ProgressIndicator {
/// need it to stay around. /// need it to stay around.
pub fn new() -> Self { pub fn new() -> Self {
let view = unsafe { let view = unsafe {
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
let view: id = msg_send![class!(NSProgressIndicator), new]; let view: id = msg_send![class!(NSProgressIndicator), new];
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO]; let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
let _: () = msg_send![view, setWantsLayer:YES]; let _: () = msg_send![view, setWantsLayer:YES];
view view

View file

@ -5,9 +5,10 @@ use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Sel}; use objc::runtime::{Class, Object, Sel};
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use crate::foundation::{BOOL}; use crate::foundation::load_or_register_class;
use crate::foundation::{BOOL, to_bool};
use crate::view::{VIEW_DELEGATE_PTR, ViewDelegate}; use crate::view::{VIEW_DELEGATE_PTR, ViewDelegate};
use crate::utils::{load, as_bool}; use crate::utils::load;
/// Called when the view controller receives a `viewWillAppear:` message. /// Called when the view controller receives a `viewWillAppear:` message.
extern fn will_appear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: BOOL) { extern fn will_appear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: BOOL) {
@ -16,7 +17,7 @@ extern fn will_appear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: BOOL
} }
let controller = load::<T>(this, VIEW_DELEGATE_PTR); let controller = load::<T>(this, VIEW_DELEGATE_PTR);
controller.will_appear(as_bool(animated)); controller.will_appear(to_bool(animated));
} }
/// Called when the view controller receives a `viewDidAppear:` message. /// Called when the view controller receives a `viewDidAppear:` message.
@ -26,7 +27,7 @@ extern fn did_appear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: BOOL)
} }
let controller = load::<T>(this, VIEW_DELEGATE_PTR); let controller = load::<T>(this, VIEW_DELEGATE_PTR);
controller.did_appear(as_bool(animated)); controller.did_appear(to_bool(animated));
} }
/// Called when the view controller receives a `viewWillDisappear:` message. /// Called when the view controller receives a `viewWillDisappear:` message.
@ -36,7 +37,7 @@ extern fn will_disappear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: B
} }
let controller = load::<T>(this, VIEW_DELEGATE_PTR); let controller = load::<T>(this, VIEW_DELEGATE_PTR);
controller.will_disappear(as_bool(animated)); controller.will_disappear(to_bool(animated));
} }
/// Called when the view controller receives a `viewDidDisappear:` message. /// Called when the view controller receives a `viewDidDisappear:` message.
@ -46,27 +47,17 @@ extern fn did_disappear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: BO
} }
let controller = load::<T>(this, VIEW_DELEGATE_PTR); let controller = load::<T>(this, VIEW_DELEGATE_PTR);
controller.did_disappear(as_bool(animated)); controller.did_disappear(to_bool(animated));
} }
/// Registers an `NSViewDelegate`. /// Registers an `NSViewDelegate`.
pub(crate) fn register_view_controller_class<T: ViewDelegate + 'static>() -> *const Class { pub(crate) fn register_view_controller_class<T: ViewDelegate + 'static>(instance: &T) -> *const Class {
static mut VIEW_CLASS: *const Class = 0 as *const Class; load_or_register_class("UIViewController", instance.subclass_name(), |decl| unsafe {
static INIT: Once = Once::new();
INIT.call_once(|| unsafe {
let superclass = class!(UIViewController);
let mut decl = ClassDecl::new("RSTViewController", superclass).unwrap();
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR); decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
decl.add_method(sel!(viewWillAppear:), will_appear::<T> as extern fn(&mut Object, _, BOOL)); decl.add_method(sel!(viewWillAppear:), will_appear::<T> as extern fn(&mut Object, _, BOOL));
decl.add_method(sel!(viewDidAppear:), did_appear::<T> as extern fn(&mut Object, _, BOOL)); decl.add_method(sel!(viewDidAppear:), did_appear::<T> as extern fn(&mut Object, _, BOOL));
decl.add_method(sel!(viewWillDisappear:), will_disappear::<T> as extern fn(&mut Object, _, BOOL)); decl.add_method(sel!(viewWillDisappear:), will_disappear::<T> as extern fn(&mut Object, _, BOOL));
decl.add_method(sel!(viewDidDisappear:), did_disappear::<T> as extern fn(&mut Object, _, BOOL)); decl.add_method(sel!(viewDidDisappear:), did_disappear::<T> as extern fn(&mut Object, _, BOOL));
})
VIEW_CLASS = decl.register();
});
unsafe { VIEW_CLASS }
} }

View file

@ -5,8 +5,8 @@ use objc::runtime::{Class, Object, Sel, BOOL};
use objc::{class, sel, sel_impl}; use objc::{class, sel, sel_impl};
use objc_id::Id; use objc_id::Id;
use crate::foundation::load_or_register_class;
use crate::foundation::{id, YES, NO, NSUInteger}; use crate::foundation::{id, YES, NO, NSUInteger};
use crate::dragdrop::DragInfo;
use crate::view::{VIEW_DELEGATE_PTR, ViewDelegate}; use crate::view::{VIEW_DELEGATE_PTR, ViewDelegate};
use crate::utils::load; use crate::utils::load;
@ -26,20 +26,10 @@ pub(crate) fn register_view_class() -> *const Class {
unsafe { VIEW_CLASS } unsafe { VIEW_CLASS }
} }
/// Injects an `NSView` subclass, with some callback and pointer ivars for what we /// Injects a `UIView` subclass, with some callback and pointer ivars for what we
/// need to do. /// need to do.
pub(crate) fn register_view_class_with_delegate<T: ViewDelegate>() -> *const Class { pub(crate) fn register_view_class_with_delegate<T: ViewDelegate>(instance: &T) -> *const Class {
static mut VIEW_CLASS: *const Class = 0 as *const Class; load_or_register_class("UIView", instance.subclass_name(), |decl| unsafe {
static INIT: Once = Once::new();
INIT.call_once(|| unsafe {
let superclass = class!(UIView);
let mut decl = ClassDecl::new("RSTViewWithDelegate", superclass).unwrap();
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR); decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
VIEW_CLASS = decl.register(); })
});
unsafe {
VIEW_CLASS
}
} }

View file

@ -48,9 +48,11 @@ use crate::foundation::{id, nil, YES, NO, NSArray, NSString};
use crate::color::Color; use crate::color::Color;
use crate::layer::Layer; use crate::layer::Layer;
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension}; use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
use crate::pasteboard::PasteboardType;
use crate::utils::properties::ObjcProperty; use crate::utils::properties::ObjcProperty;
#[cfg(target_os = "macos")]
use crate::pasteboard::PasteboardType;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
mod macos; mod macos;
@ -66,7 +68,9 @@ use ios::{register_view_class, register_view_class_with_delegate};
mod controller; mod controller;
pub use controller::ViewController; pub use controller::ViewController;
#[cfg(target_os = "macos")]
mod splitviewcontroller; mod splitviewcontroller;
#[cfg(target_os = "macos")]
pub use splitviewcontroller::SplitViewController; pub use splitviewcontroller::SplitViewController;
mod traits; mod traits;
@ -227,9 +231,15 @@ impl<T> View<T> {
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) { pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
let color: id = color.as_ref().into(); let color: id = color.as_ref().into();
#[cfg(target_os = "macos")]
self.objc.with_mut(|obj| unsafe { self.objc.with_mut(|obj| unsafe {
(&mut *obj).set_ivar(BACKGROUND_COLOR, color); (&mut *obj).set_ivar(BACKGROUND_COLOR, color);
}); });
#[cfg(target_os = "ios")]
self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![&*obj, setBackgroundColor:color];
});
} }
} }

View file

@ -71,7 +71,7 @@ where
/// You'd use this if, say, you wanted a border under one part of the `SplitViewController` but /// You'd use this if, say, you wanted a border under one part of the `SplitViewController` but
/// not the other. This API was introduced in macOS 11.0 (Big Sur) and is a noop on anything /// not the other. This API was introduced in macOS 11.0 (Big Sur) and is a noop on anything
/// prior. /// prior.
#[cfg(feature = "macos")] #[cfg(target_os = "macos")]
pub fn set_titlebar_separator_style(&self, style: crate::foundation::NSInteger) { pub fn set_titlebar_separator_style(&self, style: crate::foundation::NSInteger) {
if os::is_minimum_version(11) { if os::is_minimum_version(11) {
unsafe { unsafe {

View file

@ -1,6 +1,8 @@
//! Various traits used for Views. //! Various traits used for Views.
#[cfg(target_os = "macos")]
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 /// This trait can be used for implementing custom View behavior. You implement this trait on your
@ -41,22 +43,27 @@ pub trait ViewDelegate {
/// Invoked when the dragged image enters destination bounds or frame; returns dragging /// Invoked when the dragged image enters destination bounds or frame; returns dragging
/// operation to perform. /// operation to perform.
#[cfg(target_os = "macos")]
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 /// Invoked when the image is released, allowing the receiver to agree to or refuse
/// drag operation. /// drag operation.
#[cfg(target_os = "macos")]
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 /// Invoked after the released image has been removed from the screen, signaling the
/// receiver to import the pasteboard data. /// receiver to import the pasteboard data.
#[cfg(target_os = "macos")]
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 /// Invoked when the dragging operation is complete, signaling the receiver to perform
/// any necessary clean-up. /// any necessary clean-up.
#[cfg(target_os = "macos")]
fn conclude_drag_operation(&self, info: DragInfo) {} fn conclude_drag_operation(&self, info: DragInfo) {}
/// Invoked when the dragged image exits the destinations bounds rectangle (in the case /// Invoked when the dragged image exits the destinations bounds rectangle (in the case
/// of a view) or its frame rectangle (in the case of a window object). /// of a view) or its frame rectangle (in the case of a window object).
#[cfg(target_os = "macos")]
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 }