Skip to content

Commit 391a5fb

Browse files
committed
Move HUD Settings from a dummy module to actual Configurable
1 parent c73f682 commit 391a5fb

File tree

6 files changed

+179
-117
lines changed

6 files changed

+179
-117
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.config.configurations
19+
20+
import com.lambda.config.Configuration
21+
import com.lambda.util.FolderRegister
22+
import java.io.File
23+
24+
object HudConfig : Configuration() {
25+
override val configName get() = "hud"
26+
override val primary: File = FolderRegister.config.resolve("$configName.json").toFile()
27+
}

src/main/kotlin/com/lambda/gui/components/HudGuiLayout.kt

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,59 @@
1717

1818
package com.lambda.gui.components
1919

20+
import com.lambda.config.Configurable
21+
import com.lambda.config.configurations.HudConfig
2022
import com.lambda.core.Loadable
2123
import com.lambda.event.events.GuiEvent
2224
import com.lambda.event.listener.SafeListener.Companion.listen
23-
import com.lambda.gui.components.ModuleEntry.Companion.buildModuleSettingsContext
25+
import com.lambda.gui.components.SettingsWidget.buildConfigSettingsContext
2426
import com.lambda.gui.dsl.ImGuiBuilder
2527
import com.lambda.gui.dsl.ImGuiBuilder.buildLayout
2628
import com.lambda.gui.snap.Guide
2729
import com.lambda.gui.snap.RectF
2830
import com.lambda.gui.snap.SnapManager
2931
import com.lambda.module.HudModule
3032
import com.lambda.module.ModuleRegistry
31-
import com.lambda.module.modules.client.GuiSettings
3233
import com.lambda.module.modules.client.ClickGui
34+
import com.lambda.util.NamedEnum
3335
import imgui.ImColor
3436
import imgui.ImGui
3537
import imgui.ImDrawList
3638
import imgui.flag.ImDrawListFlags
3739
import imgui.flag.ImGuiWindowFlags
3840
import imgui.flag.ImGuiStyleVar
41+
import java.awt.Color
3942
import kotlin.math.PI
4043
import kotlin.math.max
4144

42-
object HudGuiLayout : Loadable {
45+
object HudGuiLayout : Loadable, Configurable(HudConfig) {
46+
override val name = "HUD"
47+
48+
enum class Group(override val displayName: String) : NamedEnum {
49+
Snapping("Snapping"),
50+
HudOutline("HUD Outline")
51+
}
52+
53+
// Snapping
54+
val snapEnabled by setting("Enable Snapping", true, "Master toggle for HUD snapping").group(Group.Snapping)
55+
val gridSize by setting("Grid Size", 16f, 2f..128f, 1f, "Grid step in pixels") { snapEnabled }.group(Group.Snapping)
56+
val snapToEdges by setting("Snap To Element Edges", true) { snapEnabled }.group(Group.Snapping)
57+
val snapToCenters by setting("Snap To Element Centers", true) { snapEnabled }.group(Group.Snapping)
58+
val snapToScreenCenter by setting("Snap To Screen Center", true) { snapEnabled }.group(Group.Snapping)
59+
val snapToGrid by setting("Snap To Grid", true) { snapEnabled }.group(Group.Snapping)
60+
val snapDistanceElement by setting("Snap Distance (Elements)", 20f, 1f..48f, 1f, "Distance threshold in px") { snapEnabled }.group(Group.Snapping)
61+
val snapDistanceScreen by setting("Snap Distance (Screen Center)", 14f, 1f..48f, 1f) { snapEnabled }.group(Group.Snapping)
62+
val snapDistanceGrid by setting("Snap Distance (Grid)", 12f, 1f..48f, 1f) { snapEnabled }.group(Group.Snapping)
63+
val snapLineColor by setting("Snap Line Color", Color(255, 160, 0, 220)) { snapEnabled }.group(Group.Snapping)
64+
65+
// HUD Outline
66+
val hudOutlineCornerRadius by setting("HUD Corner Radius", 6.0f, 0.0f..24.0f, 0.5f).group(Group.HudOutline)
67+
val hudOutlineHaloColor by setting("HUD Corner Halo Color", Color(140, 140, 140, 90)).group(Group.HudOutline)
68+
val hudOutlineBorderColor by setting("HUD Corner Border Color", Color(190, 190, 190, 200)).group(Group.HudOutline)
69+
val hudOutlineHaloThickness by setting("HUD Corner Halo Thickness", 3.0f, 1.0f..6.0f, 0.5f).group(Group.HudOutline)
70+
val hudOutlineBorderThickness by setting("HUD Corner Border Thickness", 1.5f, 1.0f..4.0f, 0.5f).group(Group.HudOutline)
71+
val hudOutlineCornerInflate by setting("HUD Corner Inflate", 1.0f, 0.0f..4.0f, 0.5f, "Extra radius for the halo arc").group(Group.HudOutline)
72+
4373
const val DEFAULT_HUD_FLAGS =
4474
ImGuiWindowFlags.NoDecoration or
4575
ImGuiWindowFlags.NoBackground or
@@ -78,7 +108,7 @@ object HudGuiLayout : Loadable {
78108
}
79109
separator()
80110
menu("HUD Settings") {
81-
buildModuleSettingsContext(GuiSettings)
111+
buildConfigSettingsContext(this@HudGuiLayout)
82112
}
83113
}
84114
return@buildLayout
@@ -162,7 +192,7 @@ object HudGuiLayout : Loadable {
162192
SnapManager.unregisterElement(hud.name)
163193
}
164194
separator()
165-
buildModuleSettingsContext(hud)
195+
buildConfigSettingsContext(hud)
166196
}
167197

168198
if (ClickGui.isEnabled && !isLocked) {
@@ -189,20 +219,20 @@ object HudGuiLayout : Loadable {
189219
}
190220
separator()
191221
menu("HUD Settings") {
192-
buildModuleSettingsContext(GuiSettings)
222+
buildConfigSettingsContext(this@HudGuiLayout)
193223
}
194224
separator()
195225
if (notShown.isEmpty()) {
196226
textDisabled("No hidden HUD elements")
197227
} else {
198-
text("Add HUD Element:")
199-
separator()
200-
notShown.sortedBy { it.name.lowercase() }.forEach { hud ->
201-
menuItem("+ ${hud.name}") {
202-
val mx = io.mousePos.x
203-
val my = io.mousePos.y
204-
hud.enable()
205-
pendingPositions[hud.name] = mx to my
228+
menu("Add HUD Element") {
229+
notShown.sortedBy { it.name.lowercase() }.forEach { hud ->
230+
menuItem("+ ${hud.name}") {
231+
val mx = io.mousePos.x
232+
val my = io.mousePos.y
233+
hud.enable()
234+
pendingPositions[hud.name] = mx to my
235+
}
206236
}
207237
}
208238
}
@@ -240,9 +270,9 @@ object HudGuiLayout : Loadable {
240270
}
241271

242272
private fun ImGuiBuilder.drawDragGrid() {
243-
if (!GuiSettings.snapEnabled || !GuiSettings.snapToGrid) return
273+
if (!snapEnabled || !snapToGrid) return
244274
val vp = ImGui.getMainViewport()
245-
val step = max(4f, GuiSettings.gridSize * io.fontGlobalScale)
275+
val step = max(4f, gridSize * io.fontGlobalScale)
246276
if (step <= 0f) return
247277

248278
val x0 = vp.posX
@@ -267,24 +297,24 @@ object HudGuiLayout : Loadable {
267297
}
268298

269299
private fun ImGuiBuilder.drawHudCornerArcs(draw: ImDrawList, x: Float, y: Float, w: Float, h: Float) {
270-
val baseRadius = GuiSettings.hudOutlineCornerRadius
300+
val baseRadius = hudOutlineCornerRadius
271301
val rounding = if (baseRadius > 0f) baseRadius else style.windowRounding
272-
val inflate = GuiSettings.hudOutlineCornerInflate
302+
val inflate = hudOutlineCornerInflate
273303
// Soft halo corners
274304
drawCornerArcs(
275305
draw,
276306
x, y, w, h,
277307
(rounding + inflate).coerceAtLeast(0f),
278-
GuiSettings.hudOutlineHaloColor.rgb,
279-
GuiSettings.hudOutlineHaloThickness
308+
hudOutlineHaloColor.rgb,
309+
hudOutlineHaloThickness
280310
)
281311
// Crisp inner corner arcs
282312
drawCornerArcs(
283313
draw,
284314
x, y, w, h,
285315
rounding.coerceAtLeast(0f),
286-
GuiSettings.hudOutlineBorderColor.rgb,
287-
GuiSettings.hudOutlineBorderThickness
316+
hudOutlineBorderColor.rgb,
317+
hudOutlineBorderThickness
288318
)
289319
}
290320

src/main/kotlin/com/lambda/gui/components/ModuleEntry.kt

Lines changed: 2 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,11 @@
1717

1818
package com.lambda.gui.components
1919

20-
import com.lambda.config.AbstractSetting
2120
import com.lambda.gui.Layout
21+
import com.lambda.gui.components.SettingsWidget.buildConfigSettingsContext
2222
import com.lambda.gui.dsl.ImGuiBuilder
2323
import com.lambda.module.Module
24-
import com.lambda.util.NamedEnum
25-
import com.lambda.util.KeyCode
2624
import imgui.ImGui
27-
import imgui.flag.ImGuiTabBarFlags
28-
import imgui.flag.ImGuiCol
2925

3026
class ModuleEntry(val module: Module): Layout {
3127
override fun ImGuiBuilder.buildLayout() {
@@ -36,62 +32,7 @@ class ModuleEntry(val module: Module): Layout {
3632

3733
ImGui.setNextWindowSizeConstraints(0f, 0f, Float.MAX_VALUE, io.displaySize.y * 0.5f)
3834
popupContextItem("##ctx-${module.name}") {
39-
buildModuleSettingsContext(module)
40-
}
41-
}
42-
43-
companion object {
44-
/**
45-
* Builds the settings context popup content for a given module.
46-
*/
47-
fun ImGuiBuilder.buildModuleSettingsContext(module: Module) {
48-
group {
49-
with(module.keybindSetting) { buildLayout() }
50-
sameLine()
51-
smallButton("Reset") {
52-
module.settings.forEach { it.reset(silent = true) }
53-
}
54-
lambdaTooltip("Resets all settings for this module to their default values")
55-
}
56-
separator()
57-
val visibleSettings = module.settings.filter { it.visibility() } - module.keybindSetting
58-
val (grouped, ungrouped) = visibleSettings.partition { it.groups.isNotEmpty() }
59-
ungrouped.forEach { with(it) { buildLayout() } }
60-
renderGroup(grouped, emptyList(), module)
61-
}
62-
63-
private fun ImGuiBuilder.renderGroup(
64-
settings: List<AbstractSetting<*>>,
65-
parentPath: List<NamedEnum>,
66-
module: Module
67-
) {
68-
settings.filter { it.groups.contains(parentPath) }.forEach { with(it) { buildLayout() } }
69-
70-
val subGroupSettings = settings.filter { s ->
71-
s.groups.any { it.size > parentPath.size && it.subList(0, parentPath.size) == parentPath }
72-
}
73-
val subTabs = subGroupSettings
74-
.flatMap { s ->
75-
s.groups.mapNotNull { path ->
76-
if (path.size > parentPath.size && path.subList(0, parentPath.size) == parentPath)
77-
path[parentPath.size] else null
78-
}
79-
}.distinct()
80-
81-
if (subTabs.isNotEmpty()) {
82-
val id = "##${module.name}-tabs-${parentPath.joinToString("-") { it.displayName }}"
83-
tabBar(id, ImGuiTabBarFlags.FittingPolicyResizeDown) {
84-
subTabs.forEach { tab ->
85-
tabItem(tab.displayName) {
86-
val newParentPath = parentPath + tab
87-
val settingsForSubGroup = subGroupSettings.filter { s ->
88-
s.groups.any { it.size >= newParentPath.size && it.subList(0, newParentPath.size) == newParentPath }
89-
}
90-
renderGroup(settingsForSubGroup, newParentPath, module)
91-
}
92-
}
93-
}
94-
}
35+
buildConfigSettingsContext(module)
9536
}
9637
}
9738
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.gui.components
19+
20+
import com.lambda.config.AbstractSetting
21+
import com.lambda.config.Configurable
22+
import com.lambda.gui.dsl.ImGuiBuilder
23+
import com.lambda.module.Module
24+
import com.lambda.util.NamedEnum
25+
import imgui.flag.ImGuiTabBarFlags
26+
27+
object SettingsWidget {
28+
/**
29+
* Builds the settings context popup content for a given configurable.
30+
*/
31+
fun ImGuiBuilder.buildConfigSettingsContext(config: Configurable) {
32+
group {
33+
if (config is Module) {
34+
with(config.keybindSetting) { buildLayout() }
35+
}
36+
sameLine()
37+
smallButton("Reset") {
38+
config.settings.forEach { it.reset(silent = true) }
39+
}
40+
lambdaTooltip("Resets all settings for this module to their default values")
41+
}
42+
separator()
43+
val toIgnoreSettings = if (config is Module) setOf(config.keybindSetting) else emptySet()
44+
val visibleSettings = config.settings.filter { it.visibility() } - toIgnoreSettings
45+
val (grouped, ungrouped) = visibleSettings.partition { it.groups.isNotEmpty() }
46+
ungrouped.forEach { with(it) { buildLayout() } }
47+
renderGroup(grouped, emptyList(), config)
48+
}
49+
50+
private fun ImGuiBuilder.renderGroup(
51+
settings: List<AbstractSetting<*>>,
52+
parentPath: List<NamedEnum>,
53+
config: Configurable
54+
) {
55+
settings.filter { it.groups.contains(parentPath) }.forEach { with(it) { buildLayout() } }
56+
57+
val subGroupSettings = settings.filter { s ->
58+
s.groups.any { it.size > parentPath.size && it.subList(0, parentPath.size) == parentPath }
59+
}
60+
val subTabs = subGroupSettings
61+
.flatMap { s ->
62+
s.groups.mapNotNull { path ->
63+
if (path.size > parentPath.size && path.subList(0, parentPath.size) == parentPath)
64+
path[parentPath.size] else null
65+
}
66+
}.distinct()
67+
68+
if (subTabs.isNotEmpty()) {
69+
val id = "##${config.name}-tabs-${parentPath.joinToString("-") { it.displayName }}"
70+
tabBar(id, ImGuiTabBarFlags.FittingPolicyResizeDown) {
71+
subTabs.forEach { tab ->
72+
tabItem(tab.displayName) {
73+
val newParentPath = parentPath + tab
74+
val settingsForSubGroup = subGroupSettings.filter { s ->
75+
s.groups.any { it.size >= newParentPath.size && it.subList(0, newParentPath.size) == newParentPath }
76+
}
77+
renderGroup(settingsForSubGroup, newParentPath, config)
78+
}
79+
}
80+
}
81+
}
82+
}
83+
}

src/main/kotlin/com/lambda/gui/snap/SnapManager.kt

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.lambda.gui.snap
22

3-
import com.lambda.module.modules.client.GuiSettings
4-
import com.lambda.module.modules.client.GuiSettings.gridSize
5-
import com.lambda.module.modules.client.GuiSettings.snapEnabled
6-
import com.lambda.module.modules.client.GuiSettings.snapToCenters
7-
import com.lambda.module.modules.client.GuiSettings.snapToEdges
8-
import com.lambda.module.modules.client.GuiSettings.snapToGrid
9-
import com.lambda.module.modules.client.GuiSettings.snapToScreenCenter
3+
import com.lambda.gui.components.HudGuiLayout.gridSize
4+
import com.lambda.gui.components.HudGuiLayout.snapDistanceElement
5+
import com.lambda.gui.components.HudGuiLayout.snapDistanceGrid
6+
import com.lambda.gui.components.HudGuiLayout.snapDistanceScreen
7+
import com.lambda.gui.components.HudGuiLayout.snapEnabled
8+
import com.lambda.gui.components.HudGuiLayout.snapLineColor
9+
import com.lambda.gui.components.HudGuiLayout.snapToCenters
10+
import com.lambda.gui.components.HudGuiLayout.snapToEdges
11+
import com.lambda.gui.components.HudGuiLayout.snapToGrid
12+
import com.lambda.gui.components.HudGuiLayout.snapToScreenCenter
1013
import imgui.ImDrawList
1114
import kotlin.math.abs
1215
import kotlin.math.max
@@ -78,9 +81,9 @@ object SnapManager {
7881
)
7982

8083
private fun thresholdFor(kind: Guide.Kind): Float = when (kind) {
81-
Guide.Kind.ElementEdge, Guide.Kind.ElementCenter -> GuiSettings.snapDistanceElement * scale
82-
Guide.Kind.ScreenCenter -> GuiSettings.snapDistanceScreen * scale
83-
Guide.Kind.Grid -> GuiSettings.snapDistanceGrid * scale
84+
Guide.Kind.ElementEdge, Guide.Kind.ElementCenter -> snapDistanceElement * scale
85+
Guide.Kind.ScreenCenter -> snapDistanceScreen * scale
86+
Guide.Kind.Grid -> snapDistanceGrid * scale
8487
}
8588

8689
private fun score(dist: Float, strength: Int): Float = dist - strength * 0.08f
@@ -161,7 +164,7 @@ object SnapManager {
161164
val showY = kindY == Guide.Kind.ElementEdge || kindY == Guide.Kind.ElementCenter
162165
if (!showX && !showY) return
163166

164-
val col = GuiSettings.snapLineColor.rgb
167+
val col = snapLineColor.rgb
165168
val thick = 2f
166169
if (showX && snapX != null) draw.addLine(snapX, 0f, snapX, viewH, col, thick)
167170
if (showY && snapY != null) draw.addLine(0f, snapY, viewW, snapY, col, thick)

0 commit comments

Comments
 (0)