API changes + additions

This commit is contained in:
Joonas Javanainen 2015-08-20 18:36:03 +03:00
parent e261db71ba
commit 8bf34e8ced
6 changed files with 473 additions and 50 deletions

View File

@ -1,55 +1,80 @@
use glium::{DisplayBuild, Surface};
use glium::backend::glutin_backend::GlutinFacade;
use glium::glutin;
use glium::glutin::{ElementState, Event, MouseButton, VirtualKeyCode};
use imgui::{ImGui, Frame};
use imgui::glium_renderer::Renderer;
use time::SteadyTime;
pub fn main_with_frame<'a, F: Fn(&Frame<'a>)>(f: F) {
let display = glutin::WindowBuilder::new()
.build_glium()
.unwrap();
pub struct Support {
display: GlutinFacade,
imgui: ImGui,
renderer: Renderer,
last_frame: SteadyTime,
mouse_pos: (i32, i32),
mouse_pressed: (bool, bool, bool)
}
let mut imgui = ImGui::init();
let mut renderer = Renderer::init(&mut imgui, &display).unwrap();
impl Support {
pub fn init() -> Support {
let display = glutin::WindowBuilder::new()
.build_glium()
.unwrap();
let mut last_frame = SteadyTime::now();
let mut mouse_pos = (0, 0);
let mut mouse_pressed = (false, false, false);
let mut imgui = ImGui::init();
let renderer = Renderer::init(&mut imgui, &display).unwrap();
'main: loop {
Support {
display: display,
imgui: imgui,
renderer: renderer,
last_frame: SteadyTime::now(),
mouse_pos: (0, 0),
mouse_pressed: (false, false, false)
}
}
pub fn update_mouse(&mut self) {
self.imgui.set_mouse_pos(self.mouse_pos.0 as f32, self.mouse_pos.1 as f32);
self.imgui.set_mouse_down(&[self.mouse_pressed.0, self.mouse_pressed.1, self.mouse_pressed.2, false, false]);
}
pub fn render<'fr, 'a: 'fr , F: FnMut(&Frame<'fr>) -> bool>(
&'a mut self, clear_color: (f32, f32, f32, f32), mut f: F) -> bool {
let mut result;
let now = SteadyTime::now();
let delta = now - last_frame;
let delta = now - self.last_frame;
let delta_f = delta.num_nanoseconds().unwrap() as f32 / 1_000_000_000.0;
last_frame = now;
self.last_frame = now;
imgui.set_mouse_pos(mouse_pos.0 as f32, mouse_pos.1 as f32);
imgui.set_mouse_down(&[mouse_pressed.0, mouse_pressed.1, mouse_pressed.2, false, false]);
self.update_mouse();
let mut target = display.draw();
target.clear_color(1.0, 1.0, 1.0, 1.0);
let mut target = self.display.draw();
target.clear_color(clear_color.0, clear_color.1,
clear_color.2, clear_color.3);
let (width, height) = target.get_dimensions();
let frame = imgui.frame(width, height, delta_f);
f(&frame);
renderer.render(&mut target, frame).unwrap();
let frame = self.imgui.frame(width, height, delta_f);
result = f(&frame);
self.renderer.render(&mut target, frame).unwrap();
target.finish().unwrap();
for event in display.poll_events() {
for event in self.display.poll_events() {
match event {
Event::Closed |
Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape))
=> break 'main,
Event::MouseMoved(pos) => mouse_pos = pos,
Event::MouseInput(state, MouseButton::Left) =>
mouse_pressed.0 = state == ElementState::Pressed,
Event::MouseInput(state, MouseButton::Right) =>
mouse_pressed.1 = state == ElementState::Pressed,
Event::MouseInput(state, MouseButton::Middle) =>
mouse_pressed.2 = state == ElementState::Pressed,
_ => ()
=> result = false,
Event::MouseMoved(pos) => self.mouse_pos = pos,
Event::MouseInput(state, MouseButton::Left) =>
self.mouse_pressed.0 = state == ElementState::Pressed,
Event::MouseInput(state, MouseButton::Right) =>
self.mouse_pressed.1 = state == ElementState::Pressed,
Event::MouseInput(state, MouseButton::Middle) =>
self.mouse_pressed.2 = state == ElementState::Pressed,
_ => ()
}
}
result
}
}

View File

@ -4,24 +4,166 @@ extern crate glium;
extern crate imgui;
extern crate time;
use imgui::Frame;
use imgui::*;
use self::support::Support;
mod support;
fn main() {
// let mut show_app_metrics = false;
let show_app_main_menu_bar = true;
support::main_with_frame(|frame| {
// if show_app_metrics { show_metrics_window(&mut show_app_metrics) }
if show_app_main_menu_bar { show_example_app_main_menu_bar(frame) }
});
struct State {
clear_color: (f32, f32, f32, f32),
show_app_metrics: bool,
show_app_main_menu_bar: bool,
show_app_console: bool,
show_app_layout: bool,
show_app_long_text: bool,
show_app_auto_resize: bool,
show_app_fixed_overlay: bool,
show_app_custom_rendering: bool,
show_app_manipulating_window_title: bool,
show_app_about: bool,
no_titlebar: bool,
no_border: bool,
no_resize: bool,
no_move: bool,
no_scrollbar: bool,
no_collapse: bool,
no_menu: bool,
bg_alpha: f32,
file_menu: FileMenuState
}
fn show_example_app_main_menu_bar<'a>(frame: &Frame<'a>) {
impl Default for State {
fn default() -> Self {
State {
clear_color: (114.0 / 255.0, 144.0 / 255.0, 154.0 / 255.0, 1.0),
show_app_metrics: false,
show_app_main_menu_bar: false,
show_app_console: false,
show_app_layout: false,
show_app_long_text: false,
show_app_auto_resize: false,
show_app_fixed_overlay: false,
show_app_custom_rendering: false,
show_app_manipulating_window_title: false,
show_app_about: false,
no_titlebar: false,
no_border: false,
no_resize: false,
no_move: false,
no_scrollbar: false,
no_collapse: false,
no_menu: false,
bg_alpha: 0.65,
file_menu: Default::default()
}
}
}
struct FileMenuState {
enabled: bool
}
impl Default for FileMenuState {
fn default() -> Self {
FileMenuState {
enabled: true
}
}
}
fn main() {
let mut state = State {
.. Default::default()
};
let mut support = Support::init();
loop {
let active = support.render(state.clear_color, |frame| {
show_test_window(frame, &mut state)
});
if !active { break }
}
}
fn show_test_window<'a>(frame: &Frame<'a>, state: &mut State) -> bool {
if state.show_app_main_menu_bar { show_example_app_main_menu_bar(frame, state) }
if state.show_app_fixed_overlay {
state.show_app_fixed_overlay = show_example_app_fixed_overlay(frame);
}
if state.show_app_about {
state.show_app_about = frame.window()
.name(im_str!("About ImGui"))
.always_auto_resize(true)
.closable(true)
.build(|| {
frame.text(ImStr::from_str(&format!("ImGui {}", imgui::get_version())));
frame.separator();
frame.text(im_str!("By Omar Cornut and all github contributors."));
frame.text(im_str!("ImGui is licensed under the MIT License, see LICENSE for more information."));
})
}
frame.window().name(im_str!("ImGui Demo"))
.title_bar(!state.no_titlebar)
.show_borders(!state.no_border)
.resizable(!state.no_resize)
.movable(!state.no_move)
.scroll_bar(!state.no_scrollbar)
.collapsible(!state.no_collapse)
.menu_bar(!state.no_menu)
.bg_alpha(state.bg_alpha)
.size((550.0, 680.0), ImGuiSetCond_FirstUseEver)
.closable(true)
.build(|| {
frame.text(im_str!("ImGui says hello."));
frame.menu_bar(|| {
frame.menu(im_str!("Menu")).build(|| {
show_example_menu_file(frame, &mut state.file_menu);
});
frame.menu(im_str!("Examples")).build(|| {
if frame.menu_item(im_str!("Main menu bar")).build() {
state.show_app_main_menu_bar = !state.show_app_main_menu_bar;
}
if frame.menu_item(im_str!("Console")).build() {
state.show_app_console = !state.show_app_console;
}
if frame.menu_item(im_str!("Simple layout")).build() {
state.show_app_layout = !state.show_app_layout;
}
if frame.menu_item(im_str!("Long text display")).build() {
state.show_app_long_text = !state.show_app_long_text;
}
if frame.menu_item(im_str!("Auto-resizing window")).build() {
state.show_app_auto_resize = !state.show_app_auto_resize;
}
if frame.menu_item(im_str!("Simple overlay")).build() {
state.show_app_fixed_overlay = !state.show_app_fixed_overlay;
}
if frame.menu_item(im_str!("Manipulating window title")).build() {
state.show_app_manipulating_window_title =
!state.show_app_manipulating_window_title;
}
if frame.menu_item(im_str!("Custom rendering")).build() {
state.show_app_custom_rendering = !state.show_app_custom_rendering;
}
});
frame.menu(im_str!("Help")).build(|| {
if frame.menu_item(im_str!("Metrics")).build() {
state.show_app_metrics = !state.show_app_metrics;
}
if frame.menu_item(im_str!("About ImGui")).build() {
state.show_app_about = !state.show_app_about;
}
});
});
})
}
fn show_example_app_main_menu_bar<'a>(frame: &Frame<'a>, state: &mut State) {
frame.main_menu_bar(|| {
frame.menu(im_str!("File")).build(|| {
show_example_menu_file(frame);
show_example_menu_file(frame, &mut state.file_menu);
});
frame.menu(im_str!("Edit")).build(|| {
if frame.menu_item(im_str!("Undo")).shortcut(im_str!("CTRL+Z")).build() { }
@ -35,7 +177,7 @@ fn show_example_app_main_menu_bar<'a>(frame: &Frame<'a>) {
});
}
fn show_example_menu_file<'a>(frame: &Frame<'a>) {
fn show_example_menu_file<'a>(frame: &Frame<'a>, state: &mut FileMenuState) {
frame.menu_item(im_str!("(dummy menu)")).enabled(false).build();
if frame.menu_item(im_str!("New")).build() { }
if frame.menu_item(im_str!("Open")).shortcut(im_str!("Ctrl+O")).build() { }
@ -47,7 +189,7 @@ fn show_example_menu_file<'a>(frame: &Frame<'a>) {
frame.menu_item(im_str!("Hello"));
frame.menu_item(im_str!("Sailor"));
frame.menu(im_str!("Recurse..")).build(|| {
show_example_menu_file(frame);
show_example_menu_file(frame, state);
});
});
});
@ -55,6 +197,9 @@ fn show_example_menu_file<'a>(frame: &Frame<'a>) {
if frame.menu_item(im_str!("Save As..")).build() { }
frame.separator();
frame.menu(im_str!("Options")).build(|| {
if frame.menu_item(im_str!("Enabled")).selected(state.enabled).build() {
state.enabled = !state.enabled;
}
// TODO
});
frame.menu(im_str!("Colors")).build(|| {
@ -66,3 +211,20 @@ fn show_example_menu_file<'a>(frame: &Frame<'a>) {
if frame.menu_item(im_str!("Checked")).selected(true).build() { }
if frame.menu_item(im_str!("Quit")).shortcut(im_str!("Alt+F4")).build() { }
}
fn show_example_app_fixed_overlay<'a>(frame: &Frame<'a>) -> bool {
frame.window()
.name(im_str!("Example: Fixed Overlay"))
.closable(true)
.bg_alpha(0.3)
.title_bar(false)
.resizable(false)
.movable(false)
.save_settings(false)
.build(|| {
frame.text(im_str!("Simple overlay\non the top-left side of the screen."));
frame.separator();
let mouse_pos = frame.imgui().mouse_pos();
frame.text(im_str!("Mouse Position: ({:.1},{:.1})", mouse_pos.0, mouse_pos.1));
})
}

View File

@ -161,6 +161,13 @@ bitflags!(
}
);
impl ImGuiWindowFlags {
#[inline]
pub fn with(self, mask: ImGuiWindowFlags, value: bool) -> ImGuiWindowFlags {
if value { self | mask } else { self - mask }
}
}
bitflags!(
#[repr(C)]
flags ImGuiSetCond: c_int {

View File

@ -11,16 +11,31 @@ extern crate libc;
extern crate sdl2;
use libc::{c_char, c_float, c_int, c_uchar};
use std::marker::PhantomData;
use std::borrow::Cow;
use std::ffi::CStr;
use std::mem;
use std::ptr;
use std::slice;
use std::str;
pub use ffi::{ImDrawIdx, ImDrawVert, ImGuiWindowFlags, ImVec2, ImVec4};
pub use ffi::{
ImDrawIdx, ImDrawVert,
ImGuiSetCond,
ImGuiSetCond_Always, ImGuiSetCond_Once,
ImGuiSetCond_FirstUseEver, ImGuiSetCond_Appearing,
ImGuiWindowFlags,
ImGuiWindowFlags_NoTitleBar, ImGuiWindowFlags_NoResize, ImGuiWindowFlags_NoMove,
ImGuiWindowFlags_NoScrollbar, ImGuiWindowFlags_NoScrollWithMouse, ImGuiWindowFlags_NoCollapse,
ImGuiWindowFlags_AlwaysAutoResize, ImGuiWindowFlags_ShowBorders,
ImGuiWindowFlags_NoSavedSettings, ImGuiWindowFlags_NoInputs, ImGuiWindowFlags_MenuBar,
ImVec2, ImVec4
};
pub use menus::{Menu, MenuItem};
pub use window::{Window};
pub mod ffi;
mod menus;
mod window;
#[cfg(feature = "glium")]
pub mod glium_renderer;
@ -29,20 +44,30 @@ pub struct ImGui;
#[macro_export]
macro_rules! im_str {
($e:expr) => ({
($e:tt) => ({
let value = concat!($e, "\0");
unsafe { ::imgui::ImStr::from_bytes(value.as_bytes()) }
});
($e:tt, $($arg:tt)*) => ({
::imgui::ImStr::from_str(&format!($e, $($arg)*))
})
}
pub struct ImStr<'a> {
bytes: &'a [u8]
bytes: Cow<'a, [u8]>
}
impl<'a> ImStr<'a> {
pub unsafe fn from_bytes(bytes: &'a [u8]) -> ImStr<'a> {
ImStr {
bytes: bytes
bytes: Cow::Borrowed(bytes)
}
}
pub fn from_str(value: &str) -> ImStr<'a> {
let mut bytes: Vec<u8> = value.bytes().collect();
bytes.push(0);
ImStr {
bytes: Cow::Owned(bytes)
}
}
fn as_ptr(&self) -> *const c_char { self.bytes.as_ptr() as *const c_char }
@ -54,6 +79,13 @@ pub struct TextureHandle<'a> {
pub pixels: &'a [c_uchar]
}
pub fn get_version() -> &'static str {
unsafe {
let bytes = CStr::from_ptr(ffi::igGetVersion()).to_bytes();
str::from_utf8_unchecked(bytes)
}
}
impl ImGui {
pub fn init() -> ImGui {
let io: &mut ffi::ImGuiIO = unsafe { mem::transmute(ffi::igGetIO()) };
@ -80,6 +112,10 @@ impl ImGui {
let io: &mut ffi::ImGuiIO = unsafe { mem::transmute(ffi::igGetIO()) };
io.mouse_draw_cursor = value;
}
pub fn mouse_pos(&self) -> (f32, f32) {
let io: &mut ffi::ImGuiIO = unsafe { mem::transmute(ffi::igGetIO()) };
(io.mouse_pos.x as f32, io.mouse_pos.y as f32)
}
pub fn set_mouse_pos(&mut self, x: f32, y: f32) {
let io: &mut ffi::ImGuiIO = unsafe { mem::transmute(ffi::igGetIO()) };
io.mouse_pos.x = x;
@ -89,7 +125,7 @@ impl ImGui {
let io: &mut ffi::ImGuiIO = unsafe { mem::transmute(ffi::igGetIO()) };
io.mouse_down = *states;
}
pub fn frame<'a>(&mut self, width: u32, height: u32, delta_time: f32) -> Frame<'a> {
pub fn frame<'fr, 'a: 'fr>(&'a mut self, width: u32, height: u32, delta_time: f32) -> Frame<'fr> {
unsafe {
let io: &mut ffi::ImGuiIO = mem::transmute(ffi::igGetIO());
io.display_size.x = width as c_float;
@ -99,7 +135,7 @@ impl ImGui {
ffi::igNewFrame();
}
Frame {
_phantom: PhantomData
imgui: self
}
}
}
@ -136,7 +172,7 @@ pub struct DrawList<'a> {
}
pub struct Frame<'fr> {
_phantom: PhantomData<&'fr ImGui>
imgui: &'fr ImGui
}
static FMT: &'static [u8] = b"%s\0";
@ -144,6 +180,7 @@ static FMT: &'static [u8] = b"%s\0";
fn fmt_ptr() -> *const c_char { FMT.as_ptr() as *const c_char }
impl<'fr> Frame<'fr> {
pub fn imgui(&self) -> &ImGui { self.imgui }
pub fn render<F, E>(self, mut f: F) -> Result<(), E>
where F: FnMut(DrawList<'fr>) -> Result<(), E> {
unsafe {
@ -173,6 +210,11 @@ impl<'fr> Frame<'fr> {
}
}
// Window
impl<'fr> Frame<'fr> {
pub fn window<'p>(&self) -> Window<'fr, 'p> { Window::new() }
}
// Widgets
impl<'fr> Frame<'fr> {
pub fn text<'b>(&self, text: ImStr<'b>) {

View File

@ -18,6 +18,7 @@ impl<'fr, 'p> Menu<'fr, 'p> {
_phantom: PhantomData
}
}
#[inline]
pub fn enabled(self, enabled: bool) -> Self {
Menu {
enabled: enabled,
@ -51,18 +52,21 @@ impl<'fr, 'p> MenuItem<'fr, 'p> {
_phantom: PhantomData
}
}
#[inline]
pub fn shortcut(self, shortcut: ImStr<'p>) -> Self {
MenuItem {
shortcut: Some(shortcut),
.. self
}
}
#[inline]
pub fn selected(self, selected: bool) -> Self {
MenuItem {
selected: selected,
.. self
}
}
#[inline]
pub fn enabled(self, enabled: bool) -> Self {
MenuItem {
enabled: enabled,

183
src/window.rs Normal file
View File

@ -0,0 +1,183 @@
use libc::c_float;
use std::marker::PhantomData;
use std::ptr;
use super::ffi;
use super::{
Frame,
ImGuiSetCond,
ImGuiWindowFlags,
ImGuiWindowFlags_NoTitleBar, ImGuiWindowFlags_NoResize, ImGuiWindowFlags_NoMove,
ImGuiWindowFlags_NoScrollbar, ImGuiWindowFlags_NoScrollWithMouse, ImGuiWindowFlags_NoCollapse,
ImGuiWindowFlags_AlwaysAutoResize, ImGuiWindowFlags_ShowBorders,
ImGuiWindowFlags_NoSavedSettings, ImGuiWindowFlags_NoInputs, ImGuiWindowFlags_MenuBar,
ImStr, ImVec2
};
pub struct Window<'fr, 'p> {
pos: (f32, f32),
pos_cond: ImGuiSetCond,
size: (f32, f32),
size_cond: ImGuiSetCond,
name: ImStr<'p>,
closable: bool,
bg_alpha: f32,
flags: ImGuiWindowFlags,
_phantom: PhantomData<&'fr Frame<'fr>>
}
impl<'fr, 'p> Window<'fr, 'p> {
pub fn new() -> Window<'fr, 'p> {
Window {
pos: (0.0, 0.0),
pos_cond: ImGuiSetCond::empty(),
size: (0.0, 0.0),
size_cond: ImGuiSetCond::empty(),
name: unsafe { ImStr::from_bytes(b"Debug\0") },
closable: false,
bg_alpha: -1.0,
flags: ImGuiWindowFlags::empty(),
_phantom: PhantomData
}
}
#[inline]
pub fn position(self, pos: (f32, f32), cond: ImGuiSetCond) -> Self {
Window {
pos: pos,
pos_cond: cond,
.. self
}
}
#[inline]
pub fn size(self, size: (f32, f32), cond: ImGuiSetCond) -> Self {
Window {
size: size,
size_cond: cond,
.. self
}
}
#[inline]
pub fn name(self, name: ImStr<'p>) -> Self {
Window {
name: name,
.. self
}
}
#[inline]
pub fn closable(self, closable: bool) -> Self {
Window {
closable: closable,
.. self
}
}
#[inline]
pub fn bg_alpha(self, bg_alpha: f32) -> Self {
Window {
bg_alpha: bg_alpha,
.. self
}
}
pub fn flags(self, flags: ImGuiWindowFlags) -> Self {
Window {
flags: flags,
.. self
}
}
#[inline]
pub fn title_bar(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_NoTitleBar, !value),
.. self
}
}
#[inline]
pub fn resizable(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_NoResize, !value),
.. self
}
}
#[inline]
pub fn movable(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_NoMove, !value),
.. self
}
}
#[inline]
pub fn scroll_bar(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_NoScrollbar, !value),
.. self
}
}
#[inline]
pub fn scrollable(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_NoScrollWithMouse, !value),
.. self
}
}
#[inline]
pub fn collapsible(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_NoCollapse, !value),
.. self
}
}
#[inline]
pub fn always_auto_resize(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_AlwaysAutoResize, value),
.. self
}
}
#[inline]
pub fn show_borders(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_ShowBorders, value),
.. self
}
}
#[inline]
pub fn save_settings(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_NoSavedSettings, !value),
.. self
}
}
#[inline]
pub fn inputs(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_NoInputs, !value),
.. self
}
}
#[inline]
pub fn menu_bar(self, value: bool) -> Self {
Window {
flags: self.flags.with(ImGuiWindowFlags_MenuBar, value),
.. self
}
}
pub fn build<F: FnOnce()>(self, f: F) -> bool {
let mut opened = true;
let render = unsafe {
if !self.pos_cond.is_empty() {
ffi::igSetNextWindowPos(ImVec2::new(self.pos.0, self.pos.1), self.pos_cond);
}
if !self.size_cond.is_empty() {
ffi::igSetNextWindowSize(ImVec2::new(self.size.0, self.size.1), self.size_cond);
}
ffi::igBegin2(self.name.as_ptr(),
if self.closable { &mut opened } else { ptr::null_mut() },
ImVec2::new(0.0, 0.0), self.bg_alpha as c_float, self.flags
)
};
if render {
f();
}
unsafe { ffi::igEnd() };
opened
}
}