From 64788b2832276754a1890e134ad407ed8e1b61ab Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Fri, 18 Jul 2014 20:33:41 -0400 Subject: [PATCH] Song sort refactoring. - Created "SongSort" enum to handle everything related to sorting OsuGroupNode objects. Signed-off-by: Jeffrey Han --- src/itdelatrisu/opsu/GameMod.java | 6 +- src/itdelatrisu/opsu/OsuGroupList.java | 40 +---- src/itdelatrisu/opsu/OsuGroupNode.java | 65 +------ src/itdelatrisu/opsu/SongSort.java | 203 ++++++++++++++++++++++ src/itdelatrisu/opsu/Utils.java | 20 ++- src/itdelatrisu/opsu/states/SongMenu.java | 45 ++--- src/itdelatrisu/opsu/states/Splash.java | 3 +- 7 files changed, 235 insertions(+), 147 deletions(-) create mode 100644 src/itdelatrisu/opsu/SongSort.java diff --git a/src/itdelatrisu/opsu/GameMod.java b/src/itdelatrisu/opsu/GameMod.java index 48bd2abc..78ccaf10 100644 --- a/src/itdelatrisu/opsu/GameMod.java +++ b/src/itdelatrisu/opsu/GameMod.java @@ -59,13 +59,13 @@ public enum GameMod { /** * Total number of mods. */ - private static final int size = GameMod.values().length; + private static final int SIZE = GameMod.values().length; /** * Returns the total number of game mods. * @return the number of mods */ - public static int size() { return size; } + public static int size() { return SIZE; } /** * Returns an array of GameMod objects in reverse order. @@ -101,7 +101,7 @@ public enum GameMod { // find coordinates float offsetX = img.getWidth() * 1.5f; - float x = (width / 2f) - (offsetX * size / 2.75f); + float x = (width / 2f) - (offsetX * SIZE / 2.75f); float y = (height * 0.8f) + (img.getHeight() / 2); // create button diff --git a/src/itdelatrisu/opsu/OsuGroupList.java b/src/itdelatrisu/opsu/OsuGroupList.java index 4d4a282b..979325b2 100644 --- a/src/itdelatrisu/opsu/OsuGroupList.java +++ b/src/itdelatrisu/opsu/OsuGroupList.java @@ -30,24 +30,6 @@ import java.util.regex.Pattern; * Indexed, expanding, doubly-linked list data type for song groups. */ public class OsuGroupList { - /** - * Sorting orders. - */ - public static final byte - SORT_TITLE = 0, - SORT_ARTIST = 1, - SORT_CREATOR = 2, - SORT_BPM = 3, - SORT_LENGTH = 4, - SORT_MAX = 5; // not a sort - - /** - * Sorting order names (indexed by SORT_* constants). - */ - public static final String[] SORT_NAMES = { - "Title", "Artist", "Creator", "BPM", "Length" - }; - /** * Search pattern for conditional expressions. */ @@ -221,30 +203,14 @@ public class OsuGroupList { } /** - * Initializes the links in the list, given a sorting order (SORT_* constants). + * Initializes the links in the list. */ - public void init(byte order) { + public void init() { if (size() < 1) return; // sort the list - switch (order) { - case SORT_TITLE: - Collections.sort(nodes); - break; - case SORT_ARTIST: - Collections.sort(nodes, new OsuGroupNode.ArtistOrder()); - break; - case SORT_CREATOR: - Collections.sort(nodes, new OsuGroupNode.CreatorOrder()); - break; - case SORT_BPM: - Collections.sort(nodes, new OsuGroupNode.BPMOrder()); - break; - case SORT_LENGTH: - Collections.sort(nodes, new OsuGroupNode.LengthOrder()); - break; - } + Collections.sort(nodes, SongSort.getSort().getComparator()); expandedIndex = -1; // create links diff --git a/src/itdelatrisu/opsu/OsuGroupNode.java b/src/itdelatrisu/opsu/OsuGroupNode.java index 10df7c0a..4a283ab7 100644 --- a/src/itdelatrisu/opsu/OsuGroupNode.java +++ b/src/itdelatrisu/opsu/OsuGroupNode.java @@ -19,7 +19,6 @@ package itdelatrisu.opsu; import java.util.ArrayList; -import java.util.Comparator; import java.util.concurrent.TimeUnit; import org.newdawn.slick.Color; @@ -28,7 +27,7 @@ import org.newdawn.slick.Image; /** * Node in an OsuGroupList representing a group of OsuFile objects. */ -public class OsuGroupNode implements Comparable { +public class OsuGroupNode { /** * Menu background image. */ @@ -63,70 +62,10 @@ public class OsuGroupNode implements Comparable { this.osuFiles = osuFiles; } - /** - * Compares two OsuGroupNode objects by title. - */ - @Override - public int compareTo(OsuGroupNode that) { - return this.osuFiles.get(0).title.compareToIgnoreCase(that.osuFiles.get(0).title); - } - - /** - * Compares two OsuGroupNode objects by artist. - */ - public static class ArtistOrder implements Comparator { - @Override - public int compare(OsuGroupNode v, OsuGroupNode w) { - return v.osuFiles.get(0).artist.compareToIgnoreCase(w.osuFiles.get(0).artist); - } - } - - /** - * Compares two OsuGroupNode objects by creator. - */ - public static class CreatorOrder implements Comparator { - @Override - public int compare(OsuGroupNode v, OsuGroupNode w) { - return v.osuFiles.get(0).creator.compareToIgnoreCase(w.osuFiles.get(0).creator); - } - } - - /** - * Compares two OsuGroupNode objects by BPM. - */ - public static class BPMOrder implements Comparator { - @Override - public int compare(OsuGroupNode v, OsuGroupNode w) { - return Integer.compare(v.osuFiles.get(0).bpmMax, w.osuFiles.get(0).bpmMax); - } - } - - /** - * Compares two OsuGroupNode objects by length. - * Uses the longest beatmap in each set for comparison. - */ - public static class LengthOrder implements Comparator { - @Override - public int compare(OsuGroupNode v, OsuGroupNode w) { - int vMax = 0, wMax = 0; - for (OsuFile osu : v.osuFiles) { - if (osu.endTime > vMax) - vMax = osu.endTime; - } - for (OsuFile osu : w.osuFiles) { - if (osu.endTime > wMax) - wMax = osu.endTime; - } - return Integer.compare(vMax, wMax); - } - } - /** * Sets a button background image. */ - public static void setBackground(Image background) { - bg = background; - } + public static void setBackground(Image background) { bg = background; } /** * Draws the button. diff --git a/src/itdelatrisu/opsu/SongSort.java b/src/itdelatrisu/opsu/SongSort.java new file mode 100644 index 00000000..f5202ecb --- /dev/null +++ b/src/itdelatrisu/opsu/SongSort.java @@ -0,0 +1,203 @@ +/* + * opsu! - an open-source osu! client + * Copyright (C) 2014 Jeffrey Han + * + * opsu! 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. + * + * opsu! 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 opsu!. If not, see . + */ + +package itdelatrisu.opsu; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; + +import org.newdawn.slick.Color; +import org.newdawn.slick.Image; + +/** + * OsuGroupNode sorts. + */ +public enum SongSort { + TITLE (0, "Title", new TitleOrder()), + ARTIST (1, "Artist", new ArtistOrder()), + CREATOR (2, "Creator", new CreatorOrder()), + BPM (3, "BPM", new BPMOrder()), + LENGTH (4, "Length", new LengthOrder()); + + /** + * The ID of the sort (used for tab positioning). + */ + private int id; + + /** + * The name of the sort. + */ + private String name; + + /** + * The comparator for the sort. + */ + private Comparator comparator; + + /** + * The tab associated with the sort (displayed in Song Menu screen). + */ + private GUIMenuButton tab; + + /** + * Total number of sorts. + */ + private static final int SIZE = SongSort.values().length; + + /** + * Array of SongSort objects in reverse order. + */ + private static final SongSort[] VALUES_REVERSED; + static { + VALUES_REVERSED = SongSort.values(); + Collections.reverse(Arrays.asList(VALUES_REVERSED)); + } + + /** + * Current sort. + */ + private static SongSort currentSort = TITLE; + + /** + * Returns the current sort. + * @return the current sort + */ + public static SongSort getSort() { return currentSort; } + + /** + * Sets a new sort. + * @param sort the new sort + */ + public static void setSort(SongSort sort) { SongSort.currentSort = sort; } + + /** + * Draws all sort tabs. + */ + public static void drawAll() { + Image tabImage = currentSort.tab.getImage(); + float tabTextY = currentSort.tab.getY() - (tabImage.getHeight() / 2f); + for (SongSort sort : VALUES_REVERSED) { + float tabTextX = sort.tab.getX() - (Utils.FONT_MEDIUM.getWidth(sort.name) / 2); + tabImage.setAlpha((sort == currentSort) ? 1.0f : 0.7f); + sort.tab.draw(); + Utils.FONT_MEDIUM.drawString(tabTextX, tabTextY, sort.name, Color.white); + } + } + + /** + * Compares two OsuGroupNode objects by title. + */ + private static class TitleOrder implements Comparator { + @Override + public int compare(OsuGroupNode v, OsuGroupNode w) { + return v.osuFiles.get(0).title.compareToIgnoreCase(w.osuFiles.get(0).title); + } + } + + /** + * Compares two OsuGroupNode objects by artist. + */ + private static class ArtistOrder implements Comparator { + @Override + public int compare(OsuGroupNode v, OsuGroupNode w) { + return v.osuFiles.get(0).artist.compareToIgnoreCase(w.osuFiles.get(0).artist); + } + } + + /** + * Compares two OsuGroupNode objects by creator. + */ + private static class CreatorOrder implements Comparator { + @Override + public int compare(OsuGroupNode v, OsuGroupNode w) { + return v.osuFiles.get(0).creator.compareToIgnoreCase(w.osuFiles.get(0).creator); + } + } + + /** + * Compares two OsuGroupNode objects by BPM. + */ + private static class BPMOrder implements Comparator { + @Override + public int compare(OsuGroupNode v, OsuGroupNode w) { + return Integer.compare(v.osuFiles.get(0).bpmMax, w.osuFiles.get(0).bpmMax); + } + } + + /** + * Compares two OsuGroupNode objects by length. + * Uses the longest beatmap in each set for comparison. + */ + private static class LengthOrder implements Comparator { + @Override + public int compare(OsuGroupNode v, OsuGroupNode w) { + int vMax = 0, wMax = 0; + for (OsuFile osu : v.osuFiles) { + if (osu.endTime > vMax) + vMax = osu.endTime; + } + for (OsuFile osu : w.osuFiles) { + if (osu.endTime > wMax) + wMax = osu.endTime; + } + return Integer.compare(vMax, wMax); + } + } + + /** + * Constructor. + * @param id the ID of the sort (for tab positioning) + * @param name the sort name + * @param comparator the comparator for the sort + */ + SongSort(int id, String name, Comparator comparator) { + this.id = id; + this.name = name; + this.comparator = comparator; + } + + /** + * Initializes the sort tab. + * @param img the tab image + * @param width the container width + * @param height the container height + */ + public void init(Image img, int width, int height) { + float buttonX = width * 0.6f; + float tabOffset = (width - buttonX - img.getWidth()) / (SIZE - 1); + this.tab = new GUIMenuButton(img, + (buttonX + (img.getWidth() / 2f)) + (id * tabOffset), + (height * 0.15f) - (img.getHeight() / 2f) - 2f + ); + } + + /** + * Returns the comparator for the sort. + * @return the comparator + */ + public Comparator getComparator() { return comparator; } + + /** + * Checks if the coordinates are within the image bounds. + * @param x the x coordinate + * @param y the y coordinate + * @return true if within bounds + */ + public boolean contains(float x, float y) { return tab.contains(x, y); } +} \ No newline at end of file diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index 7c3ec7d7..29de0fdd 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -162,14 +162,6 @@ public class Utils { FONT_MEDIUM = new TrueTypeFont(font.deriveFont(fontBase * 3 / 2), false); FONT_SMALL = new TrueTypeFont(font.deriveFont(fontBase), false); - // set default game images - for (GameImage img : GameImage.values()) - img.setDefaultImage(); - - // initialize game mods - for (GameMod mod : GameMod.values()) - mod.init(width, height); - // tab image tab = new Image("selection-tab.png"); float tabScale = (height * 0.033f) / tab.getHeight(); @@ -182,6 +174,18 @@ public class Utils { backButton = new GUIMenuButton(back, back.getWidth() / 2f, height - (back.getHeight() / 2f)); + + // set default game images + for (GameImage img : GameImage.values()) + img.setDefaultImage(); + + // initialize game mods + for (GameMod mod : GameMod.values()) + mod.init(width, height); + + // initialize sorts + for (SongSort sort : SongSort.values()) + sort.init(tab, width, height); } /** diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 8dc0e60a..5d656093 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -22,9 +22,9 @@ import itdelatrisu.opsu.GUIMenuButton; import itdelatrisu.opsu.MusicController; import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.OsuFile; -import itdelatrisu.opsu.OsuGroupList; import itdelatrisu.opsu.OsuGroupNode; import itdelatrisu.opsu.OsuParser; +import itdelatrisu.opsu.SongSort; import itdelatrisu.opsu.SoundController; import itdelatrisu.opsu.Utils; @@ -89,16 +89,6 @@ public class SongMenu extends BasicGameState { buttonX, buttonY, buttonOffset, buttonWidth, buttonHeight; - /** - * Sorting tab buttons (indexed by SORT_* constants). - */ - private GUIMenuButton[] sortTabs; - - /** - * The current sort order (SORT_* constant). - */ - private byte currentSort = OsuGroupList.SORT_TITLE; - /** * The options button (to enter the "Game Options" menu). */ @@ -166,15 +156,6 @@ public class SongMenu extends BasicGameState { buttonHeight = menuBackground.getHeight(); buttonOffset = (height * 0.8f) / MAX_BUTTONS; - // sorting tabs - sortTabs = new GUIMenuButton[OsuGroupList.SORT_MAX]; - Image tab = Utils.getTabImage(); - float tabX = buttonX + (tab.getWidth() / 2f); - float tabY = (height * 0.15f) - (tab.getHeight() / 2f) - 2f; - float tabOffset = (width - buttonX - tab.getWidth()) / (sortTabs.length - 1); - for (int i = 0; i < sortTabs.length; i++) - sortTabs[i] = new GUIMenuButton(tab, tabX + (i * tabOffset), tabY); - // search searchTimer = 0; searchResultString = "Type to search!"; @@ -183,9 +164,11 @@ public class SongMenu extends BasicGameState { float iconScale = Utils.FONT_BOLD.getLineHeight() * 2f / searchIcon.getHeight(); searchIcon = searchIcon.getScaledCopy(iconScale); + Image tab = Utils.getTabImage(); search = new TextField( container, Utils.FONT_DEFAULT, - (int) tabX + searchIcon.getWidth(), (int) ((height * 0.15f) - (tab.getHeight() * 2.5f)), + (int) buttonX + (tab.getWidth() / 2) + searchIcon.getWidth(), + (int) ((height * 0.15f) - (tab.getHeight() * 2.5f)), (int) (buttonWidth / 2), Utils.FONT_DEFAULT.getHeight() ); search.setBackgroundColor(Color.transparent); @@ -264,13 +247,7 @@ public class SongMenu extends BasicGameState { optionsButton.draw(); // sorting tabs - float tabTextY = sortTabs[0].getY() - (sortTabs[0].getImage().getHeight() / 2f); - for (int i = sortTabs.length - 1; i >= 0; i--) { - sortTabs[i].getImage().setAlpha((i == currentSort) ? 1.0f : 0.7f); - sortTabs[i].draw(); - float tabTextX = sortTabs[i].getX() - (Utils.FONT_MEDIUM.getWidth(OsuGroupList.SORT_NAMES[i]) / 2); - Utils.FONT_MEDIUM.drawString(tabTextX, tabTextY, OsuGroupList.SORT_NAMES[i], Color.white); - } + SongSort.drawAll(); // search Utils.FONT_BOLD.drawString( @@ -324,7 +301,7 @@ public class SongMenu extends BasicGameState { // search produced new list: re-initialize it startNode = focusNode = null; if (Opsu.groups.size() > 0) { - Opsu.groups.init(currentSort); + Opsu.groups.init(); if (search.getText().isEmpty()) { // cleared search // use previous start/focus if possible if (oldFocusNode != null) @@ -385,15 +362,15 @@ public class SongMenu extends BasicGameState { return; // sorting buttons - for (byte i = 0; i < sortTabs.length; i++) { - if (sortTabs[i].contains(x, y)) { - if (i != currentSort) { - currentSort = i; + for (SongSort sort : SongSort.values()) { + if (sort.contains(x, y)) { + if (sort != SongSort.getSort()) { + SongSort.setSort(sort); SoundController.playSound(SoundController.SOUND_MENUCLICK); OsuGroupNode oldFocusBase = Opsu.groups.getBaseNode(focusNode.index); int oldFocusFileIndex = focusNode.osuFileIndex; focusNode = null; - Opsu.groups.init(i); + Opsu.groups.init(); setFocus(oldFocusBase, oldFocusFileIndex, true); } return; diff --git a/src/itdelatrisu/opsu/states/Splash.java b/src/itdelatrisu/opsu/states/Splash.java index afa86b9f..0e24f03d 100644 --- a/src/itdelatrisu/opsu/states/Splash.java +++ b/src/itdelatrisu/opsu/states/Splash.java @@ -19,7 +19,6 @@ package itdelatrisu.opsu.states; import itdelatrisu.opsu.Opsu; -import itdelatrisu.opsu.OsuGroupList; import itdelatrisu.opsu.OsuParser; import itdelatrisu.opsu.OszUnpacker; import itdelatrisu.opsu.SoundController; @@ -124,7 +123,7 @@ public class Splash extends BasicGameState { OsuParser.parseAllFiles(beatmapDir, width, height); // initialize song list - Opsu.groups.init(OsuGroupList.SORT_TITLE); + Opsu.groups.init(); menu.setFocus(Opsu.groups.getRandomNode(), -1, true); // load sounds