1 // Copyright (c) 2012 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/search_box_view.h" 6 7 #include <algorithm> 8 9 #include "grit/ui_resources.h" 10 #include "ui/app_list/app_list_model.h" 11 #include "ui/app_list/search_box_model.h" 12 #include "ui/app_list/search_box_view_delegate.h" 13 #include "ui/app_list/views/app_list_menu_views.h" 14 #include "ui/base/events/event.h" 15 #include "ui/base/resource/resource_bundle.h" 16 #include "ui/views/controls/button/menu_button.h" 17 #include "ui/views/controls/image_view.h" 18 #include "ui/views/controls/textfield/textfield.h" 19 20 namespace app_list { 21 22 namespace { 23 24 const int kPadding = 14; 25 const int kIconDimension = 32; 26 const int kPreferredWidth = 360; 27 const int kPreferredHeight = 48; 28 const int kMenuButtonDimension = 29; 29 30 const SkColor kHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0); 31 32 // Menu offset relative to the bottom-right corner of the menu button. 33 const int kMenuYOffsetFromButton = -4; 34 const int kMenuXOffsetFromButton = -7; 35 36 } // namespace 37 38 SearchBoxView::SearchBoxView(SearchBoxViewDelegate* delegate, 39 AppListViewDelegate* view_delegate, 40 AppListModel* model) 41 : delegate_(delegate), 42 view_delegate_(view_delegate), 43 model_(model->search_box()), 44 icon_view_(new views::ImageView), 45 search_box_(new views::Textfield), 46 contents_view_(NULL) { 47 DCHECK(model_); 48 AddChildView(icon_view_); 49 50 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 51 52 #if !defined(OS_CHROMEOS) 53 menu_button_ = new views::MenuButton(NULL, base::string16(), this, false); 54 menu_button_->set_border(NULL); 55 menu_button_->SetIcon(*rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_NORMAL)); 56 menu_button_->SetHoverIcon(*rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_HOVER)); 57 menu_button_->SetPushedIcon(*rb.GetImageSkiaNamed( 58 IDR_APP_LIST_TOOLS_PRESSED)); 59 AddChildView(menu_button_); 60 #endif 61 62 search_box_->RemoveBorder(); 63 search_box_->SetFont(rb.GetFont(ui::ResourceBundle::MediumFont)); 64 search_box_->set_placeholder_text_color(kHintTextColor); 65 search_box_->SetController(this); 66 AddChildView(search_box_); 67 68 model_->AddObserver(this); 69 IconChanged(); 70 HintTextChanged(); 71 } 72 73 SearchBoxView::~SearchBoxView() { 74 model_->RemoveObserver(this); 75 } 76 77 bool SearchBoxView::HasSearch() const { 78 return !search_box_->text().empty(); 79 } 80 81 void SearchBoxView::ClearSearch() { 82 search_box_->SetText(base::string16()); 83 // Updates model and fires query changed manually because SetText() above 84 // does not generate ContentsChanged() notification. 85 UpdateModel(); 86 NotifyQueryChanged(); 87 } 88 89 void SearchBoxView::InvalidateMenu() { 90 menu_.reset(); 91 } 92 93 gfx::Size SearchBoxView::GetPreferredSize() { 94 return gfx::Size(kPreferredWidth, kPreferredHeight); 95 } 96 97 void SearchBoxView::Layout() { 98 gfx::Rect rect(GetContentsBounds()); 99 if (rect.IsEmpty()) 100 return; 101 102 gfx::Rect icon_frame(rect); 103 icon_frame.set_width(kIconDimension + 2 * kPadding); 104 icon_view_->SetBoundsRect(icon_frame); 105 106 gfx::Rect menu_button_frame(rect); 107 #if !defined(OS_CHROMEOS) 108 menu_button_frame.set_width(kMenuButtonDimension); 109 menu_button_frame.set_x(rect.right() - menu_button_frame.width() - kPadding); 110 menu_button_frame.ClampToCenteredSize(gfx::Size(menu_button_frame.width(), 111 kMenuButtonDimension)); 112 menu_button_->SetBoundsRect(menu_button_frame); 113 #else 114 menu_button_frame.set_width(0); 115 #endif 116 117 gfx::Rect edit_frame(rect); 118 edit_frame.set_x(icon_frame.right()); 119 edit_frame.set_width( 120 rect.width() - icon_frame.width() - kPadding - menu_button_frame.width()); 121 edit_frame.ClampToCenteredSize( 122 gfx::Size(edit_frame.width(), search_box_->GetPreferredSize().height())); 123 search_box_->SetBoundsRect(edit_frame); 124 } 125 126 bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent& event) { 127 if (contents_view_) 128 return contents_view_->OnMouseWheel(event); 129 130 return false; 131 } 132 133 void SearchBoxView::UpdateModel() { 134 // Temporarily remove from observer to ignore notifications caused by us. 135 model_->RemoveObserver(this); 136 model_->SetText(search_box_->text()); 137 model_->SetSelectionModel(search_box_->GetSelectionModel()); 138 model_->AddObserver(this); 139 } 140 141 void SearchBoxView::NotifyQueryChanged() { 142 DCHECK(delegate_); 143 delegate_->QueryChanged(this); 144 } 145 146 void SearchBoxView::ContentsChanged(views::Textfield* sender, 147 const base::string16& new_contents) { 148 UpdateModel(); 149 NotifyQueryChanged(); 150 } 151 152 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender, 153 const ui::KeyEvent& key_event) { 154 bool handled = false; 155 if (contents_view_ && contents_view_->visible()) 156 handled = contents_view_->OnKeyPressed(key_event); 157 158 return handled; 159 } 160 161 void SearchBoxView::OnMenuButtonClicked(View* source, const gfx::Point& point) { 162 if (!menu_) 163 menu_.reset(new AppListMenuViews(view_delegate_)); 164 165 const gfx::Point menu_location = 166 menu_button_->GetBoundsInScreen().bottom_right() + 167 gfx::Vector2d(kMenuXOffsetFromButton, kMenuYOffsetFromButton); 168 menu_->RunMenuAt(menu_button_, menu_location); 169 } 170 171 void SearchBoxView::IconChanged() { 172 icon_view_->SetImage(model_->icon()); 173 } 174 175 void SearchBoxView::HintTextChanged() { 176 search_box_->set_placeholder_text(model_->hint_text()); 177 } 178 179 void SearchBoxView::SelectionModelChanged() { 180 search_box_->SelectSelectionModel(model_->selection_model()); 181 } 182 183 void SearchBoxView::TextChanged() { 184 search_box_->SetText(model_->text()); 185 } 186 187 } // namespace app_list 188