Add documentation
This commit is contained in:
parent
7bfb18830c
commit
83bd29ecb8
|
@ -16,7 +16,7 @@ object ASMR: ModInitializer {
|
||||||
CommandRegistry.INSTANCE.register(false) { dispatcher ->
|
CommandRegistry.INSTANCE.register(false) { dispatcher ->
|
||||||
val command = CommandManager.literal("uitest").executes {
|
val command = CommandManager.literal("uitest").executes {
|
||||||
try {
|
try {
|
||||||
MinecraftClient.getInstance().openScreen(TestScreen())
|
MinecraftClient.getInstance().openScreen(TestCacaoScreen())
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package net.shadowfacts.asmr
|
package net.shadowfacts.asmr
|
||||||
|
|
||||||
import net.shadowfacts.kiwidsl.dsl
|
import net.shadowfacts.kiwidsl.dsl
|
||||||
import net.shadowfacts.cacao.Screen
|
import net.shadowfacts.cacao.CacaoScreen
|
||||||
import net.shadowfacts.cacao.view.View
|
import net.shadowfacts.cacao.view.View
|
||||||
import net.shadowfacts.cacao.Window
|
import net.shadowfacts.cacao.Window
|
||||||
import net.shadowfacts.cacao.geometry.Axis
|
import net.shadowfacts.cacao.geometry.Axis
|
||||||
|
@ -12,10 +12,10 @@ import net.shadowfacts.cacao.view.StackView
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class TestScreen: Screen() {
|
class TestCacaoScreen: CacaoScreen() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
windows.add(Window().apply {
|
addWindow(Window().apply {
|
||||||
val stack = addView(StackView(Axis.VERTICAL, StackView.Distribution.CENTER).apply {
|
val stack = addView(StackView(Axis.VERTICAL, StackView.Distribution.CENTER).apply {
|
||||||
backgroundColor = Color.WHITE
|
backgroundColor = Color.WHITE
|
||||||
})
|
})
|
|
@ -0,0 +1,43 @@
|
||||||
|
package net.shadowfacts.cacao
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.screen.Screen
|
||||||
|
import net.minecraft.network.chat.TextComponent
|
||||||
|
import net.shadowfacts.cacao.geometry.Point
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class serves as the bridge between Cacao and a Minecraft [Screen]. It renders Cacao [Window]s in Minecraft and
|
||||||
|
* sends input events from Minecraft back to Cacao objects.
|
||||||
|
*
|
||||||
|
* @author shadowfacts
|
||||||
|
*/
|
||||||
|
open class CacaoScreen: Screen(TextComponent("CacaoScreen")) {
|
||||||
|
|
||||||
|
// _windows is the internal, mutable object, since we only want it to by mutated by the add/removeWindow methods.
|
||||||
|
private val _windows = LinkedList<Window>()
|
||||||
|
/**
|
||||||
|
* The list of windows that belong to this screen.
|
||||||
|
* This list should never be modified directly, only by using the [addWindow]/[removeWindow] methods.
|
||||||
|
*/
|
||||||
|
val windows: List<Window> = _windows
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given window to this screen's window list.
|
||||||
|
* By default, the new window is added at the tail of the window list, making it the active window.
|
||||||
|
* Only the active window will receive input events.
|
||||||
|
*
|
||||||
|
* @param window The Window to add to this screen.
|
||||||
|
* @param index The index to insert the window into the window list at.
|
||||||
|
*/
|
||||||
|
fun addWindow(window: Window, index: Int = _windows.size) {
|
||||||
|
_windows.add(index, window)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
|
val mouse = Point(mouseX, mouseY)
|
||||||
|
windows.forEach {
|
||||||
|
it.draw(mouse, delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,9 @@ import net.shadowfacts.cacao.view.View
|
||||||
import no.birkett.kiwi.Variable
|
import no.birkett.kiwi.Variable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A Kiwi variable that belongs to a Cacao view.
|
||||||
|
* This class generally isn't used directly, but via the anchor *Anchor properties on [View].
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class LayoutVariable(val owner: View, val property: String): Variable("LayoutVariable") {
|
class LayoutVariable(val owner: View, val property: String): Variable("LayoutVariable") {
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
package net.shadowfacts.cacao
|
|
||||||
|
|
||||||
import net.minecraft.client.gui.screen.Screen
|
|
||||||
import net.minecraft.network.chat.TextComponent
|
|
||||||
import net.shadowfacts.cacao.geometry.Point
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author shadowfacts
|
|
||||||
*/
|
|
||||||
open class Screen: Screen(TextComponent("Screen")) {
|
|
||||||
|
|
||||||
val windows = mutableListOf<Window>()
|
|
||||||
|
|
||||||
fun addWindow(window: Window, index: Int? = null) {
|
|
||||||
if (index != null) {
|
|
||||||
windows.add(index, window)
|
|
||||||
} else {
|
|
||||||
windows.add(window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun render(mouseX: Int, mouseY: Int, delta: Float) {
|
|
||||||
val mouse = Point(mouseX, mouseY)
|
|
||||||
windows.forEach {
|
|
||||||
it.draw(mouse, delta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -3,18 +3,36 @@ package net.shadowfacts.cacao
|
||||||
import net.shadowfacts.cacao.geometry.Point
|
import net.shadowfacts.cacao.geometry.Point
|
||||||
import net.shadowfacts.cacao.view.View
|
import net.shadowfacts.cacao.view.View
|
||||||
import no.birkett.kiwi.Solver
|
import no.birkett.kiwi.Solver
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A Window is the object at the top of a Cacao view hierarchy. It occupies the entirety of the Minecraft screen size
|
||||||
|
* and provides the base coordinate system for its view hierarchy.
|
||||||
|
*
|
||||||
|
* The Window owns the Kiwi [Solver] object used for layout by all of its views.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class Window {
|
class Window {
|
||||||
|
|
||||||
var solver = Solver()
|
var solver = Solver()
|
||||||
|
|
||||||
val views = mutableListOf<View>()
|
// _views is the internal, mutable object, since we only want it to be mutated by the add/removeView methods
|
||||||
|
private val _views = LinkedList<View>()
|
||||||
|
/**
|
||||||
|
* The list of top-level views in this window.
|
||||||
|
* This list should never be modified directly, only by calling the [addView]/[removeView] methods.
|
||||||
|
*/
|
||||||
|
val views: List<View> = _views
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given view as a top-level view in this window.
|
||||||
|
*
|
||||||
|
* @param view The view to add.
|
||||||
|
* @return The same view, as a convenience.
|
||||||
|
*/
|
||||||
fun <T: View> addView(view: T): T {
|
fun <T: View> addView(view: T): T {
|
||||||
views.add(view)
|
_views.add(view)
|
||||||
view.solver = solver
|
view.solver = solver
|
||||||
|
|
||||||
view.wasAdded()
|
view.wasAdded()
|
||||||
|
@ -22,11 +40,22 @@ class Window {
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructs the solver to solve all of the provided constraints.
|
||||||
|
* Should be called after the view hierarchy is setup.
|
||||||
|
*/
|
||||||
fun layout() {
|
fun layout() {
|
||||||
solver.updateVariables()
|
solver.updateVariables()
|
||||||
views.forEach(View::didLayout)
|
views.forEach(View::didLayout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws this window and all of its views.
|
||||||
|
* This method is called by [CacaoScreen] and generally shouldn't be called directly.
|
||||||
|
*
|
||||||
|
* @param mouse The point in the coordinate system of the window.
|
||||||
|
* @param delta The time elapsed since the last frame.
|
||||||
|
*/
|
||||||
fun draw(mouse: Point, delta: Float) {
|
fun draw(mouse: Point, delta: Float) {
|
||||||
views.forEach(View::draw)
|
views.forEach(View::draw)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
package net.shadowfacts.cacao.geometry
|
package net.shadowfacts.cacao.geometry
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* An axis in a 2D coordinate plane.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
enum class Axis {
|
enum class Axis {
|
||||||
HORIZONTAL, VERTICAL;
|
HORIZONTAL, VERTICAL;
|
||||||
|
|
||||||
val other: Axis
|
/**
|
||||||
|
* Gets the axis that is perpendicular to this one.
|
||||||
|
*/
|
||||||
|
val perpendicular: Axis
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
HORIZONTAL -> VERTICAL
|
HORIZONTAL -> VERTICAL
|
||||||
VERTICAL -> HORIZONTAL
|
VERTICAL -> HORIZONTAL
|
||||||
|
|
|
@ -1,8 +1,21 @@
|
||||||
package net.shadowfacts.cacao.geometry
|
package net.shadowfacts.cacao.geometry
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A relative position on a line along an axis.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
enum class AxisPosition {
|
enum class AxisPosition {
|
||||||
LEADING, CENTER, TRAILING;
|
/**
|
||||||
|
* Top for vertical, left for horizontal.
|
||||||
|
*/
|
||||||
|
LEADING,
|
||||||
|
/**
|
||||||
|
* Center X/Y.
|
||||||
|
*/
|
||||||
|
CENTER,
|
||||||
|
/**
|
||||||
|
* Bottom for vertical, right for horizontal.
|
||||||
|
*/
|
||||||
|
TRAILING;
|
||||||
}
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package net.shadowfacts.cacao.geometry
|
package net.shadowfacts.cacao.geometry
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Helper class for defining 2D points.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
data class Point(val x: Double, val y: Double) {
|
data class Point(val x: Double, val y: Double) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package net.shadowfacts.cacao.geometry
|
package net.shadowfacts.cacao.geometry
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Helper class for defining rectangles. Provides helper values for calculating perpendicular components of a rectangle based on X/Y/W/H.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
data class Rect(val left: Double, val top: Double, val width: Double, val height: Double) {
|
data class Rect(val left: Double, val top: Double, val width: Double, val height: Double) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package net.shadowfacts.cacao.geometry
|
package net.shadowfacts.cacao.geometry
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Helper class for specifying the size of objects.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
data class Size(val width: Double, val height: Double)
|
data class Size(val width: Double, val height: Double)
|
|
@ -1,12 +1,24 @@
|
||||||
package net.shadowfacts.cacao.util
|
package net.shadowfacts.cacao.util
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Helper class for Cacao colors.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
|
* @param red The red component, from 0-255.
|
||||||
|
* @param green The green component, from 0-255.
|
||||||
|
* @param blue The blue component, from 0-255.
|
||||||
|
* @param alpha The alpha (i.e. transparency) component, from 0-255. (0 is transparent, 255 is opaque)
|
||||||
*/
|
*/
|
||||||
data class Color(val red: Int, val green: Int, val blue: Int, val alpha: Int = 255) {
|
data class Color(val red: Int, val green: Int, val blue: Int, val alpha: Int = 255) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a color from the packed RGB color.
|
||||||
|
*/
|
||||||
constructor(rgb: Int, alpha: Int = 255): this(rgb shr 16, (rgb shr 8) and 255, rgb and 255, alpha)
|
constructor(rgb: Int, alpha: Int = 255): this(rgb shr 16, (rgb shr 8) and 255, rgb and 255, alpha)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ARGB packed representation of this color.
|
||||||
|
*/
|
||||||
val argb: Int
|
val argb: Int
|
||||||
get() = ((alpha and 255) shl 24) or ((red and 255) shl 16) or ((green and 255) shl 8) or (blue and 255)
|
get() = ((alpha and 255) shl 24) or ((red and 255) shl 16) or ((green and 255) shl 8) or (blue and 255)
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,15 @@ import net.minecraft.client.gui.DrawableHelper
|
||||||
import net.shadowfacts.cacao.geometry.Rect
|
import net.shadowfacts.cacao.geometry.Rect
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Helper methods for rendering using Minecraft's utilities from Cacao views.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
object RenderHelper {
|
object RenderHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a solid [rect] filled with the given [color].
|
||||||
|
*/
|
||||||
fun fill(rect: Rect, color: Color) {
|
fun fill(rect: Rect, color: Color) {
|
||||||
DrawableHelper.fill(rect.left.toInt(), rect.top.toInt(), rect.right.toInt(), rect.bottom.toInt(), color.argb)
|
DrawableHelper.fill(rect.left.toInt(), rect.top.toInt(), rect.right.toInt(), rect.bottom.toInt(), color.argb)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,22 @@ import net.shadowfacts.cacao.geometry.Size
|
||||||
import net.shadowfacts.cacao.util.Color
|
import net.shadowfacts.cacao.util.Color
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A simple View that displays text. Allows for controlling the color and shadow of the text. Label cannot be used
|
||||||
|
* for multi-line text, instead use [TextView].
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
|
* @param text The text of this label.
|
||||||
*/
|
*/
|
||||||
class Label(text: String): View() {
|
class Label(text: String): View() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val textRenderer: TextRenderer
|
private val textRenderer: TextRenderer
|
||||||
get() = MinecraftClient.getInstance().textRenderer
|
get() = MinecraftClient.getInstance().textRenderer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text of this label. Mutating this field will update the intrinsic content size and trigger a layout.
|
||||||
|
*/
|
||||||
var text: String = text
|
var text: String = text
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
|
|
|
@ -6,29 +6,54 @@ import net.shadowfacts.cacao.geometry.Axis
|
||||||
import net.shadowfacts.cacao.geometry.AxisPosition
|
import net.shadowfacts.cacao.geometry.AxisPosition
|
||||||
import net.shadowfacts.cacao.geometry.AxisPosition.*
|
import net.shadowfacts.cacao.geometry.AxisPosition.*
|
||||||
import no.birkett.kiwi.Constraint
|
import no.birkett.kiwi.Constraint
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A view that lays out its children in a stack along either the horizontal for vertical axes.
|
||||||
|
* This view does not have any content of its own.
|
||||||
|
*
|
||||||
|
* Only arranged subviews will be laid out in the stack mode, normal subviews must perform their own layout.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
|
* @param axis The primary axis that this stack lays out its children along.
|
||||||
|
* @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): View() {
|
class StackView(val axis: Axis, val distribution: Distribution = Distribution.FILL): View() {
|
||||||
|
|
||||||
val arrangedSubviews = mutableListOf<View>()
|
// the internal mutable, list of arranged subviews
|
||||||
|
private val _arrangedSubviews = LinkedList<View>()
|
||||||
|
/**
|
||||||
|
* The list of arranged subviews belonging to this stack view.
|
||||||
|
* This list should never be mutated directly, only be calling the [addArrangedSubview]/[removeArrangedSubview]
|
||||||
|
* methods.
|
||||||
|
*/
|
||||||
|
val arrangedSubviews: List<View> = _arrangedSubviews
|
||||||
|
|
||||||
private var leadingConnection: Constraint? = null
|
private var leadingConnection: Constraint? = null
|
||||||
private var trailingConnection: Constraint? = null
|
private var trailingConnection: Constraint? = null
|
||||||
private var arrangedSubviewConnections = mutableListOf<Constraint>()
|
private var arrangedSubviewConnections = mutableListOf<Constraint>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an arranged subview to this view.
|
||||||
|
* Arranged subviews are laid out according to the stack. If you wish to add a subview that is laid out separately,
|
||||||
|
* use the normal [addSubview] method.
|
||||||
|
*
|
||||||
|
* @param view The view to add.
|
||||||
|
* @param index The index in this stack to add the view at.
|
||||||
|
* By default, adds the view to the end of the stack.
|
||||||
|
* @return The view that was added, as a convenience.
|
||||||
|
*/
|
||||||
fun <T: View> addArrangedSubview(view: T, index: Int = arrangedSubviews.size): T {
|
fun <T: View> addArrangedSubview(view: T, index: Int = arrangedSubviews.size): T {
|
||||||
addSubview(view)
|
addSubview(view)
|
||||||
|
_arrangedSubviews.add(index, view)
|
||||||
arrangedSubviews.add(index, view)
|
|
||||||
|
|
||||||
addConstraintsForArrangedView(view, index)
|
addConstraintsForArrangedView(view, index)
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addConstraintsForArrangedView(view: View, index: Int) {
|
private fun addConstraintsForArrangedView(view: View, index: Int) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
if (leadingConnection != null) {
|
if (leadingConnection != null) {
|
||||||
solver.removeConstraint(leadingConnection)
|
solver.removeConstraint(leadingConnection)
|
||||||
|
@ -77,10 +102,107 @@ class StackView(val axis: Axis, val distribution: Distribution = Distribution.FI
|
||||||
return view.getAnchor(axis, position)
|
return view.getAnchor(axis, position)
|
||||||
}
|
}
|
||||||
private fun perpAnchor(position: AxisPosition, view: View = this): LayoutVariable {
|
private fun perpAnchor(position: AxisPosition, view: View = this): LayoutVariable {
|
||||||
return view.getAnchor(axis.other, position)
|
return view.getAnchor(axis.perpendicular, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the modes of how content is distributed in a stack view along the perpendicular axis (i.e. the
|
||||||
|
* non-primary axis).
|
||||||
|
*
|
||||||
|
* ASCII-art examples are shown below in a stack view with the primary axis [Axis.VERTICAL].
|
||||||
|
*/
|
||||||
enum class Distribution {
|
enum class Distribution {
|
||||||
FILL, LEADING, CENTER, TRAILING
|
/**
|
||||||
|
* The leading edges of arranged subviews are pinned to the leading edge of the stack view.
|
||||||
|
* ```
|
||||||
|
* ┌─────────────────────────────┐
|
||||||
|
* │┌─────────────┐ │
|
||||||
|
* ││ │ │
|
||||||
|
* ││ │ │
|
||||||
|
* ││ │ │
|
||||||
|
* │└─────────────┘ │
|
||||||
|
* │┌─────────┐ │
|
||||||
|
* ││ │ │
|
||||||
|
* ││ │ │
|
||||||
|
* ││ │ │
|
||||||
|
* │└─────────┘ │
|
||||||
|
* │┌───────────────────────────┐│
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* │└───────────────────────────┘│
|
||||||
|
* └─────────────────────────────┘
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
LEADING,
|
||||||
|
/**
|
||||||
|
* The centers of the arranged subviews are pinned to the center of the stack view.
|
||||||
|
* ```
|
||||||
|
* ┌─────────────────────────────┐
|
||||||
|
* │ ┌─────────────┐ │
|
||||||
|
* │ │ │ │
|
||||||
|
* │ │ │ │
|
||||||
|
* │ │ │ │
|
||||||
|
* │ └─────────────┘ │
|
||||||
|
* │ ┌─────────┐ │
|
||||||
|
* │ │ │ │
|
||||||
|
* │ │ │ │
|
||||||
|
* │ │ │ │
|
||||||
|
* │ └─────────┘ │
|
||||||
|
* │┌───────────────────────────┐│
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* │└───────────────────────────┘│
|
||||||
|
* └─────────────────────────────┘
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
CENTER,
|
||||||
|
/**
|
||||||
|
* The trailing edges of arranged subviews are pinned to the leading edge of the stack view.
|
||||||
|
* ```
|
||||||
|
* ┌─────────────────────────────┐
|
||||||
|
* │ ┌─────────────┐│
|
||||||
|
* │ │ ││
|
||||||
|
* │ │ ││
|
||||||
|
* │ │ ││
|
||||||
|
* │ └─────────────┘│
|
||||||
|
* │ ┌─────────┐│
|
||||||
|
* │ │ ││
|
||||||
|
* │ │ ││
|
||||||
|
* │ │ ││
|
||||||
|
* │ └─────────┘│
|
||||||
|
* │┌───────────────────────────┐│
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* │└───────────────────────────┘│
|
||||||
|
* └─────────────────────────────┘
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
TRAILING,
|
||||||
|
/**
|
||||||
|
* The arranged subviews fill the perpendicular axis of the stack view.
|
||||||
|
* ```
|
||||||
|
* ┌─────────────────────────────┐
|
||||||
|
* │┌───────────────────────────┐│
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* │└───────────────────────────┘│
|
||||||
|
* │┌───────────────────────────┐│
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* │└───────────────────────────┘│
|
||||||
|
* │┌───────────────────────────┐│
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* ││ ││
|
||||||
|
* │└───────────────────────────┘│
|
||||||
|
* └─────────────────────────────┘
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
FILL
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,29 +11,73 @@ import net.shadowfacts.cacao.util.Color
|
||||||
import net.shadowfacts.cacao.util.RenderHelper
|
import net.shadowfacts.cacao.util.RenderHelper
|
||||||
import no.birkett.kiwi.Constraint
|
import no.birkett.kiwi.Constraint
|
||||||
import no.birkett.kiwi.Solver
|
import no.birkett.kiwi.Solver
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The base Cacao View class. Provides layout anchors, properties, and helper methods.
|
||||||
|
* Doesn't draw anything itself (unless [backgroundColor] is specified), but may be used for encapsulation/grouping.
|
||||||
|
*
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
open class View {
|
open class View {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constraint solver used by the [net.shadowfacts.cacao.Window] this view belongs to.
|
||||||
|
* Not initialized until [wasAdded] called, using it before that will throw a runtime exception.
|
||||||
|
*/
|
||||||
lateinit var solver: Solver
|
lateinit var solver: Solver
|
||||||
|
|
||||||
// in the coordinate system of the screen
|
/**
|
||||||
|
* Layout anchor for the left edge of this view in the window's coordinate system.
|
||||||
|
*/
|
||||||
val leftAnchor = LayoutVariable(this, "left")
|
val leftAnchor = LayoutVariable(this, "left")
|
||||||
|
/**
|
||||||
|
* Layout anchor for the right edge of this view in the window's coordinate system.
|
||||||
|
*/
|
||||||
val rightAnchor = LayoutVariable(this, "right")
|
val rightAnchor = LayoutVariable(this, "right")
|
||||||
|
/**
|
||||||
|
* Layout anchor for the top edge of this view in the window's coordinate system.
|
||||||
|
*/
|
||||||
val topAnchor = LayoutVariable(this, "top")
|
val topAnchor = LayoutVariable(this, "top")
|
||||||
|
/**
|
||||||
|
* Layout anchor for the bottom edge of this view in the window's coordinate system.
|
||||||
|
*/
|
||||||
val bottomAnchor = LayoutVariable(this, "bottom")
|
val bottomAnchor = LayoutVariable(this, "bottom")
|
||||||
|
/**
|
||||||
|
* Layout anchor for the width of this view in the window's coordinate system.
|
||||||
|
*/
|
||||||
val widthAnchor = LayoutVariable(this, "width")
|
val widthAnchor = LayoutVariable(this, "width")
|
||||||
|
/**
|
||||||
|
* Layout anchor for the height of this view in the window's coordinate system.
|
||||||
|
*/
|
||||||
val heightAnchor = LayoutVariable(this, "height")
|
val heightAnchor = LayoutVariable(this, "height")
|
||||||
|
/**
|
||||||
|
* Layout anchor for the center X position of this view in the window's coordinate system.
|
||||||
|
*/
|
||||||
val centerXAnchor = LayoutVariable(this, "centerX")
|
val centerXAnchor = LayoutVariable(this, "centerX")
|
||||||
|
/**
|
||||||
|
* Layout anchor for the center Y position of this view in the window's coordinate system.
|
||||||
|
*/
|
||||||
val centerYAnchor = LayoutVariable(this, "centerY")
|
val centerYAnchor = LayoutVariable(this, "centerY")
|
||||||
|
|
||||||
// The rectangle for this view in the coordinate system of the parent view.
|
/**
|
||||||
|
* The rectangle for this view in the coordinate system of its superview view (or the window, if there is no superview).
|
||||||
|
* Not initialized until [didLayout] called.
|
||||||
|
*/
|
||||||
lateinit var frame: Rect
|
lateinit var frame: Rect
|
||||||
// The rectangle for this view in its own coordinate system.
|
/**
|
||||||
|
* The rectangle for this view in its own coordinate system.
|
||||||
|
* Not initialized until [didLayout] called.
|
||||||
|
*/
|
||||||
lateinit var bounds: Rect
|
lateinit var bounds: Rect
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The intrinsic size of this view's content. May be null if the view doesn't have any content or there is no
|
||||||
|
* intrinsic size.
|
||||||
|
*
|
||||||
|
* Setting this creates/updates [no.birkett.kiwi.Strength.WEAK] constraints on this view's width/height using
|
||||||
|
* the size.
|
||||||
|
*/
|
||||||
var intrinsicContentSize: Size? = null
|
var intrinsicContentSize: Size? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
updateIntrinsicContentSizeConstraints(intrinsicContentSize, value)
|
updateIntrinsicContentSizeConstraints(intrinsicContentSize, value)
|
||||||
|
@ -42,11 +86,26 @@ open class View {
|
||||||
private var intrinsicContentSizeWidthConstraint: Constraint? = null
|
private var intrinsicContentSizeWidthConstraint: Constraint? = null
|
||||||
private var intrinsicContentSizeHeightConstraint: Constraint? = null
|
private var intrinsicContentSizeHeightConstraint: Constraint? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The background color of this view.
|
||||||
|
*/
|
||||||
var backgroundColor = Color.CLEAR
|
var backgroundColor = Color.CLEAR
|
||||||
|
|
||||||
var parent: View? = null
|
/**
|
||||||
val subviews = mutableListOf<View>()
|
* This view's parent view. If `null`, this view is a top-level view in the [Window].
|
||||||
|
*/
|
||||||
|
var superview: View? = null
|
||||||
|
// _subviews is the internal, mutable object since we only want it to by mutated by the add/removeSubview methods
|
||||||
|
private val _subviews = LinkedList<View>()
|
||||||
|
/**
|
||||||
|
* The list of all the subviews of this view.
|
||||||
|
* This list should never by mutated directly, only by the [addSubview]/[removeSubview] methods.
|
||||||
|
*/
|
||||||
|
val subviews: List<View> = _subviews
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for retrieve the anchor for a specific position on the given axis.
|
||||||
|
*/
|
||||||
fun getAnchor(axis: Axis, position: AxisPosition): LayoutVariable {
|
fun getAnchor(axis: Axis, position: AxisPosition): LayoutVariable {
|
||||||
return when (axis) {
|
return when (axis) {
|
||||||
Axis.HORIZONTAL ->
|
Axis.HORIZONTAL ->
|
||||||
|
@ -64,9 +123,15 @@ open class View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addSubview(view: View): View {
|
/**
|
||||||
subviews.add(view)
|
* Adds the given subview as a child of this view.
|
||||||
view.parent = this
|
*
|
||||||
|
* @param view The view to add.
|
||||||
|
* @return The view that was added, as a convenience.
|
||||||
|
*/
|
||||||
|
fun <T: View> addSubview(view: T): T {
|
||||||
|
_subviews.add(view)
|
||||||
|
view.superview = this
|
||||||
view.solver = solver
|
view.solver = solver
|
||||||
|
|
||||||
view.wasAdded()
|
view.wasAdded()
|
||||||
|
@ -74,11 +139,19 @@ open class View {
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when this view was added to a view hierarchy.
|
||||||
|
* If overridden, the super-class method must be called.
|
||||||
|
*/
|
||||||
open fun wasAdded() {
|
open fun wasAdded() {
|
||||||
createInternalConstraints()
|
createInternalConstraints()
|
||||||
updateIntrinsicContentSizeConstraints(null, intrinsicContentSize)
|
updateIntrinsicContentSizeConstraints(null, intrinsicContentSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called during [wasAdded] to add any constraints to the [solver] that are internal to this view.
|
||||||
|
* If overridden, the super-class method must be called.
|
||||||
|
*/
|
||||||
open fun createInternalConstraints() {
|
open fun createInternalConstraints() {
|
||||||
solver.dsl {
|
solver.dsl {
|
||||||
rightAnchor equalTo (leftAnchor + widthAnchor)
|
rightAnchor equalTo (leftAnchor + widthAnchor)
|
||||||
|
@ -103,15 +176,24 @@ open class View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after this view has been laid-out.
|
||||||
|
* If overridden, the super-class method must be called.
|
||||||
|
*/
|
||||||
open fun didLayout() {
|
open fun didLayout() {
|
||||||
subviews.forEach(View::didLayout)
|
subviews.forEach(View::didLayout)
|
||||||
|
|
||||||
val parentLeft = parent?.leftAnchor?.value ?: 0.0
|
val superviewLeft = superview?.leftAnchor?.value ?: 0.0
|
||||||
val parentTop = parent?.topAnchor?.value ?: 0.0
|
val superviewTop = superview?.topAnchor?.value ?: 0.0
|
||||||
frame = Rect(leftAnchor.value - parentLeft, topAnchor.value - parentTop, widthAnchor.value, heightAnchor.value)
|
frame = Rect(leftAnchor.value - superviewLeft, topAnchor.value - superviewTop, widthAnchor.value, heightAnchor.value)
|
||||||
bounds = Rect(0.0, 0.0, widthAnchor.value, heightAnchor.value)
|
bounds = Rect(0.0, 0.0, widthAnchor.value, heightAnchor.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to draw this view.
|
||||||
|
* This method should not be called directly, it is called by the parent view/window.
|
||||||
|
* This method may not be overridden, use [drawContent] to draw any custom content.
|
||||||
|
*/
|
||||||
fun draw() {
|
fun draw() {
|
||||||
GlStateManager.pushMatrix()
|
GlStateManager.pushMatrix()
|
||||||
GlStateManager.translated(frame.left, frame.top, 0.0)
|
GlStateManager.translated(frame.left, frame.top, 0.0)
|
||||||
|
@ -125,6 +207,11 @@ open class View {
|
||||||
GlStateManager.popMatrix()
|
GlStateManager.popMatrix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called during [draw] to draw content that's part of this view.
|
||||||
|
* During this method, the OpenGL coordinate system has been translated so the origin is at the top left corner
|
||||||
|
* of this view. Be careful not to translate additionally, and not to draw outside the [bounds] of the view.
|
||||||
|
*/
|
||||||
open fun drawContent() {}
|
open fun drawContent() {}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue