diff --git a/Cargo.lock b/Cargo.lock index 177798e214d..5e9dd6303d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1261,6 +1261,7 @@ dependencies = [ "document-features", "egui", "log", + "objc", "puffin", "raw-window-handle 0.6.2", "serde", @@ -1322,7 +1323,6 @@ dependencies = [ "image", "log", "mime_guess2", - "objc", "puffin", "resvg", "serde", diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index f8a11ec7a36..0500b6e4b06 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -78,6 +78,9 @@ puffin = { workspace = true, optional = true } serde = { workspace = true, optional = true } webbrowser = { version = "1.0.0", optional = true } +[target.'cfg(target_os = "ios")'.dependencies] +objc = "0.2" + [target.'cfg(any(target_os="linux", target_os="dragonfly", target_os="freebsd", target_os="netbsd", target_os="openbsd"))'.dependencies] smithay-clipboard = { version = "0.7.2", optional = true } diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 1980376b97a..7b4040b40c5 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -18,6 +18,8 @@ use egui::{Pos2, Rect, Theme, Vec2, ViewportBuilder, ViewportCommand, ViewportId pub use winit; pub mod clipboard; +#[cfg(target_os = "ios")] +mod safe_area; mod window_settings; pub use window_settings::WindowSettings; @@ -36,6 +38,9 @@ use winit::{ }; pub fn screen_size_in_pixels(window: &Window) -> egui::Vec2 { + #[cfg(target_os = "ios")] + let size = window.outer_size(); + #[cfg(not(target_os = "ios"))] let size = window.inner_size(); egui::vec2(size.width as f32, size.height as f32) } @@ -269,6 +274,17 @@ impl State { } use winit::event::WindowEvent; + #[cfg(target_os = "ios")] + match &event { + WindowEvent::Resized(_) + | WindowEvent::ScaleFactorChanged { .. } + | WindowEvent::Focused(true) + | WindowEvent::Occluded(false) => { + self.egui_input_mut().safe_area = + Some(egui::SafeArea::from(safe_area::get_ios_safe_area_insets())); + } + _ => {} + } match event { WindowEvent::ScaleFactorChanged { scale_factor, .. } => { let native_pixels_per_point = *scale_factor as f32; diff --git a/crates/egui_extras/src/safe_area.rs b/crates/egui-winit/src/safe_area.rs similarity index 72% rename from crates/egui_extras/src/safe_area.rs rename to crates/egui-winit/src/safe_area.rs index f91fd2efb97..76e1b99b057 100644 --- a/crates/egui_extras/src/safe_area.rs +++ b/crates/egui-winit/src/safe_area.rs @@ -1,4 +1,4 @@ -use egui::{vec2, Sense, Widget}; +use egui::SafeArea; use objc::runtime::Object; use objc::{class, msg_send, sel, sel_impl}; @@ -24,16 +24,13 @@ pub fn get_ios_safe_area_insets() -> UIEdgeInsets { } } -/// Adds spacing at the top on ios devices -#[derive(Default)] -pub struct IosSafeArea; - -impl Widget for IosSafeArea { - fn ui(self, ui: &mut egui::Ui) -> egui::Response { - let (id, rect) = ui.allocate_space(vec2( - ui.available_width(), - get_ios_safe_area_insets().top as f32, - )); - ui.interact(rect, id, Sense::hover()) +impl From for SafeArea { + fn from(value: UIEdgeInsets) -> Self { + SafeArea { + top: value.top as f32, + left: value.left as f32, + bottom: value.bottom as f32, + right: value.right as f32, + } } } diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index e8b0803b958..8d8829b2818 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -23,8 +23,7 @@ use crate::{ input_state::{InputState, MultiTouchInfo, PointerEvent}, interaction, layers::GraphicLayers, - load, - load::{Bytes, Loaders, SizedTexture}, + load::{self, Bytes, Loaders, SizedTexture}, memory::{Options, Theme}, menu, os::OperatingSystem, @@ -35,9 +34,10 @@ use crate::{ viewport::ViewportClass, Align2, CursorIcon, DeferredViewportUiCallback, FontDefinitions, Grid, Id, ImmediateViewport, ImmediateViewportRendererCallback, Key, KeyboardShortcut, Label, LayerId, Memory, - ModifierNames, NumExt, Order, Painter, RawInput, Response, RichText, ScrollArea, Sense, Style, - TextStyle, TextureHandle, TextureOptions, Ui, ViewportBuilder, ViewportCommand, ViewportId, - ViewportIdMap, ViewportIdPair, ViewportIdSet, ViewportOutput, Widget, WidgetRect, WidgetText, + ModifierNames, NumExt, Order, Painter, RawInput, Response, RichText, SafeArea, ScrollArea, + Sense, Style, TextStyle, TextureHandle, TextureOptions, Ui, ViewportBuilder, ViewportCommand, + ViewportId, ViewportIdMap, ViewportIdPair, ViewportIdSet, ViewportOutput, Widget, WidgetRect, + WidgetText, }; #[cfg(feature = "accesskit")] @@ -407,6 +407,7 @@ struct ContextImpl { animation_manager: AnimationManager, plugins: Plugins, + safe_area: SafeArea, /// All viewports share the same texture manager and texture namespace. /// @@ -452,6 +453,10 @@ impl ContextImpl { .unwrap_or_default(); let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent_id); + if let Some(safe_area) = new_raw_input.safe_area { + self.safe_area = safe_area; + } + let is_outermost_viewport = self.viewport_stack.is_empty(); // not necessarily root, just outermost immediate viewport self.viewport_stack.push(ids); @@ -495,7 +500,7 @@ impl ContextImpl { let screen_rect = viewport.input.screen_rect; - viewport.this_pass.begin_pass(screen_rect); + viewport.this_pass.begin_pass(screen_rect, self.safe_area); { let area_order = self.memory.areas().order_map(); diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index 3601fcae46e..cbd7c8cf858 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -7,6 +7,15 @@ use crate::{ Key, Theme, ViewportId, ViewportIdMap, }; +#[derive(Debug, PartialEq, Copy, Clone, Default)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct SafeArea { + pub top: f32, + pub right: f32, + pub bottom: f32, + pub left: f32, +} + /// What the integrations provides to egui at the start of each frame. /// /// Set the values that make sense, leave the rest at their `Default::default()`. @@ -27,6 +36,11 @@ pub struct RawInput { /// Information about all egui viewports. pub viewports: ViewportIdMap, + /// mobile safe area + /// 0,0,0,0 on desktop + /// `None` will be treated as "same as last frame" + pub safe_area: Option, + /// Position and size of the area that egui should use, in points. /// Usually you would set this to /// @@ -98,6 +112,7 @@ impl Default for RawInput { dropped_files: Default::default(), focused: true, // integrations opt into global focus tracking system_theme: None, + safe_area: Default::default(), } } } @@ -131,6 +146,7 @@ impl RawInput { dropped_files: std::mem::take(&mut self.dropped_files), focused: self.focused, system_theme: self.system_theme, + safe_area: self.safe_area, } } @@ -149,6 +165,7 @@ impl RawInput { mut dropped_files, focused, system_theme, + safe_area, } = newer; self.viewport_id = viewport_ids; @@ -163,6 +180,7 @@ impl RawInput { self.dropped_files.append(&mut dropped_files); self.focused = focused; self.system_theme = system_theme; + self.safe_area = safe_area; } } @@ -1078,6 +1096,7 @@ impl RawInput { dropped_files, focused, system_theme, + safe_area, } = self; ui.label(format!("Active viwport: {viewport_id:?}")); @@ -1103,6 +1122,7 @@ impl RawInput { ui.label(format!("dropped_files: {}", dropped_files.len())); ui.label(format!("focused: {focused}")); ui.label(format!("system_theme: {system_theme:?}")); + ui.label(format!("safe_area: {safe_area:?}")); ui.scope(|ui| { ui.set_min_height(150.0); ui.label(format!("events: {events:#?}")) diff --git a/crates/egui/src/pass_state.rs b/crates/egui/src/pass_state.rs index bbeaca9b3c6..c4d3c83776b 100644 --- a/crates/egui/src/pass_state.rs +++ b/crates/egui/src/pass_state.rs @@ -1,6 +1,8 @@ use ahash::{HashMap, HashSet}; -use crate::{id::IdSet, style, Align, Id, IdMap, LayerId, Rangef, Rect, Vec2, WidgetRects}; +use crate::{ + id::IdSet, style, Align, Id, IdMap, LayerId, Rangef, Rect, SafeArea, Vec2, WidgetRects, +}; #[cfg(debug_assertions)] use crate::{pos2, Align2, Color32, FontId, NumExt, Painter}; @@ -247,7 +249,11 @@ impl Default for PassState { } impl PassState { - pub(crate) fn begin_pass(&mut self, screen_rect: Rect) { + pub(crate) fn begin_pass(&mut self, screen_rect: Rect, safe_area: SafeArea) { + let screen_rect = Rect::from_min_max( + screen_rect.min + emath::vec2(safe_area.left, safe_area.top), + screen_rect.max - emath::vec2(safe_area.right, safe_area.bottom), + ); crate::profile_function!(); let Self { used_ids, diff --git a/crates/egui_extras/Cargo.toml b/crates/egui_extras/Cargo.toml index 4b223ab0b57..b13a518e8d0 100644 --- a/crates/egui_extras/Cargo.toml +++ b/crates/egui_extras/Cargo.toml @@ -67,7 +67,6 @@ svg = ["resvg"] ## Enable better syntax highlighting using [`syntect`](https://docs.rs/syntect). syntect = ["dep:syntect"] -ios-safe-area = ["dep:objc"] [dependencies] egui = { workspace = true, default-features = false } @@ -103,9 +102,6 @@ syntect = { version = "5", optional = true, default-features = false, features = "default-fancy", ] } -# ios -objc = { version = "0.2", optional = true } - # svg feature resvg = { version = "0.37", optional = true, default-features = false } diff --git a/crates/egui_extras/src/lib.rs b/crates/egui_extras/src/lib.rs index 4e8f68b03a4..ab2dde735b9 100644 --- a/crates/egui_extras/src/lib.rs +++ b/crates/egui_extras/src/lib.rs @@ -79,5 +79,3 @@ macro_rules! log_or_panic { }}; } pub(crate) use log_or_panic; -#[cfg(feature = "ios-safe-area")] -pub mod safe_area;