• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ash/ime/candidate_view.h"
6 
7 #include "ash/ime/candidate_window_constants.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "ui/base/ime/candidate_window.h"
10 #include "ui/gfx/color_utils.h"
11 #include "ui/native_theme/native_theme.h"
12 #include "ui/views/background.h"
13 #include "ui/views/border.h"
14 #include "ui/views/controls/label.h"
15 #include "ui/views/widget/widget.h"
16 
17 namespace ash {
18 namespace ime {
19 
20 namespace {
21 
22 // VerticalCandidateLabel is used for rendering candidate text in
23 // the vertical candidate window.
24 class VerticalCandidateLabel : public views::Label {
25  public:
VerticalCandidateLabel()26   VerticalCandidateLabel() {}
27 
28  private:
~VerticalCandidateLabel()29   virtual ~VerticalCandidateLabel() {}
30 
31   // Returns the preferred size, but guarantees that the width has at
32   // least kMinCandidateLabelWidth pixels.
GetPreferredSize() const33   virtual 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()));
37     return size;
38   }
39 
40   DISALLOW_COPY_AND_ASSIGN(VerticalCandidateLabel);
41 };
42 
43 // Creates the shortcut label, and returns it (never returns NULL).
44 // The label text is not set in this function.
CreateShortcutLabel(ui::CandidateWindow::Orientation orientation,const ui::NativeTheme & theme)45 views::Label* CreateShortcutLabel(
46     ui::CandidateWindow::Orientation orientation,
47     const ui::NativeTheme& theme) {
48   // Create the shortcut label. The label will be owned by
49   // |wrapped_shortcut_label|, hence it's deleted when
50   // |wrapped_shortcut_label| is deleted.
51   views::Label* shortcut_label = new views::Label;
52 
53   if (orientation == ui::CandidateWindow::VERTICAL) {
54     shortcut_label->SetFontList(
55         shortcut_label->font_list().Derive(kFontSizeDelta, gfx::Font::BOLD));
56   } else {
57     shortcut_label->SetFontList(
58         shortcut_label->font_list().DeriveWithSizeDelta(kFontSizeDelta));
59   }
60   // TODO(satorux): Maybe we need to use language specific fonts for
61   // candidate_label, like Chinese font for Chinese input method?
62   shortcut_label->SetEnabledColor(theme.GetSystemColor(
63       ui::NativeTheme::kColorId_LabelEnabledColor));
64   shortcut_label->SetDisabledColor(theme.GetSystemColor(
65       ui::NativeTheme::kColorId_LabelDisabledColor));
66 
67   // Setup paddings.
68   const gfx::Insets kVerticalShortcutLabelInsets(1, 6, 1, 6);
69   const gfx::Insets kHorizontalShortcutLabelInsets(1, 3, 1, 0);
70   const gfx::Insets insets =
71       (orientation == ui::CandidateWindow::VERTICAL ?
72        kVerticalShortcutLabelInsets :
73        kHorizontalShortcutLabelInsets);
74   shortcut_label->SetBorder(views::Border::CreateEmptyBorder(
75       insets.top(), insets.left(), insets.bottom(), insets.right()));
76 
77   // Add decoration based on the orientation.
78   if (orientation == ui::CandidateWindow::VERTICAL) {
79     // Set the background color.
80     SkColor blackish = color_utils::AlphaBlend(
81         SK_ColorBLACK,
82         theme.GetSystemColor(ui::NativeTheme::kColorId_WindowBackground),
83         0x40);
84     SkColor transparent_blakish = color_utils::AlphaBlend(
85         SK_ColorTRANSPARENT, blackish, 0xE0);
86     shortcut_label->set_background(
87         views::Background::CreateSolidBackground(transparent_blakish));
88   }
89 
90   return shortcut_label;
91 }
92 
93 // Creates the candidate label, and returns it (never returns NULL).
94 // The label text is not set in this function.
CreateCandidateLabel(ui::CandidateWindow::Orientation orientation)95 views::Label* CreateCandidateLabel(
96     ui::CandidateWindow::Orientation orientation) {
97   views::Label* candidate_label = NULL;
98 
99   // Create the candidate label. The label will be added to |this| as a
100   // child view, hence it's deleted when |this| is deleted.
101   if (orientation == ui::CandidateWindow::VERTICAL) {
102     candidate_label = new VerticalCandidateLabel;
103   } else {
104     candidate_label = new views::Label;
105   }
106 
107   // Change the font size.
108   candidate_label->SetFontList(
109       candidate_label->font_list().DeriveWithSizeDelta(kFontSizeDelta));
110   candidate_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
111 
112   return candidate_label;
113 }
114 
115 // Creates the annotation label, and return it (never returns NULL).
116 // The label text is not set in this function.
CreateAnnotationLabel(ui::CandidateWindow::Orientation orientation,const ui::NativeTheme & theme)117 views::Label* CreateAnnotationLabel(
118     ui::CandidateWindow::Orientation orientation,
119     const ui::NativeTheme& theme) {
120   // Create the annotation label.
121   views::Label* annotation_label = new views::Label;
122 
123   // Change the font size and color.
124   annotation_label->SetFontList(
125       annotation_label->font_list().DeriveWithSizeDelta(kFontSizeDelta));
126   annotation_label->SetEnabledColor(theme.GetSystemColor(
127       ui::NativeTheme::kColorId_LabelDisabledColor));
128   annotation_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
129 
130   return annotation_label;
131 }
132 
133 }  // namespace
134 
CandidateView(views::ButtonListener * listener,ui::CandidateWindow::Orientation orientation)135 CandidateView::CandidateView(
136     views::ButtonListener* listener,
137     ui::CandidateWindow::Orientation orientation)
138     : views::CustomButton(listener),
139       orientation_(orientation),
140       shortcut_label_(NULL),
141       candidate_label_(NULL),
142       annotation_label_(NULL),
143       infolist_icon_(NULL),
144       shortcut_width_(0),
145       candidate_width_(0),
146       highlighted_(false) {
147   SetBorder(views::Border::CreateEmptyBorder(1, 1, 1, 1));
148 
149   const ui::NativeTheme& theme = *GetNativeTheme();
150   shortcut_label_ = CreateShortcutLabel(orientation, theme);
151   candidate_label_ = CreateCandidateLabel(orientation);
152   annotation_label_ = CreateAnnotationLabel(orientation, theme);
153 
154   AddChildView(shortcut_label_);
155   AddChildView(candidate_label_);
156   AddChildView(annotation_label_);
157 
158   if (orientation == ui::CandidateWindow::VERTICAL) {
159     infolist_icon_ = new views::View;
160     infolist_icon_->set_background(
161         views::Background::CreateSolidBackground(theme.GetSystemColor(
162             ui::NativeTheme::kColorId_FocusedBorderColor)));
163     AddChildView(infolist_icon_);
164   }
165 }
166 
GetPreferredWidths(int * shortcut_width,int * candidate_width)167 void CandidateView::GetPreferredWidths(int* shortcut_width,
168                                        int* candidate_width) {
169   *shortcut_width = shortcut_label_->GetPreferredSize().width();
170   *candidate_width = candidate_label_->GetPreferredSize().width();
171 }
172 
SetWidths(int shortcut_width,int candidate_width)173 void CandidateView::SetWidths(int shortcut_width, int candidate_width) {
174   shortcut_width_ = shortcut_width;
175   shortcut_label_->SetVisible(shortcut_width_ != 0);
176   candidate_width_ = candidate_width;
177 }
178 
SetEntry(const ui::CandidateWindow::Entry & entry)179 void CandidateView::SetEntry(const ui::CandidateWindow::Entry& entry) {
180   base::string16 label = entry.label;
181   if (!label.empty() && orientation_ != ui::CandidateWindow::VERTICAL)
182     label += base::ASCIIToUTF16(".");
183   shortcut_label_->SetText(label);
184   candidate_label_->SetText(entry.value);
185   annotation_label_->SetText(entry.annotation);
186 }
187 
SetInfolistIcon(bool enable)188 void CandidateView::SetInfolistIcon(bool enable) {
189   if (infolist_icon_)
190     infolist_icon_->SetVisible(enable);
191   SchedulePaint();
192 }
193 
SetHighlighted(bool highlighted)194 void CandidateView::SetHighlighted(bool highlighted) {
195   if (highlighted_ == highlighted)
196     return;
197 
198   highlighted_ = highlighted;
199   if (highlighted) {
200     ui::NativeTheme* theme = GetNativeTheme();
201     set_background(
202         views::Background::CreateSolidBackground(theme->GetSystemColor(
203             ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused)));
204     SetBorder(views::Border::CreateSolidBorder(
205         1,
206         theme->GetSystemColor(ui::NativeTheme::kColorId_FocusedBorderColor)));
207 
208     // Cancel currently focused one.
209     for (int i = 0; i < parent()->child_count(); ++i) {
210       CandidateView* view =
211           static_cast<CandidateView*>((parent()->child_at(i)));
212       if (view != this)
213         view->SetHighlighted(false);
214     }
215   } else {
216     set_background(NULL);
217     SetBorder(views::Border::CreateEmptyBorder(1, 1, 1, 1));
218   }
219   SchedulePaint();
220 }
221 
StateChanged()222 void CandidateView::StateChanged() {
223   shortcut_label_->SetEnabled(state() != STATE_DISABLED);
224   if (state() == STATE_PRESSED)
225     SetHighlighted(true);
226 }
227 
OnMouseDragged(const ui::MouseEvent & event)228 bool CandidateView::OnMouseDragged(const ui::MouseEvent& event) {
229   if (!HitTestPoint(event.location())) {
230     // Moves the drag target to the sibling view.
231     gfx::Point location_in_widget(event.location());
232     ConvertPointToWidget(this, &location_in_widget);
233     for (int i = 0; i < parent()->child_count(); ++i) {
234       CandidateView* sibling =
235           static_cast<CandidateView*>(parent()->child_at(i));
236       if (sibling == this)
237         continue;
238       gfx::Point location_in_sibling(location_in_widget);
239       ConvertPointFromWidget(sibling, &location_in_sibling);
240       if (sibling->HitTestPoint(location_in_sibling)) {
241         GetWidget()->GetRootView()->SetMouseHandler(sibling);
242         sibling->SetHighlighted(true);
243         return sibling->OnMouseDragged(ui::MouseEvent(event, this, sibling));
244       }
245     }
246 
247     return false;
248   }
249 
250   return views::CustomButton::OnMouseDragged(event);
251 }
252 
Layout()253 void CandidateView::Layout() {
254   const int padding_width =
255       orientation_ == ui::CandidateWindow::VERTICAL ? 4 : 6;
256   int x = 0;
257   shortcut_label_->SetBounds(x, 0, shortcut_width_, height());
258   if (shortcut_width_ > 0)
259     x += shortcut_width_ + padding_width;
260   candidate_label_->SetBounds(x, 0, candidate_width_, height());
261   x += candidate_width_ + padding_width;
262 
263   int right = bounds().right();
264   if (infolist_icon_ && infolist_icon_->visible()) {
265     infolist_icon_->SetBounds(
266         right - kInfolistIndicatorIconWidth - kInfolistIndicatorIconPadding,
267         kInfolistIndicatorIconPadding,
268         kInfolistIndicatorIconWidth,
269         height() - kInfolistIndicatorIconPadding * 2);
270     right -= kInfolistIndicatorIconWidth + kInfolistIndicatorIconPadding * 2;
271   }
272   annotation_label_->SetBounds(x, 0, right - x, height());
273 }
274 
GetPreferredSize() const275 gfx::Size CandidateView::GetPreferredSize() const {
276   const int padding_width =
277       orientation_ == ui::CandidateWindow::VERTICAL ? 4 : 6;
278   gfx::Size size;
279   if (shortcut_label_->visible()) {
280     size = shortcut_label_->GetPreferredSize();
281     size.SetToMax(gfx::Size(shortcut_width_, 0));
282     size.Enlarge(padding_width, 0);
283   }
284   gfx::Size candidate_size = candidate_label_->GetPreferredSize();
285   candidate_size.SetToMax(gfx::Size(candidate_width_, 0));
286   size.Enlarge(candidate_size.width() + padding_width, 0);
287   size.SetToMax(candidate_size);
288   if (annotation_label_->visible()) {
289     gfx::Size annotation_size = annotation_label_->GetPreferredSize();
290     size.Enlarge(annotation_size.width() + padding_width, 0);
291     size.SetToMax(annotation_size);
292   }
293 
294   // Reserves the margin for infolist_icon even if it's not visible.
295   size.Enlarge(
296       kInfolistIndicatorIconWidth + kInfolistIndicatorIconPadding * 2, 0);
297   return size;
298 }
299 
300 }  // namespace ime
301 }  // namespace ash
302