1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 13:20:05 -04:00

Use egui::ViewportBuilder in eframe::NativeOptions (#3572)

* Part of https://github.com/emilk/egui/issues/3556

This PR replaces a bunch of options in `eframe::NativeOptions` with
`egui::ViewportBuilder`. For instance:

``` diff
 let options = eframe::NativeOptions {
-    initial_window_size: Some(egui::vec2(320.0, 240.0)),
-    drag_and_drop_support: true,
+    viewport: egui::ViewportBuilder::default()
+        .with_inner_size([320.0, 240.0])
+        .with_drag_and_drop(true),
     centered: true,
     ..Default::default()
 };
```
This commit is contained in:
Emil Ernerfeldt
2023-11-19 11:08:47 +01:00
committed by GitHub
parent 3a8ed37f49
commit 39e60e367f
26 changed files with 420 additions and 435 deletions

View File

@@ -6,12 +6,6 @@
#![warn(missing_docs)] // Let's keep `epi` well-documented.
#[cfg(not(target_arch = "wasm32"))]
mod icon_data;
#[cfg(not(target_arch = "wasm32"))]
pub use icon_data::IconData;
#[cfg(target_arch = "wasm32")]
use std::any::Any;
@@ -250,74 +244,23 @@ pub enum HardwareAcceleration {
/// Options controlling the behavior of a native window.
///
/// Only a single native window is currently supported.
/// Addintional windows can be opened using (egui viewports)[`egui::viewport`].
///
/// Set the window title and size using [`Self::viewport`].
///
/// ### Application id
/// [`egui::ViewportBuilder::with_app_id`] is used for determining the folder to persist the app to.
///
/// On native the path is picked using [`crate::storage_dir`].
///
/// If you don't set an app id, the title argument to [`crate::run_native`]
/// will be used as app id instead.
#[cfg(not(target_arch = "wasm32"))]
pub struct NativeOptions {
/// Sets whether or not the window will always be on top of other windows at initialization.
pub always_on_top: bool,
/// Show window in maximized mode
pub maximized: bool,
/// On desktop: add window decorations (i.e. a frame around your app)?
/// If false it will be difficult to move and resize the app.
pub decorated: bool,
/// Start in (borderless) fullscreen?
/// Controls the native window of the root viewport.
///
/// Default: `false`.
pub fullscreen: bool,
/// On Mac: the window doesn't have a titlebar, but floating window buttons.
///
/// See [winit's documentation][with_fullsize_content_view] for information on Mac-specific options.
///
/// [with_fullsize_content_view]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowBuilderExtMacOS.html#tymethod.with_fullsize_content_view
#[cfg(target_os = "macos")]
pub fullsize_content: bool,
/// On Windows: enable drag and drop support. Drag and drop can
/// not be disabled on other platforms.
///
/// See [winit's documentation][drag_and_drop] for information on why you
/// might want to disable this on windows.
///
/// [drag_and_drop]: https://docs.rs/winit/latest/x86_64-pc-windows-msvc/winit/platform/windows/trait.WindowBuilderExtWindows.html#tymethod.with_drag_and_drop
pub drag_and_drop_support: bool,
/// The application icon, e.g. in the Windows task bar or the alt-tab menu.
///
/// The default icon is a white `e` on a black background (for "egui" or "eframe").
/// If you prefer the OS default, set this to `None`.
pub icon_data: Option<IconData>,
/// The initial (inner) position of the native window in points (logical pixels).
pub initial_window_pos: Option<egui::Pos2>,
/// The initial inner size of the native window in points (logical pixels).
pub initial_window_size: Option<egui::Vec2>,
/// The minimum inner window size in points (logical pixels).
pub min_window_size: Option<egui::Vec2>,
/// The maximum inner window size in points (logical pixels).
pub max_window_size: Option<egui::Vec2>,
/// Should the app window be resizable?
pub resizable: bool,
/// On desktop: make the window transparent.
///
/// You control the transparency with [`App::clear_color()`].
/// You should avoid having a [`egui::CentralPanel`], or make sure its frame is also transparent.
pub transparent: bool,
/// On desktop: mouse clicks pass through the window, used for non-interactable overlays
/// Generally you would use this in conjunction with always_on_top
pub mouse_passthrough: bool,
/// Whether grant focus when window initially opened. True by default.
pub active: bool,
/// This is where you set things like window title and size.
pub viewport: egui::ViewportBuilder,
/// Turn on vertical syncing, limiting the FPS to the display refresh rate.
///
@@ -419,47 +362,6 @@ pub struct NativeOptions {
#[cfg(feature = "wgpu")]
pub wgpu_options: egui_wgpu::WgpuConfiguration,
/// The application id, used for determining the folder to persist the app to.
///
/// On native the path is picked using [`crate::storage_dir`].
///
/// If you don't set [`Self::app_id`], the title argument to [`crate::run_native`]
/// will be used as app id instead.
///
/// ### On Wayland
/// On Wayland this sets the Application ID for the window.
///
/// The application ID is used in several places of the compositor, e.g. for
/// grouping windows of the same application. It is also important for
/// connecting the configuration of a `.desktop` file with the window, by
/// using the application ID as file name. This allows e.g. a proper icon
/// handling under Wayland.
///
/// See [Waylands XDG shell documentation][xdg-shell] for more information
/// on this Wayland-specific option.
///
/// [xdg-shell]: https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_app_id
///
/// # Example
/// ``` no_run
/// fn main() -> eframe::Result<()> {
///
/// let mut options = eframe::NativeOptions::default();
/// // Set the application ID for Wayland only on Linux
/// #[cfg(target_os = "linux")]
/// {
/// options.app_id = Some("egui-example".to_string());
/// }
///
/// eframe::run_simple_native("My egui App", options, move |ctx, _frame| {
/// egui::CentralPanel::default().show(ctx, |ui| {
/// ui.heading("My egui Application");
/// });
/// })
/// }
/// ```
pub app_id: Option<String>,
/// Controls whether or not the native window position and size will be
/// persisted (only if the "persistence" feature is enabled).
pub persist_window: bool,
@@ -469,7 +371,7 @@ pub struct NativeOptions {
impl Clone for NativeOptions {
fn clone(&self) -> Self {
Self {
icon_data: self.icon_data.clone(),
viewport: self.viewport.clone(),
#[cfg(any(feature = "glow", feature = "wgpu"))]
event_loop_builder: None, // Skip any builder callbacks if cloning
@@ -480,8 +382,6 @@ impl Clone for NativeOptions {
#[cfg(feature = "wgpu")]
wgpu_options: self.wgpu_options.clone(),
app_id: self.app_id.clone(),
..*self
}
}
@@ -491,29 +391,13 @@ impl Clone for NativeOptions {
impl Default for NativeOptions {
fn default() -> Self {
Self {
always_on_top: false,
maximized: false,
decorated: true,
fullscreen: false,
#[cfg(target_os = "macos")]
fullsize_content: false,
// We set a default "egui" or "eframe" icon, which is usually more distinctive than the default OS icon.
icon_data: Some(
IconData::try_from_png_bytes(&include_bytes!("../../data/icon.png")[..]).unwrap(),
),
drag_and_drop_support: true,
initial_window_pos: None,
initial_window_size: None,
min_window_size: None,
max_window_size: None,
resizable: true,
transparent: false,
mouse_passthrough: false,
active: true,
viewport: egui::ViewportBuilder {
icon: Some(std::sync::Arc::new(
crate::icon_data::from_png_bytes(&include_bytes!("../data/icon.png")[..])
.unwrap(),
)),
..Default::default()
},
vsync: true,
multisampling: 0,
@@ -542,8 +426,6 @@ impl Default for NativeOptions {
#[cfg(feature = "wgpu")]
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
app_id: None,
persist_window: true,
}
}

View File

@@ -1,76 +0,0 @@
/// Image data for an application icon.
///
/// Use a square image, e.g. 256x256 pixels.
/// You can use a transparent background.
#[derive(Clone)]
pub struct IconData {
/// RGBA pixels, with separate/unmultiplied alpha.
pub rgba: Vec<u8>,
/// Image width. This should be a multiple of 4.
pub width: u32,
/// Image height. This should be a multiple of 4.
pub height: u32,
}
impl std::fmt::Debug for IconData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IconData")
.field("width", &self.width)
.field("height", &self.height)
.finish_non_exhaustive()
}
}
impl IconData {
/// Convert into [`image::RgbaImage`]
///
/// # Errors
/// If this is not a valid png.
pub fn try_from_png_bytes(png_bytes: &[u8]) -> Result<Self, image::ImageError> {
crate::profile_function!();
let image = image::load_from_memory(png_bytes)?;
Ok(Self::from_image(image))
}
fn from_image(image: image::DynamicImage) -> Self {
let image = image.into_rgba8();
Self {
width: image.width(),
height: image.height(),
rgba: image.into_raw(),
}
}
/// Convert into [`image::RgbaImage`]
///
/// # Errors
/// If `width*height != 4 * rgba.len()`, or if the image is too big.
pub fn to_image(&self) -> Result<image::RgbaImage, String> {
crate::profile_function!();
let Self {
rgba,
width,
height,
} = self.clone();
image::RgbaImage::from_raw(width, height, rgba).ok_or_else(|| "Invalid IconData".to_owned())
}
/// Encode as PNG.
///
/// # Errors
/// The image is invalid, or the PNG encoder failed.
pub fn to_png_bytes(&self) -> Result<Vec<u8>, String> {
crate::profile_function!();
let image = self.to_image()?;
let mut png_bytes: Vec<u8> = Vec::new();
image
.write_to(
&mut std::io::Cursor::new(&mut png_bytes),
image::ImageOutputFormat::Png,
)
.map_err(|err| err.to_string())?;
Ok(png_bytes)
}
}

View File

@@ -0,0 +1,62 @@
//! Helpers for loading [`egui::IconData`].
use egui::IconData;
/// Helpers for working with [`IconData`].
pub trait IconDataExt {
/// Convert into [`image::RgbaImage`]
///
/// # Errors
/// If `width*height != 4 * rgba.len()`, or if the image is too big.
fn to_image(&self) -> Result<image::RgbaImage, String>;
/// Encode as PNG.
///
/// # Errors
/// The image is invalid, or the PNG encoder failed.
fn to_png_bytes(&self) -> Result<Vec<u8>, String>;
}
/// Load the contents of .png file.
///
/// # Errors
/// If this is not a valid png.
pub fn from_png_bytes(png_bytes: &[u8]) -> Result<IconData, image::ImageError> {
crate::profile_function!();
let image = image::load_from_memory(png_bytes)?;
Ok(from_image(image))
}
fn from_image(image: image::DynamicImage) -> IconData {
let image = image.into_rgba8();
IconData {
width: image.width(),
height: image.height(),
rgba: image.into_raw(),
}
}
impl IconDataExt for IconData {
fn to_image(&self) -> Result<image::RgbaImage, String> {
crate::profile_function!();
let Self {
rgba,
width,
height,
} = self.clone();
image::RgbaImage::from_raw(width, height, rgba).ok_or_else(|| "Invalid IconData".to_owned())
}
fn to_png_bytes(&self) -> Result<Vec<u8>, String> {
crate::profile_function!();
let image = self.to_image()?;
let mut png_bytes: Vec<u8> = Vec::new();
image
.write_to(
&mut std::io::Cursor::new(&mut png_bytes),
image::ImageOutputFormat::Png,
)
.map_err(|err| err.to_string())?;
Ok(png_bytes)
}
}

View File

@@ -166,10 +166,19 @@ mod native;
#[cfg(feature = "persistence")]
pub use native::file_storage::storage_dir;
#[cfg(not(target_arch = "wasm32"))]
pub mod icon_data;
/// This is how you start a native (desktop) app.
///
/// The first argument is name of your app, used for the title bar of the native window
/// and the save location of persistence (see [`App::save`]).
/// The first argument is name of your app, which is a an identifier
/// used for the save location of persistence (see [`App::save`]).
/// It is also used as the application id on wayland.
/// If you set no title on the viewport, the app id will be used
/// as the title.
///
/// For details about application ID conventions, see the
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
///
/// Call from `fn main` like this:
/// ``` no_run
@@ -209,7 +218,7 @@ pub use native::file_storage::storage_dir;
#[allow(clippy::needless_pass_by_value)]
pub fn run_native(
app_name: &str,
native_options: NativeOptions,
mut native_options: NativeOptions,
app_creator: AppCreator,
) -> Result<()> {
let renderer = native_options.renderer;
@@ -220,6 +229,10 @@ pub fn run_native(
"EFRAME_SCREENSHOT_TO found without compiling with the '__screenshot' feature"
);
if native_options.viewport.title.is_none() {
native_options.viewport.title = Some(app_name.to_owned());
}
match renderer {
#[cfg(feature = "glow")]
Renderer::Glow => {

View File

@@ -2,16 +2,18 @@
//!
//! TODO(emilk): port this to [`winit`].
use crate::IconData;
use std::sync::Arc;
use egui::IconData;
pub struct AppTitleIconSetter {
title: String,
icon_data: Option<IconData>,
icon_data: Option<Arc<IconData>>,
status: AppIconStatus,
}
impl AppTitleIconSetter {
pub fn new(title: String, icon_data: Option<IconData>) -> Self {
pub fn new(title: String, icon_data: Option<Arc<IconData>>) -> Self {
Self {
title,
icon_data,
@@ -22,7 +24,7 @@ impl AppTitleIconSetter {
/// Call once per frame; we will set the icon when we can.
pub fn update(&mut self) {
if self.status == AppIconStatus::NotSetTryAgain {
self.status = set_title_and_icon(&self.title, self.icon_data.as_ref());
self.status = set_title_and_icon(&self.title, self.icon_data.as_deref());
}
}
}
@@ -71,6 +73,7 @@ fn set_title_and_icon(_title: &str, _icon_data: Option<&IconData>) -> AppIconSta
#[cfg(target_os = "windows")]
#[allow(unsafe_code)]
fn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus {
use crate::icon_data::IconDataExt as _;
use winapi::um::winuser;
// We would get fairly far already with winit's `set_window_icon` (which is exposed to eframe) actually!
@@ -191,6 +194,7 @@ fn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus {
#[cfg(target_os = "macos")]
#[allow(unsafe_code)]
fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconStatus {
use crate::icon_data::IconDataExt as _;
crate::profile_function!();
use cocoa::{
@@ -215,6 +219,10 @@ fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconS
// SAFETY: Accessing raw data from icon in a read-only manner. Icon data is static!
unsafe {
let app = NSApp();
if app.is_null() {
log::debug!("NSApp is null");
return AppIconStatus::NotSetIgnored;
}
if let Some(png_bytes) = png_bytes {
let data = NSData::dataWithBytes_length_(
@@ -222,17 +230,27 @@ fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconS
png_bytes.as_ptr().cast::<std::ffi::c_void>(),
png_bytes.len() as u64,
);
log::trace!("NSImage::initWithData…");
let app_icon = NSImage::initWithData_(NSImage::alloc(nil), data);
crate::profile_scope!("setApplicationIconImage_");
log::trace!("setApplicationIconImage…");
app.setApplicationIconImage_(app_icon);
}
// Change the title in the top bar - for python processes this would be again "python" otherwise.
let main_menu = app.mainMenu();
let app_menu: id = msg_send![main_menu.itemAtIndex_(0), submenu];
crate::profile_scope!("setTitle_");
app_menu.setTitle_(NSString::alloc(nil).init_str(title));
if !main_menu.is_null() {
let item = main_menu.itemAtIndex_(0);
if !item.is_null() {
let app_menu: id = msg_send![item, submenu];
if !app_menu.is_null() {
crate::profile_scope!("setTitle_");
app_menu.setTitle_(NSString::alloc(nil).init_str(title));
}
}
}
// The title in the Dock apparently can't be changed.
// At least these people didn't figure it out either:

View File

@@ -12,75 +12,14 @@ use egui_winit::{EventResponse, WindowSettings};
use crate::{epi, Theme};
pub fn window_builder<E>(
pub fn viewport_builder<E>(
event_loop: &EventLoopWindowTarget<E>,
title: &str,
native_options: &mut epi::NativeOptions,
window_settings: Option<WindowSettings>,
) -> ViewportBuilder {
let epi::NativeOptions {
maximized,
decorated,
fullscreen,
#[cfg(target_os = "macos")]
fullsize_content,
drag_and_drop_support,
icon_data,
initial_window_pos,
initial_window_size,
min_window_size,
max_window_size,
resizable,
transparent,
centered,
active,
..
} = native_options;
crate::profile_function!();
let mut viewport_builder = egui::ViewportBuilder::default()
.with_title(title)
.with_decorations(*decorated)
.with_fullscreen(*fullscreen)
.with_maximized(*maximized)
.with_resizable(*resizable)
.with_transparent(*transparent)
.with_active(*active)
// Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
// We must also keep the window hidden until AccessKit is initialized.
.with_visible(false);
if let Some(icon_data) = icon_data {
viewport_builder =
viewport_builder.with_window_icon(egui::ColorImage::from_rgba_premultiplied(
[icon_data.width as usize, icon_data.height as usize],
&icon_data.rgba,
));
}
#[cfg(target_os = "macos")]
if *fullsize_content {
viewport_builder = viewport_builder
.with_title_hidden(true)
.with_titlebar_transparent(true)
.with_fullsize_content_view(true);
}
#[cfg(all(feature = "wayland", target_os = "linux"))]
{
viewport_builder = match &native_options.app_id {
Some(app_id) => viewport_builder.with_name(app_id, ""),
None => viewport_builder.with_name(title, ""),
};
}
if let Some(min_size) = *min_window_size {
viewport_builder = viewport_builder.with_min_inner_size(min_size);
}
if let Some(max_size) = *max_window_size {
viewport_builder = viewport_builder.with_max_inner_size(max_size);
}
viewport_builder = viewport_builder.with_drag_and_drop(*drag_and_drop_support);
let mut viewport_builder = native_options.viewport.clone();
// Always use the default window size / position on iOS. Trying to restore the previous position
// causes the window to be shown too small.
@@ -94,21 +33,21 @@ pub fn window_builder<E>(
viewport_builder = window_settings.initialize_viewport_builder(viewport_builder);
window_settings.inner_size_points()
} else {
if let Some(pos) = *initial_window_pos {
if let Some(pos) = viewport_builder.position {
viewport_builder = viewport_builder.with_position(pos);
}
if let Some(initial_window_size) = *initial_window_size {
if let Some(initial_window_size) = viewport_builder.inner_size {
let initial_window_size =
initial_window_size.at_most(largest_monitor_point_size(event_loop));
viewport_builder = viewport_builder.with_inner_size(initial_window_size);
}
*initial_window_size
viewport_builder.inner_size
};
#[cfg(not(target_os = "ios"))]
if *centered {
if native_options.centered {
if let Some(monitor) = event_loop.available_monitors().next() {
let monitor_size = monitor.size().to_logical::<f32>(monitor.scale_factor());
let inner_size = inner_size_points.unwrap_or(egui::Vec2 { x: 800.0, y: 600.0 });
@@ -126,18 +65,11 @@ pub fn window_builder<E>(
}
}
pub fn apply_native_options_to_window(
pub fn apply_window_settings(
window: &winit::window::Window,
native_options: &crate::NativeOptions,
window_settings: Option<WindowSettings>,
) {
crate::profile_function!();
use winit::window::WindowLevel;
window.set_window_level(if native_options.always_on_top {
WindowLevel::AlwaysOnTop
} else {
WindowLevel::Normal
});
if let Some(window_settings) = window_settings {
window_settings.initialize_window(window);
@@ -228,8 +160,12 @@ impl EpiIntegration {
};
let app_icon_setter = super::app_icon::AppTitleIconSetter::new(
app_name.to_owned(),
native_options.icon_data.clone(),
native_options
.viewport
.title
.clone()
.unwrap_or_else(|| app_name.to_owned()),
native_options.viewport.icon.clone(),
);
Self {

View File

@@ -6,8 +6,9 @@ use std::{
/// The folder where `eframe` will store its state.
///
/// The given `app_id` is either [`crate::NativeOptions::app_id`] or
/// the title argument to [`crate::run_native`].
/// The given `app_id` is either the
/// [`egui::ViewportBuilder::app_id`] of [`crate::NativeOptions::viewport`]
/// or the title argument to [`crate::run_native`].
///
/// On native the path is picked using [`directories_next::ProjectDirs::data_dir`](https://docs.rs/directories-next/2.0.0/directories_next/struct.ProjectDirs.html#method.data_dir) which is:
/// * Linux: `/home/UserName/.local/share/APP_ID`

View File

@@ -460,7 +460,10 @@ mod glow_integration {
epaint::ahash::HashMap, DeferredViewportUiCallback, ImmediateViewport, NumExt as _,
ViewportClass, ViewportIdMap, ViewportIdPair, ViewportIdSet, ViewportInfo, ViewportOutput,
};
use egui_winit::{create_winit_window_builder, process_viewport_commands, EventResponse};
use egui_winit::{
apply_viewport_builder_to_new_window, create_winit_window_builder,
process_viewport_commands, EventResponse,
};
use crate::native::epi_integration::EpiIntegration;
@@ -885,7 +888,7 @@ mod glow_integration {
.prefer_hardware_accelerated(hardware_acceleration)
.with_depth_size(native_options.depth_buffer)
.with_stencil_size(native_options.stencil_buffer)
.with_transparency(native_options.transparent);
.with_transparency(native_options.viewport.transparent.unwrap_or(false));
// we don't know if multi sampling option is set. so, check if its more than 0.
let config_template_builder = if native_options.multisampling > 0 {
config_template_builder.with_multisampling(
@@ -904,7 +907,7 @@ mod glow_integration {
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_window_builder(Some(create_winit_window_builder(&viewport_builder)));
.with_window_builder(Some(create_winit_window_builder(viewport_builder.clone())));
let (window, gl_config) = {
crate::profile_scope!("DisplayBuilder::build");
@@ -927,6 +930,9 @@ mod glow_integration {
crate::Error::NoGlutinConfigs(config_template_builder.build(), e)
})?
};
if let Some(window) = &window {
apply_viewport_builder_to_new_window(window, &viewport_builder);
}
let gl_display = gl_config.display();
log::debug!(
@@ -1053,9 +1059,10 @@ mod glow_integration {
log::trace!("Window doesn't exist yet. Creating one now with finalize_window");
let window = glutin_winit::finalize_window(
event_loop,
create_winit_window_builder(&viewport.builder),
create_winit_window_builder(viewport.builder.clone()),
&self.gl_config,
)?;
apply_viewport_builder_to_new_window(&window, &viewport.builder);
viewport.info.minimized = window.is_minimized();
viewport.info.maximized = Some(window.is_maximized());
viewport.window.insert(Rc::new(window))
@@ -1349,7 +1356,6 @@ mod glow_integration {
fn create_glutin_windowed_context(
event_loop: &EventLoopWindowTarget<UserEvent>,
storage: Option<&dyn epi::Storage>,
title: &str,
native_options: &mut NativeOptions,
) -> Result<(GlutinWindowContext, egui_glow::Painter)> {
crate::profile_function!();
@@ -1357,7 +1363,7 @@ mod glow_integration {
let window_settings = epi_integration::load_window_settings(storage);
let winit_window_builder =
epi_integration::window_builder(event_loop, title, native_options, window_settings);
epi_integration::viewport_builder(event_loop, native_options, window_settings);
let mut glutin_window_context = unsafe {
GlutinWindowContext::new(winit_window_builder, native_options, event_loop)?
@@ -1368,11 +1374,7 @@ mod glow_integration {
if let Some(viewport) = glutin_window_context.viewports.get(&ViewportId::ROOT) {
if let Some(window) = &viewport.window {
epi_integration::apply_native_options_to_window(
window,
native_options,
window_settings,
);
epi_integration::apply_window_settings(window, window_settings);
}
}
@@ -1399,6 +1401,7 @@ mod glow_integration {
let storage = epi_integration::create_storage(
self.native_options
.viewport
.app_id
.as_ref()
.unwrap_or(&self.app_name),
@@ -1407,7 +1410,6 @@ mod glow_integration {
let (mut glutin, painter) = Self::create_glutin_windowed_context(
event_loop,
storage.as_deref(),
&self.app_name,
&mut self.native_options,
)?;
let gl = painter.gl().clone();
@@ -1470,7 +1472,12 @@ mod glow_integration {
let theme = system_theme.unwrap_or(self.native_options.default_theme);
integration.egui_ctx.set_visuals(theme.egui_visuals());
if self.native_options.mouse_passthrough {
if self
.native_options
.viewport
.mouse_passthrough
.unwrap_or(false)
{
if let Err(err) = glutin.window(ViewportId::ROOT).set_cursor_hittest(false) {
log::warn!("set_cursor_hittest(false) failed: {err}");
}
@@ -1864,7 +1871,10 @@ mod wgpu_integration {
DeferredViewportUiCallback, FullOutput, ImmediateViewport, ViewportClass, ViewportIdMap,
ViewportIdPair, ViewportIdSet, ViewportInfo, ViewportOutput,
};
use egui_winit::{create_winit_window_builder, process_viewport_commands};
use egui_winit::{
apply_viewport_builder_to_new_window, create_winit_window_builder,
process_viewport_commands,
};
use crate::native::epi_integration::EpiIntegration;
@@ -1899,8 +1909,10 @@ mod wgpu_integration {
let viewport_id = self.ids.this;
match create_winit_window_builder(&self.builder).build(event_loop) {
match create_winit_window_builder(self.builder.clone()).build(event_loop) {
Ok(window) => {
apply_viewport_builder_to_new_window(&window, &self.builder);
windows_id.insert(window.id(), viewport_id);
if let Err(err) =
@@ -2046,7 +2058,7 @@ mod wgpu_integration {
self.native_options.depth_buffer,
self.native_options.stencil_buffer,
),
self.native_options.transparent,
self.native_options.viewport.transparent.unwrap_or(false),
);
pollster::block_on(painter.set_window(ViewportId::ROOT, Some(&window)))?;
@@ -2189,20 +2201,20 @@ mod wgpu_integration {
fn create_window(
event_loop: &EventLoopWindowTarget<UserEvent>,
storage: Option<&dyn epi::Storage>,
title: &str,
native_options: &mut NativeOptions,
) -> Result<(Window, ViewportBuilder), winit::error::OsError> {
crate::profile_function!();
let window_settings = epi_integration::load_window_settings(storage);
let window_builder =
epi_integration::window_builder(event_loop, title, native_options, window_settings);
let viewport_builder =
epi_integration::viewport_builder(event_loop, native_options, window_settings);
let window = {
crate::profile_scope!("WindowBuilder::build");
create_winit_window_builder(&window_builder).build(event_loop)?
create_winit_window_builder(viewport_builder.clone()).build(event_loop)?
};
epi_integration::apply_native_options_to_window(&window, native_options, window_settings);
Ok((window, window_builder))
apply_viewport_builder_to_new_window(&window, &viewport_builder);
epi_integration::apply_window_settings(&window, window_settings);
Ok((window, viewport_builder))
}
fn render_immediate_viewport(
@@ -2388,6 +2400,7 @@ mod wgpu_integration {
} else {
let storage = epi_integration::create_storage(
self.native_options
.viewport
.app_id
.as_ref()
.unwrap_or(&self.app_name),
@@ -2395,7 +2408,6 @@ mod wgpu_integration {
let (window, builder) = create_window(
event_loop,
storage.as_deref(),
&self.app_name,
&mut self.native_options,
)?;
self.init_run_state(event_loop, storage, window, builder)?