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
23namespace app_list {
24
25namespace {
26
27const int kPreferredWidth = 360;
28const int kPreferredHeight = 48;
29const int kIconDimension = 24;
30const int kBackButtonPadding = 14;
31const int kBottomSeparatorPadding = 9;  // Non-experimental app list only.
32const int kBottomSeparatorHeight = 1;
33const int kMaxFolderNameWidth = 300;
34
35const SkColor kHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
36
37}  // namespace
38
39class 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
56FolderHeaderView::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
86FolderHeaderView::~FolderHeaderView() {
87  if (folder_item_)
88    folder_item_->RemoveObserver(this);
89}
90
91void 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
106void FolderHeaderView::UpdateFolderNameVisibility(bool visible) {
107  folder_name_visible_ = visible;
108  Update();
109  SchedulePaint();
110}
111
112void FolderHeaderView::OnFolderItemRemoved() {
113  folder_item_ = NULL;
114}
115
116void 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
129void 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
140const base::string16& FolderHeaderView::GetFolderNameForTest() {
141  return folder_name_view_->text();
142}
143
144void FolderHeaderView::SetFolderNameForTest(const base::string16& name) {
145  folder_name_view_->SetText(name);
146}
147
148bool FolderHeaderView::IsFolderNameEnabledForTest() const {
149  return folder_name_view_->enabled();
150}
151
152gfx::Size FolderHeaderView::GetPreferredSize() const {
153  return gfx::Size(kPreferredWidth, kPreferredHeight);
154}
155
156void 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
187bool FolderHeaderView::OnKeyPressed(const ui::KeyEvent& event) {
188  if (event.key_code() == ui::VKEY_RETURN)
189    delegate_->GiveBackFocusToSearchBox();
190
191  return false;
192}
193
194void 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
211void 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
230void FolderHeaderView::ButtonPressed(views::Button* sender,
231                                     const ui::Event& event) {
232  delegate_->NavigateBack(folder_item_, event);
233}
234
235void FolderHeaderView::ItemNameChanged() {
236  Update();
237}
238
239}  // namespace app_list
240