use objc::runtime::Object;
pub mod responder {
use super::Object;
#[cfg(not(test))]
use objc::{msg_send, sel, sel_impl};
#[cfg(not(test))]
pub unsafe fn make_first_responder(window: *mut Object, view: *mut Object) -> bool {
let success: bool = msg_send![window, makeFirstResponder: view];
success
}
#[cfg(not(test))]
pub unsafe fn first_responder(window: *mut Object) -> *mut Object {
msg_send![window, firstResponder]
}
#[cfg(not(test))]
pub unsafe fn accepts_first_responder(view: *mut Object) -> bool {
msg_send![view, acceptsFirstResponder]
}
#[cfg(not(test))]
pub unsafe fn resign_first_responder(view: *mut Object) -> bool {
msg_send![view, resignFirstResponder]
}
#[cfg(test)]
pub unsafe fn make_first_responder(_window: *mut Object, _view: *mut Object) -> bool {
true
}
#[cfg(test)]
pub unsafe fn first_responder(_window: *mut Object) -> *mut Object {
std::ptr::null_mut()
}
#[cfg(test)]
pub unsafe fn accepts_first_responder(_view: *mut Object) -> bool {
true
}
#[cfg(test)]
pub unsafe fn resign_first_responder(_view: *mut Object) -> bool {
true
}
}
pub mod view_properties {
use super::Object;
#[cfg(not(test))]
use objc::{msg_send, sel, sel_impl};
#[cfg(not(test))]
pub unsafe fn set_alpha(view: *mut Object, alpha: f64) {
let _: () = msg_send![view, setAlphaValue: alpha];
}
#[cfg(not(test))]
pub unsafe fn alpha(view: *mut Object) -> f64 {
msg_send![view, alphaValue]
}
#[cfg(not(test))]
pub unsafe fn set_hidden(view: *mut Object, hidden: bool) {
let _: () = msg_send![view, setHidden: hidden];
}
#[cfg(not(test))]
pub unsafe fn is_hidden(view: *mut Object) -> bool {
msg_send![view, isHidden]
}
#[cfg(not(test))]
pub unsafe fn set_frame(view: *mut Object, x: f64, y: f64, width: f64, height: f64) {
let frame = cocoa::foundation::NSRect {
origin: cocoa::foundation::NSPoint { x, y },
size: cocoa::foundation::NSSize { width, height },
};
let _: () = msg_send![view, setFrame: frame];
}
#[cfg(not(test))]
pub unsafe fn frame(view: *mut Object) -> (f64, f64, f64, f64) {
let frame: cocoa::foundation::NSRect = msg_send![view, frame];
(
frame.origin.x,
frame.origin.y,
frame.size.width,
frame.size.height,
)
}
#[cfg(not(test))]
pub unsafe fn bounds(view: *mut Object) -> (f64, f64, f64, f64) {
let bounds: cocoa::foundation::NSRect = msg_send![view, bounds];
(
bounds.origin.x,
bounds.origin.y,
bounds.size.width,
bounds.size.height,
)
}
#[cfg(not(test))]
pub unsafe fn set_corner_radius(view: *mut Object, radius: f64) {
let _: () = msg_send![view, setWantsLayer: true];
let layer: *mut Object = msg_send![view, layer];
if !layer.is_null() {
let _: () = msg_send![layer, setCornerRadius: radius];
}
}
#[cfg(not(test))]
pub unsafe fn set_background_color(view: *mut Object, r: f64, g: f64, b: f64, a: f64) {
let _: () = msg_send![view, setWantsLayer: true];
let layer: *mut Object = msg_send![view, layer];
if !layer.is_null() {
let color_space = objc::class!(NSColorSpace);
let srgb: *mut Object = msg_send![color_space, sRGBColorSpace];
let color_class = objc::class!(NSColor);
let color: *mut Object = msg_send![
color_class,
colorWithColorSpace: srgb
components: [r, g, b, a].as_ptr()
count: 4_u64
];
let cg_color: *mut Object = msg_send![color, CGColor];
let _: () = msg_send![layer, setBackgroundColor: cg_color];
}
}
#[cfg(not(test))]
pub unsafe fn set_border(view: *mut Object, width: f64, r: f64, g: f64, b: f64, a: f64) {
let _: () = msg_send![view, setWantsLayer: true];
let layer: *mut Object = msg_send![view, layer];
if !layer.is_null() {
let _: () = msg_send![layer, setBorderWidth: width];
let color_space = objc::class!(NSColorSpace);
let srgb: *mut Object = msg_send![color_space, sRGBColorSpace];
let color_class = objc::class!(NSColor);
let color: *mut Object = msg_send![
color_class,
colorWithColorSpace: srgb
components: [r, g, b, a].as_ptr()
count: 4_u64
];
let cg_color: *mut Object = msg_send![color, CGColor];
let _: () = msg_send![layer, setBorderColor: cg_color];
}
}
#[cfg(not(test))]
pub unsafe fn set_shadow(
view: *mut Object,
radius: f64,
opacity: f32,
offset_x: f64,
offset_y: f64,
) {
let _: () = msg_send![view, setWantsLayer: true];
let layer: *mut Object = msg_send![view, layer];
if !layer.is_null() {
let _: () = msg_send![layer, setShadowRadius: radius];
let _: () = msg_send![layer, setShadowOpacity: opacity];
let size = cocoa::foundation::NSSize {
width: offset_x,
height: offset_y,
};
let _: () = msg_send![layer, setShadowOffset: size];
}
}
#[cfg(test)]
pub unsafe fn set_alpha(_view: *mut Object, _alpha: f64) {}
#[cfg(test)]
pub unsafe fn alpha(_view: *mut Object) -> f64 {
1.0
}
#[cfg(test)]
pub unsafe fn set_hidden(_view: *mut Object, _hidden: bool) {}
#[cfg(test)]
pub unsafe fn is_hidden(_view: *mut Object) -> bool {
false
}
#[cfg(test)]
pub unsafe fn set_frame(_view: *mut Object, _x: f64, _y: f64, _width: f64, _height: f64) {}
#[cfg(test)]
pub unsafe fn frame(_view: *mut Object) -> (f64, f64, f64, f64) {
(0.0, 0.0, 0.0, 0.0)
}
#[cfg(test)]
pub unsafe fn bounds(_view: *mut Object) -> (f64, f64, f64, f64) {
(0.0, 0.0, 0.0, 0.0)
}
#[cfg(test)]
pub unsafe fn set_corner_radius(_view: *mut Object, _radius: f64) {}
#[cfg(test)]
pub unsafe fn set_background_color(_view: *mut Object, _r: f64, _g: f64, _b: f64, _a: f64) {}
#[cfg(test)]
pub unsafe fn set_border(_view: *mut Object, _width: f64, _r: f64, _g: f64, _b: f64, _a: f64) {}
#[cfg(test)]
pub unsafe fn set_shadow(_view: *mut Object, _radius: f64, _opacity: f32, _x: f64, _y: f64) {}
}
pub mod window {
use super::Object;
#[cfg(not(test))]
use objc::{msg_send, sel, sel_impl};
#[cfg(not(test))]
pub unsafe fn set_level(window: *mut Object, level: i64) {
let _: () = msg_send![window, setLevel: level];
}
pub const NORMAL_LEVEL: i64 = 0;
pub const FLOATING_LEVEL: i64 = 3;
pub const MODAL_PANEL_LEVEL: i64 = 8;
pub const MAIN_MENU_LEVEL: i64 = 24;
#[cfg(not(test))]
pub unsafe fn make_key_and_order_front(window: *mut Object) {
let _: () = msg_send![window, makeKeyAndOrderFront: std::ptr::null_mut::<Object>()];
}
#[cfg(not(test))]
pub unsafe fn set_alpha(window: *mut Object, alpha: f64) {
let _: () = msg_send![window, setAlphaValue: alpha];
}
#[cfg(not(test))]
pub unsafe fn set_background_color(window: *mut Object, r: f64, g: f64, b: f64, a: f64) {
let color_space = objc::class!(NSColorSpace);
let srgb: *mut Object = msg_send![color_space, sRGBColorSpace];
let color_class = objc::class!(NSColor);
let color: *mut Object = msg_send![
color_class,
colorWithColorSpace: srgb
components: [r, g, b, a].as_ptr()
count: 4_u64
];
let _: () = msg_send![window, setBackgroundColor: color];
}
#[cfg(test)]
pub unsafe fn set_level(_window: *mut Object, _level: i64) {}
#[cfg(test)]
pub unsafe fn make_key_and_order_front(_window: *mut Object) {}
#[cfg(test)]
pub unsafe fn set_alpha(_window: *mut Object, _alpha: f64) {}
#[cfg(test)]
pub unsafe fn set_background_color(_window: *mut Object, _r: f64, _g: f64, _b: f64, _a: f64) {}
}
pub mod animation {
#[cfg(not(test))]
use super::Object;
#[cfg(not(test))]
use objc::{msg_send, sel, sel_impl};
#[cfg(not(test))]
pub fn begin_animation(duration: f64) {
unsafe {
let context_class = objc::class!(NSAnimationContext);
let _: () = msg_send![context_class, beginGrouping];
let context: *mut Object = msg_send![context_class, currentContext];
let _: () = msg_send![context, setDuration: duration];
}
}
#[cfg(not(test))]
pub fn end_animation() {
unsafe {
let context_class = objc::class!(NSAnimationContext);
let _: () = msg_send![context_class, endGrouping];
}
}
#[cfg(not(test))]
pub fn animate<F>(duration: f64, animations: F)
where
F: FnOnce(),
{
begin_animation(duration);
animations();
end_animation();
}
#[cfg(test)]
pub fn begin_animation(_duration: f64) {}
#[cfg(test)]
pub fn end_animation() {}
#[cfg(test)]
pub fn animate<F>(_duration: f64, animations: F)
where
F: FnOnce(),
{
animations();
}
}
pub mod pasteboard {
use crate::error::Result;
#[cfg(not(test))]
use super::Object;
#[cfg(not(test))]
use objc::{msg_send, sel, sel_impl};
#[cfg(not(test))]
pub fn copy_text(text: &str) -> Result<()> {
unsafe {
let pb_class = objc::class!(NSPasteboard);
let pb: *mut Object = msg_send![pb_class, generalPasteboard];
let _: () = msg_send![pb, clearContents];
let cstr = std::ffi::CString::new(text)?;
let ns_str: *mut Object = msg_send![
objc::class!(NSString),
stringWithUTF8String: cstr.as_ptr()
];
let array_class = objc::class!(NSArray);
let array: *mut Object = msg_send![array_class, arrayWithObject: ns_str];
let _: bool = msg_send![pb, writeObjects: array];
}
Ok(())
}
#[cfg(not(test))]
pub fn get_text() -> Result<String> {
unsafe {
let pb_class = objc::class!(NSPasteboard);
let pb: *mut Object = msg_send![pb_class, generalPasteboard];
let ns_str: *mut Object =
msg_send![pb, stringForType: objc::class!(NSPasteboardTypeString)];
if ns_str.is_null() {
return Ok(String::new());
}
let cstr: *const i8 = msg_send![ns_str, UTF8String];
let rust_str = std::ffi::CStr::from_ptr(cstr)
.to_string_lossy()
.into_owned();
Ok(rust_str)
}
}
#[cfg(test)]
pub fn copy_text(_text: &str) -> Result<()> {
Ok(())
}
#[cfg(test)]
pub fn get_text() -> Result<String> {
Ok(String::new())
}
}
#[cfg(test)]
mod mock_tests {
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use super::Object;
use super::{animation, pasteboard, responder, view_properties, window};
#[test]
fn pasteboard_mock_copy_and_get() {
assert!(pasteboard::copy_text("clip").is_ok());
assert_eq!(pasteboard::get_text().unwrap(), "");
}
#[test]
fn animation_mock_invokes_closure() {
let n = Arc::new(AtomicUsize::new(0));
let c = n.clone();
animation::animate(0.0, move || {
c.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(n.load(Ordering::SeqCst), 1);
}
#[test]
fn view_responder_window_stubs_accept_null() {
let p: *mut Object = std::ptr::null_mut();
unsafe {
view_properties::set_alpha(p, 0.5);
assert_eq!(view_properties::alpha(p), 1.0);
window::set_level(p, window::NORMAL_LEVEL);
assert!(responder::make_first_responder(p, p));
assert!(responder::accepts_first_responder(p));
assert!(responder::resign_first_responder(p));
}
}
}