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:
Benedikt Terhechte 2023-08-01 08:51:53 +02:00 committed by GitHub
parent 1b577506a7
commit e6696eaa3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 449 additions and 30 deletions

View file

@ -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.);

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
});

View file

@ -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);
}

View file

@ -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);
});

View file

@ -29,3 +29,4 @@ pub mod toolbar;
pub mod window;
pub mod haptics;
pub mod segmentedcontrol;

View 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 }
}

View file

@ -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);
}

View file

@ -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) {

View file

@ -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);

View file

@ -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"
}
}
}

View file

@ -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 {

View file

@ -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));

View file

@ -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);

View file

@ -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);