Chromium Code Reviews| 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 "chrome/browser/ui/views/status_bubble_views.h" | 5 #include "chrome/browser/ui/views/status_bubble_views.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/i18n/rtl.h" | 10 #include "base/i18n/rtl.h" |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 128 }; | 128 }; |
| 129 | 129 |
| 130 enum BubbleStyle { | 130 enum BubbleStyle { |
| 131 STYLE_BOTTOM, | 131 STYLE_BOTTOM, |
| 132 STYLE_FLOATING, | 132 STYLE_FLOATING, |
| 133 STYLE_STANDARD, | 133 STYLE_STANDARD, |
| 134 STYLE_STANDARD_RIGHT | 134 STYLE_STANDARD_RIGHT |
| 135 }; | 135 }; |
| 136 | 136 |
| 137 StatusView(views::Widget* popup, | 137 StatusView(views::Widget* popup, |
| 138 const ui::ThemeProvider* theme_provider); | 138 const ui::ThemeProvider* theme_provider, |
| 139 const BrowserView* browser_view); | |
| 139 ~StatusView() override; | 140 ~StatusView() override; |
| 140 | 141 |
| 141 // Set the bubble text to a certain value, hides the bubble if text is | 142 // Set the bubble text to a certain value, hides the bubble if text is |
| 142 // an empty string. Trigger animation sequence to display if | 143 // an empty string. Trigger animation sequence to display if |
| 143 // |should_animate_open|. | 144 // |should_animate_open|. |
| 144 void SetText(const base::string16& text, bool should_animate_open); | 145 void SetText(const base::string16& text, bool should_animate_open); |
| 145 | 146 |
| 146 BubbleState state() const { return state_; } | 147 BubbleState state() const { return state_; } |
| 147 BubbleStyle style() const { return style_; } | 148 BubbleStyle style() const { return style_; } |
| 148 void SetStyle(BubbleStyle style); | 149 void SetStyle(BubbleStyle style); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 189 | 190 |
| 190 // Handle to the widget that contains us. | 191 // Handle to the widget that contains us. |
| 191 views::Widget* popup_; | 192 views::Widget* popup_; |
| 192 | 193 |
| 193 // The currently-displayed text. | 194 // The currently-displayed text. |
| 194 base::string16 text_; | 195 base::string16 text_; |
| 195 | 196 |
| 196 // Holds the theme provider of the frame that created us. | 197 // Holds the theme provider of the frame that created us. |
| 197 const ui::ThemeProvider* theme_provider_; | 198 const ui::ThemeProvider* theme_provider_; |
| 198 | 199 |
| 200 const BrowserView* browser_view_; | |
| 201 | |
| 199 base::WeakPtrFactory<StatusBubbleViews::StatusView> timer_factory_; | 202 base::WeakPtrFactory<StatusBubbleViews::StatusView> timer_factory_; |
| 200 | 203 |
| 201 DISALLOW_COPY_AND_ASSIGN(StatusView); | 204 DISALLOW_COPY_AND_ASSIGN(StatusView); |
| 202 }; | 205 }; |
| 203 | 206 |
| 204 StatusBubbleViews::StatusView::StatusView( | 207 StatusBubbleViews::StatusView::StatusView( |
| 205 views::Widget* popup, | 208 views::Widget* popup, |
| 206 const ui::ThemeProvider* theme_provider) | 209 const ui::ThemeProvider* theme_provider, |
| 210 const BrowserView* browser_view) | |
| 207 : state_(BUBBLE_HIDDEN), | 211 : state_(BUBBLE_HIDDEN), |
| 208 style_(STYLE_STANDARD), | 212 style_(STYLE_STANDARD), |
| 209 animation_(new StatusViewAnimation(this, 0, 0)), | 213 animation_(new StatusViewAnimation(this, 0, 0)), |
| 210 popup_(popup), | 214 popup_(popup), |
| 211 theme_provider_(theme_provider), | 215 theme_provider_(theme_provider), |
| 216 browser_view_(browser_view), | |
| 212 timer_factory_(this) {} | 217 timer_factory_(this) {} |
| 213 | 218 |
| 214 StatusBubbleViews::StatusView::~StatusView() { | 219 StatusBubbleViews::StatusView::~StatusView() { |
| 215 animation_->Stop(); | 220 animation_->Stop(); |
| 216 CancelTimer(); | 221 CancelTimer(); |
| 217 } | 222 } |
| 218 | 223 |
| 219 void StatusBubbleViews::StatusView::SetText(const base::string16& text, | 224 void StatusBubbleViews::StatusView::SetText(const base::string16& text, |
| 220 bool should_animate_open) { | 225 bool should_animate_open) { |
| 221 if (text.empty()) { | 226 if (text.empty()) { |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 359 } else if (state_ == BUBBLE_SHOWING_FADE) { | 364 } else if (state_ == BUBBLE_SHOWING_FADE) { |
| 360 state_ = BUBBLE_SHOWN; | 365 state_ = BUBBLE_SHOWN; |
| 361 } | 366 } |
| 362 } | 367 } |
| 363 | 368 |
| 364 const char* StatusBubbleViews::StatusView::GetClassName() const { | 369 const char* StatusBubbleViews::StatusView::GetClassName() const { |
| 365 return "StatusBubbleViews::StatusView"; | 370 return "StatusBubbleViews::StatusView"; |
| 366 } | 371 } |
| 367 | 372 |
| 368 void StatusBubbleViews::StatusView::OnPaint(gfx::Canvas* canvas) { | 373 void StatusBubbleViews::StatusView::OnPaint(gfx::Canvas* canvas) { |
| 369 SkPaint paint; | |
| 370 paint.setStyle(SkPaint::kFill_Style); | |
| 371 paint.setAntiAlias(true); | |
| 372 SkColor toolbar_color = theme_provider_->GetColor( | |
| 373 ThemeProperties::COLOR_TOOLBAR); | |
| 374 paint.setColor(toolbar_color); | |
| 375 | 374 |
| 376 gfx::Rect popup_bounds = popup_->GetWindowBoundsInScreen(); | 375 gfx::Rect popup_bounds = popup_->GetWindowBoundsInScreen(); |
|
Peter Kasting
2016/08/13 06:13:07
Nit: I would avoid GetWindowBoundsInScreen() here
Bret
2016/08/18 18:58:04
I played around with this and I think GetWindowBou
| |
| 377 | 376 |
| 377 canvas->Save(); | |
| 378 float scale = canvas->UndoDeviceScaleFactor(); | |
| 379 const int radius = std::round(kBubbleCornerRadius * scale); | |
|
Peter Kasting
2016/08/13 06:13:07
Seems like we should just use a float, not round.
Bret
2016/08/18 18:58:03
Done.
| |
| 380 | |
| 378 SkScalar rad[8] = {}; | 381 SkScalar rad[8] = {}; |
| 379 | 382 |
| 380 // Top Edges - if the bubble is in its bottom position (sticking downwards), | 383 // Top Edges - if the bubble is in its bottom position (sticking downwards), |
| 381 // then we square the top edges. Otherwise, we square the edges based on the | 384 // then we square the top edges. Otherwise, we square the edges based on the |
| 382 // position of the bubble within the window (the bubble is positioned in the | 385 // position of the bubble within the window (the bubble is positioned in the |
| 383 // southeast corner in RTL and in the southwest corner in LTR). | 386 // southeast corner in RTL and in the southwest corner in LTR). |
| 384 if (style_ != STYLE_BOTTOM) { | 387 if (style_ != STYLE_BOTTOM) { |
| 385 if (base::i18n::IsRTL() != (style_ == STYLE_STANDARD_RIGHT)) { | 388 if (base::i18n::IsRTL() != (style_ == STYLE_STANDARD_RIGHT)) { |
| 386 // The text is RtL or the bubble is on the right side (but not both). | 389 // The text is RtL or the bubble is on the right side (but not both). |
| 387 | 390 |
| 388 // Top Left corner. | 391 // Top Left corner. |
| 389 rad[0] = kBubbleCornerRadius; | 392 rad[0] = radius; |
| 390 rad[1] = kBubbleCornerRadius; | 393 rad[1] = radius; |
| 391 } else { | 394 } else { |
| 392 // Top Right corner. | 395 // Top Right corner. |
| 393 rad[2] = kBubbleCornerRadius; | 396 rad[2] = radius; |
| 394 rad[3] = kBubbleCornerRadius; | 397 rad[3] = radius; |
| 395 } | 398 } |
| 396 } | 399 } |
| 397 | 400 |
| 398 // Bottom edges - Keep these squared off if the bubble is in its standard | 401 // Bottom edges - Keep these squared off if the bubble is in its standard |
| 399 // position (sticking upward). | 402 // position (sticking upward). |
| 400 if (style_ != STYLE_STANDARD && style_ != STYLE_STANDARD_RIGHT) { | 403 if (style_ != STYLE_STANDARD && style_ != STYLE_STANDARD_RIGHT) { |
| 401 // Bottom Right Corner. | 404 // Bottom Right Corner. |
| 402 rad[4] = kBubbleCornerRadius; | 405 rad[4] = radius; |
| 403 rad[5] = kBubbleCornerRadius; | 406 rad[5] = radius; |
| 404 | 407 |
| 405 // Bottom Left Corner. | 408 // Bottom Left Corner. |
| 406 rad[6] = kBubbleCornerRadius; | 409 rad[6] = radius; |
| 407 rad[7] = kBubbleCornerRadius; | 410 rad[7] = radius; |
| 408 } | 411 } |
| 409 | 412 |
| 410 // Draw the bubble's shadow. | 413 const int width = std::round(popup_bounds.width() * scale); |
| 411 int width = popup_bounds.width(); | 414 const int height = std::round(popup_bounds.height() * scale); |
|
Peter Kasting
2016/08/13 06:13:07
Similarly, I think we should use floats here and R
Bret
2016/08/18 18:58:04
Done.
| |
| 412 int height = popup_bounds.height(); | 415 |
| 413 gfx::Rect rect(gfx::Rect(popup_bounds.size())); | 416 if (!browser_view_->HasClientEdge()) { |
|
Peter Kasting
2016/08/13 06:13:07
This function's return value isn't going to change
Bret
2016/08/18 18:58:04
Ok, I was thinking the value could change over the
| |
| 417 // Clip out the edges when the bubble is docked so that it doesn't draw over | |
| 418 // the window border, since there's no client edge. But we do want the | |
| 419 // border when the bubble is floating so it looks complete. | |
| 420 const int clip_left = style_ == STYLE_STANDARD ? 1 : 0; | |
| 421 const bool clip_right = style_ == STYLE_STANDARD_RIGHT ? 1 : 0; | |
| 422 const bool clip_bottom = clip_left || clip_right ? 1 : 0; | |
| 423 gfx::Rect clip_rect(clip_left, 0, width - clip_right, height - clip_bottom); | |
| 424 canvas->ClipRect(clip_rect); | |
| 425 } | |
| 426 | |
| 427 // Draw the shadow that borders the bubble. | |
| 428 gfx::Rect bubble_rect(width, height); | |
| 429 SkRRect rrect; | |
| 430 rrect.setRectRadii(RectToSkRect(bubble_rect), (const SkVector*)rad); | |
|
Peter Kasting
2016/08/13 06:13:07
The reason you're having layout issues at higher D
Bret
2016/08/18 18:58:04
I changed the painting to work the same way as Bac
| |
| 431 | |
| 414 SkPaint shadow_paint; | 432 SkPaint shadow_paint; |
|
Peter Kasting
2016/08/13 06:13:07
Nit: No need to use a different SkPaint here than
Bret
2016/08/18 18:58:04
Done.
| |
| 415 shadow_paint.setAntiAlias(true); | 433 shadow_paint.setAntiAlias(true); |
| 416 shadow_paint.setColor(kShadowColor); | 434 shadow_paint.setColor(kShadowColor); |
| 417 | |
| 418 SkRRect rrect; | |
| 419 rrect.setRectRadii(RectToSkRect(rect), (const SkVector*)rad); | |
| 420 canvas->sk_canvas()->drawRRect(rrect, shadow_paint); | 435 canvas->sk_canvas()->drawRRect(rrect, shadow_paint); |
| 421 | 436 |
| 422 const int shadow_size = 2 * kShadowThickness; | |
| 423 // Draw the bubble. | 437 // Draw the bubble. |
| 424 rect.SetRect(SkIntToScalar(kShadowThickness), SkIntToScalar(kShadowThickness), | 438 bubble_rect.Inset(kShadowThickness, kShadowThickness); |
| 425 SkIntToScalar(width - shadow_size), | 439 rrect.setRectRadii(RectToSkRect(bubble_rect), (const SkVector*)rad); |
|
Peter Kasting
2016/08/13 06:13:07
This is another place that's incorrect, and should
Bret
2016/08/18 18:58:04
Done. See above comment.
| |
| 426 SkIntToScalar(height - shadow_size)); | 440 |
| 427 rrect.setRectRadii(RectToSkRect(rect), (const SkVector*)rad); | 441 SkPaint paint; |
| 442 paint.setStyle(SkPaint::kFill_Style); | |
| 443 paint.setAntiAlias(true); | |
| 444 SkColor toolbar_color = | |
| 445 theme_provider_->GetColor(ThemeProperties::COLOR_TOOLBAR); | |
| 446 paint.setColor(toolbar_color); | |
| 428 canvas->sk_canvas()->drawRRect(rrect, paint); | 447 canvas->sk_canvas()->drawRRect(rrect, paint); |
| 429 | 448 |
| 449 canvas->Restore(); | |
| 450 | |
| 430 // Compute text bounds. | 451 // Compute text bounds. |
| 431 const gfx::FontList font_list; | 452 gfx::Rect text_rect(popup_bounds.width() - kTextHorizPadding, |
| 432 int text_width = | 453 popup_bounds.height()); |
| 433 std::min(gfx::GetStringWidth(text_, font_list), | 454 text_rect.Inset(kShadowThickness, kShadowThickness); |
| 434 width - shadow_size - kTextPositionX - kTextHorizPadding); | 455 text_rect.Inset(kTextPositionX, 0); |
| 435 int text_height = height - shadow_size; | |
| 436 gfx::Rect text_bounds(kShadowThickness + kTextPositionX, | |
| 437 kShadowThickness, | |
| 438 std::max(0, text_width), | |
| 439 std::max(0, text_height)); | |
| 440 // Make sure the text is aligned to the right on RTL UIs. | 456 // Make sure the text is aligned to the right on RTL UIs. |
| 441 text_bounds.set_x(GetMirroredXForRect(text_bounds)); | 457 text_rect.set_x(GetMirroredXForRect(text_rect)); |
| 442 | 458 |
| 443 // Text color is the foreground tab text color at 60% alpha. | 459 // Text color is the foreground tab text color at 60% alpha. |
| 444 SkColor text_color = color_utils::AlphaBlend( | 460 SkColor text_color = color_utils::AlphaBlend( |
| 445 theme_provider_->GetColor(ThemeProperties::COLOR_TAB_TEXT), toolbar_color, | 461 theme_provider_->GetColor(ThemeProperties::COLOR_TAB_TEXT), toolbar_color, |
| 446 0x99); | 462 0x99); |
| 463 const gfx::FontList font_list; | |
|
Peter Kasting
2016/08/13 06:13:07
Nit: Can we just pass FontList() in the call below
Bret
2016/08/18 18:58:04
Done.
| |
| 447 canvas->DrawStringRect( | 464 canvas->DrawStringRect( |
| 448 text_, font_list, | 465 text_, font_list, |
| 449 color_utils::GetReadableColor(text_color, toolbar_color), text_bounds); | 466 color_utils::GetReadableColor(text_color, toolbar_color), text_rect); |
| 450 } | 467 } |
| 451 | 468 |
| 452 | 469 |
| 453 // StatusBubbleViews::StatusViewAnimation -------------------------------------- | 470 // StatusBubbleViews::StatusViewAnimation -------------------------------------- |
| 454 | 471 |
| 455 StatusBubbleViews::StatusViewAnimation::StatusViewAnimation( | 472 StatusBubbleViews::StatusViewAnimation::StatusViewAnimation( |
| 456 StatusView* status_view, | 473 StatusView* status_view, |
| 457 float opacity_start, | 474 float opacity_start, |
| 458 float opacity_end) | 475 float opacity_end) |
| 459 : gfx::LinearAnimation(kFramerate, this), | 476 : gfx::LinearAnimation(kFramerate, this), |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 562 void StatusBubbleViews::StatusViewExpander::SetBubbleWidth(int width) { | 579 void StatusBubbleViews::StatusViewExpander::SetBubbleWidth(int width) { |
| 563 status_bubble_->SetBubbleWidth(width); | 580 status_bubble_->SetBubbleWidth(width); |
| 564 status_view_->SchedulePaint(); | 581 status_view_->SchedulePaint(); |
| 565 } | 582 } |
| 566 | 583 |
| 567 | 584 |
| 568 // StatusBubbleViews ----------------------------------------------------------- | 585 // StatusBubbleViews ----------------------------------------------------------- |
| 569 | 586 |
| 570 const int StatusBubbleViews::kShadowThickness = 1; | 587 const int StatusBubbleViews::kShadowThickness = 1; |
| 571 | 588 |
| 572 StatusBubbleViews::StatusBubbleViews(views::View* base_view) | 589 StatusBubbleViews::StatusBubbleViews(const BrowserView* browser_view, |
| 590 views::View* base_view) | |
| 573 : contains_mouse_(false), | 591 : contains_mouse_(false), |
| 574 offset_(0), | 592 offset_(0), |
| 593 browser_view_(browser_view), | |
| 575 base_view_(base_view), | 594 base_view_(base_view), |
| 576 view_(NULL), | 595 view_(NULL), |
| 577 download_shelf_is_visible_(false), | 596 download_shelf_is_visible_(false), |
| 578 is_expanded_(false), | 597 is_expanded_(false), |
| 579 expand_timer_factory_(this) { | 598 expand_timer_factory_(this) { |
| 580 expand_view_.reset(); | 599 expand_view_.reset(); |
| 581 } | 600 } |
| 582 | 601 |
| 583 StatusBubbleViews::~StatusBubbleViews() { | 602 StatusBubbleViews::~StatusBubbleViews() { |
| 584 CancelExpandTimer(); | 603 CancelExpandTimer(); |
| 585 if (popup_.get()) | 604 if (popup_.get()) |
| 586 popup_->CloseNow(); | 605 popup_->CloseNow(); |
| 587 } | 606 } |
| 588 | 607 |
| 589 void StatusBubbleViews::Init() { | 608 void StatusBubbleViews::Init() { |
| 590 if (!popup_.get()) { | 609 if (!popup_.get()) { |
| 591 popup_.reset(new views::Widget); | 610 popup_.reset(new views::Widget); |
| 592 views::Widget* frame = base_view_->GetWidget(); | 611 views::Widget* frame = base_view_->GetWidget(); |
| 593 if (!view_) | 612 if (!view_) |
| 594 view_ = new StatusView(popup_.get(), frame->GetThemeProvider()); | 613 view_ = new StatusView(popup_.get(), frame->GetThemeProvider(), |
| 614 browser_view_); | |
| 595 if (!expand_view_.get()) | 615 if (!expand_view_.get()) |
| 596 expand_view_.reset(new StatusViewExpander(this, view_)); | 616 expand_view_.reset(new StatusViewExpander(this, view_)); |
| 597 | 617 |
| 598 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); | 618 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); |
| 599 #if defined(OS_WIN) | 619 #if defined(OS_WIN) |
| 600 // On Windows use the software compositor to ensure that we don't block | 620 // On Windows use the software compositor to ensure that we don't block |
| 601 // the UI thread blocking issue during command buffer creation. We can | 621 // the UI thread blocking issue during command buffer creation. We can |
| 602 // revert this change once http://crbug.com/125248 is fixed. | 622 // revert this change once http://crbug.com/125248 is fixed. |
| 603 params.force_software_compositing = true; | 623 params.force_software_compositing = true; |
| 604 #endif | 624 #endif |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 619 popup_->SetContentsView(view_); | 639 popup_->SetContentsView(view_); |
| 620 #if defined(USE_ASH) | 640 #if defined(USE_ASH) |
| 621 ash::wm::GetWindowState(popup_->GetNativeWindow())-> | 641 ash::wm::GetWindowState(popup_->GetNativeWindow())-> |
| 622 set_ignored_by_shelf(true); | 642 set_ignored_by_shelf(true); |
| 623 #endif | 643 #endif |
| 624 RepositionPopup(); | 644 RepositionPopup(); |
| 625 } | 645 } |
| 626 } | 646 } |
| 627 | 647 |
| 628 void StatusBubbleViews::Reposition() { | 648 void StatusBubbleViews::Reposition() { |
| 629 // In restored mode, the client area has a client edge between it and the | 649 // Overlap the client edge that's shown in restored mode, or when there is no |
| 630 // frame. | 650 // client edge this makes the bubble snug with the corner of the window. |
| 631 int overlap = kShadowThickness; | 651 int overlap = kShadowThickness; |
| 632 int height = GetPreferredSize().height(); | 652 int height = GetPreferredSize().height(); |
| 633 int base_view_height = base_view()->bounds().height(); | 653 int base_view_height = base_view()->bounds().height(); |
| 634 gfx::Point origin(-overlap, base_view_height - height + overlap); | 654 gfx::Point origin(-overlap, base_view_height - height + overlap); |
| 635 SetBounds(origin.x(), origin.y(), base_view()->bounds().width() / 3, height); | 655 SetBounds(origin.x(), origin.y(), base_view()->bounds().width() / 3, height); |
| 636 } | 656 } |
| 637 | 657 |
| 638 void StatusBubbleViews::RepositionPopup() { | 658 void StatusBubbleViews::RepositionPopup() { |
| 639 if (popup_.get()) { | 659 if (popup_.get()) { |
| 640 gfx::Point top_left; | 660 gfx::Point top_left; |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 901 void StatusBubbleViews::SetBubbleWidth(int width) { | 921 void StatusBubbleViews::SetBubbleWidth(int width) { |
| 902 size_.set_width(width); | 922 size_.set_width(width); |
| 903 SetBounds(original_position_.x(), original_position_.y(), | 923 SetBounds(original_position_.x(), original_position_.y(), |
| 904 size_.width(), size_.height()); | 924 size_.width(), size_.height()); |
| 905 } | 925 } |
| 906 | 926 |
| 907 void StatusBubbleViews::CancelExpandTimer() { | 927 void StatusBubbleViews::CancelExpandTimer() { |
| 908 if (expand_timer_factory_.HasWeakPtrs()) | 928 if (expand_timer_factory_.HasWeakPtrs()) |
| 909 expand_timer_factory_.InvalidateWeakPtrs(); | 929 expand_timer_factory_.InvalidateWeakPtrs(); |
| 910 } | 930 } |
| OLD | NEW |