/* * 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.layout.AbstractLayoutManager; import com.alee.utils.GeometryUtils; import com.alee.utils.MathUtils; import com.alee.utils.SwingUtils; import java.awt.*; /** * Custom dynamic menu layout. * This layout takes care of each menu item position including positioning during animation. * * @author Mikle Garin */ public class DynamicMenuLayout extends AbstractLayoutManager { /** * {@inheritDoc} */ @Override public void layoutContainer ( final Container parent ) { final WebDynamicMenu menu = ( WebDynamicMenu ) parent; final float displayProgress = MathUtils.sqr ( menu.getCurrentProgress () ); final Dimension max = SwingUtils.max ( menu.getComponents () ); final Point center = new Point ( menu.getRadius (), menu.getRadius () ); final int itemSide = Math.max ( max.width, max.height ); final int hidingCause = menu.getHidingCause (); final int hidingCauseIndex = hidingCause != -1 ? hidingCause : 0; final int radius = menu.getRadius () - itemSide / 2; final double singleComponentSpacing = getSingleComponentSpacing ( menu ); final double startingAngle = getStartingAngle ( menu ); final DynamicMenuType type = !menu.isHiding () ? menu.getType () : menu.getHideType (); final int cwSign = menu.isClockwise () ? 1 : -1; final int itemsCount = menu.getComponentCount (); switch ( type ) { case roll: { final double oneAngle = displayProgress * singleComponentSpacing; for ( int i = 0; i < itemsCount; i++ ) { final int ai = hidingCauseIndex + i; final int ci = ai < itemsCount ? ai : ai - itemsCount; final double angle = startingAngle + hidingCauseIndex * singleComponentSpacing + oneAngle * i * cwSign; final int x = ( int ) Math.round ( center.x + radius * Math.cos ( angle ) ); final int y = ( int ) Math.round ( center.y + radius * Math.sin ( angle ) ); placeElement ( menu, ci, x, y ); } break; } case star: { final int cradius = Math.round ( radius * displayProgress ); for ( int i = 0; i < itemsCount; i++ ) { final double angle = startingAngle + singleComponentSpacing * i * cwSign; final int r = i == hidingCause ? radius : cradius; final int x = ( int ) Math.round ( center.x + r * Math.cos ( angle ) ); final int y = ( int ) Math.round ( center.y + r * Math.sin ( angle ) ); placeElement ( menu, i, x, y ); } break; } case shutter: { // todo Catch angle if was half-opened and continue hiding from there final int cradius = Math.round ( radius * displayProgress ); final int modSign = menu.isDisplaying () ? -1 : 1; final double angleMod = GeometryUtils.toRadians ( modSign * 90 * ( 1f - displayProgress ) ); for ( int i = 0; i < itemsCount; i++ ) { final double mod = i == hidingCause ? 0 : angleMod; final double angle = startingAngle + ( singleComponentSpacing * i + mod ) * cwSign; final int r = i == hidingCause ? radius : cradius; final int x = ( int ) Math.round ( center.x + r * Math.cos ( angle ) ); final int y = ( int ) Math.round ( center.y + r * Math.sin ( angle ) ); placeElement ( menu, i, x, y ); } break; } case fade: { for ( int i = 0; i < itemsCount; i++ ) { final double angle = startingAngle + singleComponentSpacing * i * cwSign; final int x = ( int ) Math.round ( center.x + radius * Math.cos ( angle ) ); final int y = ( int ) Math.round ( center.y + radius * Math.sin ( angle ) ); placeElement ( menu, i, x, y ); } break; } } } /** * Returns spacing angle between two menu components. * * @param menu processed menu * @return spacing angle between two menu components */ public double getSingleComponentSpacing ( final WebDynamicMenu menu ) { final double fullCircle = GeometryUtils.toRadians ( Math.min ( menu.getAngleRange (), 360 ) ); return fullCircle < Math.PI * 2 ? fullCircle / ( menu.getComponentCount () - 1 ) : fullCircle / menu.getComponentCount (); } /** * Returns menu items starting angle relative to vertical axis. * * @param menu processed menu * @return menu items starting angle relative to vertical axis */ public double getStartingAngle ( final WebDynamicMenu menu ) { return GeometryUtils.toRadians ( menu.getStartingAngle () ) - Math.PI / 2; } /** * Returns menu item angle relative to vertical axis. * * @param menu menu to process * @param index menu item index * @return menu item angle relative to vertical axis */ public double getItemAngle ( final WebDynamicMenu menu, final int index ) { final int cwSign = menu.isClockwise () ? 1 : -1; switch ( menu.getType () ) { case roll: case star: case shutter: case fade: default: { final double startingAngle = getStartingAngle ( menu ); final double singleComponentSpacing = getSingleComponentSpacing ( menu ); return startingAngle + singleComponentSpacing * index * cwSign; } } } /** * Places single menu item into its current position. * * @param menu processed menu * @param i menu item index * @param x menu item center X coordinate * @param y menu item center Y coordinate */ protected void placeElement ( final WebDynamicMenu menu, final int i, final int x, final int y ) { final Component menuItem = menu.getComponent ( i ); final Dimension ps = menuItem.getPreferredSize (); menuItem.setBounds ( x - ps.width / 2, y - ps.height / 2, ps.width, ps.height ); } /** * {@inheritDoc} */ @Override public Dimension preferredLayoutSize ( final Container parent ) { final WebDynamicMenu menu = ( WebDynamicMenu ) parent; switch ( menu.getType () ) { case roll: case star: case shutter: case fade: default: { final int radius = menu.getRadius (); return new Dimension ( radius * 2, radius * 2 ); } } } }