1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 06:10:06 -04:00

Split out new crate egui-winit from egui_glium (#735)

This commit is contained in:
Emil Ernerfeldt
2021-09-28 17:33:28 +02:00
committed by GitHub
parent ba0e3780a1
commit 1b36863248
27 changed files with 1003 additions and 616 deletions

View File

@@ -77,7 +77,7 @@ fn window_builder_drag_and_drop(
fn create_display(
app: &dyn epi::App,
native_options: &epi::NativeOptions,
window_settings: Option<WindowSettings>,
window_settings: &Option<WindowSettings>,
window_icon: Option<glutin::window::Icon>,
event_loop: &glutin::event_loop::EventLoop<RequestRepaintEvent>,
) -> glium::Display {
@@ -95,8 +95,8 @@ fn create_display(
let initial_size_points = native_options.initial_window_size;
if let Some(window_settings) = &window_settings {
window_builder = window_settings.initialize_size(window_builder);
if let Some(window_settings) = window_settings {
window_builder = window_settings.initialize_window(window_builder);
} else if let Some(initial_size_points) = initial_size_points {
window_builder = window_builder.with_inner_size(glutin::dpi::LogicalSize {
width: initial_size_points.x as f64,
@@ -110,20 +110,7 @@ fn create_display(
.with_stencil_buffer(0)
.with_vsync(true);
let display = glium::Display::new(window_builder, context_builder, event_loop).unwrap();
if !cfg!(target_os = "windows") {
// If the app last ran on two monitors and only one is now connected, then
// the given position is invalid.
// If this happens on Mac, the window is clamped into valid area.
// If this happens on Windows, the window is hidden and impossible to bring to get at.
// So we don't restore window positions on Windows.
if let Some(window_settings) = &window_settings {
window_settings.restore_positions(&display);
}
}
display
glium::Display::new(window_builder, context_builder, event_loop).unwrap()
}
#[cfg(not(feature = "persistence"))]
@@ -173,14 +160,14 @@ fn load_icon(icon_data: epi::IconData) -> Option<glutin::window::Icon> {
// ----------------------------------------------------------------------------
/// Run an egui app
pub fn run(mut app: Box<dyn epi::App>, native_options: epi::NativeOptions) {
pub fn run(mut app: Box<dyn epi::App>, native_options: &epi::NativeOptions) {
#[allow(unused_mut)]
let mut storage = create_storage(app.name());
let window_settings = deserialize_window_settings(&storage);
let mut event_loop = glutin::event_loop::EventLoop::with_user_event();
let icon = native_options.icon_data.clone().and_then(load_icon);
let display = create_display(&*app, &native_options, window_settings, icon, &event_loop);
let display = create_display(&*app, native_options, &window_settings, icon, &event_loop);
let repaint_signal = std::sync::Arc::new(GliumRepaintSignal(std::sync::Mutex::new(
event_loop.create_proxy(),
@@ -260,7 +247,7 @@ pub fn run(mut app: Box<dyn epi::App>, native_options: epi::NativeOptions) {
} else {
// Winit uses up all the CPU of one core when returning ControlFlow::Wait.
// Sleeping here helps, but still uses 1-3% of CPU :(
if is_focused || !egui.input_state.raw.hovered_files.is_empty() {
if is_focused || !egui.egui_input().hovered_files.is_empty() {
std::thread::sleep(std::time::Duration::from_millis(10));
} else {
std::thread::sleep(std::time::Duration::from_millis(50));

View File

@@ -8,495 +8,87 @@
// Forbid warnings in release builds:
#![cfg_attr(not(debug_assertions), deny(warnings))]
#![forbid(unsafe_code)]
#![warn(clippy::all, missing_crate_level_docs, rust_2018_idioms)]
#![allow(clippy::manual_range_contains, clippy::single_match)]
#![warn(
clippy::all,
clippy::await_holding_lock,
clippy::char_lit_as_u8,
clippy::checked_conversions,
clippy::dbg_macro,
clippy::debug_assert_with_mut_call,
clippy::doc_markdown,
clippy::empty_enum,
clippy::enum_glob_use,
clippy::exit,
clippy::expl_impl_clone_on_copy,
clippy::explicit_deref_methods,
clippy::explicit_into_iter_loop,
clippy::fallible_impl_from,
clippy::filter_map_next,
clippy::float_cmp_const,
clippy::fn_params_excessive_bools,
clippy::if_let_mutex,
clippy::imprecise_flops,
clippy::inefficient_to_string,
clippy::invalid_upcast_comparisons,
clippy::large_types_passed_by_value,
clippy::let_unit_value,
clippy::linkedlist,
clippy::lossy_float_literal,
clippy::macro_use_imports,
clippy::manual_ok_or,
clippy::map_err_ignore,
clippy::map_flatten,
clippy::match_on_vec_items,
clippy::match_same_arms,
clippy::match_wildcard_for_single_variants,
clippy::mem_forget,
clippy::mismatched_target_os,
clippy::missing_errors_doc,
clippy::missing_safety_doc,
clippy::mut_mut,
clippy::mutex_integer,
clippy::needless_borrow,
clippy::needless_continue,
clippy::needless_pass_by_value,
clippy::option_option,
clippy::path_buf_push_overwrite,
clippy::ptr_as_ptr,
clippy::ref_option_ref,
clippy::rest_pat_in_fully_bound_structs,
clippy::same_functions_in_if_condition,
clippy::string_add_assign,
clippy::string_add,
clippy::string_lit_as_bytes,
clippy::string_to_string,
clippy::todo,
clippy::trait_duplication_in_bounds,
clippy::unimplemented,
clippy::unnested_or_patterns,
clippy::unused_self,
clippy::useless_transmute,
clippy::verbose_file_reads,
clippy::zero_sized_map_values,
future_incompatible,
missing_crate_level_docs,
nonstandard_style,
rust_2018_idioms
)]
#![allow(clippy::float_cmp)]
#![allow(clippy::manual_range_contains)]
mod backend;
mod painter;
#[cfg(feature = "persistence")]
pub mod persistence;
pub mod screen_reader;
pub mod window_settings;
pub use backend::*;
pub use painter::Painter;
pub use egui_winit;
pub use epi::NativeOptions;
use {
copypasta::ClipboardProvider,
egui::*,
glium::glutin::{
self,
event::{Force, VirtualKeyCode},
},
std::hash::{Hash, Hasher},
};
pub use copypasta::ClipboardContext;
pub struct GliumInputState {
pub pointer_pos_in_points: Option<Pos2>,
pub any_pointer_button_down: bool,
pub raw: egui::RawInput,
}
impl GliumInputState {
pub fn from_pixels_per_point(pixels_per_point: f32) -> Self {
Self {
pointer_pos_in_points: Default::default(),
any_pointer_button_down: false,
raw: egui::RawInput {
pixels_per_point: Some(pixels_per_point),
..Default::default()
},
}
}
}
/// Helper: checks for Alt-F4 (windows/linux) or Cmd-Q (Mac)
pub fn is_quit_shortcut(
input_state: &GliumInputState,
input: &glium::glutin::event::KeyboardInput,
) -> bool {
if cfg!(target_os = "macos") {
input.state == glutin::event::ElementState::Pressed
&& input_state.raw.modifiers.mac_cmd
&& input.virtual_keycode == Some(VirtualKeyCode::Q)
} else {
input.state == glutin::event::ElementState::Pressed
&& input_state.raw.modifiers.alt
&& input.virtual_keycode == Some(VirtualKeyCode::F4)
}
}
/// Is this a close event or a Cmd-Q/Alt-F4 keyboard command?
pub fn is_quit_event(
input_state: &GliumInputState,
event: &glutin::event::WindowEvent<'_>,
) -> bool {
use glutin::event::WindowEvent;
match event {
WindowEvent::CloseRequested | WindowEvent::Destroyed => true,
WindowEvent::KeyboardInput { input, .. } => is_quit_shortcut(input_state, input),
_ => false,
}
}
pub fn input_to_egui(
pixels_per_point: f32,
event: &glutin::event::WindowEvent<'_>,
clipboard: Option<&mut ClipboardContext>,
input_state: &mut GliumInputState,
) {
// Useful for debugging egui touch support on non-touch devices.
let simulate_touches = false;
use glutin::event::WindowEvent;
match event {
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
input_state.raw.pixels_per_point = Some(*scale_factor as f32);
}
WindowEvent::MouseInput { state, button, .. } => {
if let Some(pos) = input_state.pointer_pos_in_points {
if let Some(button) = translate_mouse_button(*button) {
let pressed = *state == glutin::event::ElementState::Pressed;
input_state.raw.events.push(egui::Event::PointerButton {
pos,
button,
pressed,
modifiers: input_state.raw.modifiers,
});
if simulate_touches {
if pressed {
input_state.any_pointer_button_down = true;
input_state.raw.events.push(egui::Event::Touch {
device_id: egui::TouchDeviceId(0),
id: egui::TouchId(0),
phase: egui::TouchPhase::Start,
pos,
force: 0.0
});
} else {
input_state.any_pointer_button_down = false;
input_state.raw.events.push(egui::Event::PointerGone);
input_state.raw.events.push(egui::Event::Touch {
device_id: egui::TouchDeviceId(0),
id: egui::TouchId(0),
phase: egui::TouchPhase::End,
pos,
force: 0.0
});
};
}
}
}
}
WindowEvent::CursorMoved {
position: pos_in_pixels,
..
} => {
let pos_in_points = pos2(
pos_in_pixels.x as f32 / pixels_per_point,
pos_in_pixels.y as f32 / pixels_per_point,
);
input_state.pointer_pos_in_points = Some(pos_in_points);
if simulate_touches {
if input_state.any_pointer_button_down {
input_state
.raw
.events
.push(egui::Event::PointerMoved(pos_in_points));
input_state.raw.events.push(egui::Event::Touch {
device_id: egui::TouchDeviceId(0),
id: egui::TouchId(0),
phase: egui::TouchPhase::Move,
pos: pos_in_points,
force: 0.0
});
}
} else {
input_state
.raw
.events
.push(egui::Event::PointerMoved(pos_in_points));
}
}
WindowEvent::CursorLeft { .. } => {
input_state.pointer_pos_in_points = None;
input_state.raw.events.push(egui::Event::PointerGone);
}
WindowEvent::ReceivedCharacter(ch) => {
if is_printable_char(*ch)
&& !input_state.raw.modifiers.ctrl
&& !input_state.raw.modifiers.mac_cmd
{
input_state.raw.events.push(Event::Text(ch.to_string()));
}
}
WindowEvent::KeyboardInput { input, .. } => {
if let Some(keycode) = input.virtual_keycode {
let pressed = input.state == glutin::event::ElementState::Pressed;
// We could also use `WindowEvent::ModifiersChanged` instead, I guess.
if matches!(keycode, VirtualKeyCode::LAlt | VirtualKeyCode::RAlt) {
input_state.raw.modifiers.alt = pressed;
}
if matches!(keycode, VirtualKeyCode::LControl | VirtualKeyCode::RControl) {
input_state.raw.modifiers.ctrl = pressed;
if !cfg!(target_os = "macos") {
input_state.raw.modifiers.command = pressed;
}
}
if matches!(keycode, VirtualKeyCode::LShift | VirtualKeyCode::RShift) {
input_state.raw.modifiers.shift = pressed;
}
if cfg!(target_os = "macos")
&& matches!(keycode, VirtualKeyCode::LWin | VirtualKeyCode::RWin)
{
input_state.raw.modifiers.mac_cmd = pressed;
input_state.raw.modifiers.command = pressed;
}
if pressed {
// VirtualKeyCode::Paste etc in winit are broken/untrustworthy,
// so we detect these things manually:
if is_cut_command(input_state.raw.modifiers, keycode) {
input_state.raw.events.push(Event::Cut);
} else if is_copy_command(input_state.raw.modifiers, keycode) {
input_state.raw.events.push(Event::Copy);
} else if is_paste_command(input_state.raw.modifiers, keycode) {
if let Some(clipboard) = clipboard {
match clipboard.get_contents() {
Ok(contents) => {
input_state.raw.events.push(Event::Text(contents));
}
Err(err) => {
eprintln!("Paste error: {}", err);
}
}
}
}
}
if let Some(key) = translate_virtual_key_code(keycode) {
input_state.raw.events.push(Event::Key {
key,
pressed,
modifiers: input_state.raw.modifiers,
});
}
}
}
WindowEvent::Focused(_) => {
// We will not be given a KeyboardInput event when the modifiers are released while
// the window does not have focus. Unset all modifier state to be safe.
input_state.raw.modifiers = Modifiers::default();
}
WindowEvent::MouseWheel { delta, .. } => {
let mut delta = match *delta {
glutin::event::MouseScrollDelta::LineDelta(x, y) => {
let points_per_scroll_line = 50.0; // Scroll speed decided by consensus: https://github.com/emilk/egui/issues/461
vec2(x, y) * points_per_scroll_line
}
glutin::event::MouseScrollDelta::PixelDelta(delta) => {
vec2(delta.x as f32, delta.y as f32) / pixels_per_point
}
};
if cfg!(target_os = "macos") {
// This is still buggy in winit despite
// https://github.com/rust-windowing/winit/issues/1695 being closed
delta.x *= -1.0;
}
if input_state.raw.modifiers.ctrl || input_state.raw.modifiers.command {
// Treat as zoom instead:
input_state.raw.zoom_delta *= (delta.y / 200.0).exp();
} else {
input_state.raw.scroll_delta += delta;
}
}
WindowEvent::TouchpadPressure {
// device_id,
// pressure,
// stage,
..
} => {
// TODO
}
WindowEvent::Touch(touch) => {
let pixels_per_point_recip = 1. / pixels_per_point;
let mut hasher = std::collections::hash_map::DefaultHasher::new();
touch.device_id.hash(&mut hasher);
input_state.raw.events.push(Event::Touch {
device_id: TouchDeviceId(hasher.finish()),
id: TouchId::from(touch.id),
phase: match touch.phase {
glutin::event::TouchPhase::Started => egui::TouchPhase::Start,
glutin::event::TouchPhase::Moved => egui::TouchPhase::Move,
glutin::event::TouchPhase::Ended => egui::TouchPhase::End,
glutin::event::TouchPhase::Cancelled => egui::TouchPhase::Cancel,
},
pos: pos2(touch.location.x as f32 * pixels_per_point_recip,
touch.location.y as f32 * pixels_per_point_recip),
force: match touch.force {
Some(Force::Normalized(force)) => force as f32,
Some(Force::Calibrated {
force,
max_possible_force,
..
}) => (force / max_possible_force) as f32,
None => 0_f32,
},
});
}
WindowEvent::HoveredFile(path) => {
input_state.raw.hovered_files.push(egui::HoveredFile {
path: Some(path.clone()),
..Default::default()
});
}
WindowEvent::HoveredFileCancelled => {
input_state.raw.hovered_files.clear();
}
WindowEvent::DroppedFile(path) => {
input_state.raw.hovered_files.clear();
input_state.raw.dropped_files.push(egui::DroppedFile {
path: Some(path.clone()),
..Default::default()
});
}
_ => {
// 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 is_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()
}
fn is_cut_command(modifiers: egui::Modifiers, keycode: VirtualKeyCode) -> bool {
(modifiers.command && keycode == VirtualKeyCode::X)
|| (cfg!(target_os = "windows") && modifiers.shift && keycode == VirtualKeyCode::Delete)
}
fn is_copy_command(modifiers: egui::Modifiers, keycode: VirtualKeyCode) -> bool {
(modifiers.command && keycode == VirtualKeyCode::C)
|| (cfg!(target_os = "windows") && modifiers.ctrl && keycode == VirtualKeyCode::Insert)
}
fn is_paste_command(modifiers: egui::Modifiers, keycode: VirtualKeyCode) -> bool {
(modifiers.command && keycode == VirtualKeyCode::V)
|| (cfg!(target_os = "windows") && modifiers.shift && keycode == VirtualKeyCode::Insert)
}
pub fn translate_mouse_button(button: glutin::event::MouseButton) -> Option<egui::PointerButton> {
match button {
glutin::event::MouseButton::Left => Some(egui::PointerButton::Primary),
glutin::event::MouseButton::Right => Some(egui::PointerButton::Secondary),
glutin::event::MouseButton::Middle => Some(egui::PointerButton::Middle),
_ => None,
}
}
pub fn translate_virtual_key_code(key: VirtualKeyCode) -> Option<egui::Key> {
use VirtualKeyCode::*;
Some(match key {
Down => Key::ArrowDown,
Left => Key::ArrowLeft,
Right => Key::ArrowRight,
Up => Key::ArrowUp,
Escape => Key::Escape,
Tab => Key::Tab,
Back => Key::Backspace,
Return => Key::Enter,
Space => Key::Space,
Insert => Key::Insert,
Delete => Key::Delete,
Home => Key::Home,
End => Key::End,
PageUp => Key::PageUp,
PageDown => Key::PageDown,
Key0 | Numpad0 => Key::Num0,
Key1 | Numpad1 => Key::Num1,
Key2 | Numpad2 => Key::Num2,
Key3 | Numpad3 => Key::Num3,
Key4 | Numpad4 => Key::Num4,
Key5 | Numpad5 => Key::Num5,
Key6 | Numpad6 => Key::Num6,
Key7 | Numpad7 => Key::Num7,
Key8 | Numpad8 => Key::Num8,
Key9 | Numpad9 => Key::Num9,
A => Key::A,
B => Key::B,
C => Key::C,
D => Key::D,
E => Key::E,
F => Key::F,
G => Key::G,
H => Key::H,
I => Key::I,
J => Key::J,
K => Key::K,
L => Key::L,
M => Key::M,
N => Key::N,
O => Key::O,
P => Key::P,
Q => Key::Q,
R => Key::R,
S => Key::S,
T => Key::T,
U => Key::U,
V => Key::V,
W => Key::W,
X => Key::X,
Y => Key::Y,
Z => Key::Z,
_ => {
return None;
}
})
}
fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option<glutin::window::CursorIcon> {
match cursor_icon {
CursorIcon::None => None,
CursorIcon::Alias => Some(glutin::window::CursorIcon::Alias),
CursorIcon::AllScroll => Some(glutin::window::CursorIcon::AllScroll),
CursorIcon::Cell => Some(glutin::window::CursorIcon::Cell),
CursorIcon::ContextMenu => Some(glutin::window::CursorIcon::ContextMenu),
CursorIcon::Copy => Some(glutin::window::CursorIcon::Copy),
CursorIcon::Crosshair => Some(glutin::window::CursorIcon::Crosshair),
CursorIcon::Default => Some(glutin::window::CursorIcon::Default),
CursorIcon::Grab => Some(glutin::window::CursorIcon::Grab),
CursorIcon::Grabbing => Some(glutin::window::CursorIcon::Grabbing),
CursorIcon::Help => Some(glutin::window::CursorIcon::Help),
CursorIcon::Move => Some(glutin::window::CursorIcon::Move),
CursorIcon::NoDrop => Some(glutin::window::CursorIcon::NoDrop),
CursorIcon::NotAllowed => Some(glutin::window::CursorIcon::NotAllowed),
CursorIcon::PointingHand => Some(glutin::window::CursorIcon::Hand),
CursorIcon::Progress => Some(glutin::window::CursorIcon::Progress),
CursorIcon::ResizeHorizontal => Some(glutin::window::CursorIcon::EwResize),
CursorIcon::ResizeNeSw => Some(glutin::window::CursorIcon::NeswResize),
CursorIcon::ResizeNwSe => Some(glutin::window::CursorIcon::NwseResize),
CursorIcon::ResizeVertical => Some(glutin::window::CursorIcon::NsResize),
CursorIcon::Text => Some(glutin::window::CursorIcon::Text),
CursorIcon::VerticalText => Some(glutin::window::CursorIcon::VerticalText),
CursorIcon::Wait => Some(glutin::window::CursorIcon::Wait),
CursorIcon::ZoomIn => Some(glutin::window::CursorIcon::ZoomIn),
CursorIcon::ZoomOut => Some(glutin::window::CursorIcon::ZoomOut),
}
}
fn set_cursor_icon(display: &glium::backend::glutin::Display, cursor_icon: egui::CursorIcon) {
if let Some(cursor_icon) = translate_cursor(cursor_icon) {
display.gl_window().window().set_cursor_visible(true);
display.gl_window().window().set_cursor_icon(cursor_icon);
} else {
display.gl_window().window().set_cursor_visible(false);
}
}
pub fn handle_output(
output: egui::Output,
clipboard: Option<&mut ClipboardContext>,
display: &glium::Display,
) {
if let Some(open) = output.open_url {
if let Err(err) = webbrowser::open(&open.url) {
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);
}
}
}
if let Some(egui::Pos2 { x, y }) = output.text_cursor_pos {
display
.gl_window()
.window()
.set_ime_position(glium::glutin::dpi::LogicalPosition { x, y })
}
}
pub fn init_clipboard() -> Option<ClipboardContext> {
match ClipboardContext::new() {
Ok(clipboard) => Some(clipboard),
Err(err) => {
eprintln!("Failed to initialize clipboard: {}", err);
None
}
}
}
use glium::glutin;
// ----------------------------------------------------------------------------
@@ -514,9 +106,9 @@ pub fn seconds_since_midnight() -> Option<f64> {
None
}
pub fn screen_size_in_pixels(display: &glium::Display) -> Vec2 {
pub fn screen_size_in_pixels(display: &glium::Display) -> egui::Vec2 {
let (width_in_pixels, height_in_pixels) = display.get_framebuffer_dimensions();
vec2(width_in_pixels as f32, height_in_pixels as f32)
egui::vec2(width_in_pixels as f32, height_in_pixels as f32)
}
pub fn native_pixels_per_point(display: &glium::Display) -> f32 {
@@ -528,26 +120,16 @@ pub fn native_pixels_per_point(display: &glium::Display) -> f32 {
/// Use [`egui`] from a [`glium`] app.
pub struct EguiGlium {
egui_ctx: egui::CtxRef,
start_time: std::time::Instant,
clipboard: Option<crate::ClipboardContext>,
input_state: crate::GliumInputState,
egui_winit: egui_winit::State,
painter: crate::Painter,
current_cursor_icon: egui::CursorIcon,
screen_reader: crate::screen_reader::ScreenReader,
}
impl EguiGlium {
pub fn new(display: &glium::Display) -> Self {
Self {
egui_ctx: Default::default(),
start_time: std::time::Instant::now(),
clipboard: crate::init_clipboard(),
input_state: crate::GliumInputState::from_pixels_per_point(
crate::native_pixels_per_point(display),
),
egui_winit: egui_winit::State::new(display.gl_window().window()),
painter: crate::Painter::new(display),
current_cursor_icon: egui::CursorIcon::Default,
screen_reader: crate::screen_reader::ScreenReader::default(),
}
}
@@ -564,24 +146,26 @@ impl EguiGlium {
}
pub fn pixels_per_point(&self) -> f32 {
self.input_state
.raw
.pixels_per_point
.unwrap_or_else(|| self.egui_ctx.pixels_per_point())
self.egui_winit.pixels_per_point()
}
pub fn on_event(&mut self, event: &glium::glutin::event::WindowEvent<'_>) {
crate::input_to_egui(
self.egui_ctx.pixels_per_point(),
event,
self.clipboard.as_mut(),
&mut self.input_state,
);
pub fn egui_input(&self) -> &egui::RawInput {
self.egui_winit.egui_input()
}
/// Returns `true` if egui wants exclusive use of this event
/// (e.g. a mouse click on an egui window, or entering text into a text field).
/// For instance, if you use egui for a game, you want to first call this
/// and only when this returns `false` pass on the events to your game.
///
/// Note that egui uses `tab` to move focus between elements, so this will always return `true` for tabs.
pub fn on_event(&mut self, event: &glium::glutin::event::WindowEvent<'_>) -> bool {
self.egui_winit.on_event(&self.egui_ctx, event)
}
/// Is this a close event or a Cmd-Q/Alt-F4 keyboard command?
pub fn is_quit_event(&self, event: &glutin::event::WindowEvent<'_>) -> bool {
crate::is_quit_event(&self.input_state, event)
self.egui_winit.is_quit_event(event)
}
pub fn begin_frame(&mut self, display: &glium::Display) {
@@ -589,30 +173,14 @@ impl EguiGlium {
self.begin_frame_with_input(raw_input);
}
pub fn begin_frame_with_input(&mut self, raw_input: RawInput) {
pub fn begin_frame_with_input(&mut self, raw_input: egui::RawInput) {
self.egui_ctx.begin_frame(raw_input);
}
/// Prepare for a new frame. Normally you would call [`Self::begin_frame`] instead.
pub fn take_raw_input(&mut self, display: &glium::Display) -> egui::RawInput {
let pixels_per_point = self.pixels_per_point();
self.input_state.raw.time = Some(self.start_time.elapsed().as_secs_f64());
// On Windows, a minimized window will have 0 width and height.
// See: https://github.com/rust-windowing/winit/issues/208
// This solves an issue where egui window positions would be changed when minimizing on Windows.
let screen_size = screen_size_in_pixels(display);
self.input_state.raw.screen_rect = if screen_size.x > 0.0 && screen_size.y > 0.0 {
Some(Rect::from_min_size(
Default::default(),
screen_size / pixels_per_point,
))
} else {
None
};
self.input_state.raw.take()
self.egui_winit
.take_egui_input(display.gl_window().window())
}
/// Returns `needs_repaint` and shapes to draw.
@@ -626,19 +194,9 @@ impl EguiGlium {
(needs_repaint, shapes)
}
pub fn handle_output(&mut self, display: &glium::Display, egui_output: egui::Output) {
if self.egui_ctx.memory().options.screen_reader {
self.screen_reader.speak(&egui_output.events_description());
}
if self.current_cursor_icon != egui_output.cursor_icon {
// call only when changed to prevent flickering near frame boundary
// when Windows OS tries to control cursor icon for window resizing
set_cursor_icon(display, egui_output.cursor_icon);
self.current_cursor_icon = egui_output.cursor_icon;
}
handle_output(egui_output, self.clipboard.as_mut(), display);
pub fn handle_output(&mut self, display: &glium::Display, output: egui::Output) {
self.egui_winit
.handle_output(display.gl_window().window(), &self.egui_ctx, output);
}
pub fn paint<T: glium::Surface>(

View File

@@ -1,4 +1,5 @@
#![allow(deprecated)] // legacy implement_vertex macro
#![allow(semicolon_in_expressions_from_macros)] // glium::program! macro
use {
egui::{

View File

@@ -81,6 +81,9 @@ pub fn read_memory(ctx: &egui::Context, memory_file_path: impl AsRef<std::path::
}
/// Alternative to `FileStorage`
///
/// # Errors
/// When failing to serialize or create the file.
pub fn write_memory(
ctx: &egui::Context,
memory_file_path: impl AsRef<std::path::Path>,

View File

@@ -1,47 +0,0 @@
pub struct ScreenReader {
#[cfg(feature = "screen_reader")]
tts: Option<tts::Tts>,
}
#[cfg(not(feature = "screen_reader"))]
impl Default for ScreenReader {
fn default() -> Self {
Self {}
}
}
#[cfg(feature = "screen_reader")]
impl Default for ScreenReader {
fn default() -> Self {
let tts = match tts::Tts::default() {
Ok(screen_reader) => {
eprintln!("Initialized screen reader.");
Some(screen_reader)
}
Err(err) => {
eprintln!("Failed to load screen reader: {}", err);
None
}
};
Self { tts }
}
}
impl ScreenReader {
#[cfg(not(feature = "screen_reader"))]
pub fn speak(&mut self, _text: &str) {}
#[cfg(feature = "screen_reader")]
pub fn speak(&mut self, text: &str) {
if text.is_empty() {
return;
}
if let Some(tts) = &mut self.tts {
eprintln!("Speaking: {:?}", text);
let interrupt = true;
if let Err(err) = tts.speak(text, interrupt) {
eprintln!("Failed to read: {}", err);
}
}
}
}

View File

@@ -1,5 +1,6 @@
use glium::glutin;
use egui_winit::winit;
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct WindowSettings {
/// outer position of window in physical pixels
@@ -37,47 +38,31 @@ impl WindowSettings {
}
}
pub fn initialize_size(
pub fn initialize_window(
&self,
window: glutin::window::WindowBuilder,
) -> glutin::window::WindowBuilder {
mut window: winit::window::WindowBuilder,
) -> winit::window::WindowBuilder {
if !cfg!(target_os = "windows") {
// If the app last ran on two monitors and only one is now connected, then
// the given position is invalid.
// If this happens on Mac, the window is clamped into valid area.
// If this happens on Windows, the window is hidden and impossible to bring to get at.
// So we don't restore window positions on Windows.
if let Some(pos) = self.pos {
window = window.with_position(winit::dpi::PhysicalPosition {
x: pos.x as f64,
y: pos.y as f64,
});
}
}
if let Some(inner_size_points) = self.inner_size_points {
window.with_inner_size(glutin::dpi::LogicalSize {
window.with_inner_size(winit::dpi::LogicalSize {
width: inner_size_points.x as f64,
height: inner_size_points.y as f64,
})
} else {
window
}
// Not yet available in winit: https://github.com/rust-windowing/winit/issues/1190
// if let Some(pos) = self.pos {
// *window = window.with_outer_pos(glutin::dpi::PhysicalPosition {
// x: pos.x as f64,
// y: pos.y as f64,
// });
// }
}
pub fn restore_positions(&self, display: &glium::Display) {
// not needed, done by `initialize_size`
// let size = self.size.unwrap_or_else(|| vec2(1024.0, 800.0));
// display
// .gl_window()
// .window()
// .set_inner_size(glutin::dpi::PhysicalSize {
// width: size.x as f64,
// height: size.y as f64,
// });
if let Some(pos) = self.pos {
display
.gl_window()
.window()
.set_outer_position(glutin::dpi::PhysicalPosition::new(
pos.x as f64,
pos.y as f64,
));
}
}
}