From 7e135550e029c8be39e27329a4999ba9e703376b Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 29 Jan 2017 11:28:24 +0100 Subject: [PATCH] enchanced optionmenu --- res/control-sliderball.png | Bin 348 -> 430 bytes src/itdelatrisu/opsu/GameImage.java | 1 + src/itdelatrisu/opsu/Options.java | 30 + src/itdelatrisu/opsu/states/Game.java | 2 +- src/itdelatrisu/opsu/states/OptionsMenu.java | 95 ++- src/itdelatrisu/opsu/states/SongMenu.java | 2 +- src/org/newdawn/slick/gui/TextField.java | 25 +- src/yugecin/opsudance/ui/OptionsOverlay.java | 634 ++++++++++++++----- src/yugecin/opsudance/utils/FontUtil.java | 35 + 9 files changed, 625 insertions(+), 199 deletions(-) create mode 100644 src/yugecin/opsudance/utils/FontUtil.java diff --git a/res/control-sliderball.png b/res/control-sliderball.png index 92625731ecf7c3eb5f5a5c5007ccfd3b4176103d..8d65268b27240bc2409cd1546731e944c24e9610 100644 GIT binary patch delta 348 zcmV-i0i*uh0&Y4#EKyC`y2lQzm}_EJ;K`R5(wql_3(sKnz7IDiBB{ z2S9QN1OkCTB6(hfgE6VGuKM6*1w1Z!O*r_~K4H1-gs9Sc!?43RZRy%prVUTO|3-Bz|0W{u|1Ka|TBnkm@Qb$4nuFf3kks%uj!vFvd!vV){sAQ2wCVv3UNkl|5JV|baKIEPD7aI~9D#x(QBdR$pAwVfTjduJh69eF 0) { - scrollOffset += Fonts.MEDIUM.getLineHeight() * 2; - scrollOffset += tab.options.length * optionHeight; - } - */ - maxScrollOffset += tab.options.length * optionHeight; - tab.button = new MenuButton(tabImage, tabX, tabY); - tabX += tabOffset; - if (tabX + tabOffset > width) { - tabX = 0; - tabY += GameImage.MENU_TAB.getImage().getHeight() / 2f; - } - } - maxScrollOffset += -optionStartY - optionHeight; - - // calculate other positions - optionStartY = (int) (tabY + tabImage.getHeight() / 2 + 2); // +2 for the separator line } @Override public void onRender(Graphics g) { + g.setClip(0, 0, width, height); + // bg - g.setColor(Colors.BLACK_ALPHA_75); + g.setColor(COL_BG); g.fillRect(0, 0, width, height); // title renderTitle(); - // option tabs - renderTabs(); - - // line separator - g.setColor(Color.white); - g.setLineWidth(2f); - g.drawLine(0, optionStartY - 1, width, optionStartY - 1); - g.resetLineWidth(); + renderIndicator(g); // options renderOptions(g); - if (isListOptionOpen) { - renderOpenList(g); - } + renderOpenList(g); + + renderSearch(g); // scrollbar - g.setColor(Color.white); - g.fillRoundRect(optionStartX + optionWidth + 15, optionStartY + ((float) scrollOffset / (maxScrollOffset)) * (height - optionStartY - 45), 10, 45, 2); + g.setColor(COL_WHITE); + g.fillRect(width - 5, ((float) scrollOffset / (maxScrollOffset)) * (height - 45), 5, 45); g.clearClip(); // UI @@ -180,184 +230,301 @@ public class OptionsOverlay extends OverlayOpsuState { } } + private void renderIndicator(Graphics g) { + g.setColor(COL_INDICATOR); + int indicatorPos = this.indicatorPos; + if (indicatorMoveAnimationTime > 0) { + indicatorMoveAnimationTime += displayContainer.renderDelta; + if (indicatorMoveAnimationTime > INDICATORMOVEANIMATIONTIME) { + indicatorMoveAnimationTime = 0; + indicatorPos += indicatorOffsetToNextPos; + indicatorOffsetToNextPos = 0; + this.indicatorPos = indicatorPos; + } else { + indicatorPos += AnimationEquation.OUT_BACK.calc((float) indicatorMoveAnimationTime / INDICATORMOVEANIMATIONTIME) * indicatorOffsetToNextPos; + } + } + g.fillRect(0, indicatorPos - scrollOffset, width, optionHeight); + } + private void renderKeyEntry(Graphics g) { - g.setColor(Colors.BLACK_ALPHA_75); - g.fillRect(0, 0, width, height); - g.setColor(Color.white); + g.setColor(COL_BG); + g.fillRect(0, 0, displayContainer.width, height); + g.setColor(COL_WHITE); String prompt = (keyEntryLeft) ? "Please press the new left-click key." : "Please press the new right-click key."; - Fonts.LARGE.drawString((width - Fonts.LARGE.getWidth(prompt)) / 2, (height - Fonts.LARGE.getLineHeight()) / 2, prompt); + int y = (displayContainer.height - Fonts.LARGE.getLineHeight()) / 2; + FontUtil.drawCentered(Fonts.LARGE, displayContainer.width, 0, y, prompt, COL_WHITE); } private void renderTooltip(Graphics g) { if (hoverOption != null) { - UI.updateTooltip(displayContainer.renderDelta, hoverOption.getDescription(), false); + String tip = hoverOption.getDescription(); + if (hoverOption.getType() == OptionType.NUMERIC) { + tip = "(" + hoverOption.getValueString() + ") " + tip; + } + UI.updateTooltip(displayContainer.renderDelta, tip, false); UI.drawTooltip(g); } } private void renderOptions(Graphics g) { - g.setClip(0, optionStartY, width, height - optionStartY); - listStartX = listStartY = listWidth = listHeight = 0; // render out of the screen int y = -scrollOffset + optionStartY; - selectedTab = 0; - maxScrollOffset = Fonts.MEDIUM.getLineHeight() * 2 * tabs.length; + maxScrollOffset = optionStartY; boolean render = true; - for (int tabIndex = 0; tabIndex < tabs.length; tabIndex++) { - OptionTab tab = tabs[tabIndex]; - if (y > 0) { - if (render) { - int x = optionStartX + (optionWidth - Fonts.LARGE.getWidth(tab.name)) / 2; - Fonts.LARGE.drawString(x, y + Fonts.LARGE.getLineHeight() * 0.6f, tab.name, Color.cyan); - } - } else { - selectedTab++; + int lastNonSkippedSectionIndex = 0; + int sectionIndex = 0; + for (; sectionIndex < sections.length; sectionIndex++) { + lastNonSkippedSectionIndex = sectionIndex; + OptionTab section = sections[sectionIndex]; + if (section.filtered) { + continue; } - y += Fonts.MEDIUM.getLineHeight() * 2; - for (int optionIndex = 0; optionIndex < tab.options.length; optionIndex++) { - GameOption option = tab.options[optionIndex]; - if (!option.showCondition()) { + int lineStartY = (int) (y + Fonts.LARGE.getLineHeight() * 0.6f); + if (render) { + if (section.options == null) { + FontUtil.drawRightAligned(Fonts.XLARGE, width, -paddingRight, (int) (y + Fonts.XLARGE.getLineHeight() * 0.3f), section.name, COL_CYAN); + } else { + Fonts.MEDIUMBOLD.drawString(paddingTextLeft, lineStartY, section.name, COL_WHITE); + } + } + y += Fonts.LARGE.getLineHeight() * 1.5f; + maxScrollOffset += Fonts.LARGE.getLineHeight() * 1.5f; + if (section.options == null) { + continue; + } + int lineHeight = (int) (Fonts.LARGE.getLineHeight() * 0.9f); + for (int optionIndex = 0; optionIndex < section.options.length; optionIndex++) { + GameOption option = section.options[optionIndex]; + if (!option.showCondition() || option.isFiltered()) { continue; } - maxScrollOffset += optionHeight; - if ((y > 0 && render) || (isListOptionOpen && hoverOption == option)) { - renderOption(g, option, y, option == hoverOption); + if ((y > -optionHeight && render) || (isListOptionOpen && hoverOption == option)) { + renderOption(g, option, y); } y += optionHeight; + maxScrollOffset += optionHeight; + lineHeight += optionHeight; if (y > height) { render = false; - tabIndex = tabs.length; + sectionIndex = sections.length; + maxScrollOffset += (section.options.length - optionIndex - 1) * optionHeight; } } + g.setColor(COL_GREY); + g.fillRect(paddingLeft, lineStartY, LINEWIDTH, lineHeight); + } + // iterate over skipped options to correctly calculate max scroll offset + for (sectionIndex = lastNonSkippedSectionIndex + 1; sectionIndex < sections.length; sectionIndex++) { + maxScrollOffset += Fonts.LARGE.getLineHeight() * 1.5f; + if (sections[sectionIndex].options != null) { + maxScrollOffset += sections[sectionIndex].options.length * optionHeight; + } + } + maxScrollOffset -= height * 2 / 3; + if (maxScrollOffset < 0) { + maxScrollOffset = 0; + } + if (scrollOffset > maxScrollOffset) { + scrollOffset = maxScrollOffset; } - maxScrollOffset -= optionStartY - optionHeight * 2; } private void renderOpenList(Graphics g) { - g.setColor(Colors.BLACK_ALPHA_85); - g.fillRect(listStartX, listStartY, listWidth, listHeight); - if (listHoverIndex != -1) { - g.setColor(Colors.ORANGE_BUTTON); - g.fillRect(listStartX, listStartY + listHoverIndex * listItemHeight, listWidth, listItemHeight); + if (!isListOptionOpen && listAnimationTime == 0) { + return; + } + float progress; + int listItemHeight; + if (isListOptionOpen) { + listAnimationTime = Math.min(LISTOPENANIMATIONTIME, listAnimationTime + displayContainer.renderDelta); + progress = (float) listAnimationTime / LISTOPENANIMATIONTIME; + } else { + listAnimationTime -= displayContainer.renderDelta; + if (listAnimationTime <= 0) { + listAnimationTime = 0; + return; + } + progress = (float) listAnimationTime / LISTCLOSEANIMATIONTIME; + } + listItemHeight = (int) (this.listItemHeight * progress); + listHeight = (int) (this.listHeight * progress); + final int borderRadius = 6; + float blackAlphaA = Colors.BLACK_ALPHA_85.a; + float whiteA = COL_WHITE.a; + float bgA = COL_BG.a; + COL_WHITE.a *= progress; + COL_BG.a *= progress; + Colors.BLACK_ALPHA_85.a *= progress; + g.setColor(Colors.BLACK_ALPHA_85); + g.fillRoundRect(listStartX, listStartY, listWidth, listHeight, borderRadius, 15); + Object[] listItems = selectedListOption.getListItems(); + if (listHoverIndex != -1) { + g.setColor(COL_COMBOBOX_HOVER); + if (listHoverIndex == 0) { + g.fillRoundRect(listStartX, listStartY + listHoverIndex * listItemHeight, listWidth, listItemHeight, borderRadius, 15); + g.fillRect(listStartX, listStartY + listHoverIndex * listItemHeight + listItemHeight / 2, listWidth, listItemHeight / 2); + } else if (listHoverIndex == listItems.length - 1) { + g.fillRoundRect(listStartX, listStartY + listHoverIndex * listItemHeight, listWidth, listItemHeight, borderRadius, 15); + g.fillRect(listStartX, listStartY + listHoverIndex * listItemHeight, listWidth, listItemHeight / 2); + } else { + g.fillRect(listStartX, listStartY + listHoverIndex * listItemHeight, listWidth, listItemHeight); + } + chevronRightImg.draw(listStartX + 2, listStartY + listHoverIndex * listItemHeight + (listItemHeight - controlImageSize) / 2, COL_BG); } - g.setLineWidth(1f); - g.setColor(Color.white); - g.drawRect(listStartX, listStartY, listWidth, listHeight); - Object[] listItems = hoverOption.getListItems(); int y = listStartY; + String selectedValue = selectedListOption.getValueString(); for (Object item : listItems) { - Fonts.MEDIUM.drawString(listStartX + 20, y - Fonts.MEDIUM.getLineHeight() * 0.05f, item.toString()); + String text = item.toString(); + Font font; + if (text.equals(selectedValue)) { + font = Fonts.MEDIUMBOLD; + } else { + font = Fonts.MEDIUM; + } + font.drawString(listStartX + 20, y - Fonts.MEDIUM.getLineHeight() * 0.05f, item.toString(), COL_WHITE); y += listItemHeight; } + COL_BG.a = bgA; + COL_WHITE.a = whiteA; + Colors.BLACK_ALPHA_85.a = blackAlphaA; } - private void renderOption(Graphics g, GameOption option, int y, boolean focus) { - Color col = focus ? Colors.GREEN : Colors.WHITE_FADE; + private void renderOption(Graphics g, GameOption option, int y) { OptionType type = option.getType(); Object[] listItems = option.getListItems(); if (listItems != null) { - renderListOption(g, option, y, col, listItems); + renderListOption(g, option, y, listItems); } else if (type == OptionType.BOOLEAN) { - renderCheckOption(g, option, y, col); + renderCheckOption(option, y); } else if (type == OptionType.NUMERIC) { - renderSliderOption(g, option, y, col); + renderSliderOption(g, option, y); } else { - renderGenericOption(g, option, y, col); + renderGenericOption(option, y); } } - private void renderListOption(Graphics g, GameOption option, int y, Color textColor, Object[] listItems) { + private void renderListOption(Graphics g, GameOption option, int y, Object[] listItems) { int nameLen = Fonts.MEDIUM.getWidth(option.getName()); - Fonts.MEDIUM.drawString(optionStartX, y, option.getName(), textColor); - int padding = (int) (optionHeight / 10f); - nameLen += 20; - int itemStart = optionStartX + nameLen; - int itemWidth = optionWidth - nameLen; - Color backColor = Colors.BLACK_ALPHA; - if (hoverOption == option && listHoverIndex == -1) { - backColor = Colors.ORANGE_BUTTON; + Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.getName(), COL_WHITE); + final int padding = (int) (optionHeight / 10f); + nameLen += 15; + final int comboboxStartX = optionStartX + nameLen; + final int comboboxWidth = optionWidth - nameLen; + final int borderRadius = 6; + if (comboboxWidth <= controlImageSize) { + return; + } + Color backColor = COL_BG; + if (hoverOption == option + && comboboxStartX <= displayContainer.mouseX && displayContainer.mouseX < comboboxStartX + comboboxWidth + && y + padding <= displayContainer.mouseY && displayContainer.mouseY < y + padding + listItemHeight) { + backColor = COL_COMBOBOX_HOVER; } g.setColor(backColor); - g.fillRect(itemStart, y + padding, itemWidth, listItemHeight); - g.setColor(Color.white); - g.setLineWidth(1f); - g.drawRect(itemStart, y + padding, itemWidth, listItemHeight); - Fonts.MEDIUM.drawString(itemStart + 20, y, option.getValueString(), Color.white); + g.fillRoundRect(comboboxStartX, y + padding, comboboxWidth, listItemHeight, borderRadius, 15); + Fonts.MEDIUM.drawString(comboboxStartX + 4, y + optionTextOffsetY, option.getValueString(), COL_WHITE); + chevronDownImg.draw(width - paddingRight - controlImageSize / 3 - controlImageSize, y + controlImagePadding, COL_WHITE); if (isListOptionOpen && hoverOption == option) { - listStartX = optionStartX + nameLen; - listStartY = y + padding + listItemHeight; - listWidth = itemWidth; + listStartX = comboboxStartX; + listStartY = y + optionHeight; + listWidth = comboboxWidth; listHeight = listItems.length * listItemHeight; } } - private void renderCheckOption(Graphics g, GameOption option, int y, Color textColor) { + private void renderCheckOption(GameOption option, int y) { if (option.getBooleanValue()) { - checkOnImg.draw(optionStartX, y + optionHeight / 2 - 10, Color.pink); + checkOnImg.draw(optionStartX, y + controlImagePadding, COL_PINK); } else { - checkOffImg.draw(optionStartX, y + optionHeight / 2 - 10, Color.pink); + checkOffImg.draw(optionStartX, y + controlImagePadding, COL_PINK); } - Fonts.MEDIUM.drawString(optionStartX + 30, y, option.getName(), textColor); + Fonts.MEDIUM.drawString(optionStartX + 30, y + optionTextOffsetY, option.getName(), COL_WHITE); } - private void renderSliderOption(Graphics g, GameOption option, int y, Color textColor) { - String value = option.getValueString(); + private void renderSliderOption(Graphics g, GameOption option, int y) { + final int padding = 10; int nameLen = Fonts.MEDIUM.getWidth(option.getName()); - int valueLen = Fonts.MEDIUM.getWidth(value); - Fonts.MEDIUM.drawString(optionStartX, y, option.getName(), textColor); - Fonts.MEDIUM.drawString(optionStartX + optionWidth - valueLen, y, value, Colors.BLUE_BACKGROUND); - int sliderLen = optionWidth - nameLen - valueLen - 50; + Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.getName(), COL_WHITE); + int sliderLen = optionWidth - nameLen - padding; + if (sliderLen <= 1) { + return; + } + int sliderStartX = optionStartX + nameLen + padding; + int sliderEndX = optionStartX + optionWidth; if (hoverOption == option) { if (!isAdjustingSlider) { sliderOptionLength = sliderLen; - sliderOptionStartX = optionStartX + nameLen + 25; + sliderOptionStartX = sliderStartX; } else { sliderLen = sliderOptionLength; } } - g.setColor(Color.pink); + float sliderValue = (float) (option.getIntegerValue() - option.getMinValue()) / (option.getMaxValue() - option.getMinValue()); + float sliderBallPos = sliderStartX + (int) ((sliderLen - controlImageSize) * sliderValue); + g.setLineWidth(3f); - g.drawLine(optionStartX + nameLen + 25, y + optionHeight / 2, optionStartX + nameLen + 25 + sliderLen, y + optionHeight / 2); - float sliderValue = (float) (sliderLen + 10) * (option.getIntegerValue() - option.getMinValue()) / (option.getMaxValue() - option.getMinValue()); - sliderBallImg.draw(optionStartX + nameLen + 25 + sliderValue - 10, y + optionHeight / 2 - 10, Color.pink); - } - - private void renderGenericOption(Graphics g, GameOption option, int y, Color textColor) { - String value = option.getValueString(); - int valueLen = Fonts.MEDIUM.getWidth(value); - Fonts.MEDIUM.drawString(optionStartX, y, option.getName(), textColor); - Fonts.MEDIUM.drawString(optionStartX + optionWidth - valueLen, y, value, Colors.BLUE_BACKGROUND); - } - - public void renderTabs() { - for (int i = 0; i < tabs.length; i++) { - OptionTab tab = tabs[i]; - boolean hovering = tab.button.contains(displayContainer.mouseX, displayContainer.mouseY); - UI.drawTab(tab.button.getX(), tab.button.getY(), tab.name, i == selectedTab, hovering); + g.setColor(COL_PINK); + if (sliderValue > 0.0001f) { + g.drawLine(sliderStartX, y + optionHeight / 2, sliderBallPos, y + optionHeight / 2); + } + sliderBallImg.draw(sliderBallPos, y + controlImagePadding, COL_PINK); + if (sliderValue < 0.999f) { + float a = COL_PINK.a; + COL_PINK.a *= 0.45f; + g.setColor(COL_PINK); + g.drawLine(sliderBallPos + controlImageSize, y + optionHeight / 2, sliderEndX, y + optionHeight / 2); + COL_PINK.a = a; } } + private void renderGenericOption(GameOption option, int y) { + String value = option.getValueString(); + int valueLen = Fonts.MEDIUM.getWidth(value); + Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.getName(), COL_WHITE); + Fonts.MEDIUM.drawString(optionStartX + optionWidth - valueLen, y + optionTextOffsetY, value, COL_BLUE); + } + private void renderTitle() { - float marginX = width * 0.015f; - float marginY = height * 0.01f; - Fonts.XLARGE.drawString(marginX, marginY, "Options", Color.white); - marginX += Fonts.XLARGE.getWidth("Options") * 1.2f; - marginY += Fonts.XLARGE.getLineHeight() * 0.9f - Fonts.DEFAULT.getLineHeight(); - Fonts.DEFAULT.drawString(marginX, marginY, "Change the way opsu! behaves", Color.white); + FontUtil.drawCentered(Fonts.LARGE, width, 0, textOptionsY - scrollOffset, "Options", COL_WHITE); + FontUtil.drawCentered(Fonts.MEDIUM, width, 0, textChangeY - scrollOffset, "Change the way opsu! behaves", COL_PINK); + } + + private void renderSearch(Graphics g) { + int ypos = posSearchY + textSearchYOffset - scrollOffset; + if (scrollOffset > posSearchY) { + ypos = textSearchYOffset; + g.setColor(COL_BG); + g.fillRect(0, 0, width, textSearchYOffset * 2 + Fonts.LARGE.getLineHeight()); + } + String searchText = "Type to search!"; + if (lastSearchText.length() > 0) { + searchText = lastSearchText; + } + FontUtil.drawCentered(Fonts.LARGE, width, 0, ypos, searchText, COL_WHITE); + int imgPosX = (width - Fonts.LARGE.getWidth(searchText)) / 2 - searchImg.getWidth() - 10; + searchImg.draw(imgPosX, ypos + Fonts.LARGE.getLineHeight() * 0.25f, COL_WHITE); } @Override public void hide() { acceptInput = false; SoundController.playSound(SoundEffect.MENUBACK); - active = false; + hideAnimationTime = animationtime; + hideAnimationStartProgress = (float) animationtime / SHOWANIMATIONTIME; } @Override public void show() { + indicatorPos = -optionHeight; + indicatorOffsetToNextPos = 0; + indicatorMoveAnimationTime = 0; + indicatorHideAnimationTime = 0; acceptInput = true; active = true; + animationtime = 0; + resetSearch(); } @Override @@ -366,15 +533,25 @@ public class OptionsOverlay extends OverlayOpsuState { int mouseY = displayContainer.mouseY; int delta = displayContainer.renderDelta; + searchField.performKeyRepeat(); + + updateShowHideAnimation(delta); + if (animationtime <= 0) { + active = false; + return; + } + if (sliderSoundDelay > 0) { sliderSoundDelay -= delta; } if (mouseX - prevMouseX == 0 && mouseY - prevMouseY == 0) { + updateIndicatorAlpha(); return; } prevMouseX = mouseX; prevMouseY = mouseY; updateHoverOption(mouseX, mouseY); + updateIndicatorAlpha(); UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); if (isAdjustingSlider) { int sliderValue = hoverOption.getIntegerValue(); @@ -392,6 +569,59 @@ public class OptionsOverlay extends OverlayOpsuState { } } + private void updateIndicatorAlpha() { + if (hoverOption == null) { + if (indicatorHideAnimationTime < INDICATORHIDEANIMATIONTIME) { + indicatorHideAnimationTime += displayContainer.renderDelta; + if (indicatorHideAnimationTime > INDICATORHIDEANIMATIONTIME) { + indicatorHideAnimationTime = INDICATORHIDEANIMATIONTIME; + } + float progress = AnimationEquation.IN_CUBIC.calc((float) indicatorHideAnimationTime / INDICATORHIDEANIMATIONTIME); + COL_INDICATOR.a = (1f - progress) * INDICATOR_ALPHA; + } + } else if (indicatorHideAnimationTime > 0) { + indicatorHideAnimationTime -= displayContainer.renderDelta * 3; + if (indicatorHideAnimationTime < 0) { + indicatorHideAnimationTime = 0; + } + COL_INDICATOR.a = (1f - (float) indicatorHideAnimationTime / INDICATORHIDEANIMATIONTIME) * INDICATOR_ALPHA; + } + } + + private void updateShowHideAnimation(int delta) { + if (acceptInput && animationtime >= SHOWANIMATIONTIME) { + // animation already finished + width = finalWidth; + return; + } + optionWidth = width - optionStartX - paddingRight; + + // if acceptInput is false, it means that we're currently hiding ourselves + float progress; + if (acceptInput) { + animationtime += delta; + if (animationtime >= SHOWANIMATIONTIME) { + animationtime = SHOWANIMATIONTIME; + } + progress = AnimationEquation.OUT_EXPO.calc((float) animationtime / SHOWANIMATIONTIME); + } else { + animationtime -= delta; + if (animationtime < 0) { + animationtime = 0; + } + progress = hideAnimationStartProgress * AnimationEquation.IN_EXPO.calc((float) animationtime / hideAnimationTime); + } + width = (int) (progress * finalWidth); + COL_BG.a = BG_ALPHA * progress; + COL_WHITE.a = progress; + COL_PINK.a = progress; + COL_CYAN.a = progress; + COL_GREY.a = progress * LINEALPHA; + COL_BLUE.a = progress; + COL_COMBOBOX_HOVER.a = progress; + COL_INDICATOR.a = progress * (1f - (float) indicatorHideAnimationTime / INDICATORHIDEANIMATIONTIME) * INDICATOR_ALPHA; + } + @Override public boolean onMousePressed(int button, int x, int y) { if (keyEntryLeft || keyEntryRight) { @@ -400,7 +630,7 @@ public class OptionsOverlay extends OverlayOpsuState { } if (isListOptionOpen) { - if (y > optionStartY && listStartX <= x && x < listStartX + listWidth && listStartY <= y && y < listStartY + listHeight) { + if (listStartX <= x && x < listStartX + listWidth && listStartY <= y && y < listStartY + listHeight) { if (0 <= listHoverIndex && listHoverIndex < hoverOption.getListItems().length) { hoverOption.clickListItem(listHoverIndex); if (listener != null) { @@ -410,9 +640,14 @@ public class OptionsOverlay extends OverlayOpsuState { SoundController.playSound(SoundEffect.MENUCLICK); } isListOptionOpen = false; + listAnimationTime = LISTCLOSEANIMATIONTIME; listHoverIndex = -1; updateHoverOption(x, y); - return true; + return false; + } + + if (x > width) { + return false; } mousePressY = y; @@ -421,6 +656,8 @@ public class OptionsOverlay extends OverlayOpsuState { if (hoverOption != null) { if (hoverOption.getListItems() != null) { isListOptionOpen = true; + selectedListOption = hoverOption; + listAnimationTime = 0; } else if (hoverOption.getType() == OptionType.NUMERIC) { isAdjustingSlider = sliderOptionStartX <= x && x < sliderOptionStartX + sliderOptionLength; if (isAdjustingSlider) { @@ -446,6 +683,10 @@ public class OptionsOverlay extends OverlayOpsuState { return true; } + if (x > finalWidth) { + return false; + } + if (hoverOption != null) { if (hoverOption.getType() == OptionType.BOOLEAN) { hoverOption.click(); @@ -461,6 +702,7 @@ public class OptionsOverlay extends OverlayOpsuState { } } + /* // check if tab was clicked int tScrollOffset = 0; for (OptionTab tab : tabs) { @@ -472,6 +714,7 @@ public class OptionsOverlay extends OverlayOpsuState { tScrollOffset += Fonts.MEDIUM.getLineHeight() * 2; tScrollOffset += tab.options.length * optionHeight; } + */ if (UI.getBackButton().contains(x, y)){ hide(); @@ -516,9 +759,15 @@ public class OptionsOverlay extends OverlayOpsuState { if (key == Input.KEY_ESCAPE) { if (isListOptionOpen) { isListOptionOpen = false; + listAnimationTime = LISTCLOSEANIMATIONTIME; listHoverIndex = -1; return true; } + if (lastSearchText.length() != 0) { + resetSearch(); + updateHoverOption(prevMouseX, prevMouseY); + return true; + } hide(); if (listener != null) { listener.onLeaveOptionsMenu(); @@ -526,7 +775,13 @@ public class OptionsOverlay extends OverlayOpsuState { return true; } - return false; + searchField.keyPressed(key, c); + if (!searchField.getText().equals(lastSearchText)) { + lastSearchText = searchField.getText().toLowerCase(); + updateSearch(); + } + + return true; } @Override @@ -550,20 +805,33 @@ public class OptionsOverlay extends OverlayOpsuState { return; } hoverOption = null; - if (mouseY < optionStartY || mouseX < optionStartX || mouseX > optionStartX + optionWidth) { + if (mouseX > width) { return; } int mouseVirtualY = scrollOffset + mouseY - optionStartY; - for (OptionTab tab : tabs) { - mouseVirtualY -= Fonts.MEDIUM.getLineHeight() * 2; - for (int optionIndex = 0; optionIndex < tab.options.length; optionIndex++) { - GameOption option = tab.options[optionIndex]; - if (!option.showCondition()) { + for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { + OptionTab section = sections[sectionIndex]; + if (section.filtered) { + continue; + } + mouseVirtualY -= Fonts.LARGE.getLineHeight() * 1.5f; + if (section.options == null) { + continue; + } + for (int optionIndex = 0; optionIndex < section.options.length; optionIndex++) { + GameOption option = section.options[optionIndex]; + if (option.isFiltered() || !option.showCondition()) { continue; } if (mouseVirtualY <= optionHeight) { if (mouseVirtualY >= 0) { + int indicatorPos = scrollOffset + mouseY - mouseVirtualY; + if (indicatorPos != this.indicatorPos + indicatorOffsetToNextPos) { + this.indicatorPos += indicatorOffsetToNextPos; // finish the current moving animation + indicatorOffsetToNextPos = indicatorPos - this.indicatorPos; + indicatorMoveAnimationTime = 1; // starts animation + } hoverOption = option; } return; @@ -573,11 +841,59 @@ public class OptionsOverlay extends OverlayOpsuState { } } + private void resetSearch() { + for (OptionTab section : sections) { + section.filtered = false; + if (section.options == null) { + continue; + } + for (GameOption opt : section.options) { + opt.filter(null); + } + } + searchField.setText(""); + lastSearchText = ""; + } + + private void updateSearch() { + OptionTab lastBigSection = null; + boolean lastBigSectionMatches = false; + for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { + OptionTab section = sections[sectionIndex]; + boolean sectionMatches = section.name.toLowerCase().contains(lastSearchText); + if (section.options == null) { + lastBigSectionMatches = sectionMatches; + lastBigSection = section; + section.filtered = true; + continue; + } + boolean allOptionsHidden = true; + for (int optionIndex = 0; optionIndex < section.options.length; optionIndex++) { + GameOption option = section.options[optionIndex]; + if (lastBigSectionMatches || sectionMatches) { + allOptionsHidden = false; + option.filter(null); + continue; + } + if (!option.filter(lastSearchText)) { + allOptionsHidden = false; + } + } + if (allOptionsHidden) { + section.filtered = true; + } else { + lastBigSection.filtered = false; + section.filtered = false; + } + } + updateHoverOption(prevMouseX, prevMouseY); + } + public static class OptionTab { public final String name; public final GameOption[] options; - private MenuButton button; + private boolean filtered; public OptionTab(String name, GameOption[] options) { this.name = name; diff --git a/src/yugecin/opsudance/utils/FontUtil.java b/src/yugecin/opsudance/utils/FontUtil.java new file mode 100644 index 00000000..b3e75c76 --- /dev/null +++ b/src/yugecin/opsudance/utils/FontUtil.java @@ -0,0 +1,35 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 yugecin + * + * opsu!dance 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!dance 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!dance. If not, see . + */ +package yugecin.opsudance.utils; + +import org.newdawn.slick.Color; +import org.newdawn.slick.Font; + +public class FontUtil { + + public static void drawCentered(Font font, int width, int xoffset, int y, String text, Color color) { + int x = (width - font.getWidth(text)) / 2; + font.drawString(x + xoffset, y, text, color); + } + + public static void drawRightAligned(Font font, int width, int xoffset, int y, String text, Color color) { + int x = width - font.getWidth(text); + font.drawString(x + xoffset, y, text, color); + } + +}