use core::mem;
use core::{ffi, fmt, ptr};
use objc::rc::{autoreleasepool, AutoreleasePool, Id, Owned, Shared};
use objc::runtime::{Object, BOOL, NO, YES};
use objc::{class, msg_send, sel};
use objc_foundation::{INSString, NSString};
use std::ptr::NonNull;
use super::menu::Menu;
use super::util::NSInteger;
struct Target; struct ActionSelector; struct Image;
#[derive(Debug, PartialEq)]
pub enum MenuItemState {
On,
Mixed,
Off,
}
#[doc(alias = "NSMenuItem")]
#[repr(C)]
pub struct MenuItem {
_priv: [u8; 0],
}
unsafe impl objc::RefEncode for MenuItem {
const ENCODING_REF: objc::Encoding<'static> = objc::Encoding::Object;
}
unsafe impl objc::Message for MenuItem {}
unsafe impl Send for MenuItem {}
unsafe impl Sync for MenuItem {}
impl MenuItem {
fn alloc() -> *mut Self {
unsafe { msg_send![class!(NSMenuItem), alloc] }
}
pub(super) fn new_empty() -> Id<Self, Owned> {
let ptr = Self::alloc();
unsafe { Id::new(msg_send![ptr, init]) }
}
#[doc(alias = "initWithTitle")]
#[doc(alias = "initWithTitle:action:keyEquivalent:")]
pub fn new(
title: &str,
key_equivalent: &str,
action: Option<NonNull<ffi::c_void>>,
) -> Id<Self, Owned> {
let title = NSString::from_str(title);
let key_equivalent = NSString::from_str(key_equivalent);
let action = if let Some(p) = action {
p.as_ptr()
} else {
ptr::null_mut()
};
let ptr = Self::alloc();
unsafe {
Id::new(msg_send![
ptr,
initWithTitle: &*title
action: action
keyEquivalent: &*key_equivalent
])
}
}
#[doc(alias = "separatorItem")]
pub fn new_separator() -> Id<Self, Owned> {
let ptr: *mut Self = unsafe { msg_send![class!(NSMenuItem), separatorItem] };
unsafe { Id::retain(NonNull::new_unchecked(ptr)) }
}
fn enabled(&self) -> bool {
unimplemented!()
}
#[doc(alias = "setEnabled")]
#[doc(alias = "setEnabled:")]
fn set_enabled(&mut self, state: bool) {
unimplemented!()
}
pub fn hidden(&self) -> bool {
let hidden: BOOL = unsafe { msg_send![self, isHidden] };
hidden != NO
}
#[doc(alias = "setHidden")]
#[doc(alias = "setHidden:")]
pub fn set_hidden(&mut self, hidden: bool) {
let hidden: BOOL = if hidden { YES } else { NO };
unsafe { msg_send![self, setHidden: hidden] }
}
fn target(&self) -> Target {
unimplemented!()
}
#[doc(alias = "setTarget")]
#[doc(alias = "setTarget:")]
fn set_target(&mut self, target: Target) {
unimplemented!()
}
fn action(&self) -> ActionSelector {
unimplemented!()
}
#[doc(alias = "setAction")]
#[doc(alias = "setAction:")]
fn set_action(&mut self, action: ActionSelector) {
unimplemented!()
}
pub fn title<'p>(&self, pool: &'p AutoreleasePool) -> &'p str {
let title: &NSString = unsafe { msg_send![self, title] };
title.as_str(pool)
}
#[doc(alias = "setTitle")]
#[doc(alias = "setTitle:")]
pub fn set_title(&mut self, title: &str) {
let title = NSString::from_str(title);
unsafe { msg_send![self, setTitle: &*title] }
}
fn tag(&self) -> isize {
unimplemented!()
}
#[doc(alias = "setTag")]
#[doc(alias = "setTag:")]
fn set_tag(&mut self, tag: isize) {
unimplemented!()
}
pub fn state(&self) -> MenuItemState {
let state: NSInteger = unsafe { msg_send![self, state] };
match state {
1 => MenuItemState::On,
-1 => MenuItemState::Mixed,
0 => MenuItemState::Off,
_ => unreachable!(),
}
}
#[doc(alias = "setState")]
#[doc(alias = "setState:")]
pub fn set_state(&mut self, state: MenuItemState) {
let state: NSInteger = match state {
MenuItemState::On => 1,
MenuItemState::Mixed => -1,
MenuItemState::Off => 0,
};
unsafe { msg_send![self, setState: state] }
}
fn image(&self) -> Option<&Image> {
unimplemented!()
}
#[doc(alias = "setImage")]
#[doc(alias = "setImage:")]
fn set_image(&mut self, image: Option<&Image>) {
unimplemented!()
}
#[doc(alias = "onStateImage")]
#[doc(alias = "offStateImage")]
#[doc(alias = "mixedStateImage")]
fn image_for_state<'p>(
&self,
pool: &'p AutoreleasePool,
state: MenuItemState,
) -> Option<&'p Image> {
unimplemented!()
}
#[doc(alias = "setOnStateImage")]
#[doc(alias = "setOnStateImage:")]
#[doc(alias = "setOffStateImage")]
#[doc(alias = "setOffStateImage:")]
#[doc(alias = "setMixedStateImage")]
#[doc(alias = "setMixedStateImage:")]
fn set_image_for_state(&mut self, state: MenuItemState, image: Option<&Image>) {
unimplemented!()
}
pub fn submenu<'p>(&self, pool: &'p AutoreleasePool) -> Option<&'p Menu> {
unsafe { msg_send![self, submenu] }
}
#[doc(alias = "setSubmenu")]
#[doc(alias = "setSubmenu:")]
pub fn set_submenu(&mut self, mut menu: Option<Id<Menu, Owned>>) -> Option<Id<Menu, Shared>> {
let ptr = match menu {
Some(ref mut menu) => &mut **menu as *mut Menu,
None => ptr::null_mut(),
};
let _: () = unsafe { msg_send![self, setSubmenu: ptr] };
menu.map(|obj| obj.into())
}
#[doc(alias = "hasSubmenu")]
fn has_submenu(&self) -> bool {
unimplemented!()
}
#[doc(alias = "parentItem")]
fn parent_item<'p>(&self, pool: &'p AutoreleasePool) -> Option<&'p MenuItem> {
unimplemented!()
}
#[doc(alias = "isSeparatorItem")]
pub fn separator(&self) -> bool {
let is_separator: BOOL = unsafe { msg_send![self, isSeparatorItem] };
is_separator != NO
}
#[doc(alias = "menu")]
fn parent_menu<'p>(&self, pool: &'p AutoreleasePool) -> &'p Menu {
unimplemented!()
}
#[doc(alias = "setMenu")]
#[doc(alias = "setMenu:")]
fn set_parent_menu(&mut self, menu: &mut Menu) {
unimplemented!()
}
fn alternate(&self) -> bool {
unimplemented!()
}
#[doc(alias = "setAlternate")]
#[doc(alias = "setAlternate:")]
fn set_alternate(&mut self, alternate: bool) {
unimplemented!()
}
#[doc(alias = "indentationLevel")]
fn indentation_level(&self) -> isize {
unimplemented!()
}
#[doc(alias = "setIndentationLevel")]
#[doc(alias = "setIndentationLevel:")]
fn set_indentation_level(&mut self, level: isize) {
unimplemented!()
}
#[doc(alias = "toolTip")]
fn tooltip(&self) -> &str {
unimplemented!()
}
#[doc(alias = "setToolTip")]
#[doc(alias = "setToolTip:")]
fn set_tooltip(&mut self, tooltip: &str) {
unimplemented!()
}
#[doc(alias = "representedObject")]
fn represented_object(&self) -> *const Object {
unimplemented!()
}
#[doc(alias = "setRepresentedObject")]
#[doc(alias = "setRepresentedObject:")]
fn set_represented_object(&mut self, tooltip: *mut Object) {
unimplemented!()
}
fn view(&self) -> *const Object {
unimplemented!()
}
#[doc(alias = "setView")]
#[doc(alias = "setView:")]
fn set_view(&mut self, tooltip: *mut Object) {
unimplemented!()
}
#[doc(alias = "isHighlighted")]
fn highlighted(&self) -> bool {
unimplemented!()
}
}
impl PartialEq for MenuItem {
fn eq(&self, other: &Self) -> bool {
self as *const Self == other as *const Self
}
}
impl fmt::Debug for MenuItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
autoreleasepool(|pool| {
f.debug_struct("MenuItem")
.field("id", &(self as *const Self))
.field("separator", &self.separator())
.field("title", &self.title(pool))
.field("hidden", &self.hidden())
.field("state", &self.state())
.field("submenu", &self.submenu(pool))
.finish()
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_util::STRINGS;
fn for_each_item(_pool: &AutoreleasePool, mut f: impl FnMut(&mut MenuItem)) {
f(&mut *MenuItem::new_separator());
f(&mut *MenuItem::new_empty());
f(&mut *MenuItem::new("", "", None));
}
#[test]
fn test_hidden() {
autoreleasepool(|pool| {
for_each_item(pool, |item| {
assert!(!item.hidden());
item.set_hidden(true);
assert!(item.hidden());
item.set_hidden(false);
assert!(!item.hidden());
})
});
}
#[test]
fn test_title() {
autoreleasepool(|pool| {
for_each_item(pool, |item| {
STRINGS.iter().for_each(|&title| {
item.set_title(title);
assert_eq!(item.title(pool), title);
});
});
});
}
#[test]
fn test_title_init() {
autoreleasepool(|pool| {
STRINGS.iter().for_each(|&title| {
let item = MenuItem::new(title, "", None);
assert_eq!(item.title(pool), title);
});
});
}
#[test]
fn test_title_default() {
autoreleasepool(|pool| {
let item = MenuItem::new_empty();
assert_eq!(item.title(pool), "NSMenuItem");
let item = MenuItem::new_separator();
assert_eq!(item.title(pool), "");
});
}
#[test]
fn test_separator() {
autoreleasepool(|_| {
let item = MenuItem::new_separator();
assert!(item.separator());
let item = MenuItem::new_empty();
assert!(!item.separator());
let item = MenuItem::new("", "", None);
assert!(!item.separator());
});
}
#[test]
fn test_state() {
autoreleasepool(|pool| {
for_each_item(pool, |item| {
assert_eq!(item.state(), MenuItemState::Off);
item.set_state(MenuItemState::On);
assert_eq!(item.state(), MenuItemState::On);
item.set_state(MenuItemState::Mixed);
assert_eq!(item.state(), MenuItemState::Mixed);
item.set_state(MenuItemState::Off);
assert_eq!(item.state(), MenuItemState::Off);
});
});
}
#[test]
fn test_submenu() {
autoreleasepool(|pool| {
for_each_item(pool, |item| {
assert!(item.submenu(pool).is_none());
let menu = Menu::new();
let menu = item.set_submenu(Some(menu));
assert_eq!(item.submenu(pool), menu.as_deref());
item.set_submenu(None);
assert!(item.submenu(pool).is_none());
})
});
}
}