removed redundant methods
This commit is contained in:
@@ -2459,59 +2459,6 @@ function Element:replaceText(startPos, endPos, newText)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Split text into lines (for multi-line text)
|
|
||||||
function Element:_splitLines()
|
|
||||||
if not self.editable then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.multiline then
|
|
||||||
self._lines = { self._textBuffer or "" }
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self._lines = {}
|
|
||||||
local text = self._textBuffer or ""
|
|
||||||
|
|
||||||
-- Split on newlines
|
|
||||||
for line in (text .. "\n"):gmatch("([^\n]*)\n") do
|
|
||||||
table.insert(self._lines, line)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Ensure at least one line
|
|
||||||
if #self._lines == 0 then
|
|
||||||
self._lines = { "" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Calculate text wrapping
|
|
||||||
function Element:_calculateWrapping()
|
|
||||||
if not self.editable or not self.textWrap then
|
|
||||||
self._wrappedLines = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self._wrappedLines = {}
|
|
||||||
local availableWidth = self.width - self.padding.left - self.padding.right
|
|
||||||
|
|
||||||
for lineNum, line in ipairs(self._lines or {}) do
|
|
||||||
if line == "" then
|
|
||||||
table.insert(self._wrappedLines, {
|
|
||||||
text = "",
|
|
||||||
startIdx = 0,
|
|
||||||
endIdx = 0,
|
|
||||||
lineNum = lineNum,
|
|
||||||
})
|
|
||||||
else
|
|
||||||
local wrappedParts = self:_wrapLine(line, availableWidth)
|
|
||||||
for _, part in ipairs(wrappedParts) do
|
|
||||||
part.lineNum = lineNum
|
|
||||||
table.insert(self._wrappedLines, part)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Wrap a single line of text
|
--- Wrap a single line of text
|
||||||
---@param line string -- Line to wrap
|
---@param line string -- Line to wrap
|
||||||
---@param maxWidth number -- Maximum width in pixels
|
---@param maxWidth number -- Maximum width in pixels
|
||||||
@@ -2529,154 +2476,6 @@ end
|
|||||||
-- Input Handling - Mouse Selection
|
-- Input Handling - Mouse Selection
|
||||||
-- ====================
|
-- ====================
|
||||||
|
|
||||||
--- Convert mouse coordinates to cursor position in text
|
|
||||||
---@param mouseX number -- Mouse X coordinate (absolute)
|
|
||||||
---@param mouseY number -- Mouse Y coordinate (absolute)
|
|
||||||
---@return number -- Cursor position (character index)
|
|
||||||
function Element:_mouseToTextPosition(mouseX, mouseY)
|
|
||||||
if not self.editable or not self._textBuffer then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get content area bounds
|
|
||||||
local contentX = (self._absoluteX or self.x) + self.padding.left
|
|
||||||
local contentY = (self._absoluteY or self.y) + self.padding.top
|
|
||||||
|
|
||||||
-- Calculate relative position within text area
|
|
||||||
local relativeX = mouseX - contentX
|
|
||||||
local relativeY = mouseY - contentY
|
|
||||||
|
|
||||||
-- Get font for measuring text
|
|
||||||
local font = self:_getFont()
|
|
||||||
if not font then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local text = self._textBuffer
|
|
||||||
local textLength = utf8.len(text) or 0
|
|
||||||
|
|
||||||
-- === SINGLE-LINE TEXT HANDLING ===
|
|
||||||
if not self.multiline then
|
|
||||||
-- Account for horizontal scroll offset in single-line inputs
|
|
||||||
if self._textScrollX then
|
|
||||||
relativeX = relativeX + self._textScrollX
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Find the character position closest to the click
|
|
||||||
local closestPos = 0
|
|
||||||
local closestDist = math.huge
|
|
||||||
|
|
||||||
-- Check each position in the text
|
|
||||||
for i = 0, textLength do
|
|
||||||
-- Get text up to this position
|
|
||||||
local offset = utf8.offset(text, i + 1)
|
|
||||||
local beforeText = offset and text:sub(1, offset - 1) or text
|
|
||||||
local textWidth = font:getWidth(beforeText)
|
|
||||||
|
|
||||||
-- Calculate distance from click to this position
|
|
||||||
local dist = math.abs(relativeX - textWidth)
|
|
||||||
|
|
||||||
if dist < closestDist then
|
|
||||||
closestDist = dist
|
|
||||||
closestPos = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return closestPos
|
|
||||||
end
|
|
||||||
|
|
||||||
-- === MULTILINE TEXT HANDLING ===
|
|
||||||
|
|
||||||
-- Update text wrapping if dirty
|
|
||||||
if self._textEditor then
|
|
||||||
self._textEditor:_updateTextIfDirty()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Split text into lines
|
|
||||||
local lines = {}
|
|
||||||
for line in (text .. "\n"):gmatch("([^\n]*)\n") do
|
|
||||||
table.insert(lines, line)
|
|
||||||
end
|
|
||||||
if #lines == 0 then
|
|
||||||
lines = { "" }
|
|
||||||
end
|
|
||||||
|
|
||||||
local lineHeight = font:getHeight()
|
|
||||||
|
|
||||||
-- Get text area width for wrapping calculations
|
|
||||||
local textAreaWidth = self.width
|
|
||||||
local scaledContentPadding = self:getScaledContentPadding()
|
|
||||||
if scaledContentPadding then
|
|
||||||
local borderBoxWidth = self._borderBoxWidth or (self.width + self.padding.left + self.padding.right)
|
|
||||||
textAreaWidth = borderBoxWidth - scaledContentPadding.left - scaledContentPadding.right
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Determine which line the click is on based on Y coordinate
|
|
||||||
local clickedLineNum = math.floor(relativeY / lineHeight) + 1
|
|
||||||
clickedLineNum = math.max(1, math.min(clickedLineNum, #lines))
|
|
||||||
|
|
||||||
-- Calculate character offset for lines before the clicked line
|
|
||||||
local charOffset = 0
|
|
||||||
for i = 1, clickedLineNum - 1 do
|
|
||||||
local lineLen = utf8.len(lines[i]) or 0
|
|
||||||
charOffset = charOffset + lineLen + 1 -- +1 for newline character
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the clicked line
|
|
||||||
local clickedLine = lines[clickedLineNum]
|
|
||||||
local lineLen = utf8.len(clickedLine) or 0
|
|
||||||
|
|
||||||
-- If text wrapping is enabled, handle wrapped segments
|
|
||||||
if self.textWrap and textAreaWidth > 0 then
|
|
||||||
local wrappedSegments = self:_wrapLine(clickedLine, textAreaWidth)
|
|
||||||
|
|
||||||
-- Determine which wrapped segment was clicked
|
|
||||||
local lineYOffset = (clickedLineNum - 1) * lineHeight
|
|
||||||
local segmentNum = math.floor((relativeY - lineYOffset) / lineHeight) + 1
|
|
||||||
segmentNum = math.max(1, math.min(segmentNum, #wrappedSegments))
|
|
||||||
|
|
||||||
local segment = wrappedSegments[segmentNum]
|
|
||||||
|
|
||||||
-- Find closest position within the segment
|
|
||||||
local segmentText = segment.text
|
|
||||||
local segmentLen = utf8.len(segmentText) or 0
|
|
||||||
local closestPos = segment.startIdx
|
|
||||||
local closestDist = math.huge
|
|
||||||
|
|
||||||
for i = 0, segmentLen do
|
|
||||||
local offset = utf8.offset(segmentText, i + 1)
|
|
||||||
local beforeText = offset and segmentText:sub(1, offset - 1) or segmentText
|
|
||||||
local textWidth = font:getWidth(beforeText)
|
|
||||||
local dist = math.abs(relativeX - textWidth)
|
|
||||||
|
|
||||||
if dist < closestDist then
|
|
||||||
closestDist = dist
|
|
||||||
closestPos = segment.startIdx + i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return charOffset + closestPos
|
|
||||||
end
|
|
||||||
|
|
||||||
-- No wrapping - find closest position in the clicked line
|
|
||||||
local closestPos = 0
|
|
||||||
local closestDist = math.huge
|
|
||||||
|
|
||||||
for i = 0, lineLen do
|
|
||||||
local offset = utf8.offset(clickedLine, i + 1)
|
|
||||||
local beforeText = offset and clickedLine:sub(1, offset - 1) or clickedLine
|
|
||||||
local textWidth = font:getWidth(beforeText)
|
|
||||||
local dist = math.abs(relativeX - textWidth)
|
|
||||||
|
|
||||||
if dist < closestDist then
|
|
||||||
closestDist = dist
|
|
||||||
closestPos = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return charOffset + closestPos
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Handle mouse click on text (set cursor position or start selection)
|
--- Handle mouse click on text (set cursor position or start selection)
|
||||||
---@param mouseX number -- Mouse X coordinate
|
---@param mouseX number -- Mouse X coordinate
|
||||||
---@param mouseY number -- Mouse Y coordinate
|
---@param mouseY number -- Mouse Y coordinate
|
||||||
|
|||||||
@@ -690,12 +690,12 @@ function TestInputField:testMultilineTextSplitting()
|
|||||||
})
|
})
|
||||||
|
|
||||||
-- Trigger line splitting
|
-- Trigger line splitting
|
||||||
element:_splitLines()
|
element._textEditor:_splitLines()
|
||||||
|
|
||||||
lu.assertEquals(#element._lines, 3)
|
lu.assertEquals(#element._textEditor._lines, 3)
|
||||||
lu.assertEquals(element._lines[1], "Line 1")
|
lu.assertEquals(element._textEditor._lines[1], "Line 1")
|
||||||
lu.assertEquals(element._lines[2], "Line 2")
|
lu.assertEquals(element._textEditor._lines[2], "Line 2")
|
||||||
lu.assertEquals(element._lines[3], "Line 3")
|
lu.assertEquals(element._textEditor._lines[3], "Line 3")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ====================
|
-- ====================
|
||||||
@@ -1155,11 +1155,11 @@ function TestInputField:testMouseToTextPosition()
|
|||||||
element:focus()
|
element:focus()
|
||||||
|
|
||||||
-- Test conversion at start of element
|
-- Test conversion at start of element
|
||||||
local pos = element:_mouseToTextPosition(10, 10)
|
local pos = element._textEditor:mouseToTextPosition(10, 10)
|
||||||
lu.assertEquals(pos, 0)
|
lu.assertEquals(pos, 0)
|
||||||
|
|
||||||
-- Test conversion far to the right (should be at end)
|
-- Test conversion far to the right (should be at end)
|
||||||
pos = element:_mouseToTextPosition(200, 10)
|
pos = element._textEditor:mouseToTextPosition(200, 10)
|
||||||
lu.assertEquals(pos, 5) -- "Hello" has 5 characters
|
lu.assertEquals(pos, 5) -- "Hello" has 5 characters
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1822,15 +1822,15 @@ function TestInputField:testMultilineMouseToTextPositionBasic()
|
|||||||
local lineHeight = font:getHeight()
|
local lineHeight = font:getHeight()
|
||||||
|
|
||||||
-- Click at start (should be position 0)
|
-- Click at start (should be position 0)
|
||||||
local pos = element:_mouseToTextPosition(10, 10)
|
local pos = element._textEditor:mouseToTextPosition(10, 10)
|
||||||
lu.assertEquals(pos, 0)
|
lu.assertEquals(pos, 0)
|
||||||
|
|
||||||
-- Click on second line start (should be after "Line 1\n" = position 7)
|
-- Click on second line start (should be after "Line 1\n" = position 7)
|
||||||
pos = element:_mouseToTextPosition(10, 10 + lineHeight)
|
pos = element._textEditor:mouseToTextPosition(10, 10 + lineHeight)
|
||||||
lu.assertEquals(pos, 7)
|
lu.assertEquals(pos, 7)
|
||||||
|
|
||||||
-- Click on third line start (should be after "Line 1\nLine 2\n" = position 14)
|
-- Click on third line start (should be after "Line 1\nLine 2\n" = position 14)
|
||||||
pos = element:_mouseToTextPosition(10, 10 + lineHeight * 2)
|
pos = element._textEditor:mouseToTextPosition(10, 10 + lineHeight * 2)
|
||||||
lu.assertEquals(pos, 14)
|
lu.assertEquals(pos, 14)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1852,12 +1852,12 @@ function TestInputField:testMultilineMouseToTextPositionXCoordinate()
|
|||||||
local charWidth = font:getWidth("A")
|
local charWidth = font:getWidth("A")
|
||||||
|
|
||||||
-- Click in middle of first line (should be around position 1-2)
|
-- Click in middle of first line (should be around position 1-2)
|
||||||
local pos = element:_mouseToTextPosition(10 + charWidth * 1.5, 10)
|
local pos = element._textEditor:mouseToTextPosition(10 + charWidth * 1.5, 10)
|
||||||
lu.assertTrue(pos >= 1 and pos <= 2)
|
lu.assertTrue(pos >= 1 and pos <= 2)
|
||||||
|
|
||||||
-- Click at end of second line (should be around position 6-7)
|
-- Click at end of second line (should be around position 6-7)
|
||||||
-- Text is "ABC\nDEF\nGHI", so second line "DEF" ends at position 6 or 7 (newline)
|
-- Text is "ABC\nDEF\nGHI", so second line "DEF" ends at position 6 or 7 (newline)
|
||||||
pos = element:_mouseToTextPosition(10 + charWidth * 3, 10 + lineHeight)
|
pos = element._textEditor:mouseToTextPosition(10 + charWidth * 3, 10 + lineHeight)
|
||||||
lu.assertTrue(pos >= 6 and pos <= 7)
|
lu.assertTrue(pos >= 6 and pos <= 7)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2076,11 +2076,11 @@ function TestInputField:testMultilineEmptyLinesHandling()
|
|||||||
local lineHeight = font:getHeight()
|
local lineHeight = font:getHeight()
|
||||||
|
|
||||||
-- Click on empty line (second line)
|
-- Click on empty line (second line)
|
||||||
local pos = element:_mouseToTextPosition(10, 10 + lineHeight)
|
local pos = element._textEditor:mouseToTextPosition(10, 10 + lineHeight)
|
||||||
lu.assertEquals(pos, 7) -- After "Line 1\n"
|
lu.assertEquals(pos, 7) -- After "Line 1\n"
|
||||||
|
|
||||||
-- Click on third line
|
-- Click on third line
|
||||||
pos = element:_mouseToTextPosition(10, 10 + lineHeight * 2)
|
pos = element._textEditor:mouseToTextPosition(10, 10 + lineHeight * 2)
|
||||||
lu.assertEquals(pos, 8) -- After "Line 1\n\n"
|
lu.assertEquals(pos, 8) -- After "Line 1\n\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2101,7 +2101,7 @@ function TestInputField:testMultilineYCoordinateBeyondText()
|
|||||||
local lineHeight = font:getHeight()
|
local lineHeight = font:getHeight()
|
||||||
|
|
||||||
-- Click way below the text (should clamp to last line)
|
-- Click way below the text (should clamp to last line)
|
||||||
local pos = element:_mouseToTextPosition(10, 10 + lineHeight * 10)
|
local pos = element._textEditor:mouseToTextPosition(10, 10 + lineHeight * 10)
|
||||||
local textLen = utf8.len(element.text)
|
local textLen = utf8.len(element.text)
|
||||||
|
|
||||||
-- Should be at or near end of text
|
-- Should be at or near end of text
|
||||||
|
|||||||
Reference in New Issue
Block a user