Skip to content

Commit

Permalink
finish ui of custom config screen
Browse files Browse the repository at this point in the history
  • Loading branch information
btwonion committed Jul 9, 2024
1 parent 620c549 commit 61d1777
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 26 deletions.
6 changes: 3 additions & 3 deletions src/main/kotlin/dev/nyon/autodrop/config/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ data class TriggerConfig(
*
* @param enabled defines whether the archive is enabled.
* @param name is the identifier of the archive.
* @param entries define the entries of the archive. Those are represented by [dev.nyon.autodrop.config.ItemIdentification].
* @param entries define the entries of the archive. Those are represented by [dev.nyon.autodrop.config.ItemIdentificator].
* @param ignoredSlots define the slots that are ignored by the mod and thus don't get cleared automatically.
*/
@Serializable
data class Archive(
var enabled: Boolean = true,
val name: String,
var entries: MutableList<ItemIdentification>,
var entries: MutableList<ItemIdentificator>,
var ignoredSlots: MutableList<Int>
)

Expand All @@ -63,6 +63,6 @@ data class Archive(
* @param amount is the amount of the item that is required for the item to be dropped.
*/
@Serializable
data class ItemIdentification(
data class ItemIdentificator(
var type: @Contextual Item?, var components: @Contextual DataComponentPatch, var amount: Int
)
4 changes: 2 additions & 2 deletions src/main/kotlin/dev/nyon/autodrop/config/ConfigHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation

var config: Config = Config()
var currentItems = mutableSetOf<ItemIdentification>()
var currentItems = mutableSetOf<ItemIdentificator>()
var ignoredSlots = mutableSetOf<Int>()

fun reloadArchiveProperties() {
Expand All @@ -33,7 +33,7 @@ internal fun migrate(
archiveObject["name"]?.jsonPrimitive?.content ?: return null,
archiveObject["items"]?.jsonArray?.map secMap@{ content ->
val resourceLocation = /*? if >=1.21 {*/ ResourceLocation.parse(content.jsonPrimitive.contentOrNull ?: return null) /*?} else {*//* ResourceLocation(content.jsonPrimitive.contentOrNull ?: return null) *//*?}*/
return@secMap ItemIdentification(
return@secMap ItemIdentificator(
BuiltInRegistries.ITEM.get(resourceLocation),
DataComponentPatch.EMPTY,
1
Expand Down
124 changes: 124 additions & 0 deletions src/main/kotlin/dev/nyon/autodrop/config/screen/ArchiveItemsWidget.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package dev.nyon.autodrop.config.screen

import dev.nyon.autodrop.config.Archive
import dev.nyon.autodrop.config.ItemIdentificator
import dev.nyon.autodrop.extensions.screenComponent
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.ObjectSelectionList
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import kotlin.math.max
import dev.nyon.autodrop.minecraft as internalMinecraft

class ArchiveItemsWidget(var archive: Archive) : ObjectSelectionList<ArchiveItemEntry>(
internalMinecraft, 0, 0, OUTER_PAD, 20 + 2 * INNER_PAD
) {
override fun getX(): Int {
return internalMinecraft.screen!!.width / 4 + OUTER_PAD
}

override fun getRowLeft(): Int {
return x + INNER_PAD
}

override fun getRowWidth(): Int {
return getWidth() - 2 * INNER_PAD
}

override fun getScrollbarPosition(): Int {
return right - 7
}

override fun getMaxScroll(): Int {
return max(0, maxPosition - getHeight() + INNER_PAD)
}

override fun renderWidget(guiGraphics: GuiGraphics, i: Int, j: Int, f: Float) {
width = (internalMinecraft.screen!!.width / 4) * 3 - 2 * OUTER_PAD
height = internalMinecraft.screen!!.height - 2 * OUTER_PAD
super.renderWidget(guiGraphics, i, j, f)
}

fun refreshEntries() {
scrollAmount = 0.0
clearEntries()
archive.entries.map {
ArchiveItemEntry(it) {
archive.entries.remove(it)
refreshEntries()
}
}.forEach(::addEntry)
}
}

class ArchiveItemEntry(private val itemIdentificatior: ItemIdentificator, private val onRemove: () -> Unit) :
ObjectSelectionList.Entry<ArchiveItemEntry>() {
private val item: Item = itemIdentificatior.type ?: Items.AIR
private val itemLocationString = BuiltInRegistries.ITEM.getKey(item).run {
val string = toString()
if (string.length > 20) return@run "${string.take(17)}..."
else return@run string
}

private val removeButton = Button.builder(screenComponent("widget.items.remove")) {
onRemove()
}.width(75).build()

private val modifyButton = Button.builder(screenComponent("widget.items.modify")) {
// TODO: open modify screen
}.width(75).build()

override fun render(
guiGraphics: GuiGraphics,
index: Int,
y: Int,
x: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
isSelected: Boolean,
delta: Float
) {
val textPad = 7
guiGraphics.renderItem(ItemStack(item), x + INNER_PAD, y + 4)
guiGraphics.drawString(
internalMinecraft.font, itemLocationString, x + INNER_PAD * 2 + height, y + textPad, 0xFFFFFF
)

val twentyCharacterWidth = internalMinecraft.font.width(Component.literal("minecraft:chestplate")) * 2
guiGraphics.drawCenteredString(
internalMinecraft.font,
screenComponent("widget.items.component.${!itemIdentificatior.components.isEmpty}"),
x + INNER_PAD * 3 + twentyCharacterWidth,
y + textPad,
0xFFFFFF
)
guiGraphics.drawString(
internalMinecraft.font,
screenComponent("widget.items.amount", itemIdentificatior.amount.toString()),
x + INNER_PAD * 4 + (twentyCharacterWidth * 1.5).toInt(),
y + textPad,
0xFFFFFF
)

removeButton.setPosition(x + width - removeButton.width, y)
removeButton.render(guiGraphics, mouseX, mouseY, delta)
modifyButton.setPosition(x + width - removeButton.width - (INNER_PAD * 0.5).toInt() - modifyButton.width, y)
modifyButton.render(guiGraphics, mouseX, mouseY, delta)
}

override fun mouseClicked(d: Double, e: Double, i: Int): Boolean {
if (removeButton.isMouseOver(d, e)) return removeButton.mouseClicked(d, e, i)
if (modifyButton.isMouseOver(d, e)) return modifyButton.mouseClicked(d, e, i)
return super.mouseClicked(d, e, i)
}

override fun getNarration(): Component {
return Component.literal(item.description.toString())
}
}
67 changes: 62 additions & 5 deletions src/main/kotlin/dev/nyon/autodrop/config/screen/ArchiveScreen.kt
Original file line number Diff line number Diff line change
@@ -1,32 +1,89 @@
package dev.nyon.autodrop.config.screen

import dev.nyon.autodrop.config.Archive
import dev.nyon.autodrop.config.config
import dev.nyon.autodrop.extensions.screenComponent
import dev.nyon.konfig.config.saveConfig
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.screens.Screen
import dev.nyon.autodrop.minecraft as internalMinecraft

const val INNER_PAD = 5
const val OUTER_PAD = 10

class ArchiveScreen(private val parent: Screen?) : Screen(screenComponent("title")) {
var selected: Archive? = null
var selected: Archive = config.archives.first()

@Suppress("unused")
private val archivesWidget = ArchivesWidget(this).also {
addWidget(it)
it.refreshEntries()
}

private val archiveItemsWidget = ArchiveItemsWidget(selected).also {
addWidget(it)
it.refreshEntries()
}

private val doneButton = Button.builder(screenComponent("done")) {
onClose()
}.width(internalMinecraft.screen!!.width / 4 - 2 * OUTER_PAD).build().also { addWidget(it) }

private val setIgnoredSlotsButton = Button.builder(screenComponent("ignored")) {
// TODO: open ignored slots screen
}.width(internalMinecraft.screen!!.width / 4 - 2 * OUTER_PAD).build().also { addWidget(it) }

private val createArchiveButton = Button.builder(screenComponent("create")) {
// TODO: open create archive screen
}.width(internalMinecraft.screen!!.width / 4 - 2 * OUTER_PAD).build().also { addWidget(it) }

private val deleteArchiveButton = Button.builder(screenComponent("delete").withStyle(ChatFormatting.DARK_RED)) {
config.archives.remove(selected)
selected = config.archives.first()
archivesWidget.refreshEntries()
}.width(internalMinecraft.screen!!.width / 4 - 2 * OUTER_PAD).build().also { addWidget(it) }

override fun onClose() {
minecraft!!.setScreen(parent)
saveConfig(config)
}

override fun render(guiGraphics: GuiGraphics, i: Int, j: Int, f: Float) {
renderBackground(guiGraphics, i, j, f)
archivesWidget.render(guiGraphics, i, j, f)
override fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, tickDelta: Float) {
renderBackground(guiGraphics, mouseX, mouseY, tickDelta)
archivesWidget.render(guiGraphics, mouseX, mouseY, tickDelta)

// render archive item list, if empty, render info
if (selected.entries.isEmpty()) guiGraphics.drawCenteredString(
internalMinecraft.font,
screenComponent("noitems"),
internalMinecraft.screen!!.width / 8 * 5,
internalMinecraft.screen!!.height / 3,
0xFFFFFF
)
else archiveItemsWidget.render(guiGraphics, mouseX, mouseY, tickDelta)

// render control buttons
doneButton.setPosition(OUTER_PAD, internalMinecraft.screen!!.height - OUTER_PAD - 20)
doneButton.width = internalMinecraft.screen!!.width / 4 - 2 * OUTER_PAD
doneButton.render(guiGraphics, mouseX, mouseY, tickDelta)

setIgnoredSlotsButton.setPosition(OUTER_PAD, internalMinecraft.screen!!.height - OUTER_PAD - 2 * 20 - 3)
setIgnoredSlotsButton.width = internalMinecraft.screen!!.width / 4 - 2 * OUTER_PAD
setIgnoredSlotsButton.render(guiGraphics, mouseX, mouseY, tickDelta)

createArchiveButton.setPosition(OUTER_PAD, internalMinecraft.screen!!.height - OUTER_PAD - 3 * 20 - 6)
createArchiveButton.width = internalMinecraft.screen!!.width / 4 - 2 * OUTER_PAD
createArchiveButton.render(guiGraphics, mouseX, mouseY, tickDelta)

deleteArchiveButton.setPosition(OUTER_PAD, internalMinecraft.screen!!.height - OUTER_PAD - 4 * 20 - 9)
deleteArchiveButton.width = internalMinecraft.screen!!.width / 4 - 2 * OUTER_PAD
deleteArchiveButton.render(guiGraphics, mouseX, mouseY, tickDelta)
}

fun select(archive: Archive) {
selected = archive
archiveItemsWidget.archive = archive
archiveItemsWidget.refreshEntries()
}
}
21 changes: 9 additions & 12 deletions src/main/kotlin/dev/nyon/autodrop/config/screen/ArchivesWidget.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,8 @@ class ArchivesWidget(private val archiveScreen: ArchiveScreen) : ObjectSelection
return x + INNER_PAD
}

override fun getWidth(): Int {
return (internalMinecraft.screen!!.width / 4) - 2 * OUTER_PAD
}

override fun getHeight(): Int {
return (internalMinecraft.screen!!.height / 4) * 3 - 2 * OUTER_PAD
}

override fun getRowWidth(): Int {
return getWidth() - 2 * INNER_PAD
return width - 2 * INNER_PAD
}

override fun getScrollbarPosition(): Int {
Expand All @@ -46,6 +38,12 @@ class ArchivesWidget(private val archiveScreen: ArchiveScreen) : ObjectSelection
return max(0, maxPosition - getHeight() + INNER_PAD)
}

override fun renderWidget(guiGraphics: GuiGraphics, i: Int, j: Int, f: Float) {
width = (internalMinecraft.screen!!.width / 4) - 2 * OUTER_PAD
height = internalMinecraft.screen!!.height - 3 * OUTER_PAD - 9 - 4 * 20
super.renderWidget(guiGraphics, i, j, f)
}

fun refreshEntries() {
scrollAmount = 0.0
clearEntries()
Expand All @@ -66,12 +64,11 @@ class ArchivesWidgetEntry(private val archive: Archive, private val archiveScree
isSelected: Boolean,
delta: Float
) {
if (archiveScreen.selected?.name == archive.name) guiGraphics.fill(x - 3, y - 2, x + width - 2, y + height + 2, 0xFF404040.toInt())
if (archiveScreen.selected.name == archive.name) guiGraphics.fill(x - 3, y - 2, x + width - 2, y + height + 2, 0xFF404040.toInt())

// Draw archive name
val hundredPercentAlphaWhite = 0xFFFFFFFF.toInt()
val textPad = height - internalMinecraft.font.lineHeight / 2
guiGraphics.drawString(internalMinecraft.font, Component.literal(archive.name), x, y + textPad / 2, 0xFFFFFF)
guiGraphics.drawString(internalMinecraft.font, Component.literal(archive.name), x, y + INNER_PAD / 2, 0xFFFFFF)

// tick box - outer rectangle
val rightX = x + width - INNER_PAD
Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/dev/nyon/autodrop/extensions/Component.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package dev.nyon.autodrop.extensions

import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent

fun screenComponent(key: String): Component {
return Component.translatable("menu.autodrop.screen.$key")
fun screenComponent(key: String, vararg objects: Any): MutableComponent {
return Component.translatable("menu.autodrop.screen.$key", *objects)
}
7 changes: 5 additions & 2 deletions src/main/kotlin/dev/nyon/autodrop/extensions/Serializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,17 @@ object DataComponentPatchSerializer : KSerializer<DataComponentPatch> {
private val dynamicOps = registryAccess.createSerializationContext(NbtOps.INSTANCE)

override fun deserialize(decoder: Decoder): DataComponentPatch {
val result = itemParser.parse(StringReader(decoder.decodeString()))
val decoded = decoder.decodeString()
val correctString = if (decoded.startsWith('[')) "stone$decoded" else decoded
val result = itemParser.parse(StringReader(correctString))
return result.components
}

@Suppress("UNCHECKED_CAST")
override fun serialize(encoder: Encoder, value: DataComponentPatch) {
val stringMap = value.entrySet().mapNotNull { (type, value) ->
val resourceLocation = BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(type)
val encoded = type.codec()!!.encodeStart(dynamicOps, value.get() as Nothing?).result().getOrNull()
val encoded = (type.codec() as? com.mojang.serialization.Encoder<Any>)?.encodeStart(dynamicOps, value.orElseThrow())?.result()?.getOrNull()
return@mapNotNull if (resourceLocation == null || encoded == null) null
else "$resourceLocation=$encoded"
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/resources/assets/autodrop/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
"menu.autodrop.overlay.disabled": "disabled",
"menu.autodrop.name": "AutoDrop",
"menu.autodrop.screen.title": "AutoDrop",
"menu.autodrop.screen.noitems": "No items found in this archive.",
"menu.autodrop.screen.done": "Done",
"menu.autodrop.screen.ignored": "Set ignored slots",
"menu.autodrop.screen.create": "Create archive",
"menu.autodrop.screen.delete": "Delete selected archive",
"menu.autodrop.screen.widget.items.component.true": "Component Filter: Yes",
"menu.autodrop.screen.widget.items.component.false": "Component Filter: No",
"menu.autodrop.screen.widget.items.amount": "Min Amount: %d",
"menu.autodrop.screen.widget.items.remove": "Remove",
"menu.autodrop.screen.widget.items.modify": "Modify",
"key.autodrop.toggle": "Toggle AutoDrop",
"key.autodrop.gui": "Open GUI",
"key.autodrop.category": "AutoDrop",
Expand Down

0 comments on commit 61d1777

Please sign in to comment.