*
* <li> <b>resizing</b>: width/height and x/y positions of children
* are assigned. If a PosChange or SizeChange is triggered,
- * <tt>abort</tt> will be set and the resizing process will
- * abort.
+ * <tt>Surface.abort</tt> will be set and the resizing process will
+ * Surface.abort.
*
* <li> <b>repainting</b>: children draw their content onto the
* buffer.
* The first two passes together are called the <i>reflow</i> phase.
*
* Reflowing is done in a seperate pass since PosChanges and
- * SizeChanges trigger an abort; if rendering were done in the same
- * pass, rendering work done prior to the abort would be wasted.
+ * SizeChanges trigger an Surface.abort; if rendering were done in the same
+ * pass, rendering work done prior to the Surface.abort would be wasted.
*
* Repacking is seperate from resizing since a box's size depends on
* both the box's parent's size (so the traversal must be preorder)
// Misc instance data ////////////////////////////////////////////////////////////////
- public static boolean abort = false;
private static int sizePosChangesSinceLastRender = 0;
void reflow() {
repack();
- if (abort) return;
+ if (Surface.abort) return;
resize(x, y, width, height);
}
// --- Phase 0 ----------------------------------------------------------------------
// recurse
for(Box child = getChild(0); child != null; child = child.nextSibling()) {
- child.reflow();
- if (abort) { MARK_FOR_REFLOW_this; return; }
+ child.repack();
+ if (Surface.abort) { MARK_FOR_REFLOW_this; return; }
}
// --- Phase 1 ----------------------------------------------------------------------
// assign children to their row/column positions (assuming constrained columns)
+ if ((rows == 0 && cols == 0) || (rows != 0 && cols != 0)) throw new Error("rows == " + rows + " cols == " + cols);
//#repeat x/y y/x width/height col/row row/col cols/rows colspan/rowspan colWidth/rowHeight numRowsInCol/numColsInRow INNER/INNER2 maxwidth/maxheight minwidth/minheight contentwidth/contentheight colMaxWidth/rowMaxHeight OUTER/OUTER2 INNER/INNER2
if (rows == 0) {
int[] numRowsInCol = new int[cols]; // the number of cells occupied in each column
if (col + child.colspan >= cols) break;
for(int i=col; i < col + child.colspan; i++) numRowsInCol[i] += child.rowspan;
child.col = col;
+ child.row = row;
col += child.colspan;
for(child = child.nextSibling(); child != null && (child.absolute || child.invisible); child = child.nextSibling());
}
// compute the min/max sizes of the columns and rows and set our contentwidth
//#repeat x/y y/x width/height col/row cols/rows colspan/rowspan colWidth/rowHeight maxwidth/maxheight minwidth/minheight contentwidth/contentheight colMaxWidth/rowMaxHeight
contentwidth = 0;
- LENGTH[] colWidth = new LENGTH[cols];
+ LENGTH[] colWidth = new LENGTH[(cols == 0 ? (getChild(numChildren() - 1).col + 1) : cols)];
for(Box child = getChild(0); child != null; child = child.nextSibling())
- colWidth[child.col] = max(colWidth[child.col], child.contentwidth / child.colspan);
+ if (!(child.absolute || child.invisible))
+ colWidth[child.col] = max(colWidth[child.col], child.contentwidth / child.colspan);
for(int col=0; col<cols; col++) contentwidth += colWidth[col];
contentwidth = max(textwidth, contentwidth);
contentwidth = bound(minwidth, contentwidth, maxwidth);
sizePosChangesSinceLastRender++;
if (sizechange) put("SizeChange", Boolean.TRUE);
if (poschange) put("PosChange", Boolean.TRUE);
- abort = true;
+ Surface.abort = true;
return;
}
needs_reflow = true;
// --- short circuit ----------------------------------------------------------------
if (!needs_reflow) return;
needs_reflow = false;
-
+ if (numChildren() == 0) return;
// --- Phase 2 ----------------------------------------------------------------------
// compute the min/max sizes of the columns and rows and set initial width/height to minimums
- //#repeat x/y y/x width/height col/row cols/rows colspan/rowspan colWidth/rowHeight maxwidth/maxheight minwidth/minheight contentwidth/contentheight colMaxWidth/rowMaxHeight
- LENGTH[] colWidth = new LENGTH[cols];
- LENGTH[] colMaxWidth = new LENGTH[cols];
+ //#repeat x/y y/x width/height col/row cols/rows colspan/rowspan colWidth/rowHeight maxwidth/maxheight minwidth/minheight contentwidth/contentheight colMaxWidth/rowMaxHeight marginWidth/marginHeight
+ LENGTH[] colWidth = new LENGTH[(cols == 0 ? (getChild(numChildren() - 1).col + 1) : cols)];
+ LENGTH[] colMaxWidth = new LENGTH[(cols == 0 ? (getChild(numChildren() - 1).col + 1) : cols)];
+ int marginWidth = width;
+ for(int i=0; i<colMaxWidth.length; i++) colMaxWidth[i] = MAX_LENGTH;
//#end
for(Box child = getChild(0); child != null; child = child.nextSibling()) {
+ if (child.absolute || child.invisible) continue;
//#repeat x/y y/x width/height col/row cols/rows colspan/rowspan colWidth/rowHeight maxwidth/maxheight minwidth/minheight contentwidth/contentheight colMaxWidth/rowMaxHeight hshrink/vshrink
colWidth[child.col] = max(colWidth[child.col], child.contentwidth / child.colspan);
colMaxWidth[child.col] = min(colMaxWidth[child.col], (child.hshrink ? child.minwidth : child.maxwidth) / child.colspan);
//#end
}
+ //#repeat x/y y/x width/height col/row cols/rows colspan/rowspan colWidth/rowHeight maxwidth/maxheight minwidth/minheight contentwidth/contentheight colMaxWidth/rowMaxHeight marginWidth/marginHeight
+ for(int i=0; i<colMaxWidth.length; i++) {
+ if (colMaxWidth[i] == MAX_LENGTH) { marginWidth = 0; break; }
+ marginWidth -= colMaxWidth[i];
+ if (marginWidth < 0) { marginWidth = 0; break; }
+ }
+ //#end
+
// --- Phase 3 ----------------------------------------------------------------------
// hand out the slack
//#repeat x/y y/x width/height col/row cols/rows colspan/rowspan colWidth/rowHeight maxwidth/maxheight minwidth/minheight contentwidth/contentheight colMaxWidth/rowMaxHeight
slack = width;
for(int i=0; i<cols; i++) slack -= colWidth[i];
- while(slack > 0) {
- // FEATURE: inefficient
- int startslack = slack;
- int increment = max(1, slack / cols);
- for(int col=0; col < cols && slack > 0; col++) {
- slack += colWidth[col];
- colWidth[col] = min(colMaxWidth[col], colWidth[col] + increment);
- slack -= colWidth[col];
- }
- if (slack == startslack) break;
- }
+ if (numChildren() > 0)
+ while(slack > 0) {
+ // FEATURE: inefficient
+ int startslack = slack;
+ int increment = max(1, slack / (cols == 0 ? (getChild(numChildren() - 1).col + 1) : cols));
+ for(int col=0; col < (cols == 0 ? (getChild(numChildren() - 1).col + 1) : cols) && slack > 0; col++) {
+ slack += colWidth[col];
+ colWidth[col] = min(colMaxWidth[col], colWidth[col] + increment);
+ slack -= colWidth[col];
+ }
+ if (slack == startslack) break;
+ }
//#end
// --- Phase 4 ----------------------------------------------------------------------
// assign children's new sizes and positions and recurse
for(Box child = getChild(0); child != null; child = child.nextSibling()) {
+ if (child.absolute || child.invisible) continue;
int diff;
- //#repeat x/y y/x width/height col/row cols/rows colspan/rowspan colWidth/rowHeight maxwidth/maxheight minwidth/minheight contentwidth/contentheight colMaxWidth/rowMaxHeight hshrink/vshrink
+ //#repeat x/y y/x width/height col/row cols/rows colspan/rowspan colWidth/rowHeight maxwidth/maxheight minwidth/minheight contentwidth/contentheight colMaxWidth/rowMaxHeight hshrink/vshrink marginWidth/marginHeight
child.width = 0; for(int i=child.col; i<child.colspan; i++) child.width += colWidth[i];
diff = bound(child.contentwidth, child.width, child.hshrink ? child.contentwidth : child.maxwidth) - child.width;
- child.x = 0; for(int i=0; i<child.col; i++) child.x += colWidth[i];
+ child.x = marginWidth / 2; for(int i=0; i<child.col; i++) child.x += colWidth[i];
if (diff < 0) child.x += -1 * (diff / 2);
//#end
/** Renders self and children within the specified region. All rendering operations are clipped to xIn,yIn,wIn,hIn */
void render(int xIn, int yIn, int wIn, int hIn, DoubleBuffer buf) {
- if (abort || invisible) return;
+ if (Surface.abort || invisible) return;
// intersect the x,y,w,h rendering window with ourselves; quit if it's empty
int x = max(xIn, parent == null ? 0 : this.x);
return;
}
// FIXME put named colors back in
+ if (newcolor == b.fillcolor) return;
+ b.fillcolor = newcolor;
+ b.dirty();
}
});
//#end
//max(Surface.scarPicture.getHeight(), b.height));
MARK_FOR_REFLOW_b;
} else {
- b.minwidth = b.minheight = b.width;
+ b.minwidth = b.maxwidth = b.width;
MARK_FOR_REFLOW_b;
}
} });
Vec keywatchers = new Vec();
/** When set to true, render() should abort as soon as possible and restart the rendering process */
- volatile boolean abort = false;
+ static volatile boolean abort = false;
/** a solid red 10x10 double buffer */
private DoubleBuffer showRenderBuf = null;
// make sure the root is properly sized
do {
- Box.abort = false;
+ abort = false;
root.reflow();
- } while(Box.abort);
+ } while(abort);
// this is a bit dangerous since we're passing ourselves to another method before subclasses' ctors have run...
backbuffer = Platform.createDoubleBuffer(Platform.getScreenWidth(), Platform.getScreenHeight(), this);
public synchronized void render() {
// if the window size changed as a result of a user action, we have to update the root box's size
- if (root.width != width || root.width != height) {
+ if (root.width != width || root.height != height) {
// since the scar will be moving, dirty the place it used to be
if (scarred) dirty(hscar,
// make sure the root is properly sized
do {
- Box.abort = false;
+ abort = false;
root.reflow();
// update mouseinside and trigger Enter/Leave as a result of box size/position changes
String oldcursor = cursor;
cursor = "default";
root.Move(mousex, mousey, mousex, mousey);
if (!cursor.equals(oldcursor)) syncCursor();
- } while(Box.abort);
+ } while(abort);
if (centerSurfaceOnRender) {
centerSurfaceOnRender = false;