//! An example use of the prompt widgets. #[macro_use] extern crate conrod_core; extern crate conrod_glium; extern crate conrod_prompt; extern crate conrod_winit; extern crate find_folder; extern crate glium; extern crate image; use glium::Surface; /// Support module, copied from conrod's examples mod support { use glium::{ glutin::{event, event_loop}, Display, }; pub enum Request<'a, 'b: 'a> { Event { event: &'a event::Event<'b, ()>, should_update_ui: &'a mut bool, should_exit: &'a mut bool, }, SetUi { needs_redraw: &'a mut bool, }, Redraw, } /// In most of the examples the `glutin` crate is used for providing the window context and /// events while the `glium` crate is used for displaying `conrod_core::render::Primitives` to the /// screen. /// /// This function simplifies some of the boilerplate involved in limiting the redraw rate in the /// glutin+glium event loop. pub fn run_loop( display: Display, event_loop: event_loop::EventLoop<()>, mut callback: F, ) -> ! where F: 'static + FnMut(Request, &Display), { let sixteen_ms = std::time::Duration::from_millis(16); let mut next_update = None; let mut ui_update_needed = false; event_loop.run(move |event, _, control_flow| { { let mut should_update_ui = false; let mut should_exit = false; callback( Request::Event { event: &event, should_update_ui: &mut should_update_ui, should_exit: &mut should_exit, }, &display, ); ui_update_needed |= should_update_ui; if should_exit { *control_flow = event_loop::ControlFlow::Exit; return; } } // We don't want to draw any faster than 60 FPS, so set the UI only on every 16ms, unless: // - this is the very first event, or // - we didn't request update on the last event and new events have arrived since then. let should_set_ui_on_main_events_cleared = next_update.is_none() && ui_update_needed; match (&event, should_set_ui_on_main_events_cleared) { (event::Event::NewEvents(event::StartCause::Init { .. }), _) | (event::Event::NewEvents(event::StartCause::ResumeTimeReached { .. }), _) | (event::Event::MainEventsCleared, true) => { next_update = Some(std::time::Instant::now() + sixteen_ms); ui_update_needed = false; let mut needs_redraw = false; callback( Request::SetUi { needs_redraw: &mut needs_redraw, }, &display, ); if needs_redraw { display.gl_window().window().request_redraw(); } else { // We don't need to redraw anymore until more events arrives. next_update = None; } } _ => {} } if let Some(next_update) = next_update { *control_flow = event_loop::ControlFlow::WaitUntil(next_update); } else { *control_flow = event_loop::ControlFlow::Wait; } // Request redraw if needed. match &event { event::Event::RedrawRequested(_) => { callback(Request::Redraw, &display); } _ => {} } }) } // Conversion functions for converting between types from glium's version of `winit` and // `conrod_core`. conrod_winit::v023_conversion_fns!(); } // A struct to keep track of whether any of the prompts is still active. struct PromptState { ack: bool, yesno: bool, text: bool, text_response: String, } impl Default for PromptState { fn default() -> Self { PromptState { ack: false, yesno: false, text: false, text_response: "".into(), } } } // Most of this code is taken directly from the Canvas example in Conrod. fn main() { const WIDTH: u32 = 800; const HEIGHT: u32 = 600; // Build the window. let event_loop = glium::glutin::event_loop::EventLoop::new(); let window = glium::glutin::window::WindowBuilder::new() .with_title("Canvas") .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); let context = glium::glutin::ContextBuilder::new() .with_vsync(true) .with_multisampling(4); let display = glium::Display::new(window, context, &event_loop).unwrap(); // 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 assets = find_folder::Search::KidsThenParents(3, 5) .for_folder("assets") .unwrap(); let font_path = assets.join("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).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 = Ids::new(ui.widget_id_generator()); let mut state = PromptState::default(); // Start the loop: // // - Send available events to the `Ui`. // - Update the widgets via the `conrod_example_shared::gui` fn. // - Render the current state of the `Ui`. // - Repeat. support::run_loop(display, event_loop, move |request, display| { match request { support::Request::Event { event, should_update_ui, should_exit, } => { // Use the `winit` backend feature to convert the winit event to a conrod one. if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { ui.handle_event(event); *should_update_ui = true; } match event { glium::glutin::event::Event::WindowEvent { event, .. } => match event { // Break from the loop upon `Escape`. glium::glutin::event::WindowEvent::CloseRequested | glium::glutin::event::WindowEvent::KeyboardInput { input: glium::glutin::event::KeyboardInput { virtual_keycode: Some(glium::glutin::event::VirtualKeyCode::Escape), .. }, .. } => *should_exit = true, _ => {} }, _ => {} } } support::Request::SetUi { needs_redraw } => { // Instantiate a GUI demonstrating every widget type provided by conrod. gui(ui.set_widgets(), &ids, &mut state); *needs_redraw = ui.has_changed(); } support::Request::Redraw => { // Render the `Ui` and then display it on the screen. let primitives = ui.draw(); renderer.fill(display, primitives, &image_map); let mut target = display.draw(); target.clear_color(0.0, 0.0, 0.0, 1.0); renderer.draw(display, &mut target, &image_map).unwrap(); target.finish().unwrap(); } } }) } fn gui(ref mut ui: conrod_core::UiCell, ids: &Ids, state: &mut PromptState) { use conrod_core::{color, widget, Colorable, Labelable, Positionable, Sizeable, Widget}; // Construct our main `Canvas` tree. widget::Canvas::new() .flow_down(&[ ( ids.header, widget::Canvas::new().color(color::WHITE).pad_bottom(20.0), ), ( ids.body, widget::Canvas::new().length(400.0).flow_right(&[ ( ids.left_column, widget::Canvas::new().color(color::LIGHT_ORANGE).pad(20.0), ), ( ids.middle_column, widget::Canvas::new().color(color::ORANGE), ), ( ids.right_column, widget::Canvas::new().color(color::DARK_ORANGE).pad(20.0), ), ]), ), ]) .set(ids.master, ui); widget::Text::new( "This example demonstrates the different prompt widgets.\n\ Click on the buttons to see how they work!", ) .middle_of(ids.header) .font_size(30) .center_justify() .set(ids.explain, ui); // Construct 3 buttons that activate the prompts. let ack_button = widget::Button::new() .middle_of(ids.left_column) .color(color::RED) .w_h(50.0, 50.0) .label("Warning!"); for _click in ack_button.set(ids.ack_button, ui) { // mark that the widget is now loaded! state.ack = true; } if state.ack { let ack_widget = conrod_prompt::Prompt::ack("You have been warned!") .label_font_size(12) .w_h(100.0, 100.0) .middle_of(ids.left_column); if let Some(()) = ack_widget.set(ids.ack_prompt, ui) { state.ack = false; println!("You acknowledged the warning!"); } } let yn_button = widget::Button::new() .middle_of(ids.middle_column) .color(color::BLUE) .w_h(50.0, 50.0) .label("Feeling blue?"); for _click in yn_button.set(ids.yn_button, ui) { // mark that the widget is now loaded! state.yesno = true; } if state.yesno { let yn_widget = conrod_prompt::Prompt::yes_no("Are you allergic to the twentieth century?") .w_h(100.0, 100.0) .label_font_size(12) .middle_of(ids.middle_column); if let Some(b) = yn_widget.set(ids.yn_prompt, ui) { state.yesno = false; if b { println!("Come on down to Wrenwood!"); } else { println!("Maybe if you gave up on the milk..."); } } } let text_button = widget::Button::new() .middle_of(ids.right_column) .color(color::LIGHT_GREEN) .w_h(50.0, 50.0) .label("Tell me something..."); for _click in text_button.set(ids.text_button, ui) { state.text = true; } if state.text { use conrod_prompt::text_confirm::{Event, TextConfirm}; let text_widget = conrod_prompt::Prompt::text_confirm("What's your name?", &state.text_response) .label_font_size(12) // set font sizes of text box, ok and cancel labels .and_then_input(TextConfirm::text_box_font_size, Some(12)) .and_then_input(TextConfirm::ok_label_font_size, Some(12)) .and_then_input(TextConfirm::cancel_label_font_size, Some(12)) .w_h(100.0, 100.0) .middle_of(ids.right_column); for edit in text_widget.set(ids.text_prompt, ui) { match edit { Event::Update(s) => state.text_response = s, Event::Enter => { println!("Hello, {}!", state.text_response); state.text_response.clear(); state.text = false; } Event::Cancel => { println!("Nevermind, then!"); state.text_response.clear(); state.text = false; } } } } } // Generate a unique widget id for each widget. widget_ids! { struct Ids { master, header, explain, body, left_column, middle_column, right_column, ack_button, ack_prompt, yn_button, yn_prompt, text_button, text_prompt, } }