• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
19namespace views {
20
21BridgedNativeWidget::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
28BridgedNativeWidget::~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
41void 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
54void 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
67void 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
86void BridgedNativeWidget::OnWindowWillClose() {
87  [[window_ parentWindow] removeChildWindow:window_];
88  [window_ setDelegate:nil];
89  native_widget_mac_->OnWindowWillClose();
90}
91
92InputMethod* BridgedNativeWidget::CreateInputMethod() {
93  if (switches::IsTextInputFocusManagerEnabled())
94    return new NullInputMethod();
95
96  return new InputMethodBridge(this, GetHostInputMethod(), true);
97}
98
99ui::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
111void 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
120void BridgedNativeWidget::OnWillChangeFocus(View* focused_before,
121                                            View* focused_now) {
122}
123
124void 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
134void 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