- if (!wasinside && isinside && is_trapped("Enter")) put("Enter", null, this);
- else if (wasinside && !isinside && is_trapped("Leave")) put("Leave", null, this);
- else if (wasinside && isinside && (mousex != oldmousex || mousey != oldmousey) && is_trapped("Move")) put("Move", null, this);
-
- if (isinside && cursor != null && surface != null) surface.cursor = cursor;
-
- // if the mouse has moved into our padding region, it is considered 'outside' all our children
- if (!(mousex >= pos(0) + pad(0) && mousey >= pos(1) + pad(1) &&
- mousex < pos(0) + size(0) - pad(0) && mousey < pos(1) + size(1) + pad(1))) forceleave = true;
-
- for(Box b = getChild(numChildren() - 1); b != null; b = b.prevSibling()) {
- b.Move(oldmousex, oldmousey, mousex, mousey, forceleave);
- if (b.inside(mousex, mousey)) forceleave = true;
- }
- }
-
- /** creates a new box from an anonymous template; <tt>ids</tt> is passed through to Template.apply() */
- Box(Template anonymous, Vec pboxes, Vec ptemplates) {
- super(true);
- set(dmax, 0, Short.MAX_VALUE);
- set(dmax, 1, Short.MAX_VALUE);
- template = anonymous;
- template.apply(this, pboxes, ptemplates);
- templatename = null;
- importlist = null;
- }
-
- /** creates a new box from an unresolved templatename and an importlist; use "box" for an untemplatized box */
- public Box(String templatename, String[] importlist) {
- super(true);
- set(dmax, 0, Short.MAX_VALUE);
- set(dmax, 1, Short.MAX_VALUE);
- this.importlist = importlist;
- template = "box".equals(templatename) ? null : Template.getTemplate(templatename, importlist);
- this.templatename = templatename;
- if (template != null) {
- template.apply(this, null, null);
- if (redirect == this && !"self".equals(template.redirect)) redirect = null;
- }
- }
-
-
- // Prerendering Pipeline ///////////////////////////////////////////////////////////////
-
- /** Checks if the Box's size has changed, dirties it if necessary, and makes sure childrens' sizes are up to date */
- void prerender() {
-
- if (getParent() == null) {
- set(pos, 0, 0);
- set(pos, 1, 0);
- }
-
- if (pos(0) != oldpos(0) || pos(1) != oldpos(1) || size(0) != oldsize(0) || size(1) != oldsize(1)) {
- needs_prerender = true;
- check_geometry_changes();
- }
-
- if (!needs_prerender) return;
- needs_prerender = false;
- if (numChildren() == 0) return;
-
- int sumchildren = sizeChildren();
- positionChildren(pos(o) + pad(o) + max(0, size(o) - 2 * pad(o) - sumchildren) / 2);
-
- for(Box b = getChild(0); b != null; b = b.nextSibling()) {
- b.prerender();
- if (surface.abort) {
- mark_for_prerender();
- return;
- }
- }
- }
-
- /** if the size or position of a box has changed, dirty() the appropriate regions and possibly send Enter/Leave/SizeChange/PosChange */
- private void check_geometry_changes() {
-
- // FASTPATH: if we haven't moved position (just changed size), and we're not a stretched image:
- if (oldpos(0) == pos(0) && oldpos(1) == pos(1) && (image == null || tile)) {
-
- int bw = border == null ? 0 : border[2].getWidth();
- int bh = border == null ? 0 : border[0].getHeight();
-
- // dirty only the *change* in the area we cover, both on ourselves and on our parent
- for(Box cur = this; cur != null && (cur == this || cur == this.getParent()); cur = cur.getParent()) {
- cur.dirty(pos(0) + min(oldsize(0) - bw, size(0) - bw),
- pos(1),
- Math.abs(oldsize(0) - size(0)) + bw,
- max(oldsize(1), size(1)));
- cur.dirty(pos(0),
- pos(1) + min(oldsize(1) - bh, size(1) - bh),
- max(oldsize(0), size(0)),
- Math.abs(oldsize(1) - size(1)) + bh);
- }
-
- // SLOWPATH: dirty ourselves, as well as our former position on our parent
- } else {
- dirty();
- if (getParent() != null) getParent().dirty(oldpos(0), oldpos(1), oldsize(0), oldsize(1));
-
- }
-
- boolean sizechange = false;
- boolean poschange = false;
- if ((oldsize(0) != size(0) || oldsize(1) != size(1)) && is_trapped("SizeChange")) sizechange = true;
- if ((oldpos(0) != pos(0) || oldpos(1) != pos(1)) && is_trapped("PosChange")) poschange = true;
-
- set(oldsize, 0, size(0));
- set(oldsize, 1, size(1));
- set(oldpos, 0, pos(0));
- set(oldpos, 1, pos(1));
-
- if (sizechange || poschange)
- if (surface.sizePosChangesSinceLastRender++ > 500) {
- if (Log.on) Log.log(this, "Warning, more than 500 SizeChange/PosChange traps triggered since last complete render");
- if (Log.on) Log.log(this, " interpreter is at " + Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
- try {
- Trap t = sizechange ? Trap.getTrap(this, "SizeChange") : Trap.getTrap(this, "PosChange");
- InterpretedFunction f = (InterpretedFunction)t.f;
- if (Log.on) Log.log(this, "Current trap is at " + f.getSourceName() + ":" + f.getLineNumbers()[0]);
- } catch (Throwable t) { }
- }
-
- if (sizechange) put("SizeChange", null, Boolean.TRUE);
- if (poschange) put("PosChange", null, Boolean.TRUE);
- if (sizechange || poschange) {
- surface.abort = true;
- return;
- }
- }
-
-
- /** sets our childrens' sizes */
- int sizeChildren() {
-
- // Set sizes along minor axis, as well as sizes for absolute-positioned children
- for(Box bt = getChild(0); bt != null; bt = bt.nextSibling()) {
- if (bt.invisible) continue;
- if (bt.absolute) {
- bt.set(size, o, max(bt.cmin(o), min(size(o) - bt.abs(o) - pad(o), bt.dmax(o))));
- bt.set(size, xo, max(bt.cmin(xo), min(size(xo) - bt.abs(xo) - pad(xo), bt.dmax(xo))));
- } else if (xo == 0 && bt.hshrink || xo == 1 && bt.vshrink) {
- bt.set(size, xo, bt.cmin(xo));
- } else {
- bt.set(size, xo, bound(bt.cmin(xo), size(xo) - 2 * pad(xo), bt.dmax(xo)));
- }
- }
-
- // the ideal size of our children, along our own major axis
- int goal = (o == 0 && hshrink) || (o == 1 && vshrink) ? cmin(o) - 2 * pad(o) : size(o) - 2 * pad(o);
-
- // the current sum of the sizes of all children
- int total = 0;
-
- // each box is set to bound(box.cmin, box.flex * factor, box.dmax)
- int factor = 0;
-
- // Algorithm: we set the sizes of all boxes to bound(cmin, flex * factor, dmax) for some global value
- // 'factor'. We figure out what 'factor' should be by starting at zero, and slowly increasing
- // it. After each pass, 'factor' is set to the smaller of two values: ((goal - total) /
- // remaining_flex) or the next largest value of factor which will cause some box to exceed its
- // cmin or dmax (thereby changing remaining_flex).
-
- while(true) {
- total = 0;
-
- // the sum of the flexes of all boxes which are not pegged at either cmin or dmax
- int remaining_flex = 0;
-
- // this is the next largest value of factor at which some box exceeds its cmin/dmax
- int nextjoint = Integer.MAX_VALUE;
-
- for(Box bt = getChild(0); bt != null; bt = bt.nextSibling()) {
- if (bt.absolute || bt.invisible) continue;
-
- bt.set(size, o, bound(bt.cmin(o), factor * bt.flex, bt.dmax(o)));
- total += bt.size(o);
-
- if (factor * bt.flex < bt.cmin(o) && bt.size(o) == bt.cmin(o)) {
- nextjoint = min(nextjoint, divide_round_up(bt.cmin(o), bt.flex));
-
- } else if (bt.size(o) < bt.dmax(o)) {
- remaining_flex += bt.flex;
- nextjoint = min(nextjoint, divide_round_up(bt.dmax(o), bt.flex));
-
- }