1// This file is part of Desktop App Toolkit, 2// a set of libraries for developing nice desktop applications. 3// 4// For license and copyright information please follow this link: 5// https://github.com/desktop-app/legal/blob/master/LEGAL 6// 7#include "ui/platform/mac/ui_utility_mac.h" 8 9#include "ui/integration.h" 10 11#include <QtGui/QPainter> 12#include <QtGui/QtEvents> 13#include <QtGui/QWindow> 14 15#include <Cocoa/Cocoa.h> 16 17#ifndef OS_MAC_STORE 18extern "C" { 19void _dispatch_main_queue_callback_4CF(mach_msg_header_t *msg); 20} // extern "C" 21#endif // OS_MAC_STORE 22 23namespace Ui { 24namespace Platform { 25 26bool IsApplicationActive() { 27 return [[NSApplication sharedApplication] isActive]; 28} 29 30void InitOnTopPanel(not_null<QWidget*> panel) { 31 Expects(!panel->windowHandle()); 32 33 // Force creating windowHandle() without creating the platform window yet. 34 panel->setAttribute(Qt::WA_NativeWindow, true); 35 panel->windowHandle()->setProperty("_td_macNonactivatingPanelMask", QVariant(true)); 36 panel->setAttribute(Qt::WA_NativeWindow, false); 37 38 panel->createWinId(); 39 40 auto platformWindow = [reinterpret_cast<NSView*>(panel->winId()) window]; 41 Assert([platformWindow isKindOfClass:[NSPanel class]]); 42 43 auto platformPanel = static_cast<NSPanel*>(platformWindow); 44 [platformPanel setBackgroundColor:[NSColor clearColor]]; 45 [platformPanel setLevel:NSModalPanelWindowLevel]; 46 [platformPanel setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorStationary|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; 47 [platformPanel setHidesOnDeactivate:NO]; 48 //[platformPanel setFloatingPanel:YES]; 49 50 Integration::Instance().activationFromTopPanel(); 51} 52 53void DeInitOnTopPanel(not_null<QWidget*> panel) { 54 auto platformWindow = [reinterpret_cast<NSView*>(panel->winId()) window]; 55 Assert([platformWindow isKindOfClass:[NSPanel class]]); 56 57 auto platformPanel = static_cast<NSPanel*>(platformWindow); 58 auto newBehavior = ([platformPanel collectionBehavior] & (~NSWindowCollectionBehaviorCanJoinAllSpaces)) | NSWindowCollectionBehaviorMoveToActiveSpace; 59 [platformPanel setCollectionBehavior:newBehavior]; 60} 61 62void ReInitOnTopPanel(not_null<QWidget*> panel) { 63 auto platformWindow = [reinterpret_cast<NSView*>(panel->winId()) window]; 64 Assert([platformWindow isKindOfClass:[NSPanel class]]); 65 66 auto platformPanel = static_cast<NSPanel*>(platformWindow); 67 auto newBehavior = ([platformPanel collectionBehavior] & (~NSWindowCollectionBehaviorMoveToActiveSpace)) | NSWindowCollectionBehaviorCanJoinAllSpaces; 68 [platformPanel setCollectionBehavior:newBehavior]; 69} 70 71void ShowOverAll(not_null<QWidget*> widget, bool canFocus) { 72 NSWindow *wnd = [reinterpret_cast<NSView*>(widget->winId()) window]; 73 [wnd setLevel:NSPopUpMenuWindowLevel]; 74 if (!canFocus) { 75 [wnd setStyleMask:NSUtilityWindowMask | NSNonactivatingPanelMask]; 76 [wnd setCollectionBehavior:NSWindowCollectionBehaviorMoveToActiveSpace|NSWindowCollectionBehaviorStationary|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; 77 } 78} 79 80void BringToBack(not_null<QWidget*> widget) { 81 NSWindow *wnd = [reinterpret_cast<NSView*>(widget->winId()) window]; 82 [wnd setLevel:NSModalPanelWindowLevel]; 83} 84 85void DrainMainQueue() { 86#ifndef OS_MAC_STORE 87 _dispatch_main_queue_callback_4CF(nullptr); 88#endif // OS_MAC_STORE 89} 90 91void IgnoreAllActivation(not_null<QWidget*> widget) { 92} 93 94std::optional<bool> IsOverlapped( 95 not_null<QWidget*> widget, 96 const QRect &rect) { 97 NSWindow *window = [reinterpret_cast<NSView*>(widget->window()->winId()) window]; 98 Assert(window != nullptr); 99 100 if (![window isOnActiveSpace]) { 101 return true; 102 } 103 104 const auto nativeRect = CGRectMake( 105 rect.x(), 106 rect.y(), 107 rect.width(), 108 rect.height()); 109 110 CGWindowID windowId = (CGWindowID)[window windowNumber]; 111 const CGWindowListOption options = kCGWindowListExcludeDesktopElements 112 | kCGWindowListOptionOnScreenAboveWindow; 113 CFArrayRef windows = CGWindowListCopyWindowInfo(options, windowId); 114 if (!windows) { 115 return std::nullopt; 116 } 117 const auto guard = gsl::finally([&] { 118 CFRelease(windows); 119 }); 120 NSMutableArray *list = (__bridge NSMutableArray*)windows; 121 for (NSDictionary *window in list) { 122 NSNumber *alphaValue = [window objectForKey:@"kCGWindowAlpha"]; 123 const auto alpha = alphaValue ? [alphaValue doubleValue] : 1.; 124 if (alpha == 0.) { 125 continue; 126 } 127 NSString *owner = [window objectForKey:@"kCGWindowOwnerName"]; 128 NSNumber *layerValue = [window objectForKey:@"kCGWindowLayer"]; 129 const auto layer = layerValue ? [layerValue intValue] : 0; 130 if (owner && [owner isEqualToString:@"Dock"] && layer == 20) { 131 // It is always full screen. 132 continue; 133 } 134 CFDictionaryRef bounds = (__bridge CFDictionaryRef)[window objectForKey:@"kCGWindowBounds"]; 135 if (!bounds) { 136 continue; 137 } 138 CGRect rect; 139 if (!CGRectMakeWithDictionaryRepresentation(bounds, &rect)) { 140 continue; 141 } else if (CGRectIntersectsRect(rect, nativeRect)) { 142 return true; 143 } 144 } 145 return false; 146} 147 148TitleControls::Layout TitleControlsLayout() { 149 return TitleControls::Layout{ 150 .left = { 151 TitleControls::Control::Close, 152 TitleControls::Control::Minimize, 153 TitleControls::Control::Maximize, 154 } 155 }; 156} 157 158} // namespace Platform 159} // namespace Ui 160