Skip to content

Commit

Permalink
Merge pull request vladbat00#64 from mvlabat/multi-threaded
Browse files Browse the repository at this point in the history
Introduce mutable getters for EguiContext, feature gate immutable ones
  • Loading branch information
vladbat00 authored Feb 4, 2022
2 parents a2d9663 + f4ed94c commit 5511be2
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 36 deletions.
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,8 @@ fn main() {
.run();
}

// Note the usage of `ResMut`. Even though `ctx` method doesn't require
// mutability, accessing the context from different threads will result
// into panic if you don't enable `multi_threaded` feature.
fn ui_example(egui_context: ResMut<EguiContext>) {
egui::Window::new("Hello").show(egui_context.ctx(), |ui| {
fn ui_example(mut egui_context: ResMut<EguiContext>) {
egui::Window::new("Hello").show(egui_context.ctx_mut(), |ui| {
ui.label("world");
});
}
Expand Down
4 changes: 2 additions & 2 deletions examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ fn main() {
.run();
}

fn ui_example(egui_context: Res<EguiContext>) {
egui::Window::new("Hello").show(egui_context.ctx(), |ui| {
fn ui_example(mut egui_context: ResMut<EguiContext>) {
egui::Window::new("Hello").show(egui_context.ctx_mut(), |ui| {
ui.label("world");
});
}
9 changes: 4 additions & 5 deletions examples/two_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ const SECONDARY_EGUI_PASS: &str = "secondary_egui_pass";

fn create_new_window(
mut create_window_events: EventWriter<CreateWindow>,

mut commands: Commands,
mut active_cameras: ResMut<ActiveCameras>,
) {
Expand Down Expand Up @@ -128,13 +127,13 @@ struct SharedUiState {
}

fn ui_first_window(
egui_context: Res<EguiContext>,
mut egui_context: ResMut<EguiContext>,
mut ui_state: Local<UiState>,
mut shared_ui_state: ResMut<SharedUiState>,
) {
egui::Window::new("First Window")
.vscroll(true)
.show(egui_context.ctx(), |ui| {
.show(egui_context.ctx_mut(), |ui| {
ui.horizontal(|ui| {
ui.label("Write something: ");
ui.text_edit_singleline(&mut ui_state.input);
Expand All @@ -152,11 +151,11 @@ fn ui_first_window(
}

fn ui_second_window(
egui_context: Res<EguiContext>,
mut egui_context: ResMut<EguiContext>,
mut ui_state: Local<UiState>,
mut shared_ui_state: ResMut<SharedUiState>,
) {
let ctx = match egui_context.try_ctx_for_window(*SECOND_WINDOW_ID) {
let ctx = match egui_context.try_ctx_for_window_mut(*SECOND_WINDOW_ID) {
Some(ctx) => ctx,
None => return,
};
Expand Down
12 changes: 6 additions & 6 deletions examples/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ fn load_assets(mut egui_context: ResMut<EguiContext>, assets: Res<AssetServer>)
egui_context.set_egui_texture(BEVY_TEXTURE_ID, texture_handle);
}

fn configure_visuals(egui_ctx: ResMut<EguiContext>) {
egui_ctx.ctx().set_visuals(egui::Visuals {
fn configure_visuals(mut egui_ctx: ResMut<EguiContext>) {
egui_ctx.ctx_mut().set_visuals(egui::Visuals {
window_corner_radius: 0.0,
..Default::default()
});
Expand Down Expand Up @@ -72,7 +72,7 @@ fn ui_example(

egui::SidePanel::left("side_panel")
.default_width(200.0)
.show(egui_ctx.ctx(), |ui| {
.show(egui_ctx.ctx_mut(), |ui| {
ui.heading("Side Panel");

ui.horizontal(|ui| {
Expand Down Expand Up @@ -105,7 +105,7 @@ fn ui_example(
});
});

egui::TopBottomPanel::top("top_panel").show(egui_ctx.ctx(), |ui| {
egui::TopBottomPanel::top("top_panel").show(egui_ctx.ctx_mut(), |ui| {
// The top panel is often a good place for a menu bar:
egui::menu::bar(ui, |ui| {
egui::menu::menu_button(ui, "File", |ui| {
Expand All @@ -116,7 +116,7 @@ fn ui_example(
});
});

egui::CentralPanel::default().show(egui_ctx.ctx(), |ui| {
egui::CentralPanel::default().show(egui_ctx.ctx_mut(), |ui| {
ui.heading("Egui Template");
ui.hyperlink("https://github.com/emilk/egui_template");
ui.add(egui::github_link_file_line!(
Expand All @@ -140,7 +140,7 @@ fn ui_example(

egui::Window::new("Window")
.vscroll(true)
.show(egui_ctx.ctx(), |ui| {
.show(egui_ctx.ctx_mut(), |ui| {
ui.label("Windows can be moved by dragging them.");
ui.label("They are automatically sized based on contents.");
ui.label("You can turn on resizing and scrolling if you like.");
Expand Down
53 changes: 41 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@
//! .run();
//! }
//!
//! // Note the usage of `ResMut`. Even though `ctx` method doesn't require
//! // mutability, accessing the context from different threads will result
//! // into panic if you don't enable `multi_threaded` feature.
//! fn ui_example(egui_context: Res<EguiContext>) {
//! egui::Window::new("Hello").show(egui_context.ctx(), |ui| {
//! fn ui_example(mut egui_context: ResMut<EguiContext>) {
//! egui::Window::new("Hello").show(egui_context.ctx_mut(), |ui| {
//! ui.label("world");
//! });
//! }
Expand Down Expand Up @@ -224,19 +221,22 @@ impl EguiContext {

/// Egui context of the primary window.
///
/// Note: accessing the context from different threads simultaneously requires enabling
/// `egui/multi_threaded` feature.
/// This function is only available when the `multi_threaded` feature is enabled.
/// The preferable way is to use `ctx_mut` to avoid unpredictable blocking inside UI systems.
#[cfg(feature = "multi_threaded")]
#[track_caller]
pub fn ctx(&self) -> &egui::CtxRef {
self.ctx.get(&WindowId::primary()).expect("`EguiContext::ctx` was called for an uninitialized context (primary window), consider moving your startup system to `StartupStage::Startup` stage or run it after `EguiStartupSystem::InitContexts` system")
}

/// Egui context for a specific window.
/// If you want to display UI on a non-primary window,
/// make sure to set up the render graph by calling [`setup_pipeline`].
/// If you want to display UI on a non-primary window, make sure to set up the render graph by
/// calling [`setup_pipeline`].
///
/// Note: accessing the context from different threads simultaneously requires enabling
/// `egui/multi_threaded` feature.
/// This function is only available when the `multi_threaded` feature is enabled.
/// The preferable way is to use `ctx_for_window_mut` to avoid unpredictable blocking inside UI
/// systems.
#[cfg(feature = "multi_threaded")]
#[track_caller]
pub fn ctx_for_window(&self, window: WindowId) -> &egui::CtxRef {
self.ctx
Expand All @@ -245,11 +245,40 @@ impl EguiContext {
.unwrap()
}

/// Fallible variant of [`EguiContext::ctx_for_window`]. Make sure to set up the render graph by calling [`setup_pipeline`].
/// Fallible variant of [`EguiContext::ctx_for_window`]. Make sure to set up the render graph by
/// calling [`setup_pipeline`].
///
/// This function is only available when the `multi_threaded` feature is enabled.
/// The preferable way is to use `try_ctx_for_window_mut` to avoid unpredictable blocking inside
/// UI systems.
#[cfg(feature = "multi_threaded")]
pub fn try_ctx_for_window(&self, window: WindowId) -> Option<&egui::CtxRef> {
self.ctx.get(&window)
}

/// Egui context of the primary window.
#[track_caller]
pub fn ctx_mut(&mut self) -> &egui::CtxRef {
self.ctx.get(&WindowId::primary()).expect("`EguiContext::ctx_mut` was called for an uninitialized context (primary window), consider moving your startup system to `StartupStage::Startup` stage or run it after `EguiStartupSystem::InitContexts` system")
}

/// Egui context for a specific window.
/// If you want to display UI on a non-primary window, make sure to set up the render graph by
/// calling [`setup_pipeline`].
#[track_caller]
pub fn ctx_for_window_mut(&mut self, window: WindowId) -> &egui::CtxRef {
self.ctx
.get(&window)
.ok_or_else(|| format!("`EguiContext::ctx_for_window_mut` was called for an uninitialized context (window {}), consider moving your UI system to `CoreStage::Update` or run it after `EguiSystem::BeginFrame` system (`StartupStage::Startup` or `EguiStartupSystem::InitContexts` for startup systems respectively)", window))
.unwrap()
}

/// Fallible variant of [`EguiContext::ctx_for_window_mut`]. Make sure to set up the render
/// graph by calling [`setup_pipeline`].
pub fn try_ctx_for_window_mut(&mut self, window: WindowId) -> Option<&egui::CtxRef> {
self.ctx.get(&window)
}

/// Can accept either a strong or a weak handle.
///
/// You may want to pass a weak handle if you control removing texture assets in your
Expand Down
13 changes: 7 additions & 6 deletions src/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,24 +334,25 @@ pub fn begin_frame(
}

pub fn process_output(
egui_context: Res<EguiContext>,
mut egui_context: ResMut<EguiContext>,
mut egui_output: ResMut<HashMap<WindowId, EguiOutput>>,
mut egui_shapes: ResMut<HashMap<WindowId, EguiShapes>>,
#[cfg(feature = "manage_clipboard")] mut egui_clipboard: ResMut<crate::EguiClipboard>,
winit_windows: Option<Res<WinitWindows>>,
) {
for id in egui_context.ctx.keys().copied() {
let (output, shapes) = egui_context.ctx_for_window(id).end_frame();
egui_shapes.entry(id).or_default().shapes = shapes;
egui_output.entry(id).or_default().output = output.clone();
let window_ids: Vec<_> = egui_context.ctx.keys().copied().collect();
for window_id in window_ids {
let (output, shapes) = egui_context.ctx_for_window_mut(window_id).end_frame();
egui_shapes.entry(window_id).or_default().shapes = shapes;
egui_output.entry(window_id).or_default().output = output.clone();

#[cfg(feature = "manage_clipboard")]
if !output.copied_text.is_empty() {
egui_clipboard.set_contents(&output.copied_text);
}

if let Some(ref winit_windows) = winit_windows {
if let Some(winit_window) = winit_windows.get_window(id) {
if let Some(winit_window) = winit_windows.get_window(window_id) {
winit_window.set_cursor_icon(
egui_to_winit_cursor_icon(output.cursor_icon)
.unwrap_or(winit::window::CursorIcon::Default),
Expand Down

0 comments on commit 5511be2

Please sign in to comment.