2020-04-05 00:09:17 -07:00
|
|
|
//! This module does one specific thing: register a custom `NSView` class that's... brought to the
|
|
|
|
//! modern era.
|
|
|
|
//!
|
|
|
|
//! I kid, I kid.
|
|
|
|
//!
|
|
|
|
//! It just enforces that coordinates are judged from the top-left, which is what most people look
|
|
|
|
//! 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::sync::Once;
|
|
|
|
|
|
|
|
use objc::declare::ClassDecl;
|
|
|
|
use objc::runtime::{Class, Object, Sel, BOOL};
|
2021-02-12 17:57:06 -08:00
|
|
|
use objc::{class, msg_send, sel, sel_impl};
|
2020-04-05 00:09:17 -07:00
|
|
|
use objc_id::Id;
|
|
|
|
|
|
|
|
use crate::dragdrop::DragInfo;
|
2022-07-15 16:14:02 +02:00
|
|
|
use crate::foundation::{id, load_or_register_class, nil, NSUInteger, NO, YES};
|
2020-04-05 00:09:17 -07:00
|
|
|
use crate::utils::load;
|
2022-07-15 16:14:02 +02:00
|
|
|
use crate::view::{ViewDelegate, BACKGROUND_COLOR, VIEW_DELEGATE_PTR};
|
2020-04-05 00:09:17 -07:00
|
|
|
|
|
|
|
/// Enforces normalcy, or: a needlessly cruel method in terms of the name. You get the idea though.
|
2022-07-15 16:14:02 +02:00
|
|
|
extern "C" fn enforce_normalcy(_: &Object, _: Sel) -> BOOL {
|
2020-04-05 00:09:17 -07:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Called when a drag/drop operation has entered this view.
|
2022-07-15 16:14:02 +02:00
|
|
|
extern "C" fn dragging_entered<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) -> NSUInteger {
|
2020-04-05 00:09:17 -07:00
|
|
|
let view = load::<T>(this, VIEW_DELEGATE_PTR);
|
|
|
|
view.dragging_entered(DragInfo {
|
|
|
|
info: unsafe { Id::from_ptr(info) }
|
2022-07-15 16:14:02 +02:00
|
|
|
})
|
|
|
|
.into()
|
2020-04-05 00:09:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Called when a drag/drop operation has entered this view.
|
2022-07-15 16:14:02 +02:00
|
|
|
extern "C" fn prepare_for_drag_operation<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) -> BOOL {
|
2020-04-05 00:09:17 -07:00
|
|
|
let view = load::<T>(this, VIEW_DELEGATE_PTR);
|
2022-07-10 17:15:29 +02:00
|
|
|
|
2020-04-05 00:09:17 -07:00
|
|
|
match view.prepare_for_drag_operation(DragInfo {
|
|
|
|
info: unsafe { Id::from_ptr(info) }
|
|
|
|
}) {
|
|
|
|
true => YES,
|
|
|
|
false => NO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Called when a drag/drop operation has entered this view.
|
2022-07-15 16:14:02 +02:00
|
|
|
extern "C" fn perform_drag_operation<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) -> BOOL {
|
2020-04-05 00:09:17 -07:00
|
|
|
let view = load::<T>(this, VIEW_DELEGATE_PTR);
|
2022-07-10 17:15:29 +02:00
|
|
|
|
2020-04-05 00:09:17 -07:00
|
|
|
match view.perform_drag_operation(DragInfo {
|
|
|
|
info: unsafe { Id::from_ptr(info) }
|
|
|
|
}) {
|
|
|
|
true => YES,
|
|
|
|
false => NO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Called when a drag/drop operation has entered this view.
|
2022-07-15 16:14:02 +02:00
|
|
|
extern "C" fn conclude_drag_operation<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) {
|
2020-04-05 00:09:17 -07:00
|
|
|
let view = load::<T>(this, VIEW_DELEGATE_PTR);
|
2022-07-10 17:15:29 +02:00
|
|
|
|
2020-04-05 00:09:17 -07:00
|
|
|
view.conclude_drag_operation(DragInfo {
|
|
|
|
info: unsafe { Id::from_ptr(info) }
|
2022-07-10 17:15:29 +02:00
|
|
|
});
|
2020-04-05 00:09:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Called when a drag/drop operation has entered this view.
|
2022-07-15 16:14:02 +02:00
|
|
|
extern "C" fn dragging_exited<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) {
|
2020-04-05 00:09:17 -07:00
|
|
|
let view = load::<T>(this, VIEW_DELEGATE_PTR);
|
2022-07-10 17:15:29 +02:00
|
|
|
|
2020-04-05 00:09:17 -07:00
|
|
|
view.dragging_exited(DragInfo {
|
|
|
|
info: unsafe { Id::from_ptr(info) }
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-02-12 17:57:06 -08:00
|
|
|
/// Called for layer updates.
|
2022-07-15 16:14:02 +02:00
|
|
|
extern "C" fn update_layer(this: &Object, _: Sel) {
|
2021-02-12 17:57:06 -08:00
|
|
|
unsafe {
|
|
|
|
let background_color: id = *this.get_ivar(BACKGROUND_COLOR);
|
|
|
|
|
|
|
|
if background_color != nil {
|
|
|
|
let layer: id = msg_send![this, layer];
|
|
|
|
let cg: id = msg_send![background_color, CGColor];
|
2022-07-15 16:14:02 +02:00
|
|
|
let _: () = msg_send![layer, setBackgroundColor: cg];
|
2021-02-12 17:57:06 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-05 00:09:17 -07:00
|
|
|
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
|
|
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
|
|
|
/// used if there's no delegates.
|
|
|
|
pub(crate) fn register_view_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!(NSView);
|
|
|
|
let mut decl = ClassDecl::new("RSTView", superclass).unwrap();
|
|
|
|
|
2022-07-15 16:14:02 +02:00
|
|
|
decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
|
|
|
decl.add_method(sel!(updateLayer), update_layer as extern "C" fn(&Object, _));
|
|
|
|
decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
2021-02-12 17:57:06 -08:00
|
|
|
|
|
|
|
decl.add_ivar::<id>(BACKGROUND_COLOR);
|
2022-07-10 17:15:29 +02:00
|
|
|
|
2020-04-05 00:09:17 -07:00
|
|
|
VIEW_CLASS = decl.register();
|
|
|
|
});
|
|
|
|
|
|
|
|
unsafe { VIEW_CLASS }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Injects an `NSView` subclass, with some callback and pointer ivars for what we
|
|
|
|
/// need to do.
|
2021-02-07 20:25:56 -08:00
|
|
|
pub(crate) fn register_view_class_with_delegate<T: ViewDelegate>(instance: &T) -> *const Class {
|
|
|
|
load_or_register_class("NSView", instance.subclass_name(), |decl| unsafe {
|
|
|
|
// A pointer to the ViewDelegate instance on the Rust side.
|
|
|
|
// It's expected that this doesn't move.
|
2020-04-05 00:09:17 -07:00
|
|
|
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
|
2021-02-12 17:57:06 -08:00
|
|
|
decl.add_ivar::<id>(BACKGROUND_COLOR);
|
2022-07-10 17:15:29 +02:00
|
|
|
|
2022-07-15 16:14:02 +02:00
|
|
|
decl.add_method(sel!(updateLayer), update_layer as extern "C" fn(&Object, _));
|
2021-02-12 17:57:06 -08:00
|
|
|
|
2022-07-15 16:14:02 +02:00
|
|
|
decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
2021-02-12 17:57:06 -08:00
|
|
|
|
2022-07-15 16:14:02 +02:00
|
|
|
decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
2020-04-05 00:09:17 -07:00
|
|
|
|
|
|
|
// Drag and drop operations (e.g, accepting files)
|
2021-02-07 20:25:56 -08:00
|
|
|
decl.add_method(
|
|
|
|
sel!(draggingEntered:),
|
2022-07-15 16:14:02 +02:00
|
|
|
dragging_entered::<T> as extern "C" fn(&mut Object, _, _) -> NSUInteger
|
2021-02-07 20:25:56 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(prepareForDragOperation:),
|
2022-07-15 16:14:02 +02:00
|
|
|
prepare_for_drag_operation::<T> as extern "C" fn(&mut Object, _, _) -> BOOL
|
2021-02-07 20:25:56 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(performDragOperation:),
|
2022-07-15 16:14:02 +02:00
|
|
|
perform_drag_operation::<T> as extern "C" fn(&mut Object, _, _) -> BOOL
|
2021-02-07 20:25:56 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(concludeDragOperation:),
|
2022-07-15 16:14:02 +02:00
|
|
|
conclude_drag_operation::<T> as extern "C" fn(&mut Object, _, _)
|
2021-02-07 20:25:56 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(draggingExited:),
|
2022-07-15 16:14:02 +02:00
|
|
|
dragging_exited::<T> as extern "C" fn(&mut Object, _, _)
|
2021-02-07 20:25:56 -08:00
|
|
|
);
|
|
|
|
})
|
2020-04-05 00:09:17 -07:00
|
|
|
}
|