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.
5 #include "ash/system/tray/tray_background_view.h"
7 #include "ash/root_window_controller.h"
8 #include "ash/screen_ash.h"
9 #include "ash/shelf/shelf_layout_manager.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "ash/system/status_area_widget.h"
13 #include "ash/system/status_area_widget_delegate.h"
14 #include "ash/system/tray/tray_constants.h"
15 #include "ash/system/tray/tray_event_filter.h"
16 #include "ash/wm/property_util.h"
17 #include "ash/wm/window_animations.h"
18 #include "ui/aura/root_window.h"
19 #include "ui/aura/window.h"
20 #include "ui/base/accessibility/accessible_view_state.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/rect.h"
23 #include "ui/gfx/screen.h"
24 #include "ui/gfx/skia_util.h"
25 #include "ui/views/background.h"
26 #include "ui/views/layout/box_layout.h"
30 const SkColor kTrayBackgroundAlpha
= 100;
31 const SkColor kTrayBackgroundHoverAlpha
= 150;
33 // Adjust the size of TrayContainer with additional padding.
34 const int kTrayContainerVerticalPaddingBottomAlignment
= 1;
35 const int kTrayContainerHorizontalPaddingBottomAlignment
= 1;
36 const int kTrayContainerVerticalPaddingVerticalAlignment
= 1;
37 const int kTrayContainerHorizontalPaddingVerticalAlignment
= 1;
39 const int kAnimationDurationForPopupMS
= 200;
43 using views::TrayBubbleView
;
48 // Used to track when the anchor widget changes position on screen so that the
49 // bubble position can be updated.
50 class TrayBackgroundView::TrayWidgetObserver
: public views::WidgetObserver
{
52 explicit TrayWidgetObserver(TrayBackgroundView
* host
)
56 virtual void OnWidgetBoundsChanged(views::Widget
* widget
,
57 const gfx::Rect
& new_bounds
) OVERRIDE
{
58 host_
->AnchorUpdated();
61 virtual void OnWidgetVisibilityChanged(views::Widget
* widget
,
62 bool visible
) OVERRIDE
{
63 host_
->AnchorUpdated();
67 TrayBackgroundView
* host_
;
69 DISALLOW_COPY_AND_ASSIGN(TrayWidgetObserver
);
72 class TrayBackground
: public views::Background
{
74 TrayBackground() : alpha_(kTrayBackgroundAlpha
) {}
75 virtual ~TrayBackground() {}
77 void set_alpha(int alpha
) { alpha_
= alpha
; }
80 // Overridden from views::Background.
81 virtual void Paint(gfx::Canvas
* canvas
, views::View
* view
) const OVERRIDE
{
83 paint
.setAntiAlias(true);
84 paint
.setStyle(SkPaint::kFill_Style
);
85 paint
.setColor(SkColorSetARGB(alpha_
, 0, 0, 0));
87 gfx::Rect
bounds(view
->GetLocalBounds());
88 SkScalar radius
= SkIntToScalar(kTrayRoundedBorderRadius
);
89 path
.addRoundRect(gfx::RectToSkRect(bounds
), radius
, radius
);
90 canvas
->DrawPath(path
, paint
);
95 DISALLOW_COPY_AND_ASSIGN(TrayBackground
);
98 TrayBackgroundView::TrayContainer::TrayContainer(ShelfAlignment alignment
)
99 : alignment_(alignment
) {
103 void TrayBackgroundView::TrayContainer::SetAlignment(ShelfAlignment alignment
) {
104 if (alignment_
== alignment
)
106 alignment_
= alignment
;
110 gfx::Size
TrayBackgroundView::TrayContainer::GetPreferredSize() {
112 return views::View::GetPreferredSize();
116 void TrayBackgroundView::TrayContainer::ChildPreferredSizeChanged(
117 views::View
* child
) {
118 PreferredSizeChanged();
121 void TrayBackgroundView::TrayContainer::ChildVisibilityChanged(View
* child
) {
122 PreferredSizeChanged();
125 void TrayBackgroundView::TrayContainer::ViewHierarchyChanged(bool is_add
,
129 PreferredSizeChanged();
132 void TrayBackgroundView::TrayContainer::UpdateLayout() {
133 // Adjust the size of status tray dark background by adding additional
135 if (alignment_
== SHELF_ALIGNMENT_BOTTOM
||
136 alignment_
== SHELF_ALIGNMENT_TOP
) {
137 set_border(views::Border::CreateEmptyBorder(
138 kTrayContainerVerticalPaddingBottomAlignment
,
139 kTrayContainerHorizontalPaddingBottomAlignment
,
140 kTrayContainerVerticalPaddingBottomAlignment
,
141 kTrayContainerHorizontalPaddingBottomAlignment
));
142 views::BoxLayout
* layout
=
143 new views::BoxLayout(views::BoxLayout::kHorizontal
, 0, 0, 0);
144 layout
->set_spread_blank_space(true);
145 views::View::SetLayoutManager(layout
);
147 set_border(views::Border::CreateEmptyBorder(
148 kTrayContainerVerticalPaddingVerticalAlignment
,
149 kTrayContainerHorizontalPaddingVerticalAlignment
,
150 kTrayContainerVerticalPaddingVerticalAlignment
,
151 kTrayContainerHorizontalPaddingVerticalAlignment
));
152 views::BoxLayout
* layout
=
153 new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0);
154 layout
->set_spread_blank_space(true);
155 views::View::SetLayoutManager(layout
);
157 PreferredSizeChanged();
160 ////////////////////////////////////////////////////////////////////////////////
161 // TrayBackgroundView
163 TrayBackgroundView::TrayBackgroundView(
164 internal::StatusAreaWidget
* status_area_widget
)
165 : status_area_widget_(status_area_widget
),
166 tray_container_(NULL
),
167 shelf_alignment_(SHELF_ALIGNMENT_BOTTOM
),
169 ALLOW_THIS_IN_INITIALIZER_LIST(hide_background_animator_(
170 this, 0, kTrayBackgroundAlpha
)),
171 ALLOW_THIS_IN_INITIALIZER_LIST(hover_background_animator_(
172 this, 0, kTrayBackgroundHoverAlpha
- kTrayBackgroundAlpha
)),
173 ALLOW_THIS_IN_INITIALIZER_LIST(widget_observer_(
174 new TrayWidgetObserver(this))) {
175 set_notify_enter_exit_on_child(true);
177 // Initially we want to paint the background, but without the hover effect.
178 SetPaintsBackground(true, internal::BackgroundAnimator::CHANGE_IMMEDIATE
);
179 hover_background_animator_
.SetPaintsBackground(false,
180 internal::BackgroundAnimator::CHANGE_IMMEDIATE
);
182 tray_container_
= new TrayContainer(shelf_alignment_
);
183 SetContents(tray_container_
);
184 tray_event_filter_
.reset(new TrayEventFilter
);
187 TrayBackgroundView::~TrayBackgroundView() {
189 GetWidget()->RemoveObserver(widget_observer_
.get());
192 void TrayBackgroundView::Initialize() {
193 GetWidget()->AddObserver(widget_observer_
.get());
197 void TrayBackgroundView::OnMouseEntered(const ui::MouseEvent
& event
) {
198 hover_background_animator_
.SetPaintsBackground(true,
199 internal::BackgroundAnimator::CHANGE_ANIMATE
);
202 void TrayBackgroundView::OnMouseExited(const ui::MouseEvent
& event
) {
203 hover_background_animator_
.SetPaintsBackground(false,
204 internal::BackgroundAnimator::CHANGE_ANIMATE
);
207 void TrayBackgroundView::ChildPreferredSizeChanged(views::View
* child
) {
208 PreferredSizeChanged();
211 void TrayBackgroundView::OnPaintFocusBorder(gfx::Canvas
* canvas
) {
212 // The tray itself expands to the right and bottom edge of the screen to make
213 // sure clicking on the edges brings up the popup. However, the focus border
214 // should be only around the container.
215 if (HasFocus() && (focusable() || IsAccessibilityFocusable()))
216 DrawBorder(canvas
, GetContentsBounds());
219 void TrayBackgroundView::GetAccessibleState(ui::AccessibleViewState
* state
) {
220 state
->role
= ui::AccessibilityTypes::ROLE_PUSHBUTTON
;
221 state
->name
= GetAccessibleNameForTray();
224 void TrayBackgroundView::AboutToRequestFocusFromTabTraversal(bool reverse
) {
225 // Return focus to the login view. See crbug.com/120500.
226 views::View
* v
= GetNextFocusableView();
228 v
->AboutToRequestFocusFromTabTraversal(reverse
);
231 bool TrayBackgroundView::PerformAction(const ui::Event
& event
) {
235 void TrayBackgroundView::UpdateBackground(int alpha
) {
237 background_
->set_alpha(hide_background_animator_
.alpha() +
238 hover_background_animator_
.alpha());
243 void TrayBackgroundView::SetContents(views::View
* contents
) {
244 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
245 AddChildView(contents
);
248 void TrayBackgroundView::SetPaintsBackground(
250 internal::BackgroundAnimator::ChangeType change_type
) {
251 hide_background_animator_
.SetPaintsBackground(value
, change_type
);
254 void TrayBackgroundView::SetContentsBackground() {
255 background_
= new internal::TrayBackground
;
256 tray_container_
->set_background(background_
);
259 ShelfLayoutManager
* TrayBackgroundView::GetShelfLayoutManager() {
260 return ShelfLayoutManager::ForLauncher(GetWidget()->GetNativeView());
263 void TrayBackgroundView::SetShelfAlignment(ShelfAlignment alignment
) {
264 shelf_alignment_
= alignment
;
266 tray_container_
->SetAlignment(alignment
);
269 void TrayBackgroundView::SetBorder() {
270 views::View
* parent
= status_area_widget_
->status_area_widget_delegate();
271 // Tray views are laid out right-to-left or bottom-to-top
272 int on_edge
= (this == parent
->child_at(0));
273 // Change the border padding for different shelf alignment.
274 if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM
) {
275 set_border(views::Border::CreateEmptyBorder(
277 on_edge
? kPaddingFromBottomOfScreenBottomAlignment
:
278 kPaddingFromBottomOfScreenBottomAlignment
- 1,
279 on_edge
? kPaddingFromRightEdgeOfScreenBottomAlignment
: 0));
280 } else if (shelf_alignment() == SHELF_ALIGNMENT_TOP
) {
281 set_border(views::Border::CreateEmptyBorder(
282 on_edge
? kPaddingFromBottomOfScreenBottomAlignment
:
283 kPaddingFromBottomOfScreenBottomAlignment
- 1,
285 on_edge
? kPaddingFromRightEdgeOfScreenBottomAlignment
: 0));
286 } else if (shelf_alignment() == SHELF_ALIGNMENT_LEFT
) {
287 set_border(views::Border::CreateEmptyBorder(
288 0, kPaddingFromOuterEdgeOfLauncherVerticalAlignment
,
289 on_edge
? kPaddingFromBottomOfScreenVerticalAlignment
: 0,
290 kPaddingFromInnerEdgeOfLauncherVerticalAlignment
));
292 set_border(views::Border::CreateEmptyBorder(
293 0, kPaddingFromInnerEdgeOfLauncherVerticalAlignment
,
294 on_edge
? kPaddingFromBottomOfScreenVerticalAlignment
: 0,
295 kPaddingFromOuterEdgeOfLauncherVerticalAlignment
));
299 void TrayBackgroundView::InitializeBubbleAnimations(
300 views::Widget
* bubble_widget
) {
301 views::corewm::SetWindowVisibilityAnimationType(
302 bubble_widget
->GetNativeWindow(),
303 views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
);
304 views::corewm::SetWindowVisibilityAnimationTransition(
305 bubble_widget
->GetNativeWindow(),
306 views::corewm::ANIMATE_HIDE
);
307 views::corewm::SetWindowVisibilityAnimationDuration(
308 bubble_widget
->GetNativeWindow(),
309 base::TimeDelta::FromMilliseconds(kAnimationDurationForPopupMS
));
312 aura::Window
* TrayBackgroundView::GetBubbleWindowContainer() const {
313 return ash::Shell::GetContainer(
314 tray_container()->GetWidget()->GetNativeWindow()->GetRootWindow(),
315 ash::internal::kShellWindowId_SettingBubbleContainer
);
318 gfx::Rect
TrayBackgroundView::GetBubbleAnchorRect(
319 views::Widget
* anchor_widget
,
320 TrayBubbleView::AnchorType anchor_type
,
321 TrayBubbleView::AnchorAlignment anchor_alignment
) const {
323 if (anchor_widget
&& anchor_widget
->IsVisible()) {
324 rect
= anchor_widget
->GetWindowBoundsInScreen();
325 if (anchor_type
== TrayBubbleView::ANCHOR_TYPE_TRAY
) {
326 if (anchor_alignment
== TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM
) {
327 bool rtl
= base::i18n::IsRTL();
329 rtl
? kPaddingFromRightEdgeOfScreenBottomAlignment
: 0,
330 kTrayBubbleAnchorTopInsetBottomAnchor
,
331 rtl
? 0 : kPaddingFromRightEdgeOfScreenBottomAlignment
,
332 kPaddingFromBottomOfScreenBottomAlignment
);
333 } else if (anchor_alignment
== TrayBubbleView::ANCHOR_ALIGNMENT_LEFT
) {
334 rect
.Inset(0, 0, kPaddingFromInnerEdgeOfLauncherVerticalAlignment
+ 5,
335 kPaddingFromBottomOfScreenVerticalAlignment
);
337 rect
.Inset(kPaddingFromInnerEdgeOfLauncherVerticalAlignment
+ 1,
338 0, 0, kPaddingFromBottomOfScreenVerticalAlignment
);
340 } else if (anchor_type
== TrayBubbleView::ANCHOR_TYPE_BUBBLE
) {
341 // Invert the offsets to align with the bubble below.
342 if (anchor_alignment
== TrayBubbleView::ANCHOR_ALIGNMENT_LEFT
) {
343 rect
.Inset(kPaddingFromInnerEdgeOfLauncherVerticalAlignment
,
344 0, 0, kPaddingFromBottomOfScreenVerticalAlignment
);
345 } else if (anchor_alignment
== TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT
) {
346 rect
.Inset(0, 0, kPaddingFromInnerEdgeOfLauncherVerticalAlignment
,
347 kPaddingFromBottomOfScreenVerticalAlignment
);
352 // TODO(jennyz): May need to add left/right alignment in the following code.
353 if (rect
.IsEmpty()) {
354 aura::RootWindow
* target_root
= anchor_widget
?
355 anchor_widget
->GetNativeView()->GetRootWindow() :
356 Shell::GetPrimaryRootWindow();
357 rect
= target_root
->bounds();
359 base::i18n::IsRTL() ? kPaddingFromRightEdgeOfScreenBottomAlignment
:
360 rect
.width() - kPaddingFromRightEdgeOfScreenBottomAlignment
,
361 rect
.height() - kPaddingFromBottomOfScreenBottomAlignment
,
363 rect
= ScreenAsh::ConvertRectToScreen(target_root
, rect
);
368 TrayBubbleView::AnchorAlignment
TrayBackgroundView::GetAnchorAlignment() const {
369 switch (shelf_alignment_
) {
370 case SHELF_ALIGNMENT_BOTTOM
:
371 return TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM
;
372 case SHELF_ALIGNMENT_LEFT
:
373 return TrayBubbleView::ANCHOR_ALIGNMENT_LEFT
;
374 case SHELF_ALIGNMENT_RIGHT
:
375 return TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT
;
376 case SHELF_ALIGNMENT_TOP
:
377 return TrayBubbleView::ANCHOR_ALIGNMENT_TOP
;
380 return TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM
;
383 void TrayBackgroundView::UpdateBubbleViewArrow(
384 views::TrayBubbleView
* bubble_view
) {
385 aura::RootWindow
* root_window
=
386 bubble_view
->GetWidget()->GetNativeView()->GetRootWindow();
387 ash::internal::ShelfLayoutManager
* shelf
=
388 ShelfLayoutManager::ForLauncher(root_window
);
389 bubble_view
->SetArrowPaintType(
390 shelf
->IsVisible() ? views::BubbleBorder::PAINT_NORMAL
:
391 views::BubbleBorder::PAINT_TRANSPARENT
);
394 } // namespace internal