@@ -178,6 +178,291 @@ namespace microgui {
178178 }
179179
180180
181+ export enum KeyboardLayouts {
182+ QWERTY ,
183+ NUMERIC
184+ }
185+
186+ interface IKeyboard {
187+ appendText ( txt : string ) : void ;
188+ deletePriorCharacters ( n : number ) : void ;
189+ swapCase ( ) : void ;
190+ nextScene ( ) : void ;
191+ }
192+
193+ type KeyboardBtnFn = ( btn : Button , kb : IKeyboard ) => void ;
194+ type SpecialBtnData = { btnRow : number , btnCol : number , behaviour : ( btn : Button , kb : IKeyboard ) => void } ;
195+ type KeyboardLayoutData = {
196+ [ id : number ] : {
197+ btnTexts : string [ ] [ ] ,
198+ defaultBtnBehaviour : KeyboardBtnFn ,
199+ specialBtnBehaviours : SpecialBtnData [ ]
200+ }
201+ } ;
202+
203+ const __keyboardLayout : KeyboardLayoutData = {
204+ [ KeyboardLayouts . QWERTY ] : {
205+ btnTexts : [
206+ [ "0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" ] ,
207+ [ "Q" , "W" , "E" , "R" , "T" , "Y" , "U" , "I" , "O" , "P" ] ,
208+ [ "A" , "S" , "D" , "F" , "G" , "H" , "J" , "K" , "L" , ";" ] ,
209+ [ "Z" , "X" , "C" , "V" , "B" , "N" , "M" , "," , "." , "/" ] ,
210+ [ "<-" , "^" , " _______ " , "ENTER" ]
211+ ] ,
212+ defaultBtnBehaviour : ( btn : Button , kb : IKeyboard ) => {
213+ kb . appendText ( btn . state [ 0 ] )
214+ } ,
215+ specialBtnBehaviours : [
216+ { btnRow : 4 , btnCol : 0 , behaviour : ( btn : Button , kb : IKeyboard ) => kb . deletePriorCharacters ( 1 ) } , // Backspace
217+ { btnRow : 4 , btnCol : 1 , behaviour : ( btn : Button , kb : IKeyboard ) => kb . swapCase ( ) } , // Change case
218+ { btnRow : 4 , btnCol : 2 , behaviour : ( btn : Button , kb : IKeyboard ) => kb . appendText ( " " ) } , // Spacebar
219+ { btnRow : 4 , btnCol : 3 , behaviour : ( btn : Button , kb : IKeyboard ) => kb . nextScene ( ) } // ENTER
220+ ]
221+ }
222+ } ;
223+
224+ export class Keyboard extends CursorSceneWithPriorPage implements IKeyboard {
225+ private btns : Button [ ] [ ]
226+ private text : string ;
227+ private isUpperCase : boolean ;
228+ private nextBtnFn : ( keyboardText : string ) => void
229+ private keyboardLayout : KeyboardLayouts ;
230+
231+ // Special effects:
232+ private frameCounter : number ;
233+ private shakeText : boolean
234+ private shakeTextCounter : number
235+
236+ constructor (
237+ app : AppInterface ,
238+ keyboardLayout : KeyboardLayouts ,
239+ nextBtnFn : ( keyboardText : string ) => void ,
240+ priorScene : CursorSceneWithPriorPage ,
241+ ) {
242+ super ( app , ( ) => { app . popScene ( ) ; app . pushScene ( priorScene ) } , new GridNavigator ( [ [ ] ] ) )
243+ this . text = ""
244+ this . isUpperCase = true
245+
246+ this . nextBtnFn = nextBtnFn ;
247+ this . frameCounter = 0 ;
248+ this . shakeText = false ;
249+ this . shakeTextCounter = 0 ;
250+
251+ this . keyboardLayout = keyboardLayout ;
252+ }
253+
254+ startup ( controlSetupFn : ( ) => void ) {
255+ super . startup ( controlSetupFn )
256+
257+ const data = __keyboardLayout [ KeyboardLayouts . QWERTY ] ;
258+ this . btns = data . btnTexts . map ( _ => [ ] )
259+
260+ for ( let row = 0 ; row < data . btnTexts . length ; row ++ ) {
261+ const bitmapWidths = data . btnTexts [ row ] . map ( ( txt : string ) => 2 + ( bitmaps . font8 . charWidth * ( txt . length + 1 ) ) ) ;
262+ const totalWidth : number = bitmapWidths . reduce ( ( total : number , w : number ) => total + w , 0 )
263+
264+ let x : number = - Screen . HALF_WIDTH + ( bitmapWidths [ 0 ] >> 1 ) + 3 ;
265+ for ( let col = 0 ; col < data . btnTexts [ row ] . length ; col ++ ) {
266+ const bitmapWidth = bitmapWidths [ col ]
267+ x += bitmapWidth >> 1
268+ this . btns [ row ] [ col ] =
269+ new Button ( {
270+ parent : null ,
271+ style : ButtonStyles . Transparent ,
272+ icon : bitmaps . create ( bitmapWidth - 4 , 10 ) ,
273+ ariaId : "" ,
274+ x,
275+ y : ( 13 * ( row + 1 ) ) - 18 ,
276+ onClick : ( btn : Button ) => data . defaultBtnBehaviour ( btn , this ) ,
277+ state : [ data . btnTexts [ row ] [ col ] ]
278+ } )
279+ x += bitmapWidth >> 1 ;
280+ }
281+ }
282+
283+ // Setup special btn behaviours:
284+ data . specialBtnBehaviours . forEach (
285+ ( data : SpecialBtnData , i ) => {
286+ this . btns [ data . btnRow ] [ data . btnCol ] . onClick =
287+ ( btn : Button ) => data . behaviour ( btn , this ) ;
288+ }
289+ )
290+ this . navigator . setBtns ( this . btns )
291+ }
292+
293+ get textLength ( ) { return this . text . length }
294+
295+ //-------------------
296+ // Interface Methods:
297+ //-------------------
298+
299+ public appendText ( str : string ) {
300+ if ( this . textLength < KEYBOARD_MAX_TEXT_LENGTH )
301+ this . frameCounter = KEYBOARD_FRAME_COUNTER_CURSOR_ON
302+ else
303+ this . shakeText = true
304+ this . text += str ;
305+ }
306+
307+ public deletePriorCharacters ( n : number ) {
308+ this . text =
309+ ( this . text . length > 0 )
310+ ? this . text . substr ( 0 , this . text . length - n )
311+ : this . text
312+ this . frameCounter = KEYBOARD_FRAME_COUNTER_CURSOR_ON
313+ }
314+
315+ public swapCase ( ) : void {
316+ this . isUpperCase = ! this . isUpperCase ;
317+
318+ const swapCaseFn = ( this . isUpperCase )
319+ ? ( t : string ) => { return t . toUpperCase ( ) }
320+ : ( t : string ) => { return t . toLowerCase ( ) }
321+
322+
323+ const specialBtnData : SpecialBtnData [ ] = __keyboardLayout [ this . keyboardLayout ] . specialBtnBehaviours ;
324+ const specialBtnRows : number [ ] = specialBtnData . map ( ( sbd : SpecialBtnData ) => sbd . btnRow ) ;
325+ const specialBtnCols : number [ ] = specialBtnData . map ( ( sbd : SpecialBtnData ) => sbd . btnCol ) ;
326+
327+ const isSpecialBtn = ( row : number , col : number ) : boolean => {
328+ return ( specialBtnRows . indexOf ( row ) != - 1 ) && ( specialBtnRows . indexOf ( col ) != - 1 ) ;
329+ }
330+
331+ // Skip special char row:
332+ for ( let i = 0 ; i < this . btns . length - 1 ; i ++ ) {
333+ for ( let j = 0 ; j < this . btns [ i ] . length ; j ++ ) {
334+ if ( ! isSpecialBtn )
335+ continue
336+
337+ const btnText : string = ( this . btns [ i ] [ j ] . state [ 0 ] as string )
338+ this . btns [ i ] [ j ] . state [ 0 ] = swapCaseFn ( btnText ) ;
339+ }
340+ }
341+ }
342+
343+ public nextScene ( ) : void {
344+ this . nextBtnFn ( this . text )
345+ }
346+
347+ draw ( ) {
348+ this . frameCounter += 1
349+
350+ // Blue base colour:
351+ Screen . fillRect (
352+ Screen . LEFT_EDGE ,
353+ Screen . TOP_EDGE ,
354+ Screen . WIDTH ,
355+ Screen . HEIGHT ,
356+ 6 // Blue
357+ )
358+
359+ // Black border around the text window for depth
360+ Screen . fillRect (
361+ Screen . LEFT_EDGE + 3 ,
362+ Screen . TOP_EDGE + 4 ,
363+ Screen . WIDTH - 7 ,
364+ 34 ,
365+ 15 // Black
366+ )
367+
368+ // White text window, slightly smaller than the black
369+ Screen . fillRect (
370+ Screen . LEFT_EDGE + 6 ,
371+ Screen . TOP_EDGE + 6 ,
372+ Screen . WIDTH - 12 ,
373+ 32 ,
374+ 1 // White
375+ )
376+
377+
378+ // Legal text length, draw a flickering cursor using this.frameCounter:
379+ if ( this . text . length < KEYBOARD_MAX_TEXT_LENGTH ) {
380+ screen ( ) . printCenter ( this . text , 17 , 15 )
381+ if ( this . frameCounter >= KEYBOARD_FRAME_COUNTER_CURSOR_ON ) {
382+ screen ( ) . print (
383+ "_" ,
384+ ( screen ( ) . width / 2 ) + ( ( this . text . length * bitmaps . font8 . charWidth ) / 2 ) ,
385+ 17 ,
386+ 15
387+ )
388+
389+ if ( this . frameCounter >= KEYBOARD_FRAME_COUNTER_CURSOR_OFF )
390+ this . frameCounter = 0
391+ }
392+ }
393+
394+ // Don't draw the cursor if beyond the max length, shake the text a bit:
395+ else if ( this . shakeText ) {
396+ if ( this . shakeTextCounter % 5 == 0 ) {
397+ screen ( ) . print (
398+ this . text ,
399+ ( screen ( ) . width / 2 ) - ( ( this . text . length * bitmaps . font8 . charWidth ) / 2 ) - 2 ,
400+ 17 ,
401+ 15
402+ )
403+ }
404+
405+ else {
406+ screen ( ) . print (
407+ this . text ,
408+ ( screen ( ) . width / 2 ) - ( ( this . text . length * bitmaps . font8 . charWidth ) / 2 ) ,
409+ 17 ,
410+ 15
411+ )
412+ }
413+
414+ if ( this . shakeTextCounter >= 5 ) {
415+ this . shakeText = false ;
416+ this . shakeTextCounter = 0 ;
417+ }
418+
419+ else
420+ this . shakeTextCounter += 1
421+ }
422+
423+ // At max-length, done shaking the text:
424+ else {
425+ screen ( ) . printCenter ( this . text , 17 , 15 )
426+ }
427+
428+ // Orange Keyboard with a black shadow on the bot & right edge (depth effect):
429+
430+ // Black border around right & bot edge:
431+ Screen . fillRect (
432+ Screen . LEFT_EDGE + 4 ,
433+ Screen . TOP_EDGE + 47 ,
434+ Screen . WIDTH - 6 ,
435+ 71 ,
436+ 15 // Black
437+ )
438+
439+ // Orange keyboard that the white text will be ontop of:
440+ Screen . fillRect (
441+ Screen . LEFT_EDGE + 4 ,
442+ Screen . TOP_EDGE + 44 ,
443+ Screen . WIDTH - 8 ,
444+ 72 ,
445+ 4 // Orange
446+ )
447+
448+ for ( let i = 0 ; i < this . btns . length ; i ++ ) {
449+ for ( let j = 0 ; j < this . btns [ i ] . length ; j ++ ) {
450+ const btn = this . btns [ i ] [ j ] ;
451+ const btnText = btn . state [ 0 ] ;
452+
453+ const x = ( screen ( ) . width / 2 ) + btn . xfrm . localPos . x - ( btn . icon . width / 2 ) + 2
454+ const y = ( screen ( ) . height / 2 ) + btn . xfrm . localPos . y + font . charHeight - 12
455+
456+ btn . draw ( )
457+ screen ( ) . print ( btnText , x , y , 1 ) // White text
458+ }
459+ }
460+
461+ super . draw ( )
462+ }
463+ }
464+
465+
181466 const KEYBOARD_FRAME_COUNTER_CURSOR_ON = 20 ;
182467 const KEYBOARD_FRAME_COUNTER_CURSOR_OFF = 40 ;
183468 const KEYBOARD_MAX_TEXT_LENGTH = 20
@@ -214,12 +499,12 @@ namespace microgui {
214499 this . shakeTextCounter = 0 ;
215500 }
216501
217- /* override */ startup ( ) {
218- super . startup ( )
502+ /* override */ startup ( controlSetupFn : ( ) => void ) {
503+ super . startup ( controlSetupFn )
219504
220505 const defaultBehaviour = ( btn : Button ) => {
221506 if ( this . text . length < KEYBOARD_MAX_TEXT_LENGTH ) {
222- this . text += this . btnsText [ btn . state [ 0 ] ] [ btn . state [ 1 ] ]
507+ this . text += btn . state [ 0 ]
223508 this . frameCounter = KEYBOARD_FRAME_COUNTER_CURSOR_ON
224509 }
225510 else {
@@ -417,7 +702,7 @@ namespace microgui {
417702 }
418703 }
419704
420- super . draw ( )
705+ // super.draw()
421706 }
422707 }
423708
@@ -446,11 +731,11 @@ namespace microgui {
446731 this . next = next
447732 }
448733
449- /* override */ startup ( ) {
450- super . startup ( )
734+ /* override */ startup ( controlSetupFn : ( ) => void ) {
735+ super . startup ( controlSetupFn )
451736
452737 const defaultBehaviour = ( btn : Button ) => {
453- this . text += this . btnText [ btn . state [ 0 ] ] + " "
738+ this . text += btn . state [ 0 ] + " "
454739 }
455740
456741 const behaviours = [ ]
@@ -684,8 +969,8 @@ namespace microgui {
684969 this . callbacks = callbacks
685970 }
686971
687- /* override */ startup ( ) {
688- super . startup ( )
972+ /* override */ startup ( controlSetupFn : ( ) => void ) {
973+ super . startup ( controlSetupFn )
689974
690975 for ( let i = 0 ; i < this . callbacks . length ; i ++ ) {
691976 for ( let j = 0 ; j < this . callbacks [ 0 ] . length ; j ++ ) {
0 commit comments