Since my last post, I realized how important SCF is in Crystal Space and how it could help me implement the property editor, tools, scene browser, and asset browser in a way that remains extensible.
In my last post, I talked about how I will implement the property editor. I'll now talk about how to do the rest of the major parts.
When something in the editor needs to know about what interfaces an object implements, it must query each possible SCF interface until it finds a match. When you have all of the CS interfaces, all of the CEL interfaces, and user-defined interfaces represented, there is a lot of querying going on.
Over the weekend, I spent some time to come up with a class EditorObject which abstracts each iBase* object in the editor. This class will keep a list of the interfaces which an object implements. Since I need to do a lot of SCF interface querying, and querying isn't particularly efficient, this cache should boost performance. More importantly, it provides an abstraction of an editor object--no matter what kind of interfaces the object implements, there should be a consistent way of getting/setting the name, parent, and getting the type (factory, instance, or other) and properties.
For each SCF interface, there will be an implementation of iInterfaceWrapperFactory. This will create instances of iInterfaceWrapper, only if the passed iBase* implements the particular interface which the iInterfaceWrapperFactory wraps (it determines this using QueryInterface). Each iInterfaceWrapper implementation will keep a pointer to the queried interface to carry out requests for the Name, Parent, Properties, etc.
Upon construction with an iBase* object, the EditorObject calls each registered iInterfaceWrapperFactory on the object, and stores the resulting iInterfaceWrappers in a list. It stores iInterfaceWrappers whose iInterfaceWrapperFactory::HasNameAttribute() and HasParentAttribute(), respectively, return true, separately, but in addition to the aforementioned list. It does this so it can provide constant time access to name and parent attributes. I can't think of a case at the moment in which one object has at least two interfaces which provide a Name attribute, but if this becomes a problem, certain interfaces could be given a higher priority than others. Even in this case, the other Name attribute can still be edited in the property editor.
How does the Scene Browser and Asset Browser know which objects to display? EditorObject asks each iInterfaceWrapperFactory for the type of the object implementing the interface. By type, I mean instance or factory. For those interfaces which do not help identify whether an object is an instance or a factory, they return unknown type. The EditorObject then stores the resulting type. Ideally, all interfaces return unknown, but one which returns either instance or factory. If there is a conflict where at least one interface says it is an instance type and at least one says it is a factory type, then the logical solution would be to set the resulting EditorObject type to unknown. In this case, both the Scene Browser and the Asset Browser could show it. This isn't as uncommon as it might sound, although I can't think of an example.
The Property Editor will also need to know about the properties from each interface. Here EditorObject will ask each interface for a list of properties. I still need to come up with a property representation which these GetProperties functions use. I don't think it would be a good idea to have the interface wrappers tied to wxPropertyGrid.
Instead of showing the interface name as the category in the property editor, there will be a name based on logical groupings of properties, like "Surface", for properties related to a surface. The idea is that there is a better grouping of properties for editing than the groupings dictated by the interfaces, which were designed to be good groupings for runtime. To implement this, each property will specifie under which category that property should be displayed. Ideally, these category names would be standardized. For the advanced user, the underlying interface name can be shown in the property description, and optionally, the properties could be grouped by interface.
Finally, Tools are interested in what interfaces an object has so that they can determine whether they are available. They can use EditorObject::HasInterface to query for interfaces using cached results.
On a more practical note...
My mentor kindly created a branch for me in https://crystalspace3d.org/svnroot/crystal/CSExtra/branches/soc/editor/
I've gotten both wxAUI and wxPropertyGrid samples to compile from within the CSExtra source tree, by including wxPropertyGrid sources in the tree and assuming wxAUI is built into wxWidgets. I should require wxWidgets 2.8+ since only that has wxAUI, but I haven't messed with the standard configure check yet. A more elegant way would be to make an additional check for wxAUI. In the case that it isn't present, I could have the PanelManager use some sort of static layout with splitters, but this isn't a priority.
In my next post, I'll talk a bit about registering objects with the editor and selections.
I hope to really iron out the design during this week so I can get a good start on the code. I'll try to come up with a schedule tomorrow. Signing out.
|<< <||Current||> >>|