Bug 1359211 - Handle touch-scrolling of XUL <listbox> in JS. r=kats,dao,bz
The APZ scrolling codepath doesn't do the right thing for <listbox>
without special handling, so have it scroll in JS instead, like we
did in
bug 1302736 for <tree>.
MozReview-Commit-ID: LWJCBfhZ3Hc
--- a/dom/webidl/ListBoxObject.webidl
+++ b/dom/webidl/ListBoxObject.webidl
@@ -4,16 +4,17 @@
* 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/.
*/
[NoInterfaceObject]
interface ListBoxObject : BoxObject {
long getRowCount();
+ long getRowHeight();
long getNumberOfVisibleRows();
long getIndexOfFirstVisibleRow();
void ensureIndexIsVisible(long rowIndex);
void scrollToIndex(long rowIndex);
void scrollByLines(long numLines);
Element? getItemAtIndex(long index);
--- a/layout/xul/ListBoxObject.cpp
+++ b/layout/xul/ListBoxObject.cpp
@@ -71,16 +71,26 @@ ListBoxObject::GetRowCount()
nsListBoxBodyFrame* body = GetListBoxBody(true);
if (body) {
return body->GetRowCount();
}
return 0;
}
int32_t
+ListBoxObject::GetRowHeight()
+{
+ nsListBoxBodyFrame* body = GetListBoxBody(true);
+ if (body) {
+ return body->GetRowHeightPixels();
+ }
+ return 0;
+}
+
+int32_t
ListBoxObject::GetNumberOfVisibleRows()
{
nsListBoxBodyFrame* body = GetListBoxBody(true);
if (body) {
return body->GetNumberOfVisibleRows();
}
return 0;
}
--- a/layout/xul/ListBoxObject.h
+++ b/layout/xul/ListBoxObject.h
@@ -28,16 +28,17 @@ public:
virtual nsListBoxBodyFrame* GetListBoxBody(bool aFlush) override;
// nsPIBoxObject
virtual void Clear() override;
virtual void ClearCachedValues() override;
// ListBoxObject.webidl
int32_t GetRowCount();
+ int32_t GetRowHeight();
int32_t GetNumberOfVisibleRows();
int32_t GetIndexOfFirstVisibleRow();
void EnsureIndexIsVisible(int32_t rowIndex);
void ScrollToIndex(int32_t rowIndex);
void ScrollByLines(int32_t numLines);
already_AddRefed<Element> GetItemAtIndex(int32_t index);
int32_t GetIndexOfItem(Element& item);
--- a/layout/xul/nsListBoxBodyFrame.cpp
+++ b/layout/xul/nsListBoxBodyFrame.cpp
@@ -642,16 +642,22 @@ int32_t
nsListBoxBodyFrame::GetRowCount()
{
if (mRowCount < 0)
ComputeTotalRowCount();
return mRowCount;
}
int32_t
+nsListBoxBodyFrame::GetRowHeightPixels() const
+{
+ return nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
+}
+
+int32_t
nsListBoxBodyFrame::GetFixedRowSize()
{
nsresult dummy;
nsAutoString rows;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
if (!rows.IsEmpty())
return rows.ToInteger(&dummy);
--- a/layout/xul/nsListBoxBodyFrame.h
+++ b/layout/xul/nsListBoxBodyFrame.h
@@ -88,16 +88,17 @@ public:
virtual void MarkIntrinsicISizesDirty() override;
virtual nsSize GetXULMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState) override;
virtual nsSize GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) override;
// size calculation
int32_t GetRowCount();
int32_t GetRowHeightAppUnits() { return mRowHeight; }
+ int32_t GetRowHeightPixels() const;
int32_t GetFixedRowSize();
void SetRowHeight(nscoord aRowHeight);
nscoord GetYPosition();
nscoord GetAvailableHeight();
nscoord ComputeIntrinsicISize(nsBoxLayoutState& aBoxLayoutState);
// scrolling
nsresult InternalPositionChangedCallback();
--- a/toolkit/content/widgets/listbox.xml
+++ b/toolkit/content/widgets/listbox.xml
@@ -682,16 +682,18 @@
<!-- ///////////////// public listbox members ///////////////// -->
<property name="listBoxObject"
onget="return this.boxObject;"
readonly="true"/>
<!-- ///////////////// private listbox members ///////////////// -->
+ <field name="_touchY">-1</field>
+
<method name="_fireOnSelect">
<body>
<![CDATA[
if (!this._suppressOnSelect && !this.suppressOnSelect) {
var event = document.createEvent("Events");
event.initEvent("select", true, true);
this.dispatchEvent(event);
}
@@ -865,16 +867,52 @@
targetIndex = this.itemCount - 1;
// Fall through for actual action
case event.DIRECTION_UP:
this.ensureIndexIsVisible(targetIndex);
break;
}
]]>
</handler>
+
+ <handler event="touchstart">
+ <![CDATA[
+ if (event.touches.length > 1) {
+ // Multiple touch points detected, abort. In particular this aborts
+ // the panning gesture when the user puts a second finger down after
+ // already panning with one finger. Aborting at this point prevents
+ // the pan gesture from being resumed until all fingers are lifted
+ // (as opposed to when the user is back down to one finger).
+ this._touchY = -1;
+ } else {
+ this._touchY = event.touches[0].screenY;
+ }
+ ]]>
+ </handler>
+ <handler event="touchmove">
+ <![CDATA[
+ if (event.touches.length == 1 &&
+ this._touchY >= 0) {
+ let deltaY = this._touchY - event.touches[0].screenY;
+ let lines = Math.trunc(deltaY / this.listBoxObject.getRowHeight());
+ if (Math.abs(lines) > 0) {
+ this.listBoxObject.scrollByLines(lines);
+ deltaY -= lines * this.listBoxObject.getRowHeight();
+ this._touchY = event.touches[0].screenY + deltaY;
+ }
+ event.preventDefault();
+ }
+ ]]>
+ </handler>
+ <handler event="touchend">
+ <![CDATA[
+ this._touchY = -1;
+ ]]>
+ </handler>
+
</handlers>
</binding>
<binding id="listrows">
<resources>
<stylesheet src="chrome://global/skin/listbox.css"/>
</resources>