1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2009 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package cobalt.skins
13{
14
15import flash.display.Bitmap;
16import flash.display.BitmapData;
17import flash.display.IBitmapDrawable;
18import flash.events.Event;
19import flash.filters.GlowFilter;
20import flash.geom.ColorTransform;
21import flash.geom.Matrix;
22import flash.geom.Point;
23import flash.geom.Rectangle;
24
25import mx.core.UIComponent;
26import mx.core.mx_internal;
27
28import spark.components.supportClasses.SkinnableComponent;
29
30/**
31 *  Focus skins for Spark components.
32 *
33 *  @langversion 3.0
34 *  @playerversion Flash 10
35 *  @playerversion AIR 1.5
36 *  @productversion Flex 4
37 */
38public class FocusSkin extends UIComponent
39{
40    //--------------------------------------------------------------------------
41    //
42    //  Class constants
43    //
44    //--------------------------------------------------------------------------
45
46    // TODO: Make this a style property?
47    private const FOCUS_THICKNESS:int = 2;
48
49    //--------------------------------------------------------------------------
50    //
51    //  Class variables
52    //
53    //--------------------------------------------------------------------------
54
55    private static var colorTransform:ColorTransform = new ColorTransform(
56                1.01, 1.01, 1.01, 2);
57    private static var glowFilter:GlowFilter = new GlowFilter(
58                0x70B2EE, 0.85, 5, 5, 3, 1, false, true);
59    private static var rect:Rectangle = new Rectangle();;
60    private static var filterPt:Point = new Point();
61
62    //--------------------------------------------------------------------------
63    //
64    //  Constructor
65    //
66    //--------------------------------------------------------------------------
67
68    /**
69     * Constructor.
70     */
71    public function FocusSkin()
72    {
73        super();
74    }
75
76    //--------------------------------------------------------------------------
77    //
78    //  Variables
79    //
80    //--------------------------------------------------------------------------
81
82    /**
83     *  Bitmap capture of the focused component. This bitmap includes a glow
84     *  filter that shows the focus glow.
85     *
86     *  @langversion 3.0
87     *  @playerversion Flash 10
88     *  @playerversion AIR 1.5
89     *  @productversion Flex 4
90     */
91    private var bitmap:Bitmap;
92
93    /**
94     *  @private
95     */
96    private var _focusObject:SkinnableComponent;
97
98    /**
99     *  Object to draw focus around.  If null, uses focusManager.getFocus();
100     *
101     *  @langversion 3.0
102     *  @playerversion Flash 10
103     *  @playerversion AIR 1.5
104     *  @productversion Flex 4
105     */
106	public function get focusObject():SkinnableComponent
107	{
108	    return _focusObject;
109	}
110
111	public function set focusObject(value:SkinnableComponent):void
112	{
113	    _focusObject = value;
114
115        // Add an "updateComplete" listener to the skin so we can redraw
116        // whenever the skin is drawn.
117        if (_focusObject.skin)
118            _focusObject.skin.addEventListener("updateComplete",
119                    skin_updateCompleteHandler, false, 0, true);
120	}
121
122    //--------------------------------------------------------------------------
123    //
124    //  Overridden methods
125    //
126    //--------------------------------------------------------------------------
127
128    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
129    {
130        // Early exit if we don't have a focus manager
131        if (!focusManager)
132            return;
133
134        // Grab a bitmap of the focused object
135        if (!focusObject)
136			focusObject = focusManager.getFocus() as SkinnableComponent;
137
138        var bitmapData:BitmapData = new BitmapData(
139                    focusObject.width + (FOCUS_THICKNESS * 2),
140                    focusObject.height + (FOCUS_THICKNESS * 2), true, 0);
141        var m:Matrix = new Matrix();
142
143        // If the focus object already has a focus skin, make sure it is hidden.
144        if (focusObject.mx_internal::focusObj)
145            focusObject.mx_internal::focusObj.visible = false;
146
147        // Temporary solution for focus drawing on CheckBox and RadioButton components.
148        // Hide the label before drawing the focus.
149        // TODO: Figure out a better solution.
150        var hidLabelElement:Boolean = false;
151        if ((weakIsCheck(focusObject, "spark.components::CheckBox") ||
152             weakIsCheck(focusObject, "spark.components::RadioButton"))
153             && Object(focusObject).labelDisplay)
154        {
155            Object(focusObject).labelDisplay.displayObject.visible = false;
156            hidLabelElement = true;
157        }
158
159        m.tx = FOCUS_THICKNESS;
160        m.ty = FOCUS_THICKNESS;
161        bitmapData.draw(focusObject as IBitmapDrawable, m);
162
163        // Show the focus skin, if needed.
164        if (focusObject.mx_internal::focusObj)
165            focusObject.mx_internal::focusObj.visible = true;
166
167        // Show the label, if needed.
168        if (hidLabelElement)
169            Object(focusObject).labelDisplay.displayObject.visible = true;
170
171        // Special case for Scroller - fill the entire rect.
172        // TODO: Figure out a better solution.
173        if (weakIsCheck(focusObject, "spark.components::Scroller"))
174        {
175            rect.x = rect.y = FOCUS_THICKNESS;
176            rect.width = focusObject.width;
177            rect.height = focusObject.height;
178            bitmapData.fillRect(rect, 0xFFFFFFFF);
179        }
180
181        // Transform the color to remove the transparency. The GlowFilter has the "knockout" property
182        // set to true, which removes this image from the final display, leaving only the outer glow.
183        rect.x = rect.y = FOCUS_THICKNESS;
184        rect.width = focusObject.width;
185        rect.height = focusObject.height;
186        bitmapData.colorTransform(rect, colorTransform);
187
188        // Apply the glow filter
189        rect.x = rect.y = 0;
190        rect.width = bitmapData.width;
191        rect.height = bitmapData.height;
192        // If the focusObject has an errorString, use "errorColor" instead of "focusColor"
193        if (focusObject.errorString != null && focusObject.errorString != "")
194        {
195            glowFilter.color = focusObject.getStyle("errorColor");
196        }
197        else
198        {
199            glowFilter.color = focusObject.getStyle("focusColor");
200        }
201        bitmapData.applyFilter(bitmapData, rect, filterPt, glowFilter);
202
203        if (!bitmap)
204        {
205            bitmap = new Bitmap();
206            addChild(bitmap);
207            bitmap.x = bitmap.y = -FOCUS_THICKNESS;
208        }
209
210        bitmap.bitmapData = bitmapData;
211    }
212
213    private static var classDefCache:Object = {};
214
215    /**
216     *  @private
217     */
218    private function weakIsCheck(obj:Object, className:String):Boolean
219    {
220        if (!(className in classDefCache))
221        {
222            var classObj:Class = Class(systemManager.getDefinitionByName(className));
223
224            classDefCache[className] = classObj;
225        }
226
227        if (!classDefCache[className])
228            return false;
229
230        return obj is classDefCache[className];
231    }
232
233    private function skin_updateCompleteHandler(event:Event):void
234    {
235        // We need to redraw whenever the focus object skin redraws.
236        invalidateDisplayList();
237    }
238}
239}
240