diff --git a/.vscode/settings.json b/.vscode/settings.json index 0ac80cfd7b7..dbe8e6629dc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,14 +20,16 @@ "--workspace", "--message-format=json", "--all-targets", + "--all-features", ], "rust-analyzer.cargo.buildScripts.overrideCommand": [ "cargo", - "check", + "cranky", "--quiet", "--target-dir=target_ra", "--workspace", "--message-format=json", "--all-targets", + "--all-features", ], } diff --git a/crates/eframe/src/epi/mod.rs b/crates/eframe/src/epi.rs similarity index 84% rename from crates/eframe/src/epi/mod.rs rename to crates/eframe/src/epi.rs index 0d1a80fcea4..8ee14657cb8 100644 --- a/crates/eframe/src/epi/mod.rs +++ b/crates/eframe/src/epi.rs @@ -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, - - /// The initial (inner) position of the native window in points (logical pixels). - pub initial_window_pos: Option, - - /// The initial inner size of the native window in points (logical pixels). - pub initial_window_size: Option, - - /// The minimum inner window size in points (logical pixels). - pub min_window_size: Option, - - /// The maximum inner window size in points (logical pixels). - pub max_window_size: Option, - - /// 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, - /// 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, } } diff --git a/crates/eframe/src/epi/icon_data.rs b/crates/eframe/src/epi/icon_data.rs deleted file mode 100644 index 078b57714f1..00000000000 --- a/crates/eframe/src/epi/icon_data.rs +++ /dev/null @@ -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, - - /// 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 { - 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 { - 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, String> { - crate::profile_function!(); - let image = self.to_image()?; - let mut png_bytes: Vec = 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) - } -} diff --git a/crates/eframe/src/icon_data.rs b/crates/eframe/src/icon_data.rs new file mode 100644 index 00000000000..847228f9ec0 --- /dev/null +++ b/crates/eframe/src/icon_data.rs @@ -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; + + /// Encode as PNG. + /// + /// # Errors + /// The image is invalid, or the PNG encoder failed. + fn to_png_bytes(&self) -> Result, String>; +} + +/// Load the contents of .png file. +/// +/// # Errors +/// If this is not a valid png. +pub fn from_png_bytes(png_bytes: &[u8]) -> Result { + 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 { + 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, String> { + crate::profile_function!(); + let image = self.to_image()?; + let mut png_bytes: Vec = 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) + } +} diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index 2edc06ab290..5f47335b3a1 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -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 => { diff --git a/crates/eframe/src/native/app_icon.rs b/crates/eframe/src/native/app_icon.rs index 61713055c78..cff0349ac0f 100644 --- a/crates/eframe/src/native/app_icon.rs +++ b/crates/eframe/src/native/app_icon.rs @@ -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, + icon_data: Option>, status: AppIconStatus, } impl AppTitleIconSetter { - pub fn new(title: String, icon_data: Option) -> Self { + pub fn new(title: String, icon_data: Option>) -> 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::(), 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: diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index c906f3c38e0..8b66510efc9 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -12,75 +12,14 @@ use egui_winit::{EventResponse, WindowSettings}; use crate::{epi, Theme}; -pub fn window_builder( +pub fn viewport_builder( event_loop: &EventLoopWindowTarget, - title: &str, native_options: &mut epi::NativeOptions, window_settings: Option, ) -> 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; - - 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); - } + crate::profile_function!(); - 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( 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::(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( } } -pub fn apply_native_options_to_window( +pub fn apply_window_settings( window: &winit::window::Window, - native_options: &crate::NativeOptions, window_settings: Option, ) { 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 { diff --git a/crates/eframe/src/native/file_storage.rs b/crates/eframe/src/native/file_storage.rs index 4c44430f819..79c0c97a071 100644 --- a/crates/eframe/src/native/file_storage.rs +++ b/crates/eframe/src/native/file_storage.rs @@ -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` diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 191970ef4ad..e17b869ab28 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -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, 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, 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)? diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index de814d0abb9..1f05a9fc729 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -1147,12 +1147,8 @@ pub fn process_viewport_commands( }), ViewportCommand::WindowIcon(icon) => { window.set_window_icon(icon.map(|icon| { - winit::window::Icon::from_rgba( - icon.as_raw().to_owned(), - icon.width() as u32, - icon.height() as u32, - ) - .expect("Invalid ICON data!") + winit::window::Icon::from_rgba(icon.rgba.clone(), icon.width, icon.height) + .expect("Invalid ICON data!") })); } ViewportCommand::IMEPosition(pos) => { @@ -1201,8 +1197,8 @@ pub fn process_viewport_commands( } } ViewportCommand::CursorVisible(v) => window.set_cursor_visible(v), - ViewportCommand::CursorHitTest(v) => { - if let Err(err) = window.set_cursor_hittest(v) { + ViewportCommand::MousePassthrough(passthrough) => { + if let Err(err) = window.set_cursor_hittest(!passthrough) { log::warn!("{command:?}: {err}"); } } @@ -1213,84 +1209,109 @@ pub fn process_viewport_commands( } } -pub fn create_winit_window_builder(builder: &ViewportBuilder) -> winit::window::WindowBuilder { +pub fn create_winit_window_builder( + viewport_builder: ViewportBuilder, +) -> winit::window::WindowBuilder { crate::profile_function!(); + let ViewportBuilder { + title, + position, + inner_size, + min_inner_size, + max_inner_size, + fullscreen, + maximized, + resizable, + transparent, + decorations, + icon, + active, + visible, + close_button, + minimize_button, + maximize_button, + window_level, + + // only handled on some platforms: + title_hidden: _title_hidden, + titlebar_transparent: _titlebar_transparent, + fullsize_content_view: _fullsize_content_view, + app_id: _app_id, + drag_and_drop: _drag_and_drop, + + mouse_passthrough: _, // handled in `apply_viewport_builder_to_new_window` + } = viewport_builder; + let mut window_builder = winit::window::WindowBuilder::new() - .with_title( - builder - .title - .clone() - .unwrap_or_else(|| "egui window".to_owned()), - ) - .with_transparent(builder.transparent.unwrap_or(false)) - .with_decorations(builder.decorations.unwrap_or(true)) - .with_resizable(builder.resizable.unwrap_or(true)) - .with_visible(builder.visible.unwrap_or(true)) - .with_maximized(builder.maximized.unwrap_or(false)) + .with_title(title.unwrap_or_else(|| "egui window".to_owned())) + .with_transparent(transparent.unwrap_or(false)) + .with_decorations(decorations.unwrap_or(true)) + .with_resizable(resizable.unwrap_or(true)) + .with_visible(visible.unwrap_or(true)) + .with_maximized(maximized.unwrap_or(false)) + .with_window_level(match window_level { + egui::viewport::WindowLevel::AlwaysOnBottom => WindowLevel::AlwaysOnBottom, + egui::viewport::WindowLevel::AlwaysOnTop => WindowLevel::AlwaysOnTop, + egui::viewport::WindowLevel::Normal => WindowLevel::Normal, + }) .with_fullscreen( - builder - .fullscreen - .and_then(|e| e.then_some(winit::window::Fullscreen::Borderless(None))), + fullscreen.and_then(|e| e.then_some(winit::window::Fullscreen::Borderless(None))), ) .with_enabled_buttons({ let mut buttons = WindowButtons::empty(); - if builder.minimize_button.unwrap_or(true) { + if minimize_button.unwrap_or(true) { buttons |= WindowButtons::MINIMIZE; } - if builder.maximize_button.unwrap_or(true) { + if maximize_button.unwrap_or(true) { buttons |= WindowButtons::MAXIMIZE; } - if builder.close_button.unwrap_or(true) { + if close_button.unwrap_or(true) { buttons |= WindowButtons::CLOSE; } buttons }) - .with_active(builder.active.unwrap_or(true)); + .with_active(active.unwrap_or(true)); - if let Some(inner_size) = builder.inner_size { + if let Some(inner_size) = inner_size { window_builder = window_builder .with_inner_size(winit::dpi::LogicalSize::new(inner_size.x, inner_size.y)); } - if let Some(min_inner_size) = builder.min_inner_size { + if let Some(min_inner_size) = min_inner_size { window_builder = window_builder.with_min_inner_size(winit::dpi::LogicalSize::new( min_inner_size.x, min_inner_size.y, )); } - if let Some(max_inner_size) = builder.max_inner_size { + if let Some(max_inner_size) = max_inner_size { window_builder = window_builder.with_max_inner_size(winit::dpi::LogicalSize::new( max_inner_size.x, max_inner_size.y, )); } - if let Some(position) = builder.position { + if let Some(position) = position { window_builder = window_builder.with_position(winit::dpi::LogicalPosition::new(position.x, position.y)); } - if let Some(icon) = builder.icon.clone() { + if let Some(icon) = icon { window_builder = window_builder.with_window_icon(Some( - winit::window::Icon::from_rgba( - icon.as_raw().to_owned(), - icon.width() as u32, - icon.height() as u32, - ) - .expect("Invalid Icon Data!"), + winit::window::Icon::from_rgba(icon.rgba.clone(), icon.width, icon.height) + .expect("Invalid Icon Data!"), )); } #[cfg(all(feature = "wayland", target_os = "linux"))] - if let Some(name) = builder.name.clone() { + if let Some(app_id) = _app_id { use winit::platform::wayland::WindowBuilderExtWayland as _; - window_builder = window_builder.with_name(name.0, name.1); + window_builder = window_builder.with_name(app_id, ""); } #[cfg(target_os = "windows")] - if let Some(enable) = builder.drag_and_drop { + if let Some(enable) = _drag_and_drop { use winit::platform::windows::WindowBuilderExtWindows as _; window_builder = window_builder.with_drag_and_drop(enable); } @@ -1299,17 +1320,23 @@ pub fn create_winit_window_builder(builder: &ViewportBuilder) -> winit::window:: { use winit::platform::macos::WindowBuilderExtMacOS as _; window_builder = window_builder - .with_title_hidden(builder.title_hidden.unwrap_or(false)) - .with_titlebar_transparent(builder.titlebar_transparent.unwrap_or(false)) - .with_fullsize_content_view(builder.fullsize_content_view.unwrap_or(false)); + .with_title_hidden(_title_hidden.unwrap_or(false)) + .with_titlebar_transparent(_titlebar_transparent.unwrap_or(false)) + .with_fullsize_content_view(_fullsize_content_view.unwrap_or(false)); } - // TODO: implement `ViewportBuilder::hittest` - // Is not implemented because winit in his current state will not allow to set cursor_hittest on a `WindowBuilder` - window_builder } +/// Applies what `create_winit_window_builder` couldn't +pub fn apply_viewport_builder_to_new_window(window: &Window, builder: &ViewportBuilder) { + if let Some(mouse_passthrough) = builder.mouse_passthrough { + if let Err(err) = window.set_cursor_hittest(!mouse_passthrough) { + log::warn!("set_cursor_hittest failed: {err}"); + } + } +} + // --------------------------------------------------------------------------- mod profiling_scopes { diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index 62034cb0120..d92b9bb5977 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -69,7 +69,7 @@ use std::sync::Arc; -use epaint::{ColorImage, Pos2, Vec2}; +use epaint::{Pos2, Vec2}; use crate::{Context, Id}; @@ -153,6 +153,58 @@ pub type ViewportIdMap = nohash_hasher::IntMap; // ---------------------------------------------------------------------------- +/// Image data for an application icon. +/// +/// Use a square image, e.g. 256x256 pixels. +/// You can use a transparent background. +#[derive(Clone, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct IconData { + /// RGBA pixels, with separate/unmultiplied alpha. + pub rgba: Vec, + + /// 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 From for epaint::ColorImage { + fn from(icon: IconData) -> Self { + crate::profile_function!(); + let IconData { + rgba, + width, + height, + } = icon; + epaint::ColorImage::from_rgba_premultiplied([width as usize, height as usize], &rgba) + } +} + +impl From<&IconData> for epaint::ColorImage { + fn from(icon: &IconData) -> Self { + crate::profile_function!(); + let IconData { + rgba, + width, + height, + } = icon; + epaint::ColorImage::from_rgba_premultiplied([*width as usize, *height as usize], rgba) + } +} + +// ---------------------------------------------------------------------------- + /// A pair of [`ViewportId`], used to identify a viewport and its parent. #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -189,6 +241,8 @@ pub type ImmediateViewportRendererCallback = dyn for<'a> Fn(&Context, ImmediateV /// Control the building of a new egui viewport (i.e. native window). /// +/// See [`crate::viewport`] for how to build new viewports (native windows). +/// /// The fields are public, but you should use the builder pattern to set them, /// and that's where you'll find the documentation too. /// @@ -205,8 +259,8 @@ pub struct ViewportBuilder { /// `eframe` will use this as the title of the native window. pub title: Option, - /// This is wayland only. See [`Self::with_name`]. - pub name: Option<(String, String)>, + /// This is wayland only. See [`Self::with_app_id`]. + pub app_id: Option, pub position: Option, pub inner_size: Option, @@ -218,7 +272,7 @@ pub struct ViewportBuilder { pub resizable: Option, pub transparent: Option, pub decorations: Option, - pub icon: Option>, + pub icon: Option>, pub active: Option, pub visible: Option, pub title_hidden: Option, @@ -230,7 +284,9 @@ pub struct ViewportBuilder { pub minimize_button: Option, pub maximize_button: Option, - pub hittest: Option, + pub window_level: WindowLevel, + + pub mouse_passthrough: Option, } impl ViewportBuilder { @@ -290,6 +346,10 @@ impl ViewportBuilder { /// Sets whether the background of the window should be transparent. /// + /// You should avoid having a [`crate::CentralPanel`], or make sure its frame is also transparent. + /// + /// In `eframe` you control the transparency with `eframe::App::clear_color()`. + /// /// If this is `true`, writing colors with alpha values different than /// `1.0` will produce a transparent window. On some platforms this /// is more of a hint for the system and you'd still have the alpha @@ -304,9 +364,12 @@ impl ViewportBuilder { self } - /// The icon needs to be wrapped in Arc because will be cloned every frame + /// 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`. #[inline] - pub fn with_window_icon(mut self, icon: impl Into>) -> Self { + pub fn with_window_icon(mut self, icon: impl Into>) -> Self { self.icon = Some(icon.into()); self } @@ -355,9 +418,11 @@ impl ViewportBuilder { self } - /// Makes the window content appear behind the titlebar. + /// On Mac: the window doesn't have a titlebar, but floating window buttons. /// - /// Mac Os only. + /// 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 #[inline] pub fn with_fullsize_content_view(mut self, value: bool) -> Self { self.fullsize_content_view = Some(value); @@ -402,28 +467,34 @@ impl ViewportBuilder { self } - /// X11 not working! + /// Does not work on X11. #[inline] pub fn with_close_button(mut self, value: bool) -> Self { self.close_button = Some(value); self } - /// X11 not working! + /// Does not work on X11. #[inline] pub fn with_minimize_button(mut self, value: bool) -> Self { self.minimize_button = Some(value); self } - /// X11 not working! + /// Does not work on X11. #[inline] pub fn with_maximize_button(mut self, value: bool) -> Self { self.maximize_button = Some(value); self } - /// This currently only work on windows to be disabled! + /// 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 #[inline] pub fn with_drag_and_drop(mut self, value: bool) -> Self { self.drag_and_drop = Some(value); @@ -437,26 +508,54 @@ impl ViewportBuilder { self } - /// This is wayland only! - /// Build window with the given name. + /// ### 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. /// - /// The `general` name sets an application ID, which should match the `.desktop` - /// file distributed with your program. The `instance` is a `no-op`. + /// The `app_id` should match the `.desktop` file distributed with your program. /// /// 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) + /// + /// [xdg-shell]: https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_app_id + /// + /// ### eframe + /// On eframe, the `app_id` of the root window is also used to determine + /// the storage location of persistence files. #[inline] - pub fn with_name(mut self, id: impl Into, instance: impl Into) -> Self { - self.name = Some((id.into(), instance.into())); + pub fn with_app_id(mut self, app_id: impl Into) -> Self { + self.app_id = Some(app_id.into()); self } - /// Is not implemented for winit - /// You should use `ViewportCommand::CursorHitTest` if you want to set this! - #[deprecated] + /// Control if window i always-on-top, always-on-bottom, or neither. #[inline] - pub fn with_hittest(mut self, value: bool) -> Self { - self.hittest = Some(value); + pub fn with_window_level(mut self, level: WindowLevel) -> Self { + self.window_level = level; + self + } + + /// This window is always on top + #[inline] + pub fn with_always_on_top(self) -> Self { + self.with_window_level(WindowLevel::AlwaysOnTop) + } + + /// On desktop: mouse clicks pass through the window, used for non-interactable overlays. + /// + /// Generally you would use this in conjunction with [`Self::with_transparent`] + /// and [`Self::with_always_on_top`]. + #[inline] + pub fn with_mouse_passthrough(mut self, value: bool) -> Self { + self.mouse_passthrough = Some(value); self } @@ -554,10 +653,10 @@ impl ViewportBuilder { } } - if let Some(new_hittest) = new.hittest { - if Some(new_hittest) != self.hittest { - self.hittest = Some(new_hittest); - commands.push(ViewportCommand::CursorHitTest(new_hittest)); + if let Some(new_mouse_passthrough) = new.mouse_passthrough { + if Some(new_mouse_passthrough) != self.mouse_passthrough { + self.mouse_passthrough = Some(new_mouse_passthrough); + commands.push(ViewportCommand::MousePassthrough(new_mouse_passthrough)); } } @@ -618,33 +717,37 @@ impl ViewportBuilder { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum WindowLevel { + #[default] Normal, AlwaysOnBottom, AlwaysOnTop, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum IMEPurpose { + #[default] Normal, Password, Terminal, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum SystemTheme { + #[default] + SystemDefault, Light, Dark, - SystemDefault, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum CursorGrab { + #[default] None, Confined, Locked, @@ -664,6 +767,8 @@ pub enum ResizeDirection { /// You can send a [`ViewportCommand`] to the viewport with [`Context::send_viewport_cmd`]. /// +/// See [`crate::viewport`] for how to build new viewports (native windows). +/// /// All coordinates are in logical points. /// /// This is essentially a way to diff [`ViewportBuilder`]. @@ -737,7 +842,7 @@ pub enum ViewportCommand { WindowLevel(WindowLevel), /// The the window icon. - WindowIcon(Option>), + WindowIcon(Option>), IMEPosition(Pos2), IMEAllowed(bool), @@ -773,7 +878,8 @@ pub enum ViewportCommand { CursorVisible(bool), - CursorHitTest(bool), + /// Enable mouse pass-through: mouse clicks pass through the window, used for non-interactable overlays. + MousePassthrough(bool), /// Take a screenshot. /// diff --git a/crates/egui_demo_app/src/main.rs b/crates/egui_demo_app/src/main.rs index 177e24a754a..198afde9f32 100644 --- a/crates/egui_demo_app/src/main.rs +++ b/crates/egui_demo_app/src/main.rs @@ -18,9 +18,9 @@ fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - drag_and_drop_support: true, - - initial_window_size: Some([1280.0, 1024.0].into()), + viewport: egui::ViewportBuilder::default() + .with_inner_size([1280.0, 1024.0]) + .with_drag_and_drop(true), #[cfg(feature = "wgpu")] renderer: eframe::Renderer::Wgpu, diff --git a/examples/confirm_exit/src/main.rs b/examples/confirm_exit/src/main.rs index 33d799d3874..f0769e0e33e 100644 --- a/examples/confirm_exit/src/main.rs +++ b/examples/confirm_exit/src/main.rs @@ -5,7 +5,7 @@ use eframe::egui; fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - initial_window_size: Some(egui::vec2(320.0, 240.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), ..Default::default() }; eframe::run_native( diff --git a/examples/custom_3d_glow/src/main.rs b/examples/custom_3d_glow/src/main.rs index 7cd954c390f..cad720b115e 100644 --- a/examples/custom_3d_glow/src/main.rs +++ b/examples/custom_3d_glow/src/main.rs @@ -9,7 +9,7 @@ use std::sync::Arc; fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - initial_window_size: Some(egui::vec2(350.0, 380.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([350.0, 380.0]), multisampling: 4, renderer: eframe::Renderer::Glow, ..Default::default() diff --git a/examples/custom_font/src/main.rs b/examples/custom_font/src/main.rs index bc84223b208..8a852319dd1 100644 --- a/examples/custom_font/src/main.rs +++ b/examples/custom_font/src/main.rs @@ -5,7 +5,7 @@ use eframe::egui; fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - initial_window_size: Some(egui::vec2(320.0, 240.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), ..Default::default() }; eframe::run_native( diff --git a/examples/custom_window_frame/src/main.rs b/examples/custom_window_frame/src/main.rs index 98b24497f26..bda82a4a5f4 100644 --- a/examples/custom_window_frame/src/main.rs +++ b/examples/custom_window_frame/src/main.rs @@ -7,12 +7,12 @@ use eframe::egui::{self, ViewportCommand}; fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - // Hide the OS-specific "chrome" around the window: - decorated: false, - // To have rounded corners we need transparency: - transparent: true, - min_window_size: Some(egui::vec2(400.0, 100.0)), - initial_window_size: Some(egui::vec2(400.0, 240.0)), + viewport: egui::ViewportBuilder::default() + .with_inner_size([400.0, 100.0]) + .with_min_inner_size([400.0, 100.0]) + .with_decorations(false) // Hide the OS-specific "chrome" around the window + .with_transparent(true), // To have rounded corners we need transparency + ..Default::default() }; eframe::run_native( diff --git a/examples/file_dialog/src/main.rs b/examples/file_dialog/src/main.rs index 82b78b8a047..d9ae7dc0815 100644 --- a/examples/file_dialog/src/main.rs +++ b/examples/file_dialog/src/main.rs @@ -5,8 +5,9 @@ use eframe::egui; fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - drag_and_drop_support: true, - initial_window_size: Some(egui::vec2(320.0, 240.0)), + viewport: egui::ViewportBuilder::default() + .with_inner_size([320.0, 240.0]) + .with_drag_and_drop(true), ..Default::default() }; eframe::run_native( diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index 4a64a3bec0a..c27ae5a105e 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -5,7 +5,7 @@ use eframe::egui; fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - initial_window_size: Some(egui::vec2(320.0, 240.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), ..Default::default() }; eframe::run_native( diff --git a/examples/hello_world_par/src/main.rs b/examples/hello_world_par/src/main.rs index 8a568cb1fbf..604939ff10f 100644 --- a/examples/hello_world_par/src/main.rs +++ b/examples/hello_world_par/src/main.rs @@ -10,7 +10,7 @@ use eframe::egui; fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - initial_window_size: Some(egui::vec2(1024.0, 768.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([1024.0, 768.0]), ..Default::default() }; eframe::run_native( diff --git a/examples/hello_world_simple/src/main.rs b/examples/hello_world_simple/src/main.rs index bc815738b22..80e263f4b3d 100644 --- a/examples/hello_world_simple/src/main.rs +++ b/examples/hello_world_simple/src/main.rs @@ -6,7 +6,7 @@ fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - initial_window_size: Some(egui::vec2(320.0, 240.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), ..Default::default() }; diff --git a/examples/images/src/main.rs b/examples/images/src/main.rs index be0364e21ed..9527c4d0411 100644 --- a/examples/images/src/main.rs +++ b/examples/images/src/main.rs @@ -5,7 +5,7 @@ use eframe::egui; fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - initial_window_size: Some(egui::vec2(600.0, 800.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([600.0, 800.0]), ..Default::default() }; eframe::run_native( diff --git a/examples/multiple_viewports/src/main.rs b/examples/multiple_viewports/src/main.rs index de5eee4d73d..e6238030a39 100644 --- a/examples/multiple_viewports/src/main.rs +++ b/examples/multiple_viewports/src/main.rs @@ -10,7 +10,7 @@ use eframe::egui; fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - initial_window_size: Some(egui::vec2(320.0, 240.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), ..Default::default() }; eframe::run_native( diff --git a/examples/save_plot/src/main.rs b/examples/save_plot/src/main.rs index 065ab9755de..3f13d3f28b6 100644 --- a/examples/save_plot/src/main.rs +++ b/examples/save_plot/src/main.rs @@ -7,7 +7,7 @@ fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { - initial_window_size: Some(egui::vec2(350.0, 200.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([350.0, 200.0]), ..Default::default() }; eframe::run_native( diff --git a/examples/serial_windows/src/main.rs b/examples/serial_windows/src/main.rs index 6edccc5e0a5..9e07ca0557c 100644 --- a/examples/serial_windows/src/main.rs +++ b/examples/serial_windows/src/main.rs @@ -11,7 +11,7 @@ fn main() -> Result<(), eframe::Error> { let options = eframe::NativeOptions { run_and_return: true, - initial_window_size: Some(egui::vec2(320.0, 240.0)), + viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), ..Default::default() }; diff --git a/examples/test_viewports/src/main.rs b/examples/test_viewports/src/main.rs index 16394c4af24..bdea553fbad 100644 --- a/examples/test_viewports/src/main.rs +++ b/examples/test_viewports/src/main.rs @@ -12,10 +12,11 @@ fn main() { let _ = eframe::run_native( "Viewports", eframe::NativeOptions { + viewport: egui::ViewportBuilder::default().with_inner_size([450.0, 400.0]), + #[cfg(feature = "wgpu")] renderer: eframe::Renderer::Wgpu, - initial_window_size: Some(egui::Vec2::new(450.0, 400.0)), ..Default::default() }, Box::new(|_| Box::::default()), diff --git a/examples/user_attention/src/main.rs b/examples/user_attention/src/main.rs index 7478eb072cd..4625abefa93 100644 --- a/examples/user_attention/src/main.rs +++ b/examples/user_attention/src/main.rs @@ -6,7 +6,7 @@ use std::time::{Duration, SystemTime}; fn main() -> eframe::Result<()> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let native_options = NativeOptions { - initial_window_size: Some(egui::vec2(400., 200.)), + viewport: egui::ViewportBuilder::default().with_inner_size([400., 200.]), ..Default::default() }; eframe::run_native(