Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 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 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 | |
| 15 | import * as m from 'mithril'; |
Primiano Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 16 | |
Hector Dearman | 5ae8247 | 2018-10-03 08:30:35 +0100 | [diff] [blame] | 17 | import {Actions, deleteQuery} from '../common/actions'; |
Primiano Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 18 | import {QueryResponse} from '../common/queries'; |
| 19 | import {EngineConfig} from '../common/state'; |
| 20 | |
| 21 | import {globals} from './globals'; |
| 22 | |
| 23 | const QUERY_ID = 'quicksearch'; |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 24 | |
| 25 | let selResult = 0; |
| 26 | let numResults = 0; |
| 27 | let mode: 'search'|'command' = 'search'; |
| 28 | |
| 29 | function clearOmniboxResults() { |
Primiano Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 30 | globals.queryResults.delete(QUERY_ID); |
| 31 | globals.dispatch(deleteQuery(QUERY_ID)); |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 32 | } |
| 33 | |
| 34 | function 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 Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 44 | const txt = (e.target as HTMLInputElement); |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 45 | if (key === ':' && txt.value === '') { |
| 46 | mode = 'command'; |
Deepanjan Roy | f190cb2 | 2018-08-28 10:43:07 -0400 | [diff] [blame] | 47 | globals.rafScheduler.scheduleFullRedraw(); |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 48 | e.preventDefault(); |
| 49 | return; |
| 50 | } |
| 51 | if (key === 'Escape' && mode === 'command') { |
| 52 | txt.value = ''; |
| 53 | mode = 'search'; |
Deepanjan Roy | f190cb2 | 2018-08-28 10:43:07 -0400 | [diff] [blame] | 54 | globals.rafScheduler.scheduleFullRedraw(); |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 55 | return; |
| 56 | } |
| 57 | if (key === 'Backspace' && txt.value.length === 0 && mode === 'command') { |
| 58 | mode = 'search'; |
Deepanjan Roy | f190cb2 | 2018-08-28 10:43:07 -0400 | [diff] [blame] | 59 | globals.rafScheduler.scheduleFullRedraw(); |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 60 | return; |
| 61 | } |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 62 | } |
| 63 | |
| 64 | function 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(); |
Deepanjan Roy | f190cb2 | 2018-08-28 10:43:07 -0400 | [diff] [blame] | 73 | globals.rafScheduler.scheduleFullRedraw(); |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 74 | return; |
| 75 | } |
| 76 | if (txt.value.length <= 0 || key === 'Escape') { |
| 77 | clearOmniboxResults(); |
Deepanjan Roy | f190cb2 | 2018-08-28 10:43:07 -0400 | [diff] [blame] | 78 | globals.rafScheduler.scheduleFullRedraw(); |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 79 | return; |
| 80 | } |
Primiano Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 81 | if (mode === 'search') { |
| 82 | const name = txt.value.replace(/'/g, '\\\'').replace(/[*]/g, '%'); |
Hector Dearman | 1232336 | 2018-08-09 16:09:28 +0100 | [diff] [blame] | 83 | const query = `select str from strings where str like '%${name}%' limit 10`; |
Hector Dearman | 5ae8247 | 2018-10-03 08:30:35 +0100 | [diff] [blame] | 84 | globals.dispatch( |
| 85 | Actions.executeQuery({engineId: '0', queryId: QUERY_ID, query})); |
Primiano Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 86 | } |
| 87 | if (mode === 'command' && key === 'Enter') { |
Hector Dearman | 5ae8247 | 2018-10-03 08:30:35 +0100 | [diff] [blame] | 88 | globals.dispatch(Actions.executeQuery( |
| 89 | {engineId: '0', queryId: 'command', query: txt.value})); |
Primiano Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 90 | } |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | |
Deepanjan Roy | 97f6324 | 2018-09-20 15:32:01 -0400 | [diff] [blame] | 94 | class Omnibox implements m.ClassComponent { |
| 95 | oncreate(vnode: m.VnodeDOM) { |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 96 | const txt = vnode.dom.querySelector('input') as HTMLInputElement; |
| 97 | txt.addEventListener('blur', clearOmniboxResults); |
| 98 | txt.addEventListener('keydown', onKeyDown); |
| 99 | txt.addEventListener('keyup', onKeyUp); |
Deepanjan Roy | 97f6324 | 2018-09-20 15:32:01 -0400 | [diff] [blame] | 100 | } |
| 101 | |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 102 | view() { |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 103 | const msgTTL = globals.state.status.timestamp + 3 - Date.now() / 1e3; |
| 104 | let enginesAreBusy = false; |
| 105 | for (const engine of Object.values(globals.state.engines)) { |
| 106 | enginesAreBusy = enginesAreBusy || !engine.ready; |
| 107 | } |
| 108 | |
| 109 | if (msgTTL > 0 || enginesAreBusy) { |
Deepanjan Roy | f190cb2 | 2018-08-28 10:43:07 -0400 | [diff] [blame] | 110 | setTimeout( |
| 111 | () => globals.rafScheduler.scheduleFullRedraw(), msgTTL * 1000); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 112 | return m( |
| 113 | `.omnibox.message-mode`, |
| 114 | m(`input[placeholder=${globals.state.status.msg}][readonly]`)); |
| 115 | } |
| 116 | |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 117 | // TODO(primiano): handle query results here. |
Primiano Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 118 | const results = []; |
| 119 | const resp = globals.queryResults.get(QUERY_ID) as QueryResponse; |
| 120 | if (resp !== undefined) { |
| 121 | numResults = resp.rows ? resp.rows.length : 0; |
| 122 | for (let i = 0; i < resp.rows.length; i++) { |
| 123 | const clazz = (i === selResult) ? '.selected' : ''; |
| 124 | results.push(m(`div${clazz}`, resp.rows[i][resp.columns[0]])); |
| 125 | } |
| 126 | } |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 127 | const placeholder = { |
| 128 | search: 'Search or type : to enter command mode', |
| 129 | command: 'e.g., select * from sched left join thread using(utid) limit 10' |
| 130 | }; |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 131 | |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 132 | const commandMode = mode === 'command'; |
| 133 | return m( |
| 134 | `.omnibox${commandMode ? '.command-mode' : ''}`, |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 135 | m(`input[placeholder=${placeholder[mode]}]`), |
Primiano Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 136 | m('.omnibox-results', results)); |
Deepanjan Roy | 97f6324 | 2018-09-20 15:32:01 -0400 | [diff] [blame] | 137 | } |
| 138 | } |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 139 | |
Deepanjan Roy | 97f6324 | 2018-09-20 15:32:01 -0400 | [diff] [blame] | 140 | export class Topbar implements m.ClassComponent { |
Primiano Tucci | 21b91bf | 2018-08-06 16:42:07 +0100 | [diff] [blame] | 141 | view() { |
Primiano Tucci | 8afc06d | 2018-08-06 19:11:42 +0100 | [diff] [blame] | 142 | const progBar = []; |
| 143 | const engine: EngineConfig = globals.state.engines['0']; |
| 144 | if (globals.state.queries[QUERY_ID] !== undefined || |
| 145 | (engine !== undefined && !engine.ready)) { |
| 146 | progBar.push(m('.progress')); |
| 147 | } |
Deepanjan Roy | 97f6324 | 2018-09-20 15:32:01 -0400 | [diff] [blame] | 148 | return m('.topbar', m(Omnibox), ...progBar); |
| 149 | } |
| 150 | } |