2019-06-23 21:56:49 +00:00
|
|
|
package net.shadowfacts.cacao.view.button
|
|
|
|
|
2019-06-24 01:22:54 +00:00
|
|
|
import net.minecraft.sound.SoundEvents
|
|
|
|
import net.minecraft.util.Identifier
|
2019-06-23 21:56:49 +00:00
|
|
|
import net.shadowfacts.cacao.geometry.Point
|
|
|
|
import net.shadowfacts.cacao.util.MouseButton
|
2019-06-24 01:22:54 +00:00
|
|
|
import net.shadowfacts.cacao.util.NinePatchTexture
|
2019-06-23 21:56:49 +00:00
|
|
|
import net.shadowfacts.cacao.util.RenderHelper
|
2019-06-24 01:22:54 +00:00
|
|
|
import net.shadowfacts.cacao.util.Texture
|
|
|
|
import net.shadowfacts.cacao.view.NinePatchView
|
2019-06-23 21:56:49 +00:00
|
|
|
import net.shadowfacts.cacao.view.View
|
|
|
|
import net.shadowfacts.kiwidsl.dsl
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A abstract button class. Cannot be constructed directly, used for creating button implementations with their own logic.
|
|
|
|
* Use [Button] for a generic no-frills button.
|
|
|
|
*
|
|
|
|
* @author shadowfacts
|
|
|
|
* @param Impl The type of the concrete implementation of the button.
|
|
|
|
* Used to allow the [handler] to receive the exact button type that was constructed.
|
|
|
|
* @param content The [View] that provides the content of this button.
|
|
|
|
* Will be added as a subview of the button and laid out using constraints.
|
|
|
|
* @param padding The padding between the [content] and the edges of the button.
|
|
|
|
*/
|
|
|
|
abstract class AbstractButton<Impl: AbstractButton<Impl>>(val content: View, val padding: Double = 4.0): View() {
|
|
|
|
|
2019-06-24 01:22:54 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2019-06-23 21:56:49 +00:00
|
|
|
/**
|
|
|
|
* The function that handles when this button is clicked.
|
|
|
|
* The parameter is the type of the concrete button implementation that was used.
|
|
|
|
*/
|
|
|
|
var handler: ((Impl) -> Unit)? = null
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether the button is disabled.
|
|
|
|
* Disabled buttons have a different background ([disabledBackground]) and do not receive click events.
|
|
|
|
*/
|
|
|
|
var disabled = false
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The normal background view to draw behind the button content. It will be added as a subview during [wasAdded],
|
|
|
|
* so all background view properties must be specified prior to the button being added to a view hierarchy.
|
|
|
|
*
|
|
|
|
* The background will fill the entire button (going beneath the content [padding]).
|
|
|
|
* There are also [hoveredBackground] and [disabledBackground] for those states.
|
|
|
|
* 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.
|
|
|
|
*/
|
2019-06-24 01:22:54 +00:00
|
|
|
var background: View? = NinePatchView(DEFAULT_BG)
|
2019-06-23 21:56:49 +00:00
|
|
|
/**
|
|
|
|
* The background to draw when the button is hovered over by the mouse.
|
|
|
|
* If `null`, the normal [background] will be used.
|
|
|
|
* @see background
|
|
|
|
*/
|
2019-06-24 01:22:54 +00:00
|
|
|
var hoveredBackground: View? = NinePatchView(HOVERED_BG)
|
2019-06-23 21:56:49 +00:00
|
|
|
/**
|
|
|
|
* The background to draw when the button is [disabled].
|
|
|
|
* If `null`, the normal [background] will be used.
|
|
|
|
* @see background
|
|
|
|
*/
|
2019-06-24 01:22:54 +00:00
|
|
|
var disabledBackground: View? = NinePatchView(DISABLED_BG)
|
|
|
|
|
2019-06-23 21:56:49 +00:00
|
|
|
override fun wasAdded() {
|
|
|
|
solver.dsl {
|
|
|
|
addSubview(content)
|
|
|
|
content.leftAnchor equalTo (leftAnchor + padding)
|
|
|
|
content.rightAnchor equalTo (rightAnchor - padding)
|
|
|
|
content.topAnchor equalTo (topAnchor + padding)
|
|
|
|
content.bottomAnchor equalTo (bottomAnchor - padding)
|
|
|
|
|
|
|
|
listOfNotNull(background, hoveredBackground, disabledBackground).forEach {
|
|
|
|
addSubview(it)
|
|
|
|
it.leftAnchor equalTo leftAnchor
|
|
|
|
it.rightAnchor equalTo rightAnchor
|
|
|
|
it.topAnchor equalTo topAnchor
|
|
|
|
it.bottomAnchor equalTo bottomAnchor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
super.wasAdded()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun draw(mouse: Point, delta: Float) {
|
|
|
|
RenderHelper.pushMatrix()
|
|
|
|
RenderHelper.translate(frame.left, frame.top)
|
|
|
|
|
|
|
|
RenderHelper.fill(bounds, backgroundColor)
|
|
|
|
|
|
|
|
var currentBackground: View? = background
|
|
|
|
if (mouse in bounds) {
|
|
|
|
currentBackground = hoveredBackground ?: currentBackground
|
|
|
|
}
|
|
|
|
if (disabled) {
|
|
|
|
currentBackground = disabledBackground ?: currentBackground
|
|
|
|
}
|
|
|
|
// don't need to convert mouse to background coordinate system
|
|
|
|
// the edges are all pinned, so the coordinate space is the same
|
|
|
|
currentBackground?.draw(mouse, delta)
|
|
|
|
|
|
|
|
val mouseInContent = convert(mouse, to = content)
|
|
|
|
content.draw(mouseInContent, delta)
|
|
|
|
|
|
|
|
// don't draw subviews, otherwise all background views + content will get drawn
|
|
|
|
|
|
|
|
RenderHelper.popMatrix()
|
|
|
|
}
|
|
|
|
|
2019-06-24 02:34:12 +00:00
|
|
|
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
|
|
|
|
if (disabled) return false
|
2019-06-23 21:56:49 +00:00
|
|
|
|
|
|
|
val handler = handler
|
|
|
|
if (handler != null) {
|
|
|
|
// We can perform an unchecked cast here because we are certain that Impl will be the concrete implementation
|
|
|
|
// of AbstractButton.
|
|
|
|
// For example, an implementing class may be defined as such: `class Button: AbstractButton<Button>`
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
handler(this as Impl)
|
|
|
|
}
|
2019-06-24 02:34:12 +00:00
|
|
|
|
|
|
|
return true
|
2019-06-23 21:56:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|