Use CopyOnWrite for panel lists, update panel isHovered to be more reliable, fix FoldableSlotPanel

This commit is contained in:
DBotThePony 2024-09-02 19:31:56 +07:00
parent d7b7ab9a67
commit aa461c322f
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 112 additions and 51 deletions

View File

@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.client.screen
import com.mojang.blaze3d.systems.RenderSystem
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.Font
import net.minecraft.client.gui.GuiGraphics
@ -54,8 +53,7 @@ import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import java.util.*
import kotlin.collections.ArrayDeque
import kotlin.collections.ArrayList
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.collections.List
import kotlin.collections.MutableSet
import kotlin.collections.isNotEmpty
@ -73,7 +71,8 @@ import kotlin.collections.withIndex
abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, title: Component) : AbstractContainerScreen<T>(menu, inventory, title) {
constructor(menu: T, title: Component) : this(menu, menu.inventory, title)
protected val panels = ArrayDeque<EditablePanel<*>>()
protected val panels = CopyOnWriteArrayList<EditablePanel<*>>()
protected val panelsReversed = panels.asReversed()
val panelsView: List<EditablePanel<*>> = Collections.unmodifiableList(panels)
var inventoryFrame: FramePanel<MatteryScreen<*>>? = null
@ -473,7 +472,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
override fun onClose() {
super.onClose()
ObjectArrayList(panels).forEach { it.markRemoved() }
panels.forEach { it.markRemoved() }
}
public override fun recalculateQuickCraftRemaining() {
@ -508,7 +507,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
var click = false
var focusKilled = false
for (panel in ObjectArrayList(panels)) {
for (panel in panels) {
if (click || !panel.mouseClickedChecked(x, y, button)) {
focusKilled = panel.killFocus() || focusKilled
} else {
@ -527,7 +526,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
private var lastDragSlot: Slot? = null
override fun mouseDragged(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean {
for (panel in ObjectArrayList(panels)) {
for (panel in panels) {
if (panel.mouseDraggedChecked(x, y, button, xDelta, yDelta)) {
if (returnSlot != null) {
super.mouseDragged(x, y, button, xDelta, yDelta)
@ -555,7 +554,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
}
for (panel in ObjectArrayList(panels)) {
for (panel in panels) {
if (panel.mouseReleasedChecked(p_97812_, p_97813_, p_97814_)) {
if (returnSlot != null) {
super.mouseReleased(p_97812_, p_97813_, p_97814_)
@ -571,7 +570,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, scrollX: Double, scrollY: Double): Boolean {
for (panel in ObjectArrayList(panels)) {
for (panel in panels) {
if (panel.mouseScrolledChecked(mouseX, mouseY, scrollY)) {
return true
}
@ -581,7 +580,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
override fun keyReleased(p_94715_: Int, p_94716_: Int, p_94717_: Int): Boolean {
for (panel in ObjectArrayList(panels)) {
for (panel in panels) {
if (panel.keyReleased(p_94715_, p_94716_, p_94717_)) {
return true
}
@ -591,7 +590,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
override fun charTyped(p_94683_: Char, p_94684_: Int): Boolean {
for (panel in ObjectArrayList(panels)) {
for (panel in panels) {
if (panel.charTyped(p_94683_, p_94684_)) {
return true
}
@ -601,7 +600,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
override fun keyPressed(key: Int, scancode: Int, mods: Int): Boolean {
for (panel in ObjectArrayList(panels)) {
for (panel in panels) {
if (panel.keyPressed(key, scancode, mods)) {
if (returnSlot != null) {
super.keyPressed(key, scancode, mods)
@ -662,23 +661,26 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
for (panel in panels) {
if (hovered) {
panel.unsetHovered()
} else if (panel.tickHover(mouseXf, mouseYf)) {
} else if (panel.tickHovered0(mouseXf, mouseYf)) {
hovered = true
}
}
panels.forEach { it.tickHovered1() }
panels.forEach { it.tickHovered2() }
RenderSystem.defaultBlendFunc()
RenderSystem.enableBlend()
RenderSystem.enableDepthTest()
for (panel in panels.asReversed()) {
for (panel in panelsReversed) {
RenderSystem.depthFunc(GL11.GL_ALWAYS)
RenderSystem.setShaderColor(1f, 1f, 1f, 1f)
panel.render(wrap, mouseXf, mouseYf, partialTick)
}
if (!panels.asReversed().any { it.updateCursor0() })
panels.asReversed().any { it.updateCursor1() }
if (!panelsReversed.any { it.updateCursor0() })
panelsReversed.any { it.updateCursor1() }
RenderSystem.depthFunc(GL11.GL_LESS)
NeoForge.EVENT_BUS.post(ContainerScreenEvent.Render.Background(this, graphics, mouseX, mouseY))

View File

@ -13,7 +13,6 @@ import net.minecraft.client.gui.navigation.ScreenRectangle
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.renderer.Rect2i
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.Slot
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.SystemTime
import ru.dbotthepony.mc.otm.client.CursorType
@ -28,6 +27,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel
import ru.dbotthepony.mc.otm.core.collect.concatIterators
import ru.dbotthepony.mc.otm.core.collect.flatMap
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import java.util.function.Predicate
import kotlin.collections.ArrayList
import kotlin.math.roundToInt
@ -259,8 +259,8 @@ open class EditablePanel<out S : Screen>(
var dockedHeight: Float = 0f
private set
private val childrenInternal = ArrayList<EditablePanel<*>>()
private val visibleChildrenInternal = ArrayList<EditablePanel<*>>()
private val childrenInternal = CopyOnWriteArrayList<EditablePanel<*>>()
private val visibleChildrenInternal = CopyOnWriteArrayList<EditablePanel<*>>()
val children: List<EditablePanel<*>> = Collections.unmodifiableList(childrenInternal)
val visibleChildren: List<EditablePanel<*>> = Collections.unmodifiableList(visibleChildrenInternal)
@ -659,47 +659,54 @@ open class EditablePanel<out S : Screen>(
var absoluteY = 0f
private set
private var pendingIsHovered = false
private set(value) {
if (field != value) {
field = value
pendingIsEffectivelyHovered = value || pendingIsEffectivelyHovered
parent?.updateIsEffectivelyHovered(value || pendingIsEffectivelyHovered)
}
}
private var pendingIsEffectivelyHovered = false
private set(value) {
if (field != value) {
field = value
parent?.updateIsEffectivelyHovered(value || pendingIsHovered)
}
}
/**
* If this exact panel is hovered. Panel is not considered hovered if any of its children are hovered
*/
var isHovered = false
private set(value) {
if (value == field) {
return
}
val old = field
field = value
onHoverUpdate(old, value)
}
private set
/**
* If this panel or any of its children are hovered
*/
val isEverHovered: Boolean get() {
return isHovered || visibleChildrenInternal.any { it.isEverHovered }
var isEffectivelyHovered: Boolean = false
private set
private fun updateIsEffectivelyHovered(childrenState: Boolean) {
pendingIsEffectivelyHovered = pendingIsHovered || childrenState || visibleChildrenInternal.any { it.pendingIsEffectivelyHovered }
}
fun unsetHovered() {
isHovered = false
pendingIsHovered = false
pendingIsEffectivelyHovered = false
for (child in childrenInternal) {
child.unsetHovered()
}
}
protected open fun onHoverUpdate(oldHover: Boolean, newHover: Boolean) {
if (newHover) {
onHovered()
} else {
onUnHovered()
}
}
protected open fun onHovered() {}
protected open fun onUnHovered() {}
protected open fun onEffectivelyHovered() {}
protected open fun onEffectivelyUnHovered() {}
var hasFocusedChildren = false
private set(value) {
if (field != value) {
@ -746,6 +753,7 @@ open class EditablePanel<out S : Screen>(
if (child.visible) {
visibleChildrenInternal.add(child)
updateIsEffectivelyHovered(false)
layoutInvalidated = true
}
@ -793,7 +801,7 @@ open class EditablePanel<out S : Screen>(
}
protected open fun shouldRenderTooltips(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
return isEverHovered || isGrabbingMouseInput()
return isEffectivelyHovered || isGrabbingMouseInput()
}
fun isVisible(): Boolean {
@ -934,7 +942,10 @@ open class EditablePanel<out S : Screen>(
}
}
fun tickHover(mouseX: Float, mouseY: Float): Boolean {
/**
* Determining whenever panel is hovered or not
*/
fun tickHovered0(mouseX: Float, mouseY: Float): Boolean {
if (isRemoved)
return false
@ -957,23 +968,69 @@ open class EditablePanel<out S : Screen>(
for (child in visibleChildrenInternal) {
if (hit) {
child.unsetHovered()
} else if (child.tickHover(mouseX, mouseY)) {
} else if (child.tickHovered0(mouseX, mouseY)) {
hit = true
}
}
isHovered =
pendingIsHovered =
!hit &&
mouseX in absoluteX ..< absoluteX + width &&
mouseY in absoluteY ..< absoluteY + height
return hit || isHovered
return hit || pendingIsHovered
} else {
unsetHovered()
return false
}
}
private var needToCallIsHoveredUpdate = false
private var needToCallIsEffectivelyHoveredUpdate = false
/**
* Updating publicly-visible flags regarding hovered
*/
fun tickHovered1() {
if (isRemoved)
return
needToCallIsHoveredUpdate = isHovered != pendingIsHovered
needToCallIsEffectivelyHoveredUpdate = isEffectivelyHovered != pendingIsEffectivelyHovered
isHovered = pendingIsHovered
isEffectivelyHovered = pendingIsEffectivelyHovered
visibleChildrenInternal.forEach { it.tickHovered1() }
}
/**
* Dispatching "is hovered" update callbacks
*/
fun tickHovered2() {
if (needToCallIsHoveredUpdate) {
if (isHovered) {
onHovered()
} else {
onUnHovered()
}
needToCallIsHoveredUpdate = false
}
if (needToCallIsEffectivelyHoveredUpdate) {
if (isEffectivelyHovered) {
onEffectivelyHovered()
} else {
onEffectivelyUnHovered()
}
needToCallIsEffectivelyHoveredUpdate = false
}
visibleChildrenInternal.forEach { it.tickHovered2() }
}
data class PanelOfTypeResult<T>(val panel: T?, val interrupt: Boolean) {
constructor(panel: T) : this(panel, true)
@ -1765,7 +1822,7 @@ open class EditablePanel<out S : Screen>(
tickInner()
for (child in Array(visibleChildrenInternal.size) { visibleChildrenInternal[it] }) {
for (child in visibleChildrenInternal) {
child.tick()
}
}
@ -1796,7 +1853,7 @@ open class EditablePanel<out S : Screen>(
screen.removePanel(this as EditablePanel<MatteryScreen<*>>)
}
for (child in Array(childrenInternal.size) { childrenInternal[it] }) {
for (child in childrenInternal) {
child.remove()
}

View File

@ -22,7 +22,9 @@ class Panel2Widget<out S: Screen, out P : EditablePanel<S>>(
val wrap = MGUIGraphics(graphics)
panel.tickHover(xFloat, yFloat)
panel.tickHovered0(xFloat, yFloat)
panel.tickHovered1()
panel.tickHovered2()
panel.render(wrap, xFloat, yFloat, partialTick)
panel.renderTooltips(wrap, xFloat, yFloat, partialTick)
}

View File

@ -59,7 +59,7 @@ open class FoldableSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
this@FoldableSlotPanel.hoverPanel = null
}
override fun onUnHovered() {
override fun onEffectivelyUnHovered() {
remove()
}
@ -76,11 +76,11 @@ open class FoldableSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
var hoverPanel: FoldableSlotPanel<*, *>.HoverPanel? = null
protected set
override fun onHovered() {
override fun onEffectivelyHovered() {
hoveringSince = SystemTime()
}
override fun onUnHovered() {
override fun onEffectivelyUnHovered() {
hoveringSince = null
}