From 9ef1bb1d8897be6d89838a4f683c00b9b9bff472 Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Tue, 22 Mar 2022 00:37:35 +0000 Subject: [PATCH] Basic wrap implementation in the text edit --- TermTk/TTkCore/canvas.py | 3 +- TermTk/TTkCore/constant.py | 31 ++++++++++- TermTk/TTkCore/string.py | 32 ++++++++++- TermTk/TTkWidgets/texedit.py | 100 ++++++++++++++++++++++++++++++++--- demo/showcase/textedit.py | 7 +++ 5 files changed, 162 insertions(+), 11 deletions(-) diff --git a/TermTk/TTkCore/canvas.py b/TermTk/TTkCore/canvas.py index 3b9867dc..e5c726ab 100644 --- a/TermTk/TTkCore/canvas.py +++ b/TermTk/TTkCore/canvas.py @@ -209,7 +209,7 @@ class TTkCanvas: if isinstance(text, TTkString): text = text.align(width=width, alignment=alignment, color=color) - txt, colors = text.getData() + txt, colors = text.tab2spaces().getData() if forceColor: colors=[color]*len(colors) for i in range(max(0,-x), min(len(txt),self._width-x)): @@ -217,6 +217,7 @@ class TTkCanvas: self._data[y][x+i] = txt[i] self._colors[y][x+i] = colors[i].mod(x+i,y) else: + text = text.replace('\t',' ') if lentxt < width: pad = width-lentxt if alignment in [TTkK.NONE, TTkK.LEFT_ALIGN]: diff --git a/TermTk/TTkCore/constant.py b/TermTk/TTkCore/constant.py index d3a1dc08..773ebfdd 100644 --- a/TermTk/TTkCore/constant.py +++ b/TermTk/TTkCore/constant.py @@ -142,7 +142,7 @@ class TTkConstant: Events reported by :class:`~TermTk.TTkCore.TTkTerm.inputmouse.TTkMouseEvent` -> :class:`~TermTk.TTkCore.TTkTerm.inputmouse.TTkMouseEvent.key` ''' NoButton = 0x00000000 - '''The button state does not refer to any button (see QMouseEvent::button()).''' + '''The button state does not refer to any button.''' AllButtons = 0x07ffffff '''This value corresponds to a mask of all possible mouse buttons. Use to set the 'acceptedButtons' property of a MouseArea to accept ALL mouse buttons.''' LeftButton = 0x00000001 @@ -164,6 +164,35 @@ class TTkConstant: MiddleButton = MouseKey.MiddleButton Wheel = MouseKey.Wheel + class WrapMode(): + '''Those constants describes how text is wrapped in a document.''' + # NoWrap = 0x00 + # '''Text is not wrapped at all.''' + WordWrap = 0x01 + '''Text is wrapped at word boundaries.''' + # ManualWrap = 0x02 + # '''Same as :class:`~TermTk.TTkCore.constant.TTkConstant.WrapMode.NoWrap`''' + WrapAnywhere = 0x03 + '''Text can be wrapped at any point on a line, even if it occurs in the middle of a word.''' + WrapAtWordBoundaryOrAnywhere = 0x04 + '''If possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.''' + + # NoWrap = WrapMode.NoWrap + WordWrap = WrapMode.WordWrap + # ManualWrap = WrapMode.ManualWrap + WrapAnywhere = WrapMode.WrapAnywhere + WrapAtWordBoundaryOrAnywhere = WrapMode.WrapAtWordBoundaryOrAnywhere + + class LineWrapMode(): + NoWrap = 0x00 + WidgetWidth = 0x01 + FixedWidth = 0x03 + + NoWrap = LineWrapMode.NoWrap + WidgetWidth = LineWrapMode.WidgetWidth + FixedWidth = LineWrapMode.FixedWidth + + # Events class MouseEvent(): '''Input Mouse Event diff --git a/TermTk/TTkCore/string.py b/TermTk/TTkCore/string.py index 0135cd27..4382fd5c 100644 --- a/TermTk/TTkCore/string.py +++ b/TermTk/TTkCore/string.py @@ -130,7 +130,8 @@ class TTkString(): def __gt__(self, other): return self._text > other._text def __ge__(self, other): return self._text >= other._text - def tab2spaces(self, tabSpaces): + def tab2spaces(self, tabSpaces=4): + '''Return the string representation with the tabs (converted in spaces) trimmed and aligned''' ret = TTkString() slices = self._text.split("\t") ret._text += slices[0] @@ -145,6 +146,35 @@ class TTkString(): pos+=len(s)+1 return ret + def tabCharPos(self, pos, tabSpaces=4): + '''Return the char position in the string from the position in its representation with the tab solved + + i.e. + + :: + + pos X = 11 + tab2Spaces |----------|---------------------| + Tabs |--| | |-| |-| | + _text Lorem ipsum dolor sit amet, + chars .....t .....t .....t ...t..... + ret x = 8 (tab is a char) + ''' + slices = self._text.split("\t") + postxt = 0 # position of the text + lentxt = 0 # length of the text with resolved tabs + for s in slices: + lens = len(s) + lentxt += lens + postxt += lens + if posself._tabSpaces: + self._lastWrapUsed = w + self._rewrap() + def _updateSize(self): - self._hsize = max( [ len(l) for l in self._lines ] ) + self._hsize = max( [ len(l) for l in self._dataLines ] ) def viewFullAreaSize(self) -> (int, int): - return self._hsize, len(self._lines) + if self._lineWrapMode == TTkK.NoWrap: + return self._hsize, len(self._lines) + elif self._lineWrapMode == TTkK.WidgetWidth: + return self.width(), len(self._lines) + elif self._lineWrapMode == TTkK.FixedWidth: + return self._wrapWidth, len(self._lines) def viewDisplayedSize(self) -> (int, int): return self.size() @@ -314,8 +388,8 @@ class _TTkTextEditView(TTkAbstractScrollView): selectColor = TTkCfg.theme.lineEditTextColorSelected h = self.height() - for y, t in enumerate(self._lines[oy:oy+h]): - t = t.tab2spaces(self._tabSpaces) + for y, l in enumerate(self._lines[oy:oy+h]): + t = self._dataLines[l[0]].substring(l[1][0],l[1][1]).tab2spaces(self._tabSpaces) if self._selectionFrom[1] <= y+oy <= self._selectionTo[1]: pf = 0 if y+oy > self._selectionFrom[1] else self._selectionFrom[0] pt = len(t) if y+oy < self._selectionTo[1] else self._selectionTo[0] @@ -328,6 +402,9 @@ class TTkTextEdit(TTkAbstractScrollArea): '_textEditView', # Forwarded Methods 'setText', 'append', 'isReadOnly', 'setReadOnly' + 'wrapWidth', 'setWrapWidth', + 'lineWrapMode', 'setLineWrapMode', + 'wordWrapMode', 'setWordWrapMode', ) def __init__(self, *args, **kwargs): super().__init__(self, *args, **kwargs) @@ -338,4 +415,11 @@ class TTkTextEdit(TTkAbstractScrollArea): self.append = self._textEditView.append self.isReadOnly = self._textEditView.isReadOnly self.setReadOnly = self._textEditView.setReadOnly + # Forward Wrap Methods + self.wrapWidth = self._textEditView.wrapWidth + self.setWrapWidth = self._textEditView.setWrapWidth + self.lineWrapMode = self._textEditView.lineWrapMode + self.setLineWrapMode = self._textEditView.setLineWrapMode + self.wordWrapMode = self._textEditView.wordWrapMode + self.setWordWrapMode = self._textEditView.setWordWrapMode diff --git a/demo/showcase/textedit.py b/demo/showcase/textedit.py index e4214eec..d4113b4d 100755 --- a/demo/showcase/textedit.py +++ b/demo/showcase/textedit.py @@ -69,6 +69,13 @@ def demoTextEdit(root=None): te.append(ttk.TTkString("Random TTkString Input Test\n",ttk.TTkColor.UNDERLINE+ttk.TTkColor.BOLD)) te.append(ttk.TTkString('\n').join([ getSentence(5,25,i) for i in range(50)])) + + # use the widget size to wrap + # te.setLineWrapMode(ttk.TTkK.WidgetWidth) + + # Use a fixed wrap size + te.setLineWrapMode(ttk.TTkK.FixedWidth) + te.setWrapWidth(40) return te def main():