diff --git a/common/src/main/kotlin/fr/username404/snowygui/ClickGui.kt b/common/src/main/kotlin/fr/username404/snowygui/ClickGui.kt index e4e3f55..75b2a3b 100644 --- a/common/src/main/kotlin/fr/username404/snowygui/ClickGui.kt +++ b/common/src/main/kotlin/fr/username404/snowygui/ClickGui.kt @@ -6,6 +6,7 @@ import fr.username404.snowygui.gui.SnowyScreen import fr.username404.snowygui.gui.elements.ClickBox import net.minecraft.client.input.MouseButtonEvent import org.lwjgl.glfw.GLFW +import kotlin.math.roundToInt object ClickGui: SnowyScreen() { override val components = mutableSetOf() @@ -18,8 +19,8 @@ object ClickGui: SnowyScreen() { private var draggingBox: String? = null private inline fun currentBoxContext(args: ClickBox.() -> Unit): Unit? = draggingBox?.run { boxContext { if (name.string == draggingBox) args() } } - private var offsetX: Double = 0.0; - private var offsetY: Double = 0.0; + private var offsetX: Double = 0.0 + private var offsetY: Double = 0.0 override fun mouseClicked(event: MouseButtonEvent, isDoubleClick: Boolean): Boolean { if (event.input() == GLFW.GLFW_MOUSE_BUTTON_LEFT) { clickBoxes.find { it.isWithinBounds(event.x, event.y) }?.let { draggingBox = it.name.string } @@ -36,8 +37,12 @@ object ClickGui: SnowyScreen() { override fun mouseDragged(event: MouseButtonEvent, mouseX: Double, mouseY: Double): Boolean { if (event.input() == GLFW.GLFW_MOUSE_BUTTON_LEFT) { currentBoxContext { - x = (event.x + offsetX).coerceIn(0.0..this@ClickGui.width - width.toDouble()) - y = (event.y + offsetY).coerceIn(0.0..this@ClickGui.height - height.toDouble()) + val targetX = (event.x + offsetX).coerceIn(0.0..this@ClickGui.width - width.toDouble()) + val targetY = (event.y + offsetY).coerceIn(0.0..this@ClickGui.height - height.toDouble()) + + // Snap to integer pixels => stable spacing + x = targetX.roundToInt().toDouble() + y = targetY.roundToInt().toDouble() } } return super.mouseDragged(event, mouseX, mouseY) diff --git a/common/src/main/kotlin/fr/username404/snowygui/gui/elements/ClickBox.kt b/common/src/main/kotlin/fr/username404/snowygui/gui/elements/ClickBox.kt index 5dfa379..7037c1e 100644 --- a/common/src/main/kotlin/fr/username404/snowygui/gui/elements/ClickBox.kt +++ b/common/src/main/kotlin/fr/username404/snowygui/gui/elements/ClickBox.kt @@ -12,10 +12,11 @@ import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiGraphics import net.minecraft.network.chat.Component import net.minecraft.util.ARGB +import kotlin.math.floor +import kotlin.math.roundToInt import org.jetbrains.annotations.ApiStatus import java.util.TreeSet import kotlin.collections.LinkedHashSet -import kotlin.math.roundToInt @ApiStatus.Internal class ClickBox( @@ -74,13 +75,17 @@ class ClickBox( } override fun render(guiGraphics: GuiGraphics?) { + val xi = floor(x).toInt() + val yi = floor(y).toInt() + // Header - RenderingUtil.drawRectangle( + RenderingUtil.drawSlantedHeader( guiGraphics = guiGraphics, - x = x, - y = y, - height = height, + x = xi, + y = yi, width = width, + height = height, + slantTop = inclination, color = color, opacity = opacity ) @@ -88,8 +93,8 @@ class ClickBox( // Body RenderingUtil.drawRectangle( guiGraphics = guiGraphics, - x = x, - y = (y + height), + x = xi.toDouble(), + y = (yi + height).toDouble(), height = clickboxHeightOffset, width = width + inclination.roundToInt(), color = color, @@ -99,24 +104,36 @@ class ClickBox( // Separator line RenderingUtil.hLine( guiGraphics = guiGraphics, - x1 = (x + inclination).toInt(), - x2 = (x + width).toInt(), - y = (y + height).toInt(), + x1 = (xi + inclination).toInt(), + x2 = (xi + width), + y = (yi + height), rgb = Colors.WHITE_LINES.hexValue, opacity = 1f ) if (buttons.isNotEmpty()) { + val barWidth = 3 + val leftPadding = 3 + val gapToBar = 1 buttonsProgressBar.apply { - this.x = this@ClickBox.x + this@ClickBox.width - 3 + this.width = barWidth + this.x = this@ClickBox.x + this@ClickBox.width - barWidth this.y = this@ClickBox.y + this@ClickBox.height + 3 }.display(guiGraphics) + + val buttonWidth = (this@ClickBox.width - leftPadding - barWidth - gapToBar).coerceAtLeast(10) + buttons.forEachIndexed { num, button -> val fullHeight = (y + height.toDouble())..(this.y + height + clickboxHeightOffset) button.also { - it.x = this.x + 3 + it.x = this.x + leftPadding it.y = this.y + 3 + height + (((num + 1) - barStage) * 9) - it.hidden = if ((num + 1) <= 8) ((it.y) !in fullHeight) else ((it.y + it.height) !in fullHeight) + + it.width = buttonWidth + + it.hidden = + if ((num + 1) <= 8) ((it.y) !in fullHeight) + else ((it.y + it.height) !in fullHeight) }.display(guiGraphics) } } @@ -125,8 +142,9 @@ class ClickBox( guiGraphics?.drawString( this, Component.nullToEmpty(name.string), - (x + 5).toInt(), (y + 2).toInt(), - ARGB.opaque(Colors.TRANSPARENT.hexValue), + (xi + 5 + (inclination / 2f)).toInt(), + (yi + 2), + ARGB.opaque(Colors.WHITE.hexValue), false ) } diff --git a/common/src/main/kotlin/fr/username404/snowygui/utils/RenderingUtil.kt b/common/src/main/kotlin/fr/username404/snowygui/utils/RenderingUtil.kt index 642694e..2197a6c 100644 --- a/common/src/main/kotlin/fr/username404/snowygui/utils/RenderingUtil.kt +++ b/common/src/main/kotlin/fr/username404/snowygui/utils/RenderingUtil.kt @@ -4,6 +4,10 @@ import com.mojang.blaze3d.opengl.GlStateManager import fr.username404.snowygui.gui.feature.Colors import net.minecraft.client.gui.GuiGraphics import net.minecraft.util.ARGB +import kotlin.math.ceil +import kotlin.math.floor +import kotlin.math.max +import kotlin.math.min object RenderingUtil { fun prepareDraw() = GlStateManager._enableBlend() @@ -27,18 +31,74 @@ object RenderingUtil { color: Int = Colors.TRANSPARENT(), opacity: Float = 1F ) { + if (guiGraphics == null) return + + val x1 = floor(x).toInt() + val y1 = floor(y).toInt() + val x2 = ceil(x + width).toInt() + val y2 = ceil(y + height).toInt() + prepareDraw() - guiGraphics?.fill( - x.toInt(), y.toInt(), - (x + width).toInt(), (y + height).toInt(), - argbFromRgb(color, opacity) - ) + guiGraphics.fill(x1, y1, x2, y2, argbFromRgb(color, opacity)) + endDraw() + } + + fun drawSlantedHeader( + guiGraphics: GuiGraphics?, + x: Int, + y: Int, + width: Int, + height: Int, + slantTop: Float, + color: Int, + opacity: Float + ) { + if (guiGraphics == null) return + if (width <= 0 || height <= 0) return + + val slant = slantTop.toDouble() + val baseArgb = argbFromRgb(color, opacity) + + prepareDraw() + for (dy in 0 until height) { + val t = if (height == 1) 0.0 else dy.toDouble() / (height - 1).toDouble() + + val leftF = x + slant * (1.0 - t) + val rightF = x + width + slant * t + + val left = min(leftF, rightF) + val right = max(leftF, rightF) + + val lx = floor(left).toInt() + val rx = floor(right).toInt() + val yy = y + dy + + val midStart = lx + 1 + val midEnd = rx + if (midEnd > midStart) { + guiGraphics.fill(midStart, yy, midEnd, yy + 1, baseArgb) + } + + val leftFrac = (left - floor(left)).toFloat().coerceIn(0f, 1f) + val rightFrac = (right - floor(right)).toFloat().coerceIn(0f, 1f) + + val leftAlpha = (1f - leftFrac) * opacity + val rightAlpha = rightFrac * opacity + + if (leftAlpha > 0f) guiGraphics.fill(lx, yy, lx + 1, yy + 1, argbFromRgb(color, leftAlpha)) + if (rightAlpha > 0f) guiGraphics.fill(rx, yy, rx + 1, yy + 1, argbFromRgb(color, rightAlpha)) + + if (rx <= lx) { + guiGraphics.fill(lx, yy, lx + 1, yy + 1, baseArgb) + } + } endDraw() } fun hLine(guiGraphics: GuiGraphics?, x1: Int, x2: Int, y: Int, rgb: Int, opacity: Float = 1f) { + if (guiGraphics == null) return prepareDraw() - guiGraphics?.hLine(x1, x2, y, argbFromRgb(rgb, opacity)) + guiGraphics.hLine(x1, x2, y, argbFromRgb(rgb, opacity)) endDraw() } }