layout.lzx
<library>
<class name="layout" extends="node">
<doc>
<tag name="shortdesc"><text>Abstract layout base class.</text></tag>
<text>
<p>A layout is used to automatically distribute, align, or
otherwise arrange the subviews of the view that contains it (it
arranges the views that are it's siblings). Using a layout
obviates the need to precisely position the views of an
application and allows an application to resize or reconfigure
gracefully according to the constraints of its container.</p>
<p>The layout class is a null layout — it does not arrange
it's siblings. It is the base class that other layouts extend.
<classname>simplelayout</classname> is a basic extension of the
layout class. The example below illustrates using a simple
layout to distribute views along the y axis.</p>
<example title="Using a simple layout to arrange views"><programlisting>
<canvas height="60">
<view bgcolor="#DDFFFF">
<text>Without a layout, it can be difficult</text>
<text x="2" y="5">to manually position your views</text>
</view>
<view y="25" bgcolor="#FFDDFF">
<simplelayout axis="y" />
<text>A layout makes</text>
<text>positioning simple and automatic</text>
</view>
</canvas>
</programlisting></example>
<p>Layouts are so important, there is a shorthand for specifying them
using the <xref linkend="LzView.__ivars__.layout"/> attribute
of <xref linkend="LzView"/>. The previous example could be more
compactly specified:</p>
<example title="Using the layout attribute to specify a layout"><programlisting>
<canvas height="35">
<view layout="class: simplelayout; axis: y">
<text>A layout makes</text>
<text>positioning simple and automatic</text>
</view>
</canvas>
</programlisting></example>
<p>Because <xref linkend="lz.simplelayout"/> is the default
layout, the above example could also have used just <code>layout="axis:
y"</code>.</p>
<p>Layouts, like constraints and animators, affect specific
attributes of a view. Views can have more than one layout, as long
as each set of attributes associated with a layout does not
overlap with any of the other sets.</p>
</text>
</doc>
<attribute name="vip"/>
<attribute name="locked" value="2
"/>;
<setter name="locked" args="locked ">
if ( this.locked == locked ) return;
if ( locked ){
this.lock();
} else {
this.unlock();
}
</setter>
<attribute name="subviews"/>
<attribute name="updateDelegate"/>
<method name="construct" args="view , args">
// Set locked to special default value of 2 to prevent updates until
// __parentInit() is called. Set as early as possible - can't wait for
// setter to be called.
this.locked = 2
super.construct(view, args);
this.subviews = [];
this.vip = (this.immediateparent cast LzView);
// view.layouts: If it doesn't already exist, layouts create an array
//in the view that it attaches to that hold the list of layouts attached
//to the view.
if ( this.vip.layouts == null ){
this.vip.layouts = [ this ];
} else {
this.vip.layouts.push( this );
}
this.updateDelegate = new LzDelegate( this , "update" );
// register for unlock when parent inits, or unlock if already inited
if (this.immediateparent.isinited) {
this.__parentInit();
} else {
new LzDelegate( this , "__parentInit", this.immediateparent, "oninit" );
}
</method>
<handler name="onconstruct">
//all layouts need to know when their view adds/deletes a subview
new LzDelegate ( this, "gotNewSubview",
this.vip, "onaddsubview" );
//this is never called b/c removeSubview is not implemented yet
new LzDelegate ( this, "removeSubview",
this.vip, "onremovesubview" );
var vsl = this.vip.subviews.length;
for (var i = 0; i < vsl; i++){
this.gotNewSubview( this.vip.subviews[i] );
}
</handler>
<method name="destroy">
if ( this.__LZdeleted ) return;
this.releaseLayout(true);
super.destroy();
</method>
<method name="reset" args="e = null ">
if ( this.locked ) { return; }
//defalt behavior on reset is to update
this.update( e );
</method>
<method name="addSubview" args="sd ">
var layoutAfter = sd.options['layoutAfter'];
if ( layoutAfter ){
//this wants to get inserted in a specific place in the layout
this.__LZinsertAfter( sd , layoutAfter );
} else {
this.subviews.push ( sd );
}
</method>
<method name="gotNewSubview" args="sd ">
if ( ! sd.options['ignorelayout'] ){
this.addSubview( sd );
}
</method>
<method name="removeSubview" args="sd ">
var subviews = this.subviews;
for ( var i = subviews.length-1; i >= 0; i-- ){
if ( subviews[ i ] == sd ){
subviews.splice( i , 1 );
break;
}
}
this.reset();
</method>
<method name="ignore" args="s ">
//default behavior on addSubview is to reset
var subviews = this.subviews;
for (var i = subviews.length - 1; i >= 0; i-- ){
if ( subviews[ i ] == s ){
subviews.splice( i , 1 );
break;
}
}
this.reset();
</method>
<method name="lock">
this.locked = true;
</method>
<method name="unlock" args="ignore=null">
this.locked = false;
this.reset()
</method>
<method name="__parentInit" args="ignore=null">
// check for special value of 2, set in construct()
if ( this.locked == 2){
if (this.isinited) {
this.unlock();
} else {
new LzDelegate( this , "unlock", this, "oninit" );
}
}
</method>
<method name="releaseLayout" args="fromdestroy=null">
if ($debug) {
if (fromdestroy == null) Debug.deprecated(this, arguments.callee, this.destroy);
}
// if called from destroy(), don't remove delegates
if (fromdestroy == null && this.__delegates != null) this.removeDelegates();
if (this.immediateparent && this.vip.layouts) {
for ( var i = this.vip.layouts.length -1 ; i >= 0 ; i-- ){
if ( this.vip.layouts[ i ] == this ){
this.vip.layouts.splice( i , 1 );
}
}
}
</method>
<method name="setLayoutOrder" args="sub1 , sub2 ">
var subviews = this.subviews;
for (var i2 = subviews.length -1; i2 >= 0 ; i2-- ){
if ( subviews[ i2 ] === sub2 ){
subviews.splice( i2, 1 );
break;
}
}
// check if sub2 was found
if (i2==-1) {
if ($debug) Debug.warn('second argument for setLayoutOrder() is not a subview')
return;
}
if ( sub1 == "first" ){
subviews.unshift( sub2 );
} else if ( sub1 == "last" ){
subviews.push( sub2 );
} else {
for (var i = subviews.length -1; i >= 0 ; i-- ){
if ( subviews[ i ] === sub1 ){
subviews.splice( i+1 , 0 , sub2 );
break;
}
}
// check if sub1 was found
if (i==-1) {
if ($debug) {
if (sub1 == sub2) {
Debug.warn('%w is the same as %w', sub1, sub2)
} else {
Debug.warn('first argument for setLayoutOrder() is not a subview')
}
}
// put sub2 back
subviews.splice(i2, 0, sub2);
}
}
this.reset();
return;
</method>
<method name="swapSubviewOrder" args="sub1 , sub2 ">
//this will cause problems if sub1 or sub2 are not subviews
var s1p = -1;
var s2p = -1;
var subviews = this.subviews;
for (var i = subviews .length -1; i >= 0 && ( s1p < 0 || s2p < 0 );
i-- ){
if ( subviews[ i ] === sub1 ){
s1p = i;
}
if ( subviews[ i ] === sub2 ){
s2p = i;
}
}
if (s1p >= 0 && s2p >= 0) {
subviews[ s2p ] = sub1;
subviews[ s1p ] = sub2;
} else if ($debug) {
Debug.warn('Invalid subviews for swapSubviewOrder()');
}
this.reset();
return;
</method>
<method name="__LZinsertAfter" args="newsub , oldsub ">
var subviews = this.subviews;
for ( var i = subviews.length -1 ; i >= 0; i-- ){
if ( subviews[ i ] == oldsub ){
subviews.splice( i , 0 , newsub );
}
}
</method>
<method name="update" args="e=null">
</method>
<method name="toString">
return "lz.layout for view " + this.immediateparent;
</method>
</class>
</library>
Cross References
Classes
- <class name="layout" extends="node">