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