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

Merge branch 'master' of https://github.com/emilk/egui into multiples_viewports

This commit is contained in:
Konkitoman
2023-08-15 02:17:12 +03:00
159 changed files with 2536 additions and 1163 deletions

View File

@@ -437,7 +437,7 @@ pub struct NativeOptions {
/// will be used instead.
///
/// ### On Wayland
/// On Wauland this sets the Application ID for the window.
/// 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

View File

@@ -7,7 +7,7 @@
//! To learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!
//!
//! In short, you implement [`App`] (especially [`App::update`]) and then
//! call [`crate::run_native`] from your `main.rs`, and/or call `eframe::start_web` from your `lib.rs`.
//! call [`crate::run_native`] from your `main.rs`, and/or use `eframe::WebRunner` from your `lib.rs`.
//!
//! ## Usage, native:
//! ``` no_run
@@ -274,6 +274,7 @@ pub fn run_simple_native(
struct SimpleApp<U> {
update_fun: U,
}
impl<U: FnMut(&egui::Context, &mut Frame)> App for SimpleApp<U> {
fn update(
&mut self,

View File

@@ -9,7 +9,7 @@ use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _};
#[cfg(feature = "accesskit")]
use egui::accesskit;
use egui::{NumExt as _, ViewportBuilder, ViewportId, ViewportRender};
use egui::{mutex::RwLock, NumExt as _, ViewportBuilder, ViewportId, ViewportRender};
#[cfg(feature = "accesskit")]
use egui_winit::accesskit_winit;
use egui_winit::{native_pixels_per_point, EventResponse, WindowSettings};
@@ -125,9 +125,12 @@ pub fn window_builder<E>(
}
#[cfg(all(feature = "wayland", target_os = "linux"))]
if let Some(app_id) = &native_options.app_id {
{
use winit::platform::wayland::WindowBuilderExtWayland as _;
window_builder = window_builder.with_name(app_id, "");
match &native_options.app_id {
Some(app_id) => window_builder = window_builder.with_name(app_id, ""),
None => window_builder = window_builder.with_name(title, ""),
}
}
if let Some(min_size) = *min_window_size {
@@ -143,10 +146,11 @@ pub fn window_builder<E>(
let inner_size_points = if let Some(mut window_settings) = window_settings {
// Restore pos/size from previous session
window_settings.clamp_to_sane_values(largest_monitor_point_size(event_loop));
#[cfg(windows)]
window_settings.clamp_window_to_sane_position(event_loop);
window_builder = window_settings.initialize_window(window_builder);
window_settings.clamp_size_to_sane_values(largest_monitor_point_size(event_loop));
window_settings.clamp_position_to_monitors(event_loop);
window_builder = window_settings.initialize_window_builder(window_builder);
window_settings.inner_size_points()
} else {
if let Some(pos) = *initial_window_pos {
@@ -176,12 +180,14 @@ pub fn window_builder<E>(
}
}
}
window_builder
}
pub fn apply_native_options_to_window(
window: &winit::window::Window,
native_options: &crate::NativeOptions,
window_settings: Option<WindowSettings>,
) {
use winit::window::WindowLevel;
window.set_window_level(if native_options.always_on_top {
@@ -189,6 +195,10 @@ pub fn apply_native_options_to_window(
} else {
WindowLevel::Normal
});
if let Some(window_settings) = window_settings {
window_settings.initialize_window(window);
}
}
fn largest_monitor_point_size<E>(event_loop: &EventLoopWindowTarget<E>) -> egui::Vec2 {
@@ -340,6 +350,7 @@ pub struct EpiIntegration {
pending_full_output: egui::FullOutput,
/// When set, it is time to close the native window.
close: bool,
can_drag_window: bool,
window_state: WindowState,
follow_system_theme: bool,
@@ -588,27 +599,37 @@ impl EpiIntegration {
// ------------------------------------------------------------------------
// Persistence stuff:
pub fn maybe_autosave(&mut self, app: &mut dyn epi::App, window: &winit::window::Window) {
pub fn maybe_autosave(
&mut self,
app: &mut dyn epi::App,
window: Arc<RwLock<winit::window::Window>>,
) {
let now = std::time::Instant::now();
if now - self.last_auto_save > app.auto_save_interval() {
self.save(app, window);
self.save(app, Some(window));
self.last_auto_save = now;
}
}
#[allow(clippy::unused_self)]
pub fn save(&mut self, _app: &mut dyn epi::App, _window: &winit::window::Window) {
pub fn save(
&mut self,
_app: &mut dyn epi::App,
_window: Option<Arc<RwLock<winit::window::Window>>>,
) {
#[cfg(feature = "persistence")]
if let Some(storage) = self.frame.storage_mut() {
crate::profile_function!();
if _app.persist_native_window() {
crate::profile_scope!("native_window");
epi::set_value(
storage,
STORAGE_WINDOW_KEY,
&WindowSettings::from_display(_window),
);
if let Some(window) = _window {
if _app.persist_native_window() {
crate::profile_scope!("native_window");
epi::set_value(
storage,
STORAGE_WINDOW_KEY,
&WindowSettings::from_display(&window.read()),
);
}
}
if _app.persist_egui_memory() {
crate::profile_scope!("egui_memory");

View File

@@ -80,14 +80,45 @@ impl crate::Storage for FileStorage {
join_handle.join().ok();
}
let join_handle = std::thread::spawn(move || {
let file = std::fs::File::create(&file_path).unwrap();
let config = Default::default();
ron::ser::to_writer_pretty(file, &kv, config).unwrap();
log::trace!("Persisted to {:?}", file_path);
});
match std::thread::Builder::new()
.name("eframe_persist".to_owned())
.spawn(move || {
save_to_disk(&file_path, &kv);
}) {
Ok(join_handle) => {
self.last_save_join_handle = Some(join_handle);
}
Err(err) => {
log::warn!("Failed to spawn thread to save app state: {err}");
}
}
}
}
}
self.last_save_join_handle = Some(join_handle);
fn save_to_disk(file_path: &PathBuf, kv: &HashMap<String, String>) {
crate::profile_function!();
if let Some(parent_dir) = file_path.parent() {
if !parent_dir.exists() {
if let Err(err) = std::fs::create_dir_all(parent_dir) {
log::warn!("Failed to create directory {parent_dir:?}: {err}");
}
}
}
match std::fs::File::create(file_path) {
Ok(file) => {
let config = Default::default();
if let Err(err) = ron::ser::to_writer_pretty(file, &kv, config) {
log::warn!("Failed to serialize app state: {err}");
} else {
log::trace!("Persisted to {:?}", file_path);
}
}
Err(err) => {
log::warn!("Failed to create file {file_path:?}: {err}");
}
}
}

View File

@@ -24,6 +24,7 @@ pub enum UserEvent {
RequestRepaint {
id: ViewportId,
when: Instant,
/// What the frame number was when the repaint was _requested_.
frame_nr: u64,
},
@@ -154,12 +155,14 @@ fn run_and_return(
// 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/1619
winit::event::Event::RedrawEventsCleared if cfg!(windows) => {
#[cfg(target_os = "windows")]
winit::event::Event::RedrawEventsCleared => {
// windows_next_repaint_times.clear();
// winit_app.run_ui_and_paint(None)
vec![EventResult::Wait]
}
winit::event::Event::RedrawRequested(window_id) if !cfg!(windows) => {
#[cfg(not(target_os = "windows"))]
winit::event::Event::RedrawRequested(window_id) => {
windows_next_repaint_times.remove(window_id);
winit_app.run_ui_and_paint(*window_id)
}
@@ -214,7 +217,7 @@ fn run_and_return(
}
EventResult::RepaintNow(window_id) => {
log::trace!("Repaint caused by winit::Event: {:?}", event);
if cfg!(windows) {
if cfg!(target_os = "windows") {
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
windows_next_repaint_times.remove(&window_id);
@@ -280,7 +283,7 @@ fn run_and_return(
//
// Note that this approach may cause issues on macOS (emilk/egui#2768); therefore,
// we only apply this approach on Windows to minimize the affect.
#[cfg(windows)]
#[cfg(target_os = "windows")]
{
event_loop.run_return(|_, _, control_flow| {
control_flow.set_exit();
@@ -307,12 +310,12 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
// 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/1619
winit::event::Event::RedrawEventsCleared if cfg!(windows) => {
winit::event::Event::RedrawEventsCleared if cfg!(target_os = "windows") => {
// windows_next_repaint_times.clear();
// winit_app.run_ui_and_paint(None)
vec![]
}
winit::event::Event::RedrawRequested(window_id) if !cfg!(windows) => {
winit::event::Event::RedrawRequested(window_id) if !cfg!(target_os = "windows") => {
windows_next_repaint_times.remove(&window_id);
winit_app.run_ui_and_paint(window_id)
}
@@ -349,7 +352,7 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
match event {
EventResult::Wait => {}
EventResult::RepaintNow(window_id) => {
if cfg!(windows) {
if cfg!(target_os = "windows") {
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
windows_next_repaint_times.remove(&window_id);
@@ -828,7 +831,11 @@ mod glow_integration {
if let Some(window) = &glutin_window_context.windows.get(&ViewportId::MAIN) {
let window = window.read();
if let Some(window) = &window.window {
epi_integration::apply_native_options_to_window(&window.read(), native_options);
epi_integration::apply_native_options_to_window(
&window.read(),
native_options,
window_settings,
);
}
}
@@ -862,7 +869,7 @@ mod glow_integration {
let painter =
egui_glow::Painter::new(gl.clone(), "", self.native_options.shader_version)
.unwrap_or_else(|error| panic!("some OpenGL error occurred {}\n", error));
.unwrap_or_else(|err| panic!("An OpenGL error occurred: {err}\n"));
let system_theme = system_theme(
&gl_window
@@ -1209,15 +1216,13 @@ mod glow_integration {
if let Some(running) = self.running.write().take() {
running.integration.write().save(
running.app.write().as_mut(),
&running
running
.glutin_ctx
.read()
.window(ViewportId::MAIN)
.read()
.window
.as_ref()
.unwrap()
.read(),
.clone(),
);
running.app.write().on_exit(Some(&running.gl));
running.painter.write().destroy();
@@ -1429,10 +1434,8 @@ mod glow_integration {
.collect::<Vec<EventResult>>()
};
integration.maybe_autosave(
app.write().as_mut(),
&win.read().window.as_ref().unwrap().read(),
);
integration
.maybe_autosave(app.write().as_mut(), win.read().window.clone().unwrap());
if win.read().window.as_ref().unwrap().read().is_minimized() == Some(true) {
// On Mac, a minimized Window uses up all CPU:
@@ -1809,7 +1812,11 @@ mod wgpu_integration {
let window_builder =
epi_integration::window_builder(event_loop, title, native_options, window_settings);
let window = create_winit_window_builder(&window_builder).build(event_loop)?;
epi_integration::apply_native_options_to_window(&window, native_options);
epi_integration::apply_native_options_to_window(
&window,
native_options,
window_settings,
);
Ok((window, window_builder))
}
@@ -2104,13 +2111,11 @@ mod wgpu_integration {
fn save_and_destroy(&mut self) {
if let Some(mut running) = self.running.take() {
if let Some((Some(window), _, _, _, _)) =
running.windows.read().get(&ViewportId::MAIN)
{
if let Some((window, _, _, _, _)) = running.windows.read().get(&ViewportId::MAIN) {
running
.integration
.write()
.save(running.app.as_mut(), &window.read());
.save(running.app.as_mut(), window.clone());
}
#[cfg(feature = "glow")]
@@ -2276,7 +2281,7 @@ mod wgpu_integration {
let Some((_, (Some(window), _, _, _, _))) = windows_id.get(&window_id).and_then(|id|(windows.read().get(id).map(|w|(*id, w.clone())))) else{return vec![]};
integration
.write()
.maybe_autosave(app.as_mut(), &window.read());
.maybe_autosave(app.as_mut(), window.clone());
if window.read().is_minimized() == Some(true) {
// On Mac, a minimized Window uses up all CPU:

View File

@@ -68,7 +68,7 @@ pub fn push_touches(runner: &mut AppRunner, phase: egui::TouchPhase, event: &web
id: egui::TouchId::from(touch.identifier()),
phase,
pos: pos_from_touch(canvas_origin, &touch),
force: touch.force(),
force: Some(touch.force()),
});
}
}

View File

@@ -104,7 +104,7 @@ pub fn canvas_element(canvas_id: &str) -> Option<web_sys::HtmlCanvasElement> {
pub fn canvas_element_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElement {
canvas_element(canvas_id)
.unwrap_or_else(|| panic!("Failed to find canvas with id {:?}", canvas_id))
.unwrap_or_else(|| panic!("Failed to find canvas with id {canvas_id:?}"))
}
fn canvas_origin(canvas_id: &str) -> egui::Pos2 {

View File

@@ -104,8 +104,7 @@ pub fn install_text_agent(runner_ref: &WebRunner) -> Result<(), JsValue> {
runner_ref.add_event_listener(&input, "focusout", move |_event: web_sys::MouseEvent, _| {
// Delay 10 ms, and focus again.
let func = js_sys::Function::new_no_args(&format!(
"document.getElementById('{}').focus()",
AGENT_ID
"document.getElementById('{AGENT_ID}').focus()"
));
window
.set_timeout_with_callback_and_timeout_and_arguments_0(&func, 10)
@@ -221,8 +220,8 @@ pub fn move_text_cursor(cursor: Option<egui::Pos2>, canvas_id: &str) -> Option<(
let x = (x - canvas.offset_width() as f32 / 2.0)
.min(canvas.client_width() as f32 - bounding_rect.width() as f32);
style.set_property("position", "absolute").ok()?;
style.set_property("top", &format!("{}px", y)).ok()?;
style.set_property("left", &format!("{}px", x)).ok()
style.set_property("top", &format!("{y}px")).ok()?;
style.set_property("left", &format!("{x}px")).ok()
})
} else {
style.set_property("position", "absolute").ok()?;

View File

@@ -27,7 +27,7 @@ impl WebPainterGlow {
let gl = std::sync::Arc::new(gl);
let painter = egui_glow::Painter::new(gl, shader_prefix, None)
.map_err(|error| format!("Error starting glow painter: {}", error))?;
.map_err(|err| format!("Error starting glow painter: {err}"))?;
Ok(Self {
canvas,

View File

@@ -87,8 +87,7 @@ impl WebPainterWgpu {
} else {
// Workaround for https://github.com/gfx-rs/wgpu/issues/3710:
// Don't use `create_surface_from_canvas`, but `create_surface` instead!
let raw_window =
EguiWebWindow(egui::util::hash(&format!("egui on wgpu {canvas_id}")) as u32);
let raw_window = EguiWebWindow(egui::util::hash(("egui on wgpu", canvas_id)) as u32);
canvas.set_attribute("data-raw-handle", &raw_window.0.to_string());
#[allow(unsafe_code)]