| // 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_menu_tray.h" |
| |
| #include "ash/accessibility/accessibility_controller_impl.h" |
| #include "ash/ime/ime_controller_impl.h" |
| #include "ash/keyboard/keyboard_controller_impl.h" |
| #include "ash/keyboard/ui/keyboard_ui_controller.h" |
| #include "ash/keyboard/virtual_keyboard_controller.h" |
| #include "ash/public/cpp/ash_constants.h" |
| #include "ash/public/cpp/system_tray_client.h" |
| #include "ash/resources/vector_icons/vector_icons.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shelf/shelf.h" |
| #include "ash/shell.h" |
| #include "ash/strings/grit/ash_strings.h" |
| #include "ash/style/ash_color_provider.h" |
| #include "ash/system/ime_menu/ime_list_view.h" |
| #include "ash/system/model/system_tray_model.h" |
| #include "ash/system/tray/detailed_view_delegate.h" |
| #include "ash/system/tray/system_menu_button.h" |
| #include "ash/system/tray/system_tray_notifier.h" |
| #include "ash/system/tray/tray_constants.h" |
| #include "ash/system/tray/tray_container.h" |
| #include "ash/system/tray/tray_popup_utils.h" |
| #include "ash/system/tray/tray_utils.h" |
| #include "ash/system/unified/top_shortcut_button.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "components/session_manager/session_manager_types.h" |
| #include "ui/base/ime/chromeos/extension_ime_util.h" |
| #include "ui/base/ime/chromeos/ime_bridge.h" |
| #include "ui/base/ime/text_input_client.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/gfx/geometry/insets.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| #include "ui/gfx/range/range.h" |
| #include "ui/views/controls/image_view.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/controls/scroll_view.h" |
| #include "ui/views/controls/separator.h" |
| #include "ui/views/layout/box_layout.h" |
| #include "ui/views/metadata/metadata_impl_macros.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Used for testing. |
| const int kEmojiButtonId = 1; |
| |
| // Insets for the title view (dp). |
| constexpr gfx::Insets kTitleViewPadding(0, 0, 0, 16); |
| |
| // Returns the height range of ImeListView. |
| gfx::Range GetImeListViewRange() { |
| const int max_items = 5; |
| const int min_items = 1; |
| const int tray_item_height = kTrayPopupItemMinHeight; |
| return gfx::Range(tray_item_height * min_items, tray_item_height * max_items); |
| } |
| |
| // Returns true if the current screen is login or lock screen. |
| bool IsInLoginOrLockScreen() { |
| using session_manager::SessionState; |
| SessionState state = Shell::Get()->session_controller()->GetSessionState(); |
| return state == SessionState::LOGIN_PRIMARY || |
| state == SessionState::LOCKED || |
| state == SessionState::LOGIN_SECONDARY; |
| } |
| |
| // Returns true if the current input context type is password. |
| bool IsInPasswordInputContext() { |
| return ui::IMEBridge::Get()->GetCurrentInputContext().type == |
| ui::TEXT_INPUT_TYPE_PASSWORD; |
| } |
| |
| class ImeMenuLabel : public views::Label { |
| public: |
| METADATA_HEADER(ImeMenuLabel); |
| |
| ImeMenuLabel() { |
| // Sometimes the label will be more than 2 characters, e.g. INTL and EXTD. |
| // This border makes sure we only leave room for ~2 and the others are |
| // truncated. |
| SetBorder(views::CreateEmptyBorder(gfx::Insets(0, 6))); |
| } |
| ImeMenuLabel(const ImeMenuLabel&) = delete; |
| ImeMenuLabel& operator=(const ImeMenuLabel&) = delete; |
| ~ImeMenuLabel() override = default; |
| |
| // views:Label: |
| gfx::Size CalculatePreferredSize() const override { |
| return gfx::Size(kTrayItemSize, kTrayItemSize); |
| } |
| int GetHeightForWidth(int width) const override { return kTrayItemSize; } |
| }; |
| |
| BEGIN_METADATA(ImeMenuLabel, views::Label) |
| END_METADATA |
| |
| class ImeMenuImageView : public views::ImageView { |
| public: |
| METADATA_HEADER(ImeMenuImageView); |
| |
| ImeMenuImageView() { SetBorder(views::CreateEmptyBorder(gfx::Insets(0, 6))); } |
| ImeMenuImageView(const ImeMenuImageView&) = delete; |
| ImeMenuImageView& operator=(const ImeMenuImageView&) = delete; |
| ~ImeMenuImageView() override = default; |
| }; |
| |
| BEGIN_METADATA(ImeMenuImageView, views::ImageView) |
| END_METADATA |
| |
| // The view that contains IME menu title. |
| class ImeTitleView : public views::View { |
| public: |
| METADATA_HEADER(ImeTitleView); |
| ImeTitleView() { |
| auto* color_provider = AshColorProvider::Get(); |
| SetBorder(views::CreatePaddedBorder( |
| views::CreateSolidSidedBorder( |
| 0, 0, kMenuSeparatorWidth, 0, |
| color_provider->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kSeparatorColor)), |
| gfx::Insets(kMenuSeparatorVerticalPadding - kMenuSeparatorWidth, 0))); |
| auto box_layout = std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kHorizontal, kTitleViewPadding); |
| box_layout->set_minimum_cross_axis_size(kTrayPopupItemMinHeight); |
| views::BoxLayout* layout_ptr = SetLayoutManager(std::move(box_layout)); |
| auto* title_label = AddChildView(std::make_unique<views::Label>( |
| l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME))); |
| title_label->SetBorder( |
| views::CreateEmptyBorder(0, kMenuEdgeEffectivePadding, 1, 0)); |
| title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| title_label->SetEnabledColor(color_provider->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorPrimary)); |
| TrayPopupUtils::SetLabelFontList(title_label, |
| TrayPopupUtils::FontStyle::kPodMenuHeader); |
| layout_ptr->SetFlexForView(title_label, 1); |
| |
| settings_button_ = AddChildView(std::make_unique<TopShortcutButton>( |
| base::BindRepeating([]() { |
| base::RecordAction( |
| base::UserMetricsAction("StatusArea_IME_Detailed")); |
| Shell::Get()->system_tray_model()->client()->ShowIMESettings(); |
| }), |
| kSystemMenuSettingsIcon, IDS_ASH_STATUS_TRAY_IME_SETTINGS)); |
| settings_button_->SetEnabled(TrayPopupUtils::CanOpenWebUISettings()); |
| } |
| ImeTitleView(const ImeTitleView&) = delete; |
| ImeTitleView& operator=(const ImeTitleView&) = delete; |
| |
| ~ImeTitleView() override = default; |
| |
| private: |
| TopShortcutButton* settings_button_ = nullptr; |
| }; |
| |
| BEGIN_METADATA(ImeTitleView, views::View) |
| END_METADATA |
| |
| // The view that contains buttons shown on the bottom of IME menu. |
| class ImeButtonsView : public views::View { |
| public: |
| METADATA_HEADER(ImeButtonsView); |
| ImeButtonsView(ImeMenuTray* ime_menu_tray, |
| bool show_emoji, |
| bool show_handwriting, |
| bool show_voice) |
| : ime_menu_tray_(ime_menu_tray) { |
| DCHECK(ime_menu_tray_); |
| |
| Init(show_emoji, show_handwriting, show_voice); |
| } |
| ImeButtonsView(const ImeButtonsView&) = delete; |
| ImeButtonsView& operator=(const ImeButtonsView&) = delete; |
| |
| ~ImeButtonsView() override = default; |
| |
| void KeysetButtonPressed(chromeos::input_method::ImeKeyset keyset) { |
| // TODO(dcheng): When https://crbug.com/742517 is fixed, Mojo will generate |
| // a constant for the number of values in the enum. For now, we just define |
| // it here and keep it in sync with the enum. |
| const int kImeKeysetUmaBoundary = 4; |
| UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeMenu.EmojiHandwritingVoiceButton", |
| keyset, kImeKeysetUmaBoundary); |
| // The |keyset| will be used for drawing input view keyset in IME |
| // extensions. ImeMenuTray::ShowKeyboardWithKeyset() will deal with |
| // the |keyset| string to generate the right input view url. |
| ime_menu_tray_->ShowKeyboardWithKeyset(keyset); |
| } |
| |
| private: |
| void Init(bool show_emoji, bool show_handwriting, bool show_voice) { |
| auto box_layout = std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kHorizontal); |
| box_layout->set_minimum_cross_axis_size(kTrayPopupItemMinHeight); |
| SetLayoutManager(std::move(box_layout)); |
| SetBorder(views::CreatePaddedBorder( |
| views::CreateSolidSidedBorder( |
| kMenuSeparatorWidth, 0, 0, 0, |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kSeparatorColor)), |
| gfx::Insets(kMenuSeparatorVerticalPadding - kMenuSeparatorWidth, |
| kMenuExtraMarginFromLeftEdge))); |
| |
| if (show_emoji) { |
| emoji_button_ = new SystemMenuButton( |
| base::BindRepeating(&ImeButtonsView::KeysetButtonPressed, |
| base::Unretained(this), |
| chromeos::input_method::ImeKeyset::kEmoji), |
| kImeMenuEmoticonIcon, IDS_ASH_STATUS_TRAY_IME_EMOJI); |
| emoji_button_->SetID(kEmojiButtonId); |
| AddChildView(emoji_button_); |
| } |
| |
| if (show_handwriting) { |
| handwriting_button_ = new SystemMenuButton( |
| base::BindRepeating(&ImeButtonsView::KeysetButtonPressed, |
| base::Unretained(this), |
| chromeos::input_method::ImeKeyset::kHandwriting), |
| kImeMenuWriteIcon, IDS_ASH_STATUS_TRAY_IME_HANDWRITING); |
| AddChildView(handwriting_button_); |
| } |
| |
| if (show_voice) { |
| voice_button_ = new SystemMenuButton( |
| base::BindRepeating(&ImeButtonsView::KeysetButtonPressed, |
| base::Unretained(this), |
| chromeos::input_method::ImeKeyset::kVoice), |
| kImeMenuMicrophoneIcon, IDS_ASH_STATUS_TRAY_IME_VOICE); |
| AddChildView(voice_button_); |
| } |
| } |
| |
| ImeMenuTray* ime_menu_tray_; |
| SystemMenuButton* emoji_button_; |
| SystemMenuButton* handwriting_button_; |
| SystemMenuButton* voice_button_; |
| }; |
| |
| BEGIN_METADATA(ImeButtonsView, views::View) |
| END_METADATA |
| |
| // A list of available IMEs shown in the opt-in IME menu, which has a different |
| // height depending on the number of IMEs in the list. |
| class ImeMenuListView : public ImeListView { |
| public: |
| METADATA_HEADER(ImeMenuListView); |
| |
| ImeMenuListView() : ImeMenuListView(std::make_unique<Delegate>()) {} |
| ImeMenuListView(const ImeMenuListView&) = delete; |
| ImeMenuListView& operator=(const ImeMenuListView&) = delete; |
| |
| ~ImeMenuListView() override = default; |
| |
| private: |
| class Delegate : public DetailedViewDelegate { |
| public: |
| Delegate() : DetailedViewDelegate(nullptr /* tray_controller */) {} |
| |
| // DetailedViewDelegate: |
| void TransitionToMainView(bool restore_focus) override {} |
| void CloseBubble() override {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(Delegate); |
| }; |
| |
| explicit ImeMenuListView(std::unique_ptr<Delegate> delegate) |
| : ImeListView(delegate.get()) { |
| set_should_focus_ime_after_selection_with_keyboard(true); |
| delegate_ = std::move(delegate); |
| } |
| |
| // ImeListView: |
| void Layout() override { |
| gfx::Range height_range = GetImeListViewRange(); |
| scroller()->ClipHeightTo(height_range.start(), height_range.end()); |
| ImeListView::Layout(); |
| } |
| |
| std::unique_ptr<Delegate> delegate_; |
| }; |
| |
| BEGIN_METADATA(ImeMenuListView, ImeListView) |
| END_METADATA |
| |
| } // namespace |
| |
| ImeMenuTray::ImeMenuTray(Shelf* shelf) |
| : TrayBackgroundView(shelf), |
| ime_controller_(Shell::Get()->ime_controller()), |
| label_(nullptr), |
| image_view_(nullptr), |
| keyboard_suppressed_(false), |
| show_bubble_after_keyboard_hidden_(false), |
| is_emoji_enabled_(false), |
| is_handwriting_enabled_(false), |
| is_voice_enabled_(false) { |
| DCHECK(ime_controller_); |
| CreateLabel(); |
| SystemTrayNotifier* tray_notifier = Shell::Get()->system_tray_notifier(); |
| tray_notifier->AddIMEObserver(this); |
| tray_notifier->AddVirtualKeyboardObserver(this); |
| |
| // Show the tray even if virtual keyboard is shown. (Other tray buttons will |
| // be hidden). |
| set_show_with_virtual_keyboard(true); |
| } |
| |
| ImeMenuTray::~ImeMenuTray() { |
| if (bubble_) |
| bubble_->bubble_view()->ResetDelegate(); |
| SystemTrayNotifier* tray_notifier = Shell::Get()->system_tray_notifier(); |
| tray_notifier->RemoveIMEObserver(this); |
| tray_notifier->RemoveVirtualKeyboardObserver(this); |
| auto* keyboard_controller = keyboard::KeyboardUIController::Get(); |
| if (keyboard_controller->HasObserver(this)) |
| keyboard_controller->RemoveObserver(this); |
| } |
| |
| void ImeMenuTray::ShowImeMenuBubbleInternal(bool show_by_click) { |
| TrayBubbleView::InitParams init_params; |
| init_params.delegate = this; |
| init_params.parent_window = GetBubbleWindowContainer(); |
| init_params.anchor_view = GetBubbleAnchor(); |
| init_params.shelf_alignment = shelf()->alignment(); |
| init_params.preferred_width = kTrayMenuWidth; |
| init_params.close_on_deactivate = true; |
| init_params.has_shadow = false; |
| init_params.translucent = true; |
| init_params.corner_radius = kTrayItemCornerRadius; |
| init_params.show_by_click = show_by_click; |
| |
| auto setup_layered_view = [](views::View* view) { |
| view->SetPaintToLayer(); |
| view->layer()->SetFillsBoundsOpaquely(false); |
| }; |
| |
| TrayBubbleView* bubble_view = new TrayBubbleView(init_params); |
| bubble_view->set_anchor_view_insets(GetBubbleAnchorInsets()); |
| bubble_view->set_margins(GetSecondaryBubbleInsets()); |
| |
| // Add a title item with a separator on the top of the IME menu. |
| setup_layered_view( |
| bubble_view->AddChildView(std::make_unique<ImeTitleView>())); |
| |
| // Adds IME list to the bubble. |
| ime_list_view_ = |
| bubble_view->AddChildView(std::make_unique<ImeMenuListView>()); |
| ime_list_view_->Init(ShouldShowKeyboardToggle(), |
| ImeListView::SHOW_SINGLE_IME); |
| setup_layered_view(ime_list_view_); |
| |
| if (ShouldShowBottomButtons()) { |
| setup_layered_view( |
| bubble_view->AddChildView(std::make_unique<ImeButtonsView>( |
| this, is_emoji_enabled_, is_handwriting_enabled_, |
| is_voice_enabled_))); |
| } |
| |
| bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view, |
| false /* is_persistent */); |
| SetIsActive(true); |
| } |
| |
| void ImeMenuTray::ShowKeyboardWithKeyset( |
| chromeos::input_method::ImeKeyset keyset) { |
| CloseBubble(); |
| |
| Shell::Get() |
| ->keyboard_controller() |
| ->virtual_keyboard_controller() |
| ->ForceShowKeyboardWithKeyset(keyset); |
| } |
| |
| bool ImeMenuTray::ShouldShowBottomButtons() { |
| // Emoji, handwriting and voice input is not supported for these cases: |
| // 1) third party IME extensions. |
| // 2) login/lock screen. |
| // 3) password input client. |
| |
| bool should_show_buttom_buttoms = |
| ime_controller_->is_extra_input_options_enabled() && |
| !ime_controller_->current_ime().third_party && !IsInLoginOrLockScreen() && |
| !IsInPasswordInputContext(); |
| |
| if (!should_show_buttom_buttoms) { |
| is_emoji_enabled_ = is_handwriting_enabled_ = is_voice_enabled_ = false; |
| return false; |
| } |
| |
| is_emoji_enabled_ = ime_controller_->is_emoji_enabled(); |
| is_handwriting_enabled_ = ime_controller_->is_handwriting_enabled(); |
| is_voice_enabled_ = ime_controller_->is_voice_enabled(); |
| |
| return is_emoji_enabled_ || is_handwriting_enabled_ || is_voice_enabled_; |
| } |
| |
| bool ImeMenuTray::ShouldShowKeyboardToggle() const { |
| return keyboard_suppressed_ && !Shell::Get() |
| ->accessibility_controller() |
| ->virtual_keyboard() |
| .enabled(); |
| } |
| |
| void ImeMenuTray::OnThemeChanged() { |
| TrayBackgroundView::OnThemeChanged(); |
| UpdateTrayLabel(); |
| } |
| |
| base::string16 ImeMenuTray::GetAccessibleNameForTray() { |
| return l10n_util::GetStringUTF16(IDS_ASH_IME_MENU_ACCESSIBLE_NAME); |
| } |
| |
| void ImeMenuTray::HandleLocaleChange() { |
| if (image_view_) { |
| image_view_->SetTooltipText( |
| l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME)); |
| } |
| |
| if (label_) |
| label_->SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME)); |
| } |
| |
| void ImeMenuTray::HideBubbleWithView(const TrayBubbleView* bubble_view) { |
| if (bubble_->bubble_view() == bubble_view) |
| CloseBubble(); |
| } |
| |
| void ImeMenuTray::ClickedOutsideBubble() { |
| CloseBubble(); |
| } |
| |
| bool ImeMenuTray::PerformAction(const ui::Event& event) { |
| UserMetricsRecorder::RecordUserClickOnTray( |
| LoginMetricsRecorder::TrayClickTarget::kImeTray); |
| if (bubble_) |
| CloseBubble(); |
| else |
| ShowBubble(event.IsMouseEvent() || event.IsGestureEvent()); |
| return true; |
| } |
| |
| void ImeMenuTray::CloseBubble() { |
| bubble_.reset(); |
| ime_list_view_ = nullptr; |
| SetIsActive(false); |
| shelf()->UpdateAutoHideState(); |
| } |
| |
| void ImeMenuTray::ShowBubble(bool show_by_click) { |
| auto* keyboard_controller = keyboard::KeyboardUIController::Get(); |
| if (keyboard_controller->IsKeyboardVisible()) { |
| show_bubble_after_keyboard_hidden_ = true; |
| keyboard_controller->AddObserver(this); |
| keyboard_controller->HideKeyboardExplicitlyBySystem(); |
| } else { |
| base::RecordAction(base::UserMetricsAction("Tray_ImeMenu_Opened")); |
| ShowImeMenuBubbleInternal(show_by_click); |
| } |
| } |
| |
| TrayBubbleView* ImeMenuTray::GetBubbleView() { |
| return bubble_ ? bubble_->bubble_view() : nullptr; |
| } |
| |
| void ImeMenuTray::OnIMERefresh() { |
| UpdateTrayLabel(); |
| if (bubble_ && ime_list_view_) { |
| ime_list_view_->Update(ime_controller_->current_ime().id, |
| ime_controller_->available_imes(), |
| ime_controller_->current_ime_menu_items(), false, |
| ImeListView::SHOW_SINGLE_IME); |
| } |
| } |
| |
| void ImeMenuTray::OnIMEMenuActivationChanged(bool is_activated) { |
| SetVisiblePreferred(is_activated); |
| if (is_activated) |
| UpdateTrayLabel(); |
| else |
| CloseBubble(); |
| } |
| |
| base::string16 ImeMenuTray::GetAccessibleNameForBubble() { |
| return l10n_util::GetStringUTF16(IDS_ASH_IME_MENU_ACCESSIBLE_NAME); |
| } |
| |
| bool ImeMenuTray::ShouldEnableExtraKeyboardAccessibility() { |
| return Shell::Get()->accessibility_controller()->spoken_feedback().enabled(); |
| } |
| |
| void ImeMenuTray::HideBubble(const TrayBubbleView* bubble_view) { |
| HideBubbleWithView(bubble_view); |
| } |
| |
| void ImeMenuTray::OnKeyboardHidden(bool is_temporary_hide) { |
| if (show_bubble_after_keyboard_hidden_) { |
| show_bubble_after_keyboard_hidden_ = false; |
| auto* keyboard_controller = keyboard::KeyboardUIController::Get(); |
| keyboard_controller->RemoveObserver(this); |
| |
| ShowImeMenuBubbleInternal(false /* show_by_click */); |
| return; |
| } |
| } |
| |
| void ImeMenuTray::OnKeyboardSuppressionChanged(bool suppressed) { |
| if (suppressed != keyboard_suppressed_ && bubble_) |
| CloseBubble(); |
| keyboard_suppressed_ = suppressed; |
| } |
| |
| void ImeMenuTray::UpdateTrayLabel() { |
| const ImeInfo& current_ime = ime_controller_->current_ime(); |
| |
| // For ARC IMEs, we use the globe icon instead of the short name of the active |
| // IME. |
| if (chromeos::extension_ime_util::IsArcIME(current_ime.id)) { |
| CreateImageView(); |
| image_view_->SetImage(gfx::CreateVectorIcon( |
| kShelfGlobeIcon, |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kIconColorPrimary))); |
| return; |
| } |
| |
| // Updates the tray label based on the current input method. |
| CreateLabel(); |
| |
| label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kIconColorPrimary)); |
| |
| if (current_ime.third_party) |
| label_->SetText(current_ime.short_name + base::UTF8ToUTF16("*")); |
| else |
| label_->SetText(current_ime.short_name); |
| } |
| |
| void ImeMenuTray::CreateLabel() { |
| // Do nothing if label_ is already created. |
| if (label_) |
| return; |
| // Remove image_view_ at first if it's created. |
| if (image_view_) { |
| tray_container()->RemoveChildView(image_view_); |
| image_view_ = nullptr; |
| } |
| label_ = new ImeMenuLabel(); |
| SetupLabelForTray(label_); |
| label_->SetElideBehavior(gfx::TRUNCATE); |
| label_->SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME)); |
| tray_container()->AddChildView(label_); |
| } |
| |
| void ImeMenuTray::CreateImageView() { |
| // Do nothing if image_view_ is already created. |
| if (image_view_) |
| return; |
| // Remove label_ at first if it's created. |
| if (label_) { |
| tray_container()->RemoveChildView(label_); |
| label_ = nullptr; |
| } |
| image_view_ = new ImeMenuImageView(); |
| image_view_->SetTooltipText( |
| l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME)); |
| tray_container()->AddChildView(image_view_); |
| } |
| |
| BEGIN_METADATA(ImeMenuTray, TrayBackgroundView) |
| END_METADATA |
| |
| } // namespace ash |