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.
274 lines
11 KiB
274 lines
11 KiB
|
2 years ago
|
# MIT License
|
||
|
|
#
|
||
|
|
# Copyright (c) 2024 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.
|
||
|
|
|
||
|
2 years ago
|
__all__ = ['LayersControl']
|
||
|
2 years ago
|
|
||
|
|
import TermTk as ttk
|
||
|
|
|
||
|
2 years ago
|
from .glbls import glbls
|
||
|
2 years ago
|
from .canvaslayer import CanvasLayer
|
||
|
2 years ago
|
|
||
|
2 years ago
|
class _layerButton(ttk.TTkContainer):
|
||
|
2 years ago
|
classStyle = {
|
||
|
|
'default': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#000044"),
|
||
|
|
'borderColor': ttk.TTkColor.fg('#CCDDDD'),
|
||
|
|
'grid':1},
|
||
|
|
'disabled': {'color': ttk.TTkColor.fg('#888888'),
|
||
|
|
'borderColor':ttk.TTkColor.fg('#888888'),
|
||
|
|
'grid':0},
|
||
|
|
'hover': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#000050")+ttk.TTkColor.BOLD,
|
||
|
|
'borderColor': ttk.TTkColor.fg("#FFFFCC")+ttk.TTkColor.BOLD,
|
||
|
|
'grid':1},
|
||
|
|
'selected': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#004488"),
|
||
|
|
'borderColor': ttk.TTkColor.fg("#FFFF00"),
|
||
|
|
'grid':0},
|
||
|
|
'unchecked': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#000044"),
|
||
|
|
'borderColor': ttk.TTkColor.RST,
|
||
|
|
'grid':3},
|
||
|
|
'clicked': {'color': ttk.TTkColor.fg("#FFFFDD")+ttk.TTkColor.BOLD,
|
||
|
|
'borderColor': ttk.TTkColor.fg("#DDDDDD")+ttk.TTkColor.BOLD,
|
||
|
|
'grid':0},
|
||
|
|
'focus': {'color': ttk.TTkColor.fg("#dddd88")+ttk.TTkColor.bg("#000044")+ttk.TTkColor.BOLD,
|
||
|
|
'borderColor': ttk.TTkColor.fg("#ffff00") + ttk.TTkColor.BOLD,
|
||
|
|
'grid':1},
|
||
|
|
}
|
||
|
|
|
||
|
2 years ago
|
__slots__ = ('_canvasLayer','_first', '_isSelected',
|
||
|
|
'_ledit'
|
||
|
2 years ago
|
)
|
||
|
2 years ago
|
def __init__(self, layer:CanvasLayer, **kwargs):
|
||
|
2 years ago
|
self._canvasLayer:CanvasLayer = layer
|
||
|
2 years ago
|
self._isSelected = False
|
||
|
|
self._first = True
|
||
|
2 years ago
|
|
||
|
2 years ago
|
super().__init__(**kwargs|{'layout':ttk.TTkGridLayout()})
|
||
|
|
self.setPadding(1,1,7,2)
|
||
|
|
self._ledit = ttk.TTkLineEdit(parent=self, text=layer.name(),visible=False)
|
||
|
|
self._ledit.focusChanged.connect(self._ledit.setVisible)
|
||
|
|
self._ledit.textEdited.connect(self._textEdited)
|
||
|
2 years ago
|
# self.setFocusPolicy(ttk.TTkK.ClickFocus)
|
||
|
2 years ago
|
|
||
|
2 years ago
|
def data(self):
|
||
|
2 years ago
|
return self._canvasLayer
|
||
|
2 years ago
|
|
||
|
|
def setData(self, data):
|
||
|
2 years ago
|
self._canvasLayer = data
|
||
|
2 years ago
|
self.update()
|
||
|
|
|
||
|
2 years ago
|
def setSelected(self, selected:bool=True) -> None:
|
||
|
|
if self._isSelected == selected: return
|
||
|
|
self._isSelected = selected
|
||
|
|
self.update()
|
||
|
|
|
||
|
2 years ago
|
@ttk.pyTTkSlot(str)
|
||
|
|
def _textEdited(self, text):
|
||
|
2 years ago
|
self._canvasLayer.setName(text)
|
||
|
2 years ago
|
|
||
|
2 years ago
|
def mousePressEvent(self, evt) -> bool:
|
||
|
|
if evt.x <= 3:
|
||
|
2 years ago
|
self._canvasLayer.setVisible(not self._canvasLayer.visible())
|
||
|
2 years ago
|
self.setFocus()
|
||
|
|
self.update()
|
||
|
|
return True
|
||
|
|
|
||
|
2 years ago
|
def mouseReleaseEvent(self, evt) -> bool:
|
||
|
2 years ago
|
glbls.layers.selectLayer(self._canvasLayer)
|
||
|
2 years ago
|
return True
|
||
|
|
|
||
|
2 years ago
|
def mouseDoubleClickEvent(self, evt) -> bool:
|
||
|
|
self._ledit.setVisible(True)
|
||
|
|
self._ledit.setFocus()
|
||
|
|
return True
|
||
|
|
|
||
|
2 years ago
|
def mouseDragEvent(self, evt) -> bool:
|
||
|
|
drag = ttk.TTkDrag()
|
||
|
|
drag.setData(self)
|
||
|
2 years ago
|
name = self._canvasLayer.name()
|
||
|
2 years ago
|
pm = ttk.TTkCanvas(width=len(name)+4,height=3)
|
||
|
|
pm.drawBox(pos=(0,0),size=pm.size())
|
||
|
|
pm.drawText(pos=(2,1), text=name)
|
||
|
1 year ago
|
drag.setHotSpot(pos=(5, 1))
|
||
|
2 years ago
|
drag.setPixmap(pm)
|
||
|
|
drag.exec()
|
||
|
|
return True
|
||
|
|
|
||
|
2 years ago
|
def paintEvent(self, canvas: ttk.TTkCanvas):
|
||
|
|
if self._isSelected:
|
||
|
|
style = self.style()['selected']
|
||
|
|
else:
|
||
|
|
style = self.currentStyle()
|
||
|
|
borderColor = style['borderColor']
|
||
|
|
textColor = style['color']
|
||
|
2 years ago
|
btnVisible = '▣' if self._canvasLayer.visible() else '□'
|
||
|
2 years ago
|
w,h = self.size()
|
||
|
|
canvas.drawText( pos=(0,0),text=f" ┏{'━'*(w-7)}┓",color=borderColor)
|
||
|
|
canvas.drawText( pos=(0,2),text=f" ┗{'━'*(w-7)}┛",color=borderColor)
|
||
|
|
if self._first:
|
||
|
2 years ago
|
canvas.drawText(pos=(0,1),text=f" {btnVisible} - ┃{' '*(w-7)}┃",color=borderColor)
|
||
|
2 years ago
|
else:
|
||
|
2 years ago
|
canvas.drawText(pos=(0,1),text=f" {btnVisible} - ╽{' '*(w-7)}╽",color=borderColor)
|
||
|
2 years ago
|
canvas.drawTTkString(pos=(7,1),text=self._canvasLayer.name(), width=w-9, color=textColor)
|
||
|
2 years ago
|
|
||
|
|
class LayerScrollWidget(ttk.TTkAbstractScrollView):
|
||
|
2 years ago
|
__slots__ = ('_layers','_layerButtons', '_dropTo')
|
||
|
2 years ago
|
def __init__(self, **kwargs):
|
||
|
2 years ago
|
self._layers = glbls.layers
|
||
|
2 years ago
|
self._dropTo = None
|
||
|
2 years ago
|
self._layerButtons:list[_layerButton] = []
|
||
|
2 years ago
|
super().__init__(**kwargs)
|
||
|
|
self.viewChanged.connect(self._placeTheButtons)
|
||
|
2 years ago
|
self.viewChanged.connect(self._viewChangedHandler)
|
||
|
|
|
||
|
2 years ago
|
glbls.layers.layerAdded.connect(self._layerAdded)
|
||
|
|
glbls.layers.layerDeleted.connect(self._layerDeleted)
|
||
|
|
glbls.layers.layerSelected.connect(self._layerSelected)
|
||
|
|
glbls.layers.layersOrderChanged.connect(self._layersOrderChanged)
|
||
|
|
|
||
|
2 years ago
|
@ttk.pyTTkSlot(CanvasLayer)
|
||
|
|
def _layerAdded(self, data:CanvasLayer) -> None:
|
||
|
2 years ago
|
self._updateLayerButtons()
|
||
|
|
|
||
|
2 years ago
|
@ttk.pyTTkSlot(CanvasLayer)
|
||
|
|
def _layerDeleted(self, data:CanvasLayer) -> None:
|
||
|
2 years ago
|
self._updateLayerButtons()
|
||
|
|
|
||
|
2 years ago
|
@ttk.pyTTkSlot(CanvasLayer)
|
||
|
|
def _layerSelected(self, data:CanvasLayer) -> None:
|
||
|
2 years ago
|
for btn in self._layerButtons:
|
||
|
2 years ago
|
btn.setSelected(id(btn.data()) == id(data))
|
||
|
2 years ago
|
|
||
|
2 years ago
|
@ttk.pyTTkSlot(list[CanvasLayer])
|
||
|
|
def _layersOrderChanged(self, layers:list[CanvasLayer]) -> None:
|
||
|
2 years ago
|
self._updateLayerButtons()
|
||
|
|
|
||
|
|
def _updateLayerButtons(self):
|
||
|
|
layers = glbls.layers.layers()
|
||
|
2 years ago
|
selected = glbls.layers.selected()
|
||
|
2 years ago
|
# remove unused buttons
|
||
|
|
for btn in self._layerButtons[len(layers):]:
|
||
|
|
self.layout().removeWidget(btn)
|
||
|
2 years ago
|
btn._canvasLayer.nameChanged.clear()
|
||
|
2 years ago
|
|
||
|
|
self._layerButtons = self._layerButtons[:len(layers)]
|
||
|
2 years ago
|
for i,layer in enumerate(layers):
|
||
|
2 years ago
|
if i >= len(self._layerButtons):
|
||
|
|
self._layerButtons.append(_layerButton(parent=self,layer=layer))
|
||
|
|
btn = self._layerButtons[i]
|
||
|
|
btn.setData(layer)
|
||
|
2 years ago
|
|
||
|
|
for btn in self._layerButtons:
|
||
|
|
btn.setSelected(btn.data() == selected)
|
||
|
|
|
||
|
2 years ago
|
self._placeTheButtons()
|
||
|
|
self.viewChanged.emit()
|
||
|
|
|
||
|
|
|
||
|
2 years ago
|
@ttk.pyTTkSlot()
|
||
|
|
def _viewChangedHandler(self):
|
||
|
|
x,y = self.getViewOffsets()
|
||
|
|
self.layout().setOffset(-x,-y)
|
||
|
2 years ago
|
|
||
|
|
def viewDisplayedSize(self) -> tuple:
|
||
|
|
return self.size()
|
||
|
|
|
||
|
|
def maximumWidth(self): return 0x10000
|
||
|
|
def maximumHeight(self): return 0x10000
|
||
|
|
def minimumWidth(self): return 0
|
||
|
|
def minimumHeight(self): return 0
|
||
|
|
|
||
|
|
def _placeTheButtons(self):
|
||
|
|
w,h = self.size()
|
||
|
2 years ago
|
for i,l in enumerate(self._layerButtons):
|
||
|
2 years ago
|
l._first = i==0
|
||
|
|
l.setGeometry(0,i*2,w,3)
|
||
|
|
l.lowerWidget()
|
||
|
|
self.update()
|
||
|
|
|
||
|
2 years ago
|
def dragEnterEvent(self, evt) -> bool:
|
||
|
|
if type(evt.data())!=_layerButton: return False
|
||
|
2 years ago
|
x,y = self.getViewOffsets()
|
||
|
2 years ago
|
self._dropTo = max(0,min(len(self._layerButtons),(evt.y-1+y)//2))
|
||
|
2 years ago
|
self.update()
|
||
|
|
return True
|
||
|
|
def dragLeaveEvent(self, evt) -> bool:
|
||
|
|
if type(evt.data())!=_layerButton: return False
|
||
|
|
self._dropTo = None
|
||
|
|
self.update()
|
||
|
|
return True
|
||
|
|
def dragMoveEvent(self, evt) -> bool:
|
||
|
|
if type(evt.data())!=_layerButton: return False
|
||
|
2 years ago
|
x,y = self.getViewOffsets()
|
||
|
2 years ago
|
self._dropTo = max(0,min(len(self._layerButtons),(evt.y+y)//2))
|
||
|
2 years ago
|
self.update()
|
||
|
2 years ago
|
# ttk.TTkLog.debug(f"{evt.x},{evt.y-y} - {len(self._layerButtons)} - {self._dropTo}")
|
||
|
2 years ago
|
return True
|
||
|
|
def dropEvent(self, evt) -> bool:
|
||
|
|
if type(evt.data())!=_layerButton: return False
|
||
|
2 years ago
|
x,y = self.getViewOffsets()
|
||
|
2 years ago
|
self._dropTo = None
|
||
|
|
data = evt.data()
|
||
|
2 years ago
|
dropPos = max(0,min(len(self._layerButtons),(evt.y+y)//2))
|
||
|
2 years ago
|
# ttk.TTkLog.debug(f"{evt.x},{evt.y-y} - {len(self._layerButtons)} - {self._dropTo} {dropPos}")
|
||
|
2 years ago
|
glbls.layers.moveLayer(self._layerButtons.index(data), dropPos)
|
||
|
2 years ago
|
glbls.saveSnapshot()
|
||
|
2 years ago
|
return True
|
||
|
|
|
||
|
|
# Stupid hack to paint on top of the child widgets
|
||
|
|
def paintChildCanvas(self):
|
||
|
|
super().paintChildCanvas()
|
||
|
2 years ago
|
offx, offy = self.getViewOffsets()
|
||
|
2 years ago
|
if self._dropTo is None: return
|
||
|
2 years ago
|
canvas = self.getCanvas()
|
||
|
|
w,h = canvas.size()
|
||
|
|
color = ttk.TTkColor.YELLOW
|
||
|
2 years ago
|
canvas.drawText(pos=(0,(self._dropTo)*2-offy),text=f"╞{'═'*(w-2)}╍",color=color)
|
||
|
2 years ago
|
|
||
|
|
|
||
|
|
|
||
|
2 years ago
|
class LayersControl(ttk.TTkGridLayout):
|
||
|
|
__slots__ = ('_scrollWidget')
|
||
|
2 years ago
|
def __init__(self, **kwargs):
|
||
|
|
super().__init__(**kwargs)
|
||
|
|
self._scrollWidget = _lsw = LayerScrollWidget()
|
||
|
|
_sa = ttk.TTkAbstractScrollArea(scrollWidget=self._scrollWidget,minWidth=16)
|
||
|
|
_sa.setViewport(_lsw)
|
||
|
|
self.addWidget(_sa,0,0,1,5)
|
||
|
|
self.addWidget(btnAdd :=ttk.TTkButton(text='add') ,1,0)
|
||
|
|
self.addWidget(btnUp :=ttk.TTkButton(text='▲',maxWidth=3) ,1,1)
|
||
|
|
self.addWidget(btnDown:=ttk.TTkButton(text='▼',maxWidth=3) ,1,2)
|
||
|
|
# self.addItem(ttk.TTkLayout(),1,3)
|
||
|
|
self.addWidget(btnDel :=ttk.TTkButton(text=ttk.TTkString('del',ttk.TTkColor.RED),maxWidth=5),1,4)
|
||
|
|
|
||
|
|
btnAdd.setToolTip( "Create a new Layer\nand add it to the image")
|
||
|
|
btnDel.setToolTip( "Delete the selected Layer")
|
||
|
|
btnUp.setToolTip( "Raise the selected Layer one step")
|
||
|
|
btnDown.setToolTip("Lower the selected Layer one step")
|
||
|
|
|
||
|
2 years ago
|
btnAdd.clicked.connect( glbls.layers.addLayer)
|
||
|
|
btnDel.clicked.connect( glbls.layers.delLayer)
|
||
|
|
btnUp.clicked.connect( glbls.layers.moveUp)
|
||
|
|
btnDown.clicked.connect(glbls.layers.moveDown)
|
||
|
2 years ago
|
btnAdd.clicked.connect( glbls.saveSnapshot)
|
||
|
|
btnDel.clicked.connect( glbls.saveSnapshot)
|
||
|
|
btnUp.clicked.connect( glbls.saveSnapshot)
|
||
|
|
btnDown.clicked.connect(glbls.saveSnapshot)
|