From 831c297ecefd4b5dfb2321e74dc27251e1599ab8 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Wed, 9 Sep 2015 18:57:01 -0400 Subject: [PATCH] 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 --- .../opsu/states/DownloadsMenu.java | 59 ++++--- src/itdelatrisu/opsu/ui/DropdownMenu.java | 158 ++++++++++++------ 2 files changed, 142 insertions(+), 75 deletions(-) diff --git a/src/itdelatrisu/opsu/states/DownloadsMenu.java b/src/itdelatrisu/opsu/states/DownloadsMenu.java index b019ef0f..1a2a490e 100644 --- a/src/itdelatrisu/opsu/states/DownloadsMenu.java +++ b/src/itdelatrisu/opsu/states/DownloadsMenu.java @@ -331,8 +331,35 @@ public class DownloadsMenu extends BasicGameState { // dropdown menu int serverWidth = (int) (width * 0.12f); - serverMenu = new DropdownMenu(SERVERS, - baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth, searchY, serverWidth); + serverMenu = new DropdownMenu(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(); } diff --git a/src/itdelatrisu/opsu/ui/DropdownMenu.java b/src/itdelatrisu/opsu/ui/DropdownMenu.java index 5a9fbb5b..a75d0d9a 100644 --- a/src/itdelatrisu/opsu/ui/DropdownMenu.java +++ b/src/itdelatrisu/opsu/ui/DropdownMenu.java @@ -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. + *

+ * Basic usage: + *

    + *
  • Override {@link #menuClicked(int)} to perform actions when the menu is clicked + * (e.g. play a sound effect, block input under certain conditions). + *
  • Override {@link #itemSelected(int, Object)} to perform actions when a new item is selected. + *
  • Call {@link #activate()}/{@link #deactivate()} whenever the component is needed + * (e.g. in a state's {@code enter} and {@code leave} events. + *
*/ -public class DropdownMenu { +public class DropdownMenu 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 { /** 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 { /** * 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 { * @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 { 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 { 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 { 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