Browse Source

Tuned the separators topology

pull/239/head
Eugenio Parodi 2 years ago
parent
commit
cbbe34e711
  1. 2
      TermTk/TTkCore/constant.py
  2. 18
      TermTk/TTkTestWidgets/testwidgetsizes.py
  3. 1
      TermTk/TTkWidgets/__init__.py
  4. 172
      TermTk/TTkWidgets/apptemplate.py
  5. 106
      demo/showcase/apptemplate.py

2
TermTk/TTkCore/constant.py

@ -56,6 +56,8 @@ class TTkConstant:
LEFT = 0x0004 LEFT = 0x0004
RIGHT = 0x0008 RIGHT = 0x0008
CENTER = 0x0010 CENTER = 0x0010
HEADER = 0x0020
FOOTER = 0x0040
# SelectionMode # SelectionMode
NoSelection = 0x00 NoSelection = 0x00

18
TermTk/TTkTestWidgets/testwidgetsizes.py

@ -32,13 +32,19 @@ class TTkTestWidgetSizes(TTkFrame):
TTkTestWidgetSizes.ID+=1 TTkTestWidgetSizes.ID+=1
def paintEvent(self, canvas): def paintEvent(self, canvas):
TTkFrame.paintEvent(self, canvas)
t,_,l,_ = self.getPadding() t,_,l,_ = self.getPadding()
canvas.drawText(pos=(l,t+0), text=f"Test Widget [{self._name}]") w,h = self.size()
canvas.drawText(pos=(l,t+1), text=f"x,y ({self._x},{self._y})") style = self.currentStyle()
canvas.drawText(pos=(l,t+2), text=f"w,h ({self._width},{self._height})") color = style['color']
canvas.drawText(pos=(l,t+3), text=f"max w,h ({self._maxw},{self._maxh})") if color.background():
canvas.drawText(pos=(l,t+4), text=f"min w,h ({self._minw},{self._minh})") canvas.fill(pos=(0,0), size=(w,h), color=color)
borderColor = style['borderColor']
canvas.drawText(pos=(l,t+0), color=color, text=f"Test Widget [{self._name}]")
canvas.drawText(pos=(l,t+1), color=color, text=f"x,y ({self._x},{self._y})")
canvas.drawText(pos=(l,t+2), color=color, text=f"w,h ({self._width},{self._height})")
canvas.drawText(pos=(l,t+3), color=color, text=f"max w,h ({self._maxw},{self._maxh})")
canvas.drawText(pos=(l,t+4), color=color, text=f"min w,h ({self._minw},{self._minh})")
TTkFrame.paintEvent(self, canvas)
def mousePressEvent(self, evt): return True def mousePressEvent(self, evt): return True
def mouseReleaseEvent(self, evt): return True def mouseReleaseEvent(self, evt): return True

1
TermTk/TTkWidgets/__init__.py

@ -7,6 +7,7 @@ from .frame import *
from .resizableframe import * from .resizableframe import *
from .window import * from .window import *
from .splitter import * from .splitter import *
from .apptemplate import *
# Everything else # Everything else
from .about import * from .about import *

172
TermTk/TTkWidgets/apptemplate.py

@ -2,10 +2,11 @@
__all__ = ['TTkAppTemplate'] __all__ = ['TTkAppTemplate']
from dataclasses import dataclass from dataclasses import dataclass
from TermTk.TTkCore.canvas import TTkCanvas
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkLayouts import TTkLayout, TTkGridLayout from TermTk.TTkLayouts import TTkLayout, TTkGridLayout
from TermTk.TTkWidgets.container import TTkContainer from TermTk.TTkWidgets.container import TTkWidget, TTkContainer
class TTkAppTemplate(TTkContainer): class TTkAppTemplate(TTkContainer):
''' TTkAppTemplate Layout sizes: ''' TTkAppTemplate Layout sizes:
@ -15,19 +16,19 @@ class TTkAppTemplate(TTkContainer):
App Template Layout App Template Layout
Header Header
A (1,2,3) H (1,2,3)
Top Top
B T
Right Main Left Right Main Left
Center Center
C B
Bottom Bottom
D (1,2,3) F (1,2,3)
Footer Footer
E F R L
''' '''
MAIN = TTkK.CENTER MAIN = TTkK.CENTER
@ -42,17 +43,30 @@ class TTkAppTemplate(TTkContainer):
@dataclass(frozen=False) @dataclass(frozen=False)
class _Panel: class _Panel:
# It's either item or widget # It's either item or widget
item = None item: TTkLayout = None
widget = None widget: TTkWidget = None
size = 0 size = 0
border = True border = True
fixed = False fixed = False
def setGeometry(self,x,y,w,h):
if it := self.item:
it.setGeometry(x,y,w,h)
elif wid := self.widget:
wid.setGeometry(x,y,w,h)
def isVisible(self): def isVisible(self):
if self.widget: if self.widget:
return self.widget.isVisible() return self.widget.isVisible()
return True return True
def getSize(self):
if it := self.item:
return it.size()
if wid := self.widget:
return wid.size()
return (0,0)
def minimumWidth(self): def minimumWidth(self):
if it := self.item: if it := self.item:
return it.minimumWidth() return it.minimumWidth()
@ -76,9 +90,9 @@ class TTkAppTemplate(TTkContainer):
def maximumHeight(self): def maximumHeight(self):
if it := self.item: if it := self.item:
return it.maximumWidth() return it.maximumHeight()
if wid := self.widget: if wid := self.widget:
return wid.maximumWidth() return wid.maximumHeight()
return 0x10000 return 0x10000
__slots__ = ('_panels', __slots__ = ('_panels',
@ -98,15 +112,20 @@ class TTkAppTemplate(TTkContainer):
self._updateGeometries() self._updateGeometries()
def setWidget(self, widget, location): def setWidget(self, widget, location):
if not self._panels[location]:
self._panels[location] = TTkAppTemplate._Panel()
self._panels[location].widget = widget self._panels[location].widget = widget
if it:=self._panels[location].item: if it:=self._panels[location].item:
self.layout().removeItem(it) self.layout().removeItem(it)
self._panels[location].item = None self._panels[location].item = None
if widget: if widget:
self.layout().addWidget(widget) self.layout().addWidget(widget)
self._panels[location].size = widget.minimumWidth() if location in (TTkAppTemplate.LEFT,TTkAppTemplate.RIGHT) else widget.maximumWidth()
self._updateGeometries() self._updateGeometries()
def setItem(self, item, location): def setItem(self, item, location):
if not self._panels[location]:
self._panels[location] = TTkAppTemplate._Panel()
self._panels[location].item = item self._panels[location].item = item
if wid:=self._panels[location].widget: if wid:=self._panels[location].widget:
self.layout().removeWdget(wid) self.layout().removeWdget(wid)
@ -115,6 +134,15 @@ class TTkAppTemplate(TTkContainer):
self.layout().addItem(item) self.layout().addItem(item)
self._updateGeometries() self._updateGeometries()
def setBorder(self, border=True, location=MAIN):
if not self._panels[location]:
self._panels[location] = TTkAppTemplate._Panel()
self._panels[location].border = border
self._updateGeometries()
def resizeEvent(self, w, h):
self._updateGeometries()
def minimumWidth(self): def minimumWidth(self):
pns = self._panels pns = self._panels
@ -147,7 +175,7 @@ class TTkAppTemplate(TTkContainer):
pns = self._panels pns = self._panels
# Header and Footer sizes # Header and Footer sizes
mh=mf=0 mh=mf=0x10000
if p:=pns[TTkAppTemplate.HEADER]: if p:=pns[TTkAppTemplate.HEADER]:
mh = p.maximumWidth() mh = p.maximumWidth()
if p:=pns[TTkAppTemplate.FOOTER]: if p:=pns[TTkAppTemplate.FOOTER]:
@ -167,7 +195,7 @@ class TTkAppTemplate(TTkContainer):
if p:=pns[TTkAppTemplate.BOTTOM]: if p:=pns[TTkAppTemplate.BOTTOM]:
mcb = p.maximumWidth() mcb = p.maximumWidth()
mcm = (p:=pns[TTkAppTemplate.MAIN]).minimumWidth() mcm = (p:=pns[TTkAppTemplate.MAIN]).maximumWidth()
return min(mh, mf, mcr+mcl+min(mct, mcb, mcm)) + (2 if p.border else 0) return min(mh, mf, mcr+mcl+min(mct, mcb, mcm)) + (2 if p.border else 0)
@ -229,30 +257,120 @@ class TTkAppTemplate(TTkContainer):
return mh+mf+min(mcr ,mcl, mcm+mct+mcb ) + ( 2 if p.border else 0 ) return mh+mf+min(mcr ,mcl, mcm+mct+mcb ) + ( 2 if p.border else 0 )
def _updateGeometries(self): def _updateGeometries(self):
w,h = self.size() w,h = self.size()
pns = self._panels pns = self._panels
# E,F Splitters sl=sr=st=sb=sh=sf=0
p = pns[TTkAppTemplate.RIGHT] bm=bl=br=bt=bb=bh=bf=0
if ( not p or not p.isVisible() ): # A,B,C,D HSplitters
pass pt=pb=ph=pf=None
p = pns[TTkAppTemplate.LEFT] if ( (p:=pns[TTkAppTemplate.TOP]) and p.isVisible() ): pt=p ; st=p.size ; bt=1 if p.border else 0
if ( not p or not p.isVisible() ): if ( (p:=pns[TTkAppTemplate.BOTTOM]) and p.isVisible() ): pb=p ; sb=p.size ; bb=1 if p.border else 0
pass if ( (p:=pns[TTkAppTemplate.HEADER]) and p.isVisible() ): ph=p ; sh=p.size ; bh=1 if p.border else 0
if ( (p:=pns[TTkAppTemplate.FOOTER]) and p.isVisible() ): pf=p ; sf=p.size ; bf=1 if p.border else 0
# E,F VSplitters
pl=pr=None
if ( (p:=pns[TTkAppTemplate.LEFT]) and p.isVisible() ): pl=p ; sl=p.size ; bl=1 if p.border else 0
if ( (p:=pns[TTkAppTemplate.RIGHT]) and p.isVisible() ): pr=p ; sr=p.size ; br=1 if p.border else 0
# Main Boundaries
pm=pns[TTkAppTemplate.MAIN]
mmaxw = pm.maximumWidth()
mminw = pm.minimumWidth()
mmaxh = pm.maximumHeight()
mminh = pm.minimumHeight()
bm = 1 if pns[TTkAppTemplate.MAIN].border else 0
w-=(bm<<1)+bl+br
h-=(bm<<1)+bt+bb+bh+bf
# check horizontal sizes
if not (mminw <= (newszw:=(w-sl-sr)) <= mmaxw):
# the main width does not fit
# we need to move the (E,F) splitters
# * to avoid extra complexity,
# Let's resize the right panel first
# and adjust the left one to allows the
# main panel to fit again
if newszw < mminw:
if pr: pr.size = sr = max(pr.minimumWidth(), w-mminw-sl) ; newszw=w-sl-sr
if newszw < mminw and pl: pl.size = sl = max(pl.minimumWidth(), w-mminw-sr) ; newszw=w-sl-sr
else:
if pr: pr.size = sr = min(pr.maximumWidth(), w-mmaxw-sl) ; newszw=w-sl-sr
if newszw > mmaxw and pl: pl.size = sl = min(pl.maximumWidth(), w-mmaxw-sr) ; newszw=w-sl-sr
# check vertical sizes
if not (mminh <= (newszh:=(h-st-sb-sh-sf)) <= mmaxh):
# almost same as before except that there are 4 panels to be considered instead of 2
if newszh < mminh:
if pf: pf.size = sf = max(pf.minimumHeight(), h-mminh-sb-st-sh) ; newszh=h-st-sb-sh-sf
if newszh < mminh and pb: pb.size = sb = max(pb.minimumHeight(), h-mminh-sf-st-sh) ; newszh=h-st-sb-sh-sf
if newszh < mminh and pt: pt.size = st = max(pt.minimumHeight(), h-mminh-sf-sb-sh) ; newszh=h-st-sb-sh-sf
if newszh < mminh and ph: ph.size = sh = max(ph.minimumHeight(), h-mminh-sf-sb-st) ; newszh=h-st-sb-sh-sf
else:
if pf: pf.size = sf = min(pf.maximumHeight(), h-mmaxh-sb-st-sh) ; newszh=h-st-sb-sh-sf
if newszh > mmaxh and pb: pb.size = sb = min(pb.maximumHeight(), h-mmaxh-sf-st-sh) ; newszh=h-st-sb-sh-sf
if newszh > mmaxh and pt: pt.size = st = min(pt.maximumHeight(), h-mmaxh-sf-sb-sh) ; newszh=h-st-sb-sh-sf
if newszh > mmaxh and ph: ph.size = sh = min(ph.maximumHeight(), h-mmaxh-sf-sb-st) ; newszh=h-st-sb-sh-sf
# check vertical sizes
w+=bl+br
h+=bt+bb+bh+bf
pm.setGeometry( bm+sl+bl , bm+sh+bh+st+bt , newszw , newszh )
if pl: pl.setGeometry( bm , bm+sh+bh , sl , h-sh-bh-sf-bf )
if pr: pr.setGeometry( bm+sl+bl+newszw+br , bm+sh+bh , sr , h-sh-bh-sf-bf )
if ph: ph.setGeometry( bm , bm , w , sh )
if pt: pt.setGeometry( bm+sl+bl , bm+sh+bh , newszw , st )
if pb: pb.setGeometry( bm+sl+bl , bm+sh+bh+st+bt+newszh+bb , newszw , sb )
if pf: pf.setGeometry( bm , bm+sh+bh+st+bt+newszh+bb+sb+bf , w , sf )
self.update() self.update()
def update(self, repaint: bool =True, updateLayout: bool =False, updateParent: bool =False):
if updateLayout:
self._updateGeometries()
super().update(repaint=repaint,updateLayout=updateLayout,updateParent=updateParent)
#def layout(self): #def layout(self):
# return self._panels[TTkAppTemplate.MAIN].item # return self._panels[TTkAppTemplate.MAIN].item
#def setLayout(self, layout): #def setLayout(self, layout):
# self._panels[TTkAppTemplate.MAIN].item = layout # self._panels[TTkAppTemplate.MAIN].item = layout
def paintEvent(self, canvas: TTkCanvas):
w,h = self.size()
pns = self._panels
sl=sr=st=sb=sh=sf=0
bl=br=bt=bb=bh=bf=0
bm = 1 if pns[TTkAppTemplate.MAIN].border else 0
smw,smh = pns[TTkAppTemplate.MAIN].getSize()
# A,B,C,D HSplitters
pt=pb=ph=pf=None
if ( (p:=pns[TTkAppTemplate.TOP]) and p.isVisible() ): pt=p ; st=p.size ; bt=1 if p.border else 0
if ( (p:=pns[TTkAppTemplate.BOTTOM]) and p.isVisible() ): pb=p ; sb=p.size ; bb=1 if p.border else 0
if ( (p:=pns[TTkAppTemplate.HEADER]) and p.isVisible() ): ph=p ; sh=p.size ; bh=1 if p.border else 0
if ( (p:=pns[TTkAppTemplate.FOOTER]) and p.isVisible() ): pf=p ; sf=p.size ; bf=1 if p.border else 0
# E,F VSplitters
pl=pr=None
if ( (p:=pns[TTkAppTemplate.LEFT]) and p.isVisible() ): pl=p ; sl=p.size ; bl=1 if p.border else 0
if ( (p:=pns[TTkAppTemplate.RIGHT]) and p.isVisible() ): pr=p ; sr=p.size ; br=1 if p.border else 0
if bm: canvas.drawBox( pos= (0 , 0) , size= (w,h) )
if bh: canvas.drawHLine(pos= (0 , bm+sh) , size= w )
if bf: canvas.drawHLine(pos= (0 , bm+sh+bh+st+bt+smh+bb+sb) , size= w )
ca = sh + (bm if ph else 0 )
cb = bm+sh+bh+st+bt+smh+bb+sb + (bf if pf else bm)
if bl: canvas.drawVLine(pos= (bm+sl , ca) , size= cb-ca )
if br: canvas.drawVLine(pos= (bm+sl+bl+smw , ca) , size= cb-ca )
ca = sl + (bm if pl else 0 )
cb = bm+sl+bl+smw + (br if pr else bm)
if bt: canvas.drawHLine(pos= (ca , bm+sh+bh+st) , size= cb-ca )
if bb: canvas.drawHLine(pos= (ca , bm+sh+bh+st+bt+smh) , size= cb-ca )
return super().paintEvent(canvas)

106
demo/showcase/apptemplate.py

@ -0,0 +1,106 @@
#!/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 sys, os, argparse
from random import randint
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
class AppTestWidget(ttk.TTkContainer):
def __init__(self, at, wids, **kwargs):
super().__init__(**kwargs)
self._at = at
self._wids = wids
self.layout().addWidget(ttk.TTkLabel(pos=(1,0),text="v------< Border"))
self.layout().addWidget(ttk.TTkLabel(pos=(1,1),text=" v---< Fixed"))
self.layout().addWidget(ttk.TTkLabel(pos=(1,2),text=" <---< Main"))
self.layout().addWidget(cbbm:=ttk.TTkCheckbox(pos=(0,2),size=(3,1), checked=True))
cbbm.clicked.connect(lambda b: at.setBorder(b))
for y,wn in enumerate(wids,3):
wid = wids[wn]['wid']
style = wid.style()
# Choose a random color for the background
h,s,l = randint(0,359),100,randint(60,80)
r,g,b = ttk.TTkColor.hsl2rgb(((h+5)%360,s,l))
style['default']['color'] = ttk.TTkColor.fg('#000000')+ttk.TTkColor.bg(f"#{r:02X}{g:02X}{b:02X}", modifier=ttk.TTkColorGradient(increment=+2))
wid.setStyle(style)
self.layout().addWidget(cbw:=ttk.TTkCheckbox(pos=(7,y),size=(10,1),checked=True,text=wn))
self.layout().addWidget(cbb:=ttk.TTkCheckbox(pos=(0,y),size=(3,1), checked=True))
self.layout().addWidget(cbf:=ttk.TTkCheckbox(pos=(3,y),size=(3,1), checked=False))
cbw.clicked.connect(wid.setVisible)
cbb.clicked.connect(lambda b,loc=wids[wn]['loc']: at.setBorder(b,loc))
def demoAppTemplate(root=None):
at = ttk.TTkAppTemplate(parent=root)
twl = ttk.TTkTestWidgetSizes(border=False, name="Left", minSize=( 15, 5), maxSize=( 50, 25))
twr = ttk.TTkTestWidgetSizes(border=False, name="Right", minSize=( 15, 5), maxSize=( 50, 25))
twh = ttk.TTkTestWidgetSizes(border=False, name="Header", minSize=( 15, 3), maxSize=(160, 7))
twt = ttk.TTkTestWidgetSizes(border=False, name="Top", minSize=( 15, 3), maxSize=(100, 7))
twb = ttk.TTkTestWidgetSizes(border=False, name="Bottom", minSize=( 15, 3), maxSize=(100, 7))
twf = ttk.TTkTestWidgetSizes(border=False, name="Footer", minSize=( 15, 3), maxSize=(160, 7))
twm = AppTestWidget(
at = at,
wids={
"Header" : {'wid': twh, 'loc':at.HEADER},
"Footer" : {'wid': twf, 'loc':at.FOOTER},
"Top" : {'wid': twt, 'loc':at.TOP},
"Bottom" : {'wid': twb, 'loc':at.BOTTOM},
"Right" : {'wid': twr, 'loc':at.RIGHT},
"Left" : {'wid': twl, 'loc':at.LEFT}},
minSize=( 15, 5), maxSize=( 50, 10))
at.setWidget(twm, at.MAIN)
at.setWidget(twl, at.LEFT)
at.setWidget(twr, at.RIGHT)
at.setWidget(twh, at.HEADER)
at.setWidget(twt, at.TOP)
at.setWidget(twb, at.BOTTOM)
at.setWidget(twf, at.FOOTER)
return at
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-f', help='Full Screen', action='store_true')
args = parser.parse_args()
root = ttk.TTk()
if args.f:
rootAppTemplate = root
root.setLayout(ttk.TTkGridLayout())
else:
rootAppTemplate = ttk.TTkWindow(parent=root,pos = (0,0), size=(100,40), title="Test AppTemplate", border=True, layout=ttk.TTkGridLayout())
demoAppTemplate(rootAppTemplate)
root.mainloop()
if __name__ == "__main__":
main()
Loading…
Cancel
Save