ocornut / imgui

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

Multiple GLFW window for my datashow application #7312

Closed paulocoutinhox closed 7 months ago

paulocoutinhox commented 7 months ago

Version/Branch of Dear ImGui:

Version 1.90.2

Back-ends:

imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp

Compiler, OS:

macOS

Full config/build information:

any

Details:

How to use multiple GLFW window for my datashow application?

I have an app today https://github.com/paulocoutinhox/prprojector that i want port to ImGUI.

My problem now is understand how to render the video in a second Window that will appear on a second monitor.

My code is below and the project is hosted here: https://github.com/paulocoutinhox/imgui-image-grid-demo

The project is working with everything in the same window.

The "videoWindow" is commented and need be uncommented.

Screenshots/Video:

image

Minimal, Complete and Verifiable Example code:

#include "imgui.h"
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
#include <chrono>
#include <filesystem>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#include <opencv2/opencv.hpp>

namespace fs = std::filesystem;

struct ImageTexture {
    GLuint textureID;
    int width, height;
};

// Function to load texture from image
ImageTexture LoadTextureFromImage(const char* imagePath) {
    ImageTexture imgTexture = {0, 0, 0};
    int width, height, channels;
    unsigned char* data = stbi_load(imagePath, &width, &height, &channels, 0);
    if (data == nullptr) {
        std::cerr << "Error loading image: " << imagePath << std::endl;
        return imgTexture;
    }

    glGenTextures(1, &imgTexture.textureID);
    glBindTexture(GL_TEXTURE_2D, imgTexture.textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, channels == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    stbi_image_free(data);

    imgTexture.width = width;
    imgTexture.height = height;

    return imgTexture;
}

void windowCloseCallback(GLFWwindow* window) {
    glfwDestroyWindow(window);
}

void windowKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    auto altF4 = (key == GLFW_KEY_W && mods == GLFW_MOD_SUPER);
    auto commandQ = (key == GLFW_KEY_F4 && mods == GLFW_MOD_ALT && action == GLFW_PRESS);

    if (altF4 || commandQ) {
        glfwDestroyWindow(window);
    }
}

int main() {
    if (!glfwInit()) {
        std::cerr << "Error initializing GLFW." << std::endl;
        return -1;
    }

    // GLFW window creation
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    GLFWwindow* window = glfwCreateWindow(1024, 768, "Image Grid with ImGui", nullptr, nullptr);
    if (window == nullptr) {
        std::cerr << "Error creating GLFW window." << std::endl;
        glfwTerminate();
        return -1;
    }

    GLFWwindow* videoWindow = nullptr;

    /*
    GLFWwindow* videoWindow = glfwCreateWindow(1024, 768, "Video Player", nullptr, nullptr);
    if (videoWindow == nullptr) {
        std::cerr << "Error creating GLFW video window." << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwSetWindowCloseCallback(videoWindow, windowCloseCallback);
    glfwSetKeyCallback(videoWindow, windowKeyCallback);
*/

    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);

    // ImGui initialization
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    ImGui::StyleColorsDark();
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init("#version 410");

    // Load images from directory
    std::vector<ImageTexture> textures;
    std::string pathToImages = "images";

    for (const auto& entry : fs::directory_iterator(pathToImages)) {
        if (entry.is_regular_file()) {
            textures.push_back(LoadTextureFromImage(entry.path().string().c_str()));
        }
    }

    // Open video file
    cv::VideoCapture video("videos/video1.mp4");
    if (!video.isOpened()) {
        std::cerr << "Error opening video." << std::endl;
        return -1;
    }

    cv::Mat frame;
    GLuint videoTexture = 0;

    int videoWidth = 0;
    int videoHeight = 0;

    double fps = video.get(cv::CAP_PROP_FPS);
    auto frameDuration = std::chrono::milliseconds(static_cast<int>(1000 / fps));
    std::chrono::steady_clock::time_point lastFrameTime = std::chrono::steady_clock::now();

    while (!glfwWindowShouldClose(window)) {
        auto frameStartTime = std::chrono::high_resolution_clock::now(); // for video frame

        glfwMakeContextCurrent(window);
        glfwPollEvents();

        auto currentTime = std::chrono::steady_clock::now();
        if (std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastFrameTime) >= frameDuration) {
            if (video.read(frame)) {
                // Convert BGR to RGBA
                cv::Mat frameRGBA;
                cv::cvtColor(frame, frameRGBA, cv::COLOR_BGR2RGBA);

                // Update video texture
                if (videoTexture == 0) {
                    glGenTextures(1, &videoTexture);
                    glBindTexture(GL_TEXTURE_2D, videoTexture);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                }
                glBindTexture(GL_TEXTURE_2D, videoTexture);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frameRGBA.cols, frameRGBA.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, frameRGBA.data);

                if (videoWidth == 0 || videoHeight == 0) {
                    videoWidth = frame.cols;
                    videoHeight = frame.rows;
                }

                lastFrameTime = currentTime;
            } else {
                // Restart video playback when reaching the end
                video.set(cv::CAP_PROP_POS_FRAMES, 0);
            }
        }

        // ImGui rendering
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        // Render image grid
        ImGui::Begin("Images");
        float windowWidth = ImGui::GetContentRegionAvail().x;
        int imagesPerRow = std::max(1, static_cast<int>(windowWidth / 100.0f));
        float imageSize = windowWidth / imagesPerRow;

        for (int i = 0; i < textures.size(); ++i) {
            float aspectRatio = static_cast<float>(textures[i].width) / textures[i].height;
            ImGui::Image((void*)(intptr_t)textures[i].textureID, ImVec2(imageSize, imageSize / aspectRatio));
            if ((i + 1) % imagesPerRow != 0) ImGui::SameLine();
        }
        ImGui::End();

        // Render video        
        if (videoWindow) {
            glfwMakeContextCurrent(videoWindow);
            glfwPollEvents();
        }

        if (videoTexture != 0) {
            ImGui::Begin("Video Player", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);

            // Get total window dimensions from ImGui
            ImVec2 windowSize = ImGui::GetContentRegionAvail();

            // Calculate video aspect ratio
            float videoAspectRatio = (float)videoWidth / (float)videoHeight;

            // Calculate image size based on video and window aspect ratio
            ImVec2 imageSize;
            if (windowSize.x / windowSize.y > videoAspectRatio) {
                // Window is wider than video
                imageSize.x = windowSize.y * videoAspectRatio;
                imageSize.y = windowSize.y;
            } else {
                // Window is taller than video
                imageSize.x = windowSize.x;
                imageSize.y = windowSize.x / videoAspectRatio;
            }

            // Calculate position to center the image in the window
            ImVec2 imagePos = ImVec2(
                ImGui::GetCursorPos().x + (windowSize.x - imageSize.x) * 0.5f, // X position for centering
                ImGui::GetCursorPos().y + (windowSize.y - imageSize.y) * 0.5f  // Y position for centering
                );

            // Apply calculated position
            ImGui::SetCursorPos(imagePos);

            // Render video image with adjusted size and position
            ImGui::Image((void*)(intptr_t)videoTexture, imageSize);

            ImGui::End();
        }

        // Render ImGui
        ImGui::Render();
        int displayW, displayH;
        glfwGetFramebufferSize(window, &displayW, &displayH);
        glViewport(0, 0, displayW, displayH);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(window);
    }

    // Cleanup
    if (videoTexture != 0) {
        glDeleteTextures(1, &videoTexture);
    }

    for (auto& texture : textures) {
        glDeleteTextures(1, &texture.textureID);
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    if (videoWindow) {
        glfwDestroyWindow(videoWindow);
    }

    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}
paulocoutinhox commented 7 months ago

I tried made the code work from samples, changing context, but it simply freeze:

#include "imgui.h"
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
#include <chrono>
#include <filesystem>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#include <opencv2/opencv.hpp>

namespace fs = std::filesystem;

struct ImageTexture {
    GLuint textureID;
    int width, height;
};

// Function to load texture from image
ImageTexture LoadTextureFromImage(const char* imagePath) {
    ImageTexture imgTexture = {0, 0, 0};
    int width, height, channels;
    unsigned char* data = stbi_load(imagePath, &width, &height, &channels, 0);
    if (data == nullptr) {
        std::cerr << "Error loading image: " << imagePath << std::endl;
        return imgTexture;
    }

    glGenTextures(1, &imgTexture.textureID);
    glBindTexture(GL_TEXTURE_2D, imgTexture.textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, channels == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    stbi_image_free(data);

    imgTexture.width = width;
    imgTexture.height = height;

    return imgTexture;
}

void windowCloseCallback(GLFWwindow* window) {
    glfwDestroyWindow(window);
}

void windowKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    auto altF4 = (key == GLFW_KEY_W && mods == GLFW_MOD_SUPER);
    auto commandQ = (key == GLFW_KEY_F4 && mods == GLFW_MOD_ALT && action == GLFW_PRESS);

    if (altF4 || commandQ) {
        glfwDestroyWindow(window);
    }
}

int main() {
    if (!glfwInit()) {
        std::cerr << "Error initializing GLFW." << std::endl;
        return -1;
    }

    // GLFW window creation
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // Main window
    GLFWwindow* window = glfwCreateWindow(1024, 768, "Image Grid with ImGui", nullptr, nullptr);
    if (window == nullptr) {
        std::cerr << "Error creating GLFW window." << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSwapInterval(1); // Enable vsync

    // Video window
    GLFWwindow* videoWindow = glfwCreateWindow(1024, 768, "Video Player", nullptr, nullptr);
    if (videoWindow == nullptr) {
        std::cerr << "Error creating GLFW video window." << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwSetWindowCloseCallback(videoWindow, windowCloseCallback);
    glfwSetKeyCallback(videoWindow, windowKeyCallback);

    // ImGui initialization
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    ImGui::StyleColorsDark();
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init();

    // Load images from directory
    std::vector<ImageTexture> textures;
    std::string pathToImages = "images";

    for (const auto& entry : fs::directory_iterator(pathToImages)) {
        if (entry.is_regular_file()) {
            textures.push_back(LoadTextureFromImage(entry.path().string().c_str()));
        }
    }

    // Open video file
    cv::VideoCapture video("videos/video1.mp4");
    if (!video.isOpened()) {
        std::cerr << "Error opening video." << std::endl;
        return -1;
    }

    cv::Mat frame;
    GLuint videoTexture = 0;

    int videoWidth = 0;
    int videoHeight = 0;

    double fps = video.get(cv::CAP_PROP_FPS);
    auto frameDuration = std::chrono::milliseconds(static_cast<int>(1000 / fps));
    std::chrono::steady_clock::time_point lastFrameTime = std::chrono::steady_clock::now();

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();

        // Get video frame
        auto currentTime = std::chrono::steady_clock::now();
        if (std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastFrameTime) >= frameDuration) {
            if (video.read(frame)) {
                // Convert BGR to RGBA
                cv::Mat frameRGBA;
                cv::cvtColor(frame, frameRGBA, cv::COLOR_BGR2RGBA);

                // Update video texture
                if (videoTexture == 0) {
                    glGenTextures(1, &videoTexture);
                    glBindTexture(GL_TEXTURE_2D, videoTexture);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                }

                glBindTexture(GL_TEXTURE_2D, videoTexture);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frameRGBA.cols, frameRGBA.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, frameRGBA.data);

                if (videoWidth == 0 || videoHeight == 0) {
                    videoWidth = frame.cols;
                    videoHeight = frame.rows;
                }

                lastFrameTime = currentTime;
            } else {
                // Restart video playback when reaching the end
                video.set(cv::CAP_PROP_POS_FRAMES, 0);
            }
        }

        // Main window
        glfwMakeContextCurrent(window);
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        // Render image grid
        ImGui::Begin("Images");

        float windowWidth = ImGui::GetContentRegionAvail().x;
        int imagesPerRow = std::max(1, static_cast<int>(windowWidth / 100.0f));
        float imageSize = windowWidth / imagesPerRow;

        for (int i = 0; i < textures.size(); ++i) {
            float aspectRatio = static_cast<float>(textures[i].width) / textures[i].height;
            ImGui::Image((void*)(intptr_t)textures[i].textureID, ImVec2(imageSize, imageSize / aspectRatio));
            if ((i + 1) % imagesPerRow != 0) ImGui::SameLine();
        }

        ImGui::End();
        ImGui::Render();

        int display_w, display_h;
        glfwGetFramebufferSize(window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(window);

        // Render video

        if (videoWindow) {
            glfwMakeContextCurrent(videoWindow);
            ImGui_ImplOpenGL3_NewFrame();
            ImGui_ImplGlfw_NewFrame();
            ImGui::NewFrame();
        }

        if (videoTexture != 0) {
            ImGui::Begin("Video Player", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);

            // Get total window dimensions from ImGui
            ImVec2 windowSize = ImGui::GetContentRegionAvail();

            // Calculate video aspect ratio
            float videoAspectRatio = (float)videoWidth / (float)videoHeight;

            // Calculate image size based on video and window aspect ratio
            ImVec2 imageSize;
            if (windowSize.x / windowSize.y > videoAspectRatio) {
                // Window is wider than video
                imageSize.x = windowSize.y * videoAspectRatio;
                imageSize.y = windowSize.y;
            } else {
                // Window is taller than video
                imageSize.x = windowSize.x;
                imageSize.y = windowSize.x / videoAspectRatio;
            }

            // Calculate position to center the image in the window
            ImVec2 imagePos = ImVec2(
                ImGui::GetCursorPos().x + (windowSize.x - imageSize.x) * 0.5f, // X position for centering
                ImGui::GetCursorPos().y + (windowSize.y - imageSize.y) * 0.5f  // Y position for centering
                );

            // Apply calculated position
            ImGui::SetCursorPos(imagePos);

            // Render video image with adjusted size and position
            ImGui::Image((void*)(intptr_t)videoTexture, imageSize);

            ImGui::End();
        }

        // Render ImGui
        ImGui::Render();
        int displayW, displayH;
        glfwGetFramebufferSize(videoWindow, &displayW, &displayH);
        glViewport(0, 0, displayW, displayH);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(videoWindow);
    }

    // Cleanup
    if (videoTexture != 0) {
        glDeleteTextures(1, &videoTexture);
    }

    for (auto& texture : textures) {
        glDeleteTextures(1, &texture.textureID);
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    if (videoWindow) {
        glfwDestroyWindow(videoWindow);
    }

    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}
image
ocornut commented 7 months ago

It'll be simpler if you switch to the docking branch and enable multi-viewports, then you can move imgui windows anywhere.

ocornut commented 7 months ago

I am not sure what your "freeze" is because, since you are not providing more information, but AFAIK the stock GLFW backend doesn't currently support multi-context because of how glfwPollEvents() dispatch information you don't have a chance to change context. This is behind discussed in #7186 #7155 #5671

Switching to use multi-viewports should fix the issue for you.

paulocoutinhox commented 7 months ago

Hi,

But in my case when the user open the app, it will the main window, and when it select an image, video or text a new window will open in the datashow (second monitor) with a video in BG and text on Foreground.

We can't drag/drop it manually to other monitor.

ocornut commented 7 months ago

You can use imgui API to position windows wherever you want.

paulocoutinhox commented 7 months ago

Another problem is with font on it. When i add font TTF, it apply for everything, but i want only in my function TextCenteredWithOutline:

image
#include "imgui.h"
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
#include <chrono>
#include <filesystem>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#include <opencv2/opencv.hpp>

namespace fs = std::filesystem;

struct ImageTexture {
    GLuint textureID;
    int width, height;
};

// Function to load texture from image
ImageTexture LoadTextureFromImage(const char* imagePath) {
    ImageTexture imgTexture = {0, 0, 0};
    int width, height, channels;
    unsigned char* data = stbi_load(imagePath, &width, &height, &channels, 0);
    if (data == nullptr) {
        std::cerr << "Error loading image: " << imagePath << std::endl;
        return imgTexture;
    }

    glGenTextures(1, &imgTexture.textureID);
    glBindTexture(GL_TEXTURE_2D, imgTexture.textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, channels == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    stbi_image_free(data);

    imgTexture.width = width;
    imgTexture.height = height;

    return imgTexture;
}

void TextCenteredWithOutline(const std::string& text, float fontSize, ImFont* font) {
    ImGuiIO& io = ImGui::GetIO();

    if (font) {
        ImGui::PushFont(font);
    }

    ImVec2 textSize = ImGui::CalcTextSize(text.c_str(), NULL, true, io.DisplaySize.x);
    ImVec2 screenSize = io.DisplaySize;
    float text_indentation = (screenSize.x - textSize.x) * 0.5f;
    float textPos_y = (screenSize.y - textSize.y) * 0.5f;
    ImDrawList* draw_list = ImGui::GetForegroundDrawList();
    ImVec2 textPos = ImVec2(text_indentation > 0 ? text_indentation : 0, textPos_y);

    ImColor outlineColor = ImColor(0, 0, 0, 255);
    ImColor textColor = ImColor(50, 45, 255, 255);

    float outlineThickness = 1.0f;

    for (int x = -outlineThickness; x <= outlineThickness; ++x) {
        for (int y = -outlineThickness; y <= outlineThickness; ++y) {
            if (x != 0 || y != 0) {
                draw_list->AddText(font, fontSize, ImVec2(textPos.x + x, textPos.y + y), outlineColor, text.c_str());
            }
        }
    }

    draw_list->AddText(font, fontSize, textPos, textColor, text.c_str());

    if (font) {
        ImGui::PopFont();
    }
}

void windowCloseCallback(GLFWwindow* window) {
    glfwDestroyWindow(window);
}

void windowKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    auto altF4 = (key == GLFW_KEY_W && mods == GLFW_MOD_SUPER);
    auto commandQ = (key == GLFW_KEY_F4 && mods == GLFW_MOD_ALT && action == GLFW_PRESS);

    if (altF4 || commandQ) {
        glfwDestroyWindow(window);
    }
}

int main() {
    if (!glfwInit()) {
        std::cerr << "Error initializing GLFW." << std::endl;
        return -1;
    }

    // GLFW window creation
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // Main window
    GLFWwindow* window = glfwCreateWindow(1024, 768, "Image Grid with ImGui", nullptr, nullptr);
    if (window == nullptr) {
        std::cerr << "Error creating GLFW window." << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSwapInterval(1); // Enable vsync

    // Video window
    GLFWwindow* videoWindow = nullptr;

    /*
    GLFWwindow* videoWindow = glfwCreateWindow(1024, 768, "Video Player", nullptr, nullptr);
    if (videoWindow == nullptr) {
        std::cerr << "Error creating GLFW video window." << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwSetWindowCloseCallback(videoWindow, windowCloseCallback);
    glfwSetKeyCallback(videoWindow, windowKeyCallback);
    */

    // ImGui initialization
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    ImGui::StyleColorsDark();

    ImFont* myFont = io.Fonts->AddFontFromFileTTF("fonts/Poppins-Bold.ttf", 40);

    if (!myFont) {
        std::cerr << "Error while load font." << std::endl;
    }

    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init();

    // Load images from directory
    std::vector<ImageTexture> textures;
    std::string pathToImages = "images";

    for (const auto& entry : fs::directory_iterator(pathToImages)) {
        if (entry.is_regular_file()) {
            textures.push_back(LoadTextureFromImage(entry.path().string().c_str()));
        }
    }

    // Open video file
    cv::VideoCapture video("videos/video1.mp4");
    if (!video.isOpened()) {
        std::cerr << "Error opening video." << std::endl;
        return -1;
    }

    cv::Mat frame;
    GLuint videoTexture = 0;

    int videoWidth = 0;
    int videoHeight = 0;

    double fps = video.get(cv::CAP_PROP_FPS);
    auto frameDuration = std::chrono::milliseconds(static_cast<int>(1000 / fps));
    std::chrono::steady_clock::time_point lastFrameTime = std::chrono::steady_clock::now();

    ImVec2 pos = ImVec2(100, 100); // Posição inicial do texto na tela
    const char* text = "Texto com Contorno"; // O texto que você quer desenhar
    ImFont* pFont = ImGui::GetFont();

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();

        // Get video frame
        auto currentTime = std::chrono::steady_clock::now();
        if (std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastFrameTime) >= frameDuration) {
            if (video.read(frame)) {
                // Convert BGR to RGBA
                cv::Mat frameRGBA;
                cv::cvtColor(frame, frameRGBA, cv::COLOR_BGR2RGBA);

                // Update video texture
                if (videoTexture == 0) {
                    glGenTextures(1, &videoTexture);
                    glBindTexture(GL_TEXTURE_2D, videoTexture);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                }

                glBindTexture(GL_TEXTURE_2D, videoTexture);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frameRGBA.cols, frameRGBA.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, frameRGBA.data);

                if (videoWidth == 0 || videoHeight == 0) {
                    videoWidth = frame.cols;
                    videoHeight = frame.rows;
                }

                lastFrameTime = currentTime;
            } else {
                // Restart video playback when reaching the end
                video.set(cv::CAP_PROP_POS_FRAMES, 0);
            }
        }

        // Main window
        glfwMakeContextCurrent(window);
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        // Desenha o contorno em preto
        //TextCenteredWithOutline("Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test", 40, myFont);

        // Render image grid
        ImGui::Begin("Images");

        float windowWidth = ImGui::GetContentRegionAvail().x;
        int imagesPerRow = std::max(1, static_cast<int>(windowWidth / 100.0f));
        float imageSize = windowWidth / imagesPerRow;

        for (int i = 0; i < textures.size(); ++i) {
            float aspectRatio = static_cast<float>(textures[i].width) / textures[i].height;
            ImGui::Image((void*)(intptr_t)textures[i].textureID, ImVec2(imageSize, imageSize / aspectRatio));
            if ((i + 1) % imagesPerRow != 0) ImGui::SameLine();
        }

        ImGui::End();

        // Render video        
        if (videoWindow) {
            glfwMakeContextCurrent(videoWindow);
            glfwPollEvents();
        }

        if (videoTexture != 0) {
            ImGui::Begin("Video Player", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);

            // Get total window dimensions from ImGui
            ImVec2 windowSize = ImGui::GetContentRegionAvail();

            // Calculate video aspect ratio
            float videoAspectRatio = (float)videoWidth / (float)videoHeight;

            // Calculate image size based on video and window aspect ratio
            ImVec2 imageSize;
            if (windowSize.x / windowSize.y > videoAspectRatio) {
                // Window is wider than video
                imageSize.x = windowSize.y * videoAspectRatio;
                imageSize.y = windowSize.y;
            } else {
                // Window is taller than video
                imageSize.x = windowSize.x;
                imageSize.y = windowSize.x / videoAspectRatio;
            }

            // Calculate position to center the image in the window
            ImVec2 imagePos = ImVec2(
                ImGui::GetCursorPos().x + (windowSize.x - imageSize.x) * 0.5f, // X position for centering
                ImGui::GetCursorPos().y + (windowSize.y - imageSize.y) * 0.5f  // Y position for centering
                );

            // Apply calculated position
            ImGui::SetCursorPos(imagePos);

            // Render video image with adjusted size and position
            ImGui::Image((void*)(intptr_t)videoTexture, imageSize);

            ImGui::End();
        }

        // Render ImGui
        ImGui::Render();
        int displayW, displayH;
        glfwGetFramebufferSize(window, &displayW, &displayH);
        glViewport(0, 0, displayW, displayH);
        glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(window);
    }

    // Cleanup
    if (videoTexture != 0) {
        glDeleteTextures(1, &videoTexture);
    }

    for (auto& texture : textures) {
        glDeleteTextures(1, &texture.textureID);
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    if (videoWindow) {
        glfwDestroyWindow(videoWindow);
    }

    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}
paulocoutinhox commented 7 months ago

Also, i use your docking branch, but it is very strange uhauhahuaahu:

image
#include "imgui.h"
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
#include <chrono>
#include <filesystem>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#include <opencv2/opencv.hpp>

namespace fs = std::filesystem;

struct ImageTexture {
    GLuint textureID;
    int width, height;
};

// Function to load texture from image
ImageTexture LoadTextureFromImage(const char* imagePath) {
    ImageTexture imgTexture = {0, 0, 0};
    int width, height, channels;
    unsigned char* data = stbi_load(imagePath, &width, &height, &channels, 0);
    if (data == nullptr) {
        std::cerr << "Error loading image: " << imagePath << std::endl;
        return imgTexture;
    }

    glGenTextures(1, &imgTexture.textureID);
    glBindTexture(GL_TEXTURE_2D, imgTexture.textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, channels == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    stbi_image_free(data);

    imgTexture.width = width;
    imgTexture.height = height;

    return imgTexture;
}

void TextCenteredWithOutline(const std::string& text, float fontSize, ImFont* font) {
    ImGuiIO& io = ImGui::GetIO();

    if (font) {
        ImGui::PushFont(font);
    }

    // Configuração para quebra de texto
    float wrap_width = io.DisplaySize.x * 0.9f; // Define a largura máxima para quebra de texto

    // Calcula a posição centralizada baseado na largura máxima do texto
    ImVec2 textSize = ImGui::CalcTextSize(text.c_str(), NULL, true, wrap_width);
    ImVec2 pos = ImVec2((io.DisplaySize.x - textSize.x) * 0.5f, (io.DisplaySize.y - textSize.y) * 0.5f);

    ImDrawList* draw_list = ImGui::GetForegroundDrawList();
    ImVec2 textPos = ImVec2(pos.x, pos.y);

    ImColor outlineColor = ImColor(0, 0, 0, 255); // Cor do contorno
    ImColor textColor = ImColor(255, 255, 255, 255); // Cor do texto

    // Desenha o contorno
    float outlineThickness = 2.0f;
    for (int x = -outlineThickness; x <= outlineThickness; ++x) {
        for (int y = -outlineThickness; y <= outlineThickness; ++y) {
            if (x != 0 || y != 0) {
                // Adiciona cada letra do texto com um pequeno deslocamento para criar o efeito de contorno
                draw_list->AddText(NULL, fontSize, ImVec2(textPos.x + x, textPos.y + y), outlineColor, text.c_str(), NULL, wrap_width);
            }
        }
    }

    // Desenha o texto principal
    draw_list->AddText(NULL, fontSize, textPos, textColor, text.c_str(), NULL, wrap_width);

    if (font) {
        ImGui::PopFont();
    }
}

void windowCloseCallback(GLFWwindow* window) {
    glfwDestroyWindow(window);
}

void windowKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    auto altF4 = (key == GLFW_KEY_W && mods == GLFW_MOD_SUPER);
    auto commandQ = (key == GLFW_KEY_F4 && mods == GLFW_MOD_ALT && action == GLFW_PRESS);

    if (altF4 || commandQ) {
        glfwDestroyWindow(window);
    }
}

int main() {
    if (!glfwInit()) {
        std::cerr << "Error initializing GLFW." << std::endl;
        return -1;
    }

    // GLFW window creation
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // Main window
    GLFWwindow* window = glfwCreateWindow(1024, 768, "Image Grid with ImGui", nullptr, nullptr);
    if (window == nullptr) {
        std::cerr << "Error creating GLFW window." << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSwapInterval(1); // Enable vsync

    // Video window
    GLFWwindow* videoWindow = nullptr;

    /*
    GLFWwindow* videoWindow = glfwCreateWindow(1024, 768, "Video Player", nullptr, nullptr);
    if (videoWindow == nullptr) {
        std::cerr << "Error creating GLFW video window." << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwSetWindowCloseCallback(videoWindow, windowCloseCallback);
    glfwSetKeyCallback(videoWindow, windowKeyCallback);
    */

    // ImGui initialization
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    ImGui::StyleColorsDark();
    io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;

    ImFont* myFont = io.Fonts->AddFontFromFileTTF("fonts/Poppins-Bold.ttf", 20);

    if (!myFont) {
        std::cerr << "Error while load font." << std::endl;
    }

    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init();

    // Load images from directory
    std::vector<ImageTexture> textures;
    std::string pathToImages = "images";

    for (const auto& entry : fs::directory_iterator(pathToImages)) {
        if (entry.is_regular_file()) {
            textures.push_back(LoadTextureFromImage(entry.path().string().c_str()));
        }
    }

    // Open video file
    cv::VideoCapture video("videos/video1.mp4");
    if (!video.isOpened()) {
        std::cerr << "Error opening video." << std::endl;
        return -1;
    }

    cv::Mat frame;
    GLuint videoTexture = 0;

    int videoWidth = 0;
    int videoHeight = 0;

    double fps = video.get(cv::CAP_PROP_FPS);
    auto frameDuration = std::chrono::milliseconds(static_cast<int>(1000 / fps));
    std::chrono::steady_clock::time_point lastFrameTime = std::chrono::steady_clock::now();

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();

        // Get video frame
        auto currentTime = std::chrono::steady_clock::now();
        if (std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastFrameTime) >= frameDuration) {
            if (video.read(frame)) {
                // Convert BGR to RGBA
                cv::Mat frameRGBA;
                cv::cvtColor(frame, frameRGBA, cv::COLOR_BGR2RGBA);

                // Update video texture
                if (videoTexture == 0) {
                    glGenTextures(1, &videoTexture);
                    glBindTexture(GL_TEXTURE_2D, videoTexture);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                }

                glBindTexture(GL_TEXTURE_2D, videoTexture);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frameRGBA.cols, frameRGBA.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, frameRGBA.data);

                if (videoWidth == 0 || videoHeight == 0) {
                    videoWidth = frame.cols;
                    videoHeight = frame.rows;
                }

                lastFrameTime = currentTime;
            } else {
                // Restart video playback when reaching the end
                video.set(cv::CAP_PROP_POS_FRAMES, 0);
            }
        }

        // Main window
        glfwMakeContextCurrent(window);
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        // Desenha o contorno em preto
        TextCenteredWithOutline("Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test Demo Test", 100, myFont);

        // Render image grid
        ImGui::Begin("Images", nullptr, ImGuiWindowFlags_NoDocking);

        float windowWidth = ImGui::GetContentRegionAvail().x;
        int imagesPerRow = std::max(1, static_cast<int>(windowWidth / 100.0f));
        float imageSize = windowWidth / imagesPerRow;

        for (int i = 0; i < textures.size(); ++i) {
            float aspectRatio = static_cast<float>(textures[i].width) / textures[i].height;
            ImGui::Image((void*)(intptr_t)textures[i].textureID, ImVec2(imageSize, imageSize / aspectRatio));
            if ((i + 1) % imagesPerRow != 0) ImGui::SameLine();
        }

        ImGui::End();

        // Render video        
        if (videoWindow) {
            glfwMakeContextCurrent(videoWindow);
            glfwPollEvents();
        }

        if (videoTexture != 0) {
            ImGui::Begin("Video Player", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);

            // Get total window dimensions from ImGui
            ImVec2 windowSize = ImGui::GetContentRegionAvail();

            // Calculate video aspect ratio
            float videoAspectRatio = (float)videoWidth / (float)videoHeight;

            // Calculate image size based on video and window aspect ratio
            ImVec2 imageSize;
            if (windowSize.x / windowSize.y > videoAspectRatio) {
                // Window is wider than video
                imageSize.x = windowSize.y * videoAspectRatio;
                imageSize.y = windowSize.y;
            } else {
                // Window is taller than video
                imageSize.x = windowSize.x;
                imageSize.y = windowSize.x / videoAspectRatio;
            }

            // Calculate position to center the image in the window
            ImVec2 imagePos = ImVec2(
                ImGui::GetCursorPos().x + (windowSize.x - imageSize.x) * 0.5f, // X position for centering
                ImGui::GetCursorPos().y + (windowSize.y - imageSize.y) * 0.5f  // Y position for centering
                );

            // Apply calculated position
            ImGui::SetCursorPos(imagePos);

            // Render video image with adjusted size and position
            ImGui::Image((void*)(intptr_t)videoTexture, imageSize);

            ImGui::End();
        }

        // Render ImGui
        ImGui::Render();
        int displayW, displayH;
        glfwGetFramebufferSize(window, &displayW, &displayH);
        glViewport(0, 0, displayW, displayH);
        glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
        {
            ImGui::UpdatePlatformWindows();
            ImGui::RenderPlatformWindowsDefault();
        }

        glfwSwapBuffers(window);
    }

    // Cleanup
    if (videoTexture != 0) {
        glDeleteTextures(1, &videoTexture);
    }

    for (auto& texture : textures) {
        glDeleteTextures(1, &texture.textureID);
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    if (videoWindow) {
        glfwDestroyWindow(videoWindow);
    }

    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}
GamingMinds-DanielC commented 7 months ago

Another problem is with font on it. When i add font TTF, it apply for everything, but i want only in my function TextCenteredWithOutline:

The default font is loaded automatically only when no other font is specified, so you are replacing it. If you want both, you need to explicitly add the default font first and then add your own as a second font.

For specifics, here is a link to the font documentation: https://github.com/ocornut/imgui/blob/master/docs/FONTS.md

paulocoutinhox commented 7 months ago

Another problem is with font on it. When i add font TTF, it apply for everything, but i want only in my function TextCenteredWithOutline:

The default font is loaded automatically only when no other font is specified, so you are replacing it. If you want both, you need to explicitly add the default font first and then add your own as a second font.

For specifics, here is a link to the font documentation: https://github.com/ocornut/imgui/blob/master/docs/FONTS.md

Ok, thanks, i read it but in my tests i put AddFontDefault after, now i put before and work.

paulocoutinhox commented 7 months ago

I am not sure what your "freeze" is because, since you are not providing more information, but AFAIK the stock GLFW backend doesn't currently support multi-context because of how glfwPollEvents() dispatch information you don't have a chance to change context. This is behind discussed in #7186 #7155 #5671

Switching to use multi-viewports should fix the issue for you.

I will wait for the GLFW multi window support and will stop the project migration now until it be ready.

ocornut commented 7 months ago

This is not a chat room, you are sending messages to a mailing list 1000+ people and we can't debug your code expecially if you spam a thread with code variations. Please do more research before opening new issue.

I will wait for the GLFW multi window support and will stop the project migration now until it be ready.

Sounds like a XY Problem reaction. You should be able to use multi-viewports for this.

paulocoutinhox commented 7 months ago

Dude, you are very stressed and assume a lot of things. I just said in a friendly and laughing way that the viewport solution is weird, it's awkward to use and that I prefer the separate windows solution because that's how I do it with Qt and the user experience (UX) is much better. I tested it locally and found the multi-viewport feature strange.

If the window solution doesn't come out, this should be the only option.

ocornut commented 7 months ago

You said "it is very strange uhauhahuaahu:" without clarifying precisely what was your issue is with it, then immediately went out finding an alternative instead of pursuing the suggested route. So (1) You are not actually explaining your issue (2) you are immediately giving up right after I told you can you solve your problem with it. How are we supposed to help more?

I prefer the separate windows solution

Multi-viewports ARE separate windows. You are not specifying what your problem actually is so we could help you understand the options and features, instead you are being vague with your statement and throwing hundreds of lines of code in repeated manner.

There are nearly 1000 issues open and we're providing this service for free to you. If you want help you need to learn to ask questions and discuss problems clearly, not fire multiple messages in a row which suggests you are posting without doing researches.

ocornut commented 7 months ago

If your question was explicit and precise, e.g. "I would prefer if separate window had an OS title bar instead of a Dear ImGui title bar, is that possible?" we would provide you answer. But your question being "it is awkward to use, i'm giving up" I am not sure how I am supposed to help you.

paulocoutinhox commented 7 months ago

Hi,

I put the code to draw "video player" window in the second monitor, it is working, but it is the "correct way" to do?

if (starting) {
    if (monitorsCount > 1) {
        ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;

        int monitorPosX, monitorPosY, monitorWidth, monitorHeight;
        glfwGetMonitorWorkarea(monitors[1], &monitorPosX, &monitorPosY, &monitorWidth, &monitorHeight);

        videoWinPos = ImVec2(monitorPosX, monitorPosY);
        videoWinSize = ImVec2(monitorWidth, monitorHeight);

        videoFlags = videoFlags | ImGuiWindowFlags_NoDecoration;
    } else {
        ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;

        videoWinPos = ImVec2(ImGui::GetCursorPos().x + 50, ImGui::GetCursorPos().y + 300);
        videoWinSize = ImVec2(400, 200);
    }
} else {
    if (monitorsCount > 1) {
        ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;

        int monitorPosX, monitorPosY, monitorWidth, monitorHeight;
        glfwGetMonitorWorkarea(monitors[1], &monitorPosX, &monitorPosY, &monitorWidth, &monitorHeight);

        videoWinPos = ImVec2(monitorPosX, monitorPosY);
        videoWinSize = ImVec2(monitorWidth, monitorHeight);

        videoFlags = videoFlags | ImGuiWindowFlags_NoDecoration;
    } else {
        ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;

        videoWinPos = ImVec2(ImGui::GetCursorPos().x + 50, ImGui::GetCursorPos().y + 300);
        videoWinSize = ImVec2(400, 200);
    }
}

Now, im trying "block" the other window (image grid, for example) to don't be moved outside the main system window, i don't want that other windows be moved outside the main window, only the video player. I tried the flag ImGuiWindowFlags_NoDocking but it don't work. There is any option to this? Because if i enable viewport all window can be dragged outside the main window. Because this, i prefer the solution of multiple system window, understand.

Also, when i have only one monitor, the "video player window" need be inside main system window, without dragged outside. And when the monitor is > 1, the video player go to this second monitor.

My code is working and doing it, the main problem is disable the drag to outside of main window.

I only can reach this adding or removing flags from io:

ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_ViewportsEnable
or
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;

But what i want is disable the floating window from main system window for individual imgui windows.

Can you help me?