Hi,

Sorry if my reply will be too long, but I tried to summarise our experience
with Morphic/Brick and give some useful feedback or even provide ideas. Who
wants will read :)

Brick is not a thin layer anymore.

It depends on what you mean under 'a thin layer' ;) Anyway, Brick was born
out of need. Spotter is completely built using Brick and Rubric. No morphs
are used at all (except Rubric of course). All Bricks in Spotter in the end
are subclasses of GLMBrick, which uses from original Morph only Canvas
(without drawing) and MorphicEvents. Everything else was rewritten from
scratch. During some period of time Brick was able to render itself on
Athens canvas, but we dropped it because of Font issues in athens.
Spotter is not the only tool written using Brick, GLMPager - a pane pager
in GT-Inspector, GT-Playground and GT-Debugger is also completely done
using Brick. Almost all tab labels and tab selector in GT tools are Bricks.

I could say that we collected quite some experience building it. Now I
would like to enumerate features of UI framework we used to solve
encountered problems.


   - Local bound coordinates.

Nothing to say more here :) It's somehow obvious to have.


   - Detects graph of Bricks that should be invalidated when Brick triggers
   invalidation. So there is no need to invalidate the whole graph.
      - For example if Brick fills parent it means that it doesn't depend
      on children (simple and most common situation). So if any child triggers
      invalidation there is no need to invalidate parent as it doesn't
depend on
      them. The same works in reversed way.
      - Build graph separately for each dimension: width and height. If
      only height of Brick was changed and children depend only on width, there
      is no need to invalidate them => x2 faster.
      - Invalidate resizing and re layouting separately. If there is no
      need to reposition bricks when size changes we just skip it.


   - Link subbricks in a DoubleLinked list. So subbrickAfter and
   subbrickBefore could be done in O(1). Morphic does it in O(n). It iterates
   over the whole submorphs collection of the owner finds index if current
   morph and then returns submorphs at: index +/-1. submorphAfter is used when
   morphic tries to detect which morph should take focus. Take a look at:
   Morph>>nextMorphWantingFocus. It iterates over owner's subbricks and for
   each asks submorphAfter (O(n)) - resulting time O(n^2)! That is why opening
   a window with a lot of submorphs takes so many time, especially system
   browser for big classes.


   - Native support of *paddings* with css-like syntax:

padding: anArrayOrInteger

paddingTop: anInteger
> paddingRight: anInteger
> paddingBottom: anInteger
> paddingLeft: anInteger


padding: 10. "top, right, bottom, left padding are 10px"

padding: #(10 20). "top, bottom padding 10px. left, right padding 20px"

padding: #(10 20 30). "top 10px. left, right 20px. bottom 30px"

padding: #(10 20 30 40). "top 10px, right 20px, bottom 30px, left 40px"


   - Native support of *margins* with css-like syntax:

margin: anArrayOrInteger

marginTop: anInteger
> marginRight: anInteger
> marginBottom: anInteger
> marginLeft: anInteger

Have the same syntax as padding.


   - Native support of dynamic *minHeight, minWidth, maxHeight, maxWidth*

minWidth: anIntegerOrBlock "actually any object that implements brickValue:"
> maxHeight: anIntegerOrBlock


minHeight: [ : brick | brick label width * 2 ].

minHeight: 200.

maxWidth: [ self height / 2 ].

maxWidth: 100.


   - Native support of *z-index*, so we don't rely on subbricks' order any
   more if one brick should be drawn above/below others. Works the same as css
   one.

zIndex: 2. "default value is 1"



   -  Native support of *floating *bricks. Floating bricks don't influence
   on any other neighbour bricks in layout.

floating: aSymbol

floating: #right. "top right brick's corner matches top right parent's
corner + corresponding paddings and margins"

floating: #center. "will be in the center of the parent and doesn't care
about other subbricks"

 Example use case is scroll bar in spotter - it has floating: #right and
zIndex: 2 so it appears above all list elements. (+ some margins to make it
fancy).


   - Native support of horizontal and vertical *aligning*.

hAlign: aSymbol

vAlign: aSymbol

hAlign: #right.

vAlign: #center.



   -  Horizontal and vertical *shrinking* to fit children. It is possible
   to have in one brick two subbricks where one shrink wraps and other fills
   parent.

hShrinkWrap.
> vShrinkWrap.



   - Horizontal and vertical *filling* with support of percents and min/max
   height/width. If in brick one child shrink wraps width (or have static
   width) and other fills 100% of parent's width, filling one will take only
   available space left by shrinking (or static) brick.

hSpaceFill: aFloat - fills aFloat percents of parent's width
> hSpaceFill. - alias for 100 percents.
> vSpaceFill: aFloat - fills aFloat percents of parent's height
> vSpaceFill. - alias for 100 percents.



   - Transparent *border* styling with possibility to separately configure
   each side.

borderWidth: anArrayOrInteger.
> borderColor: anArrayOrColor

The same syntax as used for paddings/margins


   - Native support of smooth gradient *box-shadows* with rounded corners:

shadowColor: aColor.
> shadowWidth: anInteger.



   - Layout events such as:

onLayouted - "called when brick was completely resized and layouted in its
> parent"

onChildrenLayouted - "called when all children are completely resized and
> layouted"


Use case: implementing flexible and responsive UI. Used in GLMLabelBrick to
shrink text and add '...' when it doesn't fit in parent. Example is tab
labels in Playground. Morphic version of tabs shrinks text and change ui of
the tab during drawing. WAT?


   - Application dependent themes. To make app work perfect all UI elements
   should have the same style. In case of spotter, item preview is created
   outside of spotter and there is no way using existing morph theme mechanism
   to customize its look&feel especially for spotter, because widgets always
   refer to global UITheme. Brick extends theming mechanism and allows to
   extend existing themes specifically for needed application and it will be
   applied to the whole tree of subbricks. For example spotter can completely
   change theme in runtime to dark (without even closing it).

"in context of SpotterMorph" self themer: GTSpotterBrickDarkThemer new. (or
> just replace first line in initialize)

Even having white theme chosen in settings. (all rubric fields become dark
too).



   - What we really want to have in Bloc is an easy way to detect that user
   clicked outside of specific morph.


What do you want with brick ? A new framework or just an "implementation
> detail" for spotter?
> It is *really* important to clarify this.


Now it becomes more stable, api changes less and less. We already have
quite a number of tests for layouting logic and documentation for basic
widgets.
What we want from brick... I think we want to have a way to build fast and
cheap unique and complex UIs. While it will fit our needs in GT Team we
will use it. We really would like to move all tools to Bloc as soon as
possible, but we just need something right now that works and can be used
in current version. Of course it would be great to cooperate to make Bloc
release sooner.

Some time ago I tried to port Brick on top of Bloc saving all business
logic untouched and only changing events from morphic ones to bloc and
FormCanvas to AthensCanvas. It was rather successful. I will be happy if it
would be possible to do everything or almost everything mentioned above in
Bloc. I think we will never (but who knows) announce Brick as separate
project. In our minds it is just useful helper library.

Thanks for reading :)

Cheers,
Alex

Reply via email to