Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Drag & Drop to Reorder in GUI

    Cinema 4D SDK
    python
    4
    14
    1.8k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ManuelM
      Manuel
      last edited by

      Hello,

      we don't have any helpful functions to do that kind of drag and drop and no example to reproduce that within a GeUserArea.
      It's still possible but really a lot of work.

      Cheers,
      Manuel

      MAXON SDK Specialist

      MAXON Registered Developer

      ? 1 Reply Last reply Reply Quote 0
      • ?
        A Former User @Manuel
        last edited by A Former User

        @m_magalhaes
        Thank you for the reply and confirmation that it's possible. I'm happy to do the work with a little help from the Maxon SDK specialists.

        In the example below, the drag behavior seems right, but the print to the console on line 67 doesn't show until I save the file on my machine or hit enter in the console (and sometimes not at all) for some reason. I want to make sure it's not a memory leak. Could you please let me know if this is a recommended way to set up a drag in a GeUserArea? (I got the basis from this post). If so, how would I print the input events to the console?

        import c4d
        from c4d import gui
        from c4d.gui import GeUserArea, GeDialog
        
        class Area(c4d.gui.GeUserArea):
            def __init__(self):
                super(Area, self).__init__()
                self.rectangle=[-1,-1,-1,-1]
        
            def DrawMsg(self, x1, y1, x2, y2, msg):
                self.OffScreenOn()
                self.DrawSetPen(c4d.Vector(.2))
                self.DrawRectangle(x1, y1, x2, y2)
                xdr,ydr,x2dr,y2dr = self.toolDragSortEx()
                self.DrawSetPen(c4d.Vector(1))
                self.DrawBorder(c4d.BORDER_ACTIVE_4, xdr,ydr,x2dr,y2dr)
        
            def toolDragSortEx(self):
                if self.rectangle[0]<self.rectangle[2]:
                    x1,x2 = self.rectangle[0],self.rectangle[2]
                else:
                    x1,x2 = self.rectangle[2],self.rectangle[0]
                if self.rectangle[1]<self.rectangle[3]:
                    y1,y2 = self.rectangle[1],self.rectangle[3]
                else:
                    y1,y2 = self.rectangle[3],self.rectangle[1]
                return x1,y1,x2,y2
        
            def InputEvent(self, msg):
                dev = msg.GetLong(c4d.BFM_INPUT_DEVICE)
                if dev == c4d.BFM_INPUT_MOUSE:
                    mousex = msg.GetLong(c4d.BFM_INPUT_X)
                    mousey = msg.GetLong(c4d.BFM_INPUT_Y)
                    start_x = mx = mousex - self.Local2Global()['x']
                    start_y = my = mousey - self.Local2Global()['y']
                    channel = msg.GetLong(c4d.BFM_INPUT_CHANNEL)
        
                    if channel == c4d.BFM_INPUT_MOUSELEFT:
                        print("mouse down")
        
                    #drag interaction
                    state = c4d.BaseContainer()
                    self.MouseDragStart(c4d.KEY_MLEFT,start_x, start_y, c4d.MOUSEDRAGFLAGS_DONTHIDEMOUSE| c4d.MOUSEDRAGFLAGS_NOMOVE )
                    while True:
                        result, dx, dy, channels = self.MouseDrag()
        
                        #end of Drag
                        if result == c4d.MOUSEDRAGRESULT_ESCAPE:
                            break
                        if not self.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSELEFT, state):
                            print "other clicks"
                            break
                        if state[c4d.BFM_INPUT_VALUE] == 0:
                            #mouse release
                            self.rectangle = [-1,-1,-1,-1]
                            self.Redraw()
                            break
        
                        #not moving, continue
                        if dx == 0 and dy == 0:
                            continue
        
                        #dragging
                        mx -= dx
                        my -= dy
        
                        print(mx,my)
                        self.rectangle = [start_x,start_y,mx,my]
                        self.Redraw()
        
                return True
        
            def Message(self, msg, result):
                return c4d.gui.GeUserArea.Message(self, msg, result)
        
        class MyDialog(c4d.gui.GeDialog):
            def __init__(self):
                pass
        
            def CreateLayout(self):
                self.SetTitle("Drag Test")
                self.area = Area()
                self.AddUserArea(1000, c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT)
                self.AttachUserArea(self.area, 1000)
                self.GroupEnd()
                return True
        
        def main():
            dialog = None
            dialog = MyDialog()
            dialog.Open(c4d.DLG_TYPE_MODAL_RESIZEABLE, xpos=-2, ypos=-2, defaultw=960, defaulth=540)
        
        if __name__=='__main__':
            main()
        
        1 Reply Last reply Reply Quote 0
        • ?
          A Former User
          last edited by

          Hello. Just checking in again: can anyone please help me with some advice on dragging in the GeUserArea? Have I set it up properly and why is there no realtime feedback in the While loop?

          Thank you!

          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by

            Hi @blastframe,

            In general, it's not necessary to bump a topic however since the topic is already 5 days ago without an answer from us, in this case, no issue at all, and sorry for the time needed, understand that we also have other duties to do but don't worry you are not forgotten.

            We are working on a more general example that draws a bunch of cubes and let you drag them. It's under review process, once the review will be done it will be on GitHub (and we will add it to this thread), here a preview of it.
            dragExample.gif

            Anyway since it's already 5days longs, dragging is a bit particular and our documentation/example will be adapted since explanations provided for the moment are not very clear.

            So here are the usual steps to do drag operation in a GeUserArea (Very Similar to what you will in a ToolData::MouseInput)

                def InputEvent(self, msg):
                    """
                    Called by Cinema 4D, when there is an user interaction (click) on the GeUserArea.
                    This is the place to catch and handle drag interaction.
            
                    :param msg: The event container.
                    :type msg: c4d.BaseContainer
                    :return: True if the event was handled, otherwise False.
                    :rtype: bool
                    """
                    # Retrieves the initial position of the click
                    mouseX = msg[c4d.BFM_INPUT_X]
                    mouseY = msg[c4d.BFM_INPUT_Y]
            
                    # Initializes the start of the dragging process (needs to be initialized with the original mouseX, mouseY).
                    self.MouseDragStart(c4d.KEY_MLEFT, mouseX, mouseY, c4d.MOUSEDRAGFLAGS_DONTHIDEMOUSE | c4d.MOUSEDRAGFLAGS_NOMOVE)
                    isFirstTick = True
            
                    # MouseDrag needs to be called all times to update information about the current drag process.
                    # This allows catching when the mouse is released and leaves the infinite loop.
                    while True:
            
                        # Updates the current mouse information
                        result, deltaX, deltaY, channels = self.MouseDrag()
                        if result != c4d.MOUSEDRAGRESULT_CONTINUE:
                            break
            
                        # The first tick is ignored as deltaX/YY includes the mouse clicking behavior with a deltaX/Y always equal to 4.0.
                        # However it can be useful to do some initialization or even trigger single click event
                        if isFirstTick:
                            isFirstTick = False
                            continue
            
                        # If the mouse didn't move, don't need to do anything, since we passed c4d.MOUSEDRAGFLAGS_NOMOVE it's unlikely to happen
                        if deltaX == 0.0 and deltaY == 0.0:
                            continue
            
                        # Updates mouse position with the updated delta
                        mouseX -= deltaX
                        mouseY -= deltaY
                        self.clickedPos = mouseX, mouseY
            
                        # Do some operation
            
                        # Redraw the GeUserArea during the drag process (it will call DrawMsg)
                        self.Redraw()
            
                    # Asks why we leave the while loop
                    endState = self.MouseDragEnd()
            
                    # If the drag process was ended because the user releases the mouse.
                    if endState == c4d.MOUSEDRAGRESULT_FINISHED:
                        print "Drag finished normally"
                        # Probably change some internal data and then redraw
            
                    # If the drag process was canceled by the user
                    elif endState == c4d.MOUSEDRAGRESULT_ESCAPE:
                        print "Drag was escaped, do Nothing, reset initial state"
                            # Probably change some internal data and then redraw to restore the initial content
            
                    return True
            

            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            ? 1 Reply Last reply Reply Quote 2
            • ?
              A Former User @m_adam
              last edited by

              @m_adam

              Hi Maxime,
              Thank you for the response and guidance on dragging in the C4D GeUserArea. I had certainly imagined that your team has many other duties (including the fantastic support you provide on this forum). It seemed from the previous response that it was the last word as I didn't have any indication your team was still working on the issue. I am very happy to see that you will be shining light on the GeUserArea dragging process! The example you shared looks very promising as well. I very much look forward to seeing the code: thank you! 😄

              1 Reply Last reply Reply Quote 0
              • lasselauchL
                lasselauch
                last edited by lasselauch

                Interesting example, @m_adam!

                If I copy/paste the code into the script manager the gui is empty. Which is funny, because yesterday or so I tried it and it worked...

                Has the code been updated? Anyone else experiencing this..?
                (checked in multiple versions...)

                Cheers,
                Lasse

                M 1 Reply Last reply Reply Quote 0
                • M
                  m_adam @lasselauch
                  last edited by

                  @lasselauch said in Drag & Drop to Reorder in GUI:

                  Interesting example, @m_adam!

                  If I copy/paste the code into the script manager the gui is empty. Which is funny, because yesterday or so I tried it and it worked...

                  Has the code been updated? Anyone else experiencing this..?
                  (checked in multiple versions...)

                  Cheers,
                  Lasse

                  Maybe because the code I provided is only for Mouse Input, so nothing is drawn and its purpose its only to demonstrate how to handle drag in a GeUserArea. So if you simply copy/paste my script yes it will not work, at least we didn't change anything.

                  Cheers,
                  Maxime.

                  MAXON SDK Specialist

                  Development Blog, MAXON Registered Developer

                  lasselauchL 1 Reply Last reply Reply Quote 0
                  • lasselauchL
                    lasselauch @m_adam
                    last edited by

                    @m_adam Ah... I thought that was a working example as shown in your gif and I've already had it running at one point... Sorry! My bad.

                    1 Reply Last reply Reply Quote 0
                    • M
                      m_adam
                      last edited by m_adam

                      Sorry for the delay here the full example.

                      """
                      Copyright: MAXON Computer GmbH
                      Author: Maxime Adam
                      
                      Description:
                          - Creates and attaches a GeUserArea to a Dialog.
                          - Creates a series of aligned squares, that can be dragged and swapped together.
                      
                      Note:
                          - This example uses weakref, I encourage you to read https://pymotw.com/2/weakref/.
                      
                      Class/method highlighted:
                          - c4d.gui.GeUserArea
                          - GeUserArea.DrawMsg
                          - GeUserArea.InputEvent
                          - GeUserArea.MouseDragStart
                          - GeUserArea.MouseDrag
                          - GeUserArea.MouseDragEnd
                          - c4d.gui.GeDialog
                          - GeDialog.CreateLayout
                          - GeDialog.AddUserArea
                          - GeDialog.AttachUserArea
                      
                      
                      Compatible:
                          - Win / Mac
                          - R13, R14, R15, R16, R17, R18, R19, R20
                      """
                      import c4d
                      import weakref
                      
                      # Global variable to determine the Size of our Square.
                      SIZE = 100
                      
                      
                      class Square(object):
                          """
                          Abstract class to represent a Square in a GeUserArea.
                          """
                      
                          def __init__(self, geUserArea, index):
                              self.index = index  # The initial square index (only used to differentiate square between each others)
                              self.w = SIZE       # The width of the square
                              self.h = SIZE       # The height of the square
                              self.col = c4d.Vector(0.5)  # The default color of the square
                              self.parentGeUserArea = weakref.ref(geUserArea)  # A weak reference to the host GeUserArea
                      
                          def GetParentedIndex(self):
                              """
                              Returns the current index in the parent list.
                      
                              :return: The index or c4d.NOTOK if there is no parent.
                              :rtype: int
                              """
                              parent = self.GetParent()
                              if parent is None:
                                  return c4d.NOTOK
                      
                              return parent._squareList.index(self)
                      
                          def GetParent(self):
                              """
                              Retrieves the parent instance, stored in the weakreaf self.parentGeUserArea.
                      
                              :return: The parent instance of the Square.
                              :rtype: c4d.gui.GeUserArea
                              """
                              if self.parentGeUserArea:
                                  geUserArea = self.parentGeUserArea()
                                  if geUserArea is None:
                                      raise RuntimeError("GeUserArea parent is not valid.")
                                  return geUserArea
                      
                              return None
                      
                          def DrawNormal(self, x, y):
                              """
                              Called by the parent GeUserArea to draw the Square normally.
                      
                              :param x: X position to draw.
                              :param y: Y position to draw.
                              """
                              geUserArea = self.GetParent()
                              geUserArea.DrawSetPen(self.col)
                              geUserArea.DrawRectangle(x, y,
                                                       x + self.w, y + self.h)
                      
                              geUserArea.DrawText(str(self.index), x, y)
                      
                          def DrawDraggedInitial(self, x, y):
                              """
                              Called by the parent GeUserArea when the Square is dragged
                              with the initial position (same coordinate than DrawNormal).
                      
                              :param x: X position to draw.
                              :param y: Y position to draw.
                              """
                              geUserArea = self.GetParent()
                              geUserArea.DrawBorder(c4d.BORDER_ACTIVE_1,
                                                    x, y,
                                                    x + self.w, y + self.h)
                      
                          def DrawDragged(self, x, y):
                              """
                              Called by the parent GeUserArea when the Square is dragged
                              with the current mouse position.
                      
                              :param x: X position to draw.
                              :param y: Y position to draw.
                              """
                              geUserArea = self.GetParent()
                              geUserArea.DrawSetPen(c4d.Vector(1))
                              geUserArea.DrawRectangle(int(x), int(y),
                                                       int(x + self.w), int(y + self.h))
                      
                              geUserArea.DrawText(str(self.index), int(x + (SIZE / 2.0)), int(y + (SIZE / 2.0)))
                      
                      
                      class DraggingArea(c4d.gui.GeUserArea):
                          """
                          Custom implementation of a GeUserArea that creates 4 squares and lets you drag them.
                          """
                      
                          def __init__(self):
                              self._squareList = []   # Stores a list of Square that will be draw in the GeUserArea.
                      
                              self.draggedObj = None  # None if not dragging, Square if dragged
                              self.clickedPos = None  # None if not dragging, tuple(X, Y) if dragged
                      
                              # Creates 4 squares
                              self.CreateSquare()
                              self.CreateSquare()
                              self.CreateSquare()
                              self.CreateSquare()
                      
                          # ===============================
                          #  Square management
                          # ===============================
                          def CreateSquare(self):
                              """
                              Creates a square that will be draw later.
                      
                              :return: The created square
                              :rtype: Square
                              """
                              square = Square(self, len(self._squareList))
                              self._squareList.append(square)
                              return square
                      
                          def GetXYFromId(self, index):
                              """
                              Retrieves the X, Y op, left position according to an index in order.
                              This produces an array of Square correctly aligned.
                      
                              :param index: The index to retrieve X, Y from.
                              :type index: int
                              :return: tuple(x left position, y top position).
                              :return: tuple(int, int)
                              """
                              x = SIZE * index
                              xPadding = 5 * index
                              x += xPadding
                              y = 5
                      
                              return x, y
                      
                          def GetIdFromXY(self, xIn, yIn):
                              """
                              Retrieves the square id stored in self._squareList according to its normal (not dragged) position.
                      
                              :param xIn: The position in x.
                              :type xIn: int
                              :param yIn: The position in y.
                              :type yIn: int
                              :return: The id or c4d.NOTOK (-1) if not found.
                              :rtype: int
                              """
                      
                              # We could optimize the method by reversing the algorithm  from GetXYFromID,
                              # But for now we just iterate all squares and see which one is correct.
                              for squareId, square in enumerate(self._squareList):
                                  x, y = self.GetXYFromId(squareId)
                      
                                  if x < xIn < x + SIZE and y < yIn < y + SIZE:
                                      return squareId
                      
                              return c4d.NOTOK
                      
                          # ===============================
                          #  Drawing management
                          # ===============================
                      
                          def DrawSquares(self):
                              """
                              Called in DrawMsg.
                              Draws all squares contained in self._squareList
                              """
                              for squareId, square in enumerate(self._squareList):
                                  x, y = self.GetXYFromId(squareId)
                                  if square is not self.draggedObj:
                                      square.DrawNormal(x, y)
                      
                                  else:
                                      square.DrawDraggedInitial(x, y)
                      
                          def DrawDraggedSquare(self):
                              """
                              Called in DrawMsg.
                              Draws the dragged squares
                              """
                              if self.draggedObj is None or self.clickedPos is None:
                                  return
                      
                              x, y = self.clickedPos
                      
                              self.draggedObj.DrawDragged(x, y)
                      
                          def DrawMsg(self, x1, y1, x2, y2, msg):
                              """
                              This method is called automatically when Cinema 4D Draw the Gadget.
                      
                              :param x1: The upper left x coordinate.
                              :type x1: int
                              :param y1: The upper left y coordinate.
                              :type y1: int
                              :param x2: The lower right x coordinate.
                              :type x2: int
                              :param y2: The lower right y coordinate.
                              :type y2: int
                              :param msg_ref: The original mesage container.
                              :type msg_ref: c4d.BaseContainer
                              """
                      
                              # Initializes draw region
                              self.OffScreenOn()
                              self.SetClippingRegion(x1, y1, x2, y2)
                      
                              # Get default Background color
                              defaultColorRgbDict = self.GetColorRGB(c4d.COLOR_BG)
                              defaultColorRgb = c4d.Vector(defaultColorRgbDict["r"], defaultColorRgbDict["g"], defaultColorRgbDict["b"])
                              defaultColor = defaultColorRgb / 255.0
                      
                              self.DrawSetPen(defaultColor)
                              self.DrawRectangle(x1, y1, x2, y2)
                      
                              # First draw pass, we draw all not dragged object
                              self.DrawSquares()
                      
                              # Last draw pass, we draw the dragged object, this way dragged square is drawn on top of everything.
                              self.DrawDraggedSquare()
                      
                          # ===============================
                          #  Dragging management
                          # ===============================
                          @property
                          def isCurrentlyDragged(self):
                              """
                              Checks if a dragging operation currently occurs.
                      
                              :return: True if a dragging operation currently occurs otherwise False.
                              :rtype: bool
                              """
                              return self.clickedPos is not None and self.draggedObj is not None
                      
                          def GetDraggedSquareWithPosition(self):
                              """
                              Retrieves the clicked square during a drag event from the click position.
                      
                              :return: The square or None if there is nothing dragged.
                              :rtype: Union[Square, None]
                              """
                              if self.clickedPos is None:
                                  return None
                      
                              x, y = self.clickedPos
                              squareId = self.GetIdFromXY(x, y)
                              if squareId == c4d.NOTOK:
                                  return None
                      
                              return self._squareList[squareId]
                      
                          def InputEvent(self, msg):
                              """
                              Called by Cinema 4D, when there is a user interaction (click) on the GeUserArea.
                              This is the place to catch and handle drag interaction.
                      
                              :param msg: The event container.
                              :type msg: c4d.BaseContainer
                              :return: True if the event was handled, otherwise False.
                              :rtype: bool
                              """
                              # Do nothing if its not a left mouse click event
                              if msg[c4d.BFM_INPUT_DEVICE] != c4d.BFM_INPUT_MOUSE and msg[c4d.BFM_INPUT_CHANNEL] != c4d.BFM_INPUT_MOUSELEFT:
                                  return True
                              
                              # Retrieves the initial position of the click
                              mouseX = msg[c4d.BFM_INPUT_X]
                              mouseY = msg[c4d.BFM_INPUT_Y]
                      
                              # Initializes the start of the dragging process (needs to be initialized with the original mouseX, mouseY).
                              self.MouseDragStart(c4d.KEY_MLEFT, mouseX, mouseY, c4d.MOUSEDRAGFLAGS_DONTHIDEMOUSE | c4d.MOUSEDRAGFLAGS_NOMOVE)
                              isFirstTick = True
                      
                              # MouseDrag needs to be called all time to update information about the current drag process.
                              # This allow to catch when the mouse is released and leave the infinite loop.
                              while True:
                      
                                  # Updates the current mouse information
                                  result, deltaX, deltaY, channels = self.MouseDrag()
                                  if result != c4d.MOUSEDRAGRESULT_CONTINUE:
                                      break
                      
                                  # The first tick is ignored as deltaX/Y include the mouse clicking behavior with a deltaX/Y always equal to 4.0.
                                  # However it can be useful to do some initialization or even trigger single click event
                                  if isFirstTick:
                                      isFirstTick = False
                                      continue
                      
                                  # If the mouse didn't move, don't need to do anything
                                  if deltaX == 0.0 and deltaY == 0.0:
                                      continue
                      
                                  # Updates mouse position with the updated delta
                                  mouseX -= deltaX
                                  mouseY -= deltaY
                                  self.clickedPos = mouseX, mouseY
                      
                                  # Retrieves the clicked square
                                  square = self.GetDraggedSquareWithPosition()
                      
                                  # Defines the draggedObj only if the user clicked on a square and is not yet already defined
                                  if square is not None and self.draggedObj is None:
                                      self.draggedObj = square
                      
                                  # Redraw the GeUserArea (it will call DrawMsg)
                                  self.Redraw()
                      
                              # Asks why we leave the while loop
                              endState = self.MouseDragEnd()
                      
                              # If the drag process was ended because the user releases the mouse.
                              # Note that while we are not anymore really in the Drag Pooling, from our implementation we consider we are still
                              # and don't clear directly the data, so self.clickedPos and self.draggedObj still refer to the last tick of the
                              # MouseDrag pool and we will clear it once we don't need anymore those data (after this if statement).
                              if endState == c4d.MOUSEDRAGRESULT_FINISHED:
                      
                                  # Checks a dragged object is set
                                  # in case of a simple click without mouse movement nothing has to be done.
                                  if self.isCurrentlyDragged:
                                      # Retrieves the initial index of the dragged object.
                                      currentIndex = self.draggedObj.GetParentedIndex()
                      
                                      # Retrieves the index where the drag operation ended. If we find an ID, swap both items.
                                      releasedSquare = self.GetDraggedSquareWithPosition()
                                      if releasedSquare is not None:
                                          targetIndex = releasedSquare.GetParentedIndex()
                      
                                          # Swap items only if source index and target index are different
                                          if targetIndex != currentIndex:
                                              self._squareList[currentIndex], self._squareList[targetIndex] = self._squareList[targetIndex], \
                                                                                                              self._squareList[currentIndex]
                      
                                      # In case the user release the mouse not on another square.
                                      # Swaps the current square to either the first position or last position.
                                      else:
                                          # if current Index is already the first one, make no sense to inserts it
                                          if currentIndex != 0:
                                              # If the X position is before the X position of the first square
                                              # Removes and inserts it back to the first position.
                                              if self.clickedPos[0] < self.GetXYFromId(0)[0]:
                                                  self._squareList.remove(self.draggedObj)
                                                  self._squareList.insert(0, self.draggedObj)
                      
                                          # Retrieves the last index
                                          lastIndex = len(self._squareList) - 1
                                          # if current Index is already the last one, make no sense to insert it
                                          if currentIndex != lastIndex:
                                              if self.clickedPos[0] > self.GetXYFromId(lastIndex)[0] + SIZE:
                                                  # If the X position is after the X position of the last square (and its size)
                                                  # Removes and inserts it back to the last position.
                                                  self._squareList.remove(self.draggedObj)
                                                  self._squareList.insert(lastIndex, self.draggedObj)
                      
                              # Cleanup and refresh information if we dragged something
                              if self.clickedPos is not None or self.draggedObj is not None:
                                  self.clickedPos = None
                                  self.draggedObj = None
                                  self.Redraw()
                      
                              return True
                      
                      
                      class MyDialog(c4d.gui.GeDialog):
                          """
                          Creates a Dialog with only a GeUserArea within.
                          """
                      
                          def __init__(self):
                              # It's important to stores our Python implementation instance of the GeUserArea in class variable,
                              # This way we are sure the GeUserArea instance live as long as the GeDialog.
                              self.area = DraggingArea()
                      
                          def CreateLayout(self):
                              """
                              This method is called automatically when Cinema 4D Create the Layout (display) of the Dialog.
                              """
                              self.AddUserArea(1000, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT)
                              self.AttachUserArea(self.area, 1000)
                              return True
                      
                      
                      def main():
                          # Creates a new dialog
                          dialog = MyDialog()
                      
                          # Opens it
                          dialog.Open(dlgtype=c4d.DLG_TYPE_MODAL_RESIZEABLE, defaultw=500, defaulth=500)
                      
                      
                      if __name__ == '__main__':
                          main()
                      

                      It will be included soon on Github.
                      Cheers,
                      Maxime

                      MAXON SDK Specialist

                      Development Blog, MAXON Registered Developer

                      ? 2 Replies Last reply Reply Quote 1
                      • ?
                        A Former User @m_adam
                        last edited by A Former User

                        This post is deleted!
                        1 Reply Last reply Reply Quote 0
                        • ?
                          A Former User @m_adam
                          last edited by A Former User

                          @m_adam This is nice work, Maxime, thank you! I have to check out weakref 🔍

                          I'm getting a couple of errors when clicking in a part of the GeUserArea without a square or hitting a key on the keyboard. How can I handle these?

                          No square:

                          line 331, in InputEvent
                                  currentIndex = self.draggedObj.GetParentedIndex()   
                              AttributeError: 'NoneType' object has no attribute 'GetParentedIndex'`
                          

                          Keyboard input:

                          line 278, in InputEvent
                              self.MouseDragStart(c4d.KEY_MLEFT, mouseX, mouseY, c4d.MOUSEDRAGFLAGS_DONTHIDEMOUSE | c4d.MOUSEDRAGFLAGS_NOMOVE)
                          TypeError: a float is required
                          
                          1 Reply Last reply Reply Quote 1
                          • M
                            m_adam
                            last edited by

                            Thanks, I just updated the previous code with a fix to both issues.
                            The first one was related because in isCurrentlyDragged I used or while it should be and.
                            The second one is because InputEvent is called for any kind of event, and I didn't filter when I want to do the drag operation so I added the next code at the start of the Message method:

                                    # Only do something if its a left mouse click
                                    if msg[c4d.BFM_INPUT_DEVICE] != c4d.BFM_INPUT_MOUSE and msg[c4d.BFM_INPUT_CHANNEL] != c4d.BFM_INPUT_MOUSELEFT:
                                        return True
                            

                            Cheers,
                            Maxime.

                            MAXON SDK Specialist

                            Development Blog, MAXON Registered Developer

                            1 Reply Last reply Reply Quote 0
                            • ?
                              A Former User
                              last edited by

                              @m_adam Terrific work, Maxime! This is very helpful to me and I'm sure the many others who want to learn about dragging in Cinema 4D's UI. Excellent job! 🍾

                              1 Reply Last reply Reply Quote 0
                              • First post
                                Last post