Move tool allows multiple pending imports

This commit is contained in:
David Vierra 2015-01-21 17:48:18 -10:00
parent 883907ace5
commit 1878917b12
5 changed files with 159 additions and 99 deletions

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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):

View File

@ -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):