Home | History | Annotate | Download | only in widget
      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.
      4 
      5 #include "ui/views/widget/native_widget_mac.h"
      6 
      7 #import <Cocoa/Cocoa.h>
      8 
      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"
     19 
     20 @interface NativeWidgetMacNSWindow : NSWindow
     21 @end
     22 
     23 @implementation NativeWidgetMacNSWindow
     24 
     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;
     29 }
     30 
     31 - (BOOL)canBecomeMainWindow {
     32   return YES;
     33 }
     34 
     35 @end
     36 
     37 namespace views {
     38 namespace {
     39 
     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;
     44 
     45   if (params.type == Widget::InitParams::TYPE_WINDOW) {
     46     return NSTitledWindowMask | NSClosableWindowMask |
     47            NSMiniaturizableWindowMask | NSResizableWindowMask;
     48   }
     49   return NSBorderlessWindowMask;
     50 }
     51 
     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;
     58 
     59   if (NSHeight(content_rect) == 0)
     60     content_rect.size.height = 1;
     61 
     62   return content_rect;
     63 }
     64 
     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));
     69 }
     70 
     71 }  // namespace
     72 
     73 ////////////////////////////////////////////////////////////////////////////////
     74 // NativeWidgetMac, public:
     75 
     76 NativeWidgetMac::NativeWidgetMac(internal::NativeWidgetDelegate* delegate)
     77     : delegate_(delegate),
     78       bridge_(new BridgedNativeWidget(this)),
     79       ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {
     80 }
     81 
     82 NativeWidgetMac::~NativeWidgetMac() {
     83   if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
     84     delete delegate_;
     85   else
     86     CloseNow();
     87 }
     88 
     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;
     97 }
     98 
     99 ////////////////////////////////////////////////////////////////////////////////
    100 // NativeWidgetMac, internal::NativeWidgetPrivate implementation:
    101 
    102 void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) {
    103   ownership_ = params.ownership;
    104 
    105   NSInteger style_mask = StyleMaskForParams(params);
    106   NSRect content_rect = ValidateContentRect(
    107       [NSWindow contentRectForFrameRect:gfx::ScreenRectToNSRect(params.bounds)
    108                               styleMask:style_mask]);
    109 
    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);
    117 
    118   delegate_->OnNativeWidgetCreated(true);
    119 
    120   bridge_->SetFocusManager(GetWidget()->GetFocusManager());
    121 
    122   DCHECK(GetWidget()->GetRootView());
    123   bridge_->SetRootView(GetWidget()->GetRootView());
    124 }
    125 
    126 NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() {
    127   return new NativeFrameView(GetWidget());
    128 }
    129 
    130 bool NativeWidgetMac::ShouldUseNativeFrame() const {
    131   return true;
    132 }
    133 
    134 bool NativeWidgetMac::ShouldWindowContentsBeTransparent() const {
    135   NOTIMPLEMENTED();
    136   return false;
    137 }
    138 
    139 void NativeWidgetMac::FrameTypeChanged() {
    140   NOTIMPLEMENTED();
    141 }
    142 
    143 Widget* NativeWidgetMac::GetWidget() {
    144   return delegate_->AsWidget();
    145 }
    146 
    147 const Widget* NativeWidgetMac::GetWidget() const {
    148   return delegate_->AsWidget();
    149 }
    150 
    151 gfx::NativeView NativeWidgetMac::GetNativeView() const {
    152   // Returns a BridgedContentView, unless there is no views::RootView set.
    153   return [GetNativeWindow() contentView];
    154 }
    155 
    156 gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const {
    157   return bridge_ ? bridge_->ns_window() : nil;
    158 }
    159 
    160 Widget* NativeWidgetMac::GetTopLevelWidget() {
    161   NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
    162   return native_widget ? native_widget->GetWidget() : NULL;
    163 }
    164 
    165 const ui::Compositor* NativeWidgetMac::GetCompositor() const {
    166   NOTIMPLEMENTED();
    167   return NULL;
    168 }
    169 
    170 ui::Compositor* NativeWidgetMac::GetCompositor() {
    171   NOTIMPLEMENTED();
    172   return NULL;
    173 }
    174 
    175 ui::Layer* NativeWidgetMac::GetLayer() {
    176   NOTIMPLEMENTED();
    177   return NULL;
    178 }
    179 
    180 void NativeWidgetMac::ReorderNativeViews() {
    181   if (bridge_)
    182     bridge_->SetRootView(GetWidget()->GetRootView());
    183 }
    184 
    185 void NativeWidgetMac::ViewRemoved(View* view) {
    186   NOTIMPLEMENTED();
    187 }
    188 
    189 void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) {
    190   NOTIMPLEMENTED();
    191 }
    192 
    193 void* NativeWidgetMac::GetNativeWindowProperty(const char* name) const {
    194   NOTIMPLEMENTED();
    195   return NULL;
    196 }
    197 
    198 TooltipManager* NativeWidgetMac::GetTooltipManager() const {
    199   NOTIMPLEMENTED();
    200   return NULL;
    201 }
    202 
    203 void NativeWidgetMac::SetCapture() {
    204   NOTIMPLEMENTED();
    205 }
    206 
    207 void NativeWidgetMac::ReleaseCapture() {
    208   NOTIMPLEMENTED();
    209 }
    210 
    211 bool NativeWidgetMac::HasCapture() const {
    212   NOTIMPLEMENTED();
    213   return false;
    214 }
    215 
    216 InputMethod* NativeWidgetMac::CreateInputMethod() {
    217   return bridge_ ? bridge_->CreateInputMethod() : NULL;
    218 }
    219 
    220 internal::InputMethodDelegate* NativeWidgetMac::GetInputMethodDelegate() {
    221   return bridge_.get();
    222 }
    223 
    224 ui::InputMethod* NativeWidgetMac::GetHostInputMethod() {
    225   return bridge_ ? bridge_->GetHostInputMethod() : NULL;
    226 }
    227 
    228 void NativeWidgetMac::CenterWindow(const gfx::Size& size) {
    229   SetSize(WindowSizeForClientAreaSize(GetNativeWindow(), size));
    230   // Note that this is not the precise center of screen, but it is the standard
    231   // location for windows like dialogs to appear on screen for Mac.
    232   // TODO(tapted): If there is a parent window, center in that instead.
    233   [GetNativeWindow() center];
    234 }
    235 
    236 void NativeWidgetMac::GetWindowPlacement(gfx::Rect* bounds,
    237                                          ui::WindowShowState* maximized) const {
    238   NOTIMPLEMENTED();
    239 }
    240 
    241 bool NativeWidgetMac::SetWindowTitle(const base::string16& title) {
    242   NSWindow* window = GetNativeWindow();
    243   NSString* current_title = [window title];
    244   NSString* new_title = SysUTF16ToNSString(title);
    245   if ([current_title isEqualToString:new_title])
    246     return false;
    247 
    248   [window setTitle:new_title];
    249   return true;
    250 }
    251 
    252 void NativeWidgetMac::SetWindowIcons(const gfx::ImageSkia& window_icon,
    253                                      const gfx::ImageSkia& app_icon) {
    254   NOTIMPLEMENTED();
    255 }
    256 
    257 void NativeWidgetMac::InitModalType(ui::ModalType modal_type) {
    258   NOTIMPLEMENTED();
    259 }
    260 
    261 gfx::Rect NativeWidgetMac::GetWindowBoundsInScreen() const {
    262   return gfx::ScreenRectFromNSRect([GetNativeWindow() frame]);
    263 }
    264 
    265 gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const {
    266   NSWindow* window = GetNativeWindow();
    267   return gfx::ScreenRectFromNSRect(
    268       [window contentRectForFrameRect:[window frame]]);
    269 }
    270 
    271 gfx::Rect NativeWidgetMac::GetRestoredBounds() const {
    272   NOTIMPLEMENTED();
    273   return gfx::Rect();
    274 }
    275 
    276 void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) {
    277   [GetNativeWindow() setFrame:gfx::ScreenRectToNSRect(bounds)
    278                       display:YES
    279                       animate:NO];
    280 }
    281 
    282 void NativeWidgetMac::SetSize(const gfx::Size& size) {
    283   // Ensure the top-left corner stays in-place (rather than the bottom-left,
    284   // which -[NSWindow setContentSize:] would do).
    285   SetBounds(gfx::Rect(GetWindowBoundsInScreen().origin(), size));
    286 }
    287 
    288 void NativeWidgetMac::StackAbove(gfx::NativeView native_view) {
    289   NOTIMPLEMENTED();
    290 }
    291 
    292 void NativeWidgetMac::StackAtTop() {
    293   NOTIMPLEMENTED();
    294 }
    295 
    296 void NativeWidgetMac::StackBelow(gfx::NativeView native_view) {
    297   NOTIMPLEMENTED();
    298 }
    299 
    300 void NativeWidgetMac::SetShape(gfx::NativeRegion shape) {
    301   NOTIMPLEMENTED();
    302 }
    303 
    304 void NativeWidgetMac::Close() {
    305   NSWindow* window = GetNativeWindow();
    306   // Calling performClose: will momentarily highlight the close button, but
    307   // AppKit will reject it if there is no close button.
    308   SEL close_selector = ([window styleMask] & NSClosableWindowMask)
    309                            ? @selector(performClose:)
    310                            : @selector(close);
    311   [window performSelector:close_selector withObject:nil afterDelay:0];
    312 }
    313 
    314 void NativeWidgetMac::CloseNow() {
    315   // Reset |bridge_| to NULL before destroying it.
    316   scoped_ptr<BridgedNativeWidget> bridge(bridge_.Pass());
    317 }
    318 
    319 void NativeWidgetMac::Show() {
    320   ShowWithWindowState(ui::SHOW_STATE_NORMAL);
    321 }
    322 
    323 void NativeWidgetMac::Hide() {
    324   NOTIMPLEMENTED();
    325 }
    326 
    327 void NativeWidgetMac::ShowMaximizedWithBounds(
    328     const gfx::Rect& restored_bounds) {
    329   NOTIMPLEMENTED();
    330 }
    331 
    332 void NativeWidgetMac::ShowWithWindowState(ui::WindowShowState state) {
    333   switch (state) {
    334     case ui::SHOW_STATE_DEFAULT:
    335     case ui::SHOW_STATE_NORMAL:
    336     case ui::SHOW_STATE_INACTIVE:
    337       break;
    338     case ui::SHOW_STATE_MINIMIZED:
    339     case ui::SHOW_STATE_MAXIMIZED:
    340     case ui::SHOW_STATE_FULLSCREEN:
    341       NOTIMPLEMENTED();
    342       break;
    343     case ui::SHOW_STATE_END:
    344       NOTREACHED();
    345       break;
    346   }
    347   if (state == ui::SHOW_STATE_INACTIVE) {
    348     if (!IsVisible())
    349       [GetNativeWindow() orderBack:nil];
    350   } else {
    351     Activate();
    352   }
    353 }
    354 
    355 bool NativeWidgetMac::IsVisible() const {
    356   return [GetNativeWindow() isVisible];
    357 }
    358 
    359 void NativeWidgetMac::Activate() {
    360   [GetNativeWindow() makeKeyAndOrderFront:nil];
    361   [NSApp activateIgnoringOtherApps:YES];
    362 }
    363 
    364 void NativeWidgetMac::Deactivate() {
    365   NOTIMPLEMENTED();
    366 }
    367 
    368 bool NativeWidgetMac::IsActive() const {
    369   // To behave like ::GetActiveWindow on Windows, IsActive() must return the
    370   // "active" window attached to the calling application. NSWindow provides
    371   // -isKeyWindow and -isMainWindow, but these are system-wide and update
    372   // asynchronously. A window can not be main or key on Mac without the
    373   // application being active.
    374   // Here, define the active window as the frontmost visible window in the
    375   // application.
    376   // Note that this might not be the keyWindow, even when Chrome is active.
    377   // Also note that -[NSApplication orderedWindows] excludes panels and other
    378   // "unscriptable" windows, but includes invisible windows.
    379   if (!IsVisible())
    380     return false;
    381 
    382   NSWindow* window = GetNativeWindow();
    383   for (NSWindow* other_window in [NSApp orderedWindows]) {
    384     if ([window isEqual:other_window])
    385       return true;
    386 
    387     if ([other_window isVisible])
    388       return false;
    389   }
    390 
    391   return false;
    392 }
    393 
    394 void NativeWidgetMac::SetAlwaysOnTop(bool always_on_top) {
    395   NOTIMPLEMENTED();
    396 }
    397 
    398 bool NativeWidgetMac::IsAlwaysOnTop() const {
    399   NOTIMPLEMENTED();
    400   return false;
    401 }
    402 
    403 void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) {
    404   NOTIMPLEMENTED();
    405 }
    406 
    407 void NativeWidgetMac::Maximize() {
    408   NOTIMPLEMENTED();
    409 }
    410 
    411 void NativeWidgetMac::Minimize() {
    412   NOTIMPLEMENTED();
    413 }
    414 
    415 bool NativeWidgetMac::IsMaximized() const {
    416   NOTIMPLEMENTED();
    417   return false;
    418 }
    419 
    420 bool NativeWidgetMac::IsMinimized() const {
    421   NOTIMPLEMENTED();
    422   return false;
    423 }
    424 
    425 void NativeWidgetMac::Restore() {
    426   NOTIMPLEMENTED();
    427 }
    428 
    429 void NativeWidgetMac::SetFullscreen(bool fullscreen) {
    430   NOTIMPLEMENTED();
    431 }
    432 
    433 bool NativeWidgetMac::IsFullscreen() const {
    434   NOTIMPLEMENTED();
    435   return false;
    436 }
    437 
    438 void NativeWidgetMac::SetOpacity(unsigned char opacity) {
    439   NOTIMPLEMENTED();
    440 }
    441 
    442 void NativeWidgetMac::SetUseDragFrame(bool use_drag_frame) {
    443   NOTIMPLEMENTED();
    444 }
    445 
    446 void NativeWidgetMac::FlashFrame(bool flash_frame) {
    447   NOTIMPLEMENTED();
    448 }
    449 
    450 void NativeWidgetMac::RunShellDrag(View* view,
    451                                    const ui::OSExchangeData& data,
    452                                    const gfx::Point& location,
    453                                    int operation,
    454                                    ui::DragDropTypes::DragEventSource source) {
    455   NOTIMPLEMENTED();
    456 }
    457 
    458 void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) {
    459   // TODO(tapted): This should use setNeedsDisplayInRect:, once the coordinate
    460   // system of |rect| has been converted.
    461   [GetNativeView() setNeedsDisplay:YES];
    462 }
    463 
    464 void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) {
    465   NOTIMPLEMENTED();
    466 }
    467 
    468 bool NativeWidgetMac::IsMouseEventsEnabled() const {
    469   NOTIMPLEMENTED();
    470   return true;
    471 }
    472 
    473 void NativeWidgetMac::ClearNativeFocus() {
    474   NOTIMPLEMENTED();
    475 }
    476 
    477 gfx::Rect NativeWidgetMac::GetWorkAreaBoundsInScreen() const {
    478   NOTIMPLEMENTED();
    479   return gfx::Rect();
    480 }
    481 
    482 Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop(
    483     const gfx::Vector2d& drag_offset,
    484     Widget::MoveLoopSource source,
    485     Widget::MoveLoopEscapeBehavior escape_behavior) {
    486   NOTIMPLEMENTED();
    487   return Widget::MOVE_LOOP_CANCELED;
    488 }
    489 
    490 void NativeWidgetMac::EndMoveLoop() {
    491   NOTIMPLEMENTED();
    492 }
    493 
    494 void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) {
    495   NOTIMPLEMENTED();
    496 }
    497 
    498 ui::NativeTheme* NativeWidgetMac::GetNativeTheme() const {
    499   return ui::NativeTheme::instance();
    500 }
    501 
    502 void NativeWidgetMac::OnRootViewLayout() {
    503   NOTIMPLEMENTED();
    504 }
    505 
    506 bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const {
    507   return false;
    508 }
    509 
    510 void NativeWidgetMac::OnSizeConstraintsChanged() {
    511   NOTIMPLEMENTED();
    512 }
    513 
    514 void NativeWidgetMac::RepostNativeEvent(gfx::NativeEvent native_event) {
    515   NOTIMPLEMENTED();
    516 }
    517 
    518 ////////////////////////////////////////////////////////////////////////////////
    519 // Widget, public:
    520 
    521 bool Widget::ConvertRect(const Widget* source,
    522                          const Widget* target,
    523                          gfx::Rect* rect) {
    524   return false;
    525 }
    526 
    527 namespace internal {
    528 
    529 ////////////////////////////////////////////////////////////////////////////////
    530 // internal::NativeWidgetPrivate, public:
    531 
    532 // static
    533 NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget(
    534     internal::NativeWidgetDelegate* delegate) {
    535   return new NativeWidgetMac(delegate);
    536 }
    537 
    538 // static
    539 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView(
    540     gfx::NativeView native_view) {
    541   return GetNativeWidgetForNativeWindow([native_view window]);
    542 }
    543 
    544 // static
    545 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
    546     gfx::NativeWindow native_window) {
    547   id<NSWindowDelegate> window_delegate = [native_window delegate];
    548   if ([window_delegate respondsToSelector:@selector(nativeWidgetMac)]) {
    549     ViewsNSWindowDelegate* delegate =
    550         base::mac::ObjCCastStrict<ViewsNSWindowDelegate>(window_delegate);
    551     return [delegate nativeWidgetMac];
    552   }
    553   return NULL;  // Not created by NativeWidgetMac.
    554 }
    555 
    556 // static
    557 NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
    558     gfx::NativeView native_view) {
    559   NativeWidgetPrivate* native_widget =
    560       GetNativeWidgetForNativeView(native_view);
    561   if (!native_widget)
    562     return NULL;
    563 
    564   for (NativeWidgetPrivate* parent;
    565        (parent = GetNativeWidgetForNativeWindow(
    566             [native_widget->GetNativeWindow() parentWindow]));
    567        native_widget = parent) {
    568   }
    569   return native_widget;
    570 }
    571 
    572 // static
    573 void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
    574                                              Widget::Widgets* children) {
    575   NativeWidgetPrivate* native_widget =
    576       GetNativeWidgetForNativeView(native_view);
    577   if (!native_widget)
    578     return;
    579 
    580   // Code expects widget for |native_view| to be added to |children|.
    581   if (native_widget->GetWidget())
    582     children->insert(native_widget->GetWidget());
    583 
    584   for (NSWindow* child_window : [native_widget->GetNativeWindow() childWindows])
    585     GetAllChildWidgets([child_window contentView], children);
    586 }
    587 
    588 // static
    589 void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view,
    590                                              Widget::Widgets* owned) {
    591   NOTIMPLEMENTED();
    592 }
    593 
    594 // static
    595 void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view,
    596                                              gfx::NativeView new_parent) {
    597   NOTIMPLEMENTED();
    598 }
    599 
    600 // static
    601 bool NativeWidgetPrivate::IsMouseButtonDown() {
    602   return [NSEvent pressedMouseButtons] != 0;
    603 }
    604 
    605 // static
    606 gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() {
    607   NOTIMPLEMENTED();
    608   return gfx::FontList();
    609 }
    610 
    611 }  // namespace internal
    612 }  // namespace views
    613