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 <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
8503a85113
commit
8e5959d55d
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
[default.extend-words]
|
[default.extend-words]
|
||||||
nknown = "nknown" # part of @55nknown username
|
nknown = "nknown" # part of @55nknown username
|
||||||
Prefence = "Prefence" # typo in glutin_winit API
|
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
extend-exclude = ["web_demo/egui_demo_app.js"] # auto-generated
|
extend-exclude = ["web_demo/egui_demo_app.js"] # auto-generated
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -131,7 +131,7 @@ image = { version = "0.24", default-features = false, features = [
|
||||||
"png",
|
"png",
|
||||||
] } # Needed for app icon
|
] } # Needed for app icon
|
||||||
raw-window-handle.workspace = true
|
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:
|
# optional native:
|
||||||
directories-next = { version = "2", optional = true }
|
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.
|
# 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.
|
# this can be done at the same time we expose x11/wayland features of winit crate.
|
||||||
glutin = { version = "0.30", optional = true }
|
glutin = { version = "0.31", optional = true }
|
||||||
glutin-winit = { version = "0.3.0", optional = true }
|
glutin-winit = { version = "0.4", optional = true }
|
||||||
puffin = { workspace = true, optional = true }
|
puffin = { workspace = true, optional = true }
|
||||||
wgpu = { workspace = true, optional = true }
|
wgpu = { workspace = true, optional = true }
|
||||||
|
|
||||||
# mac:
|
# mac:
|
||||||
[target.'cfg(any(target_os = "macos"))'.dependencies]
|
[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"
|
objc = "0.2.7"
|
||||||
|
|
||||||
# windows:
|
# windows:
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,7 @@ pub struct NativeOptions {
|
||||||
///
|
///
|
||||||
/// This feature was introduced in <https://github.com/emilk/egui/pull/1889>.
|
/// This feature was introduced in <https://github.com/emilk/egui/pull/1889>.
|
||||||
///
|
///
|
||||||
/// 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.
|
/// When `false`, [`winit::event_loop::EventLoop::run`] is used.
|
||||||
pub run_and_return: bool,
|
pub run_and_return: bool,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -325,6 +325,11 @@ pub enum Error {
|
||||||
#[error("winit error: {0}")]
|
#[error("winit error: {0}")]
|
||||||
Winit(#[from] winit::error::OsError),
|
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`].
|
/// An error from [`glutin`] when using [`glow`].
|
||||||
#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
|
||||||
#[error("glutin error: {0}")]
|
#[error("glutin error: {0}")]
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ impl EpiIntegration {
|
||||||
&mut self,
|
&mut self,
|
||||||
window: &winit::window::Window,
|
window: &winit::window::Window,
|
||||||
egui_winit: &mut egui_winit::State,
|
egui_winit: &mut egui_winit::State,
|
||||||
event: &winit::event::WindowEvent<'_>,
|
event: &winit::event::WindowEvent,
|
||||||
) -> EventResponse {
|
) -> EventResponse {
|
||||||
crate::profile_function!(egui_winit::short_window_event_description(event));
|
crate::profile_function!(egui_winit::short_window_event_description(event));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant};
|
||||||
|
|
||||||
use glutin::{
|
use glutin::{
|
||||||
|
config::GlConfig,
|
||||||
|
context::NotCurrentGlContext,
|
||||||
display::GetGlDisplay,
|
display::GetGlDisplay,
|
||||||
prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext},
|
prelude::{GlDisplay, PossiblyCurrentGlContext},
|
||||||
surface::GlSurface,
|
surface::GlSurface,
|
||||||
};
|
};
|
||||||
use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _};
|
use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _};
|
||||||
|
|
@ -112,6 +121,8 @@ struct Viewport {
|
||||||
/// None for immediate viewports.
|
/// None for immediate viewports.
|
||||||
viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,
|
viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,
|
||||||
|
|
||||||
|
// These three live and die together.
|
||||||
|
// TODO(emilk): clump them together into one struct!
|
||||||
gl_surface: Option<glutin::surface::Surface<glutin::surface::WindowSurface>>,
|
gl_surface: Option<glutin::surface::Surface<glutin::surface::WindowSurface>>,
|
||||||
window: Option<Rc<Window>>,
|
window: Option<Rc<Window>>,
|
||||||
egui_winit: Option<egui_winit::State>,
|
egui_winit: Option<egui_winit::State>,
|
||||||
|
|
@ -160,13 +171,13 @@ impl GlowWinitApp {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Creates the window - must come before we create our glow context
|
// 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 {
|
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);
|
epi_integration::apply_window_settings(window, window_settings);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let gl = unsafe {
|
let gl = unsafe {
|
||||||
crate::profile_scope!("glow::Context::from_loader_function");
|
crate::profile_scope!("glow::Context::from_loader_function");
|
||||||
|
|
@ -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<UserEvent>,
|
||||||
|
window_id: WindowId,
|
||||||
|
) -> EventResult {
|
||||||
if let Some(running) = &mut self.running {
|
if let Some(running) = &mut self.running {
|
||||||
running.run_ui_and_paint(window_id)
|
running.run_ui_and_paint(event_loop, window_id)
|
||||||
} else {
|
} else {
|
||||||
EventResult::Wait
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
|
|
@ -401,29 +416,27 @@ impl WinitApp for GlowWinitApp {
|
||||||
fn on_event(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
event: &winit::event::Event<'_, UserEvent>,
|
event: &winit::event::Event<UserEvent>,
|
||||||
) -> Result<EventResult> {
|
) -> Result<EventResult> {
|
||||||
crate::profile_function!(winit_integration::short_event_description(event));
|
crate::profile_function!(winit_integration::short_event_description(event));
|
||||||
|
|
||||||
Ok(match event {
|
Ok(match event {
|
||||||
winit::event::Event::Resumed => {
|
winit::event::Event::Resumed => {
|
||||||
|
log::debug!("Event::Resumed");
|
||||||
|
|
||||||
let running = if let Some(running) = &mut self.running {
|
let running = if let Some(running) = &mut self.running {
|
||||||
// not the first resume event. create whatever you need.
|
// Not the first resume event. Create all outstanding windows.
|
||||||
running.glutin.borrow_mut().on_resume(event_loop)?;
|
running
|
||||||
|
.glutin
|
||||||
|
.borrow_mut()
|
||||||
|
.initialize_all_windows(event_loop);
|
||||||
running
|
running
|
||||||
} else {
|
} else {
|
||||||
// first resume event.
|
// First resume event. Created our root window etc.
|
||||||
// we can actually move this outside of event loop.
|
|
||||||
// and just run the on_resume fn of gl_window
|
|
||||||
self.init_run_state(event_loop)?
|
self.init_run_state(event_loop)?
|
||||||
};
|
};
|
||||||
let window_id = running
|
let window_id = running.glutin.borrow().window_from_viewport[&ViewportId::ROOT];
|
||||||
.glutin
|
EventResult::RepaintNow(window_id)
|
||||||
.borrow()
|
|
||||||
.window_from_viewport
|
|
||||||
.get(&ViewportId::ROOT)
|
|
||||||
.copied();
|
|
||||||
EventResult::RepaintNow(window_id.unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
winit::event::Event::Suspended => {
|
winit::event::Event::Suspended => {
|
||||||
|
|
@ -433,15 +446,6 @@ impl WinitApp for GlowWinitApp {
|
||||||
EventResult::Wait
|
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 } => {
|
winit::event::Event::WindowEvent { event, window_id } => {
|
||||||
if let Some(running) = &mut self.running {
|
if let Some(running) = &mut self.running {
|
||||||
running.on_window_event(*window_id, event)
|
running.on_window_event(*window_id, event)
|
||||||
|
|
@ -477,7 +481,11 @@ impl WinitApp for GlowWinitApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlowWinitRunning {
|
impl GlowWinitRunning {
|
||||||
fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult {
|
fn run_ui_and_paint(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
|
window_id: WindowId,
|
||||||
|
) -> EventResult {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
let Some(viewport_id) = self
|
let Some(viewport_id) = self
|
||||||
|
|
@ -666,7 +674,7 @@ impl GlowWinitRunning {
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
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() {
|
if integration.should_close() {
|
||||||
EventResult::Exit
|
EventResult::Exit
|
||||||
|
|
@ -678,7 +686,7 @@ impl GlowWinitRunning {
|
||||||
fn on_window_event(
|
fn on_window_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
window_id: WindowId,
|
window_id: WindowId,
|
||||||
event: &winit::event::WindowEvent<'_>,
|
event: &winit::event::WindowEvent,
|
||||||
) -> EventResult {
|
) -> EventResult {
|
||||||
crate::profile_function!(egui_winit::short_window_event_description(event));
|
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 => {
|
winit::event::WindowEvent::CloseRequested => {
|
||||||
if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() {
|
if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
|
@ -768,7 +769,11 @@ impl GlowWinitRunning {
|
||||||
{
|
{
|
||||||
event_response = self.integration.on_window_event(window, egui_winit, event);
|
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 {
|
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.
|
// 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()
|
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.
|
// 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(
|
.with_window_builder(Some(egui_winit::create_winit_window_builder(
|
||||||
egui_ctx,
|
egui_ctx,
|
||||||
event_loop,
|
event_loop,
|
||||||
|
|
@ -968,37 +973,29 @@ impl GlutinWindowContext {
|
||||||
focused_viewport: Some(ViewportId::ROOT),
|
focused_viewport: Some(ViewportId::ROOT),
|
||||||
};
|
};
|
||||||
|
|
||||||
slf.on_resume(event_loop)?;
|
slf.initialize_window(ViewportId::ROOT, event_loop)?;
|
||||||
|
|
||||||
Ok(slf)
|
Ok(slf)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This will be run after `new`. on android, it might be called multiple times over the course of the app's lifetime.
|
/// Create a surface, window, and winit integration for all viewports lacking any of that.
|
||||||
/// 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.
|
|
||||||
///
|
///
|
||||||
/// we presently assume that we will
|
/// Errors will be logged.
|
||||||
fn on_resume(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) -> Result<()> {
|
fn initialize_all_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
let viewports: Vec<ViewportId> = self
|
let viewports: Vec<ViewportId> = self.viewports.keys().copied().collect();
|
||||||
.viewports
|
|
||||||
.iter()
|
|
||||||
.filter(|(_, viewport)| viewport.gl_surface.is_none())
|
|
||||||
.map(|(id, _)| *id)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for viewport_id in viewports {
|
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)]
|
#[allow(unsafe_code)]
|
||||||
pub(crate) fn init_viewport(
|
pub(crate) fn initialize_window(
|
||||||
&mut self,
|
&mut self,
|
||||||
viewport_id: ViewportId,
|
viewport_id: ViewportId,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
|
|
@ -1013,12 +1010,16 @@ impl GlutinWindowContext {
|
||||||
let window = if let Some(window) = &mut viewport.window {
|
let window = if let Some(window) = &mut viewport.window {
|
||||||
window
|
window
|
||||||
} else {
|
} 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(
|
let window_builder = egui_winit::create_winit_window_builder(
|
||||||
&self.egui_ctx,
|
&self.egui_ctx,
|
||||||
event_loop,
|
event_loop,
|
||||||
viewport.builder.clone(),
|
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 =
|
let window =
|
||||||
glutin_winit::finalize_window(event_loop, window_builder, &self.gl_config)?;
|
glutin_winit::finalize_window(event_loop, window_builder, &self.gl_config)?;
|
||||||
egui_winit::apply_viewport_builder_to_window(
|
egui_winit::apply_viewport_builder_to_window(
|
||||||
|
|
@ -1031,7 +1032,20 @@ impl GlutinWindowContext {
|
||||||
viewport.window.insert(Rc::new(window))
|
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
|
// surface attributes
|
||||||
let (width_px, height_px): (u32, u32) = window.inner_size().into();
|
let (width_px, height_px): (u32, u32) = window.inner_size().into();
|
||||||
let width_px = std::num::NonZeroU32::new(width_px.at_least(1)).unwrap();
|
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.
|
// we will reach this point only once in most platforms except android.
|
||||||
// create window/surface/make context current once and just use them forever.
|
// 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);
|
viewport.gl_surface = Some(gl_surface);
|
||||||
|
|
||||||
self.current_gl_context = Some(current_gl_context);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1153,6 +1157,7 @@ impl GlutinWindowContext {
|
||||||
|
|
||||||
fn handle_viewport_output(
|
fn handle_viewport_output(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
egui_ctx: &egui::Context,
|
egui_ctx: &egui::Context,
|
||||||
viewport_output: ViewportIdMap<ViewportOutput>,
|
viewport_output: ViewportIdMap<ViewportOutput>,
|
||||||
) {
|
) {
|
||||||
|
|
@ -1197,6 +1202,9 @@ impl GlutinWindowContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create windows for any new viewports:
|
||||||
|
self.initialize_all_windows(event_loop);
|
||||||
|
|
||||||
// GC old viewports
|
// GC old viewports
|
||||||
self.viewports
|
self.viewports
|
||||||
.retain(|id, _| active_viewports_ids.contains(id));
|
.retain(|id, _| active_viewports_ids.contains(id));
|
||||||
|
|
@ -1295,10 +1303,12 @@ fn render_immediate_viewport(
|
||||||
viewport_ui_cb,
|
viewport_ui_cb,
|
||||||
} = immediate_viewport;
|
} = immediate_viewport;
|
||||||
|
|
||||||
|
let viewport_id = ids.this;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut glutin = glutin.borrow_mut();
|
let mut glutin = glutin.borrow_mut();
|
||||||
|
|
||||||
let viewport = initialize_or_update_viewport(
|
initialize_or_update_viewport(
|
||||||
egui_ctx,
|
egui_ctx,
|
||||||
&mut glutin.viewports,
|
&mut glutin.viewports,
|
||||||
ids,
|
ids,
|
||||||
|
|
@ -1308,17 +1318,18 @@ fn render_immediate_viewport(
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
if viewport.gl_surface.is_none() {
|
if let Err(err) = glutin.initialize_window(viewport_id, event_loop) {
|
||||||
glutin
|
log::error!(
|
||||||
.init_viewport(ids.this, event_loop)
|
"Failed to initialize a window for immediate viewport {viewport_id:?}: {err}"
|
||||||
.expect("Failed to initialize window in egui::Context::show_viewport_immediate");
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let input = {
|
let input = {
|
||||||
let mut glutin = glutin.borrow_mut();
|
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;
|
return;
|
||||||
};
|
};
|
||||||
let (Some(egui_winit), Some(window)) = (&mut viewport.egui_winit, &viewport.window) else {
|
let (Some(egui_winit), Some(window)) = (&mut viewport.egui_winit, &viewport.window) else {
|
||||||
|
|
@ -1362,7 +1373,7 @@ fn render_immediate_viewport(
|
||||||
..
|
..
|
||||||
} = &mut *glutin;
|
} = &mut *glutin;
|
||||||
|
|
||||||
let Some(viewport) = viewports.get_mut(&ids.this) else {
|
let Some(viewport) = viewports.get_mut(&viewport_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1423,7 +1434,7 @@ fn render_immediate_viewport(
|
||||||
|
|
||||||
egui_winit.handle_platform_output(window, platform_output);
|
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")]
|
#[cfg(feature = "__screenshot")]
|
||||||
|
|
|
||||||
|
|
@ -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 std::{cell::RefCell, time::Instant};
|
||||||
|
|
||||||
use winit::event_loop::{EventLoop, EventLoopBuilder};
|
use winit::event_loop::{EventLoop, EventLoopBuilder};
|
||||||
|
|
@ -34,12 +27,12 @@ fn create_event_loop_builder(
|
||||||
event_loop_builder
|
event_loop_builder
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop<UserEvent> {
|
fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result<EventLoop<UserEvent>> {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
let mut builder = create_event_loop_builder(native_options);
|
let mut builder = create_event_loop_builder(native_options);
|
||||||
|
|
||||||
crate::profile_scope!("EventLoopBuilder::build");
|
crate::profile_scope!("EventLoopBuilder::build");
|
||||||
builder.build()
|
Ok(builder.build()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access a thread-local event loop.
|
/// Access a thread-local event loop.
|
||||||
|
|
@ -49,16 +42,20 @@ fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop<UserE
|
||||||
fn with_event_loop<R>(
|
fn with_event_loop<R>(
|
||||||
mut native_options: epi::NativeOptions,
|
mut native_options: epi::NativeOptions,
|
||||||
f: impl FnOnce(&mut EventLoop<UserEvent>, epi::NativeOptions) -> R,
|
f: impl FnOnce(&mut EventLoop<UserEvent>, epi::NativeOptions) -> R,
|
||||||
) -> R {
|
) -> Result<R> {
|
||||||
thread_local!(static EVENT_LOOP: RefCell<Option<EventLoop<UserEvent>>> = RefCell::new(None));
|
thread_local!(static EVENT_LOOP: RefCell<Option<EventLoop<UserEvent>>> = RefCell::new(None));
|
||||||
|
|
||||||
EVENT_LOOP.with(|event_loop| {
|
EVENT_LOOP.with(|event_loop| {
|
||||||
// Since we want to reference NativeOptions when creating the EventLoop we can't
|
// 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
|
// do that as part of the lazy thread local storage initialization and so we instead
|
||||||
// create the event loop lazily here
|
// create the event loop lazily here
|
||||||
let mut event_loop = event_loop.borrow_mut();
|
let mut event_loop_lock = event_loop.borrow_mut();
|
||||||
let event_loop = event_loop.get_or_insert_with(|| create_event_loop(&mut native_options));
|
let event_loop = if let Some(event_loop) = &mut *event_loop_lock {
|
||||||
f(event_loop, native_options)
|
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<UserEvent>,
|
event_loop: &mut EventLoop<UserEvent>,
|
||||||
mut winit_app: impl WinitApp,
|
mut winit_app: impl WinitApp,
|
||||||
) -> Result<()> {
|
) -> 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
|
// When to repaint what window
|
||||||
let mut windows_next_repaint_times = HashMap::default();
|
let mut windows_next_repaint_times = HashMap::default();
|
||||||
|
|
||||||
let mut returned_result = Ok(());
|
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));
|
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 {
|
let event_result = match &event {
|
||||||
winit::event::Event::LoopDestroyed => {
|
winit::event::Event::LoopExiting => {
|
||||||
// On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name),
|
// 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:
|
// 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();
|
winit_app.save_and_destroy();
|
||||||
*control_flow = ControlFlow::Exit;
|
|
||||||
return;
|
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);
|
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 {
|
winit::event::Event::UserEvent(UserEvent::RequestRepaint {
|
||||||
|
|
@ -120,8 +125,11 @@ fn run_and_return(
|
||||||
EventResult::Wait
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
|
|
||||||
event => match winit_app.on_event(event_loop, event) {
|
event => match winit_app.on_event(event_loop_window_target, event) {
|
||||||
Ok(event_result) => event_result,
|
Ok(event_result) => {
|
||||||
|
log::trace!("event_result: {event_result:?}");
|
||||||
|
event_result
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("Exiting because of error: {err} during event {event:?}");
|
log::error!("Exiting because of error: {err} during event {event:?}");
|
||||||
returned_result = Err(err);
|
returned_result = Err(err);
|
||||||
|
|
@ -132,21 +140,28 @@ fn run_and_return(
|
||||||
|
|
||||||
match event_result {
|
match event_result {
|
||||||
EventResult::Wait => {
|
EventResult::Wait => {
|
||||||
control_flow.set_wait();
|
event_loop_window_target.set_control_flow(ControlFlow::Wait);
|
||||||
}
|
}
|
||||||
EventResult::RepaintNow(window_id) => {
|
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") {
|
if cfg!(target_os = "windows") {
|
||||||
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
||||||
windows_next_repaint_times.remove(&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);
|
||||||
} else {
|
} else {
|
||||||
// Fix for https://github.com/emilk/egui/issues/2425
|
// Fix for https://github.com/emilk/egui/issues/2425
|
||||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventResult::RepaintNext(window_id) => {
|
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());
|
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||||
}
|
}
|
||||||
EventResult::RepaintAt(window_id, repaint_time) => {
|
EventResult::RepaintAt(window_id, repaint_time) => {
|
||||||
|
|
@ -160,45 +175,35 @@ fn run_and_return(
|
||||||
EventResult::Exit => {
|
EventResult::Exit => {
|
||||||
log::debug!("Asking to exit event loop…");
|
log::debug!("Asking to exit event loop…");
|
||||||
winit_app.save_and_destroy();
|
winit_app.save_and_destroy();
|
||||||
*control_flow = ControlFlow::Exit;
|
event_loop_window_target.exit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut next_repaint_time = windows_next_repaint_times.values().min().copied();
|
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| {
|
windows_next_repaint_times.retain(|window_id, repaint_time| {
|
||||||
if Instant::now() < *repaint_time {
|
if Instant::now() < *repaint_time {
|
||||||
return true;
|
return true; // not yet ready
|
||||||
};
|
};
|
||||||
|
|
||||||
next_repaint_time = None;
|
next_repaint_time = None;
|
||||||
control_flow.set_poll();
|
event_loop_window_target.set_control_flow(ControlFlow::Poll);
|
||||||
|
|
||||||
if let Some(window) = winit_app.window(*window_id) {
|
if let Some(window) = winit_app.window(*window_id) {
|
||||||
log::trace!("request_redraw for {window_id:?}");
|
log::trace!("request_redraw for {window_id:?}");
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
log::trace!("No window found for {window_id:?}");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(next_repaint_time) = next_repaint_time {
|
if let Some(next_repaint_time) = next_repaint_time {
|
||||||
let time_until_next = next_repaint_time.saturating_duration_since(Instant::now());
|
event_loop_window_target.set_control_flow(ControlFlow::WaitUntil(next_repaint_time));
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
});
|
})?;
|
||||||
|
|
||||||
log::debug!("eframe window closed");
|
log::debug!("eframe window closed");
|
||||||
|
|
||||||
|
|
@ -211,32 +216,47 @@ fn run_and_return(
|
||||||
// we only apply this approach on Windows to minimize the affect.
|
// we only apply this approach on Windows to minimize the affect.
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
event_loop.run_return(|_, _, control_flow| {
|
event_loop
|
||||||
control_flow.set_exit();
|
.run_on_demand(|_, event_loop_window_target| {
|
||||||
});
|
event_loop_window_target.exit();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
returned_result
|
returned_result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp + 'static) -> ! {
|
fn run_and_exit(
|
||||||
|
event_loop: EventLoop<UserEvent>,
|
||||||
|
mut winit_app: impl WinitApp + 'static,
|
||||||
|
) -> Result<()> {
|
||||||
|
use winit::event_loop::ControlFlow;
|
||||||
log::debug!("Entering the winit event loop (run)…");
|
log::debug!("Entering the winit event loop (run)…");
|
||||||
|
|
||||||
// When to repaint what window
|
// When to repaint what window
|
||||||
let mut windows_next_repaint_times = HashMap::default();
|
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));
|
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 {
|
let event_result = match &event {
|
||||||
winit::event::Event::LoopDestroyed => {
|
winit::event::Event::LoopExiting => {
|
||||||
log::debug!("Received Event::LoopDestroyed");
|
log::debug!("Received Event::LoopExiting");
|
||||||
EventResult::Exit
|
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);
|
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 {
|
winit::event::Event::UserEvent(UserEvent::RequestRepaint {
|
||||||
|
|
@ -264,8 +284,11 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||||
EventResult::Wait
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
|
|
||||||
event => match winit_app.on_event(event_loop, event) {
|
event => match winit_app.on_event(event_loop_window_target, event) {
|
||||||
Ok(event_result) => event_result,
|
Ok(event_result) => {
|
||||||
|
log::trace!("event_result: {event_result:?}");
|
||||||
|
event_result
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
panic!("eframe encountered a fatal error: {err} during event {event:?}");
|
panic!("eframe encountered a fatal error: {err} during event {event:?}");
|
||||||
}
|
}
|
||||||
|
|
@ -274,22 +297,22 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||||
|
|
||||||
match event_result {
|
match event_result {
|
||||||
EventResult::Wait => {
|
EventResult::Wait => {
|
||||||
control_flow.set_wait();
|
event_loop_window_target.set_control_flow(ControlFlow::Wait);
|
||||||
}
|
}
|
||||||
EventResult::RepaintNow(window_id) => {
|
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") {
|
if cfg!(target_os = "windows") {
|
||||||
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
||||||
windows_next_repaint_times.remove(&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);
|
||||||
} else {
|
} else {
|
||||||
// Fix for https://github.com/emilk/egui/issues/2425
|
// Fix for https://github.com/emilk/egui/issues/2425
|
||||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventResult::RepaintNext(window_id) => {
|
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());
|
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||||
}
|
}
|
||||||
EventResult::RepaintAt(window_id, repaint_time) => {
|
EventResult::RepaintAt(window_id, repaint_time) => {
|
||||||
|
|
@ -303,6 +326,8 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||||
EventResult::Exit => {
|
EventResult::Exit => {
|
||||||
log::debug!("Quitting - saving app state…");
|
log::debug!("Quitting - saving app state…");
|
||||||
winit_app.save_and_destroy();
|
winit_app.save_and_destroy();
|
||||||
|
|
||||||
|
log::debug!("Exiting with return code 0");
|
||||||
#[allow(clippy::exit)]
|
#[allow(clippy::exit)]
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
@ -310,36 +335,25 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||||
|
|
||||||
let mut next_repaint_time = windows_next_repaint_times.values().min().copied();
|
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| {
|
windows_next_repaint_times.retain(|window_id, repaint_time| {
|
||||||
if Instant::now() < *repaint_time {
|
if Instant::now() < *repaint_time {
|
||||||
return true;
|
return true; // not yet ready
|
||||||
}
|
}
|
||||||
|
|
||||||
next_repaint_time = None;
|
next_repaint_time = None;
|
||||||
control_flow.set_poll();
|
event_loop_window_target.set_control_flow(ControlFlow::Poll);
|
||||||
|
|
||||||
if let Some(window) = winit_app.window(*window_id) {
|
if let Some(window) = winit_app.window(*window_id) {
|
||||||
log::trace!("request_redraw for {window_id:?}");
|
log::trace!("request_redraw for {window_id:?}");
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
log::trace!("No window found for {window_id:?}");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(next_repaint_time) = next_repaint_time {
|
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:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// WaitUntil seems to not work on iOS
|
// WaitUntil seems to not work on iOS
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
winit_app
|
winit_app
|
||||||
|
|
@ -350,9 +364,13 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||||
.map(|window| window.request_redraw())
|
.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| {
|
return with_event_loop(native_options, |event_loop, native_options| {
|
||||||
let glow_eframe = GlowWinitApp::new(event_loop, app_name, native_options, app_creator);
|
let glow_eframe = GlowWinitApp::new(event_loop, app_name, native_options, app_creator);
|
||||||
run_and_return(event_loop, glow_eframe)
|
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);
|
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| {
|
return with_event_loop(native_options, |event_loop, native_options| {
|
||||||
let wgpu_eframe = WgpuWinitApp::new(event_loop, app_name, native_options, app_creator);
|
let wgpu_eframe = WgpuWinitApp::new(event_loop, app_name, native_options, app_creator);
|
||||||
run_and_return(event_loop, wgpu_eframe)
|
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);
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant};
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
@ -109,7 +116,8 @@ impl WgpuWinitApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
|
/// Create a window for all viewports lacking one.
|
||||||
|
fn initialized_all_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
|
||||||
let Some(running) = &mut self.running else {
|
let Some(running) = &mut self.running else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -122,16 +130,14 @@ impl WgpuWinitApp {
|
||||||
} = &mut *shared;
|
} = &mut *shared;
|
||||||
|
|
||||||
for viewport in viewports.values_mut() {
|
for viewport in viewports.values_mut() {
|
||||||
if viewport.window.is_none() {
|
viewport.initialize_window(
|
||||||
viewport.init_window(
|
event_loop,
|
||||||
&running.integration.egui_ctx,
|
&running.integration.egui_ctx,
|
||||||
viewport_from_window,
|
viewport_from_window,
|
||||||
painter,
|
painter,
|
||||||
event_loop,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
fn drop_window(&mut self) -> Result<(), egui_wgpu::WgpuError> {
|
fn drop_window(&mut self) -> Result<(), egui_wgpu::WgpuError> {
|
||||||
|
|
@ -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<UserEvent>,
|
||||||
|
window_id: WindowId,
|
||||||
|
) -> EventResult {
|
||||||
|
self.initialized_all_windows(event_loop);
|
||||||
|
|
||||||
if let Some(running) = &mut self.running {
|
if let Some(running) = &mut self.running {
|
||||||
running.run_ui_and_paint(window_id)
|
running.run_ui_and_paint(window_id)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -361,14 +373,16 @@ impl WinitApp for WgpuWinitApp {
|
||||||
fn on_event(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
event: &winit::event::Event<'_, UserEvent>,
|
event: &winit::event::Event<UserEvent>,
|
||||||
) -> Result<EventResult> {
|
) -> Result<EventResult> {
|
||||||
crate::profile_function!(winit_integration::short_event_description(event));
|
crate::profile_function!(winit_integration::short_event_description(event));
|
||||||
|
|
||||||
self.build_windows(event_loop);
|
self.initialized_all_windows(event_loop);
|
||||||
|
|
||||||
Ok(match event {
|
Ok(match event {
|
||||||
winit::event::Event::Resumed => {
|
winit::event::Event::Resumed => {
|
||||||
|
log::debug!("Event::Resumed");
|
||||||
|
|
||||||
let running = if let Some(running) = &self.running {
|
let running = if let Some(running) = &self.running {
|
||||||
running
|
running
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -660,7 +674,7 @@ impl WgpuWinitRunning {
|
||||||
fn on_window_event(
|
fn on_window_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
window_id: WindowId,
|
window_id: WindowId,
|
||||||
event: &winit::event::WindowEvent<'_>,
|
event: &winit::event::WindowEvent,
|
||||||
) -> EventResult {
|
) -> EventResult {
|
||||||
crate::profile_function!(egui_winit::short_window_event_description(event));
|
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 => {
|
winit::event::WindowEvent::CloseRequested => {
|
||||||
if viewport_id == Some(ViewportId::ROOT) && integration.should_close() {
|
if viewport_id == Some(ViewportId::ROOT) && integration.should_close() {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
|
@ -775,13 +777,18 @@ impl WgpuWinitRunning {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Viewport {
|
impl Viewport {
|
||||||
fn init_window(
|
/// Create winit window, if needed.
|
||||||
|
fn initialize_window(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
egui_ctx: &egui::Context,
|
egui_ctx: &egui::Context,
|
||||||
windows_id: &mut HashMap<WindowId, ViewportId>,
|
windows_id: &mut HashMap<WindowId, ViewportId>,
|
||||||
painter: &mut egui_wgpu::winit::Painter,
|
painter: &mut egui_wgpu::winit::Painter,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
|
||||||
) {
|
) {
|
||||||
|
if self.window.is_some() {
|
||||||
|
return; // we already have one
|
||||||
|
}
|
||||||
|
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
let viewport_id = self.ids.this;
|
let viewport_id = self.ids.this;
|
||||||
|
|
@ -870,7 +877,7 @@ fn render_immediate_viewport(
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
if viewport.window.is_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 {
|
let (Some(window), Some(egui_winit)) = (&viewport.window, &mut viewport.egui_winit) else {
|
||||||
|
|
|
||||||
|
|
@ -74,12 +74,16 @@ pub trait WinitApp {
|
||||||
|
|
||||||
fn save_and_destroy(&mut self);
|
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<UserEvent>,
|
||||||
|
window_id: WindowId,
|
||||||
|
) -> EventResult;
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
event: &winit::event::Event<'_, UserEvent>,
|
event: &winit::event::Event<UserEvent>,
|
||||||
) -> crate::Result<EventResult>;
|
) -> crate::Result<EventResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,11 +121,9 @@ pub fn system_theme(window: &Window, options: &crate::NativeOptions) -> Option<c
|
||||||
|
|
||||||
/// Short and fast description of an event.
|
/// Short and fast description of an event.
|
||||||
/// Useful for logging and profiling.
|
/// Useful for logging and profiling.
|
||||||
pub fn short_event_description(event: &winit::event::Event<'_, UserEvent>) -> &'static str {
|
pub fn short_event_description(event: &winit::event::Event<UserEvent>) -> &'static str {
|
||||||
use winit::event::Event;
|
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::UserEvent(user_event) => match user_event {
|
winit::event::Event::UserEvent(user_event) => match user_event {
|
||||||
UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint",
|
UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint",
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest",
|
UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ pub struct AppRunner {
|
||||||
app: Box<dyn epi::App>,
|
app: Box<dyn epi::App>,
|
||||||
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
|
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
|
||||||
last_save_time: f64,
|
last_save_time: f64,
|
||||||
pub(crate) text_cursor_pos: Option<egui::Pos2>,
|
pub(crate) ime: Option<egui::output::IMEOutput>,
|
||||||
pub(crate) mutable_text_under_cursor: bool,
|
pub(crate) mutable_text_under_cursor: bool,
|
||||||
|
|
||||||
// Output for the last run:
|
// Output for the last run:
|
||||||
|
|
@ -112,7 +112,7 @@ impl AppRunner {
|
||||||
app,
|
app,
|
||||||
needs_repaint,
|
needs_repaint,
|
||||||
last_save_time: now_sec(),
|
last_save_time: now_sec(),
|
||||||
text_cursor_pos: None,
|
ime: None,
|
||||||
mutable_text_under_cursor: false,
|
mutable_text_under_cursor: false,
|
||||||
textures_delta: Default::default(),
|
textures_delta: Default::default(),
|
||||||
clipped_primitives: None,
|
clipped_primitives: None,
|
||||||
|
|
@ -244,7 +244,7 @@ impl AppRunner {
|
||||||
copied_text,
|
copied_text,
|
||||||
events: _, // already handled
|
events: _, // already handled
|
||||||
mutable_text_under_cursor,
|
mutable_text_under_cursor,
|
||||||
text_cursor_pos,
|
ime,
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
accesskit_update: _, // not currently implemented
|
accesskit_update: _, // not currently implemented
|
||||||
} = platform_output;
|
} = platform_output;
|
||||||
|
|
@ -264,9 +264,9 @@ impl AppRunner {
|
||||||
|
|
||||||
self.mutable_text_under_cursor = mutable_text_under_cursor;
|
self.mutable_text_under_cursor = mutable_text_under_cursor;
|
||||||
|
|
||||||
if self.text_cursor_pos != text_cursor_pos {
|
if self.ime != ime {
|
||||||
super::text_agent::move_text_cursor(text_cursor_pos, self.canvas_id());
|
super::text_agent::move_text_cursor(ime, self.canvas_id());
|
||||||
self.text_cursor_pos = text_cursor_pos;
|
self.ime = ime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa
|
||||||
if let Some(key) = egui_key {
|
if let Some(key) = egui_key {
|
||||||
runner.input.raw.events.push(egui::Event::Key {
|
runner.input.raw.events.push(egui::Event::Key {
|
||||||
key,
|
key,
|
||||||
|
physical_key: None, // TODO
|
||||||
pressed: true,
|
pressed: true,
|
||||||
repeat: false, // egui will fill this in for us!
|
repeat: false, // egui will fill this in for us!
|
||||||
modifiers,
|
modifiers,
|
||||||
|
|
@ -157,6 +158,7 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa
|
||||||
if let Some(key) = translate_key(&event.key()) {
|
if let Some(key) = translate_key(&event.key()) {
|
||||||
runner.input.raw.events.push(egui::Event::Key {
|
runner.input.raw.events.push(egui::Event::Key {
|
||||||
key,
|
key,
|
||||||
|
physical_key: None, // TODO
|
||||||
pressed: false,
|
pressed: false,
|
||||||
repeat: false,
|
repeat: false,
|
||||||
modifiers,
|
modifiers,
|
||||||
|
|
|
||||||
|
|
@ -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
|
/// 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.
|
/// a real text input or the name of a key.
|
||||||
pub fn translate_key(key: &str) -> Option<egui::Key> {
|
pub fn translate_key(key: &str) -> Option<egui::Key> {
|
||||||
use egui::Key;
|
egui::Key::from_name(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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modifiers_from_event(event: &web_sys::KeyboardEvent) -> egui::Modifiers {
|
pub fn modifiers_from_event(event: &web_sys::KeyboardEvent) -> egui::Modifiers {
|
||||||
|
|
|
||||||
|
|
@ -205,11 +205,13 @@ fn is_mobile() -> Option<bool> {
|
||||||
// candidate window moves following text element (agent),
|
// candidate window moves following text element (agent),
|
||||||
// so it appears that the IME candidate window moves with text cursor.
|
// so it appears that the IME candidate window moves with text cursor.
|
||||||
// On mobile devices, there is no need to do that.
|
// On mobile devices, there is no need to do that.
|
||||||
pub fn move_text_cursor(cursor: Option<egui::Pos2>, canvas_id: &str) -> Option<()> {
|
pub fn move_text_cursor(ime: Option<egui::output::IMEOutput>, canvas_id: &str) -> Option<()> {
|
||||||
let style = text_agent().style();
|
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) {
|
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 canvas = canvas_element(canvas_id)?;
|
||||||
let bounding_rect = text_agent().get_bounding_client_rect();
|
let bounding_rect = text_agent().get_bounding_client_rect();
|
||||||
let y = (y + (canvas.scroll_top() + canvas.offset_top()) as f32)
|
let y = (y + (canvas.scroll_top() + canvas.offset_top()) as f32)
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ wgpu.workspace = true
|
||||||
## Enable this when generating docs.
|
## Enable this when generating docs.
|
||||||
document-features = { version = "0.2", optional = true }
|
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:
|
# Native:
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
|
|
||||||
|
|
@ -61,12 +61,12 @@ egui = { version = "0.24.1", path = "../egui", default-features = false, feature
|
||||||
log = { version = "0.4", features = ["std"] }
|
log = { version = "0.4", features = ["std"] }
|
||||||
raw-window-handle.workspace = true
|
raw-window-handle.workspace = true
|
||||||
web-time = { version = "0.2" } # We use web-time so we can (maybe) compile for web
|
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
|
#! ### Optional dependencies
|
||||||
|
|
||||||
# feature accesskit
|
# feature accesskit
|
||||||
accesskit_winit = { version = "0.15.0", optional = true }
|
accesskit_winit = { version = "0.16.0", optional = true }
|
||||||
|
|
||||||
## Enable this when generating docs.
|
## Enable this when generating docs.
|
||||||
document-features = { version = "0.2", optional = true }
|
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 }
|
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]
|
[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]
|
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||||
arboard = { version = "3.2", optional = true, default-features = false }
|
arboard = { version = "3.2", optional = true, default-features = false }
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ impl State {
|
||||||
pub fn on_window_event(
|
pub fn on_window_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
window: &Window,
|
window: &Window,
|
||||||
event: &winit::event::WindowEvent<'_>,
|
event: &winit::event::WindowEvent,
|
||||||
) -> EventResponse {
|
) -> EventResponse {
|
||||||
crate::profile_function!(short_window_event_description(event));
|
crate::profile_function!(short_window_event_description(event));
|
||||||
|
|
||||||
|
|
@ -295,25 +295,7 @@ impl State {
|
||||||
consumed,
|
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) => {
|
WindowEvent::Ime(ime) => {
|
||||||
// on Mac even Cmd-C is pressed during ime, a `c` is pushed to Preedit.
|
// on Mac even Cmd-C is pressed during ime, a `c` is pushed to Preedit.
|
||||||
// So no need to check is_mac_cmd.
|
// So no need to check is_mac_cmd.
|
||||||
|
|
@ -353,11 +335,12 @@ impl State {
|
||||||
consumed: self.egui_ctx.wants_keyboard_input(),
|
consumed: self.egui_ctx.wants_keyboard_input(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput { input, .. } => {
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
self.on_keyboard_input(input);
|
|
||||||
// When pressing the Tab key, egui focuses the first focusable element, hence Tab always consumes.
|
// When pressing the Tab key, egui focuses the first focusable element, hence Tab always consumes.
|
||||||
let consumed = self.egui_ctx.wants_keyboard_input()
|
let consumed = self.on_keyboard_input(event)
|
||||||
|| input.virtual_keycode == Some(winit::event::VirtualKeyCode::Tab);
|
|| self.egui_ctx.wants_keyboard_input()
|
||||||
|
|| event.logical_key
|
||||||
|
== winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab);
|
||||||
EventResponse {
|
EventResponse {
|
||||||
repaint: true,
|
repaint: true,
|
||||||
consumed,
|
consumed,
|
||||||
|
|
@ -405,15 +388,23 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::ModifiersChanged(state) => {
|
WindowEvent::ModifiersChanged(state) => {
|
||||||
self.egui_input.modifiers.alt = state.alt();
|
let state = state.state();
|
||||||
self.egui_input.modifiers.ctrl = state.ctrl();
|
|
||||||
self.egui_input.modifiers.shift = state.shift();
|
let alt = state.alt_key();
|
||||||
self.egui_input.modifiers.mac_cmd = cfg!(target_os = "macos") && state.logo();
|
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") {
|
self.egui_input.modifiers.command = if cfg!(target_os = "macos") {
|
||||||
state.logo()
|
super_
|
||||||
} else {
|
} else {
|
||||||
state.ctrl()
|
ctrl
|
||||||
};
|
};
|
||||||
|
|
||||||
EventResponse {
|
EventResponse {
|
||||||
repaint: true,
|
repaint: true,
|
||||||
consumed: false,
|
consumed: false,
|
||||||
|
|
@ -421,7 +412,8 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Things that may require repaint:
|
// Things that may require repaint:
|
||||||
WindowEvent::CursorEntered { .. }
|
WindowEvent::RedrawRequested
|
||||||
|
| WindowEvent::CursorEntered { .. }
|
||||||
| WindowEvent::Destroyed
|
| WindowEvent::Destroyed
|
||||||
| WindowEvent::Occluded(_)
|
| WindowEvent::Occluded(_)
|
||||||
| WindowEvent::Resized(_)
|
| WindowEvent::Resized(_)
|
||||||
|
|
@ -434,7 +426,8 @@ impl State {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Things we completely ignore:
|
// Things we completely ignore:
|
||||||
WindowEvent::AxisMotion { .. }
|
WindowEvent::ActivationTokenDone { .. }
|
||||||
|
| WindowEvent::AxisMotion { .. }
|
||||||
| WindowEvent::SmartMagnify { .. }
|
| WindowEvent::SmartMagnify { .. }
|
||||||
| WindowEvent::TouchpadRotate { .. } => EventResponse {
|
| WindowEvent::TouchpadRotate { .. } => EventResponse {
|
||||||
repaint: false,
|
repaint: false,
|
||||||
|
|
@ -655,37 +648,93 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_keyboard_input(&mut self, input: &winit::event::KeyboardInput) {
|
fn on_keyboard_input(&mut self, event: &winit::event::KeyEvent) -> bool {
|
||||||
if let Some(keycode) = input.virtual_keycode {
|
let winit::event::KeyEvent {
|
||||||
let pressed = input.state == winit::event::ElementState::Pressed;
|
// 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 {
|
if pressed {
|
||||||
// VirtualKeyCode::Paste etc in winit are broken/untrustworthy,
|
// KeyCode::Paste etc in winit are broken/untrustworthy,
|
||||||
// so we detect these things manually:
|
// 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);
|
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);
|
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() {
|
if let Some(contents) = self.clipboard.get() {
|
||||||
let contents = contents.replace("\r\n", "\n");
|
let contents = contents.replace("\r\n", "\n");
|
||||||
if !contents.is_empty() {
|
if !contents.is_empty() {
|
||||||
self.egui_input.events.push(egui::Event::Paste(contents));
|
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 {
|
self.egui_input.events.push(egui::Event::Key {
|
||||||
key,
|
key: logical_key,
|
||||||
|
physical_key,
|
||||||
pressed,
|
pressed,
|
||||||
repeat: false, // egui will fill this in for us!
|
repeat: false, // egui will fill this in for us!
|
||||||
modifiers: self.egui_input.modifiers,
|
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`.
|
/// Call with the output given by `egui`.
|
||||||
///
|
///
|
||||||
|
|
@ -708,7 +757,7 @@ impl State {
|
||||||
copied_text,
|
copied_text,
|
||||||
events: _, // handled elsewhere
|
events: _, // handled elsewhere
|
||||||
mutable_text_under_cursor: _, // only used in eframe web
|
mutable_text_under_cursor: _, // only used in eframe web
|
||||||
text_cursor_pos,
|
ime,
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
accesskit_update,
|
accesskit_update,
|
||||||
} = platform_output;
|
} = platform_output;
|
||||||
|
|
@ -723,14 +772,25 @@ impl State {
|
||||||
self.clipboard.set(copied_text);
|
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 {
|
if self.allow_ime != allow_ime {
|
||||||
self.allow_ime = allow_ime;
|
self.allow_ime = allow_ime;
|
||||||
window.set_ime_allowed(allow_ime);
|
window.set_ime_allowed(allow_ime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(egui::Pos2 { x, y }) = text_cursor_pos {
|
if let Some(ime) = ime {
|
||||||
window.set_ime_position(winit::dpi::LogicalPosition { x, y });
|
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")]
|
#[cfg(feature = "accesskit")]
|
||||||
|
|
@ -880,25 +940,19 @@ fn is_printable_char(chr: char) -> bool {
|
||||||
!is_in_private_use_area && !chr.is_ascii_control()
|
!is_in_private_use_area && !chr.is_ascii_control()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_cut_command(modifiers: egui::Modifiers, keycode: winit::event::VirtualKeyCode) -> bool {
|
fn is_cut_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool {
|
||||||
(modifiers.command && keycode == winit::event::VirtualKeyCode::X)
|
(modifiers.command && keycode == egui::Key::X)
|
||||||
|| (cfg!(target_os = "windows")
|
|| (cfg!(target_os = "windows") && modifiers.shift && keycode == egui::Key::Delete)
|
||||||
&& modifiers.shift
|
|
||||||
&& keycode == winit::event::VirtualKeyCode::Delete)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_copy_command(modifiers: egui::Modifiers, keycode: winit::event::VirtualKeyCode) -> bool {
|
fn is_copy_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool {
|
||||||
(modifiers.command && keycode == winit::event::VirtualKeyCode::C)
|
(modifiers.command && keycode == egui::Key::C)
|
||||||
|| (cfg!(target_os = "windows")
|
|| (cfg!(target_os = "windows") && modifiers.ctrl && keycode == egui::Key::Insert)
|
||||||
&& modifiers.ctrl
|
|
||||||
&& keycode == winit::event::VirtualKeyCode::Insert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_paste_command(modifiers: egui::Modifiers, keycode: winit::event::VirtualKeyCode) -> bool {
|
fn is_paste_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool {
|
||||||
(modifiers.command && keycode == winit::event::VirtualKeyCode::V)
|
(modifiers.command && keycode == egui::Key::V)
|
||||||
|| (cfg!(target_os = "windows")
|
|| (cfg!(target_os = "windows") && modifiers.shift && keycode == egui::Key::Insert)
|
||||||
&& modifiers.shift
|
|
||||||
&& keycode == winit::event::VirtualKeyCode::Insert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_mouse_button(button: winit::event::MouseButton) -> Option<egui::PointerButton> {
|
fn translate_mouse_button(button: winit::event::MouseButton) -> Option<egui::PointerButton> {
|
||||||
|
|
@ -906,100 +960,159 @@ fn translate_mouse_button(button: winit::event::MouseButton) -> Option<egui::Poi
|
||||||
winit::event::MouseButton::Left => Some(egui::PointerButton::Primary),
|
winit::event::MouseButton::Left => Some(egui::PointerButton::Primary),
|
||||||
winit::event::MouseButton::Right => Some(egui::PointerButton::Secondary),
|
winit::event::MouseButton::Right => Some(egui::PointerButton::Secondary),
|
||||||
winit::event::MouseButton::Middle => Some(egui::PointerButton::Middle),
|
winit::event::MouseButton::Middle => Some(egui::PointerButton::Middle),
|
||||||
winit::event::MouseButton::Other(1) => Some(egui::PointerButton::Extra1),
|
winit::event::MouseButton::Back => Some(egui::PointerButton::Extra1),
|
||||||
winit::event::MouseButton::Other(2) => Some(egui::PointerButton::Extra2),
|
winit::event::MouseButton::Forward => Some(egui::PointerButton::Extra2),
|
||||||
winit::event::MouseButton::Other(_) => None,
|
winit::event::MouseButton::Other(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_virtual_key_code(key: winit::event::VirtualKeyCode) -> Option<egui::Key> {
|
fn key_from_winit_key(key: &winit::keyboard::Key) -> Option<egui::Key> {
|
||||||
|
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<egui::Key> {
|
||||||
use egui::Key;
|
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<egui::Key> {
|
||||||
|
use egui::Key;
|
||||||
|
use winit::keyboard::KeyCode;
|
||||||
|
|
||||||
Some(match key {
|
Some(match key {
|
||||||
VirtualKeyCode::Down => Key::ArrowDown,
|
KeyCode::ArrowDown => Key::ArrowDown,
|
||||||
VirtualKeyCode::Left => Key::ArrowLeft,
|
KeyCode::ArrowLeft => Key::ArrowLeft,
|
||||||
VirtualKeyCode::Right => Key::ArrowRight,
|
KeyCode::ArrowRight => Key::ArrowRight,
|
||||||
VirtualKeyCode::Up => Key::ArrowUp,
|
KeyCode::ArrowUp => Key::ArrowUp,
|
||||||
|
|
||||||
VirtualKeyCode::Escape => Key::Escape,
|
KeyCode::Escape => Key::Escape,
|
||||||
VirtualKeyCode::Tab => Key::Tab,
|
KeyCode::Tab => Key::Tab,
|
||||||
VirtualKeyCode::Back => Key::Backspace,
|
KeyCode::Backspace => Key::Backspace,
|
||||||
VirtualKeyCode::Return | VirtualKeyCode::NumpadEnter => Key::Enter,
|
KeyCode::Enter | KeyCode::NumpadEnter => Key::Enter,
|
||||||
VirtualKeyCode::Space => Key::Space,
|
KeyCode::Space => Key::Space,
|
||||||
|
|
||||||
VirtualKeyCode::Insert => Key::Insert,
|
KeyCode::Insert => Key::Insert,
|
||||||
VirtualKeyCode::Delete => Key::Delete,
|
KeyCode::Delete => Key::Delete,
|
||||||
VirtualKeyCode::Home => Key::Home,
|
KeyCode::Home => Key::Home,
|
||||||
VirtualKeyCode::End => Key::End,
|
KeyCode::End => Key::End,
|
||||||
VirtualKeyCode::PageUp => Key::PageUp,
|
KeyCode::PageUp => Key::PageUp,
|
||||||
VirtualKeyCode::PageDown => Key::PageDown,
|
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,
|
||||||
|
|
||||||
VirtualKeyCode::Minus | VirtualKeyCode::NumpadSubtract => Key::Minus,
|
|
||||||
// Using Mac the key with the Plus sign on it is reported as the Equals key
|
// Using Mac the key with the Plus sign on it is reported as the Equals key
|
||||||
// (with both English and Swedish keyboard).
|
// (with both English and Swedish keyboard).
|
||||||
VirtualKeyCode::Equals | VirtualKeyCode::Plus | VirtualKeyCode::NumpadAdd => {
|
KeyCode::Equal | KeyCode::NumpadAdd => Key::PlusEquals,
|
||||||
Key::PlusEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualKeyCode::Key0 | VirtualKeyCode::Numpad0 => Key::Num0,
|
KeyCode::Digit0 | KeyCode::Numpad0 => Key::Num0,
|
||||||
VirtualKeyCode::Key1 | VirtualKeyCode::Numpad1 => Key::Num1,
|
KeyCode::Digit1 | KeyCode::Numpad1 => Key::Num1,
|
||||||
VirtualKeyCode::Key2 | VirtualKeyCode::Numpad2 => Key::Num2,
|
KeyCode::Digit2 | KeyCode::Numpad2 => Key::Num2,
|
||||||
VirtualKeyCode::Key3 | VirtualKeyCode::Numpad3 => Key::Num3,
|
KeyCode::Digit3 | KeyCode::Numpad3 => Key::Num3,
|
||||||
VirtualKeyCode::Key4 | VirtualKeyCode::Numpad4 => Key::Num4,
|
KeyCode::Digit4 | KeyCode::Numpad4 => Key::Num4,
|
||||||
VirtualKeyCode::Key5 | VirtualKeyCode::Numpad5 => Key::Num5,
|
KeyCode::Digit5 | KeyCode::Numpad5 => Key::Num5,
|
||||||
VirtualKeyCode::Key6 | VirtualKeyCode::Numpad6 => Key::Num6,
|
KeyCode::Digit6 | KeyCode::Numpad6 => Key::Num6,
|
||||||
VirtualKeyCode::Key7 | VirtualKeyCode::Numpad7 => Key::Num7,
|
KeyCode::Digit7 | KeyCode::Numpad7 => Key::Num7,
|
||||||
VirtualKeyCode::Key8 | VirtualKeyCode::Numpad8 => Key::Num8,
|
KeyCode::Digit8 | KeyCode::Numpad8 => Key::Num8,
|
||||||
VirtualKeyCode::Key9 | VirtualKeyCode::Numpad9 => Key::Num9,
|
KeyCode::Digit9 | KeyCode::Numpad9 => Key::Num9,
|
||||||
|
|
||||||
VirtualKeyCode::A => Key::A,
|
KeyCode::KeyA => Key::A,
|
||||||
VirtualKeyCode::B => Key::B,
|
KeyCode::KeyB => Key::B,
|
||||||
VirtualKeyCode::C => Key::C,
|
KeyCode::KeyC => Key::C,
|
||||||
VirtualKeyCode::D => Key::D,
|
KeyCode::KeyD => Key::D,
|
||||||
VirtualKeyCode::E => Key::E,
|
KeyCode::KeyE => Key::E,
|
||||||
VirtualKeyCode::F => Key::F,
|
KeyCode::KeyF => Key::F,
|
||||||
VirtualKeyCode::G => Key::G,
|
KeyCode::KeyG => Key::G,
|
||||||
VirtualKeyCode::H => Key::H,
|
KeyCode::KeyH => Key::H,
|
||||||
VirtualKeyCode::I => Key::I,
|
KeyCode::KeyI => Key::I,
|
||||||
VirtualKeyCode::J => Key::J,
|
KeyCode::KeyJ => Key::J,
|
||||||
VirtualKeyCode::K => Key::K,
|
KeyCode::KeyK => Key::K,
|
||||||
VirtualKeyCode::L => Key::L,
|
KeyCode::KeyL => Key::L,
|
||||||
VirtualKeyCode::M => Key::M,
|
KeyCode::KeyM => Key::M,
|
||||||
VirtualKeyCode::N => Key::N,
|
KeyCode::KeyN => Key::N,
|
||||||
VirtualKeyCode::O => Key::O,
|
KeyCode::KeyO => Key::O,
|
||||||
VirtualKeyCode::P => Key::P,
|
KeyCode::KeyP => Key::P,
|
||||||
VirtualKeyCode::Q => Key::Q,
|
KeyCode::KeyQ => Key::Q,
|
||||||
VirtualKeyCode::R => Key::R,
|
KeyCode::KeyR => Key::R,
|
||||||
VirtualKeyCode::S => Key::S,
|
KeyCode::KeyS => Key::S,
|
||||||
VirtualKeyCode::T => Key::T,
|
KeyCode::KeyT => Key::T,
|
||||||
VirtualKeyCode::U => Key::U,
|
KeyCode::KeyU => Key::U,
|
||||||
VirtualKeyCode::V => Key::V,
|
KeyCode::KeyV => Key::V,
|
||||||
VirtualKeyCode::W => Key::W,
|
KeyCode::KeyW => Key::W,
|
||||||
VirtualKeyCode::X => Key::X,
|
KeyCode::KeyX => Key::X,
|
||||||
VirtualKeyCode::Y => Key::Y,
|
KeyCode::KeyY => Key::Y,
|
||||||
VirtualKeyCode::Z => Key::Z,
|
KeyCode::KeyZ => Key::Z,
|
||||||
|
|
||||||
VirtualKeyCode::F1 => Key::F1,
|
KeyCode::F1 => Key::F1,
|
||||||
VirtualKeyCode::F2 => Key::F2,
|
KeyCode::F2 => Key::F2,
|
||||||
VirtualKeyCode::F3 => Key::F3,
|
KeyCode::F3 => Key::F3,
|
||||||
VirtualKeyCode::F4 => Key::F4,
|
KeyCode::F4 => Key::F4,
|
||||||
VirtualKeyCode::F5 => Key::F5,
|
KeyCode::F5 => Key::F5,
|
||||||
VirtualKeyCode::F6 => Key::F6,
|
KeyCode::F6 => Key::F6,
|
||||||
VirtualKeyCode::F7 => Key::F7,
|
KeyCode::F7 => Key::F7,
|
||||||
VirtualKeyCode::F8 => Key::F8,
|
KeyCode::F8 => Key::F8,
|
||||||
VirtualKeyCode::F9 => Key::F9,
|
KeyCode::F9 => Key::F9,
|
||||||
VirtualKeyCode::F10 => Key::F10,
|
KeyCode::F10 => Key::F10,
|
||||||
VirtualKeyCode::F11 => Key::F11,
|
KeyCode::F11 => Key::F11,
|
||||||
VirtualKeyCode::F12 => Key::F12,
|
KeyCode::F12 => Key::F12,
|
||||||
VirtualKeyCode::F13 => Key::F13,
|
KeyCode::F13 => Key::F13,
|
||||||
VirtualKeyCode::F14 => Key::F14,
|
KeyCode::F14 => Key::F14,
|
||||||
VirtualKeyCode::F15 => Key::F15,
|
KeyCode::F15 => Key::F15,
|
||||||
VirtualKeyCode::F16 => Key::F16,
|
KeyCode::F16 => Key::F16,
|
||||||
VirtualKeyCode::F17 => Key::F17,
|
KeyCode::F17 => Key::F17,
|
||||||
VirtualKeyCode::F18 => Key::F18,
|
KeyCode::F18 => Key::F18,
|
||||||
VirtualKeyCode::F19 => Key::F19,
|
KeyCode::F19 => Key::F19,
|
||||||
VirtualKeyCode::F20 => Key::F20,
|
KeyCode::F20 => Key::F20,
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -1024,7 +1137,7 @@ fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option<winit::window::Curs
|
||||||
egui::CursorIcon::Move => Some(winit::window::CursorIcon::Move),
|
egui::CursorIcon::Move => Some(winit::window::CursorIcon::Move),
|
||||||
egui::CursorIcon::NoDrop => Some(winit::window::CursorIcon::NoDrop),
|
egui::CursorIcon::NoDrop => Some(winit::window::CursorIcon::NoDrop),
|
||||||
egui::CursorIcon::NotAllowed => Some(winit::window::CursorIcon::NotAllowed),
|
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::Progress => Some(winit::window::CursorIcon::Progress),
|
||||||
|
|
||||||
egui::CursorIcon::ResizeHorizontal => Some(winit::window::CursorIcon::EwResize),
|
egui::CursorIcon::ResizeHorizontal => Some(winit::window::CursorIcon::EwResize),
|
||||||
|
|
@ -1112,7 +1225,12 @@ fn process_viewport_command(
|
||||||
ViewportCommand::InnerSize(size) => {
|
ViewportCommand::InnerSize(size) => {
|
||||||
let width_px = pixels_per_point * size.x.max(1.0);
|
let width_px = pixels_per_point * size.x.max(1.0);
|
||||||
let height_px = pixels_per_point * size.y.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) => {
|
ViewportCommand::BeginResize(direction) => {
|
||||||
if let Err(err) = window.drag_resize_window(match direction {
|
if let Err(err) = window.drag_resize_window(match direction {
|
||||||
|
|
@ -1196,11 +1314,14 @@ fn process_viewport_command(
|
||||||
.expect("Invalid ICON data!")
|
.expect("Invalid ICON data!")
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
ViewportCommand::IMEPosition(pos) => {
|
ViewportCommand::IMERect(rect) => {
|
||||||
window.set_ime_position(PhysicalPosition::new(
|
window.set_ime_cursor_area(
|
||||||
pixels_per_point * pos.x,
|
PhysicalPosition::new(pixels_per_point * rect.min.x, pixels_per_point * rect.min.y),
|
||||||
pixels_per_point * pos.y,
|
PhysicalSize::new(
|
||||||
));
|
pixels_per_point * rect.size().x,
|
||||||
|
pixels_per_point * rect.size().y,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ViewportCommand::IMEAllowed(v) => window.set_ime_allowed(v),
|
ViewportCommand::IMEAllowed(v) => window.set_ime_allowed(v),
|
||||||
ViewportCommand::IMEPurpose(p) => window.set_ime_purpose(match p {
|
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);
|
let pixels_per_point = pixels_per_point(egui_ctx, window);
|
||||||
|
|
||||||
if let Some(size) = builder.inner_size {
|
if let Some(size) = builder.inner_size {
|
||||||
window.set_inner_size(PhysicalSize::new(
|
if window
|
||||||
|
.request_inner_size(PhysicalSize::new(
|
||||||
pixels_per_point * size.x,
|
pixels_per_point * size.x,
|
||||||
pixels_per_point * size.y,
|
pixels_per_point * size.y,
|
||||||
));
|
))
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
log::debug!("Failed to set window size");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some(size) = builder.min_inner_size {
|
if let Some(size) = builder.min_inner_size {
|
||||||
window.set_min_inner_size(Some(PhysicalSize::new(
|
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.
|
/// Short and fast description of an event.
|
||||||
/// Useful for logging and profiling.
|
/// Useful for logging and profiling.
|
||||||
pub fn short_generic_event_description<T>(event: &winit::event::Event<'_, T>) -> &'static str {
|
pub fn short_generic_event_description<T>(event: &winit::event::Event<T>) -> &'static str {
|
||||||
use winit::event::{DeviceEvent, Event, StartCause};
|
use winit::event::{DeviceEvent, Event, StartCause};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
|
Event::AboutToWait => "Event::AboutToWait",
|
||||||
|
Event::LoopExiting => "Event::LoopExiting",
|
||||||
Event::Suspended => "Event::Suspended",
|
Event::Suspended => "Event::Suspended",
|
||||||
Event::Resumed => "Event::Resumed",
|
Event::Resumed => "Event::Resumed",
|
||||||
Event::MainEventsCleared => "Event::MainEventsCleared",
|
Event::MemoryWarning => "Event::MemoryWarning",
|
||||||
Event::RedrawRequested(_) => "Event::RedrawRequested",
|
|
||||||
Event::RedrawEventsCleared => "Event::RedrawEventsCleared",
|
|
||||||
Event::LoopDestroyed => "Event::LoopDestroyed",
|
|
||||||
Event::UserEvent(_) => "UserEvent",
|
Event::UserEvent(_) => "UserEvent",
|
||||||
Event::DeviceEvent { event, .. } => match event {
|
Event::DeviceEvent { event, .. } => match event {
|
||||||
DeviceEvent::Added { .. } => "DeviceEvent::Added",
|
DeviceEvent::Added { .. } => "DeviceEvent::Added",
|
||||||
|
|
@ -1495,7 +1620,6 @@ pub fn short_generic_event_description<T>(event: &winit::event::Event<'_, T>) ->
|
||||||
DeviceEvent::Motion { .. } => "DeviceEvent::Motion",
|
DeviceEvent::Motion { .. } => "DeviceEvent::Motion",
|
||||||
DeviceEvent::Button { .. } => "DeviceEvent::Button",
|
DeviceEvent::Button { .. } => "DeviceEvent::Button",
|
||||||
DeviceEvent::Key { .. } => "DeviceEvent::Key",
|
DeviceEvent::Key { .. } => "DeviceEvent::Key",
|
||||||
DeviceEvent::Text { .. } => "DeviceEvent::Text",
|
|
||||||
},
|
},
|
||||||
Event::NewEvents(start_cause) => match start_cause {
|
Event::NewEvents(start_cause) => match start_cause {
|
||||||
StartCause::ResumeTimeReached { .. } => "NewEvents::ResumeTimeReached",
|
StartCause::ResumeTimeReached { .. } => "NewEvents::ResumeTimeReached",
|
||||||
|
|
@ -1509,10 +1633,11 @@ pub fn short_generic_event_description<T>(event: &winit::event::Event<'_, T>) ->
|
||||||
|
|
||||||
/// Short and fast description of an event.
|
/// Short and fast description of an event.
|
||||||
/// Useful for logging and profiling.
|
/// 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;
|
use winit::event::WindowEvent;
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
|
WindowEvent::ActivationTokenDone { .. } => "WindowEvent::ActivationTokenDone",
|
||||||
WindowEvent::Resized { .. } => "WindowEvent::Resized",
|
WindowEvent::Resized { .. } => "WindowEvent::Resized",
|
||||||
WindowEvent::Moved { .. } => "WindowEvent::Moved",
|
WindowEvent::Moved { .. } => "WindowEvent::Moved",
|
||||||
WindowEvent::CloseRequested { .. } => "WindowEvent::CloseRequested",
|
WindowEvent::CloseRequested { .. } => "WindowEvent::CloseRequested",
|
||||||
|
|
@ -1520,7 +1645,6 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent<'_>) ->
|
||||||
WindowEvent::DroppedFile { .. } => "WindowEvent::DroppedFile",
|
WindowEvent::DroppedFile { .. } => "WindowEvent::DroppedFile",
|
||||||
WindowEvent::HoveredFile { .. } => "WindowEvent::HoveredFile",
|
WindowEvent::HoveredFile { .. } => "WindowEvent::HoveredFile",
|
||||||
WindowEvent::HoveredFileCancelled { .. } => "WindowEvent::HoveredFileCancelled",
|
WindowEvent::HoveredFileCancelled { .. } => "WindowEvent::HoveredFileCancelled",
|
||||||
WindowEvent::ReceivedCharacter { .. } => "WindowEvent::ReceivedCharacter",
|
|
||||||
WindowEvent::Focused { .. } => "WindowEvent::Focused",
|
WindowEvent::Focused { .. } => "WindowEvent::Focused",
|
||||||
WindowEvent::KeyboardInput { .. } => "WindowEvent::KeyboardInput",
|
WindowEvent::KeyboardInput { .. } => "WindowEvent::KeyboardInput",
|
||||||
WindowEvent::ModifiersChanged { .. } => "WindowEvent::ModifiersChanged",
|
WindowEvent::ModifiersChanged { .. } => "WindowEvent::ModifiersChanged",
|
||||||
|
|
@ -1531,6 +1655,7 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent<'_>) ->
|
||||||
WindowEvent::MouseWheel { .. } => "WindowEvent::MouseWheel",
|
WindowEvent::MouseWheel { .. } => "WindowEvent::MouseWheel",
|
||||||
WindowEvent::MouseInput { .. } => "WindowEvent::MouseInput",
|
WindowEvent::MouseInput { .. } => "WindowEvent::MouseInput",
|
||||||
WindowEvent::TouchpadMagnify { .. } => "WindowEvent::TouchpadMagnify",
|
WindowEvent::TouchpadMagnify { .. } => "WindowEvent::TouchpadMagnify",
|
||||||
|
WindowEvent::RedrawRequested { .. } => "WindowEvent::RedrawRequested",
|
||||||
WindowEvent::SmartMagnify { .. } => "WindowEvent::SmartMagnify",
|
WindowEvent::SmartMagnify { .. } => "WindowEvent::SmartMagnify",
|
||||||
WindowEvent::TouchpadRotate { .. } => "WindowEvent::TouchpadRotate",
|
WindowEvent::TouchpadRotate { .. } => "WindowEvent::TouchpadRotate",
|
||||||
WindowEvent::TouchpadPressure { .. } => "WindowEvent::TouchpadPressure",
|
WindowEvent::TouchpadPressure { .. } => "WindowEvent::TouchpadPressure",
|
||||||
|
|
|
||||||
|
|
@ -361,8 +361,20 @@ pub enum Event {
|
||||||
|
|
||||||
/// A key was pressed or released.
|
/// A key was pressed or released.
|
||||||
Key {
|
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,
|
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<Key>,
|
||||||
|
|
||||||
/// Was it pressed or released?
|
/// Was it pressed or released?
|
||||||
pressed: bool,
|
pressed: bool,
|
||||||
|
|
||||||
|
|
@ -844,11 +856,8 @@ impl<'a> ModifierNames<'a> {
|
||||||
|
|
||||||
/// Keyboard keys.
|
/// Keyboard keys.
|
||||||
///
|
///
|
||||||
/// Includes all keys egui is interested in (such as `Home` and `End`)
|
/// egui usually uses logical keys, i.e. after applying any user keymap.
|
||||||
/// plus a few that are useful for detecting keyboard shortcuts.
|
// TODO(emilk): split into `LogicalKey` and `PhysicalKey`
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
|
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub enum Key {
|
pub enum Key {
|
||||||
|
|
@ -870,12 +879,28 @@ pub enum Key {
|
||||||
PageUp,
|
PageUp,
|
||||||
PageDown,
|
PageDown,
|
||||||
|
|
||||||
/// The virtual keycode for the Minus key.
|
// ----------------------------------------------
|
||||||
|
// Punctuation:
|
||||||
|
/// `:`
|
||||||
|
Colon,
|
||||||
|
|
||||||
|
/// `,`
|
||||||
|
Comma,
|
||||||
|
|
||||||
|
/// `-`
|
||||||
Minus,
|
Minus,
|
||||||
|
|
||||||
/// The virtual keycode for the Plus/Equals key.
|
/// `.`
|
||||||
|
Period,
|
||||||
|
|
||||||
|
/// The for the Plus/Equals key.
|
||||||
PlusEquals,
|
PlusEquals,
|
||||||
|
|
||||||
|
/// `;`
|
||||||
|
Semicolon,
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
// Digits:
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num0,
|
Num0,
|
||||||
|
|
||||||
|
|
@ -906,6 +931,8 @@ pub enum Key {
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num9,
|
Num9,
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
// Letters:
|
||||||
A, // Used for cmd+A (select All)
|
A, // Used for cmd+A (select All)
|
||||||
B,
|
B,
|
||||||
C, // |CMD COPY|
|
C, // |CMD COPY|
|
||||||
|
|
@ -933,7 +960,8 @@ pub enum Key {
|
||||||
Y,
|
Y,
|
||||||
Z, // |CMD UNDO|
|
Z, // |CMD UNDO|
|
||||||
|
|
||||||
// The function keys:
|
// ----------------------------------------------
|
||||||
|
// Function keys:
|
||||||
F1,
|
F1,
|
||||||
F2,
|
F2,
|
||||||
F3,
|
F3,
|
||||||
|
|
@ -954,9 +982,196 @@ pub enum Key {
|
||||||
F18,
|
F18,
|
||||||
F19,
|
F19,
|
||||||
F20,
|
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 {
|
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<Self> {
|
||||||
|
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
|
/// Emoji or name representing the key
|
||||||
pub fn symbol_or_name(self) -> &'static str {
|
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).
|
// 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::ArrowLeft => "Left",
|
||||||
Key::ArrowRight => "Right",
|
Key::ArrowRight => "Right",
|
||||||
Key::ArrowUp => "Up",
|
Key::ArrowUp => "Up",
|
||||||
|
|
||||||
Key::Escape => "Escape",
|
Key::Escape => "Escape",
|
||||||
Key::Tab => "Tab",
|
Key::Tab => "Tab",
|
||||||
Key::Backspace => "Backspace",
|
Key::Backspace => "Backspace",
|
||||||
Key::Enter => "Enter",
|
Key::Enter => "Enter",
|
||||||
Key::Space => "Space",
|
Key::Space => "Space",
|
||||||
|
|
||||||
Key::Insert => "Insert",
|
Key::Insert => "Insert",
|
||||||
Key::Delete => "Delete",
|
Key::Delete => "Delete",
|
||||||
Key::Home => "Home",
|
Key::Home => "Home",
|
||||||
Key::End => "End",
|
Key::End => "End",
|
||||||
Key::PageUp => "PageUp",
|
Key::PageUp => "PageUp",
|
||||||
Key::PageDown => "PageDown",
|
Key::PageDown => "PageDown",
|
||||||
|
|
||||||
|
Key::Colon => "Colon",
|
||||||
|
Key::Comma => "Comma",
|
||||||
Key::Minus => "Minus",
|
Key::Minus => "Minus",
|
||||||
|
Key::Period => "Period",
|
||||||
Key::PlusEquals => "Plus",
|
Key::PlusEquals => "Plus",
|
||||||
|
Key::Semicolon => "Semicolon",
|
||||||
|
|
||||||
Key::Num0 => "0",
|
Key::Num0 => "0",
|
||||||
Key::Num1 => "1",
|
Key::Num1 => "1",
|
||||||
Key::Num2 => "2",
|
Key::Num2 => "2",
|
||||||
|
|
@ -1003,6 +1226,7 @@ impl Key {
|
||||||
Key::Num7 => "7",
|
Key::Num7 => "7",
|
||||||
Key::Num8 => "8",
|
Key::Num8 => "8",
|
||||||
Key::Num9 => "9",
|
Key::Num9 => "9",
|
||||||
|
|
||||||
Key::A => "A",
|
Key::A => "A",
|
||||||
Key::B => "B",
|
Key::B => "B",
|
||||||
Key::C => "C",
|
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`.
|
/// A keyboard shortcut, e.g. `Ctrl+Alt+W`.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
/// The non-rendering part of what egui emits each frame.
|
||||||
///
|
///
|
||||||
/// You can access (and modify) this with [`crate::Context::output`].
|
/// 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.
|
/// Use by `eframe` web to show/hide mobile keyboard and IME agent.
|
||||||
pub mutable_text_under_cursor: bool,
|
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.
|
/// Useful for IME.
|
||||||
pub text_cursor_pos: Option<crate::Pos2>,
|
pub ime: Option<IMEOutput>,
|
||||||
|
|
||||||
/// The difference in the widget tree since last frame.
|
/// The difference in the widget tree since last frame.
|
||||||
///
|
///
|
||||||
|
|
@ -137,7 +152,7 @@ impl PlatformOutput {
|
||||||
copied_text,
|
copied_text,
|
||||||
mut events,
|
mut events,
|
||||||
mutable_text_under_cursor,
|
mutable_text_under_cursor,
|
||||||
text_cursor_pos,
|
ime,
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
accesskit_update,
|
accesskit_update,
|
||||||
} = newer;
|
} = newer;
|
||||||
|
|
@ -151,7 +166,7 @@ impl PlatformOutput {
|
||||||
}
|
}
|
||||||
self.events.append(&mut events);
|
self.events.append(&mut events);
|
||||||
self.mutable_text_under_cursor = mutable_text_under_cursor;
|
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")]
|
#[cfg(feature = "accesskit")]
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -899,7 +899,8 @@ pub enum ViewportCommand {
|
||||||
/// The the window icon.
|
/// The the window icon.
|
||||||
Icon(Option<Arc<IconData>>),
|
Icon(Option<Arc<IconData>>),
|
||||||
|
|
||||||
IMEPosition(Pos2),
|
/// Set the IME cursor editing area.
|
||||||
|
IMERect(crate::Rect),
|
||||||
IMEAllowed(bool),
|
IMEAllowed(bool),
|
||||||
IMEPurpose(IMEPurpose),
|
IMEPurpose(IMEPurpose),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -683,7 +683,7 @@ impl<'t> TextEdit<'t> {
|
||||||
paint_cursor_selection(ui, &painter, text_draw_pos, &galley, &cursor_range);
|
paint_cursor_selection(ui, &painter, text_draw_pos, &galley, &cursor_range);
|
||||||
|
|
||||||
if text.is_mutable() {
|
if text.is_mutable() {
|
||||||
let cursor_pos = paint_cursor_end(
|
let cursor_rect = paint_cursor_end(
|
||||||
ui,
|
ui,
|
||||||
row_height,
|
row_height,
|
||||||
&painter,
|
&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
|
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 {
|
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 {
|
if interactive {
|
||||||
// eframe web uses `text_cursor_pos` when showing IME,
|
// For IME, so only set it when text is editable and visible!
|
||||||
// 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| {
|
ui.ctx().output_mut(|o| {
|
||||||
o.text_cursor_pos = Some(cursor_pos.left_top());
|
o.ime = Some(crate::output::IMEOutput { rect, cursor_rect });
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
ui.ctx().output_mut(|o| {
|
|
||||||
o.text_cursor_pos = Some(cursor_pos.left_bottom());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ image = ["dep:image"]
|
||||||
puffin = ["dep:puffin", "egui/puffin"]
|
puffin = ["dep:puffin", "egui/puffin"]
|
||||||
|
|
||||||
## Support loading svg images.
|
## Support loading svg images.
|
||||||
svg = ["resvg", "tiny-skia", "usvg"]
|
svg = ["resvg"]
|
||||||
|
|
||||||
## Enable better syntax highlighting using [`syntect`](https://docs.rs/syntect).
|
## Enable better syntax highlighting using [`syntect`](https://docs.rs/syntect).
|
||||||
syntect = ["dep:syntect"]
|
syntect = ["dep:syntect"]
|
||||||
|
|
@ -93,8 +93,6 @@ syntect = { version = "5", optional = true, default-features = false, features =
|
||||||
|
|
||||||
# svg feature
|
# svg feature
|
||||||
resvg = { version = "0.28", optional = true, default-features = false }
|
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
|
# http feature
|
||||||
ehttp = { version = "0.3.1", optional = true, default-features = false }
|
ehttp = { version = "0.3.1", optional = true, default-features = false }
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
use egui::{mutex::Mutex, TextureOptions};
|
use egui::{mutex::Mutex, TextureOptions};
|
||||||
|
|
||||||
|
#[cfg(feature = "svg")]
|
||||||
|
use resvg::{tiny_skia, usvg};
|
||||||
|
|
||||||
#[cfg(feature = "svg")]
|
#[cfg(feature = "svg")]
|
||||||
pub use usvg::FitTo;
|
pub use usvg::FitTo;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
|
use std::{mem::size_of, path::Path, sync::Arc};
|
||||||
|
|
||||||
use egui::{
|
use egui::{
|
||||||
ahash::HashMap,
|
ahash::HashMap,
|
||||||
load::{BytesPoll, ImageLoadResult, ImageLoader, ImagePoll, LoadError, SizeHint},
|
load::{BytesPoll, ImageLoadResult, ImageLoader, ImagePoll, LoadError, SizeHint},
|
||||||
mutex::Mutex,
|
mutex::Mutex,
|
||||||
ColorImage,
|
ColorImage,
|
||||||
};
|
};
|
||||||
use std::{mem::size_of, path::Path, sync::Arc};
|
|
||||||
|
use resvg::usvg;
|
||||||
|
|
||||||
type Entry = Result<Arc<ColorImage>, String>;
|
type Entry = Result<Arc<ColorImage>, String>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,9 @@ wasm-bindgen = "0.2"
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
glutin = "0.30" # examples/pure_glow
|
glutin = "0.31" # examples/pure_glow
|
||||||
raw-window-handle.workspace = true
|
raw-window-handle.workspace = true
|
||||||
glutin-winit = "0.3.0"
|
glutin-winit = "0.4.0"
|
||||||
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ impl GlutinWindowContext {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe fn new(event_loop: &winit::event_loop::EventLoopWindowTarget<UserEvent>) -> Self {
|
unsafe fn new(event_loop: &winit::event_loop::EventLoopWindowTarget<UserEvent>) -> Self {
|
||||||
use egui::NumExt;
|
use egui::NumExt;
|
||||||
use glutin::context::NotCurrentGlContextSurfaceAccessor;
|
use glutin::context::NotCurrentGlContext;
|
||||||
use glutin::display::GetGlDisplay;
|
use glutin::display::GetGlDisplay;
|
||||||
use glutin::display::GlDisplay;
|
use glutin::display::GlDisplay;
|
||||||
use glutin::prelude::GlSurface;
|
use glutin::prelude::GlSurface;
|
||||||
|
|
@ -42,7 +42,7 @@ impl GlutinWindowContext {
|
||||||
log::debug!("trying to get gl_config");
|
log::debug!("trying to get gl_config");
|
||||||
let (mut window, gl_config) =
|
let (mut window, gl_config) =
|
||||||
glutin_winit::DisplayBuilder::new() // let glutin-winit helper crate handle the complex parts of opengl context creation
|
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()))
|
.with_window_builder(Some(winit_window_builder.clone()))
|
||||||
.build(
|
.build(
|
||||||
event_loop,
|
event_loop,
|
||||||
|
|
@ -150,7 +150,9 @@ pub enum UserEvent {
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut clear_color = [0.1, 0.1, 0.1];
|
let mut clear_color = [0.1, 0.1, 0.1];
|
||||||
|
|
||||||
let event_loop = winit::event_loop::EventLoopBuilder::<UserEvent>::with_user_event().build();
|
let event_loop = winit::event_loop::EventLoopBuilder::<UserEvent>::with_user_event()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
let (gl_window, gl) = create_display(&event_loop);
|
let (gl_window, gl) = create_display(&event_loop);
|
||||||
let gl = std::sync::Arc::new(gl);
|
let gl = std::sync::Arc::new(gl);
|
||||||
|
|
||||||
|
|
@ -168,7 +170,7 @@ fn main() {
|
||||||
|
|
||||||
let mut repaint_delay = std::time::Duration::MAX;
|
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 redraw = || {
|
||||||
let mut quit = false;
|
let mut quit = false;
|
||||||
|
|
||||||
|
|
@ -182,18 +184,20 @@ fn main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
*control_flow = if quit {
|
if quit {
|
||||||
winit::event_loop::ControlFlow::Exit
|
event_loop_window_target.exit();
|
||||||
} else if repaint_delay.is_zero() {
|
} else {
|
||||||
|
event_loop_window_target.set_control_flow(if repaint_delay.is_zero() {
|
||||||
gl_window.window().request_redraw();
|
gl_window.window().request_redraw();
|
||||||
winit::event_loop::ControlFlow::Poll
|
winit::event_loop::ControlFlow::Poll
|
||||||
} else if let Some(repaint_delay_instant) =
|
} else if let Some(repaint_after_instant) =
|
||||||
std::time::Instant::now().checked_add(repaint_delay)
|
std::time::Instant::now().checked_add(repaint_delay)
|
||||||
{
|
{
|
||||||
winit::event_loop::ControlFlow::WaitUntil(repaint_delay_instant)
|
winit::event_loop::ControlFlow::WaitUntil(repaint_after_instant)
|
||||||
} else {
|
} else {
|
||||||
winit::event_loop::ControlFlow::Wait
|
winit::event_loop::ControlFlow::Wait
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -214,25 +218,20 @@ fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
match event {
|
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, .. } => {
|
winit::event::Event::WindowEvent { event, .. } => {
|
||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
if matches!(event, WindowEvent::CloseRequested | WindowEvent::Destroyed) {
|
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 {
|
if let winit::event::WindowEvent::Resized(physical_size) = &event {
|
||||||
gl_window.resize(*physical_size);
|
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);
|
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)) => {
|
winit::event::Event::UserEvent(UserEvent::Redraw(delay)) => {
|
||||||
repaint_delay = delay;
|
repaint_delay = delay;
|
||||||
}
|
}
|
||||||
winit::event::Event::LoopDestroyed => {
|
winit::event::Event::LoopExiting => {
|
||||||
egui_glow.destroy();
|
egui_glow.destroy();
|
||||||
}
|
}
|
||||||
winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
|
winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ impl EguiGlow {
|
||||||
pub fn on_window_event(
|
pub fn on_window_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
window: &winit::window::Window,
|
window: &winit::window::Window,
|
||||||
event: &winit::event::WindowEvent<'_>,
|
event: &winit::event::WindowEvent,
|
||||||
) -> EventResponse {
|
) -> EventResponse {
|
||||||
self.egui_winit.on_window_event(window, event)
|
self.egui_winit.on_window_event(window, event)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
deny.toml
16
deny.toml
|
|
@ -37,25 +37,23 @@ skip = [
|
||||||
{ name = "arrayvec" }, # old version via tiny-skiaz
|
{ name = "arrayvec" }, # old version via tiny-skiaz
|
||||||
{ name = "base64" }, # small crate, old version from usvg
|
{ name = "base64" }, # small crate, old version from usvg
|
||||||
{ name = "bitflags" }, # old 1.0 version via glutin, png, spirv, …
|
{ 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 = "glow" }, # TODO(@wumpf): updatere glow
|
||||||
{ 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 = "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 = "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 = "memoffset" }, # tiny dependency
|
||||||
{ name = "nix" }, # old version via winit
|
{ name = "quick-xml" }, # old version via wayland-scanner
|
||||||
{ name = "redox_syscall" }, # old version via winit
|
{ name = "redox_syscall" }, # old version via directories-next
|
||||||
{ name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu.
|
{ 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 = "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 = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg
|
||||||
{ name = "wayland-sys" }, # old version via winit
|
{ name = "windows" }, # old version via accesskit_windows
|
||||||
{ name = "windows_x86_64_msvc" }, # old version via glutin
|
|
||||||
{ name = "windows-sys" }, # old version via glutin
|
|
||||||
{ name = "windows" }, # old version via accesskit
|
|
||||||
]
|
]
|
||||||
skip-tree = [
|
skip-tree = [
|
||||||
{ name = "criterion" }, # dev-dependency
|
{ name = "criterion" }, # dev-dependency
|
||||||
{ name = "foreign-types" }, # small crate. Old version via cocoa and core-graphics (winit).
|
{ 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 = "rfd" }, # example dependency
|
||||||
|
{ name = "tiny-skia" }, # old version via old resvg in egui_extras - see https://github.com/emilk/egui/issues/3652
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ edition = "2021"
|
||||||
rust-version = "1.72"
|
rust-version = "1.72"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
wgpu = ["eframe/wgpu"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
eframe = { path = "../../crates/eframe", features = [
|
eframe = { path = "../../crates/eframe", features = [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue