Showing posts with label 15. Show all posts
Showing posts with label 15. Show all posts
Thursday, February 5, 2015
Creating Advanced Alert Window class Part 15
In this tutorial we will start working on the ability to add buttons to the alert window.
Start by going to the AdvAlertManager.as and adding another optional parameter to the alert() window - a buttons Array. Set its default value to null, in the function check if the value is null, and if so - set it to an empty array. Pass the buttons array as the final parameter to AdvAlertWindow constructor:
Full class code:
The buttons array will be filled with instances of AdvAlertButton class. Create AdvAlertButton.as class right now. Temporarily we will fill it with a little piece of code that draws a 80x30 rectangle. Even though we do that, we still wont be able to see it in action in this tutorial - so leave whatever placeholder you want.
The buttons will be added to the array, then the code will read them and add them to the alert window, positioning them properly. For positioning, we will reserve a special spot for all the buttons. Well refer to this spot as button bar from now on. The button bar by default will be a 30 pixel height box, located in the alert window on the very bottom of it, under the main text. It also has all default padding values set to 5.
Go to AdvAlertSkin.as and add 2 new variables - buttonbarHeight and buttonbarPadding:
Set their default values in the constructor:
Full class code:
Now go to AdvAlertWindow.as class. Declare 3 new variables - buttons array, buttonbarPadding and buttonbarHeight:
In the constructor we receive the buttons array and apply its value to the buttons variable:
Find the setSkin() function and add 2 lines that apply the buttonbarHeight and buttonbarPadding values from the skin object to the variables in this class:
Now find the piece of code that positions the textField object in updateDraw() function.
Modify the line that sets the height of it to substract the button bar height, top and bottom borders. Add temporary lines that set the background color of the textField:
You should see the text field doesnt reach all the way down to fill all the available space in the alert window - it reserves space for the button bar.
Full updateDraw() code (I simplified some lines in bg fill and header fill parts of the code):
And full AdvAlertWindow.as code:
Thanks for reading!
Read more »
Start by going to the AdvAlertManager.as and adding another optional parameter to the alert() window - a buttons Array. Set its default value to null, in the function check if the value is null, and if so - set it to an empty array. Pass the buttons array as the final parameter to AdvAlertWindow constructor:
public function alert(text:String, title:String = "", buttons:Array = null, width:int = 300, height:int = 200, position:Point = null):void {
if (position == null) position = new Point((pWidth / 2) - (width / 2), (pHeight / 2) - (height / 2));
if (buttons == null) buttons = [];
var w:AdvAlertWindow = new AdvAlertWindow(text, title, width, height, position, skin, buttons);
var b:AdvAlertBlur = new AdvAlertBlur(pWidth, pHeight, skin);
windows.push( { window:w, blur:b } );
defaultContainer.addChild(b);
defaultContainer.addChild(w);
trace("Message: " + text);
}
Full class code:
package com.kircode.AdvAlert
{
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.geom.Point;
/**
* Advanced Alert window manager.
* @author Kirill Poletaev
*/
public class AdvAlertManager
{
private var windows:Array;
private var defaultContainer:DisplayObjectContainer;
private var pWidth:int;
private var pHeight:int;
private var skin:AdvAlertSkin;
/**
* AdvAlertManager constructor.
* @paramdefaultWindowContainer Parent container of alert windows.
* @paramparentWidth Containers width.
* @paramparentHeight Containers height.
*/
public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, parentWidth:int, parentHeight:int, windowSkin:AdvAlertSkin = null)
{
trace(": AdvAlertManager instance created.");
skin = windowSkin;
if (skin == null) skin = new AdvAlertSkin();
defaultContainer = defaultWindowContainer;
pWidth = parentWidth;
pHeight = parentHeight;
windows = [];
}
/**
* Set skin of all future created alert windows.
* @paramwindowSkin Reference to a skin object.
*/
public function setSkin(windowSkin:AdvAlertSkin):void {
skin = windowSkin;
}
/**
* Create an alert window.
* @paramtext Text value of the alert window.
* @paramtitle Title value of the alert window.
* @parambuttons (Optional) Array of AdvAlertButton objects that represent buttons in the alert window.
* @paramwidth (Optional) Width of the alert window. 300 by default.
* @paramheight (Optional) Height of the alert window. 200 by default.
* @paramposition (Optional) Coordinates of top-left corner of the alert window. If not specified - the window is centered.
*/
public function alert(text:String, title:String = "", buttons:Array = null, width:int = 300, height:int = 200, position:Point = null):void {
if (position == null) position = new Point((pWidth / 2) - (width / 2), (pHeight / 2) - (height / 2));
if (buttons == null) buttons = [];
var w:AdvAlertWindow = new AdvAlertWindow(text, title, width, height, position, skin, buttons);
var b:AdvAlertBlur = new AdvAlertBlur(pWidth, pHeight, skin);
windows.push( { window:w, blur:b } );
defaultContainer.addChild(b);
defaultContainer.addChild(w);
trace("Message: " + text);
}
}
}
The buttons array will be filled with instances of AdvAlertButton class. Create AdvAlertButton.as class right now. Temporarily we will fill it with a little piece of code that draws a 80x30 rectangle. Even though we do that, we still wont be able to see it in action in this tutorial - so leave whatever placeholder you want.
package com.kircode.AdvAlert
{
import flash.display.MovieClip;
/**
* Advanced Alert window object.
* @author Kirill Poletaev
*/
public class AdvAlertButton extends MovieClip
{
private var _w:Number = 80;
private var _h:Number = 30;
public function AdvAlertButton()
{
updateDraw();
}
public function updateDraw():void {
this.graphics.clear();
// draw
this.graphics.beginFill(0x3333aa, 1);
this.graphics.drawRect(0, 0, _w, _h);
}
}
}
The buttons will be added to the array, then the code will read them and add them to the alert window, positioning them properly. For positioning, we will reserve a special spot for all the buttons. Well refer to this spot as button bar from now on. The button bar by default will be a 30 pixel height box, located in the alert window on the very bottom of it, under the main text. It also has all default padding values set to 5.
Go to AdvAlertSkin.as and add 2 new variables - buttonbarHeight and buttonbarPadding:
public var buttonbarHeight:Number;
public var buttonbarPadding:TextPadding;
Set their default values in the constructor:
buttonbarHeight = 30;
buttonbarPadding = new TextPadding(5, 5, 5, 5);
Full class code:
package com.kircode.AdvAlert
{
import flash.filters.DropShadowFilter;
import flash.text.TextFormat;
/**
* Object containing skinning data for AdvAlertWindow.
* @author Kirill Poletaev
*/
public class AdvAlertSkin
{
public var textPadding:TextPadding;
public var headerPadding:TextPadding;
public var titlePadding:TextPadding;
public var headerHeight:int;
public var titleFormat:TextFormat;
public var textFormat:TextFormat;
public var selectable:Boolean;
public var headerRect:*;
public var bgRect:*;
public var bgStroke:*;
public var headerStroke:*;
public var filters:Array;
public var blurColor:uint;
public var blurAlpha:Number;
public var buttonbarHeight:Number;
public var buttonbarPadding:TextPadding;
public function AdvAlertSkin()
{
// default values
textPadding = new TextPadding(5, 5, 5, 5);
headerPadding = new TextPadding(5, 5, 5, 5);
titlePadding = new TextPadding(2, 2, 2, 2);
headerHeight = 30;
titleFormat = new TextFormat();
titleFormat.size = 20;
titleFormat.font = "Arial";
titleFormat.bold = true;
titleFormat.color = 0xffffff;
textFormat = new TextFormat();
textFormat.size = 16;
textFormat.font = "Arial";
textFormat.color = 0xffffff;
selectable = false;
bgRect = new SolidColorRect(0x7777cc, [10, 10, 10, 10], 1);
headerRect = new SolidColorRect(0x9999ff, [10, 10, 0, 0], 1);
bgStroke = new SolidColorStroke(2, 0x4444aa, 1);
headerStroke = new SolidColorStroke(0, 0x000000, 0);
filters = [new DropShadowFilter(0, 0, 0, 1, 20, 20, 1, 3)];
blurAlpha = 0.5;
blurColor = 0x888888;
buttonbarHeight = 30;
buttonbarPadding = new TextPadding(5, 5, 5, 5);
}
}
}
Now go to AdvAlertWindow.as class. Declare 3 new variables - buttons array, buttonbarPadding and buttonbarHeight:
private var buttons:Array;
private var buttonbarPadding:TextPadding;
private var buttonbarHeight:Number;
In the constructor we receive the buttons array and apply its value to the buttons variable:
public function AdvAlertWindow(pt:String, ptt:String, pw:int, ph:int, ppos:Point, sk:AdvAlertSkin, bt:Array)
{
t = pt;
tt = ptt;
w = pw;
h = ph;
pos = ppos;
buttons = bt;
setSkin(sk);
textField = new TextField();
addChild(textField);
titleField = new TextField();
addChild(titleField);
updateDraw();
}
Find the setSkin() function and add 2 lines that apply the buttonbarHeight and buttonbarPadding values from the skin object to the variables in this class:
public function setSkin(skin:AdvAlertSkin):void {
textPadding = skin.textPadding;
headerPadding = skin.headerPadding;
titlePadding = skin.titlePadding;
headerHeight = skin.headerHeight;
titleFormat = skin.titleFormat;
textFormat = skin.textFormat;
selectable = skin.selectable;
bgRect = skin.bgRect;
headerRect = skin.headerRect;
bgStroke = skin.bgStroke;
headerStroke = skin.headerStroke;
skinFilters = skin.filters;
buttonbarHeight = skin.buttonbarHeight;
buttonbarPadding = skin.buttonbarPadding;
}
Now find the piece of code that positions the textField object in updateDraw() function.
Modify the line that sets the height of it to substract the button bar height, top and bottom borders. Add temporary lines that set the background color of the textField:
// text
textField.width = w - (textPadding.right + textPadding.left);
textField.height = h - (textPadding.top + textPadding.bottom + headerPadding.top + headerPadding.bottom + headerHeight + buttonbarHeight + buttonbarPadding.top + buttonbarPadding.bottom);
// temporary, to see the edges of the text field
textField.background = true;
textField.backgroundColor = 0xff0000;
textField.text = t;
textField.x = pos.x + textPadding.right;
textField.y = pos.y + textPadding.top + headerHeight + headerPadding.bottom + headerPadding.top;
You should see the text field doesnt reach all the way down to fill all the available space in the alert window - it reserves space for the button bar.
Full updateDraw() code (I simplified some lines in bg fill and header fill parts of the code):
public function updateDraw():void {
this.graphics.clear();
// filters
this.filters = skinFilters;
// bg stroke
if (bgStroke is SolidColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness, bgStroke._lineColor, bgStroke._lineAlpha);
}
if (bgStroke is GradientColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineGradientStyle(bgStroke._gradientType, bgStroke._colors, bgStroke._alphas, bgStroke._ratios, bgStroke._matrix, bgStroke._spreadMethod, bgStroke._interpolationMethod, bgStroke._focalPointRatio);
}
if (bgStroke is BitmapStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineBitmapStyle(bgStroke._bitmap, bgStroke._matrix, bgStroke._repeat, bgStroke._smooth);
}
// bg fill
if (bgRect is SolidColorRect) this.graphics.beginFill(bgRect._backgroundColor, bgRect._alpha);
if (bgRect is GradientColorRect) this.graphics.beginGradientFill(bgRect._gradientType, bgRect._colors, bgRect._alphas, bgRect._ratios, bgRect._matrix, bgRect._spreadMethod, bgRect._interpolationMethod, bgRect._focalPointRatio);
if (bgRect is BitmapRect) this.graphics.beginBitmapFill(bgRect._bitmap, bgRect._matrix, bgRect._repeat, bgRect._smooth);
this.graphics.drawRoundRectComplex(pos.x, pos.y, w, h,
bgRect._radius[0], bgRect._radius[1], bgRect._radius[2], bgRect._radius[3]);
this.graphics.endFill();
// header stroke
if (headerStroke is SolidColorStroke) {
this.graphics.lineStyle(headerStroke._lineThickness, headerStroke._lineColor, headerStroke._lineAlpha);
}
if (headerStroke is GradientColorStroke) {
this.graphics.lineStyle(headerStroke._lineThickness);
this.graphics.lineGradientStyle(headerStroke._gradientType, headerStroke._colors, headerStroke._alphas, headerStroke._ratios, headerStroke._matrix, headerStroke._spreadMethod, headerStroke._interpolationMethod, headerStroke._focalPointRatio);
}
if (headerStroke is BitmapStroke) {
this.graphics.lineStyle(headerStroke._lineThickness);
this.graphics.lineBitmapStyle(headerStroke._bitmap, headerStroke._matrix, headerStroke._repeat, headerStroke._smooth);
}
// header fill
if (headerRect is SolidColorRect) this.graphics.beginFill(headerRect._backgroundColor, headerRect._alpha);
if (headerRect is GradientColorRect) this.graphics.beginGradientFill(headerRect._gradientType, headerRect._colors, headerRect._alphas, headerRect._ratios, headerRect._matrix, headerRect._spreadMethod, headerRect._interpolationMethod, headerRect._focalPointRatio);
if (headerRect is BitmapRect) this.graphics.beginBitmapFill(headerRect._bitmap, headerRect._matrix, headerRect._repeat, headerRect._smooth);
this.graphics.drawRoundRectComplex(pos.x + headerPadding.left, pos.y + headerPadding.top, w - (headerPadding.left + headerPadding.right), headerHeight,
headerRect._radius[0], headerRect._radius[1], headerRect._radius[2], headerRect._radius[3]);
this.graphics.endFill();
// title
titleField.width = w - (headerPadding.left + headerPadding.right);
titleField.text = tt;
titleField.height = headerHeight;
titleField.x = pos.x + headerPadding.left + titlePadding.left;
titleField.y = pos.y + headerPadding.top + titlePadding.top;
// text
textField.width = w - (textPadding.right + textPadding.left);
textField.height = h - (textPadding.top + textPadding.bottom + headerPadding.top + headerPadding.bottom + headerHeight + buttonbarHeight + buttonbarPadding.top + buttonbarPadding.bottom);
// temporary, to see the edges of the text field
textField.background = true;
textField.backgroundColor = 0xff0000;
textField.text = t;
textField.x = pos.x + textPadding.right;
textField.y = pos.y + textPadding.top + headerHeight + headerPadding.bottom + headerPadding.top;
// formats
textField.setTextFormat(textFormat);
titleField.setTextFormat(titleFormat);
textField.selectable = selectable;
titleField.selectable = selectable;
textField.multiline = true;
textField.wordWrap = true;
}
And full AdvAlertWindow.as code:
package com.kircode.AdvAlert
{
import flash.display.MovieClip;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.display.GradientType;
/**
* Advanced Alert window object.
* @author Kirill Poletaev
*/
public class AdvAlertWindow extends MovieClip
{
private var t:String;
private var tt:String;
private var w:int;
private var h:int;
private var pos:Point;
private var buttons:Array;
private var textField:TextField;
private var titleField:TextField;
private var textPadding:TextPadding;
private var headerPadding:TextPadding;
private var titlePadding:TextPadding;
private var headerHeight:Number;
private var buttonbarPadding:TextPadding;
private var buttonbarHeight:Number;
private var titleFormat:TextFormat;
private var textFormat:TextFormat;
private var selectable:Boolean;
private var headerRect:*;
private var bgRect:*;
private var headerStroke:*;
private var bgStroke:*;
private var skinFilters:Array;
public function AdvAlertWindow(pt:String, ptt:String, pw:int, ph:int, ppos:Point, sk:AdvAlertSkin, bt:Array)
{
t = pt;
tt = ptt;
w = pw;
h = ph;
pos = ppos;
buttons = bt;
setSkin(sk);
textField = new TextField();
addChild(textField);
titleField = new TextField();
addChild(titleField);
updateDraw();
}
public function setSkin(skin:AdvAlertSkin):void {
textPadding = skin.textPadding;
headerPadding = skin.headerPadding;
titlePadding = skin.titlePadding;
headerHeight = skin.headerHeight;
titleFormat = skin.titleFormat;
textFormat = skin.textFormat;
selectable = skin.selectable;
bgRect = skin.bgRect;
headerRect = skin.headerRect;
bgStroke = skin.bgStroke;
headerStroke = skin.headerStroke;
skinFilters = skin.filters;
buttonbarHeight = skin.buttonbarHeight;
buttonbarPadding = skin.buttonbarPadding;
}
public function updateDraw():void {
this.graphics.clear();
// filters
this.filters = skinFilters;
// bg stroke
if (bgStroke is SolidColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness, bgStroke._lineColor, bgStroke._lineAlpha);
}
if (bgStroke is GradientColorStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineGradientStyle(bgStroke._gradientType, bgStroke._colors, bgStroke._alphas, bgStroke._ratios, bgStroke._matrix, bgStroke._spreadMethod, bgStroke._interpolationMethod, bgStroke._focalPointRatio);
}
if (bgStroke is BitmapStroke) {
this.graphics.lineStyle(bgStroke._lineThickness);
this.graphics.lineBitmapStyle(bgStroke._bitmap, bgStroke._matrix, bgStroke._repeat, bgStroke._smooth);
}
// bg fill
if (bgRect is SolidColorRect) this.graphics.beginFill(bgRect._backgroundColor, bgRect._alpha);
if (bgRect is GradientColorRect) this.graphics.beginGradientFill(bgRect._gradientType, bgRect._colors, bgRect._alphas, bgRect._ratios, bgRect._matrix, bgRect._spreadMethod, bgRect._interpolationMethod, bgRect._focalPointRatio);
if (bgRect is BitmapRect) this.graphics.beginBitmapFill(bgRect._bitmap, bgRect._matrix, bgRect._repeat, bgRect._smooth);
this.graphics.drawRoundRectComplex(pos.x, pos.y, w, h,
bgRect._radius[0], bgRect._radius[1], bgRect._radius[2], bgRect._radius[3]);
this.graphics.endFill();
// header stroke
if (headerStroke is SolidColorStroke) {
this.graphics.lineStyle(headerStroke._lineThickness, headerStroke._lineColor, headerStroke._lineAlpha);
}
if (headerStroke is GradientColorStroke) {
this.graphics.lineStyle(headerStroke._lineThickness);
this.graphics.lineGradientStyle(headerStroke._gradientType, headerStroke._colors, headerStroke._alphas, headerStroke._ratios, headerStroke._matrix, headerStroke._spreadMethod, headerStroke._interpolationMethod, headerStroke._focalPointRatio);
}
if (headerStroke is BitmapStroke) {
this.graphics.lineStyle(headerStroke._lineThickness);
this.graphics.lineBitmapStyle(headerStroke._bitmap, headerStroke._matrix, headerStroke._repeat, headerStroke._smooth);
}
// header fill
if (headerRect is SolidColorRect) this.graphics.beginFill(headerRect._backgroundColor, headerRect._alpha);
if (headerRect is GradientColorRect) this.graphics.beginGradientFill(headerRect._gradientType, headerRect._colors, headerRect._alphas, headerRect._ratios, headerRect._matrix, headerRect._spreadMethod, headerRect._interpolationMethod, headerRect._focalPointRatio);
if (headerRect is BitmapRect) this.graphics.beginBitmapFill(headerRect._bitmap, headerRect._matrix, headerRect._repeat, headerRect._smooth);
this.graphics.drawRoundRectComplex(pos.x + headerPadding.left, pos.y + headerPadding.top, w - (headerPadding.left + headerPadding.right), headerHeight,
headerRect._radius[0], headerRect._radius[1], headerRect._radius[2], headerRect._radius[3]);
this.graphics.endFill();
// title
titleField.width = w - (headerPadding.left + headerPadding.right);
titleField.text = tt;
titleField.height = headerHeight;
titleField.x = pos.x + headerPadding.left + titlePadding.left;
titleField.y = pos.y + headerPadding.top + titlePadding.top;
// text
textField.width = w - (textPadding.right + textPadding.left);
textField.height = h - (textPadding.top + textPadding.bottom + headerPadding.top + headerPadding.bottom + headerHeight + buttonbarHeight + buttonbarPadding.top + buttonbarPadding.bottom);
// temporary, to see the edges of the text field
textField.background = true;
textField.backgroundColor = 0xff0000;
textField.text = t;
textField.x = pos.x + textPadding.right;
textField.y = pos.y + textPadding.top + headerHeight + headerPadding.bottom + headerPadding.top;
// formats
textField.setTextFormat(textFormat);
titleField.setTextFormat(titleFormat);
textField.selectable = selectable;
titleField.selectable = selectable;
textField.multiline = true;
textField.wordWrap = true;
}
}
}
Thanks for reading!
Sunday, February 1, 2015
Creating a Flex AIR text editor Part 15
In this tutorial we will work on tab closing.
Before we start, theres one more thing we need to include in the tabChange() function that we created in the previous part. When changing between tabs, the text data and selection data is updated, however, the status bar is not. Thus, if word wrapping is off, the caret position is not updated. We can fix this by just calling the updateStatus() function here:
Now, lets get to creating the close functionality for the tabs!
We already have the onTabClose() function, which is called when the user closes a tab using the X button. Lets create a closeTab() function, which we can then use whenever we know the index of the tab we want to remove. For now the code will only remove the tab that is selected (regardless of which tab was closed). This will be fixed in the future tutorials.
We pass the index integer value and then check if the tab was saved or not (is confirmation required or not). If it is saved, remove the tab from the tabData array using the removeTab() function that we will create shortly. If it is not saved, ask for a confirmation using the Alert class. The confirmation asks the user if they want to save the file before closing. If the user chooses to save, then (for now) we just send a debug message, we will later put a function that handles saving the file before closing it. Otherwise, just close the tab.
Now, that removeTab function... it basically deletes the item from the tabData array using the removeItemAt() function and the index value that we pass to the function. However, there are 2 things we need to remember - the first thing is that we need to set the new selected tab of the tabBar and update the text data and selection. The second thing is that if it is the last tab that we are removing, we need to create a new empty tab in its place.
And thats pretty much it for todays tutorial!
Heres the full code:
We still have a long way to go with this application. See you in next part!
Thanks for reading!
Read more »
Before we start, theres one more thing we need to include in the tabChange() function that we created in the previous part. When changing between tabs, the text data and selection data is updated, however, the status bar is not. Thus, if word wrapping is off, the caret position is not updated. We can fix this by just calling the updateStatus() function here:
private function tabChange():void {
tabData[previousIndex].textData = textArea.text;
tabData[previousIndex].selectedActive = textArea.selectionActivePosition;
tabData[previousIndex].selectedAnchor = textArea.selectionAnchorPosition;
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
updateStatus();
}
Now, lets get to creating the close functionality for the tabs!
We already have the onTabClose() function, which is called when the user closes a tab using the X button. Lets create a closeTab() function, which we can then use whenever we know the index of the tab we want to remove. For now the code will only remove the tab that is selected (regardless of which tab was closed). This will be fixed in the future tutorials.
private function onTabClose(evt:Event):void {
closeTab(tabBar.selectedIndex);
}
We pass the index integer value and then check if the tab was saved or not (is confirmation required or not). If it is saved, remove the tab from the tabData array using the removeTab() function that we will create shortly. If it is not saved, ask for a confirmation using the Alert class. The confirmation asks the user if they want to save the file before closing. If the user chooses to save, then (for now) we just send a debug message, we will later put a function that handles saving the file before closing it. Otherwise, just close the tab.
private function closeTab(index:int):void {
if (tabData[index].saved) {
removeTab(index);
}
if (!tabData[index].saved) {
Alert.show("Save " + tabData[index].title + " before closing?", tabData[index].label, Alert.YES | Alert.NO, null, confirmClose);
}
function confirmClose(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
Alert.show("Perform save function here and then delete");
}else {
removeTab(index);
}
}
}
Now, that removeTab function... it basically deletes the item from the tabData array using the removeItemAt() function and the index value that we pass to the function. However, there are 2 things we need to remember - the first thing is that we need to set the new selected tab of the tabBar and update the text data and selection. The second thing is that if it is the last tab that we are removing, we need to create a new empty tab in its place.
private function removeTab(index:int):void {
// if this is the last tab, create a new empty tab
if (tabData.length == 1) {
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
}
tabData.removeItemAt(index);
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
}
And thats pretty much it for todays tutorial!
Heres 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"
xmlns:custom="*"
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.events.NativeWindowBoundsEvent;
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;
import mx.events.CloseEvent;
import mx.events.ResizeEvent;
private var preferences:SharedObject = SharedObject.getLocal("kirpadPreferences");
[Bindable]
private var pref_wrap:Boolean = true;
[Bindable]
private var pref_status:Boolean = true;
[Bindable]
private var pref_toolbar:Boolean = true;
[Bindable]
public var pref_fontsettings:Object = new Object();
private var initHeight:Number;
private var heightFixed:Boolean = false;
private var statusMessage:String;
[Bindable]
private var textHeight:Number;
[Bindable]
private var textY:Number;
[Bindable]
private var tabY:Number;
private var previousIndex:Number = 0;
public var fontWindow:FontWindow = new FontWindow();
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.data.toolbar = true;
preferences.data.fontsettings = {fontfamily:"Lucida Console", fontsize:14, fontstyle:"normal", fontweight:"normal", fontcolor:0x000000, bgcolor:0xffffff};
preferences.flush();
}
// Set preferences loaded from local storage
pref_wrap = preferences.data.wrap;
pref_status = preferences.data.status;
pref_toolbar = preferences.data.toolbar;
pref_fontsettings = preferences.data.fontsettings;
// 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();
// Close all sub-windows if main window is closed
addEventListener(Event.CLOSING, onClose);
// Add listener for the event that is dispatched when new font settings are applied
fontWindow.addEventListener(Event.CHANGE, fontChange);
// Update real fonts with the data from the settings values
updateFonts();
// Create a listener for resizing
addEventListener(NativeWindowBoundsEvent.RESIZE, onResize);
}
private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@label == "New")?(doNew()):(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);
(evt.item.@label == "Tool bar")?(pref_toolbar = !pref_toolbar):(void);
(evt.item.@label == "Font...")?(doFont()):(void);
savePreferences();
updateStatus();
}
private function savePreferences():void {
preferences.data.wrap = pref_wrap;
preferences.data.status = pref_status;
preferences.data.toolbar = pref_toolbar;
preferences.data.fontsettings = pref_fontsettings;
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;
updateTextSize();
}
}
}
private function onResize(evt:ResizeEvent):void {
updateTextSize();
}
private function updateTextSize():void {
tabY = (toolBar.visible)?(toolBar.height):(0);
textY = tabBar.height + tabY;
var statusHeight:Number = (pref_status)?(statusBar.height):(0);
textHeight = height - textY - statusHeight;
focusManager.setFocus(textArea);
}
private function updateStatus():void {
var str:String = new String();
str = (pref_wrap)?("Word wrapping on"):(caretPosition());
status = str + " " + statusMessage;
}
private function caretPosition():String {
var pos:int = textArea.selectionActivePosition;
var str:String = textArea.text.substring(0, pos);
var lines:Array = str.split("
");
var line:int = lines.length;
var col:int = lines[lines.length - 1].length + 1;
return "Ln " + line + ", Col " + col;
}
private function doFont():void{
fontWindow.open();
fontWindow.activate();
fontWindow.visible = true;
fontWindow.setValues(pref_fontsettings.fontsize, pref_fontsettings.fontfamily, pref_fontsettings.fontstyle, pref_fontsettings.fontweight, pref_fontsettings.fontcolor, pref_fontsettings.bgcolor);
}
private function onClose(evt:Event):void {
var allWindows:Array = NativeApplication.nativeApplication.openedWindows;
for (var i:int = 0; i < allWindows.length; i++)
{
allWindows[i].close();
}
}
private function fontChange(evt:Event):void{
pref_fontsettings.fontfamily = fontWindow.fontCombo.selectedItem.fontName;
pref_fontsettings.fontsize = fontWindow.sizeStepper.value;
if (fontWindow.styleCombo.selectedIndex == 0) {
pref_fontsettings.fontstyle = "normal";
pref_fontsettings.fontweight = "normal";
}
if (fontWindow.styleCombo.selectedIndex == 1) {
pref_fontsettings.fontstyle = "italic";
pref_fontsettings.fontweight = "normal";
}
if (fontWindow.styleCombo.selectedIndex == 2) {
pref_fontsettings.fontstyle = "normal";
pref_fontsettings.fontweight = "bold";
}
if (fontWindow.styleCombo.selectedIndex == 3) {
pref_fontsettings.fontstyle = "italic";
pref_fontsettings.fontweight = "bold";
}
pref_fontsettings.fontcolor = fontWindow.colorPicker.selectedColor;
pref_fontsettings.bgcolor = fontWindow.bgColorPicker.selectedColor;
savePreferences();
updateFonts();
}
private function updateFonts():void{
textArea.setStyle("fontFamily", pref_fontsettings.fontfamily);
textArea.setStyle("fontSize", pref_fontsettings.fontsize);
textArea.setStyle("fontStyle", pref_fontsettings.fontstyle);
textArea.setStyle("fontWeight", pref_fontsettings.fontweight);
textArea.setStyle("color", pref_fontsettings.fontcolor);
textArea.setStyle("contentBackgroundColor", pref_fontsettings.bgcolor);
}
private function onTabClose(evt:Event):void {
closeTab(tabBar.selectedIndex);
}
private function closeTab(index:int):void {
if (tabData[index].saved) {
removeTab(index);
}
if (!tabData[index].saved) {
Alert.show("Save " + tabData[index].title + " before closing?", tabData[index].label, Alert.YES | Alert.NO, null, confirmClose);
}
function confirmClose(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
Alert.show("Perform save function here and then delete");
}else {
removeTab(index);
}
}
}
private function removeTab(index:int):void {
// if this is the last tab, create a new empty tab
if (tabData.length == 1) {
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
}
tabData.removeItemAt(index);
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
}
private function doNew():void{
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
tabBar.selectedIndex = tabData.length - 1;
tabChange();
}
private function tabChange():void {
tabData[previousIndex].textData = textArea.text;
tabData[previousIndex].selectedActive = textArea.selectionActivePosition;
tabData[previousIndex].selectedAnchor = textArea.selectionAnchorPosition;
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
updateStatus();
}
]]>
</fx:Script>
<fx:Declarations>
<fx:XML id="windowMenu">
<root>
<menuitem label="File">
<menuitem label="New" key="n" controlKey="true" />
<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 label="Font..."/>
</menuitem>
<menuitem label="View">
<menuitem label="Tool bar" type="check" toggled="{pref_toolbar}" />
<menuitem label="Status bar" type="check" toggled="{pref_status}" />
</menuitem>
</root>
</fx:XML>
<mx:ArrayCollection id="tabData">
<fx:Object title="Untitled" textData="" saved="false" seletedActive="0" selectedAnchor="0" />
</mx:ArrayCollection>
</fx:Declarations>
<s:Group width="100%" height="100%">
<s:TextArea id="textArea" width="100%" height="{textHeight}" y="{textY}" borderVisible="false" lineBreak="{(pref_wrap)?(toFit):(explicit)}" click="cursorFix(); updateStatus();" change="updateStatus();" keyDown="updateStatus();"/>
<custom:CustomTabBar id="tabBar" dataProvider="{tabData}" width="100%" y="{tabY}" itemRenderer="CustomTab" height="22" tabClose="onTabClose(event);" change="tabChange();" />
<mx:Box id="toolBar" width="100%" backgroundColor="#dddddd" height="24" visible="{pref_toolbar}">
</mx:Box>
</s:Group>
</s:WindowedApplication>
We still have a long way to go with this application. See you in next part!
Thanks for reading!
Subscribe to:
Posts (Atom)