Closing windows restoring z-ordered focus / how to implement menu command for top-most window #727

Open
opened 2016-07-06 18:24:51 +00:00 by pdoane · 12 comments
pdoane commented 2016-07-06 18:24:51 +00:00 (Migrated from github.com)

I am trying to implement a generic command (e.g. File/Close) that should apply to the top-most window. My assumption is that I should write something like this:

    static bool closeWindow;

    closeWindow = false;
    if (ImGui::BeginMainMenuBar())
    {
        if (ImGui::BeginMenu("File"))
        {
            if (ImGui::MenuItem("Close", "Ctrl+W")) { closeWindow = true };
        }
    }

    for (auto it = documents.begin(); it != documents.end(); )
    {
        bool opened = true;
        if (ImGui::Begin(it->name), &opened)
        {
            if (<Is Top Level Window> && closeWindow)
                opened = false;
        }

        if (!opened)
            it = documents.erase(it);
        else
            ++it;
    }

I was thinking I could implement by using IsWindowFocused() but this doesn't work because the menu bar claims focus.

Does this seem like the right strategy or is there another approach that should be used?

I am trying to implement a generic command (e.g. File/Close) that should apply to the top-most window. My assumption is that I should write something like this: ``` static bool closeWindow; closeWindow = false; if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Close", "Ctrl+W")) { closeWindow = true }; } } for (auto it = documents.begin(); it != documents.end(); ) { bool opened = true; if (ImGui::Begin(it->name), &opened) { if (<Is Top Level Window> && closeWindow) opened = false; } if (!opened) it = documents.erase(it); else ++it; } ``` I was thinking I could implement <Is Top Level Window> by using IsWindowFocused() but this doesn't work because the menu bar claims focus. Does this seem like the right strategy or is there another approach that should be used?
ocornut commented 2016-07-06 18:33:21 +00:00 (Migrated from github.com)

You probably need to keep track of your last-focused main document then.

As soon as you start introducing any other window than your "document" window you'll run into the same problem.

You probably need to keep track of your last-focused main document then. As soon as you start introducing any other window than your "document" window you'll run into the same problem.
pdoane commented 2016-07-06 21:05:17 +00:00 (Migrated from github.com)

I tried that approach as well - but it didn't work out, e.g.:

    static Document* s_activeDocument;
    for (auto it = documents.begin(); it != documents.end(); )
    {
        if (ImGui::Begin(it->name))
        {
            if (ImGui::IsWindowFocused())
                s_activeDocument = &*it;
        }
    }

    if (ImGui::BeginMainMenuBar())
    {
        if (ImGui::BeginMenu("File"))
        {
            if (ImGui::MenuItem("Close", "Ctrl+W"))
            { /* erase s_activeDocument from documents };
        }
    }

Now the first Close operation works but the menu bar is still the focus so there is no active document anymore and the second Close operation doesn't do anything. I could require that the user click in the window again to get focus but that's confusing compared to how GUIs typically work.

It might be possible to extend this to keeping track of all documents in focus order. E.g.:

    static vector<Document>* s_documentsInFocusOrder;
    for (auto it = documents.begin(); it != documents.end(); )
    {
        if (ImGui::Begin(it->name))
        {
            if (ImGui::IsWindowFocused())
            {
                /* remove document from s_documentsInFocusOrder and put it at the head */
            }
        }
    }
I tried that approach as well - but it didn't work out, e.g.: ``` static Document* s_activeDocument; for (auto it = documents.begin(); it != documents.end(); ) { if (ImGui::Begin(it->name)) { if (ImGui::IsWindowFocused()) s_activeDocument = &*it; } } if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Close", "Ctrl+W")) { /* erase s_activeDocument from documents }; } } ``` Now the first Close operation works but the menu bar is still the focus so there is no active document anymore and the second Close operation doesn't do anything. I could require that the user click in the window again to get focus but that's confusing compared to how GUIs typically work. It might be possible to extend this to keeping track of all documents in focus order. E.g.: ``` static vector<Document>* s_documentsInFocusOrder; for (auto it = documents.begin(); it != documents.end(); ) { if (ImGui::Begin(it->name)) { if (ImGui::IsWindowFocused()) { /* remove document from s_documentsInFocusOrder and put it at the head */ } } } ```
ocornut commented 2016-07-06 23:00:41 +00:00 (Migrated from github.com)

Well the whole point of tracking the last active document is that when a menu is open you don't lose this information of which one is active.
And the concept of closing a document is completely your code, so there is clearly a bug or misunderstanding on your side.

Sent from my fax machine

On 7 Jul 2016, at 06:05, pdoane notifications@github.com wrote:

I tried that approach as well - but it didn't work out, e.g.:

static Document* s_activeDocument;
for (auto it = documents.begin(); it != documents.end(); )
{
    if (ImGui::Begin(it->name))
    {
        if (ImGui::IsWindowFocused())
            s_activeDocument = &*it;
    }
}

if (ImGui::BeginMainMenuBar())
{
    if (ImGui::BeginMenu("File"))
    {
        if (ImGui::MenuItem("Close", "Ctrl+W"))
        { /* erase s_activeDocument from documents };
    }
}

Now the first Close operation works but the menu bar is still the focus so there is no active document anymore and the second Close operation doesn't do anything. I could require that the user click in the window again to get focus but that's confusing compared to how GUIs typically work.

It might be possible to extend this to keeping track of all documents in focus order. E.g.:

static vector<Document>* s_documentsInFocusOrder;
for (auto it = documents.begin(); it != documents.end(); )
{
    if (ImGui::Begin(it->name))
    {
        if (ImGui::IsWindowFocused())
        {
            /* remove document from s_documentsInFocusOrder and put it at the head */
        }
    }
}

―
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

Well the whole point of tracking the last active document is that when a menu is open you don't lose this information of which one is active. And the concept of closing a document is completely your code, so there is clearly a bug or misunderstanding on your side. Sent from my fax machine > On 7 Jul 2016, at 06:05, pdoane notifications@github.com wrote: > > I tried that approach as well - but it didn't work out, e.g.: > > ``` > static Document* s_activeDocument; > for (auto it = documents.begin(); it != documents.end(); ) > { > if (ImGui::Begin(it->name)) > { > if (ImGui::IsWindowFocused()) > s_activeDocument = &*it; > } > } > > if (ImGui::BeginMainMenuBar()) > { > if (ImGui::BeginMenu("File")) > { > if (ImGui::MenuItem("Close", "Ctrl+W")) > { /* erase s_activeDocument from documents }; > } > } > ``` > > Now the first Close operation works but the menu bar is still the focus so there is no active document anymore and the second Close operation doesn't do anything. I could require that the user click in the window again to get focus but that's confusing compared to how GUIs typically work. > > It might be possible to extend this to keeping track of all documents in focus order. E.g.: > > ``` > static vector<Document>* s_documentsInFocusOrder; > for (auto it = documents.begin(); it != documents.end(); ) > { > if (ImGui::Begin(it->name)) > { > if (ImGui::IsWindowFocused()) > { > /* remove document from s_documentsInFocusOrder and put it at the head */ > } > } > } > ``` > > ― > You are receiving this because you commented. > Reply to this email directly, view it on GitHub, or mute the thread.
pdoane commented 2016-07-06 23:42:38 +00:00 (Migrated from github.com)

Maybe I'm not following how to track the active document state. How can I do this without querying the window focus value?

It's also possible that things are behaving as you would expect, but it's pretty different than typical desktop applications which I think will be confusing.

Maybe I'm not following how to track the active document state. How can I do this without querying the window focus value? It's also possible that things are behaving as you would expect, but it's pretty different than typical desktop applications which I think will be confusing.
pdoane commented 2016-07-07 01:53:16 +00:00 (Migrated from github.com)

Also, just to clearly spell out the user experience:

  • There is a global menu bar and several document windows. No other windows exist.
  • The global menu bar has commands which should be applied to active window. File/Close is a particular one that is problematic.
  • The user chooses File/Close multiple times in succession and expects the documents to be closed according to their current z-order of display.

I can track the last active document by querying the focus state of the window as I iterate the application state for documents. This provides a solution for most commands that need to route to the active document. File/Close is different though because it changes which document is active. If the mechanism to do this is querying the focus state, that has been transferred to the menu bar so there is no longer any active document until the users clicks on a window.

Also, just to clearly spell out the user experience: - There is a global menu bar and several document windows. No other windows exist. - The global menu bar has commands which should be applied to active window. File/Close is a particular one that is problematic. - The user chooses File/Close multiple times in succession and expects the documents to be closed according to their current z-order of display. I can track the last active document by querying the focus state of the window as I iterate the application state for documents. This provides a solution for most commands that need to route to the active document. File/Close is different though because it changes which document is active. If the mechanism to do this is querying the focus state, that has been transferred to the menu bar so there is no longer any active document until the users clicks on a window.
ocornut commented 2016-07-07 02:38:07 +00:00 (Migrated from github.com)

You were doing the right thing.
Just only set the value for your "document" window that's focused, and keep the value unmodified otherwise, aka save it locally.

On 7 Jul 2016, at 08:42, pdoane notifications@github.com wrote:

Maybe I'm not following how to track the active document state. How can I do this without querying the window focus value?

It's also possible that things are behaving as you would expect, but it's pretty different than typical desktop applications which I think will be confusing.

―
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

You were doing the right thing. Just only set the value for your "document" window that's focused, and keep the value unmodified otherwise, aka save it locally. > On 7 Jul 2016, at 08:42, pdoane notifications@github.com wrote: > > Maybe I'm not following how to track the active document state. How can I do this without querying the window focus value? > > It's also possible that things are behaving as you would expect, but it's pretty different than typical desktop applications which I think will be confusing. > > ― > You are receiving this because you commented. > Reply to this email directly, view it on GitHub, or mute the thread.
pdoane commented 2016-07-07 03:39:53 +00:00 (Migrated from github.com)

But again that doesn't work (at least with a single state value). The sequence looks like this:

Initial state:

  • Documents are traversed, windows are submitted, one of them is the focus and the active document value is latched.

1st Close Command:

  • Menu bar processing begins and focus is transferred to it
  • Close command is executed and dispatched to the active document
  • Focus is still the menu bar

Next frame:

  • Documents are traversed to build the window. None of them are the focus so the active document remains null.

2nd close command:

  • Focus is still the menu bar
  • Close is chosen by the user but it doesn't know where to route it.

If the internal ImGui z-order structure is mirrored application side, then this can be made to work but it doesn't seem like you are suggesting that.

But again that doesn't work (at least with a single state value). The sequence looks like this: Initial state: - Documents are traversed, windows are submitted, one of them is the focus and the active document value is latched. 1st Close Command: - Menu bar processing begins and focus is transferred to it - Close command is executed and dispatched to the active document - Focus is still the menu bar Next frame: - Documents are traversed to build the window. None of them are the focus so the active document remains null. 2nd close command: - Focus is still the menu bar - Close is chosen by the user but it doesn't know where to route it. If the internal ImGui z-order structure is mirrored application side, then this can be made to work but it doesn't seem like you are suggesting that.
johanwendin commented 2016-07-07 09:05:05 +00:00 (Migrated from github.com)

Why not keep a (unique) stack of focused windows locally then?

After each close command, just close the .top(), pop the stack and set focus to the new .top() ?

Why not keep a (unique) stack of focused windows locally then? After each close command, just close the .top(), pop the stack and set focus to the new .top() ?
pdoane commented 2016-07-07 13:14:36 +00:00 (Migrated from github.com)

Yes that's possible to do and I've suggested it a couple of times in the thread.

What I'd like to establish is how we expect the problem to be solved. My first thought was exactly the same as Omar - just track the last active document, and that worked great until I hit the case for closing the window.

So if the thinking is that maintaining an application side copy of the ImGui window z-order for command processing is the right way to solve the problem, then we're done.

But I think there is a bigger issue with menu bar focus behavior. It was a minor issue discussed in #622 but this case creates user confusion without a workaround applied.

Yes that's possible to do and I've suggested it a couple of times in the thread. What I'd like to establish is how we expect the problem to be solved. My first thought was exactly the same as Omar - just track the last active document, and that worked great until I hit the case for closing the window. So if the thinking is that maintaining an application side copy of the ImGui window z-order for command processing is the right way to solve the problem, then we're done. But I think there is a bigger issue with menu bar focus behavior. It was a minor issue discussed in #622 but this case creates user confusion without a workaround applied.
ocornut commented 2016-07-12 10:04:11 +00:00 (Migrated from github.com)

OK. I see what you are saying. In that sort of case you expect the menu-bar so give back focus to highest remaining window in the z-order, and the case discussed in #622 also would need to be honored. Actually as part of #323 I am already doing something along the line of #622 but it is still wip.

Will get back to this later.

You might try to add ImGuiWindowFlags_NoFocusOnAppearing or ImGuiWindowFlags_NoBringToFrontOnFocus flags in BeginMainMenuBar() and see if it slightly changes the behavior in a way that's useful for you.

Otherwise as johanwendin pointed out you can easily maintain a stack locally. That's not how I'd expect the problem to be solved for end-users but would be a easy to implement workaround in the meanwhile.

OK. I see what you are saying. In that sort of case you expect the menu-bar so give back focus to highest remaining window in the z-order, and the case discussed in #622 also would need to be honored. Actually as part of #323 I am already doing something along the line of #622 but it is still wip. Will get back to this later. You might try to add `ImGuiWindowFlags_NoFocusOnAppearing` or `ImGuiWindowFlags_NoBringToFrontOnFocus` flags in BeginMainMenuBar() and see if it slightly changes the behavior in a way that's useful for you. Otherwise as johanwendin pointed out you can easily maintain a stack locally. That's not how I'd expect the problem to be solved for end-users but would be a easy to implement workaround in the meanwhile.
ocornut commented 2016-07-18 07:29:54 +00:00 (Migrated from github.com)

FYI I have committed a small fix to restore focus when closing a window but this doesn't fix your issue here because the mainmenubar window is still taking focus at the moment.

FYI I have committed a small fix to restore focus when closing a window but this doesn't fix your issue here because the mainmenubar window is still taking focus at the moment.
ocornut commented 2017-11-17 16:38:11 +00:00 (Migrated from github.com)

Forgot to link it in this topic, but as part of #787 work, on October 23 I have pushed a change that makes the Main Menu Bar restore focus after being used:
ocornut/imgui@20983773f1

It doesn't fully solve the discussion here (which can presumably currently be solved by tracking z-order of documents on the user side) but relates somehow.

Forgot to link it in this topic, but as part of #787 work, on October 23 I have pushed a change that makes the Main Menu Bar restore focus after being used: https://github.com/ocornut/imgui/commit/20983773f1f3b71ece6b04890ad2ec7f803668e0 It doesn't fully solve the discussion here (which can presumably currently be solved by tracking z-order of documents on the user side) but relates somehow.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ocornut/imgui#727
No description provided.