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:
parent
5f2f1f21b0
commit
f558f8e24d
11
Cargo.toml
11
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."
|
||||
|
|
96
examples/ios-beta/main.rs
Normal file
96
examples/ios-beta/main.rs
Normal 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();
|
||||
}
|
14
examples/ios-beta/readme.md
Normal file
14
examples/ios-beta/readme.md
Normal 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!
|
|
@ -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<BezelStyle> for NSUInteger {
|
||||
fn from(style: BezelStyle) -> Self {
|
||||
match style {
|
||||
|
@ -71,7 +71,7 @@ impl From<BezelStyle> for NSUInteger {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "macos")]
|
||||
#[cfg(target_os = "macos")]
|
||||
impl From<NSUInteger> for BezelStyle {
|
||||
fn from(i: NSUInteger) -> Self {
|
||||
match i {
|
||||
|
|
|
@ -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<C: AsRef<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<C: AsRef<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();
|
||||
|
||||
|
|
|
@ -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<F>(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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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<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>
|
||||
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<T, W, F> App<T, W, F> {
|
|||
/// 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::<Vec<CString>>();
|
||||
let c_args = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<*const c_char>>();
|
||||
let args = std::env::args().map(|arg| {
|
||||
CString::new(arg).unwrap()
|
||||
}).collect::<Vec<CString>>();
|
||||
|
||||
let c_args = args.iter().map(|arg| {
|
||||
arg.as_ptr()
|
||||
}).collect::<Vec<*const c_char>>();
|
||||
|
||||
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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -15,11 +15,11 @@ pub enum SessionRole {
|
|||
|
||||
impl From<SessionRole> 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 => ""
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ impl SceneSession {
|
|||
}
|
||||
|
||||
pub fn role(&self) -> SessionRole {
|
||||
NSString::wrap(unsafe {
|
||||
NSString::from_retained(unsafe {
|
||||
msg_send![&*self.0, role]
|
||||
}).into()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
35
src/lib.rs
35
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")]
|
||||
|
|
|
@ -364,7 +364,7 @@ impl<T> ListView<T> {
|
|||
|
||||
/// 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<T> ListView<T> {
|
|||
/// 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 {
|
||||
|
|
|
@ -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<FocusRingType> for NSUInteger {
|
||||
fn from(ring_type: FocusRingType) -> Self {
|
||||
match ring_type {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<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);
|
||||
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<T: ViewDelegate>(this: &mut Object, _: Sel, animated: BOOL)
|
|||
}
|
||||
|
||||
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.
|
||||
|
@ -36,7 +37,7 @@ extern fn will_disappear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: B
|
|||
}
|
||||
|
||||
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.
|
||||
|
@ -46,27 +47,17 @@ extern fn did_disappear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: BO
|
|||
}
|
||||
|
||||
let controller = load::<T>(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<T: ViewDelegate + 'static>() -> *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<T: ViewDelegate + 'static>(instance: &T) -> *const Class {
|
||||
load_or_register_class("UIViewController", instance.subclass_name(), |decl| unsafe {
|
||||
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!(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!(viewDidDisappear:), did_disappear::<T> as extern fn(&mut Object, _, BOOL));
|
||||
|
||||
VIEW_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe { VIEW_CLASS }
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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<T: ViewDelegate>() -> *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<T: ViewDelegate>(instance: &T) -> *const Class {
|
||||
load_or_register_class("UIView", instance.subclass_name(), |decl| unsafe {
|
||||
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
|
||||
VIEW_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe {
|
||||
VIEW_CLASS
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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<T> View<T> {
|
|||
pub fn set_background_color<C: AsRef<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];
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 }
|
||||
|
|
Loading…
Reference in a new issue