From 554b21d1be55e9e88443249b3fc6dff019bdc1aa Mon Sep 17 00:00:00 2001 From: H31M5 <2211923909@qq.com> Date: Thu, 13 Aug 2026 20:58:49 +0800 Subject: [PATCH 1/2] First refactor GUI --- .../xtracr/realcamera/config/ConfigFile.java | 2 + .../com/xtracr/realcamera/gui/Category.java | 13 ++ .../realcamera/gui/LayoutConstants.java | 20 ++ .../realcamera/gui/ModelViewScreen.java | 181 +++++------------- .../gui/components/DoubleSlider.java | 2 +- .../gui/components/UVRectangleWidget.java | 113 +++++++++++ .../realcamera/gui/{ => util}/GUIHelper.java | 2 +- .../gui/{ => util}/ModelAnalyser.java | 2 +- .../realcamera/gui/util/TextureViewport.java | 25 +++ .../realcamera/gui/util/WidgetFactory.java | 51 +++++ .../gui/ColoredFloatLineRenderState.java | 2 +- .../gui/ColoredFloatQuadRenderState.java | 2 +- .../gui/ColoredFloatRectangleRenderState.java | 2 +- 13 files changed, 273 insertions(+), 144 deletions(-) create mode 100644 common/src/main/java/com/xtracr/realcamera/gui/Category.java create mode 100644 common/src/main/java/com/xtracr/realcamera/gui/LayoutConstants.java create mode 100644 common/src/main/java/com/xtracr/realcamera/gui/components/UVRectangleWidget.java rename common/src/main/java/com/xtracr/realcamera/gui/{ => util}/GUIHelper.java (99%) rename common/src/main/java/com/xtracr/realcamera/gui/{ => util}/ModelAnalyser.java (99%) create mode 100644 common/src/main/java/com/xtracr/realcamera/gui/util/TextureViewport.java create mode 100644 common/src/main/java/com/xtracr/realcamera/gui/util/WidgetFactory.java diff --git a/common/src/main/java/com/xtracr/realcamera/config/ConfigFile.java b/common/src/main/java/com/xtracr/realcamera/config/ConfigFile.java index 61c6fba5..5bcc284f 100644 --- a/common/src/main/java/com/xtracr/realcamera/config/ConfigFile.java +++ b/common/src/main/java/com/xtracr/realcamera/config/ConfigFile.java @@ -38,7 +38,9 @@ public static void load() { } public static void save() { + if (config == null) load(); try (BufferedWriter writer = Files.newBufferedWriter(path.get())) { + config.clamp(); GSON.toJson(config, writer); } catch (Exception exception) { RealCamera.LOGGER.warn("Failed to save " + FILE_NAME, exception); diff --git a/common/src/main/java/com/xtracr/realcamera/gui/Category.java b/common/src/main/java/com/xtracr/realcamera/gui/Category.java new file mode 100644 index 00000000..8f439217 --- /dev/null +++ b/common/src/main/java/com/xtracr/realcamera/gui/Category.java @@ -0,0 +1,13 @@ +package com.xtracr.realcamera.gui; + +public enum Category { + CONFIGS, + PREVIEW, + DISABLE; + + public final String id = name().toLowerCase(); + + Category next() { + return values()[(ordinal() + 1) % values().length]; + } +} \ No newline at end of file diff --git a/common/src/main/java/com/xtracr/realcamera/gui/LayoutConstants.java b/common/src/main/java/com/xtracr/realcamera/gui/LayoutConstants.java new file mode 100644 index 00000000..206d1062 --- /dev/null +++ b/common/src/main/java/com/xtracr/realcamera/gui/LayoutConstants.java @@ -0,0 +1,20 @@ +package com.xtracr.realcamera.gui; + +import com.mojang.blaze3d.platform.InputConstants; +import com.xtracr.realcamera.config.ConfigFile; + +public record LayoutConstants(int xSize, int ySize, int x, int y, int middleWidth, int widgetWidth, int widgetHeight, int wideWidgetWidth, int compactWidgetWidth, InputConstants.Key modifierKey) { + public static LayoutConstants create(int width, int height) { + int xSize = Math.clamp(width - 10, 10, 450); + int ySize = Math.clamp(height - 10, 10, 206); + int middleWidth = Math.max(10, xSize - 200); + int x = (width - xSize) / 2; + int y = (height - ySize) / 2; + int widgetWidth = 42; + int widgetHeight = 18; + int wideWidgetWidth = widgetWidth * 2 + 4; + int compactWidgetWidth = widgetWidth * 2 - 18; + InputConstants.Key modifierKey = InputConstants.getKey(ConfigFile.config().binding.screenModifierKey); + return new LayoutConstants(xSize, ySize, x, y, middleWidth, widgetWidth, widgetHeight, wideWidgetWidth, compactWidgetWidth, modifierKey); + } +} 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 eeaeba5f..3492ad38 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java @@ -12,10 +12,10 @@ 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.util.*; import com.xtracr.realcamera.renderer.state.BuiltModelRecord; import com.xtracr.realcamera.renderer.state.VertexData; import com.xtracr.realcamera.util.LocUtil; -import com.xtracr.realcamera.util.MathUtil; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.*; @@ -23,7 +23,6 @@ import net.minecraft.client.gui.layouts.FrameLayout; import net.minecraft.client.gui.layouts.GridLayout; import net.minecraft.client.gui.layouts.LayoutSettings; -import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.input.KeyEvent; @@ -41,14 +40,16 @@ import java.util.*; -public final class ModelViewScreen extends Screen { - private static final int SELECTION_COLOR = 0x4F3333CC, SELECTION_HOVER_COLOR = 0x2F3333CC, OUTLINE_COLOR = 0xAAFFFFFF; +public final class ModelViewScreen extends Screen{ + 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; private static final int CONFIG_NAME_MAX_LENGTH = 20; - private final int widgetWidth = 42, widgetHeight = 18, wideWidgetWidth = widgetWidth * 2 + 4, compactWidgetWidth = widgetWidth * 2 - 18; + private LayoutConstants layoutConstants = LayoutConstants.create(0, 0); + private final int widgetWidth = layoutConstants.widgetWidth(), widgetHeight = layoutConstants.widgetHeight(), + wideWidgetWidth = layoutConstants.wideWidgetWidth(), compactWidgetWidth = layoutConstants.compactWidgetWidth(); private int x, y, xSize, ySize, middleWidth, page = 0; - private InputConstants.Key modifierKey = InputConstants.getKey(ConfigFile.config().binding.screenModifierKey); + 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; @@ -138,12 +139,13 @@ public ModelViewScreen() { @Override protected void init() { super.init(); - xSize = Math.clamp(width - 10, 10, 450); - ySize = Math.clamp(height - 10, 10, 206); - middleWidth = Math.max(10, xSize - 200); - x = (width - xSize) / 2; - y = (height - ySize) / 2; - modifierKey = InputConstants.getKey(ConfigFile.config().binding.screenModifierKey); + layoutConstants = LayoutConstants.create(width, height); + xSize = layoutConstants.xSize(); + ySize = layoutConstants.ySize(); + middleWidth = layoutConstants.middleWidth(); + x = layoutConstants.x(); + y = layoutConstants.y(); + modifierKey = layoutConstants.modifierKey(); initWidgets(page); if (!initialized) loadBindTarget(RealCameraCore.currentTarget()); initialized = true; @@ -396,6 +398,7 @@ public void extractBackground(@NonNull GuiGraphicsExtractor graphics, int mouseX target.offsets().scale *= modelScale; String textureId = toggleCategoryButton.getValue() == Category.DISABLE ? disabledIdField.getValue() : ""; Set hiddenNames = hiddenNameMap.getOrDefault(nameField.getValue(), Set.of()); + assert minecraft.player != null; List modelRecords = captureRotatedEntity(analyser, target, minecraft.player); List textureRecords = modelRecords.stream().filter(record -> record.containsTextureId(textureId)).toList(); ModelAnalyser.applyDisableConfigs(modelRecords, target, textureId, hiddenNames); @@ -440,7 +443,8 @@ private void computeFocusedPrimitives(ModelAnalyser analyser, List { + 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 Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP(key, args)); + return WidgetFactory.tooltip(key, args); } private UVRectangleWidget createRectWidget(UVRectangle rect) { - return new UVRectangleWidget(rect.uMin(), rect.vMin(), rect.uMax(), rect.vMax()); + return WidgetFactory.rectWidget(rect, null); } private Button createButton(Component message, int width, Button.OnPress onPress) { - return Button.builder(message, onPress).size(width, widgetHeight).build(); + return WidgetFactory.button(message, width, widgetHeight, onPress); } private CycleButton.Builder createCyclingButtonBuilder(Map messages, T defaultValue) { - return new CycleButton.Builder<>(messages::get, () -> defaultValue).withValues(messages.keySet()); + return WidgetFactory.cyclingButton(messages, defaultValue); } private DoubleSlider createSlider(String key, int width, double min, double max) { - return new DoubleSlider(width, widgetHeight, 0, min, max, d -> LocUtil.MODEL_VIEW_WIDGET(key, MathUtil.round(d, 2))); + return WidgetFactory.slider(key, width, widgetHeight, min, max); } private NumberField createFloatField(int width, float defaultValue) { - return NumberField.ofFloat(font, width - 2, widgetHeight - 2, defaultValue, null).setMax(1.0f).setMin(0f); + return WidgetFactory.floatField(font, width, widgetHeight, defaultValue); } private EditBox createTextField(int width, int maxLength) { - EditBox editBox = new EditBox(font, 0, 0, width - 2, widgetHeight - 2, CommonComponents.EMPTY); - editBox.setMaxLength(maxLength); - return editBox; + return WidgetFactory.textField(font, width, widgetHeight, maxLength); } private boolean inModelViewArea(double x, double y) { @@ -816,10 +813,14 @@ public boolean mouseClicked(@NonNull MouseButtonEvent event, boolean doubleClick if (InputConstants.isKeyDown(minecraft.getWindow(), modifierKey.getValue())) { if (leftClickedWithModifier(event.x(), event.y())) return true; } else if (inTextureViewArea(event.x(), event.y())) { + assert getNewTextureViewport() != null; if (super.mouseClicked(event, doubleClick)) return true; if (storedX >= 0 && storedY >= 0) { float xMin = (float) Math.min(storedX, event.x()), yMin = (float) Math.min(storedY, event.y()), xMax = (float) Math.max(storedX, event.x()), yMax = (float) Math.max(storedY, event.y()); - setFocused(addRectWidget(new UVRectangleWidget(xMin, yMin, xMax, yMax, textureViewArea))); + 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))); return true; } clickedX = (int) event.x(); @@ -884,104 +885,8 @@ public boolean isPauseScreen() { return pauseButton.getValue() == 1; } - private enum Category { - CONFIGS, - PREVIEW, - DISABLE; - - public final String id = name().toLowerCase(); - - public Category next() { - return values()[(ordinal() + 1) % values().length]; - } - } - - private final class UVRectangleWidget extends AbstractWidget { - private float uMin, vMin, uMax, vMax; - - public UVRectangleWidget(float uMin, float vMin, float uMax, float vMax) { - super(0, 0, 16, 16, CommonComponents.EMPTY); - this.uMin = uMin; - this.vMin = vMin; - this.uMax = uMax; - this.vMax = vMax; - } - - public UVRectangleWidget(float xMin, float yMin, float xMax, float yMax, @NonNull ScreenRectangle screenArea) { - super((int) xMin, (int) yMin, (int) (xMax - xMin), (int) (yMax - yMin), CommonComponents.EMPTY); - Vec2 minUV = translateXYToUV(xMin, yMin, screenArea), maxUV = translateXYToUV(xMax, yMax, screenArea); - this.uMin = minUV.x; - this.vMin = minUV.y; - this.uMax = maxUV.x; - this.vMax = maxUV.y; - } - - public UVRectangle toUVRectangle() { - return new UVRectangle(uMin, vMin, uMax, vMax); - } - - public boolean contains(UVRectangleWidget other) { - return uMin <= other.uMin && vMin <= other.vMin && uMax >= other.uMax && vMax >= other.vMax; - } - - public boolean mergeWith(UVRectangleWidget other) { - if (uMin == other.uMin && uMax == other.uMax && vMin <= other.vMax && vMax >= other.vMin) { - vMin = Math.min(vMin, other.vMin); - vMax = Math.max(vMax, other.vMax); - return true; - } - if (vMin == other.vMin && vMax == other.vMax && uMin <= other.uMax && uMax >= other.uMin) { - uMin = Math.min(uMin, other.uMin); - uMax = Math.max(uMax, other.uMax); - return true; - } - return false; - } - - @Override - protected void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { - Vec2 minXY = translateUVToXY(uMin, vMin, textureViewArea), maxXY = translateUVToXY(uMax, vMax, textureViewArea); - float x1 = minXY.x, y1 = minXY.y, x2 = maxXY.x, y2 = maxXY.y, width = x2 - x1, height = y2 - y1; - setX((int) x1); - setY((int) y1); - setWidth((int) width); - setHeight((int) height); - GUIHelper.enableScissor(graphics, textureViewArea); - GUIHelper.fill(graphics, x1, y1, x2, y2, SELECTION_COLOR); - if (isHoveredOrFocused()) GUIHelper.fill(graphics, x1, y1, x2, y2, SELECTION_HOVER_COLOR); - if (isFocused() || this == focusedRectWidget) - GUIHelper.outline(graphics, x1, y1, width, height, OUTLINE_COLOR); - graphics.disableScissor(); - } - - @Override - public boolean keyPressed(@NonNull KeyEvent event) { - if (textureViewArea == null) return false; - if (event.input() == InputConstants.KEY_DELETE && deleteFocusedRectWidget()) return true; - if (event.isSelection() && uMin < uMax && vMin < vMax) { - textureX = 0.5 * (1 - uMin - uMax); - textureY = 0.5 * (1 - vMin - vMax); - textureScale = (int) Mth.clamp(32d * DEFAULT_SCALE / (textureViewArea.width() * Math.sqrt((uMax - uMin) * (vMax - vMin))), MIN_SCALE, MAX_SCALE); - return true; - } - return super.keyPressed(event); - } - - @Override - public void setFocused(boolean focused) { - super.setFocused(focused); - if (focused) { - focusedRectWidget = this; - focusedRectWidgetNumberField.setNumber(rectWidgets.indexOf(this) + 1); - uMinField.setNumber(uMin); - vMinField.setNumber(vMin); - uMaxField.setNumber(uMax); - vMaxField.setNumber(vMax); - } - } - - @Override - protected void updateWidgetNarration(@NonNull NarrationElementOutput narrationElementOutput) { - } + public TextureViewport getNewTextureViewport() { + if (textureViewArea == null) return null; + return new TextureViewport(textureViewArea, textureScale, (float) textureX, (float) textureY); } } diff --git a/common/src/main/java/com/xtracr/realcamera/gui/components/DoubleSlider.java b/common/src/main/java/com/xtracr/realcamera/gui/components/DoubleSlider.java index 52862444..a673374e 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/components/DoubleSlider.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/components/DoubleSlider.java @@ -11,7 +11,7 @@ public class DoubleSlider extends AbstractSliderButton { private final double min, max; public DoubleSlider(int width, int height, double value, double min, double max, Function textFactory) { - super(0, 0, width, height, textFactory.apply(value), Mth.clamp(0, (value - min) / (max - min), 1)); + super(0, 0, width, height, textFactory.apply(value), Mth.clamp((value - min) / (max - min), 0, 1)); this.textFactory = textFactory; this.min = min; this.max = max; diff --git a/common/src/main/java/com/xtracr/realcamera/gui/components/UVRectangleWidget.java b/common/src/main/java/com/xtracr/realcamera/gui/components/UVRectangleWidget.java new file mode 100644 index 00000000..83e15a9c --- /dev/null +++ b/common/src/main/java/com/xtracr/realcamera/gui/components/UVRectangleWidget.java @@ -0,0 +1,113 @@ +package com.xtracr.realcamera.gui.components; + +import com.mojang.blaze3d.platform.InputConstants; +import com.xtracr.realcamera.config.UVRectangle; +import com.xtracr.realcamera.gui.util.GUIHelper; +import com.xtracr.realcamera.gui.util.TextureViewport; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.world.phys.Vec2; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public class UVRectangleWidget extends AbstractWidget { + private static final int SELECTION_COLOR = 0x4F3333CC, SELECTION_HOVER_COLOR = 0x2F3333CC, OUTLINE_COLOR = 0xAAFFFFFF; + + public float uMin, vMin, uMax, vMax; + @Nullable + private TextureViewport viewport; + private Runnable onDelete = () -> {}; + private Runnable onFocusUpdate = () -> {}; + + public UVRectangleWidget(float uMin, float vMin, float uMax, float vMax) { + super(0, 0, 16, 16, CommonComponents.EMPTY); + this.uMin = uMin; + this.vMin = vMin; + this.uMax = uMax; + this.vMax = vMax; + } + + public UVRectangleWidget(float uMin, float vMin, float uMax, float vMax, @Nullable TextureViewport viewport) { + super(0, 0, 16, 16, CommonComponents.EMPTY); + this.uMin = uMin; + this.vMin = vMin; + this.uMax = uMax; + this.vMax = vMax; + this.viewport = viewport; + } + + public void setOnDelete(@NonNull Runnable onDelete) { + this.onDelete = onDelete; + } + + public void setOnFocusUpdate(@NonNull Runnable onFocusUpdate) { + this.onFocusUpdate = onFocusUpdate; + } + + public void setViewport(@Nullable TextureViewport viewport) { + this.viewport = viewport; + } + + public UVRectangle toUVRectangle() { + return new UVRectangle(uMin, vMin, uMax, vMax); + } + + public boolean contains(UVRectangleWidget other) { + return uMin <= other.uMin && vMin <= other.vMin && uMax >= other.uMax && vMax >= other.vMax; + } + + public boolean mergeWith(UVRectangleWidget other) { + if (uMin == other.uMin && uMax == other.uMax && vMin <= other.vMax && vMax >= other.vMin) { + vMin = Math.min(vMin, other.vMin); + vMax = Math.max(vMax, other.vMax); + return true; + } + if (vMin == other.vMin && vMax == other.vMax && uMin <= other.uMax && uMax >= other.uMin) { + uMin = Math.min(uMin, other.uMin); + uMax = Math.max(uMax, other.uMax); + return true; + } + return false; + } + + @Override + public void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + if (viewport == null) return; + Vec2 minXY = viewport.uvToXY(uMin, vMin), maxXY = viewport.uvToXY(uMax, vMax); + float x1 = minXY.x, y1 = minXY.y, x2 = maxXY.x, y2 = maxXY.y, width = x2 - x1, height = y2 - y1; + setX((int) x1); + setY((int) y1); + setWidth((int) width); + setHeight((int) height); + GUIHelper.enableScissor(graphics, viewport.area()); + GUIHelper.fill(graphics, x1, y1, x2, y2, SELECTION_COLOR); + if (isHoveredOrFocused()) GUIHelper.fill(graphics, x1, y1, x2, y2, SELECTION_HOVER_COLOR); + if (isFocused()) GUIHelper.outline(graphics, x1, y1, width, height, OUTLINE_COLOR); + graphics.disableScissor(); + } + + @Override + public boolean keyPressed(@NonNull KeyEvent event) { + if (event.input() == InputConstants.KEY_DELETE) { + onDelete.run(); + return true; + } + if (event.isSelection() && uMin < uMax && vMin < vMax) { + return true; + } + return super.keyPressed(event); + } + + @Override + public void setFocused(boolean focused) { + super.setFocused(focused); + if (focused) onFocusUpdate.run(); + } + + @Override + protected void updateWidgetNarration(@NonNull NarrationElementOutput narrationElementOutput) { + } +} \ No newline at end of file diff --git a/common/src/main/java/com/xtracr/realcamera/gui/GUIHelper.java b/common/src/main/java/com/xtracr/realcamera/gui/util/GUIHelper.java similarity index 99% rename from common/src/main/java/com/xtracr/realcamera/gui/GUIHelper.java rename to common/src/main/java/com/xtracr/realcamera/gui/util/GUIHelper.java index 98cde370..62eb47cf 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/GUIHelper.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/util/GUIHelper.java @@ -1,4 +1,4 @@ -package com.xtracr.realcamera.gui; +package com.xtracr.realcamera.gui.util; import com.xtracr.realcamera.mixin.accessor.GuiGraphicsExtractorAccessor; import com.xtracr.realcamera.renderer.state.BuiltModelRecord; diff --git a/common/src/main/java/com/xtracr/realcamera/gui/ModelAnalyser.java b/common/src/main/java/com/xtracr/realcamera/gui/util/ModelAnalyser.java similarity index 99% rename from common/src/main/java/com/xtracr/realcamera/gui/ModelAnalyser.java rename to common/src/main/java/com/xtracr/realcamera/gui/util/ModelAnalyser.java index f963e14d..8fdcd45b 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/ModelAnalyser.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/util/ModelAnalyser.java @@ -1,4 +1,4 @@ -package com.xtracr.realcamera.gui; +package com.xtracr.realcamera.gui.util; import com.google.common.collect.ImmutableSet; import com.mojang.blaze3d.platform.Lighting; diff --git a/common/src/main/java/com/xtracr/realcamera/gui/util/TextureViewport.java b/common/src/main/java/com/xtracr/realcamera/gui/util/TextureViewport.java new file mode 100644 index 00000000..d6f91487 --- /dev/null +++ b/common/src/main/java/com/xtracr/realcamera/gui/util/TextureViewport.java @@ -0,0 +1,25 @@ +package com.xtracr.realcamera.gui.util; + +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.world.phys.Vec2; +import org.jspecify.annotations.NonNull; + +public record TextureViewport(@NonNull ScreenRectangle area, int textureScale, float textureX, float textureY) { + private static final int DEFAULT_SCALE = 80; + + public Vec2 uvToXY(float u, float v) { + int left = area.left(), width = area.width(), top = area.top(), height = area.height(); + float scale = (textureScale * width) / (float) DEFAULT_SCALE; + float x = (u + textureX - 0.5f) * scale + left + width / 2.0f; + float y = (v + textureY - 0.5f) * scale + top + height / 2.0f; + return new Vec2(x, y); + } + + public Vec2 xyToUV(float x, float y) { + int left = area.left(), width = area.width(), top = area.top(), height = area.height(); + float invScale = (float) DEFAULT_SCALE / (textureScale * width); + float u = (x - left - width / 2.0f) * invScale - textureX + 0.5f; + float v = (y - top - height / 2.0f) * invScale - textureY + 0.5f; + return new Vec2(u, v); + } +} diff --git a/common/src/main/java/com/xtracr/realcamera/gui/util/WidgetFactory.java b/common/src/main/java/com/xtracr/realcamera/gui/util/WidgetFactory.java new file mode 100644 index 00000000..f72c3df7 --- /dev/null +++ b/common/src/main/java/com/xtracr/realcamera/gui/util/WidgetFactory.java @@ -0,0 +1,51 @@ +package com.xtracr.realcamera.gui.util; + +import com.xtracr.realcamera.config.UVRectangle; +import com.xtracr.realcamera.gui.components.DoubleSlider; +import com.xtracr.realcamera.gui.components.NumberField; +import com.xtracr.realcamera.gui.components.UVRectangleWidget; +import com.xtracr.realcamera.util.LocUtil; +import com.xtracr.realcamera.util.MathUtil; +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.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; + +import java.util.Map; + +public final class WidgetFactory { + + public static Tooltip tooltip(String key, Object... args) { + return Tooltip.create(LocUtil.MODEL_VIEW_TOOLTIP(key, args)); + } + + public static Button button(Component message, int width, int height, Button.OnPress onPress) { + return Button.builder(message, onPress).size(width, height).build(); + } + + public static UVRectangleWidget rectWidget(UVRectangle rect, TextureViewport view) { + return new UVRectangleWidget(rect.uMin(), rect.vMin(), rect.uMax(), rect.vMax(), view); + } + + public static CycleButton.Builder cyclingButton(Map messages, T defaultValue) { + return new CycleButton.Builder<>(messages::get, () -> defaultValue).withValues(messages.keySet()); + } + + public static DoubleSlider slider(String key, int width, int height, double min, double max) { + return new DoubleSlider(width, height, 0, min, max, d -> LocUtil.MODEL_VIEW_WIDGET(key, MathUtil.round(d, 2))); + } + + public static NumberField floatField(Font font, int width, int height, float defaultValue) { + return NumberField.ofFloat(font, width - 2, height - 2, defaultValue, null).setMax(1.0f).setMin(0f); + } + + public static EditBox textField(Font font, int width, int height, int maxLength) { + EditBox editBox = new EditBox(font, 0, 0, width - 2, height - 2, CommonComponents.EMPTY); + editBox.setMaxLength(maxLength); + return editBox; + } +} + diff --git a/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatLineRenderState.java b/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatLineRenderState.java index 14423344..dd6b5acd 100644 --- a/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatLineRenderState.java +++ b/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatLineRenderState.java @@ -2,7 +2,7 @@ import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.vertex.VertexConsumer; -import com.xtracr.realcamera.gui.GUIHelper; +import com.xtracr.realcamera.gui.util.GUIHelper; import com.xtracr.realcamera.util.MathUtil; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.gui.render.TextureSetup; diff --git a/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatQuadRenderState.java b/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatQuadRenderState.java index ed4382ed..7ea7e618 100644 --- a/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatQuadRenderState.java +++ b/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatQuadRenderState.java @@ -2,7 +2,7 @@ import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.vertex.VertexConsumer; -import com.xtracr.realcamera.gui.GUIHelper; +import com.xtracr.realcamera.gui.util.GUIHelper; import com.xtracr.realcamera.util.MathUtil; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.gui.render.TextureSetup; diff --git a/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatRectangleRenderState.java b/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatRectangleRenderState.java index 4ef2c7d7..978a6617 100644 --- a/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatRectangleRenderState.java +++ b/common/src/main/java/com/xtracr/realcamera/renderer/state/gui/ColoredFloatRectangleRenderState.java @@ -2,7 +2,7 @@ import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.vertex.VertexConsumer; -import com.xtracr.realcamera.gui.GUIHelper; +import com.xtracr.realcamera.gui.util.GUIHelper; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.gui.render.TextureSetup; import net.minecraft.client.renderer.state.gui.GuiElementRenderState; From 26409f9fb1aa3307617f4dedc49f2f0b232ed770 Mon Sep 17 00:00:00 2001 From: H31M5 <2211923909@qq.com> Date: Fri, 14 Aug 2026 18:00:47 +0800 Subject: [PATCH 2/2] Refactor: Remove unnecessary @NonNull annotations --- .../realcamera/gui/ModelViewScreen.java | 27 +++++++++---------- .../gui/components/UVRectangleWidget.java | 14 +++------- .../realcamera/gui/util/TextureViewport.java | 3 +-- .../realcamera/gui/util/WidgetFactory.java | 4 +-- 4 files changed, 19 insertions(+), 29 deletions(-) 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 3492ad38..7937ffaa 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/ModelViewScreen.java @@ -40,7 +40,7 @@ import java.util.*; -public final class ModelViewScreen extends Screen{ +public final class ModelViewScreen extends Screen { 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; @@ -358,14 +358,17 @@ private void initRightWidgets() { addRenderableWidget(new SimpleIconButton(x + xSize - 21, y + ySize - 20, 16, 16, 32, 0, _ -> initWidgets((page + 1) % pages))); } - public boolean deleteFocusedRectWidget() { - if (focusedRectWidget == null) return false; + 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()))); - return true; } @Override @@ -381,7 +384,8 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse 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).extractWidgetRenderState(graphics, mouseX, mouseY, partialTicks); + new UVRectangleWidget(0f, 0f, 1f, 1f).setViewport(getNewTextureViewport()) + .extractWidgetRenderState(graphics, mouseX, mouseY, partialTicks); graphics.disableScissor(); } } @@ -443,7 +447,6 @@ private void computeFocusedPrimitives(ModelAnalyser analyser, List addRectWidget(WidgetFactory.rectWidget(r))); focusedRectWidgetNumberField.setMax(rectWidgets.size()); focusedRectWidgetNumberField.setNumber(0); rectWidgetsSizeWidget.setMessage(LocUtil.literal(String.valueOf(rectWidgets.size()))); @@ -689,7 +692,7 @@ private UVRectangleWidget addRectWidget(UVRectangleWidget rectWidget) { uMaxField.setNumber(rectWidget.uMax); vMaxField.setNumber(rectWidget.vMax); }); - if (textureViewArea != null) + if (textureViewArea != null) rectWidget.setViewport(getNewTextureViewport()); return rectWidget; } @@ -700,10 +703,6 @@ private Tooltip createTooltip(String key, Object... args) { return WidgetFactory.tooltip(key, args); } - private UVRectangleWidget createRectWidget(UVRectangle rect) { - return WidgetFactory.rectWidget(rect, null); - } - private Button createButton(Component message, int width, Button.OnPress onPress) { return WidgetFactory.button(message, width, widgetHeight, onPress); } @@ -813,7 +812,6 @@ public boolean mouseClicked(@NonNull MouseButtonEvent event, boolean doubleClick if (InputConstants.isKeyDown(minecraft.getWindow(), modifierKey.getValue())) { if (leftClickedWithModifier(event.x(), event.y())) return true; } else if (inTextureViewArea(event.x(), event.y())) { - assert getNewTextureViewport() != null; if (super.mouseClicked(event, doubleClick)) return true; if (storedX >= 0 && storedY >= 0) { float xMin = (float) Math.min(storedX, event.x()), yMin = (float) Math.min(storedY, event.y()), xMax = (float) Math.max(storedX, event.x()), yMax = (float) Math.max(storedY, event.y()); @@ -848,6 +846,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())); return true; } } @@ -867,6 +866,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())); } return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); } @@ -886,7 +886,6 @@ public boolean isPauseScreen() { } public TextureViewport getNewTextureViewport() { - if (textureViewArea == null) return null; return new TextureViewport(textureViewArea, textureScale, (float) textureX, (float) textureY); } } diff --git a/common/src/main/java/com/xtracr/realcamera/gui/components/UVRectangleWidget.java b/common/src/main/java/com/xtracr/realcamera/gui/components/UVRectangleWidget.java index 83e15a9c..301b75fb 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/components/UVRectangleWidget.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/components/UVRectangleWidget.java @@ -21,22 +21,13 @@ public class UVRectangleWidget extends AbstractWidget { private TextureViewport viewport; private Runnable onDelete = () -> {}; private Runnable onFocusUpdate = () -> {}; - - public UVRectangleWidget(float uMin, float vMin, float uMax, float vMax) { - super(0, 0, 16, 16, CommonComponents.EMPTY); - this.uMin = uMin; - this.vMin = vMin; - this.uMax = uMax; - this.vMax = vMax; - } - public UVRectangleWidget(float uMin, float vMin, float uMax, float vMax, @Nullable TextureViewport viewport) { + public UVRectangleWidget(float uMin, float vMin, float uMax, float vMax) { super(0, 0, 16, 16, CommonComponents.EMPTY); this.uMin = uMin; this.vMin = vMin; this.uMax = uMax; this.vMax = vMax; - this.viewport = viewport; } public void setOnDelete(@NonNull Runnable onDelete) { @@ -47,8 +38,9 @@ public void setOnFocusUpdate(@NonNull Runnable onFocusUpdate) { this.onFocusUpdate = onFocusUpdate; } - public void setViewport(@Nullable TextureViewport viewport) { + public UVRectangleWidget setViewport(@NonNull TextureViewport viewport) { this.viewport = viewport; + return this; } public UVRectangle toUVRectangle() { diff --git a/common/src/main/java/com/xtracr/realcamera/gui/util/TextureViewport.java b/common/src/main/java/com/xtracr/realcamera/gui/util/TextureViewport.java index d6f91487..0a09303d 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/util/TextureViewport.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/util/TextureViewport.java @@ -2,9 +2,8 @@ import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.world.phys.Vec2; -import org.jspecify.annotations.NonNull; -public record TextureViewport(@NonNull ScreenRectangle area, int textureScale, float textureX, float textureY) { +public record TextureViewport(ScreenRectangle area, int textureScale, float textureX, float textureY) { private static final int DEFAULT_SCALE = 80; public Vec2 uvToXY(float u, float v) { diff --git a/common/src/main/java/com/xtracr/realcamera/gui/util/WidgetFactory.java b/common/src/main/java/com/xtracr/realcamera/gui/util/WidgetFactory.java index f72c3df7..44d875d0 100644 --- a/common/src/main/java/com/xtracr/realcamera/gui/util/WidgetFactory.java +++ b/common/src/main/java/com/xtracr/realcamera/gui/util/WidgetFactory.java @@ -26,8 +26,8 @@ public static Button button(Component message, int width, int height, Button.OnP return Button.builder(message, onPress).size(width, height).build(); } - public static UVRectangleWidget rectWidget(UVRectangle rect, TextureViewport view) { - return new UVRectangleWidget(rect.uMin(), rect.vMin(), rect.uMax(), rect.vMax(), view); + public static UVRectangleWidget rectWidget(UVRectangle rect) { + return new UVRectangleWidget(rect.uMin(), rect.vMin(), rect.uMax(), rect.vMax()); } public static CycleButton.Builder cyclingButton(Map messages, T defaultValue) {