eframe error handling (#2433)
* eframe::run_native: return errors instead of crashing * Detect and handle glutin errors * egui_demo_app: silence wgpu log spam * Add trace logs for why eframe is shutting down * Fix: only save App state once on Mac * Handle Winit failure * Log where we load app state from * Don't panic on zero-sized window * Clamp loaded window size to not be too tiny to see * Simplify code: more shared code in window_builder * Improve code readability * Fix wasm32 build * fix android * Update changelog
This commit is contained in:
parent
1437ec8903
commit
cb77458f70
27 changed files with 249 additions and 162 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1325,6 +1325,7 @@ dependencies = [
|
||||||
"raw-window-handle 0.5.0",
|
"raw-window-handle 0.5.0",
|
||||||
"ron",
|
"ron",
|
||||||
"serde",
|
"serde",
|
||||||
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tts",
|
"tts",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|
|
@ -6,6 +6,9 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
#### Desktop/Native:
|
||||||
|
* `eframe::run_native` now returns a `Result` ([#2433](https://github.com/emilk/egui/pull/2433)).
|
||||||
|
|
||||||
|
|
||||||
## 0.20.1 - 2022-12-11
|
## 0.20.1 - 2022-12-11
|
||||||
* Fix docs.rs build ([#2420](https://github.com/emilk/egui/pull/2420)).
|
* Fix docs.rs build ([#2420](https://github.com/emilk/egui/pull/2420)).
|
||||||
|
|
|
@ -72,6 +72,7 @@ egui = { version = "0.20.0", path = "../egui", default-features = false, feature
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"tracing",
|
"tracing",
|
||||||
] }
|
] }
|
||||||
|
thiserror = "1.0.37"
|
||||||
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
#! ### Optional dependencies
|
#! ### Optional dependencies
|
||||||
|
|
|
@ -708,6 +708,7 @@ impl Frame {
|
||||||
#[doc(alias = "exit")]
|
#[doc(alias = "exit")]
|
||||||
#[doc(alias = "quit")]
|
#[doc(alias = "quit")]
|
||||||
pub fn close(&mut self) {
|
pub fn close(&mut self) {
|
||||||
|
tracing::debug!("eframe::Frame::close called");
|
||||||
self.output.close = true;
|
self.output.close = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ pub async fn start_web(
|
||||||
canvas_id: &str,
|
canvas_id: &str,
|
||||||
web_options: WebOptions,
|
web_options: WebOptions,
|
||||||
app_creator: AppCreator,
|
app_creator: AppCreator,
|
||||||
) -> Result<AppRunnerRef, wasm_bindgen::JsValue> {
|
) -> std::result::Result<AppRunnerRef, wasm_bindgen::JsValue> {
|
||||||
let handle = web::start(canvas_id, web_options, app_creator).await?;
|
let handle = web::start(canvas_id, web_options, app_creator).await?;
|
||||||
|
|
||||||
Ok(handle)
|
Ok(handle)
|
||||||
|
@ -174,9 +174,16 @@ mod native;
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// This function can fail if we fail to set up a graphics context.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
pub fn run_native(app_name: &str, native_options: NativeOptions, app_creator: AppCreator) {
|
pub fn run_native(
|
||||||
|
app_name: &str,
|
||||||
|
native_options: NativeOptions,
|
||||||
|
app_creator: AppCreator,
|
||||||
|
) -> Result<()> {
|
||||||
let renderer = native_options.renderer;
|
let renderer = native_options.renderer;
|
||||||
|
|
||||||
#[cfg(not(feature = "__screenshot"))]
|
#[cfg(not(feature = "__screenshot"))]
|
||||||
|
@ -189,17 +196,41 @@ pub fn run_native(app_name: &str, native_options: NativeOptions, app_creator: Ap
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
Renderer::Glow => {
|
Renderer::Glow => {
|
||||||
tracing::debug!("Using the glow renderer");
|
tracing::debug!("Using the glow renderer");
|
||||||
native::run::run_glow(app_name, native_options, app_creator);
|
native::run::run_glow(app_name, native_options, app_creator)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
Renderer::Wgpu => {
|
Renderer::Wgpu => {
|
||||||
tracing::debug!("Using the wgpu renderer");
|
tracing::debug!("Using the wgpu renderer");
|
||||||
native::run::run_wgpu(app_name, native_options, app_creator);
|
native::run::run_wgpu(app_name, native_options, app_creator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// The different problems that can occur when trying to run `eframe`.
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum EframeError {
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[error("winit error: {0}")]
|
||||||
|
Winit(#[from] winit::error::OsError),
|
||||||
|
|
||||||
|
#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
|
||||||
|
#[error("glutin error: {0}")]
|
||||||
|
Glutin(#[from] glutin::error::Error),
|
||||||
|
|
||||||
|
#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
|
||||||
|
#[error("Found no glutin configs matching the template: {0:?}")]
|
||||||
|
NoGlutinConfigs(glutin::config::ConfigTemplate),
|
||||||
|
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
#[error("WGPU error: {0}")]
|
||||||
|
Wgpu(#[from] wgpu::RequestDeviceError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, EframeError>;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Profiling macro for feature "puffin"
|
/// Profiling macro for feature "puffin"
|
||||||
|
|
|
@ -49,6 +49,7 @@ pub fn read_window_info(window: &winit::window::Window, pixels_per_point: f32) -
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window_builder(
|
pub fn window_builder(
|
||||||
|
title: &str,
|
||||||
native_options: &epi::NativeOptions,
|
native_options: &epi::NativeOptions,
|
||||||
window_settings: &Option<WindowSettings>,
|
window_settings: &Option<WindowSettings>,
|
||||||
) -> winit::window::WindowBuilder {
|
) -> winit::window::WindowBuilder {
|
||||||
|
@ -73,13 +74,17 @@ pub fn window_builder(
|
||||||
let window_icon = icon_data.clone().and_then(load_icon);
|
let window_icon = icon_data.clone().and_then(load_icon);
|
||||||
|
|
||||||
let mut window_builder = winit::window::WindowBuilder::new()
|
let mut window_builder = winit::window::WindowBuilder::new()
|
||||||
|
.with_title(title)
|
||||||
.with_always_on_top(*always_on_top)
|
.with_always_on_top(*always_on_top)
|
||||||
.with_decorations(*decorated)
|
.with_decorations(*decorated)
|
||||||
.with_fullscreen(fullscreen.then(|| winit::window::Fullscreen::Borderless(None)))
|
.with_fullscreen(fullscreen.then(|| winit::window::Fullscreen::Borderless(None)))
|
||||||
.with_maximized(*maximized)
|
.with_maximized(*maximized)
|
||||||
.with_resizable(*resizable)
|
.with_resizable(*resizable)
|
||||||
.with_transparent(*transparent)
|
.with_transparent(*transparent)
|
||||||
.with_window_icon(window_icon);
|
.with_window_icon(window_icon)
|
||||||
|
// 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);
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
if *fullsize_content {
|
if *fullsize_content {
|
||||||
|
@ -308,8 +313,15 @@ impl EpiIntegration {
|
||||||
use winit::event::{ElementState, MouseButton, WindowEvent};
|
use winit::event::{ElementState, MouseButton, WindowEvent};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CloseRequested => self.close = app.on_close_event(),
|
WindowEvent::CloseRequested => {
|
||||||
WindowEvent::Destroyed => self.close = true,
|
tracing::debug!("Received WindowEvent::CloseRequested");
|
||||||
|
self.close = app.on_close_event();
|
||||||
|
tracing::debug!("App::on_close_event returned {}", self.close);
|
||||||
|
}
|
||||||
|
WindowEvent::Destroyed => {
|
||||||
|
tracing::debug!("Received WindowEvent::Destroyed");
|
||||||
|
self.close = true;
|
||||||
|
}
|
||||||
WindowEvent::MouseInput {
|
WindowEvent::MouseInput {
|
||||||
button: MouseButton::Left,
|
button: MouseButton::Left,
|
||||||
state: ElementState::Pressed,
|
state: ElementState::Pressed,
|
||||||
|
@ -351,6 +363,7 @@ impl EpiIntegration {
|
||||||
self.can_drag_window = false;
|
self.can_drag_window = false;
|
||||||
if app_output.close {
|
if app_output.close {
|
||||||
self.close = app.on_close_event();
|
self.close = app.on_close_event();
|
||||||
|
tracing::debug!("App::on_close_event returned {}", self.close);
|
||||||
}
|
}
|
||||||
self.frame.output.visible = app_output.visible; // this is handled by post_present
|
self.frame.output.visible = app_output.visible; // this is handled by post_present
|
||||||
handle_app_output(window, self.egui_ctx.pixels_per_point(), app_output);
|
handle_app_output(window, self.egui_ctx.pixels_per_point(), app_output);
|
||||||
|
@ -432,7 +445,9 @@ const STORAGE_WINDOW_KEY: &str = "window";
|
||||||
pub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option<WindowSettings> {
|
pub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option<WindowSettings> {
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
{
|
{
|
||||||
epi::get_value(_storage?, STORAGE_WINDOW_KEY)
|
let mut settings: WindowSettings = epi::get_value(_storage?, STORAGE_WINDOW_KEY)?;
|
||||||
|
settings.clamp_to_sane_values();
|
||||||
|
Some(settings)
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "persistence"))]
|
#[cfg(not(feature = "persistence"))]
|
||||||
None
|
None
|
||||||
|
|
|
@ -26,6 +26,7 @@ impl FileStorage {
|
||||||
/// Store the state in this .ron file.
|
/// Store the state in this .ron file.
|
||||||
pub fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self {
|
pub fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self {
|
||||||
let ron_filepath: PathBuf = ron_filepath.into();
|
let ron_filepath: PathBuf = ron_filepath.into();
|
||||||
|
tracing::debug!("Loading app state from {:?}…", ron_filepath);
|
||||||
Self {
|
Self {
|
||||||
kv: read_ron(&ron_filepath).unwrap_or_default(),
|
kv: read_ron(&ron_filepath).unwrap_or_default(),
|
||||||
ron_filepath,
|
ron_filepath,
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
//! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`].
|
//! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`].
|
||||||
//! When making changes to one you often also want to apply it to the other.
|
//! When making changes to one you often also want to apply it to the other.
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::{Duration, Instant};
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
use egui_winit::accesskit_winit;
|
|
||||||
use egui_winit::winit;
|
|
||||||
use winit::event_loop::{
|
use winit::event_loop::{
|
||||||
ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget,
|
ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "accesskit")]
|
||||||
|
use egui_winit::accesskit_winit;
|
||||||
|
use egui_winit::winit;
|
||||||
|
|
||||||
|
use crate::{epi, Result};
|
||||||
|
|
||||||
use super::epi_integration::{self, EpiIntegration};
|
use super::epi_integration::{self, EpiIntegration};
|
||||||
use crate::epi;
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum UserEvent {
|
pub enum UserEvent {
|
||||||
|
@ -60,7 +63,7 @@ trait WinitApp {
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
event: &winit::event::Event<'_, UserEvent>,
|
event: &winit::event::Event<'_, UserEvent>,
|
||||||
) -> EventResult;
|
) -> Result<EventResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_event_loop_builder(
|
fn create_event_loop_builder(
|
||||||
|
@ -79,10 +82,10 @@ fn create_event_loop_builder(
|
||||||
///
|
///
|
||||||
/// We reuse the event-loop so we can support closing and opening an eframe window
|
/// We reuse the event-loop so we can support closing and opening an eframe window
|
||||||
/// multiple times. This is just a limitation of winit.
|
/// multiple times. This is just a limitation of winit.
|
||||||
fn with_event_loop(
|
fn with_event_loop<R>(
|
||||||
mut native_options: epi::NativeOptions,
|
mut native_options: epi::NativeOptions,
|
||||||
f: impl FnOnce(&mut EventLoop<UserEvent>, NativeOptions),
|
f: impl FnOnce(&mut EventLoop<UserEvent>, NativeOptions) -> R,
|
||||||
) {
|
) -> R {
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
thread_local!(static EVENT_LOOP: RefCell<Option<EventLoop<UserEvent>>> = RefCell::new(None));
|
thread_local!(static EVENT_LOOP: RefCell<Option<EventLoop<UserEvent>>> = RefCell::new(None));
|
||||||
|
|
||||||
|
@ -93,22 +96,31 @@ fn with_event_loop(
|
||||||
let mut event_loop = event_loop.borrow_mut();
|
let mut event_loop = event_loop.borrow_mut();
|
||||||
let event_loop = event_loop
|
let event_loop = event_loop
|
||||||
.get_or_insert_with(|| create_event_loop_builder(&mut native_options).build());
|
.get_or_insert_with(|| create_event_loop_builder(&mut native_options).build());
|
||||||
f(event_loop, native_options);
|
f(event_loop, native_options)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_and_return(event_loop: &mut EventLoop<UserEvent>, mut winit_app: impl WinitApp) {
|
fn run_and_return(
|
||||||
|
event_loop: &mut EventLoop<UserEvent>,
|
||||||
|
mut winit_app: impl WinitApp,
|
||||||
|
) -> Result<()> {
|
||||||
use winit::platform::run_return::EventLoopExtRunReturn as _;
|
use winit::platform::run_return::EventLoopExtRunReturn as _;
|
||||||
|
|
||||||
tracing::debug!("event_loop.run_return");
|
tracing::debug!("Entering the winit event loop (run_return)…");
|
||||||
|
|
||||||
let mut next_repaint_time = Instant::now();
|
let mut next_repaint_time = Instant::now();
|
||||||
|
|
||||||
|
let mut returned_result = Ok(());
|
||||||
|
|
||||||
event_loop.run_return(|event, event_loop, control_flow| {
|
event_loop.run_return(|event, event_loop, control_flow| {
|
||||||
let event_result = match &event {
|
let event_result = match &event {
|
||||||
winit::event::Event::LoopDestroyed => {
|
winit::event::Event::LoopDestroyed => {
|
||||||
tracing::debug!("winit::event::Event::LoopDestroyed");
|
// On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name),
|
||||||
EventResult::Exit
|
// so we need to save state now:
|
||||||
|
tracing::debug!("Received Event::LoopDestroyed - saving app state…");
|
||||||
|
winit_app.save_and_destroy();
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Platform-dependent event handlers to workaround a winit bug
|
// Platform-dependent event handlers to workaround a winit bug
|
||||||
|
@ -137,7 +149,14 @@ fn run_and_return(event_loop: &mut EventLoop<UserEvent>, mut winit_app: impl Win
|
||||||
EventResult::Wait
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
|
|
||||||
event => winit_app.on_event(event_loop, event),
|
event => match winit_app.on_event(event_loop, event) {
|
||||||
|
Ok(event_result) => event_result,
|
||||||
|
Err(err) => {
|
||||||
|
returned_result = Err(err);
|
||||||
|
tracing::debug!("Exiting because of an error");
|
||||||
|
EventResult::Exit
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match event_result {
|
match event_result {
|
||||||
|
@ -155,10 +174,7 @@ fn run_and_return(event_loop: &mut EventLoop<UserEvent>, mut winit_app: impl Win
|
||||||
next_repaint_time = next_repaint_time.min(repaint_time);
|
next_repaint_time = next_repaint_time.min(repaint_time);
|
||||||
}
|
}
|
||||||
EventResult::Exit => {
|
EventResult::Exit => {
|
||||||
// On Cmd-Q we get here and then `run_return` doesn't return,
|
tracing::debug!("Asking to exit event loop…");
|
||||||
// so we need to save state now:
|
|
||||||
tracing::debug!("Exiting event loop - saving app state…");
|
|
||||||
winit_app.save_and_destroy();
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -187,16 +203,21 @@ fn run_and_return(event_loop: &mut EventLoop<UserEvent>, mut winit_app: impl Win
|
||||||
event_loop.run_return(|_, _, control_flow| {
|
event_loop.run_return(|_, _, control_flow| {
|
||||||
control_flow.set_exit();
|
control_flow.set_exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
returned_result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp + 'static) -> ! {
|
fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp + 'static) -> ! {
|
||||||
tracing::debug!("event_loop.run");
|
tracing::debug!("Entering the winit event loop (run)…");
|
||||||
|
|
||||||
let mut next_repaint_time = Instant::now();
|
let mut next_repaint_time = Instant::now();
|
||||||
|
|
||||||
event_loop.run(move |event, event_loop, control_flow| {
|
event_loop.run(move |event, event_loop, control_flow| {
|
||||||
let event_result = match event {
|
let event_result = match event {
|
||||||
winit::event::Event::LoopDestroyed => EventResult::Exit,
|
winit::event::Event::LoopDestroyed => {
|
||||||
|
tracing::debug!("Received Event::LoopDestroyed");
|
||||||
|
EventResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
// Platform-dependent event handlers to workaround a winit bug
|
// Platform-dependent event handlers to workaround a winit bug
|
||||||
// See: https://github.com/rust-windowing/winit/issues/987
|
// See: https://github.com/rust-windowing/winit/issues/987
|
||||||
|
@ -215,7 +236,12 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||||
..
|
..
|
||||||
}) => EventResult::RepaintNext,
|
}) => EventResult::RepaintNext,
|
||||||
|
|
||||||
event => winit_app.on_event(event_loop, &event),
|
event => match winit_app.on_event(event_loop, &event) {
|
||||||
|
Ok(event_result) => event_result,
|
||||||
|
Err(err) => {
|
||||||
|
panic!("eframe encountered a fatal error: {err}");
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match event_result {
|
match event_result {
|
||||||
|
@ -231,7 +257,7 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||||
next_repaint_time = next_repaint_time.min(repaint_time);
|
next_repaint_time = next_repaint_time.min(repaint_time);
|
||||||
}
|
}
|
||||||
EventResult::Exit => {
|
EventResult::Exit => {
|
||||||
tracing::debug!("Quitting…");
|
tracing::debug!("Quitting - saving app state…");
|
||||||
winit_app.save_and_destroy();
|
winit_app.save_and_destroy();
|
||||||
#[allow(clippy::exit)]
|
#[allow(clippy::exit)]
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
|
@ -279,6 +305,8 @@ fn center_window_pos(
|
||||||
mod glow_integration {
|
mod glow_integration {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use egui::NumExt as _;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// Note: that the current Glutin API design tightly couples the GL context with
|
// Note: that the current Glutin API design tightly couples the GL context with
|
||||||
|
@ -321,7 +349,7 @@ mod glow_integration {
|
||||||
unsafe fn new(
|
unsafe fn new(
|
||||||
winit_window: winit::window::Window,
|
winit_window: winit::window::Window,
|
||||||
native_options: &epi::NativeOptions,
|
native_options: &epi::NativeOptions,
|
||||||
) -> Self {
|
) -> Result<Self> {
|
||||||
use glutin::prelude::*;
|
use glutin::prelude::*;
|
||||||
use raw_window_handle::*;
|
use raw_window_handle::*;
|
||||||
let hardware_acceleration = match native_options.hardware_acceleration {
|
let hardware_acceleration = match native_options.hardware_acceleration {
|
||||||
|
@ -351,8 +379,7 @@ mod glow_integration {
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
let preference = glutin::display::DisplayApiPreference::Egl;
|
let preference = glutin::display::DisplayApiPreference::Egl;
|
||||||
|
|
||||||
let gl_display = glutin::display::Display::new(raw_display_handle, preference)
|
let gl_display = glutin::display::Display::new(raw_display_handle, preference)?;
|
||||||
.expect("failed to create glutin display");
|
|
||||||
let swap_interval = if native_options.vsync {
|
let swap_interval = if native_options.vsync {
|
||||||
glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap())
|
glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap())
|
||||||
} else {
|
} else {
|
||||||
|
@ -383,64 +410,49 @@ mod glow_integration {
|
||||||
// options required by user like multi sampling, srgb, transparency etc..
|
// options required by user like multi sampling, srgb, transparency etc..
|
||||||
// TODO: need to figure out a good fallback config template
|
// TODO: need to figure out a good fallback config template
|
||||||
let config = gl_display
|
let config = gl_display
|
||||||
.find_configs(config_template)
|
.find_configs(config_template.clone())?
|
||||||
.expect("failed to find even a single matching configuration")
|
|
||||||
.next()
|
.next()
|
||||||
.expect("failed to find a matching configuration for creating opengl context");
|
.ok_or(crate::EframeError::NoGlutinConfigs(config_template))?;
|
||||||
|
|
||||||
let context_attributes =
|
let context_attributes =
|
||||||
glutin::context::ContextAttributesBuilder::new().build(Some(raw_window_handle));
|
glutin::context::ContextAttributesBuilder::new().build(Some(raw_window_handle));
|
||||||
// for surface creation.
|
// for surface creation.
|
||||||
let (width, height): (u32, u32) = winit_window.inner_size().into();
|
let (width, height): (u32, u32) = winit_window.inner_size().into();
|
||||||
|
let width = std::num::NonZeroU32::new(width.at_least(1)).unwrap();
|
||||||
|
let height = std::num::NonZeroU32::new(height.at_least(1)).unwrap();
|
||||||
let surface_attributes =
|
let surface_attributes =
|
||||||
glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new()
|
glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new()
|
||||||
.build(
|
.build(raw_window_handle, width, height);
|
||||||
raw_window_handle,
|
|
||||||
std::num::NonZeroU32::new(width).unwrap(),
|
|
||||||
std::num::NonZeroU32::new(height).unwrap(),
|
|
||||||
);
|
|
||||||
// start creating the gl objects
|
// start creating the gl objects
|
||||||
let gl_context = gl_display
|
let gl_context = gl_display.create_context(&config, &context_attributes)?;
|
||||||
.create_context(&config, &context_attributes)
|
|
||||||
.expect("failed to create opengl context");
|
|
||||||
|
|
||||||
let gl_surface = gl_display
|
let gl_surface = gl_display.create_window_surface(&config, &surface_attributes)?;
|
||||||
.create_window_surface(&config, &surface_attributes)
|
let gl_context = gl_context.make_current(&gl_surface)?;
|
||||||
.expect("failed to create glutin window surface");
|
gl_surface.set_swap_interval(&gl_context, swap_interval)?;
|
||||||
let gl_context = gl_context
|
Ok(GlutinWindowContext {
|
||||||
.make_current(&gl_surface)
|
|
||||||
.expect("failed to make gl context current");
|
|
||||||
gl_surface
|
|
||||||
.set_swap_interval(&gl_context, swap_interval)
|
|
||||||
.expect("failed to set vsync swap interval");
|
|
||||||
GlutinWindowContext {
|
|
||||||
window: winit_window,
|
window: winit_window,
|
||||||
gl_context,
|
gl_context,
|
||||||
gl_display,
|
gl_display,
|
||||||
gl_surface,
|
gl_surface,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
fn window(&self) -> &winit::window::Window {
|
fn window(&self) -> &winit::window::Window {
|
||||||
&self.window
|
&self.window
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize(&self, physical_size: winit::dpi::PhysicalSize<u32>) {
|
fn resize(&self, physical_size: winit::dpi::PhysicalSize<u32>) {
|
||||||
use glutin::surface::GlSurface;
|
use glutin::surface::GlSurface;
|
||||||
self.gl_surface.resize(
|
let width = std::num::NonZeroU32::new(physical_size.width.at_least(1)).unwrap();
|
||||||
&self.gl_context,
|
let height = std::num::NonZeroU32::new(physical_size.height.at_least(1)).unwrap();
|
||||||
physical_size
|
self.gl_surface.resize(&self.gl_context, width, height);
|
||||||
.width
|
|
||||||
.try_into()
|
|
||||||
.expect("physical size must not be zero"),
|
|
||||||
physical_size
|
|
||||||
.height
|
|
||||||
.try_into()
|
|
||||||
.expect("physical size must not be zero"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn swap_buffers(&self) -> glutin::error::Result<()> {
|
fn swap_buffers(&self) -> glutin::error::Result<()> {
|
||||||
use glutin::surface::GlSurface;
|
use glutin::surface::GlSurface;
|
||||||
self.gl_surface.swap_buffers(&self.gl_context)
|
self.gl_surface.swap_buffers(&self.gl_context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_proc_address(&self, addr: &std::ffi::CStr) -> *const std::ffi::c_void {
|
fn get_proc_address(&self, addr: &std::ffi::CStr) -> *const std::ffi::c_void {
|
||||||
use glutin::display::GlDisplay;
|
use glutin::display::GlDisplay;
|
||||||
self.gl_display.get_proc_address(addr)
|
self.gl_display.get_proc_address(addr)
|
||||||
|
@ -484,25 +496,19 @@ mod glow_integration {
|
||||||
fn create_glutin_windowed_context(
|
fn create_glutin_windowed_context(
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
storage: Option<&dyn epi::Storage>,
|
storage: Option<&dyn epi::Storage>,
|
||||||
title: &String,
|
title: &str,
|
||||||
native_options: &NativeOptions,
|
native_options: &NativeOptions,
|
||||||
) -> (GlutinWindowContext, glow::Context) {
|
) -> Result<(GlutinWindowContext, glow::Context)> {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
let window_settings = epi_integration::load_window_settings(storage);
|
let window_settings = epi_integration::load_window_settings(storage);
|
||||||
|
|
||||||
let window_builder = epi_integration::window_builder(native_options, &window_settings)
|
let winit_window =
|
||||||
.with_title(title)
|
epi_integration::window_builder(title, native_options, &window_settings)
|
||||||
.with_transparent(native_options.transparent)
|
.build(event_loop)?;
|
||||||
// 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);
|
|
||||||
let winit_window = window_builder
|
|
||||||
.build(event_loop)
|
|
||||||
.expect("failed to create winit window");
|
|
||||||
// a lot of the code below has been lifted from glutin example in their repo.
|
// a lot of the code below has been lifted from glutin example in their repo.
|
||||||
let glutin_window_context =
|
let glutin_window_context =
|
||||||
unsafe { GlutinWindowContext::new(winit_window, native_options) };
|
unsafe { GlutinWindowContext::new(winit_window, native_options)? };
|
||||||
let gl = unsafe {
|
let gl = unsafe {
|
||||||
glow::Context::from_loader_function(|s| {
|
glow::Context::from_loader_function(|s| {
|
||||||
let s = std::ffi::CString::new(s)
|
let s = std::ffi::CString::new(s)
|
||||||
|
@ -512,10 +518,10 @@ mod glow_integration {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
(glutin_window_context, gl)
|
Ok((glutin_window_context, gl))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
|
fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) -> Result<()> {
|
||||||
let storage = epi_integration::create_storage(&self.app_name);
|
let storage = epi_integration::create_storage(&self.app_name);
|
||||||
|
|
||||||
let (gl_window, gl) = Self::create_glutin_windowed_context(
|
let (gl_window, gl) = Self::create_glutin_windowed_context(
|
||||||
|
@ -523,7 +529,7 @@ mod glow_integration {
|
||||||
storage.as_deref(),
|
storage.as_deref(),
|
||||||
&self.app_name,
|
&self.app_name,
|
||||||
&self.native_options,
|
&self.native_options,
|
||||||
);
|
)?;
|
||||||
let gl = Arc::new(gl);
|
let gl = Arc::new(gl);
|
||||||
|
|
||||||
let painter =
|
let painter =
|
||||||
|
@ -585,6 +591,8 @@ mod glow_integration {
|
||||||
integration,
|
integration,
|
||||||
app,
|
app,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -726,11 +734,11 @@ mod glow_integration {
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
event: &winit::event::Event<'_, UserEvent>,
|
event: &winit::event::Event<'_, UserEvent>,
|
||||||
) -> EventResult {
|
) -> Result<EventResult> {
|
||||||
match event {
|
Ok(match event {
|
||||||
winit::event::Event::Resumed => {
|
winit::event::Event::Resumed => {
|
||||||
if self.running.is_none() {
|
if self.running.is_none() {
|
||||||
self.init_run_state(event_loop);
|
self.init_run_state(event_loop)?;
|
||||||
}
|
}
|
||||||
EventResult::RepaintNow
|
EventResult::RepaintNow
|
||||||
}
|
}
|
||||||
|
@ -793,7 +801,8 @@ mod glow_integration {
|
||||||
winit::event::WindowEvent::CloseRequested
|
winit::event::WindowEvent::CloseRequested
|
||||||
if running.integration.should_close() =>
|
if running.integration.should_close() =>
|
||||||
{
|
{
|
||||||
return EventResult::Exit
|
tracing::debug!("Received WindowEvent::CloseRequested");
|
||||||
|
return Ok(EventResult::Exit);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -832,7 +841,7 @@ mod glow_integration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => EventResult::Wait,
|
_ => EventResult::Wait,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -840,7 +849,7 @@ mod glow_integration {
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
mut native_options: epi::NativeOptions,
|
mut native_options: epi::NativeOptions,
|
||||||
app_creator: epi::AppCreator,
|
app_creator: epi::AppCreator,
|
||||||
) {
|
) -> Result<()> {
|
||||||
if native_options.run_and_return {
|
if native_options.run_and_return {
|
||||||
with_event_loop(native_options, |event_loop, mut native_options| {
|
with_event_loop(native_options, |event_loop, mut native_options| {
|
||||||
if native_options.centered {
|
if native_options.centered {
|
||||||
|
@ -849,8 +858,8 @@ mod glow_integration {
|
||||||
|
|
||||||
let glow_eframe =
|
let glow_eframe =
|
||||||
GlowWinitApp::new(event_loop, app_name, native_options, app_creator);
|
GlowWinitApp::new(event_loop, app_name, native_options, app_creator);
|
||||||
run_and_return(event_loop, glow_eframe);
|
run_and_return(event_loop, glow_eframe)
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
let event_loop = create_event_loop_builder(&mut native_options).build();
|
let event_loop = create_event_loop_builder(&mut native_options).build();
|
||||||
|
|
||||||
|
@ -923,38 +932,40 @@ mod wgpu_integration {
|
||||||
fn create_window(
|
fn create_window(
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
storage: Option<&dyn epi::Storage>,
|
storage: Option<&dyn epi::Storage>,
|
||||||
title: &String,
|
title: &str,
|
||||||
native_options: &NativeOptions,
|
native_options: &NativeOptions,
|
||||||
) -> winit::window::Window {
|
) -> Result<winit::window::Window> {
|
||||||
let window_settings = epi_integration::load_window_settings(storage);
|
let window_settings = epi_integration::load_window_settings(storage);
|
||||||
epi_integration::window_builder(native_options, &window_settings)
|
Ok(
|
||||||
.with_title(title)
|
epi_integration::window_builder(title, native_options, &window_settings)
|
||||||
// Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
|
.build(event_loop)?,
|
||||||
// We must also keep the window hidden until AccessKit is initialized.
|
)
|
||||||
.with_visible(false)
|
|
||||||
.build(event_loop)
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn set_window(&mut self, window: winit::window::Window) {
|
fn set_window(
|
||||||
|
&mut self,
|
||||||
|
window: winit::window::Window,
|
||||||
|
) -> std::result::Result<(), wgpu::RequestDeviceError> {
|
||||||
self.window = Some(window);
|
self.window = Some(window);
|
||||||
if let Some(running) = &mut self.running {
|
if let Some(running) = &mut self.running {
|
||||||
unsafe {
|
unsafe {
|
||||||
running.painter.set_window(self.window.as_ref()).unwrap();
|
running.painter.set_window(self.window.as_ref())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
fn drop_window(&mut self) {
|
fn drop_window(&mut self) -> std::result::Result<(), wgpu::RequestDeviceError> {
|
||||||
self.window = None;
|
self.window = None;
|
||||||
if let Some(running) = &mut self.running {
|
if let Some(running) = &mut self.running {
|
||||||
unsafe {
|
unsafe {
|
||||||
running.painter.set_window(None).unwrap();
|
running.painter.set_window(None)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_run_state(
|
fn init_run_state(
|
||||||
|
@ -962,7 +973,7 @@ mod wgpu_integration {
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
storage: Option<Box<dyn epi::Storage>>,
|
storage: Option<Box<dyn epi::Storage>>,
|
||||||
window: winit::window::Window,
|
window: winit::window::Window,
|
||||||
) {
|
) -> std::result::Result<(), wgpu::RequestDeviceError> {
|
||||||
#[allow(unsafe_code, unused_mut, unused_unsafe)]
|
#[allow(unsafe_code, unused_mut, unused_unsafe)]
|
||||||
let painter = unsafe {
|
let painter = unsafe {
|
||||||
let mut painter = egui_wgpu::winit::Painter::new(
|
let mut painter = egui_wgpu::winit::Painter::new(
|
||||||
|
@ -970,7 +981,7 @@ mod wgpu_integration {
|
||||||
self.native_options.multisampling.max(1) as _,
|
self.native_options.multisampling.max(1) as _,
|
||||||
self.native_options.depth_buffer,
|
self.native_options.depth_buffer,
|
||||||
);
|
);
|
||||||
painter.set_window(Some(&window)).unwrap();
|
painter.set_window(Some(&window))?;
|
||||||
painter
|
painter
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1028,6 +1039,8 @@ mod wgpu_integration {
|
||||||
app,
|
app,
|
||||||
});
|
});
|
||||||
self.window = Some(window);
|
self.window = Some(window);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1135,8 +1148,8 @@ mod wgpu_integration {
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
event: &winit::event::Event<'_, UserEvent>,
|
event: &winit::event::Event<'_, UserEvent>,
|
||||||
) -> EventResult {
|
) -> Result<EventResult> {
|
||||||
match event {
|
Ok(match event {
|
||||||
winit::event::Event::Resumed => {
|
winit::event::Event::Resumed => {
|
||||||
if let Some(running) = &self.running {
|
if let Some(running) = &self.running {
|
||||||
if self.window.is_none() {
|
if self.window.is_none() {
|
||||||
|
@ -1145,8 +1158,8 @@ mod wgpu_integration {
|
||||||
running.integration.frame.storage(),
|
running.integration.frame.storage(),
|
||||||
&self.app_name,
|
&self.app_name,
|
||||||
&self.native_options,
|
&self.native_options,
|
||||||
);
|
)?;
|
||||||
self.set_window(window);
|
self.set_window(window)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let storage = epi_integration::create_storage(&self.app_name);
|
let storage = epi_integration::create_storage(&self.app_name);
|
||||||
|
@ -1155,14 +1168,14 @@ mod wgpu_integration {
|
||||||
storage.as_deref(),
|
storage.as_deref(),
|
||||||
&self.app_name,
|
&self.app_name,
|
||||||
&self.native_options,
|
&self.native_options,
|
||||||
);
|
)?;
|
||||||
self.init_run_state(event_loop, storage, window);
|
self.init_run_state(event_loop, storage, window)?;
|
||||||
}
|
}
|
||||||
EventResult::RepaintNow
|
EventResult::RepaintNow
|
||||||
}
|
}
|
||||||
winit::event::Event::Suspended => {
|
winit::event::Event::Suspended => {
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
self.drop_window();
|
self.drop_window()?;
|
||||||
EventResult::Wait
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,7 +1225,8 @@ mod wgpu_integration {
|
||||||
winit::event::WindowEvent::CloseRequested
|
winit::event::WindowEvent::CloseRequested
|
||||||
if running.integration.should_close() =>
|
if running.integration.should_close() =>
|
||||||
{
|
{
|
||||||
return EventResult::Exit
|
tracing::debug!("Received WindowEvent::CloseRequested");
|
||||||
|
return Ok(EventResult::Exit);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
@ -1250,7 +1264,7 @@ mod wgpu_integration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => EventResult::Wait,
|
_ => EventResult::Wait,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1258,7 +1272,7 @@ mod wgpu_integration {
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
mut native_options: epi::NativeOptions,
|
mut native_options: epi::NativeOptions,
|
||||||
app_creator: epi::AppCreator,
|
app_creator: epi::AppCreator,
|
||||||
) {
|
) -> Result<()> {
|
||||||
if native_options.run_and_return {
|
if native_options.run_and_return {
|
||||||
with_event_loop(native_options, |event_loop, mut native_options| {
|
with_event_loop(native_options, |event_loop, mut native_options| {
|
||||||
if native_options.centered {
|
if native_options.centered {
|
||||||
|
@ -1267,8 +1281,8 @@ mod wgpu_integration {
|
||||||
|
|
||||||
let wgpu_eframe =
|
let wgpu_eframe =
|
||||||
WgpuWinitApp::new(event_loop, app_name, native_options, app_creator);
|
WgpuWinitApp::new(event_loop, app_name, native_options, app_creator);
|
||||||
run_and_return(event_loop, wgpu_eframe);
|
run_and_return(event_loop, wgpu_eframe)
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
let event_loop = create_event_loop_builder(&mut native_options).build();
|
let event_loop = create_event_loop_builder(&mut native_options).build();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ All notable changes to the `egui-wgpu` integration will be noted in this file.
|
||||||
|
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
* Fix panic if we can't find a device ([#2428](https://github.com/emilk/egui/pull/2428))
|
* Return `Err` instead of panic if we can't find a device ([#2428](https://github.com/emilk/egui/pull/2428)).
|
||||||
|
|
||||||
|
|
||||||
## 0.20.0 - 2022-12-08 - web support
|
## 0.20.0 - 2022-12-08 - web support
|
||||||
|
|
|
@ -46,12 +46,13 @@ impl WindowSettings {
|
||||||
&self,
|
&self,
|
||||||
mut window: winit::window::WindowBuilder,
|
mut window: winit::window::WindowBuilder,
|
||||||
) -> winit::window::WindowBuilder {
|
) -> winit::window::WindowBuilder {
|
||||||
if !cfg!(target_os = "windows") {
|
|
||||||
// If the app last ran on two monitors and only one is now connected, then
|
// If the app last ran on two monitors and only one is now connected, then
|
||||||
// the given position is invalid.
|
// the given position is invalid.
|
||||||
// If this happens on Mac, the window is clamped into valid area.
|
// If this happens on Mac, the window is clamped into valid area.
|
||||||
// If this happens on Windows, the window is hidden and very difficult to find.
|
// If this happens on Windows, the window is hidden and very difficult to find.
|
||||||
// So we don't restore window positions on Windows.
|
// So we don't restore window positions on Windows.
|
||||||
|
let try_restore_position = !cfg!(target_os = "windows");
|
||||||
|
if try_restore_position {
|
||||||
if let Some(pos) = self.position {
|
if let Some(pos) = self.position {
|
||||||
window = window.with_position(winit::dpi::PhysicalPosition {
|
window = window.with_position(winit::dpi::PhysicalPosition {
|
||||||
x: pos.x as f64,
|
x: pos.x as f64,
|
||||||
|
@ -74,4 +75,12 @@ impl WindowSettings {
|
||||||
window
|
window
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clamp_to_sane_values(&mut self) {
|
||||||
|
if let Some(size) = &mut self.inner_size_points {
|
||||||
|
// Prevent ridiculously small windows
|
||||||
|
let min_size = egui::Vec2::splat(64.0);
|
||||||
|
*size = size.max(min_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,18 @@
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||||
|
|
||||||
// When compiling natively:
|
// When compiling natively:
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
|
{
|
||||||
|
// Silence wgpu log spam (https://github.com/gfx-rs/wgpu/issues/3206)
|
||||||
|
let mut rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_owned());
|
||||||
|
for loud_crate in ["naga", "wgpu_core", "wgpu_hal"] {
|
||||||
|
if !rust_log.contains(&format!("{loud_crate}=")) {
|
||||||
|
rust_log += &format!(",{loud_crate}=warn");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::env::set_var("RUST_LOG", rust_log);
|
||||||
|
}
|
||||||
|
|
||||||
// Log to stdout (if you run with `RUST_LOG=debug`).
|
// Log to stdout (if you run with `RUST_LOG=debug`).
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
@ -21,5 +32,5 @@ fn main() {
|
||||||
"egui demo app",
|
"egui demo app",
|
||||||
options,
|
options,
|
||||||
Box::new(|cc| Box::new(egui_demo_app::WrapApp::new(cc))),
|
Box::new(|cc| Box::new(egui_demo_app::WrapApp::new(cc))),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,9 +223,9 @@ fn create_display(
|
||||||
height: 600.0,
|
height: 600.0,
|
||||||
})
|
})
|
||||||
.with_title("egui_glow example")
|
.with_title("egui_glow example")
|
||||||
.with_visible(false)
|
.with_visible(false) // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
|
||||||
.build(event_loop)
|
.build(event_loop)
|
||||||
.unwrap(); // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
|
.unwrap();
|
||||||
|
|
||||||
// a lot of the code below has been lifted from glutin example in their repo.
|
// a lot of the code below has been lifted from glutin example in their repo.
|
||||||
let glutin_window_context = unsafe { GlutinWindowContext::new(winit_window) };
|
let glutin_window_context = unsafe { GlutinWindowContext::new(winit_window) };
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
initial_window_size: Some(egui::vec2(320.0, 240.0)),
|
initial_window_size: Some(egui::vec2(320.0, 240.0)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -11,7 +11,7 @@ fn main() {
|
||||||
"Confirm exit",
|
"Confirm exit",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -6,7 +6,7 @@ use eframe::egui;
|
||||||
use egui::mutex::Mutex;
|
use egui::mutex::Mutex;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
initial_window_size: Some(egui::vec2(350.0, 380.0)),
|
initial_window_size: Some(egui::vec2(350.0, 380.0)),
|
||||||
multisampling: 8,
|
multisampling: 8,
|
||||||
|
@ -17,7 +17,7 @@ fn main() {
|
||||||
"Custom 3D painting in eframe using glow",
|
"Custom 3D painting in eframe using glow",
|
||||||
options,
|
options,
|
||||||
Box::new(|cc| Box::new(MyApp::new(cc))),
|
Box::new(|cc| Box::new(MyApp::new(cc))),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyApp {
|
struct MyApp {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
initial_window_size: Some(egui::vec2(550.0, 610.0)),
|
initial_window_size: Some(egui::vec2(550.0, 610.0)),
|
||||||
multisampling: 8,
|
multisampling: 8,
|
||||||
|
@ -16,7 +16,7 @@ fn main() {
|
||||||
"Custom 3D painting in eframe!",
|
"Custom 3D painting in eframe!",
|
||||||
options,
|
options,
|
||||||
Box::new(|cc| Box::new(MyApp::new(cc))),
|
Box::new(|cc| Box::new(MyApp::new(cc))),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MyApp {
|
pub struct MyApp {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
initial_window_size: Some(egui::vec2(320.0, 240.0)),
|
initial_window_size: Some(egui::vec2(320.0, 240.0)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -11,7 +11,7 @@ fn main() {
|
||||||
"egui example: custom font",
|
"egui example: custom font",
|
||||||
options,
|
options,
|
||||||
Box::new(|cc| Box::new(MyApp::new(cc))),
|
Box::new(|cc| Box::new(MyApp::new(cc))),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_custom_fonts(ctx: &egui::Context) {
|
fn setup_custom_fonts(ctx: &egui::Context) {
|
||||||
|
|
|
@ -58,14 +58,14 @@ impl eframe::App for MyApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions::default();
|
let options = eframe::NativeOptions::default();
|
||||||
|
|
||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
"egui example: global font style",
|
"egui example: global font style",
|
||||||
options,
|
options,
|
||||||
Box::new(|cc| Box::new(MyApp::new(cc))),
|
Box::new(|cc| Box::new(MyApp::new(cc))),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
pub const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
// Hide the OS-specific "chrome" around the window:
|
// Hide the OS-specific "chrome" around the window:
|
||||||
decorated: false,
|
decorated: false,
|
||||||
|
@ -18,7 +18,7 @@ fn main() {
|
||||||
"Custom window frame", // unused title
|
"Custom window frame", // unused title
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -4,13 +4,13 @@ use eframe::egui;
|
||||||
use egui_extras::RetainedImage;
|
use egui_extras::RetainedImage;
|
||||||
use poll_promise::Promise;
|
use poll_promise::Promise;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions::default();
|
let options = eframe::NativeOptions::default();
|
||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
"Download and show an image with eframe/egui",
|
"Download and show an image with eframe/egui",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
drag_and_drop_support: true,
|
drag_and_drop_support: true,
|
||||||
initial_window_size: Some(egui::vec2(320.0, 240.0)),
|
initial_window_size: Some(egui::vec2(320.0, 240.0)),
|
||||||
|
@ -12,7 +12,7 @@ fn main() {
|
||||||
"Native file dialogs and drag-and-drop files",
|
"Native file dialogs and drag-and-drop files",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
// Log to stdout (if you run with `RUST_LOG=debug`).
|
// Log to stdout (if you run with `RUST_LOG=debug`).
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ fn main() {
|
||||||
"My egui App",
|
"My egui App",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyApp {
|
struct MyApp {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use egui::*;
|
use egui::*;
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
// Log to stdout (if you run with `RUST_LOG=debug`).
|
// Log to stdout (if you run with `RUST_LOG=debug`).
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ fn main() {
|
||||||
"Keyboard events",
|
"Keyboard events",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(Content::default())),
|
Box::new(|_cc| Box::new(Content::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
start_puffin_server(); // NOTE: you may only want to call this if the users specifies some flag or clicks a button!
|
start_puffin_server(); // NOTE: you may only want to call this if the users specifies some flag or clicks a button!
|
||||||
|
|
||||||
let options = eframe::NativeOptions::default();
|
let options = eframe::NativeOptions::default();
|
||||||
|
@ -10,7 +10,7 @@ fn main() {
|
||||||
"My egui App",
|
"My egui App",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use egui_extras::RetainedImage;
|
use egui_extras::RetainedImage;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
initial_window_size: Some(egui::vec2(300.0, 900.0)),
|
initial_window_size: Some(egui::vec2(300.0, 900.0)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -13,7 +13,7 @@ fn main() {
|
||||||
"Show an image with eframe/egui",
|
"Show an image with eframe/egui",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyApp {
|
struct MyApp {
|
||||||
|
|
|
@ -6,13 +6,13 @@ use eframe::{
|
||||||
};
|
};
|
||||||
use itertools::Itertools as _;
|
use itertools::Itertools as _;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions::default();
|
let options = eframe::NativeOptions::default();
|
||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
"Take screenshots and display with eframe/egui",
|
"Take screenshots and display with eframe/egui",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
if cfg!(target_os = "macos") {
|
if cfg!(target_os = "macos") {
|
||||||
eprintln!("WARNING: this example does not work on Mac! See https://github.com/emilk/egui/issues/1918");
|
eprintln!("WARNING: this example does not work on Mac! See https://github.com/emilk/egui/issues/1918");
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ fn main() {
|
||||||
"First Window",
|
"First Window",
|
||||||
options.clone(),
|
options.clone(),
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)?;
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ fn main() {
|
||||||
"Second Window",
|
"Second Window",
|
||||||
options.clone(),
|
options.clone(),
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)?;
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ fn main() {
|
||||||
"Third Window",
|
"Third Window",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), eframe::EframeError> {
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
initial_window_size: Some(egui::vec2(1000.0, 700.0)),
|
initial_window_size: Some(egui::vec2(1000.0, 700.0)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -15,7 +15,7 @@ fn main() {
|
||||||
"svg example",
|
"svg example",
|
||||||
options,
|
options,
|
||||||
Box::new(|_cc| Box::new(MyApp::default())),
|
Box::new(|_cc| Box::new(MyApp::default())),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyApp {
|
struct MyApp {
|
||||||
|
|
Loading…
Reference in a new issue