OLD | NEW |
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/bubble_border.h" | 5 #include "ui/views/bubble/bubble_border.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "third_party/skia/include/core/SkDrawLooper.h" |
10 #include "third_party/skia/include/core/SkPaint.h" | 11 #include "third_party/skia/include/core/SkPaint.h" |
11 #include "third_party/skia/include/core/SkPath.h" | 12 #include "third_party/skia/include/core/SkPath.h" |
| 13 #include "ui/base/material_design/material_design_controller.h" |
12 #include "ui/base/resource/resource_bundle.h" | 14 #include "ui/base/resource/resource_bundle.h" |
13 #include "ui/gfx/canvas.h" | 15 #include "ui/gfx/canvas.h" |
14 #include "ui/gfx/geometry/rect.h" | 16 #include "ui/gfx/geometry/rect.h" |
15 #include "ui/gfx/path.h" | 17 #include "ui/gfx/path.h" |
| 18 #include "ui/gfx/scoped_canvas.h" |
16 #include "ui/gfx/skia_util.h" | 19 #include "ui/gfx/skia_util.h" |
17 #include "ui/resources/grit/ui_resources.h" | 20 #include "ui/resources/grit/ui_resources.h" |
18 #include "ui/views/painter.h" | 21 #include "ui/views/painter.h" |
19 #include "ui/views/resources/grit/views_resources.h" | 22 #include "ui/views/resources/grit/views_resources.h" |
20 #include "ui/views/view.h" | 23 #include "ui/views/view.h" |
21 | 24 |
22 namespace views { | 25 namespace views { |
23 | 26 |
24 namespace internal { | 27 namespace internal { |
25 | 28 |
(...skipping 24 matching lines...) Expand all Loading... |
50 arrow_thickness = top_arrow.height(); | 53 arrow_thickness = top_arrow.height(); |
51 } | 54 } |
52 } | 55 } |
53 | 56 |
54 BorderImages::~BorderImages() {} | 57 BorderImages::~BorderImages() {} |
55 | 58 |
56 } // namespace internal | 59 } // namespace internal |
57 | 60 |
58 namespace { | 61 namespace { |
59 | 62 |
| 63 // Blur and offset values for the two shadows drawn around each dialog. The |
| 64 // values are all in dip. |
| 65 const int kSmallShadowVerticalOffset = 2; |
| 66 const int kSmallShadowBlur = 4; |
| 67 const SkColor kSmallShadowColor = SkColorSetA(SK_ColorBLACK, 0x33); |
| 68 |
| 69 const int kLargeShadowVerticalOffset = 2; |
| 70 const int kLargeShadowBlur = 6; |
| 71 const SkColor kLargeShadowColor = SkColorSetA(SK_ColorBLACK, 0x1A); |
| 72 |
| 73 bool UseMd() { |
| 74 return ui::MaterialDesignController::IsSecondaryUiMaterial(); |
| 75 } |
| 76 |
60 // Bubble border and arrow image resource ids. They don't use the IMAGE_GRID | 77 // Bubble border and arrow image resource ids. They don't use the IMAGE_GRID |
61 // macro because there is no center image. | 78 // macro because there is no center image. |
62 const int kNoShadowImages[] = { | 79 const int kNoShadowImages[] = { |
63 IDR_BUBBLE_TL, IDR_BUBBLE_T, IDR_BUBBLE_TR, | 80 IDR_BUBBLE_TL, IDR_BUBBLE_T, IDR_BUBBLE_TR, |
64 IDR_BUBBLE_L, 0, IDR_BUBBLE_R, | 81 IDR_BUBBLE_L, 0, IDR_BUBBLE_R, |
65 IDR_BUBBLE_BL, IDR_BUBBLE_B, IDR_BUBBLE_BR }; | 82 IDR_BUBBLE_BL, IDR_BUBBLE_B, IDR_BUBBLE_BR }; |
66 const int kNoShadowArrows[] = { | 83 const int kNoShadowArrows[] = { |
67 IDR_BUBBLE_L_ARROW, IDR_BUBBLE_T_ARROW, | 84 IDR_BUBBLE_L_ARROW, IDR_BUBBLE_T_ARROW, |
68 IDR_BUBBLE_R_ARROW, IDR_BUBBLE_B_ARROW, }; | 85 IDR_BUBBLE_R_ARROW, IDR_BUBBLE_B_ARROW, }; |
69 | 86 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
136 } // namespace | 153 } // namespace |
137 | 154 |
138 const int BubbleBorder::kStroke = 1; | 155 const int BubbleBorder::kStroke = 1; |
139 | 156 |
140 BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color) | 157 BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color) |
141 : arrow_(arrow), | 158 : arrow_(arrow), |
142 arrow_offset_(0), | 159 arrow_offset_(0), |
143 arrow_paint_type_(PAINT_NORMAL), | 160 arrow_paint_type_(PAINT_NORMAL), |
144 alignment_(ALIGN_ARROW_TO_MID_ANCHOR), | 161 alignment_(ALIGN_ARROW_TO_MID_ANCHOR), |
145 shadow_(shadow), | 162 shadow_(shadow), |
| 163 images_(nullptr), |
146 background_color_(color), | 164 background_color_(color), |
147 use_theme_background_color_(false) { | 165 use_theme_background_color_(false) { |
148 #if defined(OS_MACOSX) | 166 #if defined(OS_MACOSX) |
149 // On Mac, use the NO_ASSETS bubble border. WindowServer on Mac is able to | 167 // On Mac, use the NO_ASSETS bubble border. WindowServer on Mac is able to |
150 // generate drop shadows for dialogs, hence we don't use raster shadows. | 168 // generate drop shadows for dialogs, hence we don't use raster shadows. |
151 shadow_ = NO_ASSETS; | 169 shadow_ = NO_ASSETS; |
152 #endif // OS_MACOSX | 170 #endif // OS_MACOSX |
153 DCHECK(shadow_ < SHADOW_COUNT); | 171 DCHECK(shadow_ < SHADOW_COUNT); |
154 images_ = GetBorderImages(shadow_); | 172 if (UseMd()) { |
| 173 // Harmony bubbles don't use arrows. |
| 174 alignment_ = ALIGN_EDGE_TO_ANCHOR_EDGE; |
| 175 arrow_paint_type_ = PAINT_NONE; |
| 176 } else { |
| 177 images_ = GetBorderImages(shadow_); |
| 178 } |
155 } | 179 } |
156 | 180 |
157 BubbleBorder::~BubbleBorder() {} | 181 BubbleBorder::~BubbleBorder() {} |
158 | 182 |
| 183 void BubbleBorder::set_paint_arrow(ArrowPaintType value) { |
| 184 if (UseMd()) |
| 185 return; |
| 186 arrow_paint_type_ = value; |
| 187 } |
| 188 |
159 gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, | 189 gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, |
160 const gfx::Size& contents_size) const { | 190 const gfx::Size& contents_size) const { |
161 int x = anchor_rect.x(); | 191 int x = anchor_rect.x(); |
162 int y = anchor_rect.y(); | 192 int y = anchor_rect.y(); |
163 int w = anchor_rect.width(); | 193 int w = anchor_rect.width(); |
164 int h = anchor_rect.height(); | 194 int h = anchor_rect.height(); |
165 const gfx::Size size(GetSizeForContentsSize(contents_size)); | 195 const gfx::Size size(GetSizeForContentsSize(contents_size)); |
166 const int arrow_offset = GetArrowOffset(size); | 196 const int arrow_offset = GetArrowOffset(size); |
167 // |arrow_shift| is necessary to visually align the tip of the bubble arrow | 197 // |arrow_shift| is necessary to visually align the tip of the bubble arrow |
168 // with the anchor point. This shift is an inverse of the shadow thickness. | 198 // with the anchor point. This shift is an inverse of the shadow thickness. |
169 int arrow_shift = | 199 int arrow_shift = UseMd() ? 0 : |
170 images_->arrow_interior_thickness + kStroke - images_->arrow_thickness; | 200 images_->arrow_interior_thickness + kStroke - images_->arrow_thickness; |
171 // When arrow is painted transparently the visible border of the bubble needs | 201 // When arrow is painted transparently the visible border of the bubble needs |
172 // to be positioned at the same bounds as when the arrow is shown. | 202 // to be positioned at the same bounds as when the arrow is shown. |
173 if (arrow_paint_type_ == PAINT_TRANSPARENT) | 203 if (arrow_paint_type_ == PAINT_TRANSPARENT) |
174 arrow_shift += images_->arrow_interior_thickness; | 204 arrow_shift += images_->arrow_interior_thickness; |
175 const bool mid_anchor = alignment_ == ALIGN_ARROW_TO_MID_ANCHOR; | 205 const bool mid_anchor = alignment_ == ALIGN_ARROW_TO_MID_ANCHOR; |
176 | 206 |
177 // Calculate the bubble coordinates based on the border and arrow settings. | 207 // Calculate the bubble coordinates based on the border and arrow settings. |
178 if (is_arrow_on_horizontal(arrow_)) { | 208 if (is_arrow_on_horizontal(arrow_)) { |
179 if (is_arrow_on_left(arrow_)) { | 209 if (is_arrow_on_left(arrow_)) { |
(...skipping 19 matching lines...) Expand all Loading... |
199 } | 229 } |
200 } else { | 230 } else { |
201 x += (w - size.width()) / 2; | 231 x += (w - size.width()) / 2; |
202 y += (arrow_ == NONE) ? h : (h - size.height()) / 2; | 232 y += (arrow_ == NONE) ? h : (h - size.height()) / 2; |
203 } | 233 } |
204 | 234 |
205 return gfx::Rect(x, y, size.width(), size.height()); | 235 return gfx::Rect(x, y, size.width(), size.height()); |
206 } | 236 } |
207 | 237 |
208 int BubbleBorder::GetBorderThickness() const { | 238 int BubbleBorder::GetBorderThickness() const { |
209 return images_->border_thickness - images_->border_interior_thickness; | 239 // TODO(estade): this shouldn't be called in MD. |
| 240 return UseMd() |
| 241 ? 0 |
| 242 : images_->border_thickness - images_->border_interior_thickness; |
210 } | 243 } |
211 | 244 |
212 int BubbleBorder::GetBorderCornerRadius() const { | 245 int BubbleBorder::GetBorderCornerRadius() const { |
213 return images_->corner_radius; | 246 return UseMd() ? 3 : images_->corner_radius; |
214 } | 247 } |
215 | 248 |
216 int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const { | 249 int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const { |
| 250 if (UseMd()) |
| 251 return 0; |
| 252 |
217 const int edge_length = is_arrow_on_horizontal(arrow_) ? | 253 const int edge_length = is_arrow_on_horizontal(arrow_) ? |
218 border_size.width() : border_size.height(); | 254 border_size.width() : border_size.height(); |
219 if (is_arrow_at_center(arrow_) && arrow_offset_ == 0) | 255 if (is_arrow_at_center(arrow_) && arrow_offset_ == 0) |
220 return edge_length / 2; | 256 return edge_length / 2; |
221 | 257 |
222 // Calculate the minimum offset to not overlap arrow and corner images. | 258 // Calculate the minimum offset to not overlap arrow and corner images. |
223 const int min = images_->border_thickness + (images_->arrow_width / 2); | 259 const int min = images_->border_thickness + (images_->arrow_width / 2); |
224 // Ensure the returned value will not cause image overlap, if possible. | 260 // Ensure the returned value will not cause image overlap, if possible. |
225 return std::max(min, std::min(arrow_offset_, edge_length - min)); | 261 return std::max(min, std::min(arrow_offset_, edge_length - min)); |
226 } | 262 } |
227 | 263 |
228 bool BubbleBorder::GetArrowPath(const gfx::Rect& view_bounds, | 264 bool BubbleBorder::GetArrowPath(const gfx::Rect& view_bounds, |
229 gfx::Path* path) const { | 265 gfx::Path* path) const { |
230 if (!has_arrow(arrow_) || arrow_paint_type_ != PAINT_NORMAL) | 266 if (!has_arrow(arrow_) || arrow_paint_type_ != PAINT_NORMAL) |
231 return false; | 267 return false; |
232 | 268 |
233 GetArrowPathFromArrowBounds(GetArrowRect(view_bounds), path); | 269 GetArrowPathFromArrowBounds(GetArrowRect(view_bounds), path); |
234 return true; | 270 return true; |
235 } | 271 } |
236 | 272 |
237 void BubbleBorder::SetBorderInteriorThickness(int border_interior_thickness) { | 273 void BubbleBorder::SetBorderInteriorThickness(int border_interior_thickness) { |
238 images_->border_interior_thickness = border_interior_thickness; | 274 images_->border_interior_thickness = border_interior_thickness; |
239 if (!has_arrow(arrow_) || arrow_paint_type_ != PAINT_NORMAL) | 275 if (!has_arrow(arrow_) || arrow_paint_type_ != PAINT_NORMAL) |
240 images_->border_thickness = border_interior_thickness; | 276 images_->border_thickness = border_interior_thickness; |
241 } | 277 } |
242 | 278 |
243 void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { | 279 void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { |
| 280 if (UseMd()) |
| 281 return PaintMd(view, canvas); |
| 282 |
244 gfx::Rect bounds(view.GetContentsBounds()); | 283 gfx::Rect bounds(view.GetContentsBounds()); |
245 bounds.Inset(-GetBorderThickness(), -GetBorderThickness()); | 284 bounds.Inset(-GetBorderThickness(), -GetBorderThickness()); |
246 const gfx::Rect arrow_bounds = GetArrowRect(view.GetLocalBounds()); | 285 const gfx::Rect arrow_bounds = GetArrowRect(view.GetLocalBounds()); |
247 if (arrow_bounds.IsEmpty()) { | 286 if (arrow_bounds.IsEmpty()) { |
248 if (images_->border_painter) | 287 if (images_->border_painter) |
249 Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds); | 288 Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds); |
250 return; | 289 return; |
251 } | 290 } |
252 if (!images_->border_painter) { | 291 if (!images_->border_painter) { |
253 DrawArrow(canvas, arrow_bounds); | 292 DrawArrow(canvas, arrow_bounds); |
254 return; | 293 return; |
255 } | 294 } |
256 | 295 |
257 // Clip the arrow bounds out to avoid painting the overlapping edge area. | 296 // Clip the arrow bounds out to avoid painting the overlapping edge area. |
258 canvas->Save(); | 297 canvas->Save(); |
259 canvas->ClipRect(arrow_bounds, SkRegion::kDifference_Op); | 298 canvas->ClipRect(arrow_bounds, SkRegion::kDifference_Op); |
260 Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds); | 299 Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds); |
261 canvas->Restore(); | 300 canvas->Restore(); |
262 | 301 |
263 DrawArrow(canvas, arrow_bounds); | 302 DrawArrow(canvas, arrow_bounds); |
264 } | 303 } |
265 | 304 |
266 gfx::Insets BubbleBorder::GetInsets() const { | 305 gfx::Insets BubbleBorder::GetInsets() const { |
| 306 if (UseMd()) { |
| 307 gfx::Insets blur(kLargeShadowBlur); |
| 308 gfx::Insets offset(-kLargeShadowVerticalOffset, 0, |
| 309 kLargeShadowVerticalOffset, 0); |
| 310 return blur + offset; |
| 311 } |
| 312 |
267 // The insets contain the stroke and shadow pixels outside the bubble fill. | 313 // The insets contain the stroke and shadow pixels outside the bubble fill. |
268 const int inset = GetBorderThickness(); | 314 const int inset = GetBorderThickness(); |
269 if (arrow_paint_type_ != PAINT_NORMAL || !has_arrow(arrow_)) | 315 if (arrow_paint_type_ != PAINT_NORMAL || !has_arrow(arrow_)) |
270 return gfx::Insets(inset); | 316 return gfx::Insets(inset); |
271 | 317 |
272 int first_inset = inset; | 318 int first_inset = inset; |
273 int second_inset = std::max(inset, images_->arrow_thickness); | 319 int second_inset = std::max(inset, images_->arrow_thickness); |
274 if (is_arrow_on_horizontal(arrow_) ? | 320 if (is_arrow_on_horizontal(arrow_) ? |
275 is_arrow_on_top(arrow_) : is_arrow_on_left(arrow_)) | 321 is_arrow_on_top(arrow_) : is_arrow_on_left(arrow_)) |
276 std::swap(first_inset, second_inset); | 322 std::swap(first_inset, second_inset); |
277 return is_arrow_on_horizontal(arrow_) ? | 323 return is_arrow_on_horizontal(arrow_) ? |
278 gfx::Insets(first_inset, inset, second_inset, inset) : | 324 gfx::Insets(first_inset, inset, second_inset, inset) : |
279 gfx::Insets(inset, first_inset, inset, second_inset); | 325 gfx::Insets(inset, first_inset, inset, second_inset); |
280 } | 326 } |
281 | 327 |
282 gfx::Size BubbleBorder::GetMinimumSize() const { | 328 gfx::Size BubbleBorder::GetMinimumSize() const { |
283 return GetSizeForContentsSize(gfx::Size()); | 329 return GetSizeForContentsSize(gfx::Size()); |
284 } | 330 } |
285 | 331 |
286 gfx::Size BubbleBorder::GetSizeForContentsSize( | 332 gfx::Size BubbleBorder::GetSizeForContentsSize( |
287 const gfx::Size& contents_size) const { | 333 const gfx::Size& contents_size) const { |
288 // Enlarge the contents size by the thickness of the border images. | 334 // Enlarge the contents size by the thickness of the border images. |
289 gfx::Size size(contents_size); | 335 gfx::Size size(contents_size); |
290 const gfx::Insets insets = GetInsets(); | 336 const gfx::Insets insets = GetInsets(); |
291 size.Enlarge(insets.width(), insets.height()); | 337 size.Enlarge(insets.width(), insets.height()); |
| 338 if (UseMd()) |
| 339 return size; |
292 | 340 |
293 // Ensure the bubble is large enough to not overlap border and arrow images. | 341 // Ensure the bubble is large enough to not overlap border and arrow images. |
294 const int min = 2 * images_->border_thickness; | 342 const int min = 2 * images_->border_thickness; |
295 // Only take arrow image sizes into account when the bubble tip is shown. | 343 // Only take arrow image sizes into account when the bubble tip is shown. |
296 if (arrow_paint_type_ != PAINT_NORMAL || !has_arrow(arrow_)) { | 344 if (arrow_paint_type_ != PAINT_NORMAL || !has_arrow(arrow_)) { |
297 size.SetToMax(gfx::Size(min, min)); | 345 size.SetToMax(gfx::Size(min, min)); |
298 return size; | 346 return size; |
299 } | 347 } |
300 const int min_with_arrow_width = min + images_->arrow_width; | 348 const int min_with_arrow_width = min + images_->arrow_width; |
301 const int min_with_arrow_thickness = images_->border_thickness + | 349 const int min_with_arrow_thickness = images_->border_thickness + |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
353 DCHECK_EQ(2 * images_->arrow_interior_thickness, images_->arrow_width); | 401 DCHECK_EQ(2 * images_->arrow_interior_thickness, images_->arrow_width); |
354 int width = images_->arrow_width; | 402 int width = images_->arrow_width; |
355 int height = images_->arrow_interior_thickness; | 403 int height = images_->arrow_interior_thickness; |
356 if (!is_arrow_on_horizontal(arrow_)) | 404 if (!is_arrow_on_horizontal(arrow_)) |
357 std::swap(width, height); | 405 std::swap(width, height); |
358 return gfx::Rect(origin, gfx::Size(width, height)); | 406 return gfx::Rect(origin, gfx::Size(width, height)); |
359 } | 407 } |
360 | 408 |
361 void BubbleBorder::GetArrowPathFromArrowBounds(const gfx::Rect& arrow_bounds, | 409 void BubbleBorder::GetArrowPathFromArrowBounds(const gfx::Rect& arrow_bounds, |
362 SkPath* path) const { | 410 SkPath* path) const { |
| 411 DCHECK(!UseMd()); |
363 const bool horizontal = is_arrow_on_horizontal(arrow_); | 412 const bool horizontal = is_arrow_on_horizontal(arrow_); |
364 const int thickness = images_->arrow_interior_thickness; | 413 const int thickness = images_->arrow_interior_thickness; |
365 float tip_x = horizontal ? arrow_bounds.CenterPoint().x() : | 414 float tip_x = horizontal ? arrow_bounds.CenterPoint().x() : |
366 is_arrow_on_left(arrow_) ? arrow_bounds.right() - thickness : | 415 is_arrow_on_left(arrow_) ? arrow_bounds.right() - thickness : |
367 arrow_bounds.x() + thickness; | 416 arrow_bounds.x() + thickness; |
368 float tip_y = !horizontal ? arrow_bounds.CenterPoint().y() + 0.5f : | 417 float tip_y = !horizontal ? arrow_bounds.CenterPoint().y() + 0.5f : |
369 is_arrow_on_top(arrow_) ? arrow_bounds.bottom() - thickness : | 418 is_arrow_on_top(arrow_) ? arrow_bounds.bottom() - thickness : |
370 arrow_bounds.y() + thickness; | 419 arrow_bounds.y() + thickness; |
371 const bool positive_offset = horizontal ? | 420 const bool positive_offset = horizontal ? |
372 is_arrow_on_top(arrow_) : is_arrow_on_left(arrow_); | 421 is_arrow_on_top(arrow_) : is_arrow_on_left(arrow_); |
373 const int offset_to_next_vertex = positive_offset ? | 422 const int offset_to_next_vertex = positive_offset ? |
374 images_->arrow_interior_thickness : -images_->arrow_interior_thickness; | 423 images_->arrow_interior_thickness : -images_->arrow_interior_thickness; |
375 | 424 |
376 path->incReserve(4); | 425 path->incReserve(4); |
377 path->moveTo(SkDoubleToScalar(tip_x), SkDoubleToScalar(tip_y)); | 426 path->moveTo(SkDoubleToScalar(tip_x), SkDoubleToScalar(tip_y)); |
378 path->lineTo(SkDoubleToScalar(tip_x + offset_to_next_vertex), | 427 path->lineTo(SkDoubleToScalar(tip_x + offset_to_next_vertex), |
379 SkDoubleToScalar(tip_y + offset_to_next_vertex)); | 428 SkDoubleToScalar(tip_y + offset_to_next_vertex)); |
380 const int multiplier = horizontal ? 1 : -1; | 429 const int multiplier = horizontal ? 1 : -1; |
381 path->lineTo(SkDoubleToScalar(tip_x - multiplier * offset_to_next_vertex), | 430 path->lineTo(SkDoubleToScalar(tip_x - multiplier * offset_to_next_vertex), |
382 SkDoubleToScalar(tip_y + multiplier * offset_to_next_vertex)); | 431 SkDoubleToScalar(tip_y + multiplier * offset_to_next_vertex)); |
383 path->close(); | 432 path->close(); |
384 } | 433 } |
385 | 434 |
386 void BubbleBorder::DrawArrow(gfx::Canvas* canvas, | 435 void BubbleBorder::DrawArrow(gfx::Canvas* canvas, |
387 const gfx::Rect& arrow_bounds) const { | 436 const gfx::Rect& arrow_bounds) const { |
| 437 DCHECK(!UseMd()); |
388 canvas->DrawImageInt(*GetArrowImage(), arrow_bounds.x(), arrow_bounds.y()); | 438 canvas->DrawImageInt(*GetArrowImage(), arrow_bounds.x(), arrow_bounds.y()); |
389 SkPath path; | 439 SkPath path; |
390 GetArrowPathFromArrowBounds(arrow_bounds, &path); | 440 GetArrowPathFromArrowBounds(arrow_bounds, &path); |
391 SkPaint paint; | 441 SkPaint paint; |
392 paint.setStyle(SkPaint::kFill_Style); | 442 paint.setStyle(SkPaint::kFill_Style); |
393 paint.setColor(background_color_); | 443 paint.setColor(background_color_); |
394 | 444 |
395 canvas->DrawPath(path, paint); | 445 canvas->DrawPath(path, paint); |
396 } | 446 } |
397 | 447 |
| 448 void BubbleBorder::PaintMd(const View& view, gfx::Canvas* canvas) { |
| 449 gfx::ScopedCanvas scoped(canvas); |
| 450 |
| 451 SkPaint paint; |
| 452 std::vector<gfx::ShadowValue> shadows; |
| 453 // gfx::ShadowValue counts blur pixels both inside and outside the shape, |
| 454 // whereas these blur values only describe the outside portion, hence they |
| 455 // must be doubled. |
| 456 shadows.emplace_back(gfx::Vector2d(0, kSmallShadowVerticalOffset), |
| 457 2 * kSmallShadowBlur, kSmallShadowColor); |
| 458 shadows.emplace_back(gfx::Vector2d(0, kLargeShadowVerticalOffset), |
| 459 2 * kLargeShadowBlur, kLargeShadowColor); |
| 460 paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); |
| 461 paint.setColor(SkColorSetA(SK_ColorBLACK, 0x0D)); |
| 462 paint.setAntiAlias(true); |
| 463 |
| 464 gfx::Rect bounds(view.GetLocalBounds()); |
| 465 bounds.Inset(GetInsets()); |
| 466 SkRRect r_rect = |
| 467 SkRRect::MakeRectXY(gfx::RectToSkRect(bounds), GetBorderCornerRadius(), |
| 468 GetBorderCornerRadius()); |
| 469 // Clip out a round rect so the fill and shadow don't draw over the contents |
| 470 // of the bubble. |
| 471 SkRRect clip_r_rect = r_rect; |
| 472 // Stroke width is a single pixel at any scale factor. |
| 473 const SkScalar one_pixel = SkFloatToScalar(1 / canvas->image_scale()); |
| 474 clip_r_rect.inset(one_pixel, one_pixel); |
| 475 canvas->sk_canvas()->clipRRect(clip_r_rect, SkRegion::kDifference_Op, |
| 476 true /*doAntiAlias*/); |
| 477 canvas->sk_canvas()->drawRRect(r_rect, paint); |
| 478 } |
| 479 |
398 internal::BorderImages* BubbleBorder::GetImagesForTest() const { | 480 internal::BorderImages* BubbleBorder::GetImagesForTest() const { |
399 return images_; | 481 return images_; |
400 } | 482 } |
401 | 483 |
402 void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const { | 484 void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const { |
403 if (border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER) | 485 if (border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER) |
404 canvas->DrawColor(border_->background_color()); | 486 canvas->DrawColor(border_->background_color()); |
405 | 487 |
406 // Fill the contents with a round-rect region to match the border images. | 488 // Fill the contents with a round-rect region to match the border images. |
407 SkPaint paint; | 489 SkPaint paint; |
408 paint.setAntiAlias(true); | 490 paint.setAntiAlias(true); |
409 paint.setStyle(SkPaint::kFill_Style); | 491 paint.setStyle(SkPaint::kFill_Style); |
410 paint.setColor(border_->background_color()); | 492 paint.setColor(border_->background_color()); |
411 SkPath path; | 493 SkPath path; |
412 gfx::Rect bounds(view->GetLocalBounds()); | 494 gfx::Rect bounds(view->GetLocalBounds()); |
413 bounds.Inset(border_->GetInsets()); | 495 bounds.Inset(border_->GetInsets()); |
414 | 496 |
415 canvas->DrawRoundRect(bounds, border_->GetBorderCornerRadius(), paint); | 497 canvas->DrawRoundRect(bounds, border_->GetBorderCornerRadius(), paint); |
416 } | 498 } |
417 | 499 |
418 } // namespace views | 500 } // namespace views |
OLD | NEW |