mirror of
https://github.com/lalbornoz/roar.git
synced 2024-11-22 15:26:37 +00:00
Various bugfixes & usability improvements.
1) Backend: initial optimised cell rendering Python C module implementation skeleton. 2) Backend: raise alpha blending {fore,back}ground colour coefficient to {0.8,1.0 - 0.8}, resp. 3) Backend: reimplement cell rendering using Draw{Rectangle,Text}List(). 4) Canvas window: eliminate {canvas,{scroll,tool}bar} flickering during resize. 5) Canvas window: fix cursor artifacts during resizing by masking cursor. 6) Canvas window: restore cursor after executing operations that remove it. 7) Import store: correctly parse non-conforming \u0003,<bg colour> sequences. 8) GUI: correctly save list of recently used files post-update.
This commit is contained in:
parent
f5b9e9da7b
commit
3bb4fb0197
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,5 +1,9 @@
|
|||||||
*.sw[op]
|
*.sw[op]
|
||||||
__pycache__/
|
__pycache__/
|
||||||
build/
|
build/
|
||||||
|
libgui/GuiCanvasWxBackendFast.exp
|
||||||
|
libgui/GuiCanvasWxBackendFast.lib
|
||||||
|
libgui/GuiCanvasWxBackendFast.obj
|
||||||
|
libgui/GuiCanvasWxBackendFast.pyd
|
||||||
librtl/ImgurApiKey.py
|
librtl/ImgurApiKey.py
|
||||||
releases/
|
releases/
|
||||||
|
23
.vscode/c_cpp_properties.json
vendored
Executable file
23
.vscode/c_cpp_properties.json
vendored
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Win32",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**",
|
||||||
|
"C:/Python37/include/**"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"_DEBUG",
|
||||||
|
"UNICODE",
|
||||||
|
"_UNICODE"
|
||||||
|
],
|
||||||
|
"windowsSdkVersion": "10.0.18362.0",
|
||||||
|
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.23.28105/bin/Hostx64/x64/cl.exe",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"cppStandard": "c++17",
|
||||||
|
"intelliSenseMode": "msvc-x64",
|
||||||
|
"compilerArgs": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"python.analysis.disabled": ["unresolved-import"],
|
||||||
"python.pythonPath": "C:\\Python37\\python.exe",
|
"python.pythonPath": "C:\\Python37\\python.exe",
|
||||||
"python.linting.enabled": false
|
"python.linting.enabled": false
|
||||||
}
|
}
|
7
.vscode/tasks.json
vendored
7
.vscode/tasks.json
vendored
@ -4,9 +4,12 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "echo",
|
"label": "Build libgui/GuiCanvasWxBackendFast.pyd",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "echo Hello"
|
"command": "cd \"${workspaceFolder}/libgui\" && cmd /c cl /LD /IC:/Python37/include GuiCanvasWxBackendFast.c C:/Python37/libs/python37.lib /FeGuiCanvasWxBackendFast.pyd",
|
||||||
|
"problemMatcher": [
|
||||||
|
"$msCompile"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,25 +1,20 @@
|
|||||||
1) ANSI CSI CU[BDPU] sequences
|
1) ANSI CSI CU[BDPU] sequences
|
||||||
2) Fix & finish Arabic/RTL text tool support
|
|
||||||
3) Documentation, instrumentation & unit tests
|
3) Documentation, instrumentation & unit tests
|
||||||
4) Layers & layout (e.g. for comics, zines, etc.)
|
4) Layers & layout (e.g. for comics, zines, etc.)
|
||||||
5) Open and toggle a reference image in the background
|
5) Open and toggle a reference image in the background
|
||||||
6) Client-Server or Peer-to-Peer realtime collaboration
|
6) Client-Server or Peer-to-Peer realtime collaboration
|
||||||
7) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.)
|
7) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.)
|
||||||
8) {record,replay} {keyboard,mouse,...} events in debugging builds
|
8) Unit tools: arrow, {cloud,speech bubble}, curve, polygon
|
||||||
9) Unit tools: arrow, {cloud,speech bubble}, curve, measure, polygon, triangle
|
9) {record,replay} {keyboard,mouse,...} events in debugging builds
|
||||||
10) Integrate ENNTool code in the form of OpenGL-based animation window (see 13) and 14))
|
10) Integrate ENNTool code in the form of OpenGL-based animation window (see 13) and 14))
|
||||||
11) Composition, parametrisation & keying of tools from higher-order operators (brushes, functions, filters, outlines, patterns & shaders) and unit tools
|
11) Composition, parametrisation & keying of tools from higher-order operators (brushes, functions, filters, outlines, patterns & shaders) and unit tools
|
||||||
12) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...)
|
12) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...)
|
||||||
13) GUI TODO list:
|
13) GUI TODO list:
|
||||||
a) switch to Gtk
|
a) switch to Gtk
|
||||||
b) https://material.io/resources/icons/?style=baseline
|
b) canvas preview in Open dialogue(s)
|
||||||
c) replace logo w/ canvas panel in About dialogue, revisit melp? dialogue
|
c) https://material.io/resources/icons/?style=baseline
|
||||||
d) replace resize buttons w/ {-,edit box,+} buttons & lock button re: ratio (ty lol3)
|
d) replace logo w/ canvas panel in About dialogue, revisit melp? dialogue
|
||||||
e) Settings/Settings window (e.g. autosave, cursor opacity, hide cursor on leaving window, ...)
|
e) replace resize buttons w/ {-,edit box,+} buttons & lock button re: ratio (ty lol3)
|
||||||
|
f) Settings/Settings window (e.g. autosave, cursor opacity, hide cursor on leaving window, ...)
|
||||||
Release roadmap:
|
|
||||||
1) {copy,cut,delete,insert from,paste}, edit asset in new canvas, import from {canvas,object}
|
|
||||||
2) operators: crop, scale, shift, slice
|
|
||||||
3) tools: unicode block elements
|
|
||||||
|
|
||||||
vim:ff=dos tw=0
|
vim:ff=dos tw=0
|
||||||
|
13
assets/text/roadmap.txt
Normal file
13
assets/text/roadmap.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
1) canvas: default to transparent background colour
|
||||||
|
2) GUI: disable tiling items unless current tool is object tool
|
||||||
|
3) GUI: edit asset in new canvas, import from {canvas,object}
|
||||||
|
4) GUI: implement GuiCanvasWxBackendFast.c{,c}
|
||||||
|
5) GUI: select all
|
||||||
|
6) GUI: show line numbers w/ either ruler or tooltip
|
||||||
|
7) operators: copy, cut, delete, insert from, paste
|
||||||
|
8) operators: crop, scale, shift, slice operators
|
||||||
|
9) tools: measure, triangle, unicode block elements tool
|
||||||
|
10) tools: text tool: finish Arabic/RTL text implementation
|
||||||
|
11) tools: text tool: implicitly draw (text) w/ bg -1, toggle drawing actual brushColours[1] mode w/ RMB
|
||||||
|
|
||||||
|
vim:ff=dos tw=0
|
@ -29,7 +29,6 @@ class Canvas():
|
|||||||
else:
|
else:
|
||||||
oldSize = self.size
|
oldSize = self.size
|
||||||
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
|
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
|
||||||
self.journal.resetCursor()
|
|
||||||
if commitUndo:
|
if commitUndo:
|
||||||
self.journal.begin()
|
self.journal.begin()
|
||||||
undoPatches, redoPatches = ["resize", *oldSize], ["resize", *newSize]
|
undoPatches, redoPatches = ["resize", *oldSize], ["resize", *newSize]
|
||||||
|
@ -101,6 +101,8 @@ class CanvasImportStore():
|
|||||||
inCurColours = (int(m[2]), int(m[3]))
|
inCurColours = (int(m[2]), int(m[3]))
|
||||||
elif (m[2] != None) and (m[3] == None):
|
elif (m[2] != None) and (m[3] == None):
|
||||||
inCurColours = (int(m[2]), int(inCurColours[1]))
|
inCurColours = (int(m[2]), int(inCurColours[1]))
|
||||||
|
elif (m[2] == None) and (m[3] != None):
|
||||||
|
inCurColours = (int(inCurColours[0]), int(m[3]))
|
||||||
else:
|
else:
|
||||||
inCurColours = (15, -1)
|
inCurColours = (15, -1)
|
||||||
inCurCol += len(m[0])
|
inCurCol += len(m[0])
|
||||||
|
@ -15,9 +15,11 @@ class CanvasJournal():
|
|||||||
if self.patchesUndoLevel > 0:
|
if self.patchesUndoLevel > 0:
|
||||||
del self.patchesUndo[:self.patchesUndoLevel]; self.patchesUndoLevel = 0;
|
del self.patchesUndo[:self.patchesUndoLevel]; self.patchesUndoLevel = 0;
|
||||||
|
|
||||||
def popCursor(self):
|
def popCursor(self, reset=True):
|
||||||
if len(self.patchesCursor):
|
if len(self.patchesCursor):
|
||||||
patchesCursor = self.patchesCursor; self.patchesCursor = [];
|
patchesCursor = self.patchesCursor
|
||||||
|
if reset:
|
||||||
|
self.resetCursor()
|
||||||
return patchesCursor
|
return patchesCursor
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
@ -40,8 +42,6 @@ class CanvasJournal():
|
|||||||
self.patchesCursor = patches
|
self.patchesCursor = patches
|
||||||
|
|
||||||
def resetCursor(self):
|
def resetCursor(self):
|
||||||
if self.patchesCursor != None:
|
|
||||||
self.patchesCursor.clear()
|
|
||||||
self.patchesCursor = []
|
self.patchesCursor = []
|
||||||
|
|
||||||
def resetUndo(self):
|
def resetUndo(self):
|
||||||
@ -57,7 +57,6 @@ class CanvasJournal():
|
|||||||
self.resetCursor(); self.resetUndo();
|
self.resetCursor(); self.resetUndo();
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.patchesCursor, self.patchesUndo, self. patchesUndoLevel = None, None, None
|
self.patchesUndo = None; self.resetCursor(); self.resetUndo();
|
||||||
self.resetCursor(); self.resetUndo();
|
|
||||||
|
|
||||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
from ctypes import *
|
from ctypes import *
|
||||||
from GuiCanvasColours import Colours
|
from GuiCanvasColours import Colours
|
||||||
import math, os, platform, Rtl, wx
|
import GuiCanvasWxBackendFast
|
||||||
|
import math, os, platform, wx
|
||||||
|
|
||||||
class GuiBufferedDC(wx.MemoryDC):
|
class GuiBufferedDC(wx.MemoryDC):
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
@ -70,7 +71,7 @@ class GuiCanvasWxBackend():
|
|||||||
CS_UNDERLINE = 0x02
|
CS_UNDERLINE = 0x02
|
||||||
|
|
||||||
def _blendColours(self, bg, fg):
|
def _blendColours(self, bg, fg):
|
||||||
return [int((fg * 0.75) + (bg * (1.0 - 0.75))) for bg, fg in zip(Colours[bg][:3], Colours[fg][:3])]
|
return [int((fg * 0.8) + (bg * (1.0 - 0.8))) for bg, fg in zip(Colours[bg][:3], Colours[fg][:3])]
|
||||||
|
|
||||||
def _finiBrushesAndPens(self):
|
def _finiBrushesAndPens(self):
|
||||||
for wxObject in Rtl.flatten([
|
for wxObject in Rtl.flatten([
|
||||||
@ -140,24 +141,23 @@ class GuiCanvasWxBackend():
|
|||||||
else:
|
else:
|
||||||
bg = patchBg[1] if patchBg[1] != -1 else 14
|
bg = patchBg[1] if patchBg[1] != -1 else 14
|
||||||
brush, pen = self._brushesBlend[bg][14], self._pensBlend[bg][14]
|
brush, pen = self._brushesBlend[bg][14], self._pensBlend[bg][14]
|
||||||
if self._lastBrush != brush:
|
|
||||||
dc.SetBrush(brush); self._lastBrush = brush;
|
|
||||||
if self._lastPen != pen:
|
|
||||||
dc.SetPen(pen); self._lastPen = pen;
|
|
||||||
return brush, pen
|
return brush, pen
|
||||||
|
|
||||||
def drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc):
|
def drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc, reset=True):
|
||||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||||
cursorCells, patches = canvasJournal.popCursor(), []
|
cursorPatches = canvasJournal.popCursor(reset=reset); patches = [];
|
||||||
for cursorCell in cursorCells:
|
for cursorCell in [p[:2] for p in cursorPatches]:
|
||||||
|
if (cursorCell[0] < canvas.size[0]) \
|
||||||
|
and (cursorCell[1] < canvas.size[1]):
|
||||||
patches += [[*cursorCell, *canvas.map[cursorCell[1]][cursorCell[0]]]]
|
patches += [[*cursorCell, *canvas.map[cursorCell[1]][cursorCell[0]]]]
|
||||||
if len(patches) > 0:
|
if len(patches) > 0:
|
||||||
self.drawPatches(canvas, eventDc, patches, False)
|
self.drawPatches(canvas, eventDc, patches, False)
|
||||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||||
return cursorCells
|
return cursorPatches
|
||||||
|
|
||||||
def drawPatches(self, canvas, eventDc, patches, isCursor=False):
|
def drawPatches(self, canvas, eventDc, patches, isCursor=False):
|
||||||
patchCursorCells, patchesRender = [], []
|
GuiCanvasWxBackendFast.drawPatches()
|
||||||
|
patchesRender = []
|
||||||
for patch in patches:
|
for patch in patches:
|
||||||
point = patch[:2]
|
point = patch[:2]
|
||||||
if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]:
|
if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]:
|
||||||
@ -166,33 +166,33 @@ class GuiCanvasWxBackend():
|
|||||||
patchesRender += [patchReshaped]
|
patchesRender += [patchReshaped]
|
||||||
else:
|
else:
|
||||||
patchesRender += [patch]
|
patchesRender += [patch]
|
||||||
for patchRender in patchesRender:
|
numPatch, textBg = 0, wx.Colour(0, 0, 0, 0)
|
||||||
absPoint, charFlag = [a * b for a, b in zip(self.cellSize, patchRender[:2])], False
|
rectangles, pens, brushes = [None] * len(patchesRender), [None] * len(patchesRender), [None] * len(patchesRender)
|
||||||
if (patchRender[5] == " ") and (patchRender[3] == -1):
|
textList, coords, foregrounds, backgrounds = [], [], [], []
|
||||||
charFlag, patchRender, textFg = True, [*patchRender[:-1], "░"], wx.Colour(0, 0, 0, 255)
|
|
||||||
elif isCursor and (patchRender[5] == " ") and ((canvas.map[patchRender[1]][patchRender[0]][3] != " ") or (canvas.map[patchRender[1]][patchRender[0]][2] & self._CellState.CS_UNDERLINE)):
|
|
||||||
charFlag, patchRender = True, [*patchRender[:-2], *canvas.map[patchRender[1]][patchRender[0]][2:]]
|
|
||||||
textFg = wx.Colour(self._blendColours(canvas.map[patchRender[1]][patchRender[0]][0], patchRender[3]))
|
|
||||||
elif (patchRender[5] != " ") or (patchRender[4] & self._CellState.CS_UNDERLINE):
|
|
||||||
charFlag, textFg = True, wx.Colour(Colours[patchRender[2]][:4])
|
|
||||||
brush, pen = self._setBrushColours(eventDc, isCursor, patchRender[2:], canvas.map[patchRender[1]][patchRender[0]])
|
|
||||||
eventDc.DrawRectangle(*absPoint, *self.cellSize)
|
|
||||||
if charFlag:
|
|
||||||
if (patchRender[4] & self._CellState.CS_UNDERLINE) or (patchRender[5] == "_"):
|
|
||||||
if isCursor and (patchRender[5] == " ") and ((canvas.map[patchRender[1]][patchRender[0]][3] != " ") or (canvas.map[patchRender[1]][patchRender[0]][2] & self._CellState.CS_UNDERLINE)):
|
|
||||||
eventDc.SetPen(self._pensBlend[patchRender[2]][patchRender[3]])
|
|
||||||
else:
|
|
||||||
eventDc.SetPen(self._pens[patchRender[2]])
|
|
||||||
eventDc.DrawLine(absPoint[0], absPoint[1] + self.cellSize[1] - 1, absPoint[0] + self.cellSize[0], absPoint[1] + self.cellSize[1] - 1)
|
|
||||||
eventDc.SetPen(pen)
|
|
||||||
if patchRender[5] != "_":
|
|
||||||
oldClippingRegion = eventDc.GetClippingBox()
|
|
||||||
eventDc.SetFont(self._font)
|
eventDc.SetFont(self._font)
|
||||||
eventDc.DestroyClippingRegion(); eventDc.SetClippingRegion(*absPoint, *self.cellSize);
|
for patchRender in patchesRender:
|
||||||
eventDc.SetTextForeground(textFg); eventDc.DrawText(patchRender[5], *absPoint);
|
if (patchRender[5] == " ") and (patchRender[3] == -1):
|
||||||
eventDc.DestroyClippingRegion()
|
text, textFg = "░", wx.Colour(0, 0, 0, 255)
|
||||||
patchCursorCells += [patchRender[:2]]
|
elif isCursor and (patchRender[5] == " ") and (canvas.map[patchRender[1]][patchRender[0]][3] != " "):
|
||||||
return patchCursorCells
|
patchRender = [*patchRender[:-2], *canvas.map[patchRender[1]][patchRender[0]][2:]]
|
||||||
|
text, textFg = canvas.map[patchRender[1]][patchRender[0]][3], wx.Colour(self._blendColours(canvas.map[patchRender[1]][patchRender[0]][0], patchRender[3]))
|
||||||
|
elif isCursor and (patchRender[5] == " ") and (canvas.map[patchRender[1]][patchRender[0]][2] & self._CellState.CS_UNDERLINE):
|
||||||
|
patchRender = [*patchRender[:-2], *canvas.map[patchRender[1]][patchRender[0]][2:]]
|
||||||
|
text, textFg = "_", wx.Colour(self._blendColours(canvas.map[patchRender[1]][patchRender[0]][0], patchRender[3]))
|
||||||
|
elif patchRender[5] != " ":
|
||||||
|
text, textFg = patchRender[5], wx.Colour(Colours[patchRender[2]][:4])
|
||||||
|
elif patchRender[4] & self._CellState.CS_UNDERLINE:
|
||||||
|
text, textFg = "_", wx.Colour(Colours[patchRender[2]][:4])
|
||||||
|
else:
|
||||||
|
text = None
|
||||||
|
brush, pen = self._setBrushColours(eventDc, isCursor, patchRender[2:], canvas.map[patchRender[1]][patchRender[0]])
|
||||||
|
rectangles[numPatch] = [patchRender[:2][0] * self.cellSize[0], patchRender[:2][1] * self.cellSize[1], self.cellSize[0], self.cellSize[1]];
|
||||||
|
pens[numPatch] = pen; brushes[numPatch] = brush;
|
||||||
|
if text != None:
|
||||||
|
textList += [text]; coords += [[patchRender[:2][0] * self.cellSize[0], patchRender[:2][1] * self.cellSize[1]]]; foregrounds += [textFg]; backgrounds += [textBg];
|
||||||
|
numPatch += 1
|
||||||
|
eventDc.DrawRectangleList(rectangles, pens, brushes)
|
||||||
|
eventDc.DrawTextList(textList, coords, foregrounds, backgrounds)
|
||||||
|
|
||||||
def getDeviceContext(self, clientSize, parentWindow, viewRect=None):
|
def getDeviceContext(self, clientSize, parentWindow, viewRect=None):
|
||||||
if viewRect == None:
|
if viewRect == None:
|
||||||
|
25
libgui/GuiCanvasWxBackendFast.c
Normal file
25
libgui/GuiCanvasWxBackendFast.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
GuiCanvasWxBackendFast_drawPatches(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef
|
||||||
|
GuiCanvasWxBackendFast_methods[] = {
|
||||||
|
{"drawPatches", GuiCanvasWxBackendFast_drawPatches, METH_VARARGS, "XXX"},
|
||||||
|
{NULL, NULL, 0, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct PyModuleDef
|
||||||
|
GuiCanvasWxBackendFastmodule = {
|
||||||
|
PyModuleDef_HEAD_INIT, "GuiCanvasWxBackendFast", NULL, -1, GuiCanvasWxBackendFast_methods,
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC
|
||||||
|
PyInit_GuiCanvasWxBackendFast(void)
|
||||||
|
{
|
||||||
|
return PyModule_Create(&GuiCanvasWxBackendFastmodule);
|
||||||
|
}
|
@ -102,10 +102,7 @@ class RoarCanvasCommandsFile():
|
|||||||
self.parentFrame.Bind(wx.EVT_MENU, lambda event: self.canvasOpenRecent(event, pathName), menuItemWindow)
|
self.parentFrame.Bind(wx.EVT_MENU, lambda event: self.canvasOpenRecent(event, pathName), menuItemWindow)
|
||||||
self.lastFiles += [{"menuItemId":menuItemId, "menuItemWindow":menuItemWindow, "pathName":pathName}]
|
self.lastFiles += [{"menuItemId":menuItemId, "menuItemWindow":menuItemWindow, "pathName":pathName}]
|
||||||
if serialise:
|
if serialise:
|
||||||
localConfFileName = getLocalConfPathName("Recent.lst")
|
self._saveRecent()
|
||||||
with open(localConfFileName, "w", encoding="utf-8") as outFile:
|
|
||||||
for lastFile in [l["pathName"] for l in self.lastFiles]:
|
|
||||||
print(lastFile, file=outFile)
|
|
||||||
|
|
||||||
def _pushSnapshot(self, pathName):
|
def _pushSnapshot(self, pathName):
|
||||||
menuItemId = wx.NewId()
|
menuItemId = wx.NewId()
|
||||||
@ -122,6 +119,12 @@ class RoarCanvasCommandsFile():
|
|||||||
if self.canvasRestore.attrDict["id"] in self.parentFrame.menuItemsById:
|
if self.canvasRestore.attrDict["id"] in self.parentFrame.menuItemsById:
|
||||||
self.parentFrame.menuItemsById[self.canvasRestore.attrDict["id"]].Enable(False)
|
self.parentFrame.menuItemsById[self.canvasRestore.attrDict["id"]].Enable(False)
|
||||||
|
|
||||||
|
def _saveRecent(self):
|
||||||
|
localConfFileName = getLocalConfPathName("Recent.lst")
|
||||||
|
with open(localConfFileName, "w", encoding="utf-8") as outFile:
|
||||||
|
for lastFile in [l["pathName"] for l in self.lastFiles]:
|
||||||
|
print(lastFile, file=outFile)
|
||||||
|
|
||||||
def _storeLastDir(self, pathName):
|
def _storeLastDir(self, pathName):
|
||||||
localConfFileName = getLocalConfPathName("RecentDir.txt")
|
localConfFileName = getLocalConfPathName("RecentDir.txt")
|
||||||
with open(localConfFileName, "w", encoding="utf-8") as outFile:
|
with open(localConfFileName, "w", encoding="utf-8") as outFile:
|
||||||
@ -170,8 +173,13 @@ class RoarCanvasCommandsFile():
|
|||||||
outPathName = dialog.GetPath(); self.lastDir = os.path.dirname(outPathName); self._storeLastDir(self.lastDir);
|
outPathName = dialog.GetPath(); self.lastDir = os.path.dirname(outPathName); self._storeLastDir(self.lastDir);
|
||||||
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||||
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas)
|
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas)
|
||||||
self.parentCanvas.backend.drawCursorMaskWithJournal(self.parentCanvas.canvas, self.parentCanvas.canvas.journal, eventDc)
|
self.parentCanvas.backend.drawCursorMaskWithJournal(self.parentCanvas.canvas, self.parentCanvas.canvas.journal, eventDc, reset=False)
|
||||||
self.parentCanvas.canvas.exportStore.exportBitmapToPngFile(self.parentCanvas.backend.canvasBitmap, outPathName, wx.BITMAP_TYPE_PNG)
|
self.parentCanvas.canvas.exportStore.exportBitmapToPngFile(self.parentCanvas.backend.canvasBitmap, outPathName, wx.BITMAP_TYPE_PNG)
|
||||||
|
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||||
|
cursorPatches = self.parentCanvas.canvas.journal.popCursor(reset=False)
|
||||||
|
if len(cursorPatches) > 0:
|
||||||
|
self.parentCanvas.backend.drawPatches(self.parentCanvas.canvas, eventDc, cursorPatches, isCursor=True)
|
||||||
|
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||||
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -179,8 +187,13 @@ class RoarCanvasCommandsFile():
|
|||||||
def canvasExportImgur(self, event):
|
def canvasExportImgur(self, event):
|
||||||
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||||
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas)
|
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas)
|
||||||
self.parentCanvas.backend.drawCursorMaskWithJournal(self.parentCanvas.canvas, self.parentCanvas.canvas.journal, eventDc)
|
self.parentCanvas.backend.drawCursorMaskWithJournal(self.parentCanvas.canvas, self.parentCanvas.canvas.journal, eventDc, reset=False)
|
||||||
rc, status, result = self.parentCanvas.canvas.exportStore.exportBitmapToImgur(self.imgurApiKey, self.parentCanvas.backend.canvasBitmap, "", "", wx.BITMAP_TYPE_PNG)
|
rc, status, result = self.parentCanvas.canvas.exportStore.exportBitmapToImgur(self.imgurApiKey, self.parentCanvas.backend.canvasBitmap, "", "", wx.BITMAP_TYPE_PNG)
|
||||||
|
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||||
|
cursorPatches = self.parentCanvas.canvas.journal.popCursor(reset=False)
|
||||||
|
if len(cursorPatches) > 0:
|
||||||
|
self.parentCanvas.backend.drawPatches(self.parentCanvas.canvas, eventDc, cursorPatches, isCursor=True)
|
||||||
|
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||||
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||||
if rc:
|
if rc:
|
||||||
if not wx.TheClipboard.IsOpened():
|
if not wx.TheClipboard.IsOpened():
|
||||||
@ -272,6 +285,7 @@ class RoarCanvasCommandsFile():
|
|||||||
if not rc:
|
if not rc:
|
||||||
numLastFile = [i for i in range(len(self.lastFiles)) if self.lastFiles[i]["pathName"] == pathName][0]
|
numLastFile = [i for i in range(len(self.lastFiles)) if self.lastFiles[i]["pathName"] == pathName][0]
|
||||||
self.canvasOpenRecent.attrDict["menu"].Delete(self.lastFiles[numLastFile]["menuItemId"]); del self.lastFiles[numLastFile];
|
self.canvasOpenRecent.attrDict["menu"].Delete(self.lastFiles[numLastFile]["menuItemId"]); del self.lastFiles[numLastFile];
|
||||||
|
self._saveRecent()
|
||||||
|
|
||||||
@GuiSubMenuDecorator("Restore Snapshot", "Res&tore Snapshot", None, None, False)
|
@GuiSubMenuDecorator("Restore Snapshot", "Res&tore Snapshot", None, None, False)
|
||||||
def canvasRestore(self, event, pathName=None):
|
def canvasRestore(self, event, pathName=None):
|
||||||
|
@ -56,12 +56,28 @@ class RoarCanvasWindow(GuiWindow):
|
|||||||
self.canvas.applyPatch(patch, commitUndo=True)
|
self.canvas.applyPatch(patch, commitUndo=True)
|
||||||
self.canvas.journal.end()
|
self.canvas.journal.end()
|
||||||
if patchesCursor != None:
|
if patchesCursor != None:
|
||||||
patchesCursorCells = self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
|
self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
|
||||||
if len(patchesCursorCells) > 0:
|
if len(patchesCursor) > 0:
|
||||||
self.canvas.journal.pushCursor(patchesCursorCells)
|
self.canvas.journal.pushCursor(patchesCursor)
|
||||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||||
self.commands.update(dirty=self.dirty, cellPos=self.brushPos, undoLevel=self.canvas.journal.patchesUndoLevel)
|
self.commands.update(dirty=self.dirty, cellPos=self.brushPos, undoLevel=self.canvas.journal.patchesUndoLevel)
|
||||||
|
|
||||||
|
def _eraseBackground(self, eventDc):
|
||||||
|
viewRect = self.GetViewStart()
|
||||||
|
canvasSize, panelSize = [a * b for a, b in zip(self.backend.canvasSize, self.backend.cellSize)], self.GetSize()
|
||||||
|
if viewRect != (0, 0):
|
||||||
|
viewRect = [a * b for a, b in zip(self.backend.cellSize, viewRect)]
|
||||||
|
canvasSize = [a - b for a, b in zip(canvasSize, viewRect)]
|
||||||
|
rectangles, pens, brushes = [], [], []
|
||||||
|
if panelSize[0] > canvasSize[0]:
|
||||||
|
brushes += [self.bgBrush]; pens += [self.bgPen];
|
||||||
|
rectangles += [[canvasSize[0], 0, panelSize[0] - canvasSize[0], panelSize[1]]]
|
||||||
|
if panelSize[1] > canvasSize[1]:
|
||||||
|
brushes += [self.bgBrush]; pens += [self.bgPen];
|
||||||
|
rectangles += [[0, canvasSize[1], panelSize[0], panelSize[1] - canvasSize[1]]]
|
||||||
|
if len(rectangles) > 0:
|
||||||
|
eventDc.DrawRectangleList(rectangles, pens, brushes)
|
||||||
|
|
||||||
def _snapshotsReset(self):
|
def _snapshotsReset(self):
|
||||||
self._snapshotFiles, self._snapshotsUpdateLast = [], time.time()
|
self._snapshotFiles, self._snapshotsUpdateLast = [], time.time()
|
||||||
self.commands._resetSnapshots()
|
self.commands._resetSnapshots()
|
||||||
@ -218,6 +234,9 @@ class RoarCanvasWindow(GuiWindow):
|
|||||||
def onEnterWindow(self, event):
|
def onEnterWindow(self, event):
|
||||||
self.lastCellState = None
|
self.lastCellState = None
|
||||||
|
|
||||||
|
def onErase(self, event):
|
||||||
|
pass
|
||||||
|
|
||||||
def onLeaveWindow(self, event):
|
def onLeaveWindow(self, event):
|
||||||
if False:
|
if False:
|
||||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
|
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
|
||||||
@ -245,6 +264,7 @@ class RoarCanvasWindow(GuiWindow):
|
|||||||
if modifiers == (wx.MOD_CONTROL | wx.MOD_ALT):
|
if modifiers == (wx.MOD_CONTROL | wx.MOD_ALT):
|
||||||
newFontSize = self.backend.fontSize + delta
|
newFontSize = self.backend.fontSize + delta
|
||||||
if newFontSize > 0:
|
if newFontSize > 0:
|
||||||
|
self.Freeze()
|
||||||
self.backend.fontSize = newFontSize
|
self.backend.fontSize = newFontSize
|
||||||
self.backend.resize(self.canvas.size); self.scrollStep = self.backend.cellSize;
|
self.backend.resize(self.canvas.size); self.scrollStep = self.backend.cellSize;
|
||||||
super().resize([a * b for a, b in zip(self.canvas.size, self.backend.cellSize)])
|
super().resize([a * b for a, b in zip(self.canvas.size, self.backend.cellSize)])
|
||||||
@ -256,6 +276,7 @@ class RoarCanvasWindow(GuiWindow):
|
|||||||
patches += [[numCol, numRow, *self.canvas.map[numRow][numCol]]]
|
patches += [[numCol, numRow, *self.canvas.map[numRow][numCol]]]
|
||||||
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
|
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
|
||||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||||
|
self.Thaw(); del eventDc; self._eraseBackground(wx.ClientDC(self));
|
||||||
elif modifiers == (wx.MOD_CONTROL | wx.MOD_SHIFT):
|
elif modifiers == (wx.MOD_CONTROL | wx.MOD_SHIFT):
|
||||||
self.commands.canvasCanvasSize(self.commands.canvasCanvasSize, 2, 1 if delta > 0 else 0)(None)
|
self.commands.canvasCanvasSize(self.commands.canvasCanvasSize, 2, 1 if delta > 0 else 0)(None)
|
||||||
elif modifiers == wx.MOD_CONTROL:
|
elif modifiers == wx.MOD_CONTROL:
|
||||||
@ -264,51 +285,64 @@ class RoarCanvasWindow(GuiWindow):
|
|||||||
event.Skip()
|
event.Skip()
|
||||||
|
|
||||||
def onPaint(self, event):
|
def onPaint(self, event):
|
||||||
|
viewRect = self.GetViewStart()
|
||||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||||
self.backend.onPaint(self.GetClientSize(), self, self.GetViewStart())
|
self.backend.onPaint(self.GetClientSize(), self, viewRect)
|
||||||
|
del eventDc; self._eraseBackground(wx.PaintDC(self));
|
||||||
|
|
||||||
def resize(self, newSize, commitUndo=True, dirty=True):
|
def resize(self, newSize, commitUndo=True, dirty=True, freeze=True):
|
||||||
|
if freeze:
|
||||||
|
self.Freeze()
|
||||||
viewRect = self.GetViewStart()
|
viewRect = self.GetViewStart()
|
||||||
oldSize = [0, 0] if self.canvas.map == None else self.canvas.size
|
oldSize = [0, 0] if self.canvas.map == None else self.canvas.size
|
||||||
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
|
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
|
||||||
if self.canvas.resize(newSize, commitUndo):
|
if self.canvas.resize(newSize, commitUndo):
|
||||||
self.backend.resize(newSize); self.scrollStep = self.backend.cellSize;
|
self.backend.resize(newSize); self.scrollStep = self.backend.cellSize;
|
||||||
super().resize([a * b for a, b in zip(newSize, self.backend.cellSize)])
|
super().resize([a * b for a, b in zip(newSize, self.backend.cellSize)])
|
||||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
|
||||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
|
||||||
patches = []
|
|
||||||
if deltaSize[0] > 0:
|
|
||||||
for numRow in range(oldSize[1]):
|
|
||||||
for numNewCol in range(oldSize[0], newSize[0]):
|
|
||||||
patches += [[numNewCol, numRow, 1, 1, 0, " "]]
|
|
||||||
if deltaSize[1] > 1:
|
|
||||||
for numNewRow in range(oldSize[1], newSize[1]):
|
|
||||||
for numNewCol in range(newSize[0]):
|
|
||||||
patches += [[numNewCol, numNewRow, 1, 1, 0, " "]]
|
|
||||||
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
|
|
||||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
|
||||||
self.Scroll(*viewRect); self.dirty = dirty;
|
self.Scroll(*viewRect); self.dirty = dirty;
|
||||||
self.commands.update(dirty=self.dirty, size=newSize, undoLevel=self.canvas.journal.patchesUndoLevel)
|
self.commands.update(dirty=self.dirty, size=newSize, undoLevel=self.canvas.journal.patchesUndoLevel)
|
||||||
if commitUndo:
|
if commitUndo:
|
||||||
self._snapshotsUpdate()
|
self._snapshotsUpdate()
|
||||||
|
if freeze:
|
||||||
|
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||||
|
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||||
|
cursorPatches = self.canvas.journal.popCursor(reset=False)
|
||||||
|
if len(cursorPatches) > 0:
|
||||||
|
self.backend.drawPatches(self.canvas, eventDc, cursorPatches, isCursor=True)
|
||||||
|
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||||
|
del eventDc; self.Thaw(); self._eraseBackground(wx.ClientDC(self));
|
||||||
|
|
||||||
def undo(self, redo=False):
|
def undo(self, redo=False):
|
||||||
|
freezeFlag = False
|
||||||
deltaPatches = self.canvas.journal.popUndo() if not redo else self.canvas.journal.popRedo()
|
deltaPatches = self.canvas.journal.popUndo() if not redo else self.canvas.journal.popRedo()
|
||||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||||
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc)
|
patchesCursor = self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc, reset=False)
|
||||||
patches = []
|
patches = []
|
||||||
for patch in deltaPatches:
|
for patch in deltaPatches:
|
||||||
if patch == None:
|
if patch == None:
|
||||||
continue
|
continue
|
||||||
elif patch[0] == "resize":
|
elif patch[0] == "resize":
|
||||||
del eventDc; self.resize(patch[1:], False);
|
if not freezeFlag:
|
||||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
self.Freeze(); freezeFlag = True;
|
||||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
eventDc = None; self.resize(patch[1:], False, freeze=False);
|
||||||
else:
|
else:
|
||||||
self.canvas._commitPatch(patch); patches += [patch];
|
self.canvas._commitPatch(patch); patches += [patch];
|
||||||
|
if eventDc == None:
|
||||||
|
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||||
|
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||||
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
|
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
|
||||||
|
if len(patchesCursor):
|
||||||
|
self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
|
||||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||||
|
if freezeFlag:
|
||||||
|
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||||
|
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||||
|
cursorPatches = self.canvas.journal.popCursor(reset=False)
|
||||||
|
if len(cursorPatches) > 0:
|
||||||
|
self.backend.drawPatches(self.canvas, eventDc, cursorPatches, isCursor=True)
|
||||||
|
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||||
|
del eventDc; self.Thaw(); self._eraseBackground(wx.ClientDC(self));
|
||||||
|
|
||||||
def update(self, newSize, commitUndo=True, newCanvas=None, dirty=True):
|
def update(self, newSize, commitUndo=True, newCanvas=None, dirty=True):
|
||||||
self.resize(newSize, commitUndo, dirty)
|
self.resize(newSize, commitUndo, dirty)
|
||||||
@ -329,6 +363,8 @@ class RoarCanvasWindow(GuiWindow):
|
|||||||
self.popupEventDc = None
|
self.popupEventDc = None
|
||||||
self.dropTarget = RoarCanvasWindowDropTarget(self)
|
self.dropTarget = RoarCanvasWindowDropTarget(self)
|
||||||
self.SetDropTarget(self.dropTarget)
|
self.SetDropTarget(self.dropTarget)
|
||||||
|
self.bgBrush, self.bgPen = wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID), wx.Pen(self.GetBackgroundColour(), 1)
|
||||||
|
self.Bind(wx.EVT_ERASE_BACKGROUND, self.onErase)
|
||||||
self.Bind(wx.EVT_MOUSEWHEEL, self.onMouseWheel)
|
self.Bind(wx.EVT_MOUSEWHEEL, self.onMouseWheel)
|
||||||
self._snapshotsReset()
|
self._snapshotsReset()
|
||||||
|
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
# This project is licensed under the terms of the MIT licence.
|
# This project is licensed under the terms of the MIT licence.
|
||||||
#
|
#
|
||||||
|
|
||||||
import re, statistics, time
|
import re
|
||||||
|
|
||||||
timeState = [None, None, 0, []]
|
|
||||||
|
|
||||||
def flatten(l):
|
def flatten(l):
|
||||||
return [li for l_ in l for li in l_]
|
return [li for l_ in l for li in l_]
|
||||||
@ -17,19 +15,4 @@ def natural_sort(l):
|
|||||||
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
|
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
|
||||||
return sorted(l, key=alphanum_key)
|
return sorted(l, key=alphanum_key)
|
||||||
|
|
||||||
def timePrint(pfx):
|
|
||||||
timeState[0] = time.time() if timeState[0] == None else timeState[0]
|
|
||||||
t1 = time.time(); td = t1 - timeState[0]
|
|
||||||
if td > 0:
|
|
||||||
timeState[1] = td if timeState[1] == None else min(td, timeState[1])
|
|
||||||
timeState[2] = max(td, timeState[2])
|
|
||||||
timeState[3] += [td]
|
|
||||||
print("{} took {:.4f} ms (min: {:.4f} max: {:.4f} avg: {:.4f})".format(pfx, td * 1000, timeState[1] * 1000, timeState[2] * 1000, statistics.mean(timeState[3]) * 1000))
|
|
||||||
|
|
||||||
def timeReset():
|
|
||||||
timeState = [None, None, 0, []]
|
|
||||||
|
|
||||||
def timeStart():
|
|
||||||
timeState[0] = time.time()
|
|
||||||
|
|
||||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||||
|
@ -24,8 +24,7 @@ class ToolCircle(Tool):
|
|||||||
for numRow in range(len(cells)):
|
for numRow in range(len(cells)):
|
||||||
for numCol in range(len(cells[numRow])):
|
for numCol in range(len(cells[numRow])):
|
||||||
point = cells[numRow][numCol]
|
point = cells[numRow][numCol]
|
||||||
if ((point[0] >= 0) and (point[1] >= 0)) \
|
if (point[0] >= 0) and (point[1] >= 0):
|
||||||
and (point[0] < canvas.size[0]) and (point[1] < canvas.size[1]):
|
|
||||||
if ((numRow == 0) or (numRow == (len(cells) - 1))) \
|
if ((numRow == 0) or (numRow == (len(cells) - 1))) \
|
||||||
or ((numCol == 0) or (numCol == (len(cells[numRow]) - 1))):
|
or ((numCol == 0) or (numCol == (len(cells[numRow]) - 1))):
|
||||||
patch = [*cells[numRow][numCol], brushColours[0], brushColours[0], 0, " "]
|
patch = [*cells[numRow][numCol], brushColours[0], brushColours[0], 0, " "]
|
||||||
|
Loading…
Reference in New Issue
Block a user