1

I have a Spark ButtonBar that has a custom skin, which defines a custom skin for the "middleButton" requirement. My CustomButtonBarSkin has a custom state, minimized, which I want to pass into my middleButton skin so it can modify its design.

Is it possible to do this? I can see that my button skin could use parentDocument.currentState to get the minimized state, but that's really ugly. Any way to pass a skin from the bar to the child button(s)?

zero323
  • 322,348
  • 103
  • 959
  • 935
Michael
  • 85
  • 1
  • 7

4 Answers4

2

I think you should extend default ButtonBar. Something like this:

package
{
import mx.core.IFactory;

import spark.components.ButtonBar;

[SkinState("minimized")]
[SkinState("minimizedDisabled")]
public class MinimizableButtonBar extends ButtonBar
{
    public function MinimizableButtonBar()
    {
        super();

        itemRendererFunction = defaultButtonBarItemRendererFunction;
    }

    [SkinPart(required="true", type="mx.core.IVisualElement")]
    public var middleButtonMinimized:IFactory;

    private var _minimized:Boolean;

    [Bindable]
    public function get minimized():Boolean
    {
        return _minimized;
    }

    public function set minimized(value:Boolean):void
    {
        if (_minimized == value)
            return;

        _minimized = value;
        invalidateSkinState();
        itemRendererFunction = defaultButtonBarItemRendererFunction;
    }

    override protected function getCurrentSkinState():String
    {
        if (_minimized)
            return enabled ? "minimized" : "minimizedDisabled";
        return super.getCurrentSkinState();
    }

    private function defaultButtonBarItemRendererFunction(data:Object):IFactory
    {
        var i:int = dataProvider.getItemIndex(data);
        if (i == 0)
            return firstButton ? firstButton : (_minimized ? middleButtonMinimized : middleButton);

        var n:int = dataProvider.length - 1;
        if (i == n)
            return lastButton ? lastButton : (_minimized ? middleButtonMinimized : middleButton);

        return (_minimized ? middleButtonMinimized : middleButton);
    }
}
}

So using this code you can declare your custom skin with the following way:

<?xml version="1.0" encoding="utf-8"?>
<s:Skin alpha.disabledGroup="0.5" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark">
    <fx:Metadata>[HostComponent("MinimizableButtonBar")]</fx:Metadata>

    <s:states>
        <s:State name="normal" />
        <s:State name="disabled" stateGroups="disabledGroup" />
        <s:State name="minimized" stateGroups="minimizedGroup" />
        <s:State name="minimizedDisabled" stateGroups="disabledGroup,minimizedGroup" />
    </s:states>

    <fx:Declarations>
        <fx:Component id="firstButton">
            <s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarFirstButtonSkin" />
        </fx:Component>
        <fx:Component id="middleButton">
            <s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarMiddleButtonSkin" />
        </fx:Component>
        <fx:Component id="middleButtonMinimized">
            <s:ButtonBarButton skinClass="MinimazedButtonBarMiddleButtonSkin" />
        </fx:Component>
        <fx:Component id="lastButton">
            <s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarLastButtonSkin" />
        </fx:Component>
    </fx:Declarations>

    <s:DataGroup height="100%" id="dataGroup" width="100%">
        <s:layout>
            <s:ButtonBarHorizontalLayout gap="-1" />
        </s:layout>
        <s:layout.minimizedGroup>
            <s:VerticalLayout />
        </s:layout.minimizedGroup>
    </s:DataGroup>
</s:Skin>

Hope this solves your problem.

And if your minimized state is only about changing middle button skin you can remove all states related code both from custom component and from skin.

Constantiner
  • 14,231
  • 4
  • 27
  • 34
  • I already have an extension of the ButtonBar to maintain the minimized state, so your answer here is really in line with what I was thinking. I didn't know about the item renderer function override, though, and I think that will do the trick. Is there any way to have just one middleButton declaration and switch the skin on that, or set a state on that one declaration's skin? – Michael Apr 16 '11 at 16:14
  • As you can see buttons are represented as `IFactory`. So it seems there is no way to set states declaratively. What about having only one declaration and switching skin for it I suppose it possible the same way as in item renderers for usual `List`. Just remember the button skin is a renderer and `ButtonBar` is a kind of list. So you can change some data provider's field value to inform renderer to switch another state. – Constantiner Apr 16 '11 at 16:23
  • This solution works, however I had to remove the minimized middle button's SkinPart.required=true property and I don't know why. If I have the minimized button as a required SkinPart, I get a runtime exception stating that the skin is missing the required part, even though it is declared just fine - I know this because the skin works just fine without required=true, but I would still like it to be a required part. Any ideas on that? – Michael Apr 17 '11 at 02:00
  • Hm. I haven't any problem with skin included in my answer and can't realize what the problem can be there :( – Constantiner Apr 17 '11 at 08:26
0

I was working with skinning the button bar recently and wanted to expand on/remove some of the default behavior. Rather than extend & overwrite or copy/paste the ButtonBar code I just rolled my own minimalistic component:.

public class HButtonBarGroup extends HGroup {

    public function HButtonBarGroup() {
        addEventListener(ElementExistenceEvent.ELEMENT_ADD, refreshSkins);
        super();
        gap = -1;
    }

    private function refreshSkins(event : * = null) : void {
        var buttonCount : int = numElements;

        for (var i : int = 0; i < buttonCount; i++) {
            var button : Button = getElementAt(i) as Button;
            var skinClass : Class

            if ((buttonCount == 0) || (buttonCount > 2 && (i != 0 && i != buttonCount)))
                skinClass = GreyButtonBarMiddleButtonSkin;
            else if (i == 0)
                skinClass = GreyButtonBarFirstButtonSkin;
            else
                skinClass = GreyButtonBarLastButtonSkin;

            Button(getElementAt(i)).setStyle("skinClass", skinClass);
        }
    }
}

This would give you the ability to do most anything you want without having to tiptoe around ButtonBar, ButtonBarBase, and ButtonBarSkin - all unnecessary unless you want togglebutton/selectedIndex. IMO it is a pain to create buttons based on a dataProvider instead of just declaring buttons in MXML and assigning handlers and other properties there.

Kevin Gallahan
  • 1,645
  • 10
  • 9
  • Thanks, Kevin, that's a great suggestion. If the ButtonBar extension doesn't work like I want, I will try something like what you have suggested here. – Michael Apr 16 '11 at 16:16
0

I recently needed to change skin on a component based on its parents state. I used the same solution I would have used in HTML, using CSS. In your case, something like:

s|ButtonBar:minimized s|ButtonBarButton {
    skinClass: ClassReference("CustomButtonBarSkin");
}
s|ButtonBarButton {
    skinClass: ClassReference("spark.skins.spark.ButtonBarMiddleButtonSkin");
}

:minimized is the Pseudo Selector (for States).

Unfortunately, this didn't seem to get picked up by child (bug?) unless I changed styleName on parent element on state change:

<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"
    styleName.normal="foo" styleName.minimized="foo"
    >

Maybe there is some invalidate-method I should have called on parents state change instead to make child pick up the change, but merely change the styleName to something bogus did the trick.

This is maybe not a widely used technique in Flex due to the fact that Flex 3 only supported basic CSS selectors.

rlovtang
  • 4,860
  • 2
  • 30
  • 30
0

Maybe I'm not getting what you're trying to do exactly, but it seems fairly obvious and easy to me. Just have your custome button bar skin set the state of your middle button when the minimized state is active:

<s:Skin>

<s:states>
<s:State name="minimized" />
</s:states>

<s:ButtonBarButton currentState.minimized="someState" />

</s:Skin>

Get it?

J_A_X
  • 12,857
  • 1
  • 25
  • 31