From b48d2f0bb853ede9a123a0a7f8e7d198f255a437 Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Thu, 29 Feb 2024 16:27:08 +0000 Subject: [PATCH] Initial drop of the Dumb Paint Tool --- tools/dumb.paint.tool.py | 77 +++++++++ tools/dumb_paint_lib/__init__.py | 2 + tools/dumb_paint_lib/paintarea.py | 207 +++++++++++++++++++++++++ tools/dumb_paint_lib/textarea.py | 250 ++++++++++++++++++++++++++++++ 4 files changed, 536 insertions(+) create mode 100755 tools/dumb.paint.tool.py create mode 100644 tools/dumb_paint_lib/__init__.py create mode 100644 tools/dumb_paint_lib/paintarea.py create mode 100644 tools/dumb_paint_lib/textarea.py diff --git a/tools/dumb.paint.tool.py b/tools/dumb.paint.tool.py new file mode 100755 index 00000000..e1a92840 --- /dev/null +++ b/tools/dumb.paint.tool.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2024 Eugenio Parodi +# +# 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, argparse + +sys.path.append(os.path.join(sys.path[0],'..')) +import TermTk as ttk + +from dumb_paint_lib import * + +ttk.TTkTheme.loadTheme(ttk.TTkTheme.NERD) + +class PaintTemplate(ttk.TTkAppTemplate): + def __init__(self, border=False, **kwargs): + super().__init__(border, **kwargs) + + self.setWidget(pa:=PaintArea() , self.MAIN) + self.setItem( ptk:=PaintToolKit(), self.TOP, size=3, fixed=True) + self.setItem( ta:=TextArea() , self.RIGHT, size=50) + + self.setMenuBar(appMenuBar:=ttk.TTkMenuBarLayout(), self.TOP) + fileMenu = appMenuBar.addMenu("&File") + buttonOpen = fileMenu.addMenu("&Open") + buttonClose = fileMenu.addMenu("&Save") + buttonClose = fileMenu.addMenu("Save &As...") + fileMenu.addSpacer() + buttonExit = fileMenu.addMenu("E&xit") + buttonExit.menuButtonClicked.connect(ttk.TTkHelper.quit) + + # extraMenu = appMenuBar.addMenu("E&xtra") + # extraMenu.addMenu("Scratchpad").menuButtonClicked.connect(self.scratchpad) + # extraMenu.addSpacer() + + helpMenu = appMenuBar.addMenu("&Help", alignment=ttk.TTkK.RIGHT_ALIGN) + helpMenu.addMenu("About ...").menuButtonClicked + helpMenu.addMenu("About tlogg").menuButtonClicked + + ptk.updatedColor.connect(pa.setGlyphColor) + ta.charSelected.connect(ptk.glyphFromString) + ta.charSelected.connect(pa.glyphFromString) + + + +root = ttk.TTk( + title="Dumb Paint Tool", + layout=ttk.TTkGridLayout(), + mouseTrack=True, + sigmask=( + # ttk.TTkTerm.Sigmask.CTRL_C | + ttk.TTkTerm.Sigmask.CTRL_Q | + ttk.TTkTerm.Sigmask.CTRL_S | + ttk.TTkTerm.Sigmask.CTRL_Z )) + +PaintTemplate(parent=root) + +root.mainloop() \ No newline at end of file diff --git a/tools/dumb_paint_lib/__init__.py b/tools/dumb_paint_lib/__init__.py new file mode 100644 index 00000000..0e08a46d --- /dev/null +++ b/tools/dumb_paint_lib/__init__.py @@ -0,0 +1,2 @@ +from .paintarea import * +from .textarea import * \ No newline at end of file diff --git a/tools/dumb_paint_lib/paintarea.py b/tools/dumb_paint_lib/paintarea.py new file mode 100644 index 00000000..0e616bc2 --- /dev/null +++ b/tools/dumb_paint_lib/paintarea.py @@ -0,0 +1,207 @@ +# MIT License +# +# Copyright (c) 2024 Eugenio Parodi +# +# 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. + +__all__ = ['PaintArea','PaintToolKit'] + +import sys, os + +sys.path.append(os.path.join(sys.path[0],'../..')) +import TermTk as ttk + + +class PaintToolKit(ttk.TTkGridLayout): + __slots__ = ('_rSelect', '_rPaint', '_lgliph', '_cbFg', '_cbBg', '_bpFg', '_bpBg', + '_glyph', + #Signals + 'updatedColor') + def __init__(self, *args, **kwargs): + self._glyph = 'X' + super().__init__(*args, **kwargs) + self.updatedColor = ttk.pyTTkSignal(ttk.TTkColor) + self._rSelect = ttk.TTkRadioButton(text='Select ' , maxWidth=10) + self._rPaint = ttk.TTkRadioButton(text='Paint ' ) + self._lgliph = ttk.TTkLabel(text="" , maxWidth=8) + self._cbFg = ttk.TTkCheckbox(text="Fg" , maxWidth= 6) + self._cbBg = ttk.TTkCheckbox(text="Bg" ) + self._bpFg = ttk.TTkColorButtonPicker(enabled=False, maxWidth= 6) + self._bpBg = ttk.TTkColorButtonPicker(enabled=False, ) + self.addWidget(self._rSelect ,0,0) + self.addWidget(self._rPaint ,1,0) + self.addWidget(self._lgliph ,0,1,2,1) + self.addWidget(self._cbFg ,0,2) + self.addWidget(self._cbBg ,1,2) + self.addWidget(self._bpFg ,0,3) + self.addWidget(self._bpBg ,1,3) + self.addItem(ttk.TTkLayout() ,0,4,3,1) + + self._cbFg.toggled.connect(self._bpFg.setEnabled) + self._cbBg.toggled.connect(self._bpBg.setEnabled) + self._cbFg.toggled.connect(self._refreshColor) + self._cbBg.toggled.connect(self._refreshColor) + + self._bpFg.colorSelected.connect(self._refreshColor) + self._bpBg.colorSelected.connect(self._refreshColor) + + self._refreshColor(emit=False) + + @ttk.pyTTkSlot() + def _refreshColor(self, emit=True): + color =self.color() + self._lgliph.setText( + ttk.TTkString("Glyph\n '") + + ttk.TTkString(self._glyph,color) + + ttk.TTkString("'")) + if emit: + self.updatedColor.emit(color) + + + @ttk.pyTTkSlot(ttk.TTkString) + def glyphFromString(self, ch:ttk.TTkString): + if len(ch)<=0: return + self._glyph = ch.charAt(0) + self.setColor(ch.colorAt(0)) + + def color(self): + color = ttk.TTkColor() + if self._cbFg.checkState() == ttk.TTkK.Checked: + color += self._bpFg.color().invertFgBg() + if self._cbBg.checkState() == ttk.TTkK.Checked: + color += self._bpBg.color() + return color + + def setColor(self, color:ttk.TTkColor): + if fg := color.foreground(): + self._cbFg.setCheckState(ttk.TTkK.Checked) + self._bpFg.setEnabled() + self._bpFg.setColor(fg.invertFgBg()) + else: + self._cbFg.setCheckState(ttk.TTkK.Unchecked) + self._bpFg.setDisabled() + + if bg := color.background(): + self._cbBg.setCheckState(ttk.TTkK.Checked) + self._bpBg.setEnabled() + self._bpBg.setColor(bg) + else: + self._cbBg.setCheckState(ttk.TTkK.Unchecked) + self._bpBg.setDisabled() + self._refreshColor(emit=False) + +class PaintArea(ttk.TTkWidget): + + __slots__ = ('_canvasArea', '_canvasSize', + '_transparentColor', + '_mouseMove', + '_glyph', '_glyphColor') + + def __init__(self, *args, **kwargs): + self._transparentColor = ttk.TTkColor.bg('#FF00FF') + self._canvasSize = (0,0) + self._canvasArea = {'data':[],'colors':[]} + self._glyph = 'X' + self._glyphColor = ttk.TTkColor.fg("#0000FF") + self._mouseMove = None + super().__init__(*args, **kwargs) + self.resizeCanvas(80,25) + self.setFocusPolicy(ttk.TTkK.ClickFocus + ttk.TTkK.TabFocus) + + def resizeCanvas(self, w, h): + self._canvasSize = (w,h) + self._canvasArea['data'] = (self._canvasArea['data'] + [[] for _ in range(h)])[:h] + self._canvasArea['colors'] = (self._canvasArea['colors'] + [[] for _ in range(h)])[:h] + for i in range(h): + self._canvasArea['data'][i] = (self._canvasArea['data'][i] + [' ' for _ in range(w)])[:w] + self._canvasArea['colors'][i] = (self._canvasArea['colors'][i] + [ttk.TTkColor.RST for _ in range(w)])[:w] + self.update() + + def mouseMoveEvent(self, evt) -> bool: + x,y = evt.x, evt.y + w,h = self._canvasSize + if 0<=x bool: + if self._placeGlyph(evt.x, evt.y): + return True + return super().mouseDragEvent(evt) + + def mousePressEvent(self, evt) -> bool: + if self._placeGlyph(evt.x, evt.y): + return True + return super().mousePressEvent(evt) + + @ttk.pyTTkSlot(ttk.TTkString) + def glyphFromString(self, ch:ttk.TTkString): + if len(ch)<=0: return + self._glyph = ch.charAt(0) + self._glyphColor = ch.colorAt(0) + + def glyph(self): + return self._glyph + def setGlyph(self, glyph): + if len(glyph) <= 0: return + if type(glyph)==str: + self._glyph = glyph[0] + if type(glyph)==ttk.TTkString: + self._glyph = glyph.charAt(0) + self._glyphColor = glyph.colorAt(0) + + def glyphColor(self): + return self._glyphColor + def setGlyphColor(self, color): + self._glyphColor = color + + def _placeGlyph(self,x,y): + self._mouseMove = None + w,h = self._canvasSize + data = self._canvasArea['data'] + colors = self._canvasArea['colors'] + if 0<=x +# +# 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. + +__all__ = ['TextArea'] + +import sys, os + +sys.path.append(os.path.join(sys.path[0],'../..')) +import TermTk as ttk + + + + + + + + + +_StartingText = '''Ansi Editor: + +─ ┌─┬┐ ┏━┳┓ ┎─┰┒ ┍━┯┑ ┏┭┲━┱┮┓ +━ │ ││ ┃ ┃┃ ┃ ┃┃ │ ││ ┞╁╀┰╀╁┦ +│ ├─┼┤ ┣━╋┫ ┠─╂┨ ┝━┿┥ ┟╄╈╇╈╃┧ +┃ └─┴┘ ┗━┻┛ ┖─┸┚ ┕━┷┙ ┡┽╊┷╉┾┩ +┄ ╴ ╵╷ ╸ ╹╻ ╼ ╽╿ ╽╵┞╼┩┕┙╻ +┅ ╶ ╺ ╾ ╿╷┝╾┤┌┮╋╸ +┆ ┌─┬┐ ╔═╦╗ ╓─╥╖ ╒═╤╕ ┢┽╆━╅┾┪╹ +┇ │ ││ ║ ║║ ║ ║║ │ ││ ┗┵┺━┹┶┛ +┈ ├─┼┤ ╠═╬╣ ╟─╫╢ ╞═╪╡ ┞ ┦ ┭ ┵ ┽ ╃ ╇ +┉ └─┴┘ ╚═╩╝ ╙─╨╜ ╘═╧╛ ┟ ┧ ┮ ┶ ┾ ╄ ╈ +┊ ╭───────────────────╮ ┡ ┩ ┱ ┹ ╀ ╅ ╉ +┋ │ ╔═══╗ Some Text │░ ┢ ┪ ┲ ┺ ╁ ╆ ╊ +╌ │ ╚═╦═╝ in the box │▒ +╍ ╞═╤══╩══╤═══════════╡▒ ╭─────╮ +╎ │ ├──┬──┤ │▒ ╭ │╲ ╳ ╱│ +╏ │ ╰──┴──╯ │▓ ╮ │ ╳╳╳ │ +═ └───────────────────┘▓ ╯ ╰─────╯ +║ ░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓█ ╰ ╱ ╲ ╳ + +▄ ▀ █ - Half,Full +▁ ▂ ▃ ▄ ▅ ▆ ▇ █ + +█ Full █ ▌ ▐ ▏ ▕ ▔ ▁ +▉ ░ ▒ ▓ █ +▊ +▋ ▘ ▚ ▟ +▌ ▝ ▞ ▙ +▍ ▖ ▌ ▜ +▎ ▗ ▐ ▛ +▏ 1/8 + ◢ ◣ ◤ ◥ ■ □ ▢ ▣ + +🬼 🭇 🭗 🭢 🭨 +🬽 🭈 🭘 🭣 🭩 +🬾 🭉 🭙 🭤 🭪 +🬿 🭊 🭚 🭥 🭫 +🭀 🭋 🭛 🭦 🭬 +🭌 🭁 🭝 🭒 🭭 +🭍 🭂 🭞 🭓 🭮 +🭎 🭃 🭟 🭔 🭯 +🭏 🭄 🭠 🭕 🮚 +🭐 🭅 🭡 🭖 🮛 +🭑 🭆 🭜 🭧 + + 🭇🬼 + 🭃🭌🬿 + 🭥🭒█🭏🬼 + 🭋🭍🭑🬽🭢🭕█🭌🬿 + 🭅███🭀 🭥🭒█🭏🬼 +🭋████🭐 🭢🭕█🭌🬿 +🭅██🭞🭜🭘 🭈🭆🭂███🭏🬼 +🭣🭧🭚 🭈🭆🭂██████🭠🭗 + 🭥🭒██🭝🭚 + 🭢🭕🭠🭗 + +''' +class TextArea(ttk.TTkGridLayout): + __slots__ = ('_te','charSelected') + def __init__(self, *args, **kwargs): + self.charSelected = ttk.pyTTkSignal(ttk.TTkString) + super().__init__(*args, **kwargs) + + self._te = ttk.TTkTextEdit(lineNumber=True, readOnly=False) + self._te.setText(_StartingText) + + self.addItem(wrapLayout := ttk.TTkGridLayout(), 0,0) + self.addItem(fontLayout := ttk.TTkGridLayout(columnMinWidth=1), 1,0) + self.addWidget(self._te,2,0,1,2) + + wrapLayout.addWidget(ttk.TTkLabel(text="Wrap: ", maxWidth=6),0,0) + wrapLayout.addWidget(lineWrap := ttk.TTkComboBox(list=['NoWrap','WidgetWidth','FixedWidth'], maxWidth=20),0,1) + wrapLayout.addWidget(ttk.TTkLabel(text=" Type: ",maxWidth=7),0,2) + wrapLayout.addWidget(wordWrap := ttk.TTkComboBox(list=['WordWrap','WrapAnywhere'], maxWidth=20, enabled=False),0,3) + wrapLayout.addWidget(ttk.TTkLabel(text=" FixW: ",maxWidth=7),0,4) + wrapLayout.addWidget(fixWidth := ttk.TTkSpinBox(value=self._te.wrapWidth(), maxWidth=5, maximum=500, minimum=10, enabled=False),0,5) + wrapLayout.addWidget(ttk.TTkSpacer(),0,10) + + # Empty columns/cells are 1 char wide due to "columnMinWidth=1" parameter in the GridLayout + # 1 3 8 11 + # 0 2 4 5 6 7 9 10 12 + # 0 [ ] FG [ ] BG [ ] LineNumber + # 1 ┌─────┐ ┌─────┐ ╒═══╕╒═══╕╒═══╕╒═══╕ ┌──────┐┌──────┐ + # 2 │ │ │ │ │ a ││ a ││ a ││ a │ │ UNDO ││ REDO │ + # 3 └─────┘ └─────┘ └───┘└───┘└───┘└───┘ ╘══════╛└──────┘ ┕━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┙ + + # Char Fg/Bg buttons + fontLayout.addWidget(cb_fg := ttk.TTkCheckbox(text=" FG"),0,0) + fontLayout.addWidget(btn_fgColor := ttk.TTkColorButtonPicker(border=True, enabled=False, maxSize=(7,3)),1,0) + + fontLayout.addWidget(cb_bg := ttk.TTkCheckbox(text=" BG"),0,2) + fontLayout.addWidget(btn_bgColor := ttk.TTkColorButtonPicker(border=True, enabled=False, maxSize=(7 ,3)),1,2) + + fontLayout.addWidget(cb_linenumber := ttk.TTkCheckbox(text=" LineNumber", checked=True),0,4,1,3) + + # Char style buttons + fontLayout.addWidget(btn_bold := ttk.TTkButton(border=True, maxSize=(5,3), checkable=True, text=ttk.TTkString( 'a' , ttk.TTkColor.BOLD) ),1,4) + fontLayout.addWidget(btn_italic := ttk.TTkButton(border=True, maxSize=(5,3), checkable=True, text=ttk.TTkString( 'a' , ttk.TTkColor.ITALIC) ),1,5) + fontLayout.addWidget(btn_underline := ttk.TTkButton(border=True, maxSize=(5,3), checkable=True, text=ttk.TTkString(' a ', ttk.TTkColor.UNDERLINE) ),1,6) + fontLayout.addWidget(btn_strikethrough := ttk.TTkButton(border=True, maxSize=(5,3), checkable=True, text=ttk.TTkString(' a ', ttk.TTkColor.STRIKETROUGH)),1,7) + + # Undo/Redo buttons + fontLayout.addWidget(btn_undo := ttk.TTkButton(border=True, maxSize=(8,3), enabled=self._te.isUndoAvailable(), text=' UNDO '),1,9 ) + fontLayout.addWidget(btn_redo := ttk.TTkButton(border=True, maxSize=(8,3), enabled=self._te.isRedoAvailable(), text=' REDO '),1,10) + # Undo/Redo events + self._te.undoAvailable.connect(btn_undo.setEnabled) + self._te.redoAvailable.connect(btn_redo.setEnabled) + btn_undo.clicked.connect(self._te.undo) + btn_redo.clicked.connect(self._te.redo) + + @ttk.pyTTkSlot(ttk.TTkColor) + def _currentColorChangedCB(format): + if fg := format.foreground(): + cb_fg.setCheckState(ttk.TTkK.Checked) + btn_fgColor.setEnabled() + btn_fgColor.setColor(fg.invertFgBg()) + else: + cb_fg.setCheckState(ttk.TTkK.Unchecked) + btn_fgColor.setDisabled() + + if bg := format.background(): + cb_bg.setCheckState(ttk.TTkK.Checked) + btn_bgColor.setEnabled() + btn_bgColor.setColor(bg) + else: + cb_bg.setCheckState(ttk.TTkK.Unchecked) + btn_bgColor.setDisabled() + + btn_bold.setChecked(format.bold()) + btn_italic.setChecked(format.italic()) + btn_underline.setChecked(format.underline()) + btn_strikethrough.setChecked(format.strikethrough()) + # ttk.TTkLog.debug(f"{fg=} {bg=} {bold=} {italic=} {underline=} {strikethrough= }") + + self._te.currentColorChanged.connect(_currentColorChangedCB) + + def _setStyle(): + color = ttk.TTkColor() + if cb_fg.checkState() == ttk.TTkK.Checked: + color += btn_fgColor.color().invertFgBg() + if cb_bg.checkState() == ttk.TTkK.Checked: + color += btn_bgColor.color() + if btn_bold.isChecked(): + color += ttk.TTkColor.BOLD + if btn_italic.isChecked(): + color += ttk.TTkColor.ITALIC + if btn_underline.isChecked(): + color += ttk.TTkColor.UNDERLINE + if btn_strikethrough.isChecked(): + color += ttk.TTkColor.STRIKETROUGH + cursor = self._te.textCursor() + cursor.applyColor(color) + cursor.setColor(color) + self._te.setFocus() + + cb_fg.toggled.connect(btn_fgColor.setEnabled) + cb_bg.toggled.connect(btn_bgColor.setEnabled) + cb_fg.clicked.connect(_setStyle) + cb_bg.clicked.connect(_setStyle) + + cb_linenumber.toggled.connect(self._te.setLineNumber) + + btn_fgColor.colorSelected.connect(_setStyle) + btn_bgColor.colorSelected.connect(_setStyle) + + btn_bold.clicked.connect(_setStyle) + btn_italic.clicked.connect(_setStyle) + btn_underline.clicked.connect(_setStyle) + btn_strikethrough.clicked.connect(_setStyle) + + lineWrap.setCurrentIndex(0) + wordWrap.setCurrentIndex(1) + + fixWidth.valueChanged.connect(self._te.setWrapWidth) + + @ttk.pyTTkSlot(int) + def _lineWrapCallback(index): + if index == 0: + self._te.setLineWrapMode(ttk.TTkK.NoWrap) + wordWrap.setDisabled() + fixWidth.setDisabled() + elif index == 1: + self._te.setLineWrapMode(ttk.TTkK.WidgetWidth) + wordWrap.setEnabled() + fixWidth.setDisabled() + else: + self._te.setLineWrapMode(ttk.TTkK.FixedWidth) + self._te.setWrapWidth(fixWidth.value()) + wordWrap.setEnabled() + fixWidth.setEnabled() + + lineWrap.currentIndexChanged.connect(_lineWrapCallback) + + @ttk.pyTTkSlot(int) + def _wordWrapCallback(index): + if index == 0: + self._te.setWordWrapMode(ttk.TTkK.WordWrap) + else: + self._te.setWordWrapMode(ttk.TTkK.WrapAnywhere) + + wordWrap.currentIndexChanged.connect(_wordWrapCallback) + + self._te.document().cursorPositionChanged.connect(self._cursorPositionChanged) + + @ttk.pyTTkSlot(ttk.TTkTextCursor) + def _cursorPositionChanged(self, cursor): + ch = cursor.positionChar() + color = cursor.positionColor() + self.charSelected.emit(ttk.TTkString(ch,color)) + + def te(self): + return self._te