Hi there,
I am creating a C++ plugin that lays out some text objects in a table structure according to some input data and parameters set by the user. In general, this works; I input data, I set parameters, I get a table. Here is a simple screenshot just for basic context.
There are a few things at play here. My object generator has laid out the numbers 1-9 according to the parameters set by the user (in this case, me). As you can see, there are also some splines around the characters. These splines come in handy as "evidence" later. There are two REAL parameters for horizontal and vertical "padding" - this is the space between the bounding box of the characters (1-9) and the splines surrounding them. If padding was 0, then the splines would touch the numbers.
There are also two other objects above the output of my plugin. A text spline, and a mograph text object. These are used as template objects - in this case, you can see that the 3D mograph text object is used as a template for each cell data. I actually clone this text object and change the text contents. In the UI for my generator plugin, I have a link field defined as such:
LINK MT_HEADERTEXTTEMPLATE {
ACCEPT { Osplinetext; Omograph_text; }
}
The idea is that the user can drag a text object (spline or mograph) that has the correct font, size, spacing, 3d depth etc., so that the numbers 1-9 look like what they want. If the user does not drag anything into the link field, I just create a basic text spline. The object is created with this simple code:
BaseObject* text_object = cell->font == nullptr
? BaseObject::Alloc(Osplinetext)
: static_cast<BaseObject*>(cell->font->GetClone(COPYFLAGS::NONE, nullptr));
cell->font in this case is a reference to the object in the MT_HEADERTEXTTEMPLATE field that I get in the SetDParameter() of my generator object.
In my GetVirtualObjects() implementation I of course try to be a good citizen and check if we can use the cache or if I need to recalculate my layout. In order to create the splines you see in the screenshot I need to measure the size of each text object (they won't typically contain just 1-9, there will be lots of different stuff in there). When either the data changes, or the text object linked, I run what I call the "measure" pass in my layout algorithm. This creates a new document, and creates a clone of all the text objects in this new document, in order to measure the bounding box of the text object. This information is then cached, so that we only run this measure process when we need to. For instance, the font size of the linked text object changing means we have to re-run measure. The padding between the text objects and the splines around it does not; So we save some expensive calculation when we can.
The start of my GetVirtualObjects() looks like this:
Bool linksDirty = false;
if(_layout_settings.font_template != nullptr)
{
int dirty = _layout_settings.font_template->GetDirty(DIRTYFLAGS::ALL);
if(dirty != _last_font_template_dirty_cs)
{
_last_font_template_dirty_cs = dirty;
linksDirty = true;
}
}
Bool isDirty = op->CheckCache(hh) || op->IsDirty(DIRTYFLAGS::DATA);
if (!isDirty && !linksDirty)
return op->GetCache();
_layout_settings.font_template is again the pointer to the linked text object. _last_font_template_dirty_cs is just a local variable for now that is used to check to see if the linked object has been changed. If it has, well, then no cache for us, and we go ahead and create the object structure normally.
And now finally we have arrived at my problem. My object (the numbers 1-9 in this case) are not always refreshed on screen, depending on how I change parameters.
-
If I change any of my own parameters (such as the input data, the linked text object, horizontal or vertical padding between the text and the splines) then my object is refreshed continually. If I drag a padding number, I get realtime updates.
-
If I select the linked text object in the C4D Object Manager, activate the Height field, type in a new number, and then press enter, on screen I see the following:
Just to be very specific - GetVirtualObjects() has run fully here - the splines are laid out correctly for the next font size. Using ApplicationOutput and the debugger I have verified that I am indeed creating the text objects along with the spline objects we see on screen. However, they are nowhere to be seen... that is, until I click somewhere in the C4D UI that causes it to update. Maybe I click my object in the Object Manager. Or anywhere really outside the Attribute Manager. This causes the view to refresh and I once more have the expected layout visible:
(I used different text Height values here to produce the screenshots, so the font size varies, that is not a layout issue - the splines ARE correct in every instance)
-
It is even more visible if I drag the text Height of the font object (either when the font object is selected in the Object Manager directly, or when I drill down into the linked text field property on my own object). While I drag Height left and right to increase and decrease the font size, I see the actual text object resize in realtime, AND I see my splines resize themselves to accomodate the new text size. However, I do not see any text objects, despite my GetVirtualObjects() returning them as part of my object structure. Until I let go of the mouse - in that case the text objects are instantly back and visible again.
-
If I put my text cursor in the Height parameter of the text box and use the arrow up/down keys to change the value, again I see the splines moving, but no text objects. If I tab to another field, still no text objects. If I press enter the text objects appear.
Now given that I don't know if my GetVirtualObjects() is being called while the user is scrubbing some value, or if it is "settled", I am not behaving any differently. The only thing changing with my GetVirtualObjects() implementation is the caching code I showed at the beginning - either it returns the cached object, or it does not. When the user is scrubbing a value, the cache is NOT returned - I have verified this with the debugger and ApplicationOutput() also.
If I select my object plugin object in the Object Manager and press C to make it an editable object, the whole structure is there exactly as I create it in my code. Text objects and splines inside some nulls to keep things organized. Of course I can't change it to an editable object in the middle of the user scrubbing parameter values, but I have verified through debugging output that I am returning the correct - same - object structure on every invocation. The text objects just do not appear while I am messing with parameters.
Am I perhaps missing some message that I should handle to tell the system to completely redraw my objects? I can't imagine, because why would the splines be redrawn in that case?
I will happily show my code, but I don't know which part of it would be useful to figure out why this happens (or doesn't happen). My GetVirtualObjects() gets kinda complex because of several layout operations happening, but if there is anything in there you need to see, let me know, and I will post it. I just can't imagine why Cinema 4D would draw the splines I return as part of my object structure, and not the text objects...
Thanks for any insights!