diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index cbc8872..d2a0d3b 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -5,6 +5,7 @@ ### Added - `ImGui::mouse_down` +- `imgui-glutin-support` crate ## [0.0.20] - 2018-08-13 diff --git a/Cargo.toml b/Cargo.toml index 1bceb20..916efbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,4 @@ travis-ci = { repository = "Gekkio/imgui-rs" } imgui-sys = { version = "0.0.21-pre", path = "imgui-sys" } [workspace] -members = ["imgui-examples", "imgui-sys", "imgui-gfx-renderer", "imgui-glium-renderer"] +members = ["imgui-examples", "imgui-sys", "imgui-gfx-renderer", "imgui-glium-renderer", "imgui-glutin-support"] diff --git a/imgui-glutin-support/Cargo.toml b/imgui-glutin-support/Cargo.toml new file mode 100644 index 0000000..ade8fea --- /dev/null +++ b/imgui-glutin-support/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "imgui-glutin-support" +version = "0.0.21-pre" +authors = ["Joonas Javanainen ", "imgui-rs contributors"] +description = "glutin support code for the imgui crate" +homepage = "https://github.com/Gekkio/imgui-rs" +repository = "https://github.com/Gekkio/imgui-rs" +license = "MIT/Apache-2.0" +categories = ["gui"] + +[badges] +travis-ci = { repository = "Gekkio/imgui-rs" } + +[dependencies] +glutin = "0.18" +imgui = { version = "0.0.21-pre", path = "../" } diff --git a/imgui-glutin-support/src/lib.rs b/imgui-glutin-support/src/lib.rs new file mode 100644 index 0000000..1756755 --- /dev/null +++ b/imgui-glutin-support/src/lib.rs @@ -0,0 +1,224 @@ +//! This crate provides support functions to simplify integrating imgui-rs with glutin. +//! +//! # Using the library +//! +//! In your initialization code call `configure_keys`: +//! +//! ``` +//! # extern crate imgui; +//! # extern crate imgui_glutin_support; +//! use imgui::ImGui; +//! +//! let mut imgui = ImGui::init(); +//! imgui_glutin_support::configure_keys(&mut imgui); +//! ``` +//! +//! In your main loop you should already be retrieving events from glutin and handling them. All +//! you need to do is pass each event to `imgui_glutin_support` as well: +//! +//! ``` +//! # extern crate glutin; +//! # extern crate imgui; +//! # extern crate imgui_glutin_support; +//! # use glutin::EventsLoop; +//! # use imgui::ImGui; +//! # let mut events_loop = EventsLoop::new(); +//! # let mut imgui = ImGui::init(); +//! events_loop.poll_events(|event| { +//! // do application-specific stuff with event +//! +//! imgui_glutin_support::handle_event(&mut imgui, &event); +//! }); +//! ``` +//! +//! # Advanced use cases +//! +//! In more advanced use cases you might want to handle and filter events yourself and call some of +//! the various smaller helper functions exported by the library. +//! +//! For example, you might want to customize mouse wheel line scrolling amount: +//! +//! ``` +//! # extern crate glutin; +//! # extern crate imgui; +//! # extern crate imgui_glutin_support; +//! # use glutin::{EventsLoop, Event, WindowEvent, MouseScrollDelta, TouchPhase}; +//! # use imgui::ImGui; +//! # let mut events_loop = EventsLoop::new(); +//! # let mut imgui = ImGui::init(); +//! events_loop.poll_events(|event| { +//! // do application-specific stuff with event +//! +//! // default handling for events +//! imgui_glutin_support::handle_event(&mut imgui, &event); +//! +//! // Scroll 10 times the pixels per line by handling LineDelta events again and +//! // overriding the mouse wheel value +//! if let Event::WindowEvent { event, .. } = event { +//! match event { +//! WindowEvent::MouseWheel { +//! delta: MouseScrollDelta::LineDelta(_, lines), +//! phase: TouchPhase::Moved, +//! .. +//! } => { +//! imgui.set_mouse_wheel(lines * 10.0); +//! } +//! _ => () +//! } +//! } +//! }); +//! ``` + +extern crate glutin; +extern crate imgui; + +use glutin::{ + ElementState, Event, KeyboardInput, ModifiersState, MouseButton, MouseCursor, MouseScrollDelta, + TouchPhase, VirtualKeyCode, Window, WindowEvent, +}; +use imgui::{FrameSize, ImGui, ImGuiKey, ImGuiMouseCursor}; + +/// Configure imgui key map with glutin `VirtualKeyCode` values +pub fn configure_keys(imgui: &mut ImGui) { + imgui.set_imgui_key(ImGuiKey::Tab, VirtualKeyCode::Tab as _); + imgui.set_imgui_key(ImGuiKey::LeftArrow, VirtualKeyCode::Left as _); + imgui.set_imgui_key(ImGuiKey::RightArrow, VirtualKeyCode::Right as _); + imgui.set_imgui_key(ImGuiKey::UpArrow, VirtualKeyCode::Up as _); + imgui.set_imgui_key(ImGuiKey::DownArrow, VirtualKeyCode::Down as _); + imgui.set_imgui_key(ImGuiKey::PageUp, VirtualKeyCode::PageUp as _); + imgui.set_imgui_key(ImGuiKey::PageDown, VirtualKeyCode::PageDown as _); + imgui.set_imgui_key(ImGuiKey::Home, VirtualKeyCode::Home as _); + imgui.set_imgui_key(ImGuiKey::End, VirtualKeyCode::End as _); + imgui.set_imgui_key(ImGuiKey::Delete, VirtualKeyCode::Delete as _); + imgui.set_imgui_key(ImGuiKey::Backspace, VirtualKeyCode::Back as _); + imgui.set_imgui_key(ImGuiKey::Enter, VirtualKeyCode::Return as _); + imgui.set_imgui_key(ImGuiKey::Escape, VirtualKeyCode::Escape as _); + imgui.set_imgui_key(ImGuiKey::A, VirtualKeyCode::A as _); + imgui.set_imgui_key(ImGuiKey::C, VirtualKeyCode::C as _); + imgui.set_imgui_key(ImGuiKey::V, VirtualKeyCode::V as _); + imgui.set_imgui_key(ImGuiKey::X, VirtualKeyCode::X as _); + imgui.set_imgui_key(ImGuiKey::Y, VirtualKeyCode::Y as _); + imgui.set_imgui_key(ImGuiKey::Z, VirtualKeyCode::Z as _); +} + +/// Update imgui keyboard state +pub fn handle_keyboard_input(imgui: &mut ImGui, event: KeyboardInput) { + handle_modifiers(imgui, event.modifiers); + if let Some(key) = event.virtual_keycode { + let state_bool = event.state == ElementState::Pressed; + imgui.set_key(key as _, state_bool); + match key { + VirtualKeyCode::LShift | VirtualKeyCode::RShift => imgui.set_key_shift(state_bool), + VirtualKeyCode::LControl | VirtualKeyCode::RControl => imgui.set_key_ctrl(state_bool), + VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => imgui.set_key_alt(state_bool), + VirtualKeyCode::LWin | VirtualKeyCode::RWin => imgui.set_key_super(state_bool), + _ => (), + } + } +} + +/// Update imgui keyboard modifier state +pub fn handle_modifiers(imgui: &mut ImGui, modifiers: ModifiersState) { + imgui.set_key_shift(modifiers.shift); + imgui.set_key_ctrl(modifiers.ctrl); + imgui.set_key_alt(modifiers.alt); + imgui.set_key_super(modifiers.logo); +} + +/// Update imgui mouse wheel position +pub fn handle_mouse_scroll_delta(imgui: &mut ImGui, delta: MouseScrollDelta) { + match delta { + MouseScrollDelta::LineDelta(_, y) => imgui.set_mouse_wheel(y), + MouseScrollDelta::PixelDelta(pos) => imgui.set_mouse_wheel(pos.y as f32), + } +} + +/// Update imgui mouse button state +pub fn handle_mouse_button_state(imgui: &mut ImGui, button: MouseButton, state: ElementState) { + let mut states = imgui.mouse_down(); + let state_bool = state == ElementState::Pressed; + match button { + MouseButton::Left => states[0] = state_bool, + MouseButton::Right => states[1] = state_bool, + MouseButton::Middle => states[2] = state_bool, + MouseButton::Other(idx @ 0...4) => states[idx as usize] = state_bool, + _ => (), + } + imgui.set_mouse_down(states); +} + +/// Update imgui state from glutin event +pub fn handle_event(imgui: &mut ImGui, event: &Event) { + match event { + &Event::WindowEvent { ref event, .. } => handle_window_event(imgui, event), + _ => (), + } +} + +/// Update imgui state from glutin window event +pub fn handle_window_event(imgui: &mut ImGui, event: &WindowEvent) { + use WindowEvent::*; + match event { + &KeyboardInput { input, .. } => handle_keyboard_input(imgui, input), + &ReceivedCharacter(ch) => imgui.add_input_character(ch), + &CursorMoved { + position, + modifiers, + .. + } => { + imgui.set_mouse_pos(position.x as f32, position.y as f32); + handle_modifiers(imgui, modifiers); + } + &MouseWheel { + delta, + modifiers, + phase: TouchPhase::Moved, + .. + } => { + handle_mouse_scroll_delta(imgui, delta); + handle_modifiers(imgui, modifiers); + } + &MouseInput { + state, + button, + modifiers, + .. + } => { + handle_mouse_button_state(imgui, button, state); + handle_modifiers(imgui, modifiers); + } + _ => (), + } +} + +/// Update glutin window mouse cursor state +pub fn update_mouse_cursor(imgui: &ImGui, window: &Window) { + let mouse_cursor = imgui.mouse_cursor(); + if imgui.mouse_draw_cursor() || mouse_cursor == ImGuiMouseCursor::None { + // Hide OS cursor + window.hide_cursor(true); + } else { + // Set OS cursor + window.hide_cursor(false); + window.set_cursor(match mouse_cursor { + ImGuiMouseCursor::None => unreachable!("mouse_cursor was None!"), + ImGuiMouseCursor::Arrow => MouseCursor::Arrow, + ImGuiMouseCursor::TextInput => MouseCursor::Text, + ImGuiMouseCursor::Move => MouseCursor::Move, + ImGuiMouseCursor::ResizeNS => MouseCursor::NsResize, + ImGuiMouseCursor::ResizeEW => MouseCursor::EwResize, + ImGuiMouseCursor::ResizeNESW => MouseCursor::NeswResize, + ImGuiMouseCursor::ResizeNWSE => MouseCursor::NwseResize, + }); + } +} + +/// Get the current frame size for imgui frame rendering. +/// +/// Returns `None` if the window no longer exists +pub fn get_frame_size(window: &mut Window) -> Option { + window.get_inner_size().map(|logical_size| FrameSize { + logical_size: logical_size.into(), + hidpi_factor: window.get_hidpi_factor(), + }) +}