1-
21namespace microgui {
32 import AppInterface = user_interface_base . AppInterface
43 import Scene = user_interface_base . Scene
@@ -190,20 +189,22 @@ namespace microgui {
190189 . . . c 1 1 d d d b b f . . . .
191190 . . c c c c c f f f f f f . . .
192191 . . . c b c b c b c c f . . . .
193- . . . c 1 c d c d c b f d . . .
194- . . . c 1 c d c d c b f d . . .
195- . . . c 1 c d c d c b f d . . .
196- . . . c 1 c d c d c b f d . . .
197- . . . c 1 1 d d d b b f d . . .
198- . . . c 1 1 d d d b b f d . . .
199- . . . . c c c f f f f d . . . .
192+ . . . c 1 c d c d c b f . . . .
193+ . . . c 1 c d c d c b f . . . .
194+ . . . c 1 c d c d c b f . . . .
195+ . . . c 1 c d c d c b f . . . .
196+ . . . c 1 1 d d d b b f . . . .
197+ . . . c 1 1 d d d b b f . . . .
198+ . . . . c c c f f f f . . . . .
200199 . . . . . . . . . . . . . . . .
201200 . . . . . . . . . . . . . . . .
202201 `
203202
204203 export enum KeyboardLayouts {
205204 QWERTY ,
206- NUMERIC
205+ NUMERIC ,
206+ NUMERIC_POSITIVE_ONLY ,
207+ NUMERIC_WITH_DELETE
207208 }
208209
209210 interface IKeyboard {
@@ -219,7 +220,7 @@ namespace microgui {
219220 }
220221
221222 type KeyboardBtnFn = ( btn : Button , kb : IKeyboard ) => void ;
222- type SpecialBtnData = { btnRow : number , btnCol : number , behaviour : ( btn : Button , kb : IKeyboard ) => void } ;
223+ type SpecialBtnData = { btnRow : number , btnCol : number , behaviour : KeyboardBtnFn } ;
223224 type KeyboardLayoutData = {
224225 [ id : number ] : {
225226 btnTexts : ( string | Bitmap ) [ ] [ ] ,
@@ -228,6 +229,68 @@ namespace microgui {
228229 }
229230 } ;
230231
232+ const __kbBehaviourNumericDefault : KeyboardBtnFn = ( btn : Button , kb : IKeyboard ) => { // Default Behaviour: Prevent leading zeroes
233+ const btnChar = btn . state [ 0 ] ;
234+ const txt = kb . getText ( ) ;
235+ const txtLen = txt . length ;
236+
237+ if ( txtLen == 0 ) {
238+ kb . appendText ( btnChar )
239+ return ;
240+ }
241+
242+ // Illegal cases: where there's a "0" or a "-0" and you want to add anything except a '.'
243+ // The decimal point '.' is allowed via the specialBtnBehaviour.
244+ const leadingZeroCase1 = txtLen == 1 && txt [ 0 ] == "0" ;
245+ const leadingZeroCase2 = txtLen == 2 && txt [ 0 ] == "-" && txt [ 1 ] == "0" ;
246+ if ( leadingZeroCase1 || leadingZeroCase2 )
247+ kb . shakeText ( )
248+ else
249+ kb . appendText ( btnChar )
250+
251+ } // End of: default behaviour: Prevent leading zeroes
252+
253+ const __kbBehaviourNumericMinus : KeyboardBtnFn = ( btn : Button , kb : IKeyboard ) => { // Minus symbol: Toggle "-" at the start.
254+ const txt = kb . getText ( ) ;
255+
256+ // Remove "-" if its already there:
257+ if ( txt [ 0 ] == btn . state [ 0 ] )
258+ if ( txt . length == 1 )
259+ kb . setText ( "" )
260+ else
261+ kb . setText ( txt . slice ( 1 ) )
262+ else // Add in "-":
263+ kb . setText ( "-" + txt )
264+ } // END OF: Minus symbol: Toggle "-" at the start.
265+
266+
267+ const __kbBehaviourNumericDeimcal : KeyboardBtnFn = ( btn : Button , kb : IKeyboard ) => { // Decimal point
268+ const txt = kb . getText ( ) ;
269+ const len = txt . length ;
270+ const decimalAlreadyPresent = txt . includes ( "." )
271+ if ( len == 0 || txt [ len - 1 ] == "-" || decimalAlreadyPresent )
272+ kb . shakeText ( )
273+ else
274+ kb . appendText ( "." )
275+ } // END OF: Decimal point
276+
277+ const __kbBehaviourNumericEnter : KeyboardBtnFn = ( btn : Button , kb : IKeyboard ) => { // Enter
278+ const txt = kb . getText ( ) ;
279+ const len = txt . length ;
280+ const lenRule = txt [ len - 1 ] != "-" ;
281+ const noDecimalEnding = txt [ len - 1 ] != "." ; // Illegal: 0. , -0. , -10. Okay: -0.00.. and 0.000 (becomes 0 later)
282+
283+ if ( len > 0 && lenRule && noDecimalEnding ) { // Last rule could be removed, casting "1." to number is valid.
284+ // Turn -0 and -0.000... into 0 before returning
285+ const txtAsNum : number = + txt ;
286+ if ( txtAsNum == 0 || txtAsNum == - 0 )
287+ kb . setText ( "0" )
288+ kb . nextScene ( )
289+ } else {
290+ kb . shakeText ( )
291+ }
292+ } // END OF: ENTER
293+
231294 const __keyboardLayout : KeyboardLayoutData = {
232295 [ KeyboardLayouts . QWERTY ] : {
233296 btnTexts : [
@@ -254,88 +317,60 @@ namespace microgui {
254317 */
255318 [ KeyboardLayouts . NUMERIC ] : {
256319 btnTexts : [
257- [ "0" , " 1", "2" , "3" , "4 " ] ,
258- [ "5 " , "6 " , "7 " , "8 " , "9 " ] ,
259- [ btn_delete , "<- " , "- " , ". " , "ENTER" ]
320+ [ "1" , "2" , "3" , "<- " ] ,
321+ [ "4 " , "5 " , "6 " , ". " , "- " ] ,
322+ [ "7" , "8 " , "9 " , "0 " , "ENTER" ]
260323 ] ,
261- defaultBtnBehaviour : ( btn : Button , kb : IKeyboard ) => { // Default Behaviour: Prevent leading zeroes
262- const btnChar = btn . state [ 0 ] ;
263- const txt = kb . getText ( ) ;
264- const txtLen = txt . length ;
265-
266- if ( txtLen == 0 ) {
267- kb . appendText ( btnChar )
268- return ;
269- }
324+ defaultBtnBehaviour : __kbBehaviourNumericDefault ,
325+ specialBtnBehaviours : [
326+ { btnRow : 0 , btnCol : 3 , behaviour : ( btn : Button , kb : IKeyboard ) => kb . deletePriorCharacters ( 1 ) } , // Backspace
327+ { btnRow : 1 , btnCol : 4 , behaviour : ( b : Button , kb : IKeyboard ) => __kbBehaviourNumericMinus ( b , kb ) } ,
328+ { btnRow : 1 , btnCol : 3 , behaviour : ( b : Button , kb : IKeyboard ) => __kbBehaviourNumericDeimcal ( b , kb ) } ,
329+ { btnRow : 2 , btnCol : 4 , behaviour : ( b : Button , kb : IKeyboard ) => __kbBehaviourNumericEnter ( b , kb ) }
330+ ]
331+ } ,
270332
271- // Illegal cases: where there's a "0" or a "-0" and you want to add anything except a '.'
272- // The decimal point '.' is allowed via the specialBtnBehaviour.
273- const leadingZeroCase1 = txtLen == 1 && txt [ 0 ] == "0" ;
274- const leadingZeroCase2 = txtLen == 2 && txt [ 0 ] == "-" && txt [ 1 ] == "0" ;
275- if ( leadingZeroCase1 || leadingZeroCase2 )
276- kb . shakeText ( )
277- else
278- kb . appendText ( btnChar )
333+ /**
334+ * Ensures that the user inputs result in a valid number.
335+ * E.g: prevents two decimal places, - only at start, etc
336+ */
337+ [ KeyboardLayouts . NUMERIC_POSITIVE_ONLY ] : {
338+ btnTexts : [
339+ [ "1" , "2" , "3" , "<-" ] ,
340+ [ "4" , "5" , "6" , "." ] ,
341+ [ "7" , "8" , "9" , "0" , "ENTER" ]
342+ ] ,
343+ defaultBtnBehaviour : __kbBehaviourNumericDefault ,
344+ specialBtnBehaviours : [
345+ { btnRow : 0 , btnCol : 3 , behaviour : ( b : Button , kb : IKeyboard ) => kb . deletePriorCharacters ( 1 ) } , // Backspace
346+ { btnRow : 1 , btnCol : 3 , behaviour : ( b : Button , kb : IKeyboard ) => __kbBehaviourNumericDeimcal ( b , kb ) } , // Decimal point
347+ { btnRow : 2 , btnCol : 4 , behaviour : ( b : Button , kb : IKeyboard ) => __kbBehaviourNumericEnter ( b , kb ) } // Enter
348+ ]
349+ } ,
279350
280- } , // End of: default behaviour: Prevent leading zeroes
351+ /**
352+ * Same as above, but we have a Trashcan bitmap for a custom delete fn.
353+ * This is used by MicroCode; so its DigitWidget (this keyboard) can be deleted like other elements.
354+ */
355+ [ KeyboardLayouts . NUMERIC_WITH_DELETE ] : {
356+ btnTexts : [
357+ [ "1" , "2" , "3" , "<-" , btn_delete ] ,
358+ [ "4" , "5" , "6" , "." , "-" ] ,
359+ [ "7" , "8" , "9" , "0" , "ENTER" ]
360+ ] ,
361+ defaultBtnBehaviour : __kbBehaviourNumericDefault ,
281362 specialBtnBehaviours : [
282- {
283- btnRow : 2 , btnCol : 0 , behaviour : ( btn : Button , kb : IKeyboard ) => { // btn_delete
284- kb . deleteFn ( ) ;
285- }
286- } , // END OF: btn_delete
287- { btnRow : 2 , btnCol : 1 , behaviour : ( btn : Button , kb : IKeyboard ) => kb . deletePriorCharacters ( 1 ) } , // Backspace
288- {
289- btnRow : 2 , btnCol : 2 , behaviour : ( btn : Button , kb : IKeyboard ) => { // Minus symbol: Toggle "-" at the start.
290- const txt = kb . getText ( ) ;
291-
292- // Remove "-" if its already there:
293- if ( txt [ 0 ] == btn . state [ 0 ] )
294- if ( txt . length == 1 )
295- kb . setText ( "" )
296- else
297- kb . setText ( txt . slice ( 1 ) )
298- else // Add in "-":
299- kb . setText ( "-" + txt )
300- }
301- } , // END OF: Minus symbol: Toggle "-" at the start.
302- {
303- btnRow : 2 , btnCol : 3 , behaviour : ( btn : Button , kb : IKeyboard ) => { // Decimal point
304- const txt = kb . getText ( ) ;
305- const len = txt . length ;
306- const decimalAlreadyPresent = txt . includes ( "." )
307- if ( len == 0 || txt [ len - 1 ] == "-" || decimalAlreadyPresent )
308- kb . shakeText ( )
309- else
310- kb . appendText ( "." )
311- }
312- } , // END OF: Decimal point
313- {
314- btnRow : 2 , btnCol : 4 , behaviour : ( btn : Button , kb : IKeyboard ) => { // Enter
315- const txt = kb . getText ( ) ;
316- const len = txt . length ;
317- const lenRule = txt [ len - 1 ] != "-" ;
318- const noDecimalEnding = txt [ len - 1 ] != "." ;
319-
320- // Handle -0:
321- const txtAsNum : number = + txt ;
322-
323- if ( txtAsNum == 0 || txtAsNum == - 0 )
324- kb . setText ( "0" )
325-
326- if ( len > 0 && lenRule && noDecimalEnding ) // Last rule could be removed, casting "1." to number is valid.
327- kb . nextScene ( )
328- else
329- kb . shakeText ( )
330- }
331- } // END OF: ENTER
363+ { btnRow : 0 , btnCol : 3 , behaviour : ( b : Button , kb : IKeyboard ) => kb . deletePriorCharacters ( 1 ) } , // Backspace
364+ { btnRow : 0 , btnCol : 4 , behaviour : ( b : Button , kb : IKeyboard ) => kb . deleteFn ( ) } , // btn_delete
365+ { btnRow : 1 , btnCol : 3 , behaviour : ( b : Button , kb : IKeyboard ) => __kbBehaviourNumericDeimcal ( b , kb ) } , // Decimal point
366+ { btnRow : 1 , btnCol : 4 , behaviour : ( b : Button , kb : IKeyboard ) => __kbBehaviourNumericMinus ( b , kb ) } , // Minus
367+ { btnRow : 2 , btnCol : 4 , behaviour : ( b : Button , kb : IKeyboard ) => __kbBehaviourNumericEnter ( b , kb ) } // Enter
332368 ]
333369 }
334370 } ;
335371
336372 const KEYBOARD_FRAME_COUNTER_CURSOR_ON = 20 ;
337373 export class Keyboard extends CursorScene implements IKeyboard {
338-
339374 private btns : Button [ ] [ ]
340375 private text : string ;
341376 private isUpperCase : boolean ;
@@ -401,7 +436,6 @@ namespace microgui {
401436 this . passedBackBtn = ( opts . backBtn ) ? opts . backBtn : ( ) => { } ;
402437 }
403438
404-
405439 startup ( ) {
406440 super . startup ( )
407441
@@ -413,35 +447,61 @@ namespace microgui {
413447
414448 const ySpacing = ( this . keyboardBounds . height - charHeight ) / ( data . btnTexts . length ) ;
415449
450+ const btnXPositions : number [ ] [ ] =
451+ data . btnTexts . map ( row =>
452+ row . map ( ( txtOrBitmap : string | Bitmap ) => {
453+ if ( typeof ( txtOrBitmap ) == "string" )
454+ // return (charWidth * (txtOrBitmap.length + 1) >> 1);
455+ return charWidth * ( txtOrBitmap . length + 1 ) ;
456+ else
457+ // return txtOrBitmap.width - (txtOrBitmap.width >> 1);
458+ return txtOrBitmap . width - txtOrBitmap . width ;
459+ } )
460+ ) ;
461+
462+ const btnBitmapWidths : number [ ] [ ] =
463+ data . btnTexts . map ( row =>
464+ row . map ( ( txtOrBitmap : ( string | Bitmap ) ) => {
465+ if ( typeof ( txtOrBitmap ) == "string" )
466+ return ( charWidth * txtOrBitmap . length ) + ( charWidth >> 1 ) ;
467+ else
468+ return txtOrBitmap . width ;
469+ } )
470+ ) ;
471+
472+ const btnXPositionAnchor : number =
473+ btnXPositions . map ( rowWidth =>
474+ rowWidth . reduce ( ( sum : number , current : number ) => sum + current , 0 )
475+ )
476+ . reduce ( ( widest : number , current : number ) => Math . max ( widest , current ) , 0 ) ;
477+
478+ const longestRowLen = data . btnTexts . map ( btnTexts => btnTexts . length ) . reduce ( ( longest , current ) => Math . max ( longest , current ) , 0 ) ;
479+ // const xSpacing = (this.keyboardBounds.width - btnXPositionAnchor) / (longestRowLen + 1);
480+
416481 for ( let row = 0 ; row < data . btnTexts . length ; row ++ ) {
417- const bitmapWidths = data . btnTexts [ row ] . map ( ( txtOrBitmap : string | Bitmap ) => {
418- if ( typeof ( txtOrBitmap ) == "string" )
419- return charWidth * ( txtOrBitmap . length + 1 ) - 4 ;
420- else
421- return txtOrBitmap . width + 3 ;
422- } ) ;
482+ // const xSpacing = ((this.keyboardBounds.width - btnXPositionAnchor) / (data.btnTexts[row].length + 1));
483+ const xSpacing = ( ( this . keyboardBounds . width - btnXPositionAnchor ) / ( longestRowLen + 1 ) ) ;
484+ // const xSpacing = (this.keyboardBounds.width - btnXPositions[row].reduce((sum, current) => sum + current, 0)) / (data.btnTexts[row].length + 1);
423485
424- const totalWidth : number = bitmapWidths . reduce ( ( total : number , w : number ) => total + w , 0 ) ;
425- const xSpacing = ( this . keyboardBounds . width - totalWidth ) / ( bitmapWidths . length + 2 ) ;
486+ let x = - Screen . HALF_WIDTH + xSpacing + ( data . btnTexts [ row ] . length >> 1 ) ;
426487
427- let x = - Screen . HALF_WIDTH + xSpacing ;
428488 for ( let col = 0 ; col < data . btnTexts [ row ] . length ; col ++ ) {
429489 const btnState : ( string | Bitmap ) = data . btnTexts [ row ] [ col ] ;
430- const bitmapWidth = bitmapWidths [ col ]
490+ const bitmapWidth = btnBitmapWidths [ row ] [ col ] ;
431491
432- x += ( bitmapWidths [ col ] + xSpacing ) >> 1
492+ x += ( btnXPositions [ row ] [ col ] + xSpacing ) >> 1
433493 this . btns [ row ] [ col ] =
434494 new Button ( {
435495 parent : null ,
436496 style : ButtonStyles . Transparent ,
437497 icon : ( typeof ( btnState ) == "string" ) ? bitmaps . create ( bitmapWidth , charHeight ) : btnState ,
438498 ariaId : "" ,
439- x,
499+ x : x ,
440500 y : - ( charHeight >> 1 ) + ( ySpacing * row ) ,
441501 onClick : ( btn : Button ) => data . defaultBtnBehaviour ( btn , this ) ,
442- state : ( typeof ( btnState ) == "string" ) ? [ btnState ] : [ ]
502+ state : ( typeof ( btnState ) == "string" ) ? [ btnState ] : [ ] // String only btnStates; for default QWERTY and NUMERIC behaviours.
443503 } )
444- x += ( bitmapWidths [ col ] + xSpacing ) >> 1
504+ x += ( btnXPositions [ row ] [ col ] + xSpacing ) >> 1 ;
445505 }
446506 }
447507
@@ -453,7 +513,7 @@ namespace microgui {
453513 }
454514 )
455515
456- context . onEvent ( ControllerButtonEvent . Pressed , controller . B . id , ( ) => this . passedBackBtn ( this . text ) )
516+ context . onEvent ( ControllerButtonEvent . Pressed , controller . B . id , ( ) => this . passedBackBtn ( this . text ) ) ;
457517 this . navigator . setBtns ( this . btns ) ;
458518 }
459519
@@ -639,7 +699,8 @@ namespace microgui {
639699 const btn = this . btns [ i ] [ j ] ;
640700 const btnText = ( btn . state . length > 0 ) ? btn . state [ 0 ] : null ;
641701
642- const x = ( screen ( ) . width / 2 ) + btn . xfrm . localPos . x - ( btn . icon . width / 2 ) + 1
702+ const X_SHIFT = 3 ; // small adjustment to get the btnText to line up with the cursor
703+ const x = ( screen ( ) . width / 2 ) + btn . xfrm . localPos . x - ( btn . icon . width / 2 ) + X_SHIFT
643704 const y = ( screen ( ) . height / 2 ) + btn . xfrm . localPos . y + charHeight - 12
644705
645706 btn . draw ( )
0 commit comments