Aliaksei Syrel

Brick for Pharo | Part III - Flexible layouts

Feb 202015

In this tutorial I will show how to create a basic flexible layouts using Brick. To do it you just need to know a few methods:

  • hSpaceFill: aNumber - width fills aNumber of percents of available space
  • hSpaceFill - width fills all available space
  • vSpaceFill: aNumber - height fills aNumber of percents of available space
  • vSpaceFill - height fills all available space
  • useHorizontalLinearLayout - elements are positioned horizontally in a line
  • useVerticalLinearLayout - elements are positioned vertically in a line

Having that hSpaceFill and vSpaceFill are just aliases for corresponding hSpaceFill: 100 and vSpaceFill: 100 the amount is reduced by a third. The first letter in message names stands for horizontal (h) or vertical (v). It also means on what kind of dimension it is applied - width (h) or height (v). Example below explains how to create the following layout:

undefined

First of all let's create a red Brick. Because both width and height should be equal to the half of parent's available space I send hSpaceFill: and vSpaceFill to the GLMBrick object with an Integer argument 50:

redBrick := GLMBrick new
   "my width is 50% of my parent"
hSpaceFill: 50; "my height is 50% of my parent" vSpaceFill: 50; "sets my colour" color: Color lightRed.

The same applies to the yellow Brick:

yellowBrick := GLMBrick new
   "my width is 50% of my parent"
hSpaceFill: 50; "my height is 50% of my parent" vSpaceFill: 50; "sets my colour" color: Color lightYellow.

And finally we create our root Brick to hold red and yellow ones:

GLMBrick new
"my width and height are 300px" extent: 300@300;
"setting colour" color: Color veryVeryLightGray;
"and adding bricks as my direct children" addAllBricks: { redBrick . yellowBrick }; yourself

By default layout of all bricks is horizontal linear. But what if I want to reposition bricks in layout above vertically? The only thing to do is to change layout of root brick manually to vertical. It can be done sending useVerticalLinearLayout to the root:

GLMBrick new
useVerticalLinearLayout;
"my width and height are 300px" extent: 300@300;
"setting colour" color: Color veryVeryLightGray;

"and adding bricks as my direct children" addAllBricks: { redBrick . yellowBrick }; yourself

The resulting layout will look as follows:

undefined

 

In next tutorial I will describe how to create shrinking layouts using Brick

Pharo-like Announcer in JavaScript

Jan 102015

I like to use Model-View pattern in Pharo to connect UI with its model. For this purpose developers use Announcer class. It's syntax is very simple:

"to subscribe"
self announcer on: OnModelChanged send: #onModelChanged to: self
"to announce"
self announcer announce: OnModelChanged new

In small applications huge frameworks like AngularJS increase complexity and add unnecessary levels of abstractions. When there are only a few models, Model-View pattern helps developer to create simple and compact one-page applications. I propose Announcer implemented in JavaScript. Syntax is almost the same:

// to subscribe
this.announcer().onSendTo(OnModelChanged, this.onModelChanged, this);
// to announce
this.announcer().announce(new OnModelChanged());
// to unsubscribe
this.announcer().unsubscribe(this);

Having that JavaScript in browser doesn't support Garbage Collection user should unsubscribe from announcer as soon as View is not used any more. Announcer.unsubscribe(receiver) stands for this.

How to use:

First of all we need to create an Announcement object:

// model announces me when it changes
function OnModelChanged() {}

 Then we create and prototype a Model with single public method doSomething() that just notifies all subscribers about changes: 

function Model () {
   this.announcer = new Announcer();
}
Model.prototype = (function(){
   return {		
      doSomething : function() {
         this.announcer.announce(new OnModelChanged());
      }
   };
})();

And last but not least we create a view:

function View() {
   this.model;
}
View.prototype = (function(){
   return {
      setModel : function(aModel) {
         this.model = aModel;
         this.model.announcer.onSendTo(OnModelChanged, this.onModelChanged, this);
      },
      onModelChanged : function(announcement) {
         console.log('model changed!');
}
};
})();

Finally let's instantiate and connect our model and a few views:

var model = new Model();
var viewA = new View();
var viewB = new View();

viewA.setModel(model);
viewB.setModel(model);

model.doSomething();

In console there should be:

model changed!
model changed!

Download:

It's open source, check out Github repo https://github.com/syrel/AnnouncerJS

Brick for Pharo | Part II - Basic layouts

Jan 032015

There are only a few basic and foundational types of layouts that are used to build flexible interfaces. In this post we will see which types of them are implemented and could be used in Brick. First of all lets divide all of them in two groups by orientation:

  • Horizontal layouts
  • Vertical layouts

Nevertheless, orientation is not the only parameter that describes a specific layout. I would like to postulate that parent's and children's dimensions depends on each other in four ways:

  • Dimension depends on parent
  • Parent depends on children
  • Height depends on width and the way around
  • Doesn't depend

 Examples

When parent and children are independent:

undefined

The width of the parent is a sum of children's widths:

undefined 

Or when children fill the height of the parent and parent expands to children's width at the same time:

undefined

Each layout like shown above I name LinearLayout because every element is positioned right after it's predecessor in one line - linearly. Children in simple LinearLayout can have different width or height. But there are cases when we want them to depend on the parent's dimension. For example proportionally:

undefined

And even fill height too:

undefined

Out of box Brick supports the following ones:

  • GLMHorizontalLinearLayout
  • GLMVerticalLinearLayout

They will be described more detailed in the next Part III of Brick for Pharo.

Brick for Pharo | Part I - Introduction

Jan 032015

During my work on some projects for Moose and then for Pharo I had to design UI using Morphic framework. It is rather powerful with lots of features and possibilities for a developer. Morphic allows to create any kind of graphic design with any complexity which sometimes leads to overcomplicated solutions with relatively big number of used hacks. After some time of deep playing with Morphic a Brick was born with a goal to simplify creation of complicated and flexible user interfaces. In this and next posts I will try to describe and explain the basic principles of using Brick. What you should know about it:

Brick is a layer on top of Morphic

Morph subclass: #GLMBrick

Brick replaces layouting mechanism

It overrides all necessary method that are used by Morph to layout submorphs

adjustLayoutBounds
"no need in it"
computeBounds
"nothing to compute, everything is already computed"
computeFullBounds
"nothing to compute, everything is already computed"
doLayoutIn: layoutBounds
"we removed morphic layouting mechanism"
fullBounds
^ self globalBounds
layoutBounds: aRectangle
"I'm not a morph anymore"

Layouting mechanism in Brick is very simple, straightforward and explicit. Everything you are interested in is done in one method:

doLayout
  "most of the time you don't want to override this method"
  "nice example to check is GLMLabelBrick>>doLayout"
  self layoutPolicy layout: self in: self brickBounds.
  self subbricks do: [ :each | each isBrick ifTrue: [ each doLayout ] ]

Brick uses local coordinates

Local coordinates allow to reach high performance implementations of scroll panes, translation animations and any other relative transformations. Brick benefits from such coordinate system, as there is no difference between manual positioning of subbricks an using any predefined layout. To work in the Morphic World, Brick can convert its local coordinates to global ones. 

undefined

Brick can't draw outside of its owner

It is useful because developer should not think about clipping and overlapping issues. Also such behavior is natural for our surrounding real word: you can't place a solid thing inside and outside of the box at the same time, ship can't go outside of the sea.

undefined
Brick supports explicit z-index

In most cases support of z-index is necessary. Morphic partially supports it: ordering depends on the index of submorph in the list. But I think it's much easier to change one integer value than swapping submorphs in order to place a submorph on top/bottom dynamically.

undefined

Brick supports advanced theming

Drawing logic could be moved out of Brick to a separate renderer class. They must subclass GLMBrickRenderer. As soon as you implemented custom renderer it can be set using:

GLMBrick>>renderer: aRenderer

Having possibility to customize drawing logic without subclassing will allow us to apply a complicated theme to a Brick as described here. In the future where vector graphics will be a standard for drawing UI in Pharo. Theme developer will be able to not only change static variables as color, font size but also modify a shape.