by .

Have you ever wondered how to create and use those pretty Description elements that are a combination of a LONG cycle and a command button?

The Character object offers them for component selection. They let you choose an option from a list, perform an action by clicking on it, and they can display icons and look cool. The SDK documentation does not really say anything about that element, but this article will show you how to make use of it.

What is it?

The Description element is called CUSTOMGUI_CYCLEBUTTON. It is even mentioned in the SDK, but there is no explanation on how to use it.

This is how it looks in the Character object:

The CYCLEBUTTON in the Character object, used for the 4 elements in the component selection

The CYCLEBUTTON in the Character object, used for the 4 elements in the component selection

This article will demonstrate how to create and use it. In the end, we will have a CYCLEBUTTON like the following, and we will be able to evaluate the value and react to clicks.

The finished CYCLEBUTTON example

The finished CYCLEBUTTON example


This must be done in the code, from GetDDescription(). Here is an easy to use convenience function that does the job:

Bool AddCycleButton(Description *dc, LONG id, const DescID &groupid, const String &name, const BaseContainer &itemnames, const BaseContainer &itemicons, Bool animatable = TRUE)
	const DescID* singleid = dc->GetSingleDescID();

	if (!singleid || ((DescID)id).IsPartOf(*singleid, NULL))
		BaseContainer bc = GetCustomDataTypeDefault(DTYPE_LONG);

		//  Set CycleButton properties
		bc.SetLong(DESC_DEFAULT, 0);
		bc.SetString(DESC_NAME, name);
		bc.SetContainer(DESC_CYCLE, itemnames);
		bc.SetContainer(DESC_CYCLEICONS, itemicons);

		//  Create CycleButton
		return dc->SetParameter(DescLevel(id, DTYPE_LONG, 0), bc, groupid);

	return TRUE;

Function parameters

Description *dc

Here we simply pass the pointer to the description that has been passed to GetDDescription().

const DescID &groupid

This is the ID of a group in the description. The CYCLEBUTTON will be appended to this group.

const String &name

Since we have to create the CYCLEBUTTON in our code, the label string is not automatically taken from the description string resource. Therefore, we have to pass a string ourselves.

const BaseContainer &itemnames

The container with items’ names.

const BaseContainer &itemicons

The container with items’ icon IDs.

Bool animatable = TRUE

Set this to FALSE to hide the animation button in the Attribute Manager (this is recommended if you are using the CYCLEBUTTON simply as a command button with icon, and not as a cycle with multiple options).

To actually create a CYCLEBUTTON, we need to fill two BaseContainers with the names and icon IDs of the cycle items, and then call the AddCycleButton():

Bool MyObject::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
	// Load description (user interface)
	if (!description->LoadDescription(node->GetType()))
		return FALSE;

	BaseContainer names;
	BaseContainer icons;

	names.SetString(0, "Item 1");
	icons.SetLong(0, Ocube);		// You can use the plugin ID of any registered object...

	names.SetString(1, "Item 2");
	icons.SetLong(1, Opyramid);

	names.SetString(2, "Item 3");
	icons.SetLong(2, Ofigure);

	if (!AddCycleButton(description, MYOBJECT_CYCLEBUTTON, DescID(ID_OBJECTPROPERTIES), "Cycle Button", names, icons))
		return FALSE;

	return SUPER::GetDDescription(node, description, flags);

As you can see the workflow is pretty straight forward. Fill the containers with strings and IDs, call AddCycleButton() and that’s already it.

Ocube, Opyramid, Ofigure

For the icon IDs, you can use the IDs of any registered plugin (objects, tags, et cetera), or IDs of icons registered with RegisterIcon().


The description ID for our CYCLEBUTTON. This has been defined in the object’s description header file.


This is a standard group in the description resource of objects. For simple demonstration purposes, we’ll use this, but you can of course use the ID of any GROUP in your description resource.


Using a CYCLEBUTTON is very simple, too. Just like with normal LONG CYCLE elements, the selected option can be retrieved by getting the LONG value from the BaseContainer. And just like with normal BUTTON elements, a click can be caught in the Message() function.

In this example, we will catch clicks on the button and print out a message depending on the selected cycle option:

Bool MyObject::Message(GeListNode* node, LONG type, void *t_data)
	// Catch button click
		DescriptionCommand *dc = (DescriptionCommand*)t_data;
		if (!dc) return FALSE;

		if (dc->id[0].id == MYOBJECT_CYCLEBUTTON)
			// Get selected cycle item
			BaseContainer *bc = ((BaseObject*)node)->GetDataInstance();
			if (!bc) return FALSE;

			String message = "You selected ";

			switch (item)
				case 0:
					message += "a cube.";
				case 1:
					message += "a pyramid.";

				case 2:
					message += "one of this neat wooden figures.";

					message += "something.";

			GeOutString(message, GEMB_ICONASTERISK);

	return SUPER::Message(node, type, t_data);

That’s it. You will find a CYCLEBUTTON in your object’s attributes now. If you select an option or click on the button, an appropriate message will pop up.

Simpler CYCLE with icons

If you don’t need the command button functionality, but simply want a CYCLE with icons, you might want to read this article.