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

Make full-viewport imgui window move host platform window (was: borderless imgui window) #8268

Open
Tellegar opened this issue Dec 29, 2024 · 4 comments

Comments

@Tellegar
Copy link

Tellegar commented Dec 29, 2024

Version/Branch of Dear ImGui:

both latest master and latest docking

Back-ends:

imgui_impl_sdl2.cpp + imgui_impl_sdlrenderer2.cpp

Compiler, OS:

windows 11 + msys clang

Full config/build information:

Dear ImGui 1.91.7 WIP (19163)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=202302
define: _WIN32
define: _WIN64
define: __MINGW32__
define: __MINGW64__
define: __GNUC__=4
define: __clang_version__=18.1.8 
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: imgui_impl_sdl2
io.BackendRendererName: imgui_impl_sdlrenderer2
io.ConfigFlags: 0x00000000
io.ConfigViewportsNoDecoration
io.ConfigNavCaptureKeyboard
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00000C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,128
io.DisplaySize: 1029.00,688.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

My Issue/Question:

is it possible to make the imgui inherit the platform window? e.g. i create a sdl borderless window and i would like to have a single imgui window to draw its own titlebar, handle resize/movement of the underlying platform window

i can achieve this (but with tearing and other issues):

ImGui::SetNextWindowPos({0, 0}, ImGuiCond_Once);
ImGui::SetNextWindowSize(
	ImVec2(static_cast<float>(screen.width), static_cast<float>(screen.height)),
	ImGuiCond_Once
);
ImGui::Begin(
	"hello world",
	&is_running,
	ImGuiWindowFlags_NoBringToFrontOnFocus
);
auto pos = ImGui::GetWindowPos();
auto size = ImGui::GetWindowSize();
if (pos.x != 0 or pos.y != 0) {
	ImGui::SetWindowPos({0, 0});
	SDL_SetWindowPosition(window, sdl_pos.x + static_cast<int>(pos.x), sdl_pos.y + static_cast<int>(pos.y));
}
if (static_cast<int>(size.x) != screen.width or static_cast<int>(size.y) != screen.height) {
	SDL_SetWindowSize(window, static_cast<int>(size.x), static_cast<int>(size.y));
}

// window contents ...

ImGui::End();

i think the tearing comes from using ImGui::SetWindowPos({0, 0}); which updates the position in the next frame, not immediatelly, but using ImGui::SetNextWindowPos would throw away the information about the window repositioning.

otherwise i would have to implement the window repositioning manually via sdl event handling.

docking branch supports this on some renderer backends (not sdlrender2), but i cant figure out how to have main window to be the imgui window

Screenshots/Video:

2024-12-29.00-41-42.mp4

Minimal, Complete and Verifiable Example code:

#define SDL_MAIN_HANDLED
#include <chrono>
#include <imgui.h>
#include <imgui_impl_sdl2.h>
#include <imgui_impl_sdlrenderer2.h>
#include <thread>
#include <SDL2/SDL.h>

using namespace std::chrono;
using namespace std::chrono_literals;
constexpr double fps_limit = 60;
constexpr auto frame_time = duration_cast<steady_clock::duration>(1s / fps_limit);

template<typename T, auto create, auto destroy>
struct PtrHolder {
	T data{};
	PtrHolder() = default;
	explicit PtrHolder(T *ptr) : data(ptr) {}
	PtrHolder(auto &&... args) {
		data = create(std::forward<decltype(args)>(args)...);
	}
	~PtrHolder() {
		destroy(data);
	}
	operator auto() {
		return data;
	}
	operator bool() {
		return data != nullptr;
	}
};

struct Screen {
	int width, height;
} screen = {500, 500};

using sdlWindow = PtrHolder<SDL_Window*, SDL_CreateWindow, SDL_DestroyWindow>;
using sdlRenderer = PtrHolder<SDL_Renderer*, SDL_CreateRenderer, SDL_DestroyRenderer>;

void handle_events(bool &is_running, SDL_Window *window) {
	SDL_Event event;
	while (SDL_PollEvent(&event)) {
		ImGui_ImplSDL2_ProcessEvent(&event);
		if (event.type == SDL_QUIT)
			is_running = false;
		if (event.type == SDL_WINDOWEVENT and
			event.window.event == SDL_WINDOWEVENT_CLOSE and
			event.window.windowID == SDL_GetWindowID(window))
			is_running = false;
	}
}

int main() {
	SDL_Init(SDL_INIT_VIDEO);

	bool is_borderless = true;

	sdlWindow window(
		"borderless_window",
		SDL_WINDOWPOS_UNDEFINED,
		SDL_WINDOWPOS_UNDEFINED,
		screen.width,
		screen.height,
		SDL_WINDOW_RESIZABLE |
		(is_borderless
			? SDL_WINDOW_BORDERLESS
			: 0)
	);
	if (not window) return -1;

	sdlRenderer renderer(
		window,
		-1,
		SDL_RENDERER_ACCELERATED
	);
	if (not renderer) return -1;

	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);
	ImGui_ImplSDLRenderer2_Init(renderer);
	ImGuiIO &io = ImGui::GetIO();
	io.IniFilename = nullptr;

	auto frame_start = steady_clock::now();
	bool is_running = true;
	while (is_running) {
		handle_events(is_running, window);
		struct {
			int x = 0, y = 0;
		} sdl_pos;
		SDL_GetWindowPosition(window, &sdl_pos.x, &sdl_pos.y);
		ImGui_ImplSDLRenderer2_NewFrame();
		ImGui_ImplSDL2_NewFrame();
		ImGui::NewFrame();

		ImGui::SetNextWindowPos({0, 0}, ImGuiCond_Once);
		ImGui::SetNextWindowSize(
			ImVec2(static_cast<float>(screen.width), static_cast<float>(screen.height)),
			ImGuiCond_Once
		);
		ImGui::Begin(
			"hello world",
			&is_running,
			ImGuiWindowFlags_NoBringToFrontOnFocus
		);
		auto pos = ImGui::GetWindowPos();
		auto size = ImGui::GetWindowSize();
		if (pos.x != 0 or pos.y != 0) {
			ImGui::SetWindowPos({0, 0});
			SDL_SetWindowPosition(window, sdl_pos.x + static_cast<int>(pos.x), sdl_pos.y + static_cast<int>(pos.y));
		}
		if (static_cast<int>(size.x) != screen.width or static_cast<int>(size.y) != screen.height) {
			SDL_SetWindowSize(window, static_cast<int>(size.x), static_cast<int>(size.y));
		}
		ImGui::Text("sdl_pos:  %d %d", sdl_pos.x, sdl_pos.y);
		ImGui::Text("pos:      %f %f", pos.x, pos.y);
		ImGui::Text("size:     %f %f", size.x, size.y);
		ImGui::End();

		static bool show_demo_window = false;
		if (ImGui::IsKeyPressed(ImGuiKey_F12))
			show_demo_window = not show_demo_window;
		if (show_demo_window)
			ImGui::ShowDemoWindow(&show_demo_window);

		if (ImGui::IsKeyPressed(ImGuiKey_F11))
			SDL_SetWindowBordered(window, static_cast<SDL_bool>(not((is_borderless = not is_borderless))));

		ImGui::Render();
		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
		SDL_RenderClear(renderer);
		ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer);
		SDL_RenderPresent(renderer);

		auto now = steady_clock::now();
		if (now - frame_start <= frame_time) {
			std::this_thread::sleep_until(frame_start + frame_time);
			frame_start += frame_time;
		} else // missed frame
			frame_start = now;
	}

	SDL_Quit();

	return 0;
}
@ocornut ocornut changed the title borderless imgui window Make full-viewport imgui window move host platform window (was: borderless imgui window) Dec 29, 2024
@PathogenDavid
Copy link
Contributor

There's currently no straightforward way to accomplish this. Proper support is covered by #3680 and #3350

ImHex appears to do what you're wanting. I've never dug into how they're doing it, but it might be worth looking into.

otherwise i would have to implement the window repositioning manually via sdl event handling.

This is basically how things work when multi-viewports are in use.

@o-3-o
Copy link

o-3-o commented Dec 30, 2024

Yes, this feature is still necessary for developing applications with ImGui. I don't know if this is in the plan. I have also been trying to deal with this problem, but it is not ideal because I also want to adapt to APIs such as ImGui::SetNextWindowSizeConstraints.

@ocornut
Copy link
Owner

ocornut commented Dec 30, 2024

It’s a frequently requested feature to be able to start a window drag from any desired location, so I will eventually work on it.

It may be implemented in two ways: emulating move (the same way we currently do when dragging eg a viewport, or forwarding to the OS move which is more involved but has a few advantages.

@WerWolv
Copy link

WerWolv commented Dec 31, 2024

ImHex does very platform-dependent things to handle this correctly.

Instead of having ImGui inherit the window in some way, we do one of the following:

  • On Windows we use a borderless window but then define a custom windowProc function so that Windows Aero, resizing and window decoration such as the shadow drawn around the window works correctly. This works great for the most part but on some Intel HD GPUs, this causes the window content to be shifted weirdly
  • On macOS, we basically just tell the OS to turn the title bar background invisible and move it down into the client area. That way the OS handles basically everything for you and you can just draw where the title bar used to be.
  • On Linux it gets a bit tricky. ImHex doesn't implement any of this right now but you would need to create a borderless window and then talk to libdecor to handle the window decoration minus the title bar
    • I believe this is actually the only way to do it properly because Wayland, by design, doesn't allow applications to move or resize their window.

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

5 participants