From ada3d3f6e6744126d2a3ab9c812a697afbdd6636 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Sat, 9 May 2020 23:19:49 +0200 Subject: [PATCH] Add support for calculate height --- .../org/openrndr/panel/elements/Element.kt | 18 +++-- .../org/openrndr/panel/layout/Layouter.kt | 80 +++++++++++-------- .../org/openrndr/panel/style/StyleSheet.kt | 5 ++ 3 files changed, 65 insertions(+), 38 deletions(-) diff --git a/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Element.kt b/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Element.kt index d54421b5..6a90b2fe 100644 --- a/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Element.kt +++ b/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Element.kt @@ -26,7 +26,7 @@ val disabled = ElementPseudoClass("disabled") class FocusEvent interface DisposableElement { - var disposed : Boolean + var disposed: Boolean fun dispose() { disposed = true @@ -44,6 +44,12 @@ open class Element(val type: ElementType) { return null } + + open val heightHint: Double? + get() { + return null + } + class MouseObservables { val clicked = Event("element-mouse-clicked") val doubleClicked = Event("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 -> val index = p.children.indexOf(this) 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 -> 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])) { return p.children[i] } @@ -319,7 +325,7 @@ fun Element.enable() { fun Element.isDisabled(): Boolean = disabled in pseudoClasses -fun Element.findAll(predicate: (Element) -> Boolean) : List { +fun Element.findAll(predicate: (Element) -> Boolean): List { val results = mutableListOf() visit { if (predicate(this)) { @@ -329,7 +335,7 @@ fun Element.findAll(predicate: (Element) -> Boolean) : List { return results } -fun Element.findAllVisible(predicate: (Element) -> Boolean) : List { +fun Element.findAllVisible(predicate: (Element) -> Boolean): List { val results = mutableListOf() visitVisible { if (predicate(this)) { diff --git a/orx-panel/src/main/kotlin/org/openrndr/panel/layout/Layouter.kt b/orx-panel/src/main/kotlin/org/openrndr/panel/layout/Layouter.kt index 37755393..00a1ebee 100644 --- a/orx-panel/src/main/kotlin/org/openrndr/panel/layout/Layouter.kt +++ b/orx-panel/src/main/kotlin/org/openrndr/panel/layout/Layouter.kt @@ -14,7 +14,8 @@ class Layouter { val blockLike = setOf(Display.BLOCK, Display.FLEX) 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 -> var y = element.layout.screenY - element.scrollTop + element.computedStyle.effectivePaddingTop @@ -26,22 +27,23 @@ class Layouter { 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 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() - element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.forEach { - - val elementGrow = (it.computedStyle.flexGrow as FlexGrow.Ratio).value + 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 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) - it.layout.screenX = x + ((it.computedStyle.marginLeft as? LinearDimension.PX)?.value + child.layout.screenX = x + ((child.computedStyle.marginLeft as? LinearDimension.PX)?.value ?: 0.0) - it.layout.growWidth = growWidth - x += width(it) + growWidth - maxHeight = max(height(it), maxHeight) + child.layout.growWidth = growWidth + + val effectiveWidth = width(child) + growWidth + x += effectiveWidth + maxHeight = max(height(child, effectiveWidth), 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 totalHeight = element.children .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 totalGrow = element.children .filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition } .sumByDouble { (it.computedStyle.flexGrow as FlexGrow.Ratio).value } - element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.forEach { - val elementGrow = (it.computedStyle.flexGrow as FlexGrow.Ratio).value + 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 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) - it.layout.screenX = lx + ((it.computedStyle.marginLeft as? LinearDimension.PX)?.value + child.layout.screenX = lx + ((child.computedStyle.marginLeft as? LinearDimension.PX)?.value ?: 0.0) - it.layout.growHeight = growHeight - ly += height(it) + growHeight - maxWidth = max(height(it), maxWidth) + child.layout.growHeight = growHeight + val effectHeight = height(child) + growHeight + ly += effectHeight + maxWidth = max(width(child), maxWidth) } 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) { 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) - maxWidth = max(0.0, width(it)) - y += height(it) + val effectiveWidth = width(it) + maxWidth = max(effectiveWidth, maxWidth) + y += height(it, effectiveWidth) } else if (it.computedStyle.position == Position.ABSOLUTE) { it.layout.screenX = element.layout.screenX + ((it.computedStyle.left as? LinearDimension.PX)?.value ?: 0.0) @@ -176,7 +180,7 @@ class Layouter { fun paddingLeft(element: Element?) = padding(element, StyleSheet::paddingLeft) 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) { return 0.0 } @@ -186,8 +190,8 @@ class Layouter { } return element.computedStyle.let { - it.height.let { - when (it) { + it.height.let { ld -> + when (val it = ld) { is LinearDimension.PX -> it.value is LinearDimension.Percent -> { val parentHeight = element.parent?.layout?.screenHeight ?: 0.0 @@ -198,7 +202,12 @@ class Layouter { } is LinearDimension.Auto -> { 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") } @@ -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) { return 0.0 } - val result = it.width.let { when (it) { @@ -223,17 +231,30 @@ class Layouter { val effectiveWidth = (parentWidth - parentPadding) * (it.value / 100.0) - margins effectiveWidth } +// is LinearDimension.Calculate -> { +// val context = CalculateContext(null, height) +// it.function(context) +// +// } is LinearDimension.Auto -> (element.widthHint ?: positionChildren(element).width) + paddingRight(element) + paddingLeft(element) else -> throw RuntimeException("not supported") } } + 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 } fun layout(element: Element) { element.computedStyle.also { cs -> 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) { Position.FIXED -> { @@ -249,12 +270,7 @@ class Layouter { is ZIndex.Auto -> element.parent?.layout?.zIndex ?: 0 is ZIndex.Inherit -> element.parent?.layout?.zIndex ?: 0 } - - 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) + val result = positionChildren(element) } element.children.forEach { layout(it) } } diff --git a/orx-panel/src/main/kotlin/org/openrndr/panel/style/StyleSheet.kt b/orx-panel/src/main/kotlin/org/openrndr/panel/style/StyleSheet.kt index 7c6dd3c5..a3523202 100644 --- a/orx-panel/src/main/kotlin/org/openrndr/panel/style/StyleSheet.kt +++ b/orx-panel/src/main/kotlin/org/openrndr/panel/style/StyleSheet.kt @@ -25,6 +25,8 @@ sealed class Color(inherit: Boolean = false) : PropertyValue(inherit) { object Inherit : Color(inherit = true) } +class CalculateContext(val elementWidth:Double?, val elementHeight:Double?) + sealed class LinearDimension(inherit: Boolean = false) : PropertyValue(inherit) { class PX(val value: Double) : LinearDimension() { override fun toString(): String { @@ -32,10 +34,13 @@ sealed class LinearDimension(inherit: Boolean = false) : PropertyValue(inherit) } } class Percent(val value: Double) : LinearDimension() + class Calculate(val function: (CalculateContext) -> Double) : LinearDimension() object Auto : LinearDimension() object Inherit : LinearDimension(inherit = true) } + + data class PropertyBehaviour(val inheritance: PropertyInheritance, val intitial: Any) object PropertyBehaviours {