<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[UserArea drag and drop example?]]></title><description><![CDATA[<p dir="auto">Hi community,</p>
<p dir="auto">I have recently started learning UserArea modules, but there are some principle documents that are not very easy to understand, especially regarding drag and drop, can you provide an example of this and explain more details about the usage of related functions?</p>
<h2>abstraction</h2>
<ul>
<li>how do we drag a BaseList2d into a UA and get some data</li>
<li>how do we drag a UA item back to Cinema and create BaseList2d depends on the context?</li>
</ul>
<h2>concretion</h2>
<p dir="auto">If the abstract problem is relatively vague, presenting the simplest case may be more clear：</p>
<ol>
<li>we create a dialog with UA,</li>
<li>we can drag a Cube and a Material into UA, create UA item.</li>
<li>we can drag the UA item(Cube) back to OM, and drag the UA item(Material) into Material Manager or OM(on the selected object)</li>
</ol>
<p dir="auto">Hope this can explain my problem clearly, and hope Python can handle these things.</p>
<p dir="auto">Cheers~<br />
DunHou</p>
]]></description><link>http://developers.maxon.net/forum/topic/15340/userarea-drag-and-drop-example</link><generator>RSS for Node</generator><lastBuildDate>Thu, 18 Jun 2026 05:40:25 GMT</lastBuildDate><atom:link href="http://developers.maxon.net/forum/topic/15340.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 22 Jan 2024 09:39:42 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to UserArea drag and drop example? on Mon, 11 Nov 2024 09:12:00 GMT]]></title><description><![CDATA[<p dir="auto">I tried using a timer to solve this problem, but I still want to know if there is a more direct way</p>
<pre><code>import c4d 
import threading
from c4d.gui import GeUserArea, GeDialog

GADGET_ID_GEUSERAREA = 10000

class DropArea(GeUserArea):

    def __init__(self):
        # Used to store all objects involved in the drag-and-drop operation
        self.currentDragObjects = []
        # Flag to indicate whether a drag operation is in progress
        self.isDragging = False
        # Define a timer to delay the handling of the drag completion
        self.dragTimer = None

    def Message(self, msg, result):
        # Handle drag-and-drop messages
        if msg.GetId() == c4d.BFM_DRAGRECEIVE:
            # Check if the drag was lost or canceled
            if msg.GetInt32(c4d.BFM_DRAG_LOST) or msg.GetInt32(c4d.BFM_DRAG_ESC):
                self.isDragging = False
                return self.SetDragDestination(c4d.MOUSE_FORBIDDEN)

            # If the drag just started, clear the previous object list
            if not self.isDragging:
                self.currentDragObjects = []  # Initialize the storage list
                self.isDragging = True  # Mark the beginning of the drag

            # Verify if it is a valid drop area
            if not self.CheckDropArea(msg, True, True):
                return self.SetDragDestination(c4d.MOUSE_FORBIDDEN)

            # Get the dragged file object
            dragInfo = self.GetDragObject(msg)
            if dragInfo is not None:
                dragObject = dragInfo['object']
                # Check if the object already exists in the list to avoid duplicates
                if dragObject not in self.currentDragObjects:
                    self.currentDragObjects.append(dragObject)

            # Reset the timer to delay the handling of drag completion
            if self.dragTimer is not None:
                self.dragTimer.cancel()

            # Set a short timer (e.g., 0.2 seconds) to determine if the drag operation is complete
            self.dragTimer = threading.Timer(0.2, self._finalize_drag)
            self.dragTimer.start()

            # Set the mouse cursor to a valid state
            return self.SetDragDestination(c4d.MOUSE_MOVE)

        # Call the base class Message() method to handle other messages
        return c4d.gui.GeUserArea.Message(self, msg, result)

    def _finalize_drag(self):
        # Delayed execution to ensure all dragged objects have been received
        self.isDragging = False

        if self.currentDragObjects:
            # Print all dropped files
            print(f"Dropped files: {self.currentDragObjects}")

            # Additional logic can be executed here, e.g., handling file paths or other content
            # Clear the object list for the next drag-and-drop operation
            self.currentDragObjects = []

        # Redraw the user area (if UI update is needed)
        self.Redraw()

class ExampleDialog(GeDialog):
    geUserArea = DropArea()

    def CreateLayout(self):
        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)

            # Add the user area gadget
            self.AddUserArea(GADGET_ID_GEUSERAREA, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 200, 200)
            # Attach the user area to the gadget
            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)

</code></pre>
]]></description><link>http://developers.maxon.net/forum/post/75295</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/75295</guid><dc:creator><![CDATA[karen]]></dc:creator><pubDate>Mon, 11 Nov 2024 09:12:00 GMT</pubDate></item><item><title><![CDATA[Reply to UserArea drag and drop example? on Mon, 11 Nov 2024 08:27:12 GMT]]></title><description><![CDATA[<p dir="auto">Hi <a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/dunhou">@<bdi>Dunhou</bdi></a> ,Hi <a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/m_adam">@<bdi>m_adam</bdi></a>  , thank you for providing the code. I have also been researching drag and drop methods in GeUserArea recently. I noticed that in the DragObject () method, when retrieving an external file, a single file path is returned. May I ask if it is possible to return a list containing multiple file paths when dragging multiple external files at the same time, similar to the DRAGTYPE-ATOMARRAY</p>
]]></description><link>http://developers.maxon.net/forum/post/75293</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/75293</guid><dc:creator><![CDATA[karen]]></dc:creator><pubDate>Mon, 11 Nov 2024 08:27:12 GMT</pubDate></item><item><title><![CDATA[Reply to UserArea drag and drop example? on Wed, 24 Jan 2024 12:59:25 GMT]]></title><description><![CDATA[<p dir="auto">Hi  <a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/m_adam">@<bdi>m_adam</bdi></a> ，</p>
<p dir="auto">Thanks for your reply! After some research, I had expanded the code for this case to adapt to the requirements I mentioned earlier:</p>
<ul>
<li>we can drag c4d.BaseObject , c4d.BaseMaterial or  Image into UA, create their icon.</li>
<li>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.</li>
</ul>
<p dir="auto">The UserArea Accept:</p>
<ul>
<li>c4d.BaseObject</li>
<li>c4d.BaseMaterial</li>
<li>Image file outside Cinema 4D.</li>
</ul>
<p dir="auto">Here is the code in case anyone is interested in this topic in the future.Hope it can be helpful.</p>
<p dir="auto">Cheers~<br />
DunHou</p>
<p dir="auto"><img src="/forum/assets/uploads/files/1706087623700-animation.gif" alt="Animation.gif" class=" img-fluid img-markdown" /></p>
<pre><code class="language-python">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) -&gt; 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)
</code></pre>
]]></description><link>http://developers.maxon.net/forum/post/73601</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/73601</guid><dc:creator><![CDATA[Dunhou]]></dc:creator><pubDate>Wed, 24 Jan 2024 12:59:25 GMT</pubDate></item><item><title><![CDATA[Reply to UserArea drag and drop example? on Tue, 23 Jan 2024 14:31:11 GMT]]></title><description><![CDATA[<p dir="auto">Hi <a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/dunhou">@<bdi>Dunhou</bdi></a> speaking more generally about drag and drop in GeUserArea, I would invite you to read the <a href="https://developers.maxon.net/forum/topic/12159/drag-drop-an-image-in-a-gui-field" target="_blank" rel="noopener noreferrer nofollow ugc"> Drag&amp;Drop an image in a gui field </a> topic which covers the basics to receive and <a href="https://developers.maxon.net/forum/topic/8563/11180_user-area--drop-finished/2?_=1706017670719" target="_blank" rel="noopener noreferrer nofollow ugc"> user area - drop finished </a> for receiving.</p>
<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/dunhou">@<bdi>Dunhou</bdi></a> said in <a href="/forum/post/73579">UserArea drag and drop example?</a>:</p>
<blockquote>
<ul>
<li>How do we drag a BaseList2d into a UA and get some data</li>
<li>how do we drag a UA item back to Cinema and create BaseList2d depends on the context?</li>
</ul>
</blockquote>
<pre><code>import c4d


class CustomUserArea(c4d.gui.GeUserArea):
    
    def __init__(self):
        self.receivedObject = []
    
    def GetMinSize(self):
        """Called by Cinema 4D to know the minimum size of the GeUserArea"""
        return 50, 100
    
    def DrawMsg(self, x1, y1, x2, y2, msg_ref):
        """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_ref (c4d.BaseContainer): The original message container.
        """
        
        self.OffScreenOn()
        self.SetClippingRegion(x1, y1, x2, y2)

        # From 0 pixel to 50 draw a red box. This one will be used to receive drop operation.
        self.DrawSetPen(c4d.Vector(1, 0,0))
        self.DrawRectangle(0, 0, 50, 50)

        # From 50 pixel to 100 draw a green box. This one will be used to start the drag operation.
        # Look at the InputEvent method to see how this is done
        self.DrawSetPen(c4d.Vector(0, 1,0))
        self.DrawRectangle(0, 50, 50, 100)

    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)
            
            # Check drop area and discard if not on the red box or not on the user area
            mouseY = self.GetDragPosition(msg)["y"] 
            if not 0 &lt; mouseY &lt; 50 or 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)
                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"""
        mouseY = msg[c4d.BFM_INPUT_Y] + self.Global2Local()["y"]
        
        if 50 &lt; mouseY &lt; 100:
            # Retrieve the first object in the scene to use or otherwise use a newly created cube           
            if self.receivedObject is None:
                self.receivedObject = c4d.BaseObject(c4d.Ocube)
                
            # 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(c4d.gui.GeDialog):

    def __init__(self):
        super().__init__()
        self.ua = CustomUserArea()

    def CreateLayout(self):
        """This Method is called automatically when Cinema 4D Create the Layout (display) of the Dialog."""
        self.AddUserArea(10000, c4d.BFH_LEFT | c4d.BFV_TOP)
        self.AttachUserArea(self.ua, 10000)

        return True


# main
if __name__ == "__main__":
    global dlg
    dlg = ExampleDialog()
    dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, defaultw=50, defaulth=100)
</code></pre>
<p dir="auto">Sadly there is no way for the part that generate the drag operation to be context specific. The GeUserArea generate the drag information, then Cinema 4D forward it to the appropriate UI element, this one receive a BFM_DRAGRECEIVE and then it handle it as it want and have no clue from where it comes.</p>
<p dir="auto">Hope it answers your questions,<br />
Cheers,<br />
Maxime.</p>
]]></description><link>http://developers.maxon.net/forum/post/73590</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/73590</guid><dc:creator><![CDATA[m_adam]]></dc:creator><pubDate>Tue, 23 Jan 2024 14:31:11 GMT</pubDate></item></channel></rss>