basetabs.lzx
<library>
<include href="base/baselistitem.lzx"/>
<include href="base/baselist.lzx"/>
<class name="basetab" extends="baselistitem" clickable="true" focusable="true">
<attribute name="tabpane" value="null
" type="expression"/>
<attribute name="text" value="${this.tabpane.text}" type="text"/>
<method name="handletpdestroy" args="ignore">
this.destroy();
</method>
<handler name="onconstruct">
this.tpDestroyDel = new LzDelegate(this, 'handletpdestroy' ,
this.tabpane, 'ondestroy' );
</handler>
<method name="init">
super.init();
// If I'm selected, tell my parent to select me. Basetabs cannot exist
// w/o basetabpanes, so not necessary to check for existence.
if (parent instanceof lz.basetabsbar && tabpane.selected) {
parent.select(this);
}
// clear out tabpane's selected argument (so as not to confuse)
this.tabpane.setAttribute('selected', null);
</method>
<method name="destroy">
if (parent instanceof lz.basetabsbar && parent._initcomplete) {
// We need to move selected to another tab
if (parent._selector.selected[0] == this) {
if (parent.subviews.length == 1) {
// select no one if this is last
parent._selector.setAttribute('selected', null);
} else {
// set to first tab that isn't me
var myTab = parent.subviews[0];
if (myTab == this) myTab = parent.subviews[1];
myTab.setSelected(true);
parent._selector.setAttribute('selected', [myTab]);
}
}
}
super.destroy();
</method>
<method name="doSpaceUp">
this.setAttribute( 'selected' , true );
</method>
<method name="setText" args="t">
this.setAttribute( 'text' , t );
</method>
<method name="setSelected" args="s">
if (s) {
if (this.tabpane['content'] != null) {
this.tabpane.content.completeInstantiation();
}
this.setAttribute("clickable",false);
this.tabpane.setAttribute('visible', true);
this.setAttribute ('focusable' , false );
} else {
this.setAttribute("clickable",true);
this.tabpane.setAttribute('visible', false);
this.setAttribute ('focusable' , true );
}
super.setSelected(s);
</method>
<doc>
<tag name="shortdesc"><text>The base tab class.</text></tag>
<text>
<para>This class is to be used only in conjunction with
basetabs and basetabapane classes. You can pass the name of this class
in the 'tabclass' attribute of basetabpane, tabpane, basetabs, and
tabs classes. Basetabs uses this class as its default 'tabclass'.</para>
<example>
<canvas height="75">
<include href="base"/>
<class name="aquatab" extends="basetab"
width="40" height="20" bgcolor="aqua">
<text>tab</text>
</class>
<class name="greentab" extends="basetab"
width="40" height="20" bgcolor="green">
<text>tab</text>
</class>
<basetabs x="10" y="10" tabclass="aquatab">
<basetabpane>
<text>one</text>
</basetabpane>
<basetabpane tabclass="greentab">
<text>two</text>
</basetabpane>
<basetabpane >
<text>three</text>
</basetabpane>
</basetabs>
</canvas>
</example>
<p><b>See Also:</b></p>
<ul>
<li>
<sgmltag class="element" role="lz.basetabsbar"><basetabsbar></sgmltag>,
<sgmltag class="element" role="lz.basetabs"><basetabs></sgmltag>,
and <sgmltag class="element" role="lz.basetabpane"><basetabpane></sgmltag>
</li>
</ul>
</text>
</doc>
</class>
<class name="basetabsbar" extends="baselist" itemclassname="basetab" layout="class: simplelayout; axis: x; spacing: 0">
<method name="init">
super.init();
var sel = this.getSelection();
if (!sel) {
if (subviews[0] != null)
subviews[0].setAttribute('selected', true);
}
</method>
<method name="select" args="item">
if (item == null) return;
if (this._initcomplete) {
this._selector.select(item);
this.setAttribute("value", item );
relayerTabs();
item.updateDefault();
} else {
this._initialselection = item;
}
// turn off doesenter when selected so that default buttons work
this.setAttribute( 'doesenter', false );
</method>
<method name="relayerTabs">
// Iterate through tabs in order of appearance, sending every tab to
// back except for the selected tab, which will be brought to front.
for (var i=0; i < this.subviews.length; i++) {
var sv = this.subviews[i];
if (sv["selected"]) {
sv.bringToFront();
} else {
sv.sendToBack();
}
}
</method>
<doc>
<tag name="shortdesc"><text>A non-visual container for basetab components.</text></tag>
<text>
<p>A <classname>basetabsbar</classname> is a non-visual container
class that uses a default x-axis <sgmltag class="element" role="lz.simplelayout"><simplelayout></sgmltag>
to lay out its <sgmltag class="element" role="lz.basetab"><basetab></sgmltag>
children. <literal>basetabsbar </literal> is contained in a
<sgmltag class="element" role="lz.basetabs"><basetabs></sgmltag> component.</p>
<example title="custom basetabsbar">
<canvas height="75">
<include href="base"/>
<class name="aquatab" extends="basetab"
width="40" height="20" bgcolor="aqua">
<text>tab</text>
</class>
<class name="spacetabsbar" extends="basetabsbar" bgcolor="black"
layout="class: simplelayout; axis: x; spacing: 4">
</class>
<basetabs x="10" y="10" tabclass="aquatab" barclass="spacetabsbar">
<basetabpane>
<text>one</text>
</basetabpane>
<basetabpane>
<text>two</text>
</basetabpane>
<basetabpane>
<text>three</text>
</basetabpane>
</basetabs>
</canvas>
</example>
<p><b>See Also:</b></p>
<ul>
<li>
<sgmltag class="element" role="lz.basetab"><basetab></sgmltag>,
and <sgmltag class="element" role="lz.basetabs"><basetabs></sgmltag>
</li>
</ul>
</text>
</doc>
</class>
<class name="basetabpanecontent" x="${parent.inset_left}" y="${parent.inset_top}">
<attribute name="defaultplacement" value="_null_
" type="string"/>
<method name="determinePlacement" args="v , p, a">
v.parent = this.parent;
return this;
</method>
<method name="init">
super.init();
_recalculateContentWidth(null);
_recalculateContentHeight(null);
this._contentWDel =
new LzDelegate(this, "_recalculateContentWidth", this, "onwidth");
this._contentHDel =
new LzDelegate(this, "_recalculateContentHeight", this, "onheight");
</method>
<method name="_recalculateContentWidth" args="ignore">
parent.parent._calculateContentWidth(this.width,
parent.inset_left,
parent.inset_right,
parent.parent.bordersize);
</method>
<method name="_recalculateContentHeight" args="ignore">
parent.parent._calculateContentHeight(this.height,
parent.inset_top,
parent.inset_bottom,
parent.parent.bordersize);
</method>
</class>
<class name="basetabpane" extends="basecomponent" focusable="false" width="${parent._contentWidth}" height="${parent._contentHeight}" visible="false">
<attribute name="selected" value="false
" type="boolean" setter="_setSelected(selected)"/>
<attribute name="tab" value="null
" type="expression"/>
<attribute name="tabclass" type="string"/>
<attribute name="inset_top" value="${parent.inset_top}" type="number"/>
<attribute name="inset_right" value="${parent.inset_right}" type="number"/>
<attribute name="inset_bottom" value="${parent.inset_bottom}" type="number"/>
<attribute name="inset_left" value="${parent.inset_left}" type="number"/>
<attribute name="text" value="tab
" type="text"/>
<attribute name="placement" value="_tabscontent
" type="string"/>
<attribute name="_keepchildren" value="0
" type="number"/>
<setter name="datapath" args="dp">
if (null != this.datapath) {
this.datapath.setXPath(dp);
} else {
var data_args = { xpath : dp }
if (this.visibility != "collapse") {
data_args.datacontrolsvisibility = (this.visibility == "collapse");
}
new lz.datapath ( this, data_args );
}
</setter>
<method name="createChildren" args="c">
var myChildren = [];
if (_keepchildren > 0) {
myChildren = c.slice(0, _keepchildren);
c.splice(0, _keepchildren);
}
// Intercept basetabpane initstage for its content. Note: createChildren
// gets called before init.
var istage = "late"
if (this['initstage'] != null) {
istage = this.initstage;
}
this.initstage = null;
// NOTE [2008-06-20 ptw] This seems fraught with danger.
// Components shouldn't be mucking at this level
myChildren.push( { 'class': lz.basetabpanecontent,
attrs: { name: 'content', parent: this,
initstage: istage },
children: c } );
super.createChildren( myChildren );
</method>
<method name="_setSelected" args="s">
this.selected = s;
if (this.selected != this.tab.selected)
this.tab.setAttribute("selected", s);
</method>
<doc>
<tag name="shortdesc"><text>A non-visual tabpane for the basetabs component.</text></tag>
<text>
<p>A <classname>basetabpane</classname> is the non-visual container
for content in <classname>basetabs</classname>. Each
<classname>basetabpane</classname> has a corresponding
<classname>basetab</classname> created. Normally, you won't want to
extend this class. If you want a consistent background across all
<classname>basetabpane</classname>s, you can add a background to your
<classname>basetabscontent</classname> component. If you want a
different background for each <classname>basetabpane</classname>, you
could do that in the content placed inside of your
<classname>basetabpane</classname>.</p>
<p>Be aware that the content of <classname>basetabpane</classname> is
<code>initstage="late"</code>. You can override this by
declaring an <attribute>initstage</attribute> on a specific subclass
or instance of <classname>basetabpane</classname>.</p>
<programlisting>
<basetabpane initstage="normal">
<!-- ..content... -->
</basetabpane>
</programlisting>
<note>The parent of a <tagname>basetabpane</tagname> is
<tagname>basetabs</tagname>, but its <em>immediate parent</em> is the
default placement of the
<classname>basetabscontent</classname>.</note>
<example title="parent and immediate parent of basetabpane">
<canvas height="140" debug="true">
<debug x="150" y="10"/>
<include href="base"/>
<class name="aquatab" extends="basetab" width="40" height="20" bgcolor="aqua">
<text>tab</text>
</class>
<class name="borderedtabscontent" extends="basetabscontent" bgcolor="black">
<attribute name="defaultplacement" value="borderedcontent" type="string"/>
<view name="borderedcontent"
x="${parent.parent.bordersize}"
y="${parent.parent.bordersize}"
width="${parent.width-(2*parent.parent.bordersize)}"
height="${parent.height-(2*parent.parent.bordersize)}"
clip="true" bgcolor="red"/>
</class>
<basetabs x="10" y="10" bordersize="3"
tabclass="aquatab"
contentclass="borderedtabscontent">
<basetabpane name="one">
<handler name="onvisible" args="isvisible">
if (isvisible) {
Debug.debug("parent: %w", parent);
Debug.debug("immediate parent: %w", immediateparent);
}
</handler>
<text>one</text>
</basetabpane>
<basetabpane>
<text>two</text>
</basetabpane>
<basetabpane>
<text height="${parent.height}" width="${parent.width}"
bgcolor="green">
three
</text>
</basetabpane>
</basetabs>
</canvas>
</example>
<p><b>See Also:</b></p>
<ul>
<li>
<sgmltag class="element" role="lz.basetab"><basetab></sgmltag>,
<sgmltag class="element" role="lz.basetabs"><basetabs></sgmltag>,
and <sgmltag class="element" role="lz.basetabscontent"><basetabscontent></sgmltag>
</li>
</ul>
</text>
</doc>
</class>
<class name="basetabscontent" extends="basecomponent" focusable="false" height="${parent._contentHeight}" width="${parent._contentWidth}" initstage="immediate">
<method name="determinePlacement" args="v,p,a">
if (v instanceof lz.basetabpane) {
// reassign parent of view to basetabs
v.parent = this.parent;
p = this.defaultplacement;
}
return super.determinePlacement( v , p, a );
</method>
<doc>
<tag name="shortdesc"><text>The non-visual container for basetabpanes.</text></tag>
<text>
<p><classname>basetabscontent</classname> is the non-visual container
(and immediate parent) of <classname>basetabpane</classname>s. Extend
<classname>basetabscontent</classname> to give the content area of
your <classname>basetabs</classname> a look, e.g., adding borders to
your content. <classname>basetabscontent</classname> is a child of the
<classname>basetabs</classname> component.</p>
<p><classname>basetabscontent</classname> should be in <classname>basetabs</classname>. If
<attribute>width</attribute> and <attribute>height</attribute> are not set,
<classname>basetabscontent</classname> will stretch to the largest content item. You can
pass the name of this class in the <attribute>contentclass</attribute> attribute of
<classname>basetabs</classname> and <classname>tabs</classname>.
<classname>basetabs</classname> uses this class as its default
<classname>contentclass</classname></p>
<example title="custom basetabscontent with borders">
<canvas height="75">
<include href="base"/>
<class name="aquatab" extends="basetab"
width="40" height="20" bgcolor="aqua">
<text>tab</text>
</class>
<class name="borderedtabscontent" extends="basetabscontent"
bgcolor="black">
<attribute name="defaultplacement" value="borderedcontent"
type="string" />
<view name="borderedcontent"
x="${parent.parent.bordersize}"
y="${parent.parent.bordersize}"
width="${parent.width-(2*parent.parent.bordersize)}"
height="${parent.height-(2*parent.parent.bordersize)}"
clip="true" bgcolor="red"/>
</class>
<basetabs x="10" y="10" bordersize="3"
tabclass="aquatab"
contentclass="borderedtabscontent">
<basetabpane>
<text>one</text>
</basetabpane>
<basetabpane>
<text>two</text>
</basetabpane>
<basetabpane>
<text>three</text>
</basetabpane>
</basetabs>
</canvas>
</example>
<p><b>See Also:</b></p>
<ul>
<li>
<sgmltag class="element" role="../reference/lz.basetabpane"><basetabpane></sgmltag>
and <sgmltag class="element" role="lz.basetabs"><basetabs></sgmltag>
</li>
</ul>
</text>
</doc>
</class>
<class name="basetabs" extends="basecomponent" focusable="false">
<attribute name="bordersize" value="0
" type="number"/>
<attribute name="inset_top" value="8
" type="number" when="immediately"/>
<attribute name="inset_left" value="8
" type="number"/>
<attribute name="inset_bottom" value="8
" type="number"/>
<attribute name="inset_right" value="8
" type="number"/>
<attribute name="tabclass" value="basetab
" type="string"/>
<attribute name="bar" value="null
" type="expression"/>
<attribute name="barclass" value="basetabsbar
" type="string"/>
<attribute name="content" value="null
" type="expression"/>
<attribute name="contentclass" value="basetabscontent
" type="string"/>
<attribute name="layout" value="class: simplelayout; axis: y; spacing: 0
"/>
<attribute name="_contentHeight" value="0
" type="number"/>
<attribute name="_contentWidth" value="${Math.max(_maxTabpaneContentWidth, this.bar.width)}" type="number"/>
<attribute name="_maxTabpaneContentWidth" value="0
" type="number"/>
<handler name="onconstruct">
// Create tabsbar
if (this['barclass'] && lz[this.barclass]) {
this.bar = new lz[this.barclass](this, {}, null, true);
} else if ($debug) {
Debug.error('%s.%s invalid: %s', this, 'barclass', this['barclass']);
}
if (this.bar == null) {
this.bar = new lz.basetabsbar(this, {}, null, true);
if ($debug) {
Debug.warn('%s.barclass defaulted to %s', this, this.bar.constructor.tagname);
}
}
// Create tabscontent, if it hasn't been already created
if (this['contentclass'] && lz[this.contentclass]) {
this.content = new lz[this.contentclass](this, {}, null, true);
} else if ($debug) {
Debug.error('%s.%s invalid: %s', this, 'contentclass', this['contentclass']);
}
if (this.content == null) {
this.content = new lz.basetabscontent(this, {}, null, true);
if ($debug) {
Debug.warn('%s.contentclass defaulted to %s', this, this.content.constructor.tagname);
}
}
</handler>
<method name="determinePlacement" args="v,p,args">
// Special flag passed on from basetabpane to tell tabs to place it
// in contents defaultplacement. Subclasses should *not* override
// this value.
if (p == "_tabscontent") {
// only create new tabs if there's a place to put them
var tc = args['tabclass'] || v['tabclass'] || this.tabclass;
if (tc && lz[tc]) {
v.tab = new lz[tc](this.bar, { tabpane: v }, null, true);
} else if ($debug) {
Debug.error("%s.tabclass invalid: %s", this, tc);
}
return this.content.determinePlacement(v, p, args);
}
return super.determinePlacement(v,p,args);
</method>
<method name="_calculateContentWidth" args="w,il,ir,b">
// only deal with this if we don't have a set height
if (!hassetwidth) {
// this can happen if child view inits before inset values
// are set in basetabs.
if (il == null) il = this.inset_left;
if (ir == null) ir = this.inset_right;
if (b == null ) b = this.bordersize;
var checkW = w+(2*b)+il+ir;
if (checkW > this._maxTabpaneContentWidth) {
setAttribute("_maxTabpaneContentWidth", checkW);
}
} else if (this.width != _contentWidth) {
setAttribute("_maxTabpaneContentWidth", this.width);
setAttribute("_contentWidth", this.width);
}
</method>
<method name="_calculateContentHeight" args="h,it,ib,b">
// only deal with this if we don't have a set height
if (!hassetheight) {
// this can happen if child view can inits before inset values
// are set in basetabs.
if (it == null) it = this.inset_top;
if (ib == null) ib = this.inset_bottom;
if (b == null ) b = this.bordersize;
var checkH = h+(2*b)+it+ib;
if (checkH > this._contentHeight) {
setAttribute("_contentHeight", checkH);
}
} else {
var myh = this.height - this.bar.height;
if (myh != this._contentHeight) {
setAttribute("_contentHeight", myh);
}
}
</method>
<doc>
<tag name="shortdesc"><text>A non-visual container for basetabpanes.</text></tag>
<text>
<p>The <classname>basetabs</classname> component conceptually houses a
row of tabs that control the visibility of a stack of
<classname>basetabpane</classname>s. When a
<classname>basetabpane</classname> is added to the
<classname>basetab</classname> component, a corresponding
<classname>basetab</classname> is created for that
<classname>basetabpane</classname>.</p>
<p>Due to the non-visual nature of <classname>basetab</classname>, a
visible subclass of <classname>basetab</classname> is used in the next
example to demonstrate the correlation between
<classname>basetabpane</classname> and <classname>basetab</classname>
items.</p>
<example title="introduction to basetabs">
<canvas height="75">
<include href="base"/>
<class name="aquatab" extends="basetab" width="40" height="20"
bgcolor="aqua">
<text>tab</text>
</class>
<basetabs x="10" y="10" tabclass="aquatab">
<basetabpane>
<text>one</text>
</basetabpane>
<basetabpane>
<text>two</text>
</basetabpane>
<basetabpane>
<text>three</text>
</basetabpane>
</basetabs>
</canvas>
</example>
<p>As can be seen, three <classname>basetab</classname> items have
been created for the three <tagname>basetabpane</tagname>s
declared.</p>
<p>Though the <tagname>basetabpane</tagname> <em>element</em> is the
child of a <tagname>basetab</tagname> element, in the runtime view
hierarchy instances of <classname>basetabs</classname> are the
immediate parents of a <classname>basetabsbar</classname> (where
<classname>basetab</classname> components are placed and laid out) and
<classname>basetabscontent</classname> (where the
<classname>basetabpane</classname> components are placed and laid
out).</p>
<p>The following diagram illustrates the correlation between
<classname>basetabs</classname>, <classname>basetabsbar</classname>
(the container for <classname>basetab</classname> items), and
<classname>basetabscontent</classname> (the container for
<classname>basetabpane</classname>s).</p>
<img src="images/basetabs/tabs-diagram.gif" width="416" height="120"/>
<p>You can change the look and feel of your basetabs by using a
different <classname>basetab</classname>,
<classname>basetabsbar</classname>, and/or
<classname>basetabscontent</classname>.</p>
<example title="modifying the basetabs look">
<canvas height="75">
<include href="base"/>
<class name="mytab" extends="basetab" width="40" height="20" bgcolor="lime">
<text>mytab</text>
</class>
<class name="mytabsbar" extends="basetabsbar" bgcolor="navy"
layout="class: simplelayout; axis: x; spacing: 5"/>
<class name="mytabscontent" extends="basetabscontent" bgcolor="silver"/>
<basetabs x="10" y="10" tabclass="mytab" barclass="mytabsbar"
contentclass="mytabscontent">
<basetabpane>
<text>one</text>
</basetabpane>
<basetabpane>
<text>two</text>
</basetabpane>
<basetabpane>
<text>three</text>
</basetabpane>
</basetabs>
</canvas>
</example>
<p><b>See Also:</b></p>
<ul>
<li>
<sgmltag class="element" role="lz.basetab"><basetab></sgmltag>,
<sgmltag class="element" role="lz.basetabsbar"><basetabsbar></sgmltag>,
<sgmltag class="element" role="lz.basetabpane"><basetabpane></sgmltag>,
and <sgmltag class="element" role="lz.basetabscontent"><basetabscontent></sgmltag>
</li>
</ul>
</text>
</doc>
</class>
</library>
Cross References
Includes
Classes