Search in sources :

Example 1 with Couple

use of com.simibubi.create.foundation.utility.Couple in project Create by Creators-of-Create.

the class SubMenuConfigScreen method init.

@Override
protected void init() {
    super.init();
    listWidth = Math.min(width - 80, 300);
    int yCenter = height / 2;
    int listL = this.width / 2 - listWidth / 2;
    int listR = this.width / 2 + listWidth / 2;
    resetAll = new BoxWidget(listR + 10, yCenter - 25, 20, 20).withPadding(2, 2).withCallback((x, y) -> new ConfirmationScreen().centered().withText(FormattedText.of("Resetting all settings of the " + type.toString() + " config. Are you sure?")).withAction(success -> {
        if (success)
            resetConfig(spec.getValues());
    }).open(this));
    resetAll.showingElement(AllIcons.I_CONFIG_RESET.asStencil().withElementRenderer(BoxWidget.gradientFactory.apply(resetAll)));
    resetAll.getToolTip().add(new TextComponent("Reset All"));
    resetAll.getToolTip().addAll(TooltipHelper.cutStringTextComponent("Click here to reset all settings to their default value.", ChatFormatting.GRAY, ChatFormatting.GRAY));
    saveChanges = new BoxWidget(listL - 30, yCenter - 25, 20, 20).withPadding(2, 2).withCallback((x, y) -> {
        if (ConfigHelper.changes.isEmpty())
            return;
        ConfirmationScreen confirm = new ConfirmationScreen().centered().withText(FormattedText.of("Saving " + ConfigHelper.changes.size() + " changed value" + (ConfigHelper.changes.size() != 1 ? "s" : "") + "")).withAction(success -> {
            if (success)
                saveChanges();
        });
        addAnnotationsToConfirm(confirm).open(this);
    });
    saveChanges.showingElement(AllIcons.I_CONFIG_SAVE.asStencil().withElementRenderer(BoxWidget.gradientFactory.apply(saveChanges)));
    saveChanges.getToolTip().add(new TextComponent("Save Changes"));
    saveChanges.getToolTip().addAll(TooltipHelper.cutStringTextComponent("Click here to save your current changes.", ChatFormatting.GRAY, ChatFormatting.GRAY));
    discardChanges = new BoxWidget(listL - 30, yCenter + 5, 20, 20).withPadding(2, 2).withCallback((x, y) -> {
        if (ConfigHelper.changes.isEmpty())
            return;
        new ConfirmationScreen().centered().withText(FormattedText.of("Discarding " + ConfigHelper.changes.size() + " unsaved change" + (ConfigHelper.changes.size() != 1 ? "s" : "") + "")).withAction(success -> {
            if (success)
                clearChanges();
        }).open(this);
    });
    discardChanges.showingElement(AllIcons.I_CONFIG_DISCARD.asStencil().withElementRenderer(BoxWidget.gradientFactory.apply(discardChanges)));
    discardChanges.getToolTip().add(new TextComponent("Discard Changes"));
    discardChanges.getToolTip().addAll(TooltipHelper.cutStringTextComponent("Click here to discard all the changes you made.", ChatFormatting.GRAY, ChatFormatting.GRAY));
    goBack = new BoxWidget(listL - 30, yCenter + 65, 20, 20).withPadding(2, 2).withCallback(this::attemptBackstep);
    goBack.showingElement(AllIcons.I_CONFIG_BACK.asStencil().withElementRenderer(BoxWidget.gradientFactory.apply(goBack)));
    goBack.getToolTip().add(new TextComponent("Go Back"));
    addRenderableWidget(resetAll);
    addRenderableWidget(saveChanges);
    addRenderableWidget(discardChanges);
    addRenderableWidget(goBack);
    list = new ConfigScreenList(minecraft, listWidth, height - 80, 35, height - 45, 40);
    list.setLeftPos(this.width / 2 - list.getWidth() / 2);
    addRenderableWidget(list);
    search = new ConfigTextField(font, width / 2 - listWidth / 2, height - 35, listWidth, 20);
    search.setResponder(this::updateFilter);
    search.setHint("Search...");
    search.moveCursorToStart();
    addRenderableWidget(search);
    configGroup.valueMap().forEach((key, obj) -> {
        String humanKey = toHumanReadable(key);
        if (obj instanceof AbstractConfig) {
            SubMenuEntry entry = new SubMenuEntry(this, humanKey, spec, (UnmodifiableConfig) obj);
            entry.path = key;
            list.children().add(entry);
            if (configGroup.valueMap().size() == 1)
                ScreenOpener.open(new SubMenuConfigScreen(parent, humanKey, type, spec, (UnmodifiableConfig) obj));
        } else if (obj instanceof ForgeConfigSpec.ConfigValue<?>) {
            ForgeConfigSpec.ConfigValue<?> configValue = (ForgeConfigSpec.ConfigValue<?>) obj;
            ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(configValue.getPath());
            Object value = configValue.get();
            ConfigScreenList.Entry entry = null;
            if (value instanceof Boolean) {
                entry = new BooleanEntry(humanKey, (ForgeConfigSpec.ConfigValue<Boolean>) configValue, valueSpec);
            } else if (value instanceof Enum) {
                entry = new EnumEntry(humanKey, (ForgeConfigSpec.ConfigValue<Enum<?>>) configValue, valueSpec);
            } else if (value instanceof Number) {
                entry = NumberEntry.create(value, humanKey, configValue, valueSpec);
            }
            if (entry == null)
                entry = new LabeledEntry("Impl missing - " + configValue.get().getClass().getSimpleName() + "  " + humanKey + " : " + value);
            if (highlights.contains(key))
                entry.annotations.put("highlight", ":)");
            list.children().add(entry);
        }
    });
    Collections.sort(list.children(), (e, e2) -> {
        int group = (e2 instanceof SubMenuEntry ? 1 : 0) - (e instanceof SubMenuEntry ? 1 : 0);
        if (group == 0 && e instanceof LabeledEntry && e2 instanceof LabeledEntry) {
            LabeledEntry le = (LabeledEntry) e;
            LabeledEntry le2 = (LabeledEntry) e2;
            return le.label.getComponent().getString().compareTo(le2.label.getComponent().getString());
        }
        return group;
    });
    list.search(highlights.stream().findFirst().orElse(""));
    // extras for server configs
    if (type != ModConfig.Type.SERVER)
        return;
    if (minecraft.hasSingleplayerServer())
        return;
    boolean canEdit = minecraft != null && minecraft.player != null && minecraft.player.hasPermissions(2);
    Couple<Color> red = Theme.p(Theme.Key.BUTTON_FAIL);
    Couple<Color> green = Theme.p(Theme.Key.BUTTON_SUCCESS);
    DelegatedStencilElement stencil = new DelegatedStencilElement();
    serverLocked = new BoxWidget(listR + 10, yCenter + 5, 20, 20).withPadding(2, 2).showingElement(stencil);
    if (!canEdit) {
        list.children().forEach(e -> e.setEditable(false));
        resetAll.active = false;
        stencil.withStencilRenderer((ms, w, h, alpha) -> AllIcons.I_CONFIG_LOCKED.render(ms, 0, 0));
        stencil.withElementRenderer((ms, w, h, alpha) -> UIRenderHelper.angledGradient(ms, 90, 8, 0, 16, 16, red));
        serverLocked.withBorderColors(red);
        serverLocked.getToolTip().add(new TextComponent("Locked").withStyle(ChatFormatting.BOLD));
        serverLocked.getToolTip().addAll(TooltipHelper.cutStringTextComponent("You do not have enough permissions to edit the server config. You can still look at the current values here though.", ChatFormatting.GRAY, ChatFormatting.GRAY));
    } else {
        stencil.withStencilRenderer((ms, w, h, alpha) -> AllIcons.I_CONFIG_UNLOCKED.render(ms, 0, 0));
        stencil.withElementRenderer((ms, w, h, alpha) -> UIRenderHelper.angledGradient(ms, 90, 8, 0, 16, 16, green));
        serverLocked.withBorderColors(green);
        serverLocked.getToolTip().add(new TextComponent("Unlocked").withStyle(ChatFormatting.BOLD));
        serverLocked.getToolTip().addAll(TooltipHelper.cutStringTextComponent("You have enough permissions to edit the server config. Changes you make here will be synced with the server when you save them.", ChatFormatting.GRAY, ChatFormatting.GRAY));
    }
    addRenderableWidget(serverLocked);
}
Also used : AllPackets(com.simibubi.create.foundation.networking.AllPackets) Arrays(java.util.Arrays) ForgeConfigSpec(net.minecraftforge.common.ForgeConfigSpec) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ValueEntry(com.simibubi.create.foundation.config.ui.entries.ValueEntry) SubMenuEntry(com.simibubi.create.foundation.config.ui.entries.SubMenuEntry) AllIcons(com.simibubi.create.foundation.gui.AllIcons) ArrayList(java.util.ArrayList) PoseStack(com.mojang.blaze3d.vertex.PoseStack) HashSet(java.util.HashSet) Lists(com.google.common.collect.Lists) ConfirmationScreen(com.simibubi.create.foundation.gui.ConfirmationScreen) EnumEntry(com.simibubi.create.foundation.config.ui.entries.EnumEntry) Minecraft(net.minecraft.client.Minecraft) Locale(java.util.Locale) ChatFormatting(net.minecraft.ChatFormatting) Map(java.util.Map) LabeledEntry(com.simibubi.create.foundation.config.ui.ConfigScreenList.LabeledEntry) AbstractConfig(com.electronwill.nightconfig.core.AbstractConfig) Nonnull(javax.annotation.Nonnull) BooleanEntry(com.simibubi.create.foundation.config.ui.entries.BooleanEntry) ScreenOpener(com.simibubi.create.foundation.gui.ScreenOpener) Nullable(javax.annotation.Nullable) TooltipHelper(com.simibubi.create.foundation.item.TooltipHelper) Color(com.simibubi.create.foundation.utility.Color) Theme(com.simibubi.create.foundation.gui.Theme) Response(com.simibubi.create.foundation.gui.ConfirmationScreen.Response) Screen(net.minecraft.client.gui.screens.Screen) Set(java.util.Set) DelegatedStencilElement(com.simibubi.create.foundation.gui.element.DelegatedStencilElement) UIRenderHelper(com.simibubi.create.foundation.gui.UIRenderHelper) Pair(com.simibubi.create.foundation.utility.Pair) GLFW(org.lwjgl.glfw.GLFW) Consumer(java.util.function.Consumer) NumberEntry(com.simibubi.create.foundation.config.ui.entries.NumberEntry) GuiEventListener(net.minecraft.client.gui.components.events.GuiEventListener) List(java.util.List) TextComponent(net.minecraft.network.chat.TextComponent) BoxWidget(com.simibubi.create.foundation.gui.widget.BoxWidget) Couple(com.simibubi.create.foundation.utility.Couple) FormattedText(net.minecraft.network.chat.FormattedText) ModConfig(net.minecraftforge.fml.config.ModConfig) Collections(java.util.Collections) UnmodifiableConfig(com.electronwill.nightconfig.core.UnmodifiableConfig) LabeledEntry(com.simibubi.create.foundation.config.ui.ConfigScreenList.LabeledEntry) DelegatedStencilElement(com.simibubi.create.foundation.gui.element.DelegatedStencilElement) BoxWidget(com.simibubi.create.foundation.gui.widget.BoxWidget) ValueEntry(com.simibubi.create.foundation.config.ui.entries.ValueEntry) SubMenuEntry(com.simibubi.create.foundation.config.ui.entries.SubMenuEntry) EnumEntry(com.simibubi.create.foundation.config.ui.entries.EnumEntry) LabeledEntry(com.simibubi.create.foundation.config.ui.ConfigScreenList.LabeledEntry) BooleanEntry(com.simibubi.create.foundation.config.ui.entries.BooleanEntry) NumberEntry(com.simibubi.create.foundation.config.ui.entries.NumberEntry) ForgeConfigSpec(net.minecraftforge.common.ForgeConfigSpec) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ConfirmationScreen(com.simibubi.create.foundation.gui.ConfirmationScreen) TextComponent(net.minecraft.network.chat.TextComponent) Color(com.simibubi.create.foundation.utility.Color) SubMenuEntry(com.simibubi.create.foundation.config.ui.entries.SubMenuEntry) AbstractConfig(com.electronwill.nightconfig.core.AbstractConfig) EnumEntry(com.simibubi.create.foundation.config.ui.entries.EnumEntry) BooleanEntry(com.simibubi.create.foundation.config.ui.entries.BooleanEntry)

Example 2 with Couple

use of com.simibubi.create.foundation.utility.Couple in project Create by Creators-of-Create.

the class CrafterScenes method setup.

public static void setup(SceneBuilder scene, SceneBuildingUtil util) {
    scene.title("mechanical_crafter", "Setting up Mechanical Crafters");
    scene.configureBasePlate(0, 0, 5);
    scene.world.showSection(util.select.layer(0), Direction.UP);
    scene.world.modifyKineticSpeed(util.select.everywhere(), f -> 1.5f * f);
    Selection redstone = util.select.fromTo(3, 1, 0, 3, 1, 1);
    Selection kinetics = util.select.fromTo(4, 1, 2, 4, 1, 5);
    BlockPos depotPos = util.grid.at(0, 1, 2);
    Selection crafters = util.select.fromTo(1, 1, 2, 3, 3, 2);
    scene.world.modifyBlocks(crafters, s -> s.setValue(MechanicalCrafterBlock.POINTING, Pointing.DOWN), false);
    scene.world.setKineticSpeed(crafters, 0);
    for (int y = 0; y < 3; y++) {
        for (int x = 0; x < 3; x++) {
            scene.world.showSection(util.select.position(y == 1 ? x + 1 : 3 - x, y + 1, 2), Direction.DOWN);
            scene.idle(2);
        }
    }
    scene.overlay.showText(70).text("An array of Mechanical Crafters can be used to automate any Crafting Recipe").pointAt(util.vector.blockSurface(util.grid.at(1, 2, 2), Direction.WEST)).attachKeyFrame().placeNearTarget();
    scene.idle(80);
    scene.overlay.showControls(new InputWindowElement(util.vector.blockSurface(util.grid.at(2, 3, 2), Direction.NORTH), Pointing.RIGHT).rightClick().withWrench(), 40);
    scene.idle(7);
    scene.world.cycleBlockProperty(util.grid.at(2, 3, 2), MechanicalCrafterBlock.POINTING);
    scene.idle(10);
    scene.overlay.showText(50).text("Using a Wrench, the Crafters' paths can be arranged").pointAt(util.vector.blockSurface(util.grid.at(2, 3, 2), Direction.NORTH)).attachKeyFrame().placeNearTarget();
    scene.idle(60);
    BlockPos[] positions = new BlockPos[] { util.grid.at(3, 1, 2), util.grid.at(2, 1, 2), util.grid.at(1, 1, 2) };
    for (BlockPos pos : positions) {
        scene.overlay.showControls(new InputWindowElement(util.vector.blockSurface(pos, Direction.NORTH), Pointing.RIGHT).rightClick().withWrench(), 10);
        scene.idle(7);
        scene.world.cycleBlockProperty(pos, MechanicalCrafterBlock.POINTING);
        scene.idle(15);
    }
    scene.overlay.showText(100).text("For a valid setup, all paths have to converge into one exit at any side").pointAt(util.vector.blockSurface(util.grid.at(1, 1, 2), Direction.WEST).add(0, 0, -.5f)).colored(PonderPalette.GREEN).attachKeyFrame().placeNearTarget();
    scene.idle(60);
    Collection<Couple<BlockPos>> couples = ImmutableList.of(Couple.create(util.grid.at(3, 3, 2), util.grid.at(3, 2, 2)), Couple.create(util.grid.at(3, 2, 2), util.grid.at(3, 1, 2)), Couple.create(util.grid.at(2, 3, 2), util.grid.at(1, 3, 2)), Couple.create(util.grid.at(3, 1, 2), util.grid.at(2, 1, 2)), Couple.create(util.grid.at(1, 3, 2), util.grid.at(1, 2, 2)), Couple.create(util.grid.at(2, 2, 2), util.grid.at(2, 1, 2)), Couple.create(util.grid.at(1, 2, 2), util.grid.at(1, 1, 2)), Couple.create(util.grid.at(2, 1, 2), util.grid.at(1, 1, 2)), Couple.create(util.grid.at(1, 1, 2), util.grid.at(0, 1, 2)));
    for (Couple<BlockPos> c : couples) {
        scene.idle(5);
        Vec3 p1 = util.vector.blockSurface(c.getFirst(), Direction.NORTH).add(0, 0, -0.125);
        Vec3 p2 = util.vector.blockSurface(c.getSecond(), Direction.NORTH).add(0, 0, -0.125);
        AABB point = new AABB(p1, p1);
        AABB line = new AABB(p1, p2);
        scene.overlay.chaseBoundingBoxOutline(PonderPalette.GREEN, p1, point, 2);
        scene.idle(1);
        scene.overlay.chaseBoundingBoxOutline(PonderPalette.GREEN, p1, line, 30);
    }
    scene.world.showSection(util.select.position(depotPos), Direction.EAST);
    scene.idle(20);
    scene.overlay.showText(60).text("The outputs will be placed into the inventory at the exit").pointAt(util.vector.blockSurface(util.grid.at(0, 1, 2), Direction.NORTH)).placeNearTarget();
    scene.idle(70);
    scene.rotateCameraY(60);
    scene.idle(20);
    scene.world.showSection(kinetics, Direction.NORTH);
    scene.overlay.showText(60).text("Mechanical Crafters require Rotational Force to operate").pointAt(util.vector.blockSurface(util.grid.at(4, 1, 2), Direction.NORTH)).attachKeyFrame().placeNearTarget();
    scene.idle(8);
    scene.world.setKineticSpeed(crafters, -48);
    scene.world.multiplyKineticSpeed(util.select.position(3, 2, 2).add(util.select.position(2, 3, 2)).add(util.select.position(1, 2, 2)).add(util.select.position(2, 1, 2)), -1);
    scene.idle(55);
    scene.rotateCameraY(-60);
    scene.idle(40);
    ItemStack planks = new ItemStack(Items.OAK_PLANKS);
    scene.overlay.showControls(new InputWindowElement(util.vector.blockSurface(util.grid.at(1, 3, 2), Direction.NORTH), Pointing.RIGHT).rightClick().withItem(planks), 40);
    scene.idle(7);
    Class<MechanicalCrafterTileEntity> type = MechanicalCrafterTileEntity.class;
    scene.world.modifyTileEntity(util.grid.at(1, 3, 2), type, mct -> mct.getInventory().insertItem(0, planks.copy(), false));
    scene.idle(10);
    scene.overlay.showText(50).text("Right-Click the front to insert Items manually").pointAt(util.vector.blockSurface(util.grid.at(1, 3, 2), Direction.NORTH)).attachKeyFrame().placeNearTarget();
    scene.idle(60);
    ItemStack alloy = AllItems.ANDESITE_ALLOY.asStack();
    ItemStack log = new ItemStack(Items.OAK_LOG);
    scene.world.setCraftingResult(util.grid.at(1, 1, 2), AllBlocks.ANDESITE_CASING.asStack(4));
    scene.world.modifyTileEntity(util.grid.at(2, 3, 2), type, mct -> mct.getInventory().insertItem(0, planks.copy(), false));
    scene.idle(5);
    scene.world.modifyTileEntity(util.grid.at(3, 3, 2), type, mct -> mct.getInventory().insertItem(0, planks.copy(), false));
    scene.idle(5);
    scene.world.modifyTileEntity(util.grid.at(3, 2, 2), type, mct -> mct.getInventory().insertItem(0, alloy.copy(), false));
    scene.idle(5);
    scene.world.modifyTileEntity(util.grid.at(2, 2, 2), type, mct -> mct.getInventory().insertItem(0, log.copy(), false));
    scene.idle(5);
    scene.world.modifyTileEntity(util.grid.at(1, 2, 2), type, mct -> mct.getInventory().insertItem(0, alloy.copy(), false));
    scene.idle(5);
    scene.world.modifyTileEntity(util.grid.at(1, 1, 2), type, mct -> mct.getInventory().insertItem(0, planks.copy(), false));
    scene.idle(5);
    scene.world.modifyTileEntity(util.grid.at(2, 1, 2), type, mct -> mct.getInventory().insertItem(0, planks.copy(), false));
    scene.idle(5);
    scene.world.modifyTileEntity(util.grid.at(3, 1, 2), type, mct -> mct.getInventory().insertItem(0, planks.copy(), false));
    scene.overlay.showText(80).attachKeyFrame().text("Once every slot of a path contains an Item, the crafting process will begin").pointAt(util.vector.blockSurface(util.grid.at(1, 3, 2), Direction.WEST)).placeNearTarget();
    scene.idle(180);
    scene.world.removeItemsFromBelt(depotPos);
    ItemStack stick = new ItemStack(Items.STICK);
    ItemStack iron = new ItemStack(Items.IRON_INGOT);
    scene.world.setCraftingResult(util.grid.at(1, 1, 2), new ItemStack(Items.IRON_PICKAXE));
    scene.world.modifyTileEntity(util.grid.at(1, 3, 2), type, mct -> mct.getInventory().insertItem(0, iron.copy(), false));
    scene.idle(2);
    scene.world.modifyTileEntity(util.grid.at(2, 3, 2), type, mct -> mct.getInventory().insertItem(0, iron.copy(), false));
    scene.idle(2);
    scene.world.modifyTileEntity(util.grid.at(3, 3, 2), type, mct -> mct.getInventory().insertItem(0, iron.copy(), false));
    scene.idle(2);
    scene.world.modifyTileEntity(util.grid.at(2, 2, 2), type, mct -> mct.getInventory().insertItem(0, stick.copy(), false));
    scene.idle(2);
    scene.world.modifyTileEntity(util.grid.at(2, 1, 2), type, mct -> mct.getInventory().insertItem(0, stick.copy(), false));
    scene.world.showSection(redstone, Direction.SOUTH);
    scene.idle(10);
    scene.overlay.showText(90).attachKeyFrame().colored(PonderPalette.RED).text("For recipes not fully occupying the crafter setup, the start can be forced using a Redstone Pulse").pointAt(util.vector.blockSurface(util.grid.at(1, 2, 2), Direction.NORTH)).placeNearTarget();
    scene.idle(100);
    scene.effects.indicateRedstone(util.grid.at(3, 1, 0));
    scene.world.toggleRedstonePower(redstone);
    scene.idle(20);
    scene.world.toggleRedstonePower(redstone);
}
Also used : Selection(com.simibubi.create.foundation.ponder.Selection) Couple(com.simibubi.create.foundation.utility.Couple) Vec3(net.minecraft.world.phys.Vec3) MechanicalCrafterTileEntity(com.simibubi.create.content.contraptions.components.crafter.MechanicalCrafterTileEntity) InputWindowElement(com.simibubi.create.foundation.ponder.element.InputWindowElement) BlockPos(net.minecraft.core.BlockPos) ItemStack(net.minecraft.world.item.ItemStack) AABB(net.minecraft.world.phys.AABB)

Aggregations

Couple (com.simibubi.create.foundation.utility.Couple)2 AbstractConfig (com.electronwill.nightconfig.core.AbstractConfig)1 UnmodifiableConfig (com.electronwill.nightconfig.core.UnmodifiableConfig)1 Lists (com.google.common.collect.Lists)1 PoseStack (com.mojang.blaze3d.vertex.PoseStack)1 MechanicalCrafterTileEntity (com.simibubi.create.content.contraptions.components.crafter.MechanicalCrafterTileEntity)1 LabeledEntry (com.simibubi.create.foundation.config.ui.ConfigScreenList.LabeledEntry)1 BooleanEntry (com.simibubi.create.foundation.config.ui.entries.BooleanEntry)1 EnumEntry (com.simibubi.create.foundation.config.ui.entries.EnumEntry)1 NumberEntry (com.simibubi.create.foundation.config.ui.entries.NumberEntry)1 SubMenuEntry (com.simibubi.create.foundation.config.ui.entries.SubMenuEntry)1 ValueEntry (com.simibubi.create.foundation.config.ui.entries.ValueEntry)1 AllIcons (com.simibubi.create.foundation.gui.AllIcons)1 ConfirmationScreen (com.simibubi.create.foundation.gui.ConfirmationScreen)1 Response (com.simibubi.create.foundation.gui.ConfirmationScreen.Response)1 ScreenOpener (com.simibubi.create.foundation.gui.ScreenOpener)1 Theme (com.simibubi.create.foundation.gui.Theme)1 UIRenderHelper (com.simibubi.create.foundation.gui.UIRenderHelper)1 DelegatedStencilElement (com.simibubi.create.foundation.gui.element.DelegatedStencilElement)1 BoxWidget (com.simibubi.create.foundation.gui.widget.BoxWidget)1