Skip to content

Commit 52483d3

Browse files
committed
UI Alignment
1 parent fc91a2d commit 52483d3

File tree

8 files changed

+250
-91
lines changed

8 files changed

+250
-91
lines changed

common/src/main/kotlin/com/lambda/module/modules/client/NewCGui.kt

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,26 @@
11
package com.lambda.module.modules.client
22

3-
import com.lambda.Lambda.mc
43
import com.lambda.module.Module
54
import com.lambda.module.tag.ModuleTag
65
import com.lambda.newgui.LambdaScreen.Companion.gui
76
import com.lambda.newgui.LambdaScreen.Companion.toScreen
8-
import com.lambda.newgui.component.HAlign
9-
import com.lambda.newgui.component.core.TextField.Companion.textField
7+
import com.lambda.newgui.component.window.Window
108
import com.lambda.newgui.component.window.Window.Companion.window
119
import com.lambda.util.math.Vec2d
12-
import java.awt.Color
1310

1411
object NewCGui : Module(
1512
name = "NewCGui",
1613
description = "ggs",
1714
defaultTags = setOf(ModuleTag.CLIENT)
1815
) {
1916
val titleBarHeight by setting("Title Bar Height", 4.0, 0.0..10.0, 0.1)
17+
val padding by setting("Padding", 2.0, 0.0..6.0, 0.1)
2018

2119
private val clickGuiLayout =
2220
gui {
2321
window(position = Vec2d.ONE * 20.0, title = "Test window") {
24-
titleBar.textField.apply {
25-
text = "Overriding the title"
22+
content.window(Vec2d.ONE * 5.0, Vec2d.ONE * 60.0) {
2623

27-
// Making it align the left corner and have 3px offset from the left side
28-
alignment = HAlign.LEFT
29-
offset = 3.0
30-
}
31-
32-
textField("Text field over the window")
33-
34-
content.textField("Text field inside of the content region") {
35-
alignment = HAlign.CENTER
36-
scale = 0.5
37-
38-
onTick {
39-
// Dynamically updating states
40-
color = if (mc.player?.isDead == true) Color.RED else Color.GREEN
41-
}
4224
}
4325
}
4426
}

common/src/main/kotlin/com/lambda/newgui/LambdaScreen.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import com.lambda.event.events.TickEvent
77
import com.lambda.event.listener.SafeListener.Companion.listener
88
import com.lambda.graphics.RenderMain
99
import com.lambda.gui.api.GuiEvent
10+
import com.lambda.newgui.component.layout.Layout
11+
import com.lambda.newgui.component.core.UIBuilder
1012
import com.lambda.util.KeyCode
1113
import com.lambda.util.Mouse
1214
import com.lambda.util.Nameable
@@ -119,7 +121,7 @@ class LambdaScreen(
119121
Layout(owner = null, useBatching = false, batchChildren = true).apply {
120122
var screenSize = Vec2d.ONE * 10000.0
121123

122-
rect {
124+
rectUpdate {
123125
Rect(Vec2d.ZERO, screenSize)
124126
}
125127

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.lambda.newgui.component.core
2+
3+
interface IListEntry {
4+
var heightOffset: Double
5+
}
Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package com.lambda.newgui.component.core
22

3-
import com.lambda.newgui.Layout
4-
import com.lambda.newgui.UIBuilder
5-
import com.lambda.newgui.component.HAlign
3+
import com.lambda.newgui.component.VAlign
4+
import com.lambda.newgui.component.layout.Layout
65
import com.lambda.util.math.MathUtils.lerp
7-
import com.lambda.util.math.Rect
86
import com.lambda.util.math.Vec2d
97
import java.awt.Color
108

@@ -14,34 +12,42 @@ class TextField(
1412
initialColor: Color = Color.WHITE,
1513
initialScale: Double = 1.0,
1614
initialShadow: Boolean = true,
17-
initialAlignment: HAlign = HAlign.LEFT,
1815
initialOffset: Double = 0.0,
1916
) : Layout(owner, true, true) {
2017
var text = initialText
2118
var color = initialColor
2219
var scale = initialScale
2320
var shadow = initialShadow
2421

25-
var alignment = initialAlignment
2622
var offset = initialOffset
2723

2824
// Let user interact through the text
2925
override val interactionPassthrough = true
3026

3127
init {
32-
rect {
33-
// Completely fill parent component by default
34-
Rect(Vec2d.ZERO, owner.rect.size)
35-
}
28+
verticalAlignment = VAlign.CENTER
29+
rectUpdate(owner::rect)
3630

3731
onRender {
32+
val w = font.getWidth(text, scale)
33+
val h = font.getHeight(scale)
34+
3835
val x = lerp(
3936
rect.left,
40-
rect.right - font.getWidth(text, scale),
41-
alignment.multiplier
42-
) - offset * alignment.offset
37+
rect.right - w,
38+
horizontalAlignment.multiplier
39+
) - offset * horizontalAlignment.offset
40+
41+
val y = when {
42+
verticalAlignment == VAlign.CENTER || rect.size.y <= h -> rect.center.y
43+
else -> lerp(
44+
rect.top + h * 0.5,
45+
rect.bottom - h * 0.5,
46+
verticalAlignment.multiplier
47+
)
48+
}
4349

44-
font.build(text, Vec2d(x, rect.center.y), color, scale, shadow)
50+
font.build(text, Vec2d(x, y), color, scale, shadow)
4551
}
4652
}
4753

@@ -52,9 +58,8 @@ class TextField(
5258
color: Color = Color.WHITE,
5359
scale: Double = 1.0,
5460
shadow: Boolean = true,
55-
alignment: HAlign = HAlign.LEFT,
5661
offset: Double = 0.0,
5762
block: TextField.() -> Unit = {}
58-
) = TextField(this, text, color, scale, shadow, alignment, offset).apply(children::add).apply(block)
63+
) = TextField(this, text, color, scale, shadow, offset).apply(children::add).apply(block)
5964
}
6065
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.lambda.newgui.component.core
2+
3+
@DslMarker
4+
annotation class UIBuilder

common/src/main/kotlin/com/lambda/newgui/Layout.kt renamed to common/src/main/kotlin/com/lambda/newgui/component/layout/Layout.kt

Lines changed: 93 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
package com.lambda.newgui
1+
package com.lambda.newgui.component.layout
22

3+
import com.lambda.graphics.RenderMain
34
import com.lambda.graphics.animation.AnimationTicker
45
import com.lambda.graphics.gl.Scissor.scissor
56
import com.lambda.gui.api.GuiEvent
67
import 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
711
import com.lambda.util.KeyCode
812
import com.lambda.util.Mouse
13+
import com.lambda.util.math.MathUtils.coerceIn
914
import com.lambda.util.math.Rect
1015
import com.lambda.util.math.Vec2d
1116

@@ -30,12 +35,65 @@ import com.lambda.util.math.Vec2d
3035
* ```
3136
*/
3237
open 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

Comments
 (0)