pivotlayout.lzx

<!--
* X_LZ_COPYRIGHT_BEGIN ****************************************************
* Copyright 2001-2008 Laszlo Systems, Inc.  All Rights Reserved.          *
* Use is subject to license terms.                                        *
* X_LZ_COPYRIGHT_END ******************************************************-->
<library>

    <!-- this layout is a more general layout the extend the abilites of wrapping layout.
         It is meant to transition bwteen a wrapping alyout into a single row and allow
         for scrolling or paging aspect by aniamting only a few parameters.
         
         first there is pivotx and pivoty point that the layout keeps invariant
         during transitions. 
    -->
    <class name="pivotlayout" extends="layout">
    
        <!--- The primary axis for wrapping. -->
        <attribute name="axis" value="x" setter="this.setAxis( axis )" type="string"/>
                   
        <!--- A pixel amount to use between the views controlled by the layout in
              both axes. By default, both xspacing and yspacing are set to this
              value. -->
        <attribute name="spacing" value="1"/>
        
        <!--- A pixel amount to offset the first view controlled by the layout in x
              axis -->
        <attribute name="xinset" value="0"/>

        <attribute name="dimmer" value="1"/> <!-- the opacity value assigned to each item  -->
        
        <!--- A pixel amount to offset the first view controlled by the layout in y
              axis -->
        <attribute name="yinset" value="0"/>
        
        <!--- A pixel amount to use between the views controlled by the layout in
              the x axis. -->
        <attribute name="xspacing" value="${this.spacing}"/>
        
        <!--- A pixel amount to use between the views controlled by the layout in
              the y axis. -->
        <attribute name="yspacing" value="${this.spacing}"/>
        
        <!-- This layout actually determines the width and height of a view. 
             There are two paramteres that affect this: The start dimension of a photo,
             and the amount that basic dimension is scaled scaled -->
        <attribute name="photoscale" value="1"/>
        <attribute name="photodimension" value="70"/>
        
        <!-- the index of the row that will have a stationary position -->
        <attribute name="skewindex" value="1"/>

        <attribute name="isgrid" value="true"/>
        <attribute name="textvisible" value="true"/>
        
        
         <!-- this multiplier that 'skews' that start point of a row from it's natural position within a basic grid layout -->
        <attribute name="skew" value="0"/>       
        <attribute name="pivotrow" value="0"/>
        
        <!-- the index of the view that will be rendered at  pivot_x,pivot_y.
             everything else will be rendered in reference to that. -->
        <attribute name="pivotindex" value="0"/>
        <attribute name="pivot_x" value="0"/> <!-- immediateparent.coordinates -->
        <attribute name="pivot_y" value="0"/> <!-- immediateparent.coordinates -->
        <attribute name="startpivot_x" value="50"/> <!-- immediateparent.coordinates -->
        <attribute name="startpivot_y" value="50"/> <!-- immediateparent.coordinates -->

        <attribute name="pagebegin" value="-1"/> 
        <attribute name="pageend" value="-1"/>
        <attribute name="pagesize" value="0"/>
        <attribute name="totalitems" value="0"/>
        
        <attribute name="currentpage" value="1"/>
        <attribute name="perpage" value="0"/>
        <attribute name="totalpages" value="0"/>
        
        <attribute name="duration" value="0"/>
        <attribute name="calcpageparams" value="true"/>
        
        <!--- @keywords private -->
        <method name="setAxis" args="a">
            <!-- CURRENTLY THIS METHOD IS NOT USED -->
            this.axis = a;
            this.otherAxis =  a == "x" ? "y" : "x"
            this.sizeAxis = a == "x" ? "width" : "height"
            this.otherSizeAxis = a == "x" ? "height" : "width"
        </method>

        <!--- @keywords private -->
        <handler name="oninit">
            this.regUpdateDelegate();
        </handler>

        <method name="regUpdateDelegate">
            //declare the attributes that will cause this layout to auto-update when they change
            this.updateDelegate.register( this.immediateparent,  'onwidth');
            this.updateDelegate.register( this,  'onphotodimension');
            this.updateDelegate.register( this,  'onphotoscale');
            this.updateDelegate.register( this,  'onyspacing');
            this.updateDelegate.register( this,  'onxspacing');
            this.updateDelegate.register( this,  'onpivotindex');
            this.updateDelegate.register( this,  'onskew');
            this.updateDelegate.register( this,  'onpivot_x');
            this.updateDelegate.register( this,  'onpivot_y');
            this.updateDelegate.register( this,  'ondimmer');
         </method>
        
        <!--- This method is usually called automatically when any of the views
              controlled by the layout change their size , or when the size of
              layout's immediateparent changes. However it can be called directly
              to force the layout to update.
              
              Each row has it own left and right limit determined by the width of the parent, the skew, and xinset.
              
              the pivot_x, and pivot_y acts like the "center" of the layout 
              and the xinset. -->
              
       <attribute name="coidel" value="$once{ new LzDelegate( this, 'update' ) }"/>
       <method name="updateOnIdle" args="ison">
            if ( ison ){
                this.updateDelegate.unregisterAll();
                coidel.register( lz.Idle, "onidle" );
            } else {
                this.regUpdateDelegate();
                coidel.unregisterAll();
            }
       </method>
       
       <method name="update" args="ignore=null">
            
            if ( this.locked ) return;  
            this.locked = true;
            //Debug.write(" PIVOTLAYOUT UPDATE ------------------------------------------ ",this.subviews.length);

            
            // first find the limitwidth that will be common for all rows
            var limitwidth = this.immediateparent.width; // - xspacing;
            
            // calc the width of an item based on photodimension and photoscale
            var itemwidth = Math.round( photodimension*photoscale );
            
            // Now find out how many items can be rendered in a row;
            var numinrow = Math.floor( limitwidth/( itemwidth + this.xspacing ) );
            limitwidth = numinrow*( itemwidth + this.xspacing );
            
            var limitheight = this.immediateparent.height;
            var numincol = Math.floor(limitheight/(itemwidth + this.xspacing));
            
            // Set the total number of items possible on the page.
            this.setAttribute('totalitems', numinrow * numincol);
            
            // now we can calculate the row of the view that is subviews[pivotindex]
            var pivotrow = Math.floor( this.pivotindex/ numinrow );
            
            // from this we can now calc the left limit of the pivotrow
            var pLeftlimit = this.pivot_x - ( this.pivotindex%numinrow)*( itemwidth + this.xspacing);
            
            //now calc limit values for the first row
            var rowindex =  0;
            var leftlimit  = (rowindex - pivotrow)*this.skew*limitwidth + pLeftlimit;
            var rightlimit = leftlimit + limitwidth;
                
            // calc the xposition of the first element of that row at its left limit
            var xpos = leftlimit;
            
            // calc the ypos of the current row
            var ypos = this.pivot_y - (itemwidth + this.yspacing)*pivotrow + this.yinset;
                
            // variables to determine the actual indicies of the views that are being shown ( not clipped by immediateparent) 
            var vis_index_start = -1;
            var vis_index_end = -1;

            // now loop through all the subviews and poistion them accordingly
            var l = this.subviews.length;
            
            for(var i=0; i < l; i++) {
                // get the i-th subview
                var s = this.subviews[i]; s.index = i;
                if (!s.intparent) continue;


                //if ( i == pivotindex ) s.setAttribute('bgcolor', 0x00FF00)
                //else s.setAttribute('bgcolor', 0xFF0000);
                              
                if ( i != pivotindex) {
                     s.intparent.setAttribute('opacity', dimmer );
                }

                
                // set the position of that subview
                s.setAttribute('x', xpos );
                s.setAttribute('y', ypos );
                if (s.txt) {
                   s.txt.setAttribute('visible',  this.textvisible );
                }
                
                // set width and height of that subview
                s.setAttribute('width', itemwidth );
                s.setAttribute('height', itemwidth );
                
                // set visibile to true or false 
                // if item is being clipped by immediateparent view

                if ( calcpageparams ) {
                    var notinside =  (s.x + s.width) < 0; // is view off to the left
                    notinside = notinside || (s.x > this.immediateparent.width ); //is view off to the right
                    notinside = notinside || ((s.y + s.height ) > this.immediateparent.height ); //is view off the bottom
                    
                    if ( notinside ) {
                        s.setAttribute('visible',  false );
                        if ( vis_index_end == -1 && vis_index_start > -1 ) vis_index_end = i - 1;
                    } else {
                        s.setAttribute('visible',  true );
                        if ( vis_index_start == -1 ) vis_index_start = i;
                    }
                }
                
                // adjust position parameters to set the next subview
                xpos += itemwidth;
                
                if ( i < (l - 1) ) {
                    xpos += xspacing; //add spacing between last subview
                                        
                    if (( xpos > rightlimit) || ( xpos + itemwidth > rightlimit)) {
                    
                        // need to wrap subviews to a new row so incrememnt the rowindex and repeat                        
                        rowindex += 1;
                        leftlimit  = (rowindex - pivotrow)*skew*limitwidth + pLeftlimit;
                        rightlimit = leftlimit + limitwidth;
                        xpos = leftlimit;
                        ypos += itemwidth + yspacing;
                    }
                }
            }
            if ( calcpageparams ) { 
                // looping throughthe views is done so update page params if allowed
                if ( vis_index_end == -1 ) vis_index_end = l - 1;
                this.setAttribute("pagebegin", vis_index_start);
                this.setAttribute("pageend",   vis_index_end);
                this.setAttribute("pagesize",  vis_index_end - vis_index_start + 1);
                //Debug.write("range: ---------------",pagebegin,pageend,pagesize);
            }
            this.locked = false;
            
        </method>
        <!--- @keywords private -->
        <method name="toString">
             return 'pivotlayout for ' + this.immediateparent;
        </method>

        <method name="pivotAround" args="s">
            //set the pivotrow and pivot_y values based on a specific view
            this.pivotview = s;
            this.pivotindex = s.index;
            
            /*

            var limitwidth = this.immediateparent.width; 
            var itemwidth  = Math.floor( photodimension*photoscale );
            var numinrow   = Math.floor( limitwidth/(itemwidth + xspacing ) );            
            var numincol   = Math.floor( this.immediateparent.height/((itemwidth + yspacing )));
            var numperpage = numinrow*numincol;
            
            // s.index%numperpage = nth item on a page
            // Math.floor( (s.index%numperpage)/numinrow) = kth row on page
            
            var ytest =  yinset + Math.floor( (s.index%numperpage )/numinrow) * (itemwidth + yspacing );
            
            // s.index%numperpage = nth item on a page
            // Math.floor(  (s.index%numperpage)%numinrow ) = nth col on page
            
            var xtest =  this.startpivot_x + ((s.index%numperpage)%numinrow)*(itemwidth + xspacing);

            
            this.pivot_y = ytest - yinset;
            this.pivot_x = xtest - xinset;
            */
            this.pivot_y = s.y - yinset;
            this.pivot_x = s.x - xinset;
            
            //s.gridy = s.y;
            //s.gridx = s.x;
           
            //Debug.write("pivotAround: v.row,v.y",v.row,v.y);
         </method>

        <method name="dim" args="o"> 
            var l = this.subviews.length;
            for(var i=0; i < l; i++) {
                var s;
                if ( i != pivotindex ) {
                    s = this.subviews[i];
                    s.intparent.setAttribute('opacity', o);
                }
            }
         </method>
        
                     

    </class>

</library>

Cross References

Classes