SoftKeyboard

SoftKeyboard is an ActionScript 3 library that allows the creation of on-screen virtual keyboards. It extends the Feathers UI Components suite which itself is based on the Starling Framework.

Warning! It seems this project does not work in Feathers 2.3. Honestly, I have not updated it in a long while and I doubt I ever will. Adobe has done a very poor job at advancing, promoting, supporting and believing in the Flash/AIR platform. So much so that even die-hard fans like me decided to pack up and move on. If you are interested in taking over the project, please fork it on GitHub and have fun!

The SoftKeyboard library is generally used with touch-based devices (kiosk touchscreens, mobile devices, etc.) but also work fine with a regular mouse. It gives the developer complete control over the keyboard layout and design.

softkeyboard

The library comes with many ready-to-use standard layouts. Layouts can also be completetly customized on a key-by-key basis if you need more control. Layouts can feature key variants (see above) which are alternate keys that show up when a main key is being held down.

softkeyboard-symbols

A single keyboard can also feature more than one layouts which are accesible by pressing the SWITCH_LAYOUT key. This allows the user to switch, for example, between letters and symbols (above is the default digits+symbols layout).

softkeyboard-numpad

The keyboard can be completely skinned to suit your needs. It is possible to change the label font and style, the keyboard’s background, the key’s backgrounds (up, hover, down) and the icons. Skinning is done in the standard Feathers fashion by using a Theme class.

How to use it ?

Before you start, just make sure that you have the latest version of Starling and Feathers. SoftKeyboard may not work in certain situations with Feathers version 1.0.

Warning : this library is still in alpha stage. It might not be production ready and it’s API may change. It works fine in my own setup but it does not mean it will in yours. Your comments and suggestions will help me improve it.

So, how can you try it out? First, you need to download the library’s current version (1.0a rev9) and add the cc folder (inside the lib folder) to your source path.

Then, you will need to add initializers in your theme class to skin the component. An example theme class is provided in the download package.

Finally, you need to instantiate it and place it on the Starling stage. If you only need a keyboard with a single layout, it’s as easy as that :

var layout:Layout = new QwertyEn();
var keyboard:SoftKeyboard = new SoftKeyboard(layout);
keyboard.addEventListener(KeyEvent.KEY_UP, onKeyUp);
addChild(keyboard)

If you need a keyboard with more than one layout, you will need to pass a vector of Layout objects to the constructor instead of a single Layout object :

var layout:Vector. = new [
    new QwertySwitchEn(NumbersSymbolsSwitch),
    new NumbersSymbolsSwitch(QwertySwitchEn)
];
var keyboard:SoftKeyboard = new SoftKeyboard(layout);
keyboard.addEventListener(KeyEvent.KEY_UP, onKeyUp);
addChild(keyboard)

As you can see, when you need a keyboard with more than one layouts, you should use layouts with the word ‘Switch’ in their name. Such layouts feature a SWITCH_LAYOUT button to go to another layout. The layout it goes to is specified by passing it to the constructor. So, in this example, a press on the SWITCH_LAYOUT key of the QwertySwitchEn layout will display the NumbersSymbolsSwitch layout.

Obviously, you will need to create the onKeyUp() function to perform whatever necessary tasks when the keys are pressed :

public function onKeyUp(e:KeyEvent):void {
    trace(e);
}

Naturally, you can listen to both Key.KEY_UP and Key.KEY_DOWN events. The KeyEvent class will provide information such as the charCode of the key that was pressed and the actual character associated to the key.

For more details, please take a look at the full API documentation. Do not hesitate to comment below with questions and suggestions.

Cheers!

Comments

  1. I made one small change to the SoftKeyboard.draw() method to resolve a little bug I found with key rows being off-center very slightly.

    I added the – padding bit to the below:

    // Center key rows
    for (var k:uint = 0; k < rowCount; k++) {
    var r:KeyRow = (keyboard.getChildAt(k) as KeyRow);
    r.x = width / 2 - r.width / 2 - padding;
    }

    Thanks again for the great work!

  2. Forgot one bit… right below the above code I also added:

    backgroundSkin.x = - padding;

    inside the if (backgroundSkin) block. That seems to get everything centering happily with the default theme.

    1. Joel,

      Thank you for taking time to post the modified code. However, I tried your fix and it effectively removes the padding on the left side for me (with the sample theme I provided in the download package). Would you mind telling me under what circumstances you saw that if was off-center? I will try to reproduce the problem and implement a fix that works in all scenarios.

      Thanks.

      1. The only difference I can see is your example Theme extends feathers.core.DisplayListWatcher while mine extends feathers.themes.MetalWorksMobileTheme (from the current beta 1.1 release).

        For what it’s worth, I was seeing the same behavior as you describe until I added the backgroundSkin.x = – padding; code. Did you add both bit’s from both my posts?

        1. Yes, I added both bits from both your posts. To try and replicate the problem, I extended the feathers.themes.MetalWorksMobileTheme myself and I got the same behaviour as I described before… Are you sure it’s not something else that’s giving you this problem?

          1. Maybe this is an implementation difference. Are you centering the keyboard horizontally on the stage where it’s being shown? Maybe that’s where our difference is coming from. The “width” reported by the widget is just a bit off as it’s not including the background skin in it’s size.

            So in my base program, the normal centering logic of:
            softKeyboard.x = this.stage.stageWidth / 2 - softKeyboard.width / 2 ;
            doesn’t exactly work, and must be (if my above changes aren’t incorporated in the SoftKeyboard code):
            softKeyboard.x = this.stage.stageWidth / 2 - softKeyboard.width / 2 - softKeyboard.padding;
            in order to exactly center and account for the visible SoftKeyboard width not equaling the reported width attribute.

            If that doesn’t make sense, I’ll see if I can’t can up an example to show you what I’m talking about.

          2. It makes perfect sense. You clearly highlighted the fact that it would be more logical to include the padding in the x, y, width and height properties. I will fix that when I get the chance.

          3. I updated the code (new version is 1.0a rev4) so that the width and height properties now always include any padding applied. It makes more sense this way and should resolve your issue by the same token.

            Please confirm it fixes your problem. Thanks again for pointing it out.

          4. The new version works great! (sorry for the delayed response, I didn’t get emailed notification of your last couple replies).
            Thanks very much for your work on this.

  3. this works great, also all the components are very cool and great in performance on Android (didn’t get a chance to work/deploy app on IOS.

    1. Glad to hear it. The component is still in beta so it’s useful to know that it performs well in various contexts. Thanks for taking the time to report it.

  4. This looks really great. As i am new to feathersui I could really use some more code examples though. Specifically: what would i need to do in order to make a simplified danish keyboard (with no shift, space or other function keys, just simply the letters)?

    1. Layouts are in the cc.cote.feathers.softkeyboard.layouts package. If you look at an existing one, you will see that it contains a rows vector. This vector contains rows of keys that this layout defines. If you add or remove a key from the vector, the layout will change accordingly.

      Therefore, to create a new layout, just create a new class that extends the Layout class. The easiest way to do that, is to copy one of the existing ones and modify it.

      If you feel your new layout could be useful to someone else, send it over and I will add it to the default layouts.

      Cheers!

      1. Hi Jean-Philippe
        Been figuring out how to work this into my project. I sometimes need to add keys for Æ Ø and Å. I have figured out how to add the keys – and have them return the correct charcode, but i cant get the label control to show the letter (i have made sure that the font does contain the characters needed). Can you help?

        1. As a test, I included the three characters you mentionned (Æ, Ø and Å) in a layout and they showed up just fine (both as regular keys and as a key variants). Are you sure your font contains those characters and is properly embedded ?

          1. Got it working when i checked to make sure i was using the correct charcodes 😉 thx

  5. Hello. Thanks for sharing this keyboard as the support for windows 8 OSK sucks.

    I am having a bit of trouble though and it seems I am missing some components.
    StarlingCanvas.as, Line 7 1172: Definition cc.cote.feathers.softkeyboard.layouts:EmailNumbersSymbolsSwitch could not be found.

    StarlingCanvas.as, Line 8 1172: Definition cc.cote.feathers.softkeyboard.layouts:EmailSwitch could not be found.

    StarlingCanvas.as, Line 12 1172: Definition feathers.themes:MetalWorksMobileTheme could not be found.

    I looked and it seems I dont have them in the libraries provided. Any help would be appreciated! thanks!

  6. sorry, another issue I am having is when the player gets resized I get this Fatal Error.

    Fatal error: The application lost the device context!

      1. Hello thanks! I managed to figure it out. I might figure this one out too but I have been slamming my head against it and I am trying desperately not to hack your beautiful code to a mutated blob of spaghetti…

        Whatever I do I cant seem to place the keyboard on top of the display list. I tried a couple things I thought would work (mutated spaghetti hackery) and it didn’t work. was wondering if you had a clean procedure?

        thanks again for sharing this and helping me out!

        1. Stage3D (used by SoftKeyboard => FeathersUI => Starling) is always rendered below the regular display list. So, if you want to put stuff in front of it, you will have to do so in Stage3D (Starling). Honestly, if you are not using Starling across your project, you probably shouldn’t be using my SoftKeyboard component anyway.

  7. Hi again Jean-Philippe,
    I am trying to use your keyboard with a project i am working on and i just upgraded feathers to the most recent version (1.2).
    Now when i try to compile i get this error: “Interface method set clipRect in namespace feathers.core:IFeathersControl not implemented by class cc.cote.feathers.softkeyboard:Key.

    Can you tell me what to do? – i am in over my head here 😉

    As i said before – this project is really cool and just works wonders compared to working with the native soft keyboard. So thanks
    Best regards
    Dennis

  8. This component is great and I thank you for creating it!
    When trying to initialize a SWITCH keyboard is there another call or part that I am missing?
    The package doesn’t include a QwertySwitchEn so obviously remove the En is the same, but I get an error on var layout:Vector. = new [
    new QwertySwitchEn(NumbersSymbolsSwitch),
    new NumbersSymbolsSwitch(QwertySwitchEn)
    ];
    an error gets thrown where it cannot instantiate a non-constructor.
    I know there is something I am missing here but I can’t figure out why it is doing this as everything looks good.

    1. You are missing the vector’s type. When you use the short bracket notation, you need so specify the type before the opening bracket like so <Layout>[]:

      var layout:Vector. = new <Layout>[
          new QwertySwitchEn(NumbersSymbolsSwitch),
          new NumbersSymbolsSwitch(QwertySwitchEn)
      ];
      1. Thanks for the extremely fast reply.
        That throws an error while compiling:
        Multiple markers at this line:
        -Vector
        -1084: Syntax error: expecting identifier before assign.
        -1086: Syntax error: expecting semicolon before greaterthan.
        it compiles if I change it to var layout:Vector. = new Vector.[
        but then throws:
        #1069: Property [object NumbersSymbolsSwitch] not found on cc.cote.feathers.softkeyboard.layouts.QwertySwitch and there is no default value.
        (Also, the Switch layouts do not have En at the end in your lib files).
        Thanks!

        1. The code didn’t print here properly. I typed
          “var layout:Vector. = new Vector.[“

          1. OK, let me try that again:
            var layout:Vector. “” = new Vector. “” [
            (exclude the quotes)

    1. You need to look at my code very carefully. If you use the short notation (with brackets), you shouldn’t put “Vector.” beforehand. You can do this (with brackets):

      var layout:Vector.<Layout> = new <Layout>[ bla, bla, bla ]

      Or that (with parenthesis):

      var layout:Vector.<Layout> = new Vector.<Layout>( bla, bla, bla );

      Hope this helps!

      P.S. Use &lt; and &gt; for the less than and greater than symbols.

  9. I hit a couple other minor items I ‘fixed’ in the keyboard package and figured I’d throw out to you if you care to incorporate in base package (I know it’d probably necessitate changing your ‘license’ but how about throwing this on github so I can submit a proper push?).

    In Key.as, I don’t believe your getter and setter for textFormat makes sense if you ever want a format other than default:


    override protected function initialize():void {
    if (textFormat) _label.textRendererProperties.textFormat = textFormat;
    }
    public function get textFormat():TextFormat {
    return _label.textRendererProperties.textFormat;
    }
    public function set textFormat(value:TextFormat):void {
    _label.textRendererProperties.textFormat = value;
    }

    Changed to:


    override protected function initialize():void {
    if ( this._textFormat;) _label.textRendererProperties.textFormat = this._textFormat;;
    }
    private var _textFormat:TextFormat;
    public function get textFormat():TextFormat {
    return this._textFormat;
    }
    public function set textFormat(value:TextFormat):void {
    this._textFormat = value;
    }

  10. And… I hit issues when listening for both ‘real’ keyboard event’s and your softkeyboard events. This might very well be a limitation of my knowledge of the AS3 event handler, but in KeyEvent.as I had to give your events unique ‘names’ by changing:

    public static const KEY_UP:String = "keyUp";
    public static const KEY_DOWN:String = "keyDown";

    to:

    public static const KEY_UP:String = "coteKeyUp";
    public static const KEY_DOWN:String = "coteKeyDown";

    1. Do you have an example where this happens ? SoftKeyboard should never be dispatching “real” keyboard events… I’m not sure I understand how that’s possible. Could it be that stage KeyboardEvents are bubbling through ?

      1. Okay, I looked into it and the problem is indeed that Starling’s “real” keyboard events are bubbling up through the display list. They start at the Starling stage and then get passed to SoftKeyboard and Key (because they are both on the stage). I tried a few things but it seems your approach is the best.

        While at it, I figured I’d add full namespacing. The events are now defined like this:

        public static const KEY_UP:String = "cc.cote.feathers.softkeyboard.KeyEvent.keyUp";

        This update was included in revision 5 which is available above. Thanks for letting me know of this issue.

  11. Hi Jean-Phillipe,

    It is written in the doc that “a sample theme is provided in the download package” but I can’t find it neither in the ZIP file or on the github repo. Am I missing something ? Is it possible to find this example somewhere else ?
    Thanks!

    1. You are right. I don’t know what happened to the “example” folder. It’s now back in the download zip and on GitHub. Sorry about that!

  12. Just to note, I was giving this a try and am using BitmapFonts, Key.as TextFormat is causing a problem as my BitmapFont uses BitmapFontTextFormat which works fine for my other labels.

    You also haven’t provided a nameList string for the Label component so that a specific initializer can handle it’s label in the theme class. I haven’t gone through all the code yet so there may be other components that would benefit from being accessible via the theme class.

    Another thing I noticed in the Key.as class was your _buildGenericSkin(), it appears to create a new texture whenever you call it, it would probably be better to create that texture once outside of key class and have each key that needs it reference it instead of creating their own which doesn’t seem to have any benefit?

    1. A static texture variable in the class might work for _buildGenericSkin()? Then you’d just create the shape and texture only if a texture doesn’t exist, after that bit of code then return a new Image with the texture.

      1. That’s a good idea. I’ll try to squeeze this in the next release. Cheers!

    2. Thanks for the comment and suggestions.

      I added names to the label’s nameList property. All labels are identified with the name softkeyboard-key-label and a name matching the content of the label (A, B, BACKSPACE, etc.). I added yet another name that exposes the character code and looks like: charCodeXX (where XX is the charCode). Finally, labels belonging to keys that have variants will have the hasVariant name as well. This should give you the flexibility needed. You can get the updated version on GitHub.

      Regarding bitmap fonts, I will have to take more time to look at it (which, unfortunately I do not have right now).

    3. The BitmapFontTextRenderer now works properly. You can look at the constructor in the Theme.as file (in the example folder) for a working sample. The changes and the new example are included in the latest release (v1.0a rev8) which is available on this page and on GitHub.

      Thanks again for notifying me of the issue!

  13. Key.as line 624, get charCode() returns a uint not an int, Any reason you type charCode as int but the getter as uint?? Was wondering why my theme wasn’t picking up on my negative integers when checking key.charCode. I’ve noticed you use unit over int for most things, so I’m unsure which is the intended type.

    I ended up changing the type to a custom string, but now my regularSkins are not being used, turns out I have to define specialSkins instead, would be nice if like with Feathers Button, a defaultSkin can be used and any states/skins with no set displayObject could reference the defaultSkin if it exists before resorting to the generic function.

    For my app I’m actually after a softkeyboard that slides up from the bottom edge with 1-2 rows, ideally my button skins would be the size I’ve defined in my theme/texture and I could use a layout like horizontalLayout or tiledRows which is configured by my theme class. I’ve got two key sets, one is like a numpad the other adds a value based on the icon/key pressed. Going to try get it working with your softKeyBoard class, though I might be better learning from it and making a more simple version for my use case.

    SoftKeyBoard.as line 418, I think you meant r.height not key.height (key is only defined in the previous loop, if you did intend on referencing key, key should be defined outside of the previous loop).

    If you want to get the keyboard to support the way I was after, the first loop that resizes and positions keys is commented out, the 2nd loop was then edited as below:


    var tWidth:Number = 0
    var tHeight:Number = 0
    var k:int //not 100% but I think it's better to define vars prior to the loop
    var r:KeyRow
    for (k = 0; k = 0 for the rows, this will resize the component. A viewport like in Feathers Panel class might be better at least for my usecase instead of defining component width/component based on row content.
    this.width = tWidth
    this.height = tHeight

    To support the above changes Key.as would need the following modification to draw():

    //relativeWidth and Height will be used on the scaleX/Y attributes to preserve width/height, unless a Scale9Image(should include Scale3Image too), readjustSize() resets to texture width/height, which is then multiplied by relativeWidth/Height,
    if (_skin is Scale9Image)
    {
    var s:Scale9Image = _skin as Scale9Image;
    s.readjustSize();
    _skin.width *= relativeWidth
    _skin.height *= relativeHeight
    }
    else
    {
    _skin.scaleX = relativeWidth;
    _skin.scaleY = relativeHeight;
    }
    //The new size is then set, in the original code this line was called first
    setSizeInternal(_skin.width, _skin.height, false);

    I know what width/height I can set the component to, but I do not know how many “keys” will be in a layout, I’d rather have the keyboard aligned within the width/height without distortion.

    Since I typed this earlier I’ve worked on it some more, KeyRow class is a LayoutGroup instead of Sprite and applies a HorizontalLayout, in SoftKeyBoard.as Keyboard and all variables/functions that reference it’s type were changed to LayoutGroup, a VerticalLayout is applied, you can then set the gaps within the theme class along with alignment such as centered rows. In SoftKeyBoard.as, layoutIndex() setter validates the keyboard/LayoutGroup at the end of the function, in draw() I resize the component to the current keyboards width/height, it is repositioned correctly in the class that uses/holds the softKeyBoard component. Thoughts?

    1. Ignore the tHeight/tWidth bits, forgot to remove that when I came back to finish the message.

    2. I’ll to have digest your message in smaller bits as I’m lacking free time right now.

      So, regarding the Key.charCode type, the right type is int because some special keys have negative values. You can look into the CharCode class for all details. I changed the getter’s type to int.

    3. Your use of LayoutGroup makes a lot more sense. Had I better understood layout groups when I created this, I would have used them. Would you be interested in contributing your edits on GitHub ?

      1. I could give it a go. I was rather destructive to the codebase though, getting it to support the older method of sizing buttons and fitting the keyboard to the components size might take some work.

  14. Hello. It’s nice to have that component in as3, thank you. I have a question about how to proper create my own custom keyboard, any tutorials or docs?

    1. The best way is to copy one of the class in the layouts subpackage. If you look at any class in the package, such as Qwerty.as, you will see that the rows vector contains the keys for each row of the keyboard. You can simply modify them to suit your needs. I’m in a bit of a rush right now. If that’s not enough to get you started, you can ask more questions and I should have a little more time next week to help you out.

      1. Thank you for quick respond, i will try to build keyboard with your recommendation. And i have another question about using bitmap fonts with keyboard, is it possible?

        1. Yes, it’s possible since version 1.0a rev8. You can look at the constructor in the Theme.as file (in the example folder) for a working sample (make sure you download the latest version).

        1. It’s probably a problem with your BitmapFontTextRenderer configuration. Check out the Theme.as file in the example folder (as stated above).

  15. Please remove my previous questions, because i find answers by myself.
    But i have another question. Now keyboard is about 60 drawcalls and it’s a lot for my needs (using for mobile game). Is there any way to optimize it by batching and at touch or texture show\hide\replacement technique?

  16. Hi,
    I am trying to implement that most basic form your keyboard but I keep getting the error:

    TypeError: Error #1034: Type Coercion failed: cannot convert cc.cote.feathers.softkeyboard.layouts::Qwerty@aebc1f1 to cc.cote.feathers.softkeyboard.SoftKeyboard.

    All the code I am using is:

    public function foo(){	
        var layout:Layout = new Qwerty();
        var keyboard:SoftKeyboard = SoftKeyboard(layout);
        keyboard.addEventListener(KeyEvent.KEY_UP, onKeyUp);
    }
     
    public function onKeyUp(e:KeyEvent):void{
    	trace(e);
    }

    Any help would be greatly appreciated!

    1. Hello Jacob,

      You are missing the new keyword on the 3rd line. It should be:

          var keyboard:SoftKeyboard = new SoftKeyboard(layout);

      Don’t kick yourself too hard 😉

  17. Hi Jean-Philippe,

    Thank you for this wonderful piece of code.
    Nevertheless, I just opened an issue on GitHub repo regarding an error raised while trying to show the callout with a long press on a key. I really would appreciate that you have a look at it, since this prevents me to use your nice component in my project. https://github.com/cotejp/SoftKeyboard/issues/3
    Thanks for your time and thanks again for your work.

    Vincent

  18. Hi, is there any plans to migrate this to feathers 2.3 ? currently it doesn’t run using hat version.
    Regards

  19. FYI, I was able to get this to work with feathersui 3.1.2 and the latest starling 2.1 with only a small number of changes. Basically in key.as change all the ‘nameList’ references to ‘styleNameList’ and remove the ‘true’ second parameter from ‘.hitTest’. Softkeyboard.as had 2 deprecated vars and flash builder recommends the replacement. Finally i did have to add this to KeyEvent.as since it seems to be missing :

    public function formatString(format:String, ...args):String
    {
        for (var i:int=0; i<args.length; ++i)
        format = format.replace(new RegExp("\\{"+i+"\\}", "g"), args[i]);
    			
        return format;
    }

    I have not done thorough testing yet, but it at least compiled and I was able to use the sample code from this page to pop up a qwerty keyboard and trace key presses to the console log.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.