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
RIGHT = 0x0008
CENTER = 0x0010
HEADER = 0x0020
FOOTER = 0x0040
# SelectionMode
NoSelection = 0x00

18
TermTk/TTkTestWidgets/testwidgetsizes.py

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

1
TermTk/TTkWidgets/__init__.py

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

172
TermTk/TTkWidgets/apptemplate.py

@ -2,10 +2,11 @@
__all__ = ['TTkAppTemplate']
from dataclasses import dataclass
from TermTk.TTkCore.canvas import TTkCanvas
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkLayouts import TTkLayout, TTkGridLayout
from TermTk.TTkWidgets.container import TTkContainer
from TermTk.TTkWidgets.container import TTkWidget, TTkContainer
class TTkAppTemplate(TTkContainer):
''' TTkAppTemplate Layout sizes:
@ -15,19 +16,19 @@ class TTkAppTemplate(TTkContainer):
App Template Layout
Header
A (1,2,3)
H (1,2,3)
Top
B
T
Right Main Left
Center
C
B
Bottom
D (1,2,3)
F (1,2,3)
Footer
E F
R L
'''
MAIN = TTkK.CENTER
@ -42,17 +43,30 @@ class TTkAppTemplate(TTkContainer):
@dataclass(frozen=False)
class _Panel:
# It's either item or widget
item = None
widget = None
item: TTkLayout = None
widget: TTkWidget = None
size = 0
border = True
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):
if self.widget:
return self.widget.isVisible()
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):
if it := self.item:
return it.minimumWidth()
@ -76,9 +90,9 @@ class TTkAppTemplate(TTkContainer):
def maximumHeight(self):
if it := self.item:
return it.maximumWidth()
return it.maximumHeight()
if wid := self.widget:
return wid.maximumWidth()
return wid.maximumHeight()
return 0x10000
__slots__ = ('_panels',
@ -98,15 +112,20 @@ class TTkAppTemplate(TTkContainer):
self._updateGeometries()
def setWidget(self, widget, location):
if not self._panels[location]:
self._panels[location] = TTkAppTemplate._Panel()
self._panels[location].widget = widget
if it:=self._panels[location].item:
self.layout().removeItem(it)
self._panels[location].item = None
if widget:
self.layout().addWidget(widget)
self._panels[location].size = widget.minimumWidth() if location in (TTkAppTemplate.LEFT,TTkAppTemplate.RIGHT) else widget.maximumWidth()
self._updateGeometries()
def setItem(self, item, location):
if not self._panels[location]:
self._panels[location] = TTkAppTemplate._Panel()
self._panels[location].item = item
if wid:=self._panels[location].widget:
self.layout().removeWdget(wid)
@ -115,6 +134,15 @@ class TTkAppTemplate(TTkContainer):
self.layout().addItem(item)
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):
pns = self._panels
@ -147,7 +175,7 @@ class TTkAppTemplate(TTkContainer):
pns = self._panels
# Header and Footer sizes
mh=mf=0
mh=mf=0x10000
if p:=pns[TTkAppTemplate.HEADER]:
mh = p.maximumWidth()
if p:=pns[TTkAppTemplate.FOOTER]:
@ -167,7 +195,7 @@ class TTkAppTemplate(TTkContainer):
if p:=pns[TTkAppTemplate.BOTTOM]:
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)
@ -229,30 +257,120 @@ class TTkAppTemplate(TTkContainer):
return mh+mf+min(mcr ,mcl, mcm+mct+mcb ) + ( 2 if p.border else 0 )
def _updateGeometries(self):
w,h = self.size()
pns = self._panels
# E,F Splitters
p = pns[TTkAppTemplate.RIGHT]
if ( not p or not p.isVisible() ):
pass
p = pns[TTkAppTemplate.LEFT]
if ( not p or not p.isVisible() ):
pass
sl=sr=st=sb=sh=sf=0
bm=bl=br=bt=bb=bh=bf=0
# 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
# 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()
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):
# return self._panels[TTkAppTemplate.MAIN].item
#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