Add support for calculate height

This commit is contained in:
Edwin Jakobs
2020-05-09 23:19:49 +02:00
parent 21b812d52d
commit ada3d3f6e6
3 changed files with 65 additions and 38 deletions

View File

@@ -26,7 +26,7 @@ val disabled = ElementPseudoClass("disabled")
class FocusEvent class FocusEvent
interface DisposableElement { interface DisposableElement {
var disposed : Boolean var disposed: Boolean
fun dispose() { fun dispose() {
disposed = true disposed = true
@@ -44,6 +44,12 @@ open class Element(val type: ElementType) {
return null return null
} }
open val heightHint: Double?
get() {
return null
}
class MouseObservables { class MouseObservables {
val clicked = Event<MouseEvent>("element-mouse-clicked") val clicked = Event<MouseEvent>("element-mouse-clicked")
val doubleClicked = Event<MouseEvent>("element-mouse-double-clicked") val doubleClicked = Event<MouseEvent>("element-mouse-double-clicked")
@@ -236,7 +242,7 @@ open class Element(val type: ElementType) {
} }
} }
fun findNext(premise: (Element) -> Boolean) : Element? { fun findNext(premise: (Element) -> Boolean): Element? {
return parent?.let { p -> return parent?.let { p ->
val index = p.children.indexOf(this) val index = p.children.indexOf(this)
val siblingCount = p.children.size val siblingCount = p.children.size
@@ -249,10 +255,10 @@ open class Element(val type: ElementType) {
} }
} }
fun findPrevious(premise: (Element) -> Boolean) : Element? { fun findPrevious(premise: (Element) -> Boolean): Element? {
return parent?.let { p -> return parent?.let { p ->
val index = p.children.indexOf(this) val index = p.children.indexOf(this)
for (i in index-1 downTo 0) { for (i in index - 1 downTo 0) {
if (premise(p.children[i])) { if (premise(p.children[i])) {
return p.children[i] return p.children[i]
} }
@@ -319,7 +325,7 @@ fun Element.enable() {
fun Element.isDisabled(): Boolean = disabled in pseudoClasses fun Element.isDisabled(): Boolean = disabled in pseudoClasses
fun Element.findAll(predicate: (Element) -> Boolean) : List<Element> { fun Element.findAll(predicate: (Element) -> Boolean): List<Element> {
val results = mutableListOf<Element>() val results = mutableListOf<Element>()
visit { visit {
if (predicate(this)) { if (predicate(this)) {
@@ -329,7 +335,7 @@ fun Element.findAll(predicate: (Element) -> Boolean) : List<Element> {
return results return results
} }
fun Element.findAllVisible(predicate: (Element) -> Boolean) : List<Element> { fun Element.findAllVisible(predicate: (Element) -> Boolean): List<Element> {
val results = mutableListOf<Element>() val results = mutableListOf<Element>()
visitVisible { visitVisible {
if (predicate(this)) { if (predicate(this)) {

View File

@@ -14,7 +14,8 @@ class Layouter {
val blockLike = setOf(Display.BLOCK, Display.FLEX) val blockLike = setOf(Display.BLOCK, Display.FLEX)
val manualPosition = setOf(Position.FIXED, Position.ABSOLUTE) val manualPosition = setOf(Position.FIXED, Position.ABSOLUTE)
fun positionChildren(element: Element): Rectangle { fun positionChildren(element: Element, knownWidth:Double? = null): Rectangle {
return element.computedStyle.let { cs -> return element.computedStyle.let { cs ->
var y = element.layout.screenY - element.scrollTop + element.computedStyle.effectivePaddingTop var y = element.layout.screenY - element.scrollTop + element.computedStyle.effectivePaddingTop
@@ -26,22 +27,23 @@ class Layouter {
var x = element.layout.screenX + element.computedStyle.effectivePaddingLeft var x = element.layout.screenX + element.computedStyle.effectivePaddingLeft
val totalWidth = element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.map { width(it) }.sum() val totalWidth = element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.map { width(it) }.sum()
val remainder = (element.layout.screenWidth - totalWidth) val remainder = (knownWidth?: element.layout.screenWidth) - totalWidth
val totalGrow = element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.map { (it.computedStyle.flexGrow as FlexGrow.Ratio).value }.sum() val totalGrow = element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.map { (it.computedStyle.flexGrow as FlexGrow.Ratio).value }.sum()
element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.forEach { element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.forEach { child ->
val elementGrow = (child.computedStyle.flexGrow as FlexGrow.Ratio).value
val elementGrow = (it.computedStyle.flexGrow as FlexGrow.Ratio).value
val growWidth = if (totalGrow > 0) (elementGrow / totalGrow) * remainder else 0.0 val growWidth = if (totalGrow > 0) (elementGrow / totalGrow) * remainder else 0.0
it.layout.screenY = y + ((it.computedStyle.marginTop as? LinearDimension.PX)?.value child.layout.screenY = y + ((child.computedStyle.marginTop as? LinearDimension.PX)?.value
?: 0.0) ?: 0.0)
it.layout.screenX = x + ((it.computedStyle.marginLeft as? LinearDimension.PX)?.value child.layout.screenX = x + ((child.computedStyle.marginLeft as? LinearDimension.PX)?.value
?: 0.0) ?: 0.0)
it.layout.growWidth = growWidth child.layout.growWidth = growWidth
x += width(it) + growWidth
maxHeight = max(height(it), maxHeight) val effectiveWidth = width(child) + growWidth
x += effectiveWidth
maxHeight = max(height(child, effectiveWidth), maxHeight)
} }
Rectangle(Vector2(x, y), x - element.layout.screenX, maxHeight) Rectangle(Vector2(x, y), x - element.layout.screenX, maxHeight)
} }
@@ -53,25 +55,26 @@ class Layouter {
val verticalPadding = element.computedStyle.effectivePaddingTop + element.computedStyle.effectivePaddingBottom val verticalPadding = element.computedStyle.effectivePaddingTop + element.computedStyle.effectivePaddingBottom
val totalHeight = element.children val totalHeight = element.children
.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition } .filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }
.sumByDouble { height(it) } .sumByDouble { height(it, width(it)) }
val remainder = ((element.layout.screenHeight - verticalPadding) - totalHeight) val remainder = ((element.layout.screenHeight - verticalPadding) - totalHeight)
val totalGrow = element.children val totalGrow = element.children
.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition } .filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }
.sumByDouble { (it.computedStyle.flexGrow as FlexGrow.Ratio).value } .sumByDouble { (it.computedStyle.flexGrow as FlexGrow.Ratio).value }
element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.forEach { element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.forEach { child ->
val elementGrow = (it.computedStyle.flexGrow as FlexGrow.Ratio).value val elementGrow = (child.computedStyle.flexGrow as FlexGrow.Ratio).value
val growHeight = if (totalGrow > 0) (elementGrow / totalGrow) * remainder else 0.0 val growHeight = if (totalGrow > 0) (elementGrow / totalGrow) * remainder else 0.0
it.layout.screenY = ly + ((it.computedStyle.marginTop as? LinearDimension.PX)?.value child.layout.screenY = ly + ((child.computedStyle.marginTop as? LinearDimension.PX)?.value
?: 0.0) ?: 0.0)
it.layout.screenX = lx + ((it.computedStyle.marginLeft as? LinearDimension.PX)?.value child.layout.screenX = lx + ((child.computedStyle.marginLeft as? LinearDimension.PX)?.value
?: 0.0) ?: 0.0)
it.layout.growHeight = growHeight child.layout.growHeight = growHeight
ly += height(it) + growHeight
maxWidth = max(height(it), maxWidth)
val effectHeight = height(child) + growHeight
ly += effectHeight
maxWidth = max(width(child), maxWidth)
} }
Rectangle(Vector2(lx, ly), maxWidth, ly - element.layout.screenY) Rectangle(Vector2(lx, ly), maxWidth, ly - element.layout.screenY)
@@ -86,8 +89,9 @@ class Layouter {
if (it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition) { if (it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition) {
it.layout.screenY = y + ((it.computedStyle.marginTop as? LinearDimension.PX)?.value ?: 0.0) it.layout.screenY = y + ((it.computedStyle.marginTop as? LinearDimension.PX)?.value ?: 0.0)
it.layout.screenX = x + ((it.computedStyle.marginLeft as? LinearDimension.PX)?.value ?: 0.0) it.layout.screenX = x + ((it.computedStyle.marginLeft as? LinearDimension.PX)?.value ?: 0.0)
maxWidth = max(0.0, width(it)) val effectiveWidth = width(it)
y += height(it) maxWidth = max(effectiveWidth, maxWidth)
y += height(it, effectiveWidth)
} else if (it.computedStyle.position == Position.ABSOLUTE) { } else if (it.computedStyle.position == Position.ABSOLUTE) {
it.layout.screenX = element.layout.screenX + ((it.computedStyle.left as? LinearDimension.PX)?.value it.layout.screenX = element.layout.screenX + ((it.computedStyle.left as? LinearDimension.PX)?.value
?: 0.0) ?: 0.0)
@@ -176,7 +180,7 @@ class Layouter {
fun paddingLeft(element: Element?) = padding(element, StyleSheet::paddingLeft) fun paddingLeft(element: Element?) = padding(element, StyleSheet::paddingLeft)
fun paddingRight(element: Element?) = padding(element, StyleSheet::paddingRight) fun paddingRight(element: Element?) = padding(element, StyleSheet::paddingRight)
fun height(element: Element, includeMargins: Boolean = true): Double { fun height(element: Element, width: Double? = null, includeMargins: Boolean = true): Double {
if (element.computedStyle.display == Display.NONE) { if (element.computedStyle.display == Display.NONE) {
return 0.0 return 0.0
} }
@@ -186,8 +190,8 @@ class Layouter {
} }
return element.computedStyle.let { return element.computedStyle.let {
it.height.let { it.height.let { ld ->
when (it) { when (val it = ld) {
is LinearDimension.PX -> it.value is LinearDimension.PX -> it.value
is LinearDimension.Percent -> { is LinearDimension.Percent -> {
val parentHeight = element.parent?.layout?.screenHeight ?: 0.0 val parentHeight = element.parent?.layout?.screenHeight ?: 0.0
@@ -198,7 +202,12 @@ class Layouter {
} }
is LinearDimension.Auto -> { is LinearDimension.Auto -> {
val padding = paddingTop(element) + paddingBottom(element) val padding = paddingTop(element) + paddingBottom(element)
positionChildren(element).height + padding (element.heightHint ?: positionChildren(element, width).height) + padding
}
is LinearDimension.Calculate -> {
val context = CalculateContext(width, null)
it.function(context)
} }
else -> throw RuntimeException("not supported") else -> throw RuntimeException("not supported")
} }
@@ -207,11 +216,10 @@ class Layouter {
} }
} }
fun width(element: Element, includeMargins: Boolean = true): Double = element.computedStyle.let { fun width(element: Element, height: Double? = null, includeMargins: Boolean = true): Double = element.computedStyle.let {
if (element.computedStyle.display == Display.NONE) { if (element.computedStyle.display == Display.NONE) {
return 0.0 return 0.0
} }
val result = val result =
it.width.let { it.width.let {
when (it) { when (it) {
@@ -223,17 +231,30 @@ class Layouter {
val effectiveWidth = (parentWidth - parentPadding) * (it.value / 100.0) - margins val effectiveWidth = (parentWidth - parentPadding) * (it.value / 100.0) - margins
effectiveWidth effectiveWidth
} }
// is LinearDimension.Calculate -> {
// val context = CalculateContext(null, height)
// it.function(context)
//
// }
is LinearDimension.Auto -> (element.widthHint ?: positionChildren(element).width) + is LinearDimension.Auto -> (element.widthHint ?: positionChildren(element).width) +
paddingRight(element) + paddingLeft(element) paddingRight(element) + paddingLeft(element)
else -> throw RuntimeException("not supported") else -> throw RuntimeException("not supported")
} }
} + if (includeMargins) marginLeft(element) + marginRight(element) else 0.0 } + if (includeMargins) marginLeft(element) + marginRight(element) else 0.0
// TODO: find out why this hack is needed, I added this because somewhere in the layout process
// this information is lost
element.layout.screenWidth = result - if (includeMargins) marginLeft(element) + marginRight(element) else 0.0
result result
} }
fun layout(element: Element) { fun layout(element: Element) {
element.computedStyle.also { cs -> element.computedStyle.also { cs ->
cs.display.let { if (it == Display.NONE) return } cs.display.let { if (it == Display.NONE) return }
element.layout.screenWidth = width(element, includeMargins = false)
element.layout.screenWidth += element.layout.growWidth
element.layout.screenHeight = height(element, element.layout.screenWidth, includeMargins = false)
element.layout.screenHeight += element.layout.growHeight
when (cs.position) { when (cs.position) {
Position.FIXED -> { Position.FIXED -> {
@@ -249,12 +270,7 @@ class Layouter {
is ZIndex.Auto -> element.parent?.layout?.zIndex ?: 0 is ZIndex.Auto -> element.parent?.layout?.zIndex ?: 0
is ZIndex.Inherit -> element.parent?.layout?.zIndex ?: 0 is ZIndex.Inherit -> element.parent?.layout?.zIndex ?: 0
} }
val result = positionChildren(element)
element.layout.screenWidth = width(element, includeMargins = false)
element.layout.screenHeight = height(element, includeMargins = false)
element.layout.screenWidth += element.layout.growWidth
element.layout.screenHeight += element.layout.growHeight
positionChildren(element)
} }
element.children.forEach { layout(it) } element.children.forEach { layout(it) }
} }

View File

@@ -25,6 +25,8 @@ sealed class Color(inherit: Boolean = false) : PropertyValue(inherit) {
object Inherit : Color(inherit = true) object Inherit : Color(inherit = true)
} }
class CalculateContext(val elementWidth:Double?, val elementHeight:Double?)
sealed class LinearDimension(inherit: Boolean = false) : PropertyValue(inherit) { sealed class LinearDimension(inherit: Boolean = false) : PropertyValue(inherit) {
class PX(val value: Double) : LinearDimension() { class PX(val value: Double) : LinearDimension() {
override fun toString(): String { override fun toString(): String {
@@ -32,10 +34,13 @@ sealed class LinearDimension(inherit: Boolean = false) : PropertyValue(inherit)
} }
} }
class Percent(val value: Double) : LinearDimension() class Percent(val value: Double) : LinearDimension()
class Calculate(val function: (CalculateContext) -> Double) : LinearDimension()
object Auto : LinearDimension() object Auto : LinearDimension()
object Inherit : LinearDimension(inherit = true) object Inherit : LinearDimension(inherit = true)
} }
data class PropertyBehaviour(val inheritance: PropertyInheritance, val intitial: Any) data class PropertyBehaviour(val inheritance: PropertyInheritance, val intitial: Any)
object PropertyBehaviours { object PropertyBehaviours {