mirror of
https://github.com/italicsjenga/rust_minifb.git
synced 2024-12-24 03:41:29 +11:00
Merge work from menu branch
This commit is contained in:
parent
73813b63f4
commit
5d9ca9e585
|
@ -2,6 +2,13 @@
|
||||||
|
|
||||||
This project follows semantic versioning.
|
This project follows semantic versioning.
|
||||||
|
|
||||||
|
### v0.5.0 (2016-03-04)
|
||||||
|
|
||||||
|
- [changed] - Proper Errors which uses ```std::Error``` as base. ```Window::new``` uses this but the API itself hasn't changed.
|
||||||
|
- [added] - Menu support on Mac and Windows. See the Menu API functions [here](http://prodbg.com/minifb/minifb/struct.Window.html#method.add_menu)
|
||||||
|
- [known issue] - ```remove_menu``` doesn't work on Windows [issue](https://github.com/emoon/rust_minifb/issues/16)
|
||||||
|
- [known issue] - On Mac when running an application from terminal on has to switch to another application and back to get menu focus. [issue](https://github.com/emoon/rust_minifb/issues/17)
|
||||||
|
|
||||||
### v0.4.0 (2016-01-31)
|
### v0.4.0 (2016-01-31)
|
||||||
|
|
||||||
This release breaks some of the API by changing names and parameters to some functions.
|
This release breaks some of the API by changing names and parameters to some functions.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "minifb"
|
name = "minifb"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
authors = ["Daniel Collin <daniel@collin.com>"]
|
authors = ["Daniel Collin <daniel@collin.com>"]
|
||||||
description = "Cross-platform window setup with optional bitmap rendering"
|
description = "Cross-platform window setup with optional bitmap rendering"
|
||||||
|
|
|
@ -14,7 +14,7 @@ Usage
|
||||||
```toml
|
```toml
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
minifb = "0.4.0"
|
minifb = "0.5.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Example
|
Example
|
||||||
|
|
126
examples/menu.rs
Normal file
126
examples/menu.rs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
extern crate minifb;
|
||||||
|
|
||||||
|
use minifb::{Window, Key, Scale, WindowOptions, Menu};
|
||||||
|
use minifb::{MENU_KEY_CTRL, MENU_KEY_COMMAND};
|
||||||
|
|
||||||
|
const WIDTH: usize = 640;
|
||||||
|
const HEIGHT: usize = 360;
|
||||||
|
|
||||||
|
const MENU_TEST_ID: usize = 1;
|
||||||
|
const OTHER_MENU_ID: usize = 2;
|
||||||
|
const COLOR_0_ID: usize = 3;
|
||||||
|
const COLOR_1_ID: usize = 4;
|
||||||
|
const COLOR_2_ID: usize = 5;
|
||||||
|
const CLOSE_MENU_ID: usize = 6;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut buffer: Vec<u32> = vec![0; WIDTH * HEIGHT];
|
||||||
|
|
||||||
|
let mut window = Window::new("Noise Test - Press ESC to exit",
|
||||||
|
WIDTH,
|
||||||
|
HEIGHT,
|
||||||
|
WindowOptions {
|
||||||
|
resize: true,
|
||||||
|
scale: Scale::X2,
|
||||||
|
..WindowOptions::default()
|
||||||
|
})
|
||||||
|
.expect("Unable to Open Window");
|
||||||
|
|
||||||
|
// Setup a sub menu
|
||||||
|
|
||||||
|
let sub_menu = vec![
|
||||||
|
Menu {
|
||||||
|
name: "Color 0",
|
||||||
|
id: COLOR_0_ID,
|
||||||
|
..Menu::default()
|
||||||
|
},
|
||||||
|
Menu {
|
||||||
|
name: "Color 1",
|
||||||
|
id: COLOR_1_ID,
|
||||||
|
..Menu::default()
|
||||||
|
},
|
||||||
|
Menu {
|
||||||
|
name: "Color 2",
|
||||||
|
id: COLOR_2_ID,
|
||||||
|
..Menu::default()
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Main menu
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
window.add_menu("Test", &menu).expect("Unable to add menu");
|
||||||
|
|
||||||
|
let mut color_mul = 1;
|
||||||
|
|
||||||
|
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||||
|
for y in 0..HEIGHT {
|
||||||
|
for x in 0..WIDTH {
|
||||||
|
buffer[(y * WIDTH) + x] = (((x ^ y) & 0xff) * color_mul) as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.is_menu_pressed().map(|menu_id| {
|
||||||
|
match menu_id {
|
||||||
|
COLOR_0_ID => {
|
||||||
|
color_mul = 0xfe0000;
|
||||||
|
}
|
||||||
|
COLOR_1_ID => {
|
||||||
|
color_mul = 0xff00;
|
||||||
|
}
|
||||||
|
COLOR_2_ID => {
|
||||||
|
color_mul = 1;
|
||||||
|
}
|
||||||
|
CLOSE_MENU_ID => {
|
||||||
|
println!("remove menu");
|
||||||
|
window.remove_menu("Test").expect("Unable to remove menu");
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Menu id {} pressed", menu_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.get_keys().map(|keys| {
|
||||||
|
for t in keys {
|
||||||
|
match t {
|
||||||
|
Key::W => println!("holding w!"),
|
||||||
|
Key::T => println!("holding t!"),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.update_with_buffer(&buffer);
|
||||||
|
}
|
||||||
|
}
|
48
src/error.rs
Normal file
48
src/error.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use std::error::Error as StdError;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Errors that can be return from various operatiors
|
||||||
|
///
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Returned if menu Menu function isn't supported
|
||||||
|
MenusNotSupported,
|
||||||
|
/// Menu already exists
|
||||||
|
MenuExists(String),
|
||||||
|
/// Menu already exists
|
||||||
|
WindowCreate(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdError for Error {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
Error::MenusNotSupported => "Menus not supported",
|
||||||
|
Error::MenuExists(_) => "Menu already exists",
|
||||||
|
Error::WindowCreate(_) => "Failed to create Window",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&StdError> {
|
||||||
|
match *self {
|
||||||
|
Error::MenusNotSupported => None,
|
||||||
|
Error::MenuExists(_) => None,
|
||||||
|
Error::WindowCreate(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Error::MenusNotSupported => {
|
||||||
|
write!(fmt, "{}", self.description())
|
||||||
|
},
|
||||||
|
Error::MenuExists(ref e) => {
|
||||||
|
write!(fmt, "{} {:?}", self.description(), e)
|
||||||
|
},
|
||||||
|
Error::WindowCreate(ref e) => {
|
||||||
|
write!(fmt, "{} {:?}", self.description(), e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
src/key.rs
Normal file
128
src/key.rs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/// Key is used by the get key functions to check if some keys on the keyboard has been pressed
|
||||||
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
|
pub enum Key {
|
||||||
|
Key0 = 0,
|
||||||
|
Key1 = 1,
|
||||||
|
Key2 = 2,
|
||||||
|
Key3 = 3,
|
||||||
|
Key4 = 4,
|
||||||
|
Key5 = 5,
|
||||||
|
Key6 = 6,
|
||||||
|
Key7 = 7,
|
||||||
|
Key8 = 8,
|
||||||
|
Key9 = 9,
|
||||||
|
|
||||||
|
A = 10,
|
||||||
|
B = 11,
|
||||||
|
C = 12,
|
||||||
|
D = 13,
|
||||||
|
E = 14,
|
||||||
|
F = 15,
|
||||||
|
G = 16,
|
||||||
|
H = 17,
|
||||||
|
I = 18,
|
||||||
|
J = 19,
|
||||||
|
K = 20,
|
||||||
|
L = 21,
|
||||||
|
M = 22,
|
||||||
|
N = 23,
|
||||||
|
O = 24,
|
||||||
|
P = 25,
|
||||||
|
Q = 26,
|
||||||
|
R = 27,
|
||||||
|
S = 28,
|
||||||
|
T = 29,
|
||||||
|
U = 30,
|
||||||
|
V = 31,
|
||||||
|
W = 32,
|
||||||
|
X = 33,
|
||||||
|
Y = 34,
|
||||||
|
Z = 35,
|
||||||
|
|
||||||
|
F1,
|
||||||
|
F2,
|
||||||
|
F3,
|
||||||
|
F4,
|
||||||
|
F5,
|
||||||
|
F6,
|
||||||
|
F7,
|
||||||
|
F8,
|
||||||
|
F9,
|
||||||
|
F10,
|
||||||
|
F11,
|
||||||
|
F12,
|
||||||
|
F13,
|
||||||
|
F14,
|
||||||
|
F15,
|
||||||
|
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Apostrophe,
|
||||||
|
Backquote,
|
||||||
|
|
||||||
|
Backslash,
|
||||||
|
Comma,
|
||||||
|
Equal,
|
||||||
|
LeftBracket,
|
||||||
|
Minus,
|
||||||
|
Period,
|
||||||
|
RightBracket,
|
||||||
|
Semicolon,
|
||||||
|
|
||||||
|
Slash,
|
||||||
|
Backspace,
|
||||||
|
Delete,
|
||||||
|
End,
|
||||||
|
Enter,
|
||||||
|
|
||||||
|
Escape,
|
||||||
|
|
||||||
|
Home,
|
||||||
|
Insert,
|
||||||
|
Menu,
|
||||||
|
|
||||||
|
PageDown,
|
||||||
|
PageUp,
|
||||||
|
|
||||||
|
Pause,
|
||||||
|
Space,
|
||||||
|
Tab,
|
||||||
|
NumLock,
|
||||||
|
CapsLock,
|
||||||
|
ScrollLock,
|
||||||
|
LeftShift,
|
||||||
|
RightShift,
|
||||||
|
LeftCtrl,
|
||||||
|
RightCtrl,
|
||||||
|
|
||||||
|
NumPad0,
|
||||||
|
NumPad1,
|
||||||
|
NumPad2,
|
||||||
|
NumPad3,
|
||||||
|
NumPad4,
|
||||||
|
NumPad5,
|
||||||
|
NumPad6,
|
||||||
|
NumPad7,
|
||||||
|
NumPad8,
|
||||||
|
NumPad9,
|
||||||
|
NumPadDot,
|
||||||
|
NumPadSlash,
|
||||||
|
NumPadAsterisk,
|
||||||
|
NumPadMinus,
|
||||||
|
NumPadPlus,
|
||||||
|
NumPadEnter,
|
||||||
|
|
||||||
|
LeftAlt,
|
||||||
|
RightAlt,
|
||||||
|
|
||||||
|
LeftSuper,
|
||||||
|
RightSuper,
|
||||||
|
|
||||||
|
/// Used when an Unknown key has been pressed
|
||||||
|
Unknown,
|
||||||
|
|
||||||
|
Count = 107,
|
||||||
|
}
|
||||||
|
|
195
src/lib.rs
195
src/lib.rs
|
@ -42,133 +42,6 @@ pub enum MouseButton
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Key is used by the get key functions to check if some keys on the keyboard has been pressed
|
|
||||||
#[derive(PartialEq, Clone, Copy)]
|
|
||||||
pub enum Key {
|
|
||||||
Key0 = 0,
|
|
||||||
Key1 = 1,
|
|
||||||
Key2 = 2,
|
|
||||||
Key3 = 3,
|
|
||||||
Key4 = 4,
|
|
||||||
Key5 = 5,
|
|
||||||
Key6 = 6,
|
|
||||||
Key7 = 7,
|
|
||||||
Key8 = 8,
|
|
||||||
Key9 = 9,
|
|
||||||
|
|
||||||
A = 10,
|
|
||||||
B = 11,
|
|
||||||
C = 12,
|
|
||||||
D = 13,
|
|
||||||
E = 14,
|
|
||||||
F = 15,
|
|
||||||
G = 16,
|
|
||||||
H = 17,
|
|
||||||
I = 18,
|
|
||||||
J = 19,
|
|
||||||
K = 20,
|
|
||||||
L = 21,
|
|
||||||
M = 22,
|
|
||||||
N = 23,
|
|
||||||
O = 24,
|
|
||||||
P = 25,
|
|
||||||
Q = 26,
|
|
||||||
R = 27,
|
|
||||||
S = 28,
|
|
||||||
T = 29,
|
|
||||||
U = 30,
|
|
||||||
V = 31,
|
|
||||||
W = 32,
|
|
||||||
X = 33,
|
|
||||||
Y = 34,
|
|
||||||
Z = 35,
|
|
||||||
|
|
||||||
F1,
|
|
||||||
F2,
|
|
||||||
F3,
|
|
||||||
F4,
|
|
||||||
F5,
|
|
||||||
F6,
|
|
||||||
F7,
|
|
||||||
F8,
|
|
||||||
F9,
|
|
||||||
F10,
|
|
||||||
F11,
|
|
||||||
F12,
|
|
||||||
F13,
|
|
||||||
F14,
|
|
||||||
F15,
|
|
||||||
|
|
||||||
Down,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Up,
|
|
||||||
Apostrophe,
|
|
||||||
Backquote,
|
|
||||||
|
|
||||||
Backslash,
|
|
||||||
Comma,
|
|
||||||
Equal,
|
|
||||||
LeftBracket,
|
|
||||||
Minus,
|
|
||||||
Period,
|
|
||||||
RightBracket,
|
|
||||||
Semicolon,
|
|
||||||
|
|
||||||
Slash,
|
|
||||||
Backspace,
|
|
||||||
Delete,
|
|
||||||
End,
|
|
||||||
Enter,
|
|
||||||
|
|
||||||
Escape,
|
|
||||||
|
|
||||||
Home,
|
|
||||||
Insert,
|
|
||||||
Menu,
|
|
||||||
|
|
||||||
PageDown,
|
|
||||||
PageUp,
|
|
||||||
|
|
||||||
Pause,
|
|
||||||
Space,
|
|
||||||
Tab,
|
|
||||||
NumLock,
|
|
||||||
CapsLock,
|
|
||||||
ScrollLock,
|
|
||||||
LeftShift,
|
|
||||||
RightShift,
|
|
||||||
LeftCtrl,
|
|
||||||
RightCtrl,
|
|
||||||
|
|
||||||
NumPad0,
|
|
||||||
NumPad1,
|
|
||||||
NumPad2,
|
|
||||||
NumPad3,
|
|
||||||
NumPad4,
|
|
||||||
NumPad5,
|
|
||||||
NumPad6,
|
|
||||||
NumPad7,
|
|
||||||
NumPad8,
|
|
||||||
NumPad9,
|
|
||||||
NumPadDot,
|
|
||||||
NumPadSlash,
|
|
||||||
NumPadAsterisk,
|
|
||||||
NumPadMinus,
|
|
||||||
NumPadPlus,
|
|
||||||
NumPadEnter,
|
|
||||||
|
|
||||||
LeftAlt,
|
|
||||||
RightAlt,
|
|
||||||
|
|
||||||
LeftSuper,
|
|
||||||
RightSuper,
|
|
||||||
|
|
||||||
/// Used when an Unknown key has been pressed
|
|
||||||
Unknown,
|
|
||||||
|
|
||||||
Count = 107,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Key is used by the get key functions to check if some keys on the keyboard has been pressed
|
/// Key is used by the get key functions to check if some keys on the keyboard has been pressed
|
||||||
#[derive(PartialEq, Clone, Copy)]
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
|
@ -186,10 +59,24 @@ extern crate libc;
|
||||||
use std::os::raw;
|
use std::os::raw;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
mod error;
|
||||||
|
pub use self::error::Error;
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
pub mod key;
|
||||||
|
pub use key::Key as Key;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
mod mouse_handler;
|
mod mouse_handler;
|
||||||
mod key_handler;
|
mod key_handler;
|
||||||
mod window_flags;
|
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;
|
||||||
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use self::os::macos as imp;
|
use self::os::macos as imp;
|
||||||
|
@ -277,7 +164,7 @@ impl Window {
|
||||||
/// }
|
/// }
|
||||||
///};
|
///};
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window, &str> {
|
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window> {
|
||||||
imp::Window::new(name, width, height, opts).map(Window)
|
imp::Window::new(name, width, height, opts).map(Window)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,6 +409,58 @@ impl Window {
|
||||||
pub fn set_key_repeat_rate(&mut self, rate: f32) {
|
pub fn set_key_repeat_rate(&mut self, rate: f32) {
|
||||||
self.0.set_key_repeat_rate(rate)
|
self.0.set_key_repeat_rate(rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns if this windows is the current active one
|
||||||
|
///
|
||||||
|
#[inline]
|
||||||
|
pub fn is_active(&mut self) -> bool {
|
||||||
|
self.0.is_active()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// This allows adding menus to your windows. As menus behaves a bit diffrently depending on
|
||||||
|
/// Operating system here is how it works. See [Menu] for description on each field.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// Windows:
|
||||||
|
/// Each window has their own menu and shortcuts are active depending on active window.
|
||||||
|
/// Mac:
|
||||||
|
/// As Mac uses one menu for the whole program the menu will change depending
|
||||||
|
/// on which window you have active.
|
||||||
|
/// Linux/BSD/etc:
|
||||||
|
/// Menus aren't supported as they depend on each WindowManager and is outside of the
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Check if a menu item has been pressed
|
||||||
|
///
|
||||||
|
#[inline]
|
||||||
|
pub fn is_menu_pressed(&mut self) -> Option<usize> {
|
||||||
|
self.0.is_menu_pressed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Impl for WindowOptions
|
// Impl for WindowOptions
|
||||||
|
|
59
src/menu.rs
Normal file
59
src/menu.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use Key;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// 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>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Menu<'a> {
|
||||||
|
pub fn separotor() -> Menu<'a> {
|
||||||
|
Menu {
|
||||||
|
id: MENU_ID_SEPARATOR,
|
||||||
|
.. Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ const uint32_t WINDOW_BORDERLESS = 1 << 1;
|
||||||
const uint32_t WINDOW_RESIZE = 1 << 2;
|
const uint32_t WINDOW_RESIZE = 1 << 2;
|
||||||
const uint32_t WINDOW_TITLE = 1 << 3;
|
const uint32_t WINDOW_TITLE = 1 << 3;
|
||||||
|
|
||||||
|
static void create_standard_menu();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
|
@ -21,11 +23,14 @@ const uint32_t WINDOW_TITLE = 1 << 3;
|
||||||
|
|
||||||
void* mfb_open(const char* name, int width, int height, uint32_t flags, int scale)
|
void* mfb_open(const char* name, int width, int height, uint32_t flags, int scale)
|
||||||
{
|
{
|
||||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
bool prev_init = s_init;
|
||||||
|
|
||||||
|
//NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
|
||||||
if (!s_init) {
|
if (!s_init) {
|
||||||
[NSApplication sharedApplication];
|
[NSApplication sharedApplication];
|
||||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||||
|
create_standard_menu();
|
||||||
s_init = true;
|
s_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +62,10 @@ void* mfb_open(const char* name, int width, int height, uint32_t flags, int scal
|
||||||
window->scale = scale;
|
window->scale = scale;
|
||||||
window->key_callback = 0;
|
window->key_callback = 0;
|
||||||
window->shared_data = 0;
|
window->shared_data = 0;
|
||||||
|
window->active_menu_id = -1;
|
||||||
|
|
||||||
|
window->menu_data = malloc(sizeof(MenuData));
|
||||||
|
memset(window->menu_data, 0, sizeof(MenuData));
|
||||||
|
|
||||||
[window updateSize];
|
[window updateSize];
|
||||||
|
|
||||||
|
@ -69,11 +78,124 @@ void* mfb_open(const char* name, int width, int height, uint32_t flags, int scal
|
||||||
|
|
||||||
[NSApp activateIgnoringOtherApps:YES];
|
[NSApp activateIgnoringOtherApps:YES];
|
||||||
|
|
||||||
[pool drain];
|
if (!prev_init)
|
||||||
|
[NSApp finishLaunching];
|
||||||
|
|
||||||
|
//[pool drain];
|
||||||
|
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static NSString* findAppName(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
|
||||||
|
|
||||||
|
// Keys to search for as potential application names
|
||||||
|
NSString* GLFWNameKeys[] =
|
||||||
|
{
|
||||||
|
@"CFBundleDisplayName",
|
||||||
|
@"CFBundleName",
|
||||||
|
@"CFBundleExecutable",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(GLFWNameKeys) / sizeof(GLFWNameKeys[0]); i++)
|
||||||
|
{
|
||||||
|
id name = [infoDictionary objectForKey:GLFWNameKeys[i]];
|
||||||
|
if (name &&
|
||||||
|
[name isKindOfClass:[NSString class]] &&
|
||||||
|
![name isEqualToString:@""])
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern char** _NSGetProgname();
|
||||||
|
char* progname = *_NSGetProgname();
|
||||||
|
|
||||||
|
if (progname)
|
||||||
|
return [NSString stringWithUTF8String:progname];
|
||||||
|
|
||||||
|
// Really shouldn't get here
|
||||||
|
return @"Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static void create_standard_menu(void)
|
||||||
|
{
|
||||||
|
NSString* appName = findAppName();
|
||||||
|
|
||||||
|
NSMenu* bar = [[NSMenu alloc] init];
|
||||||
|
[NSApp setMainMenu:bar];
|
||||||
|
|
||||||
|
NSMenuItem* appMenuItem =
|
||||||
|
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||||
|
NSMenu* appMenu = [[NSMenu alloc] init];
|
||||||
|
[appMenuItem setSubmenu:appMenu];
|
||||||
|
|
||||||
|
[appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName]
|
||||||
|
action:@selector(orderFrontStandardAboutPanel:)
|
||||||
|
keyEquivalent:@""];
|
||||||
|
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
NSMenu* servicesMenu = [[NSMenu alloc] init];
|
||||||
|
[NSApp setServicesMenu:servicesMenu];
|
||||||
|
[[appMenu addItemWithTitle:@"Services"
|
||||||
|
action:NULL
|
||||||
|
keyEquivalent:@""] setSubmenu:servicesMenu];
|
||||||
|
[servicesMenu release];
|
||||||
|
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName]
|
||||||
|
action:@selector(hide:)
|
||||||
|
keyEquivalent:@"h"];
|
||||||
|
[[appMenu addItemWithTitle:@"Hide Others"
|
||||||
|
action:@selector(hideOtherApplications:)
|
||||||
|
keyEquivalent:@"h"]
|
||||||
|
setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask];
|
||||||
|
[appMenu addItemWithTitle:@"Show All"
|
||||||
|
action:@selector(unhideAllApplications:)
|
||||||
|
keyEquivalent:@""];
|
||||||
|
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName]
|
||||||
|
action:@selector(terminate:)
|
||||||
|
keyEquivalent:@"q"];
|
||||||
|
|
||||||
|
/*
|
||||||
|
NSMenuItem* windowMenuItem =
|
||||||
|
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||||
|
[bar release];
|
||||||
|
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||||
|
[NSApp setWindowsMenu:windowMenu];
|
||||||
|
[windowMenuItem setSubmenu:windowMenu];
|
||||||
|
|
||||||
|
[windowMenu addItemWithTitle:@"Minimize"
|
||||||
|
action:@selector(performMiniaturize:)
|
||||||
|
keyEquivalent:@"m"];
|
||||||
|
[windowMenu addItemWithTitle:@"Zoom"
|
||||||
|
action:@selector(performZoom:)
|
||||||
|
keyEquivalent:@""];
|
||||||
|
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
[windowMenu addItemWithTitle:@"Bring All to Front"
|
||||||
|
action:@selector(arrangeInFront:)
|
||||||
|
keyEquivalent:@""];
|
||||||
|
// TODO: Make this appear at the bottom of the menu (for consistency)
|
||||||
|
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
[[windowMenu addItemWithTitle:@"Enter Full Screen"
|
||||||
|
action:@selector(toggleFullScreen:)
|
||||||
|
keyEquivalent:@"f"]
|
||||||
|
setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask];
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Prior to Snow Leopard, we need to use this oddly-named semi-private API
|
||||||
|
// to get the application menu working properly.
|
||||||
|
//SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:");
|
||||||
|
//[NSApp performSelector:setAppleMenuSelector withObject:appMenu];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void mfb_close(void* win)
|
void mfb_close(void* win)
|
||||||
|
@ -92,13 +214,28 @@ void mfb_close(void* win)
|
||||||
|
|
||||||
static int update_events()
|
static int update_events()
|
||||||
{
|
{
|
||||||
int state = 0;
|
NSEvent* event;
|
||||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||||
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||||
|
|
||||||
|
if (event) {
|
||||||
[NSApp sendEvent:event];
|
[NSApp sendEvent:event];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (event);
|
||||||
|
|
||||||
[pool release];
|
[pool release];
|
||||||
|
|
||||||
return state;
|
/*
|
||||||
|
int state = 0;
|
||||||
|
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||||
|
[NSApp sendEvent:event];
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -193,4 +330,104 @@ void mfb_set_mouse_data(void* window, SharedData* shared_data)
|
||||||
win->shared_data = shared_data;
|
win->shared_data = shared_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
uint32_t mfb_is_active(void* window)
|
||||||
|
{
|
||||||
|
OSXWindow* win = (OSXWindow*)window;
|
||||||
|
return win->is_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void mfb_remove_menu(void* window, const char* name)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Menu* menu = &win->menu_data->menus[i];
|
||||||
|
|
||||||
|
if (strcmp(menu->name, name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Menu* menu_end = &win->menu_data->menus[len - 1];
|
||||||
|
|
||||||
|
[main_menu removeItem:menu->menu_item];
|
||||||
|
|
||||||
|
// swap remove
|
||||||
|
menu->name = menu_end->name;
|
||||||
|
menu->menu = menu_end->menu;
|
||||||
|
menu->menu_item = menu_end->menu_item;
|
||||||
|
|
||||||
|
win->menu_data->menu_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int mfb_active_menu(void* window) {
|
||||||
|
OSXWindow* win = (OSXWindow*)window;
|
||||||
|
int active_menu_id = win->active_menu_id;
|
||||||
|
win->active_menu_id = -1;
|
||||||
|
return active_menu_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void mfb_add_menu(void* window, const char* name, void* m)
|
||||||
|
{
|
||||||
|
OSXWindow* win = (OSXWindow*)window;
|
||||||
|
|
||||||
|
const char* n = strdup(name);
|
||||||
|
|
||||||
|
NSString* ns_name = [NSString stringWithUTF8String: n];
|
||||||
|
|
||||||
|
NSMenu* main_menu = [NSApp mainMenu];
|
||||||
|
|
||||||
|
NSMenuItem* windowMenuItem = [main_menu addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||||
|
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:ns_name];
|
||||||
|
[NSApp setWindowsMenu:windowMenu];
|
||||||
|
[windowMenuItem setSubmenu:windowMenu];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void mfb_update_menu(void* window, const char* name, void* m)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Menu* menu = &win->menu_data->menus[i];
|
||||||
|
|
||||||
|
if (!strcmp(menu->name, name)) {
|
||||||
|
[menu->menu removeAllItems];
|
||||||
|
build_submenu(menu->menu, (MenuDesc*)m);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,42 @@
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#include "shared_data.h"
|
#include "shared_data.h"
|
||||||
|
|
||||||
|
#define MAX_MENUS 512
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
typedef struct Menu
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
NSMenu* menu;
|
||||||
|
NSMenuItem* menu_item;
|
||||||
|
} Menu;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
typedef struct MenuData
|
||||||
|
{
|
||||||
|
Menu menus[MAX_MENUS];
|
||||||
|
int menu_count;
|
||||||
|
} MenuData;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
typedef struct MenuDesc {
|
||||||
|
char name[512];
|
||||||
|
struct MenuDesc* sub_menu;
|
||||||
|
int menu_id;
|
||||||
|
int key;
|
||||||
|
int special_key;
|
||||||
|
int modifier;
|
||||||
|
int modifier_mac;
|
||||||
|
int enabled;
|
||||||
|
} MenuDesc;
|
||||||
|
|
||||||
|
void build_submenu(NSMenu* menu, MenuDesc* desc);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@interface OSXWindow : NSWindow
|
@interface OSXWindow : NSWindow
|
||||||
{
|
{
|
||||||
NSView* childContentView;
|
NSView* childContentView;
|
||||||
|
@ -12,6 +48,11 @@
|
||||||
@public void* rust_data;
|
@public void* rust_data;
|
||||||
@public SharedData* shared_data;
|
@public SharedData* shared_data;
|
||||||
@public bool should_close;
|
@public bool should_close;
|
||||||
|
@public bool is_active;
|
||||||
|
@public int active_menu_id;
|
||||||
|
@public MenuData* menu_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#import "OSXWindow.h"
|
#import "OSXWindow.h"
|
||||||
#import "OSXWindowFrameView.h"
|
#import "OSXWindowFrameView.h"
|
||||||
|
#include <Carbon/Carbon.h>
|
||||||
|
|
||||||
@implementation OSXWindow
|
@implementation OSXWindow
|
||||||
|
|
||||||
|
@ -58,21 +59,19 @@
|
||||||
|
|
||||||
// Right Super
|
// Right Super
|
||||||
key_callback(rust_data, 0x36, flags == 0x100110 ? 1 : 0);
|
key_callback(rust_data, 0x36, flags == 0x100110 ? 1 : 0);
|
||||||
|
|
||||||
|
[super flagsChanged:event];
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
- (void)keyDown:(NSEvent *)event
|
- (void)keyDown:(NSEvent *)event
|
||||||
{
|
{
|
||||||
// Cmd+Q always closes app
|
|
||||||
if ([event.characters.uppercaseString isEqualToString:@"Q"] && ([event modifierFlags] & NSCommandKeyMask)) {
|
|
||||||
[self performClose:self];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key_callback) {
|
if (key_callback) {
|
||||||
key_callback(rust_data, [event keyCode], 1);
|
key_callback(rust_data, [event keyCode], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[super keyDown:event];
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -82,12 +81,21 @@
|
||||||
if (key_callback) {
|
if (key_callback) {
|
||||||
key_callback(rust_data, [event keyCode], 0);
|
key_callback(rust_data, [event keyCode], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[super keyDown:event];
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
- (void)mainWindowChanged:(NSNotification *)aNotification
|
- (void)mainWindowChanged:(NSNotification *)note
|
||||||
{
|
{
|
||||||
|
void* window = [note object];
|
||||||
|
|
||||||
|
if (window == self) {
|
||||||
|
self->is_active = true;
|
||||||
|
} else {
|
||||||
|
self->is_active = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -118,6 +126,18 @@
|
||||||
OSXWindowFrameView* frameView = [super contentView];
|
OSXWindowFrameView* frameView = [super contentView];
|
||||||
if (!frameView)
|
if (!frameView)
|
||||||
{
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(mainWindowChanged:)
|
||||||
|
name:NSWindowDidBecomeMainNotification
|
||||||
|
object:self];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(mainWindowChanged:)
|
||||||
|
name:NSWindowDidResignMainNotification
|
||||||
|
object:self];
|
||||||
|
|
||||||
frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease];
|
frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease];
|
||||||
frameView->width = width;
|
frameView->width = width;
|
||||||
frameView->height = height;
|
frameView->height = height;
|
||||||
|
@ -180,4 +200,137 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
- (void)onMenuPress:(id)sender
|
||||||
|
{
|
||||||
|
int menu_id = (int)((NSButton*)sender).tag;
|
||||||
|
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;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc->key != 0x7f) {
|
||||||
|
NSString* 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
|
@end
|
||||||
|
|
|
@ -2,8 +2,11 @@
|
||||||
|
|
||||||
use {MouseButton, MouseMode, Scale, Key, KeyRepeat, WindowOptions};
|
use {MouseButton, MouseMode, Scale, Key, KeyRepeat, WindowOptions};
|
||||||
use key_handler::KeyHandler;
|
use key_handler::KeyHandler;
|
||||||
|
use error::Error;
|
||||||
|
use Result;
|
||||||
use mouse_handler;
|
use mouse_handler;
|
||||||
use window_flags;
|
use window_flags;
|
||||||
|
use menu::Menu;
|
||||||
|
|
||||||
use libc::{c_void, c_char, c_uchar};
|
use libc::{c_void, c_char, c_uchar};
|
||||||
use std::ffi::{CString};
|
use std::ffi::{CString};
|
||||||
|
@ -144,7 +147,23 @@ static KEY_MAPPINGS: [Key; 128] = [
|
||||||
/* 7f */ Key::Unknown,
|
/* 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 = "Cocoa", kind = "framework")]
|
||||||
|
#[link(name = "Carbon", kind = "framework")]
|
||||||
extern {
|
extern {
|
||||||
fn mfb_open(name: *const c_char, width: u32, height: u32, flags: u32, scale: i32) -> *mut c_void;
|
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_close(window: *mut c_void);
|
||||||
|
@ -155,6 +174,11 @@ extern {
|
||||||
fn mfb_set_mouse_data(window_handle: *mut c_void, shared_data: *mut SharedData);
|
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_should_close(window: *mut c_void) -> i32;
|
||||||
fn mfb_get_screen_size() -> u32;
|
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_active_menu(window: *mut c_void) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -190,11 +214,11 @@ unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, state: i32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window, &str> {
|
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window> {
|
||||||
let n = match CString::new(name) {
|
let n = match CString::new(name) {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Unable to convert {} to c_string", name);
|
println!("Unable to convert {} to c_string", name);
|
||||||
return Err("Unable to set correct name");
|
return Err(Error::WindowCreate("Unable to set correct name".to_owned()));
|
||||||
}
|
}
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
};
|
};
|
||||||
|
@ -204,7 +228,7 @@ impl Window {
|
||||||
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() {
|
if handle == ptr::null_mut() {
|
||||||
return Err("Unable to open Window");
|
return Err(Error::WindowCreate("Unable to open Window".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Window {
|
Ok(Window {
|
||||||
|
@ -313,11 +337,62 @@ impl Window {
|
||||||
self.key_handler.is_key_pressed(key, repeat)
|
self.key_handler.is_key_pressed(key, repeat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_menu_pressed(&mut self) -> Option<usize> {
|
||||||
|
let menu_id = unsafe { mfb_active_menu(self.window_handle) };
|
||||||
|
|
||||||
|
if menu_id < 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(menu_id as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_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_add_menu(self.window_handle,
|
||||||
|
CString::new(name).unwrap().as_ptr(),
|
||||||
|
build_menu[menu_len - 1].as_mut_ptr() as *mut c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
#[inline]
|
||||||
pub fn is_open(&self) -> bool {
|
pub fn is_open(&self) -> bool {
|
||||||
unsafe { mfb_should_close(self.window_handle) == 0 }
|
unsafe { mfb_should_close(self.window_handle) == 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_active(&mut self) -> bool {
|
||||||
|
unsafe { mfb_is_active(self.window_handle) == 0 }
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
|
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
|
||||||
let factor: i32 = match scale {
|
let factor: i32 = match scale {
|
||||||
Scale::X1 => 1,
|
Scale::X1 => 1,
|
||||||
|
@ -350,6 +425,168 @@ impl Window {
|
||||||
|
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn map_key_to_menu_key(key: Key) -> i32 {
|
||||||
|
match key {
|
||||||
|
Key::A => 0x00,
|
||||||
|
Key::S => 0x01,
|
||||||
|
Key::D => 0x02,
|
||||||
|
Key::F => 0x03,
|
||||||
|
Key::H => 0x04,
|
||||||
|
Key::G => 0x05,
|
||||||
|
Key::Z => 0x06,
|
||||||
|
Key::X => 0x07,
|
||||||
|
Key::C => 0x08,
|
||||||
|
Key::V => 0x09,
|
||||||
|
Key::B => 0x0b,
|
||||||
|
Key::Q => 0x0c,
|
||||||
|
Key::W => 0x0d,
|
||||||
|
Key::E => 0x0e,
|
||||||
|
Key::R => 0x0f,
|
||||||
|
Key::Y => 0x10,
|
||||||
|
Key::T => 0x11,
|
||||||
|
Key::Key1 => 0x12,
|
||||||
|
Key::Key2 => 0x13,
|
||||||
|
Key::Key3 => 0x14,
|
||||||
|
Key::Key4 => 0x15,
|
||||||
|
Key::Key6 => 0x16,
|
||||||
|
Key::Key5 => 0x17,
|
||||||
|
Key::Equal => 0x18,
|
||||||
|
Key::Key9 => 0x19,
|
||||||
|
Key::Key7 => 0x1a,
|
||||||
|
Key::Minus => 0x1b,
|
||||||
|
Key::Key8 => 0x1c,
|
||||||
|
Key::Key0 => 0x1d,
|
||||||
|
Key::RightBracket => 0x1e,
|
||||||
|
Key::O => 0x1f,
|
||||||
|
Key::U => 0x20,
|
||||||
|
Key::LeftBracket => 0x21,
|
||||||
|
Key::I => 0x22,
|
||||||
|
Key::P => 0x23,
|
||||||
|
Key::Enter => 0x24,
|
||||||
|
Key::L => 0x25,
|
||||||
|
Key::J => 0x26,
|
||||||
|
Key::Apostrophe => 0x27,
|
||||||
|
Key::K => 0x28,
|
||||||
|
Key::Semicolon => 0x29,
|
||||||
|
Key::Backslash => 0x2a,
|
||||||
|
Key::Comma => 0x2b,
|
||||||
|
Key::Slash => 0x2c,
|
||||||
|
Key::N => 0x2d,
|
||||||
|
Key::M => 0x2e,
|
||||||
|
Key::Period => 0x2f,
|
||||||
|
//Key::Tab => 0x30,
|
||||||
|
Key::Space => 0x31,
|
||||||
|
//Key::Backspace => 0x33,
|
||||||
|
//Key::Escape => 0x35,
|
||||||
|
Key::RightSuper => 0x36,
|
||||||
|
Key::LeftSuper => 0x37,
|
||||||
|
Key::LeftShift => 0x38,
|
||||||
|
Key::CapsLock => 0x39,
|
||||||
|
Key::LeftAlt => 0x3a,
|
||||||
|
Key::LeftCtrl => 0x3b,
|
||||||
|
Key::RightShift => 0x3c,
|
||||||
|
Key::RightAlt => 0x3d,
|
||||||
|
Key::RightCtrl => 0x3e,
|
||||||
|
//Key::Equal => 0x51,
|
||||||
|
Key::NumPad0 => 0x52,
|
||||||
|
Key::NumPad1 => 0x53,
|
||||||
|
Key::NumPad2 => 0x54,
|
||||||
|
Key::NumPad3 => 0x55,
|
||||||
|
Key::NumPad4 => 0x56,
|
||||||
|
Key::NumPad5 => 0x57,
|
||||||
|
Key::NumPad6 => 0x58,
|
||||||
|
Key::NumPad7 => 0x59,
|
||||||
|
Key::NumPad8 => 0x5b,
|
||||||
|
Key::NumPad9 => 0x5c,
|
||||||
|
Key::F5 => 0x60,
|
||||||
|
Key::F6 => 0x61,
|
||||||
|
Key::F7 => 0x62,
|
||||||
|
Key::F3 => 0x63,
|
||||||
|
Key::F8 => 0x64,
|
||||||
|
Key::F9 => 0x65,
|
||||||
|
Key::F11 => 0x67,
|
||||||
|
Key::F14 => 0x6b,
|
||||||
|
Key::F10 => 0x6d,
|
||||||
|
Key::F12 => 0x6f,
|
||||||
|
Key::F15 => 0x71,
|
||||||
|
Key::Insert => 0x72, /* Really Help... */
|
||||||
|
Key::Home => 0x73,
|
||||||
|
//Key::PageUp => 0x74,
|
||||||
|
Key::Delete => 0x75,
|
||||||
|
Key::F4 => 0x76,
|
||||||
|
Key::End => 0x77,
|
||||||
|
Key::F2 => 0x78,
|
||||||
|
//Key::PageDown => 0x79,
|
||||||
|
Key::F1 => 0x7a,
|
||||||
|
//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::Backspace => 0x232b,
|
||||||
|
Key::Tab => 0x21e4,
|
||||||
|
Key::PageUp => 0x21de,
|
||||||
|
Key::PageDown => 0x21df,
|
||||||
|
_ => 0x7f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut menu_build = Vec::<CMenu>::new();
|
||||||
|
let menu_vec = in_menu.as_ref().unwrap();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Window {
|
impl Drop for Window {
|
||||||
|
|
|
@ -8,7 +8,10 @@ extern crate x11_dl;
|
||||||
|
|
||||||
use {MouseMode, MouseButton, Scale, Key, KeyRepeat, WindowOptions};
|
use {MouseMode, MouseButton, Scale, Key, KeyRepeat, WindowOptions};
|
||||||
use key_handler::KeyHandler;
|
use key_handler::KeyHandler;
|
||||||
|
use menu::Menu;
|
||||||
use self::x11_dl::keysym::*;
|
use self::x11_dl::keysym::*;
|
||||||
|
use error::Error;
|
||||||
|
use Result;
|
||||||
|
|
||||||
use libc::{c_void, c_char, c_uchar};
|
use libc::{c_void, c_char, c_uchar};
|
||||||
use std::ffi::{CString};
|
use std::ffi::{CString};
|
||||||
|
@ -163,11 +166,11 @@ unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, s: i32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window, &str> {
|
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window> {
|
||||||
let n = match CString::new(name) {
|
let n = match CString::new(name) {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Unable to convert {} to c_string", name);
|
println!("Unable to convert {} to c_string", name);
|
||||||
return Err("Unable to set correct name");
|
return Err(Error::WindowCreate("Unable to set correct name".to_owned()));
|
||||||
}
|
}
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
};
|
};
|
||||||
|
@ -181,7 +184,7 @@ impl Window {
|
||||||
scale);
|
scale);
|
||||||
|
|
||||||
if handle == ptr::null_mut() {
|
if handle == ptr::null_mut() {
|
||||||
return Err("Unable to open Window");
|
return Err(Error::WindowCreate("Unable to open Window".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Window {
|
Ok(Window {
|
||||||
|
@ -289,6 +292,12 @@ impl Window {
|
||||||
unsafe { mfb_should_close(self.window_handle) == 1 }
|
unsafe { mfb_should_close(self.window_handle) == 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_active(&mut self) -> bool {
|
||||||
|
// TODO: Proper implementation
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
|
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
|
||||||
let factor: i32 = match scale {
|
let factor: i32 = match scale {
|
||||||
Scale::X1 => 1,
|
Scale::X1 => 1,
|
||||||
|
@ -329,6 +338,19 @@ impl Window {
|
||||||
|
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_menu(&mut self, _menu_name: &str, _menu: &Vec<Menu>) -> Result<()> {
|
||||||
|
Err(Error::MenusNotSupported)
|
||||||
|
}
|
||||||
|
pub fn update_menu(&mut self, _menu_name: &str, _menu: &Vec<Menu>) -> Result<()> {
|
||||||
|
Err(Error::MenusNotSupported)
|
||||||
|
}
|
||||||
|
pub fn remove_menu(&mut self, _menu_name: &str) -> Result<()> {
|
||||||
|
Err(Error::MenusNotSupported)
|
||||||
|
}
|
||||||
|
pub fn is_menu_pressed(&mut self) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Window {
|
impl Drop for Window {
|
||||||
|
|
|
@ -6,9 +6,15 @@ extern crate winapi;
|
||||||
extern crate gdi32;
|
extern crate gdi32;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
||||||
|
const INVALID_ACCEL: usize = 0xffffffff;
|
||||||
|
|
||||||
use {Scale, Key, KeyRepeat, MouseButton, MouseMode, WindowOptions};
|
use {Scale, Key, KeyRepeat, MouseButton, MouseMode, WindowOptions};
|
||||||
|
|
||||||
use key_handler::KeyHandler;
|
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 std::ptr;
|
use std::ptr;
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
@ -17,11 +23,16 @@ use std::mem;
|
||||||
use std::os::raw;
|
use std::os::raw;
|
||||||
use mouse_handler;
|
use mouse_handler;
|
||||||
|
|
||||||
use self::winapi::windef::HWND;
|
//use self::winapi::windef::HWND;
|
||||||
use self::winapi::windef::HDC;
|
//use self::winapi::windef::HDC;
|
||||||
use self::winapi::winuser::WNDCLASSW;
|
//use self::winapi::windef::HMENU;
|
||||||
use self::winapi::wingdi::BITMAPINFOHEADER;
|
//use self::winapi::wingdi::BITMAPINFOHEADER;
|
||||||
use self::winapi::wingdi::RGBQUAD;
|
//use self::winapi::wingdi::RGBQUAD;
|
||||||
|
//use self::winapi::winuser::WNDCLASSW;
|
||||||
|
//use self::winapi::winuser::ACCEL;
|
||||||
|
//use self::winapi::basetsd::UINT_PTR;
|
||||||
|
use self::winapi::*;
|
||||||
|
//use self::winapi::winmindefs::BYTE;
|
||||||
|
|
||||||
// Wrap this so we can have a proper numbef of bmiColors to write in
|
// Wrap this so we can have a proper numbef of bmiColors to write in
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -226,6 +237,12 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
winapi::winuser::WM_COMMAND => {
|
||||||
|
if lparam == 0 {
|
||||||
|
wnd.accel_key = (wparam & 0xffff) as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
winapi::winuser::WM_PAINT => {
|
winapi::winuser::WM_PAINT => {
|
||||||
|
|
||||||
// if we have nothing to draw here we return the default function
|
// if we have nothing to draw here we return the default function
|
||||||
|
@ -288,6 +305,12 @@ struct MouseData {
|
||||||
pub scroll: f32,
|
pub scroll: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MenuStore {
|
||||||
|
name: String,
|
||||||
|
menu: HMENU,
|
||||||
|
accel_items: Vec<ACCEL>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
mouse: MouseData,
|
mouse: MouseData,
|
||||||
dc: Option<HDC>,
|
dc: Option<HDC>,
|
||||||
|
@ -297,7 +320,18 @@ pub struct Window {
|
||||||
scale_factor: i32,
|
scale_factor: i32,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
|
menus: Vec<MenuStore>,
|
||||||
key_handler: KeyHandler,
|
key_handler: KeyHandler,
|
||||||
|
accel_table: HACCEL,
|
||||||
|
accel_key: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranslateAccelerator is currently missing in win-rs
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
|
#[link(name = "user32")]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
extern "system" {
|
||||||
|
fn TranslateAcceleratorW(hWnd: HWND, accel: *const ACCEL, pmsg: *const MSG) -> INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
@ -389,14 +423,14 @@ impl Window {
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
opts: WindowOptions)
|
opts: WindowOptions)
|
||||||
-> Result<Window, &str> {
|
-> Result<Window> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let scale_factor = Self::get_scale_factor(width, height, opts.scale);
|
let scale_factor = Self::get_scale_factor(width, height, opts.scale);
|
||||||
|
|
||||||
let handle = Self::open_window(name, width, height, opts, scale_factor);
|
let handle = Self::open_window(name, width, height, opts, scale_factor);
|
||||||
|
|
||||||
if handle.is_none() {
|
if handle.is_none() {
|
||||||
return Err("Unable to create Window");
|
return Err(Error::WindowCreate("Unable to create Window".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let window = Window {
|
let window = Window {
|
||||||
|
@ -409,6 +443,9 @@ impl Window {
|
||||||
scale_factor: scale_factor,
|
scale_factor: scale_factor,
|
||||||
width: width as i32,
|
width: width as i32,
|
||||||
height: height as i32,
|
height: height as i32,
|
||||||
|
menus: Vec::new(),
|
||||||
|
accel_table: ptr::null_mut(),
|
||||||
|
accel_key: INVALID_ACCEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(window)
|
Ok(window)
|
||||||
|
@ -491,7 +528,6 @@ impl Window {
|
||||||
|
|
||||||
fn generic_update(&mut self, window: HWND) {
|
fn generic_update(&mut self, window: HWND) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
||||||
let mut point: winapi::POINT = mem::uninitialized();
|
let mut point: winapi::POINT = mem::uninitialized();
|
||||||
user32::GetCursorPos(&mut point);
|
user32::GetCursorPos(&mut point);
|
||||||
user32::ScreenToClient(window, &mut point);
|
user32::ScreenToClient(window, &mut point);
|
||||||
|
@ -506,13 +542,21 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_loop(&mut self, window: HWND) {
|
fn message_loop(&self, window: HWND) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut msg = mem::uninitialized();
|
let mut msg = mem::uninitialized();
|
||||||
|
|
||||||
while user32::PeekMessageW(&mut msg, window, 0, 0, winapi::winuser::PM_REMOVE) != 0 {
|
while user32::PeekMessageW(&mut msg, window, 0, 0, winapi::winuser::PM_REMOVE) != 0 {
|
||||||
|
// Make this code a bit nicer
|
||||||
|
if self.accel_table == ptr::null_mut() {
|
||||||
user32::TranslateMessage(&mut msg);
|
user32::TranslateMessage(&mut msg);
|
||||||
user32::DispatchMessageW(&mut msg);
|
user32::DispatchMessageW(&mut msg);
|
||||||
|
} else {
|
||||||
|
if TranslateAcceleratorW(msg.hwnd, mem::transmute(self.accel_table), &mut msg) == 0 {
|
||||||
|
user32::TranslateMessage(&mut msg);
|
||||||
|
user32::DispatchMessageW(&mut msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -537,6 +581,12 @@ impl Window {
|
||||||
Self::message_loop(self, window);
|
Self::message_loop(self, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_active(&mut self) -> bool {
|
||||||
|
// TODO: Proper implementation
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
|
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
|
||||||
let factor: i32 = match scale {
|
let factor: i32 = match scale {
|
||||||
Scale::X1 => 1,
|
Scale::X1 => 1,
|
||||||
|
@ -568,6 +618,349 @@ impl Window {
|
||||||
|
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn map_key_to_vk_accel(key: Key) -> (raw::c_int, &'static str) {
|
||||||
|
match key {
|
||||||
|
Key::Key0 => (0x30, "0"),
|
||||||
|
Key::Key1 => (0x31, "1"),
|
||||||
|
Key::Key2 => (0x32, "2"),
|
||||||
|
Key::Key3 => (0x33, "3"),
|
||||||
|
Key::Key4 => (0x34, "4"),
|
||||||
|
Key::Key5 => (0x35, "5"),
|
||||||
|
Key::Key6 => (0x36, "6"),
|
||||||
|
Key::Key7 => (0x37, "7"),
|
||||||
|
Key::Key8 => (0x38, "8"),
|
||||||
|
Key::Key9 => (0x39, "9"),
|
||||||
|
|
||||||
|
Key::A => (0x41, "a"),
|
||||||
|
Key::B => (0x42, "b"),
|
||||||
|
Key::C => (0x43, "c"),
|
||||||
|
Key::D => (0x44, "d"),
|
||||||
|
Key::E => (0x45, "e"),
|
||||||
|
Key::F => (0x46, "f"),
|
||||||
|
Key::G => (0x47, "g"),
|
||||||
|
Key::H => (0x48, "h"),
|
||||||
|
Key::I => (0x49, "i"),
|
||||||
|
Key::J => (0x4a, "j"),
|
||||||
|
Key::K => (0x4b, "k"),
|
||||||
|
Key::L => (0x4c, "l"),
|
||||||
|
Key::M => (0x4d, "m"),
|
||||||
|
Key::N => (0x4e, "n"),
|
||||||
|
Key::O => (0x4f, "o"),
|
||||||
|
Key::P => (0x50, "p"),
|
||||||
|
Key::Q => (0x51, "q"),
|
||||||
|
Key::R => (0x52, "r"),
|
||||||
|
Key::S => (0x53, "s"),
|
||||||
|
Key::T => (0x54, "t"),
|
||||||
|
Key::U => (0x55, "u"),
|
||||||
|
Key::V => (0x56, "v"),
|
||||||
|
Key::W => (0x57, "w"),
|
||||||
|
Key::X => (0x58, "x"),
|
||||||
|
Key::Y => (0x59, "y"),
|
||||||
|
Key::Z => (0x5a, "z"),
|
||||||
|
|
||||||
|
Key::F1 => (winapi::winuser::VK_F1, "F1"),
|
||||||
|
Key::F2 => (winapi::winuser::VK_F2, "F2"),
|
||||||
|
Key::F3 => (winapi::winuser::VK_F3, "F3"),
|
||||||
|
Key::F4 => (winapi::winuser::VK_F4, "F4"),
|
||||||
|
Key::F5 => (winapi::winuser::VK_F5, "F5"),
|
||||||
|
Key::F6 => (winapi::winuser::VK_F6, "F6"),
|
||||||
|
Key::F7 => (winapi::winuser::VK_F7, "F7"),
|
||||||
|
Key::F8 => (winapi::winuser::VK_F8, "F8"),
|
||||||
|
Key::F9 => (winapi::winuser::VK_F9, "F9"),
|
||||||
|
Key::F10 => (winapi::winuser::VK_F10, "F10"),
|
||||||
|
Key::F11 => (winapi::winuser::VK_F11, "F11"),
|
||||||
|
Key::F12 => (winapi::winuser::VK_F12, "F12"),
|
||||||
|
Key::F13 => (winapi::winuser::VK_F13, "F14"),
|
||||||
|
Key::F14 => (winapi::winuser::VK_F14, "F14"),
|
||||||
|
Key::F15 => (winapi::winuser::VK_F15, "F15"),
|
||||||
|
|
||||||
|
Key::Down => (winapi::winuser::VK_DOWN, "Down"),
|
||||||
|
Key::Left => (winapi::winuser::VK_LEFT, "Left"),
|
||||||
|
Key::Right => (winapi::winuser::VK_RIGHT, "Right"),
|
||||||
|
Key::Up => (winapi::winuser::VK_UP, "Up"),
|
||||||
|
|
||||||
|
Key::Backslash => (winapi::winuser::VK_OEM_102, "Backslash"),
|
||||||
|
Key::Comma => (winapi::winuser::VK_OEM_COMMA, ","),
|
||||||
|
Key::Minus => (winapi::winuser::VK_OEM_MINUS, "-"),
|
||||||
|
Key::Period => (winapi::winuser::VK_OEM_PERIOD, "."),
|
||||||
|
|
||||||
|
Key::Backspace => (winapi::winuser::VK_BACK, "Back"),
|
||||||
|
Key::Delete => (winapi::winuser::VK_DELETE, "Delete"),
|
||||||
|
Key::End => (winapi::winuser::VK_END, "End"),
|
||||||
|
Key::Enter => (winapi::winuser::VK_RETURN, "Enter"),
|
||||||
|
|
||||||
|
Key::Escape => (winapi::winuser::VK_ESCAPE, "Esc"),
|
||||||
|
|
||||||
|
Key::Home => (winapi::winuser::VK_HOME, "Home"),
|
||||||
|
Key::Insert => (winapi::winuser::VK_INSERT, "Insert"),
|
||||||
|
Key::Menu => (winapi::winuser::VK_MENU, "Menu"),
|
||||||
|
|
||||||
|
Key::PageDown => (winapi::winuser::VK_NEXT, "PageDown"),
|
||||||
|
Key::PageUp => (winapi::winuser::VK_PRIOR, "PageUp"),
|
||||||
|
|
||||||
|
Key::Pause => (winapi::winuser::VK_PAUSE, "Pause"),
|
||||||
|
Key::Space => (winapi::winuser::VK_SPACE, "Space"),
|
||||||
|
Key::Tab => (winapi::winuser::VK_TAB, "Tab"),
|
||||||
|
Key::NumLock => (winapi::winuser::VK_NUMLOCK, "NumLock"),
|
||||||
|
Key::CapsLock => (winapi::winuser::VK_CAPITAL, "CapsLock"),
|
||||||
|
Key::ScrollLock => (winapi::winuser::VK_SCROLL, "Scroll"),
|
||||||
|
|
||||||
|
Key::LeftShift => (winapi::winuser::VK_LSHIFT, "LeftShift"),
|
||||||
|
Key::RightShift => (winapi::winuser::VK_RSHIFT, "RightShift"),
|
||||||
|
Key::LeftCtrl => (winapi::winuser::VK_CONTROL, "Ctrl"),
|
||||||
|
Key::RightCtrl => (winapi::winuser::VK_CONTROL, "Ctrl"),
|
||||||
|
|
||||||
|
Key::NumPad0 => (winapi::winuser::VK_NUMPAD0, "NumPad0"),
|
||||||
|
Key::NumPad1 => (winapi::winuser::VK_NUMPAD1, "NumPad1"),
|
||||||
|
Key::NumPad2 => (winapi::winuser::VK_NUMPAD2, "NumPad2"),
|
||||||
|
Key::NumPad3 => (winapi::winuser::VK_NUMPAD3, "NumPad3"),
|
||||||
|
Key::NumPad4 => (winapi::winuser::VK_NUMPAD4, "NumPad4"),
|
||||||
|
Key::NumPad5 => (winapi::winuser::VK_NUMPAD5, "NumPad5"),
|
||||||
|
Key::NumPad6 => (winapi::winuser::VK_NUMPAD6, "NumPad6"),
|
||||||
|
Key::NumPad7 => (winapi::winuser::VK_NUMPAD7, "NumPad7"),
|
||||||
|
Key::NumPad8 => (winapi::winuser::VK_NUMPAD8, "NumPad8"),
|
||||||
|
Key::NumPad9 => (winapi::winuser::VK_NUMPAD9, "NumPad9"),
|
||||||
|
|
||||||
|
Key::LeftAlt => (winapi::winuser::VK_MENU, "Alt"),
|
||||||
|
Key::RightAlt => (winapi::winuser::VK_MENU, "Alt"),
|
||||||
|
|
||||||
|
Key::LeftSuper => (winapi::winuser::VK_LWIN, "LeftWin"),
|
||||||
|
Key::RightSuper => (winapi::winuser::VK_RWIN, "RightWin"),
|
||||||
|
|
||||||
|
_ => (0, "Unsupported"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_name(menu_item: &Menu, key_name: &'static str) -> String {
|
||||||
|
let mut name = menu_item.name.to_owned();
|
||||||
|
|
||||||
|
name.push_str("\t");
|
||||||
|
|
||||||
|
if (menu_item.modifier & MENU_KEY_WIN) == MENU_KEY_WIN {
|
||||||
|
name.push_str("Win-");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu_item.modifier & MENU_KEY_SHIFT) == MENU_KEY_SHIFT {
|
||||||
|
name.push_str("Shift-");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu_item.modifier & MENU_KEY_CTRL) == MENU_KEY_CTRL {
|
||||||
|
name.push_str("Ctrl-");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu_item.modifier & MENU_KEY_ALT) == MENU_KEY_ALT {
|
||||||
|
name.push_str("Alt-");
|
||||||
|
}
|
||||||
|
|
||||||
|
name.push_str(key_name);
|
||||||
|
|
||||||
|
name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_key_virtual_range(key: raw::c_int) -> u32 {
|
||||||
|
if (key >= 0x30 && key <= 0x30) ||
|
||||||
|
(key >= 0x41 && key <= 0x5a) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_virt_key(menu_item: &Menu, key: raw::c_int) -> u32 {
|
||||||
|
let mut virt = Self::is_key_virtual_range(key);
|
||||||
|
|
||||||
|
if (menu_item.modifier & MENU_KEY_ALT) == MENU_KEY_ALT {
|
||||||
|
virt |= 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu_item.modifier & MENU_KEY_CTRL) == MENU_KEY_CTRL {
|
||||||
|
virt |= 0x8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu_item.modifier & MENU_KEY_SHIFT) == MENU_KEY_SHIFT {
|
||||||
|
virt |= 0x4;
|
||||||
|
}
|
||||||
|
|
||||||
|
virt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_accel(accel_table: &mut Vec<ACCEL>, menu_item: &Menu) {
|
||||||
|
let vk_accel = Self::map_key_to_vk_accel(menu_item.key);
|
||||||
|
let virt = Self::get_virt_key(menu_item, vk_accel.0);
|
||||||
|
let accel = winuser::ACCEL {
|
||||||
|
fVirt: virt as BYTE,
|
||||||
|
cmd: menu_item.id as WORD,
|
||||||
|
key: vk_accel.0 as WORD };
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Proper return here
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Window {
|
impl Drop for Window {
|
||||||
|
|
Loading…
Reference in a new issue