Bug 1197192 - Allow dragging the scrubber not just from the timeline header; r=ochameau

This adds a new invisible element centered around the scrubber and
thick enough for users to drag from.

--HG--
extra : commitid : 8T6UnzIMd35
extra : rebase_source : 4076198dae2bd3cf9fce7ad68f5f5615d98ef5bf
extra : histedit_source : e1b038a2dc1f9a6bca504c207886d67e75636e8f%2C26ac318b25a7abd516fedfbced3ced3359af58c4
This commit is contained in:
Patrick Brosset 2015-10-15 10:14:35 +02:00
parent c77bb5f29b
commit 0b132638a2
3 changed files with 58 additions and 23 deletions

View File

@ -459,10 +459,10 @@ function AnimationsTimeline(inspector) {
this.inspector = inspector; this.inspector = inspector;
this.onAnimationStateChanged = this.onAnimationStateChanged.bind(this); this.onAnimationStateChanged = this.onAnimationStateChanged.bind(this);
this.onTimeHeaderMouseDown = this.onTimeHeaderMouseDown.bind(this); this.onScrubberMouseDown = this.onScrubberMouseDown.bind(this);
this.onTimeHeaderMouseUp = this.onTimeHeaderMouseUp.bind(this); this.onScrubberMouseUp = this.onScrubberMouseUp.bind(this);
this.onTimeHeaderMouseOut = this.onTimeHeaderMouseOut.bind(this); this.onScrubberMouseOut = this.onScrubberMouseOut.bind(this);
this.onTimeHeaderMouseMove = this.onTimeHeaderMouseMove.bind(this); this.onScrubberMouseMove = this.onScrubberMouseMove.bind(this);
EventEmitter.decorate(this); EventEmitter.decorate(this);
} }
@ -487,13 +487,21 @@ AnimationsTimeline.prototype = {
} }
}); });
this.scrubberHandleEl = createNode({
parent: this.scrubberEl,
attributes: {
"class": "scrubber-handle"
}
});
this.scrubberHandleEl.addEventListener("mousedown", this.onScrubberMouseDown);
this.timeHeaderEl = createNode({ this.timeHeaderEl = createNode({
parent: this.rootWrapperEl, parent: this.rootWrapperEl,
attributes: { attributes: {
"class": "time-header" "class": "time-header"
} }
}); });
this.timeHeaderEl.addEventListener("mousedown", this.onTimeHeaderMouseDown); this.timeHeaderEl.addEventListener("mousedown", this.onScrubberMouseDown);
this.animationsEl = createNode({ this.animationsEl = createNode({
parent: this.rootWrapperEl, parent: this.rootWrapperEl,
@ -509,7 +517,9 @@ AnimationsTimeline.prototype = {
this.unrender(); this.unrender();
this.timeHeaderEl.removeEventListener("mousedown", this.timeHeaderEl.removeEventListener("mousedown",
this.onTimeHeaderMouseDown); this.onScrubberMouseDown);
this.scrubberHandleEl.removeEventListener("mousedown",
this.onScrubberMouseDown);
this.rootWrapperEl.remove(); this.rootWrapperEl.remove();
this.animations = []; this.animations = [];
@ -518,6 +528,7 @@ AnimationsTimeline.prototype = {
this.timeHeaderEl = null; this.timeHeaderEl = null;
this.animationsEl = null; this.animationsEl = null;
this.scrubberEl = null; this.scrubberEl = null;
this.scrubberHandleEl = null;
this.win = null; this.win = null;
this.inspector = null; this.inspector = null;
}, },
@ -539,18 +550,21 @@ AnimationsTimeline.prototype = {
this.animationsEl.innerHTML = ""; this.animationsEl.innerHTML = "";
}, },
onTimeHeaderMouseDown: function(e) { onScrubberMouseDown: function(e) {
this.moveScrubberTo(e.pageX); this.moveScrubberTo(e.pageX);
this.win.addEventListener("mouseup", this.onTimeHeaderMouseUp); this.win.addEventListener("mouseup", this.onScrubberMouseUp);
this.win.addEventListener("mouseout", this.onTimeHeaderMouseOut); this.win.addEventListener("mouseout", this.onScrubberMouseOut);
this.win.addEventListener("mousemove", this.onTimeHeaderMouseMove); this.win.addEventListener("mousemove", this.onScrubberMouseMove);
// Prevent text selection while dragging.
e.preventDefault();
}, },
onTimeHeaderMouseUp: function() { onScrubberMouseUp: function() {
this.cancelTimeHeaderDragging(); this.cancelTimeHeaderDragging();
}, },
onTimeHeaderMouseOut: function(e) { onScrubberMouseOut: function(e) {
// Check that mouseout happened on the window itself, and if yes, cancel // Check that mouseout happened on the window itself, and if yes, cancel
// the dragging. // the dragging.
if (!this.win.document.contains(e.relatedTarget)) { if (!this.win.document.contains(e.relatedTarget)) {
@ -559,12 +573,12 @@ AnimationsTimeline.prototype = {
}, },
cancelTimeHeaderDragging: function() { cancelTimeHeaderDragging: function() {
this.win.removeEventListener("mouseup", this.onTimeHeaderMouseUp); this.win.removeEventListener("mouseup", this.onScrubberMouseUp);
this.win.removeEventListener("mouseout", this.onTimeHeaderMouseOut); this.win.removeEventListener("mouseout", this.onScrubberMouseOut);
this.win.removeEventListener("mousemove", this.onTimeHeaderMouseMove); this.win.removeEventListener("mousemove", this.onScrubberMouseMove);
}, },
onTimeHeaderMouseMove: function(e) { onScrubberMouseMove: function(e) {
this.moveScrubberTo(e.pageX); this.moveScrubberTo(e.pageX);
}, },

View File

@ -8,23 +8,21 @@
// in the header area. // in the header area.
// Also check that doing so changes the timeline's play/pause button to paused // Also check that doing so changes the timeline's play/pause button to paused
// state. // state.
// Finally, also check that the scrubber can be moved using the scrubber handle.
add_task(function*() { add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html"); yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector(); let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent; let timeline = panel.animationsTimelineComponent;
let win = timeline.win; let {win, timeHeaderEl, scrubberEl, scrubberHandleEl} = timeline;
let timeHeaderEl = timeline.timeHeaderEl;
let scrubberEl = timeline.scrubberEl;
let playTimelineButtonEl = panel.playTimelineButtonEl; let playTimelineButtonEl = panel.playTimelineButtonEl;
ok(!playTimelineButtonEl.classList.contains("paused"), ok(!playTimelineButtonEl.classList.contains("paused"),
"The timeline play button is in its playing state by default"); "The timeline play button is in its playing state by default");
info("Mousedown in the header to move the scrubber"); info("Mousedown in the header to move the scrubber");
yield synthesizeMouseAndWaitForTimelineChange(timeline, 50, 1, "mousedown"); yield synthesizeInHeaderAndWaitForChange(timeline, 50, 1, "mousedown");
let newPos = parseInt(scrubberEl.style.left, 10); let newPos = parseInt(scrubberEl.style.left, 10);
is(newPos, 50, "The scrubber moved on mousedown"); is(newPos, 50, "The scrubber moved on mousedown");
@ -32,7 +30,7 @@ add_task(function*() {
"The timeline play button is in its paused state after mousedown"); "The timeline play button is in its paused state after mousedown");
info("Continue moving the mouse and verify that the scrubber tracks it"); info("Continue moving the mouse and verify that the scrubber tracks it");
yield synthesizeMouseAndWaitForTimelineChange(timeline, 100, 1, "mousemove"); yield synthesizeInHeaderAndWaitForChange(timeline, 100, 1, "mousemove");
newPos = parseInt(scrubberEl.style.left, 10); newPos = parseInt(scrubberEl.style.left, 10);
is(newPos, 100, "The scrubber followed the mouse"); is(newPos, 100, "The scrubber followed the mouse");
@ -44,9 +42,19 @@ add_task(function*() {
EventUtils.synthesizeMouse(timeHeaderEl, 200, 1, {type: "mousemove"}, win); EventUtils.synthesizeMouse(timeHeaderEl, 200, 1, {type: "mousemove"}, win);
newPos = parseInt(scrubberEl.style.left, 10); newPos = parseInt(scrubberEl.style.left, 10);
is(newPos, 100, "The scrubber stopped following the mouse"); is(newPos, 100, "The scrubber stopped following the mouse");
info("Try to drag the scrubber handle and check that the scrubber moves");
let onDataChanged = timeline.once("timeline-data-changed");
EventUtils.synthesizeMouse(scrubberHandleEl, 1, 20, {type: "mousedown"}, win);
EventUtils.synthesizeMouse(timeHeaderEl, 0, 0, {type: "mousemove"}, win);
EventUtils.synthesizeMouse(timeHeaderEl, 0, 0, {type: "mouseup"}, win);
yield onDataChanged;
newPos = parseInt(scrubberEl.style.left, 10);
is(newPos, 0, "The scrubber stopped following the mouse");
}); });
function* synthesizeMouseAndWaitForTimelineChange(timeline, x, y, type) { function* synthesizeInHeaderAndWaitForChange(timeline, x, y, type) {
let onDataChanged = timeline.once("timeline-data-changed"); let onDataChanged = timeline.once("timeline-data-changed");
EventUtils.synthesizeMouse(timeline.timeHeaderEl, x, y, {type}, timeline.win); EventUtils.synthesizeMouse(timeline.timeHeaderEl, x, y, {type}, timeline.win);
yield onDataChanged; yield onDataChanged;

View File

@ -201,6 +201,19 @@ body {
border-right: 5px solid transparent; border-right: 5px solid transparent;
} }
/* The scrubber handle is a transparent element displayed on top of the scrubber
line that allows users to drag it */
.animation-timeline .scrubber .scrubber-handle {
position: absolute;
height: 100%;
top: 0;
/* Make it thick enough for easy dragging */
width: 6px;
right: -3px;
cursor: col-resize;
pointer-events: all;
}
.animation-timeline .time-header { .animation-timeline .time-header {
margin-left: var(--timeline-sidebar-width); margin-left: var(--timeline-sidebar-width);
min-height: var(--toolbar-height); min-height: var(--toolbar-height);