diff options
25 files changed, 128 insertions, 93 deletions
diff --git a/build.gradle.kts b/build.gradle.kts index ad8cf5f..4b22f3c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,7 +42,6 @@ allprojects { repositories { mavenCentral() - maven("https://maven.terraformersmc.com/releases") maven("https://maven.isxander.dev/releases") maven("https://maven.isxander.dev/snapshots") maven("https://maven.quiltmc.org/repository/release") diff --git a/common/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java b/common/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java index a34329a..0c3afdd 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java @@ -5,15 +5,24 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.gui.components.WidgetSprites; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvents; import java.awt.*; public abstract class AbstractWidget implements GuiEventListener, Renderable, NarratableEntry { + private static final WidgetSprites SPRITES = new WidgetSprites( + new ResourceLocation("widget/button"), // normal + new ResourceLocation("widget/button_disabled"), // disabled & !focused + new ResourceLocation("widget/button_highlighted"), // !disabled & focused + new ResourceLocation("widget/slider_highlighted") // disabled & focused + ); + protected final Minecraft client = Minecraft.getInstance(); protected final Font textRenderer = client.font; protected final int inactiveColor = 0xFFA0A0A0; @@ -74,13 +83,7 @@ public abstract class AbstractWidget implements GuiEventListener, Renderable, Na int width = x2 - x1; int height = y2 - y1; - int i = !enabled ? 0 : hovered ? 2 : 1; - graphics.blit(net.minecraft.client.gui.components.AbstractWidget.WIDGETS_LOCATION, x1, y1, 0, 0, 46 + i * 20, width / 2, height, 256, 256); - graphics.blit(net.minecraft.client.gui.components.AbstractWidget.WIDGETS_LOCATION, x1 + width / 2, y1, 0, 200 - width / 2f, 46 + i * 20, width / 2, height, 256, 256); - - if (hovered && !enabled) { - drawOutline(graphics, x1, y1, x2, y2, 1, 0xFFD0D0D0); - } + graphics.blitSprite(SPRITES.get(enabled, hovered), x1, y1, width, height); } protected void drawOutline(GuiGraphics graphics, int x1, int y1, int x2, int y2, int width, int color) { diff --git a/common/src/main/java/dev/isxander/yacl3/gui/ElementListWidgetExt.java b/common/src/main/java/dev/isxander/yacl3/gui/ElementListWidgetExt.java index e3944ee..a36bd5e 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/ElementListWidgetExt.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/ElementListWidgetExt.java @@ -26,23 +26,18 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten this.y = y; this.x1 = this.x0 + width; this.doSmoothScrolling = smoothScrolling; + setRenderBackground(true); + setRenderHeader(false, 0); } @Override - public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + public boolean mouseScrolled(double mouseX, double mouseY, double horizontal, double vertical) { // default implementation bases scroll step from total height of entries, this is constant - this.setScrollAmount(this.getScrollAmount() - amount * 20); + this.setScrollAmount(this.getScrollAmount() - (vertical + horizontal) * 20); return true; } @Override - protected void renderBackground(GuiGraphics graphics) { - // render transparent background if in-game. - setRenderBackground(true); - setRenderTopAndBottom(false); - } - - @Override protected int getScrollbarPosition() { // default implementation does not respect left/right return this.x1 - 2; diff --git a/common/src/main/java/dev/isxander/yacl3/gui/OptionDescriptionWidget.java b/common/src/main/java/dev/isxander/yacl3/gui/OptionDescriptionWidget.java index 0732c5f..7aa3715 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/OptionDescriptionWidget.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/OptionDescriptionWidget.java @@ -122,9 +122,9 @@ public class OptionDescriptionWidget extends AbstractWidget { } @Override - public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + public boolean mouseScrolled(double mouseX, double mouseY, double horizontal, double vertical) { if (isMouseOver(mouseX, mouseY)) { - targetScrollAmount = Mth.clamp(targetScrollAmount - (int) amount * 10, 0, maxScrollAmount); + targetScrollAmount = Mth.clamp(targetScrollAmount - (int) vertical * 10, 0, maxScrollAmount); lastInteractionTime = currentTimeMS(); return true; } diff --git a/common/src/main/java/dev/isxander/yacl3/gui/OptionListWidget.java b/common/src/main/java/dev/isxander/yacl3/gui/OptionListWidget.java index 3197c44..666c3c8 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/OptionListWidget.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/OptionListWidget.java @@ -150,11 +150,11 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr } @Override - public boolean mouseScrolled(double mouseX, double mouseY, double amount) { - super.mouseScrolled(mouseX, mouseY, amount); + public boolean mouseScrolled(double mouseX, double mouseY, double horizontal, double vertical) { + super.mouseScrolled(mouseX, mouseY, horizontal, vertical); for (Entry child : children()) { - if (child.mouseScrolled(mouseX, mouseY, amount)) + if (child.mouseScrolled(mouseX, mouseY, horizontal, vertical)) break; } @@ -305,8 +305,8 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr } @Override - public boolean mouseScrolled(double mouseX, double mouseY, double amount) { - return widget.mouseScrolled(mouseX, mouseY, amount); + public boolean mouseScrolled(double mouseX, double mouseY, double horizontal, double vertical) { + return widget.mouseScrolled(mouseX, mouseY, horizontal, vertical); } @Override diff --git a/common/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java b/common/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java index b363ada..0bf9c65 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java @@ -43,7 +43,8 @@ public class YACLScreen extends Screen { public TabNavigationBar tabNavigationBar; public ScreenRectangle tabArea; - public Component saveButtonMessage, saveButtonTooltipMessage; + public Component saveButtonMessage; + public Tooltip saveButtonTooltipMessage; private int saveButtonMessageTime; public YACLScreen(YetAnotherConfigLib config, Screen parent) { @@ -122,18 +123,22 @@ public class YACLScreen extends Screen { @Override public void tick() { - tabManager.tickCurrent(); - - if (saveButtonMessage != null) { - if (saveButtonMessageTime > 140) { - saveButtonMessage = null; - saveButtonTooltipMessage = null; - saveButtonMessageTime = 0; - } else { - saveButtonMessageTime++; - //finishedSaveButton.setMessage(saveButtonMessage); - if (saveButtonTooltipMessage != null) { - //finishedSaveButton.setTooltip(saveButtonTooltipMessage); + if (tabManager.getCurrentTab() instanceof TabExt tabExt) { + tabExt.tick(); + } + + if (tabManager.getCurrentTab() instanceof CategoryTab categoryTab) { + if (saveButtonMessage != null) { + if (saveButtonMessageTime > 140) { + saveButtonMessage = null; + saveButtonTooltipMessage = null; + saveButtonMessageTime = 0; + } else { + saveButtonMessageTime++; + categoryTab.saveFinishedButton.setMessage(saveButtonMessage); + if (saveButtonTooltipMessage != null) { + categoryTab.saveFinishedButton.setTooltip(saveButtonTooltipMessage); + } } } } @@ -141,7 +146,7 @@ public class YACLScreen extends Screen { private void setSaveButtonMessage(Component message, Component tooltip) { saveButtonMessage = message; - saveButtonTooltipMessage = tooltip; + saveButtonTooltipMessage = Tooltip.create(tooltip); saveButtonMessageTime = 0; } @@ -222,7 +227,7 @@ public class YACLScreen extends Screen { private final Tooltip tooltip; private ListHolderWidget<OptionListWidget> optionList; - private final Button saveFinishedButton; + public final Button saveFinishedButton; private final Button cancelResetButton; private final Button undoButton; private final SearchFieldWidget searchField; @@ -310,7 +315,6 @@ public class YACLScreen extends Screen { @Override public void tick() { updateButtons(); - searchField.tick(); descriptionWidget.tick(); } diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/slider/SliderControllerElement.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/slider/SliderControllerElement.java index 5590dbf..32d7561 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/slider/SliderControllerElement.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/slider/SliderControllerElement.java @@ -80,11 +80,11 @@ public class SliderControllerElement extends ControllerWidget<ISliderController< } @Override - public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + public boolean mouseScrolled(double mouseX, double mouseY, double horizontal, double vertical) { if (!isAvailable() || (!isMouseOver(mouseX, mouseY)) || (!Screen.hasShiftDown() && !Screen.hasControlDown())) return false; - incrementValue(amount); + incrementValue(vertical); return true; } diff --git a/common/src/main/java/dev/isxander/yacl3/gui/tab/ListHolderWidget.java b/common/src/main/java/dev/isxander/yacl3/gui/tab/ListHolderWidget.java index 5059874..df28d27 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/tab/ListHolderWidget.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/tab/ListHolderWidget.java @@ -67,8 +67,8 @@ public class ListHolderWidget<T extends ElementListWidgetExt<?>> extends Abstrac } @Override - public boolean mouseScrolled(double mouseX, double mouseY, double amount) { - return this.list.mouseScrolled(mouseX, mouseY, amount); + public boolean mouseScrolled(double mouseX, double mouseY, double horizontal, double vertical) { + return this.list.mouseScrolled(mouseX, mouseY, horizontal, vertical); } @Override diff --git a/common/src/main/java/dev/isxander/yacl3/gui/tab/ScrollableNavigationBar.java b/common/src/main/java/dev/isxander/yacl3/gui/tab/ScrollableNavigationBar.java index da1965f..66ba6a3 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/tab/ScrollableNavigationBar.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/tab/ScrollableNavigationBar.java @@ -77,8 +77,8 @@ public class ScrollableNavigationBar extends TabNavigationBar { } @Override - public boolean mouseScrolled(double mouseX, double mouseY, double amount) { - this.setScrollOffset(this.scrollOffset - (int)(amount*10)); + public boolean mouseScrolled(double mouseX, double mouseY, double horizontal, double vertical) { + this.setScrollOffset(this.scrollOffset - (int)(vertical*15)); return true; } diff --git a/common/src/main/java/dev/isxander/yacl3/gui/tab/TabExt.java b/common/src/main/java/dev/isxander/yacl3/gui/tab/TabExt.java index 3b29594..8ddc5c7 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/tab/TabExt.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/tab/TabExt.java @@ -6,4 +6,6 @@ import org.jetbrains.annotations.Nullable; public interface TabExt extends Tab { @Nullable Tooltip getTooltip(); + + default void tick() {} } diff --git a/common/src/main/resources/yacl.accesswidener b/common/src/main/resources/yacl.accesswidener index 303b57a..286b6ab 100644 --- a/common/src/main/resources/yacl.accesswidener +++ b/common/src/main/resources/yacl.accesswidener @@ -3,7 +3,6 @@ accessWidener v2 named extendable method net/minecraft/client/gui/components/AbstractSelectionList children ()Ljava/util/List; extendable method net/minecraft/client/gui/components/AbstractSelectionList getEntryAtPosition (DD)Lnet/minecraft/client/gui/components/AbstractSelectionList$Entry; accessible class net/minecraft/client/gui/components/AbstractSelectionList$Entry -extendable method net/minecraft/client/gui/components/AbstractButton getTextureY ()I accessible method net/minecraft/client/gui/components/tabs/TabNavigationBar <init> (ILnet/minecraft/client/gui/components/tabs/TabManager;Ljava/lang/Iterable;)V accessible field net/minecraft/client/gui/components/tabs/TabNavigationBar layout Lnet/minecraft/client/gui/layouts/GridLayout; accessible field net/minecraft/client/gui/components/tabs/TabNavigationBar width I diff --git a/common/src/main/resources/yacl.mixins.json b/common/src/main/resources/yacl.mixins.json index 3b67b21..0225b44 100644 --- a/common/src/main/resources/yacl.mixins.json +++ b/common/src/main/resources/yacl.mixins.json @@ -7,7 +7,6 @@ }, "client": [ "AbstractSelectionListMixin", - "ContainerEventHandlerMixin", "MinecraftMixin", "OptionInstanceAccessor" ] diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index de256bd..b73de3a 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -38,7 +38,6 @@ dependencies { listOf( "fabric-resource-loader-v0", ).forEach { modApi(fabricApi.module(it, libs.versions.fabric.api.get())) } - modApi(libs.mod.menu) libs.bundles.twelvemonkeys.imageio.let { implementation(it) diff --git a/common/src/main/java/dev/isxander/yacl3/mixin/ContainerEventHandlerMixin.java b/fabric/src/main/java/dev/isxander/yacl3/fabric/mixin/ContainerEventHandlerMixin.java index 9fe8c58..7e1be75 100644 --- a/common/src/main/java/dev/isxander/yacl3/mixin/ContainerEventHandlerMixin.java +++ b/fabric/src/main/java/dev/isxander/yacl3/fabric/mixin/ContainerEventHandlerMixin.java @@ -1,4 +1,4 @@ -package dev.isxander.yacl3.mixin; +package dev.isxander.yacl3.fabric.mixin; import net.minecraft.client.gui.components.events.ContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; @@ -14,6 +14,10 @@ import org.spongepowered.asm.mixin.injection.Redirect; import java.util.List; +/** + * This is in Fabric-only because only the Fabric fork of mixin supports @Redirect on interfaces. + * This will change in the next release of Mixin. + */ @Mixin(ContainerEventHandler.class) public interface ContainerEventHandlerMixin { /** diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 21a6336..f74fcd5 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -22,7 +22,8 @@ "fabric-resource-loader-v0": "*" }, "mixins": [ - "yacl.mixins.json" + "yacl.mixins.json", + "yacl-fabric.mixins.json" ], "custom": { "modmenu": { diff --git a/fabric/src/main/resources/yacl-fabric.mixins.json b/fabric/src/main/resources/yacl-fabric.mixins.json new file mode 100644 index 0000000..5374dd3 --- /dev/null +++ b/fabric/src/main/resources/yacl-fabric.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "dev.isxander.yacl3.fabric.mixin", + "compatibilityLevel": "JAVA_17", + "injectors": { + "defaultRequire": 1 + }, + "client": [ + "ContainerEventHandlerMixin" + ] +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 77242e8..9a4d235 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] # Plugins -architectury_loom = "1.2.+" +architectury_loom = "1.3.+" architectury_plugin = "3.4.+" shadow = "7.1.+" loom_vineflower = "1.11.+" @@ -10,21 +10,21 @@ github_release = "2.+" machete = "2.+" grgit = "5.0.+" -minecraft = "1.20.1" -quilt_mappings = "20" +minecraft = "1.20.2" +quilt_mappings = "1" fabric_loader = "0.14.21" # Common Dependencies -mixin_extras = "0.2.0-beta.8" +mixin_extras = "0.2.0-rc.4" twelvemonkeys_imageio = "3.10.0-SNAPSHOT" quilt_parsers = "0.2.1" # Fabric-like Dependencies -fabric_api = "0.86.0+1.20.1" -mod_menu = "7.1.0" +fabric_api = "0.89.1+1.20.2" +mod_menu = "7.2.2" # Forge Dependencies -forge = "1.20.1-47.1.42" +forge = "1.20.2-48.0.4" [libraries] minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } diff --git a/test-common/src/main/java/dev/isxander/yacl3/test/mixin/TitleScreenMixin.java b/test-common/src/main/java/dev/isxander/yacl3/test/mixin/TitleScreenMixin.java new file mode 100644 index 0000000..c3fddbc --- /dev/null +++ b/test-common/src/main/java/dev/isxander/yacl3/test/mixin/TitleScreenMixin.java @@ -0,0 +1,25 @@ +package dev.isxander.yacl3.test.mixin; + +import dev.isxander.yacl3.test.GuiTest; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.network.chat.Component; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public abstract class TitleScreenMixin extends Screen { + protected TitleScreenMixin(Component title) { + super(title); + } + + @Inject(method = "init", at = @At("RETURN")) + private void addButton(CallbackInfo ci) { + addRenderableWidget(Button.builder(Component.literal("YetAnotherConfigLib Test"), button -> { + minecraft.setScreen(GuiTest.getModConfigScreenFactory(minecraft.screen)); + }).width(150).build()); + } +} diff --git a/test-common/src/main/resources/yacl-test.mixins.json b/test-common/src/main/resources/yacl-test.mixins.json new file mode 100644 index 0000000..c7f9a71 --- /dev/null +++ b/test-common/src/main/resources/yacl-test.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "dev.isxander.yacl3.test.mixin", + "compatibilityLevel": "JAVA_17", + "injectors": { + "defaultRequire": 1 + }, + "client": [ + "TitleScreenMixin" + ] +} diff --git a/test-fabric/src/main/java/dev/isxander/yacl3/test/fabric/ModMenuEntrypoint.java b/test-fabric/src/main/java/dev/isxander/yacl3/test/fabric/ModMenuEntrypoint.java deleted file mode 100644 index 708ce97..0000000 --- a/test-fabric/src/main/java/dev/isxander/yacl3/test/fabric/ModMenuEntrypoint.java +++ /dev/null @@ -1,12 +0,0 @@ -package dev.isxander.yacl3.test.fabric; - -import com.terraformersmc.modmenu.api.ConfigScreenFactory; -import com.terraformersmc.modmenu.api.ModMenuApi; -import dev.isxander.yacl3.test.GuiTest; - -public class ModMenuEntrypoint implements ModMenuApi { - @Override - public ConfigScreenFactory<?> getModConfigScreenFactory() { - return GuiTest::getModConfigScreenFactory; - } -} diff --git a/test-fabric/src/main/resources/fabric.mod.json b/test-fabric/src/main/resources/fabric.mod.json index ff9c0f3..83560f8 100644 --- a/test-fabric/src/main/resources/fabric.mod.json +++ b/test-fabric/src/main/resources/fabric.mod.json @@ -9,5 +9,8 @@ "modmenu": [ "dev.isxander.yacl3.test.fabric.ModMenuEntrypoint" ] - } + }, + "mixins": [ + "yacl-test.mixins.json" + ] } diff --git a/test-forge/build.gradle.kts b/test-forge/build.gradle.kts index b43c7eb..30a02e9 100644 --- a/test-forge/build.gradle.kts +++ b/test-forge/build.gradle.kts @@ -46,7 +46,7 @@ dependencies { forgeRuntimeLibrary(libs.bundles.quilt.parsers) "common"(project(path = ":test-common", configuration = "namedElements")) { isTransitive = false } - implementation(project(path = ":forge", configuration = "namedElements")) + "common"(project(path = ":forge", configuration = "namedElements")) { isTransitive = false } "common"(project(path = ":common", configuration = "namedElements")) { isTransitive = false } } diff --git a/test-forge/src/main/java/dev/isxander/yacl/test/forge/ForgeTest.java b/test-forge/src/main/java/dev/isxander/yacl/test/forge/ForgeTest.java deleted file mode 100644 index fcdb894..0000000 --- a/test-forge/src/main/java/dev/isxander/yacl/test/forge/ForgeTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package dev.isxander.yacl3.test.forge; - -import dev.isxander.yacl3.test.GuiTest; -import net.minecraftforge.client.ConfigScreenHandler; -import net.minecraftforge.fml.ModLoadingContext; -import net.minecraftforge.fml.common.Mod; - -@Mod("yacl_test") -public class ForgeTest { - public ForgeTest() { - ModLoadingContext.get().registerExtensionPoint( - ConfigScreenHandler.ConfigScreenFactory.class, - () -> new ConfigScreenHandler.ConfigScreenFactory( - (minecraft, parent) -> GuiTest.getModConfigScreenFactory(parent) - ) - ); - } -} diff --git a/test-forge/src/main/java/dev/isxander/yacl3/test/forge/ForgeTest.java b/test-forge/src/main/java/dev/isxander/yacl3/test/forge/ForgeTest.java new file mode 100644 index 0000000..f9a95b6 --- /dev/null +++ b/test-forge/src/main/java/dev/isxander/yacl3/test/forge/ForgeTest.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.test.forge; + +import net.minecraftforge.fml.common.Mod; + +@Mod("yacl_test") +public class ForgeTest { + public ForgeTest() { + + } +} diff --git a/test-forge/src/main/resources/META-INF/mods.toml b/test-forge/src/main/resources/META-INF/mods.toml index f4f57d0..cbccdaf 100644 --- a/test-forge/src/main/resources/META-INF/mods.toml +++ b/test-forge/src/main/resources/META-INF/mods.toml @@ -1,5 +1,5 @@ modLoader = "javafml" -loaderVersion = "[45,)" +loaderVersion = "[48,)" #issueTrackerURL = "" license = "LGPL-3.0-or-later" @@ -16,13 +16,13 @@ Test mod for YACL [[dependencies.yacl_test]] modId = "forge" mandatory = true -versionRange = "[45,)" +versionRange = "[48,)" ordering = "NONE" side = "BOTH" [[dependencies.yacl_test]] modId = "minecraft" mandatory = true -versionRange = "[1.19.4,)" +versionRange = "[1.20.2,)" ordering = "NONE" side = "BOTH" |