R21 GeUserArea in docked GeDialog behaves differently



  • Hello again,

    I have yet another of these strange situation where I start to think I am losing my mind. I cannot believe I am the only one encountering all these weird behaviours. First things I then do is assuming I am doing something wrong. Then I try to reproduce the problem by rewritting a complete dummy plugin with all the necessary code in order to obtain the same issue, but with all the details removed.
    This obviously takes quite a lot of time, but seems to be the only solution to pinpoint the issue in the hope a solution or workaround can be found.

    This time around I do seem to have issue with the redrawing of a GeUserArea when the GeDialog it is hosted by is docked. And this issue does not occur in R20 or previously. Meaning that it probably is a result or side effect from the new GUI.

    Below is the full implementation of the dummy plugin as presented in a single Main.cpp (in order to allow for an easier reproduction of the issue on anyone's development environment). If you're copy/pasting ... don't forget to include a res folder, containing an "icon.png" file.

    The code belows contains a class to handle the GeUserArea, another class for the GeDialog which hosts the userarea. And a class for the CommandData responsible for the dialog.
    The userarea is accessable via a global variable g_UserArea, which is initialized when the dialog and userarea are created. I have omitted sanity checks to keep code more readable.

    A MessageData plugin is also present, as this triggers the redrawing of the userarea on every EVMSG_CHANGE.
    There might be a cleaner and more appropriate way around this whole concept, but the way I have implemented is, was the way I found out several years ago. Since it seemed to work then, I kept using the same concept. Feel free to comment if a better way would be more appropriate.

    How to use the plugin:
    Selecting the plugin from "Extensions", this will perform the MyCommand::Execute which will open the dialog, which will create the userarea. Then, just clicking around in the 3D viewport (or any other action that triggers an EVMS_CHANGE) will result in the userarea to be redrawn. The userarea does show a gray background with a white circle.
    However, when docking the dialog into the layout and performing the same triggers will sometime result in the circle the be drawn in red.
    The reason behind this is that when non-docked the MyUserArea::DrawMsg passes x1, x2, y1, y2 as being the complete userarea rectangle.
    When docked y2 - y1 is not always the full height.

    Strangely enough, when you manually perform the MyCommand::Execute by selecting the plugin from the menu the circle is drawn in white (=> y2 - y1 equals the height) when going via the MessageData the result is different. This was not the case in R20 and previous releases.

    I can only assume this to be a bug in R21 ... or a change in behaviour which was intentional. I just cannot see what the intention would be for this change?

    // ========================
    // Cinema 4D C++ plugin
    //
    // PluginName: Test
    // Dummy "empty" plugin
    // ========================
    
    // Main.cpp
    
    #include "c4d.h"
    #include "lib_clipmap.h"
    
    // Dummy IDs - for demonstration purposes only
    #define MYCOMMAND_PLUGIN_ID	1000000
    #define MYDIALOG_PLUGIN_ID	1000001
    #define MYMESSAGE_PLUGIN_ID 1000002
    
    // gadget IDs
    #define IDC_USERAREA		50000
    
    // global var pointing to our user area
    class MyUserArea; // need to provide forward declaration (since not using header files)
    MyUserArea* g_UserArea = nullptr;
    
    // ====================================
    // GeUserArea
    // ====================================
    
    class MyUserArea : public GeUserArea
    {
    	INSTANCEOF(MyUserArea, GeUserArea)
    
    public:
    	MyUserArea(void);
    	virtual ~MyUserArea();
    
    	virtual Bool Init(void);
    	virtual Bool GetMinSize(Int32& w, Int32& h);
    	virtual void Sized(Int32 w, Int32 h);
    	virtual Bool InitValues(void);
    	virtual void DrawMsg(Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer& msg);
    
    private:
    	Int32 mWidth;
    	Int32 mHeight;
    
    	AutoAlloc<GeClipMap> mClipmap;
    };
    
    MyUserArea::MyUserArea(void) : mWidth(0), mHeight(0) {}
    MyUserArea::~MyUserArea(void) {}
    
    Bool MyUserArea::Init(void)
    {
    	return true;
    }
    
    Bool MyUserArea::GetMinSize(Int32& w, Int32& h)
    {
    	w = 100;
    	h = 100;
    	return true;
    }
    
    void MyUserArea::Sized(Int32 w, Int32 h)
    {
    	mWidth = w;
    	mHeight = h;
    }
    
    Bool MyUserArea::InitValues(void)
    {
    	mWidth = GetWidth();
    	mHeight = GetHeight();
    	return true;
    }
    
    void MyUserArea::DrawMsg(Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer& msg)
    {
    	OffScreenOn();
    	Int32 w = GetWidth();
    	Int32 h = GetHeight();
    
    	if (!mClipmap)
    		return;
    	mClipmap->Init(w, h, 32);
    	mClipmap->BeginDraw();
    
    	Vector color(128);
    	mClipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255);
    	mClipmap->FillRect(0, 0, w, h);
    
    	if ((x2 - x1) != w)
    		color = Vector(0, 255, 0); // green
    	else if ((y2 - y1) != h)
    		color = Vector(255, 0, 0); // red
    	else
    		color = Vector(255); // white
    	mClipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255);
    	if ((w > 0) && (h > 0))
    		mClipmap->Ellipse(0, 0, w-1, h-1);
    
    	mClipmap->EndDraw();
    	DrawBitmap(mClipmap->GetBitmap(), 0, 0, w, h, 0, 0, w, h, BMP_ALLOWALPHA);
    }
    
    // ====================================
    // GeDialog
    // ====================================
    
    class MyDialog : public GeDialog
    {
    public:
    	MyDialog(void);
    	virtual ~MyDialog(void);
    	virtual Bool CreateLayout(void);
    	virtual void DestroyWindow(void);
    
    	MyUserArea	mUserArea;
    };
    
    MyDialog::MyDialog(void) {}
    MyDialog::~MyDialog(void) {}
    
    Bool MyDialog::CreateLayout(void)
    {
    	Bool res = GeDialog::CreateLayout();
    
    	GroupBegin(0, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, ""_s, 0);
    	GroupBorderSpace(4, 4, 4, 4);
    
    	// userarea for drawing
    	GroupBegin(0, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, String(), 0);
    	C4DGadget* ua = AddUserArea(IDC_USERAREA, BFH_SCALEFIT | BFV_SCALEFIT, 400, 400);
    	if (ua)
    		AttachUserArea(mUserArea, IDC_USERAREA);
    	GroupEnd();
    
    	GroupEnd();
    
    	return res;
    }
    
    void MyDialog::DestroyWindow(void)
    {
    	if (g_UserArea)
    		g_UserArea = nullptr;
    }
    
    // ====================================
    // CommandData
    // ====================================
    
    class MyCommand : public CommandData
    {
    	INSTANCEOF(MyCommand, CommandData)
    
    public:
    	virtual Bool Execute(BaseDocument* doc, GeDialog* parentManager);
    	virtual Bool RestoreLayout(void* secret);
    
    	MyDialog dlg;
    };
    
    Bool MyCommand::Execute(BaseDocument* doc, GeDialog* parentManager)
    {
    	if (!dlg.IsOpen())
    	{
    		dlg.Open(DLG_TYPE::ASYNC, MYDIALOG_PLUGIN_ID, -1, -1, 0, 0, 0);
    		g_UserArea = &dlg.mUserArea;
    	}
    	else
    	{
    		if (g_UserArea)
    			g_UserArea->Redraw();
    	}
    
    	return true;
    }
    
    Bool MyCommand::RestoreLayout(void* secret)
    {
    	Int32 subid = ((RestoreLayoutSecret*)secret)->subid;
    	if (subid == 0)
    		return dlg.RestoreLayout(MYDIALOG_PLUGIN_ID, subid, secret);
    	return false;
    }
    
    
    // ====================================
    // MessageData
    // ====================================
    
    class MyMessage : public MessageData
    {
    public:
    	virtual Bool CoreMessage(Int32 id, const BaseContainer &msg);
    };
    
    Bool MyMessage::CoreMessage(Int32 id, const BaseContainer &msg)
    {
    	// we are only interested in EVMSG_CHANGE
    	if (id != EVMSG_CHANGE)
    		return true;
    
    	if (g_UserArea)
    	{
    		//ApplicationOutput("Something has changed, trigger to redraw our UserArea");
    		CallCommand(MYCOMMAND_PLUGIN_ID);
    	}
    
    	return true;
    }
    
    
    // ====================================
    // Register plugins
    // ====================================
    
    Bool RegisterMyCommand(void)
    {
    	return RegisterCommandPlugin(MYCOMMAND_PLUGIN_ID, "Test"_s, 0, AutoBitmap("icon.png"_s), "Test"_s, NewObjClear(MyCommand));
    }
    
    Bool RegisterMyMessage(void)
    {
    	return RegisterMessagePlugin(MYMESSAGE_PLUGIN_ID, String(), 0, NewObjClear(MyMessage));
    }
    
    
    // ====================================
    // Plugin Main 
    // ====================================
    Bool PluginStart(void)
    {
    	ApplicationOutput("Test"_s);
    	
    	RegisterMyCommand();
    	RegisterMyMessage();
    
    	return true;
    }
    void PluginEnd(void)
    {
    }
    Bool PluginMessage(Int32 id, void * data)
    {
    	switch (id) {
    	case C4DPL_INIT_SYS:
    		if (!g_resource.Init())
    			return false;
    		return true;
    	case C4DMSG_PRIORITY:
    		return true;
    	case C4DPL_BUILDMENU:
    		break;
    	case C4DPL_ENDACTIVITY:
    		return true;
    	}
    	return false;
    }
    
    


  • Hi @C4DS thanks for reaching us. I can indeed confirm the issue.

    I've reached the development team about, I will let you know about any workaround.

    Cheers,
    Maxime.