diff --git a/Cargo.toml b/Cargo.toml index fce483f3f..dce38100c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ name = "ui" required-features = ["render"] [dependencies] -bevy = { version = "0.12", default-features = false, features = [ +bevy = { version = "0.13", default-features = false, features = [ "bevy_asset", ] } egui = { version = "0.24.0", default-features = false, features = ["bytemuck"] } @@ -52,7 +52,7 @@ thread_local = { version = "1.1.0", optional = true } [dev-dependencies] once_cell = "1.16.0" version-sync = "0.9.4" -bevy = { version = "0.12", default-features = false, features = [ +bevy = { version = "0.13", default-features = false, features = [ "x11", "png", "bevy_pbr", diff --git a/examples/render_to_image_widget.rs b/examples/render_to_image_widget.rs index 9f1f10c17..e8ba75e07 100644 --- a/examples/render_to_image_widget.rs +++ b/examples/render_to_image_widget.rs @@ -1,8 +1,7 @@ use bevy::{ - core_pipeline::clear_color::ClearColorConfig, prelude::*, render::{ - camera::RenderTarget, + camera::{ClearColorConfig, RenderTarget}, render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, @@ -70,7 +69,7 @@ fn setup( egui_user_textures.add_image(image_handle.clone()); commands.insert_resource(CubePreviewImage(image_handle.clone())); - let cube_handle = meshes.add(Mesh::from(shape::Cube { size: 4.0 })); + let cube_handle = meshes.add(Cuboid::new(4.0, 4.0, 4.0)); let default_material = StandardMaterial { base_color: Color::rgb(0.8, 0.7, 0.6), reflectance: 0.02, @@ -102,14 +101,11 @@ fn setup( commands .spawn(Camera3dBundle { - camera_3d: Camera3d { - clear_color: ClearColorConfig::Custom(Color::rgba(1.0, 1.0, 1.0, 0.0)), - ..default() - }, camera: Camera { // render before the "main pass" camera order: -1, target: RenderTarget::Image(image_handle), + clear_color: ClearColorConfig::Custom(Color::rgba(1.0, 1.0, 1.0, 0.0)), ..default() }, transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) @@ -119,7 +115,7 @@ fn setup( .insert(preview_pass_layer); let cube_size = 4.0; - let cube_handle = meshes.add(Mesh::from(shape::Box::new(cube_size, cube_size, cube_size))); + let cube_handle = meshes.add(Cuboid::new(cube_size, cube_size, cube_size)); let main_material_handle = materials.add(default_material); @@ -210,13 +206,12 @@ fn color_picker_widget(ui: &mut egui::Ui, color: &mut Color) -> egui::Response { egui::color_picker::Alpha::Opaque, ); let [r, g, b, a] = egui_color.to_srgba_unmultiplied(); - *color = [ + *color = Color::rgba( r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, a as f32 / 255.0, - ] - .into(); + ); res } diff --git a/examples/side_panel.rs b/examples/side_panel.rs index c2b3446bf..ff739a82c 100644 --- a/examples/side_panel.rs +++ b/examples/side_panel.rs @@ -75,16 +75,13 @@ fn setup_system( mut materials: ResMut>, ) { commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { - size: 5.0, - subdivisions: 0, - })), - material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + mesh: meshes.add(Plane3d::default().mesh().size(5.0, 5.0)), + material: materials.add(Color::rgb(0.3, 0.5, 0.3)), ..Default::default() }); commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), - material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), + mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), + material: materials.add(Color::rgb(0.8, 0.7, 0.6)), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..Default::default() }); diff --git a/examples/ui.rs b/examples/ui.rs index 689f4156f..91d8174bb 100644 --- a/examples/ui.rs +++ b/examples/ui.rs @@ -55,7 +55,7 @@ fn configure_ui_state_system(mut ui_state: ResMut) { } fn update_ui_scale_factor_system( - keyboard_input: Res>, + keyboard_input: Res>, mut toggle_scale_factor: Local>, mut egui_settings: ResMut, windows: Query<&Window, With>, diff --git a/src/egui_node.rs b/src/egui_node.rs index ff6f76550..43d81041a 100644 --- a/src/egui_node.rs +++ b/src/egui_node.rs @@ -9,16 +9,17 @@ use bevy::{ ecs::world::{FromWorld, World}, prelude::{Entity, Handle, Resource}, render::{ + render_asset::RenderAssetUsages, render_graph::{Node, NodeRunError, RenderGraphContext}, render_resource::{ - BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, - BlendComponent, BlendFactor, BlendOperation, BlendState, Buffer, BufferAddress, - BufferBindingType, BufferDescriptor, BufferUsages, ColorTargetState, ColorWrites, - Extent3d, FragmentState, FrontFace, IndexFormat, LoadOp, MultisampleState, Operations, - PipelineCache, PrimitiveState, RenderPassColorAttachment, RenderPassDescriptor, - RenderPipelineDescriptor, SamplerBindingType, Shader, ShaderStages, ShaderType, - SpecializedRenderPipeline, TextureDimension, TextureFormat, TextureSampleType, - TextureViewDimension, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, + BindGroupLayout, BindGroupLayoutEntry, BindingType, BlendComponent, BlendFactor, + BlendOperation, BlendState, Buffer, BufferAddress, BufferBindingType, BufferDescriptor, + BufferUsages, ColorTargetState, ColorWrites, Extent3d, FragmentState, FrontFace, + IndexFormat, LoadOp, MultisampleState, Operations, PipelineCache, PrimitiveState, + RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, + SamplerBindingType, Shader, ShaderStages, ShaderType, SpecializedRenderPipeline, + StoreOp, TextureDimension, TextureFormat, TextureSampleType, TextureViewDimension, + VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, }, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::{Image, ImageSampler}, @@ -42,43 +43,41 @@ impl FromWorld for EguiPipeline { fn from_world(render_world: &mut World) -> Self { let render_device = render_world.get_resource::().unwrap(); - let transform_bind_group_layout = - render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("egui transform bind group layout"), - entries: &[BindGroupLayoutEntry { + let transform_bind_group_layout = render_device.create_bind_group_layout( + "egui transform bind group layout", + &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::VERTEX, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(EguiTransform::min_size()), + }, + count: None, + }], + ); + + let texture_bind_group_layout = render_device.create_bind_group_layout( + "egui texture bind group layout", + &[ + BindGroupLayoutEntry { binding: 0, - visibility: ShaderStages::VERTEX, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(EguiTransform::min_size()), + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + multisampled: false, }, count: None, - }], - }); - - let texture_bind_group_layout = - render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("egui texture bind group layout"), - entries: &[ - BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - BindGroupLayoutEntry { - binding: 1, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }, - ], - }); + }, + BindGroupLayoutEntry { + binding: 1, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Filtering), + count: None, + }, + ], + ); EguiPipeline { transform_bind_group_layout, @@ -200,7 +199,7 @@ impl Node for EguiNode { let render_device = world.get_resource::().unwrap(); - let scale_factor = window_size.scale_factor * egui_settings.scale_factor as f32; + let scale_factor = window_size.scale_factor * egui_settings.scale_factor; if window_size.physical_width == 0.0 || window_size.physical_height == 0.0 { return; } @@ -345,10 +344,12 @@ impl Node for EguiNode { resolve_target: None, ops: Operations { load: LoadOp::Load, - store: true, + store: StoreOp::Store, }, })], depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, }); let Some(pipeline_id) = egui_pipelines.get(&extracted_window.entity) else { @@ -450,6 +451,7 @@ pub(crate) fn color_image_as_bevy_image( TextureDimension::D2, pixels, TextureFormat::Rgba8UnormSrgb, + RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD, ) } } diff --git a/src/lib.rs b/src/lib.rs index 47e28765a..8ec6924b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,7 +96,7 @@ use bevy::{ use bevy::{ app::{App, Plugin, PostUpdate, PreStartup, PreUpdate}, ecs::{ - query::{QueryEntityError, WorldQuery}, + query::{QueryData, QueryEntityError}, schedule::apply_deferred, system::SystemParam, }, @@ -140,7 +140,7 @@ pub struct EguiSettings { /// } /// } /// ``` - pub scale_factor: f64, + pub scale_factor: f32, /// Will be used as a default value for hyperlink [target](https://www.w3schools.com/tags/att_a_target.asp) hints. /// If not specified, `_self` will be used. Only matters in a web browser. #[cfg(feature = "open_url")] @@ -731,8 +731,8 @@ impl Plugin for EguiPlugin { } /// Queries all the Egui related components. -#[derive(WorldQuery)] -#[world_query(mutable)] +#[derive(QueryData)] +#[query_data(mutable)] pub struct EguiContextQuery { /// Window entity. pub window_entity: Entity, @@ -904,6 +904,7 @@ mod tests { ..Default::default() }, ), + ..Default::default() }) .build() .disable::(), diff --git a/src/render_systems.rs b/src/render_systems.rs index 742d2efc0..939699351 100644 --- a/src/render_systems.rs +++ b/src/render_systems.rs @@ -8,7 +8,7 @@ use bevy::{ render::{ extract_resource::ExtractResource, render_asset::RenderAssets, - render_graph::RenderGraph, + render_graph::{RenderGraph, RenderLabel}, render_resource::{ BindGroup, BindGroupEntry, BindingResource, BufferId, CachedRenderPipelineId, DynamicUniformBuffer, PipelineCache, ShaderType, SpecializedRenderPipelines, @@ -54,6 +54,15 @@ pub struct ExtractedEguiTextures<'w> { pub user_textures: Res<'w, EguiUserTextures>, } +/// [`RenderLabel`] type for the Egui pass. +#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] +pub struct EguiPass { + /// Index of the window entity. + pub window_index: u32, + /// Generation of the window entity. + pub window_generation: u32, +} + impl ExtractedEguiTextures<'_> { /// Returns an iterator over all textures (both Egui and Bevy managed). pub fn handles(&self) -> impl Iterator)> + '_ { @@ -78,16 +87,16 @@ pub fn setup_new_windows_render_system( mut render_graph: ResMut, ) { for window in windows.iter() { - let egui_pass = format!("egui-{}-{}", window.index(), window.generation()); + let egui_pass = EguiPass { + window_index: window.index(), + window_generation: window.generation(), + }; let new_node = EguiNode::new(window); render_graph.add_node(egui_pass.clone(), new_node); - render_graph.add_node_edge( - bevy::render::main_graph::node::CAMERA_DRIVER, - egui_pass.to_string(), - ); + render_graph.add_node_edge(bevy::render::graph::CameraDriverLabel, egui_pass); } } @@ -140,10 +149,12 @@ pub fn prepare_egui_transforms_system( egui_transforms.offsets.clear(); for (window, size) in window_sizes.iter() { - let offset = egui_transforms.buffer.push(EguiTransform::from_window_size( - *size, - egui_settings.scale_factor as f32, - )); + let offset = egui_transforms + .buffer + .push(&EguiTransform::from_window_size( + *size, + egui_settings.scale_factor, + )); egui_transforms.offsets.insert(window, offset); } diff --git a/src/systems.rs b/src/systems.rs index 7fa589bdc..145e04a47 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -9,10 +9,10 @@ use bevy::{ system::{Local, Res, ResMut, SystemParam}, }, input::{ - keyboard::{KeyCode, KeyboardInput}, + keyboard::{Key, KeyboardInput}, mouse::{MouseButton, MouseButtonInput, MouseScrollUnit, MouseWheel}, touch::TouchInput, - ButtonState, Input, + ButtonState, }, prelude::{Entity, EventReader, Query, Resource, Time}, time::Real, @@ -25,6 +25,7 @@ use std::marker::PhantomData; #[allow(missing_docs)] #[derive(SystemParam)] +// IMPORTANT: remember to add the logic to clear event readers to the `clear` method. pub struct InputEvents<'w, 's> { pub ev_cursor_entered: EventReader<'w, 's, CursorEntered>, pub ev_cursor_left: EventReader<'w, 's, CursorLeft>, @@ -41,7 +42,6 @@ pub struct InputEvents<'w, 's> { impl<'w, 's> InputEvents<'w, 's> { /// Consumes all the events. pub fn clear(&mut self) { - self.ev_touch.read().last(); self.ev_cursor_entered.read().last(); self.ev_cursor_left.read().last(); self.ev_cursor.read().last(); @@ -51,6 +51,7 @@ impl<'w, 's> InputEvents<'w, 's> { self.ev_keyboard_input.read().last(); self.ev_window_focused.read().last(); self.ev_window_created.read().last(); + self.ev_touch.read().last(); } } @@ -58,14 +59,24 @@ impl<'w, 's> InputEvents<'w, 's> { #[derive(Resource, Default)] pub struct TouchId(pub Option); +/// Stores "pressed" state of modifier keys. +/// Will be removed if Bevy adds support for `ButtonInput` (logical keys). +#[derive(Resource, Default, Clone, Copy, Debug)] +pub struct ModifierKeysState { + shift: bool, + ctrl: bool, + alt: bool, + win: bool, +} + #[allow(missing_docs)] #[derive(SystemParam)] pub struct InputResources<'w, 's> { #[cfg(all(feature = "manage_clipboard", not(target_os = "android")))] pub egui_clipboard: Res<'w, crate::EguiClipboard>, - pub keyboard_input: Res<'w, Input>, + pub modifier_keys_state: Local<'s, ModifierKeysState>, #[system_param(ignore)] - _marker: PhantomData<&'s ()>, + _marker: PhantomData<&'w ()>, } #[allow(missing_docs)] @@ -81,7 +92,7 @@ pub struct ContextSystemParams<'w, 's> { /// Processes Bevy input and feeds it to Egui. pub fn process_input_system( mut input_events: InputEvents, - input_resources: InputResources, + mut input_resources: InputResources, mut context_params: ContextSystemParams, egui_settings: Res, mut egui_mouse_position: ResMut, @@ -101,17 +112,37 @@ pub fn process_input_system( }; } - let shift = input_resources.keyboard_input.pressed(KeyCode::ShiftLeft) - || input_resources.keyboard_input.pressed(KeyCode::ShiftRight); - let ctrl = input_resources.keyboard_input.pressed(KeyCode::ControlLeft) - || input_resources - .keyboard_input - .pressed(KeyCode::ControlRight); - let alt = input_resources.keyboard_input.pressed(KeyCode::AltLeft) - || input_resources.keyboard_input.pressed(KeyCode::AltRight); - let win = input_resources.keyboard_input.pressed(KeyCode::SuperLeft) - || input_resources.keyboard_input.pressed(KeyCode::SuperRight); + let mut keyboard_input_events = Vec::new(); + for event in input_events.ev_keyboard_input.read() { + // Copy the events as we might want to pass them to an Egui context later. + keyboard_input_events.push(event.clone()); + + let KeyboardInput { + logical_key, state, .. + } = event; + match logical_key { + Key::Shift => { + input_resources.modifier_keys_state.shift = state.is_pressed(); + } + Key::Control => { + input_resources.modifier_keys_state.ctrl = state.is_pressed(); + } + Key::Alt => { + input_resources.modifier_keys_state.alt = state.is_pressed(); + } + Key::Super => { + input_resources.modifier_keys_state.win = state.is_pressed(); + } + _ => {} + }; + } + let ModifierKeysState { + shift, + ctrl, + alt, + win, + } = *input_resources.modifier_keys_state; let mac_cmd = if cfg!(target_os = "macos") { win } else { @@ -153,7 +184,7 @@ pub fn process_input_system( // window this exact frame, so we are safe to ignore all `CursorMoved` events for the window // that has been left. if cursor_left_window != Some(cursor_moved.window) { - let scale_factor = egui_settings.scale_factor as f32; + let scale_factor = egui_settings.scale_factor; let mouse_position: (f32, f32) = (cursor_moved.position / scale_factor).into(); let mut context = context_params .contexts @@ -222,7 +253,7 @@ pub fn process_input_system( if !command || cfg!(target_os = "windows") && ctrl && alt { for event in input_events.ev_received_character.read() { - if !event.char.is_control() { + if event.char.matches(char::is_control).count() == 0 { let mut context = context_params.contexts.get_mut(event.window).unwrap(); context .egui_input @@ -243,15 +274,11 @@ pub fn process_input_system( } }) { - for ev in input_events.ev_keyboard_input.read() { - if let Some(key) = ev.key_code.and_then(bevy_to_egui_key) { - let pressed = match ev.state { - ButtonState::Pressed => true, - ButtonState::Released => false, - }; + for ev in keyboard_input_events { + if let Some(key) = bevy_to_egui_key(&ev.logical_key) { let egui_event = egui::Event::Key { key, - pressed, + pressed: ev.state.is_pressed(), repeat: false, modifiers, }; @@ -260,7 +287,7 @@ pub fn process_input_system( // We also check that it's an `ButtonState::Pressed` event, as we don't want to // copy, cut or paste on the key release. #[cfg(all(feature = "manage_clipboard", not(target_os = "android")))] - if command && pressed { + if command && ev.state.is_pressed() { match key { egui::Key::C => { focused_input.events.push(egui::Event::Copy); @@ -280,7 +307,7 @@ pub fn process_input_system( } for touch in input_events.ev_touch.read() { - let scale_factor = egui_settings.scale_factor as f32; + let scale_factor = egui_settings.scale_factor; let touch_position: (f32, f32) = (touch.position / scale_factor).into(); // Emit touch event @@ -376,14 +403,14 @@ pub fn update_window_contexts_system( let new_window_size = WindowSize::new( context.window.physical_width() as f32, context.window.physical_height() as f32, - context.window.scale_factor() as f32, + context.window.scale_factor(), ); let width = new_window_size.physical_width / new_window_size.scale_factor - / egui_settings.scale_factor as f32; + / egui_settings.scale_factor; let height = new_window_size.physical_height / new_window_size.scale_factor - / egui_settings.scale_factor as f32; + / egui_settings.scale_factor; if width < 1.0 || height < 1.0 { continue; @@ -397,7 +424,7 @@ pub fn update_window_contexts_system( context .ctx .0 - .set_pixels_per_point(new_window_size.scale_factor * egui_settings.scale_factor as f32); + .set_pixels_per_point(new_window_size.scale_factor * egui_settings.scale_factor); *context.window_size = new_window_size; } @@ -460,7 +487,7 @@ pub fn process_output_system( set_icon(); if ctx.has_requested_repaint() { - event.send(RequestRedraw) + event.send(RequestRedraw); } #[cfg(feature = "open_url")] @@ -487,7 +514,7 @@ pub fn process_output_system( fn egui_to_winit_cursor_icon(cursor_icon: egui::CursorIcon) -> Option { match cursor_icon { egui::CursorIcon::Default => Some(bevy::window::CursorIcon::Default), - egui::CursorIcon::PointingHand => Some(bevy::window::CursorIcon::Hand), + egui::CursorIcon::PointingHand => Some(bevy::window::CursorIcon::Pointer), egui::CursorIcon::ResizeHorizontal => Some(bevy::window::CursorIcon::EwResize), egui::CursorIcon::ResizeNeSw => Some(bevy::window::CursorIcon::NeswResize), egui::CursorIcon::ResizeNwSe => Some(bevy::window::CursorIcon::NwseResize), @@ -524,60 +551,62 @@ fn egui_to_winit_cursor_icon(cursor_icon: egui::CursorIcon) -> Option Option { - let key = match key_code { - KeyCode::Down => egui::Key::ArrowDown, - KeyCode::Left => egui::Key::ArrowLeft, - KeyCode::Right => egui::Key::ArrowRight, - KeyCode::Up => egui::Key::ArrowUp, - KeyCode::Escape => egui::Key::Escape, - KeyCode::Tab => egui::Key::Tab, - KeyCode::Back => egui::Key::Backspace, - KeyCode::Return => egui::Key::Enter, - KeyCode::NumpadEnter => egui::Key::Enter, - KeyCode::Space => egui::Key::Space, - KeyCode::Insert => egui::Key::Insert, - KeyCode::Delete => egui::Key::Delete, - KeyCode::Home => egui::Key::Home, - KeyCode::End => egui::Key::End, - KeyCode::PageUp => egui::Key::PageUp, - KeyCode::PageDown => egui::Key::PageDown, - KeyCode::Numpad0 | KeyCode::Key0 => egui::Key::Num0, - KeyCode::Numpad1 | KeyCode::Key1 => egui::Key::Num1, - KeyCode::Numpad2 | KeyCode::Key2 => egui::Key::Num2, - KeyCode::Numpad3 | KeyCode::Key3 => egui::Key::Num3, - KeyCode::Numpad4 | KeyCode::Key4 => egui::Key::Num4, - KeyCode::Numpad5 | KeyCode::Key5 => egui::Key::Num5, - KeyCode::Numpad6 | KeyCode::Key6 => egui::Key::Num6, - KeyCode::Numpad7 | KeyCode::Key7 => egui::Key::Num7, - KeyCode::Numpad8 | KeyCode::Key8 => egui::Key::Num8, - KeyCode::Numpad9 | KeyCode::Key9 => egui::Key::Num9, - KeyCode::A => egui::Key::A, - KeyCode::B => egui::Key::B, - KeyCode::C => egui::Key::C, - KeyCode::D => egui::Key::D, - KeyCode::E => egui::Key::E, - KeyCode::F => egui::Key::F, - KeyCode::G => egui::Key::G, - KeyCode::H => egui::Key::H, - KeyCode::I => egui::Key::I, - KeyCode::J => egui::Key::J, - KeyCode::K => egui::Key::K, - KeyCode::L => egui::Key::L, - KeyCode::M => egui::Key::M, - KeyCode::N => egui::Key::N, - KeyCode::O => egui::Key::O, - KeyCode::P => egui::Key::P, - KeyCode::Q => egui::Key::Q, - KeyCode::R => egui::Key::R, - KeyCode::S => egui::Key::S, - KeyCode::T => egui::Key::T, - KeyCode::U => egui::Key::U, - KeyCode::V => egui::Key::V, - KeyCode::W => egui::Key::W, - KeyCode::X => egui::Key::X, - KeyCode::Y => egui::Key::Y, - KeyCode::Z => egui::Key::Z, +fn bevy_to_egui_key(key: &Key) -> Option { + let key = match key { + Key::ArrowDown => egui::Key::ArrowDown, + Key::ArrowLeft => egui::Key::ArrowLeft, + Key::ArrowRight => egui::Key::ArrowRight, + Key::ArrowUp => egui::Key::ArrowUp, + Key::Escape => egui::Key::Escape, + Key::Tab => egui::Key::Tab, + Key::Backspace => egui::Key::Backspace, + Key::Enter => egui::Key::Enter, + Key::Space => egui::Key::Space, + Key::Insert => egui::Key::Insert, + Key::Delete => egui::Key::Delete, + Key::Home => egui::Key::Home, + Key::End => egui::Key::End, + Key::PageUp => egui::Key::PageUp, + Key::PageDown => egui::Key::PageDown, + Key::Character(c) => match c.to_string().to_lowercase().as_str() { + "0" => egui::Key::Num0, + "1" => egui::Key::Num1, + "2" => egui::Key::Num2, + "3" => egui::Key::Num3, + "4" => egui::Key::Num4, + "5" => egui::Key::Num5, + "6" => egui::Key::Num6, + "7" => egui::Key::Num7, + "8" => egui::Key::Num8, + "9" => egui::Key::Num9, + "a" => egui::Key::A, + "b" => egui::Key::B, + "c" => egui::Key::C, + "d" => egui::Key::D, + "e" => egui::Key::E, + "f" => egui::Key::F, + "g" => egui::Key::G, + "h" => egui::Key::H, + "i" => egui::Key::I, + "j" => egui::Key::J, + "k" => egui::Key::K, + "l" => egui::Key::L, + "m" => egui::Key::M, + "n" => egui::Key::N, + "o" => egui::Key::O, + "p" => egui::Key::P, + "q" => egui::Key::Q, + "r" => egui::Key::R, + "s" => egui::Key::S, + "t" => egui::Key::T, + "u" => egui::Key::U, + "v" => egui::Key::V, + "w" => egui::Key::W, + "x" => egui::Key::X, + "y" => egui::Key::Y, + "z" => egui::Key::Z, + _ => return None, + }, _ => return None, }; Some(key)