ImGui - GUI-фреймворк, написанный на C++ с минимальными зависимостями. Ссылка на github - https://github.com/ocornut/imgui Пример программы, использующей ImGui Это самый обычный пример использования ImGui для desktop-приложения с передвижением окна, можно применять для любых целей. Для тех, кто сам не может сделать. #include "Frameworks/ImGui/imgui.h" #include "Frameworks/ImGui/imgui_impl_win32.h" #include "Frameworks/ImGui/imgui_impl_dx11.h" #include <d3d11.h> #define DIRECTINPUT_VERSION 0x0800 #include <dinput.h> #include <tchar.h> #include <string> #pragma comment(lib, "d3d11.lib") POINTS m_Pos; float WindowWidth = 800.0; float WindowHeight = 600.0; bool IsOpened = true; static ID3D11Device* g_pd3dDevice = NULL; static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; static IDXGISwapChain* g_pSwapChain = NULL; static ID3D11RenderTargetView* g_mainRenderTargetView = NULL; bool CreateDeviceD3D(HWND hWnd); void CleanupDeviceD3D(); void CreateRenderTarget(); void CleanupRenderTarget(); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) { WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, LoadCursor(NULL, IDC_ARROW), NULL, NULL, _T("ImGui Example"), NULL }; ::RegisterClassEx(&wc); HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX11 Example"), WS_POPUP, 0, 0, (int)WindowWidth, (int)WindowHeight, NULL, NULL, wc.hInstance, NULL); if (!CreateDeviceD3D(hwnd)) { CleanupDeviceD3D(); ::UnregisterClass(wc.lpszClassName, wc.hInstance); return 1; } ::ShowWindow(hwnd, SW_SHOWDEFAULT); ::UpdateWindow(hwnd); IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui::StyleColorsDark(); ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); //IM_ASSERT(font != NULL); MSG msg; ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); continue; } ImGui_ImplDX11_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); ImGui::SetNextWindowPos(ImVec2(0, 0)); ImGui::Begin("ImGui Example", &IsOpened, ImVec2(WindowWidth, WindowHeight), 1.0f, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); { static float f = 0.0f; static int counter = 0; if (IsOpened == false) exit(0); ImGui::Text("This is some useful text."); if (ImGui::Button("Button")) counter++; ImGui::SameLine(); ImGui::Text("counter = %d", counter); static char str1[128] = ""; ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); } ImGui::End(); ImGui::Render(); g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, NULL); //g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, (float*)& ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); g_pSwapChain->Present(1, 0); } ImGui_ImplDX11_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); CleanupDeviceD3D(); ::DestroyWindow(hwnd); ::UnregisterClass(wc.lpszClassName, wc.hInstance); return 0; } bool CreateDeviceD3D(HWND hWnd) { DXGI_SWAP_CHAIN_DESC sd; ZeroMemory(&sd, sizeof(sd)); sd.BufferCount = 2; sd.BufferDesc.Width = 0; sd.BufferDesc.Height = 0; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.OutputWindow = hWnd; sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.Windowed = TRUE; sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; UINT createDeviceFlags = 0; D3D_FEATURE_LEVEL featureLevel; const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, }; if (D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext) != S_OK) return false; CreateRenderTarget(); return true; } void CleanupDeviceD3D() { CleanupRenderTarget(); if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = NULL; } if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; } if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } } void CreateRenderTarget() { ID3D11Texture2D* pBackBuffer; g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_mainRenderTargetView); pBackBuffer->Release(); } void CleanupRenderTarget() { if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = NULL; } } extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) return true; ImGuiIO& io = ImGui::GetIO(); switch (msg) { case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: { if (msg == WM_LBUTTONDOWN) m_Pos = MAKEPOINTS(lParam); int button = 0; if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) ::SetCapture(hWnd); io.MouseDown[button] = true; return 0; } case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: { int button = 0; if (msg == WM_LBUTTONUP) button = 0; if (msg == WM_RBUTTONUP) button = 1; if (msg == WM_MBUTTONUP) button = 2; io.MouseDown[button] = false; if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hWnd) ::ReleaseCapture(); return 0; } case WM_MOUSEWHEEL: io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; return 0; case WM_MOUSEHWHEEL: io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; return 0; case WM_KEYDOWN: case WM_SYSKEYDOWN: if (wParam < 256) io.KeysDown[wParam] = 1; return 0; case WM_KEYUP: case WM_SYSKEYUP: if (wParam < 256) io.KeysDown[wParam] = 0; return 0; //case WM_CHAR: // wchar_t wch; // MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char*)& wParam, 1, &wch, 1); // io.AddInputCharacter(wch); // return 0; case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT) { if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return false; ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { ::SetCursor(NULL); } else { LPTSTR win32_cursor = IDC_ARROW; switch (imgui_cursor) { case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; } ::SetCursor(::LoadCursor(NULL, win32_cursor)); } return 1; } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; case WM_MOUSEMOVE: { float TitleHeight = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f; io.MousePos.x = (signed short)(lParam); io.MousePos.y = (signed short)(lParam >> 16); if (wParam == MK_LBUTTON) { POINTS p = MAKEPOINTS(lParam); RECT rect; GetWindowRect(hWnd, &rect); rect.left += p.x - m_Pos.x; rect.top += p.y - m_Pos.y; if ((m_Pos.x >= 0 && m_Pos.x <= WindowWidth && m_Pos.y >= 0 && m_Pos.y <= TitleHeight)) SetWindowPos(hWnd, HWND_TOPMOST, rect.left, rect.top, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOZORDER); } return 0; } } return DefWindowProc(hWnd, msg, wParam, lParam); } Code #include "Frameworks/ImGui/imgui.h" #include "Frameworks/ImGui/imgui_impl_win32.h" #include "Frameworks/ImGui/imgui_impl_dx11.h" #include <d3d11.h> #define DIRECTINPUT_VERSION 0x0800 #include <dinput.h> #include <tchar.h> #include <string> #pragma comment(lib, "d3d11.lib") POINTS m_Pos; float WindowWidth = 800.0; float WindowHeight = 600.0; bool IsOpened = true; static ID3D11Device* g_pd3dDevice = NULL; static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; static IDXGISwapChain* g_pSwapChain = NULL; static ID3D11RenderTargetView* g_mainRenderTargetView = NULL; bool CreateDeviceD3D(HWND hWnd); void CleanupDeviceD3D(); void CreateRenderTarget(); void CleanupRenderTarget(); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) { WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, LoadCursor(NULL, IDC_ARROW), NULL, NULL, _T("ImGui Example"), NULL }; ::RegisterClassEx(&wc); HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX11 Example"), WS_POPUP, 0, 0, (int)WindowWidth, (int)WindowHeight, NULL, NULL, wc.hInstance, NULL); if (!CreateDeviceD3D(hwnd)) { CleanupDeviceD3D(); ::UnregisterClass(wc.lpszClassName, wc.hInstance); return 1; } ::ShowWindow(hwnd, SW_SHOWDEFAULT); ::UpdateWindow(hwnd); IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui::StyleColorsDark(); ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); //IM_ASSERT(font != NULL); MSG msg; ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); continue; } ImGui_ImplDX11_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); ImGui::SetNextWindowPos(ImVec2(0, 0)); ImGui::Begin("ImGui Example", &IsOpened, ImVec2(WindowWidth, WindowHeight), 1.0f, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); { static float f = 0.0f; static int counter = 0; if (IsOpened == false) exit(0); ImGui::Text("This is some useful text."); if (ImGui::Button("Button")) counter++; ImGui::SameLine(); ImGui::Text("counter = %d", counter); static char str1[128] = ""; ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); } ImGui::End(); ImGui::Render(); g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, NULL); //g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, (float*)& ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); g_pSwapChain->Present(1, 0); } ImGui_ImplDX11_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); CleanupDeviceD3D(); ::DestroyWindow(hwnd); ::UnregisterClass(wc.lpszClassName, wc.hInstance); return 0; } bool CreateDeviceD3D(HWND hWnd) { DXGI_SWAP_CHAIN_DESC sd; ZeroMemory(&sd, sizeof(sd)); sd.BufferCount = 2; sd.BufferDesc.Width = 0; sd.BufferDesc.Height = 0; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.OutputWindow = hWnd; sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.Windowed = TRUE; sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; UINT createDeviceFlags = 0; D3D_FEATURE_LEVEL featureLevel; const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, }; if (D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext) != S_OK) return false; CreateRenderTarget(); return true; } void CleanupDeviceD3D() { CleanupRenderTarget(); if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = NULL; } if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; } if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } } void CreateRenderTarget() { ID3D11Texture2D* pBackBuffer; g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_mainRenderTargetView); pBackBuffer->Release(); } void CleanupRenderTarget() { if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = NULL; } } extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) return true; ImGuiIO& io = ImGui::GetIO(); switch (msg) { case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: { if (msg == WM_LBUTTONDOWN) m_Pos = MAKEPOINTS(lParam); int button = 0; if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) ::SetCapture(hWnd); io.MouseDown[button] = true; return 0; } case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: { int button = 0; if (msg == WM_LBUTTONUP) button = 0; if (msg == WM_RBUTTONUP) button = 1; if (msg == WM_MBUTTONUP) button = 2; io.MouseDown[button] = false; if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hWnd) ::ReleaseCapture(); return 0; } case WM_MOUSEWHEEL: io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; return 0; case WM_MOUSEHWHEEL: io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; return 0; case WM_KEYDOWN: case WM_SYSKEYDOWN: if (wParam < 256) io.KeysDown[wParam] = 1; return 0; case WM_KEYUP: case WM_SYSKEYUP: if (wParam < 256) io.KeysDown[wParam] = 0; return 0; //case WM_CHAR: // wchar_t wch; // MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char*)& wParam, 1, &wch, 1); // io.AddInputCharacter(wch); // return 0; case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT) { if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return false; ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { ::SetCursor(NULL); } else { LPTSTR win32_cursor = IDC_ARROW; switch (imgui_cursor) { case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; } ::SetCursor(::LoadCursor(NULL, win32_cursor)); } return 1; } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; case WM_MOUSEMOVE: { float TitleHeight = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f; io.MousePos.x = (signed short)(lParam); io.MousePos.y = (signed short)(lParam >> 16); if (wParam == MK_LBUTTON) { POINTS p = MAKEPOINTS(lParam); RECT rect; GetWindowRect(hWnd, &rect); rect.left += p.x - m_Pos.x; rect.top += p.y - m_Pos.y; if ((m_Pos.x >= 0 && m_Pos.x <= WindowWidth && m_Pos.y >= 0 && m_Pos.y <= TitleHeight)) SetWindowPos(hWnd, HWND_TOPMOST, rect.left, rect.top, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOZORDER); } return 0; } } return DefWindowProc(hWnd, msg, wParam, lParam); } Ссылка на готовый проект - https://yadi.sk/d/qBs0aTbudC4-fA (выставить Release x86 при компиляции) Скриншот программы