Solved Grabbing a GeUserArea when using InputEvent

Hello,
Please excuse my multiple posts about the GeUserArea in a Scrollgroup, but I was unable to find any information about the following in the documentation or the forum.

By default, the GeUserArea in a ScrollGroup in this script can be grabbed by middle-mouse button clicking and dragging.
GeUserArea_Grab.jpg

In my plugin however, I need to override the GeUserArea:InputEvent method. This seems to be where the grabbing functionality exists. How can I add the middle-mouse grab to my InputEvent override? Thank you!

import c4d
import random
from c4d import gui

GADGET_ID_GEUSERAREA = 10000
SCROLL_ID = 10001
SLIDER_ID = 10002


class ExampleGeUserArea(c4d.gui.GeUserArea):
    width = 400
    height = 500
    bmp_cache = None

    def GetBitmap(self):
        bmp = c4d.bitmaps.BaseBitmap()
        bmp.Init(self.width, self.height)
        for h in range(self.height):
            for w in range(self.width):
                r = random.randint(0, 70)
                bmp.SetPixel(w, h, r, r, r)
        return bmp

    def DrawMsg(self, x1, y1, x2, y2, msg):
        self.OffScreenOn()
        self.SetClippingRegion(x1, y1, x2, y2)
        self.DrawSetPen(c4d.Vector(1, 0, 0))
        self.DrawRectangle(x1, y1, x2, y2)
        if self.bmp_cache == None:
            self.bmp_cache = self.GetBitmap()

        self.DrawBitmap(self.bmp_cache, x1, y1, x2, y2, 0, 0,
                        w=self.bmp_cache.GetBw(),
                        h=self.bmp_cache.GetBh(),
                        mode=c4d.BMP_NORMAL)

    """
    def InputEvent(self, msg):
        #This stops the grab functionality
        device = msg.GetLong(c4d.BFM_INPUT_DEVICE)
        channel = msg.GetLong(c4d.BFM_INPUT_CHANNEL)
        if device == c4d.BFM_INPUT_MOUSE and channel == c4d.BFM_INPUT_MOUSEMIDDLE:
            print (device,channel)
        return True
    """

    def Message(self, msg, result):
        # Catch the draw message to cancel it (return True)
        # and call ourself the DrawMsg with the dimension we expect
        if msg.GetId() == c4d.BFM_DRAW:
            self.DrawMsg(0, 0, self.width, self.height, c4d.BaseContainer())

            return True

        return c4d.gui.GeUserArea.Message(self, msg, result)

    def GetMinSize(self):
        return self.width, self.height


class ExampleDialog(c4d.gui.GeDialog):
    geUserArea = ExampleGeUserArea()

    def _centerAlignScrollGroup(self):
        _, _, sgw, sgh = self.GetItemDim(SCROLL_ID).values()
        _, _, uaw, uah = self.GetItemDim(GADGET_ID_GEUSERAREA).values()

        sax1, say1, sax2, say2 = self.GetVisibleArea(SCROLL_ID).values()
        dx, dy = sgw - (sax2 - sax1), sgh - (say2 - say1) # 16, 15
        sgw -= dx
        sgh -= dy

        x, y = int((uaw - sgw) * .5), int((uah - sgh) * .5)
        self.SetVisibleArea(SCROLL_ID, x, y, x + sgw, y + sgh)

    def DrawUA(self, scale):
        self.geUserArea.width = int(400*scale)
        self.geUserArea.height = int(500*scale)
        self.LayoutChanged(SCROLL_ID)

    def CreateLayout(self):
        self.SetTitle("GeUserArea")
        if self.ScrollGroupBegin(SCROLL_ID, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, c4d.SCROLLGROUP_HORIZ | c4d.SCROLLGROUP_VERT):
            self.AddUserArea(GADGET_ID_GEUSERAREA, c4d.BFH_CENTER | c4d.BFH_SCALE |
                             c4d.BFV_CENTER | c4d.BFV_SCALE, initw=gui.SizePix(400), inith=gui.SizePix(500))
            self.AttachUserArea(self.geUserArea, GADGET_ID_GEUSERAREA)
        self.GroupEnd()
        self.AddEditSlider(SLIDER_ID, c4d.BFH_SCALEFIT | c4d.BFV_CENTER)
        return True

    def InitValues(self):
        self.SetFloat(SLIDER_ID, 1.0, min=0.01, max=2, step=0.01)
        self._centerAlignScrollGroup()
        return True

    def Command(self, id, msg):
        if id == SLIDER_ID:
            self.DrawUA(msg[c4d.BFM_ACTION_VALUE])
        self._centerAlignScrollGroup()
        return True


def main():
    global dlg
    dlg = ExampleDialog()
    dlg.Open(c4d.DLG_TYPE_ASYNC, pluginid=1234567, defaultw=300, defaulth=300)


if __name__ == "__main__":
    main()

Hi @blastframe,

thank you for reaching out to us. You are returning True in your commented InputEvent which consumes that event. So you have to return False and it will work like you want it to work. Although not being stated in the documents, I would also say a call to the base/super implementation would not hurt here for retrieving the return value.

Cheers,
Ferdinand

MAXON SDK Specialist
developers.maxon.net

Hi @blastframe,

thank you for reaching out to us. You are returning True in your commented InputEvent which consumes that event. So you have to return False and it will work like you want it to work. Although not being stated in the documents, I would also say a call to the base/super implementation would not hurt here for retrieving the return value.

Cheers,
Ferdinand

MAXON SDK Specialist
developers.maxon.net

@ferdinand That was so simple, thank you!

How would I call the Base/Super Implementation in this case? Do you mean something like this?

def InputEvent(self, msg):
    # override code goes here
    return super().InputEvent(msg)

Hi @blastframe,

yes, that would be one, and probably the more modern way to do it. I personally like also an explicit call to the base implementation. I think it is more readable for someone else reading your code. But that is probably a rather subjective choice. So for clarity, I am speaking about something like this:

foo = bar * baz
return c4d.gui.GeUserArea.InputEvent(self, msg)

There is of course the advantage of super taking care of calling the proper implementation(s) for you which is not taken care of by this approach, but you cannot have everything and in this case things are rather unambiguous for us here ;)

Cheers,
Ferdinand

MAXON SDK Specialist
developers.maxon.net