1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-27 15:13:12 -04:00

Some more work for multiples windows support

This commit is contained in:
Konkitoman
2023-07-18 15:29:56 +03:00
parent 7cbe26a1a1
commit c91de8a871
9 changed files with 382 additions and 144 deletions

View File

@@ -7,7 +7,7 @@ use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _};
#[cfg(feature = "accesskit")]
use egui::accesskit;
use egui::NumExt as _;
use egui::{window::WindowBuilder, NumExt as _};
#[cfg(feature = "accesskit")]
use egui_winit::accesskit_winit;
use egui_winit::{native_pixels_per_point, EventResponse, WindowSettings};
@@ -77,7 +77,7 @@ pub fn window_builder<E>(
title: &str,
native_options: &epi::NativeOptions,
window_settings: Option<WindowSettings>,
) -> winit::window::WindowBuilder {
) -> WindowBuilder {
let epi::NativeOptions {
maximized,
decorated,
@@ -97,16 +97,14 @@ pub fn window_builder<E>(
..
} = native_options;
let window_icon = icon_data.clone().and_then(load_icon);
let mut window_builder = winit::window::WindowBuilder::new()
let mut window_builder = WindowBuilder::default()
.with_title(title)
.with_decorations(*decorated)
.with_fullscreen(fullscreen.then(|| winit::window::Fullscreen::Borderless(None)))
.with_fullscreen(*fullscreen)
.with_maximized(*maximized)
.with_resizable(*resizable)
.with_transparent(*transparent)
.with_window_icon(window_icon)
.with_window_icon(icon_data.clone().map(|d| (d.width, d.height, d.rgba)))
.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.
@@ -127,13 +125,13 @@ pub fn window_builder<E>(
}
if let Some(min_size) = *min_window_size {
window_builder = window_builder.with_min_inner_size(points_to_size(min_size));
window_builder = window_builder.with_min_inner_size((min_size.x as u32, min_size.y as u32));
}
if let Some(max_size) = *max_window_size {
window_builder = window_builder.with_max_inner_size(points_to_size(max_size));
window_builder = window_builder.with_max_inner_size((max_size.x as u32, max_size.y as u32));
}
window_builder = window_builder_drag_and_drop(window_builder, *drag_and_drop_support);
window_builder = window_builder.with_drag_and_drop(*drag_and_drop_support);
let inner_size_points = if let Some(mut window_settings) = window_settings {
// Restore pos/size from previous session
@@ -144,16 +142,14 @@ pub fn window_builder<E>(
window_settings.inner_size_points()
} else {
if let Some(pos) = *initial_window_pos {
window_builder = window_builder.with_position(winit::dpi::LogicalPosition {
x: pos.x as f64,
y: pos.y as f64,
});
window_builder = window_builder.with_position((pos.x as i32, pos.y as i32));
}
if let Some(initial_window_size) = *initial_window_size {
let initial_window_size =
initial_window_size.at_most(largest_monitor_point_size(event_loop));
window_builder = window_builder.with_inner_size(points_to_size(initial_window_size));
window_builder = window_builder
.with_inner_size((initial_window_size.x as u32, initial_window_size.y as u32));
}
*initial_window_size
@@ -166,7 +162,7 @@ pub fn window_builder<E>(
if monitor_size.width > 0.0 && monitor_size.height > 0.0 {
let x = (monitor_size.width - inner_size.x as f64) / 2.0;
let y = (monitor_size.height - inner_size.y as f64) / 2.0;
window_builder = window_builder.with_position(winit::dpi::LogicalPosition { x, y });
window_builder = window_builder.with_position((x as i32, y as i32));
}
}
}
@@ -201,7 +197,7 @@ fn largest_monitor_point_size<E>(event_loop: &EventLoopWindowTarget<E>) -> egui:
}
}
fn load_icon(icon_data: epi::IconData) -> Option<winit::window::Icon> {
pub fn load_icon(icon_data: epi::IconData) -> Option<winit::window::Icon> {
winit::window::Icon::from_rgba(icon_data.rgba, icon_data.width, icon_data.height).ok()
}

View File

@@ -3,7 +3,7 @@
use std::time::Instant;
use egui::epaint::ahash::HashMap;
use egui::{epaint::ahash::HashMap, window::WindowBuilder};
use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _};
use winit::event_loop::{
ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget,
@@ -15,7 +15,7 @@ use egui_winit::winit;
use crate::{epi, Result};
use super::epi_integration::{self, EpiIntegration};
use super::epi_integration::{self, load_icon, EpiIntegration};
// ----------------------------------------------------------------------------
@@ -400,12 +400,13 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
mod glow_integration {
use std::sync::Arc;
use egui::{epaint::ahash::HashMap, NumExt as _};
use egui::{epaint::ahash::HashMap, window::WindowBuilder, NumExt as _};
use glutin::{
display::GetGlDisplay,
prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext},
surface::GlSurface,
};
use winit::dpi::{PhysicalPosition, PhysicalSize};
use super::*;
@@ -436,7 +437,7 @@ mod glow_integration {
}
struct Window {
builder: winit::window::WindowBuilder,
builder: WindowBuilder,
gl_surface: Option<glutin::surface::Surface<glutin::surface::WindowSurface>>,
window: Option<winit::window::Window>,
window_id: u64,
@@ -468,7 +469,7 @@ mod glow_integration {
///
#[allow(unsafe_code)]
unsafe fn new(
winit_window_builder: winit::window::WindowBuilder,
window_builder: WindowBuilder,
native_options: &epi::NativeOptions,
event_loop: &EventLoopWindowTarget<UserEvent>,
) -> Result<Self> {
@@ -516,7 +517,7 @@ mod glow_integration {
let (window, gl_config) = 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(winit_window_builder.clone()))
.with_window_builder(Some(create_winit_window_builder(&window_builder)))
.build(
event_loop,
config_template_builder.clone(),
@@ -581,7 +582,7 @@ mod glow_integration {
current_gl_context: None,
not_current_gl_context,
windows: vec![Window {
builder: winit_window_builder,
builder: window_builder,
gl_surface: None,
window,
window_id: 0,
@@ -610,7 +611,7 @@ mod glow_integration {
log::debug!("window doesn't exist yet. creating one now with finalize_window");
glutin_winit::finalize_window(
event_loop,
self.windows[0].builder.clone(),
create_winit_window_builder(&self.windows[0].builder),
&self.gl_config,
)
.expect("failed to finalize glutin window")
@@ -947,119 +948,167 @@ mod glow_integration {
painter,
} = running;
let window = gl_window.window(window_index);
let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
egui_glow::painter::clear(
&gl,
screen_size_in_pixels,
app.clear_color(&integration.egui_ctx.style().visuals),
);
let egui::FullOutput {
platform_output,
repaint_after,
textures_delta,
shapes,
} = integration.update(app.as_mut(), window);
integration.handle_platform_output(window, platform_output);
let clipped_primitives = {
crate::profile_scope!("tessellate");
integration.egui_ctx.tessellate(shapes)
mut windows,
};
painter.paint_and_update_textures(
screen_size_in_pixels,
integration.egui_ctx.pixels_per_point(),
&clipped_primitives,
&textures_delta,
);
let screenshot_requested = &mut integration.frame.output.screenshot_requested;
if *screenshot_requested {
*screenshot_requested = false;
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
integration.frame.screenshot.set(Some(screenshot));
}
integration.post_rendering(app.as_mut(), window);
let control_flow;
{
crate::profile_scope!("swap_buffers");
gl_window.swap_buffers().unwrap();
}
let window = gl_window.window(window_index);
integration.post_present(window);
let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
#[cfg(feature = "__screenshot")]
// give it time to settle:
if integration.egui_ctx.frame_nr() == 2 {
if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") {
assert!(
egui_glow::painter::clear(
&gl,
screen_size_in_pixels,
app.clear_color(&integration.egui_ctx.style().visuals),
);
egui::FullOutput {
platform_output,
repaint_after,
textures_delta,
shapes,
windows,
} = integration.update(app.as_mut(), window);
integration.handle_platform_output(window, platform_output);
let clipped_primitives = {
crate::profile_scope!("tessellate");
integration.egui_ctx.tessellate(shapes)
};
painter.paint_and_update_textures(
screen_size_in_pixels,
integration.egui_ctx.pixels_per_point(),
&clipped_primitives,
&textures_delta,
);
let screenshot_requested =
&mut integration.frame.output.screenshot_requested;
if *screenshot_requested {
*screenshot_requested = false;
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
integration.frame.screenshot.set(Some(screenshot));
}
integration.post_rendering(app.as_mut(), window);
{
crate::profile_scope!("swap_buffers");
gl_window.swap_buffers().unwrap();
}
integration.post_present(window);
#[cfg(feature = "__screenshot")]
// give it time to settle:
if integration.egui_ctx.frame_nr() == 2 {
if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") {
assert!(
path.ends_with(".png"),
"Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}"
);
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
image::save_buffer(
&path,
screenshot.as_raw(),
screenshot.width() as u32,
screenshot.height() as u32,
image::ColorType::Rgba8,
)
.unwrap_or_else(|err| {
panic!("Failed to save screenshot to {path:?}: {err}");
});
eprintln!("Screenshot saved to {path:?}.");
std::process::exit(0);
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
image::save_buffer(
&path,
screenshot.as_raw(),
screenshot.width() as u32,
screenshot.height() as u32,
image::ColorType::Rgba8,
)
.unwrap_or_else(|err| {
panic!("Failed to save screenshot to {path:?}: {err}");
});
eprintln!("Screenshot saved to {path:?}.");
std::process::exit(0);
}
}
control_flow = if integration.should_close() {
EventResult::Exit
} else if repaint_after.is_zero() {
let mut id = None;
for window in gl_window.windows.iter() {
if window.window_id == 0 {
id = window.window.as_ref().map(|w| w.id());
break;
}
}
EventResult::RepaintNext(id.unwrap())
} else if let Some(repaint_after_instant) =
std::time::Instant::now().checked_add(repaint_after)
{
// if repaint_after is something huge and can't be added to Instant,
// we will use `ControlFlow::Wait` instead.
// technically, this might lead to some weird corner cases where the user *WANTS*
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
// egui backend impl i guess.
let mut id = None;
for window in gl_window.windows.iter() {
if window.window_id == 0 {
id = window.window.as_ref().map(|w| w.id());
break;
}
}
EventResult::RepaintAt(id.unwrap(), repaint_after_instant)
} else {
EventResult::Wait
};
integration.maybe_autosave(app.as_mut(), window);
if window.is_minimized() == Some(true) {
// On Mac, a minimized Window uses up all CPU:
// https://github.com/emilk/egui/issues/325
crate::profile_scope!("bg_sleep");
std::thread::sleep(std::time::Duration::from_millis(10));
}
}
let control_flow = if integration.should_close() {
EventResult::Exit
} else if repaint_after.is_zero() {
let mut id = None;
for window in gl_window.windows.iter() {
if window.window_id == 0 {
id = window.window.as_ref().map(|w| w.id());
break;
let mut wins = vec![0];
windows.retain_mut(|(id, builder)| {
for w in gl_window.windows.iter_mut() {
if w.window_id == *id {
if w.builder != *builder {
if let Some(window) = &mut w.window {
if let Ok(pos) = window.outer_position() {
builder.position = Some((pos.x, pos.y));
}
}
w.window = None;
w.gl_surface = None;
w.builder = builder.clone();
}
wins.push(*id);
return false;
}
}
EventResult::RepaintNext(id.unwrap())
} else if let Some(repaint_after_instant) =
std::time::Instant::now().checked_add(repaint_after)
{
// if repaint_after is something huge and can't be added to Instant,
// we will use `ControlFlow::Wait` instead.
// technically, this might lead to some weird corner cases where the user *WANTS*
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
// egui backend impl i guess.
true
});
let mut id = None;
for window in gl_window.windows.iter() {
if window.window_id == 0 {
id = window.window.as_ref().map(|w| w.id());
break;
}
}
EventResult::RepaintAt(id.unwrap(), repaint_after_instant)
} else {
EventResult::Wait
};
integration.maybe_autosave(app.as_mut(), window);
if window.is_minimized() == Some(true) {
// On Mac, a minimized Window uses up all CPU:
// https://github.com/emilk/egui/issues/325
crate::profile_scope!("bg_sleep");
std::thread::sleep(std::time::Duration::from_millis(10));
for (id, builder) in windows {
gl_window.windows.push(Window {
builder,
gl_surface: None,
window: None,
window_id: id,
});
}
gl_window.windows.retain(|w| wins.contains(&w.window_id));
gl_window.window_maps.retain(|_, id| wins.contains(id));
control_flow
};
@@ -1290,7 +1339,7 @@ mod wgpu_integration {
let window_settings = epi_integration::load_window_settings(storage);
let window_builder =
epi_integration::window_builder(event_loop, title, native_options, window_settings);
let window = window_builder.build(event_loop)?;
let window = create_winit_window_builder(&window_builder).build(event_loop)?;
epi_integration::apply_native_options_to_window(&window, native_options);
Ok(window)
}
@@ -1709,3 +1758,48 @@ fn system_theme(window: &winit::window::Window, options: &NativeOptions) -> Opti
fn extremely_far_future() -> std::time::Instant {
std::time::Instant::now() + std::time::Duration::from_secs(10_000_000_000)
}
fn create_winit_window_builder(builder: &WindowBuilder) -> winit::window::WindowBuilder {
let mut window_builder = winit::window::WindowBuilder::new()
.with_title(builder.title.clone())
.with_transparent(builder.transparent)
.with_decorations(builder.decorations)
.with_resizable(builder.resizable)
.with_visible(builder.visible)
.with_fullscreen(
builder
.fullscreen
.then(|| winit::window::Fullscreen::Borderless(None)),
)
.with_active(builder.active);
if let Some(inner_size) = builder.inner_size {
window_builder = window_builder
.with_inner_size(winit::dpi::PhysicalSize::new(inner_size.0, inner_size.1));
}
if let Some(min_inner_size) = builder.min_inner_size {
window_builder = window_builder.with_min_inner_size(winit::dpi::PhysicalSize::new(
min_inner_size.0,
min_inner_size.1,
));
}
if let Some(max_inner_size) = builder.max_inner_size {
window_builder = window_builder.with_max_inner_size(winit::dpi::PhysicalSize::new(
max_inner_size.0,
max_inner_size.1,
));
}
if let Some(position) = builder.position {
window_builder =
window_builder.with_position(winit::dpi::PhysicalPosition::new(position.0, position.1));
}
if let Some(icon) = builder.icon.clone() {
window_builder = window_builder.with_window_icon(load_icon(crate::IconData {
rgba: icon.2,
width: icon.0,
height: icon.1,
}))
}
window_builder
}

View File

@@ -1,3 +1,5 @@
use egui::window::WindowBuilder;
/// Can be used to store native window settings (position and size).
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
@@ -46,32 +48,20 @@ impl WindowSettings {
self.inner_size_points
}
pub fn initialize_window(
&self,
mut window: winit::window::WindowBuilder,
) -> winit::window::WindowBuilder {
pub fn initialize_window(&self, mut window: WindowBuilder) -> WindowBuilder {
// If the app last ran on two monitors and only one is now connected, then
// the given position is invalid.
// If this happens on Mac, the window is clamped into valid area.
// If this happens on Windows, the clamping behavior is managed by the function
// clamp_window_to_sane_position.
if let Some(pos) = self.position {
window = window.with_position(winit::dpi::PhysicalPosition {
x: pos.x as f64,
y: pos.y as f64,
});
window = window.with_position((pos.x as i32, pos.y as i32));
}
if let Some(inner_size_points) = self.inner_size_points {
window
.with_inner_size(winit::dpi::LogicalSize {
width: inner_size_points.x as f64,
height: inner_size_points.y as f64,
})
.with_fullscreen(
self.fullscreen
.then_some(winit::window::Fullscreen::Borderless(None)),
)
.with_inner_size((inner_size_points.x as u32, inner_size_points.y as u32))
.with_fullscreen(self.fullscreen)
} else {
window
}

View File

@@ -10,7 +10,7 @@ pub mod panel;
pub mod popup;
pub(crate) mod resize;
pub mod scroll_area;
pub(crate) mod window;
pub mod window;
pub use {
area::Area,

View File

@@ -6,6 +6,115 @@ use epaint::*;
use super::*;
#[derive(Hash, PartialEq, Clone, Default)]
pub struct WindowBuilder {
pub title: String,
pub name: Option<String>,
pub position: Option<(i32, i32)>,
pub inner_size: Option<(u32, u32)>,
pub fullscreen: bool,
pub maximized: bool,
pub resizable: bool,
pub transparent: bool,
pub decorations: bool,
pub icon: Option<(u32, u32, Vec<u8>)>,
pub active: bool,
pub visible: bool,
pub title_hidden: bool,
pub titlebar_transparent: bool,
pub fullsize_content_view: bool,
pub min_inner_size: Option<(u32, u32)>,
pub max_inner_size: Option<(u32, u32)>,
pub drag_and_drop: bool,
}
impl WindowBuilder {
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
pub fn with_decorations(mut self, decorations: bool) -> Self {
self.decorations = decorations;
self
}
pub fn with_fullscreen(mut self, fullscreen: bool) -> Self {
self.fullscreen = fullscreen;
self
}
pub fn with_maximized(mut self, maximized: bool) -> Self {
self.maximized = maximized;
self
}
pub fn with_resizable(mut self, resizable: bool) -> Self {
self.resizable = resizable;
self
}
pub fn with_transparent(mut self, transparent: bool) -> Self {
self.transparent = transparent;
self
}
pub fn with_window_icon(mut self, icon: Option<(u32, u32, Vec<u8>)>) -> Self {
self.icon = icon;
self
}
pub fn with_active(mut self, active: bool) -> Self {
self.active = active;
self
}
pub fn with_visible(mut self, visible: bool) -> Self {
self.visible = visible;
self
}
pub fn with_title_hidden(mut self, title_hidden: bool) -> Self {
self.title_hidden = title_hidden;
self
}
pub fn with_titlebar_transparent(mut self, value: bool) -> Self {
self.titlebar_transparent = value;
self
}
pub fn with_fullsize_content_view(mut self, value: bool) -> Self {
self.fullsize_content_view = value;
self
}
pub fn with_inner_size(mut self, value: (u32, u32)) -> Self {
self.inner_size = Some(value);
self
}
pub fn with_min_inner_size(mut self, value: (u32, u32)) -> Self {
self.min_inner_size = Some(value);
self
}
pub fn with_max_inner_size(mut self, value: (u32, u32)) -> Self {
self.max_inner_size = Some(value);
self
}
pub fn with_drag_and_drop(mut self, value: bool) -> Self {
self.drag_and_drop = value;
self
}
pub fn with_position(mut self, value: (i32, i32)) -> Self {
self.position = Some(value);
self
}
}
/// Builder for a floating window which can be dragged, closed, collapsed, resized and scrolled (off by default).
///
/// You can customize:
@@ -287,6 +396,8 @@ impl<'open> Window<'open> {
ctx: &Context,
add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
) -> Option<InnerResponse<Option<R>>> {
let window_id =
ctx.data_mut(|data| *data.get_temp_mut_or(self.area.id.with("window_id"), 0));
let Window {
title,
open,

View File

@@ -6,6 +6,7 @@ use crate::{
input_state::*, layers::GraphicLayers, memory::Options, os::OperatingSystem,
output::FullOutput, util::IdTypeMap, TextureHandle, *,
};
use ahash::HashMap;
use epaint::{mutex::*, stats::*, text::Fonts, TessellationOptions, *};
/// Information given to the backend about when it is time to repaint the ui.
@@ -21,6 +22,9 @@ pub struct RequestRepaintInfo {
/// This can be compared to [`Context::frame_nr`] to see if we've already
/// triggered the painting of the next frame.
pub current_frame_nr: u64,
/// This is used to specify what window to redraw
pub window_id: u64,
}
// ----------------------------------------------------------------------------
@@ -80,11 +84,11 @@ impl Default for Repaint {
}
impl Repaint {
fn request_repaint(&mut self) {
self.request_repaint_after(std::time::Duration::ZERO);
fn request_repaint(&mut self, window_id: u64) {
self.request_repaint_after(std::time::Duration::ZERO, window_id);
}
fn request_repaint_after(&mut self, after: std::time::Duration) {
fn request_repaint_after(&mut self, after: std::time::Duration, window_id: u64) {
if after == std::time::Duration::ZERO {
// Do a few extra frames to let things settle.
// This is a bit of a hack, and we don't support it for `repaint_after` callbacks yet.
@@ -100,6 +104,7 @@ impl Repaint {
let info = RequestRepaintInfo {
after,
current_frame_nr: self.frame_nr,
window_id: window_id,
};
(callback)(info);
}
@@ -155,6 +160,9 @@ struct ContextImpl {
repaint: Repaint,
windows: HashMap<String, (WindowBuilder, u64, bool)>,
current_window_id: u64,
/// Written to during the frame.
layer_rects_this_frame: ahash::HashMap<LayerId, Vec<(Id, Rect)>>,
@@ -956,7 +964,7 @@ impl Context {
/// (this will work on `eframe`).
pub fn request_repaint(&self) {
// request two frames of repaint, just to cover some corner cases (frame delays):
self.write(|ctx| ctx.repaint.request_repaint());
self.write(|ctx| ctx.repaint.request_repaint(ctx.current_window_id));
}
/// Request repaint after at most the specified duration elapses.
@@ -988,9 +996,9 @@ impl Context {
/// timeout takes 500 milliseconds AFTER the vsync swap buffer.
/// So, its not that we are requesting repaint within X duration. We are rather timing out
/// during app idle time where we are not receiving any new input events.
pub fn request_repaint_after(&self, duration: std::time::Duration) {
pub fn request_repaint_after(&self, duration: std::time::Duration, window_id: u64) {
// Maybe we can check if duration is ZERO, and call self.request_repaint()?
self.write(|ctx| ctx.repaint.request_repaint_after(duration));
self.write(|ctx| ctx.repaint.request_repaint_after(duration, window_id));
}
/// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`].
@@ -1260,11 +1268,19 @@ impl Context {
let repaint_after = self.write(|ctx| ctx.repaint.end_frame());
let shapes = self.drain_paint_lists();
let windows = self.write(|ctx| {
ctx.windows
.drain()
.map(|(_, (builder, id, _))| (id, builder))
.collect()
});
FullOutput {
platform_output,
repaint_after,
textures_delta,
shapes,
windows,
}
}
@@ -1869,6 +1885,30 @@ impl Context {
}
}
use containers::window::WindowBuilder;
/// # Windows
impl Context {
pub fn set_current_window_id(&self, window_id: u64) {
self.write(|ctx| ctx.current_window_id = window_id);
}
pub fn current_window_id(&self) -> u64 {
self.read(|ctx| ctx.current_window_id)
}
pub fn create_window(&self, window_builder: WindowBuilder) -> u64 {
self.write(|ctx| {
if let Some(window) = ctx.windows.get(&window_builder.title) {
window.1
} else {
let id = ctx.windows.len() as u64 + 1;
ctx.windows
.insert(window_builder.title.clone(), (window_builder, id, true));
id
}
})
}
}
#[test]
fn context_impl_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}

View File

@@ -1,6 +1,6 @@
//! All the data egui returns to the backend at the end of each frame.
use crate::WidgetType;
use crate::{window::WindowBuilder, WidgetType};
/// What egui emits each frame from [`crate::Context::run`].
///
@@ -30,6 +30,8 @@ pub struct FullOutput {
///
/// You can use [`crate::Context::tessellate`] to turn this into triangles.
pub shapes: Vec<epaint::ClippedShape>,
pub windows: Vec<(u64, WindowBuilder)>,
}
impl FullOutput {
@@ -40,12 +42,14 @@ impl FullOutput {
repaint_after,
textures_delta,
shapes,
mut windows,
} = newer;
self.platform_output.append(platform_output);
self.repaint_after = repaint_after; // if the last frame doesn't need a repaint, then we don't need to repaint
self.textures_delta.append(textures_delta);
self.shapes = shapes; // Only paint the latest
self.windows.append(&mut windows);
}
}

View File

@@ -281,7 +281,10 @@ impl BackendPanel {
let ctx = ui.ctx().clone();
call_after_delay(std::time::Duration::from_secs(2), move || {
log::info!("Request a repaint in 3s...");
ctx.request_repaint_after(std::time::Duration::from_secs(3));
ctx.request_repaint_after(
std::time::Duration::from_secs(3),
ctx.current_window_id(),
);
});
}
});

View File

@@ -128,6 +128,6 @@ impl eframe::App for Application {
});
});
ctx.request_repaint_after(Self::repaint_max_timeout());
ctx.request_repaint_after(Self::repaint_max_timeout(), ctx.current_window_id());
}
}