info_bubble.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright 2013 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 "chrome/browser/ui/views/autofill/info_bubble.h" 6 7#include "base/i18n/rtl.h" 8#include "ui/gfx/point.h" 9#include "ui/gfx/rect.h" 10#include "ui/gfx/size.h" 11#include "ui/gfx/text_constants.h" 12#include "ui/views/bubble/bubble_border.h" 13#include "ui/views/bubble/bubble_frame_view.h" 14#include "ui/views/controls/combobox/combobox.h" 15#include "ui/views/controls/label.h" 16#include "ui/views/layout/fill_layout.h" 17#include "ui/views/layout/layout_constants.h" 18#include "ui/views/widget/widget.h" 19 20namespace autofill { 21 22namespace { 23 24// The visible width of bubble borders (differs from the actual width) in px. 25const int kBubbleBorderVisibleWidth = 1; 26 27// The margin between the content of the error bubble and its border. 28const int kInfoBubbleHorizontalMargin = 14; 29const int kInfoBubbleVerticalMargin = 12; 30 31} // namespace 32 33InfoBubble::InfoBubble(views::View* anchor, 34 const base::string16& message) 35 : anchor_(anchor), 36 align_to_anchor_edge_(false), 37 preferred_width_(233), 38 show_above_anchor_(false) { 39 DCHECK(anchor_); 40 SetAnchorView(anchor_); 41 42 set_margins(gfx::Insets(kInfoBubbleVerticalMargin, 43 kInfoBubbleHorizontalMargin, 44 kInfoBubbleVerticalMargin, 45 kInfoBubbleHorizontalMargin)); 46 set_use_focusless(true); 47 48 SetLayoutManager(new views::FillLayout); 49 views::Label* label = new views::Label(message); 50 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 51 label->SetMultiLine(true); 52 AddChildView(label); 53} 54 55InfoBubble::~InfoBubble() {} 56 57void InfoBubble::Show() { 58 // TODO(dbeam): currently we assume that combobox menus always show downward 59 // (which isn't true). If the invalid combobox is low enough on the screen, 60 // its menu will actually show upward and obscure the bubble. Figure out when 61 // this might happen and adjust |show_above_anchor_| accordingly. This is not 62 // that big of deal because it rarely happens in practice. 63 if (show_above_anchor_) { 64 set_arrow(ShouldArrowGoOnTheRight() ? views::BubbleBorder::BOTTOM_RIGHT : 65 views::BubbleBorder::BOTTOM_LEFT); 66 } else { 67 set_arrow(ShouldArrowGoOnTheRight() ? views::BubbleBorder::TOP_RIGHT : 68 views::BubbleBorder::TOP_LEFT); 69 } 70 71 widget_ = views::BubbleDelegateView::CreateBubble(this); 72 UpdatePosition(); 73} 74 75void InfoBubble::Hide() { 76 views::Widget* widget = GetWidget(); 77 if (widget && !widget->IsClosed()) 78 widget->Close(); 79} 80 81void InfoBubble::UpdatePosition() { 82 if (!widget_) 83 return; 84 85 if (!anchor_->GetVisibleBounds().IsEmpty()) { 86 SizeToContents(); 87 widget_->SetVisibilityChangedAnimationsEnabled(true); 88 widget_->ShowInactive(); 89 } else { 90 widget_->SetVisibilityChangedAnimationsEnabled(false); 91 widget_->Hide(); 92 } 93} 94 95gfx::Size InfoBubble::GetPreferredSize() { 96 int pref_width = preferred_width_; 97 pref_width -= GetBubbleFrameView()->GetInsets().width(); 98 pref_width -= 2 * kBubbleBorderVisibleWidth; 99 return gfx::Size(pref_width, GetHeightForWidth(pref_width)); 100} 101 102gfx::Rect InfoBubble::GetBubbleBounds() { 103 gfx::Rect bounds = views::BubbleDelegateView::GetBubbleBounds(); 104 gfx::Rect anchor_rect = GetAnchorRect(); 105 106 if (show_above_anchor_) 107 bounds.set_y(anchor_rect.y() - GetBubbleFrameView()->height()); 108 else 109 bounds.set_y(anchor_rect.bottom()); 110 111 if (align_to_anchor_edge_) { 112 anchor_rect.Inset(-GetBubbleFrameView()->bubble_border()->GetInsets()); 113 bounds.set_x(ShouldArrowGoOnTheRight() ? 114 anchor_rect.right() - bounds.width() - kBubbleBorderVisibleWidth : 115 anchor_rect.x() + kBubbleBorderVisibleWidth); 116 } 117 118 return bounds; 119} 120 121void InfoBubble::OnWidgetClosing(views::Widget* widget) { 122 if (widget == widget_) 123 widget_ = NULL; 124} 125 126bool InfoBubble::ShouldFlipArrowForRtl() const { 127 return false; 128} 129 130bool InfoBubble::ShouldArrowGoOnTheRight() { 131 views::View* container = anchor_->GetWidget()->non_client_view(); 132 DCHECK(container->Contains(anchor_)); 133 134 gfx::Point anchor_offset; 135 views::View::ConvertPointToTarget(anchor_, container, &anchor_offset); 136 anchor_offset.Offset(-container_insets_.left(), 0); 137 138 if (base::i18n::IsRTL()) { 139 int anchor_right_x = anchor_offset.x() + anchor_->width(); 140 return anchor_right_x >= preferred_width_; 141 } 142 143 int container_width = container->width() - container_insets_.width(); 144 return anchor_offset.x() + preferred_width_ > container_width; 145} 146 147} // namespace autofill 148