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 |