Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / app_list / views / search_box_view.cc
blob7bb48a68e68a1b6bd6f2dec6bc616537c9eda2bf
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.
5 #include "ui/app_list/views/search_box_view.h"
7 #include <algorithm>
9 #include "ui/app_list/app_list_model.h"
10 #include "ui/app_list/app_list_switches.h"
11 #include "ui/app_list/app_list_view_delegate.h"
12 #include "ui/app_list/search_box_model.h"
13 #include "ui/app_list/speech_ui_model.h"
14 #include "ui/app_list/views/app_list_menu_views.h"
15 #include "ui/app_list/views/search_box_view_delegate.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/events/event.h"
18 #include "ui/gfx/canvas.h"
19 #include "ui/resources/grit/ui_resources.h"
20 #include "ui/views/border.h"
21 #include "ui/views/controls/button/image_button.h"
22 #include "ui/views/controls/button/menu_button.h"
23 #include "ui/views/controls/image_view.h"
24 #include "ui/views/controls/textfield/textfield.h"
25 #include "ui/views/layout/box_layout.h"
27 namespace app_list {
29 namespace {
31 const int kPadding = 14;
32 const int kPreferredWidth = 360;
33 const int kPreferredHeight = 48;
35 const SkColor kHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
37 // Menu offset relative to the bottom-right corner of the menu button.
38 const int kMenuYOffsetFromButton = -4;
39 const int kMenuXOffsetFromButton = -7;
41 // Experimental app list constants.
42 const int kExperimentalSearchBoxHeight = 37;
44 const int kBackgroundBorderWidth = 1;
45 const int kBackgroundBorderBottomWidth = 1;
46 const int kBackgroundBorderCornerRadius = 2;
47 const SkColor kBackgroundBorderColor = SkColorSetRGB(0xEE, 0xEE, 0xEE);
48 const SkColor kBackgroundBorderBottomColor = SkColorSetRGB(0xCC, 0xCC, 0xCC);
50 // A background that paints a solid white rounded rect with a thin grey border.
51 class SearchBoxBackground : public views::Background {
52 public:
53 SearchBoxBackground() {}
54 virtual ~SearchBoxBackground() {}
56 private:
57 // views::Background overrides:
58 virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
59 gfx::Rect bounds = view->GetContentsBounds();
61 SkPaint paint;
62 paint.setFlags(SkPaint::kAntiAlias_Flag);
63 paint.setColor(kBackgroundBorderColor);
64 canvas->DrawRoundRect(bounds, kBackgroundBorderCornerRadius, paint);
65 bounds.Inset(kBackgroundBorderWidth,
66 kBackgroundBorderWidth,
67 kBackgroundBorderWidth,
68 0);
69 paint.setColor(kBackgroundBorderBottomColor);
70 canvas->DrawRoundRect(bounds, kBackgroundBorderCornerRadius, paint);
71 bounds.Inset(0, 0, 0, kBackgroundBorderBottomWidth);
72 paint.setColor(SK_ColorWHITE);
73 canvas->DrawRoundRect(bounds, kBackgroundBorderCornerRadius, paint);
76 DISALLOW_COPY_AND_ASSIGN(SearchBoxBackground);
79 } // namespace
81 SearchBoxView::SearchBoxView(SearchBoxViewDelegate* delegate,
82 AppListViewDelegate* view_delegate)
83 : delegate_(delegate),
84 view_delegate_(view_delegate),
85 model_(NULL),
86 icon_view_(new views::ImageView),
87 speech_button_(NULL),
88 search_box_(new views::Textfield),
89 contents_view_(NULL) {
90 AddChildView(icon_view_);
91 if (switches::IsExperimentalAppListEnabled())
92 set_background(new SearchBoxBackground());
94 views::BoxLayout* layout = new views::BoxLayout(
95 views::BoxLayout::kHorizontal, kPadding, 0, kPadding);
96 SetLayoutManager(layout);
97 layout->set_cross_axis_alignment(
98 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
99 layout->set_minimum_cross_axis_size(switches::IsExperimentalAppListEnabled()
100 ? kExperimentalSearchBoxHeight
101 : kPreferredHeight);
103 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
105 search_box_->SetBorder(views::Border::NullBorder());
106 search_box_->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
107 search_box_->set_placeholder_text_color(kHintTextColor);
108 search_box_->set_controller(this);
109 AddChildView(search_box_);
110 layout->SetFlexForView(search_box_, 1);
112 #if !defined(OS_CHROMEOS)
113 menu_button_ = new views::MenuButton(NULL, base::string16(), this, false);
114 menu_button_->SetBorder(views::Border::NullBorder());
115 menu_button_->SetImage(views::Button::STATE_NORMAL,
116 *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_NORMAL));
117 menu_button_->SetImage(views::Button::STATE_HOVERED,
118 *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_HOVER));
119 menu_button_->SetImage(views::Button::STATE_PRESSED,
120 *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_PRESSED));
121 AddChildView(menu_button_);
122 #endif
124 view_delegate_->GetSpeechUI()->AddObserver(this);
125 ModelChanged();
128 SearchBoxView::~SearchBoxView() {
129 view_delegate_->GetSpeechUI()->RemoveObserver(this);
130 model_->search_box()->RemoveObserver(this);
133 void SearchBoxView::ModelChanged() {
134 if (model_)
135 model_->search_box()->RemoveObserver(this);
137 model_ = view_delegate_->GetModel();
138 DCHECK(model_);
139 model_->search_box()->AddObserver(this);
140 IconChanged();
141 SpeechRecognitionButtonPropChanged();
142 HintTextChanged();
145 bool SearchBoxView::HasSearch() const {
146 return !search_box_->text().empty();
149 void SearchBoxView::ClearSearch() {
150 search_box_->SetText(base::string16());
151 view_delegate_->AutoLaunchCanceled();
152 // Updates model and fires query changed manually because SetText() above
153 // does not generate ContentsChanged() notification.
154 UpdateModel();
155 NotifyQueryChanged();
158 void SearchBoxView::InvalidateMenu() {
159 menu_.reset();
162 gfx::Size SearchBoxView::GetPreferredSize() const {
163 return gfx::Size(kPreferredWidth,
164 switches::IsExperimentalAppListEnabled()
165 ? kExperimentalSearchBoxHeight
166 : kPreferredHeight);
169 bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent& event) {
170 if (contents_view_)
171 return contents_view_->OnMouseWheel(event);
173 return false;
176 void SearchBoxView::UpdateModel() {
177 // Temporarily remove from observer to ignore notifications caused by us.
178 model_->search_box()->RemoveObserver(this);
179 model_->search_box()->SetText(search_box_->text());
180 model_->search_box()->SetSelectionModel(search_box_->GetSelectionModel());
181 model_->search_box()->AddObserver(this);
184 void SearchBoxView::NotifyQueryChanged() {
185 DCHECK(delegate_);
186 delegate_->QueryChanged(this);
189 void SearchBoxView::ContentsChanged(views::Textfield* sender,
190 const base::string16& new_contents) {
191 UpdateModel();
192 view_delegate_->AutoLaunchCanceled();
193 NotifyQueryChanged();
196 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
197 const ui::KeyEvent& key_event) {
198 bool handled = false;
199 if (contents_view_ && contents_view_->visible())
200 handled = contents_view_->OnKeyPressed(key_event);
202 return handled;
205 void SearchBoxView::ButtonPressed(views::Button* sender,
206 const ui::Event& event) {
207 DCHECK(speech_button_ && sender == speech_button_);
208 view_delegate_->ToggleSpeechRecognition();
211 void SearchBoxView::OnMenuButtonClicked(View* source, const gfx::Point& point) {
212 if (!menu_)
213 menu_.reset(new AppListMenuViews(view_delegate_));
215 const gfx::Point menu_location =
216 menu_button_->GetBoundsInScreen().bottom_right() +
217 gfx::Vector2d(kMenuXOffsetFromButton, kMenuYOffsetFromButton);
218 menu_->RunMenuAt(menu_button_, menu_location);
221 void SearchBoxView::IconChanged() {
222 icon_view_->SetImage(model_->search_box()->icon());
225 void SearchBoxView::SpeechRecognitionButtonPropChanged() {
226 const SearchBoxModel::SpeechButtonProperty* speech_button_prop =
227 model_->search_box()->speech_button();
228 if (speech_button_prop) {
229 if (!speech_button_) {
230 speech_button_ = new views::ImageButton(this);
231 AddChildView(speech_button_);
234 if (view_delegate_->GetSpeechUI()->state() ==
235 SPEECH_RECOGNITION_HOTWORD_LISTENING) {
236 speech_button_->SetImage(
237 views::Button::STATE_NORMAL, &speech_button_prop->on_icon);
238 speech_button_->SetTooltipText(speech_button_prop->on_tooltip);
239 } else {
240 speech_button_->SetImage(
241 views::Button::STATE_NORMAL, &speech_button_prop->off_icon);
242 speech_button_->SetTooltipText(speech_button_prop->off_tooltip);
244 } else {
245 if (speech_button_) {
246 // Deleting a view will detach it from its parent.
247 delete speech_button_;
248 speech_button_ = NULL;
253 void SearchBoxView::HintTextChanged() {
254 search_box_->set_placeholder_text(model_->search_box()->hint_text());
257 void SearchBoxView::SelectionModelChanged() {
258 search_box_->SelectSelectionModel(model_->search_box()->selection_model());
261 void SearchBoxView::TextChanged() {
262 search_box_->SetText(model_->search_box()->text());
263 NotifyQueryChanged();
266 void SearchBoxView::OnSpeechRecognitionStateChanged(
267 SpeechRecognitionState new_state) {
268 SpeechRecognitionButtonPropChanged();
269 SchedulePaint();
272 } // namespace app_list