Browse Source

Added the ExtraSelections in the TTKTextEdit

pull/309/head
Eugenio Parodi 🌶️ 1 year ago
parent
commit
89939f6f41
  1. 18
      TermTk/TTkCore/canvas.py
  2. 62
      TermTk/TTkCore/color.py
  3. 11
      TermTk/TTkCore/constant.py
  4. 10
      TermTk/TTkCore/string.py
  5. 50
      TermTk/TTkGui/textcursor.py
  6. 157
      TermTk/TTkWidgets/texedit.py
  7. 82
      tests/t.draw/test.draw.009.colorMix.py
  8. 173
      tests/t.ui/test.ui.018.TextEdit.04.Pygments.py

18
TermTk/TTkCore/canvas.py

@ -232,18 +232,16 @@ class TTkCanvas():
text = text.align(width=width, alignment=alignment, color=color)
txt, colors = text.tab2spaces().getData()
a,b = max(0,-x), min(len(txt),self._width-x)
self._data[y][x+a:x+b] = txt[a:b]
if forceColor:
colors=[color]*len(colors)
a,b = max(0,-x), min(len(txt),self._width-x)
for i in range(a,b):
#self._set(y, x+i, txt[i-x], colors[i-x])
self._data[y][x+i] = txt[i]
if colors[i] == TTkColor.RST != color:
self._colors[y][x+i] = color.mod(x+i,y)
elif (not colors[i].hasBackground()) and color.hasBackground():
self._colors[y][x+i] = (color + colors[i]).mod(x+i,y)
else:
self._colors[y][x+i] = colors[i].mod(x+i,y)
else:
for i in range(a,b):
if color != TTkColor.RST:
self._colors[y][x+i] = (colors[i] | color).mod(x+i,y)
else:
self._colors[y][x+i] = colors[i].mod(x+i,y)
# Check the full wide chars on the edge of the two canvasses
if ((0 <= (x+a) < self._width) and self._data[y][x+a] == ''):
self._data[y][x+a] = TTkCfg.theme.unicodeWideOverflowCh[0]

62
TermTk/TTkCore/color.py

@ -205,19 +205,11 @@ class _TTkColor:
self._fg == other._fg and
self._bg == other._bg )
# # self | other
# def __or__(self, other):
# # TTkLog.debug("__add__")
# if other._clean:
# return other
# clean = self._clean
# fg: str = self._fg or other._fg
# bg: str = self._bg or other._bg
# colorMod = self._colorMod or other._colorMod
# return _TTkColor(
# fg=fg, bg=bg,
# colorMod=colorMod,
# clean=clean)
# self | other
def __or__(self, other):
c = self.copy()
c._clean = False
return other + c
# self + other
def __add__(self, other):
@ -307,21 +299,11 @@ class _TTkColor_mod(_TTkColor):
( self._mod == (other._mod if isinstance(other,_TTkColor_mod) else 0))
)
# # self | other
# def __or__(self, other):
# # TTkLog.debug("__add__")
# if other._clean:
# return other
# otherMod = other._mod if isinstance(other,_TTkColor_mod) else 0
# clean = self._clean
# fg: str = self._fg or other._fg
# bg: str = self._bg or other._bg
# mod: str = self._mod + otherMod
# colorMod = self._colorMod or other._colorMod
# return _TTkColor_mod(
# fg=fg, bg=bg, mod=mod,
# colorMod=colorMod,
# clean=clean)
# self | other
def __or__(self, other):
c = self.copy()
c._clean = False
return other + c
# self + other
def __add__(self, other):
@ -407,24 +389,12 @@ class _TTkColor_mod_link(_TTkColor_mod):
( self._link == (other._link if isinstance(other,_TTkColor_mod_link) else 0))
)
# # self | other
# def __or__(self, other):
# # TTkLog.debug("__add__")
# if other._clean:
# return other
# otherMod = other._mod if isinstance(other,_TTkColor_mod) else 0
# otherLink = other._link if isinstance(other,_TTkColor_mod_link) else ''
# clean = self._clean
# fg: str = self._fg or other._fg
# bg: str = self._bg or other._bg
# mod: str = self._mod + otherMod
# link:str = self._link or otherLink
# colorMod = self._colorMod or other._colorMod
# return _TTkColor_mod_link(
# fg=fg, bg=bg, mod=mod,
# colorMod=colorMod, link=link,
# clean=clean)
# self | other
def __or__(self, other):
c = self.copy()
c._clean = False
return other + c
# self + other
def __add__(self, other):
# TTkLog.debug("__add__")

11
TermTk/TTkCore/constant.py

@ -125,6 +125,17 @@ class TTkConstant:
# ContiguousSelection = SelectionMode.ContiguousSelection
MultiSelection = SelectionMode.MultiSelection
class SelectionFormat(int):
'''
Selection properties
.. autosummary::
FullWidthSelection
'''
FullWidthSelection = 0x06000
'''When set on the characterFormat of a selection, the whole width of the text will be shown selected.'''
# Graph types
FILLED = 0x0001
LINE = 0x0002

10
TermTk/TTkCore/string.py

@ -452,7 +452,7 @@ class TTkString():
return ret
def completeColor(self, color, match=None, posFrom=None, posTo=None) -> Self:
def completeColor(self, color:TTkColor, match=None, posFrom=None, posTo=None) -> Self:
''' Complete the color of the entire string or a slice of it
The Fg and/or Bg of the string is replaced with the selected Fg/Bg color only if missing
@ -479,17 +479,17 @@ class TTkString():
while pos := self._text.index(match, start) if match in self._text[start:] else None:
start = pos+lenMatch
for i in range(pos, pos+lenMatch):
ret._colors[i] += color
ret._colors[i] |= color
elif posFrom == posTo == None:
ret._colors = [c+color for c in self._colors]
ret._colors = [c|color for c in self._colors]
elif posFrom < posTo:
ret._colors = self._colors.copy()
posFrom = min(len(self._text),posFrom)
posTo = min(len(self._text),posTo)
for i in range(posFrom, posTo):
ret._colors[i] += color
ret._colors[i] |= color
else:
ret._colors = [c+color for c in self._colors]
ret._colors = [c|color for c in self._colors]
return ret

50
TermTk/TTkGui/textcursor.py

@ -221,6 +221,9 @@ class TTkTextCursor():
def position(self) -> _CP:
return self._properties[self._cID].position
def cursors(self) -> list[_CP]:
return self._properties
def addCursor(self, line:int, pos:int) -> None:
self._cID = 0
@ -490,6 +493,7 @@ class TTkTextCursor():
xTo += len(m.group(0))
p.position.pos = xTo
p.anchor.pos = xFrom
p.anchor.line = line
self._checkCursors(notify=self.position().toNum()!=currPos)
def selectedText(self) -> TTkString:
@ -577,12 +581,38 @@ class TTkTextCursor():
self._document._dataLines[l] = line.setColor(color=color, posFrom=pf, posTo=pt)
self._autoChanged = True
self._document.setChanged(True)
self._document._acquire()
self._document._release()
self._document.contentsChanged.emit()
# self._document.contentsChange.emit(0,0,0)
self._autoChanged = True
def getHighlightedLines(self, fr:int, to:int, color:TTkColor) -> list[TTkString]:
def _getCoveredLines(self, fr:int, to:int, lines:list[TTkColor], color:TTkColor) -> list[TTkColor]:
for p in self._properties:
selSt = p.selectionStart().line
selEn = p.selectionEnd().line
if selEn >= fr and selSt<=to:
for i in range(max(selSt,fr),min(selEn,to)+1):
lines[i-fr] = color
return lines
def _getBlinkingCursors(self, fr:int, to:int, lines, color:TTkColor) -> list[TTkString]:
ret = lines
# Add Blinking cursor
if len(self._properties)>1:
for p in self._properties:
cp = p.position
if not 0<=(cp.line-fr)<len(ret): continue
ret[cp.line-fr] = ret[cp.line-fr].setColor(color=color+TTkColor.BLINKING, posFrom=cp.pos, posTo=cp.pos+1)
if cp.pos == len(ret[cp.line-fr]):
ret[cp.line-fr] = ret[cp.line-fr]+TTkString('',color+TTkColor.BLINKING)
elif ret[cp.line-fr].charAt(cp.pos) == ' ':
ret[cp.line-fr].setCharAt(pos=cp.pos, char='')
# ret[p.line-fr].setColorAt(pos=p.pos, color=TTkCfg.theme.treeLineColor+TTkColor.BLINKING)
#elif ret[p.line-fr].charAt(p.pos) == '\t':
# ret[p.line-fr].setCharAt(pos=p.pos, char='\t')
return ret
def _getHighlightedLines(self, fr:int, to:int, lines, color:TTkColor) -> list[TTkString]:
# Create a list of cursors (filtering out the ones which
# position/selection is outside the screen boundaries)
sel = []
@ -593,7 +623,7 @@ class TTkTextCursor():
sel.append((selSt,selEn,p))
# Retrieve the sublist of lines to be required (displayed)
ret = self._document._dataLines[fr:to+1]
ret = lines
# Apply the selection color for each of them
for s in sel:
selSt, selEn, _ = s
@ -602,18 +632,4 @@ class TTkTextCursor():
pf = 0 if i > selSt.line else selSt.pos
pt = len(l) if i < selEn.line else selEn.pos
ret[i-fr] = l.setColor(color=color, posFrom=pf, posTo=pt)
# Add Blinking cursor
if len(self._properties)>1:
for s in sel:
_, _, prop = s
p = prop.position
ret[p.line-fr] = ret[p.line-fr].setColor(color=color+TTkColor.BLINKING, posFrom=p.pos, posTo=p.pos+1)
if p.pos == len(ret[p.line-fr]):
ret[p.line-fr] = ret[p.line-fr]+TTkString('',color+TTkColor.BLINKING)
elif ret[p.line-fr].charAt(p.pos) == ' ':
ret[p.line-fr].setCharAt(pos=p.pos, char='')
# ret[p.line-fr].setColorAt(pos=p.pos, color=TTkCfg.theme.treeLineColor+TTkColor.BLINKING)
#elif ret[p.line-fr].charAt(p.pos) == '\t':
# ret[p.line-fr].setCharAt(pos=p.pos, char='\t')
return ret

157
TermTk/TTkWidgets/texedit.py

@ -22,7 +22,6 @@
__all__ = ['TTkTextEditView', 'TTkTextEdit']
from math import log10, floor
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.cfg import TTkCfg
@ -127,6 +126,79 @@ class TTkTextEditView(TTkAbstractScrollView):
:ref:`ttkdesigner Tutorial <TextEdit_ttkDesigner-Tutorial_Intro>`
'''
class ExtraSelection():
'''
The :py:class:`ExtraSelection` structure provides a way of specifying a character format for a given selection in a document.
:param format: A format that is used to specify the type of selection, defaults to :py:class:`TTkK.NONE`.
:type format: :py:class:`TTkK.SelectionFormat`
:param color: The color used to specify the foreground/background color/mod for the selection.
:type color: :py:class:`TTkColor`
:param cursor: A cursor that contains a selection in a :py:class:`QTextDocument`.
:type cursor: :py:class:`TTkTextCursor`
'''
__slots__ = ('_format', '_color', '_cursor')
def __init__(self,
format:TTkK.SelectionFormat=TTkK.NONE,
color:TTkColor=TTkColor.RST,
cursor:TTkTextCursor=None) -> None:
self._color = color
self._format = format
self._cursor = cursor if cursor else TTkTextCursor()
def color(self) -> TTkColor:
'''
This propery holds the color that is used for the selection.
:rtype: :py:class:`TTkColor`
'''
return self._color
def setColor(self, color:TTkColor) -> None:
'''
Set the color.
:param color: A color that is used for the selection.
:type color: :py:class:`TTkColor`
'''
self._color = color
def format(self) -> TTkK.SelectionFormat:
'''
This propery holds the format that is used to specify the type of selection.
:rtype: :py:class:`TTkK.SelectionFormat`
'''
return self._format
def setFormat(self, format:TTkK.SelectionFormat) -> None:
'''
Set the format.
:param format: A format that is used to specify the type of selection.
:type format: :py:class:`TTkK.SelectionFormat`
'''
self._format = format
def cursor(self) -> TTkTextCursor:
'''
This propery holds the fcursor that contains a selection in a :py:class:`QTextDocument`.
:rtype: :py:class:`TTkTextCursor`
'''
return self._cursor
def setCursor(self, cursor:TTkTextCursor) -> None:
'''
Set the cursor.
:param cursor: A cursor that contains a selection in a :py:class:`QTextDocument`.
:type cursor: :py:class:`TTkTextCursor`
'''
self._cursor = cursor
currentColorChanged:pyTTkSignal
'''
This signal is emitted if the current character color has changed,
@ -136,6 +208,14 @@ class TTkTextEditView(TTkAbstractScrollView):
:type color: :py:class:`TTkColor`
'''
cursorPositionChanged:pyTTkSignal
'''
This signal is emitted whenever the position of the cursor changed.
:param cursor: the cursor changed.
:type cursor: :py:class:`TTkTextCursor`
'''
undoAvailable:pyTTkSignal
'''
This signal is emitted whenever undo operations become available (available is true)
@ -175,6 +255,7 @@ class TTkTextEditView(TTkAbstractScrollView):
__slots__ = (
'_textDocument', '_hsize',
'_textCursor', '_cursorParams',
'_extraSelections',
'_textWrap', '_lineWrapMode', '_lastWrapUsed',
'_replace',
'_readOnly', '_multiCursor',
@ -185,7 +266,7 @@ class TTkTextEditView(TTkAbstractScrollView):
# 'wrapWidth', 'setWrapWidth',
# 'wordWrapMode', 'setWordWrapMode',
# Signals
'currentColorChanged',
'currentColorChanged', 'cursorPositionChanged',
'undoAvailable', 'redoAvailable',
'textChanged'
)
@ -217,20 +298,22 @@ class TTkTextEditView(TTkAbstractScrollView):
'''
self.currentColorChanged = pyTTkSignal(TTkColor)
self.cursorPositionChanged = pyTTkSignal(TTkTextCursor)
self.undoAvailable = pyTTkSignal(bool)
self.redoAvailable = pyTTkSignal(bool)
self.textChanged = pyTTkSignal()
self._readOnly = readOnly
self._multiLine = multiLine
self._multiCursor = True
self._hsize = 0
self._readOnly:bool = readOnly
self._multiLine:bool = multiLine
self._multiCursor:bool = True
self._extraSelections:list[TTkTextEditView.ExtraSelection] = []
self._hsize:int = 0
self._lastWrapUsed = 0
self._lineWrapMode = TTkK.NoWrap
self._replace = False
self._cursorParams = None
self._textDocument = None
self._textCursor = None
self._textDocument:TTkTextDocument = None
self._textCursor:TTkTextCursor = None
self._textWrap = None
self._clipboard = TTkClipboard()
@ -326,6 +409,26 @@ class TTkTextEditView(TTkAbstractScrollView):
def wordWrapMode(self, *args, **kwargs) -> None: return self._textWrap.wordWrapMode(*args, **kwargs)
def setWordWrapMode(self, *args, **kwargs) -> None: return self._textWrap.setWordWrapMode(*args, **kwargs)
def extraSelections(self) -> list[ExtraSelection]:
'''
Returns previously set extra selections.
:rtype: list[:py:class:`ExtraSelection`]
'''
return self._extraSelections
def setExtraSelections(self, extraSelections:list[ExtraSelection]) -> None:
'''
This function allows temporarily marking certain regions in the document with a given color,
specified as selections. This can be useful for example in a programming editor to mark a
whole line of text with a given background color to indicate the existence of a breakpoint.
:param extraSelections: the list of extra selections.
:type extraSelections: list[:py:class:`ExtraSelection`]
'''
self._extraSelections = extraSelections
self.update()
def textCursor(self) -> TTkTextCursor:
return self._textCursor
@ -417,6 +520,7 @@ class TTkTextEditView(TTkAbstractScrollView):
def _cursorPositionChanged(self, cursor:TTkTextCursor) -> None:
if cursor == self._textCursor:
self.currentColorChanged.emit(cursor.positionColor())
self.cursorPositionChanged.emit(cursor)
self._pushCursor()
def resizeEvent(self, w:int, h:int) -> None:
@ -676,43 +780,59 @@ class TTkTextEditView(TTkAbstractScrollView):
def paintEvent(self, canvas: TTkCanvas) -> None:
ox, oy = self.getViewOffsets()
w,h = self.size()
style = self.currentStyle()
color = style['color']
selectColor = style['selectedColor']
lineColor = style['lineColor']
backgroundColor = self._textDocument._backgroundColor
backgroundColors = [self._textDocument._backgroundColor]*h
if backgroundColor != TTkColor.RST:
canvas.fill(color=backgroundColor)
h = self.height()
subLines = self._textWrap._lines[oy:oy+h]
if not subLines: return
outLines = self._textCursor.getHighlightedLines(subLines[0][0], subLines[-1][0], selectColor)
fr = subLines[0][0]
to = subLines[-1][0]
outLines = self._textDocument._dataLines[fr:to+1]
outLines = self._textCursor._getHighlightedLines(fr, to, outLines, selectColor)
for extraSelection in self._extraSelections:
esCursor = extraSelection._cursor
esColor = extraSelection._color
esFormat = extraSelection._format
if esFormat == TTkK.SelectionFormat.FullWidthSelection:
backgroundColors = esCursor._getCoveredLines(fr, to, backgroundColors, esColor)
outLines = esCursor._getHighlightedLines(fr, to, outLines, esColor)
outLines = self._textCursor._getBlinkingCursors(fr, to, outLines, selectColor)
for y, l in enumerate(subLines):
t = outLines[l[0]-subLines[0][0]]
t = outLines[l[0]-subLines[0][0]]
bg = backgroundColors[l[0]-subLines[0][0]]
text:TTkString = t.substring(l[1][0],l[1][1]).tab2spaces(self._textWrap._tabSpaces)
if backgroundColor != TTkColor.RST:
text = text.completeColor(backgroundColor)
if bg != TTkColor.RST:
canvas.fill(color=bg,pos=(0,y), size=(w,1))
text = text.completeColor(bg)
canvas.drawTTkString(pos=(-ox,y), text=text)
if self._lineWrapMode == TTkK.FixedWidth:
canvas.drawVLine(pos=(self._textWrap._wrapWidth,0), size=h, color=lineColor)
class TTkTextEdit(TTkAbstractScrollArea):
__doc__ = '''
:py:class:`TTkTextEdit` is a container widget which place :py:class:`TTkTextEditView` in a scrolling area with on-demand scroll bars.
''' + TTkTextEditView.__doc__
ExtraSelection = TTkTextEditView.ExtraSelection
__slots__ = (
['_textEditView',
'_lineNumberView', '_lineNumber'] +
(_forwardedSignals:=[ # Forwarded Signals From TTkTexteditView
# Signals
'focusChanged', 'currentColorChanged',
'focusChanged', 'currentColorChanged', 'cursorPositionChanged',
'undoAvailable', 'redoAvailable',
'textChanged']) +
(_forwardedMethods:=[ # Forwarded Methods From TTkTexteditView
@ -723,6 +843,7 @@ class TTkTextEdit(TTkAbstractScrollArea):
'lineWrapMode', 'setLineWrapMode',
'wordWrapMode', 'setWordWrapMode',
'textCursor', 'setFocus', 'setColor',
'extraSelections', 'setExtraSelections',
'cut', 'copy', 'paste',
'undo', 'redo', 'isUndoAvailable', 'isRedoAvailable',
# Export Methods,

82
tests/t.draw/test.draw.009.colorMix.py

@ -0,0 +1,82 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2025 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys, os
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
def testColor(prefix:str,c1:ttk.TTkColor, c2:ttk.TTkColor):
str1 = ttk.TTkString('Eugenio',c1)
str2 = ttk.TTkString('Parodi' ,c2)
rst = "\033]8;;\033\\"
print(prefix + (str1+str2).toAnsi() + rst)
fg_r = ttk.TTkColor.FG_RED
fg_g = ttk.TTkColor.FG_GREEN
fg_b = ttk.TTkColor.FG_BLUE
bg_r = ttk.TTkColor.BG_RED
bg_g = ttk.TTkColor.BG_GREEN
bg_b = ttk.TTkColor.BG_BLUE
fg_r_l = ttk.TTkColor.fg('#FF0000',link='https://github.com/ceccopierangiolieugenio/pyTermTk')
fg_g_l = ttk.TTkColor.fg('#00FF00',link='https://github.com/ceccopierangiolieugenio/pyTermTk')
fg_b_l = ttk.TTkColor.fg('#0000FF',link='https://github.com/ceccopierangiolieugenio/pyTermTk')
bg_r_l = ttk.TTkColor.bg('#FF0000',link='https://github.com/ceccopierangiolieugenio/pyTermTk')
bg_g_l = ttk.TTkColor.bg('#00FF00',link='https://github.com/ceccopierangiolieugenio/pyTermTk')
bg_b_l = ttk.TTkColor.bg('#0000FF',link='https://github.com/ceccopierangiolieugenio/pyTermTk')
testColor("r ",fg_r, bg_r)
testColor("g ",fg_g, bg_g)
testColor("b ",fg_b, bg_b)
testColor("rl ",fg_r_l, bg_r_l)
testColor("gl ",fg_g_l, bg_g_l)
testColor("bl ",fg_b_l, bg_b_l)
c1 = fg_r + bg_g
c2 = fg_r | bg_g
testColor("T1 ",c1,c2)
m1 = bg_b + fg_g
m2 = bg_b | fg_g
testColor("T2 ",m1,m2)
m3 = ttk.TTkColor.FG_YELLOW + m1
m4 = m1 + ttk.TTkColor.FG_YELLOW
m5 = ttk.TTkColor.FG_YELLOW | m1
m6 = m1 | ttk.TTkColor.FG_YELLOW
testColor("M1 ",m3,m4)
testColor("M1 ",m5,m6)
m3 = ttk.TTkColor.BG_YELLOW + m1
m4 = m1 + ttk.TTkColor.BG_YELLOW
m5 = ttk.TTkColor.BG_YELLOW | m1
m6 = m1 | ttk.TTkColor.BG_YELLOW
testColor("M2 ",m3,m4)
testColor("M2 ",m5,m6)
m3 = ttk.TTkColor.RST + m1
m4 = m1 + ttk.TTkColor.RST
m5 = ttk.TTkColor.RST | m1
m6 = m1 | ttk.TTkColor.RST
testColor("M3 ",m3,m4)
testColor("M3 ",m5,m6)

173
tests/t.ui/test.ui.018.TextEdit.04.Pygments.py

@ -0,0 +1,173 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import sys
import random
import argparse
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
def demoTextEdit(root, filename):
frame = ttk.TTkFrame(parent=root, border=False, layout=ttk.TTkGridLayout())
te = ttk.TTkTextEdit()
te.setReadOnly(False)
with open(filename, 'r') as f:
content = f.read()
doc = ttk.TextDocumentHighlight(text=content)
te.setDocument(doc)
# use the widget size to wrap
# te.setLineWrapMode(ttk.TTkK.WidgetWidth)
# te.setWordWrapMode(ttk.TTkK.WordWrap)
# Use a fixed wrap size
# te.setLineWrapMode(ttk.TTkK.FixedWidth)
# te.setWrapWidth(100)
frame.layout().addWidget(te,1,0,1,10)
frame.layout().addWidget(ttk.TTkLabel(text="Wrap: ", maxWidth=6),0,0)
frame.layout().addWidget(lineWrap := ttk.TTkComboBox(list=['NoWrap','WidgetWidth','FixedWidth']),0,1)
frame.layout().addWidget(ttk.TTkLabel(text=" Type: ",maxWidth=7),0,2)
frame.layout().addWidget(wordWrap := ttk.TTkComboBox(list=['WordWrap','WrapAnywhere'], enabled=False),0,3)
frame.layout().addWidget(ttk.TTkLabel(text=" FixW: ",maxWidth=7),0,4)
frame.layout().addWidget(fixWidth := ttk.TTkSpinBox(value=te.wrapWidth(), maxWidth=6, maximum=500, minimum=10, enabled=False),0,5)
frame.layout().addWidget(ttk.TTkLabel(text=" Lexer: ",maxWidth=8),0,6)
frame.layout().addWidget(lexers := ttk.TTkComboBox(list=ttk.TextDocumentHighlight.getLexers()),0,7)
frame.layout().addWidget(ttk.TTkLabel(text=" Style: ",maxWidth=8),0,8)
frame.layout().addWidget(styles := ttk.TTkComboBox(list=ttk.TextDocumentHighlight.getStyles()),0,9)
lineWrap.setCurrentIndex(0)
wordWrap.setCurrentIndex(1)
fixWidth.valueChanged.connect(te.setWrapWidth)
lexers.currentTextChanged.connect(doc.setLexer)
styles.currentTextChanged.connect(doc.setStyle)
@ttk.pyTTkSlot(int)
def _lineWrapCallback(index):
if index == 0:
te.setLineWrapMode(ttk.TTkK.NoWrap)
wordWrap.setDisabled()
fixWidth.setDisabled()
elif index == 1:
te.setLineWrapMode(ttk.TTkK.WidgetWidth)
wordWrap.setEnabled()
fixWidth.setDisabled()
else:
te.setLineWrapMode(ttk.TTkK.FixedWidth)
wordWrap.setEnabled()
fixWidth.setEnabled()
lineWrap.currentIndexChanged.connect(_lineWrapCallback)
@ttk.pyTTkSlot(int)
def _wordWrapCallback(index):
if index == 0:
te.setWordWrapMode(ttk.TTkK.WordWrap)
else:
te.setWordWrapMode(ttk.TTkK.WrapAnywhere)
@ttk.pyTTkSlot(ttk.TTkTextCursor)
def _positionChanged(cursor:ttk.TTkTextCursor):
extra_selections = []
# Hiighlight YELLOW all the selected lines
cursor = te.textCursor().copy()
lines = []
for cur in cursor.cursors():
selSt = cur.selectionStart().line
selEn = cur.selectionEnd().line
lines += [x for x in range(selSt,selEn+1)]
cursor.clearCursors()
cursor.clearSelection()
for x in set(lines):
cursor.addCursor(x,0)
selection = ttk.TTkTextEdit.ExtraSelection(
cursor=cursor,
color=ttk.TTkColor.BG_YELLOW,
format=ttk.TTkK.SelectionFormat.FullWidthSelection)
extra_selections.append(selection)
# Highlight Red only the lines under the cursor positions
cursor = te.textCursor().copy()
cursor.clearSelection()
selection = ttk.TTkTextEdit.ExtraSelection(
cursor=cursor,
color=ttk.TTkColor.BG_RED,
format=ttk.TTkK.SelectionFormat.FullWidthSelection)
extra_selections.append(selection)
# Highlight GREEN the words under the cursor positions
cursor = te.textCursor().copy()
cursor.select(ttk.TTkTextCursor.SelectionType.WordUnderCursor)
selection = ttk.TTkTextEdit.ExtraSelection(
cursor=cursor,
color=ttk.TTkColor.BG_GREEN)
extra_selections.append(selection)
te.setExtraSelections(extra_selections)
wordWrap.currentIndexChanged.connect(_wordWrapCallback)
te.cursorPositionChanged.connect(_positionChanged)
return frame
def main():
parser = argparse.ArgumentParser()
parser.add_argument('filename', type=str, nargs='+',
help='the filename/s')
args = parser.parse_args()
ttk.TTkTheme.loadTheme(ttk.TTkTheme.NERD)
root = ttk.TTk(layout=ttk.TTkGridLayout())
appTemplate = ttk.TTkAppTemplate(parent=root)
appTemplate.setWidget(fileTree := ttk.TTkFileTree(), position=ttk.TTkK.LEFT, size=30)
appTemplate.setItem(layoutArea := ttk.TTkLayout(), position=appTemplate.Position.MAIN)
def _openFile(fileName):
newPos = (0,0)
oldPos = [win.pos() for win in layoutArea.children()]
while newPos in oldPos:
newPos = (newPos[0]+1,newPos[1]+1,)
win = ttk.TTkWindow(pos = newPos, size=(100,40), title=f"Test Text Edit ({fileName})", layout=ttk.TTkGridLayout(), border=True)
layoutArea.addWidget(win)
demoTextEdit(win, fileName)
for file in args.filename:
_openFile(file)
fileTree.fileActivated.connect(lambda x: _openFile(x.path()))
root.mainloop()
if __name__ == "__main__":
main()
Loading…
Cancel
Save