You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

307 lines
12 KiB

# MIT License
#
# Copyright (c) 2023 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 TermTk as ttk
import ttkDesigner.app.superobj as so
from .superlayout import SuperLayout
class _superExpandButton(ttk.TTkButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._direction = ttk.TTkK.TOP
def setDirection(self, d):
self._direction = d
def leaveEvent(self, evt) -> bool:
self.hide()
return super().leaveEvent(evt)
def paintEvent(self):
canvas = self.getCanvas()
# '▶','◀','▼','▲'
w,h = self.size()
if w==1:
self.getCanvas().drawText(text='', pos=(0,0), color=ttk.TTkColor.fg("FFFF00"))
self.getCanvas().drawText(text='', pos=(0,h-1), color=ttk.TTkColor.fg("FFFF00"))
for yy in range(1,h-1):
self.getCanvas().drawText(text='', pos=(0, yy), color=ttk.TTkColor.fg("FFFF00"))
elif h==1:
txt = ''+''*(w-2)+''
self.getCanvas().drawText(text=txt, pos=(0,0), width=w, color=ttk.TTkColor.fg("FFFF00"))
ch = {
ttk.TTkK.TOP : '',
ttk.TTkK.BOTTOM : '',
ttk.TTkK.LEFT : '',
ttk.TTkK.RIGHT : '',
}.get(self._direction, 'X')
x,y = 0,0
if w==1 and h>4:
y = h//2-2
h = 4
elif h==1 and w>4:
x = w//2-2
w = 4
canvas.fill(pos=(x,y), size=(w,h), char=ch, color=ttk.TTkColor.fg("#FF0000")+ttk.TTkColor.bg("#FFFF44"))
class SuperLayoutGrid(SuperLayout):
def __init__(self, *args, **kwargs):
kwargs['layout'] = ttk.TTkGridLayout()
super().__init__(*args, **kwargs)
self._expandButton = _superExpandButton()
self.rootLayout().addWidget(self._expandButton)
self._expandButton.hide()
self._expandButton.clicked.connect(self._clickExpand)
self._dragOver = None
self._expandStuff = None
self._orientation = ttk.TTkK.HORIZONTAL|ttk.TTkK.VERTICAL
def dragEnterEvent(self, evt) -> bool:
# ttk.TTkLog.debug(f"Enter")
_, __, ___, self._dragOver = self._processDragOver(evt.x,evt.y)
return True
def dragLeaveEvent(self, evt) -> bool:
# ttk.TTkLog.debug(f"Leave")
self._dragOver = None
self.update()
return True
def dragMoveEvent(self, evt) -> bool:
# ttk.TTkLog.debug(f"Move")
_, __, ___, self._dragOver = self._processDragOver(evt.x,evt.y)
return True
def dropEvent(self, evt) -> bool:
self._dragOver = None
self._pushRow, self._pushCol, self._direction, self._dragOver = self._processDragOver(evt.x,evt.y)
return super().dropEvent(evt)
# def mouseDragEvent(self, evt) -> bool:
# if ret := super().mouseDragEvent(evt):
# # removed unallocated rows/cols
# self.parentWidget().layout().repack()
# return ret
def removeSuperWidget(self, sw):
super().removeSuperWidget(sw)
self.layout().repack()
def mouseMoveEvent(self, evt) -> bool:
# ttk.TTkLog.debug(f"Move {evt}")
dir, wid = self._processMouseOver(evt.x, evt.y)
if not wid or not dir:
self._expandButton.hide()
return super().mouseMoveEvent(evt)
x,y,w,h = wid.geometry()
ebs = {
ttk.TTkK.TOP : (x, y, w, 1),
ttk.TTkK.BOTTOM : (x, y+h-1, w, 1),
ttk.TTkK.LEFT : (x, y, 1, h),
ttk.TTkK.RIGHT : (x+w-1, y, 1, h),
}.get(dir, None)
if not ebs:
self._expandButton.hide()
else:
self._expandButton.setGeometry(*ebs)
self._expandButton.setDirection(dir)
self._expandButton.raiseWidget()
self._expandButton.show()
self._expandStuff = (dir,wid)
return True
def addSuperWidget(self, sw):
self._dragOver = None
if self._direction == ttk.TTkK.HORIZONTAL or self._orientation == ttk.TTkK.HORIZONTAL:
self.layout().insertColumn(self._pushCol)
elif self._direction == ttk.TTkK.VERTICAL or self._orientation == ttk.TTkK.VERTICAL:
self.layout().insertRow(self._pushRow)
self.layout().addWidget(sw, self._pushRow, self._pushCol,1,1)
ttk.pyTTkSlot()
def _clickExpand(self):
if not self._expandButton.isVisible(): return
self._expandButton.hide()
dir, wid = self._expandStuff
row = wid._row
col = wid._col
rowspan = wid._rowspan
colspan = wid._colspan
self.layout().removeItem(wid)
if ttk.TTkK.TOP and row==0:
self.layout().insertRow(0)
row+=1
elif ttk.TTkK.LEFT and col==0:
self.layout().insertColumn(0)
col+=1
rc = {
ttk.TTkK.TOP : (row-1,col, rowspan+1,colspan),
ttk.TTkK.BOTTOM : (row, col, rowspan+1,colspan),
ttk.TTkK.LEFT : (row, col-1,rowspan,colspan+1),
ttk.TTkK.RIGHT : (row, col, rowspan,colspan+1),
}.get(dir, (row,col,rowspan,colspan))
self.layout().addItem(wid,*rc)
def _processMouseOver(self, x, y):
# cehck the closest edge
col, row, dir = 0,0,None
wid = None
if type(self.layout()) != ttk.TTkGridLayout:
return dir,wid
# Retrieve a list of widths,heights
rows,cols = self.layout().gridSize()
if not rows or not cols: return dir,wid
horSizes, verSizes = self.layout().getSizes()
# Find the row/col where the pointer is in
for col,(a,b) in enumerate(horSizes):
if a <= x < a+b: break
for row,(a,b) in enumerate(verSizes):
if a <= y < a+b: break
ix, iw = horSizes[col]
iy, ih = verSizes[row]
wid = self.layout().itemAtPosition(row,col)
if wid == None:
return dir,wid
rowspan = wid._rowspan
colspan = wid._colspan
maxw,maxh = wid.maximumSize()
#Top
if (( y==iy ) and maxh>1 and
( not self.layout().itemAtPosition(row-1,col) ) ):
dir = ttk.TTkK.TOP
#Bottom
elif ((iy+ih==y+1) and maxh>1 and
not self.layout().itemAtPosition(row+rowspan,col)):
dir = ttk.TTkK.BOTTOM
#Left
elif ((x==ix) and maxw>1 and
( not self.layout().itemAtPosition(row,col-1) ) ):
dir = ttk.TTkK.LEFT
#Right
elif ((ix+iw==x+1) and maxw>1 and
not self.layout().itemAtPosition(row,col+colspan)):
dir = ttk.TTkK.RIGHT
# ttk.TTkLog.debug(f"Move {dir} {wid}")
# ttk.TTkLog.debug(f"{horSizes=}")
# ttk.TTkLog.debug(f"{verSizes=}")
# ttk.TTkLog.debug(f"{row=} {col=} {dir=} {self._dragOver=}")
self.update()
return dir, wid
def _processDragOver(self, x, y):
# cehck the closest edge
col, row, dir = 0,0,None
ret = None
# Retrieve a list of widths,heights
rows,cols = self.layout().gridSize()
if not rows or not cols: return col,row,dir,ret
horSizes, verSizes = self.layout().getSizes()
# Find the row/col where the pointer is in
for col,(a,b) in enumerate(horSizes):
if a <= x < a+b: break
for row,(a,b) in enumerate(verSizes):
if a <= y < a+b: break
ix, iw = horSizes[col]
iy, ih = verSizes[row]
if self.layout().itemAtPosition(row,col) == None:
ret = (ix, iy, iw, ih)
else:
#Top
if ((y==iy) and (self._orientation & ttk.TTkK.VERTICAL) and
( row==0 or
( row>0 and self.layout().itemAtPosition(row,col) != self.layout().itemAtPosition(row-1,col)))):
dir = ttk.TTkK.VERTICAL
if row>0 and self.layout().itemAtPosition(row-1,col):
ret = (ix, iy-1, iw, 2)
else:
ret = (ix, iy, iw, 1)
#Bottom
if ((iy+ih==y+1) and (self._orientation & ttk.TTkK.VERTICAL) and
self.layout().itemAtPosition(row,col) != self.layout().itemAtPosition(row+1,col)):
dir = ttk.TTkK.VERTICAL
if row<rows-1 and self.layout().itemAtPosition(row+1,col):
ret = (ix, iy+ih-1, iw, 2)
else:
ret = (ix, iy+ih-1, iw, 1)
#Left
if ((x==ix) and (self._orientation & ttk.TTkK.HORIZONTAL) and
( col==0 or
( col>0 and self.layout().itemAtPosition(row,col) != self.layout().itemAtPosition(row,col-1)))):
dir = ttk.TTkK.HORIZONTAL
if col>0 and self.layout().itemAtPosition(row,col-1):
ret = (ix-1, iy, 2, ih)
else:
ret = (ix, iy, 1, ih)
#Right
if ((ix+iw==x+1) and (self._orientation & ttk.TTkK.HORIZONTAL) and
self.layout().itemAtPosition(row,col) != self.layout().itemAtPosition(row,col+1)):
dir = ttk.TTkK.HORIZONTAL
if col<cols-1 and self.layout().itemAtPosition(row,col+1):
ret = (ix+iw-1, iy, 2, ih)
else:
ret = (ix+iw-1, iy, 1, ih)
# If we are on the edge of the item push to the next spot
if dir == ttk.TTkK.HORIZONTAL and ix+iw==x+1:
col+=1
if dir == ttk.TTkK.VERTICAL and iy+ih==y+1:
row+=1
# ttk.TTkLog.debug(f"{horSizes=}")
# ttk.TTkLog.debug(f"{verSizes=}")
# ttk.TTkLog.debug(f"{row=} {col=} {dir=} {self._dragOver=}")
self.update()
return row, col, dir, ret
# Stupid hack to paint on top of the child widgets
def paintChildCanvas(self):
super().paintChildCanvas()
if self._dragOver is not None:
x,y,w,h = self._dragOver
if h==1 and w==1:
self.getCanvas().drawText(text='', pos=(x,y), color=ttk.TTkColor.fg("FFFF00"))
elif w==1:
self.getCanvas().drawText(text='', pos=(x,y), color=ttk.TTkColor.fg("FFFF00"))
self.getCanvas().drawText(text='', pos=(x,y+h-1), color=ttk.TTkColor.fg("FFFF00"))
for yy in range(y+1,y+h-1):
self.getCanvas().drawText(text='', pos=(x, yy), color=ttk.TTkColor.fg("FFFF00"))
elif h==1:
txt = ''+''*(w-2)+''
self.getCanvas().drawText(text=txt, pos=(x,y), width=w, color=ttk.TTkColor.fg("FFFF00"))
else:
self.getCanvas().drawBox(pos=(x,y), size=(w,h), color=ttk.TTkColor.fg("FFFF00"))