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

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

Issue 11229022: Move ash/system/web_notification message_center to ui/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase again. Created 8 years, 1 month 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/tray_bubble_view.h ('k') | ash/system/tray/tray_bubble_wrapper.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/tray_bubble_view.h"
6
7 #include "ash/root_window_controller.h"
8 #include "ash/wm/property_util.h"
9 #include "ash/wm/shelf_layout_manager.h"
10 #include "third_party/skia/include/core/SkCanvas.h"
11 #include "third_party/skia/include/core/SkColor.h"
12 #include "third_party/skia/include/core/SkPaint.h"
13 #include "third_party/skia/include/core/SkPath.h"
14 #include "third_party/skia/include/effects/SkBlurImageFilter.h"
15 #include "ui/aura/root_window.h"
16 #include "ui/aura/window.h"
17 #include "ui/base/accessibility/accessible_view_state.h"
18 #include "ui/base/events/event.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/gfx/canvas.h"
21 #include "ui/gfx/insets.h"
22 #include "ui/gfx/path.h"
23 #include "ui/gfx/skia_util.h"
24 #include "ui/views/bubble/bubble_frame_view.h"
25 #include "ui/views/layout/box_layout.h"
26 #include "ui/views/widget/widget.h"
27
28 namespace {
29
30 // Inset the arrow a bit from the edge.
31 const int kArrowMinOffset = 20;
32 const int kBubbleSpacing = 20;
33
34 } // namespace
35
36 namespace message_center {
37
38 namespace internal {
39
40 // Custom border for TrayBubbleView. Contains special logic for GetBounds()
41 // to stack bubbles with no arrows correctly. Also calculates the arrow offset.
42 class TrayBubbleBorder : public views::BubbleBorder {
43 public:
44 TrayBubbleBorder(views::View* owner,
45 views::View* anchor,
46 TrayBubbleView::InitParams params)
47 : views::BubbleBorder(params.arrow_location, params.shadow),
48 owner_(owner),
49 anchor_(anchor),
50 tray_arrow_offset_(params.arrow_offset) {
51 set_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
52 set_background_color(params.arrow_color);
53 }
54
55 virtual ~TrayBubbleBorder() {}
56
57 // Overridden from views::BubbleBorder.
58 // Override views::BubbleBorder to set the bubble on top of the anchor when
59 // it has no arrow.
60 virtual gfx::Rect GetBounds(const gfx::Rect& position_relative_to,
61 const gfx::Size& contents_size) const OVERRIDE {
62 if (arrow_location() != NONE) {
63 return views::BubbleBorder::GetBounds(position_relative_to,
64 contents_size);
65 }
66
67 gfx::Size border_size(contents_size);
68 gfx::Insets insets;
69 GetInsets(&insets);
70 border_size.Enlarge(insets.width(), insets.height());
71
72 const int x = position_relative_to.x() +
73 position_relative_to.width() / 2 - border_size.width() / 2;
74 // Position the bubble on top of the anchor.
75 const int y = position_relative_to.y() - border_size.height()
76 + insets.height() - kBubbleSpacing;
77 return gfx::Rect(x, y, border_size.width(), border_size.height());
78 }
79
80 void UpdateArrowOffset() {
81 int arrow_offset = 0;
82 if (arrow_location() == views::BubbleBorder::BOTTOM_RIGHT ||
83 arrow_location() == views::BubbleBorder::BOTTOM_LEFT) {
84 // Note: tray_arrow_offset_ is relative to the anchor widget.
85 if (tray_arrow_offset_ ==
86 TrayBubbleView::InitParams::kArrowDefaultOffset) {
87 arrow_offset = kArrowMinOffset;
88 } else {
89 const int width = owner_->GetWidget()->GetContentsView()->width();
90 gfx::Point pt(tray_arrow_offset_, 0);
91 views::View::ConvertPointToScreen(
92 anchor_->GetWidget()->GetRootView(), &pt);
93 views::View::ConvertPointFromScreen(
94 owner_->GetWidget()->GetRootView(), &pt);
95 arrow_offset = pt.x();
96 if (arrow_location() == views::BubbleBorder::BOTTOM_RIGHT)
97 arrow_offset = width - arrow_offset;
98 arrow_offset = std::max(arrow_offset, kArrowMinOffset);
99 }
100 } else {
101 if (tray_arrow_offset_ ==
102 TrayBubbleView::InitParams::kArrowDefaultOffset) {
103 arrow_offset = kArrowMinOffset;
104 } else {
105 gfx::Point pt(0, tray_arrow_offset_);
106 views::View::ConvertPointToScreen(
107 anchor_->GetWidget()->GetRootView(), &pt);
108 views::View::ConvertPointFromScreen(
109 owner_->GetWidget()->GetRootView(), &pt);
110 arrow_offset = pt.y();
111 arrow_offset = std::max(arrow_offset, kArrowMinOffset);
112 }
113 }
114 set_arrow_offset(arrow_offset);
115 }
116
117 private:
118 views::View* owner_;
119 views::View* anchor_;
120 const int tray_arrow_offset_;
121
122 DISALLOW_COPY_AND_ASSIGN(TrayBubbleBorder);
123 };
124
125 // Custom background for TrayBubbleView. Fills in the top and bottom margins
126 // with appropriate background colors without overwriting the rounded corners.
127 class TrayBubbleBackground : public views::Background {
128 public:
129 explicit TrayBubbleBackground(views::BubbleBorder* border,
130 SkColor top_color,
131 SkColor bottom_color)
132 : border_(border),
133 top_color_(top_color),
134 bottom_color_(bottom_color),
135 radius_(SkIntToScalar(border->GetBorderCornerRadius() - 1)) {
136 }
137
138 SkScalar radius() const { return radius_; }
139
140 // Overridden from Background:
141 virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
142 canvas->Save();
143
144 // Set a clip mask for the bubble's rounded corners.
145 gfx::Rect bounds(view->GetContentsBounds());
146 const int border_thickness(border_->GetBorderThickness());
147 bounds.Inset(-border_thickness, -border_thickness);
148 SkPath path;
149 path.addRoundRect(gfx::RectToSkRect(bounds), radius_, radius_);
150 canvas->ClipPath(path);
151
152 // Paint the header and footer (assumes the bubble contents fill in their
153 // own backgrounds).
154 SkPaint paint;
155 paint.setStyle(SkPaint::kFill_Style);
156
157 gfx::Rect top_rect(bounds);
158 top_rect.set_height(radius_);
159 paint.setColor(top_color_);
160 canvas->DrawRect(top_rect, paint);
161
162 gfx::Rect bottom_rect(bounds);
163 bottom_rect.set_y(bounds.y() + (bounds.height() - radius_));
164 bottom_rect.set_height(radius_);
165 paint.setColor(bottom_color_);
166 canvas->DrawRect(bottom_rect, paint);
167
168 canvas->Restore();
169 }
170
171 private:
172 views::BubbleBorder* border_;
173 SkColor top_color_;
174 SkColor bottom_color_;
175 SkScalar radius_;
176
177 DISALLOW_COPY_AND_ASSIGN(TrayBubbleBackground);
178 };
179
180 // Custom layout for the bubble-view. Does the default box-layout if there is
181 // enough height. Otherwise, makes sure the bottom rows are visible.
182 class BottomAlignedBoxLayout : public views::BoxLayout {
183 public:
184 explicit BottomAlignedBoxLayout(TrayBubbleView* bubble_view)
185 : views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0),
186 bubble_view_(bubble_view) {
187 }
188
189 virtual ~BottomAlignedBoxLayout() {}
190
191 private:
192 virtual void Layout(views::View* host) OVERRIDE {
193 if (host->height() >= host->GetPreferredSize().height() ||
194 !bubble_view_->is_gesture_dragging()) {
195 views::BoxLayout::Layout(host);
196 return;
197 }
198
199 int consumed_height = 0;
200 for (int i = host->child_count() - 1;
201 i >= 0 && consumed_height < host->height(); --i) {
202 views::View* child = host->child_at(i);
203 if (!child->visible())
204 continue;
205 gfx::Size size = child->GetPreferredSize();
206 child->SetBounds(0, host->height() - consumed_height - size.height(),
207 host->width(), size.height());
208 consumed_height += size.height();
209 }
210 }
211
212 TrayBubbleView* bubble_view_;
213
214 DISALLOW_COPY_AND_ASSIGN(BottomAlignedBoxLayout);
215 };
216
217 } // namespace internal
218
219 using internal::TrayBubbleBorder;
220 using internal::TrayBubbleBackground;
221 using internal::BottomAlignedBoxLayout;
222
223 // static
224 const int TrayBubbleView::InitParams::kArrowDefaultOffset = -1;
225
226 TrayBubbleView::InitParams::InitParams(AnchorType anchor_type,
227 AnchorAlignment anchor_alignment,
228 int bubble_width)
229 : anchor_type(anchor_type),
230 anchor_alignment(anchor_alignment),
231 bubble_width(bubble_width),
232 max_height(0),
233 can_activate(false),
234 close_on_deactivate(true),
235 top_color(SK_ColorBLACK),
236 arrow_color(SK_ColorBLACK),
237 arrow_location(views::BubbleBorder::NONE),
238 arrow_offset(kArrowDefaultOffset),
239 shadow(views::BubbleBorder::BIG_SHADOW) {
240 }
241
242 // static
243 TrayBubbleView* TrayBubbleView::Create(aura::Window* parent_window,
244 views::View* anchor,
245 Delegate* delegate,
246 InitParams* init_params) {
247 // Set arrow_location here so that it can be passed correctly to the
248 // BubbleView constructor.
249 if (init_params->anchor_type == ANCHOR_TYPE_TRAY) {
250 if (init_params->anchor_alignment == ANCHOR_ALIGNMENT_BOTTOM) {
251 init_params->arrow_location = base::i18n::IsRTL() ?
252 views::BubbleBorder::BOTTOM_LEFT : views::BubbleBorder::BOTTOM_RIGHT;
253 } else if (init_params->anchor_alignment == ANCHOR_ALIGNMENT_LEFT) {
254 init_params->arrow_location = views::BubbleBorder::LEFT_BOTTOM;
255 } else {
256 init_params->arrow_location = views::BubbleBorder::RIGHT_BOTTOM;
257 }
258 } else {
259 init_params->arrow_location = views::BubbleBorder::NONE;
260 }
261
262 return new TrayBubbleView(parent_window, anchor, delegate, *init_params);
263 }
264
265 TrayBubbleView::TrayBubbleView(aura::Window* parent_window,
266 views::View* anchor,
267 Delegate* delegate,
268 const InitParams& init_params)
269 : views::BubbleDelegateView(anchor, init_params.arrow_location),
270 params_(init_params),
271 delegate_(delegate),
272 bubble_border_(NULL),
273 bubble_background_(NULL),
274 is_gesture_dragging_(false) {
275 set_parent_window(parent_window);
276 set_notify_enter_exit_on_child(true);
277 set_close_on_deactivate(init_params.close_on_deactivate);
278 SetPaintToLayer(true);
279 SetFillsBoundsOpaquely(true);
280
281 bubble_border_ = new TrayBubbleBorder(this, anchor_view(), params_);
282
283 bubble_background_ = new TrayBubbleBackground(
284 bubble_border_, init_params.top_color, init_params.arrow_color);
285
286 // Inset the view on the top and bottom by the corner radius to avoid drawing
287 // over the the bubble corners.
288 const int radius = bubble_background_->radius();
289 set_margins(gfx::Insets(radius, 0, radius, 0));
290 }
291
292 TrayBubbleView::~TrayBubbleView() {
293 // Inform host items (models) that their views are being destroyed.
294 if (delegate_)
295 delegate_->BubbleViewDestroyed();
296 }
297
298 void TrayBubbleView::InitializeAndShowBubble() {
299 // Must occur after call to BubbleDelegateView::CreateBubble().
300 SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
301 bubble_border_->UpdateArrowOffset();
302
303 Show();
304 UpdateBubble();
305 }
306
307 void TrayBubbleView::UpdateBubble() {
308 SizeToContents();
309 GetWidget()->GetRootView()->SchedulePaint();
310 aura::RootWindow* root_window = GetWidget()->GetNativeView()->GetRootWindow();
311 ash::internal::ShelfLayoutManager* shelf =
312 ash::GetRootWindowController(root_window)->shelf();
313 bubble_border_->set_paint_arrow(shelf->IsVisible());
314 }
315
316 void TrayBubbleView::SetMaxHeight(int height) {
317 params_.max_height = height;
318 if (GetWidget())
319 SizeToContents();
320 }
321
322 void TrayBubbleView::GetBorderInsets(gfx::Insets* insets) const {
323 bubble_border_->GetInsets(insets);
324 }
325
326 void TrayBubbleView::Init() {
327 views::BoxLayout* layout = new BottomAlignedBoxLayout(this);
328 layout->set_spread_blank_space(true);
329 SetLayoutManager(layout);
330 }
331
332 gfx::Rect TrayBubbleView::GetAnchorRect() {
333 if (!delegate_)
334 return gfx::Rect();
335 return delegate_->GetAnchorRect(anchor_widget(),
336 params_.anchor_type,
337 params_.anchor_alignment);
338 }
339
340 bool TrayBubbleView::CanActivate() const {
341 return params_.can_activate;
342 }
343
344 // Overridden to create BubbleFrameView and set a custom border and background.
345 views::NonClientFrameView* TrayBubbleView::CreateNonClientFrameView(
346 views::Widget* widget) {
347 views::BubbleFrameView* bubble_frame_view =
348 new views::BubbleFrameView(margins(), bubble_border_);
349 bubble_frame_view->set_background(bubble_background_);
350 return bubble_frame_view;
351 }
352
353 bool TrayBubbleView::WidgetHasHitTestMask() const {
354 return true;
355 }
356
357 void TrayBubbleView::GetWidgetHitTestMask(gfx::Path* mask) const {
358 DCHECK(mask);
359 mask->addRect(gfx::RectToSkRect(GetBubbleFrameView()->GetContentsBounds()));
360 }
361
362 gfx::Size TrayBubbleView::GetPreferredSize() {
363 gfx::Size size = views::BubbleDelegateView::GetPreferredSize();
364 int height = size.height();
365 if (params_.max_height != 0 && height > params_.max_height)
366 height = params_.max_height;
367 return gfx::Size(params_.bubble_width, height);
368 }
369
370 void TrayBubbleView::OnMouseEntered(const ui::MouseEvent& event) {
371 if (delegate_)
372 delegate_->OnMouseEnteredView();
373 }
374
375 void TrayBubbleView::OnMouseExited(const ui::MouseEvent& event) {
376 if (delegate_)
377 delegate_->OnMouseExitedView();
378 }
379
380 void TrayBubbleView::GetAccessibleState(ui::AccessibleViewState* state) {
381 if (params_.can_activate) {
382 state->role = ui::AccessibilityTypes::ROLE_WINDOW;
383 state->name = delegate_->GetAccessibleNameForBubble();
384 }
385 }
386
387 void TrayBubbleView::ChildPreferredSizeChanged(View* child) {
388 SizeToContents();
389 }
390
391 void TrayBubbleView::ViewHierarchyChanged(bool is_add,
392 views::View* parent,
393 views::View* child) {
394 if (is_add && child == this) {
395 parent->SetPaintToLayer(true);
396 parent->SetFillsBoundsOpaquely(true);
397 parent->layer()->SetMasksToBounds(true);
398 }
399 }
400
401 } // namespace message_center
OLDNEW
« no previous file with comments | « ash/system/tray/tray_bubble_view.h ('k') | ash/system/tray/tray_bubble_wrapper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698