diff --git a/src/app/mod.rs b/src/app/mod.rs index 2b49398..0eae186 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,9 +1,44 @@ +//! Wraps the application lifecycle across platforms. +//! +//! This is where the bulk of your application logic starts out from. macOS and iOS are driven +//! heavily by lifecycle events - in this case, your boilerplate would look something like this: +//! +//! ```rust,no_run +//! use cacao::app::{App, AppDelegate}; +//! use cacao::window::Window; //! +//! #[derive(Default)] +//! struct BasicApp; +//! +//! impl AppDelegate for BasicApp { +//! fn did_finish_launching(&self) { +//! // Your program in here +//! } +//! } +//! +//! fn main() { +//! App::new("com.my.app", BasicApp::default()).run(); +//! } +//! ``` +//! +//! ## Why do I need to do this? +//! A good question. Cocoa does many things for you (e.g, setting up and managing a runloop, +//! handling the view/window heirarchy, and so on). This requires certain things happen before your +//! code can safely run, which `App` in this framework does for you. +//! +//! - It ensures that the `sharedApplication` is properly initialized with your delegate. +//! - It ensures that Cocoa is put into multi-threaded mode, so standard POSIX threads work as they +//! should. +//! +//! ### Platform specificity +//! Certain lifecycle events are specific to certain platforms. Where this is the case, the +//! documentation makes every effort to note. use objc_id::Id; use objc::runtime::Object; use objc::{class, msg_send, sel, sel_impl}; +use crate::dispatcher::Dispatcher; use crate::foundation::{id, YES, NO, NSUInteger, AutoReleasePool}; use crate::menu::Menu; @@ -14,10 +49,10 @@ mod delegate; use delegate::register_app_delegate_class; pub mod enums; -pub use enums::AppDelegateResponse; +use enums::AppDelegateResponse; pub mod traits; -pub use traits::{AppDelegate, Dispatcher}; +use traits::AppDelegate; pub(crate) static APP_PTR: &str = "rstAppPtr"; diff --git a/src/app/traits.rs b/src/app/traits.rs index 56db55a..b57a08e 100644 --- a/src/app/traits.rs +++ b/src/app/traits.rs @@ -13,14 +13,6 @@ use crate::user_activity::UserActivity; #[cfg(feature = "cloudkit")] use crate::cloudkit::share::CKShareMetaData; -/// Controllers interested in processing messages can implement this to respond to messages as -/// they're dispatched. All messages come in on the main thread. -pub trait Dispatcher { - type Message: Send + Sync; - - fn on_message(&self, _message: Self::Message) {} -} - /// `AppDelegate` is more or less `NSAppDelegate` 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 diff --git a/src/collection_view/mod.rs b/src/collection_view/mod.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/collection_view/traits.rs b/src/collection_view/traits.rs deleted file mode 100644 index 1ce039f..0000000 --- a/src/collection_view/traits.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Traits used for `CollectionView` controllers. More or less maps to the iOS/macOS routines, -//! mapping over the differences between them where appropriate. - -pub trait CollectionViewController { - -} diff --git a/src/dispatcher.rs b/src/dispatcher.rs new file mode 100644 index 0000000..d372893 --- /dev/null +++ b/src/dispatcher.rs @@ -0,0 +1,8 @@ + +/// Controllers interested in processing messages can implement this to respond to messages as +/// they're dispatched. All messages come in on the main thread. +pub trait Dispatcher { + type Message: Send + Sync; + + fn on_message(&self, _message: Self::Message) {} +} diff --git a/src/lib.rs b/src/lib.rs index 517e941..eadfcbf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,8 +80,8 @@ pub mod button; pub mod cloudkit; pub mod color; -pub mod collection_view; pub mod constants; +pub mod dispatcher; pub mod dragdrop; pub mod error; pub mod events; @@ -107,28 +107,4 @@ pub mod webview; pub mod window; -pub mod prelude { - pub use crate::app::{App, AppDelegate, Dispatcher}; - - pub use crate::layout::LayoutConstraint; - - pub use crate::menu::{Menu, MenuItem}; - - #[cfg(feature = "user-notifications")] - pub use crate::notifications::{Notification, NotificationCenter, NotificationAuthOption}; - - pub use crate::toolbar::{Toolbar, ToolbarController, ToolbarHandle}; - - pub use crate::networking::URLRequest; - - pub use crate::window::{ - Window, WindowConfig, WindowDelegate - }; - - #[cfg(feature = "webview")] - pub use crate::webview::{ - WebView, WebViewConfig, WebViewDelegate - }; - - pub use crate::view::{View, ViewDelegate}; -} +pub mod prelude; diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..1c6df9b --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,29 @@ +//! The prelude imports a large amount of useful widgets and traits. You're of course free to +//! thread imports yourself, but for many smaller applications this module can be quite a time +//! saver. + +pub use crate::app::{App, traits::AppDelegate}; + +pub use crate::dispatcher::Dispatcher; + +pub use crate::layout::LayoutConstraint; + +pub use crate::menu::{Menu, MenuItem}; + +#[cfg(feature = "user-notifications")] +pub use crate::notifications::{Notification, NotificationCenter, NotificationAuthOption}; + +pub use crate::toolbar::{Toolbar, ToolbarController, ToolbarHandle}; + +pub use crate::networking::URLRequest; + +pub use crate::window::{ + Window, config::WindowConfig, traits::WindowDelegate +}; + +#[cfg(feature = "webview")] +pub use crate::webview::{ + WebView, WebViewConfig, WebViewDelegate +}; + +pub use crate::view::{View, traits::ViewDelegate}; diff --git a/src/view/mod.rs b/src/view/mod.rs index 1f33888..af29cb1 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -1,9 +1,45 @@ -//! A `ViewHandle` represents an underlying `NSView`. You're passed a reference to one during your -//! `ViewController::did_load()` method. This method is safe to store and use, however as it's -//! UI-specific it's not thread safe. +//! Wraps `NSView` and `UIView` across platforms. //! -//! You can use this struct to configure how a view should look and layout. It implements -//! AutoLayout - for more information, see the AutoLayout tutorial. +//! This implementation errs towards the `UIView` side of things, and mostly acts as a wrapper to +//! bring `NSView` to the modern era. It does this by flipping the coordinate system to be what +//! people expect in 2020, and layer-backing all views by default. +//! +//! Views implement Autolayout, which enable you to specify how things should appear on the screen. +//! +//! ```rust,no_run +//! use cacao::color::rgb; +//! use cacao::layout::{Layout, LayoutConstraint}; +//! use cacao::view::View; +//! use cacao::window::{Window, WindowDelegate}; +//! +//! #[derive(Default)] +//! struct AppWindow { +//! content: View, +//! red: View, +//! window: Window +//! } +//! +//! impl WindowDelegate for AppWindow { +//! fn did_load(&mut self, window: Window) { +//! window.set_minimum_content_size(300., 300.); +//! self.window = window; +//! +//! self.red.set_background_color(rgb(224, 82, 99)); +//! self.content.add_subview(&self.red); +//! +//! self.window.set_content_view(&self.content); +//! +//! LayoutConstraint::activate(&[ +//! self.red.top.constraint_equal_to(&self.content.top).offset(16.), +//! self.red.leading.constraint_equal_to(&self.content.leading).offset(16.), +//! self.red.trailing.constraint_equal_to(&self.content.trailing).offset(-16.), +//! self.red.bottom.constraint_equal_to(&self.content.bottom).offset(-16.), +//! ]); +//! } +//! } +//! ``` +//! +//! For more information on Autolayout, view the module or check out the examples folder. use std::rc::Rc; use std::cell::RefCell; @@ -21,10 +57,9 @@ mod class; use class::{register_view_class, register_view_class_with_delegate}; pub mod controller; -pub use controller::ViewController; pub mod traits; -pub use traits::ViewDelegate; +use traits::ViewDelegate; pub(crate) static VIEW_DELEGATE_PTR: &str = "rstViewDelegatePtr"; diff --git a/src/webview/mod.rs b/src/webview/mod.rs index b3dab1d..d1dea3b 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -1,9 +1,17 @@ -//! Implements a WebView, which wraps a number of different classes/delegates/controllers into one +//! Wraps `WKWebView` across all platforms. +//! +//! Wraps a number of different classes/delegates/controllers into one //! useful interface. This encompasses... //! //! - `WKWebView` //! - `WKUIDelegate` //! - `WKScriptMessageHandler` +//! +//! This is, thankfully, a pretty similar class across platforms. +//! +//! ### WebView is not available for tvOS +//! Apple does not ship `WKWebView` on tvOS, and as a result this control is not provided on that +//! platform. use std::rc::Rc; use std::cell::RefCell; diff --git a/src/window/controller/mod.rs b/src/window/controller/mod.rs index f8ce31a..fdb3432 100644 --- a/src/window/controller/mod.rs +++ b/src/window/controller/mod.rs @@ -1,3 +1,31 @@ +//! A `WindowController` is useful for handling certain document patterns on macOS. +//! +//! (iOS has no equivalent, as `UIWindowController` is private there). +//! +//! In particular, this is useful for certain situations regarding document handling +//! (which this framework does not yet cover, but may eventually). Note that this control can only +//! be created by providing a `WindowDelegate`. +//! +//! >If your application only uses a single `Window`, you may not even need this - just set the +//! autosave name on your `Window` to get the benefit of cached window location across restarts. +//! +//! # How to use +//! +//! ```rust,no_run +//! use cacao::app::AppDelegate; +//! use cacao::window::{WindowController, WindowDelegate}; +//! +//! #[derive(Default)] +//! struct MyWindow; +//! +//! impl WindowDelegate for MyWindow { +//! // Your implementation here... +//! } +//! +//! struct MyApp { +//! pub window: WindowController +//! } +//! ``` use objc::runtime::Object; use objc::{msg_send, sel, sel_impl}; @@ -10,10 +38,13 @@ use crate::window::{Window, WindowConfig, WindowDelegate, WINDOW_DELEGATE_PTR}; mod class; use class::register_window_controller_class; -/// A `Window` represents your way of interacting with an `NSWindow`. It wraps the various moving -/// pieces to enable you to focus on reacting to lifecycle methods and doing your thing. +/// A `WindowController` wraps your `WindowDelegate` into an underlying `Window`, and +/// provides some extra lifecycle methods. pub struct WindowController { + /// A handler to the underlying `NSWindowController`. pub objc: ShareId, + + /// The underlying `Window` that this controller wraps. pub window: Window } diff --git a/src/window/mod.rs b/src/window/mod.rs index 96466ce..999778b 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -1,11 +1,12 @@ -//! Implements an `NSWindow` wrapper for MacOS, backed by Cocoa and associated widgets. +//! Wraps `NSWindow` on macOS and `UIWindow` on iOS. //! -//! This also handles looping back lifecycle events, such as window resizing or close events. It -//! currently implements a good chunk of the API, however it should be noted that in places where -//! things are outright deprecated, this framework will opt to not bother providing access to them. +//! Using `WindowDelegate`, you're able to handle lifecycle events on your `Window` (such as +//! resizing, closing, and so on). Note that interaction patterns are different between macOS and +//! iOS windows, so your codebase may need to differ quite a bit here. //! -//! If you require functionality like that, you're free to use the `objc` field on a `Window` to -//! instrument it with the Objective-C runtime on your own. +//! Of note: on macOS, in places where things are outright deprecated, this framework will opt to +//! not bother providing access to them. If you require functionality like that, you're free to use +//! the `objc` field on a `Window` to instrument it with the Objective-C runtime on your own. use std::unreachable; use std::rc::Rc; @@ -28,16 +29,15 @@ mod class; use class::{register_window_class, register_window_class_with_delegate}; pub mod config; -pub use config::WindowConfig; +use config::WindowConfig; pub mod controller; -pub use controller::WindowController; pub mod enums; use enums::TitleVisibility; pub mod traits; -pub use traits::WindowDelegate; +use traits::WindowDelegate; pub(crate) static WINDOW_DELEGATE_PTR: &str = "rstWindowDelegate"; @@ -45,10 +45,10 @@ pub(crate) static WINDOW_DELEGATE_PTR: &str = "rstWindowDelegate"; /// pieces to enable you to focus on reacting to lifecycle methods and doing your thing. #[derive(Debug)] pub struct Window { - /// A pointer to the Objective-C `NSWindow`. Used in callback orchestration. + /// A pointer to the Objective-C `NS/UIWindow`. Used in callback orchestration. pub(crate) internal_callback_ptr: Option<*const RefCell>, - /// Represents an `NSWindow` in the Objective-C runtime. + /// Represents an `NS/UIWindow` in the Objective-C runtime. pub objc: ShareId, /// A delegate for this window.