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