Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ui / app_list / views / search_box_view.cc
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 "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"
26
27 namespace app_list {
28
29 namespace {
30
31 const int kPadding = 14;
32 const int kPreferredWidth = 360;
33 const int kPreferredHeight = 48;
34
35 const SkColor kHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
36
37 // Menu offset relative to the bottom-right corner of the menu button.
38 const int kMenuYOffsetFromButton = -4;
39 const int kMenuXOffsetFromButton = -7;
40
41 const int kBackgroundBorderWidth = 1;
42 const int kBackgroundBorderBottomWidth = 1;
43 const int kBackgroundBorderCornerRadius = 2;
44 const SkColor kBackgroundBorderColor = SkColorSetRGB(0xEE, 0xEE, 0xEE);
45 const SkColor kBackgroundBorderBottomColor = SkColorSetRGB(0xCC, 0xCC, 0xCC);
46
47 // A background that paints a solid white rounded rect with a thin grey border.
48 class SearchBoxBackground : public views::Background {
49  public:
50   SearchBoxBackground() {}
51   virtual ~SearchBoxBackground() {}
52
53  private:
54   // views::Background overrides:
55   virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
56     gfx::Rect bounds = view->GetContentsBounds();
57
58     SkPaint paint;
59     paint.setFlags(SkPaint::kAntiAlias_Flag);
60     paint.setColor(kBackgroundBorderColor);
61     canvas->DrawRoundRect(bounds, kBackgroundBorderCornerRadius, paint);
62     bounds.Inset(kBackgroundBorderWidth,
63                  kBackgroundBorderWidth,
64                  kBackgroundBorderWidth,
65                  0);
66     paint.setColor(kBackgroundBorderBottomColor);
67     canvas->DrawRoundRect(bounds, kBackgroundBorderCornerRadius, paint);
68     bounds.Inset(0, 0, 0, kBackgroundBorderBottomWidth);
69     paint.setColor(SK_ColorWHITE);
70     canvas->DrawRoundRect(bounds, kBackgroundBorderCornerRadius, paint);
71   }
72
73   DISALLOW_COPY_AND_ASSIGN(SearchBoxBackground);
74 };
75
76 }  // namespace
77
78 SearchBoxView::SearchBoxView(SearchBoxViewDelegate* delegate,
79                              AppListViewDelegate* view_delegate)
80     : delegate_(delegate),
81       view_delegate_(view_delegate),
82       model_(NULL),
83       icon_view_(NULL),
84       speech_button_(NULL),
85       search_box_(new views::Textfield),
86       contents_view_(NULL) {
87   if (switches::IsExperimentalAppListEnabled()) {
88     set_background(new SearchBoxBackground());
89   } else {
90     icon_view_ = new views::ImageView;
91     AddChildView(icon_view_);
92   }
93
94   views::BoxLayout* layout =
95       new views::BoxLayout(views::BoxLayout::kHorizontal,
96                            kPadding,
97                            0,
98                            kPadding - views::Textfield::kTextPadding);
99   SetLayoutManager(layout);
100   layout->set_cross_axis_alignment(
101       views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
102   layout->set_minimum_cross_axis_size(kPreferredHeight);
103
104   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
105
106   search_box_->SetBorder(views::Border::NullBorder());
107   search_box_->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
108   search_box_->set_placeholder_text_color(kHintTextColor);
109   search_box_->set_controller(this);
110   AddChildView(search_box_);
111   layout->SetFlexForView(search_box_, 1);
112
113 #if !defined(OS_CHROMEOS)
114   menu_button_ = new views::MenuButton(NULL, base::string16(), this, false);
115   menu_button_->SetBorder(views::Border::NullBorder());
116   menu_button_->SetImage(views::Button::STATE_NORMAL,
117                          *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_NORMAL));
118   menu_button_->SetImage(views::Button::STATE_HOVERED,
119                          *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_HOVER));
120   menu_button_->SetImage(views::Button::STATE_PRESSED,
121                          *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_PRESSED));
122   AddChildView(menu_button_);
123 #endif
124
125   view_delegate_->GetSpeechUI()->AddObserver(this);
126   ModelChanged();
127 }
128
129 SearchBoxView::~SearchBoxView() {
130   view_delegate_->GetSpeechUI()->RemoveObserver(this);
131   model_->search_box()->RemoveObserver(this);
132 }
133
134 void SearchBoxView::ModelChanged() {
135   if (model_)
136     model_->search_box()->RemoveObserver(this);
137
138   model_ = view_delegate_->GetModel();
139   DCHECK(model_);
140   model_->search_box()->AddObserver(this);
141   IconChanged();
142   SpeechRecognitionButtonPropChanged();
143   HintTextChanged();
144 }
145
146 bool SearchBoxView::HasSearch() const {
147   return !search_box_->text().empty();
148 }
149
150 void SearchBoxView::ClearSearch() {
151   search_box_->SetText(base::string16());
152   view_delegate_->AutoLaunchCanceled();
153   // Updates model and fires query changed manually because SetText() above
154   // does not generate ContentsChanged() notification.
155   UpdateModel();
156   NotifyQueryChanged();
157 }
158
159 void SearchBoxView::InvalidateMenu() {
160   menu_.reset();
161 }
162
163 gfx::Size SearchBoxView::GetPreferredSize() const {
164   return gfx::Size(kPreferredWidth, kPreferredHeight);
165 }
166
167 bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent& event) {
168   if (contents_view_)
169     return contents_view_->OnMouseWheel(event);
170
171   return false;
172 }
173
174 void SearchBoxView::UpdateModel() {
175   // Temporarily remove from observer to ignore notifications caused by us.
176   model_->search_box()->RemoveObserver(this);
177   model_->search_box()->SetText(search_box_->text());
178   model_->search_box()->SetSelectionModel(search_box_->GetSelectionModel());
179   model_->search_box()->AddObserver(this);
180 }
181
182 void SearchBoxView::NotifyQueryChanged() {
183   DCHECK(delegate_);
184   delegate_->QueryChanged(this);
185 }
186
187 void SearchBoxView::ContentsChanged(views::Textfield* sender,
188                                     const base::string16& new_contents) {
189   UpdateModel();
190   view_delegate_->AutoLaunchCanceled();
191   NotifyQueryChanged();
192 }
193
194 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
195                                    const ui::KeyEvent& key_event) {
196   bool handled = false;
197   if (contents_view_ && contents_view_->visible())
198     handled = contents_view_->OnKeyPressed(key_event);
199
200   return handled;
201 }
202
203 void SearchBoxView::ButtonPressed(views::Button* sender,
204                                   const ui::Event& event) {
205   DCHECK(speech_button_ && sender == speech_button_);
206   view_delegate_->ToggleSpeechRecognition();
207 }
208
209 void SearchBoxView::OnMenuButtonClicked(View* source, const gfx::Point& point) {
210   if (!menu_)
211     menu_.reset(new AppListMenuViews(view_delegate_));
212
213   const gfx::Point menu_location =
214       menu_button_->GetBoundsInScreen().bottom_right() +
215       gfx::Vector2d(kMenuXOffsetFromButton, kMenuYOffsetFromButton);
216   menu_->RunMenuAt(menu_button_, menu_location);
217 }
218
219 void SearchBoxView::IconChanged() {
220   if (icon_view_)
221     icon_view_->SetImage(model_->search_box()->icon());
222 }
223
224 void SearchBoxView::SpeechRecognitionButtonPropChanged() {
225   const SearchBoxModel::SpeechButtonProperty* speech_button_prop =
226       model_->search_box()->speech_button();
227   if (speech_button_prop) {
228     if (!speech_button_) {
229       speech_button_ = new views::ImageButton(this);
230       AddChildView(speech_button_);
231     }
232
233     if (view_delegate_->GetSpeechUI()->state() ==
234         SPEECH_RECOGNITION_HOTWORD_LISTENING) {
235       speech_button_->SetImage(
236           views::Button::STATE_NORMAL, &speech_button_prop->on_icon);
237       speech_button_->SetTooltipText(speech_button_prop->on_tooltip);
238     } else {
239       speech_button_->SetImage(
240           views::Button::STATE_NORMAL, &speech_button_prop->off_icon);
241       speech_button_->SetTooltipText(speech_button_prop->off_tooltip);
242     }
243   } else {
244     if (speech_button_) {
245       // Deleting a view will detach it from its parent.
246       delete speech_button_;
247       speech_button_ = NULL;
248     }
249   }
250 }
251
252 void SearchBoxView::HintTextChanged() {
253   search_box_->set_placeholder_text(model_->search_box()->hint_text());
254 }
255
256 void SearchBoxView::SelectionModelChanged() {
257   search_box_->SelectSelectionModel(model_->search_box()->selection_model());
258 }
259
260 void SearchBoxView::TextChanged() {
261   search_box_->SetText(model_->search_box()->text());
262   NotifyQueryChanged();
263 }
264
265 void SearchBoxView::OnSpeechRecognitionStateChanged(
266     SpeechRecognitionState new_state) {
267   SpeechRecognitionButtonPropChanged();
268   SchedulePaint();
269 }
270
271 }  // namespace app_list