Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow eframe apps to be detached #3340

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

rockisch
Copy link

@rockisch rockisch commented Sep 15, 2023

One choice that I'm not really sure which I prefer is whether we should check for window.id() == window_id on WindowEvent events inside the on_event method or not. Since we're already exposing the window, the user could easily check that outside. However, when writing some example code, it ended up looking really bad and confusing. At the same time, people who will use this are probably people who have a good understanding of winit and GUI development in general, so maybe that's not an issue.

This gist shows how the 2 approaches would look like:
https://gist.github.com/rockisch/c672345661dd5411f5611b7ab14faea0

This initial version of the PR has the check inside, but I could remove it if wanted.

@emilk
Copy link
Owner

emilk commented Sep 18, 2023

This looks very promising! Can you please add a working example too? 🙏

@rockisch
Copy link
Author

Added a working example and fixed the CI issues (was on a Windows machine before, so I couldn't run the check.sh script)

@rockisch rockisch requested a review from emilk September 19, 2023 11:51
@rockisch rockisch marked this pull request as ready for review September 19, 2023 11:51
@rockisch
Copy link
Author

Btw, I went and changed one of my projects to this approach, and I noticed that although it does work, communication between the 'outer' code and the 'detached eframe app' is kinda clunky, since there's no way to access or modify the app data from the returned reference.

The way I'm doing this at the moment is by leaking a RefCell, more or less like this:

struct SharedState {
    flag: bool
}

struct MyApp {
    shared: &'static RefCell<SharedState>
}

let shared_state = Box::leak(Box::new(RefCell::new(SharedState { flag: true }))) as &_;
let detached_app = eframe::run_detached_native(
    ...,
    Box::new(|_cc| Box::new(MyApp { shared: shared_state }))
)
// 'shared_state' can be accessed and modified

How would you feel about exposing a get_app(&self) -> MyApp and get_app_mut(&mut self) -> MyApp on Detached? I took a look at the source code and I feel like I could add those with minimal changes.

@CoryRobertson
Copy link

not sure I follow entirely but would this pr allow you to open a completely detached eframe, meaning you would have two separate event loops?

@Aaron1011
Copy link
Contributor

What's the status of this PR? This would be very useful for Ruffle

@samyak-jain
Copy link

Btw, I went and changed one of my projects to this approach, and I noticed that although it does work, communication between the 'outer' code and the 'detached eframe app' is kinda clunky, since there's no way to access or modify the app data from the returned reference.

The way I'm doing this at the moment is by leaking a RefCell, more or less like this:

struct SharedState {
    flag: bool
}

struct MyApp {
    shared: &'static RefCell<SharedState>
}

let shared_state = Box::leak(Box::new(RefCell::new(SharedState { flag: true }))) as &_;
let detached_app = eframe::run_detached_native(
    ...,
    Box::new(|_cc| Box::new(MyApp { shared: shared_state }))
)
// 'shared_state' can be accessed and modified

How would you feel about exposing a get_app(&self) -> MyApp and get_app_mut(&mut self) -> MyApp on Detached? I took a look at the source code and I feel like I could add those with minimal changes.

Is the problem that we need to give away ownership of the MyApp instance? Shouldn't the shared state be accessible when you are implementing the update method on MyApp?

Btw, if there's anyway I can contribute, I would be happy to help.
Thanks for taking your time to implement this!

Copy link
Owner

@emilk emilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a merging in of master and testing with multi-viewports, otherwise its looking good

@@ -0,0 +1,7 @@
Example showing some UI controls like `Label`, `TextEdit`, `Slider`, `Button`.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a good description

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the screenshot is not very useful either

);
// Winit window managed by the application
let winit_window = winit::window::WindowBuilder::new()
.build(&event_loop)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Giving this a title of e.g. "winit window" would help explain to the user what is going on

Comment on lines +78 to +88
ui.heading("My egui Application");
ui.horizontal(|ui| {
let name_label = ui.label("Your name: ");
ui.text_edit_singleline(&mut self.name)
.labelled_by(name_label.id);
});
ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age"));
if ui.button("Click each year").clicked() {
self.age += 1;
}
ui.label(format!("Hello '{}', age {}", self.name, self.age));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of showing the generic egui application, it would be better to show some text about what is going on in this example

/// An eframe app detached from the event loop.
///
/// This is useful to run `eframe` apps while still having control over the event loop.
/// For example, when you need to have a window managed by `egui` and another managed
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// For example, when you need to have a window managed by `egui` and another managed
/// For example, when you need to have a window managed by `eframe` and another managed

/// Execute an app in a detached state.
///
/// This allows you to control the event loop itself, which is necessary in a few
/// occasions, like when you need a screen not managed by `egui`.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// occasions, like when you need a screen not managed by `egui`.
/// occasions, like when you need a screen not managed by `eframe`.

Comment on lines +422 to +423
// Ignore window events for other windows
if let Some(window) = self.winit_app.window() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to merge in master and handle the case of multi-viewports

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Eframe] Allow eframe to run on an existing event loop
5 participants