Monday, January 30, 2012

Creating a Flex AIR Screenshot app: Part 9

In this tutorial, we will make the screen settings save and load, as well as add an "Export" button and add some protection against errors.

Firstly we need to set the ids of all the screen setting items. Set the first checbox's id to set1checkbox, and all the rest ScreenSetting items' ids to set2, set3, set4, set5, set6 and set7. Also add a button with label "Export" and set its click event listener to startExportScreenshot().

<s:NavigatorContent id="screenshotsettings">
<s:VGroup width="100%" horizontalAlign="center" paddingTop="20">
<s:Label styleName="descriptionText">Select screenshot screen sizes:</s:Label>
<s:SkinnableContainer backgroundColor="#999999" width="310" height="18" >
<s:CheckBox toolTip="Use this screen size" x="4" id="set1checkbox" />
<s:Label id="contSize" styleName="settingText" x="22" y="3" />
</s:SkinnableContainer>
<custom:ScreenSetting id="set2" />
<custom:ScreenSetting id="set3" />
<custom:ScreenSetting id="set4" />
<custom:ScreenSetting id="set5" />
<custom:ScreenSetting id="set6" />
<custom:ScreenSetting id="set7" />
<s:HGroup>
<s:Button label="Back" click="screenshotBack();" />
<s:Button label="Export" click="startExportScreenshot();" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>

Now, go and declare these 3 variables:

private var preferences:SharedObject = SharedObject.getLocal("kirshotPreferences");
[Bindable]
private var pref_screensizes:Array;
private var screenSettings:Array;

The first one is a shared object which will be used for storing preferences data. The second variable is an array that holds screensizes taken from the shared object. The last variable will be used to reference set2-set7 items.

In the init() function, add code that checks if the application is launched for the first time, and if it is - set default values. Then apply the shared data to pref_screensizes:

private function init():void {
//preferences.data.firsttime = null;

// Set preferences if loaded for the first time
if (preferences.data.firsttime == null) {
preferences.data.firsttime = true;
preferences.data.screensizes = [ 
{ checked:true },
{ checked:true, w:1280, h:1024 },
{ checked:true, w:1280, h:800 },
{ checked:true, w:1024, h:768 },
{ checked:false, w:"", h:"" },
{ checked:false, w:"", h:"" },
{ checked:false, w:"", h:"" }];
preferences.flush();
}

// Set preferences loaded from local storage
pref_screensizes = preferences.data.screensizes;

addEventListener(FlexNativeWindowBoundsEvent.WINDOW_RESIZE, onResize);
}

Now, we will create functions that will load and save screen size preferences - loadScreenshotSettings() and saveScreenshotSettings(). Firstly, though, let's update the functions that will call these.

Call saveScreenshotSettings() from screenshotBack():

private function screenshotBack():void {
saveScreenshotSettings()
stack.selectedChild = loadpage;
removeElement(tempHTML);
}

Now update changeState() function, in the last conditional set screenSettings value to the array of references to our set objects, and then call loadScreenshotSettings():

private function changeState():void {
if (stack.selectedChild == loadpage) {
contentBox.setStyle("horizontalAlign", "center");
urlInput.text = urlString;
}
if (stack.selectedChild == crop) {
maximize();
canSelect = true;
contentBox.setStyle("horizontalAlign", "left");
cropHTML.htmlLoader.load(new URLRequest(urlString));
cropHTML.width = contentBox.width;
cropHTML.height = contentBox.height - 24;
cropStatus.text = "Loading";
cropStatus.setStyle("color", "#ffff00");
cropHTML.addEventListener(Event.COMPLETE, onCropLoad);
}
if (stack.selectedChild == screenshotsettings) {
screenSettings = [set2, set3, set4, set5, set6, set7];
contSize.text = "Full size (" + tempHTML.contentWidth + "x" + tempHTML.contentHeight + ")";
loadScreenshotSettings();
}
}

It was needed to store the references to the set objects to use them in a loop where we save and retreive variable values in loadScreenshotSettings() and saveScreenshotSettings():

private function loadScreenshotSettings():void {
set1checkbox.selected = pref_screensizes[0].checked;

for (var i:int = 0; i < screenSettings.length; i++) {
screenSettings[i].checked = pref_screensizes[i + 1].checked;
screenSettings[i].w = pref_screensizes[i + 1].w;
screenSettings[i].h = pref_screensizes[i + 1].h;
}
}

private function saveScreenshotSettings():void {
pref_screensizes[0].checked = set1checkbox.selected;

for (var i:int = 0; i < screenSettings.length; i++) {
pref_screensizes[i + 1].checked = screenSettings[i].checked;
pref_screensizes[i + 1].w = screenSettings[i].w;
pref_screensizes[i + 1].h = screenSettings[i].h;
}

preferences.flush();
}

It is also needed in the startExportScreenshot() function, which checks whether all the entered info is valid before proceeding to export.

We check if all the checked items have their size values not blank or not 0:

private function startExportScreenshot():void {
var canExport:Boolean = true;

for (var i:int = 0; i < screenSettings.length; i++) {
if (screenSettings[i].checked && ((screenSettings[i].w == "" || screenSettings[i].w == 0) || (screenSettings[i].h == "" || screenSettings[i].h == 0))) {
canExport = false;
}
}

if (canExport) {
Alert.show("Successful export");
}else {
Alert.show("One or more selected screen sizes are not entered or are invalid!", "Oops...");
}
}

And that's it for today. 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:custom="*"
                       xmlns:mx="library://ns.adobe.com/flex/mx" showStatusBar="false"
   width="550" height="500" creationComplete="init();">
   
   
<fx:Declarations>
<mx:ArrayCollection id="headerTitles">
<fx:Object step="Step one:" description="load a web page." />
<fx:Object step="Loading..." description="please wait." />
<fx:Object step="Step two:" description="set your export preferences." />
<fx:Object step="Step two:" description="select the area you wish to crop." />
<fx:Object step="Step three:" description="set your export preferences for the cropped image." />
<fx:Object step="Final step:" description="please wait while your images are being expored." />
</mx:ArrayCollection>
</fx:Declarations>

<fx:Style>
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";

.descriptionText{
fontSize: 24;
color: #fff;
}

.settingText{
fontSize: 16;
color: #fff;
}

#headStep{
fontSize: 30;
fontWeight: bold;
color: #ffffbb;
}

#headDesc{
fontSize: 30;
color: #ffffff;
}
</fx:Style>

<fx:Script>
<![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filesystem.File;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import mx.controls.HTML;
import mx.core.FlexHTMLLoader;
import mx.events.FlexNativeWindowBoundsEvent;
import mx.controls.Alert;
import mx.graphics.ImageSnapshot;
import spark.primitives.BitmapImage;

[Bindable]
private var urlString:String;
[Bindable]
private var canSelect:Boolean = true;
private var tempHTML:HTML = new HTML();

private var preferences:SharedObject = SharedObject.getLocal("kirshotPreferences");
[Bindable]
private var pref_screensizes:Array;
private var screenSettings:Array;

private function init():void {
//preferences.data.firsttime = null;

// Set preferences if loaded for the first time
if (preferences.data.firsttime == null) {
preferences.data.firsttime = true;
preferences.data.screensizes = [ 
{ checked:true },
{ checked:true, w:1280, h:1024 },
{ checked:true, w:1280, h:800 },
{ checked:true, w:1024, h:768 },
{ checked:false, w:"", h:"" },
{ checked:false, w:"", h:"" },
{ checked:false, w:"", h:"" }];
preferences.flush();
}

// Set preferences loaded from local storage
pref_screensizes = preferences.data.screensizes;

addEventListener(FlexNativeWindowBoundsEvent.WINDOW_RESIZE, onResize);
}

private function doBrowse():void{
var file:File = new File();
file.addEventListener(Event.SELECT, browseSelect);
file.browseForOpen("Load a webpage");

function browseSelect(evt:Event):void {
urlInput.text = file.nativePath;
}
}

private function goCrop():void {
stack.selectedChild = crop;
urlString = urlInput.text;
}

private function goScreenshot():void {
stack.selectedChild = screenshotloading;
urlString = urlInput.text;

addElement(tempHTML);
tempHTML.visible = false;
tempHTML.addEventListener(Event.COMPLETE, onTempLoad);
tempHTML.htmlLoader.load(new URLRequest(urlString));
}

private function onTempLoad(evt:Event):void {
stack.selectedChild = screenshotsettings;
}

private function cancelLoading():void {
tempHTML.removeEventListener(Event.COMPLETE, onTempLoad);
tempHTML.cancelLoad();
screenshotBack();
}

private function screenshotBack():void {
saveScreenshotSettings()
stack.selectedChild = loadpage;
removeElement(tempHTML);
}

private function changeState():void {
if (stack.selectedChild == loadpage) {
contentBox.setStyle("horizontalAlign", "center");
urlInput.text = urlString;
}
if (stack.selectedChild == crop) {
maximize();
canSelect = true;
contentBox.setStyle("horizontalAlign", "left");
cropHTML.htmlLoader.load(new URLRequest(urlString));
cropHTML.width = contentBox.width;
cropHTML.height = contentBox.height - 24;
cropStatus.text = "Loading";
cropStatus.setStyle("color", "#ffff00");
cropHTML.addEventListener(Event.COMPLETE, onCropLoad);
}
if (stack.selectedChild == screenshotsettings) {
screenSettings = [set2, set3, set4, set5, set6, set7];
contSize.text = "Full size (" + tempHTML.contentWidth + "x" + tempHTML.contentHeight + ")";
loadScreenshotSettings();
}
}

private function loadScreenshotSettings():void {
set1checkbox.selected = pref_screensizes[0].checked;

for (var i:int = 0; i < screenSettings.length; i++) {
screenSettings[i].checked = pref_screensizes[i + 1].checked;
screenSettings[i].w = pref_screensizes[i + 1].w;
screenSettings[i].h = pref_screensizes[i + 1].h;
}
}

private function saveScreenshotSettings():void {
pref_screensizes[0].checked = set1checkbox.selected;

for (var i:int = 0; i < screenSettings.length; i++) {
pref_screensizes[i + 1].checked = screenSettings[i].checked;
pref_screensizes[i + 1].w = screenSettings[i].w;
pref_screensizes[i + 1].h = screenSettings[i].h;
}

preferences.flush();
}

private function startExportScreenshot():void {
var canExport:Boolean = true;

for (var i:int = 0; i < screenSettings.length; i++) {
if (screenSettings[i].checked && ((screenSettings[i].w == "" || screenSettings[i].w == 0) || (screenSettings[i].h == "" || screenSettings[i].h == 0))) {
canExport = false;
}
}

if (canExport) {
Alert.show("Successful export");
}else {
Alert.show("One or more selected screen sizes are not entered or are invalid!", "Oops...");
}
}

private function onCropLoad(evt:Event):void {
cropStatus.text = "Loaded";
cropStatus.setStyle("color", "#ffffff");
}

private function onResize(evt:Event):void {
if (stack.selectedChild == crop) {
cropHTML.width = contentBox.width;
cropHTML.height = contentBox.height - 24;
}
}
]]>
</fx:Script>
   
<s:VGroup width="100%" height="100%" gap="0">
<mx:HBox backgroundColor="#333333" height="46" width="100%" paddingTop="10" paddingLeft="10">
<s:Label id="headStep" text="{headerTitles.getItemAt(stack.selectedIndex).step}" />
<s:Label id="headDesc" text="{headerTitles.getItemAt(stack.selectedIndex).description}" />
</mx:HBox>
<mx:Box backgroundColor="#666666" width="100%" height="100%" id="contentBox" horizontalAlign="center">
<mx:ViewStack id="stack" change="changeState();">
<s:NavigatorContent id="loadpage">
<s:VGroup width="100%" horizontalAlign="center" paddingTop="20">
<s:Label styleName="descriptionText">Enter the link to the page:</s:Label>
<s:HGroup>
<s:TextInput width="250" id="urlInput" text="http://" /><s:Button label="Browse local..." click="doBrowse();" />
</s:HGroup>
<s:HGroup>
<custom:ImageButton img="@Embed('../lib/b_screenshot.png')" over="@Embed('../lib/b_screenshot_over.png')" toolTip="Take screenshots" click="goScreenshot();" buttonMode="true" enabled="{urlInput.text!=''}" />
<custom:ImageButton img="@Embed('../lib/b_cut.png')" over="@Embed('../lib/b_cut_over.png')" toolTip="Crop area" click="goCrop();" buttonMode="true" enabled="{urlInput.text!=''}" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>

<s:NavigatorContent id="screenshotloading">
<s:VGroup width="100%" horizontalAlign="center" paddingTop="20">
<s:Label styleName="descriptionText">The page is being loaded...</s:Label>
<s:Button label="Cancel" click="cancelLoading();" />
</s:VGroup>
</s:NavigatorContent>

<s:NavigatorContent id="screenshotsettings">
<s:VGroup width="100%" horizontalAlign="center" paddingTop="20">
<s:Label styleName="descriptionText">Select screenshot screen sizes:</s:Label>
<s:SkinnableContainer backgroundColor="#999999" width="310" height="18" >
<s:CheckBox toolTip="Use this screen size" x="4" id="set1checkbox" />
<s:Label id="contSize" styleName="settingText" x="22" y="3" />
</s:SkinnableContainer>
<custom:ScreenSetting id="set2" />
<custom:ScreenSetting id="set3" />
<custom:ScreenSetting id="set4" />
<custom:ScreenSetting id="set5" />
<custom:ScreenSetting id="set6" />
<custom:ScreenSetting id="set7" />
<s:HGroup>
<s:Button label="Back" click="screenshotBack();" />
<s:Button label="Export" click="startExportScreenshot();" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>

<s:NavigatorContent id="crop">
<s:VGroup width="100%" height="100%" gap="0">
<mx:HBox backgroundColor="#999999" width="100%" height="24" verticalScrollPolicy="off" verticalAlign="middle" paddingLeft="2">
<s:Button label="Back" click="{cropHTML.htmlLoader.loadString(''); stack.selectedChild = loadpage;}" />
<s:Button label="Export selection" enabled="false" />
<s:Label id="cropStatus" />
<s:Label text="{urlString}" />
</mx:HBox>
<mx:HTML id="cropHTML" horizontalScrollPolicy="on" verticalScrollPolicy="on" />
</s:VGroup>
</s:NavigatorContent>

<s:NavigatorContent id="cropsettings">

</s:NavigatorContent>

<s:NavigatorContent id="export">

</s:NavigatorContent>
</mx:ViewStack>
</mx:Box>
</s:VGroup>

</s:WindowedApplication>

Thanks for reading!

No comments:

Post a Comment