ime_menu_tray.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  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. #include "ash/system/ime_menu/ime_menu_tray.h"
  5. #include "ash/accessibility/accessibility_controller_impl.h"
  6. #include "ash/ime/ime_controller_impl.h"
  7. #include "ash/keyboard/keyboard_controller_impl.h"
  8. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  9. #include "ash/keyboard/virtual_keyboard_controller.h"
  10. #include "ash/metrics/user_metrics_recorder.h"
  11. #include "ash/public/cpp/system_tray_client.h"
  12. #include "ash/resources/vector_icons/vector_icons.h"
  13. #include "ash/root_window_controller.h"
  14. #include "ash/session/session_controller_impl.h"
  15. #include "ash/shelf/shelf.h"
  16. #include "ash/shell.h"
  17. #include "ash/strings/grit/ash_strings.h"
  18. #include "ash/style/ash_color_provider.h"
  19. #include "ash/style/icon_button.h"
  20. #include "ash/system/ime_menu/ime_list_view.h"
  21. #include "ash/system/model/system_tray_model.h"
  22. #include "ash/system/tray/detailed_view_delegate.h"
  23. #include "ash/system/tray/system_menu_button.h"
  24. #include "ash/system/tray/system_tray_notifier.h"
  25. #include "ash/system/tray/tray_background_view.h"
  26. #include "ash/system/tray/tray_constants.h"
  27. #include "ash/system/tray/tray_container.h"
  28. #include "ash/system/tray/tray_popup_utils.h"
  29. #include "ash/system/tray/tray_utils.h"
  30. #include "base/bind.h"
  31. #include "base/metrics/histogram_macros.h"
  32. #include "base/metrics/user_metrics.h"
  33. #include "base/strings/utf_string_conversions.h"
  34. #include "components/session_manager/session_manager_types.h"
  35. #include "ui/base/emoji/emoji_panel_helper.h"
  36. #include "ui/base/ime/ash/extension_ime_util.h"
  37. #include "ui/base/ime/ash/ime_bridge.h"
  38. #include "ui/base/ime/text_input_client.h"
  39. #include "ui/base/l10n/l10n_util.h"
  40. #include "ui/base/metadata/metadata_impl_macros.h"
  41. #include "ui/base/resource/resource_bundle.h"
  42. #include "ui/compositor/layer.h"
  43. #include "ui/gfx/geometry/insets.h"
  44. #include "ui/gfx/paint_vector_icon.h"
  45. #include "ui/gfx/range/range.h"
  46. #include "ui/views/border.h"
  47. #include "ui/views/controls/image_view.h"
  48. #include "ui/views/controls/label.h"
  49. #include "ui/views/controls/scroll_view.h"
  50. #include "ui/views/controls/separator.h"
  51. #include "ui/views/layout/box_layout.h"
  52. #include "ui/views/layout/box_layout_view.h"
  53. namespace ash {
  54. namespace {
  55. // Used for testing.
  56. const int kEmojiButtonId = 1;
  57. const int kSettingsButtonId = 2;
  58. // Insets for the title view (dp).
  59. constexpr auto kTitleViewPadding = gfx::Insets::TLBR(0, 0, 0, 16);
  60. // Returns the height range of ImeListView.
  61. gfx::Range GetImeListViewRange() {
  62. const int max_items = 5;
  63. const int min_items = 1;
  64. const int tray_item_height = kTrayPopupItemMinHeight;
  65. return gfx::Range(tray_item_height * min_items, tray_item_height * max_items);
  66. }
  67. // Returns true if the current screen is login or lock screen.
  68. bool IsInLoginOrLockScreen() {
  69. using session_manager::SessionState;
  70. SessionState state = Shell::Get()->session_controller()->GetSessionState();
  71. return state == SessionState::LOGIN_PRIMARY ||
  72. state == SessionState::LOCKED ||
  73. state == SessionState::LOGIN_SECONDARY;
  74. }
  75. // Returns true if the current input context type is password.
  76. bool IsInPasswordInputContext() {
  77. return ui::IMEBridge::Get()->GetCurrentInputContext().type ==
  78. ui::TEXT_INPUT_TYPE_PASSWORD;
  79. }
  80. // Returns true if it is Kiosk Session.
  81. bool IsKioskSession() {
  82. return Shell::Get()->session_controller()->IsRunningInAppMode();
  83. }
  84. class ImeMenuLabel : public views::Label {
  85. public:
  86. METADATA_HEADER(ImeMenuLabel);
  87. ImeMenuLabel() {
  88. // Sometimes the label will be more than 2 characters, e.g. INTL and EXTD.
  89. // This border makes sure we only leave room for ~2 and the others are
  90. // truncated.
  91. SetBorder(views::CreateEmptyBorder(gfx::Insets::VH(0, 6)));
  92. }
  93. ImeMenuLabel(const ImeMenuLabel&) = delete;
  94. ImeMenuLabel& operator=(const ImeMenuLabel&) = delete;
  95. ~ImeMenuLabel() override = default;
  96. // views:Label:
  97. gfx::Size CalculatePreferredSize() const override {
  98. return gfx::Size(kTrayItemSize, kTrayItemSize);
  99. }
  100. int GetHeightForWidth(int width) const override { return kTrayItemSize; }
  101. };
  102. BEGIN_METADATA(ImeMenuLabel, views::Label)
  103. END_METADATA
  104. class ImeMenuImageView : public views::ImageView {
  105. public:
  106. METADATA_HEADER(ImeMenuImageView);
  107. ImeMenuImageView() {
  108. SetBorder(views::CreateEmptyBorder(gfx::Insets::VH(0, 6)));
  109. }
  110. ImeMenuImageView(const ImeMenuImageView&) = delete;
  111. ImeMenuImageView& operator=(const ImeMenuImageView&) = delete;
  112. ~ImeMenuImageView() override = default;
  113. };
  114. BEGIN_METADATA(ImeMenuImageView, views::ImageView)
  115. END_METADATA
  116. // The view that contains IME menu title.
  117. class ImeTitleView : public views::BoxLayoutView {
  118. public:
  119. METADATA_HEADER(ImeTitleView);
  120. ImeTitleView() {
  121. auto* color_provider = AshColorProvider::Get();
  122. SetBorder(views::CreatePaddedBorder(
  123. views::CreateSolidSidedBorder(
  124. gfx::Insets::TLBR(0, 0, kMenuSeparatorWidth, 0),
  125. color_provider->GetContentLayerColor(
  126. AshColorProvider::ContentLayerType::kSeparatorColor)),
  127. gfx::Insets::VH(kMenuSeparatorVerticalPadding - kMenuSeparatorWidth,
  128. 0)));
  129. SetOrientation(views::BoxLayout::Orientation::kHorizontal);
  130. SetInsideBorderInsets(kTitleViewPadding);
  131. SetMinimumCrossAxisSize(kTrayPopupItemMinHeight);
  132. auto* title_label = AddChildView(std::make_unique<views::Label>(
  133. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME)));
  134. title_label->SetBorder(views::CreateEmptyBorder(
  135. gfx::Insets::TLBR(0, kMenuEdgeEffectivePadding, 1, 0)));
  136. title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  137. title_label->SetEnabledColor(color_provider->GetContentLayerColor(
  138. AshColorProvider::ContentLayerType::kTextColorPrimary));
  139. TrayPopupUtils::SetLabelFontList(title_label,
  140. TrayPopupUtils::FontStyle::kPodMenuHeader);
  141. SetFlexForView(title_label, 1);
  142. // Don't create Settings Button if it is Kiosk session.
  143. if (!IsKioskSession()) {
  144. settings_button_ = AddChildView(std::make_unique<IconButton>(
  145. base::BindRepeating([]() {
  146. base::RecordAction(
  147. base::UserMetricsAction("StatusArea_IME_Detailed"));
  148. Shell::Get()->system_tray_model()->client()->ShowIMESettings();
  149. }),
  150. IconButton::Type::kSmall, &kSystemMenuSettingsIcon,
  151. IDS_ASH_STATUS_TRAY_IME_SETTINGS));
  152. settings_button_->SetEnabled(TrayPopupUtils::CanOpenWebUISettings());
  153. settings_button_->SetID(kSettingsButtonId);
  154. }
  155. }
  156. ImeTitleView(const ImeTitleView&) = delete;
  157. ImeTitleView& operator=(const ImeTitleView&) = delete;
  158. ~ImeTitleView() override = default;
  159. private:
  160. IconButton* settings_button_ = nullptr;
  161. };
  162. BEGIN_METADATA(ImeTitleView, views::BoxLayoutView)
  163. END_METADATA
  164. // The view that contains buttons shown on the bottom of IME menu.
  165. class ImeButtonsView : public views::View {
  166. public:
  167. METADATA_HEADER(ImeButtonsView);
  168. ImeButtonsView(ImeMenuTray* ime_menu_tray,
  169. bool show_emoji,
  170. bool show_handwriting,
  171. bool show_voice)
  172. : ime_menu_tray_(ime_menu_tray) {
  173. DCHECK(ime_menu_tray_);
  174. Init(show_emoji, show_handwriting, show_voice);
  175. }
  176. ImeButtonsView(const ImeButtonsView&) = delete;
  177. ImeButtonsView& operator=(const ImeButtonsView&) = delete;
  178. ~ImeButtonsView() override = default;
  179. void KeysetButtonPressed(input_method::ImeKeyset keyset) {
  180. // TODO(dcheng): When https://crbug.com/742517 is fixed, Mojo will generate
  181. // a constant for the number of values in the enum. For now, we just define
  182. // it here and keep it in sync with the enum.
  183. const int kImeKeysetUmaBoundary = 4;
  184. UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeMenu.EmojiHandwritingVoiceButton",
  185. keyset, kImeKeysetUmaBoundary);
  186. // The |keyset| will be used for drawing input view keyset in IME
  187. // extensions. ImeMenuTray::ShowKeyboardWithKeyset() will deal with
  188. // the |keyset| string to generate the right input view url.
  189. ime_menu_tray_->ShowKeyboardWithKeyset(keyset);
  190. }
  191. private:
  192. void Init(bool show_emoji, bool show_handwriting, bool show_voice) {
  193. auto box_layout = std::make_unique<views::BoxLayout>(
  194. views::BoxLayout::Orientation::kHorizontal);
  195. box_layout->set_minimum_cross_axis_size(kTrayPopupItemMinHeight);
  196. SetLayoutManager(std::move(box_layout));
  197. SetBorder(views::CreatePaddedBorder(
  198. views::CreateSolidSidedBorder(
  199. gfx::Insets::TLBR(kMenuSeparatorWidth, 0, 0, 0),
  200. AshColorProvider::Get()->GetContentLayerColor(
  201. AshColorProvider::ContentLayerType::kSeparatorColor)),
  202. gfx::Insets::VH(kMenuSeparatorVerticalPadding - kMenuSeparatorWidth,
  203. kMenuExtraMarginFromLeftEdge)));
  204. if (show_emoji) {
  205. emoji_button_ = new SystemMenuButton(
  206. base::BindRepeating(&ui::ShowEmojiPanel), kImeMenuEmoticonIcon,
  207. IDS_ASH_STATUS_TRAY_IME_EMOJI);
  208. emoji_button_->SetID(kEmojiButtonId);
  209. AddChildView(emoji_button_);
  210. }
  211. if (show_handwriting) {
  212. handwriting_button_ = new SystemMenuButton(
  213. base::BindRepeating(&ImeButtonsView::KeysetButtonPressed,
  214. base::Unretained(this),
  215. input_method::ImeKeyset::kHandwriting),
  216. kImeMenuWriteIcon, IDS_ASH_STATUS_TRAY_IME_HANDWRITING);
  217. AddChildView(handwriting_button_);
  218. }
  219. if (show_voice) {
  220. voice_button_ = new SystemMenuButton(
  221. base::BindRepeating(&ImeButtonsView::KeysetButtonPressed,
  222. base::Unretained(this),
  223. input_method::ImeKeyset::kVoice),
  224. kImeMenuMicrophoneIcon, IDS_ASH_STATUS_TRAY_IME_VOICE);
  225. AddChildView(voice_button_);
  226. }
  227. }
  228. ImeMenuTray* ime_menu_tray_;
  229. SystemMenuButton* emoji_button_;
  230. SystemMenuButton* handwriting_button_;
  231. SystemMenuButton* voice_button_;
  232. };
  233. BEGIN_METADATA(ImeButtonsView, views::View)
  234. END_METADATA
  235. // A list of available IMEs shown in the opt-in IME menu, which has a different
  236. // height depending on the number of IMEs in the list.
  237. class ImeMenuListView : public ImeListView {
  238. public:
  239. METADATA_HEADER(ImeMenuListView);
  240. ImeMenuListView() : ImeMenuListView(std::make_unique<Delegate>()) {}
  241. ImeMenuListView(const ImeMenuListView&) = delete;
  242. ImeMenuListView& operator=(const ImeMenuListView&) = delete;
  243. ~ImeMenuListView() override = default;
  244. private:
  245. class Delegate : public DetailedViewDelegate {
  246. public:
  247. Delegate() : DetailedViewDelegate(nullptr /* tray_controller */) {}
  248. Delegate(const Delegate&) = delete;
  249. Delegate& operator=(const Delegate&) = delete;
  250. // DetailedViewDelegate:
  251. void TransitionToMainView(bool restore_focus) override {}
  252. void CloseBubble() override {}
  253. };
  254. explicit ImeMenuListView(std::unique_ptr<Delegate> delegate)
  255. : ImeListView(delegate.get()) {
  256. set_should_focus_ime_after_selection_with_keyboard(true);
  257. delegate_ = std::move(delegate);
  258. }
  259. // ImeListView:
  260. void Layout() override {
  261. gfx::Range height_range = GetImeListViewRange();
  262. scroller()->ClipHeightTo(height_range.start(), height_range.end());
  263. ImeListView::Layout();
  264. }
  265. std::unique_ptr<Delegate> delegate_;
  266. };
  267. BEGIN_METADATA(ImeMenuListView, ImeListView)
  268. END_METADATA
  269. } // namespace
  270. ImeMenuTray::ImeMenuTray(Shelf* shelf)
  271. : TrayBackgroundView(shelf),
  272. ime_controller_(Shell::Get()->ime_controller()),
  273. label_(nullptr),
  274. image_view_(nullptr),
  275. keyboard_suppressed_(false),
  276. show_bubble_after_keyboard_hidden_(false),
  277. is_emoji_enabled_(false),
  278. is_handwriting_enabled_(false),
  279. is_voice_enabled_(false) {
  280. DCHECK(ime_controller_);
  281. CreateLabel();
  282. SystemTrayNotifier* tray_notifier = Shell::Get()->system_tray_notifier();
  283. tray_notifier->AddIMEObserver(this);
  284. tray_notifier->AddVirtualKeyboardObserver(this);
  285. // Show the tray even if virtual keyboard is shown. (Other tray buttons will
  286. // be hidden).
  287. set_show_with_virtual_keyboard(true);
  288. }
  289. ImeMenuTray::~ImeMenuTray() {
  290. if (bubble_)
  291. bubble_->bubble_view()->ResetDelegate();
  292. SystemTrayNotifier* tray_notifier = Shell::Get()->system_tray_notifier();
  293. tray_notifier->RemoveIMEObserver(this);
  294. tray_notifier->RemoveVirtualKeyboardObserver(this);
  295. auto* keyboard_controller = keyboard::KeyboardUIController::Get();
  296. if (keyboard_controller->HasObserver(this))
  297. keyboard_controller->RemoveObserver(this);
  298. }
  299. void ImeMenuTray::ShowImeMenuBubbleInternal() {
  300. TrayBubbleView::InitParams init_params;
  301. init_params.delegate = GetWeakPtr();
  302. init_params.parent_window = GetBubbleWindowContainer();
  303. init_params.anchor_view = nullptr;
  304. init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
  305. init_params.anchor_rect = GetBubbleAnchor()->GetAnchorBoundsInScreen();
  306. init_params.anchor_rect.Inset(GetBubbleAnchorInsets());
  307. init_params.shelf_alignment = shelf()->alignment();
  308. init_params.preferred_width = kTrayMenuWidth;
  309. init_params.close_on_deactivate = true;
  310. init_params.translucent = true;
  311. init_params.corner_radius = kTrayItemCornerRadius;
  312. init_params.reroute_event_handler = true;
  313. auto setup_layered_view = [](views::View* view) {
  314. // In dark light mode, we switch TrayBubbleView to use a textured layer
  315. // instead of solid color layer, so no need to create an extra layer here.
  316. if (features::IsDarkLightModeEnabled())
  317. return;
  318. view->SetPaintToLayer();
  319. view->layer()->SetFillsBoundsOpaquely(false);
  320. };
  321. TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
  322. bubble_view->set_margins(GetSecondaryBubbleInsets());
  323. // Add a title item with a separator on the top of the IME menu.
  324. setup_layered_view(
  325. bubble_view->AddChildView(std::make_unique<ImeTitleView>()));
  326. // Adds IME list to the bubble.
  327. ime_list_view_ =
  328. bubble_view->AddChildView(std::make_unique<ImeMenuListView>());
  329. ime_list_view_->Init(ShouldShowKeyboardToggle(),
  330. ImeListView::SHOW_SINGLE_IME);
  331. setup_layered_view(ime_list_view_);
  332. if (ShouldShowBottomButtons()) {
  333. setup_layered_view(
  334. bubble_view->AddChildView(std::make_unique<ImeButtonsView>(
  335. this, is_emoji_enabled_, is_handwriting_enabled_,
  336. is_voice_enabled_)));
  337. }
  338. bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view);
  339. SetIsActive(true);
  340. }
  341. void ImeMenuTray::ShowKeyboardWithKeyset(input_method::ImeKeyset keyset) {
  342. CloseBubble();
  343. Shell::Get()
  344. ->keyboard_controller()
  345. ->virtual_keyboard_controller()
  346. ->ForceShowKeyboardWithKeyset(keyset);
  347. }
  348. bool ImeMenuTray::ShouldShowBottomButtons() {
  349. // Emoji, handwriting and voice input is not supported for these cases:
  350. // 1) third party IME extensions.
  351. // 2) login/lock screen.
  352. // 3) password input client.
  353. const bool should_show_bottom_buttons =
  354. ime_controller_->is_extra_input_options_enabled() &&
  355. !ime_controller_->current_ime().third_party && !IsInLoginOrLockScreen() &&
  356. !IsInPasswordInputContext();
  357. if (!should_show_bottom_buttons) {
  358. is_emoji_enabled_ = is_handwriting_enabled_ = is_voice_enabled_ = false;
  359. return false;
  360. }
  361. is_emoji_enabled_ = ime_controller_->is_emoji_enabled();
  362. is_handwriting_enabled_ = ime_controller_->is_handwriting_enabled();
  363. is_voice_enabled_ = ime_controller_->is_voice_enabled();
  364. return is_emoji_enabled_ || is_handwriting_enabled_ || is_voice_enabled_;
  365. }
  366. bool ImeMenuTray::ShouldShowKeyboardToggle() const {
  367. return keyboard_suppressed_ && !Shell::Get()
  368. ->accessibility_controller()
  369. ->virtual_keyboard()
  370. .enabled();
  371. }
  372. void ImeMenuTray::OnThemeChanged() {
  373. TrayBackgroundView::OnThemeChanged();
  374. UpdateTrayLabel();
  375. }
  376. std::u16string ImeMenuTray::GetAccessibleNameForTray() {
  377. return l10n_util::GetStringUTF16(IDS_ASH_IME_MENU_ACCESSIBLE_NAME);
  378. }
  379. void ImeMenuTray::HandleLocaleChange() {
  380. if (image_view_) {
  381. image_view_->SetTooltipText(
  382. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME));
  383. }
  384. if (label_)
  385. label_->SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME));
  386. }
  387. void ImeMenuTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
  388. if (bubble_->bubble_view() == bubble_view)
  389. CloseBubble();
  390. }
  391. void ImeMenuTray::ClickedOutsideBubble() {
  392. CloseBubble();
  393. }
  394. bool ImeMenuTray::PerformAction(const ui::Event& event) {
  395. if (event.IsMouseEvent() || event.IsGestureEvent()) {
  396. UserMetricsRecorder::RecordUserClickOnTray(
  397. LoginMetricsRecorder::TrayClickTarget::kImeTray);
  398. }
  399. return TrayBackgroundView::PerformAction(event);
  400. }
  401. void ImeMenuTray::CloseBubble() {
  402. bubble_.reset();
  403. ime_list_view_ = nullptr;
  404. SetIsActive(false);
  405. shelf()->UpdateAutoHideState();
  406. }
  407. void ImeMenuTray::ShowBubble() {
  408. auto* keyboard_controller = keyboard::KeyboardUIController::Get();
  409. if (keyboard_controller->IsKeyboardVisible()) {
  410. show_bubble_after_keyboard_hidden_ = true;
  411. keyboard_controller->AddObserver(this);
  412. keyboard_controller->HideKeyboardExplicitlyBySystem();
  413. } else {
  414. base::RecordAction(base::UserMetricsAction("Tray_ImeMenu_Opened"));
  415. ShowImeMenuBubbleInternal();
  416. }
  417. }
  418. TrayBubbleView* ImeMenuTray::GetBubbleView() {
  419. return bubble_ ? bubble_->GetBubbleView() : nullptr;
  420. }
  421. views::Widget* ImeMenuTray::GetBubbleWidget() const {
  422. return bubble_ ? bubble_->GetBubbleWidget() : nullptr;
  423. }
  424. void ImeMenuTray::AddedToWidget() {
  425. // SetVisiblePreferred cannot be called until after the view has been added to
  426. // a widget.
  427. auto* ime_controller = Shell::Get()->ime_controller();
  428. // On the primary display, `ImeMenuTray` is created for the primary shelf, and
  429. // then `ImeObserver`s (of which `ImeMenuTray` is one) can react to IME menu
  430. // activation. If the IME menu is active, and then a display is connected,
  431. // this object will not have been notified of previous IME menu activations.
  432. // So check for that here and modify visibility. Only necessary for secondary
  433. // displays.
  434. if (!ime_controller || !ime_controller->is_menu_active())
  435. return;
  436. SetVisiblePreferred(true);
  437. UpdateTrayLabel();
  438. }
  439. void ImeMenuTray::OnIMERefresh() {
  440. UpdateTrayLabel();
  441. if (bubble_ && ime_list_view_) {
  442. ime_list_view_->Update(
  443. ime_controller_->current_ime().id, ime_controller_->GetVisibleImes(),
  444. ime_controller_->current_ime_menu_items(), ShouldShowKeyboardToggle(),
  445. ImeListView::SHOW_SINGLE_IME);
  446. }
  447. }
  448. void ImeMenuTray::OnIMEMenuActivationChanged(bool is_activated) {
  449. SetVisiblePreferred(is_activated);
  450. if (is_activated)
  451. UpdateTrayLabel();
  452. else
  453. CloseBubble();
  454. }
  455. std::u16string ImeMenuTray::GetAccessibleNameForBubble() {
  456. return l10n_util::GetStringUTF16(IDS_ASH_IME_MENU_ACCESSIBLE_NAME);
  457. }
  458. bool ImeMenuTray::ShouldEnableExtraKeyboardAccessibility() {
  459. return Shell::Get()->accessibility_controller()->spoken_feedback().enabled();
  460. }
  461. void ImeMenuTray::HideBubble(const TrayBubbleView* bubble_view) {
  462. HideBubbleWithView(bubble_view);
  463. }
  464. void ImeMenuTray::OnKeyboardHidden(bool is_temporary_hide) {
  465. if (show_bubble_after_keyboard_hidden_) {
  466. show_bubble_after_keyboard_hidden_ = false;
  467. auto* keyboard_controller = keyboard::KeyboardUIController::Get();
  468. keyboard_controller->RemoveObserver(this);
  469. ShowImeMenuBubbleInternal();
  470. return;
  471. }
  472. }
  473. void ImeMenuTray::OnKeyboardSuppressionChanged(bool suppressed) {
  474. if (suppressed != keyboard_suppressed_ && bubble_)
  475. CloseBubble();
  476. keyboard_suppressed_ = suppressed;
  477. }
  478. void ImeMenuTray::UpdateTrayLabel() {
  479. const ImeInfo& current_ime = ime_controller_->current_ime();
  480. // For ARC IMEs, we use the globe icon instead of the short name of the active
  481. // IME.
  482. if (extension_ime_util::IsArcIME(current_ime.id)) {
  483. CreateImageView();
  484. image_view_->SetImage(gfx::CreateVectorIcon(
  485. kShelfGlobeIcon,
  486. AshColorProvider::Get()->GetContentLayerColor(
  487. AshColorProvider::ContentLayerType::kIconColorPrimary)));
  488. return;
  489. }
  490. // Updates the tray label based on the current input method.
  491. CreateLabel();
  492. label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
  493. AshColorProvider::ContentLayerType::kIconColorPrimary));
  494. if (current_ime.third_party)
  495. label_->SetText(current_ime.short_name + u"*");
  496. else
  497. label_->SetText(current_ime.short_name);
  498. }
  499. void ImeMenuTray::CreateLabel() {
  500. // Do nothing if label_ is already created.
  501. if (label_)
  502. return;
  503. // Remove image_view_ at first if it's created.
  504. if (image_view_) {
  505. tray_container()->RemoveChildView(image_view_);
  506. image_view_ = nullptr;
  507. }
  508. label_ = new ImeMenuLabel();
  509. SetupLabelForTray(label_);
  510. label_->SetElideBehavior(gfx::TRUNCATE);
  511. label_->SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME));
  512. tray_container()->AddChildView(label_);
  513. }
  514. void ImeMenuTray::CreateImageView() {
  515. // Do nothing if image_view_ is already created.
  516. if (image_view_)
  517. return;
  518. // Remove label_ at first if it's created.
  519. if (label_) {
  520. tray_container()->RemoveChildView(label_);
  521. label_ = nullptr;
  522. }
  523. image_view_ = new ImeMenuImageView();
  524. image_view_->SetTooltipText(
  525. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME));
  526. tray_container()->AddChildView(image_view_);
  527. }
  528. BEGIN_METADATA(ImeMenuTray, TrayBackgroundView)
  529. END_METADATA
  530. } // namespace ash