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 #import <objc/runtime.h> | 7 #import <objc/runtime.h> |
8 #include <stddef.h> | 8 #include <stddef.h> |
9 #include <stdint.h> | 9 #include <stdint.h> |
10 | 10 |
| 11 #include "base/debug/stack_trace.h" |
11 #include "base/logging.h" | 12 #include "base/logging.h" |
12 #import "base/mac/foundation_util.h" | 13 #import "base/mac/foundation_util.h" |
13 #include "base/mac/mac_util.h" | 14 #include "base/mac/mac_util.h" |
14 #import "base/mac/sdk_forward_declarations.h" | 15 #import "base/mac/sdk_forward_declarations.h" |
| 16 #include "base/run_loop.h" |
15 #include "base/thread_task_runner_handle.h" | 17 #include "base/thread_task_runner_handle.h" |
16 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" | 18 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" |
17 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" | 19 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" |
18 #include "ui/base/hit_test.h" | 20 #include "ui/base/hit_test.h" |
19 #include "ui/base/ime/input_method.h" | 21 #include "ui/base/ime/input_method.h" |
20 #include "ui/base/ime/input_method_factory.h" | 22 #include "ui/base/ime/input_method_factory.h" |
21 #include "ui/gfx/display.h" | 23 #include "ui/gfx/display.h" |
22 #include "ui/gfx/geometry/dip_util.h" | 24 #include "ui/gfx/geometry/dip_util.h" |
23 #import "ui/gfx/mac/coordinate_conversion.h" | 25 #import "ui/gfx/mac/coordinate_conversion.h" |
24 #import "ui/gfx/mac/nswindow_frame_controls.h" | 26 #import "ui/gfx/mac/nswindow_frame_controls.h" |
25 #include "ui/gfx/screen.h" | 27 #include "ui/gfx/screen.h" |
26 #import "ui/views/cocoa/bridged_content_view.h" | 28 #import "ui/views/cocoa/bridged_content_view.h" |
27 #import "ui/views/cocoa/cocoa_mouse_capture.h" | 29 #import "ui/views/cocoa/cocoa_mouse_capture.h" |
28 #include "ui/views/cocoa/tooltip_manager_mac.h" | 30 #include "ui/views/cocoa/tooltip_manager_mac.h" |
29 #import "ui/views/cocoa/views_nswindow_delegate.h" | 31 #import "ui/views/cocoa/views_nswindow_delegate.h" |
30 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h" | 32 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h" |
31 #include "ui/views/view.h" | 33 #include "ui/views/view.h" |
32 #include "ui/views/views_delegate.h" | 34 #include "ui/views/views_delegate.h" |
33 #include "ui/views/widget/native_widget_mac.h" | 35 #include "ui/views/widget/native_widget_mac.h" |
34 #include "ui/views/widget/widget.h" | 36 #include "ui/views/widget/widget.h" |
35 #include "ui/views/widget/widget_aura_utils.h" | 37 #include "ui/views/widget/widget_aura_utils.h" |
36 #include "ui/views/widget/widget_delegate.h" | 38 #include "ui/views/widget/widget_delegate.h" |
37 | 39 |
38 extern "C" { | |
39 | |
40 typedef int32_t CGSConnection; | |
41 CGSConnection _CGSDefaultConnection(); | |
42 CGError CGSSetWindowBackgroundBlurRadius(CGSConnection connection, | |
43 NSInteger windowNumber, | |
44 int radius); | |
45 | |
46 } | |
47 | |
48 // The NSView that hosts the composited CALayer drawing the UI. It fills the | 40 // The NSView that hosts the composited CALayer drawing the UI. It fills the |
49 // window but is not hittable so that accessibility hit tests always go to the | 41 // window but is not hittable so that accessibility hit tests always go to the |
50 // BridgedContentView. | 42 // BridgedContentView. |
51 @interface ViewsCompositorSuperview : NSView | 43 @interface ViewsCompositorSuperview : NSView |
52 @end | 44 @end |
53 | 45 |
54 @implementation ViewsCompositorSuperview | 46 @implementation ViewsCompositorSuperview |
55 - (NSView*)hitTest:(NSPoint)aPoint { | 47 - (NSView*)hitTest:(NSPoint)aPoint { |
56 return nil; | 48 return nil; |
57 } | 49 } |
(...skipping 29 matching lines...) Expand all Loading... |
87 const CGFloat kYosemiteMenuOpacity = 194.0 / 255.0; | 79 const CGFloat kYosemiteMenuOpacity = 194.0 / 255.0; |
88 const int kYosemiteMenuBlur = 80; | 80 const int kYosemiteMenuBlur = 80; |
89 | 81 |
90 // Margin at edge and corners of the window that trigger resizing. These match | 82 // Margin at edge and corners of the window that trigger resizing. These match |
91 // actual Cocoa resize margins. | 83 // actual Cocoa resize margins. |
92 const int kResizeAreaEdgeSize = 3; | 84 const int kResizeAreaEdgeSize = 3; |
93 const int kResizeAreaCornerSize = 12; | 85 const int kResizeAreaCornerSize = 12; |
94 | 86 |
95 int kWindowPropertiesKey; | 87 int kWindowPropertiesKey; |
96 | 88 |
| 89 bool g_ignore_next_mouse_down_for_draggable_regions = false; |
| 90 |
97 float GetDeviceScaleFactorFromView(NSView* view) { | 91 float GetDeviceScaleFactorFromView(NSView* view) { |
98 gfx::Display display = | 92 gfx::Display display = |
99 gfx::Screen::GetScreen()->GetDisplayNearestWindow(view); | 93 gfx::Screen::GetScreen()->GetDisplayNearestWindow(view); |
100 DCHECK(display.is_valid()); | 94 DCHECK(display.is_valid()); |
101 return display.device_scale_factor(); | 95 return display.device_scale_factor(); |
102 } | 96 } |
103 | 97 |
104 // Returns true if bounds passed to window in SetBounds should be treated as | 98 // Returns true if bounds passed to window in SetBounds should be treated as |
105 // though they are in screen coordinates. | 99 // though they are in screen coordinates. |
106 bool PositionWindowInScreenCoordinates(views::Widget* widget, | 100 bool PositionWindowInScreenCoordinates(views::Widget* widget, |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 NSInteger event_number = [ns_event eventNumber]; | 178 NSInteger event_number = [ns_event eventNumber]; |
185 | 179 |
186 // The logic here is a bit convoluted because we want to mitigate race | 180 // The logic here is a bit convoluted because we want to mitigate race |
187 // conditions if somehow a different mouse-down occurs between reposts. | 181 // conditions if somehow a different mouse-down occurs between reposts. |
188 // Specifically, we want to avoid: | 182 // Specifically, we want to avoid: |
189 // - BridgedNativeWidget's draggability getting out of sync (e.g. if it is | 183 // - BridgedNativeWidget's draggability getting out of sync (e.g. if it is |
190 // draggable outside of a repost cycle), | 184 // draggable outside of a repost cycle), |
191 // - any repost loop. | 185 // - any repost loop. |
192 | 186 |
193 if (repost_state == NONE) { | 187 if (repost_state == NONE) { |
| 188 if (g_ignore_next_mouse_down_for_draggable_regions) { |
| 189 g_ignore_next_mouse_down_for_draggable_regions = false; |
| 190 return ns_event; |
| 191 } |
| 192 |
194 if (WindowWantsMouseDownReposted(ns_event)) { | 193 if (WindowWantsMouseDownReposted(ns_event)) { |
195 repost_state = EXPECTING_REPOST; | 194 repost_state = EXPECTING_REPOST; |
196 reposted_event_number = event_number; | 195 reposted_event_number = event_number; |
| 196 DLOG(INFO) << "Reposting event for window drag"; |
197 CGEventPost(kCGSessionEventTap, [ns_event CGEvent]); | 197 CGEventPost(kCGSessionEventTap, [ns_event CGEvent]); |
198 return nil; | 198 return nil; |
199 } | 199 } |
200 | 200 |
201 return ns_event; | 201 return ns_event; |
202 } | 202 } |
203 | 203 |
204 if (repost_state == EXPECTING_REPOST) { | 204 if (repost_state == EXPECTING_REPOST) { |
205 // Call through so that the window is made non-draggable again. | 205 // Call through so that the window is made non-draggable again. |
206 WindowWantsMouseDownReposted(ns_event); | 206 WindowWantsMouseDownReposted(ns_event); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
256 // resize operations to coordinate with frames provided by the GPU process. | 256 // resize operations to coordinate with frames provided by the GPU process. |
257 scoped_refptr<base::SingleThreadTaskRunner> GetCompositorTaskRunner() { | 257 scoped_refptr<base::SingleThreadTaskRunner> GetCompositorTaskRunner() { |
258 // If the WindowResizeHelper's pumpable task runner is set, it means the GPU | 258 // If the WindowResizeHelper's pumpable task runner is set, it means the GPU |
259 // process is directing messages there, and the compositor can synchronize | 259 // process is directing messages there, and the compositor can synchronize |
260 // with it. Otherwise, just use the UI thread. | 260 // with it. Otherwise, just use the UI thread. |
261 scoped_refptr<base::SingleThreadTaskRunner> task_runner = | 261 scoped_refptr<base::SingleThreadTaskRunner> task_runner = |
262 ui::WindowResizeHelperMac::Get()->task_runner(); | 262 ui::WindowResizeHelperMac::Get()->task_runner(); |
263 return task_runner ? task_runner : base::ThreadTaskRunnerHandle::Get(); | 263 return task_runner ? task_runner : base::ThreadTaskRunnerHandle::Get(); |
264 } | 264 } |
265 | 265 |
| 266 CGPoint GetCGMousePosition() { |
| 267 return CGEventGetLocation( |
| 268 base::ScopedCFTypeRef<CGEventRef>(CGEventCreate(nullptr))); |
| 269 } |
| 270 |
| 271 void SendCustomLeftMouseEvent(CGEventType type) { |
| 272 base::ScopedCFTypeRef<CGEventRef> event( |
| 273 CGEventCreateMouseEvent( |
| 274 nullptr, type, GetCGMousePosition(), |
| 275 kCGMouseButtonLeft)); |
| 276 CGEventSetIntegerValueField(event, kCGEventSourceUserData, 1); |
| 277 CGEventPost(kCGSessionEventTap, event); |
| 278 } |
| 279 |
266 } // namespace | 280 } // namespace |
267 | 281 |
| 282 extern "C" { |
| 283 |
| 284 typedef int32_t CGSConnection; |
| 285 CGSConnection _CGSDefaultConnection(); |
| 286 CGError CGSSetWindowBackgroundBlurRadius(CGSConnection connection, |
| 287 NSInteger windowNumber, |
| 288 int radius); |
| 289 |
| 290 } |
| 291 |
268 namespace views { | 292 namespace views { |
269 | 293 |
| 294 class CocoaWindowMoveLoop { |
| 295 public: |
| 296 explicit CocoaWindowMoveLoop(BridgedNativeWidget* owner) |
| 297 : owner_(owner), |
| 298 run_loop_(new base::RunLoop), |
| 299 quit_closure_(run_loop_->QuitClosure()) { |
| 300 // AppKit continues to send mouse events to the window, but toolkit-views |
| 301 // goes berserk if it gets them during RunMoveLoop(). |
| 302 [owner_->ns_view() setIgnoreMouseEvents:YES]; |
| 303 owner_->SetDraggable(true); |
| 304 } |
| 305 |
| 306 ~CocoaWindowMoveLoop() { |
| 307 owner_->SetDraggable(false); |
| 308 // Note the crafted events in Run() should not make their way through to |
| 309 // toolkit-views, but regular events after that should be. So stop ignoring. |
| 310 [owner_->ns_view() setIgnoreMouseEvents:NO]; |
| 311 |
| 312 // Handle the pathological case, where |this| is destroyed while running. |
| 313 if (exit_reason_ref_) { |
| 314 *exit_reason_ref_ = WINDOW_DESTROYED; |
| 315 quit_closure_.Run(); |
| 316 } |
| 317 |
| 318 // Handle Run() never being called. |
| 319 if (monitor_) { |
| 320 [NSEvent removeMonitor:monitor_]; |
| 321 monitor_ = nil; |
| 322 } |
| 323 owner_ = nullptr; |
| 324 } |
| 325 |
| 326 Widget::MoveLoopResult Run() { |
| 327 LoopExitReason exit_reason = ENDED_EXTERNALLY; |
| 328 exit_reason_ref_ = &exit_reason; |
| 329 |
| 330 // Move the RunLoop to the stack so our destructor can be called while it is |
| 331 // running. |
| 332 scoped_ptr<base::RunLoop> run_loop(std::move(run_loop_)); |
| 333 |
| 334 // A new window may have just been created, so post an event at the session |
| 335 // tap level to initiate a window drag. |
| 336 // TODO(tapted): Move this "inside" the window or stuff breaks when dragging |
| 337 // the last tab/s off a browser. |
| 338 SendCustomLeftMouseEvent(kCGEventLeftMouseDown); |
| 339 |
| 340 NSEventMask mask = |
| 341 NSLeftMouseUpMask | NSKeyDownMask | NSLeftMouseDraggedMask; |
| 342 monitor_ = [NSEvent addLocalMonitorForEventsMatchingMask:mask |
| 343 handler:^NSEvent*(NSEvent* event) { |
| 344 DLOG(INFO) << "Saw a thing: " << this; |
| 345 if ([event type] == NSLeftMouseDragged) { |
| 346 DLOG(INFO) << "drag"; |
| 347 // AppKit doesn't supply position updates during a drag, so post a |
| 348 // task to notify observers once AppKit has moved the window. |
| 349 base::MessageLoop::current()->PostTask( |
| 350 FROM_HERE, base::Bind(&BridgedNativeWidget::OnPositionChanged, |
| 351 base::Unretained(owner_))); |
| 352 return event; |
| 353 } |
| 354 DLOG(INFO) << "Quitting due to event in " << this; |
| 355 quit_closure_.Run(); |
| 356 if ([event type] == NSLeftMouseUp) { |
| 357 DLOG(INFO) << "mouseup"; |
| 358 *exit_reason_ref_ = MOUSE_UP; |
| 359 return event; // Process the MouseUp. |
| 360 } |
| 361 *exit_reason_ref_ = ESCAPE_PRESSED; |
| 362 DLOG(INFO) << "key"; |
| 363 return nil; // Swallow the keypress. |
| 364 }]; |
| 365 |
| 366 // NSKeyDownMask doesn't work inside addLocalMonitorForEventsMatchingMask: |
| 367 // the event is swallowed by the window move loop before it gets to -[NSApp |
| 368 // sendEvent:]. To see an escape keypress, hook in an event tap lower. |
| 369 |
| 370 run_loop->Run(); |
| 371 |
| 372 if (exit_reason != WINDOW_DESTROYED && exit_reason != ENDED_EXTERNALLY) { |
| 373 exit_reason_ref_ = nullptr; // Ensure End() doesn't replace the reason. |
| 374 DLOG(INFO) << "Calling own EndMoveLoop in " << this; |
| 375 owner_->EndMoveLoop(); // Deletes |this|. |
| 376 } |
| 377 |
| 378 if (exit_reason != MOUSE_UP) { |
| 379 // Tell AppKit to stop moving the window. Otherwise, AppKit will refuse to |
| 380 // start a new window drag. Note the window being dragged is going away in |
| 381 // this case, so it doesn't really matter where the event is located. |
| 382 SendCustomLeftMouseEvent(kCGEventLeftMouseUp); |
| 383 |
| 384 if (exit_reason == ENDED_EXTERNALLY) { |
| 385 // When not canceled, the non-moving drag in the original window must |
| 386 // resume. To do this, AppKit needs to see a mouseDown so that it sends |
| 387 // the correct events. Ideally, it also needs to be at the original |
| 388 // offset, so that it hits a non-draggable region of the original |
| 389 // window: The tab being dragged may move some distance from the cursor |
| 390 // when it "snaps in", so the cursor may not be over a tab. Sadly, this |
| 391 // method doesn't know which window that is. But all that really needs |
| 392 // to be done is to prevent a custom-dragging area from starting a |
| 393 // window-drag. So hook into the logic in RepostEventIfHandledByWindow() |
| 394 // by setting a flag here. Note this assumes the custom mouseDown event |
| 395 // is guaranteed to hit another BridgedNativeWidget when it gets to the |
| 396 // front of the event queue. |
| 397 // TODO(tapted): A better fix would be to keep the temporary window |
| 398 // around and never call EndMoveLoop() on Mac, making this block of code |
| 399 // obsolete. |
| 400 g_ignore_next_mouse_down_for_draggable_regions = true; |
| 401 SendCustomLeftMouseEvent(kCGEventLeftMouseDown); |
| 402 } |
| 403 } |
| 404 |
| 405 return exit_reason == ESCAPE_PRESSED |
| 406 ? Widget::MOVE_LOOP_CANCELED : Widget::MOVE_LOOP_SUCCESSFUL; |
| 407 } |
| 408 |
| 409 void End() { |
| 410 DLOG(INFO) << "In End() for " << this |
| 411 << " reason_ref = " << exit_reason_ref_; |
| 412 if (exit_reason_ref_) { |
| 413 DCHECK_EQ(*exit_reason_ref_, ENDED_EXTERNALLY); |
| 414 // Ensure the destructor doesn't replace the reason. |
| 415 exit_reason_ref_ = nullptr; |
| 416 quit_closure_.Run(); |
| 417 } |
| 418 } |
| 419 |
| 420 static CGEventRef EscapeKeypressMonitor(CGEventTapProxy proxy, |
| 421 CGEventType type, |
| 422 CGEventRef event, |
| 423 void* refcon) { |
| 424 CocoaWindowMoveLoop* self = static_cast<CocoaWindowMoveLoop*>(refcon); |
| 425 (void)self; |
| 426 return event; |
| 427 } |
| 428 |
| 429 private: |
| 430 enum LoopExitReason { |
| 431 ENDED_EXTERNALLY, |
| 432 ESCAPE_PRESSED, |
| 433 MOUSE_UP, |
| 434 WINDOW_DESTROYED, |
| 435 }; |
| 436 |
| 437 BridgedNativeWidget* owner_; // Weak. Owns this. |
| 438 scoped_ptr<base::RunLoop> run_loop_; |
| 439 id monitor_ = nil; |
| 440 |
| 441 // Pointer to a stack variable holding the exit reason. |
| 442 LoopExitReason* exit_reason_ref_ = nullptr; |
| 443 base::Closure quit_closure_; |
| 444 |
| 445 DISALLOW_COPY_AND_ASSIGN(CocoaWindowMoveLoop); |
| 446 }; |
| 447 |
270 // static | 448 // static |
271 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( | 449 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( |
272 NSWindow* window, | 450 NSWindow* window, |
273 const gfx::Size& content_size) { | 451 const gfx::Size& content_size) { |
274 NSRect content_rect = | 452 NSRect content_rect = |
275 NSMakeRect(0, 0, content_size.width(), content_size.height()); | 453 NSMakeRect(0, 0, content_size.width(), content_size.height()); |
276 NSRect frame_rect = [window frameRectForContentRect:content_rect]; | 454 NSRect frame_rect = [window frameRectForContentRect:content_rect]; |
277 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); | 455 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); |
278 } | 456 } |
279 | 457 |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
464 display:YES | 642 display:YES |
465 animate:NO]; | 643 animate:NO]; |
466 } | 644 } |
467 | 645 |
468 void BridgedNativeWidget::SetRootView(views::View* view) { | 646 void BridgedNativeWidget::SetRootView(views::View* view) { |
469 if (view == [bridged_view_ hostedView]) | 647 if (view == [bridged_view_ hostedView]) |
470 return; | 648 return; |
471 | 649 |
472 // If this is ever false, the compositor will need to be properly torn down | 650 // If this is ever false, the compositor will need to be properly torn down |
473 // and replaced, pointing at the new view. | 651 // and replaced, pointing at the new view. |
| 652 if (view && compositor_widget_) { |
| 653 DLOG(INFO) << "Gonna die: view=" << view |
| 654 << " compositor_widget=" << compositor_widget_; |
| 655 } |
474 DCHECK(!view || !compositor_widget_); | 656 DCHECK(!view || !compositor_widget_); |
475 | 657 |
476 [bridged_view_ clearView]; | 658 [bridged_view_ clearView]; |
477 bridged_view_.reset(); | 659 bridged_view_.reset(); |
478 // Note that there can still be references to the old |bridged_view_| | 660 // Note that there can still be references to the old |bridged_view_| |
479 // floating around in Cocoa libraries at this point. However, references to | 661 // floating around in Cocoa libraries at this point. However, references to |
480 // the old views::View will be gone, so any method calls will become no-ops. | 662 // the old views::View will be gone, so any method calls will become no-ops. |
481 | 663 |
482 if (view) { | 664 if (view) { |
483 bridged_view_.reset([[BridgedContentView alloc] initWithView:view]); | 665 bridged_view_.reset([[BridgedContentView alloc] initWithView:view]); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
539 // hasn't yet received a frame from the compositor at this stage, so it is | 721 // hasn't yet received a frame from the compositor at this stage, so it is |
540 // fully transparent until the GPU sends a frame swap IPC. For the blocking | 722 // fully transparent until the GPU sends a frame swap IPC. For the blocking |
541 // option, the animation needs to wait until AcceleratedWidgetSwapCompleted | 723 // option, the animation needs to wait until AcceleratedWidgetSwapCompleted |
542 // has been called at least once, otherwise it will animate nothing. | 724 // has been called at least once, otherwise it will animate nothing. |
543 [show_animation setAnimationBlockingMode:NSAnimationNonblocking]; | 725 [show_animation setAnimationBlockingMode:NSAnimationNonblocking]; |
544 [show_animation startAnimation]; | 726 [show_animation startAnimation]; |
545 } | 727 } |
546 } | 728 } |
547 | 729 |
548 void BridgedNativeWidget::AcquireCapture() { | 730 void BridgedNativeWidget::AcquireCapture() { |
| 731 base::debug::StackTrace().Print(); |
| 732 DLOG(INFO) << "AcquireCapture: " << this; |
549 DCHECK(!HasCapture()); | 733 DCHECK(!HasCapture()); |
550 if (!window_visible_) | 734 if (!window_visible_) |
551 return; // Capture on hidden windows is disallowed. | 735 return; // Capture on hidden windows is disallowed. |
552 | 736 |
553 mouse_capture_.reset(new CocoaMouseCapture(this)); | 737 mouse_capture_.reset(new CocoaMouseCapture(this)); |
554 | 738 |
555 // Initiating global event capture with addGlobalMonitorForEventsMatchingMask: | 739 // Initiating global event capture with addGlobalMonitorForEventsMatchingMask: |
556 // will reset the mouse cursor to an arrow. Asking the window for an update | 740 // will reset the mouse cursor to an arrow. Asking the window for an update |
557 // here will restore what we want. However, it can sometimes cause the cursor | 741 // here will restore what we want. However, it can sometimes cause the cursor |
558 // to flicker, once, on the initial mouseDown. | 742 // to flicker, once, on the initial mouseDown. |
559 // TOOD(tapted): Make this unnecessary by only asking for global mouse capture | 743 // TOOD(tapted): Make this unnecessary by only asking for global mouse capture |
560 // for the cases that need it (e.g. menus, but not drag and drop). | 744 // for the cases that need it (e.g. menus, but not drag and drop). |
561 [window_ cursorUpdate:[NSApp currentEvent]]; | 745 [window_ cursorUpdate:[NSApp currentEvent]]; |
562 } | 746 } |
563 | 747 |
564 void BridgedNativeWidget::ReleaseCapture() { | 748 void BridgedNativeWidget::ReleaseCapture() { |
| 749 base::debug::StackTrace().Print(); |
| 750 DLOG(INFO) << "ReleaseCapture: " << this << " drag_loop=" << drag_run_loop_; |
565 mouse_capture_.reset(); | 751 mouse_capture_.reset(); |
| 752 //if (drag_run_loop_) { |
| 753 // drag_run_loop_->Quit(); |
| 754 // drag_run_loop_ = nullptr; |
| 755 //} |
566 } | 756 } |
567 | 757 |
568 bool BridgedNativeWidget::HasCapture() { | 758 bool BridgedNativeWidget::HasCapture() { |
569 return mouse_capture_ && mouse_capture_->IsActive(); | 759 return mouse_capture_ && mouse_capture_->IsActive(); |
570 } | 760 } |
571 | 761 |
| 762 Widget::MoveLoopResult BridgedNativeWidget::RunMoveLoop( |
| 763 const gfx::Vector2d& drag_offset) { |
| 764 base::debug::StackTrace().Print(); |
| 765 DLOG(INFO) << "RunMoveLoop(): " << this << " drag_loop=" << drag_run_loop_; |
| 766 |
| 767 DCHECK(!HasCapture()); |
| 768 DCHECK(!window_move_loop_); |
| 769 |
| 770 // First, position the window in the right place. The point |drag_offset| |
| 771 // away from the top-left corner needs to be positioned under the mouse. |
| 772 // TODO(tapted): Figure out why the toolkit-views drag controller doesn't get |
| 773 // this right when it first initializes the Widget. |
| 774 NSPoint mouse_in_screen = gfx::ScreenPointToNSPoint( |
| 775 gfx::Screen::GetScreen()->GetCursorScreenPoint()); |
| 776 NSRect frame = [window_ frame]; |
| 777 frame.origin.x = mouse_in_screen.x - drag_offset.x(); |
| 778 frame.origin.y = mouse_in_screen.y - NSHeight(frame) + drag_offset.y(); |
| 779 NSLog(@"mouse, frame, offset -> frame: %@, %@, %s, %@", |
| 780 NSStringFromRect([window_ frame]), |
| 781 NSStringFromPoint(mouse_in_screen), |
| 782 drag_offset.ToString().c_str(), |
| 783 NSStringFromRect(frame)); |
| 784 |
| 785 // After setting the frame to correct the initial offset, the drag controller |
| 786 // may immediately want to quit when it's notified of the new bounds. So the |
| 787 // MoveLoop must be set up before the call to setFrame. |
| 788 window_move_loop_.reset(new CocoaWindowMoveLoop(this)); |
| 789 |
| 790 // Animating may provide a less janky UX, but something custom would be |
| 791 // required so that it follows updates to the mouse position. |
| 792 [window_ setFrame:frame display:YES animate:NO]; |
| 793 |
| 794 // Setting the frame will call OnWidgetBoundsChanged(), which could result in |
| 795 // a call to EndMoveLoop(). |
| 796 if (!window_move_loop_) |
| 797 return Widget::MOVE_LOOP_SUCCESSFUL; |
| 798 |
| 799 return window_move_loop_->Run(); |
| 800 |
| 801 // |this| may be destroyed during the RunLoop, causing it to exit early. |
| 802 // Even if that doesn't happen, CocoaWindowMoveLoop will clean itself up by |
| 803 // calling EndMoveLoop(). So window_move_loop_ will always be null before the |
| 804 // function returns. But don't DCHECK since |this| might not be valid. |
| 805 } |
| 806 |
| 807 void BridgedNativeWidget::EndMoveLoop() { |
| 808 DCHECK(window_move_loop_); |
| 809 DLOG(INFO) << "Quitting due to EndMoveLoop for " << window_move_loop_.get(); |
| 810 window_move_loop_->End(); |
| 811 DLOG(INFO) << "Resetting: " << window_move_loop_.get(); |
| 812 window_move_loop_.reset(); |
| 813 } |
| 814 |
572 void BridgedNativeWidget::SetNativeWindowProperty(const char* name, | 815 void BridgedNativeWidget::SetNativeWindowProperty(const char* name, |
573 void* value) { | 816 void* value) { |
574 NSString* key = [NSString stringWithUTF8String:name]; | 817 NSString* key = [NSString stringWithUTF8String:name]; |
575 if (value) { | 818 if (value) { |
576 [GetWindowProperties() setObject:[NSValue valueWithPointer:value] | 819 [GetWindowProperties() setObject:[NSValue valueWithPointer:value] |
577 forKey:key]; | 820 forKey:key]; |
578 } else { | 821 } else { |
579 [GetWindowProperties() removeObjectForKey:key]; | 822 [GetWindowProperties() removeObjectForKey:key]; |
580 } | 823 } |
581 } | 824 } |
582 | 825 |
583 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { | 826 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { |
584 NSString* key = [NSString stringWithUTF8String:name]; | 827 NSString* key = [NSString stringWithUTF8String:name]; |
585 return [[GetWindowProperties() objectForKey:key] pointerValue]; | 828 return [[GetWindowProperties() objectForKey:key] pointerValue]; |
586 } | 829 } |
587 | 830 |
588 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { | 831 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { |
589 [window_delegate_ setCursor:cursor]; | 832 [window_delegate_ setCursor:cursor]; |
590 } | 833 } |
591 | 834 |
592 void BridgedNativeWidget::OnWindowWillClose() { | 835 void BridgedNativeWidget::OnWindowWillClose() { |
| 836 DCHECK(!drag_run_loop_); |
593 if (parent_) { | 837 if (parent_) { |
594 parent_->RemoveChildWindow(this); | 838 parent_->RemoveChildWindow(this); |
595 parent_ = nullptr; | 839 parent_ = nullptr; |
596 } | 840 } |
597 [window_ setDelegate:nil]; | 841 [window_ setDelegate:nil]; |
598 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; | 842 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; |
599 native_widget_mac_->OnWindowWillClose(); | 843 native_widget_mac_->OnWindowWillClose(); |
600 } | 844 } |
601 | 845 |
602 void BridgedNativeWidget::OnFullscreenTransitionStart( | 846 void BridgedNativeWidget::OnFullscreenTransitionStart( |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
685 | 929 |
686 // 10.9 is unable to generate a window shadow from the composited CALayer, so | 930 // 10.9 is unable to generate a window shadow from the composited CALayer, so |
687 // use Quartz. | 931 // use Quartz. |
688 // We don't update the window mask during a live resize, instead it is done | 932 // We don't update the window mask during a live resize, instead it is done |
689 // after the resize is completed in viewDidEndLiveResize: in | 933 // after the resize is completed in viewDidEndLiveResize: in |
690 // BridgedContentView. | 934 // BridgedContentView. |
691 if (base::mac::IsOSMavericksOrEarlier() && ![window_ inLiveResize]) | 935 if (base::mac::IsOSMavericksOrEarlier() && ![window_ inLiveResize]) |
692 [bridged_view_ updateWindowMask]; | 936 [bridged_view_ updateWindowMask]; |
693 } | 937 } |
694 | 938 |
| 939 void BridgedNativeWidget::OnPositionChanged() { |
| 940 native_widget_mac_->GetWidget()->OnNativeWidgetMove(); |
| 941 } |
| 942 |
695 void BridgedNativeWidget::OnVisibilityChanged() { | 943 void BridgedNativeWidget::OnVisibilityChanged() { |
696 OnVisibilityChangedTo([window_ isVisible]); | 944 OnVisibilityChangedTo([window_ isVisible]); |
697 } | 945 } |
698 | 946 |
699 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) { | 947 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) { |
700 if (window_visible_ == new_visibility) | 948 if (window_visible_ == new_visibility) |
701 return; | 949 return; |
702 | 950 |
703 window_visible_ = new_visibility; | 951 window_visible_ = new_visibility; |
704 | 952 |
(...skipping 533 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1238 [bridged_view_ setMouseDownCanMoveWindow:draggable]; | 1486 [bridged_view_ setMouseDownCanMoveWindow:draggable]; |
1239 // AppKit will not update its cache of mouseDownCanMoveWindow unless something | 1487 // AppKit will not update its cache of mouseDownCanMoveWindow unless something |
1240 // changes. Previously we tried adding an NSView and removing it, but for some | 1488 // changes. Previously we tried adding an NSView and removing it, but for some |
1241 // reason it required reposting the mouse-down event, and didn't always work. | 1489 // reason it required reposting the mouse-down event, and didn't always work. |
1242 // Calling the below seems to be an effective solution. | 1490 // Calling the below seems to be an effective solution. |
1243 [window_ setMovableByWindowBackground:NO]; | 1491 [window_ setMovableByWindowBackground:NO]; |
1244 [window_ setMovableByWindowBackground:YES]; | 1492 [window_ setMovableByWindowBackground:YES]; |
1245 } | 1493 } |
1246 | 1494 |
1247 } // namespace views | 1495 } // namespace views |
OLD | NEW |