Split buttons panel file

This commit is contained in:
DBotThePony 2022-10-21 22:45:56 +07:00
parent a53438e1f9
commit b2181819ca
Signed by: DBot
GPG Key ID: DCC23B5715498507
17 changed files with 619 additions and 556 deletions

View File

@ -18,7 +18,7 @@ import ru.dbotthepony.mc.otm.client.screen.ExoSuitInventoryScreen
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.DiscreteScrollBarPanel import ru.dbotthepony.mc.otm.client.screen.panels.DiscreteScrollBarPanel
import ru.dbotthepony.mc.otm.client.screen.panels.LargeRectangleButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.buttons.LargeRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.Panel2Widget import ru.dbotthepony.mc.otm.client.screen.panels.Panel2Widget
import ru.dbotthepony.mc.otm.compat.InventoryScrollPacket import ru.dbotthepony.mc.otm.compat.InventoryScrollPacket
import ru.dbotthepony.mc.otm.compat.cos.isCosmeticArmorScreen import ru.dbotthepony.mc.otm.compat.cos.isCosmeticArmorScreen

View File

@ -1,25 +1,21 @@
package ru.dbotthepony.mc.otm.client.screen package ru.dbotthepony.mc.otm.client.screen
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.inventory.InventoryScreen import net.minecraft.client.gui.screens.inventory.InventoryScreen
import ru.dbotthepony.mc.otm.client.mousePos import ru.dbotthepony.mc.otm.client.mousePos
import ru.dbotthepony.mc.otm.client.moveMousePos
import ru.dbotthepony.mc.otm.client.moveMousePosScaled import ru.dbotthepony.mc.otm.client.moveMousePosScaled
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.client.render.element import ru.dbotthepony.mc.otm.client.render.element
import ru.dbotthepony.mc.otm.client.screen.panels.* import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.buttons.LargeRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.setMousePos import ru.dbotthepony.mc.otm.client.setMousePos
import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton
import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
import ru.dbotthepony.mc.otm.core.maxScrollDivision import ru.dbotthepony.mc.otm.core.maxScrollDivision
import ru.dbotthepony.mc.otm.menu.ExoSuitInventoryMenu import ru.dbotthepony.mc.otm.menu.ExoSuitInventoryMenu
import ru.dbotthepony.mc.otm.network.ExoSuitMenuOpen import ru.dbotthepony.mc.otm.network.ExoSuitMenuOpen
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
import kotlin.math.cos
@MouseTweaksDisableWheelTweak @MouseTweaksDisableWheelTweak
class ExoSuitInventoryScreen(menu: ExoSuitInventoryMenu) : MatteryScreen<ExoSuitInventoryMenu>(menu, TranslatableComponent("otm.gui.exosuit")) { class ExoSuitInventoryScreen(menu: ExoSuitInventoryMenu) : MatteryScreen<ExoSuitInventoryMenu>(menu, TranslatableComponent("otm.gui.exosuit")) {

View File

@ -13,6 +13,7 @@ import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorPlayerSettings
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.Widgets8 import ru.dbotthepony.mc.otm.client.render.Widgets8
import ru.dbotthepony.mc.otm.client.screen.panels.* import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.buttons.SmallEnumRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel
import ru.dbotthepony.mc.otm.core.asGetterSetter import ru.dbotthepony.mc.otm.core.asGetterSetter

View File

@ -1,546 +0,0 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.RGBAColor
import ru.dbotthepony.mc.otm.core.next
import ru.dbotthepony.mc.otm.core.prev
import ru.dbotthepony.mc.otm.core.value
import java.util.*
import kotlin.collections.ArrayList
import kotlin.reflect.KMutableProperty0
open class ButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = 40f,
height: Float = HEIGHT,
var label: Component
) : EditablePanel<S>(screen, parent, x, y, width, height) {
constructor(screen: S, parent: EditablePanel<*>?, label: Component) : this(screen, parent, x = 0f, label = label)
constructor(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = 40f,
height: Float = HEIGHT,
label: Component,
onPress: Runnable
) : this(screen, parent, x, y, width, height, label) {
this.callback = onPress
}
protected var callback: Runnable? = null
protected var pressed = false
protected open fun onClick(button: Int) {
callback?.run()
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
if (isDisabled || pressed) {
return true
}
if (!tryToGrabMouseInput()) {
return true
}
pressed = true
playGuiClickSound()
return true
}
override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
if (!pressed) {
return true
}
grabMouseInput = false
pressed = false
if (isHovered) {
onClick(button)
}
return true
}
fun bind(runnable: Runnable) {
callback = runnable
}
var textColor = RGBAColor.WHITE
var isDisabled = false
set(value) {
if (field != value) {
if (!value) {
pressed = false
grabMouseInput = false
}
field = value
}
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
if (isDisabled) {
Widgets18.BUTTON_DISABLED_STRETCHABLE.render(stack, width = width, height = height)
} else if (pressed) {
Widgets18.BUTTON_PRESSED_STRETCHABLE.render(stack, width = width, height = height)
} else if (isHovered) {
Widgets18.BUTTON_HOVERED_STRETCHABLE.render(stack, width = width, height = height)
} else {
Widgets18.BUTTON_IDLE_STRETCHABLE.render(stack, width = width, height = height)
}
font.drawAligned(stack, label, TextAlign.CENTER_CENTER, width / 2f, height / 2f, textColor.toInt())
}
override fun sizeToContents() {
super.sizeToContents()
height = height.coerceAtLeast(HEIGHT).coerceAtLeast(font.lineHeight.toFloat() + 2f)
width = width.coerceAtLeast(HEIGHT).coerceAtLeast(font.width(label) + 4f)
}
companion object {
const val HEIGHT = 18f
}
}
@Suppress("PropertyName")
abstract class RectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float,
height: Float,
val onPress: ((clickButton: Int) -> Unit)? = null,
) : EditablePanel<S>(screen, parent, x, y, width, height) {
protected var pressed = false
protected open fun onClick(clickButton: Int) {
onPress?.invoke(clickButton)
}
abstract val PRESSED: AbstractSkinElement
abstract val HOVERED: AbstractSkinElement
abstract val IDLE: AbstractSkinElement
abstract val DISABLED: AbstractSkinElement
var isDisabled = false
set(value) {
if (field != value) {
if (!value) {
pressed = false
grabMouseInput = false
}
field = value
}
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
if (isDisabled) {
DISABLED.render(stack, 0f, 0f, width, height)
} else if (pressed) {
PRESSED.render(stack, 0f, 0f, width, height)
} else if (isHovered) {
HOVERED.render(stack, 0f, 0f, width, height)
} else {
IDLE.render(stack, 0f, 0f, width, height)
}
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
if (!isDisabled) {
if (!pressed) {
playGuiClickSound()
}
if (tryToGrabMouseInput()) {
pressed = true
}
}
return true
}
override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
if (!isDisabled && pressed) {
pressed = false
if (isHovered) {
onClick(button)
}
}
grabMouseInput = false
return true
}
}
abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float,
height: Float,
val enum: Class<T>,
val prop: GetterSetter<T>,
val defaultValue: T,
val onChange: ((newValue: T) -> Unit)? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
private var building = true
protected val enumMapping = EnumMap<T, Pair<AbstractSkinElement, UVWindingOrder>>(enum)
protected val tooltipMapping = EnumMap<T, Component>(enum)
fun addTooltip(value: T, component: Component): EnumRectangleButtonPanel<S, T> {
check(tooltipMapping.put(value, component) == null) { "Already has mapping for $value" }
return this
}
var mainTooltip: Component? = null
set(value) {
if (field != null) {
throw UnsupportedOperationException("Write once only")
}
field = value
}
fun isFullyDefined(): Boolean {
for (value in enum.enumConstants) {
if (enumMapping[value] == null) {
return false
}
}
return true
}
val missingValues: List<T> get() {
val missing = ArrayList<T>()
for (value in enum.enumConstants) {
if (enumMapping[value] == null) {
missing.add(value)
}
}
return missing
}
fun add(value: T, skinElement: AbstractSkinElement, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel<S, T> {
return add(value, skinElement to winding)
}
fun add(value: T, skinElement: AbstractSkinElement, component: Component, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel<S, T> {
return add(value, component, skinElement to winding)
}
fun add(value: T, component: Component, skinElement: AbstractSkinElement, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel<S, T> {
return add(value, component, skinElement to winding)
}
fun add(value: T, pair: Pair<AbstractSkinElement, UVWindingOrder>): EnumRectangleButtonPanel<S, T> {
check(building) { "Not building" }
check(enumMapping.put(value, pair) == null) { "Already has mapping for $value" }
if (enumMapping.size == enum.enumConstants.size) {
finish()
}
return this
}
fun add(value: T, component: Component, pair: Pair<AbstractSkinElement, UVWindingOrder>): EnumRectangleButtonPanel<S, T> {
addTooltip(value, component)
return add(value, pair)
}
fun finish(): EnumRectangleButtonPanel<S, T> {
check(building) { "Not building" }
check(isFullyDefined()) {
"Not all enums having their mapping defined, missing are: ${missingValues.joinToString(", ")}"
}
building = false
return this
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
check(!building) { "Still building this button!" }
super.innerRender(stack, mouseX, mouseY, partialTick)
val pair = checkNotNull(enumMapping[prop.get()]) { "HOW" }
pair.first.render(stack, 0f, 0f, width, height, pair.second)
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
check(!building) { "Still building this button!" }
return super.mouseClickedInner(x, y, button)
}
override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
check(!building) { "Still building this button!" }
return super.mouseReleasedInner(x, y, button)
}
override fun onClick(clickButton: Int) {
when (clickButton) {
InputConstants.MOUSE_BUTTON_LEFT -> {
prop.value = prop.value.next(enum.enumConstants)
onChange?.invoke(prop.get())
}
InputConstants.MOUSE_BUTTON_RIGHT -> {
prop.value = prop.value.prev(enum.enumConstants)
onChange?.invoke(prop.get())
}
InputConstants.MOUSE_BUTTON_MIDDLE -> {
if (prop.get() != defaultValue) {
prop.value = defaultValue
onChange?.invoke(prop.get())
}
}
}
}
override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (!isHovered && !isGrabbingMouseInput()) {
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
}
if (mainTooltip == null && tooltipMapping.size == 0) {
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
}
val listing = ArrayList<Component>()
if (mainTooltip != null) {
listing.add(mainTooltip!!)
listing.add(SPACE)
}
for ((key, value) in tooltipMapping) {
if (key == prop.get()) {
listing.add(value.copy().withStyle(ChatFormatting.WHITE))
} else {
listing.add(value.copy().withStyle(ChatFormatting.GRAY))
}
}
screen.renderComponentTooltip(
stack,
listing,
mouseX.toInt(),
mouseY.toInt(),
font
)
return true
}
companion object {
private val SPACE = TextComponent("")
}
}
abstract class BooleanRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float,
height: Float,
val prop: GetterSetter<Boolean>,
val skinElementActive: AbstractSkinElement? = null,
val skinElementInactive: AbstractSkinElement? = null,
val onChange: ((newValue: Boolean) -> Unit)? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
override fun onClick(clickButton: Int) {
if (clickButton == InputConstants.MOUSE_BUTTON_LEFT) {
val newValue = !prop.value
prop.value = newValue
onChange?.invoke(newValue)
}
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
super.innerRender(stack, mouseX, mouseY, partialTick)
if (prop.value) {
skinElementActive?.render(stack, width = width, height = height)
} else {
skinElementInactive?.render(stack, width = width, height = height)
}
}
}
open class LargeRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
onPress: ((clickButton: Int) -> Unit)? = null,
val skinElement: AbstractSkinElement? = null,
val skinElementWinding: UVWindingOrder? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, onPress) {
final override val IDLE = Widgets18.BUTTON_IDLE
final override val HOVERED = Widgets18.BUTTON_HOVERED
final override val PRESSED = Widgets18.BUTTON_PRESSED
final override val DISABLED = Widgets18.BUTTON_DISABLED
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
super.innerRender(stack, mouseX, mouseY, partialTick)
if (skinElementWinding != null) {
skinElement?.render(stack, width = width, height = height, winding = skinElementWinding)
} else {
skinElement?.render(stack, width = width, height = height)
}
}
companion object {
const val SIZE = 18f
}
}
open class LargeEnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
enum: Class<T>,
prop: GetterSetter<T>,
defaultValue: T,
onChange: ((newValue: T) -> Unit)? = null,
) : EnumRectangleButtonPanel<S, T>(screen, parent, x, y, width, height, enum, prop, defaultValue, onChange) {
final override val IDLE = Widgets18.BUTTON_IDLE
final override val HOVERED = Widgets18.BUTTON_HOVERED
final override val PRESSED = Widgets18.BUTTON_PRESSED
final override val DISABLED = Widgets18.BUTTON_DISABLED
companion object {
const val SIZE = 18f
}
}
open class LargeBooleanRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
prop: GetterSetter<Boolean>,
skinElementActive: AbstractSkinElement? = null,
skinElementInactive: AbstractSkinElement? = null,
onChange: ((newValue: Boolean) -> Unit)? = null,
) : BooleanRectangleButtonPanel<S>(screen, parent, x, y, width, height, prop, skinElementActive, skinElementInactive, onChange) {
final override val IDLE = Widgets18.BUTTON_IDLE
final override val HOVERED = Widgets18.BUTTON_HOVERED
final override val PRESSED = Widgets18.BUTTON_PRESSED
final override val DISABLED = Widgets18.BUTTON_DISABLED
companion object {
const val SIZE = 18f
}
}
open class SmallRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
onPress: ((clickButton: Int) -> Unit)? = null,
val skinElement: SkinElement? = null,
val skinElementWinding: UVWindingOrder? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, onPress) {
final override val IDLE = Widgets8.BUTTON_IDLE
final override val HOVERED = Widgets8.BUTTON_HOVERED
final override val PRESSED = Widgets8.BUTTON_PRESSED
final override val DISABLED = Widgets8.BUTTON_DISABLED
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
super.innerRender(stack, mouseX, mouseY, partialTick)
if (skinElementWinding != null) {
skinElement?.render(stack, width = width, height = height, winding = skinElementWinding)
} else {
skinElement?.render(stack, width = width, height = height)
}
}
companion object {
const val SIZE = 8f
}
}
open class SmallEnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
enum: Class<T>,
prop: GetterSetter<T>,
defaultValue: T,
onChange: ((newValue: T) -> Unit)? = null,
) : EnumRectangleButtonPanel<S, T>(screen, parent, x, y, width, height, enum, prop, defaultValue, onChange) {
final override val IDLE = Widgets8.BUTTON_IDLE
final override val HOVERED = Widgets8.BUTTON_HOVERED
final override val PRESSED = Widgets8.BUTTON_PRESSED
final override val DISABLED = Widgets8.BUTTON_DISABLED
companion object {
const val SIZE = 8f
}
}
open class SmallBooleanRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
prop: GetterSetter<Boolean>,
skinElementActive: AbstractSkinElement? = null,
skinElementInactive: AbstractSkinElement? = null,
onChange: ((newValue: Boolean) -> Unit)? = null,
) : BooleanRectangleButtonPanel<S>(screen, parent, x, y, width, height, prop, skinElementActive, skinElementInactive, onChange) {
final override val IDLE = Widgets8.BUTTON_IDLE
final override val HOVERED = Widgets8.BUTTON_HOVERED
final override val PRESSED = Widgets8.BUTTON_PRESSED
final override val DISABLED = Widgets8.BUTTON_DISABLED
companion object {
const val SIZE = 8f
}
}

View File

@ -10,6 +10,7 @@ import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.render.Widgets8 import ru.dbotthepony.mc.otm.client.render.Widgets8
import ru.dbotthepony.mc.otm.client.render.element import ru.dbotthepony.mc.otm.client.render.element
import ru.dbotthepony.mc.otm.client.screen.ExoSuitInventoryScreen import ru.dbotthepony.mc.otm.client.screen.ExoSuitInventoryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.buttons.SmallBooleanRectangleButtonPanel
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleRenderButton import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleRenderButton
import ru.dbotthepony.mc.otm.compat.cos.isCosmeticArmorLoaded import ru.dbotthepony.mc.otm.compat.cos.isCosmeticArmorLoaded
import ru.dbotthepony.mc.otm.core.asGetterOnly import ru.dbotthepony.mc.otm.core.asGetterOnly

View File

@ -3,8 +3,8 @@ package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.platform.InputConstants import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.AbstractSkinElement import ru.dbotthepony.mc.otm.client.render.AbstractSkinElement
import ru.dbotthepony.mc.otm.client.render.SkinElement
import ru.dbotthepony.mc.otm.client.render.Widgets import ru.dbotthepony.mc.otm.client.render.Widgets
import ru.dbotthepony.mc.otm.client.screen.panels.buttons.RectangleButtonPanel
open class HeightControls<out S : Screen>( open class HeightControls<out S : Screen>(
screen: S, screen: S,

View File

@ -0,0 +1,40 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.AbstractSkinElement
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.value
abstract class BooleanRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float,
height: Float,
val prop: GetterSetter<Boolean>,
val skinElementActive: AbstractSkinElement? = null,
val skinElementInactive: AbstractSkinElement? = null,
val onChange: ((newValue: Boolean) -> Unit)? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
override fun onClick(clickButton: Int) {
if (clickButton == InputConstants.MOUSE_BUTTON_LEFT) {
val newValue = !prop.value
prop.value = newValue
onChange?.invoke(newValue)
}
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
super.innerRender(stack, mouseX, mouseY, partialTick)
if (prop.value) {
skinElementActive?.render(stack, width = width, height = height)
} else {
skinElementInactive?.render(stack, width = width, height = height)
}
}
}

View File

@ -0,0 +1,115 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.render.TextAlign
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.render.drawAligned
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.RGBAColor
open class ButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = 40f,
height: Float = HEIGHT,
var label: Component
) : EditablePanel<S>(screen, parent, x, y, width, height) {
constructor(screen: S, parent: EditablePanel<*>?, label: Component) : this(screen, parent, x = 0f, label = label)
constructor(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = 40f,
height: Float = HEIGHT,
label: Component,
onPress: Runnable
) : this(screen, parent, x, y, width, height, label) {
this.callback = onPress
}
protected var callback: Runnable? = null
protected var pressed = false
protected open fun onClick(button: Int) {
callback?.run()
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
if (isDisabled || pressed) {
return true
}
if (!tryToGrabMouseInput()) {
return true
}
pressed = true
playGuiClickSound()
return true
}
override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
if (!pressed) {
return true
}
grabMouseInput = false
pressed = false
if (isHovered) {
onClick(button)
}
return true
}
fun bind(runnable: Runnable) {
callback = runnable
}
var textColor = RGBAColor.WHITE
var isDisabled = false
set(value) {
if (field != value) {
if (!value) {
pressed = false
grabMouseInput = false
}
field = value
}
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
if (isDisabled) {
Widgets18.BUTTON_DISABLED_STRETCHABLE.render(stack, width = width, height = height)
} else if (pressed) {
Widgets18.BUTTON_PRESSED_STRETCHABLE.render(stack, width = width, height = height)
} else if (isHovered) {
Widgets18.BUTTON_HOVERED_STRETCHABLE.render(stack, width = width, height = height)
} else {
Widgets18.BUTTON_IDLE_STRETCHABLE.render(stack, width = width, height = height)
}
font.drawAligned(stack, label, TextAlign.CENTER_CENTER, width / 2f, height / 2f, textColor.toInt())
}
override fun sizeToContents() {
super.sizeToContents()
height = height.coerceAtLeast(HEIGHT).coerceAtLeast(font.lineHeight.toFloat() + 2f)
width = width.coerceAtLeast(HEIGHT).coerceAtLeast(font.width(label) + 4f)
}
companion object {
const val HEIGHT = 18f
}
}

View File

@ -0,0 +1,186 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.render.AbstractSkinElement
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.next
import ru.dbotthepony.mc.otm.core.prev
import ru.dbotthepony.mc.otm.core.value
import java.util.*
import kotlin.collections.ArrayList
abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float,
height: Float,
val enum: Class<T>,
val prop: GetterSetter<T>,
val defaultValue: T,
val onChange: ((newValue: T) -> Unit)? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
private var building = true
protected val enumMapping = EnumMap<T, Pair<AbstractSkinElement, UVWindingOrder>>(enum)
protected val tooltipMapping = EnumMap<T, Component>(enum)
fun addTooltip(value: T, component: Component): EnumRectangleButtonPanel<S, T> {
check(tooltipMapping.put(value, component) == null) { "Already has mapping for $value" }
return this
}
var mainTooltip: Component? = null
set(value) {
if (field != null) {
throw UnsupportedOperationException("Write once only")
}
field = value
}
fun isFullyDefined(): Boolean {
for (value in enum.enumConstants) {
if (enumMapping[value] == null) {
return false
}
}
return true
}
val missingValues: List<T> get() {
val missing = ArrayList<T>()
for (value in enum.enumConstants) {
if (enumMapping[value] == null) {
missing.add(value)
}
}
return missing
}
fun add(value: T, skinElement: AbstractSkinElement, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel<S, T> {
return add(value, skinElement to winding)
}
fun add(value: T, skinElement: AbstractSkinElement, component: Component, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel<S, T> {
return add(value, component, skinElement to winding)
}
fun add(value: T, component: Component, skinElement: AbstractSkinElement, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel<S, T> {
return add(value, component, skinElement to winding)
}
fun add(value: T, pair: Pair<AbstractSkinElement, UVWindingOrder>): EnumRectangleButtonPanel<S, T> {
check(building) { "Not building" }
check(enumMapping.put(value, pair) == null) { "Already has mapping for $value" }
if (enumMapping.size == enum.enumConstants.size) {
finish()
}
return this
}
fun add(value: T, component: Component, pair: Pair<AbstractSkinElement, UVWindingOrder>): EnumRectangleButtonPanel<S, T> {
addTooltip(value, component)
return add(value, pair)
}
fun finish(): EnumRectangleButtonPanel<S, T> {
check(building) { "Not building" }
check(isFullyDefined()) {
"Not all enums having their mapping defined, missing are: ${missingValues.joinToString(", ")}"
}
building = false
return this
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
check(!building) { "Still building this button!" }
super.innerRender(stack, mouseX, mouseY, partialTick)
val pair = checkNotNull(enumMapping[prop.get()]) { "HOW" }
pair.first.render(stack, 0f, 0f, width, height, pair.second)
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
check(!building) { "Still building this button!" }
return super.mouseClickedInner(x, y, button)
}
override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
check(!building) { "Still building this button!" }
return super.mouseReleasedInner(x, y, button)
}
override fun onClick(clickButton: Int) {
when (clickButton) {
InputConstants.MOUSE_BUTTON_LEFT -> {
prop.value = prop.value.next(enum.enumConstants)
onChange?.invoke(prop.get())
}
InputConstants.MOUSE_BUTTON_RIGHT -> {
prop.value = prop.value.prev(enum.enumConstants)
onChange?.invoke(prop.get())
}
InputConstants.MOUSE_BUTTON_MIDDLE -> {
if (prop.get() != defaultValue) {
prop.value = defaultValue
onChange?.invoke(prop.get())
}
}
}
}
override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (!isHovered && !isGrabbingMouseInput()) {
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
}
if (mainTooltip == null && tooltipMapping.size == 0) {
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
}
val listing = ArrayList<Component>()
if (mainTooltip != null) {
listing.add(mainTooltip!!)
listing.add(SPACE)
}
for ((key, value) in tooltipMapping) {
if (key == prop.get()) {
listing.add(value.copy().withStyle(ChatFormatting.WHITE))
} else {
listing.add(value.copy().withStyle(ChatFormatting.GRAY))
}
}
screen.renderComponentTooltip(
stack,
listing,
mouseX.toInt(),
mouseY.toInt(),
font
)
return true
}
companion object {
private val SPACE = TextComponent("")
}
}

View File

@ -0,0 +1,29 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.AbstractSkinElement
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.GetterSetter
open class LargeBooleanRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
prop: GetterSetter<Boolean>,
skinElementActive: AbstractSkinElement? = null,
skinElementInactive: AbstractSkinElement? = null,
onChange: ((newValue: Boolean) -> Unit)? = null,
) : BooleanRectangleButtonPanel<S>(screen, parent, x, y, width, height, prop, skinElementActive, skinElementInactive, onChange) {
final override val IDLE = Widgets18.BUTTON_IDLE
final override val HOVERED = Widgets18.BUTTON_HOVERED
final override val PRESSED = Widgets18.BUTTON_PRESSED
final override val DISABLED = Widgets18.BUTTON_DISABLED
companion object {
const val SIZE = 18f
}
}

View File

@ -0,0 +1,28 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.GetterSetter
open class LargeEnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
enum: Class<T>,
prop: GetterSetter<T>,
defaultValue: T,
onChange: ((newValue: T) -> Unit)? = null,
) : EnumRectangleButtonPanel<S, T>(screen, parent, x, y, width, height, enum, prop, defaultValue, onChange) {
final override val IDLE = Widgets18.BUTTON_IDLE
final override val HOVERED = Widgets18.BUTTON_HOVERED
final override val PRESSED = Widgets18.BUTTON_PRESSED
final override val DISABLED = Widgets18.BUTTON_DISABLED
companion object {
const val SIZE = 18f
}
}

View File

@ -0,0 +1,39 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.AbstractSkinElement
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
open class LargeRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
onPress: ((clickButton: Int) -> Unit)? = null,
val skinElement: AbstractSkinElement? = null,
val skinElementWinding: UVWindingOrder? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, onPress) {
final override val IDLE = Widgets18.BUTTON_IDLE
final override val HOVERED = Widgets18.BUTTON_HOVERED
final override val PRESSED = Widgets18.BUTTON_PRESSED
final override val DISABLED = Widgets18.BUTTON_DISABLED
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
super.innerRender(stack, mouseX, mouseY, partialTick)
if (skinElementWinding != null) {
skinElement?.render(stack, width = width, height = height, winding = skinElementWinding)
} else {
skinElement?.render(stack, width = width, height = height)
}
}
companion object {
const val SIZE = 18f
}
}

View File

@ -0,0 +1,81 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.render.AbstractSkinElement
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
@Suppress("PropertyName")
abstract class RectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float,
height: Float,
val onPress: ((clickButton: Int) -> Unit)? = null,
) : EditablePanel<S>(screen, parent, x, y, width, height) {
protected var pressed = false
protected open fun onClick(clickButton: Int) {
onPress?.invoke(clickButton)
}
abstract val PRESSED: AbstractSkinElement
abstract val HOVERED: AbstractSkinElement
abstract val IDLE: AbstractSkinElement
abstract val DISABLED: AbstractSkinElement
var isDisabled = false
set(value) {
if (field != value) {
if (!value) {
pressed = false
grabMouseInput = false
}
field = value
}
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
if (isDisabled) {
DISABLED.render(stack, 0f, 0f, width, height)
} else if (pressed) {
PRESSED.render(stack, 0f, 0f, width, height)
} else if (isHovered) {
HOVERED.render(stack, 0f, 0f, width, height)
} else {
IDLE.render(stack, 0f, 0f, width, height)
}
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
if (!isDisabled) {
if (!pressed) {
playGuiClickSound()
}
if (tryToGrabMouseInput()) {
pressed = true
}
}
return true
}
override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
if (!isDisabled && pressed) {
pressed = false
if (isHovered) {
onClick(button)
}
}
grabMouseInput = false
return true
}
}

View File

@ -0,0 +1,28 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.GetterSetter
open class SmallBooleanRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
prop: GetterSetter<Boolean>,
skinElementActive: AbstractSkinElement? = null,
skinElementInactive: AbstractSkinElement? = null,
onChange: ((newValue: Boolean) -> Unit)? = null,
) : BooleanRectangleButtonPanel<S>(screen, parent, x, y, width, height, prop, skinElementActive, skinElementInactive, onChange) {
final override val IDLE = Widgets8.BUTTON_IDLE
final override val HOVERED = Widgets8.BUTTON_HOVERED
final override val PRESSED = Widgets8.BUTTON_PRESSED
final override val DISABLED = Widgets8.BUTTON_DISABLED
companion object {
const val SIZE = 8f
}
}

View File

@ -0,0 +1,28 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.Widgets8
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.GetterSetter
open class SmallEnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
enum: Class<T>,
prop: GetterSetter<T>,
defaultValue: T,
onChange: ((newValue: T) -> Unit)? = null,
) : EnumRectangleButtonPanel<S, T>(screen, parent, x, y, width, height, enum, prop, defaultValue, onChange) {
final override val IDLE = Widgets8.BUTTON_IDLE
final override val HOVERED = Widgets8.BUTTON_HOVERED
final override val PRESSED = Widgets8.BUTTON_PRESSED
final override val DISABLED = Widgets8.BUTTON_DISABLED
companion object {
const val SIZE = 8f
}
}

View File

@ -0,0 +1,39 @@
package ru.dbotthepony.mc.otm.client.screen.panels.buttons
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.SkinElement
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.Widgets8
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
open class SmallRectangleButtonPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
onPress: ((clickButton: Int) -> Unit)? = null,
val skinElement: SkinElement? = null,
val skinElementWinding: UVWindingOrder? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, onPress) {
final override val IDLE = Widgets8.BUTTON_IDLE
final override val HOVERED = Widgets8.BUTTON_HOVERED
final override val PRESSED = Widgets8.BUTTON_PRESSED
final override val DISABLED = Widgets8.BUTTON_DISABLED
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
super.innerRender(stack, mouseX, mouseY, partialTick)
if (skinElementWinding != null) {
skinElement?.render(stack, width = width, height = height, winding = skinElementWinding)
} else {
skinElement?.render(stack, width = width, height = height)
}
}
companion object {
const val SIZE = 8f
}
}

View File

@ -5,7 +5,6 @@ import com.mojang.blaze3d.vertex.PoseStack
import lain.mods.cos.impl.ModObjects import lain.mods.cos.impl.ModObjects
import lain.mods.cos.impl.client.PlayerRenderHandler import lain.mods.cos.impl.client.PlayerRenderHandler
import lain.mods.cos.impl.client.gui.GuiCosArmorInventory import lain.mods.cos.impl.client.gui.GuiCosArmorInventory
import lain.mods.cos.impl.client.gui.GuiCosArmorToggleButton
import lain.mods.cos.impl.network.packet.PacketSetSkinArmor import lain.mods.cos.impl.network.packet.PacketSetSkinArmor
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
@ -20,9 +19,8 @@ import net.minecraftforge.fml.ModList
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.SkinElement import ru.dbotthepony.mc.otm.client.render.SkinElement
import ru.dbotthepony.mc.otm.client.render.element import ru.dbotthepony.mc.otm.client.render.element
import ru.dbotthepony.mc.otm.client.screen.panels.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.RectangleButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.buttons.RectangleButtonPanel
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_ACTIVE import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_ACTIVE
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_INACTIVE import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_INACTIVE
import ru.dbotthepony.mc.otm.container.awareStream import ru.dbotthepony.mc.otm.container.awareStream