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