调整上传样板按钮布局

This commit is contained in:
GaLicn 2025-08-16 11:29:27 +08:00
parent e7f93b70c2
commit 6a536b90d0
7 changed files with 179 additions and 24 deletions

View File

@ -1,63 +1,142 @@
package com.extendedae_plus.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import appeng.client.gui.Icon;
import appeng.client.gui.AEBaseScreen;
import appeng.client.gui.me.items.PatternEncodingTermScreen;
import appeng.client.gui.style.ScreenStyle;
import appeng.client.gui.style.WidgetStyle;
import appeng.client.gui.widgets.IconButton;
import appeng.menu.me.items.PatternEncodingTermMenu;
import appeng.menu.AEBaseMenu;
import com.extendedae_plus.network.ModNetwork;
import com.extendedae_plus.network.UploadEncodedPatternToProviderC2SPacket;
import com.extendedae_plus.mixin.accessor.AEBaseScreenAccessor;
import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor;
import com.extendedae_plus.mixin.accessor.ScreenAccessor;
/**
* 在图样编码终端右侧工具栏加入一个上传按钮
* 在图样编码终端界面加入一个上传按钮
* 点击后把当前已编码样板上传到任意可用的样板供应器服务端自动选择
* 通过解析 AE2 样式中 encodePattern 的坐标将按钮放在其左侧紧挨位置
*/
@Mixin(AEBaseScreen.class)
public abstract class PatternEncodingTermScreenMixin<T extends AEBaseMenu> {
// 使用 @Shadow 访问 AEBaseScreen 的受保护方法
@Shadow
protected abstract <B extends Button> B addToLeftToolbar(B button);
@Unique
private boolean extendedae_plus$uploadBtnAdded = false;
private IconButton extendedae_plus$uploadBtn;
@Inject(method = "init", at = @At("HEAD"))
@Inject(method = "init", at = @At("TAIL"))
private void extendedae_plus$addUploadButton(CallbackInfo ci) {
// 仅在图样编码终端界面中添加按钮
if (!(((Object) this) instanceof PatternEncodingTermScreen)) {
return;
}
// 幂等避免每次 init() 都重复添加按钮
if (extendedae_plus$uploadBtnAdded) {
return;
}
var uploadBtn = new IconButton(btn -> ModNetwork.CHANNEL
.sendToServer(new com.extendedae_plus.network.RequestProvidersListC2SPacket())) {
// 复用已存在的按钮实例避免重复创建
if (extendedae_plus$uploadBtn == null) {
extendedae_plus$uploadBtn = new IconButton(btn -> ModNetwork.CHANNEL
.sendToServer(new com.extendedae_plus.network.RequestProvidersListC2SPacket())) {
@Override
protected Icon getIcon() {
return Icon.ARROW_UP;
}
};
uploadBtn.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.choose_provider")));
};
extendedae_plus$uploadBtn.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.choose_provider")));
}
// 直接调用 @Shadow 方法避免跨包 protected 访问问题
this.addToLeftToolbar(uploadBtn);
extendedae_plus$uploadBtnAdded = true;
// 解析 encodePattern 的样式位置
try {
ScreenStyle style = ((AEBaseScreenAccessor<?>) (Object) this).extendedae_plus$getStyle();
WidgetStyle ws = style.getWidget("encodePattern");
int leftPos = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getLeftPos();
int topPos = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getTopPos();
int imageWidth = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getImageWidth();
int imageHeight = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getImageHeight();
Rect2i bounds = new Rect2i(leftPos, topPos, imageWidth, imageHeight);
var pos = ws.resolve(bounds);
int targetW = ws.getWidth() > 0 ? ws.getWidth() : 18;
int targetH = ws.getHeight() > 0 ? ws.getHeight() : 18;
// 尺寸与 encodePattern 一致
extendedae_plus$uploadBtn.setWidth(targetW);
extendedae_plus$uploadBtn.setHeight(targetH);
// 放在其左侧紧挨预留 2px 间距
extendedae_plus$uploadBtn.setX(pos.getX() - targetW - 2);
extendedae_plus$uploadBtn.setY(pos.getY());
} catch (Throwable t) {
// 回退放在界面右侧大致位置避免不可见
extendedae_plus$uploadBtn.setWidth(18);
extendedae_plus$uploadBtn.setHeight(18);
int leftPos = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getLeftPos();
int topPos = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getTopPos();
int imageWidth = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getImageWidth();
extendedae_plus$uploadBtn.setX(leftPos + imageWidth - 18 - 8);
extendedae_plus$uploadBtn.setY(topPos + 88);
}
// 直接向 renderables / children 列表添加避免依赖受保护方法
var accessor = (ScreenAccessor) (Object) this;
var renderables = accessor.extendedae_plus$getRenderables();
var children = accessor.extendedae_plus$getChildren();
if (!renderables.contains(extendedae_plus$uploadBtn)) {
renderables.add(extendedae_plus$uploadBtn);
}
if (!children.contains(extendedae_plus$uploadBtn)) {
children.add(extendedae_plus$uploadBtn);
}
}
@Inject(method = "containerTick", at = @At("TAIL"))
private void extendedae_plus$ensureUploadButton(CallbackInfo ci) {
if (!(((Object) this) instanceof PatternEncodingTermScreen)) {
return;
}
if (extendedae_plus$uploadBtn == null) {
return;
}
var renderables2 = ((ScreenAccessor) (Object) this).extendedae_plus$getRenderables();
if (!renderables2.contains(extendedae_plus$uploadBtn)) {
// 被其它模组清空/替换后重新计算一次位置并补回
try {
ScreenStyle style = ((AEBaseScreenAccessor<?>) (Object) this).extendedae_plus$getStyle();
WidgetStyle ws = style.getWidget("encodePattern");
int leftPos = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getLeftPos();
int topPos = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getTopPos();
int imageWidth = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getImageWidth();
int imageHeight = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getImageHeight();
Rect2i bounds = new Rect2i(leftPos, topPos, imageWidth, imageHeight);
var pos = ws.resolve(bounds);
int targetW = ws.getWidth() > 0 ? ws.getWidth() : 18;
int targetH = ws.getHeight() > 0 ? ws.getHeight() : 18;
extendedae_plus$uploadBtn.setWidth(targetW);
extendedae_plus$uploadBtn.setHeight(targetH);
extendedae_plus$uploadBtn.setX(pos.getX() - targetW - 2);
extendedae_plus$uploadBtn.setY(pos.getY());
} catch (Throwable t) {
int leftPos = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getLeftPos();
int topPos = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getTopPos();
int imageWidth = ((AbstractContainerScreenAccessor<?>) (Object) this).extendedae_plus$getImageWidth();
extendedae_plus$uploadBtn.setWidth(18);
extendedae_plus$uploadBtn.setHeight(18);
extendedae_plus$uploadBtn.setX(leftPos + imageWidth - 18 - 8);
extendedae_plus$uploadBtn.setY(topPos + 88);
}
var accessor2 = (ScreenAccessor) (Object) this;
var r = accessor2.extendedae_plus$getRenderables();
var c = accessor2.extendedae_plus$getChildren();
if (!r.contains(extendedae_plus$uploadBtn)) {
r.add(extendedae_plus$uploadBtn);
}
if (!c.contains(extendedae_plus$uploadBtn)) {
c.add(extendedae_plus$uploadBtn);
}
}
}
}

View File

@ -0,0 +1,14 @@
package com.extendedae_plus.mixin.accessor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import appeng.client.gui.AEBaseScreen;
import appeng.client.gui.style.ScreenStyle;
import appeng.menu.AEBaseMenu;
@Mixin(value = AEBaseScreen.class, remap = false)
public interface AEBaseScreenAccessor<T extends AEBaseMenu> {
@Accessor(value = "style", remap = false)
ScreenStyle extendedae_plus$getStyle();
}

View File

@ -0,0 +1,11 @@
package com.extendedae_plus.mixin.accessor;
import org.spongepowered.asm.mixin.Mixin;
import appeng.client.gui.AEBaseScreen;
import appeng.menu.AEBaseMenu;
@Mixin(AEBaseScreen.class)
public interface AEBaseScreenInvoker<T extends AEBaseMenu> {
// 空接口避免在 AEBaseScreen 上声明不存在方法的 Invoker 导致编译错误
}

View File

@ -0,0 +1,15 @@
package com.extendedae_plus.mixin.accessor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.world.inventory.AbstractContainerMenu;
@Mixin(AbstractContainerScreen.class)
public interface AbstractContainerScreenAccessor<T extends AbstractContainerMenu> {
@Accessor("leftPos") int extendedae_plus$getLeftPos();
@Accessor("topPos") int extendedae_plus$getTopPos();
@Accessor("imageWidth") int extendedae_plus$getImageWidth();
@Accessor("imageHeight") int extendedae_plus$getImageHeight();
}

View File

@ -0,0 +1,19 @@
package com.extendedae_plus.mixin.accessor;
import java.util.List;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
@Mixin(Screen.class)
public interface ScreenAccessor {
@Accessor("renderables")
List<Renderable> extendedae_plus$getRenderables();
@Accessor("children")
List<GuiEventListener> extendedae_plus$getChildren();
}

View File

@ -0,0 +1,14 @@
package com.extendedae_plus.mixin.accessor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
@Mixin(Screen.class)
public interface ScreenInvoker {
@Invoker("addRenderableWidget")
<W extends GuiEventListener & Renderable> W extendedae_plus$invokeAddRenderableWidget(W widget);
}

View File

@ -10,7 +10,10 @@
"HighlightButtonMixin",
"PickFromWirelessMixin",
"PatternEncodingTermScreenMixin",
"jei.EncodePatternTransferHandlerMixin"
"jei.EncodePatternTransferHandlerMixin",
"accessor.AEBaseScreenAccessor",
"accessor.AbstractContainerScreenAccessor",
"accessor.ScreenAccessor"
],
"mixins": [
"ContainerExPatternProviderMixin",