Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inherit states with mxml?

I have the following panel component called AdvancedPanel with controlBarContent:

<!-- AdvancedPanel.mxml -->
<s:Panel>
  <s:states>
    <s:State name="normal" />
    <s:State name="edit" />
  </s:states>
  <s:controlBarContent>
    <s:Button 
      includeIn="edit"
      label="Show in edit"
      />
    <s:Button 
      label="Go to edit"
      click="{currentState='edit'}"
      />
  </s:controlBarContent>
</s:Panel>

I created a second panel, called CustomAdvancedPanel based on the AdvancedPanel since I don't want to redeclare the controlBarContent

<!-- CustomAdvancedPanel.mxml -->
<local:AdvancedPanel>
  <s:Button includeIn="edit" label="Extra edit button" />
</local:AdvancedPanel>

This doesn't work, because the 'edit' state in CustomAdvancedPanel isn't declared according to the compiler. I have to redeclare the edit state in CustomAdvancedPanel.mxml as follows:

  <!-- CustomAdvancedPanel.mxml with edit state redeclared -->
    <local:AdvancedPanel>
      <local:states>
        <s:State name="normal" />
        <s:State name="edit" />
      </local:states>
      <s:Button includeIn="edit" label="Extra edit button" />
    </local:AdvancedPanel>

Using the CustomAdvancedPanel inside an application component shows an empty panel with the "Go to edit" button. But when I click it, the "Extra edit button" becomes visible, but the "Show in edit" button inside the controlBar doesn't.

When the CustomAdvancedPanel is empty, without redeclared states and "Extra edit button" the panel works just fine.

I think it is because the State object declared in AdvancedPanel isn't the same as CustomAdvancedPanel, so the state is different, even if they have the same name. However. I can't use the states of AdvancedPanel inside CustomAdvancedPanel without (re)declare them in mxml.

Is there any way to achieve this kind of state-reuse? Or is there a better way to obtain the same result?

like image 901
Treur Avatar asked Oct 18 '10 14:10

Treur


2 Answers

I suggest you to use Spark's skinning architecture to obtain your goals. Because skin states are inherited in host component you can place all the logic in OOP way. But skins will still contain duplicate code :( Anyway it is better than duplicate code of all the component.

So our AdvancedPanel will look like the following:

package
{
    import flash.events.MouseEvent;

    import spark.components.supportClasses.ButtonBase;
    import spark.components.supportClasses.SkinnableComponent;

    [SkinState("edit")]
    [SkinState("normal")]
    public class AdvancedPanel extends SkinnableComponent
    {
        [SkinPart(required="true")]
        public var goToEditButton:ButtonBase;
        [SkinPart(required="true")]
        public var showInEditButton:ButtonBase;

        private var editMode:Boolean;

        override protected function getCurrentSkinState():String
        {
            return editMode ? "edit" : "normal";
        }

        override protected function partAdded(partName:String, instance:Object):void
        {
            super.partAdded(partName, instance);
            if (instance == goToEditButton)
                goToEditButton.addEventListener(MouseEvent.CLICK, onGoToEditButtonClick);
        }

        override protected function partRemoved(partName:String, instance:Object):void
        {
            super.partRemoved(partName, instance);
            if (instance == goToEditButton)
                goToEditButton.removeEventListener(MouseEvent.CLICK, onGoToEditButtonClick);
        }

        private function onGoToEditButtonClick(event:MouseEvent):void
        {
            editMode = true;
            invalidateSkinState();
        }
    }
}

And for CustomAdvancedPanel:

package
{
    import spark.components.supportClasses.ButtonBase;

    public class CustomAdvancedPanel extends AdvancedPanel
    {
        [SkinPart(required="true")]
        public var extraEditButton:ButtonBase;
    }
}

Of course you can inherit from Panel class but I made sample code more simple.

And the skins:

<?xml version="1.0" encoding="utf-8"?>
<!-- AdvancedPanelSkin.mxml -->
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">
    <fx:Metadata>
        [HostComponent("AdvancedPanel")]
    </fx:Metadata>
    <s:states>
        <s:State name="normal" />
        <s:State name="edit" />
    </s:states>
    <s:Panel left="0" right="0" top="0" bottom="0">
        <s:controlBarContent>
            <s:Button id="showInEditButton" label="Show in edit" includeIn="edit" />
            <s:Button id="goToEditButton" label="Go to edit" />
        </s:controlBarContent>
    </s:Panel>
</s:Skin>

And:

<?xml version="1.0" encoding="utf-8"?>
<!-- CustomAdvancedPanelSkin.mxml -->
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">
    <fx:Metadata>[HostComponent("CustomAdvancedPanel")]</fx:Metadata>
    <s:states>
        <s:State name="normal" />
        <s:State name="edit" />
    </s:states>
    <s:Panel left="0" right="0" top="0" bottom="0">
        <s:Button includeIn="edit" label="Extra edit button" id="extraEditButton" />
        <s:controlBarContent>
            <s:Button id="showInEditButton" label="Show in edit" includeIn="edit" />
            <s:Button id="goToEditButton" label="Go to edit" />
        </s:controlBarContent>
    </s:Panel>
</s:Skin>
like image 65
Constantiner Avatar answered Nov 12 '22 22:11

Constantiner


AFAIK the component's state does not cross over to inherited components. Think about it - if that were the case (if you could inherit states) then it would make life really complicated whenever you want to extend a component; you would have to be aware of all inherited states and not step on their toes.

like image 42
Assaf Lavie Avatar answered Nov 12 '22 22:11

Assaf Lavie