reflow on demand when JS reads width/height
[org.ibex.core.git] / src / org / ibex / Box.java
index faaabeb..b2608fb 100644 (file)
@@ -2,13 +2,7 @@
 // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
 package org.ibex;
 
-// FEATURE: reflow before allowing js to read from width/height 
-// FEATURE: fastpath for rows=1/cols=1
 // FEATURE: mark to reflow starting with a certain child
-// FEATURE: separate mark_for_reflow and mark_for_resize
-// FEATURE: make all methods final
-// FEATURE: use a linked list for the "frontier" when packing
-// FEATURE:    or else have a way to mark a column "same as last one"?
 // FEATURE: reintroduce surface.abort
 
 import java.util.*;
@@ -171,8 +165,8 @@ public final class Box extends JSScope implements Scheduler.Task {
     public void perform() throws JSExn {
         if (texture == null) { Log.warn(Box.class, "perform() called with null texture"); return; }
         if (texture.isLoaded) {
-            setMinWidth(max(texture.width, maxwidth));
-            setMinHeight(max(texture.height, maxheight));
+            setMinWidth(max(texture.width, minwidth));
+            setMinHeight(max(texture.height, minheight));
             DIRTY; }
         else { JS res = texture.stream; texture = null; throw new JSExn("image not found: "+res.unclone()); }
     }
@@ -199,13 +193,16 @@ public final class Box extends JSScope implements Scheduler.Task {
 
     // Reflow ////////////////////////////////////////////////////////////////////////////////////////
 
+    /** should only be invoked on the root box */
+    void reflow() { pack(); resize(x, y, maxwidth, maxheight); place(); }
+
     private static Box[] frontier = new Box[65535];
     /** pack the boxes into rows and columns, compute contentwidth */
     void pack() {
         if (!test(REPACK)) { constrain(); return; }
         boolean haskid = false;
         for(Box child = getChild(0); child != null; child = child.nextSibling()) { haskid = true; child.pack(); }
-        if (!haskid) { clear(REPACK); return; }
+        if (!haskid) { clear(REPACK); constrain(); return; }
         int frontier_size = 0;
         //#repeat COLS/ROWS rows/cols cols/rows col/row row/col colspan/rowspan rowspan/colspan \
         //        contentheight/contentwidth contentwidth/contentheight
@@ -254,25 +251,22 @@ public final class Box extends JSScope implements Scheduler.Task {
     void resize(LENGTH x, LENGTH y, LENGTH width, LENGTH height) {
         if (x == this.x && y == this.y && width == this.width && height == this.height) return;
         boolean sizechange = (this.width != width || this.height != height) && getTrap("SizeChange") != null;
-        try {
-            int thisx = parent == null ? 0 : this.x;
-            int thisy = parent == null ? 0 : this.y;
-            if (this.x != x || this.y != y) set(MOVED);
-            if (texture == null && (text == null || text.equals("")) && !test(MOVED)) {
-                if ((fillcolor & 0xff000000) != 0) {
-                    Box who = (parent == null ? this : parent);
-                    who.dirty(thisx+min(this.width,width), thisy, Math.abs(width-this.width), max(this.height, height));
-                    who.dirty(thisx, thisy+min(this.height,height), min(this.width, width), Math.abs(height-this.height));
-                }
-                //return;
+        int thisx = parent == null ? 0 : this.x;
+        int thisy = parent == null ? 0 : this.y;
+        Box who = (parent == null ? this : parent);
+        if (this.x != x || this.y != y) set(MOVED);
+        if (texture == null && (text == null || text.equals("")) && !test(MOVED)) {
+            if ((fillcolor & 0xff000000) != 0 || parent == null) {
+                who.dirty(thisx+min(this.width,width), thisy, Math.abs(width-this.width), max(this.height, height));
+                who.dirty(thisx, thisy+min(this.height,height), max(this.width, width), Math.abs(height-this.height));
             }
-            (parent == null ? this : parent).dirty(thisx, thisy, this.width, this.height);
             this.width = width; this.height = height; this.x = x; this.y = y;
-            DIRTY;
-        } finally {
+        } else {
+            who.dirty(thisx, thisy, this.width, this.height);
             this.width = width; this.height = height; this.x = x; this.y = y;
-            if (sizechange) putAndTriggerTrapsAndCatchExceptions("SizeChange", T);
+            DIRTY;
         }
+        if (sizechange) putAndTriggerTrapsAndCatchExceptions("SizeChange", T);
     }
 
     private float targetColumnSize = (float)0.0;
@@ -287,7 +281,8 @@ public final class Box extends JSScope implements Scheduler.Task {
     void solve(boolean findMinimum) {
         int numkids = 0; for(Box c = firstPackedChild(); c != null; c = c.nextPackedSibling()) numkids++;
         //#repeat col/row colspan/rowspan contentwidth/contentheight width/height HSHRINK/VSHRINK numregions/numregions_v \
-        //        maxwidth/maxheight cols/rows minwidth/minheight regions/regions_v targetColumnSize/targetRowSize sizes/sizes_v
+        //        maxwidth/maxheight cols/rows minwidth/minheight regions/regions_v targetColumnSize/targetRowSize sizes/sizes_v \
+        //        HSHRINK/VSHRINK
         if (numkids == 0) {
             if (findMinimum) contentwidth = 0;
             else targetColumnSize = 0;
@@ -339,9 +334,10 @@ public final class Box extends JSScope implements Scheduler.Task {
                         if (regions[r+1] < child.col) continue;
                         if (regions[r] >= min(child.col+child.colspan,cols)) { minregion = r; break; }
                         total -= sizes[r];
+                        int child_maxwidth = child.test(HSHRINK)?child.contentwidth:child.maxwidth;
                         if (sizes[r] <= (float)(targetColumnSize*(regions[r+1]-regions[r])))
-                            if ((child.colspan * targetColumnSize) > (child.maxwidth + (float)0.5))
-                                sizes[r] = (float)Math.min(sizes[r], (regions[r+1]-regions[r])*(child.maxwidth/child.colspan));
+                            if ((child.colspan * targetColumnSize) > (child_maxwidth + (float)0.5))
+                                sizes[r] = (float)Math.min(sizes[r], (regions[r+1]-regions[r])*(child_maxwidth/child.colspan));
                         if ((child.colspan * targetColumnSize) < (child.contentwidth - (float)0.5))
                             sizes[r] = (float)Math.max(sizes[r], (regions[r+1]-regions[r])*(child.contentwidth/child.colspan));
                         total += sizes[r];
@@ -519,8 +515,8 @@ public final class Box extends JSScope implements Scheduler.Task {
         case "rows": return test(FIXED) == ROWS ? N(rows) : N(0);
         case "colspan": return N(colspan);
         case "rowspan": return N(rowspan);
-        case "width": return N(width);
-        case "height": return N(height);
+        case "width": getRoot().reflow(); return N(width);
+        case "height": getRoot().reflow(); return N(height);
         case "minwidth": return N(minwidth);
         case "maxwidth": return N(maxwidth);
         case "minheight": return N(minheight);
@@ -572,7 +568,7 @@ public final class Box extends JSScope implements Scheduler.Task {
     public void put(Object name, Object value) throws JSExn {
         if (name instanceof Number) { put(toInt(name), value); return; }
         //#switch(name)
-        case "text": CHECKSET_STRING(text); RECONSTRAIN(); DIRTY;
+        case "text": if (value == null) value = ""; CHECKSET_STRING(text); RECONSTRAIN(); DIRTY;
         case "strokecolor": value = N(stringToColor((String)value)); CHECKSET_INT(strokecolor); DIRTY;
         case "textcolor": value = N(stringToColor((String)value)); CHECKSET_INT(strokecolor); DIRTY;
         case "strokewidth": CHECKSET_SHORT(strokewidth); DIRTY;