roar/libroar/RoarAssetsWindow.py
Lucio Andrés Illanes Albornoz 33e5645736 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.
2019-09-12 12:49:53 +02:00

327 lines
17 KiB
Python

#!/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