Field

Templating and Copying Visual Elements

Field encourages you to use visual elements for anything and everything — for example the TopologyPlugin (one of the tools on the mouse tools palette) even uses them for the edges between nodes; since one often constructs "visualizations" of data over time that you end up wanting to edit and clean, these visualizations should be made out of visual elements. Clearly, in order for this to happen, making, editing and deleting visual elements needs to be easy. This pages collects the range of ways of doing this.

First of all, let's just mention that you can copy elements with the mouse by holding and shift, and dragging the elements that you'd like to copy. However, there are some times when you'd like to make, edit and copy elements using code.

Obvious things — creating elements with code

The simplest way of creating many elements is with the code like:

otherElement, component, overrides = VisualElement.createWithName(Rect(40,40,100,100),_self.root,  VisualElement, DraggableComponent, DefaultOverride, "banana")

This creates a default looking visual element (the usual grey box) at (40,40) with dimensions (100,100) which dispatches to the root of the sheet (see DelegationAndVisualElements). The last three parameters are the classes that will supply the iVisualElement, iComponent and iVisualElementOverrides implementations. For iVisualElement you are pretty much limited to VisualElement, for iComponent you have the choice between DraggableComponent, PlainDraggableComponent and PlainComponent, and for iVisualElementOverrides I recommend starting with DefaultOverrides (or perhaps SplineComputingOverrides and 'upgrading' it later using Mixins. You are also free to have the newly created element dispatch to something other than _self.root_self is often useful — but remember, this newly created component will have all of its missing properties filled in by the parent it dispatches to, including the python_source_v !

But otherElement is now the visual element created, and it acts just like _self, you can set properties on it

otherElement.python_source_v = """
print "hello world !"
"""

otherElement() # prints "hello world !"

Finally, you can delete an element by calling VisualElement.delete(_self.root, otherElement).

Copying other elements

The code above looks a little long, but the reason that it hasn't been packaged up into something more common is that it isn't actually used that much. Far more common than creating an element from scratch are the other techniques outlined below.

The first is copying elements from the sheet, using the Templating utility class.

newElement = Templating.simpleCopy(someElement, _self.root)
newElement.setFrame(Rect(50,50,100,100))

This makes a new element, that dispatches to the root, that copies all of the properties that are locally stored in someElement. Again, you can then set properties on what is returned. Two things make this better than creating it from scratch. Firstly you get to use the editor &c to author the template element in the first place (which is certainly better than writing long python """ strings).

Secondly, the fact that newElement's properties are (initially) copies of someElements's in remembered for properties with the _v suffix. This means that you can perform three-way merges over them.

This isn't as rare or as esoteric as it sounds. Consider the following example: you have some temporal pattern that you want to visualize, and ultimately you are going to create a timeline that's going to be scrubbed. You produce your visualization that outputs a whole bunch of visual elements, copying from a template, that makes each element look like, say, a circle. After seeing this series on the canvas, you realize that some of the elements need to be nudged — your code is good, but not perfect, you clean it by hand. A few of the elements have their executable payload modified. Now later, you come back and want to change the initial template — they shouldn't be circles, and the code inside them needs to be updated. A three-way merge over each element allows you to change the original template and push the changes back to the elements that were copied from it, while retaining any changes made locally to the elements.

To initiate a merge:

Templating.merge(someElement, newElement, preferTemplate, becomeVisual)

The last two parameters specify if, in the case of collisions (that is, both the template and the copy have changes in the same place), or in the case of unversioned properties, whether the template's properties should be copied over; and whether, in the case of collisions, Field should open up Apple's FileMerge program and let you select what happens. If this seems like the kind of functionality that you need more of, see the MergeGroup hierarchy for preliminary extensions to this idea.

Copying from raw files

Finally we note that we can template things not just from the current sheet, but off of disk as well. Templating.copyPlainTextToProperty(file, target, property) copies a file from the "internal" directory of the workspace to a property (and in the case of versioned properties, notes this down). You have the option to push changes from the text file to the property and vice versa. This is a good way of including foreign files into the Field environment — for example, OpenGLSL shaders, shell scripts and the like — in a way that's version controlled.

Templating from other sheets

You can also copy material from other Field sheets without loading them. If you right-click on a visual element and select "mark as template" you are invited to give this visual element a new "template name". Nothing else happens to this element, it's just marked as having this additional name. Once the sheet is saved, you can then find these elements from the "Element File System Browser" (from the File menu) in the templates folder.

From the browser, you can insert this element into a new sheet. In fact you can insert any element from any sheet into a currently open sheet. "Templates" are just easier to find: template names automatically hierarchicalize using periods — just like, say, java packages. So some.template.this.is is a template called is in the folder this in the folder template in the folder some in the templates "folder". Inside code its a matter of:

inserted = Templating.elementFromKnownTemplate(name, _self.root)

You can also copy the entire sheet into the open sheet using the element file system browser, or from code:

insertedElements = Templating.elementsFromKnownSheet(name)

— and note, you can save just the selected subset of sheet into a new sheet using the Selection Manager:

One ought to be able to perform three-way merges over the histories of these thingsas well, but currently there's no UI for it (#13).