2019-04-21 08:13:05 +00:00
|
|
|
#![deny(warnings)]
|
2020-05-10 17:04:10 +00:00
|
|
|
#![warn(clippy::all)]
|
2020-05-07 08:47:03 +00:00
|
|
|
#![allow(clippy::single_match)]
|
2020-07-21 22:36:17 +00:00
|
|
|
#![allow(deprecated)] // TODO: remove
|
2020-07-22 16:01:27 +00:00
|
|
|
|
2020-07-23 16:54:16 +00:00
|
|
|
mod backend;
|
2019-04-21 08:13:05 +00:00
|
|
|
mod painter;
|
2020-07-23 16:54:16 +00:00
|
|
|
pub mod storage;
|
2019-04-21 08:13:05 +00:00
|
|
|
|
2020-07-23 16:54:16 +00:00
|
|
|
pub use backend::*;
|
2019-04-21 08:13:05 +00:00
|
|
|
pub use painter::Painter;
|
2020-04-29 19:58:14 +00:00
|
|
|
|
2020-07-22 16:46:12 +00:00
|
|
|
use {
|
|
|
|
clipboard::ClipboardProvider,
|
|
|
|
egui::*,
|
|
|
|
glium::glutin::{self, event::VirtualKeyCode, event_loop::ControlFlow},
|
|
|
|
};
|
|
|
|
|
|
|
|
pub use clipboard::ClipboardContext; // TODO: remove
|
2020-04-29 19:58:14 +00:00
|
|
|
|
2020-07-21 22:36:17 +00:00
|
|
|
pub fn input_to_egui(
|
|
|
|
event: glutin::event::WindowEvent,
|
2020-04-29 19:58:14 +00:00
|
|
|
clipboard: Option<&mut ClipboardContext>,
|
|
|
|
raw_input: &mut RawInput,
|
2020-07-22 10:10:14 +00:00
|
|
|
control_flow: &mut ControlFlow,
|
2020-04-29 19:58:14 +00:00
|
|
|
) {
|
2020-07-21 22:36:17 +00:00
|
|
|
use glutin::event::WindowEvent::*;
|
2020-04-29 19:58:14 +00:00
|
|
|
match event {
|
2020-07-22 10:10:14 +00:00
|
|
|
CloseRequested | Destroyed => *control_flow = ControlFlow::Exit,
|
2020-04-29 19:58:14 +00:00
|
|
|
|
2020-07-21 22:36:17 +00:00
|
|
|
Resized(physical_size) => {
|
|
|
|
raw_input.screen_size =
|
|
|
|
egui::vec2(physical_size.width as f32, physical_size.height as f32)
|
|
|
|
/ raw_input.pixels_per_point.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
ScaleFactorChanged {
|
|
|
|
scale_factor,
|
|
|
|
new_inner_size,
|
|
|
|
} => {
|
|
|
|
raw_input.pixels_per_point = Some(scale_factor as f32);
|
|
|
|
raw_input.screen_size =
|
|
|
|
egui::vec2(new_inner_size.width as f32, new_inner_size.height as f32)
|
|
|
|
/ (scale_factor as f32);
|
|
|
|
}
|
|
|
|
|
|
|
|
MouseInput { state, .. } => {
|
|
|
|
raw_input.mouse_down = state == glutin::event::ElementState::Pressed;
|
|
|
|
}
|
|
|
|
CursorMoved { position, .. } => {
|
|
|
|
raw_input.mouse_pos = Some(pos2(
|
|
|
|
position.x as f32 / raw_input.pixels_per_point.unwrap(),
|
|
|
|
position.y as f32 / raw_input.pixels_per_point.unwrap(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
CursorLeft { .. } => {
|
|
|
|
raw_input.mouse_pos = None;
|
|
|
|
}
|
|
|
|
ReceivedCharacter(ch) => {
|
2020-07-30 09:54:42 +00:00
|
|
|
if printable_char(ch) {
|
|
|
|
raw_input.events.push(Event::Text(ch.to_string()));
|
2020-04-29 19:58:14 +00:00
|
|
|
}
|
2020-07-21 22:36:17 +00:00
|
|
|
}
|
|
|
|
KeyboardInput { input, .. } => {
|
|
|
|
if let Some(virtual_keycode) = input.virtual_keycode {
|
|
|
|
// TODO: If mac
|
|
|
|
if input.modifiers.logo() && virtual_keycode == VirtualKeyCode::Q {
|
2020-07-22 10:10:14 +00:00
|
|
|
*control_flow = ControlFlow::Exit;
|
2020-07-21 22:36:17 +00:00
|
|
|
}
|
2020-04-29 19:58:14 +00:00
|
|
|
|
2020-07-21 22:36:17 +00:00
|
|
|
match virtual_keycode {
|
|
|
|
VirtualKeyCode::Paste => {
|
|
|
|
if let Some(clipboard) = clipboard {
|
|
|
|
match clipboard.get_contents() {
|
|
|
|
Ok(contents) => {
|
|
|
|
raw_input.events.push(Event::Text(contents));
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
eprintln!("Paste error: {}", err);
|
2020-04-29 19:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-21 22:36:17 +00:00
|
|
|
}
|
|
|
|
VirtualKeyCode::Copy => raw_input.events.push(Event::Copy),
|
|
|
|
VirtualKeyCode::Cut => raw_input.events.push(Event::Cut),
|
|
|
|
_ => {
|
|
|
|
if let Some(key) = translate_virtual_key_code(virtual_keycode) {
|
|
|
|
raw_input.events.push(Event::Key {
|
|
|
|
key,
|
|
|
|
pressed: input.state == glutin::event::ElementState::Pressed,
|
|
|
|
});
|
2020-04-29 19:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-21 22:36:17 +00:00
|
|
|
}
|
|
|
|
MouseWheel { delta, .. } => {
|
|
|
|
match delta {
|
|
|
|
glutin::event::MouseScrollDelta::LineDelta(x, y) => {
|
|
|
|
let line_height = 24.0; // TODO
|
|
|
|
raw_input.scroll_delta = vec2(x, y) * line_height;
|
|
|
|
}
|
|
|
|
glutin::event::MouseScrollDelta::PixelDelta(delta) => {
|
|
|
|
// Actually point delta
|
|
|
|
raw_input.scroll_delta = vec2(delta.x as f32, delta.y as f32);
|
2020-04-29 19:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-21 22:36:17 +00:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// dbg!(event);
|
|
|
|
}
|
2020-04-29 19:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 09:54:42 +00:00
|
|
|
/// Glium sends special keys (backspace, delete, F1, ...) as characters.
|
|
|
|
/// Ignore those.
|
|
|
|
/// We also ignore '\r', '\n', '\t'.
|
|
|
|
/// Newlines are handled by the `Key::Enter` event.
|
|
|
|
fn printable_char(chr: char) -> bool {
|
|
|
|
let is_in_private_use_area = '\u{e000}' <= chr && chr <= '\u{f8ff}'
|
|
|
|
|| '\u{f0000}' <= chr && chr <= '\u{ffffd}'
|
|
|
|
|| '\u{100000}' <= chr && chr <= '\u{10fffd}';
|
|
|
|
|
|
|
|
!is_in_private_use_area && !chr.is_ascii_control()
|
2020-05-16 17:38:46 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 22:36:17 +00:00
|
|
|
pub fn translate_virtual_key_code(key: VirtualKeyCode) -> Option<egui::Key> {
|
2020-04-29 19:58:14 +00:00
|
|
|
use VirtualKeyCode::*;
|
|
|
|
|
|
|
|
Some(match key {
|
|
|
|
Escape => Key::Escape,
|
|
|
|
Insert => Key::Insert,
|
|
|
|
Home => Key::Home,
|
|
|
|
Delete => Key::Delete,
|
|
|
|
End => Key::End,
|
|
|
|
PageDown => Key::PageDown,
|
|
|
|
PageUp => Key::PageUp,
|
|
|
|
Left => Key::Left,
|
|
|
|
Up => Key::Up,
|
|
|
|
Right => Key::Right,
|
|
|
|
Down => Key::Down,
|
|
|
|
Back => Key::Backspace,
|
2020-07-30 09:54:42 +00:00
|
|
|
Return => Key::Enter,
|
2020-04-29 19:58:14 +00:00
|
|
|
// Space => Key::Space,
|
|
|
|
Tab => Key::Tab,
|
|
|
|
|
|
|
|
LAlt | RAlt => Key::Alt,
|
|
|
|
LShift | RShift => Key::Shift,
|
|
|
|
LControl | RControl => Key::Control,
|
|
|
|
LWin | RWin => Key::Logo,
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-07-21 22:36:17 +00:00
|
|
|
pub fn translate_cursor(cursor_icon: egui::CursorIcon) -> glutin::window::CursorIcon {
|
2020-04-29 19:58:14 +00:00
|
|
|
match cursor_icon {
|
2020-07-21 22:36:17 +00:00
|
|
|
CursorIcon::Default => glutin::window::CursorIcon::Default,
|
|
|
|
CursorIcon::PointingHand => glutin::window::CursorIcon::Hand,
|
|
|
|
CursorIcon::ResizeHorizontal => glutin::window::CursorIcon::EwResize,
|
|
|
|
CursorIcon::ResizeNeSw => glutin::window::CursorIcon::NeswResize,
|
|
|
|
CursorIcon::ResizeNwSe => glutin::window::CursorIcon::NwseResize,
|
|
|
|
CursorIcon::ResizeVertical => glutin::window::CursorIcon::NsResize,
|
|
|
|
CursorIcon::Text => glutin::window::CursorIcon::Text,
|
2020-04-29 19:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_output(
|
2020-05-30 08:22:35 +00:00
|
|
|
output: egui::Output,
|
2020-04-29 19:58:14 +00:00
|
|
|
display: &glium::backend::glutin::Display,
|
|
|
|
clipboard: Option<&mut ClipboardContext>,
|
|
|
|
) {
|
|
|
|
if let Some(url) = output.open_url {
|
|
|
|
if let Err(err) = webbrowser::open(&url) {
|
|
|
|
eprintln!("Failed to open url: {}", err); // TODO show error in imgui
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !output.copied_text.is_empty() {
|
|
|
|
if let Some(clipboard) = clipboard {
|
|
|
|
if let Err(err) = clipboard.set_contents(output.copied_text) {
|
|
|
|
eprintln!("Copy/Cut error: {}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
display
|
|
|
|
.gl_window()
|
2020-07-21 22:36:17 +00:00
|
|
|
.window()
|
|
|
|
.set_cursor_icon(translate_cursor(output.cursor_icon));
|
2020-04-29 19:58:14 +00:00
|
|
|
}
|
2020-05-02 09:37:12 +00:00
|
|
|
|
2020-07-22 10:10:14 +00:00
|
|
|
pub fn init_clipboard() -> Option<ClipboardContext> {
|
|
|
|
match ClipboardContext::new() {
|
|
|
|
Ok(clipboard) => Some(clipboard),
|
|
|
|
Err(err) => {
|
|
|
|
eprintln!("Failed to initialize clipboard: {}", err);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-02 09:37:12 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2020-07-23 10:01:48 +00:00
|
|
|
/// Time of day as seconds since midnight. Used for clock in demo app.
|
2020-09-23 06:57:23 +00:00
|
|
|
pub fn seconds_since_midnight() -> f64 {
|
2020-05-11 18:21:24 +00:00
|
|
|
use chrono::Timelike;
|
|
|
|
let time = chrono::Local::now().time();
|
|
|
|
time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64)
|
|
|
|
}
|
2020-07-22 10:10:14 +00:00
|
|
|
|
|
|
|
pub fn make_raw_input(display: &glium::Display) -> egui::RawInput {
|
|
|
|
let pixels_per_point = display.gl_window().window().scale_factor() as f32;
|
|
|
|
egui::RawInput {
|
|
|
|
screen_size: {
|
|
|
|
let (width, height) = display.get_framebuffer_dimensions();
|
|
|
|
vec2(width as f32, height as f32) / pixels_per_point
|
|
|
|
},
|
|
|
|
pixels_per_point: Some(pixels_per_point),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
}
|