Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(149)

Side by Side Diff: ash/common/system/tray/system_tray_bubble.cc

Issue 2732813002: chromeos: Move files in //ash/common to //ash, part 1 (Closed)
Patch Set: rebase Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/common/system/tray/system_tray_bubble.h"
6
7 #include <utility>
8 #include <vector>
9
10 #include "ash/common/material_design/material_design_controller.h"
11 #include "ash/common/system/tray/system_tray.h"
12 #include "ash/common/system/tray/system_tray_delegate.h"
13 #include "ash/common/system/tray/system_tray_item.h"
14 #include "ash/common/system/tray/tray_bubble_wrapper.h"
15 #include "ash/common/system/tray/tray_constants.h"
16 #include "ash/common/system/tray/tray_popup_item_container.h"
17 #include "ash/common/wm_shell.h"
18 #include "base/metrics/histogram_macros.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "ui/compositor/layer.h"
21 #include "ui/compositor/layer_animation_observer.h"
22 #include "ui/compositor/scoped_layer_animation_settings.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/views/border.h"
25 #include "ui/views/layout/box_layout.h"
26 #include "ui/views/view.h"
27 #include "ui/views/widget/widget.h"
28
29 using views::TrayBubbleView;
30
31 namespace ash {
32
33 namespace {
34
35 // Normally a detailed view is the same size as the default view. However,
36 // when showing a detailed view directly (e.g. clicking on a notification),
37 // we may not know the height of the default view, or the default view may
38 // be too short, so we use this as a default and minimum height for any
39 // detailed view.
40 int GetDetailedBubbleMaxHeight() {
41 return kTrayPopupItemMinHeight * 5;
42 }
43
44 // Duration of swipe animation used when transitioning from a default to
45 // detailed view or vice versa.
46 const int kSwipeDelayMS = 150;
47
48 // Extra bottom padding when showing the BUBBLE_TYPE_DEFAULT view.
49 const int kDefaultViewBottomPadding = 4;
50
51 // Implicit animation observer that deletes itself and the layer at the end of
52 // the animation.
53 class AnimationObserverDeleteLayer : public ui::ImplicitAnimationObserver {
54 public:
55 explicit AnimationObserverDeleteLayer(ui::Layer* layer) : layer_(layer) {}
56
57 ~AnimationObserverDeleteLayer() override {}
58
59 void OnImplicitAnimationsCompleted() override {
60 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
61 }
62
63 private:
64 std::unique_ptr<ui::Layer> layer_;
65
66 DISALLOW_COPY_AND_ASSIGN(AnimationObserverDeleteLayer);
67 };
68
69 } // namespace
70
71 // SystemTrayBubble
72
73 SystemTrayBubble::SystemTrayBubble(
74 ash::SystemTray* tray,
75 const std::vector<ash::SystemTrayItem*>& items,
76 BubbleType bubble_type)
77 : tray_(tray),
78 bubble_view_(nullptr),
79 items_(items),
80 bubble_type_(bubble_type),
81 autoclose_delay_(0) {}
82
83 SystemTrayBubble::~SystemTrayBubble() {
84 DestroyItemViews();
85 // Reset the host pointer in bubble_view_ in case its destruction is deferred.
86 if (bubble_view_)
87 bubble_view_->reset_delegate();
88 }
89
90 void SystemTrayBubble::UpdateView(
91 const std::vector<ash::SystemTrayItem*>& items,
92 BubbleType bubble_type) {
93 std::unique_ptr<ui::Layer> scoped_layer;
94 if (bubble_type != bubble_type_) {
95 base::TimeDelta swipe_duration =
96 base::TimeDelta::FromMilliseconds(kSwipeDelayMS);
97 scoped_layer = bubble_view_->RecreateLayer();
98 // Keep the reference to layer as we need it after releasing it.
99 ui::Layer* layer = scoped_layer.get();
100 DCHECK(layer);
101 layer->SuppressPaint();
102
103 // When transitioning from detailed view to default view, animate the
104 // existing view (slide out towards the right).
105 if (bubble_type == BUBBLE_TYPE_DEFAULT) {
106 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
107 settings.AddObserver(
108 new AnimationObserverDeleteLayer(scoped_layer.release()));
109 settings.SetTransitionDuration(swipe_duration);
110 settings.SetTweenType(gfx::Tween::EASE_OUT);
111 gfx::Transform transform;
112 transform.Translate(layer->bounds().width(), 0.0);
113 layer->SetTransform(transform);
114 }
115
116 {
117 // Add a shadow layer to make the old layer darker as the animation
118 // progresses.
119 ui::Layer* shadow = new ui::Layer(ui::LAYER_SOLID_COLOR);
120 shadow->SetColor(SK_ColorBLACK);
121 shadow->SetOpacity(0.01f);
122 shadow->SetBounds(layer->bounds());
123 layer->Add(shadow);
124 layer->StackAtTop(shadow);
125 {
126 // Animate the darkening effect a little longer than the swipe-in. This
127 // is to make sure the darkening animation does not end up finishing
128 // early, because the dark layer goes away at the end of the animation,
129 // and there is a brief moment when the old view is still visible, but
130 // it does not have the shadow layer on top.
131 ui::ScopedLayerAnimationSettings settings(shadow->GetAnimator());
132 settings.AddObserver(new AnimationObserverDeleteLayer(shadow));
133 settings.SetTransitionDuration(swipe_duration +
134 base::TimeDelta::FromMilliseconds(150));
135 settings.SetTweenType(gfx::Tween::LINEAR);
136 shadow->SetOpacity(0.15f);
137 }
138 }
139 }
140
141 DestroyItemViews();
142 bubble_view_->RemoveAllChildViews(true);
143
144 items_ = items;
145 bubble_type_ = bubble_type;
146 CreateItemViews(WmShell::Get()->system_tray_delegate()->GetUserLoginStatus());
147
148 // Close bubble view if we failed to create the item view.
149 if (!bubble_view_->has_children()) {
150 Close();
151 return;
152 }
153
154 UpdateBottomPadding();
155 bubble_view_->GetWidget()->GetContentsView()->Layout();
156 // Make sure that the bubble is large enough for the default view.
157 if (bubble_type_ == BUBBLE_TYPE_DEFAULT) {
158 bubble_view_->SetMaxHeight(0); // Clear max height limit.
159 }
160
161 if (scoped_layer) {
162 // When transitioning from default view to detailed view, animate the new
163 // view (slide in from the right).
164 if (bubble_type == BUBBLE_TYPE_DETAILED) {
165 ui::Layer* new_layer = bubble_view_->layer();
166
167 // Make sure the new layer is stacked above the old layer during the
168 // animation.
169 new_layer->parent()->StackAbove(new_layer, scoped_layer.get());
170
171 gfx::Rect bounds = new_layer->bounds();
172 gfx::Transform transform;
173 transform.Translate(bounds.width(), 0.0);
174 new_layer->SetTransform(transform);
175 {
176 ui::ScopedLayerAnimationSettings settings(new_layer->GetAnimator());
177 settings.AddObserver(
178 new AnimationObserverDeleteLayer(scoped_layer.release()));
179 settings.SetTransitionDuration(
180 base::TimeDelta::FromMilliseconds(kSwipeDelayMS));
181 settings.SetTweenType(gfx::Tween::EASE_OUT);
182 new_layer->SetTransform(gfx::Transform());
183 }
184 }
185 }
186 }
187
188 void SystemTrayBubble::InitView(views::View* anchor,
189 LoginStatus login_status,
190 TrayBubbleView::InitParams* init_params) {
191 DCHECK(anchor);
192 DCHECK(!bubble_view_);
193
194 if (bubble_type_ == BUBBLE_TYPE_DETAILED &&
195 init_params->max_height < GetDetailedBubbleMaxHeight()) {
196 init_params->max_height = GetDetailedBubbleMaxHeight();
197 }
198
199 bubble_view_ = TrayBubbleView::Create(anchor, tray_, init_params);
200 UpdateBottomPadding();
201 bubble_view_->set_adjust_if_offscreen(false);
202 CreateItemViews(login_status);
203
204 if (bubble_view_->CanActivate()) {
205 bubble_view_->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
206 }
207 }
208
209 void SystemTrayBubble::FocusDefaultIfNeeded() {
210 views::FocusManager* manager = bubble_view_->GetFocusManager();
211 if (!manager || manager->GetFocusedView())
212 return;
213
214 views::View* view =
215 manager->GetNextFocusableView(nullptr, nullptr, false, false);
216 // TODO(oshima): RequestFocus calls View::OnFocus even if the widget
217 // is not active (crbug.com/621791). Remove this check once the bug
218 // is fixed.
219 if (bubble_view_->GetWidget()->IsActive()) {
220 view->RequestFocus();
221 } else {
222 manager->SetStoredFocusView(view);
223 }
224 }
225
226 void SystemTrayBubble::DestroyItemViews() {
227 for (std::vector<ash::SystemTrayItem*>::iterator it = items_.begin();
228 it != items_.end(); ++it) {
229 switch (bubble_type_) {
230 case BUBBLE_TYPE_DEFAULT:
231 (*it)->DestroyDefaultView();
232 break;
233 case BUBBLE_TYPE_DETAILED:
234 (*it)->DestroyDetailedView();
235 break;
236 }
237 }
238 }
239
240 void SystemTrayBubble::BubbleViewDestroyed() {
241 bubble_view_ = nullptr;
242 }
243
244 void SystemTrayBubble::StartAutoCloseTimer(int seconds) {
245 autoclose_.Stop();
246 autoclose_delay_ = seconds;
247 if (autoclose_delay_) {
248 autoclose_.Start(FROM_HERE, base::TimeDelta::FromSeconds(autoclose_delay_),
249 this, &SystemTrayBubble::Close);
250 }
251 }
252
253 void SystemTrayBubble::StopAutoCloseTimer() {
254 autoclose_.Stop();
255 }
256
257 void SystemTrayBubble::RestartAutoCloseTimer() {
258 if (autoclose_delay_)
259 StartAutoCloseTimer(autoclose_delay_);
260 }
261
262 void SystemTrayBubble::Close() {
263 tray_->HideBubbleWithView(bubble_view());
264 }
265
266 void SystemTrayBubble::SetVisible(bool is_visible) {
267 if (!bubble_view_)
268 return;
269 views::Widget* bubble_widget = bubble_view_->GetWidget();
270 if (is_visible)
271 bubble_widget->Show();
272 else
273 bubble_widget->Hide();
274 }
275
276 bool SystemTrayBubble::IsVisible() {
277 return bubble_view() && bubble_view()->GetWidget()->IsVisible();
278 }
279
280 bool SystemTrayBubble::ShouldShowShelf() const {
281 for (std::vector<ash::SystemTrayItem*>::const_iterator it = items_.begin();
282 it != items_.end(); ++it) {
283 if ((*it)->ShouldShowShelf())
284 return true;
285 }
286 return false;
287 }
288
289 void SystemTrayBubble::RecordVisibleRowMetrics() {
290 if (bubble_type_ != BUBBLE_TYPE_DEFAULT)
291 return;
292
293 for (const std::pair<SystemTrayItem::UmaType, views::View*>& pair :
294 tray_item_view_map_) {
295 if (pair.second->visible() &&
296 pair.first != SystemTrayItem::UMA_NOT_RECORDED) {
297 UMA_HISTOGRAM_ENUMERATION("Ash.SystemMenu.DefaultView.VisibleRows",
298 pair.first, SystemTrayItem::UMA_COUNT);
299 }
300 }
301 }
302
303 void SystemTrayBubble::UpdateBottomPadding() {
304 if (bubble_type_ == BUBBLE_TYPE_DEFAULT &&
305 MaterialDesignController::IsSystemTrayMenuMaterial()) {
306 bubble_view_->SetBottomPadding(kDefaultViewBottomPadding);
307 } else {
308 bubble_view_->SetBottomPadding(0);
309 }
310 }
311
312 void SystemTrayBubble::CreateItemViews(LoginStatus login_status) {
313 tray_item_view_map_.clear();
314
315 // If a system modal dialog is present, create the same tray as
316 // in locked state.
317 if (WmShell::Get()->IsSystemModalWindowOpen() &&
318 login_status != LoginStatus::NOT_LOGGED_IN) {
319 login_status = LoginStatus::LOCKED;
320 }
321
322 std::vector<TrayPopupItemContainer*> item_containers;
323 views::View* focus_view = nullptr;
324 const bool is_default_bubble = bubble_type_ == BUBBLE_TYPE_DEFAULT;
325 for (size_t i = 0; i < items_.size(); ++i) {
326 views::View* item_view = nullptr;
327 switch (bubble_type_) {
328 case BUBBLE_TYPE_DEFAULT:
329 item_view = items_[i]->CreateDefaultView(login_status);
330 if (items_[i]->restore_focus())
331 focus_view = item_view;
332 break;
333 case BUBBLE_TYPE_DETAILED:
334 item_view = items_[i]->CreateDetailedView(login_status);
335 break;
336 }
337 if (item_view) {
338 TrayPopupItemContainer* tray_popup_item_container =
339 new TrayPopupItemContainer(
340 item_view,
341 is_default_bubble &&
342 !MaterialDesignController::IsSystemTrayMenuMaterial());
343 bubble_view_->AddChildView(tray_popup_item_container);
344 item_containers.push_back(tray_popup_item_container);
345 tray_item_view_map_[items_[i]->uma_type()] = tray_popup_item_container;
346 }
347 }
348
349 if (!MaterialDesignController::IsSystemTrayMenuMaterial()) {
350 // For default view, draw bottom border for each item, except the last
351 // 2 items, which are the bottom header row and the one just above it.
352 if (is_default_bubble) {
353 const int last_item_with_border =
354 static_cast<int>(item_containers.size()) - 2;
355 for (int i = 0; i < last_item_with_border; ++i) {
356 item_containers.at(i)->SetBorder(
357 views::CreateSolidSidedBorder(0, 0, 1, 0, kBorderLightColor));
358 }
359 }
360 }
361
362 if (focus_view) {
363 tray_->ActivateBubble();
364 focus_view->RequestFocus();
365 }
366 }
367
368 } // namespace ash
OLDNEW
« no previous file with comments | « ash/common/system/tray/system_tray_bubble.h ('k') | ash/common/system/tray/system_tray_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698