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

Added Context::is_context_menu_open() #3267

Merged
merged 1 commit into from
Aug 22, 2023

Conversation

dmlary
Copy link
Contributor

@dmlary dmlary commented Aug 20, 2023

I encountered a case where I needed to know if an egui context menu was open, even if the mouse cursor was not in an egui area (full details in discord: https://discord.com/channels/900275882684477440/1142659953102966884).

I found that every resource I needed to detect that the context menu was open was either private, or pub(crate). Also, the Response returned by Response::context_menu() contains no way to detect that the context menu was opened or closed, so there's nothing to hook there.

While it is possible to wrap the Response::context_menu() in code to do state-tracking, it becomes necessary to duplicate that code in every place you call context_menu().

In this commit, I add Context::is_context_menu_open(). Named similarly to Context::is_pointer_over_area(), this method will return true if any context menu is open. This is possible because the context menu uses a temp var with a fixed Id to store its state. This method just fetches the state, and interrogates it to see if there is a menu present.

One pub(crate) helper method, BarState::has_root(), was added as all the fields needed to perform the check were private to the menu module.

I've also updated the Context Menu demo to show the result of is_context_menu_open() to verify the code functions as expected.

I encountered a case where I needed to know if an egui context menu was
open, even if the mouse cursor was not in an egui area (full details in
discord).  I found that every resource I needed to detect that the menu
was open was either private, or pub(crate).  While it is possible to
wrap the `Response::context_menu()` in code to do state-tracking, it
becomes necessary to duplicate that code in every place you use a
context menu.

In this commit, I add `Context::is_context_menu_open()`.  Named similarly
to `Context::is_pointer_over_area()`, this method will return true if
any context menu is open.  This is possible because the context menu
uses a temp var with a fixed Id to store its state.  This method just
fetches the state, and interrogates it to see if there is a menu
present.

One helper method, `BarState::has_root()`, was added as all the fields
needed to perform the check were private to the `menu` module.

I've also updated the Context Menu demo to show the result of
`is_context_menu_open()` to verify the code functions as expected.
@dmlary
Copy link
Contributor Author

dmlary commented Aug 20, 2023

Video of updated demo; github wouldn't inline it in the description.

Screen.Recording.2023-08-20.at.2.10.26.PM.mov

@dmlary
Copy link
Contributor Author

dmlary commented Aug 20, 2023

And here's the equivalent code needed to track the context_menu state from outside of egui:

        // create an area covering the entire screen that we use to detect
        // right-click to open the context menu.
        egui::Area::new("context_menu_area")
            // set the area as background so 'is_pointer_over_area()' ignores
            // this area when checking the pointer location.
            .order(egui::Order::Background)
            // set the position to (0, 0) to override padding/spacing and cover
            // the entire screen
            .fixed_pos([0.0, 0.0])
            .show(ctx, |ui| {
                // We need to know when the context menu is open so we don't
                // move the tile-selection cursor.  There is no mechanism in
                // egui to do this, so instead we need to manually track it
                // ourselves.
                //
                // Assume the menu is always closed; update it to open when it
                // is drawn.  If the menu state doesn't match the UiState value
                // at the end, update the UiState.
                let mut menu_is_open = false;

                // create a response covering the entire screen to detect click
                // for the context menu
                ui.allocate_response(ui.available_size(), egui::Sense::click())
                    .context_menu(|ui| {
                        let mut close_menu = false;
                        if ui.button("Link to new Chunk").clicked() {
                            close_menu = true;
                        }
                        if ui.button("Link to Existing Chunk").clicked() {
                            close_menu = true;
                        }
                        if ui.button("Link to Existing Tile").clicked() {
                            close_menu = true;
                        }

                        if close_menu {
                            ui.close_menu();
                        } else {
                            menu_is_open = true;
                        }
                    });

                // if the menu state changed, upsdate the ui state
                if menu_is_open != ui_state.context_menu {
                    debug!("context menu state change: {:?}", menu_is_open);
                    world.resource_mut::<UiState>().context_menu = menu_is_open;
                }
            });

@emilk emilk added the egui label Aug 22, 2023
@emilk emilk merged commit 32a63da into emilk:master Aug 22, 2023
7 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants