nageyqma·git
HTTPS https://git.nageyqma.icu/nageyqma/odin_next.git SSH git@git.nageyqma.icu:nageyqma/odin_next.git

HTTPS 链接可直接 git clone;推送代码请使用 SSH。

5.3 KB · 152 行raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
package com.odtheking.odin.clickgui.widget

import com.odtheking.odin.OdinMod.mc
import com.odtheking.odin.clickgui.ClickGUI
import com.odtheking.odin.clickgui.GuiTheme
import com.odtheking.odin.features.Category
import com.odtheking.odin.features.ModuleManager
import com.odtheking.odin.features.impl.render.ClickGUIModule
import com.odtheking.odin.utils.Colors
import com.odtheking.odin.utils.render.Corners
import com.odtheking.odin.utils.render.roundedRect
import com.odtheking.odin.utils.render.roundedShadow
import com.odtheking.odin.utils.render.scissored
import net.minecraft.client.gui.GuiGraphicsExtractor
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.input.MouseButtonEvent
import net.minecraft.network.chat.Component
import kotlin.math.sign

class PanelWidget(category: Category) : OdinContainerWidget(
    0, 0, GuiTheme.ROW_WIDTH, GuiTheme.ROW_HEIGHT + GuiTheme.CAP, Component.literal(category.name)
) {
    private val panelData = ClickGUIModule.panelSetting.getOrPut(category.name) { ClickGUIModule.PanelData(0, 0) }
    private val boldTitle = "§l${category.name}"

    private val modules = ModuleManager.modulesByCategory[category]
        ?.sortedByDescending { mc.font.width(it.name) }
        ?.map { ModuleWidget(it) }.orEmpty()

    override fun children(): List<GuiEventListener> = modules

    private var contentHeight = 0
    private var scroll = 0

    private var headerGrabbed = false
    private var grabX = 0
    private var grabY = 0

    var extended: Boolean
        get() = panelData.extended
        set(value) {
            panelData.extended = value
            if (!value) {
                scroll = 0
                modules.forEach { it.visible = false }
            }
        }

    fun moveTo(newX: Int, newY: Int) {
        panelData.x = newX.coerceIn(0, maxX)
        panelData.y = newY.coerceIn(0, maxY)
    }

    private val maxX get() = (ClickGUI.virtualWidth - GuiTheme.ROW_WIDTH).coerceAtLeast(0)
    private val maxY get() = (ClickGUI.virtualHeight - GuiTheme.ROW_HEIGHT).coerceAtLeast(0)

    fun release() = modules.forEach { it.release() }

    fun resetScroll() {
        scroll = 0
    }

    override fun render(graphics: GuiGraphicsExtractor, mouseX: Int, mouseY: Int) {
        layout()
        graphics.roundedShadow(x, y, x + width, y + height, GuiTheme.shadow.rgba, GuiTheme.PANEL_BLUR, CORNERS)

        graphics.roundedRect(x, y, x + width, y + GuiTheme.ROW_HEIGHT, GuiTheme.background.rgba, CORNERS_TOP)
        graphics.centeredText(mc.font, boldTitle, x + width / 2, GuiTheme.textY(y, GuiTheme.ROW_HEIGHT), Colors.WHITE.rgba)

        val capY = y + height - GuiTheme.CAP
        if (extended) {
            val top = y + GuiTheme.ROW_HEIGHT
            graphics.scissored(x, top, x + width, capY) {
                for (module in modules) {
                    if (!module.visible || module.bottom <= top || module.y >= capY) continue
                    module.extractRenderState(graphics, mouseX, mouseY, 0f)
                }
            }
        }

        graphics.roundedRect(
            x, capY, x + width, capY + GuiTheme.CAP,
            (if (modules.lastOrNull()?.module?.enabled == true && extended) GuiTheme.accent else GuiTheme.background).rgba,
            CORNERS_BOTTOM
        )
    }

    override fun onClick(event: MouseButtonEvent, doubleClick: Boolean) {
        headerGrabbed = event.y().toInt() < y + GuiTheme.ROW_HEIGHT
        if (!headerGrabbed) return

        if (event.button() == RIGHT) {
            extended = !extended
            return
        }
        grabX = event.x().toInt() - x
        grabY = event.y().toInt() - y
    }

    override fun onDrag(event: MouseButtonEvent, dragX: Double, dragY: Double) {
        if (headerGrabbed) moveTo(event.x().toInt() - grabX, event.y().toInt() - grabY)
    }

    override fun mouseScrolled(mouseX: Double, mouseY: Double, scrollX: Double, scrollY: Double): Boolean {
        if (!extended || !isMouseOver(mouseX, mouseY)) return false
        val limit = (contentHeight - MIN_VISIBLE).coerceAtLeast(0)
        scroll = (scroll + (scrollY.sign * SCROLL_STEP).toInt()).coerceIn(-limit, 0)
        return true
    }

    private fun layout() {
        x = panelData.x.coerceIn(0, maxX)
        y = panelData.y.coerceIn(0, maxY)

        if (!extended) {
            height = GuiTheme.ROW_HEIGHT + GuiTheme.CAP
            return
        }

        contentHeight = 0
        var filtering = false
        for (module in modules) {
            module.visible = module.matches(ClickGUI.searchString)
            filtering = filtering || module.isFiltering
            if (!module.visible) continue
            module.measure()
            contentHeight += module.height
        }

        if (!filtering) scroll = scroll.coerceIn(-(contentHeight - MIN_VISIBLE).coerceAtLeast(0), 0)

        var offset = GuiTheme.ROW_HEIGHT + scroll
        for (module in modules) {
            if (!module.visible) continue
            module.x = x
            module.y = y + offset
            module.place()
            offset += module.height
        }

        height = offset + GuiTheme.CAP
    }

    private companion object {
        const val MIN_VISIBLE = 72
        const val SCROLL_STEP = 16

        val CORNERS = Corners(GuiTheme.RADIUS)
        val CORNERS_TOP = Corners.top(GuiTheme.RADIUS)
        val CORNERS_BOTTOM = Corners.bottom(GuiTheme.RADIUS)
    }
}