native_window_mac.mm 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. // Copyright (c) 2013 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/native_window_mac.h"
  5. #include <Quartz/Quartz.h>
  6. #include <string>
  7. #include "atom/browser/window_list.h"
  8. #include "atom/common/color_util.h"
  9. #include "atom/common/draggable_region.h"
  10. #include "atom/common/options_switches.h"
  11. #include "base/mac/mac_util.h"
  12. #include "base/mac/scoped_cftyperef.h"
  13. #include "base/strings/sys_string_conversions.h"
  14. #include "brightray/browser/inspectable_web_contents.h"
  15. #include "brightray/browser/inspectable_web_contents_view.h"
  16. #include "brightray/browser/mac/event_dispatching_window.h"
  17. #include "content/public/browser/browser_accessibility_state.h"
  18. #include "content/public/browser/web_contents.h"
  19. #include "content/public/browser/render_view_host.h"
  20. #include "content/public/browser/render_widget_host_view.h"
  21. #include "native_mate/dictionary.h"
  22. #include "skia/ext/skia_utils_mac.h"
  23. #include "third_party/skia/include/core/SkRegion.h"
  24. #include "ui/gfx/skia_util.h"
  25. namespace {
  26. // Prevents window from resizing during the scope.
  27. class ScopedDisableResize {
  28. public:
  29. ScopedDisableResize() { disable_resize_ = true; }
  30. ~ScopedDisableResize() { disable_resize_ = false; }
  31. static bool IsResizeDisabled() { return disable_resize_; }
  32. private:
  33. static bool disable_resize_;
  34. };
  35. bool ScopedDisableResize::disable_resize_ = false;
  36. } // namespace
  37. // This view always takes the size of its superview. It is intended to be used
  38. // as a NSWindow's contentView. It is needed because NSWindow's implementation
  39. // explicitly resizes the contentView at inopportune times.
  40. @interface FullSizeContentView : NSView
  41. @end
  42. @implementation FullSizeContentView
  43. // This method is directly called by NSWindow during a window resize on OSX
  44. // 10.10.0, beta 2. We must override it to prevent the content view from
  45. // shrinking.
  46. - (void)setFrameSize:(NSSize)size {
  47. if ([self superview])
  48. size = [[self superview] bounds].size;
  49. [super setFrameSize:size];
  50. }
  51. // The contentView gets moved around during certain full-screen operations.
  52. // This is less than ideal, and should eventually be removed.
  53. - (void)viewDidMoveToSuperview {
  54. [self setFrame:[[self superview] bounds]];
  55. }
  56. @end
  57. @interface AtomNSWindowDelegate : NSObject<NSWindowDelegate> {
  58. @private
  59. atom::NativeWindowMac* shell_;
  60. bool is_zooming_;
  61. }
  62. - (id)initWithShell:(atom::NativeWindowMac*)shell;
  63. @end
  64. @implementation AtomNSWindowDelegate
  65. - (id)initWithShell:(atom::NativeWindowMac*)shell {
  66. if ((self = [super init])) {
  67. shell_ = shell;
  68. is_zooming_ = false;
  69. }
  70. return self;
  71. }
  72. - (void)windowDidChangeOcclusionState:(NSNotification *)notification {
  73. // notification.object is the window that changed its state.
  74. // It's safe to use self.window instead if you don't assign one delegate to many windows
  75. NSWindow *window = notification.object;
  76. // check occlusion binary flag
  77. if (window.occlusionState & NSWindowOcclusionStateVisible) {
  78. // The app is visible
  79. shell_->NotifyWindowShow();
  80. } else {
  81. // The app is not visible
  82. shell_->NotifyWindowHide();
  83. }
  84. }
  85. // Called when the user clicks the zoom button or selects it from the Window
  86. // menu to determine the "standard size" of the window.
  87. - (NSRect)windowWillUseStandardFrame:(NSWindow*)window
  88. defaultFrame:(NSRect)frame {
  89. if (!shell_->zoom_to_page_width())
  90. return frame;
  91. // If the shift key is down, maximize.
  92. if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask)
  93. return frame;
  94. content::WebContents* web_contents = shell_->web_contents();
  95. if (!web_contents)
  96. return frame;
  97. CGFloat page_width = static_cast<CGFloat>(
  98. web_contents->GetPreferredSize().width());
  99. NSRect window_frame = [window frame];
  100. // Never shrink from the current size on zoom.
  101. CGFloat zoomed_width = std::max(page_width, NSWidth(window_frame));
  102. // |frame| determines our maximum extents. We need to set the origin of the
  103. // frame -- and only move it left if necessary.
  104. if (window_frame.origin.x + zoomed_width > NSMaxX(frame))
  105. frame.origin.x = NSMaxX(frame) - zoomed_width;
  106. else
  107. frame.origin.x = window_frame.origin.x;
  108. // Set the width. Don't touch y or height.
  109. frame.size.width = zoomed_width;
  110. return frame;
  111. }
  112. - (void)windowDidBecomeMain:(NSNotification*)notification {
  113. content::WebContents* web_contents = shell_->web_contents();
  114. if (!web_contents)
  115. return;
  116. web_contents->RestoreFocus();
  117. content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
  118. if (rwhv)
  119. rwhv->SetActive(true);
  120. shell_->NotifyWindowFocus();
  121. }
  122. - (void)windowDidResignMain:(NSNotification*)notification {
  123. content::WebContents* web_contents = shell_->web_contents();
  124. if (!web_contents)
  125. return;
  126. web_contents->StoreFocus();
  127. content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
  128. if (rwhv)
  129. rwhv->SetActive(false);
  130. shell_->NotifyWindowBlur();
  131. }
  132. - (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize {
  133. NSSize newSize = frameSize;
  134. double aspectRatio = shell_->GetAspectRatio();
  135. if (aspectRatio > 0.0) {
  136. gfx::Size windowSize = shell_->GetSize();
  137. gfx::Size contentSize = shell_->GetContentSize();
  138. gfx::Size extraSize = shell_->GetAspectRatioExtraSize();
  139. double extraWidthPlusFrame =
  140. windowSize.width() - contentSize.width() + extraSize.width();
  141. double extraHeightPlusFrame =
  142. windowSize.height() - contentSize.height() + extraSize.height();
  143. newSize.width =
  144. roundf((frameSize.height - extraHeightPlusFrame) * aspectRatio +
  145. extraWidthPlusFrame);
  146. newSize.height =
  147. roundf((newSize.width - extraWidthPlusFrame) / aspectRatio +
  148. extraHeightPlusFrame);
  149. }
  150. return newSize;
  151. }
  152. - (void)windowDidResize:(NSNotification*)notification {
  153. shell_->UpdateDraggableRegionViews();
  154. shell_->NotifyWindowResize();
  155. }
  156. - (void)windowDidMove:(NSNotification*)notification {
  157. // TODO(zcbenz): Remove the alias after figuring out a proper
  158. // way to dispatch move.
  159. shell_->NotifyWindowMove();
  160. shell_->NotifyWindowMoved();
  161. }
  162. - (void)windowDidMiniaturize:(NSNotification*)notification {
  163. shell_->NotifyWindowMinimize();
  164. }
  165. - (void)windowDidDeminiaturize:(NSNotification*)notification {
  166. shell_->NotifyWindowRestore();
  167. }
  168. - (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame {
  169. is_zooming_ = true;
  170. return YES;
  171. }
  172. - (void)windowDidEndLiveResize:(NSNotification*)notification {
  173. if (is_zooming_) {
  174. if (shell_->IsMaximized())
  175. shell_->NotifyWindowMaximize();
  176. else
  177. shell_->NotifyWindowUnmaximize();
  178. is_zooming_ = false;
  179. }
  180. }
  181. - (void)windowWillEnterFullScreen:(NSNotification*)notification {
  182. // Hide the native toolbar before entering fullscreen, so there is no visual
  183. // artifacts.
  184. if (base::mac::IsOSYosemiteOrLater() &&
  185. shell_->title_bar_style() == atom::NativeWindowMac::HIDDEN_INSET) {
  186. NSWindow* window = shell_->GetNativeWindow();
  187. [window setToolbar:nil];
  188. }
  189. }
  190. - (void)windowDidEnterFullScreen:(NSNotification*)notification {
  191. shell_->NotifyWindowEnterFullScreen();
  192. // For frameless window we don't show set title for normal mode since the
  193. // titlebar is expected to be empty, but after entering fullscreen mode we
  194. // have to set one, because title bar is visible here.
  195. NSWindow* window = shell_->GetNativeWindow();
  196. if ((shell_->transparent() || !shell_->has_frame()) &&
  197. base::mac::IsOSYosemiteOrLater() &&
  198. // FIXME(zcbenz): Showing titlebar for hiddenInset window is weird under
  199. // fullscreen mode.
  200. shell_->title_bar_style() != atom::NativeWindowMac::HIDDEN_INSET) {
  201. [window setTitleVisibility:NSWindowTitleVisible];
  202. }
  203. // Restore the native toolbar immediately after entering fullscreen, if we do
  204. // this before leaving fullscreen, traffic light buttons will be jumping.
  205. if (base::mac::IsOSYosemiteOrLater() &&
  206. shell_->title_bar_style() == atom::NativeWindowMac::HIDDEN_INSET) {
  207. base::scoped_nsobject<NSToolbar> toolbar(
  208. [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
  209. [toolbar setShowsBaselineSeparator:NO];
  210. [window setToolbar:toolbar];
  211. // Set window style to hide the toolbar, otherwise the toolbar will show in
  212. // fullscreen mode.
  213. shell_->SetStyleMask(true, NSFullSizeContentViewWindowMask);
  214. }
  215. }
  216. - (void)windowWillExitFullScreen:(NSNotification*)notification {
  217. // Restore the titlebar visibility.
  218. NSWindow* window = shell_->GetNativeWindow();
  219. if ((shell_->transparent() || !shell_->has_frame()) &&
  220. base::mac::IsOSYosemiteOrLater() &&
  221. shell_->title_bar_style() != atom::NativeWindowMac::HIDDEN_INSET) {
  222. [window setTitleVisibility:NSWindowTitleHidden];
  223. }
  224. // Turn off the style for toolbar.
  225. if (base::mac::IsOSYosemiteOrLater() &&
  226. shell_->title_bar_style() == atom::NativeWindowMac::HIDDEN_INSET) {
  227. shell_->SetStyleMask(false, NSFullSizeContentViewWindowMask);
  228. }
  229. }
  230. - (void)windowDidExitFullScreen:(NSNotification*)notification {
  231. shell_->NotifyWindowLeaveFullScreen();
  232. }
  233. - (void)windowWillClose:(NSNotification*)notification {
  234. shell_->NotifyWindowClosed();
  235. // Clears the delegate when window is going to be closed, since EL Capitan it
  236. // is possible that the methods of delegate would get called after the window
  237. // has been closed.
  238. [shell_->GetNativeWindow() setDelegate:nil];
  239. }
  240. - (BOOL)windowShouldClose:(id)window {
  241. // When user tries to close the window by clicking the close button, we do
  242. // not close the window immediately, instead we try to close the web page
  243. // fisrt, and when the web page is closed the window will also be closed.
  244. shell_->RequestToClosePage();
  245. return NO;
  246. }
  247. - (NSRect)window:(NSWindow*)window
  248. willPositionSheet:(NSWindow*)sheet usingRect:(NSRect)rect {
  249. NSView* view = window.contentView;
  250. rect.origin.x = shell_->GetSheetOffsetX();
  251. rect.origin.y = view.frame.size.height - shell_->GetSheetOffsetY();
  252. return rect;
  253. }
  254. @end
  255. @interface AtomPreviewItem : NSObject <QLPreviewItem>
  256. @property (nonatomic, retain) NSURL* previewItemURL;
  257. @property (nonatomic, retain) NSString* previewItemTitle;
  258. - (id)initWithURL:(NSURL*)url title:(NSString*)title;
  259. @end
  260. @implementation AtomPreviewItem
  261. - (id)initWithURL:(NSURL*)url title:(NSString*)title {
  262. self = [super init];
  263. if (self) {
  264. self.previewItemURL = url;
  265. self.previewItemTitle = title;
  266. }
  267. return self;
  268. }
  269. @end
  270. @interface AtomNSWindow : EventDispatchingWindow<QLPreviewPanelDataSource, QLPreviewPanelDelegate> {
  271. @private
  272. atom::NativeWindowMac* shell_;
  273. bool enable_larger_than_screen_;
  274. CGFloat windowButtonsInterButtonSpacing_;
  275. }
  276. @property BOOL acceptsFirstMouse;
  277. @property BOOL disableAutoHideCursor;
  278. @property BOOL disableKeyOrMainWindow;
  279. @property NSPoint windowButtonsOffset;
  280. @property (nonatomic, retain) AtomPreviewItem* quickLookItem;
  281. @property (nonatomic, retain) NSView* vibrantView;
  282. - (void)setShell:(atom::NativeWindowMac*)shell;
  283. - (void)setEnableLargerThanScreen:(bool)enable;
  284. - (void)enableWindowButtonsOffset;
  285. @end
  286. @implementation AtomNSWindow
  287. - (void)setShell:(atom::NativeWindowMac*)shell {
  288. shell_ = shell;
  289. }
  290. - (void)setEnableLargerThanScreen:(bool)enable {
  291. enable_larger_than_screen_ = enable;
  292. }
  293. // NSWindow overrides.
  294. - (void)swipeWithEvent:(NSEvent *)event {
  295. if (event.deltaY == 1.0) {
  296. shell_->NotifyWindowSwipe("up");
  297. } else if (event.deltaX == -1.0) {
  298. shell_->NotifyWindowSwipe("right");
  299. } else if (event.deltaY == -1.0) {
  300. shell_->NotifyWindowSwipe("down");
  301. } else if (event.deltaX == 1.0) {
  302. shell_->NotifyWindowSwipe("left");
  303. }
  304. }
  305. - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen*)screen {
  306. // Resizing is disabled.
  307. if (ScopedDisableResize::IsResizeDisabled())
  308. return [self frame];
  309. // Enable the window to be larger than screen.
  310. if (enable_larger_than_screen_)
  311. return frameRect;
  312. else
  313. return [super constrainFrameRect:frameRect toScreen:screen];
  314. }
  315. - (void)setFrame:(NSRect)windowFrame display:(BOOL)displayViews {
  316. // constrainFrameRect is not called on hidden windows so disable adjusting
  317. // the frame directly when resize is disabled
  318. if (!ScopedDisableResize::IsResizeDisabled())
  319. [super setFrame:windowFrame display:displayViews];
  320. }
  321. - (id)accessibilityAttributeValue:(NSString*)attribute {
  322. if (![attribute isEqualToString:@"AXChildren"])
  323. return [super accessibilityAttributeValue:attribute];
  324. // Filter out objects that aren't the title bar buttons. This has the effect
  325. // of removing the window title, which VoiceOver already sees.
  326. // * when VoiceOver is disabled, this causes Cmd+C to be used for TTS but
  327. // still leaves the buttons available in the accessibility tree.
  328. // * when VoiceOver is enabled, the full accessibility tree is used.
  329. // Without removing the title and with VO disabled, the TTS would always read
  330. // the window title instead of using Cmd+C to get the selected text.
  331. NSPredicate *predicate = [NSPredicate predicateWithFormat:
  332. @"(self isKindOfClass: %@) OR (self.className == %@)",
  333. [NSButtonCell class],
  334. @"RenderWidgetHostViewCocoa"];
  335. NSArray *children = [super accessibilityAttributeValue:attribute];
  336. return [children filteredArrayUsingPredicate:predicate];
  337. }
  338. - (BOOL)canBecomeMainWindow {
  339. return !self.disableKeyOrMainWindow;
  340. }
  341. - (BOOL)canBecomeKeyWindow {
  342. return !self.disableKeyOrMainWindow;
  343. }
  344. - (void)enableWindowButtonsOffset {
  345. auto closeButton = [self standardWindowButton:NSWindowCloseButton];
  346. auto miniaturizeButton = [self standardWindowButton:NSWindowMiniaturizeButton];
  347. auto zoomButton = [self standardWindowButton:NSWindowZoomButton];
  348. [closeButton setPostsFrameChangedNotifications:YES];
  349. [miniaturizeButton setPostsFrameChangedNotifications:YES];
  350. [zoomButton setPostsFrameChangedNotifications:YES];
  351. windowButtonsInterButtonSpacing_ =
  352. NSMinX([miniaturizeButton frame]) - NSMaxX([closeButton frame]);
  353. auto center = [NSNotificationCenter defaultCenter];
  354. [center addObserver:self
  355. selector:@selector(adjustCloseButton:)
  356. name:NSViewFrameDidChangeNotification
  357. object:closeButton];
  358. [center addObserver:self
  359. selector:@selector(adjustMiniaturizeButton:)
  360. name:NSViewFrameDidChangeNotification
  361. object:miniaturizeButton];
  362. [center addObserver:self
  363. selector:@selector(adjustZoomButton:)
  364. name:NSViewFrameDidChangeNotification
  365. object:zoomButton];
  366. }
  367. - (void)adjustCloseButton:(NSNotification*)notification {
  368. [self adjustButton:[notification object]
  369. ofKind:NSWindowCloseButton];
  370. }
  371. - (void)adjustMiniaturizeButton:(NSNotification*)notification {
  372. [self adjustButton:[notification object]
  373. ofKind:NSWindowMiniaturizeButton];
  374. }
  375. - (void)adjustZoomButton:(NSNotification*)notification {
  376. [self adjustButton:[notification object]
  377. ofKind:NSWindowZoomButton];
  378. }
  379. - (void)adjustButton:(NSButton*)button
  380. ofKind:(NSWindowButton)kind {
  381. NSRect buttonFrame = [button frame];
  382. NSRect frameViewBounds = [[self frameView] bounds];
  383. NSPoint offset = self.windowButtonsOffset;
  384. buttonFrame.origin = NSMakePoint(
  385. offset.x,
  386. (NSHeight(frameViewBounds) - NSHeight(buttonFrame) - offset.y));
  387. switch (kind) {
  388. case NSWindowZoomButton:
  389. buttonFrame.origin.x += NSWidth(
  390. [[self standardWindowButton:NSWindowMiniaturizeButton] frame]);
  391. buttonFrame.origin.x += windowButtonsInterButtonSpacing_;
  392. // fallthrough
  393. case NSWindowMiniaturizeButton:
  394. buttonFrame.origin.x += NSWidth(
  395. [[self standardWindowButton:NSWindowCloseButton] frame]);
  396. buttonFrame.origin.x += windowButtonsInterButtonSpacing_;
  397. // fallthrough
  398. default:
  399. break;
  400. }
  401. BOOL didPost = [button postsBoundsChangedNotifications];
  402. [button setPostsFrameChangedNotifications:NO];
  403. [button setFrame:buttonFrame];
  404. [button setPostsFrameChangedNotifications:didPost];
  405. }
  406. - (NSView*)frameView {
  407. return [[self contentView] superview];
  408. }
  409. // Quicklook methods
  410. - (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel*)panel {
  411. return YES;
  412. }
  413. - (void)beginPreviewPanelControl:(QLPreviewPanel*)panel {
  414. panel.delegate = self;
  415. panel.dataSource = self;
  416. }
  417. - (void)endPreviewPanelControl:(QLPreviewPanel*)panel {
  418. panel.delegate = nil;
  419. panel.dataSource = nil;
  420. }
  421. - (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel*)panel {
  422. return 1;
  423. }
  424. - (id <QLPreviewItem>)previewPanel:(QLPreviewPanel*)panel previewItemAtIndex:(NSInteger)index {
  425. return [self quickLookItem];
  426. }
  427. - (void)previewFileAtPath:(NSString*)path withName:(NSString*) fileName {
  428. NSURL* url = [[[NSURL alloc] initFileURLWithPath:path] autorelease];
  429. [self setQuickLookItem:[[[AtomPreviewItem alloc] initWithURL:url title:fileName] autorelease]];
  430. [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
  431. }
  432. @end
  433. @interface ControlRegionView : NSView
  434. @end
  435. @implementation ControlRegionView
  436. - (BOOL)mouseDownCanMoveWindow {
  437. return NO;
  438. }
  439. - (NSView*)hitTest:(NSPoint)aPoint {
  440. return nil;
  441. }
  442. @end
  443. @interface NSView (WebContentsView)
  444. - (void)setMouseDownCanMoveWindow:(BOOL)can_move;
  445. @end
  446. @interface AtomProgressBar : NSProgressIndicator
  447. @end
  448. @implementation AtomProgressBar
  449. - (void)drawRect:(NSRect)dirtyRect {
  450. if (self.style != NSProgressIndicatorBarStyle)
  451. return;
  452. // Draw edges of rounded rect.
  453. NSRect rect = NSInsetRect([self bounds], 1.0, 1.0);
  454. CGFloat radius = rect.size.height / 2;
  455. NSBezierPath* bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
  456. [bezier_path setLineWidth:2.0];
  457. [[NSColor grayColor] set];
  458. [bezier_path stroke];
  459. // Fill the rounded rect.
  460. rect = NSInsetRect(rect, 2.0, 2.0);
  461. radius = rect.size.height / 2;
  462. bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
  463. [bezier_path setLineWidth:1.0];
  464. [bezier_path addClip];
  465. // Calculate the progress width.
  466. rect.size.width = floor(rect.size.width * ([self doubleValue] / [self maxValue]));
  467. // Fill the progress bar with color blue.
  468. [[NSColor colorWithSRGBRed:0.2 green:0.6 blue:1 alpha:1] set];
  469. NSRectFill(rect);
  470. }
  471. @end
  472. namespace mate {
  473. template<>
  474. struct Converter<atom::NativeWindowMac::TitleBarStyle> {
  475. static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
  476. atom::NativeWindowMac::TitleBarStyle* out) {
  477. std::string title_bar_style;
  478. if (!ConvertFromV8(isolate, val, &title_bar_style))
  479. return false;
  480. if (title_bar_style == "hidden") {
  481. *out = atom::NativeWindowMac::HIDDEN;
  482. } else if (title_bar_style == "hidden-inset" || // Deprecate this after 2.0
  483. title_bar_style == "hiddenInset") {
  484. *out = atom::NativeWindowMac::HIDDEN_INSET;
  485. } else {
  486. return false;
  487. }
  488. return true;
  489. }
  490. };
  491. } // namespace mate
  492. namespace atom {
  493. NativeWindowMac::NativeWindowMac(
  494. brightray::InspectableWebContents* web_contents,
  495. const mate::Dictionary& options,
  496. NativeWindow* parent)
  497. : NativeWindow(web_contents, options, parent),
  498. is_kiosk_(false),
  499. was_fullscreen_(false),
  500. zoom_to_page_width_(false),
  501. attention_request_id_(0),
  502. title_bar_style_(NORMAL) {
  503. int width = 800, height = 600;
  504. options.Get(options::kWidth, &width);
  505. options.Get(options::kHeight, &height);
  506. NSRect main_screen_rect = [[[NSScreen screens] firstObject] frame];
  507. NSRect cocoa_bounds = NSMakeRect(
  508. round((NSWidth(main_screen_rect) - width) / 2) ,
  509. round((NSHeight(main_screen_rect) - height) / 2),
  510. width,
  511. height);
  512. bool resizable = true;
  513. options.Get(options::kResizable, &resizable);
  514. bool minimizable = true;
  515. options.Get(options::kMinimizable, &minimizable);
  516. bool maximizable = true;
  517. options.Get(options::kMaximizable, &maximizable);
  518. bool closable = true;
  519. options.Get(options::kClosable, &closable);
  520. options.Get(options::kTitleBarStyle, &title_bar_style_);
  521. std::string windowType;
  522. options.Get(options::kType, &windowType);
  523. bool useStandardWindow = true;
  524. // eventually deprecate separate "standardWindow" option in favor of
  525. // standard / textured window types
  526. options.Get(options::kStandardWindow, &useStandardWindow);
  527. if (windowType == "textured") {
  528. useStandardWindow = false;
  529. }
  530. NSUInteger styleMask = NSTitledWindowMask;
  531. if (minimizable) {
  532. styleMask |= NSMiniaturizableWindowMask;
  533. }
  534. if (closable) {
  535. styleMask |= NSClosableWindowMask;
  536. }
  537. if (title_bar_style_ != NORMAL) {
  538. // The window without titlebar is treated the same with frameless window.
  539. set_has_frame(false);
  540. }
  541. if (!useStandardWindow || transparent() || !has_frame()) {
  542. styleMask |= NSTexturedBackgroundWindowMask;
  543. }
  544. if (resizable) {
  545. styleMask |= NSResizableWindowMask;
  546. }
  547. window_.reset([[AtomNSWindow alloc]
  548. initWithContentRect:cocoa_bounds
  549. styleMask:styleMask
  550. backing:NSBackingStoreBuffered
  551. defer:YES]);
  552. [window_ setShell:this];
  553. [window_ setEnableLargerThanScreen:enable_larger_than_screen()];
  554. window_delegate_.reset([[AtomNSWindowDelegate alloc] initWithShell:this]);
  555. [window_ setDelegate:window_delegate_];
  556. // Only use native parent window for non-modal windows.
  557. if (parent && !is_modal()) {
  558. SetParentWindow(parent);
  559. }
  560. if (transparent()) {
  561. // Setting the background color to clear will also hide the shadow.
  562. [window_ setBackgroundColor:[NSColor clearColor]];
  563. }
  564. if (windowType == "desktop") {
  565. [window_ setLevel:kCGDesktopWindowLevel - 1];
  566. [window_ setDisableKeyOrMainWindow:YES];
  567. [window_ setCollectionBehavior:
  568. (NSWindowCollectionBehaviorCanJoinAllSpaces |
  569. NSWindowCollectionBehaviorStationary |
  570. NSWindowCollectionBehaviorIgnoresCycle)];
  571. }
  572. bool focusable;
  573. if (options.Get(options::kFocusable, &focusable) && !focusable)
  574. [window_ setDisableKeyOrMainWindow:YES];
  575. if (transparent() || !has_frame()) {
  576. if (base::mac::IsOSYosemiteOrLater()) {
  577. // Don't show title bar.
  578. [window_ setTitleVisibility:NSWindowTitleHidden];
  579. }
  580. // Remove non-transparent corners, see http://git.io/vfonD.
  581. [window_ setOpaque:NO];
  582. }
  583. // We will manage window's lifetime ourselves.
  584. [window_ setReleasedWhenClosed:NO];
  585. // Hide the title bar.
  586. if (title_bar_style_ == HIDDEN_INSET) {
  587. if (base::mac::IsOSYosemiteOrLater()) {
  588. [window_ setTitlebarAppearsTransparent:YES];
  589. base::scoped_nsobject<NSToolbar> toolbar(
  590. [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
  591. [toolbar setShowsBaselineSeparator:NO];
  592. [window_ setToolbar:toolbar];
  593. } else {
  594. [window_ enableWindowButtonsOffset];
  595. [window_ setWindowButtonsOffset:NSMakePoint(12, 10)];
  596. }
  597. }
  598. // On macOS the initial window size doesn't include window frame.
  599. bool use_content_size = false;
  600. options.Get(options::kUseContentSize, &use_content_size);
  601. if (!has_frame() || !use_content_size)
  602. SetSize(gfx::Size(width, height));
  603. options.Get(options::kZoomToPageWidth, &zoom_to_page_width_);
  604. // Enable the NSView to accept first mouse event.
  605. bool acceptsFirstMouse = false;
  606. options.Get(options::kAcceptFirstMouse, &acceptsFirstMouse);
  607. [window_ setAcceptsFirstMouse:acceptsFirstMouse];
  608. // Disable auto-hiding cursor.
  609. bool disableAutoHideCursor = false;
  610. options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
  611. [window_ setDisableAutoHideCursor:disableAutoHideCursor];
  612. NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
  613. [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
  614. // Use an NSEvent monitor to listen for the wheel event.
  615. BOOL __block began = NO;
  616. wheel_event_monitor_ = [NSEvent
  617. addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
  618. handler:^(NSEvent* event) {
  619. if ([[event window] windowNumber] != [window_ windowNumber])
  620. return event;
  621. if (!web_contents)
  622. return event;
  623. if (!began && (([event phase] == NSEventPhaseMayBegin) ||
  624. ([event phase] == NSEventPhaseBegan))) {
  625. this->NotifyWindowScrollTouchBegin();
  626. began = YES;
  627. } else if (began && (([event phase] == NSEventPhaseEnded) ||
  628. ([event phase] == NSEventPhaseCancelled))) {
  629. this->NotifyWindowScrollTouchEnd();
  630. began = NO;
  631. }
  632. return event;
  633. }];
  634. InstallView();
  635. std::string type;
  636. if (options.Get(options::kVibrancyType, &type)) {
  637. SetVibrancy(type);
  638. }
  639. // Set maximizable state last to ensure zoom button does not get reset
  640. // by calls to other APIs.
  641. SetMaximizable(maximizable);
  642. RegisterInputEventObserver(
  643. web_contents->GetWebContents()->GetRenderViewHost());
  644. }
  645. NativeWindowMac::~NativeWindowMac() {
  646. [NSEvent removeMonitor:wheel_event_monitor_];
  647. Observe(nullptr);
  648. }
  649. void NativeWindowMac::Close() {
  650. // When this is a sheet showing, performClose won't work.
  651. if (is_modal() && parent() && IsVisible()) {
  652. [parent()->GetNativeWindow() endSheet:window_];
  653. CloseImmediately();
  654. return;
  655. }
  656. if (!IsClosable()) {
  657. WindowList::WindowCloseCancelled(this);
  658. return;
  659. }
  660. [window_ performClose:nil];
  661. }
  662. void NativeWindowMac::CloseImmediately() {
  663. [window_ close];
  664. }
  665. void NativeWindowMac::Focus(bool focus) {
  666. if (!IsVisible())
  667. return;
  668. if (focus) {
  669. [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
  670. [window_ makeKeyAndOrderFront:nil];
  671. } else {
  672. [window_ orderBack:nil];
  673. }
  674. }
  675. bool NativeWindowMac::IsFocused() {
  676. return [window_ isKeyWindow];
  677. }
  678. void NativeWindowMac::Show() {
  679. if (is_modal() && parent()) {
  680. if ([window_ sheetParent] == nil)
  681. [parent()->GetNativeWindow() beginSheet:window_
  682. completionHandler:^(NSModalResponse) {}];
  683. return;
  684. }
  685. // This method is supposed to put focus on window, however if the app does not
  686. // have focus then "makeKeyAndOrderFront" will only show the window.
  687. [NSApp activateIgnoringOtherApps:YES];
  688. [window_ makeKeyAndOrderFront:nil];
  689. }
  690. void NativeWindowMac::ShowInactive() {
  691. [window_ orderFrontRegardless];
  692. }
  693. void NativeWindowMac::Hide() {
  694. if (is_modal() && parent()) {
  695. [window_ orderOut:nil];
  696. [parent()->GetNativeWindow() endSheet:window_];
  697. return;
  698. }
  699. [window_ orderOut:nil];
  700. }
  701. bool NativeWindowMac::IsVisible() {
  702. return [window_ isVisible];
  703. }
  704. bool NativeWindowMac::IsEnabled() {
  705. return [window_ attachedSheet] == nil;
  706. }
  707. void NativeWindowMac::Maximize() {
  708. if (IsMaximized())
  709. return;
  710. [window_ zoom:nil];
  711. }
  712. void NativeWindowMac::Unmaximize() {
  713. if (!IsMaximized())
  714. return;
  715. [window_ zoom:nil];
  716. }
  717. bool NativeWindowMac::IsMaximized() {
  718. if (([window_ styleMask] & NSResizableWindowMask) != 0) {
  719. return [window_ isZoomed];
  720. } else {
  721. NSRect rectScreen = [[NSScreen mainScreen] visibleFrame];
  722. NSRect rectWindow = [window_ frame];
  723. return (rectScreen.origin.x == rectWindow.origin.x &&
  724. rectScreen.origin.y == rectWindow.origin.y &&
  725. rectScreen.size.width == rectWindow.size.width &&
  726. rectScreen.size.height == rectWindow.size.height);
  727. }
  728. }
  729. void NativeWindowMac::Minimize() {
  730. [window_ miniaturize:nil];
  731. }
  732. void NativeWindowMac::Restore() {
  733. [window_ deminiaturize:nil];
  734. }
  735. bool NativeWindowMac::IsMinimized() {
  736. return [window_ isMiniaturized];
  737. }
  738. void NativeWindowMac::SetFullScreen(bool fullscreen) {
  739. if (fullscreen == IsFullscreen())
  740. return;
  741. [window_ toggleFullScreen:nil];
  742. }
  743. bool NativeWindowMac::IsFullscreen() const {
  744. return [window_ styleMask] & NSFullScreenWindowMask;
  745. }
  746. void NativeWindowMac::SetBounds(const gfx::Rect& bounds, bool animate) {
  747. // Do nothing if in fullscreen mode.
  748. if (IsFullscreen())
  749. return;
  750. // Check size constraints since setFrame does not check it.
  751. gfx::Size size = bounds.size();
  752. size.SetToMax(GetMinimumSize());
  753. gfx::Size max_size = GetMaximumSize();
  754. if (!max_size.IsEmpty())
  755. size.SetToMin(max_size);
  756. NSRect cocoa_bounds = NSMakeRect(bounds.x(), 0, size.width(), size.height());
  757. // Flip coordinates based on the primary screen.
  758. NSScreen* screen = [[NSScreen screens] firstObject];
  759. cocoa_bounds.origin.y =
  760. NSHeight([screen frame]) - size.height() - bounds.y();
  761. [window_ setFrame:cocoa_bounds display:YES animate:animate];
  762. }
  763. gfx::Rect NativeWindowMac::GetBounds() {
  764. NSRect frame = [window_ frame];
  765. gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
  766. NSScreen* screen = [[NSScreen screens] firstObject];
  767. bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
  768. return bounds;
  769. }
  770. void NativeWindowMac::SetContentSizeConstraints(
  771. const extensions::SizeConstraints& size_constraints) {
  772. auto convertSize = [this](const gfx::Size& size) {
  773. // Our frameless window still has titlebar attached, so setting contentSize
  774. // will result in actual content size being larger.
  775. if (!has_frame()) {
  776. NSRect frame = NSMakeRect(0, 0, size.width(), size.height());
  777. NSRect content = [window_ contentRectForFrameRect:frame];
  778. return content.size;
  779. } else {
  780. return NSMakeSize(size.width(), size.height());
  781. }
  782. };
  783. NSView* content = [window_ contentView];
  784. if (size_constraints.HasMinimumSize()) {
  785. NSSize min_size = convertSize(size_constraints.GetMinimumSize());
  786. [window_ setContentMinSize:[content convertSize:min_size toView:nil]];
  787. }
  788. if (size_constraints.HasMaximumSize()) {
  789. NSSize max_size = convertSize(size_constraints.GetMaximumSize());
  790. [window_ setContentMaxSize:[content convertSize:max_size toView:nil]];
  791. }
  792. NativeWindow::SetContentSizeConstraints(size_constraints);
  793. }
  794. void NativeWindowMac::SetResizable(bool resizable) {
  795. SetStyleMask(resizable, NSResizableWindowMask);
  796. }
  797. bool NativeWindowMac::IsResizable() {
  798. return [window_ styleMask] & NSResizableWindowMask;
  799. }
  800. void NativeWindowMac::SetAspectRatio(double aspect_ratio,
  801. const gfx::Size& extra_size) {
  802. NativeWindow::SetAspectRatio(aspect_ratio, extra_size);
  803. // Reset the behaviour to default if aspect_ratio is set to 0 or less.
  804. if (aspect_ratio > 0.0)
  805. [window_ setAspectRatio:NSMakeSize(aspect_ratio, 1.0)];
  806. else
  807. [window_ setResizeIncrements:NSMakeSize(1.0, 1.0)];
  808. }
  809. void NativeWindowMac::PreviewFile(const std::string& path,
  810. const std::string& display_name) {
  811. NSString* path_ns = [NSString stringWithUTF8String:path.c_str()];
  812. NSString* name_ns = [NSString stringWithUTF8String:display_name.c_str()];
  813. [window_ previewFileAtPath:path_ns withName:name_ns];
  814. }
  815. void NativeWindowMac::CloseFilePreview() {
  816. if ([QLPreviewPanel sharedPreviewPanelExists]) {
  817. [[QLPreviewPanel sharedPreviewPanel] close];
  818. }
  819. }
  820. void NativeWindowMac::SetMovable(bool movable) {
  821. [window_ setMovable:movable];
  822. }
  823. bool NativeWindowMac::IsMovable() {
  824. return [window_ isMovable];
  825. }
  826. void NativeWindowMac::SetMinimizable(bool minimizable) {
  827. SetStyleMask(minimizable, NSMiniaturizableWindowMask);
  828. }
  829. bool NativeWindowMac::IsMinimizable() {
  830. return [window_ styleMask] & NSMiniaturizableWindowMask;
  831. }
  832. void NativeWindowMac::SetMaximizable(bool maximizable) {
  833. [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:maximizable];
  834. }
  835. bool NativeWindowMac::IsMaximizable() {
  836. return [[window_ standardWindowButton:NSWindowZoomButton] isEnabled];
  837. }
  838. void NativeWindowMac::SetFullScreenable(bool fullscreenable) {
  839. SetCollectionBehavior(
  840. fullscreenable, NSWindowCollectionBehaviorFullScreenPrimary);
  841. // On EL Capitan this flag is required to hide fullscreen button.
  842. SetCollectionBehavior(
  843. !fullscreenable, NSWindowCollectionBehaviorFullScreenAuxiliary);
  844. }
  845. bool NativeWindowMac::IsFullScreenable() {
  846. NSUInteger collectionBehavior = [window_ collectionBehavior];
  847. return collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary;
  848. }
  849. void NativeWindowMac::SetClosable(bool closable) {
  850. SetStyleMask(closable, NSClosableWindowMask);
  851. }
  852. bool NativeWindowMac::IsClosable() {
  853. return [window_ styleMask] & NSClosableWindowMask;
  854. }
  855. void NativeWindowMac::SetAlwaysOnTop(bool top, const std::string& level) {
  856. int windowLevel = NSNormalWindowLevel;
  857. if (top) {
  858. if (level == "floating") {
  859. windowLevel = NSFloatingWindowLevel;
  860. } else if (level == "torn-off-menu") {
  861. windowLevel = NSTornOffMenuWindowLevel;
  862. } else if (level == "modal-panel") {
  863. windowLevel = NSModalPanelWindowLevel;
  864. } else if (level == "main-menu") {
  865. windowLevel = NSMainMenuWindowLevel;
  866. } else if (level == "status") {
  867. windowLevel = NSStatusWindowLevel;
  868. } else if (level == "pop-up-menu") {
  869. windowLevel = NSPopUpMenuWindowLevel;
  870. } else if (level == "screen-saver") {
  871. windowLevel = NSScreenSaverWindowLevel;
  872. } else if (level == "dock") {
  873. // Deprecated by macOS, but kept for backwards compatibility
  874. windowLevel = NSDockWindowLevel;
  875. }
  876. }
  877. [window_ setLevel:windowLevel];
  878. }
  879. bool NativeWindowMac::IsAlwaysOnTop() {
  880. return [window_ level] != NSNormalWindowLevel;
  881. }
  882. void NativeWindowMac::Center() {
  883. [window_ center];
  884. }
  885. void NativeWindowMac::SetTitle(const std::string& title) {
  886. // For macOS <= 10.9, the setTitleVisibility API is not available, we have
  887. // to avoid calling setTitle for frameless window.
  888. if (!base::mac::IsOSYosemiteOrLater() && (transparent() || !has_frame()))
  889. return;
  890. [window_ setTitle:base::SysUTF8ToNSString(title)];
  891. }
  892. std::string NativeWindowMac::GetTitle() {
  893. return base::SysNSStringToUTF8([window_ title]);;
  894. }
  895. void NativeWindowMac::FlashFrame(bool flash) {
  896. if (flash) {
  897. attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest];
  898. } else {
  899. [NSApp cancelUserAttentionRequest:attention_request_id_];
  900. attention_request_id_ = 0;
  901. }
  902. }
  903. void NativeWindowMac::SetSkipTaskbar(bool skip) {
  904. }
  905. void NativeWindowMac::SetKiosk(bool kiosk) {
  906. if (kiosk && !is_kiosk_) {
  907. kiosk_options_ = [NSApp currentSystemPresentationOptions];
  908. NSApplicationPresentationOptions options =
  909. NSApplicationPresentationHideDock +
  910. NSApplicationPresentationHideMenuBar +
  911. NSApplicationPresentationDisableAppleMenu +
  912. NSApplicationPresentationDisableProcessSwitching +
  913. NSApplicationPresentationDisableForceQuit +
  914. NSApplicationPresentationDisableSessionTermination +
  915. NSApplicationPresentationDisableHideApplication;
  916. [NSApp setPresentationOptions:options];
  917. is_kiosk_ = true;
  918. was_fullscreen_ = IsFullscreen();
  919. if (!was_fullscreen_) SetFullScreen(true);
  920. } else if (!kiosk && is_kiosk_) {
  921. is_kiosk_ = false;
  922. if (!was_fullscreen_) SetFullScreen(false);
  923. [NSApp setPresentationOptions:kiosk_options_];
  924. }
  925. }
  926. bool NativeWindowMac::IsKiosk() {
  927. return is_kiosk_;
  928. }
  929. void NativeWindowMac::SetBackgroundColor(const std::string& color_name) {
  930. SkColor color = ParseHexColor(color_name);
  931. base::ScopedCFTypeRef<CGColorRef> cgcolor(
  932. skia::CGColorCreateFromSkColor(color));
  933. [[[window_ contentView] layer] setBackgroundColor:cgcolor];
  934. const auto view = web_contents()->GetRenderWidgetHostView();
  935. if (view)
  936. view->SetBackgroundColor(color);
  937. }
  938. void NativeWindowMac::SetHasShadow(bool has_shadow) {
  939. [window_ setHasShadow:has_shadow];
  940. }
  941. bool NativeWindowMac::HasShadow() {
  942. return [window_ hasShadow];
  943. }
  944. void NativeWindowMac::SetRepresentedFilename(const std::string& filename) {
  945. [window_ setRepresentedFilename:base::SysUTF8ToNSString(filename)];
  946. }
  947. std::string NativeWindowMac::GetRepresentedFilename() {
  948. return base::SysNSStringToUTF8([window_ representedFilename]);
  949. }
  950. void NativeWindowMac::SetDocumentEdited(bool edited) {
  951. [window_ setDocumentEdited:edited];
  952. }
  953. bool NativeWindowMac::IsDocumentEdited() {
  954. return [window_ isDocumentEdited];
  955. }
  956. void NativeWindowMac::SetIgnoreMouseEvents(bool ignore) {
  957. [window_ setIgnoresMouseEvents:ignore];
  958. }
  959. void NativeWindowMac::SetContentProtection(bool enable) {
  960. [window_ setSharingType:enable ? NSWindowSharingNone
  961. : NSWindowSharingReadOnly];
  962. }
  963. void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
  964. if (is_modal())
  965. return;
  966. NativeWindow::SetParentWindow(parent);
  967. // Remove current parent window.
  968. if ([window_ parentWindow])
  969. [[window_ parentWindow] removeChildWindow:window_];
  970. // Set new current window.
  971. if (parent)
  972. [parent->GetNativeWindow() addChildWindow:window_ ordered:NSWindowAbove];
  973. }
  974. gfx::NativeWindow NativeWindowMac::GetNativeWindow() {
  975. return window_;
  976. }
  977. gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() {
  978. return inspectable_web_contents()->GetView()->GetNativeView();
  979. }
  980. void NativeWindowMac::SetProgressBar(double progress, const NativeWindow::ProgressState state) {
  981. NSDockTile* dock_tile = [NSApp dockTile];
  982. // For the first time API invoked, we need to create a ContentView in DockTile.
  983. if (dock_tile.contentView == NULL) {
  984. NSImageView* image_view = [[NSImageView alloc] init];
  985. [image_view setImage:[NSApp applicationIconImage]];
  986. [dock_tile setContentView:image_view];
  987. }
  988. if ([[dock_tile.contentView subviews] count] == 0) {
  989. NSProgressIndicator* progress_indicator = [[AtomProgressBar alloc]
  990. initWithFrame:NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0)];
  991. [progress_indicator setStyle:NSProgressIndicatorBarStyle];
  992. [progress_indicator setIndeterminate:NO];
  993. [progress_indicator setBezeled:YES];
  994. [progress_indicator setMinValue:0];
  995. [progress_indicator setMaxValue:1];
  996. [progress_indicator setHidden:NO];
  997. [dock_tile.contentView addSubview:progress_indicator];
  998. }
  999. NSProgressIndicator* progress_indicator =
  1000. static_cast<NSProgressIndicator*>([[[dock_tile contentView] subviews]
  1001. objectAtIndex:0]);
  1002. if (progress < 0) {
  1003. [progress_indicator setHidden:YES];
  1004. } else if (progress > 1) {
  1005. [progress_indicator setHidden:NO];
  1006. [progress_indicator setIndeterminate:YES];
  1007. [progress_indicator setDoubleValue:1];
  1008. } else {
  1009. [progress_indicator setHidden:NO];
  1010. [progress_indicator setDoubleValue:progress];
  1011. }
  1012. [dock_tile display];
  1013. }
  1014. void NativeWindowMac::SetOverlayIcon(const gfx::Image& overlay,
  1015. const std::string& description) {
  1016. }
  1017. void NativeWindowMac::SetVisibleOnAllWorkspaces(bool visible) {
  1018. SetCollectionBehavior(visible, NSWindowCollectionBehaviorCanJoinAllSpaces);
  1019. }
  1020. bool NativeWindowMac::IsVisibleOnAllWorkspaces() {
  1021. NSUInteger collectionBehavior = [window_ collectionBehavior];
  1022. return collectionBehavior & NSWindowCollectionBehaviorCanJoinAllSpaces;
  1023. }
  1024. void NativeWindowMac::SetAutoHideCursor(bool auto_hide) {
  1025. [window_ setDisableAutoHideCursor:!auto_hide];
  1026. }
  1027. void NativeWindowMac::SetVibrancy(const std::string& type) {
  1028. if (!base::mac::IsOSYosemiteOrLater()) return;
  1029. NSView* vibrant_view = [window_ vibrantView];
  1030. if (type.empty()) {
  1031. if (vibrant_view == nil) return;
  1032. [vibrant_view removeFromSuperview];
  1033. [window_ setVibrantView:nil];
  1034. return;
  1035. }
  1036. NSVisualEffectView* effect_view = (NSVisualEffectView*)vibrant_view;
  1037. if (effect_view == nil) {
  1038. effect_view = [[[NSVisualEffectView alloc]
  1039. initWithFrame: [[window_ contentView] bounds]] autorelease];
  1040. [window_ setVibrantView:(NSView*)effect_view];
  1041. [effect_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
  1042. [effect_view setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
  1043. [effect_view setState:NSVisualEffectStateActive];
  1044. [[window_ contentView] addSubview:effect_view
  1045. positioned:NSWindowBelow
  1046. relativeTo:nil];
  1047. }
  1048. NSVisualEffectMaterial vibrancyType = NSVisualEffectMaterialLight;
  1049. if (type == "appearance-based") {
  1050. vibrancyType = NSVisualEffectMaterialAppearanceBased;
  1051. } else if (type == "light") {
  1052. vibrancyType = NSVisualEffectMaterialLight;
  1053. } else if (type == "dark") {
  1054. vibrancyType = NSVisualEffectMaterialDark;
  1055. } else if (type == "titlebar") {
  1056. vibrancyType = NSVisualEffectMaterialTitlebar;
  1057. }
  1058. if (base::mac::IsOSElCapitanOrLater()) {
  1059. // TODO(kevinsawicki): Use NSVisualEffectMaterial* constants directly once
  1060. // they are available in the minimum SDK version
  1061. if (type == "selection") {
  1062. // NSVisualEffectMaterialSelection
  1063. vibrancyType = (NSVisualEffectMaterial) 4;
  1064. } else if (type == "menu") {
  1065. // NSVisualEffectMaterialMenu
  1066. vibrancyType = (NSVisualEffectMaterial) 5;
  1067. } else if (type == "popover") {
  1068. // NSVisualEffectMaterialPopover
  1069. vibrancyType = (NSVisualEffectMaterial) 6;
  1070. } else if (type == "sidebar") {
  1071. // NSVisualEffectMaterialSidebar
  1072. vibrancyType = (NSVisualEffectMaterial) 7;
  1073. } else if (type == "medium-light") {
  1074. // NSVisualEffectMaterialMediumLight
  1075. vibrancyType = (NSVisualEffectMaterial) 8;
  1076. } else if (type == "ultra-dark") {
  1077. // NSVisualEffectMaterialUltraDark
  1078. vibrancyType = (NSVisualEffectMaterial) 9;
  1079. }
  1080. }
  1081. [effect_view setMaterial:vibrancyType];
  1082. }
  1083. void NativeWindowMac::OnInputEvent(const blink::WebInputEvent& event) {
  1084. switch (event.type) {
  1085. case blink::WebInputEvent::GestureScrollBegin:
  1086. case blink::WebInputEvent::GestureScrollUpdate:
  1087. case blink::WebInputEvent::GestureScrollEnd:
  1088. this->NotifyWindowScrollTouchEdge();
  1089. break;
  1090. default:
  1091. break;
  1092. }
  1093. }
  1094. void NativeWindowMac::RenderViewHostChanged(
  1095. content::RenderViewHost* old_host,
  1096. content::RenderViewHost* new_host) {
  1097. UnregisterInputEventObserver(old_host);
  1098. RegisterInputEventObserver(new_host);
  1099. }
  1100. std::vector<gfx::Rect> NativeWindowMac::CalculateNonDraggableRegions(
  1101. const std::vector<DraggableRegion>& regions, int width, int height) {
  1102. std::vector<gfx::Rect> result;
  1103. if (regions.empty()) {
  1104. result.push_back(gfx::Rect(0, 0, width, height));
  1105. } else {
  1106. std::unique_ptr<SkRegion> draggable(DraggableRegionsToSkRegion(regions));
  1107. std::unique_ptr<SkRegion> non_draggable(new SkRegion);
  1108. non_draggable->op(0, 0, width, height, SkRegion::kUnion_Op);
  1109. non_draggable->op(*draggable, SkRegion::kDifference_Op);
  1110. for (SkRegion::Iterator it(*non_draggable); !it.done(); it.next()) {
  1111. result.push_back(gfx::SkIRectToRect(it.rect()));
  1112. }
  1113. }
  1114. return result;
  1115. }
  1116. gfx::Rect NativeWindowMac::ContentBoundsToWindowBounds(
  1117. const gfx::Rect& bounds) {
  1118. if (has_frame()) {
  1119. gfx::Rect window_bounds(
  1120. [window_ frameRectForContentRect:bounds.ToCGRect()]);
  1121. int frame_height = window_bounds.height() - bounds.height();
  1122. window_bounds.set_y(window_bounds.y() - frame_height);
  1123. return window_bounds;
  1124. } else {
  1125. return bounds;
  1126. }
  1127. }
  1128. gfx::Rect NativeWindowMac::WindowBoundsToContentBounds(
  1129. const gfx::Rect& bounds) {
  1130. if (has_frame()) {
  1131. gfx::Rect content_bounds(
  1132. [window_ contentRectForFrameRect:bounds.ToCGRect()]);
  1133. int frame_height = bounds.height() - content_bounds.height();
  1134. content_bounds.set_y(content_bounds.y() + frame_height);
  1135. return content_bounds;
  1136. } else {
  1137. return bounds;
  1138. }
  1139. }
  1140. void NativeWindowMac::UpdateDraggableRegions(
  1141. const std::vector<DraggableRegion>& regions) {
  1142. NativeWindow::UpdateDraggableRegions(regions);
  1143. draggable_regions_ = regions;
  1144. UpdateDraggableRegionViews(regions);
  1145. }
  1146. void NativeWindowMac::ShowWindowButton(NSWindowButton button) {
  1147. auto view = [window_ standardWindowButton:button];
  1148. [view.superview addSubview:view positioned:NSWindowAbove relativeTo:nil];
  1149. }
  1150. void NativeWindowMac::InstallView() {
  1151. // Make sure the bottom corner is rounded for non-modal windows: http://crbug.com/396264.
  1152. // But do not enable it on OS X 10.9 for transparent window, otherwise a
  1153. // semi-transparent frame would show.
  1154. if (!(transparent() && base::mac::IsOSMavericks()) && !is_modal())
  1155. [[window_ contentView] setWantsLayer:YES];
  1156. NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
  1157. if (has_frame()) {
  1158. [view setFrame:[[window_ contentView] bounds]];
  1159. [[window_ contentView] addSubview:view];
  1160. } else {
  1161. // In OSX 10.10, adding subviews to the root view for the NSView hierarchy
  1162. // produces warnings. To eliminate the warnings, we resize the contentView
  1163. // to fill the window, and add subviews to that.
  1164. // http://crbug.com/380412
  1165. content_view_.reset([[FullSizeContentView alloc] init]);
  1166. [content_view_
  1167. setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
  1168. [content_view_ setFrame:[[[window_ contentView] superview] bounds]];
  1169. [window_ setContentView:content_view_];
  1170. [view setFrame:[content_view_ bounds]];
  1171. [content_view_ addSubview:view];
  1172. // The fullscreen button should always be hidden for frameless window.
  1173. [[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES];
  1174. if (title_bar_style_ != NORMAL) {
  1175. if (base::mac::IsOSMavericks()) {
  1176. ShowWindowButton(NSWindowZoomButton);
  1177. ShowWindowButton(NSWindowMiniaturizeButton);
  1178. ShowWindowButton(NSWindowCloseButton);
  1179. }
  1180. return;
  1181. }
  1182. // Hide the window buttons.
  1183. [[window_ standardWindowButton:NSWindowZoomButton] setHidden:YES];
  1184. [[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
  1185. [[window_ standardWindowButton:NSWindowCloseButton] setHidden:YES];
  1186. // Some third-party macOS utilities check the zoom button's enabled state to
  1187. // determine whether to show custom UI on hover, so we disable it here to
  1188. // prevent them from doing so in a frameless app window.
  1189. [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO];
  1190. }
  1191. }
  1192. void NativeWindowMac::UninstallView() {
  1193. NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
  1194. [view removeFromSuperview];
  1195. }
  1196. void NativeWindowMac::UpdateDraggableRegionViews(
  1197. const std::vector<DraggableRegion>& regions) {
  1198. if (has_frame())
  1199. return;
  1200. // All ControlRegionViews should be added as children of the WebContentsView,
  1201. // because WebContentsView will be removed and re-added when entering and
  1202. // leaving fullscreen mode.
  1203. NSView* webView = web_contents()->GetNativeView();
  1204. NSInteger webViewWidth = NSWidth([webView bounds]);
  1205. NSInteger webViewHeight = NSHeight([webView bounds]);
  1206. if ([webView respondsToSelector:@selector(setMouseDownCanMoveWindow:)]) {
  1207. [webView setMouseDownCanMoveWindow:YES];
  1208. }
  1209. // Remove all ControlRegionViews that are added last time.
  1210. // Note that [webView subviews] returns the view's mutable internal array and
  1211. // it should be copied to avoid mutating the original array while enumerating
  1212. // it.
  1213. base::scoped_nsobject<NSArray> subviews([[webView subviews] copy]);
  1214. for (NSView* subview in subviews.get())
  1215. if ([subview isKindOfClass:[ControlRegionView class]])
  1216. [subview removeFromSuperview];
  1217. // Draggable regions is implemented by having the whole web view draggable
  1218. // (mouseDownCanMoveWindow) and overlaying regions that are not draggable.
  1219. std::vector<gfx::Rect> system_drag_exclude_areas =
  1220. CalculateNonDraggableRegions(regions, webViewWidth, webViewHeight);
  1221. // Create and add a ControlRegionView for each region that needs to be
  1222. // excluded from the dragging.
  1223. for (std::vector<gfx::Rect>::const_iterator iter =
  1224. system_drag_exclude_areas.begin();
  1225. iter != system_drag_exclude_areas.end();
  1226. ++iter) {
  1227. base::scoped_nsobject<NSView> controlRegion(
  1228. [[ControlRegionView alloc] initWithFrame:NSZeroRect]);
  1229. [controlRegion setFrame:NSMakeRect(iter->x(),
  1230. webViewHeight - iter->bottom(),
  1231. iter->width(),
  1232. iter->height())];
  1233. [webView addSubview:controlRegion];
  1234. }
  1235. // AppKit will not update its cache of mouseDownCanMoveWindow unless something
  1236. // changes. Previously we tried adding an NSView and removing it, but for some
  1237. // reason it required reposting the mouse-down event, and didn't always work.
  1238. // Calling the below seems to be an effective solution.
  1239. [window_ setMovableByWindowBackground:NO];
  1240. [window_ setMovableByWindowBackground:YES];
  1241. }
  1242. void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) {
  1243. // Changing the styleMask of a frameless windows causes it to change size so
  1244. // we explicitly disable resizing while setting it.
  1245. ScopedDisableResize disable_resize;
  1246. bool was_maximizable = IsMaximizable();
  1247. if (on)
  1248. [window_ setStyleMask:[window_ styleMask] | flag];
  1249. else
  1250. [window_ setStyleMask:[window_ styleMask] & (~flag)];
  1251. // Change style mask will make the zoom button revert to default, probably
  1252. // a bug of Cocoa or macOS.
  1253. SetMaximizable(was_maximizable);
  1254. }
  1255. void NativeWindowMac::SetCollectionBehavior(bool on, NSUInteger flag) {
  1256. bool was_maximizable = IsMaximizable();
  1257. if (on)
  1258. [window_ setCollectionBehavior:[window_ collectionBehavior] | flag];
  1259. else
  1260. [window_ setCollectionBehavior:[window_ collectionBehavior] & (~flag)];
  1261. // Change collectionBehavior will make the zoom button revert to default,
  1262. // probably a bug of Cocoa or macOS.
  1263. SetMaximizable(was_maximizable);
  1264. }
  1265. void NativeWindowMac::RegisterInputEventObserver(
  1266. content::RenderViewHost* host) {
  1267. if (host)
  1268. host->GetWidget()->AddInputEventObserver(this);
  1269. }
  1270. void NativeWindowMac::UnregisterInputEventObserver(
  1271. content::RenderViewHost* host) {
  1272. if (host)
  1273. host->GetWidget()->RemoveInputEventObserver(this);
  1274. }
  1275. // static
  1276. NativeWindow* NativeWindow::Create(
  1277. brightray::InspectableWebContents* inspectable_web_contents,
  1278. const mate::Dictionary& options,
  1279. NativeWindow* parent) {
  1280. return new NativeWindowMac(inspectable_web_contents, options, parent);
  1281. }
  1282. } // namespace atom