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/infolist_window.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "ash/ime/candidate_window_constants.h"
     11 #include "base/logging.h"
     12 #include "grit/ash_strings.h"
     13 #include "ui/base/l10n/l10n_util.h"
     14 #include "ui/gfx/color_utils.h"
     15 #include "ui/native_theme/native_theme.h"
     16 #include "ui/views/background.h"
     17 #include "ui/views/border.h"
     18 #include "ui/views/bubble/bubble_border.h"
     19 #include "ui/views/bubble/bubble_frame_view.h"
     20 #include "ui/views/controls/label.h"
     21 #include "ui/views/layout/box_layout.h"
     22 #include "ui/views/widget/widget.h"
     23 #include "ui/wm/core/window_animations.h"
     24 
     25 namespace ash {
     26 namespace ime {
     27 
     28 namespace {
     29 // The width of an info-list.
     30 const int kInfolistEntryWidth = 200;
     31 
     32 // The milliseconds of the delay to show the infolist window.
     33 const int kInfolistShowDelayMilliSeconds = 500;
     34 // The milliseconds of the delay to hide the infolist window.
     35 const int kInfolistHideDelayMilliSeconds = 500;
     36 
     37 ///////////////////////////////////////////////////////////////////////////////
     38 // InfolistBorder
     39 // The BubbleBorder subclass to draw the border and determine its position.
     40 class InfolistBorder : public views::BubbleBorder {
     41  public:
     42   InfolistBorder();
     43   virtual ~InfolistBorder();
     44 
     45   // views::BubbleBorder implementation.
     46   virtual gfx::Rect GetBounds(const gfx::Rect& anchor_rect,
     47                               const gfx::Size& contents_size) const OVERRIDE;
     48   virtual gfx::Insets GetInsets() const OVERRIDE;
     49 
     50  private:
     51   DISALLOW_COPY_AND_ASSIGN(InfolistBorder);
     52 };
     53 
     54 InfolistBorder::InfolistBorder()
     55     : views::BubbleBorder(views::BubbleBorder::LEFT_CENTER,
     56                           views::BubbleBorder::NO_SHADOW,
     57                           SK_ColorTRANSPARENT) {
     58   set_paint_arrow(views::BubbleBorder::PAINT_NONE);
     59 }
     60 
     61 InfolistBorder::~InfolistBorder() {}
     62 
     63 gfx::Rect InfolistBorder::GetBounds(const gfx::Rect& anchor_rect,
     64                                     const gfx::Size& contents_size) const {
     65   gfx::Rect bounds(contents_size);
     66   bounds.set_x(is_arrow_on_left(arrow()) ?
     67                anchor_rect.right() : anchor_rect.x() - contents_size.width());
     68   // InfolistBorder modifies the vertical position based on the arrow offset
     69   // although it doesn't draw the arrow. The arrow offset is the half of
     70   // |contents_size| by default but can be modified through the off-screen logic
     71   // in BubbleFrameView.
     72   bounds.set_y(anchor_rect.y() + contents_size.height() / 2 -
     73                GetArrowOffset(contents_size));
     74   return bounds;
     75 }
     76 
     77 gfx::Insets InfolistBorder::GetInsets() const {
     78   // This has to be specified and return empty insets to place the infolist
     79   // window without the gap.
     80   return gfx::Insets();
     81 }
     82 
     83 }  // namespace
     84 
     85 // InfolistRow renderes a row of a infolist.
     86 class InfolistEntryView : public views::View {
     87  public:
     88   InfolistEntryView(const ui::InfolistEntry& entry,
     89                     const gfx::FontList& title_font_list,
     90                     const gfx::FontList& description_font_list);
     91   virtual ~InfolistEntryView();
     92 
     93   void SetEntry(const ui::InfolistEntry& entry);
     94 
     95  private:
     96   // views::View implementation.
     97   virtual gfx::Size GetPreferredSize() const OVERRIDE;
     98 
     99   void UpdateBackground();
    100 
    101   ui::InfolistEntry entry_;
    102 
    103   // The title label. Owned by views hierarchy.
    104   views::Label* title_label_;
    105 
    106   // The description label. Owned by views hierarchy.
    107   views::Label* description_label_;
    108 
    109   DISALLOW_COPY_AND_ASSIGN(InfolistEntryView);
    110 };
    111 
    112 InfolistEntryView::InfolistEntryView(const ui::InfolistEntry& entry,
    113                                      const gfx::FontList& title_font_list,
    114                                      const gfx::FontList& description_font_list)
    115     : entry_(entry) {
    116   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
    117 
    118   title_label_ = new views::Label(entry.title, title_font_list);
    119   title_label_->SetPosition(gfx::Point(0, 0));
    120   title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    121   title_label_->SetBorder(views::Border::CreateEmptyBorder(4, 7, 2, 4));
    122 
    123   description_label_ = new views::Label(entry.body, description_font_list);
    124   description_label_->SetPosition(gfx::Point(0, 0));
    125   description_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    126   description_label_->SetMultiLine(true);
    127   description_label_->SizeToFit(kInfolistEntryWidth);
    128   description_label_->SetBorder(views::Border::CreateEmptyBorder(2, 17, 4, 4));
    129   AddChildView(title_label_);
    130   AddChildView(description_label_);
    131   UpdateBackground();
    132 }
    133 
    134 InfolistEntryView::~InfolistEntryView() {}
    135 
    136 void InfolistEntryView::SetEntry(const ui::InfolistEntry& entry) {
    137   if (entry_ == entry)
    138     return;
    139 
    140   entry_ = entry;
    141   title_label_->SetText(entry_.title);
    142   description_label_->SetText(entry_.body);
    143   UpdateBackground();
    144 }
    145 
    146 gfx::Size InfolistEntryView::GetPreferredSize() const {
    147   return gfx::Size(kInfolistEntryWidth, GetHeightForWidth(kInfolistEntryWidth));
    148 }
    149 
    150 void InfolistEntryView::UpdateBackground() {
    151   if (entry_.highlighted) {
    152     set_background(
    153       views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
    154           ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused)));
    155     SetBorder(views::Border::CreateSolidBorder(
    156         1,
    157         GetNativeTheme()->GetSystemColor(
    158             ui::NativeTheme::kColorId_FocusedBorderColor)));
    159   } else {
    160     set_background(NULL);
    161     SetBorder(views::Border::CreateEmptyBorder(1, 1, 1, 1));
    162   }
    163   SchedulePaint();
    164 }
    165 
    166 ///////////////////////////////////////////////////////////////////////////////
    167 // InfolistWindow
    168 
    169 InfolistWindow::InfolistWindow(views::View* candidate_window,
    170                                const std::vector<ui::InfolistEntry>& entries)
    171     : views::BubbleDelegateView(candidate_window, views::BubbleBorder::NONE),
    172       title_font_list_(gfx::Font(kJapaneseFontName, kFontSizeDelta + 15)),
    173       description_font_list_(gfx::Font(kJapaneseFontName,
    174                                        kFontSizeDelta + 11)) {
    175   set_use_focusless(true);
    176   set_accept_events(false);
    177   set_margins(gfx::Insets());
    178 
    179   set_background(
    180       views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
    181           ui::NativeTheme::kColorId_WindowBackground)));
    182   SetBorder(views::Border::CreateSolidBorder(
    183       1,
    184       GetNativeTheme()->GetSystemColor(
    185           ui::NativeTheme::kColorId_MenuBorderColor)));
    186 
    187   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
    188 
    189   views::Label* caption_label = new views::Label(
    190       l10n_util::GetStringUTF16(IDS_ASH_IME_INFOLIST_WINDOW_TITLE));
    191   caption_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    192   caption_label->SetEnabledColor(GetNativeTheme()->GetSystemColor(
    193       ui::NativeTheme::kColorId_LabelEnabledColor));
    194   caption_label->SetBorder(views::Border::CreateEmptyBorder(2, 2, 2, 2));
    195   caption_label->set_background(views::Background::CreateSolidBackground(
    196       color_utils::AlphaBlend(SK_ColorBLACK,
    197                               GetNativeTheme()->GetSystemColor(
    198                                   ui::NativeTheme::kColorId_WindowBackground),
    199                               0x10)));
    200 
    201   AddChildView(caption_label);
    202 
    203   for (size_t i = 0; i < entries.size(); ++i) {
    204     entry_views_.push_back(new InfolistEntryView(
    205         entries[i], title_font_list_, description_font_list_));
    206     AddChildView(entry_views_.back());
    207   }
    208 }
    209 
    210 InfolistWindow::~InfolistWindow() {
    211 }
    212 
    213 void InfolistWindow::InitWidget() {
    214   views::Widget* widget = views::BubbleDelegateView::CreateBubble(this);
    215   wm::SetWindowVisibilityAnimationType(
    216       widget->GetNativeView(),
    217       wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
    218 
    219   // BubbleFrameView will be initialized through CreateBubble.
    220   GetBubbleFrameView()->SetBubbleBorder(
    221       scoped_ptr<views::BubbleBorder>(new InfolistBorder()));
    222   SizeToContents();
    223 }
    224 
    225 void InfolistWindow::Relayout(const std::vector<ui::InfolistEntry>& entries) {
    226   size_t i = 0;
    227   for (; i < entries.size(); ++i) {
    228     if (i < entry_views_.size()) {
    229       entry_views_[i]->SetEntry(entries[i]);
    230     } else {
    231       InfolistEntryView* new_entry = new InfolistEntryView(
    232           entries[i], title_font_list_, description_font_list_);
    233       AddChildView(new_entry);
    234       entry_views_.push_back(new_entry);
    235     }
    236   }
    237 
    238   if (i < entry_views_.size()) {
    239     for (; i < entry_views_.size(); ++i)
    240       delete entry_views_[i];
    241     entry_views_.resize(entries.size());
    242   }
    243 
    244   Layout();
    245   GetBubbleFrameView()->bubble_border()->set_arrow_offset(0);
    246   SizeToContents();
    247 }
    248 
    249 void InfolistWindow::ShowWithDelay() {
    250   show_hide_timer_.Start(
    251       FROM_HERE,
    252       base::TimeDelta::FromMilliseconds(kInfolistShowDelayMilliSeconds),
    253       GetWidget(),
    254       &views::Widget::Show);
    255 }
    256 
    257 void InfolistWindow::HideWithDelay() {
    258   show_hide_timer_.Start(
    259       FROM_HERE,
    260       base::TimeDelta::FromMilliseconds(kInfolistHideDelayMilliSeconds),
    261       GetWidget(),
    262       &views::Widget::Close);
    263 }
    264 
    265 void InfolistWindow::ShowImmediately() {
    266   show_hide_timer_.Stop();
    267   GetWidget()->Show();
    268 }
    269 
    270 void InfolistWindow::HideImmediately() {
    271   show_hide_timer_.Stop();
    272   GetWidget()->Close();
    273 }
    274 
    275 void InfolistWindow::WindowClosing() {
    276   show_hide_timer_.Stop();
    277 }
    278 
    279 }  // namespace ime
    280 }  // namespace ash
    281