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

Side by Side Diff: ui/views/bubble/tray_bubble_view.cc

Issue 2491033006: Adjust positioning of cros tray bubbles. (Closed)
Patch Set: rebase Created 4 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
« no previous file with comments | « ui/views/bubble/tray_bubble_view.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/bubble/tray_bubble_view.h" 5 #include "ui/views/bubble/tray_bubble_view.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/macros.h" 9 #include "base/macros.h"
10 #include "third_party/skia/include/core/SkCanvas.h" 10 #include "third_party/skia/include/core/SkCanvas.h"
11 #include "third_party/skia/include/core/SkColor.h" 11 #include "third_party/skia/include/core/SkColor.h"
12 #include "third_party/skia/include/core/SkPaint.h" 12 #include "third_party/skia/include/core/SkPaint.h"
13 #include "third_party/skia/include/core/SkPath.h" 13 #include "third_party/skia/include/core/SkPath.h"
14 #include "third_party/skia/include/effects/SkBlurImageFilter.h" 14 #include "third_party/skia/include/effects/SkBlurImageFilter.h"
15 #include "ui/accessibility/ax_node_data.h" 15 #include "ui/accessibility/ax_node_data.h"
16 #include "ui/aura/window.h" 16 #include "ui/aura/window.h"
17 #include "ui/compositor/layer.h" 17 #include "ui/compositor/layer.h"
18 #include "ui/compositor/layer_delegate.h" 18 #include "ui/compositor/layer_delegate.h"
19 #include "ui/compositor/paint_recorder.h" 19 #include "ui/compositor/paint_recorder.h"
20 #include "ui/events/event.h" 20 #include "ui/events/event.h"
21 #include "ui/gfx/canvas.h" 21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/color_palette.h"
22 #include "ui/gfx/geometry/insets.h" 23 #include "ui/gfx/geometry/insets.h"
23 #include "ui/gfx/geometry/rect.h" 24 #include "ui/gfx/geometry/rect.h"
24 #include "ui/gfx/path.h" 25 #include "ui/gfx/path.h"
25 #include "ui/gfx/skia_util.h" 26 #include "ui/gfx/skia_util.h"
26 #include "ui/views/bubble/bubble_frame_view.h" 27 #include "ui/views/bubble/bubble_frame_view.h"
27 #include "ui/views/bubble/bubble_window_targeter.h" 28 #include "ui/views/bubble/bubble_window_targeter.h"
28 #include "ui/views/layout/box_layout.h" 29 #include "ui/views/layout/box_layout.h"
29 #include "ui/views/widget/widget.h" 30 #include "ui/views/widget/widget.h"
30 31
32 namespace views {
33
31 namespace { 34 namespace {
32 35
33 // Inset the arrow a bit from the edge.
34 const int kArrowMinOffset = 20;
35 const int kBubbleSpacing = 20;
36
37 // The new theme adjusts the menus / bubbles to be flush with the shelf when
38 // there is no bubble. These are the offsets which need to be applied.
39 const int kArrowOffsetTopBottom = 4;
40 const int kArrowOffsetLeft = 9;
41 const int kArrowOffsetRight = -5;
42 const int kOffsetLeftRightForTopBottomOrientation = 5;
43
44 // The sampling time for mouse position changes in ms - which is roughly a frame 36 // The sampling time for mouse position changes in ms - which is roughly a frame
45 // time. 37 // time.
46 const int kFrameTimeInMS = 30; 38 const int kFrameTimeInMS = 30;
39
40 BubbleBorder::Arrow GetArrowAlignment(
41 TrayBubbleView::AnchorAlignment alignment) {
42 if (alignment == TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM) {
43 return base::i18n::IsRTL() ? BubbleBorder::BOTTOM_LEFT
44 : BubbleBorder::BOTTOM_RIGHT;
45 }
46 if (alignment == TrayBubbleView::ANCHOR_ALIGNMENT_LEFT)
47 return BubbleBorder::LEFT_BOTTOM;
48 return BubbleBorder::RIGHT_BOTTOM;
49 }
50
47 } // namespace 51 } // namespace
48 52
49 namespace views {
50
51 namespace internal { 53 namespace internal {
52 54
53 // Detects any mouse movement. This is needed to detect mouse movements by the 55 // Detects any mouse movement. This is needed to detect mouse movements by the
54 // user over the bubble if the bubble got created underneath the cursor. 56 // user over the bubble if the bubble got created underneath the cursor.
55 class MouseMoveDetectorHost : public MouseWatcherHost { 57 class MouseMoveDetectorHost : public MouseWatcherHost {
56 public: 58 public:
57 MouseMoveDetectorHost(); 59 MouseMoveDetectorHost();
58 ~MouseMoveDetectorHost() override; 60 ~MouseMoveDetectorHost() override;
59 61
60 bool Contains(const gfx::Point& screen_point, MouseEventType type) override; 62 bool Contains(const gfx::Point& screen_point, MouseEventType type) override;
61 63
62 private: 64 private:
63 DISALLOW_COPY_AND_ASSIGN(MouseMoveDetectorHost); 65 DISALLOW_COPY_AND_ASSIGN(MouseMoveDetectorHost);
64 }; 66 };
65 67
66 MouseMoveDetectorHost::MouseMoveDetectorHost() { 68 MouseMoveDetectorHost::MouseMoveDetectorHost() {
67 } 69 }
68 70
69 MouseMoveDetectorHost::~MouseMoveDetectorHost() { 71 MouseMoveDetectorHost::~MouseMoveDetectorHost() {
70 } 72 }
71 73
72 bool MouseMoveDetectorHost::Contains(const gfx::Point& screen_point, 74 bool MouseMoveDetectorHost::Contains(const gfx::Point& screen_point,
73 MouseEventType type) { 75 MouseEventType type) {
74 return false; 76 return false;
75 } 77 }
76 78
77 // Custom border for TrayBubbleView. Contains special logic for GetBounds()
78 // to stack bubbles with no arrows correctly. Also calculates the arrow offset.
79 class TrayBubbleBorder : public BubbleBorder {
80 public:
81 TrayBubbleBorder(View* owner,
82 View* anchor,
83 TrayBubbleView::InitParams params)
84 : BubbleBorder(params.arrow, params.shadow, params.arrow_color),
85 owner_(owner),
86 anchor_(anchor),
87 tray_arrow_offset_(params.arrow_offset),
88 first_item_has_no_margin_(params.first_item_has_no_margin) {
89 set_alignment(params.arrow_alignment);
90 set_background_color(params.arrow_color);
91 set_paint_arrow(params.arrow_paint_type);
92 }
93
94 ~TrayBubbleBorder() override {}
95
96 // Overridden from BubbleBorder.
97 // Sets the bubble on top of the anchor when it has no arrow.
98 gfx::Rect GetBounds(const gfx::Rect& position_relative_to,
99 const gfx::Size& contents_size) const override {
100 if (has_arrow(arrow())) {
101 gfx::Rect rect =
102 BubbleBorder::GetBounds(position_relative_to, contents_size);
103 if (first_item_has_no_margin_) {
104 if (arrow() == BubbleBorder::BOTTOM_RIGHT ||
105 arrow() == BubbleBorder::BOTTOM_LEFT) {
106 rect.set_y(rect.y() + kArrowOffsetTopBottom);
107 int rtl_factor = base::i18n::IsRTL() ? -1 : 1;
108 rect.set_x(rect.x() +
109 rtl_factor * kOffsetLeftRightForTopBottomOrientation);
110 } else if (arrow() == BubbleBorder::LEFT_BOTTOM) {
111 rect.set_x(rect.x() + kArrowOffsetLeft);
112 } else if (arrow() == BubbleBorder::RIGHT_BOTTOM) {
113 rect.set_x(rect.x() + kArrowOffsetRight);
114 }
115 }
116 return rect;
117 }
118
119 gfx::Size border_size(contents_size);
120 gfx::Insets insets = GetInsets();
121 border_size.Enlarge(insets.width(), insets.height());
122 const int x = position_relative_to.x() +
123 position_relative_to.width() / 2 - border_size.width() / 2;
124 // Position the bubble on top of the anchor.
125 const int y = position_relative_to.y() - border_size.height() +
126 insets.height() - kBubbleSpacing;
127 return gfx::Rect(x, y, border_size.width(), border_size.height());
128 }
129
130 void UpdateArrowOffset() {
131 int arrow_offset = 0;
132 if (arrow() == BubbleBorder::BOTTOM_RIGHT ||
133 arrow() == BubbleBorder::BOTTOM_LEFT) {
134 // Note: tray_arrow_offset_ is relative to the anchor widget.
135 if (tray_arrow_offset_ ==
136 TrayBubbleView::InitParams::kArrowDefaultOffset) {
137 arrow_offset = kArrowMinOffset;
138 } else {
139 const int width = owner_->GetWidget()->GetContentsView()->width();
140 gfx::Point pt(tray_arrow_offset_, 0);
141 View::ConvertPointToScreen(anchor_->GetWidget()->GetRootView(), &pt);
142 View::ConvertPointFromScreen(owner_->GetWidget()->GetRootView(), &pt);
143 arrow_offset = pt.x();
144 if (arrow() == BubbleBorder::BOTTOM_RIGHT)
145 arrow_offset = width - arrow_offset;
146 arrow_offset = std::max(arrow_offset, kArrowMinOffset);
147 }
148 } else {
149 if (tray_arrow_offset_ ==
150 TrayBubbleView::InitParams::kArrowDefaultOffset) {
151 arrow_offset = kArrowMinOffset;
152 } else {
153 gfx::Point pt(0, tray_arrow_offset_);
154 View::ConvertPointToScreen(anchor_->GetWidget()->GetRootView(), &pt);
155 View::ConvertPointFromScreen(owner_->GetWidget()->GetRootView(), &pt);
156 arrow_offset = pt.y();
157 arrow_offset = std::max(arrow_offset, kArrowMinOffset);
158 }
159 }
160 set_arrow_offset(arrow_offset);
161 }
162
163 private:
164 View* owner_;
165 View* anchor_;
166 const int tray_arrow_offset_;
167
168 // If true the first item should not get any additional spacing against the
169 // anchor (without the bubble tip the bubble should be flush to the shelf).
170 const bool first_item_has_no_margin_;
171
172 DISALLOW_COPY_AND_ASSIGN(TrayBubbleBorder);
173 };
174
175 // This mask layer clips the bubble's content so that it does not overwrite the 79 // This mask layer clips the bubble's content so that it does not overwrite the
176 // rounded bubble corners. 80 // rounded bubble corners.
177 // TODO(miket): This does not work on Windows. Implement layer masking or 81 // TODO(miket): This does not work on Windows. Implement layer masking or
178 // alternate solutions if the TrayBubbleView is needed there in the future. 82 // alternate solutions if the TrayBubbleView is needed there in the future.
179 class TrayBubbleContentMask : public ui::LayerDelegate { 83 class TrayBubbleContentMask : public ui::LayerDelegate {
180 public: 84 public:
181 explicit TrayBubbleContentMask(int corner_radius); 85 explicit TrayBubbleContentMask(int corner_radius);
182 ~TrayBubbleContentMask() override; 86 ~TrayBubbleContentMask() override;
183 87
184 ui::Layer* layer() { return &layer_; } 88 ui::Layer* layer() { return &layer_; }
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 } 156 }
253 } 157 }
254 158
255 TrayBubbleView* bubble_view_; 159 TrayBubbleView* bubble_view_;
256 160
257 DISALLOW_COPY_AND_ASSIGN(BottomAlignedBoxLayout); 161 DISALLOW_COPY_AND_ASSIGN(BottomAlignedBoxLayout);
258 }; 162 };
259 163
260 } // namespace internal 164 } // namespace internal
261 165
262 using internal::TrayBubbleBorder;
263 using internal::TrayBubbleContentMask; 166 using internal::TrayBubbleContentMask;
264 using internal::BottomAlignedBoxLayout; 167 using internal::BottomAlignedBoxLayout;
265 168
266 // static 169 TrayBubbleView::InitParams::InitParams(AnchorAlignment anchor_alignment,
267 const int TrayBubbleView::InitParams::kArrowDefaultOffset = -1;
268
269 TrayBubbleView::InitParams::InitParams(AnchorType anchor_type,
270 AnchorAlignment anchor_alignment,
271 int min_width, 170 int min_width,
272 int max_width) 171 int max_width)
273 : anchor_type(anchor_type), 172 : anchor_alignment(anchor_alignment),
274 anchor_alignment(anchor_alignment),
275 min_width(min_width), 173 min_width(min_width),
276 max_width(max_width), 174 max_width(max_width),
277 max_height(0), 175 max_height(0),
278 can_activate(false), 176 can_activate(false),
279 close_on_deactivate(true), 177 close_on_deactivate(true),
280 arrow_color(SK_ColorBLACK), 178 bg_color(gfx::kPlaceholderColor) {}
281 first_item_has_no_margin(false),
282 arrow(BubbleBorder::NONE),
283 arrow_offset(kArrowDefaultOffset),
284 arrow_paint_type(BubbleBorder::PAINT_NORMAL),
285 shadow(BubbleBorder::BIG_SHADOW),
286 arrow_alignment(BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE) {
287 }
288 179
289 TrayBubbleView::InitParams::InitParams(const InitParams& other) = default; 180 TrayBubbleView::InitParams::InitParams(const InitParams& other) = default;
290 181
291 // static 182 // static
292 TrayBubbleView* TrayBubbleView::Create(View* anchor, 183 TrayBubbleView* TrayBubbleView::Create(View* anchor,
293 Delegate* delegate, 184 Delegate* delegate,
294 InitParams* init_params) { 185 InitParams* init_params) {
295 DCHECK(anchor);
296 // Set arrow here so that it can be passed to the BubbleView constructor.
297 if (init_params->anchor_type == ANCHOR_TYPE_TRAY) {
298 if (init_params->anchor_alignment == ANCHOR_ALIGNMENT_BOTTOM) {
299 init_params->arrow = base::i18n::IsRTL() ?
300 BubbleBorder::BOTTOM_LEFT : BubbleBorder::BOTTOM_RIGHT;
301 } else if (init_params->anchor_alignment == ANCHOR_ALIGNMENT_TOP) {
302 init_params->arrow = BubbleBorder::TOP_LEFT;
303 } else if (init_params->anchor_alignment == ANCHOR_ALIGNMENT_LEFT) {
304 init_params->arrow = BubbleBorder::LEFT_BOTTOM;
305 } else {
306 init_params->arrow = BubbleBorder::RIGHT_BOTTOM;
307 }
308 } else {
309 init_params->arrow = BubbleBorder::NONE;
310 }
311
312 return new TrayBubbleView(anchor, delegate, *init_params); 186 return new TrayBubbleView(anchor, delegate, *init_params);
313 } 187 }
314 188
315 TrayBubbleView::TrayBubbleView(View* anchor, 189 TrayBubbleView::TrayBubbleView(View* anchor,
316 Delegate* delegate, 190 Delegate* delegate,
317 const InitParams& init_params) 191 const InitParams& init_params)
318 : BubbleDialogDelegateView(anchor, init_params.arrow), 192 : BubbleDialogDelegateView(anchor,
193 GetArrowAlignment(init_params.anchor_alignment)),
319 params_(init_params), 194 params_(init_params),
320 delegate_(delegate), 195 delegate_(delegate),
321 preferred_width_(init_params.min_width), 196 preferred_width_(init_params.min_width),
322 bubble_border_(new TrayBubbleBorder(this, GetAnchorView(), init_params)), 197 bubble_border_(new BubbleBorder(arrow(),
198 BubbleBorder::BIG_SHADOW,
199 init_params.bg_color)),
323 owned_bubble_border_(bubble_border_), 200 owned_bubble_border_(bubble_border_),
324 is_gesture_dragging_(false), 201 is_gesture_dragging_(false),
325 mouse_actively_entered_(false) { 202 mouse_actively_entered_(false) {
203 bubble_border_->set_alignment(BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
204 bubble_border_->set_paint_arrow(BubbleBorder::PAINT_NONE);
326 set_can_activate(params_.can_activate); 205 set_can_activate(params_.can_activate);
327 DCHECK(anchor_widget()); // Computed by BubbleDialogDelegateView(). 206 DCHECK(anchor_widget()); // Computed by BubbleDialogDelegateView().
328 set_notify_enter_exit_on_child(true); 207 set_notify_enter_exit_on_child(true);
329 set_close_on_deactivate(init_params.close_on_deactivate); 208 set_close_on_deactivate(init_params.close_on_deactivate);
330 set_margins(gfx::Insets()); 209 set_margins(gfx::Insets());
331 SetPaintToLayer(true); 210 SetPaintToLayer(true);
332 211
333 bubble_content_mask_.reset( 212 bubble_content_mask_.reset(
334 new TrayBubbleContentMask(bubble_border_->GetBorderCornerRadius())); 213 new TrayBubbleContentMask(bubble_border_->GetBorderCornerRadius()));
335 } 214 }
336 215
337 TrayBubbleView::~TrayBubbleView() { 216 TrayBubbleView::~TrayBubbleView() {
338 mouse_watcher_.reset(); 217 mouse_watcher_.reset();
339 // Inform host items (models) that their views are being destroyed. 218 // Inform host items (models) that their views are being destroyed.
340 if (delegate_) 219 if (delegate_)
341 delegate_->BubbleViewDestroyed(); 220 delegate_->BubbleViewDestroyed();
342 } 221 }
343 222
344 void TrayBubbleView::InitializeAndShowBubble() { 223 void TrayBubbleView::InitializeAndShowBubble() {
345 // Must occur after call to BubbleDialogDelegateView::CreateBubble().
346 SetAlignment(params_.arrow_alignment);
347 bubble_border_->UpdateArrowOffset();
348
349 layer()->parent()->SetMaskLayer(bubble_content_mask_->layer()); 224 layer()->parent()->SetMaskLayer(bubble_content_mask_->layer());
350 225
351 GetWidget()->Show(); 226 GetWidget()->Show();
352 GetWidget()->GetNativeWindow()->SetEventTargeter( 227 GetWidget()->GetNativeWindow()->SetEventTargeter(
353 std::unique_ptr<ui::EventTargeter>(new BubbleWindowTargeter(this))); 228 std::unique_ptr<ui::EventTargeter>(new BubbleWindowTargeter(this)));
354 UpdateBubble(); 229 UpdateBubble();
355 } 230 }
356 231
357 void TrayBubbleView::UpdateBubble() { 232 void TrayBubbleView::UpdateBubble() {
358 if (GetWidget()) { 233 if (GetWidget()) {
(...skipping 11 matching lines...) Expand all
370 245
371 void TrayBubbleView::SetWidth(int width) { 246 void TrayBubbleView::SetWidth(int width) {
372 width = std::max(std::min(width, params_.max_width), params_.min_width); 247 width = std::max(std::min(width, params_.max_width), params_.min_width);
373 if (preferred_width_ == width) 248 if (preferred_width_ == width)
374 return; 249 return;
375 preferred_width_ = width; 250 preferred_width_ = width;
376 if (GetWidget()) 251 if (GetWidget())
377 SizeToContents(); 252 SizeToContents();
378 } 253 }
379 254
380 void TrayBubbleView::SetArrowPaintType(
381 views::BubbleBorder::ArrowPaintType paint_type) {
382 bubble_border_->set_paint_arrow(paint_type);
383 UpdateBubble();
384 }
385
386 gfx::Insets TrayBubbleView::GetBorderInsets() const { 255 gfx::Insets TrayBubbleView::GetBorderInsets() const {
387 return bubble_border_->GetInsets(); 256 return bubble_border_->GetInsets();
388 } 257 }
389 258
390 int TrayBubbleView::GetDialogButtons() const { 259 int TrayBubbleView::GetDialogButtons() const {
391 return ui::DIALOG_BUTTON_NONE; 260 return ui::DIALOG_BUTTON_NONE;
392 } 261 }
393 262
394 void TrayBubbleView::Init() { 263 void TrayBubbleView::Init() {
395 BoxLayout* layout = new BottomAlignedBoxLayout(this); 264 BoxLayout* layout = new BottomAlignedBoxLayout(this);
396 layout->SetDefaultFlex(1); 265 layout->SetDefaultFlex(1);
397 SetLayoutManager(layout); 266 SetLayoutManager(layout);
398 } 267 }
399 268
400 gfx::Rect TrayBubbleView::GetAnchorRect() const {
401 if (!delegate_)
402 return gfx::Rect();
403 return delegate_->GetAnchorRect(anchor_widget(),
404 params_.anchor_type,
405 params_.anchor_alignment);
406 }
407
408 void TrayBubbleView::OnBeforeBubbleWidgetInit(Widget::InitParams* params, 269 void TrayBubbleView::OnBeforeBubbleWidgetInit(Widget::InitParams* params,
409 Widget* bubble_widget) const { 270 Widget* bubble_widget) const {
410 if (delegate_) 271 if (delegate_)
411 delegate_->OnBeforeBubbleWidgetInit(anchor_widget(), bubble_widget, params); 272 delegate_->OnBeforeBubbleWidgetInit(anchor_widget(), bubble_widget, params);
412 } 273 }
413 274
414 NonClientFrameView* TrayBubbleView::CreateNonClientFrameView(Widget* widget) { 275 NonClientFrameView* TrayBubbleView::CreateNonClientFrameView(Widget* widget) {
415 BubbleFrameView* frame = static_cast<BubbleFrameView*>( 276 BubbleFrameView* frame = static_cast<BubbleFrameView*>(
416 BubbleDialogDelegateView::CreateNonClientFrameView(widget)); 277 BubbleDialogDelegateView::CreateNonClientFrameView(widget));
417 frame->SetBubbleBorder(std::move(owned_bubble_border_)); 278 frame->SetBubbleBorder(std::move(owned_bubble_border_));
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
510 371
511 void TrayBubbleView::ViewHierarchyChanged( 372 void TrayBubbleView::ViewHierarchyChanged(
512 const ViewHierarchyChangedDetails& details) { 373 const ViewHierarchyChangedDetails& details) {
513 if (details.is_add && details.child == this) { 374 if (details.is_add && details.child == this) {
514 details.parent->SetPaintToLayer(true); 375 details.parent->SetPaintToLayer(true);
515 details.parent->layer()->SetMasksToBounds(true); 376 details.parent->layer()->SetMasksToBounds(true);
516 } 377 }
517 } 378 }
518 379
519 } // namespace views 380 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/bubble/tray_bubble_view.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698