diff --git a/src/mcedit2/editorsession.py b/src/mcedit2/editorsession.py index de9e4bb..241feb1 100644 --- a/src/mcedit2/editorsession.py +++ b/src/mcedit2/editorsession.py @@ -40,13 +40,28 @@ tool (why?), and the ChunkLoader that coordinates loading chunks into its viewpo """ class PendingImport(object): - def __init__(self, schematic, pos): + def __init__(self, schematic, pos, text): + self.text = text self.pos = pos self.schematic = schematic def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.schematic, self.pos) +class PasteImportCommand(QtGui.QUndoCommand): + def __init__(self, editorSession, pendingImport, text, *args, **kwargs): + super(PasteImportCommand, self).__init__(*args, **kwargs) + self.setText(text) + self.editorSession = editorSession + self.pendingImport = pendingImport + + def undo(self): + self.editorSession.moveTool.removePendingImport(self.pendingImport) + + def redo(self): + self.editorSession.moveTool.addPendingImport(self.pendingImport) + self.editorSession.chooseTool("Move") + class EditorSession(QtCore.QObject): def __init__(self, filename, versionInfo, readonly=False): QtCore.QObject.__init__(self) @@ -61,8 +76,6 @@ class EditorSession(QtCore.QObject): self.copiedSchematic = None """:type : WorldEditor""" - self.pendingImports = [] - self.versionInfo = versionInfo # --- Open world editor --- @@ -190,6 +203,7 @@ class EditorSession(QtCore.QObject): self.tools = {cls.name: cls(self) for cls in self.toolClasses} self.selectionTool = self.tools["Select"] + self.moveTool = self.tools["Move"] # --- Editor stuff --- self.editorTab = EditorTab(self) @@ -261,7 +275,9 @@ class EditorSession(QtCore.QObject): if self.copiedSchematic is None: return - self.beginImport(self.copiedSchematic, self.currentSelection.origin) + imp = PendingImport(self.copiedSchematic, self.currentSelection.origin, self.tr("")) + command = PasteImportCommand(self, imp, "Paste") + self.undoStack.push(command) def pasteBlocks(self): NotImplementedYet() @@ -276,31 +292,15 @@ class EditorSession(QtCore.QObject): def importSchematic(self, filename): schematic = WorldEditor(filename, readonly=True) - self.beginImport(schematic) - - # --- Import --- - - def beginImport(self, schematic, pos=None): - moveTool = self.tools["Move"] - + ray = self.editorTab.currentView().rayAtCenter() + pos, face = rayCastInBounds(ray, self.currentDimension) if pos is None: - ray = self.editorTab.currentView().rayAtCenter() - pos, face = rayCastInBounds(ray, self.currentDimension) - if pos is None: - pos = ray.point - - imp = PendingImport(schematic, pos) - - self.pendingImports.append(imp) - moveTool.currentImport = imp - self.chooseTool("Move") - - def addPendingImport(self, pendingImport): - self.pendingImports.append(pendingImport) - - def removePendingImport(self, pendingImport): - self.pendingImports.remove(pendingImport) + pos = ray.point + name = os.path.basename(filename) + imp = PendingImport(schematic, pos, name) + command = PasteImportCommand(self, imp, "Import %s" % name) + self.undoStack.push(command) # --- Undo support --- @@ -546,9 +546,8 @@ class EditorTab(QtGui.QWidget): self.toolOptionsArea.takeWidget() # setWidget gives ownership to the scroll area self.toolOptionsArea.setWidget(tool.toolWidget) self.toolOptionsDockWidget.setWindowTitle(self.tr(tool.name) + self.tr(" Tool Options")) - if tool.cursorNode: - log.info("Setting cursor %r for tool %r on view %r", tool.cursorNode, tool, self.currentView()) - self.currentView().setToolCursor(tool.cursorNode) + log.info("Setting cursor %r for tool %r on view %r", tool.cursorNode, tool, self.currentView()) + self.currentView().setToolCursor(tool.cursorNode) def saveState(self): pass diff --git a/src/mcedit2/editortools/edit_chunk.py b/src/mcedit2/editortools/edit_chunk.py index 3ec872d..60460bf 100644 --- a/src/mcedit2/editortools/edit_chunk.py +++ b/src/mcedit2/editortools/edit_chunk.py @@ -112,7 +112,7 @@ class ChunkTool(EditorTool): if self.selectionNode is None: self.selectionNode = SelectionBoxNode() self.selectionNode.filled = False - self.selectionNode.color = (0.3, 0.3, 1) + self.selectionNode.color = (0.3, 0.3, 1, .3) self.overlayNode.addChild(self.selectionNode) self.selectionNode.selectionBox = chunk.bounds diff --git a/src/mcedit2/editortools/move.py b/src/mcedit2/editortools/move.py index 53e0b75..87c5c5e 100644 --- a/src/mcedit2/editortools/move.py +++ b/src/mcedit2/editortools/move.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function import logging from PySide import QtGui, QtCore +from PySide.QtCore import Qt from mcedit2.editorsession import PendingImport from mcedit2.editortools import EditorTool @@ -35,12 +36,12 @@ class MoveSelectionCommand(SimpleRevisionCommand): def undo(self): super(MoveSelectionCommand, self).undo() self.moveTool.currentImport = None - self.editorSession.removePendingImport(self.currentImport) + self.moveTool.removePendingImport(self.currentImport) self.moveTool.editorSession.chooseTool("Select") def redo(self): self.moveTool.currentImport = self.currentImport - self.editorSession.addPendingImport(self.currentImport) + self.moveTool.addPendingImport(self.currentImport) self.moveTool.editorSession.chooseTool("Move") super(MoveSelectionCommand, self).redo() @@ -122,6 +123,46 @@ class CoordinateWidget(QtGui.QWidget): x, y, z = self.point self.point = Vector(x, y, value) +class PendingImportNode(scenegraph.TranslateNode): + def __init__(self, pendingImport, textureAtlas): + super(PendingImportNode, self).__init__() + self.pendingImport = pendingImport + self.pos = pendingImport.pos + + dim = pendingImport.schematic.getDimension() + + self.worldScene = WorldScene(dim, textureAtlas) + self.worldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer + self.addChild(self.worldScene) + + self.outlineNode = SelectionBoxNode() + self.outlineNode.filled = False + self.outlineNode.selectionBox = dim.bounds + self.addChild(self.outlineNode) + + self.faceHoverNode = SelectionFaceNode() + self.faceHoverNode.selectionBox = dim.bounds + self.addChild(self.faceHoverNode) + + self.loader = WorldLoader(self.worldScene) + self.loader.timer.start() + + @property + def pos(self): + return self.translateOffset + + @pos.setter + def pos(self, value): + self.translateOffset = value + + def hoverFace(self, face): + if face is not None: + self.faceHoverNode.color = 0.3, 1, 1 + self.faceHoverNode.visible = True + + self.faceHoverNode.face = face + else: + self.faceHoverNode.visible = False class MoveTool(EditorTool): iconName = "move" @@ -130,30 +171,30 @@ class MoveTool(EditorTool): def __init__(self, editorSession, *args, **kwargs): super(MoveTool, self).__init__(editorSession, *args, **kwargs) self.overlayNode = scenegraph.Node() - self.translateNode = scenegraph.TranslateNode() - self.overlayNode.addChild(self.translateNode) - - self.sceneHolderNode = scenegraph.Node() - self.translateNode.addChild(self.sceneHolderNode) - - self.outlineNode = SelectionBoxNode() - self.outlineNode.color = .9, 1., 1. - self.translateNode.addChild(self.outlineNode) - - self.faceHoverNode = SelectionFaceNode() - self.translateNode.addChild(self.faceHoverNode) self.movingWorldScene = None self.loader = None self.dragStartFace = None self.dragStartPoint = None + self.pendingImports = [] + + self.pendingImportNodes = {} + self.toolWidget = QtGui.QWidget() + + self.importsListWidget = QtGui.QListView() + self.importsListModel = QtGui.QStandardItemModel() + self.importsListWidget.setModel(self.importsListModel) + self.importsListWidget.clicked.connect(self.listClicked) + self.importsListWidget.doubleClicked.connect(self.listDoubleClicked) + self.pointInput = CoordinateWidget() self.pointInput.pointChanged.connect(self.pointInputChanged) confirmButton = QtGui.QPushButton("Confirm") # xxxx should be in worldview confirmButton.clicked.connect(self.confirmImport) - self.toolWidget.setLayout(Column(self.pointInput, + self.toolWidget.setLayout(Column(self.importsListWidget, + self.pointInput, confirmButton, None)) @@ -171,18 +212,48 @@ class MoveTool(EditorTool): self.pointInput.point = value self.pointInputChanged(value) + def pointInputChanged(self, value): + if value is not None: + self.currentImport.pos = value + self.currentImportNode.pos = value + + # --- Pending imports --- + + def addPendingImport(self, pendingImport): + self.pendingImports.append(pendingImport) + item = QtGui.QStandardItem() + item.setEditable(False) + item.setText(pendingImport.text) + item.setData(pendingImport, Qt.UserRole) + self.importsListModel.appendRow(item) + self.importsListWidget.setCurrentIndex(self.importsListModel.rowCount()-1) + node = self.pendingImportNodes[pendingImport] = PendingImportNode(pendingImport, self.editorSession.textureAtlas) + self.overlayNode.addChild(node) + self.currentImport = pendingImport + + def removePendingImport(self, pendingImport): + self.pendingImports.remove(pendingImport) + indexes = self.importsListModel.match(QtCore.QModelIndex(), Qt.UserRole, pendingImport, flags=Qt.MatchExactly) + self.importsListModel.removeRows(indexes) + + node = self.pendingImportNodes.pop(pendingImport) + if node: + self.overlayNode.removeChild(node) + def doMoveOffsetCommand(self, oldPoint, newPoint): if newPoint != oldPoint: command = MoveOffsetCommand(self, oldPoint, newPoint) self.editorSession.pushCommand(command) - def pointInputChanged(self, value): - if value is not None: - self.currentImport.pos = value - self.translateNode.visible = True - self.translateNode.translateOffset = value - else: - self.translateNode.visible = False + def listClicked(self, index): + item = self.importsListModel.itemFromIndex(index) + pendingImport = item.data(Qt.UserRole) + self.currentImport = pendingImport + + def listDoubleClicked(self, index): + item = self.importsListModel.itemFromIndex(index) + pendingImport = item.data(Qt.UserRole) + self.editorSession.editorTab.currentView().centerOnPoint(pendingImport.pos) _currentImport = None @@ -192,38 +263,14 @@ class MoveTool(EditorTool): @currentImport.setter def currentImport(self, value): - oldVal = self._currentImport self._currentImport = value - if oldVal is not value: - self.updateOverlay() self.pointInput.setEnabled(value is not None) + for node in self.pendingImportNodes.itervalues(): + node.outlineNode.wireColor = (.2, 1., .2, .5) if node.pendingImport is value else (1, 1, 1, .3) - - def updateOverlay(self): - if self.currentImport is None: - log.info("updateOverlay: Nothing to display") - if self.movingWorldScene: - self.sceneHolderNode.removeChild(self.movingWorldScene) - self.movingWorldScene = None - self.outlineNode.visible = False - - - log.info("Updating move schematic scene: %s", self.currentImport) - if self.movingWorldScene: - self.loader.timer.stop() - self.sceneHolderNode.removeChild(self.movingWorldScene) - if self.currentImport: - dim = self.currentImport.schematic.getDimension() - self.movingWorldScene = WorldScene(dim, self.editorSession.textureAtlas) - # xxx assumes import is same blocktypes as world, find atlas for imported object - self.outlineNode.selectionBox = dim.bounds - self.outlineNode.filled = False - self.outlineNode.visible = True - - self.movingWorldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer - self.sceneHolderNode.addChild(self.movingWorldScene) - self.loader = WorldLoader(self.movingWorldScene) - self.loader.timer.start() + @property + def currentImportNode(self): + return self.pendingImportNodes.get(self.currentImport) @property def schematicBox(self): @@ -248,15 +295,9 @@ class MoveTool(EditorTool): if self.currentImport is None: return + node = self.currentImportNode point, face = boxFaceUnderCursor(self.schematicBox, event.ray) - if face is not None: - self.faceHoverNode.color = (0.3, 1, 1) - self.faceHoverNode.visible = True - - self.faceHoverNode.face = face - self.faceHoverNode.selectionBox = self.currentImport.schematic.getDimension().bounds - else: - self.faceHoverNode.visible = False + node.hoverFace(face) # Highlight face of box to move along, or else axis pointers to grab and drag? pass @@ -302,7 +343,7 @@ class MoveTool(EditorTool): export = self.editorSession.currentDimension.exportSchematicIter(self.editorSession.currentSelection) schematic = showProgress("Copying...", export) pos = self.editorSession.currentSelection.origin - pendingImport = PendingImport(schematic, pos) + pendingImport = PendingImport(schematic, pos, self.tr("")) moveCommand = MoveSelectionCommand(self, pendingImport) with moveCommand.begin(): @@ -311,13 +352,11 @@ class MoveTool(EditorTool): self.editorSession.pushCommand(moveCommand) - self.outlineNode.visible = True - def toolInactive(self): self.editorSession.selectionTool.hideSelectionWalls = False - self.outlineNode.visible = False - self.faceHoverNode.visible = False + for node in self.pendingImportNodes.itervalues(): + node.hoverFace(None) def confirmImport(self): if self.currentImport is None: diff --git a/src/mcedit2/editortools/select.py b/src/mcedit2/editortools/select.py index 4c2d0a4..083c147 100644 --- a/src/mcedit2/editortools/select.py +++ b/src/mcedit2/editortools/select.py @@ -221,8 +221,12 @@ class SelectionTool(EditorTool): if self.currentSelection is not None: self.currentSelection = self.createShapedSelection(self.currentSelection) + def toolActive(self): + self.selectionNode.boxNode.wireColor = 1, 1, 1, .5 + def toolInactive(self): self.faceHoverNode.visible = False + self.selectionNode.boxNode.wireColor = 1, 1, 1, .33 @property def hideSelectionWalls(self): diff --git a/src/mcedit2/rendering/selection.py b/src/mcedit2/rendering/selection.py index 32e8853..bfc3b86 100644 --- a/src/mcedit2/rendering/selection.py +++ b/src/mcedit2/rendering/selection.py @@ -138,6 +138,14 @@ class SelectionScene(scenegraph.Node): self._dimension = value self.updateSelection() + @property + def filled(self): + return self.cullNode.visible + + @filled.setter + def filled(self, value): + self.cullNode.visible = value + def updateSelection(self): if self.dimension is None or self.selection is None: return @@ -252,8 +260,7 @@ class SelectionBoxRenderNode(rendergraph.RenderNode): if box is None: return - alpha = 0.3 - r, g, b = self.sceneNode.color + r, g, b, alpha = self.sceneNode.color with gl.glPushAttrib(GL.GL_DEPTH_BUFFER_BIT | GL.GL_ENABLE_BIT | GL.GL_POLYGON_BIT): GL.glDepthMask(False) GL.glEnable(GL.GL_BLEND) @@ -266,7 +273,8 @@ class SelectionBoxRenderNode(rendergraph.RenderNode): if self.sceneNode.wire: # Wire box, thinner behind terrain - GL.glColor(1., 1., 1., alpha) + r, g, b, alpha = self.sceneNode.wireColor + GL.glColor(r, g, b, alpha) GL.glLineWidth(2.0) cubes.drawBox(box, cubeType=GL.GL_LINES) GL.glDisable(GL.GL_DEPTH_TEST) @@ -298,7 +306,7 @@ class SelectionBoxNode(scenegraph.Node): self._selectionBox = value self.dirty = True - _color = (1, .3, 1) + _color = (1, .3, 1, .3) @property def color(self): return self._color @@ -308,6 +316,16 @@ class SelectionBoxNode(scenegraph.Node): self._color = value self.dirty = True + _wireColor = (1, 1, 1, .6) + @property + def wireColor(self): + return self._wireColor + + @wireColor.setter + def wireColor(self, value): + self._wireColor = value + self.dirty = True + class SelectionFaceRenderNode(rendergraph.RenderNode): def drawSelf(self):