Monday, October 17, 2011

Creating a Flex AIR text editor: Part 7

In this tutorial we will add some functionality to our status bar and make it display status messages, as well as caret position in the text field (only when word wrapping is off).

To set the message of the status bar, you can just apply a string value to the status object:

status = "Example";

We will, however, make our status bar consist of 2 parts - the first one will be used for caret position, the second - for the status messages. We will use a function called updateStatus() to update the status bar's text value and a statusMessage variable to set the status message value.

FIrstly, create the statusMessage variable:

private var statusMessage:String;

In the init() function, add these lines in the end of the function:

// Set status message
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] Kirpad initialized";
updateStatus();

This is how we will also set and update status messages later in our application.

Let's go to the updateStatus function now. Here we will create a variable called str, which will be the first part of our status message - this will display the caret's position. Note, that the position should only be displayed when word wrapping is off, becaue when it is on - there is just no point, since there are no actual columns anymore (we are going to display the position in line and column position of the current caret).

So, if wrapping is on, we set the str variable to "Word wrapping on" and dont display the position. When it is off, however, we set the value to caretPosition() method, which returns the position of the caret as a string.

Then we add this string value to the status message value and set status to that.

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

The caretPosition function calculates and returns the position of the caret by taking the text value in the range starting from the first character to the selected character, splitting it by "\n", which is the new line character, and seeing how many new lines there were. This value will be the current line value. We can take the value of the last element of the array that we got as a result of splitting the text by "\n", and see how many characters there were using the length property (we add 1, so that it doesn't start counting from 0).

Here's the code:

private function caretPosition():String {
var pos:int = textArea.selectionActivePosition;
var str:String = textArea.text.substring(0, pos);
var lines:Array = str.split("\n");
var line:int = lines.length;
var col:int = lines[lines.length - 1].length + 1;

return "Ln " + line + ", Col " + col;
}

And here's the full code:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/mx"
   creationComplete="init();" title="Kirpad" showStatusBar="{pref_status}">
   
<s:menu>
<mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" />
</s:menu>

<fx:Script>
<![CDATA[
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.net.SharedObject;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexNativeMenuEvent;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.elements.Configuration;
import flash.system.System;
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;
import flash.ui.Mouse;

private var preferences:SharedObject = SharedObject.getLocal("kirpadPreferences");
[Bindable]
private var pref_wrap:Boolean = true;
[Bindable]
private var pref_status:Boolean = true;

private var initHeight:Number;
private var heightFixed:Boolean = false;

private var statusMessage:String;

private function init():void {

// Create a listener for every frame
addEventListener(Event.ENTER_FRAME, everyFrame);

// Set initHeight to the initial height value on start
initHeight = height;

// Set preferences if loaded for the first time
if (preferences.data.firsttime == null) {
preferences.data.firsttime = true;
preferences.data.wrap = true;
preferences.data.status = true;
preferences.flush();
}

// Set preferences loaded from local storage
pref_wrap = preferences.data.wrap;
pref_status = preferences.data.status;

// Allow insertion of tabs
var textFlow:TextFlow = textArea.textFlow;
var config:Configuration = Configuration(textFlow.configuration);
config.manageTabKey = true;

// Set status message
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] Kirpad initialized";
updateStatus();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@label == "Word wrap")?(pref_wrap = !pref_wrap):(void);
(evt.item.@label == "Cut")?(doCut()):(void);
(evt.item.@label == "Copy")?(doCopy()):(void);
(evt.item.@label == "Paste")?(doPaste()):(void);
(evt.item.@label == "Select all")?(doSelectall()):(void);
(evt.item.@label == "Status bar")?(pref_status = !pref_status):(void);
savePreferences();
updateStatus();
}

private function savePreferences():void {
preferences.data.wrap = pref_wrap;
preferences.data.status = pref_status;
preferences.flush();
}

private function doCut():void {
var selectedText:String = textArea.text.substring(textArea.selectionActivePosition, textArea.selectionAnchorPosition);
System.setClipboard(selectedText);
insertText("");
}

private function doCopy():void {
var selectedText:String = textArea.text.substring(textArea.selectionActivePosition, textArea.selectionAnchorPosition);
System.setClipboard(selectedText);
}

private function doPaste():void{
var myClip:Clipboard = Clipboard.generalClipboard;
var pastedText:String = myClip.getData(ClipboardFormats.TEXT_FORMAT) as String;
insertText(pastedText);
}

private function doSelectall():void {
textArea.selectAll();
}

private function insertText(str:String):void {
var substrPositions:int = textArea.selectionActivePosition - textArea.selectionAnchorPosition;
var oldSel1:int = (substrPositions>0)?(textArea.selectionAnchorPosition):(textArea.selectionActivePosition);
var oldSel2:int = (substrPositions<0)?(textArea.selectionAnchorPosition):(textArea.selectionActivePosition);
var preText:String = textArea.text.substring(0, oldSel1);
var postText:String = textArea.text.substring(oldSel2);
var newSelectRange:int = preText.length + str.length;
textArea.text = preText + str + postText;
textArea.selectRange(newSelectRange, newSelectRange);
}

private function cursorFix():void{
Mouse.cursor = "ibeam";
}

private function everyFrame(evt:Event):void {
if (!heightFixed && height==initHeight) {
height = initHeight - 20;
if (height != initHeight) {
heightFixed = true;
}
}
}

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

private function caretPosition():String {
var pos:int = textArea.selectionActivePosition;
var str:String = textArea.text.substring(0, pos);
var lines:Array = str.split("\n");
var line:int = lines.length;
var col:int = lines[lines.length - 1].length + 1;

return "Ln " + line + ", Col " + col;
}
]]>
</fx:Script>

<fx:Declarations>
<fx:XML id="windowMenu">
<root>
<menuitem label="File">
<menuitem label="Open" key="o" controlKey="true" />
</menuitem>
<menuitem label="Edit">
<menuitem label="Cut" key="x" controlKey="true" />
<menuitem label="Copy" key="c" controlKey="true" />
<menuitem label="Paste" key="v" controlKey="true" />
<menuitem type="separator"/>
<menuitem label="Select all" key="a" controlKey="true" />
</menuitem>
<menuitem label="Settings">
<menuitem label="Word wrap" type="check" toggled="{pref_wrap}" />
</menuitem>
<menuitem label="View">
<menuitem label="Status bar" type="check" toggled="{pref_status}" />
</menuitem>
</root>
</fx:XML>
</fx:Declarations>

<s:TextArea id="textArea" width="100%" height="100%" borderVisible="false" lineBreak="{(pref_wrap)?('toFit'):('explicit')}"  click="cursorFix(); updateStatus();" change="updateStatus();" keyDown="updateStatus();" />
</s:WindowedApplication>

Thanks for reading!

No comments:

Post a Comment