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.
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.
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