diff --git a/res/chevron-down.png b/res/chevron-down.png new file mode 100644 index 00000000..670b237f Binary files /dev/null and b/res/chevron-down.png differ diff --git a/res/chevron-right.png b/res/chevron-right.png new file mode 100644 index 00000000..76f6c651 Binary files /dev/null and b/res/chevron-right.png differ diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index e40d3874..51b93ab7 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -310,6 +310,7 @@ public enum GameImage { MUSIC_PAUSE ("music-pause", "png", false, false), MUSIC_NEXT ("music-next", "png", false, false), MUSIC_PREVIOUS ("music-previous", "png", false, false), + DOWNLOADS ("downloads", "png", false, false) { @Override protected Image process_sub(Image img, int w, int h) { @@ -361,6 +362,8 @@ public enum GameImage { return img.getScaledCopy(w, h); } }, + CHEVRON_DOWN ("chevron-down", "png", false, false), + CHEVRON_RIGHT ("chevron-right", "png", false, false), // TODO: ensure this image hasn't been modified (checksum?) ALPHA_MAP ("alpha", "png", false, false); diff --git a/src/itdelatrisu/opsu/downloads/servers/DownloadServer.java b/src/itdelatrisu/opsu/downloads/servers/DownloadServer.java index 24cca1f5..2e6834a8 100644 --- a/src/itdelatrisu/opsu/downloads/servers/DownloadServer.java +++ b/src/itdelatrisu/opsu/downloads/servers/DownloadServer.java @@ -75,4 +75,7 @@ public abstract class DownloadServer { public String getPreviewURL(int beatmapSetID) { return String.format(PREVIEW_URL, beatmapSetID); } + + @Override + public String toString() { return getName(); } } diff --git a/src/itdelatrisu/opsu/states/DownloadsMenu.java b/src/itdelatrisu/opsu/states/DownloadsMenu.java index c4559263..ec1d4e07 100644 --- a/src/itdelatrisu/opsu/states/DownloadsMenu.java +++ b/src/itdelatrisu/opsu/states/DownloadsMenu.java @@ -39,6 +39,7 @@ import itdelatrisu.opsu.downloads.servers.MengSkyServer; import itdelatrisu.opsu.downloads.servers.MnetworkServer; import itdelatrisu.opsu.downloads.servers.YaSOnlineServer; import itdelatrisu.opsu.ui.Colors; +import itdelatrisu.opsu.ui.DropdownMenu; import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.MenuButton; import itdelatrisu.opsu.ui.UI; @@ -83,9 +84,6 @@ public class DownloadsMenu extends BasicGameState { new MnetworkServer(), new MengSkyServer() }; - /** The beatmap download server index. */ - private int serverIndex = 0; - /** The current list of search results. */ private DownloadNode[] resultList; @@ -147,7 +145,10 @@ public class DownloadsMenu extends BasicGameState { private MenuButton prevPage, nextPage; /** Buttons. */ - private MenuButton clearButton, importButton, resetButton, rankedButton, serverButton; + private MenuButton clearButton, importButton, resetButton, rankedButton; + + /** Dropdown menu. */ + private DropdownMenu serverMenu; /** Beatmap importing thread. */ private Thread importThread; @@ -297,7 +298,6 @@ public class DownloadsMenu extends BasicGameState { float buttonHeight = height * 0.038f; float resetWidth = width * 0.085f; float rankedWidth = width * 0.15f; - float serverWidth = width * 0.12f; float lowerWidth = width * 0.12f; float topButtonY = searchY + Fonts.MEDIUM.getLineHeight() / 2f; float lowerButtonY = height * 0.995f - searchY - buttonHeight / 2f; @@ -309,11 +309,9 @@ public class DownloadsMenu extends BasicGameState { int lrButtonWidth = buttonL.getWidth() + buttonR.getWidth(); Image resetButtonImage = button.getScaledCopy((int) resetWidth - lrButtonWidth, (int) buttonHeight); Image rankedButtonImage = button.getScaledCopy((int) rankedWidth - lrButtonWidth, (int) buttonHeight); - Image serverButtonImage = button.getScaledCopy((int) serverWidth - lrButtonWidth, (int) buttonHeight); Image lowerButtonImage = button.getScaledCopy((int) lowerWidth - lrButtonWidth, (int) buttonHeight); float resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth; float rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth; - float serverButtonWidth = serverButtonImage.getWidth() + lrButtonWidth; float lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth; clearButton = new MenuButton(lowerButtonImage, buttonL, buttonR, width * 0.75f + buttonMarginX + lowerButtonWidth / 2f, lowerButtonY); @@ -323,8 +321,6 @@ public class DownloadsMenu extends BasicGameState { baseX + searchWidth + buttonMarginX + resetButtonWidth / 2f, topButtonY); rankedButton = new MenuButton(rankedButtonImage, buttonL, buttonR, baseX + searchWidth + buttonMarginX * 2f + resetButtonWidth + rankedButtonWidth / 2f, topButtonY); - serverButton = new MenuButton(serverButtonImage, buttonL, buttonR, - baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth + serverButtonWidth / 2f, topButtonY); clearButton.setText("Clear", Fonts.MEDIUM, Color.white); importButton.setText("Import All", Fonts.MEDIUM, Color.white); resetButton.setText("Reset", Fonts.MEDIUM, Color.white); @@ -332,7 +328,14 @@ public class DownloadsMenu extends BasicGameState { importButton.setHoverFade(); resetButton.setHoverFade(); rankedButton.setHoverFade(); - serverButton.setHoverFade(); + + // dropdown menu + int serverWidth = (int) (width * 0.12f); + serverMenu = new DropdownMenu(SERVERS, + baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth, searchY, serverWidth); + serverMenu.setBackgroundColor(Colors.BLACK_BG_HOVER); + serverMenu.setBorderColor(Color.black); + serverMenu.setChevronRightColor(Color.white); } @Override @@ -341,6 +344,7 @@ public class DownloadsMenu extends BasicGameState { int width = container.getWidth(); int height = container.getHeight(); int mouseX = input.getMouseX(), mouseY = input.getMouseY(); + boolean inDropdownMenu = serverMenu.contains(mouseX, mouseY); // background GameImage.SEARCH_BG.getImage().draw(); @@ -366,7 +370,7 @@ public class DownloadsMenu extends BasicGameState { int index = startResult + i; if (index >= nodes.length) break; - nodes[index].drawResult(g, i, DownloadNode.resultContains(mouseX, mouseY, i), + nodes[index].drawResult(g, i, DownloadNode.resultContains(mouseX, mouseY, i) && !inDropdownMenu, (index == focusResult), (previewID == nodes[index].getID())); } g.clearClip(); @@ -422,8 +426,9 @@ public class DownloadsMenu extends BasicGameState { resetButton.draw(Color.red); rankedButton.setText((rankedOnly) ? "Show Unranked" : "Hide Unranked", Fonts.MEDIUM, Color.white); rankedButton.draw(Color.magenta); - serverButton.setText(SERVERS[serverIndex].getName(), Fonts.MEDIUM, Color.white); - serverButton.draw(Color.blue); + + // dropdown menu + serverMenu.draw(g, mouseX, mouseY); // importing beatmaps if (importThread != null) { @@ -455,7 +460,7 @@ public class DownloadsMenu extends BasicGameState { importButton.hoverUpdate(delta, mouseX, mouseY); resetButton.hoverUpdate(delta, mouseX, mouseY); rankedButton.hoverUpdate(delta, mouseX, mouseY); - serverButton.hoverUpdate(delta, mouseX, mouseY); + serverMenu.update(delta); // focus timer if (focusResult != -1 && focusTimer < FOCUS_DELAY) @@ -469,7 +474,7 @@ public class DownloadsMenu extends BasicGameState { searchTimerReset = false; String query = search.getText().trim().toLowerCase(); - DownloadServer server = SERVERS[serverIndex]; + DownloadServer server = serverMenu.getSelectedItem(); if ((lastQuery == null || !query.equals(lastQuery)) && (query.length() == 0 || query.length() >= server.minQueryLength())) { lastQuery = query; @@ -493,7 +498,7 @@ public class DownloadsMenu extends BasicGameState { UI.updateTooltip(delta, "Reset the current search.", false); else if (rankedButton.contains(mouseX, mouseY)) UI.updateTooltip(delta, "Toggle the display of unranked maps.\nSome download servers may not support this option.", true); - else if (serverButton.contains(mouseX, mouseY)) + else if (serverMenu.baseContains(mouseX, mouseY)) UI.updateTooltip(delta, "Select a download server.", false); } @@ -518,6 +523,27 @@ public class DownloadsMenu extends BasicGameState { return; } + // dropdown menu + if (serverMenu.click(x, y)) { + SoundController.playSound(SoundEffect.MENUCLICK); + 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; + } + if (serverMenu.contains(x, y)) + return; + // search results DownloadNode[] nodes = resultList; if (nodes != null) { @@ -549,7 +575,7 @@ public class DownloadsMenu extends BasicGameState { } else { // play preview try { - final URL url = new URL(SERVERS[serverIndex].getPreviewURL(node.getID())); + final URL url = new URL(serverMenu.getSelectedItem().getPreviewURL(node.getID())); MusicController.pause(); new Thread() { @Override @@ -593,7 +619,7 @@ public class DownloadsMenu extends BasicGameState { } else { // start download if (!DownloadList.get().contains(node.getID())) { - node.createDownload(SERVERS[serverIndex]); + node.createDownload(serverMenu.getSelectedItem()); if (node.getDownload() == null) UI.sendBarNotification("The download could not be started."); else { @@ -704,24 +730,6 @@ public class DownloadsMenu extends BasicGameState { resetSearchTimer(); return; } - if (serverButton.contains(x, y)) { - SoundController.playSound(SoundEffect.MENUCLICK); - resultList = null; - startResult = 0; - focusResult = -1; - totalResults = 0; - page = 0; - pageResultTotal = 1; - pageDir = Page.RESET; - searchResultString = "Loading data from server..."; - serverIndex = (serverIndex + 1) % SERVERS.length; - lastQuery = null; - pageDir = Page.RESET; - if (searchQuery != null) - searchQuery.interrupt(); - resetSearchTimer(); - return; - } // downloads if (!DownloadList.get().isEmpty() && DownloadNode.downloadAreaContains(x, y)) { @@ -855,7 +863,7 @@ public class DownloadsMenu extends BasicGameState { importButton.resetHover(); resetButton.resetHover(); rankedButton.resetHover(); - serverButton.resetHover(); + serverMenu.reset(); focusResult = -1; startResult = 0; startDownloadIndex = 0; diff --git a/src/itdelatrisu/opsu/ui/DropdownMenu.java b/src/itdelatrisu/opsu/ui/DropdownMenu.java new file mode 100644 index 00000000..ac55efec --- /dev/null +++ b/src/itdelatrisu/opsu/ui/DropdownMenu.java @@ -0,0 +1,364 @@ +/* + * opsu! - an open-source osu! client + * Copyright (C) 2014, 2015 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.ui; + +import itdelatrisu.opsu.GameImage; +import itdelatrisu.opsu.ui.animations.AnimatedValue; +import itdelatrisu.opsu.ui.animations.AnimationEquation; + +import org.newdawn.slick.Color; +import org.newdawn.slick.Font; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Image; +import org.newdawn.slick.UnicodeFont; + +/** + * Simple dropdown menu. + */ +public class DropdownMenu { + /** Padding ratios for drawing. */ + private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f; + + /** The menu items. */ + private E[] items; + + /** The menu item names. */ + private String[] itemNames; + + /** 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); + + /** 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; + + /** The vertical offset between items. */ + private float offsetY; + + /** The colors to use. */ + private Color + textColor = Color.white, backgroundColor = Color.black, + highlightColor = Colors.BLUE_DIVIDER, borderColor = Colors.BLUE_DIVIDER, + chevronDownColor = textColor, chevronRightColor = backgroundColor; + + /** The fonts to use. */ + private UnicodeFont fontNormal = Fonts.MEDIUM, fontSelected = Fonts.MEDIUMBOLD; + + /** The chevron images. */ + private Image chevronDown, chevronRight; + + /** + * Creates a new dropdown 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); + } + + /** + * Creates a new dropdown menu with the given fonts. + * @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); + } + + /** + * Creates a new dropdown menu with the given width. + * @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) { + init(items, x, y, width); + } + + /** + * Creates a new dropdown menu with the given width and fonts. + * @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(E[] items, float x, float y, int width, UnicodeFont normal, UnicodeFont selected) { + this.fontNormal = normal; + this.fontSelected = selected; + init(items, x, y, width); + } + + /** + * Returns the maximum item width from the list. + */ + private int getMaxItemWidth() { + int maxWidth = 0; + for (int i = 0; i < itemNames.length; i++) { + int w = fontSelected.getWidth(itemNames[i]); + if (w > maxWidth) + maxWidth = w; + } + return maxWidth; + } + + /** + * Initializes the component. + */ + private void init(E[] items, float x, float y, int width) { + this.items = items; + this.itemNames = new String[items.length]; + for (int i = 0; i < itemNames.length; i++) + itemNames[i] = items[i].toString(); + this.x = x; + this.y = y; + this.baseHeight = fontNormal.getLineHeight(); + this.offsetY = baseHeight + baseHeight * PADDING_Y; + this.height = (int) (offsetY * (items.length + 1)); + int chevronDownSize = baseHeight * 4 / 5; + this.chevronDown = GameImage.CHEVRON_DOWN.getImage().getScaledCopy(chevronDownSize, chevronDownSize); + int chevronRightSize = baseHeight * 2 / 3; + this.chevronRight = GameImage.CHEVRON_RIGHT.getImage().getScaledCopy(chevronRightSize, chevronRightSize); + int maxItemWidth = getMaxItemWidth(); + int minWidth = maxItemWidth + chevronRight.getWidth() * 2; + this.width = Math.max(width, minWidth); + } + + /** + * Returns the width of the menu. + */ + public int getWidth() { return width; } + + /** + * Returns the height of the base item. + */ + public int getHeight() { return baseHeight; } + + /** + * Returns whether the dropdown menu is currently open. + * @return true if open, false otherwise + */ + public boolean isOpen() { return expanded; } + + /** + * 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); + } + + /** + * 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); + float t = expandProgress.getValue(); + if (expanded) + t = AnimationEquation.OUT_CUBIC.calc(t); + + // background and border + Color oldGColor = g.getColor(); + float oldLineWidth = g.getLineWidth(); + final int cornerRadius = 6; + g.setLineWidth(1f); + g.setColor((idx == -1) ? highlightColor : backgroundColor); + g.fillRoundRect(x, y, width, baseHeight, cornerRadius); + g.setColor(borderColor); + g.drawRoundRect(x, y, width, baseHeight, cornerRadius); + if (expanded || t >= 0.0001) { + float oldBackgroundAlpha = backgroundColor.a; + backgroundColor.a *= t; + g.setColor(backgroundColor); + g.fillRoundRect(x, y + offsetY, width, (height - offsetY) * t, cornerRadius); + backgroundColor.a = oldBackgroundAlpha; + } + if (idx >= 0 && t >= 0.9999) { + g.setColor(highlightColor); + float yPos = y + offsetY + (offsetY * idx); + int yOff = 0, hOff = 0; + if (idx == 0 || idx == items.length - 1) { + g.fillRoundRect(x, yPos, width, offsetY, cornerRadius); + if (idx == 0) + yOff = cornerRadius; + hOff = cornerRadius; + } + g.fillRect(x, yPos + yOff, width, offsetY - hOff); + } + g.setColor(oldGColor); + g.setLineWidth(oldLineWidth); + + // text + 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); + float oldTextAlpha = textColor.a; + textColor.a *= t; + if (expanded || t >= 0.0001) { + for (int i = 0; i < itemNames.length; i++) { + Font f = (i == itemIndex) ? fontSelected : fontNormal; + if (i == idx && t >= 0.999) + 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); + } + } + textColor.a = oldTextAlpha; + } + + /** + * 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. + * @param cx the x coordinate + * @param cy the y coordinate + */ + private int getIndexAt(float cx, float cy) { + if (!contains(cx, cy)) + return -2; + if (cy <= y + baseHeight) + return -1; + if (!expanded) + return -2; + 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, this will select that item and return {@code true}. + * Otherwise, this will return {@code false}. + * @param cx the x coordinate + * @param cy the y coordinate + */ + public boolean click(float cx, float cy) { + int idx = getIndexAt(cx, cy); + if (idx == -2) { + this.expanded = false; + return false; + } else if (idx == -1) { + this.expanded = !expanded; + return false; + } else { + this.expanded = false; + if (itemIndex == idx) + return false; + else { + itemIndex = idx; + return true; + } + } + } + + /** + * Resets the menu state. + */ + public void reset() { + expanded = false; + expandProgress.setTime(0); + } + + /** + * Selects the item at the given index. + * @param index the list item index + * @throws IllegalArgumentException if index < -1 or index is greater than or equal to size + */ + public void setSelectedIndex(int index) { + if (index < 0 || index >= items.length) + throw new IllegalArgumentException(); + this.itemIndex = index; + } + + /** + * Returns the index of the selected item. + */ + public int getSelectedIndex() { return itemIndex; } + + /** + * Returns the selected item. + */ + public E getSelectedItem() { return items[itemIndex]; } + + /** + * Returns the item at the given index. + * @param index the list item index + */ + public E getItemAt(int index) { return items[index]; } + + /** + * Returns the number of items in the list. + */ + public int getItemCount() { return items.length; } + + /** Sets the text color. */ + public void setTextColor(Color c) { this.textColor = c; } + + /** Sets the background color. */ + public void setBackgroundColor(Color c) { this.backgroundColor = c; } + + /** Sets the highlight color. */ + public void setHighlightColor(Color c) { this.highlightColor = 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; } +} diff --git a/src/itdelatrisu/opsu/ui/Fonts.java b/src/itdelatrisu/opsu/ui/Fonts.java index 6de998c9..14ecbdb0 100644 --- a/src/itdelatrisu/opsu/ui/Fonts.java +++ b/src/itdelatrisu/opsu/ui/Fonts.java @@ -40,7 +40,7 @@ import org.newdawn.slick.util.ResourceLoader; * Fonts used for drawing. */ public class Fonts { - public static UnicodeFont DEFAULT, BOLD, XLARGE, LARGE, MEDIUM, SMALL; + public static UnicodeFont DEFAULT, BOLD, XLARGE, LARGE, MEDIUM, MEDIUMBOLD, SMALL; /** Set of all Unicode strings already loaded per font. */ private static HashMap> loadedGlyphs = new HashMap>(); @@ -59,17 +59,19 @@ public class Fonts { Font javaFont = Font.createFont(Font.TRUETYPE_FONT, ResourceLoader.getResourceAsStream(Options.FONT_NAME)); Font font = javaFont.deriveFont(Font.PLAIN, (int) (fontBase * 4 / 3)); DEFAULT = new UnicodeFont(font); - BOLD = new UnicodeFont(font.deriveFont(Font.BOLD)); - XLARGE = new UnicodeFont(font.deriveFont(fontBase * 3)); - LARGE = new UnicodeFont(font.deriveFont(fontBase * 2)); - MEDIUM = new UnicodeFont(font.deriveFont(fontBase * 3 / 2)); - SMALL = new UnicodeFont(font.deriveFont(fontBase)); + BOLD = new UnicodeFont(font.deriveFont(Font.BOLD)); + XLARGE = new UnicodeFont(font.deriveFont(fontBase * 3)); + LARGE = new UnicodeFont(font.deriveFont(fontBase * 2)); + MEDIUM = new UnicodeFont(font.deriveFont(fontBase * 3 / 2)); + MEDIUMBOLD = new UnicodeFont(font.deriveFont(Font.BOLD, fontBase * 3 / 2)); + SMALL = new UnicodeFont(font.deriveFont(fontBase)); ColorEffect colorEffect = new ColorEffect(); loadFont(DEFAULT, colorEffect); loadFont(BOLD, colorEffect); loadFont(XLARGE, colorEffect); loadFont(LARGE, colorEffect); loadFont(MEDIUM, colorEffect); + loadFont(MEDIUMBOLD, colorEffect); loadFont(SMALL, colorEffect); }