Index: third_party/WebKit/Source/devtools/front_end/ui/StaticViewportControl.js |
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/StaticViewportControl.js b/third_party/WebKit/Source/devtools/front_end/ui/StaticViewportControl.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..64fdb76fb5b5429173514c3ba3be622841e2cf9f |
--- /dev/null |
+++ b/third_party/WebKit/Source/devtools/front_end/ui/StaticViewportControl.js |
@@ -0,0 +1,176 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+/** |
+ * @constructor |
+ * @param {!WebInspector.StaticViewportControl.Provider} provider |
+ */ |
+WebInspector.StaticViewportControl = function(provider) |
+{ |
+ this.element = createElement("div"); |
+ this.element.style.overflow = "auto"; |
+ this._innerElement = this.element.createChild("div"); |
+ this._innerElement.style.height = "0px"; |
+ this._innerElement.style.position = "relative"; |
+ this._innerElement.style.overflow = "hidden"; |
+ this._provider = provider; |
+ this.element.addEventListener("scroll", this.refresh.bind(this), false); |
dgozman
2016/09/28 20:56:18
Should this be innerElement?
einbinder
2016/09/29 01:04:21
The .element scrolls, the ._innerElement is just v
|
+ |
+ this._firstActiveIndex = 0; |
+ this._lastActiveIndex = -1; |
+ this._itemCount = 0; |
+} |
+WebInspector.StaticViewportControl._insertedAt = Symbol("WebInspector.StaticViewportControl._insertedAt"); |
+WebInspector.StaticViewportControl.prototype = { |
dgozman
2016/09/28 20:56:18
nit: blank lines, please.
einbinder
2016/09/29 01:04:20
Done.
|
+ invalidateContent: function() |
+ { |
+ this._itemCount = this._provider.itemCount(); |
+ this._innerElement.removeChildren(); |
+ this.refresh(); |
+ }, |
+ |
+ refresh: function() |
+ { |
+ if (!this._visibleHeight()) |
+ return; // Do nothing for invisible controls. |
+ |
+ var height = 0; |
+ this._cumulativeHeights = new Int32Array(this._itemCount); |
+ for (var i = 0; i < this._itemCount; ++i) { |
+ height += this._provider.fastHeight(i); |
+ this._cumulativeHeights[i] = height; |
+ } |
+ this._innerElement.style.height = height + "px"; |
+ |
+ this._update(); |
+ }, |
+ |
+ _update: function() |
+ { |
+ if (!this._cumulativeHeights) { |
+ this.refresh(); |
+ return; |
+ } |
+ |
+ var visibleHeight = this._visibleHeight(); |
+ var visibleFrom = this.element.scrollTop; |
+ var activeHeight = visibleHeight * 2; |
+ this._firstActiveIndex = Math.max(Array.prototype.lowerBound.call(this._cumulativeHeights, visibleFrom + 1 - (activeHeight - visibleHeight) / 2), 0); |
+ this._lastActiveIndex = Math.min(Array.prototype.lowerBound.call(this._cumulativeHeights, visibleFrom + visibleHeight + (activeHeight - visibleHeight) / 2), this._itemCount - 1); |
+ |
+ for (var i = this._innerElement.children.length - 1; i >= 0; --i) { |
+ var element = this._innerElement.children[i]; |
+ if (element[WebInspector.StaticViewportControl._insertedAt] < this._firstActiveIndex || element[WebInspector.StaticViewportControl._insertedAt] > this._lastActiveIndex) |
+ element.remove(); |
+ } |
+ |
+ for (var i = this._firstActiveIndex; i <= this._lastActiveIndex; ++i) |
+ this._insertElement(i); |
+ }, |
+ |
+ /** |
+ * @param {number} index |
+ */ |
+ _insertElement: function(index) |
+ { |
+ var element = this._provider.itemElement(index); |
+ if (!element || element.parentElement) |
+ return; |
+ |
+ element.style.position = "absolute"; |
+ element.style.top = (this._cumulativeHeights[index - 1] || 0) + "px"; |
+ element.style.left = "0"; |
+ element.style.right = "0"; |
+ element[WebInspector.StaticViewportControl._insertedAt] = index; |
+ this._innerElement.appendChild(element); |
+ }, |
+ |
+ /** |
+ * @return {number} |
+ */ |
+ firstVisibleIndex: function() |
+ { |
+ var firstVisibleIndex = Math.max(Array.prototype.lowerBound.call(this._cumulativeHeights, this.element.scrollTop + 1), 0); |
+ return Math.max(firstVisibleIndex, this._firstActiveIndex); |
+ }, |
+ |
+ /** |
+ * @return {number} |
+ */ |
+ lastVisibleIndex: function() |
+ { |
+ var lastVisibleIndex = Math.max(Array.prototype.lowerBound.call(this._cumulativeHeights, this.element.scrollTop + this._visibleHeight()), 0); |
+ return Math.min(lastVisibleIndex, this._lastActiveIndex); |
+ }, |
+ |
+ /** |
+ * @param {number} index |
+ * @param {boolean=} makeLast |
+ */ |
+ scrollItemIntoView: function(index, makeLast) |
+ { |
+ var firstVisibleIndex = this.firstVisibleIndex(); |
+ var lastVisibleIndex = this.lastVisibleIndex(); |
+ if (index > firstVisibleIndex && index < lastVisibleIndex) |
+ return; |
+ if (makeLast) |
+ this.forceScrollItemToBeLast(index); |
+ else if (index <= firstVisibleIndex) |
+ this.forceScrollItemToBeFirst(index); |
+ else if (index >= lastVisibleIndex) |
+ this.forceScrollItemToBeLast(index); |
+ }, |
+ |
+ /** |
+ * @param {number} index |
+ */ |
+ forceScrollItemToBeFirst: function(index) |
+ { |
+ this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0; |
+ this._update(); |
+ }, |
+ |
+ /** |
+ * @param {number} index |
+ */ |
+ forceScrollItemToBeLast: function(index) |
+ { |
+ this.element.scrollTop = this._cumulativeHeights[index] - this._visibleHeight(); |
+ this._update(); |
+ }, |
+ |
+ /** |
+ * @return {number} |
+ */ |
+ _visibleHeight: function() |
+ { |
+ return this.element.offsetHeight; |
+ } |
+} |
+ |
+/** |
+ * @interface |
+ */ |
+WebInspector.StaticViewportControl.Provider = function() |
+{ |
+} |
+ |
+WebInspector.StaticViewportControl.Provider.prototype = { |
+ /** |
+ * @param {number} index |
+ * @return {number} |
+ */ |
+ fastHeight: function(index) { return 0; }, |
dgozman
2016/09/28 20:56:18
itemHeight?
einbinder
2016/09/29 01:04:20
fastItemHeight
|
+ |
+ /** |
+ * @return {number} |
+ */ |
+ itemCount: function() { return 0; }, |
+ |
+ /** |
+ * @param {number} index |
+ * @return {?Element} |
+ */ |
+ itemElement: function(index) { return null; } |
+} |