From db4d4e684cb2dd90e0a0716e9716c99bd089ff3a Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Tue, 18 Oct 2022 00:10:29 +0100 Subject: [PATCH] TTkTextCursor: contentsChange reports the exact rem/added lines in insertText --- Makefile | 3 +- TermTk/TTkGui/textcursor.py | 76 +++++++++++++++++++++++++++---- TermTk/TTkGui/textdocument.py | 3 +- demo/showcase/textedit.py | 4 +- tests/pytest/test_002_textedit.py | 35 +++++++++++++- 5 files changed, 109 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 3dfc640f..cd9e3f74 100644 --- a/Makefile +++ b/Makefile @@ -95,4 +95,5 @@ test: .venv tools/check.import.sh . .venv/bin/activate ; \ flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude .venv,build,tmp ; \ - pytest tests/pytest/test_001_demo.py + pytest tests/pytest/test_002_textedit.py ; \ + pytest tests/pytest/test_001_demo.py ; diff --git a/TermTk/TTkGui/textcursor.py b/TermTk/TTkGui/textcursor.py index 35b5d163..25cf1c99 100644 --- a/TermTk/TTkGui/textcursor.py +++ b/TermTk/TTkGui/textcursor.py @@ -327,16 +327,75 @@ class TTkTextCursor(): p.anchor.set(line,pos) return self.insertText(text) + # I need this moethod to cover the math of merging + # multiples retuen values to be used in the contentsChange + # method + # + # ┬ ┬ ┬ ┬ + # x2 -│----│-----l2 ┬┼----┼┐ + # x1 l1 ┬┼----┼┐ ││ ││ + # ││ ││ a1 r2 ││ ││ a2 + # ││ /┼┘-------││-. ││ + # r1 ││ /.│--------└┼-.. ││ + # ││ /. │ │ \.││-z1 + # y1 └┼'. /┴ ┴-. -┼┘-z2 + # y2 _│. / \ │ + # │ / -┴ + # ┴' + # + # x1 = l1 + # x2 = l2 + # y1 = l1+r1 + # y2 = l2+r2 + (r1-a1) + # z1 = l1+a1 + (a2-r2) + # z2 = l2+a2 + + @staticmethod + def _mergeChangesSlices(ch1,ch2): + l1,r1,a1 = ch1 + l2,r2,a2 = ch2 + x1 = l1 + x2 = l2 + y1 = l1+r1 + y2 = l2+r2 + (r1-a1) + z1 = l1+a1 + (a2-r2) + z2 = l2+a2 + a = min(x1,x2) + b = max(y1,y2) - a + c = max(z1,z2) - a + return a,b,c + def insertText(self, text): - l,b,c = 0,1,1 + _lineFirst = -1 if self.hasSelection(): - l,b,c = self._removeSelectedText() - # Check if the number of lines is the same as the cursor + _lineFirst, _lineRem, _lineAdd = self._removeSelectedText() + + lineFirst = self._properties[0].position.line + lineRem, lineAdd = 0,0 + + # Check if the number of lines is the same as the number of cursors # this is a corner case where each line belongs to a # different cursor - textLines = text.split('\n') + textLines = text.split('\n') if len(text)>1 else text if len(textLines) != len(self._properties): textLines = [text]*len(self._properties) + + # Calc the added and removed lines + for i, pr in enumerate(self._properties): + lenNewLines=len(textLines[i].split('\n')) + l = pr.position.line + if textLines[i] == '\n': + lineAdd += 1 + l-lineFirst-lineRem + lineRem = l - lineFirst + elif (lineFirst + lineRem) > l: + lineAdd += lenNewLines-1 + else: + lineAdd += lenNewLines + l-lineFirst-lineRem + lineRem = l - lineFirst + 1 + if _lineFirst != -1: + lineFirst, lineRem, lineAdd = TTkTextCursor._mergeChangesSlices( + (_lineFirst, _lineRem, _lineAdd), + ( lineFirst, lineRem, lineAdd)) for i, pr in enumerate(self._properties): text=textLines[i] l = pr.position.line @@ -348,11 +407,12 @@ class TTkTextCursor(): if isinstance(ttktext, str): ttktext = TTkString(text, color) - newLines = (self._document._dataLines[l].substring(to=p) + ttktext + self._document._dataLines[l].substring(fr=p)).split('\n') + newLines = ( self._document._dataLines[l].substring(to=p) + + ttktext + + self._document._dataLines[l].substring(fr=p) ).split('\n') self._document._dataLines[l] = newLines[0] for nl in reversed(newLines[1:]): self._document._dataLines.insert(l+1, nl) - c+=1 # 2 scenarios: # 1) No Newline(s) added @@ -385,7 +445,7 @@ class TTkTextCursor(): self._autoChanged = True self._document._changed = True self._document.contentsChanged.emit() - self._document.contentsChange.emit(l,b,c) + self._document.contentsChange.emit(lineFirst, lineRem, lineAdd) self._autoChanged = False self._document.cursorPositionChanged.emit(self) @@ -511,7 +571,7 @@ class TTkTextCursor(): self._autoChanged = True self._document._changed = True self._document.contentsChanged.emit() - self._document.contentsChange.emit(0,0,0) + # self._document.contentsChange.emit(0,0,0) self._autoChanged = True def getHighlightedLines(self, fr, to, color): diff --git a/TermTk/TTkGui/textdocument.py b/TermTk/TTkGui/textdocument.py index c2bae9db..42e0e5d7 100644 --- a/TermTk/TTkGui/textdocument.py +++ b/TermTk/TTkGui/textdocument.py @@ -144,12 +144,13 @@ class TTkTextDocument(): return sum([len[x] for x in self._dataLines])+self.lineCount() def setText(self, text): + remLines = len(self._dataLines) self._dataLines = [TTkString(t) for t in text.split('\n')] self._changed = False self._lastSnap = self._dataLines.copy() self._snap = TTkTextDocument._snapshot(self._lastCursor, None, None) self.contentsChanged.emit() - self.contentsChange.emit(0,0,len(self._dataLines)) + self.contentsChange.emit(0,remLines,len(self._dataLines)) def appendText(self, text): if type(text) == str: diff --git a/demo/showcase/textedit.py b/demo/showcase/textedit.py index 21e1482a..545e9c61 100755 --- a/demo/showcase/textedit.py +++ b/demo/showcase/textedit.py @@ -53,10 +53,12 @@ class superSimpleHorizontalLine(ttk.TTkWidget): self._canvas.drawText(pos=(0,h-1), text='┕'+('━'*(w-2))+'┙',color=ttk.TTkColor.fg("#888888")) def demoTextEditSecondary(root=None, document=None): - te = ttk.TTkTextEdit(parent=root, document=document) + te = ttk.TTkTextEdit(parent=root, document=document, lineNumber=True) te.setLineWrapMode(ttk.TTkK.WidgetWidth) te.setWordWrapMode(ttk.TTkK.WordWrap) te.setReadOnly(False) + # print the document events for debugging purposes + document.contentsChange.connect(lambda line,rem,add: ttk.TTkLog.debug(f"{line=} {rem=} {add=}")) def demoTextEdit(root=None, document=None): frame = ttk.TTkFrame(parent=root, border=False, layout=ttk.TTkGridLayout()) diff --git a/tests/pytest/test_002_textedit.py b/tests/pytest/test_002_textedit.py index 649360d3..0966b771 100755 --- a/tests/pytest/test_002_textedit.py +++ b/tests/pytest/test_002_textedit.py @@ -83,7 +83,7 @@ def test_demo1(): # __........ # rem=8 add=5 -def test_demo1(): +def test_demo2(): doc = ttk.TTkTextDocument(text=txt) cur = ttk.TTkTextCursor(document=doc) @@ -107,3 +107,36 @@ def test_demo1(): assert cbLine == 0 assert cbRem == 8 assert cbAdd == 5 + +# .......... +# __..__..__ +# __..______ +# __........ +# .......... +# ..__...... +# .......... +# rem=5 add=3 + +def test_demo3(): + doc = ttk.TTkTextDocument(text=txt) + cur = ttk.TTkTextCursor(document=doc) + + _setCursor(cur, [ + ((1,0),(1,2)), + ((1,4),(1,6)), + ((1,8),(2,2)), + ((2,4),(3,2)), + ((5,2),(5,4))]) + cbLine, cbRem, cbAdd = -1,-1,-1 + + def _cb(a,b,c): + nonlocal cbLine, cbRem, cbAdd + cbLine, cbRem, cbAdd = a,b,c + + doc.contentsChange.connect(_cb) + cur.removeSelectedText() + print(f"{cbLine=}, {cbRem=}, {cbAdd=}") + + assert cbLine == 1 + assert cbRem == 5 + assert cbAdd == 3 \ No newline at end of file