Logo

18.6.4 Composing browsers

Browsers encapsulate a navigation flow, and in many cases we would like to reuse this flow in a larger context. This reuse is possible because browsers are presentations. Thus, they can be used within another browser by simply adding it as a presentation to a pane.

Suppose that we would want to build a browser that given a starting method would allow us to navigate the chain of all invoking methods (similar to the Senders browser in Pharo). Given that we deal with a deep tree, we might want to use a Finder to navigate this structure. As possible result can be seen below.

Classic chaser - example of composing browsers

In our example, we are actually dealing with two browsers:

  1. an inner Finder that spawns the next pane with the invoking methods for each selected method, and
  2. an overall Tabulator that shows the Finder on top, and the preview of the source code below.

Let’s build the two step by step. First, we build the innerBrowser:

innerBrowser := GLMFinder new.
innerBrowser show: [:a |
a list
title: [:each | 'Methods invoking ', each name ];
display: #invokingMethods ;
format: #mooseName ].

To see the source code for each method, we can now embed this browser in a Tabulator that previews the code for the currently selected method in a below pane. The embedding is achieved using custom::

browser := GLMTabulator new.
browser title: [:each | 'Classic chaser on ', each mooseName ].
browser row: #methods; row: #source.
browser transmit to: #methods; andShow: [ :a |
a custom: innerBrowser ].
browser transmit from: #methods; to: #source; andShow: [:a |
a text display: #sourceText ].

This works as follows. The inner Finder takes care of navigating through methods. Whenever a method is selected, it automatically sets the #selection port of the pane holding the Finder, and this in turn triggers the transmission from the overall Tabulator.

Executing the script on a Method entity reveals a browser like in the screenshot. For example, if you have an ArgoUML model loaded, you can execute it by:

browser openOn: (
MooseModel root allModels first allMethods
entityNamed: #'org::argouml::model::Facade.getName()')

One drawback of this browser is that at any point we can see the source code of only one method. To make it more interesting, we could turn the browser around a bit, and embed the Tabulator inside the Finder. For each pane in the Finder we would display two panes: one showing the current list of invoking methods, and one showing the code preview of the selected method. When a method is selected, we want to see its invoking methods to the right in the Finder. See the below screenshot for more details.

Chaser - example of composing browsers

In this example, we are dealing with the following browser:

  1. an inner Tabulator that shows the invoking methods of a given method, and takes care of the source code preview of the selected method, and
  2. an overall Finder browser that spawns the next pane for each selection.

As before, first we build the innerBrowser:

innerBrowser := GLMTabulator new.
innerBrowser row: #methods; row: #source.
innerBrowser transmit to: #methods; andShow: [ :a |
a list
title: [:each | 'Methods invoking ', each name ];
display: #invokingMethods ;
format: #mooseName ].
innerBrowser transmit from: #methods; to: #source; andShow: [:a |
a text display: #sourceText ].

No magic here, just a plain Tabulator. While this works just fine as standalone, because the Tabulator is an explicit browser, it does not automatically populate the #selection port like in the case of the Finder. Thus, when inside the Finder it we still need to inform the pane surrounding our browser that the #selection port has changed. To do this, we have to explicitly export the #selection port from the #methods pane to the #selection port of the #outer pane:

innerBrowser transmit from: #methods; toOutsidePort: #selection.

Once this accomplished, we can now embed it in a Finder using custom::

browser  := GLMFinder new.
browser title: [ :each | 'Reference browser on ', each mooseName ].
browser show: [ :a |
a custom: innerBrowser ].

The new browser can be opened using the same code as before:

browser openOn: (
MooseModel root allModels first allMethods
entityNamed: #'org::argouml::model::Facade.getName()')

These examples show how we can compose browsers in different ways to obtain complex interactions. An important issue is to take care of what values go outside the browser. When embedding an implicit browser (e.g., Finder) we can directly rely on the #selection port of the outer pane. When embedding an explicit browser (e.g., Tabulator) we need to explicitly populate the outer ports.

Add a Note