mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 05:10:03 -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:
@@ -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:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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)?
|
||||
|
||||
Reference in New Issue
Block a user