OLD | NEW |
| (Empty) |
1 // Copyright 2016 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/chooser_content_view.h" | |
6 | |
7 #include "base/memory/ptr_util.h" | |
8 #include "base/numerics/safe_conversions.h" | |
9 #include "chrome/grit/generated_resources.h" | |
10 #include "ui/base/l10n/l10n_util.h" | |
11 #include "ui/base/resource/resource_bundle.h" | |
12 #include "ui/gfx/color_palette.h" | |
13 #include "ui/gfx/geometry/point.h" | |
14 #include "ui/gfx/geometry/rect.h" | |
15 #include "ui/gfx/image/image_skia.h" | |
16 #include "ui/gfx/paint_vector_icon.h" | |
17 #include "ui/gfx/vector_icons_public.h" | |
18 #include "ui/resources/grit/ui_resources.h" | |
19 #include "ui/views/controls/styled_label.h" | |
20 #include "ui/views/controls/table/table_view.h" | |
21 #include "ui/views/controls/throbber.h" | |
22 #include "ui/views/widget/widget.h" | |
23 | |
24 namespace { | |
25 | |
26 const int kChooserWidth = 370; | |
27 | |
28 const int kChooserHeight = 260; | |
29 | |
30 const int kThrobberDiameter = 24; | |
31 | |
32 const int kAdapterOffHelpLinkPadding = 5; | |
33 | |
34 // The lookup table for signal strength level image. | |
35 const int kSignalStrengthLevelImageIds[5] = {IDR_SIGNAL_0_BAR, IDR_SIGNAL_1_BAR, | |
36 IDR_SIGNAL_2_BAR, IDR_SIGNAL_3_BAR, | |
37 IDR_SIGNAL_4_BAR}; | |
38 | |
39 } // namespace | |
40 | |
41 ChooserContentView::ChooserContentView( | |
42 views::TableViewObserver* table_view_observer, | |
43 std::unique_ptr<ChooserController> chooser_controller) | |
44 : chooser_controller_(std::move(chooser_controller)), | |
45 help_text_(l10n_util::GetStringFUTF16( | |
46 IDS_DEVICE_CHOOSER_GET_HELP_LINK_WITH_SCANNING_STATUS, | |
47 base::string16())), | |
48 help_and_scanning_text_(l10n_util::GetStringFUTF16( | |
49 IDS_DEVICE_CHOOSER_GET_HELP_LINK_WITH_SCANNING_STATUS, | |
50 l10n_util::GetStringUTF16(IDS_BLUETOOTH_DEVICE_CHOOSER_SCANNING))) { | |
51 base::string16 re_scan_text = | |
52 l10n_util::GetStringUTF16(IDS_BLUETOOTH_DEVICE_CHOOSER_RE_SCAN); | |
53 std::vector<size_t> offsets; | |
54 help_and_re_scan_text_ = l10n_util::GetStringFUTF16( | |
55 IDS_DEVICE_CHOOSER_GET_HELP_LINK_WITH_RE_SCAN_LINK, help_text_, | |
56 re_scan_text, &offsets); | |
57 help_text_range_ = gfx::Range(offsets[0], offsets[0] + help_text_.size()); | |
58 re_scan_text_range_ = | |
59 gfx::Range(offsets[1], offsets[1] + re_scan_text.size()); | |
60 chooser_controller_->set_view(this); | |
61 std::vector<ui::TableColumn> table_columns; | |
62 table_columns.push_back(ui::TableColumn()); | |
63 table_view_ = new views::TableView( | |
64 this, table_columns, | |
65 chooser_controller_->ShouldShowIconBeforeText() ? views::ICON_AND_TEXT | |
66 : views::TEXT_ONLY, | |
67 !chooser_controller_->AllowMultipleSelection() /* single_selection */); | |
68 table_view_->set_select_on_remove(false); | |
69 table_view_->set_observer(table_view_observer); | |
70 table_view_->SetEnabled(chooser_controller_->NumOptions() > 0); | |
71 | |
72 table_parent_ = table_view_->CreateParentIfNecessary(); | |
73 AddChildView(table_parent_); | |
74 | |
75 throbber_ = new views::Throbber(); | |
76 throbber_->SetVisible(false); | |
77 AddChildView(throbber_); | |
78 | |
79 base::string16 link_text = l10n_util::GetStringUTF16( | |
80 IDS_BLUETOOTH_DEVICE_CHOOSER_TURN_ON_BLUETOOTH_LINK_TEXT); | |
81 size_t offset = 0; | |
82 base::string16 text = l10n_util::GetStringFUTF16( | |
83 IDS_BLUETOOTH_DEVICE_CHOOSER_TURN_ADAPTER_OFF, link_text, &offset); | |
84 turn_adapter_off_help_ = new views::StyledLabel(text, this); | |
85 turn_adapter_off_help_->AddStyleRange( | |
86 gfx::Range(0, link_text.size()), | |
87 views::StyledLabel::RangeStyleInfo::CreateForLink()); | |
88 turn_adapter_off_help_->SetVisible(false); | |
89 AddChildView(turn_adapter_off_help_); | |
90 } | |
91 | |
92 ChooserContentView::~ChooserContentView() { | |
93 chooser_controller_->set_view(nullptr); | |
94 table_view_->set_observer(nullptr); | |
95 table_view_->SetModel(nullptr); | |
96 } | |
97 | |
98 gfx::Size ChooserContentView::GetPreferredSize() const { | |
99 return gfx::Size(kChooserWidth, kChooserHeight); | |
100 } | |
101 | |
102 void ChooserContentView::Layout() { | |
103 gfx::Rect rect(GetContentsBounds()); | |
104 table_parent_->SetBoundsRect(rect); | |
105 // Set the throbber in the center of the chooser. | |
106 throbber_->SetBounds((rect.width() - kThrobberDiameter) / 2, | |
107 (rect.height() - kThrobberDiameter) / 2, | |
108 kThrobberDiameter, kThrobberDiameter); | |
109 turn_adapter_off_help_->SetPosition( | |
110 gfx::Point(kAdapterOffHelpLinkPadding, kAdapterOffHelpLinkPadding)); | |
111 turn_adapter_off_help_->SizeToFit(rect.width() - | |
112 2 * kAdapterOffHelpLinkPadding); | |
113 views::View::Layout(); | |
114 } | |
115 | |
116 int ChooserContentView::RowCount() { | |
117 // When there are no devices, the table contains a message saying there | |
118 // are no devices, so the number of rows is always at least 1. | |
119 return std::max(base::checked_cast<int>(chooser_controller_->NumOptions()), | |
120 1); | |
121 } | |
122 | |
123 base::string16 ChooserContentView::GetText(int row, int column_id) { | |
124 int num_options = base::checked_cast<int>(chooser_controller_->NumOptions()); | |
125 if (num_options == 0) { | |
126 DCHECK_EQ(0, row); | |
127 return chooser_controller_->GetNoOptionsText(); | |
128 } | |
129 | |
130 DCHECK_GE(row, 0); | |
131 DCHECK_LT(row, num_options); | |
132 base::string16 text = | |
133 chooser_controller_->GetOption(static_cast<size_t>(row)); | |
134 return chooser_controller_->IsPaired(row) | |
135 ? l10n_util::GetStringFUTF16( | |
136 IDS_DEVICE_CHOOSER_DEVICE_NAME_AND_PAIRED_STATUS_TEXT, text) | |
137 : text; | |
138 } | |
139 | |
140 void ChooserContentView::SetObserver(ui::TableModelObserver* observer) {} | |
141 | |
142 gfx::ImageSkia ChooserContentView::GetIcon(int row) { | |
143 DCHECK(chooser_controller_->ShouldShowIconBeforeText()); | |
144 | |
145 size_t num_options = chooser_controller_->NumOptions(); | |
146 if (num_options == 0) { | |
147 DCHECK_EQ(0, row); | |
148 return gfx::ImageSkia(); | |
149 } | |
150 | |
151 DCHECK_GE(row, 0); | |
152 DCHECK_LT(row, base::checked_cast<int>(num_options)); | |
153 | |
154 if (chooser_controller_->IsConnected(row)) { | |
155 return gfx::CreateVectorIcon(gfx::VectorIconId::BLUETOOTH_CONNECTED, | |
156 gfx::kChromeIconGrey); | |
157 } | |
158 | |
159 int level = chooser_controller_->GetSignalStrengthLevel(row); | |
160 | |
161 if (level == -1) | |
162 return gfx::ImageSkia(); | |
163 | |
164 DCHECK_GE(level, 0); | |
165 DCHECK_LT(level, static_cast<int>(arraysize(kSignalStrengthLevelImageIds))); | |
166 | |
167 return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed( | |
168 kSignalStrengthLevelImageIds[level]); | |
169 } | |
170 | |
171 void ChooserContentView::OnOptionsInitialized() { | |
172 table_view_->OnModelChanged(); | |
173 UpdateTableView(); | |
174 } | |
175 | |
176 void ChooserContentView::OnOptionAdded(size_t index) { | |
177 table_view_->OnItemsAdded(base::checked_cast<int>(index), 1); | |
178 UpdateTableView(); | |
179 table_view_->SetVisible(true); | |
180 throbber_->SetVisible(false); | |
181 throbber_->Stop(); | |
182 } | |
183 | |
184 void ChooserContentView::OnOptionRemoved(size_t index) { | |
185 table_view_->OnItemsRemoved(base::checked_cast<int>(index), 1); | |
186 UpdateTableView(); | |
187 } | |
188 | |
189 void ChooserContentView::OnOptionUpdated(size_t index) { | |
190 table_view_->OnItemsChanged(base::checked_cast<int>(index), 1); | |
191 UpdateTableView(); | |
192 } | |
193 | |
194 void ChooserContentView::OnAdapterEnabledChanged(bool enabled) { | |
195 // No row is selected since the adapter status has changed. | |
196 // This will also disable the OK button if it was enabled because | |
197 // of a previously selected row. | |
198 table_view_->Select(-1); | |
199 UpdateTableView(); | |
200 table_view_->SetVisible(enabled); | |
201 turn_adapter_off_help_->SetVisible(!enabled); | |
202 | |
203 throbber_->Stop(); | |
204 throbber_->SetVisible(false); | |
205 | |
206 if (enabled) { | |
207 SetGetHelpAndReScanLink(); | |
208 } else { | |
209 DCHECK(footnote_link_); | |
210 footnote_link_->SetText(help_text_); | |
211 footnote_link_->AddStyleRange( | |
212 help_text_range_, views::StyledLabel::RangeStyleInfo::CreateForLink()); | |
213 } | |
214 | |
215 if (GetWidget() && GetWidget()->GetRootView()) | |
216 GetWidget()->GetRootView()->Layout(); | |
217 } | |
218 | |
219 void ChooserContentView::OnRefreshStateChanged(bool refreshing) { | |
220 if (refreshing) { | |
221 // No row is selected since the chooser is refreshing. This will also | |
222 // disable the OK button if it was enabled because of a previously | |
223 // selected row. | |
224 table_view_->Select(-1); | |
225 UpdateTableView(); | |
226 } | |
227 | |
228 // When refreshing and no option available yet, hide |table_view_| and show | |
229 // |throbber_|. Otherwise show |table_view_| and hide |throbber_|. | |
230 bool throbber_visible = | |
231 refreshing && (chooser_controller_->NumOptions() == 0); | |
232 table_view_->SetVisible(!throbber_visible); | |
233 throbber_->SetVisible(throbber_visible); | |
234 if (throbber_visible) | |
235 throbber_->Start(); | |
236 else | |
237 throbber_->Stop(); | |
238 | |
239 if (refreshing) { | |
240 DCHECK(footnote_link_); | |
241 footnote_link_->SetText(help_and_scanning_text_); | |
242 footnote_link_->AddStyleRange( | |
243 help_text_range_, views::StyledLabel::RangeStyleInfo::CreateForLink()); | |
244 } else { | |
245 SetGetHelpAndReScanLink(); | |
246 } | |
247 | |
248 if (GetWidget() && GetWidget()->GetRootView()) | |
249 GetWidget()->GetRootView()->Layout(); | |
250 } | |
251 | |
252 void ChooserContentView::StyledLabelLinkClicked(views::StyledLabel* label, | |
253 const gfx::Range& range, | |
254 int event_flags) { | |
255 if (label == turn_adapter_off_help_) { | |
256 chooser_controller_->OpenAdapterOffHelpUrl(); | |
257 } else if (label == footnote_link_.get()) { | |
258 if (range == help_text_range_) | |
259 chooser_controller_->OpenHelpCenterUrl(); | |
260 else if (range == re_scan_text_range_) | |
261 chooser_controller_->RefreshOptions(); | |
262 else | |
263 NOTREACHED(); | |
264 } else { | |
265 NOTREACHED(); | |
266 } | |
267 } | |
268 | |
269 base::string16 ChooserContentView::GetWindowTitle() const { | |
270 return chooser_controller_->GetTitle(); | |
271 } | |
272 | |
273 base::string16 ChooserContentView::GetDialogButtonLabel( | |
274 ui::DialogButton button) const { | |
275 return button == ui::DIALOG_BUTTON_OK | |
276 ? chooser_controller_->GetOkButtonLabel() | |
277 : l10n_util::GetStringUTF16(IDS_DEVICE_CHOOSER_CANCEL_BUTTON_TEXT); | |
278 } | |
279 | |
280 bool ChooserContentView::IsDialogButtonEnabled(ui::DialogButton button) const { | |
281 return button != ui::DIALOG_BUTTON_OK || | |
282 !table_view_->selection_model().empty(); | |
283 } | |
284 | |
285 views::StyledLabel* ChooserContentView::footnote_link() { | |
286 if (chooser_controller_->ShouldShowFootnoteView()) { | |
287 footnote_link_ = base::MakeUnique<views::StyledLabel>(help_text_, this); | |
288 footnote_link_->set_owned_by_client(); | |
289 footnote_link_->AddStyleRange( | |
290 help_text_range_, views::StyledLabel::RangeStyleInfo::CreateForLink()); | |
291 } | |
292 | |
293 return footnote_link_.get(); | |
294 } | |
295 | |
296 void ChooserContentView::Accept() { | |
297 std::vector<size_t> indices( | |
298 table_view_->selection_model().selected_indices().begin(), | |
299 table_view_->selection_model().selected_indices().end()); | |
300 chooser_controller_->Select(indices); | |
301 } | |
302 | |
303 void ChooserContentView::Cancel() { | |
304 chooser_controller_->Cancel(); | |
305 } | |
306 | |
307 void ChooserContentView::Close() { | |
308 chooser_controller_->Close(); | |
309 } | |
310 | |
311 void ChooserContentView::UpdateTableView() { | |
312 if (chooser_controller_->NumOptions() == 0) { | |
313 table_view_->OnModelChanged(); | |
314 table_view_->SetEnabled(false); | |
315 } else { | |
316 table_view_->SetEnabled(true); | |
317 } | |
318 } | |
319 | |
320 void ChooserContentView::SetGetHelpAndReScanLink() { | |
321 DCHECK(footnote_link_); | |
322 footnote_link_->SetText(help_and_re_scan_text_); | |
323 footnote_link_->AddStyleRange( | |
324 help_text_range_, views::StyledLabel::RangeStyleInfo::CreateForLink()); | |
325 footnote_link_->AddStyleRange( | |
326 re_scan_text_range_, views::StyledLabel::RangeStyleInfo::CreateForLink()); | |
327 } | |
OLD | NEW |