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 #import "ui/views/cocoa/bridged_content_view.h" | |
| 11 #import "ui/views/cocoa/bridged_native_widget.h" | |
| 12 | |
| 13 // CocoaWindowMoveLoop can be deleted just before its local event monitor is | |
| 14 // processed and in that case it's too late to call removeMonitor: and we have a | |
| 15 // dangling this pointer. Use a proxy Obj-C class to store the weakptr. | |
| 16 @interface WeakCocoaWindowMoveLoop : NSObject { | |
| 17 @private | |
| 18 base::WeakPtr<views::CocoaWindowMoveLoop> weak_; | |
| 19 } | |
| 20 @end | |
| 21 | |
| 22 @implementation WeakCocoaWindowMoveLoop | |
| 23 - (id)initWithWeakPtr:(const base::WeakPtr<views::CocoaWindowMoveLoop>&)weak { | |
| 24 if (self = [super init]) { | |
|
tapted
2016/03/11 09:38:28
extra parens around assignment used as condition
themblsha
2016/04/05 17:20:42
Done. Hmm, strange that it didn't result in any wa
| |
| 25 weak_ = weak; | |
| 26 } | |
| 27 return self; | |
| 28 } | |
| 29 | |
| 30 - (base::WeakPtr<views::CocoaWindowMoveLoop>&)weak { | |
| 31 return weak_; | |
| 32 } | |
| 33 @end | |
| 34 | |
| 35 namespace { | |
| 36 | |
| 37 CGPoint GetCGMousePosition() { | |
| 38 return CGEventGetLocation( | |
| 39 base::ScopedCFTypeRef<CGEventRef>(CGEventCreate(nullptr))); | |
| 40 } | |
| 41 | |
| 42 void SendCustomLeftMouseEvent(CGEventType type) { | |
| 43 base::ScopedCFTypeRef<CGEventRef> event( | |
| 44 CGEventCreateMouseEvent( | |
| 45 nullptr, type, GetCGMousePosition(), | |
| 46 kCGMouseButtonLeft)); | |
| 47 CGEventSetIntegerValueField(event, kCGEventSourceUserData, 1); | |
|
tapted
2016/03/11 09:38:28
oops - this 1 should be the constant
themblsha
2016/04/05 17:20:42
Added kCocoaWindowMoveLoopSimulatedEventUserData c
| |
| 48 CGEventPost(kCGSessionEventTap, event); | |
| 49 } | |
| 50 | |
| 51 } // namespace | |
| 52 | |
| 53 namespace views { | |
| 54 | |
| 55 CocoaWindowMoveLoop::CocoaWindowMoveLoop(BridgedNativeWidget* owner) | |
| 56 : owner_(owner), | |
| 57 run_loop_(new base::RunLoop), | |
| 58 quit_closure_(run_loop_->QuitClosure()), | |
| 59 weak_factory_(this) { | |
| 60 // AppKit continues to send mouse events to the window, but toolkit-views | |
| 61 // doesn't expect them during RunMoveLoop(). | |
| 62 [owner_->ns_view() setIgnoreMouseEvents:YES]; | |
| 63 owner_->SetDraggable(true); | |
| 64 } | |
| 65 | |
| 66 CocoaWindowMoveLoop::~CocoaWindowMoveLoop() { | |
| 67 owner_->SetDraggable(false); | |
| 68 // Note the crafted events in Run() should not make their way through to | |
| 69 // toolkit-views, but regular events after that should be. So stop ignoring. | |
| 70 [owner_->ns_view() setIgnoreMouseEvents:NO]; | |
|
tapted
2016/03/11 09:38:28
Can this now use IsRunMoveLoopActive()?
themblsha
2016/04/05 17:20:42
When the dragged tab is reattached to the window,
| |
| 71 | |
| 72 // Handle the pathological case, where |this| is destroyed while running. | |
| 73 if (exit_reason_ref_) { | |
| 74 *exit_reason_ref_ = WINDOW_DESTROYED; | |
| 75 quit_closure_.Run(); | |
| 76 } | |
| 77 | |
| 78 // Handle Run() never being called. | |
| 79 if (monitor_) { | |
| 80 [NSEvent removeMonitor:monitor_]; | |
| 81 monitor_ = nil; | |
| 82 } | |
| 83 owner_ = nullptr; | |
| 84 } | |
| 85 | |
| 86 Widget::MoveLoopResult CocoaWindowMoveLoop::Run() { | |
| 87 LoopExitReason exit_reason = ENDED_EXTERNALLY; | |
| 88 exit_reason_ref_ = &exit_reason; | |
| 89 | |
| 90 // Move the RunLoop to the stack so our destructor can be called while it is | |
| 91 // running. | |
| 92 scoped_ptr<base::RunLoop> run_loop(std::move(run_loop_)); | |
| 93 | |
| 94 // A new window may have just been created, so post an event at the session | |
| 95 // tap level to initiate a window drag. | |
| 96 // TODO(tapted): Move this "inside" the window or stuff breaks when dragging | |
|
tapted
2016/03/11 09:38:28
I think this needs to be done to fix one of those
themblsha
2016/04/05 17:20:42
Removed the comment. Now I check for |expected_win
| |
| 97 // the last tab/s off a browser. | |
| 98 SendCustomLeftMouseEvent(kCGEventLeftMouseDown); | |
| 99 | |
| 100 // Will be retained by the monitor handler block. | |
| 101 WeakCocoaWindowMoveLoop* proxy_quit_closure = | |
| 102 [[[WeakCocoaWindowMoveLoop alloc] | |
| 103 initWithWeakPtr:weak_factory_.GetWeakPtr()] autorelease]; | |
| 104 | |
| 105 NSEventMask mask = NSLeftMouseUpMask | NSKeyDownMask | NSLeftMouseDraggedMask; | |
| 106 monitor_ = | |
|
tapted
2016/03/11 09:38:28
Does this need to be a data member still?
Can we
themblsha
2016/04/05 17:20:42
Done. Got no new test failures, so it seems to be
| |
| 107 [NSEvent addLocalMonitorForEventsMatchingMask: | |
| 108 mask handler:^NSEvent * (NSEvent * event) { | |
|
tapted
2016/03/11 09:38:28
I think we'll have to override clang format on thi
themblsha
2016/04/05 17:20:42
Done. Extracted the 'auto handler' variable.
| |
| 109 if ([event type] == NSLeftMouseDragged) { | |
| 110 // AppKit doesn't supply position updates during a drag, | |
| 111 // so post a task to notify observers once AppKit has | |
| 112 // moved the window. | |
| 113 base::MessageLoop::current()->PostTask( | |
| 114 FROM_HERE, | |
| 115 base::Bind(&BridgedNativeWidget::OnPositionChanged, | |
| 116 base::Unretained(owner_))); | |
| 117 return event; | |
| 118 } | |
| 119 | |
| 120 CocoaWindowMoveLoop* thiss = | |
|
tapted
2016/03/11 09:38:28
perhaps thiss->`this_move_loop`, but can this weak
themblsha
2016/04/05 17:20:42
I guess I just got lucky that the event that got C
| |
| 121 [proxy_quit_closure weak].get(); | |
| 122 if (!thiss) | |
| 123 return nil; | |
| 124 | |
| 125 thiss->quit_closure_.Run(); | |
| 126 if ([event type] == NSLeftMouseUp) { | |
| 127 *thiss->exit_reason_ref_ = MOUSE_UP; | |
| 128 return event; // Process the MouseUp. | |
| 129 } | |
| 130 *thiss->exit_reason_ref_ = ESCAPE_PRESSED; | |
| 131 return nil; // Swallow the keypress. | |
| 132 }]; | |
| 133 | |
| 134 // NSKeyDownMask doesn't work inside addLocalMonitorForEventsMatchingMask: | |
| 135 // the event is swallowed by the window move loop before it gets to -[NSApp | |
| 136 // sendEvent:]. To see an escape keypress, hook in an event tap lower. | |
| 137 | |
| 138 run_loop->Run(); | |
| 139 | |
| 140 if (exit_reason != WINDOW_DESTROYED && exit_reason != ENDED_EXTERNALLY) { | |
| 141 exit_reason_ref_ = nullptr; // Ensure End() doesn't replace the reason. | |
| 142 owner_->EndMoveLoop(); // Deletes |this|. | |
| 143 } | |
| 144 | |
| 145 if (exit_reason != MOUSE_UP) { | |
| 146 // Tell AppKit to stop moving the window. Otherwise, AppKit will refuse to | |
| 147 // start a new window drag. Note the window being dragged is going away in | |
| 148 // this case, so it doesn't really matter where the event is located. | |
| 149 SendCustomLeftMouseEvent(kCGEventLeftMouseUp); | |
| 150 | |
| 151 if (exit_reason == ENDED_EXTERNALLY) { | |
| 152 // When not canceled, the non-moving drag in the original window must | |
| 153 // resume. To do this, AppKit needs to see a mouseDown so that it sends | |
| 154 // the correct events. Ideally, it also needs to be at the original | |
| 155 // offset, so that it hits a non-draggable region of the original | |
| 156 // window: The tab being dragged may move some distance from the cursor | |
| 157 // when it "snaps in", so the cursor may not be over a tab. Sadly, this | |
| 158 // method doesn't know which window that is. But all that really needs | |
| 159 // to be done is to prevent a custom-dragging area from starting a | |
| 160 // window-drag. So hook into the logic in RepostEventIfHandledByWindow() | |
| 161 // by setting a flag here. Note this assumes the custom mouseDown event | |
| 162 // is guaranteed to hit another BridgedNativeWidget when it gets to the | |
| 163 // front of the event queue. | |
| 164 // TODO(tapted): A better fix would be to keep the temporary window | |
| 165 // around and never call EndMoveLoop() on Mac, making this block of code | |
| 166 // obsolete. | |
| 167 BridgedNativeWidget::IgnoreNextMouseDownForDraggableRegions(); | |
| 168 SendCustomLeftMouseEvent(kCGEventLeftMouseDown); | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 return exit_reason == ESCAPE_PRESSED ? Widget::MOVE_LOOP_CANCELED | |
| 173 : Widget::MOVE_LOOP_SUCCESSFUL; | |
| 174 } | |
| 175 | |
| 176 void CocoaWindowMoveLoop::End() { | |
| 177 if (exit_reason_ref_) { | |
| 178 DCHECK_EQ(*exit_reason_ref_, ENDED_EXTERNALLY); | |
| 179 // Ensure the destructor doesn't replace the reason. | |
| 180 exit_reason_ref_ = nullptr; | |
| 181 quit_closure_.Run(); | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 } // namespace views | |
| OLD | NEW |