diff options
Diffstat (limited to 'src/main')
3 files changed, 219 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt index f538f2d53..df4f15e47 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt @@ -148,6 +148,9 @@ object CollectionUtils { return this } + operator fun IntRange.contains(range: IntRange): Boolean = + range.first in this && range.last in this + fun <E> MutableList<List<E>>.addAsSingletonList(text: E) { add(Collections.singletonList(text)) } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt index 09591c62d..304e31141 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt @@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.data.HighlightOnHoverSlot import at.hannibal2.skyhanni.data.ToolTipData import at.hannibal2.skyhanni.features.chroma.ChromaShaderManager import at.hannibal2.skyhanni.features.chroma.ChromaType +import at.hannibal2.skyhanni.utils.CollectionUtils.contains import at.hannibal2.skyhanni.utils.ColorUtils import at.hannibal2.skyhanni.utils.ColorUtils.darker import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyClicked @@ -590,6 +591,136 @@ interface Renderable { } } + fun scrollList( + list: List<Renderable>, + height: Int, + scrollValue: ScrollValue = ScrollValue(), + velocity: Double = 2.0, + button: Int? = null, + horizontalAlign: HorizontalAlignment = HorizontalAlignment.LEFT, + verticalAlign: VerticalAlignment = VerticalAlignment.TOP, + ) = object : Renderable { + override val width = list.maxOf { it.width } + override val height = height + override val horizontalAlign = horizontalAlign + override val verticalAlign = verticalAlign + + private val virtualHeight = list.sumOf { it.height } + + private val scroll = ScrollInput.Companion.Vertical( + scrollValue, + 0, + virtualHeight - height, + velocity, + button + ) + + private val end get() = scroll.asInt() + height + + override fun render(posX: Int, posY: Int) { + scroll.update( + isHovered(posX, posY) + ) + + var renderY = 0 + var virtualY = 0 + var found = false + list.forEach { + if ((virtualY..virtualY + it.height) in scroll.asInt()..end) { + it.renderXAligned(posX, posY + renderY, width) + GlStateManager.translate(0f, it.height.toFloat(), 0f) + renderY += it.height + found = true + } else if (found) { + found = false + if (renderY + it.height <= height) { + it.renderXAligned(posX, posY + renderY, width) + } + return@forEach + } + virtualY += it.height + } + GlStateManager.translate(0f, -renderY.toFloat(), 0f) + } + } + + fun scrollTable( + content: List<List<Renderable?>>, + height: Int, + scrollValue: ScrollValue = ScrollValue(), + velocity: Double = 2.0, + button: Int? = null, + xPadding: Int = 1, + yPadding: Int = 0, + hasHeader: Boolean = false, + horizontalAlign: HorizontalAlignment = HorizontalAlignment.LEFT, + verticalAlign: VerticalAlignment = VerticalAlignment.TOP, + ) = object : Renderable { + + val xOffsets: List<Int> = calculateTableXOffsets(content, xPadding) + val yOffsets: List<Int> = calculateTableYOffsets(content, yPadding) + + override val width = xOffsets.last() - xPadding + override val height = height + override val horizontalAlign = horizontalAlign + override val verticalAlign = verticalAlign + + private val virtualHeight = yOffsets.last() - yPadding + + private val end get() = scroll.asInt() + height - yPadding - 1 + + private val scroll = ScrollInput.Companion.Vertical( + scrollValue, + if (hasHeader) yOffsets[1] else 0, + virtualHeight - height, + velocity, + button + ) + + override fun render(posX: Int, posY: Int) { + scroll.update( + isHovered(posX, posY) + ) + + var renderY = 0 + if (hasHeader) { + content[0].forEachIndexed { index, renderable -> + GlStateManager.translate(xOffsets[index].toFloat(), 0f, 0f) + renderable?.renderXYAligned( + posX + xOffsets[index], + posY + renderY, + xOffsets[index + 1] - xOffsets[index], + yOffsets[1] + ) + GlStateManager.translate(-xOffsets[index].toFloat(), 0f, 0f) + } + val yShift = yOffsets[1] - yOffsets[0] + GlStateManager.translate(0f, yShift.toFloat(), 0f) + renderY += yShift + } + val range = + yOffsets.indexOfFirst { it >= scroll.asInt() }..<(yOffsets.indexOfFirst { it >= end } + .takeIf { it > 0 } + ?: yOffsets.size) - 1 + for (rowIndex in range) { + content[rowIndex].forEachIndexed { index, renderable -> + GlStateManager.translate(xOffsets[index].toFloat(), 0f, 0f) + renderable?.renderXYAligned( + posX + xOffsets[index], + posY + renderY, + xOffsets[index + 1] - xOffsets[index], + yOffsets[rowIndex + 1] - yOffsets[rowIndex] + ) + GlStateManager.translate(-xOffsets[index].toFloat(), 0f, 0f) + } + val yShift = yOffsets[rowIndex + 1] - yOffsets[rowIndex] + GlStateManager.translate(0f, yShift.toFloat(), 0f) + renderY += yShift + } + GlStateManager.translate(0f, -renderY.toFloat(), 0f) + } + } + fun drawInsideRoundedRect( input: Renderable, color: Color, diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/ScrollInput.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/ScrollInput.kt new file mode 100644 index 000000000..e47cca31c --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/ScrollInput.kt @@ -0,0 +1,85 @@ +package at.hannibal2.skyhanni.utils.renderables + +import org.lwjgl.input.Mouse + +abstract class ScrollInput( + private val scrollValue: ScrollValue, + protected val minValue: Int, + protected val maxValue: Int, + protected val velocity: Double, + protected val dragScrollMouseButton: Int?, + startValue: Double? +) { + + init { + scrollValue.init(startValue ?: minValue.toDouble()) + coerceInLimit() + } + + protected var scroll + set(value) { + scrollValue.setValue(value) + } + get() = scrollValue.getValue() + + private var mouseEventTime = 0L + + fun asInt() = scroll.toInt() + fun asDouble() = scroll + + protected fun coerceInLimit() = + if (maxValue < minValue) { + scroll = minValue.toDouble() + } else { + scroll = scroll.coerceIn(minValue.toDouble(), maxValue.toDouble()) + } + + protected fun isMouseEventValid(): Boolean { + val mouseEvent = Mouse.getEventNanoseconds() + val mouseEventsValid = mouseEvent - mouseEventTime > 20L + mouseEventTime = mouseEvent + return mouseEventsValid + } + + abstract fun update(isValid: Boolean) + + companion object { + + class Vertical( + scrollValue: ScrollValue, + minHeight: Int, + maxHeight: Int, + velocity: Double, + dragScrollMouseButton: Int?, + startValue: Double? = null + ) : ScrollInput(scrollValue, minHeight, maxHeight, velocity, dragScrollMouseButton, startValue) { + override fun update(isValid: Boolean) { + if (maxValue < minValue) return + if (!isValid || !isMouseEventValid()) return + if (dragScrollMouseButton != null && Mouse.isButtonDown(dragScrollMouseButton)) { + scroll += Mouse.getEventDY() * velocity + } + val deltaWheel = Mouse.getEventDWheel() + scroll += -deltaWheel.coerceIn(-1, 1) * 2.5 * velocity + coerceInLimit() + } + + } + } +} + +class ScrollValue { + var field: Double? = null + fun getValue(): Double = + field ?: throw IllegalStateException("ScrollValue should be initialized before get.") + + fun setValue(value: Double) { + field = value + } + + fun init(value: Double) { + if (field != null) return + field = value + } + +} |