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