diff --git a/CHANGELOG.md b/CHANGELOG.md index 1371407..7c3432c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ This project follows semantic versioning. +### v0.7.0 (2016-05-12) + +- [changed] - Fully rewrote the Menu API. See the documentation/menu example for the changes. +- [added] - Added ```Window::get_unix_menus``` to get data access to menus on Linux/x11 + ### v0.6.0 (2016-05-01) - [added] added ```get_size()``` to retrive the size of the window. diff --git a/README.md b/README.md index 3b71891..45fa92d 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Usage ```toml # Cargo.toml [dependencies] -minifb = "0.6.0" +minifb = "0.7.0" ``` Example diff --git a/examples/menu.rs b/examples/menu.rs index cc14026..aacd065 100644 --- a/examples/menu.rs +++ b/examples/menu.rs @@ -44,6 +44,10 @@ fn main() { let menu_handle = window.add_menu(&menu); + window.get_unix_menus().map(|menus| { + println!("Menus {:?}", menus); + }); + let mut color_mul = 1; while window.is_open() && !window.is_key_down(Key::Escape) { diff --git a/src/lib.rs b/src/lib.rs index e20e062..3cf0799 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,11 @@ +//! minifb is a cross platform library written in [Rust](https://www.rust-lang.org) that makes to +//! open windows (usually native to the running operating system) and can optionally show a 32-bit +//! buffer. minifb also support keyboard, mouse input and menus on selected operating systems. +//! +extern crate libc; + +use std::os::raw; + /// Scale will scale the frame buffer and the window that is being sent in when calling the update /// function. This is useful if you for example want to display a 320 x 256 window on a screen with /// much higher resolution which would result in that the window is very small. @@ -60,17 +68,19 @@ pub trait InputCallback { fn add_char(&mut self, uni_char: u32); } -extern crate libc; -use std::os::raw; #[doc(hidden)] mod error; pub use self::error::Error; +#[doc(hidden)] pub type Result = std::result::Result; +#[doc(hidden)] pub mod key; +#[doc(hidden)] pub use key::Key as Key; +#[doc(hidden)] pub mod os; mod mouse_handler; mod key_handler; @@ -95,6 +105,10 @@ use self::os::windows as imp; target_os="openbsd"))] use self::os::unix as imp; +/// +/// Window is used to open up a window. It's possible to optionally display a 32-bit buffer when +/// the widow is set as non-resizable. +/// pub struct Window(imp::Window); /// @@ -112,31 +126,6 @@ pub struct WindowOptions { pub scale: Scale } -/// -/// Window is used to open up a window. It's possible to optionally display a 32-bit buffer when -/// the widow is set as non-resizable. -/// -/// # Examples -/// -/// Open up a window and display a 32-bit RGB buffer (without error checking) -/// -/// ```ignore -/// const WIDTH: usize = 640; -/// const HEIGHT: usize = 360; -/// -/// let mut buffer: Vec = vec![0; WIDTH * HEIGHT]; -/// -/// let mut window = match Window::new("Test - Press ESC to exit", WIDTH, HEIGHT, -/// WindowOptions::default()).unwrap() -/// -/// while window.is_open() && !window.is_key_down(Key::Escape) { -/// for i in buffer.iter_mut() { -/// *i = 0; // write something interesting here -/// } -/// window.update_with_buffer(&buffer); -/// } -/// ``` -/// impl Window { /// /// Opens up a new window @@ -449,7 +438,7 @@ impl Window { /// /// 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. + /// Operating system here is how it works. /// /// ```ignore /// Windows: @@ -459,33 +448,41 @@ impl Window { /// 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. + /// scope for this library to support. Use [get_unix_menus] to get a structure /// ``` /// - #[inline] pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle { self.0.add_menu(&menu.0) } /// - /// Updates an existing menu created with [add_menu] - /// - /* - #[inline] - pub fn update_menu(&mut self, menu_name: &str, menu: &Vec) -> Result<()> { - self.0.update_menu(menu_name, menu) - } - */ - - /// - /// Remove a menu that has been added with [add_menu] + /// Remove a menu that has been added with [#add_menu] /// #[inline] pub fn remove_menu(&mut self, handle: MenuHandle) { self.0.remove_menu(handle) } + /// + /// Get Unix menu. Will only return menus on Unix class OSes + /// otherwise ```None``` + /// + #[cfg(any(target_os="macos", + target_os="windows"))] + pub fn get_unix_menus(&self) -> Option<&Vec> { + None + } + + #[cfg(any(target_os="linux", + target_os="freebsd", + target_os="dragonfly", + target_os="netbsd", + target_os="openbsd"))] + pub fn get_unix_menus(&self) -> Option<&Vec> { + self.0.get_unix_menus() + } + /// /// Check if a menu item has been pressed /// @@ -509,39 +506,94 @@ pub const MENU_KEY_ALT: usize = 16; const MENU_ID_SEPARATOR:usize = 0xffffffff; -pub struct Menu(imp::Menu); +/// +/// Used on Unix (Linux, FreeBSD, etc) as menus aren't supported in a native where there. +/// This structure can be used by calling [#get_unix_menus] on Window. +/// +#[derive(Debug, Clone)] +pub struct UnixMenu { + /// Name of the menu + pub name: String, + /// All items of the menu. + pub items: Vec, + #[doc(hidden)] + pub handle: MenuHandle, + #[doc(hidden)] + pub item_counter: MenuItemHandle, +} -#[derive(Copy, Clone)] +/// +/// Used for on Unix (Linux, FreeBSD, etc) as menus aren't supported in a native where there. +/// This structure holds info for each item in a #UnixMenu +/// +#[derive(Debug, Clone)] +pub struct UnixMenuItem { + /// Set to a menu if there is a Item is a sub_menu otherwise None + pub sub_menu: Option>, + /// Handle of the MenuItem + pub handle: MenuItemHandle, + /// Id of the item (set by the user from the outside and should be reported back when pressed) + pub id: usize, + /// Name of the item + pub label: String, + /// Set to true if enabled otherwise false + pub enabled: bool, + /// Shortcut key + pub key: Key, + /// Modifier for the key (Shift, Ctrl, etc) + pub modifier: usize, +} + +#[derive(Debug, Copy, Clone)] +#[doc(hidden)] pub struct MenuItemHandle(pub u64); -#[derive(Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] +#[doc(hidden)] pub struct MenuHandle(pub u64); + +/// +/// Menu holds info for menus +/// +pub struct Menu(imp::Menu); + impl Menu { + /// Create a new menu. Returns error if failed pub fn new(name: &str) -> Result { imp::Menu::new(name).map(Menu) } #[inline] + /// Destroys a menu. Currently not implemented pub fn destroy_menu(&mut self) { //self.0.destroy_menu() } #[inline] + /// Adds a sub menu to the current menu pub fn add_sub_menu(&mut self, name: &str, menu: &Menu) { self.0.add_sub_menu(name, &menu.0) } + /// Adds a menu separator pub fn add_separator(&mut self) { self.add_menu_item(&MenuItem { id: MENU_ID_SEPARATOR, ..MenuItem::default() }); } #[inline] + /// Adds an item to the menu pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle { self.0.add_menu_item(item) } #[inline] + /// Adds an item to the menu. Notice that you need to call "build" to finish the add + /// # Examples + /// + /// ```ignore + /// menu.add_item("test", 1).shortcut(Key::A, 0).build() + /// ``` pub fn add_item(&mut self, name: &str, id: usize) -> MenuItem { MenuItem { id: id, @@ -552,17 +604,22 @@ impl Menu { } #[inline] + /// Removes an item from the menu pub fn remove_item(&mut self, item: &MenuItemHandle) { self.0.remove_item(item) } } +/// +/// Holds info about each item in a menu +/// pub struct MenuItem<'a> { pub id: usize, pub label: String, pub enabled: bool, pub key: Key, pub modifier: usize, + #[doc(hidden)] pub menu: Option<&'a mut Menu>, } @@ -593,6 +650,7 @@ impl<'a> Clone for MenuItem<'a> { } impl<'a> MenuItem<'a> { + /// Creates a new menu item pub fn new(name: &str, id: usize) -> MenuItem { MenuItem { id: id, @@ -601,6 +659,13 @@ impl<'a> MenuItem<'a> { } } #[inline] + /// Sets a shortcut key and modifer (and returns itself) + /// + /// # Examples + /// + /// ```ignore + /// menu.add_item("test", 1).shortcut(Key::A, 0).build() + /// ``` pub fn shortcut(self, key: Key, modifier: usize) -> Self { MenuItem { key: key, @@ -609,6 +674,14 @@ impl<'a> MenuItem<'a> { } } #[inline] + /// Sets item to a separator + /// + /// # Examples + /// + /// ```ignore + /// menu.add_item("", 0).separator().build() + /// ``` + /// Notice that it's usually easier to just call ```menu.add_separator()``` directly pub fn separator(self) -> Self { MenuItem { id: MENU_ID_SEPARATOR, @@ -616,6 +689,13 @@ impl<'a> MenuItem<'a> { } } #[inline] + /// Sets the menu item disabled/or not + /// + /// # Examples + /// + /// ```ignore + /// menu.add_item("test", 1).enabled(false).build() + /// ``` pub fn enabled(self, enabled: bool) -> Self { MenuItem { enabled: enabled, @@ -623,6 +703,13 @@ impl<'a> MenuItem<'a> { } } #[inline] + /// Must be called to finialize building of a menu item when started with ```menu.add_item()``` + /// + /// # Examples + /// + /// ```ignore + /// menu.add_item("test", 1).enabled(false).build() + /// ``` pub fn build(&mut self) -> MenuItemHandle { let t = self.clone(); if let Some(ref mut menu) = self.menu { diff --git a/src/os/macos/mod.rs b/src/os/macos/mod.rs index b90daaf..bbebdc2 100644 --- a/src/os/macos/mod.rs +++ b/src/os/macos/mod.rs @@ -406,7 +406,7 @@ impl Window { for i in 0..self.menus.len() { if self.menus[i] == handle { self.menus.remove(i); - unsafe { + unsafe { // + 1 here as we always have a default menu we shouldn't remove mfb_remove_menu_at(self.window_handle, (i + 1) as i32); } @@ -599,7 +599,7 @@ impl Menu { item_name.as_ptr(), item.enabled, conv_key, - item.modifier)) + item.modifier as u32)) } } diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 79ff795..d55ccdb 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -8,10 +8,11 @@ extern crate x11_dl; use {MouseMode, MouseButton, Scale, Key, KeyRepeat, WindowOptions, InputCallback}; use key_handler::KeyHandler; -use menu::Menu; +//use menu::Menu; use self::x11_dl::keysym::*; use error::Error; use Result; +use {MenuItem, MenuItemHandle, MenuHandle, UnixMenu, UnixMenuItem}; use libc::{c_void, c_char, c_uchar}; use std::ffi::{CString}; @@ -52,6 +53,8 @@ pub struct Window { window_handle: *mut c_void, shared_data: SharedData, key_handler: KeyHandler, + menu_counter: MenuHandle, + menus: Vec, } #[allow(non_upper_case_globals)] @@ -195,6 +198,8 @@ impl Window { .. SharedData::default() }, key_handler: KeyHandler::new(), + menu_counter: MenuHandle(0), + menus: Vec::new(), }) } } @@ -350,20 +355,88 @@ impl Window { return factor; } - pub fn add_menu(&mut self, _menu_name: &str, _menu: &Vec) -> Result<()> { - Err(Error::MenusNotSupported) + fn next_menu_handle(&mut self) -> MenuHandle { + let handle = self.menu_counter; + self.menu_counter.0 += 1; + handle } - pub fn update_menu(&mut self, _menu_name: &str, _menu: &Vec) -> Result<()> { - Err(Error::MenusNotSupported) + + pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle { + let handle = self.next_menu_handle(); + let mut menu = menu.internal.clone(); + menu.handle = handle; + self.menus.push(menu); + handle } - pub fn remove_menu(&mut self, _menu_name: &str) -> Result<()> { - Err(Error::MenusNotSupported) + + pub fn get_unix_menus(&self) -> Option<&Vec> { + Some(&self.menus) } + + pub fn remove_menu(&mut self, handle: MenuHandle) { + self.menus.retain(|ref menu| menu.handle != handle); + } + pub fn is_menu_pressed(&mut self) -> Option { None } } +pub struct Menu { + pub internal: UnixMenu, +} + +impl Menu { + pub fn new(name: &str) -> Result { + Ok(Menu { + internal: UnixMenu { + handle: MenuHandle(0), + item_counter: MenuItemHandle(0), + name: name.to_owned(), + items: Vec::new(), + } + }) + } + + pub fn add_sub_menu(&mut self, name: &str, sub_menu: &Menu) { + let handle = self.next_item_handle(); + self.internal.items.push(UnixMenuItem { + label: name.to_owned(), + handle: handle, + sub_menu: Some(Box::new(sub_menu.internal.clone())), + id: 0, + enabled: true, + key: Key::Unknown, + modifier: 0, + }); + } + + fn next_item_handle(&mut self) -> MenuItemHandle { + let handle = self.internal.item_counter; + self.internal.item_counter.0 += 1; + handle + } + + pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle { + let item_handle = self.next_item_handle(); + self.internal.items.push(UnixMenuItem { + sub_menu: None, + handle: self.internal.item_counter, + id: item.id, + label: item.label.clone(), + enabled: item.enabled, + key: item.key, + modifier: item.modifier, + }); + item_handle + } + + pub fn remove_item(&mut self, handle: &MenuItemHandle) { + self.internal.items.retain(|ref item| item.handle.0 != handle.0); + } +} + + impl Drop for Window { fn drop(&mut self) { unsafe {