OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ui/views/cocoa/bridged_native_widget.h" | 5 #import "ui/views/cocoa/bridged_native_widget.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "ui/base/ime/input_method.h" | 8 #include "ui/base/ime/input_method.h" |
9 #include "ui/base/ime/input_method_factory.h" | 9 #include "ui/base/ime/input_method_factory.h" |
10 #include "ui/base/ui_base_switches_util.h" | 10 #include "ui/base/ui_base_switches_util.h" |
| 11 #include "ui/gfx/display.h" |
| 12 #import "ui/gfx/mac/coordinate_conversion.h" |
| 13 #include "ui/gfx/screen.h" |
11 #import "ui/views/cocoa/bridged_content_view.h" | 14 #import "ui/views/cocoa/bridged_content_view.h" |
12 #import "ui/views/cocoa/views_nswindow_delegate.h" | 15 #import "ui/views/cocoa/views_nswindow_delegate.h" |
13 #include "ui/views/widget/native_widget_mac.h" | 16 #include "ui/views/widget/native_widget_mac.h" |
14 #include "ui/views/ime/input_method_bridge.h" | 17 #include "ui/views/ime/input_method_bridge.h" |
15 #include "ui/views/ime/null_input_method.h" | 18 #include "ui/views/ime/null_input_method.h" |
16 #include "ui/views/view.h" | 19 #include "ui/views/view.h" |
17 #include "ui/views/widget/widget.h" | 20 #include "ui/views/widget/widget.h" |
18 | 21 |
| 22 #include "cc/output/copy_output_request.h" |
| 23 #include "cc/output/copy_output_result.h" |
| 24 |
| 25 #include "third_party/skia/include/core/SkBitmap.h" |
| 26 #include "ui/gfx/codec/png_codec.h" |
| 27 #include "ui/gfx/image/image_skia_rep.h" |
| 28 #include "ui/gfx/canvas.h" |
| 29 |
| 30 namespace { |
| 31 |
| 32 float GetDeviceScaleFactorFromDisplay(NSView* view) { |
| 33 gfx::Display display = gfx::Screen::GetScreenFor(view)-> |
| 34 GetDisplayNearestWindow(view); |
| 35 DCHECK(display.is_valid()); |
| 36 return display.device_scale_factor(); |
| 37 } |
| 38 |
| 39 } // namespace |
| 40 |
19 namespace views { | 41 namespace views { |
20 | 42 |
21 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) | 43 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) |
22 : native_widget_mac_(parent), focus_manager_(NULL) { | 44 : native_widget_mac_(parent), focus_manager_(NULL) { |
23 DCHECK(parent); | 45 DCHECK(parent); |
24 window_delegate_.reset( | 46 window_delegate_.reset( |
25 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); | 47 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); |
26 } | 48 } |
27 | 49 |
28 BridgedNativeWidget::~BridgedNativeWidget() { | 50 BridgedNativeWidget::~BridgedNativeWidget() { |
(...skipping 28 matching lines...) Expand all Loading... |
57 | 79 |
58 if (focus_manager_) | 80 if (focus_manager_) |
59 focus_manager_->RemoveFocusChangeListener(this); | 81 focus_manager_->RemoveFocusChangeListener(this); |
60 | 82 |
61 if (focus_manager) | 83 if (focus_manager) |
62 focus_manager->AddFocusChangeListener(this); | 84 focus_manager->AddFocusChangeListener(this); |
63 | 85 |
64 focus_manager_ = focus_manager; | 86 focus_manager_ = focus_manager; |
65 } | 87 } |
66 | 88 |
| 89 void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds) { |
| 90 [window_ setFrame:gfx::ScreenRectToNSRect(new_bounds) |
| 91 display:YES |
| 92 animate:NO]; |
| 93 if (layer()) |
| 94 SetLayerSize(new_bounds.size()); |
| 95 } |
| 96 |
67 void BridgedNativeWidget::SetRootView(views::View* view) { | 97 void BridgedNativeWidget::SetRootView(views::View* view) { |
68 if (view == [bridged_view_ hostedView]) | 98 if (view == [bridged_view_ hostedView]) |
69 return; | 99 return; |
70 | 100 |
| 101 // If this is ever false, the compositor will need to be properly torn down |
| 102 // and replaced, pointing at the new view. |
| 103 DCHECK(!view || !compositor_view_); |
| 104 |
71 [bridged_view_ clearView]; | 105 [bridged_view_ clearView]; |
72 bridged_view_.reset(); | 106 bridged_view_.reset(); |
73 // Note that there can still be references to the old |bridged_view_| | 107 // Note that there can still be references to the old |bridged_view_| |
74 // floating around in Cocoa libraries at this point. However, references to | 108 // 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. | 109 // the old views::View will be gone, so any method calls will become no-ops. |
76 | 110 |
77 if (view) { | 111 if (view) { |
78 bridged_view_.reset([[BridgedContentView alloc] initWithView:view]); | 112 bridged_view_.reset([[BridgedContentView alloc] initWithView:view]); |
79 // Objective C initializers can return nil. However, if |view| is non-NULL | 113 // Objective C initializers can return nil. However, if |view| is non-NULL |
80 // this should be treated as an error and caught early. | 114 // this should be treated as an error and caught early. |
(...skipping 17 matching lines...) Expand all Loading... |
98 | 132 |
99 ui::InputMethod* BridgedNativeWidget::GetHostInputMethod() { | 133 ui::InputMethod* BridgedNativeWidget::GetHostInputMethod() { |
100 if (!input_method_) { | 134 if (!input_method_) { |
101 // Delegate is NULL because Mac IME does not need DispatchKeyEventPostIME | 135 // Delegate is NULL because Mac IME does not need DispatchKeyEventPostIME |
102 // callbacks. | 136 // callbacks. |
103 input_method_ = ui::CreateInputMethod(NULL, nil); | 137 input_method_ = ui::CreateInputMethod(NULL, nil); |
104 } | 138 } |
105 return input_method_.get(); | 139 return input_method_.get(); |
106 } | 140 } |
107 | 141 |
| 142 ui::Layer* BridgedNativeWidget::GetOrCreateLayer() { |
| 143 if (!bridged_view_) |
| 144 return NULL; |
| 145 |
| 146 if (layer()) |
| 147 return layer(); |
| 148 |
| 149 [bridged_view_ setWantsLayer:YES]; |
| 150 // Next line does nothing. |
| 151 [[bridged_view_ layer] |
| 152 setBackgroundColor:CGColorGetConstantColor(kCGColorClear)]; |
| 153 |
| 154 SetLayer(new ui::Layer()); |
| 155 layer()->set_delegate(this); |
| 156 DCHECK_EQ(0u, [[bridged_view_ subviews] count]); |
| 157 compositor_view_.reset(new content::BrowserCompositorViewMac(this)); |
| 158 |
| 159 DCHECK(compositor_view_->GetCompositor()); |
| 160 DCHECK_EQ(compositor_view_->GetCompositor(), layer()->GetCompositor()); |
| 161 |
| 162 [window_ setOpaque:NO]; |
| 163 layer()->GetCompositor()->SetHostHasTransparentBackground(true); |
| 164 layer()->SetFillsBoundsCompletely(true); |
| 165 |
| 166 SetLayerSize(native_widget_mac_->GetWindowBoundsInScreen().size()); |
| 167 return layer(); |
| 168 } |
| 169 |
108 //////////////////////////////////////////////////////////////////////////////// | 170 //////////////////////////////////////////////////////////////////////////////// |
109 // BridgedNativeWidget, internal::InputMethodDelegate: | 171 // BridgedNativeWidget, internal::InputMethodDelegate: |
110 | 172 |
111 void BridgedNativeWidget::DispatchKeyEventPostIME(const ui::KeyEvent& key) { | 173 void BridgedNativeWidget::DispatchKeyEventPostIME(const ui::KeyEvent& key) { |
112 // Mac key events don't go through this, but some unit tests that use | 174 // Mac key events don't go through this, but some unit tests that use |
113 // MockInputMethod do. | 175 // MockInputMethod do. |
114 DCHECK(focus_manager_); | 176 DCHECK(focus_manager_); |
115 native_widget_mac_->GetWidget()->OnKeyEvent(const_cast<ui::KeyEvent*>(&key)); | 177 native_widget_mac_->GetWidget()->OnKeyEvent(const_cast<ui::KeyEvent*>(&key)); |
116 if (!key.handled()) | 178 if (!key.handled()) |
117 focus_manager_->OnKeyEvent(key); | 179 focus_manager_->OnKeyEvent(key); |
118 } | 180 } |
119 | 181 |
| 182 //////////////////////////////////////////////////////////////////////////////// |
| 183 // BridgedNativeWidget, content::BrowserCompositorViewMacClient: |
| 184 |
| 185 void BridgedNativeWidget::BrowserCompositorViewFrameSwapped( |
| 186 const std::vector<ui::LatencyInfo>& latency_info) { |
| 187 } |
| 188 |
| 189 NSView* BridgedNativeWidget::BrowserCompositorSuperview() { |
| 190 return bridged_view_; |
| 191 } |
| 192 |
| 193 ui::Layer* BridgedNativeWidget::BrowserCompositorRootLayer() { |
| 194 return layer(); |
| 195 } |
| 196 |
| 197 //////////////////////////////////////////////////////////////////////////////// |
| 198 // BridgedNativeWidget, FocusChangeListener: |
| 199 |
120 void BridgedNativeWidget::OnWillChangeFocus(View* focused_before, | 200 void BridgedNativeWidget::OnWillChangeFocus(View* focused_before, |
121 View* focused_now) { | 201 View* focused_now) { |
122 } | 202 } |
123 | 203 |
124 void BridgedNativeWidget::OnDidChangeFocus(View* focused_before, | 204 void BridgedNativeWidget::OnDidChangeFocus(View* focused_before, |
125 View* focused_now) { | 205 View* focused_now) { |
126 ui::TextInputClient* input_client = | 206 ui::TextInputClient* input_client = |
127 focused_now ? focused_now->GetTextInputClient() : NULL; | 207 focused_now ? focused_now->GetTextInputClient() : NULL; |
128 [bridged_view_ setTextInputClient:input_client]; | 208 [bridged_view_ setTextInputClient:input_client]; |
129 } | 209 } |
130 | 210 |
131 //////////////////////////////////////////////////////////////////////////////// | 211 //////////////////////////////////////////////////////////////////////////////// |
| 212 // BridgedNativeWidget, LayerDelegate: |
| 213 |
| 214 static bool WritePNGFile(const SkBitmap& bitmap, const base::FilePath& file_path
, |
| 215 bool discard_transparency) { |
| 216 std::vector<unsigned char> png_data; |
| 217 if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, |
| 218 discard_transparency, |
| 219 &png_data) && |
| 220 base::CreateDirectory(file_path.DirName())) { |
| 221 char* data = reinterpret_cast<char*>(&png_data[0]); |
| 222 int size = static_cast<int>(png_data.size()); |
| 223 bool result = base::WriteFile(file_path, data, size) == size; |
| 224 return result; |
| 225 } |
| 226 return false; |
| 227 } |
| 228 |
| 229 static void ReadbackResult(scoped_ptr<cc::CopyOutputResult> result) { |
| 230 DLOG(INFO) << "result!"; |
| 231 scoped_ptr<SkBitmap> result_bitmap = result->TakeBitmap().Pass(); |
| 232 static int filectr = 0; |
| 233 std::ostringstream oss; |
| 234 oss << "file" << filectr++ << ".png"; |
| 235 WritePNGFile(*result_bitmap, base::FilePath().AppendASCII(oss.str().c_str()),
false); |
| 236 } |
| 237 |
| 238 void BridgedNativeWidget::OnPaintLayer(gfx::Canvas* canvas) { |
| 239 layer()->RequestCopyOfOutput(cc::CopyOutputRequest::CreateBitmapRequest(base::
Bind(&ReadbackResult))); |
| 240 native_widget_mac_->GetWidget()->OnNativeWidgetPaint(canvas); |
| 241 |
| 242 gfx::ImageSkiaRep image = canvas->ExtractImageRep(); |
| 243 static int filectr = 0; |
| 244 std::ostringstream oss; |
| 245 oss << "bpass" << filectr++ << ".png"; |
| 246 WritePNGFile(image.sk_bitmap(), base::FilePath().AppendASCII(oss.str().c_str()
), false); |
| 247 |
| 248 } |
| 249 |
| 250 void BridgedNativeWidget::OnDeviceScaleFactorChanged( |
| 251 float device_scale_factor) { |
| 252 } |
| 253 |
| 254 base::Closure BridgedNativeWidget::PrepareForLayerBoundsChange() { |
| 255 return base::Bind(&BridgedNativeWidget::OnWindowBoundsChanged, |
| 256 base::Unretained(this)); |
| 257 } |
| 258 |
| 259 //////////////////////////////////////////////////////////////////////////////// |
132 // BridgedNativeWidget, private: | 260 // BridgedNativeWidget, private: |
133 | 261 |
134 void BridgedNativeWidget::RemoveOrDestroyChildren() { | 262 void BridgedNativeWidget::RemoveOrDestroyChildren() { |
135 // TODO(tapted): Implement unowned child windows if required. | 263 // TODO(tapted): Implement unowned child windows if required. |
136 base::scoped_nsobject<NSArray> child_windows( | 264 base::scoped_nsobject<NSArray> child_windows( |
137 [[NSArray alloc] initWithArray:[window_ childWindows]]); | 265 [[NSArray alloc] initWithArray:[window_ childWindows]]); |
138 [child_windows makeObjectsPerformSelector:@selector(close)]; | 266 [child_windows makeObjectsPerformSelector:@selector(close)]; |
139 } | 267 } |
140 | 268 |
| 269 void BridgedNativeWidget::SetLayerSize(const gfx::Size& size_in_dip) { |
| 270 DCHECK(layer()); |
| 271 |
| 272 layer()->SetBounds(gfx::Rect(size_in_dip)); |
| 273 |
| 274 float scale_factor = GetDeviceScaleFactorFromDisplay(bridged_view_); |
| 275 gfx::Size size_in_pixels(size_in_dip.width() * scale_factor, |
| 276 size_in_dip.height() * scale_factor); |
| 277 compositor_view_->GetCompositor()->SetScaleAndSize(scale_factor, |
| 278 size_in_pixels); |
| 279 } |
| 280 |
| 281 void BridgedNativeWidget::OnWindowBoundsChanged() { |
| 282 } |
| 283 |
141 } // namespace views | 284 } // namespace views |
OLD | NEW |