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