From be85af84c726e5522bd04a66f9e66c6f82bc8f36 Mon Sep 17 00:00:00 2001 From: 20kdc Date: Thu, 19 Apr 2018 02:00:03 +0100 Subject: [PATCH] Improve neoux slightly, and hopefully finish most of nbox2018 --- code/libs/neoux.lua | 76 +++++--- repository/apps/app-nbox2018.lua | 311 +++++++++++++++++++++---------- repository/docs/ul-neoux | 16 +- repository/docs/us-evrst | 27 +++ 4 files changed, 296 insertions(+), 134 deletions(-) diff --git a/code/libs/neoux.lua b/code/libs/neoux.lua index d076bbb..63d3e11 100644 --- a/code/libs/neoux.lua +++ b/code/libs/neoux.lua @@ -7,7 +7,7 @@ -- Control reference -- x/y/w/h: ints, position/size, 1,1 TL -- selectable: boolean --- key(window, update, char, code, down) +-- key(window, update, char, code, down) (If this returns something truthy, defaults are inhibited) -- touch(window, update, x, y, xI, yI, button) -- drag(window, update, x, y, xI, yI, button) -- drop(window, update, x, y, xI, yI, button) @@ -94,7 +94,7 @@ newNeoux = function (event, neo) return fmt.fmtText(...) end -- UI FRAMEWORK -- - neoux.tcwindow = function (w, h, controls, closing, bg, fg, selIndex) + neoux.tcwindow = function (w, h, controls, closing, bg, fg, selIndex, keyFlags) local function rotateSelIndex() local original = selIndex while true do @@ -119,6 +119,7 @@ newNeoux = function (event, neo) end rotateSelIndex() end + keyFlags = keyFlags or {} local function moveIndex(vertical, negative) if not controls[selIndex] then return end local currentMA, currentOA = controls[selIndex].y, controls[selIndex].x @@ -230,34 +231,45 @@ newNeoux = function (event, neo) end end elseif ev == "key" then - if b == 203 then - if c then - moveIndexAU(window, false, true) + if controls[selIndex] and controls[selIndex].key then + if controls[selIndex].key(window, function () doZone(window, controls[selIndex]) end, a, b, c) then + return end - elseif b == 205 then - if c then - moveIndexAU(window, false, false) - end - elseif b == 200 then - if c then - moveIndexAU(window, true, true) - end - elseif b == 208 then - if c then - moveIndexAU(window, true, false) - end - elseif a == 9 then - if c then - local c1 = controls[selIndex] - rotateSelIndex() - local c2 = controls[selIndex] - local cache = {} - if c1 then doZone(window, c1, cache) end - if c2 then doZone(window, c2, cache) end - end - elseif controls[selIndex] then - if controls[selIndex].key then - controls[selIndex].key(window, function () doZone(window, controls[selIndex]) end, a, b, c) + end + if b == 29 then + keyFlags.ctrl = c + elseif b == 157 then + keyFlags.rctrl = c + elseif b == 42 then + keyFlags.shift = c + elseif b == 54 then + keyFlags.rshift = c + elseif not (keyFlags.ctrl or keyFlags.rctrl or keyFlags.shift or keyFlags.rshift) then + if b == 203 then + if c then + moveIndexAU(window, false, true) + end + elseif b == 205 then + if c then + moveIndexAU(window, false, false) + end + elseif b == 200 then + if c then + moveIndexAU(window, true, true) + end + elseif b == 208 then + if c then + moveIndexAU(window, true, false) + end + elseif a == 9 then + if c then + local c1 = controls[selIndex] + rotateSelIndex() + local c2 = controls[selIndex] + local cache = {} + if c1 then doZone(window, c1, cache) end + if c2 then doZone(window, c2, cache) end + end end end elseif ev == "clipboard" then @@ -271,7 +283,7 @@ newNeoux = function (event, neo) elseif ev == "close" then closing(window) end - end, doZone + end end neoux.tcrawview = function (x, y, lines) return { @@ -308,6 +320,7 @@ newNeoux = function (event, neo) if d then if a == 13 or a == 32 then callback(window) + return true end end end, @@ -335,13 +348,16 @@ newNeoux = function (event, neo) key = function (window, update, a, c, d) if d then if a == 13 then + return true elseif a == 8 then local str = textprop() textprop(unicode.sub(str, 1, unicode.len(str) - 1)) update() + return true elseif a ~= 0 then textprop(textprop() .. unicode.char(a)) update() + return true end end end, diff --git a/repository/apps/app-nbox2018.lua b/repository/apps/app-nbox2018.lua index d22f9e8..d428a40 100644 --- a/repository/apps/app-nbox2018.lua +++ b/repository/apps/app-nbox2018.lua @@ -9,16 +9,7 @@ local holos = neo.requestAccess("c.hologram") local icecap = neo.requireAccess("x.neo.pub.base", "filedialogs") local window = neo.requireAccess("x.neo.pub.window", "window")(40, 13) - -local xyz = false -local state = false -local redstone = false -local button = false -local fileLabel = "NB2018" -local fileTooltip = nil - -local cx, cy, cz = 1, 1, 1 -local cursorBlink = false +local fmttext = require("fmttext") -- [true] = {["A"] = { -- tex = "", @@ -31,15 +22,209 @@ local boxes = { [true] = {}, [false] = {} } +local redstone = false +local button = false +local fileLabel = "NB2018" +local fileTooltip = nil + +-- program + +local xyz = false +local state = false + +local cx, cy, cz = 1, 1, 1 +local cursorBlink = false local selectedBox +local tintDigi = 0 + +-- minX/minY/minZ are +1 from the usual values +-- tex/rgb are defaults until edited +-- maxX/maxY/maxZ only present after 2nd point placed +-- final corrections performed on submission to boxes table local workingOnBox = nil local programState = "none" --- ["state"] = {lines, key, clipboard} +-- ["state"] = {lines, keydown, clipboard} local programStates = { - ["none"] = { + none = { + function (miText, mxText) + -- This state handles both box selected & box not selected, + -- because the box can get deselected out of program control + if selectedBox then + local targetBox = boxes[state][selectedBox] + return { + "'" .. selectedBox .. "' " .. targetBox.tex, + "Tint #" .. string.format("%06x", targetBox.rgb), + "Enter deselects, Delete deletes.", + "F9 and F10 change texture/tint." + } + end + local str = string.format("%02i, %02i, %02i", cx, cy, cz) + return { + "Nothing selected. " .. str, + "Enter starts a new box, while ", + " a box can be selected by its ", + " key. To print, press F8. " + } + end, + function (ka, kc) + if ka == 13 then + if selectedBox then + selectedBox = nil + else + -- Beginning box! + workingOnBox = { + minX = cx, + minY = cy, + minZ = cz, + tex = "", + rgb = 0xFFFFFF + } + programState = "point2" + end + elseif kc == 67 then + -- Texture + if selectedBox then programState = "texture" end + elseif kc == 68 then + -- Tint + if selectedBox then tintDigi = 1 programState = "tint" end + else + local cc = unicode.char(ka):upper() + if boxes[state][cc] then + selectedBox = cc + end + end + end, + function (text) + end + }, + point2 = { + function (miText, mxText) + return { + "Placing Point 2:" .. miText .. "/" .. mxText, + "Enter confirms.", + "Arrows move 2nd point.", + "Delete/Backspace cancels." + } + end, + function (ka, kc) + if ka == 127 or ka == 8 then + workingOnBox = nil + programState = "none" + elseif ka == 13 then + workingOnBox.maxX = cx + workingOnBox.maxY = cy + workingOnBox.maxZ = cz + local ch = 65 + while boxes[state][string.char(ch)] do + ch = ch + 1 + end + local ax, ay, az = workingOnBox.minX, workingOnBox.minY, workingOnBox.minZ + local bx, by, bz = workingOnBox.maxX, workingOnBox.maxY, workingOnBox.maxZ + workingOnBox.minX = math.min(ax, bx) - 1 + workingOnBox.minY = math.min(ay, by) - 1 + workingOnBox.minZ = math.min(az, bz) - 1 + workingOnBox.maxX = math.max(ax, bx) + workingOnBox.maxY = math.max(ay, by) + workingOnBox.maxZ = math.max(az, bz) + selectedBox = string.char(ch) + boxes[state][selectedBox] = workingOnBox + workingOnBox = nil + programState = "none" + end + end, + function (text) + end + }, + texture = { + function (miText, mxText) + local targetBox = boxes[state][selectedBox] + local fieldContent = unicode.safeTextFormat(targetBox.tex) + fieldContent = fmttext.pad(fieldContent, 30, false, false) + fieldContent = unicode.sub(fieldContent, math.max(1, unicode.len(fieldContent) - 29)) + return { + "Texturing Box:" .. miText .. "/" .. mxText, + "Type texture ID or use clipboard", + "[" .. fieldContent .. "]", + "Enter to confirm." + } + end, + function (ka, kc) + local targetBox = boxes[state][selectedBox] + if ka == 127 or ka == 8 then + targetBox.tex = unicode.sub(targetBox.tex, 1, unicode.len(targetBox.tex) - 1) + elseif ka == 13 then + programState = "none" + elseif ka >= 32 then + targetBox.tex = targetBox.tex .. unicode.char(ka) + end + end, + function (text) + boxes[state][selectedBox].tex = text + programState = "none" + end + }, + tint = { + function (miText, mxText) + local targetBox = boxes[state][selectedBox] + local a = "#" + local b = " " + local rgb = targetBox.rgb + local div = 0x100000 + for i = 1, 6 do + a = a .. string.format("%01x", math.floor(rgb / div) % 16) + if tintDigi == i then + b = b .. "^" + else + b = b .. " " + end + div = math.floor(div / 16) + end + return { + "Tinting Box:" .. miText .. "/" .. mxText, + a, + b, + "Enter hexadecimal digits." + } + end, + function (ka, kc) + local targetBox = boxes[state][selectedBox] + local shifts = { + 20, + 16, + 12, + 8, + 4, + 0 + } + local hexChars = { + [48] = 0, [65] = 10, [97] = 10, + [49] = 1, [66] = 11, [98] = 11, + [50] = 2, [67] = 12, [99] = 12, + [51] = 3, [68] = 13, [100] = 13, + [52] = 4, [69] = 14, [101] = 14, + [53] = 5, [70] = 15, [102] = 15, + [54] = 6, + [55] = 7, + [56] = 8, + [57] = 9, + } + if hexChars[ka] then + local shift = math.floor(2^shifts[tintDigi]) + local low = targetBox.rgb % shift + local high = math.floor(targetBox.rgb / (shift * 16)) * (shift * 16) + targetBox.rgb = low + high + (hexChars[ka] * shift) + tintDigi = 1 + (tintDigi or 1) + if tintDigi == 7 then + tintDigi = nil + programState = "none" + end + end + end, + function (text) + end } } @@ -197,38 +382,16 @@ local function render(line) end local miText = mix .. "," .. miy .. "," .. miz local mxText = mxx .. "," .. mxy .. "," .. mxz - local text = { - "Nothing selected. " .. miText, - "Enter starts a new box, while |F3 Load", - " a box can be selected by its |F4 Save", - " key. To print, press F8. |F5 XYXZ" + local text = programStates[programState][1](miText, mxText) + local menu = { + "|F1 New ", + "|F3 Load", + "|F4 Save", + "|F5 XYXZ" } - if selectedBox then - text = { - "'" .. selectedBox .. "' " .. boxes[state][selectedBox].tex, - require("fmttext").pad("Tint #" .. string.format("%08x", workingOnBox.rgb), 32, false, true) .. "|F3 Load", - "Enter deselects, Delete deletes,|F4 Save", - " and the A-Z keys still select. |F5 XYXZ" - } - elseif workingOnBox then - if not workingOnBox.maxX then - text = { - "Creating: " .. miText .. "/" .. mxText, - "Arrows to move around. Use F5 to|F3 Load", - " swap from XY to XZ or back. |F4 Save", - "Enter confirms, Delete cancels. |F5 XYXZ" - } - else - local tex = require("fmttext").pad(unicode.safeTextFormat(workingOnBox.tex), 30, false, true) - text = { - "Box Texture Entry: " .. miText .. "/" .. mxText, - " Press Enter to confirm texture,|F3 Load", - " or paste out-of-game clipboard.|F4 Save", - "[" .. tex .. "]|F5 XYXZ" - } - end + for i = 1, 4 do + text[i] = fmttext.pad(text[i], 32, true, true) .. menu[i] end - text[1] = require("fmttext").pad(text[1], 32, true, true) .. "|F1 New " window.span(1, line, text[line - 9] or "", 0, 0xFFFFFF) end end @@ -245,6 +408,7 @@ local function reset() xyz = false cx, cy, cz = 1, 1, 1 workingOnBox = nil + programState = "none" end local function loadObj(obj) @@ -418,68 +582,9 @@ while true do cz = math.min(16, cz + 1) end refresh() - elseif c == 13 then - if not selectedBox then - if not workingOnBox then - workingOnBox = { - minX = cx, - minY = cy, - minZ = cz, - tex = "diamond_block", - rgb = 0xFFFFFF - } - elseif not workingOnBox.maxX then - workingOnBox.maxX = cx - workingOnBox.maxY = cy - workingOnBox.maxZ = cz - else - local ch = 65 - while boxes[state][string.char(ch)] do - ch = ch + 1 - end - local ax, ay, az = workingOnBox.minX, workingOnBox.minY, workingOnBox.minZ - local bx, by, bz = workingOnBox.maxX, workingOnBox.maxY, workingOnBox.maxZ - workingOnBox.minX = math.min(ax, bx) - 1 - workingOnBox.minY = math.min(ay, by) - 1 - workingOnBox.minZ = math.min(az, bz) - 1 - workingOnBox.maxX = math.max(ax, bx) - workingOnBox.maxY = math.max(ay, by) - workingOnBox.maxZ = math.max(az, bz) - selectedBox = string.char(ch) - boxes[state][selectedBox] = workingOnBox - workingOnBox = nil - end - else - selectedBox = nil - end - refresh() else - if workingOnBox then - if not workingOnBox.maxX then - if c == 8 or c == 127 then - workingOnBox = nil - end - else - if c >= 32 then - workingOnBox.tex = workingOnBox.tex .. unicode.char(c) - elseif c == 8 or c == 127 then - workingOnBox.tex = unicode.sub(workingOnBox.tex, 1, unicode.len(workingOnBox.tex) - 1) - end - end - refresh() - elseif c == 8 or c == 127 then - if selectedBox then - boxes[state][selectedBox] = nil - selectedBox = nil - refresh() - end - else - local cc = unicode.char(c):upper() - if boxes[state][cc] then - selectedBox = cc - end - refresh() - end + programStates[programState][2](c, d) + refresh() end end end diff --git a/repository/docs/ul-neoux b/repository/docs/ul-neoux index e82e73c..446c861 100644 --- a/repository/docs/ul-neoux +++ b/repository/docs/ul-neoux @@ -95,7 +95,7 @@ Main functions: demand and unloaded after use. neoux.tcwindow(w, h, controls, - closing, bg, fg[, selIndex]): + closing, bg, fg[, selIndex], [kf]): Creates a neoux.create-compatible callback for a NeoUX GUI framework window. @@ -114,6 +114,20 @@ Main functions: selIndex, if provided, is the index of the control that should start out selected. + kf, if provided, is a table that + is used for extended information: + ctrl: True when left Ctrl key + is down, nil or false when it's + up. + rctrl: True when right Ctrl key + is down, nil or false when it's + up. + shift: True when left Shift key + is down, nil or false when it's + up. + rshift: True when right Shift key + is down, nil or false when it's + up. startDialog(fmt, title, wait): Shows a text dialog. diff --git a/repository/docs/us-evrst b/repository/docs/us-evrst index 244f0b8..e6ec8f6 100644 --- a/repository/docs/us-evrst +++ b/repository/docs/us-evrst @@ -77,6 +77,33 @@ Palette advice for artists: so that they are not wasted if not used. +Notes on UI design: + +The UI design is heavily monochrome. + + core (neoux supported): +"[textheretext]": This means a text + field. It should preferably right- + align text if too much is inside, + ensuring that what is being written + can be read. + The enter button should do nothing, + and the backspace/delete buttons + should both remove the last Unicode + character. +"": This means a button. + Space or enter should activate it. + + additional: +"◢": Resize widget. Drag-dropping + should cause a resize. This + need not occur until the drop + is completed. + Standardized by Izaya. + The non-GUI way to do this is + for ctrl-arrows to resize + the window. + -- This is released into the public domain. -- No warranty is provided,