Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Side by Side Diff: ash/common/system/chromeos/ime_menu/ime_list_view.cc

Issue 2734653002: chromeos: Move files in //ash/common to //ash (Closed)
Patch Set: fix a11y tests, fix docs Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "ash/common/system/chromeos/ime_menu/ime_list_view.h"
6
7 #include "ash/common/material_design/material_design_controller.h"
8 #include "ash/common/system/tray/hover_highlight_view.h"
9 #include "ash/common/system/tray/ime_info.h"
10 #include "ash/common/system/tray/system_menu_button.h"
11 #include "ash/common/system/tray/system_tray_delegate.h"
12 #include "ash/common/system/tray/tray_constants.h"
13 #include "ash/common/system/tray/tray_details_view.h"
14 #include "ash/common/system/tray/tray_popup_header_button.h"
15 #include "ash/common/system/tray/tray_popup_item_style.h"
16 #include "ash/common/system/tray/tray_popup_utils.h"
17 #include "ash/common/system/tray/tri_view.h"
18 #include "ash/common/wm_shell.h"
19 #include "ash/resources/grit/ash_resources.h"
20 #include "ash/strings/grit/ash_strings.h"
21 #include "ui/accessibility/ax_node_data.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/color_palette.h"
25 #include "ui/gfx/paint_vector_icon.h"
26 #include "ui/gfx/vector_icons_public.h"
27 #include "ui/keyboard/keyboard_util.h"
28 #include "ui/views/background.h"
29 #include "ui/views/border.h"
30 #include "ui/views/controls/button/toggle_button.h"
31 #include "ui/views/controls/image_view.h"
32 #include "ui/views/controls/label.h"
33 #include "ui/views/controls/separator.h"
34 #include "ui/views/layout/fill_layout.h"
35 #include "ui/views/painter.h"
36 #include "ui/views/view.h"
37 #include "ui/views/widget/widget.h"
38
39 namespace ash {
40 namespace {
41
42 const int kMinFontSizeDelta = -10;
43
44 const SkColor kKeyboardRowSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F);
45
46 // A |HoverHighlightView| that uses bold or normal font depending on whether it
47 // is selected. This view exposes itself as a checkbox to the accessibility
48 // framework.
49 class SelectableHoverHighlightView : public HoverHighlightView {
50 public:
51 SelectableHoverHighlightView(ViewClickListener* listener,
52 const base::string16& label,
53 bool selected)
54 : HoverHighlightView(listener), selected_(selected) {
55 AddLabel(label, gfx::ALIGN_LEFT, selected);
56 }
57
58 ~SelectableHoverHighlightView() override {}
59
60 protected:
61 // views::View:
62 void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
63 HoverHighlightView::GetAccessibleNodeData(node_data);
64 node_data->role = ui::AX_ROLE_CHECK_BOX;
65 if (selected_)
66 node_data->AddStateFlag(ui::AX_STATE_CHECKED);
67 }
68
69 private:
70 bool selected_;
71
72 DISALLOW_COPY_AND_ASSIGN(SelectableHoverHighlightView);
73 };
74
75 // The IME list item view used in the material design. It contains IME info
76 // (name and label) and a check button if the item is selected. It's also used
77 // for IME property item, which has no name but label and a gray checked icon.
78 class ImeListItemView : public ActionableView {
79 public:
80 ImeListItemView(SystemTrayItem* owner,
81 ImeListView* list_view,
82 const base::string16& id,
83 const base::string16& label,
84 bool selected,
85 const SkColor button_color)
86 : ActionableView(owner, TrayPopupInkDropStyle::FILL_BOUNDS),
87 ime_list_view_(list_view),
88 selected_(selected) {
89 if (MaterialDesignController::IsSystemTrayMenuMaterial())
90 SetInkDropMode(InkDropHostView::InkDropMode::ON);
91
92 TriView* tri_view = TrayPopupUtils::CreateDefaultRowView();
93 AddChildView(tri_view);
94 SetLayoutManager(new views::FillLayout);
95
96 // The id button shows the IME short name.
97 views::Label* id_label = TrayPopupUtils::CreateDefaultLabel();
98 id_label->SetText(id);
99 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
100 const gfx::FontList& base_font_list =
101 rb.GetFontList(ui::ResourceBundle::MediumBoldFont);
102 id_label->SetFontList(base_font_list);
103
104 // For IMEs whose short name are more than 2 characters (INTL, EXTD, etc.),
105 // |kMenuIconSize| is not enough. The label will trigger eliding as "I..."
106 // or "...". So we shrink the font size until it fits within the bounds.
107 int size_delta = -1;
108 while ((id_label->GetPreferredSize().width() -
109 id_label->GetInsets().width()) > kMenuIconSize &&
110 size_delta >= kMinFontSizeDelta) {
111 id_label->SetFontList(base_font_list.DeriveWithSizeDelta(size_delta));
112 --size_delta;
113 }
114 tri_view->AddView(TriView::Container::START, id_label);
115
116 // The label shows the IME name.
117 auto* label_view = TrayPopupUtils::CreateDefaultLabel();
118 label_view->SetText(label);
119 TrayPopupItemStyle style(
120 TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
121 style.SetupLabel(label_view);
122
123 label_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
124 tri_view->AddView(TriView::Container::CENTER, label_view);
125
126 if (selected) {
127 // The checked button indicates the IME is selected.
128 views::ImageView* checked_image = TrayPopupUtils::CreateMainImageView();
129 checked_image->SetImage(gfx::CreateVectorIcon(
130 gfx::VectorIconId::CHECK_CIRCLE, kMenuIconSize, button_color));
131 tri_view->AddView(TriView::Container::END, checked_image);
132 }
133 SetAccessibleName(label_view->text());
134 }
135
136 ~ImeListItemView() override {}
137
138 // ActionableView:
139 bool PerformAction(const ui::Event& event) override {
140 if (ime_list_view_->should_focus_ime_after_selection_with_keyboard() &&
141 event.type() == ui::EventType::ET_KEY_PRESSED) {
142 ime_list_view_->set_last_item_selected_with_keyboard(true);
143 } else {
144 ime_list_view_->set_last_item_selected_with_keyboard(false);
145 }
146
147 ime_list_view_->HandleViewClicked(this);
148 return true;
149 }
150
151 void OnFocus() override {
152 ActionableView::OnFocus();
153 if (ime_list_view_ && ime_list_view_->scroll_content())
154 ime_list_view_->scroll_content()->ScrollRectToVisible(bounds());
155 }
156
157 void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
158 ActionableView::GetAccessibleNodeData(node_data);
159 node_data->role = ui::AX_ROLE_CHECK_BOX;
160 node_data->AddStateFlag(selected_ ? ui::AX_STATE_CHECKED
161 : ui::AX_STATE_NONE);
162 }
163
164 private:
165 ImeListView* ime_list_view_;
166 bool selected_;
167
168 DISALLOW_COPY_AND_ASSIGN(ImeListItemView);
169 };
170
171 } // namespace
172
173 // The view that contains a |KeyboardButtonView| and a toggle button.
174 class MaterialKeyboardStatusRowView : public views::View {
175 public:
176 MaterialKeyboardStatusRowView(views::ButtonListener* listener, bool enabled)
177 : listener_(listener), toggle_(nullptr) {
178 Init();
179 toggle_->SetIsOn(enabled, false);
180 }
181
182 ~MaterialKeyboardStatusRowView() override {}
183
184 views::Button* toggle() const { return toggle_; }
185 bool is_toggled() const { return toggle_->is_on(); }
186
187 protected:
188 // views::View:
189 int GetHeightForWidth(int w) const override {
190 return GetPreferredSize().height();
191 }
192
193 private:
194 void Init() {
195 TrayPopupUtils::ConfigureAsStickyHeader(this);
196 SetLayoutManager(new views::FillLayout);
197
198 TriView* tri_view = TrayPopupUtils::CreateDefaultRowView();
199 AddChildView(tri_view);
200
201 // The on-screen keyboard image button.
202 views::ImageView* keyboard_image = TrayPopupUtils::CreateMainImageView();
203 keyboard_image->SetImage(gfx::CreateVectorIcon(
204 kImeMenuOnScreenKeyboardIcon, kMenuIconSize, kMenuIconColor));
205 tri_view->AddView(TriView::Container::START, keyboard_image);
206
207 // The on-screen keyboard label ('On-screen keyboard').
208 auto* label = TrayPopupUtils::CreateDefaultLabel();
209 label->SetText(ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
210 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD));
211 TrayPopupItemStyle style(
212 TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
213 style.SetupLabel(label);
214 tri_view->AddView(TriView::Container::CENTER, label);
215
216 // The on-screen keyboard toggle button.
217 toggle_ = TrayPopupUtils::CreateToggleButton(
218 listener_, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD);
219 tri_view->AddView(TriView::Container::END, toggle_);
220 }
221
222 // ButtonListener to notify when |toggle_| is clicked.
223 views::ButtonListener* listener_;
224
225 // ToggleButton to toggle keyboard on or off.
226 views::ToggleButton* toggle_;
227
228 DISALLOW_COPY_AND_ASSIGN(MaterialKeyboardStatusRowView);
229 };
230
231 ImeListView::ImeListView(SystemTrayItem* owner)
232 : TrayDetailsView(owner),
233 last_item_selected_with_keyboard_(false),
234 should_focus_ime_after_selection_with_keyboard_(false),
235 current_ime_view_(nullptr) {}
236
237 ImeListView::~ImeListView() {}
238
239 void ImeListView::Init(bool show_keyboard_toggle,
240 SingleImeBehavior single_ime_behavior) {
241 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate();
242 IMEInfoList list;
243 delegate->GetAvailableIMEList(&list);
244 IMEPropertyInfoList property_list;
245 delegate->GetCurrentIMEProperties(&property_list);
246 Update(list, property_list, show_keyboard_toggle, single_ime_behavior);
247 }
248
249 void ImeListView::Update(const IMEInfoList& list,
250 const IMEPropertyInfoList& property_list,
251 bool show_keyboard_toggle,
252 SingleImeBehavior single_ime_behavior) {
253 ResetImeListView();
254 ime_map_.clear();
255 property_map_.clear();
256 CreateScrollableList();
257
258 // Appends IME list and IME properties.
259 if (single_ime_behavior == ImeListView::SHOW_SINGLE_IME || list.size() > 1) {
260 if (MaterialDesignController::IsSystemTrayMenuMaterial()) {
261 AppendImeListAndProperties(list, property_list);
262 } else {
263 AppendIMEList(list);
264 if (!property_list.empty())
265 AppendIMEProperties(property_list);
266 }
267 }
268
269 if (show_keyboard_toggle) {
270 if (MaterialDesignController::IsSystemTrayMenuMaterial()) {
271 PrependMaterialKeyboardStatus();
272 } else {
273 if (list.size() > 1 || !property_list.empty())
274 AddScrollSeparator();
275 AppendKeyboardStatus();
276 }
277 }
278
279 Layout();
280 SchedulePaint();
281
282 if (should_focus_ime_after_selection_with_keyboard_ &&
283 last_item_selected_with_keyboard_) {
284 FocusCurrentImeIfNeeded();
285 } else if (current_ime_view_) {
286 scroll_content()->ScrollRectToVisible(current_ime_view_->bounds());
287 }
288 }
289
290 void ImeListView::ResetImeListView() {
291 // Children are removed from the view hierarchy and deleted in Reset().
292 Reset();
293 material_keyboard_status_view_ = nullptr;
294 keyboard_status_ = nullptr;
295 current_ime_view_ = nullptr;
296 }
297
298 void ImeListView::CloseImeListView() {
299 last_selected_item_id_.clear();
300 current_ime_view_ = nullptr;
301 last_item_selected_with_keyboard_ = false;
302 GetWidget()->Close();
303 }
304
305 void ImeListView::AppendIMEList(const IMEInfoList& list) {
306 DCHECK(ime_map_.empty());
307 for (size_t i = 0; i < list.size(); i++) {
308 HoverHighlightView* container =
309 new SelectableHoverHighlightView(this, list[i].name, list[i].selected);
310 scroll_content()->AddChildView(container);
311 ime_map_[container] = list[i].id;
312 }
313 }
314
315 void ImeListView::AppendIMEProperties(
316 const IMEPropertyInfoList& property_list) {
317 DCHECK(property_map_.empty());
318 for (size_t i = 0; i < property_list.size(); i++) {
319 HoverHighlightView* container = new SelectableHoverHighlightView(
320 this, property_list[i].name, property_list[i].selected);
321 if (i == 0)
322 container->SetBorder(
323 views::CreateSolidSidedBorder(1, 0, 0, 0, kBorderLightColor));
324 scroll_content()->AddChildView(container);
325 property_map_[container] = property_list[i].key;
326 }
327 }
328
329 void ImeListView::AppendImeListAndProperties(
330 const IMEInfoList& list,
331 const IMEPropertyInfoList& property_list) {
332 DCHECK(ime_map_.empty());
333 for (size_t i = 0; i < list.size(); i++) {
334 views::View* ime_view =
335 new ImeListItemView(owner(), this, list[i].short_name, list[i].name,
336 list[i].selected, gfx::kGoogleGreen700);
337 scroll_content()->AddChildView(ime_view);
338 ime_map_[ime_view] = list[i].id;
339
340 if (list[i].selected)
341 current_ime_view_ = ime_view;
342
343 // In material design, the property items will be added after the current
344 // selected IME item.
345 if (list[i].selected && !property_list.empty()) {
346 // Adds a separator on the top of property items.
347 scroll_content()->AddChildView(
348 TrayPopupUtils::CreateListItemSeparator(true));
349
350 // Adds the property items.
351 for (size_t i = 0; i < property_list.size(); i++) {
352 ImeListItemView* property_view = new ImeListItemView(
353 owner(), this, base::string16(), property_list[i].name,
354 property_list[i].selected, kMenuIconColor);
355 scroll_content()->AddChildView(property_view);
356 property_map_[property_view] = property_list[i].key;
357 }
358
359 // Adds a separator on the bottom of property items if there are still
360 // other IMEs under the current one.
361 if (i < list.size() - 1)
362 scroll_content()->AddChildView(
363 TrayPopupUtils::CreateListItemSeparator(true));
364 }
365 }
366 }
367
368 void ImeListView::AppendKeyboardStatus() {
369 DCHECK(!MaterialDesignController::IsSystemTrayMenuMaterial());
370 HoverHighlightView* container = new HoverHighlightView(this);
371 int id = keyboard::IsKeyboardEnabled() ? IDS_ASH_STATUS_TRAY_DISABLE_KEYBOARD
372 : IDS_ASH_STATUS_TRAY_ENABLE_KEYBOARD;
373 container->AddLabel(
374 ui::ResourceBundle::GetSharedInstance().GetLocalizedString(id),
375 gfx::ALIGN_LEFT, false /* highlight */);
376 scroll_content()->AddChildView(container);
377 keyboard_status_ = container;
378 }
379
380 void ImeListView::PrependMaterialKeyboardStatus() {
381 DCHECK(MaterialDesignController::IsSystemTrayMenuMaterial());
382 DCHECK(!material_keyboard_status_view_);
383 MaterialKeyboardStatusRowView* view =
384 new MaterialKeyboardStatusRowView(this, keyboard::IsKeyboardEnabled());
385 scroll_content()->AddChildViewAt(view, 0);
386 material_keyboard_status_view_ = view;
387 }
388
389 void ImeListView::HandleViewClicked(views::View* view) {
390 if (view == keyboard_status_) {
391 WmShell::Get()->ToggleIgnoreExternalKeyboard();
392 last_selected_item_id_.clear();
393 last_item_selected_with_keyboard_ = false;
394 return;
395 }
396
397 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate();
398 std::map<views::View*, std::string>::const_iterator ime = ime_map_.find(view);
399 if (ime != ime_map_.end()) {
400 WmShell::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_IME_SWITCH_MODE);
401 std::string ime_id = ime->second;
402 last_selected_item_id_ = ime_id;
403 delegate->SwitchIME(ime_id);
404 } else {
405 std::map<views::View*, std::string>::const_iterator property =
406 property_map_.find(view);
407 if (property == property_map_.end())
408 return;
409 const std::string key = property->second;
410 last_selected_item_id_ = key;
411 delegate->ActivateIMEProperty(key);
412 }
413
414 if (!should_focus_ime_after_selection_with_keyboard_ ||
415 !last_item_selected_with_keyboard_) {
416 CloseImeListView();
417 }
418 }
419
420 void ImeListView::HandleButtonPressed(views::Button* sender,
421 const ui::Event& event) {
422 if (material_keyboard_status_view_ &&
423 sender == material_keyboard_status_view_->toggle()) {
424 WmShell::Get()->ToggleIgnoreExternalKeyboard();
425 last_selected_item_id_.clear();
426 last_item_selected_with_keyboard_ = false;
427 }
428 }
429
430 void ImeListView::VisibilityChanged(View* starting_from, bool is_visible) {
431 if (!is_visible || (should_focus_ime_after_selection_with_keyboard_ &&
432 last_item_selected_with_keyboard_) ||
433 !current_ime_view_) {
434 return;
435 }
436
437 scroll_content()->ScrollRectToVisible(current_ime_view_->bounds());
438 }
439
440 void ImeListView::FocusCurrentImeIfNeeded() {
441 views::FocusManager* manager = GetFocusManager();
442 if (!manager || manager->GetFocusedView() || last_selected_item_id_.empty())
443 return;
444
445 for (auto ime_map : ime_map_) {
446 if (ime_map.second == last_selected_item_id_) {
447 (ime_map.first)->RequestFocus();
448 return;
449 }
450 }
451
452 for (auto property_map : property_map_) {
453 if (property_map.second == last_selected_item_id_) {
454 (property_map.first)->RequestFocus();
455 return;
456 }
457 }
458 }
459
460 ImeListViewTestApi::ImeListViewTestApi(ImeListView* ime_list_view)
461 : ime_list_view_(ime_list_view) {}
462
463 ImeListViewTestApi::~ImeListViewTestApi() {}
464
465 views::View* ImeListViewTestApi::GetToggleView() const {
466 return ime_list_view_->material_keyboard_status_view_->toggle();
467 }
468
469 } // namespace ash
OLDNEW
« no previous file with comments | « ash/common/system/chromeos/ime_menu/ime_list_view.h ('k') | ash/common/system/chromeos/ime_menu/ime_menu_tray.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698