xref: /aosp_15_r20/external/perfetto/ui/src/core/note_manager.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2024 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 {
16  AddNoteArgs,
17  AddSpanNoteArgs,
18  Note,
19  NoteManager,
20  SpanNote,
21} from '../public/note';
22import {randomColor} from '../components/colorizer';
23import {raf} from './raf_scheduler';
24
25export class NoteManagerImpl implements NoteManager {
26  private _lastNodeId = 0;
27  private _notes = new Map<string, Note | SpanNote>();
28
29  // This function is wired up to clear the SelectionManager state if the
30  // current selection is a note.
31  // TODO(primiano): figure out some better (de-)coupling here.
32  // We cannot pass SelectionManager in our constructor because doing so would
33  // create a cyclic ctor dependency (SelectionManager requires NoteManager in
34  // its ctor). There is a 2way logical dependency between NoteManager and
35  // SelectionManager:
36  // 1. SM needs NM to handle SM.findTimeRangeOfSelection(), for [M]ark.
37  // 2. NM needs SM to tell it that a note has been delete and should be
38  //   deselected if it was currently selected.
39  onNoteDeleted?: (nodeId: string) => void;
40
41  get notes(): ReadonlyMap<string, Note | SpanNote> {
42    return this._notes;
43  }
44
45  getNote(id: string): Note | SpanNote | undefined {
46    return this._notes.get(id);
47  }
48
49  addNote(args: AddNoteArgs): string {
50    const id = args.id ?? `note_${++this._lastNodeId}`;
51    this._notes.set(id, {
52      ...args,
53      noteType: 'DEFAULT',
54      id,
55      color: args.color ?? randomColor(),
56      text: args.text ?? '',
57    });
58    raf.scheduleFullRedraw();
59    return id;
60  }
61
62  addSpanNote(args: AddSpanNoteArgs): string {
63    const id = args.id ?? `note_${++this._lastNodeId}`;
64    this._notes.set(id, {
65      ...args,
66      noteType: 'SPAN',
67      id,
68      color: args.color ?? randomColor(),
69      text: args.text ?? '',
70    });
71    raf.scheduleFullRedraw();
72    return id;
73  }
74
75  changeNote(id: string, args: {color?: string; text?: string}) {
76    const note = this._notes.get(id);
77    if (note === undefined) return;
78
79    this._notes.set(id, {
80      ...note,
81      color: args.color ?? note.color,
82      text: args.text ?? note.text,
83    });
84    raf.scheduleFullRedraw();
85  }
86
87  removeNote(id: string) {
88    raf.scheduleFullRedraw();
89    this._notes.delete(id);
90    this.onNoteDeleted?.(id);
91  }
92}
93