opsu-dance/src/yugecin/opsudance/ui/OptionsOverlay.java

883 lines
26 KiB
Java
Raw Normal View History

2016-12-11 01:17:30 +01:00
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.ui;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
2016-12-11 01:17:30 +01:00
import itdelatrisu.opsu.Options.GameOption;
2016-12-11 02:08:35 +01:00
import itdelatrisu.opsu.Options.GameOption.OptionType;
2016-12-11 01:17:30 +01:00
import itdelatrisu.opsu.Utils;
2016-12-11 02:18:50 +01:00
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
2017-01-30 00:53:28 +01:00
import itdelatrisu.opsu.ui.*;
2017-01-29 11:28:24 +01:00
import itdelatrisu.opsu.ui.animations.AnimationEquation;
2016-12-11 02:08:35 +01:00
import org.newdawn.slick.*;
2017-01-29 11:28:24 +01:00
import org.newdawn.slick.gui.TextField;
2017-01-19 16:03:53 +01:00
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.OverlayOpsuState;
2017-01-29 11:28:24 +01:00
import yugecin.opsudance.utils.FontUtil;
2016-12-11 01:17:30 +01:00
2017-01-30 00:53:28 +01:00
import java.util.HashMap;
import java.util.LinkedList;
2017-01-19 16:03:53 +01:00
public class OptionsOverlay extends OverlayOpsuState {
2016-12-11 01:17:30 +01:00
2017-01-19 16:03:53 +01:00
private final DisplayContainer displayContainer;
2016-12-11 02:08:35 +01:00
2017-01-29 11:28:24 +01:00
private static final float BG_ALPHA = 0.7f;
private static final float LINEALPHA = 0.8f;
private static final Color COL_BG = new Color(Color.black);
private static final Color COL_WHITE = new Color(1f, 1f, 1f);
private static final Color COL_PINK = new Color(235, 117, 139);
private static final Color COL_CYAN = new Color(88, 218, 254);
private static final Color COL_GREY = new Color(55, 55, 57);
private static final Color COL_BLUE = new Color(Colors.BLUE_BACKGROUND);
private static final Color COL_COMBOBOX_HOVER = new Color(185, 19, 121);
private static final float INDICATOR_ALPHA = 0.8f;
private static final Color COL_INDICATOR = new Color(Color.black);
/** Duration, in ms, of the show (slide-in) animation. */
private static final int SHOWANIMATIONTIME = 1000;
/** Current value of the animation timer. */
private int animationtime;
/** Duration, in ms, of the hide animation. */
private int hideAnimationTime;
/** How much the show animation progressed when the hide request was made. */
private float hideAnimationStartProgress;
/** Target duration, in ms, of the move animation for the indicator. */
private static final int INDICATORMOVEANIMATIONTIME = 166;
/** Selected option indicator virtual position. */
private int indicatorPos;
/** Selected option indicator offset to next position. */
private int indicatorOffsetToNextPos;
/** Selected option indicator move to next position animation time past. */
private int indicatorMoveAnimationTime;
/** Target duration, in ms, of the fadeout animation for the indicator. */
private static final int INDICATORHIDEANIMATIONTIME = 500;
/** Selected option indicator hide animation time past. */
private int indicatorHideAnimationTime;
2017-01-19 19:23:31 +01:00
private Listener listener;
2017-01-19 16:03:53 +01:00
private Image sliderBallImg;
private Image checkOnImg;
private Image checkOffImg;
2017-01-29 11:28:24 +01:00
private Image searchImg;
2016-12-11 01:17:30 +01:00
2017-01-29 11:28:24 +01:00
private OptionTab[] sections;
2016-12-11 16:41:56 +01:00
2016-12-11 01:17:30 +01:00
private GameOption hoverOption;
private GameOption selectedOption;
2016-12-11 16:41:56 +01:00
2016-12-11 10:08:22 +01:00
private int sliderOptionStartX;
private int sliderOptionLength;
private boolean isAdjustingSlider;
2016-12-11 16:41:56 +01:00
2017-01-30 00:53:28 +01:00
private final HashMap<GameOption, DropdownMenu<Object>> dropdownMenus;
private final LinkedList<DropdownMenu<Object>> visibleDropdownMenus;
private int dropdownMenuPaddingY;
private DropdownMenu<Object> openDropdownMenu;
private int openDropdownVirtualY;
2016-12-11 01:17:30 +01:00
2017-01-29 11:28:24 +01:00
private int finalWidth;
2016-12-11 01:17:30 +01:00
private int width;
private int height;
private int optionWidth;
private int optionStartX;
private int optionStartY;
private int optionHeight;
2017-01-29 11:28:24 +01:00
private int optionTextOffsetY;
private int controlImageSize;
private int controlImagePadding;
private static final int LINEWIDTH = 3;
private int paddingRight;
private int paddingLeft;
private int paddingTextLeft;
private int textOptionsY;
private int textChangeY;
private int posSearchY;
private int textSearchYOffset;
2016-12-11 01:17:30 +01:00
2017-01-29 15:33:01 +01:00
private final KineticScrolling scrollHandler;
private int maxScrollOffset;
2016-12-11 01:17:30 +01:00
private int mousePressY;
private boolean keyEntryLeft;
private boolean keyEntryRight;
2016-12-11 16:32:21 +01:00
private int prevMouseX;
private int prevMouseY;
private int sliderSoundDelay;
private int sectionLineHeight;
2017-01-29 15:33:01 +01:00
private final TextField searchField;
2017-01-29 11:28:24 +01:00
private String lastSearchText;
public OptionsOverlay(DisplayContainer displayContainer, OptionTab[] sections) {
2017-01-19 16:03:53 +01:00
this.displayContainer = displayContainer;
2016-12-11 01:17:30 +01:00
2017-01-29 11:28:24 +01:00
this.sections = sections;
2016-12-11 01:17:30 +01:00
2017-01-30 00:53:28 +01:00
dropdownMenus = new HashMap<>();
visibleDropdownMenus = new LinkedList<>();
2017-01-29 11:28:24 +01:00
searchField = new TextField(displayContainer, null, 0, 0, 0, 0);
2017-01-29 22:55:11 +01:00
searchField.setMaxLength(20);
2017-01-29 15:33:01 +01:00
scrollHandler = new KineticScrolling();
scrollHandler.setAllowOverScroll(true);
2017-01-19 16:03:53 +01:00
}
2017-01-19 19:23:31 +01:00
public void setListener(Listener listener) {
this.listener = listener;
}
2017-01-19 16:03:53 +01:00
@Override
public void revalidate() {
super.revalidate();
2016-12-12 18:46:57 +01:00
2017-01-29 23:00:22 +01:00
finalWidth = Math.max((int) (displayContainer.width * 0.36f), 340); // 0.321f
2017-01-19 16:03:53 +01:00
height = displayContainer.height;
2016-12-11 01:17:30 +01:00
// calculate positions
2017-01-29 11:28:24 +01:00
paddingRight = (int) (displayContainer.width * 0.009375f); // not so accurate
paddingLeft = (int) (displayContainer.width * 0.0180f); // not so accurate
paddingTextLeft = paddingLeft + LINEWIDTH + (int) (displayContainer.width * 0.00625f); // not so accurate
2017-01-29 11:28:24 +01:00
optionStartX = paddingTextLeft;
textOptionsY = Fonts.LARGE.getLineHeight() * 2;
textChangeY = textOptionsY + Fonts.LARGE.getLineHeight();
posSearchY = textChangeY + Fonts.MEDIUM.getLineHeight() * 2;
textSearchYOffset = Fonts.MEDIUM.getLineHeight() / 2;
optionStartY = posSearchY + Fonts.MEDIUM.getLineHeight() + Fonts.LARGE.getLineHeight();
sectionLineHeight = (int) (Fonts.LARGE.getLineHeight() * 1.5f);
2017-01-29 11:28:24 +01:00
2017-01-30 00:53:28 +01:00
if (active) {
width = finalWidth;
optionWidth = width - optionStartX - paddingRight;
}
2017-01-29 11:28:24 +01:00
optionHeight = (int) (Fonts.MEDIUM.getLineHeight() * 1.3f);
optionTextOffsetY = (int) ((optionHeight - Fonts.MEDIUM.getLineHeight()) / 2f);
controlImageSize = (int) (Fonts.MEDIUM.getLineHeight() * 0.7f);
controlImagePadding = (optionHeight - controlImageSize) / 2;
sliderBallImg = GameImage.CONTROL_SLIDER_BALL.getImage().getScaledCopy(controlImageSize, controlImageSize);
checkOnImg = GameImage.CONTROL_CHECK_ON.getImage().getScaledCopy(controlImageSize, controlImageSize);
checkOffImg = GameImage.CONTROL_CHECK_OFF.getImage().getScaledCopy(controlImageSize, controlImageSize);
2017-01-30 00:53:28 +01:00
dropdownMenus.clear();
for (OptionTab section : sections) {
if (section.options == null) {
continue;
}
for (final GameOption option : section.options) {
Object[] items = option.getListItems();
if (items == null) {
continue;
}
DropdownMenu<Object> menu = new DropdownMenu<Object>(displayContainer, items, 0, 0, 0) {
@Override
public void itemSelected(int index, Object item) {
option.clickListItem(index);
openDropdownMenu = null;
}
};
// not the best way to determine the selected option AT ALL, but seems like it's the only one right now...
String selectedValue = option.getValueString();
int idx = 0;
for (Object item : items) {
if (item.toString().equals(selectedValue)) {
break;
}
idx++;
}
menu.setSelectedIndex(idx);
menu.setBackgroundColor(COL_BG);
menu.setBorderColor(Color.transparent);
menu.setChevronDownColor(COL_WHITE);
menu.setChevronRightColor(COL_BG);
menu.setHighlightColor(COL_COMBOBOX_HOVER);
menu.setTextColor(COL_WHITE);
dropdownMenuPaddingY = (optionHeight - menu.getHeight()) / 2;
2017-01-30 00:53:28 +01:00
dropdownMenus.put(option, menu);
}
}
2017-01-29 11:28:24 +01:00
int searchImgSize = (int) (Fonts.LARGE.getLineHeight() * 0.75f);
searchImg = GameImage.SEARCH.getImage().getScaledCopy(searchImgSize, searchImgSize);
2016-12-11 01:17:30 +01:00
}
2017-01-19 16:03:53 +01:00
@Override
public void onRender(Graphics g) {
2017-01-29 11:28:24 +01:00
g.setClip(0, 0, width, height);
2016-12-11 01:17:30 +01:00
// bg
2017-01-29 11:28:24 +01:00
g.setColor(COL_BG);
2016-12-11 01:17:30 +01:00
g.fillRect(0, 0, width, height);
// title
renderTitle();
2017-01-29 11:28:24 +01:00
renderIndicator(g);
2016-12-11 01:17:30 +01:00
// options
renderOptions(g);
2017-01-30 00:53:28 +01:00
if (openDropdownMenu != null) {
openDropdownMenu.render(g);
if (!openDropdownMenu.isOpen()) {
openDropdownMenu = null;
}
}
2017-01-29 11:28:24 +01:00
renderSearch(g);
2016-12-11 11:07:28 +01:00
// scrollbar
2017-01-29 11:28:24 +01:00
g.setColor(COL_WHITE);
2017-01-29 15:33:01 +01:00
g.fillRect(width - 5, scrollHandler.getPosition() / maxScrollOffset * (height - 45), 5, 45);
2016-12-11 11:07:28 +01:00
g.clearClip();
2016-12-11 01:17:30 +01:00
2016-12-11 02:33:50 +01:00
// UI
2017-02-02 01:06:56 +01:00
UI.getBackButton().draw(g);
2016-12-11 02:33:50 +01:00
// tooltip
2017-01-19 16:03:53 +01:00
renderTooltip(g);
2016-12-11 12:47:33 +01:00
// key input options
if (keyEntryLeft || keyEntryRight) {
renderKeyEntry(g);
}
}
2017-01-29 11:28:24 +01:00
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;
}
}
2017-01-29 15:33:01 +01:00
g.fillRect(0, indicatorPos - scrollHandler.getPosition(), width, optionHeight);
2017-01-29 11:28:24 +01:00
}
private void renderKeyEntry(Graphics g) {
2017-01-29 11:28:24 +01:00
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.";
2017-01-29 11:28:24 +01:00
int y = (displayContainer.height - Fonts.LARGE.getLineHeight()) / 2;
FontUtil.drawCentered(Fonts.LARGE, displayContainer.width, 0, y, prompt, COL_WHITE);
2016-12-11 02:33:50 +01:00
}
2017-01-19 16:03:53 +01:00
private void renderTooltip(Graphics g) {
2016-12-11 02:33:50 +01:00
if (hoverOption != null) {
2017-01-29 11:28:24 +01:00
String tip = hoverOption.getDescription();
if (hoverOption.getType() == OptionType.NUMERIC) {
tip = "(" + hoverOption.getValueString() + ") " + tip;
}
2017-01-29 22:55:11 +01:00
UI.updateTooltip(displayContainer.renderDelta, tip, true);
2017-01-19 16:03:53 +01:00
UI.drawTooltip(g);
2016-12-11 02:33:50 +01:00
}
2016-12-11 01:17:30 +01:00
}
private void renderOptions(Graphics g) {
2017-01-30 00:53:28 +01:00
visibleDropdownMenus.clear();
2017-01-29 15:33:01 +01:00
int y = -scrollHandler.getIntPosition() + optionStartY;
2017-01-29 11:28:24 +01:00
maxScrollOffset = optionStartY;
boolean render = true;
2017-01-29 11:28:24 +01:00
int sectionIndex = 0;
2017-01-29 14:19:54 +01:00
for (; sectionIndex < sections.length && render; sectionIndex++) {
2017-01-29 11:28:24 +01:00
OptionTab section = sections[sectionIndex];
if (section.filtered) {
continue;
}
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);
}
2016-12-11 01:17:30 +01:00
}
y += sectionLineHeight;
maxScrollOffset += sectionLineHeight;
2017-01-29 11:28:24 +01:00
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()) {
2016-12-11 01:17:30 +01:00
continue;
}
2017-01-30 00:53:28 +01:00
if (y > -optionHeight || (openDropdownMenu != null && openDropdownMenu.equals(dropdownMenus.get(option)))) {
2017-01-29 11:28:24 +01:00
renderOption(g, option, y);
2016-12-11 01:17:30 +01:00
}
y += optionHeight;
2017-01-29 11:28:24 +01:00
maxScrollOffset += optionHeight;
lineHeight += optionHeight;
2016-12-11 01:17:30 +01:00
if (y > height) {
render = false;
while (++optionIndex < section.options.length) {
option = section.options[optionIndex];
if (option.showCondition() && !option.isFiltered()) {
maxScrollOffset += optionHeight;
}
}
2016-12-11 01:17:30 +01:00
}
}
2017-01-29 11:28:24 +01:00
g.setColor(COL_GREY);
g.fillRect(paddingLeft, lineStartY, LINEWIDTH, lineHeight);
}
// iterate over skipped options to correctly calculate max scroll offset
2017-01-29 14:19:54 +01:00
for (; sectionIndex < sections.length; sectionIndex++) {
if (sections[sectionIndex].filtered) {
continue;
}
2017-01-29 11:28:24 +01:00
maxScrollOffset += Fonts.LARGE.getLineHeight() * 1.5f;
if (sections[sectionIndex].options == null) {
continue;
}
for (GameOption option : sections[sectionIndex].options) {
if (option.showCondition() && !option.isFiltered()) {
maxScrollOffset += optionHeight;
}
2017-01-29 11:28:24 +01:00
}
}
2017-01-30 00:53:28 +01:00
if (openDropdownMenu != null) {
maxScrollOffset = Math.max(maxScrollOffset, openDropdownVirtualY + openDropdownMenu.getHeight());
}
2017-01-30 00:53:28 +01:00
maxScrollOffset -= height * 2 / 3;
2017-01-29 11:28:24 +01:00
if (maxScrollOffset < 0) {
maxScrollOffset = 0;
}
2017-01-30 00:53:28 +01:00
scrollHandler.setMinMax(0, maxScrollOffset);
2016-12-11 01:17:30 +01:00
}
2017-01-29 11:28:24 +01:00
private void renderOption(Graphics g, GameOption option, int y) {
2016-12-11 02:08:35 +01:00
OptionType type = option.getType();
Object[] listItems = option.getListItems();
if (listItems != null) {
2017-01-30 00:53:28 +01:00
renderListOption(g, option, y);
2016-12-11 02:08:35 +01:00
} else if (type == OptionType.BOOLEAN) {
2017-01-29 11:28:24 +01:00
renderCheckOption(option, y);
2016-12-11 02:08:35 +01:00
} else if (type == OptionType.NUMERIC) {
2017-01-29 11:28:24 +01:00
renderSliderOption(g, option, y);
2016-12-11 02:08:35 +01:00
} else {
2017-01-29 11:28:24 +01:00
renderGenericOption(option, y);
2016-12-11 02:08:35 +01:00
}
}
2017-01-30 00:53:28 +01:00
private void renderListOption(Graphics g, GameOption option, int y) {
// draw option name
int nameWith = Fonts.MEDIUM.getWidth(option.getName());
2017-01-29 11:28:24 +01:00
Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.getName(), COL_WHITE);
2017-01-30 00:53:28 +01:00
nameWith += 15;
int comboboxStartX = optionStartX + nameWith;
int comboboxWidth = optionWidth - nameWith;
if (comboboxWidth < controlImageSize) {
2017-01-29 11:28:24 +01:00
return;
}
2017-01-30 00:53:28 +01:00
DropdownMenu<Object> dropdown = dropdownMenus.get(option);
if (dropdown == null) {
return;
}
visibleDropdownMenus.add(dropdown);
dropdown.setWidth(comboboxWidth);
dropdown.x = comboboxStartX;
dropdown.y = y + dropdownMenuPaddingY;
if (dropdown.isOpen()) {
openDropdownMenu = dropdown;
openDropdownVirtualY = maxScrollOffset;
return;
2016-12-11 11:07:28 +01:00
}
2017-01-30 00:53:28 +01:00
dropdown.render(g);
2016-12-11 02:08:35 +01:00
}
2017-01-29 11:28:24 +01:00
private void renderCheckOption(GameOption option, int y) {
2016-12-11 02:08:35 +01:00
if (option.getBooleanValue()) {
2017-01-29 11:28:24 +01:00
checkOnImg.draw(optionStartX, y + controlImagePadding, COL_PINK);
2016-12-11 02:08:35 +01:00
} else {
2017-01-29 11:28:24 +01:00
checkOffImg.draw(optionStartX, y + controlImagePadding, COL_PINK);
2016-12-11 02:08:35 +01:00
}
2017-01-29 11:28:24 +01:00
Fonts.MEDIUM.drawString(optionStartX + 30, y + optionTextOffsetY, option.getName(), COL_WHITE);
2016-12-11 02:08:35 +01:00
}
2017-01-29 11:28:24 +01:00
private void renderSliderOption(Graphics g, GameOption option, int y) {
final int padding = 10;
2016-12-11 02:08:35 +01:00
int nameLen = Fonts.MEDIUM.getWidth(option.getName());
2017-01-29 11:28:24 +01:00
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;
2016-12-11 10:08:22 +01:00
if (hoverOption == option) {
if (!isAdjustingSlider) {
sliderOptionLength = sliderLen;
2017-01-29 11:28:24 +01:00
sliderOptionStartX = sliderStartX;
2016-12-11 10:08:22 +01:00
} else {
sliderLen = sliderOptionLength;
}
}
2017-01-29 11:28:24 +01:00
float sliderValue = (float) (option.getIntegerValue() - option.getMinValue()) / (option.getMaxValue() - option.getMinValue());
float sliderBallPos = sliderStartX + (int) ((sliderLen - controlImageSize) * sliderValue);
2016-12-11 02:08:35 +01:00
g.setLineWidth(3f);
2017-01-29 11:28:24 +01:00
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 + 1, y + optionHeight / 2, sliderEndX, y + optionHeight / 2);
2017-01-29 11:28:24 +01:00
COL_PINK.a = a;
}
2016-12-11 02:08:35 +01:00
}
2017-01-29 11:28:24 +01:00
private void renderGenericOption(GameOption option, int y) {
2016-12-11 02:08:35 +01:00
String value = option.getValueString();
int valueLen = Fonts.MEDIUM.getWidth(value);
2017-01-29 11:28:24 +01:00
Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.getName(), COL_WHITE);
Fonts.MEDIUM.drawString(optionStartX + optionWidth - valueLen, y + optionTextOffsetY, value, COL_BLUE);
2016-12-11 01:17:30 +01:00
}
2017-01-29 11:28:24 +01:00
private void renderTitle() {
2017-01-29 15:33:01 +01:00
FontUtil.drawCentered(Fonts.LARGE, width, 0, textOptionsY - scrollHandler.getIntPosition(), "Options", COL_WHITE);
FontUtil.drawCentered(Fonts.MEDIUM, width, 0, textChangeY - scrollHandler.getIntPosition(), "Change the way opsu! behaves", COL_PINK);
2016-12-11 01:17:30 +01:00
}
2017-01-29 11:28:24 +01:00
private void renderSearch(Graphics g) {
2017-01-29 15:33:01 +01:00
int ypos = posSearchY + textSearchYOffset - scrollHandler.getIntPosition();
if (scrollHandler.getIntPosition() > posSearchY) {
2017-01-29 11:28:24 +01:00
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);
2016-12-11 01:17:30 +01:00
}
2017-01-19 16:03:53 +01:00
@Override
public void hide() {
2017-01-29 22:55:11 +01:00
searchField.setFocused(false);
2017-01-19 16:03:53 +01:00
acceptInput = false;
SoundController.playSound(SoundEffect.MENUBACK);
2017-01-29 11:28:24 +01:00
hideAnimationTime = animationtime;
hideAnimationStartProgress = (float) animationtime / SHOWANIMATIONTIME;
2017-01-19 16:03:53 +01:00
}
@Override
public void show() {
2017-01-29 11:28:24 +01:00
indicatorPos = -optionHeight;
indicatorOffsetToNextPos = 0;
indicatorMoveAnimationTime = 0;
indicatorHideAnimationTime = 0;
2017-01-19 16:03:53 +01:00
acceptInput = true;
active = true;
2017-01-29 11:28:24 +01:00
animationtime = 0;
resetSearch();
2017-01-19 16:03:53 +01:00
}
@Override
public void onPreRenderUpdate() {
int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
int delta = displayContainer.renderDelta;
2017-01-29 15:33:01 +01:00
scrollHandler.update(delta);
2017-01-30 00:53:28 +01:00
if (openDropdownMenu == null) {
for (DropdownMenu<Object> menu : visibleDropdownMenus) {
menu.updateHover(mouseX, mouseY);
}
} else {
openDropdownMenu.updateHover(mouseX, mouseY);
}
2017-01-29 11:28:24 +01:00
updateShowHideAnimation(delta);
if (animationtime <= 0) {
active = false;
return;
}
if (sliderSoundDelay > 0) {
sliderSoundDelay -= delta;
}
2016-12-11 16:32:21 +01:00
if (mouseX - prevMouseX == 0 && mouseY - prevMouseY == 0) {
2017-01-29 11:28:24 +01:00
updateIndicatorAlpha();
2016-12-11 16:32:21 +01:00
return;
}
prevMouseX = mouseX;
prevMouseY = mouseY;
2016-12-11 01:17:30 +01:00
updateHoverOption(mouseX, mouseY);
2017-01-29 11:28:24 +01:00
updateIndicatorAlpha();
2016-12-11 01:17:30 +01:00
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
2016-12-11 10:08:22 +01:00
if (isAdjustingSlider) {
int sliderValue = hoverOption.getIntegerValue();
2017-01-19 16:03:53 +01:00
updateSliderOption();
if (hoverOption.getIntegerValue() - sliderValue != 0 && sliderSoundDelay <= 0) {
sliderSoundDelay = 90;
SoundController.playSound(SoundEffect.MENUHIT);
}
2016-12-11 10:08:22 +01:00
}
2016-12-11 01:17:30 +01:00
}
2017-01-29 11:28:24 +01:00
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;
}
2017-01-19 16:03:53 +01:00
@Override
public boolean onMousePressed(int button, int x, int y) {
if (keyEntryLeft || keyEntryRight) {
keyEntryLeft = keyEntryRight = false;
2017-01-19 16:03:53 +01:00
return true;
}
2017-01-29 11:28:24 +01:00
if (x > width) {
return false;
2016-12-11 11:07:28 +01:00
}
2017-01-29 15:33:01 +01:00
scrollHandler.pressed();
mousePressY = y;
2016-12-12 18:49:26 +01:00
selectedOption = hoverOption;
2016-12-11 01:17:30 +01:00
2017-01-30 00:53:28 +01:00
if (hoverOption != null && hoverOption.getType() == OptionType.NUMERIC) {
isAdjustingSlider = sliderOptionStartX <= x && x < sliderOptionStartX + sliderOptionLength;
if (isAdjustingSlider) {
updateSliderOption();
2016-12-11 11:07:28 +01:00
}
2016-12-11 10:08:22 +01:00
}
2017-01-19 16:03:53 +01:00
return true;
}
2017-01-19 16:03:53 +01:00
@Override
public boolean onMouseReleased(int button, int x, int y) {
selectedOption = null;
2017-01-19 19:23:31 +01:00
if (isAdjustingSlider && listener != null) {
listener.onSaveOption(hoverOption);
2016-12-11 16:32:21 +01:00
}
2016-12-11 10:08:22 +01:00
isAdjustingSlider = false;
sliderOptionLength = 0;
2017-01-30 00:53:28 +01:00
if (openDropdownMenu != null) {
openDropdownMenu.mouseReleased(button);
updateHoverOption(x, y);
return true;
} else {
for (DropdownMenu<Object> menu : visibleDropdownMenus) {
menu.mouseReleased(button);
if (menu.isOpen()) {
return true;
}
}
}
2017-01-29 15:33:01 +01:00
scrollHandler.released();
// check if clicked, not dragged
if (Math.abs(y - mousePressY) >= 5) {
2017-01-19 16:03:53 +01:00
return true;
}
2016-12-11 02:08:35 +01:00
2017-01-29 11:28:24 +01:00
if (x > finalWidth) {
return false;
}
if (hoverOption != null) {
if (hoverOption.getType() == OptionType.BOOLEAN) {
2017-01-19 16:03:53 +01:00
hoverOption.click();
2017-01-19 19:23:31 +01:00
if (listener != null) {
listener.onSaveOption(hoverOption);
}
SoundController.playSound(SoundEffect.MENUHIT);
2017-01-19 16:03:53 +01:00
return true;
} else if (hoverOption == GameOption.KEY_LEFT) {
keyEntryLeft = true;
} else if (hoverOption == GameOption.KEY_RIGHT) {
keyEntryLeft = true;
}
}
2016-12-11 02:14:43 +01:00
if (UI.getBackButton().contains(x, y)){
hide();
if (listener != null) {
listener.onLeaveOptionsMenu();
}
2017-01-19 19:23:31 +01:00
}
2017-01-19 16:03:53 +01:00
return true;
2016-12-11 01:17:30 +01:00
}
2017-01-19 16:03:53 +01:00
@Override
public boolean onMouseDragged(int oldx, int oldy, int newx, int newy) {
2016-12-11 10:08:22 +01:00
if (!isAdjustingSlider) {
2017-01-29 15:33:01 +01:00
int diff = newy - oldy;
if (diff != 0) {
scrollHandler.dragged(-diff);
}
2016-12-11 10:08:22 +01:00
}
2017-01-19 16:03:53 +01:00
return true;
2016-12-11 01:17:30 +01:00
}
2017-01-19 16:03:53 +01:00
@Override
public boolean onMouseWheelMoved(int delta) {
2016-12-11 10:08:22 +01:00
if (!isAdjustingSlider) {
2017-01-29 15:33:01 +01:00
scrollHandler.scrollOffset(-delta);
2016-12-11 10:08:22 +01:00
}
updateHoverOption(prevMouseX, prevMouseY);
2017-01-19 16:03:53 +01:00
return true;
2016-12-11 01:17:30 +01:00
}
2017-01-19 16:03:53 +01:00
@Override
public boolean onKeyPressed(int key, char c) {
if (keyEntryRight) {
Options.setGameKeyRight(key);
keyEntryRight = false;
return true;
}
if (keyEntryLeft) {
Options.setGameKeyLeft(key);
keyEntryLeft = false;
return true;
}
2017-01-19 19:23:31 +01:00
if (key == Input.KEY_ESCAPE) {
2017-01-30 00:53:28 +01:00
if (openDropdownMenu != null) {
openDropdownMenu.keyPressed(key, c);
return true;
2017-01-19 19:23:31 +01:00
}
2017-01-29 11:28:24 +01:00
if (lastSearchText.length() != 0) {
resetSearch();
updateHoverOption(prevMouseX, prevMouseY);
return true;
}
2017-01-19 19:23:31 +01:00
hide();
if (listener != null) {
listener.onLeaveOptionsMenu();
}
return true;
2016-12-11 01:17:30 +01:00
}
2017-01-19 19:23:31 +01:00
2017-01-29 11:28:24 +01:00
searchField.keyPressed(key, c);
if (!searchField.getText().equals(lastSearchText)) {
lastSearchText = searchField.getText().toLowerCase();
updateSearch();
}
return true;
2016-12-11 01:17:30 +01:00
}
2017-01-19 16:03:53 +01:00
@Override
public boolean onKeyReleased(int key, char c) {
return false;
}
private void updateSliderOption() {
2016-12-12 18:46:32 +01:00
int min = hoverOption.getMinValue();
int max = hoverOption.getMaxValue();
2017-01-19 16:03:53 +01:00
int value = min + Math.round((float) (max - min) * (displayContainer.mouseX - sliderOptionStartX) / (sliderOptionLength));
2016-12-12 18:46:32 +01:00
hoverOption.setValue(Utils.clamp(value, min, max));
2016-12-11 16:32:21 +01:00
}
2016-12-11 01:17:30 +01:00
private void updateHoverOption(int mouseX, int mouseY) {
2017-01-30 00:53:28 +01:00
if (openDropdownMenu != null || keyEntryLeft || keyEntryRight) {
2016-12-11 11:07:28 +01:00
return;
}
2016-12-11 02:23:02 +01:00
if (selectedOption != null) {
hoverOption = selectedOption;
return;
}
2016-12-11 01:17:30 +01:00
hoverOption = null;
2017-01-29 11:28:24 +01:00
if (mouseX > width) {
2016-12-11 01:17:30 +01:00
return;
}
2017-01-29 15:33:01 +01:00
int mouseVirtualY = scrollHandler.getIntPosition() + mouseY - optionStartY;
2017-01-29 11:28:24 +01:00
for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) {
OptionTab section = sections[sectionIndex];
if (section.filtered) {
continue;
}
mouseVirtualY -= sectionLineHeight;
2017-01-29 11:28:24 +01:00
if (section.options == null) {
continue;
}
for (int optionIndex = 0; optionIndex < section.options.length; optionIndex++) {
GameOption option = section.options[optionIndex];
if (option.isFiltered() || !option.showCondition()) {
2016-12-11 01:17:30 +01:00
continue;
}
if (mouseVirtualY <= optionHeight) {
if (mouseVirtualY >= 0) {
2017-01-29 15:33:01 +01:00
int indicatorPos = scrollHandler.getIntPosition() + mouseY - mouseVirtualY;
2017-01-29 11:28:24 +01:00
if (indicatorPos != this.indicatorPos + indicatorOffsetToNextPos) {
this.indicatorPos += indicatorOffsetToNextPos; // finish the current moving animation
indicatorOffsetToNextPos = indicatorPos - this.indicatorPos;
indicatorMoveAnimationTime = 1; // starts animation
}
2016-12-11 01:17:30 +01:00
hoverOption = option;
}
return;
}
mouseVirtualY -= optionHeight;
}
}
}
2017-01-29 11:28:24 +01:00
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;
2017-01-29 16:53:14 +01:00
for (OptionTab section : sections) {
2017-01-29 11:28:24 +01:00
boolean sectionMatches = section.name.toLowerCase().contains(lastSearchText);
if (section.options == null) {
lastBigSectionMatches = sectionMatches;
lastBigSection = section;
section.filtered = true;
continue;
}
2017-01-29 16:53:14 +01:00
section.filtered = true;
for (GameOption option : section.options) {
2017-01-29 11:28:24 +01:00
if (lastBigSectionMatches || sectionMatches) {
2017-01-29 16:53:14 +01:00
section.filtered = false;
2017-01-29 11:28:24 +01:00
option.filter(null);
continue;
}
if (!option.filter(lastSearchText)) {
2017-01-29 16:53:14 +01:00
section.filtered = false;
lastBigSection.filtered = false;
2017-01-29 11:28:24 +01:00
}
}
}
updateHoverOption(prevMouseX, prevMouseY);
}
2016-12-11 01:17:30 +01:00
public static class OptionTab {
public final String name;
public final GameOption[] options;
2017-01-29 11:28:24 +01:00
private boolean filtered;
2016-12-11 01:17:30 +01:00
public OptionTab(String name, GameOption[] options) {
this.name = name;
this.options = options;
}
}
2017-01-19 19:23:31 +01:00
public interface Listener {
2016-12-11 01:17:30 +01:00
2017-01-19 19:23:31 +01:00
void onLeaveOptionsMenu();
2016-12-11 01:17:30 +01:00
void onSaveOption(GameOption option);
}
}