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 9 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
27 changes: 27 additions & 0 deletions examples/example_apple_metal/Shared/Renderer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,29 @@ -(nonnull instancetype)initWithView:(nonnull MTKView *)view;

IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows

// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();

// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
ImGuiStyle& style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}

// Setup Platform/Renderer bindings
ImGui_ImplMetal_Init(_device);
#if TARGET_OS_OSX
ImGui_ImplOSX_Init(view);
#endif
}

return self;
Expand Down Expand Up @@ -120,6 +140,13 @@ - (void)drawInMTKView:(MTKView *)view
}

[commandBuffer commit];

// Update and Render additional Platform Windows
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
}

- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size
Expand Down
2 changes: 0 additions & 2 deletions examples/example_apple_metal/Shared/ViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ - (void)viewDidLoad
}

}];

ImGui_ImplOSX_Init();
#endif
}

Expand Down
28 changes: 26 additions & 2 deletions examples/example_apple_opengl2/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ -(void)updateAndDrawDemoView
// Present
[[self openGLContext] flushBuffer];

// Update and Render additional Platform Windows
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}

if (!animationTimer)
animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.017 target:self selector:@selector(animationTimerFired:) userInfo:nil repeats:YES];
}
Expand Down Expand Up @@ -154,7 +162,7 @@ -(void)scrollWheel:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self)
// ImGuiExampleAppDelegate
//-----------------------------------------------------------------------------------

@interface ImGuiExampleAppDelegate : NSObject <NSApplicationDelegate>
@interface ImGuiExampleAppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
@property (nonatomic, readonly) NSWindow* window;
@end

Expand All @@ -178,10 +186,17 @@ -(NSWindow*)window
[_window setAcceptsMouseMovedEvents:YES];
[_window setOpaque:YES];
[_window makeKeyAndOrderFront:NSApp];
[_window setDelegate:self];
[_window setAcceptsMouseMovedEvents:YES];

return (_window);
}

-(void)windowWillClose:(NSNotification *)notification
{
[NSApp terminate:self];
}

-(void)setupMenu
{
NSMenu* mainMenuBar = [[NSMenu alloc] init];
Expand Down Expand Up @@ -239,14 +254,16 @@ -(void)applicationDidFinishLaunching:(NSNotification *)aNotification
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
//io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows

// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();

// Setup Platform/Renderer bindings
ImGui_ImplOSX_Init();
ImGui_ImplOSX_Init(view);
ImGui_ImplOpenGL2_Init();

// Load Fonts
Expand All @@ -265,6 +282,13 @@ -(void)applicationDidFinishLaunching:(NSNotification *)aNotification
//IM_ASSERT(font != NULL);
}

-(void)applicationWillTerminate:(NSNotification *)notification;
{
ImGui_ImplOpenGL2_Shutdown();
ImGui_ImplOSX_Shutdown();
ImGui::DestroyContext();
}

@end

int main(int argc, const char* argv[])
Expand Down
149 changes: 145 additions & 4 deletions examples/imgui_impl_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
// 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-bits 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 main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui

// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 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 @@ -23,8 +23,17 @@
#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 ...)
#import <QuartzCore/CAMetalLayer.h>
#import <simd/simd.h>
metarutaiga marked this conversation as resolved.
Show resolved Hide resolved
#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 All @@ -49,6 +58,7 @@ - (instancetype)initWithRenderPassDescriptor:(MTLRenderPassDescriptor *)renderPa
// renderer backend. Stores the render pipeline state cache and the default
// font texture, and manages the reusable buffer cache.
@interface MetalContext : NSObject
@property (nonatomic, strong) id<MTLDevice> device;
@property (nonatomic, strong) id<MTLDepthStencilState> depthStencilState;
@property (nonatomic, strong) FramebufferDescriptor *framebufferDescriptor; // framebuffer descriptor for current frame; transient
@property (nonatomic, strong) NSMutableDictionary *renderPipelineStateCache; // pipeline cache; keyed on framebuffer descriptors
Expand Down Expand Up @@ -81,6 +91,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 @@ -89,11 +100,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 @@ -124,13 +139,15 @@ void ImGui_ImplMetal_DestroyFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
g_sharedMetalContext.fontTexture = nil;
io.Fonts->TexID = nullptr;
io.Fonts->TexID = NULL;
}

bool ImGui_ImplMetal_CreateDeviceObjects(id<MTLDevice> device)
{
[g_sharedMetalContext makeDeviceObjectsWithDevice:device];
g_sharedMetalContext.device = device;

ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows();
ImGui_ImplMetal_CreateFontsTexture(device);

return true;
Expand All @@ -139,9 +156,133 @@ 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.device;

float contentsScale = 1.0f;
#if TARGET_OS_OSX
NSWindow* window = (__bridge NSWindow*)handle;
contentsScale = [window backingScaleFactor];
#endif
CAMetalLayer* layer = [CAMetalLayer layer];
layer.contentsScale = contentsScale;
layer.device = device;
layer.framebufferOnly = YES;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
#if TARGET_OS_OSX
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;

size.x *= data->MetalLayer.contentsScale;
size.y *= data->MetalLayer.contentsScale;
data->MetalLayer.drawableSize = CGSizeMake(size.x, size.y);
}

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

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
2 changes: 1 addition & 1 deletion examples/imgui_impl_osx.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@class NSEvent;
@class NSView;

IMGUI_API bool ImGui_ImplOSX_Init();
IMGUI_API bool ImGui_ImplOSX_Init(NSView *_Nonnull view);
IMGUI_API void ImGui_ImplOSX_Shutdown();
IMGUI_API void ImGui_ImplOSX_NewFrame(NSView *_Nullable view);
IMGUI_API bool ImGui_ImplOSX_HandleEvent(NSEvent *_Nonnull event, NSView *_Nullable view);
Loading