Revision: 3545
          http://vexi.svn.sourceforge.net/vexi/?rev=3545&view=rev
Author:   clrg
Date:     2009-07-10 15:48:51 +0000 (Fri, 10 Jul 2009)

Log Message:
-----------
Fix core layout failures

Modified Paths:
--------------
    trunk/core/org.vexi.core/src/org/vexi/core/Box.jpp
    trunk/core/org.vexi.core/src/org/vexi/core/Vexi.jpp
    trunk/core/org.vexi.core/src_junit/test/core/box/layout/TestLayout.java

Added Paths:
-----------
    trunk/core/org.vexi.core/src_junit/test/core/box/layout/layout2.t

Modified: trunk/core/org.vexi.core/src/org/vexi/core/Box.jpp
===================================================================
--- trunk/core/org.vexi.core/src/org/vexi/core/Box.jpp  2009-07-08 16:20:10 UTC 
(rev 3544)
+++ trunk/core/org.vexi.core/src/org/vexi/core/Box.jpp  2009-07-10 15:48:51 UTC 
(rev 3545)
@@ -226,6 +226,8 @@
     //private Affine transform = null;
     //private VectorGraphics.RasterPath rpath = null;
     //private Affine rtransform = null;
+    
+    protected static int MAX_DIMENSION = Short.MAX_VALUE;
 
     // specified directly by user
     protected int minwidth = 0;
@@ -529,74 +531,81 @@
                     
                     // take into account slack - slightly more processing
                     } else {
-                        // educated guess at the slack to hand out
-                        float targetwidth = width / treeSize();
+                        // our layout lies somewhere between the min and max 
size,
+                        // loop over the children attempting to set their 
width to
+                        // targetsize and adjust until it meets the parent 
width
+                        float targetsize = (float)width/(float)treeSize();
                         float total = 0;
-                        int maxsizetotal = 0;
-                        int minsizetotal = 0;
                         int numactive = 0;
-                        int expandnum = 0;
-                        int reducenum = 0;
-                        int loop = 0;
-                        // loop over the children and establish whether each 
child accomodates
-                        // the current targetwidth or not, and adjust 
targetwidth until either
-                        // we reach an impasse or applying targetwidth meets 
the parent width 
-                        do {
+                        for (int j=0; j<100; j++) {
+                            int min_minsize = MAX_DIMENSION;
+                            int max_maxsize = 0;
+                            int numflexible = 0;
+                            int num_minsize = 0;
+                            int num_maxsize = 0;
+                            int sum_maxsize = 0;
+                            int num_passive = 0;
                             for (Box child = getChild(i=0); child != null; 
child = getChild(++i)) {
                                 if (!child.test(DISPLAY)) continue;
                                 numactive++;
-                                // can't change size
-                                if (child.test(HSHRINK) || 
(child.contentwidth==child.maxwidth)) {
-                                    maxsizetotal += child.contentwidth;
-                                    minsizetotal += child.contentwidth;
+                                if (child.test(HSHRINK) || 
child.maxwidth==child.contentwidth) {
+                                    num_passive++;
                                     total += child.contentwidth;
-                                // child minsize bigger than targetsize
-                                } else if (child.contentwidth > targetwidth) {
-                                    minsizetotal += child.contentwidth;
-                                    total += child.contentwidth;
-                                    expandnum ++;
-                                // child maxsize smaller than targetsize
-                                } else if (targetwidth > child.maxwidth) {
-                                    maxsizetotal += child.maxwidth;
-                                    total += child.maxwidth;
-                                    reducenum ++;
-                                // good for this child
+                                } else if (child.contentwidth>targetsize) {
+                                    min_minsize = min(min_minsize, 
child.contentwidth);
+                                    num_minsize++;
+                                    total += (float)child.contentwidth;
+                                } else if (child.maxwidth<targetsize) {
+                                    max_maxsize = max(max_maxsize, 
child.maxwidth);
+                                    num_maxsize++;
+                                    sum_maxsize += 
child.maxwidth-child.contentwidth;
+                                    total += (float)child.maxwidth;
                                 } else {
-                                    total += targetwidth;
-                                    expandnum ++;
-                                    reducenum ++;
+                                    numflexible++;
+                                    total += targetsize;
                                 }
                             }
-                            // no relevant children
+                            // no active children - nothing to do
                             if (numactive==0) return;
-                            // fall-short - increase targetwidth
-                            if (width>(int)(total+0.5)) {
-                                if (expandnum==0) break;
-                                targetwidth = 
(float)(width-maxsizetotal)/expandnum;
-                            // over-shoot - decrease targetwidth
-                            } else if (width<(int)(total+0.5)) {
-                                if (reducenum==0) break;
-                                targetwidth = 
(float)(width-minsizetotal)/reducenum;
-                            // no option
+                            // no solution required - reset total and break out
+                            if (num_maxsize+num_passive==numactive && 
sum_maxsize<=width) {
+                                targetsize = width;
+                                total = 0;
+                                break;
+                            }
+                            // test to see if targetsize produces a solution 
that rounds
+                            // to match the width, adjusting appropriatly if 
it does not
+                            int itotal = (int)(total+0.5);
+                            if (itotal > width) {
+                                if (numflexible>0) {
+                                    targetsize -= 
(total-(float)width)/(float)numflexible;
+                                } else {
+                                    targetsize = (float)max_maxsize - 
(total-(float)width)/(float)num_maxsize;
+                                }
+                            } else if (itotal < width) {
+                                if (numflexible>0) {
+                                    targetsize += 
((float)width-total)/(float)numflexible;
+                                } else {
+                                    targetsize = (float)min_minsize + 
((float)width-total)/(float)num_minsize;
+                                }
                             } else break;
-                            // reset everything and try again with new 
targetwidth
-                            
maxsizetotal=minsizetotal=numactive=expandnum=reducenum=0;
-                            total=0; // total is a double, the others are int
-                            // protection against infinite loops
-                            loop++;
-                            if (loop>99) {
+                            // reset total and try again
+                            total = 0;
+                            // give a helpful report if no solution is found
+                            if (j>=99) {
                                 Log.error(this, "Core layout failure, please 
report:");
                                 Log.error(this, "Parent width: "+width);
                                 for (Box child = getChild(i=0); child != null; 
child = getChild(++i)) {
                                     if (!child.test(DISPLAY)) continue;
                                     Log.error(this, "Child "+i+": 
"+child.contentwidth+", "+child.maxwidth+", "+child.test(HSHRINK));
                                 }
-                                break;
                             }
-                        } while (true);
+                        }
 
-                        // now we know how we are assigning slack, second pass 
handles layout
-                        float slack_x = (expandnum==0) ? 
(float)(width-maxsizetotal)/numactive : 0;
+                        // REMARK: slack only needed if total was reset to 0
+                        float slack_x = (total==0) ? 
(float)(width-contentwidth)/numactive : 0;
+                        // we use total (a float) to keep tabs on final layout 
totals
+                        // so we do not get stray pixel sized gaps caused by 
rounding
                         total = 0;
                         for (Box child = getChild(i=0); child != null; child = 
getChild(++i)) {
                             if (!child.test(DISPLAY)) continue;
@@ -604,18 +613,18 @@
                             child_height = child.test(VSHRINK) ? 
child.contentheight : min(child.maxheight, height);
                             child_y = top ? 0 : (bottom ? height-child_height 
: (height-child_height)/2);
                             // width, x
-                            if (slack_x > 0.0 || child.test(HSHRINK) || 
targetwidth > child.maxwidth) {
+                            if (child.test(HSHRINK) || targetsize > 
child.maxwidth || slack_x > 0.0) {
                                 child_x = (int)(total+0.5+(left ? 0 : (right ? 
slack_x : slack_x/2)));
                                 child_width = child.test(HSHRINK) ? 
child.contentwidth : child.maxwidth;
-                                total += slack_x + child_width;
+                                total += slack_x + (float)child_width;
                             } else {
                                 child_x = (int)(total+0.5);
-                                if (child.contentwidth > targetwidth) {
+                                if (child.contentwidth > targetsize) {
                                     child_width = child.contentwidth;
                                     total += child_width;
                                 } else {
-                                    child_width = 
(int)(total+targetwidth+0.5)-(int)(total+0.5);
-                                    total += targetwidth;
+                                    child_width = 
(int)(total+targetsize+0.5)-(int)(total+0.5);
+                                    total += targetsize;
                                 }
                             }
                             child.tryMoveAndResize(child_x, child_y, 
child_width, child_height, clean);
@@ -638,74 +647,81 @@
 
                     // take into account slack - slightly more processing
                     } else {
-                        // educated guess at the slack to hand out
-                        float targetheight = height / treeSize();
+                        // our layout lies somewhere between the min and max 
size,
+                        // loop over the children attempting to set their 
width to
+                        // targetsize and adjust until it meets the parent 
width
+                        float targetsize = (float)height/(float)treeSize();
                         float total = 0;
-                        int maxsizetotal = 0;
-                        int minsizetotal = 0;
                         int numactive = 0;
-                        int expandnum = 0;
-                        int reducenum = 0;
-                        int loop = 0;
-                        // loop over the children and establish whether each 
child accomodates
-                        // the current targetheight or not, and adjust 
targetheight until either
-                        // we reach an impasse or applying targetheight meets 
the parent width 
-                        do {
+                        for (int j=0; j<100; j++) {
+                            int min_minsize = MAX_DIMENSION;
+                            int max_maxsize = 0;
+                            int numflexible = 0;
+                            int num_minsize = 0;
+                            int num_maxsize = 0;
+                            int sum_maxsize = 0;
+                            int num_passive = 0;
                             for (Box child = getChild(i=0); child != null; 
child = getChild(++i)) {
                                 if (!child.test(DISPLAY)) continue;
                                 numactive++;
-                                // can't change size
-                                if (child.test(VSHRINK) || 
(child.contentheight==child.maxheight)) {
-                                    maxsizetotal += child.contentheight;
-                                    minsizetotal += child.contentheight;
+                                if (child.test(VSHRINK) || 
child.maxheight==child.contentheight) {
+                                    num_passive++;
                                     total += child.contentheight;
-                                // child minsize bigger than targetsize
-                                } else if (child.contentheight > targetheight) 
{
-                                    minsizetotal += child.contentheight;
-                                    total += child.contentheight;
-                                    expandnum ++;
-                                // child maxsize smaller than targetsize
-                                } else if (targetheight > child.maxheight) {
-                                    maxsizetotal += child.maxheight;
-                                    total += child.maxheight;
-                                    reducenum ++;
-                                // good for this child
+                                } else if (child.contentheight>targetsize) {
+                                    min_minsize = min(min_minsize, 
child.contentheight);
+                                    num_minsize++;
+                                    total += (float)child.contentheight;
+                                } else if (child.maxheight<targetsize) {
+                                    max_maxsize = max(max_maxsize, 
child.maxheight);
+                                    num_maxsize++;
+                                    sum_maxsize += 
child.maxheight-child.contentheight;
+                                    total += (float)child.maxheight;
                                 } else {
-                                    total += targetheight;
-                                    expandnum ++;
-                                    reducenum ++;
+                                    numflexible++;
+                                    total += targetsize;
                                 }
                             }
-                            // no relevant children
+                            // no active children - nothing to do
                             if (numactive==0) return;
-                            // fall-short - increase targetheight
-                            if (height>(int)(total+0.5)) {
-                                if (expandnum==0) break;
-                                targetheight = 
(float)(height-maxsizetotal)/expandnum;
-                            // over-shoot - decrease targetheight
-                            } else if (height<(int)(total+0.5)) {
-                                if (reducenum==0) break;
-                                targetheight = 
(float)(height-minsizetotal)/reducenum;
-                            // no option
+                            // no solution required - reset total and break out
+                            if (num_maxsize+num_passive==numactive && 
sum_maxsize<=height) {
+                                targetsize = height;
+                                total = 0;
+                                break;
+                            }
+                            // test to see if targetsize produces a solution 
that rounds
+                            // to match the height, adjusting appropriatly if 
it does not
+                            int itotal = (int)(total+0.5);
+                            if (itotal > height) {
+                                if (numflexible>0) {
+                                    targetsize -= 
(total-(float)height)/(float)numflexible;
+                                } else {
+                                    targetsize = (float)max_maxsize - 
(total-(float)height)/(float)num_maxsize;
+                                }
+                            } else if (itotal < height) {
+                                if (numflexible>0) {
+                                    targetsize += 
((float)height-total)/(float)numflexible;
+                                } else {
+                                    targetsize = (float)min_minsize + 
((float)height-total)/(float)num_minsize;
+                                }
                             } else break;
-                            // reset everything and try again with new 
targetheight
-                            
maxsizetotal=minsizetotal=numactive=expandnum=reducenum=0;
-                            total=0; // total is a double, the others are int
-                            // protection against infinite loops
-                            loop++;
-                            if (loop>99) {
+                            // reset total and try again
+                            total = 0;
+                            // give a helpful report if no solution is found
+                            if (j>=99) {
                                 Log.error(this, "Core layout failure, please 
report:");
                                 Log.error(this, "Parent height: "+height);
                                 for (Box child = getChild(i=0); child != null; 
child = getChild(++i)) {
                                     if (!child.test(DISPLAY)) continue;
                                     Log.error(this, "Child "+i+": 
"+child.contentheight+", "+child.maxheight+", "+child.test(VSHRINK));
                                 }
-                                break;
                             }
-                        } while (true);
+                        }
 
-                        // now we know how we are assigning slack, second pass 
handles layout
-                        float slack_y = (expandnum==0) ? 
(float)(height-maxsizetotal)/numactive : 0;
+                        // REMARK: slack only needed if total was reset to 0
+                        float slack_y = (total==0) ? 
(float)(height-contentheight)/numactive : 0;
+                        // we use total (a float) to keep tabs on final layout 
totals
+                        // so we do not get stray pixel sized gaps caused by 
rounding
                         total = 0;
                         for (Box child = getChild(i=0); child != null; child = 
getChild(++i)) {
                             if (!child.test(DISPLAY)) continue;
@@ -713,18 +729,18 @@
                             child_width = child.test(HSHRINK) ? 
child.contentwidth : min(child.maxwidth, width);
                             child_x = left ? 0 : (right ? width-child_width : 
(width-child_width)/2);
                             // height, y
-                            if (slack_y > 0.0 || child.test(VSHRINK) || 
targetheight > child.maxheight) {
+                            if (slack_y > 0.0 || child.test(VSHRINK) || 
targetsize > child.maxheight) {
                                 child_y = (int)(total+0.5+(top ? 0 : (bottom ? 
slack_y : slack_y/2)));
                                 child_height = child.test(VSHRINK) ? 
child.contentheight : child.maxheight;
                                 total += slack_y + child_height;
                             } else {
                                 child_y = (int)(total+0.5);
-                                if (child.contentheight > targetheight) {
+                                if (child.contentheight > targetsize) {
                                     child_height = child.contentheight;
                                     total += child_height;
                                 } else {
-                                    child_height = 
(int)(total+targetheight+0.5)-(int)(total+0.5);
-                                    total += targetheight;
+                                    child_height = 
(int)(total+targetsize+0.5)-(int)(total+0.5);
+                                    total += targetsize;
                                 }
                             }
                             child.tryMoveAndResize(child_x, child_y, 
child_width, child_height, clean);
@@ -886,6 +902,7 @@
     /** set minwidth and fire traps if necessary - returns false iff 
setMaxwidth is called */
     private final boolean setMinWidth(int minwidth, boolean firetraps) throws 
JSExn {
         if (this.minwidth == minwidth) return true;
+        if (minwidth<0) throw new JSExn("Attempt to set maxwidth to a value 
lower than '"+0+"'");
         if (firetraps && test(MINWIDTH_TRAP)) {
             Trap t = wtrap(SC_minwidth);
             // FIXME: investigate this
@@ -912,13 +929,13 @@
     //#repeat Width/Height width/height WIDTH/HEIGHT
     /** set maxwidth and fire traps if necessary - returns false iff 
setMinwidth is called */
     private final boolean setMaxWidth(int maxwidth, boolean firetraps) throws 
JSExn {
-        
         if (this.maxwidth == maxwidth) return true;
+        if (maxwidth>MAX_DIMENSION) throw new JSExn("Attempt to set maxwidth 
to a value higher than '"+MAX_DIMENSION+"'");
         if (firetraps && test(MAXWIDTH_TRAP)) {
             Trap t = wtrap(SC_maxwidth);
             // FIXME: investigate this
             //if (t==null && test(MAXWIDTH_TRAP))
-            //    System.err.println("Should not happen, MINWIDTH_TRAP set and 
t==null");
+            //    System.err.println("Should not happen, MAXWIDTH_TRAP set and 
t==null");
             if (t!=null) {
                 this.maxwidth = JSU.toInt(Main.SCHEDULER.runBeforePut(t, 
JSU.N(maxwidth)));
                 Main.SCHEDULER.runAfterPut(null);
@@ -1225,21 +1242,21 @@
                 break;
             case 3:
                 //#switch(JSU.toString(method))
-                   case "sendEvent":
-                       String event;
-                       int mx, my;
-                       try {
-                           event = JSU.toString(args[0]);
-                           mx = JSU.toInt(args[1]);
-                           my = JSU.toInt(args[2]);
-                       } catch (Exception e) {
-                           throw new JSExn("Illegal arguments for sendEvent; 
usage: sendEvent(<string>, <int>, <int>)");
-                       }
-                       if (event.equals("Move")) tryPropagateMove(mx, my);
-                       else propagateEvent(args[0], JSU.S('_'+event), JSU.T, 
mx, my);
-                       return null;
-                   //#end
-                   break;
+                case "sendEvent":
+                    String event;
+                    int mx, my;
+                    try {
+                        event = JSU.toString(args[0]);
+                        mx = JSU.toInt(args[1]);
+                        my = JSU.toInt(args[2]);
+                    } catch (Exception e) {
+                        throw new JSExn("Illegal arguments for sendEvent; 
usage: sendEvent(<string>, <int>, <int>)");
+                    }
+                    if (event.equals("Move")) tryPropagateMove(mx, my);
+                    else propagateEvent(args[0], JSU.S('_'+event), JSU.T, mx, 
my);
+                    return null;
+                //#end
+                break;
             }
         } catch (NullPointerException npe) {
             throw new JSExn("Cannot call "+method+"() with null arguments");
@@ -1254,7 +1271,7 @@
         case "sendEvent":   isBoxMethod = true;
         //#end
         if (isBoxMethod)
-               throw new JSExn("Incorrect number of arguments for Box method 
"+method+"()");
+            throw new JSExn("Incorrect number of arguments for Box method 
"+method+"()");
         else throw new JSExn("Box method not found: "+method+"()");
     }
 

Modified: trunk/core/org.vexi.core/src/org/vexi/core/Vexi.jpp
===================================================================
--- trunk/core/org.vexi.core/src/org/vexi/core/Vexi.jpp 2009-07-08 16:20:10 UTC 
(rev 3544)
+++ trunk/core/org.vexi.core/src/org/vexi/core/Vexi.jpp 2009-07-10 15:48:51 UTC 
(rev 3545)
@@ -21,10 +21,10 @@
 
 /** Singleton class that provides all functionality in the vexi.* namespace */
 public final class Vexi extends JS.Obj implements JS.Cloneable, Constants, 
Callable {
-       
+    
     public Vexi(Fountain rr) { 
-       try { super.addTrap(SC_,bless(this, rr, SC_));} catch(JSExn e) { throw 
new Error("should never happen: " + e); } 
-       TemplateBuilder.init(this);
+        try { super.addTrap(SC_,bless(this, rr, SC_));} catch(JSExn e) { throw 
new Error("should never happen: " + e); } 
+        TemplateBuilder.init(this);
     }
 
     
@@ -158,7 +158,7 @@
         case "ui.key.num_lock": return 
Surface.isLockingKeyOn(Surface.NUM_LOCK) ? JSU.T : JSU.F;
         case "ui.key.scroll_lock": return 
Surface.isLockingKeyOn(Surface.SCROLL_LOCK) ? JSU.T : JSU.F;
         case "ui.clipboard": return JSU.S((String)Platform.getClipBoard());
-        case "ui.maxdim": return JSU.N(Short.MAX_VALUE);
+        case "ui.maxdim": return JSU.N(Box.MAX_DIMENSION);
         case "ui.screen": return getSub(name);
         case "ui.screen.width": return JSU.N(Platform.getScreenBounds().width);
         case "ui.screen.height": return 
JSU.N(Platform.getScreenBounds().height);
@@ -168,7 +168,7 @@
         case "undocumented.initialTemplate": return 
JSU.S(Main.initialTemplate);
         case "undocumented.systemfonts": return Platform.systemfonts;
         case "devl": if(Instr.devl==null) throw new JSExn("vexi.devl is not 
available");
-                            else return Instr.devl;
+                     else return Instr.devl;
         
         //#end
         return null;
@@ -229,9 +229,9 @@
     }
     
     private Fountain.File accessFile(JS[] args, boolean write) throws JSExn {
-       String f = Platform.fileDialog(args.length>0?JSU.toString(args[0]):"", 
write);
-       if(f==null) return null;
-       return new Fountain.File(f,write);
+        String f = Platform.fileDialog(args.length>0?JSU.toString(args[0]):"", 
write);
+        if(f==null) return null;
+        return new Fountain.File(f,write);
     }
     
     public JS callMethod(JS this_, JS method, JS[] args) throws JSExn {
@@ -249,7 +249,7 @@
             switch (args.length) {
             case 0:
                 //#switch(JSU.toString(method))
-               case "exit": Main.exit(); return null; 
+                case "exit": Main.exit(); return null; 
                 case "thread.yield": JSU.sleep(-1); return null;
                 //#end
                 break;

Modified: 
trunk/core/org.vexi.core/src_junit/test/core/box/layout/TestLayout.java
===================================================================
--- trunk/core/org.vexi.core/src_junit/test/core/box/layout/TestLayout.java     
2009-07-08 16:20:10 UTC (rev 3544)
+++ trunk/core/org.vexi.core/src_junit/test/core/box/layout/TestLayout.java     
2009-07-10 15:48:51 UTC (rev 3545)
@@ -26,7 +26,7 @@
     public static void main(String[] args) throws Throwable {
 
        CoreTestSuite cts = new TestLayout();
-       TestCase t = cts.createTestCase(cts.getResourceDirs(), "mixedlayout.t");
+       TestCase t = cts.createTestCase(cts.getResourceDirs(), "layout2.t");
        t.runBare();
        }
 }

Added: trunk/core/org.vexi.core/src_junit/test/core/box/layout/layout2.t
===================================================================
--- trunk/core/org.vexi.core/src_junit/test/core/box/layout/layout2.t           
                (rev 0)
+++ trunk/core/org.vexi.core/src_junit/test/core/box/layout/layout2.t   
2009-07-10 15:48:51 UTC (rev 3545)
@@ -0,0 +1,39 @@
+<vexi xmlns:ui="vexi://ui" xmlns="" xmlns:lib="_lib">
+    
+    var newBox = function(min, max, shrink) {
+        var b = vexi.box;
+        b.minwidth = min;
+        b.maxwidth = max;
+        b.hshrink = shrink;
+        return b;
+    }
+    
+    var a = vexi.box;
+    a.width = vexi.ui.maxdim-5;
+    a[0] = vexi.box;
+    a.forcereflow();
+    assert(a[0].width==vexi.ui.maxdim-5);
+
+    var p = vexi.box;
+    p.orient = "horizontal";
+    p.layout = "pack";
+    p.width = 1020;
+    var c1 = newBox(0,20);
+    var c2 = newBox(170,vexi.ui.maxdim);
+    var c3 = newBox(0,20);
+    var c4 = newBox(789,vexi.ui.maxdim);
+    var c5 = newBox(0,vexi.ui.maxdim);
+    p[0] = c1;
+    p[1] = c2;
+    p[2] = c3;
+    p[3] = c4;
+    p[4] = c5;
+    p.forcereflow();
+    assert(c1.width==20);
+    assert(c2.width==170);
+    assert(c3.width==20);
+    assert(c4.width==789);
+    assert(c5.width==21);
+    
+    <ui:box/>
+</vexi>


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge  
This is your chance to win up to $100,000 in prizes! For a limited time, 
vendors submitting new applications to BlackBerry App World(TM) will have
the opportunity to enter the BlackBerry Developer Challenge. See full prize  
details at: http://p.sf.net/sfu/Challenge
_______________________________________________
Vexi-svn mailing list
Vexi-svn@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/vexi-svn

Reply via email to