| 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/SkDrawLooper.h" |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 arrow_thickness = top_arrow.height(); | 53 arrow_thickness = top_arrow.height(); |
| 54 } | 54 } |
| 55 } | 55 } |
| 56 | 56 |
| 57 BorderImages::~BorderImages() {} | 57 BorderImages::~BorderImages() {} |
| 58 | 58 |
| 59 } // namespace internal | 59 } // namespace internal |
| 60 | 60 |
| 61 namespace { | 61 namespace { |
| 62 | 62 |
| 63 // The border is stroked at 1px, but for the purposes of reserving space we have |
| 64 // to deal in dip coordinates, so round up to 1dip. |
| 65 const int kBorderThicknessDip = 1; |
| 66 const int kBorderStrokeThicknessPx = 1; |
| 67 |
| 63 // Blur and offset values for the two shadows drawn around each dialog. The | 68 // Blur and offset values for the two shadows drawn around each dialog. The |
| 64 // values are all in dip. | 69 // values are all in dip. |
| 65 const int kSmallShadowVerticalOffset = 2; | 70 const int kSmallShadowVerticalOffset = 2; |
| 66 const int kSmallShadowBlur = 4; | 71 const int kSmallShadowBlur = 4; |
| 67 const SkColor kSmallShadowColor = SkColorSetA(SK_ColorBLACK, 0x33); | 72 const SkColor kSmallShadowColor = SkColorSetA(SK_ColorBLACK, 0x33); |
| 68 | 73 |
| 69 const int kLargeShadowVerticalOffset = 2; | 74 const int kLargeShadowVerticalOffset = 2; |
| 70 const int kLargeShadowBlur = 6; | 75 const int kLargeShadowBlur = 6; |
| 71 const SkColor kLargeShadowColor = SkColorSetA(SK_ColorBLACK, 0x1A); | 76 const SkColor kLargeShadowColor = SkColorSetA(SK_ColorBLACK, 0x1A); |
| 72 | 77 |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 204 } | 209 } |
| 205 | 210 |
| 206 gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, | 211 gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, |
| 207 const gfx::Size& contents_size) const { | 212 const gfx::Size& contents_size) const { |
| 208 // In MD, there are no arrows, so positioning logic is significantly simpler. | 213 // In MD, there are no arrows, so positioning logic is significantly simpler. |
| 209 // TODO(estade): handle more anchor positions. | 214 // TODO(estade): handle more anchor positions. |
| 210 if (UseMd() && | 215 if (UseMd() && |
| 211 (arrow_ == TOP_RIGHT || arrow_ == TOP_LEFT || arrow_ == BOTTOM_CENTER || | 216 (arrow_ == TOP_RIGHT || arrow_ == TOP_LEFT || arrow_ == BOTTOM_CENTER || |
| 212 arrow_ == LEFT_CENTER || arrow_ == RIGHT_CENTER)) { | 217 arrow_ == LEFT_CENTER || arrow_ == RIGHT_CENTER)) { |
| 213 gfx::Rect contents_bounds(contents_size); | 218 gfx::Rect contents_bounds(contents_size); |
| 219 // Apply the border part of the inset before calculating coordinates because |
| 220 // the border should align with the anchor's border. For the purposes of |
| 221 // positioning, the border is rounded up to a dip, which may mean we have |
| 222 // misalignment in scale factors greater than 1. |
| 223 // TODO(estade): when it becomes possible to provide px bounds instead of |
| 224 // dip bounds, fix this. |
| 225 const gfx::Insets border_insets = gfx::Insets(kBorderThicknessDip); |
| 226 const gfx::Insets shadow_insets = GetInsets() - border_insets; |
| 227 contents_bounds.Inset(-border_insets); |
| 214 if (arrow_ == TOP_RIGHT) { | 228 if (arrow_ == TOP_RIGHT) { |
| 215 contents_bounds += | 229 contents_bounds += |
| 216 anchor_rect.bottom_right() - contents_bounds.top_right(); | 230 anchor_rect.bottom_right() - contents_bounds.top_right(); |
| 217 } else if (arrow_ == TOP_LEFT) { | 231 } else if (arrow_ == TOP_LEFT) { |
| 218 contents_bounds += | 232 contents_bounds += |
| 219 anchor_rect.bottom_left() - contents_bounds.origin(); | 233 anchor_rect.bottom_left() - contents_bounds.origin(); |
| 220 } else if (arrow_ == BOTTOM_CENTER) { | 234 } else if (arrow_ == BOTTOM_CENTER) { |
| 221 contents_bounds += CenterTop(anchor_rect) - CenterBottom(contents_bounds); | 235 contents_bounds += CenterTop(anchor_rect) - CenterBottom(contents_bounds); |
| 222 } else if (arrow_ == LEFT_CENTER) { | 236 } else if (arrow_ == LEFT_CENTER) { |
| 223 contents_bounds += RightCenter(anchor_rect) - LeftCenter(contents_bounds); | 237 contents_bounds += RightCenter(anchor_rect) - LeftCenter(contents_bounds); |
| 224 } else if (arrow_ == RIGHT_CENTER) { | 238 } else if (arrow_ == RIGHT_CENTER) { |
| 225 contents_bounds += LeftCenter(anchor_rect) - RightCenter(contents_bounds); | 239 contents_bounds += LeftCenter(anchor_rect) - RightCenter(contents_bounds); |
| 226 } | 240 } |
| 227 contents_bounds.Inset(-GetInsets()); | 241 contents_bounds.Inset(-shadow_insets); |
| 228 // |arrow_offset_| is used to adjust bubbles that would normally be | 242 // |arrow_offset_| is used to adjust bubbles that would normally be |
| 229 // partially offscreen. | 243 // partially offscreen. |
| 230 contents_bounds += gfx::Vector2d(-arrow_offset_, 0); | 244 contents_bounds += gfx::Vector2d(-arrow_offset_, 0); |
| 231 return contents_bounds; | 245 return contents_bounds; |
| 232 } | 246 } |
| 233 | 247 |
| 234 int x = anchor_rect.x(); | 248 int x = anchor_rect.x(); |
| 235 int y = anchor_rect.y(); | 249 int y = anchor_rect.y(); |
| 236 int w = anchor_rect.width(); | 250 int w = anchor_rect.width(); |
| 237 int h = anchor_rect.height(); | 251 int h = anchor_rect.height(); |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 345 canvas->Restore(); | 359 canvas->Restore(); |
| 346 | 360 |
| 347 DrawArrow(canvas, arrow_bounds); | 361 DrawArrow(canvas, arrow_bounds); |
| 348 } | 362 } |
| 349 | 363 |
| 350 gfx::Insets BubbleBorder::GetInsets() const { | 364 gfx::Insets BubbleBorder::GetInsets() const { |
| 351 if (UseMd()) { | 365 if (UseMd()) { |
| 352 gfx::Insets blur(kLargeShadowBlur); | 366 gfx::Insets blur(kLargeShadowBlur); |
| 353 gfx::Insets offset(-kLargeShadowVerticalOffset, 0, | 367 gfx::Insets offset(-kLargeShadowVerticalOffset, 0, |
| 354 kLargeShadowVerticalOffset, 0); | 368 kLargeShadowVerticalOffset, 0); |
| 369 gfx::Insets border(kBorderThicknessDip); |
| 355 return blur + offset; | 370 return blur + offset; |
| 356 } | 371 } |
| 357 | 372 |
| 358 // The insets contain the stroke and shadow pixels outside the bubble fill. | 373 // The insets contain the stroke and shadow pixels outside the bubble fill. |
| 359 const int inset = GetBorderThickness(); | 374 const int inset = GetBorderThickness(); |
| 360 if (arrow_paint_type_ != PAINT_NORMAL || !has_arrow(arrow_)) | 375 if (arrow_paint_type_ != PAINT_NORMAL || !has_arrow(arrow_)) |
| 361 return gfx::Insets(inset); | 376 return gfx::Insets(inset); |
| 362 | 377 |
| 363 int first_inset = inset; | 378 int first_inset = inset; |
| 364 int second_inset = std::max(inset, images_->arrow_thickness); | 379 int second_inset = std::max(inset, images_->arrow_thickness); |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 499 // whereas these blur values only describe the outside portion, hence they | 514 // whereas these blur values only describe the outside portion, hence they |
| 500 // must be doubled. | 515 // must be doubled. |
| 501 shadows.emplace_back(gfx::Vector2d(0, kSmallShadowVerticalOffset), | 516 shadows.emplace_back(gfx::Vector2d(0, kSmallShadowVerticalOffset), |
| 502 2 * kSmallShadowBlur, kSmallShadowColor); | 517 2 * kSmallShadowBlur, kSmallShadowColor); |
| 503 shadows.emplace_back(gfx::Vector2d(0, kLargeShadowVerticalOffset), | 518 shadows.emplace_back(gfx::Vector2d(0, kLargeShadowVerticalOffset), |
| 504 2 * kLargeShadowBlur, kLargeShadowColor); | 519 2 * kLargeShadowBlur, kLargeShadowColor); |
| 505 paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); | 520 paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); |
| 506 paint.setColor(SkColorSetA(SK_ColorBLACK, 0x26)); | 521 paint.setColor(SkColorSetA(SK_ColorBLACK, 0x26)); |
| 507 paint.setAntiAlias(true); | 522 paint.setAntiAlias(true); |
| 508 | 523 |
| 509 gfx::Rect bounds(view.GetLocalBounds()); | 524 gfx::RectF bounds(view.GetLocalBounds()); |
| 510 bounds.Inset(GetInsets()); | 525 bounds.Inset(GetInsets()); |
| 511 SkRRect r_rect = | |
| 512 SkRRect::MakeRectXY(gfx::RectToSkRect(bounds), GetBorderCornerRadius(), | |
| 513 GetBorderCornerRadius()); | |
| 514 // Clip out a round rect so the fill and shadow don't draw over the contents | 526 // Clip out a round rect so the fill and shadow don't draw over the contents |
| 515 // of the bubble. | 527 // of the bubble. |
| 516 SkRRect clip_r_rect = r_rect; | 528 SkRRect clip_r_rect = |
| 517 // Stroke width is a single pixel at any scale factor. | 529 SkRRect::MakeRectXY(gfx::RectFToSkRect(bounds), GetBorderCornerRadius(), |
| 518 const SkScalar one_pixel = SkFloatToScalar(1 / canvas->image_scale()); | 530 GetBorderCornerRadius()); |
| 519 clip_r_rect.inset(one_pixel, one_pixel); | |
| 520 canvas->sk_canvas()->clipRRect(clip_r_rect, SkRegion::kDifference_Op, | 531 canvas->sk_canvas()->clipRRect(clip_r_rect, SkRegion::kDifference_Op, |
| 521 true /*doAntiAlias*/); | 532 true /*doAntiAlias*/); |
| 533 |
| 534 // The border is drawn outside the content area. |
| 535 SkRRect r_rect = clip_r_rect; |
| 536 const SkScalar one_pixel = |
| 537 SkFloatToScalar(kBorderStrokeThicknessPx / canvas->image_scale()); |
| 538 r_rect.inset(-one_pixel, -one_pixel); |
| 522 canvas->sk_canvas()->drawRRect(r_rect, paint); | 539 canvas->sk_canvas()->drawRRect(r_rect, paint); |
| 523 } | 540 } |
| 524 | 541 |
| 525 internal::BorderImages* BubbleBorder::GetImagesForTest() const { | 542 internal::BorderImages* BubbleBorder::GetImagesForTest() const { |
| 526 return images_; | 543 return images_; |
| 527 } | 544 } |
| 528 | 545 |
| 529 void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const { | 546 void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const { |
| 530 if (border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER) | 547 if (border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER) |
| 531 canvas->DrawColor(border_->background_color()); | 548 canvas->DrawColor(border_->background_color()); |
| 532 | 549 |
| 533 // Fill the contents with a round-rect region to match the border images. | 550 // Fill the contents with a round-rect region to match the border images. |
| 534 SkPaint paint; | 551 SkPaint paint; |
| 535 paint.setAntiAlias(true); | 552 paint.setAntiAlias(true); |
| 536 paint.setStyle(SkPaint::kFill_Style); | 553 paint.setStyle(SkPaint::kFill_Style); |
| 537 paint.setColor(border_->background_color()); | 554 paint.setColor(border_->background_color()); |
| 538 SkPath path; | 555 SkPath path; |
| 539 gfx::RectF bounds(view->GetLocalBounds()); | 556 gfx::RectF bounds(view->GetLocalBounds()); |
| 540 bounds.Inset(gfx::InsetsF(border_->GetInsets())); | 557 bounds.Inset(gfx::InsetsF(border_->GetInsets())); |
| 541 if (UseMd()) { | |
| 542 // The border is 1px at all scale factors. Leave room for it. It's partially | |
| 543 // transparent, so we don't want to draw any background underneath it. | |
| 544 const SkScalar one_pixel = SkFloatToScalar(1 / canvas->image_scale()); | |
| 545 bounds.Inset(gfx::InsetsF(one_pixel)); | |
| 546 } | |
| 547 | 558 |
| 548 canvas->DrawRoundRect(bounds, border_->GetBorderCornerRadius(), paint); | 559 canvas->DrawRoundRect(bounds, border_->GetBorderCornerRadius(), paint); |
| 549 } | 560 } |
| 550 | 561 |
| 551 } // namespace views | 562 } // namespace views |
| OLD | NEW |