mirror of
https://github.com/italicsjenga/rust_minifb.git
synced 2024-12-23 19:31:30 +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.
|
||||
|
||||
### 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)
|
||||
|
||||
This release breaks some of the API by changing names and parameters to some functions.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "minifb"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
license = "MIT/Apache-2.0"
|
||||
authors = ["Daniel Collin <daniel@collin.com>"]
|
||||
description = "Cross-platform window setup with optional bitmap rendering"
|
||||
|
|
|
@ -14,7 +14,7 @@ Usage
|
|||
```toml
|
||||
# Cargo.toml
|
||||
[dependencies]
|
||||
minifb = "0.4.0"
|
||||
minifb = "0.5.0"
|
||||
```
|
||||
|
||||
Example
|
||||
|
@ -31,7 +31,7 @@ const HEIGHT: usize = 360;
|
|||
fn main() {
|
||||
let mut buffer: Vec<u32> = vec![0; WIDTH * HEIGHT];
|
||||
|
||||
let mut window = match minifb::Window::new("Test - ESC to exit", WIDTH, HEIGHT,
|
||||
let mut window = match minifb::Window::new("Test - ESC to exit", WIDTH, HEIGHT,
|
||||
WindowOptions::default()) {
|
||||
Ok(win) => win,
|
||||
Err(err) => {
|
||||
|
@ -52,7 +52,7 @@ fn main() {
|
|||
|
||||
Status
|
||||
------
|
||||
Currently Mac, Linux and Windows (64-bit and 32-bit) are the current supported platforms. X11 (Linux/FreeBSD/etc) support has been tested on Ubuntu (x64). Bug report(s) for other OSes/CPUs are welcome!
|
||||
Currently Mac, Linux and Windows (64-bit and 32-bit) are the current supported platforms. X11 (Linux/FreeBSD/etc) support has been tested on Ubuntu (x64). Bug report(s) for other OSes/CPUs are welcome!
|
||||
|
||||
|
||||
Build instructions
|
||||
|
@ -60,7 +60,7 @@ Build instructions
|
|||
|
||||
```
|
||||
cargo build
|
||||
cargo run --example noise
|
||||
cargo run --example noise
|
||||
```
|
||||
|
||||
This will run the [noise example](https://github.com/emoon/rust_minifb/blob/master/examples/noise.rs) which should look something like this (Mac screenshot)
|
||||
|
|
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);
|
||||
}
|
||||
}
|
|
@ -12,8 +12,8 @@ fn main() {
|
|||
|
||||
let mut buffer: Vec<u32> = vec![0; WIDTH * HEIGHT];
|
||||
|
||||
let mut window = match Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT,
|
||||
WindowOptions {
|
||||
let mut window = match Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT,
|
||||
WindowOptions {
|
||||
resize: true,
|
||||
scale: Scale::X2,
|
||||
..WindowOptions::default()
|
||||
|
|
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,
|
||||
}
|
||||
|
229
src/lib.rs
229
src/lib.rs
|
@ -42,133 +42,6 @@ pub enum MouseButton
|
|||
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
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
|
@ -186,10 +59,24 @@ extern crate libc;
|
|||
use std::os::raw;
|
||||
|
||||
#[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;
|
||||
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;
|
||||
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use self::os::macos as imp;
|
||||
|
@ -206,14 +93,14 @@ pub struct Window(imp::Window);
|
|||
|
||||
///
|
||||
/// WindowOptions is creation settings for the window. By default the settings are defined for
|
||||
/// displayng a 32-bit buffer (no scaling of window is possible)
|
||||
/// displayng a 32-bit buffer (no scaling of window is possible)
|
||||
///
|
||||
pub struct WindowOptions {
|
||||
/// If the window should be borderless (default: false)
|
||||
pub borderless: bool,
|
||||
/// If the window should have a title (default: true)
|
||||
pub title: bool,
|
||||
/// If it should be possible to resize the window (default: false)
|
||||
/// If it should be possible to resize the window (default: false)
|
||||
pub resize: bool,
|
||||
/// Scale of the window that used in conjunction with update_with_buffer (default: X1)
|
||||
pub scale: Scale
|
||||
|
@ -265,10 +152,10 @@ impl Window {
|
|||
/// Open up a window that is resizeable
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut window = match Window::new("Test", 640, 400,
|
||||
/// let mut window = match Window::new("Test", 640, 400,
|
||||
/// WindowOptions {
|
||||
/// resize: true,
|
||||
/// ..WindowOptions::default()
|
||||
/// ..WindowOptions::default()
|
||||
/// }) {
|
||||
/// Ok(win) => win,
|
||||
/// Err(err) => {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -297,7 +184,7 @@ impl Window {
|
|||
}
|
||||
|
||||
///
|
||||
/// Updates the window with a 32-bit pixel buffer. Notice that the buffer needs to be at least
|
||||
/// Updates the window with a 32-bit pixel buffer. Notice that the buffer needs to be at least
|
||||
/// the size of the created window
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -315,7 +202,7 @@ impl Window {
|
|||
}
|
||||
|
||||
///
|
||||
/// Updates the window (this is required to call in order to get keyboard/mouse input, etc)
|
||||
/// Updates the window (this is required to call in order to get keyboard/mouse input, etc)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -334,7 +221,7 @@ impl Window {
|
|||
///
|
||||
/// Checks if the window is still open. A window can be closed by the user (by for example
|
||||
/// pressing the close button on the window) It's up to the user to make sure that this is
|
||||
/// being checked and take action depending on the state.
|
||||
/// being checked and take action depending on the state.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -375,31 +262,31 @@ impl Window {
|
|||
/// println!("x {} y {}", mouse.0, mouse.1);
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
#[inline]
|
||||
pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> {
|
||||
self.0.get_mouse_pos(mode)
|
||||
}
|
||||
|
||||
///
|
||||
/// Check if a mouse button is down or not
|
||||
/// Check if a mouse button is down or not
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let left_down = window.get_mouse_down(MouseButton::Left);
|
||||
/// println!("is left down? {}", left_down)
|
||||
/// println!("is left down? {}", left_down)
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
#[inline]
|
||||
pub fn get_mouse_down(&self, button: MouseButton) -> bool {
|
||||
pub fn get_mouse_down(&self, button: MouseButton) -> bool {
|
||||
self.0.get_mouse_down(button)
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the current movement of the scroll wheel.
|
||||
/// Scroll wheel can mean different thing depending on the device attach.
|
||||
/// For example on Mac with trackpad "scroll wheel" means two finger
|
||||
/// For example on Mac with trackpad "scroll wheel" means two finger
|
||||
/// swiping up/down (y axis) and to the sides (x-axis)
|
||||
/// When using a mouse this assumes the scroll wheel which often is only y direction.
|
||||
///
|
||||
|
@ -418,7 +305,7 @@ impl Window {
|
|||
}
|
||||
|
||||
///
|
||||
/// Get the current keys that are down.
|
||||
/// Get the current keys that are down.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -460,7 +347,7 @@ impl Window {
|
|||
self.0.get_keys_pressed(repeat)
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
/// Check if a single key is down.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -476,7 +363,7 @@ impl Window {
|
|||
self.0.is_key_down(key)
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
/// Check if a single key is pressed. KeyRepeat will control if the key should be repeated or
|
||||
/// not while being pressed.
|
||||
///
|
||||
|
@ -515,13 +402,65 @@ impl Window {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// window.set_key_repeat_rate(0.01) // 0.01 sec between keys
|
||||
/// window.set_key_repeat_rate(0.01) // 0.01 sec between keys
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
pub fn set_key_repeat_rate(&mut self, rate: f32) {
|
||||
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
|
||||
|
|
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7,9 +7,11 @@
|
|||
static bool s_init = false;
|
||||
|
||||
// window_handler.rs
|
||||
const uint32_t WINDOW_BORDERLESS = 1 << 1;
|
||||
const uint32_t WINDOW_RESIZE = 1 << 2;
|
||||
const uint32_t WINDOW_TITLE = 1 << 3;
|
||||
const uint32_t WINDOW_BORDERLESS = 1 << 1;
|
||||
const uint32_t WINDOW_RESIZE = 1 << 2;
|
||||
const uint32_t WINDOW_TITLE = 1 << 3;
|
||||
|
||||
static void create_standard_menu();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -21,27 +23,30 @@ const uint32_t WINDOW_TITLE = 1 << 3;
|
|||
|
||||
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) {
|
||||
[NSApplication sharedApplication];
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
create_standard_menu();
|
||||
s_init = true;
|
||||
}
|
||||
|
||||
uint32_t styles = NSClosableWindowMask | NSMiniaturizableWindowMask;
|
||||
|
||||
if (flags & WINDOW_BORDERLESS)
|
||||
styles |= NSBorderlessWindowMask;
|
||||
styles |= NSBorderlessWindowMask;
|
||||
|
||||
if (flags & WINDOW_RESIZE)
|
||||
styles |= NSResizableWindowMask;
|
||||
styles |= NSResizableWindowMask;
|
||||
|
||||
if (flags & WINDOW_TITLE)
|
||||
styles |= NSTitledWindowMask;
|
||||
|
||||
styles |= NSTitledWindowMask;
|
||||
|
||||
NSRect rectangle = NSMakeRect(0, 0, width * scale, (height * scale));
|
||||
|
||||
|
||||
OSXWindow* window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO];
|
||||
|
||||
if (!window)
|
||||
|
@ -57,6 +62,10 @@ void* mfb_open(const char* name, int width, int height, uint32_t flags, int scal
|
|||
window->scale = scale;
|
||||
window->key_callback = 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];
|
||||
|
||||
|
@ -69,11 +78,124 @@ void* mfb_open(const char* name, int width, int height, uint32_t flags, int scal
|
|||
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
[pool drain];
|
||||
if (!prev_init)
|
||||
[NSApp finishLaunching];
|
||||
|
||||
//[pool drain];
|
||||
|
||||
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)
|
||||
|
@ -83,7 +205,7 @@ void mfb_close(void* win)
|
|||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
if (window)
|
||||
[window close];
|
||||
[window close];
|
||||
|
||||
[pool drain];
|
||||
}
|
||||
|
@ -92,13 +214,28 @@ void mfb_close(void* win)
|
|||
|
||||
static int update_events()
|
||||
{
|
||||
int state = 0;
|
||||
NSEvent* event;
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||
[NSApp sendEvent:event];
|
||||
|
||||
do
|
||||
{
|
||||
event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||
|
||||
if (event) {
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
}
|
||||
while (event);
|
||||
|
||||
[pool release];
|
||||
|
||||
return state;
|
||||
/*
|
||||
int state = 0;
|
||||
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||
[NSApp sendEvent:event];
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -142,14 +279,14 @@ int mfb_update_with_buffer(void* window, void* buffer)
|
|||
|
||||
static float transformY(float y)
|
||||
{
|
||||
float b = CGDisplayBounds(CGMainDisplayID()).size.height;
|
||||
float b = CGDisplayBounds(CGMainDisplayID()).size.height;
|
||||
float t = b - y;
|
||||
return t;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mfb_set_position(void* window, int x, int y)
|
||||
void mfb_set_position(void* window, int x, int y)
|
||||
{
|
||||
OSXWindow* win = (OSXWindow*)window;
|
||||
const NSRect contentRect = [[win contentView] frame];
|
||||
|
@ -160,7 +297,7 @@ void mfb_set_position(void* window, int x, int y)
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int mfb_should_close(void* window)
|
||||
int mfb_should_close(void* window)
|
||||
{
|
||||
OSXWindow* win = (OSXWindow*)window;
|
||||
return win->should_close;
|
||||
|
@ -168,7 +305,7 @@ int mfb_should_close(void* window)
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint32_t mfb_get_screen_size()
|
||||
uint32_t mfb_get_screen_size()
|
||||
{
|
||||
NSRect e = [[NSScreen mainScreen] frame];
|
||||
uint32_t w = (uint32_t)e.size.width;
|
||||
|
@ -193,4 +330,104 @@ void mfb_set_mouse_data(void* window, SharedData* 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>
|
||||
#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
|
||||
{
|
||||
NSView* childContentView;
|
||||
|
@ -12,6 +48,11 @@
|
|||
@public void* rust_data;
|
||||
@public SharedData* shared_data;
|
||||
@public bool should_close;
|
||||
@public bool is_active;
|
||||
@public int active_menu_id;
|
||||
@public MenuData* menu_data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import "OSXWindow.h"
|
||||
#import "OSXWindowFrameView.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
@implementation OSXWindow
|
||||
|
||||
|
@ -20,7 +21,7 @@
|
|||
NSSize childBoundsSize = [childContentView bounds].size;
|
||||
sizeDelta.width -= childBoundsSize.width;
|
||||
sizeDelta.height -= childBoundsSize.height;
|
||||
|
||||
|
||||
OSXWindowFrameView *frameView = [super contentView];
|
||||
NSSize newFrameSize = [frameView bounds].size;
|
||||
newFrameSize.width += sizeDelta.width;
|
||||
|
@ -37,7 +38,7 @@
|
|||
|
||||
// Left Shift
|
||||
key_callback(rust_data, 0x38, flags == 0x20102 ? 1 : 0);
|
||||
|
||||
|
||||
// RightShift
|
||||
key_callback(rust_data, 0x3c, flags == 0x20104 ? 1 : 0);
|
||||
|
||||
|
@ -58,21 +59,19 @@
|
|||
|
||||
// Right Super
|
||||
key_callback(rust_data, 0x36, flags == 0x100110 ? 1 : 0);
|
||||
|
||||
[super flagsChanged: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) {
|
||||
key_callback(rust_data, [event keyCode], 1);
|
||||
}
|
||||
|
||||
[super keyDown:event];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -82,17 +81,26 @@
|
|||
if (key_callback) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)windowWillClose:(NSNotification *)notification
|
||||
- (void)windowWillClose:(NSNotification *)notification
|
||||
{
|
||||
should_close = true;
|
||||
}
|
||||
|
@ -111,21 +119,33 @@
|
|||
{
|
||||
if ([childContentView isEqualTo:aView])
|
||||
return;
|
||||
|
||||
|
||||
NSRect bounds = [self frame];
|
||||
bounds.origin = NSZeroPoint;
|
||||
|
||||
OSXWindowFrameView* frameView = [super contentView];
|
||||
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->width = width;
|
||||
frameView->height = height;
|
||||
frameView->draw_buffer = draw_buffer;
|
||||
frameView->width = width;
|
||||
frameView->height = height;
|
||||
frameView->draw_buffer = draw_buffer;
|
||||
frameView->scale = scale;
|
||||
[super setContentView:frameView];
|
||||
}
|
||||
|
||||
|
||||
if (childContentView)
|
||||
[childContentView removeFromSuperview];
|
||||
|
||||
|
@ -168,16 +188,149 @@
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)updateSize
|
||||
- (void)updateSize
|
||||
{
|
||||
OSXWindowFrameView* frameView = [super contentView];
|
||||
if (frameView)
|
||||
{
|
||||
frameView->width = width;
|
||||
frameView->height = height;
|
||||
frameView->draw_buffer = draw_buffer;
|
||||
frameView->width = width;
|
||||
frameView->height = height;
|
||||
frameView->draw_buffer = draw_buffer;
|
||||
frameView->scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (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
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
use {MouseButton, MouseMode, Scale, Key, KeyRepeat, WindowOptions};
|
||||
use key_handler::KeyHandler;
|
||||
use error::Error;
|
||||
use Result;
|
||||
use mouse_handler;
|
||||
use window_flags;
|
||||
use menu::Menu;
|
||||
|
||||
use libc::{c_void, c_char, c_uchar};
|
||||
use std::ffi::{CString};
|
||||
|
@ -13,7 +16,7 @@ use std::os::raw;
|
|||
|
||||
// Table taken from GLFW and slightly modified
|
||||
|
||||
static KEY_MAPPINGS: [Key; 128] = [
|
||||
static KEY_MAPPINGS: [Key; 128] = [
|
||||
/* 00 */ Key::A,
|
||||
/* 01 */ Key::S,
|
||||
/* 02 */ Key::D,
|
||||
|
@ -80,7 +83,7 @@ static KEY_MAPPINGS: [Key; 128] = [
|
|||
/* 3f */ Key::Unknown, // Function
|
||||
/* 40 */ Key::Unknown, // F17
|
||||
/* 41 */ Key::Unknown, // Decimal
|
||||
/* 42 */ Key::Unknown,
|
||||
/* 42 */ Key::Unknown,
|
||||
/* 43 */ Key::Unknown, // Multiply
|
||||
/* 44 */ Key::Unknown,
|
||||
/* 45 */ Key::Unknown, // Add
|
||||
|
@ -89,7 +92,7 @@ static KEY_MAPPINGS: [Key; 128] = [
|
|||
/* 48 */ Key::Unknown, // VolumeUp
|
||||
/* 49 */ Key::Unknown, // VolumeDown
|
||||
/* 4a */ Key::Unknown, // Mute
|
||||
/* 4b */ Key::Unknown,
|
||||
/* 4b */ Key::Unknown,
|
||||
/* 4c */ Key::Enter,
|
||||
/* 4d */ Key::Unknown,
|
||||
/* 4e */ Key::Unknown, // Subtrackt
|
||||
|
@ -144,7 +147,23 @@ 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;
|
||||
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_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_active_menu(window: *mut c_void) -> i32;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -171,7 +195,7 @@ pub struct SharedData {
|
|||
|
||||
pub struct Window {
|
||||
window_handle: *mut c_void,
|
||||
scale_factor: usize,
|
||||
scale_factor: usize,
|
||||
pub shared_data: SharedData,
|
||||
key_handler: KeyHandler,
|
||||
pub has_set_data: bool,
|
||||
|
@ -190,11 +214,11 @@ unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, state: i32) {
|
|||
}
|
||||
|
||||
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) {
|
||||
Err(_) => {
|
||||
Err(_) => {
|
||||
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,
|
||||
};
|
||||
|
@ -204,13 +228,13 @@ impl Window {
|
|||
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("Unable to open Window");
|
||||
return Err(Error::WindowCreate("Unable to open Window".to_owned()));
|
||||
}
|
||||
|
||||
Ok(Window {
|
||||
Ok(Window {
|
||||
window_handle: handle,
|
||||
scale_factor: scale_factor,
|
||||
shared_data: SharedData {
|
||||
shared_data: SharedData {
|
||||
width: width as u32 * scale_factor as u32,
|
||||
height: height as u32 * scale_factor as u32,
|
||||
.. SharedData::default()
|
||||
|
@ -223,7 +247,7 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn get_window_handle(&self) -> *mut raw::c_void {
|
||||
self.window_handle as *mut raw::c_void
|
||||
self.window_handle as *mut raw::c_void
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -313,11 +337,62 @@ impl Window {
|
|||
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]
|
||||
pub fn is_open(&self) -> bool {
|
||||
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 {
|
||||
let factor: i32 = match scale {
|
||||
Scale::X1 => 1,
|
||||
|
@ -328,8 +403,8 @@ impl Window {
|
|||
Scale::X32 => 32,
|
||||
Scale::FitScreen => {
|
||||
let wh: u32 = mfb_get_screen_size();
|
||||
let screen_x = (wh >> 16) as i32;
|
||||
let screen_y = (wh & 0xffff) as i32;
|
||||
let screen_x = (wh >> 16) as i32;
|
||||
let screen_y = (wh & 0xffff) as i32;
|
||||
|
||||
let mut scale = 1i32;
|
||||
|
||||
|
@ -350,6 +425,168 @@ impl Window {
|
|||
|
||||
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 {
|
||||
|
|
|
@ -8,7 +8,10 @@ extern crate x11_dl;
|
|||
|
||||
use {MouseMode, MouseButton, Scale, Key, KeyRepeat, WindowOptions};
|
||||
use key_handler::KeyHandler;
|
||||
use menu::Menu;
|
||||
use self::x11_dl::keysym::*;
|
||||
use error::Error;
|
||||
use Result;
|
||||
|
||||
use libc::{c_void, c_char, c_uchar};
|
||||
use std::ffi::{CString};
|
||||
|
@ -163,31 +166,31 @@ unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, s: i32) {
|
|||
}
|
||||
|
||||
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) {
|
||||
Err(_) => {
|
||||
Err(_) => {
|
||||
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,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let scale = Self::get_scale_factor(width, height, opts.scale);
|
||||
let handle = mfb_open(n.as_ptr(),
|
||||
width as u32,
|
||||
height as u32,
|
||||
window_flags::get_flags(opts),
|
||||
let handle = mfb_open(n.as_ptr(),
|
||||
width as u32,
|
||||
height as u32,
|
||||
window_flags::get_flags(opts),
|
||||
scale);
|
||||
|
||||
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 {
|
||||
window_handle: handle,
|
||||
shared_data: SharedData {
|
||||
scale: scale as f32,
|
||||
shared_data: SharedData {
|
||||
scale: scale as f32,
|
||||
.. SharedData::default()
|
||||
},
|
||||
key_handler: KeyHandler::new(),
|
||||
|
@ -238,7 +241,7 @@ impl Window {
|
|||
}
|
||||
|
||||
pub fn get_mouse_down(&self, button: MouseButton) -> bool {
|
||||
match button {
|
||||
match button {
|
||||
MouseButton::Left => self.shared_data.state[0] > 0,
|
||||
MouseButton::Middle => self.shared_data.state[1] > 0,
|
||||
MouseButton::Right => self.shared_data.state[2] > 0,
|
||||
|
@ -246,7 +249,7 @@ impl Window {
|
|||
}
|
||||
|
||||
pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> {
|
||||
if self.shared_data.scroll_x.abs() > 0.0 ||
|
||||
if self.shared_data.scroll_x.abs() > 0.0 ||
|
||||
self.shared_data.scroll_y.abs() > 0.0 {
|
||||
Some((self.shared_data.scroll_x, self.shared_data.scroll_y))
|
||||
} else {
|
||||
|
@ -289,6 +292,12 @@ impl Window {
|
|||
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 {
|
||||
let factor: i32 = match scale {
|
||||
Scale::X1 => 1,
|
||||
|
@ -296,8 +305,8 @@ impl Window {
|
|||
Scale::X4 => 4,
|
||||
Scale::FitScreen => {
|
||||
let wh: u32 = mfb_get_screen_size();
|
||||
let screen_x = (wh >> 16) as i32;
|
||||
let screen_y = (wh & 0xffff) as i32;
|
||||
let screen_x = (wh >> 16) as i32;
|
||||
let screen_y = (wh & 0xffff) as i32;
|
||||
|
||||
println!("{} - {}", screen_x, screen_y);
|
||||
|
||||
|
@ -329,6 +338,19 @@ impl Window {
|
|||
|
||||
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 {
|
||||
|
|
|
@ -6,9 +6,15 @@ extern crate winapi;
|
|||
extern crate gdi32;
|
||||
extern crate time;
|
||||
|
||||
const INVALID_ACCEL: usize = 0xffffffff;
|
||||
|
||||
use {Scale, Key, KeyRepeat, MouseButton, MouseMode, WindowOptions};
|
||||
|
||||
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::os::windows::ffi::OsStrExt;
|
||||
|
@ -17,11 +23,16 @@ use std::mem;
|
|||
use std::os::raw;
|
||||
use mouse_handler;
|
||||
|
||||
use self::winapi::windef::HWND;
|
||||
use self::winapi::windef::HDC;
|
||||
use self::winapi::winuser::WNDCLASSW;
|
||||
use self::winapi::wingdi::BITMAPINFOHEADER;
|
||||
use self::winapi::wingdi::RGBQUAD;
|
||||
//use self::winapi::windef::HWND;
|
||||
//use self::winapi::windef::HDC;
|
||||
//use self::winapi::windef::HMENU;
|
||||
//use self::winapi::wingdi::BITMAPINFOHEADER;
|
||||
//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
|
||||
#[repr(C)]
|
||||
|
@ -226,6 +237,12 @@ unsafe extern "system" fn wnd_proc(window: winapi::HWND,
|
|||
return 0;
|
||||
}
|
||||
|
||||
winapi::winuser::WM_COMMAND => {
|
||||
if lparam == 0 {
|
||||
wnd.accel_key = (wparam & 0xffff) as usize;
|
||||
}
|
||||
}
|
||||
|
||||
winapi::winuser::WM_PAINT => {
|
||||
|
||||
// if we have nothing to draw here we return the default function
|
||||
|
@ -288,6 +305,12 @@ struct MouseData {
|
|||
pub scroll: f32,
|
||||
}
|
||||
|
||||
struct MenuStore {
|
||||
name: String,
|
||||
menu: HMENU,
|
||||
accel_items: Vec<ACCEL>,
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
mouse: MouseData,
|
||||
dc: Option<HDC>,
|
||||
|
@ -297,7 +320,18 @@ pub struct Window {
|
|||
scale_factor: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
menus: Vec<MenuStore>,
|
||||
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 {
|
||||
|
@ -347,7 +381,7 @@ impl Window {
|
|||
let mut flags = 0;
|
||||
|
||||
if opts.title {
|
||||
flags |= winapi::WS_OVERLAPPEDWINDOW as u32;
|
||||
flags |= winapi::WS_OVERLAPPEDWINDOW as u32;
|
||||
}
|
||||
|
||||
if opts.resize {
|
||||
|
@ -363,7 +397,7 @@ impl Window {
|
|||
}
|
||||
|
||||
let handle = user32::CreateWindowExW(0,
|
||||
class_name.as_ptr(),
|
||||
class_name.as_ptr(),
|
||||
window_name.as_ptr(),
|
||||
flags,
|
||||
winapi::CW_USEDEFAULT,
|
||||
|
@ -389,14 +423,14 @@ impl Window {
|
|||
width: usize,
|
||||
height: usize,
|
||||
opts: WindowOptions)
|
||||
-> Result<Window, &str> {
|
||||
-> Result<Window> {
|
||||
unsafe {
|
||||
let scale_factor = Self::get_scale_factor(width, height, opts.scale);
|
||||
|
||||
let handle = Self::open_window(name, width, height, opts, scale_factor);
|
||||
|
||||
if handle.is_none() {
|
||||
return Err("Unable to create Window");
|
||||
return Err(Error::WindowCreate("Unable to create Window".to_owned()));
|
||||
}
|
||||
|
||||
let window = Window {
|
||||
|
@ -409,6 +443,9 @@ impl Window {
|
|||
scale_factor: scale_factor,
|
||||
width: width as i32,
|
||||
height: height as i32,
|
||||
menus: Vec::new(),
|
||||
accel_table: ptr::null_mut(),
|
||||
accel_key: INVALID_ACCEL,
|
||||
};
|
||||
|
||||
Ok(window)
|
||||
|
@ -423,7 +460,7 @@ impl Window {
|
|||
#[inline]
|
||||
pub fn set_position(&mut self, x: isize, y: isize) {
|
||||
unsafe {
|
||||
user32::SetWindowPos(self.window.unwrap(), ptr::null_mut(), x as i32, y as i32,
|
||||
user32::SetWindowPos(self.window.unwrap(), ptr::null_mut(), x as i32, y as i32,
|
||||
0, 0, winapi::SWP_SHOWWINDOW | winapi::SWP_NOSIZE);
|
||||
}
|
||||
}
|
||||
|
@ -491,7 +528,6 @@ impl Window {
|
|||
|
||||
fn generic_update(&mut self, window: HWND) {
|
||||
unsafe {
|
||||
|
||||
let mut point: winapi::POINT = mem::uninitialized();
|
||||
user32::GetCursorPos(&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 {
|
||||
let mut msg = mem::uninitialized();
|
||||
|
||||
while user32::PeekMessageW(&mut msg, window, 0, 0, winapi::winuser::PM_REMOVE) != 0 {
|
||||
user32::TranslateMessage(&mut msg);
|
||||
user32::DispatchMessageW(&mut msg);
|
||||
// Make this code a bit nicer
|
||||
if self.accel_table == ptr::null_mut() {
|
||||
user32::TranslateMessage(&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);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_active(&mut self) -> bool {
|
||||
// TODO: Proper implementation
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
|
||||
let factor: i32 = match scale {
|
||||
Scale::X1 => 1,
|
||||
|
@ -568,6 +618,349 @@ impl Window {
|
|||
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue