cacao/src/appkit/cursor.rs
2022-08-14 15:44:53 -04:00

172 lines
5.9 KiB
Rust

use objc::{class, msg_send, sel, sel_impl};
use crate::foundation::{id, NO, YES};
/// Represents a type of cursor that you can associate with mouse movement.
/// @TODO: Loading?
#[derive(Debug)]
pub enum CursorType {
/// A standard arrow.
Arrow,
/// Current Cusrosr
Current,
/// Current System cursor
CurrentSystem,
/// A crosshair.
Crosshair,
/// A closed hand, typically for mousedown and drag.
ClosedHand,
/// An open hand, typically for indicating draggable.
OpenHand,
/// A pointing hand, like clicking a link.
PointingHand,
/// Indicator that something can be resized to the left.
ResizeLeft,
/// Indicator that something can be resized to the right.
ResizeRight,
/// Indicator that something can be resized on the horizontal axis.
ResizeLeftRight,
/// Indicates that something can be resized up.
ResizeUp,
/// Indicates that something can be resized down.
ResizeDown,
/// Indicator that something can be resized on the vertical axis.
ResizeUpDown,
/// Otherwise known as the "poof" or "cloud" cursor. Indicates something will vanish, like
/// dragging into the Trash.
DisappearingItem,
/// Indicate an insertion point, like for text.
IBeam,
/// The vertical version of `CursorType::IBeam`.
IBeamVertical,
/// Indicates an operation is illegal.
OperationNotAllowed,
/// The drag link cursor.
DragLink,
/// Used for drag-and-drop usually, will displayu the standard "+" icon next to the cursor.
DragCopy,
/// Indicates a context menu will open.
ContextMenu
}
/// A wrapper around NSCursor.
///
/// You use then when you need to control how the cursor (pointer) should appear. Like `NSCursor`,
/// this is stack based - you push, and you pop. You are responsible for ensuring that this is
/// correctly popped!
///
/// For a very abbreviated example:
///
/// ```rust,no_run
/// use cacao::appkit::Cursor;
/// use cacao::appkit::CursorType;
/// use cacao::dragdrop::DragInfo;
/// use cacao::dragdrop::DragOperation;
/// use cacao::view::ViewDelegate;
/// struct MyView;
/// impl ViewDelegate for MyView {
/// const NAME: &'static str = "RootView";
/// fn dragging_entered(&self, _info: DragInfo) -> DragOperation {
/// Cursor::push(CursorType::DragCopy);
/// DragOperation::Copy
/// }
///
/// fn dragging_exited(&self, _info: DragInfo) {
/// Cursor::pop();
/// }
/// }
/// ```
///
/// This will show the "add files +" indicator when the user has entered the dragging threshold
/// with some items that trigger it, and undo the cursor when the user leaves (regardless of drop
/// status).
#[derive(Debug)]
pub struct Cursor;
impl Cursor {
/// Given a cursor type, will make it the system cursor.
/// The inverse of this call, which you should call when ready, is `pop()`.
pub fn push(cursor_type: CursorType) {
unsafe {
let cursor: id = match cursor_type {
CursorType::Arrow => msg_send![class!(NSCursor), arrowCursor],
CursorType::Current => msg_send![class!(NSCursor), currentCursor],
CursorType::CurrentSystem => msg_send![class!(NSCursor), currentSystemCursor],
CursorType::Crosshair => msg_send![class!(NSCursor), crosshairCursor],
CursorType::ClosedHand => msg_send![class!(NSCursor), closedHandCursor],
CursorType::OpenHand => msg_send![class!(NSCursor), openHandCursor],
CursorType::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
CursorType::ResizeLeft => msg_send![class!(NSCursor), resizeLeftCursor],
CursorType::ResizeRight => msg_send![class!(NSCursor), resizeRightCursor],
CursorType::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor],
CursorType::ResizeUp => msg_send![class!(NSCursor), resizeUpCursor],
CursorType::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor],
CursorType::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
CursorType::DisappearingItem => msg_send![class!(NSCursor), disappearingItemCursor],
CursorType::IBeam => msg_send![class!(NSCursor), IBeamCursor],
CursorType::IBeamVertical => msg_send![class!(NSCursor), IBeamCursorForVerticalLayout],
CursorType::OperationNotAllowed => msg_send![class!(NSCursor), operationNotAllowedCursor],
CursorType::DragLink => msg_send![class!(NSCursor), dragLinkCursor],
CursorType::DragCopy => msg_send![class!(NSCursor), dragCopyCursor],
CursorType::ContextMenu => msg_send![class!(NSCursor), contextualMenuCursor]
};
let _: () = msg_send![cursor, push];
}
}
/// Pops the current cursor off the cursor-stack. The inverse of push.
pub fn pop() {
unsafe {
let _: () = msg_send![class!(NSCursor), pop];
}
}
/// Hides the cursor. Part of a balanced call stack.
pub fn hide() {
unsafe {
let _: () = msg_send![class!(NSCursor), hide];
}
}
/// Un-hides the cursor. Part of a balanced call stack.
pub fn unhide() {
unsafe {
let _: () = msg_send![class!(NSCursor), unhide];
}
}
/// Sets the cursor to hidden, but will reveal it if the user moves the mouse.
///
/// Potentially useful for games and other immersive experiences.
///
/// If you use this, do _not_ use `unhide` - just call this with the inverted boolean value.
/// Trying to invert this with `unhide` will result in undefined system behavior.
pub fn set_hidden_until_mouse_moves(status: bool) {
unsafe {
let _: () = msg_send![class!(NSCursor), setHiddenUntilMouseMoves:match status {
true => YES,
false => NO
}];
}
}
}