Commits

Brian Burg committed eb82102

Inspector: re-add guide bands to the playback slider (does not yet respect active filters).

Comments (0)

Files changed (2)

WebKit/Source/WebCore/inspector/front-end/TimelapseOverview.js

     eventSource.addEventListener(types.PlaybackHitMark, this._onPlaybackHitMark, this);
     eventSource.addEventListener(types.RecordAdded, this._scheduleRefresh, this);
     eventSource.addEventListener(types.MarkSelected, this._onMarkSelected, this);
-    eventSource.addEventListener(types.FilterChanged, this._scheduleRefresh, this);
+    eventSource.addEventListener(types.FilterChanged, this._onFilterChanged, this);
 
     this._presentationModel = WebInspector.timelapseManager;
     this._calculator = WebInspector.timelapseManager.calculator;
 	this._heatmapContainer.addEventListener("dblclick", this._resizeZoomMaximum.bind(this), true);
 
 	this._playbackSlider = new WebInspector.TimelapseOverviewSlider(this, "playback", true);
+	this._playbackSlider.addEventListener(WebInspector.TimelapseOverviewSlider.EventTypes.DragStart,
+					     this._onPlaybackSliderDragStart, this);
+	this._playbackSlider.addEventListener(WebInspector.TimelapseOverviewSlider.EventTypes.DragEnd,
+					     this._onPlaybackSliderDragEnd, this);
 	this._heatmapContainer.appendChild(this._playbackSlider.element);
+	this._playbackOldBand = new WebInspector.TimelapseOverviewSlider(this, "previous", false);
+	this._heatmapContainer.appendChild(this._playbackOldBand.element);
+	this._playbackNewBand = new WebInspector.TimelapseOverviewSlider(this, "tentative", false);
+	this._heatmapContainer.appendChild(this._playbackNewBand.element);
 
 	this._categoryHeatmaps = {};
 
 	    updateCategory(categories[category]);
 
 	this._updateHeatmaps();
-
-	// refresh playback slider
-	//if (this._shouldSetDefaultPlaybackPosition) {
-	//    this._shouldSetDefaultPlaybackPosition = false;
-	//    this._playbackSlider.enable();
-	//    this._playbackSlider.select(this.allTicks[this.allTicks.length-1], true);
-	//}
-
+	this._playbackSlider.refresh();
 	this.updateDividers(false);
     },
 
         WebInspector.elementDragEnd(event);
     },
 
+
+    _onPlaybackSliderDragStart: function(event)
+    {
+	this._playbackSlider.addEventListener(WebInspector.TimelapseOverviewSlider.EventTypes.Moved,
+					      this._onPlaybackSliderDragged,
+					      this);
+
+	this._playbackOldBand.show();
+	this._playbackOldBand.setPosition(this._playbackSlider.position);
+    },
+
+    _onPlaybackSliderDragged: function(event)
+    {
+	var position = this._playbackSlider.position;
+    	var timestamp = this.calculator.minimumBoundary + position/100.0 * this.calculator.boundarySpan;
+
+	function binarySearchNearest(key, array, comparator, distancefn)
+	{
+	    var first = 0;
+	    var last = array.length - 1;
+
+	    while (first <= last) {
+		var mid = (first + last) >> 1;
+		var c = comparator(key, array[mid]);
+		if (c > 0)
+		    first = mid + 1;
+		else if (c < 0)
+		last = mid - 1;
+		else
+		    return mid;
+	    }
+
+	    var dLeft = distancefn(key, array[first]);
+	    var dRight = distancefn(key, array[last]);
+	    return (dRight <= dLeft) ? last : first;
+	}
+
+	function timestampAndRecordComparator(ts, record) {
+	    var record_ts = record.mark.timestamp;
+	    if (record_ts > ts) return -1;
+	    if (record_ts < ts) return 1;
+	    return 0;
+	}
+
+	function timeDistanceFunction(ts, record) {
+	    if (!record)
+		return Number.POSITIVE_INFINITY;
+
+	    return Math.abs(ts - record.mark.timestamp);
+	}
+
+	if (this._potentialMarkBounds && 
+	    timestamp >= this._potentialMarkBounds.min && timestamp <= this._potentialMarkBounds.max)
+	    return;
+
+	// TODO: dragging/dropping/potentials don't respect the active filters. Need to reinstate
+	// the this._presentationModel.activeRecords filter.
+	var allRecords = this._presentationModel.allRecords;
+	var idx = binarySearchNearest(timestamp, allRecords,
+				      timestampAndRecordComparator, timeDistanceFunction);
+
+	var recordPosition = this.calculator.computeOverviewPercentage(allRecords[idx]) * 100.0;
+	this._playbackNewBand.setPosition(recordPosition);
+	var prevRecordPosition = (idx == 0) ? 0.0
+                                            : this.calculator.computeOverviewPercentage(allRecords[idx-1]) * 100.0;
+	var nextRecordPosition = (idx == allRecords.length-1) ? 100.0
+                                                              : this.calculator.computeOverviewPercentage(allRecords[idx+1]);
+	var minBounds = recordPosition - (recordPosition - prevRecordPosition)/2.0;
+	var maxBounds = recordPosition - (recordPosition - nextRecordPosition)/2.0;
+
+	// this is used to short-circuit searching for the nearest record if it's
+	// within the space "owned" by current nearest record. So, the nearest record
+	// is only recomputed when moving more than halfway away from
+	// this record to the next one
+	this._potentialMarkBounds = {min: minBounds, max: maxBounds};
+    },
+
+    _onPlaybackSliderDragEnd: function(event)
+    {
+	this._playbackSlider.removeEventListener(WebInspector.TimelapseOverviewSlider.EventTypes.Moved,
+						 this._onPlaybackSliderDragged,
+						 this);
+
+	this._playbackOldBand.hide();
+	this._playbackNewBand.hide();
+	this._playbackSlider.setPosition(this._playbackNewBand.position, true);
+	//TODO: disable slider, replay up to nearest record.
+    },
+
     _onLabelClicked: function(event)
     {
 	var labelNode = event.target;
 	// TODO: this should adjust a preview slider band.
     },
 
+    _onFilterChanged: function()
+    {
+	this._potentialMarkBounds = false;
+	this._scheduleRefresh();
+    },
+
     _onRecordingStarted: function()
     {
         this.reset();
     this.enable();
 };
 
+WebInspector.TimelapseOverviewSlider.EventTypes = {
+    Moved: "TimelapseSliderMoved",
+    DragStart: "TimelapseSliderDragStart",
+    DragEnd: "TimelapseSliderDragEnd"
+};
+
 WebInspector.TimelapseOverviewSlider.prototype = {
     clear: function()
     {
 	this.refresh();
 	
 	if (!suppressEvents) {
+	    this.dispatchEventToListeners(WebInspector.TimelapseOverviewSlider.EventTypes.Moved);
 	}
     },
 
+    get position()
+    {
+	return this._position;
+    },
+
+    set position(pos)
+    {
+	this.setPosition(pos, false);
+    },
+
     show: function()
     {
 	this.element.classList.remove("hidden");
 
     refresh: function()
     {
-	this.element.style.left = this._position + "%";
+	var parentWidth = this.element.parentElement.clientWidth;
+	var rightMaximum = (parentWidth - this._verticalBarElement.offsetWidth) / parentWidth * 100.0;
+	this.element.style.left = Number.constrain(this.position, 0.0, rightMaximum) + "%";
     },
 
     _startSliderDragging: function(event)
 	this.element.classList.add("slider-dragging");
 
 	WebInspector.elementDragStart(this.element, this._sliderDragging.bind(this), this._endSliderDragging.bind(this), event, "col-resize");
+	this.dispatchEventToListeners(WebInspector.TimelapseOverviewSlider.EventTypes.DragStart);
     },
 
     _sliderDragging: function(event)
 
 	WebInspector.elementDragEnd(event);
 	this.element.classList.remove("slider-dragging");
+	this.dispatchEventToListeners(WebInspector.TimelapseOverviewSlider.EventTypes.DragEnd);
     }
 };
 

WebKit/Source/WebCore/inspector/front-end/timelapseOverview.css

 
 .slider-dragging {
     -webkit-transition: none;
-    opacity: 0.4;
 }
 
 .timelapse-overview-slider.disabled {
     cursor: default;
+    opacity: 0.4;
 }
 
 .playback-slider .timelapse-slider-band,
     background-color: rgba(255, 0, 0, 1);
 }
 
+.previous-slider .timelapse-slider-band {
+    background-color: rgba(150, 0, 0, 0.3);
+    border: 0;
+}
+
+.tentative-slider .timelapse-slider-band {
+    border-left: 1px dashed #f00;
+    border-right: 1px dashed #f00;
+    background-color: rgba(255,0,0,0.1);;
+}
+
 .timelapse-overview-slider.slider-dragging {
     opacity: 0.6;
 }