support cursor movement in EditFields

develop
myk002 2022-07-15 22:22:51 -07:00
parent 585f6aad33
commit 27425e47f6
No known key found for this signature in database
GPG Key ID: 8A39CA0FA0C16E78
2 changed files with 72 additions and 12 deletions

@ -3904,6 +3904,13 @@ and then call the ``on_submit`` callback. Pressing the Escape key will also
release keyboard focus, but first it will restore the text that was displayed release keyboard focus, but first it will restore the text that was displayed
before the ``EditField`` gained focus and then call the ``on_change`` callback. before the ``EditField`` gained focus and then call the ``on_change`` callback.
The ``EditField`` cursor can be moved to where you want to insert/remove text.
The following cursor movement keys are recognized:
- Left/Right arrow: move the cursor one character to the left or right.
- Ctrl-Left/Right arrow: move the cursor one word to the left or right.
- Alt-Left/Right arrow: move the cursor to the beginning/end of the text.
Label class Label class
----------- -----------

@ -197,6 +197,8 @@ function EditField:init()
self:setFocus(true) self:setFocus(true)
end end
self.cursor = 1
self:addviews{HotkeyLabel{frame={t=0,l=0}, self:addviews{HotkeyLabel{frame={t=0,l=0},
key=self.key, key=self.key,
key_sep=self.key_sep, key_sep=self.key_sep,
@ -208,6 +210,19 @@ function EditField:getPreferredFocusState()
return not self.key return not self.key
end end
function EditField:setCursor(cursor)
if not cursor or cursor > #self.text then
self.cursor = #self.text + 1
return
end
self.cursor = math.max(1, cursor)
end
function EditField:setText(text, cursor)
self.text = text
self:setCursor(cursor)
end
function EditField:postUpdateLayout() function EditField:postUpdateLayout()
self.text_offset = self.subviews[1]:getTextWidth() self.text_offset = self.subviews[1]:getTextWidth()
end end
@ -215,14 +230,29 @@ end
function EditField:onRenderBody(dc) function EditField:onRenderBody(dc)
dc:pen(self.text_pen or COLOR_LIGHTCYAN):fill(0,0,dc.width-1,0) dc:pen(self.text_pen or COLOR_LIGHTCYAN):fill(0,0,dc.width-1,0)
local cursor = '_' local cursor_char = '_'
if not self.active or not self.focus or gui.blink_visible(300) then if not self.active or not self.focus or gui.blink_visible(300) then
cursor = ' ' cursor_char = (self.cursor > #self.text) and ' ' or
self.text:sub(self.cursor, self.cursor)
end end
local txt = self.text .. cursor local txt = self.text:sub(1, self.cursor - 1) .. cursor_char ..
self.text:sub(self.cursor + 1)
local max_width = dc.width - self.text_offset local max_width = dc.width - self.text_offset
if #txt > max_width then if #txt > max_width then
txt = string.char(27)..string.sub(txt, #txt-max_width+2) -- get the substring in the vicinity of the cursor
max_width = max_width - 2
local half_width = math.floor(max_width/2)
local start_pos = math.max(1, self.cursor-half_width)
local end_pos = math.min(#txt, self.cursor+half_width-1)
if self.cursor + half_width > #txt then
start_pos = #txt - max_width
end
if self.cursor - half_width <= 1 then
end_pos = max_width + 1
end
txt = ('%s%s%s'):format(start_pos == 1 and '' or string.char(27),
txt:sub(start_pos, end_pos),
end_pos == #txt and '' or string.char(26))
end end
dc:advance(self.text_offset):string(txt) dc:advance(self.text_offset):string(txt)
end end
@ -252,9 +282,7 @@ function EditField:onInput(keys)
return true return true
end end
return not not self.key return not not self.key
end elseif keys.SEC_SELECT then
if keys.SEC_SELECT then
if self.key then if self.key then
self:setFocus(false) self:setFocus(false)
end end
@ -263,17 +291,42 @@ function EditField:onInput(keys)
return true return true
end end
return not not self.key return not not self.key
end elseif keys.CURSOR_LEFT then
self.cursor = math.max(1, self.cursor - 1)
if keys._STRING then return true
elseif keys.A_MOVE_W_DOWN then -- Ctrl-Left (prev word start)
local _, prev_word_start = self.text:sub(1, self.cursor-1):
find('.*[^%w_%-]+[%w_%-]')
self.cursor = prev_word_start or 1
return true
elseif keys.A_CARE_MOVE_W then -- Alt-Left (home)
self.cursor = 1
return true
elseif keys.CURSOR_RIGHT then
self.cursor = math.min(self.cursor + 1, #self.text + 1)
return true
elseif keys.A_MOVE_E_DOWN then -- Ctrl-Right (next word end)
local _, next_word_end = self.text:find('[%w_%-]+[^%w_%-]', self.cursor)
self.cursor = next_word_end or #self.text + 1
return true
elseif keys.A_CARE_MOVE_E then -- Alt-Right (end)
self.cursor = #self.text + 1
return true
elseif keys._STRING then
local old = self.text local old = self.text
if keys._STRING == 0 then if keys._STRING == 0 then
-- handle backspace -- handle backspace
self.text = string.sub(old, 1, #old-1) local del_pos = self.cursor - 1
if del_pos > 0 then
self.text = old:sub(1, del_pos-1) .. old:sub(del_pos+1)
self.cursor = del_pos
end
else else
local cv = string.char(keys._STRING) local cv = string.char(keys._STRING)
if not self.on_char or self.on_char(cv, old) then if not self.on_char or self.on_char(cv, old) then
self.text = old .. cv self.text = old:sub(1, self.cursor-1) .. cv ..
old:sub(self.cursor)
self.cursor = self.cursor + 1
end end
end end
if self.on_change and self.text ~= old then if self.on_change and self.text ~= old then