More or less finalized Toolbar support, at least for now
This commit is contained in:
parent
db33c382b7
commit
3d93b04455
|
@ -49,7 +49,7 @@ pub mod prelude {
|
|||
|
||||
pub use crate::menu::{Menu, MenuItem};
|
||||
pub use crate::notifications::{Notification, NotificationCenter, NotificationAuthOption};
|
||||
pub use crate::toolbar::{Toolbar, ToolbarController};
|
||||
pub use crate::toolbar::{Toolbar, ToolbarController, ToolbarHandle};
|
||||
|
||||
pub use crate::networking::URLRequest;
|
||||
|
||||
|
|
91
appkit/src/toolbar/class.rs
Normal file
91
appkit/src/toolbar/class.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
//! Handles the Objective-C functionality for the Toolbar module.
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::sync::Once;
|
||||
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSArray, NSString};
|
||||
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, sel, sel_impl};
|
||||
|
||||
use crate::constants::TOOLBAR_PTR;
|
||||
use crate::toolbar::traits::ToolbarController;
|
||||
use crate::utils::{load, str_from};
|
||||
|
||||
/// Retrieves and passes the allowed item identifiers for this toolbar.
|
||||
extern fn allowed_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
|
||||
unsafe {
|
||||
let identifiers = {
|
||||
let t = toolbar.borrow();
|
||||
|
||||
(*t).allowed_item_identifiers().iter().map(|identifier| {
|
||||
NSString::alloc(nil).init_str(identifier)
|
||||
}).collect::<Vec<id>>()
|
||||
};
|
||||
|
||||
Rc::into_raw(toolbar);
|
||||
NSArray::arrayWithObjects(nil, &identifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves and passes the default item identifiers for this toolbar.
|
||||
extern fn default_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
|
||||
unsafe {
|
||||
let identifiers = {
|
||||
let t = toolbar.borrow();
|
||||
|
||||
(*t).default_item_identifiers().iter().map(|identifier| {
|
||||
NSString::alloc(nil).init_str(identifier)
|
||||
}).collect::<Vec<id>>()
|
||||
};
|
||||
|
||||
Rc::into_raw(toolbar);
|
||||
NSArray::arrayWithObjects(nil, &identifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads the controller, grabs whatever item is for this identifier, and returns what the
|
||||
/// Objective-C runtime needs.
|
||||
extern fn item_for_identifier<T: ToolbarController>(this: &Object, _: Sel, _: id, identifier: id, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
let identifier = str_from(identifier);
|
||||
|
||||
let mut item = {
|
||||
let t = toolbar.borrow();
|
||||
let item = (*t).item_for(identifier);
|
||||
item
|
||||
};
|
||||
|
||||
Rc::into_raw(toolbar);
|
||||
&mut *item.inner
|
||||
}
|
||||
|
||||
/// Registers a `NSToolbar` subclass, and configures it to hold some ivars for various things we need
|
||||
/// to store. We use it as our delegate as well, just to cut down on moving pieces.
|
||||
pub(crate) fn register_toolbar_class<T: ToolbarController>() -> *const Class {
|
||||
static mut TOOLBAR_CLASS: *const Class = 0 as *const Class;
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
INIT.call_once(|| unsafe {
|
||||
let superclass = class!(NSToolbar);
|
||||
let mut decl = ClassDecl::new("RSTToolbar", superclass).unwrap();
|
||||
|
||||
// For callbacks
|
||||
decl.add_ivar::<usize>(TOOLBAR_PTR);
|
||||
|
||||
// Add callback methods
|
||||
decl.add_method(sel!(toolbarAllowedItemIdentifiers:), allowed_item_identifiers::<T> as extern fn(&Object, _, _) -> id);
|
||||
decl.add_method(sel!(toolbarDefaultItemIdentifiers:), default_item_identifiers::<T> as extern fn(&Object, _, _) -> id);
|
||||
decl.add_method(sel!(toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:), item_for_identifier::<T> as extern fn(&Object, _, _, _, _) -> id);
|
||||
|
||||
TOOLBAR_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe { TOOLBAR_CLASS }
|
||||
}
|
55
appkit/src/toolbar/handle.rs
Normal file
55
appkit/src/toolbar/handle.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
//! A wrapper for the underlying `NSToolbar`, which is safe to clone and pass around. We do this to
|
||||
//! provide a uniform and expectable API.
|
||||
|
||||
use cocoa::base::{YES, NO};
|
||||
use cocoa::foundation::{NSUInteger};
|
||||
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::toolbar::types::{ToolbarDisplayMode, ToolbarSizeMode};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ToolbarHandle(pub ShareId<Object>);
|
||||
|
||||
impl ToolbarHandle {
|
||||
/// Indicates whether the toolbar shows the separator between the toolbar and the main window
|
||||
/// contents.
|
||||
pub fn set_shows_baseline_separator(&self, shows: bool) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, setShowsBaselineSeparator:match shows {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the toolbar's display mode.
|
||||
pub fn set_display_mode(&self, mode: ToolbarDisplayMode) {
|
||||
let mode: NSUInteger = mode.into();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, setDisplayMode:mode];
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the toolbar's size mode.
|
||||
pub fn set_size_mode(&self, mode: ToolbarSizeMode) {
|
||||
let mode: NSUInteger = mode.into();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, setSizeMode:mode];
|
||||
}
|
||||
}
|
||||
|
||||
/// Set whether the toolbar is visible or not.
|
||||
pub fn set_visible(&self, visibility: bool) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, setVisible:match visibility {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,39 @@
|
|||
//! Module hoisting.
|
||||
//! This module contains a wrapper for `NSToolbar`, one of the standard UI elements in a native
|
||||
//! Cocoa application. To customize it and provide options, you should implement a
|
||||
//! `ToolbarController` for your desired struct, and instantiate your `Toolbar` with it. For
|
||||
//! example:
|
||||
//!
|
||||
//! ```
|
||||
//! use appkit::prelude::*;
|
||||
//!
|
||||
//! #[derive(Default)]
|
||||
//! struct WindowToolbar;
|
||||
//!
|
||||
//! impl ToolbarController for WindowToolbar {
|
||||
//! /* Your trait implementation here */
|
||||
//! }
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! And then, wherever your window is:
|
||||
//!
|
||||
//! ```
|
||||
//! #[derive(Default)]
|
||||
//! struct AppWindow {
|
||||
//! pub toolbar: Toolbar<WindowToolbar>
|
||||
//! }
|
||||
//!
|
||||
//! impl WindowController for AppWindow {
|
||||
//! fn did_load(&mut self, window: WindowHandle) {
|
||||
//! window.set_toolbar(&self.toolbar);
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
pub(crate) mod class;
|
||||
|
||||
pub mod handle;
|
||||
pub use handle::ToolbarHandle;
|
||||
|
||||
pub mod item;
|
||||
pub use item::ToolbarItem;
|
||||
|
@ -8,3 +43,6 @@ pub use traits::ToolbarController;
|
|||
|
||||
pub mod toolbar;
|
||||
pub use toolbar::Toolbar;
|
||||
|
||||
pub mod types;
|
||||
pub use types::*;
|
||||
|
|
|
@ -5,26 +5,33 @@
|
|||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Once;
|
||||
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSArray, NSString};
|
||||
use cocoa::foundation::NSString;
|
||||
|
||||
use objc_id::ShareId;
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
|
||||
use crate::constants::TOOLBAR_PTR;
|
||||
use crate::toolbar::class::register_toolbar_class;
|
||||
use crate::toolbar::handle::ToolbarHandle;
|
||||
use crate::toolbar::traits::ToolbarController;
|
||||
use crate::utils::{load, str_from};
|
||||
use crate::toolbar::types::{ToolbarDisplayMode, ToolbarSizeMode};
|
||||
|
||||
/// A wrapper for `NSToolbar`. Holds (retains) pointers for the Objective-C runtime
|
||||
/// where our `NSToolbar` and associated delegate live.
|
||||
pub struct Toolbar<T> {
|
||||
/// A pointer that we "forget" until dropping this struct. This allows us to keep the retain
|
||||
/// count of things appropriate until the Toolbar is done.
|
||||
internal_callback_ptr: *const RefCell<T>,
|
||||
|
||||
/// An internal identifier used by the toolbar. We cache it here in case users want it.
|
||||
pub identifier: String,
|
||||
pub objc_controller: ShareId<Object>,
|
||||
|
||||
/// The Objective-C runtime controller (the toolbar, really - it does double duty).
|
||||
pub objc_controller: ToolbarHandle,
|
||||
|
||||
/// The user supplied controller.
|
||||
pub controller: Rc<RefCell<T>>
|
||||
}
|
||||
|
||||
|
@ -41,7 +48,7 @@ impl<T> Toolbar<T> where T: ToolbarController + 'static {
|
|||
};
|
||||
|
||||
let objc_controller = unsafe {
|
||||
let delegate_class = register_delegate_class::<T>();
|
||||
let delegate_class = register_toolbar_class::<T>();
|
||||
let identifier = NSString::alloc(nil).init_str(&identifier);
|
||||
let alloc: id = msg_send![delegate_class, alloc];
|
||||
let toolbar: id = msg_send![alloc, initWithIdentifier:identifier];
|
||||
|
@ -52,95 +59,49 @@ impl<T> Toolbar<T> where T: ToolbarController + 'static {
|
|||
ShareId::from_ptr(toolbar)
|
||||
};
|
||||
|
||||
{
|
||||
let mut c = controller.borrow_mut();
|
||||
(*c).did_load(ToolbarHandle(objc_controller.clone()));
|
||||
}
|
||||
|
||||
Toolbar {
|
||||
internal_callback_ptr: internal_callback_ptr,
|
||||
identifier: identifier,
|
||||
objc_controller: objc_controller,
|
||||
objc_controller: ToolbarHandle(objc_controller),
|
||||
controller: controller
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates whether the toolbar shows the separator between the toolbar and the main window
|
||||
/// contents.
|
||||
pub fn set_shows_baseline_separator(&self, shows: bool) {
|
||||
self.objc_controller.set_shows_baseline_separator(shows);
|
||||
}
|
||||
|
||||
/// Sets the toolbar's display mode.
|
||||
pub fn set_display_mode(&self, mode: ToolbarDisplayMode) {
|
||||
self.objc_controller.set_display_mode(mode);
|
||||
}
|
||||
|
||||
/// Sets the toolbar's size mode.
|
||||
pub fn set_size_mode(&self, mode: ToolbarSizeMode) {
|
||||
self.objc_controller.set_size_mode(mode);
|
||||
}
|
||||
|
||||
/// Set whether the toolbar is visible or not.
|
||||
pub fn set_visible(&self, visibility: bool) {
|
||||
self.objc_controller.set_visible(visibility);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Toolbar<T> {
|
||||
/// A bit of extra cleanup for delegate callback pointers.
|
||||
/// Note: this currently doesn't check to see if it needs to be removed from a Window it's
|
||||
/// attached to. In theory this is fine... in practice (and in Rust) it might be wonky, so
|
||||
/// worth circling back on at some point.
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = Rc::from_raw(self.internal_callback_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Loops back to the delegate.
|
||||
extern fn allowed_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
|
||||
unsafe {
|
||||
let identifiers = {
|
||||
let t = toolbar.borrow();
|
||||
|
||||
(*t).allowed_item_identifiers().iter().map(|identifier| {
|
||||
NSString::alloc(nil).init_str(identifier)
|
||||
}).collect::<Vec<id>>()
|
||||
};
|
||||
|
||||
Rc::into_raw(toolbar);
|
||||
NSArray::arrayWithObjects(nil, &identifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Loops back to the delegate.
|
||||
extern fn default_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
|
||||
unsafe {
|
||||
let identifiers = {
|
||||
let t = toolbar.borrow();
|
||||
|
||||
(*t).default_item_identifiers().iter().map(|identifier| {
|
||||
NSString::alloc(nil).init_str(identifier)
|
||||
}).collect::<Vec<id>>()
|
||||
};
|
||||
|
||||
Rc::into_raw(toolbar);
|
||||
NSArray::arrayWithObjects(nil, &identifiers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Loops back to the delegate.
|
||||
extern fn item_for_identifier<T: ToolbarController>(this: &Object, _: Sel, _: id, identifier: id, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
let identifier = str_from(identifier);
|
||||
|
||||
let mut item = {
|
||||
let t = toolbar.borrow();
|
||||
let item = (*t).item_for(identifier);
|
||||
item
|
||||
};
|
||||
|
||||
Rc::into_raw(toolbar);
|
||||
&mut *item.inner
|
||||
}
|
||||
|
||||
/// Registers an `NSObject` subclass, and configures it to hold some ivars for various things we need
|
||||
/// to store.
|
||||
fn register_delegate_class<T: ToolbarController>() -> *const Class {
|
||||
static mut TOOLBAR_CLASS: *const Class = 0 as *const Class;
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
INIT.call_once(|| unsafe {
|
||||
let superclass = class!(NSToolbar);
|
||||
let mut decl = ClassDecl::new("RSTToolbar", superclass).unwrap();
|
||||
|
||||
// For callbacks
|
||||
decl.add_ivar::<usize>(TOOLBAR_PTR);
|
||||
|
||||
// Add callback methods
|
||||
decl.add_method(sel!(toolbarAllowedItemIdentifiers:), allowed_item_identifiers::<T> as extern fn(&Object, _, _) -> id);
|
||||
decl.add_method(sel!(toolbarDefaultItemIdentifiers:), default_item_identifiers::<T> as extern fn(&Object, _, _) -> id);
|
||||
decl.add_method(sel!(toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:), item_for_identifier::<T> as extern fn(&Object, _, _, _, _) -> id);
|
||||
|
||||
TOOLBAR_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe { TOOLBAR_CLASS }
|
||||
}
|
||||
|
|
|
@ -2,10 +2,16 @@
|
|||
//! go. Currently a bit incomplete in that we don't support the customizing workflow, but feel free
|
||||
//! to pull request it.
|
||||
|
||||
use crate::toolbar::handle::ToolbarHandle;
|
||||
use crate::toolbar::item::ToolbarItem;
|
||||
|
||||
/// A trait that you can implement to have your struct/etc act as an `NSToolbarDelegate`.
|
||||
pub trait ToolbarController {
|
||||
/// This method can be used to configure your toolbar, if you need to do things involving the
|
||||
/// handle. Unlike some other view types, it's not strictly necessary, and is provided in the
|
||||
/// interest of a uniform and expectable API.
|
||||
fn did_load(&mut self, _toolbar: ToolbarHandle) {}
|
||||
|
||||
/// What items are allowed in this toolbar.
|
||||
fn allowed_item_identifiers(&self) -> Vec<&'static str>;
|
||||
|
||||
|
|
53
appkit/src/toolbar/types.rs
Normal file
53
appkit/src/toolbar/types.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
//! Various types used for Toolbar configuration.
|
||||
|
||||
use cocoa::foundation::NSUInteger;
|
||||
|
||||
/// Represents the display mode(s) a Toolbar can render in.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ToolbarDisplayMode {
|
||||
/// The default display mode.
|
||||
Default,
|
||||
|
||||
/// Show icon and label.
|
||||
IconAndLabel,
|
||||
|
||||
/// Show icon only.
|
||||
IconOnly,
|
||||
|
||||
/// Show label only.
|
||||
LabelOnly
|
||||
}
|
||||
|
||||
impl From<ToolbarDisplayMode> for NSUInteger {
|
||||
fn from(mode: ToolbarDisplayMode) -> Self {
|
||||
match mode {
|
||||
ToolbarDisplayMode::Default => 0,
|
||||
ToolbarDisplayMode::IconAndLabel => 1,
|
||||
ToolbarDisplayMode::IconOnly => 2,
|
||||
ToolbarDisplayMode::LabelOnly => 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the size mode a Toolbar can use.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ToolbarSizeMode {
|
||||
/// The default size mode.
|
||||
Default,
|
||||
|
||||
/// The regular size mode.
|
||||
Regular,
|
||||
|
||||
/// The small size mode.
|
||||
Small
|
||||
}
|
||||
|
||||
impl From<ToolbarSizeMode> for NSUInteger {
|
||||
fn from(mode: ToolbarSizeMode) -> Self {
|
||||
match mode {
|
||||
ToolbarSizeMode::Default => 0,
|
||||
ToolbarSizeMode::Regular => 1,
|
||||
ToolbarSizeMode::Small => 2
|
||||
}
|
||||
}
|
||||
}
|
|
@ -90,11 +90,11 @@ impl WindowHandle {
|
|||
}
|
||||
|
||||
/// Used for setting a toolbar on this window.
|
||||
pub fn set_toolbar<TB: ToolbarController>(&self, toolbar: &Toolbar<TB>) {
|
||||
pub fn set_toolbar<TC: ToolbarController>(&self, toolbar: &Toolbar<TC>) {
|
||||
if let Some(controller) = &self.0 {
|
||||
unsafe {
|
||||
let window: id = msg_send![*controller, window];
|
||||
let _: () = msg_send![window, setToolbar:&*toolbar.objc_controller];
|
||||
let _: () = msg_send![window, setToolbar:&*toolbar.objc_controller.0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ impl<T> Window<T> where T: WindowController + 'static {
|
|||
}
|
||||
|
||||
/// Sets the toolbar for this window.
|
||||
pub fn set_toolbar<TB: ToolbarController>(&self, toolbar: &Toolbar<TB>) {
|
||||
pub fn set_toolbar<TC: ToolbarController + 'static>(&self, toolbar: &Toolbar<TC>) {
|
||||
self.objc_controller.set_toolbar(toolbar);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue