Hi @m_adam ,
Thanks for your reply! After some research, I had expanded the code for this case to adapt to the requirements I mentioned earlier:
we can drag c4d.BaseObject , c4d.BaseMaterial or Image into UA, create their icon.
we can drag the UA item(Object) back to OM, and drag the UA item(Material) into Material Manager or OM(on the selected object), or drag the UA item(ImageRef) into a shader link.
The UserArea Accept:
c4d.BaseObject
c4d.BaseMaterial
Image file outside Cinema 4D.
Here is the code in case anyone is interested in this topic in the future.Hope it can be helpful.
Cheers~
DunHou
Animation.gif
import c4d
from c4d import Vector
from c4d.gui import GeUserArea, GeDialog
GADGET_ID_GEUSERAREA = 10000
class DropArea(GeUserArea):
def __init__(self):
# data host
self.receivedObject: list[c4d.BaseList2D, str] = []
# bg color of the pen
self.color: Vector = self._get_color()
def _get_color(self, colorId: int = c4d.COLOR_BG) -> Vector:
"""
Get a rgb color from c4d color id
"""
color = self.GetColorRGB(colorId)
r = color['r']/255
g = color['g']/255
b = color['b']/255
return c4d.Vector(r,g,b)
def DrawMsg(self, x1, y1, x2, y2, msg):
"""This Method is called automatically when Cinema 4D Draw the Gadget.
Args:
x1 (int): The upper left x coordinate.
y1 (int): The upper left y coordinate.
x2 (int): The lower right x coordinate.
y2 (int): The lower right y coordinate.
msg (c4d.BaseContainer): The original message container.
"""
# Initializes draw region
self.OffScreenOn()
self.SetClippingRegion(x1, y1, x2, y2)
# Defines the color used in draw operation, use c4d backgroud color here.
self.DrawSetPen(self.color)
# Draws a rectangle filling the whole UI
self.DrawRectangle(x1, y1, x2, y2)
# Draw a Icon with the drop object, only consider c4d.BaseList2D and ImageRef here
if msg.GetInt32(c4d.BFM_DRAG_FINISHED) == 1:
# If drag info recive a image, draw a icon of image
if isinstance(self.receivedObject, str):
icon = c4d.bitmaps.InitResourceBitmap(1050500)
self.DrawBitmap(icon, x1, y1, x2, y2, 0, 0, icon.GetBw(), icon.GetBh(), mode= c4d.BMP_ALLOWALPHA)
# If drag info recive a list of BaseList2D, draw a icon of first one.
else:
if self.receivedObject:
# Draw the first drop object's Icon,
# for BaseMaterial, we can get the preview image.
# for BaseObject, we can get the icon.
if isinstance(self.receivedObject[0], c4d.BaseList2D):
icon = self.receivedObject[0].GetIcon()
self.DrawBitmap(icon['bmp'], x1, y1, x2, y2, icon['x'], icon['y'], icon['w'], icon['h'],mode= c4d.BMP_ALLOWALPHA)
def GetMinSize(self):
# do a calculation here, min size.
return 200, 200
def Message(self, msg, result) :
if msg.GetId()==c4d.BFM_DRAGRECEIVE:
# Discard if lost drag or if it has been escaped
if msg.GetInt32(c4d.BFM_DRAG_LOST) or msg.GetInt32(c4d.BFM_DRAG_ESC):
return self.SetDragDestination(c4d.MOUSE_FORBIDDEN)
if not self.CheckDropArea(msg, True, True):
return self.SetDragDestination(c4d.MOUSE_FORBIDDEN)
# Check if drag is finished (=drop)
if msg.GetInt32(c4d.BFM_DRAG_FINISHED) == 1:
# Get drag object type and data
dragInfo = self.GetDragObject(msg)
# Redraw the GeUserArea (will call DrawMsg)
self.Redraw()
#print(type(dragInfo['object']))
self.receivedObject = dragInfo['object']
print(f"Dropped: {self.receivedObject}")
return True
# Return current mouse cursor for valid drag operation
return self.SetDragDestination(c4d.MOUSE_MOVE)
# Call GeUserAre.Message() implementation so that it can handle all the other messages
return c4d.gui.GeUserArea.Message(self, msg, result)
def InputEvent(self, msg):
"""Called by Cinema 4D when a click occurs"""
# If the #self.receivedObject host the file path of the image, we generate the file path.
# this means we can drag it into a image link like a shader link.
if isinstance(self.receivedObject, str):
self.HandleMouseDrag(msg, c4d.DRAGTYPE_FILENAME_IMAGE, self.receivedObject, 0)
else:
# Create a list of C4DAtom and pass that to the HandleMouseDrag function that will generate all the drag information
self.HandleMouseDrag(msg, c4d.DRAGTYPE_ATOMARRAY, self.receivedObject, 0)
return True
class ExampleDialog(GeDialog):
# The GeUserArea need to be stored somewhere, we will need this instance to be attached to the current Layout
geUserArea = DropArea()
def CreateLayout(self):
"""This Method is called automatically when Cinema 4D Create the Layout (display) of the Dialog."""
self.SetTitle("Drag Area")
if self.GroupBegin(0, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=1, rows=0, title="", groupflags=0, initw=100, inith=100):
self.GroupBorderSpace(8,8,8,8)
self.GroupSpace(2, 2)
# Adds a Gadget that will host a GeUserArea
self.AddUserArea(GADGET_ID_GEUSERAREA, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 200, 200)
# Attaches the stored GeUserArea to the Gadget previously created
self.AttachUserArea(self.geUserArea, GADGET_ID_GEUSERAREA)
self.GroupEnd()
return True
if __name__ == "__main__":
global dlg
dlg = ExampleDialog()
dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, defaultw=200, defaulth=200)