Browse Source

Adapted the drag/drop to the new layers structure

pull/260/head
Eugenio Parodi 2 years ago
parent
commit
e1559959f1
  1. 4
      .vscode/launch.json
  2. 6
      docs/MDNotes/TODO.md
  3. 4
      setup.dumbPaintTool.py
  4. 4
      setup.ttkDesigner.py
  5. 83
      tools/dumbPaintTool/app/layersctrl.py
  6. 2
      tools/dumbPaintTool/app/maintemplate.py
  7. 56
      tools/dumbPaintTool/app/paintarea.py
  8. 34
      tools/dumbPaintTool/app/state/layers.py

4
.vscode/launch.json vendored

@ -38,14 +38,14 @@
"name": "Python: DumbPaintTool",
"type": "debugpy",
"request": "launch",
"program": "tools/dumb.paint.tool.py",
"program": "tools/dumbPaintTool.py",
"console": "integratedTerminal",
"justMyCode": true
},{
"name": "Python: DumbPaintTool File",
"type": "debugpy",
"request": "launch",
"program": "tools/dumb.paint.tool.py",
"program": "tools/dumbPaintTool.py",
"console": "integratedTerminal",
"justMyCode": true,
"args":["experiments/untitled.DPT.json"]

6
docs/MDNotes/TODO.md

@ -177,6 +177,6 @@
- [x] Events (Signal/Slots)
- [x] Themes
#### Yes/No Ok/Cancel Picker
- [ ] Basic Implementation
- [ ] Events (Signal/Slots)
- [ ] Themes
- [x] Basic Implementation
- [x] Events (Signal/Slots)
- [x] Themes

4
setup.dumbPaintTool.py

@ -29,10 +29,6 @@ setup(
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Topic :: Terminals",
"Intended Audience :: Education",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Topic :: Terminals",
"Topic :: Text Editors :: Text Processing",
"Topic :: Multimedia :: Graphics",
"Topic :: Multimedia :: Graphics :: Editors",

4
setup.ttkDesigner.py

@ -29,10 +29,6 @@ setup(
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Topic :: Terminals",
"Intended Audience :: Education",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Topic :: Terminals",
"Topic :: Text Editors :: Text Processing",
"Topic :: Software Development :: User Interfaces",
"Topic :: Software Development :: Libraries",

83
tools/dumbPaintTool/app/layersctrl.py

@ -55,10 +55,9 @@ class _layerButton(ttk.TTkContainer):
__slots__ = ('_layerData','_first', '_isSelected', '_layerVisible',
'_ledit',
# signals
'clicked', 'visibilityToggled',
'visibilityToggled',
)
def __init__(self, layer:LayerData, **kwargs):
self.clicked = ttk.pyTTkSignal(_layerButton)
self._layerData:LayerData = layer
self._isSelected = False
self._first = True
@ -79,6 +78,11 @@ class _layerButton(ttk.TTkContainer):
self._layerData = data
self.update()
def setSelected(self, selected:bool=True) -> None:
if self._isSelected == selected: return
self._isSelected = selected
self.update()
@ttk.pyTTkSlot(str)
def _textEdited(self, text):
self._layerData.setName(text)
@ -92,7 +96,7 @@ class _layerButton(ttk.TTkContainer):
return True
def mouseReleaseEvent(self, evt) -> bool:
self.clicked.emit(self)
glbls.layers.selectLayer(self._layerData)
return True
def mouseDoubleClickEvent(self, evt) -> bool:
@ -130,12 +134,10 @@ class _layerButton(ttk.TTkContainer):
canvas.drawTTkString(pos=(7,1),text=self._layerData.name(), width=w-9, color=textColor)
class LayerScrollWidget(ttk.TTkAbstractScrollView):
__slots__ = ('_layers','_layerButtons','_layers_XXX_ToBeRemoved','_selected_XXX_ToBeRemoved', '_dropTo')
__slots__ = ('_layers','_layerButtons', '_dropTo')
def __init__(self, **kwargs):
self._layers = glbls.layers
self._selected_XXX_ToBeRemoved = None
self._dropTo = None
self._layers_XXX_ToBeRemoved:list[_layerButton] = []
self._layerButtons:list[_layerButton] = []
super().__init__(**kwargs)
self.viewChanged.connect(self._placeTheButtons)
@ -156,7 +158,8 @@ class LayerScrollWidget(ttk.TTkAbstractScrollView):
@ttk.pyTTkSlot(LayerData)
def _layerSelected(self, data:LayerData) -> None:
self._updateLayerButtons()
for btn in self._layerButtons:
btn.setSelected(btn.data() == data)
@ttk.pyTTkSlot(list[LayerData])
def _layersOrderChanged(self, layers:list[LayerData]) -> None:
@ -167,12 +170,10 @@ class LayerScrollWidget(ttk.TTkAbstractScrollView):
# remove unused buttons
for btn in self._layerButtons[len(layers):]:
self.layout().removeWidget(btn)
btn.clicked.clear()
btn.visibilityToggled.clear()
btn._layerData.nameChanged.clear()
self._layerButtons = self._layerButtons[:len(layers)]
for i,layer in enumerate(reversed(layers)):
for i,layer in enumerate(layers):
if i >= len(self._layerButtons):
self._layerButtons.append(_layerButton(parent=self,layer=layer))
btn = self._layerButtons[i]
@ -181,7 +182,6 @@ class LayerScrollWidget(ttk.TTkAbstractScrollView):
self.viewChanged.emit()
@ttk.pyTTkSlot()
def _viewChangedHandler(self):
x,y = self.getViewOffsets()
@ -199,40 +199,6 @@ class LayerScrollWidget(ttk.TTkAbstractScrollView):
def minimumWidth(self): return 0
def minimumHeight(self): return 0
@ttk.pyTTkSlot(_layerButton)
def _clickedLayer(self, layerButton:_layerButton):
if sel:=self._selected_XXX_ToBeRemoved:
sel._isSelected = False
sel.update()
self._selected_XXX_ToBeRemoved = layerButton
layerButton._isSelected = True
self.layerSelected.emit(layerButton._layerData)
self.update()
def clear(self):
for layBtn in self._layers_XXX_ToBeRemoved:
self.layout().removeWidget(layBtn)
layBtn.clicked.clear()
layBtn.visibilityToggled.clear()
layBtn._layerData.nameChanged.clear()
self._layers_XXX_ToBeRemoved.clear()
self.update()
@ttk.pyTTkSlot()
def addLayer(self,name=None, data=None):
name = name if name else f"Layer #{len(self._layers_XXX_ToBeRemoved)}"
_l=LayerData(name=name,data=data)
newLayerBtn:_layerButton = _layerButton(parent=self,layer=_l)
self._layers_XXX_ToBeRemoved.insert(0,newLayerBtn)
if sel:=self._selected_XXX_ToBeRemoved: sel._isSelected = False
self._selected_XXX_ToBeRemoved = newLayerBtn
newLayerBtn._isSelected = True
newLayerBtn.clicked.connect(self._clickedLayer)
self.viewChanged.emit()
self._placeTheButtons()
self.layerAdded.emit(newLayerBtn._layerData)
return _l
def _placeTheButtons(self):
w,h = self.size()
for i,l in enumerate(self._layerButtons):
@ -241,14 +207,10 @@ class LayerScrollWidget(ttk.TTkAbstractScrollView):
l.lowerWidget()
self.update()
@ttk.pyTTkSlot()
def delLayer(self):
self._layers_XXX_ToBeRemoved.remove()
def dragEnterEvent(self, evt) -> bool:
if type(evt.data())!=_layerButton: return False
x,y = self.getViewOffsets()
self._dropTo = max(0,min(len(self._layers_XXX_ToBeRemoved),(evt.y-1+y)//2))
self._dropTo = max(0,min(len(self._layerButtons),(evt.y-1+y)//2))
self.update()
return True
def dragLeaveEvent(self, evt) -> bool:
@ -259,9 +221,9 @@ class LayerScrollWidget(ttk.TTkAbstractScrollView):
def dragMoveEvent(self, evt) -> bool:
if type(evt.data())!=_layerButton: return False
x,y = self.getViewOffsets()
self._dropTo = max(0,min(len(self._layers_XXX_ToBeRemoved),(evt.y-1+y)//2))
self._dropTo = max(0,min(len(self._layerButtons),(evt.y+y)//2))
self.update()
ttk.TTkLog.debug(f"{evt.x},{evt.y-y} - {len(self._layers_XXX_ToBeRemoved)} - {self._dropTo}")
ttk.TTkLog.debug(f"{evt.x},{evt.y-y} - {len(self._layerButtons)} - {self._dropTo}")
return True
def dropEvent(self, evt) -> bool:
if type(evt.data())!=_layerButton: return False
@ -269,14 +231,15 @@ class LayerScrollWidget(ttk.TTkAbstractScrollView):
self._dropTo = None
data = evt.data()
# dropPos = len(self._layers)-(evt.y-1)//2
dropPos = max(0,min(len(self._layers_XXX_ToBeRemoved),(evt.y-1+y)//2))
ttk.TTkLog.debug(f"{evt.x},{evt.y-y} - {len(self._layers_XXX_ToBeRemoved)} - {self._dropTo} {dropPos}")
if dropPos > self._layers_XXX_ToBeRemoved.index(data):
dropPos -= 1
self._layers_XXX_ToBeRemoved.remove(data)
self._layers_XXX_ToBeRemoved.insert(dropPos,data)
self._placeTheButtons()
self.layersOrderChanged.emit([_l._layerData for _l in self._layers_XXX_ToBeRemoved])
dropPos = max(0,min(len(self._layerButtons),(evt.y+y)//2))
ttk.TTkLog.debug(f"{evt.x},{evt.y-y} - {len(self._layerButtons)} - {self._dropTo} {dropPos}")
glbls.layers.moveLayer(self._layerButtons.index(data), dropPos)
# if dropPos > self._layerButtons.index(data):
# dropPos -= 1
# self._layerButtons.remove(data)
# self._layerButtons.insert(dropPos,data)
# self._placeTheButtons()
# self.layersOrderChanged.emit([_l._layerData for _l in self._layers_XXX_ToBeRemoved])
return True
# Stupid hack to paint on top of the child widgets

2
tools/dumbPaintTool/app/maintemplate.py

@ -198,7 +198,7 @@ class PaintTemplate(ttk.TTkAppTemplate):
if (
( 'version' in data and data['version'] == '1.0.0' ) or
( 'version' in data and data['version'] == '1.0.1' and data['type'] == 'DumbPaintTool/Document') ):
for ld in reversed(data['layers']):
for ld in data['layers']:
cl = CanvasLayer()
cl.importLayer(ld)
glbls.layers.addLayer(cl.name(),cl)

56
tools/dumbPaintTool/app/paintarea.py

@ -27,6 +27,7 @@ import TermTk as ttk
from .canvaslayer import CanvasLayer
from .const import ToolType
from .glbls import glbls
from .state import LayerData
class PaintArea(ttk.TTkAbstractScrollView):
__slots__ = ('_canvasLayers', '_currentLayer',
@ -68,17 +69,40 @@ class PaintArea(ttk.TTkAbstractScrollView):
glbls.brush.colorChanged.connect( self.updateGlyph)
glbls.brush.glyphEnabledChanged.connect(self.updateGlyph)
glbls.layers.layerSelected.connect(self.setCurrentLayer)
# glbls.layers.layerAdded.connect()
# glbls.layers.layerDeleted.connect()
glbls.layers.layersOrderChanged.connect(self._setCanvasLayers)
# glbls.layers.layerSelected.connect(self.setCurrentLayer)
# # glbls.layers.layerAdded.connect()
# # glbls.layers.layerDeleted.connect()
# glbls.layers.layersOrderChanged.connect(self._setCanvasLayers)
glbls.layers.layerAdded.connect(self._layerAdded)
glbls.layers.layerDeleted.connect(self._layerDeleted)
glbls.layers.layerSelected.connect(self._layerSelected)
glbls.layers.layersOrderChanged.connect(self._layersOrderChanged)
# Retrieve the default values
self.setTool( glbls.brush.toolType())
self.updateGlyph()
def _setCanvasLayers(self, cl):
self._canvasLayers = cl
@ttk.pyTTkSlot(LayerData)
def _layerAdded(self, data:LayerData) -> None:
self._canvasLayers = glbls.layers.layersData()
self.update()
# raise NotImplementedError()
@ttk.pyTTkSlot(LayerData)
def _layerDeleted(self, data:LayerData) -> None:
self._canvasLayers = glbls.layers.layersData()
self.update()
# raise NotImplementedError()
@ttk.pyTTkSlot(LayerData)
def _layerSelected(self, data:LayerData) -> None:
self._currentLayer = data.data() if data else None
self.update()
@ttk.pyTTkSlot(list[LayerData])
def _layersOrderChanged(self, layers:list[LayerData]) -> None:
self._canvasLayers = [ld.data() for ld in layers]
self.update()
def _getGeometry(self):
@ -87,9 +111,9 @@ class PaintArea(ttk.TTkAbstractScrollView):
ww,wh = self.size()
x1,y1 = min(0,dx),min(0,dy)
x2,y2 = max(dx+dw,ww),max(dy+dh,wh)
for l in self._canvasLayers:
lx,ly = l.pos()
lw,lh = l.size()
for cl in self._canvasLayers:
lx,ly = cl.pos()
lw,lh = cl.size()
x1 = min(x1,dx+lx)
y1 = min(y1,dy+ly)
x2 = max(x2,dx+lx+lw)
@ -157,10 +181,6 @@ class PaintArea(ttk.TTkAbstractScrollView):
self._documentSize = (w,h)
self.update()
def setCurrentLayer(self, layer:CanvasLayer):
self._currentLayer = layer
self.update()
def exportLayer(self, full=False, palette=True, crop=True) -> dict:
if self._currentLayer:
return self._currentLayer.exportLayer(full=full,palette=palette,crop=crop)
@ -229,7 +249,7 @@ class PaintArea(ttk.TTkAbstractScrollView):
if not self._resizeData:
# Get The Layer to Move
self._moveData = None
for lm in reversed(self._canvasLayers):
for lm in self._canvasLayers:
mpx,mpy = mp
lmx,lmy = lm.pos()
self._moveData = {'type':PaintArea,'pos':(dx,dy)}
@ -271,7 +291,7 @@ class PaintArea(ttk.TTkAbstractScrollView):
if mp and self._tool & ToolType.PICK:
glbls.brush.setToolType(self._tool & ~ToolType.PICK)
mpx,mpy = mp
for lm in reversed(self._canvasLayers):
for lm in self._canvasLayers:
lmx,lmy = lm.pos()
if lm.isOpaque(mpx-lmx-dx,mpy-lmy-dy):
glbls.brush.setArea(lm.trim().toTTkString())
@ -467,9 +487,9 @@ class PaintArea(ttk.TTkAbstractScrollView):
canvas.fill(pos=(dx-ox-2 ,0 ),size=(2,ch),color=tcd)
canvas.fill(pos=(dx-ox+dw,0 ),size=(2,ch),color=tcd)
for l in self._canvasLayers:
lx,ly = l.pos()
l.drawInCanvas(pos=(lx+dox,ly+doy),canvas=canvas)
for cl in reversed(self._canvasLayers):
lx,ly = cl.pos()
cl.drawInCanvas(pos=(lx+dox,ly+doy),canvas=canvas)
if self._tool & ToolType.RESIZE:
rd = self._resizeData

34
tools/dumbPaintTool/app/state/layers.py

@ -23,6 +23,7 @@
__all__ = ['Layers', 'LayerData']
import TermTk as ttk
from ..canvaslayer import CanvasLayer
class LayerData():
__slots__ = ('_name','_data',
@ -31,7 +32,7 @@ class LayerData():
def __init__(self,name:ttk.TTkString=ttk.TTkString('New'),data=None) -> None:
self._name:ttk.TTkString = ttk.TTkString(name) if isinstance(name,str) else name
self.visibilityToggled = ttk.pyTTkSignal(bool)
self._data = data if data else {}
self._data = data if data else CanvasLayer()
self.nameChanged = ttk.pyTTkSignal(str)
def name(self):
@ -58,6 +59,9 @@ class Layers():
self._selected = None
self._layers:list[LayerData] = []
def __len__(self):
return len(self._layers)
def layers(self) -> list[LayerData]:
return self._layers
@ -76,7 +80,7 @@ class Layers():
self._layers = []
self._selected = None
self.layerSelected.emit(None)
self.layersOrderChanged.emit(self.layersData())
self.layersOrderChanged.emit(self._layers)
@ttk.pyTTkSlot(int, int)
def move(self, fr:int, to:int) -> None:
@ -84,7 +88,7 @@ class Layers():
if to>fr:
to-=1
self._layers.insert(to,obj)
self.layersOrderChanged.emit(self.layersData())
self.layersOrderChanged.emit(self._layers)
@ttk.pyTTkSlot()
def addLayer(self,name:str=None, data=None) -> LayerData:
@ -93,9 +97,9 @@ class Layers():
self._layers.insert(0,ld)
self._selected = ld
self.layerAdded.emit(ld.data())
self.layersOrderChanged.emit(self.layersData())
self.layersOrderChanged.emit(self._layers)
if len(self._layers) == 1:
self.layerSelected.emit(ld.data())
self.layerSelected.emit(ld)
return ld
@ttk.pyTTkSlot()
@ -105,14 +109,19 @@ class Layers():
dl = la.pop(la.index(self._selected))
self._selected = la[0] if la else None
self.layerDeleted(dl.data())
self.layersOrderChanged.emit(self.layersData())
self.layersOrderChanged.emit(self._layers)
return dl
def selectLayer(self, layer) -> None:
if layer in self._layers:
self._selected = layer
self.layerSelected.emit(layer)
def selectLayerByData(self, data) -> None:
for lay in self._layers:
if lay.data() == data:
self._selected = lay
self.layerSelected.emit(data)
self.layerSelected.emit(lay)
return
@ttk.pyTTkSlot()
@ -129,4 +138,13 @@ class Layers():
if index+direction < 0: return
la = self._layers.pop(index)
self._layers.insert(index+direction,la)
self.layersOrderChanged.emit(self.layersData())
self.layersOrderChanged.emit(self._layers)
def moveLayer(self, fr:int, to:int) -> None:
if not self._layers: return
fr = max(0,fr)
to = max(0,to) - (1 if to>fr else 0)
# ttk.TTkLog.debug(f"{fr=} {to=}")
la = self._layers.pop(fr)
self._layers.insert(to,la)
self.layersOrderChanged.emit(self._layers)

Loading…
Cancel
Save