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 |