basefloatinglist.lzx
<library>
<include href="lz/list.lzx"/>
<class name="basefloatinglist" extends="list" options="ignorelayout" x="-1000" y="-1000">
<attribute name="owner"/>
<attribute name="wouldbename" value="
" type="string"/>
<attribute name="attach" value="bottom
" type="string"/>
<attribute name="_currentattachy" value="bottom
" type="string"/>
<attribute name="_currentattachx" value="bottom
" type="string"/>
<attribute name="attachtarget" value="null
" setter="this.setAttachTarget(attachtarget)"/>
<attribute name="attachoffset" value="0
"/>
<attribute name="_updateAttachPosDel" value="null
"/>
<attribute name="_origshownitems" value="-2
" type="number"/>
<attribute name="style" value="${attachtarget ? attachtarget['style'] : null}"/>
<method name="setAttachTarget" args="target">
this.attachtarget = target;
if (visible) updateAttachLocation();
// track attachtarget position
var del = this._updateAttachPosDel;
if (del) {
del.unregisterAll();
if (target) {
for (var p = target; p !== canvas; p = p.immediateparent) {
del.register(p, 'onx');
del.register(p, 'ony');
}
}
}
</method>
<method name="construct" args="parent,args">
// keep a reference to its lexical parent
this.owner = parent;
if ( typeof( args.name ) != 'undefined' ) {
var flname = args.name; // save value of name arg
args.name = null; // zero name field. doing 'delete args.name'
// doesn't work in this case.
this.wouldbename = flname;
this.owner[flname] = this; // have parent point to me via owner attribute
}
// construct this view on the canvas width a default name
super.construct(canvas,args);
// reset _parentcomponent
var p = this.owner;
while (p !== canvas) {
if (p instanceof lz.basecomponent) {
this._parentcomponent = p;
break;
}
p = p.immediateparent;
}
// Since we are constructed on the canvas, we need to
// listen for out parent being destroyed and clean up after
// ourselves.
new lz.Delegate(this, '_handledestroy', parent, 'ondestroy');
this._updateAttachPosDel = new lz.Delegate(this, '_doUpdateAttachLocation');
</method>
<method name="_handledestroy" args="ignore">
this.destroy();
</method>
<method name="destroy">
// make sure owner and attachTarget don't know about us.
if (this.owner != null) {
if (this['wouldbename'] != null) {
this.owner[this.wouldbename] = null;
}
this.owner = null;
}
this.setAttachTarget(null);
this._updateAttachPosDel = null;
// Have to remove ourselves from the canvas
var svs = canvas.subviews;
for (var i = svs.length -1; i >= 0; i--) {
if (svs[i] === this) {
svs.splice(i, 1);
break;
}
}
// Have to remove ourselves from the canvas
var sns = canvas.subnodes;
for (var i = sns.length -1; i >= 0; i--) {
if (sns[i] === this) {
sns.splice(i, 1);
break;
}
}
super.destroy();
</method>
<method name="init">
super.init();
if (this.attachtarget==null) {
this.setAttachTarget( owner );
}
</method>
<method name="getMenuCapHeight">
return 0;
</method>
<attribute name="_constraintsApplied" value="false
"/>
<method name="_setScroll">
var sv0 = this.interior.content.subviews[0];
var svHeight = sv0 ? sv0.height : 20;
var own_y = attachtarget.getAttributeRelative('y',canvas);
var menuCapHeight = getMenuCapHeight();
// check which position allows for more items to be displayed
var maxHeightFromTop = own_y;
var maxHeightFromBottom = canvas.height -
(own_y + attachtarget.height + attachoffset + menuCapHeight);
var startY = 0;
var myAttach = "top";
var maxHeight = maxHeightFromTop;
if (maxHeightFromBottom > maxHeightFromTop) {
myAttach = "bottom";
startY = own_y + attachtarget.height + attachoffset;
maxHeight = maxHeightFromBottom;
}
// calculate number of items and height of list to display
var items = Math.floor((maxHeight + spacing - menuCapHeight) / (svHeight + spacing));
var myHeight = items * (svHeight + spacing ) - spacing + menuCapHeight;
// recalculate height if default attachment is left or right
if (attach == "left" || attach == "right") {
myHeight += svHeight + spacing;
items++;
if (myAttach == "bottom") {
startY -= attachtarget.height + attachoffset;
} else {
startY += attachtarget.height;
}
}
// calculate exact position to place list if we are to attach on top
if (myAttach == "top") {
startY += maxHeightFromTop - myHeight + menuCapHeight;
}
this.setAttribute('y', startY);
this.setAttribute('_currentattachy', myAttach);
this._keepshownitems = true;
this._setShownItems(items);
this._keepshownitems = false;
this.setAttribute('scrollable', true);
</method>
<attribute name="_keepshownitems" value="false
" type="boolean"/>
<method name="_setShownItems" args="n">
super._setShownItems(n);
if (this._origshownitems == -2 || ! this._keepshownitems ) {
this._origshownitems = n;
}
</method>
<method name="_doUpdateAttachLocation" args="ignore">
this.updateAttachLocation();
</method>
<method name="updateAttachLocation">
// Adjust x and y relative to the attachtarget as appropriate to adhere to the requested attach location.
// In the event that the floatinglist intersects the canvas bounds, the attach location
// will be temporarily changed in order to be fully visible.
if ( !isinited ) return;
if (!attachtarget) return;
// Adjust X
var doneonce = false;
var validattach = attach;
var own_x = attachtarget.getAttributeRelative('x',canvas);
while (true) {
if (validattach=="bottom" || validattach=="top") {
var tmp_x = own_x;
if (tmp_x < 0) {
//if our left edge isn't visible move list to left edge
tmp_x = 0;
} else if (tmp_x + attachtarget.width > canvas.width) {
tmp_x = canvas.width - this.width;
} else if (tmp_x + this.width > canvas.width) {
tmp_x = (own_x + attachtarget.width) - this.width;
}
this.setAttribute( 'x' , tmp_x );
break;
} else
if (validattach=="left") {
var tmp_x = own_x - this.width;
if (tmp_x > 0) {
this.setAttribute( 'x' , tmp_x );
this.setAttribute( '_currentattachx', 'left' );
break;
} else {
validattach = "right";
}
}
if (validattach=="right") {
var tmp_x = own_x + attachtarget.width; // why -1?
if (tmp_x + this.width < canvas.width ) {
this.setAttribute( 'x' , tmp_x );
this.setAttribute( '_currentattachx', 'right' );
break;
} else {
// avoid infinite loop
if (! doneonce) {
validattach = "left";
doneonce = true;
} else {
// default right
break;
}
}
}
}
// Adjust Y
this._keepshownitems = true;
this._setShownItems(this._origshownitems);
this._keepshownitems = false;
this.setAttribute('scrollable', false);
var doneonce = false;
var validattach = attach;
//NOTE: we are using calcMyHeight() here because height is returning the wrong value
var menuCapHeight = getMenuCapHeight();
var myHeight = this.calcMyHeight() + menuCapHeight;
var own_y = attachtarget.getAttributeRelative('y',canvas);
while (true) {
if ( validattach=="left" || validattach=="right" ) {
if ( own_y + myHeight < canvas.height ) {
this.setAttribute( 'y' , own_y );
this.setAttribute( '_currentattachy', 'bottom' );
break;
} else {
validattach = "top";
}
} else
if ( validattach=="bottom" ) {
var tmp_y = own_y + attachtarget.height + attachoffset;
if ( tmp_y + myHeight < canvas.height ) {
this.setAttribute( 'y' , tmp_y );
this.setAttribute( '_currentattachy', 'bottom' );
break;
} else {
validattach = "top";
}
}
if ( validattach=="top" ) {
var tmp_y = own_y - myHeight;
// if the orignal attempt was to attach it to left or right
// add back the height of the attachtarget so that the bottom
// of the floatinglist is flush with the bottom of the attachtarget
if ( attach == 'right' || attach == 'left' )tmp_y += attachtarget.height;
if ( tmp_y > 0 ) {
this.setAttribute( 'y' , tmp_y + menuCapHeight);
this.setAttribute( '_currentattachy', 'top' );
break;
} else {
// avoid infinite loop
if (! doneonce) {
validattach = "bottom";
doneonce = true;
} else {
this._setScroll();
break;
}
}
}
}
</method>
<handler name="onmousedown">
this.owner.onmousedown.sendEvent();
this.bringToFront();
</handler>
<handler name="onwidth">
this.updateAttachLocation();
</handler>
<handler name="onheight">
this.updateAttachLocation();
</handler>
<handler name="onvisible" args="v">
if (v) {
updateAttachLocation();
} else {
this.setAttribute('x', -1000);
this.setAttribute('y', -1000);
}
</handler>
<method name="toString">
return "floatinglist: wouldbename,owner = " + this.wouldbename + "," + this.owner;
</method>
<method name="getNextSelection">
var next = owner.getNextSelection();
return next;
</method>
<method name="getPrevSelection">
// A floating list should not know the structure of its owner
// and because it is not part of the owner's view hiearchy
// the owner should determine what 'shift tab' does here
return owner.resolveSelection();
</method>
<handler name="onblur">
if ( this.owner != "undefined" && this.owner['onblur']) {
this.owner.onblur.sendEvent();
}
</handler>
<doc>
<tag name="shortdesc"><text>
base class for floatinglist
</text></tag>
<text>
<p>
This is a base component with no visible elements that implements
the functionality of a floating list. See <xref linkend="lz.floatinglist"/>
and <xref linkend="lz.plainfloatinglist"/> for concrete extensions of this
base class.
</p>
</text>
</doc>
</class>
</library>
Cross References
Includes
Classes