changed:
-
For the sake of simplicity I will use the term widget and object alternatively. For this document, they mean the same thing.
Gazpacho does the following tasks with widgets/objects:
- Create and add them to the project
- Remove them from the project
- Add them to parents
- Change their general and packing properties
- Clone them (to copy them into the clipboard)
- Serialize them
There is usually a standard function to do most of this operations but the problem comes with exceptions. This world would be a very boring world without exceptions....
Thus, we need to be able to specify alternative ways to do these operations. For that reason Gazpacho provides a method for each of this operations and plugin writters will want to override this virtual methods. This methods are now implemented through the WidgetClass and PropertyClass classes. We parse the catalog xml file and if a plugin is found to be implementing a certain function that information is put in the WidgetClass or PropertyClass depending on whether it is a function related to a Widget or to a Property.
Another possible implementation could use a Python module for every plugin. Inside this package we just have classes that subclass Gazpacho classes overriding some of the methods. Then the catalog file just says which ones are these classes.
The first approach has the problem of not being very readable due to functions scattered through a file and not being a very object oriented solution. The second approach is better, but the problem with it is that it will probably make harder writing any C extensions.
<a href="plugin-system.png"><img src="plugin-system-tn.png" width="401" height="185" alt="Diagram of possible plugin system"/></a>
In this diagram we can clearly see 3 different parts: Gazpacho Core, Plugin Interface and an example plugin. With this approach the xml catalog files are only used to register adapters within the application ObjectAdaptorFactory. The rest of the information about widget special functions and properties is stored inside the plugin itself.
When a Gazpacho Object is created by the application it gets a reference to right adapter for the related GObject/GtkWidget. Note that if you have n Gazpacho Objects of the same type, all of them use the same instance of the adapter.
Let's explain the proposed API for the adapters:
GazpachoObjectInterface
An object adaptor must implement all the methods in this interface (even if this implementation is dumb)
<pre>get_type_name(): str</pre>
Returns the object name. For instance: "GtkWindow" (Hint: g_object_type_name)
<pre>create(): object</pre>
You can provide a default implementation in your superclass plugin that just gets the type of the object and creates an instance based on that. We need to expose this function because some objects/widgets need additional arguments at creation time. This method doesn't have any arguments, so it should create the object using *default* arguments. It returns the object. Note that if the user needs to change this parameters later, (and probably these will be ON_CONSTRUCT_ONLY arguments), she will need to recreate the widget again. That's one of the reasons we have the "clone" method.
<pre>post_create(object, interactive=True)</pre>
This function is called after creating a widget. This is usually used to do some setup, like setting a default size.
<pre>clone(src): object</pre>
This copies an object into another, copying all its properties and its children recursively.
Note how most of this methods can have a default implementation in your adaptor root class. So, ideally, you should only need to implement a few special cases. Here is one of the situations where inheritance is actually good :-)
<!--
This function is responsible of filling the container with one or more Placeholder objects that represent possible children. Some containers may use Placeholder subclasses to do special editing. For example, a GtkFixed may use a special Placeholder so the user can place children in fixed locations. If the interactive parameter is True this function may request the user (using a dialog) some extra information. For example, when creating a Table we may want to know how many rows and columns the user wants. If interactive is False we
dont ask anything and use default values.
-->
<pre>add_child(container, child)</pre>
For GtkWidgets this is almost always called gtk_container_add but, as we support more general objects, we need this function. Example: adding actions to action groups.
<pre>get_n_children(container: object): int</pre>
This returns the number of children in a container. A container should look at its properties and return this value, not the real children list (we can call get_children ourselves and count them). For example, a GtkTable has n-rows and n-columns properties and it should use them to return this value. A box should use the fake size property.
<pre>replace_child(container, old_child, new_child) -> None</pre>
This function replaces the *old_child* object inside *container* with the *new_child* object. It should copy the packing properties, or any similar properties, from old_child to new_child.
We also need a function to get the children of an object::
get_children(container) -> [child1, child2, ..., child_n]
This is not needed for Gtk Widgets, since we can detect if the widget is a GtkContainer and call its get_children() method. Still, we
need this function to be used with general GObjects. For example, how to get the children of a GtkActionGroup or a GtkSizeGroup.
Properties
Some properties need special customization. This is the API for them
Custom set function::
set(object, property, value) -> None
Custom get function::
get(object, property) -> value
These functions are usually implemented for fake properties but they also make sense for some real properties.
For most of the properties Gazpacho can provide a good editing widget but sometimes something else is needed::
create_editor(property) -> widget
This function creates an editor for that property and returns the resulting value
This other function is also necessary::
load_editor(editor, widget, proxy) -> None
This function is called to load a widget into a special editor (created in create_function()). The widget is the corresponding GtkWidget that the editor can read current values from.
The proxy object is used to set values on the widget since it needs to be handled through Gazpacho's own framework (to implement undo/redo and saving).
The proxy object has two functions, set_value and set_property. In most cases set_value is what you want, in cases when your editor only operates on the property it's attached to. But if you need setting some other properties on the widget by the way, (such as in the manually added property called 'icon' in GtkImage, which also sets the 'file' and 'stock' properties when changed in the editor). It's then when using the function set_property comes handy, as it also takes a property id as first argument.
Serializing objects
For most of the objects we save their type and name, properties, children and packing properties, but we may want to change this for some other widgets::
get_xml(object, xml_document) -> xml_node
This function gets an object and a XML document and builds the XML nodes needed to save the widget. It returns the root of these nodes.
We also need a way of recreating a GazpachoWidget from its GTK+ equivalent::
create_from_gtk(object) -> GazpachoWidget
Usually the standard procedure here is to create an empty GazpachoWidget and fill its properties using the gtk object as source. Then do the same recursively for each of its children. But, sometimes, (Toolbars and Menubars) we don't want to do this (creating the children recursively for example).
Sometimes widgets have internal children. We need to know them when loading the widgets from the .glade file so we don't create them again::
get_internal_child(widget, name)
This function searchs in 'widget' for an internal widget called 'name' and returns it.
Problems
There are still a number of problems:
- Glade-2 uses a fake packing property for the label widget of GtkFrame. Can we do this better?