How to dynamically add description elements?

On 21/02/2014 at 20:14, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   13,14,15 
Platform:   Windows  ;   
Language(s) :     C++  ;

Hi, I have searched the web now for almost two hours, and cannot find a single example on how to do this:

I want to dynamically (when the users presses a button) to add several Real Sliders to a specific group in my tag plugin. I really don't want to use a RES file. Well, I hope I do not need to..
The amount of sliders needed can be up to a 100, maybe more.

So the criteria is:
Add to a specific description group
Apply the sliders below existing GUI elements.

What I could do, is to use a RES file with 100 or more sliders and hide them as needed, so that I always get the number of sliders I want. But I would prefer to do it dynamically! Are there drawbacks of doing it dynamically, I might want to use the RES file after all. But it is not the way I am used to do things in when programming, in general.


On 22/02/2014 at 06:46, xxxxxxxx wrote:

Override GetDDescription() in your TagData derived class.  Do an Advanced search here for more information on it.  Note that you never call GetDDescription() directly.  It is called frequently, especially on parameter changes.  Basically, when the user clicks the button, add to your overriden Message() a check for DESCRIPTION_MESSAGE_COMMAND.

Here is a basic example:

// NodeData.GetDDescription - Descriptions and Parameters  
Bool UnfurlTag::GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags)  
  if (!(node && description))  
      return ErrPrt("UnfurlTag.GetDDescription.node/description");  
  // Load Static Resources from .res  
  if (!description->LoadDescription(node->GetType()))  
      return ErrPrt("UnfurlTag.GetDDescription.LoadDescription");  
  // *************************  
  // Add Dynamic Descriptions  
  // *************************  
  // Get the default container for the data type  
  // and then set relevant description options associated with it  
  BaseContainer    cp_id =                    GetCustomDataTypeDefault(DTYPE_STATICTEXT);  
  cp_id.SetBool(DESC_ANIMATE,                DESC_ANIMATE_OFF);  
  cp_id.SetBool(DESC_SCALEH,                FALSE);  
  cp_id.SetBool(DESC_REMOVEABLE,            FALSE);  
  BaseContainer    cp_value =                GetCustomDataTypeDefault(DTYPE_REAL);  
  cp_value.SetBool(DESC_ANIMATE,            DESC_ANIMATE_OFF);  
  cp_value.SetBool(DESC_HIDE,                FALSE);  
  cp_value.SetBool(DESC_SCALEH,            FALSE);  
  cp_value.SetBool(DESC_REMOVEABLE,        FALSE);  
  cp_value.SetReal(DESC_MIN,                0.0);  
  cp_value.SetReal(DESC_MAX,                1.0);  
  cp_value.SetReal(DESC_STEP,                0.01);  
  cp_value.SetLong(DESC_UNIT,                DESC_UNIT_PERCENT);  
  cp_value.SetLong(DESC_CUSTOMGUI,        CUSTOMGUI_REAL);  
  cp_value.SetString(DESC_SHORT_NAME,        "Unfurled at");  
  BaseContainer    cp_show =                GetCustomDataTypeDefault(DTYPE_BOOL);  
  cp_show.SetBool(DESC_ANIMATE,            DESC_ANIMATE_OFF);  
  cp_show.SetBool(DESC_HIDE,                FALSE);  
  cp_show.SetBool(DESC_SCALEH,            FALSE);  
  cp_show.SetBool(DESC_REMOVEABLE,        FALSE);  
  cp_show.SetString(DESC_SHORT_NAME,        "Show");  
  BaseContainer    cp_remove =                GetCustomDataTypeDefault(DTYPE_BUTTON);  
  cp_remove.SetString(DESC_SHORT_NAME,    "Remove");  
  cp_remove.SetLong(DESC_CUSTOMGUI,        CUSTOMGUI_BUTTON);  
  cp_remove.SetLong(DESC_ANIMATE,            DESC_ANIMATE_OFF);  
  cp_remove.SetBool(DESC_REMOVEABLE,        FALSE);  
  cp_remove.SetBool(DESC_SCALEH,            FALSE);  
  BaseContainer    cp_index =                GetCustomDataTypeDefault(DTYPE_LONG);  
  cp_index.SetBool(DESC_ANIMATE,            DESC_ANIMATE_OFF);  
  cp_index.SetBool(DESC_HIDE,                TRUE);  
  cp_index.SetBool(DESC_SCALEH,            FALSE);  
  cp_index.SetBool(DESC_REMOVEABLE,        FALSE);  
  cp_index.SetString(DESC_SHORT_NAME,        "Gradient Index");  
  LONG            c =                        0L;  
  // Like your count that should incremented when the user clicks the button  
  // and stored like: opBC->SetLong(TUNFURL_POLYGON_COUNT, opBC->GetLong(TUNFURL_POLYGON_COUNT)++);  
  LONG            gcnt =                    opBC->GetLong(TUNFURL_POLYGON_COUNT);  
  // Count controls to see if we hide "Gradient Dir" or not  
  for (LONG i = 0L; i != gcnt; ++i)  
      if (gradients[i].control)            ++c;  
  if (c > 1L)  
      BaseContainer    cp_mix =                GetCustomDataTypeDefault(DTYPE_STATICTEXT);  
      cp_mix.SetBool(DESC_ANIMATE,            DESC_ANIMATE_OFF);  
      cp_mix.SetBool(DESC_HIDE,                FALSE);  
      cp_mix.SetBool(DESC_SCALEH,                FALSE);  
      cp_mix.SetBool(DESC_REMOVEABLE,            FALSE);  
      cp_mix.SetString(DESC_SHORT_NAME,        ".");  
      NeighborPoly*    g =                        NULL;  
      c =                                        0L;  
      for (LONG i = 0L; i != gcnt; ++i)  
          g =                                    &gradients[i];  
          if (!g->control)                    continue;  
          if (c >= 2048L)                        break;  
          cp_id.SetString(DESC_SHORT_NAME,    LongToString(c+1L)+"  ("+LongToString(i)+")");  
          // description->SetParameter(...) adds the dynamic description and assigns a DescID UNIQUE within your resources  
          // - I typically set a start for each set by enumeration (as you see) and increment the value  
          // - if these descriptions can also be deleted/rearranged and there is special correlation to something  
          // - else, you may also need to maintain that correlation between DescID and whatever expects that particular one.  
          if (!description->SetParameter(DescLevel(TUNFURL_CONTROL_ID+c,        DTYPE_STATICTEXT,0L),    cp_id,        DescLevel(TUNFURL_GROUP_CONTROLS)))  
          // Control Polygon Gradation Direction - Static Text (see below)  
          if (!description->SetParameter(DescLevel(TUNFURL_CONTROL_MIX+c,        DTYPE_STATICTEXT,0L),    cp_mix,        DescLevel(TUNFURL_GROUP_CONTROLS)))  
          // Control Polygon Value - Real  
          if (!description->SetParameter(DescLevel(TUNFURL_CONTROL_VALUE+c,    DTYPE_REAL,0L),            cp_value,    DescLevel(TUNFURL_GROUP_CONTROLS)))  
          // Control Polygon Highlight - Bool  
          if (!description->SetParameter(DescLevel(TUNFURL_CONTROL_SHOW+c,    DTYPE_BOOL,0L),            cp_show,    DescLevel(TUNFURL_GROUP_CONTROLS)))  
          // Control Polygon Remove - Button  
          if (!description->SetParameter(DescLevel(TUNFURL_CONTROL_REMOVE+c,    DTYPE_BUTTON,0L),        cp_remove,    DescLevel(TUNFURL_GROUP_CONTROLS)))  
          // Control Polygon Index - LONG  
          if (!description->SetParameter(DescLevel(TUNFURL_CONTROL_INDEX+c,    DTYPE_LONG,0L),            cp_index,    DescLevel(TUNFURL_GROUP_CONTROLS)))  
  // Always call this at the end!  
  return SUPER::GetDDescription(node,description,flags);  
// NodeData.Message  
Bool UnfurlTag::Message(GeListNode* node, LONG type, void* data)  
  if (!node)    return FALSE;  
  if (type == MSG_DESCRIPTION_COMMAND)            return MsgCommand(static_cast<DescriptionCommand*>(data), static_cast<BaseTag*>(node));  
  return SUPER::Message(node,type,data);  
// UnfurlTag.MsgCommand  
Bool UnfurlTag::MsgCommand(DescriptionCommand* dc, BaseTag* tag)  
  // IMPORTANT: Buttons need to change the BaseContainer to have GetDDescription called!  
  if (!dc)                return TRUE;  
  BaseContainer*    tbc =    tag->GetDataInstance();  
  if (!tbc)                return FALSE;  
  LONG            id =    dc->id[0].id;  
  // Individual Control Actions  
  // - Remove Control Polygon  
      ControlPoly_Remove(tag, id-TUNFURL_CONTROL_REMOVE);  
  return TRUE;  
// UnfurlTag.ControlPoly_Remove  
Bool UnfurlTag::ControlPoly_Remove(BaseTag* tag, const LONG& id)  
  BaseContainer*    tbc =    tag->GetDataInstance();  
  if (!tbc)                return ErrPrt("UnfurlTag.ControlPoly_Remove.tbc");  
  NeighborPoly*    g =        &gradients[tbc->GetLong(TUNFURL_CONTROL_INDEX+id)];  
  g->control =            FALSE;  
  g->show =                FALSE;  
  g->value =                0.0;  
  g->ctrlValue =            0.0;  
  // Mark as "Not-Applied"  
  tbc->SetBool(TUNFURL_APPLIED,    FALSE);  
  // These assure that GetDDescription() gets called and the AM/View are updated  
  tbc->SetLong(TUNFURL_DIRTY,        ++dirty);  
  return TRUE;  

On 22/02/2014 at 06:53, xxxxxxxx wrote:

I will look at it a little later today, just a quick question (haven't looked much at the code yet), can I add the GUI elements to a certain Group?

On 22/02/2014 at 07:03, xxxxxxxx wrote:

The DescLevel() at the end of SetParameter() is the group to which to add the description.  The group must either exist in the static resource file or be dynamically created as well.

On 22/02/2014 at 15:08, xxxxxxxx wrote:

Hi Robert,
I have this comment: 👍

It works like a dream! Great post of yours, it exactly what I needed, thank you so much!