JMU
Developing GUI Components
An Introduction with Examples in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Introduction
A Generic Widget

The Widget Class

javaexamples/widget/Widget.java
        package widget;

import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;

import java.awt.Panel;

/**
 * The parent class of all GUI components/widgets
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class Widget extends    Panel
                             implements FocusListener
{
    protected boolean            doubleBuffered = true;
    protected boolean            hasFocus = false;
    protected Graphics           bufferedGraphics;
    protected Image              bufferedImage;


    /**
     * Default Constructor
     */
    public Widget()
    {
        super();
    }




    /**
     * Process a focusGained event
     *
     * @param evt   The FocusEvent
     */
    public void focusGained(FocusEvent evt)
    {
        if (!evt.isTemporary()) setHasFocus(true);
    }




    /**
     * Process a focusLost event
     *
     * @param evt   The FocusEvent
     */
    public void focusLost(FocusEvent evt)
    {
        if (!evt.isTemporary()) setHasFocus(false);
    }





    /**
     * Get a color for this widget/Component
     *
     * @param part   The part of the component (e.g., BORDER)
     * @param state  The state (e.g., DISABLED)
     * @return       The Color
     */
    public Color getColor(int part, int state)
    {
        return getView().getColor(part, state);
    }


    /**
     * Get the model associated with this component/Widget
     *
     * @return    The model
     */
    public abstract WidgetModel getModel();



    /**
     * Get the view associated with this component/Widget
     *
     * @return    The view
     */
    public abstract WidgetView getView();







    /**
     * Get the minimum amount of size required by this
     * component/Widget
     *
     * @return    The minimum required size
     */
    public Dimension getMinimumSize()
    {
        return getView().getMinimumSize();
    }



    /**
     * Get the preferred size for this component/Widget
     *
     * @return  The minimum required size
     */
    public Dimension getPreferredSize()
    {
        return getMinimumSize();
    }



    /**
     * Does this component/Widget have the focus?
     *
     * @return  true if the component has the focus
     */
    public boolean hasFocus()
    {
        return hasFocus;
    }





    /**
     * Is the component/Widget capable of having the focus?
     *
     * @return  true if the component can/should get the focus
     */
    public boolean isFocusTraversable()
    {
        return true;
    }







    /**
     * Paint this component/Widget
     *
     * @param g  The Graphics context to use
     */
    public void paint(Graphics g)
    {
        if (doubleBuffered) 
        {
            if ( (bufferedImage == null) || 
                 (getSize().width  != bufferedImage.getWidth(null)) || 
                 (getSize().height != bufferedImage.getHeight(null))   )
            {
                bufferedImage = createImage(getSize().width, 
                                            getSize().height);

                bufferedGraphics = bufferedImage.getGraphics();
            }

            bufferedGraphics.setFont(g.getFont());
            getView().paint(bufferedGraphics);
            g.drawImage(bufferedImage, 0, 0, null);
        } 
        else 
        {
            getView().paint(g);
        }
    }





    /**
     * Pause (i.e., sleep) for the specified amount of time
     *
     * @param   millis   The number of millseconds to pause
     */
    public void pause(int millis)
    {
        try 
        {
            Thread.sleep(millis);
        } 
        catch (InterruptedException ie) 
        {
            // Ignore
        }
    }



    /**
     * Set a color for this widget/Component
     *
     * @param part   The part of the component (e.g., BORDER_COLOR)
     * @param state  The state (e.g., DISABLED_COLOR)
     * @param color  The Color
     */
    public void setColor(int part, int state, Color color)
    {
        getView().setColor(part, state, color);
    }



    /**
     * Set whether this component has the focus
     *
     * @param hasFocus     true if it has the focus, false otherwise
     */
    public void setHasFocus(boolean hasFocus)
    {
        this.hasFocus = hasFocus;
        repaint();
    }



    /**
     * Set the height of this component/Widget
     *
     * @param height  The height (in pixels)
     */
    public void setHeight(int height)
    {
        getView().setHeight(height);
    }






    /**
     * Setup the event handlers for this Widget
     */
    public void setupEventHandlers()
    {
        addFocusListener(this);
        requestFocus();
    }



    /**
     * Update this component
     *
     * @param g   The Graphics context
     */
    public void update(Graphics g)
    {
        paint(g);
    }
}
        
A Generic Widget (cont.)

The WidgetModel Class

javaexamples/widget/WidgetModel.java
        package widget;


/**
 * The parent class of  a GUI component's/widget's model
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class  WidgetModel
{





}
        

The WidgetView Class

javaexamples/widget/WidgetView.java
        package widget;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;


/**
 * The parent class of  a GUI component's/widget's view
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class  WidgetView
{

    protected Color[][]          colors;
    protected Dimension          minimumSize = new Dimension();
    protected Insets             insets = new Insets(0,0,0,0);


    public static final int DISABLED    = 0;
    public static final int FOCUSED     = 1;
    public static final int UNFOCUSED   = 2;

    public static final int BACKGROUND  = 0;
    public static final int BORDER      = 1;
    public static final int FOREGROUND  = 2;



    /**
     * Get a color for this widget/Component
     *
     * @param part   The part of the component (e.g., BORDER)
     * @param state  The state (e.g., DISABLED)
     * @return       The Color
     */
    public Color getColor(int part, int state)
    {
        Color   color;

        color = null;
        if ( (part  >= BACKGROUND) && (part <= FOREGROUND) &&
             (state >= DISABLED)   && (part <= UNFOCUSED) ) 
        {
            color = colors[part][state];
        }

        return color;
    }





    /**
     * Get the minimum amount of size required by this
     * component
     *
     * @return    The minimum required size
     */
    public Dimension getMinimumSize()
    {
        return minimumSize;
    }




    /**
     * Get the preferred size for this component
     *
     * @return    The minimum required size
     */
    public Dimension getPreferredSize()
    {
        return getMinimumSize();
    }



    /**
     * Get the component/Widget being controlled
     *
     * @return  The Widget
     */
    public abstract Widget getWidget();




    /**
     * Get the Insets associated with this component/Widget
     *
     * @return  The Insets
     */
    public Insets getInsets()
    {
        return insets;
    }





    /**
     * Paint this component
     *
     * @param g  The Graphics context to use
     */
    public abstract void paint(Graphics g);




    /**
     * Set a color for this widget/Component
     *
     * @param part   The part of the component (e.g., BORDER)
     * @param state  The state (e.g., DISABLED)
     * @param color  The Color
     */
    public void setColor(int part, int state, Color color)
    {
        if ( (part  >= BACKGROUND) && (part <= FOREGROUND) &&
             (state >= DISABLED)   && (part <= UNFOCUSED) ) 
        {
            colors[part][state] = color;
        }
    }





    /**
     * Set the colors
     *
     * @param colors  The matrix of Color objects
     */
    protected void setColors(Color[][] colors)
    {
        this.colors = colors;
    }





    /**
     * Set the height of this component/Widget
     *
     * @param height  The height (in pixels)
     */
    public void setHeight(int height)
    {
        Dimension size;
        Widget    widget;

        widget = getWidget();
        size = widget.getSize();
        widget.setSize(size.width, height);
    }





    /**
     * Set the Insets associated with this component/Widget
     *
     * @param insets  The Insets
     */
    public void setInsets(Insets insets)
    {
        this.insets = insets;
    }




    /**
     * Set the minimum size of this component/Widget
     *
     * @param width   The width (in pixels)
     * @param height  The height (in pixels)
     */
    public void setMinimumSize(int width, int height)
    {
        minimumSize.width = width;
        minimumSize.height = height;
    }





    /**
     * Set the width (in pixels) of the component
     *
     * @param width   The width
     */
    public void setWidth(int width)
    {
        Dimension size;
        Widget    widget;

        widget = getWidget();
        size = widget.getSize();
        widget.setSize(size.height, width);
    }





}
        
A Generic Widget (cont.)

The SizeScheme Class

javaexamples/widget/SizeScheme.java
        package widget;

/**
 * A SizeScheme is a collection of sizes used by a Widget set
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class SizeScheme
{
    public static int WIDTH  = 640;
    public static int HEIGHT = 480;

    public static int BUTTON_HEIGHT =  35;
    public static int BUTTON_WIDTH =   95;
    public static int HALF_BUTTON_WIDTH =  45;

}
        

The ColorScheme Class

javaexamples/widget/ColorScheme.java
        package widget;

import java.awt.Color;


/**
 * A ColorScheme is a collection of colors used by a Widget set
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ColorScheme
{
    public static final int DISABLED    = WidgetView.DISABLED;
    public static final int FOCUSED     = WidgetView.FOCUSED;
    public static final int UNFOCUSED   = WidgetView.UNFOCUSED;

    public static final int BACKGROUND  = WidgetView.BACKGROUND;
    public static final int BORDER      = WidgetView.BORDER;
    public static final int FOREGROUND  = WidgetView.FOREGROUND;

    public static final Color blue  = new Color(4, 130, 132);
    public static final Color brown = new Color(212, 206, 188);
    public static final Color tan   = new Color(236, 230, 220);


    public static final Color FRAME = brown;



    public static Color[][] getButtonColors()
    {
        Color[][] colors;

        colors = new Color[3][3];
        colors[DISABLED][BACKGROUND]  = brown;
        colors[DISABLED][BORDER]      = brown;
        colors[DISABLED][FOREGROUND]  = Color.gray; 
        colors[FOCUSED][BACKGROUND]   = tan;
        colors[FOCUSED][BORDER]       = tan; 
        colors[FOCUSED][FOREGROUND]   = Color.black; 
        colors[UNFOCUSED][BACKGROUND] = brown; 
        colors[UNFOCUSED][BORDER]     = brown; 
        colors[UNFOCUSED][FOREGROUND] = Color.white;

        return colors;
    }
}
        
A Generic Button

The AbstractButton Class

javaexamples/widget/AbstractButton.java
        package widget;

import java.awt.AWTEventMulticaster;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.PaintEvent;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;



/**
 * The abstract parent for simple Button objects
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class AbstractButton extends    Widget
   implements KeyListener, 
MouseListener
{
    protected ActionListener  actionListener;
    protected int             buttonType;


    public static final int   RECTANGLE          = 0;
    public static final int   ROUNDED_RECTANGLE  = 1;

    public static final int   LEFT               = 0;
    public static final int   CENTER             = 1;
    public static final int   RIGHT              = 2;

    public static final int   TOP                = 0;
    //                        CENTER             = 1;
    public static final int   BOTTOM             = 2;

    public static final int   NORMAL             = 0;
    public static final int   TOGGLE             = 1;





    /**
     * Default constructor
     */
    public AbstractButton()
    {
       super();
       actionListener = null;
    }





    /**
     * Add an ActionListener
     *
     * @param listener   The ActionListener to add
     */
    public void addActionListener(ActionListener listener)
    {
       // Add the listener to the existing multicast listener
       actionListener = AWTEventMulticaster.add(actionListener, listener);
    }



    /**
     * Get the action command associated with this Button
     *
     * @return  The action command
     */
    public String getActionCommand()
    {
       AbstractButtonModel      model;

       model = (AbstractButtonModel)(getModel());
       return model.getActionCommand();
    }




    /**
     * Get the button type
     *
     * @return  The button type
     */
    public int getButtonType()
    {
       return buttonType;
    }





    /**
     * Get the default icon Image associated with  the button
     *
     * @return   The Image
     */
    public Image getIcon()
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());
       return view.getIcon();
    }







    /**
     * Get the text on this Button
     *
     * @return  The text
     */
    public String getText()
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());
       return view.getText();
    }





    /**
     * Get the shape of this  Button
     *
     * @return  The shape
     */
    public int getShape()
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());
       return view.getShape();
    }




    /**
     * Handle keyPressed events (required by KeyListener)
     *
     * @param evt   The KeyEvent
     */
    public void keyPressed(KeyEvent evt)
    {
       AbstractButtonView          view;
       Rectangle                   r;
       int                         keyCode;


       view = (AbstractButtonView)(getView());


       keyCode = evt.getKeyCode();
       if ( (keyCode == KeyEvent.VK_SPACE) || 
            (keyCode == KeyEvent.VK_ENTER)    )
       {
          dispatchEvent(new MouseEvent(this,
                                       MouseEvent.MOUSE_PRESSED,
                                       0, 0, 0, 0, 1, false));

          r = getBounds();
          r.x = 0;
          r.y = 0;


          dispatchEvent(new PaintEvent(this,
                                       PaintEvent.PAINT,
                                       r
                           ));
          notifyActionListeners();
          pause(50);

          dispatchEvent(new MouseEvent(this,
                                       MouseEvent.MOUSE_RELEASED,
                                       0, 0, 0, 0, 1, false));
       }
    }





    /**
     * Handle keyReleased events (required by KeyListener)
     *
     * @param evt   The KeyEvent
     */
    public void keyReleased(KeyEvent evt)
    {
       // Ignore
    }





    /**
     * Handle keyTyped events (required by KeyListener)
     *
     * @param evt   The KeyEvent
     */
    public void keyTyped(KeyEvent evt)
    {
       // Ignore
    }







    /**
     * Handle mouseClicked events (required by MouseListener)
     *
     * @param evt   The event
     */
    public void mouseClicked(MouseEvent evt)
    {
       // Everything is handled by the mousePressed and
       // mouseReleased handlers
    }



    /**
     * Handle mouseEntered events (required by MouseListener)
     *
     * @param evt   The event
     */
    public void mouseEntered(MouseEvent evt)
    {
       // Handled by children that care
    }




    /**
     * Handle mouseExited events (required by MouseListener)
     *
     * @param evt   The event
     */
    public void mouseExited(MouseEvent evt)
    {
       // Handled by children that care
    }




    /**
     * Handle mousePressed events (required by MouseListener)
     *
     * @param evt   The event
     */
    public void mousePressed(MouseEvent evt)
    {
       AbstractButtonModel         model;
       AbstractButtonView          view;

       model  = (AbstractButtonModel)(getModel());
       view   = (AbstractButtonView)(getView());

       if (isEnabled())
       {
          requestFocus();

          if (buttonType == TOGGLE)
          {
             if (model.isSelected())
             {
                model.setSelected(false);

             }
             else
             {
                model.setSelected(true);
                view.setViewState(AbstractButtonView.DOWN);
             }
          }
          else
          {
             view.setViewState(AbstractButtonView.DOWN);
          }
       }
    }




    /**
     * Handle mouseReleased events (required by MouseListener)
     *
     * @param evt   The event
     */
    public void mouseReleased(MouseEvent evt)
    {
       AbstractButtonModel         model;
       AbstractButtonView          view;

       model  = (AbstractButtonModel)(getModel());
       view = (AbstractButtonView)(getView());


       if (isEnabled())
       {
          notifyActionListeners();
          if (buttonType == TOGGLE)
          {
             // Ignore
          }
          else
          {
             view.setViewState(AbstractButtonView.UP);
          }
       }
    }





    /**
     * Notify all ActionListener objects of an ActionEvent
     */
    public void notifyActionListeners()
    {
       AbstractButtonModel         model;
       ActionEvent                 ae;


       model = (AbstractButtonModel)(getModel());


       ae = new ActionEvent(this, 
                            ActionEvent.ACTION_PERFORMED, 
                            model.getActionCommand());

       if (actionListener != null)
       {
          actionListener.actionPerformed(ae);
       }
    }




    /**
     * Remove an ActionListener
     *
     * @param listener   The ActionListener to remove
     */
    public void removeActionListener(ActionListener listener)
    {
       // Remove the listener from the existing multicast listener
       actionListener = AWTEventMulticaster.remove(actionListener, listener);
    }




    /**
     * Set the action command associated with this Button
     *
     * @param ac    The action command
     */
    public void setActionCommand(String ac)
    {
       AbstractButtonModel      model;

       model = (AbstractButtonModel)(getModel());
       model.setActionCommand(ac);
    }




    /**
     * Set the button type (NORMAL or TOGGLE)
     *
     * @param type   The type
     */
    public void setButtonType(int type)
    {
       if ((type == NORMAL) || (type == TOGGLE)) buttonType = type;
    }






    /**
     * Set the Font
     *
     * @param font   The Font
     */
    public void setFont(Font font)
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());
       super.setFont(font);
       if (view != null) view.setTextSize(null);
    }




    /**
     * Set the horizontal position of the text
     *
     * @param hpos   The horizontal position (LEFT, CENTER, RIGHT)
     */
    public void setHorizontalTextPosition(int hpos)
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());
       if ((hpos >= LEFT) && (hpos <= CENTER))
       {
          view.setHorizontalTextPosition(hpos);
       }
    }





    /**
     * Set the default icon Image associated with  the button
     *
     * @param image   The Image
     */
    public void setIcon(Image image)
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());

       view.setIcon(image);
    }




    /**
     * Set the model associated with this component/Widget
     *
     * @param model    The model
     */
    public void setModel(AbstractButtonModel model)
    {
       // Nothing to do
    }







    /**
     * Set the shape of this Button
     *
     * @param shape    The shape (RECTANGLE, ROUNDED_RECTANGLE)
     */
    public void setShape(int shape)
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());
       if ((shape >= RECTANGLE) && (shape <= ROUNDED_RECTANGLE))
       {
          view.setShape(shape);
       }
    }






    /**
     * Set the text on this Button
     *
     * @param text    The text
     */
    public void setText(String text)
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());
       view.setText(text);
       view.setTextSize(null);
    }





    /**
     * Setup the event handlers for this Widget
     */
    public void setupEventHandlers()
    {
       super.setupEventHandlers();

       addKeyListener(this);
       addMouseListener(this);
    }






    /**
     * Set the vertical position of the text
     *
     * @param vpos   The vertical position (TOP, CENTER, BOTTOM)
     */
    public void setVerticalTextPosition(int vpos)
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());
       if ((vpos >= TOP) && (vpos <= BOTTOM))
       {
          view.setVerticalTextPosition(vpos);
       }
    }







    /**
     * Set the View for this component/Widget
     *
     * @param view  The view
     */
    public void setView(AbstractButtonView view)
    {
       // Nothing to do
    }





    /**
     * Set the view state (e.g., UP, DOWN)
     *
     * @param  The viewState
     */
    public void setViewState(int viewState)
    {
       AbstractButtonView       view;

       view = (AbstractButtonView)(getView());
       view.setViewState(viewState);
    }



}
        
A Generic Button (cont.)

The AbstractButtonModel Class

javaexamples/widget/AbstractButtonModel.java
        package widget;


/**
 * The Model (from the Model-View-Controller pattern)
 * for an AbstractButton.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class AbstractButtonModel extends WidgetModel
{
    private boolean                 selected;
    protected String                actionCommand;


    /**
     * Constructor
     *
     * @param button  The button this controller belongs to
     * @param ac      The action command associated with  this button
     */
    public AbstractButtonModel(String ac)
    {
        this.actionCommand = ac;
        selected = false;
    }




    /**
     * Get the action command associated with this Button
     *
     * @return  The action command
     */
    public String getActionCommand()
    {
        return actionCommand;
    }






    /**
     * Is this ToggleButton selected?
     *
     * @return   true if selected, false otherwise
     */
    public boolean isSelected()
    {
        return selected;
    }





    /**
     * Set the action command associated with this Button
     *
     * @param ac    The action command
     */
    public void setActionCommand(String ac)
    {
        actionCommand = ac;
    }





    /**
     * Set whether this ToggleButton is selected
     *
     * @param selected   true to selected, false to de-select
     */
    public void setSelected(boolean selected)
    {
        this.selected = selected;
    }



}

        

The AbstractButtonView Class

javaexamples/widget/AbstractButtonView.java
        package widget;

import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;

/**
 * The View (from the Model-View-Controller pattern)
 * for a Button.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class AbstractButtonView extends WidgetView
{
    protected Dimension        textSize;
    protected FontMetrics      fm;
    protected Image            disabledIcon, enabledIcon;
    protected int              arcHeight, arcWidth, horizontalTextPosition;
    protected int              mouseState, shape, verticalTextPosition;
    protected int              viewState;
    protected String           text;


    public static final int   DOWN = 0;
    public static final int   UP   = 1;



    /**
     * Default constructor
     */
    public AbstractButtonView()
    {
        this(0, 0);
    }



    /**
     * Constructor
     *
     * @param width         The width of the button (in pixels)
     * @param height        The height of the button (in pixels)
     */
    public AbstractButtonView(int width, int height)
    {
        setMinimumSize(width, height);

        viewState  = UP;
        shape = AbstractButton.RECTANGLE;

        arcHeight = 20;
        arcWidth  = 20;

        disabledIcon = null;
        enabledIcon = null;

        horizontalTextPosition  = AbstractButton.CENTER;
        verticalTextPosition    = AbstractButton.CENTER;
    }






    /**
     * Get the default icon Image associated with  the button
     *
     * @return   The Image
     */
    public Image getIcon()
    {
        return enabledIcon;
    }






    /**
     * Get the shape of this  Button
     *
     * @return  The shape
     */
    public int getShape()
    {
        return shape;
    }





    /**
     * Get the text on this Button
     *
     * @return  The text
     */
    public String getText()
    {
        return text;
    }




    /**
     * Get the view state (e.g., UP, DOWN)
     *
     * @return  The view state
     */
    public int getViewState()
    {
        return viewState;
    }





    /**
     * Paint this component
     *
     * @param g  The Graphics context to use
     */
    public abstract void paint(Graphics g);





    /**
     * Set the horizontal position of the text
     *
     * @param hpos   The horizontal position (LEFT, CENTER, RIGHT)
     */
    public void setHorizontalTextPosition(int hpos)
    {
        horizontalTextPosition = hpos;
    }






    /**
     * Set the default icon Image associated with  the button
     *
     * @param icon   The Image
     */
    public void setIcon(Image icon)
    {
        enabledIcon = icon;
    }






    /**
     * Set the state of the mouse
     *
     * @param state  The new state
     */
    /*
    public void setMouseState(int state)
    {
        AbstractButton         button;

        button = (Button)(getWidget());


        if ((state >= MOUSE_OUT) && (state <= MOUSE_UP)) {

            mouseState = state;

        } else {

            mouseState = MOUSE_OUT;
        }

        button.repaint();
    }
    */




    /**
     * Set the shape of this Button
     *
     * @param shape    The shape (RECTANGLE, ROUNDED_RECTANGLE)
     */
    public void setShape(int shape)
    {
        this.shape = shape;
    }




    /**
     * Set the text on this Button
     *
     * @param text    The text
     */
    public void setText(String text)
    {
        this.text = text;
    }





    /**
     * Set the size of the text on this Button
     *
     * @param size    The size of the text
     */
    public void setTextSize(Dimension size)
    {
        this.textSize = size;
    }





    /**
     * Set the vertical position of the text
     *
     * @param vpos   The vertical position (TOP, CENTER, BOTTOM)
     */
    public void setVerticalTextPosition(int vpos)
    {
        verticalTextPosition = vpos;
    }



    /**
     * Set the view state (e.g., UP, DOWN)
     *
     * @param  The viewState
     */
    public void setViewState(int viewState)
    {
        AbstractButton         button;

        button = (Button)(getWidget());
        if ((viewState >= DOWN) && (viewState <=UP))
        {
            this.viewState = viewState;
            button.repaint();
        }
    }




}

        
A "Normal" Button

The Button Class

javaexamples/widget/Button.java
        package widget;


/**
 * An encapsulation of a "normal" Button
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Button extends AbstractButton
{
    protected ButtonModel       model;
    protected ButtonView        view;


    /**
     * Default constructor
     */
    public Button()
    {
        this("DefaultButtonActionCommand", 10, 10);
    }


    /**
     * Constructor
     *
     * @param ac            The action command associated with  this button
     * @param text          The text on the Button
     * @param width         The width of the Button (in pixels)
     * @param height        The height of the Button (in pixels)
     */
    public Button(String ac, int width, int height)
    {
        super();
        setModel(new ButtonModel(this, ac));
        setView(new ButtonView(this, width, height));

        setupEventHandlers();

        buttonType = NORMAL;
    }




    /**
     * Get the model associated with this component/Widget
     *
     * @return    The model
     */
    public WidgetModel getModel()
    {
        return model;
    }




    /**
     * Get the view associated with this component/Widget
     *
     * @return    The view
     */
    public WidgetView getView()
    {
        return view;
    }




    /**
     * Set the model associated with this component/Widget
     *
     * @param model    The model
     */
    public void setModel(ButtonModel model)
    {
        super.setModel(model);

        this.model = model;
    }






    /**
     * Set the View for this component/Widget
     *
     * @param view  The view
     */
    public void setView(ButtonView view)
    {
        super.setView(view);

        this.view = view;
    }


}
        
A "Normal" Button (cont.)

The ButtonView Class

javaexamples/widget/ButtonView.java
        package widget;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;



/**
 * The View (from the Model-View-Controller pattern)
 * for a Button.
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 */
public class ButtonView extends AbstractButtonView
{
    protected Button           button;
    protected Color            downColor, upColor;

    /**
     * Constructor
     *
     * @param button        The button this view belongs to
     * @param width         The width of the button (in pixels)
     * @param height        The height of the button (in pixels)
     */
    public ButtonView(Button button, int width, int height)
    {
        super(width, height);
        this.button = button;

        getWidget().setSize(width, height);

        button.setSize(new Dimension(width, height));
        button.setFont(new Font("Sans Serif", Font.PLAIN, 20));

        button.setBackground(ColorScheme.FRAME);

        setColors(ColorScheme.getButtonColors());

        downColor = Color.red;
        upColor   = Color.gray;
    }



    /**
     * Get the Button associated with this view
     *
     * @return   The Button
     */
    public Widget getWidget()
    {
        return button;
    }




    /**
     * Paint this component
     *
     * @param g  The Graphics context to use
     */
    public void paint(Graphics g)
    {
        paintBackground(g);
        paintText(g);
        paintBorder(g);
    }




    /**
     * Paint the background
     *
     * @param g   The Graphics context to use
     */
    protected void paintBackground(Graphics g)
    {
        Color      bgColor;
        Dimension  size;
        int        buttonType, h, w, x, y;


        size = button.getSize();


        // Fill the background
        bgColor = button.getBackground();
        g.setColor(bgColor);
        g.fillRect(0,0,size.width,size.height);


        // Fill the button
        if (button.hasFocus()) g.setColor(colors[FOCUSED][BACKGROUND]);
        else                   g.setColor(colors[UNFOCUSED][BACKGROUND]);

        if (shape == button.RECTANGLE)
        {
            g.fillRect(0,0,size.width,size.height);
        }
        else if (shape == button.ROUNDED_RECTANGLE)
        {
            g.fillRoundRect(0,0,size.width,size.height,
                            arcWidth,arcHeight);
        }


        // Render the icon (if one exists)
        //
        if (enabledIcon != null)
        {
            h = enabledIcon.getHeight(null);
            w = enabledIcon.getWidth(null);
            x = (size.width  - w)/2;
            y = (size.height - h)/2;

            // Modify the icon location based on whether 
            // the button is UP or DOWN
            if (viewState == DOWN)
            {
                x += 2;
                y += 2;
            }
        

            g.drawImage(enabledIcon, x, y, null);
        }


        // Render the state indicator (if it's a toggle button)
        buttonType = button.getButtonType();

        if (buttonType == Button.TOGGLE)
        {
            x = size.width -  25;
            y = 5;
            if (viewState == UP)
            {
                g.setColor(upColor);
            } 
            else if (viewState == DOWN)
            {
                g.setColor(downColor);
                x += 2;
                y += 2;
            }

            g.fillRect(x, y, 20, 5);
        }
    }




     /**
     * Paint the border
     *
     * @param g   The Graphics context to use
     */
    protected void paintBorder(Graphics g)
    {
        Dimension  size;


        size = button.getSize();


        g.setColor(colors[FOCUSED][BORDER]);

        if (shape == button.RECTANGLE)
        {
            g.drawRect(0,0,size.width-1,size.height-1);
        } 
        else if (shape == button.ROUNDED_RECTANGLE)
        {
            g.drawRoundRect(0,0,size.width-1,size.height-1,
                            arcWidth,arcHeight);
        }
    }



    /**
     * Paint the text
     *
     * @param g   The Graphics context to use
     */
    protected void paintText(Graphics g)
    {
        Dimension  size;
        int        h, w, x, y;


        size = button.getSize();


        
        // Short circuit if no text
        if ((text == null) || (text.equals(""))) return;
        
        // Determine the size of the text
        if (textSize == null)
        {
            fm = g.getFontMetrics();
            h = fm.getMaxAscent();
            w = fm.stringWidth(text);
            setTextSize(new Dimension(w, h));
        }
        
        // Determine the horizontal location
        if (horizontalTextPosition == button.LEFT)
        {
            x = 0;
        } 
        else if (horizontalTextPosition==button.RIGHT)
        {
            x = size.width - textSize.width;
        } 
        else 
        { 
            x = (size.width - textSize.width)/2;
        }
        
        // Determine the vertical location
        if (verticalTextPosition == button.TOP) 
        {
            y = textSize.height;
        } 
        else if (verticalTextPosition==button.BOTTOM) 
        {
            y=size.height - fm.getMaxDescent();
        } 
        else 
        {
            y = (size.height + textSize.height)/2;
        }
        
        
        // Modify the text location based on whether the button is UP or DOWN
        if (viewState == DOWN) 
        {
            x += 1;
            y -= 1;
        }
        
        
        // Render the text
        g.setFont(button.getFont());

        
        if (!button.isEnabled())  
        {
            g.setColor(colors[DISABLED][FOREGROUND]);
        } 
        else if (button.hasFocus()) 
        {
            g.setColor(colors[FOCUSED][FOREGROUND]);
        } 
        else 
        {
            g.setColor(colors[UNFOCUSED][FOREGROUND]);
        }
        
        g.drawString(text, x, y);
    }
    


    /**
     * Set the color used for different view states
     *
     * @param state   The view state (DOWN or UP)
     * @param color   The Color
     */
    public void setViewStateColor(int state, Color color)
    {
        if (state == DOWN)    downColor = color;
        else if (state == UP) upColor = color;
    }
}

        

The ButtonModel Class

javaexamples/widget/ButtonModel.java
        package widget;

/**
 * The Model (from the Model-View-Controller pattern)
 * for an AbstractButton.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ButtonModel extends AbstractButtonModel
{
    private Button        button;


    /**
     * Constructor
     *
     * @param button  The button this controller belongs to
     * @param ac      The action command associated with  this button
     */
    public ButtonModel(Button button, String ac)
    {
        super(ac);

        this.button = button;
    }



    /**
     * Get the Button associated with this model
     *
     * @return   The Button
     */
    public Widget getWidget()
    {
        return button;
    }




}
        
A "Normal" Button (cont.)

One View

javaexamples/widget/ButtonView_FLAT.java
        package widget;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;



/**
 * A "flat" View (from the Model-View-Controller pattern)
 * for a Button.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ButtonView extends AbstractButtonView
{
    protected Button           button;
    protected Color            downColor, upColor;

    /**
     * Constructor
     *
     * @param button        The button this view belongs to
     * @param width         The width of the button (in pixels)
     * @param height        The height of the button (in pixels)
     */
    public ButtonView(Button button, int width, int height)
    {
        super(width, height);
        this.button = button;

        getWidget().setSize(width, height);

        button.setSize(new Dimension(width, height));
        button.setFont(new Font("Sans Serif", Font.PLAIN, 20));

        button.setBackground(ColorScheme.FRAME);

        setColors(ColorScheme.getButtonColors());

        downColor = Color.red;
        upColor   = Color.gray;
    }



    /**
     * Get the Button associated with this view
     *
     * @return   The Button
     */
    public Widget getWidget()
    {
        return button;
    }




    /**
     * Paint this component
     *
     * @param g  The Graphics context to use
     */
    public void paint(Graphics g)
    {
        paintBackground(g);
        paintText(g);
        paintBorder(g);
    }




    /**
     * Paint the background
     *
     * @param g   The Graphics context to use
     */
    protected void paintBackground(Graphics g)
    {
        Color      bgColor;
        Dimension  size;
        int        buttonType, h, w, x, y;


        size = button.getSize();


        // Fill the background
        bgColor = button.getBackground();
        g.setColor(bgColor);
        g.fillRect(0,0,size.width,size.height);


        // Fill the button
        if (button.hasFocus()) g.setColor(colors[FOCUSED][BACKGROUND]);
        else                   g.setColor(colors[UNFOCUSED][BACKGROUND]);

        if (shape == button.RECTANGLE) 
        {
            g.fillRect(0,0,size.width,size.height);
        } 
        else if (shape == button.ROUNDED_RECTANGLE) 
        {
            g.fillRoundRect(0,0,size.width,size.height,
                            arcWidth,arcHeight);
        }


        // Render the icon (if one exists)
        //
        if (enabledIcon != null) 
        {
            h = enabledIcon.getHeight(null);
            w = enabledIcon.getWidth(null);
            x = (size.width  - w)/2;
            y = (size.height - h)/2;

            // Modify the icon location based on whether 
            // the button is UP or DOWN
            if (viewState == DOWN) 
            {
                x += 2;
                y += 2;
            }
        

            g.drawImage(enabledIcon, x, y, null);
        }


        // Render the state indicator (if it's a toggle button)
        buttonType = button.getButtonType();

        if (buttonType == Button.TOGGLE) 
        {
            x = size.width -  25;
            y = 5;

            if (viewState == UP)  
            {
                g.setColor(upColor);
            } 
            else if (viewState == DOWN) 
            {
                g.setColor(downColor);
                x += 2;
                y += 2;
            }

            g.fillRect(x, y, 20, 5);
        }
    }




     /**
     * Paint the border
     *
     * @param g   The Graphics context to use
     */
    protected void paintBorder(Graphics g)
    {
        Dimension  size;


        size = button.getSize();


        g.setColor(colors[FOCUSED][BORDER]);

        if (shape == button.RECTANGLE) 
        {
            g.drawRect(0,0,size.width-1,size.height-1);
        } 
        else if (shape == button.ROUNDED_RECTANGLE) 
        {
            g.drawRoundRect(0,0,size.width-1,size.height-1,
                            arcWidth,arcHeight);
        }
    }



    /**
     * Paint the text
     *
     * @param g   The Graphics context to use
     */
    protected void paintText(Graphics g)
    {
        Dimension  size;
        int        h, w, x, y;


        size = button.getSize();


        
        // Short circuit if no text
        if ((text == null) || (text.equals(""))) return;
        
        // Determine the size of the text
        if (textSize == null) 
        {
            fm = g.getFontMetrics();
            h = fm.getMaxAscent();
            w = fm.stringWidth(text);
            setTextSize(new Dimension(w, h));
        }
        
        // Determine the horizontal location
        if (horizontalTextPosition == button.LEFT) 
        {
            x = 0;
        } 
        else if (horizontalTextPosition==button.RIGHT) 
        {
            x = size.width - textSize.width;
        } 
        else 
        { 
            x = (size.width - textSize.width)/2;
        }
        
        // Determine the vertical location
        if (verticalTextPosition == button.TOP) 
        {
            y = textSize.height;
        } 
        else if (verticalTextPosition==button.BOTTOM) 
        {
            y=size.height - fm.getMaxDescent();
        } 
        else 
        {
            y = (size.height + textSize.height)/2;
        }
        
        
        // Modify the text location based on whether the button is UP or DOWN
        if (viewState == DOWN) 
        {
            x += 1;
            y -= 1;
        }
        
        
        // Render the text
        g.setFont(button.getFont());

        
        if (!button.isEnabled())  
        {
            g.setColor(colors[DISABLED][FOREGROUND]);
        } 
        else if (button.hasFocus()) 
        {
            g.setColor(colors[FOCUSED][FOREGROUND]);
        } 
        else 
        {
            g.setColor(colors[UNFOCUSED][FOREGROUND]);
        }
        
        g.drawString(text, x, y);
    }
    


    /**
     * Set the color used for different view states
     *
     * @param state   The view state (DOWN or UP)
     * @param color   The Color
     */
    public void setViewStateColor(int state, Color color)
    {
        if (state == DOWN)    downColor = color;
        else if (state == UP) upColor = color;
    }
}

        

Another View

javaexamples/widget/ButtonView_3D.java
        package widget;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;



/**
 * A 3-D View (from the Model-View-Controller pattern)
 * for a Button.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ButtonView extends AbstractButtonView
{
    protected Button           button;
    protected Color            darkColor, lightColor;

    /**
     * Constructor
     *
     * @param button        The button this view belongs to
     * @param width         The width of the button (in pixels)
     * @param height        The height of the button (in pixels)
     */
    public ButtonView(Button button, int width, int height)
    {
        this(width, height);

        this.button = button;
        setMinimumSize(width, height);
        button.setSize(width, height);


        colors = ColorScheme.getButtonColors();
        button.setBackground(ColorScheme.FRAME);

        lightColor = colors[UNFOCUSED][BACKGROUND].brighter();
        darkColor  = colors[UNFOCUSED][BACKGROUND].darker();
    }


    /**
     * Constructor
     *
     * @param width         The width of the button (in pixels)
     * @param height        The height of the button (in pixels)
     */
    public ButtonView(int width, int height)
    {
        super(width, height);
    }



    /**
     * Get the Button associated with this view
     *
     * @return   The Button
     */
    public Widget getWidget()
    {
        return button;
    }




    /**
     * Paint this component
     *
     * @param g  The Graphics context to use
     */
    public void paint(Graphics g)
    {
        paintBackground(g);
        paintText(g);
        paintBorder(g);
    }




    /**
     * Paint the background
     *
     * @param g   The Graphics context to use
     */
    protected void paintBackground(Graphics g)
    {
        Color      bgColor;
        Dimension  size;
        int        h, w, x, y;



        size = button.getSize();

        // Fill the background
        g.setColor(button.getBackground());
        g.fillRect(0,0,size.width,size.height);


        // Fill the button
        if (button.hasFocus()) g.setColor(colors[FOCUSED][BACKGROUND]);
        else                   g.setColor(colors[UNFOCUSED][BACKGROUND]);

        if (shape == button.RECTANGLE) 
        {
            g.fillRect(0,0,size.width,size.height);
        } 
        else if (shape == button.ROUNDED_RECTANGLE) 
        {
            g.fillRoundRect(0,0,size.width,size.height,
                            arcWidth,arcHeight);
        }


        // Render the icon (if one exists)
        //
        if (enabledIcon != null) 
        {
            h = enabledIcon.getHeight(null);
            w = enabledIcon.getWidth(null);
            x = (size.width  - w)/2;
            y = (size.height - h)/2;

            // Modify the icon location based on whether 
            // the button is UP or DOWN
            if (viewState == DOWN) 
            {
                x += 2;
                y += 2;
            }
            g.drawImage(enabledIcon, x, y, null);
        }

    }




    /**
     * Paint the border
     *
     * @param g   The Graphics context to use
     */
    protected void paintBorder(Graphics g)
    {
        Color       brColor, ulColor;
        Dimension   size;



        size = button.getSize();

        brColor = darkColor;
        ulColor = darkColor;

        // Determine the border colors
        if (viewState == UP) 
        {
            brColor = darkColor;
            ulColor = lightColor;
        } 
        else if (viewState == DOWN) 
        {
            brColor = lightColor;
            ulColor = darkColor;
        } 



        // Draw the border
        if (shape == button.RECTANGLE) 
        {
            g.setColor(ulColor);
            g.drawLine(0,0,size.width-1,0);
            g.drawLine(0,0,0,size.height-1);
            
            g.setColor(brColor);
            g.drawLine(0,size.height-1,size.width-1,size.height-1);
            g.drawLine(size.width-1,0,size.width-1,size.height-1);
        } 
        else if (shape == button.ROUNDED_RECTANGLE) 
        {
            g.setColor(ulColor);
            g.drawLine(arcWidth/2,0,size.width-1-arcWidth/2,0);
            g.drawLine(0,arcHeight/2,0,size.height-1-arcHeight/2);
            // Upper left
            g.drawArc(0,0,arcWidth,arcHeight,90,90);

            // Upper right
            g.drawArc(size.width-1-arcWidth,0,arcWidth,arcHeight,45,45);
            // Lower left
            g.drawArc(0,size.height-1-arcHeight,arcWidth,arcHeight,180,45);



            g.setColor(brColor);
            g.drawLine(arcWidth/2,size.height-1,
                       size.width-1-arcWidth/2,size.height-1);
            g.drawLine(size.width-1,arcHeight/2,
                       size.width-1,size.height-1-arcHeight/2);
            //Upper right
            g.drawArc(size.width-1-arcWidth,0,
                      arcWidth,arcHeight,0,45);
            // Lower left
            g.drawArc(0,size.height-1-arcHeight,
                      arcWidth,arcHeight,225,45);
            // Lower right
            g.drawArc(size.width-1-arcWidth,size.height-1-arcHeight,
                      arcWidth,arcHeight,270,90);
        }
    }



    /**
     * Paint the text
     *
     * @param g   The Graphics context to use
     */
    protected void paintText(Graphics g)
    {
        Dimension   size;
        int         h, w, x, y;


        size = button.getSize();

        // Short circuit if no text
        if ((text == null) || (text.equals(""))) return;

        // Determine the size of the text
        if (textSize == null) 
        {
            fm = g.getFontMetrics();
            h = fm.getMaxAscent();
            w = fm.stringWidth(text);
            setTextSize(new Dimension(w, h));
        }

        // Determine the horizontal location
        if (horizontalTextPosition == button.LEFT) 
        {
            x = 0;
        } 
        else if (horizontalTextPosition==button.RIGHT) 
        {
            x = size.width - textSize.width;
        } 
        else 
        { 
            x = (size.width - textSize.width)/2;
        }
        
        // Determine the vertical location
        if (verticalTextPosition == button.TOP) 
        {
            y = textSize.height;
        } 
        else if (verticalTextPosition==button.BOTTOM) 
        {
            y=size.height - fm.getMaxDescent();
        } 
        else 
        {
            y = (size.height + textSize.height)/2;
        }

        
        // Modify the text location based on whether the button is UP or DOWN
        if (viewState == DOWN) 
        {
            x += 2;
            y += 2;
        }
        

        // Render the text
        g.setFont(button.getFont());
        g.setColor(colors[FOCUSED][FOREGROUND]);
        g.drawString(text, x, y);
    }





}