2018-01-24 22:39:38 +00:00
|
|
|
local class = require('class')
|
|
|
|
|
|
|
|
local os = _G.os
|
|
|
|
|
|
|
|
local Entry = class()
|
|
|
|
|
|
|
|
function Entry:init(args)
|
|
|
|
self.pos = 0
|
|
|
|
self.scroll = 0
|
|
|
|
self.value = ''
|
2019-03-29 13:56:56 +00:00
|
|
|
self.width = args.width or 256
|
|
|
|
self.limit = args.limit or 1024
|
|
|
|
self.mark = { }
|
|
|
|
self.offset = args.offset or 1
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:reset()
|
|
|
|
self.pos = 0
|
|
|
|
self.scroll = 0
|
|
|
|
self.value = ''
|
2019-03-29 13:56:56 +00:00
|
|
|
self.mark = { }
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:nextWord()
|
|
|
|
return select(2, self.value:find("[%s%p]?%w[%s%p]", self.pos + 1)) or #self.value
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:prevWord()
|
|
|
|
local x = #self.value - (self.pos - 1)
|
|
|
|
local _, n = self.value:reverse():find("[%s%p]?%w[%s%p]", x)
|
|
|
|
return n and #self.value - n + 1 or 0
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
function Entry:updateScroll()
|
2019-03-29 13:56:56 +00:00
|
|
|
local ps = self.scroll
|
|
|
|
if self.pos > #self.value then
|
|
|
|
self.pos = #self.value
|
|
|
|
self.scroll = 0 -- ??
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
if self.pos - self.scroll > self.width then
|
2019-03-29 13:56:56 +00:00
|
|
|
self.scroll = self.pos - self.width
|
2018-01-24 22:39:38 +00:00
|
|
|
elseif self.pos < self.scroll then
|
|
|
|
self.scroll = self.pos
|
|
|
|
end
|
2019-03-29 13:56:56 +00:00
|
|
|
if ps ~= self.scroll then
|
|
|
|
self.textChanged = true
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:copyText(cx, ex)
|
|
|
|
return self.value:sub(cx + 1, ex)
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:insertText(x, text)
|
|
|
|
if #self.value + #text > self.limit then
|
|
|
|
text = text:sub(1, self.limit-#self.value)
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
2019-03-29 13:56:56 +00:00
|
|
|
self.value = self.value:sub(1, x) .. text .. self.value:sub(x + 1)
|
|
|
|
self.pos = self.pos + #text
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:deleteText(sx, ex)
|
|
|
|
local front = self.value:sub(1, sx)
|
|
|
|
local back = self.value:sub(ex + 1, #self.value)
|
|
|
|
self.value = front .. back
|
|
|
|
self.pos = sx
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:moveLeft()
|
|
|
|
if self.pos > 0 then
|
|
|
|
self.pos = self.pos - 1
|
2019-03-28 11:29:01 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:moveRight()
|
|
|
|
if self.pos < #self.value then
|
|
|
|
self.pos = self.pos + 1
|
2019-03-28 11:29:01 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:moveHome()
|
|
|
|
if self.pos ~= 0 then
|
|
|
|
self.pos = 0
|
2019-03-28 11:29:01 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:moveEnd()
|
|
|
|
if self.pos ~= #self.value then
|
|
|
|
self.pos = #self.value
|
2019-03-28 11:29:01 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:moveTo(ie)
|
|
|
|
self.pos = math.max(0, math.min(ie.x + self.scroll - self.offset, #self.value))
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:backspace()
|
|
|
|
if self.mark.active then
|
|
|
|
self:delete()
|
|
|
|
elseif self:moveLeft() then
|
|
|
|
self:delete()
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:moveWordRight()
|
|
|
|
if self.pos < #self.value then
|
|
|
|
self.pos = self:nextWord(self.value, self.pos + 1)
|
2019-03-28 11:29:01 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:moveWordLeft()
|
|
|
|
if self.pos > 0 then
|
|
|
|
self.pos = self:prevWord(self.value, self.pos - 1) or 0
|
2019-03-28 11:29:01 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:delete()
|
|
|
|
if self.mark.active then
|
|
|
|
self:deleteText(self.mark.x, self.mark.ex)
|
|
|
|
elseif self.pos < #self.value then
|
|
|
|
self:deleteText(self.pos, self.pos + 1)
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:cutFromStart()
|
|
|
|
if self.pos > 0 then
|
|
|
|
local text = self:copyText(1, self.pos)
|
|
|
|
self:deleteText(1, self.pos)
|
|
|
|
os.queueEvent('clipboard_copy', text)
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:cutToEnd()
|
|
|
|
if self.pos < #self.value then
|
|
|
|
local text = self:copyText(self.pos, #self.value)
|
|
|
|
self:deleteText(self.pos, #self.value)
|
|
|
|
os.queueEvent('clipboard_copy', text)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:cutNextWord()
|
|
|
|
if self.pos < #self.value then
|
|
|
|
local ex = self:nextWord(self.value, self.pos)
|
|
|
|
local text = self:copyText(self.pos, ex)
|
|
|
|
self:deleteText(self.pos, ex)
|
|
|
|
os.queueEvent('clipboard_copy', text)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:cutPrevWord()
|
|
|
|
if self.pos > 0 then
|
|
|
|
local sx = self:prevWord(self.value, self.pos)
|
|
|
|
local text = self:copyText(sx, self.pos)
|
|
|
|
self:deleteText(sx, self.pos)
|
|
|
|
os.queueEvent('clipboard_copy', text)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:insertChar(ie)
|
|
|
|
if self.mark.active then
|
|
|
|
self:delete()
|
|
|
|
end
|
|
|
|
self:insertText(self.pos, ie.ch)
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:copy()
|
|
|
|
if #self.value > 0 then
|
|
|
|
self.mark.continue = true
|
|
|
|
if self.mark.active then
|
|
|
|
self:copyMarked()
|
|
|
|
else
|
|
|
|
os.queueEvent('clipboard_copy', self.value)
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:cut()
|
|
|
|
if self.mark.active then
|
|
|
|
self:copyMarked()
|
|
|
|
self:delete()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:copyMarked()
|
|
|
|
local text = self:copyText(self.mark.x, self.mark.ex)
|
|
|
|
os.queueEvent('clipboard_copy', text)
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:paste(ie)
|
|
|
|
if #ie.text > 0 then
|
|
|
|
if self.mark.active then
|
|
|
|
self:delete()
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
2019-03-29 13:56:56 +00:00
|
|
|
self:insertText(self.pos, ie.text)
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:clearLine()
|
|
|
|
if #self.value > 0 then
|
|
|
|
self:reset()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:markBegin()
|
|
|
|
if not self.mark.active then
|
|
|
|
self.mark.active = true
|
|
|
|
self.mark.anchor = { x = self.pos }
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:markFinish()
|
|
|
|
if self.pos == self.mark.anchor.x then
|
|
|
|
self.mark.active = false
|
|
|
|
else
|
|
|
|
self.mark.x = math.min(self.mark.anchor.x, self.pos)
|
|
|
|
self.mark.ex = math.max(self.mark.anchor.x, self.pos)
|
|
|
|
end
|
|
|
|
self.textChanged = true
|
|
|
|
self.mark.continue = self.mark.active
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:unmark()
|
|
|
|
if self.mark.active then
|
|
|
|
self.textChanged = true
|
|
|
|
self.mark.active = false
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:markAnchor(ie)
|
|
|
|
self:unmark()
|
|
|
|
self:moveTo(ie)
|
|
|
|
self:markBegin()
|
|
|
|
self:markFinish()
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:markLeft()
|
|
|
|
self:markBegin()
|
|
|
|
if self:moveLeft() then
|
|
|
|
self:markFinish()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:markRight()
|
|
|
|
self:markBegin()
|
|
|
|
if self:moveRight() then
|
|
|
|
self:markFinish()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:markWord(ie)
|
|
|
|
local index = 1
|
|
|
|
self:moveTo(ie)
|
|
|
|
while true do
|
|
|
|
local s, e = self.value:find('%w+', index)
|
|
|
|
if not s or s - 1 > self.pos then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
if self.pos >= s - 1 and self.pos < e then
|
|
|
|
self.pos = s - 1
|
|
|
|
self:markBegin()
|
|
|
|
self.pos = e
|
|
|
|
self:markFinish()
|
|
|
|
self:moveTo(ie)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
index = e + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:markNextWord()
|
|
|
|
self:markBegin()
|
|
|
|
if self:moveWordRight() then
|
|
|
|
self:markFinish()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:markPrevWord()
|
|
|
|
self:markBegin()
|
|
|
|
if self:moveWordLeft() then
|
|
|
|
self:markFinish()
|
2019-03-28 11:29:01 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
function Entry:markAll()
|
|
|
|
if #self.value > 0 then
|
|
|
|
self.mark.anchor = { x = 1 }
|
|
|
|
self.mark.active = true
|
|
|
|
self.mark.continue = true
|
|
|
|
self.mark.x = 0
|
|
|
|
self.mark.ex = #self.value
|
|
|
|
self.textChanged = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:markHome()
|
|
|
|
self:markBegin()
|
|
|
|
if self:moveHome() then
|
|
|
|
self:markFinish()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:markEnd()
|
|
|
|
self:markBegin()
|
|
|
|
if self:moveEnd() then
|
|
|
|
self:markFinish()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Entry:markTo(ie)
|
|
|
|
self:markBegin()
|
|
|
|
self:moveTo(ie)
|
|
|
|
self:markFinish()
|
|
|
|
end
|
|
|
|
|
2019-03-28 11:29:01 +00:00
|
|
|
local mappings = {
|
2019-03-29 13:56:56 +00:00
|
|
|
[ 'left' ] = Entry.moveLeft,
|
|
|
|
[ 'control-b' ] = Entry.moveLeft,
|
|
|
|
[ 'right' ] = Entry.moveRight,
|
|
|
|
[ 'control-f' ] = Entry.moveRight,
|
|
|
|
[ 'home' ] = Entry.moveHome,
|
|
|
|
[ 'end' ] = Entry.moveEnd,
|
|
|
|
[ 'control-e' ] = Entry.moveEnd,
|
|
|
|
[ 'mouse_click' ] = Entry.moveTo,
|
|
|
|
[ 'control-right' ] = Entry.moveWordRight,
|
|
|
|
[ 'alt-f' ] = Entry.moveWordRight,
|
|
|
|
[ 'control-left' ] = Entry.moveWordLeft,
|
|
|
|
[ 'alt-b' ] = Entry.moveWordLeft,
|
2019-03-29 19:47:01 +00:00
|
|
|
|
|
|
|
[ 'backspace' ] = Entry.backspace,
|
2019-03-29 13:56:56 +00:00
|
|
|
[ 'delete' ] = Entry.delete,
|
2019-03-29 19:47:01 +00:00
|
|
|
[ 'char' ] = Entry.insertChar,
|
|
|
|
[ 'mouse_rightclick' ] = Entry.clearLine,
|
|
|
|
|
|
|
|
[ 'control-c' ] = Entry.copy,
|
2019-03-29 13:56:56 +00:00
|
|
|
[ 'control-u' ] = Entry.cutFromStart,
|
|
|
|
[ 'control-k' ] = Entry.cutToEnd,
|
|
|
|
[ 'control-w' ] = Entry.cutPrevWord,
|
2019-03-29 19:47:01 +00:00
|
|
|
--[ 'control-d' ] = Entry.cutNextWord,
|
2019-03-29 13:56:56 +00:00
|
|
|
[ 'control-x' ] = Entry.cut,
|
|
|
|
[ 'paste' ] = Entry.paste,
|
2019-04-22 03:41:17 +00:00
|
|
|
-- [ 'control-y' ] = Entry.paste, -- well this won't work...
|
2019-03-29 13:56:56 +00:00
|
|
|
|
2019-03-29 19:47:01 +00:00
|
|
|
[ 'mouse_doubleclick' ] = Entry.markWord,
|
2019-03-29 13:56:56 +00:00
|
|
|
[ 'shift-left' ] = Entry.markLeft,
|
|
|
|
[ 'shift-right' ] = Entry.markRight,
|
|
|
|
[ 'mouse_down' ] = Entry.markAnchor,
|
|
|
|
[ 'mouse_drag' ] = Entry.markTo,
|
|
|
|
[ 'shift-mouse_click' ] = Entry.markTo,
|
|
|
|
[ 'control-a' ] = Entry.markAll,
|
|
|
|
[ 'control-shift-right' ] = Entry.markNextWord,
|
|
|
|
[ 'control-shift-left' ] = Entry.markPrevWord,
|
|
|
|
[ 'shift-end' ] = Entry.markEnd,
|
|
|
|
[ 'shift-home' ] = Entry.markHome,
|
2019-03-28 11:29:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function Entry:process(ie)
|
|
|
|
local action = mappings[ie.code]
|
2019-03-29 13:56:56 +00:00
|
|
|
|
|
|
|
self.textChanged = false
|
2019-03-29 14:45:43 +00:00
|
|
|
|
2019-03-28 11:29:01 +00:00
|
|
|
if action then
|
2019-03-29 13:56:56 +00:00
|
|
|
local pos = self.pos
|
|
|
|
local line = self.value
|
|
|
|
|
|
|
|
local wasMarking = self.mark.continue
|
|
|
|
self.mark.continue = false
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
action(self, ie)
|
2018-01-24 22:39:38 +00:00
|
|
|
|
2019-03-29 13:56:56 +00:00
|
|
|
self.textChanged = self.textChanged or self.value ~= line
|
|
|
|
self.posChanged = pos ~= self.pos
|
|
|
|
self:updateScroll()
|
|
|
|
|
|
|
|
if not self.mark.continue and wasMarking then
|
|
|
|
self:unmark()
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return Entry
|