Saturday, December 24, 2011

Creating a Flex AIR text editor: Part 75

In this tutorial we will make it so that the page icons in tabs change their color depending on the file's 'saved' flag, as well as make the status bar display the encoding.

First let's deal with displaying the encoding. I thought it would be handy to always see what encoding is set without having to go to the Encoding sub menu in the native menu. The status bar is the perfect place to display current encoding.

All we have to do to accomplish that is update the updateStatus() function and add a little of code to display the currentEncoding variable in the status text field:

private function updateStatus():void {
var str:String = new String();
str = (pref_wrap)?("Word wrapping on"):(caretPosition());
status = "Encoding: " + currentEncoding + "\t" + str + "\t" + statusMessage;
}

Now let's move on to the tabs. The * symbol the application has right now shows that the file's 'saved' flag is off, meaning that the user has to save the file for any changes he made to apply. Right now this is the only indicator for that, but we can add another one - change the little blue page icon next to the tab label to a little red page icon. This is more eye catching and the user won't overlook it when looking at a long list of tabs.

First we need to create this icon. The blue page right now is called page.png and is a part of Famfamfam's Silk icon set, just like all the rest icons in this application. This is what it looks like:


To make the new red page icon, open any graphical editor like Photoshop or Gimp and play around with the hue or RGB channels or whatever you want, and you'll get something like this:


Save the new icon as page_red.png in the same 'lib' directory.

Now go to the CustomTab.mxml file. Find the line which creates the BitmapImage object containing the blue page icon. Remove its source attribute and just set its id, set it to iconImage:

<s:BitmapImage id="iconImage" top="3" left="4" />

We will make the program decide what icon to put according to the data.saved value in updateDisplayList() function. Because we are using AS3 to set the source property and we still want to have the picture embedded into final project and not loaded real-time, we need to declare 2 variables with the icons first.

It is done like this:

[Embed("../lib/page.png")] 
private var bluePage:Class; 
[Embed("../lib/page_red.png")] 
private var redPage:Class; 

Now we can apply the values in updateDisplayList() function like this:

override protected function updateDisplayList(w:Number, h:Number):void
{
super.updateDisplayList(w, 22);
if (labelDisplay)
{
labelDisplay.text = (data.saved)?(data.title):(data.title + "*");
if (data.saved) {
iconImage.source = bluePage;
}else {
iconImage.source = redPage;
}
}
}        

Here's the full code of CustomTab.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/mx"
        width="100%"
        height="100"
        autoDrawBackground="false"
>   

    <fx:Metadata>
        [Event(name="tabClose")]
    </fx:Metadata>

    <fx:Script>
    <![CDATA[
    
import flash.events.Event;
        import mx.controls.Alert;
        import mx.events.CloseEvent;

        private var tab:*;

[Embed("../lib/page.png")] 
private var bluePage:Class; 
[Embed("../lib/page_red.png")] 
private var redPage:Class; 

        override public function set data(value:Object):void
        {
            super.data = value;
            tab = value;
        }

override protected function updateDisplayList(w:Number, h:Number):void
{
super.updateDisplayList(w, 22);
if (labelDisplay)
{
labelDisplay.text = (data.saved)?(data.title):(data.title + "*");
if (data.saved) {
iconImage.source = bluePage;
}else {
iconImage.source = redPage;
}
}
}              

        protected function labelClose_clickHandler(event:MouseEvent):void
        {
            event.stopImmediatePropagation();
dispatchEvent(new Event("tabClose", true));
        }

    ]]>
    </fx:Script>
   
    <s:states>
        <s:State name="normal" basedOn="{data.state}"/>
        <s:State name="selected" basedOn="{data.state}"/>
        <s:State name="hovered" basedOn="{data.state}"/>
    </s:states>

    <!-- background -->
    <s:Rect left="1" right="1" top="1" bottom="0">
        <s:fill>
            <s:LinearGradient rotation="90">
                <s:GradientEntry color="0xffffff" />
                <s:GradientEntry
                    color="0xd8d8d8"
                    alpha="0.85"
                    color.selected="0xffffff"
                    alpha.selected="1.0"
                    color.hovered="0x929496"
                    alpha.hovered="0.85"
                />
            </s:LinearGradient>
        </s:fill>
    </s:Rect>

    <!-- border rectangle -->
    <s:Line left="0" right="0" top="1">
        <s:stroke>
            <s:SolidColorStroke
                weight="1"
                alpha="1.0"
                color="0x999999"
            />
        </s:stroke>
    </s:Line>
    <s:Line left="0" bottom="0" top="1">
        <s:stroke>
            <s:SolidColorStroke
                weight="1"
                alpha="1.0"
                color="0x999999"
            />
        </s:stroke>
    </s:Line>
    <s:Line right="0" bottom="0" top="1">
        <s:stroke>
            <s:SolidColorStroke
                weight="1"
                alpha="1.0"
                color="0x999999"
            />
        </s:stroke>
    </s:Line>
    <s:Line left="0" right="0" bottom="0">
        <s:stroke>
            <s:SolidColorStroke
                weight="1"
                alpha="1.0"
                color="0x999999"
                alpha.selected="0.0"
                color.selected="0xffffff"
            />
        </s:stroke>
    </s:Line>

<s:BitmapImage id="iconImage" top="3" left="4" />
   
    <s:Label
        id="labelDisplay"
        textAlign="left"
        verticalAlign="middle"
        maxDisplayedLines="1"
        horizontalCenter="0"
        verticalCenter="1"
        left="24"
        right="20"
        top="2"
        bottom="2"
    />

<mx:Image source="@Embed('../lib/cross.png')" top="3" right="4" id="labelClose" click="labelClose_clickHandler(event)" useHandCursor="true" buttonMode="true"/>

</s:ItemRenderer>

Now go to the CustomListItem.mxml file, find the BitmapImage object and set its id to "iconImage" as well:

<s:BitmapImage id="iconImage" top="2" left="2" />

Declare the two icon variables just the same way:

[Embed("../lib/page.png")] 
private var bluePage:Class; 
[Embed("../lib/page_red.png")] 
private var redPage:Class; 

And update updateDisplayList() function similarly:

override protected function updateDisplayList(w:Number, h:Number):void {
super.updateDisplayList(w, 20);
if (labelDisplay && data) {
labelDisplay.text = (data.saved)?(data.title):(data.title + "*");
if (data.saved) {
iconImage.source = bluePage;
}else {
iconImage.source = redPage;
}
}
}

And full CustomListItem.mxml code:

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2010/01/27/creating-a-fancy-spark-list-control-item-renderer-in-flex-4/ -->
<s:ItemRenderer name="CustomListItemRenderer"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
        autoDrawBackground="true" >

<fx:Script>
<![CDATA[
import flash.events.MouseEvent;
import flash.events.Event;
import mx.controls.Alert;

[Embed("../lib/page.png")] 
private var bluePage:Class; 
[Embed("../lib/page_red.png")] 
private var redPage:Class; 

override protected function updateDisplayList(w:Number, h:Number):void {
super.updateDisplayList(w, 20);
if (labelDisplay && data) {
labelDisplay.text = (data.saved)?(data.title):(data.title + "*");
if (data.saved) {
iconImage.source = bluePage;
}else {
iconImage.source = redPage;
}
}
}

private function labelClose(evt:MouseEvent):void {
evt.stopImmediatePropagation();
dispatchEvent(new Event("tabClose", true));
}
]]>
</fx:Script>

    <fx:Metadata>
        [Event(name="tabClose")]
    </fx:Metadata>

<s:BitmapImage id="iconImage" top="2" left="2" />
<s:Label id="labelDisplay" left="25" right="20" top="4" bottom="4" width="122" height="12" />
<mx:Image source="@Embed('../lib/cross.png')" top="2" right="4" id="closeButton" click="labelClose(event)" useHandCursor="true" buttonMode="true"/>
</s:ItemRenderer>

Now the * symbols and the differently colored icons are indicators of the tabs' "saved" flags.

Thanks for reading!

No comments:

Post a Comment