• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {raf} from '../core/raf_scheduler';
16
17export enum OmniboxMode {
18  Search,
19  Query,
20  Command,
21  Prompt,
22}
23
24export interface PromptOption {
25  key: string;
26  displayName: string;
27}
28
29interface Prompt {
30  text: string;
31  options?: PromptOption[];
32  resolve(result: string): void;
33  reject(): void;
34}
35
36const defaultMode = OmniboxMode.Search;
37
38export class OmniboxManager {
39  private _omniboxMode = defaultMode;
40  private _focusOmniboxNextRender = false;
41  private _pendingCursorPlacement?: number;
42  private _pendingPrompt?: Prompt;
43  private _text = '';
44  private _omniboxSelectionIndex = 0;
45
46  get omniboxMode(): OmniboxMode {
47    return this._omniboxMode;
48  }
49
50  get pendingPrompt(): Prompt | undefined {
51    return this._pendingPrompt;
52  }
53
54  get text(): string {
55    return this._text;
56  }
57
58  get omniboxSelectionIndex(): number {
59    return this._omniboxSelectionIndex;
60  }
61
62  get focusOmniboxNextRender(): boolean {
63    return this._focusOmniboxNextRender;
64  }
65
66  get pendingCursorPlacement(): number | undefined {
67    return this._pendingCursorPlacement;
68  }
69
70  setText(value: string): void {
71    this._text = value;
72  }
73
74  setOmniboxSelectionIndex(index: number): void {
75    this._omniboxSelectionIndex = index;
76  }
77
78  focusOmnibox(cursorPlacement?: number): void {
79    this._focusOmniboxNextRender = true;
80    this._pendingCursorPlacement = cursorPlacement;
81    raf.scheduleFullRedraw();
82  }
83
84  clearOmniboxFocusFlag(): void {
85    this._focusOmniboxNextRender = false;
86    this._pendingCursorPlacement = undefined;
87  }
88
89  setMode(mode: OmniboxMode): void {
90    this._omniboxMode = mode;
91    this._focusOmniboxNextRender = true;
92    this.resetOmniboxText();
93    this.rejectPendingPrompt();
94    raf.scheduleFullRedraw();
95  }
96
97  // Start a prompt. If options are supplied, the user must pick one from the
98  // list, otherwise the input is free-form text.
99  prompt(text: string, options?: PromptOption[]): Promise<string> {
100    this._omniboxMode = OmniboxMode.Prompt;
101    this.resetOmniboxText();
102    this.rejectPendingPrompt();
103
104    const promise = new Promise<string>((resolve, reject) => {
105      this._pendingPrompt = {
106        text,
107        options,
108        resolve,
109        reject,
110      };
111    });
112
113    this._focusOmniboxNextRender = true;
114    raf.scheduleFullRedraw();
115
116    return promise;
117  }
118
119  // Resolve the pending prompt with a value to return to the prompter.
120  resolvePrompt(value: string): void {
121    if (this._pendingPrompt) {
122      this._pendingPrompt.resolve(value);
123      this._pendingPrompt = undefined;
124    }
125    this.setMode(OmniboxMode.Search);
126  }
127
128  // Reject the prompt outright. Doing this will force the owner of the prompt
129  // promise to catch, so only do this when things go seriously wrong.
130  // Use |resolvePrompt(null)| to indicate cancellation.
131  rejectPrompt(): void {
132    if (this._pendingPrompt) {
133      this._pendingPrompt.reject();
134      this._pendingPrompt = undefined;
135    }
136    this.setMode(OmniboxMode.Search);
137  }
138
139  reset(): void {
140    this.setMode(defaultMode);
141    this.resetOmniboxText();
142    raf.scheduleFullRedraw();
143  }
144
145  private rejectPendingPrompt() {
146    if (this._pendingPrompt) {
147      this._pendingPrompt.reject();
148      this._pendingPrompt = undefined;
149    }
150  }
151
152  private resetOmniboxText() {
153    this._text = '';
154    this._omniboxSelectionIndex = 0;
155  }
156}
157