Closed dcnieho closed 1 year ago
Hi Dee,
I need some more time to study this
Thanks and no worries. Happy to discuss
Hi Dee,
Initially, DockingParams was created only for dockable windows, since the ImGui API for the initial positioning and docking of windows is a bit awkward. I had not envisioned a use outside of it, and I am still not sure it is that useful.
Anyway, it seems like you were confused by DockableWindow::windowSizeCondition
which you set to ImGuiCond_Always
. This has an effect only when you did set a manual size for the window.
See src/hello_imgui/internal/docking_details.cpp:
void ShowDockableWindows(std::vector<DockableWindow>& dockableWindows)
{
// ...
for (auto& dockableWindow: dockableWindows)
{ //...
if (dockableWindow.windowSize.x > 0.f)
ImGui::SetNextWindowSize(dockableWindow.windowSize, dockableWindow.windowSizeCondition);
You can use ImGuiWindowFlags_AlwaysAutoResize
instead.
Since HelloImGui is a C++ project, I always prefer to use C++ example code. Here is an example in C++ that always auto resizes the window:
#include "hello_imgui/hello_imgui.h"
int main()
{
auto gui1 = []()
{
static char msg[2048] = "Hello";
static ImVec2 size(300.f, 300.f);
ImGui::Text("Gui1");
ImGui::SetNextItemWidth(100.f); ImGui::SliderFloat("size.x", &size.x, 10.f, 600.f);
ImGui::SetNextItemWidth(100.f); ImGui::SliderFloat("size.y", &size.y, 10.f, 600.f);
ImGui::InputTextMultiline("Text", msg, 2048, size);
};
HelloImGui::DockableWindow w1("gui1", "", gui1);
w1.imGuiWindowFlags |= ImGuiWindowFlags_AlwaysAutoResize;
HelloImGui::RunnerParams params;
params.imGuiWindowParams.showMenuBar = true;
params.imGuiWindowParams.enableViewports = true;
params.dockingParams.dockableWindows = {w1};
HelloImGui::Run(params);
}
Now, my next question is: is it worthwile renaming DockableWindow
to Window
and to make it more generic.
I'm not sure. Let's see this on a python sample with two flavours.
from typing import List
from imgui_bundle import imgui, hello_imgui, immapp, ImVec2
class MySatelliteWindow:
text_size = ImVec2
text = "Hello"
def __init__(self):
self.text_size = ImVec2(300, 300)
def gui(self):
imgui.push_id(str(id(self)))
imgui.text(f"Satellite {id(self)}")
imgui.set_next_item_width(100);
_, self.text_size.x = imgui.slider_float("size.x", self.text_size.x, 10, 600)
imgui.set_next_item_width(100);
_, self.text_size.y = imgui.slider_float("size.y", self.text_size.y, 10, 600)
_, self.text = imgui.input_text_multiline("Text", self.text, self.text_size)
imgui.pop_id()
def main():
params = hello_imgui.RunnerParams()
params.imgui_window_params.enable_viewports = True
# Add HelloImGui menu bar, with the View menu
params.imgui_window_params.show_menu_bar = True
satellite_windows: List[MySatelliteWindow] = []
def gui():
nonlocal params
if imgui.button("add satellite window"):
satellite_window = MySatelliteWindow()
satellite_windows.append(satellite_window)
dockable_window = hello_imgui.DockableWindow()
dockable_window.imgui_window_flags = int(
# imgui.WindowFlags_.no_move |
# imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
# imgui.WindowFlags_.no_title_bar |
# imgui.WindowFlags_.no_scrollbar |
# imgui.WindowFlags_.no_scroll_with_mouse |
imgui.WindowFlags_.always_auto_resize
)
dockable_window.label = f"satellite {len(params.docking_params.dockable_windows)}"
dockable_window.gui_function = satellite_window.gui
# (!) append will silently fail, because it is a bound std::vector...
# params.docking_params.dockable_windows.append(dockable_window)
# => Use `=` operator instead
params.docking_params.dockable_windows = params.docking_params.dockable_windows + [dockable_window]
params.callbacks.show_gui = gui
hello_imgui.run(params)
if __name__ == "__main__":
main()
from typing import List
from imgui_bundle import imgui, hello_imgui, immapp, ImVec2
class MySatelliteWindow:
text_size = ImVec2
text = "Hello"
def __init__(self):
self.text_size = ImVec2(300, 300)
def gui(self):
imgui.push_id(str(id(self)))
imgui.text(f"Satellite {id(self)}")
imgui.set_next_item_width(100);
_, self.text_size.x = imgui.slider_float("size.x", self.text_size.x, 10, 600)
imgui.set_next_item_width(100);
_, self.text_size.y = imgui.slider_float("size.y", self.text_size.y, 10, 600)
_, self.text = imgui.input_text_multiline("Text", self.text, self.text_size)
imgui.pop_id()
def main():
params = hello_imgui.RunnerParams()
params.imgui_window_params.enable_viewports = True
satellite_windows: List[MySatelliteWindow] = []
def gui():
nonlocal params
if imgui.button("add satellite window"):
satellite_window = MySatelliteWindow()
satellite_windows.append(satellite_window)
for i, satellite_window in enumerate(satellite_windows):
window_flags = int(
# imgui.WindowFlags_.no_move |
# imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
# imgui.WindowFlags_.no_title_bar |
# imgui.WindowFlags_.no_scrollbar |
# imgui.WindowFlags_.no_scroll_with_mouse |
imgui.WindowFlags_.always_auto_resize
)
imgui.begin(f"satellite {i}", None, window_flags)
satellite_window.gui()
imgui.end()
params.callbacks.show_gui = gui
hello_imgui.run(params)
if __name__ == "__main__":
main()
Oh, thats nice! Indeed there is then no need to abuse dockable windows. I tried this before but it didn't work (I don't remember how exactly, just remember things going haywire...). There is only one small difference. I use OS decoration for my satellite windows, and these cannot be closed when using normal windows (mode 2 in the code below), but they can when abusing docking (mode 1):
from typing import List
from imgui_bundle import imgui, immapp, hello_imgui, immapp, ImVec2
class MySatelliteWindow:
text_size = ImVec2
text = "Hello"
def __init__(self):
self.text_size = ImVec2(300, 300)
def gui(self):
imgui.push_id(str(id(self)))
imgui.text(f"Satellite {id(self)}")
imgui.set_next_item_width(100);
_, self.text_size.x = imgui.slider_float("size.x", self.text_size.x, 10, 600)
imgui.set_next_item_width(100);
_, self.text_size.y = imgui.slider_float("size.y", self.text_size.y, 10, 600)
_, self.text = imgui.input_text_multiline("Text", self.text, self.text_size)
imgui.pop_id()
def main():
params = hello_imgui.RunnerParams()
params.imgui_window_params.config_windows_move_from_title_bar_only = True
params.imgui_window_params.enable_viewports = True
satellite_windows: List[MySatelliteWindow] = []
mode = 2
def gui():
nonlocal params
if imgui.button("add satellite window"):
satellite_window = MySatelliteWindow()
satellite_windows.append(satellite_window)
if mode==1:
dockable_window = hello_imgui.DockableWindow()
dockable_window.imgui_window_flags = int(
imgui.WindowFlags_.no_title_bar |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.no_scrollbar |
imgui.WindowFlags_.no_scroll_with_mouse |
imgui.WindowFlags_.always_auto_resize
)
dockable_window.label = f"satellite {len(params.docking_params.dockable_windows)}"
dockable_window.gui_function = satellite_window.gui
# (!) append will silently fail, because it is a bound std::vector...
# params.docking_params.dockable_windows.append(dockable_window)
# => Use `=` operator instead
params.docking_params.dockable_windows = params.docking_params.dockable_windows + [dockable_window]
if mode==2:
for i, satellite_window in enumerate(satellite_windows):
window_flags = int(
imgui.WindowFlags_.no_title_bar |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.no_scrollbar |
imgui.WindowFlags_.no_scroll_with_mouse |
imgui.WindowFlags_.always_auto_resize
)
imgui.begin(f"satellite {i}", None, window_flags)
satellite_window.gui()
imgui.end()
def post_init():
imgui.get_io().config_viewports_no_decoration = False
imgui.get_io().config_viewports_no_auto_merge = True
params.callbacks.show_gui = gui
params.callbacks.post_init = post_init
immapp.run(params)
if __name__ == "__main__":
main()
Autosizing indeed works well, but your positioning logic cannot be invoked on these satellite windows. That would be nice, but maybe not worth the effort if not easily done.
On mode 2, you need to capture the return from imgui.begin, like so:
from typing import List
from imgui_bundle import imgui, immapp, hello_imgui, immapp, ImVec2
class MySatelliteWindow:
text_size = ImVec2
text = "Hello"
def __init__(self):
self.text_size = ImVec2(300, 300)
def gui(self):
imgui.push_id(str(id(self)))
imgui.text(f"Satellite {id(self)}")
imgui.set_next_item_width(100);
_, self.text_size.x = imgui.slider_float("size.x", self.text_size.x, 10, 600)
imgui.set_next_item_width(100);
_, self.text_size.y = imgui.slider_float("size.y", self.text_size.y, 10, 600)
_, self.text = imgui.input_text_multiline("Text", self.text, self.text_size)
imgui.pop_id()
def main():
params = hello_imgui.RunnerParams()
params.imgui_window_params.config_windows_move_from_title_bar_only = True
params.imgui_window_params.enable_viewports = True
satellite_windows: List[MySatelliteWindow] = []
satellite_windows_visible: List[bool] = []
mode = 2
def gui():
nonlocal params
if imgui.button("add satellite window"):
satellite_window = MySatelliteWindow()
satellite_windows.append(satellite_window)
satellite_windows_visible.append(True)
if mode==1:
dockable_window = hello_imgui.DockableWindow()
dockable_window.imgui_window_flags = int(
imgui.WindowFlags_.no_title_bar |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.no_scrollbar |
imgui.WindowFlags_.no_scroll_with_mouse |
imgui.WindowFlags_.always_auto_resize
)
dockable_window.label = f"satellite {len(params.docking_params.dockable_windows)}"
dockable_window.gui_function = satellite_window.gui
# (!) append will silently fail, because it is a bound std::vector...
# params.docking_params.dockable_windows.append(dockable_window)
# => Use `=` operator instead
params.docking_params.dockable_windows = params.docking_params.dockable_windows + [dockable_window]
if mode==2:
for i, satellite_window in enumerate(satellite_windows):
if satellite_windows_visible[i]:
window_flags = int(
imgui.WindowFlags_.no_title_bar |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.no_scrollbar |
imgui.WindowFlags_.no_scroll_with_mouse |
imgui.WindowFlags_.always_auto_resize
)
opened, satellite_windows_visible[i] = imgui.begin(f"satellite {i}", satellite_windows_visible[i], window_flags)
if opened:
satellite_window.gui()
imgui.end()
def post_init():
imgui.get_io().config_viewports_no_decoration = False
imgui.get_io().config_viewports_no_auto_merge = True
params.callbacks.show_gui = gui
params.callbacks.post_init = post_init
immapp.run(params)
if __name__ == "__main__":
main()
Ah, of course. Thanks, super!
I like the convenience
hello_imgui
provides as well as its extra features (autosize, etc).I needed more than one window in my program, so i tried to find out how to do that with hello_imgui. Turns out i can (kinda ab-)use the docking support for that. See lines 140-164 in the diff of
_image_gui.py
here https://github.com/dcnieho/glassesValidator/commit/698223344658e6402772ef01aa6723e55f6b5e07. That gives me nice windows with native decorations for additional windows. At least on Windows, didn't test further yet.I did however lose a lot of the hello_imgui features in the process for the additional windows: e.g. autosizing and such only works for the main window. And the fact that just about all in the docking support can be used without docking, makes it seem like docking is a bit of a misnomer. It suggests a (possibly significant) API change. I haven't fully thought this through:
resize_app_window_at_next_frame
and all the window geometry stuff on a specific window, the main window is not special.params.docking_params.dockable_windows
would become something like justparams.windows