Home | History | Annotate | Download | only in cocoa
      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 #import "ui/views/cocoa/bridged_native_widget.h"
      6 
      7 #include "base/logging.h"
      8 #include "ui/base/ime/input_method.h"
      9 #include "ui/base/ime/input_method_factory.h"
     10 #include "ui/base/ui_base_switches_util.h"
     11 #import "ui/views/cocoa/bridged_content_view.h"
     12 #import "ui/views/cocoa/views_nswindow_delegate.h"
     13 #include "ui/views/widget/native_widget_mac.h"
     14 #include "ui/views/ime/input_method_bridge.h"
     15 #include "ui/views/ime/null_input_method.h"
     16 #include "ui/views/view.h"
     17 #include "ui/views/widget/widget.h"
     18 
     19 namespace views {
     20 
     21 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent)
     22     : native_widget_mac_(parent), focus_manager_(NULL) {
     23   DCHECK(parent);
     24   window_delegate_.reset(
     25       [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]);
     26 }
     27 
     28 BridgedNativeWidget::~BridgedNativeWidget() {
     29   RemoveOrDestroyChildren();
     30   SetFocusManager(NULL);
     31   SetRootView(NULL);
     32   if ([window_ delegate]) {
     33     // If the delegate is still set, it means OnWindowWillClose has not been
     34     // called and the window is still open. Calling -[NSWindow close] will
     35     // synchronously call OnWindowWillClose and notify NativeWidgetMac.
     36     [window_ close];
     37   }
     38   DCHECK(![window_ delegate]);
     39 }
     40 
     41 void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window,
     42                                const Widget::InitParams& params) {
     43   DCHECK(!window_);
     44   window_.swap(window);
     45   [window_ setDelegate:window_delegate_];
     46 
     47   if (params.parent) {
     48     // Use NSWindow to manage child windows. This won't automatically close them
     49     // but it will maintain relative positioning of the window layer and origin.
     50     [[params.parent window] addChildWindow:window_ ordered:NSWindowAbove];
     51   }
     52 }
     53 
     54 void BridgedNativeWidget::SetFocusManager(FocusManager* focus_manager) {
     55   if (focus_manager_ == focus_manager)
     56     return;
     57 
     58   if (focus_manager_)
     59     focus_manager_->RemoveFocusChangeListener(this);
     60 
     61   if (focus_manager)
     62     focus_manager->AddFocusChangeListener(this);
     63 
     64   focus_manager_ = focus_manager;
     65 }
     66 
     67 void BridgedNativeWidget::SetRootView(views::View* view) {
     68   if (view == [bridged_view_ hostedView])
     69     return;
     70 
     71   [bridged_view_ clearView];
     72   bridged_view_.reset();
     73   // Note that there can still be references to the old |bridged_view_|
     74   // floating around in Cocoa libraries at this point. However, references to
     75   // the old views::View will be gone, so any method calls will become no-ops.
     76 
     77   if (view) {
     78     bridged_view_.reset([[BridgedContentView alloc] initWithView:view]);
     79     // Objective C initializers can return nil. However, if |view| is non-NULL
     80     // this should be treated as an error and caught early.
     81     CHECK(bridged_view_);
     82   }
     83   [window_ setContentView:bridged_view_];
     84 }
     85 
     86 void BridgedNativeWidget::OnWindowWillClose() {
     87   [[window_ parentWindow] removeChildWindow:window_];
     88   [window_ setDelegate:nil];
     89   native_widget_mac_->OnWindowWillClose();
     90 }
     91 
     92 InputMethod* BridgedNativeWidget::CreateInputMethod() {
     93   if (switches::IsTextInputFocusManagerEnabled())
     94     return new NullInputMethod();
     95 
     96   return new InputMethodBridge(this, GetHostInputMethod(), true);
     97 }
     98 
     99 ui::InputMethod* BridgedNativeWidget::GetHostInputMethod() {
    100   if (!input_method_) {
    101     // Delegate is NULL because Mac IME does not need DispatchKeyEventPostIME
    102     // callbacks.
    103     input_method_ = ui::CreateInputMethod(NULL, nil);
    104   }
    105   return input_method_.get();
    106 }
    107 
    108 ////////////////////////////////////////////////////////////////////////////////
    109 // BridgedNativeWidget, internal::InputMethodDelegate:
    110 
    111 void BridgedNativeWidget::DispatchKeyEventPostIME(const ui::KeyEvent& key) {
    112   // Mac key events don't go through this, but some unit tests that use
    113   // MockInputMethod do.
    114   DCHECK(focus_manager_);
    115   native_widget_mac_->GetWidget()->OnKeyEvent(const_cast<ui::KeyEvent*>(&key));
    116   if (!key.handled())
    117     focus_manager_->OnKeyEvent(key);
    118 }
    119 
    120 void BridgedNativeWidget::OnWillChangeFocus(View* focused_before,
    121                                             View* focused_now) {
    122 }
    123 
    124 void BridgedNativeWidget::OnDidChangeFocus(View* focused_before,
    125                                            View* focused_now) {
    126   ui::TextInputClient* input_client =
    127       focused_now ? focused_now->GetTextInputClient() : NULL;
    128   [bridged_view_ setTextInputClient:input_client];
    129 }
    130 
    131 ////////////////////////////////////////////////////////////////////////////////
    132 // BridgedNativeWidget, private:
    133 
    134 void BridgedNativeWidget::RemoveOrDestroyChildren() {
    135   // TODO(tapted): Implement unowned child windows if required.
    136   base::scoped_nsobject<NSArray> child_windows(
    137       [[NSArray alloc] initWithArray:[window_ childWindows]]);
    138   [child_windows makeObjectsPerformSelector:@selector(close)];
    139 }
    140 
    141 }  // namespace views
    142