From dd74643c6721f22d8cfebc4bb93c1f3acb121718 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 8 Mar 2023 09:34:06 +0700 Subject: [PATCH] Quick search in pattern grid --- .../mc/otm/datagen/lang/English.kt | 2 + .../mc/otm/datagen/lang/Russian.kt | 2 + .../client/screen/matter/MatterPanelScreen.kt | 45 +++++++++++----- .../screen/panels/input/TextInputPanel.kt | 16 ++++++ .../panels/util/DiscreteScrollBarPanel.kt | 2 +- .../mc/otm/menu/matter/MatterPanelMenu.kt | 52 ++++++++++++++++++- 6 files changed, 104 insertions(+), 15 deletions(-) diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt index cc2c3c63c..eaf589cda 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt @@ -586,6 +586,8 @@ private fun androidFeatures(provider: MatteryLanguageProvider) { private fun gui(provider: MatteryLanguageProvider) { with(provider.english) { + gui("quicksearch", "Quick search...") + gui("item_monitor.refill_source.desc", "Controls from where to take items for slot auto refill") gui("item_monitor.refill_source.system", "System only. Crafting grid will be auto refilled only from storage system. This is the behavior you see in AE2 and Refined Storage") gui("item_monitor.refill_source.inventory", "Inventory only. Crafting grid will be auto refilled only from player's inventory") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt index e78534980..cdce390c5 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt @@ -594,6 +594,8 @@ private fun androidFeatures(provider: MatteryLanguageProvider) { private fun gui(provider: MatteryLanguageProvider) { with(provider.russian) { + gui("quicksearch", "Быстрый поиск...") + gui("item_monitor.refill_source.desc", "Контролирует источник предметов для заполнения сетки создания") gui("item_monitor.refill_source.system", "Только система. Сетка создания будет заполняться только из системы предметов. Данный параметр соответствует поведению AE2 и Refined Storage") gui("item_monitor.refill_source.inventory", "Только инвентарь. Сетка создания будет заполняться только из инвентаря игрока") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterPanelScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterPanelScreen.kt index eb86c69f5..baabc38e2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterPanelScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterPanelScreen.kt @@ -32,6 +32,7 @@ import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu import ru.dbotthepony.mc.otm.menu.matter.ReplicationRequestPacket import ru.dbotthepony.mc.otm.network.MenuNetworkChannel import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak +import java.util.function.Predicate import kotlin.math.roundToInt @MouseTweaksDisableWheelTweak @@ -45,7 +46,8 @@ class MatterPanelScreen( var scrollPatterns = 0 var scrollTasks = 0 - val frame = FramePanel.padded(this, null, GRID_WIDTH * AbstractSlotPanel.SIZE + ScrollBarConstants.WIDTH + 4f, GRID_HEIGHT * AbstractSlotPanel.SIZE, title) + val frame = FramePanel.padded(this, null, GRID_WIDTH * AbstractSlotPanel.SIZE + ScrollBarConstants.WIDTH + 4f, GRID_HEIGHT * AbstractSlotPanel.SIZE + 2f, title) + frame.dockPadding = frame.dockPadding.copy(top = frame.dockPadding.top + 2f) val controls = DeviceControls(this, frame) @@ -69,9 +71,9 @@ class MatterPanelScreen( val scrollBar = DiscreteScrollBarPanel(this, frame, { if (isPatternView) { - integerDivisionDown(menu.patterns.size, GRID_WIDTH) + integerDivisionDown(menu.patternsFiltered.size, GRID_WIDTH) } else { - integerDivisionDown(menu.tasks.size, GRID_WIDTH) + integerDivisionDown(menu.tasksFiltered.size, GRID_WIDTH) } }, { _, _, new -> if (isPatternView) @@ -80,6 +82,25 @@ class MatterPanelScreen( scrollTasks = new }) + object : TextInputPanel(this@MatterPanelScreen, frame) { + init { + width = frame.width * 0.4f + x = frame.width - width - FramePanel.PADDING + y = 4f + placeholder = TranslatableComponent("otm.gui.quicksearch") + } + + override fun onTextChanged(old: String, new: String) { + if (new == "") { + menu.filter = Predicate { true } + } else { + val new = new.lowercase() + menu.filter = Predicate { it.description.string.lowercase().contains(new) } + scrollBar.scroll = scrollBar.scroll + } + } + } + scrollBar.dock = Dock.RIGHT frame.Tab(onOpen = { isPatternView = true; scrollBar.scroll = scrollPatterns }, activeIcon = PATTERN_LIST_ACTIVE, inactiveIcon = PATTERN_LIST_INACTIVE).also { @@ -121,9 +142,9 @@ class MatterPanelScreen( override val itemStack: ItemStack get() { if (isPatternView) { - return menu.patterns.getOrNull(index)?.stack() ?: ItemStack.EMPTY + return menu.patternsFiltered.getOrNull(index)?.stack() ?: ItemStack.EMPTY } else { - return menu.tasks.getOrNull(index)?.let { it.stack(it.required + it.inProgress) } ?: ItemStack.EMPTY + return menu.tasksFiltered.getOrNull(index)?.let { it.stack(it.required + it.inProgress) } ?: ItemStack.EMPTY } } @@ -131,13 +152,13 @@ class MatterPanelScreen( val list = super.getItemStackTooltip(stack).toMutableList() if (isPatternView) { - menu.patterns.getOrNull(index)?.let { + menu.patternsFiltered.getOrNull(index)?.let { list.add(TranslatableComponent( "otm.item.pattern.research", String.format("%.2f", it.researchPercent * 100.0) ).withStyle(ChatFormatting.AQUA)) } } else { - menu.tasks.getOrNull(index)?.let { + menu.tasksFiltered.getOrNull(index)?.let { list.add(TranslatableComponent("otm.gui.matter_task.total", it.total).withStyle(ChatFormatting.GRAY)) list.add(TranslatableComponent("otm.gui.matter_task.required", it.required).withStyle(ChatFormatting.GRAY)) list.add(TranslatableComponent("otm.gui.matter_task.in_progress", it.inProgress).withStyle(ChatFormatting.GRAY)) @@ -151,9 +172,9 @@ class MatterPanelScreen( override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { if (isPatternView) { if (minecraft?.player?.isSpectator != true) - menu.patterns.getOrNull(index)?.let(this@MatterPanelScreen::openPattern) + menu.patternsFiltered.getOrNull(index)?.let(this@MatterPanelScreen::openPattern) } else { - menu.tasks.getOrNull(index)?.let(this@MatterPanelScreen::openTask) + menu.tasksFiltered.getOrNull(index)?.let(this@MatterPanelScreen::openTask) } return true @@ -180,13 +201,13 @@ class MatterPanelScreen( } override val itemStack: ItemStack get() { - return menu.tasks.firstOrNull { it.id == task.id }?.let { it.stack((it.required + it.inProgress).coerceAtLeast(1)) } ?: task.stack((task.required + task.inProgress).coerceAtLeast(1)) + return menu.tasksFiltered.firstOrNull { it.id == task.id }?.let { it.stack((it.required + it.inProgress).coerceAtLeast(1)) } ?: task.stack((task.required + task.inProgress).coerceAtLeast(1)) } override fun tickInner() { super.tickInner() - if (!menu.tasks.any { it.id == task.id }) { + if (!menu.tasksFiltered.any { it.id == task.id }) { frame.remove() } } @@ -254,7 +275,7 @@ class MatterPanelScreen( override fun tickInner() { super.tickInner() - if (!menu.patterns.any { it.id == pattern.id }) { + if (!menu.patternsFiltered.any { it.id == pattern.id }) { frame.remove() } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/TextInputPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/TextInputPanel.kt index b3f29b3ec..c93619f69 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/TextInputPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/TextInputPanel.kt @@ -12,6 +12,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap import it.unimi.dsi.fastutil.ints.Int2ObjectMap import net.minecraft.client.gui.screens.Screen import net.minecraft.client.renderer.GameRenderer +import net.minecraft.network.chat.Component import org.joml.Vector2i import ru.dbotthepony.mc.otm.client.isCtrlDown import ru.dbotthepony.mc.otm.client.isShiftDown @@ -24,6 +25,7 @@ import ru.dbotthepony.mc.otm.client.render.drawRect import ru.dbotthepony.mc.otm.client.render.tesselator import ru.dbotthepony.mc.otm.client.screen.panels.DockProperty import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel +import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.addAll import ru.dbotthepony.mc.otm.core.math.RGBAColor import ru.dbotthepony.mc.otm.milliTime @@ -141,7 +143,9 @@ open class TextInputPanel( var cursorLine = 0 var cursorRow = 0 + open var placeholder: Component = TextComponent("") open var textColor = RGBAColor.WHITE + open var placeholderColor = RGBAColor.DARK_GRAY open var cursorColor = RGBAColor.GREEN open var backgroundColor = RGBAColor.BLACK open var isActive = true @@ -1085,6 +1089,18 @@ open class TextInputPanel( var y = topPadding + if (lines.isEmpty() || lines.size == 1 && lines[0] == "") { + font.drawAligned( + poseStack = stack, + buffer = BUFFER, + text = placeholder, + align = TextAlign.TOP_LEFT, + x = dockPadding.left, + y = y, + color = placeholderColor + ) + } + for (i in scrollLines until lines.size) { val line = lines[i] val selection = selections[i] diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/DiscreteScrollBarPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/DiscreteScrollBarPanel.kt index 6bd79d6d2..43c06c6aa 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/DiscreteScrollBarPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/DiscreteScrollBarPanel.kt @@ -109,7 +109,7 @@ open class DiscreteScrollBarPanel( var scroll = 0 set(value) { - val newValue = value.coerceAtLeast(0).coerceAtMost(maxScroll.invoke(this)) + val newValue = value.coerceIn(0, maxScroll.invoke(this)) if (newValue != field) { val old = field diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterPanelMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterPanelMenu.kt index c9a62439e..e7b237950 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterPanelMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterPanelMenu.kt @@ -4,6 +4,7 @@ import net.minecraft.network.FriendlyByteBuf import net.minecraft.server.level.ServerPlayer import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.Item import net.minecraftforge.network.NetworkEvent import net.minecraftforge.network.PacketDistributor import org.apache.logging.log4j.LogManager @@ -23,6 +24,7 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.network.* import ru.dbotthepony.mc.otm.registry.MMenus import java.util.* +import java.util.function.Predicate import java.util.function.Supplier import kotlin.Comparator import kotlin.collections.ArrayList @@ -158,7 +160,9 @@ class MatterPanelMenu @JvmOverloads constructor( codec = ItemSorter::class.codec(), observer = { patterns.sortWith(actualComparator) + patternsFiltered.sortWith(actualComparator) tasks.sortWith(actualTaskComparator) + tasksFiltered.sortWith(actualTaskComparator) }) val isAscending: Boolean by mSynchronizer.ComputedField( @@ -166,7 +170,9 @@ class MatterPanelMenu @JvmOverloads constructor( codec = BooleanValueCodec, observer = { patterns.sortWith(actualComparator) + patternsFiltered.sortWith(actualComparator) tasks.sortWith(actualTaskComparator) + tasksFiltered.sortWith(actualTaskComparator) }) val totalMatterStored: Decimal by mSynchronizer.ComputedField( @@ -174,6 +180,25 @@ class MatterPanelMenu @JvmOverloads constructor( codec = DecimalValueCodec, ) + var filter: Predicate = Predicate { true } + set(value) { + field = value + patternsFiltered.clear() + tasksFiltered.clear() + + for (pattern in patterns) { + if (value.test(pattern.item)) { + patternsFiltered.add(pattern) + } + } + + for (task in tasks) { + if (value.test(task.item)) { + tasksFiltered.add(task) + } + } + } + val changeIsAscending = booleanInput(allowSpectators = true) { tile?.getPlayerSettings(ply)?.ascending = it } val changeSorting = PlayerInput(ItemSorter::class.codec(), allowSpectators = true) { tile?.getPlayerSettings(ply)?.sorter = it } @@ -183,8 +208,11 @@ class MatterPanelMenu @JvmOverloads constructor( private val actualComparator = Comparator { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) } private val actualTaskComparator = Comparator> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) } - val patterns = ArrayList() - val tasks = ArrayList>() + private val patterns = ArrayList() + private val tasks = ArrayList>() + + val patternsFiltered = ArrayList() + val tasksFiltered = ArrayList>() fun networkPatternsUpdated(patterns: Collection) { for (pattern in patterns) { @@ -195,6 +223,16 @@ class MatterPanelMenu @JvmOverloads constructor( } else { this.patterns.addSorted(pattern, actualComparator) } + + if (filter.test(pattern.item)) { + val index = this.patternsFiltered.indexOfFirst(pattern::matchId) + + if (index != -1) { + this.patternsFiltered[index] = pattern + } else { + this.patternsFiltered.addSorted(pattern, actualComparator) + } + } } } @@ -213,6 +251,16 @@ class MatterPanelMenu @JvmOverloads constructor( } else { this.tasks.addSorted(task, actualTaskComparator) } + + if (filter.test(task.item)) { + val index = this.tasksFiltered.indexOfFirst(task::matchId) + + if (index != -1) { + this.tasksFiltered[index] = task + } else { + this.tasksFiltered.addSorted(task, actualTaskComparator) + } + } } }