Commit 8b8175d8 authored by timothy@apple.com's avatar timothy@apple.com

Clean up and refactor TimelineDecorations into TimelineRuler.

https://bugs.webkit.org/show_bug.cgi?id=125709

Reviewed by Joseph Pecoraro.

* UserInterface/Main.html:
* UserInterface/OverviewTimelineView.css:
(.timeline-view.overview > .timeline-ruler):
(.timeline-view.overview > .timeline-ruler > .header):
(.timeline-view.overview > .timeline-ruler > .event-markers):
Updated class names and border sides.

* UserInterface/OverviewTimelineView.js:
(WebInspector.OverviewTimelineView):
(WebInspector.OverviewTimelineView.prototype.updateLayout):
Call updateLayout on the TimelineRuler.

* UserInterface/TimelineRuler.css: Renamed from Source/WebInspectorUI/UserInterface/TimelineDecorations.css.
(.timeline-ruler):
(.timeline-ruler > .header):
(.timeline-ruler > .header > .divider):
(.timeline-ruler > .header > .divider > .label):
(.timeline-ruler > .event-markers):
(.timeline-ruler > .event-markers > .event-marker-tooltip):
(.timeline-ruler > .event-markers > .event-marker):
(.timeline-ruler > .event-markers > .event-marker.load-event):
(.timeline-ruler > .event-markers > .event-marker.dom-content-event):
(.timeline-ruler > .event-markers > .event-marker.timestamp):
Updated class names and border sides.

* UserInterface/TimelineRuler.js: Renamed from Source/WebInspectorUI/UserInterface/TimelineDecorations.js.
(WebInspector.TimelineRuler):
(WebInspector.TimelineRuler.prototype.get element):
(WebInspector.TimelineRuler.prototype.get headerElement):
(WebInspector.TimelineRuler.prototype.get allowsClippedLabels):
(WebInspector.TimelineRuler.prototype.set allowsClippedLabels):
(WebInspector.TimelineRuler.prototype.get zeroTime):
(WebInspector.TimelineRuler.prototype.set zeroTime):
(WebInspector.TimelineRuler.prototype.get startTime):
(WebInspector.TimelineRuler.prototype.set startTime):
(WebInspector.TimelineRuler.prototype.get duration):
(WebInspector.TimelineRuler.prototype.set duration):
(WebInspector.TimelineRuler.prototype.get endTime):
(WebInspector.TimelineRuler.prototype.set endTime):
(WebInspector.TimelineRuler.prototype.get secondsPerPixel):
(WebInspector.TimelineRuler.prototype.set secondsPerPixel):
(WebInspector.TimelineRuler.prototype.updateLayout.removeDividerAndSelectNext):
(WebInspector.TimelineRuler.prototype.updateLayout):
(WebInspector.TimelineRuler.prototype._needsLayout):
(WebInspector.TimelineRuler.prototype._recalculate):
Refactor so the times are stored on TimelineRuler and not passed in each time an update is needed.
Support dividers that don't always start at the zero position, allowing a sliding time ruler.
Support for a non-pinned ruler where the end time is not fixed and the scale is specified in
seconds-per-pixel.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@162407 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 50cb8c9a
2013-12-13 Timothy Hatcher <timothy@apple.com>
Clean up and refactor TimelineDecorations into TimelineRuler.
https://bugs.webkit.org/show_bug.cgi?id=125709
Reviewed by Joseph Pecoraro.
* UserInterface/Main.html:
* UserInterface/OverviewTimelineView.css:
(.timeline-view.overview > .timeline-ruler):
(.timeline-view.overview > .timeline-ruler > .header):
(.timeline-view.overview > .timeline-ruler > .event-markers):
Updated class names and border sides.
* UserInterface/OverviewTimelineView.js:
(WebInspector.OverviewTimelineView):
(WebInspector.OverviewTimelineView.prototype.updateLayout):
Call updateLayout on the TimelineRuler.
* UserInterface/TimelineRuler.css: Renamed from Source/WebInspectorUI/UserInterface/TimelineDecorations.css.
(.timeline-ruler):
(.timeline-ruler > .header):
(.timeline-ruler > .header > .divider):
(.timeline-ruler > .header > .divider > .label):
(.timeline-ruler > .event-markers):
(.timeline-ruler > .event-markers > .event-marker-tooltip):
(.timeline-ruler > .event-markers > .event-marker):
(.timeline-ruler > .event-markers > .event-marker.load-event):
(.timeline-ruler > .event-markers > .event-marker.dom-content-event):
(.timeline-ruler > .event-markers > .event-marker.timestamp):
Updated class names and border sides.
* UserInterface/TimelineRuler.js: Renamed from Source/WebInspectorUI/UserInterface/TimelineDecorations.js.
(WebInspector.TimelineRuler):
(WebInspector.TimelineRuler.prototype.get element):
(WebInspector.TimelineRuler.prototype.get headerElement):
(WebInspector.TimelineRuler.prototype.get allowsClippedLabels):
(WebInspector.TimelineRuler.prototype.set allowsClippedLabels):
(WebInspector.TimelineRuler.prototype.get zeroTime):
(WebInspector.TimelineRuler.prototype.set zeroTime):
(WebInspector.TimelineRuler.prototype.get startTime):
(WebInspector.TimelineRuler.prototype.set startTime):
(WebInspector.TimelineRuler.prototype.get duration):
(WebInspector.TimelineRuler.prototype.set duration):
(WebInspector.TimelineRuler.prototype.get endTime):
(WebInspector.TimelineRuler.prototype.set endTime):
(WebInspector.TimelineRuler.prototype.get secondsPerPixel):
(WebInspector.TimelineRuler.prototype.set secondsPerPixel):
(WebInspector.TimelineRuler.prototype.updateLayout.removeDividerAndSelectNext):
(WebInspector.TimelineRuler.prototype.updateLayout):
(WebInspector.TimelineRuler.prototype._needsLayout):
(WebInspector.TimelineRuler.prototype._recalculate):
Refactor so the times are stored on TimelineRuler and not passed in each time an update is needed.
Support dividers that don't always start at the zero position, allowing a sliding time ruler.
Support for a non-pinned ruler where the end time is not fixed and the scale is specified in
seconds-per-pixel.
2014-01-08 Timothy Hatcher <timothy@apple.com> 2014-01-08 Timothy Hatcher <timothy@apple.com>
Improve scroll performance of WebInspector.TreeOutlineDataGridSynchronizer. Improve scroll performance of WebInspector.TreeOutlineDataGridSynchronizer.
......
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
<link rel="stylesheet" href="TimelineContentView.css"> <link rel="stylesheet" href="TimelineContentView.css">
<link rel="stylesheet" href="OverviewTimelineView.css"> <link rel="stylesheet" href="OverviewTimelineView.css">
<link rel="stylesheet" href="TimelineIcons.css"> <link rel="stylesheet" href="TimelineIcons.css">
<link rel="stylesheet" href="TimelineDecorations.css"> <link rel="stylesheet" href="TimelineRuler.css">
<link rel="stylesheet" href="TimelineDataGrid.css"> <link rel="stylesheet" href="TimelineDataGrid.css">
<link rel="stylesheet" href="TimelineOverview.css"> <link rel="stylesheet" href="TimelineOverview.css">
<link rel="stylesheet" href="ProfileView.css"> <link rel="stylesheet" href="ProfileView.css">
...@@ -343,7 +343,7 @@ ...@@ -343,7 +343,7 @@
<script src="IndeterminateProgressSpinner.js"></script> <script src="IndeterminateProgressSpinner.js"></script>
<script src="TimelineSidebarPanel.js"></script> <script src="TimelineSidebarPanel.js"></script>
<script src="SourceCodeTimelineTreeElement.js"></script> <script src="SourceCodeTimelineTreeElement.js"></script>
<script src="TimelineDecorations.js"></script> <script src="TimelineRuler.js"></script>
<script src="TimelineDataGrid.js"></script> <script src="TimelineDataGrid.js"></script>
<script src="LayoutTimelineDataGrid.js"></script> <script src="LayoutTimelineDataGrid.js"></script>
<script src="ScriptTimelineDataGrid.js"></script> <script src="ScriptTimelineDataGrid.js"></script>
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE. * THE POSSIBILITY OF SUCH DAMAGE.
*/ */
.timeline-view.overview > .timeline-decorations { .timeline-view.overview > .timeline-ruler {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
...@@ -31,12 +31,12 @@ ...@@ -31,12 +31,12 @@
bottom: 0; bottom: 0;
} }
.timeline-view.overview > .timeline-decorations > .header { .timeline-view.overview > .timeline-ruler > .header {
border-bottom: 1px solid rgb(200, 200, 200); border-top: 1px solid rgb(200, 200, 200);
height: 23px; height: 23px;
} }
.timeline-view.overview > .timeline-decorations > .event-markers { .timeline-view.overview > .timeline-ruler > .event-markers {
top: 23px; top: 23px;
} }
......
...@@ -34,8 +34,8 @@ WebInspector.OverviewTimelineView = function() ...@@ -34,8 +34,8 @@ WebInspector.OverviewTimelineView = function()
this._treeOutlineDataGridSynchronizer = new WebInspector.TreeOutlineDataGridSynchronizer(this._contentTreeOutline, this._dataGrid); this._treeOutlineDataGridSynchronizer = new WebInspector.TreeOutlineDataGridSynchronizer(this._contentTreeOutline, this._dataGrid);
this._timelineDecorations = new WebInspector.TimelineDecorations; this._timelineRuler = new WebInspector.TimelineRuler;
this.element.appendChild(this._timelineDecorations.element); this.element.appendChild(this._timelineRuler.element);
this.element.classList.add(WebInspector.OverviewTimelineView.StyleClassName); this.element.classList.add(WebInspector.OverviewTimelineView.StyleClassName);
this.element.appendChild(this._dataGrid.element); this.element.appendChild(this._dataGrid.element);
...@@ -71,6 +71,11 @@ WebInspector.OverviewTimelineView.prototype = { ...@@ -71,6 +71,11 @@ WebInspector.OverviewTimelineView.prototype = {
this._treeOutlineDataGridSynchronizer.synchronize(); this._treeOutlineDataGridSynchronizer.synchronize();
}, },
updateLayout: function()
{
this._timelineRuler.updateLayout();
},
// Private // Private
_compareTreeElementsByDetails: function(a, b) _compareTreeElementsByDetails: function(a, b)
......
...@@ -23,47 +23,39 @@ ...@@ -23,47 +23,39 @@
* THE POSSIBILITY OF SUCH DAMAGE. * THE POSSIBILITY OF SUCH DAMAGE.
*/ */
.timeline-decorations { .timeline-ruler {
position: relative; position: relative;
pointer-events: none; pointer-events: none;
} }
.timeline-decorations > .header { .timeline-ruler > .header {
border-top: 1px solid rgb(200, 200, 200); border-bottom: 1px solid rgb(200, 200, 200);
height: 22px; height: 22px;
position: relative; position: relative;
} }
.timeline-decorations > .header > .divider { .timeline-ruler > .header > .divider {
position: absolute; position: absolute;
width: 1px; width: 1px;
top: 0; top: 0;
bottom: 0; bottom: 0;
background-image: -webkit-linear-gradient(top, rgba(200, 200, 200, 1), rgba(200, 200, 200, 0) 85%); -webkit-transform: translateX(-1px);
background-image: -webkit-linear-gradient(bottom, rgba(200, 200, 200, 1), rgba(200, 200, 200, 0) 85%);
} }
.timeline-decorations > .header > .divider:first-child, .timeline-ruler > .header > .divider > .label {
.timeline-decorations > .header > .divider:last-child {
background-image: none;
}
.timeline-decorations > .header > .divider > .label {
position: absolute; position: absolute;
top: 3px; top: 5px;
right: 4px; right: 5px;
font-size: 9px; font-size: 9px;
font-family: "Lucida Grande", sans-serif; font-family: "Lucida Grande", sans-serif;
color: rgb(128, 128, 128); color: rgb(128, 128, 128);
white-space: nowrap; white-space: nowrap;
} }
.timeline-decorations > .header > .divider:first-child > .label { .timeline-ruler > .event-markers {
display: none;
}
.timeline-decorations > .event-markers {
position: absolute; position: absolute;
top: 22px; top: 22px;
left: 0; left: 0;
...@@ -71,7 +63,7 @@ ...@@ -71,7 +63,7 @@
bottom: 0; bottom: 0;
} }
.timeline-decorations > .event-markers > .event-marker-tooltip { .timeline-ruler > .event-markers > .event-marker-tooltip {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
...@@ -81,7 +73,7 @@ ...@@ -81,7 +73,7 @@
pointer-events: auto; pointer-events: auto;
} }
.timeline-decorations > .event-markers > .event-marker { .timeline-ruler > .event-markers > .event-marker {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
...@@ -93,14 +85,14 @@ ...@@ -93,14 +85,14 @@
border-left-color: rgba(128, 128, 128, 0.5); border-left-color: rgba(128, 128, 128, 0.5);
} }
.timeline-decorations > .event-markers > .event-marker.load-event { .timeline-ruler > .event-markers > .event-marker.load-event {
border-left-color: rgba(255, 0, 0, 0.5); border-left-color: rgba(255, 0, 0, 0.5);
} }
.timeline-decorations > .event-markers > .event-marker.dom-content-event { .timeline-ruler > .event-markers > .event-marker.dom-content-event {
border-left-color: rgba(0, 0, 255, 0.5); border-left-color: rgba(0, 0, 255, 0.5);
} }
.timeline-decorations > .event-markers > .event-marker.timestamp { .timeline-ruler > .event-markers > .event-marker.timestamp {
border-left-color: rgba(0, 110, 0, 0.5); border-left-color: rgba(0, 110, 0, 0.5);
} }
...@@ -23,37 +23,44 @@ ...@@ -23,37 +23,44 @@
* THE POSSIBILITY OF SUCH DAMAGE. * THE POSSIBILITY OF SUCH DAMAGE.
*/ */
WebInspector.TimelineDecorations = function() WebInspector.TimelineRuler = function()
{ {
WebInspector.Object.call(this); WebInspector.Object.call(this);
this._element = document.createElement("div"); this._element = document.createElement("div");
this._element.className = WebInspector.TimelineDecorations.StyleClassName; this._element.className = WebInspector.TimelineRuler.StyleClassName;
this._headerElement = document.createElement("div"); this._headerElement = document.createElement("div");
this._headerElement.className = WebInspector.TimelineDecorations.HeaderElementStyleClassName; this._headerElement.className = WebInspector.TimelineRuler.HeaderElementStyleClassName;
this._element.appendChild(this._headerElement); this._element.appendChild(this._headerElement);
this._markersElement = document.createElement("div"); this._markersElement = document.createElement("div");
this._markersElement.className = WebInspector.TimelineDecorations.EventMarkersElementStyleClassName; this._markersElement.className = WebInspector.TimelineRuler.EventMarkersElementStyleClassName;
this._element.appendChild(this._markersElement); this._element.appendChild(this._markersElement);
this.clear(); this._zeroTime = 0;
this._startTime = 0;
this._endTime = 0;
this._duration = NaN;
this._secondsPerPixel = 0;
this._endTimePinned = false;
this._allowsClippedLabels = false;
} }
WebInspector.TimelineDecorations.MinimumDividerSpacing = 64; WebInspector.TimelineRuler.MinimumLeftDividerSpacing = 48;
WebInspector.TimelineRuler.MinimumDividerSpacing = 64;
WebInspector.TimelineDecorations.StyleClassName = "timeline-decorations"; WebInspector.TimelineRuler.StyleClassName = "timeline-ruler";
WebInspector.TimelineDecorations.HeaderElementStyleClassName = "header"; WebInspector.TimelineRuler.HeaderElementStyleClassName = "header";
WebInspector.TimelineDecorations.DividerElementStyleClassName = "divider"; WebInspector.TimelineRuler.DividerElementStyleClassName = "divider";
WebInspector.TimelineDecorations.DividerLabelElementStyleClassName = "label"; WebInspector.TimelineRuler.DividerLabelElementStyleClassName = "label";
WebInspector.TimelineDecorations.EventMarkersElementStyleClassName = "event-markers"; WebInspector.TimelineRuler.EventMarkersElementStyleClassName = "event-markers";
WebInspector.TimelineDecorations.EventMarkerTooltipElementStyleClassName = "event-marker-tooltip"; WebInspector.TimelineRuler.EventMarkerTooltipElementStyleClassName = "event-marker-tooltip";
WebInspector.TimelineDecorations.BaseEventMarkerElementStyleClassName = "event-marker"; WebInspector.TimelineRuler.BaseEventMarkerElementStyleClassName = "event-marker";
WebInspector.TimelineDecorations.prototype = { WebInspector.TimelineRuler.prototype = {
constructor: WebInspector.TimelineDecorations, constructor: WebInspector.TimelineRuler,
// Public // Public
...@@ -67,160 +74,241 @@ WebInspector.TimelineDecorations.prototype = { ...@@ -67,160 +74,241 @@ WebInspector.TimelineDecorations.prototype = {
return this._headerElement; return this._headerElement;
}, },
clear: function() get allowsClippedLabels()
{ {
this._eventMarkers = []; return this._allowsClippedLabels
this._markersElement.removeChildren();
}, },
updateHeaderTimes: function(timeSpan, leftPadding, rightPadding, force) set allowsClippedLabels(x)
{ {
if (!this.isShowingHeaderDividers()) if (this._allowsClippedLabels === x)
return; return;
if (isNaN(timeSpan)) this._allowsClippedLabels = x || false;
this._needsLayout();
},
get zeroTime()
{
return this._zeroTime;
},
set zeroTime(x)
{
if (this._zeroTime === x)
return; return;
leftPadding = leftPadding || 0; this._zeroTime = x || 0;
rightPadding = rightPadding || 0;
var clientWidth = this._headerElement.clientWidth; this._needsLayout();
},
var leftVisibleEdge = leftPadding; get startTime()
var rightVisibleEdge = clientWidth - rightPadding; {
var visibleWidth = rightVisibleEdge - leftVisibleEdge; return this._startTime;
if (visibleWidth <= 0) },
set startTime(x)
{
if (this._startTime === x)
return; return;
var pixelsPerSecond = clientWidth / timeSpan; this._startTime = x || 0;
if (!isNaN(this._duration))
this._endTime = this._startTime + this._duration;
this._needsLayout();
},
get duration()
{
if (!isNaN(this._duration))
return this._duration;
return this.endTime - this.startTime;
},
if (!force && this._currentPixelsPerSecond === pixelsPerSecond) set duration(x)
{
if (this._duration === x)
return; return;
this._currentPixelsPerSecond = pixelsPerSecond; this._duration = x || NaN;
// Calculate a divider count based on the maximum allowed divider density. if (!isNaN(this._duration)) {
var dividerCount = Math.round(visibleWidth / WebInspector.TimelineDecorations.MinimumDividerSpacing); this._endTime = this._startTime + this._duration;
this._endTimePinned = true;
} else
this._endTimePinned = false;
// Calculate the slice time based on the rough divider count and the time span. this._needsLayout();
var sliceTime = timeSpan / dividerCount; },
get endTime()
{
if (!this._endTimePinned && this._scheduledLayoutUpdateIdentifier)
this._recalculate();
return this._endTime;
},
set endTime(x)
{
if (this._endTime === x)
return;
this._endTime = x || 0;
this._endTimePinned = true;
this._needsLayout();
},
// Snap the slice time to a nearest number (e.g. 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, etc.) get secondsPerPixel()
sliceTime = Math.pow(10, Math.ceil(Math.log(sliceTime) / Math.LN10)); {
if (sliceTime * pixelsPerSecond >= 5 * WebInspector.TimelineDecorations.MinimumDividerSpacing) if (this._scheduledLayoutUpdateIdentifier)
sliceTime = sliceTime / 5; this._recalculate();
if (sliceTime * pixelsPerSecond >= 2 * WebInspector.TimelineDecorations.MinimumDividerSpacing) return this._secondsPerPixel;
sliceTime = sliceTime / 2; },
set secondsPerPixel(x)
{
if (this._secondsPerPixel === x)
return;
var firstDividerTime = sliceTime; this._secondsPerPixel = x || 0;
var lastDividerTime = timeSpan; this._endTimePinned = false;
this._currentSliceTime = 0;
this._needsLayout();
},
updateLayout: function()
{
if (this._scheduledLayoutUpdateIdentifier) {
cancelAnimationFrame(this._scheduledLayoutUpdateIdentifier);
delete this._scheduledLayoutUpdateIdentifier;
}
var visibleWidth = this._recalculate();
if (visibleWidth <= 0)
return;
var duration = this.duration;
var pixelsPerSecond = visibleWidth / duration;
// Calculate a divider count based on the maximum allowed divider density.
var dividerCount = Math.round(visibleWidth / WebInspector.TimelineRuler.MinimumDividerSpacing);
if (this._endTimePinned || !this._currentSliceTime) {
// Calculate the slice time based on the rough divider count and the time span.
var sliceTime = duration / dividerCount;
// Snap the slice time to a nearest number (e.g. 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, etc.)
sliceTime = Math.pow(10, Math.ceil(Math.log(sliceTime) / Math.LN10));
if (sliceTime * pixelsPerSecond >= 5 * WebInspector.TimelineRuler.MinimumDividerSpacing)
sliceTime = sliceTime / 5;
if (sliceTime * pixelsPerSecond >= 2 * WebInspector.TimelineRuler.MinimumDividerSpacing)
sliceTime = sliceTime / 2;
this._currentSliceTime = sliceTime;
} else {
// Reuse the last slice time since the time duration does not scale to fit when the end time isn't pinned.
var sliceTime = this._currentSliceTime;
}
var firstDividerTime = (Math.ceil((this._startTime - this._zeroTime) / sliceTime) * sliceTime) + this._zeroTime;
var lastDividerTime = this._endTime;
// Calculate the divider count now based on the final slice time. // Calculate the divider count now based on the final slice time.
dividerCount = Math.ceil((lastDividerTime - firstDividerTime) / sliceTime); dividerCount = Math.ceil((lastDividerTime - firstDividerTime) / sliceTime);
// Make an extra divider in case the last one is partially visible.
if (!this._endTimePinned)
++dividerCount;
var dividerElement = this._headerElement.firstChild; var dividerElement = this._headerElement.firstChild;
for (var i = 0; i <= dividerCount; ++i) { for (var i = 0; i <= dividerCount; ++i) {
if (!dividerElement) { if (!dividerElement) {
dividerElement = document.createElement("div"); dividerElement = document.createElement("div");
dividerElement.className = WebInspector.TimelineDecorations.DividerElementStyleClassName; dividerElement.className = WebInspector.TimelineRuler.DividerElementStyleClassName;
this._headerElement.appendChild(dividerElement); this._headerElement.appendChild(dividerElement);
var labelElement = document.createElement("div"); var labelElement = document.createElement("div");
labelElement.className = WebInspector.TimelineDecorations.DividerLabelElementStyleClassName; labelElement.className = WebInspector.TimelineRuler.DividerLabelElementStyleClassName;
dividerElement._labelElement = labelElement; dividerElement._labelElement = labelElement;
dividerElement.appendChild(labelElement); dividerElement.appendChild(labelElement);
} }
var left = visibleWidth * (i / dividerCount); var dividerTime = firstDividerTime + (sliceTime * i);
var totalLeft = left + leftVisibleEdge;
var fractionLeft = totalLeft / clientWidth; var newLeftPosition = (dividerTime - this._startTime) / duration;
var percentLeft = 100 * fractionLeft;
var time = firstDividerTime + (sliceTime * i); if (!this._allowsClippedLabels) {
// Don't allow dividers under 0% where they will be completely hidden.
if (newLeftPosition < 0)
continue;
// When over 100% it is time to stop making/updating dividers.
if (newLeftPosition > 1)
break;
// Don't allow the left-most divider spacing to be so tight it clips.
if ((newLeftPosition * visibleWidth) < WebInspector.TimelineRuler.MinimumLeftDividerSpacing)
continue;
}