Move tool allows multiple pending imports
This commit is contained in:
parent
883907ace5
commit
1878917b12
@ -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("<Pasted Object>"))
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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("<Moved Object>"))
|
||||
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:
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
Reference in New Issue
Block a user