2019-06-22 19:02:17 +00:00
|
|
|
package net.shadowfacts.cacao.view
|
2019-06-22 18:59:37 +00:00
|
|
|
|
|
|
|
import net.shadowfacts.kiwidsl.dsl
|
2019-06-22 19:02:17 +00:00
|
|
|
import net.shadowfacts.cacao.LayoutVariable
|
|
|
|
import net.shadowfacts.cacao.geometry.Axis
|
|
|
|
import net.shadowfacts.cacao.geometry.AxisPosition
|
|
|
|
import net.shadowfacts.cacao.geometry.AxisPosition.*
|
2019-06-22 18:59:37 +00:00
|
|
|
import no.birkett.kiwi.Constraint
|
2019-06-22 20:08:00 +00:00
|
|
|
import java.util.*
|
2019-06-22 18:59:37 +00:00
|
|
|
|
|
|
|
/**
|
2019-06-22 20:08:00 +00:00
|
|
|
* 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.
|
|
|
|
*
|
2019-06-22 18:59:37 +00:00
|
|
|
* @author shadowfacts
|
2019-06-22 20:08:00 +00:00
|
|
|
* @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].
|
2019-06-22 18:59:37 +00:00
|
|
|
*/
|
2019-06-27 23:27:44 +00:00
|
|
|
class StackView(
|
|
|
|
val axis: Axis,
|
|
|
|
val distribution: Distribution = Distribution.FILL,
|
|
|
|
val spacing: Double = 0.0
|
|
|
|
): View() {
|
2019-06-22 18:59:37 +00:00
|
|
|
|
2019-06-22 20:08:00 +00:00
|
|
|
// 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
|
2019-06-22 18:59:37 +00:00
|
|
|
|
|
|
|
private var leadingConnection: Constraint? = null
|
|
|
|
private var trailingConnection: Constraint? = null
|
|
|
|
private var arrangedSubviewConnections = mutableListOf<Constraint>()
|
|
|
|
|
2019-06-22 20:08:00 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2019-06-22 18:59:37 +00:00
|
|
|
fun <T: View> addArrangedSubview(view: T, index: Int = arrangedSubviews.size): T {
|
|
|
|
addSubview(view)
|
2019-06-22 20:08:00 +00:00
|
|
|
_arrangedSubviews.add(index, view)
|
2019-06-22 18:59:37 +00:00
|
|
|
|
|
|
|
addConstraintsForArrangedView(view, index)
|
|
|
|
|
|
|
|
return view
|
|
|
|
}
|
|
|
|
|
2019-06-22 20:08:00 +00:00
|
|
|
private fun addConstraintsForArrangedView(view: View, index: Int) {
|
2019-06-22 18:59:37 +00:00
|
|
|
if (index == 0) {
|
|
|
|
if (leadingConnection != null) {
|
|
|
|
solver.removeConstraint(leadingConnection)
|
|
|
|
}
|
|
|
|
solver.dsl {
|
|
|
|
leadingConnection = anchor(LEADING) equalTo anchor(LEADING, view)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (index == arrangedSubviews.size - 1) {
|
|
|
|
if (trailingConnection != null) {
|
|
|
|
solver.removeConstraint(trailingConnection)
|
|
|
|
}
|
|
|
|
solver.dsl {
|
|
|
|
trailingConnection = anchor(TRAILING, view) equalTo anchor(TRAILING)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (arrangedSubviews.size > 1) {
|
|
|
|
solver.dsl {
|
|
|
|
val previous = arrangedSubviews.getOrNull(index - 1)
|
|
|
|
val next = arrangedSubviews.getOrNull(index + 1)
|
|
|
|
if (next != null) {
|
2019-06-27 23:27:44 +00:00
|
|
|
arrangedSubviewConnections.add(index, anchor(TRAILING, view) equalTo (anchor(LEADING, next) + spacing))
|
2019-06-22 18:59:37 +00:00
|
|
|
}
|
|
|
|
if (previous != null) {
|
2019-06-27 23:27:44 +00:00
|
|
|
arrangedSubviewConnections.add(index - 1, anchor(TRAILING, previous) equalTo (anchor(LEADING, view) - spacing))
|
2019-06-22 18:59:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
solver.dsl {
|
|
|
|
when (distribution) {
|
|
|
|
Distribution.LEADING ->
|
|
|
|
perpAnchor(LEADING) equalTo perpAnchor(LEADING, view)
|
|
|
|
Distribution.TRAILING ->
|
|
|
|
perpAnchor(TRAILING) equalTo perpAnchor(TRAILING, view)
|
|
|
|
Distribution.FILL -> {
|
|
|
|
perpAnchor(LEADING) equalTo perpAnchor(LEADING, view)
|
|
|
|
perpAnchor(TRAILING) equalTo perpAnchor(TRAILING, view)
|
|
|
|
}
|
|
|
|
Distribution.CENTER ->
|
|
|
|
perpAnchor(CENTER) equalTo perpAnchor(CENTER, view)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun anchor(position: AxisPosition, view: View = this): LayoutVariable {
|
|
|
|
return view.getAnchor(axis, position)
|
|
|
|
}
|
|
|
|
private fun perpAnchor(position: AxisPosition, view: View = this): LayoutVariable {
|
2019-06-22 20:08:00 +00:00
|
|
|
return view.getAnchor(axis.perpendicular, position)
|
2019-06-22 18:59:37 +00:00
|
|
|
}
|
|
|
|
|
2019-06-22 20:08:00 +00:00
|
|
|
/**
|
|
|
|
* 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].
|
|
|
|
*/
|
2019-06-22 18:59:37 +00:00
|
|
|
enum class Distribution {
|
2019-06-22 20:08:00 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2019-06-22 18:59:37 +00:00
|
|
|
}
|
|
|
|
}
|