mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 436059: initial 'zoom to element' support, r=stuart
This commit is contained in:
parent
6cfb3be360
commit
df3721ca7a
@ -37,20 +37,16 @@
|
||||
this._stack.addEventListener("dblclick", this.stackEventHandler, true);
|
||||
this._stack.addEventListener("DOMMouseScroll", this.stackEventHandler, true);
|
||||
|
||||
this._scrollStartTimeout = -1;
|
||||
this._dragStartTimeout = -1;
|
||||
</constructor>
|
||||
|
||||
<field name="dragData">
|
||||
({
|
||||
dragging: false,
|
||||
offX: 0,
|
||||
offY: 0,
|
||||
dragX: 0,
|
||||
dragY: 0,
|
||||
sX: 0,
|
||||
sY: 0,
|
||||
scrollableWidth: 0,
|
||||
scrollableHeight: 0,
|
||||
canvasH: 0,
|
||||
canvasW: 0,
|
||||
pageX: 0,
|
||||
pageY: 0
|
||||
})
|
||||
@ -76,6 +72,7 @@
|
||||
if (aLocationChanged) {
|
||||
this.dragData.pageX = 0;
|
||||
this.dragData.pageY = 0;
|
||||
this._zoomLevel = 1;
|
||||
}
|
||||
|
||||
if (this._updateTimeout)
|
||||
@ -104,11 +101,10 @@
|
||||
|
||||
ctx.clearRect(0,0,w,h);
|
||||
|
||||
//dump("x, y: " + this.dragData.pageX + "," + this.dragData.pageY + "\n");
|
||||
ctx.save();
|
||||
ctx.scale(this._zoomLevel, this._zoomLevel);
|
||||
ctx.drawWindow(this.browser.contentWindow,
|
||||
-this.dragData.pageX / this._zoomLevel, -this.dragData.pageY / this._zoomLevel,
|
||||
ctx.drawWindow(this.browser.contentWindow,
|
||||
this.dragData.pageX, this.dragData.pageY,
|
||||
w / this._zoomLevel, h / this._zoomLevel,
|
||||
"white");
|
||||
ctx.restore();
|
||||
@ -117,11 +113,10 @@
|
||||
|
||||
<method name="_updateCanvasPosition">
|
||||
<body><![CDATA[
|
||||
//dump("setting left/top: " + this.dragData.offX + "/" + this.dragData.offY + "\n");
|
||||
this._canvas.style.marginLeft = this.dragData.offX + "px";
|
||||
this._canvas.style.marginRight = -this.dragData.offX + "px";
|
||||
this._canvas.style.marginTop = this.dragData.offY + "px";
|
||||
this._canvas.style.marginBottom = -this.dragData.offY + "px";
|
||||
this._canvas.style.marginLeft = this.dragData.dragX + "px";
|
||||
this._canvas.style.marginRight = -this.dragData.dragX + "px";
|
||||
this._canvas.style.marginTop = this.dragData.dragY + "px";
|
||||
this._canvas.style.marginBottom = -this.dragData.dragY + "px";
|
||||
|
||||
//window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils).redraw();
|
||||
]]></body>
|
||||
@ -149,81 +144,197 @@
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
/**
|
||||
* Retrieve the content element for a given point (relative to the top
|
||||
* left corner of the browser window).
|
||||
*/
|
||||
<method name="elementFromPoint">
|
||||
<parameter name="aX"/>
|
||||
<parameter name="aY"/>
|
||||
<body><![CDATA[
|
||||
var cdoc = this.browser.contentDocument;
|
||||
|
||||
// Need to adjust for the toolbar height, etc.
|
||||
var browserTop = this.browser.getBoundingClientRect().top;
|
||||
|
||||
return cdoc.elementFromPoint(aX, aY - browserTop);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="zoomToElement">
|
||||
<parameter name="aElement"/>
|
||||
<body><![CDATA[
|
||||
const margin = 0;
|
||||
|
||||
// scale to the element's width
|
||||
var elRect = this._getPagePosition(aElement);
|
||||
this._zoomLevel = Math.max((this.browser.boxObject.width) / (elRect.width + (2 * margin)),
|
||||
2);
|
||||
|
||||
// pan to the element
|
||||
this._panTo(Math.max(elRect.x - margin, 0),
|
||||
Math.max(0, elRect.y - margin));
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_getPagePosition">
|
||||
<parameter name="aElement"/>
|
||||
<body><![CDATA[
|
||||
var r = aElement.getBoundingClientRect();
|
||||
retVal = {
|
||||
width: r.right - r.left,
|
||||
height: r.bottom - r.top,
|
||||
x: r.left,
|
||||
y: r.top
|
||||
};
|
||||
|
||||
return retVal;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_redispatchMouseEvent">
|
||||
<parameter name="aEvent"/>
|
||||
<parameter name="aType"/>
|
||||
<body><![CDATA[
|
||||
//return;
|
||||
var cwin = this.browser.contentWindow;
|
||||
|
||||
if (!(aEvent instanceof MouseEvent)) {
|
||||
Components.utils.reportError("_redispatchMouseEvent called with a non-mouse event");
|
||||
return;
|
||||
}
|
||||
|
||||
// Scroll the browser so that the event is targeted properly
|
||||
cwin.scrollTo(-this.dragData.pageX / this._zoomLevel, -this.dragData.pageY / this._zoomLevel);
|
||||
var cwin = this.browser.contentWindow;
|
||||
cwin.scrollTo(this.dragData.pageX, this.dragData.pageY);
|
||||
|
||||
// Might not have been able to scroll all the way if we're zoomed in,
|
||||
// need to account for that difference.
|
||||
var pageOffsetX = this.dragData.pageX - cwin.scrollX;
|
||||
var pageOffsetY = this.dragData.pageY - cwin.scrollY;
|
||||
|
||||
// Need to adjust for the toolbar height, etc.
|
||||
var browserTop = this.browser.getBoundingClientRect().top;
|
||||
var clickOffsetX = aEvent.clientX / this._zoomLevel;
|
||||
var clickOffsetY = (aEvent.clientY - browserTop) / this._zoomLevel;
|
||||
|
||||
var cwu = cwin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
// Need to adjust for the toolbar height, etc.
|
||||
var browserTop = this.browser.getBoundingClientRect().top;
|
||||
|
||||
cwu.sendMouseEvent(aType || aEvent.type,
|
||||
(aEvent.clientX) / this._zoomLevel,
|
||||
(aEvent.clientY - browserTop) / this._zoomLevel,
|
||||
pageOffsetX + clickOffsetX,
|
||||
pageOffsetY + clickOffsetY,
|
||||
aEvent.button || 0,
|
||||
aEvent.clickCount || 1,
|
||||
aEvent.detail || 1,
|
||||
0);
|
||||
|
||||
// Reset scroll state
|
||||
cwin.scrollTo(0, 0);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_doPan">
|
||||
<property name="_contentAreaDimensions" readonly="true">
|
||||
<getter>
|
||||
var cdoc = this.browser.contentDocument;
|
||||
|
||||
// These might not exist yet
|
||||
var body = cdoc.body || {};
|
||||
var html = cdoc.documentElement || {};
|
||||
|
||||
if (!(body.scrollWidth || html.scrollWidth))
|
||||
throw "Both body and documentElement have no scrollWidth?";
|
||||
|
||||
return [Math.max(body.scrollWidth, html.scrollWidth),
|
||||
Math.max(body.scrollHeight, html.scrollHeight)];
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<property name="_effectiveCanvasDimensions" readonly="true">
|
||||
<getter><![CDATA[
|
||||
return [this._canvas.width / this._zoomLevel,
|
||||
this._canvas.height / this._zoomLevel];
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
/**
|
||||
* Given a set of page coordinates, constrain them such that they
|
||||
* fit within the rect defined by [0,0] and [x,y], where x and y are
|
||||
* the maximum values that can be used for the canvas' .top and .left
|
||||
* such that it is still within the scrollable area of the page, taking
|
||||
* into account the current zoomLevel.
|
||||
*/
|
||||
<method name="_constrainPanCoords">
|
||||
<parameter name="aX"/>
|
||||
<parameter name="aY"/>
|
||||
<body><![CDATA[
|
||||
var [contentAreaWidth, contentAreaHeight] = this._contentAreaDimensions;
|
||||
var [canvasW, canvasH] = this._effectiveCanvasDimensions;
|
||||
|
||||
var offscreenWidth = contentAreaWidth - canvasW;
|
||||
if (offscreenWidth <= 0) {
|
||||
// Content is narrower than viewport, no need to pan horizontally
|
||||
aX = 0;
|
||||
} else {
|
||||
var newPageX = Math.min(this.dragData.pageX + aX, offscreenWidth);
|
||||
newPageX = Math.max(newPageX, 0);
|
||||
aX = newPageX - this.dragData.pageX;
|
||||
}
|
||||
|
||||
var offscreenHeight = contentAreaHeight - canvasH;
|
||||
if (offscreenHeight <= 0) {
|
||||
// Content is shorter than viewport, no need to pan vertically
|
||||
aY = 0;
|
||||
} else {
|
||||
// min of 0, max of contentAreaHeight - canvasHeight
|
||||
var newPageY = Math.min(this.dragData.pageY + aY, offscreenHeight);
|
||||
newPageY = Math.max(newPageY, 0);
|
||||
aY = newPageY - this.dragData.pageY;
|
||||
}
|
||||
|
||||
return [aX, aY];
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_moveCanvas">
|
||||
<parameter name="aDx"/>
|
||||
<parameter name="aDy"/>
|
||||
<body><![CDATA[
|
||||
// constrain offsets to the actual scrollWidth/scrollHeight
|
||||
var [x, y] = this._constrainPanCoords(aDx, aDy);
|
||||
|
||||
var offscreenWidth = this.dragData.scrollableWidth - this.dragData.canvasW;
|
||||
if (offscreenWidth <= 0) {
|
||||
// Content is narrower than viewport, no need to pan horizontally
|
||||
this.dragData.offX = 0;
|
||||
} else {
|
||||
var newPageX = Math.max(this.dragData.pageX + aDx, -offscreenWidth);
|
||||
newPageX = Math.min(newPageX, 0);
|
||||
var deltaX = newPageX - this.dragData.pageX;
|
||||
this.dragData.offX = deltaX;
|
||||
}
|
||||
|
||||
var offscreenHeight = this.dragData.scrollableHeight - this.dragData.canvasH;
|
||||
if (offscreenHeight <= 0) {
|
||||
// Content is shorter than viewport, no need to pan vertically
|
||||
this.dragData.offY = 0;
|
||||
} else {
|
||||
// min of 0, max of scrollableHeight - canvasHeight
|
||||
var newPageY = Math.max(this.dragData.pageY + aDy, -offscreenHeight);
|
||||
newPageY = Math.min(newPageY, 0);
|
||||
var deltaY = newPageY - this.dragData.pageY;
|
||||
this.dragData.offY = deltaY;
|
||||
}
|
||||
// Canvas needs to move up for content to scroll down
|
||||
this.dragData.dragX = -x;
|
||||
this.dragData.dragY = -y;
|
||||
|
||||
this._updateCanvasPosition();
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- Pans directly to a given X/Y (in page coordinates) -->
|
||||
<method name="_panTo">
|
||||
<parameter name="aX"/>
|
||||
<parameter name="aY"/>
|
||||
<body><![CDATA[
|
||||
[this.dragData.pageX, this.dragData.pageY] = this._constrainPanCoords(aX, aY);
|
||||
|
||||
this._browserToCanvas();
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_dragStartTimer">
|
||||
<body><![CDATA[
|
||||
this.dragData.lastMouseEvent = Date.now() - 10;
|
||||
this.dragData.dragging = true;
|
||||
this._scrollStartTimeout = -1;
|
||||
this._dragStartTimeout = -1;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_endPan">
|
||||
<body><![CDATA[
|
||||
// update the pageX/Y coords
|
||||
this.dragData.pageX += this.dragData.offX;
|
||||
this.dragData.pageY += this.dragData.offY;
|
||||
// dragX/dragY are garanteed to be within the correct bounds, so just
|
||||
// update pageX/pageY directly.
|
||||
this.dragData.pageX -= this.dragData.dragX / this._zoomLevel;
|
||||
this.dragData.pageY -= this.dragData.dragY / this._zoomLevel;
|
||||
|
||||
// relocate the canvas to 0x0 in the window
|
||||
this.dragData.offX = 0;
|
||||
this.dragData.offY = 0;
|
||||
this.dragData.dragX = 0;
|
||||
this.dragData.dragY = 0;
|
||||
|
||||
// update canvas position and draw the canvas at the new location
|
||||
this._updateCanvasPosition();
|
||||
@ -240,7 +351,7 @@
|
||||
|
||||
handleEvent: function seh_handleEvent(aEvent) {
|
||||
if (!aEvent.type in this) {
|
||||
dump("MouseController called with unknown event type " + aEvent.type + "\n");
|
||||
Components.reportError("MouseController called with unknown event type " + aEvent.type + "\n");
|
||||
return;
|
||||
}
|
||||
this[aEvent.type](aEvent);
|
||||
@ -248,37 +359,31 @@
|
||||
|
||||
mousedown: function seh_mousedown(aEvent) {
|
||||
if (aEvent.button != 0)
|
||||
return false;
|
||||
|
||||
return;
|
||||
|
||||
// cancel any pending canvas updates, since we're going to update again
|
||||
if (this._updateTimeout)
|
||||
clearTimeout(this._updateTimeout);
|
||||
|
||||
this.deckbrowser.dragData.canvasW = this.deckbrowser._canvas.width;
|
||||
this.deckbrowser.dragData.canvasH = this.deckbrowser._canvas.height;
|
||||
|
||||
var cdoc = this.deckbrowser.browser.contentDocument;
|
||||
var body = cdoc.body;
|
||||
var html = cdoc.documentElement;
|
||||
this.deckbrowser.dragData.scrollableWidth = Math.max(body.scrollWidth, html.scrollWidth);
|
||||
this.deckbrowser.dragData.scrollableWidth *= this.deckbrowser._zoomLevel;
|
||||
this.deckbrowser.dragData.scrollableHeight = Math.max(body.scrollHeight, html.scrollHeight);
|
||||
this.deckbrowser.dragData.scrollableHeight *= this.deckbrowser._zoomLevel;
|
||||
var zoomLevel = this.deckbrowser._zoomLevel;
|
||||
var dragData = this.deckbrowser.dragData;
|
||||
|
||||
// The start of the current portion drag
|
||||
this.deckbrowser.dragData.sX = aEvent.screenX;
|
||||
this.deckbrowser.dragData.sY = aEvent.screenY;
|
||||
dragData.sX = aEvent.screenX;
|
||||
dragData.sY = aEvent.screenY;
|
||||
|
||||
// The total delta between current mouse position and sX/sY
|
||||
this.deckbrowser.dragData.offX = 0;
|
||||
this.deckbrowser.dragData.offY = 0;
|
||||
dragData.dragX = 0;
|
||||
dragData.dragY = 0;
|
||||
|
||||
//this.deckbrowser._updateCanvasPosition();
|
||||
|
||||
var self = this.deckbrowser;
|
||||
this.deckbrowser._scrollStartTimeout = setTimeout(function () {
|
||||
this.deckbrowser._dragStartTimeout = setTimeout(function () {
|
||||
self._dragStartTimer();
|
||||
}, 200);
|
||||
|
||||
this._lastMouseDown = aEvent;
|
||||
},
|
||||
|
||||
mouseup: function seh_mouseup(aEvent) {
|
||||
@ -287,11 +392,12 @@
|
||||
} else if (aEvent.originalTarget == this.deckbrowser._canvas) {
|
||||
// Mouseup on canvas that isn't releasing from a drag
|
||||
// cancel scrollStart timer
|
||||
clearTimeout(this.deckbrowser._scrollStartTimeout);
|
||||
this.deckbrowser._scrollStartTimeout = -1;
|
||||
clearTimeout(this.deckbrowser._dragStartTimeout);
|
||||
this.deckbrowser._dragStartTimeout = -1;
|
||||
|
||||
// send mousedown & mouseup
|
||||
this.deckbrowser._redispatchMouseEvent(aEvent, "mousedown");
|
||||
this.deckbrowser._redispatchMouseEvent(this._lastMouseDown);
|
||||
this._lastMouseDown = null;
|
||||
this.deckbrowser._redispatchMouseEvent(aEvent);
|
||||
}
|
||||
},
|
||||
@ -300,20 +406,20 @@
|
||||
if (!this.deckbrowser.dragData.dragging) {
|
||||
// If we've moved more than N pixels lets go ahead and assume we're dragging
|
||||
// and not wait for the timeout to complete.
|
||||
if (this.deckbrowser._scrollStartTimeout != -1 &&
|
||||
if (this.deckbrowser._dragStartTimeout != -1 &&
|
||||
(Math.abs(this.deckbrowser.dragData.sX - aEvent.screenX) > 10 ||
|
||||
Math.abs(this.deckbrowser.dragData.sY - aEvent.screenY) > 10)) {
|
||||
clearTimeout(this.deckbrowser._scrollStartTimeout);
|
||||
clearTimeout(this.deckbrowser._dragStartTimeout);
|
||||
this.deckbrowser._dragStartTimer();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var dx = aEvent.screenX - this.deckbrowser.dragData.sX;
|
||||
var dy = aEvent.screenY - this.deckbrowser.dragData.sY;
|
||||
var dx = this.deckbrowser.dragData.sX - aEvent.screenX;
|
||||
var dy = this.deckbrowser.dragData.sY - aEvent.screenY;
|
||||
|
||||
this.deckbrowser._doPan(dx, dy);
|
||||
this.deckbrowser._moveCanvas(dx, dy);
|
||||
|
||||
if (Date.now() - this.deckbrowser.dragData.lastMouseEvent < 75) { // FIXME: make this a constant
|
||||
//dump("dropping event\n");
|
||||
@ -323,26 +429,27 @@
|
||||
this.deckbrowser.dragData.lastMouseEvent = Date.now();
|
||||
|
||||
aEvent.preventDefault();
|
||||
return true;
|
||||
return true;
|
||||
},
|
||||
|
||||
|
||||
DOMMouseScroll: function seh_DOMMouseScroll(aEvent) {
|
||||
this.deckbrowser.zoom(aEvent.detail);
|
||||
},
|
||||
|
||||
|
||||
dblclick: function seh_dblclick(aEvent) {
|
||||
//dump("Zooming...\n");
|
||||
var x = aEvent.clientX;
|
||||
var y = aEvent.clientY;
|
||||
var target = aEvent.originalTarget;
|
||||
|
||||
if (this.deckbrowser._zoomed) {
|
||||
this.deckbrowser._zoomLevel = 1;
|
||||
var dragData = this.deckbrowser.dragData;
|
||||
dragData.pageX = dragData.pageY = 0;
|
||||
this.deckbrowser._browserToCanvas();
|
||||
this.deckbrowser._zoomed = false;
|
||||
} else {
|
||||
this.deckbrowser._zoomLevel = 2;
|
||||
var element = this.deckbrowser.elementFromPoint(aEvent.clientX, aEvent.clientY);
|
||||
this.deckbrowser.zoomToElement(element);
|
||||
this.deckbrowser._zoomed = true;
|
||||
}
|
||||
|
||||
this.deckbrowser._browserToCanvas();
|
||||
}
|
||||
});
|
||||
]]>
|
||||
|
Loading…
Reference in New Issue
Block a user