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