1- package com.lambda.newgui
1+ package com.lambda.newgui.component.layout
22
3+ import com.lambda.graphics.RenderMain
34import com.lambda.graphics.animation.AnimationTicker
45import com.lambda.graphics.gl.Scissor.scissor
56import com.lambda.gui.api.GuiEvent
67import com.lambda.gui.api.RenderLayer
8+ import com.lambda.newgui.component.HAlign
9+ import com.lambda.newgui.component.VAlign
10+ import com.lambda.newgui.component.core.UIBuilder
711import com.lambda.util.KeyCode
812import com.lambda.util.Mouse
13+ import com.lambda.util.math.MathUtils.coerceIn
914import com.lambda.util.math.Rect
1015import com.lambda.util.math.Vec2d
1116
@@ -30,12 +35,65 @@ import com.lambda.util.math.Vec2d
3035 * ```
3136 */
3237open class Layout (
33- private val owner : Layout ? ,
38+ val owner : Layout ? ,
3439 useBatching : Boolean ,
35- private val batchChildren : Boolean
40+ private val batchChildren : Boolean ,
3641) {
37- // Rectangle of the component
38- open val rect: Rect get() = rectUpdate() + (owner?.rect?.leftTop ? : Vec2d .ZERO )
42+ /* *
43+ * The rectangle of this component
44+ */
45+ val rect get() = Rect .basedOn(position, size)
46+
47+ /* *
48+ * The size of this component
49+ */
50+ open var size = Vec2d .ZERO
51+
52+ /* *
53+ * Horizontal alignment
54+ */
55+ var horizontalAlignment = HAlign .LEFT ; set(to) {
56+ val from = field
57+ field = to
58+
59+ val delta = to.multiplier - from.multiplier
60+ relativePos + = Vec2d .RIGHT * delta * (size.x - ownerRect.size.x)
61+ }
62+
63+ /* *
64+ * Vertical alignment
65+ */
66+ var verticalAlignment = VAlign .TOP ; set(to) {
67+ val from = field
68+ field = to
69+
70+ val delta = to.multiplier - from.multiplier
71+ relativePos + = Vec2d .BOTTOM * delta * (size.y - ownerRect.size.y)
72+ }
73+
74+ /* *
75+ * Relative position of the component
76+ */
77+ var relativePos = Vec2d .ZERO
78+
79+ /* *
80+ * Absolute(drawn) position of the component
81+ */
82+ var position: Vec2d
83+ get() = ownerRect.leftTop + relativeToAbs(relativePos).let {
84+ if (! clampPosition) it
85+ else it.coerceIn(
86+ 0.0 , ownerRect.size.x - size.x,
87+ 0.0 , ownerRect.size.y - size.y
88+ )
89+ }; set(value) { relativePos = absToRelative(value - ownerRect.leftTop) }
90+
91+ // Rect-related properties
92+ private var screenSize = Vec2d .ZERO
93+ private val ownerRect get() = owner?.rect ? : Rect (Vec2d .ZERO , screenSize)
94+ private val dockingOffset get() = (ownerRect.size - size) * Vec2d (horizontalAlignment.multiplier, verticalAlignment.multiplier)
95+ private fun relativeToAbs (posIn : Vec2d ) = posIn + dockingOffset
96+ private fun absToRelative (posIn : Vec2d ) = posIn - dockingOffset
3997
4098 // Structure
4199 val children = mutableListOf<Layout >()
@@ -61,7 +119,9 @@ open class Layout(
61119 }
62120
63121 private var owningRenderer = false
122+
64123 protected open val interactionPassthrough = false
124+ protected open val clampPosition = false
65125
66126 // Actions
67127 private var showActions = mutableListOf< () -> Unit > ()
@@ -73,7 +133,7 @@ open class Layout(
73133 private var mouseClickActions = mutableListOf< (button: Mouse .Button , action: Mouse .Action ) -> Unit > ()
74134 private var mouseMoveActions = mutableListOf< (mouse: Vec2d ) -> Unit > ()
75135 private var mouseScrollActions = mutableListOf< (delta: Double ) -> Unit > ()
76- private var rectUpdate = { Rect . ZERO }
136+ private var rectUpdate: (() -> Rect ) ? = null
77137
78138 /* *
79139 * Sets the action to be performed when the element gets shown.
@@ -157,13 +217,22 @@ open class Layout(
157217 }
158218
159219 /* *
160- * Sets the rectangle of this component.
220+ * Sets the rect of the element
161221 */
162- fun rect (block : () -> Rect ) {
222+ fun rectUpdate (block : () -> Rect ) {
163223 rectUpdate = block
164224 }
165225
166226 fun onEvent (e : GuiEvent ) {
227+ if (e is GuiEvent .Render ) {
228+ screenSize = RenderMain .screenSize
229+
230+ rectUpdate?.invoke()?.let {
231+ position = it.leftTop
232+ size = it.size
233+ }
234+ }
235+
167236 // Select an element that's on foreground
168237 selectedChild = if (isHovered) children.lastOrNull {
169238 ! it.interactionPassthrough && mousePosition in it.rect
@@ -192,23 +261,31 @@ open class Layout(
192261 is GuiEvent .KeyPress -> { keyPressActions.forEach { it(e.key) } }
193262 is GuiEvent .CharTyped -> { charTypedActions.forEach { it((e.char)) } }
194263 is GuiEvent .MouseMove -> { mousePosition = e.mouse; mouseMoveActions.forEach { it(e.mouse) } }
195- is GuiEvent .MouseScroll -> { mousePosition = e.mouse; mouseScrollActions.forEach { it(e.delta) } }
264+ is GuiEvent .MouseScroll -> {
265+ mousePosition = e.mouse
266+
267+ if (isHovered) {
268+ mouseScrollActions.forEach { it(e.delta) }
269+ }
270+ }
196271 is GuiEvent .MouseClick -> {
197272 mousePosition = e.mouse
198273 val action = if (isHovered) e.action else Mouse .Action .Release
199274 mouseClickActions.forEach { it(e.button, action) }
200275 }
201- is GuiEvent .Render -> scissor(rect) {
276+ is GuiEvent .Render -> {
202277 val (pre, post) = children.partition { ! it.owningRenderer }
203278
204279 pre.forEach { it.onEvent(e) }
205280 renderActions.forEach { it(renderer) }
206281
207282 if (owningRenderer) {
208- scissor(rect, renderer:: render)
283+ renderer. render( )
209284 }
210285
211- post.forEach { it.onEvent(e) }
286+ scissor(rect) { // ToDo: merge to ListLayout
287+ post.forEach { it.onEvent(e) }
288+ }
212289 }
213290 }
214291 }
@@ -217,21 +294,20 @@ open class Layout(
217294 /* *
218295 * Creates an empty [Layout]
219296 *
220- * @param useBatching Increases performance by using parent's renderer instead of creating a new one.
297+ * @param useBatching Whether to use parent's renderer
221298 *
222299 * @param batchChildren Whether allow children to use the renderer of this layout
223300 *
301+ * @param block Actions to perform within this component
302+ *
224303 * Check [Layout] description for more info about batching
225304 */
226305 @UIBuilder
227306 fun Layout.layout (
228307 useBatching : Boolean = false,
229308 batchChildren : Boolean = false,
230- block : Layout .() -> Unit ,
309+ block : Layout .() -> Unit = {} ,
231310 ) = Layout (this , useBatching, batchChildren)
232311 .apply (children::add).apply (block)
233312 }
234313}
235-
236- @DslMarker
237- annotation class UIBuilder
0 commit comments