tags:

views:

96

answers:

1

I'm trying to get a spark tab bar to have a selected tab that does not change color on mouseover. I'm trying to implement this by working with a skin based on the skin spark.skins.spark.TabBarButtonSkin, but despite turning off the shadows on the selected and overAndSelected states, it still shows a dark color on mouseover.

Here's the relevant 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:fb="http://ns.adobe.com/flashbuilder/2009" 
minWidth="21" minHeight="21" alpha.disabledStates="0.5">
<fx:Metadata>[HostComponent("spark.components.ButtonBarButton")]</fx:Metadata>
<!-- host component -->
<fx:Script fb:purpose="styling" >

    import spark.components.TabBar;

    static private const exclusions:Array = ["labelDisplay"];

    /** 
     * @private
     */     
    override public function get colorizeExclusions():Array {return exclusions;}

    /**
     * @private
     */
    override protected function initializationComplete():void
    {
        useChromeColor = true;
        super.initializationComplete();
    }

    private var cornerRadius:Number = 4

    /**
     *  @private
     *  The borderTop s:Path is just a s:Rect with the bottom edge left out.
     *  Given the rounded corners per the cornerRadius style, the result is 
     *  roughly an inverted U with the specified width, height, and cornerRadius.
     * 
     *  Circular arcs are drawn with two curves per flash.display.Graphics.GraphicsUtil.
    */        
    private function updateBorderTop(width:Number, height:Number):void
    {
        var path:String = createPathData(true);
        borderTop.data = path;
    }

    /**
     *  @private
     *  Draw the selected highlight strokes.  The highlight is the same shape as the tab's
     *  border and is inset just within the border.   It must be rendererd with three separate paths, 
     *  since the top leg is rendered with two horizontal lines, with different stroke 
     *  alpha values, and the left and right vertical legs are rendered with a third alpha value.
     *  
     *  Circular arcs are drawn with two curves per flash.display.Graphics.GraphicsUtil.
     */    
    private function updateSelectedHighlight(width:Number, height:Number):void
    {
        if (!selectedHighlightV)
            return;

        var left:Number = -0.5;  // assuming stroke weight is 1.0
        var right:Number = width - 0.5;

        var path:String = createPathData(false);
        selectedHighlightV.data = path;

        // Configure the left/right sides of the two horizontal lines, defined with
        // s:Rects, that appear at the top of the selected highlight.

        selectedHighlightH1.x = selectedHighlightH2.x = left + cornerRadius;
        selectedHighlightH1.width = selectedHighlightH2.width = (right - left) - (2 * cornerRadius);            
    }  

    /**
     *  @private
     *  This function creates the path data used by borderTop and selectedHighlight.
     */
    private function createPathData(isBorder:Boolean):String
    {
        var left:Number = -0.5;  // assuming stroke weight is 1.0
        var right:Number = width - 0.5;
        var top:Number = 0.5;
        var bottom:Number = height;

        var a:Number = cornerRadius * 0.292893218813453;
        var s:Number = cornerRadius * 0.585786437626905;

        // If the path is for the highlight,
        // Draw the vertical part of the selected tab highlight that's rendered 
        // with alpha=0.07.  The s:Path is configured to include only the left and 
        // right edges of an s:Rect, along with the top left,right rounded corners. 
        // Otherwise, we draw a full path.
        var path:String = "";
        path +=  "M " + left + " " + bottom;
        path += " L " + left + " " + (top + cornerRadius);
        path += " Q " + left + " " + (top + s) + " " + (left + a) + " " + (top + a);
        path += " Q " + (left + s) + " " + top + " " + (left + cornerRadius) + " " + top;

        if (isBorder)
            path += " L " + (right - cornerRadius) + " " + top;
        else
            path += " M " + (right - cornerRadius) + " " + top;

        path += " Q " + (right - s) + " " + top + " " + (right - a) + " " + (top + a);
        path += " Q " + right + " " + (top + s) + " " + right + " " + (top + cornerRadius);
        path += " L " + right + " " + bottom;

        return path;
    }

    /**
     *  @private
     *  The cornerRadius style is specified by the TabBar, not the button itself.   
     * 
     *  Rather than bind the corner radius properties of the s:Rect's in the markup 
     *  below to hostComponent.owner.getStyle("cornerRadius"), we reset them here, 
     *  each time a change in the value of the style is detected.  Note that each 
     *  corner radius property is explicitly initialized to the default value of 
     *  the style; the initial value of the private cornerRadius property.
     */
    private function updateCornerRadius():void
    {
        var cr:Number = getStyle("cornerRadius");
        if (cornerRadius != cr)
        {
            cornerRadius = cr;
            fill.topLeftRadiusX = cornerRadius;
            fill.topRightRadiusX = cornerRadius;
            lowlight.topLeftRadiusX = cornerRadius;
            lowlight.topRightRadiusX = cornerRadius;
            highlight.topLeftRadiusX = cornerRadius;
            highlight.topRightRadiusX = cornerRadius;
            highlightStroke.topLeftRadiusX = cornerRadius;
            highlightStroke.topRightRadiusX = cornerRadius;
        }
    }

    /**
     *  @private
     */
    override protected function updateDisplayList(unscaledWidth:Number, unscaleHeight:Number):void
    {
        updateCornerRadius();
        updateSelectedHighlight(unscaledWidth, unscaledHeight);
        updateBorderTop(unscaledWidth, unscaledHeight);
        super.updateDisplayList(unscaledWidth, unscaledHeight);
    }
</fx:Script>

<!-- states -->
<s:states>
    <s:State name="up" />
    <s:State name="over" stateGroups="overStates" />
    <s:State name="down" stateGroups="downStates" />
    <s:State name="disabled" stateGroups="disabledStates" />
    <s:State name="upAndSelected" stateGroups="selectedStates, selectedUpStates" />
    <s:State name="overAndSelected" stateGroups="overStates, selectedStates" />
    <s:State name="downAndSelected" stateGroups="downStates, selectedStates" />
    <s:State name="disabledAndSelected" stateGroups="selectedUpStates, disabledStates, selectedStates" />
</s:states>

<!--- layer 2: fill @private-->
<s:Rect id="fill" left="1" right="1" top="1" bottom="1" topLeftRadiusX="4" topRightRadiusX="4" width="69" height="21">
    <s:fill>
        <s:LinearGradient rotation="90">
            <s:GradientEntry color="0xFFFFFF" 
                             color.selectedStates="0xFFFFFF"
                             color.over="0xBBBDBD" 
                             color.down="0xAAAAAA" 
                             alpha="0.85" 
                             alpha.overAndSelected="1" />
            <s:GradientEntry color="0xD8D8D8" 
                             color.selectedStates="0xFFFFFF"
                             color.over="0x9FA0A1" 
                             color.down="0x929496" 
                             alpha="0.85"
                             alpha.overAndSelected="1" />
        </s:LinearGradient>
    </s:fill>
</s:Rect>

<!--- layer 3: fill lowlight @private-->
<s:Rect id="lowlight" left="1" right="1" top="1" bottom="1" topLeftRadiusX="4" topRightRadiusX="4">
    <s:fill>
        <s:LinearGradient rotation="270">
            <s:GradientEntry color="0x000000" ratio="0.0" alpha="0.0627" />
            <s:GradientEntry color="0x000000" ratio="0.48" alpha="0.0099" />
            <s:GradientEntry color="0x000000" ratio="0.48001" alpha="0" />
        </s:LinearGradient>
    </s:fill>
</s:Rect>

<!--- layer 4: fill highlight @private-->
<s:Rect id="highlight" left="1" right="1" top="1" bottom="1" topLeftRadiusX="4" topRightRadiusX="4">
    <s:fill>
        <s:LinearGradient rotation="90">
            <s:GradientEntry color="0xFFFFFF"
                             ratio="0.0"
                             alpha="0.33" 
                             alpha.selectedUpStates="0.22"
                             alpha.overStates="0.0" 
                             alpha.downStates="0.12"/>
            <s:GradientEntry color="0xFFFFFF"
                             ratio="0.48"
                             alpha="0.33" 
                             alpha.selectedUpStates="0.22"
                             alpha.overStates="0.0" 
                             alpha.downStates="0.12" />
            <s:GradientEntry color="0xFFFFFF"
                             ratio="0.48001"
                             alpha="0" />
        </s:LinearGradient>
    </s:fill>
</s:Rect>
<!--- layer 5: highlight stroke (all states except down) @private -->
<s:Rect id="highlightStroke" left="1" right="1" top="1" bottom="1" topLeftRadiusX="4" topRightRadiusX="4"
        excludeFrom="downStates">
    <s:stroke>
        <s:LinearGradientStroke rotation="90" weight="1">
            <s:GradientEntry color="0xFFFFFF" alpha.overStates="0.22" alpha.selectedUpStates="0.33" />
            <s:GradientEntry color="0xD8D8D8" alpha.overStates="0.22" alpha.selectedUpStates="0.33" />
        </s:LinearGradientStroke>
    </s:stroke>
</s:Rect>

<!--- layer 6: highlight stroke, selected tab, alpha=0.0 when not selected @private -->
<s:Path id="selectedHighlightV" left="1" right="1" top="1" bottom="1" width="69" height="21">
    <s:stroke>
        <s:SolidColorStroke weight="1" color="0x000000" alpha="0.0" 
                            alpha.downStates="0.15" 
                            alpha.selectedUpStates="0.00" 
                            alpha.overAndSelected="0.00" />
    </s:stroke>
</s:Path>
<!--- @private -->
<s:Rect id="selectedHighlightH1" top="1" height="1">
    <s:fill>
        <s:SolidColor color="0x000000" alpha="0.0"
                            alpha.downStates="0.25" 
                            alpha.selectedUpStates="0.0" 
                            alpha.overAndSelected="0.0" />                                    
    </s:fill>
</s:Rect>
<!--- @private -->
<s:Rect id="selectedHighlightH2" top="2" height="1">
    <s:fill>
        <s:SolidColor color="0x000000" alpha="0.0"
                            alpha.downStates="0.15" 
                            alpha.selectedUpStates="0.0" 
                            alpha.overAndSelected="0.00" />  
    </s:fill>
</s:Rect>  

<!--- layer 7: border - put on top of the fill so it doesn't disappear when scale is less than 1 @private -->
<s:Line id="borderBottom" left="0" right="0" bottom="0" excludeFrom="selectedStates">
    <s:stroke>
        <s:SolidColorStroke weight="1" 
                            color="0x000000" 
                            color.selectedStates="0x434343"
                            alpha="0.75" 
                            alpha.down="0.85"/>
    </s:stroke>
</s:Line>
<!--- @private -->
<s:Path id="borderTop" left="0" right="0" top="0" bottom="0" width="69" height="21">
    <s:stroke>
        <s:LinearGradientStroke rotation="90" weight="1">
            <s:GradientEntry color="0x000000" 
                             alpha="0.5625"
                             alpha.down="0.6375"
                             alpha.selectedStates="0.6375" />
            <s:GradientEntry color="0x000000" 
                             alpha="0.75" 
                             alpha.down="0.85"
                             alpha.selectedStates="0.85" />
        </s:LinearGradientStroke>
    </s:stroke>
</s:Path>

<!-- layer 8: text -->
<!--- @copy spark.components.supportClasses.ButtonBase#labelDisplay -->
<s:Label id="labelDisplay"
         textAlign="center"
         verticalAlign="middle"
         maxDisplayedLines="1"
         horizontalCenter="0" verticalCenter="1"
         left="7" right="7" top="2" bottom="2">
</s:Label>

</s:SparkSkin>
+1  A: 
  1. You just can rename states over and overAndSelected to some another, and add fake over and overAndSelected states with no meaning. This is the fast way and dirty way.
  2. Or set all properties with over and overAndSelected states to initial values.

And let us know.

Thank you.

Eugene