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