Revision: 4789 http://sourceforge.net/p/vexi/code/4789 Author: clrg Date: 2015-05-04 16:16:14 +0000 (Mon, 04 May 2015) Log Message: ----------- Core margin+padding implementation
Modified Paths: -------------- branches/vexi3_integrated_layout/org.vexi-core.main/src/main/jpp/org/vexi/core/Box.jpp Added Paths: ----------- branches/vexi3_integrated_layout/org.vexi-core.main/src/main/java/org/vexi/core/Insets.java branches/vexi3_integrated_layout/org.vexi-vexi.widgets/src_poke/poke/layout/core.t Added: branches/vexi3_integrated_layout/org.vexi-core.main/src/main/java/org/vexi/core/Insets.java =================================================================== --- branches/vexi3_integrated_layout/org.vexi-core.main/src/main/java/org/vexi/core/Insets.java (rev 0) +++ branches/vexi3_integrated_layout/org.vexi-core.main/src/main/java/org/vexi/core/Insets.java 2015-05-04 16:16:14 UTC (rev 4789) @@ -0,0 +1,110 @@ +// Copyright (c) 2015 the Contributors, as shown in the revision logs. +// Licensed under the GNU General Public License version 2 ("the License"). +// You may not use this file except in compliance with the License. + +package org.vexi.core; + +import org.ibex.js.JS; +import org.ibex.js.JSExn; +import org.ibex.js.JSU; +import org.ibex.js.Constants; + +/** + * <p>Encapsulates margin/padding state and JS interaction</p> + * + * <p>TODO: support different metrics i.e. %/pt/em</p> + */ +class Insets { + // default empty/0 insets + final protected static Insets ZERO = new Insets(); + + final int top, right, bottom, left; + + Insets() { + top = 0; right = 0; bottom = 0; left = 0; + } + Insets(int i) { + top = i; right = i; bottom = i; left = i; + } + Insets(int t, int r, int b, int l) { + top = t; right = r; bottom = b; left = l; + } + + final JS toJS() { + if (this==ZERO) return Constants.NC_0; + if ((top==right)&&(top==bottom)&&(top==left)) + return JSU.N(top); + if (top==bottom && left==right) + return JSU.S(top+" "+left); + return JSU.S(top+" "+right+" "+bottom+" "+left); + } + final JS topToJS() { return JSU.N(top); } + final JS leftToJS() { return JSU.N(left); } + final JS rightToJS() { return JSU.N(right); } + final JS bottomToJS() { return JSU.N(bottom); } + + /** creates an Insets instance by parsing the given JS */ + final static Insets fromJS(JS inset) throws JSExn { + try { + if (inset == null) + return ZERO; + + if (JSU.isString(inset)) { + String[] put = inset.toString().split(" "); + if (put.length==1) { + int i = Integer.parseInt(put[0]); + return new Insets(i); + } else if (put.length==2) { + int v = Integer.parseInt(put[0]); + int h = Integer.parseInt(put[1]); + return new Insets(v, h, v, h); + } else if (put.length==4) { + int t = Integer.parseInt(put[0]); + int r = Integer.parseInt(put[1]); + int b = Integer.parseInt(put[2]); + int l = Integer.parseInt(put[3]); + return new Insets(t, r, b, l); + } else { + throw new Exception(); + } + } + + // Integer + int i = JSU.toInt(inset); + if (i == 0) return ZERO; + return new Insets(i); + + } catch(Exception e) { + throw new JSExn("Invalid insets value '"+inset+"'"); + } + } + + final private int insetFromJS(JS inset) throws JSExn { + try { + return (inset == null) ? 0 + : (JSU.isInt(inset) ? JSU.toInt(inset) : Integer.parseInt(JSU.toString(inset))); + } catch(Exception e) { + throw new JSExn("Invalid inset value '"+inset+"'"); + } + } + + /** creates an Insets instance by parsing the given JS for the top inset */ + final Insets fromTopJS(JS top) throws JSExn { + return new Insets(insetFromJS(top), right, bottom, left); + } + + /** creates an Insets instance by parsing the given JS for the left inset */ + final Insets fromLeftJS(JS left) throws JSExn { + return new Insets(top, right, bottom, insetFromJS(left)); + } + + /** creates an Insets instance by parsing the given JS for the right inset */ + final Insets fromRightJS(JS right) throws JSExn { + return new Insets(top, insetFromJS(right), bottom, left); + } + + /** creates an Insets instance by parsing the given JS for the bottom inset */ + final Insets fromBottomJS(JS bottom) throws JSExn { + return new Insets(top, right, insetFromJS(bottom), left); + } +} Property changes on: branches/vexi3_integrated_layout/org.vexi-core.main/src/main/java/org/vexi/core/Insets.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: branches/vexi3_integrated_layout/org.vexi-core.main/src/main/jpp/org/vexi/core/Box.jpp =================================================================== --- branches/vexi3_integrated_layout/org.vexi-core.main/src/main/jpp/org/vexi/core/Box.jpp 2015-05-02 16:30:06 UTC (rev 4788) +++ branches/vexi3_integrated_layout/org.vexi-core.main/src/main/jpp/org/vexi/core/Box.jpp 2015-05-04 16:16:14 UTC (rev 4789) @@ -78,8 +78,8 @@ * <p>Herein considering the horizontal dimension, the contentwidth of a box is the maximum of:</p> * <ol> * <li>The value of the box's minwidth property</li> - * <li>The rendered width of the box's textual content</li> - * <li>The constrained width of the box's children</li> + * <li>The rendered width of the box's textual content plus any padding</li> + * <li>The constrained width of the box's children any padding</li> * </ol> * * <p>The constrained width of a box's children depends on the layout policy of the box:</p> @@ -174,7 +174,7 @@ // postPutTriggerTrapsAndCatchExceptions(_t_, NAME, _e_);\ // } else {\ // CODE;\ - // } + // }\ // Trivial Helper Methods (should be inlined) ///////////////////////////////////////// @@ -217,7 +217,7 @@ final private boolean test(int mask) { return ((flags & mask) == mask); } - // Required by Surface/Platform ///////////////////////////////////////// + // Required by Surface/Platform ///////////////////////////////////////// public final int getIntFillcolor() { return fillcolor; } public final Surface getSurface() { return Surface.fromBox(getRoot()); } @@ -269,8 +269,8 @@ private static final int PACK = 0x00001000; private static final int CLIP = 0x00002000; - private static final int HAS_WIDTH_SLACK = 0x00004000; - private static final int HAS_HEIGHT_SLACK = 0x00008000; +// private static final int HAS_WIDTH_SLACK = 0x00004000; +// private static final int HAS_HEIGHT_SLACK = 0x00008000; private static final int ALIGN_TOP = 0x00010000; private static final int ALIGN_BOTTOM = 0x00020000; @@ -423,7 +423,14 @@ private int contentwidth = 0; // == min(maxwidth, max(minwidth, textwidth, sum(child.contentwidth))) private int contentheight = 0; + private Insets margin = Insets.ZERO; + private Insets padding = Insets.ZERO; + + // this is a calculated total of the gaps between packed children + // TODO: put into a place() loop or another alternative mechanism + private int box_spacing = 0; + // Instance Methods ///////////////////////////////////////////////////////////////////// // FIX not right. Really we want the constructor to be showing, and it does by default // if it exists, but need to consider the case if a template is not present (or indeed @@ -486,8 +493,8 @@ dirty(); // Tiled images affect contentsize if (test(TILE_IMAGE)) { - setMinWidth(texture.getWidth(), true); - setMinHeight(texture.getHeight(), true); + setMinWidth(texture.getWidth(), true); + setMinHeight(texture.getHeight(), true); setConstrain(); } } else if (texture.getLoadFailed()!=null) { @@ -624,7 +631,17 @@ } } + private final int nominalWidth() { return min(maxwidth, max(contentwidth, minwidth)); } + private final int nominalHeight() { return min(maxheight, max(contentheight, minheight)); } + + private final int constrainToWidth(int testwidth, Box c) { + return max(testwidth, c.nominalWidth() + max(padding.left, c.margin.left) + max(padding.right, c.margin.right)); + } + private final int constrainToHeight(int testheight, Box c) { + return max(testheight, c.nominalHeight() + max(padding.top, c.margin.top) + max(padding.bottom, c.margin.bottom)); + } + // Reflow/rendering Pipeline ////////////////////////////////////// /** used to invoke reflow on a box and it's children */ @@ -633,8 +650,8 @@ return; } constrain(); - int w = test(HSHRINK) ? contentwidth : min(maxwidth, max(contentwidth, width)); - int h = test(VSHRINK) ? contentheight : min(maxheight, max(contentheight, height)); + int w = test(HSHRINK) ? nominalWidth() : min(maxwidth, max(contentwidth, width)); + int h = test(VSHRINK) ? nominalHeight() : min(maxheight, max(contentheight, height)); tryResize(w, h, true); place(test(PLACE_CLEAN)); } @@ -661,8 +678,8 @@ private void constrain() { int i; if (test(CONSTRAIN_DESCENDENT)) { - // clear first, because it is possible - // that reflow may lead to more reflow + // clear first, because it is possible + // that reflow may lead to more reflow clear(CONSTRAIN_DESCENDENT); // reconstrain any children for (Box c = getChild(i=0); c != null; c = getChild(++i)) { @@ -676,10 +693,10 @@ // REMARK: must happen after children's sizes known // otherwise any update is immediately invalidated if (trap_test(RESIZE_TRAP)) { - justTriggerTrapsAndCatchExceptions(SC_Resize, JSU.T); + justTriggerTrapsAndCatchExceptions(SC_Resize, JSU.T); } - // no reconstrain necessary + // no re-constrain necessary if (!test(CONSTRAIN)) { return; } @@ -687,22 +704,28 @@ // establish new content size int new_contentwidth = 0, new_contentheight = 0; if (test(PACK)) { - //#repeat width/height HORIZONTAL/VERTICAL + //#repeat width/height HORIZONTAL/VERTICAL Width/Height left/top right/bottom if (test(ORIENT) == HORIZONTAL) { // accumulate child contentwidth + int prior_margin = padding.left; + box_spacing = 0; for (Box c = getChild(i=0); c != null; c = getChild(++i)) { if (!c.test(DISPLAY)) { continue; } - new_contentwidth += c.contentwidth; + int spacing = max(prior_margin, c.margin.left); + box_spacing += spacing; + new_contentwidth += c.nominalWidth() + spacing; + prior_margin = c.margin.right; } + new_contentwidth += max(prior_margin, padding.right); } else { // maximum child contentwidth for (Box c = getChild(i=0); c != null; c = getChild(++i)) { if (!c.test(DISPLAY)) { continue; } - new_contentwidth = max(new_contentwidth, c.contentwidth); + new_contentwidth = constrainToWidth(new_contentwidth, c); } } //#end @@ -712,12 +735,15 @@ if (!c.test(DISPLAY)) { continue; } - new_contentwidth = max(new_contentwidth, c.contentwidth); - new_contentheight = max(new_contentheight, c.contentheight); + new_contentwidth = constrainToWidth(new_contentwidth, c); + new_contentheight = constrainToHeight(new_contentheight, c); } } //#repeat width/height WIDTH/HEIGHT + if (test(CLIP)) + new_contentwidth = max(new_contentwidth, textwidth) + padding.left + padding.right; +/* if (test(CLIP)) { if (new_contentwidth < maxwidth && new_contentwidth < minwidth) { set(HAS_WIDTH_SLACK); @@ -726,7 +752,7 @@ } } else { if (new_contentwidth < maxwidth && (new_contentwidth < minwidth || new_contentwidth < textwidth || - (texture!=null && new_contentwidth < texture.getWidth()))) { + (texture!=null && new_contentwidth < texture.getWidth()))) { set(HAS_WIDTH_SLACK); } else { clear(HAS_WIDTH_SLACK); @@ -735,6 +761,7 @@ } new_contentwidth = min(maxwidth, max(minwidth, new_contentwidth)); +*/ // assign contentwidth and mark for place in parent and placing of children if (new_contentwidth != contentwidth) { @@ -746,7 +773,7 @@ } else { // constrain contentwidth to frame width if (getSurface()!=null && !test(SHRINK)) { - new_contentwidth = min(getSurface().pendingWidth, new_contentwidth); + new_contentwidth = min(getSurface().pendingWidth, new_contentwidth); } if (new_contentwidth != contentwidth) { setPlaceInTree(); @@ -778,26 +805,45 @@ set(PLACE_CLEAN); } + //#repeat HSHRINK/VSHRINK Width/Height getTargetX/getTargetY x/y tx/ty width/height left/top right/bottom + private final int getTargetWidth(final int availableWidth) { + return test(HSHRINK) ? nominalWidth() + : max(contentwidth, min(maxwidth, availableWidth - x + - max(margin.left, parent.padding.left) - max(margin.right, parent.padding.right))); + } + private final int getTargetX(final int availableWidth, final int targetWidth, final boolean left, final boolean right) { + if (left) return margin.left; + if (right) return availableWidth - targetWidth - margin.right; + + int tx = (availableWidth - targetWidth) / 2; + // honour margins + if (tx < margin.left) return margin.left; + if (availableWidth - tx - targetWidth < margin.right) + return availableWidth - margin.bottom; + return tx; + } + //#end + private final void placeChildren(boolean clean) { if (test(PLACE)) { clear(PLACE); - // needed for later resize() - int child_width, child_height, i; - // place children individually in box space if (!test(PACK)) { + // place children individually in box space + int i; for (Box child = getChild(i=treeSize()-1); child != null; child = getChild(--i)) { if (!child.test(DISPLAY)) { continue; } - child_width = child.test(HSHRINK) ? child.contentwidth : max(child.contentwidth, min(child.maxwidth, width - child.x)); - child_height = child.test(VSHRINK) ? child.contentheight : max(child.contentheight, min(child.maxheight, height - child.y)); - child.tryResize(child_width, child_height, clean); + child.tryResize(child.getTargetWidth(width), + child.getTargetHeight(height), + clean); } - // pack children into available space } else { + // pack children into available space int child_x = 0, child_y = 0; + int child_width, child_height; boolean top = test(ALIGN_TOP); boolean left = test(ALIGN_LEFT); boolean right = test(ALIGN_RIGHT); @@ -805,18 +851,23 @@ if (test(ORIENT) == HORIZONTAL) { // horizontal stacking - if (!test(HAS_WIDTH_SLACK) && 0 >= width-contentwidth) { - // simple case - no slack, place children next to eachother + if (contentwidth >= width) { + // simple case - no slack, place children next to each other + int i = 0; + int prior_margin = padding.left; for (Box child = getChild(i=0); child != null; child = getChild(++i)) { if (!child.test(DISPLAY)) { continue; } // height, y - child_height = child.test(VSHRINK) ? child.contentheight : min(child.maxheight, height); - child_y = top ? 0 : (bottom ? height-child_height : (height-child_height)/2); + child_height = child.getTargetHeight(height); + child_y = getTargetY(height, child_height, top, bottom); // width, x - child.tryMoveAndResize(child_x, child_y, child.contentwidth, child_height, clean); - child_x += child.contentwidth; + child_width = child.nominalWidth(); + child_x += max(prior_margin, child.margin.left); + child.tryMoveAndResize(child_x, child_y, child_width, child_height, clean); + child_x += child_width; + prior_margin = child.margin.right; } } else { @@ -824,76 +875,76 @@ // our layout lies somewhere between the min and max size, // loop over the children attempting to set their width to // targetsize and adjust until it meets the parent width - float targetsize = (float)width/(float)treeSize(); + int packingspace = width - box_spacing; + float targetsize = (float)(packingspace) / (float)treeSize(); float totalsize = 0; - int numactive = 0; for (int j=0; j<100; j++) { + totalsize = 0; int min_minsize = MAX_DIMENSION; int max_maxsize = 0; - int numflexible = 0; + int num_active = 0; int num_minsize = 0; int num_maxsize = 0; + int num_nolimit = 0; int num_passive = 0; + int i = 0; for (Box child = getChild(i=0); child != null; child = getChild(++i)) { if (!child.test(DISPLAY)) { continue; } - numactive++; - if (child.test(HSHRINK) || child.maxwidth==child.contentwidth) { + final int child_nomwidth = child.nominalWidth(); + num_active++; + if (child.test(HSHRINK) || child.maxwidth==child_nomwidth) { num_passive++; - totalsize += child.contentwidth; - } else if (child.contentwidth>targetsize) { - min_minsize = min(min_minsize, child.contentwidth); + totalsize += child_nomwidth; + } else if (child_nomwidth>targetsize) { + min_minsize = min(min_minsize, child_nomwidth); num_minsize++; - totalsize += (float)child.contentwidth; + totalsize += (float)child_nomwidth; } else if (child.maxwidth<targetsize) { max_maxsize = max(max_maxsize, child.maxwidth); num_maxsize++; totalsize += (float)child.maxwidth; } else { - numflexible++; + num_nolimit++; totalsize += targetsize; } } - if (numactive==0) { + if (num_active==0) { // no active children - nothing to do return; } // test to see if targetsize produces a solution that rounds - // to match the width, adjusting appropriatly if it does not + // to match the width, adjusting appropriately if it does not int totalsize_int = (int)(totalsize+0.5); - if (totalsize_int > width) { - if (numflexible>0) { - targetsize -= (totalsize-(float)width)/(float)numflexible; + if (totalsize_int > packingspace) { + if (num_nolimit>0) { + targetsize -= (totalsize-(float)packingspace)/(float)num_nolimit; } else { - if (num_minsize+num_passive==numactive) { - // no solution required - avaiable min-sizes over-consume width + if (num_minsize+num_passive==num_active) { + // no solution required - available min-sizes over-consume width targetsize = width; break; } - targetsize = (float)max_maxsize - (totalsize-(float)width)/(float)num_maxsize; + targetsize = (float)max_maxsize - (totalsize-(float)packingspace)/(float)num_maxsize; } - } else if (totalsize_int < width) { - if (numflexible>0) { - targetsize += ((float)width-totalsize)/(float)numflexible; + } else if (totalsize_int < packingspace) { + if (num_nolimit>0) { + targetsize += ((float)packingspace-totalsize)/(float)num_nolimit; } else { - if (num_maxsize+num_passive==numactive) { - // no solution required - avaiable max-sizes do not consume width + if (num_maxsize+num_passive==num_active) { + // no solution required - available max-sizes do not consume width targetsize = width; break; } - targetsize = (float)min_minsize + ((float)width-totalsize)/(float)num_minsize; + targetsize = (float)min_minsize + ((float)packingspace-totalsize)/(float)num_minsize; } } else { break; } - // reset outer variables and try again - numactive = 0; - totalsize = 0; - // REMARK: this is error / infinite loop prevention // giving a helpful report if no solution is found if (j>=99) { @@ -903,29 +954,34 @@ if (!child.test(DISPLAY)) { continue; } - Log.system.error(this, "Child "+i+": "+child.contentwidth+", "+child.maxwidth+", "+child.test(HSHRINK)); + Log.system.error(this, "Child "+i+": "+child.nominalWidth()+", "+child.maxwidth+", "+child.test(HSHRINK)); } } } // if there is 'slack' (child boxes do not consume parent width) then // align determines whether we pack boxes to left, centre, or right - int offset_x = (int)(totalsize+0.5)>=width || left ? 0 : (int)(((float)width-totalsize)/(float)(right?1:2)); + int offset_x = left ? 0 : (int)(((float)width-totalsize)/(float)(right?1:2)); // we use total (a float) to keep tabs on final layout totals // so we do not get stray pixel sized gaps caused by rounding totalsize = 0; - for (Box child = getChild(i=0); child != null; child = getChild(++i)) { + int prior_margin = padding.left; + int i = 0; + for (Box child = getChild(i); child != null; child = getChild(++i)) { if (!child.test(DISPLAY)) { continue; } // height, y - child_height = child.test(VSHRINK) ? child.contentheight : min(child.maxheight, height); - child_y = top ? 0 : (bottom ? height-child_height : (height-child_height)/2); + child_height = child.getTargetHeight(height); + child_y = child.getTargetY(height, child_height, top, bottom); + // width, x + totalsize += (float)max(prior_margin, child.margin.left); child_x = offset_x + (int)(totalsize+0.5); - if (child.test(HSHRINK) || child.contentwidth > targetsize) { - child_width = child.contentwidth; + final int child_nomwidth = child.nominalWidth(); + if (child.test(HSHRINK) || child_nomwidth > targetsize) { + child_width = child_nomwidth; totalsize += (float)child_width; } else if (targetsize > child.maxwidth) { child_width = child.maxwidth; @@ -936,23 +992,29 @@ totalsize += targetsize; } child.tryMoveAndResize(child_x, child_y, child_width, child_height, clean); + prior_margin = child.margin.right; } } } else { // vertical stacking - mirrors horizontal stacking code [see for comments] - if (!test(HAS_HEIGHT_SLACK) && 0 >= height - contentheight) { + if (contentheight >= height) { // simple case - no slack + int i = 0; + int prior_margin = padding.top; for (Box child = getChild(i=0); child != null; child = getChild(++i)) { if (!child.test(DISPLAY)) { continue; } // width, x - child_width = child.test(HSHRINK) ? child.contentwidth : min(child.maxwidth, width); - child_x = left ? 0 : (right ? width-child_width : (width-child_width)/2); + child_width = child.getTargetWidth(width); + child_x = child.getTargetX(width, child_width, left, right); // height, y - child.tryMoveAndResize(child_x, child_y, child_width, child.contentheight, clean); - child_y += child.contentheight; + child_height = child.nominalHeight(); + child_y += max(prior_margin, child.margin.top); + child.tryMoveAndResize(child_x, child_y, child_width, child_height, clean); + child_y += child_height; + prior_margin = child.margin.bottom; } } else { @@ -960,108 +1022,113 @@ // our layout lies somewhere between the min and max size, // loop over the children attempting to set their width to // targetsize and adjust until it meets the parent width - float targetsize = (float)height/(float)treeSize(); + int packingspace = height - box_spacing; + float targetsize = (float)packingspace / (float)treeSize(); float totalsize = 0; - int numactive = 0; for (int j=0; j<100; j++) { + totalsize = 0; int min_minsize = MAX_DIMENSION; int max_maxsize = 0; - int numflexible = 0; + int num_active = 0; int num_minsize = 0; int num_maxsize = 0; + int num_nolimit = 0; int num_passive = 0; + int i = 0; for (Box child = getChild(i=0); child != null; child = getChild(++i)) { if (!child.test(DISPLAY)) { continue; } - numactive++; - if (child.test(VSHRINK) || child.maxheight==child.contentheight) { + num_active++; + final int child_nomheight = child.nominalHeight(); + if (child.test(VSHRINK) || child.maxheight==child_nomheight) { num_passive++; - totalsize += child.contentheight; - } else if (child.contentheight>targetsize) { - min_minsize = min(min_minsize, child.contentheight); + totalsize += child_nomheight; + } else if (child_nomheight>targetsize) { + min_minsize = min(min_minsize, child_nomheight); num_minsize++; - totalsize += (float)child.contentheight; + totalsize += (float)child_nomheight; } else if (child.maxheight<targetsize) { max_maxsize = max(max_maxsize, child.maxheight); num_maxsize++; totalsize += (float)child.maxheight; } else { - numflexible++; + num_nolimit++; totalsize += targetsize; } } - if (numactive==0) { + if (num_active==0) { // no active children - nothing to do return; } // test to see if targetsize produces a solution that rounds - // to match the height, adjusting appropriatly if it does not + // to match the height, adjusting appropriately if it does not int totalsize_int = (int)(totalsize+0.5); - if (totalsize_int > height) { - if (numflexible>0) { - targetsize -= (totalsize-(float)height)/(float)numflexible; + if (totalsize_int > packingspace) { + if (num_nolimit>0) { + targetsize -= (totalsize-(float)packingspace)/(float)num_nolimit; } else { - if (num_minsize+num_passive==numactive) { - // no solution required - avaiable min-sizes over-consume height + if (num_minsize+num_passive==num_active) { + // no solution required - available min-sizes over-consume height targetsize = height; break; } - targetsize = (float)max_maxsize - (totalsize-(float)height)/(float)num_maxsize; + targetsize = (float)max_maxsize - (totalsize-(float)packingspace)/(float)num_maxsize; } - } else if (totalsize_int < height) { - if (numflexible>0) { - targetsize += ((float)height-totalsize)/(float)numflexible; + } else if (totalsize_int < packingspace) { + if (num_nolimit>0) { + targetsize += ((float)packingspace-totalsize)/(float)num_nolimit; } else { - if (num_maxsize+num_passive==numactive) { - // no solution required - avaiable max-sizes do not consume height + if (num_maxsize+num_passive==num_active) { + // no solution required - available max-sizes do not consume height targetsize = height; break; } - targetsize = (float)min_minsize + ((float)height-totalsize)/(float)num_minsize; + targetsize = (float)min_minsize + ((float)packingspace-totalsize)/(float)num_minsize; } } else { break; } - // reset outer variables and try again - numactive = 0; - totalsize = 0; - // REMARK: this is error / infinite loop prevention // giving a helpful report if no solution is found if (j>=99) { - Log.system.error(this, "Core layout failure, please report:"); - Log.system.error(this, "Parent height: "+height); + Log.system.error(this, "Core layout failure, please report:"); + Log.system.error(this, "Parent height: "+height); for (Box child = getChild(i=0); child != null; child = getChild(++i)) { if (!child.test(DISPLAY)) { continue; } - Log.system.error(this, "Child "+i+": "+child.contentheight+", "+child.maxheight+", "+child.test(VSHRINK)); + Log.system.error(this, "Child "+i+": "+child.nominalHeight()+", "+child.maxheight+", "+child.test(VSHRINK)); } } } // if there is 'slack' (child boxes do not consume parent width) then // align determines whether we pack boxes to left, centre, or right - int offset_y = (int)(totalsize+0.5)>=height || top ? 0 : (int)(((float)height-totalsize)/(float)(bottom?1:2)); + int offset_y = top ? 0 : (int)(((float)height-totalsize)/(float)(bottom?1:2)); // we use total (a float) to keep tabs on final layout totals // so we do not get stray pixel sized gaps caused by rounding totalsize = 0; - for (Box child = getChild(i=0); child != null; child = getChild(++i)) { + int prior_margin = padding.top; + int i = 0; + for (Box child = getChild(i); child != null; child = getChild(++i)) { if (!child.test(DISPLAY)) { continue; } // width, x - child_width = child.test(HSHRINK) ? child.contentwidth : min(child.maxwidth, width); - child_x = left ? 0 : (right ? width-child_width : (width-child_width)/2); + child_width = child.getTargetWidth(width); + child_x = child.getTargetX(width, child_width, left, right); + // height, y + totalsize += (float)max(prior_margin, child.margin.top); child_y = offset_y + (int)(totalsize+0.5); - if (child.test(VSHRINK) || child.contentheight > targetsize) { - child_height = child.contentheight; + final int child_nomheight = child.nominalHeight(); + if (child.test(VSHRINK) || child_nomheight > targetsize) { + child_height = child_nomheight; totalsize += (float)child_height; } else if (targetsize > child.maxheight) { child_height = child.maxheight; @@ -1072,6 +1139,7 @@ totalsize += targetsize; } child.tryMoveAndResize(child_x, child_y, child_width, child_height, clean); + prior_margin = child.margin.bottom; } } } @@ -1209,13 +1277,13 @@ /** fetches a write trap for property 'name' */ private Trap wtrap(JS name) { - Trap t = getTrap(name); + Trap t = getTrap(name); return t==null?null:t.findWrite(); } /** fetches a read trap for property 'name' */ private Trap rtrap(JS name) { - Trap t = getTrap(name); + Trap t = getTrap(name); return t==null?null:t.findRead(); } @@ -1811,7 +1879,7 @@ try { //#switch (JSU.toString(key)) case "from": this.from = (Box)value; return; - case "to": this.to = (Box)value; return; + case "to": this.to = (Box)value; return; //#end } catch(ClassCastException cce) { throw new JSExn("Cannot put non-Box to property '"+JSU.toString(key)+"' on DistanceTo"); @@ -1852,9 +1920,9 @@ return null; case "discover": reflow(); - return null; + return null; case "reflow": - setConstrain(); + setConstrain(); return null; case "render": reflow(); @@ -1955,16 +2023,16 @@ } /** returns true if there is a redirect to consume the attempted put */ - final private boolean tryRedirect(JS name, JS value) throws JSExn { - if (redirect == null) { - throw new JSExn("Attempt to set '"+name+"' on a box with a null redirect"); - } - if (redirect != this) { - redirect.putAndTriggerTraps(name, value); - return true; - } - return false; - } + final private boolean tryRedirect(JS name, JS value) throws JSExn { + if (redirect == null) { + throw new JSExn("Attempt to set '"+name+"' on a box with a null redirect"); + } + if (redirect != this) { + redirect.putAndTriggerTraps(name, value); + return true; + } + return false; + } /** implements reading from box properties in JS */ @SuppressWarnings("unused") @@ -2029,9 +2097,9 @@ * @nofollow * */ case "font": - if (redirect == null) return null; - if (redirect == this) return font.stream; - return redirect.getAndTriggerTraps(SC_font); + if (redirect == null) return null; + if (redirect == this) return font.stream; + return redirect.getAndTriggerTraps(SC_font); /* <p>The size, either in points or relative size, to render the text.</p> * @@ -2053,9 +2121,9 @@ * @initial_value("medium") * */ case "fontsize": - if (redirect == null) return null; - if (redirect == this) return sizeToJS(fontsize); - return redirect.getAndTriggerTraps(SC_fontsize); + if (redirect == null) return null; + if (redirect == this) return sizeToJS(fontsize); + return redirect.getAndTriggerTraps(SC_fontsize); /* <p>The text of a box. Visually <code>null</code> renders the same as the text to "" * (i.e as nothing).</p> @@ -2065,9 +2133,9 @@ * @nofollow * */ case "text": - if (redirect == null) return null; - if (redirect == this) return text; - return redirect.getAndTriggerTraps(SC_text); + if (redirect == null) return null; + if (redirect == this) return text; + return redirect.getAndTriggerTraps(SC_text); /* <p>If the value is a 5-character hex string (#RGB), 7-character hex string (#RRGGBB), * 9-character hex string (#AARRGGBB), a box's text color will be set to that color.</p> @@ -2082,9 +2150,9 @@ * @type(String) * */ case "textcolor": - if (redirect == null) return null; - if (redirect == this) return JSU.S(Color.colorToString(textcolor)); - return redirect.getAndTriggerTraps(SC_textcolor); + if (redirect == null) return null; + if (redirect == this) return JSU.S(Color.colorToString(textcolor)); + return redirect.getAndTriggerTraps(SC_textcolor); /* <p>This property can be set to any of the values specified for textcolor. If the value * written is a stream then it will interpreted as a PNG, GIF, or JPEG image, which will @@ -2141,9 +2209,9 @@ * @initial_value("center") * */ case "align": - if (redirect == null) return null; - if (redirect == this) return alignToJS(); - return redirect.getAndTriggerTraps(SC_align); + if (redirect == null) return null; + if (redirect == this) return alignToJS(); + return redirect.getAndTriggerTraps(SC_align); /* <p>The layout strategy for a box - how it lays out it's children.</p> * @@ -2166,9 +2234,9 @@ * @initial_value("pack") * */ case "layout": - if (redirect == null) return null; - if (redirect == this) return test(PACK) ? SC_pack : (test(CLIP) ? SC_place : SC_layer); - return redirect.getAndTriggerTraps(SC_layout); + if (redirect == null) return null; + if (redirect == this) return test(PACK) ? SC_pack : (test(CLIP) ? SC_place : SC_layer); + return redirect.getAndTriggerTraps(SC_layout); /* <p><em>Read only</em> reflecting the number of children a box has.</p> * @@ -2193,9 +2261,9 @@ * @initial_value("horizontal") * */ case "orient": - if (redirect == null) return null; - if (redirect == this) return test(ORIENT) ? SC_horizontal : SC_vertical; - return redirect.getAndTriggerTraps(SC_orient); + if (redirect == null) return null; + if (redirect == this) return test(ORIENT) ? SC_horizontal : SC_vertical; + return redirect.getAndTriggerTraps(SC_orient); /* <p>Writing to this property sets a box's redirect target. Reading from this property * will return a boolean instead of the redirect target - <code>true</code> if redirect @@ -2403,6 +2471,17 @@ case "contentwidth": return JSU.N(contentwidth); case "contentheight": return JSU.N(contentheight); + case "margin": return margin.toJS(); + case "margin-top": return margin.topToJS(); + case "margin-left": return margin.leftToJS(); + case "margin-right": return margin.rightToJS(); + case "margin-bottom": return margin.bottomToJS(); + case "padding": return padding.toJS(); + case "padding-top": return padding.topToJS(); + case "padding-left": return padding.leftToJS(); + case "padding-right": return padding.rightToJS(); + case "padding-bottom": return padding.bottomToJS(); + /* <p>Whether to display a box and it's contents.</p> * * <p>If this box is the root box of a window, its display property will determine whether @@ -2627,9 +2706,9 @@ @SuppressWarnings("unused") public void put(JS name, JS value) throws JSExn { // integer properties translate to box children - // SHOULD differentiate the methods here. isInt checks anything that - // could be an int (even integer strings) so probably not what we - // actually want. + // SHOULD differentiate the methods here. isInt checks anything that + // could be an int (even integer strings) so probably not what we + // actually want. if (JSU.isInt(name)) { put(JSU.toInt(name), value); return; @@ -2668,7 +2747,7 @@ throw new JSExn("Attempt to put non-null value to the 'thisbox' property"); } case "font": - if (tryRedirect(name, value)) return; + if (tryRedirect(name, value)) return; // We do not convert to a fountain straight away as if it is a Blessing then // the fountain won't work as it will refer to the file/url without .ttf. For // the moment we handle Blessings separately for this reason. @@ -2679,7 +2758,7 @@ if (test(FONTSTREAM_SET)) { clear(FONTSTREAM_SET); if (font.stream == DEFAULT_STREAM) - return; + return; setFont(DEFAULT_STREAM, fontsize); } } else { @@ -2694,11 +2773,11 @@ dirty(); } case "fontsize": - if (tryRedirect(name, value)) return; + if (tryRedirect(name, value)) return; if (value==null) { clear(FONTSIZE_SET); if (fontsize != MEDIUM_SIZE) - return; + return; setFont(font.stream, MEDIUM_SIZE); } else { int ps = jsToSize(value); @@ -2712,29 +2791,29 @@ dirty(); } case "text": - if (tryRedirect(name, value)) return; + if (tryRedirect(name, value)) return; JSString s = value == null ? null : value instanceof JSString ? (JSString)value : (JSString)JSU.S(JSU.toString(value)); - if (value == null || EMPTY_STRING.equals(s)) { - if (text == EMPTY_STRING) - return; + if (value == null || EMPTY_STRING.equals(s)) { + if (text == EMPTY_STRING) + return; text = EMPTY_STRING; - } else { - if (text.equals(s)) { - return; - } - text = s; - } + } else { + if (text.equals(s)) { + return; + } + text = s; + } textCalculateDimensions(); setConstrain(); dirty(); case "textcolor": - if (tryRedirect(name, value)) return; + if (tryRedirect(name, value)) return; try { if (value==null) { clear(FONTCOLOR_SET); if (textcolor == DEFAULT_COLOR) - return; + return; textcolor = DEFAULT_COLOR; } else { int c = Color.stringToColor(JSU.toString(value)); @@ -2751,32 +2830,32 @@ case "shrink": boolean set_shrink = JSU.toBoolean(value); if (test(HSHRINK|VSHRINK) == set_shrink) { - return; + return; } // fire other relevant traps and set shrink flags if (test(HSHRINK) != set_shrink) { if (trap_test(HSHRINK_TRAP)) { JS ret = justTriggerTraps(SC_hshrink, value); if (ret!=Interpreter.CASCADE_PREVENTED) { - setclear(HSHRINK, JSU.toBoolean(ret)); + setclear(HSHRINK, JSU.toBoolean(ret)); } } else { - setclear(HSHRINK, set_shrink); + setclear(HSHRINK, set_shrink); } } if (test(VSHRINK) != set_shrink) { if (trap_test(VSHRINK_TRAP)) { JS ret = justTriggerTraps(SC_vshrink, value); if (ret!=Interpreter.CASCADE_PREVENTED) { - setclear(VSHRINK, JSU.toBoolean(ret)); + setclear(VSHRINK, JSU.toBoolean(ret)); } } else { - setclear(VSHRINK, set_shrink); + setclear(VSHRINK, set_shrink); } } setParentPlace(); case "hshrink": - boolean set_hshrink = JSU.toBoolean(value); + boolean set_hshrink = JSU.toBoolean(value); if (test(HSHRINK) != set_hshrink) { setParentPlace(); setclear(HSHRINK, set_hshrink); @@ -2786,7 +2865,7 @@ } } case "vshrink": - boolean set_hshrink = JSU.toBoolean(value); + boolean set_hshrink = JSU.toBoolean(value); if (test(VSHRINK) != JSU.toBoolean(value)) { setParentPlace(); setclear(VSHRINK, set_hshrink); @@ -2811,7 +2890,7 @@ // to change visible state if parent.visible == false as box.visible is // always be false) or if this box is attached to a surface WriteTrapChain trapchain = (parent!=null && (parent.get(SC_visible) == JSU.T)) || (parent==null && getSurface()!=null) - ? fireVisibleTraps(set_display) : null; + ? fireVisibleTraps(set_display) : null; if (set_display) { set(DISPLAY); requestReflow(); @@ -2853,15 +2932,15 @@ } case "fill": if (value == null && texture != null && test(TILE_IMAGE)) { - // other cases handled by Box.run() + // other cases handled by Box.run() setConstrain(); } if (value == null) { - if (text == null && !test(FILLCOLOR_SET)) - return; + if (text == null && !test(FILLCOLOR_SET)) + return; clear(FILLCOLOR_SET); fillcolor = DEFAULT_FILLCOLOR; - texture = null; + texture = null; } else if (JSU.isString(value)) { // use as a hex colour value int newfillcolor = Color.stringToColor(JSU.toString(value)); @@ -2884,25 +2963,25 @@ } dirty(); case "tile": - boolean tile = JSU.toBoolean(value); + boolean tile = JSU.toBoolean(value); if (test(TILE_IMAGE) != tile) { - if (tile) - set(TILE_IMAGE); - else clear(TILE_IMAGE); + if (tile) + set(TILE_IMAGE); + else clear(TILE_IMAGE); dirty(); if (texture != null && texture.isLoaded()) { if (test(TILE_IMAGE)) { - // this will cause the Box's minimum dimensions - // to be set to the image dimensions when tiled + // this will cause the Box's minimum dimensions + // to be set to the image dimensions when tiled run(null); } } } case "align": - if (tryRedirect(name, value)) return; - setAlign(value); + if (tryRedirect(name, value)) return; + setAlign(value); case "layout": - if (tryRedirect(name, value)) return; + if (tryRedirect(name, value)) return; if (SC_place.equals(value)) { if (!test(PACK) && test(CLIP)) { return; @@ -2931,7 +3010,7 @@ throw new JSExn("Attempt to set Box property 'layout' to unsupported value '"+JSU.toString(value)+"'"); } case "orient": - if (tryRedirect(name, value)) return; + if (tryRedirect(name, value)) return; if (SC_horizontal.equals(value)) { if (test(ORIENT)) { return; @@ -3009,7 +3088,19 @@ dirty(); } } + + case "margin": margin = Insets.fromJS(value); setParentConstrain(); + case "margin-top": margin = margin.fromTopJS(value); setParentConstrain(); + case "margin-left": margin = margin.fromLeftJS(value); setParentConstrain(); + case "margin-right": margin = margin.fromRightJS(value); setParentConstrain(); + case "margin-bottom": margin = margin.fromBottomJS(value); setParentConstrain(); + case "padding": if (tryRedirect(name, value)) return; padding = Insets.fromJS(value); setConstrain(); + case "padding-top": if (tryRedirect(name, value)) return; padding = padding.fromTopJS(value); setConstrain(); + case "padding-left": if (tryRedirect(name, value)) return; padding = padding.fromLeftJS(value); setConstrain(); + case "padding-right": if (tryRedirect(name, value)) return; padding = padding.fromRightJS(value); setConstrain(); + case "padding-bottom": if (tryRedirect(name, value)) return; padding = padding.fromBottomJS(value); setConstrain(); + // return instead of throw an exception because in some corner cases // a put is required because surface operates using a read trap, thus // boxes inheriting a 'surface' box may need to initialize themselves @@ -3196,7 +3287,7 @@ case "maxheight": trap_set(MAXHEIGHT_TRAP); case "contentwidth": trap_set(CONTENTWIDTH_TRAP); case "contentheight": trap_set(CONTENTHEIGHT_TRAP); - case "surface": trap_set(SURFACE_TRAP); + case "surface": trap_set(SURFACE_TRAP); case "visible": trap_set(VISIBLE_TRAP); case "Children": trap_set(CHILDREN_TRAP); case "Enter": trap_set(ENTER_TRAP); @@ -3573,7 +3664,7 @@ Trap rangeTrap = trap_test(CHILDREN_READ_TRAP) ? rtrap(SC_Children) : null; JSExn rangeTrapException = null; - JS value = null; + JS value = null; try { if (rangeTrap != null) { value = Main.SCHEDULER.runBeforeGet(rangeTrap, JSU.N(i)); Added: branches/vexi3_integrated_layout/org.vexi-vexi.widgets/src_poke/poke/layout/core.t =================================================================== --- branches/vexi3_integrated_layout/org.vexi-vexi.widgets/src_poke/poke/layout/core.t (rev 0) +++ branches/vexi3_integrated_layout/org.vexi-vexi.widgets/src_poke/poke/layout/core.t 2015-05-04 16:16:14 UTC (rev 4789) @@ -0,0 +1,22 @@ +<vexi xmlns:ui="vexi://ui"> + <ui:Box fill="white"> + <ui:Box shrink="true" fill="black" margin="10" padding="10"> + <ui:Box fill="white"> + <ui:Box fill="red" minwidth="50" minheight="50" margin="10" /> + <ui:Box fill="blue" minwidth="50" minheight="50" margin="20" /> + <ui:Box fill="green" minwidth="50" minheight="50" margin="30" /> + </ui:Box> + </ui:Box> + + <ui:Box shrink="true" fill="black" margin="10" padding="10" orient="vertical"> + <ui:Box fill="white" orient="vertical"> + <ui:Box fill="red" minwidth="50" minheight="50" margin="30" /> + <ui:Box fill="blue" minwidth="50" minheight="50" margin="20" /> + <ui:Box fill="green" minwidth="50" minheight="50" margin="10" /> + </ui:Box> + </ui:Box> + + vexi.ui.frame = thisbox; + + </ui:Box> +</vexi> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ One dashboard for servers and applications across Physical-Virtual-Cloud Widest out-of-the-box monitoring support with 50+ applications Performance metrics, stats and reports that give you Actionable Insights Deep dive visibility with transaction tracing using APM Insight. http://ad.doubleclick.net/ddm/clk/290420510;117567292;y _______________________________________________ Vexi-svn mailing list Vexi-svn@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/vexi-svn