feat: implement libjf-resource-pack-entry-widgets to unify handling resource pack entry widgets
All checks were successful
ci/woodpecker/push/docs Pipeline was successful
ci/woodpecker/push/jfmod Pipeline was successful

This commit is contained in:
Johannes Frohnmeyer 2024-07-17 20:11:49 +02:00
parent b83d5f448c
commit 80db37575f
Signed by: Johannes
GPG Key ID: E76429612C2929F4
6 changed files with 210 additions and 0 deletions

View File

@ -0,0 +1,11 @@
plugins {
id("jfmod.module")
}
base {
archivesName = "libjf-resource-pack-entry-widgets-v0"
}
dependencies {
modImplementation("net.fabricmc.fabric-api:fabric-api-base")
}

View File

@ -0,0 +1,98 @@
package io.gitlab.jfronny.libjf.entrywidgets.api.v0;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.entrypoint.EntrypointContainer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.pack.ResourcePackOrganizer;
import java.util.Comparator;
import java.util.List;
/**
* Represents an additional widget inserted on the right hand side of a resource pack entry (in the resource pack or data pack screen)
*/
public interface ResourcePackEntryWidget {
/**
* Lists all known widgets from right to left. Immutable.
*/
List<ResourcePackEntryWidget> WIDGETS = FabricLoader.getInstance()
.getEntrypointContainers("libjf:resource_pack_entry_widget", ResourcePackEntryWidget.class)
.stream()
.sorted(Comparator.comparing(entrypoint -> entrypoint.getProvider().getMetadata().getId()))
.map(EntrypointContainer::getEntrypoint)
.toList();
/**
* Checks whether the widget should be rendered for a given pack.
*
* @param pack the pack to render the widget for
* @param selectable whether the pack is selectable
* @return whether the widget is visible
*/
default boolean isVisible(ResourcePackOrganizer.Pack pack, boolean selectable) {
return true;
}
/**
* Gets the width of this widget.
*
* @param pack the pack to render the widget for
* @return the width of the widget
*/
default int getWidth(ResourcePackOrganizer.Pack pack) {
return 20;
}
/**
* Gets the height of this widget.
*
* @param pack the pack to render the widget for
* @param rowHeight the height of the row containing the widget
* @return the height of the widget
*/
default int getHeight(ResourcePackOrganizer.Pack pack, int rowHeight) {
return 20;
}
/**
* Gets the Y position of this widget relative to the top of the row.
*
* @param pack the pack to render the widget for
* @param rowHeight the height of the row containing the widget
* @return the relative y position of the widget
*/
default int getY(ResourcePackOrganizer.Pack pack, int rowHeight) {
return (rowHeight - getHeight(pack, rowHeight)) / 2;
}
/**
* Gets the X margin of the widget.
* Two widgets will be separated by the maximum of their X margins.
* Also, the rightmost widget will be separated from the edge of the entry by its margin.
*
* @param pack the pack to render the widget for
* @return the X margin of the widget
*/
default int getXMargin(ResourcePackOrganizer.Pack pack) {
return 10;
}
/**
* Renders the widget.
*
* @param pack the pack to render the widget for
* @param context the context to draw to
* @param x the absolute x coordinate at which to draw
* @param y the absolute y coordinate at which to draw
* @param hovered whether the widget is being hovered by the cursor
* @param tickDelta the time that has passed since the last call
*/
void render(ResourcePackOrganizer.Pack pack, DrawContext context, int x, int y, boolean hovered, float tickDelta);
/**
* Executed when a widget is clicked.
*
* @param pack the pack for which the widget was clicked
*/
void onClick(ResourcePackOrganizer.Pack pack);
}

View File

@ -0,0 +1,54 @@
package io.gitlab.jfronny.libjf.entrywidgets.impl.mixin;
import io.gitlab.jfronny.libjf.entrywidgets.api.v0.ResourcePackEntryWidget;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.pack.PackListWidget;
import net.minecraft.client.gui.screen.pack.ResourcePackOrganizer;
import org.spongepowered.asm.mixin.Final;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PackListWidget.ResourcePackEntry.class)
public abstract class ResourcePackEntryMixin {
@Shadow
protected abstract boolean isSelectable();
@Shadow @Final
private ResourcePackOrganizer.Pack pack;
@Unique
int libjf$selected = -1;
@Inject(at = @At("TAIL"), method = "render(Lnet/minecraft/client/gui/DrawContext;IIIIIIIZF)V")
private void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta, CallbackInfo info) {
int prevMargin = 0;
int deltaX = 2;
boolean selectable = isSelectable();
libjf$selected = -1;
for (int i = 0; i < ResourcePackEntryWidget.WIDGETS.size(); i++) {
ResourcePackEntryWidget widget = ResourcePackEntryWidget.WIDGETS.get(i);
if (!widget.isVisible(pack, selectable)) continue;
deltaX += Math.max(prevMargin, widget.getXMargin(pack));
int width = widget.getWidth(pack);
int height = widget.getHeight(pack, entryHeight);
int entryX = x + entryWidth - deltaX - width;
int entryY = y + widget.getY(pack, entryHeight);
boolean widgetHovered = mouseX <= entryX + width && mouseX >= entryX && mouseY <= entryY + height && mouseY >= entryY;
widget.render(pack, context, entryX, entryY, widgetHovered, tickDelta);
if (widgetHovered) libjf$selected = i;
prevMargin = widget.getXMargin(pack);
}
}
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/AlwaysSelectedEntryListWidget$Entry;mouseClicked(DDI)Z"), method = "mouseClicked(DDI)Z", cancellable = true)
public void mouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> info) {
// Inject before super call
if (libjf$selected != -1) {
info.setReturnValue(true);
ResourcePackEntryWidget.WIDGETS.get(libjf$selected).onClick(pack);
}
}
}

View File

@ -0,0 +1,12 @@
{
"required": true,
"minVersion": "0.8",
"package": "io.gitlab.jfronny.libjf.entrywidgets.impl.mixin",
"compatibilityLevel": "JAVA_16",
"client": [
"ResourcePackEntryMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,33 @@
{
"schemaVersion": 1,
"id": "libjf-resource-pack-entry-widgets-v0",
"name": "LibJF Resource Pack Entry Widgets",
"version": "${version}",
"authors": [
"JFronny"
],
"contact": {
"email": "projects.contact@frohnmeyer-wds.de",
"homepage": "https://jfronny.gitlab.io",
"issues": "https://git.frohnmeyer-wds.de/JfMods/LibJF/issues",
"sources": "https://git.frohnmeyer-wds.de/JfMods/LibJF"
},
"license": "MIT",
"environment": "*",
"mixins": [
{
"config": "libjf-resource-pack-entry-widgets-v0.client.mixins.json",
"environment": "client"
}
],
"depends": {
"fabricloader": ">=0.12.0",
"minecraft": "*"
},
"custom": {
"modmenu": {
"parent": "libjf",
"badges": ["library"]
}
}
}

View File

@ -34,5 +34,7 @@ include("libjf-unsafe-v0")
include("libjf-mainhttp-v0")
include("libjf-web-v1")
include("libjf-resource-pack-entry-widgets-v0")
include("libjf-bom")
include("libjf-catalog")