Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2013 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 "ui/app_list/views/folder_header_view.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "ui/app_list/app_list_constants.h"
     11 #include "ui/app_list/app_list_folder_item.h"
     12 #include "ui/app_list/app_list_switches.h"
     13 #include "ui/app_list/views/app_list_folder_view.h"
     14 #include "ui/base/resource/resource_bundle.h"
     15 #include "ui/gfx/canvas.h"
     16 #include "ui/resources/grit/ui_resources.h"
     17 #include "ui/strings/grit/ui_strings.h"
     18 #include "ui/views/border.h"
     19 #include "ui/views/controls/button/image_button.h"
     20 #include "ui/views/controls/textfield/textfield.h"
     21 #include "ui/views/painter.h"
     22 
     23 namespace app_list {
     24 
     25 namespace {
     26 
     27 const int kPreferredWidth = 360;
     28 const int kPreferredHeight = 48;
     29 const int kIconDimension = 24;
     30 const int kBackButtonPadding = 14;
     31 const int kBottomSeparatorPadding = 9;  // Non-experimental app list only.
     32 const int kBottomSeparatorHeight = 1;
     33 const int kMaxFolderNameWidth = 300;
     34 
     35 const SkColor kHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
     36 
     37 }  // namespace
     38 
     39 class FolderHeaderView::FolderNameView : public views::Textfield {
     40  public:
     41   FolderNameView() {
     42     SetBorder(views::Border::CreateEmptyBorder(1, 1, 1, 1));
     43     const SkColor kFocusBorderColor = SkColorSetRGB(64, 128, 250);
     44     SetFocusPainter(views::Painter::CreateSolidFocusPainter(
     45           kFocusBorderColor,
     46           gfx::Insets(0, 0, 1, 1)));
     47   }
     48 
     49   virtual ~FolderNameView() {
     50   }
     51 
     52  private:
     53   DISALLOW_COPY_AND_ASSIGN(FolderNameView);
     54 };
     55 
     56 FolderHeaderView::FolderHeaderView(FolderHeaderViewDelegate* delegate)
     57     : folder_item_(NULL),
     58       back_button_(new views::ImageButton(this)),
     59       folder_name_view_(new FolderNameView),
     60       folder_name_placeholder_text_(
     61           ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
     62               IDS_APP_LIST_FOLDER_NAME_PLACEHOLDER)),
     63       delegate_(delegate),
     64       folder_name_visible_(true) {
     65   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
     66   back_button_->SetImage(views::ImageButton::STATE_NORMAL,
     67       rb.GetImageSkiaNamed(IDR_APP_LIST_FOLDER_BACK_NORMAL));
     68   back_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
     69       views::ImageButton::ALIGN_MIDDLE);
     70   AddChildView(back_button_);
     71   back_button_->SetFocusable(true);
     72   back_button_->SetAccessibleName(
     73       ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
     74           IDS_APP_LIST_FOLDER_CLOSE_FOLDER_ACCESSIBILE_NAME));
     75 
     76   folder_name_view_->SetFontList(
     77       rb.GetFontList(ui::ResourceBundle::MediumFont));
     78   folder_name_view_->set_placeholder_text_color(kHintTextColor);
     79   folder_name_view_->set_placeholder_text(folder_name_placeholder_text_);
     80   folder_name_view_->SetBorder(views::Border::NullBorder());
     81   folder_name_view_->SetBackgroundColor(kContentsBackgroundColor);
     82   folder_name_view_->set_controller(this);
     83   AddChildView(folder_name_view_);
     84 }
     85 
     86 FolderHeaderView::~FolderHeaderView() {
     87   if (folder_item_)
     88     folder_item_->RemoveObserver(this);
     89 }
     90 
     91 void FolderHeaderView::SetFolderItem(AppListFolderItem* folder_item) {
     92   if (folder_item_)
     93     folder_item_->RemoveObserver(this);
     94 
     95   folder_item_ = folder_item;
     96   if (!folder_item_)
     97     return;
     98   folder_item_->AddObserver(this);
     99 
    100   folder_name_view_->SetEnabled(folder_item_->folder_type() !=
    101                                 AppListFolderItem::FOLDER_TYPE_OEM);
    102 
    103   Update();
    104 }
    105 
    106 void FolderHeaderView::UpdateFolderNameVisibility(bool visible) {
    107   folder_name_visible_ = visible;
    108   Update();
    109   SchedulePaint();
    110 }
    111 
    112 void FolderHeaderView::OnFolderItemRemoved() {
    113   folder_item_ = NULL;
    114 }
    115 
    116 void FolderHeaderView::Update() {
    117   if (!folder_item_)
    118     return;
    119 
    120   folder_name_view_->SetVisible(folder_name_visible_);
    121   if (folder_name_visible_) {
    122     folder_name_view_->SetText(base::UTF8ToUTF16(folder_item_->name()));
    123     UpdateFolderNameAccessibleName();
    124   }
    125 
    126   Layout();
    127 }
    128 
    129 void FolderHeaderView::UpdateFolderNameAccessibleName() {
    130   // Sets |folder_name_view_|'s accessible name to the placeholder text if
    131   // |folder_name_view_| is blank; otherwise, clear the accessible name, the
    132   // accessible state's value is set to be folder_name_view_->text() by
    133   // TextField.
    134   base::string16 accessible_name = folder_name_view_->text().empty()
    135                                        ? folder_name_placeholder_text_
    136                                        : base::string16();
    137   folder_name_view_->SetAccessibleName(accessible_name);
    138 }
    139 
    140 const base::string16& FolderHeaderView::GetFolderNameForTest() {
    141   return folder_name_view_->text();
    142 }
    143 
    144 void FolderHeaderView::SetFolderNameForTest(const base::string16& name) {
    145   folder_name_view_->SetText(name);
    146 }
    147 
    148 bool FolderHeaderView::IsFolderNameEnabledForTest() const {
    149   return folder_name_view_->enabled();
    150 }
    151 
    152 gfx::Size FolderHeaderView::GetPreferredSize() const {
    153   return gfx::Size(kPreferredWidth, kPreferredHeight);
    154 }
    155 
    156 void FolderHeaderView::Layout() {
    157   gfx::Rect rect(GetContentsBounds());
    158   if (rect.IsEmpty())
    159     return;
    160 
    161   gfx::Rect back_bounds(rect);
    162   back_bounds.set_width(kIconDimension + 2 * kBackButtonPadding);
    163   if (app_list::switches::IsExperimentalAppListEnabled()) {
    164     // Align the left edge of the button image with the left margin of the
    165     // launcher window. Note that this means the physical button dimensions
    166     // extends slightly into the margin.
    167     back_bounds.set_x(kExperimentalWindowPadding - kBackButtonPadding);
    168   }
    169   back_button_->SetBoundsRect(back_bounds);
    170 
    171   gfx::Rect text_bounds(rect);
    172   base::string16 text = folder_item_ && !folder_item_->name().empty()
    173                             ? base::UTF8ToUTF16(folder_item_->name())
    174                             : folder_name_placeholder_text_;
    175   int text_width =
    176       gfx::Canvas::GetStringWidth(text, folder_name_view_->GetFontList()) +
    177       folder_name_view_->GetCaretBounds().width() +
    178       folder_name_view_->GetInsets().width();
    179   text_width = std::min(text_width, kMaxFolderNameWidth);
    180   text_bounds.set_x(back_bounds.x() + (rect.width() - text_width) / 2);
    181   text_bounds.set_width(text_width);
    182   text_bounds.ClampToCenteredSize(gfx::Size(text_bounds.width(),
    183       folder_name_view_->GetPreferredSize().height()));
    184   folder_name_view_->SetBoundsRect(text_bounds);
    185 }
    186 
    187 bool FolderHeaderView::OnKeyPressed(const ui::KeyEvent& event) {
    188   if (event.key_code() == ui::VKEY_RETURN)
    189     delegate_->GiveBackFocusToSearchBox();
    190 
    191   return false;
    192 }
    193 
    194 void FolderHeaderView::OnPaint(gfx::Canvas* canvas) {
    195   views::View::OnPaint(canvas);
    196 
    197   gfx::Rect rect(GetContentsBounds());
    198   if (rect.IsEmpty() || !folder_name_visible_)
    199     return;
    200 
    201   // Draw bottom separator line.
    202   int horizontal_padding = app_list::switches::IsExperimentalAppListEnabled()
    203                                ? kExperimentalWindowPadding
    204                                : kBottomSeparatorPadding;
    205   rect.Inset(horizontal_padding, 0);
    206   rect.set_y(rect.bottom() - kBottomSeparatorHeight);
    207   rect.set_height(kBottomSeparatorHeight);
    208   canvas->FillRect(rect, kTopSeparatorColor);
    209 }
    210 
    211 void FolderHeaderView::ContentsChanged(views::Textfield* sender,
    212                                        const base::string16& new_contents) {
    213   // Temporarily remove from observer to ignore data change caused by us.
    214   if (!folder_item_)
    215     return;
    216 
    217   folder_item_->RemoveObserver(this);
    218   // Enforce the maximum folder name length in UI.
    219   std::string name = base::UTF16ToUTF8(
    220       folder_name_view_->text().substr(0, kMaxFolderNameChars));
    221   if (name != folder_item_->name())
    222     delegate_->SetItemName(folder_item_, name);
    223   folder_item_->AddObserver(this);
    224 
    225   UpdateFolderNameAccessibleName();
    226 
    227   Layout();
    228 }
    229 
    230 void FolderHeaderView::ButtonPressed(views::Button* sender,
    231                                      const ui::Event& event) {
    232   delegate_->NavigateBack(folder_item_, event);
    233 }
    234 
    235 void FolderHeaderView::ItemNameChanged() {
    236   Update();
    237 }
    238 
    239 }  // namespace app_list
    240