OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #include "ash/wm/workspace/frame_maximize_button.h" | 5 #include "ash/wm/workspace/frame_maximize_button.h" |
6 | 6 |
7 #include "ash/launcher/launcher.h" | 7 #include "ash/launcher/launcher.h" |
8 #include "ash/screen_ash.h" | 8 #include "ash/screen_ash.h" |
9 #include "ash/shell.h" | 9 #include "ash/shell.h" |
10 #include "ash/wm/property_util.h" | 10 #include "ash/wm/property_util.h" |
| 11 #include "ash/wm/maximize_bubble_controller.h" |
11 #include "ash/wm/workspace/phantom_window_controller.h" | 12 #include "ash/wm/workspace/phantom_window_controller.h" |
12 #include "ash/wm/workspace/snap_sizer.h" | 13 #include "ash/wm/workspace/snap_sizer.h" |
13 #include "grit/ash_strings.h" | 14 #include "grit/ash_strings.h" |
14 #include "grit/ui_resources.h" | 15 #include "grit/ui_resources.h" |
15 #include "ui/aura/event.h" | 16 #include "ui/aura/event.h" |
16 #include "ui/aura/event_filter.h" | 17 #include "ui/aura/event_filter.h" |
17 #include "ui/aura/window.h" | 18 #include "ui/aura/window.h" |
18 #include "ui/base/l10n/l10n_util.h" | 19 #include "ui/base/l10n/l10n_util.h" |
19 #include "ui/base/resource/resource_bundle.h" | 20 #include "ui/base/resource/resource_bundle.h" |
20 #include "ui/gfx/image/image.h" | 21 #include "ui/gfx/image/image.h" |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 | 68 |
68 FrameMaximizeButton::EscapeEventFilter::~EscapeEventFilter() { | 69 FrameMaximizeButton::EscapeEventFilter::~EscapeEventFilter() { |
69 Shell::GetInstance()->RemoveEnvEventFilter(this); | 70 Shell::GetInstance()->RemoveEnvEventFilter(this); |
70 } | 71 } |
71 | 72 |
72 bool FrameMaximizeButton::EscapeEventFilter::PreHandleKeyEvent( | 73 bool FrameMaximizeButton::EscapeEventFilter::PreHandleKeyEvent( |
73 aura::Window* target, | 74 aura::Window* target, |
74 aura::KeyEvent* event) { | 75 aura::KeyEvent* event) { |
75 if (event->type() == ui::ET_KEY_PRESSED && | 76 if (event->type() == ui::ET_KEY_PRESSED && |
76 event->key_code() == ui::VKEY_ESCAPE) { | 77 event->key_code() == ui::VKEY_ESCAPE) { |
77 button_->Cancel(); | 78 button_->Cancel(false); |
78 } | 79 } |
79 return false; | 80 return false; |
80 } | 81 } |
81 | 82 |
82 bool FrameMaximizeButton::EscapeEventFilter::PreHandleMouseEvent( | 83 bool FrameMaximizeButton::EscapeEventFilter::PreHandleMouseEvent( |
83 aura::Window* target, | 84 aura::Window* target, |
84 aura::MouseEvent* event) { | 85 aura::MouseEvent* event) { |
85 return false; | 86 return false; |
86 } | 87 } |
87 | 88 |
(...skipping 10 matching lines...) Expand all Loading... |
98 } | 99 } |
99 | 100 |
100 // FrameMaximizeButton --------------------------------------------------------- | 101 // FrameMaximizeButton --------------------------------------------------------- |
101 | 102 |
102 FrameMaximizeButton::FrameMaximizeButton(views::ButtonListener* listener, | 103 FrameMaximizeButton::FrameMaximizeButton(views::ButtonListener* listener, |
103 views::NonClientFrameView* frame) | 104 views::NonClientFrameView* frame) |
104 : ImageButton(listener), | 105 : ImageButton(listener), |
105 frame_(frame), | 106 frame_(frame), |
106 is_snap_enabled_(false), | 107 is_snap_enabled_(false), |
107 exceeded_drag_threshold_(false), | 108 exceeded_drag_threshold_(false), |
| 109 window_(NULL), |
108 snap_type_(SNAP_NONE) { | 110 snap_type_(SNAP_NONE) { |
109 // TODO(sky): nuke this. It's temporary while we don't have good images. | 111 // TODO(sky): nuke this. It's temporary while we don't have good images. |
110 SetImageAlignment(ALIGN_LEFT, ALIGN_BOTTOM); | 112 SetImageAlignment(ALIGN_LEFT, ALIGN_BOTTOM); |
111 SetTooltipText(l10n_util::GetStringUTF16(IDS_FRAME_MAXIMIZE_BUTTON_TOOLTIP)); | |
112 } | 113 } |
113 | 114 |
114 FrameMaximizeButton::~FrameMaximizeButton() { | 115 FrameMaximizeButton::~FrameMaximizeButton() { |
115 } | 116 } |
116 | 117 |
| 118 void FrameMaximizeButton::SnapButtonHovered(SnapType type) { |
| 119 // Make sure to only show hover operations when no button is pressed and |
| 120 // a similar snap operation in progress does not get re-applied. |
| 121 if (is_snap_enabled_ || (type == snap_type_ && snap_sizer_.get())) |
| 122 return; |
| 123 // Prime the mouse location with the center of the (local) button. |
| 124 press_location_ = gfx::Point(width() / 2, height() / 2); |
| 125 // Then get an adjusted mouse position to initiate the effect. |
| 126 gfx::Point location = press_location_; |
| 127 switch (type) { |
| 128 case SNAP_LEFT: |
| 129 location.set_x(location.x() - width()); |
| 130 break; |
| 131 case SNAP_RIGHT: |
| 132 location.set_x(location.x() + width()); |
| 133 break; |
| 134 case SNAP_MINIMIZE: |
| 135 location.set_y(location.y() + height()); |
| 136 break; |
| 137 case SNAP_MAXIMIZE: |
| 138 case SNAP_RESTORE: |
| 139 break; |
| 140 case SNAP_NONE: |
| 141 Cancel(true); |
| 142 return; |
| 143 default: |
| 144 // We should not come here. |
| 145 NOTREACHED(); |
| 146 } |
| 147 UpdateSnap(location); |
| 148 } |
| 149 |
| 150 void FrameMaximizeButton::ExecuteSnapAndCloseMenu(SnapType snap_type) { |
| 151 DCHECK_NE(snap_type_, SNAP_NONE); |
| 152 snap_type_ = snap_type; |
| 153 Snap(); |
| 154 // Remove any pending snap previews. |
| 155 SnapButtonHovered(SNAP_NONE); |
| 156 // At this point the operation has been performed and the menu should be |
| 157 // closed - if not, it'll get now closed. |
| 158 maximizer_.reset(); |
| 159 } |
| 160 |
| 161 void FrameMaximizeButton::DestroyMaximizeMenu() { |
| 162 maximizer_.reset(); |
| 163 } |
| 164 |
| 165 void FrameMaximizeButton::OnWindowBoundsChanged( |
| 166 aura::Window* window, |
| 167 const gfx::Rect& old_bounds, |
| 168 const gfx::Rect& new_bounds) { |
| 169 Cancel(false); |
| 170 } |
| 171 |
| 172 void FrameMaximizeButton::OnWindowDestroying(aura::Window* window) { |
| 173 maximizer_.reset(); |
| 174 if (window_) { |
| 175 CHECK_EQ(window_, window); |
| 176 window_->RemoveObserver(this); |
| 177 window_ = NULL; |
| 178 } |
| 179 } |
| 180 |
117 bool FrameMaximizeButton::OnMousePressed(const views::MouseEvent& event) { | 181 bool FrameMaximizeButton::OnMousePressed(const views::MouseEvent& event) { |
118 is_snap_enabled_ = event.IsLeftMouseButton(); | 182 is_snap_enabled_ = event.IsLeftMouseButton(); |
119 if (is_snap_enabled_) | 183 if (is_snap_enabled_) |
120 ProcessStartEvent(event); | 184 ProcessStartEvent(event); |
121 ImageButton::OnMousePressed(event); | 185 ImageButton::OnMousePressed(event); |
122 return true; | 186 return true; |
123 } | 187 } |
124 | 188 |
125 void FrameMaximizeButton::OnMouseEntered(const views::MouseEvent& event) { | 189 void FrameMaximizeButton::OnMouseEntered(const views::MouseEvent& event) { |
126 ImageButton::OnMouseEntered(event); | 190 ImageButton::OnMouseEntered(event); |
| 191 if (!maximizer_.get()) { |
| 192 DCHECK(GetWidget()); |
| 193 if (!window_) { |
| 194 window_ = GetWidget()->GetNativeWindow(); |
| 195 window_->AddObserver(this); |
| 196 } |
| 197 maximizer_.reset(new MaximizeBubbleController( |
| 198 this, |
| 199 GetWidget()->IsMaximized())); |
| 200 } |
127 } | 201 } |
128 | 202 |
129 void FrameMaximizeButton::OnMouseExited(const views::MouseEvent& event) { | 203 void FrameMaximizeButton::OnMouseExited(const views::MouseEvent& event) { |
130 ImageButton::OnMouseExited(event); | 204 ImageButton::OnMouseExited(event); |
| 205 // Remove the bubble menu when the button is not pressed and the mouse is not |
| 206 // within the bubble. |
| 207 if (!is_snap_enabled_ && maximizer_.get() && maximizer_->GetBubbleWindow()) { |
| 208 gfx::Point screen_location = gfx::Screen::GetCursorScreenPoint(); |
| 209 if (!maximizer_->GetBubbleWindow()->GetBoundsInScreen().Contains( |
| 210 screen_location)) { |
| 211 maximizer_.reset(); |
| 212 // Make sure that all remaining snap hover states get removed. |
| 213 SnapButtonHovered(SNAP_NONE); |
| 214 } |
| 215 } |
131 } | 216 } |
132 | 217 |
133 bool FrameMaximizeButton::OnMouseDragged(const views::MouseEvent& event) { | 218 bool FrameMaximizeButton::OnMouseDragged(const views::MouseEvent& event) { |
134 if (is_snap_enabled_) | 219 if (is_snap_enabled_) |
135 ProcessUpdateEvent(event); | 220 ProcessUpdateEvent(event); |
136 return ImageButton::OnMouseDragged(event); | 221 return ImageButton::OnMouseDragged(event); |
137 } | 222 } |
138 | 223 |
139 void FrameMaximizeButton::OnMouseReleased(const views::MouseEvent& event) { | 224 void FrameMaximizeButton::OnMouseReleased(const views::MouseEvent& event) { |
140 if (!ProcessEndEvent(event)) | 225 if (!ProcessEndEvent(event)) |
141 ImageButton::OnMouseReleased(event); | 226 ImageButton::OnMouseReleased(event); |
| 227 maximizer_.reset(); |
142 } | 228 } |
143 | 229 |
144 void FrameMaximizeButton::OnMouseCaptureLost() { | 230 void FrameMaximizeButton::OnMouseCaptureLost() { |
145 Cancel(); | 231 Cancel(false); |
146 ImageButton::OnMouseCaptureLost(); | 232 ImageButton::OnMouseCaptureLost(); |
147 } | 233 } |
148 | 234 |
149 ui::GestureStatus FrameMaximizeButton::OnGestureEvent( | 235 ui::GestureStatus FrameMaximizeButton::OnGestureEvent( |
150 const views::GestureEvent& event) { | 236 const views::GestureEvent& event) { |
151 if (event.type() == ui::ET_GESTURE_TAP_DOWN) { | 237 if (event.type() == ui::ET_GESTURE_TAP_DOWN) { |
152 is_snap_enabled_ = true; | 238 is_snap_enabled_ = true; |
153 ProcessStartEvent(event); | 239 ProcessStartEvent(event); |
154 return ui::GESTURE_STATUS_CONSUMED; | 240 return ui::GESTURE_STATUS_CONSUMED; |
155 } | 241 } |
(...skipping 17 matching lines...) Expand all Loading... |
173 if (event.type() == ui::ET_GESTURE_SCROLL_UPDATE || | 259 if (event.type() == ui::ET_GESTURE_SCROLL_UPDATE || |
174 event.type() == ui::ET_GESTURE_SCROLL_BEGIN) { | 260 event.type() == ui::ET_GESTURE_SCROLL_BEGIN) { |
175 ProcessUpdateEvent(event); | 261 ProcessUpdateEvent(event); |
176 return ui::GESTURE_STATUS_CONSUMED; | 262 return ui::GESTURE_STATUS_CONSUMED; |
177 } | 263 } |
178 } | 264 } |
179 | 265 |
180 return ImageButton::OnGestureEvent(event); | 266 return ImageButton::OnGestureEvent(event); |
181 } | 267 } |
182 | 268 |
183 gfx::ImageSkia FrameMaximizeButton::GetImageToPaint() { | |
184 if (is_snap_enabled_) { | |
185 int id = 0; | |
186 if (frame_->GetWidget()->IsMaximized()) { | |
187 switch (snap_type_) { | |
188 case SNAP_LEFT: | |
189 id = IDR_AURA_WINDOW_MAXIMIZED_SNAP_LEFT_P; | |
190 break; | |
191 case SNAP_RIGHT: | |
192 id = IDR_AURA_WINDOW_MAXIMIZED_SNAP_RIGHT_P; | |
193 break; | |
194 case SNAP_MAXIMIZE: | |
195 case SNAP_RESTORE: | |
196 case SNAP_NONE: | |
197 id = IDR_AURA_WINDOW_MAXIMIZED_SNAP_P; | |
198 break; | |
199 case SNAP_MINIMIZE: | |
200 id = IDR_AURA_WINDOW_MAXIMIZED_SNAP_MINIMIZE_P; | |
201 break; | |
202 default: | |
203 NOTREACHED(); | |
204 } | |
205 } else { | |
206 switch (snap_type_) { | |
207 case SNAP_LEFT: | |
208 id = IDR_AURA_WINDOW_SNAP_LEFT_P; | |
209 break; | |
210 case SNAP_RIGHT: | |
211 id = IDR_AURA_WINDOW_SNAP_RIGHT_P; | |
212 break; | |
213 case SNAP_MAXIMIZE: | |
214 case SNAP_RESTORE: | |
215 case SNAP_NONE: | |
216 id = IDR_AURA_WINDOW_SNAP_P; | |
217 break; | |
218 case SNAP_MINIMIZE: | |
219 id = IDR_AURA_WINDOW_SNAP_MINIMIZE_P; | |
220 break; | |
221 default: | |
222 NOTREACHED(); | |
223 } | |
224 } | |
225 return *ResourceBundle::GetSharedInstance().GetImageNamed(id).ToImageSkia(); | |
226 } | |
227 // Hot and pressed states handled by regular ImageButton. | |
228 return ImageButton::GetImageToPaint(); | |
229 } | |
230 | |
231 void FrameMaximizeButton::ProcessStartEvent(const views::LocatedEvent& event) { | 269 void FrameMaximizeButton::ProcessStartEvent(const views::LocatedEvent& event) { |
232 DCHECK(is_snap_enabled_); | 270 DCHECK(is_snap_enabled_); |
| 271 // Prepare the help menu. |
| 272 if (!maximizer_.get()) { |
| 273 maximizer_.reset(new MaximizeBubbleController( |
| 274 this, |
| 275 frame_->GetWidget()->IsMaximized())); |
| 276 } else { |
| 277 // If the menu did not show up yet, we delay it even a bit more. |
| 278 maximizer_->DelayCreation(); |
| 279 } |
233 snap_sizer_.reset(NULL); | 280 snap_sizer_.reset(NULL); |
234 InstallEventFilter(); | 281 InstallEventFilter(); |
235 snap_type_ = SNAP_NONE; | 282 snap_type_ = SNAP_NONE; |
236 press_location_ = event.location(); | 283 press_location_ = event.location(); |
237 exceeded_drag_threshold_ = false; | 284 exceeded_drag_threshold_ = false; |
238 update_timer_.Start( | 285 update_timer_.Start( |
239 FROM_HERE, | 286 FROM_HERE, |
240 base::TimeDelta::FromMilliseconds(kUpdateDelayMS), | 287 base::TimeDelta::FromMilliseconds(kUpdateDelayMS), |
241 this, | 288 this, |
242 &FrameMaximizeButton::UpdateSnapFromEventLocation); | 289 &FrameMaximizeButton::UpdateSnapFromEventLocation); |
(...skipping 10 matching lines...) Expand all Loading... |
253 if (exceeded_drag_threshold_) | 300 if (exceeded_drag_threshold_) |
254 UpdateSnap(event.location()); | 301 UpdateSnap(event.location()); |
255 } | 302 } |
256 | 303 |
257 bool FrameMaximizeButton::ProcessEndEvent(const views::LocatedEvent& event) { | 304 bool FrameMaximizeButton::ProcessEndEvent(const views::LocatedEvent& event) { |
258 update_timer_.Stop(); | 305 update_timer_.Stop(); |
259 UninstallEventFilter(); | 306 UninstallEventFilter(); |
260 bool should_snap = is_snap_enabled_; | 307 bool should_snap = is_snap_enabled_; |
261 is_snap_enabled_ = false; | 308 is_snap_enabled_ = false; |
262 | 309 |
| 310 // Remove our help bubble. |
| 311 maximizer_.reset(); |
| 312 |
263 if (!should_snap || snap_type_ == SNAP_NONE) | 313 if (!should_snap || snap_type_ == SNAP_NONE) |
264 return false; | 314 return false; |
265 | 315 |
266 SetState(BS_NORMAL); | 316 SetState(BS_NORMAL); |
267 // SetState will not call SchedulePaint() if state was already set to | 317 // SetState will not call SchedulePaint() if state was already set to |
268 // BS_NORMAL during a drag. | 318 // BS_NORMAL during a drag. |
269 SchedulePaint(); | 319 SchedulePaint(); |
270 phantom_window_.reset(); | 320 phantom_window_.reset(); |
271 Snap(); | 321 Snap(); |
272 return true; | 322 return true; |
273 } | 323 } |
274 | 324 |
275 void FrameMaximizeButton::Cancel() { | 325 void FrameMaximizeButton::Cancel(bool keep_menu_open) { |
276 UninstallEventFilter(); | 326 if (!keep_menu_open) { |
277 is_snap_enabled_ = false; | 327 maximizer_.reset(); |
| 328 UninstallEventFilter(); |
| 329 is_snap_enabled_ = false; |
| 330 } |
278 phantom_window_.reset(); | 331 phantom_window_.reset(); |
279 snap_sizer_.reset(); | 332 snap_sizer_.reset(); |
| 333 snap_type_ = SNAP_NONE; |
280 update_timer_.Stop(); | 334 update_timer_.Stop(); |
281 SchedulePaint(); | 335 SchedulePaint(); |
282 } | 336 } |
283 | 337 |
284 void FrameMaximizeButton::InstallEventFilter() { | 338 void FrameMaximizeButton::InstallEventFilter() { |
285 if (escape_event_filter_.get()) | 339 if (escape_event_filter_.get()) |
286 return; | 340 return; |
287 | 341 |
288 escape_event_filter_.reset(new EscapeEventFilter(this)); | 342 escape_event_filter_.reset(new EscapeEventFilter(this)); |
289 } | 343 } |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE; | 380 SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE; |
327 int grid_size = Shell::GetInstance()->GetGridSize(); | 381 int grid_size = Shell::GetInstance()->GetGridSize(); |
328 snap_sizer_.reset(new SnapSizer(frame_->GetWidget()->GetNativeWindow(), | 382 snap_sizer_.reset(new SnapSizer(frame_->GetWidget()->GetNativeWindow(), |
329 LocationForSnapSizer(location), | 383 LocationForSnapSizer(location), |
330 snap_edge, grid_size)); | 384 snap_edge, grid_size)); |
331 } | 385 } |
332 if (!phantom_window_.get()) { | 386 if (!phantom_window_.get()) { |
333 phantom_window_.reset(new internal::PhantomWindowController( | 387 phantom_window_.reset(new internal::PhantomWindowController( |
334 frame_->GetWidget()->GetNativeWindow())); | 388 frame_->GetWidget()->GetNativeWindow())); |
335 } | 389 } |
| 390 if (maximizer_.get()) { |
| 391 phantom_window_->set_phantom_below_window(maximizer_->GetBubbleWindow()); |
| 392 maximizer_->SetSnapType(snap_type_); |
| 393 } |
336 phantom_window_->Show(ScreenBoundsForType(snap_type_)); | 394 phantom_window_->Show(ScreenBoundsForType(snap_type_)); |
337 } | 395 } |
338 | 396 |
339 FrameMaximizeButton::SnapType FrameMaximizeButton::SnapTypeForLocation( | 397 SnapType FrameMaximizeButton::SnapTypeForLocation( |
340 const gfx::Point& location) const { | 398 const gfx::Point& location) const { |
341 int delta_x = location.x() - press_location_.x(); | 399 int delta_x = location.x() - press_location_.x(); |
342 int delta_y = location.y() - press_location_.y(); | 400 int delta_y = location.y() - press_location_.y(); |
343 if (!views::View::ExceededDragThreshold(delta_x, delta_y)) | 401 if (!views::View::ExceededDragThreshold(delta_x, delta_y)) |
344 return !frame_->GetWidget()->IsMaximized() ? SNAP_MAXIMIZE : SNAP_RESTORE; | 402 return !frame_->GetWidget()->IsMaximized() ? SNAP_MAXIMIZE : SNAP_RESTORE; |
345 else if (delta_x < 0 && delta_y > delta_x && delta_y < -delta_x) | 403 else if (delta_x < 0 && delta_y > delta_x && delta_y < -delta_x) |
346 return SNAP_LEFT; | 404 return SNAP_LEFT; |
347 else if (delta_x > 0 && delta_y > -delta_x && delta_y < delta_x) | 405 else if (delta_x > 0 && delta_y > -delta_x && delta_y < delta_x) |
348 return SNAP_RIGHT; | 406 return SNAP_RIGHT; |
349 else if (delta_y > 0) | 407 else if (delta_y > 0) |
350 return SNAP_MINIMIZE; | 408 return SNAP_MINIMIZE; |
351 return !frame_->GetWidget()->IsMaximized() ? SNAP_MAXIMIZE : SNAP_RESTORE; | 409 return !frame_->GetWidget()->IsMaximized() ? SNAP_MAXIMIZE : SNAP_RESTORE; |
352 } | 410 } |
353 | 411 |
354 gfx::Rect FrameMaximizeButton::ScreenBoundsForType(SnapType type) const { | 412 gfx::Rect FrameMaximizeButton::ScreenBoundsForType( |
| 413 SnapType type) const { |
355 aura::Window* window = frame_->GetWidget()->GetNativeWindow(); | 414 aura::Window* window = frame_->GetWidget()->GetNativeWindow(); |
356 switch (type) { | 415 switch (type) { |
357 case SNAP_LEFT: | 416 case SNAP_LEFT: |
358 case SNAP_RIGHT: | 417 case SNAP_RIGHT: |
359 return ScreenAsh::ConvertRectToScreen( | 418 return ScreenAsh::ConvertRectToScreen( |
360 frame_->GetWidget()->GetNativeView()->parent(), | 419 frame_->GetWidget()->GetNativeView()->parent(), |
361 snap_sizer_->target_bounds()); | 420 snap_sizer_->target_bounds()); |
362 case SNAP_MAXIMIZE: | 421 case SNAP_MAXIMIZE: |
363 return ScreenAsh::ConvertRectToScreen( | 422 return ScreenAsh::ConvertRectToScreen( |
364 window->parent(), | 423 window->parent(), |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 break; | 471 break; |
413 case SNAP_RESTORE: | 472 case SNAP_RESTORE: |
414 frame_->GetWidget()->Restore(); | 473 frame_->GetWidget()->Restore(); |
415 break; | 474 break; |
416 case SNAP_NONE: | 475 case SNAP_NONE: |
417 NOTREACHED(); | 476 NOTREACHED(); |
418 } | 477 } |
419 } | 478 } |
420 | 479 |
421 } // namespace ash | 480 } // namespace ash |
OLD | NEW |