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:
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.
Creation
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.SetBool(DESC_ANIMATE, animatable ? DESC_ANIMATE_ON : DESC_ANIMATE_OFF); bc.SetLong(DESC_CUSTOMGUI, CUSTOMGUI_CYCLEBUTTON); bc.SetLong(DESC_DEFAULT, 0); bc.SetLong(DESC_SCALEH, FALSE); 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; flags |= DESCFLAGS_DESC_LOADED; 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().
MYOBJECT_CYCLEBUTTON
The description ID for our CYCLEBUTTON. This has been defined in the object’s description header file.
ID_OBJECTPROPERTIES
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.
Usage
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 if (type == MSG_DESCRIPTION_COMMAND) { 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; LONG item = bc->GetLong(MYOBJECT_CYCLEBUTTON); String message = "You selected "; switch (item) { case 0: message += "a cube."; break; case 1: message += "a pyramid."; break; case 2: message += "one of this neat wooden figures."; break; default: message += "something."; break; } 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.