diff --git a/.vscode/launch.json b/.vscode/launch.json index a1ffba6a..7e5df8f6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -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"] diff --git a/docs/MDNotes/TODO.md b/docs/MDNotes/TODO.md index 789053bc..087da1e4 100644 --- a/docs/MDNotes/TODO.md +++ b/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 diff --git a/setup.dumbPaintTool.py b/setup.dumbPaintTool.py index e86d9446..8568e614 100644 --- a/setup.dumbPaintTool.py +++ b/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", diff --git a/setup.ttkDesigner.py b/setup.ttkDesigner.py index 0f23206e..ec0cf6d1 100644 --- a/setup.ttkDesigner.py +++ b/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", diff --git a/tools/dumbPaintTool/app/layersctrl.py b/tools/dumbPaintTool/app/layersctrl.py index f9fa6662..bf09e311 100644 --- a/tools/dumbPaintTool/app/layersctrl.py +++ b/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 diff --git a/tools/dumbPaintTool/app/maintemplate.py b/tools/dumbPaintTool/app/maintemplate.py index 5ae2e939..5e7e0e3b 100644 --- a/tools/dumbPaintTool/app/maintemplate.py +++ b/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) diff --git a/tools/dumbPaintTool/app/paintarea.py b/tools/dumbPaintTool/app/paintarea.py index 5da49ebc..8f5850cd 100644 --- a/tools/dumbPaintTool/app/paintarea.py +++ b/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 diff --git a/tools/dumbPaintTool/app/state/layers.py b/tools/dumbPaintTool/app/state/layers.py index ca63cd89..15258876 100644 --- a/tools/dumbPaintTool/app/state/layers.py +++ b/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)