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