• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2023 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this 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 {copyToClipboard} from '../../base/clipboard';
18import {Icons} from '../../base/semantic_icons';
19import {Duration, duration} from '../../base/time';
20import {
21  DurationPrecision,
22  durationPrecision,
23  setDurationPrecision,
24  TimestampFormat,
25  timestampFormat,
26} from '../../core/timestamp_format';
27import {raf} from '../../core/raf_scheduler';
28import {Anchor} from '../../widgets/anchor';
29import {MenuDivider, MenuItem, PopupMenu2} from '../../widgets/menu';
30
31import {menuItemForFormat} from './timestamp';
32
33interface DurationWidgetAttrs {
34  dur: duration;
35  extraMenuItems?: m.Child[];
36}
37
38export class DurationWidget implements m.ClassComponent<DurationWidgetAttrs> {
39  view({attrs}: m.Vnode<DurationWidgetAttrs>) {
40    const {dur} = attrs;
41    if (dur === -1n) {
42      return '(Did not end)';
43    }
44    return m(
45      PopupMenu2,
46      {
47        trigger: m(Anchor, renderDuration(dur)),
48      },
49      m(MenuItem, {
50        icon: Icons.Copy,
51        label: `Copy raw value`,
52        onclick: () => {
53          copyToClipboard(dur.toString());
54        },
55      }),
56      m(
57        MenuItem,
58        {
59          label: 'Set time format',
60        },
61        menuItemForFormat(TimestampFormat.Timecode, 'Timecode'),
62        menuItemForFormat(TimestampFormat.UTC, 'Realtime (UTC)'),
63        menuItemForFormat(TimestampFormat.TraceTz, 'Realtime (Trace TZ)'),
64        menuItemForFormat(TimestampFormat.Seconds, 'Seconds'),
65        menuItemForFormat(TimestampFormat.Raw, 'Raw'),
66        menuItemForFormat(
67          TimestampFormat.RawLocale,
68          'Raw (with locale-specific formatting)',
69        ),
70      ),
71      m(
72        MenuItem,
73        {
74          label: 'Duration precision',
75          disabled: !durationPrecisionHasEffect(),
76          title: 'Not configurable with current time format',
77        },
78        menuItemForPrecision(DurationPrecision.Full, 'Full'),
79        menuItemForPrecision(DurationPrecision.HumanReadable, 'Human readable'),
80      ),
81      attrs.extraMenuItems ? [m(MenuDivider), attrs.extraMenuItems] : null,
82    );
83  }
84}
85
86function menuItemForPrecision(
87  value: DurationPrecision,
88  label: string,
89): m.Children {
90  return m(MenuItem, {
91    label,
92    active: value === durationPrecision(),
93    onclick: () => {
94      setDurationPrecision(value);
95      raf.scheduleFullRedraw();
96    },
97  });
98}
99
100function durationPrecisionHasEffect(): boolean {
101  switch (timestampFormat()) {
102    case TimestampFormat.Timecode:
103    case TimestampFormat.UTC:
104    case TimestampFormat.TraceTz:
105      return true;
106    default:
107      return false;
108  }
109}
110
111export function renderDuration(dur: duration): string {
112  const fmt = timestampFormat();
113  switch (fmt) {
114    case TimestampFormat.UTC:
115    case TimestampFormat.TraceTz:
116    case TimestampFormat.Timecode:
117      return renderFormattedDuration(dur);
118    case TimestampFormat.Raw:
119      return dur.toString();
120    case TimestampFormat.RawLocale:
121      return dur.toLocaleString();
122    case TimestampFormat.Seconds:
123      return Duration.formatSeconds(dur);
124    default:
125      const x: never = fmt;
126      throw new Error(`Invalid format ${x}`);
127  }
128}
129
130function renderFormattedDuration(dur: duration): string {
131  const fmt = durationPrecision();
132  switch (fmt) {
133    case DurationPrecision.HumanReadable:
134      return Duration.humanise(dur);
135    case DurationPrecision.Full:
136      return Duration.format(dur);
137    default:
138      const x: never = fmt;
139      throw new Error(`Invalid format ${x}`);
140  }
141}
142