diff options
-rw-r--r-- | src/editorui.py | 281 | ||||
-rw-r--r-- | src/mapdata.py | 3 | ||||
-rw-r--r-- | src/ui.py | 178 |
3 files changed, 424 insertions, 38 deletions
diff --git a/src/editorui.py b/src/editorui.py index c7da106..3b9b74e 100644 --- a/src/editorui.py +++ b/src/editorui.py @@ -1,6 +1,7 @@ from common import * from math import floor, ceil import weakref +import math class KPEditorItem(QtGui.QGraphicsItem): @@ -236,33 +237,36 @@ class KPEditorObject(KPEditorItem): self._layerRef().updateCache() -class KPEditorDoodad(KPEditorItem): +class KPEditorDoodad(QtGui.QGraphicsPixmapItem): def __init__(self, doodad, layer): - KPEditorItem.__init__(self) - obj.qtItem = self + QtGui.QGraphicsPixmapItem.__init__(self) + self.setFlags( + self.ItemSendsGeometryChanges | + self.ItemIsSelectable | + self.ItemIsMovable + ) + + doodad.qtItem = self self._doodadRef = weakref.ref(doodad) self._layerRef = weakref.ref(layer) - self._updatePosition() - self._updateSize() + + self.resizing = None + self.rotating = None self.setAcceptHoverEvents(True) + self.setTransformationMode(1) - def _updatePosition(self): - self.setPos(*self._doodadRef().position) - - def _updateSize(self): - self.prepareGeometryChange() - - w,h = self._doodadRef().size + + def paint(self, painter, option, widget): - self._boundingRect = QtCore.QRectF(0, 0, w, h) - self._selectionRect = QtCore.QRectF(0, 0, w, h) - + try: + QtGui.QGraphicsPixmapItem.paint(self, painter, option, widget) + except: + pass - def paint(self, painter, option, widget): if self.isSelected(): painter.setPen(QtGui.QPen(Qt.white, 1, Qt.DotLine)) - painter.drawRect(self._selectionRect) + painter.drawRect(self.boundingRect().adjusted(-1,-1,1,1)) def _itemMoved(self, oldX, oldY, newX, newY): @@ -270,6 +274,207 @@ class KPEditorDoodad(KPEditorItem): doodad.position = (newX, newY) + def itemChange(self, change, value): + + return QtGui.QGraphicsItem.itemChange(self, change, value) + + + def resizerPortionAt(self, x, y): + + rightBound = self.boundingRect().right() - 5 + bottomBound = self.boundingRect().bottom() - 5 + + if y < 5: + if x < 5: return 1 # TOP_LEFT + elif x >= rightBound: return 2 # TOP_RIGHT + else: return 5 # TOP + + elif y >= bottomBound: + if x < 5: return 3 # BOTTOM_LEFT + elif x >= rightBound: return 4 # BOTTOM_RIGHT + else: return 6 # BOTTOM + + else: + if x < 5: return 7 # LEFT + elif x >= rightBound: return 8 # RIGHT + else: return None + + + def hoverMoveEvent(self, event): + if self._layerRef() != KP.mapScene.currentLayer: + self.setCursor(Qt.ArrowCursor) + return + + pos = event.pos() + bit = self.resizerPortionAt(pos.x(), pos.y()) + + + if (event.modifiers() == Qt.AltModifier): + + if bit: + self.setCursor(Qt.OpenHandCursor) + else: + self.setCursor(Qt.ArrowCursor) + + else: + + if bit == 1 or bit == 4: + self.setCursor(Qt.SizeFDiagCursor) + elif bit == 2 or bit == 3: + self.setCursor(Qt.SizeBDiagCursor) + elif bit == 7 or bit == 8: + self.setCursor(Qt.SizeHorCursor) + elif bit == 5 or bit == 6: + self.setCursor(Qt.SizeVerCursor) + else: + self.setCursor(Qt.ArrowCursor) + + + def mousePressEvent(self, event): + if event.button() == Qt.LeftButton: + pos = event.pos() + bit = self.resizerPortionAt(pos.x(), pos.y()) + + if self._layerRef() == KP.mapScene.currentLayer and bit: + event.accept() + + if (event.modifiers() == Qt.AltModifier): + self.rotating = self.mapToScene(pos), self._doodadRef().angle + self.setCursor(Qt.ClosedHandCursor) + return + + else: + self.resizing = bit + return + + + KPEditorItem.mousePressEvent(self, event) + + + def _tryAndResize(self, bit, mousePosition, modifiers): + + itemPos = self.mapFromScene(mousePosition) + + x2 = self.boundingRect().right() + 5.0 + y2 = self.boundingRect().bottom() + 5.0 + + + if bit == 1: + relativeScale = min([(x2 - itemPos.x()) / x2, (y2 - itemPos.y()) / y2]) + elif bit == 2: + relativeScale = min([itemPos.x() / x2, (y2 - itemPos.y()) / y2]) + elif bit == 3: + relativeScale = min([(x2 - itemPos.x()) / x2, itemPos.y() / y2]) + elif bit == 4: + relativeScale = min([itemPos.x() / x2, itemPos.y() / y2]) + elif bit == 5: + relativeScale = (y2 - itemPos.y()) / y2 + elif bit == 6: + relativeScale = itemPos.y() / y2 + elif bit == 7: + relativeScale = (x2 - itemPos.x()) / x2 + elif bit == 8: + relativeScale = itemPos.x() / x2 + else: + return False, 0 + + + if relativeScale > 1.0: + relativeScale = 1.0 + + if relativeScale < 0.1: + relativeScale = 0.1 + + self.setTransformOriginPoint(self.boundingRect().center()) + + return True, relativeScale + + + def _tryAndRotate(self, obj, mouseX, mouseY, originalPos, oldAngle, modifiers): + center = self.mapToScene(self.boundingRect().center()) + + objX = center.x() + objY = center.y() + + + origX = originalPos.x() + origY = originalPos.y() + + dy = origY - objY + dx = origX - objX + rads = math.atan2(dy, dx) + + origAngle = math.degrees(rads) + + + + + dy = mouseY - objY + dx = mouseX - objX + rads = math.atan2(dy, dx) + + angle = math.degrees(rads) + + + # Move this to ItemChange() or something at some point. + # + # if (modifiers == Qt.ShiftModifier): + # if int(angle / 45.0) == int(oldAngle / 45.0): + # return False, 0 + # else: + # angle = int(angle / 45.0) * 45.0 + + return True, angle - origAngle + oldAngle + + + def mouseMoveEvent(self, event): + + # Fucking Transform can't do independent transforms with doing matrix math directly. + # And before anyone asks, using QTransform.scale() is no good. + + if self.resizing: + obj = self._doodadRef() + scenePos = event.scenePos() + + hasChanged = False + bit = self.resizing + + hasChanged, scale = self._tryAndResize(bit, scenePos, event.modifiers()) + + if hasChanged: + obj.scale = scale + + self.setScale(scale) + + + elif self.rotating: + obj = self._doodadRef() + scenePos = event.scenePos() + + hasChanged = False + + hasChanged, angle = self._tryAndRotate(obj, scenePos.x(), scenePos.y(), self.rotating[0], self.rotating[1], event.modifiers()) + + if hasChanged: + obj.angle = angle + self.setRotation(angle) + + + else: + KPEditorItem.mouseMoveEvent(self, event) + + + def mouseReleaseEvent(self, event): + if self.resizing and event.button() == Qt.LeftButton: + self.resizing = None + elif self.rotating and event.button() == Qt.LeftButton: + self.rotating = None + + else: + KPEditorItem.mouseReleaseEvent(self, event) + + + class KPMapScene(QtGui.QGraphicsScene): def __init__(self): QtGui.QGraphicsScene.__init__(self, 0, 0, 512*24, 512*24) @@ -282,7 +487,7 @@ class KPMapScene(QtGui.QGraphicsScene): def drawBackground(self, painter, rect): - painter.fillRect(rect, Qt.white) + painter.fillRect(rect, QtGui.QColor(209, 218, 236)) areaLeft, areaTop = rect.x(), rect.y() areaWidth, areaHeight = rect.width(), rect.height() @@ -320,10 +525,9 @@ class KPMapScene(QtGui.QGraphicsScene): continue for item in toDraw: - painter.save() - painter.setWorldTransform(item.transform(), True) - painter.drawPixmap(0, 0, item.pixmap) - painter.restore() + item.paint(painter, QtGui.QStyleOptionGraphicsItem(), self) + # painter.setWorldTransform(item.transform(), True) + # painter.drawPixmap(0, 0, item.pixmap) elif isinstance(layer, KPTileLayer): left, top = layer.cacheBasePos @@ -419,8 +623,8 @@ class KPEditorWidget(QtGui.QGraphicsView): '''Called when a paint attempt is initiated''' if self.paintNext is None: return - paint = self.paintNext + if isinstance(paint, KPTileObject): clicked = self.mapToScene(event.x(), event.y()) x, y = clicked.x(), clicked.y() @@ -432,6 +636,7 @@ class KPEditorWidget(QtGui.QGraphicsView): layer = self.scene().currentLayer if not layer.visible: return + if not isinstance(layer, KPTileLayer): return obj = KPObject() obj.position = (x,y) @@ -448,6 +653,36 @@ class KPEditorWidget(QtGui.QGraphicsView): self.painting = obj self.paintingItem = item self.paintBeginPosition = (x, y) + + if isinstance(paint, QtGui.QListWidgetItem): + clicked = self.mapToScene(event.x(), event.y()) + x, y = clicked.x(), clicked.y() + if x < 0: x = 0 + if y < 0: y = 0 + + layer = self.scene().currentLayer + if not layer.visible: return + if not isinstance(layer, KPDoodadLayer): return + + size = paint.icon().availableSizes()[0] + + obj = KPDoodad() + obj.position = (x,y) + obj.size = (size.width(), size.height()) + obj.index = self.paintNextID + layer.objects.append(obj) + + item = KPEditorDoodad(obj, layer) + item.setPixmap(paint.icon().pixmap(size)) + item.setTransformOriginPoint(item.boundingRect().center()) + item.setPos(x, y) + self.scene().addItem(item) + + self.painting = obj + self.paintingItem = item + self.paintBeginPosition = (x, y) + + def _movedWhilePainting(self, event): '''Called when the mouse is moved while painting something''' diff --git a/src/mapdata.py b/src/mapdata.py index 53331c5..64a8f44 100644 --- a/src/mapdata.py +++ b/src/mapdata.py @@ -115,7 +115,8 @@ class KPDoodad(object): def __init__(self): self.position = (0,0) self.angle = 0 - self.scale = (0,0) + self.scale = 1.0 + self.index = 0 class KPDoodadLayer(KPLayer): @@ -2,6 +2,7 @@ from common import * from editorui import * +import os class KPLayerList(QtGui.QWidget): @@ -28,7 +29,8 @@ class KPLayerList(QtGui.QWidget): def setupToolbar(self, tb): - self.actAdd = tb.addAction(QtGui.QIcon(), 'Add', self.addLayer) + self.actAddTile = tb.addAction(QtGui.QIcon(), 'T+', self.addTileLayer) + self.actAddDoodad = tb.addAction(QtGui.QIcon(), 'D+', self.addDoodadLayer) self.actRemove = tb.addAction(QtGui.QIcon(), 'Remove', self.removeLayer) self.actMoveUp = tb.addAction(QtGui.QIcon(), 'Move Up', self.moveUp) self.actMoveDown = tb.addAction(QtGui.QIcon(), 'Move Down', self.moveDown) @@ -55,22 +57,26 @@ class KPLayerList(QtGui.QWidget): self.setButtonStates() - def addLayer(self): + def addTileLayer(self): from dialogs import KPTilesetChooserDialog - special = ['Doodads (Special)'] - tilesetName = KPTilesetChooserDialog.run('Choose a tileset for the new layer', special) + tilesetName = KPTilesetChooserDialog.run('Choose a tileset for the new layer') if tilesetName is None: return - if tilesetName == 0: - layer = KP.map.createNewDoodadLayer() - else: - layer = KP.map.createNewTileLayer(tilesetName) + layer = KP.map.createNewTileLayer(tilesetName) + + KP.map.appendLayer(layer) + self.setButtonStates() + + + def addDoodadLayer(self): + layer = KP.map.createNewDoodadLayer() KP.map.appendLayer(layer) self.setButtonStates() + def removeLayer(self): layer = self.selectedLayer() scene = KP.mainWindow.scene @@ -96,6 +102,135 @@ class KPLayerList(QtGui.QWidget): +class KPDoodadSelector(QtGui.QWidget): + objChanged = QtCore.pyqtSignal(int, QtGui.QListWidgetItem) + + + def __init__(self): + """Initialises the widget.""" + + QtGui.QWidget.__init__(self) + + self.layout = QtGui.QVBoxLayout() + self.layout.setSpacing(0) + + self.doodadList = QtGui.QListWidget() + self.doodadList.setViewMode(1) + self.doodadList.setWrapping(True) + self.doodadList.setDragDropMode(1) + self.doodadList.setSelectionMode(1) + self.doodadList.setResizeMode(1) + self.doodadList.setGridSize(QtCore.QSize(128, 128)) + self.doodadList.setIconSize(QtCore.QSize(100, 100)) + self.doodadList.setSpacing(4) + + + self.toolbar = QtGui.QToolBar() + self.layout.addWidget(self.toolbar) + + self.addDoodadButton = self.toolbar.addAction(QtGui.QIcon(), 'Add', self.addDoodadfromFile) + self.removeDoodadButton = self.toolbar.addAction(QtGui.QIcon(), 'Remove', self.removeDoodad) + + + self.layout.addWidget(self.doodadList) + + self.setLayout(self.layout) + + self.nextIndex = 0 + + self.doodadList.currentItemChanged.connect(self.handleRowChanged) + + + def keyPressEvent(self, event): + + self.doodadList.keyPressEvent(event) + + if event.key() == 0x1000003: + + msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, + "Delete Doodad?", "Are you sure you want to delete this doodad? This action cannot be undone.", + QtGui.QMessageBox.NoButton, self) + msgBox.addButton("Delete", QtGui.QMessageBox.AcceptRole) + msgBox.addButton("Cancel", QtGui.QMessageBox.RejectRole) + if msgBox.exec_() == QtGui.QMessageBox.AcceptRole: + self.removeDoodad() + + + def addDoodad(self, image, name): + """Takes a name and a QPixmap and turns it into an icon, then goes ahead and makes a doodad. + Doodads are QListWidget items with an index number as Qt.UserRole #32.""" + + + doodie = QtGui.QListWidgetItem(QtGui.QIcon(image), name) + doodie.setSizeHint(QtCore.QSize(128,128)) + doodie.setData(32, self.nextIndex) + doodie.setToolTip(name) + doodie.setTextAlignment(Qt.AlignBottom | Qt.AlignHCenter) + + self.doodadList.addItem(doodie) + self.nextIndex += 1 + + + def addDoodadfromFile(self): + """Asks the user for files to load in as doodads.""" + + log = QtGui.QFileDialog() + log.setFileMode(3); + + files = QtGui.QFileDialog.getOpenFileNames(self, + "Choose an image or several image files.", "", + "Images (*.png *.jpeg *.jpg *.bmp)") + if files: + for image in files: + name = os.path.basename(unicode(image)).split('.')[0] + + pix = QtGui.QPixmap() + pix.load(image) + + self.addDoodad(pix, name) + + + def removeDoodad(self): + """Removes selected doodad""" + + item = self.doodadList.currentRow() + + if item != -1: + self.doodadList.takeItem(item) + + + def getDoodad(self, index): + """Retrieves a doodad by index""" + + for item in self.doodadList.items(): + if item.data(32) == index: + return item + + + def getDoodadImage(self, index, width, height): + """Retrieves a doodad pixmap by index""" + + for item in self.doodadList.items(): + if item.data(32) == index: + return item.icon().pixmap(width, height) + + def getDoodads(self): + """Returns a list of all doodads. + + Name can be retrieved with doodad.text() + Image with doodad.icon().pixmap(doodad.icon().availableSizes()[0]) + Index with doodad.data(32)""" + + return self.doodadList.items() + + + @QtCore.pyqtSlot(QtGui.QListWidgetItem) + def handleRowChanged(self, current): + """Throws a signal emitting the current object when changed""" + + self.objChanged.emit(current.data(32), current) + + class KPObjectSelector(QtGui.QWidget): @@ -246,11 +381,6 @@ class KPObjectSelector(QtGui.QWidget): - - - - - class KPMainWindow(QtGui.QMainWindow): def __init__(self): QtGui.QMainWindow.__init__(self) @@ -285,16 +415,31 @@ class KPMainWindow(QtGui.QMainWindow): self.objectSelectorDock = QtGui.QDockWidget('Objects') self.objectSelectorDock.setWidget(self.objectSelector) + self.doodadSelector = KPDoodadSelector() + self.doodadSelector.objChanged.connect(self.handleSelectedDoodadChanged) + + self.doodadSelectorDock = QtGui.QDockWidget('Doodads') + self.doodadSelectorDock.setWidget(self.doodadSelector) + self.doodadSelectorDock.hide() self.addDockWidget(Qt.RightDockWidgetArea, self.layerListDock) self.addDockWidget(Qt.RightDockWidgetArea, self.objectSelectorDock) + self.addDockWidget(Qt.RightDockWidgetArea, self.doodadSelectorDock) @QtCore.pyqtSlot(KPLayer) def handleSelectedLayerChanged(self, layer): self.scene.setCurrentLayer(layer) - self.objectSelector.setModel(KP.map.loadedTilesets[layer.tileset].getModel()) + if isinstance(layer, KPDoodadLayer): + self.objectSelectorDock.hide() + self.doodadSelectorDock.show() + + else: + self.doodadSelectorDock.hide() + self.objectSelectorDock.show() + + self.objectSelector.setModel(KP.map.loadedTilesets[layer.tileset].getModel()) @QtCore.pyqtSlot(int, KPTileObject) @@ -303,5 +448,10 @@ class KPMainWindow(QtGui.QMainWindow): self.editor.paintNextID = index + @QtCore.pyqtSlot(int, QtGui.QListWidgetItem) + def handleSelectedDoodadChanged(self, index, obj): + self.editor.paintNext = obj + self.editor.paintNextID = index + |