mirror of
https://github.com/lalbornoz/roar.git
synced 2024-12-22 12:26:37 +00:00
Initial implementation of assets window & external object tool.
libgui/GuiFrame.py:GuiMiniFrame(): added as sub-class of wx.MiniFrame(). libgui/GuiWindow.py:RoarCanvasWindowDropTarget(): initial implementation. libgui/GuiWindow.py:__init__(): receive & pass optional style parameter. libgui/GuiWindow.py:__init__(): set drop target. libroar/RoarAssetsWindow.py: initial implementation. libroar/RoarClient.py:__init__(): add RoarAssetsWindow(). libtools/ToolObject.py: initial implementation. assets/text/TODO: updated.
This commit is contained in:
parent
99e746f090
commit
b6c063c5d3
@ -1,11 +1,11 @@
|
||||
1) Implement ANSI CSI CU[BDPU] sequences & italic
|
||||
2) Implement instrumentation & unit tests, document
|
||||
3) Open and toggle a reference image in the background
|
||||
4) Client-Server or Peer-to-Peer realtime collaboration
|
||||
5) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.)
|
||||
6) Hotkey & graphical interfaces to {composed,parametrised} tools
|
||||
7) Incremental auto{load,save} & {backup,restore} (needs Settings window)
|
||||
8) Layers, layout (e.g. for comics, zines, etc.) & asset management (e.g. kade, lion, etc.) & traits w/ {inserting,merging,linking}
|
||||
2) Layers & layout (e.g. for comics, zines, etc.)
|
||||
3) Implement instrumentation & unit tests, document
|
||||
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) Incremental auto{load,save} & {backup,restore} (needs Settings window)
|
||||
9) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...)
|
||||
10) 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)
|
||||
@ -15,7 +15,7 @@
|
||||
a) {copy,cut,insert from,paste}, {de,in}crease cell size
|
||||
b) replace logo w/ canvas panel in About dialogue
|
||||
c) switch from wxPython to GTK
|
||||
d) fix underscore rendering
|
||||
d) edit asset in new canvas
|
||||
e) MRU {directories,files}
|
||||
f) ruler
|
||||
12) fix outstanding {re,un}do bugs
|
||||
|
@ -175,4 +175,10 @@ class GuiFrame(wx.Frame):
|
||||
for event, f in ((wx.EVT_CHAR, self.onChar), (wx.EVT_MENU, self.onMenu), (wx.EVT_MOUSEWHEEL, self.onMouseWheel)):
|
||||
self.Bind(event, f)
|
||||
|
||||
class GuiMiniFrame(wx.MiniFrame):
|
||||
#
|
||||
# __init__(self, parent, size, title, pos=wx.DefaultPosition): initialisation method
|
||||
def __init__(self, parent, size, title, pos=wx.DefaultPosition):
|
||||
super().__init__(parent, id=wx.ID_ANY, pos=pos, size=size, title=title)
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -55,9 +55,9 @@ class GuiWindow(wx.ScrolledWindow):
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, parent, pos, scrollStep, size): initialisation method
|
||||
def __init__(self, parent, pos, scrollStep, size):
|
||||
super().__init__(parent, pos=pos, size=size)
|
||||
# __init__(self, parent, pos, scrollStep, size, style=0): initialisation method
|
||||
def __init__(self, parent, pos, scrollStep, size, style=0):
|
||||
super().__init__(parent, pos=pos, size=size, style=style)
|
||||
self.pos, self.scrollFlag, self.scrollStep, self.size = pos, False, scrollStep, size
|
||||
for eventType, f in (
|
||||
(wx.EVT_CHAR, self.onKeyboardInput), (wx.EVT_CLOSE, self.onClose), (wx.EVT_LEAVE_WINDOW, self.onLeaveWindow),
|
||||
|
326
libroar/RoarAssetsWindow.py
Normal file
326
libroar/RoarAssetsWindow.py
Normal file
@ -0,0 +1,326 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# RoarAssetsWindow.py
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
from Canvas import Canvas
|
||||
from GuiFrame import GuiMiniFrame
|
||||
from GuiWindow import GuiWindow
|
||||
import json, os, sys, wx
|
||||
|
||||
class RoarAssetsWindow(GuiMiniFrame):
|
||||
# {{{ _drawPatch(self, eventDc, isCursor, patch, viewRect)
|
||||
def _drawPatch(self, eventDc, isCursor, patch, viewRect):
|
||||
if not isCursor:
|
||||
self.backend.drawPatch(eventDc, patch, viewRect)
|
||||
# }}}
|
||||
# {{{ _import(self, f, pathName)
|
||||
def _import(self, f, pathName):
|
||||
rc = False
|
||||
self.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||
try:
|
||||
canvas = Canvas((0, 0))
|
||||
rc, error, newMap, newPathName, newSize = f(canvas, pathName)
|
||||
if rc:
|
||||
self.update(canvas, newSize, newMap)
|
||||
except FileNotFoundError as e:
|
||||
rc, error, newMap, newPathName, newSize = False, str(e), None, None, None
|
||||
self.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
return rc, error, canvas, newMap, newPathName, newSize
|
||||
# }}}
|
||||
# {{{ _importFiles(self, f, wildcard)
|
||||
def _importFiles(self, f, wildcard):
|
||||
resultList = []
|
||||
with wx.FileDialog(self, "Load...", os.getcwd(), "", wildcard, wx.FD_MULTIPLE | wx.FD_OPEN) as dialog:
|
||||
if dialog.ShowModal() == wx.ID_CANCEL:
|
||||
resultList += [[False, "(cancelled)", None, None, None, None]]
|
||||
else:
|
||||
for pathName in dialog.GetPaths():
|
||||
resultList += [self._import(f, pathName)]
|
||||
return resultList
|
||||
# }}}
|
||||
# {{{ _updateScrollBars(self)
|
||||
def _updateScrollBars(self):
|
||||
clientSize = self.panelCanvas.GetClientSize()
|
||||
if (self.panelCanvas.size[0] > clientSize[0]) or (self.panelCanvas.size[1] > clientSize[1]):
|
||||
self.scrollFlag = True; super(wx.ScrolledWindow, self.panelCanvas).SetVirtualSize(self.panelCanvas.size);
|
||||
elif self.scrollFlag \
|
||||
and ((self.panelCanvas.size[0] <= clientSize[0]) or (self.panelCanvas.size[1] <= clientSize[1])):
|
||||
self.scrollFlag = False; super(wx.ScrolledWindow, self.panelCanvas).SetVirtualSize((0, 0));
|
||||
# }}}
|
||||
|
||||
# {{{ drawCanvas(self, canvas)
|
||||
def drawCanvas(self, canvas):
|
||||
panelSize = [a * b for a, b in zip(canvas.size, self.cellSize)]
|
||||
self.panelCanvas.SetMinSize(panelSize); self.panelCanvas.SetSize(wx.DefaultCoord, wx.DefaultCoord, *panelSize);
|
||||
curWindow = self.panelCanvas
|
||||
while curWindow != None:
|
||||
curWindow.Layout(); curWindow = curWindow.GetParent();
|
||||
self.backend.resize(canvas.size, self.cellSize)
|
||||
viewRect = self.panelCanvas.GetViewStart();
|
||||
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas, viewRect)
|
||||
for numRow in range(canvas.size[1]):
|
||||
for numCol in range(canvas.size[0]):
|
||||
self.backend.drawPatch(eventDc, [numCol, numRow, *canvas.map[numRow][numCol]], viewRect)
|
||||
# }}}
|
||||
# {{{ onPaint(self, event)
|
||||
def onPaint(self, event):
|
||||
self.backend.onPaint(self.panelCanvas.GetClientSize(), self.panelCanvas, self.panelCanvas.GetViewStart())
|
||||
# }}}
|
||||
# {{{ onPanelLeftDown(self, event)
|
||||
def onPanelLeftDown(self, event):
|
||||
self.panelCanvas.SetFocus()
|
||||
if (self.currentIndex != None):
|
||||
dataText = json.dumps((self.canvasList[self.currentIndex][0].map, self.canvasList[self.currentIndex][0].size,))
|
||||
textDataObject = wx.TextDataObject(dataText)
|
||||
dropSource = wx.DropSource(event.GetEventObject())
|
||||
dropSource.SetData(textDataObject)
|
||||
result = dropSource.DoDragDrop(True)
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onPanelPaint(self, event)
|
||||
def onPanelPaint(self, event):
|
||||
self.backend.onPaint(self.panelCanvas.GetClientSize(), self.panelCanvas, self.panelCanvas.GetViewStart())
|
||||
# }}}
|
||||
# {{{ onPanelSize(self, event)
|
||||
def onPanelSize(self, event):
|
||||
self._updateScrollBars(); event.Skip();
|
||||
# }}}
|
||||
# {{{ resize(self, canvas, newSize)
|
||||
def resize(self, canvas, newSize):
|
||||
oldSize = [0, 0] if canvas.map == None else canvas.size
|
||||
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
|
||||
if canvas.resize(newSize, False):
|
||||
panelSize = [a * b for a, b in zip(canvas.size, self.cellSize)]
|
||||
self.panelCanvas.SetMinSize(panelSize); self.panelCanvas.SetSize(wx.DefaultCoord, wx.DefaultCoord, *panelSize);
|
||||
curWindow = self.panelCanvas
|
||||
while curWindow != None:
|
||||
curWindow.Layout(); curWindow = curWindow.GetParent();
|
||||
self.backend.resize(newSize, self.cellSize)
|
||||
viewRect = self.panelCanvas.GetViewStart(); eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas, viewRect);
|
||||
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, " "], viewRect)
|
||||
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, " "], viewRect)
|
||||
# }}}
|
||||
# {{{ update(self, canvas, newSize, newCanvas=None)
|
||||
def update(self, canvas, newSize, newCanvas=None):
|
||||
self.resize(canvas, newSize);
|
||||
canvas.update(newSize, newCanvas); viewRect = self.panelCanvas.GetViewStart();
|
||||
viewRect = self.panelCanvas.GetViewStart();
|
||||
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas, viewRect)
|
||||
for numRow in range(canvas.size[1]):
|
||||
for numCol in range(canvas.size[0]):
|
||||
self.backend.drawPatch(eventDc, [numCol, numRow, *canvas.map[numRow][numCol]], viewRect)
|
||||
# }}}
|
||||
|
||||
# {{{ onImportAnsi(self, event)
|
||||
def onImportAnsi(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onImportFromClipboard(self, event)
|
||||
def onImportFromClipboard(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onImportSauce(self, event)
|
||||
def onImportSauce(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onChar(self, event)
|
||||
def onChar(self, event):
|
||||
if (event.GetModifiers() == wx.MOD_NONE) \
|
||||
and (event.GetKeyCode() in (wx.WXK_DOWN, wx.WXK_UP)):
|
||||
self.listView.SetFocus()
|
||||
return wx.PostEvent(self.listView, event)
|
||||
else:
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onListViewChar(self, event)
|
||||
def onListViewChar(self, event):
|
||||
index, rc = self.listView.GetFirstSelected(), False
|
||||
if index != -1:
|
||||
keyChar, keyModifiers = event.GetKeyCode(), event.GetModifiers()
|
||||
if (keyChar, keyModifiers) == (wx.WXK_DELETE, wx.MOD_NONE):
|
||||
self.currentIndex, rc = index, True; self.onRemove(None);
|
||||
if not rc:
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onListViewItemSelected(self, event)
|
||||
def onListViewItemSelected(self, event):
|
||||
self.currentIndex = event.GetItem().GetId()
|
||||
item = [self.listView.GetItem(self.currentIndex, col).GetText() for col in (0, 1)]
|
||||
self.drawCanvas(self.canvasList[self.currentIndex][0])
|
||||
# }}}
|
||||
# {{{ onListViewRightDown(self, event)
|
||||
def onListViewRightDown(self, event):
|
||||
eventPoint = event.GetPosition()
|
||||
if self.currentIndex == None:
|
||||
index, flags = self.listView.HitTest(eventPoint)
|
||||
if index != wx.NOT_FOUND:
|
||||
self.currentIndex = index
|
||||
if self.currentIndex == None:
|
||||
self.contextMenuItems[4].Enable(False)
|
||||
else:
|
||||
self.contextMenuItems[4].Enable(True)
|
||||
self.PopupMenu(self.contextMenu, eventPoint)
|
||||
# }}}
|
||||
# {{{ onLoad(self, event)
|
||||
def onLoad(self, event):
|
||||
def importmIRC(canvas, pathName):
|
||||
rc, error = canvas.importStore.importTextFile(pathName)
|
||||
return (rc, error, canvas.importStore.outMap, pathName, canvas.importStore.inSize)
|
||||
for rc, error, canvas, newMap, newPathName, newSize in self._importFiles(importmIRC, "mIRC art files (*.txt)|*.txt|All Files (*.*)|*.*"):
|
||||
if rc:
|
||||
self.currentIndex = self.listView.GetItemCount()
|
||||
self.canvasList[self.currentIndex] = [canvas, newPathName]
|
||||
self.listView.InsertItem(self.currentIndex, "")
|
||||
idx = -1
|
||||
while True:
|
||||
idx = self.listView.GetNextSelected(idx)
|
||||
if idx != -1:
|
||||
self.listView.Select(idx, on=0)
|
||||
else:
|
||||
break
|
||||
self.listView.Select(self.currentIndex, on=1)
|
||||
self.listView.SetFocus()
|
||||
[self.listView.SetItem(self.currentIndex, col, label) for col, label in zip((0, 1), (os.path.basename(newPathName), "{}x{}".format(*newSize)))]
|
||||
[self.listView.SetColumnWidth(col, wx.LIST_AUTOSIZE) for col in (0, 1)]
|
||||
else:
|
||||
with wx.MessageDialog(self, "Error: {}".format(error), "", wx.CANCEL | wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
if dialogChoice == wx.ID_CANCEL:
|
||||
break
|
||||
# }}}
|
||||
# {{{ onLoadList(self, event)
|
||||
def onLoadList(self, event):
|
||||
rc = True
|
||||
with wx.FileDialog(self, "Load from list...", os.getcwd(), "", "List files (*.lst)|*.lst|Text files (*.txt)|*.txt|All Files (*.*)|*.*", wx.FD_OPEN) as dialog:
|
||||
if dialog.ShowModal() != wx.ID_CANCEL:
|
||||
try:
|
||||
pathName = dialog.GetPath()
|
||||
with open(pathName, "r") as fileObject:
|
||||
try:
|
||||
for line in fileObject.readlines():
|
||||
line = line.rstrip("\r\n")
|
||||
if not os.path.isabs(line):
|
||||
line = os.path.join(os.path.dirname(pathName), line)
|
||||
def importmIRC(canvas, pathName):
|
||||
rc, error = canvas.importStore.importTextFile(pathName)
|
||||
return (rc, error, canvas.importStore.outMap, pathName, canvas.importStore.inSize)
|
||||
rc, error, canvas, newMap, newPathName, newSize = self._import(importmIRC, line)
|
||||
if rc:
|
||||
self.currentIndex = self.listView.GetItemCount()
|
||||
self.canvasList[self.currentIndex] = [canvas, newPathName]
|
||||
self.listView.InsertItem(self.currentIndex, "")
|
||||
idx = -1
|
||||
while True:
|
||||
idx = self.listView.GetNextSelected(idx)
|
||||
if idx != -1:
|
||||
self.listView.Select(idx, on=0)
|
||||
else:
|
||||
break
|
||||
self.listView.Select(self.currentIndex, on=1)
|
||||
self.listView.SetFocus()
|
||||
[self.listView.SetItem(self.currentIndex, col, label) for col, label in zip((0, 1), (os.path.basename(newPathName), "{}x{}".format(*newSize)))]
|
||||
[self.listView.SetColumnWidth(col, wx.LIST_AUTOSIZE) for col in (0, 1)]
|
||||
else:
|
||||
with wx.MessageDialog(self, "Error: {}".format(error), "", wx.CANCEL | wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
if dialogChoice == wx.ID_CANCEL:
|
||||
self.SetCursor(wx.Cursor(wx.NullCursor)); break;
|
||||
except:
|
||||
self.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
with wx.MessageDialog(self, "Error: {}".format(str(sys.exc_info()[1])), "", wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
except FileNotFoundError as e:
|
||||
self.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
with wx.MessageDialog(self, "Error: {}".format(str(e)), "", wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
# }}}
|
||||
# {{{ onRemove(self, event)
|
||||
def onRemove(self, event):
|
||||
del self.canvasList[self.currentIndex]; self.listView.DeleteItem(self.currentIndex);
|
||||
itemCount = self.listView.GetItemCount()
|
||||
if itemCount > 0:
|
||||
for numCanvas in [n for n in sorted(self.canvasList.keys()) if n >= self.currentIndex]:
|
||||
self.canvasList[numCanvas - 1] = self.canvasList[numCanvas]; del self.canvasList[numCanvas];
|
||||
[self.listView.SetColumnWidth(col, wx.LIST_AUTOSIZE) for col in (0, 1)]
|
||||
if (self.currentIndex == 0) or (self.currentIndex >= itemCount):
|
||||
self.currentIndex = 0 if itemCount > 0 else None
|
||||
else:
|
||||
self.currentIndex = self.currentIndex if self.currentIndex < itemCount else None
|
||||
if self.currentIndex != None:
|
||||
self.listView.Select(self.currentIndex, on=1)
|
||||
self.drawCanvas(self.canvasList[self.currentIndex][0])
|
||||
else:
|
||||
self.currentIndex = None
|
||||
[self.listView.SetColumnWidth(col, wx.LIST_AUTOSIZE_USEHEADER) for col in (0, 1)]
|
||||
self.drawCanvas(Canvas((0, 0)))
|
||||
# }}}
|
||||
# {{{ onSaveList(self, event)
|
||||
def onSaveList(self, event):
|
||||
rc = True
|
||||
if len(self.canvasList):
|
||||
with wx.FileDialog(self, "Save as list...", os.getcwd(), "", "List files (*.lst)|*.lst|Text files (*.txt)|*.txt|All Files (*.*)|*.*", wx.FD_SAVE) as dialog:
|
||||
if dialog.ShowModal() != wx.ID_CANCEL:
|
||||
with open(dialog.GetPath(), "w") as fileObject:
|
||||
for pathName in [self.canvasList[k][1] for k in self.canvasList.keys()]:
|
||||
print(pathName, file=fileObject)
|
||||
rc = True
|
||||
else:
|
||||
rc, error = False, "no assets currently loaded"
|
||||
if not rc:
|
||||
with wx.MessageDialog(self, "Error: {}".format(error), "", wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, backend, cellSize, parent, pos=None, size=(400, 400), title="Assets"): initialisation method
|
||||
def __init__(self, backend, cellSize, parent, pos=None, size=(400, 400), title="Assets"):
|
||||
if pos == None:
|
||||
parentRect = parent.GetScreenRect(); pos = (parentRect.x + parentRect.width, parentRect.y);
|
||||
super().__init__(parent, size, title, pos=pos)
|
||||
self.backend, self.canvasList = backend((0, 0), cellSize), {}
|
||||
self.cellSize, self.currentIndex, self.leftDown, self.parent, self.scrollFlag = cellSize, None, False, parent, False
|
||||
self.Bind(wx.EVT_CHAR, self.onChar)
|
||||
|
||||
self.contextMenu, self.contextMenuItems = wx.Menu(), []
|
||||
for text, f in (
|
||||
("&Load...", self.onLoad),
|
||||
("Import &ANSI...", self.onImportAnsi),
|
||||
("Import &SAUCE...", self.onImportSauce),
|
||||
("Import from &clipboard", self.onImportFromClipboard),
|
||||
("&Remove", self.onRemove),
|
||||
(None, None),
|
||||
("Load from l&ist...", self.onLoadList),
|
||||
("Sa&ve as list...", self.onSaveList),):
|
||||
if (text, f) == (None, None):
|
||||
self.contextMenu.AppendSeparator()
|
||||
else:
|
||||
self.contextMenuItems += [wx.MenuItem(self.contextMenu, wx.NewId(), text)]
|
||||
self.Bind(wx.EVT_MENU, f, self.contextMenuItems[-1])
|
||||
self.contextMenu.Append(self.contextMenuItems[-1])
|
||||
|
||||
self.listView = wx.ListView(self, -1, size=([int(m / n) for m, n in zip(size, (2, 4))]), style=wx.BORDER_SUNKEN | wx.LC_REPORT)
|
||||
[self.listView.InsertColumn(col, heading) for col, heading in ((0, "Name"), (1, "Size"))]
|
||||
self.listView.Bind(wx.EVT_CHAR, self.onListViewChar)
|
||||
self.listView.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onListViewItemSelected)
|
||||
self.listView.Bind(wx.EVT_RIGHT_DOWN, self.onListViewRightDown)
|
||||
|
||||
self.panelCanvas = GuiWindow(self, (0, 0), cellSize, (int(size[0] / 2), int(size[1] / 2)), wx.BORDER_SUNKEN)
|
||||
self.panelCanvas.Bind(wx.EVT_LEFT_DOWN, self.onPanelLeftDown)
|
||||
self.panelCanvas.Bind(wx.EVT_PAINT, self.onPanelPaint)
|
||||
self.panelCanvas.Bind(wx.EVT_SIZE, self.onPanelSize)
|
||||
self._updateScrollBars()
|
||||
|
||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.sizer.AddMany(((self.listView, 0, wx.ALL | wx.EXPAND, 4), (self.panelCanvas, 1, wx.ALL | wx.EXPAND, 4),))
|
||||
self.SetSizerAndFit(self.sizer)
|
||||
self.Show(True)
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
@ -8,6 +8,20 @@ from GuiFrame import GuiCommandDecorator, GuiCommandListDecorator, GuiSelectDeco
|
||||
import wx
|
||||
|
||||
class RoarCanvasCommandsEdit():
|
||||
# {{{ canvasAssetsWindowHide(self, event)
|
||||
@GuiCommandDecorator("Hide assets window", "Hide assets window", None, None, False)
|
||||
def canvasAssetsWindowHide(self, event):
|
||||
self.parentFrame.assetsWindow.Show(False)
|
||||
self.parentFrame.menuItemsById[self.canvasAssetsWindowHide.attrDict["id"]].Enable(False)
|
||||
self.parentFrame.menuItemsById[self.canvasAssetsWindowShow.attrDict["id"]].Enable(True)
|
||||
# }}}
|
||||
# {{{ canvasAssetsWindowShow(self, event)
|
||||
@GuiCommandDecorator("Show assets window", "Show assets window", None, None, False)
|
||||
def canvasAssetsWindowShow(self, event):
|
||||
self.parentFrame.assetsWindow.Show(True)
|
||||
self.parentFrame.menuItemsById[self.canvasAssetsWindowHide.attrDict["id"]].Enable(True)
|
||||
self.parentFrame.menuItemsById[self.canvasAssetsWindowShow.attrDict["id"]].Enable(False)
|
||||
# }}}
|
||||
# {{{ canvasBrush(self, f, idx)
|
||||
@GuiSelectDecorator(0, "Solid brush", "Solid brush", None, None, True)
|
||||
def canvasBrush(self, f, idx):
|
||||
@ -150,7 +164,8 @@ class RoarCanvasCommandsEdit():
|
||||
self.canvasCanvasSize(self.canvasCanvasSize, 2, True), self.canvasCanvasSize(self.canvasCanvasSize, 2, False), NID_MENU_SEP,
|
||||
self.canvasBrushSize(self.canvasBrushSize, 0, True), self.canvasBrushSize(self.canvasBrushSize, 0, False), self.canvasBrushSize(self.canvasBrushSize, 1, True), self.canvasBrushSize(self.canvasBrushSize, 1, False), NID_MENU_SEP,
|
||||
self.canvasBrushSize(self.canvasBrushSize, 2, True), self.canvasBrushSize(self.canvasBrushSize, 2, False), NID_MENU_SEP,
|
||||
self.canvasBrush(self.canvasBrush, 0),
|
||||
self.canvasBrush(self.canvasBrush, 0), NID_MENU_SEP,
|
||||
self.canvasAssetsWindowHide, self.canvasAssetsWindowShow,
|
||||
),
|
||||
)
|
||||
self.toolBars = ()
|
||||
|
@ -25,7 +25,7 @@ class RoarCanvasCommandsTools():
|
||||
@GuiSelectDecorator(6, "Text", "&Text", ["toolText.png"], [wx.ACCEL_CTRL, ord("T")], False)
|
||||
def canvasTool(self, f, idx):
|
||||
def canvasTool_(event):
|
||||
self.currentTool = [ToolCircle, ToolSelectClone, ToolFill, ToolLine, ToolSelectMove, ToolRect, ToolText][idx]()
|
||||
self.lastTool, self.currentTool = self.currentTool, [ToolCircle, ToolSelectClone, ToolFill, ToolLine, ToolSelectMove, ToolRect, ToolText][idx]()
|
||||
self.parentFrame.menuItemsById[self.canvasTool.attrList[idx]["id"]].Check(True)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasTool.attrList[idx]["id"]].GetToolBar()
|
||||
toolBar.ToggleTool(self.canvasTool.attrList[idx]["id"], True)
|
||||
@ -47,5 +47,6 @@ class RoarCanvasCommandsTools():
|
||||
),
|
||||
)
|
||||
self.toolBars = ()
|
||||
self.currentTool, self.lastTool = None, None
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0
|
||||
|
@ -5,6 +5,33 @@
|
||||
#
|
||||
|
||||
from GuiWindow import GuiWindow
|
||||
from ToolObject import ToolObject
|
||||
import json, wx, sys
|
||||
|
||||
class RoarCanvasWindowDropTarget(wx.TextDropTarget):
|
||||
# {{{ OnDropText(self, x, y, data)
|
||||
def OnDropText(self, x, y, data):
|
||||
rc = False
|
||||
try:
|
||||
dropMap, dropSize = json.loads(data)
|
||||
viewRect = self.parent.GetViewStart()
|
||||
rectX, rectY = x - (x % self.parent.backend.cellSize[0]), y - (y % self.parent.backend.cellSize[1])
|
||||
mapX, mapY = int(rectX / self.parent.backend.cellSize[0] if rectX else 0), int(rectY / self.parent.backend.cellSize[1] if rectY else 0)
|
||||
mapPoint = [m + n for m, n in zip((mapX, mapY), viewRect)]
|
||||
self.parent.commands.lastTool, self.parent.commands.currentTool = self.parent.commands.currentTool, ToolObject(self.parent.canvas, mapPoint, dropMap, dropSize)
|
||||
self.parent.commands.update(toolName=self.parent.commands.currentTool.name)
|
||||
eventDc = self.parent.backend.getDeviceContext(self.parent.GetClientSize(), self.parent, viewRect)
|
||||
self.parent.applyTool(eventDc, True, None, None, self.parent.brushPos, False, False, False, self.parent.commands.currentTool, viewRect)
|
||||
rc = True
|
||||
except:
|
||||
with wx.MessageDialog(self.parent, "Error: {}".format(sys.exc_info()[1]), "", wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
return rc
|
||||
# }}}
|
||||
# {{{ __init__(self, parent)
|
||||
def __init__(self, parent):
|
||||
super().__init__(); self.parent = parent;
|
||||
# }}}
|
||||
|
||||
class RoarCanvasWindow(GuiWindow):
|
||||
# {{{ _drawPatch(self, eventDc, isCursor, patch, viewRect)
|
||||
@ -34,6 +61,9 @@ class RoarCanvasWindow(GuiWindow):
|
||||
else:
|
||||
self.commands.update(cellPos=mapPoint if mapPoint else self.brushPos)
|
||||
self.canvas.journal.end()
|
||||
if rc and (tool.__class__ == ToolObject) and (tool.toolState == tool.TS_NONE):
|
||||
self.commands.currentTool, self.commands.lastTool = self.commands.lastTool, self.commands.currentTool
|
||||
self.commands.update(toolName=self.commands.currentTool.name)
|
||||
return rc
|
||||
# }}}
|
||||
# {{{ dispatchDeltaPatches(self, deltaPatches)
|
||||
@ -125,5 +155,7 @@ class RoarCanvasWindow(GuiWindow):
|
||||
super().__init__(parent, pos, scrollStep, [w * h for w, h in zip(cellSize, size)])
|
||||
self.backend, self.canvas, self.cellSize, self.commands, self.parentFrame = backend(self.size, cellSize), canvas, cellSize, commands(self, parentFrame), parentFrame
|
||||
self.brushColours, self.brushPos, self.brushSize, self.dirty = [4, 1], [0, 0], [1, 1], False
|
||||
self.dropTarget = RoarCanvasWindowDropTarget(self)
|
||||
self.SetDropTarget(self.dropTarget)
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -7,6 +7,7 @@
|
||||
from Canvas import Canvas
|
||||
from GuiCanvasWxBackend import GuiCanvasWxBackend
|
||||
from GuiFrame import GuiFrame, NID_TOOLBAR_HSEP
|
||||
from RoarAssetsWindow import RoarAssetsWindow
|
||||
from RoarCanvasCommands import RoarCanvasCommands
|
||||
from RoarCanvasWindow import RoarCanvasWindow
|
||||
|
||||
@ -51,5 +52,7 @@ class RoarClient(GuiFrame):
|
||||
self.canvasPanel.commands.canvasTool(self.canvasPanel.commands.canvasTool, 5)(None)
|
||||
self.canvasPanel.commands.update(brushSize=self.canvasPanel.brushSize, colours=self.canvasPanel.brushColours)
|
||||
self.addWindow(self.canvasPanel, expand=True)
|
||||
self.assetsWindow = RoarAssetsWindow(GuiCanvasWxBackend, defaultCellSize, self)
|
||||
self.canvasPanel.commands.canvasAssetsWindowShow(None)
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
126
libtools/ToolObject.py
Normal file
126
libtools/ToolObject.py
Normal file
@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# ToolObject.py
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
from Tool import Tool
|
||||
|
||||
class ToolObject(Tool):
|
||||
name = "External object"
|
||||
TS_NONE = 0
|
||||
TS_SELECT = 1
|
||||
TS_TARGET = 2
|
||||
|
||||
# {{{ _dispatchSelectEvent(self, canvas, dispatchFn, eventDc, mapPoint, mouseLeftDown, mouseRightDown, selectRect, viewRect)
|
||||
def _dispatchSelectEvent(self, canvas, dispatchFn, eventDc, mapPoint, mouseLeftDown, mouseRightDown, selectRect, viewRect):
|
||||
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)
|
||||
elif mouseRightDown:
|
||||
disp, isCursor, newTargetRect = [0, 0], False, selectRect.copy()
|
||||
else:
|
||||
disp, isCursor, newTargetRect = [0, 0], True, selectRect.copy()
|
||||
dirty = self.onSelectEvent(canvas, disp, dispatchFn, eventDc, isCursor, newTargetRect, selectRect, viewRect)
|
||||
self._drawSelectRect(newTargetRect, dispatchFn, eventDc, viewRect)
|
||||
self.targetRect = newTargetRect
|
||||
return dirty
|
||||
# }}}
|
||||
# {{{ _drawSelectRect(self, rect, dispatchFn, eventDc, viewRect)
|
||||
def _drawSelectRect(self, rect, dispatchFn, eventDc, viewRect):
|
||||
rectFrame = [[rect[m[0]][n] + m[1] for n in [0, 1]] for m in [[0, -1], [1, +1]]]
|
||||
if rectFrame[0][0] > rectFrame[1][0]:
|
||||
rectFrame[0][0], rectFrame[1][0] = rectFrame[1][0], rectFrame[0][0]
|
||||
if rectFrame[0][1] > rectFrame[1][1]:
|
||||
rectFrame[0][1], rectFrame[1][1] = rectFrame[1][1], rectFrame[0][1]
|
||||
curColours = [0, 0]
|
||||
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, " "], viewRect)
|
||||
dispatchFn(eventDc, True, [rectX, rectFrame[1][1], *curColours, 0, " "], viewRect)
|
||||
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, " "], viewRect)
|
||||
dispatchFn(eventDc, True, [rectFrame[1][0], rectY, *curColours, 0, " "], viewRect)
|
||||
# }}}
|
||||
# {{{ _mouseEventTsNone(self, brushColours, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def _mouseEventTsNone(self, brushColours, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, " "], viewRect)
|
||||
return False
|
||||
# }}}
|
||||
# {{{ _mouseEventTsSelect(self, brushColours, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def _mouseEventTsSelect(self, brushColours, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
dirty = False
|
||||
if mouseLeftDown \
|
||||
and (mapPoint[0] >= (self.targetRect[0][0] - 1)) \
|
||||
and (mapPoint[0] <= (self.targetRect[1][0] + 1)) \
|
||||
and (mapPoint[1] >= (self.targetRect[0][1] - 1)) \
|
||||
and (mapPoint[1] <= (self.targetRect[1][1] + 1)):
|
||||
self.lastAtPoint, self.toolState = list(mapPoint), self.TS_TARGET
|
||||
elif mouseRightDown:
|
||||
dirty = self._dispatchSelectEvent(canvas, dispatchFn, eventDc, mapPoint, mouseLeftDown, mouseRightDown, self.targetRect, viewRect)
|
||||
self.targetRect, self.toolState = None, self.TS_NONE
|
||||
else:
|
||||
dirty = self._dispatchSelectEvent(canvas, dispatchFn, eventDc, mapPoint, mouseLeftDown, mouseRightDown, self.targetRect, viewRect)
|
||||
return dirty
|
||||
# }}}
|
||||
# {{{ _mouseEventTsTarget(self, brushColours, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def _mouseEventTsTarget(self, brushColours, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
dirty = False
|
||||
if mouseLeftDown:
|
||||
dirty = self._dispatchSelectEvent(canvas, dispatchFn, eventDc, mapPoint, mouseLeftDown, mouseRightDown, self.targetRect, viewRect)
|
||||
elif mouseRightDown:
|
||||
dirty = self._dispatchSelectEvent(canvas, dispatchFn, eventDc, mapPoint, mouseLeftDown, mouseRightDown, self.targetRect, viewRect)
|
||||
self.targetRect, self.toolState = None, self.TS_NONE
|
||||
else:
|
||||
self.toolState = self.TS_SELECT
|
||||
return True, dirty
|
||||
# }}}
|
||||
|
||||
#
|
||||
# onMouseEvent(self, brushColours, brushSize, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
dirty = False
|
||||
if self.toolState == self.TS_NONE:
|
||||
dirty = self._mouseEventTsNone(brushColours, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
elif self.toolState == self.TS_SELECT:
|
||||
dirty = self._mouseEventTsSelect(brushColours, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
elif self.toolState == self.TS_TARGET:
|
||||
dirty = self._mouseEventTsTarget(brushColours, canvas, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
else:
|
||||
return False, dirty
|
||||
return True, dirty
|
||||
|
||||
#
|
||||
# onSelectEvent(self, canvas, disp, dispatchFn, eventDc, isCursor, newTargetRect, selectRect, viewRect)
|
||||
def onSelectEvent(self, canvas, disp, dispatchFn, eventDc, isCursor, newTargetRect, selectRect, viewRect):
|
||||
dirty = False
|
||||
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 < canvas.size[1]) and (rectX < canvas.size[0]):
|
||||
cellNew = canvas.map[rectY][rectX]
|
||||
dispatchFn(eventDc, isCursor, [rectX + disp[0], rectY + disp[1], *cellNew], viewRect)
|
||||
return dirty
|
||||
|
||||
# __init__(self, canvas, mapPoint, objectMap, objectSize): initialisation method
|
||||
def __init__(self, canvas, mapPoint, objectMap, objectSize):
|
||||
super().__init__()
|
||||
self.lastAtPoint, self.srcRect = list(mapPoint), list(mapPoint)
|
||||
self.objectMap, self.objectSize = objectMap, objectSize
|
||||
self.targetRect = [list(mapPoint), [(a + b) - (0 if a == b else 1) for a, b in zip(mapPoint, objectSize)]]
|
||||
self.toolSelectMap, self.toolState = [], self.TS_SELECT
|
||||
for numRow in range((self.targetRect[1][1] - self.targetRect[0][1]) + 1):
|
||||
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
|
||||
if (rectX < canvas.size[0]) and (rectY < canvas.size[1]):
|
||||
self.toolSelectMap[numRow].append(canvas.map[rectY][rectX])
|
||||
else:
|
||||
self.toolSelectMap[numRow].append([1, 1, 0, " "])
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
Loading…
Reference in New Issue
Block a user