hoffstadt / DearPyGui

Dear PyGui: A fast and powerful Graphical User Interface Toolkit for Python with minimal dependencies
https://dearpygui.readthedocs.io/en/latest/
MIT License
13.31k stars 691 forks source link

mvContainers: Tree nodes and collapsing headers maintain open state after minimising window #1873 #2250

Closed my1e5 closed 10 months ago

my1e5 commented 10 months ago

name: Pull Request about: Create a pull request to help us improve title: mvContainers: Tree nodes and collapsing headers maintain open/close state after minimising window #1873 assignees: @hoffstadt


Closes #1873

Description: When an open tree node or collapsing header was inside a child window, if you minimised the viewport, upon re-opening the viewport the tree node/collapsing header would be closed.

This PR adds the ImGuiCond_Appearing argument to ImGui::SetNextItemOpen.

ImGui::SetNextItemOpen sets the tree node/collapsing header open state. And ImGuiCond_Appearing condition enum has the description: "Set the variable if the object/window is appearing after being hidden/inactive (or the first time)".

This fix was proposed by user sedenka - see https://github.com/hoffstadt/DearPyGui/issues/1873#issuecomment-1232173894

Concerning Areas: I'm not 100% sure if this is the correct fix. But my testing hasn't revealed any issues and it does completely fix the bug.

v-ein commented 10 months ago

This fix breaks the ability to open or close tree nodes/collapsing headers with dpg.set_value.

import dearpygui.dearpygui as dpg
dpg.create_context()

with dpg.window(width=300, height=300) as wnd:
    def set_state(sender, checked: bool):
        for i in range(500, 504):
            dpg.set_value(i, checked)

    dpg.add_checkbox(label="Open", callback=set_state)

    with dpg.tree_node(label="A", tag=500):
        dpg.add_text("Hello World")
    with dpg.collapsing_header(label="B",tag=501):
        dpg.add_text("Hello World")
    with dpg.child_window():
        with dpg.tree_node(label="C", tag=502):
            dpg.add_text("Hello World")
        with dpg.collapsing_header(label="D",tag=503):
            dpg.add_text("Hello World")

dpg.create_viewport(width=500, height=500)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

Without the fix, clicking on "Open" would close and open the widgets depending on checkbox state. With the fix, it doesn't work anymore.

v-ein commented 10 months ago

There are only two cases that ImGui::TreeNodeBehaviorIsOpen handle: ImGuiCond_Always and anything else. The default behavior (when no flags are specified) is ImGuiCond_Always. Passing ImGuiCond_Appearing leads us to the other branch where TreeNodeBehaviorIsOpen completely ignores what was set with SetNextItemOpen, and uses an internal value instead:

if (g.NextItemData.OpenCond & ImGuiCond_Always)
//...
else
{
    // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
    const int stored_value = storage->GetInt(id, -1);
    if (stored_value == -1)
        //...
    else
    {
        is_open = stored_value != 0;
    }
}

(we get into the else-else branch).

v-ein commented 10 months ago

That is, ImGuiCond_Appearing is a poor choice here.

v-ein commented 10 months ago

So basically to fix it properly, it seems we need to account for window->SkipItems.

TreeNodeBehavior and all other ImGui TreeNode functions return the "open" state except when SkipItems is set, in which case they try to optimize by pretending the widget is closed. We just don't need to store that "false" value in config.value.