Multiple changes I had to make for Ebou (#89)
* Add segmented control, icons, toolbar * add to demos * doc tests * format code * Additional SFSymbol definitions
This commit is contained in:
parent
1b577506a7
commit
e6696eaa3e
|
@ -16,7 +16,7 @@ pub fn button(text: &str, msg: Msg) -> Button {
|
|||
button.set_bordered(false);
|
||||
button.set_bezel_style(BezelStyle::SmallSquare);
|
||||
button.set_focus_ring_type(FocusRingType::None);
|
||||
button.set_action(move || dispatch(msg.clone()));
|
||||
button.set_action(move |_| dispatch(msg.clone()));
|
||||
button.set_key_equivalent(&*text.to_lowercase());
|
||||
|
||||
let font = Font::system(22.);
|
||||
|
|
|
@ -5,13 +5,15 @@
|
|||
//! - Another Controller / View
|
||||
|
||||
use cacao::appkit::menu::{Menu, MenuItem};
|
||||
use cacao::appkit::segmentedcontrol::SegmentedControl;
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowController, WindowDelegate};
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::button::Button;
|
||||
use cacao::foundation::NSArray;
|
||||
use cacao::geometry::{Edge, Rect};
|
||||
use cacao::image::Image;
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::notification_center::Dispatcher;
|
||||
use cacao::text::{Font, Label};
|
||||
use cacao::view::{Popover, PopoverConfig, View, ViewController, ViewDelegate};
|
||||
|
||||
struct BasicApp {
|
||||
|
@ -124,7 +126,7 @@ impl ViewDelegate for PopoverExampleContentView {
|
|||
|
||||
fn did_load(&mut self, view: cacao::view::View) {
|
||||
let mut button = Button::new("Show");
|
||||
button.set_action(|| dispatch_ui(Msg::Click));
|
||||
button.set_action(|_| dispatch_ui(Msg::Click));
|
||||
|
||||
let controller = PopoverExampleContentViewController::new();
|
||||
let config = PopoverConfig {
|
||||
|
@ -164,16 +166,21 @@ impl Dispatcher for BasicApp {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct PopoverExampleContentViewController {
|
||||
pub label: Label
|
||||
pub control: SegmentedControl
|
||||
}
|
||||
|
||||
impl PopoverExampleContentViewController {
|
||||
fn new() -> Self {
|
||||
let label = Label::new();
|
||||
let font = Font::system(20.);
|
||||
label.set_font(&font);
|
||||
label.set_text("Hello");
|
||||
Self { label }
|
||||
let images = NSArray::from(vec![
|
||||
&*Image::symbol(cacao::image::SFSymbol::AtSymbol, "Hello").0,
|
||||
&*Image::symbol(cacao::image::SFSymbol::PaperPlane, "Hello").0,
|
||||
&*Image::symbol(cacao::image::SFSymbol::PaperPlaneFilled, "Hello").0,
|
||||
]);
|
||||
let mut control = SegmentedControl::new(images, cacao::appkit::segmentedcontrol::TrackingMode::SelectOne);
|
||||
control.set_action(|index| {
|
||||
println!("Selected Index {index}");
|
||||
});
|
||||
Self { control }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,6 +188,6 @@ impl ViewDelegate for PopoverExampleContentViewController {
|
|||
const NAME: &'static str = "PopoverExampleContentViewController";
|
||||
|
||||
fn did_load(&mut self, view: View) {
|
||||
view.add_subview(&self.label);
|
||||
view.add_subview(&self.control);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ impl ViewDelegate for AddNewTodoContentView {
|
|||
|
||||
let mut button = Button::new("Add");
|
||||
button.set_key_equivalent("\r");
|
||||
button.set_action(|| dispatch_ui(Message::ProcessNewTodo));
|
||||
button.set_action(|_| dispatch_ui(Message::ProcessNewTodo));
|
||||
|
||||
view.add_subview(&instructions);
|
||||
view.add_subview(&input);
|
||||
|
|
|
@ -2,6 +2,7 @@ use cacao::layout::{Layout, LayoutConstraint};
|
|||
use cacao::switch::Switch;
|
||||
use cacao::text::Label;
|
||||
use cacao::view::View;
|
||||
use objc::runtime::Object;
|
||||
|
||||
/// A reusable widget for a toggle; this is effectively a standard checkbox/label combination for
|
||||
/// toggling a boolean value.
|
||||
|
@ -55,7 +56,7 @@ impl ToggleOptionView {
|
|||
/// can toggle your settings and such there.
|
||||
pub fn configure<F>(&mut self, text: &str, subtitle: &str, state: bool, handler: F)
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static
|
||||
F: Fn(*const Object) + Send + Sync + 'static
|
||||
{
|
||||
self.title.set_text(text);
|
||||
self.subtitle.set_text(subtitle);
|
||||
|
|
|
@ -19,7 +19,7 @@ impl Default for PreferencesToolbar {
|
|||
let icon = Image::toolbar_icon(MacSystemIcon::PreferencesGeneral, "General");
|
||||
item.set_image(icon);
|
||||
|
||||
item.set_action(|| {
|
||||
item.set_action(|_| {
|
||||
dispatch_ui(Message::SwitchPreferencesToGeneralPane);
|
||||
});
|
||||
|
||||
|
@ -32,7 +32,7 @@ impl Default for PreferencesToolbar {
|
|||
let icon = Image::toolbar_icon(MacSystemIcon::PreferencesAdvanced, "Advanced");
|
||||
item.set_image(icon);
|
||||
|
||||
item.set_action(|| {
|
||||
item.set_action(|_| {
|
||||
dispatch_ui(Message::SwitchPreferencesToAdvancedPane);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use cacao::defaults::{UserDefaults, Value};
|
||||
use objc::runtime::Object;
|
||||
|
||||
const EXAMPLE: &str = "exampleSetting";
|
||||
|
||||
|
@ -25,7 +26,7 @@ impl Defaults {
|
|||
}
|
||||
|
||||
/// Toggles the example setting.
|
||||
pub fn toggle_should_whatever() {
|
||||
pub fn toggle_should_whatever(_object: *const Object) {
|
||||
toggle_bool(EXAMPLE);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ impl Default for TodosToolbar {
|
|||
item.set_title("Add Todo");
|
||||
item.set_button(Button::new("+ New"));
|
||||
|
||||
item.set_action(|| {
|
||||
item.set_action(|_| {
|
||||
dispatch_ui(Message::OpenNewTodoSheet);
|
||||
});
|
||||
|
||||
|
|
|
@ -29,3 +29,4 @@ pub mod toolbar;
|
|||
pub mod window;
|
||||
|
||||
pub mod haptics;
|
||||
pub mod segmentedcontrol;
|
||||
|
|
346
src/appkit/segmentedcontrol.rs
Normal file
346
src/appkit/segmentedcontrol.rs
Normal file
|
@ -0,0 +1,346 @@
|
|||
//! Wraps `NSSegmentedControl` on appkit
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Once;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::color::Color;
|
||||
use crate::control::Control;
|
||||
use crate::foundation::{id, nil, NSArray, NSString, NSUInteger, BOOL, NO, YES};
|
||||
use crate::image::Image;
|
||||
use crate::invoker::TargetActionHandler;
|
||||
use crate::keys::Key;
|
||||
use crate::layout::Layout;
|
||||
use crate::objc_access::ObjcAccess;
|
||||
use crate::text::{AttributedString, Font};
|
||||
use crate::utils::{load, properties::ObjcProperty};
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
use crate::appkit::FocusRingType;
|
||||
|
||||
/// Wraps `NSButton` on appkit, and `UIButton` on iOS and tvOS.
|
||||
///
|
||||
/// You'd use this type to create a button that a user can interact with. Buttons can be configured
|
||||
/// a number of ways, and support setting a callback to fire when they're clicked or tapped.
|
||||
///
|
||||
/// Some properties are platform-specific; see the documentation for further information.
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use cacao::button::Button;
|
||||
/// use cacao::view::View;
|
||||
/// use crate::cacao::layout::Layout;
|
||||
/// let mut button = Button::new("My button title");
|
||||
/// button.set_key_equivalent("c");
|
||||
///
|
||||
/// button.set_action(|_| {
|
||||
/// println!("My button was clicked.");
|
||||
/// });
|
||||
/// let my_view : View<()> = todo!();
|
||||
///
|
||||
/// // Make sure you don't let your Button drop for as long as you need it.
|
||||
/// my_view.add_subview(&button);
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct SegmentedControl {
|
||||
/// A handle for the underlying Objective-C object.
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
/// Hold on to the images
|
||||
images: NSArray,
|
||||
|
||||
handler: Option<TargetActionHandler>,
|
||||
|
||||
/// A pointer to the Objective-C runtime top layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub top: LayoutAnchorY,
|
||||
|
||||
/// A pointer to the Objective-C runtime leading layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub leading: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime left layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub left: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime trailing layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub trailing: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime right layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub right: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime bottom layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub bottom: LayoutAnchorY,
|
||||
|
||||
/// A pointer to the Objective-C runtime width layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub width: LayoutAnchorDimension,
|
||||
|
||||
/// A pointer to the Objective-C runtime height layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub height: LayoutAnchorDimension,
|
||||
|
||||
/// A pointer to the Objective-C runtime center X layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub center_x: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime center Y layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub center_y: LayoutAnchorY
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum TrackingMode {
|
||||
SelectOne = 0,
|
||||
SelectMany = 1,
|
||||
SelectMomentary = 2
|
||||
}
|
||||
|
||||
impl SegmentedControl {
|
||||
/// Creates a new `NSSegmentedControl` instance, configures it appropriately,
|
||||
/// and retains the necessary Objective-C runtime pointer.
|
||||
pub fn new(images: NSArray, tracking_mode: TrackingMode) -> Self {
|
||||
let view: id = unsafe {
|
||||
let tracking_mode = tracking_mode as u8 as i32;
|
||||
let control: id = msg_send![register_class(), segmentedControlWithImages:&*images trackingMode:tracking_mode
|
||||
target:nil
|
||||
action:nil
|
||||
];
|
||||
|
||||
let _: () = msg_send![control, setWantsLayer: YES];
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
let _: () = msg_send![control, setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
control
|
||||
};
|
||||
|
||||
SegmentedControl {
|
||||
handler: None,
|
||||
|
||||
images,
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
top: LayoutAnchorY::top(view),
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
left: LayoutAnchorX::left(view),
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
leading: LayoutAnchorX::leading(view),
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
right: LayoutAnchorX::right(view),
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
trailing: LayoutAnchorX::trailing(view),
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
bottom: LayoutAnchorY::bottom(view),
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
width: LayoutAnchorDimension::width(view),
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
height: LayoutAnchorDimension::height(view),
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
|
||||
objc: ObjcProperty::retain(view)
|
||||
}
|
||||
}
|
||||
|
||||
/// Select the segment at index
|
||||
pub fn set_tooltip_segment(&mut self, index: NSUInteger, tooltip: &str) {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let converted = NSString::new(tooltip);
|
||||
let _: () = msg_send![obj, setToolTip: converted forSegment: index];
|
||||
})
|
||||
}
|
||||
|
||||
/// Select the segment at index
|
||||
pub fn select_segment(&mut self, index: NSUInteger) {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setSelectedSegment: index];
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets an image on the underlying button.
|
||||
pub fn set_image_segment(&mut self, image: Image, segment: NSUInteger) {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setImage:&*image.0 forSegment: segment];
|
||||
});
|
||||
}
|
||||
|
||||
/// Attaches a callback for button press events. Don't get too creative now...
|
||||
/// best just to message pass or something.
|
||||
pub fn set_action<F: Fn(i32) + Send + Sync + 'static>(&mut self, action: F) {
|
||||
// @TODO: This probably isn't ideal but gets the job done for now; needs revisiting.
|
||||
let this = self.objc.get(|obj| unsafe { ShareId::from_ptr(msg_send![obj, self]) });
|
||||
let handler = TargetActionHandler::new(&*this, move |obj: *const Object| unsafe {
|
||||
let selected: i32 = msg_send![obj, selectedSegment];
|
||||
action(selected)
|
||||
});
|
||||
self.handler = Some(handler);
|
||||
}
|
||||
|
||||
/// Call this to set the background color for the backing layer.
|
||||
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||
let color: id = color.as_ref().into();
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let cell: id = msg_send![obj, cell];
|
||||
let _: () = msg_send![cell, setBackgroundColor: color];
|
||||
});
|
||||
}
|
||||
|
||||
/// Set a key to be bound to this button. When the key is pressed, the action coupled to this
|
||||
/// button will fire.
|
||||
pub fn set_key_equivalent<'a, K>(&self, key: K)
|
||||
where
|
||||
K: Into<Key<'a>>
|
||||
{
|
||||
let key: Key<'a> = key.into();
|
||||
|
||||
self.objc.with_mut(|obj| {
|
||||
let keychar = match key {
|
||||
Key::Char(s) => NSString::new(s),
|
||||
Key::Delete => NSString::new("\u{08}")
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![obj, setKeyEquivalent:&*keychar];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the text color for this button.
|
||||
///
|
||||
/// On appkit, this is done by way of an `AttributedString` under the hood.
|
||||
pub fn set_text_color<C: AsRef<Color>>(&self, color: C) {
|
||||
#[cfg(feature = "appkit")]
|
||||
self.objc.with_mut(move |obj| unsafe {
|
||||
let text: id = msg_send![obj, attributedTitle];
|
||||
let len: isize = msg_send![text, length];
|
||||
|
||||
let mut attr_str = AttributedString::wrap(text);
|
||||
attr_str.set_text_color(color.as_ref(), 0..len);
|
||||
|
||||
let _: () = msg_send![obj, setAttributedTitle:&*attr_str];
|
||||
});
|
||||
}
|
||||
|
||||
// @TODO: Figure out how to handle oddities like this.
|
||||
/// For buttons on appkit, one might need to disable the border. This does that.
|
||||
#[cfg(feature = "appkit")]
|
||||
pub fn set_bordered(&self, is_bordered: bool) {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setBordered:match is_bordered {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the font for this button.
|
||||
pub fn set_font<F: AsRef<Font>>(&self, font: F) {
|
||||
let font = font.as_ref().clone();
|
||||
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setFont:&*font];
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets how the control should draw a focus ring when a user is focused on it.
|
||||
///
|
||||
/// This is an appkit-only method.
|
||||
#[cfg(feature = "appkit")]
|
||||
pub fn set_focus_ring_type(&self, focus_ring_type: FocusRingType) {
|
||||
let ring_type: NSUInteger = focus_ring_type.into();
|
||||
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setFocusRingType: ring_type];
|
||||
});
|
||||
}
|
||||
|
||||
/// Toggles the highlighted status of the button.
|
||||
pub fn set_highlighted(&self, highlight: bool) {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, highlight:match highlight {
|
||||
true => YES,
|
||||
false => NO
|
||||
}];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjcAccess for SegmentedControl {
|
||||
fn with_backing_obj_mut<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
|
||||
fn get_from_backing_obj<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||
self.objc.get(handler)
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for SegmentedControl {}
|
||||
impl Control for SegmentedControl {}
|
||||
|
||||
impl ObjcAccess for &SegmentedControl {
|
||||
fn with_backing_obj_mut<F: Fn(id)>(&self, handler: F) {
|
||||
self.objc.with_mut(handler);
|
||||
}
|
||||
|
||||
fn get_from_backing_obj<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||
self.objc.get(handler)
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for &SegmentedControl {}
|
||||
impl Control for &SegmentedControl {}
|
||||
|
||||
impl Drop for SegmentedControl {
|
||||
/// Nils out references on the Objective-C side and removes this from the backing view.
|
||||
// Just to be sure, let's... nil these out. They should be weak references,
|
||||
// but I'd rather be paranoid and remove them later.
|
||||
fn drop(&mut self) {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setTarget: nil];
|
||||
let _: () = msg_send![obj, setAction: nil];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers an `NSButton` subclass, and configures it to hold some ivars
|
||||
/// for various things we need to store.
|
||||
fn register_class() -> *const Class {
|
||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
INIT.call_once(|| unsafe {
|
||||
let superclass = class!(NSSegmentedControl);
|
||||
let decl = ClassDecl::new("RSTSegmentedControl", superclass).unwrap();
|
||||
VIEW_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe { VIEW_CLASS }
|
||||
}
|
|
@ -10,6 +10,7 @@ use objc::runtime::Object;
|
|||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::{Id, ShareId};
|
||||
|
||||
use crate::appkit::segmentedcontrol::SegmentedControl;
|
||||
use crate::button::{BezelStyle, Button};
|
||||
use crate::foundation::{id, NSString, NO, YES};
|
||||
use crate::image::Image;
|
||||
|
@ -21,6 +22,7 @@ pub struct ToolbarItem {
|
|||
pub identifier: String,
|
||||
pub objc: Id<Object>,
|
||||
pub button: Option<Button>,
|
||||
pub segmented_control: Option<SegmentedControl>,
|
||||
pub image: Option<Image>,
|
||||
handler: Option<TargetActionHandler>
|
||||
}
|
||||
|
@ -42,6 +44,7 @@ impl ToolbarItem {
|
|||
identifier,
|
||||
objc,
|
||||
button: None,
|
||||
segmented_control: None,
|
||||
image: None,
|
||||
handler: None
|
||||
}
|
||||
|
@ -52,6 +55,7 @@ impl ToolbarItem {
|
|||
identifier: "".to_string(),
|
||||
objc: unsafe { Id::from_retained_ptr(item) },
|
||||
button: None,
|
||||
segmented_control: None,
|
||||
image: None,
|
||||
handler: None
|
||||
}
|
||||
|
@ -76,6 +80,15 @@ impl ToolbarItem {
|
|||
self.button = Some(button);
|
||||
}
|
||||
|
||||
/// Sets and takes ownership of the segmented control for this item.
|
||||
pub fn set_segmented_control(&mut self, control: SegmentedControl) {
|
||||
control.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![&*self.objc, setView: obj];
|
||||
});
|
||||
|
||||
self.segmented_control = Some(control);
|
||||
}
|
||||
|
||||
/// Sets and takes ownership of the image for this toolbar item.
|
||||
pub fn set_image(&mut self, image: Image) {
|
||||
unsafe {
|
||||
|
@ -102,7 +115,7 @@ impl ToolbarItem {
|
|||
}
|
||||
|
||||
/// Sets an action on this item.
|
||||
pub fn set_action<F: Fn() + Send + Sync + 'static>(&mut self, action: F) {
|
||||
pub fn set_action<F: Fn(*const Object) + Send + Sync + 'static>(&mut self, action: F) {
|
||||
let handler = TargetActionHandler::new(&*self.objc, action);
|
||||
self.handler = Some(handler);
|
||||
}
|
||||
|
|
|
@ -295,6 +295,14 @@ impl<T> Window<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Used for setting a toolbar on this window.
|
||||
pub fn toolbar(&self) -> ShareId<Object> {
|
||||
unsafe {
|
||||
let o: *mut Object = msg_send![&*self.objc, toolbar];
|
||||
ShareId::from_ptr(o)
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggles whether the toolbar is shown for this window. Has no effect if no toolbar exists on
|
||||
/// this window.
|
||||
pub fn toggle_toolbar_shown(&self) {
|
||||
|
@ -303,6 +311,14 @@ impl<T> Window<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the toolbar style
|
||||
pub fn set_toolbar_style(&self, style: WindowToolbarStyle) {
|
||||
let style: NSUInteger = style.into();
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setToolbarStyle: style];
|
||||
}
|
||||
}
|
||||
|
||||
/// Set whether the toolbar toggle button is shown. Has no effect if no toolbar exists on this
|
||||
/// window.
|
||||
pub fn set_shows_toolbar_button(&self, shows: bool) {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
//! let mut button = Button::new("My button title");
|
||||
//! button.set_key_equivalent("c");
|
||||
//!
|
||||
//! button.set_action(|| {
|
||||
//! button.set_action(|_| {
|
||||
//! println!("My button was clicked.");
|
||||
//! });
|
||||
//! let my_view : View<()> = todo!();
|
||||
|
@ -58,7 +58,7 @@ mod enums;
|
|||
/// let mut button = Button::new("My button title");
|
||||
/// button.set_key_equivalent("c");
|
||||
///
|
||||
/// button.set_action(|| {
|
||||
/// button.set_action(|_| {
|
||||
/// println!("My button was clicked.");
|
||||
/// });
|
||||
/// let my_view : View<()> = todo!();
|
||||
|
@ -212,7 +212,7 @@ impl Button {
|
|||
|
||||
/// Attaches a callback for button press events. Don't get too creative now...
|
||||
/// best just to message pass or something.
|
||||
pub fn set_action<F: Fn() + Send + Sync + 'static>(&mut self, action: F) {
|
||||
pub fn set_action<F: Fn(*const Object) + Send + Sync + 'static>(&mut self, action: F) {
|
||||
// @TODO: This probably isn't ideal but gets the job done for now; needs revisiting.
|
||||
let this = self.objc.get(|obj| unsafe { ShareId::from_ptr(msg_send![obj, self]) });
|
||||
let handler = TargetActionHandler::new(&*this, action);
|
||||
|
|
|
@ -74,36 +74,62 @@ impl MacSystemIcon {
|
|||
#[derive(Debug)]
|
||||
pub enum SFSymbol {
|
||||
AtSymbol,
|
||||
ArrowClockwise,
|
||||
Bell,
|
||||
BellFill,
|
||||
BellBadge,
|
||||
BellBadgeFill,
|
||||
GearShape,
|
||||
FolderFilled,
|
||||
ListAndFilm,
|
||||
PaperPlane,
|
||||
PaperPlaneFilled,
|
||||
Plus,
|
||||
Minus,
|
||||
Message,
|
||||
MessageFill,
|
||||
MessageBadge,
|
||||
MessageBadgeFill,
|
||||
MessageBadgeFilledFill,
|
||||
PersonCropCircle,
|
||||
SliderVertical3,
|
||||
SquareAndArrowUpOnSquare,
|
||||
SquareAndArrowUpOnSquareFill,
|
||||
SquareAndArrowDownOnSquare,
|
||||
SquareAndArrowDownOnSquareFill,
|
||||
SquareDashed
|
||||
SquareDashed,
|
||||
SquareAndPencil
|
||||
}
|
||||
|
||||
impl SFSymbol {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::AtSymbol => "at",
|
||||
Self::ArrowClockwise => "arrow.clockwise",
|
||||
Self::GearShape => "gearshape",
|
||||
Self::FolderFilled => "folder.fill",
|
||||
Self::ListAndFilm => "list.and.film",
|
||||
Self::Bell => "bell",
|
||||
Self::BellFill => "bell.fill",
|
||||
Self::BellBadge => "bell.badge",
|
||||
Self::BellBadgeFill => "bell.badge.fill",
|
||||
Self::PaperPlane => "paperplane",
|
||||
Self::PaperPlaneFilled => "paperplane.fill",
|
||||
Self::Plus => "plus",
|
||||
Self::Minus => "minus",
|
||||
Self::Message => "message",
|
||||
Self::MessageFill => "message.fill",
|
||||
Self::MessageBadge => "message.badge",
|
||||
Self::MessageBadgeFill => "message.badge.fill",
|
||||
Self::MessageBadgeFilledFill => "message.badge.filled.fill",
|
||||
Self::PersonCropCircle => "person.crop.circle",
|
||||
Self::SliderVertical3 => "slider.vertical.3",
|
||||
Self::SquareAndArrowUpOnSquare => "square.and.arrow.up.on.square",
|
||||
Self::SquareAndArrowUpOnSquareFill => "square.and.arrow.up.on.square.fill",
|
||||
Self::SquareAndArrowDownOnSquare => "square.and.arrow.down.on.square",
|
||||
Self::SquareAndArrowDownOnSquareFill => "square.and.arrow.down.on.square.fill",
|
||||
Self::SquareDashed => "square.dashed"
|
||||
Self::SquareDashed => "square.dashed",
|
||||
Self::SquareAndPencil => "square.and.pencil"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use core_graphics::{
|
|||
};
|
||||
|
||||
use super::icons::*;
|
||||
use crate::foundation::{id, NSData, NSString, NO, YES};
|
||||
use crate::foundation::{id, NSData, NSString, NO, NSURL, YES};
|
||||
use crate::utils::os;
|
||||
|
||||
/// Specifies resizing behavior for image drawing.
|
||||
|
@ -146,6 +146,14 @@ impl Image {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn with_contents_of_url(url: NSURL) -> Self {
|
||||
Image(unsafe {
|
||||
let alloc: id = msg_send![Self::class(), alloc];
|
||||
ShareId::from_ptr(msg_send![alloc, initWithContentsOfURL: url.objc])
|
||||
})
|
||||
}
|
||||
|
||||
/// Given a Vec of data, will transform it into an Image by passing it through NSData.
|
||||
/// This can be useful for when you need to include_bytes!() something into your binary.
|
||||
pub fn with_data(data: &[u8]) -> Self {
|
||||
|
|
|
@ -27,7 +27,7 @@ pub static ACTION_CALLBACK_PTR: &str = "rstTargetActionPtr";
|
|||
/// Point is, Button aren't created that much in the grand scheme of things,
|
||||
/// and the heap isn't our enemy in a GUI framework anyway. If someone knows
|
||||
/// a better way to do this that doesn't require double-boxing, I'm all ears.
|
||||
pub struct Action(Box<dyn Fn() + Send + Sync + 'static>);
|
||||
pub struct Action(Box<dyn Fn(*const Object) + Send + Sync + 'static>);
|
||||
|
||||
impl fmt::Debug for Action {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
@ -51,7 +51,7 @@ pub struct TargetActionHandler {
|
|||
|
||||
impl TargetActionHandler {
|
||||
/// Returns a new TargetEventHandler.
|
||||
pub fn new<F: Fn() + Send + Sync + 'static>(control: &Object, action: F) -> Self {
|
||||
pub fn new<F: Fn(*const Object) + Send + Sync + 'static>(control: &Object, action: F) -> Self {
|
||||
let block = Box::new(Action(Box::new(action)));
|
||||
let ptr = Box::into_raw(block);
|
||||
|
||||
|
@ -74,9 +74,9 @@ impl TargetActionHandler {
|
|||
}
|
||||
|
||||
/// This will fire for an NSButton callback.
|
||||
extern "C" fn perform<F: Fn() + 'static>(this: &mut Object, _: Sel, _sender: id) {
|
||||
extern "C" fn perform<F: Fn(*const Object) + 'static>(this: &mut Object, _: Sel, sender: id) {
|
||||
let action = load::<Action>(this, ACTION_CALLBACK_PTR);
|
||||
(action.0)();
|
||||
(action.0)(sender.cast_const());
|
||||
}
|
||||
|
||||
/// Due to the way that Rust and Objective-C live... very different lifestyles,
|
||||
|
@ -91,7 +91,7 @@ extern "C" fn perform<F: Fn() + 'static>(this: &mut Object, _: Sel, _sender: id)
|
|||
/// The `NSButton` owns this object on instantiation, and will release it
|
||||
/// on drop. We handle the heap copy on the Rust side, so setting the block
|
||||
/// is just an ivar.
|
||||
pub(crate) fn register_invoker_class<F: Fn() + 'static>() -> *const Class {
|
||||
pub(crate) fn register_invoker_class<F: Fn(*const Object) + 'static>() -> *const Class {
|
||||
load_or_register_class("NSObject", "RSTTargetActionHandler", |decl| unsafe {
|
||||
decl.add_ivar::<usize>(ACTION_CALLBACK_PTR);
|
||||
decl.add_method(sel!(perform:), perform::<F> as extern "C" fn(&mut Object, _, id));
|
||||
|
|
|
@ -140,7 +140,7 @@ impl Select {
|
|||
/// Really, this is not ideal.
|
||||
///
|
||||
/// I cannot stress this enough.
|
||||
pub fn set_action<F: Fn() + Send + Sync + 'static>(&mut self, action: F) {
|
||||
pub fn set_action<F: Fn(*const Object) + Send + Sync + 'static>(&mut self, action: F) {
|
||||
// @TODO: This probably isn't ideal but gets the job done for now; needs revisiting.
|
||||
let this = self.objc.get(|obj| unsafe { ShareId::from_ptr(msg_send![obj, self]) });
|
||||
let handler = TargetActionHandler::new(&*this, action);
|
||||
|
|
|
@ -130,7 +130,7 @@ impl Switch {
|
|||
|
||||
/// Attaches a callback for button press events. Don't get too creative now...
|
||||
/// best just to message pass or something.
|
||||
pub fn set_action<F: Fn() + Send + Sync + 'static>(&mut self, action: F) {
|
||||
pub fn set_action<F: Fn(*const Object) + Send + Sync + 'static>(&mut self, action: F) {
|
||||
// @TODO: This probably isn't ideal but gets the job done for now; needs revisiting.
|
||||
let this = self.objc.get(|obj| unsafe { ShareId::from_ptr(msg_send![obj, self]) });
|
||||
let handler = TargetActionHandler::new(&*this, action);
|
||||
|
|
Loading…
Reference in a new issue