OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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/website_settings/chooser_bubble_ui_view.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/prefs/pref_service.h" | |
10 #include "base/strings/string16.h" | |
11 #include "chrome/browser/profiles/profile.h" | |
12 #include "chrome/browser/ui/browser.h" | |
13 #include "chrome/browser/ui/views/exclusive_access_bubble_views.h" | |
14 #include "chrome/browser/ui/views/frame/browser_view.h" | |
15 #include "chrome/browser/ui/views/frame/top_container_view.h" | |
16 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" | |
17 #include "chrome/browser/ui/views/location_bar/location_icon_view.h" | |
18 #include "chrome/browser/ui/website_settings/chooser_bubble_delegate.h" | |
19 #include "chrome/browser/ui/website_settings/chooser_options.h" | |
20 #include "chrome/common/pref_names.h" | |
21 #include "chrome/grit/generated_resources.h" | |
22 #include "ui/accessibility/ax_view_state.h" | |
23 #include "ui/base/l10n/l10n_util.h" | |
24 #include "ui/base/resource/resource_bundle.h" | |
25 #include "ui/gfx/paint_vector_icon.h" | |
26 #include "ui/gfx/text_constants.h" | |
27 #include "ui/gfx/vector_icons_public.h" | |
28 #include "ui/views/bubble/bubble_delegate.h" | |
29 #include "ui/views/bubble/bubble_frame_view.h" | |
30 #include "ui/views/controls/button/label_button.h" | |
31 #include "ui/views/controls/button/label_button_border.h" | |
32 #include "ui/views/controls/table/table_view.h" | |
33 #include "ui/views/controls/table/table_view_observer.h" | |
34 #include "ui/views/layout/box_layout.h" | |
35 #include "ui/views/layout/grid_layout.h" | |
36 | |
37 namespace { | |
38 | |
39 // chooser permission bubble width | |
felt
2015/12/02 17:56:39
nit: "Chooser", not "chooser"
juncai
2015/12/03 00:17:34
Done.
| |
40 const int kChooserPermissionBubbleWidth = 300; | |
41 | |
42 // chooser permission bubble height | |
felt
2015/12/02 17:56:40
not: "Chooser", not "chooser"
juncai
2015/12/03 00:17:34
Done.
| |
43 const int kChooserPermissionBubbleHeight = 200; | |
44 | |
45 // Spacing constant for outer margin. This is added to the | |
46 // bubble margin itself to equalize the margins at 13px. | |
47 const int kBubbleOuterMargin = 5; | |
48 | |
49 // Spacing between major items should be 9px. | |
50 const int kItemMajorSpacing = 9; | |
51 | |
52 // Button border size, draws inside the spacing distance. | |
53 const int kButtonBorderSize = 2; | |
54 | |
55 } // namespace | |
56 | |
57 class ChooserTableModel; | |
58 | |
59 /////////////////////////////////////////////////////////////////////////////// | |
60 // View implementation for the chooser bubble. | |
61 class ChooserBubbleUiViewDelegate : public views::BubbleDelegateView, | |
62 public views::ButtonListener, | |
63 public views::TableViewObserver { | |
64 public: | |
65 ChooserBubbleUiViewDelegate(views::View* anchor_view, | |
66 views::BubbleBorder::Arrow anchor_arrow, | |
67 ChooserBubbleUiView* owner, | |
68 ChooserOptions* chooser_options, | |
69 ChooserBubbleDelegate* chooser_bubble_delegate); | |
70 ~ChooserBubbleUiViewDelegate() override; | |
71 | |
72 void Close(); | |
73 | |
74 // BubbleDelegateView: | |
75 bool ShouldShowCloseButton() const override; | |
76 bool ShouldShowWindowTitle() const override; | |
77 base::string16 GetWindowTitle() const override; | |
78 void OnWidgetDestroying(views::Widget* widget) override; | |
79 | |
80 // ButtonListener: | |
81 void ButtonPressed(views::Button* button, const ui::Event& event) override; | |
82 | |
83 // views::TableViewObserver: | |
84 void OnSelectionChanged() override; | |
85 | |
86 // Updates the anchor's arrow and view. Also repositions the bubble so it's | |
87 // displayed in the correct location. | |
88 void UpdateAnchor(views::View* anchor_view, | |
89 views::BubbleBorder::Arrow anchor_arrow); | |
90 | |
91 private: | |
92 friend ChooserBubbleUiView; | |
93 | |
94 ChooserBubbleUiView* owner_; | |
95 ChooserOptions* chooser_options_; | |
96 ChooserBubbleDelegate* chooser_bubble_delegate_; | |
97 | |
98 views::LabelButton* connect_button_; | |
99 views::LabelButton* cancel_button_; | |
100 views::TableView* table_view_; | |
101 ChooserTableModel* chooser_table_model_; | |
102 | |
103 DISALLOW_COPY_AND_ASSIGN(ChooserBubbleUiViewDelegate); | |
104 }; | |
105 | |
106 ui::TableColumn ChooserTableColumn(int id, const std::string& title) { | |
107 ui::TableColumn column; | |
108 column.id = id; | |
109 column.title = base::ASCIIToUTF16(title.c_str()); | |
110 return column; | |
111 } | |
112 | |
113 class ChooserTableModel : public ui::TableModel, | |
114 public ChooserOptions::Observer { | |
115 public: | |
116 explicit ChooserTableModel(ChooserOptions* chooser_options) | |
117 : observer_(nullptr), chooser_options_(chooser_options) { | |
118 chooser_options_->set_observer(this); | |
119 } | |
120 | |
121 // ui::TableModel: | |
122 int RowCount() override { | |
123 const std::vector<base::string16>& device_names = | |
124 chooser_options_->GetOptions(); | |
125 if (device_names.empty()) { | |
126 return 1; | |
felt
2015/12/02 17:56:39
Why is this returning 1 instead of 0? (Is it becau
juncai
2015/12/03 00:17:34
Yes, it needs to display the string: IDS_CHOOSER_B
felt
2015/12/03 01:19:18
Could you add a comment to that effect?
juncai
2015/12/03 18:33:59
Done.
| |
127 } else { | |
128 return static_cast<int>(chooser_options_->GetOptions().size()); | |
felt
2015/12/02 17:56:39
device_names.size()
juncai
2015/12/03 00:17:34
Done.
| |
129 } | |
130 } | |
131 | |
132 // ui::TableModel: | |
133 base::string16 GetText(int row, int column_id) override { | |
134 const std::vector<base::string16>& device_names = | |
135 chooser_options_->GetOptions(); | |
136 if (device_names.empty()) { | |
137 DCHECK(row == 0); | |
138 return l10n_util::GetStringUTF16( | |
139 IDS_CHOOSER_BUBBLE_NO_DEVICES_FOUND_PROMPT); | |
140 } else { | |
felt
2015/12/02 17:56:40
simplify: else if here, then else below
juncai
2015/12/03 00:17:34
Done.
| |
141 if (row >= 0 && row < static_cast<int>(device_names.size())) { | |
142 return device_names[row]; | |
143 } else { | |
144 return base::string16(); | |
felt
2015/12/02 17:56:40
Should this be a NOTREACHED()? Under what circumst
juncai
2015/12/03 00:17:34
Done.
| |
145 } | |
146 } | |
147 } | |
148 | |
149 // ui::TableModel: | |
150 void SetObserver(ui::TableModelObserver* observer) override { | |
151 observer_ = observer; | |
152 } | |
153 | |
154 // ChooserOptions::Observer: | |
155 void OnOptionsInitialized() override { | |
156 if (observer_) { | |
157 observer_->OnModelChanged(); | |
158 Update(); | |
159 } | |
160 } | |
161 | |
162 // ChooserOptions::Observer: | |
163 void OnOptionAdded(int index) override { | |
164 if (observer_) { | |
165 observer_->OnItemsAdded(index, 1); | |
166 Update(); | |
167 } | |
168 } | |
169 | |
170 // ChooserOptions::Observer: | |
171 void OnOptionRemoved(int index) override { | |
172 if (observer_) { | |
173 observer_->OnItemsRemoved(index, 1); | |
174 Update(); | |
175 } | |
176 } | |
177 | |
178 void Update() { | |
179 views::TableView* table_view = static_cast<views::TableView*>(observer_); | |
180 | |
181 if (chooser_options_->GetOptions().empty()) { | |
182 observer_->OnModelChanged(); | |
183 table_view->SetEnabled(false); | |
184 } else { | |
185 table_view->SetEnabled(true); | |
186 } | |
187 } | |
188 | |
189 void SetConnectButton(views::LabelButton* connect_button) { | |
190 connect_button_ = connect_button; | |
191 } | |
192 | |
193 private: | |
194 ui::TableModelObserver* observer_ = nullptr; | |
felt
2015/12/02 17:56:39
initialize in the constructor
juncai
2015/12/03 00:17:34
Done.
| |
195 ChooserOptions* chooser_options_; | |
196 views::LabelButton* connect_button_; | |
197 }; | |
198 | |
199 ChooserBubbleUiViewDelegate::ChooserBubbleUiViewDelegate( | |
200 views::View* anchor_view, | |
201 views::BubbleBorder::Arrow anchor_arrow, | |
202 ChooserBubbleUiView* owner, | |
203 ChooserOptions* chooser_options, | |
204 ChooserBubbleDelegate* chooser_bubble_delegate) | |
205 : views::BubbleDelegateView(anchor_view, anchor_arrow), | |
206 owner_(owner), | |
207 chooser_options_(chooser_options), | |
208 chooser_bubble_delegate_(chooser_bubble_delegate) { | |
209 views::GridLayout* layout = new views::GridLayout(this); | |
210 SetLayoutManager(layout); | |
211 | |
212 views::ColumnSet* column_set = layout->AddColumnSet(0); | |
213 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, | |
214 views::GridLayout::USE_PREF, 0, 0); | |
215 | |
216 layout->StartRow(1, 0); | |
217 | |
218 // create a table view | |
felt
2015/12/02 17:56:39
nit: comments should always be sentence-case ("Cre
juncai
2015/12/03 00:17:34
Done.
| |
219 std::vector<ui::TableColumn> table_columns; | |
220 table_columns.push_back(ChooserTableColumn( | |
221 0, "" /* empty string makes the column title invisible */)); | |
222 chooser_table_model_ = new ChooserTableModel(chooser_options_); | |
223 table_view_ = new views::TableView(chooser_table_model_, table_columns, | |
224 views::TEXT_ONLY, true); | |
225 table_view_->set_select_on_remove(false); | |
226 chooser_table_model_->SetObserver(table_view_); | |
227 table_view_->SetObserver(this); | |
228 layout->AddView(table_view_->CreateParentIfNecessary(), 1, 1, | |
229 views::GridLayout::FILL, views::GridLayout::FILL, | |
230 kChooserPermissionBubbleWidth, | |
231 kChooserPermissionBubbleHeight); | |
232 if (chooser_options_->GetOptions().empty()) { | |
233 table_view_->SetEnabled(false); | |
234 } | |
235 | |
236 layout->AddPaddingRow(0, kItemMajorSpacing); | |
237 | |
238 views::View* button_row = new views::View(); | |
239 views::GridLayout* button_layout = new views::GridLayout(button_row); | |
240 views::ColumnSet* button_columns = button_layout->AddColumnSet(0); | |
241 button_row->SetLayoutManager(button_layout); | |
242 layout->StartRow(1, 0); | |
243 layout->AddView(button_row); | |
244 | |
245 // lay out the Connect/Cancel buttons. | |
246 button_columns->AddColumn(views::GridLayout::TRAILING, | |
247 views::GridLayout::FILL, 100, | |
248 views::GridLayout::USE_PREF, 0, 0); | |
249 button_columns->AddPaddingColumn(0, | |
250 kItemMajorSpacing - (2 * kButtonBorderSize)); | |
251 button_columns->AddColumn(views::GridLayout::TRAILING, | |
252 views::GridLayout::FILL, 0, | |
253 views::GridLayout::USE_PREF, 0, 0); | |
254 button_layout->StartRow(0, 0); | |
255 | |
256 base::string16 connect_text = | |
257 l10n_util::GetStringUTF16(IDS_CHOOSER_BUBBLE_CONNECT_BUTTON_TEXT); | |
258 connect_button_ = new views::LabelButton(this, connect_text); | |
259 connect_button_->SetStyle(views::Button::STYLE_BUTTON); | |
260 // disable the connect button at the beginning since no device selected yet. | |
261 connect_button_->SetEnabled(false); | |
262 button_layout->AddView(connect_button_); | |
263 chooser_table_model_->SetConnectButton(connect_button_); | |
264 | |
265 base::string16 cancel_text = | |
266 l10n_util::GetStringUTF16(IDS_CHOOSER_BUBBLE_CANCEL_BUTTON_TEXT); | |
267 cancel_button_ = new views::LabelButton(this, cancel_text); | |
268 cancel_button_->SetStyle(views::Button::STYLE_BUTTON); | |
269 button_layout->AddView(cancel_button_); | |
270 | |
271 button_layout->AddPaddingRow(0, kBubbleOuterMargin); | |
272 } | |
273 | |
274 ChooserBubbleUiViewDelegate::~ChooserBubbleUiViewDelegate() { | |
275 RemoveAllChildViews(true); | |
276 if (owner_) | |
277 owner_->Close(); | |
278 chooser_table_model_->SetObserver(nullptr); | |
279 } | |
280 | |
281 void ChooserBubbleUiViewDelegate::Close() { | |
282 owner_ = nullptr; | |
283 GetWidget()->Close(); | |
284 } | |
285 | |
286 bool ChooserBubbleUiViewDelegate::ShouldShowCloseButton() const { | |
287 return true; | |
288 } | |
289 | |
290 bool ChooserBubbleUiViewDelegate::ShouldShowWindowTitle() const { | |
291 return true; | |
292 } | |
293 | |
294 base::string16 ChooserBubbleUiViewDelegate::GetWindowTitle() const { | |
295 return l10n_util::GetStringUTF16(IDS_CHOOSER_BUBBLE_PROMPT); | |
296 } | |
297 | |
298 void ChooserBubbleUiViewDelegate::OnWidgetDestroying(views::Widget* widget) { | |
299 views::BubbleDelegateView::OnWidgetDestroying(widget); | |
300 if (owner_) { | |
301 owner_->Close(); | |
302 owner_ = nullptr; | |
303 } | |
304 } | |
305 | |
306 void ChooserBubbleUiViewDelegate::ButtonPressed(views::Button* button, | |
307 const ui::Event& event) { | |
308 if (button == connect_button_) | |
309 chooser_bubble_delegate_->Select(table_view_->selection_model().active()); | |
310 owner_->Close(); | |
311 } | |
312 | |
313 void ChooserBubbleUiViewDelegate::OnSelectionChanged() { | |
314 connect_button_->SetEnabled(!table_view_->selection_model().empty()); | |
315 } | |
316 | |
317 void ChooserBubbleUiViewDelegate::UpdateAnchor( | |
318 views::View* anchor_view, | |
319 views::BubbleBorder::Arrow anchor_arrow) { | |
320 if (GetAnchorView() == anchor_view && arrow() == anchor_arrow) | |
321 return; | |
322 | |
323 set_arrow(anchor_arrow); | |
324 | |
325 // Update the border in the bubble: will either add or remove the arrow. | |
326 views::BubbleFrameView* frame = | |
327 views::BubbleDelegateView::GetBubbleFrameView(); | |
328 views::BubbleBorder::Arrow adjusted_arrow = anchor_arrow; | |
329 if (base::i18n::IsRTL()) | |
330 adjusted_arrow = views::BubbleBorder::horizontal_mirror(adjusted_arrow); | |
331 frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>( | |
332 new views::BubbleBorder(adjusted_arrow, shadow(), color()))); | |
333 | |
334 // Reposition the bubble based on the updated arrow and view. | |
335 SetAnchorView(anchor_view); | |
336 } | |
337 | |
338 ////////////////////////////////////////////////////////////////////////////// | |
339 // ChooserBubbleUiView | |
340 | |
341 ChooserBubbleUiView::ChooserBubbleUiView( | |
342 Browser* browser, | |
343 ChooserOptions* chooser_options, | |
344 ChooserBubbleDelegate* chooser_bubble_delegate) | |
345 : browser_(browser), | |
346 chooser_options_(chooser_options), | |
347 chooser_bubble_delegate_(chooser_bubble_delegate), | |
348 chooser_bubble_ui_view_delegate_(nullptr) { | |
349 DCHECK(browser_); | |
350 DCHECK(chooser_options_); | |
351 DCHECK(chooser_bubble_delegate_); | |
352 } | |
353 | |
354 ChooserBubbleUiView::~ChooserBubbleUiView() {} | |
355 | |
356 void ChooserBubbleUiView::Show(BubbleReference bubble_reference) { | |
357 chooser_bubble_ui_view_delegate_ = new ChooserBubbleUiViewDelegate( | |
358 GetAnchorView(), GetAnchorArrow(), this, chooser_options_, | |
359 chooser_bubble_delegate_); | |
360 | |
361 // Set |parent_window| because some valid anchors can become hidden. | |
362 views::Widget* widget = views::Widget::GetWidgetForNativeWindow( | |
363 browser_->window()->GetNativeWindow()); | |
364 chooser_bubble_ui_view_delegate_->set_parent_window(widget->GetNativeView()); | |
365 | |
366 views::BubbleDelegateView::CreateBubble(chooser_bubble_ui_view_delegate_) | |
367 ->Show(); | |
368 } | |
369 | |
370 void ChooserBubbleUiView::Close() { | |
371 if (chooser_bubble_ui_view_delegate_) { | |
372 chooser_bubble_ui_view_delegate_->Close(); | |
373 chooser_bubble_ui_view_delegate_ = nullptr; | |
374 } | |
375 } | |
376 | |
377 void ChooserBubbleUiView::UpdateAnchorPosition() { | |
378 if (chooser_bubble_ui_view_delegate_) { | |
379 chooser_bubble_ui_view_delegate_->UpdateAnchor(GetAnchorView(), | |
380 GetAnchorArrow()); | |
381 } | |
382 } | |
383 | |
384 views::View* ChooserBubbleUiView::GetAnchorView() { | |
385 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_); | |
386 | |
387 if (browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR)) | |
388 return browser_view->GetLocationBarView()->location_icon_view(); | |
389 | |
390 if (browser_view->IsFullscreenBubbleVisible()) | |
391 return browser_view->exclusive_access_bubble()->GetView(); | |
392 | |
393 return browser_view->top_container(); | |
394 } | |
395 | |
396 views::BubbleBorder::Arrow ChooserBubbleUiView::GetAnchorArrow() { | |
397 if (browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR)) | |
398 return views::BubbleBorder::TOP_LEFT; | |
399 return views::BubbleBorder::NONE; | |
400 } | |
OLD | NEW |