• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2018 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use size file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import m from 'mithril';
16
17import {Time, time, toISODateOnly} from '../base/time';
18import {TimestampFormat, timestampFormat} from '../core/timestamp_format';
19
20import {TRACK_SHELL_WIDTH} from './css_constants';
21import {globals} from './globals';
22import {
23  getMaxMajorTicks,
24  MIN_PX_PER_STEP,
25  TickGenerator,
26  TickType,
27  timeScaleForVisibleWindow,
28} from './gridline_helper';
29import {PanelSize} from './panel';
30import {Panel} from './panel_container';
31
32export class TimeAxisPanel implements Panel {
33  readonly kind = 'panel';
34  readonly selectable = false;
35
36  render(): m.Children {
37    return m('.time-axis-panel');
38  }
39
40  renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
41    ctx.fillStyle = '#999';
42    ctx.textAlign = 'left';
43    ctx.font = '11px Roboto Condensed';
44
45    const offset = globals.timestampOffset();
46    switch (timestampFormat()) {
47      case TimestampFormat.Raw:
48      case TimestampFormat.RawLocale:
49        break;
50      case TimestampFormat.Seconds:
51      case TimestampFormat.Timecode:
52        const width = renderTimestamp(ctx, offset, 6, 10, MIN_PX_PER_STEP);
53        ctx.fillText('+', 6 + width + 2, 10, 6);
54        break;
55      case TimestampFormat.UTC:
56        const offsetDate = Time.toDate(
57          globals.traceContext.utcOffset,
58          globals.traceContext.realtimeOffset,
59        );
60        const dateStr = toISODateOnly(offsetDate);
61        ctx.fillText(`UTC ${dateStr}`, 6, 10);
62        break;
63      case TimestampFormat.TraceTz:
64        const offsetTzDate = Time.toDate(
65          globals.traceContext.traceTzOffset,
66          globals.traceContext.realtimeOffset,
67        );
68        const dateTzStr = toISODateOnly(offsetTzDate);
69        ctx.fillText(dateTzStr, 6, 10);
70        break;
71    }
72
73    ctx.save();
74    ctx.beginPath();
75    ctx.rect(TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
76    ctx.clip();
77
78    // Draw time axis.
79    const span = globals.timeline.visibleTimeSpan;
80    if (size.width > TRACK_SHELL_WIDTH && span.duration > 0n) {
81      const maxMajorTicks = getMaxMajorTicks(size.width - TRACK_SHELL_WIDTH);
82      const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
83
84      const tickGen = new TickGenerator(span, maxMajorTicks, offset);
85      for (const {type, time} of tickGen) {
86        if (type === TickType.MAJOR) {
87          const position = Math.floor(map.timeToPx(time));
88          ctx.fillRect(position, 0, 1, size.height);
89          const domainTime = globals.toDomainTime(time);
90          renderTimestamp(ctx, domainTime, position + 5, 10, MIN_PX_PER_STEP);
91        }
92      }
93    }
94    ctx.restore();
95    ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
96  }
97}
98
99function renderTimestamp(
100  ctx: CanvasRenderingContext2D,
101  time: time,
102  x: number,
103  y: number,
104  minWidth: number,
105) {
106  const fmt = timestampFormat();
107  switch (fmt) {
108    case TimestampFormat.UTC:
109    case TimestampFormat.TraceTz:
110    case TimestampFormat.Timecode:
111      return renderTimecode(ctx, time, x, y, minWidth);
112    case TimestampFormat.Raw:
113      return renderRawTimestamp(ctx, time.toString(), x, y, minWidth);
114    case TimestampFormat.RawLocale:
115      return renderRawTimestamp(ctx, time.toLocaleString(), x, y, minWidth);
116    case TimestampFormat.Seconds:
117      return renderRawTimestamp(ctx, Time.formatSeconds(time), x, y, minWidth);
118    default:
119      const z: never = fmt;
120      throw new Error(`Invalid timestamp ${z}`);
121  }
122}
123
124// Print a time on the canvas in raw format.
125function renderRawTimestamp(
126  ctx: CanvasRenderingContext2D,
127  time: string,
128  x: number,
129  y: number,
130  minWidth: number,
131) {
132  ctx.font = '11px Roboto Condensed';
133  ctx.fillText(time, x, y, minWidth);
134  return ctx.measureText(time).width;
135}
136
137// Print a timecode over 2 lines with this formatting:
138// DdHH:MM:SS
139// mmm uuu nnn
140// Returns the resultant width of the timecode.
141function renderTimecode(
142  ctx: CanvasRenderingContext2D,
143  time: time,
144  x: number,
145  y: number,
146  minWidth: number,
147): number {
148  const timecode = Time.toTimecode(time);
149  ctx.font = '11px Roboto Condensed';
150
151  const {dhhmmss} = timecode;
152  const thinSpace = '\u2009';
153  const subsec = timecode.subsec(thinSpace);
154  ctx.fillText(dhhmmss, x, y, minWidth);
155  const {width: firstRowWidth} = ctx.measureText(subsec);
156
157  ctx.font = '10.5px Roboto Condensed';
158  ctx.fillText(subsec, x, y + 10, minWidth);
159  const {width: secondRowWidth} = ctx.measureText(subsec);
160
161  return Math.max(firstRowWidth, secondRowWidth);
162}
163