use crate::enums::{Color, Font, LabelType};
use crate::prelude::*;
use crate::utils::FlString;
use fltk_sys::menu::*;
use std::{
ffi::{CStr, CString},
mem,
os::raw,
};
#[derive(Debug)]
pub struct MenuBar {
inner: *mut Fl_Menu_Bar,
tracker: *mut fltk_sys::fl::Fl_Widget_Tracker,
is_derived: bool,
}
crate::macros::widget::impl_widget_ext!(MenuBar, Fl_Menu_Bar);
crate::macros::widget::impl_widget_base!(MenuBar, Fl_Menu_Bar);
crate::macros::widget::impl_widget_default!(MenuBar);
crate::macros::menu::impl_menu_ext!(MenuBar, Fl_Menu_Bar);
#[derive(Debug)]
pub struct MenuButton {
inner: *mut Fl_Menu_Button,
tracker: *mut fltk_sys::fl::Fl_Widget_Tracker,
is_derived: bool,
}
crate::macros::widget::impl_widget_ext!(MenuButton, Fl_Menu_Button);
crate::macros::widget::impl_widget_base!(MenuButton, Fl_Menu_Button);
crate::macros::widget::impl_widget_default!(MenuButton);
crate::macros::menu::impl_menu_ext!(MenuButton, Fl_Menu_Button);
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MenuButtonType {
Popup1 = 1,
Popup2,
Popup12,
Popup3,
Popup13,
Popup23,
Popup123,
}
crate::macros::widget::impl_widget_type!(MenuButtonType);
impl MenuButton {
pub fn popup(&self) -> Option<MenuItem> {
assert!(!self.was_deleted());
if self.size() == 0 {
return None;
}
unsafe {
let ptr = Fl_Menu_Button_popup(self.inner);
if ptr.is_null() {
None
} else {
let item = MenuItem {
inner: ptr as *mut Fl_Menu_Item,
size: Fl_Menu_Item_children(ptr),
};
Some(item)
}
}
}
}
#[derive(Debug)]
pub struct Choice {
inner: *mut Fl_Choice,
tracker: *mut fltk_sys::fl::Fl_Widget_Tracker,
is_derived: bool,
}
crate::macros::widget::impl_widget_ext!(Choice, Fl_Choice);
crate::macros::widget::impl_widget_base!(Choice, Fl_Choice);
crate::macros::widget::impl_widget_default!(Choice);
crate::macros::menu::impl_menu_ext!(Choice, Fl_Choice);
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum WindowMenuStyle {
NoWindowMenu = 0,
TabbingModeNone,
TabbingModeAutomatic,
TabbingModePreferred,
}
#[derive(Debug)]
pub struct SysMenuBar {
inner: *mut Fl_Sys_Menu_Bar,
tracker: *mut fltk_sys::fl::Fl_Widget_Tracker,
is_derived: bool,
}
crate::macros::widget::impl_widget_ext!(SysMenuBar, Fl_Sys_Menu_Bar);
crate::macros::widget::impl_widget_base!(SysMenuBar, Fl_Sys_Menu_Bar);
crate::macros::widget::impl_widget_default!(SysMenuBar);
crate::macros::menu::impl_menu_ext!(SysMenuBar, Fl_Sys_Menu_Bar);
impl SysMenuBar {
pub fn set_window_menu_style(style: WindowMenuStyle) {
unsafe {
Fl_Sys_Menu_Bar_set_window_menu_style(style as i32);
}
}
pub fn set_about_callback<F: FnMut(&mut Self) + 'static>(&mut self, cb: F) {
assert!(!self.was_deleted());
unsafe {
unsafe extern "C" fn shim(wid: *mut Fl_Widget, data: *mut std::os::raw::c_void) {
let mut wid = SysMenuBar::from_widget_ptr(wid as *mut _);
let a = data as *mut Box<dyn FnMut(&mut SysMenuBar)>;
let f: &mut (dyn FnMut(&mut SysMenuBar)) = &mut **a;
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&mut wid)));
}
let a: *mut Box<dyn FnMut(&mut Self)> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
let callback: Fl_Callback = Some(shim);
Fl_Sys_Menu_Bar_about(self.inner, callback, data);
}
}
}
#[derive(Debug, Clone)]
pub struct MenuItem {
inner: *mut Fl_Menu_Item,
size: i32,
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MenuFlag: i32 {
const Normal = 0;
const Inactive = 1;
const Toggle = 2;
const Value = 4;
const Radio = 8;
const Invisible = 0x10;
const SubmenuPointer = 0x20;
const Submenu = 0x40;
const MenuDivider = 0x80;
const MenuHorizontal = 0x100;
}
}
impl MenuItem {
pub fn new(choices: &[&'static str]) -> MenuItem {
unsafe {
let sz = choices.len();
let mut temp: Vec<*mut raw::c_char> = vec![];
for &choice in choices {
let c = CString::safe_new(choice);
temp.push(c.into_raw() as _);
}
let item_ptr = Fl_Menu_Item_new(temp.as_ptr() as *mut *mut raw::c_char, sz as i32);
assert!(!item_ptr.is_null());
MenuItem {
inner: item_ptr,
size: choices.len() as i32,
}
}
}
pub fn popup(&self, x: i32, y: i32) -> Option<MenuItem> {
assert!(!self.was_deleted());
if self.size() == 0 {
return None;
}
unsafe {
let item = Fl_Menu_Item_popup(self.inner, x, y);
if item.is_null() {
None
} else {
let item = MenuItem {
inner: item as *mut Fl_Menu_Item,
size: Fl_Menu_Item_children(item),
};
Some(item)
}
}
}
pub fn label(&self) -> Option<String> {
assert!(!self.was_deleted());
unsafe {
let label_ptr = Fl_Menu_Item_label(self.inner);
if label_ptr.is_null() {
return None;
}
Some(
CStr::from_ptr(label_ptr as *mut raw::c_char)
.to_string_lossy()
.to_string(),
)
}
}
pub fn set_label(&mut self, txt: &str) {
assert!(!self.was_deleted());
unsafe {
let txt = CString::safe_new(txt);
Fl_Menu_Item_set_label(self.inner, txt.into_raw() as _);
}
}
pub fn label_type(&self) -> LabelType {
assert!(!self.was_deleted());
unsafe { mem::transmute(Fl_Menu_Item_label_type(self.inner)) }
}
pub fn set_label_type(&mut self, typ: LabelType) {
assert!(!self.was_deleted());
unsafe {
Fl_Menu_Item_set_label_type(self.inner, typ as i32);
}
}
pub fn label_color(&self) -> Color {
assert!(!self.was_deleted());
unsafe { mem::transmute(Fl_Menu_Item_label_color(self.inner)) }
}
pub fn set_label_color(&mut self, color: Color) {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_set_label_color(self.inner, color.bits()) }
}
pub fn label_font(&self) -> Font {
assert!(!self.was_deleted());
unsafe { mem::transmute(Fl_Menu_Item_label_font(self.inner)) }
}
pub fn set_label_font(&mut self, font: Font) {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_set_label_font(self.inner, font.bits()) }
}
pub fn label_size(&self) -> i32 {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_label_size(self.inner) }
}
pub fn set_label_size(&mut self, sz: i32) {
assert!(!self.was_deleted());
let sz = if sz < 1 { 1 } else { sz };
unsafe { Fl_Menu_Item_set_label_size(self.inner, sz) }
}
pub fn value(&self) -> bool {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_value(self.inner) != 0 }
}
pub fn set(&mut self) {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_set(self.inner) }
}
pub fn clear(&mut self) {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_clear(self.inner) }
}
pub fn visible(&self) -> bool {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_visible(self.inner) != 0 }
}
pub fn active(&self) -> bool {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_active(self.inner) != 0 }
}
pub fn activate(&mut self) {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_activate(self.inner) }
}
pub fn deactivate(&mut self) {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_deactivate(self.inner) }
}
pub fn is_submenu(&self) -> bool {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_submenu(self.inner) != 0 }
}
pub fn is_checkbox(&self) -> bool {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_checkbox(self.inner) != 0 }
}
pub fn is_radio(&self) -> bool {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_radio(self.inner) != 0 }
}
pub fn show(&mut self) {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_show(self.inner) }
}
pub fn hide(&mut self) {
assert!(!self.was_deleted());
unsafe { Fl_Menu_Item_hide(self.inner) }
}
pub fn next(&self, idx: i32) -> Option<MenuItem> {
assert!(!self.was_deleted());
unsafe {
let ptr = Fl_Menu_Item_next(self.inner, idx);
if ptr.is_null() {
return None;
}
let label_ptr = Fl_Menu_Item_label(ptr);
if label_ptr.is_null() {
return None;
}
Some(MenuItem {
inner: ptr,
size: Fl_Menu_Item_children(ptr),
})
}
}
pub fn children(&self) -> i32 {
unsafe { Fl_Menu_Item_children(self.inner) }
}
pub fn submenus(&self) -> i32 {
let mut i = 0;
while let Some(_item) = self.next(i) {
i += 1;
}
i
}
pub fn size(&self) -> i32 {
self.size
}
pub fn at(&self, idx: i32) -> Option<MenuItem> {
assert!(idx < self.size);
unsafe {
let ptr = Fl_Menu_Item_at(self.inner, idx);
if ptr.is_null() {
None
} else {
Some(MenuItem {
inner: ptr as _,
size: Fl_Menu_Item_children(ptr),
})
}
}
}
pub unsafe fn user_data(&self) -> Option<Box<dyn FnMut()>> {
let ptr = Fl_Menu_Item_user_data(self.inner);
if ptr.is_null() {
None
} else {
let x = ptr as *mut Box<dyn FnMut()>;
let x = Box::from_raw(x);
Fl_Menu_Item_set_callback(self.inner, None, std::ptr::null_mut());
Some(*x)
}
}
pub fn set_callback<F: FnMut(&mut Choice) + 'static>(&mut self, cb: F) {
assert!(!self.was_deleted());
unsafe {
unsafe extern "C" fn shim(wid: *mut fltk_sys::menu::Fl_Widget, data: *mut raw::c_void) {
let mut wid = crate::widget::Widget::from_widget_ptr(wid as *mut _);
let a: *mut Box<dyn FnMut(&mut crate::widget::Widget)> =
data as *mut Box<dyn FnMut(&mut crate::widget::Widget)>;
let f: &mut (dyn FnMut(&mut crate::widget::Widget)) = &mut **a;
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&mut wid)));
}
let _old_data = self.user_data();
let a: *mut Box<dyn FnMut(&mut Choice)> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut raw::c_void = a as *mut std::ffi::c_void;
let callback: fltk_sys::menu::Fl_Callback = Some(shim);
Fl_Menu_Item_set_callback(self.inner, callback, data);
}
}
pub fn emit<T: 'static + Clone + Send + Sync>(
&mut self,
sender: crate::app::Sender<T>,
msg: T,
) {
self.set_callback(move |_| sender.send(msg.clone()));
}
pub fn was_deleted(&self) -> bool {
self.inner.is_null()
}
pub fn draw<M: MenuExt>(&self, x: i32, y: i32, w: i32, h: i32, menu: &M, selected: bool) {
assert!(!self.was_deleted());
unsafe {
Fl_Menu_Item_draw(
self.inner,
x,
y,
w,
h,
menu.as_widget_ptr() as _,
selected as i32,
)
}
}
pub fn measure(&self) -> (i32, i32) {
assert!(!self.was_deleted());
let mut h = 0;
let ret = unsafe { Fl_Menu_Item_measure(self.inner, &mut h as _, std::ptr::null()) };
(ret, h)
}
#[doc(hidden)]
pub unsafe fn set_image<I: ImageExt>(&mut self, image: I) {
assert!(!self.was_deleted());
assert!(!image.was_deleted());
Fl_Menu_Item_image(self.inner, image.as_image_ptr() as _)
}
pub fn add_image<I: ImageExt>(&mut self, image: Option<I>, on_left: bool) {
assert!(!self.was_deleted());
unsafe {
if let Some(image) = image {
assert!(!image.was_deleted());
Fl_Menu_Item_add_image(self.inner, image.as_image_ptr() as _, on_left as i32)
} else {
Fl_Menu_Item_add_image(self.inner, std::ptr::null_mut(), on_left as i32)
}
}
}
pub fn add<F: FnMut(&mut Choice) + 'static>(
&mut self,
name: &str,
shortcut: crate::enums::Shortcut,
flag: MenuFlag,
cb: F,
) -> i32 {
assert!(!self.was_deleted());
let temp = CString::safe_new(name);
unsafe {
unsafe extern "C" fn shim(wid: *mut Fl_Widget, data: *mut std::os::raw::c_void) {
let mut wid = crate::widget::Widget::from_widget_ptr(wid as *mut _);
let a: *mut Box<dyn FnMut(&mut crate::widget::Widget)> =
data as *mut Box<dyn FnMut(&mut crate::widget::Widget)>;
let f: &mut (dyn FnMut(&mut crate::widget::Widget)) = &mut **a;
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&mut wid)));
}
let a: *mut Box<dyn FnMut(&mut Choice)> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
let callback: Fl_Callback = Some(shim);
Fl_Menu_Item_add(
self.inner,
temp.as_ptr(),
shortcut.bits(),
callback,
data,
flag.bits(),
)
}
}
pub fn insert<F: FnMut(&mut Choice) + 'static>(
&mut self,
idx: i32,
name: &str,
shortcut: crate::enums::Shortcut,
flag: MenuFlag,
cb: F,
) -> i32 {
assert!(!self.was_deleted());
let temp = CString::safe_new(name);
unsafe {
unsafe extern "C" fn shim(wid: *mut Fl_Widget, data: *mut std::os::raw::c_void) {
let mut wid = crate::widget::Widget::from_widget_ptr(wid as *mut _);
let a: *mut Box<dyn FnMut(&mut crate::widget::Widget)> =
data as *mut Box<dyn FnMut(&mut crate::widget::Widget)>;
let f: &mut (dyn FnMut(&mut crate::widget::Widget)) = &mut **a;
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&mut wid)));
}
let a: *mut Box<dyn FnMut(&mut Choice)> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
let callback: Fl_Callback = Some(shim);
Fl_Menu_Item_insert(
self.inner,
idx,
temp.as_ptr(),
shortcut.bits(),
callback,
data,
flag.bits(),
)
}
}
pub fn add_emit<T: 'static + Clone + Send + Sync>(
&mut self,
label: &str,
shortcut: crate::enums::Shortcut,
flag: MenuFlag,
sender: crate::app::Sender<T>,
msg: T,
) -> i32 {
self.add(label, shortcut, flag, move |_| sender.send(msg.clone()))
}
pub fn insert_emit<T: 'static + Clone + Send + Sync>(
&mut self,
idx: i32,
label: &str,
shortcut: crate::enums::Shortcut,
flag: MenuFlag,
sender: crate::app::Sender<T>,
msg: T,
) -> i32 {
self.insert(idx, label, shortcut, flag, move |_| {
sender.send(msg.clone())
})
}
pub fn set_shortcut(&mut self, shortcut: crate::enums::Shortcut) {
unsafe {
Fl_Menu_Item_set_shortcut(self.inner, shortcut.bits());
}
}
pub fn set_flag(&mut self, flag: MenuFlag) {
unsafe {
Fl_Menu_Item_set_flag(self.inner, flag.bits());
}
}
}
pub unsafe fn delete_menu_item(item: MenuItem) {
Fl_Menu_Item_delete(item.inner)
}
unsafe impl Send for MenuItem {}
unsafe impl Sync for MenuItem {}
impl PartialEq for MenuItem {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl Eq for MenuItem {}
impl IntoIterator for MenuItem {
type Item = MenuItem;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
let mut v: Vec<MenuItem> = vec![];
let mut i = 0;
while let Some(item) = self.at(i) {
v.push(item);
i += 1;
}
v.into_iter()
}
}
pub fn mac_set_about<F: FnMut() + 'static>(cb: F) {
unsafe {
unsafe extern "C" fn shim(_wid: *mut fltk_sys::menu::Fl_Widget, data: *mut raw::c_void) {
let a: *mut Box<dyn FnMut()> = data as *mut Box<dyn FnMut()>;
let f: &mut (dyn FnMut()) = &mut **a;
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
}
let a: *mut Box<dyn FnMut()> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut raw::c_void = a as *mut std::ffi::c_void;
let callback: fltk_sys::menu::Fl_Callback = Some(shim);
Fl_mac_set_about(callback, data, 0);
}
}
#[derive(Debug, Clone, Copy)]
pub struct MacAppMenu;
impl MacAppMenu {
pub fn set_about(about: &'static str) {
unsafe {
let about = CString::safe_new(about).as_ptr();
Fl_Mac_App_Menu_set_about(about);
}
}
pub fn set_print(print: &'static str) {
unsafe {
let print = CString::safe_new(print).as_ptr();
Fl_Mac_App_Menu_set_print(print);
}
}
pub fn set_print_no_titlebar(print_no_titlebar: &'static str) {
unsafe {
let print_no_titlebar = CString::safe_new(print_no_titlebar).as_ptr();
Fl_Mac_App_Menu_set_print_no_titlebar(print_no_titlebar);
}
}
pub fn set_toggle_print_titlebar(toggle_print_titlebar: &'static str) {
unsafe {
let toggle_print_titlebar = CString::safe_new(toggle_print_titlebar).as_ptr();
Fl_Mac_App_Menu_set_toggle_print_titlebar(toggle_print_titlebar);
}
}
pub fn set_services(services: &'static str) {
unsafe {
let services = CString::safe_new(services).as_ptr();
Fl_Mac_App_Menu_set_services(services);
}
}
pub fn set_hide(hide: &'static str) {
unsafe {
let hide = CString::safe_new(hide).as_ptr();
Fl_Mac_App_Menu_set_hide(hide);
}
}
pub fn set_hide_others(hide_others: &'static str) {
unsafe {
let hide_others = CString::safe_new(hide_others).as_ptr();
Fl_Mac_App_Menu_set_hide_others(hide_others);
}
}
pub fn set_show(show: &'static str) {
unsafe {
let show = CString::safe_new(show).as_ptr();
Fl_Mac_App_Menu_set_show(show);
}
}
pub fn set_quit(quit: &'static str) {
unsafe {
let quit = CString::safe_new(quit).as_ptr();
Fl_Mac_App_Menu_set_quit(quit);
}
}
pub fn custom_application_menu_items(m: MenuItem) {
unsafe {
Fl_Mac_App_Menu_custom_application_menu_items(m.inner);
}
}
}