From f3140d4b3d1efc778a6b5a40a7105384e44c9efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucio=20Andr=C3=A9s=20Illanes=20Albornoz?= Date: Sat, 7 Sep 2019 10:39:26 +0200 Subject: [PATCH] libcanvas/CanvasColours.py, libgui/GuiCanvasColours.py: split from libcanvas/CanvasColours.py. libcanvas/Canvas.py, libgui/GuiCanvasPanel.py: split from libcanvas/Canvas.py. libcanvas/CanvasImportStore.py:import{Ansi{Buffer,File},SauceFile}(): fixed (via spoke.) {libgui/Gui{CanvasInterface,Frame},libtools/Tool{Fill,Select,Text},roar}.py: updated. libgui/GuiCanvasWxBackend.py: merged from libcanvas/CanvasBackend.py. --- assets/text/TODO | 21 +- libcanvas/Canvas.py | 246 +++++------------- libcanvas/CanvasColours.py | 20 -- libcanvas/CanvasImportStore.py | 49 ++-- libgui/GuiCanvasColours.py | 29 +++ libgui/GuiCanvasInterface.py | 91 ++++--- libgui/GuiCanvasPanel.py | 133 ++++++++++ .../GuiCanvasWxBackend.py | 6 +- libgui/GuiFrame.py | 77 +++--- libtools/ToolFill.py | 14 +- libtools/ToolSelect.py | 2 +- libtools/ToolText.py | 4 +- roar.py | 8 +- 13 files changed, 361 insertions(+), 339 deletions(-) create mode 100644 libgui/GuiCanvasColours.py create mode 100644 libgui/GuiCanvasPanel.py rename libcanvas/CanvasBackend.py => libgui/GuiCanvasWxBackend.py (98%) diff --git a/assets/text/TODO b/assets/text/TODO index 54acafe..d539a72 100644 --- a/assets/text/TODO +++ b/assets/text/TODO @@ -1,14 +1,15 @@ 1) General {cleanup,refactor} -2) Implement ANSI CSI CU[BDPU] sequences -3) Incremental auto{load,save} & {backup,restore} -4) Open and toggle a reference image in the background -5) Client-Server or Peer-to-Peer realtime collaboration -6) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.) -7) Hotkey & graphical interfaces to {composed,parametrised} tools -8) GUI: a) scrollbar b) switch from wxPython to GTK c) revisit About dialogue -9) Layers, layout (e.g. for comics, zines, etc.) & asset management (e.g. kade, lion, etc.) & traits w/ {inserting,merging,linking} -10) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...) -11) Composition and parametrisation of tools from higher-order operators (brushes, filters, outlines, patterns & shaders) and unit tools; unit tools: +2) Transparent {back,fore}ground colour +3) Implement ANSI CSI CU[BDPU] sequences +4) Incremental auto{load,save} & {backup,restore} +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) Hotkey & graphical interfaces to {composed,parametrised} tools +9) GUI: a) scrollbar b) switch from wxPython to GTK c) revisit About dialogue +10) Layers, layout (e.g. for comics, zines, etc.) & asset management (e.g. kade, lion, etc.) & traits w/ {inserting,merging,linking} +11) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...) +13) Composition and parametrisation of tools from higher-order operators (brushes, filters, outlines, patterns & shaders) and unit tools; unit tools: a) geometric primitives (arrow, circle, cloud/speech bubble, curve, heart, hexagon, line, pentagon, polygon, rhombus, triangle, square, star) b) regions (crop, duplicate, erase, fill, invert, measure, pick, rotate, scale, select, shift, slice, tile, translate) c) text (edit, Unicode sets) diff --git a/libcanvas/Canvas.py b/libcanvas/Canvas.py index 0241dbb..d754b43 100644 --- a/libcanvas/Canvas.py +++ b/libcanvas/Canvas.py @@ -4,194 +4,90 @@ # Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz # -from CanvasBackend import CanvasBackend -from CanvasJournal import CanvasJournal -from CanvasExportStore import CanvasExportStore, havePIL, haveUrllib +from CanvasExportStore import CanvasExportStore from CanvasImportStore import CanvasImportStore -import wx +from CanvasJournal import CanvasJournal -class Canvas(wx.Panel): +class Canvas(): """XXX""" # {{{ _commitPatch(self, patch): XXX def _commitPatch(self, patch): - self.canvasMap[patch[1]][patch[0]] = patch[2:] - # }}} - # {{{ _dispatchDeltaPatches(self, deltaPatches): XXX - def _dispatchDeltaPatches(self, deltaPatches): - eventDc = self.canvasBackend.getDeviceContext(self) - for patch in deltaPatches: - if patch[0] == "resize": - del eventDc - self.resize(patch[1:], False) - eventDc = self.canvasBackend.getDeviceContext(self) - elif self.canvasBackend.drawPatch(eventDc, patch): - self._commitPatch(patch) - self.parentFrame.onCanvasUpdate(undoLevel=self.canvasJournal.patchesUndoLevel) - # }}} - # {{{ _dispatchPatch(self, eventDc, isCursor, patch, commitUndo=True): XXX - def _dispatchPatch(self, eventDc, isCursor, patch, commitUndo=True): - if not self._canvasDirtyCursor: - self.canvasBackend.drawCursorMaskWithJournal(self.canvasJournal, eventDc) - self._canvasDirtyCursor = True - if self.canvasBackend.drawPatch(eventDc, patch): - patchDeltaCell = self.canvasMap[patch[1]][patch[0]] - patchDelta = [*patch[0:2], *patchDeltaCell] - if isCursor and commitUndo: - self.canvasJournal.pushCursor(patchDelta) - else: - if commitUndo: - if not self._canvasDirty: - self.canvasJournal.pushDeltas([], []); self._canvasDirty = True; - self.canvasJournal.updateCurrentDeltas(patch, patchDelta) - self._commitPatch(patch) + self.map[patch[1]][patch[0]] = patch[2:] # }}} - # {{{ onPanelClose(self, event): XXX - def onPanelClose(self, event): - self.Destroy() - # }}} - # {{{ onPanelEnterWindow(self, event): XXX - def onPanelEnterWindow(self, event): - self.parentFrame.SetFocus() - # }}} - # {{{ onPanelInput(self, event): XXX - def onPanelInput(self, event): - eventDc = self.canvasBackend.getDeviceContext(self) - eventType = event.GetEventType() - self._canvasDirty = self._canvasDirtyCursor = False - tool = self.canvasInterface.canvasTool - if eventType == wx.wxEVT_CHAR: - mapPoint = self.brushPos - doSkip = tool.onKeyboardEvent( \ - event, mapPoint, self.brushColours, self.brushSize, \ - chr(event.GetUnicodeKey()), self._dispatchPatch, eventDc) - if doSkip: - event.Skip(); return; + # {{{ dispatchPatch(self, isCursor, patch, commitUndo=True): XXX + def dispatchPatch(self, isCursor, patch, commitUndo=True): + patchDeltaCell = self.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell]; + if isCursor: + self.journal.pushCursor(patchDelta) else: - mapPoint = self.canvasBackend.xlateEventPoint(event, eventDc) - if mapPoint[0] >= self.canvasSize[0] \ - or mapPoint[1] >= self.canvasSize[1]: - return - self.brushPos = mapPoint - tool.onMouseEvent( \ - event, mapPoint, self.brushColours, self.brushSize, \ - event.Dragging(), event.LeftIsDown(), event.RightIsDown(), \ - self._dispatchPatch, eventDc) - if self._canvasDirty: - self.parentFrame.onCanvasUpdate(cellPos=self.brushPos, undoLevel=self.canvasJournal.patchesUndoLevel) - if eventType == wx.wxEVT_MOTION: - self.parentFrame.onCanvasUpdate(cellPos=mapPoint) - # }}} - # {{{ onPanelLeaveWindow(self, event): XXX - def onPanelLeaveWindow(self, event): - eventDc = self.canvasBackend.getDeviceContext(self) - self.canvasBackend.drawCursorMaskWithJournal(self.canvasJournal, eventDc) - # }}} - # {{{ onPanelPaint(self, event): XXX - def onPanelPaint(self, event): - self.canvasBackend.onPanelPaintEvent(event, self) - # }}} - # {{{ onStoreUpdate(self, newCanvasSize, newCanvas=None): XXX - def onStoreUpdate(self, newCanvasSize, newCanvas=None): - self.resize(newCanvasSize=newCanvasSize, commitUndo=False) - eventDc = self.canvasBackend.getDeviceContext(self) - for numRow in range(self.canvasSize[1]): - for numCol in range(self.canvasSize[0]): - if newCanvas != None \ - and numRow < len(newCanvas) \ - and numCol < len(newCanvas[numRow]): - self._commitPatch([numCol, numRow, *newCanvas[numRow][numCol]]) - self.canvasBackend.drawPatch(eventDc, [numCol, numRow, *self.canvasMap[numRow][numCol]]) - wx.SafeYield() - # }}} - # {{{ resize(self, newCanvasSize, commitUndo=True): XXX - def resize(self, newCanvasSize, commitUndo=True): - if newCanvasSize != self.canvasSize: - self._canvasDirty, self._canvasDirtyCursor = False, False - if self.canvasMap == None: - self.canvasMap, oldCanvasSize = [], [0, 0] - else: - oldCanvasSize = self.canvasSize - deltaCanvasSize = [b - a for a, b in zip(oldCanvasSize, newCanvasSize)] - - newWinSize = [a * b for a, b in zip(newCanvasSize, self.canvasBackend.cellSize)] - self.SetMinSize(newWinSize); self.SetSize(wx.DefaultCoord, wx.DefaultCoord, *newWinSize); - curWindow = self - while curWindow != None: - curWindow.Layout(); curWindow = curWindow.GetParent(); - - self.canvasBackend.resize(newCanvasSize, self.canvasBackend.cellSize) - eventDc = self.canvasBackend.getDeviceContext(self) - self.canvasJournal.resetCursor() - if commitUndo: - undoPatches, redoPatches = ["resize", *oldCanvasSize], ["resize", *newCanvasSize] - if not self._canvasDirty: - self.canvasJournal.pushDeltas([], []); self._canvasDirty = True; - self.canvasJournal.updateCurrentDeltas(redoPatches, undoPatches) - - if deltaCanvasSize[0] < 0: - for numRow in range(oldCanvasSize[1]): - if commitUndo: - for numCol in range((oldCanvasSize[0] + deltaCanvasSize[0]), oldCanvasSize[0]): - if not self._canvasDirty: - self.canvasJournal.pushDeltas([], []); self._canvasDirty = True; - self.canvasJournal.updateCurrentDeltas([numCol, numRow, 1, 1, 0, " "], [numCol, numRow, *self.canvasMap[numRow][numCol]]) - del self.canvasMap[numRow][-1:(deltaCanvasSize[0]-1):-1] - else: - for numRow in range(oldCanvasSize[1]): - self.canvasMap[numRow].extend([[1, 1, 0, " "]] * deltaCanvasSize[0]) - for numNewCol in range(oldCanvasSize[0], newCanvasSize[0]): - self._dispatchPatch(eventDc, False, [numNewCol, numRow, 1, 1, 0, " "], commitUndo) - if deltaCanvasSize[1] < 0: - if commitUndo: - for numRow in range((oldCanvasSize[1] + deltaCanvasSize[1]), oldCanvasSize[1]): - for numCol in range(oldCanvasSize[0] + deltaCanvasSize[0]): - if not self._canvasDirty: - self.canvasJournal.pushDeltas([], []); self._canvasDirty = True; - self.canvasJournal.updateCurrentDeltas([numCol, numRow, 1, 1, 0, " "], [numCol, numRow, *self.canvasMap[numRow][numCol]]) - del self.canvasMap[-1:(deltaCanvasSize[1]-1):-1] - else: - for numNewRow in range(oldCanvasSize[1], newCanvasSize[1]): - self.canvasMap.extend([[[1, 1, 0, " "]] * newCanvasSize[0]]) - for numNewCol in range(newCanvasSize[0]): - self._dispatchPatch(eventDc, False, [numNewCol, numNewRow, 1, 1, 0, " "], commitUndo) - - self.canvasSize = newCanvasSize; wx.SafeYield(); self.parentFrame.onCanvasUpdate(size=newCanvasSize, undoLevel=self.canvasJournal.patchesUndoLevel) + if not self.dirty: + self.journal.pushDeltas([], []); self.dirty = True; + self.journal.updateCurrentDeltas(patch, patchDelta) + self._commitPatch(patch) # }}} - - # {{{ __del__(self): destructor method - def __del__(self): - if self.canvasMap != None: - self.canvasMap.clear(); self.canvasMap = None; + # {{{ resize(self, newSize, commitUndo=True): XXX + def resize(self, newSize, commitUndo=True): + if newSize != self.size: + self.dirty = False + if self.map == None: + self.map, oldSize = [], [0, 0] + else: + oldSize = self.size + deltaSize = [b - a for a, b in zip(oldSize, newSize)] + self.journal.resetCursor() + if commitUndo: + undoPatches, redoPatches = ["resize", *oldSize], ["resize", *newSize] + if not self.dirty: + self.journal.pushDeltas([], []); self.dirty = True; + self.journal.updateCurrentDeltas(redoPatches, undoPatches) + if deltaSize[0] < 0: + for numRow in range(oldSize[1]): + if commitUndo: + for numCol in range((oldSize[0] + deltaSize[0]), oldSize[0]): + if not self.dirty: + self.journal.pushDeltas([], []); self.dirty = True; + self.journal.updateCurrentDeltas(None, [numCol, numRow, *self.map[numRow][numCol]]) + del self.map[numRow][-1:(deltaSize[0]-1):-1] + else: + for numRow in range(oldSize[1]): + self.map[numRow].extend([[1, 1, 0, " "]] * deltaSize[0]) + for numNewCol in range(oldSize[0], newSize[0]): + self.dispatchPatch(False, [numNewCol, numRow, 1, 1, 0, " "], commitUndo) + if deltaSize[1] < 0: + if commitUndo: + for numRow in range((oldSize[1] + deltaSize[1]), oldSize[1]): + for numCol in range(oldSize[0] + deltaSize[0]): + if not self.dirty: + self.journal.pushDeltas([], []); self.dirty = True; + self.journal.updateCurrentDeltas(None, [numCol, numRow, *self.map[numRow][numCol]]) + del self.map[-1:(deltaSize[1]-1):-1] + else: + for numNewRow in range(oldSize[1], newSize[1]): + self.map.extend([[[1, 1, 0, " "]] * newSize[0]]) + for numNewCol in range(newSize[0]): + self.dispatchPatch(False, [numNewCol, numNewRow, 1, 1, 0, " "], commitUndo) + self.size = newSize + return True + else: + return False + # }}} + # {{{ update(self, newSize, newCanvas=None): XXX + def update(self, newSize, newCanvas=None): + for numRow in range(self.size[1]): + for numCol in range(self.size[0]): + if (newCanvas != None) \ + and (numRow < len(newCanvas)) \ + and (numCol < len(newCanvas[numRow])): + self._commitPatch([numCol, numRow, *newCanvas[numRow][numCol]]) # }}} # - # __init__(self, parent, parentFrame, canvasInterface, defaultCanvasPos, defaultCanvasSize, defaultCellSize): initialisation method - def __init__(self, parent, parentFrame, canvasInterface, defaultCanvasPos, defaultCanvasSize, defaultCellSize): - super().__init__(parent, pos=defaultCanvasPos, size=[w * h for w, h in zip(defaultCanvasSize, defaultCellSize)]) - - self.brushColours, self.brushPos, self.brushSize = [4, 1], [0, 0], [1, 1] - self._canvasDirty, self._canvasDirtyCursor = False, False - self.canvasMap, self.canvasPos, self.canvasSize, = None, defaultCanvasPos, defaultCanvasSize - self.defaultCanvasPos, self.defaultCanvasSize, self.defaultCellSize = defaultCanvasPos, defaultCanvasSize, defaultCellSize - self.parentFrame = parentFrame - self.parentFrame.onCanvasUpdate(brushSize=self.brushSize, colours=self.brushColours) - - self.canvasBackend = CanvasBackend(defaultCanvasSize, defaultCellSize) - self.canvasJournal = CanvasJournal() - self.canvasExportStore = CanvasExportStore() - self.canvasImportStore = CanvasImportStore() - self.canvasInterface = canvasInterface(self, parentFrame) - - # Bind event handlers - self.Bind(wx.EVT_CLOSE, self.onPanelClose) - self.Bind(wx.EVT_ENTER_WINDOW, self.onPanelEnterWindow) - self.Bind(wx.EVT_LEAVE_WINDOW, self.onPanelLeaveWindow) - self.parentFrame.Bind(wx.EVT_CHAR, self.onPanelInput) - for eventType in (wx.EVT_LEFT_DOWN, wx.EVT_MOTION, wx.EVT_RIGHT_DOWN): - self.Bind(eventType, self.onPanelInput) - self.Bind(wx.EVT_PAINT, self.onPanelPaint) + # __init__(self, size): initialisation method + def __init__(self, size): + self.dirty, self.dirtyCursor, self.map, self.size = False, False, None, size + self.exportStore, self.importStore, self.journal = CanvasExportStore(), CanvasImportStore(), CanvasJournal() # vim:expandtab foldmethod=marker sw=4 ts=4 tw=0 diff --git a/libcanvas/CanvasColours.py b/libcanvas/CanvasColours.py index 00fb890..35e668b 100644 --- a/libcanvas/CanvasColours.py +++ b/libcanvas/CanvasColours.py @@ -104,26 +104,6 @@ ColourMapNormal = [ [187, 187, 187], # Light Grey ] # }}} -# {{{ Colours: mIRC colour number to RGBA map given none of ^[BFV_] (bold, italic, reverse, underline], -Colours = [ - [255, 255, 255, 255, "White"], - [0, 0, 0, 255, "Black"], - [0, 0, 187, 255, "Blue"], - [0, 187, 0, 255, "Green"], - [255, 85, 85, 255, "Light Red"], - [187, 0, 0, 255, "Red"], - [187, 0, 187, 255, "Purple"], - [187, 187, 0, 255, "Yellow"], - [255, 255, 85, 255, "Light Yellow"], - [85, 255, 85, 255, "Light Green"], - [0, 187, 187, 255, "Cyan"], - [85, 255, 255, 255, "Light Cyan"], - [85, 85, 255, 255, "Light Blue"], - [255, 85, 255, 255, "Pink"], - [85, 85, 85, 255, "Grey"], - [187, 187, 187, 255, "Light Grey"], -]; -# }}} # {{{ MiRCARTToAnsiColours: XXX MiRCARTToAnsiColours = [ 97, # Bright White diff --git a/libcanvas/CanvasImportStore.py b/libcanvas/CanvasImportStore.py index 0e2a5f3..847257b 100644 --- a/libcanvas/CanvasImportStore.py +++ b/libcanvas/CanvasImportStore.py @@ -6,7 +6,7 @@ # from CanvasColours import AnsiBgToMiRCARTColours, AnsiFgToMiRCARTColours, AnsiFgBoldToMiRCARTColours -import os, re, struct, sys +import io, os, re, struct, sys class CanvasImportStore(): """XXX""" @@ -23,17 +23,17 @@ class CanvasImportStore(): return cellState & ~bit if cellState & bit else cellState | bit # }}} - # {{{ importAnsiBuffer(self, inFile, encoding="cp437", width=None): XXX - def importAnsiBuffer(self, inFile, encoding="cp437", width=None): + # {{{ importAnsiBuffer(self, inBuffer, encoding="cp437", width=None): XXX + def importAnsiBuffer(self, inBuffer, encoding="cp437", width=None): curBg, curBgAnsi, curBoldAnsi, curFg, curFgAnsi = 1, 30, False, 15, 37 done, outMap, outMaxCols = False, [[]], 0 - inFileData = inFile.read().decode(encoding) - inFileChar, inFileCharMax = 0, len(inFileData) + inBufferData = inBuffer.decode(encoding) + inBufferChar, inBufferCharMax = 0, len(inBufferData) while True: - if inFileChar >= inFileCharMax: + if inBufferChar >= inBufferCharMax: break else: - m = re.match("\x1b\[((?:\d{1,3};?)+m|\d+C)", inFileData[inFileChar:]) + m = re.match("\x1b\[((?:\d{1,3};?)+m|\d+[ABCDEFG])", inBufferData[inBufferChar:]) if m: if m[1][-1] == "C": outMap[-1] += [[curFg, curBg, self._CellState.CS_NONE, " "]] * int(m[1][:-1]) @@ -48,25 +48,26 @@ class CanvasImportStore(): curBoldAnsi, newFg = False, AnsiFgToMiRCARTColours[curFgAnsi] elif ansiCode == 7: curBgAnsi, curFgAnsi, newBg, newFg = curFgAnsi, curBgAnsi, curFg, curBg - elif ansiCode in AnsiBgToMiRCARTColours: + elif (not curBoldAnsi) and (ansiCode in AnsiBgToMiRCARTColours): curBgAnsi, newBg = ansiCode, AnsiBgToMiRCARTColours[ansiCode] - elif ansiCode in AnsiFgBoldToMiRCARTColours: + elif curBoldAnsi and (ansiCode in AnsiFgBoldToMiRCARTColours): curFgAnsi, newFg = ansiCode, AnsiFgBoldToMiRCARTColours[ansiCode] elif ansiCode in AnsiFgToMiRCARTColours: newFg = AnsiFgBoldToMiRCARTColours[ansiCode] if curBoldAnsi else AnsiFgToMiRCARTColours[ansiCode] curFgAnsi = ansiCode curBg = newBg if newBg != -1 else curBg; curFg = newFg if newFg != -1 else curFg; - inFileChar += len(m[0]) - elif inFileData[inFileChar:inFileChar + 2] == "\r\n": - done = True; inFileChar += 2; - elif inFileData[inFileChar] in set("\r\n"): - done = True; inFileChar += 1; + inBufferChar += len(m[0]) + elif inBufferData[inBufferChar:inBufferChar + 2] == "\r\n": + done = True; inBufferChar += 2; + elif inBufferData[inBufferChar] in set("\r\n"): + done = True; inBufferChar += 1; else: - outMap[-1].append([curFg, curBg, self._CellState.CS_NONE, inFileData[inFileChar]]) - inFileChar += 1 + outMap[-1].append([curFg, curBg, self._CellState.CS_NONE, inBufferData[inBufferChar]]) + inBufferChar += 1 if done or (width == len(outMap[-1])): done, outMaxCols, = False, max(outMaxCols, len(outMap[-1])); outMap.append([]); - if len(outMap[0]): + if (len(outMap) > 1) \ + or ((len(outMap) == 1) and len(outMap[0])): for numRow in range(len(outMap)): for numCol in range(len(outMap[numRow]), outMaxCols): outMap[numRow].append([curFg, curBg, self._CellState.CS_NONE, " "]) @@ -77,16 +78,17 @@ class CanvasImportStore(): # }}} # {{{ importAnsiFile(self, inPathName, encoding="cp437"): XXX def importAnsiFile(self, inPathName, encoding="cp437"): - return self.importAnsiBuffer(open(inPathName, "rb"), encoding) + return self.importAnsiBuffer(open(inPathName, "rb").read(), encoding) # }}} - # {{{ importSauceFile(self, inPathName): XXX - def importSauceFile(self, inPathName): + # {{{ importSauceFile(self, inPathName, encoding="cp437"): XXX + def importSauceFile(self, inPathName, encoding="cp437"): with open(inPathName, "rb") as inFile: - inFileStat = os.stat(inPathName); inFile.seek(inFileStat.st_size - 128, 0); inFile.seek(94); + inFileStat = os.stat(inPathName) + inFile.seek(inFileStat.st_size - 128, os.SEEK_SET); inFile.seek(94, os.SEEK_CUR); if inFile.read(2) == b'\x01\x01': width = struct.unpack("H", inFile.read(2))[0] inFile.seek(0, 0); inFileData = inFile.read(inFileStat.st_size - 128); - return self.importAnsiFileBuffer(io.StringIO(inFileData), width) + return self.importAnsiBuffer(inFileData, encoding, width) else: return (False, "only character based ANSi SAUCE files are supported") # }}} @@ -124,7 +126,8 @@ class CanvasImportStore(): else: outMap[-1].append([*inCurColours, inCellState, inChar]); inCurCol += 1; inLine, outMaxCols = inFile.readline(), max(outMaxCols, len(outMap[-1])) - if len(outMap[0]): + if (len(outMap) > 1) \ + or ((len(outMap) == 1) and len(outMap[0])): self.inSize, self.outMap = [outMaxCols, len(outMap)], outMap return (True, None) else: diff --git a/libgui/GuiCanvasColours.py b/libgui/GuiCanvasColours.py new file mode 100644 index 0000000..4e6d48e --- /dev/null +++ b/libgui/GuiCanvasColours.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# +# GuiCanvasColours.py -- XXX +# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz +# + +# +# Colours: mIRC colour number to RGBA map given none of ^[BFV_] (bold, italic, reverse, underline) +# +Colours = [ + [255, 255, 255, 255, "White"], + [0, 0, 0, 255, "Black"], + [0, 0, 187, 255, "Blue"], + [0, 187, 0, 255, "Green"], + [255, 85, 85, 255, "Light Red"], + [187, 0, 0, 255, "Red"], + [187, 0, 187, 255, "Purple"], + [187, 187, 0, 255, "Yellow"], + [255, 255, 85, 255, "Light Yellow"], + [85, 255, 85, 255, "Light Green"], + [0, 187, 187, 255, "Cyan"], + [85, 255, 255, 255, "Light Cyan"], + [85, 85, 255, 255, "Light Blue"], + [255, 85, 255, 255, "Pink"], + [85, 85, 85, 255, "Grey"], + [187, 187, 187, 255, "Light Grey"], +]; + +# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120 diff --git a/libgui/GuiCanvasInterface.py b/libgui/GuiCanvasInterface.py index 263638f..f644866 100644 --- a/libgui/GuiCanvasInterface.py +++ b/libgui/GuiCanvasInterface.py @@ -15,7 +15,7 @@ from ToolText import ToolText from glob import glob from GuiCanvasInterfaceAbout import GuiCanvasInterfaceAbout from ImgurApiKey import ImgurApiKey -import io, os, random, wx, wx.adv +import io, os, random, sys, wx, wx.adv class GuiCanvasInterface(): """XXX""" @@ -42,7 +42,7 @@ class GuiCanvasInterface(): self.parentCanvas.brushColours[0] = numColour elif event.GetEventType() == wx.wxEVT_TOOL_RCLICKED: self.parentCanvas.brushColours[1] = numColour - self.parentFrame.onCanvasUpdate(colours=self.parentCanvas.brushColours) + self.parentFrame.update(colours=self.parentCanvas.brushColours) # }}} # {{{ canvasCopy(self, event): XXX def canvasCopy(self, event): @@ -82,10 +82,10 @@ class GuiCanvasInterface(): if newCanvasSize == None: newCanvasSize = list(self.parentCanvas.defaultCanvasSize) newMap = [[[1, 1, 0, " "] for x in range(newCanvasSize[0])] for y in range(newCanvasSize[1])] - self.parentCanvas.onStoreUpdate(newCanvasSize, newMap) + self.parentCanvas.update(newCanvasSize, False, newMap) self.canvasPathName = None self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) - self.parentFrame.onCanvasUpdate(pathName="", undoLevel=-1) + self.parentFrame.update(pathName="", undoLevel=-1) # }}} # {{{ canvasOpen(self, event): XXX def canvasOpen(self, event): @@ -103,12 +103,11 @@ class GuiCanvasInterface(): else: self.canvasPathName = dialog.GetPath() self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) - import pdb; pdb.set_trace() - rc, error = self.parentCanvas.canvasImportStore.importTextFile(self.canvasPathName) + rc, error = self.parentCanvas.canvas.importStore.importTextFile(self.canvasPathName) if rc: - self.parentCanvas.onStoreUpdate(self.parentCanvas.canvasImportStore.inSize, self.parentCanvas.canvasImportStore.outMap) + self.parentCanvas.update(self.parentCanvas.canvas.importStore.inSize, False, self.parentCanvas.canvas.importStore.outMap) self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) - self.parentFrame.onCanvasUpdate(pathName=self.canvasPathName, undoLevel=-1) + self.parentFrame.update(pathName=self.canvasPathName, undoLevel=-1) return True else: print("error: {}".format(error), file=sys.stderr) @@ -120,7 +119,8 @@ class GuiCanvasInterface(): # }}} # {{{ canvasRedo(self, event): XXX def canvasRedo(self, event): - self.parentCanvas._dispatchDeltaPatches(self.parentCanvas.canvasJournal.popRedo()) + self.parentCanvas.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popRedo()) + self.parentFrame.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel) # }}} # {{{ canvasSave(self, event): XXX def canvasSave(self, event): @@ -130,8 +130,8 @@ class GuiCanvasInterface(): try: with open(self.canvasPathName, "w", encoding="utf-8") as outFile: self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) - self.parentCanvas.canvasExportStore.exportTextFile( \ - self.parentCanvas.canvasMap, self.parentCanvas.canvasSize, outFile) + self.parentCanvas.canvas.exportStore.exportTextFile( \ + self.parentCanvas.canvas.map, self.parentCanvas.canvas.size, outFile) self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) return True except IOError as error: @@ -148,14 +148,15 @@ class GuiCanvasInterface(): # }}} # {{{ canvasUndo(self, event): XXX def canvasUndo(self, event): - self.parentCanvas._dispatchDeltaPatches(self.parentCanvas.canvasJournal.popUndo()) + self.parentCanvas.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popUndo()) + self.parentFrame.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel) # }}} # {{{ canvasDecrBrushHeight(self, event): XXX def canvasDecrBrushHeight(self, event): if self.parentCanvas.brushSize[1] > 1: self.parentCanvas.brushSize[1] -= 1 - self.parentFrame.onCanvasUpdate(brushSize=self.parentCanvas.brushSize) + self.parentFrame.update(brushSize=self.parentCanvas.brushSize) # }}} # {{{ canvasDecrBrushHeightWidth(self, event): XXX def canvasDecrBrushHeightWidth(self, event): @@ -166,12 +167,12 @@ class GuiCanvasInterface(): def canvasDecrBrushWidth(self, event): if self.parentCanvas.brushSize[0] > 1: self.parentCanvas.brushSize[0] -= 1 - self.parentFrame.onCanvasUpdate(brushSize=self.parentCanvas.brushSize) + self.parentFrame.update(brushSize=self.parentCanvas.brushSize) # }}} # {{{ canvasDecrCanvasHeight(self, event): XXX def canvasDecrCanvasHeight(self, event): - if self.parentCanvas.canvasSize[1] > 1: - self.parentCanvas.resize([self.parentCanvas.canvasSize[0], self.parentCanvas.canvasSize[1] - 1]) + if self.parentCanvas.canvas.size[1] > 1: + self.parentCanvas.resize([self.parentCanvas.canvas.size[0], self.parentCanvas.canvas.size[1] - 1]) # }}} # {{{ canvasDecrCanvasHeightWidth(self, event): XXX def canvasDecrCanvasHeightWidth(self, event): @@ -180,13 +181,13 @@ class GuiCanvasInterface(): # }}} # {{{ canvasDecrCanvasWidth(self, event): XXX def canvasDecrCanvasWidth(self, event): - if self.parentCanvas.canvasSize[0] > 1: - self.parentCanvas.resize([self.parentCanvas.canvasSize[0] - 1, self.parentCanvas.canvasSize[1]]) + if self.parentCanvas.canvas.size[0] > 1: + self.parentCanvas.resize([self.parentCanvas.canvas.size[0] - 1, self.parentCanvas.canvas.size[1]]) # }}} # {{{ canvasIncrBrushHeight(self, event): XXX def canvasIncrBrushHeight(self, event): self.parentCanvas.brushSize[1] += 1 - self.parentFrame.onCanvasUpdate(brushSize=self.parentCanvas.brushSize) + self.parentFrame.update(brushSize=self.parentCanvas.brushSize) # }}} # {{{ canvasIncrBrushHeightWidth(self, event): XXX def canvasIncrBrushHeightWidth(self, event): @@ -196,11 +197,11 @@ class GuiCanvasInterface(): # {{{ canvasIncrBrushWidth(self, event): XXX def canvasIncrBrushWidth(self, event): self.parentCanvas.brushSize[0] += 1 - self.parentFrame.onCanvasUpdate(brushSize=self.parentCanvas.brushSize) + self.parentFrame.update(brushSize=self.parentCanvas.brushSize) # }}} # {{{ canvasIncrCanvasHeight(self, event): XXX def canvasIncrCanvasHeight(self, event): - self.parentCanvas.resize([self.parentCanvas.canvasSize[0], self.parentCanvas.canvasSize[1] + 1]) + self.parentCanvas.resize([self.parentCanvas.canvas.size[0], self.parentCanvas.canvas.size[1] + 1]) # }}} # {{{ canvasIncrCanvasHeightWidth(self, event): XXX def canvasIncrCanvasHeightWidth(self, event): @@ -209,7 +210,7 @@ class GuiCanvasInterface(): # }}} # {{{ canvasIncrCanvasWidth(self, event): XXX def canvasIncrCanvasWidth(self, event): - self.parentCanvas.resize([self.parentCanvas.canvasSize[0] + 1, self.parentCanvas.canvasSize[1]]) + self.parentCanvas.resize([self.parentCanvas.canvas.size[0] + 1, self.parentCanvas.canvas.size[1]]) # }}} # {{{ canvasExportAsAnsi(self, event): XXX @@ -221,7 +222,7 @@ class GuiCanvasInterface(): outPathName = dialog.GetPath() self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) with open(outPathName, "w", encoding="utf-8") as outFile: - self.parentCanvas.canvasExportStore.exportAnsiFile(self.parentCanvas.canvasMap, self.parentCanvas.canvasSize, outFile) + self.parentCanvas.canvas.exportStore.exportAnsiFile(self.parentCanvas.canvas.map, self.parentCanvas.canvas.size, outFile) self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) return True # }}} @@ -233,7 +234,7 @@ class GuiCanvasInterface(): else: outPathName = dialog.GetPath() self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) - self.parentCanvas.canvasExportStore.exportBitmapToPngFile( \ + self.parentCanvas.canvas.exportStore.exportBitmapToPngFile( \ self.parentCanvas.canvasBackend.canvasBitmap, outPathName, wx.BITMAP_TYPE_PNG) self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) return True @@ -241,7 +242,7 @@ class GuiCanvasInterface(): # {{{ canvasExportImgur(self, event): XXX def canvasExportImgur(self, event): self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) - rc, status, result = self.parentCanvas.canvasExportStore.exportBitmapToImgur( \ + rc, status, result = self.parentCanvas.canvas.exportStore.exportBitmapToImgur( \ self.imgurApiKey, self.parentCanvas.canvasBackend.canvasBitmap, "", "", wx.BITMAP_TYPE_PNG) self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) if rc: @@ -254,11 +255,7 @@ class GuiCanvasInterface(): # {{{ canvasExportPastebin(self, event): XXX def canvasExportPastebin(self, event): self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) - pasteStatus, pasteResult = \ - self.parentCanvas.canvasExportStore.exportPastebin( \ - "", \ - self.parentCanvas.canvasMap, \ - self.parentCanvas.canvasSize) + pasteStatus, pasteResult = self.parentCanvas.canvas.exportStore.exportPastebin("", self.parentCanvas.canvas.map, self.parentCanvas.canvas.size) self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) if pasteStatus: if not wx.TheClipboard.IsOpened(): @@ -272,7 +269,7 @@ class GuiCanvasInterface(): # {{{ canvasExportToClipboard(self, event): XXX def canvasExportToClipboard(self, event): self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) - rc, outBuffer = self.parentCanvas.canvasExportStore.exportTextBuffer(self.parentCanvas.canvasMap, self.parentCanvas.canvasSize) + rc, outBuffer = self.parentCanvas.canvas.exportStore.exportTextBuffer(self.parentCanvas.canvas.map, self.parentCanvas.canvas.size) if rc and wx.TheClipboard.Open(): wx.TheClipboard.SetData(wx.TextDataObject(outBuffer)) wx.TheClipboard.Close() @@ -295,12 +292,12 @@ class GuiCanvasInterface(): else: self.canvasPathName = dialog.GetPath() self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) - rc, error = self.parentCanvas.canvasImportStore.importAnsiFile(self.canvasPathName) + rc, error = self.parentCanvas.canvas.importStore.importAnsiFile(self.canvasPathName) if rc: - self.parentCanvas.onStoreUpdate(self.parentCanvas.canvasImportStore.inSize, self.parentCanvas.canvasImportStore.outMap) + self.parentCanvas.update(self.parentCanvas.canvas.importStore.inSize, False, self.parentCanvas.canvas.importStore.outMap) self.canvasPathName = "(Imported)" self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) - self.parentFrame.onCanvasUpdate(pathName="(Imported)", undoLevel=-1) + self.parentFrame.update(pathName="(Imported)", undoLevel=-1) return True else: print("error: {}".format(error), file=sys.stderr) @@ -322,12 +319,12 @@ class GuiCanvasInterface(): elif saveChanges == wx.ID_YES: self.canvasSave(event) self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) - rc, error = self.parentCanvas.canvasImportStore.importTextBuffer(io.StringIO(inBuffer.GetText())) + rc, error = self.parentCanvas.canvas.importStore.importTextBuffer(io.StringIO(inBuffer.GetText())) if rc: - self.parentCanvas.onStoreUpdate(self.parentCanvas.canvasImportStore.inSize, self.parentCanvas.canvasImportStore.outMap) + self.parentCanvas.update(self.parentCanvas.canvas.importStore.inSize, False, self.parentCanvas.canvas.importStore.outMap) self.canvasPathName = "(Clipboard)" self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) - self.parentFrame.onCanvasUpdate(pathName="(Clipboard)", undoLevel=-1) + self.parentFrame.update(pathName="(Clipboard)", undoLevel=-1) else: print("error: {}".format(error), file=sys.stderr) wx.TheClipboard.Close() @@ -351,12 +348,12 @@ class GuiCanvasInterface(): else: self.canvasPathName = dialog.GetPath() self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) - rc, error = self.parentCanvas.canvasImportStore.importSauceFile(self.canvasPathName) + rc, error = self.parentCanvas.canvas.importStore.importSauceFile(self.canvasPathName) if rc: - self.parentCanvas.onStoreUpdate(self.parentCanvas.canvasImportStore.inSize, self.parentCanvas.canvasImportStore.outMap) + self.parentCanvas.update(self.parentCanvas.canvas.importStore.inSize, False, self.parentCanvas.canvas.importStore.outMap) self.canvasPathName = "(Imported)" self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) - self.parentFrame.onCanvasUpdate(pathName="(Imported)", undoLevel=-1) + self.parentFrame.update(pathName="(Imported)", undoLevel=-1) return True else: print("error: {}".format(error), file=sys.stderr) @@ -369,7 +366,7 @@ class GuiCanvasInterface(): self.parentFrame.menuItemsById[self.parentFrame.CID_CIRCLE[0]].Check(True) toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_CIRCLE[0]].GetToolBar() toolBar.ToggleTool(self.parentFrame.CID_CIRCLE[0], True) - self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name) + self.parentFrame.update(toolName=self.canvasTool.name) # }}} # {{{ canvasToolFill(self, event): XXX def canvasToolFill(self, event): @@ -377,7 +374,7 @@ class GuiCanvasInterface(): self.parentFrame.menuItemsById[self.parentFrame.CID_FILL[0]].Check(True) toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_FILL[0]].GetToolBar() toolBar.ToggleTool(self.parentFrame.CID_FILL[0], True) - self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name) + self.parentFrame.update(toolName=self.canvasTool.name) # }}} # {{{ canvasToolLine(self, event): XXX def canvasToolLine(self, event): @@ -385,7 +382,7 @@ class GuiCanvasInterface(): self.parentFrame.menuItemsById[self.parentFrame.CID_LINE[0]].Check(True) toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_LINE[0]].GetToolBar() toolBar.ToggleTool(self.parentFrame.CID_LINE[0], True) - self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name) + self.parentFrame.update(toolName=self.canvasTool.name) # }}} # {{{ canvasToolSelectClone(self, event): XXX def canvasToolSelectClone(self, event): @@ -393,7 +390,7 @@ class GuiCanvasInterface(): self.parentFrame.menuItemsById[self.parentFrame.CID_CLONE_SELECT[0]].Check(True) toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_CLONE_SELECT[0]].GetToolBar() toolBar.ToggleTool(self.parentFrame.CID_CLONE_SELECT[0], True) - self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name) + self.parentFrame.update(toolName=self.canvasTool.name) # }}} # {{{ canvasToolSelectMove(self, event): XXX def canvasToolSelectMove(self, event): @@ -401,7 +398,7 @@ class GuiCanvasInterface(): self.parentFrame.menuItemsById[self.parentFrame.CID_MOVE_SELECT[0]].Check(True) toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_MOVE_SELECT[0]].GetToolBar() toolBar.ToggleTool(self.parentFrame.CID_MOVE_SELECT[0], True) - self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name) + self.parentFrame.update(toolName=self.canvasTool.name) # }}} # {{{ canvasToolRect(self, event): XXX def canvasToolRect(self, event): @@ -409,7 +406,7 @@ class GuiCanvasInterface(): self.parentFrame.menuItemsById[self.parentFrame.CID_RECT[0]].Check(True) toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_RECT[0]].GetToolBar() toolBar.ToggleTool(self.parentFrame.CID_RECT[0], True) - self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name) + self.parentFrame.update(toolName=self.canvasTool.name) # }}} # {{{ canvasToolText(self, event): XXX def canvasToolText(self, event): @@ -417,7 +414,7 @@ class GuiCanvasInterface(): self.parentFrame.menuItemsById[self.parentFrame.CID_TEXT[0]].Check(True) toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_TEXT[0]].GetToolBar() toolBar.ToggleTool(self.parentFrame.CID_TEXT[0], True) - self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name) + self.parentFrame.update(toolName=self.canvasTool.name) # }}} # diff --git a/libgui/GuiCanvasPanel.py b/libgui/GuiCanvasPanel.py new file mode 100644 index 0000000..36117fe --- /dev/null +++ b/libgui/GuiCanvasPanel.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +# +# GuiCanvasPanel.py -- XXX +# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz +# + +import wx + +class GuiCanvasPanel(wx.Panel): + """XXX""" + + # {{{ _drawPatch(self, eventDc, isCursor, patch): XXX + def _drawPatch(self, eventDc, isCursor, patch): + if not self.canvas.dirtyCursor: + self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc) + self.canvas.dirtyCursor = True + if self.backend.drawPatch(eventDc, patch) \ + and isCursor: + patchDeltaCell = self.canvas.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell]; + self.canvas.journal.pushCursor(patchDelta) + # }}} + + # {{{ dispatchDeltaPatches(self, deltaPatches): XXX + def dispatchDeltaPatches(self, deltaPatches): + eventDc = self.backend.getDeviceContext(self) + for patch in deltaPatches: + if patch == None: + continue + elif patch[0] == "resize": + del eventDc; self.resize(patch[1:], False); eventDc = self.backend.getDeviceContext(self); + else: + self.canvas._commitPatch(patch); self.backend.drawPatch(eventDc, patch); + # }}} + # {{{ dispatchPatch(self, eventDc, isCursor, patch): XXX + def dispatchPatch(self, eventDc, isCursor, patch): + self.canvas.dispatchPatch(isCursor, patch, False if isCursor else True) + self._drawPatch(eventDc, isCursor, patch) + # }}} + # {{{ resize(self, newSize, commitUndo=True): XXX + def resize(self, newSize, commitUndo=True): + 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): + newWinSize = [a * b for a, b in zip(newSize, self.backend.cellSize)] + self.SetMinSize(newWinSize); self.SetSize(wx.DefaultCoord, wx.DefaultCoord, *newWinSize); + curWindow = self + while curWindow != None: + curWindow.Layout(); curWindow = curWindow.GetParent(); + self.backend.resize(newSize, self.backend.cellSize) + eventDc = self.backend.getDeviceContext(self) + 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, " "]) + del eventDc; wx.SafeYield(); + self.parentFrame.update(size=newSize, undoLevel=self.canvas.journal.patchesUndoLevel) + # }}} + # {{{ update(self, newSize, commitUndo=True, newCanvas=None): XXX + def update(self, newSize, commitUndo=True, newCanvas=None): + self.resize(newSize, commitUndo) + self.canvas.update(newSize, newCanvas) + eventDc = self.backend.getDeviceContext(self) + for numRow in range(newSize[1]): + for numCol in range(newSize[0]): + self.backend.drawPatch(eventDc, [numCol, numRow, *self.canvas.map[numRow][numCol]]) + wx.SafeYield() + # }}} + + # {{{ onPanelClose(self, event): XXX + def onPanelClose(self, event): + self.Destroy() + # }}} + # {{{ onPanelEnterWindow(self, event): XXX + def onPanelEnterWindow(self, event): + self.parentFrame.SetFocus() + # }}} + # {{{ onPanelInput(self, event): XXX + def onPanelInput(self, event): + self.canvas.dirty, self.canvas.dirtyCursor = False, False + eventDc, eventType, tool = self.backend.getDeviceContext(self), event.GetEventType(), self.interface.canvasTool + if eventType == wx.wxEVT_CHAR: + mapPoint = self.brushPos + doSkip = tool.onKeyboardEvent(event, mapPoint, self.brushColours, self.brushSize, chr(event.GetUnicodeKey()), self.dispatchPatch, eventDc) + if doSkip: + event.Skip(); return; + else: + mapPoint = self.backend.xlateEventPoint(event, eventDc) + if mapPoint[0] >= self.canvas.size[0] \ + or mapPoint[1] >= self.canvas.size[1]: + return + self.brushPos = mapPoint + tool.onMouseEvent( \ + event, mapPoint, self.brushColours, self.brushSize, \ + event.Dragging(), event.LeftIsDown(), event.RightIsDown(), \ + self.dispatchPatch, eventDc) + if self.canvas.dirty: + self.parentFrame.update(cellPos=self.brushPos, undoLevel=self.canvas.journal.patchesUndoLevel) + if eventType == wx.wxEVT_MOTION: + self.parentFrame.update(cellPos=mapPoint) + # }}} + # {{{ onPanelLeaveWindow(self, event): XXX + def onPanelLeaveWindow(self, event): + eventDc = self.backend.getDeviceContext(self) + self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc) + # }}} + # {{{ onPanelPaint(self, event): XXX + def onPanelPaint(self, event): + self.backend.onPanelPaintEvent(event, self) + # }}} + + # + # __init__(self, parent, parentFrame, backend, canvas, defaultCanvasPos, defaultCanvasSize, defaultCellSize, interface): initialisation method + def __init__(self, parent, parentFrame, backend, canvas, defaultCanvasPos, defaultCanvasSize, defaultCellSize, interface): + super().__init__(parent, pos=defaultCanvasPos, size=[w * h for w, h in zip(defaultCanvasSize, defaultCellSize)]) + self.backend, self.interface = backend(defaultCanvasSize, defaultCellSize), interface(self, parentFrame) + self.brushColours, self.brushPos, self.brushSize = [4, 1], [0, 0], [1, 1] + self.canvas, self.canvasPos, self.defaultCanvasPos, self.defaultCanvasSize, self.defaultCellSize = canvas, defaultCanvasPos, defaultCanvasPos, defaultCanvasSize, defaultCellSize + self.parentFrame = parentFrame + self.parentFrame.update(brushSize=self.brushSize, colours=self.brushColours) + + self.Bind(wx.EVT_CLOSE, self.onPanelClose) + self.Bind(wx.EVT_ENTER_WINDOW, self.onPanelEnterWindow) + self.Bind(wx.EVT_LEAVE_WINDOW, self.onPanelLeaveWindow) + self.parentFrame.Bind(wx.EVT_CHAR, self.onPanelInput) + for eventType in (wx.EVT_LEFT_DOWN, wx.EVT_MOTION, wx.EVT_RIGHT_DOWN): + self.Bind(eventType, self.onPanelInput) + self.Bind(wx.EVT_PAINT, self.onPanelPaint) + +# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0 diff --git a/libcanvas/CanvasBackend.py b/libgui/GuiCanvasWxBackend.py similarity index 98% rename from libcanvas/CanvasBackend.py rename to libgui/GuiCanvasWxBackend.py index ba93714..bdcf8ba 100644 --- a/libcanvas/CanvasBackend.py +++ b/libgui/GuiCanvasWxBackend.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 # -# CanvasBackend.py -- XXX +# GuiCanvasWxBackend.py -- XXX # Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz # -from CanvasColours import Colours +from GuiCanvasColours import Colours import wx -class CanvasBackend(): +class GuiCanvasWxBackend(): """XXX""" # {{{ _drawBrushPatch(self, eventDc, patch): XXX diff --git a/libgui/GuiFrame.py b/libgui/GuiFrame.py index eb9c257..967945b 100644 --- a/libgui/GuiFrame.py +++ b/libgui/GuiFrame.py @@ -4,9 +4,12 @@ # Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz # -from Canvas import Canvas, haveUrllib -from CanvasColours import Colours +from Canvas import Canvas +from CanvasExportStore import haveUrllib +from GuiCanvasColours import Colours from GuiCanvasInterface import GuiCanvasInterface +from GuiCanvasPanel import GuiCanvasPanel +from GuiCanvasWxBackend import GuiCanvasWxBackend from GuiGeneralFrame import GuiGeneralFrame, \ TID_ACCELS, TID_COMMAND, TID_LIST, TID_MENU, TID_NOTHING, TID_SELECT, TID_TOOLBAR, \ NID_MENU_SEP, NID_TOOLBAR_HSEP, NID_TOOLBAR_VSEP @@ -116,9 +119,7 @@ class GuiFrame(GuiGeneralFrame): def _initIcon(self): iconPathNames = glob(os.path.join("assets", "images", "logo*.bmp")) iconPathName = iconPathNames[random.randint(0, len(iconPathNames) - 1)] - icon = wx.Icon() - icon.CopyFromBitmap(wx.Bitmap(iconPathName, wx.BITMAP_TYPE_ANY)) - self.SetIcon(icon) + icon = wx.Icon(); icon.CopyFromBitmap(wx.Bitmap(iconPathName, wx.BITMAP_TYPE_ANY)); self.SetIcon(icon); # }}} # {{{ _initPaletteToolBitmaps(self): XXX def _initPaletteToolBitmaps(self): @@ -131,8 +132,7 @@ class GuiFrame(GuiGeneralFrame): toolBitmapColour = Colours[numColour][0:4] toolBitmap = wx.Bitmap((16,16)) toolBitmapDc = wx.MemoryDC(); toolBitmapDc.SelectObject(toolBitmap); - toolBitmapBrush = wx.Brush( \ - wx.Colour(toolBitmapColour), wx.BRUSHSTYLE_SOLID) + toolBitmapBrush = wx.Brush(wx.Colour(toolBitmapColour), wx.BRUSHSTYLE_SOLID) toolBitmapDc.SetBrush(toolBitmapBrush) toolBitmapDc.SetBackground(toolBitmapBrush) toolBitmapDc.SetPen(wx.Pen(wx.Colour(toolBitmapColour), 1)) @@ -140,35 +140,18 @@ class GuiFrame(GuiGeneralFrame): paletteDescr[numColour][4] = ["", None, toolBitmap] # }}} - # {{{ onInput(self, event): XXX - def onInput(self, event): - eventId = event.GetId() - if eventId >= self.CID_COLOUR00[0] \ - and eventId <= self.CID_COLOUR15[0]: - numColour = eventId - self.CID_COLOUR00[0] - self.itemsById[eventId][7](self.panelCanvas.canvasInterface, event, numColour) - else: - self.itemsById[eventId][7](self.panelCanvas.canvasInterface, event) - # }}} - # {{{ onCanvasUpdate(self, newBrushSize=None, newCellPos=None, newColours=None, newPathName=None, newSize=None, newToolName=None, newUndoLevel=None): XXX - def onCanvasUpdate(self, **kwargs): - self.lastPanelState.update(kwargs) - textItems = [] + # {{{ update(self, **kwargs): XXX + def update(self, **kwargs): + self.lastPanelState.update(kwargs); textItems = []; if "cellPos" in self.lastPanelState: - textItems.append("X: {:03d} Y: {:03d}".format( \ - *self.lastPanelState["cellPos"])) + textItems.append("X: {:03d} Y: {:03d}".format(*self.lastPanelState["cellPos"])) if "size" in self.lastPanelState: - textItems.append("W: {:03d} H: {:03d}".format( \ - *self.lastPanelState["size"])) + textItems.append("W: {:03d} H: {:03d}".format(*self.lastPanelState["size"])) if "brushSize" in self.lastPanelState: - textItems.append("Brush: {:02d}x{:02d}".format( \ - *self.lastPanelState["brushSize"])) + textItems.append("Brush: {:02d}x{:02d}".format(*self.lastPanelState["brushSize"])) if "colours" in self.lastPanelState: - textItems.append("FG: {:02d}, BG: {:02d}".format( \ - *self.lastPanelState["colours"])) - textItems.append("{} on {}".format( \ - Colours[self.lastPanelState["colours"][0]][4], \ - Colours[self.lastPanelState["colours"][1]][4])) + textItems.append("FG: {:02d}, BG: {:02d}".format(*self.lastPanelState["colours"])) + textItems.append("{} on {}".format(Colours[self.lastPanelState["colours"][0]][4], Colours[self.lastPanelState["colours"][1]][4])) if "pathName" in self.lastPanelState: if self.lastPanelState["pathName"] != "": basePathName = os.path.basename(self.lastPanelState["pathName"]) @@ -177,8 +160,7 @@ class GuiFrame(GuiGeneralFrame): else: self.SetTitle("roar") if "toolName" in self.lastPanelState: - textItems.append("Current tool: {}".format( \ - self.lastPanelState["toolName"])) + textItems.append("Current tool: {}".format(self.lastPanelState["toolName"])) self.statusBar.SetStatusText(" | ".join(textItems)) if "undoLevel" in self.lastPanelState: if self.lastPanelState["undoLevel"] >= 0: @@ -198,11 +180,15 @@ class GuiFrame(GuiGeneralFrame): toolBar = self.toolBarItemsById[self.CID_REDO[0]].GetToolBar() toolBar.EnableTool(self.CID_REDO[0], False) # }}} - - # {{{ __del__(self): destructor method - def __del__(self): - if self.panelCanvas != None: - del self.panelCanvas; self.panelCanvas = None; + # {{{ onInput(self, event): XXX + def onInput(self, event): + eventId = event.GetId() + if eventId >= self.CID_COLOUR00[0] \ + and eventId <= self.CID_COLOUR15[0]: + numColour = eventId - self.CID_COLOUR00[0] + self.itemsById[eventId][7](self.canvasPanel.interface, event, numColour) + else: + self.itemsById[eventId][7](self.canvasPanel.interface, event) # }}} # @@ -210,18 +196,15 @@ class GuiFrame(GuiGeneralFrame): def __init__(self, parent, appSize=(840, 630), defaultCanvasPos=(0, 75), defaultCanvasSize=(100, 30), defaultCellSize=(7, 14)): self._initPaletteToolBitmaps() self.panelSkin = super().__init__(parent, wx.ID_ANY, "", size=appSize) - self.lastPanelState, self.panelCanvas = {}, None + self.lastPanelState, self.canvasPanel = {}, None self._initIcon() - self.panelCanvas = Canvas(self.panelSkin, parentFrame=self, \ - canvasInterface=GuiCanvasInterface, \ - defaultCanvasPos=defaultCanvasPos, \ - defaultCanvasSize=defaultCanvasSize, \ - defaultCellSize=defaultCellSize) - self.panelCanvas.canvasInterface.canvasNew(None) + self.canvas = Canvas(defaultCanvasSize) + self.canvasPanel = GuiCanvasPanel(self.panelSkin, self, GuiCanvasWxBackend, self.canvas, defaultCanvasPos, defaultCanvasSize, defaultCellSize, GuiCanvasInterface) + self.canvasPanel.interface.canvasNew(None) self.sizerSkin.AddSpacer(5) - self.sizerSkin.Add(self.panelCanvas, 0, wx.ALL|wx.EXPAND, 14) + self.sizerSkin.Add(self.canvasPanel, 0, wx.ALL|wx.EXPAND, 14) self.panelSkin.SetSizer(self.sizerSkin) self.panelSkin.SetAutoLayout(1) self.sizerSkin.Fit(self.panelSkin) diff --git a/libtools/ToolFill.py b/libtools/ToolFill.py index 368cda3..6b9941e 100644 --- a/libtools/ToolFill.py +++ b/libtools/ToolFill.py @@ -14,24 +14,24 @@ class ToolFill(Tool): # onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc): XXX def onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc): pointStack, pointsDone = [list(atPoint)], [] - testColour = self.parentCanvas.canvasMap[atPoint[1]][atPoint[0]][0:2] + testColour = self.parentCanvas.canvas.map[atPoint[1]][atPoint[0]][0:2] if isLeftDown or isRightDown: if isRightDown: brushColours = [brushColours[1], brushColours[0]] while len(pointStack) > 0: point = pointStack.pop() - pointCell = self.parentCanvas.canvasMap[point[1]][point[0]] - if pointCell[0:2] == testColour: + pointCell = self.parentCanvas.canvas.map[point[1]][point[0]] + if (pointCell[0:2] == testColour) \ + or ((pointCell[3] == " ") and (pointCell[1] == testColour[1])): if not point in pointsDone: - dispatchFn(eventDc, False, [*point, \ - brushColours[0], brushColours[0], 0, " "]) + dispatchFn(eventDc, False, [*point, brushColours[0], brushColours[0], 0, " "]) if point[0] > 0: pointStack.append([point[0] - 1, point[1]]) - if point[0] < (self.parentCanvas.canvasSize[0] - 1): + if point[0] < (self.parentCanvas.canvas.size[0] - 1): pointStack.append([point[0] + 1, point[1]]) if point[1] > 0: pointStack.append([point[0], point[1] - 1]) - if point[1] < (self.parentCanvas.canvasSize[1] - 1): + if point[1] < (self.parentCanvas.canvas.size[1] - 1): pointStack.append([point[0], point[1] + 1]) pointsDone += [point] diff --git a/libtools/ToolSelect.py b/libtools/ToolSelect.py index 5bbebcf..614925b 100644 --- a/libtools/ToolSelect.py +++ b/libtools/ToolSelect.py @@ -64,7 +64,7 @@ class ToolSelect(Tool): self.toolSelectMap.append([]) 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.toolSelectMap[numRow].append(self.parentCanvas.canvasMap[rectY][rectX]) + self.toolSelectMap[numRow].append(self.parentCanvas.canvas.map[rectY][rectX]) self._drawSelectRect(self.targetRect, dispatchFn, eventDc) elif isRightDown: self.targetRect, self.toolState = None, self.TS_NONE diff --git a/libtools/ToolText.py b/libtools/ToolText.py index a7b02aa..b34c282 100644 --- a/libtools/ToolText.py +++ b/libtools/ToolText.py @@ -24,9 +24,9 @@ class ToolText(Tool): if self.textPos == None: self.textPos = list(atPoint) dispatchFn(eventDc, False, [*self.textPos, *self.textColours, 0, keyChar]) - if self.textPos[0] < (self.parentCanvas.canvasSize[0] - 1): + if self.textPos[0] < (self.parentCanvas.canvas.size[0] - 1): self.textPos[0] += 1 - elif self.textPos[1] < (self.parentCanvas.canvasSize[1] - 1): + elif self.textPos[1] < (self.parentCanvas.canvas.size[1] - 1): self.textPos[0] = 0; self.textPos[1] += 1; else: self.textPos = [0, 0] diff --git a/roar.py b/roar.py index 718f7ad..1f81328 100755 --- a/roar.py +++ b/roar.py @@ -18,11 +18,11 @@ def main(*argv): appFrame = GuiFrame(None) if len(argv) > 1 \ and len(argv[1]) > 0: - appFrame.panelCanvas.canvasInterface.canvasPathName = argv[1] - rc, error = appFrame.panelCanvas.canvasImportStore.importTextFile(argv[1]) + appFrame.canvasPanel.interface.canvasPathName = argv[1] + rc, error = appFrame.canvasPanel.canvas.importStore.importTextFile(argv[1]) if rc: - appFrame.panelCanvas.onStoreUpdate(appFrame.panelCanvas.canvasImportStore.inSize, appFrame.panelCanvas.canvasImportStore.outMap) - appFrame.onCanvasUpdate(pathName=argv[1], undoLevel=-1) + appFrame.canvasPanel.update(appFrame.canvasPanel.canvas.importStore.inSize, False, appFrame.canvasPanel.canvas.importStore.outMap) + appFrame.update(pathName=argv[1], undoLevel=-1) else: print("error: {}".format(error), file=sys.stderr) wxApp.MainLoop()