Implementation of the ui/ portion of the Touch Exploration Mode

Unofficial design doc: https://docs.google.com/a/google.com/document/d/1uOphhlascPTfNjsI2QTdeVTp-ANEz-g2rqGli5yVv34/edit#heading=h.a67n1i6wy36k

Reviews of the previous uncommitted implementation iterations:
https://codereview.chromium.org/240333007
https://codereview.chromium.org/225143007

BUG=368828

Review URL: https://codereview.chromium.org/262483003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268224 0039d316-1c4b-4281-b951-d872f2087c98
parent 12961e3c
include_rules = [ include_rules = [
"+chromeos/dbus", "+chromeos/dbus",
"+ui/aura",
"+ui/events", "+ui/events",
"+ui/gfx",
"+ui/gl",
"+ui/wm", "+ui/wm",
] ]
// Copyright 2014 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 "ui/chromeos/touch_exploration_controller.h"
#include "base/logging.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/event.h"
namespace ui {
TouchExplorationController::TouchExplorationController(
aura::Window* root_window)
: root_window_(root_window) {
CHECK(root_window);
root_window->GetHost()->GetEventSource()->AddEventRewriter(this);
}
TouchExplorationController::~TouchExplorationController() {
root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
}
ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
const ui::Event& event,
scoped_ptr<ui::Event>* rewritten_event) {
if (!event.IsTouchEvent())
return ui::EVENT_REWRITE_CONTINUE;
const ui::TouchEvent& touch_event =
static_cast<const ui::TouchEvent&>(event);
const ui::EventType type = touch_event.type();
const gfx::PointF& location = touch_event.location_f();
const int touch_id = touch_event.touch_id();
const int flags = touch_event.flags();
if (type == ui::ET_TOUCH_PRESSED) {
touch_ids_.push_back(touch_id);
touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location));
// If this is the first and only finger touching - rewrite the touch as a
// mouse move. Otherwise let the it go through as is.
if (touch_ids_.size() == 1) {
*rewritten_event = CreateMouseMoveEvent(location, flags);
EnterTouchToMouseMode();
return ui::EVENT_REWRITE_REWRITTEN;
}
return ui::EVENT_REWRITE_CONTINUE;
} else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
std::vector<int>::iterator it =
std::find(touch_ids_.begin(), touch_ids_.end(), touch_id);
// We may fail to find the finger if the exploration mode was turned on
// while the user had some fingers touching the screen. We simply ignore
// those fingers for the purposes of event transformation.
if (it == touch_ids_.end())
return ui::EVENT_REWRITE_CONTINUE;
const bool first_finger_released = it == touch_ids_.begin();
touch_ids_.erase(it);
int num_erased = touch_locations_.erase(touch_id);
DCHECK_EQ(num_erased, 1);
const int num_fingers_remaining = touch_ids_.size();
if (num_fingers_remaining == 0) {
*rewritten_event = CreateMouseMoveEvent(location, flags);
return ui::EVENT_REWRITE_REWRITTEN;
}
// If we are left with one finger - enter the mouse move mode.
const bool enter_mouse_move_mode = num_fingers_remaining == 1;
if (!enter_mouse_move_mode && !first_finger_released) {
// No special handling needed.
return ui::EVENT_REWRITE_CONTINUE;
}
// If the finger which was released was the first one, - we need to rewrite
// the release event as a release of the was second / now first finger.
// This is the finger which will now be getting "substracted".
if (first_finger_released) {
int rewritten_release_id = touch_ids_[0];
const gfx::PointF& rewritten_release_location =
touch_locations_[rewritten_release_id];
ui::TouchEvent* rewritten_release_event = new ui::TouchEvent(
ui::ET_TOUCH_RELEASED,
rewritten_release_location,
rewritten_release_id,
event.time_stamp());
rewritten_release_event->set_flags(touch_event.flags());
rewritten_event->reset(rewritten_release_event);
} else if (enter_mouse_move_mode) {
// Dispatch the release event as is.
// TODO(mfomitchev): We can get rid of this clause once we have
// EVENT_REWRITE_DISPATCH_ANOTHER working without having to set
// rewritten_event.
rewritten_event->reset(new ui::TouchEvent(touch_event));
}
if (enter_mouse_move_mode) {
// Since we are entering the mouse move mode - also dispatch a mouse move
// event at the location of the one remaining finger. (num_fingers == 1)
const gfx::PointF& mouse_move_location = touch_locations_[touch_ids_[0]];
next_dispatch_event_ =
CreateMouseMoveEvent(mouse_move_location, flags).Pass();
return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
}
return ui::EVENT_REWRITE_REWRITTEN;
} else if (type == ui::ET_TOUCH_MOVED) {
std::vector<int>::iterator it =
std::find(touch_ids_.begin(), touch_ids_.end(), touch_id);
// We may fail to find the finger if the exploration mode was turned on
// while the user had some fingers touching the screen. We simply ignore
// those fingers for the purposes of event transformation.
if (it == touch_ids_.end())
return ui::EVENT_REWRITE_CONTINUE;
touch_locations_[*it] = location;
if (touch_ids_.size() == 1) {
// Touch moves are rewritten as mouse moves when there's only one finger
// touching the screen.
*rewritten_event = CreateMouseMoveEvent(location, flags).Pass();
return ui::EVENT_REWRITE_REWRITTEN;
}
if (touch_id == touch_ids_.front()) {
// Touch moves of the first finger are discarded when there's more than
// one finger touching.
return ui::EVENT_REWRITE_DISCARD;
}
return ui::EVENT_REWRITE_CONTINUE;
}
NOTREACHED() << "Unexpected event type received.";
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent(
const ui::Event& last_event,
scoped_ptr<ui::Event>* new_event) {
CHECK(next_dispatch_event_);
DCHECK(last_event.IsTouchEvent());
*new_event = next_dispatch_event_.Pass();
// Enter the mouse move mode if needed
if ((*new_event)->IsMouseEvent())
EnterTouchToMouseMode();
return ui::EVENT_REWRITE_REWRITTEN;
}
scoped_ptr<ui::Event> TouchExplorationController::CreateMouseMoveEvent(
const gfx::PointF& location,
int flags) {
return scoped_ptr<ui::Event>(
new ui::MouseEvent(ui::ET_MOUSE_MOVED,
location,
location,
flags | ui::EF_IS_SYNTHESIZED | ui::EF_FROM_TOUCH,
0));
}
void TouchExplorationController::EnterTouchToMouseMode() {
aura::client::CursorClient* cursor_client =
aura::client::GetCursorClient(root_window_);
if (cursor_client && !cursor_client->IsMouseEventsEnabled())
cursor_client->EnableMouseEvents();
if (cursor_client && cursor_client->IsCursorVisible())
cursor_client->HideCursor();
}
} // namespace ui
// Copyright 2014 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 UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_
#define UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_
#include "base/values.h"
#include "ui/chromeos/ui_chromeos_export.h"
#include "ui/events/event_rewriter.h"
#include "ui/gfx/geometry/point.h"
namespace aura {
class Window;
}
namespace ui {
class Event;
// TouchExplorationController is used in tandem with "Spoken Feedback" to
// make the touch UI accessible. TouchExplorationController rewrites the
// incoming touch events as follows:
// - When one finger is touching the screen, touch events are converted to mouse
// moves. This is the "Touch Exploration Mode". (The idea is that mouse moves
// will be subsequently used by another component to move focus between UI
// elements, and the elements will be read out to the user.)
// - When more than one finger is touching the screen, touches from the
// first (i.e. "oldest") finger are ignored, and the other touches go through
// as is.
// The caller is expected to retain ownership of instances of this class and
// destroy them before |root_window| is destroyed.
class UI_CHROMEOS_EXPORT TouchExplorationController :
public ui::EventRewriter {
public:
explicit TouchExplorationController(aura::Window* root_window);
virtual ~TouchExplorationController();
private:
scoped_ptr<ui::Event> CreateMouseMoveEvent(const gfx::PointF& location,
int flags);
void EnterTouchToMouseMode();
// Overridden from ui::EventRewriter
virtual ui::EventRewriteStatus RewriteEvent(
const ui::Event& event, scoped_ptr<ui::Event>* rewritten_event) OVERRIDE;
virtual ui::EventRewriteStatus NextDispatchEvent(
const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) OVERRIDE;
// A set of touch ids for fingers currently touching the screen.
std::vector<int> touch_ids_;
// Map of touch ids to their last known location.
std::map<int, gfx::PointF> touch_locations_;
// Initialized from RewriteEvent() and dispatched in NextDispatchEvent().
scoped_ptr<ui::Event> next_dispatch_event_;
aura::Window* root_window_;
DISALLOW_COPY_AND_ASSIGN(TouchExplorationController);
};
} // namespace ui
#endif // UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_
This diff is collapsed.
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
'type': '<(component)', 'type': '<(component)',
'dependencies': [ 'dependencies': [
'../../base/base.gyp:base', '../../base/base.gyp:base',
'../../skia/skia.gyp:skia',
'../aura/aura.gyp:aura',
'../events/events.gyp:events', '../events/events.gyp:events',
'../wm/wm.gyp:wm', '../wm/wm.gyp:wm',
], ],
...@@ -19,6 +21,8 @@ ...@@ -19,6 +21,8 @@
'UI_CHROMEOS_IMPLEMENTATION', 'UI_CHROMEOS_IMPLEMENTATION',
], ],
'sources': [ 'sources': [
'touch_exploration_controller.cc',
'touch_exploration_controller.h',
'user_activity_power_manager_notifier.cc', 'user_activity_power_manager_notifier.cc',
'user_activity_power_manager_notifier.h', 'user_activity_power_manager_notifier.h',
], ],
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "ui/events/test/events_test_utils.h" #include "ui/events/test/events_test_utils.h"
#include "ui/events/event_source.h"
namespace ui { namespace ui {
EventTestApi::EventTestApi(Event* event) : event_(event) {} EventTestApi::EventTestApi(Event* event) : event_(event) {}
......
...@@ -6,11 +6,13 @@ ...@@ -6,11 +6,13 @@
#define UI_EVENTS_TEST_EVENTS_TEST_UTILS_H_ #define UI_EVENTS_TEST_EVENTS_TEST_UTILS_H_
#include "ui/events/event.h" #include "ui/events/event.h"
#include "ui/events/event_source.h" #include "ui/events/event_dispatcher.h"
#include "ui/events/event_target.h" #include "ui/events/event_target.h"
namespace ui { namespace ui {
class EventSource;
class EventTestApi { class EventTestApi {
public: public:
explicit EventTestApi(Event* event); explicit EventTestApi(Event* event);
......
...@@ -219,6 +219,11 @@ ...@@ -219,6 +219,11 @@
['chromeos==1', { ['chromeos==1', {
'dependencies': [ 'dependencies': [
'../chromeos/chromeos.gyp:chromeos', '../chromeos/chromeos.gyp:chromeos',
'aura/aura.gyp:aura_test_support',
'chromeos/ui_chromeos.gyp:ui_chromeos',
],
'sources': [
'chromeos/touch_exploration_controller_unittest.cc'
], ],
'sources!': [ 'sources!': [
'base/dragdrop/os_exchange_data_provider_aurax11_unittest.cc', 'base/dragdrop/os_exchange_data_provider_aurax11_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment