Bug 845925 - Add scroll/pinch actions using touch action chains to the python touch layer, r=mdas

This commit is contained in:
Yiming Yang 2013-04-26 11:14:50 -07:00
Родитель 80dfb44965
Коммит 89729a754e
8 изменённых файлов: 177 добавлений и 11 удалений

Просмотреть файл

@ -0,0 +1,71 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette import MultiActions, Actions
#axis is y or x
#direction is 0 for positive, and -1 for negative
#length is the total length we want to scroll
#increments is how much we want to move per scrolling
#wait_period is the seconds we wait between scrolling
#scroll_back is whether we want to scroll back to original view
def smooth_scroll(marionette_session, start_element, axis, direction, length, increments=None, wait_period=None, scroll_back=None):
if axis not in ["x", "y"]:
raise Exception("Axis must be either 'x' or 'y'")
if direction not in [-1, 0]:
raise Exception("Direction must either be -1 negative or 0 positive")
increments = increments or 100
wait_period = wait_period or 0.05
scroll_back = scroll_back or False
current = 0
if axis is "x":
if direction is -1:
offset = [-increments, 0]
else:
offset = [increments, 0]
else:
if direction is -1:
offset = [0, -increments]
else:
offset = [0, increments]
action = Actions(marionette_session)
action.press(start_element)
while (current < length):
current += increments
action.move_by_offset(*offset).wait(wait_period)
if scroll_back:
offset = [-value for value in offset]
while (current > 0):
current -= increments
action.move_by_offset(*offset).wait(wait_period)
action.release()
action.perform()
#element is the target
#x1,x2 are 1st finger starting position relative to the target
#x3,y3 are 1st finger ending position relative to the target
#x2,y2 are 2nd finger starting position relative to the target
#x4,y4 are 2nd finger ending position relative to the target
#duration is the amount of time in milliseconds we want to complete the pinch.
def pinch(marionette_session, element, x1, y1, x2, y2, x3, y3, x4, y4, duration=200):
time = 0
time_increment = 10
if time_increment >= duration:
time_increment = duration
move_x1 = time_increment*1.0/duration * (x3 - x1)
move_y1 = time_increment*1.0/duration * (y3 - y1)
move_x2 = time_increment*1.0/duration * (x4 - x2)
move_y2 = time_increment*1.0/duration * (y4 - y2)
multiAction = MultiActions(marionette_session)
action1 = Actions(marionette_session)
action2 = Actions(marionette_session)
action1.press(element, x1, y1)
action2.press(element, x2, y2)
while (time < duration):
time += time_increment
action1.move_by_offset(move_x1, move_y1).wait(time_increment/1000)
action2.move_by_offset(move_x2, move_y2).wait(time_increment/1000)
action1.release()
action2.release()
multiAction.add(action1).add(action2).perform()

Просмотреть файл

@ -138,6 +138,22 @@ class Actions(object):
self.action_chain.append(['cancel']) self.action_chain.append(['cancel'])
return self return self
def flick(self, element, x1, y1, x2, y2, duration=200):
element = element.id
time = 0
time_increment = 10
if time_increment >= duration:
time_increment = duration
move_x = time_increment*1.0/duration * (x2 - x1)
move_y = time_increment*1.0/duration * (y2 - y1)
self.action_chain.append(['press', element, x1, y1])
while (time < duration):
time += time_increment
self.action_chain.append(['moveByOffset', move_x, move_y])
self.action_chain.append(['wait', time_increment/1000])
self.action_chain.append(['release'])
return self
def long_press(self, element, time_in_seconds): def long_press(self, element, time_in_seconds):
element = element.id element = element.id
self.action_chain.append(['press', element]) self.action_chain.append(['press', element])

Просмотреть файл

@ -4,7 +4,8 @@
import os import os
from errors import ElementNotVisibleException from errors import ElementNotVisibleException
from marionette import Actions
from gestures import pinch
""" """
Adds touch support in Marionette Adds touch support in Marionette
""" """
@ -35,8 +36,9 @@ class MarionetteTouchMixin(object):
def flick(self, element, x1, y1, x2, y2, duration=200): def flick(self, element, x1, y1, x2, y2, duration=200):
self.check_element(element) self.check_element(element)
# there's 'flick' which is pixels per second, but I'd rather have the library support it than piece it together here. action = Actions(self.marionette)
self.execute_script("%s.swipe.apply(this, arguments);" % self.library_name, [element, x1, y1, x2, y2, duration]) action.flick(element, x1, y1, x2, y2, duration).perform()
def pinch(self, *args, **kwargs): def pinch(self, element, x1, y1, x2, y2, x3, y3, x4, y4, duration = 200):
raise Exception("Pinch is unsupported") self.check_element(element)
pinch(element, x1, y1, x2, y2, x3, y3, x4, y4, duration)

Просмотреть файл

@ -0,0 +1,23 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_test import MarionetteTestCase
from gestures import smooth_scroll, pinch
class testGestures(MarionetteTestCase):
def test_smooth_scroll(self):
testTouch = self.marionette.absolute_url("testAction.html")
self.marionette.navigate(testTouch)
button = self.marionette.find_element("id", "mozLinkScrollStart")
smooth_scroll(self.marionette, button, "y", -1, 250)
time.sleep(15)
self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkScroll').innerHTML;"))
def test_pinch(self):
testTouch = self.marionette.absolute_url("testAction.html")
self.marionette.navigate(testTouch)
button = self.marionette.find_element("id", "mozLinkScrollStart")
pinch(self.marionette, button, 0, 0, 0, 0, 0, -50, 0, 50)
time.sleep(15)

Просмотреть файл

@ -6,6 +6,7 @@ import time
from marionette_test import MarionetteTestCase from marionette_test import MarionetteTestCase
from marionette import Actions from marionette import Actions
from errors import NoSuchElementException, MarionetteException from errors import NoSuchElementException, MarionetteException
from unittest import skip
class testSingleFinger(MarionetteTestCase): class testSingleFinger(MarionetteTestCase):
def test_wait(self): def test_wait(self):
@ -131,6 +132,7 @@ class testSingleFinger(MarionetteTestCase):
time.sleep(10) time.sleep(10)
self.assertEqual("ContextEnd", self.marionette.execute_script("return document.getElementById('mozLinkCopy').innerHTML;")) self.assertEqual("ContextEnd", self.marionette.execute_script("return document.getElementById('mozLinkCopy').innerHTML;"))
@skip("Skipping due to Bug 865334")
def test_long_press_fail(self): def test_long_press_fail(self):
testTouch = self.marionette.absolute_url("testAction.html") testTouch = self.marionette.absolute_url("testAction.html")
self.marionette.navigate(testTouch) self.marionette.navigate(testTouch)
@ -144,3 +146,13 @@ class testSingleFinger(MarionetteTestCase):
self.marionette.navigate(testTouch) self.marionette.navigate(testTouch)
self.assertRaises(MarionetteException, self.marionette.send_mouse_event, "boolean") self.assertRaises(MarionetteException, self.marionette.send_mouse_event, "boolean")
def test_chain_flick(self):
testTouch = self.marionette.absolute_url("testAction.html")
self.marionette.navigate(testTouch)
button = self.marionette.find_element("id", "mozLinkScrollStart")
action = Actions(self.marionette)
action.flick(button, 0, 0, 0, -250).perform()
time.sleep(15)
self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkScroll').innerHTML;"))
self.assertEqual("Start", self.marionette.execute_script("return document.getElementById('mozLinkScrollStart').innerHTML;"))

Просмотреть файл

@ -11,6 +11,9 @@ b2g = true
; true if the test should be skipped ; true if the test should be skipped
skip = false skip = false
; true if the test requires unagi
unagi = false
[test_getstatus.py] [test_getstatus.py]
[test_import_script.py] [test_import_script.py]
[test_import_script_content.py.py] [test_import_script_content.py.py]
@ -48,14 +51,15 @@ b2g = false
b2g = true b2g = true
browser = false browser = false
[test_cancel.py]
b2g = true
browser = false
[test_press_release.py] [test_press_release.py]
b2g = true b2g = true
browser = false browser = false
[test_gesture.py]
b2g = true
browser = false
unagi = true
[test_single_finger.py] [test_single_finger.py]
b2g = true b2g = true
browser = false browser = false
@ -68,6 +72,10 @@ browser = false
b2g = true b2g = true
browser = false browser = false
[test_cancel.py]
b2g = true
browser = false
[test_simpletest_pass.js] [test_simpletest_pass.js]
[test_simpletest_sanity.py] [test_simpletest_sanity.py]
[test_simpletest_chrome.js] [test_simpletest_chrome.js]

Просмотреть файл

@ -15,6 +15,10 @@
<button id="mozLinkPos" style="position:absolute;left:0px;top:355px;" type="button" allowevents=true>Button2</button> <button id="mozLinkPos" style="position:absolute;left:0px;top:355px;" type="button" allowevents=true>Button2</button>
<!-- "mozLinkCopy" listens for a touchdown and touchup --> <!-- "mozLinkCopy" listens for a touchdown and touchup -->
<button id="mozLinkCopy" style="position:absolute;left:0px;top:455px;" type="button" allowevents=true>Button3</button> <button id="mozLinkCopy" style="position:absolute;left:0px;top:455px;" type="button" allowevents=true>Button3</button>
<!-- "mozLinkScroll" listens for scroll -->
<button id="mozLinkScroll" style="position:absolute;left:0px;top:655px;" type="button" allowevents=true>Button8</button>
<!-- "mozLinkScrollStart" listens for scroll -->
<button id="mozLinkScrollStart" style="position:absolute;left:0px;top:405px;" type="button" allowevents=true>Button9</button>
<!-- "mozLinkStart" and "mozLinkEnd" work together to perform touchdown on mozLinkStart, horizontal move and then touchup on mozLinkEnd --> <!-- "mozLinkStart" and "mozLinkEnd" work together to perform touchdown on mozLinkStart, horizontal move and then touchup on mozLinkEnd -->
<button id="mozLinkStart" style="position:absolute;left:10px;top:200px;" type="button" allowevents=true>Press</button> <button id="mozLinkStart" style="position:absolute;left:10px;top:200px;" type="button" allowevents=true>Press</button>
<button id="mozLinkEnd" style="position:absolute;left:140px;top:200px;" type="button" allowevents=true>Release</button> <button id="mozLinkEnd" style="position:absolute;left:140px;top:200px;" type="button" allowevents=true>Release</button>
@ -32,6 +36,7 @@
var fourth = document.getElementById("mozLinkCopy2"); var fourth = document.getElementById("mozLinkCopy2");
var fifth = document.getElementById("mozLinkCancel"); var fifth = document.getElementById("mozLinkCancel");
var sixth = document.getElementById("mozMouse"); var sixth = document.getElementById("mozMouse");
var seventh = document.getElementById("mozLinkScrollStart");
// touchmove and touchend must be performed on the same element as touchstart // touchmove and touchend must be performed on the same element as touchstart
// here is press for vertical move // here is press for vertical move
press.addEventListener("touchstart", function(){changePressText("mozLink")}, false); press.addEventListener("touchstart", function(){changePressText("mozLink")}, false);
@ -59,6 +64,9 @@
sixth.addEventListener("mousedown", function(){changeMouseText("MouseDown")}, false); sixth.addEventListener("mousedown", function(){changeMouseText("MouseDown")}, false);
sixth.addEventListener("mouseup", function(){changeMouseText("MouseUp")}, false); sixth.addEventListener("mouseup", function(){changeMouseText("MouseUp")}, false);
sixth.addEventListener("click", function(){changeMouseText("MouseClick")}, false); sixth.addEventListener("click", function(){changeMouseText("MouseClick")}, false);
// here is seventh for a scroll
seventh.addEventListener("touchstart", function(){changePressText("mozLinkScrollStart")}, false);
seventh.addEventListener("touchend", function(){changeScrollText("mozLinkScroll")}, false);
function changeMouseText(strId) { function changeMouseText(strId) {
var mouse = document.getElementById("mozMouse"); var mouse = document.getElementById("mozMouse");
switch(strId) { switch(strId) {
@ -193,13 +201,23 @@
} }
} }
function changeScrollText(strId) {
var seventh = document.getElementById(strId);
if (elementInViewport(seventh)) {
seventh.innerHTML = "End";
}
else {
seventh.innerHTML = "Error";
}
}
function changeTimePress() { function changeTimePress() {
var fourth = document.getElementById("mozLinkCopy2"); var fourth = document.getElementById("mozLinkCopy2");
var d = new Date(); var d = new Date();
fourth.innerHTML = d.getTime(); fourth.innerHTML = d.getTime();
var newButton = document.createElement("button"); var newButton = document.createElement("button");
newButton.id = "delayed"; newButton.id = "delayed";
newButton.setAttribute("style", "position:absolute;left:80px;top:105px;"); newButton.setAttribute("style", "position:absolute;left:220px;top:455px;");
var content = document.createTextNode("Button6"); var content = document.createTextNode("Button6");
newButton.appendChild(content); newButton.appendChild(content);
document.body.appendChild(newButton); document.body.appendChild(newButton);
@ -224,6 +242,22 @@
var context = document.getElementById("mozLinkCopy"); var context = document.getElementById("mozLinkCopy");
context.innerHTML = "Context"; context.innerHTML = "Context";
} }
function elementInViewport(el) {
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth;
var height = el.offsetHeight;
while(el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
left += el.offsetLeft;
}
return (top >= window.pageYOffset &&
left >= window.pageXOffset &&
(top + height) <= (window.pageYOffset + window.innerHeight) &&
(left + width) <= (window.pageXOffset + window.innerWidth));
}
</script> </script>
</body> </body>
</html> </html>

Просмотреть файл

@ -747,7 +747,7 @@ function touch(target, duration, xt, yt, then) {
/** /**
* This function generates the coordinates of the element * This function generates the coordinates of the element
* @param 'x0', 'y0', 'x1', and 'y1' are the relative to the viewport. * @param 'x0', 'y0', 'x1', and 'y1' are the relative to the target.
* If they are not specified, then the center of the target is used. * If they are not specified, then the center of the target is used.
*/ */
function coordinates(target, x0, y0, x1, y1) { function coordinates(target, x0, y0, x1, y1) {