CUSTOMGUI_TREEVIEW in GetDDescription()



  • Maybe I am missing it in my searches, but I'd like to add a CUSTOMGUI_TREEVIEW to my Object plugin using GetDDescription() and set it up properly for use like in a GeDialog. I have some code to do the basic creation in GetDDescription() but then you need to have the TreeViewCustomGui* for the TreeViewFunctions and other setup. Yes, this can all be found by arduous searching and piecing together but if someone has an already working basic example that would be much appreciated.



  • Here's my rig from an old plugin that displays objects, WiP though:

    Hope, this helps a bit though. :)
    Node to be displayed:

    #pragma once
    
    
    #include "c4d.h"
    
    class ASNode : public NodeData
    {
    public:
    	static NodeData* Alloc()
    	{
    		return NewObjClear(ASNode);
    	}
    
    	void SetLink(C4DAtomGoal* atomGoal);
    	C4DAtomGoal* GetLink() const;
    	bool HasInstances() const;
    	bool HasScaledChildren() const;
    
    private:
    	AutoAlloc<BaseLink> _linkedObj;
    	BaseDocument* _doc = nullptr;
    };
    

    Worker:

    #pragma once
    
    #include "c4d.h"
    
    
    class Worker
    {
    public:
    	void AddObjects(AtomArray* linkedObj) const;
    	void UnselectNodes() const;
    	void ValidateNodes() const;
    	void RemoveSelectedNodes() const;
    	bool ContainsObject(C4DAtom* atom) const;
    	GeListHead* GetRoot() const;
    
    private:
    	AutoAlloc<GeListHead> _nodelist;
    };
    
    

    TreeViewFunctions:

    #pragma once
    
    #include "c4d.h"
    #include "c4d_symbols.h"
    #include "ASNode.h"
    
    class AssetTreeViewFunctions : public TreeViewFunctions
    {
    public:
    	// Manually draw cells which are set to LV_USER
    	void DrawCell(void* root, void* userdata, void* obj, Int32 col, DrawInfo* drawinfo, const GeData& bgColor)
    	{
    		// TODO: implement drawing to cells
    		switch (col)
    		{
    		case COLUMN_CATEGORY:
    			{
    				// Draw the category string (get from cycle)
    				break;
    			}
    
    		case COLUMN_INSTANCES:
    			{
    				// Draws warning icon if hierarchy contains instances
    				BaseList2D* listNode = static_cast<BaseList2D*>(obj);
    				ASNode* node = listNode->GetNodeData<ASNode>();
    				if (node->HasInstances())
    				{
    					Int32 wx = drawinfo->xpos;
    					Int32 wy = drawinfo->ypos;
    					Int32 ww = drawinfo->width;
    					Int32 wh = drawinfo->height;
    
    
    					if (ww > 16)
    					{
    						wx += (ww - 16) / 2;
    						ww = 16;
    					}
    					if (wh > 16)
    					{
    						wy += (wh - 16) / 2;
    						wh = 16;
    					}
    
    					const Int32 drawflags = BMP_ALLOWALPHA | BMP_NORMALSCALED;
    
    					const AutoBitmap icon("warning.tif");
    					drawinfo->frame->DrawSetPen(bgColor);
    					drawinfo->frame->DrawBitmap(icon, wx, wy, ww, wh, 0, 0, 64, 64, drawflags);
    				}
    				break;
    			}
    
    		case COLUMN_SCALE:
    			{
    				// Draw warning icon if any object in the linked object's hierarchy scale parameters are not set to (1,1,1)
    				GeListNode* listNode = static_cast<GeListNode*>(obj);
    				ASNode* node = listNode->GetNodeData<ASNode>();
    				if (node->HasScaledChildren())
    				{
    					Int32 wx = drawinfo->xpos;
    					Int32 wy = drawinfo->ypos;
    					Int32 ww = drawinfo->width;
    					Int32 wh = drawinfo->height;
    
    
    					if (ww > 16)
    					{
    						wx += (ww - 16) / 2;
    						ww = 16;
    					}
    					if (wh > 16)
    					{
    						wy += (wh - 16) / 2;
    						wh = 16;
    					}
    
    					const Int32 drawflags = BMP_ALLOWALPHA | BMP_NORMALSCALED;
    
    					const AutoBitmap icon("warning.tif");
    					drawinfo->frame->DrawSetPen(bgColor);
    					drawinfo->frame->DrawBitmap(icon, wx, wy, ww, wh, 0, 0, 64, 64, drawflags);
    				}
    				break;
    			}
    
    		default:
    			break;
    		}
    	}
    
    	void Select(void* root, void* userdata, void* obj, Int32 mode)
    	{
    		// TODO: Select Node if object in scene is selected (optional)
    
    		switch (mode)
    		{
    		case SELECTION_NEW:
    			{
    				_worker->UnselectNodes();
    				static_cast<BaseList2D*>(obj)->SetBit(BIT_ACTIVE);
    				break;
    			}
    		case SELECTION_SUB:
    			{
    				static_cast<BaseList2D*>(obj)->DelBit(BIT_ACTIVE);
    				break;
    			}
    		case SELECTION_ADD:
    			{
    				static_cast<BaseList2D*>(obj)->SetBit(BIT_ACTIVE);
    				break;
    			}
    		default:
    			break;
    		}
    	}
    
    	void SelectionChanged(void* root, void* userdata)
    	{
    		// TODO: Display newly selected node attributes in DescriptionGui
    	}
    
    	Bool IsSelected(void* root, void* userdata, void* obj)
    	{
    		// Check if a Node in the tree is selected
    		return static_cast<BaseList2D*>(obj)->GetBit(BIT_ACTIVE);
    	}
    
    	Int32 IsChecked(void* root, void* userdata, void* obj, Int32 lColumn)
    	{
    		// TODO: Implement checbox settings retrieval
    		return 0;
    	}
    
    	void SetCheck(void* root, void* userdata, void* obj, Int32 lColumn, Bool bCheck, const BaseContainer& bcMsg)
    	{
    		// TODO: Handle material/texture options dependencies and switch checkboxes
    	}
    
    	void DeletePressed(void* root, void* userdata)
    	{
    		if (!root)
    			return;
    
    		_worker->RemoveSelectedNodes();
    	}
    
    	Int32 DoubleClick(void* root, void* userdata, void* obj, Int32 col, MouseInfo* mouseinfo)
    	{
    		return NOTOK;
    	}
    
    
    	// Set width for headers
    	Int32 GetHeaderColumnWidth(void* root, void* userdata, Int32 col, GeUserArea* area)
    	{
    		if (!area)
    			return 0;
    
    		String headerName;
    		RetrieveHeaderString(col, headerName); // Retrieve width for every header string
    
    		return area->DrawGetTextWidth(headerName + "88"); // Add 2 letters
    	}
    
    
    	//	Draw headers cells
    	Bool DrawHeaderCell(void* root, void* userdata, Int32 col, DrawInfo* drawinfo)
    	{
    		// Draw cell background for header
    		drawinfo->frame->DrawSetPen(COLOR_BG);
    		drawinfo->frame->DrawRectangle(drawinfo->xpos, drawinfo->ypos, drawinfo->xpos + drawinfo->width - 1,
    		                               drawinfo->ypos + drawinfo->height - 1);
    
    		// Set header captions
    		String headerName;
    		RetrieveHeaderString(col, headerName);
    
    		drawinfo->frame->DrawSetFont(FONT_STANDARD);
    		drawinfo->frame->SetClippingRegion(drawinfo->xpos, drawinfo->ypos, drawinfo->width, drawinfo->height);
    
    		drawinfo->frame->DrawSetTextCol(COLOR_TEXT, COLOR_TRANS);
    		drawinfo->frame->DrawText(headerName, drawinfo->xpos + 4, drawinfo->ypos + 2);
    		drawinfo->frame->ClearClippingRegion();
    
    		// Draw border horizontally
    		drawinfo->frame->DrawSetPen(COLOR_EDGEWH);
    		drawinfo->frame->DrawLine(drawinfo->xpos, drawinfo->ypos, drawinfo->xpos + drawinfo->width - 1, drawinfo->ypos);
    		drawinfo->frame->DrawLine(drawinfo->xpos, drawinfo->ypos, drawinfo->xpos, drawinfo->ypos + drawinfo->height - 1);
    
    		// Draw border vertically
    		drawinfo->frame->DrawSetPen(COLOR_EDGEDK);
    		drawinfo->frame->DrawLine(drawinfo->xpos + drawinfo->width - 1, drawinfo->ypos, drawinfo->xpos + drawinfo->width - 1,
    		                          drawinfo->ypos + drawinfo->height - 1);
    		drawinfo->frame->DrawLine(drawinfo->xpos, drawinfo->ypos + drawinfo->height - 1, drawinfo->xpos + drawinfo->width - 1,
    		                          drawinfo->ypos + drawinfo->height - 1);
    
    		return true;
    	}
    
    	// Retrieve ID
    	Int GetId(void* root, void* userdata, void* obj)
    	{
    		return Int(obj);
    	}
    
    
    	Int32 DragStart(void* root, void* userdata, void* obj)
    	{
    		return TREEVIEW_DRAGSTART_ALLOW | TREEVIEW_DRAGSTART_SELECT;
    	}
    
    
    	// Allow dragging objects into the TreeView
    	// TODO: implement node creation and linking
    	Int32 AcceptDragObject(void* root, void* userdata, void* obj, Int32 dragtype, void* dragobject, Bool& bAllowCopy)
    	{
    		return INSERT_AFTER | INSERT_BEFORE; // Allow only top-level insertion
    	}
    
    	Int32 GetDragType(void* root, void* userdata, void* obj)
    	{
    		// Allow re-arranging objects
    		return DRAGTYPE_ATOMARRAY;
    	}
    
    
    	// Drag one or multiple objects into TreeView
    	void InsertObject(void* root, void* userdata, void* obj, Int32 dragtype, void* dragobject, Int32 insertmode,
    	                  Bool bCopy)
    	{
    		switch (dragtype)
    		{
    		case DRAGTYPE_ATOMARRAY:
    			{
    				AtomArray* const arr = static_cast<AtomArray*>(dragobject); // Handle the dragged object as AtomArray
    				if (arr && _worker)
    				{
    					// TODO: Implement re-arranging
    					switch (insertmode)
    					{
    					case INSERT_UNDER:
    						_worker->AddObjects(arr);
    						break;
    
    					case INSERT_BEFORE:
    						_worker->AddObjects(arr);
    						break;
    					default:
    						_worker->AddObjects(arr);
    						break;
    					}
    				}
    				break;
    			}
    		default:
    			break;
    		}
    		// TODO: Show popup menu for categories on drop_finished (optional)
    	}
    
    
    	// Navigation functions
    	void* GetFirst(void* root, void* userdata)
    	{
    		if (!root)
    			return nullptr;
    
    		return static_cast<GeListHead*>(root)->GetFirst();
    	}
    
    	void* GetDown(void* root, void* userdata, void* obj)
    	{
    		return nullptr;
    	}
    
    	void* GetNext(void* root, void* userdata, void* obj)
    	{
    		return static_cast<GeListNode*>(obj)->GetNext();
    	}
    
    	void* GetPred(void* root, void* userdata, void* obj)
    	{
    		return static_cast<GeListNode*>(obj)->GetPred();
    	}
    
    
    	Bool IsOpened(void* root, void* userdata, void* obj)
    	{
    		// Always expand nodes. Does effectively nothing, because we always have a flat tree
    		return true;
    	}
    
    	// Set the node's name to the linked object's name
    	String GetName(void* root, void* userdata, void* obj)
    	{
    		return static_cast<BaseList2D*>(obj)->GetName();
    	}
    
    private:
    	// Helper function to shortcut getting header captions
    	static void RetrieveHeaderString(const Int32 id, String& str)
    	{
    		String headerName;
    		switch (id)
    		{
    		case COLUMN_OBJECT:
    			headerName = resource.LoadString(STR_COLUMN_OBJECT);
    			break;
    		case COLUMN_CATEGORY:
    			headerName = resource.LoadString(STR_COLUMN_CATEGORY);
    			break;
    		case COLUMN_MATERIALS:
    			headerName = resource.LoadString(STR_COLUMN_MATERIALS);
    			break;
    		case COLUMN_TEXTURES:
    			headerName = resource.LoadString(STR_COLUMN_TEXTURES);
    			break;
    		case COLUMN_INSTANCES:
    			headerName = resource.LoadString(STR_COLUMN_INSTANCES);
    			break;
    		case COLUMN_SCALE:
    			headerName = resource.LoadString(STR_COLUMN_SCALE);
    			break;
    		default:
    			headerName = "NULL";
    			break;
    		}
    		str = headerName;
    	}
    
    	Worker* _worker = nullptr;
    };
    
    


  • Hello,

    you cannot add a CUSTOMGUI_TREEVIEW to an Object plugin using GetDDescription().

    A parameter description is exactly that: the description of a list of parameters. A parameter is described by its type, minimum or maximum values and maybe the GUI used by the Attribute Manager to display that parameter. But this GUI only exists when the parameter is displayed; if the object and its parameters are not displayed in the Attribute Manager, there is no GUI.

    You find more information on parameter description here:

    CUSTOMGUI_TREEVIEW is a special GUI element. It does not display a specific parameter value but is configured using a custom function. You can add a TreeViewCustomGui to a GeDialog and configure it in this context. See GeDialog Manual.

    There is no inherent connection between parameter descriptions and GeDialog gadgets (as mentioned, if an object is not displayed in the Attribute Manager, there is no GUI).

    If you want to use a specific GUI element in your object's parameter description, you have to create a custom data type and a specific custom GUI presenting that data type. In that custom GUI you can re-use other GUI elements like the tree view.

    You find examples for custom GUIs and custom data types in the cinema4dsdk:

    Also, please add tags to your post and mark it as a question. Thanks.

    best wishes,
    Sebastian



  • Sebastian,

    Added tags. Not really a question but a request. I thought that I had read about people adding a TreeView to the AM using GetDDescription() but maybe it wasn't as well fleshed out as they (or I) suspected.

    I will look into the custom data type and GUI for it.

    Please mark as Solved.

    Thanks!
    Robert