Compare commits

..

No commits in common. "df3523347c1910b384b776eeb1e59dd2a73b9e84" and "c79b93a4f9a0c229fa822994c94b3a53ce54a47f" have entirely different histories.

15 changed files with 16 additions and 368 deletions

View File

@ -1,7 +1,6 @@
package net.shadowfacts.asmr package net.shadowfacts.asmr
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.asmr.util.RedstoneMode
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.cacao.CacaoScreen import net.shadowfacts.cacao.CacaoScreen
import net.shadowfacts.cacao.Window import net.shadowfacts.cacao.Window
@ -12,8 +11,6 @@ import net.shadowfacts.cacao.util.NinePatchTexture
import net.shadowfacts.cacao.util.Texture import net.shadowfacts.cacao.util.Texture
import net.shadowfacts.cacao.view.* import net.shadowfacts.cacao.view.*
import net.shadowfacts.cacao.view.button.Button import net.shadowfacts.cacao.view.button.Button
import net.shadowfacts.cacao.view.button.EnumButton
import net.shadowfacts.cacao.view.button.ToggleButton
/** /**
* @author shadowfacts * @author shadowfacts
@ -36,10 +33,13 @@ class TestCacaoScreen: CacaoScreen() {
intrinsicContentSize = Size(50.0, 50.0) intrinsicContentSize = Size(50.0, 50.0)
backgroundColor = Color(0x0000ff) backgroundColor = Color(0x0000ff)
}) })
val purple = blue.addSubview(ToggleButton(false).apply { val purple = blue.addSubview(Button(Label("Hello, button!").apply {
textColor = Color.WHITE
}).apply {
handler = { handler = {
println("enum button clicked, new value: ${it.state}") println("$it clicked!")
} }
background = NinePatchView(buttonNinePatch)
}) })
solver.dsl { solver.dsl {
@ -48,7 +48,6 @@ class TestCacaoScreen: CacaoScreen() {
stack.rightAnchor equalTo 150 stack.rightAnchor equalTo 150
purple.centerXAnchor equalTo blue.centerXAnchor purple.centerXAnchor equalTo blue.centerXAnchor
purple.centerYAnchor equalTo blue.centerYAnchor purple.centerYAnchor equalTo blue.centerYAnchor
// purple.widthAnchor equalTo 50
} }
layout() layout()

View File

@ -1,14 +0,0 @@
package net.shadowfacts.asmr.util
/**
* @author shadowfacts
*/
enum class RedstoneMode {
HIGH, LOW, TOGGLE;
companion object {
fun localize(value: RedstoneMode): String {
return value.name.toLowerCase().capitalize()
}
}
}

View File

@ -1,20 +0,0 @@
package net.shadowfacts.cacao.util
/**
* @author shadowfacts
*/
object EnumHelper {
fun <E: Enum<E>> next(value: E): E {
val constants = value.declaringClass.enumConstants
val index = constants.indexOf(value) + 1
return if (index < constants.size) constants[index] else constants.first()
}
fun <E: Enum<E>> previous(value: E): E {
val constants = value.declaringClass.enumConstants
val index = constants.indexOf(value) - 1
return if (index >= 0) constants[index] else constants.last()
}
}

View File

@ -4,15 +4,14 @@ package net.shadowfacts.cacao.util
* @author shadowfacts * @author shadowfacts
*/ */
enum class MouseButton { enum class MouseButton {
LEFT, RIGHT, MIDDLE, UNKNOWN; LEFT, RIGHT, MIDDLE;
companion object { companion object {
fun fromMC(button: Int): MouseButton { fun fromMC(button: Int): MouseButton {
return when (button) { return when (button) {
0 -> LEFT
1 -> RIGHT 1 -> RIGHT
2 -> MIDDLE 2 -> MIDDLE
else -> UNKNOWN else -> LEFT
} }
} }
} }

View File

@ -3,8 +3,6 @@ package net.shadowfacts.cacao.util
import com.mojang.blaze3d.platform.GlStateManager import com.mojang.blaze3d.platform.GlStateManager
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.gui.DrawableHelper
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.sound.SoundEvent
import net.shadowfacts.cacao.geometry.Rect import net.shadowfacts.cacao.geometry.Rect
/** /**
@ -15,13 +13,7 @@ import net.shadowfacts.cacao.geometry.Rect
*/ */
object RenderHelper { object RenderHelper {
val disabled = (System.getProperty("cacao.drawing.disabled") ?: "false").toBoolean() private val disabled = (System.getProperty("cacao.drawing.disabled") ?: "false").toBoolean()
// TODO: find a better place for this
fun playSound(event: SoundEvent) {
if (disabled) return
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(event, 1f))
}
/** /**
* Draws a solid [rect] filled with the given [color]. * Draws a solid [rect] filled with the given [color].
@ -73,9 +65,6 @@ object RenderHelper {
GlStateManager.translated(x, y, z) GlStateManager.translated(x, y, z)
} }
/**
* @see org.lwjgl.opengl.GL11.glColor4f
*/
fun color(r: Float, g: Float, b: Float, alpha: Float) { fun color(r: Float, g: Float, b: Float, alpha: Float) {
if (disabled) return if (disabled) return
GlStateManager.color4f(r, g, b, alpha) GlStateManager.color4f(r, g, b, alpha)

View File

@ -5,7 +5,6 @@ import net.minecraft.client.font.TextRenderer
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.RenderHelper
/** /**
* A simple View that displays text. Allows for controlling the color and shadow of the text. Label cannot be used * A simple View that displays text. Allows for controlling the color and shadow of the text. Label cannot be used
@ -30,7 +29,7 @@ class Label(text: String): View() {
updateIntrinsicContentSize() updateIntrinsicContentSize()
} }
var textColor = Color.WHITE var textColor = Color(0x404040)
override fun wasAdded() { override fun wasAdded() {
super.wasAdded() super.wasAdded()
@ -39,8 +38,6 @@ class Label(text: String): View() {
} }
private fun updateIntrinsicContentSize() { private fun updateIntrinsicContentSize() {
if (RenderHelper.disabled) return
val width = textRenderer.getStringWidth(text) val width = textRenderer.getStringWidth(text)
val height = textRenderer.fontHeight val height = textRenderer.fontHeight
intrinsicContentSize = Size(width.toDouble(), height.toDouble()) intrinsicContentSize = Size(width.toDouble(), height.toDouble())

View File

@ -10,7 +10,7 @@ import net.shadowfacts.cacao.util.Texture
* *
* @author shadowfacts * @author shadowfacts
*/ */
class TextureView(var texture: Texture): View() { class TextureView(val texture: Texture): View() {
override fun drawContent(mouse: Point, delta: Float) { override fun drawContent(mouse: Point, delta: Float) {
RenderHelper.draw(bounds, texture) RenderHelper.draw(bounds, texture)

View File

@ -1,13 +1,8 @@
package net.shadowfacts.cacao.view.button package net.shadowfacts.cacao.view.button
import net.minecraft.sound.SoundEvents
import net.minecraft.util.Identifier
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.MouseButton import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.NinePatchTexture
import net.shadowfacts.cacao.util.RenderHelper import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.util.Texture
import net.shadowfacts.cacao.view.NinePatchView
import net.shadowfacts.cacao.view.View import net.shadowfacts.cacao.view.View
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
@ -24,12 +19,6 @@ import net.shadowfacts.kiwidsl.dsl
*/ */
abstract class AbstractButton<Impl: AbstractButton<Impl>>(val content: View, val padding: Double = 4.0): View() { abstract class AbstractButton<Impl: AbstractButton<Impl>>(val content: View, val padding: Double = 4.0): View() {
companion object {
val DEFAULT_BG = NinePatchTexture(Texture(Identifier("textures/gui/widgets.png"), 0, 66), 3, 3, 194, 14)
val HOVERED_BG = NinePatchTexture(Texture(Identifier("textures/gui/widgets.png"), 0, 86), 3, 3, 194, 14)
val DISABLED_BG = NinePatchTexture(Texture(Identifier("textures/gui/widgets.png"), 0, 46), 3, 3, 194, 14)
}
/** /**
* The function that handles when this button is clicked. * The function that handles when this button is clicked.
* The parameter is the type of the concrete button implementation that was used. * The parameter is the type of the concrete button implementation that was used.
@ -51,24 +40,19 @@ abstract class AbstractButton<Impl: AbstractButton<Impl>>(val content: View, val
* If a [backgroundColor] is specified, it will be drawn behind the background View and thus not visible * If a [backgroundColor] is specified, it will be drawn behind the background View and thus not visible
* unless the background view is not fully opaque. * unless the background view is not fully opaque.
*/ */
var background: View? = NinePatchView(DEFAULT_BG) var background: View? = null
/** /**
* The background to draw when the button is hovered over by the mouse. * The background to draw when the button is hovered over by the mouse.
* If `null`, the normal [background] will be used. * If `null`, the normal [background] will be used.
* @see background * @see background
*/ */
var hoveredBackground: View? = NinePatchView(HOVERED_BG) var hoveredBackground: View? = null
/** /**
* The background to draw when the button is [disabled]. * The background to draw when the button is [disabled].
* If `null`, the normal [background] will be used. * If `null`, the normal [background] will be used.
* @see background * @see background
*/ */
var disabledBackground: View? = NinePatchView(DISABLED_BG) var disabledBackground: View? = null
/**
* If the button will play the Minecraft button click sound when clicked.
*/
var clickSoundEnabled = true
override fun wasAdded() { override fun wasAdded() {
solver.dsl { solver.dsl {
@ -125,10 +109,6 @@ abstract class AbstractButton<Impl: AbstractButton<Impl>>(val content: View, val
// For example, an implementing class may be defined as such: `class Button: AbstractButton<Button>` // For example, an implementing class may be defined as such: `class Button: AbstractButton<Button>`
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
handler(this as Impl) handler(this as Impl)
if (clickSoundEnabled && !RenderHelper.disabled) {
RenderHelper.playSound(SoundEvents.UI_BUTTON_CLICK)
}
} }
} }

View File

@ -1,45 +0,0 @@
package net.shadowfacts.cacao.view.button
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.EnumHelper
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.view.Label
/**
* A button that cycles through enum values.
* Left click: forwards
* Right click: backwards
* All other mouse buttons call the handler with the unchanged value
*
* @author shadowfacts
* @param initialValue The initial enum value for this button.
* @param localizer A function that takes an enum value and converts into a string for the button's label.
*/
class EnumButton<E: Enum<E>>(initialValue: E, val localizer: (E) -> String): AbstractButton<EnumButton<E>>(Label(localizer(initialValue))) {
private val label: Label
get() = content as Label
/**
* The current value of the enum button.
* Updating this property will use the [localizer] to update the label.
*/
var value: E = initialValue
set(value) {
field = value
label.text = localizer(value)
}
override fun mouseClicked(point: Point, mouseButton: MouseButton) {
if (!disabled) {
value = when (mouseButton) {
MouseButton.LEFT -> EnumHelper.next(value)
MouseButton.RIGHT -> EnumHelper.previous(value)
else -> value
}
}
super.mouseClicked(point, mouseButton)
}
}

View File

@ -1,46 +0,0 @@
package net.shadowfacts.cacao.view.button
import net.minecraft.util.Identifier
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.Texture
import net.shadowfacts.cacao.view.TextureView
/**
* A button for toggling between on/off states.
*
* @author shadowfacts
* @param initialState Whether the button starts as on or off.
*/
class ToggleButton(initialState: Boolean): AbstractButton<ToggleButton>(TextureView(if (initialState) ON else OFF).apply {
intrinsicContentSize = Size(19.0, 19.0)
}, padding = 0.0) {
companion object {
val ON = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 0)
val OFF = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 19)
}
private val textureView: TextureView
get() = content as TextureView
/**
* The button's current on/off state.
* Updating this property updates the button's texture.
*/
var state: Boolean = initialState
set(value) {
field = value
textureView.texture = if (value) ON else OFF
}
override fun mouseClicked(point: Point, mouseButton: MouseButton) {
if (!disabled && (mouseButton == MouseButton.LEFT || mouseButton == MouseButton.RIGHT)) {
state = !state
}
super.mouseClicked(point, mouseButton)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,27 +0,0 @@
package net.shadowfacts.cacao.util
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
/**
* @author shadowfacts
*/
class EnumHelperTests {
enum class MyEnum {
ONE, TWO
}
@Test
fun testNext() {
assertEquals(MyEnum.TWO, EnumHelper.next(MyEnum.ONE))
assertEquals(MyEnum.ONE, EnumHelper.next(MyEnum.TWO))
}
@Test
fun testPrev() {
assertEquals(MyEnum.ONE, EnumHelper.previous(MyEnum.TWO))
assertEquals(MyEnum.TWO, EnumHelper.previous(MyEnum.ONE))
}
}

View File

@ -1,10 +1,11 @@
package net.shadowfacts.cacao.view.button package net.shadowfacts.cacao.view
import net.shadowfacts.cacao.Window import net.shadowfacts.cacao.Window
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Rect
import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.MouseButton import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.view.View import net.shadowfacts.cacao.view.button.Button
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Assertions.assertTrue

View File

@ -1,93 +0,0 @@
package net.shadowfacts.cacao.view.button
import net.shadowfacts.cacao.Window
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Rect
import net.shadowfacts.cacao.util.MouseButton
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.util.concurrent.CompletableFuture
/**
* @author shadowfacts
*/
class EnumButtonTests {
companion object {
@BeforeAll
@JvmStatic
fun setupAll() {
System.setProperty("cacao.drawing.disabled", "true")
}
}
enum class MyEnum {
ONE, TWO, THREE
}
lateinit var window: Window
@BeforeEach
fun setup() {
window = Window()
}
@Test
fun testHandlerCalled() {
val called = CompletableFuture<Boolean>()
val button = window.addView(EnumButton(MyEnum.ONE, MyEnum::name).apply {
frame = Rect(0.0, 0.0, 25.0, 25.0)
content.frame = bounds
handler = {
called.complete(true)
}
})
window.mouseClicked(Point(5.0, 5.0), MouseButton.LEFT)
assertTrue(called.getNow(false))
assertEquals(MyEnum.TWO, button.value)
}
@Test
fun testCyclesValues() {
val button = window.addView(EnumButton(MyEnum.ONE, MyEnum::name).apply {
frame = Rect(0.0, 0.0, 25.0, 25.0)
content.frame = bounds
})
window.mouseClicked(Point(5.0, 5.0), MouseButton.LEFT)
assertEquals(MyEnum.TWO, button.value)
window.mouseClicked(Point(5.0, 5.0), MouseButton.LEFT)
assertEquals(MyEnum.THREE, button.value)
window.mouseClicked(Point(5.0, 5.0), MouseButton.LEFT)
assertEquals(MyEnum.ONE, button.value)
}
@Test
fun testCyclesValuesBackwards() {
val button = window.addView(EnumButton(MyEnum.ONE, MyEnum::name).apply {
frame = Rect(0.0, 0.0, 25.0, 25.0)
content.frame = Rect(0.0, 0.0, 25.0, 25.0)
})
window.mouseClicked(Point(5.0, 5.0), MouseButton.LEFT)
assertEquals(MyEnum.TWO, button.value)
window.mouseClicked(Point(5.0, 5.0), MouseButton.RIGHT)
assertEquals(MyEnum.ONE, button.value)
}
@Test
fun testMiddleClickDoesNotChangeValue() {
val button = window.addView(EnumButton(MyEnum.ONE, MyEnum::name).apply {
frame = Rect(0.0, 0.0, 25.0, 25.0)
content.frame = Rect(0.0, 0.0, 25.0, 25.0)
})
window.mouseClicked(Point(5.0, 5.0), MouseButton.MIDDLE)
assertEquals(MyEnum.ONE, button.value)
}
}

View File

@ -1,72 +0,0 @@
package net.shadowfacts.cacao.view.button
import net.shadowfacts.cacao.Window
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Rect
import net.shadowfacts.cacao.util.MouseButton
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.util.concurrent.CompletableFuture
/**
* @author shadowfacts
*/
class ToggleButtonTests {
companion object {
@BeforeAll
@JvmStatic
fun setupAll() {
System.setProperty("cacao.drawing.disabled", "true")
}
}
lateinit var window: Window
@BeforeEach
fun setup() {
window = Window()
}
@Test
fun testHandlerCalled() {
val called = CompletableFuture<Boolean>()
val button = window.addView(ToggleButton(false).apply {
frame = Rect(0.0, 0.0, 25.0, 25.0)
content.frame = bounds
handler = {
called.complete(true)
}
})
window.mouseClicked(Point(5.0, 5.0), MouseButton.LEFT)
assertTrue(called.getNow(false))
}
@Test
fun testTogglesValues() {
val button = window.addView(ToggleButton(false).apply {
frame = Rect(0.0, 0.0, 25.0, 25.0)
content.frame = bounds
})
window.mouseClicked(Point(5.0, 5.0), MouseButton.LEFT)
assertTrue(button.state)
window.mouseClicked(Point(5.0, 5.0), MouseButton.LEFT)
assertFalse(button.state)
}
@Test
fun testMiddleClickDoesNotChangeValue() {
val button = window.addView(ToggleButton(false).apply {
frame = Rect(0.0, 0.0, 25.0, 25.0)
content.frame = bounds
})
window.mouseClicked(Point(5.0, 5.0), MouseButton.MIDDLE)
assertFalse(button.state)
}
}