Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 #include "ui/views/cocoa/cocoa_window_move_loop.h" | |
| 6 | |
| 7 #import <Cocoa/Cocoa.h> | |
| 8 | |
| 9 #include "base/run_loop.h" | |
| 10 #include "ui/display/screen.h" | |
| 11 #import "ui/gfx/mac/coordinate_conversion.mm" | |
| 12 #import "ui/views/cocoa/bridged_content_view.h" | |
| 13 #import "ui/views/cocoa/bridged_native_widget.h" | |
| 14 | |
| 15 // CocoaWindowMoveLoop can be deleted just before its local event monitor is | |
| 16 // processed and in that case it's too late to call removeMonitor: and we have a | |
| 17 // dangling this pointer. Use a proxy Obj-C class to store the weakptr. | |
|
tapted
2016/05/23 07:29:28
This needs to say something about your findings re
themblsha
2016/05/26 15:13:25
Reworded the comment. Actually the class is no lon
| |
| 18 @interface WeakCocoaWindowMoveLoop : NSObject { | |
| 19 @private | |
| 20 base::WeakPtr<views::CocoaWindowMoveLoop> weak_; | |
| 21 } | |
| 22 @end | |
| 23 | |
| 24 @implementation WeakCocoaWindowMoveLoop | |
| 25 - (id)initWithWeakPtr:(const base::WeakPtr<views::CocoaWindowMoveLoop>&)weak { | |
| 26 if ((self = [super init])) { | |
| 27 weak_ = weak; | |
| 28 } | |
| 29 return self; | |
| 30 } | |
| 31 | |
| 32 - (base::WeakPtr<views::CocoaWindowMoveLoop>&)weak { | |
| 33 return weak_; | |
| 34 } | |
| 35 @end | |
| 36 | |
| 37 namespace views { | |
| 38 | |
| 39 CocoaWindowMoveLoop::CocoaWindowMoveLoop( | |
| 40 BridgedNativeWidget* owner, | |
| 41 const gfx::Point& initial_mouse_in_screen) | |
| 42 : owner_(owner), | |
| 43 initial_mouse_in_screen_(initial_mouse_in_screen), | |
| 44 weak_factory_(this) { | |
| 45 // AppKit continues to send mouse events to the window, but toolkit-views | |
| 46 // doesn't expect them during RunMoveLoop(). | |
| 47 [BridgedContentView setIgnoreMouseEvents:YES]; | |
| 48 owner_->SetDraggable(true); | |
| 49 } | |
| 50 | |
| 51 CocoaWindowMoveLoop::~CocoaWindowMoveLoop() { | |
| 52 owner_->SetDraggable(false); | |
| 53 // Note the crafted events in Run() should not make their way through to | |
| 54 // toolkit-views, but regular events after that should be. So stop ignoring. | |
| 55 [BridgedContentView setIgnoreMouseEvents:NO]; | |
| 56 | |
| 57 // Handle the pathological case, where |this| is destroyed while running. | |
| 58 if (exit_reason_ref_) { | |
| 59 *exit_reason_ref_ = WINDOW_DESTROYED; | |
| 60 quit_closure_.Run(); | |
| 61 } | |
| 62 | |
| 63 owner_ = nullptr; | |
| 64 } | |
| 65 | |
| 66 Widget::MoveLoopResult CocoaWindowMoveLoop::Run() { | |
| 67 LoopExitReason exit_reason = ENDED_EXTERNALLY; | |
| 68 exit_reason_ref_ = &exit_reason; | |
| 69 NSWindow* window = owner_->ns_window(); | |
| 70 const gfx::Rect initial_frame = gfx::ScreenRectFromNSRect([window frame]); | |
| 71 | |
| 72 base::RunLoop run_loop; | |
| 73 quit_closure_ = run_loop.QuitClosure(); | |
| 74 | |
| 75 // Will be retained by the monitor handler block. | |
| 76 WeakCocoaWindowMoveLoop* weak_cocoa_window_move_loop = | |
| 77 [[[WeakCocoaWindowMoveLoop alloc] | |
| 78 initWithWeakPtr:weak_factory_.GetWeakPtr()] autorelease]; | |
| 79 | |
| 80 // NSKeyDownMask doesn't work inside addLocalMonitorForEventsMatchingMask: | |
|
tapted
2016/05/23 07:29:28
This comment isn't right. Instead there should be
themblsha
2016/05/26 15:13:25
TabDragController now instantiates EscapeTracker w
| |
| 81 // the event is swallowed by the window move loop before it gets to -[NSApp | |
| 82 // sendEvent:]. To see an escape keypress, hook in an event tap lower. | |
| 83 NSEventMask mask = NSLeftMouseUpMask | NSLeftMouseDraggedMask; | |
| 84 auto handler = ^NSEvent*(NSEvent* event) { | |
| 85 CocoaWindowMoveLoop* strong = [weak_cocoa_window_move_loop weak].get(); | |
| 86 if (!strong) { | |
| 87 // By this point CocoaWindowMoveLoop was deleted while processing this | |
| 88 // same event, and this event monitor was not unregistered in time. It is | |
| 89 // reproducible by the PressEscapeWhileDetached test. | |
| 90 // Continue processing the event. | |
| 91 return event; | |
| 92 } | |
| 93 | |
| 94 if ([event type] == NSLeftMouseDragged) { | |
| 95 gfx::Point mouse_in_screen = | |
| 96 display::Screen::GetScreen()->GetCursorScreenPoint(); | |
|
tapted
2016/05/23 07:29:28
This should come off |event|, no?
themblsha
2016/05/26 15:13:25
There's only instance-specific locationInWindow, a
tapted
2016/06/01 11:29:55
Hm - ok. I think I'd prefer this to use [NSEvent m
| |
| 97 | |
| 98 gfx::Rect frame = initial_frame; | |
| 99 frame.Offset(mouse_in_screen.x() - initial_mouse_in_screen_.x(), | |
| 100 mouse_in_screen.y() - initial_mouse_in_screen_.y()); | |
| 101 | |
| 102 const NSRect ns_frame = gfx::ScreenRectToNSRect(frame); | |
|
tapted
2016/05/23 07:29:28
There's no need to convert all these coordinates j
themblsha
2016/05/26 15:13:25
Used NSOffsetRect in the end. Code definitely look
| |
| 103 [window setFrame:ns_frame display:YES animate:NO]; | |
|
tapted
2016/05/23 07:29:28
Does display:NO work? It might do less work. We pr
themblsha
2016/05/26 15:13:25
Works fine, thanks! I've even used display:NO in B
| |
| 104 | |
| 105 return event; | |
| 106 } | |
| 107 | |
| 108 strong->quit_closure_.Run(); | |
| 109 if ([event type] == NSLeftMouseUp) { | |
|
tapted
2016/05/23 07:29:28
DCHECK_EQ(NSLeftMouseUp, [event type])
no need fo
themblsha
2016/05/26 15:13:25
Good idea, thanks.
| |
| 110 *strong->exit_reason_ref_ = MOUSE_UP; | |
| 111 return event; // Process the MouseUp. | |
| 112 } | |
| 113 | |
| 114 NOTREACHED(); | |
| 115 return event; | |
| 116 }; | |
| 117 id monitor = | |
| 118 [NSEvent addLocalMonitorForEventsMatchingMask:mask handler:handler]; | |
| 119 | |
| 120 run_loop.Run(); | |
| 121 [NSEvent removeMonitor:monitor]; | |
| 122 | |
| 123 if (exit_reason != WINDOW_DESTROYED && exit_reason != ENDED_EXTERNALLY) { | |
| 124 exit_reason_ref_ = nullptr; // Ensure End() doesn't replace the reason. | |
| 125 owner_->EndMoveLoop(); // Deletes |this|. | |
| 126 } | |
| 127 | |
| 128 return exit_reason != MOUSE_UP ? Widget::MOVE_LOOP_CANCELED | |
| 129 : Widget::MOVE_LOOP_SUCCESSFUL; | |
| 130 } | |
| 131 | |
| 132 void CocoaWindowMoveLoop::End() { | |
| 133 if (exit_reason_ref_) { | |
| 134 DCHECK_EQ(*exit_reason_ref_, ENDED_EXTERNALLY); | |
| 135 // Ensure the destructor doesn't replace the reason. | |
| 136 exit_reason_ref_ = nullptr; | |
| 137 quit_closure_.Run(); | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 void CocoaWindowMoveLoop::OnPositionChanged() { | |
| 142 return owner_->OnPositionChanged(); | |
| 143 } | |
| 144 | |
| 145 } // namespace views | |
| OLD | NEW |