blob: 775d010c8019d8571ac2083dad447e56eaf26428 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2015 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.
-->
<link rel="import" href="/core/tracks/heading_track.html">
<link rel="import" href="/base/ui/color_scheme.html">
<link rel="import" href="/base/sorted_array_utils.html">
<link rel="import" href="/base/ui.html">
<style>
.letter-dot-track {
height: 18px;
}
</style>
<script>
'use strict';
tv.exportTo('tv.c.tracks', function() {
var SelectionState = tv.c.trace_model.SelectionState;
/**
* A track that displays an array of dots with filled letters inside them.
* @constructor
* @extends {HeadingTrack}
*/
var LetterDotTrack = tv.b.ui.define(
'letter-dot-track', tv.c.tracks.HeadingTrack);
LetterDotTrack.prototype = {
__proto__: tv.c.tracks.HeadingTrack.prototype,
decorate: function(viewport) {
tv.c.tracks.HeadingTrack.prototype.decorate.call(this, viewport);
this.classList.add('letter-dot-track');
this.items_ = undefined;
},
getModelEventFromItem: function(item) {
throw new Error('Not implemented.');
},
get items() {
return this.items_;
},
set items(items) {
this.items_ = items;
this.invalidateDrawingContainer();
},
get height() {
return window.getComputedStyle(this).height;
},
set height(height) {
this.style.height = height;
},
get dumpRadiusView() {
return 7 * (window.devicePixelRatio || 1);
},
draw: function(type, viewLWorld, viewRWorld) {
if (this.items_ === undefined)
return;
switch (type) {
case tv.c.tracks.DrawType.SLICE:
this.drawSlices_(viewLWorld, viewRWorld);
break;
}
},
drawSlices_: function(viewLWorld, viewRWorld) {
var ctx = this.context();
var pixelRatio = window.devicePixelRatio || 1;
var bounds = this.getBoundingClientRect();
var height = bounds.height * pixelRatio;
var halfHeight = height * 0.5;
var twoPi = Math.PI * 2;
var palette = tv.b.ui.getColorPalette();
var highlightIdBoost = tv.b.ui.paletteProperties.highlightIdBoost;
// Culling parameters.
var dt = this.viewport.currentDisplayTransform;
var dumpRadiusView = this.dumpRadiusView;
var itemRadiusWorld = dt.xViewVectorToWorld(height);
// Draw the memory dumps.
var items = this.items_;
var loI = tv.b.findLowIndexInSortedArray(
items,
function(item) { return item.start; },
viewLWorld);
var oldFont = ctx.font;
ctx.font = '400 ' + Math.floor(9 * pixelRatio) + 'px Arial';
ctx.strokeStyle = 'rgb(0,0,0)';
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
var drawItems = function(selected) {
for (var i = loI; i < items.length; ++i) {
var item = items[i];
var x = item.start;
if (x - itemRadiusWorld > viewRWorld)
break;
if (item.selected !== selected)
continue;
var xView = dt.xWorldToView(x);
if (item.selected)
ctx.fillStyle = palette[item.colorId + highlightIdBoost];
else
ctx.fillStyle = palette[item.colorId];
ctx.beginPath();
ctx.arc(xView, halfHeight, dumpRadiusView + 0.5, 0, twoPi);
ctx.fill();
if (item.selected) {
ctx.lineWidth = 3;
ctx.strokeStyle = 'rgb(100,100,0)';
ctx.stroke();
ctx.beginPath();
ctx.arc(xView, halfHeight, dumpRadiusView, 0, twoPi);
ctx.lineWidth = 1.5;
ctx.strokeStyle = 'rgb(255,255,0)';
ctx.stroke();
} else {
ctx.lineWidth = 1;
ctx.strokeStyle = 'rgb(0,0,0)';
ctx.stroke();
}
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillText(item.dotLetter, xView, halfHeight);
}
};
// Draw unselected items first to make sure they don't occlude selected
// items.
drawItems(false);
drawItems(true);
ctx.lineWidth = 1;
ctx.font = oldFont;
// For performance reasons we only check the SelectionState of the first
// memory dump. If it's DIMMED we assume that all are DIMMED.
// TODO(petrcermak): Allow partial highlight.
var selectionState = SelectionState.NONE;
if (items.length &&
items[0].selectionState === SelectionState.DIMMED) {
selectionState = SelectionState.DIMMED;
}
// Dim the track when there is an active highlight.
if (selectionState === SelectionState.DIMMED) {
var width = bounds.width * pixelRatio;
ctx.fillStyle = 'rgba(255,255,255,0.5)';
ctx.fillRect(0, 0, width, height);
}
},
addEventsToTrackMap: function(eventToTrackMap) {
if (this.items_ === undefined)
return;
this.items_.forEach(function(item) {
var event = this.getModelEventFromItem(item);
if (event)
eventToTrackMap.addEvent(event, this);
}, this);
},
addIntersectingItemsInRangeToSelectionInWorldSpace: function(
loWX, hiWX, viewPixWidthWorld, selection) {
if (this.items_ === undefined)
return;
var itemRadiusWorld = viewPixWidthWorld * this.dumpRadiusView;
tv.b.iterateOverIntersectingIntervals(
this.items_,
function(x) { return x.start - itemRadiusWorld; },
function(x) { return 2 * itemRadiusWorld; },
loWX, hiWX,
function(item) {
var event = this.getModelEventFromItem(item);
if (event)
selection.push(event);
}.bind(this));
},
/**
* Add the item to the left or right of the provided event, if any, to the
* selection.
* @param {event} The current event item.
* @param {Number} offset Number of slices away from the event to look.
* @param {Selection} selection The selection to add an event to,
* if found.
* @return {boolean} Whether an event was found.
* @private
*/
addItemNearToProvidedEventToSelection: function(event, offset, selection) {
if (this.items_ === undefined)
return;
var items = this.items_;
var index = items.indexOf(event);
var newIndex = index + offset;
if (newIndex >= 0 && newIndex < items.length) {
var event = this.getModelEventFromItem(items[newIndex]);
if (event)
selection.push(event);
return true;
}
return false;
},
addAllObjectsMatchingFilterToSelection: function(filter, selection) {
},
addClosestEventToSelection: function(worldX, worldMaxDist, loY, hiY,
selection) {
if (this.items_ === undefined)
return;
var item = tv.b.findClosestElementInSortedArray(
this.items_,
function(x) { return x.start; },
worldX,
worldMaxDist);
if (!item)
return;
var event = this.getModelEventFromItem(item);
if (event)
selection.push(event);
}
};
return {
LetterDotTrack: LetterDotTrack
};
});
</script>