shelf_widget.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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/shelf/shelf_widget.h"
6
7#include "ash/ash_switches.h"
8#include "ash/focus_cycler.h"
9#include "ash/root_window_controller.h"
10#include "ash/session/session_state_delegate.h"
11#include "ash/shelf/shelf_constants.h"
12#include "ash/shelf/shelf_delegate.h"
13#include "ash/shelf/shelf_layout_manager.h"
14#include "ash/shelf/shelf_model.h"
15#include "ash/shelf/shelf_navigator.h"
16#include "ash/shelf/shelf_view.h"
17#include "ash/shelf/shelf_widget.h"
18#include "ash/shell.h"
19#include "ash/shell_window_ids.h"
20#include "ash/system/tray/system_tray_delegate.h"
21#include "ash/wm/status_area_layout_manager.h"
22#include "ash/wm/window_properties.h"
23#include "ash/wm/workspace_controller.h"
24#include "grit/ash_resources.h"
25#include "ui/aura/window.h"
26#include "ui/aura/window_event_dispatcher.h"
27#include "ui/aura/window_observer.h"
28#include "ui/base/resource/resource_bundle.h"
29#include "ui/compositor/layer.h"
30#include "ui/compositor/scoped_layer_animation_settings.h"
31#include "ui/events/event_constants.h"
32#include "ui/gfx/canvas.h"
33#include "ui/gfx/image/image.h"
34#include "ui/gfx/image/image_skia_operations.h"
35#include "ui/gfx/skbitmap_operations.h"
36#include "ui/views/accessible_pane_view.h"
37#include "ui/views/widget/widget.h"
38#include "ui/views/widget/widget_delegate.h"
39#include "ui/wm/core/easy_resize_window_targeter.h"
40#include "ui/wm/public/activation_client.h"
41
42namespace {
43// Size of black border at bottom (or side) of shelf.
44const int kNumBlackPixels = 3;
45// Alpha to paint dimming image with.
46const int kDimAlpha = 128;
47
48// The time to dim and un-dim.
49const int kTimeToDimMs = 3000;  // Slow in dimming.
50const int kTimeToUnDimMs = 200;  // Fast in activating.
51
52// Class used to slightly dim shelf items when maximized and visible.
53class DimmerView : public views::View,
54                   public views::WidgetDelegate,
55                   ash::BackgroundAnimatorDelegate {
56 public:
57  // If |disable_dimming_animations_for_test| is set, all alpha animations will
58  // be performed instantly.
59  DimmerView(ash::ShelfWidget* shelf_widget,
60             bool disable_dimming_animations_for_test);
61  virtual ~DimmerView();
62
63  // Called by |DimmerEventFilter| when the mouse |hovered| state changes.
64  void SetHovered(bool hovered);
65
66  // Force the dimmer to be undimmed.
67  void ForceUndimming(bool force);
68
69  // views::WidgetDelegate overrides:
70  virtual views::Widget* GetWidget() OVERRIDE {
71    return View::GetWidget();
72  }
73  virtual const views::Widget* GetWidget() const OVERRIDE {
74    return View::GetWidget();
75  }
76
77  // ash::BackgroundAnimatorDelegate overrides:
78  virtual void UpdateBackground(int alpha) OVERRIDE {
79    alpha_ = alpha;
80    SchedulePaint();
81  }
82
83  // views::View overrides:
84  virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE;
85
86  // A function to test the current alpha used.
87  int get_dimming_alpha_for_test() { return alpha_; }
88
89 private:
90  // This class monitors mouse events to see if it is on top of the shelf.
91  class DimmerEventFilter : public ui::EventHandler {
92   public:
93    explicit DimmerEventFilter(DimmerView* owner);
94    virtual ~DimmerEventFilter();
95
96    // Overridden from ui::EventHandler:
97    virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
98    virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
99
100   private:
101    // The owning class.
102    DimmerView* owner_;
103
104    // TRUE if the mouse is inside the shelf.
105    bool mouse_inside_;
106
107    // TRUE if a touch event is inside the shelf.
108    bool touch_inside_;
109
110    DISALLOW_COPY_AND_ASSIGN(DimmerEventFilter);
111  };
112
113  // The owning shelf.
114  ash::ShelfWidget* shelf_;
115
116  // The alpha to use for covering the shelf.
117  int alpha_;
118
119  // True if the event filter claims that we should not be dimmed.
120  bool is_hovered_;
121
122  // True if someone forces us not to be dimmed (e.g. a menu is open).
123  bool force_hovered_;
124
125  // True if animations should be suppressed for a test.
126  bool disable_dimming_animations_for_test_;
127
128  // The animator for the background transitions.
129  ash::BackgroundAnimator background_animator_;
130
131  // Notification of entering / exiting of the shelf area by mouse.
132  scoped_ptr<DimmerEventFilter> event_filter_;
133
134  DISALLOW_COPY_AND_ASSIGN(DimmerView);
135};
136
137DimmerView::DimmerView(ash::ShelfWidget* shelf_widget,
138                       bool disable_dimming_animations_for_test)
139    : shelf_(shelf_widget),
140      alpha_(kDimAlpha),
141      is_hovered_(false),
142      force_hovered_(false),
143      disable_dimming_animations_for_test_(disable_dimming_animations_for_test),
144      background_animator_(this, 0, kDimAlpha) {
145  event_filter_.reset(new DimmerEventFilter(this));
146  // Make sure it is undimmed at the beginning and then fire off the dimming
147  // animation.
148  background_animator_.SetPaintsBackground(false,
149                                           ash::BACKGROUND_CHANGE_IMMEDIATE);
150  SetHovered(false);
151}
152
153DimmerView::~DimmerView() {
154}
155
156void DimmerView::SetHovered(bool hovered) {
157  // Remember the hovered state so that we can correct the state once a
158  // possible force state has disappeared.
159  is_hovered_ = hovered;
160  // Undimm also if we were forced to by e.g. an open menu.
161  hovered |= force_hovered_;
162  background_animator_.SetDuration(hovered ? kTimeToUnDimMs : kTimeToDimMs);
163  background_animator_.SetPaintsBackground(!hovered,
164      disable_dimming_animations_for_test_ ?
165          ash::BACKGROUND_CHANGE_IMMEDIATE : ash::BACKGROUND_CHANGE_ANIMATE);
166}
167
168void DimmerView::ForceUndimming(bool force) {
169  bool previous = force_hovered_;
170  force_hovered_ = force;
171  // If the forced change does change the result we apply the change.
172  if (is_hovered_ || force_hovered_ != is_hovered_ || previous)
173    SetHovered(is_hovered_);
174}
175
176void DimmerView::OnPaintBackground(gfx::Canvas* canvas) {
177  SkPaint paint;
178  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
179  gfx::ImageSkia shelf_background =
180      *rb.GetImageNamed(IDR_ASH_SHELF_DIMMING).ToImageSkia();
181
182  if (shelf_->GetAlignment() != ash::SHELF_ALIGNMENT_BOTTOM) {
183    shelf_background = gfx::ImageSkiaOperations::CreateRotatedImage(
184        shelf_background,
185        shelf_->shelf_layout_manager()->SelectValueForShelfAlignment(
186            SkBitmapOperations::ROTATION_90_CW,
187            SkBitmapOperations::ROTATION_90_CW,
188            SkBitmapOperations::ROTATION_270_CW,
189            SkBitmapOperations::ROTATION_180_CW));
190  }
191  paint.setAlpha(alpha_);
192  canvas->DrawImageInt(shelf_background,
193                       0,
194                       0,
195                       shelf_background.width(),
196                       shelf_background.height(),
197                       0,
198                       0,
199                       width(),
200                       height(),
201                       false,
202                       paint);
203}
204
205DimmerView::DimmerEventFilter::DimmerEventFilter(DimmerView* owner)
206    : owner_(owner),
207      mouse_inside_(false),
208      touch_inside_(false) {
209  ash::Shell::GetInstance()->AddPreTargetHandler(this);
210}
211
212DimmerView::DimmerEventFilter::~DimmerEventFilter() {
213  ash::Shell::GetInstance()->RemovePreTargetHandler(this);
214}
215
216void DimmerView::DimmerEventFilter::OnMouseEvent(ui::MouseEvent* event) {
217  if (event->type() != ui::ET_MOUSE_MOVED &&
218      event->type() != ui::ET_MOUSE_DRAGGED)
219    return;
220  bool inside = owner_->GetBoundsInScreen().Contains(event->root_location());
221  if (mouse_inside_ || touch_inside_ != inside || touch_inside_)
222    owner_->SetHovered(inside || touch_inside_);
223  mouse_inside_ = inside;
224}
225
226void DimmerView::DimmerEventFilter::OnTouchEvent(ui::TouchEvent* event) {
227  bool touch_inside = false;
228  if (event->type() != ui::ET_TOUCH_RELEASED &&
229      event->type() != ui::ET_TOUCH_CANCELLED)
230    touch_inside = owner_->GetBoundsInScreen().Contains(event->root_location());
231
232  if (mouse_inside_ || touch_inside_ != mouse_inside_ || touch_inside)
233    owner_->SetHovered(mouse_inside_ || touch_inside);
234  touch_inside_ = touch_inside;
235}
236
237using ash::ShelfLayoutManager;
238
239// ShelfWindowTargeter makes it easier to resize windows with the mouse when the
240// window-edge slightly overlaps with the shelf edge. The targeter also makes it
241// easier to drag the shelf out with touch while it is hidden.
242class ShelfWindowTargeter : public wm::EasyResizeWindowTargeter,
243                            public ash::ShelfLayoutManagerObserver {
244 public:
245  ShelfWindowTargeter(aura::Window* container,
246                      ShelfLayoutManager* shelf)
247      : wm::EasyResizeWindowTargeter(container, gfx::Insets(), gfx::Insets()),
248        shelf_(shelf) {
249    WillChangeVisibilityState(shelf_->visibility_state());
250    shelf_->AddObserver(this);
251  }
252
253  virtual ~ShelfWindowTargeter() {
254    // |shelf_| may have been destroyed by this time.
255    if (shelf_)
256      shelf_->RemoveObserver(this);
257  }
258
259 private:
260  gfx::Insets GetInsetsForAlignment(int distance,
261                                    ash::ShelfAlignment alignment) {
262    switch (alignment) {
263      case ash::SHELF_ALIGNMENT_BOTTOM:
264        return gfx::Insets(distance, 0, 0, 0);
265      case ash::SHELF_ALIGNMENT_LEFT:
266        return gfx::Insets(0, 0, 0, distance);
267      case ash::SHELF_ALIGNMENT_RIGHT:
268        return gfx::Insets(0, distance, 0, 0);
269      case ash::SHELF_ALIGNMENT_TOP:
270        return gfx::Insets(0, 0, distance, 0);
271    }
272    NOTREACHED();
273    return gfx::Insets();
274  }
275
276  // ash::ShelfLayoutManagerObserver:
277  virtual void WillDeleteShelf() OVERRIDE {
278    shelf_ = NULL;
279  }
280
281  virtual void WillChangeVisibilityState(
282      ash::ShelfVisibilityState new_state) OVERRIDE {
283    gfx::Insets mouse_insets;
284    gfx::Insets touch_insets;
285    if (new_state == ash::SHELF_VISIBLE) {
286      // Let clicks at the very top of the shelf through so windows can be
287      // resized with the bottom-right corner and bottom edge.
288      mouse_insets = GetInsetsForAlignment(
289          ShelfLayoutManager::kWorkspaceAreaVisibleInset,
290          shelf_->GetAlignment());
291    } else if (new_state == ash::SHELF_AUTO_HIDE) {
292      // Extend the touch hit target out a bit to allow users to drag shelf out
293      // while hidden.
294      touch_insets = GetInsetsForAlignment(
295          -ShelfLayoutManager::kWorkspaceAreaAutoHideInset,
296          shelf_->GetAlignment());
297    }
298
299    set_mouse_extend(mouse_insets);
300    set_touch_extend(touch_insets);
301  }
302
303  ShelfLayoutManager* shelf_;
304
305  DISALLOW_COPY_AND_ASSIGN(ShelfWindowTargeter);
306};
307
308}  // namespace
309
310namespace ash {
311
312// The contents view of the Shelf. This view contains ShelfView and
313// sizes it to the width of the shelf minus the size of the status area.
314class ShelfWidget::DelegateView : public views::WidgetDelegate,
315                                  public views::AccessiblePaneView,
316                                  public BackgroundAnimatorDelegate,
317                                  public aura::WindowObserver {
318 public:
319  explicit DelegateView(ShelfWidget* shelf);
320  virtual ~DelegateView();
321
322  void set_focus_cycler(FocusCycler* focus_cycler) {
323    focus_cycler_ = focus_cycler;
324  }
325  FocusCycler* focus_cycler() { return focus_cycler_; }
326
327  ui::Layer* opaque_background() { return &opaque_background_; }
328
329  // Set if the shelf area is dimmed (eg when a window is maximized).
330  void SetDimmed(bool dimmed);
331  bool GetDimmed() const;
332
333  void SetParentLayer(ui::Layer* layer);
334
335  // views::View overrides:
336  virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE;
337
338  // views::WidgetDelegateView overrides:
339  virtual views::Widget* GetWidget() OVERRIDE {
340    return View::GetWidget();
341  }
342  virtual const views::Widget* GetWidget() const OVERRIDE {
343    return View::GetWidget();
344  }
345
346  virtual bool CanActivate() const OVERRIDE;
347  virtual void Layout() OVERRIDE;
348  virtual void ReorderChildLayers(ui::Layer* parent_layer) OVERRIDE;
349  // This will be called when the parent local bounds change.
350  virtual void OnBoundsChanged(const gfx::Rect& old_bounds) OVERRIDE;
351
352  // aura::WindowObserver overrides:
353  // This will be called when the shelf itself changes its absolute position.
354  // Since the |dimmer_| panel needs to be placed in screen coordinates it needs
355  // to be repositioned. The difference to the OnBoundsChanged call above is
356  // that this gets also triggered when the shelf only moves.
357  virtual void OnWindowBoundsChanged(aura::Window* window,
358                                     const gfx::Rect& old_bounds,
359                                     const gfx::Rect& new_bounds) OVERRIDE;
360
361  // BackgroundAnimatorDelegate overrides:
362  virtual void UpdateBackground(int alpha) OVERRIDE;
363
364  // Force the shelf to be presented in an undimmed state.
365  void ForceUndimming(bool force);
366
367  // A function to test the current alpha used by the dimming bar. If there is
368  // no dimmer active, the function will return -1.
369  int GetDimmingAlphaForTest();
370
371  // A function to test the bounds of the dimming bar. Returns gfx::Rect() if
372  // the dimmer is inactive.
373  gfx::Rect GetDimmerBoundsForTest();
374
375  // Disable dimming animations for running tests. This needs to be called
376  // prior to the creation of of the |dimmer_|.
377  void disable_dimming_animations_for_test() {
378    disable_dimming_animations_for_test_ = true;
379  }
380
381 private:
382  ShelfWidget* shelf_;
383  scoped_ptr<views::Widget> dimmer_;
384  FocusCycler* focus_cycler_;
385  int alpha_;
386  ui::Layer opaque_background_;
387
388  // The view which does the dimming.
389  DimmerView* dimmer_view_;
390
391  // True if dimming animations should be turned off.
392  bool disable_dimming_animations_for_test_;
393
394  DISALLOW_COPY_AND_ASSIGN(DelegateView);
395};
396
397ShelfWidget::DelegateView::DelegateView(ShelfWidget* shelf)
398    : shelf_(shelf),
399      focus_cycler_(NULL),
400      alpha_(0),
401      opaque_background_(ui::LAYER_SOLID_COLOR),
402      dimmer_view_(NULL),
403      disable_dimming_animations_for_test_(false) {
404  set_allow_deactivate_on_esc(true);
405  opaque_background_.SetColor(SK_ColorBLACK);
406  opaque_background_.SetBounds(GetLocalBounds());
407  opaque_background_.SetOpacity(0.0f);
408}
409
410ShelfWidget::DelegateView::~DelegateView() {
411  // Make sure that the dimmer goes away since it might have set an observer.
412  SetDimmed(false);
413}
414
415void ShelfWidget::DelegateView::SetDimmed(bool value) {
416  if (value == (dimmer_.get() != NULL))
417    return;
418
419  if (value) {
420    dimmer_.reset(new views::Widget);
421    views::Widget::InitParams params(
422        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
423    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
424    params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
425    params.accept_events = false;
426    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
427    params.parent = shelf_->GetNativeView();
428    dimmer_->Init(params);
429    dimmer_->GetNativeWindow()->SetName("ShelfDimmer");
430    dimmer_->SetBounds(shelf_->GetWindowBoundsInScreen());
431    // The shelf should not take focus when it is initially shown.
432    dimmer_->set_focus_on_creation(false);
433    dimmer_view_ = new DimmerView(shelf_, disable_dimming_animations_for_test_);
434    dimmer_->SetContentsView(dimmer_view_);
435    dimmer_->GetNativeView()->SetName("ShelfDimmerView");
436    dimmer_->Show();
437    shelf_->GetNativeView()->AddObserver(this);
438  } else {
439    // Some unit tests will come here with a destroyed window.
440    if (shelf_->GetNativeView())
441      shelf_->GetNativeView()->RemoveObserver(this);
442    dimmer_view_ = NULL;
443    dimmer_.reset(NULL);
444  }
445}
446
447bool ShelfWidget::DelegateView::GetDimmed() const {
448  return dimmer_.get() && dimmer_->IsVisible();
449}
450
451void ShelfWidget::DelegateView::SetParentLayer(ui::Layer* layer) {
452  layer->Add(&opaque_background_);
453  ReorderLayers();
454}
455
456void ShelfWidget::DelegateView::OnPaintBackground(gfx::Canvas* canvas) {
457  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
458  gfx::ImageSkia shelf_background =
459      *rb.GetImageSkiaNamed(IDR_ASH_SHELF_BACKGROUND);
460  if (SHELF_ALIGNMENT_BOTTOM != shelf_->GetAlignment())
461    shelf_background = gfx::ImageSkiaOperations::CreateRotatedImage(
462        shelf_background,
463        shelf_->shelf_layout_manager()->SelectValueForShelfAlignment(
464            SkBitmapOperations::ROTATION_90_CW,
465            SkBitmapOperations::ROTATION_90_CW,
466            SkBitmapOperations::ROTATION_270_CW,
467            SkBitmapOperations::ROTATION_180_CW));
468  const gfx::Rect dock_bounds(shelf_->shelf_layout_manager()->dock_bounds());
469  SkPaint paint;
470  paint.setAlpha(alpha_);
471  canvas->DrawImageInt(shelf_background,
472                       0,
473                       0,
474                       shelf_background.width(),
475                       shelf_background.height(),
476                       (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() &&
477                        dock_bounds.x() == 0 && dock_bounds.width() > 0)
478                           ? dock_bounds.width()
479                           : 0,
480                       0,
481                       SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment()
482                           ? width() - dock_bounds.width()
483                           : width(),
484                       height(),
485                       false,
486                       paint);
487  if (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() &&
488      dock_bounds.width() > 0) {
489    // The part of the shelf background that is in the corner below the docked
490    // windows close to the work area is an arched gradient that blends
491    // vertically oriented docked background and horizontal shelf.
492    gfx::ImageSkia shelf_corner = *rb.GetImageSkiaNamed(IDR_ASH_SHELF_CORNER);
493    if (dock_bounds.x() == 0) {
494      shelf_corner = gfx::ImageSkiaOperations::CreateRotatedImage(
495          shelf_corner, SkBitmapOperations::ROTATION_90_CW);
496    }
497    canvas->DrawImageInt(
498        shelf_corner,
499        0,
500        0,
501        shelf_corner.width(),
502        shelf_corner.height(),
503        dock_bounds.x() > 0 ? dock_bounds.x() : dock_bounds.width() - height(),
504        0,
505        height(),
506        height(),
507        false,
508        paint);
509    // The part of the shelf background that is just below the docked windows
510    // is drawn using the last (lowest) 1-pixel tall strip of the image asset.
511    // This avoids showing the border 3D shadow between the shelf and the dock.
512    canvas->DrawImageInt(shelf_background,
513                         0,
514                         shelf_background.height() - 1,
515                         shelf_background.width(),
516                         1,
517                         dock_bounds.x() > 0 ? dock_bounds.x() + height() : 0,
518                         0,
519                         dock_bounds.width() - height(),
520                         height(),
521                         false,
522                         paint);
523  }
524  gfx::Rect black_rect =
525      shelf_->shelf_layout_manager()->SelectValueForShelfAlignment(
526          gfx::Rect(0, height() - kNumBlackPixels, width(), kNumBlackPixels),
527          gfx::Rect(0, 0, kNumBlackPixels, height()),
528          gfx::Rect(width() - kNumBlackPixels, 0, kNumBlackPixels, height()),
529          gfx::Rect(0, 0, width(), kNumBlackPixels));
530  canvas->FillRect(black_rect, SK_ColorBLACK);
531}
532
533bool ShelfWidget::DelegateView::CanActivate() const {
534  // Allow to activate as fallback.
535  if (shelf_->activating_as_fallback_)
536    return true;
537  // Allow to activate from the focus cycler.
538  if (focus_cycler_ && focus_cycler_->widget_activating() == GetWidget())
539    return true;
540  // Disallow activating in other cases, especially when using mouse.
541  return false;
542}
543
544void ShelfWidget::DelegateView::Layout() {
545  for(int i = 0; i < child_count(); ++i) {
546    if (shelf_->shelf_layout_manager()->IsHorizontalAlignment()) {
547      child_at(i)->SetBounds(child_at(i)->x(), child_at(i)->y(),
548                             child_at(i)->width(), height());
549    } else {
550      child_at(i)->SetBounds(child_at(i)->x(), child_at(i)->y(),
551                             width(), child_at(i)->height());
552    }
553  }
554}
555
556void ShelfWidget::DelegateView::ReorderChildLayers(ui::Layer* parent_layer) {
557  views::View::ReorderChildLayers(parent_layer);
558  parent_layer->StackAtBottom(&opaque_background_);
559}
560
561void ShelfWidget::DelegateView::OnBoundsChanged(const gfx::Rect& old_bounds) {
562  opaque_background_.SetBounds(GetLocalBounds());
563  if (dimmer_)
564    dimmer_->SetBounds(GetBoundsInScreen());
565}
566
567void ShelfWidget::DelegateView::OnWindowBoundsChanged(
568    aura::Window* window,
569    const gfx::Rect& old_bounds,
570    const gfx::Rect& new_bounds) {
571  // Coming here the shelf got repositioned and since the |dimmer_| is placed
572  // in screen coordinates and not relative to the parent it needs to be
573  // repositioned accordingly.
574  dimmer_->SetBounds(GetBoundsInScreen());
575}
576
577void ShelfWidget::DelegateView::ForceUndimming(bool force) {
578  if (GetDimmed())
579    dimmer_view_->ForceUndimming(force);
580}
581
582int ShelfWidget::DelegateView::GetDimmingAlphaForTest() {
583  if (GetDimmed())
584    return dimmer_view_->get_dimming_alpha_for_test();
585  return -1;
586}
587
588gfx::Rect ShelfWidget::DelegateView::GetDimmerBoundsForTest() {
589  if (GetDimmed())
590    return dimmer_view_->GetBoundsInScreen();
591  return gfx::Rect();
592}
593
594void ShelfWidget::DelegateView::UpdateBackground(int alpha) {
595  alpha_ = alpha;
596  SchedulePaint();
597}
598
599ShelfWidget::ShelfWidget(aura::Window* shelf_container,
600                         aura::Window* status_container,
601                         WorkspaceController* workspace_controller)
602    : delegate_view_(new DelegateView(this)),
603      background_animator_(delegate_view_, 0, kShelfBackgroundAlpha),
604      activating_as_fallback_(false),
605      window_container_(shelf_container) {
606  views::Widget::InitParams params(
607      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
608  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
609  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
610  params.parent = shelf_container;
611  params.delegate = delegate_view_;
612  Init(params);
613
614  // The shelf should not take focus when initially shown.
615  set_focus_on_creation(false);
616  SetContentsView(delegate_view_);
617  delegate_view_->SetParentLayer(GetLayer());
618
619  status_area_widget_ = new StatusAreaWidget(status_container);
620  status_area_widget_->CreateTrayViews();
621  if (Shell::GetInstance()->session_state_delegate()->
622          IsActiveUserSessionStarted()) {
623    status_area_widget_->Show();
624  }
625  Shell::GetInstance()->focus_cycler()->AddWidget(status_area_widget_);
626
627  shelf_layout_manager_ = new ShelfLayoutManager(this);
628  shelf_layout_manager_->AddObserver(this);
629  shelf_container->SetLayoutManager(shelf_layout_manager_);
630  shelf_layout_manager_->set_workspace_controller(workspace_controller);
631  workspace_controller->SetShelf(shelf_layout_manager_);
632
633  status_container->SetLayoutManager(new StatusAreaLayoutManager(this));
634
635  shelf_container->SetEventTargeter(scoped_ptr<ui::EventTargeter>(new
636      ShelfWindowTargeter(shelf_container, shelf_layout_manager_)));
637  status_container->SetEventTargeter(scoped_ptr<ui::EventTargeter>(new
638      ShelfWindowTargeter(status_container, shelf_layout_manager_)));
639
640  views::Widget::AddObserver(this);
641}
642
643ShelfWidget::~ShelfWidget() {
644  RemoveObserver(this);
645}
646
647void ShelfWidget::SetPaintsBackground(
648    ShelfBackgroundType background_type,
649    BackgroundAnimatorChangeType change_type) {
650  ui::Layer* opaque_background = delegate_view_->opaque_background();
651  float target_opacity =
652      (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f;
653  scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation;
654  if (change_type != BACKGROUND_CHANGE_IMMEDIATE) {
655    opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings(
656        opaque_background->GetAnimator()));
657    opaque_background_animation->SetTransitionDuration(
658        base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs));
659  }
660  opaque_background->SetOpacity(target_opacity);
661
662  // TODO(mukai): use ui::Layer on both opaque_background and normal background
663  // retire background_animator_ at all. It would be simpler.
664  // See also DockedBackgroundWidget::SetPaintsBackground.
665  background_animator_.SetPaintsBackground(
666      background_type != SHELF_BACKGROUND_DEFAULT,
667      change_type);
668  delegate_view_->SchedulePaint();
669}
670
671ShelfBackgroundType ShelfWidget::GetBackgroundType() const {
672  if (delegate_view_->opaque_background()->GetTargetOpacity() == 1.0f)
673    return SHELF_BACKGROUND_MAXIMIZED;
674  if (background_animator_.paints_background())
675    return SHELF_BACKGROUND_OVERLAP;
676
677  return SHELF_BACKGROUND_DEFAULT;
678}
679
680// static
681bool ShelfWidget::ShelfAlignmentAllowed() {
682  user::LoginStatus login_status =
683      Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
684
685  switch (login_status) {
686    case user::LOGGED_IN_USER:
687    case user::LOGGED_IN_OWNER:
688      return true;
689    case user::LOGGED_IN_LOCKED:
690    case user::LOGGED_IN_PUBLIC:
691    case user::LOGGED_IN_LOCALLY_MANAGED:
692    case user::LOGGED_IN_GUEST:
693    case user::LOGGED_IN_RETAIL_MODE:
694    case user::LOGGED_IN_KIOSK_APP:
695    case user::LOGGED_IN_NONE:
696      return false;
697  }
698
699  DCHECK(false);
700  return false;
701}
702
703ShelfAlignment ShelfWidget::GetAlignment() const {
704  return shelf_layout_manager_->GetAlignment();
705}
706
707void ShelfWidget::SetAlignment(ShelfAlignment alignment) {
708  if (shelf_)
709    shelf_->SetAlignment(alignment);
710  status_area_widget_->SetShelfAlignment(alignment);
711  delegate_view_->SchedulePaint();
712}
713
714void ShelfWidget::SetDimsShelf(bool dimming) {
715  delegate_view_->SetDimmed(dimming);
716  // Repaint all children, allowing updates to reflect dimmed state eg:
717  // status area background, app list button and overflow button.
718  if (shelf_)
719    shelf_->SchedulePaint();
720  status_area_widget_->GetContentsView()->SchedulePaint();
721}
722
723bool ShelfWidget::GetDimsShelf() const {
724  return delegate_view_->GetDimmed();
725}
726
727void ShelfWidget::CreateShelf() {
728  if (shelf_)
729    return;
730
731  Shell* shell = Shell::GetInstance();
732  // This needs to be called before shelf_model().
733  ShelfDelegate* shelf_delegate = shell->GetShelfDelegate();
734  if (!shelf_delegate)
735    return;  // Not ready to create Shelf.
736
737  shelf_.reset(
738      new Shelf(shell->shelf_model(), shell->GetShelfDelegate(), this));
739  SetFocusCycler(shell->focus_cycler());
740
741  // Inform the root window controller.
742  RootWindowController::ForWindow(window_container_)->OnShelfCreated();
743
744  shelf_->SetVisible(
745      shell->session_state_delegate()->IsActiveUserSessionStarted());
746  shelf_layout_manager_->LayoutShelf();
747  Show();
748}
749
750bool ShelfWidget::IsShelfVisible() const {
751  return shelf_.get() && shelf_->IsVisible();
752}
753
754void ShelfWidget::SetShelfVisibility(bool visible) {
755  if (shelf_)
756    shelf_->SetVisible(visible);
757}
758
759void ShelfWidget::SetFocusCycler(FocusCycler* focus_cycler) {
760  delegate_view_->set_focus_cycler(focus_cycler);
761  if (focus_cycler)
762    focus_cycler->AddWidget(this);
763}
764
765FocusCycler* ShelfWidget::GetFocusCycler() {
766  return delegate_view_->focus_cycler();
767}
768
769void ShelfWidget::ShutdownStatusAreaWidget() {
770  if (status_area_widget_)
771    status_area_widget_->Shutdown();
772  status_area_widget_ = NULL;
773}
774
775void ShelfWidget::ForceUndimming(bool force) {
776  delegate_view_->ForceUndimming(force);
777}
778
779void ShelfWidget::OnWidgetActivationChanged(views::Widget* widget,
780                                            bool active) {
781  activating_as_fallback_ = false;
782  if (active)
783    delegate_view_->SetPaneFocusAndFocusDefault();
784  else
785    delegate_view_->GetFocusManager()->ClearFocus();
786}
787
788int ShelfWidget::GetDimmingAlphaForTest() {
789  if (delegate_view_)
790    return delegate_view_->GetDimmingAlphaForTest();
791  return -1;
792}
793
794gfx::Rect ShelfWidget::GetDimmerBoundsForTest() {
795  if (delegate_view_)
796    return delegate_view_->GetDimmerBoundsForTest();
797  return gfx::Rect();
798}
799
800void ShelfWidget::DisableDimmingAnimationsForTest() {
801  DCHECK(delegate_view_);
802  return delegate_view_->disable_dimming_animations_for_test();
803}
804
805void ShelfWidget::WillDeleteShelf() {
806  shelf_layout_manager_->RemoveObserver(this);
807  shelf_layout_manager_ = NULL;
808}
809
810}  // namespace ash
811