This source file includes following definitions.
- GetLayer
- IsDraggingTrayEnabled
- GetPreferredShelfSize
- in_mouse_drag
- in_mouse_drag_
- OnMouseEvent
- OnGestureEvent
- UpdateShelfObserver
- Detach
- OnImplicitAnimationsCompleted
- UpdateShelfObserver
- duration_override_in_ms_
- SetAutoHideBehavior
- PrepareForShutdown
- IsVisible
- SetAlignment
- GetAlignment
- GetIdealBounds
- LayoutShelf
- CalculateShelfVisibility
- UpdateVisibilityState
- UpdateAutoHideState
- SetWindowOverlapsShelf
- AddObserver
- RemoveObserver
- OnGestureEdgeSwipe
- StartGestureDrag
- UpdateGestureDrag
- CompleteGestureDrag
- CancelGestureDrag
- SetAnimationDurationOverride
- OnWindowResized
- OnWindowAddedToLayout
- OnWillRemoveWindowFromLayout
- OnWindowRemovedFromLayout
- OnChildWindowVisibilityChanged
- SetChildBounds
- OnLockStateChanged
- OnMaximizeModeStarted
- OnMaximizeModeEnded
- OnWindowActivated
- IsHorizontalAlignment
- ForShelf
- SetState
- UpdateBoundsAndOpacity
- StopAnimating
- GetShelfSize
- AdjustBoundsBasedOnAlignment
- CalculateTargetBounds
- UpdateTargetBoundsForGesture
- UpdateShelfBackground
- GetShelfBackgroundType
- UpdateAutoHideStateNow
- StopAutoHideTimer
- GetAutoHideShowShelfRegionInScreen
- CalculateAutoHideState
- IsShelfWindow
- GetWorkAreaSize
- GetAvailableBounds
- OnKeyboardBoundsChanging
- OnDockBoundsChanging
- OnLockStateEvent
#include "ash/shelf/shelf_layout_manager.h"
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include "ash/accelerators/accelerator_commands.h"
#include "ash/ash_switches.h"
#include "ash/root_window_controller.h"
#include "ash/screen_util.h"
#include "ash/session_state_delegate.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_bezel_event_filter.h"
#include "ash/shelf/shelf_constants.h"
#include "ash/shelf/shelf_layout_manager_observer.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/system/status_area_widget.h"
#include "ash/wm/gestures/shelf_gesture_handler.h"
#include "ash/wm/lock_state_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_animations.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace_controller.h"
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/command_line.h"
#include "base/i18n/rtl.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/ui_base_switches.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/screen.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/public/activation_client.h"
namespace ash {
namespace {
const int kAutoHideDelayMS = 200;
const int kNotificationBubbleGapHeight = 6;
const int kMaxAutoHideShowShelfRegionSize = 10;
ui::Layer* GetLayer(views::Widget* widget) {
return widget->GetNativeView()->layer();
}
bool IsDraggingTrayEnabled() {
static bool dragging_tray_allowed = CommandLine::ForCurrentProcess()->
HasSwitch(ash::switches::kAshEnableTrayDragging);
return dragging_tray_allowed;
}
}
const int ShelfLayoutManager::kWorkspaceAreaVisibleInset = 2;
const int ShelfLayoutManager::kWorkspaceAreaAutoHideInset = 5;
const int ShelfLayoutManager::kAutoHideSize = 3;
const int ShelfLayoutManager::kShelfSize = 47;
const int ShelfLayoutManager::kShelfItemInset = 3;
int ShelfLayoutManager::GetPreferredShelfSize() {
return ash::switches::UseAlternateShelfLayout() ?
ShelfLayoutManager::kShelfSize : kShelfPreferredSize;
}
class ShelfLayoutManager::AutoHideEventFilter : public ui::EventHandler {
public:
explicit AutoHideEventFilter(ShelfLayoutManager* shelf);
virtual ~AutoHideEventFilter();
bool in_mouse_drag() const { return in_mouse_drag_; }
virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
private:
ShelfLayoutManager* shelf_;
bool in_mouse_drag_;
ShelfGestureHandler gesture_handler_;
DISALLOW_COPY_AND_ASSIGN(AutoHideEventFilter);
};
ShelfLayoutManager::AutoHideEventFilter::AutoHideEventFilter(
ShelfLayoutManager* shelf)
: shelf_(shelf),
in_mouse_drag_(false) {
Shell::GetInstance()->AddPreTargetHandler(this);
}
ShelfLayoutManager::AutoHideEventFilter::~AutoHideEventFilter() {
Shell::GetInstance()->RemovePreTargetHandler(this);
}
void ShelfLayoutManager::AutoHideEventFilter::OnMouseEvent(
ui::MouseEvent* event) {
in_mouse_drag_ = (event->type() == ui::ET_MOUSE_DRAGGED ||
(in_mouse_drag_ && event->type() != ui::ET_MOUSE_RELEASED &&
event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)) &&
!shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()));
if (event->type() == ui::ET_MOUSE_MOVED)
shelf_->UpdateAutoHideState();
return;
}
void ShelfLayoutManager::AutoHideEventFilter::OnGestureEvent(
ui::GestureEvent* event) {
if (shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()))) {
if (gesture_handler_.ProcessGestureEvent(*event))
event->StopPropagation();
}
}
class ShelfLayoutManager::UpdateShelfObserver
: public ui::ImplicitAnimationObserver {
public:
explicit UpdateShelfObserver(ShelfLayoutManager* shelf) : shelf_(shelf) {
shelf_->update_shelf_observer_ = this;
}
void Detach() {
shelf_ = NULL;
}
virtual void OnImplicitAnimationsCompleted() OVERRIDE {
if (shelf_)
shelf_->UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
delete this;
}
private:
virtual ~UpdateShelfObserver() {
if (shelf_)
shelf_->update_shelf_observer_ = NULL;
}
ShelfLayoutManager* shelf_;
DISALLOW_COPY_AND_ASSIGN(UpdateShelfObserver);
};
ShelfLayoutManager::ShelfLayoutManager(ShelfWidget* shelf)
: root_window_(shelf->GetNativeView()->GetRootWindow()),
updating_bounds_(false),
force_shelf_always_visibile_(
Shell::GetInstance()->IsMaximizeModeWindowManagerEnabled()),
auto_hide_behavior_(SHELF_AUTO_HIDE_BEHAVIOR_NEVER),
alignment_(SHELF_ALIGNMENT_BOTTOM),
shelf_(shelf),
workspace_controller_(NULL),
window_overlaps_shelf_(false),
mouse_over_shelf_when_auto_hide_timer_started_(false),
bezel_event_filter_(new ShelfBezelEventFilter(this)),
gesture_drag_status_(GESTURE_DRAG_NONE),
gesture_drag_amount_(0.f),
gesture_drag_auto_hide_state_(SHELF_AUTO_HIDE_SHOWN),
update_shelf_observer_(NULL),
duration_override_in_ms_(0) {
Shell::GetInstance()->AddShellObserver(this);
Shell::GetInstance()->lock_state_controller()->AddObserver(this);
aura::client::GetActivationClient(root_window_)->AddObserver(this);
}
ShelfLayoutManager::~ShelfLayoutManager() {
if (update_shelf_observer_)
update_shelf_observer_->Detach();
FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, WillDeleteShelf());
Shell::GetInstance()->RemoveShellObserver(this);
Shell::GetInstance()->lock_state_controller()->RemoveObserver(this);
aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
}
void ShelfLayoutManager::SetAutoHideBehavior(ShelfAutoHideBehavior behavior) {
if (auto_hide_behavior_ == behavior)
return;
auto_hide_behavior_ = behavior;
UpdateVisibilityState();
FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
OnAutoHideBehaviorChanged(root_window_,
auto_hide_behavior_));
}
void ShelfLayoutManager::PrepareForShutdown() {
set_workspace_controller(NULL);
auto_hide_event_filter_.reset();
bezel_event_filter_.reset();
}
bool ShelfLayoutManager::IsVisible() const {
return shelf_->status_area_widget() &&
shelf_->status_area_widget()->IsVisible() &&
(state_.visibility_state == SHELF_VISIBLE ||
(state_.visibility_state == SHELF_AUTO_HIDE &&
state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN));
}
bool ShelfLayoutManager::SetAlignment(ShelfAlignment alignment) {
if (alignment_ == alignment)
return false;
DCHECK(!Shell::GetInstance()->session_state_delegate()->IsScreenLocked());
alignment_ = alignment;
shelf_->SetAlignment(alignment);
LayoutShelf();
return true;
}
ShelfAlignment ShelfLayoutManager::GetAlignment() const {
if (Shell::GetInstance()->session_state_delegate()->IsScreenLocked())
return SHELF_ALIGNMENT_BOTTOM;
return alignment_;
}
gfx::Rect ShelfLayoutManager::GetIdealBounds() {
gfx::Rect bounds(
ScreenUtil::GetDisplayBoundsInParent(shelf_->GetNativeView()));
int width = 0, height = 0;
GetShelfSize(&width, &height);
return SelectValueForShelfAlignment(
gfx::Rect(bounds.x(), bounds.bottom() - height, bounds.width(), height),
gfx::Rect(bounds.x(), bounds.y(), width, bounds.height()),
gfx::Rect(bounds.right() - width, bounds.y(), width, bounds.height()),
gfx::Rect(bounds.x(), bounds.y(), bounds.width(), height));
}
void ShelfLayoutManager::LayoutShelf() {
TargetBounds target_bounds;
CalculateTargetBounds(state_, &target_bounds);
UpdateBoundsAndOpacity(target_bounds, false, NULL);
if (shelf_->shelf()) {
shelf_->shelf()->SetShelfViewBounds(
target_bounds.shelf_bounds_in_shelf);
}
}
ShelfVisibilityState ShelfLayoutManager::CalculateShelfVisibility() {
switch(auto_hide_behavior_) {
case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
return SHELF_AUTO_HIDE;
case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
return SHELF_VISIBLE;
case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
return SHELF_HIDDEN;
}
return SHELF_VISIBLE;
}
void ShelfLayoutManager::UpdateVisibilityState() {
if (!workspace_controller_)
return;
if (Shell::GetInstance()->session_state_delegate()->IsScreenLocked() ||
force_shelf_always_visibile_) {
SetState(SHELF_VISIBLE);
} else {
WorkspaceWindowState window_state(workspace_controller_->GetWindowState());
switch (window_state) {
case WORKSPACE_WINDOW_STATE_FULL_SCREEN: {
const aura::Window* fullscreen_window = GetRootWindowController(
root_window_)->GetWindowForFullscreenMode();
if (fullscreen_window && wm::GetWindowState(fullscreen_window)->
hide_shelf_when_fullscreen()) {
SetState(SHELF_HIDDEN);
} else {
SetState(SHELF_AUTO_HIDE);
}
break;
}
case WORKSPACE_WINDOW_STATE_MAXIMIZED:
SetState(CalculateShelfVisibility());
break;
case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF:
case WORKSPACE_WINDOW_STATE_DEFAULT:
SetState(CalculateShelfVisibility());
SetWindowOverlapsShelf(window_state ==
WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF);
break;
}
}
}
void ShelfLayoutManager::UpdateAutoHideState() {
ShelfAutoHideState auto_hide_state =
CalculateAutoHideState(state_.visibility_state);
if (auto_hide_state != state_.auto_hide_state) {
if (auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
SetState(state_.visibility_state);
} else {
if (!auto_hide_timer_.IsRunning()) {
mouse_over_shelf_when_auto_hide_timer_started_ =
shelf_->GetWindowBoundsInScreen().Contains(
Shell::GetScreen()->GetCursorScreenPoint());
}
auto_hide_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kAutoHideDelayMS),
this, &ShelfLayoutManager::UpdateAutoHideStateNow);
}
} else {
StopAutoHideTimer();
}
}
void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) {
window_overlaps_shelf_ = value;
UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
}
void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) {
observers_.AddObserver(observer);
}
void ShelfLayoutManager::RemoveObserver(ShelfLayoutManagerObserver* observer) {
observers_.RemoveObserver(observer);
}
void ShelfLayoutManager::OnGestureEdgeSwipe(const ui::GestureEvent& gesture) {
if (force_shelf_always_visibile_)
return;
if (visibility_state() == SHELF_AUTO_HIDE) {
gesture_drag_auto_hide_state_ = SHELF_AUTO_HIDE_SHOWN;
gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS;
UpdateVisibilityState();
gesture_drag_status_ = GESTURE_DRAG_NONE;
}
}
void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) {
if (force_shelf_always_visibile_)
return;
gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS;
gesture_drag_amount_ = 0.f;
gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ?
auto_hide_state() : SHELF_AUTO_HIDE_SHOWN;
UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
}
ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag(
const ui::GestureEvent& gesture) {
if (force_shelf_always_visibile_)
return DRAG_SHELF;
bool horizontal = IsHorizontalAlignment();
gesture_drag_amount_ += horizontal ? gesture.details().scroll_y() :
gesture.details().scroll_x();
LayoutShelf();
if (horizontal && gesture.details().scroll_y() < 0) {
int min_height = 0;
if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN && shelf_)
min_height = shelf_->GetContentsView()->GetPreferredSize().height();
if (min_height < shelf_->GetWindowBoundsInScreen().height() &&
gesture.root_location().x() >=
shelf_->status_area_widget()->GetWindowBoundsInScreen().x() &&
IsDraggingTrayEnabled())
return DRAG_TRAY;
}
return DRAG_SHELF;
}
void ShelfLayoutManager::CompleteGestureDrag(const ui::GestureEvent& gesture) {
if (force_shelf_always_visibile_)
return;
bool horizontal = IsHorizontalAlignment();
bool should_change = false;
if (gesture.type() == ui::ET_GESTURE_SCROLL_END) {
const float kDragHideThreshold = 0.4f;
gfx::Rect bounds = GetIdealBounds();
float drag_ratio = fabs(gesture_drag_amount_) /
(horizontal ? bounds.height() : bounds.width());
if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
should_change = drag_ratio > kDragHideThreshold;
} else {
bool correct_direction = false;
switch (GetAlignment()) {
case SHELF_ALIGNMENT_BOTTOM:
case SHELF_ALIGNMENT_RIGHT:
correct_direction = gesture_drag_amount_ < 0;
break;
case SHELF_ALIGNMENT_LEFT:
case SHELF_ALIGNMENT_TOP:
correct_direction = gesture_drag_amount_ > 0;
break;
}
should_change = correct_direction && drag_ratio > kDragHideThreshold;
}
} else if (gesture.type() == ui::ET_SCROLL_FLING_START) {
if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
should_change = horizontal ? fabs(gesture.details().velocity_y()) > 0 :
fabs(gesture.details().velocity_x()) > 0;
} else {
should_change = SelectValueForShelfAlignment(
gesture.details().velocity_y() < 0,
gesture.details().velocity_x() > 0,
gesture.details().velocity_x() < 0,
gesture.details().velocity_y() > 0);
}
} else {
NOTREACHED();
}
if (!should_change) {
CancelGestureDrag();
return;
}
if (shelf_) {
shelf_->Deactivate();
shelf_->status_area_widget()->Deactivate();
}
gesture_drag_auto_hide_state_ =
gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
SHELF_AUTO_HIDE_HIDDEN : SHELF_AUTO_HIDE_SHOWN;
ShelfAutoHideBehavior new_auto_hide_behavior =
gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
SHELF_AUTO_HIDE_BEHAVIOR_NEVER : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS;
if (auto_hide_behavior_ != new_auto_hide_behavior)
SetAutoHideBehavior(new_auto_hide_behavior);
else
UpdateVisibilityState();
gesture_drag_status_ = GESTURE_DRAG_NONE;
}
void ShelfLayoutManager::CancelGestureDrag() {
gesture_drag_status_ = GESTURE_DRAG_CANCEL_IN_PROGRESS;
UpdateVisibilityState();
gesture_drag_status_ = GESTURE_DRAG_NONE;
}
void ShelfLayoutManager::SetAnimationDurationOverride(
int duration_override_in_ms) {
duration_override_in_ms_ = duration_override_in_ms;
}
void ShelfLayoutManager::OnWindowResized() {
LayoutShelf();
}
void ShelfLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
}
void ShelfLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
}
void ShelfLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
}
void ShelfLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
bool visible) {
}
void ShelfLayoutManager::SetChildBounds(aura::Window* child,
const gfx::Rect& requested_bounds) {
SetChildBoundsDirect(child, requested_bounds);
if (!updating_bounds_ &&
((shelf_->GetNativeView() == child) ||
(shelf_->status_area_widget()->GetNativeView() == child))) {
LayoutShelf();
}
}
void ShelfLayoutManager::OnLockStateChanged(bool locked) {
state_.is_screen_locked = locked;
shelf_->SetAlignment(locked ? SHELF_ALIGNMENT_BOTTOM : alignment_);
UpdateVisibilityState();
LayoutShelf();
}
void ShelfLayoutManager::OnMaximizeModeStarted() {
DCHECK(!force_shelf_always_visibile_);
force_shelf_always_visibile_ = true;
UpdateVisibilityState();
}
void ShelfLayoutManager::OnMaximizeModeEnded() {
DCHECK(force_shelf_always_visibile_);
force_shelf_always_visibile_ = false;
UpdateVisibilityState();
}
void ShelfLayoutManager::OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) {
UpdateAutoHideStateNow();
}
bool ShelfLayoutManager::IsHorizontalAlignment() const {
return GetAlignment() == SHELF_ALIGNMENT_BOTTOM ||
GetAlignment() == SHELF_ALIGNMENT_TOP;
}
ShelfLayoutManager* ShelfLayoutManager::ForShelf(aura::Window* window) {
ShelfWidget* shelf = RootWindowController::ForShelf(window)->shelf();
return shelf ? shelf->shelf_layout_manager() : NULL;
}
ShelfLayoutManager::TargetBounds::TargetBounds() : opacity(0.0f) {}
ShelfLayoutManager::TargetBounds::~TargetBounds() {}
void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
if (!shelf_->GetNativeView())
return;
State state;
state.visibility_state = visibility_state;
state.auto_hide_state = CalculateAutoHideState(visibility_state);
state.window_state = workspace_controller_ ?
workspace_controller_->GetWindowState() : WORKSPACE_WINDOW_STATE_DEFAULT;
bool force_update =
(gesture_drag_status_ == GESTURE_DRAG_CANCEL_IN_PROGRESS ||
gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS);
if (!force_update && state_.Equals(state))
return;
FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
WillChangeVisibilityState(visibility_state));
if (state.visibility_state == SHELF_AUTO_HIDE) {
if (!auto_hide_event_filter_)
auto_hide_event_filter_.reset(new AutoHideEventFilter(this));
} else {
auto_hide_event_filter_.reset(NULL);
}
StopAutoHideTimer();
State old_state = state_;
state_ = state;
BackgroundAnimatorChangeType change_type = BACKGROUND_CHANGE_ANIMATE;
bool delay_background_change = false;
if (state.visibility_state == SHELF_VISIBLE &&
state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED &&
old_state.visibility_state != SHELF_VISIBLE) {
change_type = BACKGROUND_CHANGE_IMMEDIATE;
} else {
if (state.visibility_state == SHELF_VISIBLE &&
old_state.visibility_state == SHELF_AUTO_HIDE &&
old_state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
delay_background_change = true;
}
}
if (delay_background_change) {
if (update_shelf_observer_)
update_shelf_observer_->Detach();
update_shelf_observer_ = new UpdateShelfObserver(this);
} else {
UpdateShelfBackground(change_type);
}
shelf_->SetDimsShelf(
state.visibility_state == SHELF_VISIBLE &&
state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED);
TargetBounds target_bounds;
CalculateTargetBounds(state_, &target_bounds);
UpdateBoundsAndOpacity(target_bounds, true,
delay_background_change ? update_shelf_observer_ : NULL);
if ((old_state.visibility_state != state_.visibility_state &&
state_.visibility_state == SHELF_AUTO_HIDE) ||
old_state.auto_hide_state != state_.auto_hide_state) {
FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
OnAutoHideStateChanged(state_.auto_hide_state));
}
}
void ShelfLayoutManager::UpdateBoundsAndOpacity(
const TargetBounds& target_bounds,
bool animate,
ui::ImplicitAnimationObserver* observer) {
base::AutoReset<bool> auto_reset_updating_bounds(&updating_bounds_, true);
ui::ScopedLayerAnimationSettings shelf_animation_setter(
GetLayer(shelf_)->GetAnimator());
ui::ScopedLayerAnimationSettings status_animation_setter(
GetLayer(shelf_->status_area_widget())->GetAnimator());
if (animate) {
int duration = duration_override_in_ms_ ? duration_override_in_ms_ :
kCrossFadeDurationMS;
shelf_animation_setter.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(duration));
shelf_animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
shelf_animation_setter.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
status_animation_setter.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(duration));
status_animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
status_animation_setter.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
} else {
StopAnimating();
shelf_animation_setter.SetTransitionDuration(base::TimeDelta());
status_animation_setter.SetTransitionDuration(base::TimeDelta());
}
if (observer)
status_animation_setter.AddObserver(observer);
GetLayer(shelf_)->SetOpacity(target_bounds.opacity);
shelf_->SetBounds(ScreenUtil::ConvertRectToScreen(
shelf_->GetNativeView()->parent(),
target_bounds.shelf_bounds_in_root));
GetLayer(shelf_->status_area_widget())->SetOpacity(
target_bounds.status_opacity);
gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
status_bounds.set_x(status_bounds.x() +
target_bounds.shelf_bounds_in_root.x());
status_bounds.set_y(status_bounds.y() +
target_bounds.shelf_bounds_in_root.y());
shelf_->status_area_widget()->SetBounds(
ScreenUtil::ConvertRectToScreen(
shelf_->status_area_widget()->GetNativeView()->parent(),
status_bounds));
SessionStateDelegate* session_state_delegate =
Shell::GetInstance()->session_state_delegate();
if (!state_.is_screen_locked &&
(session_state_delegate->IsActiveUserSessionStarted() ||
!keyboard_bounds_.IsEmpty())) {
Shell::GetInstance()->SetDisplayWorkAreaInsets(
root_window_, target_bounds.work_area_insets);
}
}
void ShelfLayoutManager::StopAnimating() {
GetLayer(shelf_)->GetAnimator()->StopAnimating();
GetLayer(shelf_->status_area_widget())->GetAnimator()->StopAnimating();
}
void ShelfLayoutManager::GetShelfSize(int* width, int* height) {
*width = *height = 0;
gfx::Size status_size(
shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
if (IsHorizontalAlignment())
*height = GetPreferredShelfSize();
else
*width = GetPreferredShelfSize();
}
void ShelfLayoutManager::AdjustBoundsBasedOnAlignment(int inset,
gfx::Rect* bounds) const {
bounds->Inset(SelectValueForShelfAlignment(
gfx::Insets(0, 0, inset, 0),
gfx::Insets(0, inset, 0, 0),
gfx::Insets(0, 0, 0, inset),
gfx::Insets(inset, 0, 0, 0)));
}
void ShelfLayoutManager::CalculateTargetBounds(
const State& state,
TargetBounds* target_bounds) {
const gfx::Rect available_bounds(GetAvailableBounds());
gfx::Rect status_size(
shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
int shelf_width = 0, shelf_height = 0;
GetShelfSize(&shelf_width, &shelf_height);
if (IsHorizontalAlignment())
shelf_width = available_bounds.width();
else
shelf_height = available_bounds.height();
if (state.visibility_state == SHELF_AUTO_HIDE &&
state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
if (IsHorizontalAlignment())
shelf_height = kAutoHideSize;
else
shelf_width = kAutoHideSize;
} else if (state.visibility_state == SHELF_HIDDEN ||
!keyboard_bounds_.IsEmpty()) {
if (IsHorizontalAlignment())
shelf_height = 0;
else
shelf_width = 0;
}
target_bounds->shelf_bounds_in_root = SelectValueForShelfAlignment(
gfx::Rect(available_bounds.x(), available_bounds.bottom() - shelf_height,
available_bounds.width(), shelf_height),
gfx::Rect(available_bounds.x(), available_bounds.y(),
shelf_width, available_bounds.height()),
gfx::Rect(available_bounds.right() - shelf_width, available_bounds.y(),
shelf_width, available_bounds.height()),
gfx::Rect(available_bounds.x(), available_bounds.y(),
available_bounds.width(), shelf_height));
int status_inset = std::max(0, GetPreferredShelfSize() -
PrimaryAxisValue(status_size.height(), status_size.width()));
if (ash::switches::UseAlternateShelfLayout()) {
status_inset = 0;
if (IsHorizontalAlignment())
status_size.set_height(kShelfSize);
else
status_size.set_width(kShelfSize);
}
target_bounds->status_bounds_in_shelf = SelectValueForShelfAlignment(
gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
status_inset, status_size.width(), status_size.height()),
gfx::Rect(shelf_width - (status_size.width() + status_inset),
shelf_height - status_size.height(), status_size.width(),
status_size.height()),
gfx::Rect(status_inset, shelf_height - status_size.height(),
status_size.width(), status_size.height()),
gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
shelf_height - (status_size.height() + status_inset),
status_size.width(), status_size.height()));
target_bounds->work_area_insets = SelectValueForShelfAlignment(
gfx::Insets(0, 0, GetWorkAreaSize(state, shelf_height), 0),
gfx::Insets(0, GetWorkAreaSize(state, shelf_width), 0, 0),
gfx::Insets(0, 0, 0, GetWorkAreaSize(state, shelf_width)),
gfx::Insets(GetWorkAreaSize(state, shelf_height), 0, 0, 0));
if (!keyboard_bounds_.IsEmpty()) {
gfx::Insets keyboard_insets(0, 0, keyboard_bounds_.height(), 0);
target_bounds->work_area_insets += keyboard_insets;
}
if (!dock_bounds_.IsEmpty()) {
gfx::Insets dock_insets(
0, (dock_bounds_.x() > 0 ? 0 : dock_bounds_.width()),
0, (dock_bounds_.x() > 0 ? dock_bounds_.width() : 0));
target_bounds->work_area_insets += dock_insets;
}
target_bounds->opacity =
(gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
state.visibility_state == SHELF_VISIBLE ||
state.visibility_state == SHELF_AUTO_HIDE) ? 1.0f : 0.0f;
target_bounds->status_opacity =
(state.visibility_state == SHELF_AUTO_HIDE &&
state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
gesture_drag_status_ != GESTURE_DRAG_IN_PROGRESS) ?
0.0f : target_bounds->opacity;
if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS)
UpdateTargetBoundsForGesture(target_bounds);
target_bounds->shelf_bounds_in_shelf = SelectValueForShelfAlignment(
gfx::Rect(0, 0,
shelf_width - status_size.width(),
target_bounds->shelf_bounds_in_root.height()),
gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
shelf_height - status_size.height()),
gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
shelf_height - status_size.height()),
gfx::Rect(0, 0,
shelf_width - status_size.width(),
target_bounds->shelf_bounds_in_root.height()));
}
void ShelfLayoutManager::UpdateTargetBoundsForGesture(
TargetBounds* target_bounds) const {
CHECK_EQ(GESTURE_DRAG_IN_PROGRESS, gesture_drag_status_);
bool horizontal = IsHorizontalAlignment();
const gfx::Rect& available_bounds(root_window_->bounds());
int resistance_free_region = 0;
if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
visibility_state() == SHELF_AUTO_HIDE &&
auto_hide_state() != SHELF_AUTO_HIDE_SHOWN) {
resistance_free_region = GetPreferredShelfSize() - kAutoHideSize;
}
bool resist = SelectValueForShelfAlignment(
gesture_drag_amount_ < -resistance_free_region,
gesture_drag_amount_ > resistance_free_region,
gesture_drag_amount_ < -resistance_free_region,
gesture_drag_amount_ > resistance_free_region);
float translate = 0.f;
if (resist) {
float diff = fabsf(gesture_drag_amount_) - resistance_free_region;
diff = std::min(diff, sqrtf(diff));
if (gesture_drag_amount_ < 0)
translate = -resistance_free_region - diff;
else
translate = resistance_free_region + diff;
} else {
translate = gesture_drag_amount_;
}
if (horizontal) {
int shelf_height = target_bounds->shelf_bounds_in_root.height() - translate;
shelf_height = std::max(shelf_height, kAutoHideSize);
target_bounds->shelf_bounds_in_root.set_height(shelf_height);
if (GetAlignment() == SHELF_ALIGNMENT_BOTTOM) {
target_bounds->shelf_bounds_in_root.set_y(
available_bounds.bottom() - shelf_height);
}
if (ash::switches::UseAlternateShelfLayout()) {
target_bounds->status_bounds_in_shelf.set_y(0);
} else {
gfx::Rect status_y = target_bounds->shelf_bounds_in_root;
status_y.set_y(0);
status_y.ClampToCenteredSize(
target_bounds->status_bounds_in_shelf.size());
target_bounds->status_bounds_in_shelf.set_y(status_y.y());
}
} else {
int shelf_width = target_bounds->shelf_bounds_in_root.width();
bool right_aligned = GetAlignment() == SHELF_ALIGNMENT_RIGHT;
if (right_aligned)
shelf_width -= translate;
else
shelf_width += translate;
shelf_width = std::max(shelf_width, kAutoHideSize);
target_bounds->shelf_bounds_in_root.set_width(shelf_width);
if (right_aligned) {
target_bounds->shelf_bounds_in_root.set_x(
available_bounds.right() - shelf_width);
}
if (ash::switches::UseAlternateShelfLayout()) {
if (right_aligned)
target_bounds->status_bounds_in_shelf.set_x(0);
else
target_bounds->status_bounds_in_shelf.set_x(
target_bounds->shelf_bounds_in_root.width() -
kShelfSize);
} else {
gfx::Rect status_x = target_bounds->shelf_bounds_in_root;
status_x.set_x(0);
status_x.ClampToCenteredSize(
target_bounds->status_bounds_in_shelf.size());
target_bounds->status_bounds_in_shelf.set_x(status_x.x());
}
}
}
void ShelfLayoutManager::UpdateShelfBackground(
BackgroundAnimatorChangeType type) {
const ShelfBackgroundType background_type(GetShelfBackgroundType());
shelf_->SetPaintsBackground(background_type, type);
FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
OnBackgroundUpdated(background_type, type));
}
ShelfBackgroundType ShelfLayoutManager::GetShelfBackgroundType() const {
if (state_.visibility_state != SHELF_AUTO_HIDE &&
state_.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED) {
return SHELF_BACKGROUND_MAXIMIZED;
}
if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
(!state_.is_screen_locked && window_overlaps_shelf_) ||
(state_.visibility_state == SHELF_AUTO_HIDE)) {
return SHELF_BACKGROUND_OVERLAP;
}
return SHELF_BACKGROUND_DEFAULT;
}
void ShelfLayoutManager::UpdateAutoHideStateNow() {
SetState(state_.visibility_state);
StopAutoHideTimer();
}
void ShelfLayoutManager::StopAutoHideTimer() {
auto_hide_timer_.Stop();
mouse_over_shelf_when_auto_hide_timer_started_ = false;
}
gfx::Rect ShelfLayoutManager::GetAutoHideShowShelfRegionInScreen() const {
gfx::Rect shelf_bounds_in_screen = shelf_->GetWindowBoundsInScreen();
gfx::Vector2d offset = SelectValueForShelfAlignment(
gfx::Vector2d(0, shelf_bounds_in_screen.height()),
gfx::Vector2d(-kMaxAutoHideShowShelfRegionSize, 0),
gfx::Vector2d(shelf_bounds_in_screen.width(), 0),
gfx::Vector2d(0, -kMaxAutoHideShowShelfRegionSize));
gfx::Rect show_shelf_region_in_screen = shelf_bounds_in_screen;
show_shelf_region_in_screen += offset;
if (IsHorizontalAlignment())
show_shelf_region_in_screen.set_height(kMaxAutoHideShowShelfRegionSize);
else
show_shelf_region_in_screen.set_width(kMaxAutoHideShowShelfRegionSize);
return show_shelf_region_in_screen;
}
ShelfAutoHideState ShelfLayoutManager::CalculateAutoHideState(
ShelfVisibilityState visibility_state) const {
if (force_shelf_always_visibile_)
return SHELF_AUTO_HIDE_SHOWN;
if (visibility_state != SHELF_AUTO_HIDE || !shelf_)
return SHELF_AUTO_HIDE_HIDDEN;
Shell* shell = Shell::GetInstance();
if (shell->GetAppListTargetVisibility())
return SHELF_AUTO_HIDE_SHOWN;
if (shelf_->status_area_widget() &&
shelf_->status_area_widget()->ShouldShowShelf())
return SHELF_AUTO_HIDE_SHOWN;
if (shelf_->shelf() && shelf_->shelf()->IsShowingMenu())
return SHELF_AUTO_HIDE_SHOWN;
if (shelf_->shelf() && shelf_->shelf()->IsShowingOverflowBubble())
return SHELF_AUTO_HIDE_SHOWN;
if (shelf_->IsActive() || shelf_->status_area_widget()->IsActive())
return SHELF_AUTO_HIDE_SHOWN;
const std::vector<aura::Window*> windows =
ash::MruWindowTracker::BuildWindowList(false);
bool visible_window = false;
for (size_t i = 0; i < windows.size(); ++i) {
if (windows[i] && windows[i]->IsVisible() &&
!wm::GetWindowState(windows[i])->IsMinimized() &&
root_window_ == windows[i]->GetRootWindow()) {
visible_window = true;
break;
}
}
if (!visible_window)
return SHELF_AUTO_HIDE_SHOWN;
if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS)
return gesture_drag_auto_hide_state_;
if (auto_hide_event_filter_.get() && auto_hide_event_filter_->in_mouse_drag())
return SHELF_AUTO_HIDE_HIDDEN;
aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(
shelf_->GetNativeWindow()->GetRootWindow());
if (!cursor_client->IsMouseEventsEnabled())
return SHELF_AUTO_HIDE_HIDDEN;
gfx::Rect shelf_region = shelf_->GetWindowBoundsInScreen();
if (shelf_->status_area_widget() &&
shelf_->status_area_widget()->IsMessageBubbleShown() &&
IsVisible()) {
ShelfAlignment alignment = GetAlignment();
shelf_region.Inset(alignment == SHELF_ALIGNMENT_RIGHT ?
-kNotificationBubbleGapHeight : 0,
alignment == SHELF_ALIGNMENT_BOTTOM ?
-kNotificationBubbleGapHeight : 0,
alignment == SHELF_ALIGNMENT_LEFT ?
-kNotificationBubbleGapHeight : 0,
alignment == SHELF_ALIGNMENT_TOP ?
-kNotificationBubbleGapHeight : 0);
}
gfx::Point cursor_position_in_screen =
Shell::GetScreen()->GetCursorScreenPoint();
if (shelf_region.Contains(cursor_position_in_screen))
return SHELF_AUTO_HIDE_SHOWN;
if ((state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN ||
mouse_over_shelf_when_auto_hide_timer_started_) &&
GetAutoHideShowShelfRegionInScreen().Contains(
cursor_position_in_screen)) {
return SHELF_AUTO_HIDE_SHOWN;
}
return SHELF_AUTO_HIDE_HIDDEN;
}
bool ShelfLayoutManager::IsShelfWindow(aura::Window* window) {
if (!window)
return false;
return (shelf_ && shelf_->GetNativeWindow()->Contains(window)) ||
(shelf_->status_area_widget() &&
shelf_->status_area_widget()->GetNativeWindow()->Contains(window));
}
int ShelfLayoutManager::GetWorkAreaSize(const State& state, int size) const {
if (state.visibility_state == SHELF_VISIBLE)
return size;
if (state.visibility_state == SHELF_AUTO_HIDE)
return kAutoHideSize;
return 0;
}
gfx::Rect ShelfLayoutManager::GetAvailableBounds() const {
gfx::Rect bounds(root_window_->bounds());
bounds.set_height(bounds.height() - keyboard_bounds_.height());
return bounds;
}
void ShelfLayoutManager::OnKeyboardBoundsChanging(const gfx::Rect& new_bounds) {
bool keyboard_is_about_to_hide = false;
if (new_bounds.IsEmpty() && !keyboard_bounds_.IsEmpty())
keyboard_is_about_to_hide = true;
keyboard_bounds_ = new_bounds;
OnWindowResized();
SessionStateDelegate* session_state_delegate =
Shell::GetInstance()->session_state_delegate();
if (!session_state_delegate->IsActiveUserSessionStarted() &&
keyboard_is_about_to_hide) {
Shell::GetInstance()->SetDisplayWorkAreaInsets(root_window_, gfx::Insets());
}
}
void ShelfLayoutManager::OnDockBoundsChanging(
const gfx::Rect& dock_bounds,
DockedWindowLayoutManagerObserver::Reason reason) {
if (reason == DISPLAY_INSETS_CHANGED)
return;
if (dock_bounds_ != dock_bounds) {
dock_bounds_ = dock_bounds;
OnWindowResized();
UpdateVisibilityState();
UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
}
}
void ShelfLayoutManager::OnLockStateEvent(LockStateObserver::EventType event) {
if (event == EVENT_LOCK_ANIMATION_STARTED) {
state_.is_screen_locked = true;
base::AutoReset<ShelfVisibilityState> state(&state_.visibility_state,
SHELF_HIDDEN);
TargetBounds target_bounds;
CalculateTargetBounds(state_, &target_bounds);
UpdateBoundsAndOpacity(target_bounds, true, NULL);
UpdateVisibilityState();
}
}
}