mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
636 lines
22 KiB
XML
636 lines
22 KiB
XML
<?xml version="1.0"?>
|
|
|
|
<!DOCTYPE bindings [
|
|
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
|
%globalDTD;
|
|
]>
|
|
|
|
<bindings id="arrowscrollboxBindings"
|
|
xmlns="http://www.mozilla.org/xbl"
|
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
xmlns:xbl="http://www.mozilla.org/xbl">
|
|
|
|
<binding id="scrollbox-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
|
|
<resources>
|
|
<stylesheet src="chrome://global/skin/scrollbox.css"/>
|
|
</resources>
|
|
</binding>
|
|
|
|
<binding id="scrollbox" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
|
|
<content>
|
|
<xul:box class="box-inherit scrollbox-innerbox" xbl:inherits="orient,align,pack,dir" flex="1">
|
|
<children/>
|
|
</xul:box>
|
|
</content>
|
|
</binding>
|
|
|
|
<binding id="arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
|
|
<content>
|
|
<xul:autorepeatbutton class="autorepeatbutton-up"
|
|
anonid="scrollbutton-up"
|
|
collapsed="true"
|
|
xbl:inherits="orient"
|
|
oncommand="_autorepeatbuttonScroll(event);"/>
|
|
<xul:scrollbox xbl:inherits="orient,align,pack,dir" flex="1" anonid="scrollbox">
|
|
<children/>
|
|
</xul:scrollbox>
|
|
<xul:autorepeatbutton class="autorepeatbutton-down"
|
|
anonid="scrollbutton-down"
|
|
collapsed="true"
|
|
xbl:inherits="orient"
|
|
oncommand="_autorepeatbuttonScroll(event);"/>
|
|
</content>
|
|
|
|
<implementation>
|
|
<destructor><![CDATA[
|
|
this._stopSmoothScroll();
|
|
]]></destructor>
|
|
|
|
<field name="_scrollbox">
|
|
document.getAnonymousElementByAttribute(this, "anonid", "scrollbox");
|
|
</field>
|
|
<field name="_scrollButtonUp">
|
|
document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-up");
|
|
</field>
|
|
<field name="_scrollButtonDown">
|
|
document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-down");
|
|
</field>
|
|
|
|
<field name="__prefBranch">null</field>
|
|
<property name="_prefBranch" readonly="true">
|
|
<getter><![CDATA[
|
|
if (this.__prefBranch === null) {
|
|
this.__prefBranch = Components.classes['@mozilla.org/preferences-service;1']
|
|
.getService(Components.interfaces.nsIPrefBranch2);
|
|
}
|
|
return this.__prefBranch;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<field name="_scrollIncrement">null</field>
|
|
<property name="scrollIncrement" readonly="true">
|
|
<getter><![CDATA[
|
|
if (this._scrollIncrement === null) {
|
|
try {
|
|
this._scrollIncrement = this._prefBranch
|
|
.getIntPref("toolkit.scrollbox.scrollIncrement");
|
|
}
|
|
catch (ex) {
|
|
this._scrollIncrement = 20;
|
|
}
|
|
}
|
|
return this._scrollIncrement;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<field name="_smoothScroll">null</field>
|
|
<property name="smoothScroll">
|
|
<getter><![CDATA[
|
|
if (this._smoothScroll === null) {
|
|
if (this.hasAttribute("smoothscroll")) {
|
|
this._smoothScroll = (this.getAttribute("smoothscroll") == "true");
|
|
} else {
|
|
try {
|
|
this._smoothScroll = this._prefBranch
|
|
.getBoolPref("toolkit.scrollbox.smoothScroll");
|
|
}
|
|
catch (ex) {
|
|
this._smoothScroll = true;
|
|
}
|
|
}
|
|
}
|
|
return this._smoothScroll;
|
|
]]></getter>
|
|
<setter><![CDATA[
|
|
this._smoothScroll = val;
|
|
return val;
|
|
]]></setter>
|
|
</property>
|
|
|
|
<field name="_scrollBoxObject">null</field>
|
|
<property name="scrollBoxObject" readonly="true">
|
|
<getter><![CDATA[
|
|
if (!this._scrollBoxObject) {
|
|
this._scrollBoxObject =
|
|
this._scrollbox.boxObject
|
|
.QueryInterface(Components.interfaces.nsIScrollBoxObject);
|
|
}
|
|
return this._scrollBoxObject;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<field name="_isLTRScrollbox">
|
|
document.defaultView.getComputedStyle(this._scrollbox, "").direction == "ltr";
|
|
</field>
|
|
|
|
<method name="ensureElementIsVisible">
|
|
<parameter name="element"/>
|
|
<body><![CDATA[
|
|
if (!this.smoothScroll || this.getAttribute("orient") == "vertical") {
|
|
this.scrollBoxObject.ensureElementIsVisible(element);
|
|
return;
|
|
}
|
|
|
|
var rect = this._scrollbox.getBoundingClientRect();
|
|
var containerStart = rect.left;
|
|
var containerEnd = rect.right;
|
|
rect = element.getBoundingClientRect();
|
|
var elementStart = rect.left;
|
|
var elementEnd = rect.right;
|
|
var amountToScroll;
|
|
|
|
if (elementStart < containerStart) {
|
|
amountToScroll = elementStart - containerStart;
|
|
} else if (containerEnd < elementEnd) {
|
|
amountToScroll = elementEnd - containerEnd;
|
|
} else if (this._isScrolling) {
|
|
// decelerate if a currently-visible element is selected during the scroll
|
|
const STOP_DISTANCE = 15;
|
|
if (this._isScrolling == -1 && elementStart - STOP_DISTANCE < containerStart)
|
|
amountToScroll = elementStart - containerStart;
|
|
else if (this._isScrolling == 1 && containerEnd - STOP_DISTANCE < elementEnd)
|
|
amountToScroll = elementEnd - containerEnd;
|
|
else
|
|
amountToScroll = this._isScrolling * STOP_DISTANCE;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
this._smoothScrollByPixels(amountToScroll);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_smoothScrollByPixels">
|
|
<parameter name="amountToScroll"/>
|
|
<body><![CDATA[
|
|
this._stopSmoothScroll();
|
|
|
|
// Positive amountToScroll makes us scroll right (elements fly left), negative scrolls left.
|
|
var round;
|
|
if (amountToScroll < 0) {
|
|
this._isScrolling = -1;
|
|
round = Math.floor;
|
|
} else {
|
|
this._isScrolling = 1;
|
|
round = Math.ceil;
|
|
}
|
|
|
|
const FRAME_LENGTH = 60;
|
|
|
|
function processFrame(self, scrollAmounts, off) {
|
|
var distance = scrollAmounts.shift();
|
|
|
|
// Skip frames if we aren't getting the desired frame rate.
|
|
if (off > 0) {
|
|
for (var i = Math.round(off / FRAME_LENGTH); i > 0; i--)
|
|
distance += scrollAmounts.shift() || 0;
|
|
}
|
|
|
|
self.scrollBoxObject.scrollBy(distance, 0);
|
|
if (!scrollAmounts.length)
|
|
self._stopSmoothScroll();
|
|
}
|
|
|
|
// amountToScroll: total distance to scroll
|
|
// scrollAmount: distance to move during the particular effect frame (60ms)
|
|
var scrollAmount, scrollAmounts = [];
|
|
if (amountToScroll > 2 || amountToScroll < -2) {
|
|
scrollAmount = round(amountToScroll * 0.2);
|
|
scrollAmounts.push(scrollAmount, scrollAmount, scrollAmount);
|
|
amountToScroll -= 3 * scrollAmount;
|
|
}
|
|
while (this._isScrolling < 0 && amountToScroll < 0 ||
|
|
this._isScrolling > 0 && amountToScroll > 0) {
|
|
amountToScroll -= (scrollAmount = round(amountToScroll * 0.5));
|
|
scrollAmounts.push(scrollAmount);
|
|
}
|
|
this._smoothScrollTimer = setInterval(processFrame, FRAME_LENGTH, this, scrollAmounts);
|
|
processFrame(this, scrollAmounts, 0);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="scrollByIndex">
|
|
<parameter name="index"/>
|
|
<body><![CDATA[
|
|
if (index == 0)
|
|
return;
|
|
if (this.getAttribute("orient") == "vertical") {
|
|
this.scrollBoxObject.scrollByIndex(index);
|
|
return;
|
|
}
|
|
|
|
var x;
|
|
if (index > 0)
|
|
x = this._scrollbox.getBoundingClientRect().right + 1;
|
|
else
|
|
x = this._scrollbox.getBoundingClientRect().left - 1;
|
|
var nextElement = this._elementFromPoint(x);
|
|
if (!nextElement)
|
|
return;
|
|
|
|
var targetElement;
|
|
if (!this._isLTRScrollbox)
|
|
index *= -1;
|
|
|
|
while (index < 0 && nextElement) {
|
|
targetElement = nextElement;
|
|
nextElement = nextElement.previousSibling;
|
|
index++;
|
|
}
|
|
while (index > 0 && nextElement) {
|
|
targetElement = nextElement;
|
|
nextElement = nextElement.nextSibling;
|
|
index--;
|
|
}
|
|
|
|
this.ensureElementIsVisible(targetElement);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_elementFromPoint">
|
|
<parameter name="aX"/>
|
|
<body><![CDATA[
|
|
var elements = this.hasChildNodes() ?
|
|
this.childNodes :
|
|
document.getBindingParent(this).childNodes;
|
|
if (!this._isLTRScrollbox) {
|
|
elements = Array.slice(elements);
|
|
elements.reverse();
|
|
}
|
|
var low = 0;
|
|
var high = elements.length - 1;
|
|
|
|
while (low <= high) {
|
|
var mid = Math.floor((low + high) / 2);
|
|
var element = elements[mid];
|
|
var rect = element.getBoundingClientRect();
|
|
if (rect.left > aX)
|
|
high = mid - 1;
|
|
else if (rect.right < aX)
|
|
low = mid + 1;
|
|
else
|
|
return element;
|
|
}
|
|
|
|
return null;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_autorepeatbuttonScroll">
|
|
<parameter name="event"/>
|
|
<body><![CDATA[
|
|
var dir = event.originalTarget == this._scrollButtonUp ? -1 : 1;
|
|
if (this.getAttribute("orient") == "horizontal" && !this._isLTRScrollbox)
|
|
dir *= -1;
|
|
|
|
this.scrollByPixels(this.scrollIncrement * dir);
|
|
|
|
event.stopPropagation();
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="scrollByPixels">
|
|
<parameter name="px"/>
|
|
<body><![CDATA[
|
|
if (this.getAttribute("orient") == "horizontal")
|
|
this.scrollBoxObject.scrollBy(px, 0);
|
|
else
|
|
this.scrollBoxObject.scrollBy(0, px);
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- 0: idle
|
|
1: scrolling right
|
|
-1: scrolling left -->
|
|
<field name="_isScrolling">0</field>
|
|
<field name="_smoothScrollTimer">0</field>
|
|
|
|
<method name="_stopSmoothScroll">
|
|
<body><![CDATA[
|
|
if (this._isScrolling) {
|
|
clearInterval(this._smoothScrollTimer);
|
|
this._isScrolling = 0;
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_updateScrollButtonsDisabledState">
|
|
<body><![CDATA[
|
|
var disableUpButton = false;
|
|
var disableDownButton = false;
|
|
|
|
if (this.getAttribute("orient") == "horizontal") {
|
|
var width = {};
|
|
this.scrollBoxObject.getScrolledSize(width, {});
|
|
var xPos = {};
|
|
this.scrollBoxObject.getPosition(xPos, {});
|
|
if (xPos.value == 0) {
|
|
// In the RTL case, this means the _last_ element in the
|
|
// scrollbox is visible
|
|
if (this._isLTRScrollbox)
|
|
disableUpButton = true;
|
|
else
|
|
disableDownButton = true;
|
|
}
|
|
else if (this._scrollbox.boxObject.width + xPos.value == width.value) {
|
|
// In the RTL case, this means the _first_ element in the
|
|
// scrollbox is visible
|
|
if (this._isLTRScrollbox)
|
|
disableDownButton = true;
|
|
else
|
|
disableUpButton = true;
|
|
}
|
|
}
|
|
else { // vertical scrollbox
|
|
var height = {};
|
|
this.scrollBoxObject.getScrolledSize({}, height);
|
|
var yPos = {};
|
|
this.scrollBoxObject.getPosition({}, yPos);
|
|
if (yPos.value == 0)
|
|
disableUpButton = true;
|
|
else if (this._scrollbox.boxObject.height + yPos.value == height.value)
|
|
disableDownButton = true;
|
|
}
|
|
|
|
if (this._scrollButtonUp.disabled != disableUpButton ||
|
|
this._scrollButtonDown.disabled != disableDownButton) {
|
|
this._scrollButtonUp.disabled = disableUpButton;
|
|
this._scrollButtonDown.disabled = disableDownButton;
|
|
|
|
var event = document.createEvent("Events");
|
|
event.initEvent("UpdatedScrollButtonsDisabledState", true, false);
|
|
this.dispatchEvent(event);
|
|
}
|
|
]]></body>
|
|
</method>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="DOMMouseScroll"><![CDATA[
|
|
// prevent horizontal scrolling from scrolling a vertical scrollbox
|
|
if (event.axis == event.HORIZONTAL_AXIS &&
|
|
this.getAttribute("orient") != "horizontal")
|
|
return;
|
|
// We allow vertical scrolling to scroll a horizontal scrollbox
|
|
// because many users have a vertical scroll wheel but no
|
|
// horizontal support.
|
|
|
|
this.scrollByIndex(event.detail);
|
|
event.stopPropagation();
|
|
]]></handler>
|
|
|
|
<handler event="underflow"><![CDATA[
|
|
// filter underflow events which were dispatched on nested scrollboxes
|
|
if (event.target != this)
|
|
return;
|
|
|
|
// Ignore events that doesn't match our orientation.
|
|
// Scrollport event orientation:
|
|
// 0: vertical
|
|
// 1: horizontal
|
|
// 2: both
|
|
if (this.getAttribute("orient") == "horizontal") {
|
|
if (event.detail == 0) {
|
|
return;
|
|
}
|
|
}
|
|
else { // vertical scrollbox
|
|
if (event.detail == 1) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this._scrollButtonUp.collapsed = true;
|
|
this._scrollButtonDown.collapsed = true;
|
|
try {
|
|
// See bug 341047 and comments in overflow handler as to why
|
|
// try..catch is needed here
|
|
var childNodes = document.getAnonymousNodes(this._scrollbox);
|
|
if (childNodes && childNodes.length)
|
|
this.scrollBoxObject.ensureElementIsVisible(childNodes[0]);
|
|
}
|
|
catch(e) {
|
|
this._scrollButtonUp.collapsed = false;
|
|
this._scrollButtonDown.collapsed = false;
|
|
}
|
|
]]></handler>
|
|
|
|
<handler event="overflow"><![CDATA[
|
|
// filter underflow events which were dispatched on nested scrollboxes
|
|
if (event.target != this)
|
|
return;
|
|
|
|
// Ignore events that doesn't match our orientation.
|
|
// Scrollport event orientation:
|
|
// 0: vertical
|
|
// 1: horizontal
|
|
// 2: both
|
|
if (this.getAttribute("orient") == "horizontal") {
|
|
if (event.detail == 0) {
|
|
return;
|
|
}
|
|
}
|
|
else { // vertical scrollbox
|
|
if (event.detail == 1) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this._scrollButtonUp.collapsed = false;
|
|
this._scrollButtonDown.collapsed = false;
|
|
try {
|
|
// See bug 341047, the overflow event is dispatched when the
|
|
// scrollbox already is mostly destroyed. This causes some code in
|
|
// _updateScrollButtonsDisabledState() to throw an error. It also
|
|
// means that the scrollbarbuttons were uncollapsed when that should
|
|
// not be happening, because the whole overflow event should not be
|
|
// happening in that case.
|
|
this._updateScrollButtonsDisabledState();
|
|
}
|
|
catch(e) {
|
|
this._scrollButtonUp.collapsed = true;
|
|
this._scrollButtonDown.collapsed = true;
|
|
}
|
|
]]></handler>
|
|
|
|
<handler event="scroll" action="this._updateScrollButtonsDisabledState()"/>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="autorepeatbutton" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
|
|
<content repeat="hover" chromedir="&locale.dir;">
|
|
<xul:image class="autorepeatbutton-icon"/>
|
|
</content>
|
|
</binding>
|
|
|
|
<binding id="arrowscrollbox-clicktoscroll" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox">
|
|
<content>
|
|
<xul:toolbarbutton class="scrollbutton-up" collapsed="true"
|
|
xbl:inherits="orient"
|
|
anonid="scrollbutton-up"
|
|
onclick="_distanceScroll(event);"
|
|
onmousedown="_startScroll(-1);"
|
|
onmouseover="_continueScroll(-1);"
|
|
onmouseup="_stopScroll();"
|
|
onmouseout="_pauseScroll();"
|
|
chromedir="&locale.dir;"/>
|
|
<xul:scrollbox xbl:inherits="orient,align,pack,dir" flex="1" anonid="scrollbox">
|
|
<children/>
|
|
</xul:scrollbox>
|
|
<xul:toolbarbutton class="scrollbutton-down" collapsed="true"
|
|
xbl:inherits="orient"
|
|
anonid="scrollbutton-down"
|
|
onclick="_distanceScroll(event);"
|
|
onmousedown="_startScroll(1);"
|
|
onmouseover="_continueScroll(1);"
|
|
onmouseup="_stopScroll();"
|
|
onmouseout="_pauseScroll();"
|
|
chromedir="&locale.dir;"/>
|
|
</content>
|
|
<implementation implements="nsITimerCallback, nsIDOMEventListener">
|
|
<constructor><![CDATA[
|
|
try {
|
|
this._scrollDelay = this._prefBranch
|
|
.getIntPref("toolkit.scrollbox.clickToScroll.scrollDelay");
|
|
}
|
|
catch (ex) {
|
|
}
|
|
]]></constructor>
|
|
|
|
<destructor><![CDATA[
|
|
// Release timer to avoid reference cycles.
|
|
if (this._scrollTimer) {
|
|
this._scrollTimer.cancel();
|
|
this._scrollTimer = null;
|
|
}
|
|
]]></destructor>
|
|
|
|
<field name="_scrollIndex">0</field>
|
|
<field name="_scrollDelay">150</field>
|
|
|
|
<method name="notify">
|
|
<parameter name="aTimer"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (!document)
|
|
aTimer.cancel();
|
|
|
|
if (this.smoothScroll)
|
|
this.scrollByPixels(25 * this._scrollIndex);
|
|
else
|
|
this.scrollBoxObject.scrollByIndex(this._scrollIndex);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_startScroll">
|
|
<parameter name="index"/>
|
|
<body><![CDATA[
|
|
if (this.getAttribute("orient") == "horizontal" && !this._isLTRScrollbox)
|
|
index *= -1;
|
|
this._scrollIndex = index;
|
|
var scrollDelay = this.smoothScroll ? 60 : this._scrollDelay;
|
|
if (!this._scrollTimer)
|
|
this._scrollTimer =
|
|
Components.classes["@mozilla.org/timer;1"]
|
|
.createInstance(Components.interfaces.nsITimer);
|
|
else
|
|
this._scrollTimer.cancel();
|
|
|
|
this._scrollTimer.initWithCallback(this, scrollDelay,
|
|
this._scrollTimer.TYPE_REPEATING_SLACK);
|
|
this.notify(this._scrollTimer);
|
|
this._mousedown = true;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_stopScroll">
|
|
<body><![CDATA[
|
|
if (this._scrollTimer)
|
|
this._scrollTimer.cancel();
|
|
this._mousedown = false;
|
|
if (!this._scrollIndex || !this.smoothScroll)
|
|
return;
|
|
|
|
this.scrollByIndex(this._scrollIndex);
|
|
this._scrollIndex = 0;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_pauseScroll">
|
|
<body><![CDATA[
|
|
if (this._mousedown) {
|
|
this._stopScroll();
|
|
this._mousedown = true;
|
|
document.addEventListener("mouseup", this, false);
|
|
document.addEventListener("blur", this, true);
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_continueScroll">
|
|
<parameter name="index"/>
|
|
<body><![CDATA[
|
|
if (this._mousedown)
|
|
this._startScroll(index);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="handleEvent">
|
|
<parameter name="aEvent"/>
|
|
<body><![CDATA[
|
|
if (aEvent.type == "mouseup" ||
|
|
aEvent.type == "blur" && aEvent.target == document) {
|
|
this._mousedown = false;
|
|
document.removeEventListener("mouseup", this, false);
|
|
document.removeEventListener("blur", this, true);
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_distanceScroll">
|
|
<parameter name="aEvent"/>
|
|
<body><![CDATA[
|
|
if (this.getAttribute("orient") == "vertical" ||
|
|
aEvent.detail < 2 || aEvent.detail > 3)
|
|
return;
|
|
|
|
var scrollLeft = (aEvent.originalTarget == this._scrollButtonUp);
|
|
if (!this._isLTRScrollbox)
|
|
scrollLeft = !scrollLeft;
|
|
var targetElement;
|
|
|
|
if (aEvent.detail == 2) {
|
|
// scroll by the width of the scrollbox; make sure that the next
|
|
// partly-hidden element will become fully visible.
|
|
var rect = this._scrollbox.getBoundingClientRect();
|
|
var x;
|
|
if (scrollLeft)
|
|
x = rect.left - (rect.right - rect.left);
|
|
else
|
|
x = rect.right + (rect.right - rect.left);
|
|
targetElement = this._elementFromPoint(x);
|
|
|
|
if (targetElement)
|
|
targetElement = scrollLeft ?
|
|
targetElement.nextSibling :
|
|
targetElement.previousSibling;
|
|
}
|
|
|
|
if (!targetElement) {
|
|
// scroll to the first resp. last element
|
|
var container = this.hasChildNodes() ? this : document.getBindingParent(this);
|
|
targetElement = (this._isLTRScrollbox ? scrollLeft : !scrollLeft) ?
|
|
container.firstChild :
|
|
container.lastChild;
|
|
}
|
|
|
|
this.ensureElementIsVisible(targetElement);
|
|
]]></body>
|
|
</method>
|
|
|
|
</implementation>
|
|
</binding>
|
|
</bindings>
|