private short col = 0;
public LENGTH x = 0;
public LENGTH y = 0;
+ public LENGTH ax = 0; // FEATURE: roll these into x/y; requires lots of changes
+ public LENGTH ay = 0; // FEATURE: roll these into x/y; requires lots of changes; perhaps y()?
public LENGTH width = 0;
public LENGTH height = 0;
private LENGTH contentwidth = 0; // == max(minwidth, textwidth, sum(child.contentwidth))
// Instance Methods /////////////////////////////////////////////////////////////////////
+ public final int fontSize() { return font == null ? DEFAULT_FONT.pointsize : font.pointsize; }
/** invoked when a resource needed to render ourselves finishes loading */
public void perform() throws JSExn {
// as external events have occured, check the state of box
if (texture != null) {
if (texture.isLoaded) { minwidth = min(texture.width, maxwidth); minheight = min(texture.height, maxheight); }
- else { JS res = texture.stream; texture = null; throw new JSExn("image not found: "+res); }
+ else { JS res = texture.stream; texture = null; throw new JSExn("image not found: "+res.unclone()); }
+ } else {
+ Log.warn(Box.class, "perform() called with null texture");
}
-
MARK_REPACK;
MARK_REFLOW;
MARK_RESIZE;
// static stuff so we don't have to keep reallocating
private static int[] numRowsInCol = new int[65535];
private static LENGTH[] colWidth = new LENGTH[65535];
+ private static LENGTH[] colMinWidth = new LENGTH[65535];
private static LENGTH[] colMaxWidth = new LENGTH[65535];
private static LENGTH[] rowHeight = new LENGTH[65535];
+ private static LENGTH[] rowMinHeight = new LENGTH[65535];
private static LENGTH[] rowMaxHeight = new LENGTH[65535];
static { for(int i=0; i<rowMaxHeight.length; i++) { rowMaxHeight[i] = MAX_LENGTH; colMaxWidth[i] = MAX_LENGTH; } }
}
//#end
- //#repeat contentwidth/contentheight colWidth/rowHeight colspan/rowspan col/row cols/rows minwidth/minheight \
- // textwidth/textheight maxwidth/maxheight
+ //#repeat contentwidth/contentheight colMinWidth/rowMinHeight colspan/rowspan col/row cols/rows minwidth/minheight \
+ // textwidth/textheight maxwidth/maxheight colWidth/rowHeight
contentwidth = 0;
- for(Box child = firstPackedChild(); child != null; child = child.nextPackedSibling())
- colWidth[child.col] = max(colWidth[child.col], child.contentwidth / child.colspan);
- for(int i=0; i<cols; i++) { contentwidth += colWidth[i]; colWidth[i] = 0; }
+ for(Box child = firstPackedChild(); child != null; child = child.nextPackedSibling()) {
+ colMinWidth[child.col] = max(colMinWidth[child.col], child.contentwidth / child.colspan);
+ colWidth[child.col] = 0;
+ }
+ for(int i=0; i<cols; i++) { contentwidth += colMinWidth[i]; colMinWidth[i] = 0; }
contentwidth = bound(minwidth, max(font == null || text == null ? 0 : font.textwidth(text), contentwidth), maxwidth);
//#end
}
void resize_children() {
+ int eligible;
//#repeat col/row colspan/rowspan contentwidth/contentheight x/y width/height colMaxWidth/rowMaxHeight colWidth/rowHeight \
- // HSHRINK/VSHRINK maxwidth/maxheight cols/rows minwidth/minheight colWidth/rowHeight x_slack/y_slack
+ // colMinWidth/rowMinHeight HSHRINK/VSHRINK maxwidth/maxheight cols/rows minwidth/minheight colWidth/rowHeight \
+ // x_slack/y_slack
// PHASE 1: compute column min/max sizes
int x_slack = width;
+ eligible = 0;
for(int i=0; i<cols; i++) x_slack -= colWidth[i];
for(Box child = firstPackedChild(); child != null; child = child.nextPackedSibling())
for(int i=child.col; i < child.col + child.colspan; i++) {
- x_slack += colWidth[i];
- colWidth[i] = max(colWidth[i], child.contentwidth / child.colspan);
- x_slack -= colWidth[i];
+ colMinWidth[i] = max(colWidth[i], child.contentwidth / child.colspan);
colMaxWidth[i] = min(colMaxWidth[i], child.test(HSHRINK) ? child.contentwidth : child.maxwidth) / child.colspan;
+ colWidth[i] = 0;
+ eligible++;
}
// PHASE 2: hand out slack
for(int startslack = 0; x_slack > 0 && cols > 0 && startslack != x_slack;) {
- int increment = max(1, x_slack / cols);
+ if (eligible == 0) break;
+ int increment = x_slack / eligible;
+ eligible = 0;
startslack = x_slack;
for(short col=0; col < cols; col++) {
// FIXME: double check this
int diff = min(min(colMaxWidth[col], colWidth[col] + increment) - colWidth[col], x_slack);
+ if (colWidth[col] + diff < colMinWidth[col]) diff = colMinWidth[col] - colWidth[col];
+ if (diff == 0) continue;
+ eligible++;
x_slack -= diff;
colWidth[col] += diff;
}
if (!child.test(VISIBLE)) continue;
int child_width, child_height, child_x, child_y;
if (!child.test(PACKED)) {
- child_x = child.x;
- child_y = child.y;
- child_width = child.test(HSHRINK) ? child.contentwidth : min(child.maxwidth, width - child_x);
- child_height = child.test(VSHRINK) ? child.contentheight : min(child.maxheight, height - child_y);
+ child_width = child.test(HSHRINK) ? child.contentwidth : min(child.maxwidth, width - Math.abs(child.ax));
+ child_height = child.test(VSHRINK) ? child.contentheight : min(child.maxheight, height - Math.abs(child.ay));
child_width = max(child.minwidth, child_width);
child_height = max(child.minheight, child_height);
+ int gap_x = width - child_width;
+ int gap_y = height - child_height;
+ child_x = child.ax + (child.test(ALIGN_RIGHT) ? gap_x : !child.test(ALIGN_LEFT) ? gap_x / 2 : 0);
+ child_y = child.ay + (child.test(ALIGN_BOTTOM) ? gap_y : !child.test(ALIGN_TOP) ? gap_y / 2 : 0);
} else {
int unbounded;
//#repeat col/row colspan/rowspan contentwidth/contentheight width/height colMaxWidth/rowMaxHeight \
int globaly = parenty + (parent == null ? 0 : y);
// intersect the x,y,w,h rendering window with ourselves; quit if it's empty
-
if (test(CLIP)) {
- cx1 = max(cx1, parent == null ? 0 : globalx);
- cy1 = max(cy1, parent == null ? 0 : globaly);
+ cx1 = max(cx1, globalx);
+ cy1 = max(cy1, globaly);
cx2 = min(cx2, globalx + width);
cy2 = min(cy2, globaly + height);
if (cx2 <= cx1 || cy2 <= cy1) return;
for(int x = globalx; x < cx2; x += texture.width)
for(int y = globaly; y < cy2; y += texture.height)
buf.drawPicture(texture, x, y, cx1, cy1, cx2, cy2);
-
- if (text != null && !text.equals("") && font != null)
- if (font.rasterizeGlyphs(text, buf, strokecolor, globalx, globaly, cx1, cy1, cx2, cy2, null) == -1)
- font.rasterizeGlyphs(text, buf, strokecolor, globalx, globaly, cx1, cy1, cx2, cy2, this);
+
+ if (text != null && !text.equals("") && font != null) {
+ int gap_x = width - font.textwidth(text);
+ int gap_y = height - font.textheight(text);
+ int text_x = globalx + (test(ALIGN_RIGHT) ? gap_x : !test(ALIGN_LEFT) ? gap_x/2 : 0);
+ int text_y = globaly + (test(ALIGN_BOTTOM) ? gap_y : !test(ALIGN_TOP) ? gap_y/2 : 0);
+ if (font.rasterizeGlyphs(text, buf, strokecolor, text_x, text_y, cx1, cy1, cx2, cy2, null) == -1)
+ font.rasterizeGlyphs(text, buf, strokecolor, text_x, text_y, cx1, cy1, cx2, cy2, this);
+ }
for(Box b = getChild(0); b != null; b = b.nextSibling())
b.render(globalx, globaly, cx1, cy1, cx2, cy2, buf, null);
case "Minimized": if (parent == null && getSurface() != null) getSurface().minimized = toBoolean(value); // FEATURE
case "Maximized": if (parent == null && getSurface() != null) getSurface().maximized = toBoolean(value); // FEATURE
case "Close": if (parent == null && getSurface() != null) getSurface().dispose(true);
- case "redirect": if (redirect == this) redirect = (Box)value; else Log.info(this, "redirect can only be set once");
+ case "redirect":
+ for(Box cur = (Box)value; cur != null; cur = cur.parent)
+ if (cur == redirect) {
+ redirect = (Box)value;
+ return;
+ }
+ JS.error("redirect can only be set to a descendant of its current value");
case "font":
if(!(value instanceof Stream)) throw new JSExn("You can only put streams to the font property");
font = value == null ? null : Font.getFont((Stream)value, font == null ? 10 : font.pointsize);
MARK_RESIZE;
dirty();
case "fontsize": font = Font.getFont(font == null ? null : font.stream, toInt(value)); MARK_RESIZE; dirty();
- case "x": if (parent==null && Surface.fromBox(this)!=null) { CHECKSET_INT(x); } else { if (test(PACKED) && parent != null) return; dirty(); CHECKSET_INT(x); dirty(); MARK_RESIZE; dirty(); }
- case "y": if (parent==null && Surface.fromBox(this)!=null) { CHECKSET_INT(y); } else { if (test(PACKED) && parent != null) return; dirty(); CHECKSET_INT(y); dirty(); MARK_RESIZE; dirty(); }
+ case "x": if (parent==null && Surface.fromBox(this)!=null) {
+ CHECKSET_INT(x);
+ } else {
+ if (test(PACKED) && parent != null) return;
+ dirty(); CHECKSET_INT(ax);
+ dirty(); MARK_RESIZE;
+ dirty();
+ }
+ case "y": if (parent==null && Surface.fromBox(this)!=null) {
+ CHECKSET_INT(y);
+ } else {
+ if (test(PACKED) && parent != null) return;
+ dirty(); CHECKSET_INT(ay);
+ dirty(); MARK_RESIZE;
+ dirty();
+ }
case "titlebar":
if (getSurface() != null && value != null) getSurface().setTitleBarText(JS.toString(value));
super.put(name,value);
case "KeyPressed": if (!test(STOP_UPWARD_PROPAGATION) && parent != null) parent.putAndTriggerTraps(name, value);
case "KeyReleased": if (!test(STOP_UPWARD_PROPAGATION) && parent != null) parent.putAndTriggerTraps(name, value);
case "Move": if (!test(STOP_UPWARD_PROPAGATION) && parent != null) parent.putAndTriggerTraps(name, value);
- case "Enter": if (!test(STOP_UPWARD_PROPAGATION) && parent != null) parent.putAndTriggerTraps(name, value);
- case "Leave": if (!test(STOP_UPWARD_PROPAGATION) && parent != null) parent.putAndTriggerTraps(name, value);
+
+ case "HScroll": if (!test(STOP_UPWARD_PROPAGATION) && parent != null)
+ parent.putAndTriggerTraps(name, N(((Number)value).floatValue() * ((float)parent.fontSize()) / ((float)fontSize())));
+ case "VScroll": if (!test(STOP_UPWARD_PROPAGATION) && parent != null)
+ parent.putAndTriggerTraps(name, N(((Number)value).floatValue() * ((float)parent.fontSize()) / ((float)fontSize())));
case "_Move": propagateDownward(name, value, false);
case "_Press1": propagateDownward(name, value, false);
case "_DoubleClick3": propagateDownward(name, value, false);
case "_KeyPressed": propagateDownward(name, value, false);
case "_KeyReleased": propagateDownward(name, value, false);
+ case "_HScroll": propagateDownward(name, value, false);
+ case "_VScroll": propagateDownward(name, value, false);
case "PosChange": return;
case "SizeChange": return;
case "childadded": return;
case "childremoved": return;
+ case "Enter": return;
+ case "Leave": return;
case "thisbox": if (value == null) removeSelf();
set(CURSOR);
boxToCursor.put(this, value);
Surface surface = getSurface();
- String tempcursor = surface.cursor;
- // FIXME
- //Move(surface.mousex, surface.mousey, surface.mousex, surface.mousey);
- if (surface.cursor != tempcursor) surface.syncCursor();
+ if (surface != null) {
+ String tempcursor = surface.cursor;
+ propagateDownward(null, null, false);
+ if (surface.cursor != tempcursor) surface.syncCursor();
+ }
}
private void setFill(Object value) throws JSExn {
int y = globalToLocalY(getSurface()._mousey);
boolean wasinside = test(MOUSEINSIDE);
boolean isinside = test(VISIBLE) && inside(x, y) && !obscured;
- if (!wasinside && isinside) { set(MOUSEINSIDE); putAndTriggerTrapsAndCatchExceptions("Enter", T); }
- if (wasinside && !isinside) { clear(MOUSEINSIDE); putAndTriggerTrapsAndCatchExceptions("Leave", T); }
+ if (!wasinside && isinside) {
+ set(MOUSEINSIDE);
+ putAndTriggerTrapsAndCatchExceptions("Enter", T);
+ }
+ if (isinside && test(CURSOR)) getSurface().cursor = (String)boxToCursor.get(this);
+ if (wasinside && !isinside) {
+ clear(MOUSEINSIDE);
+ putAndTriggerTrapsAndCatchExceptions("Leave", T);
+ }
boolean found = false;
if (wasinside || isinside)
for(Box child = getChild(treeSize() - 1); child != null; child = child.prevSibling()) {
boolean save_stop = child.test(STOP_UPWARD_PROPAGATION);
+ Object value2 = value;
+ if (name.equals("_HScroll") || name.equals("_VScroll"))
+ value2 = N(((Number)value).floatValue() * ((float)child.fontSize()) / (float)fontSize());
if (obscured || !child.inside(x - child.x, y - child.y)) {
- child.propagateDownward(name, value, true);
+ child.propagateDownward(name, value2, true);
} else try {
found = true;
child.clear(STOP_UPWARD_PROPAGATION);
- child.putAndTriggerTrapsAndCatchExceptions(name, value);
+ if (name != null) child.putAndTriggerTrapsAndCatchExceptions(name, value2);
+ else child.propagateDownward(name, value2, obscured);
} finally {
if (save_stop) child.set(STOP_UPWARD_PROPAGATION); else child.clear(STOP_UPWARD_PROPAGATION);
}
if (child.inside(x - child.x, y - child.y))
- if (name.equals("_Move")) obscured = true;
+ if (name != null && name.equals("_Move")) obscured = true;
else break;
}
if (!obscured && !found)
- if (!name.equals("_Move") || wasinside) putAndTriggerTrapsAndCatchExceptions(name.substring(1), value);
+ if ("_Move".equals(name) || wasinside)
+ if (name != null)
+ putAndTriggerTrapsAndCatchExceptions(name.substring(1), value);
}
private static int stringToColor(String s) {