Browse Source

TTkTabWidget: Added closable feature

pull/44/head
Eugenio Parodi 4 years ago
parent
commit
5694b77ef9
  1. 8
      TermTk/TTkCore/canvas.py
  2. 92
      TermTk/TTkWidgets/tabwidget.py
  3. 5
      TermTk/TTkWidgets/texedit.py
  4. 2
      demo/showcase/tab.py
  5. 12
      demo/ttkode.py
  6. 25
      docs/MDNotes/Resources.md

8
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]

92
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):

5
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

2
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")

12
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

25
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
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
Loading…
Cancel
Save