diff options
author | pkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-20 06:25:10 +0000 |
---|---|---|
committer | pkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-20 06:25:10 +0000 |
commit | 2a28dc24c68f8cf3cee1b9667f5e51342cc5df04 (patch) | |
tree | 2817ae41fb74d22662a88a21d304dd739082a2e3 | |
parent | 4e62446f597ada42213a573ccc1a683adb41eff6 (diff) | |
download | chromium_src-2a28dc24c68f8cf3cee1b9667f5e51342cc5df04.zip chromium_src-2a28dc24c68f8cf3cee1b9667f5e51342cc5df04.tar.gz chromium_src-2a28dc24c68f8cf3cee1b9667f5e51342cc5df04.tar.bz2 |
Adjust infobar arrow height when the toolbar height changes. This also fixes infobars having thewrong top arrow height in popups.
BUG=76388
TEST=open and close the bookmark bar with an infobar open. The infobar arrow should adjust per the mocks in the bug.
Review URL: http://codereview.chromium.org/6880059
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82251 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc | 44 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/browser_view.cc | 54 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/browser_view_layout.cc | 14 | ||||
-rw-r--r-- | chrome/browser/ui/views/infobars/extension_infobar.cc | 3 | ||||
-rw-r--r-- | chrome/browser/ui/views/infobars/infobar.cc | 62 | ||||
-rw-r--r-- | chrome/browser/ui/views/infobars/infobar.h | 37 | ||||
-rw-r--r-- | chrome/browser/ui/views/infobars/infobar_background.cc | 14 | ||||
-rw-r--r-- | chrome/browser/ui/views/infobars/infobar_background.h | 2 | ||||
-rw-r--r-- | chrome/browser/ui/views/infobars/infobar_container.cc | 35 | ||||
-rw-r--r-- | chrome/browser/ui/views/infobars/infobar_container.h | 21 | ||||
-rw-r--r-- | chrome/browser/ui/views/infobars/infobar_view.cc | 60 | ||||
-rw-r--r-- | chrome/browser/ui/views/infobars/infobar_view.h | 2 |
12 files changed, 241 insertions, 107 deletions
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc index 657a169..e7d78a5 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc @@ -439,8 +439,7 @@ gfx::Size BookmarkBarView::GetMinimumSize() { if (OnNewTabPage()) { double current_state = 1 - size_animation_->GetCurrentValue(); - width += 2 * static_cast<int>(static_cast<double> - (kNewtabHorizontalPadding) * current_state); + width += 2 * static_cast<int>(kNewtabHorizontalPadding * current_state); } int sync_error_total_width = 0; @@ -740,8 +739,7 @@ int BookmarkBarView::GetToolbarOverlap(bool return_max) const { return 0; // When on the New Tab Page with no infobar, animate the overlap between the // attached and detached states. - return static_cast<int>(static_cast<double>(kToolbarOverlap) * - size_animation_->GetCurrentValue()); + return static_cast<int>(kToolbarOverlap * size_animation_->GetCurrentValue()); } bool BookmarkBarView::is_animating() { @@ -1639,21 +1637,23 @@ gfx::Size BookmarkBarView::LayoutItems(bool compute_bounds_only) { int top_margin = IsDetached() ? kDetachedTopMargin : 0; int y = top_margin; int width = View::width() - kRightMargin - kLeftMargin; - int height = View::height() - top_margin - kBottomMargin; + int height = -top_margin - kBottomMargin; int separator_margin = kSeparatorMargin; if (OnNewTabPage()) { double current_state = 1 - size_animation_->GetCurrentValue(); - x += static_cast<int>(static_cast<double> - (kNewtabHorizontalPadding) * current_state); - y += static_cast<int>(static_cast<double> - (kNewtabVerticalPadding) * current_state); - width -= static_cast<int>(static_cast<double> - (kNewtabHorizontalPadding) * current_state); - height -= static_cast<int>(static_cast<double> - (kNewtabVerticalPadding * 2) * current_state); - separator_margin -= static_cast<int>(static_cast<double> - (kSeparatorMargin) * current_state); + x += static_cast<int>(kNewtabHorizontalPadding * current_state); + y += static_cast<int>(kNewtabVerticalPadding * current_state); + width -= static_cast<int>(kNewtabHorizontalPadding * current_state); + height += View::height() - + static_cast<int>(kNewtabVerticalPadding * 2 * current_state); + separator_margin -= static_cast<int>(kSeparatorMargin * current_state); + } else { + // For the attached appearance, pin the content to the bottom of the bar + // when animating in/out, as shrinking its height instead looks weird. This + // also matches how we layout infobars. + y += View::height() - kBarHeight; + height += kBarHeight; } gfx::Size other_bookmarked_pref = @@ -1761,14 +1761,14 @@ gfx::Size BookmarkBarView::LayoutItems(bool compute_bounds_only) { x += kRightMargin; prefsize.set_width(x); if (OnNewTabPage()) { - x += static_cast<int>(static_cast<double>(kNewtabHorizontalPadding) * - (1 - size_animation_->GetCurrentValue())); - prefsize.set_height(kBarHeight + static_cast<int>(static_cast<double> - (kNewtabBarHeight - kBarHeight) * - (1 - size_animation_->GetCurrentValue()))); + x += static_cast<int>( + kNewtabHorizontalPadding * (1 - size_animation_->GetCurrentValue())); + prefsize.set_height(kBarHeight + + static_cast<int>((kNewtabBarHeight - kBarHeight) * + (1 - size_animation_->GetCurrentValue()))); } else { - prefsize.set_height(static_cast<int>(static_cast<double>(kBarHeight) * - size_animation_->GetCurrentValue())); + prefsize.set_height( + static_cast<int>(kBarHeight * size_animation_->GetCurrentValue())); } } return prefsize; diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 08c035b..e52e0c1 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc @@ -8,6 +8,7 @@ #include <gtk/gtk.h> #endif +#include "base/auto_reset.h" #include "base/command_line.h" #include "base/i18n/rtl.h" #include "base/metrics/histogram.h" @@ -614,17 +615,51 @@ StatusBubble* BrowserView::GetStatusBubble() { return status_bubble_.get(); } +namespace { + // Only used by ToolbarSizeChanged() below, but placed here because template + // arguments (to AutoReset<>) must have external linkage. + enum CallState { NORMAL, REENTRANT, REENTRANT_FORCE_FAST_RESIZE }; +} + void BrowserView::ToolbarSizeChanged(bool is_animating) { - if (is_animating) { + // The call to InfoBarContainer::SetMaxTopArrowHeight() below can result in + // reentrancy; |call_state| tracks whether we're reentrant. We can't just + // early-return in this case because we need to layout again so the infobar + // container's bounds are set correctly. + static CallState call_state = NORMAL; + + // A reentrant call can (and should) use the fast resize path unless both it + // and the normal call are both non-animating. + bool use_fast_resize = + is_animating || (call_state == REENTRANT_FORCE_FAST_RESIZE); + if (use_fast_resize) contents_container_->SetFastResize(true); - UpdateUIForContents(browser_->GetSelectedTabContentsWrapper()); + UpdateUIForContents(browser_->GetSelectedTabContentsWrapper()); + if (use_fast_resize) contents_container_->SetFastResize(false); - } else { - UpdateUIForContents(browser_->GetSelectedTabContentsWrapper()); - // When transitioning from animating to not animating we need to make sure - // the contents_container_ gets layed out. If we don't do this and the - // bounds haven't changed contents_container_ won't get a Layout out and - // we'll end up with a gray rect because the clip wasn't updated. + + // Inform the InfoBarContainer that the distance to the location icon may have + // changed. We have to do this after the block above so that the toolbars are + // laid out correctly for calculating the maximum arrow height below. + AutoReset<CallState> resetter(&call_state, + is_animating ? REENTRANT : REENTRANT_FORCE_FAST_RESIZE); + const LocationIconView* location_icon_view = + toolbar_->location_bar()->location_icon_view(); + // The +1 in the next line creates a 1-px gap between icon and arrow tip. + gfx::Point icon_bottom(0, location_icon_view->GetImageBounds().bottom() - + LocationBarView::kIconInternalPadding + 1); + ConvertPointToView(location_icon_view, this, &icon_bottom); + gfx::Point infobar_top(0, infobar_container_->GetVerticalOverlap(NULL)); + ConvertPointToView(infobar_container_, this, &infobar_top); + infobar_container_->SetMaxTopArrowHeight(infobar_top.y() - icon_bottom.y()); + + // When transitioning from animating to not animating we need to make sure the + // contents_container_ gets layed out. If we don't do this and the bounds + // haven't changed contents_container_ won't get a Layout out and we'll end up + // with a gray rect because the clip wasn't updated. Note that a reentrant + // call never needs to do this, because after it returns, the normal call + // wrapping it will do it. + if ((call_state == NORMAL) && !is_animating) { contents_container_->InvalidateLayout(); contents_split_->Layout(); } @@ -1667,8 +1702,7 @@ void BrowserView::InfoBarContainerHeightChanged(bool is_animating) { bool BrowserView::DrawInfoBarArrows(int* x) const { const LocationIconView* location_icon_view = toolbar_->location_bar()->location_icon_view(); - gfx::Rect icon_bounds = location_icon_view->GetLocalBounds(); - gfx::Point icon_center = icon_bounds.CenterPoint(); + gfx::Point icon_center(location_icon_view->GetImageBounds().CenterPoint()); ConvertPointToView(location_icon_view, this, &icon_center); *x = icon_center.x(); return true; diff --git a/chrome/browser/ui/views/frame/browser_view_layout.cc b/chrome/browser/ui/views/frame/browser_view_layout.cc index 4308939..eae56ab 100644 --- a/chrome/browser/ui/views/frame/browser_view_layout.cc +++ b/chrome/browser/ui/views/frame/browser_view_layout.cc @@ -34,9 +34,6 @@ namespace { const int kTabShadowSize = 2; // The vertical overlap between the TabStrip and the Toolbar. const int kToolbarTabStripVerticalOverlap = 3; -// An offset distance between certain toolbars and the toolbar that preceded -// them in layout. -const int kSeparationLineHeight = 1; } // namespace @@ -74,8 +71,9 @@ gfx::Size BrowserViewLayout::GetMinimumSize() { if (active_bookmark_bar_ && browser()->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR)) { bookmark_bar_size = active_bookmark_bar_->GetMinimumSize(); - bookmark_bar_size.Enlarge(0, -(kSeparationLineHeight + - active_bookmark_bar_->GetToolbarOverlap(true))); + bookmark_bar_size.Enlarge(0, + -(views::NonClientFrameView::kClientEdgeThickness + + active_bookmark_bar_->GetToolbarOverlap(true))); } gfx::Size contents_size(contents_split_->GetMinimumSize()); @@ -353,7 +351,8 @@ int BrowserViewLayout::LayoutBookmarkBar(int top) { active_bookmark_bar_->set_infobar_visible(InfobarVisible()); int bookmark_bar_height = active_bookmark_bar_->GetPreferredSize().height(); - y -= kSeparationLineHeight + active_bookmark_bar_->GetToolbarOverlap(false); + y -= views::NonClientFrameView::kClientEdgeThickness + + active_bookmark_bar_->GetToolbarOverlap(false); active_bookmark_bar_->SetVisible(true); active_bookmark_bar_->SetBounds(vertical_layout_rect_.x(), y, vertical_layout_rect_.width(), @@ -501,7 +500,8 @@ int BrowserViewLayout::GetTopMarginForActiveContent() { } // Adjust for separator. - return active_bookmark_bar_->height() - kSeparationLineHeight; + return active_bookmark_bar_->height() - + views::NonClientFrameView::kClientEdgeThickness; } int BrowserViewLayout::LayoutDownloadShelf(int bottom) { diff --git a/chrome/browser/ui/views/infobars/extension_infobar.cc b/chrome/browser/ui/views/infobars/extension_infobar.cc index 1966636..6892509 100644 --- a/chrome/browser/ui/views/infobars/extension_infobar.cc +++ b/chrome/browser/ui/views/infobars/extension_infobar.cc @@ -43,8 +43,7 @@ ExtensionInfoBar::ExtensionInfoBar(ExtensionInfoBarDelegate* delegate) ExtensionView* extension_view = delegate->extension_host()->view(); int height = extension_view->GetPreferredSize().height(); - SetBarTargetHeight((height > 0) ? - (height + InfoBarBackground::kSeparatorLineHeight) : height); + SetBarTargetHeight((height > 0) ? (height + kSeparatorLineHeight) : 0); // Get notified of resize events for the ExtensionView. extension_view->SetContainer(this); diff --git a/chrome/browser/ui/views/infobars/infobar.cc b/chrome/browser/ui/views/infobars/infobar.cc index 1d32362..49ca2bc 100644 --- a/chrome/browser/ui/views/infobars/infobar.cc +++ b/chrome/browser/ui/views/infobars/infobar.cc @@ -14,9 +14,11 @@ InfoBar::InfoBar(InfoBarDelegate* delegate) : delegate_(delegate), container_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(animation_(new ui::SlideAnimation(this))), - bar_target_height_(kDefaultBarTargetHeight), arrow_height_(0), - bar_height_(0) { + arrow_target_height_(kDefaultArrowTargetHeight), + arrow_half_width_(0), + bar_height_(0), + bar_target_height_(kDefaultBarTargetHeight) { DCHECK(delegate != NULL); animation_->SetTweenType(ui::Tween::LINEAR); } @@ -43,8 +45,18 @@ void InfoBar::Hide(bool animate) { } } +void InfoBar::SetArrowTargetHeight(int height) { + DCHECK_LE(height, kMaximumArrowTargetHeight); + // Once the closing animation starts, we ignore further requests to change the + // target height. + if ((arrow_target_height_ != height) && !animation()->IsClosing()) { + arrow_target_height_ = height; + RecalculateHeights(); + } +} + void InfoBar::AnimationProgressed(const ui::Animation* animation) { - RecalculateHeight(); + RecalculateHeights(); } void InfoBar::RemoveInfoBar() { @@ -55,7 +67,7 @@ void InfoBar::RemoveInfoBar() { void InfoBar::SetBarTargetHeight(int height) { if (bar_target_height_ != height) { bar_target_height_ = height; - RecalculateHeight(); + RecalculateHeights(); } } @@ -66,31 +78,47 @@ int InfoBar::OffsetY(const gfx::Size& prefsize) const { } void InfoBar::AnimationEnded(const ui::Animation* animation) { - RecalculateHeight(); + RecalculateHeights(); MaybeDelete(); } -void InfoBar::RecalculateHeight() { +void InfoBar::RecalculateHeights() { int old_arrow_height = arrow_height_; int old_bar_height = bar_height_; - // The arrow area is |arrow_size| ^ 2. By taking the square root of the - // animation value, we cause a linear animation of the area, which matches the - // perception of the animation of the InfoBar. - arrow_height_ = static_cast<int>(kArrowTargetHeight * - sqrt(animation()->GetCurrentValue())); - // Add one more pixel for the stroke, if the arrow is to be visible at all. - // Without this, changing the arrow height from 0 to 1 would produce no - // visible effect, because the single pixel of stroke would paint atop the - // divider line above the infobar. + + // Find the desired arrow height/half-width. The arrow area is + // |arrow_height_| * |arrow_half_width_|. When the bar is opening or closing, + // scaling each of these with the square root of the animation value causes a + // linear animation of the area, which matches the perception of the animation + // of the bar portion. + double scale_factor = sqrt(animation()->GetCurrentValue()); + arrow_height_ = static_cast<int>(arrow_target_height_ * scale_factor); + if (animation_->is_animating()) { + arrow_half_width_ = static_cast<int>(std::min(arrow_target_height_, + kMaximumArrowTargetHalfWidth) * scale_factor); + } else { + // When the infobar is not animating (i.e. fully open), we set the + // half-width to be proportionally the same distance between its default and + // maximum values as the height is between its. + arrow_half_width_ = kDefaultArrowTargetHalfWidth + + ((kMaximumArrowTargetHalfWidth - kDefaultArrowTargetHalfWidth) * + ((arrow_height_ - kDefaultArrowTargetHeight) / + (kMaximumArrowTargetHeight - kDefaultArrowTargetHeight))); + } + // Add pixels for the stroke, if the arrow is to be visible at all. Without + // this, changing the arrow height from 0 to kSeparatorLineHeight would + // produce no visible effect, because the stroke would paint atop the divider + // line above the infobar. if (arrow_height_) - ++arrow_height_; + arrow_height_ += kSeparatorLineHeight; + bar_height_ = static_cast<int>(bar_target_height_ * animation()->GetCurrentValue()); // Don't re-layout if nothing has changed, e.g. because the animation step was // not large enough to actually change the heights by at least a pixel. if ((old_arrow_height != arrow_height_) || (old_bar_height != bar_height_)) { - PlatformSpecificOnHeightRecalculated(); + PlatformSpecificOnHeightsRecalculated(); if (container_) container_->OnInfoBarHeightChanged(animation_->is_animating()); } diff --git a/chrome/browser/ui/views/infobars/infobar.h b/chrome/browser/ui/views/infobars/infobar.h index ed9250e..c863ce9 100644 --- a/chrome/browser/ui/views/infobars/infobar.h +++ b/chrome/browser/ui/views/infobars/infobar.h @@ -23,6 +23,15 @@ class InfoBar : public ui::AnimationDelegate { explicit InfoBar(InfoBarDelegate* delegate); virtual ~InfoBar(); + // Platforms must define these. + static const int kSeparatorLineHeight; + static const int kDefaultArrowTargetHeight; + static const int kMaximumArrowTargetHeight; + // The half-width (see comments on |arrow_half_width_| below) scales to its + // default and maximum values proportionally to how the height scales to its. + static const int kDefaultArrowTargetHalfWidth; + static const int kMaximumArrowTargetHalfWidth; + InfoBarDelegate* delegate() { return delegate_; } void set_container(InfoBarContainer* container) { container_ = container; } @@ -35,14 +44,16 @@ class InfoBar : public ui::AnimationDelegate { // container (triggering its deletion), and its delegate is closed. void Hide(bool animate); + // Changes the target height of the arrow portion of the infobar. This has no + // effect once the infobar is animating closed. + void SetArrowTargetHeight(int height); + + const ui::SlideAnimation* animation() const { return animation_.get(); } int arrow_height() const { return arrow_height_; } int total_height() const { return arrow_height_ + bar_height_; } protected: - // The target heights of the InfoBar arrow and bar portions, regardless of - // what their current heights are (due to animation). Platforms must define - // these! - static const int kArrowTargetHeight; + // Platforms must define this. static const int kDefaultBarTargetHeight; // ui::AnimationDelegate: @@ -62,13 +73,13 @@ class InfoBar : public ui::AnimationDelegate { const InfoBarContainer* container() const { return container_; } ui::SlideAnimation* animation() { return animation_.get(); } - const ui::SlideAnimation* animation() const { return animation_.get(); } + int arrow_half_width() const { return arrow_half_width_; } int bar_height() const { return bar_height_; } // Platforms may optionally override these if they need to do work during // processing of the given calls. virtual void PlatformSpecificHide(bool animate) {} - virtual void PlatformSpecificOnHeightRecalculated() {} + virtual void PlatformSpecificOnHeightsRecalculated() {} private: // ui::AnimationDelegate: @@ -77,7 +88,7 @@ class InfoBar : public ui::AnimationDelegate { // Finds the new desired arrow and bar heights, and if they differ from the // current ones, calls PlatformSpecificOnHeightRecalculated() and informs our // container our height has changed. - void RecalculateHeight(); + void RecalculateHeights(); // Checks whether we're closed. If so, notifies the container that it should // remove us (which will cause the platform-specific code to asynchronously @@ -88,13 +99,15 @@ class InfoBar : public ui::AnimationDelegate { InfoBarContainer* container_; scoped_ptr<ui::SlideAnimation> animation_; - // The target height for the bar portion of the InfoBarView. + // The current and target heights of the arrow and bar portions, and half the + // current arrow width. (It's easier to work in half-widths as we draw the + // arrow as two halves on either side of a center point.) + int arrow_height_; // Includes both fill and top stroke. + int arrow_target_height_; + int arrow_half_width_; // Includes only fill. + int bar_height_; // Includes both fill and bottom separator. int bar_target_height_; - // The current heights of the arrow and bar portions. - int arrow_height_; - int bar_height_; - DISALLOW_COPY_AND_ASSIGN(InfoBar); }; diff --git a/chrome/browser/ui/views/infobars/infobar_background.cc b/chrome/browser/ui/views/infobars/infobar_background.cc index b6f94f8..fa74322 100644 --- a/chrome/browser/ui/views/infobars/infobar_background.cc +++ b/chrome/browser/ui/views/infobars/infobar_background.cc @@ -10,9 +10,6 @@ #include "third_party/skia/include/effects/SkGradientShader.h" #include "views/view.h" -// static -const int InfoBarBackground::kSeparatorLineHeight = 1; - InfoBarBackground::InfoBarBackground(InfoBarDelegate::Type infobar_type) : separator_color_(SK_ColorBLACK), top_color_(GetTopColor(infobar_type)), @@ -54,7 +51,7 @@ void InfoBarBackground::Paint(gfx::Canvas* canvas, views::View* view) const { SkShader* gradient_shader = SkGradientShader::CreateLinear( gradient_points, gradient_colors, NULL, 2, SkShader::kClamp_TileMode); SkPaint paint; - paint.setStrokeWidth(1.0); + paint.setStrokeWidth(SkIntToScalar(InfoBar::kSeparatorLineHeight)); paint.setStyle(SkPaint::kFill_Style); paint.setStrokeCap(SkPaint::kRound_Cap); paint.setShader(gradient_shader); @@ -68,10 +65,15 @@ void InfoBarBackground::Paint(gfx::Canvas* canvas, views::View* view) const { paint.setColor(SkColorSetA(separator_color_, SkColorGetA(gradient_colors[0]))); paint.setStyle(SkPaint::kStroke_Style); + // Anti-alias the path so it doesn't look goofy when the edges are not at 45 + // degree angles, but don't anti-alias anything else, especially not the fill, + // lest we get weird color bleeding problems. + paint.setAntiAlias(true); canvas_skia->drawPath(*infobar->stroke_path(), paint); + paint.setAntiAlias(false); // Now draw the separator at the bottom. canvas->FillRectInt(separator_color_, 0, - view->height() - kSeparatorLineHeight, view->width(), - kSeparatorLineHeight); + view->height() - InfoBar::kSeparatorLineHeight, + view->width(), InfoBar::kSeparatorLineHeight); } diff --git a/chrome/browser/ui/views/infobars/infobar_background.h b/chrome/browser/ui/views/infobars/infobar_background.h index f948129..50bcce1 100644 --- a/chrome/browser/ui/views/infobars/infobar_background.h +++ b/chrome/browser/ui/views/infobars/infobar_background.h @@ -11,8 +11,6 @@ class InfoBarBackground : public views::Background { public: - static const int kSeparatorLineHeight; - explicit InfoBarBackground(InfoBarDelegate::Type infobar_type); virtual ~InfoBarBackground(); diff --git a/chrome/browser/ui/views/infobars/infobar_container.cc b/chrome/browser/ui/views/infobars/infobar_container.cc index 3f0b3f9..b451ae7 100644 --- a/chrome/browser/ui/views/infobars/infobar_container.cc +++ b/chrome/browser/ui/views/infobars/infobar_container.cc @@ -9,13 +9,15 @@ #include "content/browser/tab_contents/tab_contents.h" #include "content/common/notification_details.h" #include "content/common/notification_source.h" +#include "ui/base/animation/slide_animation.h" InfoBarContainer::Delegate::~Delegate() { } InfoBarContainer::InfoBarContainer(Delegate* delegate) : delegate_(delegate), - tab_contents_(NULL) { + tab_contents_(NULL), + top_arrow_target_height_(InfoBar::kDefaultArrowTargetHeight) { } InfoBarContainer::~InfoBarContainer() { @@ -77,6 +79,15 @@ int InfoBarContainer::GetVerticalOverlap(int* total_height) { return vertical_overlap; } +void InfoBarContainer::SetMaxTopArrowHeight(int height) { + // Decrease the height by the arrow stroke thickness, which is the separator + // line height, because the infobar arrow target heights are without-stroke. + top_arrow_target_height_ = std::min( + std::max(height - InfoBar::kSeparatorLineHeight, 0), + InfoBar::kMaximumArrowTargetHeight); + UpdateInfoBarArrowTargetHeights(); +} + void InfoBarContainer::OnInfoBarHeightChanged(bool is_animating) { if (delegate_) delegate_->InfoBarContainerHeightChanged(is_animating); @@ -143,6 +154,7 @@ void InfoBarContainer::RemoveInfoBar(InfoBarDelegate* delegate, // We merely need hide the infobar; it will call back to RemoveInfoBar() // itself once it's hidden. infobar->Hide(use_animation); + UpdateInfoBarArrowTargetHeights(); break; } } @@ -154,6 +166,7 @@ void InfoBarContainer::AddInfoBar(InfoBar* infobar, DCHECK(std::find(infobars_.begin(), infobars_.end(), infobar) == infobars_.end()); infobars_.push_back(infobar); + UpdateInfoBarArrowTargetHeights(); PlatformSpecificAddInfoBar(infobar); if (callback_status == WANT_CALLBACK) infobar->set_container(this); @@ -161,3 +174,23 @@ void InfoBarContainer::AddInfoBar(InfoBar* infobar, if (callback_status == NO_CALLBACK) infobar->set_container(this); } + +void InfoBarContainer::UpdateInfoBarArrowTargetHeights() { + for (size_t i = 0; i < infobars_.size(); ++i) + infobars_[i]->SetArrowTargetHeight(ArrowTargetHeightForInfoBar(i)); +} + +int InfoBarContainer::ArrowTargetHeightForInfoBar(size_t infobar_index) const { + if (infobar_index == 0) + return top_arrow_target_height_; + const ui::SlideAnimation* first_infobar_animation = + const_cast<const InfoBar*>(infobars_.front())->animation(); + if ((infobar_index > 1) || first_infobar_animation->IsShowing()) + return InfoBar::kDefaultArrowTargetHeight; + // When the first infobar is animating closed, we animate the second infobar's + // arrow target height from the default to the top target height. Note that + // the animation values here are going from 1.0 -> 0.0 as the top bar closes. + return top_arrow_target_height_ + static_cast<int>( + (InfoBar::kDefaultArrowTargetHeight - top_arrow_target_height_) * + first_infobar_animation->GetCurrentValue()); +} diff --git a/chrome/browser/ui/views/infobars/infobar_container.h b/chrome/browser/ui/views/infobars/infobar_container.h index 0c0a129..35c789d 100644 --- a/chrome/browser/ui/views/infobars/infobar_container.h +++ b/chrome/browser/ui/views/infobars/infobar_container.h @@ -50,17 +50,28 @@ class InfoBarContainer : public NotificationObserver { // |contents|, and show them all. |contents| may be NULL. void ChangeTabContents(TabContents* contents); - // Return the amount by which to overlap the toolbar above, and, when + // Returns the amount by which to overlap the toolbar above, and, when // |total_height| is non-NULL, set it to the height of the InfoBarContainer // (including overlap). int GetVerticalOverlap(int* total_height); + // Called by the delegate when the distance between what the top infobar's + // "unspoofable" arrow would point to and the top infobar itself changes. + // This enables the top infobar to show a longer arrow (e.g. because of a + // visible bookmark bar) or shorter (e.g. due to being in a popup window) if + // desired. + // + // IMPORTANT: This MUST NOT result in a call back to + // Delegate::InfoBarContainerHeightChanged() unless it causes an actual + // change, lest we infinitely recurse. + void SetMaxTopArrowHeight(int height); + // Called when a contained infobar has animated or by some other means changed // its height. The container is expected to do anything necessary to respond, // e.g. re-layout. void OnInfoBarHeightChanged(bool is_animating); - // Remove the specified InfoBarDelegate from the selected TabContents. This + // Removes the specified InfoBarDelegate from the selected TabContents. This // will notify us back and cause us to close the InfoBar. This is called from // the InfoBar's close button handler. void RemoveDelegate(InfoBarDelegate* delegate); @@ -106,11 +117,17 @@ class InfoBarContainer : public NotificationObserver { bool animate, CallbackStatus callback_status); + void UpdateInfoBarArrowTargetHeights(); + int ArrowTargetHeightForInfoBar(size_t infobar_index) const; + NotificationRegistrar registrar_; Delegate* delegate_; TabContents* tab_contents_; InfoBars infobars_; + // Calculated in SetMaxTopArrowHeight(). + int top_arrow_target_height_; + DISALLOW_COPY_AND_ASSIGN(InfoBarContainer); }; diff --git a/chrome/browser/ui/views/infobars/infobar_view.cc b/chrome/browser/ui/views/infobars/infobar_view.cc index 4a8af9320..d10bb36 100644 --- a/chrome/browser/ui/views/infobars/infobar_view.cc +++ b/chrome/browser/ui/views/infobars/infobar_view.cc @@ -25,6 +25,7 @@ #include "views/controls/link.h" #include "views/focus/external_focus_tracker.h" #include "views/widget/widget.h" +#include "views/window/non_client_view.h" #if defined(OS_WIN) #include <shellapi.h> @@ -36,7 +37,12 @@ #endif // static -const int InfoBar::kArrowTargetHeight = 9; +const int InfoBar::kSeparatorLineHeight = + views::NonClientFrameView::kClientEdgeThickness; +const int InfoBar::kDefaultArrowTargetHeight = 9; +const int InfoBar::kMaximumArrowTargetHeight = 24; +const int InfoBar::kDefaultArrowTargetHalfWidth = kDefaultArrowTargetHeight; +const int InfoBar::kMaximumArrowTargetHalfWidth = 14; const int InfoBar::kDefaultBarTargetHeight = 36; const int InfoBarView::kButtonButtonSpacing = 10; @@ -144,39 +150,43 @@ void InfoBarView::Layout() { // width is changed, which affects both paths. stroke_path_->rewind(); fill_path_->rewind(); - int arrow_bottom = std::max(arrow_height() - 1, 0); - SkScalar arrow_bottom_scalar = SkIntToScalar(arrow_bottom); const InfoBarContainer::Delegate* delegate = container_delegate(); - int arrow_x; if (delegate) { static_cast<InfoBarBackground*>(background())->set_separator_color( delegate->GetInfoBarSeparatorColor()); - if (delegate->DrawInfoBarArrows(&arrow_x) && arrow_bottom) { - stroke_path_->moveTo(SkIntToScalar(arrow_x - arrow_bottom), - arrow_bottom_scalar); - stroke_path_->rLineTo(arrow_bottom_scalar, -arrow_bottom_scalar); - stroke_path_->rLineTo(arrow_bottom_scalar, arrow_bottom_scalar); - - // Without extending the fill downward by a pixel, Skia doesn't seem to - // want to fill over the divider above the bar portion. + int arrow_x; + SkScalar arrow_fill_height = + SkIntToScalar(std::max(arrow_height() - kSeparatorLineHeight, 0)); + SkScalar arrow_fill_half_width = SkIntToScalar(arrow_half_width()); + SkScalar separator_height = SkIntToScalar(kSeparatorLineHeight); + if (delegate->DrawInfoBarArrows(&arrow_x) && arrow_fill_height) { + // Skia pixel centers are at the half-values, so the arrow is horizontally + // centered at |arrow_x| + 0.5. Vertically, the stroke path is the center + // of the separator, while the fill path is a closed path that extends up + // through the entire height of the separator and down to the bottom of + // the arrow where it joins the bar. + stroke_path_->moveTo( + SkIntToScalar(arrow_x) + SK_ScalarHalf - arrow_fill_half_width, + SkIntToScalar(arrow_height()) - (separator_height * SK_ScalarHalf)); + stroke_path_->rLineTo(arrow_fill_half_width, -arrow_fill_height); + stroke_path_->rLineTo(arrow_fill_half_width, arrow_fill_height); + *fill_path_ = *stroke_path_; - fill_path_->rLineTo(0.0, 1.0); - fill_path_->rLineTo(-arrow_bottom_scalar * 2, 0.0); + // Move the top of the fill path up to the top of the separator and then + // extend it down all the way through. + fill_path_->offset(0, -separator_height * SK_ScalarHalf); + // This 0.01 hack prevents the fill from filling more pixels on the right + // edge of the arrow than on the left. + const SkScalar epsilon = 0.01f; + fill_path_->rLineTo(-epsilon, 0); + fill_path_->rLineTo(0, separator_height); + fill_path_->rLineTo(epsilon - (arrow_fill_half_width * 2), 0); fill_path_->close(); - - // Fill and stroke have different opinions about how to treat paths. - // Because in Skia integral coordinates represent pixel boundaries, - // offsetting the path makes it go exactly through pixel centers; this - // results in lines that are exactly where we expect, instead of having - // odd "off by one" issues. Were we to do this for |fill_path|, however, - // which tries to fill "inside" the path (using some questionable math), - // we'd get a fill at a very different place than we'd want. - stroke_path_->offset(SK_ScalarHalf, SK_ScalarHalf); } } if (bar_height()) { fill_path_->addRect(0.0, SkIntToScalar(arrow_height()), - SkIntToScalar(width()), SkIntToScalar(height())); + SkIntToScalar(width()), SkIntToScalar(height() - kSeparatorLineHeight)); } int start_x = kHorizontalPadding; @@ -318,7 +328,7 @@ void InfoBarView::PlatformSpecificHide(bool animate) { DestroyFocusTracker(restore_focus); } -void InfoBarView::PlatformSpecificOnHeightRecalculated() { +void InfoBarView::PlatformSpecificOnHeightsRecalculated() { // Ensure that notifying our container of our size change will result in a // re-layout. InvalidateLayout(); diff --git a/chrome/browser/ui/views/infobars/infobar_view.h b/chrome/browser/ui/views/infobars/infobar_view.h index 56bebc8..ec32051 100644 --- a/chrome/browser/ui/views/infobars/infobar_view.h +++ b/chrome/browser/ui/views/infobars/infobar_view.h @@ -90,7 +90,7 @@ class InfoBarView : public InfoBar, // InfoBar: virtual void PlatformSpecificHide(bool animate) OVERRIDE; - virtual void PlatformSpecificOnHeightRecalculated() OVERRIDE; + virtual void PlatformSpecificOnHeightsRecalculated() OVERRIDE; // views::View: virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; |