mirror of
https://github.com/lalbornoz/roar.git
synced 2024-11-26 08:56:36 +00:00
Minor cleanup & usability improvements.
1) Add format clarification to commands documentation. 2) Allow changing {canvas,brush} size w/ {<Ctrl> <Shift>,<Ctrl>} <Mouse wheel>. 3) Backend: {draw,produce,remove} cursor w/ list of coordinates instead of cells. 4) Update changing rendered cell size hotkey to <Ctrl> <Alt> <Mouse wheel>.
This commit is contained in:
parent
c84ef3a47e
commit
f428daa9e0
@ -19,9 +19,8 @@
|
||||
|
||||
Release roadmap:
|
||||
1) {copy,cut,delete,insert from,paste}, edit asset in new canvas, import from {canvas,object}
|
||||
2) reimplement cursor unmasking w/ simple list of points
|
||||
3) operators: crop, scale, shift, slice
|
||||
4) auto{load,save} & {backup,restore}
|
||||
5) tools: unicode block elements
|
||||
2) operators: crop, scale, shift, slice
|
||||
3) auto{load,save} & {backup,restore}
|
||||
4) tools: unicode block elements
|
||||
|
||||
vim:ff=dos tw=0
|
||||
|
@ -1,6 +1,10 @@
|
||||
Global hotkeys:
|
||||
<Ctrl> -/+ Decrease/increase brush size height and width
|
||||
<Ctrl> <Shift> -/+ Decrease/increase canvas size height and width
|
||||
Keys or mouse actions separated by forward slashes (`/') indicate alternatives.
|
||||
Keys or mouse actions separated by commas indicate separate commands.
|
||||
|
||||
Global commands:
|
||||
<Ctrl> -, +/<Mouse wheel> Decrease/increase brush size height and width
|
||||
<Ctrl> <Alt> <Mouse wheel> Decrease/increase rendered cell size
|
||||
<Ctrl> <Shift> -, +/<Mouse wheel> Decrease/increase canvas size height and width
|
||||
<Ctrl> 0-9 Set foreground colour to #0-9
|
||||
<Ctrl> <Shift> 0-5, 6 Set foreground colour to #10-15 or transparent colour, resp.
|
||||
<Ctrl> <Alt> 0-9 Set background colour to #0-9
|
||||
@ -15,15 +19,15 @@ Global hotkeys:
|
||||
<F1> View melp?
|
||||
<Shift> <Pause> Break into Python debugger
|
||||
|
||||
Canvas hotkeys:
|
||||
Canvas commands:
|
||||
<Down>, <Left>, <Right>, <Up> Move canvas cursor
|
||||
<LMB>/<Space> Apply current tool with foreground colour (with exceptions)
|
||||
<RMB> Apply current tool with background colour (with exceptions)
|
||||
|
||||
Tool-specific hotkeys:
|
||||
(Circle, rectangle) <Ctrl> <LMB>/<RMB> Initiate circle/rectangle dragging irrespective of brush size
|
||||
Tool-specific commands:
|
||||
(Circle, rectangle) <Ctrl> <LMB>, <RMB> Initiate circle/rectangle dragging irrespective of brush size
|
||||
(Erase) <RMB> Erase background colour with foreground colour
|
||||
(Fill) <Ctrl> <LMB>/<Space>/<RMB> Fill entire region with foreground/background colour ignoring character cells
|
||||
(Fill) <Ctrl> <LMB>, <Space>/<RMB> Fill entire region with foreground/background colour ignoring character cells
|
||||
(Line, object) <LMB>/<Space> Initiate line drawing/selection
|
||||
(Object) <Ctrl> <LMB> Move selection instead of cloning
|
||||
(Pick colour) <LMB>/<Space> Pick current cell's foreground colour
|
@ -12,30 +12,14 @@ class Canvas():
|
||||
def _commitPatch(self, patch):
|
||||
self.map[patch[1]][patch[0]] = patch[2:]
|
||||
|
||||
def dispatchPatch(self, isCursor, patch, commitUndo=True):
|
||||
def applyPatch(self, patch, commitUndo=True):
|
||||
if (patch[0] >= self.size[0]) or (patch[1] >= self.size[1]):
|
||||
return False
|
||||
else:
|
||||
patchDeltaCell = self.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell];
|
||||
if isCursor:
|
||||
self.journal.pushCursor(patchDelta)
|
||||
else:
|
||||
if commitUndo:
|
||||
self.journal.begin(); self.journal.updateCurrentDeltas(patch, patchDelta); self.journal.end();
|
||||
self._commitPatch(patch)
|
||||
return True
|
||||
|
||||
def dispatchPatchSingle(self, isCursor, patch, commitUndo=True):
|
||||
if (patch[0] >= self.size[0]) or (patch[1] >= self.size[1]):
|
||||
return False
|
||||
else:
|
||||
patchDeltaCell = self.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell];
|
||||
if isCursor:
|
||||
self.journal.pushCursor(patchDelta)
|
||||
else:
|
||||
if commitUndo:
|
||||
self.journal.updateCurrentDeltas(patch, patchDelta)
|
||||
self._commitPatch(patch)
|
||||
if commitUndo:
|
||||
self.journal.updateCurrentDeltas(patch, patchDelta)
|
||||
self._commitPatch(patch)
|
||||
return True
|
||||
|
||||
def resize(self, newSize, commitUndo=True):
|
||||
@ -62,7 +46,7 @@ class Canvas():
|
||||
for numNewCol in range(oldSize[0], newSize[0]):
|
||||
if commitUndo:
|
||||
self.journal.updateCurrentDeltas([numNewCol, numRow, 1, 1, 0, " "], None)
|
||||
self.dispatchPatch(False, [numNewCol, numRow, 1, 1, 0, " "], False)
|
||||
self.applyPatch([numNewCol, numRow, 1, 1, 0, " "], False)
|
||||
if deltaSize[1] < 0:
|
||||
if commitUndo:
|
||||
for numRow in range((oldSize[1] + deltaSize[1]), oldSize[1]):
|
||||
@ -75,7 +59,7 @@ class Canvas():
|
||||
for numNewCol in range(newSize[0]):
|
||||
if commitUndo:
|
||||
self.journal.updateCurrentDeltas([numNewCol, numNewRow, 1, 1, 0, " "], None)
|
||||
self.dispatchPatch(False, [numNewCol, numNewRow, 1, 1, 0, " "], False)
|
||||
self.applyPatch([numNewCol, numNewRow, 1, 1, 0, " "], False)
|
||||
self.size = newSize
|
||||
if commitUndo:
|
||||
self.journal.end()
|
||||
@ -86,13 +70,11 @@ class Canvas():
|
||||
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])):
|
||||
if (newCanvas != None) \
|
||||
and (numRow < len(newCanvas)) and (numCol < len(newCanvas[numRow])):
|
||||
self._commitPatch([numCol, numRow, *newCanvas[numRow][numCol]])
|
||||
|
||||
def __init__(self, size):
|
||||
self.dirtyCursor, self.map, self.size = False, None, size
|
||||
self.exportStore, self.importStore, self.journal = CanvasExportStore(), CanvasImportStore(), CanvasJournal()
|
||||
self.exportStore, self.importStore, self.journal, self.map, self.size = CanvasExportStore(), CanvasImportStore(), CanvasJournal(), None, size
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -37,7 +37,7 @@ class CanvasJournal():
|
||||
return []
|
||||
|
||||
def pushCursor(self, patches):
|
||||
self.patchesCursor.append(patches)
|
||||
self.patchesCursor = patches
|
||||
|
||||
def resetCursor(self):
|
||||
if self.patchesCursor != None:
|
||||
|
@ -148,11 +148,16 @@ class GuiCanvasWxBackend():
|
||||
|
||||
def drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc):
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
self.drawPatches(canvas, eventDc, canvasJournal.popCursor(), False)
|
||||
cursorCells, patches = canvasJournal.popCursor(), []
|
||||
for cursorCell in cursorCells:
|
||||
patches += [[*cursorCell, *canvas.map[cursorCell[1]][cursorCell[0]]]]
|
||||
if len(patches) > 0:
|
||||
self.drawPatches(canvas, eventDc, patches, False)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
return cursorCells
|
||||
|
||||
def drawPatches(self, canvas, eventDc, patches, isCursor=False):
|
||||
patchDeltaCells, patchesRender = [], []
|
||||
patchCursorCells, patchesRender = [], []
|
||||
for patch in patches:
|
||||
point = patch[:2]
|
||||
if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]:
|
||||
@ -186,8 +191,8 @@ class GuiCanvasWxBackend():
|
||||
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
|
||||
patchCursorCells += [patchRender[:2]]
|
||||
return patchCursorCells
|
||||
|
||||
def getDeviceContext(self, clientSize, parentWindow, viewRect=None):
|
||||
if viewRect == None:
|
||||
|
@ -194,13 +194,11 @@ class RoarCanvasCommandsEdit():
|
||||
|
||||
@GuiCommandDecorator("Redo", "&Redo", ["", wx.ART_REDO], [wx.ACCEL_CTRL, ord("Y")], False)
|
||||
def canvasRedo(self, event):
|
||||
self.parentCanvas.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popRedo(), forceDirtyCursor=True)
|
||||
self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel)
|
||||
self.parentCanvas.undo(redo=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):
|
||||
self.parentCanvas.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popUndo(), forceDirtyCursor=True)
|
||||
self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel)
|
||||
self.parentCanvas.undo(); self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel);
|
||||
|
||||
def __init__(self):
|
||||
self.accels = (
|
||||
|
@ -45,20 +45,18 @@ class RoarCanvasWindow(GuiWindow):
|
||||
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;
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc)
|
||||
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.applyPatch(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)
|
||||
patchesCursorCells = self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
|
||||
if len(patchesCursorCells) > 0:
|
||||
self.canvas.journal.pushCursor(patchesCursorCells)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
self.commands.update(dirty=self.dirty, cellPos=self.brushPos, undoLevel=self.canvas.journal.patchesUndoLevel)
|
||||
|
||||
@ -122,26 +120,6 @@ class RoarCanvasWindow(GuiWindow):
|
||||
self.commands.update(undoInhibit=False)
|
||||
return rc
|
||||
|
||||
def dispatchDeltaPatches(self, deltaPatches, eventDc=None, forceDirtyCursor=True):
|
||||
if eventDc == None:
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
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
|
||||
elif patch[0] == "resize":
|
||||
del eventDc; self.resize(patch[1:], False);
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
else:
|
||||
self.canvas._commitPatch(patch); patches += [patch];
|
||||
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
|
||||
def onKeyboardInput(self, event):
|
||||
keyCode, keyModifiers = event.GetKeyCode(), event.GetModifiers()
|
||||
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
|
||||
@ -211,9 +189,9 @@ class RoarCanvasWindow(GuiWindow):
|
||||
event.Skip()
|
||||
|
||||
def onMouseWheel(self, event):
|
||||
if event.GetModifiers() == wx.MOD_CONTROL:
|
||||
fd = +1 if event.GetWheelRotation() >= event.GetWheelDelta() else -1
|
||||
newFontSize = self.backend.fontSize + fd
|
||||
delta, modifiers = +1 if event.GetWheelRotation() >= event.GetWheelDelta() else -1, event.GetModifiers()
|
||||
if modifiers == (wx.MOD_CONTROL | wx.MOD_ALT):
|
||||
newFontSize = self.backend.fontSize + delta
|
||||
if newFontSize > 0:
|
||||
self.backend.fontSize = newFontSize
|
||||
self.backend.resize(self.canvas.size); self.scrollStep = self.backend.cellSize;
|
||||
@ -226,12 +204,15 @@ class RoarCanvasWindow(GuiWindow):
|
||||
patches += [[numCol, numRow, *self.canvas.map[numRow][numCol]]]
|
||||
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
elif modifiers == (wx.MOD_CONTROL | wx.MOD_SHIFT):
|
||||
self.commands.canvasCanvasSize(self.commands.canvasCanvasSize, 2, 1 if delta > 0 else 0)(None)
|
||||
elif modifiers == wx.MOD_CONTROL:
|
||||
self.commands.canvasBrushSize(self.commands.canvasBrushSize, 2, 1 if delta > 0 else 0)(None)
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
def onPaint(self, event):
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||
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):
|
||||
@ -257,6 +238,24 @@ class RoarCanvasWindow(GuiWindow):
|
||||
self.Scroll(*viewRect); self.dirty = dirty;
|
||||
self.commands.update(dirty=self.dirty, size=newSize, undoLevel=self.canvas.journal.patchesUndoLevel)
|
||||
|
||||
def undo(self, redo=False):
|
||||
deltaPatches = self.canvas.journal.popUndo() if not redo else self.canvas.journal.popRedo()
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc)
|
||||
patches = []
|
||||
for patch in deltaPatches:
|
||||
if patch == None:
|
||||
continue
|
||||
elif patch[0] == "resize":
|
||||
del eventDc; self.resize(patch[1:], False);
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
else:
|
||||
self.canvas._commitPatch(patch); patches += [patch];
|
||||
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
|
||||
def update(self, newSize, commitUndo=True, newCanvas=None, dirty=True):
|
||||
self.resize(newSize, commitUndo, dirty)
|
||||
self.canvas.update(newSize, newCanvas)
|
||||
|
@ -14,7 +14,7 @@ class RoarWindowMelp(wx.Dialog):
|
||||
super().__init__(parent, size=minSize, title=title)
|
||||
self.panel, self.sizer = wx.Panel(self), wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
with open(os.path.join("assets", "text", "hotkeys.txt"), "r") as fileObject:
|
||||
with open(os.path.join("assets", "text", "melp.txt"), "r") as fileObject:
|
||||
helpLabel = "".join(fileObject.readlines())
|
||||
self.title = wx.StaticText(self.panel, label=helpLabel, style=wx.ALIGN_LEFT)
|
||||
self.title.SetFont(wx.Font(8, wx.MODERN, wx.NORMAL, wx.NORMAL, underline=False))
|
||||
|
Loading…
Reference in New Issue
Block a user