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

How to programmatically dock windows? #729

Open
nikitablack opened this issue Jun 6, 2023 · 4 comments
Open

How to programmatically dock windows? #729

nikitablack opened this issue Jun 6, 2023 · 4 comments

Comments

@nikitablack
Copy link

Hello. I am using imgui_rs version 0.11.0 with the docking feature enabled. But the only safe dock-space-related function is dockspace_over_main_viewport in dock_space.rs module. If I call it I can indeed manually dock my windows, but how to do it programmatically? I.e. I want my application starts in some default layout with the bottom, left and central panel. How to achieve that?

@KiraCoding
Copy link

I need this too, I think the DockBuilder doesn't have safe bindings yet, unless I'm wrong.

@stfnp
Copy link

stfnp commented Sep 7, 2023

I had the same question, so I did some searching. Before imgui-rs added the docking feature, there was a fork called imgui-docking-rs that has since been discontinued. But this fork seemed to have some safe abstraction for building docked layouts, shown in the hello-docking example.

So I extracted that code and tried making the hello-docking example work with the latest version of imgui-rs. I ended up with the following:

File dock.rs:

use imgui::sys;
use imgui::Direction;

#[derive(Clone, Copy, PartialEq)]
pub struct DockNode {
    id: sys::ImGuiID,
}

impl DockNode {
    pub fn new(id: sys::ImGuiID) -> Self {
        Self { id }
    }

    pub fn size(self, size: [f32; 2]) -> Self {
        unsafe { sys::igDockBuilderSetNodeSize(self.id, sys::ImVec2::from(size)); }

        self
    }

    pub fn position(self, position: [f32; 2]) -> Self {
        unsafe { sys::igDockBuilderSetNodePos(self.id, sys::ImVec2::from(position)); }

        self
    }

    pub fn dock_window(self, window_name: &str) -> Self {
        let window_name = imgui::ImString::from(window_name.to_string());
        unsafe { sys::igDockBuilderDockWindow(window_name.as_ptr(), self.id); }

        self
    }

    pub fn split<D: FnOnce(DockNode), O: FnOnce(DockNode)>(
        self,
        split_dir: Direction,
        size_ratio: f32,
        dir: D,
        opposite_dir: O,
    ) {
        let mut out_id_at_dir: sys::ImGuiID = 0;
        let mut out_id_at_opposite_dir: sys::ImGuiID = 0;

        unsafe {
            sys::igDockBuilderSplitNode(
                self.id,
                split_dir as i32,
                size_ratio,
                &mut out_id_at_dir,
                &mut out_id_at_opposite_dir,
            );
        }

        dir(DockNode::new(out_id_at_dir));
        opposite_dir(DockNode::new(out_id_at_opposite_dir));
    }
}

pub struct Dock {}

impl Dock {
    pub fn new() -> Self {
        Self {}
    }

    pub fn build<F: FnOnce(DockNode)>(self, f: F) {
        let dock_id = unsafe { sys::igDockBuilderAddNode(0, sys::ImGuiDockNodeFlags_None as i32) };

        f(DockNode::new(dock_id));

        unsafe { sys::igDockBuilderFinish(dock_id) }
    }
}

File main.rs:

use imgui::*;

mod support;
mod dock;

use dock::Dock;

fn main() {
    let mut system = support::init(file!());
    system.imgui.io_mut().config_flags |= ConfigFlags::DOCKING_ENABLE;

    let mut first_time = true;
    system.main_loop(move |run, ui| {
        ui.window("Hello").build(|| {
            ui.text("Hello");
        });

        ui.window("Docking").build(|| {
            ui.text("Docking");
        });

        ui.window("Awesome").build(|| {
            ui.text("Awesome");
        });

        if first_time {
            first_time = false;
            Dock::new().build(|root| {
                root.size([512_f32, 512_f32])
                    .position([0_f32, 0_f32])
                    .split(
                        Direction::Left,
                        0.7_f32,
                        |left| {
                            left.dock_window("Hello");
                        },
                        |right| {
                            right.split(Direction::Up, 0.5_f32,
                                |top| {
                                    top.dock_window("Docking");
                                },
                                |bottom| {
                                    bottom.dock_window("Awesome");
                                }
                            );
                        },
                    )
            });
        }
    });
}

This works and looks like the screenshot below, but I still don't know how to modify this example so that the docking area fills the entire window.

image

@dbr
Copy link
Contributor

dbr commented Sep 15, 2023

There's a slightly updated version of that code in
#683 (comment)
which demonstrates having the dock area over the entire window (essentially just make a window that occupies the entire host-window). There is also the dockspace_over_viewport method which does very similar thing (although for some reason I stuck with the window approach, can't remember now if there was a specific reason or it just works well enough)

It would probably be worth adding that to the imgui-examples dir at least, so it's easier to find. Actually adding those methods into the imgui-rs bindings would be better, but that requires a lot more more care and effort (the current bindings work okay, but are quite easy to misuse and cause crashes - it probably needs reworked almost entirely)

@KiraCoding
Copy link

It would be neat to have this in the lib under the docking feature instead of having to implement it manually every time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants