Compare commits

..

No commits in common. "4bb7970234691d9849cc6c645c8026f0c2769333" and "eb1756df3fc4e53b82947c4333f68259da6b9c09" have entirely different histories.

7 changed files with 5 additions and 143 deletions

View File

@ -392,7 +392,7 @@ impl WinitApp for WgpuWinitApp<'_> {
self.initialized_all_windows(event_loop);
if let Some(running) = &mut self.running {
running.run_ui_and_paint(event_loop, window_id)
running.run_ui_and_paint(window_id)
} else {
Ok(EventResult::Wait)
}
@ -540,11 +540,7 @@ impl WgpuWinitRunning<'_> {
}
/// This is called both for the root viewport, and all deferred viewports
fn run_ui_and_paint(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
) -> Result<EventResult> {
fn run_ui_and_paint(&mut self, window_id: WindowId) -> Result<EventResult> {
profiling::function_scope!();
let Some(viewport_id) = self
@ -679,9 +675,6 @@ impl WgpuWinitRunning<'_> {
};
egui_winit.handle_platform_output(window, platform_output);
// Custom cursor images can only be created from an `ActiveEventLoop`, so they're applied
// here rather than inside `handle_platform_output`.
egui_winit.apply_pending_cursor_image(event_loop, window);
let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point);

View File

@ -321,7 +321,6 @@ impl AppRunner {
let egui::PlatformOutput {
commands,
cursor_icon,
cursor_image: _, // TODO: web could express this as a CSS `cursor: url(...)`
events: _, // already handled
mutable_text_under_cursor: _, // TODO(#4569): https://github.com/emilk/egui/issues/4569
ime,

View File

@ -85,15 +85,6 @@ pub struct State {
any_pointer_button_down: bool,
current_cursor_icon: Option<egui::CursorIcon>,
/// Custom cursor image requested by the app this frame, not yet applied.
///
/// Applying it needs an `ActiveEventLoop` (that's where winit creates custom cursors), which
/// `handle_platform_output` doesn't have — so we stash it and let the integration call
/// [`State::apply_pending_cursor_image`] from somewhere that does.
pending_cursor_image: Option<Option<egui::CursorImage>>,
/// `id` of the custom cursor currently set on the window, so we only rebuild it on change.
current_cursor_image_id: Option<u64>,
clipboard: clipboard::Clipboard,
/// If `true`, mouse inputs will be treated as touches.
@ -150,8 +141,6 @@ impl State {
pointer_pos_in_points: None,
any_pointer_button_down: false,
current_cursor_icon: None,
pending_cursor_image: None,
current_cursor_image_id: None,
clipboard: clipboard::Clipboard::new(
display_target.display_handle().ok().map(|h| h.as_raw()),
@ -895,11 +884,9 @@ impl State {
let contents = contents.replace("\r\n", "\n");
if !contents.is_empty() {
self.egui_input.events.push(egui::Event::Paste(contents));
return;
}
}
// Clipboard is empty or unavailable — fall through to push a
// Key event so the application can handle paste from its own cache.
return;
}
}
@ -954,7 +941,6 @@ impl State {
let egui::PlatformOutput {
commands,
cursor_icon,
cursor_image,
events: _, // handled elsewhere
mutable_text_under_cursor: _, // only used in eframe web
ime,
@ -978,18 +964,7 @@ impl State {
}
}
// A custom cursor image, when set, *is* the cursor — don't also apply the icon, or we'd
// fight with it every frame. Stash it for `apply_pending_cursor_image`, which runs where
// an `ActiveEventLoop` is in scope.
if cursor_image.is_some() {
self.pending_cursor_image = Some(cursor_image);
} else {
if self.current_cursor_image_id.is_some() {
// Custom cursor was just cleared — go back to a normal icon.
self.pending_cursor_image = Some(None);
}
self.set_cursor_icon(window, cursor_icon);
}
self.set_cursor_icon(window, cursor_icon);
// When IME is disabled as a workaround for buggy systems, don't enable IME at all.
// This ensures the system doesn't try to intercept keyboard input.
@ -1038,56 +1013,6 @@ impl State {
}
}
/// Apply the custom cursor image requested by the last [`Self::handle_platform_output`].
///
/// Must be called from somewhere with an `ActiveEventLoop` — winit only creates custom cursors
/// there. Cheap to call every frame: it's a no-op unless the image actually changed.
pub fn apply_pending_cursor_image(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
window: &Window,
) {
let Some(pending) = self.pending_cursor_image.take() else {
return;
};
match pending {
Some(image) => {
if self.current_cursor_image_id == Some(image.id) {
return; // Already set — rebuilding it every frame would be wasteful.
}
let source = winit::window::CustomCursor::from_rgba(
image.rgba.as_ref().clone(),
image.size.0,
image.size.1,
image.hotspot.0,
image.hotspot.1,
);
match source {
Ok(source) => {
let cursor = event_loop.create_custom_cursor(source);
window.set_cursor_visible(true);
window.set_cursor(winit::window::Cursor::Custom(cursor));
self.current_cursor_image_id = Some(image.id);
// Force the next icon change to be re-applied, since the window is no
// longer showing whatever icon `current_cursor_icon` claims.
self.current_cursor_icon = None;
}
Err(err) => {
log::warn!("Failed to build custom cursor: {err}");
self.current_cursor_image_id = None;
}
}
}
None => {
self.current_cursor_image_id = None;
self.current_cursor_icon = None; // Force the icon to be re-applied.
}
}
}
fn set_cursor_icon(&mut self, window: &Window, cursor_icon: egui::CursorIcon) {
if self.current_cursor_icon == Some(cursor_icon) {
// Prevent flickering near frame boundary when Windows OS tries to control cursor icon for window resizing.

View File

@ -515,13 +515,6 @@ impl SubMenu {
content(ui)
});
if let Some(_) = &popup_response {
// Keep the submenu alive so `from_id`'s staleness check doesn't close it.
// Without this, menus whose content has no further SubMenuButtons never update
// `last_visible_pass`, causing the parent to reset `open_item` after ~2 frames.
MenuState::mark_shown(ui.ctx(), id);
}
if let Some(popup_response) = &popup_response {
// If no child sub menu is open means we must be the deepest child sub menu.
let is_deepest_submenu = MenuState::is_deepest_open_sub_menu(ui.ctx(), id);

View File

@ -1514,15 +1514,6 @@ impl Context {
self.output_mut(|o| o.cursor_icon = cursor_icon);
}
/// Replace the mouse cursor with a custom image for this frame.
///
/// This becomes the real OS cursor, so it is composited by the window system and does not lag
/// behind the pointer the way a cursor painted into the egui scene does. Pass `None` to go
/// back to the icon set by [`Self::set_cursor_icon`].
pub fn set_cursor_image(&self, cursor_image: Option<crate::CursorImage>) {
self.output_mut(|o| o.cursor_image = cursor_image);
}
/// Add a command to [`PlatformOutput::commands`],
/// for the integration to execute at the end of the frame.
pub fn send_cmd(&self, cmd: crate::OutputCommand) {

View File

@ -66,34 +66,6 @@ impl FullOutput {
}
}
/// A custom mouse cursor image, handed to the windowing backend to become the real OS cursor.
///
/// Because the OS composites the cursor, this has none of the one-frame lag you get from painting
/// a cursor into the egui scene.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CursorImage {
/// Identifies this image. The backend recreates the OS cursor only when this changes, so the
/// pixels don't have to be compared (or even hashed) every frame.
pub id: u64,
/// Premultiplied-alpha RGBA8, `size.0 * size.1 * 4` bytes, row-major.
pub rgba: std::sync::Arc<Vec<u8>>,
/// Width and height in physical pixels.
pub size: (u16, u16),
/// The click point, in physical pixels from the image's top-left.
pub hotspot: (u16, u16),
}
impl PartialEq for CursorImage {
fn eq(&self, other: &Self) -> bool {
// `id` identifies the pixels; comparing the buffers themselves would be pointless work.
self.id == other.id && self.size == other.size && self.hotspot == other.hotspot
}
}
/// Information about text being edited.
///
/// Useful for IME.
@ -141,15 +113,6 @@ pub struct PlatformOutput {
/// Set the cursor to this icon.
pub cursor_icon: CursorIcon,
/// Replace the mouse cursor with a custom image.
///
/// Unlike drawing a cursor into the egui scene, this becomes the real OS cursor, so it is
/// composited by the window system and does not lag a frame behind the pointer.
///
/// Takes precedence over [`Self::cursor_icon`] when set. Backends that do not support custom
/// cursors ignore this and fall back to `cursor_icon`.
pub cursor_image: Option<CursorImage>,
/// Events that may be useful to e.g. a screen reader.
pub events: Vec<OutputEvent>,
@ -209,7 +172,6 @@ impl PlatformOutput {
let Self {
mut commands,
cursor_icon,
cursor_image,
mut events,
mutable_text_under_cursor,
ime,
@ -221,7 +183,6 @@ impl PlatformOutput {
self.commands.append(&mut commands);
self.cursor_icon = cursor_icon;
self.cursor_image = cursor_image;
self.events.append(&mut events);
self.mutable_text_under_cursor = mutable_text_under_cursor;
self.ime = ime.or(self.ime);

View File

@ -488,7 +488,7 @@ pub use self::{
Key, UserData,
input::*,
output::{
self, CursorIcon, CursorImage, FullOutput, OpenUrl, OutputCommand, PlatformOutput,
self, CursorIcon, FullOutput, OpenUrl, OutputCommand, PlatformOutput,
UserAttentionType, WidgetInfo,
},
},