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

com.alee.managers.style.skin.web.WebPopupMenuPainter Maven / Gradle / Ivy

There is a newer version: 1.2.14
Show newest version
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.managers.style.skin.web;

import com.alee.laf.menu.*;

import javax.swing.*;
import java.awt.*;

/**
 * Base painter for JPopupMenu component.
 * It is used as WebPopupMenuUI default styling.
 *
 * @author Mikle Garin
 */

public class WebPopupMenuPainter extends WebPopupPainter implements PopupMenuPainter
{
    /**
     * todo 1. Incorrect menu placement when corner is off (spacing == shade)
     * todo 2. When using popupMenuWay take invoker shade into account (if its UI has one -> ShadeProvider interface)
     * todo 3. Add left/right corners display support
     */

    /**
     * Style settings.
     */
    protected int menuSpacing = WebPopupMenuStyle.menuSpacing;
    protected boolean fixLocation = WebPopupMenuStyle.fixLocation;

    /**
     * Runtime variables.
     */
    protected PopupMenuWay popupMenuWay = null;
    protected PopupMenuType popupMenuType = null;

    /**
     * Returns spacing between popup menus.
     *
     * @return spacing between popup menus
     */
    public int getMenuSpacing ()
    {
        return menuSpacing;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setMenuSpacing ( final int spacing )
    {
        this.menuSpacing = spacing;
    }

    /**
     * Returns whether should fix initial popup menu location or not.
     *
     * @return true if should fix initial popup menu location, false otherwise
     */
    public boolean isFixLocation ()
    {
        return fixLocation;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setFixLocation ( final boolean fix )
    {
        this.fixLocation = fix;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setPopupMenuWay ( final PopupMenuWay way )
    {
        this.popupMenuWay = way;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setPopupMenuType ( final PopupMenuType type )
    {
        this.popupMenuType = type;
        if ( popupMenuType == PopupMenuType.menuBarSubMenu )
        {
            setPopupStyle ( PopupStyle.simple );
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Insets getMargin ( final E c )
    {
        final Insets margin = super.getMargin ( c );
        margin.top += round;
        margin.bottom += round;
        return margin;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void paintTransparentPopup ( final Graphics2D g2d, final E popupMenu )
    {
        final Dimension menuSize = popupMenu.getSize ();

        // Painting shade
        paintShade ( g2d, popupMenu, menuSize );

        // Painting background
        paintBackground ( g2d, popupMenu, menuSize );

        // Painting dropdown corner fill
        // This is a specific for WebPopupMenuUI feature
        paintDropdownCornerFill ( g2d, popupMenu, menuSize );

        // Painting border
        paintBorder ( g2d, popupMenu, menuSize );
    }

    /**
     * Paints dropdown-styled popup menu corner fill if menu item near it is selected.
     *
     * @param g2d       graphics context
     * @param popupMenu popup menu
     * @param menuSize  menu size
     */
    protected void paintDropdownCornerFill ( final Graphics2D g2d, final E popupMenu, final Dimension menuSize )
    {
        // Checking whether corner should be filled or not
        if ( popupStyle == PopupStyle.dropdown && round == 0 )
        {
            // Check that menu item is attached to menu side
            final boolean top = cornerSide == TOP;
            final WebPopupMenuUI pmui = ( WebPopupMenuUI ) popupMenu.getUI ();
            final boolean stick = top ? ( pmui.getMargin ().top + margin.top == 0 ) : ( pmui.getMargin ().bottom + margin.bottom == 0 );
            if ( stick )
            {
                // Checking that we can actually retrieve what item wants to fill corner with
                final int zIndex = top ? 0 : popupMenu.getComponentCount () - 1;
                final Component component = popupMenu.getComponent ( zIndex );
                if ( component instanceof JMenuItem )
                {
                    final JMenuItem menuItem = ( JMenuItem ) component;
                    if ( menuItem.isEnabled () && ( menuItem.getModel ().isArmed () || menuItem.isSelected () ) )
                    {
                        // Filling corner properly
                        if ( menuItem.getUI () instanceof WebMenuUI )
                        {
                            final WebMenuUI ui = ( WebMenuUI ) menuItem.getUI ();
                            g2d.setPaint ( top ? ui.getNorthCornerFill () : ui.getSouthCornerFill () );
                            g2d.fill ( getDropdownCornerShape ( popupMenu, menuSize, true ) );
                        }
                        else if ( menuItem.getUI () instanceof WebMenuItemUI )
                        {
                            final WebMenuItemUI ui = ( WebMenuItemUI ) menuItem.getUI ();
                            g2d.setPaint ( top ? ui.getNorthCornerFill () : ui.getSouthCornerFill () );
                            g2d.fill ( getDropdownCornerShape ( popupMenu, menuSize, true ) );
                        }
                    }
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Point preparePopupMenu ( final E popupMenu, final Component invoker, int x, int y )
    {
        // Default corner position
        final boolean ltr = invoker.getComponentOrientation ().isLeftToRight ();
        relativeCorner = ltr ? 0 : Integer.MAX_VALUE;

        // Updating popup location according to popup menu UI settings
        if ( invoker != null )
        {
            // Position calculations constants
            final Point los = invoker.isShowing () ? invoker.getLocationOnScreen () : null;
            final boolean fixLocation = this.fixLocation && invoker.isShowing ();
            final int sideWidth = getSideWidth ();

            if ( invoker instanceof JMenu )
            {
                if ( invoker.getParent () instanceof JPopupMenu )
                {
                    // Displaying simple-styled sub-menu
                    // It is displayed to left or right of the menu item
                    setPopupStyle ( PopupStyle.simple );
                    if ( fixLocation )
                    {
                        // Invoker X-location on screen also works as orientation indicator, so we don't need to check it here
                        x += ( los.x <= x ? -1 : 1 ) * ( transparent ? sideWidth - menuSpacing : -menuSpacing );
                        y += ( los.y <= y ? -1 : 1 ) * ( transparent ? sideWidth + 1 + round : round );
                    }
                }
                else if ( !WebPopupMenuStyle.dropdownStyleForMenuBar )
                {
                    // Displaying simple-styled top-level menu
                    // It is displayed below or above the menu bar
                    setPopupStyle ( PopupStyle.simple );
                    if ( fixLocation )
                    {
                        // Invoker X-location on screen also works as orientation indicator, so we don't need to check it here
                        x += ( los.x <= x ? -1 : 1 ) * ( transparent ? sideWidth - menuSpacing : -menuSpacing );
                        y -= transparent ? sideWidth + round + 1 : round;
                    }
                }
                else
                {
                    // Displaying dropdown-styled top-level menu
                    // It is displayed below or above the menu bar
                    setPopupStyle ( PopupStyle.dropdown );
                    cornerSide = los.y <= y ? TOP : BOTTOM;
                    if ( fixLocation )
                    {
                        // Invoker X-location on screen also works as orientation indicator, so we don't need to check it here
                        x += ( los.x <= x ? -1 : 1 ) * ( transparent ? sideWidth : 0 );
                        y += ( los.y <= y ? -1 : 1 ) * ( transparent ? sideWidth - cornerWidth : 0 );
                    }
                    relativeCorner = los.x + invoker.getWidth () / 2 - x;
                }
            }
            else
            {
                final boolean dropdown = popupStyle == PopupStyle.dropdown;
                if ( invoker instanceof JComboBox && popupMenu.getName ().equals ( "ComboPopup.popup" ) )
                {
                    cornerSide = los.y <= y ? TOP : BOTTOM;
                    if ( fixLocation )
                    {
                        x += transparent ? -sideWidth : 0;
                        if ( cornerSide == TOP )
                        {
                            y -= transparent ? ( sideWidth - ( dropdown ? cornerWidth : 0 ) ) : 0;
                        }
                        else
                        {
                            y -= invoker.getHeight () + ( transparent ? ( sideWidth - ( dropdown ? cornerWidth : 0 ) ) : 0 );
                        }
                    }
                    relativeCorner = los.x + invoker.getWidth () / 2 - x;
                }
                else
                {
                    if ( fixLocation )
                    {
                        // Applying new location according to specified popup menu way
                        if ( popupMenuWay != null )
                        {
                            final Dimension ps = popupMenu.getPreferredSize ();
                            final Dimension is = invoker.getSize ();
                            final int cornerShear = dropdown ? sideWidth - cornerWidth : 0;
                            switch ( popupMenuWay )
                            {
                                case aboveStart:
                                {
                                    x = ( ltr ? los.x : los.x + is.width - ps.width ) +
                                            ( transparent ? ( ltr ? -sideWidth : sideWidth ) : 0 );
                                    y = los.y - ps.height + cornerShear;
                                    relativeCorner = ltr ? 0 : Integer.MAX_VALUE;
                                    break;
                                }
                                case aboveMiddle:
                                {
                                    x = los.x + is.width / 2 - ps.width / 2;
                                    y = los.y - ps.height + cornerShear;
                                    relativeCorner = los.x + invoker.getWidth () / 2 - x;
                                    break;
                                }
                                case aboveEnd:
                                {
                                    x = ( ltr ? los.x + is.width - ps.width : los.x ) +
                                            ( transparent ? ( ltr ? sideWidth : -sideWidth ) : 0 );
                                    y = los.y - ps.height + cornerShear;
                                    relativeCorner = ltr ? Integer.MAX_VALUE : 0;
                                    break;
                                }
                                case belowStart:
                                {
                                    x = ( ltr ? los.x : los.x + is.width - ps.width ) +
                                            ( transparent ? ( ltr ? -sideWidth : sideWidth ) : 0 );
                                    y = los.y + is.height - cornerShear;
                                    relativeCorner = ltr ? 0 : Integer.MAX_VALUE;
                                    break;
                                }
                                case belowMiddle:
                                {
                                    x = los.x + is.width / 2 - ps.width / 2;
                                    y = los.y + is.height - cornerShear;
                                    relativeCorner = los.x + invoker.getWidth () / 2 - x;
                                    break;
                                }
                                case belowEnd:
                                {
                                    x = ( ltr ? los.x + is.width - ps.width : los.x ) +
                                            ( transparent ? ( ltr ? sideWidth : -sideWidth ) : 0 );
                                    y = los.y + is.height - cornerShear;
                                    relativeCorner = ltr ? Integer.MAX_VALUE : 0;
                                    break;
                                }
                            }
                            cornerSide = popupMenuWay.getCornerSide ();
                        }
                    }
                    //                    else if ( cornerAlignment != -1 )
                    //                    {
                    //                        final Dimension ps = popupMenu.getPreferredSize ();
                    //                        final Dimension is = invoker.getSize ();
                    //                    }
                }
            }
        }

        // Resetting preferred popup menu display way
        popupMenuWay = null;

        return new Point ( x, y );
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy