Showing posts with label icon fonts. Show all posts
Showing posts with label icon fonts. Show all posts


I think everyone who deploy a first app on Windows Phone is facing the same problem: The app is running fine, but where the heck are my icons?

Apparently there is an issue present in Windows Phone 8 that prevents custom font-faces from working when HTML and CSS are hosted locally.

So we came up with the first solution: Hosting everything remote. But really, this is not a solution, right?

After spending some more time on google, I found another solution: Including the font-file instead of the base 64 encoded font.

Sample Code:
@font-face {
 font-family: 'Pictos';
 src:url('fonts/pictos-web.woff') format('woff');
 font-weight: normal;
 font-style: normal;
}
However, this code never worked for me. Some user reported that it works, but the rest said that it doesn't work for them too or only for some and not all of their font files.

Finally I came up with another idea: When I cannot rely on the CSS font-face, then I'll go back to the roots and display pictures instead. Of course this costs more effort and is not as elegant as using icon fonts, but so far this seems to be the only safe solution to get it done.

Before we start, let me tell you that we don't have to modify any of the Java Script Code and it will not affect other platforms like Android or iOS.

If you are already working with Themes (what I would suggest you) then you can just place the new CSS code to your WP.scss

To reduce the CSS markup, I created a small mixin. In this mixin we set a new background image with height and width, also we replace the characters in content with an empty string:

@mixin wpIcon($name,$dim) {
  $url:"../images/wp/#{$name}.png";
  background: transparent url($url) 0 0 no-repeat;
  content: '';
  height: $dim;
  width: $dim;
  display: inline-block;
}

The sample code above requires that your images are located in "/resources/images/wp".

Replacing images is very straight forward, lets take a look at another sample which shows how to use this mixin:
/* Replace the checkbox checked icon */
.x-input-checkbox:checked ~ .x-field-mask:after {
  @include wpIcon(checkmark, 20px);
}

/* Replace the back button icon and add a second rule for back button pressed */
.x-button-back:before {
  @include wpIcon(arrow-left, 32px);
}

.x-button-back.x-button-pressing:before {
  @include wpIcon(arrow-left-pressed, 32px);
}
As you can see we just pass the image name (without the file extension) + a size to the mixin.

If you don't use the WP.scss yet you can add ".x-windowsphone" as an additional class to your CSS rule. This makes sure that the rule will only apply on Windows Phone.

I hope this trick help you to get your icons back on Windows Phone. Please let me know when you have even a better idea/solution for this problem.


I would like to take this opportunity to explain a few pitfalls that arise when in development with Sencha. This article is designed to help develop Sencha applications faster and more robust.

If you should find that something important is missing or something is wrong, do not hesitate to contact me.

Loading Sencha Framework and external Resources

It is recommended that the Sencha framework is loaded via the Microloader.

The "index.html" should be kept as lightweight as possible to avoid negative side effects. It seems Sencha has problems, for example, when trying to place child elements within the body region.

Below is an example of linking against the Sencha Touch framework:
<!DOCTYPE HTML>
<html manifest="" lang="en-US">
<head>
    <meta charset="UTF-8">
    <title>Your Application</title>
    <script id="microloader" type="text/javascript" src="touch/microloader/development.js"></script>
</head>
<body></body>
</html>
If additional JavaScript files required, these should not be included in the "index.html". Additional JavaScript files and CSS should always defined in the file "app.json".

The defintion of the files in the "app.json ' has the following advantages:
  • Included files are automatically compressed during the Sencha Build process. 
  • Included files can be loaded depending on the target platform. 
  • The application loads faster. 
  • index.html is lightweight.

Styling your Application

When it comes to styling your application, Sencha has a few ways to do this directly in JavaScript. However, this should be avoided. All style information should be defined via CSS or better SASS.
This results in the following advantages:
  • The actual source code is lightweight. 
  • All style information are defined centrally. 
  • The application can be easily edit at the same time by several developers. 
  • The design can be flexibly adapted at any time.

The app.js File

All changes to this file should be as minimal as possible. The reason for this, as soon as a Sencha update is performed, this file must be manually merged with the update.

Using Ext.getCmp()

The use of the function Ext.getCmp() should be largely avoided. The use of this function makes the source code hard to read and maintainable. By using the MVC principle, the function should no longer be needed.
See also MVC in depth Part 1 and MVC in depth Part 2.

In short, if Ext.getCmp() is used, the developer has missed the object-oriented approach.

In many applications, this function is still used to prototype faster or because the programmer does not know better.
When using Ext.getCmp() is essential to make sure that the object exists. I.e. the object must not be 'undefined' when its function is called. Otherwise, the interpreter throws an exception.

Using Ext.Viewport.removeAll() to destroy components

In the life cycle of an application it often happens that components are no longer needed. A good example to log out in an application. It is tempting to destroy all components on the Viewport with the Ext.Viewport.removeAll (http://docs.sencha.com/touch/2.2.1/#!/api/Ext.Container-method-removeAll) command:
Ext.Viewport.removeAll(true, true);
At first glance, all components were destroyed which were located on the Viewport. But what happens if the user now wants to re-login? Furthermore, we assume that the user enters his password incorrectly, and we show via Ext.Msg an error message.
Unfortunately, the following error message is displayed instead Ext.Msg:
Uncaught TypeError: Cannot read property 'dom' of null
Ext.Msg is a singleton instance of Ext.MessageBox. When you do the removeAll(true, true) the second argument is to destroy everything, inner items, docked items and floating items. Ext.Msg is a floating item so you have destroyed Ext.Msg, but you are then trying to use it. Now, if the component is destroyed it ends with the error message above.
It is recommended to destroy all the components separately. Not with "Ext.Viewport.remove" but directly through "myObj.destroy();"

Naming Model, Store and View

In the naming of models, views, and stores should pay attention to the following:
Basically, the name always begins with a capital letter, all the following words also with a capital letter.
  • Model, in the singular. Example: "TrainStation.js" 
  • Store in the plural. Example: "TrainStations.js" 
  • View, in the singular. Example: "TrainStation.js" 
Following this convention, the structure of the application is easier to read and quicker to learn.

JavaScript === versus == (Comparison Operators)

The identity "===" operator behaves identically to the equality "==" operator except no type conversion is done, and the types must be the same to be considered equal.

If values ​​are compared with each other it is recommended to use "===" instead of "==". This makes the program less error prone.

This is also the case for "!==" and "!=".

See also http://www.c-point.com/javascript_tutorial/jsgrpComparison.htm

Stop over nesting the View

When creating new views, make sure that they are as lightweight as possible. I.e. you should not go deeper than it is absolutely necessary in the structure. The use of unnecessary panels is a prime example of this. Is a view nested too deeply, it will have a negative impact on the performance of the application.

Is a view defined with "Ext.View", the view should not contain any action listener. These are defined in the relevant controller. This has the advantage that the view is more legible.

In short, in the view there is no business logic! All business logic should always be to find in the respective controller.

Code Completion

If the application is developed in Eclipse there are two ways to obtain a code completion. The first way is to use Spket. How this works can be found here. The second alternative is to purchase Sencha Complete. This version includes the necessary Eclipse plugins.

JSLINT for better source code

For the development of JavaScript applications, it is recommended to use JSLint. Who JSLint does not know yet, thus it is possible to validate your code against best practices. How to configure Eclipse with JSLint is described here.

Icon fonts instead of the classic icons

One element of themes in Sencha Touch 2.2 is the new use of icons as fonts. This is an awesome feature, because this offers the following new features:
  • size can be easily changed. 
  • color can be easily changed. 
  • shadow can be easily added to their shape. 
  • transparency nearly everywhere, unlike transparent png's in ie6. 
  • smaller in file size 
  • infinitely scaleable (because vector)
The goal of using icon fonts is to replace the need to render icons on a web page using images.

Click here to continue...

Using ComponentQueries instead of Id Config

Avoid using the id config, let ComponentQuery resolve the component using xtype with a property.

For those who are not familiar with ComponentQueries yet, they are used to retrieve pointers to one or many components in the application using a syntax similar to that of CSS selectors.

Let‘s compare the following cases. With Id Config, we define the following Sencha controller:
Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            myButton: '#buttonId'
        },
        control: {
            myButton: {
                tap: 'onButtonTap',
                change: 'onButtonChange'
            }
        }
    },
});

As seen in the above example, we refer to the button with "#buttonId". The code would work for now. But what happens if we destroy the view with the button on it and then re-create the view?

The result is, the "tap" handler fires, but not the "change" listener. The controller reference do not point to the button anymore. The reason being is, the button has been referenced via the config id. As you can clearly see Id Config is lack of flexibility.

Here is the same case, but using the ComponentQuery:

Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            myButton: 'button[id=buttonId]'
        },
        control: {
            myButton: {
                tap: 'onButtonTap',
                change: 'onButtonChange'
            }
        }
    },
});
There is no difference between quoted and unquoted references. myButton and also "myButton" are valid references. 

More about ComponentQueries

ComponentQueries can be built with: 
  • The XTYPE-property
  • Attributes in brackets, i.E. 'button[action=submitForm]' 
  • Functions which return true/false values in curly braces '{isValid()}'
  • The itemId property '#itemId'
  • The 'not' operator. Used to retrieve all components that are not the pointed component. I.E. 'not textfields'
  • Like in css '>' can used for parent and child relationships ('panel > button'). 
  • Multiple queries can be separated by using ','. I.E. 'button, textfield'

Testing ComponentQueries

ComponentQueries are mostly defined in controllers (ref section), but it is also possible to use the singleton object Ext.ComponentQuery (http://docs.sencha.com/touch/2.2.1/#!/api/Ext.ComponentQuery). To run and test a ComponentQuery,  there is a handy option in the JavaScript console. I.E Ext.ComponentQuery.query('panel > button')

Using itemId instead of id

I recommend you not to use the "id" attribute for the identification of components. The problem is that the "id" must be unique throughout the entire application. Replace "id" with "itemId" will avoid this conflict. The itemId property requires uniqueness of value only among child components of a container.

Caution: If the itemId used in a ComponentQuery, the itemId must either pre-qualified via a parent item and/or you could put its xtype#myItemId.

Short example. We  define a button with the itemId="navButton" and the parent container of our button has the xtype="navigation". So our config could look like this:
Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            myButton: 'button#navButton'
        },
        control: {
            myButton: {
                tap: ...
            }
        }
    },
});

While itemIds are not unique throughout the entire application, it is a good advice to put the xtype into the query too. So instead of 'button#navButton' we use the following query: 'xtype button#navButton' to gain more precision:
Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            myButton: 'navigation button#navButton'
        },
        control: {
            myButton: {
                tap: ...
            }
        }
    },
});

Using GLOBAL Variables

It is generally advised not to use global variables. First problem, there is no namespace for these variables. Second problem, the debugging of these variables often proves to be very difficult.
messageText = 'My message text.';
timeout = 5000;
In the above example, the variable "messageText" and "timeout" was defined as global. A better solution is to define these variables in a separate class.
Ext.define('MyApp.util.Config', {
    singleton : true,
    alternateClassName : ['Config'],

    config : {
        app : {
            messageText = 'My message text.'
            //...
        },
        services : {
            timeout = 5000;
            //....
        }
    },
    constructor: function () {
        return this.config;
    }
});
The new class "Config" is included in the "app.js" file via require:
Ext.application({
    name : 'MyApp',

    requires: [
        'MyApp.util.Config'
        //...
    ],
    //...
});
Access to the variables is done with:
Config.app.messageText
Config.services.timeout
Alternatively setter and getter functions can be automatically generated by Sencha. The Config class looks like this:
Ext.define('MyApp.util.Config', {
    singleton : true,
    alternateClassName : ['Config'],
    config: {
        messageText = 'My message text.',
        timeout = 5000
    },

    constructor: function(config) {
        this.initConfig(config);
    }
});
Access to the variables is done with:
Config.getTimeout();
Config.getMessageText();


One element of themes in Sencha Touch 2.2 is the new use of icons as fonts. This is an awesome feature, because this offers the following new features:
  • size can be easily changed. 
  • color can be easily changed. 
  • shadow can be easily added to their shape. 
  • transparency nearly everywhere, unlike transparent png's in ie6. 
  • smaller in file size 
  • infinitely scaleable (because vector) 
The goal of using icon fonts is to replace the need to render icons on a web page using images.

Requirements

To use this new feature it is mandatory to setup your project with SASS. If your project not yet including SASS, take a look at this article "Theming Sencha Touch with SASS".

Out-of-the-box icons

The lightweight Sencha Touch 2.2 default theme already includes some basic icons. It is shipped with 27 icons in total.

The default icon set includes:
action, add, arrow_down, arrow_left, arrow_right, arrow_up, bookmarks, compose, delete, download, favorites, home, info, locate, maps, more, organize, refresh, reply, search, settings, star, team, time, trash and user.
Use the default icons above by typing:
{  xtype: 'button',  iconCls: 'user',  title: 'User'  }

Extend the Out-of-the-box icons

Sencha Touch 2.2 is now using the http://www.pictos.cc/font/ library. When you checkout this website, you'll find out that they have more to offer in terms of icons.

At the first glance, the icon collections on the website might be a little bit confusing. You might ask yourself how does the mapping works between the "iconCls" and the pictos.

Well, when you take a look inside the framework of Sencha Touch 2.2, you will find the following mapping in "Sencha Touch SDK/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 == "help") { @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"; }
    @else if ($name == "tags") { @return "z"; }

    // 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 == "radio") { @return "L"; }
    @else if ($name == "mail") { @return "M"; }
    @else if ($name == "news") { @return "N"; }
    @else if ($name == "case") { @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 == "chart2") { @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 == "calendar") { @return "\005C"; }
    @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 == "loop2") { @return "r"; }
    @else if ($name == "star") { @return "S"; }
    @else if ($name == "maps") { @return "@"; }
    @else if ($name == "reply") { @return "R"; }

    @else {
        @return null;
    }
}
Ok, lets try this:
{  xtype: 'button',  iconCls: 'speedometer',  title: 'Speedometer'  }
But as you see in the result, the icon is still missing! This is because we did not included the icon in our "*.scss" file yet (because it is not in the range of the default icon set). To do this, add the following line to your "*.scss" file:
@include icon('speedometer');

Change the color or size of an icon

Changing the color of an icon is easier now than ever. All you need to do is adding the following line to your "*.scss" file:
.x-button-icon {
    color: #fff;
}
Now the color of your icon should change to white.

In case you want to change the size of the icon:
.x-button-icon {
    font-size: 50px;
}
Pretty simple, isn't it?

Add a icon to Ext.List

If you want to add a icon to Ext.List you need to define your own css class, because Ext.List do not have the iconCls config attribute.

In this example we add an user icon to Ext.List.

Open your "*.scss" file and add the following lines:
.userCls:before {
    font-family: "Pictos";
    content: "U";
    font-size: 1.5em;
}
Using the ":before" selector in CSS you can insert the user icon before the anchor text. Similarly, you can also use the :after selector to insert the icon after the text.

Ext.List item:
xtype : 'list',
store : 'your store',
itemTpl : new Ext.XTemplate(
 '<span class="userCls"></span>',
 '<span>{name}</span>',
 ...

Add more custom Icon-Fonts

If you need more icons, you can also attach icons from http://icomoon.io/app/ or checkout more icon collections.

The way to add custom Icon-Fonts is explained in this video: http://vimeo.com/66191690

Conclusion

Well, I hope this article helps. If you have any question, please contact me. In the mean time - let's keep developing apps guys.