mirror of
https://github.com/italicsjenga/rust_minifb.git
synced 2024-12-23 11:21:30 +11:00
Merge remote-tracking branch 'refs/remotes/origin/menu-api-rework'
This commit is contained in:
commit
1dbcf2dee1
|
@ -1,7 +1,7 @@
|
|||
extern crate minifb;
|
||||
|
||||
use minifb::{Window, Key, Scale, WindowOptions, Menu};
|
||||
use minifb::{MENU_KEY_CTRL, MENU_KEY_COMMAND};
|
||||
use minifb::{MENU_KEY_CTRL};
|
||||
|
||||
const WIDTH: usize = 640;
|
||||
const HEIGHT: usize = 360;
|
||||
|
@ -26,63 +26,23 @@ fn main() {
|
|||
})
|
||||
.expect("Unable to Open Window");
|
||||
|
||||
// Setup a sub menu
|
||||
let mut menu = Menu::new("Test").unwrap();
|
||||
let mut sub = Menu::new("Select Color").unwrap();
|
||||
|
||||
let sub_menu = vec![
|
||||
Menu {
|
||||
name: "Color 0",
|
||||
key: Key::F1,
|
||||
id: COLOR_0_ID,
|
||||
..Menu::default()
|
||||
},
|
||||
Menu {
|
||||
name: "Color 1",
|
||||
key: Key::F2,
|
||||
id: COLOR_1_ID,
|
||||
..Menu::default()
|
||||
},
|
||||
Menu {
|
||||
name: "Color 2",
|
||||
key: Key::F12,
|
||||
id: COLOR_2_ID,
|
||||
..Menu::default()
|
||||
},
|
||||
];
|
||||
sub.add_item("Color 0", COLOR_0_ID).shortcut(Key::F1, 0).build();
|
||||
sub.add_item("Color 1", COLOR_1_ID).shortcut(Key::F2, 0).build();
|
||||
sub.add_item("Color 2", COLOR_2_ID).shortcut(Key::F7, 0).build();
|
||||
|
||||
// Main menu
|
||||
menu.add_item("Menu Test", MENU_TEST_ID).shortcut(Key::W, MENU_KEY_CTRL).build();
|
||||
|
||||
let menu = vec![
|
||||
Menu {
|
||||
name: "Menu Test",
|
||||
key: Key::W,
|
||||
id: MENU_TEST_ID,
|
||||
modifier: MENU_KEY_CTRL,
|
||||
mac_mod: MENU_KEY_COMMAND,
|
||||
..Menu::default()
|
||||
},
|
||||
Menu::separotor(),
|
||||
Menu {
|
||||
name: "Other menu!",
|
||||
key: Key::S,
|
||||
modifier: MENU_KEY_CTRL,
|
||||
mac_mod: MENU_KEY_CTRL,
|
||||
id: OTHER_MENU_ID,
|
||||
..Menu::default()
|
||||
},
|
||||
Menu {
|
||||
name: "Remove Menu",
|
||||
key: Key::R,
|
||||
id: CLOSE_MENU_ID,
|
||||
..Menu::default()
|
||||
},
|
||||
Menu {
|
||||
name: "Select Color",
|
||||
sub_menu: Some(&sub_menu),
|
||||
..Menu::default()
|
||||
}
|
||||
];
|
||||
menu.add_separator();
|
||||
|
||||
window.add_menu("Test", &menu).expect("Unable to add menu");
|
||||
menu.add_item("Other Menu", OTHER_MENU_ID).shortcut(Key::W, MENU_KEY_CTRL).build();
|
||||
menu.add_item("Remove Menu", CLOSE_MENU_ID).shortcut(Key::R, 0).build();
|
||||
|
||||
menu.add_sub_menu("Sub Test", &sub);
|
||||
|
||||
let menu_handle = window.add_menu(&menu);
|
||||
|
||||
let mut color_mul = 1;
|
||||
|
||||
|
@ -106,7 +66,7 @@ fn main() {
|
|||
}
|
||||
CLOSE_MENU_ID => {
|
||||
println!("remove menu");
|
||||
window.remove_menu("Test").expect("Unable to remove menu");
|
||||
window.remove_menu(menu_handle);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// Key is used by the get key functions to check if some keys on the keyboard has been pressed
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum Key {
|
||||
Key0 = 0,
|
||||
Key1 = 1,
|
||||
|
|
163
src/lib.rs
163
src/lib.rs
|
@ -75,13 +75,13 @@ pub mod os;
|
|||
mod mouse_handler;
|
||||
mod key_handler;
|
||||
mod window_flags;
|
||||
mod menu;
|
||||
pub use menu::Menu as Menu;
|
||||
pub use menu::MENU_KEY_COMMAND;
|
||||
pub use menu::MENU_KEY_WIN;
|
||||
pub use menu::MENU_KEY_SHIFT;
|
||||
pub use menu::MENU_KEY_CTRL;
|
||||
pub use menu::MENU_KEY_ALT;
|
||||
//mod menu;
|
||||
//pub use menu::Menu as Menu;
|
||||
//pub use menu::MENU_KEY_COMMAND;
|
||||
//pub use menu::MENU_KEY_WIN;
|
||||
//pub use menu::MENU_KEY_SHIFT;
|
||||
//pub use menu::MENU_KEY_CTRL;
|
||||
//pub use menu::MENU_KEY_ALT;
|
||||
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -462,25 +462,28 @@ impl Window {
|
|||
/// scope for this library to support.
|
||||
/// ```
|
||||
///
|
||||
|
||||
#[inline]
|
||||
pub fn add_menu(&mut self, menu_name: &str, menu: &Vec<Menu>) -> Result<()> {
|
||||
self.0.add_menu(menu_name, menu)
|
||||
pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle {
|
||||
self.0.add_menu(&menu.0)
|
||||
}
|
||||
|
||||
///
|
||||
/// Updates an existing menu created with [add_menu]
|
||||
///
|
||||
/*
|
||||
#[inline]
|
||||
pub fn update_menu(&mut self, menu_name: &str, menu: &Vec<Menu>) -> Result<()> {
|
||||
self.0.update_menu(menu_name, menu)
|
||||
}
|
||||
*/
|
||||
|
||||
///
|
||||
/// Remove a menu that has been added with [add_menu]
|
||||
///
|
||||
#[inline]
|
||||
pub fn remove_menu(&mut self, menu_name: &str) -> Result<()> {
|
||||
self.0.remove_menu(menu_name)
|
||||
pub fn remove_menu(&mut self, handle: MenuHandle) {
|
||||
self.0.remove_menu(handle)
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -492,6 +495,144 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Command key on Mac OS
|
||||
pub const MENU_KEY_COMMAND: usize = 1;
|
||||
/// Windows key on Windows
|
||||
pub const MENU_KEY_WIN: usize = 2;
|
||||
/// Shift key
|
||||
pub const MENU_KEY_SHIFT: usize = 4;
|
||||
/// Control key
|
||||
pub const MENU_KEY_CTRL: usize = 8;
|
||||
/// Alt key
|
||||
pub const MENU_KEY_ALT: usize = 16;
|
||||
|
||||
const MENU_ID_SEPARATOR:usize = 0xffffffff;
|
||||
|
||||
pub struct Menu(imp::Menu);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct MenuItemHandle(pub u64);
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct MenuHandle(pub u64);
|
||||
|
||||
impl Menu {
|
||||
pub fn new(name: &str) -> Result<Menu> {
|
||||
imp::Menu::new(name).map(Menu)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn destroy_menu(&mut self) {
|
||||
//self.0.destroy_menu()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_sub_menu(&mut self, name: &str, menu: &Menu) {
|
||||
self.0.add_sub_menu(name, &menu.0)
|
||||
}
|
||||
|
||||
pub fn add_separator(&mut self) {
|
||||
self.add_menu_item(&MenuItem { id: MENU_ID_SEPARATOR, ..MenuItem::default() });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle {
|
||||
self.0.add_menu_item(item)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_item(&mut self, name: &str, id: usize) -> MenuItem {
|
||||
MenuItem {
|
||||
id: id,
|
||||
label: name.to_owned(),
|
||||
menu: Some(self),
|
||||
..MenuItem::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn remove_item(&mut self, item: &MenuItemHandle) {
|
||||
self.0.remove_item(item)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MenuItem<'a> {
|
||||
pub id: usize,
|
||||
pub label: String,
|
||||
pub enabled: bool,
|
||||
pub key: Key,
|
||||
pub modifier: usize,
|
||||
pub menu: Option<&'a mut Menu>,
|
||||
}
|
||||
|
||||
impl<'a> Default for MenuItem<'a> {
|
||||
fn default() -> Self {
|
||||
MenuItem {
|
||||
id: MENU_ID_SEPARATOR,
|
||||
label: "".to_owned(),
|
||||
enabled: true,
|
||||
key: Key::Unknown,
|
||||
modifier: 0,
|
||||
menu: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Clone for MenuItem<'a> {
|
||||
fn clone(&self) -> Self {
|
||||
MenuItem {
|
||||
id: self.id,
|
||||
label: self.label.clone(),
|
||||
enabled: self.enabled,
|
||||
key: self.key,
|
||||
modifier: self.modifier,
|
||||
menu: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MenuItem<'a> {
|
||||
pub fn new(name: &str, id: usize) -> MenuItem {
|
||||
MenuItem {
|
||||
id: id,
|
||||
label: name.to_owned(),
|
||||
..MenuItem::default()
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn shortcut(self, key: Key, modifier: usize) -> Self {
|
||||
MenuItem {
|
||||
key: key,
|
||||
modifier: modifier,
|
||||
.. self
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn separator(self) -> Self {
|
||||
MenuItem {
|
||||
id: MENU_ID_SEPARATOR,
|
||||
.. self
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn enabled(self, enabled: bool) -> Self {
|
||||
MenuItem {
|
||||
enabled: enabled,
|
||||
.. self
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn build(&mut self) -> MenuItemHandle {
|
||||
let t = self.clone();
|
||||
if let Some(ref mut menu) = self.menu {
|
||||
menu.0.add_menu_item(&t)
|
||||
} else {
|
||||
MenuItemHandle(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Impl for WindowOptions
|
||||
|
||||
#[doc(hidden)]
|
||||
|
|
78
src/menu.rs
78
src/menu.rs
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
use Key;
|
||||
|
||||
/// Command key on Mac OS
|
||||
|
@ -13,47 +14,42 @@ pub const MENU_KEY_ALT: usize = 16;
|
|||
|
||||
const MENU_ID_SEPARATOR:usize = 0xffffffff;
|
||||
|
||||
///
|
||||
/// Used to hold the data for creating menus for the Application
|
||||
///
|
||||
pub struct Menu<'a> {
|
||||
/// Name of the menu item
|
||||
pub name: &'a str,
|
||||
/// User-defined Id thot will be sent back to the application in [get_menu_event]
|
||||
pub id: usize,
|
||||
/// Shortcut key for the menu item
|
||||
pub key: Key,
|
||||
/// Modifier on Windows for the menu
|
||||
pub modifier: usize,
|
||||
/// Modifier on Mac OS
|
||||
pub mac_mod: usize,
|
||||
/// Menu item should be enabled on grayed out
|
||||
pub enabled: bool,
|
||||
/// Sub-menu. Vector of a sub-menu, otherwise None if no sub-menu
|
||||
pub sub_menu: Option<&'a Vec<Menu<'a>>>,
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
use self::os::macos as imp;
|
||||
#[cfg(target_os = "windows")]
|
||||
use self::os::windows as imp;
|
||||
#[cfg(any(target_os="linux",
|
||||
target_os="freebsd",
|
||||
target_os="dragonfly",
|
||||
target_os="netbsd",
|
||||
target_os="openbsd"))]
|
||||
use self::os::unix as imp;
|
||||
|
||||
impl<'a> Menu<'a> {
|
||||
pub fn separotor() -> Menu<'a> {
|
||||
Menu {
|
||||
id: MENU_ID_SEPARATOR,
|
||||
.. Self::default()
|
||||
}
|
||||
pub struct Menu(imp::Menu);
|
||||
|
||||
impl Menu {
|
||||
pub fn new(name: &name) -> Result<Menu> {
|
||||
imp::Menu::new(name).map(Menu)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn destroy_menu(&mut self) {
|
||||
self.0.destroy_menu()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_sub_menu(&mut self, menu: &Menu) {
|
||||
self.0.add_sub_menu(menu)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_item(&mut self, item: &mut MenuItem) {
|
||||
self.0.add_item(item)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn remove_item(&mut self, item: &mut MenuItem) {
|
||||
self.0.remove_item(item)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for Menu<'a> {
|
||||
fn default() -> Menu<'a> {
|
||||
Menu {
|
||||
name: "",
|
||||
id: 0,
|
||||
key: Key::Unknown,
|
||||
modifier: 0,
|
||||
mac_mod: 0,
|
||||
enabled: true,
|
||||
sub_menu: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
#include "OSXWindow.h"
|
||||
#include "OSXWindowFrameView.h"
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static bool s_init = false;
|
||||
|
@ -240,7 +240,8 @@ static int update_events()
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int generic_update(OSXWindow* win) {
|
||||
static int generic_update(OSXWindow* win)
|
||||
{
|
||||
int state = update_events();
|
||||
|
||||
if (win->shared_data) {
|
||||
|
@ -383,54 +384,216 @@ int mfb_active_menu(void* window) {
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mfb_add_menu(void* window, const char* name, void* m)
|
||||
static CFStringRef create_string_for_key(CGKeyCode keyCode)
|
||||
{
|
||||
OSXWindow* win = (OSXWindow*)window;
|
||||
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
|
||||
CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
|
||||
|
||||
const char* n = strdup(name);
|
||||
if (!layoutData)
|
||||
return 0;
|
||||
|
||||
NSString* ns_name = [NSString stringWithUTF8String: n];
|
||||
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
|
||||
|
||||
NSMenu* main_menu = [NSApp mainMenu];
|
||||
UInt32 keysDown = 0;
|
||||
UniChar chars[4];
|
||||
UniCharCount realLength;
|
||||
|
||||
NSMenuItem* windowMenuItem = [main_menu addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:ns_name];
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
UCKeyTranslate(keyboardLayout,
|
||||
keyCode,
|
||||
kUCKeyActionDisplay,
|
||||
0,
|
||||
LMGetKbdType(),
|
||||
kUCKeyTranslateNoDeadKeysBit,
|
||||
&keysDown,
|
||||
sizeof(chars) / sizeof(chars[0]),
|
||||
&realLength,
|
||||
chars);
|
||||
CFRelease(currentKeyboard);
|
||||
|
||||
MenuDesc* menu_desc = (MenuDesc*)m;
|
||||
|
||||
[windowMenu setAutoenablesItems:NO];
|
||||
|
||||
build_submenu(windowMenu, menu_desc);
|
||||
|
||||
Menu* menu = &win->menu_data->menus[win->menu_data->menu_count++];
|
||||
|
||||
menu->name = n;
|
||||
menu->menu = windowMenu;
|
||||
menu->menu_item = windowMenuItem;
|
||||
return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mfb_update_menu(void* window, const char* name, void* m)
|
||||
static NSString* convert_key_code_to_string(int key)
|
||||
{
|
||||
OSXWindow* win = (OSXWindow*)window;
|
||||
|
||||
NSString* ns_name = [NSString stringWithUTF8String: name];
|
||||
NSMenu* main_menu = [NSApp mainMenu];
|
||||
|
||||
int len = win->menu_data->menu_count;
|
||||
|
||||
for (int i = 0; i < len; ++i)
|
||||
if (key < 128)
|
||||
{
|
||||
Menu* menu = &win->menu_data->menus[i];
|
||||
NSString* charName = (NSString*)create_string_for_key(key);
|
||||
|
||||
if (!strcmp(menu->name, name)) {
|
||||
[menu->menu removeAllItems];
|
||||
build_submenu(menu->menu, (MenuDesc*)m);
|
||||
return;
|
||||
}
|
||||
if (charName)
|
||||
return charName;
|
||||
|
||||
return [NSString stringWithFormat:@"%c", (char)key];
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:@"%C", (uint16_t)key];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const uint32_t MENU_KEY_COMMAND = 1;
|
||||
const uint32_t MENU_KEY_WIN = 2;
|
||||
const uint32_t MENU_KEY_SHIFT= 4;
|
||||
const uint32_t MENU_KEY_CTRL = 8;
|
||||
const uint32_t MENU_KEY_ALT = 16;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static NSString* get_string_for_key(uint32_t t) {
|
||||
unichar c = (unichar)t;
|
||||
NSString* key = [NSString stringWithCharacters:&c length:1];
|
||||
return key;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint64_t mfb_add_menu_item(
|
||||
void* in_menu,
|
||||
int32_t menu_id,
|
||||
const char* item_name,
|
||||
bool enabled,
|
||||
uint32_t key,
|
||||
uint32_t modfier)
|
||||
{
|
||||
NSMenu* menu = (NSMenu*)in_menu;
|
||||
|
||||
NSString* name = [NSString stringWithUTF8String: item_name];
|
||||
|
||||
if (menu_id == -1)
|
||||
{
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* key_string = 0;
|
||||
int mask = 0;
|
||||
NSMenuItem* newItem = [[NSMenuItem alloc] initWithTitle:name action:@selector(onMenuPress:) keyEquivalent:@""];
|
||||
[newItem setTag:menu_id];
|
||||
|
||||
// This code may look a bit weird but is here for a reason:
|
||||
//
|
||||
// In order to make it easier to bulid cross-platform apps Ctrl is often used as
|
||||
// default modifier on Windows/Nix* while it's Command on Mac. Now we when Ctrl
|
||||
// is set we default to Command on Mac for that reason but if Command AND Ctrl is
|
||||
// set we allow both Ctrl and Command to be used but then it's up to the developer
|
||||
// to deal with diffrent shortcuts depending on OS.
|
||||
//
|
||||
|
||||
if ((modfier & MENU_KEY_CTRL)) {
|
||||
mask |= NSCommandKeyMask;
|
||||
}
|
||||
if ((modfier & MENU_KEY_CTRL) &&
|
||||
(modfier & MENU_KEY_COMMAND)) {
|
||||
mask |= NSControlKeyMask;
|
||||
}
|
||||
if (modfier & MENU_KEY_SHIFT) {
|
||||
mask |= NSShiftKeyMask;
|
||||
}
|
||||
if (modfier & MENU_KEY_ALT) {
|
||||
mask |= NSAlternateKeyMask;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case 0x7a: { key_string = get_string_for_key(NSF1FunctionKey); break; } // F1
|
||||
case 0x78: { key_string = get_string_for_key(NSF2FunctionKey); break; } // F2
|
||||
case 0x63: { key_string = get_string_for_key(NSF3FunctionKey); break; } // F3
|
||||
case 0x76: { key_string = get_string_for_key(NSF4FunctionKey); break; } // F4
|
||||
case 0x60: { key_string = get_string_for_key(NSF5FunctionKey); break; } // F5
|
||||
case 0x61: { key_string = get_string_for_key(NSF6FunctionKey); break; } // F6
|
||||
case 0x62: { key_string = get_string_for_key(NSF7FunctionKey); break; } // F7
|
||||
case 0x64: { key_string = get_string_for_key(NSF8FunctionKey); break; } // F8
|
||||
case 0x65: { key_string = get_string_for_key(NSF9FunctionKey); break; } // F9
|
||||
case 0x6d: { key_string = get_string_for_key(NSF10FunctionKey); break; } // F10
|
||||
case 0x67: { key_string = get_string_for_key(NSF11FunctionKey); break; } // F11
|
||||
case 0x6f: { key_string = get_string_for_key(NSF12FunctionKey); break; } // F12
|
||||
case 0x7f: break;
|
||||
default: {
|
||||
key_string = convert_key_code_to_string(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (key_string) {
|
||||
[newItem setKeyEquivalentModifierMask: mask];
|
||||
[newItem setKeyEquivalent:key_string];
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
[newItem setEnabled:YES];
|
||||
} else {
|
||||
[newItem setEnabled:NO];
|
||||
}
|
||||
|
||||
[newItem setOnStateImage: newItem.offStateImage];
|
||||
[menu addItem:newItem];
|
||||
|
||||
return (uint64_t)newItem;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mfb_add_sub_menu(void* parent_menu, const char* menu_name, void* attach_menu) {
|
||||
NSMenu* parent = (NSMenu*)parent_menu;
|
||||
NSMenu* attach = (NSMenu*)attach_menu;
|
||||
NSString* name = [NSString stringWithUTF8String: menu_name];
|
||||
|
||||
NSMenuItem* newItem = [[NSMenuItem alloc] initWithTitle:name action:NULL keyEquivalent:@""];
|
||||
[newItem setSubmenu:attach];
|
||||
|
||||
[parent addItem:newItem];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void* mfb_create_menu(const char* name) {
|
||||
NSString* ns_name = [NSString stringWithUTF8String: name];
|
||||
NSMenu* menu = [[NSMenu alloc] initWithTitle:ns_name];
|
||||
return (void*)menu;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mfb_destroy_menu(void* menu_item, const char* name)
|
||||
{
|
||||
NSMenuItem* item = (NSMenuItem*)menu_item;
|
||||
NSMenu* main_menu = [NSApp mainMenu];
|
||||
[main_menu removeItem:item];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mfb_remove_menu_item(void* parent, uint64_t menu_item) {
|
||||
NSMenu* menu = (NSMenu*)parent;
|
||||
NSMenuItem* item = (NSMenuItem*)(uintptr_t)menu_item;
|
||||
[menu removeItem:item];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint64_t mfb_add_menu(void* window, void* m)
|
||||
{
|
||||
OSXWindow* win = (OSXWindow*)window;
|
||||
NSMenu* menu = (NSMenu*)m;
|
||||
|
||||
NSMenu* main_menu = [NSApp mainMenu];
|
||||
|
||||
NSMenuItem* windowMenuItem = [main_menu addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
[NSApp setWindowsMenu:menu];
|
||||
[windowMenuItem setSubmenu:menu];
|
||||
|
||||
return (uint64_t)menu;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mfb_remove_menu_at(void* window, int index)
|
||||
{
|
||||
(void)window;
|
||||
NSMenu* main_menu = [NSApp mainMenu];
|
||||
[main_menu removeItemAtIndex:index];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#import "OSXWindow.h"
|
||||
#import "OSXWindowFrameView.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
@implementation OSXWindow
|
||||
|
||||
|
@ -71,14 +70,6 @@
|
|||
key_callback(rust_data, [event keyCode], 1);
|
||||
}
|
||||
|
||||
if (char_callback) {
|
||||
NSString* characters = [event characters];
|
||||
NSUInteger i, length = [characters length];
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
char_callback(rust_data, [characters characterAtIndex:i]);
|
||||
}
|
||||
|
||||
[super keyDown:event];
|
||||
}
|
||||
|
||||
|
@ -216,154 +207,7 @@
|
|||
self->active_menu_id = menu_id;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static CFStringRef create_string_for_key(CGKeyCode keyCode)
|
||||
{
|
||||
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
|
||||
CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
|
||||
|
||||
if (!layoutData)
|
||||
return 0;
|
||||
|
||||
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
|
||||
|
||||
UInt32 keysDown = 0;
|
||||
UniChar chars[4];
|
||||
UniCharCount realLength;
|
||||
|
||||
UCKeyTranslate(keyboardLayout,
|
||||
keyCode,
|
||||
kUCKeyActionDisplay,
|
||||
0,
|
||||
LMGetKbdType(),
|
||||
kUCKeyTranslateNoDeadKeysBit,
|
||||
&keysDown,
|
||||
sizeof(chars) / sizeof(chars[0]),
|
||||
&realLength,
|
||||
chars);
|
||||
CFRelease(currentKeyboard);
|
||||
|
||||
return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static NSString* convert_key_code_to_string(int key)
|
||||
{
|
||||
if (key < 128)
|
||||
{
|
||||
NSString* charName = (NSString*)create_string_for_key(key);
|
||||
|
||||
if (charName)
|
||||
return charName;
|
||||
|
||||
return [NSString stringWithFormat:@"%c", (char)key];
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:@"%C", (uint16_t)key];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const uint32_t MENU_KEY_COMMAND = 1;
|
||||
const uint32_t MENU_KEY_WIN = 2;
|
||||
const uint32_t MENU_KEY_SHIFT= 4;
|
||||
const uint32_t MENU_KEY_CTRL = 8;
|
||||
const uint32_t MENU_KEY_ALT = 16;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static NSString* get_string_for_key(uint32_t t) {
|
||||
unichar c = (unichar)t;
|
||||
NSString* key = [NSString stringWithCharacters:&c length:1];
|
||||
return key;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void build_submenu(NSMenu* menu, MenuDesc* desc)
|
||||
{
|
||||
[menu removeAllItems];
|
||||
|
||||
while (desc->menu_id != -2)
|
||||
{
|
||||
NSString* name = [NSString stringWithUTF8String: desc->name];
|
||||
|
||||
if (desc->menu_id == -1)
|
||||
{
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
}
|
||||
else if (desc->sub_menu)
|
||||
{
|
||||
NSMenuItem* newItem = [[NSMenuItem alloc] initWithTitle:name action:NULL keyEquivalent:@""];
|
||||
NSMenu* newMenu = [[NSMenu alloc] initWithTitle:name];
|
||||
[newItem setSubmenu:newMenu];
|
||||
|
||||
build_submenu(newMenu, desc->sub_menu);
|
||||
|
||||
[newMenu release];
|
||||
[menu addItem:newItem];
|
||||
[newItem release];
|
||||
}
|
||||
else
|
||||
{
|
||||
int mask = 0;
|
||||
NSString* key = 0;
|
||||
|
||||
NSMenuItem* newItem = [[NSMenuItem alloc] initWithTitle:name action:@selector(onMenuPress:) keyEquivalent:@""];
|
||||
[newItem setTag:desc->menu_id];
|
||||
|
||||
if (desc->modifier_mac & MENU_KEY_COMMAND) {
|
||||
mask |= NSCommandKeyMask;
|
||||
}
|
||||
if (desc->modifier_mac & MENU_KEY_SHIFT) {
|
||||
mask |= NSShiftKeyMask;
|
||||
}
|
||||
if (desc->modifier_mac & MENU_KEY_CTRL) {
|
||||
mask |= NSControlKeyMask;
|
||||
}
|
||||
if (desc->modifier_mac & MENU_KEY_ALT) {
|
||||
mask |= NSAlternateKeyMask;
|
||||
}
|
||||
|
||||
switch (desc->key) {
|
||||
case 0x7a: { key = get_string_for_key(NSF1FunctionKey); break; } // F1
|
||||
case 0x78: { key = get_string_for_key(NSF2FunctionKey); break; } // F2
|
||||
case 0x63: { key = get_string_for_key(NSF3FunctionKey); break; } // F3
|
||||
case 0x76: { key = get_string_for_key(NSF4FunctionKey); break; } // F4
|
||||
case 0x60: { key = get_string_for_key(NSF5FunctionKey); break; } // F5
|
||||
case 0x61: { key = get_string_for_key(NSF6FunctionKey); break; } // F6
|
||||
case 0x62: { key = get_string_for_key(NSF7FunctionKey); break; } // F7
|
||||
case 0x64: { key = get_string_for_key(NSF8FunctionKey); break; } // F8
|
||||
case 0x65: { key = get_string_for_key(NSF9FunctionKey); break; } // F9
|
||||
case 0x6d: { key = get_string_for_key(NSF10FunctionKey); break; } // F10
|
||||
case 0x67: { key = get_string_for_key(NSF11FunctionKey); break; } // F11
|
||||
case 0x6f: { key = get_string_for_key(NSF12FunctionKey); break; } // F12
|
||||
case 0x7f: break;
|
||||
default: {
|
||||
key = convert_key_code_to_string(desc->key);
|
||||
}
|
||||
}
|
||||
|
||||
if (key) {
|
||||
[newItem setKeyEquivalentModifierMask: mask];
|
||||
[newItem setKeyEquivalent:key];
|
||||
}
|
||||
|
||||
if (desc->enabled) {
|
||||
[newItem setEnabled:YES];
|
||||
} else {
|
||||
[newItem setEnabled:NO];
|
||||
}
|
||||
|
||||
[newItem setOnStateImage: newItem.offStateImage];
|
||||
[menu addItem:newItem];
|
||||
[newItem release];
|
||||
}
|
||||
|
||||
desc++;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,13 +4,15 @@ use {MouseButton, MouseMode, Scale, Key, KeyRepeat, WindowOptions};
|
|||
use key_handler::KeyHandler;
|
||||
use error::Error;
|
||||
use Result;
|
||||
// use MenuItem;
|
||||
use InputCallback;
|
||||
use mouse_handler;
|
||||
use window_flags;
|
||||
use menu::Menu;
|
||||
use {MenuItem, MenuItemHandle, MenuHandle};
|
||||
// use menu::Menu;
|
||||
|
||||
use libc::{c_void, c_char, c_uchar};
|
||||
use std::ffi::{CString};
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use std::os::raw;
|
||||
|
@ -148,40 +150,42 @@ static KEY_MAPPINGS: [Key; 128] = [
|
|||
/* 7f */ Key::Unknown,
|
||||
];
|
||||
|
||||
|
||||
const STRING_SIZE: usize = 512;
|
||||
|
||||
#[repr(C)]
|
||||
struct CMenu {
|
||||
name: [i8; STRING_SIZE],
|
||||
sub_menu: *mut raw::c_void,
|
||||
id: raw::c_int,
|
||||
key: raw::c_int,
|
||||
special_key: raw::c_int,
|
||||
modifier: raw::c_int,
|
||||
mac_mod: raw::c_int,
|
||||
enabled: raw::c_int,
|
||||
}
|
||||
|
||||
#[link(name = "Cocoa", kind = "framework")]
|
||||
#[link(name = "Carbon", kind = "framework")]
|
||||
extern {
|
||||
fn mfb_open(name: *const c_char, width: u32, height: u32, flags: u32, scale: i32) -> *mut c_void;
|
||||
extern "C" {
|
||||
fn mfb_open(name: *const c_char,
|
||||
width: u32,
|
||||
height: u32,
|
||||
flags: u32,
|
||||
scale: i32)
|
||||
-> *mut c_void;
|
||||
fn mfb_close(window: *mut c_void);
|
||||
fn mfb_update(window: *mut c_void);
|
||||
fn mfb_update_with_buffer(window: *mut c_void, buffer: *const c_uchar);
|
||||
fn mfb_set_position(window: *mut c_void, x: i32, y: i32);
|
||||
fn mfb_set_key_callback(window: *mut c_void, target: *mut c_void,
|
||||
cb: unsafe extern fn(*mut c_void, i32, i32),
|
||||
cb: unsafe extern fn(*mut c_void, u32));
|
||||
fn mfb_set_key_callback(window: *mut c_void,
|
||||
target: *mut c_void,
|
||||
cb: unsafe extern "C" fn(*mut c_void, i32, i32),
|
||||
cb: unsafe extern "C" fn(*mut c_void, u32));
|
||||
fn mfb_set_mouse_data(window_handle: *mut c_void, shared_data: *mut SharedData);
|
||||
fn mfb_should_close(window: *mut c_void) -> i32;
|
||||
fn mfb_get_screen_size() -> u32;
|
||||
fn mfb_is_active(window: *mut c_void) -> u32;
|
||||
fn mfb_add_menu(window: *mut c_void, name: *const c_char, menu: *mut c_void);
|
||||
fn mfb_remove_menu(window: *mut c_void, name: *const c_char);
|
||||
fn mfb_update_menu(window: *mut c_void, name: *const c_char, menu: *mut c_void);
|
||||
fn mfb_add_menu(window: *mut c_void, menu: *mut c_void) -> u64;
|
||||
fn mfb_add_sub_menu(parent_menu: *mut c_void, name: *const c_char, menu: *mut c_void);
|
||||
fn mfb_active_menu(window: *mut c_void) -> i32;
|
||||
|
||||
fn mfb_create_menu(name: *const c_char) -> *mut c_void;
|
||||
fn mfb_remove_menu_at(window: *mut c_void, index: i32);
|
||||
|
||||
fn mfb_add_menu_item(menu_item: *mut c_void,
|
||||
menu_id: i32,
|
||||
name: *const c_char,
|
||||
enabled: bool,
|
||||
key: u32,
|
||||
modifier: u32)
|
||||
-> u64;
|
||||
fn mfb_remove_menu_item(menu: *mut c_void, item_handle: u64);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -202,6 +206,7 @@ pub struct Window {
|
|||
pub shared_data: SharedData,
|
||||
key_handler: KeyHandler,
|
||||
pub has_set_data: bool,
|
||||
menus: Vec<MenuHandle>,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, state: i32) {
|
||||
|
@ -241,7 +246,11 @@ impl Window {
|
|||
|
||||
unsafe {
|
||||
let scale_factor = Self::get_scale_factor(width, height, opts.scale) as usize;
|
||||
let handle = mfb_open(n.as_ptr(), width as u32, height as u32, window_flags::get_flags(opts), scale_factor as i32);
|
||||
let handle = mfb_open(n.as_ptr(),
|
||||
width as u32,
|
||||
height as u32,
|
||||
window_flags::get_flags(opts),
|
||||
scale_factor as i32);
|
||||
|
||||
if handle == ptr::null_mut() {
|
||||
return Err(Error::WindowCreate("Unable to open Window".to_owned()));
|
||||
|
@ -253,10 +262,11 @@ impl Window {
|
|||
shared_data: SharedData {
|
||||
width: width as u32 * scale_factor as u32,
|
||||
height: height as u32 * scale_factor as u32,
|
||||
.. SharedData::default()
|
||||
..SharedData::default()
|
||||
},
|
||||
key_handler: KeyHandler::new(),
|
||||
has_set_data: false,
|
||||
menus: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -277,7 +287,10 @@ impl Window {
|
|||
unsafe {
|
||||
mfb_update_with_buffer(self.window_handle, buffer.as_ptr() as *const u8);
|
||||
Self::set_mouse_data(self);
|
||||
mfb_set_key_callback(self.window_handle, mem::transmute(self), key_callback, char_callback);
|
||||
mfb_set_key_callback(self.window_handle,
|
||||
mem::transmute(self),
|
||||
key_callback,
|
||||
char_callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,7 +300,10 @@ impl Window {
|
|||
unsafe {
|
||||
mfb_update(self.window_handle);
|
||||
Self::set_mouse_data(self);
|
||||
mfb_set_key_callback(self.window_handle, mem::transmute(self), key_callback, char_callback);
|
||||
mfb_set_key_callback(self.window_handle,
|
||||
mem::transmute(self),
|
||||
key_callback,
|
||||
char_callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,7 +313,8 @@ impl Window {
|
|||
}
|
||||
|
||||
pub fn get_size(&self) -> (usize, usize) {
|
||||
(self.shared_data.width as usize, self.shared_data.height as usize)
|
||||
(self.shared_data.width as usize,
|
||||
self.shared_data.height as usize)
|
||||
}
|
||||
|
||||
pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> {
|
||||
|
@ -324,7 +341,12 @@ impl Window {
|
|||
let w = self.shared_data.width as f32;
|
||||
let h = self.shared_data.height as f32;
|
||||
|
||||
mouse_handler::get_pos(mode, self.shared_data.mouse_x, self.shared_data.mouse_y, s, w, h)
|
||||
mouse_handler::get_pos(mode,
|
||||
self.shared_data.mouse_x,
|
||||
self.shared_data.mouse_y,
|
||||
s,
|
||||
w,
|
||||
h)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -358,7 +380,7 @@ impl Window {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_input_callback(&mut self, callback: Box<InputCallback>) {
|
||||
pub fn set_input_callback(&mut self, callback: Box<InputCallback>) {
|
||||
self.key_handler.set_input_callback(callback)
|
||||
}
|
||||
|
||||
|
@ -372,40 +394,25 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_menu(&mut self, name: &str, menu: &Vec<Menu>) -> Result<()> {
|
||||
let mut build_menu = Vec::<Vec<CMenu>>::new();
|
||||
|
||||
pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle {
|
||||
unsafe {
|
||||
Self::recursive_convert(&mut build_menu, &Some(menu));
|
||||
let menu_len = build_menu.len();
|
||||
mfb_add_menu(self.window_handle,
|
||||
CString::new(name).unwrap().as_ptr(),
|
||||
build_menu[menu_len - 1].as_mut_ptr() as *mut c_void);
|
||||
let handle = MenuHandle(mfb_add_menu(self.window_handle, menu.menu_handle));
|
||||
self.menus.push(handle);
|
||||
handle
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_menu(&mut self, name: &str, menu: &Vec<Menu>) -> Result<()> {
|
||||
let mut build_menu = Vec::<Vec<CMenu>>::new();
|
||||
|
||||
unsafe {
|
||||
Self::recursive_convert(&mut build_menu, &Some(menu));
|
||||
let menu_len = build_menu.len();
|
||||
mfb_update_menu(self.window_handle,
|
||||
CString::new(name).unwrap().as_ptr(),
|
||||
build_menu[menu_len - 1].as_mut_ptr() as *mut c_void);
|
||||
pub fn remove_menu(&mut self, handle: MenuHandle) {
|
||||
for i in 0..self.menus.len() {
|
||||
if self.menus[i] == handle {
|
||||
self.menus.remove(i);
|
||||
unsafe {
|
||||
// + 1 here as we always have a default menu we shouldn't remove
|
||||
mfb_remove_menu_at(self.window_handle, (i + 1) as i32);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_menu(&mut self, name: &str) -> Result<()> {
|
||||
unsafe {
|
||||
mfb_remove_menu(self.window_handle, CString::new(name).unwrap().as_ptr());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -450,8 +457,21 @@ impl Window {
|
|||
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn map_key_to_menu_key(key: Key) -> i32 {
|
||||
pub struct Menu {
|
||||
menu_handle: *mut c_void,
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new(name: &str) -> Result<Menu> {
|
||||
unsafe {
|
||||
let menu_name = CString::new(name).unwrap();
|
||||
Ok(Menu { menu_handle: mfb_create_menu(menu_name.as_ptr()) })
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn map_key_to_menu_key(key: Key) -> u32 {
|
||||
match key {
|
||||
Key::A => 0x00,
|
||||
Key::S => 0x01,
|
||||
|
@ -500,10 +520,10 @@ impl Window {
|
|||
Key::N => 0x2d,
|
||||
Key::M => 0x2e,
|
||||
Key::Period => 0x2f,
|
||||
//Key::Tab => 0x30,
|
||||
// Key::Tab => 0x30,
|
||||
Key::Space => 0x31,
|
||||
//Key::Backspace => 0x33,
|
||||
//Key::Escape => 0x35,
|
||||
// Key::Backspace => 0x33,
|
||||
// Key::Escape => 0x35,
|
||||
Key::RightSuper => 0x36,
|
||||
Key::LeftSuper => 0x37,
|
||||
Key::LeftShift => 0x38,
|
||||
|
@ -513,7 +533,7 @@ impl Window {
|
|||
Key::RightShift => 0x3c,
|
||||
Key::RightAlt => 0x3d,
|
||||
Key::RightCtrl => 0x3e,
|
||||
//Key::Equal => 0x51,
|
||||
// Key::Equal => 0x51,
|
||||
Key::NumPad0 => 0x52,
|
||||
Key::NumPad1 => 0x53,
|
||||
Key::NumPad2 => 0x54,
|
||||
|
@ -537,23 +557,23 @@ impl Window {
|
|||
Key::F15 => 0x71,
|
||||
Key::Insert => 0x72, /* Really Help... */
|
||||
Key::Home => 0x73,
|
||||
//Key::PageUp => 0x74,
|
||||
// Key::PageUp => 0x74,
|
||||
Key::Delete => 0x75,
|
||||
Key::F4 => 0x76,
|
||||
Key::End => 0x77,
|
||||
Key::F2 => 0x78,
|
||||
//Key::PageDown => 0x79,
|
||||
// Key::PageDown => 0x79,
|
||||
Key::F1 => 0x7a,
|
||||
//Key::Left => 0x7b,
|
||||
//Key::Right => 0x7c,
|
||||
//Key::Down => 0x7d,
|
||||
//Key::Up => 0x7e,
|
||||
// Key::Left => 0x7b,
|
||||
// Key::Right => 0x7c,
|
||||
// Key::Down => 0x7d,
|
||||
// Key::Up => 0x7e,
|
||||
Key::Left => 0x2190,
|
||||
Key::Up => 0x2191,
|
||||
Key::Down => 0x2193,
|
||||
Key::Right => 0x2192,
|
||||
Key::Escape => 0x238b,
|
||||
//Key::Enter => 0x000d,
|
||||
// Key::Enter => 0x000d,
|
||||
Key::Backspace => 0x232b,
|
||||
Key::Tab => 0x21e4,
|
||||
Key::PageUp => 0x21de,
|
||||
|
@ -562,55 +582,31 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe fn recursive_convert(menu_build_vec: &mut Vec<Vec<CMenu>>, in_menu: &Option<&Vec<Menu>>) -> *mut raw::c_void {
|
||||
if in_menu.is_none() {
|
||||
return ptr::null_mut();
|
||||
pub fn add_sub_menu(&mut self, name: &str, sub_menu: &Menu) {
|
||||
unsafe {
|
||||
let menu_name = CString::new(name).unwrap();
|
||||
mfb_add_sub_menu(self.menu_handle, menu_name.as_ptr(), sub_menu.menu_handle)
|
||||
}
|
||||
}
|
||||
|
||||
let mut menu_build = Vec::<CMenu>::new();
|
||||
let menu_vec = in_menu.as_ref().unwrap();
|
||||
pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle {
|
||||
unsafe {
|
||||
let item_name = CString::new(item.label.as_str()).unwrap();
|
||||
let conv_key = Self::map_key_to_menu_key(item.key);
|
||||
|
||||
for m in menu_vec.iter() {
|
||||
let key_map = Self::map_key_to_menu_key(m.key);
|
||||
|
||||
let mut menu = CMenu {
|
||||
name: mem::uninitialized(),
|
||||
id: m.id as raw::c_int,
|
||||
key: key_map as raw::c_int,
|
||||
special_key: 0,
|
||||
modifier: m.modifier as raw::c_int,
|
||||
mac_mod: m.mac_mod as raw::c_int,
|
||||
enabled: m.enabled as raw::c_int,
|
||||
sub_menu : Self::recursive_convert(menu_build_vec, &m.sub_menu),
|
||||
};
|
||||
|
||||
let name = CString::new(m.name).unwrap();
|
||||
let name_len = m.name.len();
|
||||
|
||||
ptr::copy_nonoverlapping(name.as_ptr(),
|
||||
menu.name.as_mut_ptr() as *mut i8,
|
||||
name_len);
|
||||
menu.name[name_len] = 0;
|
||||
|
||||
menu_build.push(menu);
|
||||
MenuItemHandle(mfb_add_menu_item(self.menu_handle,
|
||||
item.id as i32,
|
||||
item_name.as_ptr(),
|
||||
item.enabled,
|
||||
conv_key,
|
||||
item.modifier))
|
||||
}
|
||||
}
|
||||
|
||||
// end marker
|
||||
|
||||
menu_build.push(CMenu {
|
||||
name: [0; STRING_SIZE],
|
||||
id: -2,
|
||||
key: 0,
|
||||
special_key: 0,
|
||||
modifier: 0,
|
||||
mac_mod: 0,
|
||||
enabled: 0,
|
||||
sub_menu : ptr::null_mut(),
|
||||
});
|
||||
|
||||
let ptr = menu_build.as_mut_ptr() as *mut raw::c_void ;
|
||||
menu_build_vec.push(menu_build);
|
||||
ptr
|
||||
pub fn remove_item(&mut self, handle: &MenuItemHandle) {
|
||||
unsafe {
|
||||
mfb_remove_menu_item(self.menu_handle, handle.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ const INVALID_ACCEL: usize = 0xffffffff;
|
|||
|
||||
use {Scale, Key, KeyRepeat, MouseButton, MouseMode, WindowOptions, InputCallback};
|
||||
use key_handler::KeyHandler;
|
||||
use menu::Menu;
|
||||
use error::Error;
|
||||
use Result;
|
||||
use menu::{MENU_KEY_WIN, MENU_KEY_SHIFT, MENU_KEY_CTRL, MENU_KEY_ALT};
|
||||
use {MenuItem, MenuItemHandle, MenuHandle};
|
||||
use {MENU_KEY_WIN, MENU_KEY_SHIFT, MENU_KEY_CTRL, MENU_KEY_ALT};
|
||||
|
||||
use std::ptr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
@ -298,8 +298,7 @@ pub enum MinifbError {
|
|||
}
|
||||
|
||||
fn to_wstring(str: &str) -> Vec<u16> {
|
||||
let mut v: Vec<u16> = OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect();
|
||||
v.push(0u16);
|
||||
let v: Vec<u16> = OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect();
|
||||
v
|
||||
}
|
||||
|
||||
|
@ -311,11 +310,13 @@ struct MouseData {
|
|||
pub scroll: f32,
|
||||
}
|
||||
|
||||
/*
|
||||
struct MenuStore {
|
||||
name: String,
|
||||
menu: HMENU,
|
||||
accel_items: Vec<ACCEL>,
|
||||
}
|
||||
*/
|
||||
|
||||
pub struct Window {
|
||||
mouse: MouseData,
|
||||
|
@ -326,7 +327,7 @@ pub struct Window {
|
|||
scale_factor: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
menus: Vec<MenuStore>,
|
||||
menus: Vec<Menu>,
|
||||
key_handler: KeyHandler,
|
||||
accel_table: HACCEL,
|
||||
accel_key: usize,
|
||||
|
@ -338,6 +339,7 @@ pub struct Window {
|
|||
#[allow(non_snake_case)]
|
||||
extern "system" {
|
||||
fn TranslateAcceleratorW(hWnd: HWND, accel: *const ACCEL, pmsg: *const MSG) -> INT;
|
||||
fn RemoveMenu(menu: HMENU, pos: UINT, flags: UINT) -> BOOL;
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
@ -635,6 +637,120 @@ impl Window {
|
|||
return factor;
|
||||
}
|
||||
|
||||
//
|
||||
// When attaching a menu to the window we need to resize it so
|
||||
// the current client size is preserved and still show all pixels
|
||||
//
|
||||
unsafe fn adjust_window_size_for_menu(handle: HWND) {
|
||||
let mut rect: winapi::RECT = mem::uninitialized();
|
||||
|
||||
let menu_height = user32::GetSystemMetrics(winapi::winuser::SM_CYMENU);
|
||||
|
||||
user32::GetWindowRect(handle, &mut rect);
|
||||
user32::MoveWindow(handle,
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
(rect.bottom - rect.top) + menu_height,
|
||||
1);
|
||||
}
|
||||
|
||||
unsafe fn set_accel_table(&mut self) {
|
||||
let mut temp_accel_table = Vec::<ACCEL>::new();
|
||||
|
||||
for menu in self.menus.iter() {
|
||||
for item in menu.accel_table.iter() {
|
||||
println!("virt {} - cmd {} - key {}", item.fVirt, item.cmd, item.key);
|
||||
temp_accel_table.push(item.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if self.accel_table != ptr::null_mut() {
|
||||
user32::DestroyAcceleratorTable(self.accel_table);
|
||||
}
|
||||
|
||||
self.accel_table = user32::CreateAcceleratorTableW(temp_accel_table.as_mut_ptr(),
|
||||
temp_accel_table.len() as i32);
|
||||
|
||||
println!("accel {:?}", self.accel_table);
|
||||
}
|
||||
|
||||
|
||||
pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle {
|
||||
unsafe {
|
||||
let window = self.window.unwrap();
|
||||
let mut main_menu = user32::GetMenu(window);
|
||||
|
||||
if main_menu == ptr::null_mut() {
|
||||
main_menu = user32::CreateMenu();
|
||||
user32::SetMenu(window, main_menu);
|
||||
Self::adjust_window_size_for_menu(window);
|
||||
}
|
||||
|
||||
user32::AppendMenuW(main_menu,
|
||||
0x10,
|
||||
menu.menu_handle as UINT_PTR,
|
||||
menu.name.as_ptr());
|
||||
|
||||
self.menus.push(menu.clone());
|
||||
// TODO: Setup accel table
|
||||
|
||||
//Self::add_menu_store(self, main_menu, menu_name, menu);
|
||||
self.set_accel_table();
|
||||
|
||||
user32::DrawMenuBar(window);
|
||||
}
|
||||
|
||||
// TODO: Proper handle
|
||||
|
||||
MenuHandle(menu.menu_handle as u64)
|
||||
}
|
||||
|
||||
pub fn remove_menu(&mut self, handle: MenuHandle) {
|
||||
let window = self.window.unwrap();
|
||||
let main_menu = unsafe { user32::GetMenu(window) };
|
||||
for i in 0..self.menus.len() {
|
||||
if self.menus[i].menu_handle == handle.0 as HMENU {
|
||||
unsafe {
|
||||
println!("Removed menu at {}", i);
|
||||
let _t = RemoveMenu(main_menu, i as UINT, 0);
|
||||
user32::DrawMenuBar(self.window.unwrap());
|
||||
}
|
||||
self.menus.swap_remove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_menu_pressed(&mut self) -> Option<usize> {
|
||||
if self.accel_key == INVALID_ACCEL {
|
||||
None
|
||||
} else {
|
||||
let t = self.accel_key;
|
||||
self.accel_key = INVALID_ACCEL;
|
||||
Some(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Menu {
|
||||
menu_handle: HMENU,
|
||||
name: Vec<u16>,
|
||||
accel_table: Vec<ACCEL>,
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new(name: &str) -> Result<Menu> {
|
||||
unsafe {
|
||||
Ok(Menu {
|
||||
menu_handle: user32::CreatePopupMenu(),
|
||||
name: to_wstring(name),
|
||||
accel_table: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn map_key_to_vk_accel(key: Key) -> (raw::c_int, &'static str) {
|
||||
match key {
|
||||
Key::Key0 => (0x30, "0"),
|
||||
|
@ -748,27 +864,20 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// When attaching a menu to the window we need to resize it so
|
||||
// the current client size is preserved and still show all pixels
|
||||
//
|
||||
unsafe fn adjust_window_size_for_menu(handle: HWND) {
|
||||
let mut rect: winapi::RECT = mem::uninitialized();
|
||||
|
||||
let menu_height = user32::GetSystemMetrics(winapi::winuser::SM_CYMENU);
|
||||
|
||||
user32::GetWindowRect(handle, &mut rect);
|
||||
user32::MoveWindow(handle,
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
(rect.bottom - rect.top) + menu_height,
|
||||
1);
|
||||
pub fn add_sub_menu(&mut self, name: &str, menu: &Menu) {
|
||||
unsafe {
|
||||
let menu_name = to_wstring(name);
|
||||
user32::AppendMenuW(self.menu_handle,
|
||||
0x10,
|
||||
menu.menu_handle as UINT_PTR,
|
||||
menu_name.as_ptr());
|
||||
self.accel_table.extend_from_slice(menu.accel_table.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
fn format_name(menu_item: &Menu, key_name: &'static str) -> String {
|
||||
let mut name = menu_item.name.to_owned();
|
||||
|
||||
fn format_name(menu_item: &MenuItem, key_name: &'static str) -> String {
|
||||
let mut name = menu_item.label.clone();
|
||||
|
||||
name.push_str("\t");
|
||||
|
||||
|
@ -793,16 +902,20 @@ impl Window {
|
|||
name
|
||||
}
|
||||
|
||||
fn is_key_virtual_range(key: raw::c_int) -> u32 {
|
||||
fn is_key_virtual_range(_key: raw::c_int) -> u32 {
|
||||
/*
|
||||
if (key >= 0x30 && key <= 0x30) ||
|
||||
(key >= 0x41 && key <= 0x5a) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
*/
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
fn get_virt_key(menu_item: &Menu, key: raw::c_int) -> u32 {
|
||||
fn get_virt_key(menu_item: &MenuItem, key: raw::c_int) -> u32 {
|
||||
let mut virt = Self::is_key_virtual_range(key);
|
||||
|
||||
if (menu_item.modifier & MENU_KEY_ALT) == MENU_KEY_ALT {
|
||||
|
@ -820,165 +933,48 @@ impl Window {
|
|||
virt
|
||||
}
|
||||
|
||||
fn add_accel(accel_table: &mut Vec<ACCEL>, menu_item: &Menu) {
|
||||
fn add_accel(&mut self, vk: raw::c_int, menu_item: &MenuItem) {
|
||||
let vk_accel = Self::map_key_to_vk_accel(menu_item.key);
|
||||
let virt = Self::get_virt_key(menu_item, vk_accel.0);
|
||||
let virt = Self::get_virt_key(menu_item, vk);
|
||||
let accel = winuser::ACCEL {
|
||||
fVirt: virt as BYTE,
|
||||
cmd: menu_item.id as WORD,
|
||||
key: vk_accel.0 as WORD };
|
||||
|
||||
accel_table.push(accel);
|
||||
self.accel_table.push(accel);
|
||||
}
|
||||
|
||||
unsafe fn add_menu_item(&mut self, parent_menu: HMENU, menu_item: &Menu) {
|
||||
let item_name = to_wstring(menu_item.name);
|
||||
pub fn add_menu_item(&mut self, menu_item: &MenuItem) -> MenuItemHandle {
|
||||
let vk_accel = Self::map_key_to_vk_accel(menu_item.key);
|
||||
|
||||
match vk_accel.0 {
|
||||
0 => {
|
||||
user32::AppendMenuW(parent_menu, 0x10, menu_item.id as UINT_PTR, item_name.as_ptr());
|
||||
},
|
||||
_ => {
|
||||
let menu_name = Self::format_name(menu_item, vk_accel.1);
|
||||
let w_name = to_wstring(&menu_name);
|
||||
user32::AppendMenuW(parent_menu, 0x10, menu_item.id as UINT_PTR, w_name.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_accel_table(&mut self) {
|
||||
let mut temp_accel_table = Vec::<ACCEL>::new();
|
||||
|
||||
for menu in self.menus.iter() {
|
||||
for item in menu.accel_items.iter() {
|
||||
println!("virt {} - cmd {} - key {}", item.fVirt, item.cmd, item.key);
|
||||
temp_accel_table.push(item.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if self.accel_table != ptr::null_mut() {
|
||||
user32::DestroyAcceleratorTable(self.accel_table);
|
||||
}
|
||||
|
||||
self.accel_table = user32::CreateAcceleratorTableW(temp_accel_table.as_mut_ptr(),
|
||||
temp_accel_table.len() as i32);
|
||||
}
|
||||
|
||||
|
||||
unsafe fn recursive_add_menu(&mut self, parent_menu: HMENU, name: &str, menu: &Vec<Menu>) -> HMENU {
|
||||
let menu_name = to_wstring(name);
|
||||
|
||||
let popup_menu = user32::CreatePopupMenu();
|
||||
|
||||
user32::AppendMenuW(parent_menu, 0x10, popup_menu as UINT_PTR, menu_name.as_ptr());
|
||||
|
||||
for m in menu.iter() {
|
||||
if let Some(ref sub_menu) = m.sub_menu {
|
||||
Self::recursive_add_menu(self, popup_menu, m.name, sub_menu);
|
||||
} else {
|
||||
if m.id == 0xffffffff {
|
||||
user32::AppendMenuW(popup_menu, 0x800, 0, ptr::null()); // separator
|
||||
} else {
|
||||
Self::add_menu_item(self, popup_menu, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
popup_menu
|
||||
}
|
||||
|
||||
pub fn menu_exists(&mut self, menu_name: &str) -> bool {
|
||||
for menu in self.menus.iter() {
|
||||
if menu.name == menu_name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn clone_menu(accel_dest: &mut Vec<ACCEL>, menu: &Vec<Menu>) {
|
||||
for m in menu.iter() {
|
||||
if let Some(ref sub_menu) = m.sub_menu {
|
||||
Self::clone_menu(accel_dest, sub_menu);
|
||||
}
|
||||
|
||||
if m.key != Key::Unknown {
|
||||
Self::add_accel(accel_dest, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn add_menu_store(&mut self, parent_menu: HMENU, menu_name: &str, menu: &Vec<Menu>) {
|
||||
let mut items = Vec::<ACCEL>::new();
|
||||
let menu_handle = Self::recursive_add_menu(self, parent_menu, menu_name, menu);
|
||||
|
||||
Self::clone_menu(&mut items, menu);
|
||||
|
||||
self.menus.push(MenuStore {
|
||||
name: menu_name.to_owned(),
|
||||
menu: menu_handle,
|
||||
accel_items: items
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_menu(&mut self, menu_name: &str, menu: &Vec<Menu>) -> Result<()> {
|
||||
if Self::menu_exists(self, menu_name) {
|
||||
return Err(Error::MenuExists(menu_name.to_owned()));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let window = self.window.unwrap();
|
||||
let mut main_menu = user32::GetMenu(window);
|
||||
|
||||
if main_menu == ptr::null_mut() {
|
||||
main_menu = user32::CreateMenu();
|
||||
user32::SetMenu(window, main_menu);
|
||||
Self::adjust_window_size_for_menu(window);
|
||||
}
|
||||
|
||||
Self::add_menu_store(self, main_menu, menu_name, menu);
|
||||
Self::set_accel_table(self);
|
||||
|
||||
user32::DrawMenuBar(window);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_menu(&mut self, menu_name: &str, menu: &Vec<Menu>) -> Result<()> {
|
||||
try!(Self::remove_menu(self, menu_name));
|
||||
Self::add_menu(self, menu_name, menu)
|
||||
}
|
||||
|
||||
pub fn remove_menu(&mut self, menu_name: &str) -> Result<()> {
|
||||
for i in 0..self.menus.len() {
|
||||
if self.menus[i].name == menu_name {
|
||||
unsafe {
|
||||
user32::DestroyMenu(self.menus[i].menu);
|
||||
user32::DrawMenuBar(self.window.unwrap());
|
||||
self.menus.swap_remove(i);
|
||||
break;
|
||||
match vk_accel.0 {
|
||||
0 => {
|
||||
let item_name = to_wstring(&menu_item.label);
|
||||
user32::AppendMenuW(self.menu_handle, 0x10, menu_item.id as UINT_PTR, item_name.as_ptr());
|
||||
},
|
||||
_ => {
|
||||
let menu_name = Self::format_name(menu_item, vk_accel.1);
|
||||
let w_name = to_wstring(&menu_name);
|
||||
user32::AppendMenuW(self.menu_handle, 0x10, menu_item.id as UINT_PTR, w_name.as_ptr());
|
||||
self.add_accel(vk_accel.0, menu_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Proper return here
|
||||
Ok(())
|
||||
// TODO: This is not correct and needs to be fixed if remove_item is added. The
|
||||
// issue here is that AppendMenuW doesn't return a handle so it's hard to track
|
||||
// in an easy way :(
|
||||
|
||||
MenuItemHandle(0)
|
||||
}
|
||||
|
||||
pub fn is_menu_pressed(&mut self) -> Option<usize> {
|
||||
if self.accel_key == INVALID_ACCEL {
|
||||
None
|
||||
} else {
|
||||
let t = self.accel_key;
|
||||
self.accel_key = INVALID_ACCEL;
|
||||
Some(t)
|
||||
}
|
||||
pub fn remove_item(&mut self, _item: &MenuItemHandle) {
|
||||
panic!("remove item hasn't been implemented");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
|
|
Loading…
Reference in a new issue