3 Replies Latest reply on Feb 18, 2010 12:53 AM by novamatt

    Custom template component with Spark ?

    novamatt Level 1

      Hi there, everyday's got its own (silly?) question :

       

      I'm trying to develop a new component I've called "Drawer", made of a handle (button) and two skinnable containers displayed when the drawer is "opened" and hidden when it is closed.

       

      So here is my AS class:

       

      package nova.style
      {
           import flash.events.MouseEvent;
           
           import spark.components.Button;
           import spark.components.SkinnableContainer;
           import spark.components.supportClasses.SkinnableComponent;
           
           [SkinState("closed")]
           [SkinState("opened")]
           public class Drawer extends SkinnableComponent
           {
                // Define skin parts
                [SkinPart(required="true")]
                public var handle:Button;
                [SkinPart(required="true")]
                public var track:SkinnableContainer;
                [SkinPart(required="true")]
                public var drawerContent:SkinnableContainer;
                
                // Define component data
                [Bindable]
                public var drawerLabel:String;
                [Bindable]
                public var isOpen:Boolean=false;
                [Bindable]
                public var handleX:Number=500;
                
                public function Drawer()
                {
                     super();
                     isOpen = false;
                }
                
                override protected function getCurrentSkinState():String {
                     if (isOpen) {
                          return "opened";
                     } else {
                          return "closed";
                     }
                }
                
                override protected function partAdded(partName:String, instance:Object):void
                {
                     super.partAdded(partName,instance);
                     if( instance == handle)
                     {
                          handle.addEventListener(MouseEvent.CLICK,handle_clickHandler);
                     }
                }
                
                override protected function partRemoved(partName:String, instance:Object):void
                {
                     super.partRemoved(partName,instance);
                     if( instance == handle)
                     {
                          handle.removeEventListener(MouseEvent.CLICK,handle_clickHandler);
                     }
                }
                
                private function handle_clickHandler(event:MouseEvent):void {
                     isOpen=!isOpen;
                     invalidateSkinState();
                }
           }
      }
      

       

      and the associated skin :

      <?xml version="1.0" encoding="utf-8"?>
      <s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" 
                      xmlns:s="library://ns.adobe.com/flex/spark" 
                      xmlns:mx="library://ns.adobe.com/flex/halo">
           <s:states>
                <s:State name="closed"/>
                <s:State name="opened"/>
           </s:states>
           <!-- host component -->
           <fx:Metadata>
                [HostComponent("nova.style.Drawer")]
           </fx:Metadata>
           <s:layout>
                <s:BasicLayout />
           </s:layout>
           <s:VGroup width="100%" height="100%" gap="-1">
                <s:Group includeIn="opened" width="100%" height="100%">
                     <s:SkinnableContainer id="drawerContent" width="100%" height="100%">
                     </s:SkinnableContainer>
                     <s:SkinnableContainer id="track" x="{hostComponent.handleX}" width="{handle.width}">
                     </s:SkinnableContainer>
                </s:Group>
                <s:Button id="handle" label="{hostComponent.drawerLabel}" x="{hostComponent.handleX}" skinClass="nova.style.skins.DrawerHandleSkin"/>
                </s:VGroup>
           </s:VGroup>
      </s:SparkSkin>
      


      I have defined my drawerContent and track parts as SkinnableContainer be cause I want them to be able to hold child elements that the user of my component would specify later on. Actually, I was hoping that I could instanciate my drawer's content in this way :

      <nova:Drawer isOpen="true" drawerLabel="Alarms" skinClass="nova.style.DrawerSkin" handleX="100" width="100%">
                <nova:drawerContent>
                     <s:Panel>
                               <s:Button label="drawer content button"></s:Button>
      </s:Panel>
      </nova:drawerContent>
       <nova:track>
       <s:Panel>
       <s:Button label="track button">
       </s:Button>
       </s:Panel>
       </nova:track>
      </nova:Drawer>
      

       

      Unfortunately, neither of the inner buttons are showing at runtime. I guess I've missed a point in my skin class to indicate that my skinnablecontainers can have a content, but I can't find out what.

      Any clue much appreciated !

       

      Thanks

      Matt

        • 1. Re: Custom template component with Spark ?
          novamatt Level 1

          After a few more searches in the documentation I think that the Template Component pattern http://help.adobe.com/en_US/Flex/4.0/html/WS2db454920e96a9e51e63e3d11c0bf68a49-7ffa.html address my issue. But I'm not sure if this still work with Flex 4 and spark components ? Though I am looking at the Flex 4 documentation, the example given above refers to UIComponent objects. Can I do the same with SkinnableComponent instances instead ?

          Thanks

          Matt

          • 2. Re: Custom template component with Spark ?
            rfrishbe Level 3

            Hey Matt,

             

            I took a look at your skinnable approach, and you're pretty close.  You're just missing the part where the properties get pushed in to the skin parts.  For instance, in a button, we have a skin part, called labelDisplay, and a property called label.  When someone sets the label property, you need to push that in to the labelDisplay skin part.  If you were to look at the label setter in Button, you'd see something like:

             

            if (labelDisplay)
                    labelDisplay.text = label;

             

            For your component, you need a track skin part, and then a trackContent property. When the trackContent gets set, you'd push that in to the track skin part. This is similar to what we do in Panel with controlBarContent and controlBarGroup.  Here's some code snippets from Panel to get you started (I modified the code a bit to make it easier to understand):

             

            //----------------------------------
            //  controlBarGroup
            //----------------------------------

             

            [SkinPart(required="false")]

             

            /**
            *  The skin part that defines the appearance of the
            *  control bar area of the container.
            *  By default, the PanelSkin class defines the control bar area to appear at the bottom
            *  of the content area of the Panel container with a grey background.
            *
            *  @see spark.skins.spark.PanelSkin

            *  @langversion 3.0
            *  @playerversion Flash 10
            *  @playerversion AIR 1.5
            *  @productversion Flex 4
            */
            public var controlBarGroup:Group;

             

            ....

             

            //----------------------------------
            //  controlBarContent
            //----------------------------------

             

            [ArrayElementType("mx.core.IVisualElement")]

             

            /**
            *  The set of components to include in the control bar area of the
            *  Panel container.
            *  The location and appearance of the control bar area of the Panel container
            *  is determined by the spark.skins.spark.PanelSkin class.
            *  By default, the PanelSkin class defines the control bar area to appear at the bottom
            *  of the content area of the Panel container with a grey background.
            *  Create a custom skin to change the default appearance of the control bar.
            *
            *  @default null
            *
            *  @see spark.skins.spark.PanelSkin

            *  @langversion 3.0
            *  @playerversion Flash 10
            *  @playerversion AIR 1.5
            *  @productversion Flex 4
            */
            public function get controlBarContent():Array
            {
                if (controlBarGroup)
                    return controlBarGroup.getMXMLContent();
                else
                    return _controlBarContent;
            }

             

            /**
            *  @private
            */
            public function set controlBarContent(value:Array):void
            {
                if (controlBarGroup)
                    controlBarGroup.mxmlContent = value;


                _controlBarContent = value;
            }

             

            .....

             

            /**
            *  @private
            */
            override protected function partAdded(partName:String, instance:Object):void
            {
                super.partAdded(partName, instance);
               
                if (instance == controlBarGroup)
                {       
                    if (_controlBarContent !== undefined)
                        controlBarGroup.mxmlContent = _controlBarContent

                }
            }

             

            /**
            *  @private
            */
            override protected function partRemoved(partName:String, instance:Object):void
            {
                super.partRemoved(partName, instance);

             

                if (instance == controlBarGroup)
                {
                    _controlBarContent = controlBarGroup.getMXMLContent();

                    controlBarGroup.mxmlContent = null;

                }

             

            }

             

            As I said, I modified the code snippet a bit to make it easier to understand, but that should get you started.

             

            As per the template component pattern, that stuff should still work, but I think you'd be better off following this new pattern which splits it up in tot he SkinnableComponent and the Skin.

             

            -Ryan

            1 person found this helpful
            • 3. Re: Custom template component with Spark ?
              novamatt Level 1

              Thanks Ryan for the hint

              Actually I found a workaround for my drawer implementation, that is it now extends SkinnableContainer and the track and content are put in the "contentGroup" part of my skin. I can live with that for now. But I'm glad to see that my first idea is feasible with this pattern, I'll definitely check it out for my next custom component

              Cheers

              Matt