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 17namespace ash { 18namespace ime { 19 20namespace { 21 22// VerticalCandidateLabel is used for rendering candidate text in 23// the vertical candidate window. 24class 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. 45views::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. 95views::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. 117views::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 135CandidateView::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 167void 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 173void 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 179void 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 188void CandidateView::SetInfolistIcon(bool enable) { 189 if (infolist_icon_) 190 infolist_icon_->SetVisible(enable); 191 SchedulePaint(); 192} 193 194void 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 222void CandidateView::StateChanged() { 223 shortcut_label_->SetEnabled(state() != STATE_DISABLED); 224 if (state() == STATE_PRESSED) 225 SetHighlighted(true); 226} 227 228bool 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 253void 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 275gfx::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