Simple view

Problem:

How much effort should you invest into creating a new view?

Forces

There are many different kinds of views; how can you tell which one is most suitable?

Solution

A view exists only to answer a question about a domain object. Pick the simplest view that will answer the question. Only refine the view if it can answer more questions.

Steps

When you design a custom view, think of the GQM approach to defining metrics. As with metrics, a view exists to answer a specific question that should be answered to achieve a certain goal .

Also think of the KISS principle, which essentially states that you should do the simplest thing that solves your problem.

forward

There are numerous kinds of Inspector views available. One of the simplest is the forward view, which defines a view to be forwarded to an existing view of another object. An example is the Lepiter page view of the file storing the contents of a Lepiter page, such as this one:

thisSnippet page database monitor pageFileReference: thisSnippet page
  

The code for this view is defined in AbstractFileReference>>#gtLepiterPageFor: gtLepiterPageFor: aView <gtView> (self isFile and: [ self extension = #lepiter ]) ifFalse: [ ^ aView empty ]. ^ aView forward title: 'Lepiter page'; priority: 10; object: [ LeLocalStoreLoad current loadPageFrom: self ]; view: #gtLiveFor: and just forwards the view to the existing Live view of the actual lepiter page (i.e., this page).

textEditor

Another trivial view is the textEditor view, such as is used in the default Print view of any Object. It's defined in Object>>#gtPrintFor: gtPrintFor: aView <gtView> ^ aView textEditor title: 'Print'; priority: 110; aptitude: BrGlamorousCodeEditorAptitude; text: [ self printString asRopedText ]; actionUpdateButton .

lists and trees

The most common non-trivial views are variants of the list and tree views. The first two views of this example are columnedList and list views. (Opt-click on the view header to browse or edit the view code.)

explicit and mondrian

An explicit view allows you to plug in any graphical element. This can be a simple view if the graphical element exists already, for example the Live view of the Memory game: GtMemoryGame>>#gtLiveFor: gtLiveFor: aView <gtView> ^ aView explicit title: 'Live'; stencil: [ self asElement ] . If you have to build the element, then it can be arbitrarily complex.

Similarly, a Mondrian view, such as the Circular view of the address book example above, can be quite simple, but in general visualizations can quickly become complicated.

It is common to start with a forward view, and then later replace it by a custom view, or to start with a very simple columnedList view, and gradually add more columns.

Consider a columnedTree if the list items can be group and organized into a tree.

To find examples of a given view type, you can use a query like this one.

#gtView gtPragmas & #columnedTree gtSenders
  

Dont forget the empty view. This can be used as a placeholder for a new view you are creating, or as an option if the view is not needed in some cases. For example, the Items (files) view is empty if the file being inspected is not a directory: AbstractFileReference>>#gtItemsFor: gtItemsFor: aView <gtView> | eventLoop | self isDirectory ifFalse: [ ^ aView empty ]. eventLoop := self watcher startLoop. ^ aView columnedList title: 'Items' translated; priority: 10; updateWhen: GtFileWatcherChangeAnnouncement in: eventLoop announcer; items: [ self gtChildrenWithParent ]; column: 'Icon' translated icon: [ :each | each isDirectory ifTrue: [ BrGlamorousIcons folder ] ifFalse: [ BrGlamorousIcons file ] ]; column: 'Name' translated text: [ :each | (self isChildOf: each) ifTrue: [ '..' ] ifFalse: [ each basename asString ] ]; column: 'Size' translated text: [ :each | [ each isDirectory ifTrue: [ '--' ] ifFalse: [ each humanReadableSize asString ] ] on: FileException, FileSystemError do: [ :anException | anException return: '' ] ] width: 100; column: 'Creation' translated text: [ :each | [ String streamContents: [ :s | each creationTime printYMDOn: s. s nextPut: Character space. each creationTime printHMSOn: s ] ] on: FileException, FileSystemError do: [ :anException | anException return: '' ] ] width: 150; actionUpdateButton

A list of heterogeneous attributes can also be displayed as a columnedList by dynamically creating a dictionary of keys and values, and using those as the column headers. For an example, inspect the result of the query above, click on the (i) inspect button, and go to the Metrics view.

Related patterns

When you introduce a Viewable entity, you want to start with a Simple view.