com.alee.extended.menu.WebDynamicMenu 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.extended.menu;
import com.alee.extended.image.WebImage;
import com.alee.global.StyleConstants;
import com.alee.laf.rootpane.WebWindow;
import com.alee.managers.focus.GlobalFocusListener;
import com.alee.utils.GeometryUtils;
import com.alee.utils.GraphicsUtils;
import com.alee.utils.swing.WebHeavyWeightPopup;
import com.alee.utils.swing.WebTimer;
import com.alee.utils.swing.WindowFollowAdapter;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
/**
* Custom dynamic menu with pretty display/hide animations.
*
* @author Mikle Garin
*/
public class WebDynamicMenu extends WebHeavyWeightPopup
{
/**
* todo 1. Add sliding down vertical menu (with text and selection background)
*/
/**
* Radius of the menu background in px.
*/
protected int radius;
/**
* First element position angle in degrees.
* Counted from the circle top-most point.
*/
protected double startingAngle;
/**
* Angle which is available for menu elements.
* It is 360 degress by default (whole circle available).
*/
protected double angleRange;
/**
* Single animation step progress.
* Making this value bigger will speedup the animation, reduce required resources but will also make it less soft.
*/
protected float stepProgress;
/**
* Menu animation type.
*/
protected DynamicMenuType type;
/**
* Menu hide animation type.
*/
protected DynamicMenuType hideType;
/**
* Menu animation direction.
*/
protected boolean clockwise;
/**
* Menu items list.
*/
protected List items = new ArrayList ();
/**
* Menu display progress.
*/
protected float currentProgress = 0f;
/**
* Actions synchronization object.
*/
protected final Object sync = new Object ();
/**
* Animation timer.
*/
protected WebTimer animator = null;
/**
* Invoker window follow adapter.
*/
protected WindowFollowAdapter followAdapter;
/**
* Whether menu is being displayed or not.
*/
protected boolean displaying = false;
/**
* Whether menu is being hidden or not.
*/
protected boolean hiding = false;
/**
* Index of menu item that caused menu to close.
* This might affect the hiding animation.
*/
protected int hidingCause = -1;
/**
* Custom global mouse listener that closes menu.
*/
protected AWTEventListener mouseListener;
/**
* Custom global focus listener that closes menu.
*/
protected GlobalFocusListener focusListener;
/**
* Listeners synchronization object.
*/
protected final Object lsync = new Object ();
/**
* Actions to perform on full display.
*/
protected final List onFullDisplay = new ArrayList ();
/**
* Actions to perform on full hide.
*/
protected final List onFullHide = new ArrayList ();
/**
* Constructs new dynamic menu.
*/
public WebDynamicMenu ()
{
super ( "transparent", new DynamicMenuLayout () );
setRadius ( 60 );
setStartingAngle ( 0 );
setAngleRange ( 360 );
setStepProgress ( 0.04f );
setType ( DynamicMenuType.shutter );
setHideType ( null );
setClockwise ( true );
setWindowOpaque ( false );
setFollowInvoker ( true );
}
public int getRadius ()
{
return radius;
}
public void setRadius ( final int radius )
{
this.radius = radius;
}
public double getStartingAngle ()
{
return startingAngle;
}
public void setStartingAngle ( final double startingAngle )
{
this.startingAngle = startingAngle;
}
public double getAngleRange ()
{
return angleRange;
}
public void setAngleRange ( final double angleRange )
{
this.angleRange = angleRange;
}
public float getStepProgress ()
{
return stepProgress;
}
public void setStepProgress ( final float stepProgress )
{
this.stepProgress = stepProgress;
}
public DynamicMenuType getType ()
{
return type;
}
public void setType ( final DynamicMenuType type )
{
this.type = type;
}
public DynamicMenuType getHideType ()
{
return hideType != null ? hideType : type;
}
public void setHideType ( final DynamicMenuType hideType )
{
this.hideType = hideType;
}
public boolean isClockwise ()
{
return clockwise;
}
public void setClockwise ( final boolean clockwise )
{
this.clockwise = clockwise;
}
public List getItems ()
{
return items;
}
public WebImage addItem ( final ImageIcon icon )
{
return addItem ( icon, null );
}
public WebImage addItem ( final ImageIcon icon, final ActionListener action )
{
return addItem ( new WebDynamicMenuItem ( icon, action ) );
}
public WebImage addItem ( final WebDynamicMenuItem item )
{
items.add ( item );
final WebImage menuItem = new WebImage ( item.getIcon () )
{
@Override
protected void paintComponent ( final Graphics g )
{
if ( item.isDrawBorder () )
{
final Graphics2D g2d = ( Graphics2D ) g;
final Object aa = GraphicsUtils.setupAntialias ( g2d );
g2d.setPaint ( isEnabled () ? item.getBorderColor () : item.getDisabledBorderColor () );
g2d.fillOval ( 0, 0, getWidth (), getHeight () );
g2d.setColor ( Color.WHITE );
g2d.fillOval ( 2, 2, getWidth () - 4, getHeight () - 4 );
GraphicsUtils.restoreAntialias ( g2d, aa );
}
super.paintComponent ( g );
}
};
menuItem.setEnabled ( item.getAction () != null );
menuItem.setMargin ( item.getMargin () );
menuItem.addMouseListener ( new MouseAdapter ()
{
@Override
public void mousePressed ( final MouseEvent e )
{
final ActionListener action = item.getAction ();
if ( action != null )
{
action.actionPerformed ( new ActionEvent ( e.getSource (), 0, "Action performed" ) );
}
hideMenu ( getComponentZOrder ( menuItem ) );
}
} );
add ( menuItem );
return menuItem;
}
public float getCurrentProgress ()
{
return currentProgress;
}
public WebTimer getAnimator ()
{
return animator;
}
public boolean isDisplaying ()
{
return displaying;
}
public boolean isHiding ()
{
return hiding;
}
/**
* Returns index of menu item that caused menu to close.
*
* @return index of menu item that caused menu to close
*/
public int getHidingCause ()
{
return hidingCause;
}
/**
* Displays dynamic menu for the specified invoker location.
*
* @param invoker menu invoker
* @param location menu location
*/
public void showMenu ( final Component invoker, final Point location )
{
synchronized ( sync )
{
if ( displaying || window != null || getComponentCount () == 0 )
{
return;
}
displaying = true;
// Stop displaying if we were
if ( hiding )
{
if ( animator != null )
{
animator.stop ();
}
hiding = false;
hidingCause = -1;
}
// Creating menu and displaying it
displayMenuWindow ( invoker, location );
// Displaying menu softly
animator = WebTimer.repeat ( StyleConstants.fastAnimationDelay, 0L, new ActionListener ()
{
@Override
public void actionPerformed ( final ActionEvent e )
{
synchronized ( sync )
{
if ( currentProgress < 1f )
{
currentProgress = Math.min ( currentProgress + stepProgress, 1f );
revalidate ();
setWindowOpacity ( currentProgress );
}
else
{
animator.stop ();
animator = null;
displaying = false;
fullyDisplayed ();
}
}
}
} );
}
}
/**
* Creates new menu window.
*
* @param invoker menu invoker
* @param location menu location
*/
protected void displayMenuWindow ( final Component invoker, final Point location )
{
// Updating opacity
setWindowOpacity ( currentProgress );
// Displaying popup
final Dimension size = getPreferredSize ();
showPopup ( invoker, location.x - size.width / 2, location.y - size.height / 2 );
}
/**
* {@inheritDoc}
*/
@Override
public WebHeavyWeightPopup hidePopup ()
{
hideMenu ();
return this;
}
/**
* Hides dynamic menu.
*/
public void hideMenu ()
{
hideMenu ( -1 );
}
/**
* Hides dynamic menu.
*
* @param index menu item that forced menu to hide
*/
public void hideMenu ( final int index )
{
synchronized ( sync )
{
if ( hiding )
{
return;
}
hiding = true;
hidingCause = index;
// Stop displaying if we were
if ( displaying )
{
if ( animator != null )
{
animator.stop ();
}
displaying = false;
}
// Hiding menu softly
animator = WebTimer.repeat ( StyleConstants.fastAnimationDelay, 0L, new ActionListener ()
{
@Override
public void actionPerformed ( final ActionEvent e )
{
synchronized ( sync )
{
if ( currentProgress > 0f )
{
currentProgress = Math.max ( currentProgress - stepProgress, 0f );
revalidate ();
setWindowOpacity ( currentProgress );
}
else
{
destroyMenuWindow ();
animator.stop ();
animator = null;
hiding = false;
hidingCause = -1;
fullyHidden ();
}
}
}
} );
}
}
/**
* Disposes old menu window.
*/
protected void destroyMenuWindow ()
{
// Disposing of menu window
super.hidePopup ();
}
/**
* Performs provided action when menu is fully displayed.
* Might be useful to display sub-menus or perform some other actions.
* Be aware that this action will be performed only once and then removed from the actions list.
*
* @param action action to perform
*/
public void onFullDisplay ( final Runnable action )
{
synchronized ( lsync )
{
if ( !isShowing () || isDisplaying () )
{
onFullDisplay.add ( action );
}
else if ( isShowing () && !isHiding () )
{
action.run ();
}
}
}
/**
* Performs actions waiting for menu display animation finish.
*/
public void fullyDisplayed ()
{
synchronized ( lsync )
{
for ( final Runnable runnable : onFullDisplay )
{
runnable.run ();
}
onFullDisplay.clear ();
}
}
/**
* Performs provided action when menu is fully hidden.
* Be aware that this action will be performed only once and then removed from the actions list.
*
* @param action action to perform
*/
public void onFullHide ( final Runnable action )
{
synchronized ( lsync )
{
if ( isShowing () )
{
onFullHide.add ( action );
}
else
{
action.run ();
}
}
}
/**
* Performs actions waiting for menu hide animation finish.
*/
public void fullyHidden ()
{
synchronized ( lsync )
{
for ( final Runnable runnable : onFullHide )
{
runnable.run ();
}
onFullHide.clear ();
}
}
/**
* Returns menu item angle relative to vertical axis.
*
* @param item menu item
* @return menu item center point angle
*/
public double getItemAngle ( final Component item )
{
return getItemAngle ( getComponentZOrder ( item ) );
}
/**
* Returns menu item angle relative to vertical axis.
*
* @param index menu item index
* @return menu item center point angle
*/
public double getItemAngle ( final int index )
{
return GeometryUtils.toDegrees ( getActualLayout ().getItemAngle ( this, index ) );
}
/**
* Returns actual menu layout manager.
*
* @return actual menu layout manager
*/
public DynamicMenuLayout getActualLayout ()
{
return ( DynamicMenuLayout ) getLayout ();
}
/**
* Returns whether any dynamic menu is currently displayed or not.
*
* @return true if any dynamic menu is currently displayed, false otherwise
*/
public static boolean isAnyDynamicMenuDisplayed ()
{
for ( final Window window : Window.getWindows () )
{
if ( window.isShowing () && window instanceof WebWindow &&
( ( WebWindow ) window ).getContentPane () instanceof WebDynamicMenu )
{
return true;
}
}
return false;
}
/**
* Hides all visible dynamic menus.
*/
public static void hideAllDynamicMenus ()
{
for ( final Window window : Window.getWindows () )
{
if ( window.isShowing () && window instanceof WebWindow &&
( ( WebWindow ) window ).getContentPane () instanceof WebDynamicMenu )
{
final WebDynamicMenu menu = ( WebDynamicMenu ) ( ( WebWindow ) window ).getContentPane ();
menu.hideMenu ();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy