1// Copyright 2015 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "chrome/browser/ui/views/apps/chrome_native_app_window_views_mac.h" 6 7#import <Cocoa/Cocoa.h> 8 9#import "base/mac/scoped_nsobject.h" 10#include "chrome/browser/apps/app_shim/app_shim_manager_mac.h" 11#include "chrome/browser/profiles/profile.h" 12#import "chrome/browser/ui/views/apps/app_window_native_widget_mac.h" 13#import "chrome/browser/ui/views/apps/native_app_window_frame_view_mac.h" 14#import "ui/gfx/mac/coordinate_conversion.h" 15 16// This observer is used to get NSWindow notifications. We need to monitor 17// zoom and full screen events to store the correct bounds to Restore() to. 18@interface ResizeNotificationObserver : NSObject { 19 @private 20 // Weak. Owns us. 21 ChromeNativeAppWindowViewsMac* _nativeAppWindow; 22} 23- (id)initForNativeAppWindow:(ChromeNativeAppWindowViewsMac*)nativeAppWindow; 24- (void)onWindowWillStartLiveResize:(NSNotification*)notification; 25- (void)onWindowWillExitFullScreen:(NSNotification*)notification; 26- (void)onWindowDidExitFullScreen:(NSNotification*)notification; 27- (void)stopObserving; 28@end 29 30@implementation ResizeNotificationObserver 31 32- (id)initForNativeAppWindow:(ChromeNativeAppWindowViewsMac*)nativeAppWindow { 33 if ((self = [super init])) { 34 _nativeAppWindow = nativeAppWindow; 35 [[NSNotificationCenter defaultCenter] 36 addObserver:self 37 selector:@selector(onWindowWillStartLiveResize:) 38 name:NSWindowWillStartLiveResizeNotification 39 object:static_cast<ui::BaseWindow*>(nativeAppWindow) 40 ->GetNativeWindow() 41 .GetNativeNSWindow()]; 42 [[NSNotificationCenter defaultCenter] 43 addObserver:self 44 selector:@selector(onWindowWillExitFullScreen:) 45 name:NSWindowWillExitFullScreenNotification 46 object:static_cast<ui::BaseWindow*>(nativeAppWindow) 47 ->GetNativeWindow() 48 .GetNativeNSWindow()]; 49 [[NSNotificationCenter defaultCenter] 50 addObserver:self 51 selector:@selector(onWindowDidExitFullScreen:) 52 name:NSWindowDidExitFullScreenNotification 53 object:static_cast<ui::BaseWindow*>(nativeAppWindow) 54 ->GetNativeWindow() 55 .GetNativeNSWindow()]; 56 } 57 return self; 58} 59 60- (void)onWindowWillStartLiveResize:(NSNotification*)notification { 61 _nativeAppWindow->OnWindowWillStartLiveResize(); 62} 63 64- (void)onWindowWillExitFullScreen:(NSNotification*)notification { 65 _nativeAppWindow->OnWindowWillExitFullScreen(); 66} 67 68- (void)onWindowDidExitFullScreen:(NSNotification*)notification { 69 _nativeAppWindow->OnWindowDidExitFullScreen(); 70} 71 72- (void)stopObserving { 73 [[NSNotificationCenter defaultCenter] removeObserver:self]; 74 _nativeAppWindow = nullptr; 75} 76 77@end 78 79namespace { 80 81bool NSWindowIsMaximized(NSWindow* window) { 82 // -[NSWindow isZoomed] only works if the zoom button is enabled. 83 if ([[window standardWindowButton:NSWindowZoomButton] isEnabled]) 84 return [window isZoomed]; 85 86 // We don't attempt to distinguish between a window that has been explicitly 87 // maximized versus one that has just been dragged by the user to fill the 88 // screen. This is the same behavior as -[NSWindow isZoomed] above. 89 return NSEqualRects([window frame], [[window screen] visibleFrame]); 90} 91 92} // namespace 93 94ChromeNativeAppWindowViewsMac::ChromeNativeAppWindowViewsMac() {} 95 96ChromeNativeAppWindowViewsMac::~ChromeNativeAppWindowViewsMac() { 97 [nswindow_observer_ stopObserving]; 98} 99 100void ChromeNativeAppWindowViewsMac::OnWindowWillStartLiveResize() { 101 if (!NSWindowIsMaximized(GetNativeWindow().GetNativeNSWindow()) && 102 !in_fullscreen_transition_) { 103 bounds_before_maximize_ = [GetNativeWindow().GetNativeNSWindow() frame]; 104 } 105} 106 107void ChromeNativeAppWindowViewsMac::OnWindowWillExitFullScreen() { 108 in_fullscreen_transition_ = true; 109} 110 111void ChromeNativeAppWindowViewsMac::OnWindowDidExitFullScreen() { 112 in_fullscreen_transition_ = false; 113} 114 115void ChromeNativeAppWindowViewsMac::OnBeforeWidgetInit( 116 const extensions::AppWindow::CreateParams& create_params, 117 views::Widget::InitParams* init_params, 118 views::Widget* widget) { 119 DCHECK(!init_params->native_widget); 120 init_params->remove_standard_frame = IsFrameless(); 121 init_params->native_widget = new AppWindowNativeWidgetMac(widget, this); 122 ChromeNativeAppWindowViews::OnBeforeWidgetInit(create_params, init_params, 123 widget); 124} 125 126std::unique_ptr<views::NonClientFrameView> 127ChromeNativeAppWindowViewsMac::CreateStandardDesktopAppFrame() { 128 return std::make_unique<NativeAppWindowFrameViewMac>(widget(), this); 129} 130 131std::unique_ptr<views::NonClientFrameView> 132ChromeNativeAppWindowViewsMac::CreateNonStandardAppFrame() { 133 return std::make_unique<NativeAppWindowFrameViewMac>(widget(), this); 134} 135 136bool ChromeNativeAppWindowViewsMac::IsMaximized() const { 137 return !IsMinimized() && !IsFullscreen() && 138 NSWindowIsMaximized(GetNativeWindow().GetNativeNSWindow()); 139} 140 141gfx::Rect ChromeNativeAppWindowViewsMac::GetRestoredBounds() const { 142 if (NSWindowIsMaximized(GetNativeWindow().GetNativeNSWindow())) 143 return gfx::ScreenRectFromNSRect(bounds_before_maximize_); 144 145 return ChromeNativeAppWindowViews::GetRestoredBounds(); 146} 147 148void ChromeNativeAppWindowViewsMac::Maximize() { 149 if (IsFullscreen()) 150 return; 151 152 NSWindow* window = GetNativeWindow().GetNativeNSWindow(); 153 if (!NSWindowIsMaximized(window)) 154 [window setFrame:[[window screen] visibleFrame] display:YES animate:YES]; 155 156 if (IsMinimized()) 157 [window deminiaturize:nil]; 158} 159 160void ChromeNativeAppWindowViewsMac::Restore() { 161 NSWindow* window = GetNativeWindow().GetNativeNSWindow(); 162 if (NSWindowIsMaximized(window)) 163 [window setFrame:bounds_before_maximize_ display:YES animate:YES]; 164 165 ChromeNativeAppWindowViews::Restore(); 166} 167 168void ChromeNativeAppWindowViewsMac::FlashFrame(bool flash) { 169 Profile* profile = 170 Profile::FromBrowserContext(app_window()->browser_context()); 171 AppShimHost* shim_host = apps::AppShimManager::Get()->FindHost( 172 profile, app_window()->extension_id()); 173 if (!shim_host) 174 return; 175 shim_host->GetAppShim()->SetUserAttention( 176 flash ? chrome::mojom::AppShimAttentionType::kCritical 177 : chrome::mojom::AppShimAttentionType::kCancel); 178} 179 180void ChromeNativeAppWindowViewsMac::OnWidgetCreated(views::Widget* widget) { 181 nswindow_observer_.reset( 182 [[ResizeNotificationObserver alloc] initForNativeAppWindow:this]); 183} 184