Mac/Linux: Ensure window size constraints propagate to the window server during Init
[chromium-blink-merge.git] / ui / views / widget / native_widget_mac.mm
blobca8fcb64b12697c0465e57238dd512b3adfb3a02
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 #import "ui/base/cocoa/window_size_constants.h"
13 #include "ui/gfx/font_list.h"
14 #import "ui/gfx/mac/coordinate_conversion.h"
15 #include "ui/native_theme/native_theme.h"
16 #import "ui/views/cocoa/bridged_content_view.h"
17 #import "ui/views/cocoa/bridged_native_widget.h"
18 #import "ui/views/cocoa/native_widget_mac_nswindow.h"
19 #import "ui/views/cocoa/views_nswindow_delegate.h"
20 #include "ui/views/window/native_frame_view.h"
22 namespace views {
23 namespace {
25 NSInteger StyleMaskForParams(const Widget::InitParams& params) {
26   // TODO(tapted): Determine better masks when there are use cases for it.
27   if (params.remove_standard_frame)
28     return NSBorderlessWindowMask;
30   if (params.type == Widget::InitParams::TYPE_WINDOW) {
31     return NSTitledWindowMask | NSClosableWindowMask |
32            NSMiniaturizableWindowMask | NSResizableWindowMask;
33   }
34   return NSBorderlessWindowMask;
37 }  // namespace
39 ////////////////////////////////////////////////////////////////////////////////
40 // NativeWidgetMac, public:
42 NativeWidgetMac::NativeWidgetMac(internal::NativeWidgetDelegate* delegate)
43     : delegate_(delegate),
44       bridge_(new BridgedNativeWidget(this)),
45       ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {
48 NativeWidgetMac::~NativeWidgetMac() {
49   if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
50     delete delegate_;
51   else
52     CloseNow();
55 // static
56 BridgedNativeWidget* NativeWidgetMac::GetBridgeForNativeWindow(
57     gfx::NativeWindow window) {
58   id<NSWindowDelegate> window_delegate = [window delegate];
59   if ([window_delegate respondsToSelector:@selector(nativeWidgetMac)]) {
60     ViewsNSWindowDelegate* delegate =
61         base::mac::ObjCCastStrict<ViewsNSWindowDelegate>(window_delegate);
62     return [delegate nativeWidgetMac]->bridge_.get();
63   }
64   return nullptr;  // Not created by NativeWidgetMac.
67 void NativeWidgetMac::OnWindowWillClose() {
68   delegate_->OnNativeWidgetDestroying();
69   // Note: If closed via CloseNow(), |bridge_| will already be reset. If closed
70   // by the user, or via Close() and a RunLoop, this will reset it.
71   bridge_.reset();
72   delegate_->OnNativeWidgetDestroyed();
73   if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
74     delete this;
77 ////////////////////////////////////////////////////////////////////////////////
78 // NativeWidgetMac, internal::NativeWidgetPrivate implementation:
80 void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) {
81   ownership_ = params.ownership;
82   base::scoped_nsobject<NSWindow> window([CreateNSWindow(params) retain]);
83   [window setReleasedWhenClosed:NO];  // Owned by scoped_nsobject.
84   bridge_->Init(window, params);
86   delegate_->OnNativeWidgetCreated(true);
88   bridge_->SetFocusManager(GetWidget()->GetFocusManager());
90   DCHECK(GetWidget()->GetRootView());
91   bridge_->SetRootView(GetWidget()->GetRootView());
93   // "Infer" must be handled by ViewsDelegate::OnBeforeWidgetInit().
94   DCHECK_NE(Widget::InitParams::INFER_OPACITY, params.opacity);
95   bool translucent = params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW;
96   bridge_->CreateLayer(params.layer_type, translucent);
99 NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() {
100   return new NativeFrameView(GetWidget());
103 bool NativeWidgetMac::ShouldUseNativeFrame() const {
104   return true;
107 bool NativeWidgetMac::ShouldWindowContentsBeTransparent() const {
108   // On Windows, this returns true when Aero is enabled which draws the titlebar
109   // with translucency.
110   return false;
113 void NativeWidgetMac::FrameTypeChanged() {
114   NOTIMPLEMENTED();
117 Widget* NativeWidgetMac::GetWidget() {
118   return delegate_->AsWidget();
121 const Widget* NativeWidgetMac::GetWidget() const {
122   return delegate_->AsWidget();
125 gfx::NativeView NativeWidgetMac::GetNativeView() const {
126   // Returns a BridgedContentView, unless there is no views::RootView set.
127   return [GetNativeWindow() contentView];
130 gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const {
131   return bridge_ ? bridge_->ns_window() : nil;
134 Widget* NativeWidgetMac::GetTopLevelWidget() {
135   NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
136   return native_widget ? native_widget->GetWidget() : NULL;
139 const ui::Compositor* NativeWidgetMac::GetCompositor() const {
140   return bridge_ && bridge_->layer() ? bridge_->layer()->GetCompositor()
141                                      : nullptr;
144 const ui::Layer* NativeWidgetMac::GetLayer() const {
145   return bridge_ ? bridge_->layer() : nullptr;
148 void NativeWidgetMac::ReorderNativeViews() {
149   if (bridge_)
150     bridge_->SetRootView(GetWidget()->GetRootView());
153 void NativeWidgetMac::ViewRemoved(View* view) {
154   NOTIMPLEMENTED();
157 void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) {
158   if (bridge_)
159     bridge_->SetNativeWindowProperty(name, value);
162 void* NativeWidgetMac::GetNativeWindowProperty(const char* name) const {
163   if (bridge_)
164     return bridge_->GetNativeWindowProperty(name);
166   return nullptr;
169 TooltipManager* NativeWidgetMac::GetTooltipManager() const {
170   NOTIMPLEMENTED();
171   return NULL;
174 void NativeWidgetMac::SetCapture() {
175   if (bridge_ && !bridge_->HasCapture())
176     bridge_->AcquireCapture();
179 void NativeWidgetMac::ReleaseCapture() {
180   if (bridge_)
181     bridge_->ReleaseCapture();
184 bool NativeWidgetMac::HasCapture() const {
185   return bridge_ && bridge_->HasCapture();
188 InputMethod* NativeWidgetMac::CreateInputMethod() {
189   return bridge_ ? bridge_->CreateInputMethod() : NULL;
192 internal::InputMethodDelegate* NativeWidgetMac::GetInputMethodDelegate() {
193   return bridge_.get();
196 ui::InputMethod* NativeWidgetMac::GetHostInputMethod() {
197   return bridge_ ? bridge_->GetHostInputMethod() : NULL;
200 void NativeWidgetMac::CenterWindow(const gfx::Size& size) {
201   SetSize(
202       BridgedNativeWidget::GetWindowSizeForClientSize(GetNativeWindow(), size));
203   // Note that this is not the precise center of screen, but it is the standard
204   // location for windows like dialogs to appear on screen for Mac.
205   // TODO(tapted): If there is a parent window, center in that instead.
206   [GetNativeWindow() center];
209 void NativeWidgetMac::GetWindowPlacement(
210     gfx::Rect* bounds,
211     ui::WindowShowState* show_state) const {
212   *bounds = GetRestoredBounds();
213   if (IsFullscreen())
214     *show_state = ui::SHOW_STATE_FULLSCREEN;
215   else if (IsMinimized())
216     *show_state = ui::SHOW_STATE_MINIMIZED;
217   else
218     *show_state = ui::SHOW_STATE_NORMAL;
221 bool NativeWidgetMac::SetWindowTitle(const base::string16& title) {
222   NSWindow* window = GetNativeWindow();
223   NSString* current_title = [window title];
224   NSString* new_title = SysUTF16ToNSString(title);
225   if ([current_title isEqualToString:new_title])
226     return false;
228   [window setTitle:new_title];
229   return true;
232 void NativeWidgetMac::SetWindowIcons(const gfx::ImageSkia& window_icon,
233                                      const gfx::ImageSkia& app_icon) {
234   NOTIMPLEMENTED();
237 void NativeWidgetMac::InitModalType(ui::ModalType modal_type) {
238   if (modal_type == ui::MODAL_TYPE_NONE)
239     return;
241   // System modal windows not implemented (or used) on Mac.
242   DCHECK_NE(ui::MODAL_TYPE_SYSTEM, modal_type);
243   DCHECK(bridge_->parent());
244   // Everyhing happens upon show.
247 gfx::Rect NativeWidgetMac::GetWindowBoundsInScreen() const {
248   return gfx::ScreenRectFromNSRect([GetNativeWindow() frame]);
251 gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const {
252   NSWindow* window = GetNativeWindow();
253   return gfx::ScreenRectFromNSRect(
254       [window contentRectForFrameRect:[window frame]]);
257 gfx::Rect NativeWidgetMac::GetRestoredBounds() const {
258   return bridge_ ? bridge_->GetRestoredBounds() : gfx::Rect();
261 void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) {
262   if (bridge_)
263     bridge_->SetBounds(bounds);
266 void NativeWidgetMac::SetSize(const gfx::Size& size) {
267   // Ensure the top-left corner stays in-place (rather than the bottom-left,
268   // which -[NSWindow setContentSize:] would do).
269   SetBounds(gfx::Rect(GetWindowBoundsInScreen().origin(), size));
272 void NativeWidgetMac::StackAbove(gfx::NativeView native_view) {
273   NOTIMPLEMENTED();
276 void NativeWidgetMac::StackAtTop() {
277   NOTIMPLEMENTED();
280 void NativeWidgetMac::StackBelow(gfx::NativeView native_view) {
281   NOTIMPLEMENTED();
284 void NativeWidgetMac::SetShape(gfx::NativeRegion shape) {
285   NOTIMPLEMENTED();
288 void NativeWidgetMac::Close() {
289   if (!bridge_)
290     return;
292   if (delegate_->IsModal()) {
293     // Sheets can't be closed normally. This starts the sheet closing. Once the
294     // sheet has finished animating, it will call sheetDidEnd: on the parent
295     // window's delegate. Note it still needs to be asynchronous, since code
296     // calling Widget::Close() doesn't expect things to be deleted upon return.
297     [NSApp performSelector:@selector(endSheet:)
298                 withObject:GetNativeWindow()
299                 afterDelay:0];
300     return;
301   }
303   // Clear the view early to suppress repaints.
304   bridge_->SetRootView(NULL);
306   NSWindow* window = GetNativeWindow();
307   // Calling performClose: will momentarily highlight the close button, but
308   // AppKit will reject it if there is no close button.
309   SEL close_selector = ([window styleMask] & NSClosableWindowMask)
310                            ? @selector(performClose:)
311                            : @selector(close);
312   [window performSelector:close_selector withObject:nil afterDelay:0];
315 void NativeWidgetMac::CloseNow() {
316   // Reset |bridge_| to NULL before destroying it.
317   scoped_ptr<BridgedNativeWidget> bridge(bridge_.Pass());
320 void NativeWidgetMac::Show() {
321   ShowWithWindowState(ui::SHOW_STATE_NORMAL);
324 void NativeWidgetMac::Hide() {
325   if (!bridge_)
326     return;
328   bridge_->SetVisibilityState(BridgedNativeWidget::HIDE_WINDOW);
331 void NativeWidgetMac::ShowMaximizedWithBounds(
332     const gfx::Rect& restored_bounds) {
333   NOTIMPLEMENTED();
336 void NativeWidgetMac::ShowWithWindowState(ui::WindowShowState state) {
337   if (!bridge_)
338     return;
340   switch (state) {
341     case ui::SHOW_STATE_DEFAULT:
342     case ui::SHOW_STATE_NORMAL:
343     case ui::SHOW_STATE_INACTIVE:
344       break;
345     case ui::SHOW_STATE_MINIMIZED:
346     case ui::SHOW_STATE_MAXIMIZED:
347     case ui::SHOW_STATE_FULLSCREEN:
348       NOTIMPLEMENTED();
349       break;
350     case ui::SHOW_STATE_END:
351       NOTREACHED();
352       break;
353   }
354   bridge_->SetVisibilityState(state == ui::SHOW_STATE_INACTIVE
355       ? BridgedNativeWidget::SHOW_INACTIVE
356       : BridgedNativeWidget::SHOW_AND_ACTIVATE_WINDOW);
359 bool NativeWidgetMac::IsVisible() const {
360   return bridge_ && bridge_->window_visible();
363 void NativeWidgetMac::Activate() {
364   if (!bridge_)
365     return;
367   bridge_->SetVisibilityState(BridgedNativeWidget::SHOW_AND_ACTIVATE_WINDOW);
370 void NativeWidgetMac::Deactivate() {
371   NOTIMPLEMENTED();
374 bool NativeWidgetMac::IsActive() const {
375   return [GetNativeWindow() isKeyWindow];
378 void NativeWidgetMac::SetAlwaysOnTop(bool always_on_top) {
379   NOTIMPLEMENTED();
382 bool NativeWidgetMac::IsAlwaysOnTop() const {
383   NOTIMPLEMENTED();
384   return false;
387 void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) {
388   NOTIMPLEMENTED();
391 void NativeWidgetMac::Maximize() {
392   NOTIMPLEMENTED();  // See IsMaximized().
395 void NativeWidgetMac::Minimize() {
396   NSWindow* window = GetNativeWindow();
397   // Calling performMiniaturize: will momentarily highlight the button, but
398   // AppKit will reject it if there is no miniaturize button.
399   if ([window styleMask] & NSMiniaturizableWindowMask)
400     [window performMiniaturize:nil];
401   else
402     [window miniaturize:nil];
405 bool NativeWidgetMac::IsMaximized() const {
406   // The window frame isn't altered on Mac unless going fullscreen. The green
407   // "+" button just makes the window bigger. So, always false.
408   return false;
411 bool NativeWidgetMac::IsMinimized() const {
412   return [GetNativeWindow() isMiniaturized];
415 void NativeWidgetMac::Restore() {
416   [GetNativeWindow() deminiaturize:nil];
419 void NativeWidgetMac::SetFullscreen(bool fullscreen) {
420   if (!bridge_ || fullscreen == IsFullscreen())
421     return;
423   bridge_->ToggleDesiredFullscreenState();
426 bool NativeWidgetMac::IsFullscreen() const {
427   return bridge_ && bridge_->target_fullscreen_state();
430 void NativeWidgetMac::SetOpacity(unsigned char opacity) {
431   NOTIMPLEMENTED();
434 void NativeWidgetMac::SetUseDragFrame(bool use_drag_frame) {
435   NOTIMPLEMENTED();
438 void NativeWidgetMac::FlashFrame(bool flash_frame) {
439   NOTIMPLEMENTED();
442 void NativeWidgetMac::RunShellDrag(View* view,
443                                    const ui::OSExchangeData& data,
444                                    const gfx::Point& location,
445                                    int operation,
446                                    ui::DragDropTypes::DragEventSource source) {
447   NOTIMPLEMENTED();
450 void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) {
451   // TODO(tapted): This should use setNeedsDisplayInRect:, once the coordinate
452   // system of |rect| has been converted.
453   [GetNativeView() setNeedsDisplay:YES];
454   if (bridge_ && bridge_->layer())
455     bridge_->layer()->SchedulePaint(rect);
458 void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) {
459   if (bridge_)
460     bridge_->SetCursor(cursor);
463 bool NativeWidgetMac::IsMouseEventsEnabled() const {
464   NOTIMPLEMENTED();
465   return true;
468 void NativeWidgetMac::ClearNativeFocus() {
469   // To quote DesktopWindowTreeHostX11, "This method is weird and misnamed."
470   // The goal is to set focus to the content window, thereby removing focus from
471   // any NSView in the window that doesn't belong to toolkit-views.
472   [GetNativeWindow() makeFirstResponder:GetNativeView()];
475 gfx::Rect NativeWidgetMac::GetWorkAreaBoundsInScreen() const {
476   NOTIMPLEMENTED();
477   return gfx::Rect();
480 Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop(
481     const gfx::Vector2d& drag_offset,
482     Widget::MoveLoopSource source,
483     Widget::MoveLoopEscapeBehavior escape_behavior) {
484   NOTIMPLEMENTED();
485   return Widget::MOVE_LOOP_CANCELED;
488 void NativeWidgetMac::EndMoveLoop() {
489   NOTIMPLEMENTED();
492 void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) {
493   NOTIMPLEMENTED();
496 void NativeWidgetMac::SetVisibilityAnimationDuration(
497     const base::TimeDelta& duration) {
498   NOTIMPLEMENTED();
501 void NativeWidgetMac::SetVisibilityAnimationTransition(
502     Widget::VisibilityTransition transition) {
503   NOTIMPLEMENTED();
506 ui::NativeTheme* NativeWidgetMac::GetNativeTheme() const {
507   return ui::NativeTheme::instance();
510 void NativeWidgetMac::OnRootViewLayout() {
511   // Ensure possible changes to the non-client view (e.g. Minimum/Maximum size)
512   // propagate through to the NSWindow properties.
513   OnSizeConstraintsChanged();
516 bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const {
517   return false;
520 void NativeWidgetMac::OnSizeConstraintsChanged() {
521   bridge_->OnSizeConstraintsChanged();
524 void NativeWidgetMac::RepostNativeEvent(gfx::NativeEvent native_event) {
525   NOTIMPLEMENTED();
528 ////////////////////////////////////////////////////////////////////////////////
529 // NativeWidgetMac, protected:
531 NSWindow* NativeWidgetMac::CreateNSWindow(const Widget::InitParams& params) {
532   return [[[NativeWidgetMacNSWindow alloc]
533       initWithContentRect:ui::kWindowSizeDeterminedLater
534                 styleMask:StyleMaskForParams(params)
535                   backing:NSBackingStoreBuffered
536                     defer:YES] autorelease];
539 ////////////////////////////////////////////////////////////////////////////////
540 // Widget, public:
542 bool Widget::ConvertRect(const Widget* source,
543                          const Widget* target,
544                          gfx::Rect* rect) {
545   return false;
548 namespace internal {
550 ////////////////////////////////////////////////////////////////////////////////
551 // internal::NativeWidgetPrivate, public:
553 // static
554 NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget(
555     internal::NativeWidgetDelegate* delegate) {
556   return new NativeWidgetMac(delegate);
559 // static
560 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView(
561     gfx::NativeView native_view) {
562   return GetNativeWidgetForNativeWindow([native_view window]);
565 // static
566 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
567     gfx::NativeWindow native_window) {
568   id<NSWindowDelegate> window_delegate = [native_window delegate];
569   if ([window_delegate respondsToSelector:@selector(nativeWidgetMac)]) {
570     ViewsNSWindowDelegate* delegate =
571         base::mac::ObjCCastStrict<ViewsNSWindowDelegate>(window_delegate);
572     return [delegate nativeWidgetMac];
573   }
574   return NULL;  // Not created by NativeWidgetMac.
577 // static
578 NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
579     gfx::NativeView native_view) {
580   BridgedNativeWidget* bridge =
581       NativeWidgetMac::GetBridgeForNativeWindow([native_view window]);
582   if (!bridge)
583     return NULL;
585   for (BridgedNativeWidget* parent;
586        (parent = bridge->parent());
587        bridge = parent) {
588   }
589   return bridge->native_widget_mac();
592 // static
593 void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
594                                              Widget::Widgets* children) {
595   BridgedNativeWidget* bridge =
596       NativeWidgetMac::GetBridgeForNativeWindow([native_view window]);
597   if (!bridge)
598     return;
600   // If |native_view| is a subview of the contentView, it will share an
601   // NSWindow, but will itself be a native child of the Widget. That is, adding
602   // bridge->..->GetWidget() to |children| would be adding the _parent_ of
603   // |native_view|, not the Widget for |native_view|. |native_view| doesn't have
604   // a corresponding Widget of its own in this case (and so can't have Widget
605   // children of its own on Mac).
606   if (bridge->ns_view() != native_view)
607     return;
609   // Code expects widget for |native_view| to be added to |children|.
610   if (bridge->native_widget_mac()->GetWidget())
611     children->insert(bridge->native_widget_mac()->GetWidget());
613   for (BridgedNativeWidget* child : bridge->child_windows())
614     GetAllChildWidgets(child->ns_view(), children);
617 // static
618 void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view,
619                                              Widget::Widgets* owned) {
620   NOTIMPLEMENTED();
623 // static
624 void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view,
625                                              gfx::NativeView new_parent) {
626   NOTIMPLEMENTED();
629 // static
630 bool NativeWidgetPrivate::IsMouseButtonDown() {
631   return [NSEvent pressedMouseButtons] != 0;
634 // static
635 gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() {
636   NOTIMPLEMENTED();
637   return gfx::FontList();
640 }  // namespace internal
641 }  // namespace views