mirror of
https://github.com/20kdc/OC-KittenOS.git
synced 2024-11-23 10:58:06 +11:00
Better line editing
This commit is contained in:
parent
d2ee505316
commit
479412a5bb
@ -3,7 +3,7 @@
|
||||
return {
|
||||
["neo"] = {
|
||||
desc = "KittenOS NEO Kernel & Base Libs",
|
||||
v = 8,
|
||||
v = 9,
|
||||
deps = {
|
||||
},
|
||||
dirs = {
|
||||
@ -18,6 +18,7 @@ return {
|
||||
"libs/serial.lua",
|
||||
"libs/fmttext.lua",
|
||||
"libs/neoux.lua",
|
||||
"libs/lineedit.lua",
|
||||
"libs/braille.lua",
|
||||
"libs/bmp.lua",
|
||||
"libs/sys-filewrap.lua",
|
||||
@ -96,7 +97,7 @@ return {
|
||||
},
|
||||
["neo-coreapps"] = {
|
||||
desc = "KittenOS NEO Core Apps",
|
||||
v = 5,
|
||||
v = 9,
|
||||
deps = {
|
||||
"neo"
|
||||
},
|
||||
|
@ -226,7 +226,7 @@ window = neoux.create(currentGen())
|
||||
while running do
|
||||
local src, id, k, v = event.pull()
|
||||
if src == "x.neo.sys.manage" then
|
||||
if id == "set_setting" then
|
||||
if id == "set_setting" and currentGen ~= logGen then
|
||||
window.reset(currentGen())
|
||||
end
|
||||
end
|
||||
|
@ -30,32 +30,17 @@ local clipsrc = neo.requireAccess("x.neo.pub.globals", "clipboard")
|
||||
local windows = neo.requireAccess("x.neo.pub.window", "windows")
|
||||
local files = neo.requireAccess("x.neo.pub.base", "files").showFileDialogAsync
|
||||
|
||||
local lineEdit = require("lineedit")
|
||||
|
||||
local cursorX = 1
|
||||
local cursorY = math.ceil(#lines / 2)
|
||||
local cFlash = true
|
||||
local ctrlFlag, focFlag, appendFlag
|
||||
local ctrlFlag, appendFlag
|
||||
local dialogLock = false
|
||||
local sW, sH = 37, #lines + 2
|
||||
local window = windows(sW, sH)
|
||||
local filedialog = nil
|
||||
local flush
|
||||
|
||||
local function splitCur()
|
||||
local s = lines[cursorY]
|
||||
local st = unicode.sub(s, 1, cursorX - 1)
|
||||
local en = unicode.sub(s, cursorX)
|
||||
return st, en
|
||||
end
|
||||
|
||||
local function clampCursorX()
|
||||
local s = lines[cursorY]
|
||||
if unicode.len(s) < (cursorX - 1) then
|
||||
cursorX = unicode.len(s) + 1
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local cbs = {}
|
||||
|
||||
local function fileDialog(writing, callback)
|
||||
@ -139,31 +124,14 @@ local function getline(y)
|
||||
-- rX is difficult!
|
||||
local rX = 1
|
||||
local Xthold = math.max(1, math.floor(sW / 2) - 1)
|
||||
local _, cursorXP = unicode.safeTextFormat(lines[cursorY], cursorX)
|
||||
local cLine, cursorXP = unicode.safeTextFormat(lines[cursorY], cursorX)
|
||||
rX = (math.max(0, math.floor(cursorXP / Xthold) - 1) * Xthold) + 1
|
||||
local line = lines[rY]
|
||||
if not line then
|
||||
return ("¬"):rep(sW)
|
||||
end
|
||||
line = unicode.safeTextFormat(line)
|
||||
-- <alter RX here by 1 if needed>
|
||||
local tl = unicode.sub(line, rX, rX + sW - 1)
|
||||
cursorXP = (cursorXP - rX) + 1
|
||||
if cFlash then
|
||||
if rY == cursorY then
|
||||
if cursorXP >= 1 then
|
||||
if cursorXP <= sW then
|
||||
local start = unicode.sub(tl, 1, cursorXP - 1)
|
||||
local endx = unicode.sub(tl, cursorXP + 1)
|
||||
tl = start .. "_" .. endx
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
while unicode.len(tl) < sW do
|
||||
tl = tl .. " "
|
||||
end
|
||||
return tl
|
||||
return lineEdit.draw(sW, line, cursorXP, rY == cursorY, rX)
|
||||
end
|
||||
local function delLine()
|
||||
local contents = lines[cursorY]
|
||||
@ -179,22 +147,7 @@ local function delLine()
|
||||
end
|
||||
return contents
|
||||
end
|
||||
-- add a single character
|
||||
local function putLetter(ch)
|
||||
if ch == "\r" then
|
||||
local a, b = splitCur()
|
||||
lines[cursorY] = a
|
||||
table.insert(lines, cursorY + 1, b)
|
||||
cursorY = cursorY + 1
|
||||
cursorX = 1
|
||||
return
|
||||
end
|
||||
local a, b = splitCur()
|
||||
a = a .. ch
|
||||
lines[cursorY] = a .. b
|
||||
cursorX = unicode.len(a) + 1
|
||||
end
|
||||
local function key(ka, kc, down)
|
||||
local function key(ks, kc, down)
|
||||
if dialogLock then
|
||||
return false
|
||||
end
|
||||
@ -212,18 +165,22 @@ local function key(ka, kc, down)
|
||||
sH = 1
|
||||
end
|
||||
sW, sH = window.setSize(sW, sH)
|
||||
return false
|
||||
elseif kc == 208 then -- Down
|
||||
sH = sH + 1
|
||||
sW, sH = window.setSize(sW, sH)
|
||||
return false
|
||||
elseif kc == 203 then -- Left
|
||||
sW = sW - 1
|
||||
if sW == 0 then
|
||||
sW = 1
|
||||
end
|
||||
sW, sH = window.setSize(sW, sH)
|
||||
return false
|
||||
elseif kc == 205 then -- Right
|
||||
sW = sW + 1
|
||||
sW, sH = window.setSize(sW, sH)
|
||||
return false
|
||||
elseif kc == 14 then -- ^Backspace
|
||||
delLine()
|
||||
return true
|
||||
@ -240,7 +197,7 @@ local function key(ka, kc, down)
|
||||
if cursorY < 1 then
|
||||
cursorY = 1
|
||||
end
|
||||
clampCursorX()
|
||||
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
|
||||
return true
|
||||
elseif kc == 208 or kc == 209 then -- Go down one - go down page
|
||||
local moveAmount = 1
|
||||
@ -251,36 +208,7 @@ local function key(ka, kc, down)
|
||||
if cursorY > #lines then
|
||||
cursorY = #lines
|
||||
end
|
||||
clampCursorX()
|
||||
return true
|
||||
elseif kc == 203 then
|
||||
if cursorX > 1 then
|
||||
cursorX = cursorX - 1
|
||||
else
|
||||
if cursorY > 1 then
|
||||
cursorY = cursorY - 1
|
||||
cursorX = unicode.len(lines[cursorY]) + 1
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
elseif kc == 205 then
|
||||
cursorX = cursorX + 1
|
||||
if clampCursorX() then
|
||||
if cursorY < #lines then
|
||||
cursorY = cursorY + 1
|
||||
cursorX = 1
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
-- Extra Non-Control Keys
|
||||
if kc == 199 then
|
||||
cursorX = 1
|
||||
return true
|
||||
elseif kc == 207 then
|
||||
cursorX = unicode.len(lines[cursorY]) + 1
|
||||
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
|
||||
return true
|
||||
end
|
||||
-- Major Actions
|
||||
@ -291,10 +219,13 @@ local function key(ka, kc, down)
|
||||
return true
|
||||
elseif kc == 61 then -- F3
|
||||
startLoad()
|
||||
return false
|
||||
elseif kc == 62 then -- F4
|
||||
startSave()
|
||||
return false
|
||||
elseif kc == 63 then -- F5
|
||||
clipsrc.setSetting("clipboard", lines[cursorY])
|
||||
return false
|
||||
elseif kc == 64 then -- F6
|
||||
local tx = clipsrc.getSetting("clipboard") or ""
|
||||
local txi = tx:find("\n")
|
||||
@ -311,6 +242,7 @@ local function key(ka, kc, down)
|
||||
return true
|
||||
elseif kc == 65 then -- F7
|
||||
appendFlag = false
|
||||
return false
|
||||
elseif kc == 66 then -- F8
|
||||
if appendFlag then
|
||||
local base = clipsrc.getSetting("clipboard")
|
||||
@ -322,30 +254,34 @@ local function key(ka, kc, down)
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- Letters
|
||||
if ka == 8 or kc == 211 then
|
||||
if cursorX == 1 then
|
||||
if cursorY == 1 then
|
||||
return false
|
||||
-- LEL Keys
|
||||
local lT, lC, lX = lineEdit.key(ks, kc, lines[cursorY], cursorX)
|
||||
if lT then
|
||||
lines[cursorY] = lT
|
||||
end
|
||||
if lC then
|
||||
cursorX = lC
|
||||
end
|
||||
if lX == "l<" and cursorY > 1 then
|
||||
cursorY = cursorY - 1
|
||||
cursorX = unicode.len(lines[cursorY]) + 1
|
||||
elseif lX == "l>" and cursorY < #lines then
|
||||
cursorY = cursorY + 1
|
||||
cursorX = 1
|
||||
elseif lX == "wpl" and cursorY ~= 1 then
|
||||
local l = table.remove(lines, cursorY)
|
||||
cursorY = cursorY - 1
|
||||
cursorX = unicode.len(lines[cursorY]) + 1
|
||||
lines[cursorY] = lines[cursorY] .. l
|
||||
else
|
||||
local a, b = splitCur()
|
||||
a = unicode.sub(a, 1, unicode.len(a) - 1)
|
||||
lines[cursorY] = a.. b
|
||||
cursorX = cursorX - 1
|
||||
elseif lX == "nl" then
|
||||
local line = lines[cursorY]
|
||||
lines[cursorY] = unicode.sub(line, 1, cursorX - 1)
|
||||
table.insert(lines, cursorY + 1, unicode.sub(line, cursorX))
|
||||
cursorX = 1
|
||||
cursorY = cursorY + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
if ka ~= 0 then
|
||||
putLetter(unicode.char(ka))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
flush = function ()
|
||||
for i = 1, sH do
|
||||
@ -353,16 +289,9 @@ flush = function ()
|
||||
end
|
||||
end
|
||||
|
||||
neo.scheduleTimer(os.uptime() + 0.5)
|
||||
|
||||
while true do
|
||||
local e = {coroutine.yield()}
|
||||
if e[1] == "k.timer" and e[2] == focFlag then
|
||||
cFlash = not cFlash
|
||||
local csY = math.ceil(sH / 2)
|
||||
window.span(1, csY, getline(csY), 0xFFFFFF, 0)
|
||||
focFlag = neo.scheduleTimer(os.uptime() + 0.5)
|
||||
elseif e[1] == "x.neo.pub.window" then
|
||||
if e[1] == "x.neo.pub.window" then
|
||||
if e[2] == window.id then
|
||||
if e[3] == "line" then
|
||||
window.span(1, e[4], getline(e[4]), 0xFFFFFF, 0)
|
||||
@ -373,14 +302,13 @@ while true do
|
||||
local csY = math.ceil(sH / 2)
|
||||
local nY = math.max(1, math.min(#lines, (math.floor(e[5]) - csY) + cursorY))
|
||||
cursorY = nY
|
||||
clampCursorX()
|
||||
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
|
||||
flush()
|
||||
elseif e[3] == "key" then
|
||||
if key(e[4], e[5], e[6]) then
|
||||
if key(e[4] ~= 0 and unicode.char(e[4]), e[5], e[6]) then
|
||||
flush()
|
||||
end
|
||||
elseif e[3] == "focus" then
|
||||
focFlag = e[4] and neo.scheduleTimer(0)
|
||||
ctrlFlag = false
|
||||
elseif e[3] == "close" then
|
||||
return
|
||||
@ -392,7 +320,7 @@ while true do
|
||||
if c == "\n" then
|
||||
c = "\r"
|
||||
end
|
||||
putLetter(c)
|
||||
key(c, 0, true)
|
||||
end
|
||||
end
|
||||
flush()
|
||||
|
@ -32,6 +32,7 @@ local l15 = ""
|
||||
-- sW must not go below 3.
|
||||
-- sH must not go below 2.
|
||||
local sW, sH = 40, 15
|
||||
local cX = 1
|
||||
local windows = neo.requireAccess("x.neo.pub.window", "windows")
|
||||
local window = windows(sW, sH, title)
|
||||
|
||||
@ -42,11 +43,12 @@ local function fmtLine(s)
|
||||
end
|
||||
|
||||
local function line(i)
|
||||
local l = console[i] or l15
|
||||
l = require("lineedit").draw(sW, l, cX, i == sH)
|
||||
if i ~= sH then
|
||||
assert(console[i], "console" .. i)
|
||||
window.span(1, i, fmtLine(console[i]), 0xFFFFFF, 0)
|
||||
window.span(1, i, l, 0xFFFFFF, 0)
|
||||
else
|
||||
window.span(1, i, fmtLine(l15), 0, 0xFFFFFF)
|
||||
window.span(1, i, l, 0, 0xFFFFFF)
|
||||
end
|
||||
end
|
||||
|
||||
@ -73,22 +75,6 @@ end
|
||||
|
||||
local sendSigs = {}
|
||||
|
||||
local function submitLine()
|
||||
for _, v in pairs(sendSigs) do
|
||||
v("line", l15)
|
||||
end
|
||||
l15 = ""
|
||||
line(sH)
|
||||
end
|
||||
|
||||
local function clipEnt(tx)
|
||||
tx = tx:gsub("\r", "")
|
||||
local ci = tx:find("\n") or (#tx + 1)
|
||||
tx = tx:sub(1, ci - 1)
|
||||
l15 = l15 .. tx
|
||||
line(sH)
|
||||
end
|
||||
|
||||
do
|
||||
tReg(function (_, pid, sendSig)
|
||||
sendSigs[pid] = sendSig
|
||||
@ -112,6 +98,19 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
local function key(a, c)
|
||||
local lT, lC, lX = require("lineedit").key(a, c, l15, cX)
|
||||
l15 = lT or l15
|
||||
cX = lC or cX
|
||||
if lX == "nl" then
|
||||
for _, v in pairs(sendSigs) do
|
||||
v("line", l15)
|
||||
end
|
||||
l15 = ""
|
||||
cX = 1
|
||||
end
|
||||
end
|
||||
|
||||
local control = false
|
||||
while not closeNow do
|
||||
local e = {coroutine.yield()}
|
||||
@ -121,19 +120,22 @@ while not closeNow do
|
||||
if e[3] == "close" then
|
||||
break
|
||||
elseif e[3] == "clipboard" then
|
||||
clipEnt(e[4])
|
||||
for i = 1, unicode.len(e[4]) do
|
||||
local c = unicode.sub(e[4], i, i)
|
||||
if c ~= "\r" then
|
||||
if c == "\n" then
|
||||
c = "\r"
|
||||
end
|
||||
key(c, 0)
|
||||
end
|
||||
end
|
||||
line(sH)
|
||||
elseif e[3] == "key" then
|
||||
if e[5] == 29 or e[5] == 157 then
|
||||
control = e[6]
|
||||
elseif e[6] then
|
||||
if not control then
|
||||
if e[4] == 8 or e[4] == 127 then
|
||||
l15 = unicode.sub(l15, 1, -2)
|
||||
elseif e[4] == 13 then
|
||||
submitLine()
|
||||
elseif e[4] >= 32 then
|
||||
l15 = l15 .. unicode.char(e[4])
|
||||
end
|
||||
key(e[4] ~= 0 and unicode.char(e[4]), e[5])
|
||||
line(sH)
|
||||
elseif e[5] == 203 and sW > 8 then
|
||||
sW = sW - 1
|
||||
|
69
code/libs/lineedit.lua
Normal file
69
code/libs/lineedit.lua
Normal file
@ -0,0 +1,69 @@
|
||||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
return {
|
||||
-- note: everything must already be unicode.safeTextFormat'd
|
||||
draw = function (sW, line, cursorX, cursorOn, rX)
|
||||
-- if no camera, provide a default
|
||||
rX = rX or math.max(1, cursorX - math.floor(sW * 2 / 3))
|
||||
-- transform into area-relative
|
||||
local tl = unicode.sub(line, rX, rX + sW - 1)
|
||||
-- inject cursor
|
||||
if cursorOn then
|
||||
cursorX = (cursorX - rX) + 1
|
||||
if cursorX >= 1 then
|
||||
if cursorX <= sW then
|
||||
tl = unicode.sub(tl, 1, cursorX - 1) .. "_" .. unicode.sub(tl, cursorX + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
return tl .. (" "):rep(sW - unicode.len(tl))
|
||||
end,
|
||||
clamp = function (tl, cursorX)
|
||||
tl = unicode.len(tl)
|
||||
if tl < cursorX - 1 then
|
||||
return tl + 1
|
||||
end
|
||||
return cursorX
|
||||
end,
|
||||
-- returns line, cursorX, special
|
||||
-- return values may be nil if irrelevant
|
||||
key = function (ks, kc, line, cursorX)
|
||||
local cS = unicode.sub(line, 1, cursorX - 1)
|
||||
local cE = unicode.sub(line, cursorX)
|
||||
if kc == 203 then -- navi <
|
||||
if cursorX > 1 then
|
||||
return nil, cursorX - 1
|
||||
else
|
||||
-- cline underflow
|
||||
return nil, nil, "l<"
|
||||
end
|
||||
elseif kc == 205 then -- navi >
|
||||
local ll = unicode.len(line)
|
||||
if cursorX > ll then
|
||||
-- cline overflow
|
||||
return nil, nil, "l>"
|
||||
end
|
||||
return nil, cursorX + 1
|
||||
elseif kc == 199 then -- home
|
||||
return nil, 1
|
||||
elseif kc == 207 then -- end
|
||||
return nil, unicode.len(line) + 1
|
||||
elseif ks == "\8" or kc == 211 then -- del
|
||||
if cursorX == 1 then
|
||||
-- weld prev line
|
||||
return nil, nil, "wpl"
|
||||
else
|
||||
cS = unicode.sub(cS, 1, unicode.len(cS) - 1)
|
||||
return cS .. cE, cursorX - 1
|
||||
end
|
||||
elseif ks then -- standard letters
|
||||
if ks == "\r" then
|
||||
-- new line
|
||||
return nil, nil, "nl"
|
||||
end
|
||||
return cS .. ks .. cE, cursorX + unicode.len(ks)
|
||||
end
|
||||
-- :(
|
||||
end
|
||||
}
|
@ -339,6 +339,7 @@ newNeoux = function (event, neo)
|
||||
end
|
||||
-- Note: w should be at least 2 - this is similar to buttons.
|
||||
neoux.tcfield = function (x, y, w, textprop)
|
||||
local p = 1
|
||||
return {
|
||||
x = x,
|
||||
y = y,
|
||||
@ -347,24 +348,26 @@ newNeoux = function (event, neo)
|
||||
selectable = true,
|
||||
key = function (window, update, a, c, d, f)
|
||||
if d then
|
||||
local ot = textprop()
|
||||
local le = require("lineedit")
|
||||
p = le.clamp(ot, p)
|
||||
if c == 63 then
|
||||
neo.requireAccess("x.neo.pub.globals", "clipboard").setSetting("clipboard", textprop())
|
||||
neo.requireAccess("x.neo.pub.globals", "clipboard").setSetting("clipboard", ot)
|
||||
elseif c == 64 then
|
||||
local contents = neo.requireAccess("x.neo.pub.globals", "clipboard").getSetting("clipboard")
|
||||
contents = contents:match("^[^\r\n]*")
|
||||
textprop(contents)
|
||||
update()
|
||||
elseif a == 8 then
|
||||
local str = textprop()
|
||||
textprop(unicode.sub(str, 1, unicode.len(str) - 1))
|
||||
update()
|
||||
return true
|
||||
elseif a >= 32 then
|
||||
textprop(textprop() .. unicode.char(a))
|
||||
elseif a ~= 9 then
|
||||
local lT, lC, lX = le.key(a ~= 0 and unicode.char(a), c, ot, p)
|
||||
if lT or lC then
|
||||
if lT then textprop(lT) end
|
||||
p = lC or p
|
||||
update()
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
clipboard = function (window, update, contents)
|
||||
contents = contents:match("^[^\r\n]*")
|
||||
@ -377,9 +380,10 @@ newNeoux = function (event, neo)
|
||||
fg = bg
|
||||
bg = fg1
|
||||
end
|
||||
local text = unicode.safeTextFormat(textprop())
|
||||
text = "[" .. neoux.pad(text, w - 2, false, true, true) .. "]"
|
||||
window.span(x, y, text, bg, fg)
|
||||
local t, e, r = textprop(), require("lineedit")
|
||||
p = e.clamp(t, p)
|
||||
t, r = unicode.safeTextFormat(t, p)
|
||||
window.span(x, y, "[" .. e.draw(w - 2, t, r, selected) .. "]", bg, fg)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user