blob: 147c70f55625b17b4bc8e827931fbf8657f14201 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/tray/tray_background_view.h"
#include "ash/launcher/background_animator.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/status_area_widget_delegate.h"
#include "ash/system/tray/tray_constants.h"
#include "ui/aura/window.h"
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/skia_util.h"
#include "ui/views/background.h"
#include "ui/views/layout/box_layout.h"
namespace {
const SkColor kTrayBackgroundAlpha = 100;
const SkColor kTrayBackgroundHoverAlpha = 150;
// Adjust the size of TrayContainer with additional padding.
const int kTrayContainerVerticalPaddingBottomAlignment = 1;
const int kTrayContainerHorizontalPaddingBottomAlignment = 1;
const int kTrayContainerVerticalPaddingVerticalAlignment = 1;
const int kTrayContainerHorizontalPaddingVerticalAlignment = 1;
} // namespace
namespace ash {
namespace internal {
// Observe the tray layer animation and update the anchor when it changes.
// TODO(stevenjb): Observe or mirror the actual animation, not just the start
// and end points.
class TrayLayerAnimationObserver : public ui::LayerAnimationObserver {
public:
explicit TrayLayerAnimationObserver(TrayBackgroundView* host)
: host_(host) {
}
virtual void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) {
host_->AnchorUpdated();
}
virtual void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) {
host_->AnchorUpdated();
}
virtual void OnLayerAnimationScheduled(ui::LayerAnimationSequence* sequence) {
host_->AnchorUpdated();
}
private:
TrayBackgroundView* host_;
DISALLOW_COPY_AND_ASSIGN(TrayLayerAnimationObserver);
};
class TrayBackground : public views::Background {
public:
TrayBackground() : alpha_(kTrayBackgroundAlpha) {}
virtual ~TrayBackground() {}
void set_alpha(int alpha) { alpha_ = alpha; }
private:
// Overridden from views::Background.
virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kFill_Style);
paint.setColor(SkColorSetARGB(alpha_, 0, 0, 0));
SkPath path;
gfx::Rect bounds(view->GetLocalBounds());
SkScalar radius = SkIntToScalar(kTrayRoundedBorderRadius);
path.addRoundRect(gfx::RectToSkRect(bounds), radius, radius);
canvas->DrawPath(path, paint);
}
int alpha_;
DISALLOW_COPY_AND_ASSIGN(TrayBackground);
};
TrayBackgroundView::TrayContainer::TrayContainer(ShelfAlignment alignment)
: alignment_(alignment) {
UpdateLayout();
}
void TrayBackgroundView::TrayContainer::SetAlignment(ShelfAlignment alignment) {
if (alignment_ == alignment)
return;
alignment_ = alignment;
UpdateLayout();
}
gfx::Size TrayBackgroundView::TrayContainer::GetPreferredSize() {
if (size_.IsEmpty())
return views::View::GetPreferredSize();
return size_;
}
void TrayBackgroundView::TrayContainer::ChildPreferredSizeChanged(
views::View* child) {
PreferredSizeChanged();
}
void TrayBackgroundView::TrayContainer::ChildVisibilityChanged(View* child) {
PreferredSizeChanged();
}
void TrayBackgroundView::TrayContainer::ViewHierarchyChanged(bool is_add,
View* parent,
View* child) {
if (parent == this)
PreferredSizeChanged();
}
void TrayBackgroundView::TrayContainer::UpdateLayout() {
// Adjust the size of status tray dark background by adding additional
// empty border.
if (alignment_ == SHELF_ALIGNMENT_BOTTOM) {
set_border(views::Border::CreateEmptyBorder(
kTrayContainerVerticalPaddingBottomAlignment,
kTrayContainerHorizontalPaddingBottomAlignment,
kTrayContainerVerticalPaddingBottomAlignment,
kTrayContainerHorizontalPaddingBottomAlignment));
views::BoxLayout* layout =
new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
layout->set_spread_blank_space(true);
views::View::SetLayoutManager(layout);
} else {
set_border(views::Border::CreateEmptyBorder(
kTrayContainerVerticalPaddingVerticalAlignment,
kTrayContainerHorizontalPaddingVerticalAlignment,
kTrayContainerVerticalPaddingVerticalAlignment,
kTrayContainerHorizontalPaddingVerticalAlignment));
views::BoxLayout* layout =
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
layout->set_spread_blank_space(true);
views::View::SetLayoutManager(layout);
}
PreferredSizeChanged();
}
////////////////////////////////////////////////////////////////////////////////
// TrayBackgroundView
TrayBackgroundView::TrayBackgroundView(
internal::StatusAreaWidget* status_area_widget)
: status_area_widget_(status_area_widget),
tray_container_(NULL),
shelf_alignment_(SHELF_ALIGNMENT_BOTTOM),
background_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(hide_background_animator_(
this, 0, kTrayBackgroundAlpha)),
ALLOW_THIS_IN_INITIALIZER_LIST(hover_background_animator_(
this, 0, kTrayBackgroundHoverAlpha - kTrayBackgroundAlpha)),
ALLOW_THIS_IN_INITIALIZER_LIST(layer_animation_observer_(
new TrayLayerAnimationObserver(this))) {
set_notify_enter_exit_on_child(true);
// Initially we want to paint the background, but without the hover effect.
SetPaintsBackground(true, internal::BackgroundAnimator::CHANGE_IMMEDIATE);
hover_background_animator_.SetPaintsBackground(false,
internal::BackgroundAnimator::CHANGE_IMMEDIATE);
tray_container_ = new TrayContainer(shelf_alignment_);
SetContents(tray_container_);
}
TrayBackgroundView::~TrayBackgroundView() {
if (GetWidget()) {
GetWidget()->GetNativeView()->layer()->GetAnimator()->RemoveObserver(
layer_animation_observer_.get());
}
}
void TrayBackgroundView::Initialize() {
GetWidget()->GetNativeView()->layer()->GetAnimator()->AddObserver(
layer_animation_observer_.get());
SetBorder();
}
void TrayBackgroundView::OnMouseEntered(const ui::MouseEvent& event) {
hover_background_animator_.SetPaintsBackground(true,
internal::BackgroundAnimator::CHANGE_ANIMATE);
}
void TrayBackgroundView::OnMouseExited(const ui::MouseEvent& event) {
hover_background_animator_.SetPaintsBackground(false,
internal::BackgroundAnimator::CHANGE_ANIMATE);
}
void TrayBackgroundView::ChildPreferredSizeChanged(views::View* child) {
PreferredSizeChanged();
}
void TrayBackgroundView::OnPaintFocusBorder(gfx::Canvas* canvas) {
// The tray itself expands to the right and bottom edge of the screen to make
// sure clicking on the edges brings up the popup. However, the focus border
// should be only around the container.
if (HasFocus() && (focusable() || IsAccessibilityFocusable()))
DrawBorder(canvas, GetContentsBounds());
}
void TrayBackgroundView::GetAccessibleState(ui::AccessibleViewState* state) {
state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
state->name = GetAccessibleName();
}
void TrayBackgroundView::AboutToRequestFocusFromTabTraversal(bool reverse) {
// Return focus to the login view. See crbug.com/120500.
views::View* v = GetNextFocusableView();
if (v)
v->AboutToRequestFocusFromTabTraversal(reverse);
}
bool TrayBackgroundView::PerformAction(const ui::Event& event) {
return false;
}
void TrayBackgroundView::UpdateBackground(int alpha) {
if (background_) {
background_->set_alpha(hide_background_animator_.alpha() +
hover_background_animator_.alpha());
}
SchedulePaint();
}
void TrayBackgroundView::SetContents(views::View* contents) {
SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
AddChildView(contents);
}
void TrayBackgroundView::SetContentsBackground() {
background_ = new internal::TrayBackground;
tray_container_->set_background(background_);
}
void TrayBackgroundView::SetPaintsBackground(
bool value,
internal::BackgroundAnimator::ChangeType change_type) {
hide_background_animator_.SetPaintsBackground(value, change_type);
}
void TrayBackgroundView::SetShelfAlignment(ShelfAlignment alignment) {
shelf_alignment_ = alignment;
SetBorder();
tray_container_->SetAlignment(alignment);
}
void TrayBackgroundView::SetBorder() {
views::View* parent = status_area_widget_->status_area_widget_delegate();
// Tray views are laid out right-to-left or bottom-to-top
int on_edge = (this == parent->child_at(0));
// Change the border padding for different shelf alignment.
if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM) {
set_border(views::Border::CreateEmptyBorder(
0, 0, kPaddingFromBottomOfScreenBottomAlignment,
on_edge ? kPaddingFromRightEdgeOfScreenBottomAlignment : 0));
} else if (shelf_alignment() == SHELF_ALIGNMENT_LEFT) {
set_border(views::Border::CreateEmptyBorder(
0, kPaddingFromOuterEdgeOfLauncherVerticalAlignment,
on_edge ? kPaddingFromBottomOfScreenVerticalAlignment : 0,
kPaddingFromInnerEdgeOfLauncherVerticalAlignment));
} else {
set_border(views::Border::CreateEmptyBorder(
0, kPaddingFromInnerEdgeOfLauncherVerticalAlignment,
on_edge ? kPaddingFromBottomOfScreenVerticalAlignment : 0,
kPaddingFromOuterEdgeOfLauncherVerticalAlignment));
}
}
} // namespace internal
} // namespace ash