diff options
author | rjkroege@google.com <rjkroege@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-08 14:06:10 +0000 |
---|---|---|
committer | rjkroege@google.com <rjkroege@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-08 14:06:10 +0000 |
commit | ad84c8f53a2973e4d752de9fe8c52e3268a867e1 (patch) | |
tree | cb330cc04d3b0dd3a93eca847e367d794310653c /views | |
parent | 2229e8264e786f5265bf5c0a6265d5aa3dfaf713 (diff) | |
download | chromium_src-ad84c8f53a2973e4d752de9fe8c52e3268a867e1.zip chromium_src-ad84c8f53a2973e4d752de9fe8c52e3268a867e1.tar.gz chromium_src-ad84c8f53a2973e4d752de9fe8c52e3268a867e1.tar.bz2 |
Added entry points to view/View to dispatch and process TouchEvents.
This change is part of a series of changes to bring TouchEvent
processing to views.
BUG=na
TESTED=compiled with and without touchui
Review URL: http://codereview.chromium.org/3192002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58823 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/event.h | 3 | ||||
-rw-r--r-- | views/touchui/gesture_manager.cc | 62 | ||||
-rw-r--r-- | views/touchui/gesture_manager.h | 54 | ||||
-rw-r--r-- | views/view.cc | 16 | ||||
-rw-r--r-- | views/view.h | 15 | ||||
-rw-r--r-- | views/view_unittest.cc | 158 | ||||
-rw-r--r-- | views/views.gyp | 8 | ||||
-rw-r--r-- | views/widget/root_view.cc | 65 | ||||
-rw-r--r-- | views/widget/root_view.h | 29 |
9 files changed, 401 insertions, 9 deletions
diff --git a/views/event.h b/views/event.h index 14a6af7..f02bd1b 100644 --- a/views/event.h +++ b/views/event.h @@ -290,7 +290,8 @@ class TouchEvent : public LocatedEvent { } private: - // The identity (typically finger) of the touch. + // The identity (typically finger) of the touch starting at 0 and incrementing + // for each separable additional touch that the hardware can detect. const int touch_id_; DISALLOW_COPY_AND_ASSIGN(TouchEvent); diff --git a/views/touchui/gesture_manager.cc b/views/touchui/gesture_manager.cc new file mode 100644 index 0000000..7123519 --- /dev/null +++ b/views/touchui/gesture_manager.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "views/touchui/gesture_manager.h" +#ifndef NDEBUG +#include <iostream> +#endif + +#include "base/logging.h" +#include "views/event.h" +#include "views/view.h" + +namespace views { + +GestureManager::~GestureManager() { +} + +GestureManager* GestureManager::Get() { + return Singleton<GestureManager>::get(); +} + +bool GestureManager::ProcessTouchEventForGesture(const TouchEvent& event, + View* source, + bool previouslyHandled) { + // TODO(rjkroege): A realistic version of the GestureManager will + // appear in a subsequent CL. This interim version permits verifying that the + // event distirbution code works by turning all touch inputs into + // mouse approximations. + bool handled = false; + if (event.GetType() == Event::ET_TOUCH_PRESSED) { + DLOG(INFO) << "GestureManager::ProcessTouchEventForGesture: " << + "TouchPressed\n"; + MouseEvent mouse_event(Event::ET_MOUSE_PRESSED, event.x(), event.y(), + event.GetFlags()); + source->OnMousePressed(mouse_event); + handled = true; + } else if (event.GetType() == Event::ET_TOUCH_RELEASED) { + DLOG(INFO) << "GestureManager::ProcessTouchEventForGesture: " << + "TouchReleased\n"; + MouseEvent mouse_event(Event::ET_MOUSE_RELEASED, event.x(), event.y(), + event.GetFlags()); + source->OnMouseReleased(mouse_event, false); + handled = true; + } else if (event.GetType() == Event::ET_TOUCH_MOVED) { + DLOG(INFO) << "GestureManager::ProcessTouchEventForGesture: " << + "TouchMotion\n"; + MouseEvent mouse_event(Event::ET_MOUSE_DRAGGED, event.x(), event.y(), + event.GetFlags()); + source->OnMouseDragged(mouse_event); + handled = true; + } else { + DLOG(INFO) << "GestureManager::ProcessTouchEventForGesture: " << + "unhandled event\n"; + } + return handled; +} + +GestureManager::GestureManager() { +} + +} // namespace views diff --git a/views/touchui/gesture_manager.h b/views/touchui/gesture_manager.h new file mode 100644 index 0000000..2b905d3 --- /dev/null +++ b/views/touchui/gesture_manager.h @@ -0,0 +1,54 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef VIEWS_TOUCHUI_GESTURE_MANAGER_H_ +#define VIEWS_TOUCHUI_GESTURE_MANAGER_H_ +#pragma once + +#include "base/singleton.h" + +namespace views { +class View; +class TouchEvent; + + +// A GestureManager singleton detects gestures occurring in the +// incoming feed of touch events across all of the RootViews in +// the system. In response to a given touch event, the GestureManager +// updates its internal state and optionally dispatches synthetic +// events to the invoking view. +// +class GestureManager { + public: + virtual ~GestureManager(); + + static GestureManager* Get(); + + // Invoked for each touch event that could contribute to the current gesture. + // Takes the event and the View that originated it and which will also + // be the target of any generated synthetic event. Finally, handled + // specifies if the event was actually handled explicitly by a view so that + // GestureManager state can correctly reflect events that are handled + // already. + // Returns true if the event resulted in firing a synthetic event. + virtual bool ProcessTouchEventForGesture(const TouchEvent& event, + View* source, + bool previouslyHandled); + + // TODO(rjkroege): Write the remainder of this class. + // It will appear in a subsequent CL. + + protected: + GestureManager(); + + private: + friend struct DefaultSingletonTraits<GestureManager>; + + DISALLOW_COPY_AND_ASSIGN(GestureManager); +}; + + +} // namespace views + +#endif // VIEWS_TOUCHUI_GESTURE_MANAGER_H_ diff --git a/views/view.cc b/views/view.cc index 04dbc40..a4e7aa0 100644 --- a/views/view.cc +++ b/views/view.cc @@ -536,6 +536,15 @@ void View::ProcessMouseReleased(const MouseEvent& e, bool canceled) { // WARNING: we may have been deleted. } +#if defined(TOUCH_UI) +bool View::ProcessTouchEvent(const TouchEvent& e) { + // TODO(rjkroege): Implement a grab scheme similar to as + // as is found in MousePressed. + const bool result = OnTouchEvent(e); + return result; +} +#endif + void View::AddChildView(View* v) { AddChildView(static_cast<int>(child_views_.size()), v); } @@ -1289,6 +1298,13 @@ void View::OnMouseEntered(const MouseEvent& e) { void View::OnMouseExited(const MouseEvent& e) { } +#if defined(TOUCH_UI) +bool View::OnTouchEvent(const TouchEvent& event) { + DLOG(INFO) << "visited the OnTouchEvent"; + return false; +} +#endif + void View::SetMouseHandler(View *new_mouse_handler) { // It is valid for new_mouse_handler to be NULL if (parent_) diff --git a/views/view.h b/views/view.h index 293c5c7..2504667 100644 --- a/views/view.h +++ b/views/view.h @@ -705,6 +705,12 @@ class View : public AcceleratorTarget { // Default implementation does nothing. Override as needed. virtual void OnMouseExited(const MouseEvent& event); +#if defined(TOUCH_UI) + // This method is invoked for each touch event. Default implementation + // does nothing. Override as needed. + virtual bool OnTouchEvent(const TouchEvent& event); +#endif + // Set the MouseHandler for a drag session. // // A drag session is a stream of mouse events starting @@ -712,7 +718,7 @@ class View : public AcceleratorTarget { // events and finishing with a MouseReleased event. // // This method should be only invoked while processing a - // MouseDragged or MouseReleased event. + // MouseDragged or MousePressed event. // // All further mouse dragged and mouse up events will be sent // the MouseHandler, even if it is reparented to another window. @@ -1160,6 +1166,13 @@ class View : public AcceleratorTarget { bool ProcessMouseDragged(const MouseEvent& e, DragInfo* drop_info); void ProcessMouseReleased(const MouseEvent& e, bool canceled); +#if defined(TOUCH_UI) + // RootView will invoke this with incoming TouchEvents. Returns the + // the result of OnTouchEvent: true if the event was handled by the + // callee. + bool ProcessTouchEvent(const TouchEvent& e); +#endif + // Starts a drag and drop operation originating from this view. This invokes // WriteDragData to write the data and GetDragOperations to determine the // supported drag operations. When done, OnDragDone is invoked. diff --git a/views/view_unittest.cc b/views/view_unittest.cc index a44ce6f..886f1f2 100644 --- a/views/view_unittest.cc +++ b/views/view_unittest.cc @@ -14,9 +14,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "views/background.h" #include "views/controls/button/checkbox.h" -#if defined(OS_WIN) -#include "views/controls/button/native_button_win.h" -#endif #include "views/controls/native/native_view_host.h" #include "views/controls/scroll_view.h" #include "views/controls/textfield/textfield.h" @@ -26,14 +23,19 @@ #include "views/view.h" #include "views/views_delegate.h" #include "views/widget/root_view.h" +#include "views/window/dialog_delegate.h" +#include "views/window/window.h" + #if defined(OS_WIN) #include "views/widget/widget_win.h" +#include "views/controls/button/native_button_win.h" #elif defined(OS_LINUX) #include "views/widget/widget_gtk.h" #include "views/window/window_gtk.h" #endif -#include "views/window/dialog_delegate.h" -#include "views/window/window.h" +#if defined(TOUCH_UI) +#include "views/touchui/gesture_manager.h" +#endif using namespace views; @@ -155,6 +157,10 @@ class TestView : public View { child_removed_ = false; last_mouse_event_type_ = 0; location_.SetPoint(0, 0); +#if defined(TOUCH_UI) + last_touch_event_type_ = 0; + last_touch_event_was_handled_ = false; +#endif last_clip_.setEmpty(); accelerator_count_map_.clear(); } @@ -165,6 +171,9 @@ class TestView : public View { virtual bool OnMousePressed(const MouseEvent& event); virtual bool OnMouseDragged(const MouseEvent& event); virtual void OnMouseReleased(const MouseEvent& event, bool canceled); +#if defined(TOUCH_UI) + virtual bool OnTouchEvent(const TouchEvent& event); +#endif virtual void Paint(gfx::Canvas* canvas); virtual bool AcceleratorPressed(const Accelerator& accelerator); @@ -183,6 +192,12 @@ class TestView : public View { int last_mouse_event_type_; gfx::Point location_; +#if defined(TOUCH_UI) + // TouchEvent + int last_touch_event_type_; + bool last_touch_event_was_handled_; +#endif + // Painting SkRect last_clip_; @@ -190,6 +205,31 @@ class TestView : public View { std::map<Accelerator, int> accelerator_count_map_; }; +#if defined(TOUCH_UI) +// Mock instance of the GestureManager for testing. +class MockGestureManager : public GestureManager { + public: + // Reset all test state + void Reset() { + last_touch_event_ = 0; + last_view_ = NULL; + previously_handled_flag_ = false; + } + + bool previously_handled_flag_; + bool ProcessTouchEventForGesture(const TouchEvent& event, + View* source, + bool previouslyHandled); + MockGestureManager(); + + int last_touch_event_; + View *last_view_; + + DISALLOW_COPY_AND_ASSIGN(MockGestureManager); +}; + +#endif + //////////////////////////////////////////////////////////////////////////////// // DidChangeBounds //////////////////////////////////////////////////////////////////////////////// @@ -383,6 +423,114 @@ TEST_F(ViewTest, MouseEvent) { window->CloseNow(); } +#if defined(TOUCH_UI) +//////////////////////////////////////////////////////////////////////////////// +// TouchEvent +//////////////////////////////////////////////////////////////////////////////// +bool MockGestureManager::ProcessTouchEventForGesture( + const TouchEvent& event, + View* source, + bool previouslyHandled) { + last_touch_event_ = event.GetType(); + last_view_ = source; + previously_handled_flag_ = previouslyHandled; + return true; +} + +MockGestureManager::MockGestureManager() { +} + +bool TestView::OnTouchEvent(const TouchEvent& event) { + last_touch_event_type_ = event.GetType(); + location_.SetPoint(event.x(), event.y()); + return last_touch_event_was_handled_; +} + +TEST_F(ViewTest, TouchEvent) { + MockGestureManager* gm = new MockGestureManager(); + + TestView* v1 = new TestView(); + v1->SetBounds(0, 0, 300, 300); + + TestView* v2 = new TestView(); + v2->SetBounds(100, 100, 100, 100); + + scoped_ptr<Widget> window(CreateWidget()); +#if defined(OS_WIN) + // This code would need to be here when we support + // touch on windows? + WidgetWin* window_win = static_cast<WidgetWin*>(window.get()); + window_win->set_delete_on_destroy(false); + window_win->set_window_style(WS_OVERLAPPEDWINDOW); + window_win->Init(NULL, gfx::Rect(50, 50, 650, 650)); +#endif + RootView* root = window->GetRootView(); + + root->AddChildView(v1); + root->SetGestureManager(gm); + v1->AddChildView(v2); + + v1->Reset(); + v2->Reset(); + gm->Reset(); + + TouchEvent pressed(Event::ET_TOUCH_PRESSED, + 110, + 120, + 0, /* no flags */ + 0 /* first finger touch */); + v2->last_touch_event_was_handled_ = true; + root->OnTouchEvent(pressed); + + EXPECT_EQ(v2->last_touch_event_type_, Event::ET_TOUCH_PRESSED); + EXPECT_EQ(v2->location_.x(), 10); + EXPECT_EQ(v2->location_.y(), 20); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_touch_event_type_, 0); + + EXPECT_EQ(gm->last_touch_event_, Event::ET_TOUCH_PRESSED); + EXPECT_EQ(gm->last_view_, root); + EXPECT_EQ(gm->previously_handled_flag_, true); + + // Drag event out of bounds. Should still go to v2 + v1->Reset(); + v2->Reset(); + TouchEvent dragged(Event::ET_TOUCH_MOVED, + 50, + 40, + 0, /* no flags */ + 0 /* first finger touch */); + root->OnTouchEvent(dragged); + EXPECT_EQ(v2->last_touch_event_type_, Event::ET_TOUCH_MOVED); + EXPECT_EQ(v2->location_.x(), -50); + EXPECT_EQ(v2->location_.y(), -60); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_touch_event_type_, 0); + + EXPECT_EQ(gm->last_touch_event_, Event::ET_TOUCH_MOVED); + EXPECT_EQ(gm->last_view_, root); + EXPECT_EQ(gm->previously_handled_flag_, true); + + // Releasted event out of bounds. Should still go to v2 + v1->Reset(); + v2->Reset(); + TouchEvent released(Event::ET_TOUCH_RELEASED, 0, 0, 0, 0 /* first finger */); + v2->last_touch_event_was_handled_ = true; + root->OnTouchEvent(released); + EXPECT_EQ(v2->last_touch_event_type_, Event::ET_TOUCH_RELEASED); + EXPECT_EQ(v2->location_.x(), -100); + EXPECT_EQ(v2->location_.y(), -100); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_touch_event_type_, 0); + + EXPECT_EQ(gm->last_touch_event_, Event::ET_TOUCH_RELEASED); + EXPECT_EQ(gm->last_view_, root); + EXPECT_EQ(gm->previously_handled_flag_, true); + + window->CloseNow(); +} +#endif + //////////////////////////////////////////////////////////////////////////////// // Painting //////////////////////////////////////////////////////////////////////////////// diff --git a/views/views.gyp b/views/views.gyp index 8c437a6..ef8ba07 100644 --- a/views/views.gyp +++ b/views/views.gyp @@ -31,6 +31,9 @@ ['include', '/win/'], ['include', '/win_[^/]*\\.cc$'], ]}], + ['touchui==0', {'sources/': [ + ['exclude', 'touchui/'], + ]}], ], }, 'targets': [ @@ -260,6 +263,8 @@ 'screen_gtk.cc', 'screen_win.cc', 'standard_layout.h', + 'touchui/gesture_manager.h', + 'touchui/gesture_manager.cc', 'view.cc', 'view.h', 'view_constants.cc', @@ -359,6 +364,9 @@ 'widget/widget_win.cc', ], }], + ['touchui==1', { + 'defines': ['TOUCH_UI=1'], + }], ['OS=="win"', { 'sources!': [ 'controls/slider/slider.cc', diff --git a/views/widget/root_view.cc b/views/widget/root_view.cc index 20464cc..8b8f896 100644 --- a/views/widget/root_view.cc +++ b/views/widget/root_view.cc @@ -16,6 +16,10 @@ #include "views/widget/widget.h" #include "views/window/window.h" +#if defined(TOUCH_UI) +#include "views/touchui/gesture_manager.h" +#endif + #if defined(OS_LINUX) #include "views/widget/widget_gtk.h" #endif // defined(OS_LINUX) @@ -76,6 +80,11 @@ RootView::RootView(Widget* widget) focus_traversable_parent_(NULL), focus_traversable_parent_view_(NULL), drag_view_(NULL) +#if defined(TOUCH_UI) + , + gesture_manager_(GestureManager::Get()), + touch_pressed_handler_(NULL) +#endif #ifndef NDEBUG , is_processing_paint_(false) @@ -289,6 +298,62 @@ void RootView::SetFocusOnMousePressed(bool f) { focus_on_mouse_pressed_ = f; } +#if defined(TOUCH_UI) +bool RootView::OnTouchEvent(const TouchEvent& e) { + // If touch_pressed_handler_ is non null, we are currently processing + // a touch down on the screen situation. In that case we send the + // event to touch_pressed_handler_ + + if (touch_pressed_handler_) { + TouchEvent touch_event(e, this, touch_pressed_handler_); + touch_pressed_handler_->ProcessTouchEvent(touch_event); + gesture_manager_->ProcessTouchEventForGesture(e, this, true); + return true; + } + + bool handled = false; + // Walk up the tree until we find a view that wants the touch event. + for (touch_pressed_handler_ = GetViewForPoint(e.location()); + touch_pressed_handler_ && (touch_pressed_handler_ != this); + touch_pressed_handler_ = touch_pressed_handler_->GetParent()) { + if (!touch_pressed_handler_->IsEnabled()) { + // Disabled views eat events but are treated as not handled by the + // the GestureManager. + handled = false; + break; + } + + // See if this view wants to handle the touch + TouchEvent touch_event(e, this, touch_pressed_handler_); + handled = touch_pressed_handler_->ProcessTouchEvent(touch_event); + + // The view could have removed itself from the tree when handling + // OnTouchEvent(). So handle as per OnMousePressed. NB: we + // assume that the RootView itself cannot be so removed. + // + // NOTE: Don't return true here, because we don't want the frame to + // forward future events to us when there's no handler. + if (!touch_pressed_handler_) + break; + + // If the view handled the event, leave touch_pressed_handler_ set and + // return true, which will cause subsequent drag/release events to get + // forwarded to that view. + if (handled) { + gesture_manager_->ProcessTouchEventForGesture(e, this, handled); + return true; + } + } + + // Reset touch_pressed_handler_ to indicate that no processing is occurring. + touch_pressed_handler_ = NULL; + + // Give the touch event to the gesture manager. + gesture_manager_->ProcessTouchEventForGesture(e, this, handled); + return handled; +} +#endif + bool RootView::OnMousePressed(const MouseEvent& e) { // This function does not normally handle non-client messages except for // non-client double-clicks. Actually, all double-clicks are special as the diff --git a/views/widget/root_view.h b/views/widget/root_view.h index c749bc8..91c1672 100644 --- a/views/widget/root_view.h +++ b/views/widget/root_view.h @@ -22,6 +22,10 @@ namespace views { class PaintTask; class Widget; +#if defined(TOUCH_UI) +class GestureManager; +#endif + ///////////////////////////////////////////////////////////////////////////// // // RootView class @@ -93,6 +97,9 @@ class RootView : public View, virtual void OnMouseReleased(const MouseEvent& e, bool canceled); virtual void OnMouseMoved(const MouseEvent& e); virtual void SetMouseHandler(View* new_mouse_handler); +#if defined(TOUCH_UI) + virtual bool OnTouchEvent(const TouchEvent& e); +#endif // Invoked By the Widget if the mouse drag is interrupted by // the system. Invokes OnMouseReleased with a value of true for canceled. @@ -175,6 +182,12 @@ class RootView : public View, // Accessibility accessors/mutators, overridden from View. virtual bool GetAccessibleRole(AccessibilityTypes::Role* role); +#if defined(TOUCH_UI) && defined(UNIT_TEST) + // For unit testing purposes, we use this method to set a mock + // GestureManager + void SetGestureManager(GestureManager* g) { gesture_manager_ = g; } +#endif + protected: // Overridden to properly reset our event propagation member @@ -189,6 +202,12 @@ class RootView : public View, friend class View; friend class PaintTask; +#if defined(TOUCH_UI) + // Required so the GestureManager can call the Process* entry points + // with synthetic events as necessary. + friend class GestureManager; +#endif + RootView(); // Convert a point to our current mouse handler. Returns false if the @@ -321,14 +340,20 @@ class RootView : public View, // view the drag started from. View* drag_view_; +#if defined(TOUCH_UI) + // The gesture_manager_ for this. + GestureManager* gesture_manager_; + + // The view currently handling touch events. + View *touch_pressed_handler_; +#endif + #ifndef NDEBUG // True if we're currently processing paint. bool is_processing_paint_; #endif - DISALLOW_COPY_AND_ASSIGN(RootView); }; - } // namespace views #endif // VIEWS_WIDGET_ROOT_VIEW_H_ |