summaryrefslogtreecommitdiff
path: root/src/editorui
diff options
context:
space:
mode:
authorTreeki <treeki@gmail.com>2011-11-21 15:25:54 +0100
committerTreeki <treeki@gmail.com>2011-11-21 15:25:54 +0100
commit30188c779e1b419f6818596a29f7f3c7edad42bb (patch)
tree4bf37618ec1721b6f41b1686d71a9d727333e104 /src/editorui
parent63ba5f8a58bd26f42712ca61d7d18a1858083443 (diff)
downloadkoopatlas-30188c779e1b419f6818596a29f7f3c7edad42bb.tar.gz
koopatlas-30188c779e1b419f6818596a29f7f3c7edad42bb.zip
some refactoring of editorui and co
Diffstat (limited to 'src/editorui')
-rw-r--r--src/editorui/__init__.py0
-rw-r--r--src/editorui/doodads.py248
-rw-r--r--src/editorui/editorcommon.py77
-rw-r--r--src/editorui/editormain.py419
-rw-r--r--src/editorui/objects.py182
-rw-r--r--src/editorui/paths.py319
6 files changed, 1245 insertions, 0 deletions
diff --git a/src/editorui/__init__.py b/src/editorui/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/editorui/__init__.py
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)
+
+