Revision: 4605 http://sourceforge.net/p/vexi/code/4605 Author: clrg Date: 2013-11-29 15:11:07 +0000 (Fri, 29 Nov 2013) Log Message: ----------- Rewrite of grid to work across both regions and frontiers. Update grid demo.
Modified Paths: -------------- branches/vexi3/org.vexi-vexi.demo/src_main/org/vexi/demo/feature/grid.t branches/vexi3/org.vexi-vexi.demo/src_poke/testfeature.t branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/lib/layout/grid.t Removed Paths: ------------- branches/vexi3/org.vexi-vexi.demo/src_main/org/vexi/demo/inner/grid_block.t Modified: branches/vexi3/org.vexi-vexi.demo/src_main/org/vexi/demo/feature/grid.t =================================================================== --- branches/vexi3/org.vexi-vexi.demo/src_main/org/vexi/demo/feature/grid.t 2013-11-29 15:08:38 UTC (rev 4604) +++ branches/vexi3/org.vexi-vexi.demo/src_main/org/vexi/demo/feature/grid.t 2013-11-29 15:11:07 UTC (rev 4605) @@ -1,7 +1,9 @@ -<!-- Copyright 2007 - see COPYING for details [LGPL] --> +<!-- Copyright 2013 - see COPYING for details [LGPL] --> -<vexi xmlns:ui="vexi://ui" xmlns:lay="vexi.layout" xmlns="vexi.widget" - xmlns:demo="org.vexi.demo"> +<vexi xmlns:ui="vexi://ui" + xmlns:lang="vexi://lang" + xmlns:lay="vexi.layout" + xmlns="vexi.widget"> static.name = "Grid"; static.category = "Layout"; @@ -31,9 +33,9 @@ <button id="create" hshrink="false" text="Create" /> <separator /> <ui:box text="Colspan:" /> - <spin width="50" id="colspan" minvalue="1" maxvalue="20" value="1" /> + <spin width="50" id="colspan" minvalue="1" maxvalue="20" /> <ui:box text="Rowspan:" /> - <spin width="50" id="rowspan" minvalue="1" maxvalue="20" value="1" /> + <spin width="50" id="rowspan" minvalue="1" maxvalue="20" /> <separator /> <button id="moveup" hshrink="false" text="Move up" /> <button id="movedown" hshrink="false" text="Move down" /> @@ -41,16 +43,43 @@ </ui:box> <bevel id="bvl" form="down" margintop="5" marginleft="5"> <scrollpane autohide="true"> - <lay:grid id="grid" cols="6" /> + <lay:grid id="grid" /> // cols initialized by reset </scrollpane> </bevel> </ui:box> + <lang:subtemplate property="GridBlock"> + <border border="black" cursor="hand" depth="2" fill="white" layout="place"> + <ui:box orient="vertical" shrink="true"> + <ui:box shrink="true" id="boxindex" /> + <ui:box shrink="true"> + <ui:box id="boxcol" /> + <ui:box id="boxcolspan" /> + </ui:box> + <ui:box shrink="true"> + <ui:box id="boxrow" /> + <ui:box id="boxrowspan" /> + </ui:box> + </ui:box> + + thisbox.v_col ++= function(v) { cascade = v; $boxcol.text = " Col: "+v; } + thisbox.colspan ++= function(v) { cascade = v; $boxcolspan.text = ", Span: "+v+" "; } + thisbox.v_row ++= function(v) { cascade = v; $boxrow.text = " Row: "+v; } + thisbox.rowspan ++= function(v) { cascade = v; $boxrowspan.text = ", Span: "+v+" "; } + thisbox.index ++= function(v) { cascade = v; $boxindex.text = " Index: "+v+" "; } + + thisbox.selected ++= function(v) { cascade = v; fill = v ? "#ffeedd" : "white"; } + + </border> + </lang:subtemplate> + thisbox.selected ++= function(v) { cascade = v; - $delete.enabled = (selected != null); - $moveup.enabled = (selected != null); - $movedown.enabled = (selected != null); + $delete.enabled = (v != null); + $moveup.enabled = (v != null); + $movedown.enabled = (v != null); + $colspan.value = v?v.colspan:1; + $rowspan.value = v?v.rowspan:1; } selected = null; @@ -67,11 +96,11 @@ } $create.action ++= function(v) { - var b = demo.inner.grid_block(vexi.box); + var b = new GridBlock(); b.colspan = $colspan.value; b.rowspan = $rowspan.value; b.Press1 ++= selectWrite; - $grid[$grid.numchildren] = b; + $grid.add(b); return; } @@ -87,20 +116,29 @@ return; } + const updateIndice = function() { + for (var i,b in $grid) { + b.index = i; + } + } + $delete.action ++= function(v) { selected.thisbox = null; + updateIndice(); return; } - $moveup.action ++= function(v) { + $movedown.action ++= function(v) { var i = $grid.indexof(selected); if (i>0) $grid[i-1] = selected; + updateIndice(); return; } $moveup.action ++= function(v) { var i = $grid.indexof(selected); if ($grid.numchildren>i+1) $grid[i+1] = selected; + updateIndice(); return; } @@ -119,11 +157,16 @@ if (v == null) { var _v = $grid[trapname]; if (_v == selected) selected = null; + } else { + // adding + v.index = trapname; } cascade = v; } var startGrid = [ [1,1], [2,1], [1,1], [1,2], [1,2], [3,2], [1,1], [1,1] ]; + // the below starting grid only occupies 4 of the 5 available columns + //var startGrid = [ [1,1], [1,1], [1,1], [1,2], [2,1], [1,2], [3,2], [1,1] ]; $reset.action ++= function(v) { $clear.action = true; @@ -131,7 +174,7 @@ $use.value = 5; var l = startGrid.length; for (var i=0; l>i; i++) { - var b = demo.inner.grid_block(vexi.box); + var b = new GridBlock(); b.colspan = startGrid[i][0]; b.rowspan = startGrid[i][1]; b.Press1 ++= selectWrite; Deleted: branches/vexi3/org.vexi-vexi.demo/src_main/org/vexi/demo/inner/grid_block.t =================================================================== --- branches/vexi3/org.vexi-vexi.demo/src_main/org/vexi/demo/inner/grid_block.t 2013-11-29 15:08:38 UTC (rev 4604) +++ branches/vexi3/org.vexi-vexi.demo/src_main/org/vexi/demo/inner/grid_block.t 2013-11-29 15:11:07 UTC (rev 4605) @@ -1,30 +0,0 @@ -<!-- Copyright 2007 - see COPYING for details [LGPL] --> - -<vexi xmlns:ui="vexi://ui" xmlns="vexi.layout"> - <border border="black" depth="2" fill="white" layout="place"> - <ui:box orient="vertical" shrink="true"> - <ui:box shrink="true"> - <ui:box text=" Col: " /> - <ui:box id="col" /> - <ui:box text=" Span: " /> - <ui:box id="colspan" /> - <ui:box text=" " /> - </ui:box> - <ui:box shrink="true"> - <ui:box text=" Row: " /> - <ui:box id="row" /> - <ui:box text=" Span: " /> - <ui:box id="rowspan" /> - <ui:box text=" " /> - </ui:box> - </ui:box> - - thisbox.v_col ++= function(v) { $col.text = v; cascade = v; } - thisbox.colspan ++= function(v) { $colspan.text = v; cascade = v; } - thisbox.v_row ++= function(v) { $row.text = v; cascade = v; } - thisbox.rowspan ++= function(v) { $rowspan.text = v; cascade = v; } - - thisbox.selected ++= function(v) { cascade = v; fill = v ? "#ffeedd" : "white"; } - - </border> -</vexi> Modified: branches/vexi3/org.vexi-vexi.demo/src_poke/testfeature.t =================================================================== --- branches/vexi3/org.vexi-vexi.demo/src_poke/testfeature.t 2013-11-29 15:08:38 UTC (rev 4604) +++ branches/vexi3/org.vexi-vexi.demo/src_poke/testfeature.t 2013-11-29 15:11:07 UTC (rev 4605) @@ -1,12 +1,18 @@ -<!-- Copyright 2007 - see COPYING for details [LGPL] --> +<!-- public domain --> <vexi xmlns:ui="vexi://ui" xmlns="vexi.widget" xmlns:f="org.vexi.demo.feature"> <surface /> <ui:box> + // very simple template to open a feature in a frame var feature = vexi.params["feature"]; if (feature==null) feature = "grid"; thisbox[0] = f[feature](vexi.box); + + // or comment out the above and uncomment this + <!--f:testcase_grid2 /--> + vexi.ui.frame = thisbox; + </ui:box> </vexi> \ No newline at end of file Modified: branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/lib/layout/grid.t =================================================================== --- branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/lib/layout/grid.t 2013-11-29 15:08:38 UTC (rev 4604) +++ branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/lib/layout/grid.t 2013-11-29 15:11:07 UTC (rev 4605) @@ -1,348 +1,276 @@ -<!-- Copyright 2011 - see COPYING for details [LGPL] --> +<!-- Copyright 2013 - see COPYING for details [LGPL] --> -<vexi xmlns:ui="vexi://ui" xmlns:meta="vexi://meta" xmlns="vexi.util"> - <meta:doc> - <author>Charles Goodwin</author> - <todo> - * hand out slack like the core does when packing - * use static functions to make grids lightweight - * potential optimization: do not restart frontier - checking if next child to pack has colspan gte - previous child (i.e. as big or bigger). - </todo> - </meta:doc> - +<vexi xmlns:ui="vexi://ui" + xmlns:lang="vexi://lang" + xmlns:js="vexi://js" + xmlns="vexi.util"> + <ui:box layout="layer"> + <!-- no redirect; it is managed by a Children trap --> <ui:box id="content" align="topleft" layout="place" /> + <lang:subtemplate property="Regions"> + <js:Object> + + /** A region is represented primarily by 2 facets + * - the origin i.e. box.col + * - the frontier it affects i.e. box.col+box.colspan + + * As multiple boxes may affect the same region, it is + * convenient to group them together with the region + */ + const newRegion = function(box, origin, frontier) { + return { boxes:[box], origin:origin, frontier:frontier }; + } + + thisobj.list = new vexi.js.ProxyList(); + + thisobj.add = function(box, origin, frontier) { + for (var i=list.length; i>0; i--) { + const region = list[i-1]; + if (region.frontier > frontier) continue; + if (frontier == region.frontier) { + if (region.origin > origin) + continue; + if (region.origin == origin) { + region.boxes.push(box); + return; + } + } + list[i] = newRegion(box, origin, frontier); + return; + } + // reached the end + list[0] = newRegion(box, origin, frontier); + }; + + </js:Object> + </lang:subtemplate> + /******************************** ******** INTERNAL LOGIC ******** ********************************/ - var numcols = 0; - var numrows = 1; - var userows = true; + var numcols = 0; // these two vars + var numrows = 0; // are calcualated + var setsize = 1; // these two vars represent + var userows = true; // the user-specified break + var pack_children = false; + var place_horizontal = false; + var place_vertical = false; - var min = function(a, b) { return a > b ? b : a; } - var max = function(a, b) { return a > b ? a : b; } + const min = function(a, b) { return a > b ? b : a; } + const max = function(a, b) { return a > b ? a : b; } + const round = vexi.math.round; - // used to track what regions of the grid a child affects - var colregions = new .orderedlist(); - var colregrefs = {}; - var rowregions = new .orderedlist(); - var rowregrefs = {}; + // container object for regions,frontiers,etc + const byCol = {}; + const byRow = {}; - /* - * PLACING - * - * The effect of each child is measured against the region it - * occupies - the row/column boundary it is pushing. If this - * region changes then boxes in subsequent regions shift over. - */ - - var fromHAlign = function(a, v1, v2) { - if (a.indexOf("left") > -1) return v1; - if (a.indexOf("right") > -1) return v1+v2; - return v1+(v2/2); - } - - var fromVAlign = function(a, v1, v2) { - if (a.indexOf("top") > -1) return v1; - if (a.indexOf("bottom") > -1) return v1+v2; - return v1+(v2/2); - } - - var place = function(findwidths, findheights) { - if (!numcols or !numrows) { - return; - } - // honor min/max sizes for boxes in order of region - var n = $content.numchildren; - var c, c0; - var content; - var mindim; - var maxdim; - var minsizes; - var maxsizes; - var regmark; - var regions; - var rindmap; - var r1, r2; - var sizes; + const placeRegions = function(bySet, fullreach, gridsize, shrink, contentdim, mindim, maxdim, dim, pos) { + const regions = bySet.regions; + const frontiers = bySet.frontiers; + const f2i = bySet.frontier2index; - // child width solving - if (findwidths) { - // PHASE 1: honor min/max sizes for boxes in order of region - content = 0; - minsizes = [0]; - maxsizes = [0]; - regions = [0]; - rindmap = {}; - rindmap[0] = 0; - regmark = 1; - sizes = [0]; - for (c = colregions.mark(); c != null; c = colregions.next()) { - c0 = c[0]; - // bypass hidden children - if (!c0.display) continue; - r1 = rindmap[c0.v_col]; - r2 = rindmap[c0.v_col+c0.colspan]; - if (r2 == null) { - // first to affect this region - r2 = regmark; - regions[r2] = c0.v_col+c0.colspan; - rindmap[regions[r2]] = r2; - minsizes[r2] = 0; - maxsizes[r2] = 0; - sizes[r2] = 0; - content += minsizes[regmark-1]; - regmark++; - } - if (r2-r1 == 1) { - // affect single region - minsizes[r2] = max(c0.contentwidth, minsizes[r2]); - maxsizes[r2] = max((c0.hshrink ? c0.contentwidth : c0.maxwidth), maxsizes[r2]); - } else { - // affect multiple regions - mindim = c0.contentwidth; - maxdim = c0.hshrink ? c0.contentwidth : c0.maxwidth; - // work out the minimum size of this box's region - // the box affects c.v_col+1 -> c.v_col+c.colspan - for (var r=r1+1; r2>r; r++) { - mindim -= minsizes[r]; - } - minsizes[r2] = max(mindim, minsizes[r2]); - // work out the maximum size of this box's region - for (var r=r1+1; r2>r; r++) { - if (maxdim > maxsizes[r]) { - maxdim -= maxsizes[r]; - } else { - maxdim = 0; - break; - } - } - maxsizes[r2] = max(maxdim, maxsizes[r2]); - } + // PHASE 1: honor min/max sizes for boxes in order of region + var contentMin = 0; + var contentMax = 0; + const frontierMin = {}; + const frontierMax = {}; + for (var i,region in regions.list) { + var minsize = 0; + var maxsize = 0; + for (var j,box in region.boxes) { + const c = box[0]; + minsize = max(c[contentdim], minsize); + maxsize = max((c[shrink] ? c[contentdim] : c[maxdim]), maxsize); } - // by now we know our minimum size - $content.minwidth = content + (r2!=null?minsizes[r2]:0); + region.minsize = minsize; + region.maxsize = maxsize; - // PHASE 2: final region width assignment - // priority 0: (inviolable) honor minwidths - // priority 1: sum of columns no greater than parent - // priority 2: honor maxwidths - // priority 3: equalize columns + const origin = region.origin; + const frontier = region.frontier; + const priorMin = origin>0 ? frontierMin[origin] : 0; + const priorMax = origin>0 ? frontierMax[origin] : 0; + const currentMin = frontierMin[region.frontier]; + const currentMax = frontierMax[region.frontier]; - var mincols, maxcols; - var diff, size; - var target = width; - var targetColumnSize = target / numcols; - - // limit solving loop to 100 iterations - for (var i=0; 100>i; i++) { - // reset markers - r1 = 0; mincols=0; maxcols=0; - // calculate size for each region based on targetColumnSize - for (var r=1; regmark>r; r++) { - r2 = regions[r]; - // honor maxwidths then minwidths - diff = max(minsizes[r], min(maxsizes[r], targetColumnSize * (r2-r1))); - sizes[r] = diff + sizes[r-1]; - // account for cols that can be further affected - if (diff > minsizes[r]) mincols += (r2-r1); - if (maxsizes[r] > diff) maxcols += (r2-r1); - r1 = r2; - } - // our current size - size = sizes[regmark-1]; - if (0.5 > size-target and size-target >= -0.5) { - // break if we were close to the target - break; - } - if (size > target) { - // not at the target size, adjust accordingly - if (mincols == 0) { - // no cols to be further decreased in size - break; - } - // else adjust targetColumnSize according to target-total deficit - targetColumnSize += ((target-size) / mincols); - } else { - if (maxcols == 0) { - // no cols to be further increased in size - break; - } - // adjust targetColumnSize according to target-total deficit - targetColumnSize += ((target-size) / maxcols); - } - if (i>=99) { - // infinite loop prevention - vexi.log.warn("failed to solve grid"); - } + if (currentMin == null or priorMin + minsize > currentMin) { + contentMin = max(priorMin + minsize, contentMin); + frontierMin[frontier] = priorMin + minsize; } - - // place our boxes - c = colregions.mark(); - c0 = c?c[0]:null; - for (var r=1; regmark>r; r++) { - sizes[r] = vexi.math.round(sizes[r]); - while (c and regions[r] == c0.v_col+c0.colspan) { - r1 = sizes[rindmap[c0.v_col]]; - r2 = sizes[r]; - c.width = c0.hshrink ? c0.contentwidth : min(c0.maxwidth, r2-r1); - c.x = (c.width == r2-r1) ? r1 : fromHAlign(c0.align, r1, r2-r1-c.width); - c = colregions.next(); - if (c) { - c0 = c[0]; - } - } + if (currentMax == null or priorMax + maxsize > currentMax) { + if (contentMax != vexi.ui.maxdim) { + contentMax = min(vexi.ui.maxdim, max(priorMax + maxsize, contentMax)); + frontierMax[frontier] = priorMax + maxsize; + } else frontierMax[frontier] = min(vexi.ui.maxdim, priorMax + maxsize); } - - // hand out any slack - //if (target > size+0.5) { - // targetColumnSize = (target-size) / cols; - // for (int r=0; regmark>r; r++) - // sizes[regions[r]] += regions[r] * targetColumnSize+0.5; } + // by now we know our minimum/maximum size + $content[mindim] = contentMin; + $content[maxdim] = contentMax; - // child height solving - if (findheights) { - // PHASE 1: honor min/max sizes for boxes in order of region - content = 0; - minsizes = [0]; - maxsizes = [0]; - regions = [0]; - rindmap = {}; - rindmap[0] = 0; - regmark = 1; - sizes = [0]; - for (c = rowregions.mark(); c != null; c = rowregions.next()) { - c0 = c[0]; - // bypass hidden children - if (!c0.display) continue; - r1 = rindmap[c0.v_row]; - r2 = rindmap[c0.v_row+c0.rowspan]; - // first to affect this region - if (r2 == null) { - r2 = regmark; - regions[r2] = c0.v_row+c0.rowspan; - rindmap[regions[r2]] = r2; - minsizes[r2] = 0; - maxsizes[r2] = 0; - sizes[r2] = 0; - content += minsizes[regmark-1]; - regmark++; + // PHASE 2: place wrapper boxes in accordance with region/frontier min/max sizes + if (thisbox[shrink] or $content[mindim] > gridsize) { + // honor min sizes + for (var i,region in regions.list) { + const regionPos = region.origin>0 ? frontierMin[region.origin] : 0; + const regionDim = frontierMin[region.frontier] - regionPos; + for (var j,box in region.boxes) { + box[pos] = regionPos; + box[dim] = regionDim; } - if (r2-r1 == 1) { - // affect single region - minsizes[r2] = max(c0.contentheight, minsizes[r2]); - maxsizes[r2] = max((c0.vshrink ? c0.contentheight : c0.maxheight), maxsizes[r2]); - } else { - // affect multiple regions - mindim = c0.contentheight; - maxdim = c0.vshrink ? c0.contentheight : c0.maxheight; - // work out the minimum size of this box's region - // the box affects c.v_col+1 -> c.v_col+c.colspan - for (var r=r1+1; r2>r; r++) { - mindim -= minsizes[r]; - } - minsizes[r2] = max(mindim, minsizes[r2]); - // work out the maximum size of this box's region - for (var r=r1+1; r2>r; r++) { - if (maxdim > maxsizes[r]) { - maxdim -= maxsizes[r]; - } else { - maxdim = 0; - break; - } - } - maxsizes[r2] = max(maxdim, maxsizes[r2]); + } + } else + if (gridsize >= $content[maxdim]) { + // honor max sizes + for (var i,region in regions.list) { + const regionPos = region.origin>0 ? frontierMax[region.origin] : 0; + const regionDim = frontierMax[region.frontier] - regionPos; + for (var j,box in region.boxes) { + box[pos] = regionPos; + box[dim] = regionDim; } } - // by now we know our minimum size - $content.minheight = content + (r2!=null?minsizes[r2]:0); + } else { + // solving required; guess at a workable target col/row size, + // test it and adjust it up/down until it tests successfully + var targetSize = gridsize / fullreach; - // PHASE 2: final region height assignment - // priority 0: (inviolable) honor minwidths - // priority 1: sum of columns no greater than parent - // priority 2: honor maxwidths - // priority 3: equalize columns + // these exist outside of the loop as their + // contents get fully overwritten each pass + const frontierMin = []; + const frontierMax = []; + const frontierDim = {}; + // this is constant + frontierDim[0] = 0; + // need this after solving + var lastFrontier; - var minrows, maxrows; - var diff, size; - var target = height; - var targetRowSize = target / numrows; - // limit solving loop to 100 iterations for (var i=0; 100>i; i++) { - // reset markers - r1 = 0; minrows=0; maxrows=0; - // calculate size for each region based on targetRowSize - for (var r=1; regmark>r; r++) { - r2 = regions[r]; - // honor maxwidths then minwidths - diff = max(minsizes[r], min(maxsizes[r], targetRowSize * (r2-r1))); - sizes[r] = diff + (r == 0 ? 0 : sizes[r-1]); - // account for rows that can be further affected - if (diff > minsizes[r]) minrows += (r2-r1); - if (maxsizes[r] > diff) maxrows += (r2-r1); - r1 = r2; + lastFrontier = 0; + for (var i,region in regions.list) { + const frontier = region.frontier; + const f_index = f2i[frontier]; + const origin = region.origin; + const reach = frontier-origin; + const minsize = region.minsize; + const maxsize = region.maxsize; + var target; + + if (frontier!=lastFrontier) { + // reset frontierMin/Max + frontierMin[f_index] = false; + frontierMax[f_index] = false; + } + + if (minsize == maxsize) { + // region size fixed + target = minsize; + } else { + target = targetSize * reach; + const o_index = f2i[origin]; + + if (target > minsize) + for (var i=f_index; i>o_index; i--) + frontierMin[i] = true; + else target = minsize; + + if (maxsize > target) + for (var i=f_index; i>o_index; i--) + frontierMax[i] = true; + else target = maxsize; + } + + const frontierPush = frontierDim[origin] + target; + if (frontier!=lastFrontier or frontierPush > frontierDim[frontier]) + frontierDim[frontier] = frontierPush; + lastFrontier = frontier; } - // our current size - size = sizes[regmark-1]; - if (0.5 > size-target and size-target >= -0.5) { + + const totalSize = frontierDim[lastFrontier]; + if (0.5 > totalSize-gridsize and totalSize-gridsize >= -0.5) { // break if we were close to the target break; } - if (size > target) { - // not at the target size, adjust accordingly - if (minrows == 0) { + + // not at the target size, adjust accordingly + if (totalSize > gridsize) { + // too big; adjust down + var reachDown = 0; + for (var i,isMin in frontierMin) { + if (isMin) continue; + reachDown += frontiers[i]-(i==0?0:frontiers[i-1]); + } + if (reachDown == 0) { // no rows to be further decreased in size - break; + throw "Should not be possible"; } // else adjust targetRowSize according to target-total deficit - targetRowSize += ((target-size) / minrows); + targetSize += ((totalSize-gridsize) / reachDown); + } else { - if (maxrows == 0) { + // too small; adjust up + var reachUp = 0; + for (var i,isMax in frontierMax) { + if (isMax) continue; + reachUp += frontiers[i]-(i==0?0:frontiers[i-1]); + } + if (reachUp == 0) { // no rows to be further increased in size - break; + throw "Should not be possible"; } // adjust targetRowSize according to target-total deficit - targetRowSize += ((target-size) / maxrows); + targetSize += ((gridsize-totalSize) / reachUp); } if (i>=99) { // infinite loop prevention - vexi.log.warn("failed to solve grid"); + vexi.log.warn("failed to solve grid "+dim+" "+fullreach); } } + // just to be sure we don't miss the edge of the grid + frontierDim[lastFrontier] = gridsize; + // place our boxes - c = rowregions.mark(); - c0 = c?c[0]:null; - for (var r=1; regmark>r; r++) { - sizes[r] = vexi.math.round(sizes[r]); - while (c and regions[r] == c0.v_row+c0.rowspan) { - r1 = sizes[rindmap[c0.v_row]]; - r2 = sizes[r]; - c.height = c0.vshrink ? c0.contentheight : min(c0.maxheight, r2-r1); - c.y = (c.height == r2-r1) ? r1 : fromVAlign(c0.align, r1, r2-r1-c.height); - c = rowregions.next(); - if (c) { - c0 = c[0]; - } + for (var i,region in regions.list) { + const regionPos = round(frontierDim[region.origin]); + const targetDim = round(frontierDim[region.frontier]) - regionPos; + const regionDim = min(region.maxsize, max(region.minsize, targetDim)); + for (var j,box in region.boxes) { + box[pos] = regionPos; + box[dim] = regionDim; } } - - // hand out any slack - //if (target > size+0.5) { - // targetRowSize = (target-size) / rows; - // for (int r=0; regmark>r; r++) - // sizes[regions[r]] += regions[r] * targetRowSize+0.5; } } + /* + * PLACING + * + * The effect of each child is measured against the region it + * occupies - the row/column boundary it is pushing. If this + * region changes then boxes in subsequent regions shift over. + */ + + const place = function() { + if (!numcols or !numrows) { + // not yet initialized + return; + } + if (place_horizontal) { + placeRegions(byCol, numcols, width, "hshrink", "contentwidth", "minwidth", "maxwidth", "width", "x"); + place_horizontal = false; + } + if (place_vertical) { + placeRegions(byRow, numrows, height, "vshrink", "contentheight", "minheight", "maxheight", "height", "y"); + place_vertical = false; + } + } + /* * PACKING * @@ -353,34 +281,50 @@ * adding extra columns. */ + /** establish an ordered list of frontiers for placing */ + const establishFrontier = function(frontiers, frontier) { + for (var i = frontiers.length-1; i>=0; --i) { + var f = frontiers[i]; + if (f == frontier) + return; + if (f > frontier) + continue; + frontiers.splice(i+1, 0, frontier); + return; + } + frontiers.unshift(frontier); + } + /** assign child c to the pack slot specific by col,row */ - var assignSlot = function(c, col, row) { + const assignSlot = function(c, col, row) { var c0 = c[0]; - if (!colregions.contains(c) or c0.v_col != col) { - // col-related assignments - c0.v_col = col; - colregions.insert(c, c0.v_col+c0.colspan); - } - if (!rowregions.contains(c) or c0.v_row != row) { - // row-related assignments - c0.v_row = row; - rowregions.insert(c, c0.v_row+c0.rowspan); - } + c0.v_col = col; + c0.v_row = row; + var colfrontier = col+c0.colspan; + var rowfrontier = row+c0.rowspan; + // update the number of in-use rows/cols + numcols = max(numcols, colfrontier); + numrows = max(numrows, rowfrontier); + // establish a list of frontier/from-col pairs + // which can be iterated over for solving + byCol.regions.add(c, col, colfrontier); + byRow.regions.add(c, row, rowfrontier); + establishFrontier(byCol.frontiers, colfrontier); + establishFrontier(byRow.frontiers, rowfrontier); } /** pack by column first, expanding rows as required */ - var packByCol = function() { - var frontier = new .vector(); - var fullpass = true; + const packByCol = function() { + const frontier = new .vector(); var nextcol = 0; // the next available col var nextrow = 0; // the next available row var minrows = 0; // the minimum row past a frontier box - var n = $content.numchildren; var c, c0; var f, f0; + // assign col/row values to packed children - for (var j=0; n>j; j++) { - c = $content[j]; + for (var i,c in $content) { c0 = c[0]; + if (!c0.display) { // disregard hidden children c.display = false; @@ -388,13 +332,16 @@ } else { c.display = true; } - if (nextcol!=0 and nextcol+c0.colspan > numcols) { + + if (nextcol!=0 and nextcol+c0.colspan > setsize) { // if we don't fit on the row, jump to the next nextcol=0; nextrow++; } + // check to see if we are making a full pass at the row - fullpass = nextcol==0; + const fullpass = nextcol==0; + // work through frontier boxes until a suitable position is found PACKME: while (frontier.length>0) { f = frontier.first; @@ -415,7 +362,7 @@ // establish next available row minrows = (minrows == 0) ? f0.v_row+f0.rowspan : min(minrows, f0.v_row+f0.rowspan); - if (nextcol+c0.colspan > numcols) { + if (nextcol+c0.colspan > setsize) { // c will not fit on nextrow if (!fullpass) { // if not a full pass, try next immediate row @@ -438,7 +385,7 @@ // next frontier f = frontier.after(f); } - if (numcols >= nextcol+c0.colspan) { + if (setsize >= nextcol+c0.colspan) { // fits in the col after frontier break; } @@ -464,22 +411,18 @@ // update the number of rows numrows = max(numrows, c0.v_row+c0.rowspan); } - // always want to do this after packing - place(true, true); } /** pack by row first - for comments see packByCol **/ var packByRow = function() { var frontier = new .vector(); - var fullpass = true; var nextrow = 0; // the next available row var nextcol = 0; // the next available col var mincols = 0; // the minimum col past a frontier box - var n = $content.numchildren; var c, c0; var f, f0; + // assign row/col values to packed children - for (var j=0; n>j; j++) { - c = $content[j]; + for (var i,c in $content) { c0 = c[0]; // disregard hidden children if (!c0.display) { @@ -488,13 +431,14 @@ } else { c.display = true; } - if (nextrow!=0 and nextrow+c0.rowspan > numrows) { + if (nextrow!=0 and nextrow+c0.rowspan > setsize) { // does not fit in the column, jump to next row nextrow=0; nextcol++; } + // check to see if we are making a full pass at the col - fullpass = nextrow==0; + const fullpass = nextrow==0; // work through frontier boxes until a suitable position is found PACKME: while (frontier.length>0) { @@ -516,7 +460,7 @@ // establish next available col mincols = (mincols == 0) ? f0.v_col+f0.colspan : min(mincols, f0.v_col+f0.colspan); - if (nextrow+c0.rowspan > numrows) { + if (nextrow+c0.rowspan > setsize) { // c will not fit on nextcol if (!fullpass) { // if not a full pass, try next immediate col @@ -535,7 +479,7 @@ // next frontier f = frontier.after(f); } - if (numrows >= nextrow+c0.rowspan) { + if (setsize >= nextrow+c0.rowspan) { // fits on this row after frontier break; } @@ -556,27 +500,44 @@ // prepare for next iteration mincols = 0; // reset mincols nextrow += c0.rowspan; // bump up nextrow - // update the number of cols - numcols = max(numcols, c0.v_col+c0.colspan); } - // always want to do this after packing - place(true, true); } + const getIndice = function(bySet) { + const f2i = {}; + for (var i,f in bySet.frontiers) + f2i[f] = i; + bySet.frontier2index = f2i; + } + /************************************ ****** SCHEDULING FUNCTIONS ******** ************************************/ /** ambiguous function to invoke specific packing function */ var pack = function() { + // reset! + numcols = 0; + numrows = 0; + byCol.regions = new Regions(); + byCol.frontiers = []; + byRow.regions = new Regions(); + byRow.frontiers = []; + // pack! userows ? packByRow() : packByCol(); + getIndice(byCol); + getIndice(byRow); pack_children = false; } thisbox.Resize ++= function(v) { - if (pack_children) + if (pack_children) { pack(); - else place(true, true); + // always want to place after packing + place_horizontal = true; + place_vertical = true; + } + place(); } var schedulePack = function() { @@ -584,11 +545,6 @@ reflow(); } - var sc_number = "number"; - var checkInt = function(v) { - cascade = typeof(v)==sc_number ? v : vexi.string.parseInt(v); - } - var invokePack = function(v) { if (v == trapee[trapname]) return; cascade = v; @@ -597,6 +553,7 @@ var invokePlaceWidth = function(v) { if (v != trapee[trapname]) { + place_horizontal = true; reflow(); } cascade = v; @@ -604,15 +561,47 @@ var invokePlaceHeight = function(v) { if (v != trapee[trapname]) { + place_vertical = true; reflow(); } cascade = v; } + var sc_number = "number"; + var checkInt = function(v) { + cascade = typeof(v)==sc_number ? v : vexi.string.parseInt(v); + } + /**************************** ******** PUBLIC API ******** ****************************/ + thisbox.height ++= invokePlaceHeight; + thisbox.width ++= invokePlaceWidth; + + thisbox.cols ++= function(v) { + if (userows or v != setsize) { + setsize = v; + userows = false; + orient = "vertical"; // symbolic + schedulePack(); + } + return; + } + + thisbox.rows ++= function(v) { + if (!userows or v != setsize) { + setsize = v; + userows = true; + orient = "horizontal"; // symbolic + schedulePack(); + } + return; + } + + thisbox.cols ++= function() { return userows ? numcols : setsize; } + thisbox.rows ++= function() { return userows ? setsize : numrows; } + var gridboxChildren = function(v) { cascade = v; if (v == null) { @@ -621,8 +610,6 @@ return; } $content[i] = null; - colregions.remove(trapee); - rowregions.remove(trapee); schedulePack(); } } @@ -636,7 +623,7 @@ var n = c.numchildren; for (var i=0; n>i; i++) { thisbox.add(c[0]); - } + } return; } // adding a child @@ -659,6 +646,10 @@ v.rowspan ++= invokePack; v.rowspan ++= checkInt; v.display ++= invokePack; + v.maxwidth ++= invokePlaceWidth; + v.minwidth ++= invokePlaceWidth; + v.maxheight ++= invokePlaceHeight; + v.minheight ++= invokePlaceHeight; // we wrap them in an outer box which we can manipulate var b = v.v_gridbox; if (b == null) { @@ -682,8 +673,6 @@ var b = $content[trapname]; if (b) { b.Children --= gridboxChildren; - colregions.remove(b); - rowregions.remove(b); c = b[0]; } else { throw "attempt to get null child from a box"; @@ -710,6 +699,8 @@ var c = $content[trapname]; return c ? c[0] : null; } + + /** no redirect so must redirect this manually */ thisbox.numchildren ++= function() { return $content.numchildren; } /** return the grid wrapper index as grid.indexof(child) == -1 */ @@ -722,46 +713,21 @@ } thisbox.indexof ++= function() { return getindexof; } - thisbox.height ++= invokePlaceHeight; - thisbox.width ++= invokePlaceWidth; - - thisbox.cols ++= function(v) { - if (userows or v != numcols) { - numcols = v; - numrows = 0; - userows = false; - orient = "vertical"; - schedulePack(); - } - return; - } - - thisbox.rows ++= function(v) { - if (!userows or v != numrows) { - numcols = 0; - numrows = v; - userows = true; - orient = "horizontal"; - schedulePack(); - } - return; - } - - thisbox.cols ++= function() { return numcols; } - thisbox.rows ++= function() { return numrows; } - /** forward align property to children */ - thisbox.align ++= function(v) { + thisbox.gridalign ++= function(v) { + cascade = v; for (var i,box in $content) { box.align = v; } - return; } - /** prevent breaking of grid */ - thisbox.layout ++= function(v) { - throw new vexi.js.Exception("Layout is fixed on grid"); - } + thisbox.layout ++= static.layoutRead; + thisbox.layout ++= static.layoutWrite; </ui:box> -</vexi> \ No newline at end of file + + /** prevent breaking of grid */ + static.layoutWrite = function(v) { throw new vexi.js.Exception("Layout is fixed on grid"); } + static.layoutRead = function() { return "grid"; } + +</vexi> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ Rapidly troubleshoot problems before they affect your business. Most IT organizations don't have a clear picture of how application performance affects their revenue. With AppDynamics, you get 100% visibility into your Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro! http://pubads.g.doubleclick.net/gampad/clk?id=84349351&iu=/4140/ostg.clktrk _______________________________________________ Vexi-svn mailing list Vexi-svn@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/vexi-svn