1 // Copyright 2014 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.
5 #include "base/strings/utf_string_conversions.h"
6 #include "ui/base/ime/candidate_window.h"
7 #include "ui/chromeos/ime/candidate_view.h"
8 #include "ui/chromeos/ime/candidate_window_constants.h"
9 #include "ui/gfx/color_utils.h"
10 #include "ui/native_theme/native_theme.h"
11 #include "ui/views/background.h"
12 #include "ui/views/border.h"
13 #include "ui/views/controls/label.h"
14 #include "ui/views/widget/widget.h"
21 // VerticalCandidateLabel is used for rendering candidate text in
22 // the vertical candidate window.
23 class VerticalCandidateLabel
: public views::Label
{
25 VerticalCandidateLabel() {}
28 ~VerticalCandidateLabel() override
{}
31 // Returns the preferred size, but guarantees that the width has at
32 // least kMinCandidateLabelWidth pixels.
33 gfx::Size
GetPreferredSize() const override
{
34 gfx::Size size
= Label::GetPreferredSize();
35 size
.SetToMax(gfx::Size(kMinCandidateLabelWidth
, 0));
36 size
.SetToMin(gfx::Size(kMaxCandidateLabelWidth
, size
.height()));
40 const char* GetClassName() const override
{ return "VerticalCandidateLabel"; }
42 DISALLOW_COPY_AND_ASSIGN(VerticalCandidateLabel
);
45 // Creates the shortcut label, and returns it (never returns NULL).
46 // The label text is not set in this function.
47 views::Label
* CreateShortcutLabel(
48 ui::CandidateWindow::Orientation orientation
,
49 const ui::NativeTheme
& theme
) {
50 // Create the shortcut label. The label will be owned by
51 // |wrapped_shortcut_label|, hence it's deleted when
52 // |wrapped_shortcut_label| is deleted.
53 views::Label
* shortcut_label
= new views::Label
;
55 if (orientation
== ui::CandidateWindow::VERTICAL
) {
56 shortcut_label
->SetFontList(
57 shortcut_label
->font_list().Derive(kFontSizeDelta
, gfx::Font::BOLD
));
59 shortcut_label
->SetFontList(
60 shortcut_label
->font_list().DeriveWithSizeDelta(kFontSizeDelta
));
62 // TODO(satorux): Maybe we need to use language specific fonts for
63 // candidate_label, like Chinese font for Chinese input method?
64 shortcut_label
->SetEnabledColor(theme
.GetSystemColor(
65 ui::NativeTheme::kColorId_LabelEnabledColor
));
66 shortcut_label
->SetDisabledColor(theme
.GetSystemColor(
67 ui::NativeTheme::kColorId_LabelDisabledColor
));
70 const gfx::Insets
kVerticalShortcutLabelInsets(1, 6, 1, 6);
71 const gfx::Insets
kHorizontalShortcutLabelInsets(1, 3, 1, 0);
72 const gfx::Insets insets
=
73 (orientation
== ui::CandidateWindow::VERTICAL
?
74 kVerticalShortcutLabelInsets
:
75 kHorizontalShortcutLabelInsets
);
76 shortcut_label
->SetBorder(views::Border::CreateEmptyBorder(
77 insets
.top(), insets
.left(), insets
.bottom(), insets
.right()));
79 // Add decoration based on the orientation.
80 if (orientation
== ui::CandidateWindow::VERTICAL
) {
81 // Set the background color.
82 SkColor blackish
= color_utils::AlphaBlend(
84 theme
.GetSystemColor(ui::NativeTheme::kColorId_WindowBackground
),
86 SkColor transparent_blakish
= color_utils::AlphaBlend(
87 SK_ColorTRANSPARENT
, blackish
, 0xE0);
88 shortcut_label
->set_background(
89 views::Background::CreateSolidBackground(transparent_blakish
));
91 shortcut_label
->SetElideBehavior(gfx::NO_ELIDE
);
93 return shortcut_label
;
96 // Creates the candidate label, and returns it (never returns NULL).
97 // The label text is not set in this function.
98 views::Label
* CreateCandidateLabel(
99 ui::CandidateWindow::Orientation orientation
) {
100 views::Label
* candidate_label
= NULL
;
102 // Create the candidate label. The label will be added to |this| as a
103 // child view, hence it's deleted when |this| is deleted.
104 if (orientation
== ui::CandidateWindow::VERTICAL
) {
105 candidate_label
= new VerticalCandidateLabel
;
107 candidate_label
= new views::Label
;
110 // Change the font size.
111 candidate_label
->SetFontList(
112 candidate_label
->font_list().DeriveWithSizeDelta(kFontSizeDelta
));
113 candidate_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
114 candidate_label
->SetElideBehavior(gfx::NO_ELIDE
);
116 return candidate_label
;
119 // Creates the annotation label, and return it (never returns NULL).
120 // The label text is not set in this function.
121 views::Label
* CreateAnnotationLabel(
122 ui::CandidateWindow::Orientation orientation
,
123 const ui::NativeTheme
& theme
) {
124 // Create the annotation label.
125 views::Label
* annotation_label
= new views::Label
;
127 // Change the font size and color.
128 annotation_label
->SetFontList(
129 annotation_label
->font_list().DeriveWithSizeDelta(kFontSizeDelta
));
130 annotation_label
->SetEnabledColor(theme
.GetSystemColor(
131 ui::NativeTheme::kColorId_LabelDisabledColor
));
132 annotation_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
133 annotation_label
->SetElideBehavior(gfx::NO_ELIDE
);
135 return annotation_label
;
140 CandidateView::CandidateView(
141 views::ButtonListener
* listener
,
142 ui::CandidateWindow::Orientation orientation
)
143 : views::CustomButton(listener
),
144 orientation_(orientation
),
145 shortcut_label_(NULL
),
146 candidate_label_(NULL
),
147 annotation_label_(NULL
),
148 infolist_icon_(NULL
),
151 highlighted_(false) {
152 SetBorder(views::Border::CreateEmptyBorder(1, 1, 1, 1));
154 const ui::NativeTheme
& theme
= *GetNativeTheme();
155 shortcut_label_
= CreateShortcutLabel(orientation
, theme
);
156 candidate_label_
= CreateCandidateLabel(orientation
);
157 annotation_label_
= CreateAnnotationLabel(orientation
, theme
);
159 AddChildView(shortcut_label_
);
160 AddChildView(candidate_label_
);
161 AddChildView(annotation_label_
);
163 if (orientation
== ui::CandidateWindow::VERTICAL
) {
164 infolist_icon_
= new views::View
;
165 infolist_icon_
->set_background(
166 views::Background::CreateSolidBackground(theme
.GetSystemColor(
167 ui::NativeTheme::kColorId_FocusedBorderColor
)));
168 AddChildView(infolist_icon_
);
172 void CandidateView::GetPreferredWidths(int* shortcut_width
,
173 int* candidate_width
) {
174 *shortcut_width
= shortcut_label_
->GetPreferredSize().width();
175 *candidate_width
= candidate_label_
->GetPreferredSize().width();
178 void CandidateView::SetWidths(int shortcut_width
, int candidate_width
) {
179 shortcut_width_
= shortcut_width
;
180 shortcut_label_
->SetVisible(shortcut_width_
!= 0);
181 candidate_width_
= candidate_width
;
184 void CandidateView::SetEntry(const ui::CandidateWindow::Entry
& entry
) {
185 base::string16 label
= entry
.label
;
186 if (!label
.empty() && orientation_
!= ui::CandidateWindow::VERTICAL
)
187 label
+= base::ASCIIToUTF16(".");
188 shortcut_label_
->SetText(label
);
189 candidate_label_
->SetText(entry
.value
);
190 annotation_label_
->SetText(entry
.annotation
);
193 void CandidateView::SetInfolistIcon(bool enable
) {
195 infolist_icon_
->SetVisible(enable
);
199 void CandidateView::SetHighlighted(bool highlighted
) {
200 if (highlighted_
== highlighted
)
203 highlighted_
= highlighted
;
205 ui::NativeTheme
* theme
= GetNativeTheme();
207 views::Background::CreateSolidBackground(theme
->GetSystemColor(
208 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused
)));
209 SetBorder(views::Border::CreateSolidBorder(
211 theme
->GetSystemColor(ui::NativeTheme::kColorId_FocusedBorderColor
)));
213 // Cancel currently focused one.
214 for (int i
= 0; i
< parent()->child_count(); ++i
) {
215 CandidateView
* view
=
216 static_cast<CandidateView
*>((parent()->child_at(i
)));
218 view
->SetHighlighted(false);
221 set_background(NULL
);
222 SetBorder(views::Border::CreateEmptyBorder(1, 1, 1, 1));
227 void CandidateView::StateChanged() {
228 shortcut_label_
->SetEnabled(state() != STATE_DISABLED
);
229 if (state() == STATE_PRESSED
)
230 SetHighlighted(true);
233 const char* CandidateView::GetClassName() const {
234 return "CandidateView";
237 bool CandidateView::OnMouseDragged(const ui::MouseEvent
& event
) {
238 if (!HitTestPoint(event
.location())) {
239 // Moves the drag target to the sibling view.
240 gfx::Point
location_in_widget(event
.location());
241 ConvertPointToWidget(this, &location_in_widget
);
242 for (int i
= 0; i
< parent()->child_count(); ++i
) {
243 CandidateView
* sibling
=
244 static_cast<CandidateView
*>(parent()->child_at(i
));
247 gfx::Point
location_in_sibling(location_in_widget
);
248 ConvertPointFromWidget(sibling
, &location_in_sibling
);
249 if (sibling
->HitTestPoint(location_in_sibling
)) {
250 GetWidget()->GetRootView()->SetMouseHandler(sibling
);
251 sibling
->SetHighlighted(true);
252 return sibling
->OnMouseDragged(ui::MouseEvent(event
, this, sibling
));
259 return views::CustomButton::OnMouseDragged(event
);
262 void CandidateView::Layout() {
263 const int padding_width
=
264 orientation_
== ui::CandidateWindow::VERTICAL
? 4 : 6;
266 shortcut_label_
->SetBounds(x
, 0, shortcut_width_
, height());
267 if (shortcut_width_
> 0)
268 x
+= shortcut_width_
+ padding_width
;
269 candidate_label_
->SetBounds(x
, 0, candidate_width_
, height());
270 x
+= candidate_width_
+ padding_width
;
272 int right
= bounds().right();
273 if (infolist_icon_
&& infolist_icon_
->visible()) {
274 infolist_icon_
->SetBounds(
275 right
- kInfolistIndicatorIconWidth
- kInfolistIndicatorIconPadding
,
276 kInfolistIndicatorIconPadding
,
277 kInfolistIndicatorIconWidth
,
278 height() - kInfolistIndicatorIconPadding
* 2);
279 right
-= kInfolistIndicatorIconWidth
+ kInfolistIndicatorIconPadding
* 2;
281 annotation_label_
->SetBounds(x
, 0, right
- x
, height());
284 gfx::Size
CandidateView::GetPreferredSize() const {
285 const int padding_width
=
286 orientation_
== ui::CandidateWindow::VERTICAL
? 4 : 6;
288 if (shortcut_label_
->visible()) {
289 size
= shortcut_label_
->GetPreferredSize();
290 size
.SetToMax(gfx::Size(shortcut_width_
, 0));
291 size
.Enlarge(padding_width
, 0);
293 gfx::Size candidate_size
= candidate_label_
->GetPreferredSize();
294 candidate_size
.SetToMax(gfx::Size(candidate_width_
, 0));
295 size
.Enlarge(candidate_size
.width() + padding_width
, 0);
296 size
.SetToMax(candidate_size
);
297 if (annotation_label_
->visible()) {
298 gfx::Size annotation_size
= annotation_label_
->GetPreferredSize();
299 size
.Enlarge(annotation_size
.width() + padding_width
, 0);
300 size
.SetToMax(annotation_size
);
303 // Reserves the margin for infolist_icon even if it's not visible.
305 kInfolistIndicatorIconWidth
+ kInfolistIndicatorIconPadding
* 2, 0);