mirror of
https://github.com/lalbornoz/roar.git
synced 2024-11-02 13:56:39 +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
e1bf3a3ad4
commit
bc969295dd
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,5 +1,9 @@
|
||||
*.sw[op]
|
||||
__pycache__/
|
||||
build/
|
||||
libgui/GuiCanvasWxBackendFast.exp
|
||||
libgui/GuiCanvasWxBackendFast.lib
|
||||
libgui/GuiCanvasWxBackendFast.obj
|
||||
libgui/GuiCanvasWxBackendFast.pyd
|
||||
librtl/ImgurApiKey.py
|
||||
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.linting.enabled": false
|
||||
}
|
7
.vscode/tasks.json
vendored
7
.vscode/tasks.json
vendored
@ -4,9 +4,12 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "echo",
|
||||
"label": "Build libgui/GuiCanvasWxBackendFast.pyd",
|
||||
"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
|
||||
2) Fix & finish Arabic/RTL text tool support
|
||||
3) Documentation, instrumentation & unit tests
|
||||
4) Layers & layout (e.g. for comics, zines, etc.)
|
||||
5) Open and toggle a reference image in the background
|
||||
6) Client-Server or Peer-to-Peer realtime collaboration
|
||||
7) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.)
|
||||
8) {record,replay} {keyboard,mouse,...} events in debugging builds
|
||||
9) Unit tools: arrow, {cloud,speech bubble}, curve, measure, polygon, triangle
|
||||
8) Unit tools: arrow, {cloud,speech bubble}, curve, polygon
|
||||
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))
|
||||
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, ...)
|
||||
13) GUI TODO list:
|
||||
a) switch to Gtk
|
||||
b) https://material.io/resources/icons/?style=baseline
|
||||
c) replace logo w/ canvas panel in About dialogue, revisit melp? dialogue
|
||||
d) replace resize buttons w/ {-,edit box,+} buttons & lock button re: ratio (ty lol3)
|
||||
e) 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
|
||||
b) canvas preview in Open dialogue(s)
|
||||
c) https://material.io/resources/icons/?style=baseline
|
||||
d) replace logo w/ canvas panel in About dialogue, revisit melp? dialogue
|
||||
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, ...)
|
||||
|
||||
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:
|
||||
oldSize = self.size
|
||||
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
|
||||
self.journal.resetCursor()
|
||||
if commitUndo:
|
||||
self.journal.begin()
|
||||
undoPatches, redoPatches = ["resize", *oldSize], ["resize", *newSize]
|
||||
|
@ -101,6 +101,8 @@ class CanvasImportStore():
|
||||
inCurColours = (int(m[2]), int(m[3]))
|
||||
elif (m[2] != None) and (m[3] == None):
|
||||
inCurColours = (int(m[2]), int(inCurColours[1]))
|
||||
elif (m[2] == None) and (m[3] != None):
|
||||
inCurColours = (int(inCurColours[0]), int(m[3]))
|
||||
else:
|
||||
inCurColours = (15, -1)
|
||||
inCurCol += len(m[0])
|
||||
|
@ -15,9 +15,11 @@ class CanvasJournal():
|
||||
if self.patchesUndoLevel > 0:
|
||||
del self.patchesUndo[:self.patchesUndoLevel]; self.patchesUndoLevel = 0;
|
||||
|
||||
def popCursor(self):
|
||||
def popCursor(self, reset=True):
|
||||
if len(self.patchesCursor):
|
||||
patchesCursor = self.patchesCursor; self.patchesCursor = [];
|
||||
patchesCursor = self.patchesCursor
|
||||
if reset:
|
||||
self.resetCursor()
|
||||
return patchesCursor
|
||||
else:
|
||||
return []
|
||||
@ -40,8 +42,6 @@ class CanvasJournal():
|
||||
self.patchesCursor = patches
|
||||
|
||||
def resetCursor(self):
|
||||
if self.patchesCursor != None:
|
||||
self.patchesCursor.clear()
|
||||
self.patchesCursor = []
|
||||
|
||||
def resetUndo(self):
|
||||
@ -57,7 +57,6 @@ class CanvasJournal():
|
||||
self.resetCursor(); self.resetUndo();
|
||||
|
||||
def __init__(self):
|
||||
self.patchesCursor, self.patchesUndo, self. patchesUndoLevel = None, None, None
|
||||
self.resetCursor(); self.resetUndo();
|
||||
self.patchesUndo = None; self.resetCursor(); self.resetUndo();
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
from ctypes import *
|
||||
from GuiCanvasColours import Colours
|
||||
import math, os, platform, Rtl, wx
|
||||
import GuiCanvasWxBackendFast
|
||||
import math, os, platform, wx
|
||||
|
||||
class GuiBufferedDC(wx.MemoryDC):
|
||||
def __del__(self):
|
||||
@ -70,7 +71,7 @@ class GuiCanvasWxBackend():
|
||||
CS_UNDERLINE = 0x02
|
||||
|
||||
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):
|
||||
for wxObject in Rtl.flatten([
|
||||
@ -140,24 +141,23 @@ class GuiCanvasWxBackend():
|
||||
else:
|
||||
bg = patchBg[1] if patchBg[1] != -1 else 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
|
||||
|
||||
def drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc):
|
||||
def drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc, reset=True):
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
cursorCells, patches = canvasJournal.popCursor(), []
|
||||
for cursorCell in cursorCells:
|
||||
patches += [[*cursorCell, *canvas.map[cursorCell[1]][cursorCell[0]]]]
|
||||
cursorPatches = canvasJournal.popCursor(reset=reset); patches = [];
|
||||
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]]]]
|
||||
if len(patches) > 0:
|
||||
self.drawPatches(canvas, eventDc, patches, False)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
return cursorCells
|
||||
return cursorPatches
|
||||
|
||||
def drawPatches(self, canvas, eventDc, patches, isCursor=False):
|
||||
patchCursorCells, patchesRender = [], []
|
||||
GuiCanvasWxBackendFast.drawPatches()
|
||||
patchesRender = []
|
||||
for patch in patches:
|
||||
point = patch[:2]
|
||||
if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]:
|
||||
@ -166,33 +166,33 @@ class GuiCanvasWxBackend():
|
||||
patchesRender += [patchReshaped]
|
||||
else:
|
||||
patchesRender += [patch]
|
||||
numPatch, textBg = 0, wx.Colour(0, 0, 0, 0)
|
||||
rectangles, pens, brushes = [None] * len(patchesRender), [None] * len(patchesRender), [None] * len(patchesRender)
|
||||
textList, coords, foregrounds, backgrounds = [], [], [], []
|
||||
eventDc.SetFont(self._font)
|
||||
for patchRender in patchesRender:
|
||||
absPoint, charFlag = [a * b for a, b in zip(self.cellSize, patchRender[:2])], False
|
||||
if (patchRender[5] == " ") and (patchRender[3] == -1):
|
||||
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])
|
||||
text, textFg = "░", wx.Colour(0, 0, 0, 255)
|
||||
elif isCursor and (patchRender[5] == " ") and (canvas.map[patchRender[1]][patchRender[0]][3] != " "):
|
||||
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]])
|
||||
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.DestroyClippingRegion(); eventDc.SetClippingRegion(*absPoint, *self.cellSize);
|
||||
eventDc.SetTextForeground(textFg); eventDc.DrawText(patchRender[5], *absPoint);
|
||||
eventDc.DestroyClippingRegion()
|
||||
patchCursorCells += [patchRender[:2]]
|
||||
return patchCursorCells
|
||||
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):
|
||||
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.lastFiles += [{"menuItemId":menuItemId, "menuItemWindow":menuItemWindow, "pathName":pathName}]
|
||||
if serialise:
|
||||
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)
|
||||
self._saveRecent()
|
||||
|
||||
def _pushSnapshot(self, pathName):
|
||||
menuItemId = wx.NewId()
|
||||
@ -122,6 +119,12 @@ class RoarCanvasCommandsFile():
|
||||
if self.canvasRestore.attrDict["id"] in self.parentFrame.menuItemsById:
|
||||
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):
|
||||
localConfFileName = getLocalConfPathName("RecentDir.txt")
|
||||
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);
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||
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)
|
||||
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))
|
||||
return True
|
||||
|
||||
@ -179,8 +187,13 @@ class RoarCanvasCommandsFile():
|
||||
def canvasExportImgur(self, event):
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||
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)
|
||||
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))
|
||||
if rc:
|
||||
if not wx.TheClipboard.IsOpened():
|
||||
@ -272,6 +285,7 @@ class RoarCanvasCommandsFile():
|
||||
if not rc:
|
||||
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._saveRecent()
|
||||
|
||||
@GuiSubMenuDecorator("Restore Snapshot", "Res&tore Snapshot", None, None, False)
|
||||
def canvasRestore(self, event, pathName=None):
|
||||
|
@ -56,12 +56,28 @@ class RoarCanvasWindow(GuiWindow):
|
||||
self.canvas.applyPatch(patch, commitUndo=True)
|
||||
self.canvas.journal.end()
|
||||
if patchesCursor != None:
|
||||
patchesCursorCells = self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
|
||||
if len(patchesCursorCells) > 0:
|
||||
self.canvas.journal.pushCursor(patchesCursorCells)
|
||||
self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
|
||||
if len(patchesCursor) > 0:
|
||||
self.canvas.journal.pushCursor(patchesCursor)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
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):
|
||||
self._snapshotFiles, self._snapshotsUpdateLast = [], time.time()
|
||||
self.commands._resetSnapshots()
|
||||
@ -218,6 +234,9 @@ class RoarCanvasWindow(GuiWindow):
|
||||
def onEnterWindow(self, event):
|
||||
self.lastCellState = None
|
||||
|
||||
def onErase(self, event):
|
||||
pass
|
||||
|
||||
def onLeaveWindow(self, event):
|
||||
if False:
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
|
||||
@ -245,6 +264,7 @@ class RoarCanvasWindow(GuiWindow):
|
||||
if modifiers == (wx.MOD_CONTROL | wx.MOD_ALT):
|
||||
newFontSize = self.backend.fontSize + delta
|
||||
if newFontSize > 0:
|
||||
self.Freeze()
|
||||
self.backend.fontSize = newFontSize
|
||||
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)])
|
||||
@ -256,6 +276,7 @@ class RoarCanvasWindow(GuiWindow):
|
||||
patches += [[numCol, numRow, *self.canvas.map[numRow][numCol]]]
|
||||
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
self.Thaw(); del eventDc; self._eraseBackground(wx.ClientDC(self));
|
||||
elif modifiers == (wx.MOD_CONTROL | wx.MOD_SHIFT):
|
||||
self.commands.canvasCanvasSize(self.commands.canvasCanvasSize, 2, 1 if delta > 0 else 0)(None)
|
||||
elif modifiers == wx.MOD_CONTROL:
|
||||
@ -264,51 +285,64 @@ class RoarCanvasWindow(GuiWindow):
|
||||
event.Skip()
|
||||
|
||||
def onPaint(self, event):
|
||||
viewRect = self.GetViewStart()
|
||||
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()
|
||||
oldSize = [0, 0] if self.canvas.map == None else self.canvas.size
|
||||
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
|
||||
if self.canvas.resize(newSize, commitUndo):
|
||||
self.backend.resize(newSize); self.scrollStep = 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.commands.update(dirty=self.dirty, size=newSize, undoLevel=self.canvas.journal.patchesUndoLevel)
|
||||
if commitUndo:
|
||||
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):
|
||||
freezeFlag = False
|
||||
deltaPatches = self.canvas.journal.popUndo() if not redo else self.canvas.journal.popRedo()
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||
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 = []
|
||||
for patch in deltaPatches:
|
||||
if patch == None:
|
||||
continue
|
||||
elif patch[0] == "resize":
|
||||
del eventDc; self.resize(patch[1:], False);
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
if not freezeFlag:
|
||||
self.Freeze(); freezeFlag = True;
|
||||
eventDc = None; self.resize(patch[1:], False, freeze=False);
|
||||
else:
|
||||
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)
|
||||
if len(patchesCursor):
|
||||
self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
|
||||
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):
|
||||
self.resize(newSize, commitUndo, dirty)
|
||||
@ -329,6 +363,8 @@ class RoarCanvasWindow(GuiWindow):
|
||||
self.popupEventDc = None
|
||||
self.dropTarget = RoarCanvasWindowDropTarget(self)
|
||||
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._snapshotsReset()
|
||||
|
||||
|
@ -5,9 +5,7 @@
|
||||
# This project is licensed under the terms of the MIT licence.
|
||||
#
|
||||
|
||||
import re, statistics, time
|
||||
|
||||
timeState = [None, None, 0, []]
|
||||
import re
|
||||
|
||||
def flatten(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)]
|
||||
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
|
||||
|
@ -24,8 +24,7 @@ class ToolCircle(Tool):
|
||||
for numRow in range(len(cells)):
|
||||
for numCol in range(len(cells[numRow])):
|
||||
point = cells[numRow][numCol]
|
||||
if ((point[0] >= 0) and (point[1] >= 0)) \
|
||||
and (point[0] < canvas.size[0]) and (point[1] < canvas.size[1]):
|
||||
if (point[0] >= 0) and (point[1] >= 0):
|
||||
if ((numRow == 0) or (numRow == (len(cells) - 1))) \
|
||||
or ((numCol == 0) or (numCol == (len(cells[numRow]) - 1))):
|
||||
patch = [*cells[numRow][numCol], brushColours[0], brushColours[0], 0, " "]
|
||||
|
Loading…
Reference in New Issue
Block a user