ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
60.64k stars 10.24k forks source link

How to disable certain sides of a window from being resized? #6545

Closed pegvin closed 1 year ago

pegvin commented 1 year ago

Version/Branch of Dear ImGui:

Version: v1.89.5 Branch: master

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp Compiler: gcc & g++ Operating System: Arch Linux

My Issue/Question:

in the below attached video i have a "sidebar" which is just a regular window clamped on the right side of the "master/native window", as you can see i am able to resize the window from all 4 of it's borders but i only want to allow the window to be resizable from the left side border of the window.

is there a way to achieve such behavior? or i'll just have to make the window a bit bigger so that it overflows thus the overflowed side of the window can't be resized...

Screenshots/Video

https://github.com/ocornut/imgui/assets/75035219/b480a671-e872-4458-8c8f-218de0f84534


thanks alot ocornut and contributors for such a beautiful library!

ocornut commented 1 year ago

Hello,

You should be using the docking branch for that purpose. See https://github.com/ocornut/imgui/wiki/Docking

Often people just call `ImGui::DockspaceOverViewport(GetMainViewport()); at the beginning of their frame to allow docking on the side.

pegvin commented 1 year ago

i actually did try docking once to make layouts and stuff for my pixel-art editor but i failed miserably, is there a way to fix it without using docking or will i have to actually use docking branch?

i mean no offense but but i am not a smart guy like you all and it gets very complicated for me, but if it's the only way i will have to try it then i guess...

ocornut commented 1 year ago

I'm afraid you would have to with docking this is the easiest path.

The simple way is just to call that DockspaceOverViewport() function and let the user dock the window however they want. Initial state (first time) will be a little messy but there's no other code to deal with. Probably 2 lines of change in your codebase (set ConfigFlags + DockspaceOverViewport(NULL) call).

If you want a default state that's a little tricky as the API are in imgui_internal.h and not great.

pegvin commented 1 year ago

well i want the windows to be pre-docked, so i guess i will dig more deep into the API, probably look into example code i can find and if i am able to do so i will attach an example.

but as for now i am closing the issue.

once again, thanks alot ocornut & contributors for such a beautiful library! I LOVE YOU ALL!!!

GamingMinds-DanielC commented 1 year ago

I made a generic extension for pre-docked windows a while back, sounds like this is exactly what you need. As general advice, try to collect extensions that are generic enough in a small library that is independent of your actual user interface. Here is a version of mine, reduced to the things you currently need...

First an example usage for your case:

    ImGuiID dockRootId = ImGui::DockSpaceOverViewport( nullptr, ImGuiDockNodeFlags_NoDockingInCentralNode | ImGuiDockNodeFlags_PassthruCentralNode );

    // ...

    // initial size, reference size for pre-docked version
    ImGui::SetNextWindowSize( ImVec2( 240, 480 ), ImGuiCond_FirstUseEver );

    if ( !ImGuiEx::WindowExistsOrHasPersistentData( "Sidebar" ) )
        ImGui::SetNextWindowDockID( ImGuiEx::CreateDockNodeByCentralNode( dockRootId, ImGuiDir_Right, ImGuiDir_None ), ImGuiCond_FirstUseEver );

    // now draw the window
    if ( ImGui::Begin( "Sidebar", nullptr, ImGuiWindowFlags_None ) )
    {
        // ...
    }
    ImGui::End();

ImGuiEx.h:

/*
    This header contains extensions (f.e. useful shortcuts) for imgui that do not depend
    on anything in our engine and are also platform agnostic, therefore the ImGuiEx namespace
    is on the same level as the ImGui namespace.

    If you need access to internals of the library (anything from imgui_internal.h), please
    wrap those accesses here instead of distributing them throughout the rest of the sources.
*/

#pragma once

#include <imgui.h>

namespace ImGuiEx
{
    // use to determine if expensive preparations for a first use of a window are needed
    bool WindowExistsOrHasPersistentData( const char* name );

    // use to create a dock node next to the central node
    // please use this only if WindowExistsOrHasPersistentData() returns false
    // call SetNextWindowSize() before calling this to create the split in the correct size
    // give the returned ID (returns 0 if there is no central node) to SetNextWindowDockID()
    ImGuiID CreateDockNodeByCentralNode( ImGuiID rootId, ImGuiDir mainDir, ImGuiDir subDir );
} // namespace ImGuiEx

ImGuiEx.cpp:

#include "ImGuiEx.h"
#include <imgui_internal.h>

namespace ImGuiEx
{
    bool WindowExistsOrHasPersistentData( const char* name )
    {
        // see ImGui::Begin() internals for reference on implementation
        ImGuiWindow* window = ImGui::FindWindowByName( name );
        if ( window != nullptr )
            return true;

        ImGuiID windowId = ImHashStr( name );

        ImGuiWindowSettings* settings = ImGui::FindWindowSettings( windowId );
        if ( settings != nullptr )
            return true;

        return false;
    }

    ImGuiID CreateDockNodeByCentralNode( ImGuiID rootId, ImGuiDir mainDir, ImGuiDir subDir )
    {
        ImGuiDockNode* root    = ImGui::DockBuilderGetNode( rootId );
        ImGuiDockNode* central = root->CentralNode;

        // early out if there is no central node
        if ( central == nullptr )
            return (ImGuiID)0;

        ImGuiDockNode* node = central;
        ImGuiDir       dir  = mainDir;
        ImGuiAxis      axis = ( dir == ImGuiDir_Left ) || ( dir == ImGuiDir_Right ) ? ImGuiAxis_X : ImGuiAxis_Y;

        // central is already the node to split if there is no sub direction, otherwise search first
        if ( subDir != ImGuiDir_None )
        {
            ImGuiDockNode* sub = node;

            int dir0 = ( dir == ImGuiDir_Left ) || ( dir == ImGuiDir_Up ) ? 0 : 1;
            int dir1 = 1 - dir0;

            // go up while sub is the furthest in the wanted direction
            for ( ;; )
            {
                ImGuiDockNode* parent = sub->ParentNode;
                if ( ( parent == nullptr ) || ( parent->SplitAxis != axis ) )
                {
                    sub = nullptr;
                    break;
                }

                if ( parent->ChildNodes[ dir1 ] == sub )
                {
                    sub = parent->ChildNodes[ dir0 ];
                    break;
                }

                sub = parent;
            }

            // if there is no node left of central, keep central as the node to split
            if ( sub != nullptr )
            {
                // go down in the other direction to find the neighboring node
                while ( sub->IsSplitNode() && ( sub->SplitAxis == axis ) )
                    sub = sub->ChildNodes[ dir1 ];

                // a node in the main direction has been found, switch to sub direction
                dir  = subDir;
                axis = ( dir == ImGuiDir_Left ) || ( dir == ImGuiDir_Right ) ? ImGuiAxis_X : ImGuiAxis_Y;
                dir0 = ( dir == ImGuiDir_Left ) || ( dir == ImGuiDir_Up ) ? 0 : 1;

                // go to the furthest node in the wanted sub direction
                while ( sub->IsSplitNode() && ( sub->SplitAxis == axis ) )
                    sub = sub->ChildNodes[ dir0 ];

                node = sub;
            }
        }

        // calculate split ratio based on the size of the next window
        float split_ratio = 0.5f;

        ImGuiContext* ctx = ImGui::GetCurrentContext();
        if ( ( ctx->NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize ) != 0 )
        {
            constexpr float DOCKING_SPLITTER_SIZE_ = 2.0f; // from imgui.cpp, replicated here because it is static there
            constexpr float MAX_SPLIT_RATIO        = 0.75f;

            float available = 0.0f;
            float wanted    = 0.0f;

            if ( axis == ImGuiAxis_X )
            {
                available = node->Size.x - DOCKING_SPLITTER_SIZE_;
                wanted    = ctx->NextWindowData.SizeVal.x;
            }
            else
            {
                available = node->Size.y - DOCKING_SPLITTER_SIZE_;
                wanted    = ctx->NextWindowData.SizeVal.y;
            }

            split_ratio = wanted / available;
            if ( split_ratio > MAX_SPLIT_RATIO )
                split_ratio = MAX_SPLIT_RATIO;
        }

        return ImGui::DockBuilderSplitNode( node->ID, dir, split_ratio, nullptr, nullptr );
    }
} // namespace ImGuiEx
pegvin commented 1 year ago

Thanks for sharing the code snippet! i saw it quite some time back but i just didn't get the time to reply.

means alot to me!