Monthly Archives: April 2013

Tips for Running Microsoft’s Windows Phone 8 Emulator in VMWare

Now that Sencha Touch supports Windows Phone 8, I thought that I’d give one of my new apps a whirl in Microsoft’s Simulator to see how well it worked.

Of course, as is frequently the case with products from Mr. Softie, things are never as easy as they seem – particularly when running their products on a mac.

First and foremost, you’ll need to purchase Windows 8 Professional 64-bit…and a machine with at least 8 GB of RAM (I’m running a 13″ Macbook Air)

Install Windows 8 into a new VM with the following settings:

  • Disk space: 30 GB (~20 GB will be used by the OS and Microsoft Vis Studio)
  • Memory: 3.5GB
  • CPU: 2 Cores (minimum)

After completing the installation process for Win 8, shut it down, go into your VM settings and select the following:

  • Settings > CPU and Memory > Advanced > Enable hypervisor applications (checked)
  • Settings > Advanced > Preferred virtualization engine: Intel VT-x with EPT
  • General > OS > Hyper-V (unsupported)

Next, launch Windows and install the Microsoft Windows Phone SDK from the following URL:

http://dev.windowsphone.com/en-us/downloadsdk

Once the SDK (which includes Visual Studio Express) has been installed and licensed, you can launch the emulator by running Visual Studio Express 2012 for Windows Phone, creating a new Windows Phone 8 project, and then clicking on the Emulator WVGA 512MB button. 

By default, your hardware keyboard is disabled in the emulator. Enable the hardware keyboard by selecting VMware Fusion > Virtual Machine > Send Key > Pause Key.

Conversely, you can disable keyboard support in the emulator by repeating the enablement steps.

From the emulator, you can browse your web server in OSX via your OS/X IP Address (System Preferences > Network)

Happy testing!!!

jQuery Mobile vs. Sencha Touch vs. Appcelerator Titanium!

During this 2 hour executive briefing for developers and I.T. managers, you’ll get an in-depth review of the three most popular javascript-based mobile application development platforms – jQuery Mobile, Sencha Touch, and Appcelerator Titanium! You’ll review the development tools, hear about the developer and debugging experience, and participate in a brief code review in order to determine which framework is the most appropriate for your next mobile project! We’ll demonstrate how each framework can be used to develop the same mobile app, highlighting the strengths and weaknesses of each.

Check out the synopsis here:

http://www.slideshare.net/stevedrucker/mobile-platforms-19979061

Check out the 2.5 hour Thunderdome video special here:

http://figleafsoftware.adobeconnect.com/p27spbhvp8d/

And for jQuery Mobile, Sencha Touch, or Appcelerator Titanium training or consulting, please contact us!

Selecting a Random Row from a Web SQL Table

There seems to be a bug in Webkit that generates an exception when trying to use the RANDOM() method to retrieve a random record from a database table.

Here’s a work-around:

db.runQuery("select count(*) as thecount from friend", 
            function(records) {
             var randomNum = Math.floor(Math.random() * records[0].thecount) + 1;
             db.runQuery(new String().concat(" select *",
                                             " from friend",
                                             " where id not in ( ",
                                             "    select id ",
                                             "    from friend ",
                                             "    limit " + (randomNum - 1),
                                             " )",
                                             " limit 1"), 
                         function(record) {
                            // random record returned here!
                            console.log(record);
                         }
              );
            }
          );

Here’s the runQuery function that I wrote in order to abstract some of the websql technobabble:

this.runQuery = function(sql, callback) {
        this.db.transaction(function (tx) {
            tx.executeSql(sql, 
                          [],
                          function(tx,rs) {
                            var a = [];
                            for (var i=0; i < rs.rows.length; i++) {
                                a[a.length] = rs.rows.item(i);
                            }
                            callback(a);
                          },
                          function() {             
                            console.log('fail',arguments);
                          }
            )
        });
 }

Tap and Drag Animation Domination Part 3 of 3: Implementing Drag and Drop in Sencha Touch 2

In this final blog article, we’ll look at how to implement the animated tap and drag ui depicted below. If you haven’t already done so, please read the following related posts:

The tap and drag interface is comprised of the following two custom classes:

  • DD.view.DraggableItem : An Ext.Container with custom styling to implement rounded edges and box shadow as well as configurable label and id properties
  • DD.view.DDEditor : An Ext.Container that instantiates the draggable items, sets up the drop grid, and handles drag and drop activities.

Reviewing the DragableItem Class

The DraggableItem class is fairly straightforward – it’s simply a themed container with a toolbar that could potentially have buttons added to it for use in some other context.

Ext.define('DD.view.DraggableItem', {
 extend: 'Ext.Container',
 alias: 'widget.draggablesetting',

 requires: ['Ext.Label'],

 config: {
  label: '',       // configurable text label
  sectionId: 0,    // pass in a unique identifier
  cls: 'draggablesetting', // unique dom selector
  initialIndex: 0,
  
  // configure physical characteristics
  // of the draggable
  height: 60,
  margin: '0 10 0 10',
  style: 'border-radius: 5px; background-color: lightgray; opacity: 0.0; box-shadow: 0px 0px 10px rgba(50, 50, 50, 0.75);',
  width: 380,
  
  // subcomponents      
  items: [
   {
    xtype: 'container',
    docked: 'top',
    height: 50,
    itemId: 'toolbar',
    layout: {
     type: 'hbox'
    },
    items: [
     {
      xtype: 'label',
      flex: 1,
      height: 50,
      margin: '5 5 0 5'
     }
    ]
   } 
  ] // draggable subitems
 },

 initialize: function() {
  this.callParent(arguments);
  // set the label on the draggable
  this.down('label').setHtml(this.getLabel());
 }
});

Reviewing the Drag and Drop Container

The DDEditor class is a lot more complicated as it implements all of the drag and drop logic and animations used in the example.

The class starts out simply enough by extending the Ext.Container class and docking the titlebar, instructions text, and button bar at the top and bottom of the panel.

Note the following:

  • The slots property is an array that tracks the current sorted order of the draggable items
  • The save button handler throws an event that passes an array containing the label/id properties of the draggables (in the order in which the user sorted them) up to a controller.
  • The Ext.Anim class is required in order to add the groovy animations, discussed later in this post.
  • The entire body of the container, minus the area occupied by the docked components, becomes the drag/drop zone.
Ext.define('DD.view.DDEditor', {
 extend: 'Ext.Container',
 alias: 'widget.arranger',
 requires: ['Ext.Anim', 'Ext.TitleBar'],
 
// private properties
 slots : [], // an array representing the item present in 
             // each drop area
 slot : {},
 
 // 80 pixels per "drop slot"
 spacing : 80,

 config: {
  centered: true,
  height: 540,
  width: 400,
  style: 'background-color: white; webkit-box-shadow: 0px 3px 14px rgba(51, 50, 50, 0.65)',
  title: "Section Editor",
  layout: { type: 'fit'},
  modal: true,
  hideOnMaskTap: true,      
  hideAnimation: {
   type: 'fadeOut',
   duration: 200
  },
  
  // draggables for the window      
  dragItems: [
          { id: 1, label: 'Section One' },
          { id: 2, label: 'Section Two' },
          { id: 3, label: 'Section Three' },
          { id: 4, label: 'Section Four' }
  ],

  items: [
   {
    xtype: 'toolbar',
    docked: 'bottom',
    itemId: 'toolbar',
    layout: { pack: 'end', type: 'hbox'},
    items: [
      {
       xtype: 'button',
       left: 5,
       top: 6,
       text: 'Reset',
       handler: function(button, event) {
         var top = button.up('arranger');
         top.resetSettings.call(top); // described later
       }
      },
      {
       xtype: 'button',
       ui: 'confirm',
       text: 'Save',
       handler: function(button, event) {
         var top = button.up('arranger');
         var cmp = {};
         var results = [];
         for (var i=0; i<top.slots.length; i++) {
           cmp = Ext.getCmp(top.slots[i].id);
           results.push({
             id: cmp.getSectionId(),
             label: cmp.getLabel()
           });
          }
          console.log('order of selections: ', results);
          top.fireEvent('save',top,results);
       }
      }
     ]
    },
    {
      xtype: 'titlebar',
      docked: 'top'
    },
    {
      xtype: 'component',
      docked: 'top',
      height: 70,
      html: '<p style="font-size: 0.8em">Tap and drag fields to customize your view. Tap the Save button to save your preferences. Tap Reset to revert to the previous layout.</p>',
      itemId: 'instructions',
      margin: '5 5 20 8'
     }
   ],
   listeners: [
    {
     fn: 'onContainerHide',
     event: 'hide'
    }
   ]
 },

 onContainerHide: function(component, eOpts) {
  component.destroy();
 }
});

Instantiating the Draggables

The initialize() method of the component sets the title of the titlebar and instantiates the draggable items into the ui.

initialize: function() {
 
 this.callParent(arguments);
 
 // set the text on the titlebar
 this.down('titlebar').setTitle(this.getTitle());

 // define draggable sections
 var layoutItems = [];
 
 var sections = this.getDragItems();
 this.numItems = sections.length; // cache num items for spacing calculation
 var draggable = {};

 // build array of draggables

 for (var i=0; i<sections.length; i++) {
   draggable = {
     xtype: 'draggablesetting',
     hidden: false,
     initialIndex: i,
     draggable: {
       direction: 'vertical',
       listeners: {
        drag: this.onDrag,
        dragend: this.onDrop,
        dragstart: this.onDragStart,
        scope: this
       }
     },
     itemId: 'draggable' + i,
     label: sections[i].label,
     sectionId: sections[i].id
   }; // draggable 
   layoutItems.push(draggable);
 }
 
 // add draggables to the container
 this.add(layoutItems);
 
 // a little bit of a simple kludge here - 
 // delay initializing draggables until they've been 
 // fully instantiated.
 Ext.Function.defer(this.initializeDraggables,250,this);
}

Implementing the initial fade-in animation

The initializeDraggables() method performs the following two functions:

  • It fills the slots array with pointers to the top-level dom elements that were created by instantiating the ‘draggablesetting’ components
  • It animates the move of each draggable into its initial associated drop slot
initializeDraggables: function() {
 
 var me = this;
 this.slots = [];
 
 // get pointers to the top-level dom elements
 // created by each draggable component

 var els = Ext.select("div.draggablesetting");
 
 // loop through the draggables and animate their move
 // to an initial position
    
 for (var i=0; i<els.elements.length; i++) {

   var el = Ext.get(els.elements[i]);
   
   // calculate each draggable initial, vertical position
   var tf = 'translate3d(0px, ' + (( i * this.spacing) + 1) + 'px,0px)';

   // cache a pointer to the draggable dom element
   this.slots[i] = el;

   // run the initial fade/move animation
   // note that animations run asynchronously

   Ext.Anim.run(el, new Ext.Anim({
     autoClear: true,
     easing: 'ease-in',
     duration: 500,
     from: {
      '-webkit-transform': 'translate3d(0px,0px,0px)',
      'opacity': 0.0
     },
     to: {
      '-webkit-transform': tf,
      'opacity' : 1.0
     },
     after: function(el) {
      // after animation is complete, formally
      // move component into initial position

      // get pointer to related Ext.Component
      var cmp =  Ext.getCmp(el.getId());

      // position the initial drag position of the component
      cmp.getDraggable().setOffset(0,cmp.getInitialIndex() * me.spacing);
     }
  }));
  }
}

Handling Drag Actions

The following three handlers take care of the drag action:

  • onDragStart : Determines the slot in which the draggable is currently sitting
  • onDrag : Runs animations on the draggables that are being encroached upon by the item currently being dragged.
  • onDrop: Locks the draggable components into their new positions
onDragStart: function(el, e, offsetX, offsetY, eOpts) {
 this.slot = Ext.Number.toFixed(offsetY / this.spacing,0);
},
    
onDrag: function(dd, e, offsetX, offsetY, eOpts) {
 el = dd.getElement();

 // collision detection on slot
 var oldslot = this.slot;
 var slot = Ext.Number.constrain(Ext.Number.toFixed(offsetY / this.spacing,0),0,this.slots.length);

 if (slot != this.slot && slot >= 0) {
  this.slot = slot;

  // move item in new slot out of the way
  var tf1 = 'translate3d(0px, ' + ( this.slot * this.spacing) + 'px,0px)';
  var tf2 = 'translate3d(0px, ' + ( oldslot * this.spacing) + 'px,0px)';

  Ext.Anim.run(this.slots[this.slot], new Ext.Anim({
                autoClear: false,
                easing: 'ease-in',
                duration: 500,
                from: {
                    '-webkit-transform': tf1
                },
                to: {
                    '-webkit-transform': tf2
                }
   }));

   var temp = this.slots[this.slot];
   this.slots[this.slot] = el;
   this.slots[oldslot] = temp;
 }
},

onDrop: function(el, e, offsetX, offsetY, eOpts) {
 
 // snap all draggables into their "slots"
 for (var i=0; i<this.slots.length; i++) {
   var cmp =  Ext.getCmp(this.slots[i].getId());
   cmp.getDraggable().setOffset(0,i * this.spacing);
  }
}

Resetting the draggables back to their initial positions

The resetSettings() method, illustrated below, applies an animation to each of the draggables, causing them to float back up to the top of the panel.

Once the animation is complete (after 400 ms), the draggable components are all destroyed and the component’s initialize() method is re-invoked, thereby starting the process anew.

resetSettings: function(settings) {
  // roll back up
  for (var i=0; i<this.slots.length; i++) {
    Ext.Anim.run(this.slots[i], new Ext.Anim({
      autoClear: false,
      easing: 'ease-in',
      duration: 400,
      from: {
        '-webkit-transform': this.slots[i].getStyle('-webkit-transform')
      },
      to: {
        '-webkit-transform': 'translate3d(0px,0px,0px)' 
      }
    }));
   }

   Ext.Function.defer(function() {
    var draggables = this.query('draggablesetting');
    for (var i=0; i<draggables.length; i++) {
       draggables[i].destroy();   
    }       
    this.initialize();
   },400,this);
}

Run the Example and Download the Code!

Tap and Drag Animation Domination Part 2: Implementing Custom Animations with Ext.Anim


Note: This is part of a three-part series on drag and drop animation in Sencha Touch 2. Click here to read part 1.

Use the Ext.Anim.run() method to execute custom animations from your Sencha Touch apps. Note that Ext.Anim.run() can only operate on DOM elements. Therefore, if you wanted to define a custom fade effect on an Ext.Container component, your code would need to appear similar to the following:


Ext.Viewport.add(
 {
  xtype: 'component',
  html: 'Beam me up, scotty!',
  style: 'background-color: silver; opacity: 0.0',
  height: 200,
  width: 200,
  listeners: {
    show: function(cmp) {

     // define custom fade animation on the component
      Ext.Anim.run(cmp.element, new Ext.Anim({
          autoClear: true,    // clear final animation effect
          easing: 'ease-in',
          duration: 1000,     // transition over 1 sec
          from: {
           'opacity': 0.0     // fade from fully transparent
          },
          to: {
           'opacity' : 1.0    // end effect will be opaque
          },
          after: function(el) {
            // after animation is complete,
            // set the component to be fully opaque
            var cmp =  Ext.getCmp(el.getId());
            cmp.setStyle('background-color: silver; opacity: 1.0');
          }
       }));
     }
   }
}

Note that prior to invoking the Ext.Anim singleton, you must load the class into memory using either the Ext.require() method or the requires config property of a component.

You can put any CSS3 property that is supported by your device into the from/to config blocks. Note, however, that any property that is present in the from config object must also be specified in the to object.

Before you consider implementing all kinds of zany CSS-3 based animations, consider that not all CSS3 animations are performant across all devices. Performance in Desktop Chrome and the iOS Simulator is not indicative of how well your animation will perform on an actual iPhone 5, iPad, or Android device. I strongly urge you to set up a small test harness, as indicated above, to test complex animations on physical devices before dropping them into your app’s codebase.

Tap and Drag Animation Domination Part 1: Working with the DOM

Working with DOM elements in Sencha Touch 2 is a topic that has very little coverage from within the official documentation and the community at-large. Officially, you are strongly encouraged to develop apps using only the rich set of components that Sencha has provided to you in their framework. However, if you need to impress your manager by creating custom Touch 2 components or extend out-of-the-box components with custom gestures and animations, it is vital that you understand how the DOM system operates. Because nothing impresses I.T. managers more than a completely gratuitous, well-executed animation!

Working with DOM elements in Touch 2

The first thing to understand about Components is that they generate DOM elements as illustrated by the following example

Sencha Touch Generated Output
 Ext.Viewport.add({
  xtype: 'component',
  html: 'Hello World',
  itemId: 'mycomponent',
  cls: 'customstyle'
 });
<div class="customstyle" 
     id="ext-component-1">
  <div class="x-innerhtml" 
      id="ext-element-9">
     Hello World
  </div>
</div>

You can programmatically access the top level DOM element that is created by a component (typically a <div>) by referencing the component.element property as illustrated below:

var el = Ext.ComponentQuery.query("#mycomponent")[0].element;

You can also access the DOM elements generated by components by invoking the Ext.select() method, targeting any CSS style classes that are generated by the component as illustrated by the following snippet:

// get first div tag with css style class of 'customstyle'
var htmlEl = Ext.select('div.customstyle').elements[0];

// convert to Ext.dom.Element reference
var el = Ext.get(htmlEl);

Moving between Ext.dom.Element and their corresponding components

Since the DOM element typically has the same ID property as the Sencha Touch Component that generated it, you can usually get a pointer back to the Component by using the following syntax:

// get DOM element from component
var el = Ext.ComponentQuery.query("#mycomponent")[0].element;

// get Component reference from DOM component
var cmp = Ext.getCmp(el.dom.id);

Ext.dom.Element instances are wrappers for HTML DOM elements. They give you a programmatic interface that normalizes the differences between different browser implementations. Much like another wrapper, Vanilla Ice, they don’t look like much on first glance:

dom1

However, once you dig deeper, realize that you’ve actually pulled an instance of the Sencha Touch 2 Ext.dom.Element class, and discover that there are 117 additional methods that you can invoke as well as 19 events that you can listen for (including pinch, rotate, and swipe gestures), a joyous nerdgasm is sure to follow!

Attaching DOM event listeners to Component Elements

Attaching a DOM event listener, such as a taphold gesture to an element generated by a component now becomes a relatively straightforward affair. Here’s an example that echoes the high-brow conversation of my children as I drive them to school:

el.on('taphold',
      function(event, node, options,eOpts) { 
        Ext.Msg.alert("Stop touching me!",
                      "Would you stop touching me??"
        );
      }
);

You can also attach a DOM element event listener directly to a component using the component’s listeners config property as illustrated below:

Ext.Viewport.add({
 xtype: 'component',
 html: 'Hello World',
 cls: 'customstyle',
 itemId: 'mycomponent',
 listeners: {
  element: 'element',
  'taphold': function(event, node, options,eOpts) { 
     Ext.Msg.alert("Stop touching me!",
                   "Would you stop touching me??"
     );
  }
 }
});

Now, a potential problem here is that the event listener for the taphold event returns a reference to the DOM element that fired the event (the node param) instead of the component instance that generated the DOM element. (In our case, this is likely to return back the DOM element “ext-element-9”). Fortunately, if you bind the DOM event listener to the component directly, the eOpts argument contains the id of the top-level container that had the event listener attached to it, e.g. #ext-component-1. You can simply strip the # from the string and pass it to Ext.getCmp() to get a pointer back to the Component.

Ext.Viewport.add({
 xtype: 'component',
 html: 'Hello World',
 cls: 'customstyle',
 itemId: 'mycomponent',
 listeners: {
  element: 'element',
  'taphold': function(event, node, options,eOpts) { 
              Ext.Msg.alert("Stop touching me!",
                            "Would you stop touching me??"
              );
              console.log('component ref:',   
               Ext.getCmp(eOpts.info.target.substring(1))
              );
            } // end function
  } // end listeners
}); // end viewport add

Now that you know how to access DOM elements from their generating Components (and vice-versa), you can enhance components by adding new behaviors and animations. I’ll cover this in more detail in another blog post later today.

Changes to Sencha Touch 2.2 Theming Part 1: Using Iconography

Sencha Touch 2.2 handles iconography very differently than its predecessors. Whereas Touch 1.x-2.1 base-64 encoded PNG files into your app’s stylesheet, Touch 2.2 instead uses a web font from http://pictos.cc/font/ to render iconography in your app’s buttons. This new methodology should significantly improve the performance of your app, particularly with respect to memory consumption. Unfortunately, you will likely need to spend a small bit of time refactoring your code as the strings used to identify icons do not match up to the icon library that had been available in previous versions of Touch. More importantly, the procedure for incorporating your own custom iconography has changed as you will now need to encode your custom icons into a web font (covered in part 2 of this blog entry).

Reviewing the Out of the Box Icons

I’ve catalogued the new iconography and their corresponding string identifiers below:

anchor box c d e  f g i
anchor box upload forbidden lightning rss  team info
 k  l m  n  o  p  q  r
heart list list table folder pencil chat2 retweet
t u v w  x  y A B
time switch camera chat settings2 settings  attach
ment2
bird
D  E  F  G  H  I  J  K
 delete_
black1
eye  file  browser home inbox  network key
N  P  Q  R  S  T  U
 V
news photos power action favorites plane user video
truck  Z  expand  refresh  check  check2  play  pause
truck chart expand refresh check check2 play pause
forward  rewind  play2 refresh2  minus battery left right
forward rewind play2 refresh2 minus battery left right
 wireless spedometer  more  print  download  warning_black  locate  trash
wireless spedo
meter
more print down
load
warning
_black
locate trash
 bank flag  add  delete  lock unlock  minus2  up
bank flag add delete lock unlock minus2 up
 screens bell quote volume_mute volume help  arrow_left  arrow_right
screens bell quote volume
_mute
volume help arrow
_left
arrow
_right
 arrow_down  organize  star  maps  reply  attachment  s
arrow
_down
organize book
marks
star maps reply attach
ment
search
 C  W
M stop shuffle cart down arrow_up
cloud compose mail stop shuffle cart down arrow
_up

Behind the scenes, the SCSS mixin icon-character-for-name maps the iconCls icon identifier to a specific character in the pictos font. This mixin is located at the following location:

touch-2-2-0/resources/themes/stylesheets/sencha-touch/base/mixins/_Class.scss


@function icon-character-for-name($name) {
    // http://pictos.cc/font/

    // Row 1
    @if ($name == "anchor") { @return "a"; }
    @else if ($name == "box") { @return "b"; }
    @else if ($name == "upload") { @return "c"; }
    @else if ($name == "forbidden") { @return "d"; }
    @else if ($name == "lightning") { @return "e"; }
    @else if ($name == "rss") { @return "f"; }
    @else if ($name == "team") { @return "g"; }
//    @else if ($name == "h") { @return "h"; }
    @else if ($name == "info") { @return "i"; }
    @else if ($name == "attachment") { @return "j"; }
    @else if ($name == "heart") { @return "k"; }
    @else if ($name == "list") { @return "l"; }
    @else if ($name == "music") { @return "m"; }
    @else if ($name == "table") { @return "n"; }
    @else if ($name == "folder") { @return "o"; }
    @else if ($name == "pencil") { @return "p"; }
    @else if ($name == "chat2") { @return "q"; }
    @else if ($name == "retweet") { @return "r"; }
    @else if ($name == "search") { @return "s"; }
    @else if ($name == "time") { @return "t"; }
    @else if ($name == "switch") { @return "u"; }
    @else if ($name == "camera") { @return "v"; }
    @else if ($name == "chat") { @return "w"; }
    @else if ($name == "settings2") { @return "x"; }
    @else if ($name == "settings") { @return "y"; }

    // Row 2
    @else if ($name == "attachment2") { @return "A"; }
    @else if ($name == "bird") { @return "B"; }
    @else if ($name == "cloud") { @return "C"; }
    @else if ($name == "delete_black1") { @return "D"; }
    @else if ($name == "eye") { @return "E"; }
    @else if ($name == "file") { @return "F"; }
    @else if ($name == "browser") { @return "G"; }
    @else if ($name == "home") { @return "H"; }
    @else if ($name == "inbox") { @return "I"; }
    @else if ($name == "network") { @return "J"; }
    @else if ($name == "key") { @return "K"; }
//    @else if ($name == "") { @return "L"; }
    @else if ($name == "mail") { @return "M"; }
    @else if ($name == "news") { @return "N"; }
//    @else if ($name == "") { @return "O"; }
    @else if ($name == "photos") { @return "P"; }
    @else if ($name == "power") { @return "Q"; }
    @else if ($name == "action") { @return "R"; }
    @else if ($name == "favorites") { @return "S"; }
    @else if ($name == "plane") { @return "T"; }
    @else if ($name == "user") { @return "U"; }
    @else if ($name == "video") { @return "V"; }
    @else if ($name == "compose") { @return "W"; }
    @else if ($name == "truck") { @return "X"; }
//    @else if ($name == "") { @return "Y"; }
    @else if ($name == "chart") { @return "Z"; }

    // Row 3
    @else if ($name == "expand") { @return "`"; }
    @else if ($name == "refresh") { @return "1"; }
    @else if ($name == "check") { @return "2"; }
    @else if ($name == "check2") { @return "3"; }
    @else if ($name == "play") { @return "4"; }
    @else if ($name == "pause") { @return "5"; }
    @else if ($name == "stop") { @return "6"; }
    @else if ($name == "forward") { @return "7"; }
    @else if ($name == "rewind") { @return "8"; }
    @else if ($name == "play2") { @return "9"; }
    @else if ($name == "refresh2") { @return "0"; }
    @else if ($name == "minus") { @return "-"; }
    @else if ($name == "battery") { @return "="; }
    @else if ($name == "left") { @return "["; }
    @else if ($name == "right") { @return "]"; }
    @else if ($name == "shuffle") { @return ";"; }
    @else if ($name == "wireless") { @return "'"; }
    @else if ($name == "speedometer") { @return ","; }
    @else if ($name == "more") { @return "."; }
    @else if ($name == "print") { @return "/"; }

    // Row 4
    @else if ($name == "download") { @return "~"; }
    @else if ($name == "warning_black") { @return "!"; }
    @else if ($name == "locate") { @return "@"; }
    @else if ($name == "trash") { @return "#"; }
    @else if ($name == "cart") { @return "$"; }
    @else if ($name == "bank") { @return "%"; }
    @else if ($name == "flag") { @return "^"; }
    @else if ($name == "add") { @return "&"; }
    @else if ($name == "delete") { @return "*"; }
    @else if ($name == "lock") { @return "("; }
    @else if ($name == "unlock") { @return ")"; }
    @else if ($name == "minus2") { @return "_"; }
    @else if ($name == "add2") { @return "+"; }
    @else if ($name == "up") { @return "{"; }
    @else if ($name == "down") { @return "}"; }
    @else if ($name == "screens") { @return "|"; }
    @else if ($name == "bell") { @return ":"; }
    @else if ($name == "quote") { @return "\""; }
    @else if ($name == "volume_mute") { @return "<"; }     @else if ($name == "volume") { @return ">"; }
    @else if ($name == "help") { @return "?"; }

    // Backwards compat; icons that are not in the font
    @else if ($name == "arrow_left") { @return "["; }
    @else if ($name == "arrow_right") { @return "]"; }
    @else if ($name == "arrow_up") { @return "{"; }
    @else if ($name == "arrow_down") { @return "}"; }
    @else if ($name == "organize") { @return "I"; }
    @else if ($name == "bookmarks") { @return "I"; }
    @else if ($name == "star") { @return "S"; }
    @else if ($name == "maps") { @return "@"; }
    @else if ($name == "reply") { @return "R"; }

    @else {
        // @debug "#icon: icon with name '#{$name}' not found.";
        @return false;
    }
}

Using the Out of the Box Icons

In prior versions of Sencha Touch you would use the SASS pictos-iconmask() mixin to encode a png file and add appropriate style references into your generated CSS file. In Touch 2.2, however, you’ll need to use the new icon() mixin  that generates a style class referencing the new pictos webfont. The sourcecode for the new icon() mixin is listed below:

/**
 * Includes an icon to be used on Button or Tab components. The $name is the name of the icon, or the character
 * of the font being used.
 *
 *     @include icon('attachment');
 *
 * @param {string} $name The name of the icon to be included.
 * @param {string} $font-family The `font-family` used for this icon. Defaults to the `Pictos` font.
 */
@mixin icon($name, $font-family: false) {
    .x-tab .x-button-icon.#{$name},
    .x-button .x-button-icon.#{$name} {
        $character: icon-character-for-name($name);

        &:before {
            @include absolute-position;
            text-align: center;

            @if $font-family {
                font-family: $font-family;
            }

            @if $character {
                content: "#{$character}";
            } @else {
                content: "#{$name}";
            }
        }
    }
}

Therefore, if you wanted to include a button that used the “network” icon, you would have to add the following to your .SCSS file:

@include icon('network');

Inside of your application’s source files, you would reference the icon name as an iconCls configuration property for a button component or tab as illustrated by the following snippet.

{
 xtype: 'button',
 iconCls: 'network',
 iconAlign: 'top',
 text: 'Net'
}

Note: The iconMask configuration property is no longer required on a button torender the icon.

[CLICK HERE FOR PART 2 OF THIS TWO-PART SERIES]