Text input panel test

This commit is contained in:
DBotThePony 2023-01-25 14:21:44 +07:00
parent 7dffa61bae
commit 472cb057a5
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 844 additions and 9 deletions

View File

@ -15,7 +15,7 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MBlocks
class HoloSignBlockEntity(blockPos: BlockPos, blockState: BlockState) : SynchronizedBlockEntity(MBlockEntities.HOLO_SIGN, blockPos, blockState), MenuProvider {
var text by synchronizer.string("АМОГУС", name = "text")
var text by synchronizer.string("АМОГУС\nБОНУС\nИИСУС\nПЛЮС", name = "text")
override fun createMenu(p_39954_: Int, p_39955_: Inventory, p_39956_: Player): AbstractContainerMenu {
return HoloSignMenu(p_39954_, p_39955_, this)

View File

@ -1,5 +1,7 @@
package ru.dbotthepony.mc.otm.client
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.platform.Window
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Font
import net.minecraft.client.resources.sounds.SimpleSoundInstance
@ -13,6 +15,12 @@ import java.nio.DoubleBuffer
inline val minecraft: Minecraft get() = Minecraft.getInstance()
inline val font: Font get() = minecraft.font
fun Window.isKeyDown(key: Int) = InputConstants.isKeyDown(window, key)
val Window.isShiftDown get() = isKeyDown(InputConstants.KEY_LSHIFT) || isKeyDown(InputConstants.KEY_RSHIFT)
val Window.isCtrlDown get() = isKeyDown(InputConstants.KEY_RCONTROL) || isKeyDown(InputConstants.KEY_LCONTROL)
fun playGuiClickSound() {
minecraft.soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0f))
}

View File

@ -25,13 +25,21 @@ class HoloSignRenderer(private val context: BlockEntityRendererProvider.Context)
p_112312_: Int
) {
poseStack.pushPose()
poseStack.rotateWithBlockFacing(tile.blockState[RotatableMatteryBlock.FACING_FULL], clarifyingAxis = Direction.SOUTH)
poseStack.translate(0.0f, 0.0f, -0.1f)
poseStack.rotateWithBlockFacing(tile.blockState[RotatableMatteryBlock.FACING_FULL])
poseStack.translate(0.5f, 0.5f, -0.05f)
poseStack.scale(0.01f, 0.01f, 0.01f)
val sorse = DynamicBufferSource.WORLD
font.drawAligned(poseStack = poseStack, buffer = sorse, text = tile.text, align = TextAlign.CENTER_CENTER, x = 0f, y = 0f, color = RGBAColor.BLUE.toInt())
val lines = tile.text.split('\n')
val totalHeight = lines.size * font.lineHeight + (lines.size - 1) * 2f
var y = -totalHeight / 2f
for (line in lines) {
font.drawAligned(poseStack = poseStack, buffer = sorse, text = line, align = TextAlign.TOP_CENTER, x = 0f, y = y, color = RGBAColor.BLUE.toInt())
y += font.lineHeight + 2f
}
poseStack.popPose()
}
}

View File

@ -0,0 +1,21 @@
package ru.dbotthepony.mc.otm.client.screen
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.TextInputPanel
import ru.dbotthepony.mc.otm.menu.HoloSignMenu
class HoloSignScreen(menu: HoloSignMenu, inventory: Inventory, title: Component) : MatteryScreen<HoloSignMenu>(menu, title) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = FramePanel(this, null, 0f, 0f, 200f, 200f, getTitle())
val textbox = TextInputPanel(this, frame)
textbox.dock = Dock.FILL
textbox.text = "МОГУС\nБОНУС\n\nСУС"
textbox.multiLine = true
return frame
}
}

View File

@ -1251,7 +1251,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return list
}
operator fun get(index: Int): EditablePanel<*>? {
fun getChildren(index: Int): EditablePanel<*>? {
if (index < 0 || index >= childrenInternal.size) return null
return childrenInternal[index]
}

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
open class GridPanel<out S : Screen> @JvmOverloads constructor(
screen: S,
@ -23,7 +22,7 @@ open class GridPanel<out S : Screen> @JvmOverloads constructor(
var column = 0
while (column < columns) {
val child = get(index) ?: break
val child = getChildren(index) ?: break
if (child.visible && child.dock === Dock.NONE) {
lineY = lineY.coerceAtLeast(child.height + child.dockMargin.top + child.dockMargin.bottom)
@ -41,7 +40,7 @@ open class GridPanel<out S : Screen> @JvmOverloads constructor(
currentX = 0f
lineY = 0f
get(index) ?: break
getChildren(index) ?: break
}
super.performLayout()

View File

@ -0,0 +1,787 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.vertex.PoseStack
import it.unimi.dsi.fastutil.chars.CharOpenHashSet
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.isCtrlDown
import ru.dbotthepony.mc.otm.client.isKeyDown
import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.DynamicBufferSource
import ru.dbotthepony.mc.otm.client.render.TextAlign
import ru.dbotthepony.mc.otm.client.render.drawAligned
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.milliTime
open class TextInputPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = 10f,
height: Float = 10f,
) : EditablePanel<S>(screen, parent, x, y, width, height) {
data class TextSelection(val start: Int, val end: Int) {
init {
require(start <= end) { "$start <= $end" }
require(start >= 0) { "$start >= 0" }
}
}
private inner class Snapshot {
private val lines = ArrayList(this@TextInputPanel.lines) // ultra fast copy
private val cursorLine = this@TextInputPanel.cursorLine
private val cursorCharacter = this@TextInputPanel.cursorCharacter
fun apply() {
this@TextInputPanel.lines.clear()
this@TextInputPanel.lines.addAll(lines)
this@TextInputPanel.cursorCharacter = cursorCharacter
this@TextInputPanel.cursorLine = cursorLine
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TextInputPanel<*>.Snapshot
if (lines != other.lines) return false
if (cursorLine != other.cursorLine) return false
if (cursorCharacter != other.cursorCharacter) return false
return true
}
override fun hashCode(): Int {
var result = lines.hashCode()
result = 31 * result + cursorLine
result = 31 * result + cursorCharacter
return result
}
}
protected var ignoreUpdates = false
var debug = true
var multiLine = false
var cursorLine = 0
var cursorCharacter = 0
var textColor = RGBAColor.WHITE
var cursorColor = RGBAColor.BLACK
private var textCache: String? = null
private val lines = ArrayList<String>()
private val selections = Int2ObjectOpenHashMap<TextSelection>()
private val undo = ArrayDeque<Snapshot>()
private val redo = ArrayDeque<Snapshot>()
private var snapshotTimer: Long? = null
override fun tick() {
super.tick()
if (snapshotTimer != null && snapshotTimer!! <= milliTime) {
pushbackSnapshot()
snapshotTimer = null
redo.clear()
}
}
private fun pushbackSnapshot() {
val snapshot = Snapshot()
if (undo.isEmpty() || undo.last() != snapshot)
undo.addLast(snapshot)
}
private fun recordHistory(hard: Boolean = false) {
if (hard) {
pushbackSnapshot()
snapshotTimer = null
} else if (snapshotTimer == null) {
snapshotTimer = milliTime + 800L
} else if (snapshotTimer!! <= milliTime) {
pushbackSnapshot()
snapshotTimer = null
}
redo.clear()
}
// it gets really dirty
// idk man :(
private fun undo() {
if (undo.isEmpty())
return
if (snapshotTimer != null || redo.isEmpty()) {
pushbackSnapshot()
snapshotTimer = null
}
val current = Snapshot()
while (undo.isNotEmpty()) {
val removed = undo.removeLast()
redo.addFirst(removed)
if (removed != current) {
removed.apply()
break
}
}
}
// it gets really dirty
// idk man :(
private fun redo() {
if (redo.isEmpty())
return
val current = Snapshot()
snapshotTimer = null
while (redo.isNotEmpty()) {
val removed = redo.removeFirst()
undo.addLast(removed)
if (current != removed) {
removed.apply()
break
}
}
}
operator fun get(index: Int): String? {
return lines.getOrNull(index)
}
operator fun set(index: Int, value: String) {
if (index < 0) {
throw IndexOutOfBoundsException("negative index $index")
}
lines.ensureCapacity(index)
while (lines.size <= index) {
lines.add("")
}
lines[index] = value
}
fun insertLine(index: Int, value: String = "") {
lines.ensureCapacity(index)
while (lines.size < index) {
lines.add("")
}
val upperLines = selections.int2ObjectEntrySet().filter { it.intKey >= index }
for (entry in upperLines) {
selections.remove(entry.intKey)
}
for (entry in upperLines) {
selections.put(entry.intKey + 1, entry.value)
}
lines.add(index, value)
}
fun removeLine(index: Int): Boolean {
if (index < 0 || index >= lines.size) {
return false
}
if (cursorLine == index) {
if (index != 0)
cursorLine--
cursorCharacter = lines.getOrNull(cursorLine)?.length ?: 0
}
lines.removeAt(index)
selections.remove(index)
val upperLines = selections.int2ObjectEntrySet().filter { it.intKey > index }
for (entry in upperLines) {
selections.remove(entry.intKey)
}
for (entry in upperLines) {
selections.put(entry.intKey - 1, entry.value)
}
return true
}
fun moveCursors(line: Int, character: Int, moveBy: Int) {
@Suppress("name_shadowing")
val moveBy = if (character + moveBy < 0) -character else moveBy
if (cursorLine == line && cursorCharacter >= character) {
cursorCharacter = (cursorCharacter + moveBy).coerceIn(0, this[cursorLine]?.length ?: Int.MAX_VALUE)
}
val selection = selections[line]
if (selection != null) {
val (start, end) = selection
if (character < start) {
selections[line] = TextSelection(start + moveBy, end + moveBy)
} else if (character > end && character + moveBy < end) {
if (character + moveBy < start) {
// selections.remove(line)
selections[line] = TextSelection(0, 0)
} else {
selections[line] = TextSelection(start, character + moveBy)
}
} else if (character in start .. end) {
if (moveBy > 0 || character + moveBy <= start) {
selections[line] = TextSelection(start, end + moveBy)
} else {
selections[line] = TextSelection(character + moveBy, end + moveBy)
}
}
}
}
fun wipeSelection() {
val inversed = ArrayList(selections.int2ObjectEntrySet())
inversed.sortByDescending { it.intKey }
for ((lineNumber, selection) in inversed) {
if (selection.start != selection.end) {
if (selection.start == 0 && selection.end == this[lineNumber]!!.length) {
}
}
}
selections.clear()
}
protected open fun textChanged(oldText: String, newText: String) {}
var text: String
get() {
var textCache = textCache
if (textCache == null) {
textCache = lines.joinToString("\n")
this.textCache = textCache
}
return textCache
}
set(value) {
if ((textCache ?: lines.joinToString("\n")) == value)
return
snapshotTimer = null
selections.clear()
lines.clear()
redo.clear()
undo.clear()
cursorLine = 0
cursorCharacter = 0
lines.addAll(value.split(NEWLINES))
}
fun advanceCursorLeft() {
val line = this[cursorLine]
if (line != null && cursorCharacter > line.length) {
cursorCharacter = line.length - 1
}
if (cursorCharacter > 0) {
cursorCharacter--
} else if (cursorLine > 0) {
cursorLine--
cursorCharacter = 0
@Suppress("name_shadowing")
val line = this[cursorLine]
if (line != null) {
cursorCharacter = line.length
}
}
}
fun advanceCursorRight() {
cursorCharacter++
val line = this[cursorLine]
if (line != null && cursorCharacter > line.length) {
if (lines.size <= cursorLine + 1) {
cursorCharacter = line.length
} else {
cursorLine++
cursorCharacter = 0
}
} else if (line == null) {
cursorCharacter = 0
}
}
override fun keyPressedInternal(key: Int, scancode: Int, mods: Int): Boolean {
if (key == InputConstants.KEY_ESCAPE) {
killFocus()
return true
}
if (key == InputConstants.KEY_RETURN) {
if (multiLine) {
if (!minecraft.window.isShiftDown && !minecraft.window.isCtrlDown)
wipeSelection()
val line = this[cursorLine]
if (line != null && !minecraft.window.isShiftDown) {
if (cursorCharacter <= 0) {
recordHistory(true)
insertLine(cursorLine, "")
if (!minecraft.window.isCtrlDown) {
cursorLine++
cursorCharacter = 0
}
} else if (cursorCharacter >= line.length) {
recordHistory(true)
insertLine(cursorLine + 1, "")
if (!minecraft.window.isCtrlDown) {
cursorLine++
cursorCharacter = 0
}
} else {
val before = line.substring(0, cursorCharacter)
val after = line.substring(cursorCharacter)
recordHistory(true)
this[cursorLine] = before
insertLine(cursorLine + 1, after)
if (!minecraft.window.isCtrlDown) {
cursorLine++
cursorCharacter = 0
}
}
} else {
recordHistory(true)
insertLine(cursorLine + 1)
cursorLine++
cursorCharacter = 0
}
return true
} else {
killFocus()
return true
}
}
if (key == InputConstants.KEY_TAB) {
if (multiLine) {
val lines = if (selections.isNotEmpty()) selections.keys else listOf(cursorLine)
if (minecraft.window.isKeyDown(InputConstants.KEY_RSHIFT) || minecraft.window.isKeyDown(InputConstants.KEY_LSHIFT)) {
var hit = false
for (lineNumber in lines) {
val line = this[lineNumber]!!
if (line[0] == '\t') {
if (!hit) {
hit = true
recordHistory(true)
}
this[lineNumber] = line.substring(1)
moveCursors(lineNumber, 1, -1)
}
}
} else {
if (lines.isNotEmpty())
recordHistory(true)
for (lineNumber in lines) {
this[lineNumber] = "\t" + this[lineNumber]!!
moveCursors(lineNumber, 0, 1)
}
}
return true
}
killFocus()
return true
}
if (key == InputConstants.KEY_LEFT) {
if (minecraft.window.isCtrlDown) {
val line = this[cursorLine]
if (line == null || cursorCharacter <= 0) {
advanceCursorLeft()
} else {
if (cursorCharacter >= line.length)
cursorCharacter = line.length - 1
cursorCharacter = greedyAdvanceLeft(line, cursorCharacter)
}
} else {
advanceCursorLeft()
}
return true
} else if (key == InputConstants.KEY_RIGHT) {
if (minecraft.window.isCtrlDown) {
val line = this[cursorLine]
if (line == null || cursorCharacter + 1 >= line.length) {
advanceCursorRight()
} else {
if (cursorCharacter < 0)
cursorCharacter = 0
cursorCharacter = greedyAdvanceRight(line, cursorCharacter)
}
} else {
advanceCursorRight()
}
return true
} else if (key == InputConstants.KEY_UP) {
if (cursorLine > 0) {
cursorLine--
}
return true
} else if (key == InputConstants.KEY_DOWN) {
if (cursorLine < lines.size - 1) {
cursorLine++
}
return true
}
if (key == InputConstants.KEY_BACKSPACE) {
if (cursorLine <= 0 && cursorCharacter <= 0) {
return true
} else if (cursorCharacter <= 0) {
val line = this[cursorLine]
if (line == null) {
cursorCharacter = this[--cursorLine]?.length ?: 0
} else {
removeLine(cursorLine)
val newLine = this[cursorLine]
if (newLine == null) {
this[cursorLine] = line
} else {
this[cursorLine] = newLine + line
}
recordHistory()
}
} else {
val line = this[cursorLine]
if (line != null) {
var cursorCharacter = cursorCharacter
if (cursorCharacter >= line.length)
cursorCharacter = line.length - 1
val newLine = line.substring(0, cursorCharacter - 1) + line.substring(cursorCharacter)
moveCursors(cursorLine, cursorCharacter, -1)
this[cursorLine] = newLine
recordHistory()
}
}
return true
}
if (key == InputConstants.KEY_DELETE) {
if (cursorLine !in 0 until lines.size) {
cursorLine = lines.size - 1
return true
}
if (minecraft.window.isShiftDown) {
pushbackSnapshot()
removeLine(cursorLine)
if (cursorLine >= lines.size)
cursorLine = lines.size
cursorCharacter = 0
recordHistory(true)
return true
}
val line = this[cursorLine]!!
if (cursorCharacter >= line.length) {
if (cursorLine + 1 == lines.size) {
return true
}
if (snapshotTimer == null)
pushbackSnapshot()
val bottomLine = this[cursorLine + 1]!!
cursorCharacter = line.length
this[cursorLine] = line + bottomLine
removeLine(cursorLine + 1)
recordHistory()
} else {
if (snapshotTimer == null)
pushbackSnapshot()
val cursorCharacter = cursorCharacter
moveCursors(cursorLine, cursorCharacter, -1)
this[cursorLine] = line.substring(0, cursorCharacter) + line.substring(cursorCharacter + 1)
if (cursorCharacter != 0)
this.cursorCharacter++
recordHistory()
}
return true
}
if (key == InputConstants.KEY_Z && minecraft.window.isCtrlDown) {
undo()
} else if (key == InputConstants.KEY_Y && minecraft.window.isCtrlDown) {
redo()
}
return true
}
override fun charTypedInternal(codepoint: Char, mods: Int): Boolean {
var line = this[cursorLine]
if (line == null) {
set(cursorLine, "")
line = ""
cursorCharacter = 0
}
if (cursorCharacter >= line.length)
line += codepoint
else
line = line.substring(0, cursorCharacter) + codepoint + line.substring(cursorCharacter)
if (snapshotTimer == null)
pushbackSnapshot()
set(cursorLine, line)
moveCursors(cursorLine, cursorCharacter, 1)
recordHistory()
return true
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
var y = 0f
for (line in lines) {
font.drawAligned(
poseStack = stack,
buffer = BUFFER,
text = line,
align = TextAlign.TOP_LEFT,
x = 0f,
y = y,
color = textColor
)
y += font.lineHeight + 2f
}
if (isFocused && milliTime % 1000L > 200L) {
val activeLine = this[cursorLine]
if (activeLine == null || cursorCharacter >= activeLine.length) {
font.drawAligned(
poseStack = stack,
buffer = BUFFER,
text = "_",
align = TextAlign.TOP_LEFT,
x = if (activeLine == null) 0f else font.width(activeLine).toFloat(),
y = cursorLine * (font.lineHeight + 2f),
color = cursorColor
)
} else {
font.drawAligned(
poseStack = stack,
buffer = BUFFER,
text = "|",
align = TextAlign.TOP_LEFT,
x = font.width(activeLine.substring(0, cursorCharacter)).toFloat() - 1f,
y = cursorLine * (font.lineHeight + 2f),
color = cursorColor
)
}
}
if (debug) {
font.drawAligned(
poseStack = stack,
buffer = BUFFER,
text = cursorLine.toString(),
align = TextAlign.TOP_RIGHT,
x = width,
y = 0f,
color = cursorColor
)
font.drawAligned(
poseStack = stack,
buffer = BUFFER,
text = cursorCharacter.toString(),
align = TextAlign.TOP_RIGHT,
x = width,
y = font.lineHeight + 2f,
color = cursorColor
)
font.drawAligned(
poseStack = stack,
buffer = BUFFER,
text = lines.size.toString(),
align = TextAlign.TOP_RIGHT,
x = width,
y = font.lineHeight * 2 + 4f,
color = cursorColor
)
}
BUFFER.endBatch()
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
requestFocus()
return true
}
private enum class CharType {
SPACES {
override fun contains(input: Char): Boolean {
return _SPACES.contains(input)
}
override fun canJumpInto(category: CharType): Boolean {
return true
}
},
CONTROL {
override fun contains(input: Char): Boolean {
return _CONTROL.contains(input)
}
override fun canJumpInto(category: CharType): Boolean {
return category == CONTROL
}
},
OTHER {
override fun contains(input: Char): Boolean {
return !_SPACES.contains(input) && !_CONTROL.contains(input)
}
override fun canJumpInto(category: CharType): Boolean {
return category == OTHER
}
};
abstract fun contains(input: Char): Boolean
abstract fun canJumpInto(category: CharType): Boolean
companion object {
private val values = values()
operator fun get(input: Char): CharType {
for (value in values) {
if (value.contains(input)) {
return value
}
}
throw NoSuchElementException("for $input")
}
}
}
companion object {
val NEWLINES = Regex("\n\r?")
private val BUFFER = DynamicBufferSource()
private val _SPACES = CharOpenHashSet().also {
for (char in " \t\n\r\u0000")
it.add(char)
}
private val _CONTROL = CharOpenHashSet().also {
for (char in "!@\"'#$%^&?*()~`[]/\\;:|<>.,")
it.add(char)
}
private fun greedyAdvanceLeft(input: String, position: Int): Int {
if (position <= 1)
return 0
@Suppress("name_shadowing")
var position = position
var type = CharType[input[--position]]
while (position > 0) {
val newType = CharType[input[position]]
if (type.canJumpInto(newType)) {
position--
type = newType
} else {
return position + 1
}
}
return position
}
private fun greedyAdvanceRight(input: String, position: Int): Int {
@Suppress("name_shadowing")
var position = position
var type = CharType[input[position]]
while (position < input.length) {
val newType = CharType[input[position]]
if (type.canJumpInto(newType)) {
position++
type = newType
} else {
return position
}
}
return position
}
}
}

View File

@ -566,6 +566,8 @@ const val PIf = 3.1415927f
/**
* aligns 0,0 with top left point of block corner when looking directly at it,
* and swaps Y to +Y when going down and -Y when going up
*
* allows to draw 2D interface in 3D space
*/
fun PoseStack.rotateWithBlockFacing(rotation: Direction, clarifyingAxis: Direction? = null): PoseStack {
when (rotation) {

View File

@ -10,6 +10,15 @@ class HoloSignMenu @JvmOverloads constructor(
inventory: Inventory,
tile: HoloSignBlockEntity? = null
) : MatteryMenu(MMenus.HOLO_SIGN, containerId, inventory, tile) {
var text by mSynchronizer.string(name = "text")
override val storageSlots: Collection<Slot>
get() = listOf()
override fun broadcastChanges() {
super.broadcastChanges()
if (tile is HoloSignBlockEntity)
text = tile.text
}
}

View File

@ -33,7 +33,7 @@ object MMenus {
val PLATE_PRESS: MenuType<*> by registry.register(MNames.PLATE_PRESS) { MenuType(::PlatePressMenu) }
val MATTER_RECYCLER: MenuType<*> by registry.register(MNames.MATTER_RECYCLER) { MenuType(::MatterRecyclerMenu) }
val ENERGY_SERVO: MenuType<*> by registry.register(MNames.ENERGY_SERVO) { MenuType(::EnergyServoMenu) }
val HOLO_SIGN: MenuType<*> by registry.register(MNames.HOLO_SIGN) { MenuType(::HoloSignMenu) }
val HOLO_SIGN: MenuType<HoloSignMenu> by registry.register(MNames.HOLO_SIGN) { MenuType(::HoloSignMenu) }
val STORAGE_BUS: MenuType<*> by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu) }
val STORAGE_EXPORTER: MenuType<*> by registry.register(MNames.STORAGE_EXPORTER) { MenuType(::StorageExporterMenu) }
@ -71,6 +71,7 @@ object MMenus {
MenuScreens.register(STORAGE_IMPORTER as MenuType<StorageImporterMenu>, ::StorageImporterScreen)
MenuScreens.register(STORAGE_POWER_SUPPLIER as MenuType<StoragePowerSupplierMenu>, ::StoragePowerSupplierScreen)
MenuScreens.register(ENERGY_SERVO as MenuType<EnergyServoMenu>, ::EnergyServoScreen)
MenuScreens.register(HOLO_SIGN, ::HoloSignScreen)
}
}
}