From 30188c779e1b419f6818596a29f7f3c7edad42bb Mon Sep 17 00:00:00 2001 From: Treeki Date: Mon, 21 Nov 2011 15:25:54 +0100 Subject: some refactoring of editorui and co --- src/editorui/__init__.py | 0 src/editorui/doodads.py | 248 +++++++++++++++++++++++++ src/editorui/editorcommon.py | 77 ++++++++ src/editorui/editormain.py | 419 +++++++++++++++++++++++++++++++++++++++++++ src/editorui/objects.py | 182 +++++++++++++++++++ src/editorui/paths.py | 319 ++++++++++++++++++++++++++++++++ 6 files changed, 1245 insertions(+) create mode 100644 src/editorui/__init__.py create mode 100644 src/editorui/doodads.py create mode 100644 src/editorui/editorcommon.py create mode 100644 src/editorui/editormain.py create mode 100644 src/editorui/objects.py create mode 100644 src/editorui/paths.py (limited to 'src/editorui') diff --git a/src/editorui/__init__.py b/src/editorui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/editorui/doodads.py b/src/editorui/doodads.py new file mode 100644 index 0000000..5a02248 --- /dev/null +++ b/src/editorui/doodads.py @@ -0,0 +1,248 @@ +from common import * +from editorcommon import * +import weakref +from math import floor, ceil +import math + + +class KPEditorDoodad(KPEditorItem): + SNAP_TO = (12,12) + + def __init__(self, doodad, layer): + KPEditorItem.__init__(self) + + doodad.qtItem = self + self._doodadRef = weakref.ref(doodad) + self._layerRef = weakref.ref(layer) + + # TODO: refactor this to store source doodad data under KP.map + sourceItem = KP.mainWindow.doodadSelector.getDoodad(doodad.index) + self._sourceRef = weakref.ref(sourceItem) + + self.resizing = None + self.rotating = None + + self._updatePixmap() + self._updatePosition() + self._updateSize() + + self.setAcceptHoverEvents(True) + + if not hasattr(KPEditorDoodad, 'SELECTION_PEN'): + KPEditorDoodad.SELECTION_PEN = QtGui.QPen(Qt.red, 1, Qt.DotLine) + + + + def _updatePixmap(self): + source = self._sourceRef() + pixmap = source.icon().pixmap(source.icon().availableSizes()[0]) + + self.prepareGeometryChange() + w, h = pixmap.width(), pixmap.height() + + self.pixmap = pixmap + + + def _updatePosition(self): + # NOTE: EditorDoodads originate at the centre, not the top left like the others + doodad = self._doodadRef() + x,y = doodad.position + w,h = doodad.size + self.setPos(x+floor(w/2.0), y+floor(h/2.0)) + + + def _updateSize(self): + self.prepareGeometryChange() + + w,h = self._doodadRef().size + + self._boundingRect = QtCore.QRectF(-w/2, -h/2, w, h) + self._selectionRect = self._boundingRect.adjusted(0, 0, -1, -1) + + + def _updateTransform(self): + doodad = self._doodadRef() + + self.setRotation(doodad.angle) + + + def paint(self, painter, option, widget): + if self.isSelected(): + painter.setPen(self.SELECTION_PEN) + painter.drawRect(self._selectionRect) + + + def _itemMoved(self, oldX, oldY, newX, newY): + doodad = self._doodadRef() + w,h = doodad.size + doodad.position = [newX-floor(w/2.0), newY-floor(h/2.0)] + + + 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.ShiftModifier): + + 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.ShiftModifier): + self.rotating = self.mapToScene(pos), self._doodadRef().angle + self.setCursor(Qt.ClosedHandCursor) + return + + else: + x, xSide, y, ySide = False, None, False, None + + if bit == 1 or bit == 7 or bit == 3: # left + x, xSide = True, 1 + + elif bit == 2 or bit == 4 or bit == 8: # right + x, xSide = True, 0 + + if bit == 1 or bit == 2 or bit == 5: # top + y, ySide = True, 1 + + elif bit == 3 or bit == 4 or bit == 6: # bottom + y, ySide = True, 0 + + self._updateSize() + self.resizing = (x, xSide, y, ySide) + return + + + KPEditorItem.mousePressEvent(self, event) + + + def _tryAndResize(self, obj, axisIndex, mousePosition, stationarySide): + + newSize = abs(mousePosition) * 2 + + if newSize < 10: + return False + + obj.size[axisIndex] = newSize + + return True + + + 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. + finalAngle = angle - origAngle + oldAngle + + if (modifiers & Qt.ControlModifier): + finalAngle = int(finalAngle / 45.0) * 45.0 + + return True, finalAngle + + + def mouseMoveEvent(self, event): + if self.resizing: + obj = self._doodadRef() + + hasChanged = False + resizeX, xSide, resizeY, ySide = self.resizing + + if resizeX: + hasChanged |= self._tryAndResize(obj, 0, event.pos().x(), xSide) + if resizeY: + hasChanged |= self._tryAndResize(obj, 1, event.pos().y(), ySide) + + if hasChanged: + # Doodads aren't supposed to snap, they're all free flowing like the wind. + self._updateSize() + + + elif self.rotating: + obj = self._doodadRef() + scenePos = event.scenePos() + self.setTransformOriginPoint(self.boundingRect().center()) + + 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._updateTransform() + + + else: + KPEditorItem.mouseMoveEvent(self, event) + + + def mouseReleaseEvent(self, event): + if self.resizing and event.button() == Qt.LeftButton: + self.resizing = None + # self._doodadRef().position = [self.x(), self.y()] + + elif self.rotating and event.button() == Qt.LeftButton: + self.rotating = None + + else: + KPEditorItem.mouseReleaseEvent(self, event) + + + def remove(self, withItem=False): + doodad = self._doodadRef() + layer = self._layerRef() + + layer.objects.remove(doodad) + + if withItem: + self.scene().removeItem(self) + + + diff --git a/src/editorui/editorcommon.py b/src/editorui/editorcommon.py new file mode 100644 index 0000000..945375e --- /dev/null +++ b/src/editorui/editorcommon.py @@ -0,0 +1,77 @@ +from common import * + +class KPEditorItem(QtGui.QGraphicsItem): + def __init__(self): + QtGui.QGraphicsItem.__init__(self) + self.setFlags( + self.ItemSendsGeometryChanges | + self.ItemIsSelectable | + self.ItemIsMovable + ) + + self.ignoreMovement = False + self.overrideSnap = False + + def itemChange(self, change, value): + if change == self.ItemPositionChange and not self.ignoreMovement: + currentX, currentY = self.x(), self.y() + + newpos = value.toPyObject() + + x, y = newpos.x(), newpos.y() + + if self.overrideSnap: + snapX, snapY = 1, 1 + else: + # snap the item + snapX, snapY = self.SNAP_TO + x = int((x + (snapX/2)) / snapX) * snapX + y = int((y + (snapY/2)) / snapY) * snapY + + if x < 0: x = 0 + if x >= (12288+snapX): x = (12288+snapX-1) + if y < 0: y = 0 + if y >= (12288+snapY): y = (12288+snapY-1) + + if x != currentX or y != currentY: + self._itemMoved(currentX, currentY, x, y) + + newpos.setX(x) + newpos.setY(y) + return newpos + + return QtGui.QGraphicsItem.itemChange(self, change, value) + + def boundingRect(self): + return self._boundingRect + + + def resizerPortionAt(self, x, y, originX=0, originY=0): + try: + leftBound, topBound = originX+5, originY+5 + rightBound, bottomBound = self._resizerEndXY + except AttributeError: + rect = self._boundingRect + leftBound, topBound = rect.x() + 5, rect.y() + 5 + rightBound, bottomBound = rect.right() - 5, rect.bottom() - 5 + + if y < topBound: + if x < leftBound: return 1 # TOP_LEFT + elif x >= rightBound: return 2 # TOP_RIGHT + else: return 5 # TOP + + elif y >= bottomBound: + if x < leftBound: return 3 # BOTTOM_LEFT + elif x >= rightBound: return 4 # BOTTOM_RIGHT + else: return 6 # BOTTOM + + else: + if x < leftBound: return 7 # LEFT + elif x >= rightBound: return 8 # RIGHT + else: return None + + + def _itemMoved(self, oldX, oldY, newX, newY): + pass + + diff --git a/src/editorui/editormain.py b/src/editorui/editormain.py new file mode 100644 index 0000000..d11642a --- /dev/null +++ b/src/editorui/editormain.py @@ -0,0 +1,419 @@ +from common import * +from math import floor, ceil + +from objects import * +from doodads import * +from paths import * + +class KPMapScene(QtGui.QGraphicsScene): + def __init__(self): + QtGui.QGraphicsScene.__init__(self, 0, 0, 512*24, 512*24) + + # todo: handle selectionChanged + # todo: look up why I used setItemIndexMethod(self.NoIndex) in Reggie + + self.currentLayer = None + KP.mapScene = self + + + def drawBackground(self, painter, rect): + painter.fillRect(rect, QtGui.QColor(209, 218, 236)) + + areaLeft, areaTop = rect.x(), rect.y() + areaWidth, areaHeight = rect.width(), rect.height() + areaRight, areaBottom = areaLeft+areaWidth, areaTop+areaHeight + + areaLeftT = floor(areaLeft / 24) + areaTopT = floor(areaTop / 24) + areaRightT = ceil(areaRight / 24) + areaBottomT = ceil(areaBottom / 24) + + # compile a list of doodads + visibleDoodadsByLayer = {} + + for obj in self.items(rect): + if not isinstance(obj, KPEditorDoodad): continue + + layer = obj._layerRef() + + try: + doodadList = visibleDoodadsByLayer[layer] + except KeyError: + doodadList = [] + visibleDoodadsByLayer[layer] = doodadList + + doodadList.append(obj) + + # now draw everything! + for layer in reversed(KP.map.layers): + if not layer.visible: continue + + if isinstance(layer, KPDoodadLayer): + try: + toDraw = visibleDoodadsByLayer[layer] + except KeyError: + continue + + for item in reversed(toDraw): + painter.save() + painter.setWorldTransform(item.sceneTransform(), True) + w, h = item._doodadRef().size + p = item._boundingRect + painter.drawPixmap(p.x(), p.y(), p.width(), p.height(), item.pixmap) + painter.restore() + + elif isinstance(layer, KPTileLayer): + left, top = layer.cacheBasePos + width, height = layer.cacheSize + right, bottom = left+width, top+height + + if width == 0 and height == 0: continue + + if right < areaLeftT: continue + if left > areaRightT: continue + + if bottom < areaTopT: continue + if top > areaBottomT: continue + + # decide how much of the layer we'll actually draw + drawLeft = int(max(areaLeftT, left)) + drawRight = int(min(areaRightT, right)) + + drawTop = int(max(areaTopT, top)) + drawBottom = int(min(areaBottomT, bottom)) + + srcY = drawTop - top + destY = drawTop * 24 + + baseSrcX = drawLeft - left + baseDestX = drawLeft * 24 + + rows = layer.cache + tileset = KP.map.loadedTilesets[layer.tileset] + tileList = tileset.tiles + + for y in xrange(drawTop, drawBottom): + srcX = baseSrcX + destX = baseDestX + row = rows[srcY] + + for x in xrange(drawLeft, drawRight): + tile = row[srcX] + if tile != -1: + painter.drawPixmap(destX, destY, tileList[tile]) + + srcX += 1 + destX += 24 + + srcY += 1 + destY += 24 + + + def setCurrentLayer(self, layer): + if self.currentLayer is not None: + self.currentLayer.setActivated(False) + + self.currentLayer = layer + self.currentLayer.setActivated(True) + + +class KPEditorWidget(QtGui.QGraphicsView): + def __init__(self, scene, parent=None): + QtGui.QGraphicsView.__init__(self, scene, parent) + + self.setRenderHints(QtGui.QPainter.Antialiasing) + + self.setAlignment(Qt.AlignLeft | Qt.AlignTop) + self.setDragMode(self.RubberBandDrag) + + self.xScrollBar = QtGui.QScrollBar(Qt.Horizontal, parent) + self.setHorizontalScrollBar(self.xScrollBar) + + self.yScrollBar = QtGui.QScrollBar(Qt.Vertical, parent) + self.setVerticalScrollBar(self.yScrollBar) + + self.centerOn(0,0) + + # set up stuff for painting + self.paintNext = None + self.paintNextID = None + self._resetPaintVars() + + def _resetPaintVars(self): + self.painting = None + self.paintingItem = None + self.paintBeginPosition = None + + def _tryToPaint(self, event): + '''Called when a paint attempt is initiated''' + + paint = self.paintNext + layer = self.scene().currentLayer + if not layer.visible: return + + if isinstance(layer, KPTileLayer): + if paint is None: return + + clicked = self.mapToScene(event.x(), event.y()) + x, y = clicked.x(), clicked.y() + if x < 0: x = 0 + if y < 0: y = 0 + + x = int(x / 24) + y = int(y / 24) + + obj = KPObject() + obj.position = (x,y) + obj.size = (1,1) + obj.tileset = layer.tileset + obj.kind = paint + obj.updateCache() + layer.objects.append(obj) + layer.updateCache() + + item = KPEditorObject(obj, layer) + self.scene().addItem(item) + + self.painting = obj + self.paintingItem = item + self.paintBeginPosition = (x, y) + + elif isinstance(layer, KPDoodadLayer): + if paint is None: return + + clicked = self.mapToScene(event.x(), event.y()) + x, y = clicked.x(), clicked.y() + if x < 0: x = 0 + if y < 0: y = 0 + + obj = KPDoodad() + obj.position = [x,y] + obj.index = self.paintNextID + obj.setDefaultSize() + layer.objects.append(obj) + + item = KPEditorDoodad(obj, layer) + self.scene().addItem(item) + + self.painting = obj + self.paintingItem = item + self.paintBeginPosition = (x, y) + + elif isinstance(layer, KPPathLayer): + # decide what's under the mouse + clicked = self.mapToScene(event.x(), event.y()) + x, y = clicked.x(), clicked.y() + itemsUnder = self.scene().items(clicked) + + for item in itemsUnder: + if isinstance(item, KPEditorNode): + # Paint a path to this node (if one is selected) + sourceItem, sourceNode = None, None + selected = self.scene().selectedItems() + + for selItem in selected: + if isinstance(item, KPEditorNode) and selItem != item: + sourceItem = selItem + sourceNode = selItem._nodeRef() + break + + if sourceItem is None: return + + # Make sure that no path already exists between these nodes + destNode = item._nodeRef() + + for pathToCheck in sourceNode.exits: + if pathToCheck._startNodeRef() == destNode: + return + if pathToCheck._endNodeRef() == destNode: + return + + # No node can have more than four paths, because there are only + # four directions registered by a Wiimote DPad. + + if len(sourceNode.exits) > 3: + return + + if len(destNode.exits) > 3: + return + + path = KPPath(sourceNode, destNode) + + KP.map.pathLayer.paths.append(path) + + item = KPEditorPath(path) + self.scene().addItem(item) + + return + + elif isinstance(item, KPEditorPath): + # Split this path into two... at this point + + origPath = item._pathRef() + + node = KPNode() + node.position = (x - 12, y - 12) + KP.map.pathLayer.nodes.append(node) + + # Start node => Original path => New node => New path => End node + + endNode = origPath._endNodeRef() + + origPath.setEnd(node) + origPath.qtItem.updatePosition() + + nodeItem = KPEditorNode(node) + self.scene().addItem(nodeItem) + self.scene().addItem(nodeItem.buttonProxy) + self.scene().addItem(nodeItem.stageProxy) + self.scene().addItem(nodeItem.worldProxy) + + self.painting = node + self.paintingItem = item + self.paintBeginPosition = (x - 12, y - 12) + + newPath = KPPath(node, endNode, origPath) + KP.map.pathLayer.paths.append(newPath) + + pathItem = KPEditorPath(newPath) + self.scene().addItem(pathItem) + + return + + # Paint a new node + node = KPNode() + node.position = (x - 12, y - 12) + KP.map.pathLayer.nodes.append(node) + + item = KPEditorNode(node) + self.scene().addItem(item) + self.scene().addItem(item.buttonProxy) + self.scene().addItem(item.stageProxy) + self.scene().addItem(item.worldProxy) + + # Paint a path to this node (if one is selected) + sourceItem, sourceNode = None, None + selected = self.scene().selectedItems() + + for selItem in selected: + if isinstance(item, KPEditorNode) and selItem != item: + sourceItem = selItem + sourceNode = selItem._nodeRef() + break + + # No node can have more than four paths, because there are only + # four directions registered by a Wiimote DPad. + + if not sourceItem is None: + if len(sourceNode.exits) > 3: + return + + # There, now you can draw paths easily in a row. + path = KPPath(sourceNode, node) + + KP.map.pathLayer.paths.append(path) + + pathitem = KPEditorPath(path) + self.scene().addItem(pathitem) + + + # Switch the selection to the recently drawn node, so you can keep on rolling. + self.scene().clearSelection() + item.setSelected(True) + + self.painting = node + self.paintingItem = item + self.paintBeginPosition = (x - 12, y - 12) + + + + def _movedWhilePainting(self, event): + '''Called when the mouse is moved while painting something''' + + obj = self.painting + item = self.paintingItem + + if isinstance(obj, KPObject): + clicked = self.mapToScene(event.x(), event.y()) + x, y = clicked.x(), clicked.y() + if x < 0: x = 0 + if y < 0: y = 0 + + x = int(x / 24) + y = int(y / 24) + + beginX, beginY = self.paintBeginPosition + + if x >= beginX: + objX = beginX + width = x - beginX + 1 + else: + objX = x + width = beginX - x + 1 + + if y >= beginY: + objY = beginY + height = y - beginY + 1 + else: + objY = y + height = beginY - y + 1 + + currentX, currentY = obj.position + currentWidth, currentHeight = obj.size + + # update everything if changed + changed = False + + if currentX != objX or currentY != objY: + obj.position = (objX, objY) + item._updatePosition() + changed = True + + if currentWidth != width or currentHeight != height: + obj.size = (width, height) + obj.updateCache() + item._updateSize() + changed = True + + if not changed: return + + item._layerRef().updateCache() + + + def mousePressEvent(self, event): + if event.button() == Qt.RightButton: + self._tryToPaint(event) + event.accept() + + else: + QtGui.QGraphicsView.mousePressEvent(self, event) + + + def mouseMoveEvent(self, event): + if event.buttons() == Qt.RightButton and self.painting: + self._movedWhilePainting(event) + event.accept() + + else: + QtGui.QGraphicsView.mouseMoveEvent(self, event) + + + def keyPressEvent(self, event): + if event.key() == QtCore.Qt.Key_Delete or event.key() == QtCore.Qt.Key_Backspace: + scene = self.scene() + + selection = scene.selectedItems() + if len(selection) > 0: + for obj in selection: + obj.setSelected(False) + obj.remove(True) + scene.update() + self.update() + return + + else: + QtGui.QGraphicsView.keyPressEvent(self, event) + + + + diff --git a/src/editorui/objects.py b/src/editorui/objects.py new file mode 100644 index 0000000..1ae46f7 --- /dev/null +++ b/src/editorui/objects.py @@ -0,0 +1,182 @@ +from common import * +from editorcommon import * +import weakref + +class KPEditorObject(KPEditorItem): + SNAP_TO = (24,24) + + def __init__(self, obj, layer): + KPEditorItem.__init__(self) + obj.qtItem = self + self._objRef = weakref.ref(obj) + self._layerRef = weakref.ref(layer) + self._updatePosition() + self._updateSize() + + self.setAcceptHoverEvents(True) + + self.resizing = None + + if not hasattr(KPEditorObject, 'SELECTION_PEN'): + KPEditorObject.SELECTION_PEN = QtGui.QPen(Qt.white, 1, Qt.DotLine) + + # I don't bother setting the ZValue because it doesn't quite matter: + # only one layer's objects are ever clickable, and drawBackground takes + # care of the layered drawing + + def _updatePosition(self): + self.ignoreMovement = True + x,y = self._objRef().position + self.setPos(x*24, y*24) + self.ignoreMovement = False + + def _updateSize(self): + self.prepareGeometryChange() + + obj = self._objRef() + w,h = obj.size + + self._boundingRect = QtCore.QRectF(0, 0, w*24, h*24) + self._selectionRect = QtCore.QRectF(0, 0, w*24-1, h*24-1) + + self._resizerEndXY = (w*24-5, h*24-5) + + + def paint(self, painter, option, widget): + if self.isSelected(): + painter.setPen(self.SELECTION_PEN) + painter.drawRect(self._selectionRect) + + + 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 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() + + x, xSide, y, ySide = False, None, False, None + + if bit == 1 or bit == 7 or bit == 3: + x, xSide = True, 1 + elif bit == 2 or bit == 4 or bit == 8: + x, xSide = True, 0 + + if bit == 1 or bit == 2 or bit == 5: + y, ySide = True, 1 + elif bit == 3 or bit == 4 or bit == 6: + y, ySide = True, 0 + + self.resizing = (x, xSide, y, ySide) + return + + KPEditorItem.mousePressEvent(self, event) + + + def _tryAndResize(self, obj, axisIndex, mousePosition, stationarySide): + objPosition = obj.position[axisIndex] + objSize = obj.size[axisIndex] + + if stationarySide == 0: + # Resize the right/bottom side + relativeMousePosition = mousePosition - objPosition + newSize = relativeMousePosition + 1 + if newSize == objSize or newSize < 1: + return False + + if axisIndex == 1: + obj.size = (obj.size[0], newSize) + else: + obj.size = (newSize, obj.size[1]) + + else: + # Resize the left/top side + rightSide = objPosition + objSize - 1 + newLeftSide = mousePosition + + newPosition = newLeftSide + newSize = rightSide - newLeftSide + 1 + + if newSize < 1: + return False + if newPosition == objPosition and newSize == objSize: + return False + + if axisIndex == 1: + obj.position = (obj.position[0], newPosition) + obj.size = (obj.size[0], newSize) + else: + obj.position = (newPosition, obj.position[1]) + obj.size = (newSize, obj.size[1]) + + return True + + + def mouseMoveEvent(self, event): + if self.resizing: + obj = self._objRef() + scenePos = event.scenePos() + + hasChanged = False + resizeX, xSide, resizeY, ySide = self.resizing + + if resizeX: + hasChanged |= self._tryAndResize(obj, 0, int(scenePos.x() / 24), xSide) + if resizeY: + hasChanged |= self._tryAndResize(obj, 1, int(scenePos.y() / 24), ySide) + + if hasChanged: + obj.updateCache() + self._layerRef().updateCache() + self._updatePosition() + self._updateSize() + + else: + KPEditorItem.mouseMoveEvent(self, event) + + + def mouseReleaseEvent(self, event): + if self.resizing and event.button() == Qt.LeftButton: + self.resizing = None + else: + KPEditorItem.mouseReleaseEvent(self, event) + + + def _itemMoved(self, oldX, oldY, newX, newY): + obj = self._objRef() + obj.position = (newX/24, newY/24) + self._layerRef().updateCache() + + + def remove(self, withItem=False): + obj = self._objRef() + layer = self._layerRef() + + layer.objects.remove(obj) + layer.updateCache() + + if withItem: + self.scene().removeItem(self) + + diff --git a/src/editorui/paths.py b/src/editorui/paths.py new file mode 100644 index 0000000..4dca31e --- /dev/null +++ b/src/editorui/paths.py @@ -0,0 +1,319 @@ +from common import * +from editorcommon import * +import weakref + +class KPEditorNode(KPEditorItem): + SNAP_TO = (12,12) + + + class toggleButton(QtGui.QPushButton): + + stateToggled = QtCore.pyqtSignal(int) + + + def __init__(self): + QtGui.QPushButton.__init__(self) + + self.setIconSize(QtCore.QSize(24, 24)) + self.setFixedSize(24, 24) + + self.iconList = [QtGui.QIcon("Resources/Through.png"), + QtGui.QIcon("Resources/Level.png"), + QtGui.QIcon("Resources/Stop.png")] + + self.state = 0 + + self.setPalette(QtGui.QPalette(QtGui.QColor(0,0,0,0))) + + self.released.connect(self.toggle) + + + def toggle(self): + + self.state += 1 + if self.state == 3: + self.state = 0 + + self.stateToggled.emit(self.state) + + + def paintEvent(self, event): + + painter = QtGui.QPainter(self) + + painter.setBackgroundMode(Qt.TransparentMode) + painter.setBrush(QtGui.QColor(0,0,0,0)) + painter.setPen(QtGui.QColor(0,0,0,0)) + + if self.isDown(): + self.iconList[self.state].paint(painter, self.contentsRect(), Qt.AlignCenter, QtGui.QIcon.Disabled) + else: + self.iconList[self.state].paint(painter, self.contentsRect()) + painter.end() + + + class hiddenProxy(QtGui.QGraphicsProxyWidget): + def __init__(self, button): + QtGui.QGraphicsProxyWidget.__init__(self) + + self.setWidget(button) + self.setZValue(self.zValue()+1000) + self.hide() + + + class levelSlotSpinner(QtGui.QSpinBox): + + def __init__(self): + QtGui.QSpinBox.__init__(self) + + self.setRange(1, 99) + + self.setPalette(QtGui.QPalette(QtGui.QColor(0,0,0,0))) + + + + def __init__(self, node): + KPEditorItem.__init__(self) + + node.qtItem = self + self._nodeRef = weakref.ref(node) + + self.setZValue(101) + + self._boundingRect = QtCore.QRectF(-24, -24, 48, 48) + self._tinyRect = QtCore.QRectF(-12, -12, 24, 24) + + + if not hasattr(KPEditorNode, 'SELECTION_PEN'): + KPEditorNode.SELECTION_PEN = QtGui.QPen(Qt.blue, 1, Qt.DotLine) + + self.button = self.toggleButton() + self.buttonProxy = self.hiddenProxy(self.button) + self.button.stateToggled.connect(self.stateChange) + + + self.world = self.levelSlotSpinner() + self.worldProxy = self.hiddenProxy(self.world) + self.world.valueChanged.connect(self.worldChange) + + self.stage = self.levelSlotSpinner() + self.stageProxy = self.hiddenProxy(self.stage) + self.stage.valueChanged.connect(self.stageChange) + + + self._updatePosition() + + + + @QtCore.pyqtSlot(int) + def stateChange(self, state): + + node = self._nodeRef() + + if state == 1: + node.level = [1, 1] + self.world.setValue(node.level[0]) + self.stage.setValue(node.level[1]) + + elif state == 2: + node.isStop = True + node.level = [0,0] + + else: + node.isStop = False + node.level = [0,0] + + self.update() + + + @QtCore.pyqtSlot(int) + def worldChange(self, world): + + node = self._nodeRef() + node.level[0] = world + + + @QtCore.pyqtSlot(int) + def stageChange(self, stage): + + node = self._nodeRef() + node.level[1] = stage + + + + def _updatePosition(self): + node = self._nodeRef() + x, y = node.position + self.setPos(x+12, y+12) + self.buttonProxy.setPos(self.x()+12, self.y()-24) + self.worldProxy.setPos(self.x()-42, self.y()+24) + self.stageProxy.setPos(self.x()+6, self.y()+24) + + + def _itemMoved(self, oldX, oldY, newX, newY): + node = self._nodeRef() + node.position = (newX-12, newY-12) + + self.buttonProxy.setPos(newX+12, newY-24) + self.worldProxy.setPos(newX-42, newY+24) + self.stageProxy.setPos(newX+6, newY+24) + + for exit in node.exits: + exit.qtItem.updatePosition() + + + def paint(self, painter, option, widget): + + node = self._nodeRef() + + selectionRect = None + + if node.level != [0,0]: + painter.setBrush(QtGui.QColor(0, 0, 0, 0)) + painter.setPen(QtGui.QColor(0, 0, 0, 0)) + painter.drawPixmap(self._boundingRect.topLeft(), QtGui.QPixmap("Resources/BlackLevel.png")) + selectionRect = self._boundingRect.adjusted(-1,-1,1,1) + + elif node.isStop: + brush = QtGui.QBrush(QtGui.QColor(255, 220, 220)) + painter.setPen(QtGui.QColor(255, 255, 255)) + painter.setBrush(brush) + painter.drawEllipse(self._tinyRect) + selectionRect = self._tinyRect.adjusted(-1,-1,1,1) + + else: + brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) + painter.setPen(QtGui.QColor(255, 255, 255)) + painter.setBrush(brush) + painter.drawEllipse(self._tinyRect) + selectionRect = self._tinyRect.adjusted(-1,-1,1,1) + + + if self.isSelected(): + painter.setPen(self.SELECTION_PEN) + painter.setBrush(QtGui.QColor(0,0,0,0)) + painter.drawEllipse(selectionRect) + + self.buttonProxy.show() + + if node.level != [0,0]: + self.worldProxy.show() + self.stageProxy.show() + + else: + self.worldProxy.hide() + self.stageProxy.hide() + + else: + self.buttonProxy.hide() + self.worldProxy.hide() + self.stageProxy.hide() + + + + + def remove(self, withItem=False): + node = self._nodeRef() + layer = KP.map.pathLayer + + layer.nodes.remove(node) + + self.scene().removeItem(self.buttonProxy) + self.scene().removeItem(self.stageProxy) + self.scene().removeItem(self.worldProxy) + + if len(node.exits) == 2: + # let's try to join the two! + pathOne, pathTwo = node.exits + + start1, end1 = pathOne._startNodeRef(), pathOne._endNodeRef() + start2, end2 = pathTwo._startNodeRef(), pathTwo._endNodeRef() + + if start1 == node: + start = end1 + else: + start = start1 + + if start2 == node: + end = end2 + else: + end = start2 + + # make sure no path already exists between these nodes + nope = False + + for pathToCheck in start.exits: + if pathToCheck._startNodeRef() == end: + nope = True + elif pathToCheck._endNodeRef() == end: + nope = True + + if not nope: + joinedPath = KPPath(start, end, pathOne) + layer.paths.append(joinedPath) + item = KPEditorPath(joinedPath) + self.scene().addItem(item) + + for path in (pathOne, pathTwo): + path.qtItem.remove(True) + else: + # we can't join them so just nuke them + for exit in node.exits[:]: + exit.qtItem.remove(True) + + if withItem: + self.scene().removeItem(self) + + + +class KPEditorPath(QtGui.QGraphicsLineItem): + def __init__(self, path): + QtGui.QGraphicsLineItem.__init__(self) + + self.setFlag(self.ItemIsSelectable, True) + + self.setZValue(100) + + startNode = path._startNodeRef().qtItem + endNode = path._endNodeRef().qtItem + + self._startNodeRef = weakref.ref(startNode) + self._endNodeRef = weakref.ref(endNode) + self._pathRef = weakref.ref(path) + + path.qtItem = self + + self.updatePosition() + + if not hasattr(KPEditorPath, 'PEN'): + KPEditorPath.BRUSH = QtGui.QBrush(QtGui.QColor(255, 255, 255, 140)) + KPEditorPath.PEN = QtGui.QPen(KPEditorPath.BRUSH, 8, Qt.SolidLine, Qt.RoundCap) + self.setPen(KPEditorPath.PEN) + + + def updatePosition(self): + path = self._pathRef() + + x1, y1 = path._startNodeRef().position + x2, y2 = path._endNodeRef().position + + self.setLine(QtCore.QLineF(x1+12, y1+12, x2+12, y2+12)) + + + def remove(self, withItem=False): + path = self._pathRef() + layer = KP.map.pathLayer + + layer.paths.remove(path) + + for ref in (self._startNodeRef, self._endNodeRef): + node = ref()._nodeRef() + try: + node.exits.remove(path) + except ValueError: + pass + + if withItem: + self.scene().removeItem(self) + + -- cgit v1.2.3