Building dynamic browsers with Glamour

Glamour is the Moose engine that helps you build browsers easily.

Let's take a look at an example that builds a simple browser that shows all SystemWindow instances and for each of them offers a preview and the tree of submorphs.

GLMCompositePresentation new tabulator
     with: [ :t |
          t column: #morphs; column: #details span: 3.
          t transmit to: #morphs; andShow: [ :a |
               a list
                    format: [:m | m label];
                    icon: [:m | m imageForm scaledToSize: 64@64 ] ].
          t transmit from: #morphs; to: #details; andShow: [ :a |
               a morph
                    title: 'Preview';
                    display: [:morph | morph imageForm asMorph ].
               a tree
                    title: 'Submorphs';
                    rootsExpanded;
                    display: [:morph | {morph} ];
                    children: [:m | m submorphs] ] ];
     openOn: SystemWindow allInstances

The picture below shows the result of executing the script in my image. In the great tradition of Smalltalk, the picture shows the screenshot of the workspace window holding the code used produced the browser (regardless of how used I am with it, I am always pleasantly surprised by how easy it is in Pharo to build behavior that has reflective abilities).

Morphs-browser.png

But, let's get back to our topic. Glamour was first conceived for browsers whose structures are known upfront, like the one in our example. To this end, we chose to define the presentations at browser creation time and make dynamic the specification of the contents inside the presentation. In our example, the second transmission that populates the #details pane, constructs a morph and a tree presentation. This is defined at construction time, and cannot be changed easily dynamically. What remains dynamic is the way the actual morph is computed (display: [:morph | morph imageForm asMorph ]) and how the tree is being constructed based on the currently selected SystemWindow (display: [:morph | {morph} ]).

This works reasonably well for these types of browsers. But, even in this case, it is not ideal that we declare the :morph in two blocks. However, there exist another class of browsers that need to decide the presentation at execution time, and not at creation time. To support this case, we later added the GLMDynamicPresentation.

For example, the MooseFinder needs to decide the presentations of each object based on the type of each object. To solve this, the core implementation looks like:

MooseFinder>>compose
   self finder show: [ :a |
      a dynamic
         display: [:each |
            | dynamic |
            dynamic := GLMCompositePresentation new.
            each mooseInterestingEntity mooseFinderPresentationsIn: dynamic.
            dynamic ] ].

Essentially, the dynamic presentation takes each object and it builds a new presentation by dynamically asking the object for it.

This works well technically. However, one goal of Glamour is to offer a elegant fluent API, and the above solution is anything but elegant.

As a consequence, Andrei and I realized that we need to take a step back and rethink the original assumptions. In the original implementation, the presentations were built at browser construction time by executing the contents of the show block at the beginning. At this point in time, we cannot know what objects will pass through the browser, so the only input to the show block is a GLMCompositePresentation. However, this was an unneeded restriction. By simply creating the presentations every time a transmission got fired by some value moving between panes, we could achieve a full dynamic behavior without needing a special presentation.

What is the consequence? First, the show block now receives all objects from the ports that are involved in the transmission. Second, because of that, we no longer need a special dynamic presentation. For example, the MooseFinder now looks like:

MooseFinder>>compose
   self finder show: [ :a :each |
      each mooseInterestingEntity mooseFinderPresentationsIn: a ].
each== is passed in the show block, and based on it we can construct presentations dynamically.

All in all, the new Glamour shipped with Moose 4.8 makes it easier than ever to build dynamic browsers.

Posted by Tudor Girba at 26 August 2013, 7:05 am with tags tooling, moose link
|