diff --git a/Cargo.toml b/Cargo.toml index 3baa2ad..ee369d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,12 +31,17 @@ url = "2.1.1" eval = "0.4" [features] -default = ["macos"] +default = [] cloudkit = [] -ios = [] -macos = [] color_fallbacks = [] quicklook = [] user-notifications = ["uuid"] webview = [] 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." diff --git a/examples/ios-beta/main.rs b/examples/ios-beta/main.rs new file mode 100644 index 0000000..e8d362d --- /dev/null +++ b/examples/ios-beta/main.rs @@ -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>, + pub root_view_controller: RwLock>> +} + +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(); +} diff --git a/examples/ios-beta/readme.md b/examples/ios-beta/readme.md new file mode 100644 index 0000000..08217c3 --- /dev/null +++ b/examples/ios-beta/readme.md @@ -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! diff --git a/src/button/enums.rs b/src/button/enums.rs index 6b30521..bd3396a 100644 --- a/src/button/enums.rs +++ b/src/button/enums.rs @@ -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 /// under iOS or tvOS. -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] #[derive(Debug)] pub enum BezelStyle { /// A standard circular button. @@ -49,7 +49,7 @@ pub enum BezelStyle { Unknown(NSUInteger) } -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] impl From for NSUInteger { fn from(style: BezelStyle) -> Self { match style { @@ -71,7 +71,7 @@ impl From for NSUInteger { } } -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] impl From for BezelStyle { fn from(i: NSUInteger) -> Self { match i { diff --git a/src/button/mod.rs b/src/button/mod.rs index 770fefe..5df9739 100644 --- a/src/button/mod.rs +++ b/src/button/mod.rs @@ -36,7 +36,7 @@ use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension} use crate::text::{AttributedString, Font}; use crate::utils::{load, properties::ObjcProperty}; -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] use crate::macos::FocusRingType; mod enums; @@ -145,7 +145,7 @@ impl Button { } /// 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) { let style: NSUInteger = bezel_style.into(); @@ -167,7 +167,7 @@ impl Button { pub fn set_background_color>(&self, color: C) { let color: id = color.as_ref().into(); - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] self.objc.with_mut(|obj| unsafe { let cell: id = msg_send![obj, cell]; 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. pub fn set_text_color>(&self, color: C) { - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] self.objc.with_mut(move |obj| unsafe { let text: id = msg_send![obj, attributedTitle]; let len: isize = msg_send![text, length]; @@ -202,7 +202,7 @@ impl Button { // @TODO: Figure out how to handle oddities like this. /// For buttons on macOS, one might need to disable the border. This does that. - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] pub fn set_bordered(&self, is_bordered: bool) { self.objc.with_mut(|obj| unsafe { 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. /// /// This is a macOS-only method. - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] pub fn set_focus_ring_type(&self, focus_ring_type: FocusRingType) { let ring_type: NSUInteger = focus_ring_type.into(); diff --git a/src/color/mod.rs b/src/color/mod.rs index b599408..7453277 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -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 /// 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. @@ -26,10 +26,10 @@ use objc_id::Id; use crate::foundation::id; use crate::utils::os; -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] mod macos_dynamic_color; -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] use macos_dynamic_color::{ AQUA_LIGHT_COLOR_NORMAL_CONTRAST, AQUA_LIGHT_COLOR_HIGH_CONTRAST, AQUA_DARK_COLOR_NORMAL_CONTRAST, AQUA_DARK_COLOR_HIGH_CONTRAST @@ -230,11 +230,11 @@ pub enum Color { LightText, /// The background color for a given window in the system theme. - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] MacOSWindowBackgroundColor, /// The background color that should appear under a page per the system theme. - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] MacOSUnderPageBackgroundColor } @@ -246,16 +246,13 @@ impl Color { let g = green as CGFloat / 255.0; let b = blue 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 { - #[cfg(feature = "macos")] - Id::from_ptr(msg_send![color, colorWithCalibratedRed:r green:g blue:b alpha:a]) + #[cfg(target_os = "macos")] + { 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 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 { - #[cfg(feature = "macos")] - Id::from_ptr(msg_send![color, colorWithCalibratedHue:h saturation:s brightness:b alpha:a]) + #[cfg(target_os = "macos")] + { 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 /// specified alpha. 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 { - #[cfg(feature = "macos")] - Id::from_ptr(msg_send![color, colorWithCalibratedWhite:level alpha:alpha]) + #[cfg(target_os = "macos")] + { 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. /// /// Returning a dynamic color in your handler is unsupported and may panic. + #[cfg(target_os = "macos")] pub fn dynamic(handler: F) -> Self where 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 // 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(target_os = "macos")] Color::Custom(Arc::new(RwLock::new(unsafe { 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. macro_rules! system_color_with_fallback { ($class:ident, $color:ident, $fallback:ident) => ({ - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] { #[cfg(feature = "color-fallbacks")] if os::minimum_semversion(10, 10, 0) { @@ -441,6 +433,11 @@ macro_rules! system_color_with_fallback { #[cfg(not(feature = "color-fallbacks"))] 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 /// an important piece. It's not on the framework to make your app look good, though. unsafe fn to_objc(obj: &Color) -> id { - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] let color = class!(NSColor); - #[cfg(feature = "ios")] + #[cfg(target_os = "ios")] let color = class!(UIColor); match obj { @@ -503,10 +500,10 @@ unsafe fn to_objc(obj: &Color) -> id { Color::DarkText => system_color_with_fallback!(color, darkTextColor, blackColor), 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), - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] Color::MacOSUnderPageBackgroundColor => system_color_with_fallback!(color, underPageBackgroundColor, clearColor), } } diff --git a/src/image/icons.rs b/src/image/icons.rs index 3287fc9..b1decca 100644 --- a/src/image/icons.rs +++ b/src/image/icons.rs @@ -9,7 +9,7 @@ /// /// 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). -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] #[derive(Debug)] pub enum MacSystemIcon { /// A standard "General" preferences icon. This is intended for usage in Preferences toolbars. @@ -26,7 +26,7 @@ pub enum MacSystemIcon { Add } -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] impl MacSystemIcon { /// Maps system icons to their pre-11.0 framework identifiers. pub fn to_str(&self) -> &'static str { diff --git a/src/image/image.rs b/src/image/image.rs index 2e8d00f..974bbd8 100644 --- a/src/image/image.rs +++ b/src/image/image.rs @@ -133,7 +133,7 @@ impl Image { /// Returns a stock system icon. These are guaranteed to exist across all versions of macOS /// supported. - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] pub fn system_icon(icon: MacSystemIcon, accessibility_description: &str) -> Self { Image(unsafe { ShareId::from_ptr(match os::is_minimum_version(11) { @@ -166,7 +166,7 @@ impl Image { }, false => { - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] panic!("SFSymbols are only supported on macOS 11.0 and up."); } }) diff --git a/src/ios/app/mod.rs b/src/ios/app/mod.rs index 854d263..ce47d78 100644 --- a/src/ios/app/mod.rs +++ b/src/ios/app/mod.rs @@ -49,7 +49,7 @@ mod class; use class::register_app_class; mod delegate; -use delegate::{register_app_delegate_class}; +use delegate::register_app_delegate_class; mod enums; pub use enums::*; @@ -95,6 +95,14 @@ pub struct App< _w: std::marker::PhantomData } +// Temporary. ;P +impl std::fmt::Debug for App { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("App") + .finish() + } +} + impl App where T: AppDelegate + 'static, @@ -134,8 +142,8 @@ where App { delegate: app_delegate, - vendor: vendor, - pool: pool, + vendor, + pool, _w: std::marker::PhantomData } } @@ -145,14 +153,24 @@ impl App { /// Handles calling through to `UIApplicationMain()`, ensuring that it's using our custom /// `UIApplication` and `UIApplicationDelegate` classes. pub fn run(&self) { - let args = std::env::args().map(|arg| CString::new(arg).unwrap() ).collect::>(); - let c_args = args.iter().map(|arg| arg.as_ptr()).collect::>(); + let args = std::env::args().map(|arg| { + CString::new(arg).unwrap() + }).collect::>(); + + let c_args = args.iter().map(|arg| { + arg.as_ptr() + }).collect::>(); - let s = NSString::no_copy("RSTApplication"); - let s2 = NSString::no_copy("RSTAppDelegate"); + let mut s = NSString::new("RSTApplication"); + let mut s2 = NSString::new("RSTAppDelegate"); 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(); diff --git a/src/ios/scene/delegate.rs b/src/ios/scene/delegate.rs index ac861c6..4b1f0b9 100644 --- a/src/ios/scene/delegate.rs +++ b/src/ios/scene/delegate.rs @@ -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::sync::Once; use std::unreachable; @@ -39,7 +35,7 @@ extern fn init< let factory: &F = &*scene_delegate_vendor; let scene_delegate = factory(); 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 diff --git a/src/ios/scene/enums.rs b/src/ios/scene/enums.rs index 5aee1d8..81838fd 100644 --- a/src/ios/scene/enums.rs +++ b/src/ios/scene/enums.rs @@ -15,11 +15,11 @@ pub enum SessionRole { impl From for NSString<'_> { fn from(role: SessionRole) -> Self { - NSString::new(match role { + match role { SessionRole::Application => NSString::no_copy("UIWindowSceneSessionRoleApplication"), SessionRole::ExternalDisplay => NSString::no_copy("UIWindowSceneSessionRoleExternalDisplay"), //SessionRole::CarPlayApplication => "" - }) + } } } diff --git a/src/ios/scene/session.rs b/src/ios/scene/session.rs index 2302bd5..6518098 100644 --- a/src/ios/scene/session.rs +++ b/src/ios/scene/session.rs @@ -16,7 +16,7 @@ impl SceneSession { } pub fn role(&self) -> SessionRole { - NSString::wrap(unsafe { + NSString::from_retained(unsafe { msg_send![&*self.0, role] }).into() } diff --git a/src/layout/traits.rs b/src/layout/traits.rs index 0805629..c2f8815 100644 --- a/src/layout/traits.rs +++ b/src/layout/traits.rs @@ -9,6 +9,8 @@ use objc_id::ShareId; use crate::foundation::{id, nil, to_bool, YES, NO, NSArray, NSString}; use crate::geometry::Rect; + +#[cfg(target_os = "macos")] use crate::pasteboard::PasteboardType; /// 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. + #[cfg(target_os = "macos")] fn is_hidden_or_ancestor_is_hidden(&self) -> bool { self.get_from_backing_node(|obj| { to_bool(unsafe { @@ -112,6 +115,7 @@ pub trait Layout { } /// Register this view for drag and drop operations. + #[cfg(target_os = "macos")] fn register_for_dragged_types(&self, types: &[PasteboardType]) { let types: NSArray = types.into_iter().map(|t| { let x: NSString = (*t).into(); @@ -124,6 +128,7 @@ pub trait Layout { } /// Unregisters this as a target for drag and drop operations. + #[cfg(target_os = "macos")] fn unregister_dragged_types(&self) { self.with_backing_node(|obj| unsafe { 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 /// can be helpful - but always test! + #[cfg(target_os = "macos")] fn set_posts_frame_change_notifications(&self, posts: bool) { self.with_backing_node(|obj| unsafe { 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 /// can be helpful - but always test! + #[cfg(target_os = "macos")] fn set_posts_bounds_change_notifications(&self, posts: bool) { self.with_backing_node(|obj| unsafe { let _: () = msg_send![obj, setPostsBoundsChangedNotifications:match posts { diff --git a/src/lib.rs b/src/lib.rs index 86d1756..dddef19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,14 +96,15 @@ pub use objc; pub use url; pub use lazy_static; -#[cfg(feature = "macos")] -#[cfg_attr(docsrs, doc(cfg(feature = "macos")))] +#[cfg(target_os = "macos")] +#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))] pub mod macos; -#[cfg(feature = "ios")] -#[cfg_attr(docsrs, doc(cfg(feature = "ios")))] +#[cfg(target_os = "ios")] +#[cfg_attr(docsrs, doc(cfg(target_os = "ios")))] pub mod ios; +#[cfg(target_os = "macos")] pub mod button; #[cfg(any(feature = "cloudkit", doc))] @@ -111,25 +112,51 @@ pub mod button; pub mod cloudkit; pub mod color; + +#[cfg(target_os = "macos")] pub mod dragdrop; + pub mod error; + +#[cfg(target_os = "macos")] pub mod events; + pub mod defaults; + +#[cfg(target_os = "macos")] pub mod filesystem; + pub mod foundation; pub mod geometry; + +#[cfg(target_os = "macos")] pub mod image; + +#[cfg(target_os = "macos")] pub mod input; + pub mod layer; pub(crate) mod invoker; pub mod layout; + +#[cfg(target_os = "macos")] pub mod listview; pub mod networking; pub mod notification_center; + +#[cfg(target_os = "macos")] pub mod pasteboard; + +#[cfg(target_os = "macos")] pub mod progress; + +#[cfg(target_os = "macos")] pub mod scrollview; + +#[cfg(target_os = "macos")] pub mod switch; + +#[cfg(target_os = "macos")] pub mod text; #[cfg(feature = "quicklook")] diff --git a/src/listview/mod.rs b/src/listview/mod.rs index 12b21bf..16fa1d6 100644 --- a/src/listview/mod.rs +++ b/src/listview/mod.rs @@ -364,7 +364,7 @@ impl ListView { /// Sets the style for the underlying NSTableView. This property is only supported on macOS /// 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) { if os::is_minimum_version(11) { self.objc.with_mut(|obj| unsafe { @@ -378,7 +378,7 @@ impl ListView { /// 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 /// view for navigation purposes. - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] pub fn set_allows_empty_selection(&self, allows: bool) { self.objc.with_mut(|obj| unsafe { let _: () = msg_send![obj, setAllowsEmptySelection:match allows { diff --git a/src/macos/enums.rs b/src/macos/enums.rs index b90ae23..c60515d 100644 --- a/src/macos/enums.rs +++ b/src/macos/enums.rs @@ -3,7 +3,7 @@ use crate::foundation::{NSUInteger}; /// Used to set whether and/or how a view or cell draws a focus ring. -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] #[derive(Debug)] pub enum FocusRingType { /// Whatever the default is. @@ -20,7 +20,7 @@ pub enum FocusRingType { Unknown(NSUInteger) } -#[cfg(feature = "macos")] +#[cfg(target_os = "macos")] impl From for NSUInteger { fn from(ring_type: FocusRingType) -> Self { match ring_type { diff --git a/src/progress/mod.rs b/src/progress/mod.rs index 260ac3f..83ef309 100644 --- a/src/progress/mod.rs +++ b/src/progress/mod.rs @@ -79,11 +79,11 @@ impl ProgressIndicator { /// need it to stay around. pub fn new() -> Self { let view = unsafe { - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] let view: id = msg_send![class!(NSProgressIndicator), new]; let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO]; - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] let _: () = msg_send![view, setWantsLayer:YES]; view diff --git a/src/view/controller/ios.rs b/src/view/controller/ios.rs index 3db20a5..d923e92 100644 --- a/src/view/controller/ios.rs +++ b/src/view/controller/ios.rs @@ -5,9 +5,10 @@ use objc::declare::ClassDecl; use objc::runtime::{Class, Object, Sel}; 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::utils::{load, as_bool}; +use crate::utils::load; /// Called when the view controller receives a `viewWillAppear:` message. extern fn will_appear(this: &mut Object, _: Sel, animated: BOOL) { @@ -16,7 +17,7 @@ extern fn will_appear(this: &mut Object, _: Sel, animated: BOOL } let controller = load::(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. @@ -26,7 +27,7 @@ extern fn did_appear(this: &mut Object, _: Sel, animated: BOOL) } let controller = load::(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. @@ -36,7 +37,7 @@ extern fn will_disappear(this: &mut Object, _: Sel, animated: B } let controller = load::(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. @@ -46,27 +47,17 @@ extern fn did_disappear(this: &mut Object, _: Sel, animated: BO } let controller = load::(this, VIEW_DELEGATE_PTR); - controller.did_disappear(as_bool(animated)); + controller.did_disappear(to_bool(animated)); } /// Registers an `NSViewDelegate`. -pub(crate) fn register_view_controller_class() -> *const Class { - static mut VIEW_CLASS: *const Class = 0 as *const Class; - static INIT: Once = Once::new(); - - INIT.call_once(|| unsafe { - let superclass = class!(UIViewController); - let mut decl = ClassDecl::new("RSTViewController", superclass).unwrap(); - +pub(crate) fn register_view_controller_class(instance: &T) -> *const Class { + load_or_register_class("UIViewController", instance.subclass_name(), |decl| unsafe { decl.add_ivar::(VIEW_DELEGATE_PTR); - + decl.add_method(sel!(viewWillAppear:), will_appear:: as extern fn(&mut Object, _, BOOL)); decl.add_method(sel!(viewDidAppear:), did_appear:: as extern fn(&mut Object, _, BOOL)); decl.add_method(sel!(viewWillDisappear:), will_disappear:: as extern fn(&mut Object, _, BOOL)); decl.add_method(sel!(viewDidDisappear:), did_disappear:: as extern fn(&mut Object, _, BOOL)); - - VIEW_CLASS = decl.register(); - }); - - unsafe { VIEW_CLASS } + }) } diff --git a/src/view/ios.rs b/src/view/ios.rs index 1e835ed..9964cfb 100644 --- a/src/view/ios.rs +++ b/src/view/ios.rs @@ -5,8 +5,8 @@ use objc::runtime::{Class, Object, Sel, BOOL}; use objc::{class, sel, sel_impl}; use objc_id::Id; +use crate::foundation::load_or_register_class; use crate::foundation::{id, YES, NO, NSUInteger}; -use crate::dragdrop::DragInfo; use crate::view::{VIEW_DELEGATE_PTR, ViewDelegate}; use crate::utils::load; @@ -26,20 +26,10 @@ pub(crate) fn register_view_class() -> *const 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. -pub(crate) fn register_view_class_with_delegate() -> *const Class { - static mut VIEW_CLASS: *const Class = 0 as *const Class; - static INIT: Once = Once::new(); - - INIT.call_once(|| unsafe { - let superclass = class!(UIView); - let mut decl = ClassDecl::new("RSTViewWithDelegate", superclass).unwrap(); +pub(crate) fn register_view_class_with_delegate(instance: &T) -> *const Class { + load_or_register_class("UIView", instance.subclass_name(), |decl| unsafe { decl.add_ivar::(VIEW_DELEGATE_PTR); - VIEW_CLASS = decl.register(); - }); - - unsafe { - VIEW_CLASS - } + }) } diff --git a/src/view/mod.rs b/src/view/mod.rs index 4153d24..ce64030 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -48,9 +48,11 @@ use crate::foundation::{id, nil, YES, NO, NSArray, NSString}; use crate::color::Color; use crate::layer::Layer; use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension}; -use crate::pasteboard::PasteboardType; use crate::utils::properties::ObjcProperty; +#[cfg(target_os = "macos")] +use crate::pasteboard::PasteboardType; + #[cfg(target_os = "macos")] mod macos; @@ -66,7 +68,9 @@ use ios::{register_view_class, register_view_class_with_delegate}; mod controller; pub use controller::ViewController; +#[cfg(target_os = "macos")] mod splitviewcontroller; +#[cfg(target_os = "macos")] pub use splitviewcontroller::SplitViewController; mod traits; @@ -227,9 +231,15 @@ impl View { pub fn set_background_color>(&self, color: C) { let color: id = color.as_ref().into(); + #[cfg(target_os = "macos")] self.objc.with_mut(|obj| unsafe { (&mut *obj).set_ivar(BACKGROUND_COLOR, color); }); + + #[cfg(target_os = "ios")] + self.objc.with_mut(|obj| unsafe { + let _: () = msg_send![&*obj, setBackgroundColor:color]; + }); } } diff --git a/src/view/splitviewcontroller/mod.rs b/src/view/splitviewcontroller/mod.rs index d61f631..b229965 100644 --- a/src/view/splitviewcontroller/mod.rs +++ b/src/view/splitviewcontroller/mod.rs @@ -71,7 +71,7 @@ where /// 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 /// prior. - #[cfg(feature = "macos")] + #[cfg(target_os = "macos")] pub fn set_titlebar_separator_style(&self, style: crate::foundation::NSInteger) { if os::is_minimum_version(11) { unsafe { diff --git a/src/view/traits.rs b/src/view/traits.rs index c9c5e2e..6a936c8 100644 --- a/src/view/traits.rs +++ b/src/view/traits.rs @@ -1,6 +1,8 @@ //! Various traits used for Views. +#[cfg(target_os = "macos")] use crate::dragdrop::{DragInfo, DragOperation}; + use crate::view::View; /// 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 /// operation to perform. + #[cfg(target_os = "macos")] 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. + #[cfg(target_os = "macos")] 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. + #[cfg(target_os = "macos")] 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. + #[cfg(target_os = "macos")] 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 rectangle (in the case of a window object). + #[cfg(target_os = "macos")] fn dragging_exited(&self, info: DragInfo) {} //fn perform_key_equivalent(&self, event: Event) -> bool { false }