egui/egui_glium/src/lib.rs

246 lines
8.2 KiB
Rust
Raw Normal View History

#![forbid(unsafe_code)]
2019-04-21 08:13:05 +00:00
#![deny(warnings)]
2020-05-10 17:04:10 +00:00
#![warn(clippy::all)]
#![allow(clippy::single_match)]
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;
use {
clipboard::ClipboardProvider,
egui::*,
glium::glutin::{self, event::VirtualKeyCode, event_loop::ControlFlow},
};
pub use clipboard::ClipboardContext; // TODO: remove
2020-11-13 10:04:45 +00:00
pub struct GliumInputState {
raw: egui::RawInput,
/// Command modifier key.
/// Mac command key on Mac, ctrl on Window/Linux.
cmd: bool,
}
impl GliumInputState {
pub fn from_pixels_per_point(pixels_per_point: f32) -> Self {
Self {
raw: egui::RawInput {
pixels_per_point: Some(pixels_per_point),
..Default::default()
},
cmd: false,
}
}
}
pub fn input_to_egui(
event: glutin::event::WindowEvent,
clipboard: Option<&mut ClipboardContext>,
2020-11-13 10:04:45 +00:00
input_state: &mut GliumInputState,
control_flow: &mut ControlFlow,
) {
use glutin::event::WindowEvent::*;
match event {
CloseRequested | Destroyed => *control_flow = ControlFlow::Exit,
MouseInput { state, .. } => {
2020-11-13 10:04:45 +00:00
input_state.raw.mouse_down = state == glutin::event::ElementState::Pressed;
}
CursorMoved {
position: pos_in_pixels,
..
} => {
2020-11-13 10:04:45 +00:00
input_state.raw.mouse_pos = Some(pos2(
pos_in_pixels.x as f32 / input_state.raw.pixels_per_point.unwrap(),
pos_in_pixels.y as f32 / input_state.raw.pixels_per_point.unwrap(),
));
}
CursorLeft { .. } => {
2020-11-13 10:04:45 +00:00
input_state.raw.mouse_pos = None;
}
ReceivedCharacter(ch) => {
2020-11-13 10:04:45 +00:00
if !input_state.cmd && printable_char(ch) {
input_state.raw.events.push(Event::Text(ch.to_string()));
}
}
KeyboardInput { input, .. } => {
if let Some(virtual_keycode) = input.virtual_keycode {
2020-11-13 10:04:45 +00:00
let is_command_key = if cfg!(target_os = "macos") {
matches!(virtual_keycode, VirtualKeyCode::LWin | VirtualKeyCode::RWin)
} else {
matches!(
virtual_keycode,
VirtualKeyCode::LControl | VirtualKeyCode::RControl
)
};
if is_command_key {
input_state.cmd = input.state == glutin::event::ElementState::Pressed;
}
2020-11-13 10:04:45 +00:00
if input.state == glutin::event::ElementState::Pressed {
if cfg!(target_os = "macos")
&& input_state.cmd
&& virtual_keycode == VirtualKeyCode::Q
{
*control_flow = ControlFlow::Exit;
}
// VirtualKeyCode::Paste etc in winit are broken/untrustworthy,
// so we detect these things manually:
if input_state.cmd && virtual_keycode == VirtualKeyCode::X {
input_state.raw.events.push(Event::Cut);
} else if input_state.cmd && virtual_keycode == VirtualKeyCode::C {
input_state.raw.events.push(Event::Copy);
} else if input_state.cmd && virtual_keycode == VirtualKeyCode::V {
if let Some(clipboard) = clipboard {
match clipboard.get_contents() {
Ok(contents) => {
2020-11-13 10:04:45 +00:00
input_state.raw.events.push(Event::Text(contents));
}
Err(err) => {
eprintln!("Paste error: {}", err);
}
}
}
2020-11-13 10:04:45 +00:00
} else if let Some(key) = translate_virtual_key_code(virtual_keycode) {
input_state.raw.events.push(Event::Key {
key,
pressed: input.state == glutin::event::ElementState::Pressed,
});
}
}
}
}
MouseWheel { delta, .. } => {
match delta {
glutin::event::MouseScrollDelta::LineDelta(x, y) => {
let line_height = 24.0; // TODO
2020-11-13 10:04:45 +00:00
input_state.raw.scroll_delta = vec2(x, y) * line_height;
}
glutin::event::MouseScrollDelta::PixelDelta(delta) => {
// Actually point delta
2020-11-13 10:04:45 +00:00
input_state.raw.scroll_delta = vec2(delta.x as f32, delta.y as f32);
}
}
}
_ => {
// dbg!(event);
}
}
}
/// 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()
}
pub fn translate_virtual_key_code(key: VirtualKeyCode) -> Option<egui::Key> {
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,
Return => Key::Enter,
// Space => Key::Space,
Tab => Key::Tab,
LAlt | RAlt => Key::Alt,
LShift | RShift => Key::Shift,
LControl | RControl => Key::Control,
LWin | RWin => Key::Logo,
_ => {
return None;
}
})
}
pub fn translate_cursor(cursor_icon: egui::CursorIcon) -> glutin::window::CursorIcon {
match cursor_icon {
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,
CursorIcon::Grab => glutin::window::CursorIcon::Grab,
CursorIcon::Grabbing => glutin::window::CursorIcon::Grabbing,
}
}
pub fn handle_output(
output: egui::Output,
display: &glium::backend::glutin::Display,
clipboard: Option<&mut ClipboardContext>,
) {
if let Some(url) = output.open_url {
if let Err(err) = webbrowser::open(&url) {
2020-10-01 20:40:49 +00:00
eprintln!("Failed to open url: {}", err);
}
}
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()
.window()
.set_cursor_icon(translate_cursor(output.cursor_icon));
}
pub fn init_clipboard() -> Option<ClipboardContext> {
match ClipboardContext::new() {
Ok(clipboard) => Some(clipboard),
Err(err) => {
eprintln!("Failed to initialize clipboard: {}", err);
None
}
}
}
// ----------------------------------------------------------------------------
/// Time of day as seconds since midnight. Used for clock in demo app.
pub fn seconds_since_midnight() -> f64 {
use chrono::Timelike;
let time = chrono::Local::now().time();
time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64)
}
pub fn screen_size_in_pixels(display: &glium::Display) -> Vec2 {
let (width_in_pixels, height_in_pixels) = display.get_framebuffer_dimensions();
vec2(width_in_pixels as f32, height_in_pixels as f32)
}
pub fn native_pixels_per_point(display: &glium::Display) -> f32 {
display.gl_window().window().scale_factor() as f32
}