blob: 923b6a32c2b05ac4ecaedf991d388e968670a636 [file] [log] [blame]
Primiano Tucci21b91bf2018-08-06 16:42:07 +01001// 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 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 * as m from 'mithril';
Primiano Tucci8afc06d2018-08-06 19:11:42 +010016
17import {deleteQuery, executeQuery} from '../common/actions';
18import {QueryResponse} from '../common/queries';
19import {EngineConfig} from '../common/state';
20
21import {globals} from './globals';
22
23const QUERY_ID = 'quicksearch';
Primiano Tucci21b91bf2018-08-06 16:42:07 +010024
25let selResult = 0;
26let numResults = 0;
27let mode: 'search'|'command' = 'search';
28
29function clearOmniboxResults() {
Primiano Tucci8afc06d2018-08-06 19:11:42 +010030 globals.queryResults.delete(QUERY_ID);
31 globals.dispatch(deleteQuery(QUERY_ID));
Primiano Tucci21b91bf2018-08-06 16:42:07 +010032}
33
34function onKeyDown(e: Event) {
35 e.stopPropagation();
36 const key = (e as KeyboardEvent).key;
37
38 // Avoid that the global 'a', 'd', 'w', 's' handler sees these keystrokes.
39 // TODO: this seems a bug in the pan_and_zoom_handler.ts.
40 if (key === 'ArrowUp' || key === 'ArrowDown') {
41 e.preventDefault();
42 return;
43 }
Primiano Tucci8afc06d2018-08-06 19:11:42 +010044 const txt = (e.target as HTMLInputElement);
Primiano Tucci21b91bf2018-08-06 16:42:07 +010045 if (key === ':' && txt.value === '') {
46 mode = 'command';
47 m.redraw();
48 e.preventDefault();
49 return;
50 }
51 if (key === 'Escape' && mode === 'command') {
52 txt.value = '';
53 mode = 'search';
54 m.redraw();
55 return;
56 }
57 if (key === 'Backspace' && txt.value.length === 0 && mode === 'command') {
58 mode = 'search';
59 m.redraw();
60 return;
61 }
Primiano Tucci21b91bf2018-08-06 16:42:07 +010062}
63
64function onKeyUp(e: Event) {
65 e.stopPropagation();
66 const key = (e as KeyboardEvent).key;
67 const txt = e.target as HTMLInputElement;
68 if (key === 'ArrowUp' || key === 'ArrowDown') {
69 selResult += (key === 'ArrowUp') ? -1 : 1;
70 selResult = Math.max(selResult, 0);
71 selResult = Math.min(selResult, numResults - 1);
72 e.preventDefault();
73 m.redraw();
74 return;
75 }
76 if (txt.value.length <= 0 || key === 'Escape') {
77 clearOmniboxResults();
78 m.redraw();
79 return;
80 }
Primiano Tucci8afc06d2018-08-06 19:11:42 +010081 if (mode === 'search') {
82 const name = txt.value.replace(/'/g, '\\\'').replace(/[*]/g, '%');
Hector Dearman12323362018-08-09 16:09:28 +010083 const query = `select str from strings where str like '%${name}%' limit 10`;
Primiano Tucci8afc06d2018-08-06 19:11:42 +010084 globals.dispatch(executeQuery('0', QUERY_ID, query));
85 }
86 if (mode === 'command' && key === 'Enter') {
87 globals.dispatch(executeQuery('0', 'command', txt.value));
88 }
Primiano Tucci21b91bf2018-08-06 16:42:07 +010089}
90
91
92const Omnibox: m.Component = {
93 oncreate(vnode) {
94 const txt = vnode.dom.querySelector('input') as HTMLInputElement;
95 txt.addEventListener('blur', clearOmniboxResults);
96 txt.addEventListener('keydown', onKeyDown);
97 txt.addEventListener('keyup', onKeyUp);
98 },
99 view() {
Primiano Tuccie36ca632018-08-21 14:32:23 +0200100 const msgTTL = globals.state.status.timestamp + 3 - Date.now() / 1e3;
101 let enginesAreBusy = false;
102 for (const engine of Object.values(globals.state.engines)) {
103 enginesAreBusy = enginesAreBusy || !engine.ready;
104 }
105
106 if (msgTTL > 0 || enginesAreBusy) {
107 setTimeout(() => m.redraw(), msgTTL * 1000);
108 return m(
109 `.omnibox.message-mode`,
110 m(`input[placeholder=${globals.state.status.msg}][readonly]`));
111 }
112
Primiano Tucci21b91bf2018-08-06 16:42:07 +0100113 // TODO(primiano): handle query results here.
Primiano Tucci8afc06d2018-08-06 19:11:42 +0100114 const results = [];
115 const resp = globals.queryResults.get(QUERY_ID) as QueryResponse;
116 if (resp !== undefined) {
117 numResults = resp.rows ? resp.rows.length : 0;
118 for (let i = 0; i < resp.rows.length; i++) {
119 const clazz = (i === selResult) ? '.selected' : '';
120 results.push(m(`div${clazz}`, resp.rows[i][resp.columns[0]]));
121 }
122 }
Primiano Tucci21b91bf2018-08-06 16:42:07 +0100123 const placeholder = {
124 search: 'Search or type : to enter command mode',
125 command: 'e.g., select * from sched left join thread using(utid) limit 10'
126 };
Primiano Tuccie36ca632018-08-21 14:32:23 +0200127
Primiano Tucci21b91bf2018-08-06 16:42:07 +0100128 const commandMode = mode === 'command';
129 return m(
130 `.omnibox${commandMode ? '.command-mode' : ''}`,
Primiano Tuccie36ca632018-08-21 14:32:23 +0200131 m(`input[placeholder=${placeholder[mode]}]`),
Primiano Tucci8afc06d2018-08-06 19:11:42 +0100132 m('.omnibox-results', results));
Primiano Tucci21b91bf2018-08-06 16:42:07 +0100133 },
134};
135
136export const Topbar: m.Component = {
137 view() {
Primiano Tucci8afc06d2018-08-06 19:11:42 +0100138 const progBar = [];
139 const engine: EngineConfig = globals.state.engines['0'];
140 if (globals.state.queries[QUERY_ID] !== undefined ||
141 (engine !== undefined && !engine.ready)) {
142 progBar.push(m('.progress'));
143 }
144
145 return m('.topbar', m(Omnibox), ...progBar);
Primiano Tucci21b91bf2018-08-06 16:42:07 +0100146 },
147};