Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(263)

Side by Side Diff: ash/wm/shelf_layout_manager.cc

Issue 12313118: Refactor: Shelf Widget (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: safer shutdown (status_area_widget_) Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ash/wm/shelf_layout_manager.h ('k') | ash/wm/shelf_layout_manager_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "ash/wm/shelf_layout_manager.h"
6
7 #include <algorithm>
8 #include <cmath>
9
10 #include "ash/ash_switches.h"
11 #include "ash/launcher/launcher.h"
12 #include "ash/root_window_controller.h"
13 #include "ash/screen_ash.h"
14 #include "ash/shell.h"
15 #include "ash/shell_delegate.h"
16 #include "ash/shell_window_ids.h"
17 #include "ash/system/status_area_widget.h"
18 #include "ash/wm/property_util.h"
19 #include "ash/wm/window_cycle_controller.h"
20 #include "ash/wm/window_util.h"
21 #include "ash/wm/workspace_controller.h"
22 #include "ash/wm/workspace/workspace_animations.h"
23 #include "base/auto_reset.h"
24 #include "base/command_line.h"
25 #include "base/i18n/rtl.h"
26 #include "ui/aura/client/activation_client.h"
27 #include "ui/aura/root_window.h"
28 #include "ui/base/events/event.h"
29 #include "ui/base/events/event_handler.h"
30 #include "ui/compositor/layer.h"
31 #include "ui/compositor/layer_animation_observer.h"
32 #include "ui/compositor/layer_animator.h"
33 #include "ui/compositor/scoped_layer_animation_settings.h"
34 #include "ui/gfx/screen.h"
35 #include "ui/views/widget/widget.h"
36
37 namespace ash {
38 namespace internal {
39
40 namespace {
41
42 // Delay before showing the launcher. This is after the mouse stops moving.
43 const int kAutoHideDelayMS = 200;
44
45 // To avoid hiding the shelf when the mouse transitions from a message bubble
46 // into the shelf, the hit test area is enlarged by this amount of pixels to
47 // keep the shelf from hiding.
48 const int kNotificationBubbleGapHeight = 6;
49
50 ui::Layer* GetLayer(views::Widget* widget) {
51 return widget->GetNativeView()->layer();
52 }
53
54 bool IsDraggingTrayEnabled() {
55 static bool dragging_tray_allowed = CommandLine::ForCurrentProcess()->
56 HasSwitch(ash::switches::kAshEnableTrayDragging);
57 return dragging_tray_allowed;
58 }
59
60 } // namespace
61
62 // static
63 const int ShelfLayoutManager::kWorkspaceAreaBottomInset = 2;
64
65 // static
66 const int ShelfLayoutManager::kAutoHideSize = 3;
67
68 // ShelfLayoutManager::AutoHideEventFilter -------------------------------------
69
70 // Notifies ShelfLayoutManager any time the mouse moves.
71 class ShelfLayoutManager::AutoHideEventFilter : public ui::EventHandler {
72 public:
73 explicit AutoHideEventFilter(ShelfLayoutManager* shelf);
74 virtual ~AutoHideEventFilter();
75
76 // Returns true if the last mouse event was a mouse drag.
77 bool in_mouse_drag() const { return in_mouse_drag_; }
78
79 // Overridden from ui::EventHandler:
80 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
81
82 private:
83 ShelfLayoutManager* shelf_;
84 bool in_mouse_drag_;
85
86 DISALLOW_COPY_AND_ASSIGN(AutoHideEventFilter);
87 };
88
89 ShelfLayoutManager::AutoHideEventFilter::AutoHideEventFilter(
90 ShelfLayoutManager* shelf)
91 : shelf_(shelf),
92 in_mouse_drag_(false) {
93 Shell::GetInstance()->AddPreTargetHandler(this);
94 }
95
96 ShelfLayoutManager::AutoHideEventFilter::~AutoHideEventFilter() {
97 Shell::GetInstance()->RemovePreTargetHandler(this);
98 }
99
100 void ShelfLayoutManager::AutoHideEventFilter::OnMouseEvent(
101 ui::MouseEvent* event) {
102 // This also checks IsShelfWindow() to make sure we don't attempt to hide the
103 // shelf if the mouse down occurs on the shelf.
104 in_mouse_drag_ = (event->type() == ui::ET_MOUSE_DRAGGED ||
105 (in_mouse_drag_ && event->type() != ui::ET_MOUSE_RELEASED &&
106 event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)) &&
107 !shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()));
108 if (event->type() == ui::ET_MOUSE_MOVED)
109 shelf_->UpdateAutoHideState();
110 return;
111 }
112
113 // ShelfLayoutManager:UpdateShelfObserver --------------------------------------
114
115 // UpdateShelfObserver is used to delay updating the background until the
116 // animation completes.
117 class ShelfLayoutManager::UpdateShelfObserver
118 : public ui::ImplicitAnimationObserver {
119 public:
120 explicit UpdateShelfObserver(ShelfLayoutManager* shelf) : shelf_(shelf) {
121 shelf_->update_shelf_observer_ = this;
122 }
123
124 void Detach() {
125 shelf_ = NULL;
126 }
127
128 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
129 if (shelf_) {
130 shelf_->UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
131 }
132 delete this;
133 }
134
135 private:
136 virtual ~UpdateShelfObserver() {
137 if (shelf_)
138 shelf_->update_shelf_observer_ = NULL;
139 }
140
141 // Shelf we're in. NULL if deleted before we're deleted.
142 ShelfLayoutManager* shelf_;
143
144 DISALLOW_COPY_AND_ASSIGN(UpdateShelfObserver);
145 };
146
147 // ShelfLayoutManager ----------------------------------------------------------
148
149 ShelfLayoutManager::ShelfLayoutManager(StatusAreaWidget* status_area_widget)
150 : root_window_(status_area_widget->GetNativeView()->GetRootWindow()),
151 in_layout_(false),
152 auto_hide_behavior_(SHELF_AUTO_HIDE_BEHAVIOR_NEVER),
153 alignment_(SHELF_ALIGNMENT_BOTTOM),
154 launcher_(NULL),
155 status_area_widget_(status_area_widget),
156 workspace_controller_(NULL),
157 window_overlaps_shelf_(false),
158 gesture_drag_status_(GESTURE_DRAG_NONE),
159 gesture_drag_amount_(0.f),
160 gesture_drag_auto_hide_state_(SHELF_AUTO_HIDE_SHOWN),
161 update_shelf_observer_(NULL) {
162 Shell::GetInstance()->AddShellObserver(this);
163 aura::client::GetActivationClient(root_window_)->AddObserver(this);
164 }
165
166 ShelfLayoutManager::~ShelfLayoutManager() {
167 if (update_shelf_observer_)
168 update_shelf_observer_->Detach();
169
170 FOR_EACH_OBSERVER(Observer, observers_, WillDeleteShelf());
171 Shell::GetInstance()->RemoveShellObserver(this);
172 aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
173 }
174
175 void ShelfLayoutManager::SetAutoHideBehavior(ShelfAutoHideBehavior behavior) {
176 if (auto_hide_behavior_ == behavior)
177 return;
178 auto_hide_behavior_ = behavior;
179 UpdateVisibilityState();
180 FOR_EACH_OBSERVER(Observer, observers_,
181 OnAutoHideStateChanged(state_.auto_hide_state));
182 }
183
184 bool ShelfLayoutManager::IsVisible() const {
185 return status_area_widget_->IsVisible() &&
186 (state_.visibility_state == SHELF_VISIBLE ||
187 (state_.visibility_state == SHELF_AUTO_HIDE &&
188 state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN));
189 }
190
191 void ShelfLayoutManager::SetLauncher(Launcher* launcher) {
192 if (launcher == launcher_)
193 return;
194
195 launcher_ = launcher;
196 if (launcher_)
197 launcher_->SetAlignment(alignment_);
198 LayoutShelf();
199 }
200
201 bool ShelfLayoutManager::SetAlignment(ShelfAlignment alignment) {
202 if (alignment_ == alignment)
203 return false;
204
205 alignment_ = alignment;
206 if (launcher_)
207 launcher_->SetAlignment(alignment);
208 status_area_widget_->SetShelfAlignment(alignment);
209 LayoutShelf();
210 return true;
211 }
212
213 gfx::Rect ShelfLayoutManager::GetIdealBounds() {
214 // TODO(oshima): this is wrong. Figure out what display shelf is on
215 // and everything should be based on it.
216 gfx::Rect bounds(ScreenAsh::GetDisplayBoundsInParent(
217 status_area_widget_->GetNativeView()));
218 int width = 0, height = 0;
219 GetShelfSize(&width, &height);
220 return SelectValueForShelfAlignment(
221 gfx::Rect(bounds.x(), bounds.bottom() - height, bounds.width(), height),
222 gfx::Rect(bounds.x(), bounds.y(), width, bounds.height()),
223 gfx::Rect(bounds.right() - width, bounds.y(), width, bounds.height()),
224 gfx::Rect(bounds.x(), bounds.y(), bounds.width(), height));
225 }
226
227 void ShelfLayoutManager::LayoutShelf() {
228 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
229 StopAnimating();
230 TargetBounds target_bounds;
231 CalculateTargetBounds(state_, &target_bounds);
232 if (launcher_widget()) {
233 GetLayer(launcher_widget())->SetOpacity(target_bounds.opacity);
234 launcher_->SetWidgetBounds(
235 ScreenAsh::ConvertRectToScreen(
236 launcher_widget()->GetNativeView()->parent(),
237 target_bounds.launcher_bounds_in_root));
238 launcher_->SetStatusSize(target_bounds.status_bounds_in_root.size());
239 }
240 GetLayer(status_area_widget_)->SetOpacity(target_bounds.opacity);
241 status_area_widget_->SetBounds(
242 ScreenAsh::ConvertRectToScreen(
243 status_area_widget_->GetNativeView()->parent(),
244 target_bounds.status_bounds_in_root));
245 Shell::GetInstance()->SetDisplayWorkAreaInsets(
246 root_window_, target_bounds.work_area_insets);
247 UpdateHitTestBounds();
248 }
249
250 ShelfVisibilityState ShelfLayoutManager::CalculateShelfVisibility() {
251 switch(auto_hide_behavior_) {
252 case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
253 return SHELF_AUTO_HIDE;
254 case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
255 return SHELF_VISIBLE;
256 case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
257 return SHELF_HIDDEN;
258 }
259 return SHELF_VISIBLE;
260 }
261
262 ShelfVisibilityState
263 ShelfLayoutManager::CalculateShelfVisibilityWhileDragging() {
264 switch(auto_hide_behavior_) {
265 case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
266 case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
267 return SHELF_AUTO_HIDE;
268 case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
269 return SHELF_HIDDEN;
270 }
271 return SHELF_VISIBLE;
272 }
273
274 void ShelfLayoutManager::UpdateVisibilityState() {
275 ShellDelegate* delegate = Shell::GetInstance()->delegate();
276 if (delegate && delegate->IsScreenLocked()) {
277 SetState(SHELF_VISIBLE);
278 } else if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS) {
279 // TODO(zelidrag): Verify shelf drag animation still shows on the device
280 // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN.
281 SetState(CalculateShelfVisibilityWhileDragging());
282 } else if (GetRootWindowController(root_window_)->IsImmersiveMode()) {
283 // The user choosing immersive mode indicates he or she wants to maximize
284 // screen real-estate for content, so always auto-hide the shelf.
285 DCHECK_NE(auto_hide_behavior_, SHELF_AUTO_HIDE_ALWAYS_HIDDEN);
286 SetState(SHELF_AUTO_HIDE);
287 } else {
288 WorkspaceWindowState window_state(workspace_controller_->GetWindowState());
289 switch (window_state) {
290 case WORKSPACE_WINDOW_STATE_FULL_SCREEN:
291 SetState(SHELF_HIDDEN);
292 break;
293
294 case WORKSPACE_WINDOW_STATE_MAXIMIZED:
295 SetState(CalculateShelfVisibility());
296 break;
297
298 case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF:
299 case WORKSPACE_WINDOW_STATE_DEFAULT:
300 SetState(CalculateShelfVisibility());
301 SetWindowOverlapsShelf(window_state ==
302 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF);
303 break;
304 }
305 }
306 }
307
308 void ShelfLayoutManager::UpdateAutoHideState() {
309 ShelfAutoHideState auto_hide_state =
310 CalculateAutoHideState(state_.visibility_state);
311 if (auto_hide_state != state_.auto_hide_state) {
312 if (auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
313 // Hides happen immediately.
314 SetState(state_.visibility_state);
315 FOR_EACH_OBSERVER(Observer, observers_,
316 OnAutoHideStateChanged(auto_hide_state));
317 } else {
318 auto_hide_timer_.Stop();
319 auto_hide_timer_.Start(
320 FROM_HERE,
321 base::TimeDelta::FromMilliseconds(kAutoHideDelayMS),
322 this, &ShelfLayoutManager::UpdateAutoHideStateNow);
323 FOR_EACH_OBSERVER(Observer, observers_, OnAutoHideStateChanged(
324 CalculateAutoHideState(state_.visibility_state)));
325 }
326 } else {
327 auto_hide_timer_.Stop();
328 }
329 }
330
331 void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) {
332 window_overlaps_shelf_ = value;
333 UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
334 }
335
336 void ShelfLayoutManager::AddObserver(Observer* observer) {
337 observers_.AddObserver(observer);
338 }
339
340 void ShelfLayoutManager::RemoveObserver(Observer* observer) {
341 observers_.RemoveObserver(observer);
342 }
343
344 ////////////////////////////////////////////////////////////////////////////////
345 // ShelfLayoutManager, Gesture dragging:
346
347 void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) {
348 gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS;
349 gesture_drag_amount_ = 0.f;
350 gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ?
351 auto_hide_state() : SHELF_AUTO_HIDE_SHOWN;
352 UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
353 }
354
355 ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag(
356 const ui::GestureEvent& gesture) {
357 bool horizontal = IsHorizontalAlignment();
358 gesture_drag_amount_ += horizontal ? gesture.details().scroll_y() :
359 gesture.details().scroll_x();
360 LayoutShelf();
361
362 // Start reveling the status menu when:
363 // - dragging up on an already visible shelf
364 // - dragging up on a hidden shelf, but it is currently completely visible.
365 if (horizontal && gesture.details().scroll_y() < 0) {
366 int min_height = 0;
367 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
368 launcher_widget())
369 min_height = launcher_widget()->GetContentsView()->
370 GetPreferredSize().height();
371
372 if (min_height < launcher_widget()->GetWindowBoundsInScreen().height() &&
373 gesture.root_location().x() >=
374 status_area_widget_->GetWindowBoundsInScreen().x() &&
375 IsDraggingTrayEnabled())
376 return DRAG_TRAY;
377 }
378
379 return DRAG_SHELF;
380 }
381
382 void ShelfLayoutManager::CompleteGestureDrag(const ui::GestureEvent& gesture) {
383 bool horizontal = IsHorizontalAlignment();
384 bool should_change = false;
385 if (gesture.type() == ui::ET_GESTURE_SCROLL_END) {
386 // The visibility of the shelf changes only if the shelf was dragged X%
387 // along the correct axis. If the shelf was already visible, then the
388 // direction of the drag does not matter.
389 const float kDragHideThreshold = 0.4f;
390 gfx::Rect bounds = GetIdealBounds();
391 float drag_ratio = fabs(gesture_drag_amount_) /
392 (horizontal ? bounds.height() : bounds.width());
393 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
394 should_change = drag_ratio > kDragHideThreshold;
395 } else {
396 bool correct_direction = false;
397 switch (alignment_) {
398 case SHELF_ALIGNMENT_BOTTOM:
399 case SHELF_ALIGNMENT_RIGHT:
400 correct_direction = gesture_drag_amount_ < 0;
401 break;
402 case SHELF_ALIGNMENT_LEFT:
403 case SHELF_ALIGNMENT_TOP:
404 correct_direction = gesture_drag_amount_ > 0;
405 break;
406 }
407 should_change = correct_direction && drag_ratio > kDragHideThreshold;
408 }
409 } else if (gesture.type() == ui::ET_SCROLL_FLING_START) {
410 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
411 should_change = horizontal ? fabs(gesture.details().velocity_y()) > 0 :
412 fabs(gesture.details().velocity_x()) > 0;
413 } else {
414 should_change = SelectValueForShelfAlignment(
415 gesture.details().velocity_y() < 0,
416 gesture.details().velocity_x() > 0,
417 gesture.details().velocity_x() < 0,
418 gesture.details().velocity_y() > 0);
419 }
420 } else {
421 NOTREACHED();
422 }
423
424 if (!should_change) {
425 CancelGestureDrag();
426 return;
427 }
428
429 gesture_drag_auto_hide_state_ =
430 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
431 SHELF_AUTO_HIDE_HIDDEN : SHELF_AUTO_HIDE_SHOWN;
432 if (launcher_widget())
433 launcher_widget()->Deactivate();
434 status_area_widget_->Deactivate();
435 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
436 auto_hide_behavior_ != SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS) {
437 gesture_drag_status_ = GESTURE_DRAG_NONE;
438 SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
439 } else if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN &&
440 auto_hide_behavior_ != SHELF_AUTO_HIDE_BEHAVIOR_NEVER) {
441 gesture_drag_status_ = GESTURE_DRAG_NONE;
442 SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
443 } else {
444 gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS;
445 UpdateVisibilityState();
446 gesture_drag_status_ = GESTURE_DRAG_NONE;
447 }
448 }
449
450 void ShelfLayoutManager::CancelGestureDrag() {
451 gesture_drag_status_ = GESTURE_DRAG_NONE;
452 ui::ScopedLayerAnimationSettings
453 launcher_settings(GetLayer(launcher_widget())->GetAnimator()),
454 status_settings(GetLayer(status_area_widget_)->GetAnimator());
455 LayoutShelf();
456 UpdateVisibilityState();
457 UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
458 }
459
460 ////////////////////////////////////////////////////////////////////////////////
461 // ShelfLayoutManager, aura::LayoutManager implementation:
462
463 void ShelfLayoutManager::OnWindowResized() {
464 LayoutShelf();
465 }
466
467 void ShelfLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
468 }
469
470 void ShelfLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
471 }
472
473 void ShelfLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
474 }
475
476 void ShelfLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
477 bool visible) {
478 }
479
480 void ShelfLayoutManager::SetChildBounds(aura::Window* child,
481 const gfx::Rect& requested_bounds) {
482 SetChildBoundsDirect(child, requested_bounds);
483 // We may contain other widgets (such as frame maximize bubble) but they don't
484 // effect the layout in anyway.
485 if (!in_layout_ &&
486 ((launcher_widget() && launcher_widget()->GetNativeView() == child) ||
487 (status_area_widget_->GetNativeView() == child))) {
488 LayoutShelf();
489 }
490 }
491
492 void ShelfLayoutManager::OnLockStateChanged(bool locked) {
493 UpdateVisibilityState();
494 }
495
496 void ShelfLayoutManager::OnWindowActivated(aura::Window* gained_active,
497 aura::Window* lost_active) {
498 UpdateAutoHideStateNow();
499 }
500
501 bool ShelfLayoutManager::IsHorizontalAlignment() const {
502 return alignment_ == SHELF_ALIGNMENT_BOTTOM ||
503 alignment_ == SHELF_ALIGNMENT_TOP;
504 }
505
506 // static
507 ShelfLayoutManager* ShelfLayoutManager::ForLauncher(aura::Window* window) {
508 return RootWindowController::ForLauncher(window)->shelf();
509 }
510
511 ////////////////////////////////////////////////////////////////////////////////
512 // ShelfLayoutManager, private:
513
514 ShelfLayoutManager::TargetBounds::TargetBounds() : opacity(0.0f) {}
515
516 void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
517 ShellDelegate* delegate = Shell::GetInstance()->delegate();
518 State state;
519 state.visibility_state = visibility_state;
520 state.auto_hide_state = CalculateAutoHideState(visibility_state);
521 state.is_screen_locked = delegate && delegate->IsScreenLocked();
522
523 // It's possible for SetState() when a window becomes maximized but the state
524 // won't have changed value. Do the dimming check before the early exit.
525 if (launcher_ && workspace_controller_) {
526 launcher_->SetDimsShelf(
527 (state.visibility_state == SHELF_VISIBLE) &&
528 workspace_controller_->GetWindowState() ==
529 WORKSPACE_WINDOW_STATE_MAXIMIZED);
530 }
531
532 if (state_.Equals(state))
533 return; // Nothing changed.
534
535 FOR_EACH_OBSERVER(Observer, observers_,
536 WillChangeVisibilityState(visibility_state));
537
538 if (state.visibility_state == SHELF_AUTO_HIDE) {
539 // When state is SHELF_AUTO_HIDE we need to track when the mouse is over the
540 // launcher to unhide the shelf. AutoHideEventFilter does that for us.
541 if (!event_filter_.get())
542 event_filter_.reset(new AutoHideEventFilter(this));
543 } else {
544 event_filter_.reset(NULL);
545 }
546
547 auto_hide_timer_.Stop();
548
549 // Animating the background when transitioning from auto-hide & hidden to
550 // visible is janky. Update the background immediately in this case.
551 BackgroundAnimator::ChangeType change_type =
552 (state_.visibility_state == SHELF_AUTO_HIDE &&
553 state_.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
554 state.visibility_state == SHELF_VISIBLE) ?
555 BackgroundAnimator::CHANGE_IMMEDIATE : BackgroundAnimator::CHANGE_ANIMATE;
556 StopAnimating();
557
558 State old_state = state_;
559 state_ = state;
560 TargetBounds target_bounds;
561 CalculateTargetBounds(state_, &target_bounds);
562 if (launcher_widget()) {
563 ui::ScopedLayerAnimationSettings launcher_animation_setter(
564 GetLayer(launcher_widget())->GetAnimator());
565 launcher_animation_setter.SetTransitionDuration(
566 base::TimeDelta::FromMilliseconds(kWorkspaceSwitchTimeMS));
567 launcher_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
568 GetLayer(launcher_widget())->SetBounds(
569 target_bounds.launcher_bounds_in_root);
570 GetLayer(launcher_widget())->SetOpacity(target_bounds.opacity);
571 }
572 ui::ScopedLayerAnimationSettings status_animation_setter(
573 GetLayer(status_area_widget_)->GetAnimator());
574 status_animation_setter.SetTransitionDuration(
575 base::TimeDelta::FromMilliseconds(kWorkspaceSwitchTimeMS));
576 status_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
577
578 // Delay updating the background when going from SHELF_AUTO_HIDE_SHOWN to
579 // SHELF_AUTO_HIDE_HIDDEN until the shelf animates out. Otherwise during the
580 // animation you see the background change.
581 // Also delay the animation when the shelf was hidden, and has just been made
582 // visible (e.g. using a gesture-drag).
583 bool delay_shelf_update =
584 state.visibility_state == SHELF_AUTO_HIDE &&
585 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
586 old_state.visibility_state == SHELF_AUTO_HIDE;
587
588 if (state.visibility_state == SHELF_VISIBLE &&
589 old_state.visibility_state == SHELF_AUTO_HIDE &&
590 old_state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN)
591 delay_shelf_update = true;
592
593 if (delay_shelf_update) {
594 if (update_shelf_observer_)
595 update_shelf_observer_->Detach();
596 // UpdateShelfBackground deletes itself when the animation is done.
597 update_shelf_observer_ = new UpdateShelfObserver(this);
598 status_animation_setter.AddObserver(update_shelf_observer_);
599 }
600 ui::Layer* layer = GetLayer(status_area_widget_);
601 layer->SetBounds(target_bounds.status_bounds_in_root);
602 layer->SetOpacity(target_bounds.opacity);
603 Shell::GetInstance()->SetDisplayWorkAreaInsets(
604 root_window_, target_bounds.work_area_insets);
605 UpdateHitTestBounds();
606 if (!delay_shelf_update)
607 UpdateShelfBackground(change_type);
608 }
609
610 void ShelfLayoutManager::StopAnimating() {
611 ui::Layer* layer = GetLayer(status_area_widget_);
612 if (launcher_widget())
613 layer->GetAnimator()->StopAnimating();
614 layer->GetAnimator()->StopAnimating();
615 }
616
617 void ShelfLayoutManager::GetShelfSize(int* width, int* height) {
618 *width = *height = 0;
619 gfx::Size status_size(status_area_widget_->GetWindowBoundsInScreen().size());
620 gfx::Size launcher_size = launcher_ ?
621 launcher_widget()->GetContentsView()->GetPreferredSize() : gfx::Size();
622 if (IsHorizontalAlignment())
623 *height = std::max(launcher_size.height(), status_size.height());
624 else
625 *width = std::max(launcher_size.width(), status_size.width());
626 }
627
628 void ShelfLayoutManager::AdjustBoundsBasedOnAlignment(int inset,
629 gfx::Rect* bounds) const {
630 bounds->Inset(SelectValueForShelfAlignment(
631 gfx::Insets(0, 0, inset, 0),
632 gfx::Insets(0, inset, 0, 0),
633 gfx::Insets(0, 0, 0, inset),
634 gfx::Insets(inset, 0, 0, 0)));
635 }
636
637 void ShelfLayoutManager::CalculateTargetBounds(
638 const State& state,
639 TargetBounds* target_bounds) {
640 const gfx::Rect& available_bounds(root_window_->bounds());
641 gfx::Rect status_size(status_area_widget_->GetWindowBoundsInScreen().size());
642 gfx::Size launcher_size = launcher_ ?
643 launcher_widget()->GetContentsView()->GetPreferredSize() : gfx::Size();
644 int shelf_size = 0;
645 int shelf_width = 0, shelf_height = 0;
646 GetShelfSize(&shelf_width, &shelf_height);
647 if (state.visibility_state == SHELF_VISIBLE ||
648 (state.visibility_state == SHELF_AUTO_HIDE &&
649 state.auto_hide_state == SHELF_AUTO_HIDE_SHOWN)) {
650 shelf_size = std::max(shelf_width, shelf_height);
651 launcher_size.set_width(std::max(shelf_width,launcher_size.width()));
652 launcher_size.set_height(std::max(shelf_height,launcher_size.height()));
653 } else if (state.visibility_state == SHELF_AUTO_HIDE &&
654 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
655 shelf_size = kAutoHideSize;
656 // Keep the launcher to its full height when dragging is in progress.
657 if (gesture_drag_status_ == GESTURE_DRAG_NONE) {
658 if (IsHorizontalAlignment())
659 launcher_size.set_height(kAutoHideSize);
660 else
661 launcher_size.set_width(kAutoHideSize);
662 }
663 }
664 switch(alignment_) {
665 case SHELF_ALIGNMENT_BOTTOM:
666 // The status widget should extend to the bottom and right edges.
667 target_bounds->status_bounds_in_root = gfx::Rect(
668 base::i18n::IsRTL() ? available_bounds.x() :
669 available_bounds.right() - status_size.width(),
670 available_bounds.bottom() - shelf_size + shelf_height -
671 status_size.height(),
672 status_size.width(), status_size.height());
673 if (launcher_widget())
674 target_bounds->launcher_bounds_in_root = gfx::Rect(
675 available_bounds.x(),
676 available_bounds.bottom() - shelf_size,
677 available_bounds.width(),
678 launcher_size.height());
679 target_bounds->work_area_insets.Set(
680 0, 0, GetWorkAreaSize(state, shelf_height), 0);
681 break;
682 case SHELF_ALIGNMENT_LEFT:
683 target_bounds->status_bounds_in_root = gfx::Rect(
684 available_bounds.x() + launcher_size.width() - status_size.width(),
685 available_bounds.bottom() - status_size.height(),
686 shelf_width, status_size.height());
687 if (launcher_widget())
688 target_bounds->launcher_bounds_in_root = gfx::Rect(
689 available_bounds.x(), available_bounds.y(),
690 launcher_size.width(), available_bounds.height());
691 target_bounds->work_area_insets.Set(
692 0, GetWorkAreaSize(state, launcher_size.width()), 0, 0);
693 break;
694 case SHELF_ALIGNMENT_RIGHT:
695 target_bounds->status_bounds_in_root = gfx::Rect(
696 available_bounds.right()- status_size.width() -
697 shelf_size + shelf_width,
698 available_bounds.bottom() - status_size.height(),
699 shelf_width, status_size.height());
700 if (launcher_widget())
701 target_bounds->launcher_bounds_in_root = gfx::Rect(
702 available_bounds.right() - launcher_size.width(),
703 available_bounds.y(),
704 launcher_size.width(), available_bounds.height());
705 target_bounds->work_area_insets.Set(
706 0, 0, 0, GetWorkAreaSize(state, launcher_size.width()));
707 break;
708 case SHELF_ALIGNMENT_TOP:
709 target_bounds->status_bounds_in_root = gfx::Rect(
710 base::i18n::IsRTL() ? available_bounds.x() :
711 available_bounds.right() - status_size.width(),
712 available_bounds.y() + launcher_size.height() - status_size.height(),
713 status_size.width(), status_size.height());
714 if (launcher_widget())
715 target_bounds->launcher_bounds_in_root = gfx::Rect(
716 available_bounds.x(),
717 available_bounds.y(),
718 available_bounds.width(),
719 launcher_size.height());
720 target_bounds->work_area_insets.Set(
721 GetWorkAreaSize(state, shelf_height), 0, 0, 0);
722 break;
723 }
724 target_bounds->opacity =
725 (gesture_drag_status_ != GESTURE_DRAG_NONE ||
726 state.visibility_state == SHELF_VISIBLE ||
727 state.visibility_state == SHELF_AUTO_HIDE) ? 1.0f : 0.0f;
728 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS)
729 UpdateTargetBoundsForGesture(target_bounds);
730 }
731
732 void ShelfLayoutManager::UpdateTargetBoundsForGesture(
733 TargetBounds* target_bounds) const {
734 CHECK_EQ(GESTURE_DRAG_IN_PROGRESS, gesture_drag_status_);
735 bool horizontal = IsHorizontalAlignment();
736 int resistance_free_region = 0;
737
738 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
739 visibility_state() == SHELF_AUTO_HIDE &&
740 auto_hide_state() != SHELF_AUTO_HIDE_SHOWN) {
741 // If the shelf was hidden when the drag started (and the state hasn't
742 // changed since then, e.g. because the tray-menu was shown because of the
743 // drag), then allow the drag some resistance-free region at first to make
744 // sure the shelf sticks with the finger until the shelf is visible.
745 resistance_free_region += horizontal ?
746 target_bounds->launcher_bounds_in_root.height() :
747 target_bounds->launcher_bounds_in_root.width();
748 resistance_free_region -= kAutoHideSize;
749 }
750
751 bool resist = SelectValueForShelfAlignment(
752 gesture_drag_amount_ < -resistance_free_region,
753 gesture_drag_amount_ > resistance_free_region,
754 gesture_drag_amount_ < -resistance_free_region,
755 gesture_drag_amount_ > resistance_free_region);
756
757 float translate = 0.f;
758 if (resist) {
759 float diff = fabsf(gesture_drag_amount_) - resistance_free_region;
760 diff = std::min(diff, sqrtf(diff));
761 if (gesture_drag_amount_ < 0)
762 translate = -resistance_free_region - diff;
763 else
764 translate = resistance_free_region + diff;
765 } else {
766 translate = gesture_drag_amount_;
767 }
768
769 if (horizontal) {
770 // Move the launcher with the gesture.
771 target_bounds->launcher_bounds_in_root.Offset(0, translate);
772 target_bounds->status_bounds_in_root.Offset(0, translate);
773
774 if (translate < 0) {
775 // When dragging up, the launcher height should increase.
776 float move = std::max(translate,
777 -static_cast<float>(resistance_free_region));
778 target_bounds->launcher_bounds_in_root.set_height(
779 target_bounds->launcher_bounds_in_root.height() + move - translate);
780 }
781 } else {
782 // Move the launcher with the gesture.
783 if (alignment_ == SHELF_ALIGNMENT_RIGHT)
784 target_bounds->launcher_bounds_in_root.Offset(translate, 0);
785
786 if ((translate > 0 && alignment_ == SHELF_ALIGNMENT_RIGHT) ||
787 (translate < 0 && alignment_ == SHELF_ALIGNMENT_LEFT)) {
788 // When dragging towards the edge, the statusbar should move.
789 target_bounds->status_bounds_in_root.Offset(translate, 0);
790 } else {
791 // When dragging away from the edge, the launcher width should increase.
792 float move = alignment_ == SHELF_ALIGNMENT_RIGHT ?
793 std::max(translate, -static_cast<float>(resistance_free_region)) :
794 std::min(translate, static_cast<float>(resistance_free_region));
795
796 if (alignment_ == SHELF_ALIGNMENT_RIGHT) {
797 target_bounds->launcher_bounds_in_root.set_width(
798 target_bounds->launcher_bounds_in_root.width() + move - translate);
799 } else {
800 target_bounds->launcher_bounds_in_root.set_width(
801 target_bounds->launcher_bounds_in_root.width() - move + translate);
802 }
803
804 // The statusbar should be in the center.
805 gfx::Rect status_x = target_bounds->launcher_bounds_in_root;
806 status_x.ClampToCenteredSize(
807 target_bounds->status_bounds_in_root.size());
808 target_bounds->status_bounds_in_root.set_x(status_x.x());
809 }
810 }
811 }
812
813 void ShelfLayoutManager::UpdateShelfBackground(
814 BackgroundAnimator::ChangeType type) {
815 bool launcher_paints = GetLauncherPaintsBackground();
816 if (launcher_)
817 launcher_->SetPaintsBackground(launcher_paints, type);
818 // The status area normally draws a background, but we don't want it to draw a
819 // background when the launcher does or when we're at login/lock screen.
820 ShellDelegate* delegate = Shell::GetInstance()->delegate();
821 bool delegate_allows_tray_bg =
822 delegate->IsUserLoggedIn() && !delegate->IsScreenLocked();
823 bool status_area_paints = !launcher_paints && delegate_allows_tray_bg;
824 status_area_widget_->SetPaintsBackground(status_area_paints, type);
825 }
826
827 bool ShelfLayoutManager::GetLauncherPaintsBackground() const {
828 return gesture_drag_status_ != GESTURE_DRAG_NONE ||
829 (!state_.is_screen_locked && window_overlaps_shelf_) ||
830 (state_.visibility_state == SHELF_AUTO_HIDE) ;
831 }
832
833 void ShelfLayoutManager::UpdateAutoHideStateNow() {
834 SetState(state_.visibility_state);
835 }
836
837 ShelfAutoHideState ShelfLayoutManager::CalculateAutoHideState(
838 ShelfVisibilityState visibility_state) const {
839 if (visibility_state != SHELF_AUTO_HIDE || !launcher_widget())
840 return SHELF_AUTO_HIDE_HIDDEN;
841
842 if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS)
843 return gesture_drag_auto_hide_state_;
844
845 Shell* shell = Shell::GetInstance();
846 if (shell->GetAppListTargetVisibility())
847 return SHELF_AUTO_HIDE_SHOWN;
848
849 if (status_area_widget_ && status_area_widget_->ShouldShowLauncher())
850 return SHELF_AUTO_HIDE_SHOWN;
851
852 if (launcher_ && launcher_->IsShowingMenu())
853 return SHELF_AUTO_HIDE_SHOWN;
854
855 if (launcher_ && launcher_->IsShowingOverflowBubble())
856 return SHELF_AUTO_HIDE_SHOWN;
857
858 if (launcher_widget()->IsActive() || status_area_widget_->IsActive())
859 return SHELF_AUTO_HIDE_SHOWN;
860
861 // Don't show if the user is dragging the mouse.
862 if (event_filter_.get() && event_filter_->in_mouse_drag())
863 return SHELF_AUTO_HIDE_HIDDEN;
864
865 gfx::Rect shelf_region = launcher_widget()->GetWindowBoundsInScreen();
866 if (status_area_widget_ &&
867 status_area_widget_->IsMessageBubbleShown() &&
868 IsVisible()) {
869 // Increase the the hit test area to prevent the shelf from disappearing
870 // when the mouse is over the bubble gap.
871 shelf_region.Inset(alignment_ == SHELF_ALIGNMENT_RIGHT ?
872 -kNotificationBubbleGapHeight : 0,
873 alignment_ == SHELF_ALIGNMENT_BOTTOM ?
874 -kNotificationBubbleGapHeight : 0,
875 alignment_ == SHELF_ALIGNMENT_LEFT ?
876 -kNotificationBubbleGapHeight : 0,
877 alignment_ == SHELF_ALIGNMENT_TOP ?
878 -kNotificationBubbleGapHeight : 0);
879 }
880
881 if (shelf_region.Contains(Shell::GetScreen()->GetCursorScreenPoint()))
882 return SHELF_AUTO_HIDE_SHOWN;
883
884 const std::vector<aura::Window*> windows =
885 ash::WindowCycleController::BuildWindowList(NULL);
886
887 // Process the window list and check if there are any visible windows.
888 for (size_t i = 0; i < windows.size(); ++i) {
889 if (windows[i] && windows[i]->IsVisible() &&
890 !ash::wm::IsWindowMinimized(windows[i]))
891 return SHELF_AUTO_HIDE_HIDDEN;
892 }
893
894 // If there are no visible windows do not hide the shelf.
895 return SHELF_AUTO_HIDE_SHOWN;
896 }
897
898 void ShelfLayoutManager::UpdateHitTestBounds() {
899 gfx::Insets insets;
900 // Only modify the hit test when the shelf is visible, so we don't mess with
901 // hover hit testing in the auto-hide state.
902 if (state_.visibility_state == SHELF_VISIBLE) {
903 // Let clicks at the very top of the launcher through so windows can be
904 // resized with the bottom-right corner and bottom edge.
905 switch (alignment_) {
906 case SHELF_ALIGNMENT_BOTTOM:
907 insets.Set(kWorkspaceAreaBottomInset, 0, 0, 0);
908 break;
909 case SHELF_ALIGNMENT_LEFT:
910 insets.Set(0, 0, 0, kWorkspaceAreaBottomInset);
911 break;
912 case SHELF_ALIGNMENT_RIGHT:
913 insets.Set(0, kWorkspaceAreaBottomInset, 0, 0);
914 break;
915 case SHELF_ALIGNMENT_TOP:
916 insets.Set(0, 0, kWorkspaceAreaBottomInset, 0);
917 break;
918 }
919 }
920 if (launcher_widget() && launcher_widget()->GetNativeWindow()) {
921 launcher_widget()->GetNativeWindow()->SetHitTestBoundsOverrideOuter(
922 insets, 1);
923 }
924 status_area_widget_->GetNativeWindow()->
925 SetHitTestBoundsOverrideOuter(insets, 1);
926 }
927
928 bool ShelfLayoutManager::IsShelfWindow(aura::Window* window) {
929 if (!window)
930 return false;
931 return (launcher_widget() &&
932 launcher_widget()->GetNativeWindow()->Contains(window)) ||
933 (status_area_widget_->GetNativeWindow()->Contains(window));
934 }
935
936 int ShelfLayoutManager::GetWorkAreaSize(const State& state, int size) const {
937 if (state.visibility_state == SHELF_VISIBLE)
938 return size;
939 if (state.visibility_state == SHELF_AUTO_HIDE)
940 return kAutoHideSize;
941 return 0;
942 }
943
944 } // namespace internal
945 } // namespace ash
OLDNEW
« no previous file with comments | « ash/wm/shelf_layout_manager.h ('k') | ash/wm/shelf_layout_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698