123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/system/ime_menu/ime_list_view.h"
- #include "ash/ime/ime_controller_impl.h"
- #include "ash/ime/ime_switch_type.h"
- #include "ash/keyboard/keyboard_controller_impl.h"
- #include "ash/keyboard/ui/keyboard_util.h"
- #include "ash/keyboard/virtual_keyboard_controller.h"
- #include "ash/public/cpp/ime_info.h"
- #include "ash/resources/vector_icons/vector_icons.h"
- #include "ash/shell.h"
- #include "ash/strings/grit/ash_strings.h"
- #include "ash/style/ash_color_provider.h"
- #include "ash/system/tray/actionable_view.h"
- #include "ash/system/tray/system_menu_button.h"
- #include "ash/system/tray/tray_detailed_view.h"
- #include "ash/system/tray/tray_popup_utils.h"
- #include "ash/system/tray/tray_toggle_button.h"
- #include "ash/system/tray/tri_view.h"
- #include "base/bind.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/metrics/user_metrics.h"
- #include "ui/accessibility/ax_enums.mojom.h"
- #include "ui/accessibility/ax_node_data.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/base/metadata/metadata_impl_macros.h"
- #include "ui/base/resource/resource_bundle.h"
- #include "ui/gfx/color_palette.h"
- #include "ui/gfx/paint_vector_icon.h"
- #include "ui/views/animation/ink_drop.h"
- #include "ui/views/background.h"
- #include "ui/views/controls/button/toggle_button.h"
- #include "ui/views/controls/image_view.h"
- #include "ui/views/controls/label.h"
- #include "ui/views/controls/separator.h"
- #include "ui/views/layout/fill_layout.h"
- #include "ui/views/painter.h"
- #include "ui/views/view.h"
- #include "ui/views/widget/widget.h"
- namespace ash {
- namespace {
- const int kMinFontSizeDelta = -10;
- // Represents a row in the scrollable IME list; each row is either an IME or
- // an IME property. A checkmark icon is shown in the row if selected.
- class ImeListItemView : public ActionableView {
- public:
- METADATA_HEADER(ImeListItemView);
- ImeListItemView(ImeListView* list_view,
- const std::u16string& id,
- const std::u16string& label,
- bool selected,
- const SkColor button_color)
- : ActionableView(TrayPopupInkDropStyle::FILL_BOUNDS),
- ime_list_view_(list_view),
- selected_(selected) {
- views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
- TriView* tri_view = TrayPopupUtils::CreateDefaultRowView();
- AddChildView(tri_view);
- SetLayoutManager(std::make_unique<views::FillLayout>());
- // |id_label| contains the IME short name (e.g., 'US', 'GB', 'IT').
- views::Label* id_label = TrayPopupUtils::CreateDefaultLabel();
- id_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kTextColorPrimary));
- id_label->SetAutoColorReadabilityEnabled(false);
- id_label->SetText(id);
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
- const gfx::FontList& base_font_list =
- rb.GetFontList(ui::ResourceBundle::MediumBoldFont);
- id_label->SetFontList(base_font_list);
- // IMEs having a short name of more than two characters (e.g., 'INTL') will
- // elide if rendered within |kMenuIconSize|. Shrink the font size until the
- // entire short name fits within the bounds.
- int size_delta = -1;
- while ((id_label->GetPreferredSize().width() -
- id_label->GetInsets().width()) > kMenuIconSize &&
- size_delta >= kMinFontSizeDelta) {
- id_label->SetFontList(base_font_list.DeriveWithSizeDelta(size_delta));
- --size_delta;
- }
- tri_view->AddView(TriView::Container::START, id_label);
- // The label shows the IME full name.
- auto* label_view = TrayPopupUtils::CreateDefaultLabel();
- label_view->SetText(label);
- label_view->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kTextColorPrimary));
- TrayPopupUtils::SetLabelFontList(
- label_view, TrayPopupUtils::FontStyle::kDetailedViewLabel);
- label_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
- tri_view->AddView(TriView::Container::CENTER, label_view);
- if (selected) {
- // The checked button indicates the IME is selected.
- views::ImageView* checked_image = TrayPopupUtils::CreateMainImageView();
- checked_image->SetImage(gfx::CreateVectorIcon(
- kHollowCheckCircleIcon, kMenuIconSize, button_color));
- tri_view->AddView(TriView::Container::END, checked_image);
- }
- SetAccessibleName(label_view->GetText());
- }
- ImeListItemView(const ImeListItemView&) = delete;
- ImeListItemView& operator=(const ImeListItemView&) = delete;
- ~ImeListItemView() override = default;
- // ActionableView:
- bool PerformAction(const ui::Event& event) override {
- ime_list_view_->set_last_item_selected_with_keyboard(
- ime_list_view_->should_focus_ime_after_selection_with_keyboard() &&
- event.type() == ui::EventType::ET_KEY_PRESSED);
- ime_list_view_->HandleViewClicked(this);
- return true;
- }
- void OnFocus() override {
- ActionableView::OnFocus();
- if (ime_list_view_)
- ime_list_view_->ScrollItemToVisible(this);
- }
- void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
- ActionableView::GetAccessibleNodeData(node_data);
- node_data->role = ax::mojom::Role::kCheckBox;
- node_data->SetCheckedState(selected_ ? ax::mojom::CheckedState::kTrue
- : ax::mojom::CheckedState::kFalse);
- }
- private:
- ImeListView* ime_list_view_;
- bool selected_;
- };
- BEGIN_METADATA(ImeListItemView, ActionableView)
- END_METADATA
- } // namespace
- // Contains a toggle button to let the user enable/disable whether the
- // on-screen keyboard should be shown when focusing a textfield. This row is
- // shown only under certain conditions, e.g., when an external keyboard is
- // attached and the user is in TabletMode mode.
- class KeyboardStatusRow : public views::View {
- public:
- METADATA_HEADER(KeyboardStatusRow);
- KeyboardStatusRow() = default;
- KeyboardStatusRow(const KeyboardStatusRow&) = delete;
- KeyboardStatusRow& operator=(const KeyboardStatusRow&) = delete;
- ~KeyboardStatusRow() override = default;
- views::ToggleButton* toggle() const { return toggle_; }
- void Init(views::Button::PressedCallback callback) {
- TrayPopupUtils::ConfigureAsStickyHeader(this);
- SetLayoutManager(std::make_unique<views::FillLayout>());
- TriView* tri_view = TrayPopupUtils::CreateDefaultRowView();
- AddChildView(tri_view);
- auto* color_provider = AshColorProvider::Get();
- // The on-screen keyboard image button.
- views::ImageView* keyboard_image = TrayPopupUtils::CreateMainImageView();
- keyboard_image->SetImage(gfx::CreateVectorIcon(
- kImeMenuOnScreenKeyboardIcon, kMenuIconSize,
- color_provider->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kIconColorPrimary)));
- tri_view->AddView(TriView::Container::START, keyboard_image);
- // The on-screen keyboard label ('On-screen keyboard').
- auto* label = TrayPopupUtils::CreateDefaultLabel();
- label->SetText(ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
- IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD));
- label->SetEnabledColor(color_provider->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kTextColorPrimary));
- TrayPopupUtils::SetLabelFontList(
- label, TrayPopupUtils::FontStyle::kDetailedViewLabel);
- tri_view->AddView(TriView::Container::CENTER, label);
- // The on-screen keyboard toggle button.
- toggle_ = new TrayToggleButton(
- std::move(callback),
- IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD);
- toggle_->SetIsOn(keyboard::IsKeyboardEnabled());
- tri_view->AddView(TriView::Container::END, toggle_);
- }
- private:
- // ToggleButton to toggle keyboard on or off.
- views::ToggleButton* toggle_ = nullptr;
- };
- BEGIN_METADATA(KeyboardStatusRow, views::View)
- END_METADATA
- ImeListView::ImeListView(DetailedViewDelegate* delegate)
- : TrayDetailedView(delegate) {}
- ImeListView::~ImeListView() = default;
- void ImeListView::Init(bool show_keyboard_toggle,
- SingleImeBehavior single_ime_behavior) {
- ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
- Update(ime_controller->current_ime().id, ime_controller->GetVisibleImes(),
- ime_controller->current_ime_menu_items(), show_keyboard_toggle,
- single_ime_behavior);
- }
- void ImeListView::Update(const std::string& current_ime_id,
- const std::vector<ImeInfo>& list,
- const std::vector<ImeMenuItem>& property_items,
- bool show_keyboard_toggle,
- SingleImeBehavior single_ime_behavior) {
- ResetImeListView();
- ime_map_.clear();
- property_map_.clear();
- CreateScrollableList();
- if (single_ime_behavior == ImeListView::SHOW_SINGLE_IME || list.size() > 1)
- AppendImeListAndProperties(current_ime_id, list, property_items);
- if (show_keyboard_toggle)
- PrependKeyboardStatusRow();
- Layout();
- SchedulePaint();
- if (should_focus_ime_after_selection_with_keyboard_ &&
- last_item_selected_with_keyboard_) {
- FocusCurrentImeIfNeeded();
- } else if (current_ime_view_) {
- ScrollItemToVisible(current_ime_view_);
- }
- }
- void ImeListView::ResetImeListView() {
- // Children are removed from the view hierarchy and deleted in Reset().
- Reset();
- keyboard_status_row_ = nullptr;
- current_ime_view_ = nullptr;
- }
- void ImeListView::ScrollItemToVisible(views::View* item_view) {
- if (scroll_content())
- scroll_content()->ScrollRectToVisible(item_view->bounds());
- }
- void ImeListView::CloseImeListView() {
- last_selected_item_id_.clear();
- current_ime_view_ = nullptr;
- last_item_selected_with_keyboard_ = false;
- GetWidget()->Close();
- }
- void ImeListView::AppendImeListAndProperties(
- const std::string& current_ime_id,
- const std::vector<ImeInfo>& list,
- const std::vector<ImeMenuItem>& property_list) {
- DCHECK(ime_map_.empty());
- for (size_t i = 0; i < list.size(); i++) {
- const bool selected = current_ime_id == list[i].id;
- views::View* ime_view = new ImeListItemView(
- this, list[i].short_name, list[i].name, selected,
- AshColorProvider::Get()->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kIconColorProminent));
- scroll_content()->AddChildView(ime_view);
- ime_map_[ime_view] = list[i].id;
- if (selected)
- current_ime_view_ = ime_view;
- // Add the properties, if any, of the currently-selected IME.
- if (selected && !property_list.empty()) {
- // Adds a separator on the top of property items.
- scroll_content()->AddChildView(
- TrayPopupUtils::CreateListItemSeparator(true));
- const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kIconColorPrimary);
- // Adds the property items.
- for (size_t i = 0; i < property_list.size(); i++) {
- ImeListItemView* property_view =
- new ImeListItemView(this, std::u16string(), property_list[i].label,
- property_list[i].checked, icon_color);
- scroll_content()->AddChildView(property_view);
- property_map_[property_view] = property_list[i].key;
- }
- // Adds a separator on the bottom of property items if there are still
- // other IMEs under the current one.
- if (i < list.size() - 1)
- scroll_content()->AddChildView(
- TrayPopupUtils::CreateListItemSeparator(true));
- }
- }
- }
- void ImeListView::PrependKeyboardStatusRow() {
- DCHECK(!keyboard_status_row_);
- keyboard_status_row_ = new KeyboardStatusRow;
- keyboard_status_row_->Init(base::BindRepeating(
- &ImeListView::KeyboardStatusTogglePressed, base::Unretained(this)));
- scroll_content()->AddChildViewAt(keyboard_status_row_, 0);
- }
- void ImeListView::KeyboardStatusTogglePressed() {
- Shell::Get()
- ->keyboard_controller()
- ->virtual_keyboard_controller()
- ->ToggleIgnoreExternalKeyboard();
- last_selected_item_id_.clear();
- last_item_selected_with_keyboard_ = false;
- }
- void ImeListView::HandleViewClicked(views::View* view) {
- ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
- std::map<views::View*, std::string>::const_iterator ime = ime_map_.find(view);
- if (ime != ime_map_.end()) {
- base::RecordAction(base::UserMetricsAction("StatusArea_IME_SwitchMode"));
- std::string ime_id = ime->second;
- last_selected_item_id_ = ime_id;
- ime_controller->SwitchImeById(ime_id, false /* show_message */);
- UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeSwitch", ImeSwitchType::kTray);
- } else {
- std::map<views::View*, std::string>::const_iterator property =
- property_map_.find(view);
- if (property == property_map_.end())
- return;
- const std::string key = property->second;
- last_selected_item_id_ = key;
- ime_controller->ActivateImeMenuItem(key);
- }
- if (!should_focus_ime_after_selection_with_keyboard_ ||
- !last_item_selected_with_keyboard_) {
- CloseImeListView();
- }
- }
- void ImeListView::VisibilityChanged(View* starting_from, bool is_visible) {
- if (!is_visible ||
- (should_focus_ime_after_selection_with_keyboard_ &&
- last_item_selected_with_keyboard_) ||
- !current_ime_view_) {
- return;
- }
- ScrollItemToVisible(current_ime_view_);
- }
- void ImeListView::FocusCurrentImeIfNeeded() {
- views::FocusManager* manager = GetFocusManager();
- if (!manager || manager->GetFocusedView() || last_selected_item_id_.empty())
- return;
- for (auto ime_map : ime_map_) {
- if (ime_map.second == last_selected_item_id_) {
- (ime_map.first)->RequestFocus();
- return;
- }
- }
- for (auto property_map : property_map_) {
- if (property_map.second == last_selected_item_id_) {
- (property_map.first)->RequestFocus();
- return;
- }
- }
- }
- BEGIN_METADATA(ImeListView, TrayDetailedView)
- END_METADATA
- ImeListViewTestApi::ImeListViewTestApi(ImeListView* ime_list_view)
- : ime_list_view_(ime_list_view) {}
- ImeListViewTestApi::~ImeListViewTestApi() = default;
- views::View* ImeListViewTestApi::GetToggleView() const {
- if (!ime_list_view_->keyboard_status_row_)
- return nullptr;
- return ime_list_view_->keyboard_status_row_->toggle();
- }
- } // namespace ash
|