From 637d6fc3888327deda8e812558e6842db0785232 Mon Sep 17 00:00:00 2001 From: H31M5 <2211923909@qq.com> Date: Thu, 13 Aug 2026 22:54:19 +0800 Subject: [PATCH 1/4] Add CategoryHost and UIFactory for page infrastructure --- .../xtracr/realcamera/gui/CategoryHost.java | 25 ++++++++ .../realcamera/gui/ModelViewScreen.java | 61 ++++++++++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 common/src/main/java/com/xtracr/realcamera/gui/CategoryHost.java diff --git a/common/src/main/java/com/xtracr/realcamera/gui/CategoryHost.java b/common/src/main/java/com/xtracr/realcamera/gui/CategoryHost.java new file mode 100644 index 0000000..55f0d73 --- /dev/null +++ b/common/src/main/java/com/xtracr/realcamera/gui/CategoryHost.java @@ -0,0 +1,25 @@ +package com.xtracr.realcamera.gui; + +import com.xtracr.realcamera.config.BindTarget; +import com.xtracr.realcamera.gui.util.TextureViewport; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public interface CategoryHost { + Minecraft minecraft(); + Font font(); + LayoutConstants layout(); + void hostInitWidgets(int page); + void hostLoadBindTarget(BindTarget target); + BindTarget hostGenBindTarget(); + String getNameValue(); + int getPage(); + void screenRemoveWidget(@NonNull GuiEventListener widget); + + @Nullable ScreenRectangle getTextureViewArea(); + TextureViewport getNewTextureViewport(); +} diff --git a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java index 7937ffa..7eea8a8 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java @@ -17,6 +17,8 @@ import com.xtracr.realcamera.renderer.state.VertexData; import com.xtracr.realcamera.util.LocUtil; import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.*; import net.minecraft.client.gui.components.events.GuiEventListener; @@ -39,8 +41,9 @@ import org.jspecify.annotations.Nullable; import java.util.*; +import java.util.function.UnaryOperator; -public final class ModelViewScreen extends Screen { +public final class ModelViewScreen extends Screen implements CategoryHost { private static final int SELECTION_COLOR = 0x4F3333CC; private static final int SIDE_PANEL_BG = 0xFF444444, CENTER_PANEL_BG = 0xFF222222; private static final int DEFAULT_SCALE = 80, MIN_SCALE = 16, MAX_SCALE = 1024; @@ -885,7 +888,63 @@ public boolean isPauseScreen() { return pauseButton.getValue() == 1; } + // CategoryHost + @Override + public Minecraft minecraft() { + return minecraft; + } + + @Override + public Font font() { + return font; + } + + @Override + public LayoutConstants layout() { + return layoutConstants; + } + + @Override + public void hostInitWidgets(int page) { + initWidgets(page); + } + + @Override + public void hostLoadBindTarget(BindTarget target) { + loadBindTarget(target); + } + + @Override + public BindTarget hostGenBindTarget() { + return genBindTarget(); + } + + @Override + public String getNameValue() { + return nameField.getValue(); + } + + @Override + public int getPage() { + return page; + } + + @Override + public void screenRemoveWidget(@NonNull GuiEventListener widget) { + removeWidget(widget); + } + + @Override + @Nullable + public ScreenRectangle getTextureViewArea() { + return textureViewArea; + } + + @Override public TextureViewport getNewTextureViewport() { return new TextureViewport(textureViewArea, textureScale, (float) textureX, (float) textureY); } + + public record UIFactory (GridLayout grid, LayoutSettings smallSettings, GridLayout.RowHelper rows, UnaryOperator addRenderable){ + } } From 43e06ffa3610f4fd152d90d0b8a21662ef88f6b1 Mon Sep 17 00:00:00 2001 From: H31M5 <2211923909@qq.com> Date: Thu, 13 Aug 2026 02:01:43 +0800 Subject: [PATCH 2/4] Refactor: ConfigsPage --- .../realcamera/gui/ModelViewScreen.java | 256 ++++++------------ .../realcamera/gui/page/ConfigsPage.java | 215 +++++++++++++++ 2 files changed, 294 insertions(+), 177 deletions(-) create mode 100644 common/src/main/java/com/xtracr/realcamera/gui/page/ConfigsPage.java diff --git a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java index 7eea8a8..c17c813 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java @@ -4,14 +4,13 @@ import com.google.common.collect.ImmutableSortedMap; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.serialization.DataResult; import com.xtracr.realcamera.RealCameraCore; import com.xtracr.realcamera.compat.CompatibilityHelper; import com.xtracr.realcamera.config.*; import com.xtracr.realcamera.config.BindTarget.BindConfig; import com.xtracr.realcamera.config.BindTarget.TargetConfig; -import com.xtracr.realcamera.config.codec.ConfigCodec; import com.xtracr.realcamera.gui.components.*; +import com.xtracr.realcamera.gui.page.*; import com.xtracr.realcamera.gui.util.*; import com.xtracr.realcamera.renderer.state.BuiltModelRecord; import com.xtracr.realcamera.renderer.state.VertexData; @@ -54,29 +53,18 @@ public final class ModelViewScreen extends Screen implements CategoryHost { private int x, y, xSize, ySize, middleWidth, page = 0; private InputConstants.Key modifierKey = layoutConstants.modifierKey(); private boolean initialized; - private int modelScale = DEFAULT_SCALE, textureScale = DEFAULT_SCALE, layers = 0, selectionRadius = 10; - private double modelX, modelY, textureX, textureY, clickedX = -1, clickedY = -1; - private float xRot, yRot; - private String focusedTextureId; - private ScreenRectangle modelViewArea; - @Nullable - private ScreenRectangle textureViewArea; - private VertexData[][] focusedPolyhedron = new VertexData[0][]; + + private ConfigsPage configsPage; + + @Nullable private UVRectangleWidget focusedRectWidget; private final StringWidget rectWidgetsSizeWidget = new StringWidget(widgetWidth - 22, widgetHeight, CommonComponents.EMPTY, font); private final EditBox nameField = createTextField(wideWidgetWidth, CONFIG_NAME_MAX_LENGTH); - private final EditBox textureIdField = createTextField(wideWidgetWidth, 1024); private final EditBox disabledNameField = createTextField(compactWidgetWidth, CONFIG_NAME_MAX_LENGTH); private final EditBox disabledIdField = createTextField(wideWidgetWidth, 1024); private final NumberField priorityField = NumberField.ofInt(font, widgetWidth - 2, widgetHeight - 2, 0, null); private final NumberField focusedRectWidgetNumberField = NumberField.ofInt(font, widgetWidth - 2, widgetHeight - 2, 0, null).setMin(0); - private final NumberField forwardUField = createFloatField(widgetWidth, 0); - private final NumberField forwardVField = createFloatField(widgetWidth, 0); - private final NumberField upwardUField = createFloatField(widgetWidth, 0); - private final NumberField upwardVField = createFloatField(widgetWidth, 0); - private final NumberField posUField = createFloatField(widgetWidth, 0); - private final NumberField posVField = createFloatField(widgetWidth, 0); private final NumberField uMinField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); private final NumberField uMaxField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); private final NumberField vMinField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); @@ -89,8 +77,6 @@ public final class ModelViewScreen extends Screen implements CategoryHost { private final CycleIconButton bindYButton = new CycleIconButton(16, 16, 0, 2); private final CycleIconButton bindZButton = new CycleIconButton(16, 16, 1, 2); private final CycleIconButton bindRotButton = new CycleIconButton(16, 16, 1, 2); - private final DoubleSlider entityPitchSlider = createSlider("pitch", wideWidgetWidth, -90.0, 90.0); - private final DoubleSlider entityYawSlider = createSlider("yaw", wideWidgetWidth, -60.0, 60.0); private final NumberWidgetPair offsetXPair = new NumberWidgetPair(font, "offsetX", compactWidgetWidth, widgetHeight, ModConfig.MIN_OFFSET_F, ModConfig.MAX_OFFSET_F); private final NumberWidgetPair offsetYPair = new NumberWidgetPair(font, "offsetY", compactWidgetWidth, widgetHeight, ModConfig.MIN_OFFSET_F, ModConfig.MAX_OFFSET_F); private final NumberWidgetPair offsetZPair = new NumberWidgetPair(font, "offsetZ", compactWidgetWidth, widgetHeight, ModConfig.MIN_OFFSET_F, ModConfig.MAX_OFFSET_F); @@ -101,12 +87,6 @@ public final class ModelViewScreen extends Screen implements CategoryHost { private final List rectWidgets = new ArrayList<>(); private final Map> hiddenNameMap = new HashMap<>(); private final List widgetPairs = List.of(offsetXPair, offsetYPair, offsetZPair, offsetPitchPair, offsetYawPair, offsetRollPair); - private final CycleButton selectingButton = createCyclingButtonBuilder(ImmutableSortedMap.of( - 0, LocUtil.MODEL_VIEW_WIDGET("forwardVector").withStyle(ChatFormatting.GREEN), - 1, LocUtil.MODEL_VIEW_WIDGET("upwardVector").withStyle(ChatFormatting.RED), - 2, LocUtil.MODEL_VIEW_WIDGET("position").withStyle(ChatFormatting.BLUE)), 0) - .withTooltip(_ -> createTooltip("selecting", modifierKey.getDisplayName(), modifierKey.getDisplayName())) - .create(0, 0, wideWidgetWidth, widgetHeight, LocUtil.MODEL_VIEW_WIDGET("selecting")); private final CycleButton disableModeButton = createCyclingButtonBuilder(ImmutableMap.of( 0, LocUtil.MODEL_VIEW_WIDGET("all").withStyle(ChatFormatting.GREEN), 1, LocUtil.MODEL_VIEW_WIDGET("part").withStyle(ChatFormatting.BLUE)), 0) @@ -135,6 +115,21 @@ public final class ModelViewScreen extends Screen implements CategoryHost { .displayOnlyValue() .create(0, 0, compactWidgetWidth, widgetHeight, CommonComponents.EMPTY, (_, _) -> initWidgets(0)); + private ScreenRectangle modelViewArea; + @Nullable + private ScreenRectangle textureViewArea; + private double modelX, modelY; + private double textureX, textureY; + private int modelScale = DEFAULT_SCALE, textureScale = DEFAULT_SCALE; + private int selectionRadius = 10; + private double clickedX = -1, clickedY = -1; + private int layers = 0; + private final UVRectangleWidget disableAllWidget = new UVRectangleWidget(0f, 0f, 1f, 1f); + private float xRot, yRot; + @Nullable + private VertexData[][] focusedPolyhedron = new VertexData[0][]; + private String focusedTextureId; + public ModelViewScreen() { super(LocUtil.MODEL_VIEW_TITLE()); } @@ -149,8 +144,11 @@ protected void init() { x = layoutConstants.x(); y = layoutConstants.y(); modifierKey = layoutConstants.modifierKey(); + if (!initialized) { + configsPage = new ConfigsPage(this); + loadBindTarget(RealCameraCore.currentTarget()); + } initWidgets(page); - if (!initialized) loadBindTarget(RealCameraCore.currentTarget()); initialized = true; } @@ -173,8 +171,8 @@ private void initWidgets(int page) { addRenderableWidget(pauseButton).setPosition(x + (xSize + middleWidth) / 2 - 38, y + 4); addRenderableWidget(new SimpleIconButton(x + (xSize + middleWidth) / 2 - 20, y + 4, 16, 16, 0, 0, _ -> { modelScale = textureScale = DEFAULT_SCALE; - entityYawSlider.setNumber(0); - entityPitchSlider.setNumber(0); + configsPage.setEntityYaw(0); + configsPage.setEntityPitch(0); modelX = modelY = textureX = textureY = 0; xRot = yRot = 0; layers = 0; @@ -187,21 +185,9 @@ private void initLeftWidgets() { grid.defaultCellSetting().padding(4, 2, 0, 0); LayoutSettings smallSettings = grid.newCellSettings().padding(5, 3, 1, 1); GridLayout.RowHelper rows = grid.createRowHelper(2); + UIFactory uiFactory = new UIFactory(grid, smallSettings, rows, this::addRenderableWidget); switch (toggleCategoryButton.getValue()) { - case CONFIGS -> { - rows.addChild(createButton(LocUtil.MODEL_VIEW_WIDGET("import"), widgetWidth, this::importBindTarget)).setTooltip(createTooltip("import")); - rows.addChild(createButton(LocUtil.MODEL_VIEW_WIDGET("export"), widgetWidth, this::exportBindTarget)).setTooltip(createTooltip("export")); - rows.addChild(entityPitchSlider, 2); - rows.addChild(entityYawSlider, 2); - rows.addChild(selectingButton, 2); - rows.addChild(forwardUField, smallSettings); - rows.addChild(forwardVField, smallSettings); - rows.addChild(upwardUField, smallSettings); - rows.addChild(upwardVField, smallSettings); - rows.addChild(posUField, smallSettings); - rows.addChild(posVField, smallSettings); - rows.addChild(textureIdField, 2, smallSettings).setTooltip(createTooltip("textureId")); - } + case CONFIGS -> configsPage.initLeftWidgets(uiFactory); case PREVIEW -> { rows.addChild(toggleSliderButton, 2); LayoutSettings numericControlSettings = grid.newCellSettings().padding(-20, 2, 0, 0); @@ -259,12 +245,12 @@ else if (index > 0 && index <= rectWidgets.size()) { } default -> throw new IllegalStateException("Unexpected value: " + toggleCategoryButton.getValue()); } - rows.addChild(createButton(LocUtil.MODEL_VIEW_WIDGET("save"), widgetWidth, button -> { + rows.addChild(WidgetFactory.button(LocUtil.MODEL_VIEW_WIDGET("save"), widgetWidth, widgetHeight, button -> { if (nameField.getValue().isBlank()) { button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("emptyName").withStyle(ChatFormatting.RED))); return; } - if (textureIdField.getValue().isBlank()) { + if (configsPage.getTextureId().isBlank()) { button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("emptyTextureId").withStyle(ChatFormatting.RED))); return; } @@ -273,10 +259,9 @@ else if (index > 0 && index <= rectWidgets.size()) { BindTarget bindTarget = genBindTarget(); ConfigFile.config().putBindTarget(bindTarget); ConfigFile.save(); - if (toggleCategoryButton.getValue() != Category.DISABLE) loadBindTarget(bindTarget); initWidgets(page); })); - rows.addChild(priorityField, smallSettings).setTooltip(createTooltip("priority")); + rows.addChild(priorityField, smallSettings).setTooltip(WidgetFactory.tooltip("priority")); boolean editableName = toggleCategoryButton.getValue() == Category.CONFIGS; nameField.setEditable(editableName); if (!editableName && nameField.isFocused()) nameField.setFocused(false); @@ -294,67 +279,50 @@ private void initRightWidgets() { rows.addChild(toggleCategoryButton, 3); rows.addChild(new SimpleIconButton(80, 0, _ -> { if (CompatibilityHelper.isModLoaded("cloth-config")) minecraft.setScreen(ConfigScreen.create(this)); - }), smallSettings).setTooltip(createTooltip("toConfigScreen")); - final int widgetsPerPage, size; - if (toggleCategoryButton.getValue() == Category.DISABLE) { - widgetsPerPage = 7; - size = disableConfigs.size(); - rows.addChild(disabledNameField, 3, smallSettings).setTooltip(createTooltip("disabledName")); - rows.addChild(new SimpleIconButton(64, 0, button -> { - if (syncDisableDraft(button, false)) { - button.setTooltip(createTooltip("saveAs")); - initWidgets(page); + }), smallSettings).setTooltip(WidgetFactory.tooltip("toConfigScreen")); + UIFactory uiFactory = new UIFactory(grid, smallSettings, rows, this::addRenderableWidget); + final int pages; + // Return category pages + switch (toggleCategoryButton.getValue()){ + case CONFIGS, PREVIEW -> pages = configsPage.initRightWidgets(uiFactory); + case DISABLE -> { + int widgetsPerPage = 7; + int size = disableConfigs.size(); + rows.addChild(disabledNameField, 3, smallSettings).setTooltip(createTooltip("disabledName")); + rows.addChild(new SimpleIconButton(64, 0, button -> { + if (syncDisableDraft(button, false)) { + button.setTooltip(createTooltip("saveAs")); + initWidgets(page); + } + }), smallSettings).setTooltip(createTooltip("saveAs")); + for (int i = page * widgetsPerPage; i < Math.min((page + 1) * widgetsPerPage, size); i++) { + DisableConfig config = disableConfigs.get(i); + String targetName = nameField.getValue(); + Set hiddenNames = hiddenNameMap.computeIfAbsent(targetName, _ -> new HashSet<>()); + addRenderableWidget(new CycleIconButton(32, 16, hiddenNames.contains(config.name()) ? 1 : 0, 2)) + .setOnValueChange(value -> { + if (value == 0) hiddenNames.remove(config.name()); + else hiddenNames.add(config.name()); + }) + .setPosition(x + (xSize + middleWidth) / 2 - 20, y + 5 + (widgetHeight + 2) * (2 + i % widgetsPerPage)); + rows.addChild(createButton(LocUtil.literal(config.name()), compactWidgetWidth, _ -> { + loadDisableConfig(config); + initWidgets(page); + }), 3).setTooltip(Tooltip.create(LocUtil.literal(config.name()))); + rows.addChild(new SimpleIconButton(48, 0, _ -> { + disableConfigs.removeIf(disableConfig -> disableConfig.name().equals(config.name())); + hiddenNameMap.values().forEach(names -> names.remove(config.name())); + if (disabledNameField.getValue().equals(config.name())) clearDisableDraft(); + initWidgets(page * widgetsPerPage > size - 2 && size > 1 ? page - 1 : page); + }), smallSettings); } - }), smallSettings).setTooltip(createTooltip("saveAs")); - for (int i = page * widgetsPerPage; i < Math.min((page + 1) * widgetsPerPage, size); i++) { - DisableConfig config = disableConfigs.get(i); - String targetName = nameField.getValue(); - Set hiddenNames = hiddenNameMap.computeIfAbsent(targetName, _ -> new HashSet<>()); - addRenderableWidget(new CycleIconButton(32, 16, hiddenNames.contains(config.name()) ? 1 : 0, 2)) - .setOnValueChange(value -> { - if (value == 0) hiddenNames.remove(config.name()); - else hiddenNames.add(config.name()); - }) - .setPosition(x + (xSize + middleWidth) / 2 - 20, y + 5 + (widgetHeight + 2) * (2 + i % widgetsPerPage)); - rows.addChild(createButton(LocUtil.literal(config.name()), compactWidgetWidth, _ -> { - loadDisableConfig(config); - initWidgets(page); - }), 3).setTooltip(Tooltip.create(LocUtil.literal(config.name()))); - rows.addChild(new SimpleIconButton(48, 0, _ -> { - disableConfigs.removeIf(disableConfig -> disableConfig.name().equals(config.name())); - hiddenNameMap.values().forEach(names -> names.remove(config.name())); - if (disabledNameField.getValue().equals(config.name())) clearDisableDraft(); - initWidgets(page * widgetsPerPage > size - 2 && size > 1 ? page - 1 : page); - }), smallSettings); - } - } else { - widgetsPerPage = 8; - List fixedTargetList = ConfigFile.config().binding.fixedTargetList.stream().filter(target -> target.name().equals(RealCameraCore.currentTarget().name())).toList(); - List targetList = ConfigFile.config().binding.targetList; - final int fixedTargetCount = fixedTargetList.size(); - size = fixedTargetCount + targetList.size(); - for (int i = page * widgetsPerPage; i < Math.min((page + 1) * widgetsPerPage, size); i++) { - BindTarget target = i < fixedTargetCount ? fixedTargetList.get(i) : targetList.get(i - fixedTargetCount); - final int targetIndex = i; - String name = target.name(); - rows.addChild(createButton(LocUtil.literal(name), compactWidgetWidth, _ -> loadBindTarget(target)), 3) - .setTooltip(Tooltip.create(name.equals(RealCameraCore.currentTarget().name()) ? - LocUtil.literal(name + "\n").append(LocUtil.MODEL_VIEW_WIDGET("currentConfig")) : - LocUtil.literal(name)) - ); - if (i < fixedTargetCount) continue; - rows.addChild(new SimpleIconButton(48, 0, _ -> { - targetList.remove(target); - ConfigFile.save(); - if (nameField.getValue().equals(target.name())) loadAdjacentBindTarget(fixedTargetList, targetList, targetIndex); - initWidgets(page * widgetsPerPage > size - 2 && size > 1 ? page - 1 : page); - }), smallSettings); + pages = (size - 1) / widgetsPerPage + 1; } + default -> throw new IllegalStateException("Unexpected value: " + toggleCategoryButton.getValue()); } grid.arrangeElements(); FrameLayout.alignInRectangle(grid, x + (xSize + middleWidth) / 2 + 4, y + 2, x + xSize, y + ySize, 0, 0); grid.visitWidgets(this::addRenderableWidget); - final int pages = (size - 1) / widgetsPerPage + 1; addRenderableWidget(new SimpleIconButton(x + (xSize + middleWidth) / 2 + 8, y + ySize - 20, 16, 16, 16, 0, _ -> initWidgets((page - 1 + pages) % pages))); Component pageInfoText = LocUtil.literal((page + 1) + " / " + pages); addRenderableWidget(new StringWidget(x + (3 * xSize + middleWidth) / 4 + 2 - font.width(pageInfoText) / 2, y + ySize - 20, font.width(pageInfoText), widgetHeight, pageInfoText, font)); @@ -399,7 +367,6 @@ public void extractBackground(@NonNull GuiGraphicsExtractor graphics, int mouseX graphics.fill(x, y, x + (xSize - middleWidth) / 2 - 4, y + ySize, SIDE_PANEL_BG); graphics.fill(x + (xSize - middleWidth) / 2, y, x + (xSize + middleWidth) / 2, y + ySize, CENTER_PANEL_BG); graphics.fill(x + (xSize + middleWidth) / 2 + 4, y, x + xSize, y + ySize, SIDE_PANEL_BG); - ModelAnalyser analyser = new ModelAnalyser(); BindTarget target = genBindTarget(); target.offsets().scale *= modelScale; @@ -422,8 +389,8 @@ private List captureRotatedEntity(ModelAnalyser analyser, Bind float entityPrevHeadYaw = entity.yHeadRotO; float entityHeadYaw = entity.yHeadRot; entity.yBodyRot = 180.0f; - entity.setYRot(180.0f + (float) entityYawSlider.getNumber()); - entity.setXRot((float) entityPitchSlider.getNumber()); + entity.setYRot(180.0f + (float) configsPage.getEntityYaw()); + entity.setXRot((float) configsPage.getEntityPitch()); entity.yHeadRot = entity.getYRot(); entity.yHeadRotO = entity.getYRot(); try { @@ -487,40 +454,7 @@ private void renderFlattenedModels(GuiGraphicsExtractor graphics, ModelAnalyser analyser.drawFocusedInTextureArea(graphics, texturePose); graphics.disableScissor(); } - - - private void importBindTarget(Button button) { - String base64 = minecraft.keyboardHandler.getClipboard(); - DataResult result = ConfigCodec.fromCompressedBase64(base64); - switch (result) { - case DataResult.Success success -> { - BindTarget target = success.value(); - loadBindTarget(target); - button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("importSucceeded", LocUtil.literal("'" + target.name() + "'").withStyle(ChatFormatting.WHITE)).withStyle(ChatFormatting.GREEN))); - } - case DataResult.Error error -> { - String message = error.message(); - button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("importFailed", message).withStyle(ChatFormatting.RED))); - } - } - } - - private void exportBindTarget(Button button) { - BindTarget target = genBindTarget(); - DataResult result = ConfigCodec.toCompressedBase64(target); - switch (result) { - case DataResult.Success success -> { - String base64 = success.value(); - minecraft.keyboardHandler.setClipboard(base64); - button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("exportSucceeded").withStyle(ChatFormatting.GREEN))); - } - case DataResult.Error error -> { - String message = error.message(); - button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("exportFailed", message).withStyle(ChatFormatting.RED))); - } - } - } - + private void clearDisableDraft() { disabledNameField.setValue(""); disabledIdField.setValue(""); @@ -631,25 +565,13 @@ private boolean disableConfigNameExists(String name) { return disableConfigs.stream().anyMatch(disableConfig -> disableConfig.name().equals(name)); } - private void loadAdjacentBindTarget(List fixedTargetList, List targetList, int deletedIndex) { - List targets = new ArrayList<>(fixedTargetList); - targets.addAll(targetList); - if (targets.isEmpty()) loadBindTarget(BindTarget.blank("", "")); - else loadBindTarget(targets.get(Math.min(deletedIndex, targets.size() - 1))); - } - private void loadBindTarget(BindTarget target) { if (target.isEmpty()) return; nameField.setValue(target.name()); - textureIdField.setValue(target.textureId()); priorityField.setNumber(target.priority()); + configsPage.loadBindTarget(target); + depthField.setNumber(target.disablingDepth()); - forwardUField.setNumber(target.targetConfig().forwardU()); - forwardVField.setNumber(target.targetConfig().forwardV()); - upwardUField.setNumber(target.targetConfig().upwardU()); - upwardVField.setNumber(target.targetConfig().upwardV()); - posUField.setNumber(target.targetConfig().posU()); - posVField.setNumber(target.targetConfig().posV()); bindXButton.setValue(target.bindConfig().bindX() ? 0 : 1); bindYButton.setValue(target.bindConfig().bindY() ? 0 : 1); bindZButton.setValue(target.bindConfig().bindZ() ? 0 : 1); @@ -674,10 +596,11 @@ private BindTarget genBindTarget() { if (newDisableConfigs.get(i).name().equals(currentDisableConfig.name())) newDisableConfigs.set(i, currentDisableConfig); } - TargetConfig targetConfig = new TargetConfig(forwardUField.getNumber(), forwardVField.getNumber(), upwardUField.getNumber(), upwardVField.getNumber(), posUField.getNumber(), posVField.getNumber()); + TargetConfig targetConfig = configsPage.genTargetConfig(); BindConfig bindConfig = new BindConfig(bindXButton.getValue() == 0, bindYButton.getValue() == 0, bindZButton.getValue() == 0, bindRotButton.getValue() == 0); OffsetConfig offsets = new OffsetConfig(scaleField.getNumber(), offsetXPair.getNumber(), offsetYPair.getNumber(), offsetZPair.getNumber(), offsetPitchPair.getNumber(), offsetYawPair.getNumber(), offsetRollPair.getNumber()); - return new BindTarget(nameField.getValue(), textureIdField.getValue(), priorityField.getNumber(), depthField.getNumber(), targetConfig, bindConfig, offsets, newDisableConfigs); + return new BindTarget(nameField.getValue(), configsPage.getTextureId(), priorityField.getNumber(), depthField.getNumber(), + targetConfig, bindConfig, offsets, newDisableConfigs); } private UVRectangleWidget addRectWidget(UVRectangleWidget rectWidget) { @@ -714,10 +637,6 @@ private CycleButton.Builder createCyclingButtonBuilder(Map return WidgetFactory.cyclingButton(messages, defaultValue); } - private DoubleSlider createSlider(String key, int width, double min, double max) { - return WidgetFactory.slider(key, width, widgetHeight, min, max); - } - private NumberField createFloatField(int width, float defaultValue) { return WidgetFactory.floatField(font, width, widgetHeight, defaultValue); } @@ -737,24 +656,7 @@ private boolean inTextureViewArea(double x, double y) { public boolean leftClickedWithModifier(double mouseX, double mouseY) { if (focusedPolyhedron.length == 0) return false; if (inModelViewArea(mouseX, mouseY) && toggleCategoryButton.getValue() == Category.CONFIGS) { - float u = 0, v = 0; - for (VertexData vertex : focusedPolyhedron[0]) { - u += vertex.u(); - v += vertex.v(); - } - u /= focusedPolyhedron[0].length; - v /= focusedPolyhedron[0].length; - if (selectingButton.getValue() == 0) { - forwardUField.setNumber(u); - forwardVField.setNumber(v); - } else if (selectingButton.getValue() == 1) { - upwardUField.setNumber(u); - upwardVField.setNumber(v); - } else { - posUField.setNumber(u); - posVField.setNumber(v); - } - textureIdField.setValue(focusedTextureId); + configsPage.leftClickedWithModifier(focusedTextureId, focusedPolyhedron); return true; } else if ((inModelViewArea(mouseX, mouseY) || inTextureViewArea(mouseX, mouseY)) && toggleCategoryButton.getValue() == Category.DISABLE) { String disabledId = disabledIdField.getValue(); @@ -811,7 +713,7 @@ public boolean leftClickedWithModifier(double mouseX, double mouseY) { public boolean mouseClicked(@NonNull MouseButtonEvent event, boolean doubleClick) { double storedX = clickedX, storedY = clickedY; clickedX = clickedY = -1; - if (event.input() == InputConstants.MOUSE_BUTTON_LEFT && toggleCategoryButton.getValue() != Category.PREVIEW) { + if (event.input() == InputConstants.MOUSE_BUTTON_LEFT && (toggleCategoryButton.getValue() == Category.CONFIGS || toggleCategoryButton.getValue() == Category.DISABLE)) { if (InputConstants.isKeyDown(minecraft.getWindow(), modifierKey.getValue())) { if (leftClickedWithModifier(event.x(), event.y())) return true; } else if (inTextureViewArea(event.x(), event.y())) { diff --git a/common/src/main/java/com/xtracr/realcamera/gui/page/ConfigsPage.java b/common/src/main/java/com/xtracr/realcamera/gui/page/ConfigsPage.java new file mode 100644 index 0000000..e287f04 --- /dev/null +++ b/common/src/main/java/com/xtracr/realcamera/gui/page/ConfigsPage.java @@ -0,0 +1,215 @@ +package com.xtracr.realcamera.gui.page; + +import com.google.common.collect.ImmutableSortedMap; +import com.mojang.blaze3d.platform.InputConstants; +import com.mojang.serialization.DataResult; +import com.xtracr.realcamera.RealCameraCore; +import com.xtracr.realcamera.config.BindTarget; +import com.xtracr.realcamera.config.BindTarget.TargetConfig; +import com.xtracr.realcamera.config.ConfigFile; +import com.xtracr.realcamera.config.codec.ConfigCodec; +import com.xtracr.realcamera.gui.*; +import com.xtracr.realcamera.gui.components.DoubleSlider; +import com.xtracr.realcamera.gui.components.NumberField; +import com.xtracr.realcamera.gui.components.SimpleIconButton; +import com.xtracr.realcamera.gui.util.WidgetFactory; +import com.xtracr.realcamera.renderer.state.VertexData; +import com.xtracr.realcamera.util.LocUtil; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.CycleButton; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.layouts.GridLayout; +import net.minecraft.client.gui.layouts.LayoutSettings; + +import java.util.List; + +public final class ConfigsPage{ + private final CategoryHost host; + private final int widgetHeight, compactWidgetWidth; + + private final Button importButton, exportButton; + private final DoubleSlider entityPitchSlider, entityYawSlider; + private final NumberField forwardUField, forwardVField; + private final NumberField upwardUField, upwardVField; + private final NumberField posUField, posVField; + private final EditBox textureIdField; + private final CycleButton selectingButton; + + public ConfigsPage(CategoryHost host){ + this.host = host; + Font font = host.font(); + int widgetWidth = host.layout().widgetWidth(); + int wideWidgetWidth = host.layout().wideWidgetWidth(); + widgetHeight = host.layout().widgetHeight(); + compactWidgetWidth = host.layout().compactWidgetWidth(); + InputConstants.Key modifierKey = host.layout().modifierKey(); + + importButton = WidgetFactory.button(LocUtil.MODEL_VIEW_WIDGET("import"), widgetWidth, widgetHeight, this::importBindTarget); + exportButton = WidgetFactory.button(LocUtil.MODEL_VIEW_WIDGET("export"), widgetWidth, widgetHeight, this::exportBindTarget); + entityPitchSlider = WidgetFactory.slider("pitch", wideWidgetWidth, widgetHeight, -90.0, 90.0); + entityYawSlider = WidgetFactory.slider("yaw", wideWidgetWidth, widgetHeight, -60.0, 60.0); + + forwardUField = WidgetFactory.floatField(font, widgetWidth, widgetHeight, 0); + forwardVField = WidgetFactory.floatField(font, widgetWidth, widgetHeight, 0); + upwardUField = WidgetFactory.floatField(font, widgetWidth, widgetHeight, 0); + upwardVField = WidgetFactory.floatField(font, widgetWidth, widgetHeight, 0); + posUField = WidgetFactory.floatField(font, widgetWidth, widgetHeight, 0); + posVField = WidgetFactory.floatField(font, widgetWidth, widgetHeight, 0); + + textureIdField = WidgetFactory.textField(font, wideWidgetWidth, widgetHeight, 1024); + selectingButton = WidgetFactory.cyclingButton(ImmutableSortedMap.of( + "forward", LocUtil.MODEL_VIEW_WIDGET("forwardVector").withStyle(ChatFormatting.GREEN), + "upward", LocUtil.MODEL_VIEW_WIDGET("upwardVector").withStyle(ChatFormatting.RED), + "pos", LocUtil.MODEL_VIEW_WIDGET("position").withStyle(ChatFormatting.BLUE)), "forward") + .withTooltip(_ -> WidgetFactory.tooltip("selecting", modifierKey.getDisplayName(), modifierKey.getDisplayName())) + .create(0, 0, wideWidgetWidth, widgetHeight, LocUtil.MODEL_VIEW_WIDGET("selecting")); + } + + public void initLeftWidgets(ModelViewScreen.UIFactory uiFactory){ + GridLayout.RowHelper rows = uiFactory.rows(); + LayoutSettings smallSettings = uiFactory.smallSettings(); + rows.addChild(importButton).setTooltip(WidgetFactory.tooltip("import")); + rows.addChild(exportButton).setTooltip(WidgetFactory.tooltip("export")); + rows.addChild(entityPitchSlider, 2); + rows.addChild(entityYawSlider, 2); + rows.addChild(selectingButton, 2); + rows.addChild(forwardUField, smallSettings); + rows.addChild(forwardVField, smallSettings); + rows.addChild(upwardUField, smallSettings); + rows.addChild(upwardVField, smallSettings); + rows.addChild(posUField, smallSettings); + rows.addChild(posVField, smallSettings); + rows.addChild(textureIdField, 2, smallSettings).setTooltip(WidgetFactory.tooltip("textureId")); + } + + public int initRightWidgets(ModelViewScreen.UIFactory uiFactory){ + LayoutSettings smallSettings = uiFactory.smallSettings(); + GridLayout.RowHelper rows = uiFactory.rows(); + final int page = host.getPage(); + final int widgetsPerPage = 8; + List fixedTargetList = ConfigFile.config().binding.fixedTargetList.stream().filter(target -> target.name().equals(RealCameraCore.currentTarget().name())).toList(); + List targetList = ConfigFile.config().binding.targetList; + final int fixedTargetCount = fixedTargetList.size(); + final int TargetListSize = fixedTargetCount + targetList.size(); + for (int i = page * widgetsPerPage; i < Math.min((page + 1) * widgetsPerPage, TargetListSize); i++) { + BindTarget target = i < fixedTargetCount ? fixedTargetList.get(i) : targetList.get(i - fixedTargetCount); + String name = target.name(); + rows.addChild(WidgetFactory.button(LocUtil.literal(name), compactWidgetWidth, widgetHeight, _ -> host.hostLoadBindTarget(target)), 3) + .setTooltip(Tooltip.create(name.equals(RealCameraCore.currentTarget().name()) ? + LocUtil.literal(name + "\n").append(LocUtil.MODEL_VIEW_WIDGET("currentConfig")) : + LocUtil.literal(name)) + ); + if (i < fixedTargetCount) continue; + rows.addChild(new SimpleIconButton(48, 0, _ -> { + targetList.remove(target); + ConfigFile.save(); + if (target.name().equals(host.getNameValue())) host.hostLoadBindTarget(BindTarget.blank("", "")); + host.hostInitWidgets(page * widgetsPerPage > TargetListSize - 2 && TargetListSize > 1 ? page - 1 : page); + }), smallSettings); + } + return (TargetListSize - 1) / widgetsPerPage + 1; + } + + public void loadBindTarget(BindTarget target){ + textureIdField.setValue(target.textureId()); + forwardUField.setNumber(target.targetConfig().forwardU()); + forwardVField.setNumber(target.targetConfig().forwardV()); + upwardUField.setNumber(target.targetConfig().upwardU()); + upwardVField.setNumber(target.targetConfig().upwardV()); + posUField.setNumber(target.targetConfig().posU()); + posVField.setNumber(target.targetConfig().posV()); + } + + public TargetConfig genTargetConfig(){ + return new TargetConfig( + forwardUField.getNumber(), + forwardVField.getNumber(), + upwardUField.getNumber(), + upwardVField.getNumber(), + posUField.getNumber(), + posVField.getNumber() + ); + } + + // Private Methods + private void importBindTarget(Button button) { + String base64 = host.minecraft().keyboardHandler.getClipboard().strip(); + DataResult result = ConfigCodec.fromCompressedBase64(base64); + switch (result) { + case DataResult.Success success -> { + BindTarget target = success.value(); + host.hostLoadBindTarget(target); + button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("importSucceeded", LocUtil.literal("'" + target.name() + "'").withStyle(ChatFormatting.WHITE)).withStyle(ChatFormatting.GREEN))); + } + case DataResult.Error error -> { + String message = error.message(); + button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("importFailed", message).withStyle(ChatFormatting.RED))); + } + } + } + + private void exportBindTarget(Button button) { + BindTarget target = host.hostGenBindTarget(); + DataResult result = ConfigCodec.toCompressedBase64(target); + switch (result) { + case DataResult.Success success -> { + String base64 = success.value(); + host.minecraft().keyboardHandler.setClipboard(base64); + button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("exportSucceeded").withStyle(ChatFormatting.GREEN))); + } + case DataResult.Error error -> { + String message = error.message(); + button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("exportFailed", message).withStyle(ChatFormatting.RED))); + } + } + } + + // Helper Methods + public void leftClickedWithModifier(String focusedTextureId, VertexData[][] focusedPolyhedron) { + float u = 0, v = 0; + for (VertexData vertex : focusedPolyhedron[0]) { + u += vertex.u(); + v += vertex.v(); + } + u /= focusedPolyhedron[0].length; + v /= focusedPolyhedron[0].length; + switch (selectingButton.getValue()) { + case "forward" -> { + forwardUField.setNumber(u); + forwardVField.setNumber(v); + } + case "upward" -> { + upwardUField.setNumber(u); + upwardVField.setNumber(v); + } + case "pos" -> { + posUField.setNumber(u); + posVField.setNumber(v); + } + } + textureIdField.setValue(focusedTextureId); + } + + public String getTextureId(){ + return textureIdField.getValue(); + } + + public double getEntityPitch(){ + return entityPitchSlider.getNumber(); + } + + public void setEntityPitch(double value) { + entityPitchSlider.setNumber(value); + } + + public double getEntityYaw(){ + return entityYawSlider.getNumber(); + } + + public void setEntityYaw(double value) { + entityYawSlider.setNumber(value); + } +} From 1bf8fdd40d0bebe9e998bb4feb6b2e1edaab4f5f Mon Sep 17 00:00:00 2001 From: H31M5 <2211923909@qq.com> Date: Thu, 13 Aug 2026 14:21:59 +0800 Subject: [PATCH 3/4] Refactor: PreviewPage --- .../realcamera/gui/ModelViewScreen.java | 62 +-------- .../realcamera/gui/page/PreviewPage.java | 125 ++++++++++++++++++ 2 files changed, 132 insertions(+), 55 deletions(-) create mode 100644 common/src/main/java/com/xtracr/realcamera/gui/page/PreviewPage.java diff --git a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java index c17c813..5cd334e 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java @@ -55,6 +55,7 @@ public final class ModelViewScreen extends Screen implements CategoryHost { private boolean initialized; private ConfigsPage configsPage; + private PreviewPage previewPage; @Nullable @@ -69,24 +70,11 @@ public final class ModelViewScreen extends Screen implements CategoryHost { private final NumberField uMaxField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); private final NumberField vMinField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); private final NumberField vMaxField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); - private final NumberField scaleField = createFloatField(widgetWidth, 1.0f).setMax(64.0f); - private final NumberField depthField = createFloatField(widgetWidth, 0.2f).setMax(16.0f); private final CycleIconButton showTextureButton = new CycleIconButton(48, 16, 0, 2).setOnValueChange(_ -> initWidgets(page)); private final CycleIconButton pauseButton = new CycleIconButton(0, 16, 0, 2); - private final CycleIconButton bindXButton = new CycleIconButton(16, 16, 1, 2); - private final CycleIconButton bindYButton = new CycleIconButton(16, 16, 0, 2); - private final CycleIconButton bindZButton = new CycleIconButton(16, 16, 1, 2); - private final CycleIconButton bindRotButton = new CycleIconButton(16, 16, 1, 2); - private final NumberWidgetPair offsetXPair = new NumberWidgetPair(font, "offsetX", compactWidgetWidth, widgetHeight, ModConfig.MIN_OFFSET_F, ModConfig.MAX_OFFSET_F); - private final NumberWidgetPair offsetYPair = new NumberWidgetPair(font, "offsetY", compactWidgetWidth, widgetHeight, ModConfig.MIN_OFFSET_F, ModConfig.MAX_OFFSET_F); - private final NumberWidgetPair offsetZPair = new NumberWidgetPair(font, "offsetZ", compactWidgetWidth, widgetHeight, ModConfig.MIN_OFFSET_F, ModConfig.MAX_OFFSET_F); - private final NumberWidgetPair offsetPitchPair = new NumberWidgetPair(font, "pitch", compactWidgetWidth, widgetHeight, -180.0f, 180.0f); - private final NumberWidgetPair offsetYawPair = new NumberWidgetPair(font, "yaw", compactWidgetWidth, widgetHeight, -180.0f, 180.0f); - private final NumberWidgetPair offsetRollPair = new NumberWidgetPair(font, "roll", compactWidgetWidth, widgetHeight, -180.0f, 180.0f); private final List disableConfigs = new ArrayList<>(); private final List rectWidgets = new ArrayList<>(); private final Map> hiddenNameMap = new HashMap<>(); - private final List widgetPairs = List.of(offsetXPair, offsetYPair, offsetZPair, offsetPitchPair, offsetYawPair, offsetRollPair); private final CycleButton disableModeButton = createCyclingButtonBuilder(ImmutableMap.of( 0, LocUtil.MODEL_VIEW_WIDGET("all").withStyle(ChatFormatting.GREEN), 1, LocUtil.MODEL_VIEW_WIDGET("part").withStyle(ChatFormatting.BLUE)), 0) @@ -98,15 +86,6 @@ public final class ModelViewScreen extends Screen implements CategoryHost { 2, LocUtil.MODEL_VIEW_WIDGET("range").withStyle(ChatFormatting.BLUE)), 0) .withTooltip(_ -> createTooltip("selectionMode", modifierKey.getDisplayName(), modifierKey.getDisplayName())) .create(0, 0, wideWidgetWidth, widgetHeight, LocUtil.MODEL_VIEW_WIDGET("selectionMode")); - private final CycleButton toggleSliderButton = createCyclingButtonBuilder(ImmutableMap.of( - 0, LocUtil.MODEL_VIEW_WIDGET("toggleSliderToField"), - 1, LocUtil.MODEL_VIEW_WIDGET("toggleFieldToSlider")), 0) - .displayOnlyValue() - .create(0, 0, wideWidgetWidth, widgetHeight, CommonComponents.EMPTY, (_, i) -> { - boolean useSlider = i == 0; - for (NumberWidgetPair pair : widgetPairs) pair.syncAndSwitch(useSlider); - initWidgets(page); - }); private final CycleButton toggleCategoryButton = createCyclingButtonBuilder(ImmutableSortedMap.of( Category.CONFIGS, LocUtil.MODEL_VIEW_WIDGET(Category.CONFIGS.next().id), Category.PREVIEW, LocUtil.MODEL_VIEW_WIDGET(Category.PREVIEW.next().id), @@ -146,6 +125,7 @@ protected void init() { modifierKey = layoutConstants.modifierKey(); if (!initialized) { configsPage = new ConfigsPage(this); + previewPage = new PreviewPage(this); loadBindTarget(RealCameraCore.currentTarget()); } initWidgets(page); @@ -188,23 +168,7 @@ private void initLeftWidgets() { UIFactory uiFactory = new UIFactory(grid, smallSettings, rows, this::addRenderableWidget); switch (toggleCategoryButton.getValue()) { case CONFIGS -> configsPage.initLeftWidgets(uiFactory); - case PREVIEW -> { - rows.addChild(toggleSliderButton, 2); - LayoutSettings numericControlSettings = grid.newCellSettings().padding(-20, 2, 0, 0); - rows.addChild(bindXButton, smallSettings).setTooltip(createTooltip("bindButtons")); - rows.addChild(offsetXPair, numericControlSettings); - rows.addChild(bindYButton, smallSettings).setTooltip(createTooltip("bindButtons")); - rows.addChild(offsetYPair, numericControlSettings); - rows.addChild(bindZButton, smallSettings).setTooltip(createTooltip("bindButtons")); - rows.addChild(offsetZPair, numericControlSettings); - rows.addChild(bindRotButton, smallSettings).setTooltip(createTooltip("bindButtons")); - rows.addChild(offsetPitchPair, numericControlSettings); - rows.addChild(offsetYawPair, 2, grid.newCellSettings().padding(26, 2, 0, 0)); - rows.addChild(new SimpleIconButton(0, 0, _ -> widgetPairs.forEach(pair -> pair.setNumber(0))), smallSettings); - rows.addChild(offsetRollPair, numericControlSettings); - rows.addChild(scaleField, smallSettings).setTooltip(createTooltip("scale")); - rows.addChild(depthField, smallSettings).setTooltip(createTooltip("depth")); - } + case PREVIEW -> previewPage.initLeftWidgets(uiFactory); case DISABLE -> { LayoutSettings offsetXSettings = grid.newCellSettings().padding(-13, 3, 1, 1); rows.addChild(disableModeButton, 2); @@ -570,20 +534,8 @@ private void loadBindTarget(BindTarget target) { nameField.setValue(target.name()); priorityField.setNumber(target.priority()); configsPage.loadBindTarget(target); + previewPage.loadBindTarget(target); - depthField.setNumber(target.disablingDepth()); - bindXButton.setValue(target.bindConfig().bindX() ? 0 : 1); - bindYButton.setValue(target.bindConfig().bindY() ? 0 : 1); - bindZButton.setValue(target.bindConfig().bindZ() ? 0 : 1); - bindRotButton.setValue(target.bindConfig().bindRotation() ? 0 : 1); - OffsetConfig offsets = target.offsets(); - scaleField.setNumber(offsets.scale); - offsetXPair.setNumber(offsets.x); - offsetYPair.setNumber(offsets.y); - offsetZPair.setNumber(offsets.z); - offsetPitchPair.setNumber(offsets.pitch); - offsetYawPair.setNumber(offsets.yaw); - offsetRollPair.setNumber(offsets.roll); disableConfigs.clear(); disableConfigs.addAll(target.disableConfigs()); clearDisableDraft(); @@ -597,9 +549,9 @@ private BindTarget genBindTarget() { newDisableConfigs.set(i, currentDisableConfig); } TargetConfig targetConfig = configsPage.genTargetConfig(); - BindConfig bindConfig = new BindConfig(bindXButton.getValue() == 0, bindYButton.getValue() == 0, bindZButton.getValue() == 0, bindRotButton.getValue() == 0); - OffsetConfig offsets = new OffsetConfig(scaleField.getNumber(), offsetXPair.getNumber(), offsetYPair.getNumber(), offsetZPair.getNumber(), offsetPitchPair.getNumber(), offsetYawPair.getNumber(), offsetRollPair.getNumber()); - return new BindTarget(nameField.getValue(), configsPage.getTextureId(), priorityField.getNumber(), depthField.getNumber(), + BindConfig bindConfig = previewPage.genBindConfig(); + OffsetConfig offsets = previewPage.genOffsetConfig(); + return new BindTarget(nameField.getValue(), configsPage.getTextureId(), priorityField.getNumber(), previewPage.getDepth(), targetConfig, bindConfig, offsets, newDisableConfigs); } diff --git a/common/src/main/java/com/xtracr/realcamera/gui/page/PreviewPage.java b/common/src/main/java/com/xtracr/realcamera/gui/page/PreviewPage.java new file mode 100644 index 0000000..c9e3ed3 --- /dev/null +++ b/common/src/main/java/com/xtracr/realcamera/gui/page/PreviewPage.java @@ -0,0 +1,125 @@ +package com.xtracr.realcamera.gui.page; + +import com.google.common.collect.ImmutableMap; +import com.xtracr.realcamera.config.BindTarget; +import com.xtracr.realcamera.config.BindTarget.BindConfig; +import com.xtracr.realcamera.config.ModConfig; +import com.xtracr.realcamera.config.OffsetConfig; +import com.xtracr.realcamera.gui.*; +import com.xtracr.realcamera.gui.components.CycleIconButton; +import com.xtracr.realcamera.gui.components.NumberField; +import com.xtracr.realcamera.gui.components.NumberWidgetPair; +import com.xtracr.realcamera.gui.components.SimpleIconButton; +import com.xtracr.realcamera.gui.util.WidgetFactory; +import com.xtracr.realcamera.util.LocUtil; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.CycleButton; +import net.minecraft.client.gui.layouts.GridLayout; +import net.minecraft.client.gui.layouts.LayoutSettings; +import net.minecraft.network.chat.CommonComponents; + +import java.util.List; + +public final class PreviewPage { + private final CycleButton toggleSliderButton; + private final NumberField scaleField; + private final NumberField depthField; + private final CycleIconButton bindXButton, bindYButton, bindZButton, bindRotButton; + private final NumberWidgetPair offsetXPair, offsetYPair, offsetZPair; + private final NumberWidgetPair offsetPitchPair, offsetYawPair, offsetRollPair; + + private final List widgetPairs; + + + public PreviewPage(CategoryHost host) { + Font font = host.font(); + LayoutConstants layout = host.layout(); + int widgetWidth = layout.widgetWidth(), widgetHeight = layout.widgetHeight(); + int wideWidgetWidth = layout.wideWidgetWidth(); + int compactWidgetWidth = layout.compactWidgetWidth(); + + bindXButton = new CycleIconButton(16, 16, 1, 2); + bindYButton = new CycleIconButton(16, 16, 0, 2); + bindZButton = new CycleIconButton(16, 16, 1, 2); + bindRotButton = new CycleIconButton(16, 16, 1, 2); + + offsetXPair = new NumberWidgetPair(font, "offsetX", compactWidgetWidth, widgetHeight, ModConfig.MIN_OFFSET_F, ModConfig.MAX_OFFSET_F); + offsetYPair = new NumberWidgetPair(font, "offsetY", compactWidgetWidth, widgetHeight, ModConfig.MIN_OFFSET_F, ModConfig.MAX_OFFSET_F); + offsetZPair = new NumberWidgetPair(font, "offsetZ", compactWidgetWidth, widgetHeight, ModConfig.MIN_OFFSET_F, ModConfig.MAX_OFFSET_F); + + offsetPitchPair = new NumberWidgetPair(font, "pitch", compactWidgetWidth, widgetHeight, -180.0f, 180.0f); + offsetYawPair = new NumberWidgetPair(font, "yaw", compactWidgetWidth, widgetHeight, -180.0f, 180.0f); + offsetRollPair = new NumberWidgetPair(font, "roll", compactWidgetWidth, widgetHeight, -180.0f, 180.0f); + + scaleField = WidgetFactory.floatField(font, widgetWidth, widgetHeight, 1.0f).setMax(64.0f); + depthField = WidgetFactory.floatField(font, widgetWidth, widgetHeight, 0.2f).setMax(16.0f); + + widgetPairs = List.of(offsetXPair, offsetYPair, offsetZPair, + offsetPitchPair, offsetYawPair, offsetRollPair); + + toggleSliderButton = WidgetFactory.cyclingButton(ImmutableMap.of( + 0, LocUtil.MODEL_VIEW_WIDGET("toggleSliderToField"), + 1, LocUtil.MODEL_VIEW_WIDGET("toggleFieldToSlider")), 0) + .displayOnlyValue() + .create(0, 0, wideWidgetWidth, widgetHeight, CommonComponents.EMPTY, (_, i) -> { + boolean useSlider = i == 0; + for (NumberWidgetPair pair : widgetPairs) pair.syncAndSwitch(useSlider); + host.hostInitWidgets(host.getPage()); + }); + } + + public void initLeftWidgets(ModelViewScreen.UIFactory uiFactory){ + GridLayout grid = uiFactory.grid(); + GridLayout.RowHelper rows = uiFactory.rows(); + LayoutSettings smallSettings = uiFactory.smallSettings(); + + rows.addChild(toggleSliderButton, 2); + LayoutSettings numericControlSettings = grid.newCellSettings().padding(-20, 2, 0, 0); + rows.addChild(bindXButton, smallSettings).setTooltip(WidgetFactory.tooltip("bindButtons")); + rows.addChild(offsetXPair, numericControlSettings); + rows.addChild(bindYButton, smallSettings).setTooltip(WidgetFactory.tooltip("bindButtons")); + rows.addChild(offsetYPair, numericControlSettings); + rows.addChild(bindZButton, smallSettings).setTooltip(WidgetFactory.tooltip("bindButtons")); + rows.addChild(offsetZPair, numericControlSettings); + rows.addChild(bindRotButton, smallSettings).setTooltip(WidgetFactory.tooltip("bindButtons")); + rows.addChild(offsetPitchPair, numericControlSettings); + rows.addChild(offsetYawPair, 2, grid.newCellSettings().padding(26, 2, 0, 0)); + rows.addChild(new SimpleIconButton(0, 0, _ -> widgetPairs.forEach(pair -> pair.setNumber(0))), smallSettings); + rows.addChild(offsetRollPair, numericControlSettings); + rows.addChild(scaleField, smallSettings).setTooltip(WidgetFactory.tooltip("scale")); + rows.addChild(depthField, smallSettings).setTooltip(WidgetFactory.tooltip("depth")); + } + + + public void loadBindTarget(BindTarget target){ + depthField.setNumber(target.disablingDepth()); + bindXButton.setValue(target.bindConfig().bindX() ? 0 : 1); + bindYButton.setValue(target.bindConfig().bindY() ? 0 : 1); + bindZButton.setValue(target.bindConfig().bindZ() ? 0 : 1); + bindRotButton.setValue(target.bindConfig().bindRotation() ? 0 : 1); + OffsetConfig offsets = target.offsets(); + scaleField.setNumber(offsets.scale); + offsetXPair.setNumber(offsets.x); + offsetYPair.setNumber(offsets.y); + offsetZPair.setNumber(offsets.z); + offsetPitchPair.setNumber(offsets.pitch); + offsetYawPair.setNumber(offsets.yaw); + offsetRollPair.setNumber(offsets.roll); + } + + public BindConfig genBindConfig(){ + return new BindConfig(bindXButton.getValue() == 0, + bindYButton.getValue() == 0, + bindZButton.getValue() == 0, + bindRotButton.getValue() == 0); + } + + public OffsetConfig genOffsetConfig(){ + return new OffsetConfig(scaleField.getNumber(), offsetXPair.getNumber(), offsetYPair.getNumber(), offsetZPair.getNumber(), + offsetPitchPair.getNumber(), offsetYawPair.getNumber(), offsetRollPair.getNumber()); + } + + public float getDepth(){ + return depthField.getNumber(); + } +} From 2d8741dc6d3d22c910a4e22219d9bd13e4cb9113 Mon Sep 17 00:00:00 2001 From: H31M5 <2211923909@qq.com> Date: Thu, 13 Aug 2026 22:09:17 +0800 Subject: [PATCH 4/4] Refactor: DisablePage --- .../realcamera/gui/ModelViewScreen.java | 374 ++--------------- .../realcamera/gui/page/DisablePage.java | 383 ++++++++++++++++++ .../assets/realcamera/lang/en_us.json | 4 +- .../assets/realcamera/lang/zh_cn.json | 4 +- 4 files changed, 419 insertions(+), 346 deletions(-) create mode 100644 common/src/main/java/com/xtracr/realcamera/gui/page/DisablePage.java diff --git a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java index 5cd334e..5d91ea8 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java @@ -1,6 +1,5 @@ package com.xtracr.realcamera.gui; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedMap; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.vertex.PoseStack; @@ -56,41 +55,17 @@ public final class ModelViewScreen extends Screen implements CategoryHost { private ConfigsPage configsPage; private PreviewPage previewPage; - - - @Nullable - private UVRectangleWidget focusedRectWidget; - private final StringWidget rectWidgetsSizeWidget = new StringWidget(widgetWidth - 22, widgetHeight, CommonComponents.EMPTY, font); - private final EditBox nameField = createTextField(wideWidgetWidth, CONFIG_NAME_MAX_LENGTH); - private final EditBox disabledNameField = createTextField(compactWidgetWidth, CONFIG_NAME_MAX_LENGTH); - private final EditBox disabledIdField = createTextField(wideWidgetWidth, 1024); + private DisablePage disablePage; + + private final EditBox nameField = WidgetFactory.textField(font, wideWidgetWidth, widgetHeight, CONFIG_NAME_MAX_LENGTH); private final NumberField priorityField = NumberField.ofInt(font, widgetWidth - 2, widgetHeight - 2, 0, null); - private final NumberField focusedRectWidgetNumberField = NumberField.ofInt(font, widgetWidth - 2, widgetHeight - 2, 0, null).setMin(0); - private final NumberField uMinField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); - private final NumberField uMaxField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); - private final NumberField vMinField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); - private final NumberField vMaxField = createFloatField(widgetWidth * 2 - 24, 0).setMin(-1f).setMax(2f); - private final CycleIconButton showTextureButton = new CycleIconButton(48, 16, 0, 2).setOnValueChange(_ -> initWidgets(page)); private final CycleIconButton pauseButton = new CycleIconButton(0, 16, 0, 2); - private final List disableConfigs = new ArrayList<>(); - private final List rectWidgets = new ArrayList<>(); - private final Map> hiddenNameMap = new HashMap<>(); - private final CycleButton disableModeButton = createCyclingButtonBuilder(ImmutableMap.of( - 0, LocUtil.MODEL_VIEW_WIDGET("all").withStyle(ChatFormatting.GREEN), - 1, LocUtil.MODEL_VIEW_WIDGET("part").withStyle(ChatFormatting.BLUE)), 0) - .withTooltip(_ -> createTooltip("disableMode", modifierKey.getDisplayName())) - .create(0, 0, wideWidgetWidth, widgetHeight, LocUtil.MODEL_VIEW_WIDGET("disableMode")); - private final CycleButton selectionModeButton = createCyclingButtonBuilder(ImmutableSortedMap.of( - 0, LocUtil.MODEL_VIEW_WIDGET("single"), - 1, LocUtil.MODEL_VIEW_WIDGET("multiple"), - 2, LocUtil.MODEL_VIEW_WIDGET("range").withStyle(ChatFormatting.BLUE)), 0) - .withTooltip(_ -> createTooltip("selectionMode", modifierKey.getDisplayName(), modifierKey.getDisplayName())) - .create(0, 0, wideWidgetWidth, widgetHeight, LocUtil.MODEL_VIEW_WIDGET("selectionMode")); - private final CycleButton toggleCategoryButton = createCyclingButtonBuilder(ImmutableSortedMap.of( + private final CycleIconButton showTextureButton = new CycleIconButton(48, 16, 0, 2).setOnValueChange(_ -> initWidgets(page)); + private final CycleButton toggleCategoryButton = WidgetFactory.cyclingButton(ImmutableSortedMap.of( Category.CONFIGS, LocUtil.MODEL_VIEW_WIDGET(Category.CONFIGS.next().id), Category.PREVIEW, LocUtil.MODEL_VIEW_WIDGET(Category.PREVIEW.next().id), Category.DISABLE, LocUtil.MODEL_VIEW_WIDGET(Category.DISABLE.next().id)), Category.CONFIGS) - .withTooltip(category -> createTooltip(category.next().id)) + .withTooltip(category -> WidgetFactory.tooltip(category.next().id)) .displayOnlyValue() .create(0, 0, compactWidgetWidth, widgetHeight, CommonComponents.EMPTY, (_, _) -> initWidgets(0)); @@ -125,7 +100,8 @@ protected void init() { modifierKey = layoutConstants.modifierKey(); if (!initialized) { configsPage = new ConfigsPage(this); - previewPage = new PreviewPage(this); + previewPage = new PreviewPage(this); + disablePage = new DisablePage(this); loadBindTarget(RealCameraCore.currentTarget()); } initWidgets(page); @@ -140,12 +116,12 @@ private void initWidgets(int page) { } else { modelViewArea = new ScreenRectangle(x + (xSize - middleWidth) / 2, y, middleWidth, ySize); textureViewArea = null; - if (focusedRectWidget != null && focusedRectWidget.isFocused()) setFocused(null); - focusedRectWidget = null; + if (disablePage.getFocusedRectWidget() != null && disablePage.getFocusedRectWidget().isFocused()) setFocused(null); + disablePage.setFocusedRectWidget(null); } clearWidgets(); initLeftWidgets(); - if (textureViewArea != null) rectWidgets.forEach(this::addRenderableWidget); + if (textureViewArea != null) disablePage.renderableRectWidgets(this::addRenderableWidget, getNewTextureViewport()); if (toggleCategoryButton.getValue() == Category.DISABLE) addRenderableWidget(showTextureButton).setPosition(x + (xSize - middleWidth) / 2 + 4, y + 4); addRenderableWidget(pauseButton).setPosition(x + (xSize + middleWidth) / 2 - 38, y + 4); @@ -169,44 +145,7 @@ private void initLeftWidgets() { switch (toggleCategoryButton.getValue()) { case CONFIGS -> configsPage.initLeftWidgets(uiFactory); case PREVIEW -> previewPage.initLeftWidgets(uiFactory); - case DISABLE -> { - LayoutSettings offsetXSettings = grid.newCellSettings().padding(-13, 3, 1, 1); - rows.addChild(disableModeButton, 2); - rows.addChild(disabledIdField, 2, smallSettings).setTooltip(createTooltip("textureId")); - rows.addChild(selectionModeButton, 2); - rows.addChild(focusedRectWidgetNumberField.setMax(rectWidgets.size()), smallSettings).setOnValueChange(index -> { - if (index == 0) focusedRectWidget = null; - else if (index > 0 && index <= rectWidgets.size()) { - focusedRectWidget = rectWidgets.get(index - 1); - uMinField.setNumber(focusedRectWidget.uMin); - vMinField.setNumber(focusedRectWidget.vMin); - uMaxField.setNumber(focusedRectWidget.uMax); - vMaxField.setNumber(focusedRectWidget.vMax); - } - }).setTooltip(createTooltip("focusedRectangleNumber")); - addRenderableWidget(new StringWidget(x + 4 + widgetWidth + 3, y + 4 + (widgetHeight + 2) * 3, 6, widgetHeight, LocUtil.literal("/"), font)); - rectWidgetsSizeWidget.setPosition(x + 4 + widgetWidth + 5 + font.width("/"), y + 4 + (widgetHeight + 2) * 3); - rectWidgetsSizeWidget.setMessage(LocUtil.literal(String.valueOf(rectWidgets.size()))); - addRenderableWidget(rectWidgetsSizeWidget); - rows.addChild(new SimpleIconButton(48, 0, _ -> deleteFocusedRectWidget()), grid.newCellSettings().padding(5 + widgetWidth - 18, 3, 1, 1)) - .setTooltip(createTooltip("deleteSelectedRectangle")); - rows.addChild(new StringWidget(26, widgetHeight, LocUtil.literal("uMin:"), font)); - rows.addChild(uMinField, offsetXSettings).setOnValueChange(f -> { - if (focusedRectWidget != null) focusedRectWidget.uMin = f; - }); - rows.addChild(new StringWidget(26, widgetHeight, LocUtil.literal("vMin:"), font)); - rows.addChild(vMinField, offsetXSettings).setOnValueChange(f -> { - if (focusedRectWidget != null) focusedRectWidget.vMin = f; - }); - rows.addChild(new StringWidget(26, widgetHeight, LocUtil.literal("uMax:"), font)); - rows.addChild(uMaxField, offsetXSettings).setOnValueChange(f -> { - if (focusedRectWidget != null) focusedRectWidget.uMax = f; - }); - rows.addChild(new StringWidget(26, widgetHeight, LocUtil.literal("vMax:"), font)); - rows.addChild(vMaxField, offsetXSettings).setOnValueChange(f -> { - if (focusedRectWidget != null) focusedRectWidget.vMax = f; - }); - } + case DISABLE -> disablePage.initLeftWidgets(uiFactory); default -> throw new IllegalStateException("Unexpected value: " + toggleCategoryButton.getValue()); } rows.addChild(WidgetFactory.button(LocUtil.MODEL_VIEW_WIDGET("save"), widgetWidth, widgetHeight, button -> { @@ -218,7 +157,7 @@ else if (index > 0 && index <= rectWidgets.size()) { button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("emptyTextureId").withStyle(ChatFormatting.RED))); return; } - if (toggleCategoryButton.getValue() == Category.DISABLE && !syncDisableDraft(button, true)) return; + if (toggleCategoryButton.getValue() == Category.DISABLE && !disablePage.syncDisableConfig(button, true)) return; button.setTooltip(null); BindTarget bindTarget = genBindTarget(); ConfigFile.config().putBindTarget(bindTarget); @@ -229,7 +168,7 @@ else if (index > 0 && index <= rectWidgets.size()) { boolean editableName = toggleCategoryButton.getValue() == Category.CONFIGS; nameField.setEditable(editableName); if (!editableName && nameField.isFocused()) nameField.setFocused(false); - rows.addChild(nameField, 2, smallSettings).setTooltip(createTooltip("targetName")); + rows.addChild(nameField, 2, smallSettings).setTooltip(WidgetFactory.tooltip("targetName")); grid.arrangeElements(); FrameLayout.alignInRectangle(grid, x, y + 2, x + (xSize - middleWidth) / 2 - 4, y + ySize, 0, 0); grid.visitWidgets(this::addRenderableWidget); @@ -249,39 +188,7 @@ private void initRightWidgets() { // Return category pages switch (toggleCategoryButton.getValue()){ case CONFIGS, PREVIEW -> pages = configsPage.initRightWidgets(uiFactory); - case DISABLE -> { - int widgetsPerPage = 7; - int size = disableConfigs.size(); - rows.addChild(disabledNameField, 3, smallSettings).setTooltip(createTooltip("disabledName")); - rows.addChild(new SimpleIconButton(64, 0, button -> { - if (syncDisableDraft(button, false)) { - button.setTooltip(createTooltip("saveAs")); - initWidgets(page); - } - }), smallSettings).setTooltip(createTooltip("saveAs")); - for (int i = page * widgetsPerPage; i < Math.min((page + 1) * widgetsPerPage, size); i++) { - DisableConfig config = disableConfigs.get(i); - String targetName = nameField.getValue(); - Set hiddenNames = hiddenNameMap.computeIfAbsent(targetName, _ -> new HashSet<>()); - addRenderableWidget(new CycleIconButton(32, 16, hiddenNames.contains(config.name()) ? 1 : 0, 2)) - .setOnValueChange(value -> { - if (value == 0) hiddenNames.remove(config.name()); - else hiddenNames.add(config.name()); - }) - .setPosition(x + (xSize + middleWidth) / 2 - 20, y + 5 + (widgetHeight + 2) * (2 + i % widgetsPerPage)); - rows.addChild(createButton(LocUtil.literal(config.name()), compactWidgetWidth, _ -> { - loadDisableConfig(config); - initWidgets(page); - }), 3).setTooltip(Tooltip.create(LocUtil.literal(config.name()))); - rows.addChild(new SimpleIconButton(48, 0, _ -> { - disableConfigs.removeIf(disableConfig -> disableConfig.name().equals(config.name())); - hiddenNameMap.values().forEach(names -> names.remove(config.name())); - if (disabledNameField.getValue().equals(config.name())) clearDisableDraft(); - initWidgets(page * widgetsPerPage > size - 2 && size > 1 ? page - 1 : page); - }), smallSettings); - } - pages = (size - 1) / widgetsPerPage + 1; - } + case DISABLE -> pages = disablePage.initRightWidgets(uiFactory); default -> throw new IllegalStateException("Unexpected value: " + toggleCategoryButton.getValue()); } grid.arrangeElements(); @@ -293,23 +200,10 @@ private void initRightWidgets() { addRenderableWidget(new SimpleIconButton(x + xSize - 21, y + ySize - 20, 16, 16, 32, 0, _ -> initWidgets((page + 1) % pages))); } - public void deleteFocusedRectWidget() { - if (focusedRectWidget == null) return; - rectWidgets.remove(focusedRectWidget); - removeWidget(focusedRectWidget); - uMinField.setNumber(0F); - vMinField.setNumber(0F); - uMaxField.setNumber(0F); - vMaxField.setNumber(0F); - focusedRectWidgetNumberField.setNumber(0); - focusedRectWidgetNumberField.setMax(rectWidgets.size()); - rectWidgetsSizeWidget.setMessage(LocUtil.literal(String.valueOf(rectWidgets.size()))); - } - @Override public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { super.extractRenderState(graphics, mouseX, mouseY, partialTicks); - if (toggleCategoryButton.getValue() == Category.DISABLE && selectionModeButton.getValue() == 2 && inModelViewArea(mouseX, mouseY)) { + if (toggleCategoryButton.getValue() == Category.DISABLE && disablePage.getSelectionMode() == 2 && inModelViewArea(mouseX, mouseY)) { GUIHelper.enableScissor(graphics, modelViewArea); GUIHelper.fill(graphics, mouseX - selectionRadius, mouseY - selectionRadius, mouseX + selectionRadius, mouseY + selectionRadius, 400, SELECTION_COLOR); graphics.disableScissor(); @@ -318,9 +212,9 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse GUIHelper.enableScissor(graphics, textureViewArea); if (clickedX >= 0 && clickedY >= 0) graphics.fill(Math.min((int) clickedX, mouseX), Math.min((int) clickedY, mouseY), Math.max((int) clickedX, mouseX), Math.max((int) clickedY, mouseY), SELECTION_COLOR); - if (disableModeButton.getValue() == 0) - new UVRectangleWidget(0f, 0f, 1f, 1f).setViewport(getNewTextureViewport()) - .extractWidgetRenderState(graphics, mouseX, mouseY, partialTicks); + if (disablePage.getDisableMode()) { + disableAllWidget.setViewport(getNewTextureViewport()).extractWidgetRenderState(graphics, mouseX, mouseY, partialTicks); + } graphics.disableScissor(); } } @@ -334,8 +228,8 @@ public void extractBackground(@NonNull GuiGraphicsExtractor graphics, int mouseX ModelAnalyser analyser = new ModelAnalyser(); BindTarget target = genBindTarget(); target.offsets().scale *= modelScale; - String textureId = toggleCategoryButton.getValue() == Category.DISABLE ? disabledIdField.getValue() : ""; - Set hiddenNames = hiddenNameMap.getOrDefault(nameField.getValue(), Set.of()); + String textureId = toggleCategoryButton.getValue() == Category.DISABLE ? disablePage.getDisabledTextureId() : ""; + Set hiddenNames = disablePage.getHiddenNames(nameField.getValue()); assert minecraft.player != null; List modelRecords = captureRotatedEntity(analyser, target, minecraft.player); List textureRecords = modelRecords.stream().filter(record -> record.containsTextureId(textureId)).toList(); @@ -378,14 +272,14 @@ private List captureRotatedEntity(ModelAnalyser analyser, Bind } private void computeFocusedPrimitives(ModelAnalyser analyser, List modelRecords, List textureRecords, int mouseX, int mouseY) { - if (toggleCategoryButton.getValue() == Category.DISABLE && selectionModeButton.getValue() == 2 && inModelViewArea(mouseX, mouseY)) + if (toggleCategoryButton.getValue() == Category.DISABLE && disablePage.getSelectionMode() == 2 && inModelViewArea(mouseX, mouseY)) analyser.computeFocusedOnModel(modelRecords, mouseX - selectionRadius, mouseY - selectionRadius, mouseX + selectionRadius, mouseY + selectionRadius); if (inTextureViewArea(mouseX, mouseY)) { Vec2 mouseUV = getNewTextureViewport().xyToUV(mouseX, mouseY); analyser.computeFocusedOnTexture(textureRecords, mouseUV.x, mouseUV.y); } if (inModelViewArea(mouseX, mouseY)) analyser.computeFocusedOnModel(modelRecords, mouseX, mouseY, layers); - if (toggleCategoryButton.getValue() == Category.CONFIGS || selectionModeButton.getValue() == 1) analyser.computeFocusedPolyhedron(); + if (toggleCategoryButton.getValue() == Category.CONFIGS || disablePage.getSelectionMode() == 1) analyser.computeFocusedPolyhedron(); focusedPolyhedron = analyser.getFocusedPolyhedron(); focusedTextureId = analyser.getFocusedTextureId(); } @@ -418,116 +312,6 @@ private void renderFlattenedModels(GuiGraphicsExtractor graphics, ModelAnalyser analyser.drawFocusedInTextureArea(graphics, texturePose); graphics.disableScissor(); } - - private void clearDisableDraft() { - disabledNameField.setValue(""); - disabledIdField.setValue(""); - focusedRectWidget = null; - rectWidgets.clear(); - focusedRectWidgetNumberField.setMax(0); - focusedRectWidgetNumberField.setNumber(0); - rectWidgetsSizeWidget.setMessage(LocUtil.literal("0")); - uMinField.setNumber(0f); - vMinField.setNumber(0f); - uMaxField.setNumber(0f); - vMaxField.setNumber(0f); - } - - private void loadDisableConfig(DisableConfig config) { - disabledNameField.setValue(config.name()); - disabledIdField.setValue(config.textureId()); - disableModeButton.setValue(config.disableAll() ? 0 : 1); - focusedRectWidget = null; - rectWidgets.clear(); - config.rectangles().forEach(r -> addRectWidget(WidgetFactory.rectWidget(r))); - focusedRectWidgetNumberField.setMax(rectWidgets.size()); - focusedRectWidgetNumberField.setNumber(0); - rectWidgetsSizeWidget.setMessage(LocUtil.literal(String.valueOf(rectWidgets.size()))); - } - - private boolean syncDisableDraft(Button button, boolean autoName) { - String name = disabledNameField.getValue(); - String textureId = disabledIdField.getValue(); - if (name.isBlank()) { - if (!autoName) { - button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("emptyName").withStyle(ChatFormatting.RED))); - return false; - } - if (!hasMeaningfulDisableDraft()) { - if (textureId.isBlank() && !rectWidgets.isEmpty()) { - button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("emptyTextureId").withStyle(ChatFormatting.RED))); - return false; - } - return true; - } - name = generateDisableConfigName(textureId); - disabledNameField.setValue(name); - } - if (textureId.isBlank()) { - button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("emptyTextureId").withStyle(ChatFormatting.RED))); - return false; - } - DisableConfig disableConfig = new DisableConfig(name, textureId, disableModeButton.getValue() == 0, rectWidgets.stream().map(UVRectangleWidget::toUVRectangle).toList()); - upsertDisableConfig(disableConfig); - return true; - } - - private boolean hasMeaningfulDisableDraft() { - return !disabledIdField.getValue().isBlank() && (disableModeButton.getValue() == 0 || !rectWidgets.isEmpty()); - } - - private void upsertDisableConfig(DisableConfig disableConfig) { - for (int i = 0; i < disableConfigs.size(); i++) { - if (disableConfigs.get(i).name().equals(disableConfig.name())) { - disableConfigs.set(i, disableConfig); - return; - } - } - disableConfigs.add(disableConfig); - } - - private String generateDisableConfigName(String textureId) { - String base = textureId.trim(); - int separatorIndex = Math.max(base.lastIndexOf('/'), base.lastIndexOf(':')); - if (separatorIndex >= 0 && separatorIndex < base.length() - 1) base = base.substring(separatorIndex + 1); - int extensionIndex = base.lastIndexOf('.'); - if (extensionIndex > 0) base = base.substring(0, extensionIndex); - base = sanitizeDisableConfigName(base); - if (base.length() > CONFIG_NAME_MAX_LENGTH) base = base.substring(0, CONFIG_NAME_MAX_LENGTH); - - String candidate = base; - for (int suffix = 2; disableConfigNameExists(candidate); suffix++) { - String suffixText = "_" + suffix; - int baseLength = Math.clamp(CONFIG_NAME_MAX_LENGTH - suffixText.length(), 1, base.length()); - candidate = base.substring(0, baseLength) + suffixText; - } - return candidate; - } - - private String sanitizeDisableConfigName(String name) { - StringBuilder builder = new StringBuilder(); - boolean lastWasSeparator = false; - for (int i = 0; i < name.length(); i++) { - char c = Character.toLowerCase(name.charAt(i)); - if (Character.isLetterOrDigit(c)) { - builder.append(c); - lastWasSeparator = false; - } else if ((c == '_' || c == '-') && !builder.isEmpty() && !lastWasSeparator) { - builder.append(c); - lastWasSeparator = true; - } else if (!builder.isEmpty() && !lastWasSeparator) { - builder.append('_'); - lastWasSeparator = true; - } - } - while (!builder.isEmpty() && (builder.charAt(builder.length() - 1) == '_' || builder.charAt(builder.length() - 1) == '-')) - builder.deleteCharAt(builder.length() - 1); - return builder.isEmpty() ? "disable" : builder.toString(); - } - - private boolean disableConfigNameExists(String name) { - return disableConfigs.stream().anyMatch(disableConfig -> disableConfig.name().equals(name)); - } private void loadBindTarget(BindTarget target) { if (target.isEmpty()) return; @@ -535,66 +319,16 @@ private void loadBindTarget(BindTarget target) { priorityField.setNumber(target.priority()); configsPage.loadBindTarget(target); previewPage.loadBindTarget(target); - - disableConfigs.clear(); - disableConfigs.addAll(target.disableConfigs()); - clearDisableDraft(); + disablePage.loadBindTarget(target); } private BindTarget genBindTarget() { - DisableConfig currentDisableConfig = new DisableConfig(disabledNameField.getValue(), disabledIdField.getValue(), disableModeButton.getValue() == 0, rectWidgets.stream().map(UVRectangleWidget::toUVRectangle).toList()); - List newDisableConfigs = new ArrayList<>(disableConfigs); - for (int i = 0; i < newDisableConfigs.size(); i++) { - if (newDisableConfigs.get(i).name().equals(currentDisableConfig.name())) - newDisableConfigs.set(i, currentDisableConfig); - } TargetConfig targetConfig = configsPage.genTargetConfig(); BindConfig bindConfig = previewPage.genBindConfig(); OffsetConfig offsets = previewPage.genOffsetConfig(); + List disableConfig = disablePage.genDisableConfigs(); return new BindTarget(nameField.getValue(), configsPage.getTextureId(), priorityField.getNumber(), previewPage.getDepth(), - targetConfig, bindConfig, offsets, newDisableConfigs); - } - - private UVRectangleWidget addRectWidget(UVRectangleWidget rectWidget) { - UVRectangleWidget foundRectWidget = rectWidgets.stream().filter(r -> r.contains(rectWidget)).findFirst().orElse(null); - if (foundRectWidget == null) { - rectWidgets.add(rectWidget); - focusedRectWidgetNumberField.setMax(rectWidgets.size()); - rectWidgetsSizeWidget.setMessage(LocUtil.literal(String.valueOf(rectWidgets.size()))); - rectWidget.setOnDelete(this::deleteFocusedRectWidget); - rectWidget.setOnFocusUpdate(() -> { - int idx = rectWidgets.indexOf(rectWidget); - focusedRectWidgetNumberField.setNumber(idx + 1); - uMinField.setNumber(rectWidget.uMin); - vMinField.setNumber(rectWidget.vMin); - uMaxField.setNumber(rectWidget.uMax); - vMaxField.setNumber(rectWidget.vMax); - }); - if (textureViewArea != null) - rectWidget.setViewport(getNewTextureViewport()); - return rectWidget; - } - return foundRectWidget; - } - - private Tooltip createTooltip(String key, Object... args) { - return WidgetFactory.tooltip(key, args); - } - - private Button createButton(Component message, int width, Button.OnPress onPress) { - return WidgetFactory.button(message, width, widgetHeight, onPress); - } - - private CycleButton.Builder createCyclingButtonBuilder(Map messages, T defaultValue) { - return WidgetFactory.cyclingButton(messages, defaultValue); - } - - private NumberField createFloatField(int width, float defaultValue) { - return WidgetFactory.floatField(font, width, widgetHeight, defaultValue); - } - - private EditBox createTextField(int width, int maxLength) { - return WidgetFactory.textField(font, width, widgetHeight, maxLength); + targetConfig, bindConfig, offsets, disableConfig); } private boolean inModelViewArea(double x, double y) { @@ -611,51 +345,7 @@ public boolean leftClickedWithModifier(double mouseX, double mouseY) { configsPage.leftClickedWithModifier(focusedTextureId, focusedPolyhedron); return true; } else if ((inModelViewArea(mouseX, mouseY) || inTextureViewArea(mouseX, mouseY)) && toggleCategoryButton.getValue() == Category.DISABLE) { - String disabledId = disabledIdField.getValue(); - if (disabledId.isBlank() || !focusedTextureId.contains(disabledId)) { - disabledIdField.setValue(focusedTextureId); - return true; - } - if (selectionModeButton.getValue() == 2) { - List rectWidgets = new ArrayList<>(); - for (VertexData[] primitive : focusedPolyhedron) { - float uMin = 1f, vMin = 1f, uMax = 0, vMax = 0; - for (VertexData vertex : primitive) { - if (vertex.u() < uMin) uMin = vertex.u(); - if (vertex.v() < vMin) vMin = vertex.v(); - if (vertex.u() > uMax) uMax = vertex.u(); - if (vertex.v() > vMax) vMax = vertex.v(); - } - rectWidgets.add(new UVRectangleWidget(uMin, vMin, uMax, vMax)); - } - boolean merged; - do { - merged = false; - for (int i = 0; i < rectWidgets.size(); i++) { - UVRectangleWidget rectWidget = rectWidgets.get(i); - for (int j = i + 1; j < rectWidgets.size(); ) { - if (rectWidget.mergeWith(rectWidgets.get(j))) { - rectWidgets.remove(j); - merged = true; - } else { - j++; - } - } - } - } while (merged); - rectWidgets.forEach(this::addRectWidget); - } else { - float uMin = 1f, vMin = 1f, uMax = 0, vMax = 0; - for (VertexData[] primitive : focusedPolyhedron) { - for (VertexData vertex : primitive) { - if (vertex.u() < uMin) uMin = vertex.u(); - if (vertex.v() < vMin) vMin = vertex.v(); - if (vertex.u() > uMax) uMax = vertex.u(); - if (vertex.v() > vMax) vMax = vertex.v(); - } - } - setFocused(addRectWidget(new UVRectangleWidget(uMin, vMin, uMax, vMax))); - } + disablePage.leftClickedWithModifier(focusedTextureId, focusedPolyhedron, this::addRenderableWidget, this::setFocused); return true; } return false; @@ -675,7 +365,7 @@ public boolean mouseClicked(@NonNull MouseButtonEvent event, boolean doubleClick TextureViewport viewport = getNewTextureViewport(); Vec2 minUV = viewport.xyToUV(xMin, yMin); Vec2 maxUV = viewport.xyToUV(xMax, yMax); - setFocused(addRectWidget(new UVRectangleWidget(minUV.x, minUV.y, maxUV.x, maxUV.y))); + setFocused(disablePage.addRectWidget(new UVRectangleWidget(minUV.x, minUV.y, maxUV.x, maxUV.y), this::addRenderableWidget)); return true; } clickedX = (int) event.x(); @@ -703,7 +393,7 @@ public boolean mouseDragged(@NonNull MouseButtonEvent event, double dx, double d if (inTextureViewArea(event.x(), event.y())) { textureX += dx / textureScale; textureY += dy / textureScale; - rectWidgets.forEach(r -> r.setViewport(getNewTextureViewport())); + disablePage.setRectWidgetsViewport(getNewTextureViewport()); return true; } } @@ -714,7 +404,7 @@ public boolean mouseDragged(@NonNull MouseButtonEvent event, double dx, double d public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { if (inModelViewArea(mouseX, mouseY)) { if (InputConstants.isKeyDown(minecraft.getWindow(), modifierKey.getValue())) { - if (toggleCategoryButton.getValue() == Category.DISABLE && selectionModeButton.getValue() == 2) + if (toggleCategoryButton.getValue() == Category.DISABLE && disablePage.getSelectionMode() == 2) selectionRadius = Mth.clamp(selectionRadius + (int) verticalAmount * 2, 2, 48); else layers = Math.max(0, layers + (int) verticalAmount); } else { @@ -723,7 +413,7 @@ public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmou return true; } else if (inTextureViewArea(mouseX, mouseY)) { textureScale = Mth.clamp(textureScale + (int) verticalAmount * textureScale / 16, MIN_SCALE, MAX_SCALE); - rectWidgets.forEach(r -> r.setViewport(getNewTextureViewport())); + disablePage.setRectWidgetsViewport(getNewTextureViewport()); } return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); } @@ -732,7 +422,7 @@ public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmou public boolean keyPressed(@NonNull KeyEvent event) { if (event.isSelection()) { GuiEventListener focused = getFocused(); - if (focused != null && !focused.isFocused()) setFocused(focusedRectWidget); + if (focused != null && !focused.isFocused()) setFocused(disablePage.getFocusedRectWidget()); } return super.keyPressed(event); } diff --git a/common/src/main/java/com/xtracr/realcamera/gui/page/DisablePage.java b/common/src/main/java/com/xtracr/realcamera/gui/page/DisablePage.java new file mode 100644 index 0000000..c61169e --- /dev/null +++ b/common/src/main/java/com/xtracr/realcamera/gui/page/DisablePage.java @@ -0,0 +1,383 @@ +package com.xtracr.realcamera.gui.page; + + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; +import com.mojang.blaze3d.platform.InputConstants; +import com.xtracr.realcamera.config.BindTarget; +import com.xtracr.realcamera.config.DisableConfig; +import com.xtracr.realcamera.gui.*; + +import com.xtracr.realcamera.gui.components.CycleIconButton; +import com.xtracr.realcamera.gui.components.NumberField; +import com.xtracr.realcamera.gui.components.SimpleIconButton; +import com.xtracr.realcamera.gui.components.UVRectangleWidget; +import com.xtracr.realcamera.gui.util.TextureViewport; +import com.xtracr.realcamera.gui.util.WidgetFactory; +import com.xtracr.realcamera.renderer.state.VertexData; +import com.xtracr.realcamera.util.LocUtil; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.*; +import net.minecraft.client.gui.layouts.GridLayout; +import net.minecraft.client.gui.layouts.LayoutSettings; +import net.minecraft.client.gui.layouts.LinearLayout; +import net.minecraft.network.chat.CommonComponents; +import org.jspecify.annotations.Nullable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.IntStream; + +public final class DisablePage { + private final CategoryHost host; + private final int widgetWidth, compactWidgetWidth, widgetHeight; + + private static final int DISABLE_NAME_MAX_LENGTH = 20; + + private final EditBox disabledName; + private final EditBox disabledTextureId; + + private final NumberField uMin; + private final NumberField uMax; + private final NumberField vMin; + private final NumberField vMax; + + private final CycleButton disableMode; + private final CycleButton selectionMode; + + private final List disableConfigs = new ArrayList<>(); + private final Map> hiddenNameMap = new HashMap<>(); + + @Nullable + private UVRectangleWidget focusedRectWidget; + private final List rectWidgets = new ArrayList<>(); + private final NumberField focusedRectIndex; + private final StringWidget rectWidgetsSize; + + public DisablePage(CategoryHost host) { + this.host = host; + Font font = host.font(); + LayoutConstants layout = host.layout(); + InputConstants.Key modifierKey = layout.modifierKey(); + widgetWidth = layout.widgetWidth(); + compactWidgetWidth = layout.compactWidgetWidth(); + widgetHeight = layout.widgetHeight(); + int wideWidgetWidth = layout.wideWidgetWidth(); + + disabledTextureId = WidgetFactory.textField(font, wideWidgetWidth, widgetHeight, 1024); + disabledName = WidgetFactory.textField(font, compactWidgetWidth, widgetHeight, DISABLE_NAME_MAX_LENGTH); + focusedRectIndex = NumberField.ofInt(font, widgetWidth - 2, widgetHeight - 2, 0, null).setMin(0); + rectWidgetsSize = new StringWidget(widgetWidth - 22, widgetHeight, CommonComponents.EMPTY, font); + + uMin = WidgetFactory.floatField(font, compactWidgetWidth - 6, widgetHeight, 0).setMin(-1f).setMax(2f); + uMax = WidgetFactory.floatField(font, compactWidgetWidth - 6, widgetHeight, 0).setMin(-1f).setMax(2f); + vMin = WidgetFactory.floatField(font, compactWidgetWidth - 6, widgetHeight, 0).setMin(-1f).setMax(2f); + vMax = WidgetFactory.floatField(font, compactWidgetWidth - 6, widgetHeight, 0).setMin(-1f).setMax(2f); + + disableMode = WidgetFactory.cyclingButton(ImmutableMap.of( + true, LocUtil.MODEL_VIEW_WIDGET("all").withStyle(ChatFormatting.GREEN), + false, LocUtil.MODEL_VIEW_WIDGET("part").withStyle(ChatFormatting.BLUE)), true) + .withTooltip(_ -> WidgetFactory.tooltip("disableMode", modifierKey.getDisplayName())) + .create(0, 0, wideWidgetWidth, widgetHeight, LocUtil.MODEL_VIEW_WIDGET("disableMode")); + selectionMode = WidgetFactory.cyclingButton(ImmutableSortedMap.of( + 0, LocUtil.MODEL_VIEW_WIDGET("single"), + 1, LocUtil.MODEL_VIEW_WIDGET("multiple"), + 2, LocUtil.MODEL_VIEW_WIDGET("range").withStyle(ChatFormatting.BLUE)), 0) + .withTooltip(_ -> WidgetFactory.tooltip("selectionMode", modifierKey.getDisplayName(), modifierKey.getDisplayName())) + .create(0, 0, wideWidgetWidth, widgetHeight, LocUtil.MODEL_VIEW_WIDGET("selectionMode")); + } + + public void initLeftWidgets(ModelViewScreen.UIFactory uiFactory) { + GridLayout grid = uiFactory.grid(); + GridLayout.RowHelper rows = uiFactory.rows(); + LayoutSettings smallSettings = uiFactory.smallSettings(); + LayoutSettings offsetXSettings = grid.newCellSettings().padding(-13, 3, 1, 1); + LinearLayout selectionLayout = LinearLayout.horizontal().spacing(2); + selectionLayout.defaultCellSetting().alignVertically(0); + + rows.addChild(disableMode, 2); + rows.addChild(disabledTextureId, 2, smallSettings).setTooltip(WidgetFactory.tooltip("textureId")); + rows.addChild(selectionMode, 2); + rows.addChild(focusedRectIndex.setMax(rectWidgets.size()), smallSettings).setOnValueChange(index -> { + if (index == 0) focusedRectWidget = null; + else if (index > 0 && index <= rectWidgets.size()) { + focusedRectWidget = rectWidgets.get(index - 1); + uMin.setNumber(focusedRectWidget.uMin); + vMin.setNumber(focusedRectWidget.vMin); + uMax.setNumber(focusedRectWidget.uMax); + vMax.setNumber(focusedRectWidget.vMax); + } + }).setTooltip(WidgetFactory.tooltip("focusedRectangleNumber")); + + selectionLayout.addChild(new StringWidget(6, widgetHeight, LocUtil.literal("/"), host.font())); + rectWidgetsSize.setMessage(LocUtil.literal(String.valueOf(rectWidgets.size()))); + selectionLayout.addChild(rectWidgetsSize); + selectionLayout.addChild(new SimpleIconButton(48, 0, _ -> deleteFocusedRect()), + s -> s.paddingLeft(widgetWidth - host.font().width("/" + rectWidgets.size()) - widgetHeight - 2)) + .setTooltip(WidgetFactory.tooltip("deleteSelectedRectangle")); + rows.addChild(selectionLayout); + // + rows.addChild(UVWidget("uMin:")); + rows.addChild(uMin, offsetXSettings).setOnValueChange(f -> { + if (focusedRectWidget != null) focusedRectWidget.uMin = f; + }); + rows.addChild(UVWidget("vMin:")); + rows.addChild(vMin, offsetXSettings).setOnValueChange(f -> { + if (focusedRectWidget != null) focusedRectWidget.vMin = f; + }); + rows.addChild(UVWidget("uMax:")); + rows.addChild(uMax, offsetXSettings).setOnValueChange(f -> { + if (focusedRectWidget != null) focusedRectWidget.uMax = f; + }); + rows.addChild(UVWidget("vMax:")); + rows.addChild(vMax, offsetXSettings).setOnValueChange(f -> { + if (focusedRectWidget != null) focusedRectWidget.vMax = f; + }); + } + + private StringWidget UVWidget(String message) { + return new StringWidget(26, widgetHeight, LocUtil.literal(message), host.font()); + } + + public int initRightWidgets(ModelViewScreen.UIFactory uiFactory) { + LayoutSettings smallSettings = uiFactory.smallSettings(); + GridLayout.RowHelper rows = uiFactory.rows(); + LayoutConstants layout = host.layout(); + int widgetsPerPage = 7; + int size = disableConfigs.size(); + int page = host.getPage(); + int x = layout.x(); + int y = layout.y(); + int xSize = layout.xSize(); + int middleWidth = layout.middleWidth(); + + rows.addChild(disabledName, 3, smallSettings).setTooltip(WidgetFactory.tooltip("disabledName")); + rows.addChild(new SimpleIconButton(64, 0, button -> { + if (syncDisableConfig(button, false)) { + button.setTooltip(WidgetFactory.tooltip("saveDisableConfig")); + host.hostInitWidgets(page); + } + }), smallSettings).setTooltip(WidgetFactory.tooltip("saveDisableConfig")); + for (int i = page * widgetsPerPage; i < Math.min((page + 1) * widgetsPerPage, size); i++) { + DisableConfig config = disableConfigs.get(i); + String targetName = host.getNameValue(); + Set hiddenNames = hiddenNameMap.computeIfAbsent(targetName, _ -> new HashSet<>()); + uiFactory.addRenderable().apply(new CycleIconButton(32, 16, hiddenNames.contains(config.name()) ? 1 : 0, 2) + .setOnValueChange(value -> { + if (value == 0) hiddenNames.remove(config.name()); + else hiddenNames.add(config.name()); + })) + .setPosition(x + (xSize + middleWidth) / 2 - 20, y + 5 + (widgetHeight + 2) * (2 + i % widgetsPerPage)); + + rows.addChild(WidgetFactory.button(LocUtil.literal(config.name()), compactWidgetWidth, widgetHeight, _ -> { + loadDisableConfig(config); + host.hostInitWidgets(page); + }), 3).setTooltip(Tooltip.create(LocUtil.literal(config.name()))); + rows.addChild(new SimpleIconButton(48, 0, _ -> { + disableConfigs.removeIf(disableConfig -> disableConfig.name().equals(config.name())); + hiddenNameMap.values().forEach(names -> names.remove(config.name())); + if (disabledName.getValue().equals(config.name())) clearDisableConfig(); + host.hostInitWidgets(page * widgetsPerPage > size - 2 && size > 1 ? page - 1 : page); + }), smallSettings); + } + return (size - 1) / widgetsPerPage + 1; + } + + public void loadBindTarget(BindTarget target) { + disableConfigs.clear(); + hiddenNameMap.clear(); + disableConfigs.addAll(target.disableConfigs()); + } + + public List genDisableConfigs() { + DisableConfig currentDisableConfig = new DisableConfig(disabledName.getValue(), disabledTextureId.getValue(), disableMode.getValue(), rectWidgets.stream().map(UVRectangleWidget::toUVRectangle).toList()); + List newDisableConfigs = new ArrayList<>(disableConfigs); + IntStream.range(0, newDisableConfigs.size()).filter( + i -> newDisableConfigs.get(i).name().equals(currentDisableConfig.name())) + .forEach(i -> newDisableConfigs.set(i, currentDisableConfig)); + return newDisableConfigs; + } + + // Private Methods + + private void clearDisableConfig() { + disabledName.setValue(""); + disabledTextureId.setValue(""); + focusedRectWidget = null; + rectWidgets.clear(); + focusedRectIndex.setMax(0); + focusedRectIndex.setNumber(0); + rectWidgetsSize.setMessage(LocUtil.literal("0")); + uMin.setNumber(0f); + vMin.setNumber(0f); + uMax.setNumber(0f); + vMax.setNumber(0f); + } + + private void loadDisableConfig(DisableConfig config) { + disabledName.setValue(config.name()); + disabledTextureId.setValue(config.textureId()); + disableMode.setValue(config.disableAll()); + focusedRectWidget = null; + rectWidgets.clear(); + config.rectangles().forEach(r -> rectWidgets.add(WidgetFactory.rectWidget(r))); + focusedRectIndex.setMax(rectWidgets.size()); + focusedRectIndex.setNumber(0); + rectWidgetsSize.setMessage(LocUtil.literal(String.valueOf(rectWidgets.size()))); + } + + private void deleteFocusedRect() { + if (focusedRectWidget == null) return; + rectWidgets.remove(focusedRectWidget); + host.screenRemoveWidget(focusedRectWidget); + uMin.setNumber(0F); + vMin.setNumber(0F); + uMax.setNumber(0F); + vMax.setNumber(0F); + focusedRectIndex.setNumber(0); + focusedRectIndex.setMax(rectWidgets.size()); + rectWidgetsSize.setMessage(LocUtil.literal(String.valueOf(rectWidgets.size()))); + } + + // Helper Methods + + public boolean syncDisableConfig(Button button, boolean autoName) { + String name = disabledName.getValue(); + String textureId = disabledTextureId.getValue(); + if (name.isBlank()) { + boolean isMeaningful = !textureId.isBlank() && (disableMode.getValue() || !rectWidgets.isEmpty()); + if (!isMeaningful) { + return true; + } + if (autoName) { + name = host.getNameValue(); + disabledName.setValue(name); + } else { + button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("emptyName").withStyle(ChatFormatting.RED))); + return false; + } + } + if (textureId.isBlank()) { + button.setTooltip(Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP("emptyTextureId").withStyle(ChatFormatting.RED))); + return false; + } + DisableConfig disableConfig = new DisableConfig(name, textureId, disableMode.getValue(), rectWidgets.stream().map(UVRectangleWidget::toUVRectangle).toList()); + for (int i = 0; i < disableConfigs.size(); i++) { + if (disableConfigs.get(i).name().equals(name)) { + disableConfigs.set(i, disableConfig); + return true; + } + } + disableConfigs.add(disableConfig); + return true; + } + + public UVRectangleWidget addRectWidget(UVRectangleWidget rectWidget, Consumer addRenderable) { + UVRectangleWidget foundRectWidget = rectWidgets.stream().filter(r -> r.contains(rectWidget)).findFirst().orElse(null); + if (foundRectWidget == null) { + rectWidgets.add(rectWidget); + focusedRectIndex.setMax(rectWidgets.size()); + rectWidgetsSize.setMessage(LocUtil.literal(String.valueOf(rectWidgets.size()))); + rectWidget.setOnDelete(this::deleteFocusedRect); + rectWidget.setOnFocusUpdate(() -> { + focusedRectWidget = rectWidget; + focusedRectIndex.setNumber(rectWidgets.indexOf(rectWidget) + 1); + uMin.setNumber(rectWidget.uMin); + vMin.setNumber(rectWidget.vMin); + uMax.setNumber(rectWidget.uMax); + vMax.setNumber(rectWidget.vMax); + }); + if (host.getTextureViewArea() != null) + rectWidget.setViewport(host.getNewTextureViewport()); + addRenderable.accept(rectWidget); + return rectWidget; + } + return foundRectWidget; + } + + public void leftClickedWithModifier(String focusedTextureId, VertexData[][] focusedPolyhedron, Consumer addRenderable, Consumer setFocused) { + String disabledId = disabledTextureId.getValue(); + if (disabledId.isBlank() || !focusedTextureId.contains(disabledId)) { + disabledTextureId.setValue(focusedTextureId); + return; + } + if (selectionMode.getValue() == 2) { + List newRectWidgets = new ArrayList<>(); + for (VertexData[] primitive : focusedPolyhedron) { + float uMin = 1f, vMin = 1f, uMax = 0, vMax = 0; + for (VertexData vertex : primitive) { + if (vertex.u() < uMin) uMin = vertex.u(); + if (vertex.v() < vMin) vMin = vertex.v(); + if (vertex.u() > uMax) uMax = vertex.u(); + if (vertex.v() > vMax) vMax = vertex.v(); + } + newRectWidgets.add(new UVRectangleWidget(uMin, vMin, uMax, vMax)); + } + boolean merged; + do { + merged = false; + for (int i = 0; i < newRectWidgets.size(); i++) { + UVRectangleWidget rectWidget = newRectWidgets.get(i); + for (int j = i + 1; j < newRectWidgets.size(); ) { + if (rectWidget.mergeWith(newRectWidgets.get(j))) { + newRectWidgets.remove(j); + merged = true; + } else { + j++; + } + } + } + } while (merged); + newRectWidgets.forEach(w -> addRectWidget(w, addRenderable)); + } else { + float uMin = 1f, vMin = 1f, uMax = 0, vMax = 0; + for (VertexData[] primitive : focusedPolyhedron) { + for (VertexData vertex : primitive) { + if (vertex.u() < uMin) uMin = vertex.u(); + if (vertex.v() < vMin) vMin = vertex.v(); + if (vertex.u() > uMax) uMax = vertex.u(); + if (vertex.v() > vMax) vMax = vertex.v(); + } + } + setFocused.accept(addRectWidget(new UVRectangleWidget(uMin, vMin, uMax, vMax), addRenderable)); + } + } + + public String getDisabledTextureId() { + return disabledTextureId.getValue(); + } + + public int getSelectionMode() { + return selectionMode.getValue(); + } + + public boolean getDisableMode() { + return disableMode.getValue(); + } + + @Nullable + public UVRectangleWidget getFocusedRectWidget() { + return focusedRectWidget; + } + + public Set getHiddenNames(String targetName) { + return hiddenNameMap.getOrDefault(targetName, Set.of()); + } + + public void renderableRectWidgets(Consumer addRenderable, TextureViewport view) { + rectWidgets.forEach(r -> { + r.setViewport(view); + addRenderable.accept(r); + }); + } + + public void setRectWidgetsViewport(TextureViewport view) { + rectWidgets.forEach(r -> r.setViewport(view)); + } + + public void setFocusedRectWidget(@Nullable UVRectangleWidget rectWidget) { + focusedRectWidget = rectWidget; + } +} \ No newline at end of file diff --git a/common/src/main/resources/assets/realcamera/lang/en_us.json b/common/src/main/resources/assets/realcamera/lang/en_us.json index 50fd10f..cfbc611 100644 --- a/common/src/main/resources/assets/realcamera/lang/en_us.json +++ b/common/src/main/resources/assets/realcamera/lang/en_us.json @@ -83,13 +83,13 @@ "screen.tooltip.xtracr_realcamera.modelView_export": "Export the current config to the clipboard", "screen.tooltip.xtracr_realcamera.modelView_exportFailed": "Export failed: \n%s", "screen.tooltip.xtracr_realcamera.modelView_exportSucceeded": "Export succeeded!", - "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "Focused Rectangle Number\n(0 means no selection)\nPress Space or Enter to locate the current focused rectangle (When typing, press twice to locate)", + "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "Focused Rectangle Number\n(0 means no selection)", "screen.tooltip.xtracr_realcamera.modelView_import": "Import the config from the clipboard", "screen.tooltip.xtracr_realcamera.modelView_importFailed": "Import failed: \n%s", "screen.tooltip.xtracr_realcamera.modelView_importSucceeded": "Import %s succeeded!", "screen.tooltip.xtracr_realcamera.modelView_preview": "In the preview mode, you can adjust the camera's offset and rotation", "screen.tooltip.xtracr_realcamera.modelView_priority": "Priority when binding, the larger the value the higher the priority, and the higher the sorting on the right side", - "screen.tooltip.xtracr_realcamera.modelView_saveAs": "Add/update this disable setting\nPress Save on the left to save the config", + "screen.tooltip.xtracr_realcamera.modelView_saveDisableConfig": "Add/update this disable setting\nPress Save on the left to save the config", "screen.tooltip.xtracr_realcamera.modelView_scale": "Scale, which controls the size of the offsets", "screen.tooltip.xtracr_realcamera.modelView_selecting": "%s+Left Click to get the UV coordinates at the mouse pointer\nThe three sets of UV coordinates below are, from top to bottom, the UV coordinates of the forward vector, the upward vector, and the binding point on the target plane\nNote: %s+Scroll to switch between the different layers of the model. Manually inputting UV coordinates allows you to bind more accurately to the target position", "screen.tooltip.xtracr_realcamera.modelView_selectionMode": "Decide the mode of %s+Left Click quick selection\n(%s+Scroll to change the selection range in the range mode)", diff --git a/common/src/main/resources/assets/realcamera/lang/zh_cn.json b/common/src/main/resources/assets/realcamera/lang/zh_cn.json index b5f22c3..2341b02 100644 --- a/common/src/main/resources/assets/realcamera/lang/zh_cn.json +++ b/common/src/main/resources/assets/realcamera/lang/zh_cn.json @@ -83,13 +83,13 @@ "screen.tooltip.xtracr_realcamera.modelView_export": "导出当前配置到剪贴板", "screen.tooltip.xtracr_realcamera.modelView_exportFailed": "导出失败:\n%s", "screen.tooltip.xtracr_realcamera.modelView_exportSucceeded": "导出成功!", - "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "选中的矩形编号\n(0表示不选中)\n可通过空格或回车键定位到当前选中的矩形(当正在输入时,按两次即可定位)", + "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "选中的矩形编号\n(0表示不选中)", "screen.tooltip.xtracr_realcamera.modelView_import": "从剪贴板导入配置", "screen.tooltip.xtracr_realcamera.modelView_importFailed": "导入失败:\n%s", "screen.tooltip.xtracr_realcamera.modelView_importSucceeded": "导入%s成功!", "screen.tooltip.xtracr_realcamera.modelView_preview": "在预览模式可以调整相机的各个偏移量", "screen.tooltip.xtracr_realcamera.modelView_priority": "绑定时的优先级,值越大优先级越高,并且在右侧排序越靠上", - "screen.tooltip.xtracr_realcamera.modelView_saveAs": "添加/更新此禁用设置\n再点左侧保存以保存到当前配置", + "screen.tooltip.xtracr_realcamera.modelView_saveDisableConfig": "添加/更新此禁用设置\n再点左侧保存以保存到当前配置", "screen.tooltip.xtracr_realcamera.modelView_scale": "缩放比例,控制偏移量的大小", "screen.tooltip.xtracr_realcamera.modelView_selecting": "%s+左键可获取鼠标指针处的UV坐标\n下方三组UV坐标由上到下依次为向前矢量、向上矢量和目标平面上绑定点的UV坐标\n注:%s键+滚轮可在模型的不同层间切换,手动输入UV坐标可以更加精确地绑定到目标位置", "screen.tooltip.xtracr_realcamera.modelView_selectionMode": "决定%s+左键快速选择的模式\n(%s键+滚轮可改变范围模式下的选择范围)",