feat: add colour config options

This commit is contained in:
Linnea Gräf
2025-07-04 19:30:31 +02:00
parent 89642c2443
commit 918ee6ab5b
6 changed files with 190 additions and 18 deletions

View File

@@ -1,6 +1,7 @@
package moe.nea.firmament.compat.moulconfig package moe.nea.firmament.compat.moulconfig
import com.google.auto.service.AutoService import com.google.auto.service.AutoService
import io.github.notenoughupdates.moulconfig.ChromaColour
import io.github.notenoughupdates.moulconfig.Config import io.github.notenoughupdates.moulconfig.Config
import io.github.notenoughupdates.moulconfig.DescriptionRendereringBehaviour import io.github.notenoughupdates.moulconfig.DescriptionRendereringBehaviour
import io.github.notenoughupdates.moulconfig.Social import io.github.notenoughupdates.moulconfig.Social
@@ -20,6 +21,7 @@ import io.github.notenoughupdates.moulconfig.gui.editors.ComponentEditor
import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorAccordion import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorAccordion
import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorBoolean import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorBoolean
import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorButton import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorButton
import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorColour
import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorDropdown import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorDropdown
import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorText import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorText
import io.github.notenoughupdates.moulconfig.observer.GetSetter import io.github.notenoughupdates.moulconfig.observer.GetSetter
@@ -39,6 +41,7 @@ import moe.nea.firmament.gui.config.AllConfigsGui
import moe.nea.firmament.gui.config.BooleanHandler import moe.nea.firmament.gui.config.BooleanHandler
import moe.nea.firmament.gui.config.ChoiceHandler import moe.nea.firmament.gui.config.ChoiceHandler
import moe.nea.firmament.gui.config.ClickHandler import moe.nea.firmament.gui.config.ClickHandler
import moe.nea.firmament.gui.config.ColourHandler
import moe.nea.firmament.gui.config.DurationHandler import moe.nea.firmament.gui.config.DurationHandler
import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider
import moe.nea.firmament.gui.config.HudMeta import moe.nea.firmament.gui.config.HudMeta
@@ -186,6 +189,26 @@ class MCConfigEditorIntegration : FirmamentConfigScreenProvider {
} }
} }
} }
register(ColourHandler::class.java) { handler, option, accordionId, configObject ->
object : ProcessedEditableOptionFirm<ChromaColour>(option, accordionId, configObject) {
override fun fromT(t: ChromaColour): Any {
return t
}
override fun toT(any: Any?): ChromaColour? {
return any as ChromaColour?
}
override fun createEditor(): GuiOptionEditor {
return GuiOptionEditorColour(this)
}
override fun getType(): Type? {
return ChromaColour::class.java
}
}
}
register(ClickHandler::class.java) { handler, option, categoryAccordionId, configObject -> register(ClickHandler::class.java) { handler, option, categoryAccordionId, configObject ->
object : ProcessedEditableOptionFirm<Unit>(option, categoryAccordionId, configObject) { object : ProcessedEditableOptionFirm<Unit>(option, categoryAccordionId, configObject) {
override fun createEditor(): GuiOptionEditor { override fun createEditor(): GuiOptionEditor {

View File

@@ -9,6 +9,7 @@ import dev.isxander.yacl3.api.Option
import dev.isxander.yacl3.api.OptionDescription import dev.isxander.yacl3.api.OptionDescription
import dev.isxander.yacl3.api.OptionGroup import dev.isxander.yacl3.api.OptionGroup
import dev.isxander.yacl3.api.YetAnotherConfigLib import dev.isxander.yacl3.api.YetAnotherConfigLib
import dev.isxander.yacl3.api.controller.ColorControllerBuilder
import dev.isxander.yacl3.api.controller.ControllerBuilder import dev.isxander.yacl3.api.controller.ControllerBuilder
import dev.isxander.yacl3.api.controller.DoubleSliderControllerBuilder import dev.isxander.yacl3.api.controller.DoubleSliderControllerBuilder
import dev.isxander.yacl3.api.controller.EnumControllerBuilder import dev.isxander.yacl3.api.controller.EnumControllerBuilder
@@ -18,6 +19,8 @@ import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder
import dev.isxander.yacl3.api.controller.ValueFormatter import dev.isxander.yacl3.api.controller.ValueFormatter
import dev.isxander.yacl3.gui.YACLScreen import dev.isxander.yacl3.gui.YACLScreen
import dev.isxander.yacl3.gui.tab.ListHolderWidget import dev.isxander.yacl3.gui.tab.ListHolderWidget
import io.github.notenoughupdates.moulconfig.ChromaColour
import java.awt.Color
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit import kotlin.time.DurationUnit
@@ -27,6 +30,7 @@ import net.minecraft.text.Text
import moe.nea.firmament.gui.config.BooleanHandler import moe.nea.firmament.gui.config.BooleanHandler
import moe.nea.firmament.gui.config.ChoiceHandler import moe.nea.firmament.gui.config.ChoiceHandler
import moe.nea.firmament.gui.config.ClickHandler import moe.nea.firmament.gui.config.ClickHandler
import moe.nea.firmament.gui.config.ColourHandler
import moe.nea.firmament.gui.config.DurationHandler import moe.nea.firmament.gui.config.DurationHandler
import moe.nea.firmament.gui.config.EnumRenderer import moe.nea.firmament.gui.config.EnumRenderer
import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider
@@ -39,6 +43,8 @@ import moe.nea.firmament.gui.config.ManagedOption
import moe.nea.firmament.gui.config.StringHandler import moe.nea.firmament.gui.config.StringHandler
import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.keybindings.SavedKeyBinding
import moe.nea.firmament.util.FirmFormatters import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.getRGBAWithoutAnimation
import moe.nea.firmament.util.toChromaWithoutAnimation
@AutoService(FirmamentConfigScreenProvider::class) @AutoService(FirmamentConfigScreenProvider::class)
@@ -56,20 +62,22 @@ class YaclIntegration : FirmamentConfigScreenProvider {
OptionGroup.createBuilder() OptionGroup.createBuilder()
.name(it.labelText) .name(it.labelText)
.options(buildOptions(it.sortedOptions)) .options(buildOptions(it.sortedOptions))
.build()) .build()
)
} }
} }
.build() .build()
} }
fun buildOptions(options: List<ManagedOption<*>>): Collection<Option<*>> = fun buildOptions(options: List<ManagedOption<*>>): Collection<Option<*>> =
options.map { buildOption(it) } options.flatMap { buildOption(it) }
private fun <T : Any> buildOption(managedOption: ManagedOption<T>): Option<*> { private fun <T : Any> buildOption(managedOption: ManagedOption<T>): Collection<Option<*>> {
val handler = managedOption.handler val handler = managedOption.handler
val binding = Binding.generic(managedOption.default(), val binding = Binding.generic(
managedOption::value, managedOption.default(),
{ managedOption.value = it; managedOption.element.save() }) managedOption::value,
{ managedOption.value = it; managedOption.element.save() })
fun <T> createDefaultBinding(function: (Option<T>) -> ControllerBuilder<T>): Option.Builder<T> { fun <T> createDefaultBinding(function: (Option<T>) -> ControllerBuilder<T>): Option.Builder<T> {
return Option.createBuilder<T>() return Option.createBuilder<T>()
@@ -78,30 +86,72 @@ class YaclIntegration : FirmamentConfigScreenProvider {
.binding(binding as Binding<T>) .binding(binding as Binding<T>)
.controller { function(it) } .controller { function(it) }
} }
fun Option<out Any>.single() = listOf(this)
fun ButtonOption.Builder.single() = build().single()
fun Option.Builder<out Any>.single() = build().single()
when (handler) { when (handler) {
is ClickHandler -> return ButtonOption.createBuilder() is ClickHandler -> return ButtonOption.createBuilder()
.name(managedOption.labelText) .name(managedOption.labelText)
.action { t, u -> .action { t, u ->
handler.runnable() handler.runnable()
} }
.build() .single()
is HudMetaHandler -> return ButtonOption.createBuilder() is HudMetaHandler -> return ButtonOption.createBuilder()
.name(managedOption.labelText) .name(managedOption.labelText)
.action { t, u -> .action { t, u ->
handler.openEditor(managedOption as ManagedOption<HudMeta>, t) handler.openEditor(managedOption as ManagedOption<HudMeta>, t)
} }
.build() .single()
is ChoiceHandler<*> -> return createDefaultBinding { is ChoiceHandler<*> -> return createDefaultBinding {
createChoiceBinding(handler as ChoiceHandler<*>, managedOption as ManagedOption<*>, it as Option<*>) createChoiceBinding(handler as ChoiceHandler<*>, managedOption as ManagedOption<*>, it as Option<*>)
}.build() }.single()
is BooleanHandler -> return createDefaultBinding(TickBoxControllerBuilder::create).build() is ColourHandler -> {
is StringHandler -> return createDefaultBinding(StringControllerBuilder::create).build() managedOption as ManagedOption<ChromaColour>
val colorBinding =
Binding.generic(
managedOption.default().getRGBAWithoutAnimation(),
{ managedOption.value.getRGBAWithoutAnimation() },
{
managedOption.value =
it.toChromaWithoutAnimation(managedOption.value.timeForFullRotationInMillis)
managedOption.element.save()
})
val speedBinding =
Binding.generic(
managedOption.default().timeForFullRotationInMillis,
{ managedOption.value.timeForFullRotationInMillis },
{
managedOption.value = managedOption.value.copy(timeForFullRotationInMillis = it)
managedOption.element.save()
}
)
return listOf(
Option.createBuilder<Color>()
.name(managedOption.labelText)
.binding(colorBinding)
.controller {
ColorControllerBuilder.create(it)
.allowAlpha(true)
}
.build(),
Option.createBuilder<Int>()
.name(managedOption.labelText)
.binding(speedBinding)
.controller { IntegerSliderControllerBuilder.create(it).range(0, 60_000).step(10) }
.build(),
)
}
is BooleanHandler -> return createDefaultBinding(TickBoxControllerBuilder::create).single()
is StringHandler -> return createDefaultBinding(StringControllerBuilder::create).single()
is IntegerHandler -> return createDefaultBinding { is IntegerHandler -> return createDefaultBinding {
IntegerSliderControllerBuilder.create(it).range(handler.min, handler.max).step(1) IntegerSliderControllerBuilder.create(it).range(handler.min, handler.max).step(1)
}.build() }.single()
is DurationHandler -> return Option.createBuilder<Double>() is DurationHandler -> return Option.createBuilder<Double>()
.name(managedOption.labelText) .name(managedOption.labelText)
@@ -112,13 +162,13 @@ class YaclIntegration : FirmamentConfigScreenProvider {
.step(0.1) .step(0.1)
.range(handler.min.toDouble(DurationUnit.SECONDS), handler.max.toDouble(DurationUnit.SECONDS)) .range(handler.min.toDouble(DurationUnit.SECONDS), handler.max.toDouble(DurationUnit.SECONDS))
} }
.build() .single()
is KeyBindingHandler -> return createDefaultBinding { is KeyBindingHandler -> return createDefaultBinding {
KeybindingBuilder(it, managedOption as ManagedOption<SavedKeyBinding>) KeybindingBuilder(it, managedOption as ManagedOption<SavedKeyBinding>)
}.build() }.single()
else -> return LabelOption.create(Text.literal("This option is currently unhandled for this config menu. Please report this as a bug.")) else -> return listOf(LabelOption.create(Text.literal("This option is currently unhandled for this config menu. Please report this as a bug.")))
} }
} }

View File

@@ -1,12 +1,13 @@
package moe.nea.firmament.features.items package moe.nea.firmament.features.items
import io.github.notenoughupdates.moulconfig.ChromaColour
import me.shedaniel.math.Color import me.shedaniel.math.Color
import net.minecraft.util.hit.BlockHitResult
import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import net.minecraft.util.hit.BlockHitResult
import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.util.extraAttributes import moe.nea.firmament.util.extraAttributes
import moe.nea.firmament.util.render.RenderInWorldContext import moe.nea.firmament.util.render.RenderInWorldContext
import moe.nea.firmament.util.skyBlockId import moe.nea.firmament.util.skyBlockId
@@ -19,6 +20,7 @@ object EtherwarpOverlay : FirmamentFeature {
object TConfig : ManagedConfig(identifier, Category.ITEMS) { object TConfig : ManagedConfig(identifier, Category.ITEMS) {
var etherwarpOverlay by toggle("etherwarp-overlay") { false } var etherwarpOverlay by toggle("etherwarp-overlay") { false }
var cube by toggle("cube") { true } var cube by toggle("cube") { true }
val cubeColour by colour("cube-colour") { ChromaColour.fromStaticRGB(172, 0, 255, 60) }
var wireframe by toggle("wireframe") { false } var wireframe by toggle("wireframe") { false }
} }
@@ -44,7 +46,7 @@ object EtherwarpOverlay : FirmamentFeature {
if (!world.getBlockState(blockPos.up()).isAir) return if (!world.getBlockState(blockPos.up()).isAir) return
if (!world.getBlockState(blockPos.up(2)).isAir) return if (!world.getBlockState(blockPos.up(2)).isAir) return
RenderInWorldContext.renderInWorld(event) { RenderInWorldContext.renderInWorld(event) {
if (TConfig.cube) block(blockPos, Color.ofRGBA(172, 0, 255, 60).color) if (TConfig.cube) block(blockPos, TConfig.cubeColour.getEffectiveColourRGB())
if (TConfig.wireframe) wireframeCube(blockPos, 10f) if (TConfig.wireframe) wireframeCube(blockPos, 10f)
} }
} }

View File

@@ -0,0 +1,82 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.ChromaColour
import io.github.notenoughupdates.moulconfig.gui.component.ColorSelectComponent
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
class ColourHandler(val config: ManagedConfig) :
ManagedConfig.OptionHandler<ChromaColour> {
@Serializable
data class ChromaDelegate(
@SerialName("h")
val hue: Float,
@SerialName("s")
val saturation: Float,
@SerialName("b")
val brightness: Float,
@SerialName("a")
val alpha: Int,
@SerialName("c")
val timeForFullRotationInMillis: Int,
) {
constructor(delegate: ChromaColour) : this(
delegate.hue,
delegate.saturation,
delegate.brightness,
delegate.alpha,
delegate.timeForFullRotationInMillis
)
fun into(): ChromaColour = ChromaColour(hue, saturation, brightness, alpha, timeForFullRotationInMillis)
}
object ChromaSerializer : KSerializer<ChromaColour> {
override val descriptor: SerialDescriptor
get() = SerialDescriptor("FirmChromaColour", ChromaDelegate.serializer().descriptor)
override fun serialize(
encoder: Encoder,
value: ChromaColour
) {
encoder.encodeSerializableValue(ChromaDelegate.serializer(), ChromaDelegate(value))
}
override fun deserialize(decoder: Decoder): ChromaColour {
return decoder.decodeSerializableValue(ChromaDelegate.serializer()).into()
}
}
override fun toJson(element: ChromaColour): JsonElement? {
return Json.encodeToJsonElement(ChromaSerializer, element)
}
override fun fromJson(element: JsonElement): ChromaColour {
return Json.decodeFromJsonElement(ChromaSerializer, element)
}
override fun emitGuiElements(
opt: ManagedOption<ChromaColour>,
guiAppender: GuiAppender
) {
guiAppender.appendLabeledRow(
opt.labelText,
ColorSelectComponent(
0,
0,
opt.value.toLegacyString(),
{
opt.value = ChromaColour.forLegacyString(it)
config.save()
},
{ }
)
)
}
}

View File

@@ -1,6 +1,7 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import com.mojang.serialization.Codec import com.mojang.serialization.Codec
import io.github.notenoughupdates.moulconfig.ChromaColour
import io.github.notenoughupdates.moulconfig.gui.CloseEventListener import io.github.notenoughupdates.moulconfig.gui.CloseEventListener
import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
import io.github.notenoughupdates.moulconfig.gui.GuiContext import io.github.notenoughupdates.moulconfig.gui.GuiContext
@@ -118,6 +119,10 @@ abstract class ManagedConfig(
return option(propertyName, default, BooleanHandler(this)) return option(propertyName, default, BooleanHandler(this))
} }
protected fun colour(propertyName: String, default: ()-> ChromaColour) : ManagedOption<ChromaColour> {
return option(propertyName, default, ColourHandler(this))
}
protected fun <E> choice( protected fun <E> choice(
propertyName: String, propertyName: String,
enumClass: Class<E>, enumClass: Class<E>,

View File

@@ -0,0 +1,10 @@
package moe.nea.firmament.util
import io.github.notenoughupdates.moulconfig.ChromaColour
import java.awt.Color
fun ChromaColour.getRGBAWithoutAnimation() =
Color(ChromaColour.specialToSimpleRGB(toLegacyString()), true)
fun Color.toChromaWithoutAnimation(timeForFullRotationInMillis: Int = 0) =
ChromaColour.fromRGB(red, green, blue, timeForFullRotationInMillis, alpha)