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