remove debug prints, delete prev behavior

This commit is contained in:
Michael Freno
2025-11-08 22:40:54 -05:00
parent d73fdbebe8
commit cf65ceabf0

View File

@@ -343,7 +343,7 @@ function Element.new(props)
self._lines = nil -- Split lines (for multiline)
self._wrappedLines = nil -- Wrapped line data
self._textDirty = true -- Flag to recalculate lines/wrapping
-- Scroll state for text overflow
self._textScrollX = 0 -- Horizontal scroll offset in pixels
end
@@ -2551,10 +2551,10 @@ function Element:draw(backdropCanvas)
end
end
else
print("[FlexLove] Component not found: " .. self.themeComponent .. " in theme: " .. themeToUse.name)
-- Component not found in theme
end
else
print("[FlexLove] No theme available for themeComponent: " .. self.themeComponent)
-- No theme available for themeComponent
end
end
@@ -2591,20 +2591,19 @@ function Element:draw(backdropCanvas)
self:_updateTextIfDirty()
self:_updateAutoGrowHeight()
end
-- For editable elements, use _textBuffer; for non-editable, use text
local displayText = self.editable and self._textBuffer or self.text
local isPlaceholder = false
-- Show placeholder if editable, empty, and not focused
if self.editable and (not displayText or displayText == "") and self.placeholder and not self._focused then
displayText = self.placeholder
isPlaceholder = true
end
if displayText and displayText ~= "" then
local textColor = isPlaceholder
and Color.new(self.textColor.r * 0.5, self.textColor.g * 0.5, self.textColor.b * 0.5, self.textColor.a * 0.5)
local textColor = isPlaceholder and Color.new(self.textColor.r * 0.5, self.textColor.g * 0.5, self.textColor.b * 0.5, self.textColor.a * 0.5)
or self.textColor
local textColorWithOpacity = Color.new(textColor.r, textColor.g, textColor.b, textColor.a * self.opacity)
love.graphics.setColor(textColorWithOpacity:toRGBA())
@@ -2694,103 +2693,103 @@ function Element:draw(backdropCanvas)
tx = contentX
ty = contentY
end
-- Apply scroll offset for editable single-line inputs
if self.editable and not self.multiline and self._textScrollX then
tx = tx - self._textScrollX
end
-- Use scissor to clip text to content area for editable inputs
if self.editable and not self.multiline then
love.graphics.setScissor(contentX, contentY, textAreaWidth, textAreaHeight)
end
love.graphics.print(displayText, tx, ty)
-- Reset scissor
if self.editable and not self.multiline then
love.graphics.setScissor()
end
end
-- Draw cursor for focused editable elements (even if text is empty)
if self.editable and self._focused and self._cursorVisible then
local cursorColor = self.cursorColor or self.textColor
local cursorWithOpacity = Color.new(cursorColor.r, cursorColor.g, cursorColor.b, cursorColor.a * self.opacity)
love.graphics.setColor(cursorWithOpacity:toRGBA())
-- Calculate cursor position using new method that handles multiline
local cursorRelX, cursorRelY = self:_getCursorScreenPosition()
local cursorX = contentX + cursorRelX
local cursorY = contentY + cursorRelY
local cursorHeight = textHeight
-- Apply scroll offset for single-line inputs
if not self.multiline and self._textScrollX then
cursorX = cursorX - self._textScrollX
end
-- Apply scissor for single-line editable inputs
if not self.multiline then
love.graphics.setScissor(contentX, contentY, textAreaWidth, textAreaHeight)
end
-- Draw cursor line
love.graphics.rectangle("fill", cursorX, cursorY, 2, cursorHeight)
-- Reset scissor
if not self.multiline then
love.graphics.setScissor()
end
end
-- Draw selection highlight for editable elements
if self.editable and self._focused and self:hasSelection() and self.text and self.text ~= "" then
local selStart, selEnd = self:getSelection()
local selectionColor = self.selectionColor or Color.new(0.3, 0.5, 0.8, 0.5)
local selectionWithOpacity = Color.new(selectionColor.r, selectionColor.g, selectionColor.b, selectionColor.a * self.opacity)
-- Calculate selection bounds safely
local beforeSelection = ""
local selectedText = ""
local startByte = utf8.offset(self.text, selStart + 1)
local endByte = utf8.offset(self.text, selEnd + 1)
if startByte and endByte then
beforeSelection = self.text:sub(1, startByte - 1)
selectedText = self.text:sub(startByte, endByte - 1)
end
local selX = (tx or contentX) + font:getWidth(beforeSelection)
local selWidth = font:getWidth(selectedText)
local selY = ty or contentY
local selHeight = textHeight
-- Apply scissor for single-line editable inputs
if not self.multiline then
love.graphics.setScissor(contentX, contentY, textAreaWidth, textAreaHeight)
end
-- Draw selection background
love.graphics.setColor(selectionWithOpacity:toRGBA())
love.graphics.rectangle("fill", selX, selY, selWidth, selHeight)
-- Redraw selected text on top
love.graphics.setColor(textColorWithOpacity:toRGBA())
love.graphics.print(selectedText, selX, selY)
-- Reset scissor
if not self.multiline then
love.graphics.setScissor()
end
end
if self.textSize then
love.graphics.setFont(origFont)
end
end
-- Draw cursor for focused editable elements even when empty
if self.editable and self._focused and self._cursorVisible and (not displayText or displayText == "") then
-- Set up font for cursor rendering
@@ -2808,10 +2807,10 @@ function Element:draw(backdropCanvas)
local font = FONT_CACHE.get(self.textSize, fontPath)
love.graphics.setFont(font)
end
local font = love.graphics.getFont()
local textHeight = font:getHeight()
-- Calculate text area position
local textPaddingLeft = self.padding.left
local textPaddingTop = self.padding.top
@@ -2820,16 +2819,16 @@ function Element:draw(backdropCanvas)
textPaddingLeft = scaledContentPadding.left
textPaddingTop = scaledContentPadding.top
end
local contentX = self.x + textPaddingLeft
local contentY = self.y + textPaddingTop
-- Draw cursor
local cursorColor = self.cursorColor or self.textColor
local cursorWithOpacity = Color.new(cursorColor.r, cursorColor.g, cursorColor.b, cursorColor.a * self.opacity)
love.graphics.setColor(cursorWithOpacity:toRGBA())
love.graphics.rectangle("fill", contentX, contentY, 2, textHeight)
if self.textSize then
love.graphics.setFont(origFont)
end
@@ -2892,7 +2891,7 @@ function Element:draw(backdropCanvas)
love.graphics.setCanvas()
love.graphics.stencil(stencilFunc, "replace", 1)
love.graphics.setCanvas(currentCanvas)
love.graphics.setStencilTest("greater", 0)
-- Apply scroll offset AFTER clipping is set
@@ -3255,7 +3254,7 @@ function Element:update(dt)
})
self.callback(self, dragEvent)
end
-- Handle text selection drag for editable elements
if button == 1 and self.editable and self._focused then
self:_handleTextDrag(mx, my)
@@ -3310,7 +3309,7 @@ function Element:update(dt)
-- Clean up drag tracking
self._dragStartX[button] = nil
self._dragStartY[button] = nil
-- Clean up text selection drag tracking
if button == 1 then
self._mouseDownPosition = nil
@@ -3318,13 +3317,11 @@ function Element:update(dt)
-- Focus editable elements on left click
if button == 1 and self.editable then
print("[Element:update] Calling focus on editable element")
self:focus()
-- Handle text click for cursor positioning and word selection
self:_handleTextClick(mx, my, clickCount)
elseif button == 1 then
print("[Element:update] Button 1 clicked but editable:", self.editable)
end
-- Fire release event
@@ -4069,7 +4066,7 @@ function Element:_resetCursorBlink()
end
self._cursorBlinkTimer = 0
self._cursorVisible = true
-- Update scroll to keep cursor visible
self:_updateTextScroll()
end
@@ -4201,16 +4198,16 @@ function Element:getSelectedText()
local text = self._textBuffer or ""
local startByte = utf8.offset(text, startPos + 1)
local endByte = utf8.offset(text, endPos + 1)
if not startByte then
return ""
end
-- If endByte is nil, it means we want to the end of the string
if endByte then
endByte = endByte - 1 -- Adjust to get the last byte of the character
end
return string.sub(text, startByte, endByte)
end
@@ -4239,13 +4236,9 @@ end
--- Focus this element for keyboard input
function Element:focus()
if not self.editable then
print("[Element:focus] Not editable, skipping focus")
return
end
print("[Element:focus] Focusing element, editable:", self.editable)
-- Blur previously focused element
if Gui._focusedElement and Gui._focusedElement ~= self then
Gui._focusedElement:blur()
end
@@ -4254,16 +4247,11 @@ function Element:focus()
self._focused = true
Gui._focusedElement = self
print("[Element:focus] Focus set, _focused:", self._focused)
-- Reset cursor blink
self:_resetCursorBlink()
-- Select all text if selectOnFocus is enabled
if self.selectOnFocus then
self:selectAll()
else
-- Move cursor to end of text
self:moveCursorToEnd()
end
@@ -4362,19 +4350,14 @@ function Element:insertText(text, position)
self._textBuffer = before .. text .. after
self.text = self._textBuffer -- Sync display text
-- Update cursor position
self._cursorPosition = position + utf8.len(text)
print(string.format("[InsertText] Text: '%s', multiline: %s, autoGrow: %s",
self._textBuffer:gsub("\n", "\\n"), tostring(self.multiline), tostring(self.autoGrow)))
self:_markTextDirty()
self:_updateTextIfDirty() -- Update immediately to recalculate lines/wrapping
self:_updateAutoGrowHeight() -- Then update height based on new content
self:_validateCursorPosition()
end
--- Delete text in range
---@param startPos number -- Start position (inclusive)
---@param endPos number -- End position (inclusive)
function Element:deleteText(startPos, endPos)
@@ -4507,11 +4490,13 @@ function Element:_wrapLine(line, maxWidth)
local wrappedParts = {}
local currentLine = ""
local startIdx = 0
-- Helper function to extract a UTF-8 character by character index
local function getUtf8Char(str, charIndex)
local byteStart = utf8.offset(str, charIndex)
if not byteStart then return "" end
if not byteStart then
return ""
end
local byteEnd = utf8.offset(str, charIndex + 1)
if byteEnd then
return str:sub(byteStart, byteEnd - 1)
@@ -4519,12 +4504,8 @@ function Element:_wrapLine(line, maxWidth)
return str:sub(byteStart)
end
end
print(string.format("[WrapLine] line length: %d, maxWidth: %.1f, textWrap: %s",
utf8.len(line) or 0, maxWidth, tostring(self.textWrap)))
if self.textWrap == "word" then
-- Word wrapping
local words = {}
for word in line:gmatch("%S+") do
table.insert(words, word)
@@ -4535,7 +4516,6 @@ function Element:_wrapLine(line, maxWidth)
local width = font:getWidth(testLine)
if width > maxWidth and currentLine ~= "" then
-- Current line is full, start new line
local currentLineLen = utf8.len(currentLine)
table.insert(wrappedParts, {
text = currentLine,
@@ -4544,19 +4524,19 @@ function Element:_wrapLine(line, maxWidth)
})
startIdx = startIdx + currentLineLen + 1 -- +1 for the space
currentLine = word
-- Check if the word itself is too long - if so, break it with character wrapping
if font:getWidth(word) > maxWidth then
local wordLen = utf8.len(word)
local charLine = ""
local charStartIdx = startIdx
for j = 1, wordLen do
local char = getUtf8Char(word, j)
local testCharLine = charLine .. char
local charWidth = font:getWidth(testCharLine)
if charWidth > maxWidth and charLine ~= "" then
for j = 1, wordLen do
local char = getUtf8Char(word, j)
local testCharLine = charLine .. char
local charWidth = font:getWidth(testCharLine)
if charWidth > maxWidth and charLine ~= "" then
table.insert(wrappedParts, {
text = charLine,
startIdx = charStartIdx,
@@ -4568,7 +4548,7 @@ function Element:_wrapLine(line, maxWidth)
charLine = testCharLine
end
end
currentLine = charLine
startIdx = charStartIdx
end
@@ -4577,12 +4557,12 @@ function Element:_wrapLine(line, maxWidth)
local wordLen = utf8.len(word)
local charLine = ""
local charStartIdx = startIdx
for j = 1, wordLen do
local char = getUtf8Char(word, j)
local testCharLine = charLine .. char
local charWidth = font:getWidth(testCharLine)
if charWidth > maxWidth and charLine ~= "" then
table.insert(wrappedParts, {
text = charLine,
@@ -4595,7 +4575,7 @@ function Element:_wrapLine(line, maxWidth)
charLine = testCharLine
end
end
currentLine = charLine
startIdx = charStartIdx
else
@@ -4641,16 +4621,10 @@ function Element:_wrapLine(line, maxWidth)
endIdx = 0,
})
end
if #wrappedParts > 1 then
print(string.format("[WrapLine] Returning %d segments for line length %d",
#wrappedParts, utf8.len(line) or 0))
end
return wrappedParts
end
--- Get font for text rendering
---@return love.Font
function Element:_getFont()
-- Get font path from theme or element
@@ -4660,7 +4634,6 @@ function Element:_getFont()
if themeToUse and themeToUse.fonts and themeToUse.fonts[self.fontFamily] then
fontPath = themeToUse.fonts[self.fontFamily]
else
-- Assume fontFamily is a direct path
fontPath = self.fontFamily
end
end
@@ -4724,16 +4697,16 @@ function Element:_getCursorScreenPosition()
for lineNum, line in ipairs(lines) do
local lineLength = utf8.len(line) or 0
-- Check if cursor is on this line (before the newline)
if cursorPos <= charCount + lineLength then
-- Cursor is on this line
local posInLine = cursorPos - charCount
-- If text wrapping is enabled, find which wrapped segment
if self.textWrap and textAreaWidth > 0 then
local wrappedSegments = self:_wrapLine(line, textAreaWidth)
for segmentIdx, segment in ipairs(wrappedSegments) do
-- Check if cursor is within this segment's character range
if posInLine >= segment.startIdx and posInLine <= segment.endIdx then
@@ -4750,10 +4723,8 @@ function Element:_getCursorScreenPosition()
end
end
cursorX = font:getWidth(segmentText)
-- Add line offset for wrapped segments
cursorY = (lineNum - 1) * lineHeight + (segmentIdx - 1) * lineHeight
print(string.format("[CursorCalc] Line %d, Segment %d, posInLine: %d, startIdx: %d, endIdx: %d, cursorY: %.1f",
lineNum, segmentIdx, posInLine, segment.startIdx, segment.endIdx, cursorY))
return cursorX, cursorY
end
end
@@ -4774,13 +4745,12 @@ function Element:_getCursorScreenPosition()
return cursorX, cursorY
end
end
-- Move to next line (add 1 for the newline character)
charCount = charCount + lineLength + 1
end
-- Cursor is at the very end
return 0, (#lines) * lineHeight
return 0, #lines * lineHeight
end
--- Update element height based on text content (for autoGrow multiline fields)
@@ -4796,7 +4766,7 @@ function Element:_updateAutoGrowHeight()
local text = self._textBuffer or ""
local lineHeight = font:getHeight()
-- Get text area width for wrapping
local textAreaWidth = self.width
local scaledContentPadding = self:getScaledContentPadding()
@@ -4829,22 +4799,14 @@ function Element:_updateAutoGrowHeight()
totalWrappedLines = #lines
end
-- Ensure at least one line
totalWrappedLines = math.max(1, totalWrappedLines)
-- Calculate new content height
local newContentHeight = totalWrappedLines * lineHeight
-- Update height if it changed
if self.height ~= newContentHeight then
print(string.format("[AutoGrow] Height changing from %s to %s (lines: %d)",
tostring(self.height), tostring(newContentHeight), totalWrappedLines))
self.height = newContentHeight
self._borderBoxHeight = self.height + self.padding.top + self.padding.bottom
-- Re-layout parent if this element participates in parent layout
if self.parent and not self._explicitlyAbsolute then
print("[AutoGrow] Re-layouting parent")
self.parent:layoutChildren()
end
end
@@ -4866,35 +4828,40 @@ function Element:_mouseToTextPosition(mouseX, mouseY)
-- 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 X position within text area
local relativeX = mouseX - contentX
-- Account for horizontal scroll offset in single-line inputs
if not self.multiline and self._textScrollX then
relativeX = relativeX + self._textScrollX
end
-- Get font for measuring text
local font = self:_getFont()
-- Find the character position closest to the click
local text = self._textBuffer
local textLength = utf8.len(text) or 0
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
@@ -4912,19 +4879,17 @@ function Element:_handleTextClick(mouseX, mouseY, clickCount)
local pos = self:_mouseToTextPosition(mouseX, mouseY)
self:setCursorPosition(pos)
self:clearSelection()
-- Store position for potential drag selection
self._mouseDownPosition = pos
elseif clickCount == 2 then
-- Double click: Select word
self:_selectWordAtPosition(self:_mouseToTextPosition(mouseX, mouseY))
elseif clickCount >= 3 then
-- Triple click: Select all (or line in multi-line mode)
self:selectAll()
end
self:_resetCursorBlink()
end
@@ -4937,7 +4902,7 @@ function Element:_handleTextDrag(mouseX, mouseY)
end
local currentPos = self:_mouseToTextPosition(mouseX, mouseY)
-- Create selection from mouse down position to current position
if currentPos ~= self._mouseDownPosition then
self:setSelection(self._mouseDownPosition, currentPos)
@@ -4945,7 +4910,7 @@ function Element:_handleTextDrag(mouseX, mouseY)
else
self:clearSelection()
end
self:_resetCursorBlink()
end
@@ -4958,7 +4923,7 @@ function Element:_selectWordAtPosition(position)
local text = self._textBuffer
local textLength = utf8.len(text) or 0
if position < 0 or position > textLength then
return
end
@@ -4966,7 +4931,7 @@ function Element:_selectWordAtPosition(position)
-- Find word boundaries
local wordStart = position
local wordEnd = position
-- Find start of word (move left while alphanumeric)
while wordStart > 0 do
local offset = utf8.offset(text, wordStart)
@@ -4977,7 +4942,7 @@ function Element:_selectWordAtPosition(position)
break
end
end
-- Find end of word (move right while alphanumeric)
while wordEnd < textLength do
local offset = utf8.offset(text, wordEnd + 1)
@@ -4988,7 +4953,7 @@ function Element:_selectWordAtPosition(position)
break
end
end
-- Select the word
if wordEnd > wordStart then
self:setSelection(wordStart, wordEnd)
@@ -5129,6 +5094,13 @@ function Element:keypressed(key, scancode, isrepeat)
if self:hasSelection() then
-- Delete selection
self:deleteSelection()
elseif ctrl then
-- Ctrl/Cmd+Backspace: Delete all text from start to cursor
if self._cursorPosition > 0 then
self:deleteText(0, self._cursorPosition)
self._cursorPosition = 0
self:_validateCursorPosition()
end
elseif self._cursorPosition > 0 then
-- Delete character before cursor
-- Update cursor position BEFORE deleteText so updates use correct position
@@ -5206,11 +5178,11 @@ function Element:keypressed(key, scancode, isrepeat)
local selectedText = self:getSelectedText()
if selectedText then
love.system.setClipboardText(selectedText)
-- Delete the selected text
local oldText = self._textBuffer
self:deleteSelection()
-- Trigger onTextChange callback
if self.onTextChange and self._textBuffer ~= oldText then
self.onTextChange(self, self._textBuffer, oldText)
@@ -5224,15 +5196,15 @@ function Element:keypressed(key, scancode, isrepeat)
local clipboardText = love.system.getClipboardText()
if clipboardText and clipboardText ~= "" then
local oldText = self._textBuffer
-- Delete selection if exists
if self:hasSelection() then
self:deleteSelection()
end
-- Insert clipboard text
self:insertText(clipboardText)
-- Trigger onTextChange callback
if self.onTextChange and self._textBuffer ~= oldText then
self.onTextChange(self, self._textBuffer, oldText)