refactor UI to use floats everywhere

This commit is contained in:
りき萌 2023-05-04 13:47:58 +02:00
parent 81ada8fd9b
commit 8324bff154
23 changed files with 296 additions and 232 deletions

View file

@ -41,7 +41,7 @@ interface DeviceBlockDescriptor<out CS : DeviceBlockDescriptor.ClientState, out
}
interface UI<out Controls : ControlSet> {
fun open(controls: @UnsafeVariance Controls, x: Int, y: Int): Widget
fun open(controls: @UnsafeVariance Controls, x: Float, y: Float): Widget
}
val ui: UI<Controls>?

View file

@ -13,6 +13,7 @@ import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtHelper
import net.minecraft.nbt.NbtList
import net.minecraft.util.math.BlockPos
import java.util.*
private typealias DeviceBlockFactory = FabricBlockEntityTypeBuilder.Factory<DeviceBlockEntity>
@ -41,6 +42,15 @@ class DeviceBlockEntity(
*/
internal val outputConnections = hashMapOf<OutputPortName, BlockPos>()
/**
* UI shelf the block's control panel is seated on.
*
* This only applies to devices with control panels; other devices always have this set to null.
* For devices with control panels which do have this set to null, this means the device doesn't
* have a shelf assigned and should be placed on the sidebar.
*/
var shelf: UUID? = null
/** NBT compound keys. */
private object Nbt {
const val controls = "controls"
@ -60,6 +70,8 @@ class DeviceBlockEntity(
const val port = "port"
const val block = "block"
}
const val shelf = "shelf"
}
override fun readNbt(nbt: NbtCompound) {
@ -118,6 +130,10 @@ class DeviceBlockEntity(
outputConnections[port] = blockPosition
}
if (nbt.containsUuid(Nbt.shelf)) {
shelf = nbt.getUuid(Nbt.shelf)
}
}
override fun writeNbt(nbt: NbtCompound) {

View file

@ -32,11 +32,11 @@ object AdsrBlockDescriptor : DeviceBlockDescriptor<AdsrBlockDescriptor.ClientSta
ClientState(controls)
override val ui = object : DeviceBlockDescriptor.UI<AdsrDevice.Controls> {
override fun open(controls: AdsrDevice.Controls, x: Int, y: Int) =
Window(x, y, width = 121, height = 48, Text.translatable("block.dawd3.adsr")).apply {
override fun open(controls: AdsrDevice.Controls, x: Float, y: Float) =
Window(x, y, width = 121f, height = 48f, Text.translatable("block.dawd3.adsr")).apply {
children.add(
Knob(
x = 8, y = 18,
x = 8f, y = 18f,
controls.attack,
min = 0f, max = 8f,
Knob.Color.Yellow,
@ -45,7 +45,7 @@ object AdsrBlockDescriptor : DeviceBlockDescriptor<AdsrBlockDescriptor.ClientSta
)
children.add(
Knob(
x = 36, y = 18,
x = 36f, y = 18f,
controls.decay,
min = 0f, max = 8f,
Knob.Color.Orange,
@ -54,7 +54,7 @@ object AdsrBlockDescriptor : DeviceBlockDescriptor<AdsrBlockDescriptor.ClientSta
)
children.add(
Knob(
x = 64, y = 18,
x = 64f, y = 18f,
controls.sustain,
min = 0f, max = 1f,
Knob.Color.Red,
@ -63,7 +63,7 @@ object AdsrBlockDescriptor : DeviceBlockDescriptor<AdsrBlockDescriptor.ClientSta
)
children.add(
Knob(
x = 92, y = 18,
x = 92f, y = 18f,
controls.release,
min = 0f, max = 8f,
Knob.Color.Purple,

View file

@ -52,17 +52,17 @@ class BiquadBlockDescriptor(
ClientState(type, controls)
override val ui = object : DeviceBlockDescriptor.UI<BiquadDevice.Controls> {
override fun open(controls: BiquadDevice.Controls, x: Int, y: Int) =
override fun open(controls: BiquadDevice.Controls, x: Float, y: Float) =
Window(
x,
y,
width = 121,
height = 48,
width = 121f,
height = 48f,
Text.translatable(id.toTranslationKey("block"))
).apply {
children.add(
Knob(
x = 8, y = 18,
x = 8f, y = 18f,
controls.frequency,
min = 1f, max = 25000f,
Knob.Color.Orange,
@ -71,7 +71,7 @@ class BiquadBlockDescriptor(
)
children.add(
Knob(
x = 36, y = 18,
x = 36f, y = 18f,
controls.frequencyCV,
min = -20000f, max = 20000f,
Knob.Color.Orange,
@ -80,7 +80,7 @@ class BiquadBlockDescriptor(
)
children.add(
Knob(
x = 64, y = 18,
x = 64f, y = 18f,
controls.resonance,
min = 0f, max = 1f,
Knob.Color.Blue,
@ -89,7 +89,7 @@ class BiquadBlockDescriptor(
)
children.add(
Knob(
x = 92, y = 18,
x = 92f, y = 18f,
controls.resonanceCV,
min = -1f, max = 1f,
Knob.Color.Blue,

View file

@ -31,14 +31,14 @@ object KeyboardBlockDescriptor : DeviceBlockDescriptor<KeyboardBlockDescriptor.C
ClientState(controls)
override val ui = object : DeviceBlockDescriptor.UI<KeyboardDevice.Controls> {
override fun open(controls: KeyboardDevice.Controls, x: Int, y: Int) =
override fun open(controls: KeyboardDevice.Controls, x: Float, y: Float) =
Window(
x, y,
width = 332,
height = 56,
width = 332f,
height = 56f,
title = Text.translatable("block.dawd3.keyboard")
).apply {
children.add(Keyboard(x = 8, y = 16, firstNote = -33, lastNote = 27, controls))
children.add(Keyboard(x = 8f, y = 16f, firstNote = -33, lastNote = 27, controls))
}
}
}

View file

@ -39,12 +39,12 @@ object KnobBlockDescriptor : DeviceBlockDescriptor<KnobBlockDescriptor.ClientSta
ClientState(controls)
override val ui = object : DeviceBlockDescriptor.UI<ConstantDevice.Controls> {
override fun open(controls: ConstantDevice.Controls, x: Int, y: Int): Widget =
Window(x, y, 48, 48, Text.translatable("block.dawd3.knob")).apply {
override fun open(controls: ConstantDevice.Controls, x: Float, y: Float): Widget =
Window(x, y, 48f, 48f, Text.translatable("block.dawd3.knob")).apply {
children.add(
Knob(
x = 14,
y = 18,
x = 14f,
y = 18f,
control = controls.value,
min = -48f,
max = 48f,

View file

@ -45,18 +45,18 @@ object AmplifierBlockDescriptor : DeviceBlockDescriptor<AmplifierBlockDescriptor
ClientState(controls)
override val ui = object : DeviceBlockDescriptor.UI<AmplifierDevice.Controls> {
override fun open(controls: AmplifierDevice.Controls, x: Int, y: Int): Widget =
override fun open(controls: AmplifierDevice.Controls, x: Float, y: Float): Widget =
Window(
x,
y,
width = 78,
height = 48,
width = 78f,
height = 48f,
Text.translatable("block.dawd3.amplifier")
).apply {
children.add(
Knob(
x = 14,
y = 18,
x = 14f,
y = 18f,
control = controls.amplitude,
min = 0f,
max = 1f,
@ -66,8 +66,8 @@ object AmplifierBlockDescriptor : DeviceBlockDescriptor<AmplifierBlockDescriptor
)
children.add(
Knob(
x = 42,
y = 18,
x = 42f,
y = 18f,
control = controls.amplitudeCV,
min = -8f,
max = 8f,

View file

@ -32,12 +32,12 @@ object MixerBlockDescriptor : DeviceBlockDescriptor<MixerBlockDescriptor.ClientS
ClientState(controls)
override val ui = object : DeviceBlockDescriptor.UI<MixDevice.Controls> {
override fun open(controls: MixDevice.Controls, x: Int, y: Int) =
Window(x, y, width = 78, height = 48, Text.translatable("block.dawd3.mixer")).apply {
override fun open(controls: MixDevice.Controls, x: Float, y: Float) =
Window(x, y, width = 78f, height = 48f, Text.translatable("block.dawd3.mixer")).apply {
children.add(
Knob(
x = 14,
y = 18,
x = 14f,
y = 18f,
control = controls.aAmplitude,
min = 0f,
max = 1f,
@ -47,8 +47,8 @@ object MixerBlockDescriptor : DeviceBlockDescriptor<MixerBlockDescriptor.ClientS
)
children.add(
Knob(
x = 42,
y = 18,
x = 42f,
y = 18f,
control = controls.bAmplitude,
min = 0f,
max = 1f,

View file

@ -43,12 +43,12 @@ object ModulatorBlockDescriptor : DeviceBlockDescriptor<ModulatorBlockDescriptor
ClientState(controls)
override val ui = object : DeviceBlockDescriptor.UI<FmaDevice.Controls> {
override fun open(controls: FmaDevice.Controls, x: Int, y: Int): Widget =
Window(x, y, 78, 48, Text.translatable("block.dawd3.modulator")).apply {
override fun open(controls: FmaDevice.Controls, x: Float, y: Float): Widget =
Window(x, y, 78f, 48f, Text.translatable("block.dawd3.modulator")).apply {
children.add(
Knob(
x = 14,
y = 18,
x = 14f,
y = 18f,
control = controls.add,
min = -48f,
max = 48f,
@ -57,8 +57,8 @@ object ModulatorBlockDescriptor : DeviceBlockDescriptor<ModulatorBlockDescriptor
)
children.add(
Knob(
x = 42,
y = 18,
x = 42f,
y = 18f,
control = controls.multiply,
min = -8f,
max = 8f,

View file

@ -44,18 +44,18 @@ object PulseOscillatorBlockDescriptor : DeviceBlockDescriptor<PulseOscillatorBlo
ClientState(controls)
override val ui = object : DeviceBlockDescriptor.UI<PulseOscillatorDevice.Controls> {
override fun open(controls: PulseOscillatorDevice.Controls, x: Int, y: Int) =
override fun open(controls: PulseOscillatorDevice.Controls, x: Float, y: Float) =
Window(
x,
y,
width = 78,
height = 48,
width = 78f,
height = 48f,
Text.translatable("block.dawd3.pulse_oscillator")
).apply {
children.add(
Knob(
x = 14,
y = 18,
x = 14f,
y = 18f,
control = controls.dutyCycle,
min = 0f,
max = 1f,
@ -65,8 +65,8 @@ object PulseOscillatorBlockDescriptor : DeviceBlockDescriptor<PulseOscillatorBlo
)
children.add(
Knob(
x = 42,
y = 18,
x = 42f,
y = 18f,
control = controls.dutyCycleCV,
min = 0f,
max = 1f,

View file

@ -4,5 +4,5 @@ import net.minecraft.util.Identifier
data class Atlas(
val asset: Identifier,
val size: Int,
val size: Float,
)

View file

@ -1,16 +1,16 @@
package net.liquidev.dawd3.render
data class NinePatch(
val u: Int,
val v: Int,
val width: Int,
val height: Int,
val borderTop: Int,
val borderBottom: Int,
val borderLeft: Int,
val borderRight: Int,
val u: Float,
val v: Float,
val width: Float,
val height: Float,
val borderTop: Float,
val borderBottom: Float,
val borderLeft: Float,
val borderRight: Float,
) {
constructor(u: Int, v: Int, width: Int, height: Int, border: Int) : this(
constructor(u: Float, v: Float, width: Float, height: Float, border: Float) : this(
u,
v,
width,

View file

@ -19,30 +19,30 @@ import kotlin.math.sqrt
object Render {
fun sprite(
matrices: MatrixStack,
x: Int,
y: Int,
width: Int,
height: Int,
x: Float,
y: Float,
width: Float,
height: Float,
atlas: Atlas,
sprite: Sprite,
) {
RenderSystem.setShaderTexture(0, atlas.asset)
DrawableHelper.drawTexture(
matrices,
x,
y,
width,
height,
sprite.u.toFloat(),
sprite.v.toFloat(),
sprite.width,
sprite.height,
atlas.size,
atlas.size
x.toInt(),
y.toInt(),
width.toInt(),
height.toInt(),
sprite.u,
sprite.v,
sprite.width.toInt(),
sprite.height.toInt(),
atlas.size.toInt(),
atlas.size.toInt()
)
}
fun sprite(matrices: MatrixStack, x: Int, y: Int, atlas: Atlas, icon: Sprite) {
fun sprite(matrices: MatrixStack, x: Float, y: Float, atlas: Atlas, icon: Sprite) {
sprite(matrices, x, y, icon.width, icon.height, atlas, icon)
}
@ -151,138 +151,168 @@ object Render {
Tessellator.getInstance().draw()
}
private fun drawTexture(
matrices: MatrixStack,
x: Float,
y: Float,
width: Float,
height: Float,
u: Float,
v: Float,
regionWidth: Float,
regionHeight: Float,
textureWidth: Float,
textureHeigh: Float,
) {
// Temporarily defined to just defer to DrawableHelper, later on we should introduce support
// for rendering at floating-point coordinates.
DrawableHelper.drawTexture(
matrices,
x.toInt(),
y.toInt(),
width.toInt(),
height.toInt(),
u,
v,
regionWidth.toInt(),
regionHeight.toInt(),
textureWidth.toInt(),
textureHeigh.toInt()
)
}
fun ninePatch(
matrices: MatrixStack,
x: Int,
y: Int,
width: Int,
height: Int,
x: Float,
y: Float,
width: Float,
height: Float,
atlas: Atlas,
ninePatch: NinePatch,
) {
RenderSystem.setShaderTexture(0, atlas.asset)
// Top left
DrawableHelper.drawTexture(
drawTexture(
matrices,
x,
y,
ninePatch.borderLeft,
ninePatch.borderTop,
ninePatch.u.toFloat(),
ninePatch.v.toFloat(),
ninePatch.u,
ninePatch.v,
ninePatch.borderLeft,
ninePatch.borderTop,
atlas.size,
atlas.size
)
// Top center
DrawableHelper.drawTexture(
drawTexture(
matrices,
x + ninePatch.borderLeft,
y,
width - ninePatch.borderLeft - ninePatch.borderRight,
ninePatch.borderTop,
(ninePatch.u + ninePatch.borderLeft).toFloat(),
ninePatch.v.toFloat(),
ninePatch.u + ninePatch.borderLeft,
ninePatch.v,
ninePatch.width - ninePatch.borderLeft - ninePatch.borderRight,
ninePatch.borderTop,
atlas.size,
atlas.size
)
// Top right
DrawableHelper.drawTexture(
drawTexture(
matrices,
x + width - ninePatch.borderRight,
y,
ninePatch.borderRight,
ninePatch.borderTop,
(ninePatch.u + ninePatch.width - ninePatch.borderRight).toFloat(),
ninePatch.v.toFloat(),
ninePatch.u + ninePatch.width - ninePatch.borderRight,
ninePatch.v,
ninePatch.borderRight,
ninePatch.borderTop,
atlas.size,
atlas.size
)
// Middle left
DrawableHelper.drawTexture(
drawTexture(
matrices,
x,
y + ninePatch.borderTop,
ninePatch.borderLeft,
height - ninePatch.borderTop - ninePatch.borderBottom,
ninePatch.u.toFloat(),
(ninePatch.v + ninePatch.borderTop).toFloat(),
ninePatch.u,
ninePatch.v + ninePatch.borderTop,
ninePatch.borderLeft,
ninePatch.height - ninePatch.borderTop - ninePatch.borderBottom,
atlas.size,
atlas.size
)
// Center
DrawableHelper.drawTexture(
drawTexture(
matrices,
x + ninePatch.borderLeft,
y + ninePatch.borderTop,
width - ninePatch.borderLeft - ninePatch.borderRight,
height - ninePatch.borderTop - ninePatch.borderBottom,
(ninePatch.u + ninePatch.borderLeft).toFloat(),
(ninePatch.v + ninePatch.borderTop).toFloat(),
ninePatch.u + ninePatch.borderLeft,
ninePatch.v + ninePatch.borderTop,
ninePatch.width - ninePatch.borderLeft - ninePatch.borderRight,
ninePatch.height - ninePatch.borderTop - ninePatch.borderBottom,
atlas.size,
atlas.size
)
// Middle right
DrawableHelper.drawTexture(
drawTexture(
matrices,
x + width - ninePatch.borderRight,
y + ninePatch.borderTop,
ninePatch.borderLeft,
height - ninePatch.borderTop - ninePatch.borderBottom,
(ninePatch.u + ninePatch.width - ninePatch.borderRight).toFloat(),
(ninePatch.v + ninePatch.borderTop).toFloat(),
ninePatch.u + ninePatch.width - ninePatch.borderRight,
ninePatch.v + ninePatch.borderTop,
ninePatch.borderLeft,
ninePatch.height - ninePatch.borderTop - ninePatch.borderBottom,
atlas.size,
atlas.size
)
// Bottom left
DrawableHelper.drawTexture(
drawTexture(
matrices,
x,
y + height - ninePatch.borderBottom,
ninePatch.borderLeft,
ninePatch.borderBottom,
ninePatch.u.toFloat(),
(ninePatch.v + ninePatch.height - ninePatch.borderBottom).toFloat(),
ninePatch.u,
ninePatch.v + ninePatch.height - ninePatch.borderBottom,
ninePatch.borderLeft,
ninePatch.borderBottom,
atlas.size,
atlas.size
)
// Bottom center
DrawableHelper.drawTexture(
drawTexture(
matrices,
x + ninePatch.borderLeft,
y + height - ninePatch.borderBottom,
width - ninePatch.borderLeft - ninePatch.borderRight,
ninePatch.borderBottom,
(ninePatch.u + ninePatch.borderLeft).toFloat(),
(ninePatch.v + ninePatch.height - ninePatch.borderBottom).toFloat(),
ninePatch.u + ninePatch.borderLeft,
ninePatch.v + ninePatch.height - ninePatch.borderBottom,
ninePatch.width - ninePatch.borderLeft - ninePatch.borderRight,
ninePatch.borderBottom,
atlas.size,
atlas.size
)
// Bottom right
DrawableHelper.drawTexture(
drawTexture(
matrices,
x + width - ninePatch.borderRight,
y + height - ninePatch.borderBottom,
ninePatch.borderRight,
ninePatch.borderBottom,
(ninePatch.u + ninePatch.width - ninePatch.borderRight).toFloat(),
(ninePatch.v + ninePatch.height - ninePatch.borderBottom).toFloat(),
ninePatch.u + ninePatch.width - ninePatch.borderRight,
ninePatch.v + ninePatch.height - ninePatch.borderBottom,
ninePatch.borderRight,
ninePatch.borderBottom,
atlas.size,
@ -290,54 +320,42 @@ object Render {
)
}
fun textWidth(text: Text): Int {
fun textWidth(text: Text): Float {
val textRenderer = MinecraftClient.getInstance().textRenderer
return textRenderer.getWidth(text)
return textRenderer.getWidth(text).toFloat()
}
fun text(matrices: MatrixStack, x: Int, y: Int, text: Text, color: Int) {
fun text(matrices: MatrixStack, x: Float, y: Float, text: Text, color: Int) {
val textRenderer = MinecraftClient.getInstance().textRenderer
textRenderer.draw(
matrices,
text,
x.toFloat(),
y.toFloat(),
color
)
textRenderer.draw(matrices, text, x, y, color)
}
fun textCentered(matrices: MatrixStack, centerX: Int, y: Int, text: Text, color: Int) {
fun textCentered(matrices: MatrixStack, centerX: Float, y: Float, text: Text, color: Int) {
val textRenderer = MinecraftClient.getInstance().textRenderer
val textWidth = textRenderer.getWidth(text)
val x = centerX - textWidth / 2
textRenderer.draw(
matrices,
text,
x.toFloat(),
y.toFloat(),
color
)
text(matrices, x, y, text, color)
}
fun tooltipWidth(text: Text): Int = textWidth(text) + 5
fun tooltipWidth(text: Text): Float = textWidth(text) + 5f
fun tooltip(
matrices: MatrixStack,
x: Int,
y: Int,
x: Float,
y: Float,
text: Text,
atlas: Atlas,
tooltip: Tooltip,
) {
val width = tooltipWidth(text)
ninePatch(matrices, x, y, width, height = 14, atlas, tooltip.ninePatch)
ninePatch(matrices, x, y, width, height = 14f, atlas, tooltip.ninePatch)
text(matrices, x + 3, y + 3, text, tooltip.textColor)
}
fun tooltipCentered(
matrices: MatrixStack,
centerX: Int,
y: Int,
centerX: Float,
y: Float,
text: Text,
atlas: Atlas,
tooltip: Tooltip,

View file

@ -1,8 +1,8 @@
package net.liquidev.dawd3.render
data class Sprite(
val u: Int,
val v: Int,
val width: Int,
val height: Int,
val u: Float,
val v: Float,
val width: Float,
val height: Float,
)

View file

@ -3,7 +3,7 @@ package net.liquidev.dawd3.render
data class Tooltip(val ninePatch: NinePatch, val textColor: Int) {
companion object {
val dark = Tooltip(
NinePatch(u = 32, v = 0, width = 8, height = 8, border = 2),
NinePatch(u = 32f, v = 0f, width = 8f, height = 8f, border = 2f),
textColor = 0xFFFFFF
)
}

View file

@ -30,32 +30,34 @@ class Rack(
ArrayList(shownDevices.mapNotNull { blockPosition ->
val blockEntity = world.getBlockEntity(blockPosition) as DeviceBlockEntity
blockEntity.descriptor.ui
?.open(blockEntity.controls, 0, 0)
?.open(blockEntity.controls, 0f, 0f)
?.let { widget -> OpenWidget(blockPosition, widget) }
})
private fun layoutWindows() {
var x = 16
var y = 16
var rowHeight = 0
var x = 16f
var y = 16f
var rowHeight = 0f
for (openWidget in openWidgets) {
if (x + openWidget.widget.width >= width - 16) {
x = 16
y += rowHeight + 8
rowHeight = 0
x = 16f
y += rowHeight + 8f
rowHeight = 0f
}
openWidget.widget.x = x
openWidget.widget.y = y
x += openWidget.widget.width + 8
x += openWidget.widget.width + 8f
rowHeight = max(rowHeight, openWidget.widget.height)
}
}
override fun init() {
super.init()
layoutWindows()
}
override fun resize(client: MinecraftClient?, width: Int, height: Int) {
super.resize(client, width, height)
layoutWindows()
}
@ -69,12 +71,12 @@ class Rack(
}
for (openWidget in openWidgets) {
openWidget.widget.draw(matrices, mouseX, mouseY, delta)
openWidget.widget.draw(matrices, mouseX.toFloat(), mouseY.toFloat(), delta)
}
Render.sprite(
matrices,
8,
8f,
height - badge.height - 8,
badge.width * 2,
badge.height * 2,
@ -88,10 +90,7 @@ class Rack(
if (
openWidget.widget.event(
EventContext(world, openWidget.blockPosition),
event.relativeTo(
openWidget.widget.x.toDouble(),
openWidget.widget.y.toDouble(),
)
event.relativeTo(openWidget.widget.x, openWidget.widget.y)
)
) return true
}
@ -99,14 +98,14 @@ class Rack(
}
override fun mouseMoved(mouseX: Double, mouseY: Double) {
propagateEvent(MouseMove(mouseX, mouseY))
propagateEvent(MouseMove(mouseX.toFloat(), mouseY.toFloat()))
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean =
propagateEvent(MouseButton(Action.Down, mouseX, mouseY, button))
propagateEvent(MouseButton(Action.Down, mouseX.toFloat(), mouseY.toFloat(), button))
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean =
propagateEvent(MouseButton(Action.Up, mouseX, mouseY, button))
propagateEvent(MouseButton(Action.Up, mouseX.toFloat(), mouseY.toFloat(), button))
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
return if (super.keyPressed(keyCode, scanCode, modifiers)) {
@ -124,8 +123,8 @@ class Rack(
override fun shouldPause(): Boolean = false
companion object {
val atlas = Atlas(asset = Identifier(Mod.id, "textures/ui/rack.png"), size = 64)
val badge = Sprite(u = 0, v = 16, width = 6, height = 3)
val atlas = Atlas(asset = Identifier(Mod.id, "textures/ui/rack.png"), size = 64f)
val badge = Sprite(u = 0f, v = 16f, width = 6f, height = 3f)
val smallFont = Identifier(Mod.id, "altopixel")
val smallText = Style.EMPTY.withFont(smallFont)!!

View file

@ -4,7 +4,7 @@ import net.minecraft.client.world.ClientWorld
import net.minecraft.util.math.BlockPos
sealed interface Event {
fun relativeTo(x: Double, y: Double): Event
fun relativeTo(x: Float, y: Float): Event
}
enum class Action {
@ -14,13 +14,13 @@ enum class Action {
data class MouseButton(
val action: Action,
val mouseX: Double,
val mouseY: Double,
val absoluteMouseX: Double,
val absoluteMouseY: Double,
val mouseX: Float,
val mouseY: Float,
val absoluteMouseX: Float,
val absoluteMouseY: Float,
val button: Int,
) : Event {
constructor(action: Action, mouseX: Double, mouseY: Double, button: Int) : this(
constructor(action: Action, mouseX: Float, mouseY: Float, button: Int) : this(
action,
mouseX,
mouseY,
@ -29,19 +29,19 @@ data class MouseButton(
button
)
override fun relativeTo(x: Double, y: Double) =
override fun relativeTo(x: Float, y: Float) =
MouseButton(action, mouseX - x, mouseY - y, absoluteMouseX, absoluteMouseY, button)
}
data class MouseMove(
val mouseX: Double,
val mouseY: Double,
val absoluteMouseX: Double,
val absoluteMouseY: Double,
val mouseX: Float,
val mouseY: Float,
val absoluteMouseX: Float,
val absoluteMouseY: Float,
) : Event {
constructor(mouseX: Double, mouseY: Double) : this(mouseX, mouseY, mouseX, mouseY)
constructor(mouseX: Float, mouseY: Float) : this(mouseX, mouseY, mouseX, mouseY)
override fun relativeTo(x: Double, y: Double): Event =
override fun relativeTo(x: Float, y: Float): Event =
MouseMove(mouseX - x, mouseY - y, absoluteMouseX, absoluteMouseY)
}

View file

@ -4,10 +4,15 @@ import net.liquidev.dawd3.ui.Event
import net.liquidev.dawd3.ui.EventContext
import net.minecraft.client.util.math.MatrixStack
abstract class Container(x: Int, y: Int) : Widget(x, y) {
abstract class Container(x: Float, y: Float) : Widget(x, y) {
abstract val children: List<Widget>
override fun drawContent(matrices: MatrixStack, mouseX: Int, mouseY: Int, deltaTime: Float) {
override fun drawContent(
matrices: MatrixStack,
mouseX: Float,
mouseY: Float,
deltaTime: Float,
) {
for (child in children) {
child.draw(matrices, mouseX, mouseY, deltaTime)
}

View file

@ -19,34 +19,42 @@ import net.minecraft.client.util.InputUtil
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import org.lwjgl.glfw.GLFW
import kotlin.math.*
import kotlin.math.cos
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sin
class Knob(
x: Int,
y: Int,
x: Float,
y: Float,
val control: FloatControl,
val min: Float,
val max: Float,
val color: Color,
size: Int = normalSize,
size: Float = normalSize,
val unit: FloatUnit = RawValue,
// Pick a default sensitivity such that for pitch ranges we move by steps of 0.25.
val sensitivity: Float = (max - min) * 0.25f / 96f,
) : Widget(x, y) {
override val width = size
override val height = size + 4
override val height = size + 4f
private data class DraggingInfo(var previousMouseY: Double)
private data class DraggingInfo(var previousMouseY: Float)
private var draggingInfo: DraggingInfo? = null
private val controlLabel =
Text.translatable(control.descriptor.name.toTranslationKey()).setStyle(Rack.smallText)
override fun drawContent(matrices: MatrixStack, mouseX: Int, mouseY: Int, deltaTime: Float) {
val centerX = width.toFloat() / 2
val centerY = height.toFloat() / 2
val radius = width.toFloat() / 2
override fun drawContent(
matrices: MatrixStack,
mouseX: Float,
mouseY: Float,
deltaTime: Float,
) {
val centerX = width / 2f
val centerY = height / 2f
val radius = width / 2f
val valueAngle = mapRange(control.value, min, max, startAngle.value, endAngle.value)
val zeroAngle = clamp(
@ -92,7 +100,7 @@ class Knob(
Render.textCentered(
matrices,
centerX.roundToInt() + 1,
centerX + 1f,
height - 5,
controlLabel,
0x111111
@ -101,7 +109,7 @@ class Knob(
if (draggingInfo != null) {
Render.tooltipCentered(
matrices,
centerX.toInt(),
centerX,
height - 4,
Text.literal(unit.display(control.value)),
Rack.atlas,
@ -117,8 +125,8 @@ class Knob(
when (event.action) {
Action.Down -> if (
containsRelativePoint(
event.mouseX.toInt(),
event.mouseY.toInt()
event.mouseX,
event.mouseY
)
) {
draggingInfo = DraggingInfo(event.absoluteMouseY)
@ -154,11 +162,14 @@ class Knob(
val draggingInfo = draggingInfo
if (draggingInfo != null) {
val guiScale = client.options.guiScale.value.toFloat()
val deltaY = (draggingInfo.previousMouseY - event.absoluteMouseY) * guiScale
var deltaY = (draggingInfo.previousMouseY - event.absoluteMouseY) * guiScale
if (isFineTuning()) {
deltaY *= 0.05f
}
BlockEntityControls.setFloatControlValue(
context.blockPosition,
control,
alterValue(control.value, by = deltaY.toFloat())
alterValue(control.value, by = deltaY)
)
draggingInfo.previousMouseY = event.absoluteMouseY
// Consume the events so that other controls don't get triggered while the knob
@ -171,6 +182,12 @@ class Knob(
return false
}
private fun isFineTuning(): Boolean =
InputUtil.isKeyPressed(
MinecraftClient.getInstance().window.handle,
GLFW.GLFW_KEY_LEFT_SHIFT
)
private fun alterValue(value: Float, by: Float): Float =
max(min(value + by * sensitivity, max), min)
@ -195,7 +212,6 @@ class Knob(
return TextureStrip(u, 16f, u, 32f)
}
const val normalSize = 20
const val smallSize = 16
const val normalSize = 20f
}
}

View file

@ -4,28 +4,28 @@ import net.liquidev.dawd3.ui.Event
import net.liquidev.dawd3.ui.EventContext
import net.minecraft.client.util.math.MatrixStack
abstract class Widget(var x: Int, var y: Int) {
abstract val width: Int
abstract val height: Int
abstract class Widget(var x: Float, var y: Float) {
abstract val width: Float
abstract val height: Float
protected abstract fun drawContent(
matrices: MatrixStack,
mouseX: Int,
mouseY: Int,
mouseX: Float,
mouseY: Float,
deltaTime: Float,
)
/** Returns false to propagate the event, or true to consume it. */
abstract fun event(context: EventContext, event: Event): Boolean
fun draw(matrices: MatrixStack, mouseX: Int, mouseY: Int, deltaTime: Float) {
fun draw(matrices: MatrixStack, mouseX: Float, mouseY: Float, deltaTime: Float) {
matrices.push()
matrices.translate(x.toDouble(), y.toDouble(), 0.0)
drawContent(matrices, mouseX - x, mouseY - y, deltaTime)
matrices.pop()
}
fun containsRelativePoint(x: Int, y: Int) =
fun containsRelativePoint(x: Float, y: Float) =
x >= 0 && y >= 0 && x <= width && y <= height
companion object {
@ -39,7 +39,7 @@ abstract class Widget(var x: Int, var y: Int) {
if (
widget.event(
context,
event.relativeTo(widget.x.toDouble(), widget.y.toDouble())
event.relativeTo(widget.x, widget.y)
)
) {
return true

View file

@ -7,23 +7,28 @@ import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
class Window(
x: Int,
y: Int,
override val width: Int,
override val height: Int,
x: Float,
y: Float,
override val width: Float,
override val height: Float,
val title: Text,
) : Container(x, y) {
override val children = mutableListOf<Widget>()
override fun drawContent(matrices: MatrixStack, mouseX: Int, mouseY: Int, deltaTime: Float) {
Render.ninePatch(matrices, 2, 2, width, height, Rack.atlas, windowShadow)
Render.ninePatch(matrices, 0, 0, width, height, Rack.atlas, windowBackground)
override fun drawContent(
matrices: MatrixStack,
mouseX: Float,
mouseY: Float,
deltaTime: Float,
) {
Render.ninePatch(matrices, 2f, 2f, width, height, Rack.atlas, windowShadow)
Render.ninePatch(matrices, 0f, 0f, width, height, Rack.atlas, windowBackground)
Render.textCentered(
matrices,
width / 2,
4,
4f,
title,
0x111111
)
@ -33,14 +38,14 @@ class Window(
companion object {
val windowBackground = NinePatch(
u = 0, v = 0,
width = 16, height = 16,
border = 3
u = 0f, v = 0f,
width = 16f, height = 16f,
border = 3f
)
val windowShadow = NinePatch(
u = 16, v = 0,
width = 16, height = 16,
border = 3
u = 16f, v = 0f,
width = 16f, height = 16f,
border = 3f
)
}
}

View file

@ -10,33 +10,33 @@ import net.liquidev.dawd3.ui.widget.Widget
import net.minecraft.client.util.math.MatrixStack
class Key(
x: Int,
y: Int,
x: Float,
y: Float,
val type: Type,
val note: Float,
val pitchControl: FloatControl,
) : Widget(x, y) {
enum class Type(
val width: Int,
val height: Int,
val width: Float,
val height: Float,
val idleSprite: Sprite,
val pressedSprite: Sprite,
val currentPitchSprite: Sprite,
) {
White(
width = 10,
height = 32,
idleSprite = Sprite(u = 32, v = 16, width = 10, height = 32),
pressedSprite = Sprite(u = 42, v = 16, width = 10, height = 32),
currentPitchSprite = Sprite(u = 59, v = 16, width = 4, height = 4),
width = 10f,
height = 32f,
idleSprite = Sprite(u = 32f, v = 16f, width = 10f, height = 32f),
pressedSprite = Sprite(u = 42f, v = 16f, width = 10f, height = 32f),
currentPitchSprite = Sprite(u = 59f, v = 16f, width = 4f, height = 4f),
),
Black(
width = 7,
height = 20,
idleSprite = Sprite(u = 52, v = 16, width = 7, height = 20),
pressedSprite = Sprite(u = 52, v = 36, width = 7, height = 20),
currentPitchSprite = Sprite(u = 59, v = 20, width = 3, height = 3),
width = 7f,
height = 20f,
idleSprite = Sprite(u = 52f, v = 16f, width = 7f, height = 20f),
pressedSprite = Sprite(u = 52f, v = 36f, width = 7f, height = 20f),
currentPitchSprite = Sprite(u = 59f, v = 20f, width = 3f, height = 3f),
),
}
@ -45,18 +45,23 @@ class Key(
private var isPressed = false
override fun drawContent(matrices: MatrixStack, mouseX: Int, mouseY: Int, deltaTime: Float) {
override fun drawContent(
matrices: MatrixStack,
mouseX: Float,
mouseY: Float,
deltaTime: Float,
) {
Render.sprite(
matrices,
x = 0,
y = 0,
x = 0f,
y = 0f,
Rack.atlas,
if (isPressed) type.pressedSprite else type.idleSprite
)
if (pitchControl.value == note) {
Render.sprite(
matrices,
x = (width / 2f - type.currentPitchSprite.width / 2f).toInt(),
x = width / 2f - type.currentPitchSprite.width / 2f,
y = height - type.currentPitchSprite.height - 2,
Rack.atlas,
type.currentPitchSprite

View file

@ -7,27 +7,27 @@ import net.liquidev.dawd3.ui.widget.Container
import org.lwjgl.glfw.GLFW
class Keyboard(
x: Int,
y: Int,
x: Float,
y: Float,
firstNote: Int,
lastNote: Int,
private val controls: KeyboardDevice.Controls,
) : Container(x, y) {
override val children = run {
var whiteX = 0
var whiteX = 0f
List(lastNote - firstNote) { index ->
val note = firstNote + index
val keyType = octave[note.mod(octave.size)]
val keyX = when (keyType) {
Key.Type.White -> {
val xx = whiteX
whiteX += keyType.width - 1
whiteX += keyType.width - 1f
xx
}
Key.Type.Black -> whiteX - keyType.width / 2
}
Key(keyX, y = 0, keyType, note.toFloat(), controls.pitch)
Key(keyX, y = 0f, keyType, note.toFloat(), controls.pitch)
// We want black keys to render after white keys.
}.sortedBy { it.type }
}
@ -40,8 +40,8 @@ class Keyboard(
override fun event(context: EventContext, event: Event): Boolean {
if (event is MouseButton && event.button == GLFW.GLFW_MOUSE_BUTTON_LEFT) {
mouseIsDown = event.action == Action.Down && containsRelativePoint(
event.mouseX.toInt(),
event.mouseY.toInt()
event.mouseX,
event.mouseY
)
if (mouseIsDown) {
setPressedKey(context, findPressedKey(event.mouseX.toInt(), event.mouseY.toInt()))