Add support for text input in emilib and glium.
Add input inspectors
This commit is contained in:
parent
41eea6cd86
commit
1e685d1cb0
5 changed files with 144 additions and 50 deletions
|
@ -9,6 +9,8 @@ pub struct Context {
|
||||||
/// The default style for new regions
|
/// The default style for new regions
|
||||||
pub(crate) style: Mutex<Style>,
|
pub(crate) style: Mutex<Style>,
|
||||||
pub(crate) fonts: Arc<Fonts>,
|
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) input: GuiInput,
|
||||||
pub(crate) memory: Mutex<Memory>,
|
pub(crate) memory: Mutex<Memory>,
|
||||||
pub(crate) graphics: Mutex<GraphicLayers>,
|
pub(crate) graphics: Mutex<GraphicLayers>,
|
||||||
|
@ -25,7 +27,8 @@ impl Clone for Context {
|
||||||
Context {
|
Context {
|
||||||
style: Mutex::new(self.style()),
|
style: Mutex::new(self.style()),
|
||||||
fonts: self.fonts.clone(),
|
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()),
|
memory: Mutex::new(self.memory.lock().clone()),
|
||||||
graphics: Mutex::new(self.graphics.lock().clone()),
|
graphics: Mutex::new(self.graphics.lock().clone()),
|
||||||
output: Mutex::new(self.output.lock().clone()),
|
output: Mutex::new(self.output.lock().clone()),
|
||||||
|
@ -39,6 +42,7 @@ impl Context {
|
||||||
Context {
|
Context {
|
||||||
style: Default::default(),
|
style: Default::default(),
|
||||||
fonts: Arc::new(Fonts::new(pixels_per_point)),
|
fonts: Arc::new(Fonts::new(pixels_per_point)),
|
||||||
|
last_raw_input: Default::default(),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
memory: Default::default(),
|
memory: Default::default(),
|
||||||
graphics: Default::default(),
|
graphics: Default::default(),
|
||||||
|
@ -52,6 +56,11 @@ impl Context {
|
||||||
(point * self.input.pixels_per_point).round() / self.input.pixels_per_point
|
(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 {
|
pub fn input(&self) -> &GuiInput {
|
||||||
&self.input
|
&self.input
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{mesher::*, widgets::*, *};
|
use crate::{containers::*, mesher::*, widgets::*, *};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
struct Stats {
|
struct Stats {
|
||||||
|
@ -38,10 +38,12 @@ impl Emigui {
|
||||||
}
|
}
|
||||||
|
|
||||||
let gui_input = GuiInput::from_last_and_new(&self.last_input, &new_input);
|
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
|
// TODO: avoid this clone
|
||||||
let mut new_ctx = (*self.ctx).clone();
|
let mut new_ctx = (*self.ctx).clone();
|
||||||
|
|
||||||
|
new_ctx.last_raw_input = new_input;
|
||||||
new_ctx.begin_frame(gui_input);
|
new_ctx.begin_frame(gui_input);
|
||||||
self.ctx = Arc::new(new_ctx);
|
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.collapsing("Stats", |region| {
|
||||||
region.add(label!(
|
region.add(label!(
|
||||||
"Screen size: {} x {} points, pixels_per_point: {}",
|
"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();
|
*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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
|
|
||||||
/// What the integration gives to the gui.
|
/// What the integration gives to the gui.
|
||||||
/// All coordinates in emigui is in point/logical coordinates.
|
/// All coordinates in emigui is in point/logical coordinates.
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct RawInput {
|
pub struct RawInput {
|
||||||
/// Is the button currently down?
|
/// Is the button currently down?
|
||||||
|
@ -29,10 +29,19 @@ pub struct RawInput {
|
||||||
|
|
||||||
/// Time in seconds. Relative to whatever. Used for animation.
|
/// Time in seconds. Relative to whatever. Used for animation.
|
||||||
pub time: f64,
|
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
|
/// What the gui maintains
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct GuiInput {
|
pub struct GuiInput {
|
||||||
/// Is the button currently down?
|
/// Is the button currently down?
|
||||||
/// true the frame when it is pressed,
|
/// true the frame when it is pressed,
|
||||||
|
@ -63,6 +72,15 @@ pub struct GuiInput {
|
||||||
|
|
||||||
/// Time in seconds. Relative to whatever. Used for animation.
|
/// Time in seconds. Relative to whatever. Used for animation.
|
||||||
pub time: f64,
|
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 {
|
impl GuiInput {
|
||||||
|
@ -81,6 +99,9 @@ impl GuiInput {
|
||||||
screen_size: new.screen_size,
|
screen_size: new.screen_size,
|
||||||
pixels_per_point: new.pixels_per_point,
|
pixels_per_point: new.pixels_per_point,
|
||||||
time: new.time,
|
time: new.time,
|
||||||
|
text: new.text.clone(),
|
||||||
|
dropped_files: new.dropped_files.clone(),
|
||||||
|
hovered_files: new.hovered_files.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ fn main() {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut quit = false;
|
let mut running = true;
|
||||||
|
|
||||||
// used to keep track of time for animations
|
// used to keep track of time for animations
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
@ -45,7 +45,7 @@ fn main() {
|
||||||
|
|
||||||
let mut example_app = ExampleApp::default();
|
let mut example_app = ExampleApp::default();
|
||||||
|
|
||||||
while !quit {
|
while running {
|
||||||
{
|
{
|
||||||
// Keep smooth frame rate. TODO: proper vsync
|
// Keep smooth frame rate. TODO: proper vsync
|
||||||
let frame_duration = frame_start.elapsed();
|
let frame_duration = frame_start.elapsed();
|
||||||
|
@ -55,54 +55,22 @@ fn main() {
|
||||||
frame_start = Instant::now();
|
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.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 {
|
emigui.begin_frame(raw_input.clone()); // TODO: avoid clone
|
||||||
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);
|
|
||||||
let mut region = emigui.background_region();
|
let mut region = emigui.background_region();
|
||||||
let mut region = region.centered_column(region.available_width().min(480.0));
|
let mut region = region.centered_column(region.available_width().min(480.0));
|
||||||
region.set_align(Align::Min);
|
region.set_align(Align::Min);
|
||||||
region.add(label!("Emigui running inside of Glium").text_style(emigui::TextStyle::Heading));
|
region.add(label!("Emigui running inside of Glium").text_style(emigui::TextStyle::Heading));
|
||||||
if region.add(Button::new("Quit")).clicked {
|
if region.add(Button::new("Quit")).clicked {
|
||||||
quit = true;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make it even simpler to show a window
|
// TODO: Make it even simpler to show a window
|
||||||
|
@ -139,3 +107,53 @@ fn main() {
|
||||||
display.gl_window().set_cursor(cursor);
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ impl State {
|
||||||
fn run(&mut self, raw_input: RawInput) -> Result<Output, JsValue> {
|
fn run(&mut self, raw_input: RawInput) -> Result<Output, JsValue> {
|
||||||
let everything_start = now_sec();
|
let everything_start = now_sec();
|
||||||
|
|
||||||
|
let pixels_per_point = raw_input.pixels_per_point;
|
||||||
self.emigui.begin_frame(raw_input);
|
self.emigui.begin_frame(raw_input);
|
||||||
|
|
||||||
let mut region = self.emigui.background_region();
|
let mut region = self.emigui.background_region();
|
||||||
|
@ -98,7 +99,7 @@ impl State {
|
||||||
bg_color,
|
bg_color,
|
||||||
batches,
|
batches,
|
||||||
self.emigui.texture(),
|
self.emigui.texture(),
|
||||||
raw_input.pixels_per_point,
|
pixels_per_point,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.frame_times.push_back(now_sec() - everything_start);
|
self.frame_times.push_back(now_sec() - everything_start);
|
||||||
|
|
Loading…
Reference in a new issue