| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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 "views/bubble/bubble_border.h" | |
| 6 | |
| 7 #include <algorithm> // for std::max | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "grit/ui_resources.h" | |
| 11 #include "grit/ui_resources_standard.h" | |
| 12 #include "third_party/skia/include/core/SkBitmap.h" | |
| 13 #include "ui/base/resource/resource_bundle.h" | |
| 14 #include "ui/gfx/canvas_skia.h" | |
| 15 #include "ui/gfx/path.h" | |
| 16 | |
| 17 namespace views { | |
| 18 | |
| 19 struct BubbleBorder::BorderImages { | |
| 20 BorderImages() | |
| 21 : left(NULL), | |
| 22 top_left(NULL), | |
| 23 top(NULL), | |
| 24 top_right(NULL), | |
| 25 right(NULL), | |
| 26 bottom_right(NULL), | |
| 27 bottom(NULL), | |
| 28 bottom_left(NULL), | |
| 29 left_arrow(NULL), | |
| 30 top_arrow(NULL), | |
| 31 right_arrow(NULL), | |
| 32 bottom_arrow(NULL) { | |
| 33 } | |
| 34 | |
| 35 SkBitmap* left; | |
| 36 SkBitmap* top_left; | |
| 37 SkBitmap* top; | |
| 38 SkBitmap* top_right; | |
| 39 SkBitmap* right; | |
| 40 SkBitmap* bottom_right; | |
| 41 SkBitmap* bottom; | |
| 42 SkBitmap* bottom_left; | |
| 43 SkBitmap* left_arrow; | |
| 44 SkBitmap* top_arrow; | |
| 45 SkBitmap* right_arrow; | |
| 46 SkBitmap* bottom_arrow; | |
| 47 }; | |
| 48 | |
| 49 // static | |
| 50 struct BubbleBorder::BorderImages* BubbleBorder::normal_images_ = NULL; | |
| 51 struct BubbleBorder::BorderImages* BubbleBorder::shadow_images_ = NULL; | |
| 52 | |
| 53 | |
| 54 // The height inside the arrow image, in pixels. | |
| 55 static const int kArrowInteriorHeight = 7; | |
| 56 | |
| 57 BubbleBorder::BubbleBorder(ArrowLocation arrow_location, Shadow shadow) | |
| 58 : override_arrow_offset_(0), | |
| 59 arrow_location_(arrow_location), | |
| 60 alignment_(ALIGN_ARROW_TO_MID_ANCHOR), | |
| 61 background_color_(SK_ColorWHITE) { | |
| 62 images_ = GetBorderImages(shadow); | |
| 63 | |
| 64 // Calculate horizontal and vertical insets for arrow by ensuring that | |
| 65 // the widest arrow and corner images will have enough room to avoid overlap | |
| 66 int offset_x = | |
| 67 (std::max(images_->top_arrow->width(), | |
| 68 images_->bottom_arrow->width()) / 2) + | |
| 69 std::max(std::max(images_->top_left->width(), | |
| 70 images_->top_right->width()), | |
| 71 std::max(images_->bottom_left->width(), | |
| 72 images_->bottom_right->width())); | |
| 73 int offset_y = | |
| 74 (std::max(images_->left_arrow->height(), | |
| 75 images_->right_arrow->height()) / 2) + | |
| 76 std::max(std::max(images_->top_left->height(), | |
| 77 images_->top_right->height()), | |
| 78 std::max(images_->bottom_left->height(), | |
| 79 images_->bottom_right->height())); | |
| 80 arrow_offset_ = std::max(offset_x, offset_y); | |
| 81 } | |
| 82 | |
| 83 gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& position_relative_to, | |
| 84 const gfx::Size& contents_size) const { | |
| 85 // Desired size is size of contents enlarged by the size of the border images. | |
| 86 gfx::Size border_size(contents_size); | |
| 87 gfx::Insets insets; | |
| 88 GetInsets(&insets); | |
| 89 border_size.Enlarge(insets.left() + insets.right(), | |
| 90 insets.top() + insets.bottom()); | |
| 91 | |
| 92 // Screen position depends on the arrow location. | |
| 93 // The arrow should overlap the target by some amount since there is space | |
| 94 // for shadow between arrow tip and bitmap bounds. | |
| 95 const int kArrowOverlap = 3; | |
| 96 int x = position_relative_to.x(); | |
| 97 int y = position_relative_to.y(); | |
| 98 int w = position_relative_to.width(); | |
| 99 int h = position_relative_to.height(); | |
| 100 int arrow_offset = override_arrow_offset_ ? override_arrow_offset_ : | |
| 101 arrow_offset_; | |
| 102 | |
| 103 // Calculate bubble x coordinate. | |
| 104 switch (arrow_location_) { | |
| 105 case TOP_LEFT: | |
| 106 case BOTTOM_LEFT: | |
| 107 x += alignment_ == ALIGN_ARROW_TO_MID_ANCHOR ? w / 2 - arrow_offset : | |
| 108 -kArrowOverlap; | |
| 109 break; | |
| 110 | |
| 111 case TOP_RIGHT: | |
| 112 case BOTTOM_RIGHT: | |
| 113 x += alignment_ == ALIGN_ARROW_TO_MID_ANCHOR ? | |
| 114 w / 2 + arrow_offset - border_size.width() + 1 : | |
| 115 w - border_size.width() + kArrowOverlap; | |
| 116 break; | |
| 117 | |
| 118 case LEFT_TOP: | |
| 119 case LEFT_BOTTOM: | |
| 120 x += w - kArrowOverlap; | |
| 121 break; | |
| 122 | |
| 123 case RIGHT_TOP: | |
| 124 case RIGHT_BOTTOM: | |
| 125 x += kArrowOverlap - border_size.width(); | |
| 126 break; | |
| 127 | |
| 128 case NONE: | |
| 129 case FLOAT: | |
| 130 x += w / 2 - border_size.width() / 2; | |
| 131 break; | |
| 132 } | |
| 133 | |
| 134 // Calculate bubble y coordinate. | |
| 135 switch (arrow_location_) { | |
| 136 case TOP_LEFT: | |
| 137 case TOP_RIGHT: | |
| 138 y += h - kArrowOverlap; | |
| 139 break; | |
| 140 | |
| 141 case BOTTOM_LEFT: | |
| 142 case BOTTOM_RIGHT: | |
| 143 y += kArrowOverlap - border_size.height(); | |
| 144 break; | |
| 145 | |
| 146 case LEFT_TOP: | |
| 147 case RIGHT_TOP: | |
| 148 y += alignment_ == ALIGN_ARROW_TO_MID_ANCHOR ? h / 2 - arrow_offset : | |
| 149 -kArrowOverlap; | |
| 150 break; | |
| 151 | |
| 152 case LEFT_BOTTOM: | |
| 153 case RIGHT_BOTTOM: | |
| 154 y += alignment_ == ALIGN_ARROW_TO_MID_ANCHOR ? | |
| 155 h / 2 + arrow_offset - border_size.height() + 1 : | |
| 156 h - border_size.height() + kArrowOverlap; | |
| 157 break; | |
| 158 | |
| 159 case NONE: | |
| 160 y += h; | |
| 161 break; | |
| 162 | |
| 163 case FLOAT: | |
| 164 y += h / 2 - border_size.height() / 2; | |
| 165 break; | |
| 166 } | |
| 167 | |
| 168 return gfx::Rect(x, y, border_size.width(), border_size.height()); | |
| 169 } | |
| 170 | |
| 171 void BubbleBorder::GetInsets(gfx::Insets* insets) const { | |
| 172 int top = images_->top->height(); | |
| 173 int bottom = images_->bottom->height(); | |
| 174 int left = images_->left->width(); | |
| 175 int right = images_->right->width(); | |
| 176 switch (arrow_location_) { | |
| 177 case TOP_LEFT: | |
| 178 case TOP_RIGHT: | |
| 179 top = std::max(top, images_->top_arrow->height()); | |
| 180 break; | |
| 181 | |
| 182 case BOTTOM_LEFT: | |
| 183 case BOTTOM_RIGHT: | |
| 184 bottom = std::max(bottom, images_->bottom_arrow->height()); | |
| 185 break; | |
| 186 | |
| 187 case LEFT_TOP: | |
| 188 case LEFT_BOTTOM: | |
| 189 left = std::max(left, images_->left_arrow->width()); | |
| 190 break; | |
| 191 | |
| 192 case RIGHT_TOP: | |
| 193 case RIGHT_BOTTOM: | |
| 194 right = std::max(right, images_->right_arrow->width()); | |
| 195 break; | |
| 196 | |
| 197 case NONE: | |
| 198 case FLOAT: | |
| 199 // Nothing to do. | |
| 200 break; | |
| 201 } | |
| 202 insets->Set(top, left, bottom, right); | |
| 203 } | |
| 204 | |
| 205 int BubbleBorder::SetArrowOffset(int offset, const gfx::Size& contents_size) { | |
| 206 gfx::Size border_size(contents_size); | |
| 207 gfx::Insets insets; | |
| 208 GetInsets(&insets); | |
| 209 border_size.Enlarge(insets.left() + insets.right(), | |
| 210 insets.top() + insets.bottom()); | |
| 211 offset = std::max(arrow_offset_, | |
| 212 std::min(offset, (is_arrow_on_horizontal(arrow_location_) ? | |
| 213 border_size.width() : border_size.height()) - arrow_offset_)); | |
| 214 override_arrow_offset_ = offset; | |
| 215 return override_arrow_offset_; | |
| 216 } | |
| 217 | |
| 218 // static | |
| 219 BubbleBorder::BorderImages* BubbleBorder::GetBorderImages(Shadow shadow) { | |
| 220 if (shadow == SHADOW && shadow_images_ == NULL) { | |
| 221 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 222 shadow_images_ = new BorderImages(); | |
| 223 shadow_images_->left = rb.GetBitmapNamed(IDR_BUBBLE_SHADOW_L); | |
| 224 shadow_images_->top_left = rb.GetBitmapNamed(IDR_BUBBLE_SHADOW_TL); | |
| 225 shadow_images_->top = rb.GetBitmapNamed(IDR_BUBBLE_SHADOW_T); | |
| 226 shadow_images_->top_right = rb.GetBitmapNamed(IDR_BUBBLE_SHADOW_TR); | |
| 227 shadow_images_->right = rb.GetBitmapNamed(IDR_BUBBLE_SHADOW_R); | |
| 228 shadow_images_->bottom_right = rb.GetBitmapNamed(IDR_BUBBLE_SHADOW_BR); | |
| 229 shadow_images_->bottom = rb.GetBitmapNamed(IDR_BUBBLE_SHADOW_B); | |
| 230 shadow_images_->bottom_left = rb.GetBitmapNamed(IDR_BUBBLE_SHADOW_BL); | |
| 231 shadow_images_->left_arrow = new SkBitmap(); | |
| 232 shadow_images_->top_arrow = new SkBitmap(); | |
| 233 shadow_images_->right_arrow = new SkBitmap(); | |
| 234 shadow_images_->bottom_arrow = new SkBitmap(); | |
| 235 } else if (shadow == NO_SHADOW && normal_images_ == NULL) { | |
| 236 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 237 normal_images_ = new BorderImages(); | |
| 238 normal_images_->left = rb.GetBitmapNamed(IDR_BUBBLE_L); | |
| 239 normal_images_->top_left = rb.GetBitmapNamed(IDR_BUBBLE_TL); | |
| 240 normal_images_->top = rb.GetBitmapNamed(IDR_BUBBLE_T); | |
| 241 normal_images_->top_right = rb.GetBitmapNamed(IDR_BUBBLE_TR); | |
| 242 normal_images_->right = rb.GetBitmapNamed(IDR_BUBBLE_R); | |
| 243 normal_images_->bottom_right = rb.GetBitmapNamed(IDR_BUBBLE_BR); | |
| 244 normal_images_->bottom = rb.GetBitmapNamed(IDR_BUBBLE_B); | |
| 245 normal_images_->bottom_left = rb.GetBitmapNamed(IDR_BUBBLE_BL); | |
| 246 normal_images_->left_arrow = rb.GetBitmapNamed(IDR_BUBBLE_L_ARROW); | |
| 247 normal_images_->top_arrow = rb.GetBitmapNamed(IDR_BUBBLE_T_ARROW); | |
| 248 normal_images_->right_arrow = rb.GetBitmapNamed(IDR_BUBBLE_R_ARROW); | |
| 249 normal_images_->bottom_arrow = rb.GetBitmapNamed(IDR_BUBBLE_B_ARROW); | |
| 250 } | |
| 251 return shadow == SHADOW ? shadow_images_ : normal_images_; | |
| 252 } | |
| 253 | |
| 254 BubbleBorder::~BubbleBorder() {} | |
| 255 | |
| 256 void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const { | |
| 257 // Convenience shorthand variables. | |
| 258 const int tl_width = images_->top_left->width(); | |
| 259 const int tl_height = images_->top_left->height(); | |
| 260 const int t_height = images_->top->height(); | |
| 261 const int tr_width = images_->top_right->width(); | |
| 262 const int tr_height = images_->top_right->height(); | |
| 263 const int l_width = images_->left->width(); | |
| 264 const int r_width = images_->right->width(); | |
| 265 const int br_width = images_->bottom_right->width(); | |
| 266 const int br_height = images_->bottom_right->height(); | |
| 267 const int b_height = images_->bottom->height(); | |
| 268 const int bl_width = images_->bottom_left->width(); | |
| 269 const int bl_height = images_->bottom_left->height(); | |
| 270 | |
| 271 gfx::Insets insets; | |
| 272 GetInsets(&insets); | |
| 273 const int top = insets.top() - t_height; | |
| 274 const int bottom = view.height() - insets.bottom() + b_height; | |
| 275 const int left = insets.left() - l_width; | |
| 276 const int right = view.width() - insets.right() + r_width; | |
| 277 const int height = bottom - top; | |
| 278 const int width = right - left; | |
| 279 | |
| 280 // |arrow_offset| is offset of arrow from the begining of the edge. | |
| 281 int arrow_offset = arrow_offset_; | |
| 282 if (override_arrow_offset_) | |
| 283 arrow_offset = override_arrow_offset_; | |
| 284 else if (is_arrow_on_horizontal(arrow_location_) && | |
| 285 !is_arrow_on_left(arrow_location_)) { | |
| 286 arrow_offset = view.width() - arrow_offset - 1; | |
| 287 } else if (!is_arrow_on_horizontal(arrow_location_) && | |
| 288 !is_arrow_on_top(arrow_location_)) { | |
| 289 arrow_offset = view.height() - arrow_offset - 1; | |
| 290 } | |
| 291 | |
| 292 // Left edge. | |
| 293 if (arrow_location_ == LEFT_TOP || arrow_location_ == LEFT_BOTTOM) { | |
| 294 int start_y = top + tl_height; | |
| 295 int before_arrow = | |
| 296 arrow_offset - start_y - images_->left_arrow->height() / 2; | |
| 297 int after_arrow = height - tl_height - bl_height - | |
| 298 images_->left_arrow->height() - before_arrow; | |
| 299 int tip_y = start_y + before_arrow + images_->left_arrow->height() / 2; | |
| 300 DrawArrowInterior(canvas, | |
| 301 false, | |
| 302 images_->left_arrow->width() - kArrowInteriorHeight, | |
| 303 tip_y, | |
| 304 kArrowInteriorHeight, | |
| 305 images_->left_arrow->height() / 2 - 1); | |
| 306 DrawEdgeWithArrow(canvas, | |
| 307 false, | |
| 308 images_->left, | |
| 309 images_->left_arrow, | |
| 310 left, | |
| 311 start_y, | |
| 312 before_arrow, | |
| 313 after_arrow, | |
| 314 images_->left->width() - images_->left_arrow->width()); | |
| 315 } else { | |
| 316 canvas->TileImageInt(*images_->left, left, top + tl_height, l_width, | |
| 317 height - tl_height - bl_height); | |
| 318 } | |
| 319 | |
| 320 // Top left corner. | |
| 321 canvas->DrawBitmapInt(*images_->top_left, left, top); | |
| 322 | |
| 323 // Top edge. | |
| 324 if (arrow_location_ == TOP_LEFT || arrow_location_ == TOP_RIGHT) { | |
| 325 int start_x = left + tl_width; | |
| 326 int before_arrow = arrow_offset - start_x - images_->top_arrow->width() / 2; | |
| 327 int after_arrow = width - tl_width - tr_width - | |
| 328 images_->top_arrow->width() - before_arrow; | |
| 329 DrawArrowInterior(canvas, | |
| 330 true, | |
| 331 start_x + before_arrow + images_->top_arrow->width() / 2, | |
| 332 images_->top_arrow->height() - kArrowInteriorHeight, | |
| 333 1 - images_->top_arrow->width() / 2, | |
| 334 kArrowInteriorHeight); | |
| 335 DrawEdgeWithArrow(canvas, | |
| 336 true, | |
| 337 images_->top, | |
| 338 images_->top_arrow, | |
| 339 start_x, | |
| 340 top, | |
| 341 before_arrow, | |
| 342 after_arrow, | |
| 343 images_->top->height() - images_->top_arrow->height()); | |
| 344 } else { | |
| 345 canvas->TileImageInt(*images_->top, left + tl_width, top, | |
| 346 width - tl_width - tr_width, t_height); | |
| 347 } | |
| 348 | |
| 349 // Top right corner. | |
| 350 canvas->DrawBitmapInt(*images_->top_right, right - tr_width, top); | |
| 351 | |
| 352 // Right edge. | |
| 353 if (arrow_location_ == RIGHT_TOP || arrow_location_ == RIGHT_BOTTOM) { | |
| 354 int start_y = top + tr_height; | |
| 355 int before_arrow = | |
| 356 arrow_offset - start_y - images_->right_arrow->height() / 2; | |
| 357 int after_arrow = height - tl_height - bl_height - | |
| 358 images_->right_arrow->height() - before_arrow; | |
| 359 int tip_y = start_y + before_arrow + images_->right_arrow->height() / 2; | |
| 360 DrawArrowInterior(canvas, | |
| 361 false, | |
| 362 right - r_width + kArrowInteriorHeight, | |
| 363 tip_y, | |
| 364 -kArrowInteriorHeight, | |
| 365 images_->right_arrow->height() / 2 - 1); | |
| 366 DrawEdgeWithArrow(canvas, | |
| 367 false, | |
| 368 images_->right, | |
| 369 images_->right_arrow, | |
| 370 right - r_width, | |
| 371 start_y, | |
| 372 before_arrow, | |
| 373 after_arrow, | |
| 374 0); | |
| 375 } else { | |
| 376 canvas->TileImageInt(*images_->right, right - r_width, top + tr_height, | |
| 377 r_width, height - tr_height - br_height); | |
| 378 } | |
| 379 | |
| 380 // Bottom right corner. | |
| 381 canvas->DrawBitmapInt(*images_->bottom_right, | |
| 382 right - br_width, | |
| 383 bottom - br_height); | |
| 384 | |
| 385 // Bottom edge. | |
| 386 if (arrow_location_ == BOTTOM_LEFT || arrow_location_ == BOTTOM_RIGHT) { | |
| 387 int start_x = left + bl_width; | |
| 388 int before_arrow = | |
| 389 arrow_offset - start_x - images_->bottom_arrow->width() / 2; | |
| 390 int after_arrow = width - bl_width - br_width - | |
| 391 images_->bottom_arrow->width() - before_arrow; | |
| 392 int tip_x = start_x + before_arrow + images_->bottom_arrow->width() / 2; | |
| 393 DrawArrowInterior(canvas, | |
| 394 true, | |
| 395 tip_x, | |
| 396 bottom - b_height + kArrowInteriorHeight, | |
| 397 1 - images_->bottom_arrow->width() / 2, | |
| 398 -kArrowInteriorHeight); | |
| 399 DrawEdgeWithArrow(canvas, | |
| 400 true, | |
| 401 images_->bottom, | |
| 402 images_->bottom_arrow, | |
| 403 start_x, | |
| 404 bottom - b_height, | |
| 405 before_arrow, | |
| 406 after_arrow, | |
| 407 0); | |
| 408 } else { | |
| 409 canvas->TileImageInt(*images_->bottom, left + bl_width, bottom - b_height, | |
| 410 width - bl_width - br_width, b_height); | |
| 411 } | |
| 412 | |
| 413 // Bottom left corner. | |
| 414 canvas->DrawBitmapInt(*images_->bottom_left, left, bottom - bl_height); | |
| 415 } | |
| 416 | |
| 417 void BubbleBorder::DrawEdgeWithArrow(gfx::Canvas* canvas, | |
| 418 bool is_horizontal, | |
| 419 SkBitmap* edge, | |
| 420 SkBitmap* arrow, | |
| 421 int start_x, | |
| 422 int start_y, | |
| 423 int before_arrow, | |
| 424 int after_arrow, | |
| 425 int offset) const { | |
| 426 /* Here's what the parameters mean: | |
| 427 * start_x | |
| 428 * . | |
| 429 * . ┌───┐ ┬ offset | |
| 430 * start_y..........┌────┬────────┤ ▲ ├────────┬────┐ | |
| 431 * │ / │--------│∙ ∙│--------│ \ │ | |
| 432 * │ / ├────────┴───┴────────┤ \ │ | |
| 433 * ├───┬┘ └┬───┤ | |
| 434 * └───┬────┘ └───┬────┘ | |
| 435 * before_arrow ─┘ └─ after_arrow | |
| 436 */ | |
| 437 if (before_arrow) { | |
| 438 canvas->TileImageInt(*edge, start_x, start_y, | |
| 439 is_horizontal ? before_arrow : edge->width(), | |
| 440 is_horizontal ? edge->height() : before_arrow); | |
| 441 } | |
| 442 | |
| 443 canvas->DrawBitmapInt(*arrow, | |
| 444 start_x + (is_horizontal ? before_arrow : offset), | |
| 445 start_y + (is_horizontal ? offset : before_arrow)); | |
| 446 | |
| 447 if (after_arrow) { | |
| 448 start_x += (is_horizontal ? before_arrow + arrow->width() : 0); | |
| 449 start_y += (is_horizontal ? 0 : before_arrow + arrow->height()); | |
| 450 canvas->TileImageInt(*edge, start_x, start_y, | |
| 451 is_horizontal ? after_arrow : edge->width(), | |
| 452 is_horizontal ? edge->height() : after_arrow); | |
| 453 } | |
| 454 } | |
| 455 | |
| 456 void BubbleBorder::DrawArrowInterior(gfx::Canvas* canvas, | |
| 457 bool is_horizontal, | |
| 458 int tip_x, | |
| 459 int tip_y, | |
| 460 int shift_x, | |
| 461 int shift_y) const { | |
| 462 /* This function fills the interior of the arrow with background color. | |
| 463 * It draws isosceles triangle under semitransparent arrow tip. | |
| 464 * | |
| 465 * Here's what the parameters mean: | |
| 466 * | |
| 467 * ┌──────── |tip_x| | |
| 468 * ┌─────┐ | |
| 469 * │ ▲ │ ──── |tip y| | |
| 470 * │∙∙∙∙∙│ ┐ | |
| 471 * └─────┘ └─── |shift_x| (offset from tip to vertexes of isosceles triangle) | |
| 472 * └────────── |shift_y| | |
| 473 */ | |
| 474 SkPaint paint; | |
| 475 paint.setStyle(SkPaint::kFill_Style); | |
| 476 paint.setColor(background_color_); | |
| 477 gfx::Path path; | |
| 478 path.incReserve(4); | |
| 479 path.moveTo(SkIntToScalar(tip_x), SkIntToScalar(tip_y)); | |
| 480 path.lineTo(SkIntToScalar(tip_x + shift_x), | |
| 481 SkIntToScalar(tip_y + shift_y)); | |
| 482 if (is_horizontal) | |
| 483 path.lineTo(SkIntToScalar(tip_x - shift_x), SkIntToScalar(tip_y + shift_y)); | |
| 484 else | |
| 485 path.lineTo(SkIntToScalar(tip_x + shift_x), SkIntToScalar(tip_y - shift_y)); | |
| 486 path.close(); | |
| 487 canvas->GetSkCanvas()->drawPath(path, paint); | |
| 488 } | |
| 489 | |
| 490 ///////////////////////// | |
| 491 | |
| 492 void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const { | |
| 493 // The border of this view creates an anti-aliased round-rect region for the | |
| 494 // contents, which we need to fill with the background color. | |
| 495 // NOTE: This doesn't handle an arrow location of "NONE", which has square top | |
| 496 // corners. | |
| 497 SkPaint paint; | |
| 498 paint.setAntiAlias(true); | |
| 499 paint.setStyle(SkPaint::kFill_Style); | |
| 500 paint.setColor(border_->background_color()); | |
| 501 gfx::Path path; | |
| 502 gfx::Rect bounds(view->GetContentsBounds()); | |
| 503 SkRect rect; | |
| 504 rect.set(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()), | |
| 505 SkIntToScalar(bounds.right()), SkIntToScalar(bounds.bottom())); | |
| 506 SkScalar radius = SkIntToScalar(BubbleBorder::GetCornerRadius()); | |
| 507 path.addRoundRect(rect, radius, radius); | |
| 508 canvas->GetSkCanvas()->drawPath(path, paint); | |
| 509 } | |
| 510 | |
| 511 } // namespace views | |
| OLD | NEW |