From 2841ecc5ae4f2cbde1cda04ae2bca3182332728e Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Fri, 9 Jun 2023 16:40:02 +0100 Subject: [PATCH] TTkSplitter: Removed Frame Dependencies, added titles --- TermTk/TTkCore/canvas.py | 5 +- TermTk/TTkTheme/draw_utf8.py | 6 +- TermTk/TTkUiTools/properties/splitter.py | 8 +- TermTk/TTkWidgets/splitter.py | 154 ++++++++++++++++------- demo/showcase/splitter4.py | 137 ++++++++++++++++++++ 5 files changed, 264 insertions(+), 46 deletions(-) create mode 100755 demo/showcase/splitter4.py diff --git a/TermTk/TTkCore/canvas.py b/TermTk/TTkCore/canvas.py index 2a5bf16c..4a71eaed 100644 --- a/TermTk/TTkCore/canvas.py +++ b/TermTk/TTkCore/canvas.py @@ -300,10 +300,11 @@ class TTkCanvas: l=1 else: l = w-2-text.termWidth() + l+=x r = l+text.termWidth()+1 - self._set(y,l, gg[7], color) - self._set(y,r, gg[6], color) + self._set(y,l, gg[0x0B], color) + self._set(y,r, gg[0x08], color) self.drawText(pos=(l+1,y),text=text,color=colorText) diff --git a/TermTk/TTkTheme/draw_utf8.py b/TermTk/TTkTheme/draw_utf8.py index 86ce4aa1..b7bd2a7b 100644 --- a/TermTk/TTkTheme/draw_utf8.py +++ b/TermTk/TTkTheme/draw_utf8.py @@ -71,7 +71,11 @@ class TTkTheme(): '╞','═','╬','╡', '└','─','╨','┘'), (), # TODO: Grid 4 - (), # TODO: Grid 5 + ( # Grid 5 + '╒','═','╤','╕', + '│',' ','│','│', + '╞','═','╪','╡', + '╘','═','╧','╛',), ( # Grid 6 '╓','─','┬','┐', '║',' ','│','│', diff --git a/TermTk/TTkUiTools/properties/splitter.py b/TermTk/TTkUiTools/properties/splitter.py index 5ea6e028..528ef077 100644 --- a/TermTk/TTkUiTools/properties/splitter.py +++ b/TermTk/TTkUiTools/properties/splitter.py @@ -28,13 +28,19 @@ from TermTk.TTkWidgets.splitter import TTkSplitter TTkSplitterProperties = { 'properties' : { 'Border' : { - 'init': {'name':'border', 'type':bool } }, + 'init': {'name':'border', 'type':bool }, + 'get': {'cb':TTkSplitter.border, 'type':bool } , + 'set': {'cb':TTkSplitter.setBorder, 'type':bool } }, 'Orientation' : { 'init': {'name':'orientation', 'type':'singleflag', 'flags': { 'Horizontal' : TTkK.HORIZONTAL , 'Vertical' : TTkK.VERTICAL } } , 'get': {'cb':TTkSplitter.orientation, 'type':'singleflag', + 'flags': { + 'Horizontal' : TTkK.HORIZONTAL , + 'Vertical' : TTkK.VERTICAL } } , + 'set': {'cb':TTkSplitter.setOrientation, 'type':'singleflag', 'flags': { 'Horizontal' : TTkK.HORIZONTAL , 'Vertical' : TTkK.VERTICAL } } } diff --git a/TermTk/TTkWidgets/splitter.py b/TermTk/TTkWidgets/splitter.py index 4d690409..3ce28f31 100644 --- a/TermTk/TTkWidgets/splitter.py +++ b/TermTk/TTkWidgets/splitter.py @@ -23,18 +23,23 @@ # SOFTWARE. from TermTk.TTkCore.constant import TTkK +from TermTk.TTkCore.string import TTkString from TermTk.TTkLayouts.layout import TTkLayout +from TermTk.TTkWidgets.widget import TTkWidget from TermTk.TTkWidgets.frame import TTkFrame -class TTkSplitter(TTkFrame): +class TTkSplitter(TTkWidget): '''TTkSplitter''' __slots__ = ( '_orientation', '_separators', '_refSizes', - '_items', '_separatorSelected') + '_items', '_titles', '_separatorSelected', + '_border') def __init__(self, *args, **kwargs): self._items = [] + self._titles = [] self._separators = [] self._refSizes = [] + self._border = False self._separatorSelected = None self._orientation = TTkK.HORIZONTAL super().__init__(*args, **kwargs) @@ -53,10 +58,34 @@ class TTkSplitter(TTkFrame): self.addItem(item) self.setLayout(_SplitterLayout()) + def setBorder(self, border): + '''setBorder''' + self._border = border + if border: self.setPadding(1,1,1,1) + else: self.setPadding(0,0,0,0) + self.update() + + def border(self): + '''border''' + return self._border + def orientation(self): '''orientation''' return self._orientation + def setOrientation(self, orientation): + if orientation == self._orientation: return + if orientation not in (TTkK.HORIZONTAL, TTkK.VERTICAL): return + self._orientation = orientation + self._updateGeometries() + + def clean(self): + for i in reversed(self._items): + if issubclass(type(i),TTkWidget): + self.removeWidget(i) + else: + self.removeItem(i) + def count(self): '''count''' return len(self._items) @@ -69,18 +98,28 @@ class TTkSplitter(TTkFrame): '''widget''' return self._items[index] - def replaceItem(self, index, item): + def replaceItem(self, index, item, title=None): '''replaceItem''' + if index >= len(self._items): + return self.addItem(item) TTkLayout.removeItem(self.layout(), self._items[index]) - TTkLayout.insertItem(self.layout(), index, item) + TTkLayout.insertItem(self.layout(), index, item, title=title) self._items[index] = item + w,h = self.size() + b = 2 if self._border else 0 + self._processRefSizes(w-b,h-b) self._updateGeometries() - def replaceWidget(self, index, widget): + def replaceWidget(self, index, widget, title=None): '''replaceWidget''' + if index >= len(self._items): + return self.addWidget(widget, title=title) TTkLayout.removeWidget(self.layout(), self._items[index]) - TTkLayout.insertWidget(self.layout(), index, widget) + TTkLayout.insertWidget(self.layout(), index, widget, title=title) self._items[index] = widget + w,h = self.size() + b = 2 if self._border else 0 + self._processRefSizes(w-b,h-b) self._updateGeometries() def removeItem(self, item): @@ -90,6 +129,9 @@ class TTkSplitter(TTkFrame): self._refSizes.pop(index) self._separators.pop(index) TTkLayout.removeItem(self.layout(), item) + w,h = self.size() + b = 2 if self._border else 0 + self._processRefSizes(w-b,h-b) self._updateGeometries() def removeWidget(self, widget): @@ -99,36 +141,38 @@ class TTkSplitter(TTkFrame): self._refSizes.pop(index) self._separators.pop(index) TTkLayout.removeWidget(self.layout(), widget) + w,h = self.size() + b = 2 if self._border else 0 + self._processRefSizes(w-b,h-b) self._updateGeometries() - def addItem(self, item, size=None): + def addItem(self, item, size=None, title=None): '''addItem''' - self.insertItem(len(self._items), item, size) + self.insertItem(len(self._items), item, size=size, title=title) - def insertItem(self, index, item, size=None): + def insertItem(self, index, item, size=None, title=None): '''insertItem''' TTkLayout.insertItem(self.layout(), index, item) - self._insertWidgetItem(index, item, size) + self._insertWidgetItem(index, item, size=size, title=title) - def addWidget(self, widget, size=None): + def addWidget(self, widget, size=None, title=None): '''addWidget''' - self.insertWidget(len(self._items), widget, size) + self.insertWidget(len(self._items), widget, size=size, title=title) - def insertWidget(self, index, widget, size=None): + def insertWidget(self, index, widget, size=None, title=None): '''insertWidget''' TTkLayout.insertWidget(self.layout(), index, widget) - self._insertWidgetItem(index, widget, size) + self._insertWidgetItem(index, widget, size=size, title=title) - def _insertWidgetItem(self, index, widgetItem, size=None): - _,_,w,h = self.geometry() - if self.border(): - w-=2 - h-=2 + def _insertWidgetItem(self, index, widgetItem, size=None, title=None): self._items.insert(index, widgetItem) + self._titles.insert(index, TTkString(title) if title else None) # assign the same slice to all the widgets self._refSizes.insert(index, size) - self._processRefSizes(w,h) + w,h = self.size() + b = 2 if self._border else 0 + self._processRefSizes(w-b,h-b) self._updateGeometries() if self.parentWidget(): self.parentWidget().update(repaint=True, updateLayout=True) @@ -139,7 +183,7 @@ class TTkSplitter(TTkFrame): sizes=sizes[:ls]+[None]*max(0,ls-len(sizes)) self._refSizes = sizes.copy() w,h = self.size() - b = 2 if self.border() else 0 + b = 2 if self._border else 0 self._processRefSizes(w-b,h-b) self._updateGeometries() @@ -169,10 +213,10 @@ class TTkSplitter(TTkFrame): def _updateGeometries(self, resized=False): if not self.isVisible() or not self._items: return - _,_,w,h = self.geometry() + w,h = self.size() if w==h==0: return sep = self._separators = self._separators[0:len(self._items)] - if self.border(): + if self._border: w-=2 h-=2 @@ -277,25 +321,14 @@ class TTkSplitter(TTkFrame): self._separators = [int(i*diff) for i in self._separators] def resizeEvent(self, w, h): - b = 2 if self.border() else 0 + b = 2 if self._border else 0 self._processRefSizes(w-b,h-b) self._updateGeometries(resized=True) - def paintEvent(self, canvas): - off = 1 if self.border() else 0 - TTkFrame.paintEvent(self, canvas) - w,h = self.size() - if self._orientation == TTkK.HORIZONTAL: - for i in self._separators[:-1]: - canvas.drawVLine(pos=(i+off,0), size=h) - else: - for i in self._separators[:-1]: - canvas.drawHLine(pos=(0,i+off), size=w) - def mousePressEvent(self, evt): self._separatorSelected = None x,y = evt.x, evt.y - if self.border(): + if self._border: x-=1 ; y-=1 # TTkLog.debug(f"{self._separators} {evt}") for i, val in enumerate(self._separators): @@ -312,7 +345,7 @@ class TTkSplitter(TTkFrame): def mouseDragEvent(self, evt): if self._separatorSelected is not None: x,y = evt.x, evt.y - if self.border(): + if self._border: x-=1 ; y-=1 if self._orientation == TTkK.HORIZONTAL: self._separators[self._separatorSelected] = x @@ -326,7 +359,7 @@ class TTkSplitter(TTkFrame): self._separatorSelected = None def minimumHeight(self) -> int: - ret = 2 if self.border() else 0 + ret = 2 if self._border else 0 if not self._items: return ret if self._orientation == TTkK.VERTICAL: for item in self._items: @@ -339,7 +372,7 @@ class TTkSplitter(TTkFrame): return ret def minimumWidth(self) -> int: - ret = 2 if self.border() else 0 + ret = 2 if self._border else 0 if not self._items: return ret if self._orientation == TTkK.HORIZONTAL: for item in self._items: @@ -352,7 +385,7 @@ class TTkSplitter(TTkFrame): return ret def maximumHeight(self) -> int: - b = 2 if self.border() else 0 + b = 2 if self._border else 0 if not self._items: return 0x10000 if self._orientation == TTkK.VERTICAL: ret = b @@ -367,12 +400,12 @@ class TTkSplitter(TTkFrame): return ret def maximumWidth(self) -> int: - b = 2 if self.border() else 0 + b = 2 if self._border else 0 if not self._items: return 0x10000 if self._orientation == TTkK.HORIZONTAL: ret = b for item in self._items: - ret+=item.maximumHeight()+1 + ret+=item.maximumWidth()+1 ret = max(b,ret-1) else: ret = 0x10000 @@ -380,3 +413,40 @@ class TTkSplitter(TTkFrame): if ret > item.maximumWidth(): ret = item.maximumWidth() return ret + + def paintEvent(self, canvas): + off = 0 + w,h = self.size() + + if self._border: + off= 1 + canvas.drawBox(pos=(0,0),size=(w,h)) + + if self._orientation == TTkK.HORIZONTAL: + for i in self._separators[:-1]: + canvas.drawVLine(pos=(i+off,0), size=h) + else: + for i in self._separators[:-1]: + canvas.drawHLine(pos=(0,i+off), size=w) + + if self._orientation == TTkK.HORIZONTAL and self._border: + for i,t in enumerate(self._titles): + if not t: continue + a = (off + self._separators[i-1]) if i>0 else 0 + b = off + self._separators[i] + canvas.drawBoxTitle( + pos=(a,0), + size=(b-a+1,1), + text=t) + elif self._orientation == TTkK.VERTICAL: + for i,t in enumerate(self._titles): + if i == 0 and not self._border: continue + if not t: continue + a = (off + self._separators[i-1]) if i>0 else 0 + grid = 0 if i == 0 else 5 + canvas.drawBoxTitle( + pos=(0,a), + size=(w,1), + grid=grid, + text=t) + diff --git a/demo/showcase/splitter4.py b/demo/showcase/splitter4.py new file mode 100755 index 00000000..02b98ad2 --- /dev/null +++ b/demo/showcase/splitter4.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 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 + + +def demoSplitter(root=None): + vsplitter = ttk.TTkSplitter(parent=root, border=True, orientation=ttk.TTkK.VERTICAL) + hsplitter1 = ttk.TTkSplitter(border=True) + hsplitter2 = ttk.TTkSplitter(border=True) + + wid11 = ttk.TTkFrame(border=True, title="Frame1.1") + wid12 = ttk.TTkFrame(border=True, title="Frame1.2") + wid13 = ttk.TTkFrame(border=True, title="Frame1.3") + wid3 = ttk.TTkFrame(border=True, title="Frame3") + wid2 = ttk.TTkTestWidgetSizes(border=True, title="Frame2", minSize=(33,7), maxSize=(33,7)) + wid4 = ttk.TTkFrame(border=True, title="Frame4") + + wid5 = ttk.TTkFrame(border=True, title="Frame5") + wid6 = ttk.TTkTestWidgetSizes(border=True, title="Frame6", minSize=(33,7), maxSize=(33,7)) + wid7 = ttk.TTkFrame(border=True, title="Frame7") + wid8 = ttk.TTkTestWidgetSizes(border=True, title="Frame8", minSize=(33,7), maxSize=(33,7)) + wid9 = ttk.TTkFrame(border=True, title="Frame9") + + vsplitter.addWidget(wid11, title="Test 1.1") + vsplitter.addWidget(hsplitter1) + vsplitter.addWidget(wid12, title="Test 1.2") + vsplitter.addWidget(hsplitter2, title="Test HSplit 2") + vsplitter.addWidget(wid13) + + hsplitter1.addWidget(wid3, title="Test 3") + hsplitter1.addWidget(wid2, title="Test 2") + hsplitter1.addWidget(wid4) + + hsplitter2.addWidget(wid5, title="Test 5") + hsplitter2.addWidget(wid6) + hsplitter2.addWidget(wid7, title="Test 7") + hsplitter2.addWidget(wid8, title="Test 8") + hsplitter2.addWidget(wid9, title="Test 9") + + return vsplitter + +def demoSplitter(root=None): + vsplitter = ttk.TTkSplitter(parent=root, border=True, orientation=ttk.TTkK.VERTICAL) + hsplitter1 = ttk.TTkSplitter(border=True) + hsplitter2 = ttk.TTkSplitter(border=True) + + wid11 = ttk.TTkFrame(border=True, title="Frame1.1") + wid12 = ttk.TTkFrame(border=True, title="Frame1.2") + wid13 = ttk.TTkFrame(border=True, title="Frame1.3") + wid3 = ttk.TTkFrame(border=True, title="Frame3") + wid2 = ttk.TTkTestWidgetSizes(border=True, title="Frame2", minSize=(33,7), maxSize=(33,7)) + wid4 = ttk.TTkFrame(border=True, title="Frame4") + + wid5 = ttk.TTkFrame(border=True, title="Frame5") + wid6 = ttk.TTkTestWidgetSizes(border=True, title="Frame6", minSize=(33,7), maxSize=(33,7)) + wid7 = ttk.TTkFrame(border=True, title="Frame7") + wid8 = ttk.TTkTestWidgetSizes(border=True, title="Frame8", minSize=(33,7), maxSize=(33,7)) + wid9 = ttk.TTkFrame(border=True, title="Frame9") + + vsplitter.addWidget(wid11, title="Test 1.1") + vsplitter.addWidget(hsplitter1) + vsplitter.addWidget(wid12, title="Test 1.2") + vsplitter.addWidget(hsplitter2, title="Test HSplit 2") + vsplitter.addWidget(wid13) + + hsplitter1.addWidget(wid3, title="Test 3") + hsplitter1.addWidget(wid2, title="Test 2") + hsplitter1.addWidget(wid4) + + hsplitter2.addWidget(wid5, title="Test 5") + hsplitter2.addWidget(wid6) + hsplitter2.addWidget(wid7, title="Test 7") + hsplitter2.addWidget(wid8, title="Test 8") + hsplitter2.addWidget(wid9, title="Test 9") + + return vsplitter + +def demoHSplitter(root=None): + hsplitter2 = ttk.TTkSplitter(parent=root, border=True) + + wid5 = ttk.TTkFrame(border=True, title="Frame5") + wid6 = ttk.TTkTestWidgetSizes(border=True, title="Frame6") + wid7 = ttk.TTkFrame(border=True, title="Frame7") + wid8 = ttk.TTkTestWidgetSizes(border=True, title="Frame8") + wid9 = ttk.TTkFrame(border=True, title="Frame9") + + hsplitter2.addWidget(wid5, title="Test 5") + hsplitter2.addWidget(wid6) + hsplitter2.addWidget(wid7, title="Test 7") + hsplitter2.addWidget(wid8, title="Test 8") + hsplitter2.addWidget(wid9, title="Test 9") + + return hsplitter2 +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-f', help='Full Screen', action='store_true') + args = parser.parse_args() + + ttk.TTkLog.use_default_file_logging() + + root = ttk.TTk() + if args.f: + rootSplitter = root + root.setLayout(ttk.TTkGridLayout()) + else: + rootSplitter = ttk.TTkWindow(parent=root,pos = (5,5), size=(100,40), title="Test Splitter", border=True, layout=ttk.TTkGridLayout()) + demoHSplitter(ttk.TTkWindow(parent=root,pos = (0,0), size=(100,15), title="Test H Splitter", border=True, layout=ttk.TTkGridLayout())) + demoSplitter(rootSplitter) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file