diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt index 08a7414d8..5c97f4607 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt @@ -139,6 +139,8 @@ private fun misc(provider: MatteryLanguageProvider) { gui("exopack.go_back", "Open vanilla inventory") gui("exopack.go_in", "Open Exopack inventory") gui("exopack.toggle_visibility", "Toggle Exopack visibility") + gui("exopack.change_color", "Customize Exopack color") + gui("exopack.change_color2", "Press Middle Mouse Button to remove color") gui("exopack.probe1", "This little device feels unnatural to touch, it is almost certainly resilient to any possible attempt to break it open.") gui("exopack.probe2", "There is fingerprint reader built into one of sides which gently glow when touched.") @@ -664,6 +666,22 @@ private fun gui(provider: MatteryLanguageProvider) { with(provider.english) { gui("quicksearch", "Quick search...") + gui("color_picker", "Color Picker") + + gui("color.short.red", "R") + gui("color.short.green", "G") + gui("color.short.blue", "B") + gui("color.short.hue", "H") + gui("color.short.saturation", "S") + gui("color.short.value", "V") + + gui("color.full.red", "Red") + gui("color.full.green", "Green") + gui("color.full.blue", "Blue") + gui("color.full.hue", "Hue") + gui("color.full.saturation", "Saturation") + gui("color.full.value", "Value") + gui("item_monitor.refill_source.desc", "Controls from where to take items for slot auto refill") gui("item_monitor.refill_source.system", "System only. Crafting grid will be auto refilled only from storage system. This is the behavior you see in AE2 and Refined Storage") gui("item_monitor.refill_source.inventory", "Inventory only. Crafting grid will be auto refilled only from player's inventory") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt index faecb219c..34b334622 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt @@ -147,6 +147,8 @@ private fun misc(provider: MatteryLanguageProvider) { gui("exopack.go_back", "Открыть обычный инвентарь") gui("exopack.go_in", "Открыть инвентарь экзопака") gui("exopack.toggle_visibility", "Переключить отображение Экзопака") + gui("exopack.change_color", "Изменить окраску Экзопака") + gui("exopack.change_color2", "Нажмите среднюю кнопку мыши для сброса окраски") gui("exopack.probe1", "Данное маленькое устройство необычно на ощупь, а так же неприступно для любых попыток вскрыть.") gui("exopack.probe2", "На одной из сторон данного устройства находится сканер отпечатка, который тускло загорается при касании.") @@ -668,6 +670,22 @@ private fun gui(provider: MatteryLanguageProvider) { with(provider.russian) { gui("quicksearch", "Быстрый поиск...") + gui("color_picker", "Выбор цвета") + + gui("color.short.red", "К") + gui("color.short.green", "З") + gui("color.short.blue", "С") + gui("color.short.hue", "Ц") + gui("color.short.saturation", "Н") + gui("color.short.value", "Я") + + gui("color.full.red", "Красный") + gui("color.full.green", "Зелёный") + gui("color.full.blue", "Синий") + gui("color.full.hue", "Цвет") + gui("color.full.saturation", "Насыщение") + gui("color.full.value", "Яркость") + gui("item_monitor.refill_source.desc", "Контролирует источник предметов для заполнения сетки создания") gui("item_monitor.refill_source.system", "Только система. Сетка создания будет заполняться только из системы предметов. Данный параметр соответствует поведению AE2 и Refined Storage") gui("item_monitor.refill_source.inventory", "Только инвентарь. Сетка создания будет заполняться только из инвентаря игрока") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/ColorPicker.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/ColorPicker.kt new file mode 100644 index 000000000..af60b2190 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/ColorPicker.kt @@ -0,0 +1,746 @@ +package ru.dbotthepony.mc.otm.client.screen.panels + +import com.mojang.blaze3d.platform.InputConstants +import com.mojang.datafixers.util.Either +import it.unimi.dsi.fastutil.floats.FloatConsumer +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.client.gui.screens.Screen +import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.client.playGuiClickSound +import ru.dbotthepony.mc.otm.client.render.MatterySprite +import ru.dbotthepony.mc.otm.client.render.RenderGravity +import ru.dbotthepony.mc.otm.client.render.UVWindingOrder +import ru.dbotthepony.mc.otm.client.render.WidgetLocation +import ru.dbotthepony.mc.otm.client.render.renderRect +import ru.dbotthepony.mc.otm.client.screen.MatteryScreen +import ru.dbotthepony.mc.otm.client.screen.panels.button.AbstractButtonPanel +import ru.dbotthepony.mc.otm.client.screen.panels.input.TextInputPanel +import ru.dbotthepony.mc.otm.core.TextComponent +import ru.dbotthepony.mc.otm.core.TranslatableComponent +import ru.dbotthepony.mc.otm.core.math.HSVColor +import ru.dbotthepony.mc.otm.core.math.RGBAColor +import java.util.function.Consumer +import kotlin.math.roundToInt + +open class ColorBoxPanel( + screen: S, + parent: EditablePanel<*>?, + x: Float, + y: Float, + width: Float = 64f, + height: Float = 64f, + protected val callback: Consumer? = null, +) : EditablePanel(screen, parent, x, y, width, height) { + var backgroundColor = RGBAColor.RED + private set + var markerPos = backgroundColor.toHSV() + protected set + + fun setColor(color: Either) { + color.map( + { markerPos = it.toHSV(); if (it.canRepresentHue()) backgroundColor = HSVColor(it.toHSV().hue, 1f, 1f).toRGBA() }, + { markerPos = it; backgroundColor = HSVColor(it.hue, 1f, 1f).toRGBA() }) + } + + var isPressed = false + private set + + override fun shouldRenderTooltips(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { + return super.shouldRenderTooltips(graphics, mouseX, mouseY, partialTick) || isPressed + } + + protected open fun onPickedColor(color: HSVColor) { + callback?.accept(color) + } + + protected fun pickColor(mouseX: Double, mouseY: Double) { + val (x, y) = screenToLocal(mouseX, mouseY) + + val saturation = 1f - x.coerceIn(0f, width) / width + val value = 1f - y.coerceIn(0f, height) / height + + markerPos = HSVColor(backgroundColor.toHSV().hue, saturation, value) + onPickedColor(markerPos) + } + + override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { + if (button == InputConstants.MOUSE_BUTTON_LEFT) { + if (!isPressed) { + isPressed = true + grabMouseInput = true + + pickColor(x, y) + playGuiClickSound() + } + + return true + } + + return super.mouseClickedInner(x, y, button) + } + + override fun mouseDraggedInner(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean { + if (isPressed && button == InputConstants.MOUSE_BUTTON_LEFT) { + pickColor(x, y) + return true + } + + return super.mouseDraggedInner(x, y, button, xDelta, yDelta) + } + + override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean { + if (button == InputConstants.MOUSE_BUTTON_LEFT) { + if (isPressed) { + isPressed = false + grabMouseInput = false + } + + return true + } + + return super.mouseReleasedInner(x, y, button) + } + + override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + graphics.renderRect(0f, 0f, width, height, color = RGBAColor.WHITE) + GRADIENT_LEFT.render(graphics, 0f, 0f, width, height, color = backgroundColor) + GRADIENT_DOWN.render(graphics, 0f, 0f, width, height, color = RGBAColor.BLACK) + + val x = (1f - markerPos.saturation) * width + val y = (1f - markerPos.value) * height + + LINE_VERTICAL.render(graphics, x = x - 1f, height = height) + LINE_HORIZONTAL.render(graphics, y = y - 1f, width = width) + LINE_CROSS.render(graphics, x = x - 1f, y = y - 1f) + } + + companion object { + val GRADIENT_UP = MatterySprite(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/gradient_v.png"), 0f, 0f, 32f, 256f, 32f, 256f) + val GRADIENT_DOWN = GRADIENT_UP.copy(winding = UVWindingOrder.FLIP) + val GRADIENT_RIGHT = MatterySprite(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/gradient_h.png"), 0f, 0f, 256f, 32f, 256f, 32f) + val GRADIENT_LEFT = GRADIENT_RIGHT.copy(winding = UVWindingOrder.FLOP) + + val LINE_VERTICAL = WidgetLocation.MISC.sprite(36f, 0f, 3f, 3f) + val LINE_HORIZONTAL = WidgetLocation.MISC.sprite(36f, 3f, 3f, 3f) + val LINE_CROSS = WidgetLocation.MISC.sprite(36f, 6f, 3f, 3f) + } +} + +abstract class AbstractColorWangPanel( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + width: Float = 40f, + height: Float = 10f, + protected val callback: Consumer? = null, +) : EditablePanel(screen, parent, x, y, width, height) { + abstract val leftColor: RGBAColor + abstract val rightColor: RGBAColor + abstract val wangPosition: Float + abstract fun setColor(color: Either) + protected abstract fun onWangInput(newPosition: Float) + + init { + scissor = true + } + + var isPressed = false + private set + + protected fun updateColor(mouseX: Double) { + onWangInput((screenToLocal(mouseX, 0.0).x / width).coerceIn(0f, 1f)) + } + + override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { + if (button == InputConstants.MOUSE_BUTTON_LEFT) { + if (!isPressed) { + isPressed = true + grabMouseInput = true + + updateColor(x) + playGuiClickSound() + } + + return true + } + + return super.mouseClickedInner(x, y, button) + } + + override fun mouseDraggedInner(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean { + if (isPressed && button == InputConstants.MOUSE_BUTTON_LEFT) { + updateColor(x) + return true + } + + return super.mouseDraggedInner(x, y, button, xDelta, yDelta) + } + + override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean { + if (button == InputConstants.MOUSE_BUTTON_LEFT) { + if (isPressed) { + isPressed = false + grabMouseInput = false + } + + return true + } + + return super.mouseReleasedInner(x, y, button) + } + + protected fun renderGradients(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + ColorBoxPanel.GRADIENT_RIGHT.render(graphics, 0f, 0f, width, height, color = rightColor) + ColorBoxPanel.GRADIENT_LEFT.render(graphics, 0f, 0f, width, height, color = leftColor) + } + + protected fun renderWang(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + if (wangPosition in 0f .. 1f) { + ColorBoxPanel.LINE_VERTICAL.render(graphics, x = wangPosition * width - 1f, height = height) + } + } + + override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + renderGradients(graphics, mouseX, mouseY, partialTick) + renderWang(graphics, mouseX, mouseY, partialTick) + } +} + +open class RedColorWangPanel( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + width: Float = 40f, + height: Float = 10f, + callback: Consumer? = null, +) : AbstractColorWangPanel(screen, parent, x, y, width, height, callback) { + override var leftColor: RGBAColor = RGBAColor.BLACK + protected set + override var rightColor: RGBAColor = RGBAColor.RED + protected set + override var wangPosition: Float = 0f + protected set + + override fun onWangInput(newPosition: Float) { + val color = leftColor.copy(red = newPosition) + wangPosition = newPosition + callback?.accept(color) + } + + override fun setColor(color: Either) { + @Suppress("name_shadowing") + val color = color.map({ it }, { it.toRGBA() }) + leftColor = color.copy(red = 0f, alpha = 1f) + rightColor = color.copy(red = 1f, alpha = 1f) + wangPosition = color.red + } +} + +open class GreenColorWangPanel( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + width: Float = 40f, + height: Float = 10f, + callback: Consumer? = null, +) : AbstractColorWangPanel(screen, parent, x, y, width, height, callback) { + override var leftColor: RGBAColor = RGBAColor.BLACK + protected set + override var rightColor: RGBAColor = RGBAColor.GREEN + protected set + override var wangPosition: Float = 0f + protected set + + override fun onWangInput(newPosition: Float) { + val color = leftColor.copy(green = newPosition) + wangPosition = newPosition + callback?.accept(color) + } + + override fun setColor(color: Either) { + @Suppress("name_shadowing") + val color = color.map({ it }, { it.toRGBA() }) + leftColor = color.copy(green = 0f, alpha = 1f) + rightColor = color.copy(green = 1f, alpha = 1f) + wangPosition = color.green + } +} + +open class BlueColorWangPanel( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + width: Float = 40f, + height: Float = 10f, + callback: Consumer? = null, +) : AbstractColorWangPanel(screen, parent, x, y, width, height, callback) { + override var leftColor: RGBAColor = RGBAColor.BLACK + protected set + override var rightColor: RGBAColor = RGBAColor.BLUE + protected set + override var wangPosition: Float = 0f + protected set + + override fun onWangInput(newPosition: Float) { + val color = leftColor.copy(blue = newPosition) + wangPosition = newPosition + callback?.accept(color) + } + + override fun setColor(color: Either) { + @Suppress("name_shadowing") + val color = color.map({ it }, { it.toRGBA() }) + leftColor = color.copy(blue = 0f, alpha = 1f) + rightColor = color.copy(blue = 1f, alpha = 1f) + wangPosition = color.blue + } +} + +open class HueWangPanel( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + width: Float = 40f, + height: Float = 10f, + protected val hueCallback: FloatConsumer? = null, +) : AbstractColorWangPanel(screen, parent, x, y, width, height) { + override val leftColor: RGBAColor get() = RGBAColor.WHITE + override val rightColor: RGBAColor get() = RGBAColor.WHITE + override var wangPosition: Float = 1f + protected set + + override fun setColor(color: Either) { + color.map( + { + if (it.canRepresentHue()) { + wangPosition = it.toHSV().hue / 360f + } + }, + { + wangPosition = it.hue / 360f + } + ) + } + + override fun onWangInput(newPosition: Float) { + wangPosition = newPosition + hueCallback?.accept(newPosition * 360f) + } + + override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + HSV_BAR.render(graphics, 0f, 0f, width, height) + renderWang(graphics, mouseX, mouseY, partialTick) + } + + companion object { + val HSV_BAR = MatterySprite(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/hsv.png"), 0f, 0f, 256f, 16f, 256f, 16f) + } +} + +open class SaturationWangPanel( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + width: Float = 40f, + height: Float = 10f, + protected val saturationCallback: FloatConsumer? = null, +) : AbstractColorWangPanel(screen, parent, x, y, width, height) { + override var leftColor: RGBAColor = RGBAColor.WHITE + protected set + override var rightColor: RGBAColor = RGBAColor.WHITE + protected set + override var wangPosition: Float = 1f + protected set + + override fun onWangInput(newPosition: Float) { + wangPosition = newPosition + saturationCallback?.accept(newPosition) + } + + override fun setColor(color: Either) { + color.map( + { + val hsv = it.toHSV() + + if (it.canRepresentHue()) { + leftColor = hsv.copy(saturation = 0f).toRGBA() + rightColor = hsv.copy(saturation = 1f).toRGBA() + } else { + leftColor = hsv.copy(hue = leftColor.toHSV().hue, saturation = 0f).toRGBA() + rightColor = hsv.copy(hue = rightColor.toHSV().hue, saturation = 1f).toRGBA() + } + + wangPosition = hsv.saturation + }, + { hsv -> + leftColor = hsv.copy(saturation = 0f).toRGBA() + rightColor = hsv.copy(saturation = 1f).toRGBA() + wangPosition = hsv.saturation + } + ) + } +} + +open class ValueWangPanel( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + width: Float = 40f, + height: Float = 10f, + protected val valueCallback: FloatConsumer? = null, +) : AbstractColorWangPanel(screen, parent, x, y, width, height) { + override var leftColor: RGBAColor = RGBAColor.BLACK + protected set + override var rightColor: RGBAColor = RGBAColor.WHITE + protected set + override var wangPosition: Float = 1f + protected set + + override fun onWangInput(newPosition: Float) { + wangPosition = newPosition + valueCallback?.accept(newPosition) + } + + override fun setColor(color: Either) { + color.map( + { + val hsv = it.toHSV() + + if (it.canRepresentHue()) { + leftColor = hsv.copy(value = 0f).toRGBA() + rightColor = hsv.copy(value = 1f).toRGBA() + } else { + leftColor = hsv.copy(hue = leftColor.toHSV().hue, value = 0f).toRGBA() + rightColor = hsv.copy(hue = rightColor.toHSV().hue, value = 1f).toRGBA() + } + + wangPosition = hsv.value + }, + { hsv -> + leftColor = hsv.copy(value = 0f).toRGBA() + rightColor = hsv.copy(value = 1f).toRGBA() + wangPosition = hsv.value + } + ) + } +} + +open class ColorPalettePanel( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + width: Float = 64f, + height: Float = 64f, + protected val callback: Consumer? = null +) : EditablePanel(screen, parent, x, y, width, height) { + open fun onColorChoose(color: RGBAColor) { + callback?.accept(color) + } + + inner class Button(val color: RGBAColor) : AbstractButtonPanel(screen, this@ColorPalettePanel, 0f, 0f, 8f, 8f) { + init { + tooltips.add(TextComponent("${color.redInt}, ${color.greenInt}, ${color.blueInt}")) + tooltips.add(TextComponent(color.toHexStringRGB())) + } + + override fun onClick(mouseButton: Int) { + onColorChoose(color) + } + + override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + graphics.renderRect(0f, 0f, width, height, color = RGBAColor.BLACK) + graphics.renderRect(1f, 1f, width - 2f, height - 2f, color = color) + } + + override fun compareTo(other: EditablePanel<*>): Int { + return super.compareTo(other).let { + if (it != 0 || other !is Button) + it + else + color.compareTo(other.color) + } + } + } + + override fun performLayout() { + super.performLayout() + + var x = 0f + var y = 0f + + for (children in visibleChildren) { + if (children is Button) { + if (x != 0f && x + children.width > width) { + y += 9f + x = 0f + } + + children.x = x + children.y = y + x += 9f + } + } + } +} + +open class ColorPickerPanel( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + width: Float = 164f, + height: Float = 118f, + val callback: Consumer? = null +) : EditablePanel(screen, parent, x, y, width, height) { + open fun onColorChanged(color: RGBAColor) { + callback?.accept(color) + } + + var currentColor: Either = Either.left(RGBAColor.WHITE) + protected set + + fun setColor(color: Either) { + currentColor = color + hexInput.text = color.map({ it }, { it.toRGBA() }).toHexStringRGB() + updateWangs(color) + } + + protected fun updateWangs(color: Either) { + box.setColor(color) + + for (wang in wangs) + wang.update(color) + } + + protected open fun onPaletteChoose(color: RGBAColor) { + setColor(Either.left(color)) + onColorChanged(color) + } + + protected open fun onColorBoxChoose(color: HSVColor) { + setColor(Either.right(color)) + onColorChanged(color.toRGBA()) + } + + protected open fun onWangChoose(color: RGBAColor) { + setColor(Either.left(color)) + onColorChanged(color) + } + + protected open fun onHueChoose(hue: Float) { + val current = currentColor.map({ it.toHSV() }, { it }) + val new = current.copy(hue = hue) + setColor(Either.right(new)) + onColorChanged(new.toRGBA()) + } + + protected open fun onSaturationChoose(saturation: Float) { + val current = currentColor.map({ it.toHSV() }, { it }) + val new = current.copy(saturation = saturation) + setColor(Either.right(new)) + onColorChanged(new.toRGBA()) + } + + protected open fun onValueChoose(value: Float) { + val current = currentColor.map({ it.toHSV() }, { it }) + val new = current.copy(value = value) + setColor(Either.right(new)) + onColorChanged(new.toRGBA()) + } + + val topStrip = EditablePanel(screen, this, 0f, 0f, width = width, height = 70f) + val middleStrip = EditablePanel(screen, this) + val palette = ColorPalettePanel(screen, this, callback = { onPaletteChoose(it) }) + + val hexInput = object : TextInputPanel(screen, middleStrip, width = 50f) { + init { + dock = Dock.RIGHT + } + + override fun onFocusChanged() { + if (!isFocusedThis) { + val newColor = RGBAColor.fromHexStringRGB(text) + + if (newColor == null) { + text = currentColor.map({ it }, { it.toRGBA() }).toHexStringRGB() + } else { + setColor(Either.left(newColor)) + onColorChanged(newColor) + } + } + } + + override fun acceptsCharacter(codepoint: Char, mods: Int): Boolean { + return RGBAColor.isHexCharacter(codepoint) + } + } + + init { + topStrip.dock = Dock.TOP + middleStrip.dock = Dock.TOP + palette.dock = Dock.FILL + palette.dockTop = 2f + middleStrip.dockTop = 2f + + for (color in paletteColors) { + palette.Button(color) + } + } + + val box = ColorBoxPanel(screen, topStrip, 0f, 0f, width = 70f, height = 70f, callback = { onColorBoxChoose(it) }) + val wangCanvas = EditablePanel(screen, topStrip, width = 80f) + + inner class WangLine(label: String, val wang: AbstractColorWangPanel, val text: (color: Either) -> Component?) { + val canvas = EditablePanel(screen, wangCanvas, height = 10f) + val label = Label(screen, canvas, width = 10f, text = TranslatableComponent("otm.gui.color.short.$label")) + val textLabel = Label(screen, canvas, width = 25f) + + init { + this.wang.parent = canvas + this.wang.width = 45f + + this.label.childrenOrder = 0 + this.wang.childrenOrder = 1 + this.textLabel.childrenOrder = 2 + + this.canvas.dock = Dock.TOP + this.canvas.dockTop = 2f + + this.label.dock = Dock.LEFT + this.label.dockRight = 2f + + this.wang.dock = Dock.LEFT + this.wang.dockRight = 2f + + this.textLabel.dock = Dock.LEFT + this.textLabel.align = RenderGravity.CENTER_LEFT + + this.label.tooltips.add(TranslatableComponent("otm.gui.color.full.$label")) + this.label.align = RenderGravity.CENTER_RIGHT + } + + fun update(color: Either) { + wang.setColor(color) + + text.invoke(color)?.let { + textLabel.text = it + } + } + } + + val red = WangLine("red", RedColorWangPanel(screen, wangCanvas, callback = { onWangChoose(it) })) { TextComponent((it.map({ it }, { it.toRGBA() }).red * 255f).roundToInt().toString()) } + val green = WangLine("green", GreenColorWangPanel(screen, wangCanvas, callback = { onWangChoose(it) })) { TextComponent((it.map({ it }, { it.toRGBA() }).green * 255f).roundToInt().toString()) } + val blue = WangLine("blue", BlueColorWangPanel(screen, wangCanvas, callback = { onWangChoose(it) })) { TextComponent((it.map({ it }, { it.toRGBA() }).blue * 255f).roundToInt().toString()) } + + val hue = WangLine("hue", HueWangPanel(screen, wangCanvas, hueCallback = { onHueChoose(it) })) { it.map({ if (it.canRepresentHue()) it.toHSV() else null }, { it })?.let { TextComponent(it.hue.roundToInt().toString()) } } + val saturation = WangLine("saturation", SaturationWangPanel(screen, wangCanvas, saturationCallback = { onSaturationChoose(it) })) { it.map({ if (it.canRepresentHue()) it.toHSV() else null }, { it })?.let { TextComponent((it.saturation * 100f).roundToInt().toString() + "%") } } + val value = WangLine("value", ValueWangPanel(screen, wangCanvas, valueCallback = { onValueChoose(it) })) { it.map({ if (it.canRepresentHue()) it.toHSV() else null }, { it })?.let { TextComponent((it.value * 100f).roundToInt().toString() + "%") } } + + val wangs = listOf(red, green, blue, hue, saturation, value) + + init { + box.dock = Dock.LEFT + wangCanvas.dock = Dock.RIGHT + red.canvas.dockTop = 0f + + setColor(Either.right(HSVColor.WHITE)) + } + + companion object { + val paletteColors = listOf( + RGBAColor.rgb(0x9b59b6L), // Amethyst + RGBAColor.rgb(0x010101L), // Black + RGBAColor.rgb(0x0000ffL), // Blue + RGBAColor.rgb(0x8B4513L), // Brown + RGBAColor.rgb(0xe67e22L), // Carrot + RGBAColor.rgb(0xD2691EL), // Chocolate + RGBAColor.rgb(0xecf0f1L), // Clouds + RGBAColor.rgb(0xFF7F50L), // Coral + RGBAColor.rgb(0xDC143CL), // Crimson + RGBAColor.rgb(0x00FFFFL), // Cyan + RGBAColor.rgb(0x008B8BL), // DarkCyan + RGBAColor.rgb(0xBDB76BL), // DarkGold + RGBAColor.rgb(0xB8860BL), // DarkGoldenRod + RGBAColor.rgb(0x006400L), // DarkGreen + RGBAColor.rgb(0x16a085L), // DarkGreen + RGBAColor.rgb(0x8B008BL), // DarkMagenta + RGBAColor.rgb(0x556B2FL), // DarkOlive + RGBAColor.rgb(0xFF8C00L), // DarkOrange + RGBAColor.rgb(0x8B0000L), // DarkRed + RGBAColor.rgb(0xE9967AL), // DarkSalmon + RGBAColor.rgb(0x9400D3L), // DarkViolet + RGBAColor.rgb(0xFF1493L), // DeepPink + RGBAColor.rgb(0x00BFFFL), // DeepSkyBlue + RGBAColor.rgb(0x2ecc71L), // Emerald + RGBAColor.rgb(0xB22222L), // FireBrick + RGBAColor.rgb(0x228B22L), // ForestGreen + RGBAColor.rgb(0xFF00FFL), // Fuchsia + RGBAColor.rgb(0xDCDCDCL), // Gainsboro + RGBAColor.rgb(0xFFD700L), // Gold + RGBAColor.rgb(0xDAA520L), // GoldenRod + RGBAColor.rgb(0x00B000L), // Green + RGBAColor.rgb(0x808080L), // Grey + RGBAColor.rgb(0xFF69B4L), // HotPink + RGBAColor.rgb(0x4B0082L), // Indigo + RGBAColor.rgb(0xF0E68CL), // Khaki + RGBAColor.rgb(0xE6E6FAL), // Lavender + RGBAColor.rgb(0xFF9FF7L), // LavenderRose + RGBAColor.rgb(0xD3D3D3L), // LightGrey + RGBAColor.rgb(0x87CEFAL), // LightSkyBlue + RGBAColor.rgb(0xFFFFE0L), // LightYellow + RGBAColor.rgb(0x00ff00L), // Lime + RGBAColor.rgb(0x32CD32L), // LimeGreen + RGBAColor.rgb(0xBA55D3L), // MediumOrchid + RGBAColor.rgb(0x9370DBL), // MediumPurple + RGBAColor.rgb(0x7B68EEL), // MediumSlateBlue + RGBAColor.rgb(0x808000L), // Olive + RGBAColor.rgb(0x6B8E23L), // OliveGreen + RGBAColor.rgb(0xFFA500L), // Orange + RGBAColor.rgb(0xDA70D6L), // Orchid + RGBAColor.rgb(0xDB7093L), // PaleVioletRed + RGBAColor.rgb(0xCD853FL), // Peru + RGBAColor.rgb(0xFFC0CBL), // Pink + RGBAColor.rgb(0xB0E0E6L), // PowderBlue + RGBAColor.rgb(0xd35400L), // Pumpkin + RGBAColor.rgb(0x800080L), // Purple + RGBAColor.rgb(0xff0000L), // Red + RGBAColor.rgb(0x4169E1L), // RoyalBlue + RGBAColor.rgb(0xFA8072L), // Salmon + RGBAColor.rgb(0xF4A460L), // SandyBrown + RGBAColor.rgb(0x2E8B57L), // SeaGreen + RGBAColor.rgb(0xA0522DL), // Sienna + RGBAColor.rgb(0xbdc3c7L), // Silver + RGBAColor.rgb(0x87CEEBL), // SkyBlue + RGBAColor.rgb(0x708090L), // SlateGrey + RGBAColor.rgb(0x4682B4L), // SteelBlue + RGBAColor.rgb(0xf1c40fL), // SunFlower + RGBAColor.rgb(0x008080L), // Teal + RGBAColor.rgb(0xD8BFD8L), // Thistle + RGBAColor.rgb(0xEE82EEL), // Violet + ) + + fun > frame(screen: S, callback: Consumer, color: RGBAColor = RGBAColor.RED, title: Component? = TranslatableComponent("otm.gui.color_picker")): FramePanel { + return FramePanel.padded(screen, 164f, 118f, title).also { + ColorPickerPanel(screen, it, 0f, 0f, callback = callback).also { + it.dock = Dock.FILL + it.setColor(Either.left(color)) + } + + screen.addPanel(it) + it.toScreenCenter() + it.behaveAsWindow() + it.popup() + } + } + } +} diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/gradient_h.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/gradient_h.png new file mode 100644 index 000000000..2a39ad8d4 Binary files /dev/null and b/src/main/resources/assets/overdrive_that_matters/textures/gui/gradient_h.png differ diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/gradient_v.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/gradient_v.png new file mode 100644 index 000000000..cb1905189 Binary files /dev/null and b/src/main/resources/assets/overdrive_that_matters/textures/gui/gradient_v.png differ diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/hsv.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/hsv.png new file mode 100644 index 000000000..0c21edb22 Binary files /dev/null and b/src/main/resources/assets/overdrive_that_matters/textures/gui/hsv.png differ diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/misc.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/misc.png index 607714384..be39738d5 100644 Binary files a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/misc.png and b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/misc.png differ