emilk / egui

egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
https://www.egui.rs/
Apache License 2.0
21.91k stars 1.58k forks source link

context_menus as separate windows #5144

Open dagit opened 1 week ago

dagit commented 1 week ago

Is your feature request related to a problem? Please describe.

Right now if you create a context_menu it's constrained to the rendering area of the parent window because it's drawn inside that window. My app is a timer designed for use with OBS. That is, you're meant to setup the window how you want it and then capture it. The user might want a small timer, but this creates an issue with using context_menus where the window is too small to display the menu.

Describe the solution you'd like

I think the cleanest solution now that multiple windows are supported, is to spawn a new native window for these menus.

Describe alternatives you've considered

Well, currently I just use context_menus as implemented but I'm getting complaints from users.

I have not tried the multiwindow API for egui yet, but that's probably what I'll do next. I don't actually know if it will work because I don't know if I can set the right window properties like the location for the window and removing decorations, etc. And that may even be an issue/limitation for my feature request. I seem to recall even winit doesn't fully support this use-case yet.

Additional context

YgorSouza commented 1 week ago

I think the winit issue is https://github.com/rust-windowing/winit/issues/403. It's been open for a long time, but one of the maintainers said it might be obsolete.

In any case, it doesn't seem to be too difficult to make the context menu show up in a new viewport, except that the viewport API itself is pretty finicky and doesn't work very well in some systems.

I tried the code below as a test, but I haven't managed to set the initial position on Linux. Trying to read the position of the main window returns None, and even if I hardcode some position, the viewport always appears in the same place near the top left corner of the screen regardless. This could be a Linux-only problem, but I haven't tested on other platforms. Also, the empty context menu in the main window still appears as a tiny empty area. But if these problems can be solved, this implementation seems to get the job done.

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            let main_window_pos = ctx.viewport(|vp| {
                vp.input
                    .viewport()
                    .outer_rect
                    .map(|r| r.left_top())
                    .unwrap_or_default()
            });
            ui.button("I have a context menu").context_menu(|ui| {
                let ui_pos = ui.max_rect().left_top().to_vec2();
                let mut close = false;
                ctx.show_viewport_immediate(
                    egui::ViewportId::from_hash_of("ctx_menu"),
                    egui::ViewportBuilder::default()
                        .with_decorations(false)
                        .with_position(main_window_pos + ui_pos)
                        .with_inner_size([100.0, 500.0]),
                    |ctx, _class| {
                        egui::CentralPanel::default().show(ctx, |ui| {
                            if ui.button("Option 1").clicked() {
                                close = true;
                            }
                            if ui.button("Option 2").clicked() {
                                close = true;
                            }
                        });
                    },
                );
                if close {
                    ui.close_menu();
                }
            })
        });
    }
}
dagit commented 1 week ago

I think getting the root window's coordinates is going to be one of the trickier parts of doing it this way. As I understand it, for desktop you have to be prepared to deal with multi-monitor setups and on some OSes DPI settings? For those reasons, it would definitely be nice to have a good robust implementation baked into a library (egui or otherwise). I'm not sure if it's even possible to do it this way on wayland. There you might actually be forced to request a special window type. I don't really understand wayland, but I think I read that you don't get to position windows there and so they had to make a special case for menus? That said, I'd be happy if it only works on x11, macOS, and windows.

Another thing I discovered from attempting to do it like you've done it is that calculating the window size is a bit tricky. Maybe this feature request has an additional request that viewport builder gets an "auto size" option sort of like Window has for use cases like this.

YgorSouza commented 1 week ago

Related: #2538