use conrod_core::{ widget, widget_ids, Borderable, Colorable, Labelable, Positionable, Sizeable, Widget, }; use conrod_floatwin::windowing_area::{ layout::{WinId, WindowingState}, WindowBuilder, WindowingArea, WindowingContext, }; use glium::Surface; mod support; fn main() { const WIDTH: u32 = 800; const HEIGHT: u32 = 600; // Build the window. let mut events_loop = glium::glutin::EventsLoop::new(); let window = glium::glutin::WindowBuilder::new() .with_title("conrod_floatwin demo") .with_dimensions((WIDTH, HEIGHT).into()); let context = glium::glutin::ContextBuilder::new() .with_vsync(true) .with_multisampling(4); let display = glium::Display::new(window, context, &events_loop).unwrap(); let display = support::GliumDisplayWinitWrapper(display); let mut current_hidpi_factor = display.0.gl_window().get_hidpi_factor(); // construct our `Ui`. let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); // Add a `Font` to the `Ui`'s `font::Map` from file. let font_path = "./assets/fonts/NotoSans/NotoSans-Regular.ttf"; ui.fonts.insert_from_file(font_path).unwrap(); // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used // for drawing to the glium `Surface`. let mut renderer = conrod_glium::Renderer::new(&display.0).unwrap(); // The image map describing each of our widget->image mappings (in our case, none). let image_map = conrod_core::image::Map::::new(); // Instantiate the generated list of widget identifiers. let ids = &mut Ids::new(ui.widget_id_generator()); // Instantiate the windowing state. let mut win_state = WindowingState::new(); let win_ids = WinIds { test1: win_state.next_id(), test2: win_state.next_id(), }; let mut ui_state = UiState { enable_debug: false, win_state, win_ids, array_wins: vec![], reusable_win_ids: vec![], next_array_win_idx: 1, hide_test2: false, }; // Poll events from the window. let mut event_loop = support::EventLoop::new(); 'main: loop { // Handle all events. for event in event_loop.next(&mut events_loop) { // Use the `winit` backend feature to convert the winit event to a conrod one. if let Some(event) = support::convert_event(event.clone(), &display) { ui.handle_event(event); event_loop.needs_update(); } match event { glium::glutin::Event::WindowEvent { event, .. } => match event { // Break from the loop upon `Escape`. glium::glutin::WindowEvent::CloseRequested | glium::glutin::WindowEvent::KeyboardInput { input: glium::glutin::KeyboardInput { virtual_keycode: Some(glium::glutin::VirtualKeyCode::Escape), .. }, .. } => break 'main, glium::glutin::WindowEvent::HiDpiFactorChanged(hidpi_factor) => { current_hidpi_factor = hidpi_factor; } // Toggle fullscreen on `F11`. glium::glutin::WindowEvent::KeyboardInput { input: glium::glutin::KeyboardInput { virtual_keycode: Some(glium::glutin::VirtualKeyCode::F11), state: glium::glutin::ElementState::Pressed, .. }, .. } => match display.0.gl_window().window().get_fullscreen() { Some(_) => display.0.gl_window().window().set_fullscreen(None), None => display.0.gl_window().window().set_fullscreen(Some( display.0.gl_window().window().get_current_monitor(), )), }, glium::glutin::WindowEvent::KeyboardInput { input: glium::glutin::KeyboardInput { virtual_keycode: Some(glium::glutin::VirtualKeyCode::F12), state: glium::glutin::ElementState::Pressed, .. }, .. } => ui_state.enable_debug = !ui_state.enable_debug, _ => (), }, _ => (), } } // Instantiate all widgets in the GUI. set_widgets(ui.set_widgets(), ids, current_hidpi_factor, &mut ui_state); // Get the underlying winit window and update the mouse cursor as set by conrod. display .0 .gl_window() .window() .set_cursor(support::convert_mouse_cursor(ui.mouse_cursor())); // Render the `Ui` and then display it on the screen. if let Some(primitives) = ui.draw_if_changed() { renderer.fill(&display.0, primitives, &image_map); let mut target = display.0.draw(); target.clear_color(0.0, 0.0, 0.0, 1.0); renderer.draw(&display.0, &mut target, &image_map).unwrap(); target.finish().unwrap(); } } } widget_ids! { struct Ids { backdrop, windowing_area, text, button, toggle, } } struct WinIds { test1: WinId, test2: WinId, } struct UiState { enable_debug: bool, win_state: WindowingState, win_ids: WinIds, array_wins: Vec, reusable_win_ids: Vec, next_array_win_idx: usize, hide_test2: bool, } struct ArrayWinState { index: usize, win_id: WinId, } fn set_widgets( ref mut ui: conrod_core::UiCell, ids: &mut Ids, hidpi_factor: f64, state: &mut UiState, ) { widget::Rectangle::fill(ui.window_dim()) .color(conrod_core::color::BLUE) .middle() .set(ids.backdrop, ui); let mut win_ctx: WindowingContext = WindowingArea::new(&mut state.win_state, hidpi_factor) .with_debug(state.enable_debug) .set(ids.windowing_area, ui); let builder = WindowBuilder::new() .title("Test1") .is_collapsible(false) .initial_position([100.0, 100.0]) .initial_size([150.0, 100.0]) .min_size([200.0, 50.0]); if let (_, Some(win)) = win_ctx.make_window(builder, state.win_ids.test1, ui) { let c = widget::Canvas::new() .border(0.0) .color(conrod_core::color::LIGHT_YELLOW) .scroll_kids(); let (container_id, _) = win.set(c, ui); widget::Text::new("Hello World!") .color(conrod_core::color::RED) .font_size(32) .parent(container_id) .set(ids.text, ui); let clicked = widget::Toggle::new(state.hide_test2) .label(if state.hide_test2 { "Test2:\nHidden" } else { "Test2:\nShown" }) .label_color(conrod_core::color::LIGHT_BLUE) .w_h(100.0, 50.0) .up(8.0) .parent(container_id) .set(ids.toggle, ui); state.hide_test2 = clicked.last().unwrap_or(state.hide_test2); } let mut add_win = 0; let builder = WindowBuilder::new() .title("Test2") .is_hidden(state.hide_test2) .initial_position([150.0, 150.0]) .initial_size([200.0, 200.0]); if let (_, Some(win)) = win_ctx.make_window(builder, state.win_ids.test2, ui) { let c = widget::Canvas::new() .border(0.0) .color(conrod_core::color::LIGHT_BLUE) .scroll_kids(); let (container_id, _) = win.set(c, ui); let clicks = widget::Button::new() .label("Click me") .w_h(100.0, 50.0) .middle_of(container_id) .parent(container_id) .set(ids.button, ui); for _ in clicks { println!("Clicked me!"); add_win += 1; } } let mut array_win_to_close = vec![]; for (i, array_win_state) in state.array_wins.iter().enumerate() { let title = format!("Test multi - {}", array_win_state.index); let builder = WindowBuilder::new() .title(&title) .is_closable(true) .initial_size([150.0, 100.0]); let (event, win) = win_ctx.make_window(builder, array_win_state.win_id, ui); if let Some(win) = win { let c = widget::Canvas::new() .border(0.0) .color(conrod_core::color::LIGHT_CHARCOAL) .scroll_kids(); let (_container_id, _) = win.set(c, ui); } if event.close_clicked.was_clicked() { array_win_to_close.push(i); } } // Since `WindowingContext` borrows our `WindowingState`, we can't use it // to get `WinId`s unless it has been dropped. Other than calling // `std::mem::drop`, one can also wrap the above code in a scope using // curly braces `{` and `}` so that the `WindowingContext` gets dropped // at the end of the scope. Putting it in a function also works. std::mem::drop(win_ctx); // Create new windows, getting new `WinId`s if necessary. while add_win > 0 { let win_state = &mut state.win_state; // Reuse an existing `WinId` if available or get a new `WinId`. let win_id = state .reusable_win_ids .pop() .unwrap_or_else(|| win_state.next_id()); state.array_wins.push(ArrayWinState { index: state.next_array_win_idx, win_id, }); state.next_array_win_idx += 1; add_win -= 1; } // Remove the windows to be closed. Note that we do this *after* creating // new windows, because the windows are only destroyed after the next // iteration and we don't want new windows to re-use the leftover states of // these windows which are yet to be destroyed. for i in array_win_to_close.into_iter().rev() { let s = state.array_wins.swap_remove(i); // We want to be able to reuse the `WinId`. state.reusable_win_ids.push(s.win_id); } }