Home | History | Annotate | Download | only in ime
      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:
     26   VerticalCandidateLabel() {}
     27 
     28  private:
     29   virtual ~VerticalCandidateLabel() {}
     30 
     31   // Returns the preferred size, but guarantees that the width has at
     32   // least kMinCandidateLabelWidth pixels.
     33   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.
     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.
     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.
    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 
    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 
    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 
    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 
    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 
    188 void CandidateView::SetInfolistIcon(bool enable) {
    189   if (infolist_icon_)
    190     infolist_icon_->SetVisible(enable);
    191   SchedulePaint();
    192 }
    193 
    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 
    222 void CandidateView::StateChanged() {
    223   shortcut_label_->SetEnabled(state() != STATE_DISABLED);
    224   if (state() == STATE_PRESSED)
    225     SetHighlighted(true);
    226 }
    227 
    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 
    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 
    275 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