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

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

Issue 10378070: ash: Separate out SystemTrayBubble and related classes into a new file. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: merge Created 8 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « ash/system/tray/system_tray_bubble.h ('k') | ash/system/tray/tray_constants.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/system/tray/system_tray_bubble.h"
6
7 #include "ash/shell.h"
8 #include "ash/shell_window_ids.h"
9 #include "ash/system/tray/system_tray.h"
10 #include "ash/system/tray/system_tray_item.h"
11 #include "ash/system/tray/tray_constants.h"
12 #include "ash/wm/shelf_layout_manager.h"
13 #include "ash/wm/window_animations.h"
14 #include "base/message_loop.h"
15 #include "grit/ash_strings.h"
16 #include "third_party/skia/include/core/SkCanvas.h"
17 #include "third_party/skia/include/core/SkColor.h"
18 #include "third_party/skia/include/core/SkPaint.h"
19 #include "third_party/skia/include/core/SkPath.h"
20 #include "third_party/skia/include/effects/SkBlurImageFilter.h"
21 #include "ui/base/accessibility/accessible_view_state.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/compositor/layer.h"
24 #include "ui/gfx/canvas.h"
25 #include "ui/gfx/screen.h"
26 #include "ui/views/bubble/bubble_frame_view.h"
27 #include "ui/views/layout/box_layout.h"
28 #include "ui/views/layout/fill_layout.h"
29 #include "ui/views/view.h"
30
31 namespace ash {
32
33 namespace {
34
35 const int kShadowThickness = 4;
36
37 const int kLeftPadding = 4;
38 const int kBottomLineHeight = 1;
39
40 const int kArrowHeight = 10;
41 const int kArrowWidth = 20;
42 const int kArrowPaddingFromRight = 20;
43
44 const int kAnimationDurationForPopupMS = 200;
45
46 const SkColor kShadowColor = SkColorSetARGB(0xff, 0, 0, 0);
47
48 void DrawBlurredShadowAroundView(gfx::Canvas* canvas,
49 int top,
50 int bottom,
51 int width,
52 const gfx::Insets& inset) {
53 SkPath path;
54 path.incReserve(4);
55 path.moveTo(SkIntToScalar(inset.left() + kShadowThickness),
56 SkIntToScalar(top + kShadowThickness + 1));
57 path.lineTo(SkIntToScalar(inset.left() + kShadowThickness),
58 SkIntToScalar(bottom));
59 path.lineTo(SkIntToScalar(width),
60 SkIntToScalar(bottom));
61 path.lineTo(SkIntToScalar(width),
62 SkIntToScalar(top + kShadowThickness + 1));
63
64 SkPaint paint;
65 paint.setColor(kShadowColor);
66 paint.setStyle(SkPaint::kStroke_Style);
67 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
68 paint.setStrokeWidth(SkIntToScalar(3));
69 paint.setImageFilter(new SkBlurImageFilter(
70 SkIntToScalar(3), SkIntToScalar(3)))->unref();
71 canvas->sk_canvas()->drawPath(path, paint);
72 }
73
74 // A view with some special behaviour for tray items in the popup:
75 // - changes background color on hover.
76 class TrayPopupItemContainer : public views::View {
77 public:
78 explicit TrayPopupItemContainer(views::View* view) : hover_(false) {
79 set_notify_enter_exit_on_child(true);
80 set_border(view->border() ? views::Border::CreateEmptyBorder(0, 0, 0, 0) :
81 NULL);
82 SetLayoutManager(new views::FillLayout);
83 SetPaintToLayer(view->layer() != NULL);
84 if (view->layer())
85 SetFillsBoundsOpaquely(view->layer()->fills_bounds_opaquely());
86 AddChildView(view);
87 SetVisible(view->visible());
88 }
89
90 virtual ~TrayPopupItemContainer() {}
91
92 private:
93 // Overridden from views::View.
94 virtual void ChildVisibilityChanged(View* child) OVERRIDE {
95 if (visible() == child->visible())
96 return;
97 SetVisible(child->visible());
98 PreferredSizeChanged();
99 }
100
101 virtual void ChildPreferredSizeChanged(View* child) OVERRIDE {
102 PreferredSizeChanged();
103 }
104
105 virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE {
106 hover_ = true;
107 SchedulePaint();
108 }
109
110 virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE {
111 hover_ = false;
112 SchedulePaint();
113 }
114
115 virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE {
116 if (child_count() == 0)
117 return;
118
119 views::View* view = child_at(0);
120 if (!view->background()) {
121 canvas->FillRect(gfx::Rect(size()),
122 hover_ ? kHoverBackgroundColor : kBackgroundColor);
123 }
124 }
125
126 bool hover_;
127
128 DISALLOW_COPY_AND_ASSIGN(TrayPopupItemContainer);
129 };
130
131 class SystemTrayBubbleBackground : public views::Background {
132 public:
133 explicit SystemTrayBubbleBackground(views::View* owner)
134 : owner_(owner) {
135 }
136
137 virtual ~SystemTrayBubbleBackground() {}
138
139 private:
140 // Overridden from views::Background.
141 virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
142 views::View* last_view = NULL;
143 for (int i = 0; i < owner_->child_count(); i++) {
144 views::View* v = owner_->child_at(i);
145
146 if (!v->border()) {
147 canvas->DrawLine(gfx::Point(v->x(), v->y() - 1),
148 gfx::Point(v->x() + v->width(), v->y() - 1),
149 !last_view || last_view->border() ? kBorderDarkColor :
150 kBorderLightColor);
151 canvas->DrawLine(gfx::Point(v->x() - 1, v->y() - 1),
152 gfx::Point(v->x() - 1, v->y() + v->height() + 1),
153 kBorderDarkColor);
154 canvas->DrawLine(gfx::Point(v->x() + v->width(), v->y() - 1),
155 gfx::Point(v->x() + v->width(), v->y() + v->height() + 1),
156 kBorderDarkColor);
157 } else if (last_view && !last_view->border()) {
158 canvas->DrawLine(gfx::Point(v->x() - 1, v->y() - 1),
159 gfx::Point(v->x() + v->width() + 1, v->y() - 1),
160 kBorderDarkColor);
161 }
162
163 last_view = v;
164 }
165 }
166
167 views::View* owner_;
168
169 DISALLOW_COPY_AND_ASSIGN(SystemTrayBubbleBackground);
170 };
171
172 class SystemTrayBubbleBorder : public views::BubbleBorder {
173 public:
174 enum ArrowType {
175 ARROW_TYPE_NONE,
176 ARROW_TYPE_BOTTOM,
177 };
178
179 SystemTrayBubbleBorder(views::View* owner, ArrowType arrow_type)
180 : views::BubbleBorder(views::BubbleBorder::BOTTOM_RIGHT,
181 views::BubbleBorder::NO_SHADOW),
182 owner_(owner),
183 arrow_type_(arrow_type) {
184 set_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
185 }
186
187 virtual ~SystemTrayBubbleBorder() {}
188
189 private:
190 // Overridden from views::Border.
191 virtual void Paint(const views::View& view,
192 gfx::Canvas* canvas) const OVERRIDE {
193 views::View* first = NULL, *last = NULL;
194 gfx::Insets inset;
195 GetInsets(&inset);
196 for (int i = 0; i < owner_->child_count(); i++) {
197 views::View* v = owner_->child_at(i);
198 if (v->border()) {
199 if (first) {
200 DrawBlurredShadowAroundView(canvas, first->y(),
201 last->y() + last->height(), owner_->width(), inset);
202 first = NULL;
203 last = NULL;
204 }
205 continue;
206 }
207
208 if (!first)
209 first = v;
210 last = v;
211 }
212 if (first) {
213 DrawBlurredShadowAroundView(canvas, first->y(),
214 last->y() + last->height(), owner_->width(), inset);
215 }
216
217 // Draw the bottom line.
218 int y = owner_->height() + 1;
219 canvas->FillRect(gfx::Rect(kLeftPadding, y, owner_->width(),
220 kBottomLineHeight), kBorderDarkColor);
221
222 if (!Shell::GetInstance()->shelf()->IsVisible())
223 return;
224
225 // Draw the arrow.
226 if (arrow_type_ == ARROW_TYPE_BOTTOM) {
227 int left_base_x = base::i18n::IsRTL() ? kArrowWidth :
228 owner_->width() - kArrowPaddingFromRight - kArrowWidth;
229 int left_base_y = y;
230 int tip_x = left_base_x + kArrowWidth / 2;
231 int tip_y = left_base_y + kArrowHeight;
232 SkPath path;
233 path.incReserve(4);
234 path.moveTo(SkIntToScalar(left_base_x), SkIntToScalar(left_base_y));
235 path.lineTo(SkIntToScalar(tip_x), SkIntToScalar(tip_y));
236 path.lineTo(SkIntToScalar(left_base_x + kArrowWidth),
237 SkIntToScalar(left_base_y));
238
239 SkPaint paint;
240 paint.setStyle(SkPaint::kFill_Style);
241 paint.setColor(kBackgroundColor);
242 canvas->DrawPath(path, paint);
243
244 // Now draw the arrow border.
245 paint.setStyle(SkPaint::kStroke_Style);
246 paint.setColor(kBorderDarkColor);
247 canvas->DrawPath(path, paint);
248 }
249 }
250
251 views::View* owner_;
252 ArrowType arrow_type_;
253
254 DISALLOW_COPY_AND_ASSIGN(SystemTrayBubbleBorder);
255 };
256
257 } // namespace
258
259 namespace internal {
260
261 // SystemTrayBubbleView
262
263 SystemTrayBubbleView::SystemTrayBubbleView(views::View* anchor,
264 SystemTrayBubble* host,
265 bool can_activate)
266 : views::BubbleDelegateView(anchor, views::BubbleBorder::BOTTOM_RIGHT),
267 host_(host),
268 can_activate_(can_activate) {
269 set_margin(0);
270 set_parent_window(ash::Shell::GetInstance()->GetContainer(
271 ash::internal::kShellWindowId_SettingBubbleContainer));
272 set_notify_enter_exit_on_child(true);
273 }
274
275 SystemTrayBubbleView::~SystemTrayBubbleView() {
276 // Inform host items (models) that their views are being destroyed.
277 if (host_)
278 host_->DestroyItemViews();
279 }
280
281 void SystemTrayBubbleView::SetBubbleBorder(views::BubbleBorder* border) {
282 GetBubbleFrameView()->SetBubbleBorder(border);
283 }
284
285 void SystemTrayBubbleView::UpdateAnchor() {
286 SizeToContents();
287 GetWidget()->GetRootView()->SchedulePaint();
288 }
289
290 void SystemTrayBubbleView::Init() {
291 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 1, 1, 1));
292 set_background(new SystemTrayBubbleBackground(this));
293 }
294
295 gfx::Rect SystemTrayBubbleView::GetAnchorRect() {
296 gfx::Rect rect;
297 if (host_)
298 rect = host_->GetAnchorRect();
299 if (rect.IsEmpty()) {
300 rect = gfx::Screen::GetPrimaryMonitor().bounds();
301 rect = gfx::Rect(base::i18n::IsRTL() ? kPaddingFromRightEdgeOfScreen :
302 rect.width() - kPaddingFromRightEdgeOfScreen,
303 rect.height() - kPaddingFromBottomOfScreen,
304 0, 0);
305 }
306 return rect;
307 }
308
309 void SystemTrayBubbleView::ChildPreferredSizeChanged(View* child) {
310 SizeToContents();
311 }
312
313 void SystemTrayBubbleView::GetAccessibleState(ui::AccessibleViewState* state) {
314 if (can_activate_) {
315 state->role = ui::AccessibilityTypes::ROLE_WINDOW;
316 state->name = l10n_util::GetStringUTF16(
317 IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME);
318 }
319 }
320
321 bool SystemTrayBubbleView::CanActivate() const {
322 return can_activate_;
323 }
324
325 gfx::Size SystemTrayBubbleView::GetPreferredSize() {
326 gfx::Size size = views::BubbleDelegateView::GetPreferredSize();
327 return gfx::Size(kTrayPopupWidth, size.height());
328 }
329
330 void SystemTrayBubbleView::OnMouseEntered(const views::MouseEvent& event) {
331 if (host_)
332 host_->StopAutoCloseTimer();
333 }
334
335 void SystemTrayBubbleView::OnMouseExited(const views::MouseEvent& event) {
336 if (host_)
337 host_->RestartAutoCloseTimer();
338 }
339
340 // SystemTrayBubble
341
342 SystemTrayBubble::SystemTrayBubble(
343 ash::SystemTray* tray,
344 const std::vector<ash::SystemTrayItem*>& items,
345 BubbleType bubble_type)
346 : tray_(tray),
347 bubble_view_(NULL),
348 bubble_widget_(NULL),
349 items_(items),
350 bubble_type_(bubble_type),
351 anchor_type_(ANCHOR_TYPE_TRAY),
352 autoclose_delay_(0) {
353 }
354
355 SystemTrayBubble::~SystemTrayBubble() {
356 // The bubble may be closing without having been hidden first. So it may still
357 // be in the message-loop's observer list.
358 MessageLoopForUI::current()->RemoveObserver(this);
359
360 DestroyItemViews();
361 // Reset the host pointer in bubble_view_ in case its destruction is deferred.
362 if (bubble_view_)
363 bubble_view_->reset_host();
364 if (bubble_widget_) {
365 bubble_widget_->RemoveObserver(this);
366 // This triggers the destruction of bubble_view_.
367 bubble_widget_->Close();
368 }
369 }
370
371 void SystemTrayBubble::InitView(views::View* anchor,
372 AnchorType anchor_type,
373 bool can_activate,
374 ash::user::LoginStatus login_status) {
375 DCHECK(bubble_view_ == NULL);
376 anchor_type_ = anchor_type;
377 bubble_view_ = new SystemTrayBubbleView(anchor, this, can_activate);
378
379 for (std::vector<ash::SystemTrayItem*>::iterator it = items_.begin();
380 it != items_.end();
381 ++it) {
382 views::View* view = NULL;
383 switch (bubble_type_) {
384 case BUBBLE_TYPE_DEFAULT:
385 view = (*it)->CreateDefaultView(login_status);
386 break;
387 case BUBBLE_TYPE_DETAILED:
388 view = (*it)->CreateDetailedView(login_status);
389 break;
390 case BUBBLE_TYPE_NOTIFICATION:
391 view = (*it)->CreateNotificationView(login_status);
392 break;
393 }
394 if (view)
395 bubble_view_->AddChildView(new TrayPopupItemContainer(view));
396 }
397
398 DCHECK(bubble_widget_ == NULL);
399 bubble_widget_ = views::BubbleDelegateView::CreateBubble(bubble_view_);
400
401 // Must occur after call to CreateBubble()
402 bubble_view_->SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
403 bubble_widget_->non_client_view()->frame_view()->set_background(NULL);
404 SystemTrayBubbleBorder::ArrowType arrow_type;
405 if (anchor_type_ == ANCHOR_TYPE_TRAY)
406 arrow_type = SystemTrayBubbleBorder::ARROW_TYPE_BOTTOM;
407 else
408 arrow_type = SystemTrayBubbleBorder::ARROW_TYPE_NONE;
409 bubble_view_->SetBubbleBorder(
410 new SystemTrayBubbleBorder(bubble_view_, arrow_type));
411
412 bubble_widget_->AddObserver(this);
413
414 // Setup animation.
415 ash::SetWindowVisibilityAnimationType(
416 bubble_widget_->GetNativeWindow(),
417 ash::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
418 ash::SetWindowVisibilityAnimationTransition(
419 bubble_widget_->GetNativeWindow(),
420 ash::ANIMATE_BOTH);
421 ash::SetWindowVisibilityAnimationDuration(
422 bubble_widget_->GetNativeWindow(),
423 base::TimeDelta::FromMilliseconds(kAnimationDurationForPopupMS));
424
425 bubble_view_->Show();
426 }
427
428 gfx::Rect SystemTrayBubble::GetAnchorRect() const {
429 gfx::Rect rect;
430 views::Widget* widget = bubble_view()->anchor_widget();
431 if (widget->IsVisible()) {
432 rect = widget->GetWindowScreenBounds();
433 if (anchor_type_ == ANCHOR_TYPE_TRAY) {
434 rect.Inset(
435 base::i18n::IsRTL() ? kPaddingFromRightEdgeOfScreen : 0,
436 0,
437 base::i18n::IsRTL() ? 0 : kPaddingFromRightEdgeOfScreen,
438 kPaddingFromBottomOfScreen);
439 } else if (anchor_type_ == ANCHOR_TYPE_BUBBLE) {
440 rect.Inset(
441 base::i18n::IsRTL() ? kShadowThickness - 1 : 0,
442 0,
443 base::i18n::IsRTL() ? 0 : kShadowThickness - 1,
444 0);
445 }
446 }
447 return rect;
448 }
449
450 void SystemTrayBubble::DestroyItemViews() {
451 for (std::vector<ash::SystemTrayItem*>::iterator it = items_.begin();
452 it != items_.end();
453 ++it) {
454 switch (bubble_type_) {
455 case BUBBLE_TYPE_DEFAULT:
456 (*it)->DestroyDefaultView();
457 break;
458 case BUBBLE_TYPE_DETAILED:
459 (*it)->DestroyDetailedView();
460 break;
461 case BUBBLE_TYPE_NOTIFICATION:
462 (*it)->DestroyNotificationView();
463 break;
464 }
465 }
466 }
467
468 void SystemTrayBubble::StartAutoCloseTimer(int seconds) {
469 autoclose_.Stop();
470 autoclose_delay_ = seconds;
471 if (autoclose_delay_) {
472 autoclose_.Start(FROM_HERE,
473 base::TimeDelta::FromSeconds(autoclose_delay_),
474 this, &SystemTrayBubble::Close);
475 }
476 }
477
478 void SystemTrayBubble::StopAutoCloseTimer() {
479 autoclose_.Stop();
480 }
481
482 void SystemTrayBubble::RestartAutoCloseTimer() {
483 if (autoclose_delay_)
484 StartAutoCloseTimer(autoclose_delay_);
485 }
486
487 void SystemTrayBubble::Close() {
488 if (bubble_widget_)
489 bubble_widget_->Close();
490 }
491
492 base::EventStatus SystemTrayBubble::WillProcessEvent(
493 const base::NativeEvent& event) {
494 // Check if the user clicked outside of the bubble and close it if they did.
495 if (bubble_type_ != BUBBLE_TYPE_NOTIFICATION &&
496 ui::EventTypeFromNative(event) == ui::ET_MOUSE_PRESSED) {
497 gfx::Point cursor_in_view = ui::EventLocationFromNative(event);
498 views::View::ConvertPointFromScreen(bubble_view_, &cursor_in_view);
499 if (!bubble_view_->HitTest(cursor_in_view)) {
500 bubble_widget_->Close();
501 }
502 }
503 return base::EVENT_CONTINUE;
504 }
505
506 void SystemTrayBubble::DidProcessEvent(const base::NativeEvent& event) {
507 }
508
509 void SystemTrayBubble::OnWidgetClosing(views::Widget* widget) {
510 CHECK_EQ(bubble_widget_, widget);
511 MessageLoopForUI::current()->RemoveObserver(this);
512 bubble_widget_ = NULL;
513 tray_->RemoveBubble(this);
514 }
515
516 void SystemTrayBubble::OnWidgetVisibilityChanged(views::Widget* widget,
517 bool visible) {
518 if (!visible)
519 MessageLoopForUI::current()->RemoveObserver(this);
520 else
521 MessageLoopForUI::current()->AddObserver(this);
522 }
523
524 } // namespace internal
525 } // namespace ash
OLDNEW
« no previous file with comments | « ash/system/tray/system_tray_bubble.h ('k') | ash/system/tray/tray_constants.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698