Cacao: Add TabViewController
This commit is contained in:
parent
d20ba7460e
commit
277bcb71ee
|
@ -11,6 +11,7 @@ import no.birkett.kiwi.Constraint
|
||||||
import no.birkett.kiwi.Solver
|
import no.birkett.kiwi.Solver
|
||||||
import java.lang.RuntimeException
|
import java.lang.RuntimeException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.HashSet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base Cacao View class. Provides layout anchors, properties, and helper methods.
|
* The base Cacao View class. Provides layout anchors, properties, and helper methods.
|
||||||
|
@ -27,11 +28,16 @@ open class View() {
|
||||||
*/
|
*/
|
||||||
var window: Window? = null
|
var window: Window? = null
|
||||||
|
|
||||||
|
private val solverDelegate = ObservableLateInitProperty<Solver> {
|
||||||
|
for (v in subviews) {
|
||||||
|
v.solver = it
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* The constraint solver used by the [net.shadowfacts.cacao.Window] this view belongs to.
|
* 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.
|
* Not initialized until [wasAdded] called, using it before that will throw a runtime exception.
|
||||||
*/
|
*/
|
||||||
lateinit var solver: Solver
|
var solver: Solver by solverDelegate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layout anchor for the left edge of this view in the window's coordinate system.
|
* Layout anchor for the left edge of this view in the window's coordinate system.
|
||||||
|
@ -167,7 +173,9 @@ open class View() {
|
||||||
subviewsSortedByZIndex = subviews.sortedBy(View::zIndex)
|
subviewsSortedByZIndex = subviews.sortedBy(View::zIndex)
|
||||||
|
|
||||||
view.superview = this
|
view.superview = this
|
||||||
view.solver = solver
|
if (solverDelegate.isInitialized) {
|
||||||
|
view.solver = solver
|
||||||
|
}
|
||||||
view.window = window
|
view.window = window
|
||||||
|
|
||||||
view.wasAdded()
|
view.wasAdded()
|
||||||
|
@ -182,16 +190,34 @@ open class View() {
|
||||||
* @throws RuntimeException If the given [view] is not a subview of this view.
|
* @throws RuntimeException If the given [view] is not a subview of this view.
|
||||||
*/
|
*/
|
||||||
fun removeSubview(view: View) {
|
fun removeSubview(view: View) {
|
||||||
if (view.superview != this) {
|
if (view.superview !== this) {
|
||||||
throw RuntimeException("Cannot remove subview whose superview is not this view")
|
throw RuntimeException("Cannot remove subview whose superview is not this view")
|
||||||
}
|
}
|
||||||
solver.constraints.filter { constraint ->
|
|
||||||
constraint.getVariables().any { it is LayoutVariable && it.owner == view }
|
|
||||||
}.forEach(solver::removeConstraint)
|
|
||||||
_subviews.remove(view)
|
_subviews.remove(view)
|
||||||
subviewsSortedByZIndex = subviews.sortedBy(View::zIndex)
|
subviewsSortedByZIndex = subviews.sortedBy(View::zIndex)
|
||||||
|
|
||||||
view.superview = null
|
view.superview = null
|
||||||
|
|
||||||
|
// we need to remove constraints for this subview that cross the boundary between the subview and ourself
|
||||||
|
val constraintsToRemove = solver.constraints.filter { constraint ->
|
||||||
|
val variables = constraint.getVariables().mapNotNull { it as? LayoutVariable }
|
||||||
|
|
||||||
|
for (a in 0 until variables.size - 1) {
|
||||||
|
for (b in a + 1 until variables.size) {
|
||||||
|
// if the variable views have no common ancestor after the removed view's superview is unset,
|
||||||
|
// the constraint crossed the this<->view boundary and should be removed
|
||||||
|
val ancestor = LowestCommonAncestor.find(variables[a].owner, variables[b].owner, View::superview)
|
||||||
|
if (ancestor == null) {
|
||||||
|
return@filter true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
constraintsToRemove.forEach(solver::removeConstraint)
|
||||||
|
|
||||||
// todo: does this need to be reset
|
// todo: does this need to be reset
|
||||||
// view.solver = null
|
// view.solver = null
|
||||||
view.window = null
|
view.window = null
|
||||||
|
@ -254,7 +280,7 @@ open class View() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateIntrinsicContentSizeConstraints(old: Size?, new: Size?) {
|
private fun updateIntrinsicContentSizeConstraints(old: Size?, new: Size?) {
|
||||||
if (!usesConstraintBasedLayout || !this::solver.isInitialized) return
|
if (!usesConstraintBasedLayout || !solverDelegate.isInitialized) return
|
||||||
|
|
||||||
if (old != null) {
|
if (old != null) {
|
||||||
solver.removeConstraint(intrinsicContentSizeWidthConstraint!!)
|
solver.removeConstraint(intrinsicContentSizeWidthConstraint!!)
|
||||||
|
@ -273,14 +299,14 @@ open class View() {
|
||||||
* If overridden, the super-class method must be called.
|
* If overridden, the super-class method must be called.
|
||||||
*/
|
*/
|
||||||
open fun didLayout() {
|
open fun didLayout() {
|
||||||
subviews.forEach(View::didLayout)
|
|
||||||
|
|
||||||
if (usesConstraintBasedLayout) {
|
if (usesConstraintBasedLayout) {
|
||||||
val superviewLeft = superview?.leftAnchor?.value ?: 0.0
|
val superviewLeft = superview?.leftAnchor?.value ?: 0.0
|
||||||
val superviewTop = superview?.topAnchor?.value ?: 0.0
|
val superviewTop = superview?.topAnchor?.value ?: 0.0
|
||||||
frame = Rect(leftAnchor.value - superviewLeft, topAnchor.value - superviewTop, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subviews.forEach(View::didLayout)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -86,16 +86,9 @@ abstract class AbstractButton<Impl: AbstractButton<Impl>>(val content: View, val
|
||||||
|
|
||||||
RenderHelper.fill(matrixStack, bounds, backgroundColor)
|
RenderHelper.fill(matrixStack, 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
|
// don't need to convert mouse to background coordinate system
|
||||||
// the edges are all pinned, so the coordinate space is the same
|
// the edges are all pinned, so the coordinate space is the same
|
||||||
currentBackground?.draw(matrixStack, mouse, delta)
|
getCurrentBackground(mouse)?.draw(matrixStack, mouse, delta)
|
||||||
|
|
||||||
val mouseInContent = convert(mouse, to = content)
|
val mouseInContent = convert(mouse, to = content)
|
||||||
content.draw(matrixStack, mouseInContent, delta)
|
content.draw(matrixStack, mouseInContent, delta)
|
||||||
|
@ -117,4 +110,14 @@ abstract class AbstractButton<Impl: AbstractButton<Impl>>(val content: View, val
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun getCurrentBackground(mouse: Point): View? {
|
||||||
|
return if (disabled) {
|
||||||
|
disabledBackground ?: background
|
||||||
|
} else if (mouse in bounds) {
|
||||||
|
hoveredBackground ?: background
|
||||||
|
} else {
|
||||||
|
background
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
package net.shadowfacts.cacao.viewcontroller
|
||||||
|
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.shadowfacts.cacao.geometry.Axis
|
||||||
|
import net.shadowfacts.cacao.geometry.Point
|
||||||
|
import net.shadowfacts.cacao.geometry.Rect
|
||||||
|
import net.shadowfacts.cacao.geometry.Size
|
||||||
|
import net.shadowfacts.cacao.util.MouseButton
|
||||||
|
import net.shadowfacts.cacao.util.texture.NinePatchTexture
|
||||||
|
import net.shadowfacts.cacao.util.texture.Texture
|
||||||
|
import net.shadowfacts.cacao.view.NinePatchView
|
||||||
|
import net.shadowfacts.cacao.view.StackView
|
||||||
|
import net.shadowfacts.cacao.view.TextureView
|
||||||
|
import net.shadowfacts.cacao.view.View
|
||||||
|
import net.shadowfacts.cacao.view.button.AbstractButton
|
||||||
|
import net.shadowfacts.kiwidsl.dsl
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shadowfacts
|
||||||
|
*/
|
||||||
|
class TabViewController<T: TabViewController.Tab>(
|
||||||
|
val tabs: Array<T>,
|
||||||
|
initalTab: T = tabs.first()
|
||||||
|
): ViewController() {
|
||||||
|
|
||||||
|
interface Tab {
|
||||||
|
val tabView: View
|
||||||
|
val tooltip: Text?
|
||||||
|
val controller: ViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentTab: T = initalTab
|
||||||
|
private set
|
||||||
|
|
||||||
|
private lateinit var tabButtons: List<TabButton<T>>
|
||||||
|
|
||||||
|
private lateinit var outerStack: StackView
|
||||||
|
private lateinit var tabStack: StackView
|
||||||
|
private lateinit var tabVCContainer: View
|
||||||
|
|
||||||
|
override fun viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
// padding is -4 so tab button texture overlaps with panel BG as expected
|
||||||
|
outerStack = StackView(Axis.VERTICAL, StackView.Distribution.FILL, -4.0)
|
||||||
|
view.addSubview(outerStack)
|
||||||
|
|
||||||
|
tabStack = StackView(Axis.HORIZONTAL, StackView.Distribution.FILL)
|
||||||
|
tabStack.zIndex = 1.0
|
||||||
|
outerStack.addArrangedSubview(tabStack)
|
||||||
|
|
||||||
|
tabVCContainer = View()
|
||||||
|
outerStack.addArrangedSubview(tabVCContainer)
|
||||||
|
|
||||||
|
tabButtons = tabs.mapIndexed { index, tab ->
|
||||||
|
val btn = TabButton(tab)
|
||||||
|
btn.handler = this::selectTab
|
||||||
|
if (tab == currentTab) {
|
||||||
|
btn.setSelected(true)
|
||||||
|
}
|
||||||
|
btn
|
||||||
|
}
|
||||||
|
// todo: batch calls to addArrangedSubview
|
||||||
|
tabButtons.forEach(tabStack::addArrangedSubview)
|
||||||
|
|
||||||
|
// spacer
|
||||||
|
tabStack.addArrangedSubview(View())
|
||||||
|
|
||||||
|
val background = NinePatchView(NinePatchTexture.PANEL_BG)
|
||||||
|
background.zIndex = -1.0
|
||||||
|
tabVCContainer.addSubview(background)
|
||||||
|
|
||||||
|
embedChild(currentTab.controller, tabVCContainer)
|
||||||
|
|
||||||
|
view.solver.dsl {
|
||||||
|
outerStack.leftAnchor equalTo view.leftAnchor
|
||||||
|
outerStack.rightAnchor equalTo view.rightAnchor
|
||||||
|
outerStack.topAnchor equalTo view.topAnchor
|
||||||
|
outerStack.bottomAnchor equalTo view.bottomAnchor
|
||||||
|
|
||||||
|
background.leftAnchor equalTo tabVCContainer.leftAnchor
|
||||||
|
background.rightAnchor equalTo tabVCContainer.rightAnchor
|
||||||
|
background.topAnchor equalTo tabVCContainer.topAnchor
|
||||||
|
background.bottomAnchor equalTo tabVCContainer.bottomAnchor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectTab(button: TabButton<T>) {
|
||||||
|
val oldTab = currentTab
|
||||||
|
currentTab = button.tab
|
||||||
|
|
||||||
|
// todo: unselect old button
|
||||||
|
tabButtons.forEach { it.setSelected(false) }
|
||||||
|
oldTab.controller.removeFromParent()
|
||||||
|
|
||||||
|
button.setSelected(true)
|
||||||
|
embedChild(currentTab.controller, tabVCContainer)
|
||||||
|
// todo: setNeedsLayout
|
||||||
|
window!!.layout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TabButton<T: Tab>(
|
||||||
|
val tab: T,
|
||||||
|
): AbstractButton<TabButton<T>>(
|
||||||
|
tab.tabView,
|
||||||
|
padding = 2.0
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val BACKGROUND = Identifier("textures/gui/container/creative_inventory/tabs.png")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var selected = false
|
||||||
|
private var backgroundView = TextureView(Texture(BACKGROUND, 0, 0))
|
||||||
|
|
||||||
|
init {
|
||||||
|
intrinsicContentSize = Size(28.0, 32.0)
|
||||||
|
background = null
|
||||||
|
hoveredBackground = null
|
||||||
|
disabledBackground = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun wasAdded() {
|
||||||
|
super.wasAdded()
|
||||||
|
backgroundView.usesConstraintBasedLayout = false
|
||||||
|
backgroundView.frame = Rect(0.0, 0.0, 28.0, 32.0)
|
||||||
|
addSubview(backgroundView)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didLayout() {
|
||||||
|
super.didLayout()
|
||||||
|
updateBackgroundTexture()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSelected(selected: Boolean) {
|
||||||
|
this.selected = selected
|
||||||
|
updateBackgroundTexture()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCurrentBackground(mouse: Point) = backgroundView
|
||||||
|
|
||||||
|
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
|
||||||
|
if (selected) return false
|
||||||
|
else return super.mouseClicked(point, mouseButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateBackgroundTexture() {
|
||||||
|
val v = if (selected) 32 else 0
|
||||||
|
val u = when {
|
||||||
|
superview == null -> 0
|
||||||
|
frame.left == 0.0 -> 0
|
||||||
|
frame.right == superview!!.bounds.right -> 140
|
||||||
|
else -> 28
|
||||||
|
}
|
||||||
|
backgroundView.texture = Texture(BACKGROUND, u, v)
|
||||||
|
backgroundView.frame = Rect(0.0, 0.0, 28.0, if (selected) 32.0 else 28.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -23,6 +23,12 @@ abstract class ViewController {
|
||||||
* b) this VC is added as a child of another view controller.
|
* b) this VC is added as a child of another view controller.
|
||||||
*/
|
*/
|
||||||
var window: Window? = null
|
var window: Window? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
for (vc in children) {
|
||||||
|
vc.window = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for creating layout constraints in the domain of this VC's window.
|
* Helper function for creating layout constraints in the domain of this VC's window.
|
||||||
|
@ -70,6 +76,18 @@ abstract class ViewController {
|
||||||
view = View()
|
view = View()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isViewLoaded: Boolean
|
||||||
|
get() = ::view.isInitialized
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls [loadView] to load this controller's view only if it has not already been loaded.
|
||||||
|
*/
|
||||||
|
fun loadViewIfNeeded() {
|
||||||
|
if (!isViewLoaded) {
|
||||||
|
loadView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called after the view is loaded, it's properties are initialized, and [View.wasAdded] has been
|
* This method is called after the view is loaded, it's properties are initialized, and [View.wasAdded] has been
|
||||||
* called.
|
* called.
|
||||||
|
@ -149,7 +167,8 @@ abstract class ViewController {
|
||||||
viewController.parent = this
|
viewController.parent = this
|
||||||
viewController.window = window
|
viewController.window = window
|
||||||
_children.add(viewController)
|
_children.add(viewController)
|
||||||
viewController.loadView()
|
val wasViewLoaded = viewController.isViewLoaded
|
||||||
|
viewController.loadViewIfNeeded()
|
||||||
|
|
||||||
container.addSubview(viewController.view)
|
container.addSubview(viewController.view)
|
||||||
|
|
||||||
|
@ -162,7 +181,9 @@ abstract class ViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewController.viewDidLoad()
|
if (!wasViewLoaded) {
|
||||||
|
viewController.viewDidLoad()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,7 +207,6 @@ abstract class ViewController {
|
||||||
fun removeFromParent() {
|
fun removeFromParent() {
|
||||||
parent?.removeChild(this)
|
parent?.removeChild(this)
|
||||||
view.removeFromSuperview()
|
view.removeFromSuperview()
|
||||||
// todo: remove view from superview
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ open class Window(
|
||||||
|
|
||||||
fun wasAdded() {
|
fun wasAdded() {
|
||||||
viewController.window = this
|
viewController.window = this
|
||||||
viewController.loadView()
|
viewController.loadViewIfNeeded()
|
||||||
|
|
||||||
viewController.view.window = this
|
viewController.view.window = this
|
||||||
viewController.view.solver = solver
|
viewController.view.solver = solver
|
||||||
|
|
|
@ -2,6 +2,7 @@ package net.shadowfacts.phycon.screen
|
||||||
|
|
||||||
import net.minecraft.client.MinecraftClient
|
import net.minecraft.client.MinecraftClient
|
||||||
import net.minecraft.text.LiteralText
|
import net.minecraft.text.LiteralText
|
||||||
|
import net.minecraft.text.Text
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.Identifier
|
||||||
import net.shadowfacts.cacao.CacaoScreen
|
import net.shadowfacts.cacao.CacaoScreen
|
||||||
import net.shadowfacts.cacao.window.Window
|
import net.shadowfacts.cacao.window.Window
|
||||||
|
@ -12,6 +13,7 @@ import net.shadowfacts.cacao.util.texture.NinePatchTexture
|
||||||
import net.shadowfacts.cacao.util.texture.Texture
|
import net.shadowfacts.cacao.util.texture.Texture
|
||||||
import net.shadowfacts.cacao.view.*
|
import net.shadowfacts.cacao.view.*
|
||||||
import net.shadowfacts.cacao.view.button.Button
|
import net.shadowfacts.cacao.view.button.Button
|
||||||
|
import net.shadowfacts.cacao.viewcontroller.TabViewController
|
||||||
import net.shadowfacts.cacao.viewcontroller.ViewController
|
import net.shadowfacts.cacao.viewcontroller.ViewController
|
||||||
import net.shadowfacts.kiwidsl.dsl
|
import net.shadowfacts.kiwidsl.dsl
|
||||||
|
|
||||||
|
@ -21,48 +23,104 @@ import net.shadowfacts.kiwidsl.dsl
|
||||||
class TestCacaoScreen: CacaoScreen() {
|
class TestCacaoScreen: CacaoScreen() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val viewController = object: ViewController() {
|
// val viewController = object: ViewController() {
|
||||||
override fun loadView() {
|
// override fun loadView() {
|
||||||
view = View()
|
// view = View()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
// override fun viewDidLoad() {
|
||||||
|
// super.viewDidLoad()
|
||||||
|
//
|
||||||
|
// val stack = view.addSubview(StackView(Axis.VERTICAL, StackView.Distribution.CENTER, spacing = 4.0)).apply {
|
||||||
|
// backgroundColor = Color.WHITE
|
||||||
|
// }
|
||||||
|
// val birch = stack.addArrangedSubview(TextureView(Texture(Identifier("textures/block/birch_log_top.png"), 0, 0, 16, 16))).apply {
|
||||||
|
// intrinsicContentSize = Size(50.0, 50.0)
|
||||||
|
// }
|
||||||
|
// val ninePatch = stack.addArrangedSubview(NinePatchView(NinePatchTexture.PANEL_BG)).apply {
|
||||||
|
// intrinsicContentSize = Size(75.0, 100.0)
|
||||||
|
// }
|
||||||
|
// val red = stack.addArrangedSubview(View()).apply {
|
||||||
|
// intrinsicContentSize = Size(50.0, 50.0)
|
||||||
|
// backgroundColor = Color.RED
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// val label = Label(LiteralText("Test"), wrappingMode = Label.WrappingMode.NO_WRAP).apply {
|
||||||
|
//// textColor = Color.BLACK
|
||||||
|
// }
|
||||||
|
//// stack.addArrangedSubview(label)
|
||||||
|
// val button = red.addSubview(Button(label))
|
||||||
|
//
|
||||||
|
// view.solver.dsl {
|
||||||
|
// stack.topAnchor equalTo 0
|
||||||
|
// stack.centerXAnchor equalTo window!!.centerXAnchor
|
||||||
|
// stack.widthAnchor equalTo 100
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// button.centerXAnchor equalTo red.centerXAnchor
|
||||||
|
// button.centerYAnchor equalTo red.centerYAnchor
|
||||||
|
//// label.heightAnchor equalTo 9
|
||||||
|
// button.heightAnchor equalTo 20
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// addWindow(Window(viewController))
|
||||||
|
|
||||||
|
val viewController = object: ViewController() {
|
||||||
override fun viewDidLoad() {
|
override fun viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
val stack = view.addSubview(StackView(Axis.VERTICAL, StackView.Distribution.CENTER, spacing = 4.0)).apply {
|
val tabs = arrayOf(
|
||||||
backgroundColor = Color.WHITE
|
Tab(Label("A"), AViewController()),
|
||||||
}
|
Tab(Label("B"), BViewController()),
|
||||||
val birch = stack.addArrangedSubview(TextureView(Texture(Identifier("textures/block/birch_log_top.png"), 0, 0, 16, 16))).apply {
|
)
|
||||||
intrinsicContentSize = Size(50.0, 50.0)
|
val tabVC = TabViewController(tabs)
|
||||||
}
|
embedChild(tabVC, pinEdges = false)
|
||||||
val ninePatch = stack.addArrangedSubview(NinePatchView(NinePatchTexture.PANEL_BG)).apply {
|
|
||||||
intrinsicContentSize = Size(75.0, 100.0)
|
|
||||||
}
|
|
||||||
val red = stack.addArrangedSubview(View()).apply {
|
|
||||||
intrinsicContentSize = Size(50.0, 50.0)
|
|
||||||
backgroundColor = Color.RED
|
|
||||||
}
|
|
||||||
|
|
||||||
val label = Label(LiteralText("Test"), wrappingMode = Label.WrappingMode.NO_WRAP).apply {
|
|
||||||
// textColor = Color.BLACK
|
|
||||||
}
|
|
||||||
// stack.addArrangedSubview(label)
|
|
||||||
val button = red.addSubview(Button(label))
|
|
||||||
|
|
||||||
view.solver.dsl {
|
view.solver.dsl {
|
||||||
stack.topAnchor equalTo 0
|
tabVC.view.centerXAnchor equalTo view.centerXAnchor
|
||||||
stack.centerXAnchor equalTo window!!.centerXAnchor
|
tabVC.view.centerYAnchor equalTo view.centerYAnchor
|
||||||
stack.widthAnchor equalTo 100
|
tabVC.view.widthAnchor equalTo 200
|
||||||
|
tabVC.view.heightAnchor equalTo 150
|
||||||
|
|
||||||
button.centerXAnchor equalTo red.centerXAnchor
|
|
||||||
button.centerYAnchor equalTo red.centerYAnchor
|
|
||||||
// label.heightAnchor equalTo 9
|
|
||||||
button.heightAnchor equalTo 20
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addWindow(Window(viewController))
|
addWindow(Window(viewController))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Tab(
|
||||||
|
override val tabView: View,
|
||||||
|
override val controller: ViewController,
|
||||||
|
override val tooltip: Text? = null
|
||||||
|
): TabViewController.Tab
|
||||||
|
|
||||||
|
class AViewController: ViewController() {
|
||||||
|
override fun viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
val button = Button(Label("A content")) {
|
||||||
|
println("A pressed")
|
||||||
|
}
|
||||||
|
view.addSubview(button)
|
||||||
|
view.solver.dsl {
|
||||||
|
button.centerXAnchor equalTo view.centerXAnchor
|
||||||
|
button.centerYAnchor equalTo view.centerYAnchor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BViewController: ViewController() {
|
||||||
|
override fun viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
val button = Button(Label("B content")) {
|
||||||
|
println("B pressed")
|
||||||
|
}
|
||||||
|
view.addSubview(button)
|
||||||
|
view.solver.dsl {
|
||||||
|
button.centerXAnchor equalTo view.centerXAnchor
|
||||||
|
button.centerYAnchor equalTo view.centerYAnchor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,14 @@ class LCATest {
|
||||||
assertEquals(parent, LowestCommonAncestor.find(child, parent, Node::parent))
|
assertEquals(parent, LowestCommonAncestor.find(child, parent, Node::parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNestedParent() {
|
||||||
|
val parent = Node("parent", null)
|
||||||
|
val middle = Node("middle", parent)
|
||||||
|
val child = Node("child", middle)
|
||||||
|
assertEquals(parent, LowestCommonAncestor.find(parent, child, Node::parent))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSiblings() {
|
fun testSiblings() {
|
||||||
val root = Node("root", null)
|
val root = Node("root", null)
|
||||||
|
|
Loading…
Reference in New Issue