More widening.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@2516 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
samskivert
2008-12-29 20:08:08 +00:00
parent 6b21917b25
commit cc88420aa2
+74 -109
View File
@@ -44,15 +44,14 @@ import com.samskivert.util.ObserverList;
import com.samskivert.util.RunAnywhere; import com.samskivert.util.RunAnywhere;
/** /**
* Provides a radial menu with iconic menu items that expand to include * Provides a radial menu with iconic menu items that expand to include textual descriptions when
* textual descriptions when moused over. * moused over.
*/ */
public class RadialMenu public class RadialMenu
implements MouseMotionListener, MouseListener implements MouseMotionListener, MouseListener
{ {
/** /**
* The interface used to communicate back to the component that hosts * The interface used to communicate back to the component that hosts this menu.
* this menu.
*/ */
public static interface Host public static interface Host
{ {
@@ -62,10 +61,9 @@ public class RadialMenu
public Component getComponent (); public Component getComponent ();
/** /**
* Requests the bounds of the visible region in which we'll be * Requests the bounds of the visible region in which we'll be rendering the menu. The menu
* rendering the menu. The menu will be constrained to fit within * will be constrained to fit within these bounds starting with a natural position around
* these bounds starting with a natural position around the * the supplied target bounds and adjusting minimally to fit.
* supplied target bounds and adjusting minimally to fit.
*/ */
public Rectangle getViewBounds (); public Rectangle getViewBounds ();
@@ -75,34 +73,30 @@ public class RadialMenu
public void repaintRect (int x, int y, int width, int height); public void repaintRect (int x, int y, int width, int height);
/** /**
* Instructs the host component to stop rendering this menu * Instructs the host component to stop rendering this menu because it has been popped
* because it has been popped down. * down.
*/ */
public void menuDeactivated (RadialMenu menu); public void menuDeactivated (RadialMenu menu);
} }
/** /**
* Used to determine at the time that a menu is shown, whether its * Used to determine at the time that a menu is shown, whether its items should be included
* items should be included and/or enabled. Predicate methods are * and/or enabled. Predicate methods are called immediately before showing a radial menu and
* called immediately before showing a radial menu and thus should not * thus should not perform complicated computations. They generally will check some simple
* perform complicated computations. They generally will check some * model to determine a menu item's status.
* simple model to determine a menu item's status.
*/ */
public static interface Predicate public static interface Predicate
{ {
/** /**
* If true, the menu will be included in the character menu when * If true, the menu will be included in the character menu when it is displayed.
* it is displayed.
* *
* @param menu the menu that will include or not include the item * @param menu the menu that will include or not include the item in question.
* in question.
* @param item the menu item that will or will not be included. * @param item the menu item that will or will not be included.
*/ */
public boolean isIncluded (RadialMenu menu, RadialMenuItem item); public boolean isIncluded (RadialMenu menu, RadialMenuItem item);
/** /**
* If true, the menu will be enabled when displayed in a * If true, the menu will be enabled when displayed in a character's menu.
* character's menu.
* *
* @param menu the menu that contains the item in question. * @param menu the menu that contains the item in question.
* @param item the menu item that will or will not be enabled. * @param item the menu item that will or will not be enabled.
@@ -111,8 +105,7 @@ public class RadialMenu
} }
/** /**
* An additional interface that can be implemented by the predicate * An additional interface that can be implemented by the predicate to display extra status.
* to display extra status.
*/ */
public static interface IconPredicate extends Predicate public static interface IconPredicate extends Predicate
{ {
@@ -130,32 +123,27 @@ public class RadialMenu
} }
/** /**
* Adds a menu item to the menu. The menu should not currently be * Adds a menu item to the menu. The menu should not currently be active.
* active.
* *
* @param command the command to be issued when the item is selected. * @param command the command to be issued when the item is selected.
* @param label the textual label to be displayed with the menu item. * @param label the textual label to be displayed with the menu item.
* @param icon the icon to display next to the menu text or null if no * @param icon the icon to display next to the menu text or null if no
* icon is desired. * icon is desired.
* @param predicate a predicate that will be used to determine whether * @param predicate a predicate that will be used to determine whether or not this menu item
* or not this menu item should be included in the menu and enabled * should be included in the menu and enabled when it is shown.
* when it is shown.
* *
* @return the item that was added to the menu. It can be modified * @return the item that was added to the menu. It can be modified while the menu is not being
* while the menu is not being displayed. * displayed.
*/ */
public RadialMenuItem addMenuItem (String command, String label, public RadialMenuItem addMenuItem (String command, String label, Image icon, Predicate predicate)
Image icon, Predicate predicate)
{ {
RadialMenuItem item = new RadialMenuItem( RadialMenuItem item = new RadialMenuItem(command, label, new ImageIcon(icon), predicate);
command, label, new ImageIcon(icon), predicate);
addMenuItem(item); addMenuItem(item);
return item; return item;
} }
/** /**
* Adds an already constructed menu item to the menu. The menu should * Adds an already constructed menu item to the menu. The menu should not currently be active.
* not currently be active.
* *
* @param item the menu item instance to be added. * @param item the menu item instance to be added.
*/ */
@@ -168,8 +156,7 @@ public class RadialMenu
} }
/** /**
* Adds an already constructed menu item to the menu. The menu should * Adds an already constructed menu item to the menu. The menu should not currently be active.
* not currently be active.
* *
* @param item the menu item instance to be added. * @param item the menu item instance to be added.
*/ */
@@ -187,8 +174,7 @@ public class RadialMenu
} }
/** /**
* Removes the first menu item that matches the specified command from * Removes the first menu item that matches the specified command from this menu.
* this menu.
* *
* @return true if a matching menu item was removed, false if not. * @return true if a matching menu item was removed, false if not.
*/ */
@@ -218,9 +204,8 @@ public class RadialMenu
} }
/** /**
* Adds a listener that will be notified when a menu item is * Adds a listener that will be notified when a menu item is selected. When the the menu is
* selected. When the the menu is popped down, all listeners will be * popped down, all listeners will be cleared.
* cleared.
*/ */
public void addActionListener (ActionListener listener) public void addActionListener (ActionListener listener)
{ {
@@ -228,8 +213,7 @@ public class RadialMenu
} }
/** /**
* Sets the optional centerpiece icon to be displayed in the center of * Sets the optional centerpiece icon to be displayed in the center of the menu.
* the menu.
*/ */
public void setCenterIcon (Icon icon) public void setCenterIcon (Icon icon)
{ {
@@ -237,19 +221,15 @@ public class RadialMenu
} }
/** /**
* Activates the radial menu, rendering a menu around the prescribed * Activates the radial menu, rendering a menu around the prescribed bounds. It is expected
* bounds. It is expected that the host component will subsequently * that the host component will subsequently call {@link #render} if the menu invalidates the
* call {@link #render} if the menu invalidates the region of the * region of the component occupied by the menu and requests it to repaint.
* component occupied by the menu and requests it to repaint.
* *
* @param host the host component within which the radial menu is * @param host the host component within which the radial menu is displayed.
* displayed. * @param bounds the bounds of the object that was clicked to activate the menu.
* @param bounds the bounds of the object that was clicked to activate * @param argument a reference to an object that will be provided along with the command that
* the menu. * is issued if the user selects a menu item (unless that item has an overriding argument, in
* @param argument a reference to an object that will be provided * which case the overriding argument will be used).
* along with the command that is issued if the user selects a menu
* item (unless that item has an overriding argument, in which case
* the overriding argument will be used).
*/ */
public void activate (Host host, Rectangle bounds, Object argument) public void activate (Host host, Rectangle bounds, Object argument)
{ {
@@ -258,15 +238,12 @@ public class RadialMenu
} }
/** /**
* Activates the radial menu, rendering a menu around the prescribed * Activates the radial menu, rendering a menu around the prescribed bounds. It is expected
* bounds. It is expected that the host component will subsequently * that the host component will subsequently call {@link #render} if the menu invalidates the
* call {@link #render} if the menu invalidates the region of the * region of the component occupied by the menu and requests it to repaint.
* component occupied by the menu and requests it to repaint.
* *
* @param host the host component within which the radial menu is * @param host the host component within which the radial menu is displayed.
* displayed. * @param bounds the bounds of the object that was clicked to activate the menu.
* @param bounds the bounds of the object that was clicked to activate
* the menu.
*/ */
public void activate (Host host, Rectangle bounds) public void activate (Host host, Rectangle bounds)
{ {
@@ -296,10 +273,9 @@ public class RadialMenu
} }
/** /**
* Returns the argument provided to this menu when {@link #activate} * Returns the argument provided to this menu when {@link #activate} was called. This will only
* was called. This will only be non-null while the menu is active and * be non-null while the menu is active and only then if an argument was provided in the call
* only then if an argument was provided in the call to {@link * to {@link #activate}.
* #activate}.
*/ */
public Object getActivationArgument () public Object getActivationArgument ()
{ {
@@ -363,8 +339,7 @@ public class RadialMenu
} }
// we have to wait and render the active item last // we have to wait and render the active item last
if (item != _activeItem) { if (item != _activeItem) {
item.render(host, this, gfx, x + item.closedBounds.x, item.render(host, this, gfx, x + item.closedBounds.x, y + item.closedBounds.y);
y + item.closedBounds.y);
} }
} }
@@ -419,8 +394,8 @@ public class RadialMenu
_activeItem.setActive(true); _activeItem.setActive(true);
repaint = true; repaint = true;
// if we got here with a mouse moved, we've definately // if we got here with a mouse moved, we've definately made the decision to be
// made the decision to be in non drag mode // in non drag mode
if (event.getID() == MouseEvent.MOUSE_MOVED) { if (event.getID() == MouseEvent.MOUSE_MOVED) {
_msMode = true; _msMode = true;
} }
@@ -462,9 +437,8 @@ public class RadialMenu
long when = RunAnywhere.getWhen(event); long when = RunAnywhere.getWhen(event);
if (!_msMode && if (!_msMode &&
(when < _poptime + DRAGMODE_DELAY)) { (when < _poptime + DRAGMODE_DELAY)) {
// if the dragmode delay time hasn't passed, then we're // if the dragmode delay time hasn't passed, then we're going to leave the menu up
// going to leave the menu up because the user wants it // because the user wants it to behave like a MS Windows menu.
// to behave like a MS Windows menu.
_msMode = true; _msMode = true;
return; return;
@@ -501,8 +475,7 @@ public class RadialMenu
} }
/** /**
* Lays the radial menu items out based on the currently configured * Lays the radial menu items out based on the currently configured bounds information.
* bounds information.
*/ */
protected void layout () protected void layout ()
{ {
@@ -537,16 +510,14 @@ public class RadialMenu
} }
gfx.dispose(); gfx.dispose();
// use the maximum of either width or height and make a circle // use the maximum of either width or height and make a circle around that
// around that
double radius = Math.max(_tbounds.height, _tbounds.width) / 2; double radius = Math.max(_tbounds.height, _tbounds.width) / 2;
// be sure to add a gap and space for the menu item itself // be sure to add a gap and space for the menu item itself
radius += (5 + maxwid/2); radius += (5 + maxwid/2);
// compute the angle between menu items (we use the diameter of // compute the angle between menu items (we use the diameter of the menu items as an
// the menu items as an approximate measure of the distance along // approximate measure of the distance along the circumference)
// the circumference)
double theta = (maxwid + 10) / radius ; double theta = (maxwid + 10) / radius ;
// now position each item accordingly // now position each item accordingly
@@ -586,8 +557,8 @@ public class RadialMenu
_bounds.add(item.openBounds); _bounds.add(item.openBounds);
} }
// now translate everything from the center of the target bounds // now translate everything from the center of the target bounds to the upper left of the
// to the upper left of the menu bounds // menu bounds
if (_centerLabel != null) { if (_centerLabel != null) {
_centerLabel.openBounds.translate(-_bounds.x, -_bounds.y); _centerLabel.openBounds.translate(-_bounds.x, -_bounds.y);
_centerLabel.closedBounds.translate(-_bounds.x, -_bounds.y); _centerLabel.closedBounds.translate(-_bounds.x, -_bounds.y);
@@ -598,28 +569,25 @@ public class RadialMenu
item.closedBounds.translate(-_bounds.x, -_bounds.y); item.closedBounds.translate(-_bounds.x, -_bounds.y);
} }
// the origin was at the center of the encircled rectangle; we // the origin was at the center of the encircled rectangle; we need to translate it such
// need to translate it such that the origin of our bounds are in // that the origin of our bounds are in screen coordinates and at the upper left rather
// screen coordinates and at the upper left rather than the center // than the center
_bounds.x += (_tbounds.x + _tbounds.width/2); _bounds.x += (_tbounds.x + _tbounds.width/2);
_bounds.y += (_tbounds.y + _tbounds.height/2); _bounds.y += (_tbounds.y + _tbounds.height/2);
// now make sure the whole shebang is fully visible within the // now make sure the whole shebang is fully visible within the host component
// host component
Rectangle hbounds = _host.getViewBounds(); Rectangle hbounds = _host.getViewBounds();
Point pos = SwingUtil.fitRectInRect(_bounds, hbounds); Point pos = SwingUtil.fitRectInRect(_bounds, hbounds);
_bounds.setLocation(pos); _bounds.setLocation(pos);
} }
/** /**
* Requests that our host component repaint the part of itself that we * Requests that our host component repaint the part of itself that we occupy.
* occupy.
*/ */
protected void repaint () protected void repaint ()
{ {
// only repaint the area that we overlap // only repaint the area that we overlap
_host.repaintRect(_bounds.x, _bounds.y, _host.repaintRect(_bounds.x, _bounds.y, _bounds.width+1, _bounds.height+1);
_bounds.width+1, _bounds.height+1);
} }
/** Our host component. */ /** Our host component. */
@@ -631,8 +599,8 @@ public class RadialMenu
/** The bounds that all of our menu items occupy. */ /** The bounds that all of our menu items occupy. */
protected Rectangle _bounds; protected Rectangle _bounds;
/** The argument object, which will be delivered along with a posted /** The argument object, which will be delivered along with a posted command when a menu item
* command when a menu item is selected. */ * is selected. */
protected Object _argument; protected Object _argument;
/** The centerpiece icon or null if there is none. */ /** The centerpiece icon or null if there is none. */
@@ -642,31 +610,28 @@ public class RadialMenu
protected RadialLabelSausage _centerLabel; protected RadialLabelSausage _centerLabel;
/** Our menu items. */ /** Our menu items. */
protected ArrayList<RadialMenuItem> _items = protected ArrayList<RadialMenuItem> _items = new ArrayList<RadialMenuItem>();
new ArrayList<RadialMenuItem>();
/** The item that has the mouse over it presently, and the default item /** The item that has the mouse over it presently, and the default item if we're double
* if we're double clicked. */ * clicked. */
protected RadialMenuItem _activeItem, _defaultItem; protected RadialMenuItem _activeItem, _defaultItem;
/** The amount of time the user must hold down the mouse in order /** The amount of time the user must hold down the mouse in order to put it in drag mode, after
* to put it in drag mode, after which the first mouse-up will pop * which the first mouse-up will pop down the menu. */
* down the menu. */
protected static final long DRAGMODE_DELAY = 1000L; protected static final long DRAGMODE_DELAY = 1000L;
/** The amount of time after the menu has been popped up before we /** The amount of time after the menu has been popped up before we accept selections. */
* accept selections. */
protected static final long DEBOUNCE_DELAY = 200L; protected static final long DEBOUNCE_DELAY = 200L;
/** How quickly a second click must arrive for us to count it as a double /** How quickly a second click must arrive for us to count it as a double click and not two
* click and not two single clicks. */ * single clicks. */
protected static final long DOUBLECLICK_DELAY = 400L; protected static final long DOUBLECLICK_DELAY = 400L;
/** The time at which the menu was popped up. */ /** The time at which the menu was popped up. */
protected long _poptime; protected long _poptime;
/** Whether we're popping the menu up in microsoft mode, where the /** Whether we're popping the menu up in microsoft mode, where the user does not need to drag
* user does not need to drag to the item they want to select. */ * to the item they want to select. */
protected boolean _msMode = false; protected boolean _msMode = false;
/** This hijacks and holds onto the previous mouse listeners. */ /** This hijacks and holds onto the previous mouse listeners. */