Upstream version 8.37.180.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 "grit/ui_resources.h"
10 #include "ui/app_list/app_list_model.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/l10n/l10n_util.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/events/event.h"
19 #include "ui/views/border.h"
20 #include "ui/views/controls/button/image_button.h"
21 #include "ui/views/controls/button/menu_button.h"
22 #include "ui/views/controls/image_view.h"
23 #include "ui/views/controls/textfield/textfield.h"
24
25 namespace app_list {
26
27 namespace {
28
29 const int kPadding = 14;
30 const int kIconDimension = 32;
31 const int kPreferredWidth = 360;
32 const int kPreferredHeight = 48;
33 #if !defined(OS_CHROMEOS)
34 const int kMenuButtonDimension = 29;
35 #endif
36
37 const SkColor kHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
38
39 // Menu offset relative to the bottom-right corner of the menu button.
40 const int kMenuYOffsetFromButton = -4;
41 const int kMenuXOffsetFromButton = -7;
42
43 }  // namespace
44
45 SearchBoxView::SearchBoxView(SearchBoxViewDelegate* delegate,
46                              AppListViewDelegate* view_delegate)
47     : delegate_(delegate),
48       view_delegate_(view_delegate),
49       model_(NULL),
50       icon_view_(new views::ImageView),
51       speech_button_(NULL),
52       search_box_(new views::Textfield),
53       contents_view_(NULL) {
54   AddChildView(icon_view_);
55
56   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
57
58 #if !defined(OS_CHROMEOS)
59   menu_button_ = new views::MenuButton(NULL, base::string16(), this, false);
60   menu_button_->SetBorder(views::Border::NullBorder());
61   menu_button_->SetImage(views::Button::STATE_NORMAL,
62                          *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_NORMAL));
63   menu_button_->SetImage(views::Button::STATE_HOVERED,
64                          *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_HOVER));
65   menu_button_->SetImage(views::Button::STATE_PRESSED,
66                          *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_PRESSED));
67   AddChildView(menu_button_);
68 #endif
69
70   search_box_->SetBorder(views::Border::NullBorder());
71   search_box_->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
72   search_box_->set_placeholder_text_color(kHintTextColor);
73   search_box_->set_controller(this);
74   AddChildView(search_box_);
75
76   view_delegate_->GetSpeechUI()->AddObserver(this);
77   ModelChanged();
78 }
79
80 SearchBoxView::~SearchBoxView() {
81   view_delegate_->GetSpeechUI()->RemoveObserver(this);
82   model_->search_box()->RemoveObserver(this);
83 }
84
85 void SearchBoxView::ModelChanged() {
86   if (model_)
87     model_->search_box()->RemoveObserver(this);
88
89   model_ = view_delegate_->GetModel();
90   DCHECK(model_);
91   model_->search_box()->AddObserver(this);
92   IconChanged();
93   SpeechRecognitionButtonPropChanged();
94   HintTextChanged();
95 }
96
97 bool SearchBoxView::HasSearch() const {
98   return !search_box_->text().empty();
99 }
100
101 void SearchBoxView::ClearSearch() {
102   search_box_->SetText(base::string16());
103   view_delegate_->AutoLaunchCanceled();
104   // Updates model and fires query changed manually because SetText() above
105   // does not generate ContentsChanged() notification.
106   UpdateModel();
107   NotifyQueryChanged();
108 }
109
110 void SearchBoxView::InvalidateMenu() {
111   menu_.reset();
112 }
113
114 gfx::Size SearchBoxView::GetPreferredSize() const {
115   return gfx::Size(kPreferredWidth, kPreferredHeight);
116 }
117
118 void SearchBoxView::Layout() {
119   gfx::Rect rect(GetContentsBounds());
120   if (rect.IsEmpty())
121     return;
122
123   gfx::Rect icon_frame(rect);
124   icon_frame.set_width(kIconDimension + 2 * kPadding);
125   icon_view_->SetBoundsRect(icon_frame);
126
127   // Places |speech_button_| if exists. |speech_button_frame| holds its bounds
128   // to calculate the search box bounds.
129   gfx::Rect speech_button_frame;
130   if (speech_button_) {
131     speech_button_frame = icon_frame;
132     speech_button_frame.set_x(rect.right() - icon_frame.width());
133     gfx::Size button_size = speech_button_->GetPreferredSize();
134     gfx::Point button_origin = speech_button_frame.CenterPoint();
135     button_origin.Offset(-button_size.width() / 2, -button_size.height() / 2);
136     speech_button_->SetBoundsRect(gfx::Rect(button_origin, button_size));
137   }
138
139   gfx::Rect menu_button_frame(rect);
140 #if !defined(OS_CHROMEOS)
141   menu_button_frame.set_width(kMenuButtonDimension);
142   menu_button_frame.set_x(rect.right() - menu_button_frame.width() - kPadding);
143   menu_button_frame.ClampToCenteredSize(gfx::Size(menu_button_frame.width(),
144                                                   kMenuButtonDimension));
145   menu_button_->SetBoundsRect(menu_button_frame);
146 #else
147   menu_button_frame.set_width(0);
148 #endif
149
150   gfx::Rect edit_frame(rect);
151   edit_frame.set_x(icon_frame.right());
152   int edit_frame_width =
153       rect.width() - icon_frame.width() - kPadding - menu_button_frame.width();
154   if (!speech_button_frame.IsEmpty())
155     edit_frame_width -= speech_button_frame.width() + kPadding;
156   edit_frame.set_width(edit_frame_width);
157   edit_frame.ClampToCenteredSize(
158       gfx::Size(edit_frame.width(), search_box_->GetPreferredSize().height()));
159   search_box_->SetBoundsRect(edit_frame);
160 }
161
162 bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent& event) {
163   if (contents_view_)
164     return contents_view_->OnMouseWheel(event);
165
166   return false;
167 }
168
169 void SearchBoxView::UpdateModel() {
170   // Temporarily remove from observer to ignore notifications caused by us.
171   model_->search_box()->RemoveObserver(this);
172   model_->search_box()->SetText(search_box_->text());
173   model_->search_box()->SetSelectionModel(search_box_->GetSelectionModel());
174   model_->search_box()->AddObserver(this);
175 }
176
177 void SearchBoxView::NotifyQueryChanged() {
178   DCHECK(delegate_);
179   delegate_->QueryChanged(this);
180 }
181
182 void SearchBoxView::ContentsChanged(views::Textfield* sender,
183                                     const base::string16& new_contents) {
184   UpdateModel();
185   view_delegate_->AutoLaunchCanceled();
186   NotifyQueryChanged();
187 }
188
189 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
190                                    const ui::KeyEvent& key_event) {
191   bool handled = false;
192   if (contents_view_ && contents_view_->visible())
193     handled = contents_view_->OnKeyPressed(key_event);
194
195   return handled;
196 }
197
198 void SearchBoxView::ButtonPressed(views::Button* sender,
199                                   const ui::Event& event) {
200   DCHECK(speech_button_ && sender == speech_button_);
201   view_delegate_->ToggleSpeechRecognition();
202 }
203
204 void SearchBoxView::OnMenuButtonClicked(View* source, const gfx::Point& point) {
205   if (!menu_)
206     menu_.reset(new AppListMenuViews(view_delegate_));
207
208   const gfx::Point menu_location =
209       menu_button_->GetBoundsInScreen().bottom_right() +
210       gfx::Vector2d(kMenuXOffsetFromButton, kMenuYOffsetFromButton);
211   menu_->RunMenuAt(menu_button_, menu_location);
212 }
213
214 void SearchBoxView::IconChanged() {
215   icon_view_->SetImage(model_->search_box()->icon());
216 }
217
218 void SearchBoxView::SpeechRecognitionButtonPropChanged() {
219   const SearchBoxModel::SpeechButtonProperty* speech_button_prop =
220       model_->search_box()->speech_button();
221   if (speech_button_prop) {
222     if (!speech_button_) {
223       speech_button_ = new views::ImageButton(this);
224       AddChildView(speech_button_);
225     }
226
227     if (view_delegate_->GetSpeechUI()->state() ==
228         SPEECH_RECOGNITION_HOTWORD_LISTENING) {
229       speech_button_->SetImage(
230           views::Button::STATE_NORMAL, &speech_button_prop->on_icon);
231       speech_button_->SetTooltipText(speech_button_prop->on_tooltip);
232     } else {
233       speech_button_->SetImage(
234           views::Button::STATE_NORMAL, &speech_button_prop->off_icon);
235       speech_button_->SetTooltipText(speech_button_prop->off_tooltip);
236     }
237   } else {
238     if (speech_button_) {
239       // Deleting a view will detach it from its parent.
240       delete speech_button_;
241       speech_button_ = NULL;
242     }
243   }
244 }
245
246 void SearchBoxView::HintTextChanged() {
247   search_box_->set_placeholder_text(model_->search_box()->hint_text());
248 }
249
250 void SearchBoxView::SelectionModelChanged() {
251   search_box_->SelectSelectionModel(model_->search_box()->selection_model());
252 }
253
254 void SearchBoxView::TextChanged() {
255   search_box_->SetText(model_->search_box()->text());
256   NotifyQueryChanged();
257 }
258
259 void SearchBoxView::OnSpeechRecognitionStateChanged(
260     SpeechRecognitionState new_state) {
261   SpeechRecognitionButtonPropChanged();
262   SchedulePaint();
263 }
264
265 }  // namespace app_list