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

Side by Side Diff: chrome/browser/views/status_bubble_views.cc

Issue 149474: Expand status bubble to accommodate long URLs.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 7 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 | « chrome/browser/views/status_bubble_views.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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/views/status_bubble_views.h" 5 #include "chrome/browser/views/status_bubble_views.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "app/animation.h" 9 #include "app/animation.h"
10 #include "app/l10n_util.h" 10 #include "app/l10n_util.h"
11 #include "app/resource_bundle.h" 11 #include "app/resource_bundle.h"
12 #include "app/text_elider.h" 12 #include "app/text_elider.h"
13 #include "base/i18n/rtl.h" 13 #include "base/i18n/rtl.h"
14 #include "base/message_loop.h" 14 #include "base/message_loop.h"
15 #include "base/string_util.h" 15 #include "base/string_util.h"
16 #include "chrome/browser/browser_theme_provider.h" 16 #include "chrome/browser/browser_theme_provider.h"
17 #include "gfx/canvas.h" 17 #include "gfx/canvas.h"
18 #include "gfx/point.h" 18 #include "gfx/point.h"
19 #include "googleurl/src/gurl.h" 19 #include "googleurl/src/gurl.h"
20 #include "grit/generated_resources.h" 20 #include "grit/generated_resources.h"
21 #include "grit/theme_resources.h" 21 #include "grit/theme_resources.h"
22 #include "net/base/net_util.h" 22 #include "net/base/net_util.h"
23 #include "third_party/skia/include/core/SkPaint.h" 23 #include "third_party/skia/include/core/SkPaint.h"
24 #include "third_party/skia/include/core/SkPath.h" 24 #include "third_party/skia/include/core/SkPath.h"
25 #include "third_party/skia/include/core/SkRect.h" 25 #include "third_party/skia/include/core/SkRect.h"
26 #include "views/controls/label.h" 26 #include "views/controls/label.h"
27 #include "views/controls/scrollbar/native_scroll_bar.h"
27 #include "views/screen.h" 28 #include "views/screen.h"
28 #include "views/widget/root_view.h" 29 #include "views/widget/root_view.h"
29 #include "views/widget/widget.h" 30 #include "views/widget/widget.h"
30 #include "views/window/window.h" 31 #include "views/window/window.h"
31 32
32 using views::Widget; 33 using views::Widget;
33 34
34 // The alpha and color of the bubble's shadow. 35 // The alpha and color of the bubble's shadow.
35 static const SkColor kShadowColor = SkColorSetARGB(30, 0, 0, 0); 36 static const SkColor kShadowColor = SkColorSetARGB(30, 0, 0, 0);
36 37
(...skipping 15 matching lines...) Expand all
52 // Delays before we start hiding or showing the bubble after we receive a 53 // Delays before we start hiding or showing the bubble after we receive a
53 // show or hide request. 54 // show or hide request.
54 static const int kShowDelay = 80; 55 static const int kShowDelay = 80;
55 static const int kHideDelay = 250; 56 static const int kHideDelay = 250;
56 57
57 // How long each fade should last for. 58 // How long each fade should last for.
58 static const int kShowFadeDurationMS = 120; 59 static const int kShowFadeDurationMS = 120;
59 static const int kHideFadeDurationMS = 200; 60 static const int kHideFadeDurationMS = 200;
60 static const int kFramerate = 25; 61 static const int kFramerate = 25;
61 62
63 // How long each expansion step should take.
64 static const int kMinExpansionStepDurationMS = 20;
65 static const int kMaxExpansionStepDurationMS = 150;
66
62 // View ----------------------------------------------------------------------- 67 // View -----------------------------------------------------------------------
63 // StatusView manages the display of the bubble, applying text changes and 68 // StatusView manages the display of the bubble, applying text changes and
64 // fading in or out the bubble as required. 69 // fading in or out the bubble as required.
65 class StatusBubbleViews::StatusView : public views::Label, 70 class StatusBubbleViews::StatusView : public views::Label,
66 public Animation, 71 public Animation,
67 public AnimationDelegate { 72 public AnimationDelegate {
68 public: 73 public:
69 StatusView(StatusBubble* status_bubble, views::Widget* popup, 74 StatusView(StatusBubble* status_bubble, views::Widget* popup,
70 ThemeProvider* theme_provider) 75 ThemeProvider* theme_provider)
71 : ALLOW_THIS_IN_INITIALIZER_LIST(Animation(kFramerate, this)), 76 : ALLOW_THIS_IN_INITIALIZER_LIST(Animation(kFramerate, this)),
(...skipping 26 matching lines...) Expand all
98 }; 103 };
99 104
100 enum BubbleStyle { 105 enum BubbleStyle {
101 STYLE_BOTTOM, 106 STYLE_BOTTOM,
102 STYLE_FLOATING, 107 STYLE_FLOATING,
103 STYLE_STANDARD, 108 STYLE_STANDARD,
104 STYLE_STANDARD_RIGHT 109 STYLE_STANDARD_RIGHT
105 }; 110 };
106 111
107 // Set the bubble text to a certain value, hides the bubble if text is 112 // Set the bubble text to a certain value, hides the bubble if text is
108 // an empty string. 113 // an empty string. Do not trigger animation sequence.
109 void SetText(const std::wstring& text); 114 void SetText(const std::wstring& text);
110 115
116 // Set the bubble text to a certain value, or hide the bubble if text is
117 // an empty string. Start animation sequence.
118 void SetTextAndAnimate(const std::wstring& text);
119
111 BubbleStage GetState() const { return stage_; } 120 BubbleStage GetState() const { return stage_; }
112 121
113 void SetStyle(BubbleStyle style); 122 void SetStyle(BubbleStyle style);
114 123
115 BubbleStyle GetStyle() const { return style_; } 124 BubbleStyle GetStyle() const { return style_; }
116 125
117 // Show the bubble instantly. 126 // Show the bubble instantly.
118 void Show(); 127 void Show();
119 128
120 // Hide the bubble instantly. 129 // Hide the bubble instantly.
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
164 // fade-in can easily turn into a fade out, opacity_start_ is sometimes 173 // fade-in can easily turn into a fade out, opacity_start_ is sometimes
165 // a value between 0 and 1. 174 // a value between 0 and 1.
166 double opacity_start_; 175 double opacity_start_;
167 double opacity_end_; 176 double opacity_end_;
168 177
169 // Holds the theme provider of the frame that created us. 178 // Holds the theme provider of the frame that created us.
170 ThemeProvider* theme_provider_; 179 ThemeProvider* theme_provider_;
171 }; 180 };
172 181
173 void StatusBubbleViews::StatusView::SetText(const std::wstring& text) { 182 void StatusBubbleViews::StatusView::SetText(const std::wstring& text) {
183 text_ = text;
184 SchedulePaint();
185 }
186
187 void StatusBubbleViews::StatusView::SetTextAndAnimate(
188 const std::wstring& text) {
174 if (text.empty()) { 189 if (text.empty()) {
175 // The string was empty. 190 // The string was empty.
176 StartHiding(); 191 StartHiding();
177 } else { 192 } else {
178 // We want to show the string. 193 // We want to show the string.
179 text_ = text; 194 text_ = text;
180 StartShowing(); 195 StartShowing();
181 } 196 }
182 197
183 SchedulePaint(); 198 SchedulePaint();
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
439 (SkColorGetB(text_color) + SkColorGetR(toolbar_color)) / 2); 454 (SkColorGetB(text_color) + SkColorGetR(toolbar_color)) / 2);
440 canvas->DrawStringInt(text_, 455 canvas->DrawStringInt(text_,
441 views::Label::font(), 456 views::Label::font(),
442 text_color, 457 text_color,
443 body_bounds.x(), 458 body_bounds.x(),
444 body_bounds.y(), 459 body_bounds.y(),
445 body_bounds.width(), 460 body_bounds.width(),
446 body_bounds.height()); 461 body_bounds.height());
447 } 462 }
448 463
464 // StatusViewExpander ---------------------------------------------------------
465 // Manages the expansion and contraction of the status bubble as it accommodates
466 // URLs too long to fit in the standard bubble. Changes are passed through the
467 // StatusView to paint.
468 class StatusBubbleViews::StatusViewExpander : public Animation,
469 public AnimationDelegate {
470 public:
471 StatusViewExpander(StatusBubble* status_bubble,
472 StatusView* status_view)
473 : ALLOW_THIS_IN_INITIALIZER_LIST(Animation(kFramerate, this)),
474 status_bubble_(status_bubble),
475 status_view_(status_view),
476 expansion_start_(0),
477 expansion_end_(0) {
478 }
479
480 // Manage the expansion of the bubble.
481 void StartExpansion(std::wstring expanded_text, int current_width,
482 int expansion_end);
483
484 // Set width of fully expanded bubble.
485 void SetExpandedWidth(int expanded_width);
486
487 private:
488 // Animation functions.
489 int GetCurrentBubbleWidth();
490 void SetBubbleWidth(int width);
491 void AnimateToState(double state);
492 void AnimationEnded(const Animation* animation);
493
494 // Manager that owns us.
495 StatusBubble* status_bubble_;
496
497 // Change the bounds and text of this view.
498 StatusView* status_view_;
499
500 // Text elided (if needed) to fit maximum status bar width.
501 std::wstring expanded_text_;
502
503 // Widths at expansion start and end.
504 int expansion_start_;
505 int expansion_end_;
506 };
507
508 void StatusBubbleViews::StatusViewExpander::AnimateToState(double state) {
509 SetBubbleWidth(GetCurrentBubbleWidth());
510 }
511
512 void StatusBubbleViews::StatusViewExpander::AnimationEnded(
513 const Animation* animation) {
514 SetBubbleWidth(expansion_end_);
515 status_view_->SetText(expanded_text_);
516 }
517
518 void StatusBubbleViews::StatusViewExpander::StartExpansion(
519 std::wstring expanded_text, int expansion_start,
520 int expansion_end) {
521 expanded_text_ = expanded_text;
522 expansion_start_ = expansion_start;
523 expansion_end_ = expansion_end;
524 int min_duration = std::max(kMinExpansionStepDurationMS,
525 static_cast<int>(kMaxExpansionStepDurationMS *
526 (expansion_end - expansion_start) / 100.0));
527 SetDuration(std::min(kMaxExpansionStepDurationMS, min_duration));
528 Start();
529 }
530
531 int StatusBubbleViews::StatusViewExpander::GetCurrentBubbleWidth() {
532 return static_cast<int>(expansion_start_ +
533 (expansion_end_ - expansion_start_) * Animation::GetCurrentValue());
534 }
535
536 void StatusBubbleViews::StatusViewExpander::SetBubbleWidth(int width) {
537 status_bubble_->SetBubbleWidth(width);
538 status_view_->SchedulePaint();
539 }
540
449 // StatusBubble --------------------------------------------------------------- 541 // StatusBubble ---------------------------------------------------------------
450 542
451 const int StatusBubbleViews::kShadowThickness = 1; 543 const int StatusBubbleViews::kShadowThickness = 1;
452 544
453 StatusBubbleViews::StatusBubbleViews(views::Widget* frame) 545 StatusBubbleViews::StatusBubbleViews(views::Widget* frame)
454 : offset_(0), 546 : offset_(0),
455 popup_(NULL), 547 popup_(NULL),
456 opacity_(0), 548 opacity_(0),
457 frame_(frame), 549 frame_(frame),
458 view_(NULL), 550 view_(NULL),
459 download_shelf_is_visible_(false) { 551 download_shelf_is_visible_(false),
552 is_expanded_(false),
553 ALLOW_THIS_IN_INITIALIZER_LIST(expand_timer_factory_(this)) {
554 expand_view_.reset();
460 } 555 }
461 556
462 StatusBubbleViews::~StatusBubbleViews() { 557 StatusBubbleViews::~StatusBubbleViews() {
558 CancelExpandTimer();
463 if (popup_.get()) 559 if (popup_.get())
464 popup_->CloseNow(); 560 popup_->CloseNow();
465 } 561 }
466 562
467 void StatusBubbleViews::Init() { 563 void StatusBubbleViews::Init() {
468 if (!popup_.get()) { 564 if (!popup_.get()) {
469 popup_.reset(Widget::CreatePopupWidget(Widget::Transparent, 565 popup_.reset(Widget::CreatePopupWidget(Widget::Transparent,
470 Widget::NotAcceptEvents, 566 Widget::NotAcceptEvents,
471 Widget::NotDeleteOnDestroy)); 567 Widget::NotDeleteOnDestroy));
472 if (!view_) 568 if (!view_)
473 view_ = new StatusView(this, popup_.get(), frame_->GetThemeProvider()); 569 view_ = new StatusView(this, popup_.get(), frame_->GetThemeProvider());
570 if (!expand_view_.get())
571 expand_view_.reset(new StatusViewExpander(this, view_));
474 popup_->SetOpacity(0x00); 572 popup_->SetOpacity(0x00);
475 popup_->Init(frame_->GetNativeView(), gfx::Rect()); 573 popup_->Init(frame_->GetNativeView(), gfx::Rect());
476 popup_->SetContentsView(view_); 574 popup_->SetContentsView(view_);
477 Reposition(); 575 Reposition();
478 popup_->Show(); 576 popup_->Show();
479 } 577 }
480 } 578 }
481 579
482 void StatusBubbleViews::Reposition() { 580 void StatusBubbleViews::Reposition() {
483 if (popup_.get()) { 581 if (popup_.get()) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
517 615
518 if (status_text_ == status_text) 616 if (status_text_ == status_text)
519 return; 617 return;
520 618
521 if (!IsFrameVisible()) 619 if (!IsFrameVisible())
522 return; // Don't show anything if the parent isn't visible. 620 return; // Don't show anything if the parent isn't visible.
523 621
524 Init(); 622 Init();
525 status_text_ = status_text; 623 status_text_ = status_text;
526 if (!status_text_.empty()) { 624 if (!status_text_.empty()) {
527 view_->SetText(status_text); 625 view_->SetTextAndAnimate(status_text);
528 view_->Show(); 626 view_->Show();
529 } else if (!url_text_.empty()) { 627 } else if (!url_text_.empty()) {
530 view_->SetText(url_text_); 628 view_->SetTextAndAnimate(url_text_);
531 } else { 629 } else {
532 view_->SetText(std::wstring()); 630 view_->SetTextAndAnimate(std::wstring());
533 } 631 }
534 } 632 }
535 633
536 void StatusBubbleViews::SetURL(const GURL& url, const std::wstring& languages) { 634 void StatusBubbleViews::SetURL(const GURL& url, const std::wstring& languages) {
635 languages_ = languages;
636 url_ = url;
537 if (size_.IsEmpty()) 637 if (size_.IsEmpty())
538 return; // We have no bounds, don't attempt to show the popup. 638 return; // We have no bounds, don't attempt to show the popup.
539 639
540 Init(); 640 Init();
541 641
542 // If we want to clear a displayed URL but there is a status still to 642 // If we want to clear a displayed URL but there is a status still to
543 // display, display that status instead. 643 // display, display that status instead.
544 if (url.is_empty() && !status_text_.empty()) { 644 if (url.is_empty() && !status_text_.empty()) {
545 url_text_ = std::wstring(); 645 url_text_ = std::wstring();
546 if (IsFrameVisible()) 646 if (IsFrameVisible())
547 view_->SetText(status_text_); 647 view_->SetTextAndAnimate(status_text_);
548 return; 648 return;
549 } 649 }
550 650
651 // Reset expansion state only when bubble is completely hidden.
652 if (view_->GetState() == StatusView::BUBBLE_HIDDEN) {
653 is_expanded_ = false;
654 SetBubbleWidth(GetStandardStatusBubbleWidth());
655 }
656
551 // Set Elided Text corresponding to the GURL object. 657 // Set Elided Text corresponding to the GURL object.
552 gfx::Rect popup_bounds; 658 gfx::Rect popup_bounds;
553 popup_->GetBounds(&popup_bounds, true); 659 popup_->GetBounds(&popup_bounds, true);
554 int text_width = static_cast<int>(popup_bounds.width() - 660 int text_width = static_cast<int>(popup_bounds.width() -
555 (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1); 661 (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1);
556 url_text_ = gfx::ElideUrl(url, view_->Label::font(), text_width, 662 url_text_ = gfx::ElideUrl(url, view_->Label::font(), text_width,
557 languages); 663 languages);
664
665 std::wstring original_url_text = net::FormatUrl(url, languages);
558 666
559 // An URL is always treated as a left-to-right string. On right-to-left UIs 667 // An URL is always treated as a left-to-right string. On right-to-left UIs
560 // we need to explicitly mark the URL as LTR to make sure it is displayed 668 // we need to explicitly mark the URL as LTR to make sure it is displayed
561 // correctly. 669 // correctly.
562 if (base::i18n::IsRTL() && !url_text_.empty()) 670 if (base::i18n::IsRTL() && !url_text_.empty())
563 base::i18n::WrapStringWithLTRFormatting(&url_text_); 671 base::i18n::WrapStringWithLTRFormatting(&url_text_);
564 672
565 if (IsFrameVisible()) 673 if (IsFrameVisible()) {
566 view_->SetText(url_text_); 674 view_->SetTextAndAnimate(url_text_);
675
676 CancelExpandTimer();
677
678 // If bubble is already in expanded state, shift to adjust to new text
679 // size (shrinking or expanding). Otherwise delay.
680 if (is_expanded_ && !url.is_empty())
681 ExpandBubble();
682 else if (original_url_text.length() > url_text_.length())
683 MessageLoop::current()->PostDelayedTask(FROM_HERE,
684 expand_timer_factory_.NewRunnableMethod(
685 &StatusBubbleViews::ExpandBubble), kExpandHoverDelay);
686 }
567 } 687 }
568 688
569 void StatusBubbleViews::Hide() { 689 void StatusBubbleViews::Hide() {
570 status_text_ = std::wstring(); 690 status_text_ = std::wstring();
571 url_text_ = std::wstring(); 691 url_text_ = std::wstring();
572 if (view_) 692 if (view_)
573 view_->Hide(); 693 view_->Hide();
574 } 694 }
575 695
576 void StatusBubbleViews::MouseMoved(const gfx::Point& location, 696 void StatusBubbleViews::MouseMoved(const gfx::Point& location,
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
679 } 799 }
680 } 800 }
681 801
682 bool StatusBubbleViews::IsFrameVisible() { 802 bool StatusBubbleViews::IsFrameVisible() {
683 if (!frame_->IsVisible()) 803 if (!frame_->IsVisible())
684 return false; 804 return false;
685 805
686 views::Window* window = frame_->GetWindow(); 806 views::Window* window = frame_->GetWindow();
687 return !window || !window->IsMinimized(); 807 return !window || !window->IsMinimized();
688 } 808 }
809
810 void StatusBubbleViews::ExpandBubble() {
811 // Elide URL to maximum possible size, then check actual length (it may
812 // still be too long to fit) before expanding bubble.
813 gfx::Rect popup_bounds;
814 popup_->GetBounds(&popup_bounds, true);
815 int max_status_bubble_width = GetMaxStatusBubbleWidth();
816 url_text_ = gfx::ElideUrl(url_, view_->Label::font(),
817 max_status_bubble_width, languages_);
818 int expanded_bubble_width =
819 std::max(GetStandardStatusBubbleWidth(),
820 std::min(view_->Label::font().GetStringWidth(url_text_) +
821 (kShadowThickness * 2) + kTextPositionX +
822 kTextHorizPadding + 1,
823 max_status_bubble_width));
824 is_expanded_ = true;
825 expand_view_->StartExpansion(url_text_, popup_bounds.width(),
826 expanded_bubble_width);
827 }
828
829 int StatusBubbleViews::GetStandardStatusBubbleWidth() {
830 gfx::Rect frame_bounds;
831 frame_->GetBounds(&frame_bounds, false);
832 return frame_bounds.width() / 3;
833 }
834
835 int StatusBubbleViews::GetMaxStatusBubbleWidth() {
836 gfx::Rect frame_bounds;
837 frame_->GetBounds(&frame_bounds, false);
838 return static_cast<int>(frame_bounds.width() - (kShadowThickness * 2) -
839 kTextPositionX - kTextHorizPadding - 1 -
840 views::NativeScrollBar::GetVerticalScrollBarWidth());
841 }
842
843 void StatusBubbleViews::SetBubbleWidth(int width) {
844 size_.set_width(width);
845 SetBounds(position_.x(), position_.y(), size_.width(), size_.height());
846 }
847
848 void StatusBubbleViews::CancelExpandTimer() {
849 if (!expand_timer_factory_.empty())
850 expand_timer_factory_.RevokeAll();
851 }
OLDNEW
« no previous file with comments | « chrome/browser/views/status_bubble_views.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698