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

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

Issue 10905311: Consolidate bubble border code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: remove USE_AURA ifdef for kBigShadowImages and kSmalleShadowImages Created 8 years, 3 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 | « ui/views/bubble/bubble_border_2.h ('k') | ui/views/bubble/bubble_border_unittest.cc » ('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 "ui/views/bubble/bubble_border_2.h"
6
7 #include <algorithm> // for std::max
8
9 #include "base/logging.h"
10 #include "ui/gfx/canvas.h"
11 #include "ui/gfx/path.h"
12 #include "ui/gfx/rect.h"
13 #include "ui/gfx/screen.h"
14 #include "ui/gfx/skia_util.h"
15
16 namespace {
17
18 // Bubble border corner radius.
19 const int kCornerRadius = 2;
20
21 // Arrow width and height.
22 const int kArrowHeight = 10;
23 const int kArrowWidth = 20;
24
25 const int kBorderSize = 1;
26 const SkColor kBorderColor = SkColorSetARGB(0x26, 0, 0, 0);
27 const SkColor kBackgroundColor = SK_ColorWHITE;
28
29 const int kShadowOffsetX = 0;
30 const int kShadowOffsetY = 5;
31 const double kShadowBlur = 30;
32 const SkColor kShadowColor = SkColorSetARGB(0x72, 0, 0, 0);
33
34 // Builds a bubble shape for given |bounds|.
35 void BuildShape(const gfx::Rect& bounds,
36 views::BubbleBorder::ArrowLocation arrow_location,
37 SkScalar arrow_offset,
38 SkScalar padding,
39 SkPath* path,
40 int corner_radius_int,
41 int arrow_height_int,
42 int arrow_width_int) {
43 const SkScalar corner_radius = SkIntToScalar(corner_radius_int);
44
45 const SkScalar left = SkIntToScalar(bounds.x()) + padding;
46 const SkScalar top = SkIntToScalar(bounds.y()) + padding;
47 const SkScalar right = SkIntToScalar(bounds.right()) - padding;
48 const SkScalar bottom = SkIntToScalar(bounds.bottom()) - padding;
49
50 const SkScalar center_x = SkIntToScalar((bounds.x() + bounds.right()) / 2);
51 const SkScalar center_y = SkIntToScalar((bounds.y() + bounds.bottom()) / 2);
52
53 const SkScalar half_arrow_width =
54 (SkIntToScalar(arrow_width_int) - padding) / 2;
55 const SkScalar arrow_height = SkIntToScalar(arrow_height_int) - padding;
56
57 path->reset();
58 path->incReserve(12);
59
60 switch (arrow_location) {
61 case views::BubbleBorder::TOP_LEFT:
62 case views::BubbleBorder::TOP_RIGHT:
63 path->moveTo(center_x, bottom);
64 path->arcTo(right, bottom, right, center_y, corner_radius);
65 path->arcTo(right, top, center_x - half_arrow_width, top,
66 corner_radius);
67 path->lineTo(center_x + arrow_offset + half_arrow_width, top);
68 path->lineTo(center_x + arrow_offset, top - arrow_height);
69 path->lineTo(center_x + arrow_offset - half_arrow_width, top);
70 path->arcTo(left, top, left, center_y, corner_radius);
71 path->arcTo(left, bottom, center_x, bottom, corner_radius);
72 break;
73 case views::BubbleBorder::BOTTOM_LEFT:
74 case views::BubbleBorder::BOTTOM_RIGHT:
75 path->moveTo(center_x, top);
76 path->arcTo(left, top, left, center_y, corner_radius);
77 path->arcTo(left, bottom, center_x - half_arrow_width, bottom,
78 corner_radius);
79 path->lineTo(center_x + arrow_offset - half_arrow_width, bottom);
80 path->lineTo(center_x + arrow_offset, bottom + arrow_height);
81 path->lineTo(center_x + arrow_offset + half_arrow_width, bottom);
82 path->arcTo(right, bottom, right, center_y, corner_radius);
83 path->arcTo(right, top, center_x, top, corner_radius);
84 break;
85 case views::BubbleBorder::LEFT_TOP:
86 case views::BubbleBorder::LEFT_BOTTOM:
87 path->moveTo(right, center_y);
88 path->arcTo(right, top, center_x, top, corner_radius);
89 path->arcTo(left, top, left, center_y + arrow_offset - half_arrow_width,
90 corner_radius);
91 path->lineTo(left, center_y + arrow_offset - half_arrow_width);
92 path->lineTo(left - arrow_height, center_y + arrow_offset);
93 path->lineTo(left, center_y + arrow_offset + half_arrow_width);
94 path->arcTo(left, bottom, center_x, bottom, corner_radius);
95 path->arcTo(right, bottom, right, center_y, corner_radius);
96 break;
97 case views::BubbleBorder::RIGHT_TOP:
98 case views::BubbleBorder::RIGHT_BOTTOM:
99 path->moveTo(left, center_y);
100 path->arcTo(left, bottom, center_x, bottom, corner_radius);
101 path->arcTo(right, bottom,
102 right, center_y + arrow_offset + half_arrow_width,
103 corner_radius);
104 path->lineTo(right, center_y + arrow_offset + half_arrow_width);
105 path->lineTo(right + arrow_height, center_y + arrow_offset);
106 path->lineTo(right, center_y + arrow_offset - half_arrow_width);
107 path->arcTo(right, top, center_x, top, corner_radius);
108 path->arcTo(left, top, left, center_y, corner_radius);
109 break;
110 default:
111 // No arrows.
112 path->addRoundRect(gfx::RectToSkRect(bounds),
113 corner_radius,
114 corner_radius);
115 break;
116 }
117
118 path->close();
119 }
120
121 } // namespace
122
123 namespace views {
124
125 BubbleBorder2::BubbleBorder2(ArrowLocation arrow_location)
126 : BubbleBorder(arrow_location, views::BubbleBorder::NO_SHADOW),
127 corner_radius_(kCornerRadius),
128 border_size_(kBorderSize),
129 arrow_height_(kArrowHeight),
130 arrow_width_(kArrowWidth),
131 background_color_(kBackgroundColor),
132 border_color_(kBorderColor) {
133 SetShadow(gfx::ShadowValue(gfx::Point(kShadowOffsetX, kShadowOffsetY),
134 kShadowBlur, kShadowColor));
135 }
136
137 BubbleBorder2::~BubbleBorder2() {}
138
139 gfx::Rect BubbleBorder2::ComputeOffsetAndUpdateBubbleRect(
140 gfx::Rect bubble_rect,
141 const gfx::Rect& anchor_view_rect) {
142 offset_ = gfx::Point();
143
144 gfx::Rect monitor_rect = gfx::Screen::GetDisplayNearestPoint(
145 anchor_view_rect.CenterPoint()).bounds();
146 if (monitor_rect.IsEmpty() || monitor_rect.Contains(bubble_rect))
147 return bubble_rect;
148
149 gfx::Point offset;
150
151 if (has_arrow(arrow_location())) {
152 if (is_arrow_on_horizontal(arrow_location())) {
153 if (bubble_rect.x() < monitor_rect.x())
154 offset.set_x(monitor_rect.x() - bubble_rect.x());
155 else if (bubble_rect.right() > monitor_rect.right())
156 offset.set_x(monitor_rect.right() - bubble_rect.right());
157 } else {
158 if (bubble_rect.y() < monitor_rect.y())
159 offset.set_y(monitor_rect.y() - bubble_rect.y());
160 else if (bubble_rect.bottom() > monitor_rect.bottom())
161 offset.set_y(monitor_rect.bottom() - bubble_rect.bottom());
162 }
163 }
164
165 bubble_rect.Offset(offset);
166 set_offset(offset);
167
168 return bubble_rect;
169 }
170
171 void BubbleBorder2::GetMask(const gfx::Rect& bounds,
172 gfx::Path* mask) const {
173 gfx::Insets insets;
174 GetInsets(&insets);
175
176 gfx::Rect content_bounds(bounds);
177 content_bounds.Inset(insets);
178
179 BuildShape(content_bounds,
180 arrow_location(),
181 SkIntToScalar(GetArrowOffset()),
182 SkIntToScalar(kBorderSize),
183 mask,
184 corner_radius_,
185 arrow_height_,
186 arrow_width_);
187 }
188
189 void BubbleBorder2::SetShadow(gfx::ShadowValue shadow) {
190 shadows_.clear();
191 shadows_.push_back(shadow);
192 }
193
194 int BubbleBorder2::GetBorderThickness() const {
195 return border_size_;
196 }
197
198 void BubbleBorder2::PaintBackground(gfx::Canvas* canvas,
199 const gfx::Rect& bounds) const {
200 canvas->FillRect(bounds, background_color_);
201 }
202
203 int BubbleBorder2::GetArrowOffset() const {
204 if (has_arrow(arrow_location())) {
205 if (is_arrow_on_horizontal(arrow_location())) {
206 // Picks x offset and moves bubble arrow in the opposite direction.
207 // i.e. If bubble bounds is moved to right (positive offset), we need to
208 // move arrow to left so that it points to the same position.
209 return -offset_.x();
210 } else {
211 // Picks y offset and moves bubble arrow in the opposite direction.
212 return -offset_.y();
213 }
214 }
215
216 // Other style does not have an arrow, so return 0.
217 return 0;
218 }
219
220 void BubbleBorder2::GetInsets(gfx::Insets* insets) const {
221 // Negate to change from outer margin to inner padding.
222 gfx::Insets shadow_padding(-gfx::ShadowValue::GetMargin(shadows_));
223
224 if (arrow_location() == views::BubbleBorder::TOP_LEFT ||
225 arrow_location() == views::BubbleBorder::TOP_RIGHT) {
226 // Arrow at top.
227 insets->Set(shadow_padding.top() + arrow_height_,
228 shadow_padding.left(),
229 shadow_padding.bottom(),
230 shadow_padding.right());
231 } else if (arrow_location() == views::BubbleBorder::BOTTOM_LEFT ||
232 arrow_location() == views::BubbleBorder::BOTTOM_RIGHT) {
233 // Arrow at bottom.
234 insets->Set(shadow_padding.top(),
235 shadow_padding.left(),
236 shadow_padding.bottom() + arrow_height_,
237 shadow_padding.right());
238 } else if (arrow_location() == views::BubbleBorder::LEFT_TOP ||
239 arrow_location() == views::BubbleBorder::LEFT_BOTTOM) {
240 // Arrow on left.
241 insets->Set(shadow_padding.top(),
242 shadow_padding.left() + arrow_height_,
243 shadow_padding.bottom(),
244 shadow_padding.right());
245 } else if (arrow_location() == views::BubbleBorder::RIGHT_TOP ||
246 arrow_location() == views::BubbleBorder::RIGHT_BOTTOM) {
247 // Arrow on right.
248 insets->Set(shadow_padding.top(),
249 shadow_padding.left(),
250 shadow_padding.bottom(),
251 shadow_padding.right() + arrow_height_);
252 }
253 }
254
255 gfx::Rect BubbleBorder2::GetBounds(const gfx::Rect& position_relative_to,
256 const gfx::Size& contents_size) const {
257 gfx::Size border_size(contents_size);
258 gfx::Insets insets;
259 GetInsets(&insets);
260 border_size.Enlarge(insets.width(), insets.height());
261
262 // Negate to change from outer margin to inner padding.
263 gfx::Insets shadow_padding(-gfx::ShadowValue::GetMargin(shadows_));
264
265 // Anchor center that arrow aligns with.
266 const int anchor_center_x =
267 (position_relative_to.x() + position_relative_to.right()) / 2;
268 const int anchor_center_y =
269 (position_relative_to.y() + position_relative_to.bottom()) / 2;
270
271 // Arrow position relative to top-left of bubble. |arrow_tip_x| is used for
272 // arrow at the top or bottom and |arrow_tip_y| is used for arrow on left or
273 // right. The 1px offset for |arrow_tip_y| is needed because the app list grid
274 // icon start at a different position (1px earlier) compared with bottom
275 // launcher bar.
276 // TODO(xiyuan): Remove 1px offset when app list icon image asset is updated.
277 int arrow_tip_x = insets.left() + contents_size.width() / 2 +
278 GetArrowOffset();
279 int arrow_tip_y = insets.top() + contents_size.height() / 2 +
280 GetArrowOffset() + 1;
281
282 if (arrow_location() == views::BubbleBorder::TOP_LEFT ||
283 arrow_location() == views::BubbleBorder::TOP_RIGHT) {
284 // Arrow at top.
285 return gfx::Rect(
286 gfx::Point(anchor_center_x - arrow_tip_x,
287 position_relative_to.bottom() - shadow_padding.top()),
288 border_size);
289 } else if (arrow_location() == views::BubbleBorder::BOTTOM_LEFT ||
290 arrow_location() == views::BubbleBorder::BOTTOM_RIGHT) {
291 // Arrow at bottom.
292 return gfx::Rect(
293 gfx::Point(anchor_center_x - arrow_tip_x,
294 position_relative_to.y() - border_size.height() +
295 shadow_padding.bottom()),
296 border_size);
297 } else if (arrow_location() == views::BubbleBorder::LEFT_TOP ||
298 arrow_location() == views::BubbleBorder::LEFT_BOTTOM) {
299 // Arrow on left.
300 return gfx::Rect(
301 gfx::Point(position_relative_to.right() - shadow_padding.left(),
302 anchor_center_y - arrow_tip_y),
303 border_size);
304 } else if (arrow_location() == views::BubbleBorder::RIGHT_TOP ||
305 arrow_location() == views::BubbleBorder::RIGHT_BOTTOM) {
306 // Arrow on right.
307 return gfx::Rect(
308 gfx::Point(position_relative_to.x() - border_size.width() +
309 shadow_padding.right(),
310 anchor_center_y - arrow_tip_y),
311 border_size);
312 }
313
314 // No arrow bubble, center align with anchor.
315 return position_relative_to.Center(border_size);
316 }
317
318 void BubbleBorder2::GetInsetsForArrowLocation(gfx::Insets* insets,
319 ArrowLocation arrow_loc) const {
320 int top = border_size_;
321 int bottom = border_size_;
322 int left = border_size_;
323 int right = border_size_;
324 switch (arrow_loc) {
325 case TOP_LEFT:
326 case TOP_RIGHT:
327 top = std::max(top, arrow_height_);
328 break;
329
330 case BOTTOM_LEFT:
331 case BOTTOM_RIGHT:
332 bottom = std::max(bottom, arrow_height_);
333 break;
334
335 case LEFT_TOP:
336 case LEFT_BOTTOM:
337 left = std::max(left, arrow_height_);
338 break;
339
340 case RIGHT_TOP:
341 case RIGHT_BOTTOM:
342 right = std::max(right, arrow_height_);
343 break;
344
345 case NONE:
346 case FLOAT:
347 // Nothing to do.
348 break;
349 }
350 insets->Set(top, left, bottom, right);
351 }
352
353 void BubbleBorder2::Paint(const views::View& view, gfx::Canvas* canvas) const {
354 gfx::Insets insets;
355 GetInsets(&insets);
356
357 gfx::Rect content_bounds = view.bounds();
358 content_bounds.Inset(insets);
359
360 SkPath path;
361 // Pads with 0.5 pixel since anti alias is used.
362 BuildShape(content_bounds,
363 arrow_location(),
364 SkIntToScalar(GetArrowOffset()),
365 SkDoubleToScalar(0.5),
366 &path,
367 corner_radius_,
368 arrow_height_,
369 arrow_width_);
370
371 // Draw border and shadow. Note fill is needed to generate enough shadow.
372 SkPaint paint;
373 paint.setAntiAlias(true);
374 paint.setStyle(SkPaint::kStrokeAndFill_Style);
375 paint.setStrokeWidth(SkIntToScalar(border_size_));
376 paint.setColor(border_color_);
377 SkSafeUnref(paint.setLooper(gfx::CreateShadowDrawLooper(shadows_)));
378 canvas->DrawPath(path, paint);
379
380 // Pads with |border_size_| pixels to leave space for border lines.
381 BuildShape(content_bounds,
382 arrow_location(),
383 SkIntToScalar(GetArrowOffset()),
384 SkIntToScalar(border_size_),
385 &path,
386 corner_radius_,
387 arrow_height_,
388 arrow_width_);
389 canvas->Save();
390 canvas->ClipPath(path);
391
392 // Use full bounds so that arrow is also painted.
393 const gfx::Rect& bounds = view.bounds();
394 PaintBackground(canvas, bounds);
395
396 canvas->Restore();
397 }
398
399 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/bubble/bubble_border_2.h ('k') | ui/views/bubble/bubble_border_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698