- /** the Box's font, null inherits from parent -- you must call textupdate() after changing this */
- String font = null;
-
- /** if font == null, this might be a cached copy of the inherited ancestor font */
- String cachedFont = null;
-
- /** The surface for us to render on; null if none; INVARIANT: surface == getParent().surface */
- Surface surface = null;
-
- /** Our alignment: top/left == -1, center == 0, bottom/right == +1 */
- byte align = 0;
-
- /** Our background image; to set this, use setImage() */
- public Picture image;
-
- /** The flex for this box */
- int flex = 1;
-
- /** The orientation, horizontal == 0, vertical == 1 */
- byte o = 0;
-
- /** The opposite of the orientation */
- byte xo = 1;
-
- /** The id of this Box */
- public String id = "";
-
- /** The text of this Box -- you must call textupdate() after changing this */
- String text = "";
-
- /** The color of the text in this Box in 00RRGGBB form -- default is black */
- int textcolor = 0xFF000000;
-
- /** The background color of this box in AARRGGBB form -- default is clear; alpha is all-or-nothing */
- int color = 0x00000000;
-
- /** Holds four "strip images" -- 0=top, 1=bottom, 2=left, 3=right, 4=all */
- Picture[] border = null;
-
- /** true iff the box's background image should be tiled */
- boolean tile = false;
-
- /** True iff the Box is invisible */
- public boolean invisible = false;
-
- /** If true, the Box will force its own size to the natural size of its background image */
- boolean sizetoimage = false;
-
- /** If true and tile is false, the background of this image will never be stretched */
- boolean fixedaspect = false;
-
- /** If true, the box will shrink to the smallest vertical size possible */
- boolean vshrink = false;
-
- /** If true, the box will shrink to the smallest horizontal size possible */
- boolean hshrink = false;
-
- /** If true, the box will be positioned absolutely */
- boolean absolute = false;
-
- /** True iff the Box must be run through the prerender() pipeline before render()ing;<br>
- * INVARIANT: if (needs_prerender) then getParent().needs_prerender. **/
- boolean needs_prerender = true;
-
- /** The cursor for this Box -- only meaningful on the root Box */
- public String cursor = null;
-
- /** Any traps placed on this Box */
- public Hash traps = null;
-
-
- // Instance Data: IndexOf ////////////////////////////////////////////////////////////
-
- /** The indexof() Function; created lazily */
- public JS.Callable indexof = null;
- public JS.Callable indexof() { if (indexof == null) indexof = new IndexOf(); return indexof; }
-
- /** a trivial private class to serve as the box.indexof function object */
- private class IndexOf extends JS.Callable {
- public IndexOf() { this.setSeal(true); }
- public Object call(JS.Array args) throws JS.Exn {
- if (args.length() != 1 || args.elementAt(0) == null || !(args.elementAt(0) instanceof Box)) return new Integer(-1);
- Box b = (Box)args.elementAt(0);
- if (b.getParent() != Box.this) {
- if (redirect == null || redirect == Box.this) return new Integer(-1);
- return Box.this.redirect.indexof().call(args);
- }
- return new Integer(b.getIndexInParent());
- }
- }
-
-
- // Methods which enforce/preserve invariants ////////////////////////////////////////////
-
- /** This method MUST be used to change geometry values -- it ensures that certain invariants are preserved. */
- public final void set(int which, int axis, int newvalue) {
-
- // if this Box is the root of the Surface, notify the Surface of size changes
- if (getParent() == null && surface != null && which == size)
- surface._setSize(axis == 0 ? newvalue : size(0), axis == 1 ? newvalue : size(1));
-
- if (getParent() == null && surface != null && (which == dmin || which == dmax))
- surface.setLimits(dmin(0), dmin(1), dmax(0), dmax(1));
-
- switch(which) {
- case dmin: if (dmin(axis) == newvalue) return; if (axis == 0) _dmin_0 = newvalue; else _dmin_1 = newvalue; break;
- case dmax: if (dmax(axis) == newvalue) return; if (axis == 0) _dmax_0 = newvalue; else _dmax_1 = newvalue; break;
- case cmin: if (cmin(axis) == newvalue) return; if (axis == 0) _cmin_0 = newvalue; else _cmin_1 = newvalue; break;
- case abs: if (abs(axis) == newvalue) return; if (axis == 0) _abs_0 = newvalue; else _abs_1 = newvalue; break;
- case pos: if (pos(axis) == newvalue) return; if (axis == 0) _pos_0 = newvalue; else _pos_1 = newvalue; break;
- case size: if (size(axis) == newvalue) return; if (axis == 0) _size_0 = newvalue; else _size_1 = newvalue; break;
- case oldpos: if (oldpos(axis) == newvalue) return; if (axis == 0) _oldpos_0 = newvalue; else _oldpos_1 = newvalue; break;
- case oldsize: if (oldsize(axis) == newvalue) return; if (axis == 0) _oldsize_0 = newvalue; else _oldsize_1 = newvalue; break;
- case pad: if (pad(axis) == newvalue) return; if (axis == 0) _pad_0 = newvalue; else _pad_1 = newvalue; break;
- case textdim: if (textdim(axis) == newvalue) return; if (axis == 0) _textdim_0 = newvalue; else _textdim_1 = newvalue; break;
- default: return;
- }
-
- // size must always agree with dmin/dmax
- if (which == dmin) set(size, axis, max(size(axis), newvalue));
- if (which == dmax) set(size, axis, min(size(axis), newvalue));
-
- // keep cmin in line with dmin/dmax/textdim
- if (which == dmax || which == dmin || which == textdim || which == pad || which == cmin)
- set(cmin, axis,
- max(
- min(dmax(axis), cmin(axis)),
- dmin(axis),
- min(dmax(axis), textdim(axis) + 2 * pad(axis))
- )
- );
-
- // if the pad changes, update cmin
- if (which == pad) sync_cmin_to_children();
-
- // needed in the shrink case, since dmin may have been the deciding factor in calculating cmin
- if ((vshrink || hshrink) && (which == dmin || which == textdim || which == pad)) sync_cmin_to_children();
-
- // if the cmin changes, we need to be re-prerendered
- if (which == cmin) mark_for_prerender();
-
- // if the absolute position of a box changes, its parent needs to be re-prerendered (to update the child's position)
- if (which == abs && getParent() != null) getParent().mark_for_prerender();
-
- // if our cmin changes, then our parent's needs to be recalculated
- if (getParent() != null && which == cmin) {
- mark_for_prerender();
- getParent().sync_cmin_to_children();
- }
-
- }
-
- /** Marks this node and all its ancestors so that they will be prerender()ed */
- public final void mark_for_prerender() {
- if (needs_prerender) return;
- needs_prerender = true;
- if (getParent() != null) getParent().mark_for_prerender();
- }
-
- /** Ensures that cmin is in sync with the cmin's of our children. This should be called whenever a child is added or
- * removed, as well as when our pad is changed. */
- final void sync_cmin_to_children() {
- int co = (int)(2 * pad(o));
- int cxo = (int)(2 * pad(xo));
-
- for(Box bt = getChild(0); bt != null; bt = bt.nextSibling()) {
- if (bt.invisible || bt.absolute) continue;
- co += bt.cmin(o);
- cxo = (int)max(bt.cmin(xo) + 2 * pad(xo), cxo);
- }
-
- set(cmin, o, co);
- set(cmin, xo, cxo);
- }
-
- /** must be called after changes to <tt>image</tt> or <tt>border</tt> if sizetoimage is true */
- public void syncSizeToImage() {
- set(dmax, 0, (image == null ? 0 : image.getWidth()) + (border == null ? 0 : border[2].getWidth()) * 2);
- set(dmax, 1, (image == null ? 0 : image.getHeight()) + (border == null ? 0 : border[0].getHeight()) * 2);
- set(dmin, 0, (image == null ? 0 : image.getWidth()) + (border == null ? 0 : border[2].getWidth()) * 2);
- set(dmin, 1, (image == null ? 0 : image.getHeight()) + (border == null ? 0 : border[0].getHeight()) * 2);
- }