feat: reimplement on-screen keyboard for monitor search

UI.TextEntry requires keyboard input which monitors don't have.
Replaced it with the original touch-friendly approach:
- Search bar shows query text with toggle button (? / X)
- Tapping the search row opens an on-screen keyboard overlay
- 3-row QWERTY layout with Bksp, Done, Space, Clr keys
- Keyboard overlays the bottom status bars when active
- All key zones use touch hit-testing for monitor_touch events
This commit is contained in:
MayaTheShy
2026-03-22 19:22:03 -04:00
parent b49574f39b
commit 215652d47c

View File

@@ -40,6 +40,7 @@ local smelterPage = nil
local selectedAmount = 1
local amountOptions = {1, 4, 8, 16, 32, 64}
local searchQuery = ""
local showKeyboard = false
local smelterView = "status"
-------------------------------------------------
@@ -263,17 +264,45 @@ local function buildMainPage()
searchRow = UI.Window {
x = 1, y = 6, ex = -1, height = 1,
backgroundColor = colors.black,
},
searchEntry = UI.TextEntry {
x = 3, y = 6,
ex = '45%',
shadowText = "search...",
backgroundColor = colors.black,
backgroundFocusColor = colors.gray,
textColor = colors.white,
shadowTextColor = colors.gray,
limit = 30,
draw = function(self)
self:clear(colors.black)
-- Keyboard toggle button
local kbLabel = showKeyboard and " X " or " ? "
local kbBg = showKeyboard and colors.red or colors.purple
self:write(1, 1, kbLabel, kbBg, colors.white)
-- Search query display
local fieldW = math.floor(self.width * 0.4)
if fieldW < 10 then fieldW = 10 end
local queryDisplay = searchQuery
if showKeyboard then
queryDisplay = queryDisplay .. "|"
elseif queryDisplay == "" then
queryDisplay = "search..."
end
local displayText = queryDisplay:sub(1, fieldW)
displayText = displayText .. string.rep("_", math.max(0, fieldW - #displayText))
local tc = (searchQuery == "" and not showKeyboard) and colors.gray or colors.white
self:write(5, 1, displayText, colors.black, tc)
end,
eventHandler = function(self, event)
if event.type == 'mouse_click' then
showKeyboard = not showKeyboard
local page = self:getPage()
if showKeyboard then
UI.Window.enable(page.keyboard)
page.keyboard:draw()
else
page.keyboard:disable()
page.alertBar:draw()
page.footerBar:draw()
page.bottomBar:draw()
end
self:draw()
page:sync()
return true
end
return UI.Window.eventHandler(self, event)
end,
},
refreshBtn = UI.Button {
@@ -383,15 +412,118 @@ local function buildMainPage()
end,
},
-- On-screen keyboard overlay (bottom 3 rows; starts disabled)
keyboard = UI.Window {
x = 1, ex = -1, ey = -1, height = 3,
backgroundColor = colors.black,
enable = function() end, -- prevent auto-enable; toggled manually
draw = function(self)
self:clear(colors.black)
local kbDefs = {
{ keys = {"Q","W","E","R","T","Y","U","I","O","P"}, specials = {{ label = " Bksp ", bg = colors.red, action = "kb_bksp" }} },
{ keys = {"A","S","D","F","G","H","J","K","L"}, specials = {{ label = " Done ", bg = colors.green, action = "kb_done" }} },
{ keys = {"Z","X","C","V","B","N","M"}, specials = {
{ label = " Space ", bg = colors.lightGray, action = "kb_space" },
{ label = " Clr ", bg = colors.orange, action = "kb_clear" },
}},
}
self._zones = {}
local keyW = 3
local keyGap = 1
for rowIdx, def in ipairs(kbDefs) do
local y = rowIdx
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((self.width - rowW) / 2) + 1
-- Draw letter keys
for ki, key in ipairs(def.keys) do
self:write(x, y, " " .. key .. " ", colors.gray, colors.white)
table.insert(self._zones, { x1 = x, y1 = y, x2 = x + keyW - 1, y2 = y, action = "kb_key", data = 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
self:write(x, y, sp.label, sp.bg, colors.white)
table.insert(self._zones, { x1 = x, y1 = y, x2 = x + #sp.label - 1, y2 = y, action = sp.action })
x = x + #sp.label
end
end
end,
eventHandler = function(self, event)
if event.type == 'mouse_click' then
if self._zones then
for _, zone in ipairs(self._zones) do
if event.x >= zone.x1 and event.x <= zone.x2
and event.y >= zone.y1 and event.y <= zone.y2 then
self:emit({ type = zone.action, data = zone.data, element = self })
return true
end
end
end
return true -- consume click even if no zone hit
end
return UI.Window.eventHandler(self, event)
end,
},
-- Notification overlay
notification = UI.Notification {
anchor = 'bottom',
},
eventHandler = function(self, event)
if event.type == 'text_change' then
searchQuery = event.text or ""
if event.type == 'kb_key' then
if #searchQuery < 30 then
searchQuery = searchQuery .. event.data
end
D.refreshItemGrid()
self.searchRow:draw()
self.footerBar:draw()
self:sync()
return true
elseif event.type == 'kb_bksp' then
if #searchQuery > 0 then
searchQuery = searchQuery:sub(1, -2)
end
D.refreshItemGrid()
self.searchRow:draw()
self.footerBar:draw()
self:sync()
return true
elseif event.type == 'kb_space' then
if #searchQuery < 30 then
searchQuery = searchQuery .. " "
end
D.refreshItemGrid()
self.searchRow:draw()
self.footerBar:draw()
self:sync()
return true
elseif event.type == 'kb_done' then
showKeyboard = false
self.keyboard:disable()
self.searchRow:draw()
self.alertBar:draw()
self.footerBar:draw()
self.bottomBar:draw()
self:sync()
return true
elseif event.type == 'kb_clear' then
searchQuery = ""
showKeyboard = false
self.keyboard:disable()
D.refreshItemGrid()
self.searchRow:draw()
self.alertBar:draw()
self.footerBar:draw()
self.bottomBar:draw()