Changes to KineticController to stop it from painting as it scrolls.

Added sidebar snapping.
More debug dumps for the content click redispatch bug.
This commit is contained in:
Roy Frostig 2009-07-27 19:04:22 -07:00
Родитель 0874fc97b3
Коммит 19e225ec8f
2 изменённых файлов: 150 добавлений и 36 удалений

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

@ -95,6 +95,43 @@ function InputHandler() {
}
/**
* The input handler is an arbiter between the Fennec chrome window inputs and any
* registered input modules. It keeps an array of input module objects. Incoming
* input events are wrapped in an EventInfo object and passed down to the input modules
* in the order of the modules array.
*
* Input modules must provide the following interface:
*
* handleEvent(evInfo)
* Entry point by which InputHandler passes wrapped Fennec chrome window events
* to the module.
*
* cancelPending()
* Called by the InputHandler as a hint to the module that it may wish to reset
* whatever state it might have entered by processing events thus far. For
* instance, a module may have grabbed (cf grab()) focus, in which case the
* InputHandler will call cancelPending() on all remaining modules.
*
*
*
* An input module may wish to grab event focus of the InputHandler, which means that it
* wants to process all incoming events for a while. When the InputHandler is grabbed
* by one of its modules, only that module will receive incoming events until it ungrabs
* the InputHandler. No other modules' handleEvent() function will be called while the
* InputHandler is grabbed. Grabs and ungrabs of the InputHandler require an object reference
* corresponding to the grabbing object. That is, a module must call inputHandler.grab(this)
* and .ungrab(this) in order for the calls to succeed. The object given as the argument
* will be that which is given event focus. grab/ungrab may be nested (that is, a module can
* grab as many times as it wants to) provided that they are one-to-one. That is, if a
* module grabs four times, it should be sure to ungrab that many times as well. Lastly,
* an input module may, in ungrabbing, provide an array of queued EventInfo objects, each of
* which will be passed by the InputHandler to each of the subsequent modules ("subsequent"
* as in "next in the ordering within the modules array") via handleEvent(). This can be
* thought of as the module's way of saying "sorry for grabbing focus, here's everything I
* kept you from processing this whole time" to the modules of lower priority. To prevent
* infinite loops, this event queue is only passed to lower-priority modules.
*/
InputHandler.prototype = {
/**
@ -110,7 +147,7 @@ InputHandler.prototype = {
*/
// XXX froystig: grab(null) is supported because the old grab() supported
// it, but I'm not sure why. The comment on that was "grab(null) is
// allowed because of mouseout handling. Feel free to remove if that
// allowed because of mouseout handling". Feel free to remove if that
// is no longer relevant, or remove this comment if it still is.
grab: function grab(grabber) {
if (grabber == null) {
@ -148,7 +185,7 @@ InputHandler.prototype = {
* events all along.
*/
// XXX froystig: ungrab(null) is supported here too because the old ungrab()
// happened to support it (not sure if intentionally --- there was no
// happened to support it (not sure if intentionally; there was no
// comment it), so cf the corresponding comment on grab().
ungrab: function ungrab(grabber, restoreEventInfos) {
if (this._grabber == null && grabber == null) {
@ -242,7 +279,7 @@ function MouseModule(owner) {
this._owner = owner;
this._dragData = new DragData(this, 50, 200);
this._dragger = this._defaultDragger;
this._dragger = null;
this._clicker = null;
this._downUpEvents = [];
@ -253,7 +290,10 @@ function MouseModule(owner) {
this._fastPath = false;
var self = this;
this._kinetic = new KineticController( function (dx, dy) { return self._dragBy(dx, dy); } );
this._kinetic = new KineticController(
function _dragByBound(dx, dy) { return self._dragBy(dx, dy); },
function _dragStopBound() { return self._doDragStop(0, 0, true); }
);
}
@ -284,6 +324,8 @@ MouseModule.prototype = {
_onMouseDown: function _onMouseDown(evInfo) {
this._owner.allowClicks();
if (this._kinetic.isActive())
this._kinetic.end();
// walk up the DOM tree in search of nearest scrollable ancestor. nulls are
// returned if none found.
@ -294,13 +336,8 @@ MouseModule.prototype = {
this._targetScrollInterface = targetScrollInterface;
// fast path: we have no scrollable element nor any element with custom clicker, so
// just let the event bubble on through
//this._fastPath = !targetScrollInterface && !targetClicker;
//if (this._fastPath)
// return;
this._dragger = (targetScrollInterface && targetScrollbox.customDragger) || this._defaultDragger;
this._dragger = (targetScrollInterface) ? (targetScrollbox.customDragger || this._defaultDragger)
: null;
this._clicker = (targetClicker) ? targetClicker.customClicker : null;
evInfo.event.stopPropagation();
@ -309,7 +346,6 @@ MouseModule.prototype = {
this._owner.grab(this);
if (targetScrollInterface) {
this._kinetic.end();
this._doDragStart(evInfo.event.screenX, evInfo.event.screenY);
}
@ -396,18 +432,22 @@ MouseModule.prototype = {
this._dragger.dragStart(this._targetScrollInterface);
},
_doDragStop: function _doDragStop(sX, sY) {
let dragData = this._dragData;
_doDragStop: function _doDragStop(sX, sY, kineticStop) {
if (!kineticStop) { // we're not really done, since now it is
// kinetic's turn to scroll about
let dragData = this._dragData;
let dx = dragData.sX - sX;
let dy = dragData.sY - sY;
let dx = dragData.sX - sX;
let dy = dragData.sY - sY;
dragData.setDragPosition(sX, sY);
this._kinetic.addData(sX, sY);
dragData.reset();
this._kinetic.addData(sX, sY);
this._dragger.dragStop(dx, dy, this._targetScrollInterface);
this._kinetic.start();
this._kinetic.start();
} else { // now we're done, says our secret 3rd argument
this._dragger.dragStop(0, 0, this._targetScrollInterface);
}
},
_doDragMove: function _doDragMove(sX, sY) {
@ -447,12 +487,14 @@ MouseModule.prototype = {
*
*/
_commitAnotherClick: function _commitAnotherClick() {
this._doSingleClick();
/*
if (this._clickTimeout) { // we're waiting for a second click for double
window.clearTimeout(this._clickTimeout);
this._doDoubleClick();
} else {
this._clickTimeout = window.setTimeout(function _clickTimeout(self) { self._doSingleClick(); }, 400, this);
}
}*/
},
/**
@ -667,9 +709,10 @@ DragData.prototype = {
* generated by the kinetic algorithm. It should return true if the
* object was panned, false if there was no movement.
*/
function KineticController(aPanBy) {
function KineticController(aPanBy, aEndCallback) {
this._panBy = aPanBy;
this._timer = null;
this._beforeEnd = aEndCallback;
try {
this._updateInterval = gPrefService.getIntPref("browser.ui.kinetic.updateInterval");
@ -782,6 +825,7 @@ KineticController.prototype = {
},
end: function end() {
this._beforeEnd();
this._reset();
},

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

@ -27,6 +27,7 @@
* Johnathan Nightingale <johnath@mozilla.com>
* Stuart Parmenter <stuart@mozilla.com>
* Taras Glek <tglek@mozilla.com>
* Roy Frostig <rfrostig@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -89,6 +90,7 @@ function debug() {
dump('visibleRect from foo: ' + getVisibleRect().toString() + endl);
dump('batch depth: ' + bv._batchOps.length + endl);
dump('renderpause depth: ' + bv._renderMode + endl);
dump(endl);
@ -233,12 +235,12 @@ function screenToBrowserView(x, y) {
}
// Return the visible rect in terms of the tile container
function getVisibleRect() {
function getVisibleRect(noTranslate) {
let container = document.getElementById("tile-container");
let containerBCR = container.getBoundingClientRect();
let x = Math.round(-containerBCR.left);
let y = Math.round(-containerBCR.top);
let x = Math.round(noTranslate ? 0 : -containerBCR.left);
let y = Math.round(noTranslate ? 0 : -containerBCR.top);
let w = window.innerWidth;
let h = window.innerHeight;
@ -279,12 +281,17 @@ var Browser = {
},
dragStop: function dragStop(dx, dy, scroller) {
let ret = this.dragMove(dx, dy, scroller);
let dx = this.dragMove(dx, dy, scroller, true);
let snapdx = Browser.snapSidebars(scroller);
bv.onAfterVisibleMove(snapdx, 0);
bv.resumeRendering();
return ret;
return (dy != 0) || ((dx + snapdx) != 0);
},
dragMove: function dragMove(dx, dy, scroller) {
dragMove: function dragMove(dx, dy, scroller, doReturnDX) {
bv.onBeforeVisibleMove(dx, dy);
let [x0, y0] = getScrollboxPosition(scroller);
@ -302,7 +309,7 @@ var Browser = {
dump('--> scroll asked for ' + dx + ',' + dy + ' and got ' + realdx + ',' + realdy + '\n');
}
return !(realdx == 0 && realdy == 0);
return (doReturnDX) ? realdx : !(realdx == 0 && realdy == 0);
}
};
@ -378,12 +385,12 @@ var Browser = {
bv.commitBatchOperation();
}
window.addEventListener("resize", resizeHandler, false);
function fullscreenHandler() {
if (!window.fullScreen)
document.getElementById("toolbar-main").setAttribute("fullscreen", "true");
else
document.getElementById("toolbar-main").removeAttribute("fullscreen");
document.getElementById("toolbar-main").removeAttribute("fullscreen");
}
window.addEventListener("fullscreen", fullscreenHandler, false);
@ -837,9 +844,9 @@ var Browser = {
singleClick: function singleClick(cX, cY) {
let browser = browserView.getBrowser();
if (browser) {
dump('singleClick was invoked with ' + cX + ', ' + cY + '\n');
dump('singleClick was invoked with ' + cX + ', ' + cY + '\n');
let [x, y] = transformScreenToBrowser(cX, cY);
dump('dispatching in browser ' + x + ', ' + y + '\n');
dump('dispatching in browser ' + x + ', ' + y + '\n');
dispatchContentClick(browser, x, y);
}
},
@ -850,10 +857,10 @@ var Browser = {
let zoomElement = elementFromPoint(browser, cX2, cY2);
if (zoomElement) {
// TODO actually zoom to and from element
// TODO actually zoom to and from element
//browserView.zoom(this.zoomDir);
//this.zoomDir *= -1;
dump('zooming to/from element: ' + zoomElement + '\n');
//this.zoomDir *= -1;
dump('zooming to/from element: ' + zoomElement + '\n');
}
//let [x, y] = transformScreenToBrowser(cX1, cY1);
@ -863,6 +870,57 @@ var Browser = {
}
}
};
},
/**
* Returns dx of snap
*/
snapSidebars: function snapSidebars(scroller) {
function visibility(bar, visrect) {
try {
let w = bar.width;
let h = bar.height;
bar.restrictTo(visrect); // throws exception if intersection of rects is empty
return [bar.width / w, bar.height / h];
} catch (e) {
return [0, 0];
}
}
let visrect = getVisibleRect(true);
let leftbarCBR = document.getElementById('tabs-container').getBoundingClientRect();
let ritebarCBR = document.getElementById('browser-controls').getBoundingClientRect();
let leftbar = new wsRect(leftbarCBR.left, leftbarCBR.top, leftbarCBR.width, leftbarCBR.height);
let ritebar = new wsRect(ritebarCBR.left, ritebarCBR.top, ritebarCBR.width, ritebarCBR.height);
let leftw = leftbar.width;
let ritew = ritebar.width;
let [leftvis, ] = visibility(leftbar, visrect);
let [ritevis, ] = visibility(ritebar, visrect);
let snappedX = 0;
if (leftvis != 0 && leftvis != 1) {
if (leftvis >= 0.6666)
snappedX = -((1 - leftvis) * leftw);
else
snappedX = leftvis * leftw;
snappedX = Math.round(snappedX);
scroller.scrollBy(snappedX, 0);
}
else if (ritevis != 0 && ritevis != 1) {
if (ritevis >= 0.6666)
snappedX = (1 - ritevis) * ritew;
else
snappedX = -ritevis * ritew;
snappedX = Math.round(snappedX);
scroller.scrollBy(snappedX, 0);
}
return snappedX;
}
};
@ -1677,9 +1735,16 @@ Tab.prototype = {
},
load: function(uri) {
dump('browser 3: ' + this._browser.contentWindow + '\n');
dump("cb set src\n");
this._browser.setAttribute("src", uri);
dump("cb end src\n");
dump('browser 4: ' + this._browser.contentWindow + '\n');
try {
dump('QIs to: ' + this._browser.contentWindow.QueryInterface(Ci.nsIDOMChromeWindow) + '\n');
} catch (e) {
dump('failed to QI\n');
}
},
create: function() {
@ -1697,6 +1762,7 @@ Tab.prototype = {
},
_createBrowser: function() {
dump('for the break\n');
if (this._browser)
throw "Browser already exists";
@ -1704,6 +1770,8 @@ Tab.prototype = {
let scaledHeight = kDefaultBrowserWidth * (window.innerHeight / window.innerWidth);
let browser = this._browser = document.createElement("browser");
dump('browser 1: ' + browser.contentWindow + '\n');
browser.setAttribute("style", "overflow: -moz-hidden-unscrollable; visibility: hidden; width: " + kDefaultBrowserWidth + "px; height: " + scaledHeight + "px;");
browser.setAttribute("type", "content");
@ -1720,6 +1788,8 @@ Tab.prototype = {
// stop about:blank from loading
browser.stop();
dump('browser 2: ' + browser.contentWindow + '\n');
// Attach a separate progress listener to the browser
this._listener = new ProgressController(this);
browser.addProgressListener(this._listener);