mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 05:40:03 -04:00
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:
@@ -298,7 +298,7 @@ pub struct NativeOptions {
|
||||
///
|
||||
/// 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.
|
||||
pub run_and_return: bool,
|
||||
|
||||
|
||||
@@ -325,6 +325,11 @@ pub enum Error {
|
||||
#[error("winit error: {0}")]
|
||||
Winit(#[from] winit::error::OsError),
|
||||
|
||||
/// An error from [`winit::event_loop::EventLoop`].
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("winit EventLoopError: {0}")]
|
||||
WinitEventLoop(#[from] winit::error::EventLoopError),
|
||||
|
||||
/// An error from [`glutin`] when using [`glow`].
|
||||
#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
|
||||
#[error("glutin error: {0}")]
|
||||
|
||||
@@ -231,7 +231,7 @@ impl EpiIntegration {
|
||||
&mut self,
|
||||
window: &winit::window::Window,
|
||||
egui_winit: &mut egui_winit::State,
|
||||
event: &winit::event::WindowEvent<'_>,
|
||||
event: &winit::event::WindowEvent,
|
||||
) -> EventResponse {
|
||||
crate::profile_function!(egui_winit::short_window_event_description(event));
|
||||
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
//! Note that this file contains code very similar to [`wgpu_integration`].
|
||||
//! When making changes to one you often also want to apply it to the other.
|
||||
//!
|
||||
//! This is also very complex code, and not very pretty.
|
||||
//! There is a bunch of improvements we could do,
|
||||
//! like removing a bunch of `unwraps`.
|
||||
|
||||
use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant};
|
||||
|
||||
use glutin::{
|
||||
config::GlConfig,
|
||||
context::NotCurrentGlContext,
|
||||
display::GetGlDisplay,
|
||||
prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext},
|
||||
prelude::{GlDisplay, PossiblyCurrentGlContext},
|
||||
surface::GlSurface,
|
||||
};
|
||||
use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _};
|
||||
@@ -112,6 +121,8 @@ struct Viewport {
|
||||
/// None for immediate viewports.
|
||||
viewport_ui_cb: Option<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>>,
|
||||
window: Option<Rc<Window>>,
|
||||
egui_winit: Option<egui_winit::State>,
|
||||
@@ -160,12 +171,12 @@ impl GlowWinitApp {
|
||||
};
|
||||
|
||||
// Creates the window - must come before we create our glow context
|
||||
glutin_window_context.on_resume(event_loop)?;
|
||||
glutin_window_context.initialize_window(ViewportId::ROOT, event_loop)?;
|
||||
|
||||
if let Some(viewport) = glutin_window_context.viewports.get(&ViewportId::ROOT) {
|
||||
if let Some(window) = &viewport.window {
|
||||
epi_integration::apply_window_settings(window, window_settings);
|
||||
}
|
||||
{
|
||||
let viewport = &glutin_window_context.viewports[&ViewportId::ROOT];
|
||||
let window = viewport.window.as_ref().unwrap(); // Can't fail - we just called `initialize_all_viewports`
|
||||
epi_integration::apply_window_settings(window, window_settings);
|
||||
}
|
||||
|
||||
let gl = unsafe {
|
||||
@@ -390,9 +401,13 @@ impl WinitApp for GlowWinitApp {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult {
|
||||
fn run_ui_and_paint(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
window_id: WindowId,
|
||||
) -> EventResult {
|
||||
if let Some(running) = &mut self.running {
|
||||
running.run_ui_and_paint(window_id)
|
||||
running.run_ui_and_paint(event_loop, window_id)
|
||||
} else {
|
||||
EventResult::Wait
|
||||
}
|
||||
@@ -401,29 +416,27 @@ impl WinitApp for GlowWinitApp {
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
event: &winit::event::Event<'_, UserEvent>,
|
||||
event: &winit::event::Event<UserEvent>,
|
||||
) -> Result<EventResult> {
|
||||
crate::profile_function!(winit_integration::short_event_description(event));
|
||||
|
||||
Ok(match event {
|
||||
winit::event::Event::Resumed => {
|
||||
log::debug!("Event::Resumed");
|
||||
|
||||
let running = if let Some(running) = &mut self.running {
|
||||
// not the first resume event. create whatever you need.
|
||||
running.glutin.borrow_mut().on_resume(event_loop)?;
|
||||
// Not the first resume event. Create all outstanding windows.
|
||||
running
|
||||
.glutin
|
||||
.borrow_mut()
|
||||
.initialize_all_windows(event_loop);
|
||||
running
|
||||
} else {
|
||||
// first resume event.
|
||||
// we can actually move this outside of event loop.
|
||||
// and just run the on_resume fn of gl_window
|
||||
// First resume event. Created our root window etc.
|
||||
self.init_run_state(event_loop)?
|
||||
};
|
||||
let window_id = running
|
||||
.glutin
|
||||
.borrow()
|
||||
.window_from_viewport
|
||||
.get(&ViewportId::ROOT)
|
||||
.copied();
|
||||
EventResult::RepaintNow(window_id.unwrap())
|
||||
let window_id = running.glutin.borrow().window_from_viewport[&ViewportId::ROOT];
|
||||
EventResult::RepaintNow(window_id)
|
||||
}
|
||||
|
||||
winit::event::Event::Suspended => {
|
||||
@@ -433,15 +446,6 @@ impl WinitApp for GlowWinitApp {
|
||||
EventResult::Wait
|
||||
}
|
||||
|
||||
winit::event::Event::MainEventsCleared => {
|
||||
if let Some(running) = &self.running {
|
||||
if let Err(err) = running.glutin.borrow_mut().on_resume(event_loop) {
|
||||
log::warn!("on_resume failed {err}");
|
||||
}
|
||||
}
|
||||
EventResult::Wait
|
||||
}
|
||||
|
||||
winit::event::Event::WindowEvent { event, window_id } => {
|
||||
if let Some(running) = &mut self.running {
|
||||
running.on_window_event(*window_id, event)
|
||||
@@ -477,7 +481,11 @@ impl WinitApp for GlowWinitApp {
|
||||
}
|
||||
|
||||
impl GlowWinitRunning {
|
||||
fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult {
|
||||
fn run_ui_and_paint(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
window_id: WindowId,
|
||||
) -> EventResult {
|
||||
crate::profile_function!();
|
||||
|
||||
let Some(viewport_id) = self
|
||||
@@ -666,7 +674,7 @@ impl GlowWinitRunning {
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
|
||||
glutin.handle_viewport_output(&integration.egui_ctx, viewport_output);
|
||||
glutin.handle_viewport_output(event_loop, &integration.egui_ctx, viewport_output);
|
||||
|
||||
if integration.should_close() {
|
||||
EventResult::Exit
|
||||
@@ -678,7 +686,7 @@ impl GlowWinitRunning {
|
||||
fn on_window_event(
|
||||
&mut self,
|
||||
window_id: WindowId,
|
||||
event: &winit::event::WindowEvent<'_>,
|
||||
event: &winit::event::WindowEvent,
|
||||
) -> EventResult {
|
||||
crate::profile_function!(egui_winit::short_window_event_description(event));
|
||||
|
||||
@@ -717,13 +725,6 @@ impl GlowWinitRunning {
|
||||
}
|
||||
}
|
||||
|
||||
winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
|
||||
if let Some(viewport_id) = viewport_id {
|
||||
repaint_asap = true;
|
||||
glutin.resize(viewport_id, **new_inner_size);
|
||||
}
|
||||
}
|
||||
|
||||
winit::event::WindowEvent::CloseRequested => {
|
||||
if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() {
|
||||
log::debug!(
|
||||
@@ -768,7 +769,11 @@ impl GlowWinitRunning {
|
||||
{
|
||||
event_response = self.integration.on_window_event(window, egui_winit, event);
|
||||
}
|
||||
} else {
|
||||
log::trace!("Ignoring event: no viewport for {viewport_id:?}");
|
||||
}
|
||||
} else {
|
||||
log::trace!("Ignoring event: no viewport_id");
|
||||
}
|
||||
|
||||
if event_response.repaint {
|
||||
@@ -855,7 +860,7 @@ impl GlutinWindowContext {
|
||||
// Create GL display. This may probably create a window too on most platforms. Definitely on `MS windows`. Never on Android.
|
||||
let display_builder = glutin_winit::DisplayBuilder::new()
|
||||
// we might want to expose this option to users in the future. maybe using an env var or using native_options.
|
||||
.with_preference(glutin_winit::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150
|
||||
.with_preference(glutin_winit::ApiPreference::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150
|
||||
.with_window_builder(Some(egui_winit::create_winit_window_builder(
|
||||
egui_ctx,
|
||||
event_loop,
|
||||
@@ -968,37 +973,29 @@ impl GlutinWindowContext {
|
||||
focused_viewport: Some(ViewportId::ROOT),
|
||||
};
|
||||
|
||||
slf.on_resume(event_loop)?;
|
||||
slf.initialize_window(ViewportId::ROOT, event_loop)?;
|
||||
|
||||
Ok(slf)
|
||||
}
|
||||
|
||||
/// This will be run after `new`. on android, it might be called multiple times over the course of the app's lifetime.
|
||||
/// roughly,
|
||||
/// 1. check if window already exists. otherwise, create one now.
|
||||
/// 2. create attributes for surface creation.
|
||||
/// 3. create surface.
|
||||
/// 4. make surface and context current.
|
||||
/// Create a surface, window, and winit integration for all viewports lacking any of that.
|
||||
///
|
||||
/// we presently assume that we will
|
||||
fn on_resume(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) -> Result<()> {
|
||||
/// Errors will be logged.
|
||||
fn initialize_all_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
|
||||
crate::profile_function!();
|
||||
|
||||
let viewports: Vec<ViewportId> = self
|
||||
.viewports
|
||||
.iter()
|
||||
.filter(|(_, viewport)| viewport.gl_surface.is_none())
|
||||
.map(|(id, _)| *id)
|
||||
.collect();
|
||||
let viewports: Vec<ViewportId> = self.viewports.keys().copied().collect();
|
||||
|
||||
for viewport_id in viewports {
|
||||
self.init_viewport(viewport_id, event_loop)?;
|
||||
if let Err(err) = self.initialize_window(viewport_id, event_loop) {
|
||||
log::error!("Failed to initialize a window for viewport {viewport_id:?}: {err}");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a surface, window, and winit integration for the viewport, if missing.
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) fn init_viewport(
|
||||
pub(crate) fn initialize_window(
|
||||
&mut self,
|
||||
viewport_id: ViewportId,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
@@ -1013,12 +1010,16 @@ impl GlutinWindowContext {
|
||||
let window = if let Some(window) = &mut viewport.window {
|
||||
window
|
||||
} else {
|
||||
log::trace!("Window doesn't exist yet. Creating one now with finalize_window");
|
||||
log::debug!("Creating a window for viewport {viewport_id:?}");
|
||||
let window_builder = egui_winit::create_winit_window_builder(
|
||||
&self.egui_ctx,
|
||||
event_loop,
|
||||
viewport.builder.clone(),
|
||||
);
|
||||
if window_builder.transparent() && self.gl_config.supports_transparency() == Some(false)
|
||||
{
|
||||
log::error!("Cannot create transparent window: the GL config does not support it");
|
||||
}
|
||||
let window =
|
||||
glutin_winit::finalize_window(event_loop, window_builder, &self.gl_config)?;
|
||||
egui_winit::apply_viewport_builder_to_window(
|
||||
@@ -1031,7 +1032,20 @@ impl GlutinWindowContext {
|
||||
viewport.window.insert(Rc::new(window))
|
||||
};
|
||||
|
||||
{
|
||||
viewport.egui_winit.get_or_insert_with(|| {
|
||||
log::debug!("Initializing egui_winit for viewport {viewport_id:?}");
|
||||
egui_winit::State::new(
|
||||
self.egui_ctx.clone(),
|
||||
viewport_id,
|
||||
event_loop,
|
||||
Some(window.scale_factor() as f32),
|
||||
self.max_texture_side,
|
||||
)
|
||||
});
|
||||
|
||||
if viewport.gl_surface.is_none() {
|
||||
log::debug!("Creating a gl_surface for viewport {viewport_id:?}");
|
||||
|
||||
// surface attributes
|
||||
let (width_px, height_px): (u32, u32) = window.inner_size().into();
|
||||
let width_px = std::num::NonZeroU32::new(width_px.at_least(1)).unwrap();
|
||||
@@ -1071,24 +1085,14 @@ impl GlutinWindowContext {
|
||||
// we will reach this point only once in most platforms except android.
|
||||
// create window/surface/make context current once and just use them forever.
|
||||
|
||||
viewport.egui_winit.get_or_insert_with(|| {
|
||||
egui_winit::State::new(
|
||||
self.egui_ctx.clone(),
|
||||
viewport_id,
|
||||
event_loop,
|
||||
Some(window.scale_factor() as f32),
|
||||
self.max_texture_side,
|
||||
)
|
||||
});
|
||||
|
||||
viewport.gl_surface = Some(gl_surface);
|
||||
|
||||
self.current_gl_context = Some(current_gl_context);
|
||||
self.viewport_from_window
|
||||
.insert(window.id(), viewport.ids.this);
|
||||
self.window_from_viewport
|
||||
.insert(viewport.ids.this, window.id());
|
||||
}
|
||||
|
||||
self.viewport_from_window.insert(window.id(), viewport_id);
|
||||
self.window_from_viewport.insert(viewport_id, window.id());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1153,6 +1157,7 @@ impl GlutinWindowContext {
|
||||
|
||||
fn handle_viewport_output(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
egui_ctx: &egui::Context,
|
||||
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
|
||||
self.viewports
|
||||
.retain(|id, _| active_viewports_ids.contains(id));
|
||||
@@ -1295,10 +1303,12 @@ fn render_immediate_viewport(
|
||||
viewport_ui_cb,
|
||||
} = immediate_viewport;
|
||||
|
||||
let viewport_id = ids.this;
|
||||
|
||||
{
|
||||
let mut glutin = glutin.borrow_mut();
|
||||
|
||||
let viewport = initialize_or_update_viewport(
|
||||
initialize_or_update_viewport(
|
||||
egui_ctx,
|
||||
&mut glutin.viewports,
|
||||
ids,
|
||||
@@ -1308,17 +1318,18 @@ fn render_immediate_viewport(
|
||||
None,
|
||||
);
|
||||
|
||||
if viewport.gl_surface.is_none() {
|
||||
glutin
|
||||
.init_viewport(ids.this, event_loop)
|
||||
.expect("Failed to initialize window in egui::Context::show_viewport_immediate");
|
||||
if let Err(err) = glutin.initialize_window(viewport_id, event_loop) {
|
||||
log::error!(
|
||||
"Failed to initialize a window for immediate viewport {viewport_id:?}: {err}"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let input = {
|
||||
let mut glutin = glutin.borrow_mut();
|
||||
|
||||
let Some(viewport) = glutin.viewports.get_mut(&ids.this) else {
|
||||
let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else {
|
||||
return;
|
||||
};
|
||||
let (Some(egui_winit), Some(window)) = (&mut viewport.egui_winit, &viewport.window) else {
|
||||
@@ -1362,7 +1373,7 @@ fn render_immediate_viewport(
|
||||
..
|
||||
} = &mut *glutin;
|
||||
|
||||
let Some(viewport) = viewports.get_mut(&ids.this) else {
|
||||
let Some(viewport) = viewports.get_mut(&viewport_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -1423,7 +1434,7 @@ fn render_immediate_viewport(
|
||||
|
||||
egui_winit.handle_platform_output(window, platform_output);
|
||||
|
||||
glutin.handle_viewport_output(egui_ctx, viewport_output);
|
||||
glutin.handle_viewport_output(event_loop, egui_ctx, viewport_output);
|
||||
}
|
||||
|
||||
#[cfg(feature = "__screenshot")]
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
//! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`].
|
||||
//! When making changes to one you often also want to apply it to the other.
|
||||
//!
|
||||
//! This is also very complex code, and not very pretty.
|
||||
//! There is a bunch of improvements we could do,
|
||||
//! like removing a bunch of `unwraps`.
|
||||
|
||||
use std::{cell::RefCell, time::Instant};
|
||||
|
||||
use winit::event_loop::{EventLoop, EventLoopBuilder};
|
||||
@@ -34,12 +27,12 @@ fn create_event_loop_builder(
|
||||
event_loop_builder
|
||||
}
|
||||
|
||||
fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop<UserEvent> {
|
||||
fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result<EventLoop<UserEvent>> {
|
||||
crate::profile_function!();
|
||||
let mut builder = create_event_loop_builder(native_options);
|
||||
|
||||
crate::profile_scope!("EventLoopBuilder::build");
|
||||
builder.build()
|
||||
Ok(builder.build()?)
|
||||
}
|
||||
|
||||
/// Access a thread-local event loop.
|
||||
@@ -49,16 +42,20 @@ fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop<UserE
|
||||
fn with_event_loop<R>(
|
||||
mut native_options: epi::NativeOptions,
|
||||
f: impl FnOnce(&mut EventLoop<UserEvent>, epi::NativeOptions) -> R,
|
||||
) -> R {
|
||||
) -> Result<R> {
|
||||
thread_local!(static EVENT_LOOP: RefCell<Option<EventLoop<UserEvent>>> = RefCell::new(None));
|
||||
|
||||
EVENT_LOOP.with(|event_loop| {
|
||||
// Since we want to reference NativeOptions when creating the EventLoop we can't
|
||||
// do that as part of the lazy thread local storage initialization and so we instead
|
||||
// create the event loop lazily here
|
||||
let mut event_loop = event_loop.borrow_mut();
|
||||
let event_loop = event_loop.get_or_insert_with(|| create_event_loop(&mut native_options));
|
||||
f(event_loop, native_options)
|
||||
let mut event_loop_lock = event_loop.borrow_mut();
|
||||
let event_loop = if let Some(event_loop) = &mut *event_loop_lock {
|
||||
event_loop
|
||||
} else {
|
||||
event_loop_lock.insert(create_event_loop(&mut native_options)?)
|
||||
};
|
||||
Ok(f(event_loop, native_options))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -67,31 +64,39 @@ fn run_and_return(
|
||||
event_loop: &mut EventLoop<UserEvent>,
|
||||
mut winit_app: impl WinitApp,
|
||||
) -> Result<()> {
|
||||
use winit::{event_loop::ControlFlow, platform::run_return::EventLoopExtRunReturn as _};
|
||||
use winit::{event_loop::ControlFlow, platform::run_on_demand::EventLoopExtRunOnDemand};
|
||||
|
||||
log::debug!("Entering the winit event loop (run_return)…");
|
||||
log::debug!("Entering the winit event loop (run_on_demand)…");
|
||||
|
||||
// When to repaint what window
|
||||
let mut windows_next_repaint_times = HashMap::default();
|
||||
|
||||
let mut returned_result = Ok(());
|
||||
|
||||
event_loop.run_return(|event, event_loop, control_flow| {
|
||||
event_loop.run_on_demand(|event, event_loop_window_target| {
|
||||
crate::profile_scope!("winit_event", short_event_description(&event));
|
||||
|
||||
log::trace!("winit event: {event:?}");
|
||||
|
||||
if matches!(event, winit::event::Event::AboutToWait) {
|
||||
return; // early-out: don't trigger another wait
|
||||
}
|
||||
|
||||
let event_result = match &event {
|
||||
winit::event::Event::LoopDestroyed => {
|
||||
// On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name),
|
||||
winit::event::Event::LoopExiting => {
|
||||
// On Mac, Cmd-Q we get here and then `run_on_demand` doesn't return (despite its name),
|
||||
// so we need to save state now:
|
||||
log::debug!("Received Event::LoopDestroyed - saving app state…");
|
||||
log::debug!("Received Event::LoopExiting - saving app state…");
|
||||
winit_app.save_and_destroy();
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
|
||||
winit::event::Event::RedrawRequested(window_id) => {
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::RedrawRequested,
|
||||
window_id,
|
||||
} => {
|
||||
windows_next_repaint_times.remove(window_id);
|
||||
winit_app.run_ui_and_paint(*window_id)
|
||||
winit_app.run_ui_and_paint(event_loop_window_target, *window_id)
|
||||
}
|
||||
|
||||
winit::event::Event::UserEvent(UserEvent::RequestRepaint {
|
||||
@@ -120,8 +125,11 @@ fn run_and_return(
|
||||
EventResult::Wait
|
||||
}
|
||||
|
||||
event => match winit_app.on_event(event_loop, event) {
|
||||
Ok(event_result) => event_result,
|
||||
event => match winit_app.on_event(event_loop_window_target, event) {
|
||||
Ok(event_result) => {
|
||||
log::trace!("event_result: {event_result:?}");
|
||||
event_result
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Exiting because of error: {err} during event {event:?}");
|
||||
returned_result = Err(err);
|
||||
@@ -132,21 +140,28 @@ fn run_and_return(
|
||||
|
||||
match event_result {
|
||||
EventResult::Wait => {
|
||||
control_flow.set_wait();
|
||||
event_loop_window_target.set_control_flow(ControlFlow::Wait);
|
||||
}
|
||||
EventResult::RepaintNow(window_id) => {
|
||||
log::trace!("Repaint caused by {}", short_event_description(&event));
|
||||
log::trace!(
|
||||
"RepaintNow of {window_id:?} caused by {}",
|
||||
short_event_description(&event)
|
||||
);
|
||||
if cfg!(target_os = "windows") {
|
||||
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
||||
windows_next_repaint_times.remove(&window_id);
|
||||
|
||||
winit_app.run_ui_and_paint(window_id);
|
||||
winit_app.run_ui_and_paint(event_loop_window_target, window_id);
|
||||
} else {
|
||||
// Fix for https://github.com/emilk/egui/issues/2425
|
||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||
}
|
||||
}
|
||||
EventResult::RepaintNext(window_id) => {
|
||||
log::trace!(
|
||||
"RepaintNext of {window_id:?} caused by {}",
|
||||
short_event_description(&event)
|
||||
);
|
||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||
}
|
||||
EventResult::RepaintAt(window_id, repaint_time) => {
|
||||
@@ -160,45 +175,35 @@ fn run_and_return(
|
||||
EventResult::Exit => {
|
||||
log::debug!("Asking to exit event loop…");
|
||||
winit_app.save_and_destroy();
|
||||
*control_flow = ControlFlow::Exit;
|
||||
event_loop_window_target.exit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut next_repaint_time = windows_next_repaint_times.values().min().copied();
|
||||
|
||||
// This is for not duplicating redraw requests
|
||||
use winit::event::Event;
|
||||
if matches!(
|
||||
event,
|
||||
Event::RedrawEventsCleared | Event::RedrawRequested(_) | Event::Resumed
|
||||
) {
|
||||
windows_next_repaint_times.retain(|window_id, repaint_time| {
|
||||
if Instant::now() < *repaint_time {
|
||||
return true;
|
||||
};
|
||||
windows_next_repaint_times.retain(|window_id, repaint_time| {
|
||||
if Instant::now() < *repaint_time {
|
||||
return true; // not yet ready
|
||||
};
|
||||
|
||||
next_repaint_time = None;
|
||||
control_flow.set_poll();
|
||||
next_repaint_time = None;
|
||||
event_loop_window_target.set_control_flow(ControlFlow::Poll);
|
||||
|
||||
if let Some(window) = winit_app.window(*window_id) {
|
||||
log::trace!("request_redraw for {window_id:?}");
|
||||
window.request_redraw();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
if let Some(window) = winit_app.window(*window_id) {
|
||||
log::trace!("request_redraw for {window_id:?}");
|
||||
window.request_redraw();
|
||||
true
|
||||
} else {
|
||||
log::trace!("No window found for {window_id:?}");
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(next_repaint_time) = next_repaint_time {
|
||||
let time_until_next = next_repaint_time.saturating_duration_since(Instant::now());
|
||||
if time_until_next < std::time::Duration::from_secs(10_000) {
|
||||
log::trace!("WaitUntil {time_until_next:?}");
|
||||
}
|
||||
control_flow.set_wait_until(next_repaint_time);
|
||||
event_loop_window_target.set_control_flow(ControlFlow::WaitUntil(next_repaint_time));
|
||||
};
|
||||
});
|
||||
})?;
|
||||
|
||||
log::debug!("eframe window closed");
|
||||
|
||||
@@ -211,32 +216,47 @@ fn run_and_return(
|
||||
// we only apply this approach on Windows to minimize the affect.
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
event_loop.run_return(|_, _, control_flow| {
|
||||
control_flow.set_exit();
|
||||
});
|
||||
event_loop
|
||||
.run_on_demand(|_, event_loop_window_target| {
|
||||
event_loop_window_target.exit();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
returned_result
|
||||
}
|
||||
|
||||
fn run_and_exit(event_loop: EventLoop<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)…");
|
||||
|
||||
// When to repaint what window
|
||||
let mut windows_next_repaint_times = HashMap::default();
|
||||
|
||||
event_loop.run(move |event, event_loop, control_flow| {
|
||||
event_loop.run(move |event, event_loop_window_target| {
|
||||
crate::profile_scope!("winit_event", short_event_description(&event));
|
||||
|
||||
log::trace!("winit event: {event:?}");
|
||||
|
||||
if matches!(event, winit::event::Event::AboutToWait) {
|
||||
return; // early-out: don't trigger another wait
|
||||
}
|
||||
|
||||
let event_result = match &event {
|
||||
winit::event::Event::LoopDestroyed => {
|
||||
log::debug!("Received Event::LoopDestroyed");
|
||||
winit::event::Event::LoopExiting => {
|
||||
log::debug!("Received Event::LoopExiting");
|
||||
EventResult::Exit
|
||||
}
|
||||
|
||||
winit::event::Event::RedrawRequested(window_id) => {
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::RedrawRequested,
|
||||
window_id,
|
||||
} => {
|
||||
windows_next_repaint_times.remove(window_id);
|
||||
winit_app.run_ui_and_paint(*window_id)
|
||||
winit_app.run_ui_and_paint(event_loop_window_target, *window_id)
|
||||
}
|
||||
|
||||
winit::event::Event::UserEvent(UserEvent::RequestRepaint {
|
||||
@@ -264,8 +284,11 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
EventResult::Wait
|
||||
}
|
||||
|
||||
event => match winit_app.on_event(event_loop, event) {
|
||||
Ok(event_result) => event_result,
|
||||
event => match winit_app.on_event(event_loop_window_target, event) {
|
||||
Ok(event_result) => {
|
||||
log::trace!("event_result: {event_result:?}");
|
||||
event_result
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("eframe encountered a fatal error: {err} during event {event:?}");
|
||||
}
|
||||
@@ -274,22 +297,22 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
|
||||
match event_result {
|
||||
EventResult::Wait => {
|
||||
control_flow.set_wait();
|
||||
event_loop_window_target.set_control_flow(ControlFlow::Wait);
|
||||
}
|
||||
EventResult::RepaintNow(window_id) => {
|
||||
log::trace!("Repaint caused by {}", short_event_description(&event));
|
||||
log::trace!("RepaintNow caused by {}", short_event_description(&event));
|
||||
if cfg!(target_os = "windows") {
|
||||
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
||||
windows_next_repaint_times.remove(&window_id);
|
||||
|
||||
winit_app.run_ui_and_paint(window_id);
|
||||
winit_app.run_ui_and_paint(event_loop_window_target, window_id);
|
||||
} else {
|
||||
// Fix for https://github.com/emilk/egui/issues/2425
|
||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||
}
|
||||
}
|
||||
EventResult::RepaintNext(window_id) => {
|
||||
log::trace!("Repaint caused by {}", short_event_description(&event));
|
||||
log::trace!("RepaintNext caused by {}", short_event_description(&event));
|
||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||
}
|
||||
EventResult::RepaintAt(window_id, repaint_time) => {
|
||||
@@ -303,6 +326,8 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
EventResult::Exit => {
|
||||
log::debug!("Quitting - saving app state…");
|
||||
winit_app.save_and_destroy();
|
||||
|
||||
log::debug!("Exiting with return code 0");
|
||||
#[allow(clippy::exit)]
|
||||
std::process::exit(0);
|
||||
}
|
||||
@@ -310,36 +335,25 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
|
||||
let mut next_repaint_time = windows_next_repaint_times.values().min().copied();
|
||||
|
||||
// This is for not duplicating redraw requests
|
||||
use winit::event::Event;
|
||||
if matches!(
|
||||
event,
|
||||
Event::RedrawEventsCleared | Event::RedrawRequested(_) | Event::Resumed
|
||||
) {
|
||||
windows_next_repaint_times.retain(|window_id, repaint_time| {
|
||||
if Instant::now() < *repaint_time {
|
||||
return true;
|
||||
}
|
||||
|
||||
next_repaint_time = None;
|
||||
control_flow.set_poll();
|
||||
|
||||
if let Some(window) = winit_app.window(*window_id) {
|
||||
log::trace!("request_redraw for {window_id:?}");
|
||||
window.request_redraw();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(next_repaint_time) = next_repaint_time {
|
||||
let time_until_next = next_repaint_time.saturating_duration_since(Instant::now());
|
||||
if time_until_next < std::time::Duration::from_secs(10_000) {
|
||||
log::trace!("WaitUntil {time_until_next:?}");
|
||||
windows_next_repaint_times.retain(|window_id, repaint_time| {
|
||||
if Instant::now() < *repaint_time {
|
||||
return true; // not yet ready
|
||||
}
|
||||
|
||||
next_repaint_time = None;
|
||||
event_loop_window_target.set_control_flow(ControlFlow::Poll);
|
||||
|
||||
if let Some(window) = winit_app.window(*window_id) {
|
||||
log::trace!("request_redraw for {window_id:?}");
|
||||
window.request_redraw();
|
||||
true
|
||||
} else {
|
||||
log::trace!("No window found for {window_id:?}");
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(next_repaint_time) = next_repaint_time {
|
||||
// WaitUntil seems to not work on iOS
|
||||
#[cfg(target_os = "ios")]
|
||||
winit_app
|
||||
@@ -350,9 +364,13 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
.map(|window| window.request_redraw())
|
||||
});
|
||||
|
||||
control_flow.set_wait_until(next_repaint_time);
|
||||
event_loop_window_target.set_control_flow(ControlFlow::WaitUntil(next_repaint_time));
|
||||
};
|
||||
})
|
||||
})?;
|
||||
|
||||
log::debug!("winit event loop unexpectedly returned");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -370,12 +388,12 @@ pub fn run_glow(
|
||||
return with_event_loop(native_options, |event_loop, native_options| {
|
||||
let glow_eframe = GlowWinitApp::new(event_loop, app_name, native_options, app_creator);
|
||||
run_and_return(event_loop, glow_eframe)
|
||||
});
|
||||
})?;
|
||||
}
|
||||
|
||||
let event_loop = create_event_loop(&mut native_options);
|
||||
let event_loop = create_event_loop(&mut native_options)?;
|
||||
let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
||||
run_and_exit(event_loop, glow_eframe);
|
||||
run_and_exit(event_loop, glow_eframe)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -393,10 +411,10 @@ pub fn run_wgpu(
|
||||
return with_event_loop(native_options, |event_loop, native_options| {
|
||||
let wgpu_eframe = WgpuWinitApp::new(event_loop, app_name, native_options, app_creator);
|
||||
run_and_return(event_loop, wgpu_eframe)
|
||||
});
|
||||
})?;
|
||||
}
|
||||
|
||||
let event_loop = create_event_loop(&mut native_options);
|
||||
let event_loop = create_event_loop(&mut native_options)?;
|
||||
let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
||||
run_and_exit(event_loop, wgpu_eframe);
|
||||
run_and_exit(event_loop, wgpu_eframe)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//! Note that this file contains code very similar to [`glow_integration`].
|
||||
//! When making changes to one you often also want to apply it to the other.
|
||||
//!
|
||||
//! This is also very complex code, and not very pretty.
|
||||
//! There is a bunch of improvements we could do,
|
||||
//! like removing a bunch of `unwraps`.
|
||||
|
||||
use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
@@ -109,7 +116,8 @@ impl WgpuWinitApp {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_windows(&mut self, event_loop: &EventLoopWindowTarget<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 {
|
||||
return;
|
||||
};
|
||||
@@ -122,14 +130,12 @@ impl WgpuWinitApp {
|
||||
} = &mut *shared;
|
||||
|
||||
for viewport in viewports.values_mut() {
|
||||
if viewport.window.is_none() {
|
||||
viewport.init_window(
|
||||
&running.integration.egui_ctx,
|
||||
viewport_from_window,
|
||||
painter,
|
||||
event_loop,
|
||||
);
|
||||
}
|
||||
viewport.initialize_window(
|
||||
event_loop,
|
||||
&running.integration.egui_ctx,
|
||||
viewport_from_window,
|
||||
painter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +356,13 @@ impl WinitApp for WgpuWinitApp {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult {
|
||||
fn run_ui_and_paint(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
window_id: WindowId,
|
||||
) -> EventResult {
|
||||
self.initialized_all_windows(event_loop);
|
||||
|
||||
if let Some(running) = &mut self.running {
|
||||
running.run_ui_and_paint(window_id)
|
||||
} else {
|
||||
@@ -361,14 +373,16 @@ impl WinitApp for WgpuWinitApp {
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
event: &winit::event::Event<'_, UserEvent>,
|
||||
event: &winit::event::Event<UserEvent>,
|
||||
) -> Result<EventResult> {
|
||||
crate::profile_function!(winit_integration::short_event_description(event));
|
||||
|
||||
self.build_windows(event_loop);
|
||||
self.initialized_all_windows(event_loop);
|
||||
|
||||
Ok(match event {
|
||||
winit::event::Event::Resumed => {
|
||||
log::debug!("Event::Resumed");
|
||||
|
||||
let running = if let Some(running) = &self.running {
|
||||
running
|
||||
} else {
|
||||
@@ -660,7 +674,7 @@ impl WgpuWinitRunning {
|
||||
fn on_window_event(
|
||||
&mut self,
|
||||
window_id: WindowId,
|
||||
event: &winit::event::WindowEvent<'_>,
|
||||
event: &winit::event::WindowEvent,
|
||||
) -> EventResult {
|
||||
crate::profile_function!(egui_winit::short_window_event_description(event));
|
||||
|
||||
@@ -709,18 +723,6 @@ impl WgpuWinitRunning {
|
||||
}
|
||||
}
|
||||
|
||||
winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
|
||||
use std::num::NonZeroU32;
|
||||
if let (Some(width), Some(height), Some(viewport_id)) = (
|
||||
NonZeroU32::new(new_inner_size.width),
|
||||
NonZeroU32::new(new_inner_size.height),
|
||||
viewport_id,
|
||||
) {
|
||||
repaint_asap = true;
|
||||
shared.painter.on_window_resized(viewport_id, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
winit::event::WindowEvent::CloseRequested => {
|
||||
if viewport_id == Some(ViewportId::ROOT) && integration.should_close() {
|
||||
log::debug!(
|
||||
@@ -775,13 +777,18 @@ impl WgpuWinitRunning {
|
||||
}
|
||||
|
||||
impl Viewport {
|
||||
fn init_window(
|
||||
/// Create winit window, if needed.
|
||||
fn initialize_window(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
egui_ctx: &egui::Context,
|
||||
windows_id: &mut HashMap<WindowId, ViewportId>,
|
||||
painter: &mut egui_wgpu::winit::Painter,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
) {
|
||||
if self.window.is_some() {
|
||||
return; // we already have one
|
||||
}
|
||||
|
||||
crate::profile_function!();
|
||||
|
||||
let viewport_id = self.ids.this;
|
||||
@@ -870,7 +877,7 @@ fn render_immediate_viewport(
|
||||
None,
|
||||
);
|
||||
if viewport.window.is_none() {
|
||||
viewport.init_window(egui_ctx, viewport_from_window, painter, event_loop);
|
||||
viewport.initialize_window(event_loop, egui_ctx, viewport_from_window, painter);
|
||||
}
|
||||
|
||||
let (Some(window), Some(egui_winit)) = (&viewport.window, &mut viewport.egui_winit) else {
|
||||
|
||||
@@ -74,12 +74,16 @@ pub trait WinitApp {
|
||||
|
||||
fn save_and_destroy(&mut self);
|
||||
|
||||
fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult;
|
||||
fn run_ui_and_paint(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
window_id: WindowId,
|
||||
) -> EventResult;
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
event: &winit::event::Event<'_, UserEvent>,
|
||||
event: &winit::event::Event<UserEvent>,
|
||||
) -> 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.
|
||||
/// Useful for logging and profiling.
|
||||
pub fn short_event_description(event: &winit::event::Event<'_, UserEvent>) -> &'static str {
|
||||
use winit::event::Event;
|
||||
|
||||
pub fn short_event_description(event: &winit::event::Event<UserEvent>) -> &'static str {
|
||||
match event {
|
||||
Event::UserEvent(user_event) => match user_event {
|
||||
winit::event::Event::UserEvent(user_event) => match user_event {
|
||||
UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint",
|
||||
#[cfg(feature = "accesskit")]
|
||||
UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest",
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct AppRunner {
|
||||
app: Box<dyn epi::App>,
|
||||
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
|
||||
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,
|
||||
|
||||
// Output for the last run:
|
||||
@@ -112,7 +112,7 @@ impl AppRunner {
|
||||
app,
|
||||
needs_repaint,
|
||||
last_save_time: now_sec(),
|
||||
text_cursor_pos: None,
|
||||
ime: None,
|
||||
mutable_text_under_cursor: false,
|
||||
textures_delta: Default::default(),
|
||||
clipped_primitives: None,
|
||||
@@ -244,7 +244,7 @@ impl AppRunner {
|
||||
copied_text,
|
||||
events: _, // already handled
|
||||
mutable_text_under_cursor,
|
||||
text_cursor_pos,
|
||||
ime,
|
||||
#[cfg(feature = "accesskit")]
|
||||
accesskit_update: _, // not currently implemented
|
||||
} = platform_output;
|
||||
@@ -264,9 +264,9 @@ impl AppRunner {
|
||||
|
||||
self.mutable_text_under_cursor = mutable_text_under_cursor;
|
||||
|
||||
if self.text_cursor_pos != text_cursor_pos {
|
||||
super::text_agent::move_text_cursor(text_cursor_pos, self.canvas_id());
|
||||
self.text_cursor_pos = text_cursor_pos;
|
||||
if self.ime != ime {
|
||||
super::text_agent::move_text_cursor(ime, self.canvas_id());
|
||||
self.ime = ime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa
|
||||
if let Some(key) = egui_key {
|
||||
runner.input.raw.events.push(egui::Event::Key {
|
||||
key,
|
||||
physical_key: None, // TODO
|
||||
pressed: true,
|
||||
repeat: false, // egui will fill this in for us!
|
||||
modifiers,
|
||||
@@ -157,6 +158,7 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa
|
||||
if let Some(key) = translate_key(&event.key()) {
|
||||
runner.input.raw.events.push(egui::Event::Key {
|
||||
key,
|
||||
physical_key: None, // TODO
|
||||
pressed: false,
|
||||
repeat: false,
|
||||
modifiers,
|
||||
|
||||
@@ -112,91 +112,7 @@ pub fn should_ignore_key(key: &str) -> bool {
|
||||
/// Web sends all keys as strings, so it is up to us to figure out if it is
|
||||
/// a real text input or the name of a key.
|
||||
pub fn translate_key(key: &str) -> Option<egui::Key> {
|
||||
use egui::Key;
|
||||
|
||||
match key {
|
||||
"ArrowDown" => Some(Key::ArrowDown),
|
||||
"ArrowLeft" => Some(Key::ArrowLeft),
|
||||
"ArrowRight" => Some(Key::ArrowRight),
|
||||
"ArrowUp" => Some(Key::ArrowUp),
|
||||
|
||||
"Esc" | "Escape" => Some(Key::Escape),
|
||||
"Tab" => Some(Key::Tab),
|
||||
"Backspace" => Some(Key::Backspace),
|
||||
"Enter" => Some(Key::Enter),
|
||||
"Space" | " " => Some(Key::Space),
|
||||
|
||||
"Help" | "Insert" => Some(Key::Insert),
|
||||
"Delete" => Some(Key::Delete),
|
||||
"Home" => Some(Key::Home),
|
||||
"End" => Some(Key::End),
|
||||
"PageUp" => Some(Key::PageUp),
|
||||
"PageDown" => Some(Key::PageDown),
|
||||
|
||||
"-" => Some(Key::Minus),
|
||||
"+" | "=" => Some(Key::PlusEquals),
|
||||
|
||||
"0" => Some(Key::Num0),
|
||||
"1" => Some(Key::Num1),
|
||||
"2" => Some(Key::Num2),
|
||||
"3" => Some(Key::Num3),
|
||||
"4" => Some(Key::Num4),
|
||||
"5" => Some(Key::Num5),
|
||||
"6" => Some(Key::Num6),
|
||||
"7" => Some(Key::Num7),
|
||||
"8" => Some(Key::Num8),
|
||||
"9" => Some(Key::Num9),
|
||||
|
||||
"a" | "A" => Some(Key::A),
|
||||
"b" | "B" => Some(Key::B),
|
||||
"c" | "C" => Some(Key::C),
|
||||
"d" | "D" => Some(Key::D),
|
||||
"e" | "E" => Some(Key::E),
|
||||
"f" | "F" => Some(Key::F),
|
||||
"g" | "G" => Some(Key::G),
|
||||
"h" | "H" => Some(Key::H),
|
||||
"i" | "I" => Some(Key::I),
|
||||
"j" | "J" => Some(Key::J),
|
||||
"k" | "K" => Some(Key::K),
|
||||
"l" | "L" => Some(Key::L),
|
||||
"m" | "M" => Some(Key::M),
|
||||
"n" | "N" => Some(Key::N),
|
||||
"o" | "O" => Some(Key::O),
|
||||
"p" | "P" => Some(Key::P),
|
||||
"q" | "Q" => Some(Key::Q),
|
||||
"r" | "R" => Some(Key::R),
|
||||
"s" | "S" => Some(Key::S),
|
||||
"t" | "T" => Some(Key::T),
|
||||
"u" | "U" => Some(Key::U),
|
||||
"v" | "V" => Some(Key::V),
|
||||
"w" | "W" => Some(Key::W),
|
||||
"x" | "X" => Some(Key::X),
|
||||
"y" | "Y" => Some(Key::Y),
|
||||
"z" | "Z" => Some(Key::Z),
|
||||
|
||||
"F1" => Some(Key::F1),
|
||||
"F2" => Some(Key::F2),
|
||||
"F3" => Some(Key::F3),
|
||||
"F4" => Some(Key::F4),
|
||||
"F5" => Some(Key::F5),
|
||||
"F6" => Some(Key::F6),
|
||||
"F7" => Some(Key::F7),
|
||||
"F8" => Some(Key::F8),
|
||||
"F9" => Some(Key::F9),
|
||||
"F10" => Some(Key::F10),
|
||||
"F11" => Some(Key::F11),
|
||||
"F12" => Some(Key::F12),
|
||||
"F13" => Some(Key::F13),
|
||||
"F14" => Some(Key::F14),
|
||||
"F15" => Some(Key::F15),
|
||||
"F16" => Some(Key::F16),
|
||||
"F17" => Some(Key::F17),
|
||||
"F18" => Some(Key::F18),
|
||||
"F19" => Some(Key::F19),
|
||||
"F20" => Some(Key::F20),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
egui::Key::from_name(key)
|
||||
}
|
||||
|
||||
pub fn modifiers_from_event(event: &web_sys::KeyboardEvent) -> egui::Modifiers {
|
||||
|
||||
@@ -205,11 +205,13 @@ fn is_mobile() -> Option<bool> {
|
||||
// candidate window moves following text element (agent),
|
||||
// so it appears that the IME candidate window moves with text cursor.
|
||||
// On mobile devices, there is no need to do that.
|
||||
pub fn move_text_cursor(cursor: Option<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();
|
||||
// Note: movint agent on mobile devices will lead to unpredictable scroll.
|
||||
// Note: moving agent on mobile devices will lead to unpredictable scroll.
|
||||
if is_mobile() == Some(false) {
|
||||
cursor.as_ref().and_then(|&egui::Pos2 { x, y }| {
|
||||
ime.as_ref().and_then(|ime| {
|
||||
let egui::Pos2 { x, y } = ime.cursor_rect.left_top();
|
||||
|
||||
let canvas = canvas_element(canvas_id)?;
|
||||
let bounding_rect = text_agent().get_bounding_client_rect();
|
||||
let y = (y + (canvas.scroll_top() + canvas.offset_top()) as f32)
|
||||
|
||||
Reference in New Issue
Block a user