class CanvasPanel(): def __init__(self, parent): self.parent = parent self.elementHandler = None color = ( (0.8, 0.8, 0.8, 1), # Normal (0.9, 0.9, 1, 1), # Click (0.8, 0.8, 1, 1), # Hover (0.5, 0.5, 0.5, 1)) # Disabled # respect menu bar self.canvasScale = 1080 # min(base.getSize()[0], base.getSize()[1]) # we default to a 1920x1080 FHD screen self.canvasLeft = -1920/2 self.canvasRight = 1920/2 self.canvasTop = 1080/2 self.canvasBottom = -1080/2 self.visualEditor = DirectScrolledFrame( frameColor=(0.25, 0.25, 0.25, 1), canvasSize=(self.canvasLeft, self.canvasRight, self.canvasBottom, self.canvasTop), scrollBarWidth=20, # vertical scrollbar verticalScroll_value=0.5, verticalScroll_thumb_relief=DGG.FLAT, verticalScroll_incButton_relief=DGG.FLAT, verticalScroll_decButton_relief=DGG.FLAT, verticalScroll_thumb_frameColor=color, verticalScroll_incButton_frameColor=color, verticalScroll_decButton_frameColor=color, # horizontal scrollbar horizontalScroll_value=0.5, horizontalScroll_thumb_relief=DGG.FLAT, horizontalScroll_incButton_relief=DGG.FLAT, horizontalScroll_decButton_relief=DGG.FLAT, horizontalScroll_thumb_frameColor=color, horizontalScroll_incButton_frameColor=color, horizontalScroll_decButton_frameColor=color, ) self.scaleParent = DirectFrame( scale=(1,1,1) ) # store which base parent should be used for the elements self.currentVisEditorParent = base.aspect2d self.visEditorInAspect2D = True # Layouting self.sizer = DirectAutoSizer( updateOnWindowResize=False, parent=parent, child=self.visualEditor, ) # zoom scale self.minScale = DEFAULT_MIN_SCALE self.maxScale = DEFAULT_MAX_SCALE self.zoomInMultiplyer = 1.1 self.zoomOutMultiplyer = 0.9 # This frame will be the base parent for the added GUI elements self.elementHolder = DirectFrame( #frameColor=(0.25, 0.25, 0.25, 1), scale=LVecBase3f(self.canvasScale/2,1,self.canvasScale/2), parent=self.scaleParent ) self.elementHolder.bind(DGG.B1RELEASE, base.messenger.send, ["mouse3"]) # Ensure the holder frame will be streched to fill the parent self.scaleParentSizer = DirectAutoSizer( parent=self.visualEditor.canvas, child=self.scaleParent, parentGetSizeFunction=self.visualEditor.cget, parentGetSizeExtraArgs=["canvasSize"], ) self.elementHolderSizer = DirectAutoSizer( parent=self.scaleParent, child=self.elementHolder ) # The designers grid self.grid = DirectGrid(gridSize=50.0, gridSpacing=0.05,parent=self.elementHolder) self.grid.setP(90) self.grid.snapMarker.hide() self.snapToGrid = not self.grid.isHidden() self.canvasTopCenter = self.elementHolder.attachNewNode("canvasTopCenter") self.canvasBottomCenter = self.elementHolder.attachNewNode("canvasBottomCenter") self.canvasLeftCenter = self.elementHolder.attachNewNode("canvasLeftCenter") self.canvasRightCenter = self.elementHolder.attachNewNode("canvasRightCenter") self.canvasTopLeft = self.elementHolder.attachNewNode("canvasTopLeft") self.canvasTopRight = self.elementHolder.attachNewNode("canvasTopRight") self.canvasBottomLeft = self.elementHolder.attachNewNode("canvasBottomLeft") self.canvasBottomRight = self.elementHolder.attachNewNode("canvasBottomRight") # default to Aspect2D self.setVisualEditorParent(False) base.taskMgr.add(self.watchCanvasProps, "watch-canvas-properties", sort=50, priority=0) def getEditorCanvasSize(self): cs = self.elementHolder["frameSize"] if self.currentVisEditorParent == base.pixel2d: cs = self.visualEditor["canvasSize"] return cs def getEditorRootCanvas(self): return self.elementHolder def watchCanvasProps(self, task): """Watch for all properties that can be changed on the canvas and won't directly propagate down to the actual background, which is the element holder.""" self.sizer.refresh() sizeChanged = False cs = self.getEditorCanvasSize() if self.canvasLeft != cs[0]: sizeChanged = True elif self.canvasRight != cs[1]: sizeChanged = True elif self.canvasBottom != cs[2]: sizeChanged = True elif self.canvasTop != cs[3]: sizeChanged = True if sizeChanged: width = cs[1] - cs[0] height = cs[3] - cs[2] self.canvasScale = min(width, height) if self.currentVisEditorParent == base.pixel2d: if width > height: self.canvasScale *= self.visualEditor.getScale()[2] else: self.canvasScale *= self.visualEditor.getScale()[0] else: if width > height: self.canvasScale *= self.elementHolder.getScale()[2] else: self.canvasScale *= self.elementHolder.getScale()[0] #TODO: the scale probably needs to be calculated dependent on the users screen size self.elementHolder["scale"]= LVecBase3f(self.canvasScale/2,1,self.canvasScale/2), self.elementHolderSizer.refresh() self.scaleParentSizer.refresh() self.setCanvasPlacers() if self.visualEditor["frameColor"] != self.elementHolder["frameColor"]: fc = self.visualEditor["frameColor"] self.elementHolder["frameColor"] = fc self.elementHolderSizer["frameColor"] = fc self.scaleParentSizer["frameColor"] = fc self.scaleParent["frameColor"] = fc self.visualEditor return task.cont def setCanvasPlacers(self): cs = self.getEditorCanvasSize() self.canvasLeft = cs[0] self.canvasRight = cs[1] self.canvasBottom = cs[2] self.canvasTop = cs[3] # Put the nodes in their places self.canvasTopCenter.setPos(0, 0, self.canvasTop) self.canvasBottomCenter.setPos(0, 0, self.canvasBottom) self.canvasLeftCenter.setPos(self.canvasLeft, 0, 0) self.canvasRightCenter.setPos(self.canvasRight, 0, 0) self.canvasTopLeft.setPos(self.canvasLeft, 0, self.canvasTop) self.canvasTopRight.setPos(self.canvasRight, 0, self.canvasTop) self.canvasBottomLeft.setPos(self.canvasLeft, 0, self.canvasBottom) self.canvasBottomRight.setPos(self.canvasRight, 0, self.canvasBottom) def getEditorPlacer(self, placerName): placerName = placerName.lower() placerName = placerName.replace("a2d", "canvas") if placerName == "canvasTopCenter".lower(): return self.canvasTopCenter elif placerName == "canvasBottomCenter".lower(): return self.canvasBottomCenter elif placerName == "canvasLeftCenter".lower(): return self.canvasLeftCenter elif placerName == "canvasRightCenter".lower(): return self.canvasRightCenter elif placerName == "canvasTopLeft".lower(): return self.canvasTopLeft elif placerName == "canvasTopRight".lower(): return self.canvasTopRight elif placerName == "canvasBottomLeft".lower(): return self.canvasBottomLeft elif placerName == "canvasBottomRight".lower(): return self.canvasBottomRight def setElementHandler(self, elementHandler): self.elementHandler = elementHandler def setVisualEditorCanvasSize(self, newCanvasSize): self.visualEditor["canvasSize"] = newCanvasSize self.elementHolderSizer.refresh() self.scaleParentSizer.refresh() self.setCanvasPlacers() def setVisualEditorParent(self, toPixel2D): if toPixel2D: # change to pixel2d # we default to a 1920x1080 FHD screen self.canvasLeft = 0 self.canvasRight = 1920 self.canvasBottom = -1080 self.canvasTop = 0 self.setVisualEditorCanvasSize((self.canvasLeft, self.canvasRight, self.canvasBottom, self.canvasTop)) self.currentVisEditorParent = base.pixel2d # Speed up the setGridSpacing call by setting the size to 1 self.grid.setGridSize(1) self.grid.setGridSpacing(0.05 * (self.canvasScale / 2)) self.grid.setGridSize(1920*4) self.visEditorInAspect2D = False if self.elementHandler is not None: self.elementHandler.setEditorParentType(self.visEditorInAspect2D) self.elementHandler.setEditorCenter((self.visualEditor.getWidth()/2, 0, -self.visualEditor.getHeight()/2)) else: # change to aspect2d # we default to a 1920x1080 FHD screen self.canvasLeft = -1920/2 self.canvasRight = 1920/2 self.canvasTop = 1080/2 self.canvasBottom = -1080/2 self.scaleParent.setScale(1, 1, 1) self.setVisualEditorCanvasSize((self.canvasLeft, self.canvasRight, self.canvasBottom, self.canvasTop)) self.currentVisEditorParent = base.aspect2d # Speed up the setGridSpacing call by setting the size to 1 self.grid.setGridSize(1) self.grid.setGridSpacing(0.05) self.grid.setGridSize(50) self.visEditorInAspect2D = True if self.elementHandler is not None: self.elementHandler.setEditorParentType(self.visEditorInAspect2D) self.elementHandler.setEditorCenter((0, 0, 0)) # reset the zoom value self.resetZoom() self.setCanvasPlacers() def toggleVisualEditorParent(self): if self.currentVisEditorParent == base.aspect2d: self.setVisualEditorParent(True) elif self.currentVisEditorParent != base.aspect2d: self.setVisualEditorParent(False) def resizeFrame(self): self.sizer.refresh() def toggleGrid(self, enable): if enable: self.grid.show() self.snapToGrid = True else: self.grid.hide() self.snapToGrid = False def resetZoom(self): self.visualEditor["verticalScroll_range"] = (0, 1) self.visualEditor["horizontalScroll_range"] = (0, 1) if self.currentVisEditorParent != base.aspect2d: # we are in pixel2d self.getEditorRootCanvas().setScale(1,1,1) self.visualEditor.verticalScroll["value"] = 0 self.visualEditor.horizontalScroll["value"] = 0 posParentScaleX = DGH.getRealWidth(self.parent) self.minScale = DEFAULT_MIN_SCALE self.maxScale = DEFAULT_MAX_SCALE base.messenger.send("setZoomValeMinMax", [self.minScale, self.maxScale]) base.messenger.send("setZoomValue", [1]) else: # we are in aspect2d self.getEditorRootCanvas().setScale(self.canvasScale/2,1,self.canvasScale/2) self.visualEditor.verticalScroll["value"] = 0.5 self.visualEditor.horizontalScroll["value"] = 0.5 posParentScaleX = DGH.getRealWidth(self.parent) self.minScale = posParentScaleX * DEFAULT_MIN_SCALE self.maxScale = posParentScaleX * DEFAULT_MAX_SCALE base.messenger.send("setZoomValeMinMax", [self.minScale, self.maxScale]) base.messenger.send("setZoomValue", [self.canvasScale/2]) def setZoom(self, zoomValue): z = zoomValue s = self.getEditorRootCanvas().getScale() self.getEditorRootCanvas().setScale(z, s[1], z) # update scroll bars vr = self.visualEditor["verticalScroll_range"] vv = self.visualEditor.verticalScroll["value"] hr = self.visualEditor["horizontalScroll_range"] hv = self.visualEditor.horizontalScroll["value"] vw = vr[1] - vr[0] hw = hr[1] - hr[0] curPosVer = vv / vw * 100 curPosHor = hv / hw * 100 self.visualEditor["verticalScroll_range"] = (vr[0]*(z/s[0]), vr[1]*(z/s[2])) self.visualEditor["horizontalScroll_range"] = (hr[0]*(z/s[0]), hr[1]*(z/s[2])) vr = self.visualEditor["verticalScroll_range"] hr = self.visualEditor["horizontalScroll_range"] self.visualEditor.verticalScroll["value"] = (vr[1] - vr[0]) / 100 * curPosVer self.visualEditor.horizontalScroll["value"] = (hr[1] - hr[0]) / 100 * curPosHor self.elementHolderSizer.refresh() def zoom(self, direction): z = 1 s = self.getEditorRootCanvas().getScale() if direction < 0 and self.getEditorRootCanvas().getScale()[0] > self.minScale: z = self.zoomOutMultiplyer elif direction > 0 and self.getEditorRootCanvas().getScale()[0] < self.maxScale: z = self.zoomInMultiplyer self.getEditorRootCanvas().setScale(s[0]*z, s[1], s[2]*z) base.messenger.send("setZoomValue", [self.getEditorRootCanvas().getScale()[0]]) #print(self.getEditorRootCanvas().getScale()) # update scroll bars vr = self.visualEditor["verticalScroll_range"] vv = self.visualEditor.verticalScroll["value"] hr = self.visualEditor["horizontalScroll_range"] hv = self.visualEditor.horizontalScroll["value"] vw = vr[1] - vr[0] hw = hr[1] - hr[0] curPosVer = vv / vw * 100 curPosHor = hv / hw * 100 self.visualEditor["verticalScroll_range"] = (vr[0]*z, vr[1]*z) self.visualEditor["horizontalScroll_range"] = (hr[0]*z, hr[1]*z) vr = self.visualEditor["verticalScroll_range"] hr = self.visualEditor["horizontalScroll_range"] self.visualEditor.verticalScroll["value"] = (vr[1] - vr[0]) / 100 * curPosVer self.visualEditor.horizontalScroll["value"] = (hr[1] - hr[0]) / 100 * curPosHor self.elementHolderSizer.refresh() def dragEditorFrame(self, dragEnabled): taskMgr.remove("dragEditorFrameTask") mwn = base.mouseWatcherNode if dragEnabled: t = taskMgr.add(self.dragEditorFrameTask, "dragEditorFrameTask") t.vMouse2render2d = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1]) def dragEditorFrameTask(self, t): mwn = base.mouseWatcherNode if mwn.hasMouse(): vMouse2render2d = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1]) moveVec = t.vMouse2render2d - vMouse2render2d t.vMouse2render2d = vMouse2render2d newValue = self.visualEditor["verticalScroll_value"] - moveVec.getZ() if newValue <= 1 and newValue >= 0: self.visualEditor["verticalScroll_value"] = newValue elif newValue > 1: self.visualEditor["verticalScroll_value"] = 1 elif newValue < 0: self.visualEditor["verticalScroll_value"] = 0 newValue = self.visualEditor["horizontalScroll_value"] + moveVec.getX() if newValue <= 1 and newValue >= 0: self.visualEditor["horizontalScroll_value"] = newValue elif newValue > 1: self.visualEditor["horizontalScroll_value"] = 1 elif newValue < 0: self.visualEditor["horizontalScroll_value"] = 0 return t.cont