From b9b415ac42009bc603604421d3fa7ee36c11e24c Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 28 Sep 2021 17:33:28 +0200 Subject: [PATCH] Split out new crate egui-winit from egui_glium (#735) --- .github/workflows/rust.yml | 2 +- ARCHITECTURE.md | 8 +- CHANGELOG.md | 2 +- Cargo.lock | 16 +- Cargo.toml | 1 + README.md | 2 +- eframe/Cargo.toml | 12 +- eframe/src/lib.rs | 2 +- egui-winit/CHANGELOG.md | 7 + egui-winit/Cargo.toml | 45 ++ egui-winit/README.md | 11 + egui-winit/src/clipboard.rs | 69 ++ egui-winit/src/lib.rs | 675 ++++++++++++++++++ .../src/screen_reader.rs | 1 + egui/Cargo.toml | 3 +- egui/src/data/input.rs | 4 +- egui_glium/CHANGELOG.md | 2 + egui_glium/Cargo.toml | 25 +- egui_glium/README.md | 2 + egui_glium/src/backend.rs | 27 +- egui_glium/src/lib.rs | 628 +++------------- egui_glium/src/painter.rs | 1 + egui_glium/src/persistence.rs | 3 + egui_glium/src/window_settings.rs | 55 +- epaint/Cargo.toml | 3 +- sh/check.sh | 11 +- sh/docs.sh | 2 +- 27 files changed, 1003 insertions(+), 616 deletions(-) create mode 100644 egui-winit/CHANGELOG.md create mode 100644 egui-winit/Cargo.toml create mode 100644 egui-winit/README.md create mode 100644 egui-winit/src/clipboard.rs create mode 100644 egui-winit/src/lib.rs rename {egui_glium => egui-winit}/src/screen_reader.rs (97%) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9f4747a7d47b..56bb1ad77a50 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -132,7 +132,7 @@ jobs: toolchain: 1.54.0 override: true - run: sudo apt-get install libspeechd-dev - - run: cargo doc -p emath -p epaint -p egui -p eframe -p epi -p egui_web -p egui_glium --lib --no-deps --all-features + - run: cargo doc -p emath -p epaint -p egui -p eframe -p epi -p egui_web -p egui-winit -p egui_glium --lib --no-deps --all-features doc_web: name: cargo doc web diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 8473ca2e7658..f9b8b0cc18ed 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -5,7 +5,7 @@ Also see [`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUT ## Crate overview -The crates in this repository are: `egui, emath, epaint, egui, epi, egui_web, egui_glium, egui_demo_lib, egui_demo_app`. +The crates in this repository are: `egui, emath, epaint, egui, epi, egui-winit, egui_web, egui_glium, egui_demo_lib, egui_demo_app`. ### `egui`: The main GUI library. Example code: `if ui.button("Click me").clicked() { … }` @@ -25,6 +25,11 @@ Depends on `emath`, [`ab_glyph`](https://crates.io/crates/ab_glyph), [`atomic_re Depends only on `egui`. Adds a thin application level wrapper around `egui` for hosting an `egui` app inside of `eframe`. +### `egui-winit` +This crates provides bindings between [`egui`](https://github.com/emilk/egui) and [winit](https://crates.io/crates/winit). + +The library translates winit events to egui, handled copy/paste, updates the cursor, open links clicked in egui, etc. + ### `egui_web` Puts an egui app inside the web browser by compiling to WASM and binding to the web browser with [`js-sys`](https://crates.io/crates/js-sys) and [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen). Paints the triangles that egui outputs using WebGL. @@ -40,7 +45,6 @@ The demo that you can see at is using Depends on `egui` + `epi`. This contains a bunch of uses of `egui` and looks like the ui code you would write for an `egui` app. - ### `egui_demo_app` Thin wrapper around `egui_demo_lib` so we can compile it to a web site or a native app executable. Depends on `egui_demo_lib` + `eframe`. diff --git a/CHANGELOG.md b/CHANGELOG.md index ed0f604cecfe..513aa39a847e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to the egui crate will be documented in this file. -NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md) and [`egui_glium`](egui_glium/CHANGELOG.md) have their own changelogs! +NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md), [`egui-winit`](egui-winit/CHANGELOG.md) and [`egui_glium`](egui_glium/CHANGELOG.md) have their own changelogs! ## Unreleased diff --git a/Cargo.lock b/Cargo.lock index 3fdb63e89cc8..eee3d4685bea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -799,6 +799,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "egui-winit" +version = "0.14.0" +dependencies = [ + "copypasta", + "egui", + "epi", + "tts", + "webbrowser", + "winit", +] + [[package]] name = "egui_demo_app" version = "0.14.0" @@ -826,16 +838,14 @@ name = "egui_glium" version = "0.14.0" dependencies = [ "chrono", - "copypasta", "directories-next", "egui", + "egui-winit", "epi", "glium", "image", "ron", "serde", - "tts", - "webbrowser", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 09f6cb92d537..6cb7a0944394 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "egui_demo_lib", "egui_glium", "egui_web", + "egui-winit", "egui", "emath", "epaint", diff --git a/README.md b/README.md index 9b64d1fea164..b8074fbdfa40 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ I maintain two official egui integrations made for apps: * [`egui_web`](https://github.com/emilk/egui/tree/master/egui_web) for making a web app. Compiles to WASM, renders with WebGL. [Click to run the egui demo](https://emilk.github.io/egui/index.html). * [`egui_glium`](https://github.com/emilk/egui/tree/master/egui_glium) for compiling native apps with [Glium](https://github.com/glium/glium). +* [`egui-winit`](https://github.com/emilk/egui/tree/master/egui-winit) for integrating with [`winit`](https://github.com/rust-windowing/winit). `egui-winit` is used by `egui_glium`. If you making an app, consider using [`eframe`](https://github.com/emilk/egui/tree/master/eframe), a framework which allows you to write code that works on both the web (`egui_web`) and native (using `egui_glium`). @@ -180,7 +181,6 @@ If you making an app, consider using [`eframe`](https://github.com/emilk/egui/tr * [`egui_sdl2_gl`](https://crates.io/crates/egui_sdl2_gl) for [SDL2](https://crates.io/crates/sdl2). * [`egui_vulkano`](https://github.com/derivator/egui_vulkano) for [Vulkano](https://github.com/vulkano-rs/vulkano). * [`egui-winit-ash-integration`](https://github.com/MatchaChoco010/egui-winit-ash-integration) for [winit](https://github.com/rust-windowing/winit) and [ash](https://github.com/MaikKlein/ash). -* [`egui_winit_platform`](https://github.com/hasenbanck/egui_winit_platform) for [winit](https://crates.io/crates/winit) (requires separate painter). * [`egui_winit_vulkano`](https://github.com/hakolao/egui_winit_vulkano) for [Vulkano](https://github.com/vulkano-rs/vulkano). * [`fltk-egui`](https://crates.io/crates/fltk-egui) for [fltk-rs](https://github.com/fltk-rs/fltk-rs). * [`ggez-egui`](https://github.com/NemuiSen/ggez-egui) for the [ggez](https://ggez.rs/) game framework. diff --git a/eframe/Cargo.toml b/eframe/Cargo.toml index 673bd35606d3..9f9b7fae646d 100644 --- a/eframe/Cargo.toml +++ b/eframe/Cargo.toml @@ -28,7 +28,7 @@ epi = { version = "0.14.0", path = "../epi" } # For compiling natively: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -egui_glium = { version = "0.14.0", path = "../egui_glium", default-features = false } +egui_glium = { version = "0.14.0", path = "../egui_glium", default-features = false, features = ["clipboard", "links"] } # For compiling to web: [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -43,6 +43,12 @@ default = ["default_fonts"] # If set, egui will use `include_bytes!` to bundle some fonts. # If you plan on specifying your own fonts you may disable this feature. default_fonts = ["egui/default_fonts"] + +# Enable saving app state to disk. persistence = ["epi/persistence", "egui_glium/persistence", "egui_web/persistence"] -screen_reader = ["egui_glium/screen_reader", "egui_web/screen_reader"] # experimental -time = ["egui_glium/time"] # for seconds_since_midnight + +# experimental support for a screen reader +screen_reader = ["egui_glium/screen_reader", "egui_web/screen_reader"] + +# for seconds_since_midnight (used in egui_demo_lib) +time = ["egui_glium/time"] diff --git a/eframe/src/lib.rs b/eframe/src/lib.rs index 51daeaf5980a..dd37d890ff88 100644 --- a/eframe/src/lib.rs +++ b/eframe/src/lib.rs @@ -62,5 +62,5 @@ pub fn start_web(canvas_id: &str, app: Box) -> Result<(), wasm_bin /// Call from `fn main` like this: `eframe::run_native(Box::new(MyEguiApp::default()))` #[cfg(not(target_arch = "wasm32"))] pub fn run_native(app: Box, native_options: epi::NativeOptions) { - egui_glium::run(app, native_options) + egui_glium::run(app, &native_options) } diff --git a/egui-winit/CHANGELOG.md b/egui-winit/CHANGELOG.md new file mode 100644 index 000000000000..5e5f53557a8c --- /dev/null +++ b/egui-winit/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog for egui-winit + +All notable changes to the `egui-winit` integration will be noted in this file. + + +## Unreleased +First stand-alone release. Previously part of `egui_glium`. diff --git a/egui-winit/Cargo.toml b/egui-winit/Cargo.toml new file mode 100644 index 000000000000..83b1f4e26412 --- /dev/null +++ b/egui-winit/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "egui-winit" +version = "0.14.0" +authors = ["Emil Ernerfeldt "] +description = "Bindings for using egui with winit" +edition = "2018" +homepage = "https://github.com/emilk/egui/tree/master/egui-winit" +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/emilk/egui/tree/master/egui-winit" +categories = ["gui", "game-development"] +keywords = ["winit", "egui", "gui", "gamedev"] +include = [ + "../LICENSE-APACHE", + "../LICENSE-MIT", + "**/*.rs", + "Cargo.toml", +] + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +egui = { version = "0.14.0", path = "../egui", default-features = false } +epi = { version = "0.14.0", path = "../epi" } +winit = "0.25" + +copypasta = { version = "0.7", optional = true } +webbrowser = { version = "0.5", optional = true } + +# feature screen_reader +tts = { version = "0.17", optional = true } + +[features] +default = ["clipboard", "links"] + +# enable cut/copy/paste to OS clipboard. +# if disabled a clipboard will be simulated so you can still copy/paste within the egui app. +clipboard = ["copypasta"] + +# enable opening links in a browser when an egui hyperlink is clicked. +links = ["webbrowser"] + +# experimental support for a screen reader +screen_reader = ["tts"] diff --git a/egui-winit/README.md b/egui-winit/README.md new file mode 100644 index 000000000000..07eb28ff9165 --- /dev/null +++ b/egui-winit/README.md @@ -0,0 +1,11 @@ +# egui-winit + +[![Latest version](https://img.shields.io/crates/v/egui-winit.svg)](https://crates.io/crates/egui-winit) +[![Documentation](https://docs.rs/egui-winit/badge.svg)](https://docs.rs/egui-winit) +[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) +![MIT](https://img.shields.io/badge/license-MIT-blue.svg) +![Apache](https://img.shields.io/badge/license-Apache-blue.svg) + +This crates provides bindings between [`egui`](https://github.com/emilk/egui) and [winit](https://crates.io/crates/winit). + +The library translates winit events to egui, handled copy/paste, updates the cursor, open links clicked in egui, etc. diff --git a/egui-winit/src/clipboard.rs b/egui-winit/src/clipboard.rs new file mode 100644 index 000000000000..4a70d47fc942 --- /dev/null +++ b/egui-winit/src/clipboard.rs @@ -0,0 +1,69 @@ +/// Handles interfacing either with the OS clipboard. +/// If the "clipboard" feature is off it will instead simulate the clipboard locally. +pub struct Clipboard { + #[cfg(feature = "copypasta")] + copypasta: Option, + + /// Fallback manual clipboard. + #[cfg(not(feature = "copypasta"))] + clipboard: String, +} + +impl Default for Clipboard { + fn default() -> Self { + Self { + #[cfg(feature = "copypasta")] + copypasta: init_copypasta(), + + #[cfg(not(feature = "copypasta"))] + clipboard: String::default(), + } + } +} + +impl Clipboard { + pub fn get(&mut self) -> Option { + #[cfg(feature = "copypasta")] + if let Some(clipboard) = &mut self.copypasta { + use copypasta::ClipboardProvider as _; + match clipboard.get_contents() { + Ok(contents) => Some(contents), + Err(err) => { + eprintln!("Paste error: {}", err); + None + } + } + } else { + None + } + + #[cfg(not(feature = "copypasta"))] + Some(self.clipboard.clone()) + } + + pub fn set(&mut self, text: String) { + #[cfg(feature = "copypasta")] + if let Some(clipboard) = &mut self.copypasta { + use copypasta::ClipboardProvider as _; + if let Err(err) = clipboard.set_contents(text) { + eprintln!("Copy/Cut error: {}", err); + } + } + + #[cfg(not(feature = "copypasta"))] + { + self.clipboard = text; + } + } +} + +#[cfg(feature = "copypasta")] +fn init_copypasta() -> Option { + match copypasta::ClipboardContext::new() { + Ok(clipboard) => Some(clipboard), + Err(err) => { + eprintln!("Failed to initialize clipboard: {}", err); + None + } + } +} diff --git a/egui-winit/src/lib.rs b/egui-winit/src/lib.rs new file mode 100644 index 000000000000..ed8a006bdaaf --- /dev/null +++ b/egui-winit/src/lib.rs @@ -0,0 +1,675 @@ +//! [`egui`] bindings for [`winit`](https://github.com/rust-windowing/winit). +//! +//! The library translates winit events to egui, handled copy/paste, +//! updates the cursor, open links clicked in egui, etc. + +#![forbid(unsafe_code)] +#![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)] + +pub use winit; + +pub mod clipboard; +pub mod screen_reader; + +pub fn native_pixels_per_point(window: &winit::window::Window) -> f32 { + window.scale_factor() as f32 +} + +pub fn screen_size_in_pixels(window: &winit::window::Window) -> egui::Vec2 { + // let (width_in_pixels, height_in_pixels) = display.get_framebuffer_dimensions(); + // egui::vec2(width_in_pixels as f32, height_in_pixels as f32) + let size = window.inner_size(); + egui::vec2(size.width as f32, size.height as f32) +} + +/// Handles the integration between egui and winit. +pub struct State { + start_time: std::time::Instant, + egui_input: egui::RawInput, + pointer_pos_in_points: Option, + any_pointer_button_down: bool, + current_cursor_icon: egui::CursorIcon, + /// What egui uses. + current_pixels_per_point: f32, + + clipboard: clipboard::Clipboard, + screen_reader: screen_reader::ScreenReader, + + /// If `true`, mouse inputs will be treated as touches. + /// Useful for debugging touch support in egui. + simulate_touch_screen: bool, +} + +impl State { + /// Initialize with the native `pixels_per_point` (dpi scaling). + pub fn new(window: &winit::window::Window) -> Self { + Self::from_pixels_per_point(native_pixels_per_point(window)) + } + + /// Initialize with a given dpi scaling. + pub fn from_pixels_per_point(pixels_per_point: f32) -> Self { + Self { + start_time: std::time::Instant::now(), + egui_input: egui::RawInput { + pixels_per_point: Some(pixels_per_point), + ..Default::default() + }, + pointer_pos_in_points: None, + any_pointer_button_down: false, + current_cursor_icon: egui::CursorIcon::Default, + current_pixels_per_point: pixels_per_point, + + clipboard: Default::default(), + screen_reader: screen_reader::ScreenReader::default(), + + simulate_touch_screen: false, + } + } + + /// The number of physical pixels per logical point, + /// as configured on the current egui context (see [`egui::Context::pixels_per_point`]). + #[inline] + pub fn pixels_per_point(&self) -> f32 { + self.current_pixels_per_point + } + + /// The current input state. + /// This is changed by [`Self::on_event`] and cleared by [`Self::take_egui_input`]. + #[inline] + pub fn egui_input(&self) -> &egui::RawInput { + &self.egui_input + } + + /// Prepare for a new frame by extracting the accumulated input, + /// as well as setting [the time](egui::RawInput::time) and [screen rectangle](egui::RawInput::screen_rect). + pub fn take_egui_input(&mut self, display: &winit::window::Window) -> egui::RawInput { + let pixels_per_point = self.pixels_per_point(); + + self.egui_input.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_in_pixels = screen_size_in_pixels(display); + let screen_size_in_points = screen_size_in_pixels / pixels_per_point; + self.egui_input.screen_rect = + if screen_size_in_points.x > 0.0 && screen_size_in_points.y > 0.0 { + Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + screen_size_in_points, + )) + } else { + None + }; + + self.egui_input.take() + } + + /// Call this when there is a new event. + /// + /// The result can be found in [`Self::egui_input`] and be extracted with [`Self::take_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, + egui_ctx: &egui::Context, + event: &winit::event::WindowEvent<'_>, + ) -> bool { + use winit::event::WindowEvent; + match event { + WindowEvent::ScaleFactorChanged { scale_factor, .. } => { + let pixels_per_point = *scale_factor as f32; + self.egui_input.pixels_per_point = Some(pixels_per_point); + self.current_pixels_per_point = pixels_per_point; + false + } + WindowEvent::MouseInput { state, button, .. } => { + self.on_mouse_button_input(*state, *button); + egui_ctx.wants_pointer_input() + } + WindowEvent::MouseWheel { delta, .. } => { + self.on_mouse_wheel(*delta); + egui_ctx.wants_pointer_input() + } + WindowEvent::CursorMoved { position, .. } => { + self.on_cursor_moved(*position); + egui_ctx.is_using_pointer() + } + WindowEvent::CursorLeft { .. } => { + self.pointer_pos_in_points = None; + self.egui_input.events.push(egui::Event::PointerGone); + false + } + // WindowEvent::TouchpadPressure {device_id, pressure, stage, .. } => {} // TODO + WindowEvent::Touch(touch) => { + self.on_touch(touch); + match touch.phase { + winit::event::TouchPhase::Started + | winit::event::TouchPhase::Ended + | winit::event::TouchPhase::Cancelled => egui_ctx.wants_pointer_input(), + winit::event::TouchPhase::Moved => egui_ctx.is_using_pointer(), + } + } + WindowEvent::ReceivedCharacter(ch) => { + if is_printable_char(*ch) + && !self.egui_input.modifiers.ctrl + && !self.egui_input.modifiers.mac_cmd + { + self.egui_input + .events + .push(egui::Event::Text(ch.to_string())); + egui_ctx.wants_keyboard_input() + } else { + false + } + } + WindowEvent::KeyboardInput { input, .. } => { + self.on_keyboard_input(input); + egui_ctx.wants_keyboard_input() + || input.virtual_keycode == Some(winit::event::VirtualKeyCode::Tab) + } + 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. + self.egui_input.modifiers = egui::Modifiers::default(); + false + } + WindowEvent::HoveredFile(path) => { + self.egui_input.hovered_files.push(egui::HoveredFile { + path: Some(path.clone()), + ..Default::default() + }); + false + } + WindowEvent::HoveredFileCancelled => { + self.egui_input.hovered_files.clear(); + false + } + WindowEvent::DroppedFile(path) => { + self.egui_input.hovered_files.clear(); + self.egui_input.dropped_files.push(egui::DroppedFile { + path: Some(path.clone()), + ..Default::default() + }); + false + } + _ => { + // dbg!(event); + false + } + } + } + + fn on_mouse_button_input( + &mut self, + state: winit::event::ElementState, + button: winit::event::MouseButton, + ) { + if let Some(pos) = self.pointer_pos_in_points { + if let Some(button) = translate_mouse_button(button) { + let pressed = state == winit::event::ElementState::Pressed; + + self.egui_input.events.push(egui::Event::PointerButton { + pos, + button, + pressed, + modifiers: self.egui_input.modifiers, + }); + + if self.simulate_touch_screen { + if pressed { + self.any_pointer_button_down = true; + + self.egui_input.events.push(egui::Event::Touch { + device_id: egui::TouchDeviceId(0), + id: egui::TouchId(0), + phase: egui::TouchPhase::Start, + pos, + force: 0.0, + }); + } else { + self.any_pointer_button_down = false; + + self.egui_input.events.push(egui::Event::PointerGone); + + self.egui_input.events.push(egui::Event::Touch { + device_id: egui::TouchDeviceId(0), + id: egui::TouchId(0), + phase: egui::TouchPhase::End, + pos, + force: 0.0, + }); + }; + } + } + } + } + + fn on_cursor_moved(&mut self, pos_in_pixels: winit::dpi::PhysicalPosition) { + let pos_in_points = egui::pos2( + pos_in_pixels.x as f32 / self.pixels_per_point(), + pos_in_pixels.y as f32 / self.pixels_per_point(), + ); + self.pointer_pos_in_points = Some(pos_in_points); + + if self.simulate_touch_screen { + if self.any_pointer_button_down { + self.egui_input + .events + .push(egui::Event::PointerMoved(pos_in_points)); + + self.egui_input.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 { + self.egui_input + .events + .push(egui::Event::PointerMoved(pos_in_points)); + } + } + + fn on_touch(&mut self, touch: &winit::event::Touch) { + self.egui_input.events.push(egui::Event::Touch { + device_id: egui::TouchDeviceId(egui::epaint::util::hash(touch.device_id)), + id: egui::TouchId::from(touch.id), + phase: match touch.phase { + winit::event::TouchPhase::Started => egui::TouchPhase::Start, + winit::event::TouchPhase::Moved => egui::TouchPhase::Move, + winit::event::TouchPhase::Ended => egui::TouchPhase::End, + winit::event::TouchPhase::Cancelled => egui::TouchPhase::Cancel, + }, + pos: egui::pos2( + touch.location.x as f32 / self.pixels_per_point(), + touch.location.y as f32 / self.pixels_per_point(), + ), + force: match touch.force { + Some(winit::event::Force::Normalized(force)) => force as f32, + Some(winit::event::Force::Calibrated { + force, + max_possible_force, + .. + }) => (force / max_possible_force) as f32, + None => 0_f32, + }, + }); + } + + fn on_mouse_wheel(&mut self, delta: winit::event::MouseScrollDelta) { + let mut delta = match delta { + winit::event::MouseScrollDelta::LineDelta(x, y) => { + let points_per_scroll_line = 50.0; // Scroll speed decided by consensus: https://github.com/emilk/egui/issues/461 + egui::vec2(x, y) * points_per_scroll_line + } + winit::event::MouseScrollDelta::PixelDelta(delta) => { + egui::vec2(delta.x as f32, delta.y as f32) / self.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 self.egui_input.modifiers.ctrl || self.egui_input.modifiers.command { + // Treat as zoom instead: + self.egui_input.zoom_delta *= (delta.y / 200.0).exp(); + } else { + self.egui_input.scroll_delta += delta; + } + } + + fn on_keyboard_input(&mut self, input: &winit::event::KeyboardInput) { + if let Some(keycode) = input.virtual_keycode { + use winit::event::VirtualKeyCode; + + let pressed = input.state == winit::event::ElementState::Pressed; + + // We could also use `WindowEvent::ModifiersChanged` instead, I guess. + if matches!(keycode, VirtualKeyCode::LAlt | VirtualKeyCode::RAlt) { + self.egui_input.modifiers.alt = pressed; + } + if matches!(keycode, VirtualKeyCode::LControl | VirtualKeyCode::RControl) { + self.egui_input.modifiers.ctrl = pressed; + if !cfg!(target_os = "macos") { + self.egui_input.modifiers.command = pressed; + } + } + if matches!(keycode, VirtualKeyCode::LShift | VirtualKeyCode::RShift) { + self.egui_input.modifiers.shift = pressed; + } + if cfg!(target_os = "macos") + && matches!(keycode, VirtualKeyCode::LWin | VirtualKeyCode::RWin) + { + self.egui_input.modifiers.mac_cmd = pressed; + self.egui_input.modifiers.command = pressed; + } + + if pressed { + // VirtualKeyCode::Paste etc in winit are broken/untrustworthy, + // so we detect these things manually: + if is_cut_command(self.egui_input.modifiers, keycode) { + self.egui_input.events.push(egui::Event::Cut); + } else if is_copy_command(self.egui_input.modifiers, keycode) { + self.egui_input.events.push(egui::Event::Copy); + } else if is_paste_command(self.egui_input.modifiers, keycode) { + if let Some(contents) = self.clipboard.get() { + self.egui_input.events.push(egui::Event::Text(contents)); + } + } + } + + if let Some(key) = translate_virtual_key_code(keycode) { + self.egui_input.events.push(egui::Event::Key { + key, + pressed, + modifiers: self.egui_input.modifiers, + }); + } + } + } + + /// Call with the output given by `egui`. + /// + /// This will, if needed: + /// * update the cursor + /// * copy text to the clipboard + /// * open any clicked urls + /// * update the IME + /// * + pub fn handle_output( + &mut self, + window: &winit::window::Window, + egui_ctx: &egui::Context, + output: egui::Output, + ) { + self.current_pixels_per_point = egui_ctx.pixels_per_point(); // someone can have changed it to scale the UI + + if egui_ctx.memory().options.screen_reader { + self.screen_reader.speak(&output.events_description()); + } + + self.set_cursor_icon(window, output.cursor_icon); + + if let Some(open) = output.open_url { + open_url(&open.url); + } + + if !output.copied_text.is_empty() { + self.clipboard.set(output.copied_text); + } + + if let Some(egui::Pos2 { x, y }) = output.text_cursor_pos { + window.set_ime_position(winit::dpi::LogicalPosition { x, y }) + } + } + + /// Returns `true` if Alt-F4 (windows/linux) or Cmd-Q (Mac) + pub fn is_quit_shortcut(&self, input: &winit::event::KeyboardInput) -> bool { + if cfg!(target_os = "macos") { + input.state == winit::event::ElementState::Pressed + && self.egui_input.modifiers.mac_cmd + && input.virtual_keycode == Some(winit::event::VirtualKeyCode::Q) + } else { + input.state == winit::event::ElementState::Pressed + && self.egui_input.modifiers.alt + && input.virtual_keycode == Some(winit::event::VirtualKeyCode::F4) + } + } + + /// Returns `true` if this a close event or a Cmd-Q/Alt-F4 keyboard command. + pub fn is_quit_event(&self, event: &winit::event::WindowEvent<'_>) -> bool { + use winit::event::WindowEvent; + match event { + WindowEvent::CloseRequested | WindowEvent::Destroyed => true, + WindowEvent::KeyboardInput { input, .. } => self.is_quit_shortcut(input), + _ => false, + } + } + + fn set_cursor_icon(&mut self, window: &winit::window::Window, cursor_icon: egui::CursorIcon) { + // prevent flickering near frame boundary when Windows OS tries to control cursor icon for window resizing + if self.current_cursor_icon == cursor_icon { + return; + } + self.current_cursor_icon = cursor_icon; + + if let Some(cursor_icon) = translate_cursor(cursor_icon) { + window.set_cursor_visible(true); + + let is_pointer_in_window = self.pointer_pos_in_points.is_some(); + if is_pointer_in_window { + window.set_cursor_icon(cursor_icon); + } + } else { + window.set_cursor_visible(false); + } + } +} + +fn open_url(_url: &str) { + #[cfg(feature = "webbrowser")] + if let Err(err) = webbrowser::open(_url) { + eprintln!("Failed to open url: {}", err); + } + + #[cfg(not(feature = "webbrowser"))] + { + eprintln!("Cannot open url - feature \"links\" not enabled."); + } +} + +/// 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: winit::event::VirtualKeyCode) -> bool { + (modifiers.command && keycode == winit::event::VirtualKeyCode::X) + || (cfg!(target_os = "windows") + && modifiers.shift + && keycode == winit::event::VirtualKeyCode::Delete) +} + +fn is_copy_command(modifiers: egui::Modifiers, keycode: winit::event::VirtualKeyCode) -> bool { + (modifiers.command && keycode == winit::event::VirtualKeyCode::C) + || (cfg!(target_os = "windows") + && modifiers.ctrl + && keycode == winit::event::VirtualKeyCode::Insert) +} + +fn is_paste_command(modifiers: egui::Modifiers, keycode: winit::event::VirtualKeyCode) -> bool { + (modifiers.command && keycode == winit::event::VirtualKeyCode::V) + || (cfg!(target_os = "windows") + && modifiers.shift + && keycode == winit::event::VirtualKeyCode::Insert) +} + +fn translate_mouse_button(button: winit::event::MouseButton) -> Option { + match button { + winit::event::MouseButton::Left => Some(egui::PointerButton::Primary), + winit::event::MouseButton::Right => Some(egui::PointerButton::Secondary), + winit::event::MouseButton::Middle => Some(egui::PointerButton::Middle), + winit::event::MouseButton::Other(_) => None, + } +} + +fn translate_virtual_key_code(key: winit::event::VirtualKeyCode) -> Option { + use egui::Key; + use winit::event::VirtualKeyCode; + + Some(match key { + VirtualKeyCode::Down => Key::ArrowDown, + VirtualKeyCode::Left => Key::ArrowLeft, + VirtualKeyCode::Right => Key::ArrowRight, + VirtualKeyCode::Up => Key::ArrowUp, + + VirtualKeyCode::Escape => Key::Escape, + VirtualKeyCode::Tab => Key::Tab, + VirtualKeyCode::Back => Key::Backspace, + VirtualKeyCode::Return => Key::Enter, + VirtualKeyCode::Space => Key::Space, + + VirtualKeyCode::Insert => Key::Insert, + VirtualKeyCode::Delete => Key::Delete, + VirtualKeyCode::Home => Key::Home, + VirtualKeyCode::End => Key::End, + VirtualKeyCode::PageUp => Key::PageUp, + VirtualKeyCode::PageDown => Key::PageDown, + + VirtualKeyCode::Key0 | VirtualKeyCode::Numpad0 => Key::Num0, + VirtualKeyCode::Key1 | VirtualKeyCode::Numpad1 => Key::Num1, + VirtualKeyCode::Key2 | VirtualKeyCode::Numpad2 => Key::Num2, + VirtualKeyCode::Key3 | VirtualKeyCode::Numpad3 => Key::Num3, + VirtualKeyCode::Key4 | VirtualKeyCode::Numpad4 => Key::Num4, + VirtualKeyCode::Key5 | VirtualKeyCode::Numpad5 => Key::Num5, + VirtualKeyCode::Key6 | VirtualKeyCode::Numpad6 => Key::Num6, + VirtualKeyCode::Key7 | VirtualKeyCode::Numpad7 => Key::Num7, + VirtualKeyCode::Key8 | VirtualKeyCode::Numpad8 => Key::Num8, + VirtualKeyCode::Key9 | VirtualKeyCode::Numpad9 => Key::Num9, + + VirtualKeyCode::A => Key::A, + VirtualKeyCode::B => Key::B, + VirtualKeyCode::C => Key::C, + VirtualKeyCode::D => Key::D, + VirtualKeyCode::E => Key::E, + VirtualKeyCode::F => Key::F, + VirtualKeyCode::G => Key::G, + VirtualKeyCode::H => Key::H, + VirtualKeyCode::I => Key::I, + VirtualKeyCode::J => Key::J, + VirtualKeyCode::K => Key::K, + VirtualKeyCode::L => Key::L, + VirtualKeyCode::M => Key::M, + VirtualKeyCode::N => Key::N, + VirtualKeyCode::O => Key::O, + VirtualKeyCode::P => Key::P, + VirtualKeyCode::Q => Key::Q, + VirtualKeyCode::R => Key::R, + VirtualKeyCode::S => Key::S, + VirtualKeyCode::T => Key::T, + VirtualKeyCode::U => Key::U, + VirtualKeyCode::V => Key::V, + VirtualKeyCode::W => Key::W, + VirtualKeyCode::X => Key::X, + VirtualKeyCode::Y => Key::Y, + VirtualKeyCode::Z => Key::Z, + + _ => { + return None; + } + }) +} + +fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option { + match cursor_icon { + egui::CursorIcon::None => None, + + egui::CursorIcon::Alias => Some(winit::window::CursorIcon::Alias), + egui::CursorIcon::AllScroll => Some(winit::window::CursorIcon::AllScroll), + egui::CursorIcon::Cell => Some(winit::window::CursorIcon::Cell), + egui::CursorIcon::ContextMenu => Some(winit::window::CursorIcon::ContextMenu), + egui::CursorIcon::Copy => Some(winit::window::CursorIcon::Copy), + egui::CursorIcon::Crosshair => Some(winit::window::CursorIcon::Crosshair), + egui::CursorIcon::Default => Some(winit::window::CursorIcon::Default), + egui::CursorIcon::Grab => Some(winit::window::CursorIcon::Grab), + egui::CursorIcon::Grabbing => Some(winit::window::CursorIcon::Grabbing), + egui::CursorIcon::Help => Some(winit::window::CursorIcon::Help), + egui::CursorIcon::Move => Some(winit::window::CursorIcon::Move), + egui::CursorIcon::NoDrop => Some(winit::window::CursorIcon::NoDrop), + egui::CursorIcon::NotAllowed => Some(winit::window::CursorIcon::NotAllowed), + egui::CursorIcon::PointingHand => Some(winit::window::CursorIcon::Hand), + egui::CursorIcon::Progress => Some(winit::window::CursorIcon::Progress), + egui::CursorIcon::ResizeHorizontal => Some(winit::window::CursorIcon::EwResize), + egui::CursorIcon::ResizeNeSw => Some(winit::window::CursorIcon::NeswResize), + egui::CursorIcon::ResizeNwSe => Some(winit::window::CursorIcon::NwseResize), + egui::CursorIcon::ResizeVertical => Some(winit::window::CursorIcon::NsResize), + egui::CursorIcon::Text => Some(winit::window::CursorIcon::Text), + egui::CursorIcon::VerticalText => Some(winit::window::CursorIcon::VerticalText), + egui::CursorIcon::Wait => Some(winit::window::CursorIcon::Wait), + egui::CursorIcon::ZoomIn => Some(winit::window::CursorIcon::ZoomIn), + egui::CursorIcon::ZoomOut => Some(winit::window::CursorIcon::ZoomOut), + } +} diff --git a/egui_glium/src/screen_reader.rs b/egui-winit/src/screen_reader.rs similarity index 97% rename from egui_glium/src/screen_reader.rs rename to egui-winit/src/screen_reader.rs index a27c964b6f98..9397d2ea8b11 100644 --- a/egui_glium/src/screen_reader.rs +++ b/egui-winit/src/screen_reader.rs @@ -29,6 +29,7 @@ impl Default for ScreenReader { impl ScreenReader { #[cfg(not(feature = "screen_reader"))] + #[allow(clippy::unused_self)] pub fn speak(&mut self, _text: &str) {} #[cfg(feature = "screen_reader")] diff --git a/egui/Cargo.toml b/egui/Cargo.toml index cfcda442e1fa..d1582acf81cb 100644 --- a/egui/Cargo.toml +++ b/egui/Cargo.toml @@ -47,7 +47,8 @@ cint = ["epaint/cint"] persistence = ["serde", "epaint/persistence", "ron"] -# multi_threaded is only needed if you plan to use the same egui::Context from multiple threads. +# multi_threaded is only needed if you plan to use the same egui::Context +# from multiple threads. It comes with a minor performance impact. single_threaded = ["epaint/single_threaded"] multi_threaded = ["epaint/multi_threaded"] diff --git a/egui/src/data/input.rs b/egui/src/data/input.rs index a91cc3f9a04a..69ab6752ceb4 100644 --- a/egui/src/data/input.rs +++ b/egui/src/data/input.rs @@ -134,7 +134,7 @@ impl RawInput { #[derive(Clone, Debug, Default, PartialEq)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] pub struct HoveredFile { - /// Set by the `egui_glium` backend. + /// Set by the `egui-winit` backend. pub path: Option, /// With the `egui_web` backend, this is set to the mime-type of the file (if available). pub mime: String, @@ -144,7 +144,7 @@ pub struct HoveredFile { #[derive(Clone, Debug, Default, PartialEq)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] pub struct DroppedFile { - /// Set by the `egui_glium` backend. + /// Set by the `egui-winit` backend. pub path: Option, /// Name of the file. Set by the `egui_web` backend. pub name: String, diff --git a/egui_glium/CHANGELOG.md b/egui_glium/CHANGELOG.md index 0e089ad7509d..1cd5df85b976 100644 --- a/egui_glium/CHANGELOG.md +++ b/egui_glium/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to the `egui_glium` integration will be noted in this file. * Add `epi::NativeTexture` trait for glium painter * Deprecate 'Painter::register_glium_texture' * Increase scroll speed. +* Restore window position on startup without flickering. +* A lot of the code has been moved to the new library [`egui-winit`](https://github.com/emilk/egui/tree/master/egui-winit). ## 0.14.0 - 2021-08-24 diff --git a/egui_glium/Cargo.toml b/egui_glium/Cargo.toml index 9cea0432fa14..575c7e9732a4 100644 --- a/egui_glium/Cargo.toml +++ b/egui_glium/Cargo.toml @@ -22,31 +22,36 @@ include = [ all-features = true [dependencies] -copypasta = "0.7" egui = { version = "0.14.0", path = "../egui", default-features = false, features = ["single_threaded"] } +egui-winit = { version = "0.14.0", path = "../egui-winit", default-features = false } epi = { version = "0.14.0", path = "../epi" } glium = "0.30" -webbrowser = "0.5" # feature "persistence": directories-next = { version = "2", optional = true } ron = { version = "0.6", optional = true } serde = { version = "1", optional = true } -# feature screen_reader -tts = { version = "0.17", optional = true } - # feature "time" chrono = { version = "0.4", optional = true } [dev-dependencies] image = { version = "0.23", default-features = false, features = ["png"] } + [features] -default = ["default_fonts"] +default = ["clipboard", "default_fonts", "links"] + +# enable cut/copy/paste to OS clipboard. +# if disabled a clipboard will be simulated so you can still copy/paste within the egui app. +clipboard = ["egui-winit/clipboard"] # If set, egui will use `include_bytes!` to bundle some fonts. # If you plan on specifying your own fonts you may disable this feature. default_fonts = ["egui/default_fonts"] + +# enable opening links in a browser when an egui hyperlink is clicked. +links = ["egui-winit/links"] + persistence = [ "directories-next", "egui/persistence", @@ -54,5 +59,9 @@ persistence = [ "ron", "serde", ] -time = ["chrono"] # for seconds_since_midnight -screen_reader = ["tts"] # experimental + +# experimental support for a screen reader +screen_reader = ["egui-winit/screen_reader"] + +# for seconds_since_midnight (used in egui_demo_lib) +time = ["chrono"] diff --git a/egui_glium/README.md b/egui_glium/README.md index 8cad6c2953bd..18d235d7df49 100644 --- a/egui_glium/README.md +++ b/egui_glium/README.md @@ -13,3 +13,5 @@ To use on Linux, first run: ``` sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libspeechd-dev libxkbcommon-dev libssl-dev ``` + +This crate depends on [`egui-winit`](https://github.com/emilk/egui/tree/master/egui-winit). diff --git a/egui_glium/src/backend.rs b/egui_glium/src/backend.rs index 000b6af01141..357616e9ceef 100644 --- a/egui_glium/src/backend.rs +++ b/egui_glium/src/backend.rs @@ -77,7 +77,7 @@ fn window_builder_drag_and_drop( fn create_display( app: &dyn epi::App, native_options: &epi::NativeOptions, - window_settings: Option, + window_settings: &Option, window_icon: Option, event_loop: &glutin::event_loop::EventLoop, ) -> 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 { // ---------------------------------------------------------------------------- /// Run an egui app -pub fn run(mut app: Box, native_options: epi::NativeOptions) { +pub fn run(mut app: Box, 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, 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)); diff --git a/egui_glium/src/lib.rs b/egui_glium/src/lib.rs index bed15c32256a..87064b7a2c05 100644 --- a/egui_glium/src/lib.rs +++ b/egui_glium/src/lib.rs @@ -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, - 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 { - 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 { - 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 { - 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 { - 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 { 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, - 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( diff --git a/egui_glium/src/painter.rs b/egui_glium/src/painter.rs index 58d3b6bd6c68..042b0b3fb4fe 100644 --- a/egui_glium/src/painter.rs +++ b/egui_glium/src/painter.rs @@ -1,4 +1,5 @@ #![allow(deprecated)] // legacy implement_vertex macro +#![allow(semicolon_in_expressions_from_macros)] // glium::program! macro use { egui::{ diff --git a/egui_glium/src/persistence.rs b/egui_glium/src/persistence.rs index 3d523e4aa564..1ebf720c87c1 100644 --- a/egui_glium/src/persistence.rs +++ b/egui_glium/src/persistence.rs @@ -81,6 +81,9 @@ pub fn read_memory(ctx: &egui::Context, memory_file_path: impl AsRef, diff --git a/egui_glium/src/window_settings.rs b/egui_glium/src/window_settings.rs index 8d490521347a..8bec207918ba 100644 --- a/egui_glium/src/window_settings.rs +++ b/egui_glium/src/window_settings.rs @@ -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, - )); - } } } diff --git a/epaint/Cargo.toml b/epaint/Cargo.toml index 3e17af92cda4..2c8aeae47593 100644 --- a/epaint/Cargo.toml +++ b/epaint/Cargo.toml @@ -36,7 +36,7 @@ parking_lot = { version = "0.11", optional = true } # Using parking_lot over std serde = { version = "1", features = ["derive"], optional = true } [features] -default = ["multi_threaded", "default_fonts"] +default = ["default_fonts", "multi_threaded"] # If set, epaint will use `include_bytes!` to bundle some fonts. # If you plan on specifying your own fonts you may disable this feature. @@ -55,4 +55,5 @@ persistence = ["serde", "emath/serde"] single_threaded = ["atomic_refcell"] # Only needed if you plan to use the same fonts from multiple threads. +# It comes with a minor performance impact. multi_threaded = ["parking_lot"] diff --git a/sh/check.sh b/sh/check.sh index bce9335d2410..d67b79502b65 100755 --- a/sh/check.sh +++ b/sh/check.sh @@ -15,9 +15,18 @@ cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy: cargo test --workspace --all-targets --all-features cargo fmt --all -- --check -cargo doc -p emath -p epaint -p egui -p eframe -p epi -p egui_web -p egui_glium --lib --no-deps --all-features +cargo doc -p emath -p epaint -p egui -p eframe -p epi -p egui_web -p egui-winit -p egui_glium --lib --no-deps --all-features cargo doc -p egui_web --target wasm32-unknown-unknown --lib --no-deps --all-features +(cd emath && cargo check --no-default-features) +(cd epaint && cargo check --no-default-features --features "single_threaded") +(cd egui && cargo check --no-default-features --features "multi_threaded") +(cd eframe && cargo check --no-default-features) +(cd epi && cargo check --no-default-features) +(cd egui_web && cargo check --no-default-features) +(cd egui-winit && cargo check --no-default-features) +(cd egui_glium && cargo check --no-default-features) + # ------------------------------------------------------------ # diff --git a/sh/docs.sh b/sh/docs.sh index 07ad9bdfe890..61da02902f20 100755 --- a/sh/docs.sh +++ b/sh/docs.sh @@ -4,6 +4,6 @@ script_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) cd "$script_path/.." cargo doc -p egui_web --target wasm32-unknown-unknown --lib --no-deps --all-features -cargo doc -p emath -p epaint -p egui -p eframe -p epi -p egui_web -p egui_glium --lib --no-deps --all-features --open +cargo doc -p emath -p epaint -p egui -p eframe -p epi -p egui_web -p egui-winit -p egui_glium --lib --no-deps --all-features --open # cargo watch -c -x 'doc -p emath -p epaint -p egui --lib --no-deps --all-features'