From b790fb1d322fc94f62d9dbf65f27d45ecf42d99c Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sat, 6 Dec 2025 11:59:00 -0500 Subject: [PATCH] fix text selection in immediate mode --- modules/Element.lua | 26 +++++++++++++++++++++- modules/Renderer.lua | 50 +++++++++++++++++++++++------------------- modules/TextEditor.lua | 6 ++--- 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/modules/Element.lua b/modules/Element.lua index 9b0501b..b37ba5f 100644 --- a/modules/Element.lua +++ b/modules/Element.lua @@ -416,7 +416,15 @@ function Element.new(props) onTextChange = props.onTextChange, onEnter = props.onEnter, }, textEditorDeps) - -- Initialize will be called after self is fully constructed + + -- Restore TextEditor state from StateManager in immediate mode + if Element._Context._immediateMode and self._stateId and self._stateId ~= "" then + local state = Element._StateManager.getState(self._stateId) + if state and state.textEditor then + -- Restore from nested textEditor state (saved via saveState()) + self._textEditor:setState(state.textEditor, self) + end + end end -- Set parent first so it's available for size calculations @@ -3305,6 +3313,14 @@ function Element:saveState() end end + -- Save drag tracking state for text selection + if self._mouseDownPosition ~= nil then + state._mouseDownPosition = self._mouseDownPosition + end + if self._textDragOccurred ~= nil then + state._textDragOccurred = self._textDragOccurred + end + return state end @@ -3337,6 +3353,14 @@ function Element:restoreState(state) self._scrollManager:setState(state.scrollManager) end + -- Restore drag tracking state for text selection + if state._mouseDownPosition ~= nil then + self._mouseDownPosition = state._mouseDownPosition + end + if state._textDragOccurred ~= nil then + self._textDragOccurred = state._textDragOccurred + end + -- Note: Blur cache data is used for invalidation, not restoration end diff --git a/modules/Renderer.lua b/modules/Renderer.lua index 406352c..ff48fc1 100644 --- a/modules/Renderer.lua +++ b/modules/Renderer.lua @@ -801,33 +801,37 @@ function Renderer:drawText(element) end -- Draw selection highlight for editable elements - if element._textEditor and element._textEditor:isFocused() and element._textEditor:hasSelection() and element.text and element.text ~= "" then - local selStart, selEnd = element._textEditor:getSelection() - local selectionColor = element.selectionColor or self._Color.new(0.3, 0.5, 0.8, 0.5) - local selectionWithOpacity = self._Color.new(selectionColor.r, selectionColor.g, selectionColor.b, selectionColor.a * self.opacity) + if element._textEditor and element._textEditor:isFocused() and element._textEditor:hasSelection() then + -- For editable elements, check TextEditor buffer instead of element.text + local textBuffer = element._textEditor:getText() + if textBuffer and textBuffer ~= "" then + local selStart, selEnd = element._textEditor:getSelection() + local selectionColor = element.selectionColor or self._Color.new(0.3, 0.5, 0.8, 0.5) + local selectionWithOpacity = self._Color.new(selectionColor.r, selectionColor.g, selectionColor.b, selectionColor.a * self.opacity) - -- Get selection rectangles from TextEditor - local selectionRects = element._textEditor:_getSelectionRects(element, selStart, selEnd) + -- Get selection rectangles from TextEditor + local selectionRects = element._textEditor:_getSelectionRects(element, selStart, selEnd) - -- Apply scissor for single-line editable inputs - if not element.multiline then - love.graphics.setScissor(contentX, contentY, textAreaWidth, textAreaHeight) - end - - -- Draw selection background rectangles - love.graphics.setColor(selectionWithOpacity:toRGBA()) - for _, rect in ipairs(selectionRects) do - local rectX = contentX + rect.x - local rectY = contentY + rect.y - if not element.multiline and element._textEditor._textScrollX then - rectX = rectX - element._textEditor._textScrollX + -- Apply scissor for single-line editable inputs + if not element.multiline then + love.graphics.setScissor(contentX, contentY, textAreaWidth, textAreaHeight) end - love.graphics.rectangle("fill", rectX, rectY, rect.width, rect.height) - end - -- Reset scissor - if not element.multiline then - love.graphics.setScissor() + -- Draw selection background rectangles + love.graphics.setColor(selectionWithOpacity:toRGBA()) + for _, rect in ipairs(selectionRects) do + local rectX = contentX + rect.x + local rectY = contentY + rect.y + if not element.multiline and element._textEditor._textScrollX then + rectX = rectX - element._textEditor._textScrollX + end + love.graphics.rectangle("fill", rectX, rectY, rect.width, rect.height) + end + + -- Reset scissor + if not element.multiline then + love.graphics.setScissor() + end end end diff --git a/modules/TextEditor.lua b/modules/TextEditor.lua index 9c47134..e62683b 100644 --- a/modules/TextEditor.lua +++ b/modules/TextEditor.lua @@ -1500,14 +1500,14 @@ end ---@param mouseX number ---@param mouseY number function TextEditor:handleTextDrag(element, mouseX, mouseY) - if not self._focused or not self._mouseDownPosition then + if not self._focused or not element._mouseDownPosition then return end local currentPos = self:mouseToTextPosition(element, mouseX, mouseY) - if currentPos ~= self._mouseDownPosition then - self:setSelection(element, self._mouseDownPosition, currentPos) + if currentPos ~= element._mouseDownPosition then + self:setSelection(element, element._mouseDownPosition, currentPos) self._cursorPosition = currentPos self._textDragOccurred = true else