From b287cff75fbf7f2c73f261d7df3d233065895823 Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Sun, 15 Mar 2026 18:35:51 -0400 Subject: [PATCH] Implement search functionality with keyboard support and pagination in inventory dashboard --- inventoryManager.lua | 315 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 255 insertions(+), 60 deletions(-) diff --git a/inventoryManager.lua b/inventoryManager.lua index 0f6b4af..5c026a3 100644 --- a/inventoryManager.lua +++ b/inventoryManager.lua @@ -151,6 +151,34 @@ local touchZones = {} local pendingZones = {} local needsRedraw = true +local currentPage = 1 +local totalPages = 1 +local searchQuery = "" +local showKeyboard = false + +-- Keyboard layout +local kbRows = { + {"Q","W","E","R","T","Y","U","I","O","P"}, + {"A","S","D","F","G","H","J","K","L"}, + {"Z","X","C","V","B","N","M"}, +} + +-- Get items filtered by search query +local function getFilteredItems() + local filtered = {} + for _, item in ipairs(cache.itemList) do + if searchQuery == "" then + table.insert(filtered, item) + else + local lower = item.name:lower():gsub("minecraft:", ""):gsub("_", " ") + if lower:find(searchQuery:lower(), 1, true) then + table.insert(filtered, item) + end + end + end + return filtered +end + local function addZone(x1, y1, x2, y2, action, data) table.insert(pendingZones, { x1 = x1, y1 = y1, x2 = x2, y2 = y2, @@ -294,9 +322,63 @@ local function drawDashboard() local sx1, sy1, sx2, sy2 = drawButton(scanX, 5, refreshTxt, refreshFg, refreshBg, 1, 1) addZone(sx1, sy1, sx2, sy2, "scan", nil) - -- ===== Row 6: blank spacer ===== + -- ===== Search bar + Pagination (row 6) ===== monFill(6, colors.black) + -- Keyboard toggle button + local kbLabel = showKeyboard and " X " or " ? " + local kbBg = showKeyboard and colors.red or colors.purple + monWrite(2, 6, kbLabel, colors.white, kbBg) + addZone(2, 6, 4, 6, "kb_toggle", nil) + + -- Search query display + local queryDisplay = searchQuery + if showKeyboard then + queryDisplay = queryDisplay .. "|" + elseif queryDisplay == "" then + queryDisplay = "search..." + end + local fieldW = math.floor(w * 0.4) + if fieldW < 10 then fieldW = 10 end + local displayText = queryDisplay:sub(1, fieldW) + displayText = displayText .. string.rep("_", math.max(0, fieldW - #displayText)) + monWrite(6, 6, displayText, + (searchQuery == "" and not showKeyboard) and colors.gray or colors.white, + colors.black) + addZone(6, 6, 5 + fieldW, 6, "kb_toggle", nil) + + -- Filter items + local filteredItems = getFilteredItems() + + -- Pagination + local maxRows = h - 10 + if maxRows < 1 then maxRows = 1 end + totalPages = math.max(1, math.ceil(#filteredItems / maxRows)) + if currentPage > totalPages then currentPage = totalPages end + if currentPage < 1 then currentPage = 1 end + + -- Page controls (right side of row 6) + local pageStr = string.format("Pg %d/%d", currentPage, totalPages) + local navW = 3 + 1 + #pageStr + 1 + 3 + local navX = w - navW + + if currentPage > 1 then + monWrite(navX, 6, " < ", colors.white, colors.gray) + addZone(navX, 6, navX + 2, 6, "page_prev", nil) + else + monWrite(navX, 6, " < ", colors.lightGray, colors.black) + end + + monWrite(navX + 4, 6, pageStr, colors.lightGray, colors.black) + + local nextX = navX + 4 + #pageStr + 1 + if currentPage < totalPages then + monWrite(nextX, 6, " > ", colors.white, colors.gray) + addZone(nextX, 6, nextX + 2, 6, "page_next", nil) + else + monWrite(nextX, 6, " > ", colors.lightGray, colors.black) + end + -- ===== Column headers (row 7) ===== local row = 7 monFill(row, colors.gray) @@ -307,74 +389,140 @@ local function drawDashboard() monWrite(w - 1, row, ">", colors.lightGray, colors.gray) row = row + 1 - -- ===== Item rows ===== - local itemList = cache.itemList + -- ===== Item rows (paginated + filtered) ===== local maxCount = 0 - for _, item in ipairs(itemList) do + for _, item in ipairs(filteredItems) do if item.total > maxCount then maxCount = item.total end end if maxCount == 0 then maxCount = 1 end - local maxRows = h - row - 3 - for i, item in ipairs(itemList) do - if i > maxRows then break end + local startIdx = (currentPage - 1) * maxRows + 1 + local endIdx = math.min(startIdx + maxRows - 1, #filteredItems) - local y = row - local short = item.name:gsub("^minecraft:", ""):gsub("_", " ") - short = short:sub(1,1):upper() .. short:sub(2) - - local maxNameLen = w - 30 - if #short > maxNameLen then - short = short:sub(1, maxNameLen - 2) .. ".." + if #filteredItems == 0 then + monFill(8, colors.black) + monFill(9, colors.black) + if searchQuery ~= "" then + monCenter(9, "No items match \"" .. searchQuery .. "\"", colors.gray, colors.black) + else + monCenter(9, "No items in storage", colors.gray, colors.black) end + row = 10 + else + for i = startIdx, endIdx do + local item = filteredItems[i] + local y = row + local short = item.name:gsub("^minecraft:", ""):gsub("_", " ") + short = short:sub(1,1):upper() .. short:sub(2) - local rowBg = (i % 2 == 0) and colors.gray or colors.black - monFill(y, rowBg) + local maxNameLen = w - 30 + if #short > maxNameLen then + short = short:sub(1, maxNameLen - 2) .. ".." + end - monWrite(2, y, string.format("%2d", i), colors.lightBlue, rowBg) - monWrite(5, y, short, colors.white, rowBg) - monWrite(w - 22, y, tostring(item.total), colors.yellow, rowBg) + local rowBg = ((i - startIdx) % 2 == 0) and colors.black or colors.gray + monFill(y, rowBg) - local ratio = item.total / maxCount - local barColor = colors.lime - if ratio < 0.25 then barColor = colors.red - elseif ratio < 0.5 then barColor = colors.orange + monWrite(2, y, string.format("%2d", i), colors.lightBlue, rowBg) + monWrite(5, y, short, colors.white, rowBg) + monWrite(w - 22, y, tostring(item.total), colors.yellow, rowBg) + + local ratio = item.total / maxCount + local barColor = colors.lime + if ratio < 0.25 then barColor = colors.red + elseif ratio < 0.5 then barColor = colors.orange + end + monBar(w - 14, y, 12, ratio, barColor, rowBg == colors.gray and colors.lightGray or colors.gray) + + monWrite(w - 1, y, ">", colors.orange, rowBg) + addZone(1, y, w, y, "order", item.name) + + row = row + 1 end - monBar(w - 14, y, 12, ratio, barColor, rowBg == colors.gray and colors.lightGray or colors.gray) - - monWrite(w - 1, y, ">", colors.orange, rowBg) - addZone(1, y, w, y, "order", i) + end + -- Fill remaining empty item rows + local lastItemRow = h - 3 + while row <= lastItemRow do + monFill(row, colors.black) row = row + 1 end - -- ===== Status message ===== - local msgY = h - 2 - monFill(msgY, colors.black) - if statusTimer > 0 and #statusMessage > 0 then - monCenter(msgY, statusMessage, statusColor, colors.black) + if showKeyboard then + -- ===== Keyboard overlay (bottom 3 rows: h-2, h-1, h) ===== + local keyW = 3 + local keyGap = 1 + + local kbDefs = { + { keys = kbRows[1], specials = {{ label = " Bksp ", action = "kb_bksp", bg = colors.red }} }, + { keys = kbRows[2], specials = {{ label = " Done ", action = "kb_done", bg = colors.green }} }, + { keys = kbRows[3], specials = { + { label = " Space ", action = "kb_space", bg = colors.lightGray }, + { label = " Clr ", action = "kb_clear", bg = colors.orange }, + }}, + } + + for rowIdx, def in ipairs(kbDefs) do + local y = h - 3 + rowIdx + monFill(y, colors.black) + + -- Calculate total row width for centering + local keysW = #def.keys * keyW + math.max(0, #def.keys - 1) * keyGap + local specialsW = 0 + for _, sp in ipairs(def.specials) do + specialsW = specialsW + keyGap + #sp.label + end + local rowW = keysW + specialsW + local x = math.floor((w - rowW) / 2) + 1 + + -- Draw letter keys + for ki, key in ipairs(def.keys) do + monWrite(x, y, " " .. key .. " ", colors.white, colors.gray) + addZone(x, y, x + keyW - 1, y, "kb_key", key:lower()) + x = x + keyW + if ki < #def.keys then x = x + keyGap end + end + + -- Draw special keys + for _, sp in ipairs(def.specials) do + x = x + keyGap + monWrite(x, y, sp.label, colors.white, sp.bg) + addZone(x, y, x + #sp.label - 1, y, sp.action, nil) + x = x + #sp.label + end + end + else + -- ===== Status message (h-2) ===== + monFill(h - 2, colors.black) + if statusTimer > 0 and #statusMessage > 0 then + monCenter(h - 2, statusMessage, statusColor, colors.black) + end + + -- ===== Footer (h-1) ===== + monFill(h - 1, colors.gray) + local footerLeft = string.format(" Total: %d items | %d types ", + cache.grandTotal, #cache.itemList) + monWrite(2, h - 1, footerLeft, colors.white, colors.gray) + + if searchQuery ~= "" then + local filterNote = string.format("| Showing %d ", #filteredItems) + monWrite(2 + #footerLeft + 1, h - 1, filterNote, colors.yellow, colors.gray) + end + + local timeStr = textutils.formatTime(os.time(), true) + monWrite(w - #timeStr - 1, h - 1, timeStr, colors.lightGray, colors.gray) + + -- ===== Bottom accent (h) ===== + monFill(h, colors.blue) + local bottomMsg = " Tap item to order " + if activity.dispensing then + bottomMsg = " DISPENSING... " + elseif activity.sorting then + bottomMsg = " SORTING BARREL... " + end + monCenter(h, bottomMsg, colors.lightBlue, colors.blue) end - -- ===== Footer ===== - local footerY = h - 1 - monFill(footerY, colors.gray) - monWrite(2, footerY, - string.format(" Total: %d items | %d types ", cache.grandTotal, #itemList), - colors.white, colors.gray) - - local timeStr = textutils.formatTime(os.time(), true) - monWrite(w - #timeStr - 1, footerY, timeStr, colors.lightGray, colors.gray) - - -- Bottom accent - monFill(h, colors.blue) - local bottomMsg = " Tap item to order " - if activity.dispensing then - bottomMsg = " DISPENSING... " - elseif activity.sorting then - bottomMsg = " SORTING BARREL... " - end - monCenter(h, bottomMsg, colors.lightBlue, colors.blue) - -- Flush to monitor draw.setVisible(true) @@ -512,22 +660,18 @@ local function handleTouch(x, y) if action == "amount" then selectedAmount = data print("[UI] Amount set to " .. data) - -- Instant visual feedback: just redraw (no peripheral calls) needsRedraw = true elseif action == "order" then - local idx = data - if cache.itemList[idx] then - local item = cache.itemList[idx] - local short = item.name:gsub("^minecraft:", ""):gsub("_", " ") - -- Immediate feedback + local itemName = data + if itemName then + local short = itemName:gsub("^minecraft:", ""):gsub("_", " ") statusMessage = string.format("Ordering %s x%d...", short, selectedAmount) statusColor = colors.cyan statusTimer = 10 activity.dispensing = true needsRedraw = true - -- Actual order happens next — draw will flush before peripheral calls - orderItem(item.name, selectedAmount) + orderItem(itemName, selectedAmount) end elseif action == "scan" then @@ -536,6 +680,57 @@ local function handleTouch(x, y) statusTimer = 3 needsRedraw = true print("[UI] Manual refresh") + + elseif action == "kb_toggle" then + showKeyboard = not showKeyboard + print("[UI] Keyboard " .. (showKeyboard and "open" or "closed")) + needsRedraw = true + + elseif action == "kb_key" then + if #searchQuery < 30 then + searchQuery = searchQuery .. data + end + currentPage = 1 + needsRedraw = true + + elseif action == "kb_bksp" then + if #searchQuery > 0 then + searchQuery = searchQuery:sub(1, -2) + end + currentPage = 1 + needsRedraw = true + + elseif action == "kb_space" then + if #searchQuery < 30 then + searchQuery = searchQuery .. " " + end + currentPage = 1 + needsRedraw = true + + elseif action == "kb_done" then + showKeyboard = false + print("[UI] Keyboard closed") + needsRedraw = true + + elseif action == "kb_clear" then + searchQuery = "" + currentPage = 1 + print("[UI] Search cleared") + needsRedraw = true + + elseif action == "page_prev" then + if currentPage > 1 then + currentPage = currentPage - 1 + print("[UI] Page " .. currentPage) + end + needsRedraw = true + + elseif action == "page_next" then + if currentPage < totalPages then + currentPage = currentPage + 1 + print("[UI] Page " .. currentPage) + end + needsRedraw = true end end