Home | History | Annotate | Download | only in tracks
      1 <!DOCTYPE html>
      2 <!--
      3 Copyright (c) 2015 The Chromium Authors. All rights reserved.
      4 Use of this source code is governed by a BSD-style license that can be
      5 found in the LICENSE file.
      6 -->
      7 
      8 <link rel="import" href="/base/sorted_array_utils.html">
      9 <link rel="import" href="/model/proxy_selectable_item.html">
     10 <link rel="import" href="/ui/base/color_scheme.html">
     11 <link rel="import" href="/ui/base/event_presenter.html">
     12 <link rel="import" href="/ui/base/heading.html">
     13 <link rel="import" href="/ui/base/ui.html">
     14 <link rel="import" href="/ui/tracks/track.html">
     15 
     16 <style>
     17 .letter-dot-track {
     18   height: 18px;
     19 }
     20 </style>
     21 
     22 <script>
     23 'use strict';
     24 
     25 tr.exportTo('tr.ui.tracks', function() {
     26   var EventPresenter = tr.ui.b.EventPresenter;
     27   var SelectionState = tr.model.SelectionState;
     28 
     29   /**
     30    * A track that displays an array of dots with filled letters inside them.
     31    * @constructor
     32    * @extends {Track}
     33    */
     34   var LetterDotTrack = tr.ui.b.define(
     35       'letter-dot-track', tr.ui.tracks.Track);
     36 
     37   LetterDotTrack.prototype = {
     38     __proto__: tr.ui.tracks.Track.prototype,
     39 
     40     decorate: function(viewport) {
     41       tr.ui.tracks.Track.prototype.decorate.call(this, viewport);
     42       this.classList.add('letter-dot-track');
     43       this.items_ = undefined;
     44 
     45       this.heading_ = document.createElement('tr-ui-heading');
     46       this.appendChild(this.heading_);
     47     },
     48 
     49     set heading(heading) {
     50       this.heading_.heading = heading;
     51     },
     52 
     53     get heading() {
     54       return this.heading_.heading;
     55     },
     56 
     57     set tooltip(tooltip) {
     58       this.heading_.tooltip = tooltip;
     59     },
     60 
     61     get items() {
     62       return this.items_;
     63     },
     64 
     65     set items(items) {
     66       this.items_ = items;
     67       this.invalidateDrawingContainer();
     68     },
     69 
     70     get height() {
     71       return window.getComputedStyle(this).height;
     72     },
     73 
     74     set height(height) {
     75       this.style.height = height;
     76     },
     77 
     78     get dumpRadiusView() {
     79       return 7 * (window.devicePixelRatio || 1);
     80     },
     81 
     82     draw: function(type, viewLWorld, viewRWorld) {
     83       if (this.items_ === undefined)
     84         return;
     85       switch (type) {
     86         case tr.ui.tracks.DrawType.GENERAL_EVENT:
     87           this.drawLetterDots_(viewLWorld, viewRWorld);
     88           break;
     89       }
     90     },
     91 
     92     drawLetterDots_: function(viewLWorld, viewRWorld) {
     93       var ctx = this.context();
     94       var pixelRatio = window.devicePixelRatio || 1;
     95 
     96       var bounds = this.getBoundingClientRect();
     97       var height = bounds.height * pixelRatio;
     98       var halfHeight = height * 0.5;
     99       var twoPi = Math.PI * 2;
    100       var palette = tr.ui.b.getColorPalette();
    101       var highlightIdBoost = tr.ui.b.paletteProperties.highlightIdBoost;
    102 
    103       // Culling parameters.
    104       var dt = this.viewport.currentDisplayTransform;
    105       var dumpRadiusView = this.dumpRadiusView;
    106       var itemRadiusWorld = dt.xViewVectorToWorld(height);
    107 
    108       // Draw the memory dumps.
    109       var items = this.items_;
    110       var loI = tr.b.findLowIndexInSortedArray(
    111           items,
    112           function(item) { return item.start; },
    113           viewLWorld);
    114 
    115       var oldFont = ctx.font;
    116       ctx.font = '400 ' + Math.floor(9 * pixelRatio) + 'px Arial';
    117       ctx.strokeStyle = 'rgb(0,0,0)';
    118       ctx.textBaseline = 'middle';
    119       ctx.textAlign = 'center';
    120 
    121       var drawItems = function(selected) {
    122         for (var i = loI; i < items.length; ++i) {
    123           var item = items[i];
    124           var x = item.start;
    125           if (x - itemRadiusWorld > viewRWorld)
    126             break;
    127           if (item.selected !== selected)
    128             continue;
    129           var xView = dt.xWorldToView(x);
    130 
    131           ctx.fillStyle = EventPresenter.getSelectableItemColor(item);
    132           ctx.beginPath();
    133           ctx.arc(xView, halfHeight, dumpRadiusView + 0.5, 0, twoPi);
    134           ctx.fill();
    135           if (item.selected) {
    136             ctx.lineWidth = 3;
    137             ctx.strokeStyle = 'rgb(100,100,0)';
    138             ctx.stroke();
    139 
    140             ctx.beginPath();
    141             ctx.arc(xView, halfHeight, dumpRadiusView, 0, twoPi);
    142             ctx.lineWidth = 1.5;
    143             ctx.strokeStyle = 'rgb(255,255,0)';
    144             ctx.stroke();
    145           } else {
    146             ctx.lineWidth = 1;
    147             ctx.strokeStyle = 'rgb(0,0,0)';
    148             ctx.stroke();
    149           }
    150 
    151           ctx.fillStyle = 'rgb(255, 255, 255)';
    152           ctx.fillText(item.dotLetter, xView, halfHeight);
    153         }
    154       };
    155 
    156       // Draw unselected items first to make sure they don't occlude selected
    157       // items.
    158       drawItems(false);
    159       drawItems(true);
    160 
    161       ctx.lineWidth = 1;
    162       ctx.font = oldFont;
    163     },
    164 
    165     addEventsToTrackMap: function(eventToTrackMap) {
    166       if (this.items_ === undefined)
    167         return;
    168 
    169       this.items_.forEach(function(item) {
    170         item.addToTrackMap(eventToTrackMap, this);
    171       }, this);
    172     },
    173 
    174     addIntersectingEventsInRangeToSelectionInWorldSpace: function(
    175         loWX, hiWX, viewPixWidthWorld, selection) {
    176       if (this.items_ === undefined)
    177         return;
    178 
    179       var itemRadiusWorld = viewPixWidthWorld * this.dumpRadiusView;
    180       tr.b.iterateOverIntersectingIntervals(
    181           this.items_,
    182           function(x) { return x.start - itemRadiusWorld; },
    183           function(x) { return 2 * itemRadiusWorld; },
    184           loWX, hiWX,
    185           function(item) {
    186             item.addToSelection(selection);
    187           }.bind(this));
    188     },
    189 
    190     /**
    191      * Add the item to the left or right of the provided event, if any, to the
    192      * selection.
    193      * @param {event} The current event item.
    194      * @param {Number} offset Number of slices away from the event to look.
    195      * @param {Selection} selection The selection to add an event to,
    196      * if found.
    197      * @return {boolean} Whether an event was found.
    198      * @private
    199      */
    200     addEventNearToProvidedEventToSelection: function(event, offset, selection) {
    201       if (this.items_ === undefined)
    202         return;
    203 
    204       var items = this.items_;
    205       var index = tr.b.findFirstIndexInArray(items, function(item) {
    206         return item.modelItem === event;
    207       });
    208       if (index === -1)
    209         return false;
    210 
    211       var newIndex = index + offset;
    212       if (newIndex >= 0 && newIndex < items.length) {
    213         items[newIndex].addToSelection(selection);
    214         return true;
    215       }
    216       return false;
    217     },
    218 
    219     addAllEventsMatchingFilterToSelection: function(filter, selection) {
    220     },
    221 
    222     addClosestEventToSelection: function(worldX, worldMaxDist, loY, hiY,
    223                                          selection) {
    224       if (this.items_ === undefined)
    225         return;
    226 
    227       var item = tr.b.findClosestElementInSortedArray(
    228           this.items_,
    229           function(x) { return x.start; },
    230           worldX,
    231           worldMaxDist);
    232 
    233       if (!item)
    234         return;
    235 
    236       item.addToSelection(selection);
    237     }
    238   };
    239 
    240   /**
    241    * A filled dot with a letter inside it.
    242    *
    243    * @constructor
    244    * @extends {ProxySelectableItem}
    245    */
    246   function LetterDot(modelItem, dotLetter, colorId, start) {
    247     tr.model.ProxySelectableItem.call(this, modelItem);
    248     this.dotLetter = dotLetter;
    249     this.colorId = colorId;
    250     this.start = start;
    251   };
    252 
    253   LetterDot.prototype = {
    254     __proto__: tr.model.ProxySelectableItem.prototype
    255   };
    256 
    257   return {
    258     LetterDotTrack: LetterDotTrack,
    259     LetterDot: LetterDot
    260   };
    261 });
    262 </script>
    263