1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | "use strict" ;
|
28 |
|
29 |
|
30 |
|
31 | const Promise = require( 'seventh' ) ;
|
32 | const TextBox = require( './TextBox.js' ) ;
|
33 | const EditableTextBox = require( './EditableTextBox.js' ) ;
|
34 | const RowMenu = require( './RowMenu.js' ) ;
|
35 | const string = require( 'string-kit' ) ;
|
36 | const computeAutoCompleteArray = require( '../autoComplete.js' ) ;
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | function InlineInput( options ) {
|
59 |
|
60 | options = ! options ? {} : options.internal ? options : Object.create( options ) ;
|
61 | options.internal = true ;
|
62 |
|
63 | if ( options.value ) { options.content = options.value ; }
|
64 |
|
65 |
|
66 | options.outputHeight = 1 ;
|
67 |
|
68 |
|
69 | options.scrollable = options.hasVScrollBar = options.hasHScrollBar = options.extraScrolling = false ;
|
70 | options.scrollX = options.scrollY = 0 ;
|
71 |
|
72 |
|
73 | options.lineWrap = true ;
|
74 |
|
75 | this.onAutoCompleteMenuSubmit = this.onAutoCompleteMenuSubmit.bind( this ) ;
|
76 | this.onAutoCompleteMenuCancel = this.onAutoCompleteMenuCancel.bind( this ) ;
|
77 |
|
78 | this.promptTextBox = null ;
|
79 |
|
80 | if ( options.prompt ) {
|
81 | this.promptTextBox = new TextBox( Object.assign(
|
82 | {
|
83 | textAttr: options.textAttr
|
84 | } ,
|
85 | options.prompt ,
|
86 | {
|
87 | internal: true ,
|
88 |
|
89 | outputX: options.outputX || options.x ,
|
90 | outputY: options.outputY || options.y ,
|
91 | outputWidth: options.outputWidth || options.width ,
|
92 | outputHeight: options.outputHeight || options.height ,
|
93 | lineWrap: options.lineWrap ,
|
94 | wordWrap: options.wordWrap || options.wordwrap
|
95 | }
|
96 | ) ) ;
|
97 |
|
98 |
|
99 | this.promptTextBox.textBuffer.setVoidAttr( null ) ;
|
100 |
|
101 | let size = this.promptTextBox.getContentSize() ;
|
102 | this.promptTextBox.setSizeAndPosition( size ) ;
|
103 |
|
104 | if ( size.height > 1 ) {
|
105 | options.outputY = ( options.outputY || options.y ) + size.height - 1 ;
|
106 | options.firstLineRightShift = this.promptTextBox.textBuffer.buffer[ this.promptTextBox.textBuffer.buffer.length - 1 ].length ;
|
107 | }
|
108 | else {
|
109 | options.firstLineRightShift = size.width ;
|
110 | }
|
111 | }
|
112 |
|
113 | EditableTextBox.call( this , options ) ;
|
114 |
|
115 |
|
116 | this.history = options.history ;
|
117 | this.contentArray = options.history ? [ ... options.history , this.content ] : [ this.content ] ;
|
118 | this.contentIndex = this.contentArray.length - 1 ;
|
119 |
|
120 | this.disabled = !! options.disabled ;
|
121 | this.submitted = !! options.submitted ;
|
122 | this.cancelable = !! options.cancelable ;
|
123 | this.canceled = !! options.canceled ;
|
124 |
|
125 | this.autoComplete = options.autoComplete ;
|
126 | this.useAutoCompleteHint = !! ( this.autoComplete && ( options.useAutoCompleteHint || options.autoCompleteHint ) ) ;
|
127 | this.useAutoCompleteMenu = !! ( this.autoComplete && ( options.useAutoCompleteMenu || options.autoCompleteMenu ) ) ;
|
128 | this.autoCompleteMenu = null ;
|
129 | this.autoCompleteLeftPart = null ;
|
130 | this.autoCompleteRightPart = null ;
|
131 |
|
132 | this.menuOptions = Object.assign( {} , this.defaultMenuOptions , options.menu ) ;
|
133 |
|
134 | this.placeholder = options.placeholder ;
|
135 | this.placeholderHasMarkup = !! options.placeholderHasMarkup ;
|
136 |
|
137 | if ( this.placeholder ) {
|
138 | this.setAltContent( this.placeholder , this.placeholderHasMarkup ) ;
|
139 | }
|
140 |
|
141 | if ( this.promptTextBox ) {
|
142 | this.attach( this.promptTextBox ) ;
|
143 | }
|
144 |
|
145 |
|
146 | if ( this.elementType === 'InlineInput' && ! options.noDraw ) { this.draw() ; }
|
147 | }
|
148 |
|
149 | module.exports = InlineInput ;
|
150 |
|
151 | InlineInput.prototype = Object.create( EditableTextBox.prototype ) ;
|
152 | InlineInput.prototype.constructor = InlineInput ;
|
153 | InlineInput.prototype.elementType = 'InlineInput' ;
|
154 |
|
155 |
|
156 | InlineInput.prototype.useAltTextBuffer = true ;
|
157 |
|
158 |
|
159 |
|
160 | InlineInput.prototype.defaultMenuOptions = {
|
161 | buttonBlurAttr: { bgColor: 'default' , color: 'default' } ,
|
162 | buttonFocusAttr: { bgColor: 'green' , color: 'blue' , dim: true } ,
|
163 | buttonDisabledAttr: { bgColor: 'white' , color: 'brightBlack' } ,
|
164 | buttonSubmittedAttr: { bgColor: 'brightWhite' , color: 'brightBlack' } ,
|
165 | buttonSeparatorAttr: { bgColor: 'default' } ,
|
166 | backgroundAttr: { bgColor: 'default' } ,
|
167 |
|
168 | justify: true ,
|
169 | keyBindings: Object.assign( {} , RowMenu.prototype.keyBindings , {
|
170 | TAB: 'next' ,
|
171 | SHIFT_TAB: 'previous'
|
172 | } )
|
173 | } ;
|
174 |
|
175 |
|
176 |
|
177 | InlineInput.prototype.destroy = function( isSubDestroy ) {
|
178 | EditableTextBox.prototype.destroy.call( this , isSubDestroy ) ;
|
179 | } ;
|
180 |
|
181 |
|
182 |
|
183 | InlineInput.prototype.keyBindings = {
|
184 | ENTER: 'submit' ,
|
185 | KP_ENTER: 'submit' ,
|
186 | ESCAPE: 'cancel' ,
|
187 | TAB: 'autoComplete' ,
|
188 | CTRL_R: 'historyAutoComplete' ,
|
189 | UP: 'historyPrevious' ,
|
190 | DOWN: 'historyNext' ,
|
191 | BACKSPACE: 'backDelete' ,
|
192 | DELETE: 'delete' ,
|
193 | LEFT: 'backward' ,
|
194 | RIGHT: 'forward' ,
|
195 | CTRL_LEFT: 'startOfWord' ,
|
196 | CTRL_RIGHT: 'endOfWord' ,
|
197 | HOME: 'startOfLine' ,
|
198 | END: 'endOfLine' ,
|
199 | CTRL_O: 'copyClipboard' ,
|
200 | CTRL_P: 'pasteClipboard'
|
201 | } ;
|
202 |
|
203 |
|
204 |
|
205 | InlineInput.prototype.preDrawSelf = function() {
|
206 | if ( this.promptTextBuffer ) {
|
207 |
|
208 |
|
209 | this.promptTextBuffer.draw( { dst: this.outputDst } ) ;
|
210 | }
|
211 |
|
212 | EditableTextBox.prototype.preDrawSelf.call( this ) ;
|
213 | } ;
|
214 |
|
215 |
|
216 |
|
217 | InlineInput.prototype.autoResizeAndDraw = function( onlyDrawCursor = false ) {
|
218 | var height = Math.max( this.textBuffer.buffer.length , ( this.altTextBuffer && this.altTextBuffer.buffer.length ) || 0 ) ;
|
219 |
|
220 | if ( height > this.outputHeight ) {
|
221 | this.setSizeAndPosition( { outputHeight: height } ) ;
|
222 | }
|
223 |
|
224 | if ( ! onlyDrawCursor ) {
|
225 | this.draw() ;
|
226 | }
|
227 | else {
|
228 | this.drawCursor() ;
|
229 | }
|
230 | } ;
|
231 |
|
232 |
|
233 |
|
234 | InlineInput.prototype.autoResizeAndDrawCursor = function() {
|
235 | return this.autoResizeAndDraw( true ) ;
|
236 | } ;
|
237 |
|
238 |
|
239 |
|
240 | InlineInput.prototype.runAutoCompleteHint = async function( autoComplete ) {
|
241 |
|
242 | var autoCompleted ;
|
243 |
|
244 | var [ leftPart , rightPart ] = this.textBuffer.getCursorSplittedText() ;
|
245 |
|
246 | if ( rightPart ) {
|
247 | this.altTextBuffer.setText( '' ) ;
|
248 | }
|
249 | else {
|
250 | if ( Array.isArray( autoComplete ) ) {
|
251 | autoCompleted = computeAutoCompleteArray( autoComplete , leftPart , false ) ;
|
252 | }
|
253 | else if ( typeof autoComplete === 'function' ) {
|
254 | autoCompleted = await autoComplete( leftPart , false ) ;
|
255 | }
|
256 | else {
|
257 | return ;
|
258 | }
|
259 |
|
260 | if ( Array.isArray( autoCompleted ) ) {
|
261 | if ( ! autoCompleted.length ) { return ; }
|
262 | autoCompleted = autoCompleted[ 0 ] ;
|
263 | }
|
264 |
|
265 | if ( autoCompleted === leftPart ) {
|
266 | this.altTextBuffer.setText( '' ) ;
|
267 | }
|
268 | else {
|
269 | this.altTextBuffer.setText( autoCompleted ) ;
|
270 |
|
271 | }
|
272 | }
|
273 |
|
274 | this.autoResizeAndDraw() ;
|
275 | } ;
|
276 |
|
277 |
|
278 |
|
279 | InlineInput.prototype.runAutoComplete = async function( autoComplete ) {
|
280 | var autoCompleted ;
|
281 |
|
282 | [ this.autoCompleteLeftPart , this.autoCompleteRightPart ] = this.textBuffer.getCursorSplittedText() ;
|
283 |
|
284 | if ( Array.isArray( autoComplete ) ) {
|
285 | autoCompleted = computeAutoCompleteArray( autoComplete , this.autoCompleteLeftPart , this.useAutoCompleteMenu ) ;
|
286 | }
|
287 | else if ( typeof autoComplete === 'function' ) {
|
288 | autoCompleted = await autoComplete( this.autoCompleteLeftPart , this.useAutoCompleteMenu ) ;
|
289 | }
|
290 | else {
|
291 | return ;
|
292 | }
|
293 |
|
294 | if ( Array.isArray( autoCompleted ) ) {
|
295 | if ( ! autoCompleted.length ) { return ; }
|
296 |
|
297 | if ( this.useAutoCompleteMenu ) {
|
298 | this.runAutoCompleteMenu( autoCompleted ) ;
|
299 | return ;
|
300 | }
|
301 |
|
302 | autoCompleted = autoCompleted[ 0 ] ;
|
303 | }
|
304 |
|
305 | this.runAutoCompleted( autoCompleted ) ;
|
306 | } ;
|
307 |
|
308 |
|
309 |
|
310 | InlineInput.prototype.runAutoCompleted = async function( autoCompleted ) {
|
311 | this.textBuffer.setText( autoCompleted + this.autoCompleteRightPart ) ;
|
312 | this.textBuffer.setCursorOffset( autoCompleted.length ) ;
|
313 | this.textBuffer.runStateMachine() ;
|
314 | this.autoResizeAndDraw() ;
|
315 | } ;
|
316 |
|
317 |
|
318 |
|
319 | InlineInput.prototype.runAutoCompleteMenu = async function( items ) {
|
320 |
|
321 | if ( ! items || ! items.length ) { return ; }
|
322 |
|
323 | if ( this.autoCompleteMenu ) {
|
324 |
|
325 | this.autoCompleteMenu.destroy() ;
|
326 | }
|
327 |
|
328 |
|
329 | this.autoCompleteMenu = new RowMenu( Object.assign( {} , this.menuOptions , {
|
330 | internal: true ,
|
331 | parent: this ,
|
332 | x: this.outputX ,
|
333 | y: this.outputY + this.outputHeight ,
|
334 | outputWidth: this.outputWidth ,
|
335 | items: items.map( item => ( { value: item , content: item } ) )
|
336 | } ) ) ;
|
337 |
|
338 | this.document.giveFocusTo( this.autoCompleteMenu ) ;
|
339 |
|
340 | this.autoCompleteMenu.once( 'submit' , this.onAutoCompleteMenuSubmit ) ;
|
341 | this.autoCompleteMenu.once( 'cancel' , this.onAutoCompleteMenuCancel ) ;
|
342 | } ;
|
343 |
|
344 |
|
345 |
|
346 | InlineInput.prototype.onAutoCompleteMenuSubmit = function( selectedText ) {
|
347 | this.autoCompleteMenu.destroy() ;
|
348 | this.autoCompleteMenu = null ;
|
349 | this.document.giveFocusTo( this ) ;
|
350 | this.runAutoCompleted( selectedText ) ;
|
351 | } ;
|
352 |
|
353 |
|
354 |
|
355 | InlineInput.prototype.onAutoCompleteMenuCancel = function() {
|
356 | this.autoCompleteMenu.destroy() ;
|
357 | this.autoCompleteMenu = null ;
|
358 | this.document.giveFocusTo( this ) ;
|
359 | } ;
|
360 |
|
361 |
|
362 |
|
363 | InlineInput.prototype.onKey = function( key , trash , data ) {
|
364 | if ( this.autoCompleteMenu ) {
|
365 |
|
366 | this.autoCompleteMenu.emit( 'cancel' ) ;
|
367 | }
|
368 |
|
369 | if ( data && data.isCharacter ) {
|
370 | if ( this.placeholder ) {
|
371 |
|
372 | this.placeholder = null ;
|
373 | this.setAltContent( '' , false , true ) ;
|
374 | }
|
375 |
|
376 | this.textBuffer.insert( key , this.textAttr ) ;
|
377 | this.textBuffer.runStateMachine() ;
|
378 |
|
379 | if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; }
|
380 | else { this.autoResizeAndDraw() ; }
|
381 | }
|
382 | else {
|
383 |
|
384 |
|
385 | switch( this.keyBindings[ key ] ) {
|
386 | case 'submit' :
|
387 | if ( this.disabled || this.submitted || this.canceled ) { break ; }
|
388 |
|
389 | this.emit( 'submit' , this.getValue() , undefined , this ) ;
|
390 | break ;
|
391 |
|
392 | case 'cancel' :
|
393 | if ( ! this.cancelable || this.disabled || this.canceled ) { break ; }
|
394 |
|
395 | this.emit( 'cancel' , this ) ;
|
396 | break ;
|
397 |
|
398 | case 'autoComplete' :
|
399 | if ( ! this.autoComplete ) { break ; }
|
400 | this.runAutoComplete( this.autoComplete ) ;
|
401 | break ;
|
402 |
|
403 | case 'historyAutoComplete' :
|
404 | if ( ! this.autoComplete ) { break ; }
|
405 | this.runAutoComplete( this.history ) ;
|
406 | break ;
|
407 |
|
408 | case 'historyPrevious' :
|
409 | if ( this.contentIndex <= 0 ) { break ; }
|
410 | this.contentArray[ this.contentIndex ] = this.getContent() ;
|
411 | this.contentIndex -- ;
|
412 | this.setContent( this.contentArray[ this.contentIndex ] ) ;
|
413 | this.textBuffer.runStateMachine() ;
|
414 | this.autoResizeAndDraw() ;
|
415 | break ;
|
416 |
|
417 | case 'historyNext' :
|
418 | if ( this.contentIndex >= this.contentArray.length - 1 ) { break ; }
|
419 | this.contentArray[ this.contentIndex ] = this.getContent() ;
|
420 | this.contentIndex ++ ;
|
421 | this.setContent( this.contentArray[ this.contentIndex ] ) ;
|
422 | this.textBuffer.runStateMachine() ;
|
423 | this.autoResizeAndDraw() ;
|
424 | break ;
|
425 |
|
426 | case 'backDelete' :
|
427 | this.textBuffer.backDelete() ;
|
428 | this.textBuffer.runStateMachine() ;
|
429 | if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; }
|
430 | else { this.autoResizeAndDraw() ; }
|
431 | break ;
|
432 |
|
433 | case 'delete' :
|
434 | this.textBuffer.delete() ;
|
435 | this.textBuffer.runStateMachine() ;
|
436 | if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; }
|
437 | else { this.autoResizeAndDraw() ; }
|
438 | break ;
|
439 |
|
440 | case 'backward' :
|
441 | this.textBuffer.moveBackward() ;
|
442 | this.autoResizeAndDrawCursor() ;
|
443 | break ;
|
444 |
|
445 | case 'forward' :
|
446 | this.textBuffer.moveForward() ;
|
447 | this.autoResizeAndDrawCursor() ;
|
448 | break ;
|
449 |
|
450 | case 'startOfWord' :
|
451 | this.textBuffer.moveToStartOfWord() ;
|
452 | this.autoResizeAndDrawCursor() ;
|
453 | break ;
|
454 |
|
455 | case 'endOfWord' :
|
456 | this.textBuffer.moveToEndOfWord() ;
|
457 | this.autoResizeAndDrawCursor() ;
|
458 | break ;
|
459 |
|
460 | case 'startOfLine' :
|
461 | this.textBuffer.moveToColumn( 0 ) ;
|
462 | this.autoResizeAndDrawCursor() ;
|
463 | break ;
|
464 |
|
465 | case 'endOfLine' :
|
466 | this.textBuffer.moveToEndOfLine() ;
|
467 | this.autoResizeAndDrawCursor() ;
|
468 | break ;
|
469 |
|
470 | case 'left' :
|
471 | this.textBuffer.moveLeft() ;
|
472 | this.autoResizeAndDrawCursor() ;
|
473 | break ;
|
474 |
|
475 | case 'right' :
|
476 | this.textBuffer.moveRight() ;
|
477 | this.autoResizeAndDrawCursor() ;
|
478 | break ;
|
479 |
|
480 | case 'pasteClipboard' :
|
481 | if ( this.document ) {
|
482 | this.document.getClipboard().then( str => {
|
483 | if ( str ) {
|
484 | this.textBuffer.insert( str , this.textAttr ) ;
|
485 | this.textBuffer.runStateMachine() ;
|
486 | if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; }
|
487 | else { this.autoResizeAndDraw() ; }
|
488 | }
|
489 | } )
|
490 | .catch( () => undefined ) ;
|
491 | }
|
492 | break ;
|
493 |
|
494 | case 'copyClipboard' :
|
495 | if ( this.document ) {
|
496 | this.document.setClipboard( this.textBuffer.getSelectionText() ).catch( () => undefined ) ;
|
497 | }
|
498 | break ;
|
499 |
|
500 | default :
|
501 | return ;
|
502 | }
|
503 | }
|
504 |
|
505 | return true ;
|
506 | } ;
|
507 |
|
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 |
|
514 |
|
515 |
|
516 |
|
517 |
|
518 |
|
519 |
|
520 |
|
521 |
|
522 |
|
523 |
|
524 |
|
525 |
|
526 |
|
527 |
|
528 |
|
529 |
|
530 |
|
531 |
|
532 |
|
533 |
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 |
|
553 |
|
554 |
|
555 |
|
556 |
|
557 |
|
558 |
|