Digital Art Zone

 
     
Adding a String List to the UI
Posted: 03 July 2014 07:41 AM   [ Ignore ]
Member
Avatar
Rank
Total Posts:  29
Joined  2004-03-30

I’m having trouble trying to add a TMFNameListPart into the User Interface to simply add a list of strings.  I can get the list to appear and to add some strings into it, using a few attempted methods, but then this causes the plugin to become unstable and indirectly cause Nil Pointer errors.  I haven’t seen some full example code for this but there was a rough outline on the old SDK group.

I started in MCSketch by adding a TMFScrollPart (with attached sliders) then its child leaf part is a TMFNameListPart with a part ID ‘myID’.  Nothing was added to the resource or PMap for the plugin.  If that is all I do I get an empty space for the list and the plugin works as normal.

Then in the plugin’s ::HandleEvent(...) I add the following :

...
MCOMErr MyPlugin::HandleEvent(int32 message, IMFResponder* source, void *data)
  {
  ...
  switch(message)
  {
  case kMsg_CUIP_ComponentAttached:
    {
    TMCCountedPtr<IMFPart> sourcePart;
    source->QueryInterface(IID_IMFPart,(void**)&sourcePart;);
    TMCCountedPtr<IMFPart> childPart;
    sourcePart>FindChildPartByID(&childPart;,‘myID’);
    TMCCountedPtr<IMFListPart> listPart;
    childPart->QueryInterface(IID_IMFListPart,(void**)&listPart;);

    // method 1 : create an TMCClassArray<TMCDynamicString> Names; elsewhere and add some strings
    // listPart->CreateNamedCells(Names);

    // method 2: use IMFPart to SetValue
    listPart->AddCells(0,2,true);
    TMCString nameString=“item 1”;
    listPart->SelectCell(0,true,false);
    childPart->SetValue(&nameString;,kStringValueType,true,true);
    nameString=“item 2”;
    listPart->SelectCell(1,true,false);
    childPart->SetValue(&nameString;,kStringValueType,true,true);
    }
  break;
...

When this code is used the string list is filled with the names, they can be selected, but Nil Pointer errors start up elsewhere in the plugin’s operations.

Should I be using TMFStringDataPacket here, and if so, how ?  What is the correct way to add (and manage) a selectable list of strings into the UI ?

 Signature 

www.Sparrowhawke3D.com

Profile
 
 
Posted: 06 July 2014 04:22 AM   [ Ignore ]   [ # 1 ]
Member
Avatar
Rank
Total Posts:  32
Joined  2005-09-14

I use the same set up in Toon! Pro Override without any problems, but I use CreateNamedCells instead of doing the AddCells and SetValue. I don’t have any recollection about gotchas for this, but it’s been close to a decade since writing this code so I may have landed there because of other issues.

I did notice that while doing this and processing other messages (e.g. kMsg_PartValueChanged) I set a flag before calling this function titled ignoreSelectionChange which drops me out of another chunk of HandleEvent code if set. Maybe your exception is downstream because you’re not fully prepared when you get a selection change.

MCCOMErr  ToonEnabled::HandleEvent(MessageID messageIMFRespondersourcevoiddata)

{
 IDType sourceID
;
 
TMCCountedPtr<IMFPartsourcePart;


 
source->QueryInterface(IID_IMFPart, (void**) &sourcePart;);
 
ThrowIfNil(sourcePart);
 
sourceID sourcePart->GetIMFPartID();

 if (
message == kMsg_CUIP_ComponentAttached && tree != NULL{
  TMCCountedPtr
<IMFPartlistPart;
  
TMCCountedPtr<IMFListPart> list;
  
sourcePart->FindChildPartByID(&listPart;, UI_ELEM_SHADER_LIST);

  
ThrowIfNil(listPart);
  
listPart->QueryInterface(IID_IMFListPart, (void**)&list;);

  
mergeLevelsWithScene();
  
fillShadingDomainList(list, 0);
  if (
toonSettings.GetElemCount() == 0)
  
{
   enableDomainControls
(sourcePartfalse);

   
listPart->Enable(false);
  
}

 }

...


void ToonEnabled::fillShadingDomainList(TMCCountedPtr<IMFListPart> list, uint32 selection)
{
 TMCClassArray
<TMCDynamicStringinNames;
 
uint32 domainCount toonSettings.GetElemCount();
 for (
uint32 levelsIndex 0levelsIndex domainCountlevelsIndex++)
 
{
  TMCString255 currentState
;
  
ShadingDomainLineLevelslevel toonSettings.getDomainByRank(levelsIndex);
  if (
level.overrideSettings == true)
  
{
    currentState 
" - overridden";
  
}
  
else
  
{
    currentState 
"";
  
}
  inNames
.AddElem(level.name currentState);
 
}

 
list->CreateNamedCells(inNames);
 if (
inNames.GetElemCount() > 0)
 
{
  
list->SelectCell(selectiontruefalse);
 
}
 
list = NULL;

Regards,

 Signature 

Eric Winemiller
Digital Carvers Guild
Plug-ins for Carrara and LightWave

Profile
 
 
Posted: 07 July 2014 05:44 AM   [ Ignore ]   [ # 2 ]
Member
Avatar
Rank
Total Posts:  29
Joined  2004-03-30

Thanks Eric for sharing your wisdom.

If listPart->CreateNameCells() works and the code I’ve used is almost the same as yours then I’m on the right road.  Knowing that when I ran a debug with more close attention I could see that kMsg_CUIP_ComponentAttached is called many many times and that could narrow it down to making sure the message is directly associated with the list part to solve this.

There are so many simple things with the UI that are nearly impossible to figure out without the help of ‘zot’ The Great and Powerful…

 Signature 

www.Sparrowhawke3D.com

Profile
 
 
Posted: 09 July 2014 12:19 AM   [ Ignore ]   [ # 3 ]
Member
Avatar
Rank
Total Posts:  29
Joined  2004-03-30

I’m still having difficulty to add such a simple element to the UI - but I’ve got much closer to something functional by half-hacking and debugging.

In the code I listed above I left out the obvious ThrowIfNil() checks.  One of the errors was showing up as a Nil Pointer because another component (a dialog) was being attached.  So if the list part isn’t the only one in the UI a safety check is needed.

...
  case 
kMsg_CUIP_ComponentAttached:
    
{
    TMCCountedPtr
<IMFPartsourcePart;
    
source->QueryInterface(IID_IMFPart,(void**)&sourcePart;);
    
ThrowIfNil(sourcePart);
    
TMCCountedPtr<IMFPartchildPart;
    if( (
sourcePart>FindChildPartByID(&childPart;,‘myID’)==MC_S_OK) && (childPart) )
      
{
      TMCCountedPtr
<IMFListPartlistPart;
      if( (
childPart->QueryInterface(IID_IMFListPart,(void**)&listPart;)==MC_S_OK) && (listPart) )
        
{
        
if(listPart->GetNumOfCells()==0)
          
{
          listPart
->CreateNamedCells(Names);
          
listPart->SelectCell(0,true,false);
          
}
        }
      }    
    }
  
break; 

The best solution I can come up with for now is to check if the list is empty first (or the right size) before creating new cells.  There is no apparent way to get the contents of the cells later and TMFNameListPart is not included in the SDK.  I’ll have to hope this will work to build the interface I want where the list can be used to add and remove (and edit) separate influence zones inside a single deformer.

Whenever a list item is selected it generates another kMsg_CUIP_ComponentAttached event as well, causing some of the trouble.

Without that check to see if the list has already been filled it put the code into a terminal loop when I called listPart->SelectCell(0,true,false);

The debug reaches into listPart->CreateNamedCells(Names); twice when the modifier is added to the object.  I first tried adding in a boolean flag to create the list only once but this resulted in an empty list. I presume this is due to some sort of cloning for undo and redo.

 Signature 

www.Sparrowhawke3D.com

Profile
 
 
Posted: 21 July 2014 06:13 AM   [ Ignore ]   [ # 4 ]
Member
Avatar
Rank
Total Posts:  29
Joined  2004-03-30

I’ve got another issue with the String List that I can’t figure out.  Managing the list to be able to add and remove items and respond to a change of selection is achievable with UI buttons and events and is working.

I can get a button press event and then knowing the hierarchy inside the UI use the responder to query for its source part, get the parent and then find my list using code like this:

MCCOMErr MyPlugin::HandleEvent(int32 messageIMFRespondersourcevoid *data)
...
  
TMCCountedPtr<IMFPartsourcePart;
  
source->QueryInterface(IID_IMFPart,(void**)&sourcePart;);
  
ThrowIfNil(sourcePart);
...
  
TMCCountedPtr<IMFPartsourceParent;
  
sourceParent=sourcePart>GetPartParent();
  
TMCCountedPtr<IMFPartchildPart;
  
TMCCountedPtr<IMFListPartlistPart;
  if( (
sourceParent->FindChildPartByID(&childPart;,'myID' /*the id of my list part*/ )==MC_S_OK) && (childPart) )
    if( (
childPart->QueryInterface(IID_IMFListPart,(void**)&listPart;)==MC_S_OK) && (listPart) )
      
{
      
// change the list in response to the event
      
}
... 

What I can’t figure out is how to make changes to the list outside of ::HandleEvent.  I want to be able to build a list then during MyModifier::DeformFacetMesh(...) detect if the scene or selection etc has changed and then update a list. 

It comes down to a general question of how do I get hold of the IMFPart of the UI inside the plugin code outside of ::HandleEvent() ?

 Signature 

www.Sparrowhawke3D.com

Profile
 
 
Posted: 21 July 2014 02:21 PM   [ Ignore ]   [ # 5 ]
Member
Avatar
Rank
Total Posts:  32
Joined  2005-09-14

I have in the past, passed around a pointer to a UI element using the Clone method, but it was very unsafe and only worked for very specific workflows. I don’t do it anymore.

Is there no activate event or something when you return to the UI where you can resynch with your object?

My other thought was put an explicit Refresh button there. I’ve done that in the past where I could not automagically detect when stuff changed.

Good luck,

 Signature 

Eric Winemiller
Digital Carvers Guild
Plug-ins for Carrara and LightWave

Profile
 
 
Posted: 22 July 2014 04:31 AM   [ Ignore ]   [ # 6 ]
Member
Avatar
Rank
Total Posts:  29
Joined  2004-03-30

Thanks again for some experienced insight and advice Eric,

Saving a copy of the IMFPart when the list is created should have occurred to me - but clearly that isn’t safe.

I couldn’t see any clear path through the TBasicDataExchanger or IExDataExchanger (that handle the UI) to get to an IMFPart.  There is TBasicDataExchanger::GetMyPrefsComponent() which returns IShParameterComponent - from which it might be possible to call ::GetView(IMFPart **outPart).  I was hoping to avoid another time wasting fishing trip in debug to see what comes up there.

I’m still in the design stage at the moment.  I want to add a ‘parameter link’ into a deformer where the user can select another instance in the scene and then I would create a list of compatible modifiers that are found on that tree and select one, to copy and keep up to date with the parameters.  All of this should work in ::HandleEvent() and the plugin would easily be able to detect a broken link.  To improve user friendliness I wanted to update the list and attempt to repair the link or raise an alert to fix it.  Dumping the potential problem on the user might be the only way to proceed.

 Signature 

www.Sparrowhawke3D.com

Profile