Improve backend latency and throughput via batching.

assets/text/TODO: updated.
libtools/ToolLine.py: reflect brush width in pre-line dragging cursor.
This commit is contained in:
Lucio Andrés Illanes Albornoz 2019-09-26 22:38:28 +02:00
parent 86e2c9e904
commit 76e57bd081
17 changed files with 340 additions and 367 deletions

View File

@ -14,14 +14,16 @@
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) Settings/Settings window (e.g. autosave, hide cursor on leaving window, ...)
e) replace resize buttons w/ {-,edit box,+} buttons & lock button re: ratio (ty lol3)
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) auto{load,save} & {backup,restore}
4) tools: unicode block elements
5) floating/dockable toolbar https://wxpython.org/Phoenix/docs/html/wx.aui.AuiManager.html
2) floating/dockable toolbar https://wxpython.org/Phoenix/docs/html/wx.aui.AuiManager.html
3) allow dragging {circle,rect,...} w/ <Ctrl> irrespective of current brush size
4) reimplement cursor unmasking w/ simple list of points
5) operators: crop, scale, shift, slice
6) auto{load,save} & {backup,restore}
7) tools: unicode block elements
vim:ff=dos tw=0

View File

@ -6,7 +6,7 @@
from ctypes import *
from GuiCanvasColours import Colours
import math, os, platform, wx
import math, os, platform, Rtl, wx
class GuiBufferedDC(wx.MemoryDC):
def __del__(self):
@ -65,70 +65,38 @@ class GuiCanvasWxBackend():
}
class _CellState():
CS_NONE = 0x00
CS_BOLD = 0x01
CS_UNDERLINE = 0x02
CS_NONE = 0x00
CS_BOLD = 0x01
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])]
def _blendColoursBrush(self, bg, fg):
colour = self._blendColours(bg, fg)
return wx.Brush(wx.Colour(colour), wx.BRUSHSTYLE_SOLID), wx.Pen(wx.Colour(colour), 1)
def _drawPatch(self, eventDc, isCursor, patch, patchBg, point):
absPoint, charFlag = self._xlatePoint(point), False
if (patch[3] == " ") and (patch[1] == -1):
charFlag, patch = True, [*patch[:-1], ""]
textBg, textFg = wx.Colour(Colours[patch[1]][:4]), wx.Colour(Colours[patch[0]][:4])
if isCursor and (patch[3] == " ") and ((patchBg[3] != " ") or (patchBg[2] & self._CellState.CS_UNDERLINE)):
charFlag, patch = True, [*patch[:-2], *patchBg[2:]]
textFg = wx.Colour(self._blendColours(patchBg[0], patch[1]))
elif (patch[3] != " ") or (patch[2] & self._CellState.CS_UNDERLINE):
charFlag = True
textBg, textFg = wx.Colour(Colours[patch[1]][:4]), wx.Colour(Colours[patch[0]][:4])
brush, pen = self._setBrushColours(eventDc, isCursor, patch, patchBg)
eventDc.DrawRectangle(*absPoint, *self.cellSize)
if charFlag:
if (patch[2] & self._CellState.CS_UNDERLINE) or (patch[3] == "_"):
eventDc.SetPen(self._pens[patch[0]]);
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 patch[3] != "_":
oldClippingRegion = eventDc.GetClippingBox()
eventDc.SetFont(self._font)
eventDc.DestroyClippingRegion(); eventDc.SetClippingRegion(*absPoint, *self.cellSize);
eventDc.SetTextForeground(textFg)
eventDc.DrawText(patch[3], *absPoint)
eventDc.DestroyClippingRegion()
if isCursor:
brush.Destroy(); pen.Destroy();
if self._lastBrush != None:
eventDc.SetBrush(self._lastBrush)
if self._lastPen != None:
eventDc.SetPen(self._lastPen)
def _finiBrushesAndPens(self):
[brush.Destroy() for brush in self._brushes or []]
[pen.Destroy() for pen in self._pens or []]
self._brushAlpha.Destroy(); self._penAlpha.Destroy();
self._brushes, self._lastBrush, self._lastPen, self._pens = None, None, None, None
for wxObject in Rtl.flatten([
(self._brushAlpha,), (*(self._brushes or ()),), (self._penAlpha,), (*(self._pens or ()),),
*[[self._brushesBlend[bg][fg] for fg in self._brushesBlend[bg].keys()] for bg in self._brushesBlend.keys()],
*[[self._pensBlend[bg][fg] for fg in self._pensBlend[bg].keys()] for bg in self._pensBlend.keys()]]):
if wxObject != None:
wxObject.Destroy()
self._brushAlpha, self._brushes, self._brushesBlend, self._lastBrush, self._lastPen, self._penAlpha, self._pens, self._pensBlend = None, [], {}, None, None, None, [], {}
def _initBrushesAndPens(self):
self._brushes, self._pens = [None for x in range(len(Colours))], [None for x in range(len(Colours))]
self._brushes, self._brushesBlend, self._lastBrush, self._lastPen, self._pens, self._pensBlend = [], {}, None, None, [], {}
self._brushAlpha, self._penAlpha = wx.Brush(wx.Colour(Colours[14][:4]), wx.BRUSHSTYLE_SOLID), wx.Pen(wx.Colour(Colours[14][:4]), 1)
for mircColour in range(len(Colours)):
self._brushes[mircColour] = wx.Brush(wx.Colour(Colours[mircColour][:4]), wx.BRUSHSTYLE_SOLID)
self._pens[mircColour] = wx.Pen(wx.Colour(Colours[mircColour][:4]), 1)
self._brushAlpha = wx.Brush(wx.Colour(Colours[14][:4]), wx.BRUSHSTYLE_SOLID)
self._penAlpha = wx.Pen(wx.Colour(Colours[14][:4]), 1)
self._lastBrush, self._lastPen = None, None
self._brushes += [wx.Brush(wx.Colour(Colours[mircColour][:4]), wx.BRUSHSTYLE_SOLID)]; self._brushesBlend[mircColour] = {};
self._pens += [wx.Pen(wx.Colour(Colours[mircColour][:4]), 1)]; self._pensBlend[mircColour] = {};
for mircColourFg in range(len(Colours)):
colourBlend = self._blendColours(mircColour, mircColourFg)
self._brushesBlend[mircColour][mircColourFg] = wx.Brush(wx.Colour(colourBlend), wx.BRUSHSTYLE_SOLID)
self._pensBlend[mircColour][mircColourFg] = wx.Pen(wx.Colour(colourBlend), 1)
def _reshapeArabic(self, canvas, eventDc, isCursor, patch, point):
patches = []
lastCell = point[0]
lastCell, patches = point[0], []
while True:
if ((lastCell + 1) >= (canvas.size[0] - 1)) \
or (not canvas.map[point[1]][lastCell + 1][3] in self.arabicShapes):
if ((lastCell + 1) >= (canvas.size[0] - 1)) \
or (not canvas.map[point[1]][lastCell + 1][3] in self.arabicShapes):
break
else:
lastCell += 1
@ -164,40 +132,64 @@ class GuiCanvasWxBackend():
if not isCursor:
brush, pen = self._brushes[patch[1]], self._pens[patch[1]]
else:
brush, pen = self._blendColoursBrush(patchBg[1], patch[1])
bg = patchBg[1] if patchBg[1] != -1 else 14
brush, pen = self._brushesBlend[bg][patch[1]], self._pensBlend[bg][patch[1]]
else:
if not isCursor:
brush, pen = self._brushAlpha, self._penAlpha
else:
brush, pen = self._blendColoursBrush(patchBg[1], 14)
if not isCursor:
if self._lastBrush != brush:
dc.SetBrush(brush); self._lastBrush = brush;
if self._lastPen != pen:
dc.SetPen(pen); self._lastPen = pen;
else:
dc.SetBrush(brush); dc.SetPen(pen);
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 _xlatePoint(self, point):
return [a * b for a, b in zip(point, self.cellSize)]
def drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc):
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
[self.drawPatch(canvas, eventDc, patch) for patch in canvasJournal.popCursor()]
self.drawPatches(canvas, eventDc, canvasJournal.popCursor(), True)
eventDc.SetDeviceOrigin(*eventDcOrigin)
def drawPatch(self, canvas, eventDc, patch, isCursor=False):
point = patch[:2]
if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]:
if patch[5] in self.arabicShapes:
for patchReshaped in self._reshapeArabic(canvas, eventDc, isCursor, patch, point):
self._drawPatch(eventDc, isCursor, patchReshaped[2:], canvas.map[patchReshaped[1]][patchReshaped[0]], patchReshaped[:2])
else:
self._drawPatch(eventDc, isCursor, patch[2:], canvas.map[patch[1]][patch[0]], point)
return True
else:
return False
def drawPatches(self, canvas, eventDc, patches, isCursor=False):
patchDeltaCells, patchesRender = [], []
for patch in patches:
point = patch[:2]
if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]:
if patch[5] in self.arabicShapes:
for patchReshaped in self._reshapeArabic(canvas, eventDc, isCursor, patch, point):
patchesRender += [patchReshaped]
else:
patchesRender += [patch]
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 = True, [*patchRender[:-1], ""]
textBg, textFg = wx.Colour(Colours[patchRender[3]][:4]), wx.Colour(Colours[patchRender[2]][:4])
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)):
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[2]))
elif (patchRender[5] != " ") or (patchRender[4] & self._CellState.CS_UNDERLINE):
charFlag = True
textBg, textFg = wx.Colour(Colours[patchRender[3]][:4]), 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.DestroyClippingRegion(); eventDc.SetClippingRegion(*absPoint, *self.cellSize);
eventDc.SetTextForeground(textFg); eventDc.DrawText(patchRender[5], *absPoint);
eventDc.DestroyClippingRegion()
patchDeltaCells += [[*patchRender[:2], *canvas.map[patchRender[1]][patchRender[0]]]]
return patchDeltaCells
def getDeviceContext(self, clientSize, parentWindow, viewRect=None):
if viewRect == None:

0
liboperators/OperatorRotate.py Executable file → Normal file
View File

0
liboperators/OperatorTile.py Executable file → Normal file
View File

View File

@ -11,10 +11,6 @@ from RtlPlatform import getLocalConfPathName
import json, os, sys, wx
class RoarAssetsWindow(GuiMiniFrame):
def _drawPatch(self, canvas, eventDc, isCursor, patch):
if not isCursor:
self.backend.drawPatch(canvas, eventDc, patch)
def _import(self, f, pathName):
rc = False
self.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
@ -118,9 +114,11 @@ class RoarAssetsWindow(GuiMiniFrame):
self.backend.resize(canvas.size, self.cellSize)
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
patches = []
for numRow in range(canvas.size[1]):
for numCol in range(canvas.size[0]):
self.backend.drawPatch(canvas, eventDc, [numCol, numRow, *canvas.map[numRow][numCol]])
patches += [[numCol, numRow, *canvas.map[numRow][numCol]]]
self.backend.drawPatches(canvas, eventDc, patches, isCursor=False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
def onPaint(self, event):
@ -154,14 +152,16 @@ class RoarAssetsWindow(GuiMiniFrame):
self.backend.resize(newSize, self.cellSize)
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas)
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]):
self._drawPatch(canvas, eventDc, False, [numNewCol, numRow, 1, 1, 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]):
self._drawPatch(canvas, eventDc, False, [numNewCol, numNewRow, 1, 1, 0, " "])
patches += [[numNewCol, numNewRow, 1, 1, 0, " "]]
self.backend.drawPatches(canvas, eventDc, patches, isCursor=False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
def update(self, canvas, newSize, newCanvas=None):
@ -169,9 +169,11 @@ class RoarAssetsWindow(GuiMiniFrame):
canvas.update(newSize, newCanvas);
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
patches = []
for numRow in range(canvas.size[1]):
for numCol in range(canvas.size[0]):
self.backend.drawPatch(canvas, eventDc, [numCol, numRow, *canvas.map[numRow][numCol]])
patches += [[numCol, numRow, *canvas.map[numRow][numCol]]]
self.backend.drawPatches(canvas, eventDc, patches, isCursor=False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
def onImportAnsi(self, event):

View File

@ -194,14 +194,12 @@ class RoarCanvasCommandsEdit():
@GuiCommandDecorator("Redo", "&Redo", ["", wx.ART_REDO], [wx.ACCEL_CTRL, ord("Y")], False)
def canvasRedo(self, event):
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas, self.parentCanvas.GetViewStart())
self.parentCanvas.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popRedo(), eventDc=eventDc, forceDirtyCursor=True)
self.parentCanvas.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popRedo(), forceDirtyCursor=True)
self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel)
@GuiCommandDecorator("Undo", "&Undo", ["", wx.ART_UNDO], [wx.ACCEL_CTRL, ord("Z")], False)
def canvasUndo(self, event):
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas, self.parentCanvas.GetViewStart())
self.parentCanvas.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popUndo(), eventDc=eventDc, forceDirtyCursor=True)
self.parentCanvas.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popUndo(), forceDirtyCursor=True)
self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel)
def __init__(self):

View File

@ -40,97 +40,86 @@ class RoarCanvasWindowDropTarget(wx.TextDropTarget):
super().__init__(); self.inProgress, self.parent = False, parent;
class RoarCanvasWindow(GuiWindow):
def _drawPatch(self, eventDc, isCursor, patch):
if not self.canvas.dirtyCursor:
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc)
self.canvas.dirtyCursor = True
if self.backend.drawPatch(self.canvas, eventDc, patch, isCursor=isCursor) and isCursor:
patchDeltaCell = self.canvas.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell];
self.canvas.journal.pushCursor(patchDelta)
def _applyPatches(self, eventDc, patches, patchesCursor, rc):
if rc:
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
if ((patches != None) and (len(patches) > 0)) \
or ((patchesCursor != None) and (len(patchesCursor) > 0)):
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc); self.canvas.dirtyCursor = True;
if (patches != None) and (len(patches) > 0):
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
self.dirty = True if not self.dirty else self.dirty;
self.canvas.journal.begin()
for patch in patches if patches != None else []:
self.canvas.dispatchPatchSingle(False, patch, commitUndo=True)
self.canvas.journal.end()
if patchesCursor != None:
patchesCursorDeltaCells = self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
if len(patchesCursorDeltaCells) > 0:
self.canvas.dirtyCursor = False if self.canvas.dirtyCursor else self.canvas.dirtyCursor
for patchCursorDeltaCell in patchesCursorDeltaCells:
self.canvas.journal.pushCursor(patchCursorDeltaCell)
eventDc.SetDeviceOrigin(*eventDcOrigin)
self.commands.update(dirty=self.dirty, cellPos=self.brushPos, undoLevel=self.canvas.journal.patchesUndoLevel)
def applyOperator(self, currentTool, mapPoint, mouseLeftDown, mousePoint, operator, viewRect):
self.canvas.dirtyCursor = False
if (currentTool.__class__ == ToolObject) \
and (currentTool.toolState >= currentTool.TS_SELECT):
eventDc, patches, patchesCursor, rc = self.backend.getDeviceContext(self.GetClientSize(), self), None, None, True
if (currentTool.__class__ == ToolObject) and (currentTool.toolState >= currentTool.TS_SELECT):
region = currentTool.getRegion(self.canvas)
else:
region = self.canvas.map
if hasattr(operator, "apply2"):
if mouseLeftDown:
if self.commands.operatorState == None:
self.commands.operatorState = True
self.commands.operatorState = True if self.commands.operatorState == None else self.commands.operatorState
region = operator.apply2(mapPoint, mousePoint, region, copy.deepcopy(region))
self.commands.update(operator=self.commands.currentOperator.name)
elif self.commands.operatorState != None:
self.commands.currentOperator = None
self.commands.update(operator=None)
return
self.commands.currentOperator = None; self.commands.update(operator=None); rc = False;
else:
region = operator.apply(copy.deepcopy(region))
self.commands.currentOperator = None
if (currentTool.__class__ == ToolObject) \
and (currentTool.toolState >= currentTool.TS_SELECT):
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self) if self.popupEventDc == None else self.popupEventDc
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
currentTool.setRegion(self.canvas, None, region, [len(region[0]), len(region)], currentTool.external)
currentTool.onSelectEvent(self.canvas, (0, 0), self.dispatchPatchSingle, eventDc, True, wx.MOD_NONE, None, currentTool.targetRect)
currentTool._drawSelectRect(currentTool.targetRect, self.dispatchPatchSingle, eventDc)
eventDc.SetDeviceOrigin(*eventDcOrigin)
else:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self) if self.popupEventDc == None else self.popupEventDc
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
self.canvas.journal.begin()
for numRow in range(len(region)):
for numCol in range(len(region[numRow])):
self.dirty = True if not self.dirty else self.dirty
self.dispatchPatchSingle(eventDc, False, [numCol, numRow, *region[numRow][numCol]])
self.canvas.journal.end()
self.commands.update(dirty=self.dirty, undoLevel=self.canvas.journal.patchesUndoLevel)
eventDc.SetDeviceOrigin(*eventDcOrigin)
region = operator.apply(copy.deepcopy(region)); self.commands.currentOperator = None;
if rc:
if (currentTool.__class__ == ToolObject) and (currentTool.toolState >= currentTool.TS_SELECT):
currentTool.setRegion(self.canvas, None, region, [len(region[0]), len(region)], currentTool.external)
rc, patches, patchesCursor = currentTool.onSelectEvent(self.canvas, (0, 0), True, wx.MOD_NONE, None, currentTool.targetRect)
patchesCursor = [] if patchesCursor == None else patchesCursor
patchesCursor += currentTool._drawSelectRect(currentTool.targetRect)
else:
patches = []
for numRow in range(len(region)):
for numCol in range(len(region[numRow])):
patches += [[numCol, numRow, *region[numRow][numCol]]]
self._applyPatches(eventDc, patches, patchesCursor, rc)
return rc
def applyTool(self, eventDc, eventMouse, keyChar, keyCode, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, tool, viewRect, force=False):
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
dirty, self.canvas.dirtyCursor, rc = False, False, False
self.canvas.journal.begin()
dirty, patches, patchesCursor, rc = False, None, None, False
if eventMouse:
if force:
self.lastCellState = None
if ((mapPoint[0] < self.canvas.size[0]) \
and (mapPoint[1] < self.canvas.size[1])) \
and ((self.lastCellState == None) \
or (self.lastCellState != [list(mapPoint), mouseDragging, mouseLeftDown, mouseRightDown, list(viewRect)])):
if tool.__class__ != ToolText:
self.brushPos = list(mapPoint)
self.lastCellState = None if force else self.lastCellState
if ((mapPoint[0] < self.canvas.size[0]) and (mapPoint[1] < self.canvas.size[1])) \
and ((self.lastCellState == None) or (self.lastCellState != [list(mapPoint), mouseDragging, mouseLeftDown, mouseRightDown, list(viewRect)])):
self.brushPos = list(mapPoint) if tool.__class__ != ToolText else self.brushPos
if tool != None:
rc, dirty = tool.onMouseEvent(mapPoint, self.brushColours, self.brushPos, self.brushSize, self.canvas, self.dispatchPatchSingle, eventDc, keyModifiers, self.brushPos, mouseDragging, mouseLeftDown, mouseRightDown)
rc, patches, patchesCursor = tool.onMouseEvent(mapPoint, self.brushColours, self.brushPos, self.brushSize, self.canvas, keyModifiers, self.brushPos, mouseDragging, mouseLeftDown, mouseRightDown)
else:
self.dispatchPatchSingle(eventDc, True, [*mapPoint, self.brushColours[0], self.brushColours[0], 0, " "])
rc, patches, patchesCursor = True, None, [[*mapPoint, self.brushColours[0], self.brushColours[0], 0, " "]]
self.lastCellState = [list(mapPoint), mouseDragging, mouseLeftDown, mouseRightDown, list(viewRect)]
else:
if tool != None:
rc, dirty = tool.onKeyboardEvent(mapPoint, self.brushColours, self.brushPos, self.brushSize, self.canvas, self.dispatchPatchSingle, eventDc, keyChar, keyCode, keyModifiers, self.brushPos)
rc, patches, patchesCursor = tool.onKeyboardEvent(mapPoint, self.brushColours, self.brushPos, self.brushSize, self.canvas, keyChar, keyCode, keyModifiers, self.brushPos)
elif mapPoint != None:
self.dispatchPatchSingle(eventDc, True, [*mapPoint, self.brushColours[0], self.brushColours[0], 0, " "])
self.canvas.journal.end()
if dirty:
self.dirty = True
self.commands.update(dirty=self.dirty, cellPos=self.brushPos, undoLevel=self.canvas.journal.patchesUndoLevel)
else:
self.commands.update(cellPos=self.brushPos)
if rc and (tool.__class__ == ToolObject):
if tool.toolState > tool.TS_NONE:
self.commands.update(undoInhibit=True)
elif tool.toolState == tool.TS_NONE:
if tool.external:
self.dropTarget.done()
self.commands.currentTool, self.commands.lastTool = self.commands.lastTool, self.commands.currentTool
if self.commands.currentTool != None:
self.commands.update(toolName=self.commands.currentTool.name, undoInhibit=False)
rc, patches, patchesCursor = True, None, [[*mapPoint, self.brushColours[0], self.brushColours[0], 0, " "]]
if rc:
self._applyPatches(eventDc, patches, patchesCursor, rc)
if tool.__class__ == ToolObject:
if tool.toolState > tool.TS_NONE:
self.commands.update(undoInhibit=True)
elif tool.toolState == tool.TS_NONE:
if tool.external:
self.dropTarget.done(); self.commands.currentTool, self.commands.lastTool = self.commands.lastTool, self.commands.currentTool;
newToolName = "Cursor" if self.commands.currentTool == None else self.commands.currentTool.name
self.commands.update(toolName=newToolName, undoInhibit=False)
else:
self.commands.update(toolName="Cursor", undoInhibit=False)
else:
self.commands.update(undoInhibit=False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
self.commands.update(undoInhibit=False)
return rc
def dispatchDeltaPatches(self, deltaPatches, eventDc=None, forceDirtyCursor=True):
@ -140,6 +129,7 @@ class RoarCanvasWindow(GuiWindow):
if self.canvas.dirtyCursor or forceDirtyCursor:
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc)
self.canvas.dirtyCursor = False
patches = []
for patch in deltaPatches:
if patch == None:
continue
@ -148,46 +138,8 @@ class RoarCanvasWindow(GuiWindow):
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
else:
self.canvas._commitPatch(patch); self.backend.drawPatch(self.canvas, eventDc, patch)
eventDc.SetDeviceOrigin(*eventDcOrigin)
def dispatchPatch(self, eventDc, isCursor, patch):
if self.canvas.dispatchPatch(isCursor, patch, False if isCursor else True):
self._drawPatch(eventDc, isCursor, patch)
def dispatchPatchSingle(self, eventDc, isCursor, patch):
if self.canvas.dispatchPatchSingle(isCursor, patch, False if isCursor else True):
self._drawPatch(eventDc, isCursor, patch)
def resize(self, newSize, commitUndo=True, dirty=True):
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):
super().resize([a * b for a, b in zip(newSize, self.backend.cellSize)])
self.backend.resize(newSize, self.backend.cellSize)
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
if deltaSize[0] > 0:
for numRow in range(oldSize[1]):
for numNewCol in range(oldSize[0], newSize[0]):
self._drawPatch(eventDc, False, [numNewCol, numRow, 1, 1, 0, " "])
if deltaSize[1] > 1:
for numNewRow in range(oldSize[1], newSize[1]):
for numNewCol in range(newSize[0]):
self._drawPatch(eventDc, False, [numNewCol, numNewRow, 1, 1, 0, " "])
eventDc.SetDeviceOrigin(*eventDcOrigin)
self.Scroll(*viewRect); self.dirty = dirty;
self.commands.update(dirty=self.dirty, size=newSize, undoLevel=self.canvas.journal.patchesUndoLevel)
def update(self, newSize, commitUndo=True, newCanvas=None, dirty=True):
self.resize(newSize, commitUndo, dirty)
self.canvas.update(newSize, newCanvas)
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
for numRow in range(newSize[1]):
for numCol in range(newSize[0]):
self.backend.drawPatch(self.canvas, eventDc, [numCol, numRow, *self.canvas.map[numRow][numCol]])
self.canvas._commitPatch(patch); patches += [patch];
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
def onKeyboardInput(self, event):
@ -268,9 +220,11 @@ class RoarCanvasWindow(GuiWindow):
self.backend.resize(self.canvas.size, self.backend.cellSize)
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
patches = []
for numRow in range(self.canvas.size[1]):
for numCol in range(len(self.canvas.map[numRow])):
self._drawPatch(eventDc, False, [numCol, numRow, *self.canvas.map[numRow][numCol]])
patches += [[numCol, numRow, *self.canvas.map[numRow][numCol]]]
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
else:
event.Skip()
@ -280,6 +234,40 @@ class RoarCanvasWindow(GuiWindow):
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc)
self.backend.onPaint(self.GetClientSize(), self, self.GetViewStart())
def resize(self, newSize, commitUndo=True, dirty=True):
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):
super().resize([a * b for a, b in zip(newSize, self.backend.cellSize)])
self.backend.resize(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)
def update(self, newSize, commitUndo=True, newCanvas=None, dirty=True):
self.resize(newSize, commitUndo, dirty)
self.canvas.update(newSize, newCanvas)
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0); patches = [];
for numRow in range(newSize[1]):
for numCol in range(newSize[0]):
patches += [[numCol, numRow, *self.canvas.map[numRow][numCol]]]
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
def __init__(self, backend, canvas, cellSize, commands, parent, parentFrame, pos, scrollStep, size):
super().__init__(parent, pos, scrollStep)
self.size = size

30
librtl/Rtl.py Normal file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
#
# Rtl.py
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
# This project is licensed under the terms of the MIT licence.
#
import statistics, time
timeState = [None, None, 0, []]
def flatten(l):
return [li for l_ in l for li in l_]
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

View File

@ -5,10 +5,10 @@
#
class Tool(object):
def onKeyboardEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyChar, keyCode, keyModifiers, mapPoint):
return False, False
def onKeyboardEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyChar, keyCode, keyModifiers, mapPoint):
return False, None, None
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
return False, False
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
return False, None, None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -9,8 +9,8 @@ from Tool import Tool
class ToolCircle(Tool):
name = "Circle"
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
brushColours, brushSize, dirty = list(brushColours), [brushSize[0] * 2, brushSize[1]], False
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
brushColours, brushSize, isCursor = list(brushColours), [brushSize[0] * 2, brushSize[1]], not (mouseLeftDown or mouseRightDown)
originPoint, radius = (brushSize[0] / 2, brushSize[0] / 2), brushSize[0]
if mouseRightDown:
brushColours = [brushColours[1], brushColours[0]]
@ -22,6 +22,7 @@ class ToolCircle(Tool):
cells[-1] += [[mapPoint[i] + int(originPoint[i] + o) for i, o in zip((0, 1,), (brushX, brushY,))]]
if cells[-1] == []:
del cells[-1]
patches = []
for numRow in range(len(cells)):
for numCol in range(len(cells[numRow])):
if ((numRow == 0) or (numRow == (len(cells) - 1))) \
@ -41,12 +42,7 @@ class ToolCircle(Tool):
patch = [*cells[numRow][numCol], brushColours[1], brushColours[1], 0, " "]
else:
patch = [*cells[numRow][numCol], brushColours[1], brushColours[1], 0, " "]
if mouseLeftDown or mouseRightDown:
if not dirty:
dirty = True
dispatchFn(eventDc, False, patch); dispatchFn(eventDc, True, patch);
else:
dispatchFn(eventDc, True, patch)
return True, dirty
patches += [patch]
return True, patches if not isCursor else None, patches if isCursor else None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -9,28 +9,21 @@ from Tool import Tool
class ToolErase(Tool):
name = "Erase"
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
brushColours, brushSize, dirty = list(brushColours), list(brushSize), False
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
brushColours, brushSize, isCursor, patches, patchesCursor = list(brushColours), list(brushSize), not (mouseLeftDown or mouseRightDown), [], []
if brushSize[0] > 1:
brushSize[0] *= 2
for brushRow in range(brushSize[1]):
for brushCol in range(brushSize[0]):
if mouseLeftDown:
patch = [mapPoint[0] + brushCol, mapPoint[1] + brushRow, brushColours[1], brushColours[1], 0, " "]
if not dirty:
dirty = True
dispatchFn(eventDc, False, patch); dispatchFn(eventDc, True, patch);
patches += [[mapPoint[0] + brushCol, mapPoint[1] + brushRow, brushColours[1], brushColours[1], 0, " "]]
elif mouseRightDown \
and ((mapPoint[0] + brushCol) < canvas.size[0]) \
and ((mapPoint[1] + brushRow) < canvas.size[1]) \
and (canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][1] == brushColours[1]):
patch = [mapPoint[0] + brushCol, mapPoint[1] + brushRow, canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][0], brushColours[0], *canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][2:]]
if not dirty:
dirty = True
dispatchFn(eventDc, False, patch); dispatchFn(eventDc, True, patch);
patches += [[mapPoint[0] + brushCol, mapPoint[1] + brushRow, canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][0], brushColours[0], *canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][2:]]]
else:
patch = [mapPoint[0] + brushCol, mapPoint[1] + brushRow, brushColours[1], brushColours[1], 0, " "]
dispatchFn(eventDc, True, patch)
return True, dirty
patchesCursor += [[mapPoint[0] + brushCol, mapPoint[1] + brushRow, brushColours[1], brushColours[1], 0, " "]]
return True, patches if not isCursor else None, patchesCursor
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -10,22 +10,17 @@ import wx
class ToolFill(Tool):
name = "Fill"
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
dirty, pointsDone, pointStack, testChar, testColour = False, [], [list(mapPoint)], canvas.map[mapPoint[1]][mapPoint[0]][3], canvas.map[mapPoint[1]][mapPoint[0]][0:2]
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
isCursor, patches, pointsDone, pointStack, testChar, testColour = not (mouseLeftDown or mouseRightDown), [], [], [list(mapPoint)], canvas.map[mapPoint[1]][mapPoint[0]][3], canvas.map[mapPoint[1]][mapPoint[0]][0:2]
if mouseLeftDown or mouseRightDown:
if mouseLeftDown:
fillColour = brushColours[0]
else:
fillColour = brushColours[1]
fillColour = brushColours[0] if mouseLeftDown else brushColours[1]
while len(pointStack) > 0:
point = pointStack.pop()
pointCell = canvas.map[point[1]][point[0]]
if ((pointCell[1] == testColour[1]) and ((pointCell[3] == testChar) or (keyModifiers == wx.MOD_CONTROL))) \
or ((pointCell[3] == " ") and (pointCell[1] == testColour[1])):
if not point in pointsDone:
if not dirty:
dirty = True
dispatchFn(eventDc, False, [*point, fillColour, fillColour, 0, " "])
patches += [[*point, fillColour, fillColour, 0, " "]]
if point[0] > 0:
pointStack.append([point[0] - 1, point[1]])
if point[0] < (canvas.size[0] - 1):
@ -36,8 +31,7 @@ class ToolFill(Tool):
pointStack.append([point[0], point[1] + 1])
pointsDone += [point]
else:
patch = [mapPoint[0], mapPoint[1], brushColours[0], brushColours[0], 0, " "]
dispatchFn(eventDc, True, patch)
return True, dirty
patches = [[mapPoint[0], mapPoint[1], brushColours[0], brushColours[0], 0, " "]]
return True, patches if not isCursor else None, patches if isCursor else None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -11,9 +11,8 @@ class ToolLine(Tool):
TS_NONE = 0
TS_ORIGIN = 1
def _getLine(self, brushColours, brushSize, dispatchFn, eventDc, isCursor, originPoint, targetPoint):
dirty = False
originPoint, targetPoint = originPoint.copy(), targetPoint.copy()
def _getLine(self, brushColours, brushSize, isCursor, originPoint, targetPoint):
originPoint, patches, targetPoint = originPoint.copy(), [], targetPoint.copy()
pointDelta = self._pointDelta(originPoint, targetPoint)
lineXSign = 1 if pointDelta[0] > 0 else -1; lineYSign = 1 if pointDelta[1] > 0 else -1;
pointDelta = [abs(a) for a in pointDelta]
@ -27,21 +26,15 @@ class ToolLine(Tool):
for lineX in range(pointDelta[0] + 1):
for brushStep in range(brushSize[0]):
if not ([originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, originPoint[1] + lineX * lineXY + lineY * lineYY] in pointsDone):
patch = [ \
patches += [[ \
originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, \
originPoint[1] + lineX * lineXY + lineY * lineYY, \
*brushColours, 0, " "]
if not isCursor:
if not dirty:
dirty = True
dispatchFn(eventDc, False, patch)
else:
dispatchFn(eventDc, True, patch)
*brushColours, 0, " "]]
pointsDone += [[originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, originPoint[1] + lineX * lineXY + lineY * lineYY]]
if lineD > 0:
lineD -= pointDelta[0]; lineY += 1;
lineD += pointDelta[1]
return dirty
return patches
def _pointDelta(self, a, b):
return [a2 - a1 for a1, a2 in zip(a, b)]
@ -49,8 +42,8 @@ class ToolLine(Tool):
def _pointSwap(self, a, b):
return [b, a]
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
brushColours, dirty = brushColours.copy(), False
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
brushColours, isCursor, patches, rc = brushColours.copy(), not (mouseLeftDown or mouseRightDown), [], False
if mouseLeftDown:
brushColours[1] = brushColours[0]
elif mouseRightDown:
@ -60,17 +53,20 @@ class ToolLine(Tool):
if self.toolState == self.TS_NONE:
if mouseLeftDown or mouseRightDown:
self.toolOriginPoint, self.toolState = list(mapPoint), self.TS_ORIGIN
dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, " "])
patches, rc = [], True
for brushCol in range(brushSize[0]):
if ((mapPoint[0] + brushCol) < canvas.size[0]) \
and (mapPoint[1] < canvas.size[1]):
patches += [[mapPoint[0] + brushCol, mapPoint[1], *brushColours, 0, " "]]
elif self.toolState == self.TS_ORIGIN:
originPoint, targetPoint = self.toolOriginPoint, list(mapPoint)
if mouseLeftDown or mouseRightDown:
dirty = self._getLine(brushColours, brushSize, dispatchFn, eventDc, False, originPoint, targetPoint)
patches = self._getLine(brushColours, brushSize, False, originPoint, targetPoint)
self.toolOriginPoint, self.toolState = None, self.TS_NONE
else:
dirty = self._getLine(brushColours, brushSize, dispatchFn, eventDc, True, originPoint, targetPoint)
else:
return False, dirty
return True, dirty
patches = self._getLine(brushColours, brushSize, True, originPoint, targetPoint)
rc = True
return rc, patches if not isCursor else None, patches if isCursor else None
def __init__(self, *args):
super().__init__(*args)

View File

@ -14,19 +14,21 @@ class ToolObject(Tool):
TS_SELECT = 2
TS_TARGET = 3
def _dispatchSelectEvent(self, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseLeftDown, selectRect):
def _dispatchSelectEvent(self, canvas, keyModifiers, mapPoint, mouseLeftDown, selectRect):
if mouseLeftDown:
disp, isCursor = [mapPoint[m] - self.lastAtPoint[m] for m in [0, 1]], True
newTargetRect = [[selectRect[n][m] + disp[m] for m in [0, 1]] for n in [0, 1]]
self.lastAtPoint = list(mapPoint)
else:
disp, isCursor, newTargetRect = [0, 0], True, selectRect.copy()
dirty = self.onSelectEvent(canvas, disp, dispatchFn, eventDc, isCursor, keyModifiers, newTargetRect, selectRect)
self._drawSelectRect(newTargetRect, dispatchFn, eventDc)
rc, patches, patchesCursor = self.onSelectEvent(canvas, disp, isCursor, keyModifiers, newTargetRect, selectRect)
patchesCursor = [] if patchesCursor == None else patchesCursor
patchesCursor += self._drawSelectRect(newTargetRect)
self.targetRect = newTargetRect
return dirty
return rc, patches, patchesCursor
def _drawSelectRect(self, rect, dispatchFn, eventDc):
def _drawSelectRect(self, rect):
patches = []
rectFrame = [[rect[m][n] for n in [0, 1]] for m in (0, 1)]
if rectFrame[0][0] > rectFrame[1][0]:
rectFrame[0][0], rectFrame[1][0] = rectFrame[1][0], rectFrame[0][0]
@ -35,25 +37,19 @@ class ToolObject(Tool):
curColours, rectFrame = [0, 0], [[rectFrame[m[0]][n] + m[1] for n in [0, 1]] for m in [[0, -1], [1, +1]]]
for rectX in range(rectFrame[0][0], rectFrame[1][0] + 1):
curColours = [1, 1] if curColours == [0, 0] else [0, 0]
dispatchFn(eventDc, True, [rectX, rectFrame[0][1], *curColours, 0, " "])
dispatchFn(eventDc, True, [rectX, rectFrame[1][1], *curColours, 0, " "])
patches += [[rectX, rectFrame[0][1], *curColours, 0, " "], [rectX, rectFrame[1][1], *curColours, 0, " "]]
for rectY in range(rectFrame[0][1], rectFrame[1][1] + 1):
curColours = [1, 1] if curColours == [0, 0] else [0, 0]
dispatchFn(eventDc, True, [rectFrame[0][0], rectY, *curColours, 0, " "])
dispatchFn(eventDc, True, [rectFrame[1][0], rectY, *curColours, 0, " "])
patches += [[rectFrame[0][0], rectY, *curColours, 0, " "], [rectFrame[1][0], rectY, *curColours, 0, " "]]
return patches
def _mouseEventTsNone(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
self.substract = False
if self.external:
dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, " "])
else:
if mouseLeftDown:
self.targetRect, self.toolState = [list(mapPoint), []], self.TS_ORIGIN
else:
dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, " "])
return False
def _mouseEventTsNone(self, brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
patchesCursor = [[*mapPoint, brushColours[0], brushColours[0], 0, " "]]; self.substract = False;
if (not self.external) and mouseLeftDown:
self.targetRect, self.toolState = [list(mapPoint), []], self.TS_ORIGIN
return True, None, patchesCursor
def _mouseEventTsOrigin(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
def _mouseEventTsOrigin(self, brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
self.targetRect[1] = list(mapPoint)
if not mouseLeftDown:
if self.targetRect[0][0] > self.targetRect[1][0]:
@ -66,13 +62,10 @@ class ToolObject(Tool):
for numCol in range((self.targetRect[1][0] - self.targetRect[0][0]) + 1):
rectX, rectY = self.targetRect[0][0] + numCol, self.targetRect[0][1] + numRow
self.objectMap[numRow].append(canvas.map[rectY][rectX])
self._drawSelectRect(self.targetRect, dispatchFn, eventDc)
else:
self._drawSelectRect(self.targetRect, dispatchFn, eventDc)
return False
return True, None, self._drawSelectRect(self.targetRect)
def _mouseEventTsSelect(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
dirty = False
def _mouseEventTsSelect(self, brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
rc, patches, patchesCursor = False, None, None
if mouseLeftDown:
if (mapPoint[0] >= (self.targetRect[0][0] - 1)) \
and (mapPoint[0] <= (self.targetRect[1][0] + 1)) \
@ -80,80 +73,78 @@ class ToolObject(Tool):
and (mapPoint[1] <= (self.targetRect[1][1] + 1)):
self.lastAtPoint, self.toolState = list(mapPoint), self.TS_TARGET
else:
dirty = self.onSelectEvent(canvas, (0, 0), dispatchFn, eventDc, False, keyModifiers, self.targetRect.copy(), self.targetRect)
self._drawSelectRect(self.targetRect, dispatchFn, eventDc)
rc, patches, patchesCursor = self.onSelectEvent(canvas, (0, 0), False, keyModifiers, self.targetRect.copy(), self.targetRect)
patchesCursor = [] if patchesCursor == None else patchesCursor
patchesCursor += self._drawSelectRect(self.targetRect)
self.objectMap, self.objectSize, self.targetRect, self.toolState = None, None, None, self.TS_NONE
else:
dirty = self._dispatchSelectEvent(canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseLeftDown, self.targetRect)
return dirty
rc, patches, patchesCursor = self._dispatchSelectEvent(canvas, keyModifiers, mapPoint, mouseLeftDown, self.targetRect)
return rc, patches, patchesCursor
def _mouseEventTsTarget(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
def _mouseEventTsTarget(self, brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
rc, patches, patchesCursor = False, None, None
if (keyModifiers == wx.MOD_CONTROL) and (self.srcRect == self.targetRect):
self.substract = True
dirty = False
if mouseLeftDown:
dirty = self._dispatchSelectEvent(canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseLeftDown, self.targetRect)
rc, patches, patchesCursor = self._dispatchSelectEvent(canvas, keyModifiers, mapPoint, mouseLeftDown, self.targetRect)
else:
self.toolState = self.TS_SELECT
return dirty
return rc, patches, patchesCursor
def getRegion(self, canvas):
return self.objectMap
def onKeyboardEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyChar, keyCode, keyModifiers, mapPoint):
rc, dirty = False, False
if (ord(keyChar) == wx.WXK_ESCAPE) \
and (self.toolState >= self.TS_SELECT):
dirty = self.onSelectEvent(canvas, (0, 0), dispatchFn, eventDc, False, keyModifiers, self.targetRect.copy(), self.targetRect)
self._drawSelectRect(self.targetRect, dispatchFn, eventDc)
def onKeyboardEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyChar, keyCode, keyModifiers, mapPoint):
if (ord(keyChar) == wx.WXK_ESCAPE) and (self.toolState >= self.TS_SELECT):
rc, patches, patchesCursor = self.onSelectEvent(canvas, (0, 0), False, keyModifiers, self.targetRect.copy(), self.targetRect)
patchesCursor = [] if patchesCursor == None else patchesCursor
patchesCursor += self._drawSelectRect(self.targetRect)
self.objectMap, self.objectSize, self.targetRect, self.toolState = None, None, None, self.TS_NONE
return rc, dirty
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
dirty = False
if self.toolState == self.TS_NONE:
dirty = self._mouseEventTsNone(brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
elif self.toolState == self.TS_SELECT:
dirty = self._mouseEventTsSelect(brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
elif self.toolState == self.TS_ORIGIN:
dirty = self._mouseEventTsOrigin(brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
elif self.toolState == self.TS_TARGET:
dirty = self._mouseEventTsTarget(brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
else:
return False, dirty
return True, dirty
rc, patches, patchesCursor = False, None, None
return rc, patches, patchesCursor
def onSelectEvent(self, canvas, disp, dispatchFn, eventDc, isCursor, keyModifiers, newTargetRect, selectRect):
dirty = False
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
if self.toolState == self.TS_NONE:
rc, patches, patchesCursor = self._mouseEventTsNone(brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
elif self.toolState == self.TS_SELECT:
rc, patches, patchesCursor = self._mouseEventTsSelect(brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
elif self.toolState == self.TS_ORIGIN:
rc, patches, patchesCursor = self._mouseEventTsOrigin(brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
elif self.toolState == self.TS_TARGET:
rc, patches, patchesCursor = self._mouseEventTsTarget(brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
else:
rc, patches, patchesCursor = False, None, None
return rc, patches, patchesCursor
def onSelectEvent(self, canvas, disp, isCursor, keyModifiers, newTargetRect, selectRect):
patches = []
if self.external:
for numRow in range(len(self.objectMap)):
for numCol in range(len(self.objectMap[numRow])):
rectX, rectY = selectRect[0][0] + numCol, selectRect[0][1] + numRow
dirty = False if isCursor else True
cellNew = self.objectMap[numRow][numCol]
if (cellNew[1] == -1) and (cellNew[3] == " "):
if ((rectY + disp[1]) < canvas.size[1]) and ((rectX + disp[0]) < canvas.size[0]):
cellNew = canvas.map[rectY + disp[1]][rectX + disp[0]]
dispatchFn(eventDc, isCursor, [rectX + disp[0], rectY + disp[1], *cellNew])
patches += [[rectX + disp[0], rectY + disp[1], *cellNew]]
else:
if self.substract:
for numRow in range(self.srcRect[0][1], self.srcRect[1][1]):
for numCol in range(self.srcRect[0][0], self.srcRect[1][0]):
if ((numCol < selectRect[0][0]) or (numCol > selectRect[1][0])) \
or ((numRow < selectRect[0][1]) or (numRow > selectRect[1][1])):
dirty = False if isCursor else True
dispatchFn(eventDc, isCursor, [numCol, numRow, 1, 1, 0, " "])
patches += [[numCol, numRow, 1, 1, 0, " "]]
for numRow in range(len(self.objectMap)):
for numCol in range(len(self.objectMap[numRow])):
cellOld = self.objectMap[numRow][numCol]
rectX, rectY = selectRect[0][0] + numCol, selectRect[0][1] + numRow
dirty = False if isCursor else True
cellNew = self.objectMap[numRow][numCol]
if (cellNew[1] == -1) and (cellNew[3] == " "):
if ((rectY + disp[1]) < canvas.size[1]) and ((rectX + disp[0]) < canvas.size[0]):
cellNew = canvas.map[rectY + disp[1]][rectX + disp[0]]
dispatchFn(eventDc, isCursor, [rectX + disp[0], rectY + disp[1], *cellNew])
return dirty
patches += [[rectX + disp[0], rectY + disp[1], *cellNew]]
return True, patches if not isCursor else None, patches if isCursor else None
def setRegion(self, canvas, mapPoint, objectMap, objectSize, external=True):
self.external, self.toolState = external, self.TS_SELECT

View File

@ -9,9 +9,8 @@ from Tool import Tool
class ToolPickColour(Tool):
name = "Pick colour"
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
if (mapPoint[0] < canvas.size[0]) \
and (mapPoint[1] < canvas.size[1]):
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
if (mapPoint[0] < canvas.size[0]) and (mapPoint[1] < canvas.size[1]):
if mouseLeftDown:
if canvas.map[mapPoint[1]][mapPoint[0]][3] == " ":
brushColours[0] = canvas.map[mapPoint[1]][mapPoint[0]][1]
@ -19,7 +18,6 @@ class ToolPickColour(Tool):
brushColours[0] = canvas.map[mapPoint[1]][mapPoint[0]][0]
elif mouseRightDown:
brushColours[1] = canvas.map[mapPoint[1]][mapPoint[0]][1]
dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, ""])
return True, False
return True, None, [[*mapPoint, *brushColours, 0, ""]]
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -9,8 +9,8 @@ from Tool import Tool
class ToolRect(Tool):
name = "Rectangle"
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
brushColours, brushSize, dirty = list(brushColours), list(brushSize), False
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
brushColours, brushSize, isCursor, patches = list(brushColours), list(brushSize), not (mouseLeftDown or mouseRightDown), []
if mouseRightDown:
brushColours = [brushColours[1], brushColours[0]]
if brushSize[0] > 1:
@ -30,12 +30,7 @@ class ToolRect(Tool):
else:
patchColours = [brushColours[1]] * 2
patch = [mapPoint[0] + brushCol, mapPoint[1] + brushRow, *patchColours, 0, " "]
if mouseLeftDown or mouseRightDown:
if not dirty:
dirty = True
dispatchFn(eventDc, False, patch); dispatchFn(eventDc, True, patch);
else:
dispatchFn(eventDc, True, patch)
return True, dirty
patches += [patch]
return True, patches if not isCursor else None, patches if isCursor else None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -41,11 +41,12 @@ class ToolText(Tool):
break
return rtlFlag
def _processKeyChar(self, brushColours, brushPos, canvas, dispatchFn, eventDc, keyChar, keyModifiers):
def _processKeyChar(self, brushColours, brushPos, canvas, keyChar, keyModifiers):
patches, rc = [], False
if (ord(keyChar) != wx.WXK_NONE) \
and (not keyChar in set("\t\n\v\f\r")) \
and ((ord(keyChar) >= 32) if ord(keyChar) < 127 else True):
dispatchFn(eventDc, False, [*brushPos, *brushColours, 0, keyChar]);
patches += [[*brushPos, *brushColours, 0, keyChar]]
if not self._checkRtl(canvas, brushPos, keyChar):
if brushPos[0] < (canvas.size[0] - 1):
brushPos[0] += 1
@ -60,38 +61,36 @@ class ToolText(Tool):
brushPos[0], brushPos[1] = canvas.size[0] - 1, brushPos[1] - 1
else:
brushPos[0], brushPos[1] = canvas.size[0] - 1, canvas.size[1] - 1
rc, dirty = True, True
else:
rc, dirty = False, False
return rc, dirty
rc = True
return rc, patches
def onKeyboardEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyChar, keyCode, keyModifiers, mapPoint):
def onKeyboardEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyChar, keyCode, keyModifiers, mapPoint):
patches, patchesCursor, rc = [], [], False
if re.match(self.arabicCombiningRegEx, keyChar):
rc, dirty = True, False
rc = True
elif keyCode == wx.WXK_CONTROL_V:
rc, dirty = True, False
rc = True
if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)) \
and wx.TheClipboard.Open():
inBuffer = wx.TextDataObject()
if wx.TheClipboard.GetData(inBuffer):
for inBufferChar in list(inBuffer.GetText()):
if not re.match(self.arabicCombiningRegEx, inBufferChar):
rc_, dirty_ = self._processKeyChar(brushColours, brushPos, canvas, dispatchFn, eventDc, inBufferChar, 0)
rc = True if rc_ else rc; dirty = True if dirty_ else dirty;
rc_, patches_ = self._processKeyChar(brushColours, brushPos, canvas, inBufferChar, 0)
patches += patches_
rc = True if rc_ else rc
if rc:
dispatchFn(eventDc, True, [*brushPos, *brushColours, 0, "_"])
patchesCursor += [[*brushPos, *brushColours, 0, "_"]]
wx.TheClipboard.Close()
else:
rc, error = False, "Clipboard does not contain text data and/or cannot be opened"
elif keyCode == wx.WXK_BACK:
if ((brushPos[0] + 1) >= canvas.size[0]):
if brushPos[1] > 0:
lastBrushPos = [0, brushPos[1] - 1]
else:
lastBrushPos = [0, 0]
lastBrushPos = [0, brushPos[1] - 1] if brushPos[1] > 0 else [0, 0]
else:
lastBrushPos = [brushPos[0] + 1, brushPos[1]]
if not self._checkRtl(canvas, lastBrushPos, None):
patches += [[*brushPos, *brushColours, 0, " "]]
if brushPos[0] > 0:
brushPos[0] -= 1
elif brushPos[1] > 0:
@ -105,24 +104,23 @@ class ToolText(Tool):
brushPos[0], brushPos[1] = 0, brushPos[1] - 1
else:
brushPos[0], brushPos[1] = canvas.size[0] - 1, 0
rc, dirty = True, False; dispatchFn(eventDc, False, [*brushPos, *brushColours, 0, " "]);
dispatchFn(eventDc, True, [*brushPos, *brushColours, 0, "_"]);
rc = True; patchesCursor += [[*brushPos, *brushColours, 0, "_"]];
elif keyCode == wx.WXK_RETURN:
if brushPos[1] < (canvas.size[1] - 1):
brushPos[0], brushPos[1] = 0, brushPos[1] + 1
else:
brushPos[0], brushPos[1] = 0, 0
rc, dirty = True, False; dispatchFn(eventDc, True, [*brushPos, *brushColours, 0, "_"]);
rc = True; patchesCursor += [[*brushPos, *brushColours, 0, "_"]];
else:
rc, dirty = self._processKeyChar(brushColours, brushPos, canvas, dispatchFn, eventDc, keyChar, keyModifiers)
rc, patches_ = self._processKeyChar(brushColours, brushPos, canvas, keyChar, keyModifiers)
patches += patches_
if rc:
dispatchFn(eventDc, True, [*brushPos, *brushColours, 0, "_"])
return rc, dirty
patchesCursor += [[*brushPos, *brushColours, 0, "_"]]
return rc, patches, patchesCursor
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
if mouseLeftDown or mouseRightDown:
brushPos[0], brushPos[1] = atPoint[0], atPoint[1]
dispatchFn(eventDc, True, [*brushPos, *brushColours, 0, "_"])
return True, False
return True, None, [[*brushPos, *brushColours, 0, "_"]]
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120