Solved 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