| 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 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/run_loop.h" |
| 10 #import "base/mac/foundation_util.h" | 11 #import "base/mac/foundation_util.h" |
| 11 #include "base/mac/mac_util.h" | 12 #include "base/mac/mac_util.h" |
| 12 #import "base/mac/sdk_forward_declarations.h" | 13 #import "base/mac/sdk_forward_declarations.h" |
| 13 #include "base/thread_task_runner_handle.h" | 14 #include "base/thread_task_runner_handle.h" |
| 14 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" | 15 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" |
| 15 #include "ui/base/hit_test.h" | 16 #include "ui/base/hit_test.h" |
| 16 #include "ui/base/ime/input_method.h" | 17 #include "ui/base/ime/input_method.h" |
| 17 #include "ui/base/ime/input_method_factory.h" | 18 #include "ui/base/ime/input_method_factory.h" |
| 18 #include "ui/gfx/display.h" | 19 #include "ui/gfx/display.h" |
| 19 #include "ui/gfx/geometry/dip_util.h" | 20 #include "ui/gfx/geometry/dip_util.h" |
| 20 #import "ui/gfx/mac/coordinate_conversion.h" | 21 #import "ui/gfx/mac/coordinate_conversion.h" |
| 21 #import "ui/gfx/mac/nswindow_frame_controls.h" | 22 #import "ui/gfx/mac/nswindow_frame_controls.h" |
| 22 #include "ui/gfx/screen.h" | 23 #include "ui/gfx/screen.h" |
| 23 #import "ui/views/cocoa/bridged_content_view.h" | 24 #import "ui/views/cocoa/bridged_content_view.h" |
| 24 #import "ui/views/cocoa/cocoa_mouse_capture.h" | 25 #import "ui/views/cocoa/cocoa_mouse_capture.h" |
| 25 #include "ui/views/cocoa/tooltip_manager_mac.h" | 26 #include "ui/views/cocoa/tooltip_manager_mac.h" |
| 26 #import "ui/views/cocoa/views_nswindow_delegate.h" | 27 #import "ui/views/cocoa/views_nswindow_delegate.h" |
| 27 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h" | 28 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h" |
| 28 #include "ui/views/view.h" | 29 #include "ui/views/view.h" |
| 29 #include "ui/views/views_delegate.h" | 30 #include "ui/views/views_delegate.h" |
| 30 #include "ui/views/widget/native_widget_mac.h" | 31 #include "ui/views/widget/native_widget_mac.h" |
| 31 #include "ui/views/widget/widget.h" | 32 #include "ui/views/widget/widget.h" |
| 32 #include "ui/views/widget/widget_aura_utils.h" | 33 #include "ui/views/widget/widget_aura_utils.h" |
| 33 #include "ui/views/widget/widget_delegate.h" | 34 #include "ui/views/widget/widget_delegate.h" |
| 34 | 35 |
| 36 #include "base/debug/stack_trace.h" |
| 37 |
| 35 // The NSView that hosts the composited CALayer drawing the UI. It fills the | 38 // The NSView that hosts the composited CALayer drawing the UI. It fills the |
| 36 // window but is not hittable so that accessibility hit tests always go to the | 39 // window but is not hittable so that accessibility hit tests always go to the |
| 37 // BridgedContentView. | 40 // BridgedContentView. |
| 38 @interface ViewsCompositorSuperview : NSView | 41 @interface ViewsCompositorSuperview : NSView |
| 39 @end | 42 @end |
| 40 | 43 |
| 41 @implementation ViewsCompositorSuperview | 44 @implementation ViewsCompositorSuperview |
| 42 - (NSView*)hitTest:(NSPoint)aPoint { | 45 - (NSView*)hitTest:(NSPoint)aPoint { |
| 43 return nil; | 46 return nil; |
| 44 } | 47 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 65 - (void)setCurrentProgress:(NSAnimationProgress)progress { | 68 - (void)setCurrentProgress:(NSAnimationProgress)progress { |
| 66 [super setCurrentProgress:progress]; | 69 [super setCurrentProgress:progress]; |
| 67 [window_ invalidateShadow]; | 70 [window_ invalidateShadow]; |
| 68 } | 71 } |
| 69 @end | 72 @end |
| 70 | 73 |
| 71 namespace { | 74 namespace { |
| 72 | 75 |
| 73 int kWindowPropertiesKey; | 76 int kWindowPropertiesKey; |
| 74 | 77 |
| 78 bool g_ignore_next_mouse_down_for_draggable_regions = false; |
| 79 |
| 75 float GetDeviceScaleFactorFromView(NSView* view) { | 80 float GetDeviceScaleFactorFromView(NSView* view) { |
| 76 gfx::Display display = | 81 gfx::Display display = |
| 77 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view); | 82 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view); |
| 78 DCHECK(display.is_valid()); | 83 DCHECK(display.is_valid()); |
| 79 return display.device_scale_factor(); | 84 return display.device_scale_factor(); |
| 80 } | 85 } |
| 81 | 86 |
| 82 // Returns true if bounds passed to window in SetBounds should be treated as | 87 // Returns true if bounds passed to window in SetBounds should be treated as |
| 83 // though they are in screen coordinates. | 88 // though they are in screen coordinates. |
| 84 bool PositionWindowInScreenCoordinates(views::Widget* widget, | 89 bool PositionWindowInScreenCoordinates(views::Widget* widget, |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 NSInteger event_number = [ns_event eventNumber]; | 137 NSInteger event_number = [ns_event eventNumber]; |
| 133 | 138 |
| 134 // The logic here is a bit convoluted because we want to mitigate race | 139 // The logic here is a bit convoluted because we want to mitigate race |
| 135 // conditions if somehow a different mouse-down occurs between reposts. | 140 // conditions if somehow a different mouse-down occurs between reposts. |
| 136 // Specifically, we want to avoid: | 141 // Specifically, we want to avoid: |
| 137 // - BridgedNativeWidget's draggability getting out of sync (e.g. if it is | 142 // - BridgedNativeWidget's draggability getting out of sync (e.g. if it is |
| 138 // draggable outside of a repost cycle), | 143 // draggable outside of a repost cycle), |
| 139 // - any repost loop. | 144 // - any repost loop. |
| 140 | 145 |
| 141 if (repost_state == NONE) { | 146 if (repost_state == NONE) { |
| 147 if (g_ignore_next_mouse_down_for_draggable_regions) { |
| 148 g_ignore_next_mouse_down_for_draggable_regions = false; |
| 149 return ns_event; |
| 150 } |
| 151 |
| 142 if (WindowWantsMouseDownReposted(ns_event)) { | 152 if (WindowWantsMouseDownReposted(ns_event)) { |
| 143 repost_state = EXPECTING_REPOST; | 153 repost_state = EXPECTING_REPOST; |
| 144 reposted_event_number = event_number; | 154 reposted_event_number = event_number; |
| 155 DLOG(INFO) << "Reposting event for window drag"; |
| 145 CGEventPost(kCGSessionEventTap, [ns_event CGEvent]); | 156 CGEventPost(kCGSessionEventTap, [ns_event CGEvent]); |
| 146 return nil; | 157 return nil; |
| 147 } | 158 } |
| 148 | 159 |
| 149 return ns_event; | 160 return ns_event; |
| 150 } | 161 } |
| 151 | 162 |
| 152 if (repost_state == EXPECTING_REPOST) { | 163 if (repost_state == EXPECTING_REPOST) { |
| 153 // Call through so that the window is made non-draggable again. | 164 // Call through so that the window is made non-draggable again. |
| 154 WindowWantsMouseDownReposted(ns_event); | 165 WindowWantsMouseDownReposted(ns_event); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 192 if (monitor) | 203 if (monitor) |
| 193 return; | 204 return; |
| 194 | 205 |
| 195 monitor = [NSEvent | 206 monitor = [NSEvent |
| 196 addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask | 207 addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask |
| 197 handler:^NSEvent*(NSEvent* ns_event) { | 208 handler:^NSEvent*(NSEvent* ns_event) { |
| 198 return RepostEventIfHandledByWindow(ns_event); | 209 return RepostEventIfHandledByWindow(ns_event); |
| 199 }]; | 210 }]; |
| 200 } | 211 } |
| 201 | 212 |
| 213 CGPoint GetCGMousePosition() { |
| 214 return CGEventGetLocation( |
| 215 base::ScopedCFTypeRef<CGEventRef>(CGEventCreate(nullptr))); |
| 216 } |
| 217 |
| 218 void SendCustomLeftMouseEvent(CGEventType type) { |
| 219 base::ScopedCFTypeRef<CGEventRef> event( |
| 220 CGEventCreateMouseEvent( |
| 221 nullptr, type, GetCGMousePosition(), |
| 222 kCGMouseButtonLeft)); |
| 223 CGEventSetIntegerValueField(event, kCGEventSourceUserData, 1); |
| 224 CGEventPost(kCGSessionEventTap, event); |
| 225 } |
| 226 |
| 202 } // namespace | 227 } // namespace |
| 203 | 228 |
| 204 namespace views { | 229 namespace views { |
| 205 | 230 |
| 231 class CocoaWindowMoveLoop { |
| 232 public: |
| 233 explicit CocoaWindowMoveLoop(BridgedNativeWidget* owner) |
| 234 : owner_(owner), |
| 235 run_loop_(new base::RunLoop), |
| 236 quit_closure_(run_loop_->QuitClosure()) { |
| 237 // AppKit continues to send mouse events to the window, but toolkit-views |
| 238 // goes berserk if it gets them during RunMoveLoop(). |
| 239 [owner_->ns_view() setIgnoreMouseEvents:YES]; |
| 240 owner_->SetDraggable(true); |
| 241 } |
| 242 |
| 243 ~CocoaWindowMoveLoop() { |
| 244 owner_->SetDraggable(false); |
| 245 // Note the crafted events in Run() should not make their way through to |
| 246 // toolkit-views, but regular events after that should be. So stop ignoring. |
| 247 [owner_->ns_view() setIgnoreMouseEvents:NO]; |
| 248 |
| 249 // Handle the pathological case, where |this| is destroyed while running. |
| 250 if (exit_reason_ref_) { |
| 251 *exit_reason_ref_ = WINDOW_DESTROYED; |
| 252 quit_closure_.Run(); |
| 253 } |
| 254 |
| 255 // Handle Run() never being called. |
| 256 if (monitor_) { |
| 257 [NSEvent removeMonitor:monitor_]; |
| 258 monitor_ = nil; |
| 259 } |
| 260 owner_ = nullptr; |
| 261 } |
| 262 |
| 263 Widget::MoveLoopResult Run() { |
| 264 LoopExitReason exit_reason = ENDED_EXTERNALLY; |
| 265 exit_reason_ref_ = &exit_reason; |
| 266 |
| 267 // Move the RunLoop to the stack so our destructor can be called while it is |
| 268 // running. |
| 269 scoped_ptr<base::RunLoop> run_loop(run_loop_.Pass()); |
| 270 |
| 271 // A new window may have just been created, so post an event at the session |
| 272 // tap level to initiate a window drag. |
| 273 // TODO(tapted): Move this "inside" the window or stuff breaks when dragging |
| 274 // the last tab/s off a browser. |
| 275 SendCustomLeftMouseEvent(kCGEventLeftMouseDown); |
| 276 |
| 277 NSEventMask mask = NSLeftMouseUpMask | NSKeyDownMask | NSLeftMouseDraggedMas
k; |
| 278 monitor_ = [NSEvent addLocalMonitorForEventsMatchingMask:mask |
| 279 handler:^NSEvent*(NSEvent* event) { |
| 280 DLOG(INFO) << "Saw a thing: " << this; |
| 281 if ([event type] == NSLeftMouseDragged) { |
| 282 DLOG(INFO) << "drag"; |
| 283 // AppKit doesn't supply position updates during a drag, so post a |
| 284 // task to notify observers once AppKit has moved the window. |
| 285 base::MessageLoop::current()->PostTask(FROM_HERE, |
| 286 base::Bind(&BridgedNativeWidg
et::OnPositionChanged, |
| 287 base::Unretained(o
wner_))); |
| 288 return event; |
| 289 } |
| 290 DLOG(INFO) << "Quitting due to event in " << this; |
| 291 quit_closure_.Run(); |
| 292 if ([event type] == NSLeftMouseUp) { |
| 293 DLOG(INFO) << "mouseup"; |
| 294 *exit_reason_ref_ = MOUSE_UP; |
| 295 return event; // Process the MouseUp. |
| 296 } |
| 297 *exit_reason_ref_ = ESCAPE_PRESSED; |
| 298 DLOG(INFO) << "key"; |
| 299 return nil; // Swallow the keypress. |
| 300 }]; |
| 301 |
| 302 // NSKeyDownMask doesn't work inside addLocalMonitorForEventsMatchingMask: t
he |
| 303 // event is swallowed by the window move loop before it gets to -[NSApp |
| 304 // sendEvent:]. To see an escape keypress, hook in an event tap lower. |
| 305 |
| 306 run_loop->Run(); |
| 307 |
| 308 if (exit_reason != WINDOW_DESTROYED && exit_reason != ENDED_EXTERNALLY) { |
| 309 exit_reason_ref_ = nullptr; // Ensure End() doesn't replace the reason. |
| 310 DLOG(INFO) << "Calling own EndMoveLoop in " << this; |
| 311 owner_->EndMoveLoop(); // Deletes |this|. |
| 312 } |
| 313 |
| 314 if (exit_reason != MOUSE_UP) { |
| 315 // Tell AppKit to stop moving the window. Otherwise, AppKit will refuse to |
| 316 // start a new window drag. Note the window being dragged is going away in |
| 317 // this case, so it doesn't really matter where the event is located. |
| 318 SendCustomLeftMouseEvent(kCGEventLeftMouseUp); |
| 319 |
| 320 if (exit_reason == ENDED_EXTERNALLY) { |
| 321 // When not canceled, the non-moving drag in the original window must |
| 322 // resume. To do this, AppKit needs to see a mouseDown so that it sends |
| 323 // the correct events. Ideally, it also needs to be at the original |
| 324 // offset, so that it hits a non-draggable region of the original window
: |
| 325 // The tab being dragged may move some distance from the cursor when it |
| 326 // "snaps in", so the cursor may not be over a tab. Sadly, this method |
| 327 // doesn't know which window that is. But all that really needs to be |
| 328 // done is to prevent a custom-dragging area from starting a window-drag
. |
| 329 // So hook into the logic in RepostEventIfHandledByWindow() by setting a |
| 330 // flag here. Note this assumes the custom mouseDown event is guaranteed |
| 331 // to hit another BridgedNativeWidget when it gets to the front of the |
| 332 // event queue. |
| 333 // TODO(tapted): A better fix would be to keep the temporary window |
| 334 // around and never call EndMoveLoop() on Mac, making this block of code |
| 335 // obsolete. |
| 336 g_ignore_next_mouse_down_for_draggable_regions = true; |
| 337 SendCustomLeftMouseEvent(kCGEventLeftMouseDown); |
| 338 } |
| 339 } |
| 340 |
| 341 return exit_reason == ESCAPE_PRESSED |
| 342 ? Widget::MOVE_LOOP_CANCELED : Widget::MOVE_LOOP_SUCCESSFUL; |
| 343 } |
| 344 |
| 345 void End() { |
| 346 DLOG(INFO) << "In End() for " << this << " reason_ref = " << exit_reason_ref
_; |
| 347 if (exit_reason_ref_) { |
| 348 DCHECK_EQ(*exit_reason_ref_, ENDED_EXTERNALLY); |
| 349 // Ensure the destructor doesn't replace the reason. |
| 350 exit_reason_ref_ = nullptr; |
| 351 quit_closure_.Run(); |
| 352 } |
| 353 } |
| 354 |
| 355 static CGEventRef EscapeKeypressMonitor(CGEventTapProxy proxy, |
| 356 CGEventType type, |
| 357 CGEventRef event, |
| 358 void* refcon) { |
| 359 CocoaWindowMoveLoop* self = static_cast<CocoaWindowMoveLoop*>(refcon); |
| 360 (void)self; |
| 361 return event; |
| 362 } |
| 363 |
| 364 private: |
| 365 enum LoopExitReason { |
| 366 ENDED_EXTERNALLY, |
| 367 ESCAPE_PRESSED, |
| 368 MOUSE_UP, |
| 369 WINDOW_DESTROYED, |
| 370 }; |
| 371 |
| 372 BridgedNativeWidget* owner_; // Weak. Owns this. |
| 373 scoped_ptr<base::RunLoop> run_loop_; |
| 374 id monitor_ = nil; |
| 375 |
| 376 // Pointer to a stack variable holding the exit reason. |
| 377 LoopExitReason* exit_reason_ref_ = nullptr; |
| 378 base::Closure quit_closure_; |
| 379 |
| 380 DISALLOW_COPY_AND_ASSIGN(CocoaWindowMoveLoop); |
| 381 }; |
| 382 |
| 206 // static | 383 // static |
| 207 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( | 384 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( |
| 208 NSWindow* window, | 385 NSWindow* window, |
| 209 const gfx::Size& content_size) { | 386 const gfx::Size& content_size) { |
| 210 NSRect content_rect = | 387 NSRect content_rect = |
| 211 NSMakeRect(0, 0, content_size.width(), content_size.height()); | 388 NSMakeRect(0, 0, content_size.width(), content_size.height()); |
| 212 NSRect frame_rect = [window frameRectForContentRect:content_rect]; | 389 NSRect frame_rect = [window frameRectForContentRect:content_rect]; |
| 213 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); | 390 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); |
| 214 } | 391 } |
| 215 | 392 |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 display:YES | 562 display:YES |
| 386 animate:NO]; | 563 animate:NO]; |
| 387 } | 564 } |
| 388 | 565 |
| 389 void BridgedNativeWidget::SetRootView(views::View* view) { | 566 void BridgedNativeWidget::SetRootView(views::View* view) { |
| 390 if (view == [bridged_view_ hostedView]) | 567 if (view == [bridged_view_ hostedView]) |
| 391 return; | 568 return; |
| 392 | 569 |
| 393 // If this is ever false, the compositor will need to be properly torn down | 570 // If this is ever false, the compositor will need to be properly torn down |
| 394 // and replaced, pointing at the new view. | 571 // and replaced, pointing at the new view. |
| 572 if (view && compositor_widget_) { |
| 573 DLOG(INFO) << "Gonna die: view=" << view << " compositor_widget=" << composi
tor_widget_; |
| 574 } |
| 395 DCHECK(!view || !compositor_widget_); | 575 DCHECK(!view || !compositor_widget_); |
| 396 | 576 |
| 397 [bridged_view_ clearView]; | 577 [bridged_view_ clearView]; |
| 398 bridged_view_.reset(); | 578 bridged_view_.reset(); |
| 399 // Note that there can still be references to the old |bridged_view_| | 579 // Note that there can still be references to the old |bridged_view_| |
| 400 // floating around in Cocoa libraries at this point. However, references to | 580 // floating around in Cocoa libraries at this point. However, references to |
| 401 // the old views::View will be gone, so any method calls will become no-ops. | 581 // the old views::View will be gone, so any method calls will become no-ops. |
| 402 | 582 |
| 403 if (view) { | 583 if (view) { |
| 404 bridged_view_.reset([[BridgedContentView alloc] initWithView:view]); | 584 bridged_view_.reset([[BridgedContentView alloc] initWithView:view]); |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 467 // hasn't yet received a frame from the compositor at this stage, so it is | 647 // hasn't yet received a frame from the compositor at this stage, so it is |
| 468 // fully transparent until the GPU sends a frame swap IPC. For the blocking | 648 // fully transparent until the GPU sends a frame swap IPC. For the blocking |
| 469 // option, the animation needs to wait until AcceleratedWidgetSwapCompleted | 649 // option, the animation needs to wait until AcceleratedWidgetSwapCompleted |
| 470 // has been called at least once, otherwise it will animate nothing. | 650 // has been called at least once, otherwise it will animate nothing. |
| 471 [show_animation setAnimationBlockingMode:NSAnimationNonblocking]; | 651 [show_animation setAnimationBlockingMode:NSAnimationNonblocking]; |
| 472 [show_animation startAnimation]; | 652 [show_animation startAnimation]; |
| 473 } | 653 } |
| 474 } | 654 } |
| 475 | 655 |
| 476 void BridgedNativeWidget::AcquireCapture() { | 656 void BridgedNativeWidget::AcquireCapture() { |
| 657 base::debug::StackTrace().Print(); |
| 658 DLOG(INFO) << "AcquireCapture: " << this; |
| 477 DCHECK(!HasCapture()); | 659 DCHECK(!HasCapture()); |
| 478 if (!window_visible_) | 660 if (!window_visible_) |
| 479 return; // Capture on hidden windows is disallowed. | 661 return; // Capture on hidden windows is disallowed. |
| 480 | 662 |
| 481 mouse_capture_.reset(new CocoaMouseCapture(this)); | 663 mouse_capture_.reset(new CocoaMouseCapture(this)); |
| 482 | 664 |
| 483 // Initiating global event capture with addGlobalMonitorForEventsMatchingMask: | 665 // Initiating global event capture with addGlobalMonitorForEventsMatchingMask: |
| 484 // will reset the mouse cursor to an arrow. Asking the window for an update | 666 // will reset the mouse cursor to an arrow. Asking the window for an update |
| 485 // here will restore what we want. However, it can sometimes cause the cursor | 667 // here will restore what we want. However, it can sometimes cause the cursor |
| 486 // to flicker, once, on the initial mouseDown. | 668 // to flicker, once, on the initial mouseDown. |
| 487 // TOOD(tapted): Make this unnecessary by only asking for global mouse capture | 669 // TOOD(tapted): Make this unnecessary by only asking for global mouse capture |
| 488 // for the cases that need it (e.g. menus, but not drag and drop). | 670 // for the cases that need it (e.g. menus, but not drag and drop). |
| 489 [window_ cursorUpdate:[NSApp currentEvent]]; | 671 [window_ cursorUpdate:[NSApp currentEvent]]; |
| 490 } | 672 } |
| 491 | 673 |
| 492 void BridgedNativeWidget::ReleaseCapture() { | 674 void BridgedNativeWidget::ReleaseCapture() { |
| 675 base::debug::StackTrace().Print(); |
| 676 DLOG(INFO) << "ReleaseCapture: " << this << " drag_loop=" << drag_run_loop_; |
| 493 mouse_capture_.reset(); | 677 mouse_capture_.reset(); |
| 678 //if (drag_run_loop_) { |
| 679 // drag_run_loop_->Quit(); |
| 680 // drag_run_loop_ = nullptr; |
| 681 //} |
| 494 } | 682 } |
| 495 | 683 |
| 496 bool BridgedNativeWidget::HasCapture() { | 684 bool BridgedNativeWidget::HasCapture() { |
| 497 return mouse_capture_ && mouse_capture_->IsActive(); | 685 return mouse_capture_ && mouse_capture_->IsActive(); |
| 498 } | 686 } |
| 499 | 687 |
| 688 Widget::MoveLoopResult BridgedNativeWidget::RunMoveLoop( |
| 689 const gfx::Vector2d& drag_offset) { |
| 690 base::debug::StackTrace().Print(); |
| 691 DLOG(INFO) << "RunMoveLoop(): " << this << " drag_loop=" << drag_run_loop_; |
| 692 |
| 693 DCHECK(!HasCapture()); |
| 694 DCHECK(!window_move_loop_); |
| 695 |
| 696 // First, position the window in the right place. The point |drag_offset| |
| 697 // away from the top-left corner needs to be positioned under the mouse. |
| 698 // TODO(tapted): Figure out why the toolkit-views drag controller doesn't get |
| 699 // this right when it first initializes the Widget. |
| 700 NSPoint mouse_in_screen = [NSEvent mouseLocation]; |
| 701 NSRect frame = [window_ frame]; |
| 702 frame.origin.x = mouse_in_screen.x - drag_offset.x(); |
| 703 frame.origin.y = mouse_in_screen.y - NSHeight(frame) + drag_offset.y(); |
| 704 NSLog(@"mouse, frame, offset -> frame: %@, %@, %s, %@", |
| 705 NSStringFromRect([window_ frame]), |
| 706 NSStringFromPoint(mouse_in_screen), |
| 707 drag_offset.ToString().c_str(), |
| 708 NSStringFromRect(frame)); |
| 709 |
| 710 // After setting the frame to correct the initial offset, the drag controller |
| 711 // may immediately want to quit when it's notified of the new bounds. So the |
| 712 // MoveLoop must be set up before the call to setFrame. |
| 713 window_move_loop_.reset(new CocoaWindowMoveLoop(this)); |
| 714 |
| 715 // Animating may provide a less janky UX, but something custom would be |
| 716 // required so that it follows updates to the mouse position. |
| 717 [window_ setFrame:frame display:YES animate:NO]; |
| 718 |
| 719 // Setting the frame will call OnWidgetBoundsChanged(), which could result in |
| 720 // a call to EndMoveLoop(). |
| 721 if (!window_move_loop_) |
| 722 return Widget::MOVE_LOOP_SUCCESSFUL; |
| 723 |
| 724 return window_move_loop_->Run(); |
| 725 |
| 726 // |this| may be destroyed during the RunLoop, causing it to exit early. |
| 727 // Even if that doesn't happen, CocoaWindowMoveLoop will clean itself up by |
| 728 // calling EndMoveLoop(). So window_move_loop_ will always be null before the |
| 729 // function returns. But don't DCHECK since |this| might not be valid. |
| 730 } |
| 731 |
| 732 void BridgedNativeWidget::EndMoveLoop() { |
| 733 DCHECK(window_move_loop_); |
| 734 DLOG(INFO) << "Quitting due to EndMoveLoop for " << window_move_loop_.get(); |
| 735 window_move_loop_->End(); |
| 736 DLOG(INFO) << "Resetting: " << window_move_loop_.get(); |
| 737 window_move_loop_.reset(); |
| 738 } |
| 739 |
| 500 void BridgedNativeWidget::SetNativeWindowProperty(const char* name, | 740 void BridgedNativeWidget::SetNativeWindowProperty(const char* name, |
| 501 void* value) { | 741 void* value) { |
| 502 NSString* key = [NSString stringWithUTF8String:name]; | 742 NSString* key = [NSString stringWithUTF8String:name]; |
| 503 if (value) { | 743 if (value) { |
| 504 [GetWindowProperties() setObject:[NSValue valueWithPointer:value] | 744 [GetWindowProperties() setObject:[NSValue valueWithPointer:value] |
| 505 forKey:key]; | 745 forKey:key]; |
| 506 } else { | 746 } else { |
| 507 [GetWindowProperties() removeObjectForKey:key]; | 747 [GetWindowProperties() removeObjectForKey:key]; |
| 508 } | 748 } |
| 509 } | 749 } |
| 510 | 750 |
| 511 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { | 751 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { |
| 512 NSString* key = [NSString stringWithUTF8String:name]; | 752 NSString* key = [NSString stringWithUTF8String:name]; |
| 513 return [[GetWindowProperties() objectForKey:key] pointerValue]; | 753 return [[GetWindowProperties() objectForKey:key] pointerValue]; |
| 514 } | 754 } |
| 515 | 755 |
| 516 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { | 756 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { |
| 517 [window_delegate_ setCursor:cursor]; | 757 [window_delegate_ setCursor:cursor]; |
| 518 } | 758 } |
| 519 | 759 |
| 520 void BridgedNativeWidget::OnWindowWillClose() { | 760 void BridgedNativeWidget::OnWindowWillClose() { |
| 761 DCHECK(!drag_run_loop_); |
| 521 if (parent_) { | 762 if (parent_) { |
| 522 parent_->RemoveChildWindow(this); | 763 parent_->RemoveChildWindow(this); |
| 523 parent_ = nullptr; | 764 parent_ = nullptr; |
| 524 } | 765 } |
| 525 [window_ setDelegate:nil]; | 766 [window_ setDelegate:nil]; |
| 526 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; | 767 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; |
| 527 native_widget_mac_->OnWindowWillClose(); | 768 native_widget_mac_->OnWindowWillClose(); |
| 528 } | 769 } |
| 529 | 770 |
| 530 void BridgedNativeWidget::OnFullscreenTransitionStart( | 771 void BridgedNativeWidget::OnFullscreenTransitionStart( |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 602 [window_ toggleFullScreen:nil]; | 843 [window_ toggleFullScreen:nil]; |
| 603 } | 844 } |
| 604 | 845 |
| 605 void BridgedNativeWidget::OnSizeChanged() { | 846 void BridgedNativeWidget::OnSizeChanged() { |
| 606 gfx::Size new_size = GetClientAreaSize(); | 847 gfx::Size new_size = GetClientAreaSize(); |
| 607 native_widget_mac_->GetWidget()->OnNativeWidgetSizeChanged(new_size); | 848 native_widget_mac_->GetWidget()->OnNativeWidgetSizeChanged(new_size); |
| 608 if (layer()) | 849 if (layer()) |
| 609 UpdateLayerProperties(); | 850 UpdateLayerProperties(); |
| 610 } | 851 } |
| 611 | 852 |
| 853 void BridgedNativeWidget::OnPositionChanged() { |
| 854 native_widget_mac_->GetWidget()->OnNativeWidgetMove(); |
| 855 } |
| 856 |
| 612 void BridgedNativeWidget::OnVisibilityChanged() { | 857 void BridgedNativeWidget::OnVisibilityChanged() { |
| 613 OnVisibilityChangedTo([window_ isVisible]); | 858 OnVisibilityChangedTo([window_ isVisible]); |
| 614 } | 859 } |
| 615 | 860 |
| 616 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) { | 861 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) { |
| 617 if (window_visible_ == new_visibility) | 862 if (window_visible_ == new_visibility) |
| 618 return; | 863 return; |
| 619 | 864 |
| 620 window_visible_ = new_visibility; | 865 window_visible_ = new_visibility; |
| 621 | 866 |
| (...skipping 460 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1082 [bridged_view_ setMouseDownCanMoveWindow:draggable]; | 1327 [bridged_view_ setMouseDownCanMoveWindow:draggable]; |
| 1083 // AppKit will not update its cache of mouseDownCanMoveWindow unless something | 1328 // AppKit will not update its cache of mouseDownCanMoveWindow unless something |
| 1084 // changes. Previously we tried adding an NSView and removing it, but for some | 1329 // changes. Previously we tried adding an NSView and removing it, but for some |
| 1085 // reason it required reposting the mouse-down event, and didn't always work. | 1330 // reason it required reposting the mouse-down event, and didn't always work. |
| 1086 // Calling the below seems to be an effective solution. | 1331 // Calling the below seems to be an effective solution. |
| 1087 [window_ setMovableByWindowBackground:NO]; | 1332 [window_ setMovableByWindowBackground:NO]; |
| 1088 [window_ setMovableByWindowBackground:YES]; | 1333 [window_ setMovableByWindowBackground:YES]; |
| 1089 } | 1334 } |
| 1090 | 1335 |
| 1091 } // namespace views | 1336 } // namespace views |
| OLD | NEW |