1414package de.bixilon.minosoft.gui.rendering.gui.elements.input.slider
1515
1616import de.bixilon.kmath.vec.vec2.f.Vec2f
17- import de.bixilon.kmath.vec.vec2.i.Vec2i
1817import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft
1918import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
2019import de.bixilon.minosoft.gui.rendering.gui.elements.Element
2120import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.AtlasImageElement
2221import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextElement
22+ import de.bixilon.minosoft.gui.rendering.gui.input.MouseCapturing
23+ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions.Companion.copy
2324import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions
2425import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons
2526import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
2627import de.bixilon.minosoft.gui.rendering.gui.mesh.consumer.GuiVertexConsumer
28+ import de.bixilon.minosoft.gui.rendering.system.window.CursorShapes
2729import kotlin.math.roundToInt
2830
2931class SliderElement (
@@ -33,12 +35,22 @@ class SliderElement(
3335 private val max : Float ,
3436 initialValue : Float ,
3537 private val onChange : (Float ) -> Unit
36- ) : Element(guiRenderer) {
38+ ) : Element(guiRenderer), MouseCapturing {
3739 private val buttonAtlas = guiRenderer.atlas[BUTTON_ATLAS ]
38- private val sliderAtlas = guiRenderer.atlas[SLIDER_ATLAS ]
3940
4041 var textElement: TextElement
4142 private var isDragging = false
43+ private var isHovered = false
44+ private var isHandleHovered = false
45+
46+ var disabled: Boolean = false
47+ set(value) {
48+ if (field == value) return
49+ field = value
50+ cacheUpToDate = false
51+ }
52+
53+ override val isCapturingMouse: Boolean get() = isDragging
4254
4355 var value: Float = initialValue.coerceIn(min, max)
4456 set(value) {
@@ -54,8 +66,7 @@ class SliderElement(
5466 init {
5567 textElement = TextElement (guiRenderer, getDisplayText(), background = null , parent = this )
5668 updateText()
57- // Set initial size based on text element size with padding (similar to ButtonElement)
58- size = Vec2f (textElement.size.x + TEXT_PADDING * 2 , 20.0f )
69+ size = Vec2f (textElement.size.x + TEXT_PADDING * 2 , SLIDER_HEIGHT )
5970 }
6071
6172 private fun getDisplayText (): String {
@@ -66,7 +77,7 @@ class SliderElement(
6677 textElement.text = getDisplayText()
6778 textElement.silentApply()
6879 if (size.x == 0.0f ) {
69- size = Vec2f (textElement.size.x + TEXT_PADDING * 2 , 20.0f )
80+ size = Vec2f (textElement.size.x + TEXT_PADDING * 2 , SLIDER_HEIGHT )
7081 }
7182 }
7283
@@ -77,26 +88,40 @@ class SliderElement(
7788
7889 override fun forceRender (offset : Vec2f , consumer : GuiVertexConsumer , options : GUIVertexOptions ? ) {
7990 val size = size
80- val sliderHeight = 20.0f
91+ val renderOptions = if (disabled) options.copy(alpha = 0.4f ) else options
8192
82- val background = AtlasImageElement (guiRenderer, buttonAtlas?.get(" normal" ) ? : guiRenderer.context.textures.whiteTexture)
83- background.size = size
84- background.render(offset, consumer, options)
93+ val trackTexture = buttonAtlas?.get(" disabled" ) ? : guiRenderer.context.textures.whiteTexture
94+ val trackBackground = AtlasImageElement (guiRenderer, trackTexture)
95+ trackBackground.size = size
96+ trackBackground.render(offset, consumer, renderOptions)
8597
8698 val normalizedValue = (value - min) / (max - min)
87- val sliderWidth = 8.0f
88- val trackWidth = size.x - sliderWidth
89- val sliderX = trackWidth * normalizedValue
90- val slider = AtlasImageElement (guiRenderer, buttonAtlas?.get(" hovered" ) ? : guiRenderer.context.textures.whiteTexture)
91- slider.size = Vec2f (sliderWidth, sliderHeight)
92- slider.render(offset + Vec2f (sliderX, 0.0f ), consumer, options)
99+ val handleWidth = HANDLE_WIDTH
100+ val trackWidth = size.x - handleWidth
101+ val handleX = trackWidth * normalizedValue
102+
103+ val handleTexture = if (disabled) {
104+ buttonAtlas?.get(" disabled" )
105+ } else if (isHandleHovered || isDragging) {
106+ buttonAtlas?.get(" hovered" )
107+ } else {
108+ buttonAtlas?.get(" normal" )
109+ } ? : guiRenderer.context.textures.whiteTexture
110+
111+ val slider = AtlasImageElement (guiRenderer, handleTexture)
112+ slider.size = Vec2f (handleWidth, SLIDER_HEIGHT )
113+ slider.render(offset + Vec2f (handleX, 0.0f ), consumer, renderOptions)
114+
93115 val textSize = textElement.size
94116 val textX = (size.x - textSize.x) / 2
95117 val textY = (size.y - textSize.y) / 2
96- textElement.render(offset + Vec2f (textX, textY), consumer, options )
118+ textElement.render(offset + Vec2f (textX, textY), consumer, renderOptions )
97119 }
98120
99121 override fun onMouseAction (position : Vec2f , button : MouseButtons , action : MouseActions , count : Int ): Boolean {
122+ if (disabled) {
123+ return true
124+ }
100125 if (button != MouseButtons .LEFT ) {
101126 return true
102127 }
@@ -105,9 +130,12 @@ class SliderElement(
105130 MouseActions .PRESS -> {
106131 isDragging = true
107132 updateValueFromPosition(position.x)
133+ cacheUpToDate = false
108134 }
109135 MouseActions .RELEASE -> {
110136 isDragging = false
137+ context.window.resetCursor()
138+ cacheUpToDate = false
111139 }
112140 }
113141
@@ -118,31 +146,84 @@ class SliderElement(
118146 if (isDragging) {
119147 updateValueFromPosition(position.x)
120148 }
149+
150+ if (! isDragging) {
151+ val normalizedValue = (value - min) / (max - min)
152+ val handleWidth = HANDLE_WIDTH
153+ val trackWidth = size.x - handleWidth
154+ val handleX = trackWidth * normalizedValue
155+
156+ val wasHandleHovered = isHandleHovered
157+ isHandleHovered = position.x >= handleX && position.x < handleX + handleWidth
158+
159+ if (wasHandleHovered != isHandleHovered) {
160+ cacheUpToDate = false
161+ }
162+ }
163+
164+ return true
165+ }
166+
167+ override fun onMouseEnter (position : Vec2f , absolute : Vec2f ): Boolean {
168+ isHovered = true
169+ context.window.cursorShape = CursorShapes .HAND
170+
171+ val normalizedValue = (value - min) / (max - min)
172+ val handleWidth = HANDLE_WIDTH
173+ val trackWidth = size.x - handleWidth
174+ val handleX = trackWidth * normalizedValue
175+
176+ isHandleHovered = position.x >= handleX && position.x < handleX + handleWidth
177+ cacheUpToDate = false
121178 return true
122179 }
123180
124181 override fun onMouseLeave (): Boolean {
125- isDragging = false
182+ isHovered = false
183+ isHandleHovered = false
184+ if (! isDragging) {
185+ context.window.resetCursor()
186+ }
187+ cacheUpToDate = false
126188 return super .onMouseLeave()
127189 }
190+
191+ override fun onMouseActionOutside (relativeX : Float , button : MouseButtons , action : MouseActions ): Boolean {
192+ if (! isDragging) return false
193+
194+ if (button == MouseButtons .LEFT && action == MouseActions .RELEASE ) {
195+ isDragging = false
196+ context.window.resetCursor()
197+ cacheUpToDate = false
198+ return true
199+ }
200+ return false
201+ }
202+
203+ override fun onMouseMoveOutside (relativeX : Float ): Boolean {
204+ if (! isDragging) return false
205+ updateValueFromPosition(relativeX)
206+ return true
207+ }
128208
129209 private fun updateValueFromPosition (x : Float ) {
130- val sliderWidth = 8.0f
131- val trackWidth = size.x - sliderWidth
210+ val handleWidth = HANDLE_WIDTH
211+ val trackWidth = size.x - handleWidth
132212
133213 if (trackWidth <= 0 ) {
134214 value = min
135215 return
136216 }
137217
138- val normalizedX = (x - sliderWidth / 2 ) / trackWidth
218+ val normalizedX = (x - handleWidth / 2 ) / trackWidth
139219 value = min + normalizedX * (max - min)
140220 }
141221
142222 companion object {
143223 val BUTTON_ATLAS = minecraft(" elements/button" )
144- val SLIDER_ATLAS = minecraft(" elements/slider" )
145224 private const val TEXT_PADDING = 4.0f
225+ private const val SLIDER_HEIGHT = 20.0f
226+ private const val HANDLE_WIDTH = 8.0f
146227 }
147228}
148229
0 commit comments