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/system/status_area_widget_delegate.h" 6 7#include "ash/ash_export.h" 8#include "ash/ash_switches.h" 9#include "ash/focus_cycler.h" 10#include "ash/shell.h" 11#include "ash/shell_window_ids.h" 12#include "ash/system/tray/tray_constants.h" 13#include "base/memory/scoped_ptr.h" 14#include "base/strings/utf_string_conversions.h" 15#include "ui/aura/window_event_dispatcher.h" 16#include "ui/compositor/layer.h" 17#include "ui/compositor/scoped_layer_animation_settings.h" 18#include "ui/gfx/animation/tween.h" 19#include "ui/gfx/canvas.h" 20#include "ui/gfx/image/image.h" 21#include "ui/views/accessible_pane_view.h" 22#include "ui/views/layout/grid_layout.h" 23#include "ui/views/widget/widget.h" 24 25namespace { 26 27const int kAnimationDurationMs = 250; 28 29class StatusAreaWidgetDelegateAnimationSettings 30 : public ui::ScopedLayerAnimationSettings { 31 public: 32 explicit StatusAreaWidgetDelegateAnimationSettings(ui::Layer* layer) 33 : ui::ScopedLayerAnimationSettings(layer->GetAnimator()) { 34 SetTransitionDuration( 35 base::TimeDelta::FromMilliseconds(kAnimationDurationMs)); 36 SetPreemptionStrategy( 37 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 38 SetTweenType(gfx::Tween::EASE_IN_OUT); 39 } 40 41 virtual ~StatusAreaWidgetDelegateAnimationSettings() {} 42 43 private: 44 DISALLOW_COPY_AND_ASSIGN(StatusAreaWidgetDelegateAnimationSettings); 45}; 46 47} // namespace 48 49namespace ash { 50 51StatusAreaWidgetDelegate::StatusAreaWidgetDelegate() 52 : focus_cycler_for_testing_(NULL), 53 alignment_(SHELF_ALIGNMENT_BOTTOM) { 54 // Allow the launcher to surrender the focus to another window upon 55 // navigation completion by the user. 56 set_allow_deactivate_on_esc(true); 57 SetPaintToLayer(true); 58 SetFillsBoundsOpaquely(false); 59} 60 61StatusAreaWidgetDelegate::~StatusAreaWidgetDelegate() { 62} 63 64void StatusAreaWidgetDelegate::SetFocusCyclerForTesting( 65 const FocusCycler* focus_cycler) { 66 focus_cycler_for_testing_ = focus_cycler; 67} 68 69views::View* StatusAreaWidgetDelegate::GetDefaultFocusableChild() { 70 return child_at(0); 71} 72 73views::Widget* StatusAreaWidgetDelegate::GetWidget() { 74 return View::GetWidget(); 75} 76 77const views::Widget* StatusAreaWidgetDelegate::GetWidget() const { 78 return View::GetWidget(); 79} 80 81void StatusAreaWidgetDelegate::OnGestureEvent(ui::GestureEvent* event) { 82 if (gesture_handler_.ProcessGestureEvent(*event)) 83 event->StopPropagation(); 84 else 85 views::AccessiblePaneView::OnGestureEvent(event); 86} 87 88bool StatusAreaWidgetDelegate::CanActivate() const { 89 // We don't want mouse clicks to activate us, but we need to allow 90 // activation when the user is using the keyboard (FocusCycler). 91 const FocusCycler* focus_cycler = focus_cycler_for_testing_ ? 92 focus_cycler_for_testing_ : Shell::GetInstance()->focus_cycler(); 93 return focus_cycler->widget_activating() == GetWidget(); 94} 95 96void StatusAreaWidgetDelegate::DeleteDelegate() { 97} 98 99void StatusAreaWidgetDelegate::AddTray(views::View* tray) { 100 SetLayoutManager(NULL); // Reset layout manager before adding a child. 101 AddChildView(tray); 102 // Set the layout manager with the new list of children. 103 UpdateLayout(); 104} 105 106void StatusAreaWidgetDelegate::UpdateLayout() { 107 // Use a grid layout so that the trays can be centered in each cell, and 108 // so that the widget gets laid out correctly when tray sizes change. 109 views::GridLayout* layout = new views::GridLayout(this); 110 SetLayoutManager(layout); 111 112 views::ColumnSet* columns = layout->AddColumnSet(0); 113 if (alignment_ == SHELF_ALIGNMENT_BOTTOM || 114 alignment_ == SHELF_ALIGNMENT_TOP) { 115 bool is_first_visible_child = true; 116 for (int c = 0; c < child_count(); ++c) { 117 views::View* child = child_at(c); 118 if (!child->visible()) 119 continue; 120 if (!is_first_visible_child) 121 columns->AddPaddingColumn(0, kTraySpacing); 122 is_first_visible_child = false; 123 columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 124 0, /* resize percent */ 125 views::GridLayout::USE_PREF, 0, 0); 126 } 127 layout->StartRow(0, 0); 128 for (int c = child_count() - 1; c >= 0; --c) { 129 views::View* child = child_at(c); 130 if (child->visible()) 131 layout->AddView(child); 132 } 133 } else { 134 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 135 0, /* resize percent */ 136 views::GridLayout::USE_PREF, 0, 0); 137 bool is_first_visible_child = true; 138 for (int c = child_count() - 1; c >= 0; --c) { 139 views::View* child = child_at(c); 140 if (!child->visible()) 141 continue; 142 if (!is_first_visible_child) 143 layout->AddPaddingRow(0, kTraySpacing); 144 is_first_visible_child = false; 145 layout->StartRow(0, 0); 146 layout->AddView(child); 147 } 148 } 149 150 layer()->GetAnimator()->StopAnimating(); 151 StatusAreaWidgetDelegateAnimationSettings settings(layer()); 152 153 Layout(); 154 UpdateWidgetSize(); 155} 156 157void StatusAreaWidgetDelegate::ChildPreferredSizeChanged(View* child) { 158 // Need to resize the window when trays or items are added/removed. 159 StatusAreaWidgetDelegateAnimationSettings settings(layer()); 160 UpdateWidgetSize(); 161} 162 163void StatusAreaWidgetDelegate::ChildVisibilityChanged(View* child) { 164 UpdateLayout(); 165} 166 167void StatusAreaWidgetDelegate::UpdateWidgetSize() { 168 if (GetWidget()) 169 GetWidget()->SetSize(GetPreferredSize()); 170} 171 172} // namespace ash 173