1
+ /*
2
+ * Copyright 2021 DeathsGun
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package xyz.deathsgun.modmanager.api.gui.list
18
+
19
+ import com.mojang.blaze3d.systems.RenderSystem
20
+ import net.minecraft.client.MinecraftClient
21
+ import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder
22
+ import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget
23
+ import net.minecraft.client.render.GameRenderer
24
+ import net.minecraft.client.render.Tessellator
25
+ import net.minecraft.client.render.VertexFormat
26
+ import net.minecraft.client.render.VertexFormats
27
+ import net.minecraft.client.util.math.MatrixStack
28
+ import net.minecraft.util.math.MathHelper
29
+ import java.util.*
30
+ import kotlin.math.max
31
+
32
+ /* *
33
+ * Using ModMenu's implementation because the implementation from
34
+ * Mojang is broken. [com.terraformersmc.modmenu.gui.ModsScreen].
35
+ * All credits for this code go to the Terraformer's
36
+ */
37
+ abstract class MultiSelectListWidget <E : MultiSelectListWidget .Entry <E >>(
38
+ client : MinecraftClient ,
39
+ width : Int ,
40
+ height : Int ,
41
+ top : Int ,
42
+ var bottom : Int ,
43
+ itemHeight : Int ,
44
+ private val parent : IListScreen
45
+ ) : AlwaysSelectedEntryListWidget<E>(client, width, height, top, bottom, itemHeight) {
46
+
47
+ var renderOutline: Boolean = true
48
+ private var selectedIds = ArrayList <String >()
49
+ private var scrolling = false
50
+
51
+ abstract fun init ()
52
+
53
+ override fun isFocused (): Boolean {
54
+ return parent.getFocused() == this
55
+ }
56
+
57
+ override fun setSelected (entry : E ? ) {
58
+ super .setSelected(entry)
59
+ if (entry == null ) {
60
+ return
61
+ }
62
+ if (selectedIds.contains(entry.id)) {
63
+ selectedIds.removeIf { it == entry.id }
64
+ } else {
65
+ selectedIds.add(entry.id)
66
+ }
67
+ parent.updateMultipleEntries(this , ArrayList (children().filter { selectedIds.contains(it.id) }))
68
+ }
69
+
70
+ fun setSelected (entries : List <E >) {
71
+ selectedIds.clear()
72
+ for (entry in entries) {
73
+ setSelected(entry)
74
+ }
75
+ }
76
+
77
+ override fun isSelectedEntry (index : Int ): Boolean {
78
+ return selectedIds.contains(getEntry(index).id)
79
+ }
80
+
81
+ override fun addEntry (entry : E ): Int {
82
+ val i = super .addEntry(entry)
83
+ if (selectedIds.contains(entry.id)) {
84
+ setSelected(entry)
85
+ }
86
+ return i
87
+ }
88
+
89
+ override fun renderList (matrices : MatrixStack ? , x : Int , y : Int , mouseX : Int , mouseY : Int , delta : Float ) {
90
+ val itemCount = this .entryCount
91
+ val tessellator = Tessellator .getInstance()
92
+ val buffer = tessellator.buffer
93
+
94
+ for (index in 0 until itemCount) {
95
+ val entryTop = getRowTop(index) + 2
96
+ val entryBottom = getRowTop(index) + itemHeight
97
+ if (entryBottom >= top && entryTop <= bottom) {
98
+ val entryHeight = itemHeight - 4
99
+ val entry: Entry <E > = getEntry(index)
100
+ val rowWidth = this .rowWidth
101
+ var entryLeft: Int
102
+ if (isSelectedEntry(index) && renderOutline) {
103
+ entryLeft = rowLeft - 2
104
+ val selectionRight = x + rowWidth + 2
105
+ RenderSystem .disableTexture()
106
+ val color = if (this .isFocused) 1.0f else 0.5f
107
+ RenderSystem .setShader { GameRenderer .getPositionShader() }
108
+ RenderSystem .setShaderColor(color, color, color, 1.0f )
109
+ val matrix = matrices!! .peek().model
110
+ buffer.begin(VertexFormat .DrawMode .QUADS , VertexFormats .POSITION )
111
+ buffer.vertex(matrix, entryLeft.toFloat(), (entryTop + entryHeight + 2 ).toFloat(), 0.0f ).next()
112
+ buffer.vertex(matrix, selectionRight.toFloat(), (entryTop + entryHeight + 2 ).toFloat(), 0.0f ).next()
113
+ buffer.vertex(matrix, selectionRight.toFloat(), (entryTop - 2 ).toFloat(), 0.0f ).next()
114
+ buffer.vertex(matrix, entryLeft.toFloat(), (entryTop - 2 ).toFloat(), 0.0f ).next()
115
+ tessellator.draw()
116
+ RenderSystem .setShader { GameRenderer .getPositionShader() }
117
+ RenderSystem .setShaderColor(0.0f , 0.0f , 0.0f , 1.0f )
118
+ buffer.begin(VertexFormat .DrawMode .QUADS , VertexFormats .POSITION )
119
+ buffer.vertex(matrix, (entryLeft + 1 ).toFloat(), (entryTop + entryHeight + 1 ).toFloat(), 0.0f )
120
+ .next()
121
+ buffer.vertex(matrix, (selectionRight - 1 ).toFloat(), (entryTop + entryHeight + 1 ).toFloat(), 0.0f )
122
+ .next()
123
+ buffer.vertex(matrix, (selectionRight - 1 ).toFloat(), (entryTop - 1 ).toFloat(), 0.0f ).next()
124
+ buffer.vertex(matrix, (entryLeft + 1 ).toFloat(), (entryTop - 1 ).toFloat(), 0.0f ).next()
125
+ tessellator.draw()
126
+ RenderSystem .enableTexture()
127
+ }
128
+ entryLeft = this .rowLeft
129
+ entry.render(
130
+ matrices, index, entryTop, entryLeft, rowWidth, entryHeight, mouseX, mouseY, this .isMouseOver(
131
+ mouseX.toDouble(), mouseY.toDouble()
132
+ ) && Objects .equals(this .getEntryAtPos(mouseX, mouseY), entry), delta
133
+ )
134
+ }
135
+ }
136
+ }
137
+
138
+ override fun getRowWidth (): Int {
139
+ return width - if (max(0 , this .maxPosition - (bottom - top - 4 )) > 0 ) 18 else 12
140
+ }
141
+
142
+ override fun getRowLeft (): Int {
143
+ return left + 6
144
+ }
145
+
146
+ override fun getScrollbarPositionX (): Int {
147
+ return left + width - 6
148
+ }
149
+
150
+ override fun getMaxPosition (): Int {
151
+ return super .getMaxPosition() + 4
152
+ }
153
+
154
+ override fun appendNarrations (builder : NarrationMessageBuilder ? ) {
155
+ super .appendNarrations(builder)
156
+ }
157
+
158
+ fun isSelectedEntry (entry : Entry <E >): Boolean {
159
+ return selectedIds.contains(entry.id)
160
+ }
161
+
162
+ fun getEntryAtPos (x : Int , y : Int ): Entry <E >? {
163
+ val int5 = MathHelper .floor(y - top.toDouble()) - headerHeight + scrollAmount.toInt() - 4
164
+ val index = int5 / itemHeight
165
+ return if (x < this .scrollbarPositionX.toDouble() && x >= rowLeft.toDouble() && x <= (rowLeft + rowWidth).toDouble() && index >= 0
166
+ && int5 >= 0 && index < this .entryCount
167
+ ) children()[index] else null
168
+ }
169
+
170
+ fun getElementCount (): Int {
171
+ return super .getEntryCount()
172
+ }
173
+
174
+ public override fun remove (index : Int ): E ? {
175
+ return super .remove(index)
176
+ }
177
+
178
+ public override fun getEntry (index : Int ): E {
179
+ return super .getEntry(index)
180
+ }
181
+
182
+ abstract class Entry <E : Entry <E >>(open val list : MultiSelectListWidget <E >, val id : String ) :
183
+ AlwaysSelectedEntryListWidget .Entry <E >() {
184
+ @Suppress(" UNCHECKED_CAST" )
185
+ override fun mouseClicked (mouseX : Double , mouseY : Double , button : Int ): Boolean {
186
+ list.setSelected(this as E )
187
+ return true
188
+ }
189
+ }
190
+ }
0 commit comments