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 26namespace { 27 28const int kAnimationDurationMs = 250; 29 30class 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 50namespace ash { 51 52StatusAreaWidgetDelegate::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 62StatusAreaWidgetDelegate::~StatusAreaWidgetDelegate() { 63} 64 65void StatusAreaWidgetDelegate::SetFocusCyclerForTesting( 66 const FocusCycler* focus_cycler) { 67 focus_cycler_for_testing_ = focus_cycler; 68} 69 70views::View* StatusAreaWidgetDelegate::GetDefaultFocusableChild() { 71 return child_at(0); 72} 73 74views::Widget* StatusAreaWidgetDelegate::GetWidget() { 75 return View::GetWidget(); 76} 77 78const views::Widget* StatusAreaWidgetDelegate::GetWidget() const { 79 return View::GetWidget(); 80} 81 82void StatusAreaWidgetDelegate::OnGestureEvent(ui::GestureEvent* event) { 83 if (gesture_handler_.ProcessGestureEvent(*event)) 84 event->StopPropagation(); 85 else 86 views::AccessiblePaneView::OnGestureEvent(event); 87} 88 89bool 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 97void StatusAreaWidgetDelegate::DeleteDelegate() { 98} 99 100void 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 107void 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 158void 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 164void StatusAreaWidgetDelegate::ChildVisibilityChanged(View* child) { 165 UpdateLayout(); 166} 167 168void StatusAreaWidgetDelegate::UpdateWidgetSize() { 169 if (GetWidget()) 170 GetWidget()->SetSize(GetPreferredSize()); 171} 172 173} // namespace ash 174