Add support for text input in emilib and glium.

Add input inspectors
This commit is contained in:
Emil Ernerfeldt 2020-04-28 23:05:22 +02:00
parent 41eea6cd86
commit 1e685d1cb0
5 changed files with 144 additions and 50 deletions

View file

@ -9,6 +9,8 @@ pub struct Context {
/// The default style for new regions
pub(crate) style: Mutex<Style>,
pub(crate) fonts: Arc<Fonts>,
/// Raw input from last frame. Use `input()` instead.
pub(crate) last_raw_input: RawInput,
pub(crate) input: GuiInput,
pub(crate) memory: Mutex<Memory>,
pub(crate) graphics: Mutex<GraphicLayers>,
@ -25,7 +27,8 @@ impl Clone for Context {
Context {
style: Mutex::new(self.style()),
fonts: self.fonts.clone(),
input: self.input,
last_raw_input: self.last_raw_input.clone(),
input: self.input.clone(),
memory: Mutex::new(self.memory.lock().clone()),
graphics: Mutex::new(self.graphics.lock().clone()),
output: Mutex::new(self.output.lock().clone()),
@ -39,6 +42,7 @@ impl Context {
Context {
style: Default::default(),
fonts: Arc::new(Fonts::new(pixels_per_point)),
last_raw_input: Default::default(),
input: Default::default(),
memory: Default::default(),
graphics: Default::default(),
@ -52,6 +56,11 @@ impl Context {
(point * self.input.pixels_per_point).round() / self.input.pixels_per_point
}
/// Raw input from last frame. Use `input()` instead.
pub fn last_raw_input(&self) -> &RawInput {
&self.last_raw_input
}
pub fn input(&self) -> &GuiInput {
&self.input
}

View file

@ -1,6 +1,6 @@
use std::sync::Arc;
use crate::{mesher::*, widgets::*, *};
use crate::{containers::*, mesher::*, widgets::*, *};
#[derive(Clone, Copy, Default)]
struct Stats {
@ -38,10 +38,12 @@ impl Emigui {
}
let gui_input = GuiInput::from_last_and_new(&self.last_input, &new_input);
self.last_input = new_input;
self.last_input = new_input.clone(); // TODO: also stored in Context. Remove this one
// TODO: avoid this clone
let mut new_ctx = (*self.ctx).clone();
new_ctx.last_raw_input = new_input;
new_ctx.begin_frame(gui_input);
self.ctx = Arc::new(new_ctx);
}
@ -100,6 +102,17 @@ impl Emigui {
}
});
region.collapsing("Input", |region| {
CollapsingHeader::new("Raw Input")
.default_open()
.show(region, |region| {
region.ctx().last_raw_input().clone().ui(region)
});
CollapsingHeader::new("Input")
.default_open()
.show(region, |region| region.input().clone().ui(region));
});
region.collapsing("Stats", |region| {
region.add(label!(
"Screen size: {} x {} points, pixels_per_point: {}",
@ -132,3 +145,35 @@ fn font_definitions_ui(font_definitions: &mut FontDefinitions, region: &mut Regi
*font_definitions = crate::fonts::default_font_definitions();
}
}
impl RawInput {
pub fn ui(&self, region: &mut Region) {
// TODO: simpler way to show values, e.g. `region.value("Mouse Pos:", self.mouse_pos);
region.add(label!("mouse_down: {}", self.mouse_down));
region.add(label!("mouse_pos: {:.1?}", self.mouse_pos));
region.add(label!("scroll_delta: {:?}", self.scroll_delta));
region.add(label!("screen_size: {:?}", self.screen_size));
region.add(label!("pixels_per_point: {}", self.pixels_per_point));
region.add(label!("time: {:.3} s", self.time));
region.add(label!("text: {:?}", self.text));
// region.add(label!("dropped_files: {}", self.dropped_files));
// region.add(label!("hovered_files: {}", self.hovered_files));
}
}
impl GuiInput {
pub fn ui(&self, region: &mut Region) {
region.add(label!("mouse_down: {}", self.mouse_down));
region.add(label!("mouse_pressed: {}", self.mouse_pressed));
region.add(label!("mouse_released: {}", self.mouse_released));
region.add(label!("mouse_pos: {:?}", self.mouse_pos));
region.add(label!("mouse_move: {:?}", self.mouse_move));
region.add(label!("scroll_delta: {:?}", self.scroll_delta));
region.add(label!("screen_size: {:?}", self.screen_size));
region.add(label!("pixels_per_point: {}", self.pixels_per_point));
region.add(label!("time: {}", self.time));
region.add(label!("text: {:?}", self.text));
// region.add(label!("dropped_files: {}", self.dropped_files));
// region.add(label!("hovered_files: {}", self.hovered_files));
}
}

View file

@ -9,7 +9,7 @@ use crate::{
/// What the integration gives to the gui.
/// All coordinates in emigui is in point/logical coordinates.
#[derive(Clone, Copy, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(default)]
pub struct RawInput {
/// Is the button currently down?
@ -29,10 +29,19 @@ pub struct RawInput {
/// Time in seconds. Relative to whatever. Used for animation.
pub time: f64,
/// Text input, e.g. via keyboard or paste action
pub text: String,
/// Files has been dropped into the window.
pub dropped_files: Vec<std::path::PathBuf>,
/// Someone is threatening to drop these on us.
pub hovered_files: Vec<std::path::PathBuf>,
}
/// What the gui maintains
#[derive(Clone, Copy, Debug, Default)]
#[derive(Clone, Debug, Default)]
pub struct GuiInput {
/// Is the button currently down?
/// true the frame when it is pressed,
@ -63,6 +72,15 @@ pub struct GuiInput {
/// Time in seconds. Relative to whatever. Used for animation.
pub time: f64,
/// Text input, e.g. via keyboard or paste action
pub text: String,
/// Files has been dropped into the window.
pub dropped_files: Vec<std::path::PathBuf>,
/// Someone is threatening to drop these on us.
pub hovered_files: Vec<std::path::PathBuf>,
}
impl GuiInput {
@ -81,6 +99,9 @@ impl GuiInput {
screen_size: new.screen_size,
pixels_per_point: new.pixels_per_point,
time: new.time,
text: new.text.clone(),
dropped_files: new.dropped_files.clone(),
hovered_files: new.hovered_files.clone(),
}
}
}

View file

@ -36,7 +36,7 @@ fn main() {
..Default::default()
};
let mut quit = false;
let mut running = true;
// used to keep track of time for animations
let start_time = Instant::now();
@ -45,7 +45,7 @@ fn main() {
let mut example_app = ExampleApp::default();
while !quit {
while running {
{
// Keep smooth frame rate. TODO: proper vsync
let frame_duration = frame_start.elapsed();
@ -55,54 +55,22 @@ fn main() {
frame_start = Instant::now();
}
{
raw_input.time = start_time.elapsed().as_nanos() as f64 * 1e-9;
raw_input.scroll_delta = vec2(0.0, 0.0);
raw_input.text.clear();
raw_input.dropped_files.clear();
raw_input.hovered_files.clear();
events_loop.poll_events(|event| input_event(event, &mut raw_input, &mut running));
}
events_loop.poll_events(|event| match event {
glutin::Event::WindowEvent { event, .. } => match event {
glutin::WindowEvent::CloseRequested => quit = true,
glutin::WindowEvent::Resized(glutin::dpi::LogicalSize { width, height }) => {
raw_input.screen_size = vec2(width as f32, height as f32);
}
glutin::WindowEvent::MouseInput { state, .. } => {
raw_input.mouse_down = state == glutin::ElementState::Pressed;
}
glutin::WindowEvent::CursorMoved { position, .. } => {
raw_input.mouse_pos = Some(pos2(position.x as f32, position.y as f32));
}
glutin::WindowEvent::KeyboardInput { input, .. } => {
if input.virtual_keycode == Some(glutin::VirtualKeyCode::Q)
&& input.modifiers.logo
{
quit = true;
}
}
glutin::WindowEvent::MouseWheel { delta, .. } => {
match delta {
glutin::MouseScrollDelta::LineDelta(x, y) => {
raw_input.scroll_delta = vec2(x, y) * 24.0;
}
glutin::MouseScrollDelta::PixelDelta(delta) => {
// Actually point delta
raw_input.scroll_delta = vec2(delta.x as f32, delta.y as f32);
}
}
}
_ => {
// dbg!(event);
}
},
_ => (),
});
emigui.begin_frame(raw_input);
emigui.begin_frame(raw_input.clone()); // TODO: avoid clone
let mut region = emigui.background_region();
let mut region = region.centered_column(region.available_width().min(480.0));
region.set_align(Align::Min);
region.add(label!("Emigui running inside of Glium").text_style(emigui::TextStyle::Heading));
if region.add(Button::new("Quit")).clicked {
quit = true;
running = false;
}
// TODO: Make it even simpler to show a window
@ -139,3 +107,53 @@ fn main() {
display.gl_window().set_cursor(cursor);
}
}
fn input_event(event: glutin::Event, raw_input: &mut RawInput, running: &mut bool) {
use glutin::WindowEvent::*;
match event {
glutin::Event::WindowEvent { event, .. } => match event {
CloseRequested | Destroyed => *running = false,
DroppedFile(path) => raw_input.dropped_files.push(path),
HoveredFile(path) => raw_input.hovered_files.push(path),
Resized(glutin::dpi::LogicalSize { width, height }) => {
raw_input.screen_size = vec2(width as f32, height as f32);
}
MouseInput { state, .. } => {
raw_input.mouse_down = state == glutin::ElementState::Pressed;
}
CursorMoved { position, .. } => {
raw_input.mouse_pos = Some(pos2(position.x as f32, position.y as f32));
}
CursorLeft { .. } => {
raw_input.mouse_pos = None;
}
ReceivedCharacter(ch) => {
raw_input.text.push(ch);
}
KeyboardInput { input, .. } => {
if input.virtual_keycode == Some(glutin::VirtualKeyCode::Q) && input.modifiers.logo
{
*running = false;
}
}
MouseWheel { delta, .. } => {
match delta {
glutin::MouseScrollDelta::LineDelta(x, y) => {
raw_input.scroll_delta = vec2(x, y) * 24.0;
}
glutin::MouseScrollDelta::PixelDelta(delta) => {
// Actually point delta
raw_input.scroll_delta = vec2(delta.x as f32, delta.y as f32);
}
}
}
// TODO: HiDpiFactorChanged
_ => {
// dbg!(event);
}
},
_ => (),
}
}

View file

@ -42,6 +42,7 @@ impl State {
fn run(&mut self, raw_input: RawInput) -> Result<Output, JsValue> {
let everything_start = now_sec();
let pixels_per_point = raw_input.pixels_per_point;
self.emigui.begin_frame(raw_input);
let mut region = self.emigui.background_region();
@ -98,7 +99,7 @@ impl State {
bg_color,
batches,
self.emigui.texture(),
raw_input.pixels_per_point,
pixels_per_point,
)?;
self.frame_times.push_back(now_sec() - everything_start);