Further work on splitting for macOS/iOS support

This commit is contained in:
Ryan McGrath 2020-03-29 22:33:51 -07:00
parent 27e534a612
commit d69f59d284
No known key found for this signature in database
GPG key ID: 811674B62B666830
36 changed files with 132 additions and 257 deletions

View file

@ -21,6 +21,8 @@ uuid = { version = "0.8", features = ["v4"], optional = true }
url = "2.1.1"
[features]
default = ["macos"]
macos = []
cloudkit = []
user-notifications = ["uuid"]
webview = []

View file

@ -1,18 +1,17 @@
//! This example showcases setting up a basic application and window, and setting up some views to
//! work with autolayout.
use cacao::app::{App, AppDelegate, MacAppDelegate};
use cacao::color::rgb;
use cacao::layout::{Layout, LayoutConstraint};
use cacao::view::View;
use cacao::window::{Window, WindowConfig, WindowDelegate};
use cacao::macos::app::{App, AppDelegate};
use cacao::macos::window::{Window, WindowConfig, WindowDelegate};
struct BasicApp {
window: Window<AppWindow>
}
impl MacAppDelegate for BasicApp {}
impl AppDelegate for BasicApp {
fn did_finish_launching(&self) {
self.window.show();
@ -29,10 +28,9 @@ struct AppWindow {
}
impl WindowDelegate for AppWindow {
fn did_load(&mut self, window: Window) {
fn did_load(&self, window: Window) {
window.set_title("AutoLayout Example");
window.set_minimum_content_size(300., 300.);
self.window = window;
self.blue.set_background_color(rgb(105, 162, 176));
self.content.add_subview(&self.blue);
@ -43,7 +41,7 @@ impl WindowDelegate for AppWindow {
self.green.set_background_color(rgb(161, 192, 132));
self.content.add_subview(&self.green);
self.window.set_content_view(&self.content);
window.set_content_view(&self.content);
LayoutConstraint::activate(&[
self.blue.top.constraint_equal_to(&self.content.top).offset(16.),

View file

@ -1,7 +1,7 @@
//! This example showcases setting up a basic application and window.
use cacao::app::{App, AppDelegate};
use cacao::window::Window;
use cacao::macos::app::{App, AppDelegate};
use cacao::macos::window::Window;
#[derive(Default)]
struct BasicApp {

View file

@ -4,8 +4,8 @@
//!
//! If you're not using that, you can probably get by fine with a standard `NSWindow`.
use cacao::app::{App, AppDelegate};
use cacao::window::{Window, WindowConfig, WindowController, WindowDelegate};
use cacao::macos::app::{App, AppDelegate};
use cacao::macos::window::{Window, WindowConfig, WindowController, WindowDelegate};
struct BasicApp {
window: WindowController<MyWindow>

View file

@ -1,8 +1,8 @@
//! This example showcases setting up a basic application and window delegate.
//! Window Delegate's give you lifecycle methods that you can respond to.
use cacao::app::{App, AppDelegate};
use cacao::window::{Window, WindowConfig, WindowDelegate};
use cacao::macos::app::{App, AppDelegate};
use cacao::macos::window::{Window, WindowConfig, WindowDelegate};
struct BasicApp {
window: Window<MyWindow>
@ -18,7 +18,7 @@ impl AppDelegate for BasicApp {
struct MyWindow;
impl WindowDelegate for MyWindow {
fn did_load(&mut self, window: Window) {
fn did_load(&self, window: Window) {
window.set_minimum_content_size(400., 400.);
window.set_title("A Basic Window!?");
}

View file

@ -72,8 +72,10 @@ pub use core_graphics;
pub use objc;
pub use url;
#[cfg(feature = "macos")]
pub mod macos;
pub mod alert;
pub mod app;
pub mod button;
#[cfg(feature = "cloudkit")]
@ -87,22 +89,18 @@ pub mod filesystem;
pub mod foundation;
pub mod geometry;
pub mod layout;
pub mod menu;
pub mod networking;
pub mod notification_center;
pub mod pasteboard;
#[cfg(feature = "user-notifications")]
pub mod user_notifications;
pub mod pasteboard;
pub mod printing;
pub mod toolbar;
pub mod user_activity;
pub mod utils;
pub(crate) mod utils;
pub mod user_defaults;
//pub mod view;
pub mod view;
#[cfg(feature = "webview")]
pub mod webview;
pub mod window;

View file

@ -14,11 +14,10 @@ use objc::runtime::{Class, Object, Sel};
use url::Url;
use crate::app::APP_PTR;
use crate::app::traits::{AppDelegate, MacAppDelegate};
use crate::error::AppKitError;
use crate::foundation::{id, nil, BOOL, YES, NO, NSUInteger, NSArray, NSString};
use crate::printing::PrintSettings;
use crate::macos::app::{APP_PTR, AppDelegate};
use crate::macos::printing::PrintSettings;
use crate::user_activity::UserActivity;
#[cfg(feature = "cloudkit")]
@ -45,7 +44,7 @@ extern fn did_finish_launching<T: AppDelegate>(this: &Object, _: Sel, _: id) {
}
/// Fires when the Application Delegate receives a `applicationWillBecomeActive` notification.
extern fn will_become_active<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn will_become_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).will_become_active();
}
@ -60,12 +59,12 @@ extern fn will_resign_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
}
/// Fires when the Application Delegate receives a `applicationDidResignActive` notification.
extern fn did_resign_active<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn did_resign_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).did_resign_active();
}
/// Fires when the Application Delegate receives a 'applicationShouldTerminate:` notification.
extern fn should_terminate<T: MacAppDelegate>(this: &Object, _: Sel, _: id) -> NSUInteger {
extern fn should_terminate<T: AppDelegate>(this: &Object, _: Sel, _: id) -> NSUInteger {
app::<T>(this).should_terminate().into()
}
@ -75,38 +74,38 @@ extern fn will_terminate<T: AppDelegate>(this: &Object, _: Sel, _: id) {
}
/// Fires when the Application Delegate receives a `applicationWillHide:` notification.
extern fn will_hide<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn will_hide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).will_hide();
}
/// Fires when the Application Delegate receives a `applicationDidHide:` notification.
extern fn did_hide<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn did_hide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).did_hide();
}
/// Fires when the Application Delegate receives a `applicationWillUnhide:` notification.
extern fn will_unhide<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn will_unhide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).will_unhide();
}
/// Fires when the Application Delegate receives a `applicationDidUnhide:` notification.
extern fn did_unhide<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn did_unhide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).did_unhide();
}
/// Fires when the Application Delegate receives a `applicationWillUpdate:` notification.
extern fn will_update<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn will_update<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).will_update();
}
/// Fires when the Application Delegate receives a `applicationDidUpdate:` notification.
extern fn did_update<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn did_update<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).did_update();
}
/// Fires when the Application Delegate receives a
/// `applicationShouldHandleReopen:hasVisibleWindows:` notification.
extern fn should_handle_reopen<T: MacAppDelegate>(this: &Object, _: Sel, _: id, has_visible_windows: BOOL) -> BOOL {
extern fn should_handle_reopen<T: AppDelegate>(this: &Object, _: Sel, _: id, has_visible_windows: BOOL) -> BOOL {
match app::<T>(this).should_handle_reopen(match has_visible_windows {
YES => true,
NO => false,
@ -118,7 +117,7 @@ extern fn should_handle_reopen<T: MacAppDelegate>(this: &Object, _: Sel, _: id,
}
/// Fires when the application delegate receives a `applicationDockMenu:` request.
extern fn dock_menu<T: MacAppDelegate>(this: &Object, _: Sel, _: id) -> id {
extern fn dock_menu<T: AppDelegate>(this: &Object, _: Sel, _: id) -> id {
match app::<T>(this).dock_menu() {
Some(mut menu) => &mut *menu.inner,
None => nil
@ -126,13 +125,13 @@ extern fn dock_menu<T: MacAppDelegate>(this: &Object, _: Sel, _: id) -> id {
}
/// Fires when the application delegate receives a `application:willPresentError:` notification.
extern fn will_present_error<T: MacAppDelegate>(this: &Object, _: Sel, _: id, error: id) -> id {
extern fn will_present_error<T: AppDelegate>(this: &Object, _: Sel, _: id, error: id) -> id {
let error = AppKitError::new(error);
app::<T>(this).will_present_error(error).into_nserror()
}
/// Fires when the application receives a `applicationDidChangeScreenParameters:` notification.
extern fn did_change_screen_parameters<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn did_change_screen_parameters<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).did_change_screen_parameters();
}
@ -202,7 +201,7 @@ extern fn accepted_cloudkit_share<T: AppDelegate>(_this: &Object, _: Sel, _: id,
}
/// Fires when the application receives an `application:openURLs` message.
extern fn open_urls<T: MacAppDelegate>(this: &Object, _: Sel, _: id, file_urls: id) {
extern fn open_urls<T: AppDelegate>(this: &Object, _: Sel, _: id, file_urls: id) {
let urls = NSArray::wrap(file_urls).map(|url| {
let uri = NSString::wrap(unsafe {
msg_send![url, absoluteString]
@ -215,7 +214,7 @@ extern fn open_urls<T: MacAppDelegate>(this: &Object, _: Sel, _: id, file_urls:
}
/// Fires when the application receives an `application:openFileWithoutUI:` message.
extern fn open_file_without_ui<T: MacAppDelegate>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
extern fn open_file_without_ui<T: AppDelegate>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
let filename = NSString::wrap(file);
match app::<T>(this).open_file_without_ui(filename.to_str()) {
@ -225,7 +224,7 @@ extern fn open_file_without_ui<T: MacAppDelegate>(this: &Object, _: Sel, _: id,
}
/// Fired when the application receives an `applicationShouldOpenUntitledFile:` message.
extern fn should_open_untitled_file<T: MacAppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
extern fn should_open_untitled_file<T: AppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
match app::<T>(this).should_open_untitled_file() {
true => YES,
false => NO
@ -233,7 +232,7 @@ extern fn should_open_untitled_file<T: MacAppDelegate>(this: &Object, _: Sel, _:
}
/// Fired when the application receives an `applicationOpenUntitledFile:` message.
extern fn open_untitled_file<T: MacAppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
extern fn open_untitled_file<T: AppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
match app::<T>(this).open_untitled_file() {
true => YES,
false => NO
@ -241,7 +240,7 @@ extern fn open_untitled_file<T: MacAppDelegate>(this: &Object, _: Sel, _: id) ->
}
/// Fired when the application receives an `application:openTempFile:` message.
extern fn open_temp_file<T: MacAppDelegate>(this: &Object, _: Sel, _: id, filename: id) -> BOOL {
extern fn open_temp_file<T: AppDelegate>(this: &Object, _: Sel, _: id, filename: id) -> BOOL {
let filename = NSString::wrap(filename);
match app::<T>(this).open_temp_file(filename.to_str()) {
@ -251,7 +250,7 @@ extern fn open_temp_file<T: MacAppDelegate>(this: &Object, _: Sel, _: id, filena
}
/// Fired when the application receives an `application:printFile:` message.
extern fn print_file<T: MacAppDelegate>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
extern fn print_file<T: AppDelegate>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
let filename = NSString::wrap(file);
match app::<T>(this).print_file(filename.to_str()) {
@ -262,7 +261,7 @@ extern fn print_file<T: MacAppDelegate>(this: &Object, _: Sel, _: id, file: id)
/// Fired when the application receives an `application:printFiles:withSettings:showPrintPanels:`
/// message.
extern fn print_files<T: MacAppDelegate>(this: &Object, _: Sel, _: id, files: id, settings: id, show_print_panels: BOOL) -> NSUInteger {
extern fn print_files<T: AppDelegate>(this: &Object, _: Sel, _: id, files: id, settings: id, show_print_panels: BOOL) -> NSUInteger {
let files = NSArray::wrap(files).map(|file| {
NSString::wrap(file).to_str().to_string()
});
@ -277,14 +276,14 @@ extern fn print_files<T: MacAppDelegate>(this: &Object, _: Sel, _: id, files: id
}
/// Called when the application's occlusion state has changed.
extern fn did_change_occlusion_state<T: MacAppDelegate>(this: &Object, _: Sel, _: id) {
extern fn did_change_occlusion_state<T: AppDelegate>(this: &Object, _: Sel, _: id) {
app::<T>(this).occlusion_state_changed();
}
/// Called when the application receives an `application:delegateHandlesKey:` message.
/// Note: this may not fire in sandboxed applications. Apple's documentation is unclear on the
/// matter.
extern fn delegate_handles_key<T: MacAppDelegate>(this: &Object, _: Sel, _: id, key: id) -> BOOL {
extern fn delegate_handles_key<T: AppDelegate>(this: &Object, _: Sel, _: id, key: id) -> BOOL {
let key = NSString::wrap(key);
match app::<T>(this).delegate_handles_key(key.to_str()) {
@ -295,53 +294,7 @@ extern fn delegate_handles_key<T: MacAppDelegate>(this: &Object, _: Sel, _: id,
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
/// pointers we need to have.
pub(crate) fn register_app_delegate_class<T: AppDelegate>() -> *const Class {
static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
static INIT: Once = Once::new();
INIT.call_once(|| unsafe {
let superclass = class!(NSObject);
let mut decl = ClassDecl::new("RSTAppDelegate", superclass).unwrap();
decl.add_ivar::<usize>(APP_PTR);
// Launching Applications
decl.add_method(sel!(applicationWillFinishLaunching:), will_finish_launching::<T> as extern fn(&Object, _, _));
decl.add_method(sel!(applicationDidFinishLaunching:), did_finish_launching::<T> as extern fn(&Object, _, _));
// Managing Active Status
decl.add_method(sel!(applicationDidBecomeActive:), did_become_active::<T> as extern fn(&Object, _, _));
decl.add_method(sel!(applicationWillResignActive:), will_resign_active::<T> as extern fn(&Object, _, _));
// Terminating Applications
decl.add_method(sel!(applicationWillTerminate:), will_terminate::<T> as extern fn(&Object, _, _));
// User Activities
decl.add_method(sel!(application:willContinueUserActivityWithType:), will_continue_user_activity_with_type::<T> as extern fn(&Object, _, _, id) -> BOOL);
decl.add_method(sel!(application:continueUserActivity:restorationHandler:), continue_user_activity::<T> as extern fn(&Object, _, _, id, id) -> BOOL);
decl.add_method(sel!(application:didFailToContinueUserActivityWithType:error:), failed_to_continue_user_activity::<T> as extern fn(&Object, _, _, id, id));
decl.add_method(sel!(application:didUpdateUserActivity:), did_update_user_activity::<T> as extern fn(&Object, _, _, id));
// Handling push notifications
decl.add_method(sel!(application:didRegisterForRemoteNotificationsWithDeviceToken:), registered_for_remote_notifications::<T> as extern fn(&Object, _, _, id));
decl.add_method(sel!(application:didFailToRegisterForRemoteNotificationsWithError:), failed_to_register_for_remote_notifications::<T> as extern fn(&Object, _, _, id));
decl.add_method(sel!(application:didReceiveRemoteNotification:), did_receive_remote_notification::<T> as extern fn(&Object, _, _, id));
// CloudKit
#[cfg(feature = "cloudkit")]
decl.add_method(sel!(application:userDidAcceptCloudKitShareWithMetadata:), accepted_cloudkit_share::<T> as extern fn(&Object, _, _, id));
DELEGATE_CLASS = decl.register();
});
unsafe {
DELEGATE_CLASS
}
}
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
/// pointers we need to have.
pub(crate) fn register_mac_app_delegate_class<T: AppDelegate + MacAppDelegate>() -> *const Class {
pub(crate) fn register_app_delegate_class<T: AppDelegate + AppDelegate>() -> *const Class {
static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
static INIT: Once = Once::new();

View file

@ -39,20 +39,20 @@ use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use crate::foundation::{id, YES, NO, NSUInteger, AutoReleasePool};
use crate::menu::Menu;
use crate::macos::menu::Menu;
use crate::notification_center::Dispatcher;
mod class;
use class::register_app_class;
mod delegate;
use delegate::{register_app_delegate_class, register_mac_app_delegate_class};
use delegate::{register_app_delegate_class};
mod enums;
pub use enums::*;
mod traits;
pub use traits::{AppDelegate, MacAppDelegate};
pub use traits::AppDelegate;
pub(crate) static APP_PTR: &str = "rstAppPtr";
@ -118,7 +118,7 @@ impl<T> App<T> {
}
}
impl<T> App<T> where T: AppDelegate + MacAppDelegate + 'static {
impl<T> App<T> where T: AppDelegate + 'static {
/// Creates an NSAutoReleasePool, configures various NSApplication properties (e.g, activation
/// policies), injects an `NSObject` delegate wrapper, and retains everything on the
/// Objective-C side of things.
@ -138,7 +138,7 @@ impl<T> App<T> where T: AppDelegate + MacAppDelegate + 'static {
let app_delegate = Box::new(delegate);
let objc_delegate = unsafe {
let delegate_class = register_mac_app_delegate_class::<T>();
let delegate_class = register_app_delegate_class::<T>();
let delegate: id = msg_send![delegate_class, new];
let delegate_ptr: *const T = &*app_delegate;
(&mut *delegate).set_ivar(APP_PTR, delegate_ptr as usize);

View file

@ -3,17 +3,18 @@
use url::Url;
use crate::app::enums::TerminateResponse;
use crate::error::AppKitError;
use crate::menu::Menu;
use crate::printing::enums::PrintResponse;
use crate::printing::settings::PrintSettings;
use crate::user_activity::UserActivity;
use crate::macos::app::enums::TerminateResponse;
use crate::macos::menu::Menu;
use crate::macos::printing::enums::PrintResponse;
use crate::macos::printing::settings::PrintSettings;
#[cfg(feature = "cloudkit")]
use crate::cloudkit::share::CKShareMetaData;
/// `AppDelegate` is more or less `NSAppDelegate` from the Objective-C/Swift side, just named
/// `AppDelegate` is more or less `NSApplicationDelegate` from the Objective-C/Swift side, just named
/// differently to fit in with the general naming scheme found within this framework. You can
/// implement methods from this trait in order to respond to lifecycle events that the system will
/// fire off.
@ -64,28 +65,7 @@ pub trait AppDelegate {
/// Fired before the application terminates. You can use this to do any required cleanup.
fn will_terminate(&self) {}
}
/// `SceneDelegate` maps over to the newer iOS13+ API. This is necessary in order to support
/// multiple windows (scenes) on iPadOS, which is a desirable feature.
pub trait SceneDelegate {
/*fn configuration_for(
&mut self,
session: SceneSession,
options: &[SceneConnectionOptions]
) -> SceneConfiguration {
}
fn did_discard(&mut self, sessions: &[SceneSession]) {}
*/
}
pub trait IOSAppDelegate {
}
pub trait MacAppDelegate {
/// Fired immediately before the application is about to become active.
fn will_become_active(&self) {}

View file

@ -5,7 +5,7 @@ use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use crate::foundation::{id, NSString};
use crate::menu::item::MenuItem;
use crate::macos::menu::item::MenuItem;
/// A struct that represents an `NSMenu`. It takes ownership of items, and handles instrumenting
/// them throughout the application lifecycle.

22
src/macos/mod.rs Normal file
View file

@ -0,0 +1,22 @@
//! Mac-specific implementations.
//!
//! macOS is a much older system than iOS, and as a result has some... quirks, in addition to just
//! plain different APIs. It's tempting to want to find a common one and just implement that, but
//! unfortunately doing so erases a lot of control and finer points of the macOS platform.
//!
//! With that said, this framework makes attempts to make things mostly work as you'd expect them
//! to from the iOS-side of things, which means we wrap things like `NSView` and `NSTableView` and
//! so on to act like their iOS counterparts (we also layer-back everything by default, as it's
//! typically what you want).
//!
//! _However_, there are some specific things that just can't be wrapped well - for example,
//! `NSToolbar`. Yes, `UIToolbar` exists, but it's really not close to `NSToolbar` in functionality
//! at all. For controls like these, we surface them here - the goal is to enable you to write 90%
//! of your app as a cross platform codebase, with the initial 10% being scaffolding code for the
//! platform (e.g, NSApplication vs UIApplication lifecycle).
pub mod app;
pub mod menu;
pub mod printing;
pub mod toolbar;
pub mod window;

View file

@ -7,7 +7,7 @@ use objc::runtime::{Class, Object, Sel};
use objc::{class, sel, sel_impl};
use crate::foundation::{id, NSArray, NSString};
use crate::toolbar::{TOOLBAR_PTR, ToolbarDelegate};
use crate::macos::toolbar::{TOOLBAR_PTR, ToolbarDelegate};
use crate::utils::load;
/// Retrieves and passes the allowed item identifiers for this toolbar.

View file

@ -2,7 +2,7 @@
//! go. Currently a bit incomplete in that we don't support the customizing workflow, but feel free
//! to pull request it.
use crate::toolbar::{Toolbar, ToolbarItem};
use crate::macos::toolbar::{Toolbar, ToolbarItem};
/// A trait that you can implement to have your struct/etc act as an `NSToolbarDelegate`.
pub trait ToolbarDelegate {

View file

@ -11,7 +11,7 @@ use objc::{class, sel, sel_impl};
use crate::foundation::{id, BOOL, YES, NO, NSUInteger};
use crate::utils::{load, CGSize};
use crate::window::{WindowDelegate, WINDOW_DELEGATE_PTR};
use crate::macos::window::{WindowDelegate, WINDOW_DELEGATE_PTR};
/// Called when an `NSWindowDelegate` receives a `windowWillClose:` event.
/// Good place to clean up memory and what not.

View file

@ -4,7 +4,7 @@
use crate::foundation::NSUInteger;
use crate::geometry::Rect;
use crate::window::enums::WindowStyle;
use crate::macos::window::enums::WindowStyle;
#[derive(Debug)]
pub struct WindowConfig {

View file

@ -7,7 +7,7 @@ use objc::declare::ClassDecl;
use objc::runtime::Class;
use objc::class;
use crate::window::{WindowDelegate, WINDOW_DELEGATE_PTR};
use crate::macos::window::{WindowDelegate, WINDOW_DELEGATE_PTR};
/// Injects an `NSWindowController` subclass, with some callback and pointer ivars for what we
/// need to do.

View file

@ -12,8 +12,8 @@
//! # How to use
//!
//! ```rust,no_run
//! use cacao::app::AppDelegate;
//! use cacao::window::{WindowController, WindowDelegate};
//! use cacao::macos::app::AppDelegate;
//! use cacao::macos::window::{WindowController, WindowDelegate};
//!
//! #[derive(Default)]
//! struct MyWindow;
@ -33,7 +33,7 @@ use objc_id::ShareId;
use crate::foundation::{id, nil};
use crate::utils::Controller;
use crate::window::{Window, WindowConfig, WindowDelegate, WINDOW_DELEGATE_PTR};
use crate::macos::window::{Window, WindowConfig, WindowDelegate, WINDOW_DELEGATE_PTR};
mod class;
use class::register_window_controller_class;

View file

@ -20,7 +20,7 @@ use objc_id::ShareId;
use crate::color::Color;
use crate::foundation::{id, nil, YES, NO, NSString, NSInteger, NSUInteger};
use crate::layout::traits::Layout;
use crate::toolbar::{Toolbar, ToolbarDelegate};
use crate::macos::toolbar::{Toolbar, ToolbarDelegate};
use crate::utils::Controller;
mod class;

View file

@ -2,8 +2,8 @@
//! module. There's a few different ones, and it's just... cleaner, if
//! it's organized here.
use crate::app::PresentationOption;
use crate::window::Window;
use crate::macos::app::PresentationOption;
use crate::macos::window::Window;
/// Lifecycle events for anything that `impl Window`'s. These map to the standard Cocoa
/// lifecycle methods, but mix in a few extra things to handle offering configuration tools

View file

@ -12,7 +12,7 @@ use url::Url;
use crate::foundation::{id, nil, NSString, NSArray};
use crate::error::AppKitError;
pub mod types;
mod types;
pub use types::{PasteboardName, PasteboardType};
/// Represents an `NSPasteboard`, enabling you to handle copy/paste/drag and drop.

View file

@ -7,7 +7,6 @@
//! for in the modern era. It also implements a few helpers for things like setting a background
//! color, and enforcing layer backing by default.
use std::rc::Rc;
use std::sync::Once;
use objc::declare::ClassDecl;
@ -28,83 +27,51 @@ extern fn enforce_normalcy(_: &Object, _: Sel) -> BOOL {
/// Called when a drag/drop operation has entered this view.
extern fn dragging_entered<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) -> NSUInteger {
let view = load::<T>(this, VIEW_DELEGATE_PTR);
let response = {
let v = view.borrow();
(*v).dragging_entered(DragInfo {
view.dragging_entered(DragInfo {
info: unsafe { Id::from_ptr(info) }
}).into()
};
Rc::into_raw(view);
response
}
/// Called when a drag/drop operation has entered this view.
extern fn prepare_for_drag_operation<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) -> BOOL {
let view = load::<T>(this, VIEW_DELEGATE_PTR);
let response = {
let v = view.borrow();
match (*v).prepare_for_drag_operation(DragInfo {
match view.prepare_for_drag_operation(DragInfo {
info: unsafe { Id::from_ptr(info) }
}) {
true => YES,
false => NO
}
};
Rc::into_raw(view);
response
}
/// Called when a drag/drop operation has entered this view.
extern fn perform_drag_operation<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) -> BOOL {
let view = load::<T>(this, VIEW_DELEGATE_PTR);
let response = {
let v = view.borrow();
match (*v).perform_drag_operation(DragInfo {
match view.perform_drag_operation(DragInfo {
info: unsafe { Id::from_ptr(info) }
}) {
true => YES,
false => NO
}
};
Rc::into_raw(view);
response
}
/// Called when a drag/drop operation has entered this view.
extern fn conclude_drag_operation<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) {
let view = load::<T>(this, VIEW_DELEGATE_PTR);
{
let v = view.borrow();
(*v).conclude_drag_operation(DragInfo {
view.conclude_drag_operation(DragInfo {
info: unsafe { Id::from_ptr(info) }
});
}
Rc::into_raw(view);
}
/// Called when a drag/drop operation has entered this view.
extern fn dragging_exited<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) {
let view = load::<T>(this, VIEW_DELEGATE_PTR);
{
let v = view.borrow();
(*v).dragging_exited(DragInfo {
view.dragging_exited(DragInfo {
info: unsafe { Id::from_ptr(info) }
});
}
Rc::into_raw(view);
}
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we

View file

@ -1,6 +1,5 @@
//! Hoists a basic `NSViewController`.
use std::rc::Rc;
use std::sync::Once;
use objc::declare::ClassDecl;
@ -13,49 +12,25 @@ use crate::utils::load;
/// Called when the view controller receives a `viewWillAppear` message.
extern fn will_appear<T: ViewDelegate>(this: &mut Object, _: Sel) {
let controller = load::<T>(this, VIEW_DELEGATE_PTR);
{
let vc = controller.borrow();
(*vc).will_appear();
}
Rc::into_raw(controller);
controller.will_appear();
}
/// Called when the view controller receives a `viewDidAppear` message.
extern fn did_appear<T: ViewDelegate>(this: &mut Object, _: Sel) {
let controller = load::<T>(this, VIEW_DELEGATE_PTR);
{
let vc = controller.borrow();
(*vc).did_appear();
}
Rc::into_raw(controller);
controller.did_appear();
}
/// Called when the view controller receives a `viewWillDisappear` message.
extern fn will_disappear<T: ViewDelegate>(this: &mut Object, _: Sel) {
let controller = load::<T>(this, VIEW_DELEGATE_PTR);
{
let vc = controller.borrow();
(*vc).will_disappear();
}
Rc::into_raw(controller);
controller.will_disappear();
}
/// Called when the view controller receives a `viewDidDisappear` message.
extern fn did_disappear<T: ViewDelegate>(this: &mut Object, _: Sel) {
let controller = load::<T>(this, VIEW_DELEGATE_PTR);
{
let vc = controller.borrow();
(*vc).did_disappear();
}
Rc::into_raw(controller);
controller.did_disappear();
}
/// Registers an `NSViewDelegate`.

View file

@ -17,12 +17,13 @@ pub struct ViewController<T> {
impl<T> ViewController<T> where T: ViewDelegate + 'static {
pub fn new(delegate: T) -> Self {
let mut view = View::with(delegate);
let view = View::with(delegate);
let objc = unsafe {
let vc: id = msg_send![register_view_controller_class::<T>(), new];
if let Some(ptr)= view.internal_callback_ptr {
if let Some(delegate)= &view.delegate {
let ptr: *const T = &**delegate;
(&mut *vc).set_ivar(VIEW_DELEGATE_PTR, ptr as usize);
}
@ -32,9 +33,8 @@ impl<T> ViewController<T> where T: ViewDelegate + 'static {
};
let handle = view.clone_as_handle();
if let Some(view_delegate) = &mut view.delegate {
let mut view_delegate = view_delegate.borrow_mut();
(*view_delegate).did_load(handle);
if let Some(view_delegate) = &view.delegate {
view_delegate.did_load(handle);
}
ViewController {

View file

@ -41,9 +41,6 @@
//!
//! For more information on Autolayout, view the module or check out the examples folder.
use std::rc::Rc;
use std::cell::RefCell;
use objc_id::ShareId;
use objc::runtime::{Class, Object};
use objc::{msg_send, sel, sel_impl};
@ -81,12 +78,8 @@ pub struct View<T = ()> {
/// A pointer to the Objective-C runtime view controller.
pub objc: ShareId<Object>,
/// An internal callback pointer that we use in delegate loopbacks. Default implementations
/// don't require this.
pub(crate) internal_callback_ptr: Option<*const RefCell<T>>,
/// A pointer to the delegate for this view.
pub delegate: Option<Rc<RefCell<T>>>,
pub delegate: Option<Box<T>>,
/// A pointer to the Objective-C runtime top layout constraint.
pub top: LayoutAnchorY,
@ -125,7 +118,6 @@ impl View {
let view = allocate_view(register_view_class);
View {
internal_callback_ptr: None,
delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }),
@ -144,22 +136,17 @@ impl<T> View<T> where T: ViewDelegate + 'static {
/// Initializes a new View with a given `ViewDelegate`. This enables you to respond to events
/// and customize the view as a module, similar to class-based systems.
pub fn with(delegate: T) -> View<T> {
let delegate = Rc::new(RefCell::new(delegate));
let internal_callback_ptr = {
let cloned = Rc::clone(&delegate);
Rc::into_raw(cloned)
};
let delegate = Box::new(delegate);
let view = allocate_view(register_view_class_with_delegate::<T>);
unsafe {
//let view: id = msg_send![register_view_class_with_delegate::<T>(), new];
//let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
(&mut *view).set_ivar(VIEW_DELEGATE_PTR, internal_callback_ptr as usize);
let ptr: *const T = &*delegate;
(&mut *view).set_ivar(VIEW_DELEGATE_PTR, ptr as usize);
};
let mut view = View {
internal_callback_ptr: Some(internal_callback_ptr),
delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }),
@ -172,11 +159,7 @@ impl<T> View<T> where T: ViewDelegate + 'static {
objc: unsafe { ShareId::from_ptr(view) },
};
{
let mut delegate = delegate.borrow_mut();
(*delegate).did_load(view.clone_as_handle());
}
&delegate.did_load(view.clone_as_handle());
view.delegate = Some(delegate);
view
}
@ -189,7 +172,6 @@ impl<T> View<T> {
/// delegate - the `View` is the only true holder of those.
pub(crate) fn clone_as_handle(&self) -> View {
View {
internal_callback_ptr: None,
delegate: None,
top: self.top.clone(),
leading: self.leading.clone(),
@ -245,20 +227,18 @@ impl<T> Layout for View<T> {
impl<T> Drop for View<T> {
/// A bit of extra cleanup for delegate callback pointers. If the originating `View` is being
/// dropped, we do some logic to release the loopback ptr. We also go ahead and check to see if
/// dropped, we do some logic to clean it all up (e.g, we go ahead and check to see if
/// this has a superview (i.e, it's in the heirarchy) on the AppKit side. If it does, we go
/// ahead and remove it - this is intended to match the semantics of how Rust handles things.
/// ahead and remove it - this is intended to match the semantics of how Rust handles things).
///
/// There are, thankfully, no delegates we need to break here.
fn drop(&mut self) {
if let Some(ptr) = &self.internal_callback_ptr {
if self.delegate.is_some() {
unsafe {
let superview: id = msg_send![&*self.objc, superview];
if superview != nil {
let _: () = msg_send![&*self.objc, removeFromSuperview];
}
let _ = Rc::from_raw(ptr);
}
}
}

View file

@ -7,7 +7,7 @@ pub trait ViewDelegate {
/// Called when the View is ready to work with. You're passed a `ViewHandle` - this is safe to
/// store and use repeatedly, but it's not thread safe - any UI calls must be made from the
/// main thread!
fn did_load(&mut self, _view: View) {}
fn did_load(&self, _view: View) {}
/// Called when this is about to be added to the view heirarchy.
fn will_appear(&self) {}