From 36c6b6304d026505f15860b600eaaea38ca4f624 Mon Sep 17 00:00:00 2001 From: Sebastian Urban Date: Mon, 4 Dec 2023 15:58:05 +0100 Subject: [PATCH 01/45] Make glow Send + Sync again. (#3646) * Reverts #3598 * Closes #3645 --- crates/eframe/src/epi.rs | 6 +++--- crates/eframe/src/native/epi_integration.rs | 2 +- crates/eframe/src/native/glow_integration.rs | 2 +- crates/eframe/src/web/web_painter_glow.rs | 5 +++-- crates/egui_glow/examples/pure_glow.rs | 2 +- crates/egui_glow/src/painter.rs | 8 ++++---- crates/egui_glow/src/winit.rs | 2 +- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 982eb54814d..5748d4875ca 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -64,7 +64,7 @@ pub struct CreationContext<'s> { /// /// Only available when compiling with the `glow` feature and using [`Renderer::Glow`]. #[cfg(feature = "glow")] - pub gl: Option>, + pub gl: Option>, /// The underlying WGPU render state. /// @@ -584,7 +584,7 @@ pub struct Frame { /// A reference to the underlying [`glow`] (OpenGL) context. #[cfg(feature = "glow")] - pub(crate) gl: Option>, + pub(crate) gl: Option>, /// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s. #[cfg(feature = "wgpu")] @@ -656,7 +656,7 @@ impl Frame { /// To get a [`glow`] context you need to compile with the `glow` feature flag, /// and run eframe using [`Renderer::Glow`]. #[cfg(feature = "glow")] - pub fn gl(&self) -> Option<&std::rc::Rc> { + pub fn gl(&self) -> Option<&std::sync::Arc> { self.gl.as_ref() } diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index efd8135cce5..7b3d12f110f 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -152,7 +152,7 @@ impl EpiIntegration { app_name: &str, native_options: &crate::NativeOptions, storage: Option>, - #[cfg(feature = "glow")] gl: Option>, + #[cfg(feature = "glow")] gl: Option>, #[cfg(feature = "wgpu")] wgpu_render_state: Option, ) -> Self { let frame = epi::Frame { diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 6bf7336af85..4dff0c86fe7 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -170,7 +170,7 @@ impl GlowWinitApp { let gl = unsafe { crate::profile_scope!("glow::Context::from_loader_function"); - Rc::new(glow::Context::from_loader_function(|s| { + Arc::new(glow::Context::from_loader_function(|s| { let s = std::ffi::CString::new(s) .expect("failed to construct C string from string for gl proc address"); diff --git a/crates/eframe/src/web/web_painter_glow.rs b/crates/eframe/src/web/web_painter_glow.rs index e02c6dd973a..cd62758688f 100644 --- a/crates/eframe/src/web/web_painter_glow.rs +++ b/crates/eframe/src/web/web_painter_glow.rs @@ -15,7 +15,7 @@ pub(crate) struct WebPainterGlow { } impl WebPainterGlow { - pub fn gl(&self) -> &std::rc::Rc { + pub fn gl(&self) -> &std::sync::Arc { self.painter.gl() } @@ -24,7 +24,8 @@ impl WebPainterGlow { let (gl, shader_prefix) = init_glow_context_from_canvas(&canvas, options.webgl_context_option)?; - let gl = std::rc::Rc::new(gl); + #[allow(clippy::arc_with_non_send_sync)] + let gl = std::sync::Arc::new(gl); let painter = egui_glow::Painter::new(gl, shader_prefix, None) .map_err(|err| format!("Error starting glow painter: {err}"))?; diff --git a/crates/egui_glow/examples/pure_glow.rs b/crates/egui_glow/examples/pure_glow.rs index 5709a4d03e6..5df4d23ecdf 100644 --- a/crates/egui_glow/examples/pure_glow.rs +++ b/crates/egui_glow/examples/pure_glow.rs @@ -152,7 +152,7 @@ fn main() { let event_loop = winit::event_loop::EventLoopBuilder::::with_user_event().build(); let (gl_window, gl) = create_display(&event_loop); - let gl = std::rc::Rc::new(gl); + let gl = std::sync::Arc::new(gl); let mut egui_glow = egui_glow::EguiGlow::new(&event_loop, gl.clone(), None, None); diff --git a/crates/egui_glow/src/painter.rs b/crates/egui_glow/src/painter.rs index e37de40cbc3..2df12298c46 100644 --- a/crates/egui_glow/src/painter.rs +++ b/crates/egui_glow/src/painter.rs @@ -1,7 +1,7 @@ #![allow(clippy::collapsible_else_if)] #![allow(unsafe_code)] -use std::{collections::HashMap, rc::Rc}; +use std::{collections::HashMap, sync::Arc}; use egui::{ emath::Rect, @@ -62,7 +62,7 @@ impl From for PainterError { /// /// NOTE: all egui viewports share the same painter. pub struct Painter { - gl: Rc, + gl: Arc, max_texture_side: usize, @@ -120,7 +120,7 @@ impl Painter { /// * failed to create postprocess on webgl with `sRGB` support /// * failed to create buffer pub fn new( - gl: Rc, + gl: Arc, shader_prefix: &str, shader_version: Option, ) -> Result { @@ -250,7 +250,7 @@ impl Painter { } /// Access the shared glow context. - pub fn gl(&self) -> &Rc { + pub fn gl(&self) -> &Arc { &self.gl } diff --git a/crates/egui_glow/src/winit.rs b/crates/egui_glow/src/winit.rs index 653aef07a96..7a154b27d2b 100644 --- a/crates/egui_glow/src/winit.rs +++ b/crates/egui_glow/src/winit.rs @@ -24,7 +24,7 @@ impl EguiGlow { /// For automatic shader version detection set `shader_version` to `None`. pub fn new( event_loop: &winit::event_loop::EventLoopWindowTarget, - gl: std::rc::Rc, + gl: std::sync::Arc, shader_version: Option, native_pixels_per_point: Option, ) -> Self { From 80d7143b156ac91bdc0e76dbb18ad64b864b9e2c Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 4 Dec 2023 15:59:13 +0100 Subject: [PATCH 02/45] Log all available wgpu adapters during startup (#3671) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Useful when debugging, e.g. if the default choice is one of many, and perhaps the other ones would be better. **EDIT**: Oh, `enumerate_adapters` doesn't work on web… will fix. --- crates/egui-wgpu/src/lib.rs | 113 +++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index 46e97d0a939..447f717b947 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -69,6 +69,9 @@ impl RenderState { ) -> Result { crate::profile_scope!("RenderState::create"); // async yield give bad names using `profile_function` + #[cfg(not(target_arch = "wasm32"))] + let adapters: Vec<_> = instance.enumerate_adapters(wgpu::Backends::all()).collect(); + let adapter = { crate::profile_scope!("request_adapter"); instance @@ -78,9 +81,51 @@ impl RenderState { force_fallback_adapter: false, }) .await - .ok_or(WgpuError::NoSuitableAdapterFound)? + .ok_or_else(|| { + #[cfg(not(target_arch = "wasm32"))] + if adapters.is_empty() { + log::info!("No wgpu adapters found"); + } else if adapters.len() == 1 { + log::info!( + "The only available wgpu adapter was not suitable: {}", + adapter_info_summary(&adapters[0].get_info()) + ); + } else { + log::info!( + "No suitable wgpu adapter found out of the {} available ones: {}", + adapters.len(), + describe_adapters(&adapters) + ); + } + + WgpuError::NoSuitableAdapterFound + })? }; + #[cfg(target_arch = "wasm32")] + log::debug!( + "Picked wgpu adapter: {}", + adapter_info_summary(&adapter.get_info()) + ); + + #[cfg(not(target_arch = "wasm32"))] + if adapters.len() == 1 { + log::debug!( + "Picked the only available wgpu adapter: {}", + adapter_info_summary(&adapter.get_info()) + ); + } else { + log::info!( + "There were {} available wgpu adapters: {}", + adapters.len(), + describe_adapters(&adapters) + ); + log::debug!( + "Picked wgpu adapter: {}", + adapter_info_summary(&adapter.get_info()) + ); + } + let capabilities = { crate::profile_scope!("get_capabilities"); surface.get_capabilities(&adapter).formats @@ -106,6 +151,24 @@ impl RenderState { } } +#[cfg(not(target_arch = "wasm32"))] +fn describe_adapters(adapters: &[wgpu::Adapter]) -> String { + if adapters.is_empty() { + "(none)".to_owned() + } else if adapters.len() == 1 { + adapter_info_summary(&adapters[0].get_info()) + } else { + let mut list_string = String::new(); + for adapter in adapters { + if !list_string.is_empty() { + list_string += ", "; + } + list_string += &format!("{{{}}}", adapter_info_summary(&adapter.get_info())); + } + list_string + } +} + /// Specifies which action should be taken as consequence of a [`wgpu::SurfaceError`] pub enum SurfaceErrorAction { /// Do nothing and skip the current frame. @@ -116,6 +179,10 @@ pub enum SurfaceErrorAction { } /// Configuration for using wgpu with eframe or the egui-wgpu winit feature. +/// +/// This can be configured with the environment variables: +/// * `WGPU_BACKEND`: `vulkan`, `dx11`, `dx12`, `metal`, `opengl`, `webgpu` +/// * `WGPU_POWER_PREF`: `low`, `high` or `none` #[derive(Clone)] pub struct WgpuConfiguration { /// Backends that should be supported (wgpu will pick one of these) @@ -151,6 +218,7 @@ impl Default for WgpuConfiguration { // (note however, that the GL backend needs to be opted-in via a wgpu feature flag) supported_backends: wgpu::util::backend_bits_from_env() .unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::GL), + device_descriptor: Arc::new(|adapter| { let base_limits = if adapter.get_info().backend == wgpu::Backend::Gl { wgpu::Limits::downlevel_webgl2_defaults() @@ -169,7 +237,9 @@ impl Default for WgpuConfiguration { }, } }), + present_mode: wgpu::PresentMode::AutoVsync, + power_preference: wgpu::util::power_preference_from_env() .unwrap_or(wgpu::PowerPreference::HighPerformance), @@ -224,6 +294,47 @@ pub fn depth_format_from_bits(depth_buffer: u8, stencil_buffer: u8) -> Option String { + let wgpu::AdapterInfo { + name, + vendor, + device, + device_type, + driver, + driver_info, + backend, + } = &info; + + // Example values: + // > name: "llvmpipe (LLVM 16.0.6, 256 bits)", device_type: Cpu, backend: Vulkan, driver: "llvmpipe", driver_info: "Mesa 23.1.6-arch1.4 (LLVM 16.0.6)" + // > name: "Apple M1 Pro", device_type: IntegratedGpu, backend: Metal, driver: "", driver_info: "" + // > name: "ANGLE (Apple, Apple M1 Pro, OpenGL 4.1)", device_type: IntegratedGpu, backend: Gl, driver: "", driver_info: "" + + let mut summary = format!("backend: {backend:?}, device_type: {device_type:?}"); + + if !name.is_empty() { + summary += &format!(", name: {name:?}"); + } + if !driver.is_empty() { + summary += &format!(", driver: {driver:?}"); + } + if !driver_info.is_empty() { + summary += &format!(", driver_info: {driver_info:?}"); + } + if *vendor != 0 { + // TODO(emilk): decode using https://github.com/gfx-rs/wgpu/blob/767ac03245ee937d3dc552edc13fe7ab0a860eec/wgpu-hal/src/auxil/mod.rs#L7 + summary += &format!(", vendor: 0x{vendor:04X}"); + } + if *device != 0 { + summary += &format!(", device: 0x{device:02X}"); + } + + summary +} + +// --------------------------------------------------------------------------- + mod profiling_scopes { #![allow(unused_macros)] #![allow(unused_imports)] From a4e389431d1146f5bae5ea687919df34a53e20cf Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 5 Dec 2023 11:45:25 +0100 Subject: [PATCH 03/45] Simplify `egui_winit::State` (#3678) By storing a clone of the egui context in it, we can be sure to always use the correct `pixels_per_point` --- crates/eframe/src/native/epi_integration.rs | 3 +- crates/eframe/src/native/glow_integration.rs | 7 +- crates/eframe/src/native/wgpu_integration.rs | 13 +-- crates/egui-winit/src/lib.rs | 114 ++++++++----------- crates/egui_glow/examples/pure_glow.rs | 2 +- crates/egui_glow/src/winit.rs | 15 ++- 6 files changed, 70 insertions(+), 84 deletions(-) diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 7b3d12f110f..7ad2f9dfdd1 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -255,8 +255,7 @@ impl EpiIntegration { _ => {} } - egui_winit.update_pixels_per_point(&self.egui_ctx, window); - egui_winit.on_window_event(&self.egui_ctx, event) + egui_winit.on_window_event(window, event) } pub fn pre_update(&mut self) { diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 4dff0c86fe7..bc3bb1e174c 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -517,7 +517,6 @@ impl GlowWinitRunning { egui_winit::update_viewport_info(&mut viewport.info, &egui_ctx, window); let egui_winit = viewport.egui_winit.as_mut().unwrap(); - egui_winit.update_pixels_per_point(&egui_ctx, window); let mut raw_input = egui_winit.take_egui_input(window); let viewport_ui_cb = viewport.viewport_ui_cb.clone(); @@ -600,7 +599,7 @@ impl GlowWinitRunning { let egui_winit = viewport.egui_winit.as_mut().unwrap(); integration.post_update(); - egui_winit.handle_platform_output(window, &integration.egui_ctx, platform_output); + egui_winit.handle_platform_output(window, platform_output); let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point); @@ -1066,6 +1065,7 @@ impl GlutinWindowContext { viewport.egui_winit.get_or_insert_with(|| { egui_winit::State::new( + self.egui_ctx.clone(), viewport_id, event_loop, Some(window.scale_factor() as f32), @@ -1318,7 +1318,6 @@ fn render_immediate_viewport( }; egui_winit::update_viewport_info(&mut viewport.info, egui_ctx, window); - egui_winit.update_pixels_per_point(egui_ctx, window); let mut raw_input = egui_winit.take_egui_input(window); raw_input.viewports = glutin .viewports @@ -1414,7 +1413,7 @@ fn render_immediate_viewport( } } - egui_winit.handle_platform_output(window, egui_ctx, platform_output); + egui_winit.handle_platform_output(window, platform_output); glutin.handle_viewport_output(egui_ctx, viewport_output); } diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index 3f9cf14e983..8b859f5dda3 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -205,6 +205,7 @@ impl WgpuWinitApp { #[allow(unused_mut)] // used for accesskit let mut egui_winit = egui_winit::State::new( + egui_ctx.clone(), ViewportId::ROOT, event_loop, Some(window.scale_factor() as f32), @@ -488,10 +489,7 @@ impl WgpuWinitRunning { let mut shared_lock = shared.borrow_mut(); let SharedState { - egui_ctx, - viewports, - painter, - .. + viewports, painter, .. } = &mut *shared_lock; if viewport_id != ViewportId::ROOT { @@ -539,7 +537,6 @@ impl WgpuWinitRunning { } let egui_winit = egui_winit.as_mut().unwrap(); - egui_winit.update_pixels_per_point(egui_ctx, window); let mut raw_input = egui_winit.take_egui_input(window); integration.pre_update(); @@ -596,7 +593,7 @@ impl WgpuWinitRunning { viewport_output, } = full_output; - egui_winit.handle_platform_output(window, egui_ctx, platform_output); + egui_winit.handle_platform_output(window, platform_output); { let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point); @@ -799,6 +796,7 @@ impl Viewport { } self.egui_winit = Some(egui_winit::State::new( + egui_ctx.clone(), viewport_id, event_loop, Some(window.scale_factor() as f32), @@ -880,7 +878,6 @@ fn render_immediate_viewport( }; egui_winit::update_viewport_info(&mut viewport.info, egui_ctx, window); - egui_winit.update_pixels_per_point(egui_ctx, window); let mut input = egui_winit.take_egui_input(window); input.viewports = viewports .iter() @@ -944,7 +941,7 @@ fn render_immediate_viewport( false, ); - egui_winit.handle_platform_output(window, &egui_ctx, platform_output); + egui_winit.handle_platform_output(window, platform_output); handle_viewport_output(&egui_ctx, viewport_output, viewports, *focused_viewport); } diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index f22dd81d2f4..11f672bb933 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -69,6 +69,9 @@ pub struct EventResponse { /// /// Instantiate one of these per viewport/window. pub struct State { + /// Shared clone. + egui_ctx: egui::Context, + viewport_id: ViewportId, start_time: web_time::Instant, egui_input: egui::RawInput, @@ -76,9 +79,6 @@ pub struct State { any_pointer_button_down: bool, current_cursor_icon: Option, - /// What egui uses. - current_pixels_per_point: f32, // TODO: remove - calculate with [`pixels_per_point`] instead - clipboard: clipboard::Clipboard, /// If `true`, mouse inputs will be treated as touches. @@ -104,6 +104,7 @@ pub struct State { impl State { /// Construct a new instance pub fn new( + egui_ctx: egui::Context, viewport_id: ViewportId, display_target: &dyn HasRawDisplayHandle, native_pixels_per_point: Option, @@ -117,13 +118,13 @@ impl State { }; let mut slf = Self { + egui_ctx, viewport_id, start_time: web_time::Instant::now(), egui_input, pointer_pos_in_points: None, any_pointer_button_down: false, current_cursor_icon: None, - current_pixels_per_point: native_pixels_per_point.unwrap_or(1.0), clipboard: clipboard::Clipboard::new(display_target), @@ -172,9 +173,8 @@ impl State { } #[inline] - #[deprecated = "Use egui_winit::pixels_per_point instead"] - pub fn pixels_per_point(&self) -> f32 { - self.current_pixels_per_point + pub fn egui_ctx(&self) -> &egui::Context { + &self.egui_ctx } /// The current input state. @@ -191,18 +191,12 @@ impl State { &mut self.egui_input } - /// Update the given viewport info with the current state of the window. - #[deprecated = "Use egui_winit::update_viewport_info instead"] - pub fn update_viewport_info(&self, info: &mut ViewportInfo, window: &Window) { - update_viewport_info_impl(info, window, self.current_pixels_per_point); - } - /// 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). /// /// You need to set [`egui::RawInput::viewports`] yourself though. - /// Use [`Self::update_viewport_info`] to update the info for each + /// Use [`update_viewport_info`] to update the info for each /// viewport. pub fn take_egui_input(&mut self, window: &Window) -> egui::RawInput { crate::profile_function!(); @@ -213,7 +207,8 @@ impl State { // 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(window); - let screen_size_in_points = screen_size_in_pixels / self.current_pixels_per_point; + let screen_size_in_points = + screen_size_in_pixels / pixels_per_point(&self.egui_ctx, window); self.egui_input.screen_rect = (screen_size_in_points.x > 0.0 && screen_size_in_points.y > 0.0) @@ -231,18 +226,12 @@ impl State { self.egui_input.take() } - // TODO(emilk): remove asap. - #[doc(hidden)] - pub fn update_pixels_per_point(&mut self, egui_ctx: &egui::Context, window: &Window) { - self.current_pixels_per_point = pixels_per_point(egui_ctx, window); - } - /// 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`]. pub fn on_window_event( &mut self, - egui_ctx: &egui::Context, + window: &Window, event: &winit::event::WindowEvent<'_>, ) -> EventResponse { crate::profile_function!(short_window_event_description(event)); @@ -257,7 +246,7 @@ impl State { .entry(self.viewport_id) .or_default() .native_pixels_per_point = Some(native_pixels_per_point); - self.current_pixels_per_point = egui_ctx.zoom_factor() * native_pixels_per_point; + EventResponse { repaint: true, consumed: false, @@ -267,21 +256,21 @@ impl State { self.on_mouse_button_input(*state, *button); EventResponse { repaint: true, - consumed: egui_ctx.wants_pointer_input(), + consumed: self.egui_ctx.wants_pointer_input(), } } WindowEvent::MouseWheel { delta, .. } => { - self.on_mouse_wheel(*delta); + self.on_mouse_wheel(window, *delta); EventResponse { repaint: true, - consumed: egui_ctx.wants_pointer_input(), + consumed: self.egui_ctx.wants_pointer_input(), } } WindowEvent::CursorMoved { position, .. } => { - self.on_cursor_moved(*position); + self.on_cursor_moved(window, *position); EventResponse { repaint: true, - consumed: egui_ctx.is_using_pointer(), + consumed: self.egui_ctx.is_using_pointer(), } } WindowEvent::CursorLeft { .. } => { @@ -294,12 +283,12 @@ impl State { } // WindowEvent::TouchpadPressure {device_id, pressure, stage, .. } => {} // TODO WindowEvent::Touch(touch) => { - self.on_touch(touch); + self.on_touch(window, touch); let consumed = 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(), + | winit::event::TouchPhase::Cancelled => self.egui_ctx.wants_pointer_input(), + winit::event::TouchPhase::Moved => self.egui_ctx.is_using_pointer(), }; EventResponse { repaint: true, @@ -316,7 +305,7 @@ impl State { self.egui_input .events .push(egui::Event::Text(ch.to_string())); - egui_ctx.wants_keyboard_input() + self.egui_ctx.wants_keyboard_input() } else { false }; @@ -361,13 +350,13 @@ impl State { EventResponse { repaint: true, - consumed: egui_ctx.wants_keyboard_input(), + consumed: self.egui_ctx.wants_keyboard_input(), } } WindowEvent::KeyboardInput { input, .. } => { self.on_keyboard_input(input); // When pressing the Tab key, egui focuses the first focusable element, hence Tab always consumes. - let consumed = egui_ctx.wants_keyboard_input() + let consumed = self.egui_ctx.wants_keyboard_input() || input.virtual_keycode == Some(winit::event::VirtualKeyCode::Tab); EventResponse { repaint: true, @@ -459,7 +448,7 @@ impl State { self.egui_input.events.push(egui::Event::Zoom(zoom_factor)); EventResponse { repaint: true, - consumed: egui_ctx.wants_pointer_input(), + consumed: self.egui_ctx.wants_pointer_input(), } } } @@ -520,10 +509,16 @@ impl State { } } - fn on_cursor_moved(&mut self, pos_in_pixels: winit::dpi::PhysicalPosition) { + fn on_cursor_moved( + &mut self, + window: &Window, + pos_in_pixels: winit::dpi::PhysicalPosition, + ) { + let pixels_per_point = pixels_per_point(&self.egui_ctx, window); + let pos_in_points = egui::pos2( - pos_in_pixels.x as f32 / self.current_pixels_per_point, - pos_in_pixels.y as f32 / self.current_pixels_per_point, + pos_in_pixels.x as f32 / pixels_per_point, + pos_in_pixels.y as f32 / pixels_per_point, ); self.pointer_pos_in_points = Some(pos_in_points); @@ -548,7 +543,9 @@ impl State { } } - fn on_touch(&mut self, touch: &winit::event::Touch) { + fn on_touch(&mut self, window: &Window, touch: &winit::event::Touch) { + let pixels_per_point = pixels_per_point(&self.egui_ctx, window); + // Emit touch event self.egui_input.events.push(egui::Event::Touch { device_id: egui::TouchDeviceId(egui::epaint::util::hash(touch.device_id)), @@ -560,8 +557,8 @@ impl State { winit::event::TouchPhase::Cancelled => egui::TouchPhase::Cancel, }, pos: egui::pos2( - touch.location.x as f32 / self.current_pixels_per_point, - touch.location.y as f32 / self.current_pixels_per_point, + touch.location.x as f32 / pixels_per_point, + touch.location.y as f32 / pixels_per_point, ), force: match touch.force { Some(winit::event::Force::Normalized(force)) => Some(force as f32), @@ -581,14 +578,14 @@ impl State { winit::event::TouchPhase::Started => { self.pointer_touch_id = Some(touch.id); // First move the pointer to the right location - self.on_cursor_moved(touch.location); + self.on_cursor_moved(window, touch.location); self.on_mouse_button_input( winit::event::ElementState::Pressed, winit::event::MouseButton::Left, ); } winit::event::TouchPhase::Moved => { - self.on_cursor_moved(touch.location); + self.on_cursor_moved(window, touch.location); } winit::event::TouchPhase::Ended => { self.pointer_touch_id = None; @@ -610,7 +607,9 @@ impl State { } } - fn on_mouse_wheel(&mut self, delta: winit::event::MouseScrollDelta) { + fn on_mouse_wheel(&mut self, window: &Window, delta: winit::event::MouseScrollDelta) { + let pixels_per_point = pixels_per_point(&self.egui_ctx, window); + { let (unit, delta) = match delta { winit::event::MouseScrollDelta::LineDelta(x, y) => { @@ -621,7 +620,7 @@ impl State { y, }) => ( egui::MouseWheelUnit::Point, - egui::vec2(x as f32, y as f32) / self.current_pixels_per_point, + egui::vec2(x as f32, y as f32) / pixels_per_point, ), }; let modifiers = self.egui_input.modifiers; @@ -637,7 +636,7 @@ impl State { egui::vec2(x, y) * points_per_scroll_line } winit::event::MouseScrollDelta::PixelDelta(delta) => { - egui::vec2(delta.x as f32, delta.y as f32) / self.current_pixels_per_point + egui::vec2(delta.x as f32, delta.y as f32) / pixels_per_point } }; @@ -699,7 +698,6 @@ impl State { pub fn handle_platform_output( &mut self, window: &Window, - egui_ctx: &egui::Context, platform_output: egui::PlatformOutput, ) { crate::profile_function!(); @@ -715,8 +713,6 @@ impl State { accesskit_update, } = platform_output; - self.current_pixels_per_point = egui_ctx.pixels_per_point(); // someone can have changed it to scale the UI - self.set_cursor_icon(window, cursor_icon); if let Some(open_url) = open_url { @@ -772,17 +768,15 @@ impl State { /// Update the given viewport info with the current state of the window. /// /// Call before [`State::take_egui_input`]. -pub fn update_viewport_info(info: &mut ViewportInfo, egui_ctx: &egui::Context, window: &Window) { - update_viewport_info_impl(info, window, pixels_per_point(egui_ctx, window)); -} - -fn update_viewport_info_impl( +pub fn update_viewport_info( viewport_info: &mut ViewportInfo, + egui_ctx: &egui::Context, window: &Window, - pixels_per_point: f32, ) { crate::profile_function!(); + let pixels_per_point = pixels_per_point(egui_ctx, window); + let has_a_position = match window.is_minimized() { None | Some(true) => false, Some(false) => true, @@ -1433,16 +1427,6 @@ pub fn create_winit_window_builder( window_builder } -/// Applies what `create_winit_window_builder` couldn't -#[deprecated = "Use apply_viewport_builder_to_window instead"] -pub fn apply_viewport_builder_to_new_window(window: &Window, builder: &ViewportBuilder) { - if let Some(mouse_passthrough) = builder.mouse_passthrough { - if let Err(err) = window.set_cursor_hittest(!mouse_passthrough) { - log::warn!("set_cursor_hittest failed: {err}"); - } - } -} - /// Applies what `create_winit_window_builder` couldn't pub fn apply_viewport_builder_to_window( egui_ctx: &egui::Context, diff --git a/crates/egui_glow/examples/pure_glow.rs b/crates/egui_glow/examples/pure_glow.rs index 5df4d23ecdf..1bff00d5837 100644 --- a/crates/egui_glow/examples/pure_glow.rs +++ b/crates/egui_glow/examples/pure_glow.rs @@ -235,7 +235,7 @@ fn main() { gl_window.resize(**new_inner_size); } - let event_response = egui_glow.on_window_event(&event); + let event_response = egui_glow.on_window_event(gl_window.window(), &event); if event_response.repaint { gl_window.window().request_redraw(); diff --git a/crates/egui_glow/src/winit.rs b/crates/egui_glow/src/winit.rs index 7a154b27d2b..567ce65f60d 100644 --- a/crates/egui_glow/src/winit.rs +++ b/crates/egui_glow/src/winit.rs @@ -34,7 +34,10 @@ impl EguiGlow { }) .unwrap(); + let egui_ctx = egui::Context::default(); + let egui_winit = egui_winit::State::new( + egui_ctx.clone(), ViewportId::ROOT, event_loop, native_pixels_per_point, @@ -42,7 +45,7 @@ impl EguiGlow { ); Self { - egui_ctx: Default::default(), + egui_ctx, egui_winit, painter, viewport_info: Default::default(), @@ -52,8 +55,12 @@ impl EguiGlow { } } - pub fn on_window_event(&mut self, event: &winit::event::WindowEvent<'_>) -> EventResponse { - self.egui_winit.on_window_event(&self.egui_ctx, event) + pub fn on_window_event( + &mut self, + window: &winit::window::Window, + event: &winit::event::WindowEvent<'_>, + ) -> EventResponse { + self.egui_winit.on_window_event(window, event) } /// Call [`Self::paint`] later to paint. @@ -87,7 +94,7 @@ impl EguiGlow { } self.egui_winit - .handle_platform_output(window, &self.egui_ctx, platform_output); + .handle_platform_output(window, platform_output); self.shapes = shapes; self.pixels_per_point = pixels_per_point; From 4e75f4313ef55ec7f6b336915baa350e1316d337 Mon Sep 17 00:00:00 2001 From: Brian Janssen Date: Tue, 5 Dec 2023 16:38:21 +0100 Subject: [PATCH 04/45] Fix typos in viewports docs (`then` vs `than`) (#3681) I looked through the new viewport docs and noticed these small typos. This PR fixes them. --- crates/egui/src/viewport.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index f811f95e0cc..3a9c60ebaa6 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -439,7 +439,7 @@ impl ViewportBuilder { /// /// If this is not set, some platform-specific dimensions will be used. /// - /// Should be bigger then 0 + /// Should be bigger than 0 /// Look at winit for more details #[inline] pub fn with_inner_size(mut self, size: impl Into) -> Self { @@ -452,7 +452,7 @@ impl ViewportBuilder { /// If this is not set, the window will have no minimum dimensions (aside /// from reserved). /// - /// Should be bigger then 0 + /// Should be bigger than 0 /// Look at winit for more details #[inline] pub fn with_min_inner_size(mut self, size: impl Into) -> Self { @@ -465,7 +465,7 @@ impl ViewportBuilder { /// If this is not set, the window will have no maximum or will be set to /// the primary monitor's dimensions by the platform. /// - /// Should be bigger then 0 + /// Should be bigger than 0 /// Look at winit for more details #[inline] pub fn with_max_inner_size(mut self, size: impl Into) -> Self { @@ -854,16 +854,16 @@ pub enum ViewportCommand { /// Set the outer position of the viewport, i.e. moves the window. OuterPosition(Pos2), - /// Should be bigger then 0 + /// Should be bigger than 0 InnerSize(Vec2), - /// Should be bigger then 0 + /// Should be bigger than 0 MinInnerSize(Vec2), - /// Should be bigger then 0 + /// Should be bigger than 0 MaxInnerSize(Vec2), - /// Should be bigger then 0 + /// Should be bigger than 0 ResizeIncrements(Option), /// Begin resizing the viewport with the left mouse button until the button is released. From d8a795598f063cfc90b147cb20fd5591a21a8b08 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler <49431240+abey79@users.noreply.github.com> Date: Fri, 8 Dec 2023 09:08:27 +0100 Subject: [PATCH 05/45] Fix `egui_extras::Table` scrolling bug (#3690) `egui_extras::Table` now uses the clip rectangle to decide which rows to draw when using `TableBody::rows()`. - Closes #3682 - Closes #3670 --- crates/egui_extras/src/table.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/egui_extras/src/table.rs b/crates/egui_extras/src/table.rs index 8b497f20de0..81a75df43de 100644 --- a/crates/egui_extras/src/table.rs +++ b/crates/egui_extras/src/table.rs @@ -592,7 +592,7 @@ impl<'a> Table<'a> { auto_shrink, } = scroll_options; - let avail_rect = ui.available_rect_before_wrap(); + let cursor_position = ui.cursor().min; let mut scroll_area = ScrollArea::new([false, vscroll]) .auto_shrink(true) @@ -613,6 +613,8 @@ impl<'a> Table<'a> { scroll_area.show(ui, move |ui| { let mut scroll_to_y_range = None; + let clip_rect = ui.clip_rect(); + // Hide first-frame-jitters when auto-sizing. ui.add_visible_ui(!first_frame_auto_size_columns, |ui| { let layout = StripLayout::new(ui, CellDirection::Horizontal, cell_layout); @@ -624,8 +626,8 @@ impl<'a> Table<'a> { max_used_widths: max_used_widths_ref, striped, row_nr: 0, - start_y: avail_rect.top(), - end_y: avail_rect.bottom(), + start_y: clip_rect.top(), + end_y: clip_rect.bottom(), scroll_to_row: scroll_to_row.map(|(r, _)| r), scroll_to_y_range: &mut scroll_to_y_range, }); @@ -647,7 +649,7 @@ impl<'a> Table<'a> { let bottom = ui.min_rect().bottom(); let spacing_x = ui.spacing().item_spacing.x; - let mut x = avail_rect.left() - spacing_x * 0.5; + let mut x = cursor_position.x - spacing_x * 0.5; for (i, column_width) in state.column_widths.iter_mut().enumerate() { let column = &columns[i]; let column_is_resizable = column.resizable.unwrap_or(resizable); From b1721a3ce773bdfebe7c95ea9f83d9bf390d28be Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 8 Dec 2023 10:32:06 +0100 Subject: [PATCH 06/45] Release `egui_extras` 0.24.2 - Fix `Table` scrolling bug (#3691) Includes: * https://github.com/emilk/egui/pull/3690 --------- Co-authored-by: Antoine Beyeler <49431240+abey79@users.noreply.github.com> --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 2 +- crates/egui_extras/CHANGELOG.md | 4 ++++ crates/egui_extras/src/table.rs | 3 ++- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4de3c27ab52..748b3265d14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1099,7 +1099,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "ecolor" -version = "0.24.1" +version = "0.24.2" dependencies = [ "bytemuck", "cint", @@ -1110,7 +1110,7 @@ dependencies = [ [[package]] name = "eframe" -version = "0.24.1" +version = "0.24.2" dependencies = [ "bytemuck", "cocoa", @@ -1146,7 +1146,7 @@ dependencies = [ [[package]] name = "egui" -version = "0.24.1" +version = "0.24.2" dependencies = [ "accesskit", "ahash", @@ -1162,7 +1162,7 @@ dependencies = [ [[package]] name = "egui-wgpu" -version = "0.24.1" +version = "0.24.2" dependencies = [ "bytemuck", "document-features", @@ -1178,7 +1178,7 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.24.1" +version = "0.24.2" dependencies = [ "accesskit_winit", "arboard", @@ -1196,7 +1196,7 @@ dependencies = [ [[package]] name = "egui_demo_app" -version = "0.24.1" +version = "0.24.2" dependencies = [ "bytemuck", "chrono", @@ -1220,7 +1220,7 @@ dependencies = [ [[package]] name = "egui_demo_lib" -version = "0.24.1" +version = "0.24.2" dependencies = [ "chrono", "criterion", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "egui_extras" -version = "0.24.1" +version = "0.24.2" dependencies = [ "chrono", "document-features", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "egui_glow" -version = "0.24.1" +version = "0.24.2" dependencies = [ "bytemuck", "document-features", @@ -1274,7 +1274,7 @@ dependencies = [ [[package]] name = "egui_plot" -version = "0.24.1" +version = "0.24.2" dependencies = [ "document-features", "egui", @@ -1303,7 +1303,7 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "emath" -version = "0.24.1" +version = "0.24.2" dependencies = [ "bytemuck", "document-features", @@ -1379,7 +1379,7 @@ dependencies = [ [[package]] name = "epaint" -version = "0.24.1" +version = "0.24.2" dependencies = [ "ab_glyph", "ahash", diff --git a/Cargo.toml b/Cargo.toml index bbf9fb19b03..7db8cec3594 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ edition = "2021" license = "MIT OR Apache-2.0" rust-version = "1.72" -version = "0.24.1" +version = "0.24.2" [profile.release] diff --git a/crates/egui_extras/CHANGELOG.md b/crates/egui_extras/CHANGELOG.md index 9208b85c860..5cc3804182c 100644 --- a/crates/egui_extras/CHANGELOG.md +++ b/crates/egui_extras/CHANGELOG.md @@ -5,6 +5,10 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.24.2 - 2023-12-08 - `Table` scroll bug fix +* Fix `Table` scrolling bug [#3690](https://github.com/emilk/egui/pull/3690) + + ## 0.24.1 - 2023-11-30 * Add more years for datepicker [#3599](https://github.com/emilk/egui/pull/3599) (thanks [@vaqxai](https://github.com/vaqxai)!) diff --git a/crates/egui_extras/src/table.rs b/crates/egui_extras/src/table.rs index 81a75df43de..c456ba9645e 100644 --- a/crates/egui_extras/src/table.rs +++ b/crates/egui_extras/src/table.rs @@ -802,7 +802,8 @@ impl<'a> TableBody<'a> { /// Add a single row with the given height. /// - /// If you have many thousands of row it can be more performant to instead use [`Self::rows`] or [`Self::heterogeneous_rows`]. + /// ⚠️ It is much more performant to use [`Self::rows`] or [`Self::heterogeneous_rows`], + /// as those functions will only render the visible rows. pub fn row(&mut self, height: f32, add_row_content: impl FnOnce(TableRow<'a, '_>)) { let top_y = self.layout.cursor.y; add_row_content(TableRow { From 8d4de866d4da1835b31e85f9068a5257e6ccbccb Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 8 Dec 2023 11:02:57 +0100 Subject: [PATCH 07/45] Remove deprecated functions (#3692) --- crates/egui/src/containers/area.rs | 7 ----- .../egui/src/containers/collapsing_header.rs | 30 ------------------- crates/egui/src/containers/frame.rs | 6 ---- crates/egui/src/containers/window.rs | 9 ------ crates/egui/src/context.rs | 10 ------- crates/egui/src/data/input.rs | 9 ------ crates/egui/src/data/output.rs | 8 ----- crates/egui/src/id.rs | 5 ---- crates/egui/src/memory.rs | 17 +---------- crates/egui/src/painter.rs | 10 ------- crates/egui/src/util/id_type_map.rs | 2 +- crates/egui/src/widgets/text_edit/builder.rs | 5 ---- crates/egui_extras/src/image.rs | 10 +------ crates/egui_extras/src/table.rs | 5 ---- crates/egui_glow/src/painter.rs | 5 ---- crates/egui_plot/src/items/mod.rs | 14 --------- crates/egui_plot/src/lib.rs | 18 ----------- crates/emath/src/rect.rs | 7 ----- crates/epaint/src/shape.rs | 11 ------- crates/epaint/src/stroke.rs | 8 +---- 20 files changed, 4 insertions(+), 192 deletions(-) diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs index c1eab977da7..a8c1d417139 100644 --- a/crates/egui/src/containers/area.rs +++ b/crates/egui/src/containers/area.rs @@ -173,13 +173,6 @@ impl Area { self } - #[deprecated = "Use `constrain_to` instead"] - #[inline] - pub fn drag_bounds(mut self, constrain_rect: Rect) -> Self { - self.constrain_rect = Some(constrain_rect); - self - } - /// Where the "root" of the area is. /// /// For instance, if you set this to [`Align2::RIGHT_TOP`] diff --git a/crates/egui/src/containers/collapsing_header.rs b/crates/egui/src/containers/collapsing_header.rs index 2fc28d3dd51..96bf5cdef7e 100644 --- a/crates/egui/src/containers/collapsing_header.rs +++ b/crates/egui/src/containers/collapsing_header.rs @@ -424,36 +424,6 @@ impl CollapsingHeader { self } - /// Can the [`CollapsingHeader`] be selected by clicking it? Default: `false`. - #[deprecated = "Use the more powerful egui::collapsing_header::CollapsingState::show_header"] // Deprecated in 2022-04-28, before egui 0.18 - #[inline] - pub fn selectable(mut self, selectable: bool) -> Self { - self.selectable = selectable; - self - } - - /// If you set this to 'true', the [`CollapsingHeader`] will be shown as selected. - /// - /// Example: - /// ``` - /// # egui::__run_test_ui(|ui| { - /// let mut selected = false; - /// let response = egui::CollapsingHeader::new("Select and open me") - /// .selectable(true) - /// .selected(selected) - /// .show(ui, |ui| ui.label("Body")); - /// if response.header_response.clicked() { - /// selected = true; - /// } - /// # }); - /// ``` - #[deprecated = "Use the more powerful egui::collapsing_header::CollapsingState::show_header"] // Deprecated in 2022-04-28, before egui 0.18 - #[inline] - pub fn selected(mut self, selected: bool) -> Self { - self.selected = selected; - self - } - /// Should the [`CollapsingHeader`] show a background behind it? Default: `false`. /// /// To show it behind all [`CollapsingHeader`] you can just use: diff --git a/crates/egui/src/containers/frame.rs b/crates/egui/src/containers/frame.rs index 49829247193..01c04be79cf 100644 --- a/crates/egui/src/containers/frame.rs +++ b/crates/egui/src/containers/frame.rs @@ -152,12 +152,6 @@ impl Frame { self } - #[deprecated = "Renamed inner_margin in egui 0.18"] - #[inline] - pub fn margin(self, margin: impl Into) -> Self { - self.inner_margin(margin) - } - #[inline] pub fn shadow(mut self, shadow: Shadow) -> Self { self.shadow = shadow; diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 66bb4a93081..cc0f28e90e5 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -209,15 +209,6 @@ impl<'open> Window<'open> { self } - #[deprecated = "Use `constrain_to` instead"] - #[inline] - pub fn drag_bounds(mut self, constrain_rect: Rect) -> Self { - #![allow(deprecated)] - - self.area = self.area.drag_bounds(constrain_rect); - self - } - /// Where the "root" of the window is. /// /// For instance, if you set this to [`Align2::RIGHT_TOP`] diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 503177d1ff4..c24a9d02ba7 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -707,16 +707,6 @@ impl Context { }) } - /// Read-write access to [`Fonts`]. - #[inline] - #[deprecated = "This function will be removed"] - pub fn fonts_mut(&self, writer: impl FnOnce(Option<&mut Fonts>) -> R) -> R { - self.write(move |ctx| { - let pixels_per_point = ctx.pixels_per_point(); - writer(ctx.fonts.get_mut(&pixels_per_point.into())) - }) - } - /// Read-only access to [`Options`]. #[inline] pub fn options(&self, reader: impl FnOnce(&Options) -> R) -> R { diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index 50073338b3c..e550c0a4717 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -572,15 +572,6 @@ impl Modifiers { command: false, }; - #[deprecated = "Use `Modifiers::ALT | Modifiers::SHIFT` instead"] - pub const ALT_SHIFT: Self = Self { - alt: true, - ctrl: false, - shift: true, - mac_cmd: false, - command: false, - }; - /// The Mac ⌘ Command key pub const MAC_CMD: Self = Self { alt: false, diff --git a/crates/egui/src/data/output.rs b/crates/egui/src/data/output.rs index 11275e7b800..f9ee018c3e4 100644 --- a/crates/egui/src/data/output.rs +++ b/crates/egui/src/data/output.rs @@ -111,14 +111,6 @@ pub struct PlatformOutput { } impl PlatformOutput { - /// Open the given url in a web browser. - /// - /// If egui is running in a browser, the same tab will be reused. - #[deprecated = "Use Context::open_url instead"] - pub fn open_url(&mut self, url: impl ToString) { - self.open_url = Some(OpenUrl::same_tab(url)); - } - /// This can be used by a text-to-speech system to describe the events (if any). pub fn events_description(&self) -> String { // only describe last event: diff --git a/crates/egui/src/id.rs b/crates/egui/src/id.rs index e588271d9d8..fe42d20fbfc 100644 --- a/crates/egui/src/id.rs +++ b/crates/egui/src/id.rs @@ -37,11 +37,6 @@ impl Id { /// though obviously it will lead to a lot of collisions if you do use it! pub const NULL: Self = Self(0); - #[deprecated = "Use Id::NULL"] - pub fn null() -> Self { - Self(0) - } - pub(crate) const fn background() -> Self { Self(1) } diff --git a/crates/egui/src/memory.rs b/crates/egui/src/memory.rs index fb15fdd3897..a96d8d973be 100644 --- a/crates/egui/src/memory.rs +++ b/crates/egui/src/memory.rs @@ -39,7 +39,7 @@ pub struct Memory { /// /// This will be saved between different program runs if you use the `persistence` feature. /// - /// To store a state common for all your widgets (a singleton), use [`Id::null`] as the key. + /// To store a state common for all your widgets (a singleton), use [`Id::NULL`] as the key. pub data: crate::util::IdTypeMap, // ------------------------------------------ @@ -666,21 +666,6 @@ impl Memory { } } - /// Set an event filter for a widget. - /// - /// You must first give focus to the widget before calling this. - #[deprecated = "Use set_focus_lock_filter instead"] - pub fn lock_focus(&mut self, id: Id, lock_focus: bool) { - self.set_focus_lock_filter( - id, - EventFilter { - tab: lock_focus, - arrows: lock_focus, - escape: false, - }, - ); - } - /// Give keyboard focus to a specific widget. /// See also [`crate::Response::request_focus`]. #[inline(always)] diff --git a/crates/egui/src/painter.rs b/crates/egui/src/painter.rs index a88862325a6..8376d16ed7b 100644 --- a/crates/egui/src/painter.rs +++ b/crates/egui/src/painter.rs @@ -83,16 +83,6 @@ impl Painter { pub(crate) fn set_invisible(&mut self) { self.fade_to_color = Some(Color32::TRANSPARENT); } - - #[deprecated = "Use Painter::with_clip_rect"] // Deprecated in 2022-04-18, before egui 0.18 - pub fn sub_region(&self, rect: Rect) -> Self { - Self { - ctx: self.ctx.clone(), - layer_id: self.layer_id, - clip_rect: rect.intersect(self.clip_rect), - fade_to_color: self.fade_to_color, - } - } } /// ## Accessors etc diff --git a/crates/egui/src/util/id_type_map.rs b/crates/egui/src/util/id_type_map.rs index 9e33849a991..d7af233ac25 100644 --- a/crates/egui/src/util/id_type_map.rs +++ b/crates/egui/src/util/id_type_map.rs @@ -318,7 +318,7 @@ use crate::Id; /// /// Values can either be "persisted" (serializable) or "temporary" (cleared when egui is shut down). /// -/// You can store state using the key [`Id::null`]. The state will then only be identified by its type. +/// You can store state using the key [`Id::NULL`]. The state will then only be identified by its type. /// /// ``` /// # use egui::{Id, util::IdTypeMap}; diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index d96a8a216f7..0b4bb0b9d9f 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -193,11 +193,6 @@ impl<'t> TextEdit<'t> { self } - #[deprecated = "Use .font(…) instead"] - pub fn text_style(self, text_style: TextStyle) -> Self { - self.font(text_style) - } - #[inline] pub fn text_color(mut self, text_color: Color32) -> Self { self.text_color = Some(text_color); diff --git a/crates/egui_extras/src/image.rs b/crates/egui_extras/src/image.rs index 1ad3cea6c7e..72be580d2c9 100644 --- a/crates/egui_extras/src/image.rs +++ b/crates/egui_extras/src/image.rs @@ -1,6 +1,6 @@ #![allow(deprecated)] -use egui::{mutex::Mutex, TextureFilter, TextureOptions}; +use egui::{mutex::Mutex, TextureOptions}; #[cfg(feature = "svg")] pub use usvg::FitTo; @@ -123,14 +123,6 @@ impl RetainedImage { self } - #[deprecated = "Use with_options instead"] - pub fn with_texture_filter(self, filter: TextureFilter) -> Self { - self.with_options(TextureOptions { - magnification: filter, - minification: filter, - }) - } - /// The size of the image data (number of pixels wide/high). pub fn size(&self) -> [usize; 2] { self.size diff --git a/crates/egui_extras/src/table.rs b/crates/egui_extras/src/table.rs index c456ba9645e..6659ed395ea 100644 --- a/crates/egui_extras/src/table.rs +++ b/crates/egui_extras/src/table.rs @@ -278,11 +278,6 @@ impl<'a> TableBuilder<'a> { self } - #[deprecated = "Renamed to vscroll"] - pub fn scroll(self, vscroll: bool) -> Self { - self.vscroll(vscroll) - } - /// Enables scrolling the table's contents using mouse drag (default: `true`). /// /// See [`ScrollArea::drag_to_scroll`] for more. diff --git a/crates/egui_glow/src/painter.rs b/crates/egui_glow/src/painter.rs index 2df12298c46..4f2d437f9aa 100644 --- a/crates/egui_glow/src/painter.rs +++ b/crates/egui_glow/src/painter.rs @@ -624,11 +624,6 @@ impl Painter { self.textures.get(&texture_id).copied() } - #[deprecated = "renamed 'texture'"] - pub fn get_texture(&self, texture_id: egui::TextureId) -> Option { - self.texture(texture_id) - } - #[allow(clippy::needless_pass_by_value)] // False positive pub fn register_native_texture(&mut self, native: glow::Texture) -> egui::TextureId { self.assert_not_destroyed(); diff --git a/crates/egui_plot/src/items/mod.rs b/crates/egui_plot/src/items/mod.rs index b49f6773cba..92d607570d8 100644 --- a/crates/egui_plot/src/items/mod.rs +++ b/crates/egui_plot/src/items/mod.rs @@ -574,20 +574,6 @@ impl Polygon { self } - #[deprecated = "Use `fill_color`."] - #[allow(unused, clippy::needless_pass_by_value)] - #[inline] - pub fn color(mut self, color: impl Into) -> Self { - self - } - - #[deprecated = "Use `fill_color`."] - #[allow(unused, clippy::needless_pass_by_value)] - #[inline] - pub fn fill_alpha(mut self, _alpha: impl Into) -> Self { - self - } - /// Fill color. Defaults to the stroke color with added transparency. #[inline] pub fn fill_color(mut self, color: impl Into) -> Self { diff --git a/crates/egui_plot/src/lib.rs b/crates/egui_plot/src/lib.rs index 61b902834e4..a10953311dc 100644 --- a/crates/egui_plot/src/lib.rs +++ b/crates/egui_plot/src/lib.rs @@ -1393,24 +1393,6 @@ impl PlotUi { &self.response } - /// Returns `true` if the plot area is currently hovered. - #[deprecated = "Use plot_ui.response().hovered()"] - pub fn plot_hovered(&self) -> bool { - self.response.hovered() - } - - /// Returns `true` if the plot was clicked by the primary button. - #[deprecated = "Use plot_ui.response().clicked()"] - pub fn plot_clicked(&self) -> bool { - self.response.clicked() - } - - /// Returns `true` if the plot was clicked by the secondary button. - #[deprecated = "Use plot_ui.response().secondary_clicked()"] - pub fn plot_secondary_clicked(&self) -> bool { - self.response.secondary_clicked() - } - /// The pointer position in plot coordinates. Independent of whether the pointer is in the plot area. pub fn pointer_coordinate(&self) -> Option { // We need to subtract the drag delta to keep in sync with the frame-delayed screen transform: diff --git a/crates/emath/src/rect.rs b/crates/emath/src/rect.rs index 69ff7d30384..e671135fce6 100644 --- a/crates/emath/src/rect.rs +++ b/crates/emath/src/rect.rs @@ -407,13 +407,6 @@ impl Rect { inside_dist + outside_dist } - /// Linearly interpolate so that `[0, 0]` is [`Self::min`] and - /// `[1, 1]` is [`Self::max`]. - #[deprecated = "Use `lerp_inside` instead"] - pub fn lerp(&self, t: Vec2) -> Pos2 { - self.lerp_inside(t) - } - /// Linearly interpolate so that `[0, 0]` is [`Self::min`] and /// `[1, 1]` is [`Self::max`]. pub fn lerp_inside(&self, t: Vec2) -> Pos2 { diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index 07e06cb3db6..8de1f0ffc1b 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -622,17 +622,6 @@ impl Rounding { } } - #[inline] - #[deprecated = "Use Rounding::ZERO"] - pub fn none() -> Self { - Self { - nw: 0.0, - ne: 0.0, - sw: 0.0, - se: 0.0, - } - } - /// Do all corners have the same rounding? #[inline] pub fn is_same(&self) -> bool { diff --git a/crates/epaint/src/stroke.rs b/crates/epaint/src/stroke.rs index 72f5a8cfddc..fca821b1ac0 100644 --- a/crates/epaint/src/stroke.rs +++ b/crates/epaint/src/stroke.rs @@ -4,7 +4,7 @@ use super::*; /// Describes the width and color of a line. /// -/// The default stroke is the same as [`Stroke::none`]. +/// The default stroke is the same as [`Stroke::NONE`]. #[derive(Clone, Copy, Debug, Default, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Stroke { @@ -19,12 +19,6 @@ impl Stroke { color: Color32::TRANSPARENT, }; - #[deprecated = "Use Stroke::NONE instead"] - #[inline(always)] - pub fn none() -> Self { - Self::new(0.0, Color32::TRANSPARENT) - } - #[inline] pub fn new(width: impl Into, color: impl Into) -> Self { Self { From c8dd3dd01a7ea40b95925b8de11ecfa981eaa738 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 12 Dec 2023 12:59:40 +0100 Subject: [PATCH 08/45] Update dependencies (#3700) Also disable `regex` `env_logger` feature in examples to improve compile times. --- Cargo.lock | 139 ++++++++------------- Cargo.toml | 1 + crates/egui_demo_app/Cargo.toml | 5 +- crates/egui_demo_lib/Cargo.toml | 2 +- crates/epaint/Cargo.toml | 2 +- deny.toml | 1 + examples/confirm_exit/Cargo.toml | 5 +- examples/custom_3d_glow/Cargo.toml | 5 +- examples/custom_font/Cargo.toml | 5 +- examples/custom_font_style/Cargo.toml | 5 +- examples/custom_window_frame/Cargo.toml | 5 +- examples/file_dialog/Cargo.toml | 5 +- examples/hello_world/Cargo.toml | 5 +- examples/hello_world_par/Cargo.toml | 5 +- examples/hello_world_simple/Cargo.toml | 5 +- examples/images/Cargo.toml | 5 +- examples/keyboard_events/Cargo.toml | 5 +- examples/multiple_viewports/Cargo.toml | 5 +- examples/puffin_profiler/Cargo.toml | 5 +- examples/save_plot/Cargo.toml | 5 +- examples/screenshot/Cargo.toml | 5 +- examples/serial_windows/Cargo.toml | 5 +- examples/test_inline_glow_paint/Cargo.toml | 5 +- examples/test_viewports/Cargo.toml | 5 +- examples/user_attention/Cargo.toml | 5 +- 25 files changed, 139 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 748b3265d14..ee39fbbaec7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -181,6 +181,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + [[package]] name = "anyhow" version = "1.0.75" @@ -437,17 +443,6 @@ dependencies = [ "zbus", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -734,25 +729,29 @@ checksum = "7a0e87cdf78571d9fbeff16861c37a006cd718d2433dc6d5b80beaae367d899a" [[package]] name = "clap" -version = "3.2.25" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ - "bitflags 1.3.2", - "clap_lex", - "indexmap 1.9.3", - "textwrap", + "clap_builder", ] [[package]] -name = "clap_lex" -version = "0.2.4" +name = "clap_builder" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ - "os_str_bytes", + "anstyle", + "clap_lex", ] +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + [[package]] name = "clipboard-win" version = "4.5.0" @@ -909,19 +908,19 @@ dependencies = [ [[package]] name = "criterion" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", - "atty", "cast", "ciborium", "clap", "criterion-plot", + "is-terminal", "itertools", - "lazy_static", "num-traits", + "once_cell", "oorandom", "regex", "serde", @@ -1021,9 +1020,12 @@ checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", +] [[package]] name = "derivative" @@ -1373,7 +1375,6 @@ dependencies = [ "humantime", "is-terminal", "log", - "regex", "termcolor", ] @@ -1884,7 +1885,7 @@ checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ "bitflags 2.4.0", "gpu-descriptor-types", - "hashbrown 0.14.0", + "hashbrown", ] [[package]] @@ -1922,15 +1923,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -1982,15 +1977,6 @@ dependencies = [ "env_logger", ] -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.3" @@ -2090,22 +2076,12 @@ checksum = "df19da1e92fbfec043ca97d622955381b1f3ee72a180ec999912df31b1ccd951" [[package]] name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown", ] [[package]] @@ -2126,7 +2102,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi", "libc", "windows-sys 0.48.0", ] @@ -2137,7 +2113,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi", "rustix 0.38.14", "windows-sys 0.48.0", ] @@ -2441,15 +2417,15 @@ dependencies = [ [[package]] name = "naga" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61d829abac9f5230a85d8cc83ec0879b4c09790208ae25b5ea031ef84562e071" +checksum = "ae585df4b6514cf8842ac0f1ab4992edc975892704835b549cf818dc0191249e" dependencies = [ "bit-set", "bitflags 2.4.0", "codespan-reporting", "hexf-parse", - "indexmap 2.0.0", + "indexmap", "log", "num-traits", "rustc-hash", @@ -2706,12 +2682,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "os_str_bytes" -version = "6.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" - [[package]] name = "owned_ttf_parser" version = "0.19.0" @@ -2811,12 +2781,12 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plist" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" +checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ "base64 0.21.4", - "indexmap 1.9.3", + "indexmap", "line-wrap", "quick-xml", "serde", @@ -2868,6 +2838,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2945,9 +2921,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" dependencies = [ "memchr", ] @@ -3612,12 +3588,6 @@ dependencies = [ "env_logger", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.49" @@ -3640,12 +3610,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -3743,7 +3714,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", diff --git a/Cargo.toml b/Cargo.toml index 7db8cec3594..134f3244180 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ opt-level = 2 [workspace.dependencies] +criterion = { version = "0.5.1", default-features = false } puffin = "0.18" raw-window-handle = "0.5.0" thiserror = "1.0.37" diff --git a/crates/egui_demo_app/Cargo.toml b/crates/egui_demo_app/Cargo.toml index b7c8e24d244..2d8d449c988 100644 --- a/crates/egui_demo_app/Cargo.toml +++ b/crates/egui_demo_app/Cargo.toml @@ -73,7 +73,10 @@ serde = { version = "1", optional = true, features = ["derive"] } # native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } rfd = { version = "0.11", optional = true } # web: diff --git a/crates/egui_demo_lib/Cargo.toml b/crates/egui_demo_lib/Cargo.toml index cfe9fd4c724..14189bc6787 100644 --- a/crates/egui_demo_lib/Cargo.toml +++ b/crates/egui_demo_lib/Cargo.toml @@ -52,7 +52,7 @@ serde = { version = "1", optional = true, features = ["derive"] } [dev-dependencies] -criterion = { version = "0.4", default-features = false } +criterion.workspace = true [[bench]] diff --git a/crates/epaint/Cargo.toml b/crates/epaint/Cargo.toml index 15f66fe5edb..e39b8f0193c 100644 --- a/crates/epaint/Cargo.toml +++ b/crates/epaint/Cargo.toml @@ -98,7 +98,7 @@ backtrace = { version = "0.3", optional = true } [dev-dependencies] -criterion = { version = "0.4", default-features = false } +criterion.workspace = true [[bench]] diff --git a/deny.toml b/deny.toml index 4536cd3f9f0..73f492c5d91 100644 --- a/deny.toml +++ b/deny.toml @@ -36,6 +36,7 @@ deny = [ skip = [ { name = "arrayvec" }, # old version via tiny-skiaz { name = "base64" }, # small crate, old version from usvg + { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … { name = "glow" }, # TODO(@wumpf): Old version use for glow backend right now, newer for wgpu. Updating this trickles out to updating winit. { name = "glutin_wgl_sys" }, # TODO(@wumpf): Old version use for glow backend right now, newer for wgpu. Updating this trickles out to updating winit. { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 diff --git a/examples/confirm_exit/Cargo.toml b/examples/confirm_exit/Cargo.toml index 1a7504775d3..2b0fae85dbd 100644 --- a/examples/confirm_exit/Cargo.toml +++ b/examples/confirm_exit/Cargo.toml @@ -12,4 +12,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/custom_3d_glow/Cargo.toml b/examples/custom_3d_glow/Cargo.toml index 849faa5c463..0d268759c27 100644 --- a/examples/custom_3d_glow/Cargo.toml +++ b/examples/custom_3d_glow/Cargo.toml @@ -12,4 +12,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/custom_font/Cargo.toml b/examples/custom_font/Cargo.toml index c87ad9dadab..0eec65cd4d5 100644 --- a/examples/custom_font/Cargo.toml +++ b/examples/custom_font/Cargo.toml @@ -12,4 +12,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/custom_font_style/Cargo.toml b/examples/custom_font_style/Cargo.toml index 45ee60c352b..a429303bb07 100644 --- a/examples/custom_font_style/Cargo.toml +++ b/examples/custom_font_style/Cargo.toml @@ -12,4 +12,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/custom_window_frame/Cargo.toml b/examples/custom_window_frame/Cargo.toml index d518f615fd2..6dc91e4e503 100644 --- a/examples/custom_window_frame/Cargo.toml +++ b/examples/custom_window_frame/Cargo.toml @@ -12,4 +12,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/file_dialog/Cargo.toml b/examples/file_dialog/Cargo.toml index 3d5cc6d8af9..0ad331dff06 100644 --- a/examples/file_dialog/Cargo.toml +++ b/examples/file_dialog/Cargo.toml @@ -12,5 +12,8 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } rfd = "0.11" diff --git a/examples/hello_world/Cargo.toml b/examples/hello_world/Cargo.toml index 9e846a2362e..fb2ca48c920 100644 --- a/examples/hello_world/Cargo.toml +++ b/examples/hello_world/Cargo.toml @@ -16,4 +16,7 @@ eframe = { path = "../../crates/eframe", features = [ # For image support: egui_extras = { path = "../../crates/egui_extras", features = ["image"] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/hello_world_par/Cargo.toml b/examples/hello_world_par/Cargo.toml index 65fb00646da..46d6aa741e9 100644 --- a/examples/hello_world_par/Cargo.toml +++ b/examples/hello_world_par/Cargo.toml @@ -14,4 +14,7 @@ eframe = { path = "../../crates/eframe", default-features = false, features = [ "default_fonts", "wgpu", ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/hello_world_simple/Cargo.toml b/examples/hello_world_simple/Cargo.toml index b08fc3857c6..36efecdda60 100644 --- a/examples/hello_world_simple/Cargo.toml +++ b/examples/hello_world_simple/Cargo.toml @@ -12,4 +12,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/images/Cargo.toml b/examples/images/Cargo.toml index d400e1087a6..bddc9afbfaa 100644 --- a/examples/images/Cargo.toml +++ b/examples/images/Cargo.toml @@ -13,7 +13,10 @@ eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } egui_extras = { path = "../../crates/egui_extras", features = ["all_loaders"] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } image = { version = "0.24", default-features = false, features = [ "jpeg", "png", diff --git a/examples/keyboard_events/Cargo.toml b/examples/keyboard_events/Cargo.toml index e38260ec38c..4f12579e03d 100644 --- a/examples/keyboard_events/Cargo.toml +++ b/examples/keyboard_events/Cargo.toml @@ -12,4 +12,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/multiple_viewports/Cargo.toml b/examples/multiple_viewports/Cargo.toml index 81e7c7b2d28..18898bdc0a8 100644 --- a/examples/multiple_viewports/Cargo.toml +++ b/examples/multiple_viewports/Cargo.toml @@ -12,4 +12,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/puffin_profiler/Cargo.toml b/examples/puffin_profiler/Cargo.toml index d6098685ea5..d1329f9f31b 100644 --- a/examples/puffin_profiler/Cargo.toml +++ b/examples/puffin_profiler/Cargo.toml @@ -17,6 +17,9 @@ eframe = { path = "../../crates/eframe", features = [ "puffin", "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } puffin = "0.18" puffin_http = "0.15" diff --git a/examples/save_plot/Cargo.toml b/examples/save_plot/Cargo.toml index 04e337020c7..49a46f58094 100644 --- a/examples/save_plot/Cargo.toml +++ b/examples/save_plot/Cargo.toml @@ -14,4 +14,7 @@ eframe = { path = "../../crates/eframe", features = [ egui_plot = { path = "../../crates/egui_plot" } image = { version = "0.24", default-features = false, features = ["png"] } rfd = "0.11.0" -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/screenshot/Cargo.toml b/examples/screenshot/Cargo.toml index b2a521cac06..f7a65a8bf3f 100644 --- a/examples/screenshot/Cargo.toml +++ b/examples/screenshot/Cargo.toml @@ -16,5 +16,8 @@ eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO "wgpu", ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } image = { version = "0.24", default-features = false, features = ["png"] } diff --git a/examples/serial_windows/Cargo.toml b/examples/serial_windows/Cargo.toml index f8b0388a666..e3ba3159a92 100644 --- a/examples/serial_windows/Cargo.toml +++ b/examples/serial_windows/Cargo.toml @@ -12,4 +12,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/test_inline_glow_paint/Cargo.toml b/examples/test_inline_glow_paint/Cargo.toml index 4fabb8be269..472935ed5bf 100644 --- a/examples/test_inline_glow_paint/Cargo.toml +++ b/examples/test_inline_glow_paint/Cargo.toml @@ -11,4 +11,7 @@ publish = false [dependencies] eframe = { path = "../../crates/eframe" } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/test_viewports/Cargo.toml b/examples/test_viewports/Cargo.toml index 132f98cc7d7..9ebebae205f 100644 --- a/examples/test_viewports/Cargo.toml +++ b/examples/test_viewports/Cargo.toml @@ -14,4 +14,7 @@ wgpu = ["eframe/wgpu"] eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/user_attention/Cargo.toml b/examples/user_attention/Cargo.toml index 01ebe116f43..91103635d20 100644 --- a/examples/user_attention/Cargo.toml +++ b/examples/user_attention/Cargo.toml @@ -11,4 +11,7 @@ publish = false eframe = { path = "../../crates/eframe", features = [ "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } -env_logger = "0.10" +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } From 60f46f7c40b2c535e8ad70e9b4f740623568e694 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:47:20 +0100 Subject: [PATCH 09/45] Bump zerocopy from 0.7.21 to 0.7.31 (#3707) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [zerocopy](https://github.com/google/zerocopy) from 0.7.21 to 0.7.31.
Release notes

Sourced from zerocopy's releases.

v0.7.30

What's Changed

Full Changelog: https://github.com/google/zerocopy/compare/v0.7.29...v0.7.30

v0.7.29

What's Changed

Full Changelog: https://github.com/google/zerocopy/compare/v0.7.28...v0.7.29

v0.7.28

What's Changed

Full Changelog: https://github.com/google/zerocopy/compare/v0.7.27...v0.7.28

v0.7.27

What's Changed

... (truncated)

Changelog

Sourced from zerocopy's changelog.

Changelog

Releases

We track releases and release notes using GitHub Releases.

Yanks and Regressions

0.7.27, 0.7.28

These versions were briefly yanked due to a non-soundness regression reported in #672pull-672. After reconsidering our yanking policy in #679issue-679, we un-yanked these versions.

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zerocopy&package-manager=cargo&previous-version=0.7.21&new-version=0.7.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/emilk/egui/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee39fbbaec7..51e854eb474 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4654,18 +4654,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.21" +version = "0.7.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686b7e407015242119c33dab17b8f61ba6843534de936d94368856528eae4dcc" +checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.21" +version = "0.7.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020f3dfe25dfc38dfea49ce62d5d45ecdd7f0d8a724fa63eb36b6eba4ec76806" +checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" dependencies = [ "proc-macro2", "quote", From 3361d399fb2adf9c486a9a1c0a1ff73114c9b23d Mon Sep 17 00:00:00 2001 From: zeozeozeo <108888572+zeozeozeo@users.noreply.github.com> Date: Mon, 18 Dec 2023 13:48:16 +0500 Subject: [PATCH 10/45] derive serde Serialize and Deserialize on `KeyboardShortcut` (#3694) This is a very simple change so you don't have to do this: https://github.com/zeozeozeo/egui-keybind/blob/master/src/bind.rs#L149-L172 --- crates/egui/src/data/input.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index e550c0a4717..0c10f79078f 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -1060,6 +1060,7 @@ impl Key { /// Can be used with [`crate::InputState::consume_shortcut`] /// and [`crate::Context::format_shortcut`]. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct KeyboardShortcut { pub modifiers: Modifiers, pub key: Key, From 81a47066c6713005555d55314f9febfddc85f42b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 18 Dec 2023 10:07:50 +0100 Subject: [PATCH 11/45] Add link to `egui-ash` in README.md Closes https://github.com/emilk/egui/issues/3706 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 59d63e16579..814c3bc23d7 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,7 @@ These are the official egui integrations: ### 3rd party integrations * [`amethyst_egui`](https://github.com/jgraef/amethyst_egui) for [the Amethyst game engine](https://amethyst.rs/). +* [`egui-ash`](https://github.com/MatchaChoco010/egui-ash) for [`ash`](https://github.com/ash-rs/ash) (a very lightweight wrapper around Vulkan). * [`bevy_egui`](https://github.com/mvlabat/bevy_egui) for [the Bevy game engine](https://bevyengine.org/). * [`egui_glfw_gl`](https://github.com/cohaereo/egui_glfw_gl) for [GLFW](https://crates.io/crates/glfw). * [`egui_glium`](https://github.com/fayalalebrun/egui_glium) for compiling native apps with [Glium](https://github.com/glium/glium). From 8503a851138c36d76289202d603c23229558b890 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 18 Dec 2023 12:47:23 +0100 Subject: [PATCH 12/45] Bug fix: framebuffer clear when using glow with multi-viewports (#3713) I broke this in https://github.com/emilk/egui/pull/3665 For some reason, when using multiple viewports, the first "clear" doesn't take. I don't have time to investigate further, so I am adding a hack here which: * For single-viewport apps, the clear is done before `App::update` so user can paint there * For multi-viewport apps, the clear is done after `App::update`, so that it works This means painting in `App::update` won't work with multi-viewports. Ideally, users should use paint callbacks instead of painting directly in `App::update` anyways. --- crates/eframe/src/native/glow_integration.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index bc3bb1e174c..967ff640ffa 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -532,12 +532,16 @@ impl GlowWinitRunning { (raw_input, viewport_ui_cb) }; - { + let clear_color = self + .app + .clear_color(&self.integration.egui_ctx.style().visuals); + + let has_many_viewports = self.glutin.borrow().viewports.len() > 1; + let clear_before_update = !has_many_viewports; // HACK: for some reason, an early clear doesn't "take" on Mac with multiple viewports. + + if clear_before_update { // clear before we call update, so users can paint between clear-color and egui windows: - let clear_color = self - .app - .clear_color(&self.integration.egui_ctx.style().visuals); let mut glutin = self.glutin.borrow_mut(); let GlutinWindowContext { viewports, @@ -608,6 +612,10 @@ impl GlowWinitRunning { let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); + if !clear_before_update { + painter.clear(screen_size_in_pixels, clear_color); + } + painter.paint_and_update_textures( screen_size_in_pixels, pixels_per_point, From 8e5959d55d2f5799a365dd2474b8868b7d49ad14 Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Mon, 18 Dec 2023 14:53:14 +0100 Subject: [PATCH 13/45] Update to winit 0.29 (#3649) * Closes https://github.com/emilk/egui/issues/3542 * Closes https://github.com/emilk/egui/issues/2977 * Closes https://github.com/emilk/egui/issues/3303 --------- Co-authored-by: Emil Ernerfeldt --- .typos.toml | 3 +- Cargo.lock | 637 +++++++++++------- crates/eframe/Cargo.toml | 8 +- crates/eframe/src/epi.rs | 2 +- crates/eframe/src/lib.rs | 5 + crates/eframe/src/native/epi_integration.rs | 2 +- crates/eframe/src/native/glow_integration.rs | 177 ++--- crates/eframe/src/native/run.rs | 230 ++++--- crates/eframe/src/native/wgpu_integration.rs | 63 +- crates/eframe/src/native/winit_integration.rs | 14 +- crates/eframe/src/web/app_runner.rs | 12 +- crates/eframe/src/web/events.rs | 2 + crates/eframe/src/web/input.rs | 86 +-- crates/eframe/src/web/text_agent.rs | 8 +- crates/egui-wgpu/Cargo.toml | 2 +- crates/egui-winit/Cargo.toml | 6 +- crates/egui-winit/src/lib.rs | 461 ++++++++----- crates/egui/src/data/input.rs | 265 +++++++- crates/egui/src/data/output.rs | 25 +- crates/egui/src/viewport.rs | 3 +- crates/egui/src/widgets/text_edit/builder.rs | 21 +- crates/egui_extras/Cargo.toml | 4 +- crates/egui_extras/src/image.rs | 3 + crates/egui_extras/src/loaders/svg_loader.rs | 5 +- crates/egui_glow/Cargo.toml | 4 +- crates/egui_glow/examples/pure_glow.rs | 55 +- crates/egui_glow/src/winit.rs | 2 +- deny.toml | 32 +- examples/multiple_viewports/Cargo.toml | 2 + 29 files changed, 1309 insertions(+), 830 deletions(-) diff --git a/.typos.toml b/.typos.toml index 46d137138ea..6d856495178 100644 --- a/.typos.toml +++ b/.typos.toml @@ -3,8 +3,7 @@ # run: typos [default.extend-words] -nknown = "nknown" # part of @55nknown username -Prefence = "Prefence" # typo in glutin_winit API +nknown = "nknown" # part of @55nknown username [files] extend-exclude = ["web_demo/egui_demo_app.js"] # auto-generated diff --git a/Cargo.lock b/Cargo.lock index 51e854eb474..6137494b7b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,9 +20,9 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0cc53b7e5d8f45ebe687178cf91af0f45fdba6e78fedf94f0269c5be5b9f296" +checksum = "ca8410747ed85a17c4a1e9ed3f5a74d3e7bdcc876cf9a18ff40ae21d645997b2" dependencies = [ "enumn", "serde", @@ -30,30 +30,30 @@ dependencies = [ [[package]] name = "accesskit_consumer" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39dfcfd32eb0c1b525daaf4b02adcd2fa529c22cd713491e15bf002a01a714f5" +checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6" dependencies = [ "accesskit", ] [[package]] name = "accesskit_macos" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c7e8406319ac3149d7b59983637984f0864bbf738319b1c443976268b6426c" +checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7" dependencies = [ "accesskit", "accesskit_consumer", - "objc2", + "objc2 0.3.0-beta.3.patch-leaks.3", "once_cell", ] [[package]] name = "accesskit_unix" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0c84552a7995c981d5f22e2d4b24ba9a55718bb12fba883506d6d7344acaf1" +checksum = "6c8c9b4467d77cacfbc93cee9aa8e7822f6d527c774efdca5f8b3a5280c34847" dependencies = [ "accesskit", "accesskit_consumer", @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "314d4a797fc82d182b04f4f0665a368924fb556ad9557fccd2d39d38dc8c1c1b" +checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b" dependencies = [ "accesskit", "accesskit_consumer", @@ -82,9 +82,9 @@ dependencies = [ [[package]] name = "accesskit_winit" -version = "0.15.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88e39fcec2e10971e188730b7a76bab60647dacc973d4591855ebebcadfaa738" +checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67" dependencies = [ "accesskit", "accesskit_macos", @@ -115,6 +115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", + "getrandom", "once_cell", "serde", "version_check", @@ -138,13 +139,15 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "android-activity" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c77a0045eda8b888c76ea473c2b0515ba6f471d318f8927c5c72240937035a6" +checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d" dependencies = [ "android-properties", - "bitflags 1.3.2", + "bitflags 2.4.0", "cc", + "cesu8", + "jni", "jni-sys", "libc", "log", @@ -152,6 +155,7 @@ dependencies = [ "ndk-context", "ndk-sys", "num_enum", + "thiserror", ] [[package]] @@ -195,9 +199,9 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arboard" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac57f2b058a76363e357c056e4f74f1945bf734d37b8b3ef49066c4787dde0fc" +checksum = "aafb29b107435aa276664c1db8954ac27a6e105cdad3c88287a199eb0e313c08" dependencies = [ "clipboard-win", "log", @@ -222,6 +226,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5f312b0a56c5cdf967c0aeb67f6289603354951683bc97ddc595ab974ba9aa" + [[package]] name = "ash" version = "0.37.3+1.3.251" @@ -291,7 +301,7 @@ dependencies = [ "futures-lite", "log", "parking", - "polling", + "polling 2.8.0", "rustix 0.37.25", "slab", "socket2", @@ -326,7 +336,7 @@ dependencies = [ "cfg-if", "event-listener 3.0.0", "futures-lite", - "rustix 0.38.14", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -536,7 +546,16 @@ version = "0.1.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" dependencies = [ - "objc-sys", + "objc-sys 0.2.0-beta.2", +] + +[[package]] +name = "block-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd7cf50912cddc06dc5ea7c08c5e81c1b2c842a70d19def1848d54c586fed92" +dependencies = [ + "objc-sys 0.3.1", ] [[package]] @@ -545,8 +564,18 @@ version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" dependencies = [ - "block-sys", - "objc2-encode", + "block-sys 0.1.0-beta.1", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "block2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" +dependencies = [ + "block-sys 0.2.0", + "objc2 0.4.1", ] [[package]] @@ -615,16 +644,28 @@ dependencies = [ [[package]] name = "calloop" -version = "0.10.6" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "log", - "nix 0.25.1", - "slotmap", + "polling 3.3.0", + "rustix 0.38.21", + "slab", "thiserror", - "vec_map", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix 0.38.21", + "wayland-backend", + "wayland-client", ] [[package]] @@ -765,16 +806,16 @@ dependencies = [ [[package]] name = "cocoa" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" dependencies = [ "bitflags 1.3.2", "block", "cocoa-foundation", "core-foundation", "core-graphics", - "foreign-types 0.3.2", + "foreign-types", "libc", "objc", ] @@ -866,14 +907,14 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" dependencies = [ "bitflags 1.3.2", "core-foundation", "core-graphics-types", - "foreign-types 0.3.2", + "foreign-types", "libc", ] @@ -969,6 +1010,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + [[package]] name = "custom_3d_glow" version = "0.1.0" @@ -1133,7 +1180,7 @@ dependencies = [ "percent-encoding", "pollster", "puffin", - "raw-window-handle", + "raw-window-handle 0.5.2", "ron", "serde", "static_assertions", @@ -1188,7 +1235,7 @@ dependencies = [ "egui", "log", "puffin", - "raw-window-handle", + "raw-window-handle 0.5.2", "serde", "smithay-clipboard", "web-time", @@ -1251,8 +1298,6 @@ dependencies = [ "resvg", "serde", "syntect", - "tiny-skia", - "usvg", ] [[package]] @@ -1267,9 +1312,9 @@ dependencies = [ "glutin", "glutin-winit", "log", - "memoffset 0.7.1", + "memoffset", "puffin", - "raw-window-handle", + "raw-window-handle 0.5.2", "wasm-bindgen", "web-sys", ] @@ -1527,15 +1572,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -1543,7 +1579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] @@ -1557,12 +1593,6 @@ dependencies = [ "syn 2.0.37", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1675,9 +1705,9 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177" dependencies = [ "libc", "winapi", @@ -1762,68 +1792,60 @@ dependencies = [ [[package]] name = "glutin" -version = "0.30.10" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc93b03242719b8ad39fb26ed2b01737144ce7bd4bfc7adadcef806596760fe" +checksum = "eca18d477e18c996c1fd1a50e04c6a745b67e2d512c7fb51f2757d9486a0e3ee" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cfg_aliases", "cgl", "core-foundation", "dispatch", "glutin_egl_sys", "glutin_glx_sys", - "glutin_wgl_sys 0.4.0", - "libloading 0.7.4", - "objc2", + "glutin_wgl_sys", + "icrate", + "libloading 0.8.0", + "objc2 0.4.1", "once_cell", - "raw-window-handle", - "wayland-sys 0.30.1", - "windows-sys 0.45.0", + "raw-window-handle 0.5.2", + "wayland-sys", + "windows-sys 0.48.0", "x11-dl", ] [[package]] name = "glutin-winit" -version = "0.3.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629a873fc04062830bfe8f97c03773bcd7b371e23bcc465d0a61448cd1588fa4" +checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" dependencies = [ "cfg_aliases", "glutin", - "raw-window-handle", + "raw-window-handle 0.5.2", "winit", ] [[package]] name = "glutin_egl_sys" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af784eb26c5a68ec85391268e074f0aa618c096eadb5d6330b0911cf34fe57c5" +checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" dependencies = [ "gl_generator", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "glutin_glx_sys" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b53cb5fe568964aa066a3ba91eac5ecbac869fb0842cd0dc9e412434f1a1494" +checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" dependencies = [ "gl_generator", "x11-dl", ] -[[package]] -name = "glutin_wgl_sys" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef89398e90033fc6bc65e9bd42fd29bbbfd483bda5b56dc5562f455550618165" -dependencies = [ - "gl_generator", -] - [[package]] name = "glutin_wgl_sys" version = "0.5.0" @@ -2033,6 +2055,17 @@ dependencies = [ "cc", ] +[[package]] +name = "icrate" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2 0.3.0", + "dispatch", + "objc2 0.4.1", +] + [[package]] name = "idna" version = "0.4.0" @@ -2091,9 +2124,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", ] [[package]] @@ -2114,7 +2144,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.14", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -2213,17 +2243,11 @@ dependencies = [ "arrayvec", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -2268,9 +2292,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "litrs" @@ -2317,22 +2341,13 @@ checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memmap2" -version = "0.5.10" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.7.1" @@ -2351,7 +2366,7 @@ dependencies = [ "bitflags 2.4.0", "block", "core-graphics-types", - "foreign-types 0.5.0", + "foreign-types", "log", "objc", "paste", @@ -2395,18 +2410,6 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "multiple_viewports" version = "0.1.0" @@ -2446,15 +2449,17 @@ dependencies = [ [[package]] name = "ndk" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "jni-sys", + "log", "ndk-sys", "num_enum", - "raw-window-handle", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "thiserror", ] @@ -2466,38 +2471,13 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.4.1+23.1.7779620" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ "jni-sys", ] -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - [[package]] name = "nix" version = "0.26.4" @@ -2507,7 +2487,7 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset 0.7.1", + "memoffset", ] [[package]] @@ -2558,23 +2538,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.37", ] [[package]] @@ -2604,15 +2584,31 @@ version = "0.2.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" +[[package]] +name = "objc-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e1d07c6eab1ce8b6382b8e3c7246fe117ff3f8b34be065f5ebace6749fe845" + [[package]] name = "objc2" version = "0.3.0-beta.3.patch-leaks.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" dependencies = [ - "block2", - "objc-sys", - "objc2-encode", + "block2 0.2.0-alpha.6", + "objc-sys 0.2.0-beta.2", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys 0.3.1", + "objc2-encode 3.0.0", ] [[package]] @@ -2621,9 +2617,15 @@ version = "2.0.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" dependencies = [ - "objc-sys", + "objc-sys 0.2.0-beta.2", ] +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + [[package]] name = "objc_exception" version = "0.1.2" @@ -2788,7 +2790,7 @@ dependencies = [ "base64 0.21.4", "indexmap", "line-wrap", - "quick-xml", + "quick-xml 0.31.0", "serde", "time", ] @@ -2832,6 +2834,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "polling" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.21", + "tracing", + "windows-sys 0.48.0", +] + [[package]] name = "pollster" version = "0.3.0" @@ -2919,6 +2935,15 @@ dependencies = [ "puffin_http", ] +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.31.0" @@ -2979,6 +3004,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + [[package]] name = "rctree" version = "0.5.0" @@ -3059,7 +3090,7 @@ dependencies = [ "pico-args", "rgb", "svgtypes", - "tiny-skia", + "tiny-skia 0.8.4", "usvg", ] @@ -3081,7 +3112,7 @@ dependencies = [ "objc", "objc-foundation", "objc_id", - "raw-window-handle", + "raw-window-handle 0.5.2", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -3161,14 +3192,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys 0.4.7", + "linux-raw-sys 0.4.11", "windows-sys 0.48.0", ] @@ -3259,15 +3290,15 @@ dependencies = [ [[package]] name = "sctk-adwaita" -version = "0.5.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +checksum = "1729a30a469de249c6effc17ec8d039b0aa29b3af79b819b7f51cb6ab8046a90" dependencies = [ "ab_glyph", "log", "memmap2", "smithay-client-toolkit", - "tiny-skia", + "tiny-skia 0.11.2", ] [[package]] @@ -3396,31 +3427,47 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "smithay-client-toolkit" -version = "0.16.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" +checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "calloop", - "dlib", - "lazy_static", + "calloop-wayland-source", + "cursor-icon", + "libc", "log", "memmap2", - "nix 0.24.3", - "pkg-config", + "rustix 0.38.21", + "thiserror", + "wayland-backend", "wayland-client", + "wayland-csd-frame", "wayland-cursor", "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", ] [[package]] name = "smithay-clipboard" -version = "0.6.6" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" +checksum = "0bb62b280ce5a5cba847669933a0948d00904cf83845c944eae96a4738cea1a6" dependencies = [ + "libc", "smithay-client-toolkit", - "wayland-client", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", ] [[package]] @@ -3559,7 +3606,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.3.5", - "rustix 0.38.14", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -3648,7 +3695,21 @@ dependencies = [ "bytemuck", "cfg-if", "png", - "tiny-skia-path", + "tiny-skia-path 0.8.4", +] + +[[package]] +name = "tiny-skia" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b72a92a05db376db09fe6d50b7948d106011761c05a6a45e23e17ee9b556222" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path 0.11.2", ] [[package]] @@ -3662,6 +3723,17 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tiny-skia-path" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac3865b9708fc7e1961a65c3a4fa55e984272f33092d3c859929f887fceb647" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -3814,6 +3886,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.11" @@ -3893,12 +3971,6 @@ dependencies = [ "svgtypes", ] -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version-compare" version = "0.1.1" @@ -4000,87 +4072,111 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] -name = "wayland-client" -version = "0.29.5" +name = "wayland-backend" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" dependencies = [ - "bitflags 1.3.2", + "cc", "downcast-rs", - "libc", - "nix 0.24.3", + "nix", "scoped-tls", - "wayland-commons", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" +dependencies = [ + "bitflags 2.4.0", + "nix", + "wayland-backend", "wayland-scanner", - "wayland-sys 0.29.5", ] [[package]] -name = "wayland-commons" -version = "0.29.5" +name = "wayland-csd-frame" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys 0.29.5", + "bitflags 2.4.0", + "cursor-icon", + "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.29.5" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b" dependencies = [ - "nix 0.24.3", + "nix", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.29.5" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", + "wayland-backend", "wayland-client", - "wayland-commons", "wayland-scanner", ] [[package]] -name = "wayland-scanner" -version = "0.29.5" +name = "wayland-protocols-plasma" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" dependencies = [ - "proc-macro2", - "quote", - "xml-rs", + "bitflags 2.4.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", ] [[package]] -name = "wayland-sys" -version = "0.29.5" +name = "wayland-protocols-wlr" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "dlib", - "lazy_static", - "pkg-config", + "bitflags 2.4.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" +dependencies = [ + "proc-macro2", + "quick-xml 0.30.0", + "quote", ] [[package]] name = "wayland-sys" -version = "0.30.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", - "lazy_static", "log", + "once_cell", "pkg-config", ] @@ -4117,7 +4213,7 @@ dependencies = [ "log", "ndk-context", "objc", - "raw-window-handle", + "raw-window-handle 0.5.2", "url", "web-sys", ] @@ -4142,7 +4238,7 @@ dependencies = [ "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "smallvec", "static_assertions", "wasm-bindgen", @@ -4167,7 +4263,7 @@ dependencies = [ "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "rustc-hash", "smallvec", "thiserror", @@ -4191,7 +4287,7 @@ dependencies = [ "core-graphics-types", "d3d12", "glow 0.13.0", - "glutin_wgl_sys 0.5.0", + "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", "gpu-descriptor", @@ -4208,7 +4304,7 @@ dependencies = [ "parking_lot", "profiling", "range-alloc", - "raw-window-handle", + "raw-window-handle 0.5.2", "renderdoc-sys", "rustc-hash", "smallvec", @@ -4471,37 +4567,51 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winit" -version = "0.28.7" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" +checksum = "d25d662bb83b511acd839534bb2d88521b0bbc81440969cb077d23c4db9e62c7" dependencies = [ + "ahash", "android-activity", - "bitflags 1.3.2", + "atomic-waker", + "bitflags 2.4.0", + "bytemuck", + "calloop", "cfg_aliases", "core-foundation", "core-graphics", - "dispatch", - "instant", + "cursor-icon", + "icrate", + "js-sys", "libc", "log", - "mio", + "memmap2", "ndk", - "objc2", + "ndk-sys", + "objc2 0.4.1", "once_cell", "orbclient", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "redox_syscall 0.3.5", + "rustix 0.38.21", "sctk-adwaita", "smithay-client-toolkit", + "smol_str", + "unicode-segmentation", "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", "wayland-client", - "wayland-commons", "wayland-protocols", - "wayland-scanner", + "wayland-protocols-plasma", "web-sys", - "windows-sys 0.45.0", + "web-time", + "windows-sys 0.48.0", "x11-dl", + "x11rb", + "xkbcommon-dl", ] [[package]] @@ -4526,12 +4636,16 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.10.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" +checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" dependencies = [ + "as-raw-xcb-connection", "gethostname", - "nix 0.24.3", + "libc", + "libloading 0.7.4", + "nix", + "once_cell", "winapi", "winapi-wsapoll", "x11rb-protocol", @@ -4539,11 +4653,11 @@ dependencies = [ [[package]] name = "x11rb-protocol" -version = "0.10.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" +checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc" dependencies = [ - "nix 0.24.3", + "nix", ] [[package]] @@ -4561,10 +4675,29 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" dependencies = [ - "nix 0.26.4", + "nix", "winapi", ] +[[package]] +name = "xkbcommon-dl" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699" +dependencies = [ + "bitflags 2.4.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + [[package]] name = "xml-rs" version = "0.8.19" @@ -4610,7 +4743,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix 0.26.4", + "nix", "once_cell", "ordered-stream", "rand", diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 68c0bfd7bd8..e9f360802d4 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -131,7 +131,7 @@ image = { version = "0.24", default-features = false, features = [ "png", ] } # Needed for app icon raw-window-handle.workspace = true -winit = { version = "0.28.1", default-features = false } +winit = { version = "0.29.4", default-features = false, features = ["rwh_05"] } # optional native: directories-next = { version = "2", optional = true } @@ -142,14 +142,14 @@ pollster = { version = "0.3", optional = true } # needed for wgpu # we can expose these to user so that they can select which backends they want to enable to avoid compiling useless deps. # this can be done at the same time we expose x11/wayland features of winit crate. -glutin = { version = "0.30", optional = true } -glutin-winit = { version = "0.3.0", optional = true } +glutin = { version = "0.31", optional = true } +glutin-winit = { version = "0.4", optional = true } puffin = { workspace = true, optional = true } wgpu = { workspace = true, optional = true } # mac: [target.'cfg(any(target_os = "macos"))'.dependencies] -cocoa = "0.24.1" # Stuck on old version until we update to winit 0.29 +cocoa = "0.25.0" objc = "0.2.7" # windows: diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 5748d4875ca..95ed9feaccd 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -298,7 +298,7 @@ pub struct NativeOptions { /// /// This feature was introduced in . /// - /// When `true`, [`winit::platform::run_return::EventLoopExtRunReturn::run_return`] is used. + /// When `true`, [`winit::platform::run_on_demand::EventLoopExtRunOnDemand`] is used. /// When `false`, [`winit::event_loop::EventLoop::run`] is used. pub run_and_return: bool, diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index fd6437e305e..de226bc8e0a 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -325,6 +325,11 @@ pub enum Error { #[error("winit error: {0}")] Winit(#[from] winit::error::OsError), + /// An error from [`winit::event_loop::EventLoop`]. + #[cfg(not(target_arch = "wasm32"))] + #[error("winit EventLoopError: {0}")] + WinitEventLoop(#[from] winit::error::EventLoopError), + /// An error from [`glutin`] when using [`glow`]. #[cfg(all(feature = "glow", not(target_arch = "wasm32")))] #[error("glutin error: {0}")] diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 7ad2f9dfdd1..e80785b5d37 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -231,7 +231,7 @@ impl EpiIntegration { &mut self, window: &winit::window::Window, egui_winit: &mut egui_winit::State, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResponse { crate::profile_function!(egui_winit::short_window_event_description(event)); diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 967ff640ffa..cc77972be97 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -1,8 +1,17 @@ +//! Note that this file contains code very similar to [`wgpu_integration`]. +//! When making changes to one you often also want to apply it to the other. +//! +//! This is also very complex code, and not very pretty. +//! There is a bunch of improvements we could do, +//! like removing a bunch of `unwraps`. + use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant}; use glutin::{ + config::GlConfig, + context::NotCurrentGlContext, display::GetGlDisplay, - prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext}, + prelude::{GlDisplay, PossiblyCurrentGlContext}, surface::GlSurface, }; use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _}; @@ -112,6 +121,8 @@ struct Viewport { /// None for immediate viewports. viewport_ui_cb: Option>, + // These three live and die together. + // TODO(emilk): clump them together into one struct! gl_surface: Option>, window: Option>, egui_winit: Option, @@ -160,12 +171,12 @@ impl GlowWinitApp { }; // Creates the window - must come before we create our glow context - glutin_window_context.on_resume(event_loop)?; + glutin_window_context.initialize_window(ViewportId::ROOT, event_loop)?; - if let Some(viewport) = glutin_window_context.viewports.get(&ViewportId::ROOT) { - if let Some(window) = &viewport.window { - epi_integration::apply_window_settings(window, window_settings); - } + { + let viewport = &glutin_window_context.viewports[&ViewportId::ROOT]; + let window = viewport.window.as_ref().unwrap(); // Can't fail - we just called `initialize_all_viewports` + epi_integration::apply_window_settings(window, window_settings); } let gl = unsafe { @@ -390,9 +401,13 @@ impl WinitApp for GlowWinitApp { } } - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + fn run_ui_and_paint( + &mut self, + event_loop: &EventLoopWindowTarget, + window_id: WindowId, + ) -> EventResult { if let Some(running) = &mut self.running { - running.run_ui_and_paint(window_id) + running.run_ui_and_paint(event_loop, window_id) } else { EventResult::Wait } @@ -401,29 +416,27 @@ impl WinitApp for GlowWinitApp { fn on_event( &mut self, event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, UserEvent>, + event: &winit::event::Event, ) -> Result { crate::profile_function!(winit_integration::short_event_description(event)); Ok(match event { winit::event::Event::Resumed => { + log::debug!("Event::Resumed"); + let running = if let Some(running) = &mut self.running { - // not the first resume event. create whatever you need. - running.glutin.borrow_mut().on_resume(event_loop)?; + // Not the first resume event. Create all outstanding windows. + running + .glutin + .borrow_mut() + .initialize_all_windows(event_loop); running } else { - // first resume event. - // we can actually move this outside of event loop. - // and just run the on_resume fn of gl_window + // First resume event. Created our root window etc. self.init_run_state(event_loop)? }; - let window_id = running - .glutin - .borrow() - .window_from_viewport - .get(&ViewportId::ROOT) - .copied(); - EventResult::RepaintNow(window_id.unwrap()) + let window_id = running.glutin.borrow().window_from_viewport[&ViewportId::ROOT]; + EventResult::RepaintNow(window_id) } winit::event::Event::Suspended => { @@ -433,15 +446,6 @@ impl WinitApp for GlowWinitApp { EventResult::Wait } - winit::event::Event::MainEventsCleared => { - if let Some(running) = &self.running { - if let Err(err) = running.glutin.borrow_mut().on_resume(event_loop) { - log::warn!("on_resume failed {err}"); - } - } - EventResult::Wait - } - winit::event::Event::WindowEvent { event, window_id } => { if let Some(running) = &mut self.running { running.on_window_event(*window_id, event) @@ -477,7 +481,11 @@ impl WinitApp for GlowWinitApp { } impl GlowWinitRunning { - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + fn run_ui_and_paint( + &mut self, + event_loop: &EventLoopWindowTarget, + window_id: WindowId, + ) -> EventResult { crate::profile_function!(); let Some(viewport_id) = self @@ -666,7 +674,7 @@ impl GlowWinitRunning { std::thread::sleep(std::time::Duration::from_millis(10)); } - glutin.handle_viewport_output(&integration.egui_ctx, viewport_output); + glutin.handle_viewport_output(event_loop, &integration.egui_ctx, viewport_output); if integration.should_close() { EventResult::Exit @@ -678,7 +686,7 @@ impl GlowWinitRunning { fn on_window_event( &mut self, window_id: WindowId, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResult { crate::profile_function!(egui_winit::short_window_event_description(event)); @@ -717,13 +725,6 @@ impl GlowWinitRunning { } } - winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - if let Some(viewport_id) = viewport_id { - repaint_asap = true; - glutin.resize(viewport_id, **new_inner_size); - } - } - winit::event::WindowEvent::CloseRequested => { if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() { log::debug!( @@ -768,7 +769,11 @@ impl GlowWinitRunning { { event_response = self.integration.on_window_event(window, egui_winit, event); } + } else { + log::trace!("Ignoring event: no viewport for {viewport_id:?}"); } + } else { + log::trace!("Ignoring event: no viewport_id"); } if event_response.repaint { @@ -855,7 +860,7 @@ impl GlutinWindowContext { // Create GL display. This may probably create a window too on most platforms. Definitely on `MS windows`. Never on Android. let display_builder = glutin_winit::DisplayBuilder::new() // we might want to expose this option to users in the future. maybe using an env var or using native_options. - .with_preference(glutin_winit::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 + .with_preference(glutin_winit::ApiPreference::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 .with_window_builder(Some(egui_winit::create_winit_window_builder( egui_ctx, event_loop, @@ -968,37 +973,29 @@ impl GlutinWindowContext { focused_viewport: Some(ViewportId::ROOT), }; - slf.on_resume(event_loop)?; + slf.initialize_window(ViewportId::ROOT, event_loop)?; Ok(slf) } - /// This will be run after `new`. on android, it might be called multiple times over the course of the app's lifetime. - /// roughly, - /// 1. check if window already exists. otherwise, create one now. - /// 2. create attributes for surface creation. - /// 3. create surface. - /// 4. make surface and context current. + /// Create a surface, window, and winit integration for all viewports lacking any of that. /// - /// we presently assume that we will - fn on_resume(&mut self, event_loop: &EventLoopWindowTarget) -> Result<()> { + /// Errors will be logged. + fn initialize_all_windows(&mut self, event_loop: &EventLoopWindowTarget) { crate::profile_function!(); - let viewports: Vec = self - .viewports - .iter() - .filter(|(_, viewport)| viewport.gl_surface.is_none()) - .map(|(id, _)| *id) - .collect(); + let viewports: Vec = self.viewports.keys().copied().collect(); for viewport_id in viewports { - self.init_viewport(viewport_id, event_loop)?; + if let Err(err) = self.initialize_window(viewport_id, event_loop) { + log::error!("Failed to initialize a window for viewport {viewport_id:?}: {err}"); + } } - Ok(()) } + /// Create a surface, window, and winit integration for the viewport, if missing. #[allow(unsafe_code)] - pub(crate) fn init_viewport( + pub(crate) fn initialize_window( &mut self, viewport_id: ViewportId, event_loop: &EventLoopWindowTarget, @@ -1013,12 +1010,16 @@ impl GlutinWindowContext { let window = if let Some(window) = &mut viewport.window { window } else { - log::trace!("Window doesn't exist yet. Creating one now with finalize_window"); + log::debug!("Creating a window for viewport {viewport_id:?}"); let window_builder = egui_winit::create_winit_window_builder( &self.egui_ctx, event_loop, viewport.builder.clone(), ); + if window_builder.transparent() && self.gl_config.supports_transparency() == Some(false) + { + log::error!("Cannot create transparent window: the GL config does not support it"); + } let window = glutin_winit::finalize_window(event_loop, window_builder, &self.gl_config)?; egui_winit::apply_viewport_builder_to_window( @@ -1031,7 +1032,20 @@ impl GlutinWindowContext { viewport.window.insert(Rc::new(window)) }; - { + viewport.egui_winit.get_or_insert_with(|| { + log::debug!("Initializing egui_winit for viewport {viewport_id:?}"); + egui_winit::State::new( + self.egui_ctx.clone(), + viewport_id, + event_loop, + Some(window.scale_factor() as f32), + self.max_texture_side, + ) + }); + + if viewport.gl_surface.is_none() { + log::debug!("Creating a gl_surface for viewport {viewport_id:?}"); + // surface attributes let (width_px, height_px): (u32, u32) = window.inner_size().into(); let width_px = std::num::NonZeroU32::new(width_px.at_least(1)).unwrap(); @@ -1071,24 +1085,14 @@ impl GlutinWindowContext { // we will reach this point only once in most platforms except android. // create window/surface/make context current once and just use them forever. - viewport.egui_winit.get_or_insert_with(|| { - egui_winit::State::new( - self.egui_ctx.clone(), - viewport_id, - event_loop, - Some(window.scale_factor() as f32), - self.max_texture_side, - ) - }); - viewport.gl_surface = Some(gl_surface); + self.current_gl_context = Some(current_gl_context); - self.viewport_from_window - .insert(window.id(), viewport.ids.this); - self.window_from_viewport - .insert(viewport.ids.this, window.id()); } + self.viewport_from_window.insert(window.id(), viewport_id); + self.window_from_viewport.insert(viewport_id, window.id()); + Ok(()) } @@ -1153,6 +1157,7 @@ impl GlutinWindowContext { fn handle_viewport_output( &mut self, + event_loop: &EventLoopWindowTarget, egui_ctx: &egui::Context, viewport_output: ViewportIdMap, ) { @@ -1197,6 +1202,9 @@ impl GlutinWindowContext { } } + // Create windows for any new viewports: + self.initialize_all_windows(event_loop); + // GC old viewports self.viewports .retain(|id, _| active_viewports_ids.contains(id)); @@ -1295,10 +1303,12 @@ fn render_immediate_viewport( viewport_ui_cb, } = immediate_viewport; + let viewport_id = ids.this; + { let mut glutin = glutin.borrow_mut(); - let viewport = initialize_or_update_viewport( + initialize_or_update_viewport( egui_ctx, &mut glutin.viewports, ids, @@ -1308,17 +1318,18 @@ fn render_immediate_viewport( None, ); - if viewport.gl_surface.is_none() { - glutin - .init_viewport(ids.this, event_loop) - .expect("Failed to initialize window in egui::Context::show_viewport_immediate"); + if let Err(err) = glutin.initialize_window(viewport_id, event_loop) { + log::error!( + "Failed to initialize a window for immediate viewport {viewport_id:?}: {err}" + ); + return; } } let input = { let mut glutin = glutin.borrow_mut(); - let Some(viewport) = glutin.viewports.get_mut(&ids.this) else { + let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else { return; }; let (Some(egui_winit), Some(window)) = (&mut viewport.egui_winit, &viewport.window) else { @@ -1362,7 +1373,7 @@ fn render_immediate_viewport( .. } = &mut *glutin; - let Some(viewport) = viewports.get_mut(&ids.this) else { + let Some(viewport) = viewports.get_mut(&viewport_id) else { return; }; @@ -1423,7 +1434,7 @@ fn render_immediate_viewport( egui_winit.handle_platform_output(window, platform_output); - glutin.handle_viewport_output(egui_ctx, viewport_output); + glutin.handle_viewport_output(event_loop, egui_ctx, viewport_output); } #[cfg(feature = "__screenshot")] diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 5e3dc91554c..95e355a1048 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -1,10 +1,3 @@ -//! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`]. -//! When making changes to one you often also want to apply it to the other. -//! -//! This is also very complex code, and not very pretty. -//! There is a bunch of improvements we could do, -//! like removing a bunch of `unwraps`. - use std::{cell::RefCell, time::Instant}; use winit::event_loop::{EventLoop, EventLoopBuilder}; @@ -34,12 +27,12 @@ fn create_event_loop_builder( event_loop_builder } -fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop { +fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result> { crate::profile_function!(); let mut builder = create_event_loop_builder(native_options); crate::profile_scope!("EventLoopBuilder::build"); - builder.build() + Ok(builder.build()?) } /// Access a thread-local event loop. @@ -49,16 +42,20 @@ fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop( mut native_options: epi::NativeOptions, f: impl FnOnce(&mut EventLoop, epi::NativeOptions) -> R, -) -> R { +) -> Result { thread_local!(static EVENT_LOOP: RefCell>> = RefCell::new(None)); EVENT_LOOP.with(|event_loop| { // Since we want to reference NativeOptions when creating the EventLoop we can't // do that as part of the lazy thread local storage initialization and so we instead // create the event loop lazily here - let mut event_loop = event_loop.borrow_mut(); - let event_loop = event_loop.get_or_insert_with(|| create_event_loop(&mut native_options)); - f(event_loop, native_options) + let mut event_loop_lock = event_loop.borrow_mut(); + let event_loop = if let Some(event_loop) = &mut *event_loop_lock { + event_loop + } else { + event_loop_lock.insert(create_event_loop(&mut native_options)?) + }; + Ok(f(event_loop, native_options)) }) } @@ -67,31 +64,39 @@ fn run_and_return( event_loop: &mut EventLoop, mut winit_app: impl WinitApp, ) -> Result<()> { - use winit::{event_loop::ControlFlow, platform::run_return::EventLoopExtRunReturn as _}; + use winit::{event_loop::ControlFlow, platform::run_on_demand::EventLoopExtRunOnDemand}; - log::debug!("Entering the winit event loop (run_return)…"); + log::debug!("Entering the winit event loop (run_on_demand)…"); // When to repaint what window let mut windows_next_repaint_times = HashMap::default(); let mut returned_result = Ok(()); - event_loop.run_return(|event, event_loop, control_flow| { + event_loop.run_on_demand(|event, event_loop_window_target| { crate::profile_scope!("winit_event", short_event_description(&event)); + log::trace!("winit event: {event:?}"); + + if matches!(event, winit::event::Event::AboutToWait) { + return; // early-out: don't trigger another wait + } + let event_result = match &event { - winit::event::Event::LoopDestroyed => { - // On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name), + winit::event::Event::LoopExiting => { + // On Mac, Cmd-Q we get here and then `run_on_demand` doesn't return (despite its name), // so we need to save state now: - log::debug!("Received Event::LoopDestroyed - saving app state…"); + log::debug!("Received Event::LoopExiting - saving app state…"); winit_app.save_and_destroy(); - *control_flow = ControlFlow::Exit; return; } - winit::event::Event::RedrawRequested(window_id) => { + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::RedrawRequested, + window_id, + } => { windows_next_repaint_times.remove(window_id); - winit_app.run_ui_and_paint(*window_id) + winit_app.run_ui_and_paint(event_loop_window_target, *window_id) } winit::event::Event::UserEvent(UserEvent::RequestRepaint { @@ -120,8 +125,11 @@ fn run_and_return( EventResult::Wait } - event => match winit_app.on_event(event_loop, event) { - Ok(event_result) => event_result, + event => match winit_app.on_event(event_loop_window_target, event) { + Ok(event_result) => { + log::trace!("event_result: {event_result:?}"); + event_result + } Err(err) => { log::error!("Exiting because of error: {err} during event {event:?}"); returned_result = Err(err); @@ -132,21 +140,28 @@ fn run_and_return( match event_result { EventResult::Wait => { - control_flow.set_wait(); + event_loop_window_target.set_control_flow(ControlFlow::Wait); } EventResult::RepaintNow(window_id) => { - log::trace!("Repaint caused by {}", short_event_description(&event)); + log::trace!( + "RepaintNow of {window_id:?} caused by {}", + short_event_description(&event) + ); if cfg!(target_os = "windows") { // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280 windows_next_repaint_times.remove(&window_id); - winit_app.run_ui_and_paint(window_id); + winit_app.run_ui_and_paint(event_loop_window_target, window_id); } else { // Fix for https://github.com/emilk/egui/issues/2425 windows_next_repaint_times.insert(window_id, Instant::now()); } } EventResult::RepaintNext(window_id) => { + log::trace!( + "RepaintNext of {window_id:?} caused by {}", + short_event_description(&event) + ); windows_next_repaint_times.insert(window_id, Instant::now()); } EventResult::RepaintAt(window_id, repaint_time) => { @@ -160,45 +175,35 @@ fn run_and_return( EventResult::Exit => { log::debug!("Asking to exit event loop…"); winit_app.save_and_destroy(); - *control_flow = ControlFlow::Exit; + event_loop_window_target.exit(); return; } } let mut next_repaint_time = windows_next_repaint_times.values().min().copied(); - // This is for not duplicating redraw requests - use winit::event::Event; - if matches!( - event, - Event::RedrawEventsCleared | Event::RedrawRequested(_) | Event::Resumed - ) { - windows_next_repaint_times.retain(|window_id, repaint_time| { - if Instant::now() < *repaint_time { - return true; - }; - - next_repaint_time = None; - control_flow.set_poll(); - - if let Some(window) = winit_app.window(*window_id) { - log::trace!("request_redraw for {window_id:?}"); - window.request_redraw(); - true - } else { - false - } - }); - } + windows_next_repaint_times.retain(|window_id, repaint_time| { + if Instant::now() < *repaint_time { + return true; // not yet ready + }; + + next_repaint_time = None; + event_loop_window_target.set_control_flow(ControlFlow::Poll); + + if let Some(window) = winit_app.window(*window_id) { + log::trace!("request_redraw for {window_id:?}"); + window.request_redraw(); + true + } else { + log::trace!("No window found for {window_id:?}"); + false + } + }); if let Some(next_repaint_time) = next_repaint_time { - let time_until_next = next_repaint_time.saturating_duration_since(Instant::now()); - if time_until_next < std::time::Duration::from_secs(10_000) { - log::trace!("WaitUntil {time_until_next:?}"); - } - control_flow.set_wait_until(next_repaint_time); + event_loop_window_target.set_control_flow(ControlFlow::WaitUntil(next_repaint_time)); }; - }); + })?; log::debug!("eframe window closed"); @@ -211,32 +216,47 @@ fn run_and_return( // we only apply this approach on Windows to minimize the affect. #[cfg(target_os = "windows")] { - event_loop.run_return(|_, _, control_flow| { - control_flow.set_exit(); - }); + event_loop + .run_on_demand(|_, event_loop_window_target| { + event_loop_window_target.exit(); + }) + .ok(); } returned_result } -fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + 'static) -> ! { +fn run_and_exit( + event_loop: EventLoop, + mut winit_app: impl WinitApp + 'static, +) -> Result<()> { + use winit::event_loop::ControlFlow; log::debug!("Entering the winit event loop (run)…"); // When to repaint what window let mut windows_next_repaint_times = HashMap::default(); - event_loop.run(move |event, event_loop, control_flow| { + event_loop.run(move |event, event_loop_window_target| { crate::profile_scope!("winit_event", short_event_description(&event)); + log::trace!("winit event: {event:?}"); + + if matches!(event, winit::event::Event::AboutToWait) { + return; // early-out: don't trigger another wait + } + let event_result = match &event { - winit::event::Event::LoopDestroyed => { - log::debug!("Received Event::LoopDestroyed"); + winit::event::Event::LoopExiting => { + log::debug!("Received Event::LoopExiting"); EventResult::Exit } - winit::event::Event::RedrawRequested(window_id) => { + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::RedrawRequested, + window_id, + } => { windows_next_repaint_times.remove(window_id); - winit_app.run_ui_and_paint(*window_id) + winit_app.run_ui_and_paint(event_loop_window_target, *window_id) } winit::event::Event::UserEvent(UserEvent::RequestRepaint { @@ -264,8 +284,11 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + EventResult::Wait } - event => match winit_app.on_event(event_loop, event) { - Ok(event_result) => event_result, + event => match winit_app.on_event(event_loop_window_target, event) { + Ok(event_result) => { + log::trace!("event_result: {event_result:?}"); + event_result + } Err(err) => { panic!("eframe encountered a fatal error: {err} during event {event:?}"); } @@ -274,22 +297,22 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + match event_result { EventResult::Wait => { - control_flow.set_wait(); + event_loop_window_target.set_control_flow(ControlFlow::Wait); } EventResult::RepaintNow(window_id) => { - log::trace!("Repaint caused by {}", short_event_description(&event)); + log::trace!("RepaintNow caused by {}", short_event_description(&event)); if cfg!(target_os = "windows") { // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280 windows_next_repaint_times.remove(&window_id); - winit_app.run_ui_and_paint(window_id); + winit_app.run_ui_and_paint(event_loop_window_target, window_id); } else { // Fix for https://github.com/emilk/egui/issues/2425 windows_next_repaint_times.insert(window_id, Instant::now()); } } EventResult::RepaintNext(window_id) => { - log::trace!("Repaint caused by {}", short_event_description(&event)); + log::trace!("RepaintNext caused by {}", short_event_description(&event)); windows_next_repaint_times.insert(window_id, Instant::now()); } EventResult::RepaintAt(window_id, repaint_time) => { @@ -303,6 +326,8 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + EventResult::Exit => { log::debug!("Quitting - saving app state…"); winit_app.save_and_destroy(); + + log::debug!("Exiting with return code 0"); #[allow(clippy::exit)] std::process::exit(0); } @@ -310,36 +335,25 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + let mut next_repaint_time = windows_next_repaint_times.values().min().copied(); - // This is for not duplicating redraw requests - use winit::event::Event; - if matches!( - event, - Event::RedrawEventsCleared | Event::RedrawRequested(_) | Event::Resumed - ) { - windows_next_repaint_times.retain(|window_id, repaint_time| { - if Instant::now() < *repaint_time { - return true; - } - - next_repaint_time = None; - control_flow.set_poll(); + windows_next_repaint_times.retain(|window_id, repaint_time| { + if Instant::now() < *repaint_time { + return true; // not yet ready + } - if let Some(window) = winit_app.window(*window_id) { - log::trace!("request_redraw for {window_id:?}"); - window.request_redraw(); - true - } else { - false - } - }); - } + next_repaint_time = None; + event_loop_window_target.set_control_flow(ControlFlow::Poll); - if let Some(next_repaint_time) = next_repaint_time { - let time_until_next = next_repaint_time.saturating_duration_since(Instant::now()); - if time_until_next < std::time::Duration::from_secs(10_000) { - log::trace!("WaitUntil {time_until_next:?}"); + if let Some(window) = winit_app.window(*window_id) { + log::trace!("request_redraw for {window_id:?}"); + window.request_redraw(); + true + } else { + log::trace!("No window found for {window_id:?}"); + false } + }); + if let Some(next_repaint_time) = next_repaint_time { // WaitUntil seems to not work on iOS #[cfg(target_os = "ios")] winit_app @@ -350,9 +364,13 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + .map(|window| window.request_redraw()) }); - control_flow.set_wait_until(next_repaint_time); + event_loop_window_target.set_control_flow(ControlFlow::WaitUntil(next_repaint_time)); }; - }) + })?; + + log::debug!("winit event loop unexpectedly returned"); + + Ok(()) } // ---------------------------------------------------------------------------- @@ -370,12 +388,12 @@ pub fn run_glow( return with_event_loop(native_options, |event_loop, native_options| { let glow_eframe = GlowWinitApp::new(event_loop, app_name, native_options, app_creator); run_and_return(event_loop, glow_eframe) - }); + })?; } - let event_loop = create_event_loop(&mut native_options); + let event_loop = create_event_loop(&mut native_options)?; let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator); - run_and_exit(event_loop, glow_eframe); + run_and_exit(event_loop, glow_eframe) } // ---------------------------------------------------------------------------- @@ -393,10 +411,10 @@ pub fn run_wgpu( return with_event_loop(native_options, |event_loop, native_options| { let wgpu_eframe = WgpuWinitApp::new(event_loop, app_name, native_options, app_creator); run_and_return(event_loop, wgpu_eframe) - }); + })?; } - let event_loop = create_event_loop(&mut native_options); + let event_loop = create_event_loop(&mut native_options)?; let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator); - run_and_exit(event_loop, wgpu_eframe); + run_and_exit(event_loop, wgpu_eframe) } diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index 8b859f5dda3..8f82b7380b7 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -1,3 +1,10 @@ +//! Note that this file contains code very similar to [`glow_integration`]. +//! When making changes to one you often also want to apply it to the other. +//! +//! This is also very complex code, and not very pretty. +//! There is a bunch of improvements we could do, +//! like removing a bunch of `unwraps`. + use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant}; use parking_lot::Mutex; @@ -109,7 +116,8 @@ impl WgpuWinitApp { } } - fn build_windows(&mut self, event_loop: &EventLoopWindowTarget) { + /// Create a window for all viewports lacking one. + fn initialized_all_windows(&mut self, event_loop: &EventLoopWindowTarget) { let Some(running) = &mut self.running else { return; }; @@ -122,14 +130,12 @@ impl WgpuWinitApp { } = &mut *shared; for viewport in viewports.values_mut() { - if viewport.window.is_none() { - viewport.init_window( - &running.integration.egui_ctx, - viewport_from_window, - painter, - event_loop, - ); - } + viewport.initialize_window( + event_loop, + &running.integration.egui_ctx, + viewport_from_window, + painter, + ); } } @@ -350,7 +356,13 @@ impl WinitApp for WgpuWinitApp { } } - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + fn run_ui_and_paint( + &mut self, + event_loop: &EventLoopWindowTarget, + window_id: WindowId, + ) -> EventResult { + self.initialized_all_windows(event_loop); + if let Some(running) = &mut self.running { running.run_ui_and_paint(window_id) } else { @@ -361,14 +373,16 @@ impl WinitApp for WgpuWinitApp { fn on_event( &mut self, event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, UserEvent>, + event: &winit::event::Event, ) -> Result { crate::profile_function!(winit_integration::short_event_description(event)); - self.build_windows(event_loop); + self.initialized_all_windows(event_loop); Ok(match event { winit::event::Event::Resumed => { + log::debug!("Event::Resumed"); + let running = if let Some(running) = &self.running { running } else { @@ -660,7 +674,7 @@ impl WgpuWinitRunning { fn on_window_event( &mut self, window_id: WindowId, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResult { crate::profile_function!(egui_winit::short_window_event_description(event)); @@ -709,18 +723,6 @@ impl WgpuWinitRunning { } } - winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - use std::num::NonZeroU32; - if let (Some(width), Some(height), Some(viewport_id)) = ( - NonZeroU32::new(new_inner_size.width), - NonZeroU32::new(new_inner_size.height), - viewport_id, - ) { - repaint_asap = true; - shared.painter.on_window_resized(viewport_id, width, height); - } - } - winit::event::WindowEvent::CloseRequested => { if viewport_id == Some(ViewportId::ROOT) && integration.should_close() { log::debug!( @@ -775,13 +777,18 @@ impl WgpuWinitRunning { } impl Viewport { - fn init_window( + /// Create winit window, if needed. + fn initialize_window( &mut self, + event_loop: &EventLoopWindowTarget, egui_ctx: &egui::Context, windows_id: &mut HashMap, painter: &mut egui_wgpu::winit::Painter, - event_loop: &EventLoopWindowTarget, ) { + if self.window.is_some() { + return; // we already have one + } + crate::profile_function!(); let viewport_id = self.ids.this; @@ -870,7 +877,7 @@ fn render_immediate_viewport( None, ); if viewport.window.is_none() { - viewport.init_window(egui_ctx, viewport_from_window, painter, event_loop); + viewport.initialize_window(event_loop, egui_ctx, viewport_from_window, painter); } let (Some(window), Some(egui_winit)) = (&viewport.window, &mut viewport.egui_winit) else { diff --git a/crates/eframe/src/native/winit_integration.rs b/crates/eframe/src/native/winit_integration.rs index 53de13cc44d..a7e4bb384b3 100644 --- a/crates/eframe/src/native/winit_integration.rs +++ b/crates/eframe/src/native/winit_integration.rs @@ -74,12 +74,16 @@ pub trait WinitApp { fn save_and_destroy(&mut self); - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult; + fn run_ui_and_paint( + &mut self, + event_loop: &EventLoopWindowTarget, + window_id: WindowId, + ) -> EventResult; fn on_event( &mut self, event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, UserEvent>, + event: &winit::event::Event, ) -> crate::Result; } @@ -117,11 +121,9 @@ pub fn system_theme(window: &Window, options: &crate::NativeOptions) -> Option) -> &'static str { - use winit::event::Event; - +pub fn short_event_description(event: &winit::event::Event) -> &'static str { match event { - Event::UserEvent(user_event) => match user_event { + winit::event::Event::UserEvent(user_event) => match user_event { UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint", #[cfg(feature = "accesskit")] UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest", diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index f5aab09a367..42cca65ed21 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -13,7 +13,7 @@ pub struct AppRunner { app: Box, pub(crate) needs_repaint: std::sync::Arc, last_save_time: f64, - pub(crate) text_cursor_pos: Option, + pub(crate) ime: Option, pub(crate) mutable_text_under_cursor: bool, // Output for the last run: @@ -112,7 +112,7 @@ impl AppRunner { app, needs_repaint, last_save_time: now_sec(), - text_cursor_pos: None, + ime: None, mutable_text_under_cursor: false, textures_delta: Default::default(), clipped_primitives: None, @@ -244,7 +244,7 @@ impl AppRunner { copied_text, events: _, // already handled mutable_text_under_cursor, - text_cursor_pos, + ime, #[cfg(feature = "accesskit")] accesskit_update: _, // not currently implemented } = platform_output; @@ -264,9 +264,9 @@ impl AppRunner { self.mutable_text_under_cursor = mutable_text_under_cursor; - if self.text_cursor_pos != text_cursor_pos { - super::text_agent::move_text_cursor(text_cursor_pos, self.canvas_id()); - self.text_cursor_pos = text_cursor_pos; + if self.ime != ime { + super::text_agent::move_text_cursor(ime, self.canvas_id()); + self.ime = ime; } } } diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 9bb5c0f885a..2418bed95c5 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -91,6 +91,7 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa if let Some(key) = egui_key { runner.input.raw.events.push(egui::Event::Key { key, + physical_key: None, // TODO pressed: true, repeat: false, // egui will fill this in for us! modifiers, @@ -157,6 +158,7 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa if let Some(key) = translate_key(&event.key()) { runner.input.raw.events.push(egui::Event::Key { key, + physical_key: None, // TODO pressed: false, repeat: false, modifiers, diff --git a/crates/eframe/src/web/input.rs b/crates/eframe/src/web/input.rs index 33ea30b6dce..96cad32e20f 100644 --- a/crates/eframe/src/web/input.rs +++ b/crates/eframe/src/web/input.rs @@ -112,91 +112,7 @@ pub fn should_ignore_key(key: &str) -> bool { /// Web sends all keys as strings, so it is up to us to figure out if it is /// a real text input or the name of a key. pub fn translate_key(key: &str) -> Option { - use egui::Key; - - match key { - "ArrowDown" => Some(Key::ArrowDown), - "ArrowLeft" => Some(Key::ArrowLeft), - "ArrowRight" => Some(Key::ArrowRight), - "ArrowUp" => Some(Key::ArrowUp), - - "Esc" | "Escape" => Some(Key::Escape), - "Tab" => Some(Key::Tab), - "Backspace" => Some(Key::Backspace), - "Enter" => Some(Key::Enter), - "Space" | " " => Some(Key::Space), - - "Help" | "Insert" => Some(Key::Insert), - "Delete" => Some(Key::Delete), - "Home" => Some(Key::Home), - "End" => Some(Key::End), - "PageUp" => Some(Key::PageUp), - "PageDown" => Some(Key::PageDown), - - "-" => Some(Key::Minus), - "+" | "=" => Some(Key::PlusEquals), - - "0" => Some(Key::Num0), - "1" => Some(Key::Num1), - "2" => Some(Key::Num2), - "3" => Some(Key::Num3), - "4" => Some(Key::Num4), - "5" => Some(Key::Num5), - "6" => Some(Key::Num6), - "7" => Some(Key::Num7), - "8" => Some(Key::Num8), - "9" => Some(Key::Num9), - - "a" | "A" => Some(Key::A), - "b" | "B" => Some(Key::B), - "c" | "C" => Some(Key::C), - "d" | "D" => Some(Key::D), - "e" | "E" => Some(Key::E), - "f" | "F" => Some(Key::F), - "g" | "G" => Some(Key::G), - "h" | "H" => Some(Key::H), - "i" | "I" => Some(Key::I), - "j" | "J" => Some(Key::J), - "k" | "K" => Some(Key::K), - "l" | "L" => Some(Key::L), - "m" | "M" => Some(Key::M), - "n" | "N" => Some(Key::N), - "o" | "O" => Some(Key::O), - "p" | "P" => Some(Key::P), - "q" | "Q" => Some(Key::Q), - "r" | "R" => Some(Key::R), - "s" | "S" => Some(Key::S), - "t" | "T" => Some(Key::T), - "u" | "U" => Some(Key::U), - "v" | "V" => Some(Key::V), - "w" | "W" => Some(Key::W), - "x" | "X" => Some(Key::X), - "y" | "Y" => Some(Key::Y), - "z" | "Z" => Some(Key::Z), - - "F1" => Some(Key::F1), - "F2" => Some(Key::F2), - "F3" => Some(Key::F3), - "F4" => Some(Key::F4), - "F5" => Some(Key::F5), - "F6" => Some(Key::F6), - "F7" => Some(Key::F7), - "F8" => Some(Key::F8), - "F9" => Some(Key::F9), - "F10" => Some(Key::F10), - "F11" => Some(Key::F11), - "F12" => Some(Key::F12), - "F13" => Some(Key::F13), - "F14" => Some(Key::F14), - "F15" => Some(Key::F15), - "F16" => Some(Key::F16), - "F17" => Some(Key::F17), - "F18" => Some(Key::F18), - "F19" => Some(Key::F19), - "F20" => Some(Key::F20), - - _ => None, - } + egui::Key::from_name(key) } pub fn modifiers_from_event(event: &web_sys::KeyboardEvent) -> egui::Modifiers { diff --git a/crates/eframe/src/web/text_agent.rs b/crates/eframe/src/web/text_agent.rs index d4f3b5ca3c6..0bf8b532b7c 100644 --- a/crates/eframe/src/web/text_agent.rs +++ b/crates/eframe/src/web/text_agent.rs @@ -205,11 +205,13 @@ fn is_mobile() -> Option { // candidate window moves following text element (agent), // so it appears that the IME candidate window moves with text cursor. // On mobile devices, there is no need to do that. -pub fn move_text_cursor(cursor: Option, canvas_id: &str) -> Option<()> { +pub fn move_text_cursor(ime: Option, canvas_id: &str) -> Option<()> { let style = text_agent().style(); - // Note: movint agent on mobile devices will lead to unpredictable scroll. + // Note: moving agent on mobile devices will lead to unpredictable scroll. if is_mobile() == Some(false) { - cursor.as_ref().and_then(|&egui::Pos2 { x, y }| { + ime.as_ref().and_then(|ime| { + let egui::Pos2 { x, y } = ime.cursor_rect.left_top(); + let canvas = canvas_element(canvas_id)?; let bounding_rect = text_agent().get_bounding_client_rect(); let y = (y + (canvas.scroll_top() + canvas.offset_top()) as f32) diff --git a/crates/egui-wgpu/Cargo.toml b/crates/egui-wgpu/Cargo.toml index 22200bafdbe..546c1b32987 100644 --- a/crates/egui-wgpu/Cargo.toml +++ b/crates/egui-wgpu/Cargo.toml @@ -51,7 +51,7 @@ wgpu.workspace = true ## Enable this when generating docs. document-features = { version = "0.2", optional = true } -winit = { version = "0.28", default-features = false, optional = true } +winit = { version = "0.29.4", default-features = false, optional = true, features = ["rwh_05"] } # Native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index 178d99cdaf2..8e8a8108ab3 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -61,12 +61,12 @@ egui = { version = "0.24.1", path = "../egui", default-features = false, feature log = { version = "0.4", features = ["std"] } raw-window-handle.workspace = true web-time = { version = "0.2" } # We use web-time so we can (maybe) compile for web -winit = { version = "0.28", default-features = false } +winit = { version = "0.29.4", default-features = false, features = ["rwh_05"] } #! ### Optional dependencies # feature accesskit -accesskit_winit = { version = "0.15.0", optional = true } +accesskit_winit = { version = "0.16.0", optional = true } ## Enable this when generating docs. document-features = { version = "0.2", optional = true } @@ -76,7 +76,7 @@ serde = { version = "1.0", optional = true, features = ["derive"] } webbrowser = { version = "0.8.3", optional = true } [target.'cfg(any(target_os="linux", target_os="dragonfly", target_os="freebsd", target_os="netbsd", target_os="openbsd"))'.dependencies] -smithay-clipboard = { version = "0.6.3", optional = true } +smithay-clipboard = { version = "0.7.0", optional = true } [target.'cfg(not(target_os = "android"))'.dependencies] arboard = { version = "3.2", optional = true, default-features = false } diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 11f672bb933..da4d2bc4358 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -232,7 +232,7 @@ impl State { pub fn on_window_event( &mut self, window: &Window, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResponse { crate::profile_function!(short_window_event_description(event)); @@ -295,25 +295,7 @@ impl State { consumed, } } - WindowEvent::ReceivedCharacter(ch) => { - // On Mac we get here when the user presses Cmd-C (copy), ctrl-W, etc. - // We need to ignore these characters that are side-effects of commands. - let is_mac_cmd = cfg!(target_os = "macos") - && (self.egui_input.modifiers.ctrl || self.egui_input.modifiers.mac_cmd); - let consumed = if is_printable_char(*ch) && !is_mac_cmd { - self.egui_input - .events - .push(egui::Event::Text(ch.to_string())); - self.egui_ctx.wants_keyboard_input() - } else { - false - }; - EventResponse { - repaint: true, - consumed, - } - } WindowEvent::Ime(ime) => { // on Mac even Cmd-C is pressed during ime, a `c` is pushed to Preedit. // So no need to check is_mac_cmd. @@ -353,11 +335,12 @@ impl State { consumed: self.egui_ctx.wants_keyboard_input(), } } - WindowEvent::KeyboardInput { input, .. } => { - self.on_keyboard_input(input); + WindowEvent::KeyboardInput { event, .. } => { // When pressing the Tab key, egui focuses the first focusable element, hence Tab always consumes. - let consumed = self.egui_ctx.wants_keyboard_input() - || input.virtual_keycode == Some(winit::event::VirtualKeyCode::Tab); + let consumed = self.on_keyboard_input(event) + || self.egui_ctx.wants_keyboard_input() + || event.logical_key + == winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab); EventResponse { repaint: true, consumed, @@ -405,15 +388,23 @@ impl State { } } WindowEvent::ModifiersChanged(state) => { - self.egui_input.modifiers.alt = state.alt(); - self.egui_input.modifiers.ctrl = state.ctrl(); - self.egui_input.modifiers.shift = state.shift(); - self.egui_input.modifiers.mac_cmd = cfg!(target_os = "macos") && state.logo(); + let state = state.state(); + + let alt = state.alt_key(); + let ctrl = state.control_key(); + let shift = state.shift_key(); + let super_ = state.super_key(); + + self.egui_input.modifiers.alt = alt; + self.egui_input.modifiers.ctrl = ctrl; + self.egui_input.modifiers.shift = shift; + self.egui_input.modifiers.mac_cmd = cfg!(target_os = "macos") && super_; self.egui_input.modifiers.command = if cfg!(target_os = "macos") { - state.logo() + super_ } else { - state.ctrl() + ctrl }; + EventResponse { repaint: true, consumed: false, @@ -421,7 +412,8 @@ impl State { } // Things that may require repaint: - WindowEvent::CursorEntered { .. } + WindowEvent::RedrawRequested + | WindowEvent::CursorEntered { .. } | WindowEvent::Destroyed | WindowEvent::Occluded(_) | WindowEvent::Resized(_) @@ -434,7 +426,8 @@ impl State { }, // Things we completely ignore: - WindowEvent::AxisMotion { .. } + WindowEvent::ActivationTokenDone { .. } + | WindowEvent::AxisMotion { .. } | WindowEvent::SmartMagnify { .. } | WindowEvent::TouchpadRotate { .. } => EventResponse { repaint: false, @@ -655,36 +648,92 @@ impl State { } } - fn on_keyboard_input(&mut self, input: &winit::event::KeyboardInput) { - if let Some(keycode) = input.virtual_keycode { - let pressed = input.state == winit::event::ElementState::Pressed; + fn on_keyboard_input(&mut self, event: &winit::event::KeyEvent) -> bool { + let winit::event::KeyEvent { + // Represents the position of a key independent of the currently active layout. + // + // It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). + // The most prevalent use case for this is games. For example the default keys for the player + // to move around might be the W, A, S, and D keys on a US layout. The position of these keys + // is more important than their label, so they should map to Z, Q, S, and D on an "AZERTY" + // layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.) + physical_key, + + // Represents the results of a keymap, i.e. what character a certain key press represents. + // When telling users "Press Ctrl-F to find", this is where we should + // look for the "F" key, because they may have a dvorak layout on + // a qwerty keyboard, and so the logical "F" character may not be located on the physical `KeyCode::KeyF` position. + logical_key, + + text, + state, + + location: _, // e.g. is it on the numpad? + repeat: _, // egui will figure this out for us + .. + } = event; + + let pressed = *state == winit::event::ElementState::Pressed; + + let physical_key = if let winit::keyboard::PhysicalKey::Code(keycode) = *physical_key { + key_from_key_code(keycode) + } else { + None + }; + + let logical_key = key_from_winit_key(logical_key); + + if let Some(logical_key) = logical_key { if pressed { - // VirtualKeyCode::Paste etc in winit are broken/untrustworthy, + // KeyCode::Paste etc in winit are broken/untrustworthy, // so we detect these things manually: - if is_cut_command(self.egui_input.modifiers, keycode) { + if is_cut_command(self.egui_input.modifiers, logical_key) { self.egui_input.events.push(egui::Event::Cut); - } else if is_copy_command(self.egui_input.modifiers, keycode) { + return true; + } else if is_copy_command(self.egui_input.modifiers, logical_key) { self.egui_input.events.push(egui::Event::Copy); - } else if is_paste_command(self.egui_input.modifiers, keycode) { + return true; + } else if is_paste_command(self.egui_input.modifiers, logical_key) { if let Some(contents) = self.clipboard.get() { let contents = contents.replace("\r\n", "\n"); if !contents.is_empty() { self.egui_input.events.push(egui::Event::Paste(contents)); } } + return true; } } - if let Some(key) = translate_virtual_key_code(keycode) { - self.egui_input.events.push(egui::Event::Key { - key, - pressed, - repeat: false, // egui will fill this in for us! - modifiers: self.egui_input.modifiers, - }); + self.egui_input.events.push(egui::Event::Key { + key: logical_key, + physical_key, + pressed, + repeat: false, // egui will fill this in for us! + modifiers: self.egui_input.modifiers, + }); + } + + if let Some(text) = &text { + // Make sure there is text, and that it is not control characters + // (e.g. delete is sent as "\u{f728}" on macOS). + if !text.is_empty() && text.chars().all(is_printable_char) { + // On some platforms we get here when the user presses Cmd-C (copy), ctrl-W, etc. + // We need to ignore these characters that are side-effects of commands. + // Also make sure the key is pressed (not released). On Linux, text might + // contain some data even when the key is released. + let is_cmd = self.egui_input.modifiers.ctrl + || self.egui_input.modifiers.command + || self.egui_input.modifiers.mac_cmd; + if pressed && !is_cmd { + self.egui_input + .events + .push(egui::Event::Text(text.to_string())); + } } } + + false } /// Call with the output given by `egui`. @@ -708,7 +757,7 @@ impl State { copied_text, events: _, // handled elsewhere mutable_text_under_cursor: _, // only used in eframe web - text_cursor_pos, + ime, #[cfg(feature = "accesskit")] accesskit_update, } = platform_output; @@ -723,14 +772,25 @@ impl State { self.clipboard.set(copied_text); } - let allow_ime = text_cursor_pos.is_some(); + let allow_ime = ime.is_some(); if self.allow_ime != allow_ime { self.allow_ime = allow_ime; window.set_ime_allowed(allow_ime); } - if let Some(egui::Pos2 { x, y }) = text_cursor_pos { - window.set_ime_position(winit::dpi::LogicalPosition { x, y }); + if let Some(ime) = ime { + let rect = ime.rect; + let pixels_per_point = pixels_per_point(&self.egui_ctx, window); + window.set_ime_cursor_area( + winit::dpi::PhysicalPosition { + x: pixels_per_point * rect.min.x, + y: pixels_per_point * rect.min.y, + }, + winit::dpi::PhysicalSize { + width: pixels_per_point * rect.width(), + height: pixels_per_point * rect.height(), + }, + ); } #[cfg(feature = "accesskit")] @@ -880,25 +940,19 @@ fn is_printable_char(chr: char) -> bool { !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_cut_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool { + (modifiers.command && keycode == egui::Key::X) + || (cfg!(target_os = "windows") && modifiers.shift && keycode == egui::Key::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_copy_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool { + (modifiers.command && keycode == egui::Key::C) + || (cfg!(target_os = "windows") && modifiers.ctrl && keycode == egui::Key::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 is_paste_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool { + (modifiers.command && keycode == egui::Key::V) + || (cfg!(target_os = "windows") && modifiers.shift && keycode == egui::Key::Insert) } fn translate_mouse_button(button: winit::event::MouseButton) -> Option { @@ -906,100 +960,159 @@ fn translate_mouse_button(button: winit::event::MouseButton) -> Option Some(egui::PointerButton::Primary), winit::event::MouseButton::Right => Some(egui::PointerButton::Secondary), winit::event::MouseButton::Middle => Some(egui::PointerButton::Middle), - winit::event::MouseButton::Other(1) => Some(egui::PointerButton::Extra1), - winit::event::MouseButton::Other(2) => Some(egui::PointerButton::Extra2), + winit::event::MouseButton::Back => Some(egui::PointerButton::Extra1), + winit::event::MouseButton::Forward => Some(egui::PointerButton::Extra2), winit::event::MouseButton::Other(_) => None, } } -fn translate_virtual_key_code(key: winit::event::VirtualKeyCode) -> Option { +fn key_from_winit_key(key: &winit::keyboard::Key) -> Option { + match key { + winit::keyboard::Key::Named(named_key) => key_from_named_key(*named_key), + winit::keyboard::Key::Character(str) => egui::Key::from_name(str.as_str()), + winit::keyboard::Key::Unidentified(_) | winit::keyboard::Key::Dead(_) => None, + } +} + +fn key_from_named_key(named_key: winit::keyboard::NamedKey) -> Option { use egui::Key; - use winit::event::VirtualKeyCode; + use winit::keyboard::NamedKey; + + match named_key { + NamedKey::Enter => Some(Key::Enter), + NamedKey::Tab => Some(Key::Tab), + NamedKey::Space => Some(Key::Space), + NamedKey::ArrowDown => Some(Key::ArrowDown), + NamedKey::ArrowLeft => Some(Key::ArrowLeft), + NamedKey::ArrowRight => Some(Key::ArrowRight), + NamedKey::ArrowUp => Some(Key::ArrowUp), + NamedKey::End => Some(Key::End), + NamedKey::Home => Some(Key::Home), + NamedKey::PageDown => Some(Key::PageDown), + NamedKey::PageUp => Some(Key::PageUp), + NamedKey::Backspace => Some(Key::Backspace), + NamedKey::Delete => Some(Key::Delete), + NamedKey::Insert => Some(Key::Insert), + NamedKey::Escape => Some(Key::Escape), + NamedKey::F1 => Some(Key::F1), + NamedKey::F2 => Some(Key::F2), + NamedKey::F3 => Some(Key::F3), + NamedKey::F4 => Some(Key::F4), + NamedKey::F5 => Some(Key::F5), + NamedKey::F6 => Some(Key::F6), + NamedKey::F7 => Some(Key::F7), + NamedKey::F8 => Some(Key::F8), + NamedKey::F9 => Some(Key::F9), + NamedKey::F10 => Some(Key::F10), + NamedKey::F11 => Some(Key::F11), + NamedKey::F12 => Some(Key::F12), + NamedKey::F13 => Some(Key::F13), + NamedKey::F14 => Some(Key::F14), + NamedKey::F15 => Some(Key::F15), + NamedKey::F16 => Some(Key::F16), + NamedKey::F17 => Some(Key::F17), + NamedKey::F18 => Some(Key::F18), + NamedKey::F19 => Some(Key::F19), + NamedKey::F20 => Some(Key::F20), + _ => { + log::trace!("Unknown key: {named_key:?}"); + None + } + } +} + +fn key_from_key_code(key: winit::keyboard::KeyCode) -> Option { + use egui::Key; + use winit::keyboard::KeyCode; 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 | VirtualKeyCode::NumpadEnter => 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::Minus | VirtualKeyCode::NumpadSubtract => Key::Minus, + KeyCode::ArrowDown => Key::ArrowDown, + KeyCode::ArrowLeft => Key::ArrowLeft, + KeyCode::ArrowRight => Key::ArrowRight, + KeyCode::ArrowUp => Key::ArrowUp, + + KeyCode::Escape => Key::Escape, + KeyCode::Tab => Key::Tab, + KeyCode::Backspace => Key::Backspace, + KeyCode::Enter | KeyCode::NumpadEnter => Key::Enter, + KeyCode::Space => Key::Space, + + KeyCode::Insert => Key::Insert, + KeyCode::Delete => Key::Delete, + KeyCode::Home => Key::Home, + KeyCode::End => Key::End, + KeyCode::PageUp => Key::PageUp, + KeyCode::PageDown => Key::PageDown, + + KeyCode::Comma => Key::Comma, + KeyCode::Period => Key::Period, + // KeyCode::Colon => Key::Colon, // NOTE: there is no physical colon key on an american keyboard + KeyCode::Semicolon => Key::Semicolon, + + KeyCode::Minus | KeyCode::NumpadSubtract => Key::Minus, + // Using Mac the key with the Plus sign on it is reported as the Equals key // (with both English and Swedish keyboard). - VirtualKeyCode::Equals | VirtualKeyCode::Plus | VirtualKeyCode::NumpadAdd => { - Key::PlusEquals - } - - 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, - - VirtualKeyCode::F1 => Key::F1, - VirtualKeyCode::F2 => Key::F2, - VirtualKeyCode::F3 => Key::F3, - VirtualKeyCode::F4 => Key::F4, - VirtualKeyCode::F5 => Key::F5, - VirtualKeyCode::F6 => Key::F6, - VirtualKeyCode::F7 => Key::F7, - VirtualKeyCode::F8 => Key::F8, - VirtualKeyCode::F9 => Key::F9, - VirtualKeyCode::F10 => Key::F10, - VirtualKeyCode::F11 => Key::F11, - VirtualKeyCode::F12 => Key::F12, - VirtualKeyCode::F13 => Key::F13, - VirtualKeyCode::F14 => Key::F14, - VirtualKeyCode::F15 => Key::F15, - VirtualKeyCode::F16 => Key::F16, - VirtualKeyCode::F17 => Key::F17, - VirtualKeyCode::F18 => Key::F18, - VirtualKeyCode::F19 => Key::F19, - VirtualKeyCode::F20 => Key::F20, + KeyCode::Equal | KeyCode::NumpadAdd => Key::PlusEquals, + + KeyCode::Digit0 | KeyCode::Numpad0 => Key::Num0, + KeyCode::Digit1 | KeyCode::Numpad1 => Key::Num1, + KeyCode::Digit2 | KeyCode::Numpad2 => Key::Num2, + KeyCode::Digit3 | KeyCode::Numpad3 => Key::Num3, + KeyCode::Digit4 | KeyCode::Numpad4 => Key::Num4, + KeyCode::Digit5 | KeyCode::Numpad5 => Key::Num5, + KeyCode::Digit6 | KeyCode::Numpad6 => Key::Num6, + KeyCode::Digit7 | KeyCode::Numpad7 => Key::Num7, + KeyCode::Digit8 | KeyCode::Numpad8 => Key::Num8, + KeyCode::Digit9 | KeyCode::Numpad9 => Key::Num9, + + KeyCode::KeyA => Key::A, + KeyCode::KeyB => Key::B, + KeyCode::KeyC => Key::C, + KeyCode::KeyD => Key::D, + KeyCode::KeyE => Key::E, + KeyCode::KeyF => Key::F, + KeyCode::KeyG => Key::G, + KeyCode::KeyH => Key::H, + KeyCode::KeyI => Key::I, + KeyCode::KeyJ => Key::J, + KeyCode::KeyK => Key::K, + KeyCode::KeyL => Key::L, + KeyCode::KeyM => Key::M, + KeyCode::KeyN => Key::N, + KeyCode::KeyO => Key::O, + KeyCode::KeyP => Key::P, + KeyCode::KeyQ => Key::Q, + KeyCode::KeyR => Key::R, + KeyCode::KeyS => Key::S, + KeyCode::KeyT => Key::T, + KeyCode::KeyU => Key::U, + KeyCode::KeyV => Key::V, + KeyCode::KeyW => Key::W, + KeyCode::KeyX => Key::X, + KeyCode::KeyY => Key::Y, + KeyCode::KeyZ => Key::Z, + + KeyCode::F1 => Key::F1, + KeyCode::F2 => Key::F2, + KeyCode::F3 => Key::F3, + KeyCode::F4 => Key::F4, + KeyCode::F5 => Key::F5, + KeyCode::F6 => Key::F6, + KeyCode::F7 => Key::F7, + KeyCode::F8 => Key::F8, + KeyCode::F9 => Key::F9, + KeyCode::F10 => Key::F10, + KeyCode::F11 => Key::F11, + KeyCode::F12 => Key::F12, + KeyCode::F13 => Key::F13, + KeyCode::F14 => Key::F14, + KeyCode::F15 => Key::F15, + KeyCode::F16 => Key::F16, + KeyCode::F17 => Key::F17, + KeyCode::F18 => Key::F18, + KeyCode::F19 => Key::F19, + KeyCode::F20 => Key::F20, _ => { return None; @@ -1024,7 +1137,7 @@ fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option 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::PointingHand => Some(winit::window::CursorIcon::Pointer), egui::CursorIcon::Progress => Some(winit::window::CursorIcon::Progress), egui::CursorIcon::ResizeHorizontal => Some(winit::window::CursorIcon::EwResize), @@ -1112,7 +1225,12 @@ fn process_viewport_command( ViewportCommand::InnerSize(size) => { let width_px = pixels_per_point * size.x.max(1.0); let height_px = pixels_per_point * size.y.max(1.0); - window.set_inner_size(PhysicalSize::new(width_px, height_px)); + if window + .request_inner_size(PhysicalSize::new(width_px, height_px)) + .is_some() + { + log::debug!("ViewportCommand::InnerSize ignored by winit"); + } } ViewportCommand::BeginResize(direction) => { if let Err(err) = window.drag_resize_window(match direction { @@ -1196,11 +1314,14 @@ fn process_viewport_command( .expect("Invalid ICON data!") })); } - ViewportCommand::IMEPosition(pos) => { - window.set_ime_position(PhysicalPosition::new( - pixels_per_point * pos.x, - pixels_per_point * pos.y, - )); + ViewportCommand::IMERect(rect) => { + window.set_ime_cursor_area( + PhysicalPosition::new(pixels_per_point * rect.min.x, pixels_per_point * rect.min.y), + PhysicalSize::new( + pixels_per_point * rect.size().x, + pixels_per_point * rect.size().y, + ), + ); } ViewportCommand::IMEAllowed(v) => window.set_ime_allowed(v), ViewportCommand::IMEPurpose(p) => window.set_ime_purpose(match p { @@ -1448,10 +1569,15 @@ pub fn apply_viewport_builder_to_window( let pixels_per_point = pixels_per_point(egui_ctx, window); if let Some(size) = builder.inner_size { - window.set_inner_size(PhysicalSize::new( - pixels_per_point * size.x, - pixels_per_point * size.y, - )); + if window + .request_inner_size(PhysicalSize::new( + pixels_per_point * size.x, + pixels_per_point * size.y, + )) + .is_some() + { + log::debug!("Failed to set window size"); + } } if let Some(size) = builder.min_inner_size { window.set_min_inner_size(Some(PhysicalSize::new( @@ -1476,16 +1602,15 @@ pub fn apply_viewport_builder_to_window( /// Short and fast description of an event. /// Useful for logging and profiling. -pub fn short_generic_event_description(event: &winit::event::Event<'_, T>) -> &'static str { +pub fn short_generic_event_description(event: &winit::event::Event) -> &'static str { use winit::event::{DeviceEvent, Event, StartCause}; match event { + Event::AboutToWait => "Event::AboutToWait", + Event::LoopExiting => "Event::LoopExiting", Event::Suspended => "Event::Suspended", Event::Resumed => "Event::Resumed", - Event::MainEventsCleared => "Event::MainEventsCleared", - Event::RedrawRequested(_) => "Event::RedrawRequested", - Event::RedrawEventsCleared => "Event::RedrawEventsCleared", - Event::LoopDestroyed => "Event::LoopDestroyed", + Event::MemoryWarning => "Event::MemoryWarning", Event::UserEvent(_) => "UserEvent", Event::DeviceEvent { event, .. } => match event { DeviceEvent::Added { .. } => "DeviceEvent::Added", @@ -1495,7 +1620,6 @@ pub fn short_generic_event_description(event: &winit::event::Event<'_, T>) -> DeviceEvent::Motion { .. } => "DeviceEvent::Motion", DeviceEvent::Button { .. } => "DeviceEvent::Button", DeviceEvent::Key { .. } => "DeviceEvent::Key", - DeviceEvent::Text { .. } => "DeviceEvent::Text", }, Event::NewEvents(start_cause) => match start_cause { StartCause::ResumeTimeReached { .. } => "NewEvents::ResumeTimeReached", @@ -1509,10 +1633,11 @@ pub fn short_generic_event_description(event: &winit::event::Event<'_, T>) -> /// Short and fast description of an event. /// Useful for logging and profiling. -pub fn short_window_event_description(event: &winit::event::WindowEvent<'_>) -> &'static str { +pub fn short_window_event_description(event: &winit::event::WindowEvent) -> &'static str { use winit::event::WindowEvent; match event { + WindowEvent::ActivationTokenDone { .. } => "WindowEvent::ActivationTokenDone", WindowEvent::Resized { .. } => "WindowEvent::Resized", WindowEvent::Moved { .. } => "WindowEvent::Moved", WindowEvent::CloseRequested { .. } => "WindowEvent::CloseRequested", @@ -1520,7 +1645,6 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent<'_>) -> WindowEvent::DroppedFile { .. } => "WindowEvent::DroppedFile", WindowEvent::HoveredFile { .. } => "WindowEvent::HoveredFile", WindowEvent::HoveredFileCancelled { .. } => "WindowEvent::HoveredFileCancelled", - WindowEvent::ReceivedCharacter { .. } => "WindowEvent::ReceivedCharacter", WindowEvent::Focused { .. } => "WindowEvent::Focused", WindowEvent::KeyboardInput { .. } => "WindowEvent::KeyboardInput", WindowEvent::ModifiersChanged { .. } => "WindowEvent::ModifiersChanged", @@ -1531,6 +1655,7 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent<'_>) -> WindowEvent::MouseWheel { .. } => "WindowEvent::MouseWheel", WindowEvent::MouseInput { .. } => "WindowEvent::MouseInput", WindowEvent::TouchpadMagnify { .. } => "WindowEvent::TouchpadMagnify", + WindowEvent::RedrawRequested { .. } => "WindowEvent::RedrawRequested", WindowEvent::SmartMagnify { .. } => "WindowEvent::SmartMagnify", WindowEvent::TouchpadRotate { .. } => "WindowEvent::TouchpadRotate", WindowEvent::TouchpadPressure { .. } => "WindowEvent::TouchpadPressure", diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index 0c10f79078f..afb6e1dcbeb 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -361,8 +361,20 @@ pub enum Event { /// A key was pressed or released. Key { + /// The logical key, heeding the users keymap. + /// + /// For instance, if the user is using Dvorak keyboard layout, + /// this will take that into account. key: Key, + /// The physical key, corresponding to the actual position on the keyboard. + /// + /// This ignored keymaps, so it is not recommended to use this. + /// The only thing it makes sense for is things like games, + /// where e.g. the physical location of WSAD on QWERTY should always map to movement, + /// even if the user is using Dvorak or AZERTY. + physical_key: Option, + /// Was it pressed or released? pressed: bool, @@ -844,11 +856,8 @@ impl<'a> ModifierNames<'a> { /// Keyboard keys. /// -/// Includes all keys egui is interested in (such as `Home` and `End`) -/// plus a few that are useful for detecting keyboard shortcuts. -/// -/// Many keys are omitted because they are not always physical keys (depending on keyboard language), e.g. `;` and `§`, -/// and are therefore unsuitable as keyboard shortcuts if you want your app to be portable. +/// egui usually uses logical keys, i.e. after applying any user keymap. +// TODO(emilk): split into `LogicalKey` and `PhysicalKey` #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Key { @@ -870,12 +879,28 @@ pub enum Key { PageUp, PageDown, - /// The virtual keycode for the Minus key. + // ---------------------------------------------- + // Punctuation: + /// `:` + Colon, + + /// `,` + Comma, + + /// `-` Minus, - /// The virtual keycode for the Plus/Equals key. + /// `.` + Period, + + /// The for the Plus/Equals key. PlusEquals, + /// `;` + Semicolon, + + // ---------------------------------------------- + // Digits: /// Either from the main row or from the numpad. Num0, @@ -906,6 +931,8 @@ pub enum Key { /// Either from the main row or from the numpad. Num9, + // ---------------------------------------------- + // Letters: A, // Used for cmd+A (select All) B, C, // |CMD COPY| @@ -933,7 +960,8 @@ pub enum Key { Y, Z, // |CMD UNDO| - // The function keys: + // ---------------------------------------------- + // Function keys: F1, F2, F3, @@ -954,9 +982,196 @@ pub enum Key { F18, F19, F20, + // When adding keys, remember to also update `crates/egui-winit/src/lib.rs` + // and [`Self::ALL`]. + // Also: don't add keys last; add them to the group they best belong to. } impl Key { + /// All egui keys + pub const ALL: &'static [Self] = &[ + Self::ArrowDown, + Self::ArrowLeft, + Self::ArrowRight, + Self::ArrowUp, + Self::Escape, + Self::Tab, + Self::Backspace, + Self::Enter, + Self::Space, + Self::Insert, + Self::Delete, + Self::Home, + Self::End, + Self::PageUp, + Self::PageDown, + // Punctuation: + Self::Colon, + Self::Comma, + Self::Minus, + Self::Period, + Self::PlusEquals, + Self::Semicolon, + // Digits: + Self::Num0, + Self::Num1, + Self::Num2, + Self::Num3, + Self::Num4, + Self::Num5, + Self::Num6, + Self::Num7, + Self::Num8, + Self::Num9, + // Letters: + Self::A, + Self::B, + Self::C, + Self::D, + Self::E, + Self::F, + Self::G, + Self::H, + Self::I, + Self::J, + Self::K, + Self::L, + Self::M, + Self::N, + Self::O, + Self::P, + Self::Q, + Self::R, + Self::S, + Self::T, + Self::U, + Self::V, + Self::W, + Self::X, + Self::Y, + Self::Z, + // Function keys: + Self::F1, + Self::F2, + Self::F3, + Self::F4, + Self::F5, + Self::F6, + Self::F7, + Self::F8, + Self::F9, + Self::F10, + Self::F11, + Self::F12, + Self::F13, + Self::F14, + Self::F15, + Self::F16, + Self::F17, + Self::F18, + Self::F19, + Self::F20, + ]; + + /// Converts `"A"` to `Key::A`, `Space` to `Key::Space`, etc. + /// + /// Makes sense for logical keys. + /// + /// This will parse the output of both [`Self::name`] and [`Self::symbol_or_name`], + /// but will also parse single characters, so that both `"-"` and `"Minus"` will return `Key::Minus`. + /// + /// This should support both the names generated in a web browser, + /// and by winit. Please test on both with `eframe`. + pub fn from_name(key: &str) -> Option { + Some(match key { + "ArrowDown" | "Down" | "⏷" => Self::ArrowDown, + "ArrowLeft" | "Left" | "⏴" => Self::ArrowLeft, + "ArrowRight" | "Right" | "⏵" => Self::ArrowRight, + "ArrowUp" | "Up" | "⏶" => Self::ArrowUp, + + "Escape" | "Esc" => Self::Escape, + "Tab" => Self::Tab, + "Backspace" => Self::Backspace, + "Enter" | "Return" => Self::Enter, + "Space" | " " => Self::Space, + + "Help" | "Insert" => Self::Insert, + "Delete" => Self::Delete, + "Home" => Self::Home, + "End" => Self::End, + "PageUp" => Self::PageUp, + "PageDown" => Self::PageDown, + + "Colon" | ":" => Self::Colon, + "Comma" | "," => Self::Comma, + "Minus" | "-" | "−" => Self::Minus, + "Period" | "." => Self::Period, + "Plus" | "+" | "Equals" | "=" => Self::PlusEquals, + "Semicolon" | ";" => Self::Semicolon, + + "0" => Self::Num0, + "1" => Self::Num1, + "2" => Self::Num2, + "3" => Self::Num3, + "4" => Self::Num4, + "5" => Self::Num5, + "6" => Self::Num6, + "7" => Self::Num7, + "8" => Self::Num8, + "9" => Self::Num9, + + "a" | "A" => Self::A, + "b" | "B" => Self::B, + "c" | "C" => Self::C, + "d" | "D" => Self::D, + "e" | "E" => Self::E, + "f" | "F" => Self::F, + "g" | "G" => Self::G, + "h" | "H" => Self::H, + "i" | "I" => Self::I, + "j" | "J" => Self::J, + "k" | "K" => Self::K, + "l" | "L" => Self::L, + "m" | "M" => Self::M, + "n" | "N" => Self::N, + "o" | "O" => Self::O, + "p" | "P" => Self::P, + "q" | "Q" => Self::Q, + "r" | "R" => Self::R, + "s" | "S" => Self::S, + "t" | "T" => Self::T, + "u" | "U" => Self::U, + "v" | "V" => Self::V, + "w" | "W" => Self::W, + "x" | "X" => Self::X, + "y" | "Y" => Self::Y, + "z" | "Z" => Self::Z, + + "F1" => Self::F1, + "F2" => Self::F2, + "F3" => Self::F3, + "F4" => Self::F4, + "F5" => Self::F5, + "F6" => Self::F6, + "F7" => Self::F7, + "F8" => Self::F8, + "F9" => Self::F9, + "F10" => Self::F10, + "F11" => Self::F11, + "F12" => Self::F12, + "F13" => Self::F13, + "F14" => Self::F14, + "F15" => Self::F15, + "F16" => Self::F16, + "F17" => Self::F17, + "F18" => Self::F18, + "F19" => Self::F19, + "F20" => Self::F20, + + _ => return None, + }) + } + /// Emoji or name representing the key pub fn symbol_or_name(self) -> &'static str { // TODO(emilk): add support for more unicode symbols (see for instance https://wincent.com/wiki/Unicode_representations_of_modifier_keys). @@ -980,19 +1195,27 @@ impl Key { Key::ArrowLeft => "Left", Key::ArrowRight => "Right", Key::ArrowUp => "Up", + Key::Escape => "Escape", Key::Tab => "Tab", Key::Backspace => "Backspace", Key::Enter => "Enter", Key::Space => "Space", + Key::Insert => "Insert", Key::Delete => "Delete", Key::Home => "Home", Key::End => "End", Key::PageUp => "PageUp", Key::PageDown => "PageDown", + + Key::Colon => "Colon", + Key::Comma => "Comma", Key::Minus => "Minus", + Key::Period => "Period", Key::PlusEquals => "Plus", + Key::Semicolon => "Semicolon", + Key::Num0 => "0", Key::Num1 => "1", Key::Num2 => "2", @@ -1003,6 +1226,7 @@ impl Key { Key::Num7 => "7", Key::Num8 => "8", Key::Num9 => "9", + Key::A => "A", Key::B => "B", Key::C => "C", @@ -1053,6 +1277,31 @@ impl Key { } } +#[test] +fn test_key_from_name() { + assert_eq!( + Key::ALL.len(), + Key::F20 as usize + 1, + "Some keys are missing in Key::ALL" + ); + + for &key in Key::ALL { + let name = key.name(); + assert_eq!( + Key::from_name(name), + Some(key), + "Failed to roundtrip {key:?} from name {name:?}" + ); + + let symbol = key.symbol_or_name(); + assert_eq!( + Key::from_name(symbol), + Some(key), + "Failed to roundtrip {key:?} from symbol {symbol:?}" + ); + } +} + // ---------------------------------------------------------------------------- /// A keyboard shortcut, e.g. `Ctrl+Alt+W`. diff --git a/crates/egui/src/data/output.rs b/crates/egui/src/data/output.rs index f9ee018c3e4..7a239dd05c5 100644 --- a/crates/egui/src/data/output.rs +++ b/crates/egui/src/data/output.rs @@ -64,6 +64,21 @@ impl FullOutput { } } +/// Information about text being edited. +/// +/// Useful for IME. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct IMEOutput { + /// Where the [`crate::TextEdit`] is located on screen. + pub rect: crate::Rect, + + /// Where the cursor is. + /// + /// This is a very thin rectangle. + pub cursor_rect: crate::Rect, +} + /// The non-rendering part of what egui emits each frame. /// /// You can access (and modify) this with [`crate::Context::output`]. @@ -98,10 +113,10 @@ pub struct PlatformOutput { /// Use by `eframe` web to show/hide mobile keyboard and IME agent. pub mutable_text_under_cursor: bool, - /// Screen-space position of text edit cursor (used for IME). + /// This is et if, and only if, the user is currently editing text. /// - /// Iff `Some`, the user is editing text. - pub text_cursor_pos: Option, + /// Useful for IME. + pub ime: Option, /// The difference in the widget tree since last frame. /// @@ -137,7 +152,7 @@ impl PlatformOutput { copied_text, mut events, mutable_text_under_cursor, - text_cursor_pos, + ime, #[cfg(feature = "accesskit")] accesskit_update, } = newer; @@ -151,7 +166,7 @@ impl PlatformOutput { } self.events.append(&mut events); self.mutable_text_under_cursor = mutable_text_under_cursor; - self.text_cursor_pos = text_cursor_pos.or(self.text_cursor_pos); + self.ime = ime.or(self.ime); #[cfg(feature = "accesskit")] { diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index 3a9c60ebaa6..df1b30a9287 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -899,7 +899,8 @@ pub enum ViewportCommand { /// The the window icon. Icon(Option>), - IMEPosition(Pos2), + /// Set the IME cursor editing area. + IMERect(crate::Rect), IMEAllowed(bool), IMEPurpose(IMEPurpose), diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 0b4bb0b9d9f..adf53149146 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -683,7 +683,7 @@ impl<'t> TextEdit<'t> { paint_cursor_selection(ui, &painter, text_draw_pos, &galley, &cursor_range); if text.is_mutable() { - let cursor_pos = paint_cursor_end( + let cursor_rect = paint_cursor_end( ui, row_height, &painter, @@ -694,23 +694,14 @@ impl<'t> TextEdit<'t> { let is_fully_visible = ui.clip_rect().contains_rect(rect); // TODO: remove this HACK workaround for https://github.com/emilk/egui/issues/1531 if (response.changed || selection_changed) && !is_fully_visible { - ui.scroll_to_rect(cursor_pos, None); // keep cursor in view + ui.scroll_to_rect(cursor_rect, None); // keep cursor in view } if interactive { - // eframe web uses `text_cursor_pos` when showing IME, - // so only set it when text is editable and visible! - // But `winit` and `egui_web` differs in how to set the - // position of IME. - if cfg!(target_arch = "wasm32") { - ui.ctx().output_mut(|o| { - o.text_cursor_pos = Some(cursor_pos.left_top()); - }); - } else { - ui.ctx().output_mut(|o| { - o.text_cursor_pos = Some(cursor_pos.left_bottom()); - }); - } + // For IME, so only set it when text is editable and visible! + ui.ctx().output_mut(|o| { + o.ime = Some(crate::output::IMEOutput { rect, cursor_rect }); + }); } } } diff --git a/crates/egui_extras/Cargo.toml b/crates/egui_extras/Cargo.toml index 92c2a44af6e..21bcfe20a76 100644 --- a/crates/egui_extras/Cargo.toml +++ b/crates/egui_extras/Cargo.toml @@ -53,7 +53,7 @@ image = ["dep:image"] puffin = ["dep:puffin", "egui/puffin"] ## Support loading svg images. -svg = ["resvg", "tiny-skia", "usvg"] +svg = ["resvg"] ## Enable better syntax highlighting using [`syntect`](https://docs.rs/syntect). syntect = ["dep:syntect"] @@ -93,8 +93,6 @@ syntect = { version = "5", optional = true, default-features = false, features = # svg feature resvg = { version = "0.28", optional = true, default-features = false } -tiny-skia = { version = "0.8", optional = true, default-features = false } # must be updated in lock-step with resvg -usvg = { version = "0.28", optional = true, default-features = false } # http feature ehttp = { version = "0.3.1", optional = true, default-features = false } diff --git a/crates/egui_extras/src/image.rs b/crates/egui_extras/src/image.rs index 72be580d2c9..fc609e1dae6 100644 --- a/crates/egui_extras/src/image.rs +++ b/crates/egui_extras/src/image.rs @@ -2,6 +2,9 @@ use egui::{mutex::Mutex, TextureOptions}; +#[cfg(feature = "svg")] +use resvg::{tiny_skia, usvg}; + #[cfg(feature = "svg")] pub use usvg::FitTo; diff --git a/crates/egui_extras/src/loaders/svg_loader.rs b/crates/egui_extras/src/loaders/svg_loader.rs index ff2cab86844..00a3bdfef89 100644 --- a/crates/egui_extras/src/loaders/svg_loader.rs +++ b/crates/egui_extras/src/loaders/svg_loader.rs @@ -1,10 +1,13 @@ +use std::{mem::size_of, path::Path, sync::Arc}; + use egui::{ ahash::HashMap, load::{BytesPoll, ImageLoadResult, ImageLoader, ImagePoll, LoadError, SizeHint}, mutex::Mutex, ColorImage, }; -use std::{mem::size_of, path::Path, sync::Arc}; + +use resvg::usvg; type Entry = Result, String>; diff --git a/crates/egui_glow/Cargo.toml b/crates/egui_glow/Cargo.toml index 3d169aa3b51..71005497706 100644 --- a/crates/egui_glow/Cargo.toml +++ b/crates/egui_glow/Cargo.toml @@ -69,9 +69,9 @@ wasm-bindgen = "0.2" [dev-dependencies] -glutin = "0.30" # examples/pure_glow +glutin = "0.31" # examples/pure_glow raw-window-handle.workspace = true -glutin-winit = "0.3.0" +glutin-winit = "0.4.0" [[example]] diff --git a/crates/egui_glow/examples/pure_glow.rs b/crates/egui_glow/examples/pure_glow.rs index 1bff00d5837..c9fbb33dab0 100644 --- a/crates/egui_glow/examples/pure_glow.rs +++ b/crates/egui_glow/examples/pure_glow.rs @@ -19,7 +19,7 @@ impl GlutinWindowContext { #[allow(unsafe_code)] unsafe fn new(event_loop: &winit::event_loop::EventLoopWindowTarget) -> Self { use egui::NumExt; - use glutin::context::NotCurrentGlContextSurfaceAccessor; + use glutin::context::NotCurrentGlContext; use glutin::display::GetGlDisplay; use glutin::display::GlDisplay; use glutin::prelude::GlSurface; @@ -42,7 +42,7 @@ impl GlutinWindowContext { log::debug!("trying to get gl_config"); let (mut window, gl_config) = glutin_winit::DisplayBuilder::new() // let glutin-winit helper crate handle the complex parts of opengl context creation - .with_preference(glutin_winit::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 + .with_preference(glutin_winit::ApiPreference::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 .with_window_builder(Some(winit_window_builder.clone())) .build( event_loop, @@ -150,7 +150,9 @@ pub enum UserEvent { fn main() { let mut clear_color = [0.1, 0.1, 0.1]; - let event_loop = winit::event_loop::EventLoopBuilder::::with_user_event().build(); + let event_loop = winit::event_loop::EventLoopBuilder::::with_user_event() + .build() + .unwrap(); let (gl_window, gl) = create_display(&event_loop); let gl = std::sync::Arc::new(gl); @@ -168,7 +170,7 @@ fn main() { let mut repaint_delay = std::time::Duration::MAX; - event_loop.run(move |event, _, control_flow| { + let _ = event_loop.run(move |event, event_loop_window_target| { let mut redraw = || { let mut quit = false; @@ -182,18 +184,20 @@ fn main() { }); }); - *control_flow = if quit { - winit::event_loop::ControlFlow::Exit - } else if repaint_delay.is_zero() { - gl_window.window().request_redraw(); - winit::event_loop::ControlFlow::Poll - } else if let Some(repaint_delay_instant) = - std::time::Instant::now().checked_add(repaint_delay) - { - winit::event_loop::ControlFlow::WaitUntil(repaint_delay_instant) + if quit { + event_loop_window_target.exit(); } else { - winit::event_loop::ControlFlow::Wait - }; + event_loop_window_target.set_control_flow(if repaint_delay.is_zero() { + gl_window.window().request_redraw(); + winit::event_loop::ControlFlow::Poll + } else if let Some(repaint_after_instant) = + std::time::Instant::now().checked_add(repaint_delay) + { + winit::event_loop::ControlFlow::WaitUntil(repaint_after_instant) + } else { + winit::event_loop::ControlFlow::Wait + }); + } { unsafe { @@ -214,25 +218,20 @@ fn main() { }; match event { - // Platform-dependent event handlers to workaround a winit bug - // See: https://github.com/rust-windowing/winit/issues/987 - // See: https://github.com/rust-windowing/winit/issues/1619 - winit::event::Event::RedrawEventsCleared if cfg!(target_os = "windows") => redraw(), - winit::event::Event::RedrawRequested(_) if !cfg!(target_os = "windows") => redraw(), - winit::event::Event::WindowEvent { event, .. } => { use winit::event::WindowEvent; if matches!(event, WindowEvent::CloseRequested | WindowEvent::Destroyed) { - *control_flow = winit::event_loop::ControlFlow::Exit; + event_loop_window_target.exit(); + return; + } + + if matches!(event, WindowEvent::RedrawRequested) { + redraw(); + return; } if let winit::event::WindowEvent::Resized(physical_size) = &event { gl_window.resize(*physical_size); - } else if let winit::event::WindowEvent::ScaleFactorChanged { - new_inner_size, .. - } = &event - { - gl_window.resize(**new_inner_size); } let event_response = egui_glow.on_window_event(gl_window.window(), &event); @@ -245,7 +244,7 @@ fn main() { winit::event::Event::UserEvent(UserEvent::Redraw(delay)) => { repaint_delay = delay; } - winit::event::Event::LoopDestroyed => { + winit::event::Event::LoopExiting => { egui_glow.destroy(); } winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached { diff --git a/crates/egui_glow/src/winit.rs b/crates/egui_glow/src/winit.rs index 567ce65f60d..5d87d000416 100644 --- a/crates/egui_glow/src/winit.rs +++ b/crates/egui_glow/src/winit.rs @@ -58,7 +58,7 @@ impl EguiGlow { pub fn on_window_event( &mut self, window: &winit::window::Window, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResponse { self.egui_winit.on_window_event(window, event) } diff --git a/deny.toml b/deny.toml index 73f492c5d91..6882337c630 100644 --- a/deny.toml +++ b/deny.toml @@ -34,28 +34,26 @@ deny = [ ] skip = [ - { name = "arrayvec" }, # old version via tiny-skiaz - { name = "base64" }, # small crate, old version from usvg - { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … - { name = "glow" }, # TODO(@wumpf): Old version use for glow backend right now, newer for wgpu. Updating this trickles out to updating winit. - { name = "glutin_wgl_sys" }, # TODO(@wumpf): Old version use for glow backend right now, newer for wgpu. Updating this trickles out to updating winit. - { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 - { name = "memoffset" }, # tiny dependency - { name = "nix" }, # old version via winit - { name = "redox_syscall" }, # old version via winit - { name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu. - { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' - { name = "tiny-skia" }, # winit uses a different version from egui_extras (TODO(emilk): update egui_extras!) - { name = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg - { name = "wayland-sys" }, # old version via winit - { name = "windows_x86_64_msvc" }, # old version via glutin - { name = "windows-sys" }, # old version via glutin - { name = "windows" }, # old version via accesskit + { name = "arrayvec" }, # old version via tiny-skiaz + { name = "base64" }, # small crate, old version from usvg + { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … + { name = "glow" }, # TODO(@wumpf): updatere glow + { name = "glutin_wgl_sys" }, # TODO(@wumpf): updatere glow + { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 + { name = "memoffset" }, # tiny dependency + { name = "quick-xml" }, # old version via wayland-scanner + { name = "redox_syscall" }, # old version via directories-next + { name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu. + { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' + { name = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg + { name = "windows" }, # old version via accesskit_windows ] skip-tree = [ { name = "criterion" }, # dev-dependency { name = "foreign-types" }, # small crate. Old version via cocoa and core-graphics (winit). + { name = "objc2" }, # old version via accesskit_macos { name = "rfd" }, # example dependency + { name = "tiny-skia" }, # old version via old resvg in egui_extras - see https://github.com/emilk/egui/issues/3652 ] diff --git a/examples/multiple_viewports/Cargo.toml b/examples/multiple_viewports/Cargo.toml index 18898bdc0a8..1e77e32ea74 100644 --- a/examples/multiple_viewports/Cargo.toml +++ b/examples/multiple_viewports/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" rust-version = "1.72" publish = false +[features] +wgpu = ["eframe/wgpu"] [dependencies] eframe = { path = "../../crates/eframe", features = [ From 449678e61776ef60f34eb2638daea8efb52d7593 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 18 Dec 2023 15:15:20 +0100 Subject: [PATCH 14/45] Update to glow 0.13 (#3715) --- Cargo.lock | 18 +++------------ Cargo.toml | 1 + crates/eframe/Cargo.toml | 2 +- crates/eframe/src/native/glow_integration.rs | 2 ++ crates/egui_glow/Cargo.toml | 2 +- crates/egui_glow/examples/pure_glow.rs | 1 + deny.toml | 24 +++++++++----------- 7 files changed, 20 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6137494b7b2..978db37bd2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1169,7 +1169,7 @@ dependencies = [ "egui-wgpu", "egui-winit", "egui_glow", - "glow 0.12.3", + "glow", "glutin", "glutin-winit", "image", @@ -1308,7 +1308,7 @@ dependencies = [ "document-features", "egui", "egui-winit", - "glow 0.12.3", + "glow", "glutin", "glutin-winit", "log", @@ -1766,18 +1766,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "glow" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0fe580e4b60a8ab24a868bc08e2f03cbcb20d3d676601fa909386713333728" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "glow" version = "0.13.0" @@ -4286,7 +4274,7 @@ dependencies = [ "block", "core-graphics-types", "d3d12", - "glow 0.13.0", + "glow", "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", diff --git a/Cargo.toml b/Cargo.toml index 134f3244180..641073f4d79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ opt-level = 2 [workspace.dependencies] criterion = { version = "0.5.1", default-features = false } +glow = "0.13" puffin = "0.18" raw-window-handle = "0.5.0" thiserror = "1.0.37" diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index e9f360802d4..3d7d8a28f1e 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -116,7 +116,7 @@ thiserror.workspace = true document-features = { version = "0.2", optional = true } egui_glow = { version = "0.24.1", path = "../egui_glow", optional = true, default-features = false } -glow = { version = "0.12", optional = true } +glow = { workspace = true, optional = true } ron = { version = "0.8", optional = true, features = ["integer128"] } serde = { version = "1", optional = true, features = ["derive"] } diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index cc77972be97..922039bf307 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -5,6 +5,8 @@ //! There is a bunch of improvements we could do, //! like removing a bunch of `unwraps`. +#![allow(clippy::arc_with_non_send_sync)] // glow::Context was accidentally non-Sync in glow 0.13, but that will be fixed in future releases of glow: https://github.com/grovesNL/glow/commit/c4a5f7151b9b4bbb380faa06ec27415235d1bf7e + use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant}; use glutin::{ diff --git a/crates/egui_glow/Cargo.toml b/crates/egui_glow/Cargo.toml index 71005497706..bd0657dd994 100644 --- a/crates/egui_glow/Cargo.toml +++ b/crates/egui_glow/Cargo.toml @@ -49,7 +49,7 @@ egui = { version = "0.24.1", path = "../egui", default-features = false, feature ] } bytemuck = "1.7" -glow = "0.12" +glow.workspace = true log = { version = "0.4", features = ["std"] } memoffset = "0.7" diff --git a/crates/egui_glow/examples/pure_glow.rs b/crates/egui_glow/examples/pure_glow.rs index c9fbb33dab0..a802249a718 100644 --- a/crates/egui_glow/examples/pure_glow.rs +++ b/crates/egui_glow/examples/pure_glow.rs @@ -2,6 +2,7 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release #![allow(unsafe_code)] +#![allow(clippy::arc_with_non_send_sync)] // glow::Context was accidentally non-Sync in glow 0.13, but that will be fixed in future releases of glow: https://github.com/grovesNL/glow/commit/c4a5f7151b9b4bbb380faa06ec27415235d1bf7e use egui_winit::winit; diff --git a/deny.toml b/deny.toml index 6882337c630..5460ef4eb25 100644 --- a/deny.toml +++ b/deny.toml @@ -34,19 +34,17 @@ deny = [ ] skip = [ - { name = "arrayvec" }, # old version via tiny-skiaz - { name = "base64" }, # small crate, old version from usvg - { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … - { name = "glow" }, # TODO(@wumpf): updatere glow - { name = "glutin_wgl_sys" }, # TODO(@wumpf): updatere glow - { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 - { name = "memoffset" }, # tiny dependency - { name = "quick-xml" }, # old version via wayland-scanner - { name = "redox_syscall" }, # old version via directories-next - { name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu. - { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' - { name = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg - { name = "windows" }, # old version via accesskit_windows + { name = "arrayvec" }, # old version via tiny-skiaz + { name = "base64" }, # small crate, old version from usvg + { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … + { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 + { name = "memoffset" }, # tiny dependency + { name = "quick-xml" }, # old version via wayland-scanner + { name = "redox_syscall" }, # old version via directories-next + { name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu. + { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' + { name = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg + { name = "windows" }, # old version via accesskit_windows ] skip-tree = [ { name = "criterion" }, # dev-dependency From a726e656a4feeace59d3696bc32548be6d93f600 Mon Sep 17 00:00:00 2001 From: crispy-strawberry <145422627+crispy-strawberry@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:13:09 +0530 Subject: [PATCH 15/45] Very small typo fix (#3693) Small spelling error - changed `Addintional` to `Additional` --- crates/eframe/src/epi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 95ed9feaccd..3d9867a6323 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -217,7 +217,7 @@ pub enum HardwareAcceleration { /// Options controlling the behavior of a native window. /// -/// Addintional windows can be opened using (egui viewports)[`egui::viewport`]. +/// Additional windows can be opened using (egui viewports)[`egui::viewport`]. /// /// Set the window title and size using [`Self::viewport`]. /// From 4060d02f27c9ccca027c26f9297bafff46f484b3 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 18 Dec 2023 17:22:31 +0100 Subject: [PATCH 16/45] Add a bunch of `#[inline]` --- crates/egui/src/load.rs | 4 ++++ crates/emath/src/rect.rs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/crates/egui/src/load.rs b/crates/egui/src/load.rs index f1c61a644e1..b39e4812e51 100644 --- a/crates/egui/src/load.rs +++ b/crates/egui/src/load.rs @@ -145,12 +145,14 @@ pub enum SizeHint { } impl Default for SizeHint { + #[inline] fn default() -> Self { Self::Scale(1.0.ord()) } } impl From for SizeHint { + #[inline] fn from(value: Vec2) -> Self { Self::Size(value.x.round() as u32, value.y.round() as u32) } @@ -412,6 +414,7 @@ impl From<(TextureId, Vec2)> for SizedTexture { } impl<'a> From<&'a TextureHandle> for SizedTexture { + #[inline] fn from(handle: &'a TextureHandle) -> Self { Self::from_handle(handle) } @@ -435,6 +438,7 @@ pub enum TexturePoll { } impl TexturePoll { + #[inline] pub fn size(self) -> Option { match self { TexturePoll::Pending { size } => size, diff --git a/crates/emath/src/rect.rs b/crates/emath/src/rect.rs index e671135fce6..16f5633aadd 100644 --- a/crates/emath/src/rect.rs +++ b/crates/emath/src/rect.rs @@ -409,6 +409,7 @@ impl Rect { /// Linearly interpolate so that `[0, 0]` is [`Self::min`] and /// `[1, 1]` is [`Self::max`]. + #[inline] pub fn lerp_inside(&self, t: Vec2) -> Pos2 { Pos2 { x: lerp(self.min.x..=self.max.x, t.x), @@ -417,6 +418,7 @@ impl Rect { } /// Linearly self towards other rect. + #[inline] pub fn lerp_towards(&self, other: &Rect, t: f32) -> Self { Self { min: self.min.lerp(other.min, t), @@ -611,6 +613,7 @@ impl std::fmt::Debug for Rect { /// from (min, max) or (left top, right bottom) impl From<[Pos2; 2]> for Rect { + #[inline] fn from([min, max]: [Pos2; 2]) -> Self { Self { min, max } } From 25903d0a02a3745bcff55b7d197b506832d8eecd Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 18 Dec 2023 17:24:03 +0100 Subject: [PATCH 17/45] Add `TexturePoll::texture_id` --- crates/egui/src/load.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/egui/src/load.rs b/crates/egui/src/load.rs index b39e4812e51..ca668b262f2 100644 --- a/crates/egui/src/load.rs +++ b/crates/egui/src/load.rs @@ -439,12 +439,20 @@ pub enum TexturePoll { impl TexturePoll { #[inline] - pub fn size(self) -> Option { + pub fn size(&self) -> Option { match self { - TexturePoll::Pending { size } => size, + TexturePoll::Pending { size } => *size, TexturePoll::Ready { texture } => Some(texture.size), } } + + #[inline] + pub fn texture_id(&self) -> Option { + match self { + TexturePoll::Pending { .. } => None, + TexturePoll::Ready { texture } => Some(texture.id), + } + } } pub type TextureLoadResult = Result; From 4a2cafee7cc9bbe01c95d08d7737e08657fb6d5b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 18 Dec 2023 17:25:10 +0100 Subject: [PATCH 18/45] Add a TODO --- crates/eframe/src/web/events.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 2418bed95c5..cec66bc20a5 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -517,6 +517,7 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu move |event: web_sys::DragEvent, runner| { if let Some(data_transfer) = event.data_transfer() { + // TODO(https://github.com/emilk/egui/issues/3702): support dropping folders runner.input.raw.hovered_files.clear(); runner.needs_repaint.repaint_asap(); From 9253cafedda22a719ccfc7f898f56e8da631b3b0 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 19 Dec 2023 09:51:05 +0100 Subject: [PATCH 19/45] `eframe`: If both `glow` and `wgpu` features are enabled, default to `wgpu` (#3717) By default, only the `glow` feature is enabled, so if the user added `wgpu` to the feature list they probably wanted to use `wgpu`. --- crates/eframe/src/epi.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 3d9867a6323..115235cd34a 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -526,16 +526,23 @@ pub enum Renderer { #[cfg(any(feature = "glow", feature = "wgpu"))] impl Default for Renderer { fn default() -> Self { + #[cfg(not(feature = "glow"))] + #[cfg(not(feature = "wgpu"))] + compile_error!("eframe: you must enable at least one of the rendering backend features: 'glow' or 'wgpu'"); + #[cfg(feature = "glow")] + #[cfg(not(feature = "wgpu"))] return Self::Glow; #[cfg(not(feature = "glow"))] #[cfg(feature = "wgpu")] return Self::Wgpu; - #[cfg(not(feature = "glow"))] - #[cfg(not(feature = "wgpu"))] - compile_error!("eframe: you must enable at least one of the rendering backend features: 'glow' or 'wgpu'"); + // By default, only the `glow` feature is enabled, so if the user added `wgpu` to the feature list + // they probably wanted to use wgpu: + #[cfg(feature = "glow")] + #[cfg(feature = "wgpu")] + return Self::Wgpu; } } From add1695624d136484542abf1bc18749cf7b8eee3 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler <49431240+abey79@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:58:59 +0100 Subject: [PATCH 20/45] Fix plot auto-bounds unset by default (#3722) These PR recently cleaned up the code around auto-bounds, but introduced an involuntary change whereby auto-bounds would not be enabled by default. All plots would default to being not properly centred as a result. - #3587 - #3586 This PR changes the default back to enabled. It also deprecates `auto_bounds_x()` and `auto_bounds_y()`, which could only enable auto-bounds (which is not very useful as auto-bounds were, and now are again, enabled by default). A new `auto_bounds()` API can now be sued to disable auto-bounds if needed. Fixes #3712 Fixes https://github.com/rerun-io/rerun/issues/4503 --- crates/egui_plot/src/lib.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/egui_plot/src/lib.rs b/crates/egui_plot/src/lib.rs index a10953311dc..41c5ec63482 100644 --- a/crates/egui_plot/src/lib.rs +++ b/crates/egui_plot/src/lib.rs @@ -225,7 +225,7 @@ impl Plot { allow_scroll: true, allow_double_click_reset: true, allow_boxed_zoom: true, - default_auto_bounds: false.into(), + default_auto_bounds: true.into(), min_auto_bounds: PlotBounds::NOTHING, margin_fraction: Vec2::splat(0.05), boxed_zoom_pointer_button: PointerButton::Secondary, @@ -498,7 +498,17 @@ impl Plot { self } + /// Set whether the bounds should be automatically set based on data by default. + /// + /// This is enabled by default. + #[inline] + pub fn auto_bounds(mut self, auto_bounds: Vec2b) -> Self { + self.default_auto_bounds = auto_bounds; + self + } + /// Expand bounds to fit all items across the x axis, including values given by `include_x`. + #[deprecated = "Use `auto_bounds` instead"] #[inline] pub fn auto_bounds_x(mut self) -> Self { self.default_auto_bounds.x = true; @@ -506,6 +516,7 @@ impl Plot { } /// Expand bounds to fit all items across the y axis, including values given by `include_y`. + #[deprecated = "Use `auto_bounds` instead"] #[inline] pub fn auto_bounds_y(mut self) -> Self { self.default_auto_bounds.y = true; From 963be247f26f2ddd72c03649e9ebc0ec88ac8431 Mon Sep 17 00:00:00 2001 From: PingPongun <46752179+PingPongun@users.noreply.github.com> Date: Wed, 20 Dec 2023 08:09:03 +0100 Subject: [PATCH 21/45] Update resvg dependency of egui_extras (#3719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update `resvg` from v0.28 to v0.37. Remove related, unnecessary entries from `deny.toml`. ⚠ In example `images` ferris is scaled differently, but I guess that now it scales in expected way (takes all available space; before this PR it takes up to space that, was available at first render- it does not upscale). This PR is minimal adaptation to new `resvg` api and small related simplification, however it should be considered to update loaders (currently if svg image initially was small and was scaled up it will be blurred, see https://github.com/emilk/egui/issues/3501). As svg image now scales over render size, problem will be more often seen now. (currently `SvgLoader` theoretically should rerender for different sizes (but I guess it will result in memory leak in that case), but refreshing is stopped earlier in `DefaultTextureLoader`). I have initial version of loaders update, that will fix issue with svg scaling (and also enable e.g. reloading image if file has been changed), I will submit these changes in separate PR once this one is merged. Closes . --- Cargo.lock | 118 +++++++++---------- crates/egui/src/load.rs | 2 - crates/egui_extras/Cargo.toml | 2 +- crates/egui_extras/src/image.rs | 58 ++++----- crates/egui_extras/src/loaders/svg_loader.rs | 12 +- deny.toml | 4 - 6 files changed, 83 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 978db37bd2e..58a98024f22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,12 +474,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.4" @@ -1061,9 +1055,9 @@ dependencies = [ [[package]] name = "data-url" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "deranged" @@ -2091,9 +2085,9 @@ dependencies = [ [[package]] name = "imagesize" -version = "0.10.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df19da1e92fbfec043ca97d622955381b1f3ee72a180ec999912df31b1ccd951" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "indexmap" @@ -2224,9 +2218,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kurbo" -version = "0.8.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" dependencies = [ "arrayvec", ] @@ -2775,7 +2769,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ - "base64 0.21.4", + "base64", "indexmap", "line-wrap", "quick-xml 0.31.0", @@ -3070,15 +3064,15 @@ checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" [[package]] name = "resvg" -version = "0.28.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c115863f2d3621999cf187e318bc92b16402dfeff6a48c74df700d77381394c1" +checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4" dependencies = [ "log", "pico-args", "rgb", "svgtypes", - "tiny-skia 0.8.4", + "tiny-skia", "usvg", ] @@ -3137,7 +3131,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64 0.21.4", + "base64", "bitflags 2.4.0", "serde", "serde_derive", @@ -3145,12 +3139,9 @@ dependencies = [ [[package]] name = "roxmltree" -version = "0.15.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9de9831a129b122e7e61f242db509fa9d0838008bf0b29bb0624669edfe48a" -dependencies = [ - "xmlparser", -] +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" [[package]] name = "rustc-demangle" @@ -3286,7 +3277,7 @@ dependencies = [ "log", "memmap2", "smithay-client-toolkit", - "tiny-skia 0.11.2", + "tiny-skia", ] [[package]] @@ -3516,10 +3507,11 @@ dependencies = [ [[package]] name = "svgtypes" -version = "0.8.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22975e8a2bac6a76bb54f898a6b18764633b00e780330f0b689f65afb3975564" +checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70" dependencies = [ + "kurbo", "siphasher", ] @@ -3674,48 +3666,24 @@ dependencies = [ [[package]] name = "tiny-skia" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "png", - "tiny-skia-path 0.8.4", -] - -[[package]] -name = "tiny-skia" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b72a92a05db376db09fe6d50b7948d106011761c05a6a45e23e17ee9b556222" +checksum = "b6a067b809476893fce6a254cf285850ff69c847e6cfbade6a20b655b6c7e80d" dependencies = [ "arrayref", "arrayvec", "bytemuck", "cfg-if", "log", - "tiny-skia-path 0.11.2", -] - -[[package]] -name = "tiny-skia-path" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", + "png", + "tiny-skia-path", ] [[package]] name = "tiny-skia-path" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac3865b9708fc7e1961a65c3a4fa55e984272f33092d3c859929f887fceb647" +checksum = "5de35e8a90052baaaf61f171680ac2f8e925a1e43ea9d2e3a00514772250e541" dependencies = [ "arrayref", "bytemuck", @@ -3910,7 +3878,7 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" dependencies = [ - "base64 0.21.4", + "base64", "flate2", "log", "once_cell", @@ -3941,22 +3909,46 @@ dependencies = [ [[package]] name = "usvg" -version = "0.28.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5b7c2b30845b3348c067ca3d09e20cc6e327c288f0ca4c48698712abf432e9" +checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756" +dependencies = [ + "base64", + "log", + "pico-args", + "usvg-parser", + "usvg-tree", + "xmlwriter", +] + +[[package]] +name = "usvg-parser" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc" dependencies = [ - "base64 0.13.1", "data-url", "flate2", "imagesize", "kurbo", "log", - "rctree", "roxmltree", "simplecss", "siphasher", + "svgtypes", + "usvg-tree", +] + +[[package]] +name = "usvg-tree" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3" +dependencies = [ + "rctree", "strict-num", "svgtypes", + "tiny-skia-path", ] [[package]] @@ -4693,10 +4685,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" [[package]] -name = "xmlparser" -version = "0.13.5" +name = "xmlwriter" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "yaml-rust" diff --git a/crates/egui/src/load.rs b/crates/egui/src/load.rs index ca668b262f2..0b31af1c9d6 100644 --- a/crates/egui/src/load.rs +++ b/crates/egui/src/load.rs @@ -127,8 +127,6 @@ pub type Result = std::result::Result; /// Used mostly for rendering SVG:s to a good size. /// /// All variants will preserve the original aspect ratio. -/// -/// Similar to `usvg::FitTo`. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum SizeHint { /// Scale original size by some factor. diff --git a/crates/egui_extras/Cargo.toml b/crates/egui_extras/Cargo.toml index 21bcfe20a76..f6b8c2b1104 100644 --- a/crates/egui_extras/Cargo.toml +++ b/crates/egui_extras/Cargo.toml @@ -92,7 +92,7 @@ syntect = { version = "5", optional = true, default-features = false, features = ] } # svg feature -resvg = { version = "0.28", optional = true, default-features = false } +resvg = { version = "0.37", optional = true, default-features = false } # http feature ehttp = { version = "0.3.1", optional = true, default-features = false } diff --git a/crates/egui_extras/src/image.rs b/crates/egui_extras/src/image.rs index fc609e1dae6..31263aa9a66 100644 --- a/crates/egui_extras/src/image.rs +++ b/crates/egui_extras/src/image.rs @@ -3,10 +3,7 @@ use egui::{mutex::Mutex, TextureOptions}; #[cfg(feature = "svg")] -use resvg::{tiny_skia, usvg}; - -#[cfg(feature = "svg")] -pub use usvg::FitTo; +use egui::SizeHint; /// An image to be shown in egui. /// @@ -67,7 +64,7 @@ impl RetainedImage { /// On invalid image #[cfg(feature = "svg")] pub fn from_svg_bytes(debug_name: impl Into, svg_bytes: &[u8]) -> Result { - Self::from_svg_bytes_with_size(debug_name, svg_bytes, FitTo::Original) + Self::from_svg_bytes_with_size(debug_name, svg_bytes, None) } /// Pass in the str of an SVG that you've loaded. @@ -88,11 +85,11 @@ impl RetainedImage { pub fn from_svg_bytes_with_size( debug_name: impl Into, svg_bytes: &[u8], - size: FitTo, + size_hint: Option, ) -> Result { Ok(Self::from_color_image( debug_name, - load_svg_bytes_with_size(svg_bytes, size)?, + load_svg_bytes_with_size(svg_bytes, size_hint)?, )) } @@ -223,7 +220,7 @@ pub fn load_image_bytes(image_bytes: &[u8]) -> Result /// On invalid image #[cfg(feature = "svg")] pub fn load_svg_bytes(svg_bytes: &[u8]) -> Result { - load_svg_bytes_with_size(svg_bytes, FitTo::Original) + load_svg_bytes_with_size(svg_bytes, None) } /// Load an SVG and rasterize it into an egui image with a scaling parameter. @@ -235,36 +232,31 @@ pub fn load_svg_bytes(svg_bytes: &[u8]) -> Result { #[cfg(feature = "svg")] pub fn load_svg_bytes_with_size( svg_bytes: &[u8], - fit_to: FitTo, + size_hint: Option, ) -> Result { + use resvg::tiny_skia::{IntSize, Pixmap}; + use resvg::usvg::{Options, Tree, TreeParsing}; + crate::profile_function!(); - let opt = usvg::Options::default(); - - let rtree = usvg::Tree::from_data(svg_bytes, &opt).map_err(|err| err.to_string())?; - - let pixmap_size = rtree.size.to_screen_size(); - let [w, h] = match fit_to { - FitTo::Original => [pixmap_size.width(), pixmap_size.height()], - FitTo::Size(w, h) => [w, h], - FitTo::Height(h) => [ - (pixmap_size.width() as f32 * (h as f32 / pixmap_size.height() as f32)) as u32, - h, - ], - FitTo::Width(w) => [ - w, - (pixmap_size.height() as f32 * (w as f32 / pixmap_size.width() as f32)) as u32, - ], - FitTo::Zoom(z) => [ - (pixmap_size.width() as f32 * z) as u32, - (pixmap_size.height() as f32 * z) as u32, - ], + let opt = Options::default(); + + let mut rtree = Tree::from_data(svg_bytes, &opt).map_err(|err| err.to_string())?; + + let mut size = rtree.size.to_int_size(); + match size_hint { + None => (), + Some(SizeHint::Size(w, h)) => size = size.scale_to(IntSize::from_wh(w, h).unwrap()), + Some(SizeHint::Height(h)) => size = size.scale_to_height(h).unwrap(), + Some(SizeHint::Width(w)) => size = size.scale_to_width(w).unwrap(), + Some(SizeHint::Scale(z)) => size = size.scale_by(z.into_inner()).unwrap(), }; + let (w, h) = (size.width(), size.height()); - let mut pixmap = tiny_skia::Pixmap::new(w, h) - .ok_or_else(|| format!("Failed to create SVG Pixmap of size {w}x{h}"))?; + let mut pixmap = + Pixmap::new(w, h).ok_or_else(|| format!("Failed to create SVG Pixmap of size {w}x{h}"))?; - resvg::render(&rtree, fit_to, Default::default(), pixmap.as_mut()) - .ok_or_else(|| "Failed to render SVG".to_owned())?; + rtree.size = size.to_size(); + resvg::Tree::from_usvg(&rtree).render(Default::default(), &mut pixmap.as_mut()); let image = egui::ColorImage::from_rgba_unmultiplied([w as _, h as _], pixmap.data()); diff --git a/crates/egui_extras/src/loaders/svg_loader.rs b/crates/egui_extras/src/loaders/svg_loader.rs index 00a3bdfef89..1794a8724f4 100644 --- a/crates/egui_extras/src/loaders/svg_loader.rs +++ b/crates/egui_extras/src/loaders/svg_loader.rs @@ -7,8 +7,6 @@ use egui::{ ColorImage, }; -use resvg::usvg; - type Entry = Result, String>; #[derive(Default)] @@ -51,14 +49,8 @@ impl ImageLoader for SvgLoader { match ctx.try_load_bytes(&uri) { Ok(BytesPoll::Ready { bytes, .. }) => { log::trace!("started loading {uri:?}"); - let fit_to = match size_hint { - SizeHint::Scale(factor) => usvg::FitTo::Zoom(factor.into_inner()), - SizeHint::Width(w) => usvg::FitTo::Width(w), - SizeHint::Height(h) => usvg::FitTo::Height(h), - SizeHint::Size(w, h) => usvg::FitTo::Size(w, h), - }; - let result = - crate::image::load_svg_bytes_with_size(&bytes, fit_to).map(Arc::new); + let result = crate::image::load_svg_bytes_with_size(&bytes, Some(size_hint)) + .map(Arc::new); log::trace!("finished loading {uri:?}"); cache.insert((uri, size_hint), result.clone()); match result { diff --git a/deny.toml b/deny.toml index 5460ef4eb25..633e8bf00ac 100644 --- a/deny.toml +++ b/deny.toml @@ -34,8 +34,6 @@ deny = [ ] skip = [ - { name = "arrayvec" }, # old version via tiny-skiaz - { name = "base64" }, # small crate, old version from usvg { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 { name = "memoffset" }, # tiny dependency @@ -43,7 +41,6 @@ skip = [ { name = "redox_syscall" }, # old version via directories-next { name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu. { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' - { name = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg { name = "windows" }, # old version via accesskit_windows ] skip-tree = [ @@ -51,7 +48,6 @@ skip-tree = [ { name = "foreign-types" }, # small crate. Old version via cocoa and core-graphics (winit). { name = "objc2" }, # old version via accesskit_macos { name = "rfd" }, # example dependency - { name = "tiny-skia" }, # old version via old resvg in egui_extras - see https://github.com/emilk/egui/issues/3652 ] From e36b9811180476fadd5b67c3e311ac9ce5a82302 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 21 Dec 2023 09:08:16 +0100 Subject: [PATCH 22/45] Add @Bromeon and @EmbersArc as CODEOWNERS for egui_plot (#3711) Please take a look @Bromeon and @EmbersArc! You have both contributed substantially to `egui_plot` (more than I have). This is me inviting you to co-maintain the `egui_plot` crate. If you accept, you'll get a notification when someone opens a PR touching `egui_plot`, and you can review and merge it as you see fit. You can also merge your own egui_plot PRs without waiting on me to review them. --- CODEOWNERS | 1 + crates/egui_plot/Cargo.toml | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000000..1c8d123d025 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +/crates/egui_plot @Bromeon @EmbersArc diff --git a/crates/egui_plot/Cargo.toml b/crates/egui_plot/Cargo.toml index f364e4f3242..a4781aab94f 100644 --- a/crates/egui_plot/Cargo.toml +++ b/crates/egui_plot/Cargo.toml @@ -1,7 +1,11 @@ [package] name = "egui_plot" version.workspace = true -authors = ["Emil Ernerfeldt "] +authors = [ + "Emil Ernerfeldt ", # https://github.com/emilk + "Jan Haller ", # https://github.com/Bromeon + "Sven Niederberger ", # https://github.com/EmbersArc +] description = "Immediate mode plotting for the egui GUI library" edition.workspace = true rust-version.workspace = true From 0561fcaba957d0e7f3966db265892a9dab4b49f8 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 22 Dec 2023 15:09:10 +0100 Subject: [PATCH 23/45] Replace a special `Color32::PLACEHOLDER` with widget fallback color (#3727) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces a special `Color32::PLACEHOLDER` which, during text painting, will be replaced with `TextShape::fallback_color`. The fallback color is mandatory to set in all text painting. Usually this comes from the current visual style. This lets users color only parts of a `WidgetText` (using e.g. a `LayoutJob` or a `Galley`), where the uncolored parts (using `Color32::PLACEHOLDER`) will be replaced by a default widget color (e.g. blue for a hyperlink). For instance, you can color the `⚠️`-emoji red in a piece of text red and leave the rest of the text uncolored. The color of the rest of the text will then depend on wether or not you put that text in a label, a button, or a hyperlink. Overall this simplifies a lot of complexity in the code but comes with a few breaking changes: * `TextShape::new`, `Shape::galley`, and `Painter::galley` now take a fallback color by argument * `Shape::galley_with_color` has been deprecated (use `Shape::galley` instead) * `Painter::galley_with_color` has been deprecated (use `Painter::galley` instead) * `WidgetTextGalley` is gone (use `Arc` instead) * `WidgetTextJob` is gone (use `LayoutJob` instead) * `RichText::into_text_job` has been replaced with `RichText::into_layout_job` * `WidgetText::into_text_job` has been replaced with `WidgetText::into_layout_job` --- crates/ecolor/src/color32.rs | 13 +- .../egui/src/containers/collapsing_header.rs | 12 +- crates/egui/src/containers/combo_box.rs | 3 +- crates/egui/src/containers/window.rs | 12 +- crates/egui/src/menu.rs | 4 +- crates/egui/src/painter.rs | 59 ++++--- crates/egui/src/ui.rs | 5 +- crates/egui/src/widget_text.rs | 150 ++++-------------- crates/egui/src/widgets/button.rs | 63 ++++---- crates/egui/src/widgets/hyperlink.rs | 13 +- crates/egui/src/widgets/label.rs | 90 +++++------ crates/egui/src/widgets/progress_bar.rs | 8 +- crates/egui/src/widgets/selected_label.rs | 10 +- crates/egui/src/widgets/text_edit/builder.rs | 9 +- crates/egui_demo_app/src/apps/http_app.rs | 6 +- crates/egui_demo_lib/benches/benchmark.rs | 10 +- crates/egui_plot/src/axis.rs | 16 +- crates/egui_plot/src/items/mod.rs | 6 +- crates/egui_plot/src/legend.rs | 2 +- crates/epaint/src/shape.rs | 64 ++++++-- crates/epaint/src/tessellator.rs | 6 + crates/epaint/src/text/fonts.rs | 7 +- crates/epaint/src/text/text_layout_types.rs | 5 +- 23 files changed, 267 insertions(+), 306 deletions(-) diff --git a/crates/ecolor/src/color32.rs b/crates/ecolor/src/color32.rs index 56b96589256..32e1487bc7f 100644 --- a/crates/ecolor/src/color32.rs +++ b/crates/ecolor/src/color32.rs @@ -7,6 +7,8 @@ use crate::{gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_f32_from_ /// /// Internally this uses 0-255 gamma space `sRGBA` color with premultiplied alpha. /// Alpha channel is in linear space. +/// +/// The special value of alpha=0 means the color is to be treated as an additive color. #[repr(C)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -61,7 +63,16 @@ impl Color32 { pub const DEBUG_COLOR: Color32 = Color32::from_rgba_premultiplied(0, 200, 0, 128); /// An ugly color that is planned to be replaced before making it to the screen. - pub const TEMPORARY_COLOR: Color32 = Color32::from_rgb(64, 254, 0); + /// + /// This is an invalid color, in that it does not correspond to a valid multiplied color, + /// nor to an additive color. + /// + /// This is used as a special color key, + /// i.e. often taken to mean "no color". + pub const PLACEHOLDER: Color32 = Color32::from_rgba_premultiplied(64, 254, 0, 128); + + #[deprecated = "Renmaed to PLACEHOLDER"] + pub const TEMPORARY_COLOR: Color32 = Self::PLACEHOLDER; #[inline] pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self { diff --git a/crates/egui/src/containers/collapsing_header.rs b/crates/egui/src/containers/collapsing_header.rs index 96bf5cdef7e..f4738fc0e0d 100644 --- a/crates/egui/src/containers/collapsing_header.rs +++ b/crates/egui/src/containers/collapsing_header.rs @@ -495,22 +495,22 @@ impl CollapsingHeader { let text_pos = available.min + vec2(ui.spacing().indent, 0.0); let wrap_width = available.right() - text_pos.x; let wrap = Some(false); - let text = text.into_galley(ui, wrap, wrap_width, TextStyle::Button); - let text_max_x = text_pos.x + text.size().x; + let galley = text.into_galley(ui, wrap, wrap_width, TextStyle::Button); + let text_max_x = text_pos.x + galley.size().x; let mut desired_width = text_max_x + button_padding.x - available.left(); if ui.visuals().collapsing_header_frame { desired_width = desired_width.max(available.width()); // fill full width } - let mut desired_size = vec2(desired_width, text.size().y + 2.0 * button_padding.y); + let mut desired_size = vec2(desired_width, galley.size().y + 2.0 * button_padding.y); desired_size = desired_size.at_least(ui.spacing().interact_size); let (_, rect) = ui.allocate_space(desired_size); let mut header_response = ui.interact(rect, id, Sense::click()); let text_pos = pos2( text_pos.x, - header_response.rect.center().y - text.size().y / 2.0, + header_response.rect.center().y - galley.size().y / 2.0, ); let mut state = CollapsingState::load_with_default_open(ui.ctx(), id, default_open); @@ -525,7 +525,7 @@ impl CollapsingHeader { } header_response - .widget_info(|| WidgetInfo::labeled(WidgetType::CollapsingHeader, text.text())); + .widget_info(|| WidgetInfo::labeled(WidgetType::CollapsingHeader, galley.text())); let openness = state.openness(ui.ctx()); @@ -563,7 +563,7 @@ impl CollapsingHeader { } } - text.paint_with_visuals(ui.painter(), text_pos, &visuals); + ui.painter().galley(text_pos, galley, visuals.text_color()); } Prepared { diff --git a/crates/egui/src/containers/combo_box.rs b/crates/egui/src/containers/combo_box.rs index 4bc3edc4b57..70ed046b396 100644 --- a/crates/egui/src/containers/combo_box.rs +++ b/crates/egui/src/containers/combo_box.rs @@ -327,7 +327,8 @@ fn combo_box_dyn<'c, R>( } let text_rect = Align2::LEFT_CENTER.align_size_within_rect(galley.size(), rect); - galley.paint_with_visuals(ui.painter(), text_rect.min, visuals); + ui.painter() + .galley(text_rect.min, galley, visuals.text_color()); } }); diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index cc0f28e90e5..4182af4e8c3 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -1,7 +1,9 @@ // WARNING: the code in here is horrible. It is a behemoth that needs breaking up into simpler parts. +use std::sync::Arc; + use crate::collapsing_header::CollapsingState; -use crate::{widget_text::WidgetTextGalley, *}; +use crate::*; use epaint::*; use super::*; @@ -885,7 +887,7 @@ struct TitleBar { id: Id, /// Prepared text in the title - title_galley: WidgetTextGalley, + title_galley: Arc, /// Size of the title bar in a collapsed state (if window is collapsible), /// which includes all necessary space for showing the expand button, the @@ -984,11 +986,11 @@ impl TitleBar { let full_top_rect = Rect::from_x_y_ranges(self.rect.x_range(), self.min_rect.y_range()); let text_pos = emath::align::center_size_in_rect(self.title_galley.size(), full_top_rect).left_top(); - let text_pos = text_pos - self.title_galley.galley().rect.min.to_vec2(); + let text_pos = text_pos - self.title_galley.rect.min.to_vec2(); let text_pos = text_pos - 1.5 * Vec2::Y; // HACK: center on x-height of text (looks better) - self.title_galley.paint_with_fallback_color( - ui.painter(), + ui.painter().galley( text_pos, + self.title_galley.clone(), ui.visuals().text_color(), ); diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index 34b33a31fb6..bd7305ed80e 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -503,8 +503,8 @@ impl SubMenuButton { } let text_color = visuals.text_color(); - text_galley.paint_with_fallback_color(ui.painter(), text_pos, text_color); - icon_galley.paint_with_fallback_color(ui.painter(), icon_pos, text_color); + ui.painter().galley(text_pos, text_galley, text_color); + ui.painter().galley(icon_pos, icon_galley, text_color); } response } diff --git a/crates/egui/src/painter.rs b/crates/egui/src/painter.rs index 8376d16ed7b..dfae0a92cfe 100644 --- a/crates/egui/src/painter.rs +++ b/crates/egui/src/painter.rs @@ -88,7 +88,7 @@ impl Painter { /// ## Accessors etc impl Painter { /// Get a reference to the parent [`Context`]. - #[inline(always)] + #[inline] pub fn ctx(&self) -> &Context { &self.ctx } @@ -96,45 +96,45 @@ impl Painter { /// Read-only access to the shared [`Fonts`]. /// /// See [`Context`] documentation for how locks work. - #[inline(always)] + #[inline] pub fn fonts(&self, reader: impl FnOnce(&Fonts) -> R) -> R { self.ctx.fonts(reader) } /// Where we paint - #[inline(always)] + #[inline] pub fn layer_id(&self) -> LayerId { self.layer_id } /// Everything painted in this [`Painter`] will be clipped against this. /// This means nothing outside of this rectangle will be visible on screen. - #[inline(always)] + #[inline] pub fn clip_rect(&self) -> Rect { self.clip_rect } /// Everything painted in this [`Painter`] will be clipped against this. /// This means nothing outside of this rectangle will be visible on screen. - #[inline(always)] + #[inline] pub fn set_clip_rect(&mut self, clip_rect: Rect) { self.clip_rect = clip_rect; } /// Useful for pixel-perfect rendering. - #[inline(always)] + #[inline] pub fn round_to_pixel(&self, point: f32) -> f32 { self.ctx().round_to_pixel(point) } /// Useful for pixel-perfect rendering. - #[inline(always)] + #[inline] pub fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 { self.ctx().round_vec_to_pixels(vec) } /// Useful for pixel-perfect rendering. - #[inline(always)] + #[inline] pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 { self.ctx().round_pos_to_pixels(pos) } @@ -236,7 +236,7 @@ impl Painter { 0.0, Color32::from_black_alpha(150), )); - self.galley(rect.min, galley); + self.galley(rect.min, galley, color); frame_rect } } @@ -379,14 +379,15 @@ impl Painter { ) -> Rect { let galley = self.layout_no_wrap(text.to_string(), font_id, text_color); let rect = anchor.anchor_rect(Rect::from_min_size(pos, galley.size())); - self.galley(rect.min, galley); + self.galley(rect.min, galley, text_color); rect } /// Will wrap text at the given width and line break at `\n`. /// /// Paint the results with [`Self::galley`]. - #[inline(always)] + #[inline] + #[must_use] pub fn layout( &self, text: String, @@ -400,7 +401,8 @@ impl Painter { /// Will line break at `\n`. /// /// Paint the results with [`Self::galley`]. - #[inline(always)] + #[inline] + #[must_use] pub fn layout_no_wrap( &self, text: String, @@ -414,11 +416,13 @@ impl Painter { /// /// You can create the [`Galley`] with [`Self::layout`]. /// - /// If you want to change the color of the text, use [`Self::galley_with_color`]. - #[inline(always)] - pub fn galley(&self, pos: Pos2, galley: Arc) { + /// Any uncolored parts of the [`Galley`] (using [`Color32::PLACEHOLDER`]) will be replaced with the given color. + /// + /// Any non-placeholder color in the galley takes precedence over this fallback color. + #[inline] + pub fn galley(&self, pos: Pos2, galley: Arc, fallback_color: Color32) { if !galley.is_empty() { - self.add(Shape::galley(pos, galley)); + self.add(Shape::galley(pos, galley, fallback_color)); } } @@ -426,11 +430,28 @@ impl Painter { /// /// You can create the [`Galley`] with [`Self::layout`]. /// - /// The text color in the [`Galley`] will be replaced with the given color. - #[inline(always)] + /// All text color in the [`Galley`] will be replaced with the given color. + #[inline] + pub fn galley_with_override_text_color( + &self, + pos: Pos2, + galley: Arc, + text_color: Color32, + ) { + if !galley.is_empty() { + self.add(Shape::galley_with_override_text_color( + pos, galley, text_color, + )); + } + } + + #[deprecated = "Use `Painter::galley` or `Painter::galley_with_override_text_color` instead"] + #[inline] pub fn galley_with_color(&self, pos: Pos2, galley: Arc, text_color: Color32) { if !galley.is_empty() { - self.add(Shape::galley_with_color(pos, galley, text_color)); + self.add(Shape::galley_with_override_text_color( + pos, galley, text_color, + )); } } } diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 8474bcb0e72..4c4c0501f3a 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2269,7 +2269,8 @@ fn register_rect(ui: &Ui, rect: Rect) { if !callstack.is_empty() { let font_id = FontId::monospace(12.0); let text = format!("{callstack}\n\n(click to copy)"); - let galley = painter.layout_no_wrap(text, font_id, Color32::WHITE); + let text_color = Color32::WHITE; + let galley = painter.layout_no_wrap(text, font_id, text_color); // Position the text either under or above: let screen_rect = ui.ctx().screen_rect(); @@ -2299,7 +2300,7 @@ fn register_rect(ui: &Ui, rect: Rect) { }; let text_rect = Rect::from_min_size(text_pos, galley.size()); painter.rect(text_rect, 0.0, text_bg_color, (1.0, text_rect_stroke_color)); - painter.galley(text_pos, galley); + painter.galley(text_pos, galley, text_color); if ui.input(|i| i.pointer.any_click()) { ui.ctx().copy_text(callstack); diff --git a/crates/egui/src/widget_text.rs b/crates/egui/src/widget_text.rs index fe0de370c01..9bad481cb38 100644 --- a/crates/egui/src/widget_text.rs +++ b/crates/egui/src/widget_text.rs @@ -1,8 +1,8 @@ use std::{borrow::Cow, sync::Arc}; use crate::{ - style::WidgetVisuals, text::LayoutJob, Align, Color32, FontFamily, FontSelection, Galley, Pos2, - Style, TextStyle, Ui, Visuals, + text::LayoutJob, Align, Color32, FontFamily, FontSelection, Galley, Style, TextStyle, Ui, + Visuals, }; /// Text and optional style choices for it. @@ -247,6 +247,9 @@ impl RichText { } /// Override text color. + /// + /// If not set, [`Color32::PLACEHOLDER`] will be used, + /// which will be replaced with a color chosen by the widget that paints the text. #[inline] pub fn color(mut self, color: impl Into) -> Self { self.text_color = Some(color.into()); @@ -310,17 +313,14 @@ impl RichText { layout_job.append(&text, 0.0, format); } - fn into_text_job( + fn into_layout_job( self, style: &Style, fallback_font: FontSelection, default_valign: Align, - ) -> WidgetTextJob { - let job_has_color = self.get_text_color(&style.visuals).is_some(); + ) -> LayoutJob { let (text, text_format) = self.into_text_and_format(style, fallback_font, default_valign); - - let job = LayoutJob::single_section(text, text_format); - WidgetTextJob { job, job_has_color } + LayoutJob::single_section(text, text_format) } fn into_text_and_format( @@ -350,7 +350,7 @@ impl RichText { } = self; let line_color = text_color.unwrap_or_else(|| style.visuals.text_color()); - let text_color = text_color.unwrap_or(crate::Color32::TEMPORARY_COLOR); + let text_color = text_color.unwrap_or(crate::Color32::PLACEHOLDER); let font_id = { let mut font_id = text_style @@ -429,6 +429,9 @@ impl RichText { /// but it can be a [`RichText`] (text with color, style, etc), /// a [`LayoutJob`] (for when you want full control of how the text looks) /// or text that has already been laid out in a [`Galley`]. +/// +/// You can color the text however you want, or use [`Color32::PLACEHOLDER`] +/// which will be replaced with a color chosen by the widget that paints the text. #[derive(Clone)] pub enum WidgetText { RichText(RichText), @@ -442,9 +445,15 @@ pub enum WidgetText { /// of the [`Ui`] the widget is placed in. /// If you want all parts of the [`LayoutJob`] respected, then convert it to a /// [`Galley`] and use [`Self::Galley`] instead. + /// + /// You can color the text however you want, or use [`Color32::PLACEHOLDER`] + /// which will be replaced with a color chosen by the widget that paints the text. LayoutJob(LayoutJob), /// Use exactly this galley when painting the text. + /// + /// You can color the text however you want, or use [`Color32::PLACEHOLDER`] + /// which will be replaced with a color chosen by the widget that paints the text. Galley(Arc), } @@ -616,25 +625,16 @@ impl WidgetText { } } - pub fn into_text_job( + pub fn into_layout_job( self, style: &Style, fallback_font: FontSelection, default_valign: Align, - ) -> WidgetTextJob { + ) -> LayoutJob { match self { - Self::RichText(text) => text.into_text_job(style, fallback_font, default_valign), - Self::LayoutJob(job) => WidgetTextJob { - job, - job_has_color: true, - }, - Self::Galley(galley) => { - let job: LayoutJob = (*galley.job).clone(); - WidgetTextJob { - job, - job_has_color: true, - } - } + Self::RichText(text) => text.into_layout_job(style, fallback_font, default_valign), + Self::LayoutJob(job) => job, + Self::Galley(galley) => (*galley.job).clone(), } } @@ -647,31 +647,22 @@ impl WidgetText { wrap: Option, available_width: f32, fallback_font: impl Into, - ) -> WidgetTextGalley { + ) -> Arc { let wrap = wrap.unwrap_or_else(|| ui.wrap_text()); let wrap_width = if wrap { available_width } else { f32::INFINITY }; match self { Self::RichText(text) => { let valign = ui.layout().vertical_align(); - let mut text_job = text.into_text_job(ui.style(), fallback_font.into(), valign); - text_job.job.wrap.max_width = wrap_width; - WidgetTextGalley { - galley: ui.fonts(|f| f.layout_job(text_job.job)), - galley_has_color: text_job.job_has_color, - } + let mut layout_job = text.into_layout_job(ui.style(), fallback_font.into(), valign); + layout_job.wrap.max_width = wrap_width; + ui.fonts(|f| f.layout_job(layout_job)) } Self::LayoutJob(mut job) => { job.wrap.max_width = wrap_width; - WidgetTextGalley { - galley: ui.fonts(|f| f.layout_job(job)), - galley_has_color: true, - } + ui.fonts(|f| f.layout_job(job)) } - Self::Galley(galley) => WidgetTextGalley { - galley, - galley_has_color: true, - }, + Self::Galley(galley) => galley, } } } @@ -724,86 +715,3 @@ impl From> for WidgetText { Self::Galley(galley) } } - -// ---------------------------------------------------------------------------- - -#[derive(Clone, PartialEq)] -pub struct WidgetTextJob { - pub job: LayoutJob, - pub job_has_color: bool, -} - -impl WidgetTextJob { - pub fn into_galley(self, fonts: &crate::text::Fonts) -> WidgetTextGalley { - let Self { job, job_has_color } = self; - let galley = fonts.layout_job(job); - WidgetTextGalley { - galley, - galley_has_color: job_has_color, - } - } -} - -// ---------------------------------------------------------------------------- - -/// Text that has been laid out and ready to be painted. -#[derive(Clone, PartialEq)] -pub struct WidgetTextGalley { - pub galley: Arc, - pub galley_has_color: bool, -} - -impl WidgetTextGalley { - /// Size of the laid out text. - #[inline] - pub fn size(&self) -> crate::Vec2 { - self.galley.size() - } - - /// The full, non-elided text of the input job. - #[inline] - pub fn text(&self) -> &str { - self.galley.text() - } - - #[inline] - pub fn galley(&self) -> &Arc { - &self.galley - } - - /// Use the colors in the original [`WidgetText`] if any, - /// else fall back to the one specified by the [`WidgetVisuals`]. - pub fn paint_with_visuals( - self, - painter: &crate::Painter, - text_pos: Pos2, - visuals: &WidgetVisuals, - ) { - self.paint_with_fallback_color(painter, text_pos, visuals.text_color()); - } - - /// Use the colors in the original [`WidgetText`] if any, - /// else fall back to the given color. - pub fn paint_with_fallback_color( - self, - painter: &crate::Painter, - text_pos: Pos2, - text_color: Color32, - ) { - if self.galley_has_color { - painter.galley(text_pos, self.galley); - } else { - painter.galley_with_color(text_pos, self.galley, text_color); - } - } - - /// Paint with this specific color. - pub fn paint_with_color_override( - self, - painter: &crate::Painter, - text_pos: Pos2, - text_color: Color32, - ) { - painter.galley_with_color(text_pos, self.galley, text_color); - } -} diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index 236d68e05c1..6ca7cf86e40 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -210,8 +210,9 @@ impl Widget for Button<'_> { text_wrap_width -= 60.0; // Some space for the shortcut text (which we never wrap). } - let text = text.map(|text| text.into_galley(ui, wrap, text_wrap_width, TextStyle::Button)); - let shortcut_text = (!shortcut_text.is_empty()) + let galley = + text.map(|text| text.into_galley(ui, wrap, text_wrap_width, TextStyle::Button)); + let shortcut_galley = (!shortcut_text.is_empty()) .then(|| shortcut_text.into_galley(ui, Some(false), f32::INFINITY, TextStyle::Button)); let mut desired_size = Vec2::ZERO; @@ -219,14 +220,14 @@ impl Widget for Button<'_> { desired_size.x += image_size.x; desired_size.y = desired_size.y.max(image_size.y); } - if image.is_some() && text.is_some() { + if image.is_some() && galley.is_some() { desired_size.x += ui.spacing().icon_spacing; } - if let Some(text) = &text { + if let Some(text) = &galley { desired_size.x += text.size().x; desired_size.y = desired_size.y.max(text.size().y); } - if let Some(shortcut_text) = &shortcut_text { + if let Some(shortcut_text) = &shortcut_galley { desired_size.x += ui.spacing().item_spacing.x + shortcut_text.size().x; desired_size.y = desired_size.y.max(shortcut_text.size().y); } @@ -238,8 +239,8 @@ impl Widget for Button<'_> { let (rect, mut response) = ui.allocate_at_least(desired_size, sense); response.widget_info(|| { - if let Some(text) = &text { - WidgetInfo::labeled(WidgetType::Button, text.text()) + if let Some(galley) = &galley { + WidgetInfo::labeled(WidgetType::Button, galley.text()) } else { WidgetInfo::new(WidgetType::Button) } @@ -297,30 +298,30 @@ impl Widget for Button<'_> { widgets::image::texture_load_result_response(image.source(), &tlr, response); } - if image.is_some() && text.is_some() { + if image.is_some() && galley.is_some() { cursor_x += ui.spacing().icon_spacing; } - if let Some(text) = text { - let text_pos = if image.is_some() || shortcut_text.is_some() { - pos2(cursor_x, rect.center().y - 0.5 * text.size().y) + if let Some(galley) = galley { + let text_pos = if image.is_some() || shortcut_galley.is_some() { + pos2(cursor_x, rect.center().y - 0.5 * galley.size().y) } else { // Make sure button text is centered if within a centered layout ui.layout() - .align_size_within_rect(text.size(), rect.shrink2(button_padding)) + .align_size_within_rect(galley.size(), rect.shrink2(button_padding)) .min }; - text.paint_with_visuals(ui.painter(), text_pos, visuals); + ui.painter().galley(text_pos, galley, visuals.text_color()); } - if let Some(shortcut_text) = shortcut_text { + if let Some(shortcut_galley) = shortcut_galley { let shortcut_text_pos = pos2( - rect.max.x - button_padding.x - shortcut_text.size().x, - rect.center().y - 0.5 * shortcut_text.size().y, + rect.max.x - button_padding.x - shortcut_galley.size().x, + rect.center().y - 0.5 * shortcut_galley.size().y, ); - shortcut_text.paint_with_fallback_color( - ui.painter(), + ui.painter().galley( shortcut_text_pos, + shortcut_galley, ui.visuals().weak_text_color(), ); } @@ -378,18 +379,18 @@ impl<'a> Widget for Checkbox<'a> { let icon_width = spacing.icon_width; let icon_spacing = spacing.icon_spacing; - let (text, mut desired_size) = if text.is_empty() { + let (galley, mut desired_size) = if text.is_empty() { (None, vec2(icon_width, 0.0)) } else { let total_extra = vec2(icon_width + icon_spacing, 0.0); let wrap_width = ui.available_width() - total_extra.x; - let text = text.into_galley(ui, None, wrap_width, TextStyle::Button); + let galley = text.into_galley(ui, None, wrap_width, TextStyle::Button); - let mut desired_size = total_extra + text.size(); + let mut desired_size = total_extra + galley.size(); desired_size = desired_size.at_least(spacing.interact_size); - (Some(text), desired_size) + (Some(galley), desired_size) }; desired_size = desired_size.at_least(Vec2::splat(spacing.interact_size.y)); @@ -404,7 +405,7 @@ impl<'a> Widget for Checkbox<'a> { WidgetInfo::selected( WidgetType::Checkbox, *checked, - text.as_ref().map_or("", |x| x.text()), + galley.as_ref().map_or("", |x| x.text()), ) }); @@ -430,12 +431,12 @@ impl<'a> Widget for Checkbox<'a> { visuals.fg_stroke, )); } - if let Some(text) = text { + if let Some(galley) = galley { let text_pos = pos2( rect.min.x + icon_width + icon_spacing, - rect.center().y - 0.5 * text.size().y, + rect.center().y - 0.5 * galley.size().y, ); - text.paint_with_visuals(ui.painter(), text_pos, visuals); + ui.painter().galley(text_pos, galley, visuals.text_color()); } } @@ -487,7 +488,7 @@ impl Widget for RadioButton { let icon_width = spacing.icon_width; let icon_spacing = spacing.icon_spacing; - let (text, mut desired_size) = if text.is_empty() { + let (galley, mut desired_size) = if text.is_empty() { (None, vec2(icon_width, 0.0)) } else { let total_extra = vec2(icon_width + icon_spacing, 0.0); @@ -509,7 +510,7 @@ impl Widget for RadioButton { WidgetInfo::selected( WidgetType::RadioButton, checked, - text.as_ref().map_or("", |x| x.text()), + galley.as_ref().map_or("", |x| x.text()), ) }); @@ -538,12 +539,12 @@ impl Widget for RadioButton { }); } - if let Some(text) = text { + if let Some(galley) = galley { let text_pos = pos2( rect.min.x + icon_width + icon_spacing, - rect.center().y - 0.5 * text.size().y, + rect.center().y - 0.5 * galley.size().y, ); - text.paint_with_visuals(ui.painter(), text_pos, visuals); + ui.painter().galley(text_pos, galley, visuals.text_color()); } } diff --git a/crates/egui/src/widgets/hyperlink.rs b/crates/egui/src/widgets/hyperlink.rs index 5d592272641..79a833bd77e 100644 --- a/crates/egui/src/widgets/hyperlink.rs +++ b/crates/egui/src/widgets/hyperlink.rs @@ -34,8 +34,8 @@ impl Widget for Link { let Link { text } = self; let label = Label::new(text).sense(Sense::click()); - let (pos, text_galley, response) = label.layout_in_ui(ui); - response.widget_info(|| WidgetInfo::labeled(WidgetType::Link, text_galley.text())); + let (pos, galley, response) = label.layout_in_ui(ui); + response.widget_info(|| WidgetInfo::labeled(WidgetType::Link, galley.text())); if response.hovered() { ui.ctx().set_cursor_icon(CursorIcon::PointingHand); @@ -51,13 +51,8 @@ impl Widget for Link { Stroke::NONE }; - ui.painter().add(epaint::TextShape { - pos, - galley: text_galley.galley, - override_text_color: Some(color), - underline, - angle: 0.0, - }); + ui.painter() + .add(epaint::TextShape::new(pos, galley, color).with_underline(underline)); } response diff --git a/crates/egui/src/widgets/label.rs b/crates/egui/src/widgets/label.rs index eb786e75013..dee9b1c303f 100644 --- a/crates/egui/src/widgets/label.rs +++ b/crates/egui/src/widgets/label.rs @@ -1,4 +1,6 @@ -use crate::{widget_text::WidgetTextGalley, *}; +use std::sync::Arc; + +use crate::*; /// Static text. /// @@ -94,7 +96,7 @@ impl Label { impl Label { /// Do layout and position the galley in the ui, without painting it or adding widget info. - pub fn layout_in_ui(self, ui: &mut Ui) -> (Pos2, WidgetTextGalley, Response) { + pub fn layout_in_ui(self, ui: &mut Ui) -> (Pos2, Arc, Response) { let sense = self.sense.unwrap_or_else(|| { // We only want to focus labels if the screen reader is on. if ui.memory(|mem| mem.options.screen_reader) { @@ -111,17 +113,13 @@ impl Label { Align::Center => rect.center_top(), Align::RIGHT => rect.right_top(), }; - let text_galley = WidgetTextGalley { - galley, - galley_has_color: true, - }; - return (pos, text_galley, response); + return (pos, galley, response); } let valign = ui.layout().vertical_align(); - let mut text_job = self + let mut layout_job = self .text - .into_text_job(ui.style(), FontSelection::Default, valign); + .into_layout_job(ui.style(), FontSelection::Default, valign); let truncate = self.truncate; let wrap = !truncate && self.wrap.unwrap_or_else(|| ui.wrap_text()); @@ -139,70 +137,65 @@ impl Label { let first_row_indentation = available_width - ui.available_size_before_wrap().x; egui_assert!(first_row_indentation.is_finite()); - text_job.job.wrap.max_width = available_width; - text_job.job.first_row_min_height = cursor.height(); - text_job.job.halign = Align::Min; - text_job.job.justify = false; - if let Some(first_section) = text_job.job.sections.first_mut() { + layout_job.wrap.max_width = available_width; + layout_job.first_row_min_height = cursor.height(); + layout_job.halign = Align::Min; + layout_job.justify = false; + if let Some(first_section) = layout_job.sections.first_mut() { first_section.leading_space = first_row_indentation; } - let text_galley = ui.fonts(|f| text_job.into_galley(f)); + let galley = ui.fonts(|fonts| fonts.layout_job(layout_job)); let pos = pos2(ui.max_rect().left(), ui.cursor().top()); - assert!( - !text_galley.galley.rows.is_empty(), - "Galleys are never empty" - ); + assert!(!galley.rows.is_empty(), "Galleys are never empty"); // collect a response from many rows: - let rect = text_galley.galley.rows[0] - .rect - .translate(vec2(pos.x, pos.y)); + let rect = galley.rows[0].rect.translate(vec2(pos.x, pos.y)); let mut response = ui.allocate_rect(rect, sense); - for row in text_galley.galley.rows.iter().skip(1) { + for row in galley.rows.iter().skip(1) { let rect = row.rect.translate(vec2(pos.x, pos.y)); response |= ui.allocate_rect(rect, sense); } - (pos, text_galley, response) + (pos, galley, response) } else { if truncate { - text_job.job.wrap.max_width = available_width; - text_job.job.wrap.max_rows = 1; - text_job.job.wrap.break_anywhere = true; + layout_job.wrap.max_width = available_width; + layout_job.wrap.max_rows = 1; + layout_job.wrap.break_anywhere = true; } else if wrap { - text_job.job.wrap.max_width = available_width; + layout_job.wrap.max_width = available_width; } else { - text_job.job.wrap.max_width = f32::INFINITY; + layout_job.wrap.max_width = f32::INFINITY; }; if ui.is_grid() { // TODO(emilk): remove special Grid hacks like these - text_job.job.halign = Align::LEFT; - text_job.job.justify = false; + layout_job.halign = Align::LEFT; + layout_job.justify = false; } else { - text_job.job.halign = ui.layout().horizontal_placement(); - text_job.job.justify = ui.layout().horizontal_justify(); + layout_job.halign = ui.layout().horizontal_placement(); + layout_job.justify = ui.layout().horizontal_justify(); }; - let text_galley = ui.fonts(|f| text_job.into_galley(f)); - let (rect, response) = ui.allocate_exact_size(text_galley.size(), sense); - let pos = match text_galley.galley.job.halign { + let galley = ui.fonts(|fonts| fonts.layout_job(layout_job)); + let (rect, response) = ui.allocate_exact_size(galley.size(), sense); + let pos = match galley.job.halign { Align::LEFT => rect.left_top(), Align::Center => rect.center_top(), Align::RIGHT => rect.right_top(), }; - (pos, text_galley, response) + (pos, galley, response) } } } impl Widget for Label { fn ui(self, ui: &mut Ui) -> Response { - let (pos, text_galley, mut response) = self.layout_in_ui(ui); - response.widget_info(|| WidgetInfo::labeled(WidgetType::Label, text_galley.text())); + let (pos, galley, mut response) = self.layout_in_ui(ui); + response.widget_info(|| WidgetInfo::labeled(WidgetType::Label, galley.text())); - if text_galley.galley.elided { + if galley.elided { // Show the full (non-elided) text on hover: - response = response.on_hover_text(text_galley.text()); + response = response.on_hover_text(galley.text()); } if ui.is_rect_visible(response.rect) { @@ -214,19 +207,8 @@ impl Widget for Label { Stroke::NONE }; - let override_text_color = if text_galley.galley_has_color { - None - } else { - Some(response_color) - }; - - ui.painter().add(epaint::TextShape { - pos, - galley: text_galley.galley, - override_text_color, - underline, - angle: 0.0, - }); + ui.painter() + .add(epaint::TextShape::new(pos, galley, response_color).with_underline(underline)); } response diff --git a/crates/egui/src/widgets/progress_bar.rs b/crates/egui/src/widgets/progress_bar.rs index ad3c298399a..c8439802544 100644 --- a/crates/egui/src/widgets/progress_bar.rs +++ b/crates/egui/src/widgets/progress_bar.rs @@ -161,11 +161,9 @@ impl Widget for ProgressBar { let text_color = visuals .override_text_color .unwrap_or(visuals.selection.stroke.color); - galley.paint_with_fallback_color( - &ui.painter().with_clip_rect(outer_rect), - text_pos, - text_color, - ); + ui.painter() + .with_clip_rect(outer_rect) + .galley(text_pos, galley, text_color); } } diff --git a/crates/egui/src/widgets/selected_label.rs b/crates/egui/src/widgets/selected_label.rs index 364ba0391e6..105232b40a5 100644 --- a/crates/egui/src/widgets/selected_label.rs +++ b/crates/egui/src/widgets/selected_label.rs @@ -44,19 +44,19 @@ impl Widget for SelectableLabel { let total_extra = button_padding + button_padding; let wrap_width = ui.available_width() - total_extra.x; - let text = text.into_galley(ui, None, wrap_width, TextStyle::Button); + let galley = text.into_galley(ui, None, wrap_width, TextStyle::Button); - let mut desired_size = total_extra + text.size(); + let mut desired_size = total_extra + galley.size(); desired_size.y = desired_size.y.at_least(ui.spacing().interact_size.y); let (rect, response) = ui.allocate_at_least(desired_size, Sense::click()); response.widget_info(|| { - WidgetInfo::selected(WidgetType::SelectableLabel, selected, text.text()) + WidgetInfo::selected(WidgetType::SelectableLabel, selected, galley.text()) }); if ui.is_rect_visible(response.rect) { let text_pos = ui .layout() - .align_size_within_rect(text.size(), rect.shrink2(button_padding)) + .align_size_within_rect(galley.size(), rect.shrink2(button_padding)) .min; let visuals = ui.style().interact_selectable(&response, selected); @@ -72,7 +72,7 @@ impl Widget for SelectableLabel { ); } - text.paint_with_visuals(ui.painter(), text_pos, &visuals); + ui.painter().galley(text_pos, galley, visuals.text_color()); } response diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index adf53149146..8b80151975f 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -164,13 +164,14 @@ impl<'t> TextEdit<'t> { /// .desired_width(f32::INFINITY); /// let output = text_edit.show(ui); /// let painter = ui.painter_at(output.response.rect); + /// let text_color = Color32::from_rgba_premultiplied(100, 100, 100, 100); /// let galley = painter.layout( /// String::from("Enter text"), /// FontId::default(), - /// Color32::from_rgba_premultiplied(100, 100, 100, 100), + /// text_color, /// f32::INFINITY /// ); - /// painter.galley(output.text_draw_pos, galley); + /// painter.galley(output.text_draw_pos, galley, text_color); /// # }); /// ``` #[inline] @@ -664,7 +665,7 @@ impl<'t> TextEdit<'t> { }; if ui.is_rect_visible(rect) { - painter.galley(text_draw_pos, galley.clone()); + painter.galley(text_draw_pos, galley.clone(), text_color); if text.as_str().is_empty() && !hint_text.is_empty() { let hint_text_color = ui.visuals().weak_text_color(); @@ -673,7 +674,7 @@ impl<'t> TextEdit<'t> { } else { hint_text.into_galley(ui, Some(false), f32::INFINITY, font_id) }; - galley.paint_with_fallback_color(&painter, response.rect.min, hint_text_color); + painter.galley(response.rect.min, galley, hint_text_color); } if ui.memory(|mem| mem.has_focus(id)) { diff --git a/crates/egui_demo_app/src/apps/http_app.rs b/crates/egui_demo_app/src/apps/http_app.rs index a118a90fcb6..7e8d7e54c48 100644 --- a/crates/egui_demo_app/src/apps/http_app.rs +++ b/crates/egui_demo_app/src/apps/http_app.rs @@ -260,7 +260,11 @@ impl ColoredText { job.wrap.max_width = ui.available_width(); let galley = ui.fonts(|f| f.layout_job(job)); let (response, painter) = ui.allocate_painter(galley.size(), egui::Sense::hover()); - painter.add(egui::Shape::galley(response.rect.min, galley)); + painter.add(egui::Shape::galley( + response.rect.min, + galley, + ui.visuals().text_color(), + )); } } } diff --git a/crates/egui_demo_lib/benches/benchmark.rs b/crates/egui_demo_lib/benches/benchmark.rs index 1baaefd7e01..75c68a0de32 100644 --- a/crates/egui_demo_lib/benches/benchmark.rs +++ b/crates/egui_demo_lib/benches/benchmark.rs @@ -89,7 +89,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { let max_texture_side = 8 * 1024; let wrap_width = 512.0; let font_id = egui::FontId::default(); - let color = egui::Color32::WHITE; + let text_color = egui::Color32::WHITE; let fonts = egui::epaint::text::Fonts::new( pixels_per_point, max_texture_side, @@ -104,7 +104,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { let job = LayoutJob::simple( LOREM_IPSUM_LONG.to_owned(), font_id.clone(), - color, + text_color, wrap_width, ); layout(&mut locked_fonts.fonts, job.into()) @@ -116,13 +116,13 @@ pub fn criterion_benchmark(c: &mut Criterion) { fonts.layout( LOREM_IPSUM_LONG.to_owned(), font_id.clone(), - color, + text_color, wrap_width, ) }); }); - let galley = fonts.layout(LOREM_IPSUM_LONG.to_owned(), font_id, color, wrap_width); + let galley = fonts.layout(LOREM_IPSUM_LONG.to_owned(), font_id, text_color, wrap_width); let font_image_size = fonts.font_image_size(); let prepared_discs = fonts.texture_atlas().lock().prepared_discs(); let mut tessellator = egui::epaint::Tessellator::new( @@ -132,7 +132,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { prepared_discs, ); let mut mesh = egui::epaint::Mesh::default(); - let text_shape = TextShape::new(egui::Pos2::ZERO, galley); + let text_shape = TextShape::new(egui::Pos2::ZERO, galley, text_color); c.bench_function("tessellate_text", |b| { b.iter(|| { tessellator.tessellate_text(&text_shape, &mut mesh); diff --git a/crates/egui_plot/src/axis.rs b/crates/egui_plot/src/axis.rs index 12dcac8828c..fae09e47ed3 100644 --- a/crates/egui_plot/src/axis.rs +++ b/crates/egui_plot/src/axis.rs @@ -1,7 +1,7 @@ use std::{fmt::Debug, ops::RangeInclusive, sync::Arc}; use egui::emath::{remap_clamp, round_to_decimals, Pos2, Rect}; -use egui::epaint::{Shape, Stroke, TextShape}; +use egui::epaint::{Shape, TextShape}; use crate::{Response, Sense, TextStyle, Ui, WidgetText}; @@ -247,14 +247,9 @@ impl AxisWidget { } }, }; - let shape = TextShape { - pos: text_pos, - galley: galley.galley, - underline: Stroke::NONE, - override_text_color: Some(text_color), - angle, - }; - ui.painter().add(shape); + + ui.painter() + .add(TextShape::new(text_pos, galley, text_color).with_angle(angle)); // --- add ticks --- let font_id = TextStyle::Body.resolve(ui.style()); @@ -311,7 +306,8 @@ impl AxisWidget { } }; - ui.painter().add(Shape::galley(text_pos, galley)); + ui.painter() + .add(Shape::galley(text_pos, galley, text_color)); } } } diff --git a/crates/egui_plot/src/items/mod.rs b/crates/egui_plot/src/items/mod.rs index 92d607570d8..c8949f58ce4 100644 --- a/crates/egui_plot/src/items/mod.rs +++ b/crates/egui_plot/src/items/mod.rs @@ -732,11 +732,7 @@ impl PlotItem for Text { .anchor .anchor_rect(Rect::from_min_size(pos, galley.size())); - let mut text_shape = epaint::TextShape::new(rect.min, galley.galley); - if !galley.galley_has_color { - text_shape.override_text_color = Some(color); - } - shapes.push(text_shape.into()); + shapes.push(epaint::TextShape::new(rect.min, galley, color).into()); if self.highlight { shapes.push(Shape::rect_stroke( diff --git a/crates/egui_plot/src/legend.rs b/crates/egui_plot/src/legend.rs index 7d2a3e00954..0a0f459f2f8 100644 --- a/crates/egui_plot/src/legend.rs +++ b/crates/egui_plot/src/legend.rs @@ -144,7 +144,7 @@ impl LegendEntry { }; let text_position = pos2(text_position_x, rect.center().y - 0.5 * galley.size().y); - painter.galley_with_color(text_position, galley, visuals.text_color()); + painter.galley(text_position, galley, visuals.text_color()); *checked ^= response.clicked_by(PointerButton::Primary); *hovered = response.hovered(); diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index 8de1f0ffc1b..cfae4493d0f 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -211,24 +211,36 @@ impl Shape { ) -> Self { let galley = fonts.layout_no_wrap(text.to_string(), font_id, color); let rect = anchor.anchor_rect(Rect::from_min_size(pos, galley.size())); - Self::galley(rect.min, galley) + Self::galley(rect.min, galley, color) } + /// Any uncolored parts of the [`Galley`] (using [`Color32::PLACEHOLDER`]) will be replaced with the given color. + /// + /// Any non-placeholder color in the galley takes precedence over this fallback color. + #[inline] + pub fn galley(pos: Pos2, galley: Arc, fallback_color: Color32) -> Self { + TextShape::new(pos, galley, fallback_color).into() + } + + /// All text color in the [`Galley`] will be replaced with the given color. #[inline] - pub fn galley(pos: Pos2, galley: Arc) -> Self { - TextShape::new(pos, galley).into() + pub fn galley_with_override_text_color( + pos: Pos2, + galley: Arc, + text_color: Color32, + ) -> Self { + TextShape::new(pos, galley, text_color) + .with_override_text_color(text_color) + .into() } #[inline] - /// The text color in the [`Galley`] will be replaced with the given color. + #[deprecated = "Use `Shape::galley` or `Shape::galley_with_override_text_color` instead"] pub fn galley_with_color(pos: Pos2, galley: Arc, text_color: Color32) -> Self { - TextShape { - override_text_color: Some(text_color), - ..TextShape::new(pos, galley) - } - .into() + Self::galley_with_override_text_color(pos, galley, text_color) } + #[inline] pub fn mesh(mesh: Mesh) -> Self { crate::epaint_assert!(mesh.is_valid()); Self::Mesh(mesh) @@ -669,9 +681,14 @@ pub struct TextShape { /// You can also set an underline when creating the galley. pub underline: Stroke, + /// Any [`Color32::PLACEHOLDER`] in the galley will be replaced by the given color. + /// Affects everything: backgrounds, glyphs, strikethough, underline, etc. + pub fallback_color: Color32, + /// If set, the text color in the galley will be ignored and replaced /// with the given color. - /// This will NOT replace background color nor strikethrough/underline color. + /// + /// This only affects the glyphs and will NOT replace background color nor strikethrough/underline color. pub override_text_color: Option, /// Rotate text by this many radians clockwise. @@ -680,12 +697,16 @@ pub struct TextShape { } impl TextShape { + /// The given fallback color will be used for any uncolored part of the galley (using [`Color32::PLACEHOLDER`]). + /// + /// Any non-placeholder color in the galley takes precedence over this fallback color. #[inline] - pub fn new(pos: Pos2, galley: Arc) -> Self { + pub fn new(pos: Pos2, galley: Arc, fallback_color: Color32) -> Self { Self { pos, galley, underline: Stroke::NONE, + fallback_color, override_text_color: None, angle: 0.0, } @@ -696,6 +717,27 @@ impl TextShape { pub fn visual_bounding_rect(&self) -> Rect { self.galley.mesh_bounds.translate(self.pos.to_vec2()) } + + #[inline] + pub fn with_underline(mut self, underline: Stroke) -> Self { + self.underline = underline; + self + } + + /// Use the given color for the text, regardless of what color is already in the galley. + #[inline] + pub fn with_override_text_color(mut self, override_text_color: Color32) -> Self { + self.override_text_color = Some(override_text_color); + self + } + + /// Rotate text by this many radians clockwise. + /// The pivot is `pos` (the upper left corner of the text). + #[inline] + pub fn with_angle(mut self, angle: f32) -> Self { + self.angle = angle; + self + } } impl From for Shape { diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index fa50f0df435..72899f2844c 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1473,6 +1473,7 @@ impl Tessellator { galley, underline, override_text_color, + fallback_color, angle, } = text_shape; @@ -1539,11 +1540,16 @@ impl Tessellator { let Vertex { pos, uv, mut color } = *vertex; if let Some(override_text_color) = override_text_color { + // Only override the glyph color (not background color, strike-through color, etc) if row.visuals.glyph_vertex_range.contains(&i) { color = *override_text_color; } + } else if color == Color32::PLACEHOLDER { + color = *fallback_color; } + crate::epaint_assert!(color != Color32::PLACEHOLDER, "A placeholder color made it to the tessellator. You forgot to set a fallback color."); + let offset = if *angle == 0.0 { pos.to_vec2() } else { diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index 61ed80fe0d4..7165b2655db 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -531,12 +531,7 @@ impl Fonts { font_id: FontId, wrap_width: f32, ) -> Arc { - self.layout_job(LayoutJob::simple( - text, - font_id, - crate::Color32::TEMPORARY_COLOR, - wrap_width, - )) + self.layout(text, font_id, crate::Color32::PLACEHOLDER, wrap_width) } } diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 7b81cd82440..6d9748aa33a 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -509,8 +509,9 @@ pub struct RowVisuals { /// Does NOT include leading or trailing whitespace glyphs!! pub mesh_bounds: Rect, - /// The range of vertices in the mesh the contain glyphs. - /// Before comes backgrounds (if any), and after any underlines and strikethrough. + /// The range of vertices in the mesh that contain glyphs (as opposed to background, underlines, strikethorugh, etc). + /// + /// The glyph vertices comes before backgrounds (if any), and after any underlines and strikethrough. pub glyph_vertex_range: Range, } From ffcc3f066c181132956a5938683361be34d14f9a Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 22 Dec 2023 17:33:01 +0100 Subject: [PATCH 24/45] Typos and doc improvements --- crates/ecolor/src/color32.rs | 2 +- crates/eframe/src/epi.rs | 2 +- crates/egui/src/context.rs | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/ecolor/src/color32.rs b/crates/ecolor/src/color32.rs index 32e1487bc7f..f3cf3fe6c95 100644 --- a/crates/ecolor/src/color32.rs +++ b/crates/ecolor/src/color32.rs @@ -71,7 +71,7 @@ impl Color32 { /// i.e. often taken to mean "no color". pub const PLACEHOLDER: Color32 = Color32::from_rgba_premultiplied(64, 254, 0, 128); - #[deprecated = "Renmaed to PLACEHOLDER"] + #[deprecated = "Renamed to PLACEHOLDER"] pub const TEMPORARY_COLOR: Color32 = Self::PLACEHOLDER; #[inline] diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 115235cd34a..8f96b7962eb 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -581,7 +581,7 @@ impl std::str::FromStr for Renderer { /// Represents the surroundings of your app. /// /// It provides methods to inspect the surroundings (are we on the web?), -/// allocate textures, and change settings (e.g. window size). +/// access to persistent storage, and access to the rendering backend. pub struct Frame { /// Information about the integration. pub(crate) info: IntegrationInfo, diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index c24a9d02ba7..02d58390812 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1191,11 +1191,13 @@ impl Context { /// If this is called at least once in a frame, then there will be another frame right after this. /// Call as many times as you wish, only one repaint will be issued. /// + /// To request repaint with a delay, use [`Self::request_repaint_after`]. + /// /// If called from outside the UI thread, the UI thread will wake up and run, /// provided the egui integration has set that up via [`Self::set_request_repaint_callback`] /// (this will work on `eframe`). /// - /// This will repaint the current viewport + /// This will repaint the current viewport. pub fn request_repaint(&self) { self.request_repaint_of(self.viewport_id()); } @@ -1205,11 +1207,13 @@ impl Context { /// If this is called at least once in a frame, then there will be another frame right after this. /// Call as many times as you wish, only one repaint will be issued. /// + /// To request repaint with a delay, use [`Self::request_repaint_after_for`]. + /// /// If called from outside the UI thread, the UI thread will wake up and run, /// provided the egui integration has set that up via [`Self::set_request_repaint_callback`] /// (this will work on `eframe`). /// - /// This will repaint the specified viewport + /// This will repaint the specified viewport. pub fn request_repaint_of(&self, id: ViewportId) { self.write(|ctx| ctx.request_repaint(id)); } From 365a8d2240feb66b10734bfc397070363f797e2c Mon Sep 17 00:00:00 2001 From: Antoine Beyeler <49431240+abey79@users.noreply.github.com> Date: Sat, 23 Dec 2023 15:53:40 +0100 Subject: [PATCH 25/45] Fix `Window` positioning bug when bad `pivot` is stored in app data (#3721) This PR fixes an issue where a bad pivot stored in app state would override the pivot actually set from code. The root code of this issue if the `Window`'s `State` containing `pivot` in the first place, as `pivot` is not part of a state to be tracked. It makes the `State` structure more ergonomic though, so this PR leaves the `pivot` field but always overrides it with the value provided by user code. #### Repro of the original issue This issue can be reproduced using the `re_ui_example` app from the Rerun repo, at this commit: https://github.com/rerun-io/rerun/pull/4577/commits/fb5add0047f108769c413393d65d52e362cbcc8e. By using this `app.ron` file, the bug will appear: ``` { "egui": "( areas:{ ((0)):( areas:{ (13430889033718688666):(pivot_pos:(x:565.5,y:328.0),pivot:((Min,Min)),size:(x:364.0,y:75.5),interactable:true), }, ) } )", } ``` The modal is entered based on it's top-left corner even though the code actually specifies a center-center pivot: image With this PR, the centring is correct even with the "poisoned" `app.ron`: image --- crates/egui/src/containers/area.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs index a8c1d417139..52abb7499ed 100644 --- a/crates/egui/src/containers/area.rs +++ b/crates/egui/src/containers/area.rs @@ -265,7 +265,13 @@ impl Area { let layer_id = LayerId::new(order, id); - let state = ctx.memory(|mem| mem.areas().get(id).copied()); + let state = ctx + .memory(|mem| mem.areas().get(id).copied()) + .map(|mut state| { + // override the saved state with the correct value + state.pivot = pivot; + state + }); let is_new = state.is_none(); if is_new { ctx.request_repaint(); // if we don't know the previous size we are likely drawing the area in the wrong place From 76025f2c159dc19e977e5610763af6c3642024c7 Mon Sep 17 00:00:00 2001 From: Wybe Westra Date: Sat, 23 Dec 2023 15:58:48 +0100 Subject: [PATCH 26/45] Fix: `Grid` now follows `style.visuals.striped` setting if not explicitly overwritten (#3723) The docs of [`Visuals.striped`](https://docs.rs/egui/latest/egui/style/struct.Visuals.html#structfield.striped) mention that they control the default striping of Grid. However, this seems to be broken. This pr makes the Grid follow the striped setting if it doesn't yet have a row coloring set. --- crates/egui/src/grid.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/egui/src/grid.rs b/crates/egui/src/grid.rs index 1ecb319a9b7..374902e3a4b 100644 --- a/crates/egui/src/grid.rs +++ b/crates/egui/src/grid.rs @@ -341,14 +341,12 @@ impl Grid { /// Default is whatever is in [`crate::Visuals::striped`]. pub fn striped(self, striped: bool) -> Self { if striped { - self.with_row_color(move |row, style| { - if row % 2 == 1 { - return Some(style.visuals.faint_bg_color); - } - None - }) + self.with_row_color(striped_row_color) } else { - self + // Explicitly set the row color to nothing. + // Needed so that when the style.visuals.striped value is checked later on, + // it is clear that the user does not want stripes on this specific Grid. + self.with_row_color(|_row: usize, _style: &Style| None) } } @@ -410,11 +408,14 @@ impl Grid { max_cell_size, spacing, start_row, - color_picker, + mut color_picker, } = self; let min_col_width = min_col_width.unwrap_or_else(|| ui.spacing().interact_size.x); let min_row_height = min_row_height.unwrap_or_else(|| ui.spacing().interact_size.y); let spacing = spacing.unwrap_or_else(|| ui.spacing().item_spacing); + if color_picker.is_none() && ui.visuals().striped { + color_picker = Some(Box::new(striped_row_color)); + } let id = ui.make_persistent_id(id_source); let prev_state = State::load(ui.ctx(), id); @@ -454,3 +455,10 @@ impl Grid { }) } } + +fn striped_row_color(row: usize, style: &Style) -> Option { + if row % 2 == 1 { + return Some(style.visuals.faint_bg_color); + } + None +} From fc18d6f8f445bf840478979b97b9806a110d1b6f Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Sat, 23 Dec 2023 16:17:04 +0100 Subject: [PATCH 27/45] Add support for dashed lines with offset (#3720) Closes . This can naturally be obtained by starting the drawing a bit "later", but providing support for this directly will simplify interleaved drawing of dashed lines. An even more general approach would be to allow passing an even-length vector of lengths and gaps, in addition to offset. Primarily thinking if it is better to go even further if a new dashed-line-method is introduced. The second commit introduce this (I can naturally squash these!). --- crates/epaint/src/shape.rs | 79 ++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index cfae4493d0f..8d8a5ffba00 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -144,12 +144,39 @@ impl Shape { gap_length: f32, ) -> Vec { let mut shapes = Vec::new(); - dashes_from_line(path, stroke.into(), dash_length, gap_length, &mut shapes); + dashes_from_line( + path, + stroke.into(), + &[dash_length], + &[gap_length], + &mut shapes, + 0., + ); + shapes + } + + /// Turn a line into dashes with different dash/gap lengths and a start offset. + pub fn dashed_line_with_offset( + path: &[Pos2], + stroke: impl Into, + dash_lengths: &[f32], + gap_lengths: &[f32], + dash_offset: f32, + ) -> Vec { + let mut shapes = Vec::new(); + dashes_from_line( + path, + stroke.into(), + dash_lengths, + gap_lengths, + &mut shapes, + dash_offset, + ); shapes } /// Turn a line into dashes. If you need to create many dashed lines use this instead of - /// [`Self::dashed_line`] + /// [`Self::dashed_line`]. pub fn dashed_line_many( points: &[Pos2], stroke: impl Into, @@ -157,7 +184,34 @@ impl Shape { gap_length: f32, shapes: &mut Vec, ) { - dashes_from_line(points, stroke.into(), dash_length, gap_length, shapes); + dashes_from_line( + points, + stroke.into(), + &[dash_length], + &[gap_length], + shapes, + 0., + ); + } + + /// Turn a line into dashes with different dash/gap lengths and a start offset. If you need to + /// create many dashed lines use this instead of [`Self::dashed_line_with_offset`]. + pub fn dashed_line_many_with_offset( + points: &[Pos2], + stroke: impl Into, + dash_lengths: &[f32], + gap_lengths: &[f32], + dash_offset: f32, + shapes: &mut Vec, + ) { + dashes_from_line( + points, + stroke.into(), + dash_lengths, + gap_lengths, + shapes, + dash_offset, + ); } /// A convex polygon with a fill and optional stroke. @@ -775,12 +829,16 @@ fn points_from_line( fn dashes_from_line( path: &[Pos2], stroke: Stroke, - dash_length: f32, - gap_length: f32, + dash_lengths: &[f32], + gap_lengths: &[f32], shapes: &mut Vec, + dash_offset: f32, ) { - let mut position_on_segment = 0.0; + assert_eq!(dash_lengths.len(), gap_lengths.len()); + let mut position_on_segment = dash_offset; let mut drawing_dash = false; + let mut step = 0; + let steps = dash_lengths.len(); path.windows(2).for_each(|window| { let (start, end) = (window[0], window[1]); let vector = end - start; @@ -792,11 +850,16 @@ fn dashes_from_line( if drawing_dash { // This is the end point. shapes.push(Shape::line_segment([start_point, new_point], stroke)); - position_on_segment += gap_length; + position_on_segment += gap_lengths[step]; + // Increment step counter + step += 1; + if step >= steps { + step = 0; + } } else { // Start a new dash. start_point = new_point; - position_on_segment += dash_length; + position_on_segment += dash_lengths[step]; } drawing_dash = !drawing_dash; } From 0a6ea15f6c1857f3a036e70e34df2565737ccd07 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 25 Dec 2023 19:00:54 +0100 Subject: [PATCH 28/45] impl `Clone` for `Fonts` (#3737) Closes https://github.com/emilk/egui/issues/3731 --- .vscode/settings.json | 1 + crates/epaint/src/text/fonts.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index dbe8e6629dc..8ec1e4aa67f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,4 +32,5 @@ "--all-targets", "--all-features", ], + "rust-analyzer.showUnlinkedFileNotification": false, } diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index 7165b2655db..177c7327819 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -348,6 +348,7 @@ impl FontDefinitions { /// If you are using `egui`, use `egui::Context::set_fonts` and `egui::Context::fonts`. /// /// You need to call [`Self::begin_frame`] and [`Self::font_image_delta`] once every frame. +#[derive(Clone)] pub struct Fonts(Arc>); impl Fonts { From c3a9355279ef2896f109944d534e507e0eb32fae Mon Sep 17 00:00:00 2001 From: Arnold Loubriat Date: Mon, 25 Dec 2023 19:01:05 +0100 Subject: [PATCH 29/45] Fix: Let `accesskit` process window events (#3733) Since accesskit_winit version 0.15.0, it is necessary to call `Adapter::process_events` to let AccessKit fire up window focus events on Unix and macOS. Furthermore this has always been needed on Unix (X11 only) to let AccessKit know about the window bounds, which is needed to perform hit-testing on nodes. --- crates/egui-winit/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index da4d2bc4358..73a87eb5fb2 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -236,6 +236,11 @@ impl State { ) -> EventResponse { crate::profile_function!(short_window_event_description(event)); + #[cfg(feature = "accesskit")] + if let Some(accesskit) = &self.accesskit { + accesskit.process_event(window, event); + } + use winit::event::WindowEvent; match event { WindowEvent::ScaleFactorChanged { scale_factor, .. } => { From 5b591d26f6960bffab87beadd3e9250523fb3a18 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 25 Dec 2023 19:02:02 +0100 Subject: [PATCH 30/45] Add `Key::Cut`, `Key::Copy`, `Key::Paste` (#3725) The comment added in commit 8a0bc97e ("[egui_glium] Fix paste") seems to assume that `winit` "should have translated" common "paste" keyboard combos to a `Cut`/`Copy`/`Paste` "KeyCode", but completely glossed over the fact that this `KeyCode` (now also `NamedKey`) maps to a special key dedicated to this purpose found on some keyboards and OSes. Make sure that this key is still handled in addition to the combo that is detected. --- Note that this PR does not compile as it is (and I have hence not tested this nor even ran into this limitation), just noticed this inconsistency while failing to understand a code comment. We'd have to decide if the variants should be added to `egui::Key` or if these helper functions need to take `winit` keys (`ScanCode` or `NamedKey`) directly? I should have an old keyboard with a physical paste key in a drawer somewhere. And on Android there are special copy/paste events that can be sent by a virtual keyboard or the debug shell, so that this can be properly tested before it is merged. (Except that the current `clipboard` implementation is not supported on Android) --- crates/egui-winit/src/lib.rs | 94 +++++++++++++++++++---------------- crates/egui/src/data/input.rs | 15 ++++++ 2 files changed, 66 insertions(+), 43 deletions(-) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 73a87eb5fb2..3da3781e093 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -691,8 +691,6 @@ impl State { if let Some(logical_key) = logical_key { if pressed { - // KeyCode::Paste etc in winit are broken/untrustworthy, - // so we detect these things manually: if is_cut_command(self.egui_input.modifiers, logical_key) { self.egui_input.events.push(egui::Event::Cut); return true; @@ -946,17 +944,20 @@ fn is_printable_char(chr: char) -> bool { } fn is_cut_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool { - (modifiers.command && keycode == egui::Key::X) + keycode == egui::Key::Cut + || (modifiers.command && keycode == egui::Key::X) || (cfg!(target_os = "windows") && modifiers.shift && keycode == egui::Key::Delete) } fn is_copy_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool { - (modifiers.command && keycode == egui::Key::C) + keycode == egui::Key::Copy + || (modifiers.command && keycode == egui::Key::C) || (cfg!(target_os = "windows") && modifiers.ctrl && keycode == egui::Key::Insert) } fn is_paste_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool { - (modifiers.command && keycode == egui::Key::V) + keycode == egui::Key::Paste + || (modifiers.command && keycode == egui::Key::V) || (cfg!(target_os = "windows") && modifiers.shift && keycode == egui::Key::Insert) } @@ -983,47 +984,50 @@ fn key_from_named_key(named_key: winit::keyboard::NamedKey) -> Option use egui::Key; use winit::keyboard::NamedKey; - match named_key { - NamedKey::Enter => Some(Key::Enter), - NamedKey::Tab => Some(Key::Tab), - NamedKey::Space => Some(Key::Space), - NamedKey::ArrowDown => Some(Key::ArrowDown), - NamedKey::ArrowLeft => Some(Key::ArrowLeft), - NamedKey::ArrowRight => Some(Key::ArrowRight), - NamedKey::ArrowUp => Some(Key::ArrowUp), - NamedKey::End => Some(Key::End), - NamedKey::Home => Some(Key::Home), - NamedKey::PageDown => Some(Key::PageDown), - NamedKey::PageUp => Some(Key::PageUp), - NamedKey::Backspace => Some(Key::Backspace), - NamedKey::Delete => Some(Key::Delete), - NamedKey::Insert => Some(Key::Insert), - NamedKey::Escape => Some(Key::Escape), - NamedKey::F1 => Some(Key::F1), - NamedKey::F2 => Some(Key::F2), - NamedKey::F3 => Some(Key::F3), - NamedKey::F4 => Some(Key::F4), - NamedKey::F5 => Some(Key::F5), - NamedKey::F6 => Some(Key::F6), - NamedKey::F7 => Some(Key::F7), - NamedKey::F8 => Some(Key::F8), - NamedKey::F9 => Some(Key::F9), - NamedKey::F10 => Some(Key::F10), - NamedKey::F11 => Some(Key::F11), - NamedKey::F12 => Some(Key::F12), - NamedKey::F13 => Some(Key::F13), - NamedKey::F14 => Some(Key::F14), - NamedKey::F15 => Some(Key::F15), - NamedKey::F16 => Some(Key::F16), - NamedKey::F17 => Some(Key::F17), - NamedKey::F18 => Some(Key::F18), - NamedKey::F19 => Some(Key::F19), - NamedKey::F20 => Some(Key::F20), + Some(match named_key { + NamedKey::Enter => Key::Enter, + NamedKey::Tab => Key::Tab, + NamedKey::Space => Key::Space, + NamedKey::ArrowDown => Key::ArrowDown, + NamedKey::ArrowLeft => Key::ArrowLeft, + NamedKey::ArrowRight => Key::ArrowRight, + NamedKey::ArrowUp => Key::ArrowUp, + NamedKey::End => Key::End, + NamedKey::Home => Key::Home, + NamedKey::PageDown => Key::PageDown, + NamedKey::PageUp => Key::PageUp, + NamedKey::Backspace => Key::Backspace, + NamedKey::Delete => Key::Delete, + NamedKey::Insert => Key::Insert, + NamedKey::Escape => Key::Escape, + NamedKey::Cut => Key::Cut, + NamedKey::Copy => Key::Copy, + NamedKey::Paste => Key::Paste, + NamedKey::F1 => Key::F1, + NamedKey::F2 => Key::F2, + NamedKey::F3 => Key::F3, + NamedKey::F4 => Key::F4, + NamedKey::F5 => Key::F5, + NamedKey::F6 => Key::F6, + NamedKey::F7 => Key::F7, + NamedKey::F8 => Key::F8, + NamedKey::F9 => Key::F9, + NamedKey::F10 => Key::F10, + NamedKey::F11 => Key::F11, + NamedKey::F12 => Key::F12, + NamedKey::F13 => Key::F13, + NamedKey::F14 => Key::F14, + NamedKey::F15 => Key::F15, + NamedKey::F16 => Key::F16, + NamedKey::F17 => Key::F17, + NamedKey::F18 => Key::F18, + NamedKey::F19 => Key::F19, + NamedKey::F20 => Key::F20, _ => { log::trace!("Unknown key: {named_key:?}"); - None + return None; } - } + }) } fn key_from_key_code(key: winit::keyboard::KeyCode) -> Option { @@ -1054,6 +1058,10 @@ fn key_from_key_code(key: winit::keyboard::KeyCode) -> Option { // KeyCode::Colon => Key::Colon, // NOTE: there is no physical colon key on an american keyboard KeyCode::Semicolon => Key::Semicolon, + KeyCode::Cut => Key::Cut, + KeyCode::Copy => Key::Copy, + KeyCode::Paste => Key::Paste, + KeyCode::Minus | KeyCode::NumpadSubtract => Key::Minus, // Using Mac the key with the Plus sign on it is reported as the Equals key diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index afb6e1dcbeb..71b77d97020 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -879,6 +879,10 @@ pub enum Key { PageUp, PageDown, + Copy, + Cut, + Paste, + // ---------------------------------------------- // Punctuation: /// `:` @@ -1005,6 +1009,9 @@ impl Key { Self::End, Self::PageUp, Self::PageDown, + Self::Copy, + Self::Cut, + Self::Paste, // Punctuation: Self::Colon, Self::Comma, @@ -1102,6 +1109,10 @@ impl Key { "PageUp" => Self::PageUp, "PageDown" => Self::PageDown, + "Copy" => Self::Copy, + "Cut" => Self::Cut, + "Paste" => Self::Paste, + "Colon" | ":" => Self::Colon, "Comma" | "," => Self::Comma, "Minus" | "-" | "−" => Self::Minus, @@ -1209,6 +1220,10 @@ impl Key { Key::PageUp => "PageUp", Key::PageDown => "PageDown", + Key::Copy => "Copy", + Key::Cut => "Cut", + Key::Paste => "Paste", + Key::Colon => "Colon", Key::Comma => "Comma", Key::Minus => "Minus", From d978b3773274a57365913ae6e3b7e5d1272e464e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 28 Dec 2023 09:33:16 +0100 Subject: [PATCH 31/45] Improve CONTRIBUTING.md --- CONTRIBUTING.md | 119 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 39 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d3897457d4..9565c41dbaf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing guidelines +# Contribution Guidelines ## Introduction @@ -6,6 +6,8 @@ / Emil +## How to contribute to egui +You want to contribute to egui, but don't know how? First of all: thank you! I created a special issue just for that: . But make sure you still read this file first :) ## Discussion @@ -25,7 +27,7 @@ If you are filing a bug, please provide a way to reproduce it. ## Making a PR -First file an issue (or find an existing one) and announce that you plan to work on something. That way we will avoid having several people doing double work. Please ask for feedback before you start working on something non-trivial! +For small things, just go ahead an open a PR. For bigger things, please file an issue first (or find an existing one) and announce that you plan to work on something. That way we will avoid having several people doing double work, and you might get useful feedback on the issue before you start working. Browse through [`ARCHITECTURE.md`](ARCHITECTURE.md) to get a sense of how all pieces connects. @@ -34,76 +36,115 @@ You can test your code locally by running `./scripts/check.sh`. When you have something that works, open a draft PR. You may get some helpful feedback early! When you feel the PR is ready to go, do a self-review of the code, and then open it for review. -Please keep pull requests small and focused. - Don't worry about having many small commits in the PR - they will be squashed to one commit once merged. -Do not include the `.js` and `.wasm` build artifacts generated for building for web. -`git` is not great at storing large files like these, so we only commit a new web demo after a new egui release. +Please keep pull requests small and focused. The smaller it is, the more likely it is to get merged. + +## PR review + +Most PR reviews are done by me, Emil, but I very much appreciate any help I can get reviewing PRs! +It is very easy to add complexity to a project, but remember that each line of code added is code that needs to be maintained in perpituity, so we have a high bar on what get merged! + +When reviewing, we look for: +* The PR title and description should be helpful +* Breaking changes are documented in the PR description +* The code should be readable +* The code should have helpful docstrings +* The code should follow the [Code Style](CONTRIBUTING.md#code-style) + +Note that each new egui release have some breaking changes, so we don't mind having a few of those in a PR. Of course, we still try to avoid them if we can, and if we can't we try to first deprecate old code using the `#[deprecated]` attribute. ## Creating an integration for egui -If you make an integration for `egui` for some engine or renderer, please share it with the world! -I will add a link to it from the `egui` README.md so others can easily find it. +See for how to write your own egui integration. -Read the section on integrations at . +If you make an integration for `egui` for some engine or renderer, please share it with the world! +Make a PR to add it as a link to [`README.md`](README.md#integrations) so others can easily find it. ## Testing the web viewer -* Install some tools with `scripts/setup_web.sh` * Build with `scripts/build_demo_web.sh` * Host with `scripts/start_server.sh` * Open -## Code Conventions -Conventions unless otherwise specified: - -* angles are in radians and clock-wise -* `Vec2::X` is right and `Vec2::Y` is down. -* `Pos2::ZERO` is left top. - +## Code Style While using an immediate mode gui is simple, implementing one is a lot more tricky. There are many subtle corner-case you need to think through. The `egui` source code is a bit messy, partially because it is still evolving. -* Read some code before writing your own. -* Follow the `egui` code style. -* Add blank lines around all `fn`, `struct`, `enum`, etc. -* `// Comment like this.` and not `//like this`. -* Use `TODO` instead of `FIXME`. -* Add your github handle to the `TODO`:s you write, e.g: `TODO(emilk): clean this up`. -* Write idiomatic rust. -* Avoid `unsafe`. -* Avoid code that can cause panics. -* Use good names for everything. -* Add docstrings to types, `struct` fields and all `pub fn`. -* Add some example code (doc-tests). -* Before making a function longer, consider adding a helper function. -* If you are only using it in one function, put the `use` statement in that function. This improves locality, making it easier to read and move the code. -* When importing a `trait` to use it's trait methods, do this: `use Trait as _;`. That lets the reader know why you imported it, even though it seems unused. -* Follow the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/). -* Break the above rules when it makes sense. +* Read some code before writing your own +* Leave the code cleaner than how you found it +* Write idiomatic rust +* Follow the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/) +* Add blank lines around all `fn`, `struct`, `enum`, etc +* `// Comment like this.` and not `//like this` +* Use `TODO` instead of `FIXME` +* Add your github handle to the `TODO`:s you write, e.g: `TODO(emilk): clean this up` +* Avoid `unsafe` +* Avoid `unwrap` and any other code that can cause panics +* Use good names for everything +* Add docstrings to types, `struct` fields and all `pub fn` +* Add some example code (doc-tests) +* Before making a function longer, consider adding a helper function +* If you are only using it in one function, put the `use` statement in that function. This improves locality, making it easier to read and move the code +* When importing a `trait` to use it's trait methods, do this: `use Trait as _;`. That lets the reader know why you imported it, even though it seems unused +* Avoid double negatives +* Flip `if !condition {} else {}` +* Sets of things should be lexicographically sorted (e.g. crate dependencies in `Cargo.toml`) +* Break the above rules when it makes sense ### Good: ``` rust /// The name of the thing. -fn name(&self) -> &str { +pub fn name(&self) -> &str { &self.name } fn foo(&self) { - // TODO(emilk): implement + // TODO(emilk): this can be optimized } ``` ### Bad: ``` rust -//some function -fn get_name(&self) -> &str { +//gets the name +pub fn get_name(&self) -> &str { &self.name } fn foo(&self) { - //FIXME: implement + //FIXME: this can be optimized } ``` + +### Coordinate system +The left-top corner of the screen is `(0.0, 0.0)`, +with `Vec2::X` increasing to the right and `Vec2::Y` increasing downwards. + +`egui` uses logical _points_ as its coordinate system. +Those related to physical _pixels_ by the `pixels_per_point` scale factor. +For example, a high-dpi screeen can have `pixels_per_point = 2.0`, +meaning there are two physical screen pixels for each logical point. + +Angles are in radians, and are measured clockwise from the X-axis, which has angle=0. + + +### Avoid `unwrap`, `expect` etc. +The code should never panic or crash, which means that any instance of `unwrap` or `expect` is a potential time-bomb. Even if you structured your code to make them impossible, any reader will have to read the code very carefully to prove to themselves that an `unwrap` won't panic. Often you can instead rewrite your code so as to avoid it. The same goes for indexing into a slice (which will panic on out-of-bounds) - it is often preferable to use `.get()`. + +For instance: + +``` rust +let first = if vec.is_empty() { + return; +} else { + vec[0] +}; +``` +can be better written as: + +``` rust +let Some(first) = vec.first() else { + return; +}; +``` From 790b401adf892d983dc8a84df24c1b01eb2b738b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 28 Dec 2023 10:36:07 +0100 Subject: [PATCH 32/45] Add -g option to build_demo_web.sh --- scripts/build_demo_web.sh | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/scripts/build_demo_web.sh b/scripts/build_demo_web.sh index 658c6bb3ca5..e905066d42a 100755 --- a/scripts/build_demo_web.sh +++ b/scripts/build_demo_web.sh @@ -19,23 +19,37 @@ OPTIMIZE=false BUILD=debug BUILD_FLAGS="" WEB_GPU=false +WASM_OPT_FLAGS="-O2 --fast-math" while test $# -gt 0; do case "$1" in -h|--help) echo "build_demo_web.sh [--release] [--webgpu] [--open]" echo "" - echo " --release: Build with --release, and enable extra optimization step" - echo " Runs wasm-opt." - echo " NOTE: --release also removes debug symbols which are otherwise useful for in-browser profiling." + echo " -g: Keep debug symbols even with --release." + echo " These are useful profiling and size trimming." echo "" - echo " --webgpu: Build a binary for WebGPU instead of WebGL" - echo " Note that the resulting wasm will ONLY work on browsers with WebGPU." + echo " --open: Open the result in a browser." + echo "" + echo " --release: Build with --release, and then run wasm-opt." + echo " NOTE: --release also removes debug symbols, unless you also use -g." echo "" - echo " --open: Open the result in a browser" + echo " --webgpu: Build a binary for WebGPU instead of WebGL." + echo " Note that the resulting wasm will ONLY work on browsers with WebGPU." exit 0 ;; + -g) + shift + WASM_OPT_FLAGS="${WASM_OPT_FLAGS} -g" + echo "'${WASM_OPT_FLAGS}'" + ;; + + --open) + shift + OPEN=true + ;; + --release) shift OPTIMIZE=true @@ -48,11 +62,6 @@ while test $# -gt 0; do WEB_GPU=true ;; - --open) - shift - OPEN=true - ;; - *) echo "Unknown option: $1" exit 1 @@ -96,6 +105,7 @@ wasm-bindgen "${WASM_PATH}" --out-dir web_demo --out-name ${OUT_FILE_NAME} --no- # if this fails with "error: cannot import from modules (`env`) with `--no-modules`", you can use: # wasm2wat target/wasm32-unknown-unknown/release/egui_demo_app.wasm | rg env # wasm2wat target/wasm32-unknown-unknown/release/egui_demo_app.wasm | rg "call .now\b" -B 20 # What calls `$now` (often a culprit) +# Or use https://rustwasm.github.io/twiggy/usage/command-line-interface/paths.html#twiggy-paths # to get wasm-strip: apt/brew/dnf install wabt # wasm-strip ${FINAL_WASM_PATH} @@ -103,7 +113,7 @@ wasm-bindgen "${WASM_PATH}" --out-dir web_demo --out-name ${OUT_FILE_NAME} --no- if [[ "${OPTIMIZE}" = true ]]; then echo "Optimizing wasm…" # to get wasm-opt: apt/brew/dnf install binaryen - wasm-opt "${FINAL_WASM_PATH}" -O2 --fast-math -o "${FINAL_WASM_PATH}" # add -g to get debug symbols + wasm-opt "${FINAL_WASM_PATH}" $WASM_OPT_FLAGS -o "${FINAL_WASM_PATH}" fi echo "Finished ${FINAL_WASM_PATH}" From 9dbfb8ce4e26ca47db3db30ec1e7637a6a351ddf Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 28 Dec 2023 10:38:07 +0100 Subject: [PATCH 33/45] Make check.sh more quiet --- scripts/check.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/check.sh b/scripts/check.sh index eb9cee8c7fd..ab3063d91ca 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -9,8 +9,8 @@ set -x # Checks all tests, lints etc. # Basically does what the CI does. -cargo install cargo-cranky # Uses lints defined in Cranky.toml. See https://github.com/ericseppanen/cargo-cranky -cargo install typos-cli +cargo install --quiet cargo-cranky # Uses lints defined in Cranky.toml. See https://github.com/ericseppanen/cargo-cranky +cargo install --quiet typos-cli # web_sys_unstable_apis is required to enable the web_sys clipboard API which eframe web uses, # as well as by the wasm32-backend of the wgpu crate. From e44f54f81f65a224c6cbd110f0defa94503d205d Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 28 Dec 2023 11:16:39 +0100 Subject: [PATCH 34/45] Fix text color of disabled widgets (#3744) * Introduced recently in https://github.com/emilk/egui/pull/3727 * Closes https://github.com/emilk/egui/issues/3732 --- crates/egui/src/painter.rs | 4 +- crates/epaint/src/shape_transform.rs | 85 +++++++++++++++++++--------- 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/crates/egui/src/painter.rs b/crates/egui/src/painter.rs index dfae0a92cfe..8d6e3e6a8d6 100644 --- a/crates/egui/src/painter.rs +++ b/crates/egui/src/painter.rs @@ -458,6 +458,8 @@ impl Painter { fn tint_shape_towards(shape: &mut Shape, target: Color32) { epaint::shape_transform::adjust_colors(shape, &|color| { - *color = crate::ecolor::tint_color_towards(*color, target); + if *color != Color32::PLACEHOLDER { + *color = crate::ecolor::tint_color_towards(*color, target); + } }); } diff --git a/crates/epaint/src/shape_transform.rs b/crates/epaint/src/shape_transform.rs index 1b0e9c73e87..b36accb5a3f 100644 --- a/crates/epaint/src/shape_transform.rs +++ b/crates/epaint/src/shape_transform.rs @@ -1,5 +1,6 @@ use crate::*; +/// Remember to handle [`Color32::PLACEHOLDER`] specially! pub fn adjust_colors(shape: &mut Shape, adjust_color: &impl Fn(&mut Color32)) { #![allow(clippy::match_same_arms)] match shape { @@ -9,28 +10,62 @@ pub fn adjust_colors(shape: &mut Shape, adjust_color: &impl Fn(&mut Color32)) { adjust_colors(shape, adjust_color); } } - Shape::Circle(circle_shape) => { - adjust_color(&mut circle_shape.fill); - adjust_color(&mut circle_shape.stroke.color); - } - Shape::LineSegment { stroke, .. } => { + Shape::LineSegment { stroke, points: _ } => { adjust_color(&mut stroke.color); } - Shape::Path(path_shape) => { - adjust_color(&mut path_shape.fill); - adjust_color(&mut path_shape.stroke.color); - } - Shape::Rect(rect_shape) => { - adjust_color(&mut rect_shape.fill); - adjust_color(&mut rect_shape.stroke.color); + + Shape::Circle(CircleShape { + center: _, + radius: _, + fill, + stroke, + }) + | Shape::Path(PathShape { + points: _, + closed: _, + fill, + stroke, + }) + | Shape::Rect(RectShape { + rect: _, + rounding: _, + fill, + stroke, + fill_texture_id: _, + uv: _, + }) + | Shape::QuadraticBezier(QuadraticBezierShape { + points: _, + closed: _, + fill, + stroke, + }) + | Shape::CubicBezier(CubicBezierShape { + points: _, + closed: _, + fill, + stroke, + }) => { + adjust_color(fill); + adjust_color(&mut stroke.color); } - Shape::Text(text_shape) => { - if let Some(override_text_color) = &mut text_shape.override_text_color { + + Shape::Text(TextShape { + pos: _, + galley, + underline, + fallback_color, + override_text_color, + angle: _, + }) => { + adjust_color(&mut underline.color); + adjust_color(fallback_color); + if let Some(override_text_color) = override_text_color { adjust_color(override_text_color); } - if !text_shape.galley.is_empty() { - let galley = std::sync::Arc::make_mut(&mut text_shape.galley); + if !galley.is_empty() { + let galley = std::sync::Arc::make_mut(galley); for row in &mut galley.rows { for vertex in &mut row.visuals.mesh.vertices { adjust_color(&mut vertex.color); @@ -38,19 +73,17 @@ pub fn adjust_colors(shape: &mut Shape, adjust_color: &impl Fn(&mut Color32)) { } } } - Shape::Mesh(mesh) => { - for v in &mut mesh.vertices { + + Shape::Mesh(Mesh { + indices: _, + vertices, + texture_id: _, + }) => { + for v in vertices { adjust_color(&mut v.color); } } - Shape::QuadraticBezier(quadratic) => { - adjust_color(&mut quadratic.fill); - adjust_color(&mut quadratic.stroke.color); - } - Shape::CubicBezier(bezier) => { - adjust_color(&mut bezier.fill); - adjust_color(&mut bezier.stroke.color); - } + Shape::Callback(_) => { // Can't tint user callback code } From f4102c05e6abe9283ee29bebf3fa558af33cc176 Mon Sep 17 00:00:00 2001 From: Daniel Buch Hansen Date: Fri, 29 Dec 2023 16:59:29 +0100 Subject: [PATCH 35/45] Add missing `ResizeDirection::East` (#3749) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds the missing variant described in below issue. Closes . If you wish - or even desired - I could look into implement the From conversion between ResizeDirection and CursorInfo in a seperate PR. I ran into an issue then running `check.sh` however it seems unrelated to this PR. For clarity I'm running Linux, with a wayland compositor. ```bash ~/dev/rust/egui ❯ cargo clean [add-missing-resizedirection ≡] ~/dev/rust/egui ❯ ./scripts/check.sh [add-missing-resizedirection ≡] + cargo install --quiet cargo-cranky + cargo install --quiet typos-cli + export 'RUSTFLAGS=--cfg=web_sys_unstable_apis -D warnings' + RUSTFLAGS='--cfg=web_sys_unstable_apis -D warnings' + export 'RUSTDOCFLAGS=-D warnings' + RUSTDOCFLAGS='-D warnings' + typos + ./scripts/lint.py ./scripts/lint.py finished without error + cargo fmt --all -- --check + cargo doc --quiet --lib --no-deps --all-features + cargo doc --quiet --document-private-items --no-deps --all-features + cargo cranky --quiet --all-targets --all-features -- -D warnings + ./scripts/clippy_wasm.sh + export CLIPPY_CONF_DIR=scripts/clippy_wasm + CLIPPY_CONF_DIR=scripts/clippy_wasm + cargo cranky --quiet --all-features --target wasm32-unknown-unknown --target-dir target_wasm -p egui_demo_app --lib -- --deny warnings + cargo check --quiet --all-targets + cargo check --quiet --all-targets --all-features + cargo check --quiet -p egui_demo_app --lib --target wasm32-unknown-unknown + cargo check --quiet -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features + cargo test --quiet --all-targets --all-features running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 4 tests i... test result: ok. 3 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 14 tests .............. test result: ok. 14 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s running 1 test . test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 3 tests ... test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.52s Testing demo_with_tessellate__realistic Success Testing demo_no_tessellate Success Testing demo_only_tessellate Success Testing label &str Success Testing label format! Success Testing Painter::rect Success Testing text_layout_uncached Success Testing text_layout_cached Success Testing tessellate_text Success running 3 tests ... test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 1 test . test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 7 tests ....... test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 23 tests ....................... test result: ok. 23 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.20s Testing single_dashed_lines Success Testing many_dashed_lines Success Testing tessellate_circles_100k Success running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + cargo test --quiet --doc running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 4 tests .... test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.26s running 146 tests ........................................................................................ 88/146 i......................................................... test result: ok. 145 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 108.27s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 5 tests iiii. test result: ok. 1 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 3.65s running 5 tests ..... test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.99s running 2 tests .. test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s running 5 tests ii... test result: ok. 3 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 2.53s running 11 tests ........... test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.38s running 5 tests i.... test result: ok. 4 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 1.01s + cd crates/eframe + cargo check --quiet --no-default-features --features glow + cd crates/eframe + cargo check --quiet --no-default-features --features wgpu error: The platform you're compiling for is not supported by winit --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/platform_impl/mod.rs:67:1 | 67 | compile_error!("The platform you're compiling for is not supported by winit"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0432]: unresolved import `self::platform` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/platform_impl/mod.rs:26:15 | 26 | pub use self::platform::*; | ^^^^^^^^ could not find `platform` in `self` error[E0432]: unresolved import `crate::platform_impl::PlatformIcon` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/icon.rs:1:5 | 1 | use crate::platform_impl::PlatformIcon; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `PlatformIcon` in `platform_impl` error[E0433]: failed to resolve: could not find `DeviceId` in `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/event.rs:616:42 | 616 | DeviceId(unsafe { platform_impl::DeviceId::dummy() }) | ^^^^^^^^ could not find `DeviceId` in `platform_impl` error[E0433]: failed to resolve: could not find `WindowId` in `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/window.rs:99:42 | 99 | WindowId(unsafe { platform_impl::WindowId::dummy() }) | ^^^^^^^^ could not find `WindowId` in `platform_impl` error[E0433]: failed to resolve: could not find `Window` in `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/window.rs:513:28 | 513 | platform_impl::Window::new(&window_target.p, self.window, self.platform_specific)?; | ^^^^^^ could not find `Window` in `platform_impl` error[E0412]: cannot find type `OsError` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/error.rs:28:27 | 28 | error: platform_impl::OsError, | ^^^^^^^ not found in `platform_impl` | help: there is an enum variant `crate::window::BadIcon::OsError`; try using the variant's enum | 28 | error: crate::window::BadIcon, | ~~~~~~~~~~~~~~~~~~~~~~ error[E0412]: cannot find type `OsError` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/error.rs:62:76 | 62 | pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError { | ^^^^^^^ not found in `platform_impl` | help: there is an enum variant `crate::window::BadIcon::OsError`; try using the variant's enum | 62 | pub(crate) fn new(line: u32, file: &'static str, error: crate::window::BadIcon) -> OsError { | ~~~~~~~~~~~~~~~~~~~~~~ error[E0412]: cannot find type `DeviceId` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/event.rs:602:47 | 602 | pub struct DeviceId(pub(crate) platform_impl::DeviceId); | ^^^^^^^^ not found in `platform_impl` error[E0412]: cannot find type `KeyEventExtra` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/event.rs:787:50 | 787 | pub(crate) platform_specific: platform_impl::KeyEventExtra, | ^^^^^^^^^^^^^ not found in `platform_impl` error[E0412]: cannot find type `EventLoop` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/event_loop.rs:41:43 | 41 | pub(crate) event_loop: platform_impl::EventLoop, | ^^^^^^^^^ not found in `platform_impl` | help: consider importing this struct | 10 + use calloop::EventLoop; | help: if you import `EventLoop`, refer to it directly | 41 - pub(crate) event_loop: platform_impl::EventLoop, 41 + pub(crate) event_loop: EventLoop, | error[E0412]: cannot find type `EventLoopWindowTarget` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/event_loop.rs:52:34 | 52 | pub(crate) p: platform_impl::EventLoopWindowTarget, | ^^^^^^^^^^^^^^^^^^^^^ not found in `platform_impl` error[E0412]: cannot find type `PlatformSpecificEventLoopAttributes` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/event_loop.rs:62:50 | 62 | pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `platform_impl` error[E0433]: failed to resolve: could not find `EventLoop` in `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/event_loop.rs:128:40 | 128 | event_loop: platform_impl::EventLoop::new(&mut self.platform_specific)?, | ^^^^^^^^^ could not find `EventLoop` in `platform_impl` | help: consider importing this struct | 10 + use calloop::EventLoop; | help: if you import `EventLoop`, refer to it directly | 128 - event_loop: platform_impl::EventLoop::new(&mut self.platform_specific)?, 128 + event_loop: EventLoop::new(&mut self.platform_specific)?, | error[E0412]: cannot find type `EventLoopProxy` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/event_loop.rs:394:38 | 394 | event_loop_proxy: platform_impl::EventLoopProxy, | ^^^^^^^^^^^^^^ not found in `platform_impl` error[E0412]: cannot find type `VideoMode` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/monitor.rs:18:43 | 18 | pub(crate) video_mode: platform_impl::VideoMode, | ^^^^^^^^^ not found in `platform_impl` error[E0412]: cannot find type `MonitorHandle` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/monitor.rs:104:38 | 104 | pub(crate) inner: platform_impl::MonitorHandle, | ^^^^^^^^^^^^^ not found in `platform_impl` error[E0412]: cannot find type `VideoMode` in this scope --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/platform_impl/mod.rs:31:15 | 31 | Exclusive(VideoMode), | ^^^^^^^^^ not found in this scope | help: consider importing this struct | 1 + use crate::monitor::VideoMode; | error[E0412]: cannot find type `MonitorHandle` in this scope --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/platform_impl/mod.rs:32:23 | 32 | Borderless(Option), | ^^^^^^^^^^^^^ | ::: /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/monitor.rs:103:1 | 103 | pub struct MonitorHandle { | ------------------------ similarly named struct `RootMonitorHandle` defined here | help: a struct with a similar name exists | 32 | Borderless(Option), | ~~~~~~~~~~~~~~~~~ help: consider importing this struct | 1 + use crate::monitor::MonitorHandle; | error[E0412]: cannot find type `Window` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/window.rs:55:39 | 55 | pub(crate) window: platform_impl::Window, | ^^^^^^ not found in `platform_impl` error[E0412]: cannot find type `WindowId` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/window.rs:85:47 | 85 | pub struct WindowId(pub(crate) platform_impl::WindowId); | ^^^^^^^^ not found in `platform_impl` error[E0412]: cannot find type `PlatformSpecificWindowBuilderAttributes` in module `platform_impl` --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/window.rs:123:50 | 123 | pub(crate) platform_specific: platform_impl::PlatformSpecificWindowBuilderAttributes, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `platform_impl` error[E0282]: type annotations needed --> /home/dbuch/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.4/src/window.rs:537:17 | 537 | builder.build(event_loop) | ^^^^^ cannot infer type of the type parameter `T` declared on the method `build` | help: consider specifying the generic argument | 537 | builder.build::(event_loop) | +++++ Some errors have detailed explanations: E0282, E0412, E0432, E0433. For more information about an error, try `rustc --explain E0282`. error: could not compile `winit` (lib) due to 23 previous errors ~/dev/rust/egui ❯ [add-missing-resizedirection ≡] ``` --- crates/egui-winit/src/lib.rs | 1 + crates/egui/src/viewport.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 3da3781e093..607afdbac2b 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -1249,6 +1249,7 @@ fn process_viewport_command( if let Err(err) = window.drag_resize_window(match direction { egui::viewport::ResizeDirection::North => ResizeDirection::North, egui::viewport::ResizeDirection::South => ResizeDirection::South, + egui::viewport::ResizeDirection::East => ResizeDirection::East, egui::viewport::ResizeDirection::West => ResizeDirection::West, egui::viewport::ResizeDirection::NorthEast => ResizeDirection::NorthEast, egui::viewport::ResizeDirection::SouthEast => ResizeDirection::SouthEast, diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index df1b30a9287..bba152943e0 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -808,6 +808,7 @@ pub enum CursorGrab { pub enum ResizeDirection { North, South, + East, West, NorthEast, SouthEast, From 239ade9a59692f23a6b1d779a9c2631cf774896a Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 29 Dec 2023 17:20:52 +0100 Subject: [PATCH 36/45] Fix crash due to assertion during image loading from http (#3750) * Closes https://github.com/emilk/egui/issues/3747 --- crates/egui_extras/src/loaders/ehttp_loader.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/egui_extras/src/loaders/ehttp_loader.rs b/crates/egui_extras/src/loaders/ehttp_loader.rs index ad7ce49d22d..216dba703b7 100644 --- a/crates/egui_extras/src/loaders/ehttp_loader.rs +++ b/crates/egui_extras/src/loaders/ehttp_loader.rs @@ -95,8 +95,7 @@ impl BytesLoader for EhttpLoader { } }; log::trace!("finished loading {uri:?}"); - let prev = cache.lock().insert(uri, Poll::Ready(result)); - assert!(matches!(prev, Some(Poll::Pending))); + cache.lock().insert(uri, Poll::Ready(result)); ctx.request_repaint(); } }); From 4487f8ff9f1363ea090f109309745eed75af3e4e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 29 Dec 2023 17:22:39 +0100 Subject: [PATCH 37/45] Rename `TextBuffer::replace` to `replace_with` (#3751) This removes the name conflict with `str::replace`. * Closes https://github.com/emilk/egui/issues/3746 --- crates/egui/src/widgets/text_edit/builder.rs | 4 ++-- crates/egui/src/widgets/text_edit/text_buffer.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 8b80151975f..3d6ecb185f6 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -996,7 +996,7 @@ fn events( .lock() .undo(&(cursor_range.as_ccursor_range(), text.as_str().to_owned())) { - text.replace(undo_txt); + text.replace_with(undo_txt); Some(*undo_ccursor_range) } else { None @@ -1015,7 +1015,7 @@ fn events( .lock() .redo(&(cursor_range.as_ccursor_range(), text.as_str().to_owned())) { - text.replace(redo_txt); + text.replace_with(redo_txt); Some(*redo_ccursor_range) } else { None diff --git a/crates/egui/src/widgets/text_edit/text_buffer.rs b/crates/egui/src/widgets/text_edit/text_buffer.rs index a97d264a15c..58f5afd30fa 100644 --- a/crates/egui/src/widgets/text_edit/text_buffer.rs +++ b/crates/egui/src/widgets/text_edit/text_buffer.rs @@ -44,7 +44,7 @@ pub trait TextBuffer { } /// Replaces all contents of this string with `text` - fn replace(&mut self, text: &str) { + fn replace_with(&mut self, text: &str) { self.clear(); self.insert_text(text, 0); } @@ -91,7 +91,7 @@ impl TextBuffer for String { self.clear(); } - fn replace(&mut self, text: &str) { + fn replace_with(&mut self, text: &str) { *self = text.to_owned(); } @@ -121,7 +121,7 @@ impl<'a> TextBuffer for Cow<'a, str> { ::clear(self.to_mut()); } - fn replace(&mut self, text: &str) { + fn replace_with(&mut self, text: &str) { *self = Cow::Owned(text.to_owned()); } From 9951fe0d36fbc409ab0d45e70a846a65892199b0 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sat, 30 Dec 2023 11:23:21 +0100 Subject: [PATCH 38/45] Fix some clippy 1.75 lints (#3754) --- crates/ecolor/src/lib.rs | 2 -- crates/egui-wgpu/src/lib.rs | 2 +- crates/egui/src/id.rs | 5 +---- crates/egui_demo_app/src/main.rs | 1 + crates/egui_demo_lib/src/lib.rs | 2 +- crates/egui_extras/src/syntax_highlighting.rs | 2 ++ 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/ecolor/src/lib.rs b/crates/ecolor/src/lib.rs index 06bf55396b2..431e51d85fc 100644 --- a/crates/ecolor/src/lib.rs +++ b/crates/ecolor/src/lib.rs @@ -12,8 +12,6 @@ #[cfg(feature = "cint")] mod cint_impl; -#[cfg(feature = "cint")] -pub use cint_impl::*; mod color32; pub use color32::*; diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index 447f717b947..c2ce3e5b986 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -274,7 +274,7 @@ pub fn preferred_framebuffer_format( } formats - .get(0) + .first() .copied() .ok_or(WgpuError::NoSurfaceFormatsAvailable) } diff --git a/crates/egui/src/id.rs b/crates/egui/src/id.rs index fe42d20fbfc..098b1b10a25 100644 --- a/crates/egui/src/id.rs +++ b/crates/egui/src/id.rs @@ -43,10 +43,7 @@ impl Id { /// Generate a new [`Id`] by hashing some source (e.g. a string or integer). pub fn new(source: impl std::hash::Hash) -> Id { - use std::hash::{BuildHasher, Hasher}; - let mut hasher = epaint::ahash::RandomState::with_seeds(1, 2, 3, 4).build_hasher(); - source.hash(&mut hasher); - Id(hasher.finish()) + Id(epaint::ahash::RandomState::with_seeds(1, 2, 3, 4).hash_one(source)) } /// Generate a new [`Id`] by hashing the parent [`Id`] and the given argument. diff --git a/crates/egui_demo_app/src/main.rs b/crates/egui_demo_app/src/main.rs index d81cedddb3b..45b0ee067a4 100644 --- a/crates/egui_demo_app/src/main.rs +++ b/crates/egui_demo_app/src/main.rs @@ -1,6 +1,7 @@ //! Demo app for egui #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release +#![allow(clippy::never_loop)] // False positive // When compiling natively: fn main() -> Result<(), eframe::Error> { diff --git a/crates/egui_demo_lib/src/lib.rs b/crates/egui_demo_lib/src/lib.rs index f45b1f6db4b..cf31ee8d43b 100644 --- a/crates/egui_demo_lib/src/lib.rs +++ b/crates/egui_demo_lib/src/lib.rs @@ -18,7 +18,7 @@ mod demo; pub mod easy_mark; pub use color_test::ColorTest; -pub use demo::DemoWindows; +pub use demo::{DemoWindows, WidgetGallery}; /// View some Rust code with syntax highlighting and selection. pub(crate) fn rust_view_ui(ui: &mut egui::Ui, code: &str) { diff --git a/crates/egui_extras/src/syntax_highlighting.rs b/crates/egui_extras/src/syntax_highlighting.rs index 8708dcf9fbf..aced1ad5d8e 100644 --- a/crates/egui_extras/src/syntax_highlighting.rs +++ b/crates/egui_extras/src/syntax_highlighting.rs @@ -3,6 +3,8 @@ //! Turn on the `syntect` feature for great syntax highlighting of any language. //! Otherwise, a very simple fallback will be used, that works okish for C, C++, Rust, and Python. +#![allow(clippy::mem_forget)] // False positive from enum_map macro + use egui::text::LayoutJob; /// View some code with syntax highlighting and selection. From 12468438a30966b1d44e73fd9c4d33f63f42b868 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 31 Dec 2023 10:26:02 +0100 Subject: [PATCH 39/45] `eframe`: Fix building the `wasm32` docs for `docs.rs` (#3757) * Closes https://github.com/emilk/egui/issues/3756 Fix suggested by @morionelab --- crates/eframe/Cargo.toml | 1 + crates/eframe/src/lib.rs | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 3d7d8a28f1e..09b98abdf5b 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -21,6 +21,7 @@ include = [ [package.metadata.docs.rs] all-features = true +rustc-args = ["--cfg=web_sys_unstable_apis"] targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] [lib] diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index de226bc8e0a..d8b5bd1fdf9 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -9,6 +9,21 @@ //! In short, you implement [`App`] (especially [`App::update`]) and then //! call [`crate::run_native`] from your `main.rs`, and/or use `eframe::WebRunner` from your `lib.rs`. //! +//! ## Compiling for web +//! To get copy-paste working on web, you need to compile with +//! `export RUSTFLAGS=--cfg=web_sys_unstable_apis`. +//! +//! You need to install the `wasm32` target with `rustup target add wasm32-unknown-unknown`. +//! +//! Build the `.wasm` using `cargo build --target wasm32-unknown-unknown` +//! and then use [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) to generate the JavaScript glue code. +//! +//! See the [`eframe_template` repository](https://github.com/emilk/eframe_template/) for more. +//! +//! ## Simplified usage +//! If your app is only for native, and you don't need advanced features like state persistence, +//! then you can use the simpler function [`run_simple_native`]. +//! //! ## Usage, native: //! ``` no_run //! use eframe::egui; @@ -114,10 +129,6 @@ //! } //! ``` //! -//! ## Simplified usage -//! If your app is only for native, and you don't need advanced features like state persistence, -//! then you can use the simpler function [`run_simple_native`]. -//! //! ## Feature flags #![cfg_attr(feature = "document-features", doc = document_features::document_features!())] //! From 5cf8a085e3555ac92a9ca22a435a53c292ce237b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 31 Dec 2023 10:27:42 +0100 Subject: [PATCH 40/45] Document that you need to install additional fonts for asian scripts --- crates/egui/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 377860057fc..b61c313c900 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -333,6 +333,10 @@ //! }); // the temporary settings are reverted here //! # }); //! ``` +//! +//! ## Installing additional fonts +//! The default egui fonts only support latin and cryllic characters, and some emojis. +//! To use egui with e.g. asian characters you need to install your own font (`.ttf` or `.otf`) using [`Context::set_fonts`]. #![allow(clippy::float_cmp)] #![allow(clippy::manual_range_contains)] From e2c1d9f90d360b8ba91d9c0388df3f9828490d86 Mon Sep 17 00:00:00 2001 From: Daniel Buch Hansen Date: Tue, 2 Jan 2024 07:27:52 +0100 Subject: [PATCH 41/45] Bump `winit` to `0.29.7` (#3753) Compiled changelog below: - On X11, fix Xft.dpi reload during runtime. - On X11, fix window minimize. - On Web, fix context menu not being disabled by with_prevent_default(true). - On Wayland, fix WindowEvent::Destroyed not being delivered after destroying window. - Fix EventLoopExtRunOnDemand::run_on_demand not working for consequent invocation - On macOS, remove spurious error logging when handling Fn. - On X11, fix an issue where floating point data from the server is - misinterpreted during a drag and drop operation. - On X11, fix a bug where focusing the window would panic. - On macOS, fix refresh_rate_millihertz. - On Wayland, disable Client Side Decorations when wl_subcompositor is not supported. - On X11, fix Xft.dpi detection from Xresources. - On Windows, fix consecutive calls to window.set_fullscreen(Some(Fullscreen::Borderless(None))) resulting in losing previous window state when eventually exiting fullscreen using window.set_fullscreen(None). - On Wayland, fix resize being sent on focus change. - On Windows, fix set_ime_cursor_area. --- Cargo.lock | 51 ++++++++++++++++------ crates/egui-wgpu/Cargo.toml | 4 +- deny.toml | 84 +++++++++++++++++++------------------ 3 files changed, 85 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58a98024f22..5bf86d724ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,7 +211,7 @@ dependencies = [ "parking_lot", "thiserror", "winapi", - "x11rb", + "x11rb 0.12.0", ] [[package]] @@ -1707,6 +1707,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -3269,9 +3279,9 @@ dependencies = [ [[package]] name = "sctk-adwaita" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729a30a469de249c6effc17ec8d039b0aa29b3af79b819b7f51cb6ab8046a90" +checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" dependencies = [ "ab_glyph", "log", @@ -4547,9 +4557,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winit" -version = "0.29.4" +version = "0.29.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25d662bb83b511acd839534bb2d88521b0bbc81440969cb077d23c4db9e62c7" +checksum = "7fd430cd4560ee9c48885a4ef473b609a56796e37b1e18222abee146143f7457" dependencies = [ "ahash", "android-activity", @@ -4590,7 +4600,7 @@ dependencies = [ "web-time", "windows-sys 0.48.0", "x11-dl", - "x11rb", + "x11rb 0.13.0", "xkbcommon-dl", ] @@ -4620,15 +4630,26 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" dependencies = [ - "as-raw-xcb-connection", - "gethostname", - "libc", - "libloading 0.7.4", + "gethostname 0.3.0", "nix", - "once_cell", "winapi", "winapi-wsapoll", - "x11rb-protocol", + "x11rb-protocol 0.12.0", +] + +[[package]] +name = "x11rb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +dependencies = [ + "as-raw-xcb-connection", + "gethostname 0.4.3", + "libc", + "libloading 0.8.0", + "once_cell", + "rustix 0.38.21", + "x11rb-protocol 0.13.0", ] [[package]] @@ -4640,6 +4661,12 @@ dependencies = [ "nix", ] +[[package]] +name = "x11rb-protocol" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" + [[package]] name = "xcursor" version = "0.3.4" diff --git a/crates/egui-wgpu/Cargo.toml b/crates/egui-wgpu/Cargo.toml index 546c1b32987..fc11b5792a1 100644 --- a/crates/egui-wgpu/Cargo.toml +++ b/crates/egui-wgpu/Cargo.toml @@ -51,7 +51,9 @@ wgpu.workspace = true ## Enable this when generating docs. document-features = { version = "0.2", optional = true } -winit = { version = "0.29.4", default-features = false, optional = true, features = ["rwh_05"] } +winit = { version = "0.29.4", default-features = false, optional = true, features = [ + "rwh_05", +] } # Native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/deny.toml b/deny.toml index 633e8bf00ac..9fc18cbc359 100644 --- a/deny.toml +++ b/deny.toml @@ -3,17 +3,17 @@ # Note: running just `cargo deny check` without a `--target` can result in # false positives due to https://github.com/EmbarkStudios/cargo-deny/issues/324 targets = [ - { triple = "aarch64-apple-darwin" }, - { triple = "i686-pc-windows-gnu" }, - { triple = "i686-pc-windows-msvc" }, - { triple = "i686-unknown-linux-gnu" }, - { triple = "wasm32-unknown-unknown" }, - { triple = "x86_64-apple-darwin" }, - { triple = "x86_64-pc-windows-gnu" }, - { triple = "x86_64-pc-windows-msvc" }, - { triple = "x86_64-unknown-linux-gnu" }, - { triple = "x86_64-unknown-linux-musl" }, - { triple = "x86_64-unknown-redox" }, + { triple = "aarch64-apple-darwin" }, + { triple = "i686-pc-windows-gnu" }, + { triple = "i686-pc-windows-msvc" }, + { triple = "i686-unknown-linux-gnu" }, + { triple = "wasm32-unknown-unknown" }, + { triple = "x86_64-apple-darwin" }, + { triple = "x86_64-pc-windows-gnu" }, + { triple = "x86_64-pc-windows-msvc" }, + { triple = "x86_64-unknown-linux-gnu" }, + { triple = "x86_64-unknown-linux-musl" }, + { triple = "x86_64-unknown-redox" }, ] [advisories] @@ -21,33 +21,35 @@ vulnerability = "deny" unmaintained = "warn" yanked = "deny" ignore = [ - "RUSTSEC-2020-0071", # https://rustsec.org/advisories/RUSTSEC-2020-0071 - chrono/time: Potential segfault in the time crate + "RUSTSEC-2020-0071", # https://rustsec.org/advisories/RUSTSEC-2020-0071 - chrono/time: Potential segfault in the time crate ] [bans] multiple-versions = "deny" wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed deny = [ - { name = "cmake" }, # Lord no - { name = "openssl-sys" }, # prefer rustls - { name = "openssl" }, # prefer rustls + { name = "cmake" }, # Lord no + { name = "openssl-sys" }, # prefer rustls + { name = "openssl" }, # prefer rustls ] skip = [ - { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … - { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 - { name = "memoffset" }, # tiny dependency - { name = "quick-xml" }, # old version via wayland-scanner - { name = "redox_syscall" }, # old version via directories-next - { name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu. - { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' - { name = "windows" }, # old version via accesskit_windows + { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … + { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 + { name = "memoffset" }, # tiny dependency + { name = "quick-xml" }, # old version via wayland-scanner + { name = "redox_syscall" }, # old version via directories-next + { name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu. + { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' + { name = "windows" }, # old version via accesskit_windows + { name = "x11rb" }, # old version via arboard + { name = "x11rb-protocol" }, # old version via arboard ] skip-tree = [ - { name = "criterion" }, # dev-dependency - { name = "foreign-types" }, # small crate. Old version via cocoa and core-graphics (winit). - { name = "objc2" }, # old version via accesskit_macos - { name = "rfd" }, # example dependency + { name = "criterion" }, # dev-dependency + { name = "foreign-types" }, # small crate. Old version via cocoa and core-graphics (winit). + { name = "objc2" }, # old version via accesskit_macos + { name = "rfd" }, # example dependency ] @@ -57,20 +59,20 @@ allow-osi-fsf-free = "neither" confidence-threshold = 0.92 # We want really high confidence when inferring licenses from text copyleft = "deny" allow = [ - "Apache-2.0 WITH LLVM-exception", # https://spdx.org/licenses/LLVM-exception.html - "Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0) - "BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd) - "BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised) - "BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained - "CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/ - "ISC", # https://tldrlegal.com/license/-isc-license - "LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 - no official SPDX, see https://github.com/emilk/egui/issues/2321 - "MIT", # https://tldrlegal.com/license/mit-license - "MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11 - "OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html - "OpenSSL", # https://www.openssl.org/source/license.html - "Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html - "Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib) + "Apache-2.0 WITH LLVM-exception", # https://spdx.org/licenses/LLVM-exception.html + "Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0) + "BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd) + "BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised) + "BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained + "CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/ + "ISC", # https://tldrlegal.com/license/-isc-license + "LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 - no official SPDX, see https://github.com/emilk/egui/issues/2321 + "MIT", # https://tldrlegal.com/license/mit-license + "MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11 + "OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html + "OpenSSL", # https://www.openssl.org/source/license.html + "Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html + "Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib) ] [[licenses.clarify]] From 34c341444f45686f5ae115884bb6232f15a2e1b5 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 3 Jan 2024 09:42:56 +0100 Subject: [PATCH 42/45] Release `egui_plot` 0.24.2 (#3763) ## Changelog * Fix plot auto-bounds default [#3722](https://github.com/emilk/egui/pull/3722) (thanks [@abey79](https://github.com/abey79)!) --------- Co-authored-by: Antoine Beyeler <49431240+abey79@users.noreply.github.com> --- crates/egui_plot/CHANGELOG.md | 4 ++++ crates/egui_plot/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/egui_plot/CHANGELOG.md b/crates/egui_plot/CHANGELOG.md index c476a55ac6f..dcbf254e212 100644 --- a/crates/egui_plot/CHANGELOG.md +++ b/crates/egui_plot/CHANGELOG.md @@ -5,6 +5,10 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.24.1 - 2024-12-03 +* Fix plot auto-bounds default [#3722](https://github.com/emilk/egui/pull/3722) (thanks [@abey79](https://github.com/abey79)!) + + ## 0.24.0 - 2023-11-23 * Add `emath::Vec2b`, replacing `egui_plot::AxisBools` [#3543](https://github.com/emilk/egui/pull/3543) * Add `auto_bounds/set_auto_bounds` to `PlotUi` [#3587](https://github.com/emilk/egui/pull/3587) [#3586](https://github.com/emilk/egui/pull/3586) (thanks [@abey79](https://github.com/abey79)!) diff --git a/crates/egui_plot/src/lib.rs b/crates/egui_plot/src/lib.rs index 41c5ec63482..bef977d81a0 100644 --- a/crates/egui_plot/src/lib.rs +++ b/crates/egui_plot/src/lib.rs @@ -781,7 +781,7 @@ impl Plot { max: pos + size, }; // Next we want to create this layout. - // Incides are only examples. + // Indices are only examples. // // left right // +---+---------x----------+ + From f25e4171b9972c3c55ecf8ea82a08529c51b96b9 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 4 Jan 2024 10:29:52 +0100 Subject: [PATCH 43/45] Fix: don't open context menu on drag (#3767) This fixes a bug where the context menu would open when dragging with the secondary mouse button. Now the context menu requires a click to open. This is important for things like plots, where right-click and right-drag means different things. --- crates/egui/src/menu.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index bd7305ed80e..03c4ebce6c2 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -367,20 +367,18 @@ impl MenuRoot { let response = response.interact(Sense::click()); response.ctx.input(|input| { let pointer = &input.pointer; - if pointer.any_pressed() { - if let Some(pos) = pointer.interact_pos() { - let mut destroy = false; - let mut in_old_menu = false; - if let Some(root) = root { - in_old_menu = root.menu_state.read().area_contains(pos); - destroy = root.id == response.id; - } - if !in_old_menu { - if response.hovered() && pointer.secondary_down() { - return MenuResponse::Create(pos, id); - } else if (response.hovered() && pointer.primary_down()) || destroy { - return MenuResponse::Close; - } + if let Some(pos) = pointer.interact_pos() { + let mut in_old_menu = false; + let mut destroy = false; + if let Some(root) = root { + in_old_menu = root.menu_state.read().area_contains(pos); + destroy = !in_old_menu && pointer.any_pressed() && root.id == response.id; + } + if !in_old_menu { + if response.hovered() && response.secondary_clicked() { + return MenuResponse::Create(pos, id); + } else if (response.hovered() && pointer.primary_down()) || destroy { + return MenuResponse::Close; } } } From 1efa66014981b39f0357b8a49599e7266a96ad79 Mon Sep 17 00:00:00 2001 From: Frank Lee <603128423@qq.com> Date: Thu, 4 Jan 2024 22:47:49 +0800 Subject: [PATCH 44/45] Fix IME input of `CompositionEnd` without a `CompositionStart` (#3768) * Closes https://github.com/emilk/egui/issues/3766 Add support for type in CompositionEnd only characters without trigger CompositionStart first. This usually works with no-latin character input. --------- Co-authored-by: Emil Ernerfeldt --- crates/egui/src/widgets/text_edit/builder.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 3d6ecb185f6..1a2243a92e7 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -1050,7 +1050,8 @@ fn events( } Event::CompositionEnd(prediction) => { - if prediction != "\n" && prediction != "\r" && state.has_ime { + // CompositionEnd only characters may be typed into TextEdit without trigger CompositionStart first, so do not check `state.has_ime = true` in the following statement. + if prediction != "\n" && prediction != "\r" { state.has_ime = false; let mut ccursor = delete_selected(text, &cursor_range); if !prediction.is_empty() { From 9faf4b44ff12af00c26763667fdf41bf64a7c060 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 5 Jan 2024 10:53:14 +0100 Subject: [PATCH 45/45] Ignore extra SHIFT and ALT when matching modifiers (#3769) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Closes https://github.com/emilk/egui/issues/3626 Basically, egui now ignores extra SHIFT and ALT pressed when matching keyboard shortcuts. This is because SHIFT and ALT are often requires to produce some logical keys. For instance, typing `+` on an English keyboard requires pressing `SHIFT =`, so the keyboard shortcut looking for `CTRL +` should ignore the SHIFT key. @abey79 You reported problem using `Cmd +` and `Cmd -` to zoom - does this fix it for you? You can run with `RUST_LOG=egui_winit=trace cargo run` to see a printout of how winit reports the logical and physical keys, and how egui interprets them. Weirdly, on Mac winit reports `SHIFT =` as `+`, but `CMD SHIFT =` as `=` (on an English keyboard) so things are… difficult. --- crates/egui-winit/src/lib.rs | 14 ++- crates/egui/src/data/input.rs | 110 ++++++++++++++++--- crates/egui/src/gui_zoom.rs | 21 +++- crates/egui/src/input_state.rs | 21 +++- crates/egui/src/widgets/text_edit/builder.rs | 7 +- 5 files changed, 144 insertions(+), 29 deletions(-) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 607afdbac2b..c7123648b1b 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -689,6 +689,15 @@ impl State { let logical_key = key_from_winit_key(logical_key); + // Helpful logging to enable when adding new key support + log::trace!( + "logical {:?} -> {:?}, physical {:?} -> {:?}", + event.logical_key, + logical_key, + event.physical_key, + physical_key + ); + if let Some(logical_key) = logical_key { if pressed { if is_cut_command(self.egui_input.modifiers, logical_key) { @@ -1064,9 +1073,8 @@ fn key_from_key_code(key: winit::keyboard::KeyCode) -> Option { KeyCode::Minus | KeyCode::NumpadSubtract => Key::Minus, - // Using Mac the key with the Plus sign on it is reported as the Equals key - // (with both English and Swedish keyboard). - KeyCode::Equal | KeyCode::NumpadAdd => Key::PlusEquals, + KeyCode::NumpadAdd => Key::Plus, + KeyCode::Equal => Key::Equals, KeyCode::Digit0 | KeyCode::Numpad0 => Key::Num0, KeyCode::Digit1 | KeyCode::Numpad1 => Key::Num1, diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index 71b77d97020..451e7f4e6da 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -655,13 +655,68 @@ impl Modifiers { !self.alt && !self.shift && self.command } + /// Checks that the `ctrl/cmd` matches, and that the `shift/alt` of the argument is a subset + /// of the pressed ksey (`self`). + /// + /// This means that if the pattern has not set `shift`, then `self` can have `shift` set or not. + /// + /// The reason is that many logical keys require `shift` or `alt` on some keyboard layouts. + /// For instance, in order to press `+` on an English keyboard, you need to press `shift` and `=`, + /// but a Swedish keyboard has dedicated `+` key. + /// So if you want to make a [`KeyboardShortcut`] looking for `Cmd` + `+`, it makes sense + /// to ignore the shift key. + /// Similarly, the `Alt` key is sometimes used to type special characters. + /// + /// However, if the pattern (the argument) explicitly requires the `shift` or `alt` keys + /// to be pressed, then they must be pressed. + /// + /// # Example: + /// ``` + /// # use egui::Modifiers; + /// # let pressed_modifiers = Modifiers::default(); + /// if pressed_modifiers.matches(Modifiers::ALT | Modifiers::SHIFT) { + /// // Alt and Shift are pressed, and nothing else + /// } + /// ``` + /// + /// ## Behavior: + /// ``` + /// # use egui::Modifiers; + /// assert!(Modifiers::CTRL.matches_logically(Modifiers::CTRL)); + /// assert!(!Modifiers::CTRL.matches_logically(Modifiers::CTRL | Modifiers::SHIFT)); + /// assert!((Modifiers::CTRL | Modifiers::SHIFT).matches_logically(Modifiers::CTRL)); + /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_logically(Modifiers::CTRL)); + /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_logically(Modifiers::COMMAND)); + /// assert!((Modifiers::MAC_CMD | Modifiers::COMMAND).matches_logically(Modifiers::COMMAND)); + /// assert!(!Modifiers::COMMAND.matches_logically(Modifiers::MAC_CMD)); + /// ``` + pub fn matches_logically(&self, pattern: Modifiers) -> bool { + if pattern.alt && !self.alt { + return false; + } + if pattern.shift && !self.shift { + return false; + } + + self.cmd_ctrl_matches(pattern) + } + /// Check for equality but with proper handling of [`Self::command`]. /// + /// `self` here are the currently pressed modifiers, + /// and the argument the pattern we are testing for. + /// + /// Note that this will require the `shift` and `alt` keys match, even though + /// these modifiers are sometimes required to produce some logical keys. + /// For instance, to press `+` on an English keyboard, you need to press `shift` and `=`, + /// but on a Swedish keyboard you can press the dedicated `+` key. + /// Therefore, you often want to use [`Self::matches_logically`] instead. + /// /// # Example: /// ``` /// # use egui::Modifiers; - /// # let current_modifiers = Modifiers::default(); - /// if current_modifiers.matches(Modifiers::ALT | Modifiers::SHIFT) { + /// # let pressed_modifiers = Modifiers::default(); + /// if pressed_modifiers.matches(Modifiers::ALT | Modifiers::SHIFT) { /// // Alt and Shift are pressed, and nothing else /// } /// ``` @@ -677,12 +732,28 @@ impl Modifiers { /// assert!((Modifiers::MAC_CMD | Modifiers::COMMAND).matches(Modifiers::COMMAND)); /// assert!(!Modifiers::COMMAND.matches(Modifiers::MAC_CMD)); /// ``` - pub fn matches(&self, pattern: Modifiers) -> bool { + pub fn matches_exact(&self, pattern: Modifiers) -> bool { // alt and shift must always match the pattern: if pattern.alt != self.alt || pattern.shift != self.shift { return false; } + self.cmd_ctrl_matches(pattern) + } + + #[deprecated = "Renamed `matches_exact`, but maybe you want to use `matches_logically` instead"] + pub fn matches(&self, pattern: Modifiers) -> bool { + self.matches_exact(pattern) + } + + /// Checks only cmd/ctrl, not alt/shift. + /// + /// `self` here are the currently pressed modifiers, + /// and the argument the pattern we are testing for. + /// + /// This takes care to properly handle the difference between + /// [`Self::ctrl`], [`Self::command`] and [`Self::mac_cmd`]. + pub fn cmd_ctrl_matches(&self, pattern: Modifiers) -> bool { if pattern.mac_cmd { // Mac-specific match: if !self.mac_cmd { @@ -897,8 +968,11 @@ pub enum Key { /// `.` Period, - /// The for the Plus/Equals key. - PlusEquals, + /// `+` + Plus, + + /// `=` + Equals, /// `;` Semicolon, @@ -1017,7 +1091,8 @@ impl Key { Self::Comma, Self::Minus, Self::Period, - Self::PlusEquals, + Self::Plus, + Self::Equals, Self::Semicolon, // Digits: Self::Num0, @@ -1117,7 +1192,8 @@ impl Key { "Comma" | "," => Self::Comma, "Minus" | "-" | "−" => Self::Minus, "Period" | "." => Self::Period, - "Plus" | "+" | "Equals" | "=" => Self::PlusEquals, + "Plus" | "+" => Self::Plus, + "Equals" | "=" => Self::Equals, "Semicolon" | ";" => Self::Semicolon, "0" => Self::Num0, @@ -1194,7 +1270,8 @@ impl Key { Key::ArrowRight => "⏵", Key::ArrowUp => "⏶", Key::Minus => crate::MINUS_CHAR_STR, - Key::PlusEquals => "+", + Key::Plus => "+", + Key::Equals => "=", _ => self.name(), } } @@ -1228,7 +1305,8 @@ impl Key { Key::Comma => "Comma", Key::Minus => "Minus", Key::Period => "Period", - Key::PlusEquals => "Plus", + Key::Plus => "Plus", + Key::Equals => "Equals", Key::Semicolon => "Semicolon", Key::Num0 => "0", @@ -1327,12 +1405,16 @@ fn test_key_from_name() { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct KeyboardShortcut { pub modifiers: Modifiers, - pub key: Key, + + pub logical_key: Key, } impl KeyboardShortcut { - pub const fn new(modifiers: Modifiers, key: Key) -> Self { - Self { modifiers, key } + pub const fn new(modifiers: Modifiers, logical_key: Key) -> Self { + Self { + modifiers, + logical_key, + } } pub fn format(&self, names: &ModifierNames<'_>, is_mac: bool) -> String { @@ -1341,9 +1423,9 @@ impl KeyboardShortcut { s += names.concat; } if names.is_short { - s += self.key.symbol_or_name(); + s += self.logical_key.symbol_or_name(); } else { - s += self.key.name(); + s += self.logical_key.name(); } s } diff --git a/crates/egui/src/gui_zoom.rs b/crates/egui/src/gui_zoom.rs index fb4f9bd11ed..c4b5deb5ed6 100644 --- a/crates/egui/src/gui_zoom.rs +++ b/crates/egui/src/gui_zoom.rs @@ -6,9 +6,22 @@ use crate::*; pub mod kb_shortcuts { use super::*; - pub const ZOOM_IN: KeyboardShortcut = - KeyboardShortcut::new(Modifiers::COMMAND, Key::PlusEquals); + /// Primary keyboard shortcut for zooming in (`Cmd` + `+`). + pub const ZOOM_IN: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Plus); + + /// Secondary keyboard shortcut for zooming in (`Cmd` + `=`). + /// + /// On an English keyboard `+` is `Shift` + `=`, + /// but it is annoying to have to press shift. + /// So most browsers also allow `Cmd` + `=` for zooming in. + /// We do the same. + pub const ZOOM_IN_SECONDARY: KeyboardShortcut = + KeyboardShortcut::new(Modifiers::COMMAND, Key::Equals); + + /// Keyboard shortcut for zooming in (`Cmd` + `-`). pub const ZOOM_OUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Minus); + + /// Keyboard shortcut for resetting zoom in (`Cmd` + `0`). pub const ZOOM_RESET: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Num0); } @@ -21,7 +34,9 @@ pub(crate) fn zoom_with_keyboard(ctx: &Context) { if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_RESET)) { ctx.set_zoom_factor(1.0); } else { - if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_IN)) { + if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_IN)) + || ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_IN_SECONDARY)) + { zoom_in(ctx); } if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_OUT)) { diff --git a/crates/egui/src/input_state.rs b/crates/egui/src/input_state.rs index 693f6c56ee2..b95303ec819 100644 --- a/crates/egui/src/input_state.rs +++ b/crates/egui/src/input_state.rs @@ -289,7 +289,13 @@ impl InputState { /// Count presses of a key. If non-zero, the presses are consumed, so that this will only return non-zero once. /// /// Includes key-repeat events. - pub fn count_and_consume_key(&mut self, modifiers: Modifiers, key: Key) -> usize { + /// + /// This uses [`Modifiers::matches_logically`] to match modifiers. + /// This means that e.g. the shortcut `Ctrl` + `Key::Plus` will be matched + /// as long as `Ctrl` and `Plus` are pressed, ignoring if + /// `Shift` or `Alt` are also pressed (because those modifiers might + /// be required to produce the logical `Key::Plus`). + pub fn count_and_consume_key(&mut self, modifiers: Modifiers, logical_key: Key) -> usize { let mut count = 0usize; self.events.retain(|event| { @@ -300,7 +306,7 @@ impl InputState { modifiers: ev_mods, pressed: true, .. - } if *ev_key == key && ev_mods.matches(modifiers) + } if *ev_key == logical_key && ev_mods.matches_logically(modifiers) ); count += is_match as usize; @@ -314,8 +320,8 @@ impl InputState { /// Check for a key press. If found, `true` is returned and the key pressed is consumed, so that this will only return `true` once. /// /// Includes key-repeat events. - pub fn consume_key(&mut self, modifiers: Modifiers, key: Key) -> bool { - self.count_and_consume_key(modifiers, key) > 0 + pub fn consume_key(&mut self, modifiers: Modifiers, logical_key: Key) -> bool { + self.count_and_consume_key(modifiers, logical_key) > 0 } /// Check if the given shortcut has been pressed. @@ -324,8 +330,11 @@ impl InputState { /// /// Includes key-repeat events. pub fn consume_shortcut(&mut self, shortcut: &KeyboardShortcut) -> bool { - let KeyboardShortcut { modifiers, key } = *shortcut; - self.consume_key(modifiers, key) + let KeyboardShortcut { + modifiers, + logical_key, + } = *shortcut; + self.consume_key(modifiers, logical_key) } /// Was the given key pressed this frame? diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 1a2243a92e7..ded1f335d57 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -990,7 +990,7 @@ fn events( pressed: true, modifiers, .. - } if modifiers.matches(Modifiers::COMMAND) => { + } if modifiers.matches_logically(Modifiers::COMMAND) => { if let Some((undo_ccursor_range, undo_txt)) = state .undoer .lock() @@ -1007,8 +1007,9 @@ fn events( pressed: true, modifiers, .. - } if (modifiers.matches(Modifiers::COMMAND) && *key == Key::Y) - || (modifiers.matches(Modifiers::SHIFT | Modifiers::COMMAND) && *key == Key::Z) => + } if (modifiers.matches_logically(Modifiers::COMMAND) && *key == Key::Y) + || (modifiers.matches_logically(Modifiers::SHIFT | Modifiers::COMMAND) + && *key == Key::Z) => { if let Some((redo_ccursor_range, redo_txt)) = state .undoer