Sunday, March 9, 2014

LayoutProperties in Morphic

(continuing from the previous post)

The only non-trivial aspect when defining a new LayoutPolicy to be used in Morphic is the handling of the associated layout properties.
These properties allow to configure the way a LayoutPolicy arrange submorphs. E.g. the listDirection property in a TableLayout has the possible values #leftToRight, #rightToLeft, #topToBottom and #bottomToTop, allowing to build a row or a column, and also to arrange the submorphs in reversed order in either dimension.

An instance of LayoutProperties is the holder of the set of properties for a morph. This instance is stored not by the LayoutPolicy, but by the morph itself, and it is also modified through Morph methods. The corresponding methods are gathered in the layout-properties method category in the Morph class.
Therefore, for each property to be added to any kind of LayoutPolicy, the corresponding accessors should be added to the Morph class.
The intent of each property is documented in the corresponding setter in the Morph class.

The package Morphic-Layout includes the TableLayout, which requires the definition of some particular properties in addition to the properties being common to all layout policies. Correspondingly, the class TableLayoutProperties is defined, being a subclass of LayoutProperties.

Now, how can a Morph know which kind of layout property holder to instantiate? This issue is handled as follows.

When a common layout property is set to a Morph, it forces the creation of a LayoutProperties instance; the method assureLayoutProperties does the job. E.g.
  hResizing: aSymbol
    "Layout specific. This property describes 
    how the receiver should be resized 
    with respect to its owner and its children."
    self assureLayoutProperties hResizing: aSymbol.
    self layoutChanged.

If a particular table property is set, then it is a TableLayoutProperties what must be assured. E.g.
  cellInset: aNumber
    "Layout specific. This property specifies 
    an extra inset for each cell in the layout."
    self assureTableProperties cellInset: aNumber.
    self layoutChanged.

Now image that a Morph is set first the hResizing property, and then the cellInset property. When creating the TableLayoutProperties instance, we should keep the value already set to the hResizing property. Let us take a look to the assureTableProperties method in Morph.
  assureTableProperties
    | props |
    props := self layoutProperties.
    props == self ifTrue:[props := nil].
    props ifNil:[
      props := TableLayoutProperties new initializeFrom: self.
      self layoutProperties: props].
    props includesTableProperties 
      ifFalse:[self layoutProperties: 
                (props := props asTableLayoutProperties)].
    ^props

So we observe that any object representing a holder for layout properties should understand two additional messages: includesTableProperties and asTableLayoutProperties.

To add even more chaos to the situation, let us take a look to the getter for a table-specific property in Morph.
  cellInset
    "Layout specific. This property specifies 
     an extra inset for each cell in the layout."
    | props |
    props := self layoutProperties.
    ^props ifNil:[0] ifNotNil:[props cellInset].
but ... what if props is a "base" layout properties holder, i.e. does not include the table-specific properties? Aie ... the table-specific property getters are duplicated in LayoutProperties, e.g.
  cellInset
    "Default"
    ^0
so that the default value for each table-specific property is duplicated. 

This mess should be taken into account if LayoutPolices having additional, specific properties, want to be defined.

Saturday, March 8, 2014

Custom LayoutPolicies in Morphic

A LayoutPolicy is an object which can be attached to a Morph, whose responsibility is to spatially organise the submorphs of that morph, and eventually change the size of the morph in order to encompass the space used by submorphs.
Four LayoutPolicies are provided, two in the Morphic-Layout packages, two in some Polymorph package. They seem to be limited to one-dimension proportional layout, and different flavors of row or column layout.

The LayoutPolicies are invoked on several morph events: change of dimensions or position, adding or removing submorphs, also when content is dropped. They can also be invoked explicitly by sending the message layoutChanged to the morph.

I guess that a LayoutPolicy could be used to perform different dynamic changes on the submorphs of a morph: hide/show submorphs, change colors, change editable/non editable settings, etc..

Fortunately, it seems to be almost very easy to implement a new LayoutPolicy: it suffices to redefine the method layout:in:. The first argument is the morph whose submorphs are to be arranged, and the second some sort of reference box for the morph. You just arrange the submorphs modifying their bounds, position or extent. You can also modify the bounds of the morph itself. I guess that some forms of modifying the morph bounds can imply entering into a layout-computing loop, I found that the method layoutBounds: seems to be safe (a note about bounds in Morphic is to be released shortly).
Other method that one would possibly like to redefine is minExtentOf:in:, whose arguments are the same as layout:in:.It should return the minimal extent needed for the morph to encompass its submorphs, given the way the policy arrange the submorphs. I am still not quite sure about what is this method used for.

Other methods which can be redefined, but I guess most of the times can be ignored, are:
  • flushLayoutCache, which is sent in order to let the policy to clear information which should not be mantained between different layout computations.
  • indexForInserting:at:, which is used when something is drop onto the morph in a drag/drop operation.
  • isTableLayout, which I think is better to respect the default behaviour which is to answer false.
The code for a very simple LayoutPolicy follows. It simply position the first and second submorph to fixed positions, and hide the rest.

LayoutPolicy subclass: #TrivialTwoSubmorphsPolicy
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Carlos-Sandbox'

layout: aMorph in: newBounds
"Compute the layout for the given morph based on the new bounds"
| submorphs aStream |
submorphs := aMorph submorphs.
"first submorph"
submorphs isEmpty ifTrue: [ ^self ].
submorphs first position: aMorph position + (20@5).
"second submorph"
(submorphs size < 2) ifTrue: [ ^self ].
submorphs second position: aMorph position + (20@35).
"the remaining submorphs"
(submorphs size < 3) ifTrue: [ ^self ].
aStream := ReadStream on: (aMorph submorphs).
aStream next; next.
[ aStream atEnd ] whileFalse: [ aStream next hide ].

minExtentOf: aMorph in: newBounds
"Return the minimal size aMorph's children would require given the new bounds.
It is not required to override this method 
 in order to obtain a minimal yet working subclass of LayoutPolicy"
^aMorph bounds


Why implementing a new LayoutPolicy is only almost very easy? Stay tuned!

How to obtain a nice form in Morphic ...

... i.e., in which labels and fields are nicely rendered, and tab/shift-tab behave as expected.
Some adequate field morphs seem to exist, provided by something called Polymorph, which I found through this post entry. In the example, a very nice form is generated.
Nicely, the text fields in the Magritte-generated form are built by using the tools described in the Polymorph example, and indeed they observe a good tab-shift/tab behaviour. The main problems in the Magritte form are the rendering of labels and fields, and the general aesthetic.

I will address the rendering first, trying to produce a simple GridFormLikeLayout for the form morph. Wish me luck!

Some very preliminary comments on Magritte

Following a suggestion made by Esteban Lorenzano, I will try to describe the objects constituting the laboratory book model by using Magritte. I gave it a look, reading some of the available documentation, and playing a little bit with it also.

I liked very much its main idea, at least as I understood it: a protocol for describing objects, such that the resulting descriptions can be used to build automatically a lot of related stuff (given the adequate tools to do so): both Web and desktop user interface, reports, persistence, etc..
Wrt Web user interface, it seems that Magritte integrates well with Seaside.
I also liked the object description protocol, seems simple and powerful at the sime time.

On the downside, I felt the support given by Magritte, in its current version 3, to build desktop user interfaces using Morphic, as very limited, and also somewhat unsatisfactory. Even a simple form generated by Magritte looks far from nice.


This is a problem since I intend the laboratory book as a desktop application.
I will try to enhance the Morphic support given by Magritte. The results, soon in this blog.

Starting point

Just to start, a tiny intent declaration.

I want to experiment the software development environment provided by Pharo Smalltalk, including some libraries and tools.
The main goal is to get acquainted with some tools I know only loosely, get back programming fluency which I somewhat lost during the last year, and hopefully make some little contributions to the Pharo ecosystem.
I have an application in mind, a digital version of a laboratory book, which could help people to gather and organise the notes and logs taken by scientist, like my wife who works on biochemistry stuff.

The aim of this blog is to record and log my experiences working in Pharo. It is mainly for personal use. Nonetheless, I want to include some bits of documentation, comments and (maybe even) insight about tools and classes included in Pharo, which could be useful to others, specially given that the documentation around the Pharo ecosystem is usually quite poor.