- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / controls / button / custom_button.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/views/controls/button/custom_button.h"
6
7 #include "ui/base/accessibility/accessible_view_state.h"
8 #include "ui/events/event.h"
9 #include "ui/events/keycodes/keyboard_codes.h"
10 #include "ui/gfx/animation/throb_animation.h"
11 #include "ui/gfx/screen.h"
12 #include "ui/views/widget/widget.h"
13
14 namespace views {
15
16 // How long the hover animation takes if uninterrupted.
17 static const int kHoverFadeDurationMs = 150;
18
19 // static
20 const char CustomButton::kViewClassName[] = "CustomButton";
21
22 ////////////////////////////////////////////////////////////////////////////////
23 // CustomButton, public:
24
25 CustomButton::~CustomButton() {
26 }
27
28 void CustomButton::SetState(ButtonState state) {
29   if (state == state_)
30     return;
31
32   if (animate_on_state_change_ &&
33       (!is_throbbing_ || !hover_animation_->is_animating())) {
34     is_throbbing_ = false;
35     if (state_ == STATE_NORMAL && state == STATE_HOVERED) {
36       // Button is hovered from a normal state, start hover animation.
37       hover_animation_->Show();
38     } else if ((state_ == STATE_HOVERED || state_ == STATE_PRESSED)
39           && state == STATE_NORMAL) {
40       // Button is returning to a normal state from hover, start hover
41       // fade animation.
42       hover_animation_->Hide();
43     } else {
44       hover_animation_->Stop();
45     }
46   }
47
48   state_ = state;
49   StateChanged();
50   if (state_changed_delegate_.get())
51     state_changed_delegate_->StateChanged(state_);
52   SchedulePaint();
53 }
54
55 void CustomButton::StartThrobbing(int cycles_til_stop) {
56   is_throbbing_ = true;
57   hover_animation_->StartThrobbing(cycles_til_stop);
58 }
59
60 void CustomButton::StopThrobbing() {
61   if (hover_animation_->is_animating()) {
62     hover_animation_->Stop();
63     SchedulePaint();
64   }
65 }
66
67 void CustomButton::SetAnimationDuration(int duration) {
68   hover_animation_->SetSlideDuration(duration);
69 }
70
71 void CustomButton::SetHotTracked(bool is_hot_tracked) {
72   if (state_ != STATE_DISABLED)
73     SetState(is_hot_tracked ? STATE_HOVERED : STATE_NORMAL);
74
75   if (is_hot_tracked)
76     NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, true);
77 }
78
79 bool CustomButton::IsHotTracked() const {
80   return state_ == STATE_HOVERED;
81 }
82
83 ////////////////////////////////////////////////////////////////////////////////
84 // CustomButton, View overrides:
85
86 void CustomButton::OnEnabledChanged() {
87   if (enabled() ? (state_ != STATE_DISABLED) : (state_ == STATE_DISABLED))
88     return;
89
90   if (enabled())
91     SetState(IsMouseHovered() ? STATE_HOVERED : STATE_NORMAL);
92   else
93     SetState(STATE_DISABLED);
94 }
95
96 const char* CustomButton::GetClassName() const {
97   return kViewClassName;
98 }
99
100 bool CustomButton::OnMousePressed(const ui::MouseEvent& event) {
101   if (state_ != STATE_DISABLED) {
102     if (ShouldEnterPushedState(event) && HitTestPoint(event.location()))
103       SetState(STATE_PRESSED);
104     if (request_focus_on_press_)
105       RequestFocus();
106   }
107   return true;
108 }
109
110 bool CustomButton::OnMouseDragged(const ui::MouseEvent& event) {
111   if (state_ != STATE_DISABLED) {
112     if (HitTestPoint(event.location()))
113       SetState(ShouldEnterPushedState(event) ? STATE_PRESSED : STATE_HOVERED);
114     else
115       SetState(STATE_NORMAL);
116   }
117   return true;
118 }
119
120 void CustomButton::OnMouseReleased(const ui::MouseEvent& event) {
121   if (state_ == STATE_DISABLED)
122     return;
123
124   if (!HitTestPoint(event.location())) {
125     SetState(STATE_NORMAL);
126     return;
127   }
128
129   SetState(STATE_HOVERED);
130   if (IsTriggerableEvent(event)) {
131     NotifyClick(event);
132     // NOTE: We may be deleted at this point (by the listener's notification
133     // handler).
134   }
135 }
136
137 void CustomButton::OnMouseCaptureLost() {
138   // Starting a drag results in a MouseCaptureLost, we need to ignore it.
139   if (state_ != STATE_DISABLED && !InDrag())
140     SetState(STATE_NORMAL);
141 }
142
143 void CustomButton::OnMouseEntered(const ui::MouseEvent& event) {
144   if (state_ != STATE_DISABLED)
145     SetState(STATE_HOVERED);
146 }
147
148 void CustomButton::OnMouseExited(const ui::MouseEvent& event) {
149   // Starting a drag results in a MouseExited, we need to ignore it.
150   if (state_ != STATE_DISABLED && !InDrag())
151     SetState(STATE_NORMAL);
152 }
153
154 void CustomButton::OnMouseMoved(const ui::MouseEvent& event) {
155   if (state_ != STATE_DISABLED)
156     SetState(HitTestPoint(event.location()) ? STATE_HOVERED : STATE_NORMAL);
157 }
158
159 bool CustomButton::OnKeyPressed(const ui::KeyEvent& event) {
160   if (state_ == STATE_DISABLED)
161     return false;
162
163   // Space sets button state to pushed. Enter clicks the button. This matches
164   // the Windows native behavior of buttons, where Space clicks the button on
165   // KeyRelease and Enter clicks the button on KeyPressed.
166   if (event.key_code() == ui::VKEY_SPACE) {
167     SetState(STATE_PRESSED);
168   } else if (event.key_code() == ui::VKEY_RETURN) {
169     SetState(STATE_NORMAL);
170     // TODO(beng): remove once NotifyClick takes ui::Event.
171     ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED,
172                                    gfx::Point(),
173                                    gfx::Point(),
174                                    ui::EF_LEFT_MOUSE_BUTTON);
175     NotifyClick(synthetic_event);
176   } else {
177     return false;
178   }
179   return true;
180 }
181
182 bool CustomButton::OnKeyReleased(const ui::KeyEvent& event) {
183   if ((state_ == STATE_DISABLED) || (event.key_code() != ui::VKEY_SPACE))
184     return false;
185
186   SetState(STATE_NORMAL);
187   // TODO(beng): remove once NotifyClick takes ui::Event.
188   ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED,
189                                  gfx::Point(),
190                                  gfx::Point(),
191                                  ui::EF_LEFT_MOUSE_BUTTON);
192   NotifyClick(synthetic_event);
193   return true;
194 }
195
196 void CustomButton::OnGestureEvent(ui::GestureEvent* event) {
197   if (state_ == STATE_DISABLED) {
198     Button::OnGestureEvent(event);
199     return;
200   }
201
202   if (event->type() == ui::ET_GESTURE_TAP && IsTriggerableEvent(*event)) {
203     // Set the button state to hot and start the animation fully faded in. The
204     // TAP_UP event issued immediately after will set the state to STATE_NORMAL
205     // beginning the fade out animation. See http://crbug.com/131184.
206     SetState(STATE_HOVERED);
207     hover_animation_->Reset(1.0);
208     NotifyClick(*event);
209     event->StopPropagation();
210   } else if (event->type() == ui::ET_GESTURE_TAP_DOWN &&
211              ShouldEnterPushedState(*event)) {
212     SetState(STATE_PRESSED);
213     if (request_focus_on_press_)
214       RequestFocus();
215     event->StopPropagation();
216   } else {
217     SetState(STATE_NORMAL);
218   }
219   if (!event->handled())
220     Button::OnGestureEvent(event);
221 }
222
223 bool CustomButton::AcceleratorPressed(const ui::Accelerator& accelerator) {
224   SetState(STATE_NORMAL);
225   /*
226   ui::KeyEvent key_event(ui::ET_KEY_RELEASED, accelerator.key_code(),
227                          accelerator.modifiers());
228                          */
229   // TODO(beng): remove once NotifyClick takes ui::Event.
230   ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED,
231                                  gfx::Point(),
232                                  gfx::Point(),
233                                  ui::EF_LEFT_MOUSE_BUTTON);
234   NotifyClick(synthetic_event);
235   return true;
236 }
237
238 void CustomButton::ShowContextMenu(const gfx::Point& p,
239                                    ui::MenuSourceType source_type) {
240   if (!context_menu_controller())
241     return;
242
243   // We're about to show the context menu. Showing the context menu likely means
244   // we won't get a mouse exited and reset state. Reset it now to be sure.
245   if (state_ != STATE_DISABLED)
246     SetState(STATE_NORMAL);
247   View::ShowContextMenu(p, source_type);
248 }
249
250 void CustomButton::OnDragDone() {
251   SetState(STATE_NORMAL);
252 }
253
254 void CustomButton::GetAccessibleState(ui::AccessibleViewState* state) {
255   Button::GetAccessibleState(state);
256   switch (state_) {
257     case STATE_HOVERED:
258       state->state = ui::AccessibilityTypes::STATE_HOTTRACKED;
259       break;
260     case STATE_PRESSED:
261       state->state = ui::AccessibilityTypes::STATE_PRESSED;
262       break;
263     case STATE_DISABLED:
264       state->state = ui::AccessibilityTypes::STATE_UNAVAILABLE;
265       break;
266     case STATE_NORMAL:
267     case STATE_COUNT:
268       // No additional accessibility state set for this button state.
269       break;
270   }
271 }
272
273 void CustomButton::VisibilityChanged(View* starting_from, bool visible) {
274   if (state_ == STATE_DISABLED)
275     return;
276   SetState(visible && IsMouseHovered() ? STATE_HOVERED : STATE_NORMAL);
277 }
278
279 ////////////////////////////////////////////////////////////////////////////////
280 // CustomButton, gfx::AnimationDelegate implementation:
281
282 void CustomButton::AnimationProgressed(const gfx::Animation* animation) {
283   SchedulePaint();
284 }
285
286 ////////////////////////////////////////////////////////////////////////////////
287 // CustomButton, protected:
288
289 CustomButton::CustomButton(ButtonListener* listener)
290     : Button(listener),
291       state_(STATE_NORMAL),
292       animate_on_state_change_(true),
293       is_throbbing_(false),
294       triggerable_event_flags_(ui::EF_LEFT_MOUSE_BUTTON),
295       request_focus_on_press_(true) {
296   hover_animation_.reset(new gfx::ThrobAnimation(this));
297   hover_animation_->SetSlideDuration(kHoverFadeDurationMs);
298 }
299
300 void CustomButton::StateChanged() {
301 }
302
303 bool CustomButton::IsTriggerableEvent(const ui::Event& event) {
304   return event.type() == ui::ET_GESTURE_TAP_DOWN ||
305          event.type() == ui::ET_GESTURE_TAP ||
306          (event.IsMouseEvent() &&
307              (triggerable_event_flags_ & event.flags()) != 0);
308 }
309
310 bool CustomButton::ShouldEnterPushedState(const ui::Event& event) {
311   return IsTriggerableEvent(event);
312 }
313
314 ////////////////////////////////////////////////////////////////////////////////
315 // CustomButton, View overrides (protected):
316
317 void CustomButton::ViewHierarchyChanged(
318     const ViewHierarchyChangedDetails& details) {
319   if (!details.is_add && state_ != STATE_DISABLED)
320     SetState(STATE_NORMAL);
321 }
322
323 void CustomButton::OnBlur() {
324   if (IsHotTracked())
325     SetState(STATE_NORMAL);
326 }
327
328 }  // namespace views