Compare commits
No commits in common. "d750339c07c53c4cf97d04e7c09136d5d9db348d" and "cb66900cb5f52ead1e1284144faa31233f88c969" have entirely different histories.
d750339c07
...
cb66900cb5
|
@ -20,7 +20,7 @@ class TestCacaoScreen: CacaoScreen() {
|
|||
|
||||
init {
|
||||
addWindow(Window().apply {
|
||||
val stack = addView(StackView(Axis.VERTICAL, StackView.Distribution.CENTER, spacing = 4.0).apply {
|
||||
val stack = addView(StackView(Axis.VERTICAL, StackView.Distribution.CENTER).apply {
|
||||
backgroundColor = Color.WHITE
|
||||
})
|
||||
val red = stack.addArrangedSubview(TextureView(Texture(Identifier("textures/block/birch_log_top.png"), 0, 0, 16, 16)).apply {
|
||||
|
@ -41,23 +41,9 @@ class TestCacaoScreen: CacaoScreen() {
|
|||
).apply {
|
||||
handler = {
|
||||
println("dropdown value: ${it.value}")
|
||||
val dialog = DialogView(
|
||||
"New redstone mode",
|
||||
it.value.name,
|
||||
arrayOf(DialogView.DefaultButtonType.OK),
|
||||
Texture(Identifier("textures/block/birch_log_top.png"), 0, 0, 16, 16),
|
||||
buttonCallback = { _, window ->
|
||||
window.removeFromScreen()
|
||||
}
|
||||
)
|
||||
val dialogWindow = Window()
|
||||
dialogWindow.addView(dialog)
|
||||
this@TestCacaoScreen.addWindow(dialogWindow)
|
||||
}
|
||||
})
|
||||
|
||||
val label = addView(Label("test ".repeat(100), maxLines = 5))
|
||||
|
||||
solver.dsl {
|
||||
stack.topAnchor equalTo 0
|
||||
stack.centerXAnchor equalTo this@apply.centerXAnchor
|
||||
|
@ -65,12 +51,10 @@ class TestCacaoScreen: CacaoScreen() {
|
|||
purple.centerXAnchor equalTo blue.centerXAnchor
|
||||
purple.centerYAnchor equalTo blue.centerYAnchor
|
||||
// purple.widthAnchor equalTo 50
|
||||
|
||||
label.topAnchor equalTo 0
|
||||
label.leftAnchor equalTo 0
|
||||
label.widthAnchor equalTo 100
|
||||
}
|
||||
|
||||
layout()
|
||||
|
||||
|
||||
// val red = addView(View().apply {
|
||||
// backgroundColor = Color(0xff0000)
|
||||
|
|
|
@ -38,7 +38,6 @@ open class CacaoScreen: Screen(TextComponent("CacaoScreen")) {
|
|||
fun <T: Window> addWindow(window: T, index: Int = _windows.size): T {
|
||||
_windows.add(index, window)
|
||||
window.screen = this
|
||||
window.resize(width, height)
|
||||
return window
|
||||
}
|
||||
|
||||
|
@ -72,12 +71,16 @@ open class CacaoScreen: Screen(TextComponent("CacaoScreen")) {
|
|||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
val window = windows.lastOrNull()
|
||||
val result = window?.mouseClicked(Point(mouseX, mouseY), MouseButton.fromMC(button))
|
||||
return if (result == true) {
|
||||
when (result) {
|
||||
true ->
|
||||
RenderHelper.playSound(SoundEvents.UI_BUTTON_CLICK)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
false ->
|
||||
if (windows.size > 1) {
|
||||
removeWindow(windows.last())
|
||||
}
|
||||
}
|
||||
|
||||
return result == true
|
||||
}
|
||||
|
||||
}
|
|
@ -86,7 +86,7 @@ class Window {
|
|||
* Creates the internal constraints used by the window.
|
||||
* If overridden, the super-class method must be called.
|
||||
*/
|
||||
protected fun createInternalConstraints() {
|
||||
protected open fun createInternalConstraints() {
|
||||
solver.dsl {
|
||||
leftAnchor equalTo 0
|
||||
topAnchor equalTo 0
|
||||
|
@ -195,11 +195,6 @@ class Window {
|
|||
if (view != null) {
|
||||
val pointInView = Point(point.x - view.frame.left, point.y - view.frame.top)
|
||||
return view.mouseClicked(pointInView, mouseButton)
|
||||
} else {
|
||||
// remove the window from the screen when the mouse clicks outside the window and this is not the primary window
|
||||
if (screen.windows.size > 1) {
|
||||
removeFromScreen()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
package net.shadowfacts.cacao.view
|
||||
|
||||
import net.minecraft.ChatFormat
|
||||
import net.shadowfacts.cacao.Window
|
||||
import net.shadowfacts.cacao.geometry.Axis
|
||||
import net.shadowfacts.cacao.util.Color
|
||||
import net.shadowfacts.cacao.util.texture.NinePatchTexture
|
||||
import net.shadowfacts.cacao.util.texture.Texture
|
||||
import net.shadowfacts.cacao.view.button.Button
|
||||
import net.shadowfacts.kiwidsl.dsl
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class DialogView(
|
||||
val title: String,
|
||||
val message: String,
|
||||
val buttonTypes: Array<ButtonType>,
|
||||
val iconTexture: Texture?,
|
||||
val buttonCallback: (ButtonType, Window) -> Unit
|
||||
): View() {
|
||||
|
||||
interface ButtonType {
|
||||
val localizedName: String
|
||||
}
|
||||
|
||||
enum class DefaultButtonType: ButtonType {
|
||||
CANCEL, CONFIRM, OK, CLOSE;
|
||||
|
||||
override val localizedName: String
|
||||
get() = name.toLowerCase().capitalize() // todo: actually localize me
|
||||
}
|
||||
|
||||
private lateinit var background: NinePatchView
|
||||
private lateinit var hStack: StackView
|
||||
private var iconView: TextureView? = null
|
||||
private lateinit var vStack: StackView
|
||||
private lateinit var messageLabel: Label
|
||||
private var buttonContainer: View? = null
|
||||
private var buttonStack: StackView? = null
|
||||
|
||||
override fun wasAdded() {
|
||||
background = addSubview(NinePatchView(NinePatchTexture.PANEL_BG).apply { zIndex = -1.0 })
|
||||
|
||||
hStack = addSubview(StackView(Axis.HORIZONTAL, StackView.Distribution.LEADING, spacing = 8.0))
|
||||
|
||||
if (iconTexture != null) {
|
||||
iconView = hStack.addArrangedSubview(TextureView(iconTexture))
|
||||
}
|
||||
|
||||
vStack = hStack.addArrangedSubview(StackView(Axis.VERTICAL, spacing = 4.0))
|
||||
|
||||
vStack.addArrangedSubview(Label(ChatFormat.BOLD.toString() + title, shadow = false).apply {
|
||||
textColor = Color(0x404040)
|
||||
})
|
||||
messageLabel = vStack.addArrangedSubview(Label(message, shadow = false).apply {
|
||||
textColor = Color(0x404040)
|
||||
})
|
||||
|
||||
if (buttonTypes.isNotEmpty()) {
|
||||
buttonContainer = vStack.addArrangedSubview(View())
|
||||
buttonStack = buttonContainer!!.addSubview(StackView(Axis.HORIZONTAL))
|
||||
for (type in buttonTypes) {
|
||||
buttonStack!!.addArrangedSubview(Button(Label(type.localizedName)).apply {
|
||||
handler = {
|
||||
this@DialogView.buttonCallback(type, this@DialogView.window)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
super.wasAdded()
|
||||
}
|
||||
|
||||
override fun createInternalConstraints() {
|
||||
super.createInternalConstraints()
|
||||
|
||||
solver.dsl {
|
||||
centerXAnchor equalTo window.centerXAnchor
|
||||
centerYAnchor equalTo window.centerYAnchor
|
||||
|
||||
widthAnchor greaterThanOrEqualTo 175
|
||||
|
||||
background.leftAnchor equalTo leftAnchor - 8
|
||||
background.rightAnchor equalTo rightAnchor + 8
|
||||
background.topAnchor equalTo topAnchor - 8
|
||||
background.bottomAnchor equalTo bottomAnchor + 8
|
||||
|
||||
hStack.leftAnchor equalTo leftAnchor
|
||||
hStack.rightAnchor equalTo rightAnchor
|
||||
hStack.topAnchor equalTo topAnchor
|
||||
hStack.bottomAnchor equalTo bottomAnchor
|
||||
|
||||
if (iconView != null) {
|
||||
hStack.bottomAnchor greaterThanOrEqualTo iconView!!.bottomAnchor
|
||||
}
|
||||
hStack.bottomAnchor greaterThanOrEqualTo vStack.bottomAnchor
|
||||
|
||||
if (iconView != null) {
|
||||
iconView!!.widthAnchor equalTo 30
|
||||
iconView!!.heightAnchor equalTo 30
|
||||
}
|
||||
|
||||
messageLabel.heightAnchor greaterThanOrEqualTo 50
|
||||
|
||||
if (buttonContainer != null) {
|
||||
buttonStack!!.heightAnchor equalTo buttonContainer!!.heightAnchor
|
||||
buttonStack!!.centerYAnchor equalTo buttonContainer!!.centerYAnchor
|
||||
|
||||
buttonStack!!.rightAnchor equalTo buttonContainer!!.rightAnchor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -13,29 +13,14 @@ import net.shadowfacts.cacao.util.RenderHelper
|
|||
*
|
||||
* @author shadowfacts
|
||||
* @param text The text of this label.
|
||||
* @param shadow Whether the text should be rendered with a shadow or not.
|
||||
* @param maxLines The maximum number of lines of text to render.
|
||||
* `0` means that there is no line limit and all lines will be rendered.
|
||||
* When using a non-zero [maxLines] value, the [intrinsicContentSize] of the Label still assumes all
|
||||
* content on one line. So, constraints must be provided to calculate the actual width to use for line
|
||||
* wrapping.
|
||||
*/
|
||||
class Label(
|
||||
text: String,
|
||||
val shadow: Boolean = true,
|
||||
val maxLines: Int = 0,
|
||||
val wrappingMode: WrappingMode = WrappingMode.WRAP
|
||||
): View() {
|
||||
class Label(text: String): View() {
|
||||
|
||||
companion object {
|
||||
private val textRenderer: TextRenderer
|
||||
get() = MinecraftClient.getInstance().textRenderer
|
||||
}
|
||||
|
||||
enum class WrappingMode {
|
||||
WRAP, NO_WRAP
|
||||
}
|
||||
|
||||
/**
|
||||
* The text of this label. Mutating this field will update the intrinsic content size and trigger a layout.
|
||||
*/
|
||||
|
@ -43,20 +28,9 @@ class Label(
|
|||
set(value) {
|
||||
field = value
|
||||
updateIntrinsicContentSize()
|
||||
window.layout()
|
||||
}
|
||||
private lateinit var lines: List<String>
|
||||
|
||||
var textColor = Color.WHITE
|
||||
set(value) {
|
||||
field = value
|
||||
textColorARGB = value.argb
|
||||
}
|
||||
private var textColorARGB: Int = textColor.argb
|
||||
|
||||
private val drawFunc by lazy {
|
||||
if (shadow) textRenderer::drawWithShadow else textRenderer::draw
|
||||
}
|
||||
|
||||
override fun wasAdded() {
|
||||
super.wasAdded()
|
||||
|
@ -73,38 +47,7 @@ class Label(
|
|||
}
|
||||
|
||||
override fun drawContent(mouse: Point, delta: Float) {
|
||||
if (!this::lines.isInitialized) {
|
||||
computeLines()
|
||||
}
|
||||
|
||||
for (i in 0 until lines.size) {
|
||||
val y = i * textRenderer.fontHeight
|
||||
drawFunc(lines[i], 0f, y.toFloat(), textColorARGB)
|
||||
}
|
||||
}
|
||||
|
||||
override fun didLayout() {
|
||||
super.didLayout()
|
||||
|
||||
computeLines()
|
||||
}
|
||||
|
||||
private fun computeLines() {
|
||||
var lines = text.split("\n")
|
||||
if (wrappingMode == WrappingMode.WRAP) {
|
||||
lines = lines.flatMap {
|
||||
wrapStringToWidthAsList(it, bounds.width)
|
||||
}
|
||||
}
|
||||
if (0 < maxLines && maxLines < lines.size) {
|
||||
lines = lines.dropLast(lines.size - maxLines)
|
||||
}
|
||||
this.lines = lines
|
||||
}
|
||||
|
||||
private fun wrapStringToWidthAsList(string: String, width: Double): List<String> {
|
||||
if (RenderHelper.disabled) return listOf(string)
|
||||
return textRenderer.wrapStringToWidthAsList(string, width.toInt())
|
||||
textRenderer.draw(text, 0f, 0f, textColor.argb)
|
||||
}
|
||||
|
||||
}
|
|
@ -19,11 +19,7 @@ import java.util.*
|
|||
* @param distribution The mode by which this stack lays out its children along the axis perpendicular to the
|
||||
* primary [axis].
|
||||
*/
|
||||
class StackView(
|
||||
val axis: Axis,
|
||||
val distribution: Distribution = Distribution.FILL,
|
||||
val spacing: Double = 0.0
|
||||
): View() {
|
||||
class StackView(val axis: Axis, val distribution: Distribution = Distribution.FILL): View() {
|
||||
|
||||
// the internal mutable, list of arranged subviews
|
||||
private val _arrangedSubviews = LinkedList<View>()
|
||||
|
@ -79,10 +75,10 @@ class StackView(
|
|||
val previous = arrangedSubviews.getOrNull(index - 1)
|
||||
val next = arrangedSubviews.getOrNull(index + 1)
|
||||
if (next != null) {
|
||||
arrangedSubviewConnections.add(index, anchor(TRAILING, view) equalTo (anchor(LEADING, next) + spacing))
|
||||
arrangedSubviewConnections.add(index, anchor(TRAILING, view) equalTo anchor(LEADING, next))
|
||||
}
|
||||
if (previous != null) {
|
||||
arrangedSubviewConnections.add(index - 1, anchor(TRAILING, previous) equalTo (anchor(LEADING, view) - spacing))
|
||||
arrangedSubviewConnections.add(index - 1, anchor(TRAILING, previous) equalTo anchor(LEADING, view))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ class DropdownButton<Value, ContentView: View>(
|
|||
val dropdownBackground = dropdownWindow.addView(NinePatchView(NinePatchTexture.BUTTON_BG).apply {
|
||||
zIndex = -1.0
|
||||
})
|
||||
val stack = dropdownWindow.addView(StackView(Axis.VERTICAL, StackView.Distribution.FILL))
|
||||
val stack = dropdownWindow.addView(StackView(Axis.VERTICAL, StackView.Distribution.LEADING))
|
||||
lateinit var selectedButton: View
|
||||
val buttons = mutableListOf<Button>()
|
||||
val last = allValues.count() - 1
|
||||
|
@ -113,9 +113,7 @@ class DropdownButton<Value, ContentView: View>(
|
|||
}
|
||||
buttons.add(button)
|
||||
dropdownWindow.solver.dsl {
|
||||
if (button.content.intrinsicContentSize != null) {
|
||||
button.widthAnchor greaterThanOrEqualTo button.content.intrinsicContentSize!!.width + 2 * button.padding
|
||||
}
|
||||
stack.widthAnchor greaterThanOrEqualTo button.widthAnchor
|
||||
}
|
||||
}
|
||||
dropdownWindow.solver.dsl {
|
||||
|
@ -130,6 +128,12 @@ class DropdownButton<Value, ContentView: View>(
|
|||
dropdownBackground.bottomAnchor equalTo stack.bottomAnchor
|
||||
}
|
||||
dropdownWindow.layout()
|
||||
dropdownWindow.solver.dsl {
|
||||
buttons.forEach {
|
||||
it.widthAnchor equalTo stack.frame.width
|
||||
}
|
||||
}
|
||||
dropdownWindow.layout()
|
||||
}
|
||||
|
||||
private fun valueSelected(value: Value) {
|
||||
|
|
|
@ -191,57 +191,4 @@ class StackViewLayoutTests {
|
|||
assertEquals(100.0, three.rightAnchor.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testVerticalLayoutWithSpacing() {
|
||||
val stack = window.addView(StackView(Axis.VERTICAL, spacing = 10.0))
|
||||
val one = stack.addArrangedSubview(View().apply {
|
||||
intrinsicContentSize = Size(50.0, 50.0)
|
||||
})
|
||||
val two = stack.addArrangedSubview(View().apply {
|
||||
intrinsicContentSize = Size(75.0, 75.0)
|
||||
})
|
||||
val three = stack.addArrangedSubview(View().apply {
|
||||
intrinsicContentSize = Size(50.0, 50.0)
|
||||
})
|
||||
window.solver.dsl {
|
||||
stack.topAnchor equalTo 0
|
||||
}
|
||||
window.layout()
|
||||
|
||||
assertEquals(0.0, abs(one.topAnchor.value)) // sometimes -0.0, which fails the assertion but is actually ok
|
||||
assertEquals(50.0, one.bottomAnchor.value)
|
||||
assertEquals(60.0, two.topAnchor.value)
|
||||
assertEquals(135.0, two.bottomAnchor.value)
|
||||
assertEquals(145.0, three.topAnchor.value)
|
||||
assertEquals(195.0, three.bottomAnchor.value)
|
||||
|
||||
assertEquals(195.0, stack.heightAnchor.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHorizontalLayoutWithSpacing() {
|
||||
val stack = window.addView(StackView(Axis.HORIZONTAL, spacing = 10.0))
|
||||
val one = stack.addArrangedSubview(View().apply {
|
||||
intrinsicContentSize = Size(50.0, 50.0)
|
||||
})
|
||||
val two = stack.addArrangedSubview(View().apply {
|
||||
intrinsicContentSize = Size(75.0, 75.0)
|
||||
})
|
||||
val three = stack.addArrangedSubview(View().apply {
|
||||
intrinsicContentSize = Size(50.0, 50.0)
|
||||
})
|
||||
window.solver.dsl {
|
||||
stack.leftAnchor equalTo 0
|
||||
}
|
||||
window.layout()
|
||||
|
||||
assertEquals(0.0, abs(one.leftAnchor.value)) // sometimes -0.0, which fails the assertion but is actually ok
|
||||
assertEquals(50.0, one.rightAnchor.value)
|
||||
assertEquals(60.0, two.leftAnchor.value)
|
||||
assertEquals(135.0, two.rightAnchor.value)
|
||||
assertEquals(145.0, three.leftAnchor.value)
|
||||
assertEquals(195.0, three.rightAnchor.value)
|
||||
|
||||
assertEquals(195.0, stack.widthAnchor.value)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package net.shadowfacts.cacao.view
|
||||
|
||||
import net.shadowfacts.cacao.CacaoScreen
|
||||
import net.shadowfacts.cacao.Window
|
||||
import net.shadowfacts.cacao.geometry.Point
|
||||
import net.shadowfacts.cacao.geometry.Rect
|
||||
|
@ -15,13 +14,11 @@ import java.util.concurrent.CompletableFuture
|
|||
*/
|
||||
class ViewClickTests {
|
||||
|
||||
lateinit var screen: CacaoScreen
|
||||
lateinit var window: Window
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
screen = CacaoScreen()
|
||||
window = screen.addWindow(Window())
|
||||
window = Window()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package net.shadowfacts.cacao.view.button
|
||||
|
||||
import net.shadowfacts.cacao.CacaoScreen
|
||||
import net.shadowfacts.cacao.Window
|
||||
import net.shadowfacts.cacao.geometry.Point
|
||||
import net.shadowfacts.cacao.geometry.Size
|
||||
|
@ -18,13 +17,11 @@ import java.util.concurrent.CompletableFuture
|
|||
*/
|
||||
class ButtonClickTests {
|
||||
|
||||
lateinit var screen: CacaoScreen
|
||||
lateinit var window: Window
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
screen = CacaoScreen()
|
||||
window = screen.addWindow(Window())
|
||||
window = Window()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package net.shadowfacts.cacao.view.button
|
||||
|
||||
import net.shadowfacts.cacao.CacaoScreen
|
||||
import net.shadowfacts.cacao.Window
|
||||
import net.shadowfacts.cacao.geometry.Point
|
||||
import net.shadowfacts.cacao.geometry.Rect
|
||||
|
@ -29,13 +28,11 @@ class EnumButtonTests {
|
|||
ONE, TWO, THREE
|
||||
}
|
||||
|
||||
lateinit var screen: CacaoScreen
|
||||
lateinit var window: Window
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
screen = CacaoScreen()
|
||||
window = screen.addWindow(Window())
|
||||
window = Window()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue