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

Implement Multi-Viewports on macOS #2778

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
6f2ea0d
Implement Multi-Viewports on macOS
Sep 6, 2019
cbe0446
Fix wrong parameter
Sep 6, 2019
bd865c9
Fix compile error
Sep 6, 2019
d997eef
Rename window -> Window
Sep 17, 2019
d75bea2
Revert fix mouse position
Sep 17, 2019
7567dd0
Fix NSWindow not found
Sep 17, 2019
78ad75a
Implement Multi-Viewport for Metal
Sep 17, 2019
4a2394d
Move some code
Sep 17, 2019
f629ebc
Merge branch 'docking' into docking
metarutaiga Oct 21, 2019
32077a0
Compatible for Xcode version below 12.X
Nov 5, 2019
273ff30
Merge branch 'docking' into docking
metarutaiga Mar 21, 2020
57b17ba
Merge commit '455c21df7100a4727dd6e4c8e69249b7de21d24c' into docking
Oct 26, 2020
00d62fd
Merge commit '455c21df7100a4727dd6e4c8e69249b7de21d24c' into docking
Oct 26, 2020
ff9b514
Fix render problem when using between LowDPI and HiDPI monitors
Oct 27, 2020
ba453e9
FIX Miss to add tracking area on other windows
Oct 28, 2020
0dcd202
FIX Miss to call shutdown when closing
Oct 28, 2020
d5969ab
Rename ImGui_ImplOSX_TrackingArea to ImGui_ImplOSX_AddTrackingArea
Oct 28, 2020
fea123f
FIX validateMTLScissorRect problem when dragging window between LowDP…
Oct 28, 2020
5013801
Remove some unused codes
Oct 28, 2020
39d4005
Revert
Oct 28, 2020
95f0c20
Remove some unused codes
Oct 28, 2020
97cb8fe
Oops
Oct 28, 2020
c7877a6
Oops
Oct 29, 2020
d4567d5
Merge branch 'docking' of https://github.com/ocornut/imgui into docking
Oct 31, 2020
5756bd4
Oops
Oct 31, 2020
614519f
Oops
Oct 31, 2020
f50b219
Oops
Oct 31, 2020
2238477
Add QuartzCore for CAMetalLayer
Oct 31, 2020
e100b56
Merge commit 'ac08593b9645aee7e086b1e9b98a6a1d79d09210' into docking
Nov 9, 2020
d4bb050
Reduce modified codes
Nov 9, 2020
db5d317
Try to fix top floating windows
Nov 12, 2020
a92ab94
Try to fix hide floating windows when the main window is minimized
Nov 12, 2020
7748d4a
The floating windows is not always on top
Nov 12, 2020
804954e
Merge remote-tracking branch 'ocornut/docking' into docking
Nov 20, 2020
d45c882
Merge branch 'docking' of https://github.com/ocornut/imgui into docking
Mar 6, 2021
bf2770b
Try the method but it would be flashing when click main window
Mar 6, 2021
cff6714
Or try the method
Mar 6, 2021
d264c9d
Prevent to add many monitors
Mar 6, 2021
47edd02
More stable
Mar 6, 2021
a757b0f
Fix problem when mouse move titlebar and up to click other application
Mar 6, 2021
2c71cde
Revert merged
Mar 6, 2021
b28f4a6
Reduce modified codes
Mar 6, 2021
2de9bf8
Hide other windows when the main window is minimized
Mar 6, 2021
8ffa379
NSWindow may leak when destroying
Mar 7, 2021
b836d10
Remove addChildWindow because it can't show in multiple screens
Mar 8, 2021
69cd520
Reorder other windows when the main window is focused
Mar 8, 2021
401717a
Prevent show windows randomly when NSApp deactivated
Mar 15, 2021
4376316
Use monitor instead of event for all mouse event
Mar 16, 2021
e03e992
Fix typo
Mar 19, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 169 additions & 4 deletions backends/imgui_impl_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
// Implemented features:
// [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
// Missing features:
// [ ] Renderer: Multi-viewport / platform windows.
// [X] Renderer: Multi-viewport / platform windows.

// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
Expand All @@ -15,6 +14,7 @@
// (minor and older changes stripped away, please see git history for details)
// 2021-02-18: Metal: Change blending equation to preserve alpha in output buffer.
// 2021-01-25: Metal: Fixed texture storage mode when building on Mac Catalyst.
// 2019-09-17: Metal: Added support for Multi-viewport.
// 2019-05-29: Metal: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: Metal: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-02-11: Metal: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
Expand All @@ -25,8 +25,32 @@
#include "imgui_impl_metal.h"

#import <Metal/Metal.h>
// #import <QuartzCore/CAMetalLayer.h> // Not supported in XCode 9.2. Maybe a macro to detect the SDK version can be used (something like #if MACOS_SDK >= 10.13 ...)
#if TARGET_OS_SIMULATOR == 0 || __IPHONE_13_0
#import <QuartzCore/CAMetalLayer.h>
#else
// Compatible for Xcode version below 12.X
#import <QuartzCore/CALayer.h>
@protocol CAMetalDrawable <MTLDrawable>
@property(readonly) id<MTLTexture> texture;
@end
@interface CAMetalLayer : CALayer
@property(nullable, retain) id<MTLDevice> device;
@property MTLPixelFormat pixelFormat;
@property BOOL framebufferOnly;
@property CGSize drawableSize;
- (nullable id<CAMetalDrawable>)nextDrawable;
@end
#endif
#import <simd/simd.h>
#if TARGET_OS_OSX
#import <Cocoa/Cocoa.h>
#endif

// Forward Declarations
static void ImGui_ImplMetal_InitPlatformInterface();
static void ImGui_ImplMetal_ShutdownPlatformInterface();
static void ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows();
static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows();

#pragma mark - Support classes

Expand Down Expand Up @@ -83,6 +107,7 @@ bool ImGui_ImplMetal_Init(id<MTLDevice> device)
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_metal";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Expand All @@ -91,11 +116,15 @@ bool ImGui_ImplMetal_Init(id<MTLDevice> device)

ImGui_ImplMetal_CreateDeviceObjects(device);

if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplMetal_InitPlatformInterface();

return true;
}

void ImGui_ImplMetal_Shutdown()
{
ImGui_ImplMetal_ShutdownPlatformInterface();
ImGui_ImplMetal_DestroyDeviceObjects();
}

Expand Down Expand Up @@ -133,6 +162,7 @@ bool ImGui_ImplMetal_CreateDeviceObjects(id<MTLDevice> device)
{
[g_sharedMetalContext makeDeviceObjectsWithDevice:device];

ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows();
ImGui_ImplMetal_CreateFontsTexture(device);

return true;
Expand All @@ -141,9 +171,145 @@ bool ImGui_ImplMetal_CreateDeviceObjects(id<MTLDevice> device)
void ImGui_ImplMetal_DestroyDeviceObjects()
{
ImGui_ImplMetal_DestroyFontsTexture();
ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows();

[g_sharedMetalContext emptyRenderPipelineStateCache];
}

//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------

struct ImGuiViewportDataMetal
{
CAMetalLayer* MetalLayer;
id <MTLCommandQueue> CommandQueue;
MTLRenderPassDescriptor* RenderPassDescriptor;
void* Handle;

ImGuiViewportDataMetal() { Handle = NULL; }
~ImGuiViewportDataMetal() {}
};

static void ImGui_ImplMetal_CreateWindow(ImGuiViewport* viewport)
{
ImGuiViewportDataMetal* data = IM_NEW(ImGuiViewportDataMetal)();
viewport->RendererUserData = data;

// PlatformHandleRaw should always be a NSWindow*, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
// Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the NSWindow*.
void* handle = viewport->PlatformHandleRaw ? viewport->PlatformHandleRaw : viewport->PlatformHandle;
IM_ASSERT(handle != 0);

id<MTLDevice> device = [g_sharedMetalContext.depthStencilState device];

CAMetalLayer* layer = [CAMetalLayer layer];
layer.device = device;
layer.framebufferOnly = YES;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
#if TARGET_OS_OSX
NSWindow* window = (__bridge NSWindow*)handle;
NSView* view = nil;
if (view == nil)
view = [window contentView];
if (view == nil)
view = [[window contentViewController] view];
[view setLayer:layer];
[view setWantsLayer:YES];
#endif
data->MetalLayer = layer;
data->CommandQueue = [device newCommandQueue];
data->RenderPassDescriptor = [[MTLRenderPassDescriptor alloc] init];
data->Handle = handle;
}

static void ImGui_ImplMetal_DestroyWindow(ImGuiViewport* viewport)
{
// The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it.
if (ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData)
{
IM_DELETE(data);
}
viewport->RendererUserData = NULL;
}

static void ImGui_ImplMetal_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData;

CGSize drawableSize = CGSizeMake(size.x, size.y);
drawableSize.width *= viewport->DpiScale;
drawableSize.height *= viewport->DpiScale;
data->MetalLayer.drawableSize = drawableSize;
}

static void ImGui_ImplMetal_RenderWindow(ImGuiViewport* viewport, void*)
{
ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData;

#if TARGET_OS_OSX
void* handle = viewport->PlatformHandleRaw ? viewport->PlatformHandleRaw : viewport->PlatformHandle;
NSWindow* window = (__bridge NSWindow*)handle;
viewport->DpiScale = [window backingScaleFactor];
if (data->MetalLayer.contentsScale != viewport->DpiScale)
{
data->MetalLayer.contentsScale = viewport->DpiScale;

CGSize drawableSize = [window frame].size;
drawableSize.width *= viewport->DpiScale;
drawableSize.height *= viewport->DpiScale;
data->MetalLayer.drawableSize = drawableSize;
}
viewport->DrawData->FramebufferScale = ImVec2(viewport->DpiScale, viewport->DpiScale);
#endif

id <CAMetalDrawable> drawable = [data->MetalLayer nextDrawable];

MTLRenderPassDescriptor* renderPassDescriptor = data->RenderPassDescriptor;
renderPassDescriptor.colorAttachments[0].texture = [drawable texture];
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0);

id <MTLCommandBuffer> commandBuffer = [data->CommandQueue commandBuffer];
id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
ImGui_ImplMetal_RenderDrawData(viewport->DrawData, commandBuffer, renderEncoder);
[renderEncoder endEncoding];

[commandBuffer presentDrawable:drawable];
[commandBuffer commit];
}

static void ImGui_ImplMetal_InitPlatformInterface()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_CreateWindow = ImGui_ImplMetal_CreateWindow;
platform_io.Renderer_DestroyWindow = ImGui_ImplMetal_DestroyWindow;
platform_io.Renderer_SetWindowSize = ImGui_ImplMetal_SetWindowSize;
platform_io.Renderer_RenderWindow = ImGui_ImplMetal_RenderWindow;
}

static void ImGui_ImplMetal_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}

static void ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
for (int i = 1; i < platform_io.Viewports.Size; i++)
if (!platform_io.Viewports[i]->RendererUserData)
ImGui_ImplMetal_CreateWindow(platform_io.Viewports[i]);
}

static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
for (int i = 1; i < platform_io.Viewports.Size; i++)
if (platform_io.Viewports[i]->RendererUserData)
ImGui_ImplMetal_DestroyWindow(platform_io.Viewports[i]);
}

#pragma mark - MetalBuffer implementation

@implementation MetalBuffer
Expand Down Expand Up @@ -522,7 +688,6 @@ - (void)renderDrawData:(ImDrawData *)drawData
};
[commandEncoder setScissorRect:scissorRect];


// Bind texture, Draw
if (pcmd->TextureId != NULL)
[commandEncoder setFragmentTexture:(__bridge id<MTLTexture>)(pcmd->TextureId) atIndex:0];
Expand Down
6 changes: 4 additions & 2 deletions backends/imgui_impl_osx.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
// [X] Platform: Multi-viewport / platform windows.
// Issues:
// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters]..
// [ ] Platform: Multi-viewport / platform windows.

// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
Expand All @@ -17,8 +17,10 @@

@class NSEvent;
@class NSView;
@class NSViewController;

IMGUI_IMPL_API bool ImGui_ImplOSX_Init();
IMGUI_IMPL_API bool ImGui_ImplOSX_Init(NSView* _Nullable view);
IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view);
IMGUI_IMPL_API bool ImGui_ImplOSX_HandleEvent(NSEvent* _Nonnull event, NSView* _Nullable view);
IMGUI_IMPL_API void ImGui_ImplOSX_AddTrackingArea(NSViewController* _Nonnull controller);
Loading