use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher}
};
use crate::{
accelerator::Accelerator,
platform_impl::{Menu as MenuPlatform, MenuItemAttributes as CustomMenuItemPlatform}
};
pub struct ContextMenu(pub(crate) Menu);
pub struct MenuBar(pub(crate) Menu);
pub struct MenuItemAttributes<'a> {
id: MenuId,
title: &'a str,
keyboard_accelerator: Option<Accelerator>,
enabled: bool,
selected: bool
}
impl<'a> MenuItemAttributes<'a> {
pub fn new(title: &'a str) -> Self {
Self {
id: MenuId::new(title),
title,
keyboard_accelerator: None,
enabled: true,
selected: false
}
}
pub fn with_id(mut self, id: MenuId) -> Self {
self.id = id;
self
}
pub fn with_accelerators(mut self, keyboard_accelerators: &Accelerator) -> Self {
self.keyboard_accelerator = Some(keyboard_accelerators.to_owned());
self
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn with_selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
}
#[derive(Debug, Clone)]
pub(crate) struct Menu {
pub(crate) menu_platform: MenuPlatform,
pub(crate) menu_type: MenuType
}
impl ContextMenu {
pub fn new() -> Self {
Self(Menu {
menu_platform: MenuPlatform::new_popup_menu(),
menu_type: MenuType::ContextMenu
})
}
pub fn add_submenu(&mut self, title: &str, enabled: bool, submenu: ContextMenu) {
self.0.menu_platform.add_submenu(title, enabled, submenu.0.menu_platform);
}
pub fn add_item(&mut self, item: MenuItemAttributes<'_>) -> CustomMenuItem {
self.0
.menu_platform
.add_item(item.id, item.title, item.keyboard_accelerator, item.enabled, item.selected, MenuType::ContextMenu)
}
pub fn add_native_item(&mut self, item: MenuItem) -> Option<CustomMenuItem> {
self.0.menu_platform.add_native_item(item, self.0.menu_type)
}
}
impl Default for ContextMenu {
fn default() -> Self {
Self::new()
}
}
impl MenuBar {
pub fn new() -> Self {
Self(Menu {
menu_platform: MenuPlatform::new(),
menu_type: MenuType::MenuBar
})
}
pub fn add_submenu(&mut self, title: &str, enabled: bool, submenu: MenuBar) {
self.0.menu_platform.add_submenu(title, enabled, submenu.0.menu_platform);
}
pub fn add_item(&mut self, item: MenuItemAttributes<'_>) -> CustomMenuItem {
self.0
.menu_platform
.add_item(item.id, item.title, item.keyboard_accelerator, item.enabled, item.selected, MenuType::MenuBar)
}
pub fn add_native_item(&mut self, item: MenuItem) -> Option<CustomMenuItem> {
self.0.menu_platform.add_native_item(item, self.0.menu_type)
}
}
impl Default for MenuBar {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Default)]
pub struct AboutMetadata {
pub version: Option<String>,
pub authors: Option<Vec<String>>,
pub comments: Option<String>,
pub copyright: Option<String>,
pub license: Option<String>,
pub website: Option<String>,
pub website_label: Option<String>
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum MenuItem {
About(String, AboutMetadata),
Hide,
Services,
HideOthers,
ShowAll,
CloseWindow,
Quit,
Copy,
Cut,
Undo,
Redo,
SelectAll,
Paste,
EnterFullScreen,
Minimize,
Zoom,
Separator
}
#[derive(Debug, Clone)]
pub struct CustomMenuItem(pub CustomMenuItemPlatform);
impl CustomMenuItem {
pub fn id(self) -> MenuId {
self.0.id()
}
pub fn title(&self) -> String {
self.0.title()
}
pub fn set_enabled(&mut self, is_enabled: bool) {
self.0.set_enabled(is_enabled)
}
pub fn set_title(&mut self, title: &str) {
self.0.set_title(title)
}
pub fn set_selected(&mut self, is_selected: bool) {
self.0.set_selected(is_selected)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct MenuId(pub u16);
impl From<MenuId> for u16 {
fn from(s: MenuId) -> u16 {
s.0
}
}
impl MenuId {
pub const EMPTY: MenuId = MenuId(0);
pub fn new(unique_string: &str) -> MenuId {
MenuId(hash_string_to_u16(unique_string))
}
pub fn is_empty(self) -> bool {
Self::EMPTY == self
}
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MenuType {
MenuBar,
ContextMenu
}
fn hash_string_to_u16(title: &str) -> u16 {
let mut s = DefaultHasher::new();
title.to_uppercase().hash(&mut s);
s.finish() as u16
}