Made DropdownMenu extend Slick's AbstractComponent class.

Clicks and animation updates are now handled internally within the DropdownMenu class, which should simplify usage a bit.  Override menuClicked() and itemSelected() instead for event handling.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2015-09-09 18:57:01 -04:00
parent 7f1193bb77
commit 831c297ece
2 changed files with 142 additions and 75 deletions

View File

@ -331,8 +331,35 @@ public class DownloadsMenu extends BasicGameState {
// dropdown menu
int serverWidth = (int) (width * 0.12f);
serverMenu = new DropdownMenu<DownloadServer>(SERVERS,
baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth, searchY, serverWidth);
serverMenu = new DropdownMenu<DownloadServer>(container, SERVERS,
baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth, searchY, serverWidth) {
@Override
public void itemSelected(int index, DownloadServer item) {
resultList = null;
startResult = 0;
focusResult = -1;
totalResults = 0;
page = 0;
pageResultTotal = 1;
pageDir = Page.RESET;
searchResultString = "Loading data from server...";
lastQuery = null;
pageDir = Page.RESET;
if (searchQuery != null)
searchQuery.interrupt();
resetSearchTimer();
}
@Override
public boolean menuClicked(int index) {
// block input during beatmap importing
if (importThread != null)
return false;
SoundController.playSound(SoundEffect.MENUCLICK);
return true;
}
};
serverMenu.setBackgroundColor(Colors.BLACK_BG_HOVER);
serverMenu.setBorderColor(Color.black);
serverMenu.setChevronRightColor(Color.white);
@ -428,7 +455,7 @@ public class DownloadsMenu extends BasicGameState {
rankedButton.draw(Color.magenta);
// dropdown menu
serverMenu.draw(g, mouseX, mouseY);
serverMenu.render(container, g);
// importing beatmaps
if (importThread != null) {
@ -460,7 +487,6 @@ public class DownloadsMenu extends BasicGameState {
importButton.hoverUpdate(delta, mouseX, mouseY);
resetButton.hoverUpdate(delta, mouseX, mouseY);
rankedButton.hoverUpdate(delta, mouseX, mouseY);
serverMenu.update(delta);
// focus timer
if (focusResult != -1 && focusTimer < FOCUS_DELAY)
@ -523,29 +549,6 @@ public class DownloadsMenu extends BasicGameState {
return;
}
// dropdown menu
int oldServerIndex = serverMenu.getSelectedIndex(), serverMenuClickResult = serverMenu.click(x, y);
if (serverMenuClickResult > -2) {
SoundController.playSound(SoundEffect.MENUCLICK);
if (serverMenuClickResult == -1 || serverMenuClickResult == oldServerIndex)
return;
resultList = null;
startResult = 0;
focusResult = -1;
totalResults = 0;
page = 0;
pageResultTotal = 1;
pageDir = Page.RESET;
searchResultString = "Loading data from server...";
lastQuery = null;
pageDir = Page.RESET;
if (searchQuery != null)
searchQuery.interrupt();
resetSearchTimer();
return;
}
// search results
DownloadNode[] nodes = resultList;
if (nodes != null) {
@ -865,6 +868,7 @@ public class DownloadsMenu extends BasicGameState {
importButton.resetHover();
resetButton.resetHover();
rankedButton.resetHover();
serverMenu.activate();
serverMenu.reset();
focusResult = -1;
startResult = 0;
@ -881,6 +885,7 @@ public class DownloadsMenu extends BasicGameState {
public void leave(GameContainer container, StateBasedGame game)
throws SlickException {
search.setFocus(false);
serverMenu.deactivate();
SoundController.stopTrack();
MusicController.resume();
}

View File

@ -26,15 +26,31 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.Font;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.gui.AbstractComponent;
import org.newdawn.slick.gui.GUIContext;
/**
* Simple dropdown menu.
* <p>
* Basic usage:
* <ul>
* <li>Override {@link #menuClicked(int)} to perform actions when the menu is clicked
* (e.g. play a sound effect, block input under certain conditions).
* <li>Override {@link #itemSelected(int, Object)} to perform actions when a new item is selected.
* <li>Call {@link #activate()}/{@link #deactivate()} whenever the component is needed
* (e.g. in a state's {@code enter} and {@code leave} events.
* </ul>
*/
public class DropdownMenu<E> {
public class DropdownMenu<E> extends AbstractComponent {
/** Padding ratios for drawing. */
private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f;
/** Whether this component is active. */
private boolean active;
/** The menu items. */
private E[] items;
@ -50,6 +66,9 @@ public class DropdownMenu<E> {
/** The expanding animation progress. */
private AnimatedValue expandProgress = new AnimatedValue(300, 0f, 1f, AnimationEquation.LINEAR);
/** The last update time, in milliseconds. */
private long lastUpdateTime;
/** The top-left coordinates. */
private float x, y;
@ -76,39 +95,44 @@ public class DropdownMenu<E> {
/**
* Creates a new dropdown menu.
* @param container the container rendering this menu
* @param items the list of items (with names given as their {@code toString()} methods)
* @param x the top-left x coordinate
* @param y the top-left y coordinate
*/
public DropdownMenu(E[] items, float x, float y) {
this(items, x, y, 0);
public DropdownMenu(GUIContext container, E[] items, float x, float y) {
this(container, items, x, y, 0);
}
/**
* Creates a new dropdown menu with the given fonts.
* @param container the container rendering this menu
* @param items the list of items (with names given as their {@code toString()} methods)
* @param x the top-left x coordinate
* @param y the top-left y coordinate
* @param normal the normal font
* @param selected the font for the selected item
*/
public DropdownMenu(E[] items, float x, float y, UnicodeFont normal, UnicodeFont selected) {
this(items, x, y, 0, normal, selected);
public DropdownMenu(GUIContext container, E[] items, float x, float y, UnicodeFont normal, UnicodeFont selected) {
this(container, items, x, y, 0, normal, selected);
}
/**
* Creates a new dropdown menu with the given width.
* @param container the container rendering this menu
* @param items the list of items (with names given as their {@code toString()} methods)
* @param x the top-left x coordinate
* @param y the top-left y coordinate
* @param width the menu width
*/
public DropdownMenu(E[] items, float x, float y, int width) {
public DropdownMenu(GUIContext container, E[] items, float x, float y, int width) {
super(container);
init(items, x, y, width);
}
/**
* Creates a new dropdown menu with the given width and fonts.
* @param container the container rendering this menu
* @param items the list of items (with names given as their {@code toString()} methods)
* @param x the top-left x coordinate
* @param y the top-left y coordinate
@ -116,7 +140,8 @@ public class DropdownMenu<E> {
* @param normal the normal font
* @param selected the font for the selected item
*/
public DropdownMenu(E[] items, float x, float y, int width, UnicodeFont normal, UnicodeFont selected) {
public DropdownMenu(GUIContext container, E[] items, float x, float y, int width, UnicodeFont normal, UnicodeFont selected) {
super(container);
this.fontNormal = normal;
this.fontSelected = selected;
init(items, x, y, width);
@ -157,15 +182,29 @@ public class DropdownMenu<E> {
this.width = Math.max(width, minWidth);
}
/**
* Returns the width of the menu.
*/
@Override
public void setLocation(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int getX() { return (int) x; }
@Override
public int getY() { return (int) y; }
@Override
public int getWidth() { return width; }
/**
* Returns the height of the base item.
*/
public int getHeight() { return baseHeight; }
@Override
public int getHeight() { return (expanded) ? height : baseHeight; }
/** Activates the component. */
public void activate() { this.active = true; }
/** Deactivates the component. */
public void deactivate() { this.active = false; }
/**
* Returns whether the dropdown menu is currently open.
@ -199,14 +238,19 @@ public class DropdownMenu<E> {
return (cx > x && cx < x + width && cy > y && cy < y + baseHeight);
}
/**
* Draws the dropdown menu.
* @param g the graphics context
* @param cx the mouse x coordinate
* @param cy the mouse y coordinate
*/
public void draw(Graphics g, float cx, float cy) {
int idx = getIndexAt(cx, cy);
@Override
public void render(GUIContext container, Graphics g) throws SlickException {
// update animation
long time = container.getTime();
if (lastUpdateTime > 0) {
int delta = (int) (time - lastUpdateTime);
expandProgress.update((expanded) ? delta : -delta * 2);
}
this.lastUpdateTime = time;
// get parameters
Input input = container.getInput();
int idx = getIndexAt(input.getMouseX(), input.getMouseY());
float t = expandProgress.getValue();
if (expanded)
t = AnimationEquation.OUT_CUBIC.calc(t);
@ -274,40 +318,58 @@ public class DropdownMenu<E> {
return (int) ((cy - (y + offsetY)) / offsetY);
}
/**
* Updates the animations by a delta interval.
* @param delta the delta interval since the last call
*/
public void update(int delta) {
expandProgress.update((expanded) ? delta : -delta * 2);
}
/**
* Registers a click at the given location.
* If the base item is clicked and the menu is unexpanded, it will be expanded;
* in all other cases, the menu will be unexpanded. If an item different from
* the current one is selected, that item will be selected.
* @param cx the x coordinate
* @param cy the y coordinate
* @return the index of the item at the given location, -1 for the base item,
* and -2 if there is no item at the location
*/
public int click(float cx, float cy) {
int idx = getIndexAt(cx, cy);
this.expanded = (idx == -1) ? !expanded : false;
if (idx >= 0)
this.itemIndex = idx;
return idx;
}
/**
* Resets the menu state.
*/
public void reset() {
this.expanded = false;
this.lastUpdateTime = 0;
expandProgress.setTime(0);
}
@Override
public void mousePressed(int button, int x, int y) {
if (!active)
return;
if (button == Input.MOUSE_MIDDLE_BUTTON)
return;
int idx = getIndexAt(x, y);
if (idx == -2) {
this.expanded = false;
return;
}
if (!menuClicked(idx))
return;
this.expanded = (idx == -1) ? !expanded : false;
if (idx >= 0 && itemIndex != idx) {
this.itemIndex = idx;
itemSelected(idx, items[idx]);
}
consumeEvent();
}
/**
* Notification that a new item was selected (via override).
* @param index the index of the item selected
* @param item the item selected
*/
public void itemSelected(int index, E item) {}
/**
* Notification that the menu was clicked (via override).
* @param index the index of the item clicked, or -1 for the base item
* @return true to process the click, or false to block/intercept it
*/
public boolean menuClicked(int index) { return true; }
@Override
public void setFocus(boolean focus) { /* does not currently use the "focus" concept */ }
@Override
public void mouseReleased(int button, int x, int y) { /* does not currently use the "focus" concept */ }
/**
* Selects the item at the given index.
* @param index the list item index