diff options
| author | shedaniel <daniel@shedaniel.me> | 2023-11-03 18:00:23 +0800 |
|---|---|---|
| committer | shedaniel <daniel@shedaniel.me> | 2024-04-16 00:38:18 +0900 |
| commit | 1be6fdd292cd5a96af8d9bc1e3432820f3737545 (patch) | |
| tree | 9f8ff23e38d2db83c750b6d8314d69abdf274fdd | |
| parent | c671f024abd0eb5b0e0795ef966232a791896a0d (diff) | |
| download | RoughlyEnoughItems-1be6fdd292cd5a96af8d9bc1e3432820f3737545.tar.gz RoughlyEnoughItems-1be6fdd292cd5a96af8d9bc1e3432820f3737545.tar.bz2 RoughlyEnoughItems-1be6fdd292cd5a96af8d9bc1e3432820f3737545.zip | |
Implement boundaries config and add option for panel alignment
Close #1502
11 files changed, 434 insertions, 16 deletions
diff --git a/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java b/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java index 87d242ad3..a15816f24 100644 --- a/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java +++ b/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java @@ -373,6 +373,12 @@ public interface ConfigObject { double getVerticalEntriesBoundariesRows(); @ApiStatus.Experimental + double getHorizontalEntriesBoundariesAlignments(); + + @ApiStatus.Experimental + double getVerticalEntriesBoundariesAlignments(); + + @ApiStatus.Experimental double getFavoritesHorizontalEntriesBoundariesPercentage(); @ApiStatus.Experimental diff --git a/api/src/main/java/me/shedaniel/rei/api/client/gui/config/PanelBoundary.java b/api/src/main/java/me/shedaniel/rei/api/client/gui/config/PanelBoundary.java new file mode 100644 index 000000000..eb9e57f15 --- /dev/null +++ b/api/src/main/java/me/shedaniel/rei/api/client/gui/config/PanelBoundary.java @@ -0,0 +1,34 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.api.client.gui.config; + +public record PanelBoundary( + double horizontalPercentage, + double verticalPercentage, + int horizontalLimit, + int verticalLimit, + double horizontalAlign, + double verticalAlign +) { +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java index ceb0ed302..a1a94e88c 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java @@ -541,6 +541,16 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { return Mth.clamp(appearance.verticalEntriesBoundariesRows, 1, 1000); } + @Override + public double getHorizontalEntriesBoundariesAlignments() { + return appearance.horizontalEntriesBoundariesAlignment; + } + + @Override + public double getVerticalEntriesBoundariesAlignments() { + return appearance.verticalEntriesBoundariesAlignment; + } + @ApiStatus.Experimental @Override public double getFavoritesHorizontalEntriesBoundariesPercentage() { @@ -665,6 +675,8 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { @UsePercentage(min = 0.1, max = 1.0, prefix = "Limit: ") public double verticalEntriesBoundaries = 1.0; public int horizontalEntriesBoundariesColumns = 50; public int verticalEntriesBoundariesRows = 1000; + public double horizontalEntriesBoundariesAlignment = 1.0; + public double verticalEntriesBoundariesAlignment = 0.5; @UsePercentage(min = 0.1, max = 1.0, prefix = "Limit: ") public double favoritesHorizontalEntriesBoundaries = 1.0; public int favoritesHorizontalEntriesBoundariesColumns = 50; @UseSpecialSearchFilterSyntaxHighlightingScreen public SyntaxHighlightingMode syntaxHighlightingMode = SyntaxHighlightingMode.COLORFUL; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java index 85751f649..27048fe86 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java @@ -269,14 +269,13 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { private static Rectangle calculateOverlayBounds() { Rectangle bounds = ScreenRegistry.getInstance().getOverlayBounds(ConfigObject.getInstance().getDisplayPanelLocation(), Minecraft.getInstance().screen); + double hAlign = ConfigObject.getInstance().getDisplayPanelLocation() == DisplayPanelLocation.LEFT ? 1 - ConfigObject.getInstance().getHorizontalEntriesBoundariesAlignments() : ConfigObject.getInstance().getHorizontalEntriesBoundariesAlignments(); int widthReduction = (int) Math.round(bounds.width * (1 - ConfigObject.getInstance().getHorizontalEntriesBoundariesPercentage())); - if (ConfigObject.getInstance().getDisplayPanelLocation() == DisplayPanelLocation.RIGHT) - bounds.x += widthReduction; + bounds.x += (int) Math.round(widthReduction * hAlign); bounds.width -= widthReduction; int maxWidth = (int) Math.ceil(entrySize() * ConfigObject.getInstance().getHorizontalEntriesBoundariesColumns() + entrySize() * 0.75); if (bounds.width > maxWidth) { - if (ConfigObject.getInstance().getDisplayPanelLocation() == DisplayPanelLocation.RIGHT) - bounds.x += bounds.width - maxWidth; + bounds.x += (int) Math.round((bounds.width - maxWidth) * hAlign); bounds.width = maxWidth; } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionValueWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionValueWidget.java index 81b35a47c..015804d65 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionValueWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionValueWidget.java @@ -34,7 +34,6 @@ import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; import me.shedaniel.rei.api.client.gui.widgets.Widgets; import me.shedaniel.rei.api.client.util.MatrixUtils; import me.shedaniel.rei.api.common.util.CollectionUtils; -import me.shedaniel.rei.api.common.util.FormattingUtils; import me.shedaniel.rei.impl.client.gui.config.ConfigAccess; import me.shedaniel.rei.impl.client.gui.config.REIConfigScreen; import me.shedaniel.rei.impl.client.gui.config.options.CompositeOption; @@ -44,14 +43,11 @@ import me.shedaniel.rei.impl.client.gui.modules.entries.ToggleMenuEntry; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.FormattedText; -import net.minecraft.network.chat.Style; -import net.minecraft.network.chat.TextComponent; import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.FormattedCharSequence; -import java.lang.ref.Reference; import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -68,7 +64,7 @@ public class ConfigOptionValueWidget { text[0] = literal("> ").withStyle(ChatFormatting.YELLOW) .append(t.copy().withStyle(ChatFormatting.YELLOW)) .append(literal(" <").withStyle(ChatFormatting.YELLOW)); - } else if (access.get(option).equals(Objects.requireNonNullElseGet(option.getDefaultValue(), () -> access.getDefault(option)))) { + } else if (!(option.getEntry() instanceof OptionValueEntry.Configure<T>) && access.get(option).equals(Objects.requireNonNullElseGet(option.getDefaultValue(), () -> access.getDefault(option)))) { text[0] = translatable("config.rei.value.default", t); if (font.width(text[0]) > width) { @@ -106,6 +102,13 @@ public class ConfigOptionValueWidget { applySelection(access, option, selection, label, setText, matrix); } else if (access.get(option) instanceof ModifierKeyCode) { applyKeycode(access, option, label, setText, matrix); + } else if (option.getEntry() instanceof OptionValueEntry.Configure<T>) { + label.clickable().onClick($ -> { + ((OptionValueEntry.Configure<T>) option.getEntry()).configure(access, option, () -> { + Minecraft.getInstance().setScreen((Screen) access); + setText.accept(option.getEntry().getOption(access.get(option))); + }); + }); } return Widgets.concatWithBounds(() -> new Rectangle(-label.getBounds().width, 0, label.getBounds().width + 8, 14), diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java index 59636eebd..a39658806 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java @@ -27,6 +27,7 @@ import me.shedaniel.clothconfig2.api.ModifierKeyCode; import me.shedaniel.rei.api.client.gui.config.*; import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; import me.shedaniel.rei.impl.client.gui.config.REIConfigScreen; +import me.shedaniel.rei.impl.client.gui.config.options.configure.PanelBoundariesConfiguration; import net.minecraft.client.Minecraft; import java.util.function.BiConsumer; @@ -103,9 +104,18 @@ public interface AllREIConfigOptions { })); CompositeOption<Boolean> CRAFTABLE_FILTER = make("layout.craftable_filter", i -> i.appearance.layout.showCraftableOnlyButton, (i, v) -> i.appearance.layout.showCraftableOnlyButton = v) .enabledDisabled(); - // TODO: BOUNDARIES - CompositeOption<Boolean> BOUNDARIES = make("layout.boundaries", i -> true, (i, v) -> { - }); + CompositeOption<PanelBoundary> BOUNDARIES = make("layout.boundaries", i -> { + return new PanelBoundary(i.appearance.horizontalEntriesBoundaries, i.appearance.verticalEntriesBoundaries, + i.appearance.horizontalEntriesBoundariesColumns, i.appearance.verticalEntriesBoundariesRows, + i.appearance.horizontalEntriesBoundariesAlignment, i.appearance.verticalEntriesBoundariesAlignment); + }, (i, v) -> { + i.appearance.horizontalEntriesBoundaries = v.horizontalPercentage(); + i.appearance.verticalEntriesBoundaries = v.verticalPercentage(); + i.appearance.horizontalEntriesBoundariesColumns = v.horizontalLimit(); + i.appearance.verticalEntriesBoundariesRows = v.verticalLimit(); + i.appearance.horizontalEntriesBoundariesAlignment = v.horizontalAlign(); + i.appearance.verticalEntriesBoundariesAlignment = v.verticalAlign(); + }).configure(PanelBoundariesConfiguration.INSTANCE); CompositeOption<DisplayPanelLocation> LOCATION = make("layout.location", i -> i.advanced.accessibility.displayPanelLocation, (i, v) -> i.advanced.accessibility.displayPanelLocation = v) .enumOptions(); CompositeOption<Boolean> HIDE_LIST_IF_IDLE = make("layout.hide_when_idle", i -> i.appearance.hideEntryPanelIfIdle, (i, v) -> i.appearance.hideEntryPanelIfIdle = v) diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/CompositeOption.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/CompositeOption.java index 50a982b81..5a5d8190f 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/CompositeOption.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/CompositeOption.java @@ -79,6 +79,10 @@ public class CompositeOption<T> { return this.entry(OptionValueEntry.options(entry)); } + public CompositeOption<T> configure(OptionValueEntry.Configurator<T> configurator) { + return this.entry(OptionValueEntry.configure(configurator)); + } + public CompositeOption<T> previewer(ConfigPreviewer<T> previewer) { this.previewer = previewer; return this; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionValueEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionValueEntry.java index da457c5c8..48697d882 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionValueEntry.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionValueEntry.java @@ -25,6 +25,7 @@ package me.shedaniel.rei.impl.client.gui.config.options; import me.shedaniel.clothconfig2.api.ModifierKeyCode; import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.impl.client.gui.config.ConfigAccess; import net.minecraft.network.chat.Component; import java.util.Arrays; @@ -95,10 +96,31 @@ public interface OptionValueEntry<T> { }; } + static <T> OptionValueEntry.Configure<T> configure(Configurator<T> configurator) { + return new Configure<>() { + @Override + public void configure(ConfigAccess access, CompositeOption<T> option, Runnable onClose) { + configurator.configure(access, option, onClose); + } + + @Override + public Component getOption(T value) { + return translatable("config.rei.texts.configure"); + } + }; + } + static OptionValueEntry<ModifierKeyCode> keybind() { return ModifierKeyCode::getLocalizedName; } + interface Configurator<T> { + void configure(ConfigAccess access, CompositeOption<T> option, Runnable onClose); + } + + interface Configure<T> extends OptionValueEntry<T>, Configurator<T> { + } + interface Selection<T> extends OptionValueEntry<T> { List<T> getOptions(); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/configure/PanelBoundariesConfiguration.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/configure/PanelBoundariesConfiguration.java new file mode 100644 index 000000000..10c2da12e --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/configure/PanelBoundariesConfiguration.java @@ -0,0 +1,315 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.options.configure; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.api.animator.NumberAnimator; +import me.shedaniel.clothconfig2.api.animator.ValueAnimator; +import me.shedaniel.math.FloatingRectangle; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.config.DisplayPanelLocation; +import me.shedaniel.rei.api.client.gui.config.PanelBoundary; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.impl.client.gui.config.ConfigAccess; +import me.shedaniel.rei.impl.client.gui.config.options.AllREIConfigOptions; +import me.shedaniel.rei.impl.client.gui.config.options.CompositeOption; +import me.shedaniel.rei.impl.client.gui.config.options.OptionValueEntry; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.AbstractSliderButton; +import net.minecraft.client.gui.components.Checkbox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.util.Mth; + +import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.*; + +public enum PanelBoundariesConfiguration implements OptionValueEntry.Configurator<PanelBoundary> { + INSTANCE; + + @Override + public void configure(ConfigAccess access, CompositeOption<PanelBoundary> option, Runnable onClose) { + Minecraft.getInstance().setScreen(new BoundariesScreen(access, option, onClose)); + } + + private static class BoundariesScreen extends Screen { + private final ConfigAccess access; + private final CompositeOption<PanelBoundary> option; + private final Runnable onClose; + private Checkbox horizontalLimit, verticalLimit; + private AbstractSliderButton horizontalSlider, verticalSlider; + private AbstractSliderButton horizontalAlignmentSlider, verticalAlignmentSlider; + private boolean horizontalUsePercentage, verticalUsePercentage; + private ValueAnimator<FloatingRectangle> boundsAnimator = ValueAnimator.ofFloatingRectangle(); + private NumberAnimator<Float> innerAlphaAnimator = ValueAnimator.ofFloat(1.0F); + + public BoundariesScreen(ConfigAccess access, CompositeOption<PanelBoundary> option, Runnable onClose) { + super(literal("")); + this.access = access; + this.option = option; + this.onClose = onClose; + + this.horizontalUsePercentage = access.get(option).horizontalPercentage() != 1.0; + this.verticalUsePercentage = access.get(option).verticalPercentage() != 1.0; + } + + @Override + public void init() { + super.init(); + PanelBoundary boundary = access.get(option); + addRenderableWidget(horizontalLimit = new Checkbox(0, 0, 20, 20, literal("config.rei.options.layout.boundaries.desc.limit_by_percentage"), this.horizontalUsePercentage, false) { + @Override + public void onPress() { + horizontalUsePercentage = !horizontalUsePercentage; + PanelBoundary boundary = access.get(option); + access.set(option, new PanelBoundary(1.0, boundary.verticalPercentage(), 50, boundary.verticalLimit(), 1.0, boundary.verticalAlign())); + if (!isReducedMotion()) innerAlphaAnimator.setTo(-1.0F, 200); + init(minecraft, BoundariesScreen.this.width, BoundariesScreen.this.height); + } + }); + double v = horizontalUsePercentage ? boundary.horizontalPercentage() : boundary.horizontalLimit() / 50.0; + addRenderableWidget(horizontalSlider = new AbstractSliderButton(0, 0, 20, 20, getSliderMessage("config.rei.options.layout.boundaries.desc.limit", horizontalUsePercentage, v, 50), v) { + @Override + protected void updateMessage() { + setMessage(getSliderMessage("config.rei.options.layout.boundaries.desc.limit", horizontalUsePercentage, value, 50)); + } + + @Override + protected void applyValue() { + PanelBoundary boundary = access.get(option); + if (horizontalUsePercentage) { + access.set(option, new PanelBoundary(value, boundary.verticalPercentage(), 50, boundary.verticalLimit(), boundary.horizontalAlign(), boundary.verticalAlign())); + } else { + access.set(option, new PanelBoundary(1.0, boundary.verticalPercentage(), valueToLimit(value, 50), boundary.verticalLimit(), boundary.horizontalAlign(), boundary.verticalAlign())); + } + if (!isReducedMotion()) innerAlphaAnimator.setTo(-1.0F, 200); + } + }); + addRenderableWidget(horizontalAlignmentSlider = new AbstractSliderButton(0, 0, 20, 20, getHAlignmentSliderMessage(boundary.horizontalAlign()), boundary.horizontalAlign()) { + @Override + protected void updateMessage() { + setMessage(getHAlignmentSliderMessage(value)); + } + + @Override + protected void applyValue() { + PanelBoundary boundary = access.get(option); + access.set(option, new PanelBoundary(boundary.horizontalPercentage(), boundary.verticalPercentage(), boundary.horizontalLimit(), boundary.verticalLimit(), snapPercentage(value), boundary.verticalAlign())); + if (!isReducedMotion()) innerAlphaAnimator.setTo(-1.0F, 200); + } + }); + addRenderableWidget(verticalLimit = new Checkbox(0, 0, 20, 20, literal("config.rei.options.layout.boundaries.desc.limit_by_percentage"), this.verticalUsePercentage, false) { + @Override + public void onPress() { + verticalUsePercentage = !verticalUsePercentage; + PanelBoundary boundary = access.get(option); + access.set(option, new PanelBoundary(boundary.horizontalPercentage(), 1.0, boundary.horizontalLimit(), 1000, boundary.horizontalAlign(), 0.5)); + if (!isReducedMotion()) innerAlphaAnimator.setTo(-1.0F, 200); + init(minecraft, BoundariesScreen.this.width, BoundariesScreen.this.height); + } + }); + v = verticalUsePercentage ? boundary.verticalPercentage() : boundary.verticalLimit() / 1000.0; + addRenderableWidget(verticalSlider = new AbstractSliderButton(0, 0, 20, 20, getSliderMessage("config.rei.options.layout.boundaries.desc.limit", verticalUsePercentage, v, 1000), v) { + @Override + protected void updateMessage() { + setMessage(getSliderMessage("config.rei.options.layout.boundaries.desc.limit", verticalUsePercentage, value, 1000)); + } + + @Override + protected void applyValue() { + PanelBoundary boundary = access.get(option); + if (verticalUsePercentage) { + access.set(option, new PanelBoundary(boundary.horizontalPercentage(), value, boundary.horizontalLimit(), 1000, boundary.horizontalAlign(), boundary.verticalAlign())); + } else { + access.set(option, new PanelBoundary(boundary.horizontalPercentage(), 1.0, boundary.horizontalLimit(), valueToLimit(value, 1000), boundary.horizontalAlign(), boundary.verticalAlign())); + } + if (!isReducedMotion()) innerAlphaAnimator.setTo(-1.0F, 200); + } + }); + addRenderableWidget(verticalAlignmentSlider = new AbstractSliderButton(0, 0, 20, 20, getVAlignmentSliderMessage(boundary.verticalAlign()), boundary.verticalAlign()) { + @Override + protected void updateMessage() { + setMessage(getVAlignmentSliderMessage(value)); + } + + @Override + protected void applyValue() { + PanelBoundary boundary = access.get(option); + access.set(option, new PanelBoundary(boundary.horizontalPercentage(), boundary.verticalPercentage(), boundary.horizontalLimit(), boundary.verticalLimit(), boundary.horizontalAlign(), snapPercentage(value))); + if (!isReducedMotion()) innerAlphaAnimator.setTo(-1.0F, 200); + } + }); + } + + private Component getSliderMessage(String translationKey, boolean usePercentage, double percentage, int max) { + if (usePercentage) { + return translatable(translationKey, String.format("%.1f%%", percentage * 100)); + } else if (valueToLimit(percentage, max) == max) { + return translatable(translationKey, translatable("config.rei.value.default", valueToLimit(percentage, max) + "")); + } else { + return translatable(translationKey, valueToLimit(percentage, max) + ""); + } + } + + private int valueToLimit(double v, int max) { + return Mth.clamp((int) Math.round(v * max), 1, max); + } + + private Component getHAlignmentSliderMessage(double percentage) { + String translationKey = "config.rei.options.layout.boundaries.desc.alignment"; + DisplayPanelLocation location = access.get(AllREIConfigOptions.LOCATION); + if (percentage <= 0.02) { + Component component = translatable(translationKey, translatable("config.rei.options.layout.boundaries.desc.horizontal_alignment.left")); + if (location == DisplayPanelLocation.LEFT) return translatable("config.rei.value.default", component); + return component; + } else if (percentage >= 0.98) { + Component component = translatable(translationKey, translatable("config.rei.options.layout.boundaries.desc.horizontal_alignment.right")); + if (location == DisplayPanelLocation.RIGHT) return translatable("config.rei.value.default", component); + return component; + } else if (percentage >= 0.45 && percentage <= 0.55) { + return translatable(translationKey, translatable("config.rei.options.layout.boundaries.desc.horizontal_alignment.center")); + } else { + return translatable(translationKey, String.format("%.1f%%", percentage * 100)); + } + } + + private Component getVAlignmentSliderMessage(double percentage) { + String translationKey = "config.rei.options.layout.boundaries.desc.alignment"; + if (percentage <= 0.02) { + return translatable(translationKey, translatable("config.rei.options.layout.boundaries.desc.vertical_alignment.top")); + } else if (percentage >= 0.98) { + return translatable(translationKey, translatable("config.rei.options.layout.boundaries.desc.vertical_alignment.bottom")); + } else if (percentage >= 0.45 && percentage <= 0.55) { + return translatable(translationKey, translatable("config.rei.value.default", translatable("config.rei.options.layout.boundaries.desc.vertical_alignment.center"))); + } else { + return translatable(translationKey, String.format("%.1f%%", percentage * 100)); + } + } + + private double snapPercentage(double percentage) { + if (percentage <= 0.02) { + return 0; + } else if (percentage >= 0.98) { + return 1; + } else if (percentage >= 0.45 && percentage <= 0.55) { + return 0.5; + } else { + return percentage; + } + } + + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + this.boundsAnimator.update(delta); + this.innerAlphaAnimator.setTarget(this.innerAlphaAnimator.target() + (1.0F - this.innerAlphaAnimator.target()) * 0.06F); + this.innerAlphaAnimator.update(delta); + this.renderBackground(poses); + Rectangle panelBounds = new Rectangle(this.width * 3 / 10, this.height * 4 / 40, this.width * 4 / 10, this.height * 32 / 40); + Widgets.createCategoryBase(panelBounds).render(poses, mouseX, mouseY, delta); + int y = panelBounds.y + 6; + this.font.draw(poses, translatable("config.rei.options.layout.boundaries.desc.configure").withStyle(ChatFormatting.UNDERLINE), panelBounds.x + 6, y, 0xff404040); + y += 14; + this.font.draw(poses, translatable("config.rei.options.layout.boundaries.desc.horizontal"), panelBounds.x + 6, y, 0xff404040); + this.horizontalLimit.x = panelBounds.x + 6; + this.horizontalLimit.y = y + 10; + this.font.draw(poses, translatable("config.rei.options.layout.boundaries.desc.limit_by_percentage"), horizontalLimit.x + 24, horizontalLimit.y + 6, 0xff404040); + y += 32; + this.horizontalSlider.x = panelBounds.x + 6; + this.horizontalSlider.y = y; + this.horizontalSlider.setWidth(panelBounds.width - 12); + y += 22; + this.horizontalAlignmentSlider.x = panelBounds.x + 6; + this.horizontalAlignmentSlider.y = y; + this.horizontalAlignmentSlider.setWidth(panelBounds.width - 12); + y += 28; + + this.font.draw(poses, translatable("config.rei.options.layout.boundaries.desc.vertical"), panelBounds.x + 6, y, 0xff404040); + this.verticalLimit.x = panelBounds.x + 6; + this.verticalLimit.y = y + 10; + this.font.draw(poses, translatable("config.rei.options.layout.boundaries.desc.limit_by_percentage"), verticalLimit.x + 24, verticalLimit.y + 6, 0xff404040); + y += 32; + this.verticalSlider.x = panelBounds.x + 6; + this.verticalSlider.y = y; + this.verticalSlider.setWidth(panelBounds.width - 12); + y += 22; + this.verticalAlignmentSlider.x = panelBounds.x + 6; + this.verticalAlignmentSlider.y = y; + this.verticalAlignmentSlider.setWidth(panelBounds.width - 12); + + super.render(poses, mouseX, mouseY, delta); + renderPreview(poses, panelBounds); + } + + private void renderPreview(PoseStack poses, Rectangle panelBounds) { + int entrySize = Mth.ceil(18 * access.get(AllREIConfigOptions.ZOOM)); + Rectangle overlayBounds; + DisplayPanelLocation location = access.get(AllREIConfigOptions.LOCATION); + PanelBoundary boundary = access.get(option); + if (location == DisplayPanelLocation.LEFT) { + overlayBounds = new Rectangle(2, 0, panelBounds.x - 2, height); + } else { + overlayBounds = new Rectangle(panelBounds.getMaxX() + 2, 0, width - panelBounds.getMaxX() - 4, height); + } + + double hAlign = location == DisplayPanelLocation.LEFT ? 1 - boundary.horizontalAlign() : boundary.horizontalAlign(); + int widthReduction = (int) Math.round(overlayBounds.width * (1 - boundary.horizontalPercentage())); + overlayBounds.x += (int) Math.round(widthReduction * hAlign); + overlayBounds.width -= widthReduction; + int maxWidth = (int) Math.ceil(entrySize * boundary.horizontalLimit() + entrySize * 0.75); + if (overlayBounds.width > maxWidth) { + overlayBounds.x += (int) Math.round((overlayBounds.width - maxWidth) * hAlign); + overlayBounds.width = maxWidth; + } + int heightReduction = (int) Math.round(overlayBounds.height * (1 - boundary.verticalPercentage())); + overlayBounds.height -= heightReduction; + overlayBounds.y += (int) Math.round(heightReduction * boundary.verticalAlign()); + int maxHeight = entrySize * boundary.verticalLimit(); + if (overlayBounds.height > maxHeight) { + overlayBounds.y += (int) Math.round((overlayBounds.height - maxHeight) * boundary.verticalAlign()); + overlayBounds.height = maxHeight; + } + this.boundsAnimator.setTo(overlayBounds.getFloatingBounds(), isReducedMotion() ? 0 : 200); + overlayBounds = this.boundsAnimator.value().getBounds(); + + this.fillGradient(poses, overlayBounds.x, overlayBounds.y, overlayBounds.getMaxX(), overlayBounds.getMaxY(), 0x80ff0000, 0x80ff0000); + + int width = Math.max(Mth.floor((overlayBounds.width - 2) / (float) entrySize), 1); + int height = Math.max(Mth.floor((overlayBounds.height - 2) / (float) entrySize), 1); + Rectangle innerBounds = new Rectangle((int) (overlayBounds.getCenterX() - width * (entrySize / 2f)), (int) (overlayBounds.getCenterY() - height * (entrySize / 2f)), width * entrySize, height * entrySize); + int color = 0x00ff00 | ((int) (Math.ceil(Mth.clamp(innerAlphaAnimator.floatValue(), 0, 1) * 0x80))) << 24; + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + int slotX = innerBounds.x + i * entrySize + 1; + int slotY = innerBounds.y + j * entrySize + 1; + this.fillGradient(poses, slotX, slotY, slotX + entrySize - 2, slotY + entrySize - 2, color, color); + } + } + } + + @Override + public void onClose() { + this.onClose.run(); + } + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListWidget.java index a3c60fc20..c4cd6d064 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListWidget.java @@ -135,11 +135,11 @@ public abstract class EntryListWidget extends WidgetWithBounds implements Overla private static Rectangle updateInnerBounds(Rectangle bounds) { bounds = bounds.clone(); int heightReduction = (int) Math.round(bounds.height * (1 - ConfigObject.getInstance().getVerticalEntriesBoundariesPercentage())); - bounds.y += heightReduction / 2; + bounds.y += (int) Math.round(heightReduction * ConfigObject.getInstance().getVerticalEntriesBoundariesAlignments()); |
