com.alee.managers.style.skin.web.WebPopupMenuPainter Maven / Gradle / Ivy
/*
* 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