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:
parent
3a8ed37f49
commit
39e60e367f
|
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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)?
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<T> = nohash_hasher::IntMap<ViewportId, T>;
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// 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<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 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)
|
||||
}
|
||||
}
|
||||
|
||||
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<String>,
|
||||
|
||||
/// 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<String>,
|
||||
|
||||
pub position: Option<Pos2>,
|
||||
pub inner_size: Option<Vec2>,
|
||||
|
|
@ -218,7 +272,7 @@ pub struct ViewportBuilder {
|
|||
pub resizable: Option<bool>,
|
||||
pub transparent: Option<bool>,
|
||||
pub decorations: Option<bool>,
|
||||
pub icon: Option<Arc<ColorImage>>,
|
||||
pub icon: Option<Arc<IconData>>,
|
||||
pub active: Option<bool>,
|
||||
pub visible: Option<bool>,
|
||||
pub title_hidden: Option<bool>,
|
||||
|
|
@ -230,7 +284,9 @@ pub struct ViewportBuilder {
|
|||
pub minimize_button: Option<bool>,
|
||||
pub maximize_button: Option<bool>,
|
||||
|
||||
pub hittest: Option<bool>,
|
||||
pub window_level: WindowLevel,
|
||||
|
||||
pub mouse_passthrough: Option<bool>,
|
||||
}
|
||||
|
||||
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<Arc<ColorImage>>) -> Self {
|
||||
pub fn with_window_icon(mut self, icon: impl Into<Arc<IconData>>) -> 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 `general` name sets an application ID, which should match the `.desktop`
|
||||
/// file distributed with your program. The `instance` is a `no-op`.
|
||||
/// 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 `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<String>, instance: impl Into<String>) -> Self {
|
||||
self.name = Some((id.into(), instance.into()));
|
||||
pub fn with_app_id(mut self, app_id: impl Into<String>) -> 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<Arc<ColorImage>>),
|
||||
WindowIcon(Option<Arc<IconData>>),
|
||||
|
||||
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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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::<App>::default()),
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue