Realsoft 3D separates the functionality from the user interface using model-view concept. Model objects implement functionality whereas view classes implement the user interface.
View objects are connected to the model objects typically using R3WGA_Model attribute. The following code demonstrates a simple application which consists of two objects: a functionality object and a user interface object:
R3OBJ *model, *gui; // create our functionality object model = R3New(R3CLID_MYMODEL, R3TAG_END); // create our user interface and attach it to the model gui = R3New(R3CLID_MYGUI, R3WGA_Model, model, R3TAG_END);
When a model is changed, the user interface will be updated to reflect the new model status. Correspondingly, when the user plays with the user interface, the model attributes are updated accordingly.
Any number of views can be connected to a single model. For example, one can create a curve model and attach two views to it: one large curve editor and one miniatyre window:
R3OBJ *model, *gui; // create our functionality object model = R3New(R3CLID_CURVE R3TAG_END); // create our user interface and attach it to the model win1 = R3New(R3CLID_CURVEGADGET, R3WGA_Model, model, R3TAG_END); win2 = R3New(R3CLID_CURVEMINIATYREGADGET, R3WGA_Model, model, R3TAG_END);
Some view classes may use more than just one model object. For this they implement class specific model attributes.
When you need to close the program, then the view objects must be detached from their models. To do this set the R3WGA_Model attribute to NULL:
R3SetAttrs(win2, R3WGA_Model, NULL, R3TAG_END);
Model classes are derived from inc/oops/r3model.h. These classes implement the actual functionality in the program.
What is specific to model classes is that they call R3MM_CHANGED method when any of their attributes are changed.
The following code demonstrates how class implementors should do this in R3RM_SET method:
case MYA_MyAttr: self->myattr = (R3INT)tag->value; R3Do(obj, R3MM_CHANGED, MYA_MyAttr, NULL, R3TAG_END); break;
This is the only thing model classes need to do. The base class takes care of everything else.
View classes are derived from inc/oops/r3widget.h and they define the user interface through which the user can interact with the functionality layer.
View objects must take care of the following tasks:
Attach/detach to/from the given model object
Update the user interface to reflect the model, whenever the status of the model object is changed.
Change model attributes when the user plays with the user interface.
The following code demonstrates how view classes should attach and detach the model passed to them:
// R3RM_SET case R3WGA_Model: if(self->model != tag-value) { if(self->model) // detach from the current model R3DoA(self->model, R3MM_REMOVEDEPENDENT, obj); self->model = tag->value; if(self->model) // attach to the new model R3DoA(self->model, R3MM_ADDDEPENDENT, obj); } break;
When a model attribute is changed the model object sends R3RM_UPDATE method to all view objects connected to the model via R3MM_ADDDEPENDENT method. The parameter for the R3RM_UPDATE method tells the view which model attribute changed. For example, when the R3RSPHA_Center attribute of the sphere class is changed, the sphere property gadget will update its center control as follows:
static void r3rm_update(R3CLASS *cl, R3OBJ *obj, int p3) { R3IDATA *self = R3CL_IADDR(cl, obj); if(!p3 || p3 == R3SPHA_Center) { R3VECTOR center; R3GetAttrs(self->model, R3SPHA_Center, ¢er, R3TAG_END); R3SetAttrs(self->centergad, R3VCGA_Vector, ¢er, R3TAG_END); }
If the p3 parameter for the R3RM_UPDATE is 0, then the view object should do full update. This occur, for example, when the user interface is attached to a new model and everything needs be be updated.
View objects must also handle the situation where their model object get deleted.
When a view object connects to a model object by calling R3MM_ADDDEPENDENT, the model object adds the reference count of the view object by calling the view's R3RM_REF method. Correspondingly the model calls R3RM_UNREF when it is detached from a view. The 'p3' parameter of the R3RM_UNREF method can be studied to find out the model that has died:
case R3RM_UNREF: { R3IDATA self = R3CL_IADDR(cl, obj); if(p3 == self->model) self->model = NULL; R3DoSuperA(cl, obj, mth, p3); break; }
Note that view does not need to call R3MM_REMOVEDEPENDENT in this case. When a model object gets deleted, it detaches itself from all views in its R3RM_DELETE method.