Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / views / widget / native_widget_mac.mm
blobf40c2f476281fbcb4ba69d29ecb56291ff407a46
1 // Copyright 2014 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.
5 #include "ui/views/widget/native_widget_mac.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/mac/foundation_util.h"
10 #include "base/mac/scoped_nsobject.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "ui/gfx/font_list.h"
13 #import "ui/gfx/mac/coordinate_conversion.h"
14 #include "ui/native_theme/native_theme.h"
15 #import "ui/views/cocoa/bridged_content_view.h"
16 #import "ui/views/cocoa/bridged_native_widget.h"
17 #import "ui/views/cocoa/views_nswindow_delegate.h"
18 #include "ui/views/window/native_frame_view.h"
20 @interface NativeWidgetMacNSWindow : NSWindow
21 @end
23 @implementation NativeWidgetMacNSWindow
25 // Override canBecome{Key,Main}Window to always return YES, otherwise Windows
26 // with a styleMask of NSBorderlessWindowMask default to NO.
27 - (BOOL)canBecomeKeyWindow {
28   return YES;
31 - (BOOL)canBecomeMainWindow {
32   return YES;
35 @end
37 namespace views {
38 namespace {
40 NSInteger StyleMaskForParams(const Widget::InitParams& params) {
41   // TODO(tapted): Determine better masks when there are use cases for it.
42   if (params.remove_standard_frame)
43     return NSBorderlessWindowMask;
45   if (params.type == Widget::InitParams::TYPE_WINDOW) {
46     return NSTitledWindowMask | NSClosableWindowMask |
47            NSMiniaturizableWindowMask | NSResizableWindowMask;
48   }
49   return NSBorderlessWindowMask;
52 NSRect ValidateContentRect(NSRect content_rect) {
53   // A contentRect with zero width or height is a banned practice in Chrome, due
54   // to unpredictable OSX treatment. For now, silently give a minimum dimension.
55   // TODO(tapted): Add a DCHECK, or add emulation logic (e.g. to auto-hide).
56   if (NSWidth(content_rect) == 0)
57     content_rect.size.width = 1;
59   if (NSHeight(content_rect) == 0)
60     content_rect.size.height = 1;
62   return content_rect;
65 gfx::Size WindowSizeForClientAreaSize(NSWindow* window, const gfx::Size& size) {
66   NSRect content_rect = NSMakeRect(0, 0, size.width(), size.height());
67   NSRect frame_rect = [window frameRectForContentRect:content_rect];
68   return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect));
71 }  // namespace
73 ////////////////////////////////////////////////////////////////////////////////
74 // NativeWidgetMac, public:
76 NativeWidgetMac::NativeWidgetMac(internal::NativeWidgetDelegate* delegate)
77     : delegate_(delegate),
78       bridge_(new BridgedNativeWidget(this)),
79       ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {
82 NativeWidgetMac::~NativeWidgetMac() {
83   if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
84     delete delegate_;
85   else
86     CloseNow();
89 void NativeWidgetMac::OnWindowWillClose() {
90   delegate_->OnNativeWidgetDestroying();
91   // Note: If closed via CloseNow(), |bridge_| will already be reset. If closed
92   // by the user, or via Close() and a RunLoop, this will reset it.
93   bridge_.reset();
94   delegate_->OnNativeWidgetDestroyed();
95   if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
96     delete this;
99 ////////////////////////////////////////////////////////////////////////////////
100 // NativeWidgetMac, internal::NativeWidgetPrivate implementation:
102 void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) {
103   ownership_ = params.ownership;
105   NSInteger style_mask = StyleMaskForParams(params);
106   NSRect content_rect = ValidateContentRect(
107       [NSWindow contentRectForFrameRect:gfx::ScreenRectToNSRect(params.bounds)
108                               styleMask:style_mask]);
110   base::scoped_nsobject<NSWindow> window([[NativeWidgetMacNSWindow alloc]
111       initWithContentRect:content_rect
112                 styleMask:style_mask
113                   backing:NSBackingStoreBuffered
114                     defer:YES]);
115   [window setReleasedWhenClosed:NO];  // Owned by scoped_nsobject.
116   bridge_->Init(window, params);
118   delegate_->OnNativeWidgetCreated(true);
120   bridge_->SetFocusManager(GetWidget()->GetFocusManager());
123 NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() {
124   return new NativeFrameView(GetWidget());
127 bool NativeWidgetMac::ShouldUseNativeFrame() const {
128   return true;
131 bool NativeWidgetMac::ShouldWindowContentsBeTransparent() const {
132   NOTIMPLEMENTED();
133   return false;
136 void NativeWidgetMac::FrameTypeChanged() {
137   NOTIMPLEMENTED();
140 Widget* NativeWidgetMac::GetWidget() {
141   return delegate_->AsWidget();
144 const Widget* NativeWidgetMac::GetWidget() const {
145   return delegate_->AsWidget();
148 gfx::NativeView NativeWidgetMac::GetNativeView() const {
149   // Returns a BridgedContentView, unless there is no views::RootView set.
150   return [GetNativeWindow() contentView];
153 gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const {
154   return bridge_ ? bridge_->ns_window() : nil;
157 Widget* NativeWidgetMac::GetTopLevelWidget() {
158   NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
159   return native_widget ? native_widget->GetWidget() : NULL;
162 const ui::Compositor* NativeWidgetMac::GetCompositor() const {
163   NOTIMPLEMENTED();
164   return NULL;
167 ui::Compositor* NativeWidgetMac::GetCompositor() {
168   NOTIMPLEMENTED();
169   return NULL;
172 ui::Layer* NativeWidgetMac::GetLayer() {
173   NOTIMPLEMENTED();
174   return NULL;
177 void NativeWidgetMac::ReorderNativeViews() {
178   if (bridge_)
179     bridge_->SetRootView(GetWidget()->GetRootView());
182 void NativeWidgetMac::ViewRemoved(View* view) {
183   NOTIMPLEMENTED();
186 void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) {
187   NOTIMPLEMENTED();
190 void* NativeWidgetMac::GetNativeWindowProperty(const char* name) const {
191   NOTIMPLEMENTED();
192   return NULL;
195 TooltipManager* NativeWidgetMac::GetTooltipManager() const {
196   NOTIMPLEMENTED();
197   return NULL;
200 void NativeWidgetMac::SetCapture() {
201   NOTIMPLEMENTED();
204 void NativeWidgetMac::ReleaseCapture() {
205   NOTIMPLEMENTED();
208 bool NativeWidgetMac::HasCapture() const {
209   NOTIMPLEMENTED();
210   return false;
213 InputMethod* NativeWidgetMac::CreateInputMethod() {
214   return bridge_ ? bridge_->CreateInputMethod() : NULL;
217 internal::InputMethodDelegate* NativeWidgetMac::GetInputMethodDelegate() {
218   return bridge_.get();
221 ui::InputMethod* NativeWidgetMac::GetHostInputMethod() {
222   return bridge_ ? bridge_->GetHostInputMethod() : NULL;
225 void NativeWidgetMac::CenterWindow(const gfx::Size& size) {
226   SetSize(WindowSizeForClientAreaSize(GetNativeWindow(), size));
227   // Note that this is not the precise center of screen, but it is the standard
228   // location for windows like dialogs to appear on screen for Mac.
229   // TODO(tapted): If there is a parent window, center in that instead.
230   [GetNativeWindow() center];
233 void NativeWidgetMac::GetWindowPlacement(gfx::Rect* bounds,
234                                          ui::WindowShowState* maximized) const {
235   NOTIMPLEMENTED();
238 bool NativeWidgetMac::SetWindowTitle(const base::string16& title) {
239   NSWindow* window = GetNativeWindow();
240   NSString* current_title = [window title];
241   NSString* new_title = SysUTF16ToNSString(title);
242   if ([current_title isEqualToString:new_title])
243     return false;
245   [window setTitle:new_title];
246   return true;
249 void NativeWidgetMac::SetWindowIcons(const gfx::ImageSkia& window_icon,
250                                      const gfx::ImageSkia& app_icon) {
251   NOTIMPLEMENTED();
254 void NativeWidgetMac::InitModalType(ui::ModalType modal_type) {
255   NOTIMPLEMENTED();
258 gfx::Rect NativeWidgetMac::GetWindowBoundsInScreen() const {
259   return gfx::ScreenRectFromNSRect([GetNativeWindow() frame]);
262 gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const {
263   NSWindow* window = GetNativeWindow();
264   return gfx::ScreenRectFromNSRect(
265       [window contentRectForFrameRect:[window frame]]);
268 gfx::Rect NativeWidgetMac::GetRestoredBounds() const {
269   NOTIMPLEMENTED();
270   return gfx::Rect();
273 void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) {
274   [GetNativeWindow() setFrame:gfx::ScreenRectToNSRect(bounds)
275                       display:YES
276                       animate:NO];
279 void NativeWidgetMac::SetSize(const gfx::Size& size) {
280   // Ensure the top-left corner stays in-place (rather than the bottom-left,
281   // which -[NSWindow setContentSize:] would do).
282   SetBounds(gfx::Rect(GetWindowBoundsInScreen().origin(), size));
285 void NativeWidgetMac::StackAbove(gfx::NativeView native_view) {
286   NOTIMPLEMENTED();
289 void NativeWidgetMac::StackAtTop() {
290   NOTIMPLEMENTED();
293 void NativeWidgetMac::StackBelow(gfx::NativeView native_view) {
294   NOTIMPLEMENTED();
297 void NativeWidgetMac::SetShape(gfx::NativeRegion shape) {
298   NOTIMPLEMENTED();
301 void NativeWidgetMac::Close() {
302   NSWindow* window = GetNativeWindow();
303   // Calling performClose: will momentarily highlight the close button, but
304   // AppKit will reject it if there is no close button.
305   SEL close_selector = ([window styleMask] & NSClosableWindowMask)
306                            ? @selector(performClose:)
307                            : @selector(close);
308   [window performSelector:close_selector withObject:nil afterDelay:0];
311 void NativeWidgetMac::CloseNow() {
312   // Reset |bridge_| to NULL before destroying it.
313   scoped_ptr<BridgedNativeWidget> bridge(bridge_.Pass());
316 void NativeWidgetMac::Show() {
317   ShowWithWindowState(ui::SHOW_STATE_NORMAL);
320 void NativeWidgetMac::Hide() {
321   NOTIMPLEMENTED();
324 void NativeWidgetMac::ShowMaximizedWithBounds(
325     const gfx::Rect& restored_bounds) {
326   NOTIMPLEMENTED();
329 void NativeWidgetMac::ShowWithWindowState(ui::WindowShowState state) {
330   switch (state) {
331     case ui::SHOW_STATE_DEFAULT:
332     case ui::SHOW_STATE_NORMAL:
333     case ui::SHOW_STATE_INACTIVE:
334       break;
335     case ui::SHOW_STATE_MINIMIZED:
336     case ui::SHOW_STATE_MAXIMIZED:
337     case ui::SHOW_STATE_FULLSCREEN:
338       NOTIMPLEMENTED();
339       break;
340     case ui::SHOW_STATE_END:
341       NOTREACHED();
342       break;
343   }
344   if (state == ui::SHOW_STATE_INACTIVE) {
345     if (!IsVisible())
346       [GetNativeWindow() orderBack:nil];
347   } else {
348     Activate();
349   }
352 bool NativeWidgetMac::IsVisible() const {
353   return [GetNativeWindow() isVisible];
356 void NativeWidgetMac::Activate() {
357   [GetNativeWindow() makeKeyAndOrderFront:nil];
358   [NSApp activateIgnoringOtherApps:YES];
361 void NativeWidgetMac::Deactivate() {
362   NOTIMPLEMENTED();
365 bool NativeWidgetMac::IsActive() const {
366   // To behave like ::GetActiveWindow on Windows, IsActive() must return the
367   // "active" window attached to the calling application. NSWindow provides
368   // -isKeyWindow and -isMainWindow, but these are system-wide and update
369   // asynchronously. A window can not be main or key on Mac without the
370   // application being active.
371   // Here, define the active window as the frontmost visible window in the
372   // application.
373   // Note that this might not be the keyWindow, even when Chrome is active.
374   // Also note that -[NSApplication orderedWindows] excludes panels and other
375   // "unscriptable" windows, but includes invisible windows.
376   if (!IsVisible())
377     return false;
379   NSWindow* window = GetNativeWindow();
380   for (NSWindow* other_window in [NSApp orderedWindows]) {
381     if ([window isEqual:other_window])
382       return true;
384     if ([other_window isVisible])
385       return false;
386   }
388   return false;
391 void NativeWidgetMac::SetAlwaysOnTop(bool always_on_top) {
392   NOTIMPLEMENTED();
395 bool NativeWidgetMac::IsAlwaysOnTop() const {
396   NOTIMPLEMENTED();
397   return false;
400 void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) {
401   NOTIMPLEMENTED();
404 void NativeWidgetMac::Maximize() {
405   NOTIMPLEMENTED();
408 void NativeWidgetMac::Minimize() {
409   NOTIMPLEMENTED();
412 bool NativeWidgetMac::IsMaximized() const {
413   NOTIMPLEMENTED();
414   return false;
417 bool NativeWidgetMac::IsMinimized() const {
418   NOTIMPLEMENTED();
419   return false;
422 void NativeWidgetMac::Restore() {
423   NOTIMPLEMENTED();
426 void NativeWidgetMac::SetFullscreen(bool fullscreen) {
427   NOTIMPLEMENTED();
430 bool NativeWidgetMac::IsFullscreen() const {
431   NOTIMPLEMENTED();
432   return false;
435 void NativeWidgetMac::SetOpacity(unsigned char opacity) {
436   NOTIMPLEMENTED();
439 void NativeWidgetMac::SetUseDragFrame(bool use_drag_frame) {
440   NOTIMPLEMENTED();
443 void NativeWidgetMac::FlashFrame(bool flash_frame) {
444   NOTIMPLEMENTED();
447 void NativeWidgetMac::RunShellDrag(View* view,
448                                    const ui::OSExchangeData& data,
449                                    const gfx::Point& location,
450                                    int operation,
451                                    ui::DragDropTypes::DragEventSource source) {
452   NOTIMPLEMENTED();
455 void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) {
456   // TODO(tapted): This should use setNeedsDisplayInRect:, once the coordinate
457   // system of |rect| has been converted.
458   [GetNativeView() setNeedsDisplay:YES];
461 void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) {
462   NOTIMPLEMENTED();
465 bool NativeWidgetMac::IsMouseEventsEnabled() const {
466   NOTIMPLEMENTED();
467   return true;
470 void NativeWidgetMac::ClearNativeFocus() {
471   NOTIMPLEMENTED();
474 gfx::Rect NativeWidgetMac::GetWorkAreaBoundsInScreen() const {
475   NOTIMPLEMENTED();
476   return gfx::Rect();
479 Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop(
480     const gfx::Vector2d& drag_offset,
481     Widget::MoveLoopSource source,
482     Widget::MoveLoopEscapeBehavior escape_behavior) {
483   NOTIMPLEMENTED();
484   return Widget::MOVE_LOOP_CANCELED;
487 void NativeWidgetMac::EndMoveLoop() {
488   NOTIMPLEMENTED();
491 void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) {
492   NOTIMPLEMENTED();
495 ui::NativeTheme* NativeWidgetMac::GetNativeTheme() const {
496   return ui::NativeTheme::instance();
499 void NativeWidgetMac::OnRootViewLayout() {
500   NOTIMPLEMENTED();
503 bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const {
504   return false;
507 void NativeWidgetMac::RepostNativeEvent(gfx::NativeEvent native_event) {
508   NOTIMPLEMENTED();
511 ////////////////////////////////////////////////////////////////////////////////
512 // Widget, public:
514 bool Widget::ConvertRect(const Widget* source,
515                          const Widget* target,
516                          gfx::Rect* rect) {
517   return false;
520 namespace internal {
522 ////////////////////////////////////////////////////////////////////////////////
523 // internal::NativeWidgetPrivate, public:
525 // static
526 NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget(
527     internal::NativeWidgetDelegate* delegate) {
528   return new NativeWidgetMac(delegate);
531 // static
532 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView(
533     gfx::NativeView native_view) {
534   return GetNativeWidgetForNativeWindow([native_view window]);
537 // static
538 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
539     gfx::NativeWindow native_window) {
540   id<NSWindowDelegate> window_delegate = [native_window delegate];
541   if ([window_delegate respondsToSelector:@selector(nativeWidgetMac)]) {
542     ViewsNSWindowDelegate* delegate =
543         base::mac::ObjCCastStrict<ViewsNSWindowDelegate>(window_delegate);
544     return [delegate nativeWidgetMac];
545   }
546   return NULL;  // Not created by NativeWidgetMac.
549 // static
550 NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
551     gfx::NativeView native_view) {
552   NativeWidgetPrivate* native_widget =
553       GetNativeWidgetForNativeView(native_view);
554   if (!native_widget)
555     return NULL;
557   for (NativeWidgetPrivate* parent;
558        (parent = GetNativeWidgetForNativeWindow(
559             [native_widget->GetNativeWindow() parentWindow]));
560        native_widget = parent) {
561   }
562   return native_widget;
565 // static
566 void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
567                                              Widget::Widgets* children) {
568   NativeWidgetPrivate* native_widget =
569       GetNativeWidgetForNativeView(native_view);
570   if (!native_widget)
571     return;
573   // Code expects widget for |native_view| to be added to |children|.
574   if (native_widget->GetWidget())
575     children->insert(native_widget->GetWidget());
577   for (NSWindow* child_window : [native_widget->GetNativeWindow() childWindows])
578     GetAllChildWidgets([child_window contentView], children);
581 // static
582 void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view,
583                                              Widget::Widgets* owned) {
584   NOTIMPLEMENTED();
587 // static
588 void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view,
589                                              gfx::NativeView new_parent) {
590   NOTIMPLEMENTED();
593 // static
594 bool NativeWidgetPrivate::IsMouseButtonDown() {
595   return [NSEvent pressedMouseButtons] != 0;
598 // static
599 gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() {
600   NOTIMPLEMENTED();
601   return gfx::FontList();
604 }  // namespace internal
605 }  // namespace views