The most complex piece of visual design in my project is the custom buttons. I'm attaching the code for them so you can see how I approached it.

Basically, each button is a partly transparent (50%) horizontal bar with lettering in the center and a border around it. The right side of the bar extends some distance, gradually fading to 0% via a LinearGradient. The button changes color when hovered over.

The ButtonStyles object is just a wrapper around an array of parameters, that specify the dimensions, color, margin, etc. of the buttons, so that I can have several different kinds of buttons, all drawn by the same base class.

But I've been reviewing the book I'm learning from, and it talks about custom and skinnable components. And I'm wondering if I really need all this code. Maybe I can do all this with a skinnable custom component instead? Then I can statically "draw" the main menu and list of options in MXML. That's one of my goals: to make as much of the layout/visual component of the project static as possible.

What do people think? Is this doable? Is it any simpler than the class I wrote? Or does it make things more complex instead of simpler?
package Displayable
{

import flash.events.MouseEvent;
import flash.display.*;
import mx.graphics.GradientEntry;
import mx.graphics.LinearGradient;
import spark.components.*;
import support.*;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import flash.geom.*;

/**
 * My "button" consisting of a horizontal bar with a border around it and
 * some text over it.
 */

public class HBarButton extends Sprite
{

    public var text;                    // The text of the button
    private var clickFN:Function;       // The callback object

    private var hgroup:HGroup;  // group containing the bar (and fadeout)
    private var dispGroup:Group; // group that everything is drawn in
    private var styles:ButtonStyles;
    private var fadeoutWidth:int;

    /**
     * Constructor.
     * @param btext     The button's label
     * @param clickFN   Called when the button is clicked
     */
    public function HBarButton(msgtext: String, click: Function)
    {
        testAbstract();
        text = msgtext;
        clickFN = click;
        // inherited properties
        buttonMode = true;
        styles = new ButtonStyles();
        fadeoutWidth = 0;
    }

    public function setStyles(newStyles:ButtonStyles): void
    {
        styles = newStyles;
    }

    public function getStyle(which: int):Object
    {
        return styles.getValue(which);
    }

    public function getIntStyle(which: int):int
    {
        return styles.getIntValue(which);
    }

    public function getStringStyle(which: int):String
    {
        return styles.getStringValue(which);
    }

    public function setStyle(which: int, val: int): void
    {
        styles.setValue(which, val);
    }

    /**
     * Make this class abstract by preventing it from being instantiated.
     * Must be overridden in child (non-abstract) classes.
     */
    protected function testAbstract(): void
    {
        throw new NotImplementedError(
            "Can't instantiate abstract class: HBarButton" );
    }

    public function draw(): void
    {
        // Create the bar
        drawBar();
        if (styles.getIntValue(ButtonStyles.BORDER_HEIGHT) > 0) {
            drawBorders();
        }
        // Add the lettering
        drawText();
        // Add the fadeout, if there is one.
        if (fadeoutWidth > 0) {
            drawFadeout();
        }
        setupEvents();
    }

    protected function drawBar(color = null)
    {
            // get dimensions
        var barx = 0;
        var bary = styles.getIntValue(ButtonStyles.BORDER_HEIGHT);
        var barht = styles.getIntValue(ButtonStyles.BAR_HEIGHT);
        var barwd = styles.getIntValue(ButtonStyles.BAR_WIDTH);
        if (color == null) {
            color = styles.getIntValue(ButtonStyles.BAR_COLOR);
        }
            // Set up the fill and draw the rectangle
        graphics.beginFill(color);
        graphics.drawRect(barx, bary, barwd, barht);
        graphics.endFill();
    }

    protected function drawBorders()
    {
            // get dimensions
            // Borders start at x=0; top border starts at y=0, bottom
            // border at y = border height + bar height.
        var x = 0;
        var topy = 0;
        var bdwd = styles.getIntValue(ButtonStyles.BAR_WIDTH);
        var bdht = styles.getIntValue(ButtonStyles.BORDER_HEIGHT);
        var barht = styles.getIntValue(ButtonStyles.BAR_HEIGHT);
        var bdcolor = styles.getIntValue(ButtonStyles.BORDER_COLOR);
        var boty = bdht + barht;
            // Draw the top and bottom border lines
        graphics.lineStyle(bdht, bdcolor);
        graphics.moveTo(x, topy);
        graphics.lineTo(bdwd, topy);
        graphics.moveTo(x, boty);
        graphics.lineTo(bdwd, boty);
    }

    protected function drawText()
    {
        // "Style" the text the way I want it to look
        var myFormat:TextFormat = new TextFormat();
        myFormat.align = styles.getStringValue(ButtonStyles.TEXT_ALIGN);
        myFormat.color = styles.getStringValue(ButtonStyles.FONT_COLOR);
        myFormat.font = styles.getStringValue(ButtonStyles.FONT_NAME);
        myFormat.size = styles.getIntValue(ButtonStyles.FONT_SIZE);
        myFormat.leftMargin = styles.getIntValue(ButtonStyles.MARGIN_RIGHT);
        myFormat.rightMargin = styles.getIntValue(ButtonStyles.MARGIN_RIGHT);

        // Create a label and put the text into it.
        var label = new Label();
        label.setStyle("textFormat", myFormat);
        label.text = text;
        label.autoSize = TextFieldAutoSize.CENTER;
        // Add the label to this sprite
        addChild(label);
    }

    protected function drawFadeout()
    {
            // Dimensions of the Fading rectangle
        var fadex = styles.getIntValue(ButtonStyles.BAR_WIDTH);
        var fadey = styles.getIntValue(ButtonStyles.BORDER_HEIGHT);
        var fadewd = styles.getIntValue(ButtonStyles.FADEOUT_WIDTH);
        var fadeht = styles.getIntValue(ButtonStyles.BAR_HEIGHT);
            // Gradient fill, from normal to zero alpha
        var fill:LinearGradient = new LinearGradient();
        var barColor:int = int(styles.getIntValue(ButtonStyles.BAR_COLOR));
        var g1: GradientEntry = new GradientEntry(barColor, barColor, 1.0);
        var g2: GradientEntry = new GradientEntry(barColor, barColor, 0.0);
        fill.entries = [g1, g2];
            // Draw the rectangle
        var fadeRectangle = new Rectangle(fadex, fadey, fadewd, fadeht);
        var fadeOrigin = new Point(0, 0);
        fill.begin(graphics, fadeRectangle, fadeOrigin);
        graphics.lineStyle(NaN, 0, 0);
        graphics.drawRect(fadex, fadey, fadewd, fadeht);
        fill.end(graphics);
    }

    public function addObject(obj:DisplayObject): void
    {
        throw new Error("addObject not implemented");
    }

    protected function setupEvents(): void
    {
        addEventListener(MouseEvent.CLICK, clickFN);
        addEventListener(MouseEvent.MOUSE_OVER, MouseOverHandler);
        addEventListener(MouseEvent.MOUSE_OUT, MouseOutHandler);
        addEventListener(MouseEvent.MOUSE_DOWN, MouseDownHandler);
        addEventListener(MouseEvent.MOUSE_UP, MouseUpHandler);
    }

    private function MouseOverHandler(): void
    {
        drawBar(styles.getIntValue(ButtonStyles.HOVER_COLOR));
    }

    private function MouseOutHandler(): void
    {
        drawBar(styles.getIntValue(ButtonStyles.BAR_COLOR));
    }

    private function MouseDownHandler(): void
    {
    }

    private function MouseUpHandler(): void
    {
    }
}

} // end package
package Displayable
{

import Displayable.Appearance;

public class ButtonStyles
{
    public static const BAR_COLOR:int =   0;            // The color of the bar
    public static const HOVER_COLOR:int =   1;  // Color when hovered over
    public static const PUSH_COLOR:int =   2;           // Color when button 
pressed
    public static const BORDER_HEIGHT:int = 3;
    public static const BORDER_COLOR:int = 4;   // The color of the borders
    public static const FONT_COLOR:int = 5;
    public static const FONT_SIZE:int = 6;
    public static const FONT_NAME:int = 7;
    public static const BAR_HEIGHT:int = 8;
    public static const BAR_WIDTH:int = 9;
    public static const TOTAL_HEIGHT:int =   10;
    public static const MARGIN_LEFT:int = 11;
    public static const MARGIN_RIGHT:int = 12;
        // Fadeout width:if non-zero, the bar is extended with a gradient
        // that reduces the alpha to zero so that it fades into the
        // background.
    public static const FADEOUT_WIDTH:int = 13;
        // Inherited properties that I use
    public static const ALPHA:int = 14;
    public static const BUTTONMODE:int = 15;
    public static const TEXT_ALIGN:int = 16;

    private static const ARRAY_LENGTH:int = 17;

    private var theValues:Array;

    public function ButtonStyles()
    {
        theValues = new Array(ARRAY_LENGTH);
        theValues[BAR_COLOR] = 0x000000;        // default = black
        theValues[HOVER_COLOR] = Appearance.BUTTON_DEFAULT_HOVER_COLOR;
        theValues[PUSH_COLOR] = Appearance.BUTTON_DEFAULT_PUSH_COLOR;
        theValues[BORDER_COLOR] = Appearance.BUTTON_DEFAULT_BORDER_COLOR;
        theValues[FONT_COLOR] = Appearance.BUTTON_DEFAULT_FONT_COLOR;
        theValues[FONT_SIZE] = Appearance.BUTTON_DEFAULT_FONT_SIZE;
        theValues[FONT_NAME] = Appearance.BUTTON_DEFAULT_FONT_NAME;
        theValues[BAR_HEIGHT] = -1;
        theValues[BAR_WIDTH] = -1;
        theValues[TOTAL_HEIGHT] = -1;
        theValues[MARGIN_LEFT] = 0;
        theValues[MARGIN_RIGHT] = 0;
        theValues[FADEOUT_WIDTH] = 0;
        theValues[BORDER_HEIGHT] = 0;
        theValues[ALPHA] = 50;          // 50%
        theValues[TEXT_ALIGN] = "left";
    }

    public function getValue(which:int):Object
    {
        var val:int = theValues[which];
        if (val < 0) {
            throw new RangeError("unset ButtonStyle: " + which);
        }
        return val;
    }

    public function getStringValue(which:int):String
    {
        return String(getValue(which));
    }

    public function getIntValue(which:int):int
    {
        return int(getValue(which));
    }

    public function setValue(which:int, val:Object):void
    {
        theValues[which] = val;
    }
}

}

Reply via email to