Merge menu-api-rework -> master

Rewrote Menu API
This commit is contained in:
Daniel Collin 2016-05-12 20:08:37 +02:00
parent 1dbcf2dee1
commit b5f3cf1ca3
6 changed files with 223 additions and 54 deletions

View file

@ -2,6 +2,11 @@
This project follows semantic versioning. 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) ### v0.6.0 (2016-05-01)
- [added] added ```get_size()``` to retrive the size of the window. - [added] added ```get_size()``` to retrive the size of the window.

View file

@ -14,7 +14,7 @@ Usage
```toml ```toml
# Cargo.toml # Cargo.toml
[dependencies] [dependencies]
minifb = "0.6.0" minifb = "0.7.0"
``` ```
Example Example

View file

@ -44,6 +44,10 @@ fn main() {
let menu_handle = window.add_menu(&menu); let menu_handle = window.add_menu(&menu);
window.get_unix_menus().map(|menus| {
println!("Menus {:?}", menus);
});
let mut color_mul = 1; let mut color_mul = 1;
while window.is_open() && !window.is_key_down(Key::Escape) { while window.is_open() && !window.is_key_down(Key::Escape) {

View file

@ -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 /// 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 /// 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. /// 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); fn add_char(&mut self, uni_char: u32);
} }
extern crate libc;
use std::os::raw;
#[doc(hidden)] #[doc(hidden)]
mod error; mod error;
pub use self::error::Error; pub use self::error::Error;
#[doc(hidden)]
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[doc(hidden)]
pub mod key; pub mod key;
#[doc(hidden)]
pub use key::Key as Key; pub use key::Key as Key;
#[doc(hidden)]
pub mod os; pub mod os;
mod mouse_handler; mod mouse_handler;
mod key_handler; mod key_handler;
@ -95,6 +105,10 @@ use self::os::windows as imp;
target_os="openbsd"))] target_os="openbsd"))]
use self::os::unix as imp; 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); pub struct Window(imp::Window);
/// ///
@ -112,31 +126,6 @@ pub struct WindowOptions {
pub scale: Scale 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<u32> = 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 { impl Window {
/// ///
/// Opens up a new 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 /// 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 /// ```ignore
/// Windows: /// Windows:
@ -459,33 +448,41 @@ impl Window {
/// on which window you have active. /// on which window you have active.
/// Linux/BSD/etc: /// Linux/BSD/etc:
/// Menus aren't supported as they depend on each WindowManager and is outside of the /// 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] #[inline]
pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle { pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle {
self.0.add_menu(&menu.0) self.0.add_menu(&menu.0)
} }
/// ///
/// Updates an existing menu created with [add_menu] /// Remove a menu that has been added 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] #[inline]
pub fn remove_menu(&mut self, handle: MenuHandle) { pub fn remove_menu(&mut self, handle: MenuHandle) {
self.0.remove_menu(handle) 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<UnixMenu>> {
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<UnixMenu>> {
self.0.get_unix_menus()
}
/// ///
/// Check if a menu item has been pressed /// 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; 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<UnixMenuItem>,
#[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<Box<UnixMenu>>,
/// 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); pub struct MenuItemHandle(pub u64);
#[derive(Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
#[doc(hidden)]
pub struct MenuHandle(pub u64); pub struct MenuHandle(pub u64);
///
/// Menu holds info for menus
///
pub struct Menu(imp::Menu);
impl Menu { impl Menu {
/// Create a new menu. Returns error if failed
pub fn new(name: &str) -> Result<Menu> { pub fn new(name: &str) -> Result<Menu> {
imp::Menu::new(name).map(Menu) imp::Menu::new(name).map(Menu)
} }
#[inline] #[inline]
/// Destroys a menu. Currently not implemented
pub fn destroy_menu(&mut self) { pub fn destroy_menu(&mut self) {
//self.0.destroy_menu() //self.0.destroy_menu()
} }
#[inline] #[inline]
/// Adds a sub menu to the current menu
pub fn add_sub_menu(&mut self, name: &str, menu: &Menu) { pub fn add_sub_menu(&mut self, name: &str, menu: &Menu) {
self.0.add_sub_menu(name, &menu.0) self.0.add_sub_menu(name, &menu.0)
} }
/// Adds a menu separator
pub fn add_separator(&mut self) { pub fn add_separator(&mut self) {
self.add_menu_item(&MenuItem { id: MENU_ID_SEPARATOR, ..MenuItem::default() }); self.add_menu_item(&MenuItem { id: MENU_ID_SEPARATOR, ..MenuItem::default() });
} }
#[inline] #[inline]
/// Adds an item to the menu
pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle { pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle {
self.0.add_menu_item(item) self.0.add_menu_item(item)
} }
#[inline] #[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 { pub fn add_item(&mut self, name: &str, id: usize) -> MenuItem {
MenuItem { MenuItem {
id: id, id: id,
@ -552,17 +604,22 @@ impl Menu {
} }
#[inline] #[inline]
/// Removes an item from the menu
pub fn remove_item(&mut self, item: &MenuItemHandle) { pub fn remove_item(&mut self, item: &MenuItemHandle) {
self.0.remove_item(item) self.0.remove_item(item)
} }
} }
///
/// Holds info about each item in a menu
///
pub struct MenuItem<'a> { pub struct MenuItem<'a> {
pub id: usize, pub id: usize,
pub label: String, pub label: String,
pub enabled: bool, pub enabled: bool,
pub key: Key, pub key: Key,
pub modifier: usize, pub modifier: usize,
#[doc(hidden)]
pub menu: Option<&'a mut Menu>, pub menu: Option<&'a mut Menu>,
} }
@ -593,6 +650,7 @@ impl<'a> Clone for MenuItem<'a> {
} }
impl<'a> MenuItem<'a> { impl<'a> MenuItem<'a> {
/// Creates a new menu item
pub fn new(name: &str, id: usize) -> MenuItem { pub fn new(name: &str, id: usize) -> MenuItem {
MenuItem { MenuItem {
id: id, id: id,
@ -601,6 +659,13 @@ impl<'a> MenuItem<'a> {
} }
} }
#[inline] #[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 { pub fn shortcut(self, key: Key, modifier: usize) -> Self {
MenuItem { MenuItem {
key: key, key: key,
@ -609,6 +674,14 @@ impl<'a> MenuItem<'a> {
} }
} }
#[inline] #[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 { pub fn separator(self) -> Self {
MenuItem { MenuItem {
id: MENU_ID_SEPARATOR, id: MENU_ID_SEPARATOR,
@ -616,6 +689,13 @@ impl<'a> MenuItem<'a> {
} }
} }
#[inline] #[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 { pub fn enabled(self, enabled: bool) -> Self {
MenuItem { MenuItem {
enabled: enabled, enabled: enabled,
@ -623,6 +703,13 @@ impl<'a> MenuItem<'a> {
} }
} }
#[inline] #[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 { pub fn build(&mut self) -> MenuItemHandle {
let t = self.clone(); let t = self.clone();
if let Some(ref mut menu) = self.menu { if let Some(ref mut menu) = self.menu {

View file

@ -599,7 +599,7 @@ impl Menu {
item_name.as_ptr(), item_name.as_ptr(),
item.enabled, item.enabled,
conv_key, conv_key,
item.modifier)) item.modifier as u32))
} }
} }

View file

@ -8,10 +8,11 @@ extern crate x11_dl;
use {MouseMode, MouseButton, Scale, Key, KeyRepeat, WindowOptions, InputCallback}; use {MouseMode, MouseButton, Scale, Key, KeyRepeat, WindowOptions, InputCallback};
use key_handler::KeyHandler; use key_handler::KeyHandler;
use menu::Menu; //use menu::Menu;
use self::x11_dl::keysym::*; use self::x11_dl::keysym::*;
use error::Error; use error::Error;
use Result; use Result;
use {MenuItem, MenuItemHandle, MenuHandle, UnixMenu, UnixMenuItem};
use libc::{c_void, c_char, c_uchar}; use libc::{c_void, c_char, c_uchar};
use std::ffi::{CString}; use std::ffi::{CString};
@ -52,6 +53,8 @@ pub struct Window {
window_handle: *mut c_void, window_handle: *mut c_void,
shared_data: SharedData, shared_data: SharedData,
key_handler: KeyHandler, key_handler: KeyHandler,
menu_counter: MenuHandle,
menus: Vec<UnixMenu>,
} }
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
@ -195,6 +198,8 @@ impl Window {
.. SharedData::default() .. SharedData::default()
}, },
key_handler: KeyHandler::new(), key_handler: KeyHandler::new(),
menu_counter: MenuHandle(0),
menus: Vec::new(),
}) })
} }
} }
@ -350,20 +355,88 @@ impl Window {
return factor; return factor;
} }
pub fn add_menu(&mut self, _menu_name: &str, _menu: &Vec<Menu>) -> Result<()> { fn next_menu_handle(&mut self) -> MenuHandle {
Err(Error::MenusNotSupported) let handle = self.menu_counter;
self.menu_counter.0 += 1;
handle
} }
pub fn update_menu(&mut self, _menu_name: &str, _menu: &Vec<Menu>) -> 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<UnixMenu>> {
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<usize> { pub fn is_menu_pressed(&mut self) -> Option<usize> {
None None
} }
} }
pub struct Menu {
pub internal: UnixMenu,
}
impl Menu {
pub fn new(name: &str) -> Result<Menu> {
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 { impl Drop for Window {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {