diff --git a/TermTk/TTkCore/canvas.py b/TermTk/TTkCore/canvas.py index 2430993e..145ecdf0 100644 --- a/TermTk/TTkCore/canvas.py +++ b/TermTk/TTkCore/canvas.py @@ -215,7 +215,10 @@ class TTkCanvas: for i in range(max(0,-x), min(len(txt),self._width-x)): #self._set(y, x+i, txt[i-x], colors[i-x]) self._data[y][x+i] = txt[i] - self._colors[y][x+i] = colors[i].mod(x+i,y) + if colors[i] == TTkColor.RST != color: + self._colors[y][x+i] = color.mod(x+i,y) + else: + self._colors[y][x+i] = colors[i].mod(x+i,y) else: text = text.replace('\t',' ') if lentxt < width: @@ -368,10 +371,11 @@ class TTkCanvas: textPos = (x,y+1) self.drawText(pos=textPos, text=text, color=color) - def drawTabButton(self, pos, size, label, sideEnd, small, status, color=TTkColor.RST): + def drawTabButton(self, pos, size, sideEnd, small, status, color=TTkColor.RST): x,y = pos w,h = size tt = TTkCfg.theme.tab + label = ' '*(w-2) if small: if status == TTkK.Checked: txtCenter = tt[10] + label + tt[10] diff --git a/TermTk/TTkWidgets/tabwidget.py b/TermTk/TTkWidgets/tabwidget.py index 60175fe0..4fab458d 100644 --- a/TermTk/TTkWidgets/tabwidget.py +++ b/TermTk/TTkWidgets/tabwidget.py @@ -58,20 +58,28 @@ class _TTkTabBarDragData(): def tabBar(self): return self._tabBar class TTkTabButton(TTkButton): - __slots__ = ('_sideEnd', '_tabStatus') + __slots__ = ('_sideEnd', '_tabStatus', '_closable', 'closeClicked', '_closeButton') def __init__(self, *args, **kwargs): self._sideEnd = TTkK.NONE self._tabStatus = TTkK.Unchecked + self._closable = kwargs.get('closable', False) + self.closeClicked = pyTTkSignal() TTkButton.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkTabButton' ) + size = len(self.text) + 2 + if self._closable: + size += 3 + self._closeButton = TTkButton(parent=self, border=False, text="x", pos=(size-4,1 if self._border else 0), size=(3,1)) + self._closeButton.setFocusPolicy(TTkK.ParentFocus) + self._closeButton.clicked.connect(self.closeClicked.emit) if self._border: - self.resize(len(self._text)+2, 3) - self.setMinimumSize(2+len(self._text), 3) - self.setMaximumSize(2+len(self._text), 3) + self.resize(size, 3) + self.setMinimumSize(size, 3) + self.setMaximumSize(size, 3) else: - self.resize(len(self._text)+2, 2) - self.setMinimumSize(len(self._text)+2, 2) - self.setMaximumSize(len(self._text)+2, 2) + self.resize(size, 2) + self.setMinimumSize(size, 2) + self.setMaximumSize(size, 2) self.setFocusPolicy(TTkK.ParentFocus) def sideEnd(self): @@ -91,6 +99,9 @@ class TTkTabButton(TTkButton): # This is a hack to force the action aftet the keypress # And not key release as normally happen to the button def mousePressEvent(self, evt): + if self._closable and evt.key == TTkK.MidButton: + self.closeClicked.emit() + return True return super().mouseReleaseEvent(evt) def mouseReleaseEvent(self, evt): return False @@ -115,7 +126,7 @@ class TTkTabButton(TTkButton): pos=(0,0), size=self.size(), small=(not self._border), sideEnd=self._sideEnd, status=self._tabStatus, - label=self.text, color=self._borderColor ) + color=self._borderColor ) self._canvas.drawText(pos=(1,1 if self._border else 0), text=self.text, color=self.color()) class _TTkTabMenuButton(TTkMenuButton): @@ -211,21 +222,22 @@ _labels= │◀│La│Label1║Label2║Label3│Label4│▶│ class TTkTabBar(TTkWidget): __slots__ = ( - '_tabButtons', '_small', + '_tabButtons', '_tabData', '_small', '_highlighted', '_currentIndex','_lastIndex', '_leftScroller', '_rightScroller', - '_borderColor', + '_borderColor', '_tabClosable', '_sideEnd', #Signals - 'currentChanged', 'tabBarClicked') + 'currentChanged', 'tabBarClicked', 'tabCloseRequested') def __init__(self, *args, **kwargs): self._tabButtons = [] + self._tabData = [] self._currentIndex = -1 self._lastIndex = -1 self._highlighted = -1 self._tabMovable = False - self._tabClosable = False + self._tabClosable = kwargs.get('closable',False) self._sideEnd = TTkK.LEFT | TTkK.RIGHT self._borderColor = TTkCfg.theme.tabBorderColor self._small = kwargs.get('small',True) @@ -245,8 +257,9 @@ class TTkTabBar(TTkWidget): self.layout().addWidget(self._rightScroller) # Signals - self.currentChanged = pyTTkSignal(int) - self.tabBarClicked = pyTTkSignal(int) + self.currentChanged = pyTTkSignal(int) + self.tabBarClicked = pyTTkSignal(int) + self.tabCloseRequested = pyTTkSignal(int) self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus) @@ -259,27 +272,37 @@ class TTkTabBar(TTkWidget): self._leftScroller.setSideEnd(sideEnd&TTkK.LEFT) self._updateTabs() - def addTab(self, label): + def addTab(self, label, data=None): self.insertTab(len(self._tabButtons), label) - def insertTab(self, index, label): - button = TTkTabButton(parent=self, text=label, border=not self._small) + def insertTab(self, index, label, data=None): + button = TTkTabButton(parent=self, text=label, border=not self._small, closable=self._tabClosable) button._borderColor = self._borderColor self._tabButtons.insert(index,button) + self._tabData.insert(index,data) button.clicked.connect(lambda :self.setCurrentIndex(self._tabButtons.index(button))) + button.closeClicked.connect(lambda :self.tabCloseRequested.emit(self._tabButtons.index(button))) self._updateTabs() + @pyTTkSlot(int) def removeTab(self, index): button = self._tabButtons[index] self.layout().removeWidget(button) - self._tabButtons = self._tabButtons[:index] + self._tabButtons[index+1:] + self._tabButtons.pop(index) + self._tabData.pop(index) if self._currentIndex == index: - self._lastIndex = -1 + self._lastIndex = -2 if self._currentIndex >= index: self._currentIndex -= 1 self._highlighted = self._currentIndex self._updateTabs() + def data(self, index): + return self._tabData[index] + + def setData(self, index, data): + self._tabData[index] = data + def borderColor(self): return self._borderColor @@ -291,6 +314,12 @@ class TTkTabBar(TTkWidget): self._rightScroller.setBorderColor(color) self.update() + def tabsClosable(self): + return self._tabClosable + + def setTabsClosable(self, closable): + self._tabClosable = closable + def currentIndex(self): return self._currentIndex @@ -444,6 +473,8 @@ class TTkTabWidget(TTkFrame): # Forward Signals 'currentChanged', 'tabBarClicked', # forward methods + 'tabsClosable', 'setTabsClosable', + 'data', 'setData', 'currentIndex', 'setCurrentIndex') def __init__(self, *args, **kwargs): @@ -454,7 +485,7 @@ class TTkTabWidget(TTkFrame): TTkFrame.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkTabWidget') - self._tabBar = TTkTabBar(small = not self.border()) + self._tabBar = TTkTabBar(small = not self.border(), closable=kwargs.get('closable', False)) self._topLeftLayout = None self._topRightLayout = None self._tabBarTopLayout.addWidget(self._tabBar,0,1,3 if self.border() else 2,1) @@ -474,13 +505,21 @@ class TTkTabWidget(TTkFrame): self.rootLayout().addItem(self._tabBarTopLayout) self._tabBarTopLayout.setGeometry(0,0,self._width,self._padt) + self._tabBar.tabCloseRequested.connect(self.removeTab) # forwarded methods self.currentIndex = self._tabBar.currentIndex self.setCurrentIndex = self._tabBar.setCurrentIndex + self.data = self._tabBar.data + self.setData = self._tabBar.setData + self.tabsClosable = self._tabBar.tabsClosable + self.setTabsClosable = self._tabBar.setTabsClosable # forwarded Signals self.currentChanged = self._tabBar.currentChanged self.tabBarClicked = self._tabBar.tabBarClicked + def widget(self, index): + return self._tabWidgets[index] + def currentWidget(self): for w in self._tabWidgets: if w.isVisible(): @@ -539,7 +578,7 @@ class TTkTabWidget(TTkFrame): tw.removeTab(index) self.insertTab(newIndex, widget, tb.text) self.setCurrentIndex(newIndex) - self._tabChanged(newIndex) + #self._tabChanged(newIndex) elif tw != self: tw.removeTab(index) newIndex = len(self._tabWidgets) @@ -566,21 +605,22 @@ class TTkTabWidget(TTkFrame): layout.addWidget(button) return button - def addTab(self, widget, label): + def addTab(self, widget, label, data=None): widget.hide() self._tabWidgets.append(widget) self.layout().addWidget(widget) - self._tabBar.addTab(label) + self._tabBar.addTab(label, data) - def insertTab(self, index, widget, label): + def insertTab(self, index, widget, label, data=None): widget.hide() self._tabWidgets.insert(index, widget) self.layout().addWidget(widget) - self._tabBar.insertTab(index, label) + self._tabBar.insertTab(index, label, data) + @pyTTkSlot(int) def removeTab(self, index): self.layout().removeWidget(self._tabWidgets[index]) - self._tabWidgets = self._tabWidgets[:index] + self._tabWidgets[index+1:] + self._tabWidgets.pop(index) self._tabBar.removeTab(index) def resizeEvent(self, w, h): diff --git a/TermTk/TTkWidgets/texedit.py b/TermTk/TTkWidgets/texedit.py index 948cf9f9..bd79f728 100644 --- a/TermTk/TTkWidgets/texedit.py +++ b/TermTk/TTkWidgets/texedit.py @@ -504,6 +504,8 @@ class TTkTextEdit(TTkAbstractScrollArea): super().__init__(*args, **kwargs) self._name = kwargs.get('name' , 'TTkTextEdit' ) self._textEditView = _TTkTextEditView() + # self.setFocusPolicy(self._textEditView.focusPolicy()) + # self._textEditView.setFocusPolicy(TTkK.ParentFocus) self.setViewport(self._textEditView) self.clear = self._textEditView.clear self.setText = self._textEditView.setText @@ -517,4 +519,5 @@ class TTkTextEdit(TTkAbstractScrollArea): self.setLineWrapMode = self._textEditView.setLineWrapMode self.wordWrapMode = self._textEditView.wordWrapMode self.setWordWrapMode = self._textEditView.setWordWrapMode - + # Forward Signals + self.focusChanged = self._textEditView.focusChanged diff --git a/demo/showcase/tab.py b/demo/showcase/tab.py index 63e6c960..eb1fbf7c 100755 --- a/demo/showcase/tab.py +++ b/demo/showcase/tab.py @@ -28,7 +28,7 @@ sys.path.append(os.path.join(sys.path[0],'../..')) import TermTk as ttk def demoTab(root=None, border=True): - tabWidget1 = ttk.TTkTabWidget(parent=root, border=border) + tabWidget1 = ttk.TTkTabWidget(parent=root, border=border, closable=True) tabWidget1.addTab(ttk.TTkTestWidgetSizes(border=True, title="Frame1.1"), "Label 1.1") tabWidget1.addTab(ttk.TTkTestWidgetSizes(border=True, title="Frame1.2"), "Label 1.2") tabWidget1.addTab(ttk.TTkTestWidget( border=True, title="Frame1.3"), "Label Test 1.3") diff --git a/demo/ttkode.py b/demo/ttkode.py index 553e7191..a8454a24 100755 --- a/demo/ttkode.py +++ b/demo/ttkode.py @@ -136,12 +136,14 @@ class KodeTab(TTkTabWidget): else: ret = super().dropEvent(evt) + # Remove the widget and/or all the cascade empty splitters if not tw._tabWidgets: - splitter = tw.parentWidget() - if splitter.count() == 1: - splitter.parentWidget().removeWidget(splitter) - else: - splitter.removeWidget(tw) + widget = tw + splitter = widget.parentWidget() + while splitter.count() == 1: + widget = splitter + splitter = widget.parentWidget() + splitter.removeWidget(widget) return ret diff --git a/docs/MDNotes/Resources.md b/docs/MDNotes/Resources.md index 6492699f..59b99ee7 100644 --- a/docs/MDNotes/Resources.md +++ b/docs/MDNotes/Resources.md @@ -23,4 +23,27 @@ https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda #### Domains - docstring syntax https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#directive-py-class #### ReStructuredText -https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#explicit-markup-blocks \ No newline at end of file +https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#explicit-markup-blocks + +## Disable SIGSTOP triggered by CTRL+S +```python +import sys, termios + +attr = termios.tcgetattr(sys.stdin) +# Save the value to be restored +bak = attr[6][termios.VSTOP] + +# Disable SIGSTOP triggered by CTRL+S +attr[6][termios.VSTOP]=0 +termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr) + +'''. . . do stuff . . .''' + +# reEnable SIGSTOP triggered by CTRL+S +attr[6][termios.VSTOP]=bak +termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr) +``` +### Terminal Mapping: + - CTRL-C -> termios.VINTR + - CTRL-S -> termios.VSTOP + - CTRL-Z -> termios.VSUSP \ No newline at end of file