fix early wrap for multi-line input elements
This commit is contained in:
@@ -4042,11 +4042,27 @@ function Element:moveCursorToPreviousWord()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Helper function to get character at position
|
||||||
|
local function getCharAt(p)
|
||||||
|
if p < 0 or p >= utf8.len(text) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local offset1 = utf8.offset(text, p + 1)
|
||||||
|
local offset2 = utf8.offset(text, p + 2)
|
||||||
|
if not offset1 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if not offset2 then
|
||||||
|
-- Last character in string
|
||||||
|
return text:sub(offset1)
|
||||||
|
end
|
||||||
|
return text:sub(offset1, offset2 - 1)
|
||||||
|
end
|
||||||
|
|
||||||
-- Skip any whitespace/punctuation before current position
|
-- Skip any whitespace/punctuation before current position
|
||||||
while pos > 0 do
|
while pos > 0 do
|
||||||
local offset = utf8.offset(text, pos + 1)
|
local char = getCharAt(pos - 1)
|
||||||
local char = offset and text:sub(offset, utf8.offset(text, pos + 2) - 1) or ""
|
if char and char:match("[%w]") then
|
||||||
if char:match("[%w]") then
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
pos = pos - 1
|
pos = pos - 1
|
||||||
@@ -4054,11 +4070,8 @@ function Element:moveCursorToPreviousWord()
|
|||||||
|
|
||||||
-- Move to start of current word
|
-- Move to start of current word
|
||||||
while pos > 0 do
|
while pos > 0 do
|
||||||
local offset = utf8.offset(text, pos + 1)
|
local char = getCharAt(pos - 1)
|
||||||
local char = offset and text:sub(offset, utf8.offset(text, pos + 2) - 1) or ""
|
if not char or not char:match("[%w]") then
|
||||||
if not char:match("[%w]") then
|
|
||||||
-- We've moved one position past the start, so move back
|
|
||||||
pos = pos + 1
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
pos = pos - 1
|
pos = pos - 1
|
||||||
@@ -4082,11 +4095,27 @@ function Element:moveCursorToNextWord()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Helper function to get character at position
|
||||||
|
local function getCharAt(p)
|
||||||
|
if p < 0 or p >= textLength then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local offset1 = utf8.offset(text, p + 1)
|
||||||
|
local offset2 = utf8.offset(text, p + 2)
|
||||||
|
if not offset1 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if not offset2 then
|
||||||
|
-- Last character in string
|
||||||
|
return text:sub(offset1)
|
||||||
|
end
|
||||||
|
return text:sub(offset1, offset2 - 1)
|
||||||
|
end
|
||||||
|
|
||||||
-- Skip current word
|
-- Skip current word
|
||||||
while pos < textLength do
|
while pos < textLength do
|
||||||
local offset = utf8.offset(text, pos + 1)
|
local char = getCharAt(pos)
|
||||||
local char = offset and text:sub(offset, utf8.offset(text, pos + 2) - 1) or ""
|
if not char or not char:match("[%w]") then
|
||||||
if not char:match("[%w]") then
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
pos = pos + 1
|
pos = pos + 1
|
||||||
@@ -4094,9 +4123,8 @@ function Element:moveCursorToNextWord()
|
|||||||
|
|
||||||
-- Skip any whitespace/punctuation
|
-- Skip any whitespace/punctuation
|
||||||
while pos < textLength do
|
while pos < textLength do
|
||||||
local offset = utf8.offset(text, pos + 1)
|
local char = getCharAt(pos)
|
||||||
local char = offset and text:sub(offset, utf8.offset(text, pos + 2) - 1) or ""
|
if char and char:match("[%w]") then
|
||||||
if char:match("[%w]") then
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
pos = pos + 1
|
pos = pos + 1
|
||||||
@@ -4597,33 +4625,68 @@ function Element:_wrapLine(line, maxWidth)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.textWrap == "word" then
|
if self.textWrap == "word" then
|
||||||
local words = {}
|
-- Tokenize into words and whitespace, preserving exact spacing
|
||||||
for word in line:gmatch("%S+") do
|
local tokens = {}
|
||||||
table.insert(words, word)
|
local pos = 1
|
||||||
|
local lineLen = utf8.len(line)
|
||||||
|
|
||||||
|
while pos <= lineLen do
|
||||||
|
-- Check if current position is whitespace
|
||||||
|
local char = getUtf8Char(line, pos)
|
||||||
|
if char:match("%s") then
|
||||||
|
-- Collect whitespace sequence
|
||||||
|
local wsStart = pos
|
||||||
|
while pos <= lineLen and getUtf8Char(line, pos):match("%s") do
|
||||||
|
pos = pos + 1
|
||||||
|
end
|
||||||
|
table.insert(tokens, {
|
||||||
|
type = "space",
|
||||||
|
text = line:sub(utf8.offset(line, wsStart), utf8.offset(line, pos) and utf8.offset(line, pos) - 1 or #line),
|
||||||
|
startPos = wsStart - 1,
|
||||||
|
length = pos - wsStart
|
||||||
|
})
|
||||||
|
else
|
||||||
|
-- Collect word (non-whitespace sequence)
|
||||||
|
local wordStart = pos
|
||||||
|
while pos <= lineLen and not getUtf8Char(line, pos):match("%s") do
|
||||||
|
pos = pos + 1
|
||||||
|
end
|
||||||
|
table.insert(tokens, {
|
||||||
|
type = "word",
|
||||||
|
text = line:sub(utf8.offset(line, wordStart), utf8.offset(line, pos) and utf8.offset(line, pos) - 1 or #line),
|
||||||
|
startPos = wordStart - 1,
|
||||||
|
length = pos - wordStart
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for i, word in ipairs(words) do
|
-- Process tokens and wrap
|
||||||
local testLine = currentLine == "" and word or (currentLine .. " " .. word)
|
local charPos = 0 -- Track our position in the original line
|
||||||
|
for i, token in ipairs(tokens) do
|
||||||
|
if token.type == "word" then
|
||||||
|
local testLine = currentLine .. token.text
|
||||||
local width = font:getWidth(testLine)
|
local width = font:getWidth(testLine)
|
||||||
|
|
||||||
if width > maxWidth and currentLine ~= "" then
|
if width > maxWidth and currentLine ~= "" then
|
||||||
|
-- Current line is full, wrap before this word
|
||||||
local currentLineLen = utf8.len(currentLine)
|
local currentLineLen = utf8.len(currentLine)
|
||||||
table.insert(wrappedParts, {
|
table.insert(wrappedParts, {
|
||||||
text = currentLine,
|
text = currentLine,
|
||||||
startIdx = startIdx,
|
startIdx = startIdx,
|
||||||
endIdx = startIdx + currentLineLen,
|
endIdx = startIdx + currentLineLen,
|
||||||
})
|
})
|
||||||
startIdx = startIdx + currentLineLen + 1 -- +1 for the space
|
startIdx = charPos
|
||||||
currentLine = word
|
currentLine = token.text
|
||||||
|
charPos = charPos + token.length
|
||||||
|
|
||||||
-- Check if the word itself is too long - if so, break it with character wrapping
|
-- Check if the word itself is too long - if so, break it with character wrapping
|
||||||
if font:getWidth(word) > maxWidth then
|
if font:getWidth(token.text) > maxWidth then
|
||||||
local wordLen = utf8.len(word)
|
local wordLen = utf8.len(token.text)
|
||||||
local charLine = ""
|
local charLine = ""
|
||||||
local charStartIdx = startIdx
|
local charStartIdx = startIdx
|
||||||
|
|
||||||
for j = 1, wordLen do
|
for j = 1, wordLen do
|
||||||
local char = getUtf8Char(word, j)
|
local char = getUtf8Char(token.text, j)
|
||||||
local testCharLine = charLine .. char
|
local testCharLine = charLine .. char
|
||||||
local charWidth = font:getWidth(testCharLine)
|
local charWidth = font:getWidth(testCharLine)
|
||||||
|
|
||||||
@@ -4645,12 +4708,12 @@ function Element:_wrapLine(line, maxWidth)
|
|||||||
end
|
end
|
||||||
elseif width > maxWidth and currentLine == "" then
|
elseif width > maxWidth and currentLine == "" then
|
||||||
-- Word is too long to fit on a line by itself - use character wrapping
|
-- Word is too long to fit on a line by itself - use character wrapping
|
||||||
local wordLen = utf8.len(word)
|
local wordLen = utf8.len(token.text)
|
||||||
local charLine = ""
|
local charLine = ""
|
||||||
local charStartIdx = startIdx
|
local charStartIdx = startIdx
|
||||||
|
|
||||||
for j = 1, wordLen do
|
for j = 1, wordLen do
|
||||||
local char = getUtf8Char(word, j)
|
local char = getUtf8Char(token.text, j)
|
||||||
local testCharLine = charLine .. char
|
local testCharLine = charLine .. char
|
||||||
local charWidth = font:getWidth(testCharLine)
|
local charWidth = font:getWidth(testCharLine)
|
||||||
|
|
||||||
@@ -4669,8 +4732,15 @@ function Element:_wrapLine(line, maxWidth)
|
|||||||
|
|
||||||
currentLine = charLine
|
currentLine = charLine
|
||||||
startIdx = charStartIdx
|
startIdx = charStartIdx
|
||||||
|
charPos = charPos + token.length
|
||||||
else
|
else
|
||||||
currentLine = testLine
|
currentLine = testLine
|
||||||
|
charPos = charPos + token.length
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- It's whitespace - add to current line
|
||||||
|
currentLine = currentLine .. token.text
|
||||||
|
charPos = charPos + token.length
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user