All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.extended.button.SplitButtonPainter Maven / Gradle / Ivy

The newest version!
package com.alee.extended.button;

import com.alee.api.jdk.Objects;
import com.alee.laf.WebLookAndFeel;
import com.alee.laf.button.AbstractButtonPainter;
import com.alee.managers.icon.Icons;
import com.alee.painter.decoration.DecorationState;
import com.alee.painter.decoration.IDecoration;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;

/**
 * Basic painter for {@link WebSplitButton} component.
 * It is used as {@link WSplitButtonUI} default painter.
 *
 * @param  component type
 * @param  component UI type
 * @param  decoration type
 * @author Mikle Garin
 */
public class SplitButtonPainter, D extends IDecoration>
        extends AbstractButtonPainter implements ISplitButtonPainter
{
    /**
     * todo 1. Replace custom split button painting with button component
     * todo 2. Replace custom separator painting with separator component
     * todo 3. Add appropriate border to incorporate two extra components
     */

    /**
     * Style settings.
     * todo This should be in separator & button styles
     */
    protected Color splitLineColor;
    protected Color splitLineDisabledColor;
    protected int menuIconGap;
    protected int contentGap;

    /**
     * Listeners.
     */
    protected transient MouseAdapter menuButtonTracker;
    protected transient PropertyChangeListener popupMenuPropertyChangeListener;

    /**
     * Runtime variables.
     */
    protected transient boolean onMenu;

    @Override
    protected void installPropertiesAndListeners ()
    {
        super.installPropertiesAndListeners ();
        installMenuButtonListeners ();
    }

    @Override
    protected void uninstallPropertiesAndListeners ()
    {
        uninstallMenuButtonListeners ();
        super.uninstallPropertiesAndListeners ();
    }

    /**
     * Installs menu button mouseover listener.
     */
    protected void installMenuButtonListeners ()
    {
        // todo Should be replaced with state-based settings
        onMenu = false;
        menuButtonTracker = new MouseAdapter ()
        {
            @Override
            public void mouseMoved ( final MouseEvent e )
            {
                // Saving old menu button mouseover state
                final boolean wasOnMenu = onMenu;

                // Updating menu button mouseover state
                onMenu = getMenuButtonHitbox ( component ).contains ( e.getPoint () );

                // Repainting button if state has changed
                if ( wasOnMenu != onMenu )
                {
                    repaint ();
                }
            }

            @Override
            public void mouseExited ( final MouseEvent e )
            {
                // Saving old menu button mouseover state
                final boolean wasOnMenu = onMenu;

                // Resetting menu button mouseover state
                onMenu = false;

                // Repainting button if state has changed
                if ( wasOnMenu != onMenu )
                {
                    repaint ();
                }
            }
        };
        component.addMouseListener ( menuButtonTracker );
        component.addMouseMotionListener ( menuButtonTracker );

        popupMenuPropertyChangeListener = new PropertyChangeListener ()
        {
            @Override
            public void propertyChange ( final PropertyChangeEvent event )
            {
                if ( Objects.equals ( event.getPropertyName (), WebLookAndFeel.VISIBLE_PROPERTY ) )
                {
                    updateDecorationState ();
                }
            }
        };
        installPopupMenuPropertyChangeListener ( component.getPopupMenu () );
    }

    /**
     * Uninstalls menu button mouseover listener.
     */
    protected void uninstallMenuButtonListeners ()
    {
        uninstallPopupMenuPropertyChangeListener ( component.getPopupMenu () );
        popupMenuPropertyChangeListener = null;
        component.removeMouseMotionListener ( menuButtonTracker );
        component.removeMouseListener ( menuButtonTracker );
        menuButtonTracker = null;
        onMenu = false;
    }

    @Override
    protected void propertyChanged ( final String property, final Object oldValue, final Object newValue )
    {
        // Perform basic actions on property changes
        super.propertyChanged ( property, oldValue, newValue );

        // Updating border on menu icon change
        if ( Objects.equals ( property, WebSplitButton.MENU_ICON_PROPERTY ) )
        {
            updateBorder ();
        }

        // Updating border on popup menu change
        if ( Objects.equals ( property, WebSplitButton.POPUP_MENU_PROPERTY ) )
        {
            uninstallPopupMenuPropertyChangeListener ( ( JPopupMenu ) oldValue );
            installPopupMenuPropertyChangeListener ( ( JPopupMenu ) newValue );
        }
    }

    /**
     * Installs {@link PropertyChangeListener} into {@link JPopupMenu} of the {@link WebSplitButton}.
     *
     * @param popupMenu {@link JPopupMenu} to install {@link PropertyChangeListener} into
     */
    protected void installPopupMenuPropertyChangeListener ( final JPopupMenu popupMenu )
    {
        if ( popupMenu != null )
        {
            popupMenu.addPropertyChangeListener ( popupMenuPropertyChangeListener );
        }
    }

    /**
     * Uninstalls {@link PropertyChangeListener} from {@link JPopupMenu} of the {@link WebSplitButton}.
     *
     * @param popupMenu {@link JPopupMenu} to uninstall {@link PropertyChangeListener} from
     */
    protected void uninstallPopupMenuPropertyChangeListener ( final JPopupMenu popupMenu )
    {
        if ( popupMenu != null )
        {
            popupMenu.removePropertyChangeListener ( popupMenuPropertyChangeListener );
        }
    }

    @Override
    public List getDecorationStates ()
    {
        final List states = super.getDecorationStates ();
        states.add ( component.isPopupMenuVisible () ? DecorationState.popupVisible : DecorationState.popupHidden );
        return states;
    }

    /**
     * Returns gap between menu icon and menu button part sides.
     *
     * @return gap between menu icon and menu button part sides
     */
    public int getMenuIconGap ()
    {
        return menuIconGap;
    }

    /**
     * Sets gap between menu icon and menu button part sides
     *
     * @param menuIconGap gap between menu icon and menu button part sides
     */
    public void setMenuIconGap ( final int menuIconGap )
    {
        this.menuIconGap = menuIconGap;
    }

    /**
     * Returns gap between menu button part and button content.
     *
     * @return gap between menu button part and button content
     */
    public int getContentGap ()
    {
        return contentGap;
    }

    /**
     * Sets gap between menu button part and button content.
     *
     * @param contentGap gap between menu button part and button content
     */
    public void setContentGap ( final int contentGap )
    {
        this.contentGap = contentGap;
    }

    @Override
    public boolean isOnMenu ()
    {
        return onMenu;
    }

    @Override
    protected Insets getBorder ()
    {
        final Insets result;
        final Insets border = super.getBorder ();
        final Icon menuIcon = getMenuIcon ();
        final int extra = contentGap + 1 + menuIconGap + menuIcon.getIconWidth () + menuIconGap;
        if ( border != null )
        {
            result = new Insets ( border.top, border.left, border.bottom, border.right + extra );
        }
        else
        {
            result = new Insets ( 0, 0, 0, extra );
        }
        return result;
    }

    @Override
    protected void paintContent ( final Graphics2D g2d, final Rectangle bounds, final C c, final U ui )
    {
        // Painting menu button
        paintMenuButton ( g2d, bounds, c );
    }

    /**
     * Paints menu button.
     *
     * @param g2d    {@link Graphics2D}
     * @param bounds painting bounds
     * @param c      {@link WebSplitButton}
     */
    protected void paintMenuButton ( final Graphics2D g2d, final Rectangle bounds, final C c )
    {
        // Painting menu button icon
        final Icon menuIcon = getMenuIcon ();
        final Rectangle br = getMenuButtonBounds ( bounds, c );
        final int ix = br.x + br.width / 2 - menuIcon.getIconWidth () / 2;
        final int iy = br.y + br.height / 2 - menuIcon.getIconHeight () / 2;
        menuIcon.paintIcon ( component, g2d, ix, iy );

        // Painting menu button side line
        final Rectangle lr = getMenuButtonLineBounds ( bounds, c );
        g2d.setPaint ( c.isEnabled () ? splitLineColor : splitLineDisabledColor );
        g2d.drawLine ( lr.x, lr.y, lr.x, lr.y + lr.height );
    }

    /**
     * Returns bounds of the menu button part.
     *
     * @param b painting bounds
     * @param c {@link WebSplitButton}
     * @return bounds of the menu button part
     */
    protected Rectangle getMenuButtonBounds ( final Rectangle b, final C c )
    {
        final Insets i = c.getInsets ();
        final int iconWidth = getMenuIcon ().getIconWidth ();
        final int x = b.x + ( ltr ? b.width - i.right + contentGap + 1 + menuIconGap :
                i.left - contentGap - 1 - menuIconGap - iconWidth );
        return new Rectangle ( x, b.y + i.top, iconWidth, b.height - i.top - i.bottom );
    }

    /**
     * Returns bounds of the menu line part.
     *
     * @param b painting bounds
     * @param c {@link WebSplitButton}
     * @return bounds of the menu line part
     */
    protected Rectangle getMenuButtonLineBounds ( final Rectangle b, final C c )
    {
        final Insets i = c.getInsets ();
        final int x = b.x + ( ltr ? b.width - i.right + contentGap : i.left - contentGap );
        return new Rectangle ( x, b.y + i.top, 1, b.height - i.top - i.bottom - 1 );
    }

    /**
     * Returns menu button part hitbox.
     *
     * @param c {@link WebSplitButton}
     * @return menu button part hitbox
     */
    protected Rectangle getMenuButtonHitbox ( final C c )
    {
        final Insets i = c.getInsets ();
        return new Rectangle ( ltr ? c.getWidth () - i.right : 0, 0, ltr ? i.right : i.left, c.getHeight () );
    }

    /**
     * Returns menu {@link Icon}.
     *
     * @return menu {@link Icon}
     */
    protected Icon getMenuIcon ()
    {
        final Icon customIcon = component.getMenuIcon ();
        return customIcon != null ? customIcon : Icons.downSmall;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy