convert download state

This commit is contained in:
yugecin 2017-01-18 22:36:09 +01:00
parent 36cfe3813a
commit 81b71d5703
5 changed files with 262 additions and 430 deletions

View File

@ -93,7 +93,7 @@ public class Opsu extends StateBasedGame {
addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); addState(new GamePauseMenu(STATE_GAMEPAUSEMENU));
addState(new GameRanking(STATE_GAMERANKING)); addState(new GameRanking(STATE_GAMERANKING));
addState(new OptionsMenu(STATE_OPTIONSMENU)); addState(new OptionsMenu(STATE_OPTIONSMENU));
addState(new DownloadsMenu(STATE_DOWNLOADSMENU)); //addState(new DownloadsMenu(STATE_DOWNLOADSMENU));
} }
/** /**

View File

@ -51,17 +51,15 @@ import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener; import javax.sound.sampled.LineListener;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
import org.newdawn.slick.gui.TextField; import org.newdawn.slick.gui.TextField;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.ComplexOpsuState;
/** /**
* Downloads menu. * Downloads menu.
@ -69,7 +67,10 @@ import org.newdawn.slick.util.Log;
* Players are able to download beatmaps off of various servers and import them * Players are able to download beatmaps off of various servers and import them
* from this state. * from this state.
*/ */
public class DownloadsMenu extends BasicGameState { public class DownloadsMenu extends ComplexOpsuState {
private final InstanceContainer instanceContainer;
/** Delay time, in milliseconds, between each search. */ /** Delay time, in milliseconds, between each search. */
private static final int SEARCH_DELAY = 700; private static final int SEARCH_DELAY = 700;
@ -288,45 +289,42 @@ public class DownloadsMenu extends BasicGameState {
} }
} }
// game-related variables public DownloadsMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
private GameContainer container; super(displayContainer);
private StateBasedGame game; this.instanceContainer = instanceContainer;
private Input input;
private final int state;
public DownloadsMenu(int state) {
this.state = state;
} }
@Override @Override
public void init(GameContainer container, StateBasedGame game) public void revalidate() {
throws SlickException { super.revalidate();
this.container = container;
this.game = game;
this.input = container.getInput();
int width = container.getWidth(); components.clear();
int height = container.getHeight();
float baseX = width * 0.024f; int width = displayContainer.width;
float searchY = (height * 0.04f) + Fonts.LARGE.getLineHeight(); int height = displayContainer.height;
float searchWidth = width * 0.3f; int baseX = (int) (width * 0.024f);
int searchY = (int) (height * 0.04f + Fonts.LARGE.getLineHeight());
int searchWidth = (int) (width * 0.3f);
// search // search
searchTimer = SEARCH_DELAY; searchTimer = SEARCH_DELAY;
searchResultString = "Loading data from server..."; searchResultString = "Loading data from server...";
search = new TextField( search = new TextField(displayContainer, Fonts.DEFAULT, baseX, searchY, searchWidth, Fonts.MEDIUM.getLineHeight()) {
container, Fonts.DEFAULT, (int) baseX, (int) searchY, @Override
(int) searchWidth, Fonts.MEDIUM.getLineHeight() public boolean isFocusable() {
); return false;
}
};
search.setFocused(true);
search.setBackgroundColor(Colors.BLACK_BG_NORMAL); search.setBackgroundColor(Colors.BLACK_BG_NORMAL);
search.setBorderColor(Color.white); search.setBorderColor(Color.white);
search.setTextColor(Color.white); search.setTextColor(Color.white);
search.setConsumeEvents(false);
search.setMaxLength(255); search.setMaxLength(255);
components.add(search);
// page buttons // page buttons
float pageButtonY = height * 0.2f; int pageButtonY = (int) (height * 0.2f);
float pageButtonWidth = width * 0.7f; int pageButtonWidth = (int) (width * 0.7f);
Image prevImg = GameImage.MUSIC_PREVIOUS.getImage(); Image prevImg = GameImage.MUSIC_PREVIOUS.getImage();
Image nextImg = GameImage.MUSIC_NEXT.getImage(); Image nextImg = GameImage.MUSIC_NEXT.getImage();
prevPage = new MenuButton(prevImg, baseX + prevImg.getWidth() / 2f, prevPage = new MenuButton(prevImg, baseX + prevImg.getWidth() / 2f,
@ -337,25 +335,25 @@ public class DownloadsMenu extends BasicGameState {
nextPage.setHoverExpand(1.5f); nextPage.setHoverExpand(1.5f);
// buttons // buttons
float buttonMarginX = width * 0.004f; int buttonMarginX = (int) (width * 0.004f);
float buttonHeight = height * 0.038f; int buttonHeight = (int) (height * 0.038f);
float resetWidth = width * 0.085f; int resetWidth = (int) (width * 0.085f);
float rankedWidth = width * 0.15f; int rankedWidth = (int) (width * 0.15f);
float lowerWidth = width * 0.12f; int lowerWidth = (int) (width * 0.12f);
float topButtonY = searchY + Fonts.MEDIUM.getLineHeight() / 2f; int topButtonY = (int) (searchY + Fonts.MEDIUM.getLineHeight() / 2f);
float lowerButtonY = height * 0.995f - searchY - buttonHeight / 2f; int lowerButtonY = (int) (height * 0.995f - searchY - buttonHeight / 2f);
Image button = GameImage.MENU_BUTTON_MID.getImage(); Image button = GameImage.MENU_BUTTON_MID.getImage();
Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage(); Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage();
Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage(); Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage();
buttonL = buttonL.getScaledCopy(buttonHeight / buttonL.getHeight()); buttonL = buttonL.getScaledCopy(buttonHeight / buttonL.getHeight());
buttonR = buttonR.getScaledCopy(buttonHeight / buttonR.getHeight()); buttonR = buttonR.getScaledCopy(buttonHeight / buttonR.getHeight());
int lrButtonWidth = buttonL.getWidth() + buttonR.getWidth(); int lrButtonWidth = buttonL.getWidth() + buttonR.getWidth();
Image resetButtonImage = button.getScaledCopy((int) resetWidth - lrButtonWidth, (int) buttonHeight); Image resetButtonImage = button.getScaledCopy(resetWidth - lrButtonWidth, buttonHeight);
Image rankedButtonImage = button.getScaledCopy((int) rankedWidth - lrButtonWidth, (int) buttonHeight); Image rankedButtonImage = button.getScaledCopy(rankedWidth - lrButtonWidth, buttonHeight);
Image lowerButtonImage = button.getScaledCopy((int) lowerWidth - lrButtonWidth, (int) buttonHeight); Image lowerButtonImage = button.getScaledCopy(lowerWidth - lrButtonWidth, buttonHeight);
float resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth; int resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth;
float rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth; int rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth;
float lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth; int lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth;
clearButton = new MenuButton(lowerButtonImage, buttonL, buttonR, clearButton = new MenuButton(lowerButtonImage, buttonL, buttonR,
width * 0.75f + buttonMarginX + lowerButtonWidth / 2f, lowerButtonY); width * 0.75f + buttonMarginX + lowerButtonWidth / 2f, lowerButtonY);
importButton = new MenuButton(lowerButtonImage, buttonL, buttonR, importButton = new MenuButton(lowerButtonImage, buttonL, buttonR,
@ -374,8 +372,8 @@ public class DownloadsMenu extends BasicGameState {
// dropdown menu // dropdown menu
int serverWidth = (int) (width * 0.12f); int serverWidth = (int) (width * 0.12f);
serverMenu = new DropdownMenu<DownloadServer>(container, SERVERS, int x = baseX + searchWidth + buttonMarginX * 3 + resetButtonWidth + rankedButtonWidth;
baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth, searchY, serverWidth) { serverMenu = new DropdownMenu<DownloadServer>(displayContainer, SERVERS, x, searchY, serverWidth) {
@Override @Override
public void itemSelected(int index, DownloadServer item) { public void itemSelected(int index, DownloadServer item) {
resultList = null; resultList = null;
@ -388,13 +386,14 @@ public class DownloadsMenu extends BasicGameState {
searchResultString = "Loading data from server..."; searchResultString = "Loading data from server...";
lastQuery = null; lastQuery = null;
pageDir = Page.RESET; pageDir = Page.RESET;
if (searchQuery != null) if (searchQuery != null) {
searchQuery.interrupt(); searchQuery.interrupt();
}
resetSearchTimer(); resetSearchTimer();
} }
@Override @Override
public boolean menuClicked(int index) { public boolean canSelect(int index) {
// block input during beatmap importing // block input during beatmap importing
if (importThread != null) if (importThread != null)
return false; return false;
@ -406,28 +405,25 @@ public class DownloadsMenu extends BasicGameState {
serverMenu.setBackgroundColor(Colors.BLACK_BG_HOVER); serverMenu.setBackgroundColor(Colors.BLACK_BG_HOVER);
serverMenu.setBorderColor(Color.black); serverMenu.setBorderColor(Color.black);
serverMenu.setChevronRightColor(Color.white); serverMenu.setChevronRightColor(Color.white);
components.add(serverMenu);
} }
@Override @Override
public void render(GameContainer container, StateBasedGame game, Graphics g) public void render(Graphics g) {
throws SlickException { super.render(g);
int width = container.getWidth();
int height = container.getHeight();
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
boolean inDropdownMenu = serverMenu.contains(mouseX, mouseY);
// background // background
GameImage.SEARCH_BG.getImage().draw(); GameImage.SEARCH_BG.getImage().draw();
// title // title
Fonts.LARGE.drawString(width * 0.024f, height * 0.03f, "Download Beatmaps!", Color.white); Fonts.LARGE.drawString(displayContainer.width * 0.024f, displayContainer.height * 0.03f, "Download Beatmaps!", Color.white);
// search // search
g.setColor(Color.white); g.setColor(Color.white);
g.setLineWidth(2f); g.setLineWidth(2f);
search.render(container, g); search.render(g);
Fonts.BOLD.drawString( Fonts.BOLD.drawString(
search.getX() + search.getWidth() * 0.01f, search.getY() + search.getHeight() * 1.3f, search.x + search.width * 0.01f, search.y + search.height * 1.3f,
searchResultString, Color.white searchResultString, Color.white
); );
@ -446,7 +442,7 @@ public class DownloadsMenu extends BasicGameState {
if (index >= nodes.length) if (index >= nodes.length)
break; break;
nodes[index].drawResult(g, offset + i * DownloadNode.getButtonOffset(), nodes[index].drawResult(g, offset + i * DownloadNode.getButtonOffset(),
DownloadNode.resultContains(mouseX, mouseY - offset, i) && !inDropdownMenu, DownloadNode.resultContains(displayContainer.mouseX, displayContainer.mouseY - offset, i) && !serverMenu.isHovered(),
(index == focusResult), (previewID == nodes[index].getID())); (index == focusResult), (previewID == nodes[index].getID()));
} }
g.clearClip(); g.clearClip();
@ -457,9 +453,9 @@ public class DownloadsMenu extends BasicGameState {
// pages // pages
if (nodes.length > 0) { if (nodes.length > 0) {
float baseX = width * 0.024f; float baseX = displayContainer.width * 0.024f;
float buttonY = height * 0.2f; float buttonY = displayContainer.height * 0.2f;
float buttonWidth = width * 0.7f; float buttonWidth = displayContainer.width * 0.7f;
Fonts.BOLD.drawString( Fonts.BOLD.drawString(
baseX + (buttonWidth - Fonts.BOLD.getWidth("Page 1")) / 2f, baseX + (buttonWidth - Fonts.BOLD.getWidth("Page 1")) / 2f,
buttonY - Fonts.BOLD.getLineHeight() * 1.3f, buttonY - Fonts.BOLD.getLineHeight() * 1.3f,
@ -473,11 +469,11 @@ public class DownloadsMenu extends BasicGameState {
} }
// downloads // downloads
float downloadsX = width * 0.75f, downloadsY = search.getY(); float downloadsX = displayContainer.width * 0.75f, downloadsY = search.y;
g.setColor(Colors.BLACK_BG_NORMAL); g.setColor(Colors.BLACK_BG_NORMAL);
g.fillRect(downloadsX, downloadsY, g.fillRect(downloadsX, downloadsY,
width * 0.25f, height - downloadsY * 2f); displayContainer.width * 0.25f, displayContainer.height - downloadsY * 2f);
Fonts.LARGE.drawString(downloadsX + width * 0.015f, downloadsY + height * 0.015f, "Downloads", Color.white); Fonts.LARGE.drawString(downloadsX + displayContainer.width * 0.015f, downloadsY + displayContainer.height * 0.015f, "Downloads", Color.white);
int downloadsSize = DownloadList.get().size(); int downloadsSize = DownloadList.get().size();
if (downloadsSize > 0) { if (downloadsSize > 0) {
int maxDownloadsShown = DownloadNode.maxDownloadsShown(); int maxDownloadsShown = DownloadNode.maxDownloadsShown();
@ -493,7 +489,7 @@ public class DownloadsMenu extends BasicGameState {
if (node == null) if (node == null)
break; break;
node.drawDownload(g, i * DownloadNode.getInfoHeight() + offset, index, node.drawDownload(g, i * DownloadNode.getInfoHeight() + offset, index,
DownloadNode.downloadContains(mouseX, mouseY - offset, i)); DownloadNode.downloadContains(displayContainer.mouseX, displayContainer.mouseY - offset, i));
} }
g.clearClip(); g.clearClip();
@ -510,13 +506,13 @@ public class DownloadsMenu extends BasicGameState {
rankedButton.draw(Color.magenta); rankedButton.draw(Color.magenta);
// dropdown menu // dropdown menu
serverMenu.render(container, g); serverMenu.render(g);
// importing beatmaps // importing beatmaps
if (importThread != null) { if (importThread != null) {
// darken the screen // darken the screen
g.setColor(Colors.BLACK_ALPHA); g.setColor(Colors.BLACK_ALPHA);
g.fillRect(0, 0, width, height); g.fillRect(0, 0, displayContainer.width, displayContainer.height);
UI.drawLoadingProgress(g); UI.drawLoadingProgress(g);
} }
@ -529,8 +525,10 @@ public class DownloadsMenu extends BasicGameState {
} }
@Override @Override
public void update(GameContainer container, StateBasedGame game, int delta) public void preRenderUpdate() {
throws SlickException { super.preRenderUpdate();
int delta = displayContainer.renderDelta;
UI.update(delta); UI.update(delta);
if (importThread == null) if (importThread == null)
MusicController.loopTrackIfEnded(false); MusicController.loopTrackIfEnded(false);
@ -547,11 +545,12 @@ public class DownloadsMenu extends BasicGameState {
// focus new beatmap // focus new beatmap
// NOTE: This can't be called in another thread because it makes OpenGL calls. // NOTE: This can't be called in another thread because it makes OpenGL calls.
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(importedNode, -1, true, true); instanceContainer.provide(SongMenu.class).setFocus(importedNode, -1, true, true);
} }
importThread = null; importThread = null;
} }
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
prevPage.hoverUpdate(delta, mouseX, mouseY); prevPage.hoverUpdate(delta, mouseX, mouseY);
nextPage.hoverUpdate(delta, mouseX, mouseY); nextPage.hoverUpdate(delta, mouseX, mouseY);
@ -572,7 +571,6 @@ public class DownloadsMenu extends BasicGameState {
focusTimer += delta; focusTimer += delta;
// search // search
search.setFocus(true);
searchTimer += delta; searchTimer += delta;
if (searchTimer >= SEARCH_DELAY && importThread == null) { if (searchTimer >= SEARCH_DELAY && importThread == null) {
searchTimer = 0; searchTimer = 0;
@ -608,24 +606,26 @@ public class DownloadsMenu extends BasicGameState {
} }
@Override @Override
public int getID() { return state; } public boolean mousePressed(int button, int x, int y) {
if (super.mousePressed(button, x, y)) {
return true;
}
@Override if (button == Input.MOUSE_MIDDLE_BUTTON) {
public void mousePressed(int button, int x, int y) { return false;
// check mouse button }
if (button == Input.MOUSE_MIDDLE_BUTTON)
return;
// block input during beatmap importing // block input during beatmap importing
if (importThread != null) if (importThread != null) {
return; return true;
}
// back // back
if (UI.getBackButton().contains(x, y)) { if (UI.getBackButton().contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); instanceContainer.provide(MainMenu.class).reset();
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition()); displayContainer.switchState(MainMenu.class);
return; return true;
} }
// search results // search results
@ -694,11 +694,12 @@ public class DownloadsMenu extends BasicGameState {
} }
}.start(); }.start();
} }
return; return true;
} }
if (isLoaded) if (isLoaded) {
return; return true;
}
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
if (index == focusResult) { if (index == focusResult) {
@ -725,7 +726,7 @@ public class DownloadsMenu extends BasicGameState {
break; break;
} }
} }
return; return true;
} }
// pages // pages
@ -741,7 +742,7 @@ public class DownloadsMenu extends BasicGameState {
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
} }
return; return true;
} }
if (pageResultTotal < totalResults && nextPage.contains(x, y)) { if (pageResultTotal < totalResults && nextPage.contains(x, y)) {
if (lastQueryDir == Page.NEXT && searchQuery != null && !searchQuery.isComplete()) if (lastQueryDir == Page.NEXT && searchQuery != null && !searchQuery.isComplete())
@ -753,7 +754,7 @@ public class DownloadsMenu extends BasicGameState {
if (searchQuery != null) if (searchQuery != null)
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
return; return true;
} }
} }
} }
@ -763,7 +764,7 @@ public class DownloadsMenu extends BasicGameState {
if (clearButton.contains(x, y)) { if (clearButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
DownloadList.get().clearInactiveDownloads(); DownloadList.get().clearInactiveDownloads();
return; return true;
} }
if (importButton.contains(x, y)) { if (importButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
@ -771,7 +772,7 @@ public class DownloadsMenu extends BasicGameState {
// import songs in new thread // import songs in new thread
importThread = new BeatmapImportThread(); importThread = new BeatmapImportThread();
importThread.start(); importThread.start();
return; return true;
} }
if (resetButton.contains(x, y)) { if (resetButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
@ -781,7 +782,7 @@ public class DownloadsMenu extends BasicGameState {
if (searchQuery != null) if (searchQuery != null)
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
return; return true;
} }
if (rankedButton.contains(x, y)) { if (rankedButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
@ -791,7 +792,7 @@ public class DownloadsMenu extends BasicGameState {
if (searchQuery != null) if (searchQuery != null)
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
return; return true;
} }
// downloads // downloads
@ -807,8 +808,9 @@ public class DownloadsMenu extends BasicGameState {
if (DownloadNode.downloadIconContains(x, y - offset, i)) { if (DownloadNode.downloadIconContains(x, y - offset, i)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
DownloadNode node = DownloadList.get().getNode(index); DownloadNode node = DownloadList.get().getNode(index);
if (node == null) if (node == null) {
return; return true;
}
Download dl = node.getDownload(); Download dl = node.getDownload();
switch (dl.getStatus()) { switch (dl.getStatus()) {
case CANCELLED: case CANCELLED:
@ -822,62 +824,78 @@ public class DownloadsMenu extends BasicGameState {
dl.cancel(); dl.cancel();
break; break;
} }
return; return true;
} }
} }
} }
return false;
} }
@Override @Override
public void mouseReleased(int button, int x, int y) { public boolean mouseReleased(int button, int x, int y) {
// check mouse button if (super.mouseReleased(button, x, y)) {
if (button == Input.MOUSE_MIDDLE_BUTTON) return true;
return; }
if (button == Input.MOUSE_MIDDLE_BUTTON) {
return false;
}
startDownloadIndexPos.released(); startDownloadIndexPos.released();
startResultPos.released(); startResultPos.released();
return true;
} }
@Override @Override
public void mouseWheelMoved(int newValue) { public boolean mouseWheelMoved(int newValue) {
// change volume // change volume
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) { if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) {
UI.changeVolume((newValue < 0) ? -1 : 1); UI.changeVolume((newValue < 0) ? -1 : 1);
return; return true;
} }
// block input during beatmap importing // block input during beatmap importing
if (importThread != null) if (importThread != null) {
return; return true;
}
int shift = (newValue < 0) ? 1 : -1; int shift = (newValue < 0) ? 1 : -1;
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); scrollLists(displayContainer.mouseX, displayContainer.mouseY, shift);
scrollLists(mouseX, mouseY, shift); return true;
} }
@Override @Override
public void mouseDragged(int oldx, int oldy, int newx, int newy) { public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
// block input during beatmap importing // block input during beatmap importing
if (importThread != null) if (importThread != null) {
return; return true;
}
// check mouse button
if (!input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON) &&
!input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON))
return;
int diff = newy - oldy; int diff = newy - oldy;
if (diff == 0) if (diff == 0) {
return; return false;
}
startDownloadIndexPos.dragged(-diff); startDownloadIndexPos.dragged(-diff);
startResultPos.dragged(-diff); startResultPos.dragged(-diff);
return true;
} }
@Override @Override
public void keyPressed(int key, char c) { public boolean keyReleased(int key, char c) {
return super.keyReleased(key, c);
}
@Override
public boolean keyPressed(int key, char c) {
if (super.keyPressed(key, c)) {
return true;
}
// block input during beatmap importing // block input during beatmap importing
if (importThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) if (importThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) {
return; return true;
}
switch (key) { switch (key) {
case Input.KEY_ESCAPE: case Input.KEY_ESCAPE:
@ -892,16 +910,16 @@ public class DownloadsMenu extends BasicGameState {
} else { } else {
// return to main menu // return to main menu
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); instanceContainer.provide(MainMenu.class).reset();
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition()); displayContainer.switchState(MainMenu.class);
} }
break; return true;
case Input.KEY_ENTER: case Input.KEY_ENTER:
if (!search.getText().isEmpty()) { if (!search.getText().isEmpty()) {
pageDir = Page.RESET; pageDir = Page.RESET;
resetSearchTimer(); resetSearchTimer();
} }
break; return true;
case Input.KEY_F5: case Input.KEY_F5:
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
lastQuery = null; lastQuery = null;
@ -909,30 +927,31 @@ public class DownloadsMenu extends BasicGameState {
if (searchQuery != null) if (searchQuery != null)
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
break; return true;
case Input.KEY_F7: case Input.KEY_F7:
// TODO d // TODO d
//Options.setNextFPS(container); //Options.setNextFPS(container);
break; return true;
case Input.KEY_F10: case Input.KEY_F10:
Options.toggleMouseDisabled(); Options.toggleMouseDisabled();
break; return true;
case Input.KEY_F12: case Input.KEY_F12:
Utils.takeScreenShot(); Utils.takeScreenShot();
break; return true;
default:
// wait for user to finish typing
if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK) {
searchTimer = 0;
pageDir = Page.RESET;
}
break;
} }
// wait for user to finish typing
if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK) {
search.keyPressed(key, c);
searchTimer = 0;
pageDir = Page.RESET;
}
return true;
} }
@Override @Override
public void enter(GameContainer container, StateBasedGame game) public void enter() {
throws SlickException { super.enter();
UI.enter(); UI.enter();
prevPage.resetHover(); prevPage.resetHover();
nextPage.resetHover(); nextPage.resetHover();
@ -940,7 +959,6 @@ public class DownloadsMenu extends BasicGameState {
importButton.resetHover(); importButton.resetHover();
resetButton.resetHover(); resetButton.resetHover();
rankedButton.resetHover(); rankedButton.resetHover();
serverMenu.activate();
serverMenu.reset(); serverMenu.reset();
focusResult = -1; focusResult = -1;
startResultPos.setPosition(0); startResultPos.setPosition(0);
@ -954,10 +972,10 @@ public class DownloadsMenu extends BasicGameState {
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) public void leave() {
throws SlickException { super.leave();
search.setFocus(false);
serverMenu.deactivate(); focusComponent(search);
SoundController.stopTrack(); SoundController.stopTrack();
MusicController.resume(); MusicController.resume();
} }

View File

@ -553,8 +553,7 @@ public class MainMenu extends BaseOpsuState {
// downloads button actions // downloads button actions
if (downloadsButton.contains(x, y)) { if (downloadsButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
// TODO //displayContainer.switchState(DownloadsMenu.class); displayContainer.switchState(DownloadsMenu.class);
//game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition());
return true; return true;
} }
@ -659,9 +658,8 @@ public class MainMenu extends BaseOpsuState {
enterSongMenu(); enterSongMenu();
return true; return true;
case Input.KEY_D: case Input.KEY_D:
// TODO SoundController.playSound(SoundEffect.MENUHIT);
//SoundController.playSound(SoundEffect.MENUHIT); displayContainer.switchState(DownloadsMenu.class);
//game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition());
return true; return true;
case Input.KEY_R: case Input.KEY_R:
nextTrack(true); nextTrack(true);
@ -754,7 +752,7 @@ public class MainMenu extends BaseOpsuState {
Class<? extends OpsuState> state = SongMenu.class; Class<? extends OpsuState> state = SongMenu.class;
if (BeatmapSetList.get().getMapSetCount() == 0) { if (BeatmapSetList.get().getMapSetCount() == 0) {
instanceContainer.provide(DownloadsMenu.class).notifyOnLoad("Download some beatmaps to get started!"); instanceContainer.provide(DownloadsMenu.class).notifyOnLoad("Download some beatmaps to get started!");
// TODO d state = DownloadsMenu.class; state = DownloadsMenu.class;
} }
displayContainer.switchState(state); displayContainer.switchState(state);
} }

View File

@ -27,152 +27,62 @@ import org.newdawn.slick.Font;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.UnicodeFont; import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.gui.AbstractComponent; import yugecin.opsudance.core.DisplayContainer;
import org.newdawn.slick.gui.GUIContext; import yugecin.opsudance.core.components.Component;
public class DropdownMenu<E> extends Component {
/**
* 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>
*
* @param <E> the type of the elements in the menu
*/
public class DropdownMenu<E> extends AbstractComponent {
/** Padding ratios for drawing. */
private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f; private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f;
/** Whether this component is active. */ private final DisplayContainer displayContainer;
private boolean active;
/** The menu items. */
private E[] items; private E[] items;
/** The menu item names. */
private String[] itemNames; private String[] itemNames;
private int selectedItemIndex = 0;
private boolean expanded;
/** The index of the selected item. */
private int itemIndex = 0;
/** Whether the menu is expanded. */
private boolean expanded = false;
/** The expanding animation progress. */
private AnimatedValue expandProgress = new AnimatedValue(300, 0f, 1f, AnimationEquation.LINEAR); 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;
/** The width and height of the dropdown menu. */
private int width, height;
/** The height of the base item. */
private int baseHeight; private int baseHeight;
/** The vertical offset between items. */
private float offsetY; private float offsetY;
/** The colors to use. */ private Color textColor = Color.white;
private Color private Color backgroundColor = Color.black;
textColor = Color.white, backgroundColor = Color.black, private Color highlightColor = Colors.BLUE_DIVIDER;
highlightColor = Colors.BLUE_DIVIDER, borderColor = Colors.BLUE_DIVIDER, private Color borderColor = Colors.BLUE_DIVIDER;
chevronDownColor = textColor, chevronRightColor = backgroundColor; private Color chevronDownColor = textColor;
private Color chevronRightColor = backgroundColor;
/** The fonts to use. */ private UnicodeFont fontNormal = Fonts.MEDIUM;
private UnicodeFont fontNormal = Fonts.MEDIUM, fontSelected = Fonts.MEDIUMBOLD; private UnicodeFont fontSelected = Fonts.MEDIUMBOLD;
/** The chevron images. */ private Image chevronDown;
private Image chevronDown, chevronRight; private Image chevronRight;
/** Should the next click be blocked? */ public DropdownMenu(DisplayContainer displayContainer, E[] items, int x, int y, int width) {
private boolean blockClick = false; this.displayContainer = displayContainer;
/**
* 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(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(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(GUIContext container, E[] items, float x, float y, int width) {
super(container);
init(items, x, y, width); 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
* @param width the menu width
* @param normal the normal font
* @param selected the font for the selected item
*/
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);
}
/**
* Returns the maximum item width from the list.
*/
private int getMaxItemWidth() { private int getMaxItemWidth() {
int maxWidth = 0; int maxWidth = 0;
for (int i = 0; i < itemNames.length; i++) { for (String itemName : itemNames) {
int w = fontSelected.getWidth(itemNames[i]); int w = fontSelected.getWidth(itemName);
if (w > maxWidth) if (w > maxWidth) {
maxWidth = w; maxWidth = w;
}
} }
return maxWidth; return maxWidth;
} }
/** @SuppressWarnings("SuspiciousNameCombination")
* Initializes the component. private void init(E[] items, int x, int y, int width) {
*/
private void init(E[] items, float x, float y, int width) {
this.items = items; this.items = items;
this.itemNames = new String[items.length]; this.itemNames = new String[items.length];
for (int i = 0; i < itemNames.length; i++) for (int i = 0; i < itemNames.length; i++) {
itemNames[i] = items[i].toString(); itemNames[i] = items[i].toString();
}
this.x = x; this.x = x;
this.y = y; this.y = y;
this.baseHeight = fontNormal.getLineHeight(); this.baseHeight = fontNormal.getLineHeight();
@ -188,77 +98,27 @@ public class DropdownMenu<E> extends AbstractComponent {
} }
@Override @Override
public void setLocation(int x, int y) { public void updateHover(int x, int y) {
this.x = x; this.hovered = this.x <= x && x <= this.x + width && this.y <= y && y <= this.y + (expanded ? height : baseHeight);
this.y = y; }
public boolean baseContains(int x, int y) {
return (x > this.x && x < this.x + width && y > this.y && y < this.y + baseHeight);
} }
@Override @Override
public int getX() { return (int) x; } public void render(Graphics g) {
int delta = displayContainer.renderDelta;
@Override
public int getY() { return (int) y; }
@Override
public int getWidth() { return width; }
@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.
* @return true if open, false otherwise
*/
public boolean isOpen() { return expanded; }
/**
* Opens or closes the dropdown menu.
* @param flag true to open, false to close
*/
public void open(boolean flag) { this.expanded = flag; }
/**
* Returns true if the coordinates are within the menu bounds.
* @param cx the x coordinate
* @param cy the y coordinate
*/
public boolean contains(float cx, float cy) {
return (cx > x && cx < x + width && (
(cy > y && cy < y + baseHeight) ||
(expanded && cy > y + offsetY && cy < y + height)));
}
/**
* Returns true if the coordinates are within the base item bounds.
* @param cx the x coordinate
* @param cy the y coordinate
*/
public boolean baseContains(float cx, float cy) {
return (cx > x && cx < x + width && cy > y && cy < y + baseHeight);
}
@Override
public void render(GUIContext container, Graphics g) throws SlickException {
// update animation // update animation
long time = container.getTime(); expandProgress.update((expanded) ? delta : -delta * 2);
if (lastUpdateTime > 0) {
int delta = (int) (time - lastUpdateTime);
expandProgress.update((expanded) ? delta : -delta * 2);
}
this.lastUpdateTime = time;
// get parameters // get parameters
Input input = container.getInput(); int idx = getIndexAt(displayContainer.mouseY);
int idx = getIndexAt(input.getMouseX(), input.getMouseY());
float t = expandProgress.getValue(); float t = expandProgress.getValue();
if (expanded) if (expanded) {
t = AnimationEquation.OUT_CUBIC.calc(t); t = AnimationEquation.OUT_CUBIC.calc(t);
}
// background and border // background and border
Color oldGColor = g.getColor(); Color oldGColor = g.getColor();
@ -293,12 +153,12 @@ public class DropdownMenu<E> extends AbstractComponent {
// text // text
chevronDown.draw(x + width - chevronDown.getWidth() - width * CHEVRON_X, y + (baseHeight - chevronDown.getHeight()) / 2f, chevronDownColor); chevronDown.draw(x + width - chevronDown.getWidth() - width * CHEVRON_X, y + (baseHeight - chevronDown.getHeight()) / 2f, chevronDownColor);
fontNormal.drawString(x + (width * 0.03f), y + (fontNormal.getPaddingTop() + fontNormal.getPaddingBottom()) / 2f, itemNames[itemIndex], textColor); fontNormal.drawString(x + (width * 0.03f), y + (fontNormal.getPaddingTop() + fontNormal.getPaddingBottom()) / 2f, itemNames[selectedItemIndex], textColor);
float oldTextAlpha = textColor.a; float oldTextAlpha = textColor.a;
textColor.a *= t; textColor.a *= t;
if (expanded || t >= 0.0001) { if (expanded || t >= 0.0001) {
for (int i = 0; i < itemNames.length; i++) { for (int i = 0; i < itemNames.length; i++) {
Font f = (i == itemIndex) ? fontSelected : fontNormal; Font f = (i == selectedItemIndex) ? fontSelected : fontNormal;
if (i == idx && t >= 0.999) if (i == idx && t >= 0.999)
chevronRight.draw(x, y + offsetY + (offsetY * i) + (offsetY - chevronRight.getHeight()) / 2f, chevronRightColor); chevronRight.draw(x, y + offsetY + (offsetY * i) + (offsetY - chevronRight.getHeight()) / 2f, chevronRightColor);
f.drawString(x + chevronRight.getWidth(), y + offsetY + (offsetY * i * t), itemNames[i], textColor); f.drawString(x + chevronRight.getWidth(), y + offsetY + (offsetY * i * t), itemNames[i], textColor);
@ -310,131 +170,89 @@ public class DropdownMenu<E> extends AbstractComponent {
/** /**
* Returns the index of the item at the given location, -1 for the base item, * Returns the index of the item at the given location, -1 for the base item,
* and -2 if there is no item at the location. * and -2 if there is no item at the location.
* @param cx the x coordinate * @param y the y coordinate
* @param cy the y coordinate
*/ */
private int getIndexAt(float cx, float cy) { private int getIndexAt(int y) {
if (!contains(cx, cy)) if (!hovered) {
return -2; return -2;
if (cy <= y + baseHeight) }
if (y <= this.y + baseHeight) {
return -1; return -1;
if (!expanded) }
if (!expanded) {
return -2; return -2;
return (int) ((cy - (y + offsetY)) / offsetY); }
return (int) ((y - (this.y + offsetY)) / offsetY);
} }
/**
* Resets the menu state.
*/
public void reset() { public void reset() {
this.expanded = false; this.expanded = false;
this.lastUpdateTime = 0;
expandProgress.setTime(0); expandProgress.setTime(0);
blockClick = false; }
@Override
public void setFocused(boolean focused) {
super.setFocused(focused);
expanded = focused;
} }
@Override @Override
public void mousePressed(int button, int x, int y) { public boolean isFocusable() {
if (!active) return true;
return; }
if (button == Input.MOUSE_MIDDLE_BUTTON) @Override
return; public void mouseReleased(int button) {
super.mouseReleased(button);
int idx = getIndexAt(x, y); if (button == Input.MOUSE_MIDDLE_BUTTON) {
return;
}
int idx = getIndexAt(displayContainer.mouseY);
if (idx == -2) { if (idx == -2) {
this.expanded = false; this.expanded = false;
return; return;
} }
if (!menuClicked(idx)) if (!canSelect(selectedItemIndex)) {
return; return;
this.expanded = (idx == -1) ? !expanded : false;
if (idx >= 0 && itemIndex != idx) {
this.itemIndex = idx;
itemSelected(idx, items[idx]);
} }
blockClick = true; this.expanded = (idx == -1) && !expanded;
consumeEvent(); if (idx >= 0 && selectedItemIndex != idx) {
} this.selectedItemIndex = idx;
itemSelected(idx, items[selectedItemIndex]);
@Override
public void mouseClicked(int button, int x, int y, int clickCount) {
if (!active)
return;
if (blockClick) {
blockClick = false;
consumeEvent();
} }
} }
/** protected boolean canSelect(int index) {
* Notification that a new item was selected (via override). return true;
* @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
* @throws IllegalArgumentException if {@code index} is negative or greater than or equal to size
*/
public void setSelectedIndex(int index) {
if (index < 0 || index >= items.length)
throw new IllegalArgumentException();
this.itemIndex = index;
} }
/** protected void itemSelected(int index, E item) {
* Returns the index of the selected item. }
*/
public int getSelectedIndex() { return itemIndex; }
/** public E getSelectedItem() {
* Returns the selected item. return items[selectedItemIndex];
*/ }
public E getSelectedItem() { return items[itemIndex]; }
/** public void setBackgroundColor(Color c) {
* Returns the item at the given index. this.backgroundColor = c;
* @param index the list item index }
*/
public E getItemAt(int index) { return items[index]; }
/** public void setHighlightColor(Color c) {
* Returns the number of items in the list. this.highlightColor = c;
*/ }
public int getItemCount() { return items.length; }
/** Sets the text color. */ public void setBorderColor(Color c) {
public void setTextColor(Color c) { this.textColor = c; } this.borderColor = c;
}
/** Sets the background color. */ public void setChevronDownColor(Color c) {
public void setBackgroundColor(Color c) { this.backgroundColor = c; } this.chevronDownColor = c;
}
/** Sets the highlight color. */ public void setChevronRightColor(Color c) {
public void setHighlightColor(Color c) { this.highlightColor = c; } this.chevronRightColor = c;
}
/** Sets the border color. */
public void setBorderColor(Color c) { this.borderColor = c; }
/** Sets the down chevron color. */
public void setChevronDownColor(Color c) { this.chevronDownColor = c; }
/** Sets the right chevron color. */
public void setChevronRightColor(Color c) { this.chevronRightColor = c; }
} }

View File

@ -17,10 +17,7 @@
*/ */
package yugecin.opsudance.core.inject; package yugecin.opsudance.core.inject;
import itdelatrisu.opsu.states.ButtonMenu; import itdelatrisu.opsu.states.*;
import itdelatrisu.opsu.states.MainMenu;
import itdelatrisu.opsu.states.SongMenu;
import itdelatrisu.opsu.states.Splash;
import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.PreStartupInitializer;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
@ -59,6 +56,7 @@ public class OpsuDanceInjector extends Injector {
bind(MainMenu.class).asEagerSingleton(); bind(MainMenu.class).asEagerSingleton();
bind(ButtonMenu.class).asEagerSingleton(); bind(ButtonMenu.class).asEagerSingleton();
bind(SongMenu.class).asEagerSingleton(); bind(SongMenu.class).asEagerSingleton();
bind(DownloadsMenu.class).asEagerSingleton();
} }
} }