package net.shadowfacts.cacao.view import com.mojang.blaze3d.platform.GlStateManager import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.cacao.LayoutVariable import net.shadowfacts.cacao.geometry.Axis import net.shadowfacts.cacao.geometry.AxisPosition import net.shadowfacts.cacao.geometry.Rect import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.RenderHelper import no.birkett.kiwi.Constraint import no.birkett.kiwi.Solver /** * @author shadowfacts */ open class View { lateinit var solver: Solver // in the coordinate system of the screen val leftAnchor = LayoutVariable(this, "left") val rightAnchor = LayoutVariable(this, "right") val topAnchor = LayoutVariable(this, "top") val bottomAnchor = LayoutVariable(this, "bottom") val widthAnchor = LayoutVariable(this, "width") val heightAnchor = LayoutVariable(this, "height") val centerXAnchor = LayoutVariable(this, "centerX") val centerYAnchor = LayoutVariable(this, "centerY") // The rectangle for this view in the coordinate system of the parent view. lateinit var frame: Rect // The rectangle for this view in its own coordinate system. lateinit var bounds: Rect var intrinsicContentSize: Size? = null set(value) { updateIntrinsicContentSizeConstraints(intrinsicContentSize, value) field = value } private var intrinsicContentSizeWidthConstraint: Constraint? = null private var intrinsicContentSizeHeightConstraint: Constraint? = null var backgroundColor = Color.CLEAR var parent: View? = null val subviews = mutableListOf() fun getAnchor(axis: Axis, position: AxisPosition): LayoutVariable { return when (axis) { Axis.HORIZONTAL -> when (position) { AxisPosition.LEADING -> leftAnchor AxisPosition.CENTER -> centerXAnchor AxisPosition.TRAILING -> rightAnchor } Axis.VERTICAL -> when (position) { AxisPosition.LEADING -> topAnchor AxisPosition.CENTER -> centerYAnchor AxisPosition.TRAILING -> bottomAnchor } } } fun addSubview(view: View): View { subviews.add(view) view.parent = this view.solver = solver view.wasAdded() return view } open fun wasAdded() { createInternalConstraints() updateIntrinsicContentSizeConstraints(null, intrinsicContentSize) } open fun createInternalConstraints() { solver.dsl { rightAnchor equalTo (leftAnchor + widthAnchor) bottomAnchor equalTo (topAnchor + heightAnchor) centerXAnchor equalTo (leftAnchor + widthAnchor / 2) centerYAnchor equalTo (topAnchor + heightAnchor / 2) } } private fun updateIntrinsicContentSizeConstraints(old: Size?, new: Size?) { if (!this::solver.isInitialized) return if (old != null) { solver.removeConstraint(intrinsicContentSizeWidthConstraint!!) solver.removeConstraint(intrinsicContentSizeHeightConstraint!!) } if (new != null) { solver.dsl { this@View.intrinsicContentSizeWidthConstraint = (widthAnchor.equalTo(new.width, strength = WEAK)) this@View.intrinsicContentSizeHeightConstraint = (heightAnchor.equalTo(new.height, strength = WEAK)) } } } open fun didLayout() { subviews.forEach(View::didLayout) val parentLeft = parent?.leftAnchor?.value ?: 0.0 val parentTop = parent?.topAnchor?.value ?: 0.0 frame = Rect(leftAnchor.value - parentLeft, topAnchor.value - parentTop, widthAnchor.value, heightAnchor.value) bounds = Rect(0.0, 0.0, widthAnchor.value, heightAnchor.value) } fun draw() { GlStateManager.pushMatrix() GlStateManager.translated(frame.left, frame.top, 0.0) RenderHelper.fill(bounds, backgroundColor) drawContent() subviews.forEach(View::draw) GlStateManager.popMatrix() } open fun drawContent() {} }