Open dagit opened 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();
}
})
});
}
}
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.
Related: #2538
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