501 lines
21 KiB
JavaScript
501 lines
21 KiB
JavaScript
"use strict";
|
|
(function gridLayout_js() {
|
|
var moduleName = "views/gridLayout";
|
|
|
|
var defineArray = [];
|
|
defineArray.push("views/view");
|
|
defineArray.push("views/layout");
|
|
|
|
define(moduleName, defineArray, function gridLayout_module() {
|
|
var View = require("views/view");
|
|
var Layout = require("views/layout");
|
|
|
|
var GridLayout = Layout.inherit({
|
|
"className": "GridLayout",
|
|
"constructor": function(controller, options) {
|
|
var base = {
|
|
|
|
};
|
|
W.extend(base, options);
|
|
Layout.fn.constructor.call(this, controller, base);
|
|
|
|
this._lay = [[[]]];
|
|
this._cols = [{}];
|
|
this._rows = [{}];
|
|
},
|
|
"append": function(child, row, col, rowSpan, colSpan, aligment) {
|
|
aligment = aligment || 5;
|
|
this._addChild(child, aligment);
|
|
|
|
rowSpan = rowSpan || 1;
|
|
colSpan = colSpan || 1;
|
|
|
|
var tRow = row + rowSpan;
|
|
var tCol = col + colSpan;
|
|
|
|
while (this._lay.length < tRow) {
|
|
this._lay.push([]);
|
|
|
|
}
|
|
for (var i = 0; i < this._lay.length; ++i) {
|
|
while (this._lay[i].length < tCol) {
|
|
this._lay[i].push([]);
|
|
}
|
|
}
|
|
var obj = {
|
|
child: child,
|
|
colspan: colSpan,
|
|
rowspan: rowSpan,
|
|
a: aligment
|
|
}
|
|
|
|
for (i = row; i < tRow; ++i) {
|
|
for (var j = col; j < tCol; ++j) {
|
|
this._lay[i][j].push(obj);
|
|
}
|
|
}
|
|
|
|
this._recountLimits();
|
|
if (this._w !== undefined && this._h !== undefined) {
|
|
this.refreshLay();
|
|
}
|
|
},
|
|
"_cleanupLay": function() {
|
|
var i;
|
|
var rowsC = false;
|
|
var colsC = false;
|
|
while (!rowsC) {
|
|
for (i = 0; i < this._lay[this._lay.length - 1].length; ++i) {
|
|
if (this._lay[this._lay.length - 1][i].length) {
|
|
rowsC = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!rowsC) {
|
|
this._lay.pop()
|
|
rowsC = !this._lay.length;
|
|
colsC = !this._lay.length;
|
|
}
|
|
}
|
|
while (!colsC) {
|
|
for (i = 0; i < this._lay.length; ++i) {
|
|
if (this._lay[i][this._lay[i].length - 1].length) {
|
|
colsC = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!colsC) {
|
|
for (i = 0; i < this._lay.length; ++i) {
|
|
this._lay[i].pop();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"clear": function() {
|
|
Layout.fn.clear.call(this);
|
|
|
|
this._lay = [[[]]];
|
|
this._cols = [{}];
|
|
this._rows = [{}];
|
|
},
|
|
"_onChildChangeLimits": function() {
|
|
var notification = this._recountLimits();
|
|
if (!notification) {
|
|
this.refreshLay();
|
|
}
|
|
},
|
|
"_positionElements": function() {
|
|
var shiftW = 0;
|
|
var shiftH = 0;
|
|
|
|
var positioned = [];
|
|
|
|
for (var i = 0; i < this._lay.length; ++i) {
|
|
shiftW = 0;
|
|
for (var j = 0; j < this._lay[i].length; ++j) {
|
|
for (var k = 0; k < this._lay[i][j].length; ++k) {
|
|
var e = this._lay[i][j][k];
|
|
var child = e.child;
|
|
if (positioned.indexOf(child) === -1) {
|
|
var tWidth = 0;
|
|
var tHeight = 0;
|
|
var s;
|
|
for (s = 0; s < e.colspan; ++s) {
|
|
tWidth += this._cols[j + s].cur;
|
|
}
|
|
for (s = 0; s < e.rowspan; ++s) {
|
|
tHeight += this._rows[i + s].cur;
|
|
}
|
|
child.setSize(tWidth, tHeight);
|
|
|
|
switch (e.a) {
|
|
case Layout.Aligment.LeftTop:
|
|
child.setTop(shiftH);
|
|
child.setLeft(shiftW);
|
|
break;
|
|
case Layout.Aligment.LeftCenter:
|
|
child.setTop(shiftH + (tHeight - child._h) / 2);
|
|
child.setLeft(shiftW);
|
|
break;
|
|
case Layout.Aligment.LeftBottom:
|
|
child.setTop(shiftH + (tHeight - child._h));
|
|
child.setLeft(shiftW);
|
|
break;
|
|
case Layout.Aligment.CenterTop:
|
|
child.setTop(shiftH);
|
|
child.setLeft(shiftW + (tWidth - child._w) / 2);
|
|
break;
|
|
case Layout.Aligment.CenterCenter:
|
|
child.setTop(shiftH + (tHeight - child._h) / 2);
|
|
child.setLeft(shiftW + (tWidth - child._w) / 2);
|
|
break;
|
|
case Layout.Aligment.CenterBottom:
|
|
child.setTop(shiftH + (tHeight - child._h));
|
|
child.setLeft((tWidth - child._w) / 2);
|
|
break;
|
|
case Layout.Aligment.RightTop:
|
|
child.setTop(shiftH);
|
|
child.setLeft(shiftW + (tWidth - child._h));
|
|
break;
|
|
case Layout.Aligment.RightCenter:
|
|
child.setTop((tHeight - child._h) / 2);
|
|
child.setLeft(shiftW + (tWidth - child._h));
|
|
break;
|
|
case Layout.Aligment.RightBottom:
|
|
child.setTop(shiftH + (tHeight - child._h));
|
|
child.setLeft(shiftW + (tWidth - child._h));
|
|
break;
|
|
|
|
}
|
|
positioned.push(child);
|
|
}
|
|
}
|
|
shiftW += this._cols[j].cur;
|
|
}
|
|
shiftH += this._rows[i].cur;
|
|
}
|
|
},
|
|
"_recountLimits": function() {
|
|
this._cols = [];
|
|
this._rows = [];
|
|
var i, j;
|
|
var multiCols = [];
|
|
var multiRows = [];
|
|
var proccessed = Object.create(null);
|
|
|
|
for (i = 0; i < this._lay.length; ++i) {
|
|
while (!this._rows[i]) {
|
|
this._rows.push({});
|
|
}
|
|
for (j = 0; j < this._lay[i].length; ++j) {
|
|
while (!this._cols[j]) {
|
|
this._cols.push({});
|
|
}
|
|
for (var k = 0; k < this._lay[i][j].length; ++k) {
|
|
var e = this._lay[i][j][k];
|
|
|
|
if (proccessed[e.child._id]) {
|
|
this._cols[j].min = this._cols[j].min || 0;
|
|
this._rows[i].min = this._rows[i].min || 0;
|
|
this._cols[j].max = this._cols[j].max || 0;
|
|
this._rows[i].max = this._rows[i].max || 0;
|
|
continue;
|
|
}
|
|
|
|
var colMinW = this._cols[j].min || 0;
|
|
var rowMinH = this._rows[i].min || 0;
|
|
var colMaxW = this._cols[j].max || Infinity;
|
|
var rowMaxH = this._rows[i].max || Infinity;
|
|
|
|
if (e.colspan === 1) {
|
|
this._cols[j].min = Math.max(colMinW, e.child._o.minWidth);
|
|
this._cols[j].max = Math.min(colMaxW, e.child._o.maxWidth);
|
|
} else {
|
|
this._cols[j].min = colMinW;
|
|
this._cols[j].max = colMaxW;
|
|
|
|
multiCols.push({
|
|
p: j,
|
|
e: e
|
|
});
|
|
}
|
|
if (e.rowspan === 1) {
|
|
this._rows[i].min = Math.max(rowMinH, e.child._o.minHeight);
|
|
this._rows[i].max = Math.min(rowMaxH, e.child._o.maxHeight);
|
|
} else {
|
|
this._rows[i].min = rowMinH;
|
|
this._rows[i].max = rowMaxH;
|
|
|
|
multiRows.push({
|
|
p: i,
|
|
e: e
|
|
});
|
|
}
|
|
|
|
proccessed[e.child._id] = true;
|
|
}
|
|
if (!this._lay[i][j].length) {
|
|
this._cols[j].min = this._cols[j].min || 0;
|
|
this._rows[i].min = this._rows[i].min || 0;
|
|
this._cols[j].max = this._cols[j].max || 0;
|
|
this._rows[i].max = this._rows[i].max || 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < multiCols.length; ++i) {
|
|
var e = multiCols[i].e;
|
|
var pos = multiCols[i].p;
|
|
var span = e.colspan;
|
|
var target = pos + span;
|
|
var minSize = 0;
|
|
var maxSize = 0;
|
|
var flexibleColls = [];
|
|
for (j = pos; j < target; ++j) {
|
|
minSize += this._cols[j].min;
|
|
maxSize += this._cols[j].max;
|
|
if (this._cols[j].min < this._cols[j].max) {
|
|
flexibleColls.push(this._cols[j]);
|
|
}
|
|
}
|
|
|
|
var leftMin = e.child._o.minWidth - minSize;
|
|
if (leftMin > 0) {
|
|
while (leftMin > 0 && flexibleColls.length > 0) {
|
|
var portion = leftMin / flexibleColls.length;
|
|
|
|
for (j = 0; j < flexibleColls.length; ++j) {
|
|
var lPortion = Math.min(flexibleColls[j].max, portion);
|
|
flexibleColls[j].min += lPortion;
|
|
leftMin -= lPortion;
|
|
if (flexibleColls[j].min === flexibleColls[j].max) {
|
|
flexibleColls.splice(j, 1);
|
|
break;
|
|
}
|
|
}
|
|
if (leftMin < 1) {
|
|
leftMin = 0;
|
|
}
|
|
}
|
|
}
|
|
var leftMax = maxSize - e.child._o.maxWidth
|
|
if (leftMax > 0) {
|
|
while (leftMax > 0 && flexibleColls.length > 0) {
|
|
var portion = leftMax / flexibleColls.length;
|
|
|
|
for (j = 0; j < flexibleColls.length; ++j) {
|
|
var lPortion = Math.max(flexibleColls[j].min, portion);
|
|
flexibleColls[j].max -= lPortion;
|
|
leftMax -= lPortion;
|
|
if (flexibleColls[j].min === flexibleColls[j].max) {
|
|
flexibleColls.splice(j, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (leftMax < 1) {
|
|
leftMax = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < multiRows.length; ++i) {
|
|
var e = multiRows[i].e;
|
|
var pos = multiRows[i].p;
|
|
var span = e.rowspan;
|
|
var target = pos + span;
|
|
var minSize = 0;
|
|
var maxSize = 0;
|
|
var flexibleRows = [];
|
|
for (j = pos; j < target; ++j) {
|
|
minSize += this._rows[j].min;
|
|
maxSize += this._rows[j].max;
|
|
if (this._rows[j].min < this._rows[j].max) {
|
|
flexibleRows.push(this._rows[j]);
|
|
}
|
|
}
|
|
var leftMin = e.child._o.minHeigh - minSize;
|
|
if (leftMin > 0) {
|
|
while (leftMin > 0 && flexibleRows.length > 0) {
|
|
var portion = leftMin / flexibleRows.length;
|
|
|
|
for (j = 0; j < flexibleRows.length; ++j) {
|
|
var lPortion = Math.min(flexibleRows[j].max, portion);
|
|
flexibleRows[j].min += lPortion;
|
|
leftMin -= lPortion;
|
|
if (flexibleRows[j].min === flexibleRows[j].max) {
|
|
flexibleRows.splice(j, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (leftMin < 1) {
|
|
leftMin = 0;
|
|
}
|
|
}
|
|
}
|
|
var leftMax = maxSize - e.child._o.maxHeigh
|
|
if (leftMax > 0) {
|
|
while (leftMax > 0 && flexibleRows.length > 0) {
|
|
var portion = leftMax / flexibleRows.length;
|
|
|
|
for (j = 0; j < flexibleRows.length; ++j) {
|
|
var lPortion = Math.max(flexibleRows[j].min, portion);
|
|
flexibleRows[j].max -= lPortion;
|
|
leftMax -= lPortion;
|
|
if (flexibleRows[j].min === flexibleRows[j].max) {
|
|
flexibleRows.splice(j, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (leftMax < 1) {
|
|
leftMax = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var minWidth = 0;
|
|
var minHeight = 0;
|
|
var maxWidth = 0;
|
|
var maxHeight = 0;
|
|
|
|
for (i = 0; i < this._rows.length; ++i) {
|
|
minHeight += this._rows[i].min;
|
|
this._rows[i].max = Math.max(this._rows[i].max, this._rows[i].min);
|
|
maxHeight += this._rows[i].max;
|
|
}
|
|
for (i = 0; i < this._cols.length; ++i) {
|
|
minWidth += this._cols[i].min;
|
|
this._cols[i].max = Math.max(this._cols[i].max, this._cols[i].min);
|
|
maxWidth += this._cols[i].max;
|
|
}
|
|
|
|
return this._setLimits(minWidth, minHeight, maxWidth, maxHeight);
|
|
},
|
|
"refreshLay": function() {
|
|
var totalMaxW = 0;
|
|
var totalMaxH = 0;
|
|
var totalMinW = 0;
|
|
var totalMinH = 0;
|
|
var i;
|
|
|
|
for (i = 0; i < this._cols.length; ++i) {
|
|
totalMaxW += this._cols[i].max;
|
|
totalMinW += this._cols[i].min
|
|
}
|
|
for (i = 0; i < this._rows.length; ++i) {
|
|
totalMaxH += this._rows[i].max;
|
|
totalMinH += this._rows[i].min;
|
|
}
|
|
|
|
if (this._w <= totalMinW || this._w >= totalMaxW) {
|
|
var kW;
|
|
var keyW;
|
|
if (this._w <= totalMinW) {
|
|
kW = this._w / totalMinW;
|
|
keyW = "min";
|
|
} else {
|
|
kW = this._w / totalMaxW;
|
|
keyW = "max";
|
|
}
|
|
|
|
for (i = 0; i < this._cols.length; ++i) {
|
|
this._cols[i].cur = this._cols[i][keyW] * kW;
|
|
}
|
|
} else {
|
|
distribute(this._w, this._cols);
|
|
}
|
|
|
|
if (this._h <= totalMinH || this._h >= totalMaxH) {
|
|
var kH;
|
|
var keyH;
|
|
if (this._h <= totalMinH) {
|
|
kH = this._h / totalMinH;
|
|
keyH = "min";
|
|
} else {
|
|
kH = this._h / totalMaxH;
|
|
keyH = "max";
|
|
}
|
|
|
|
for (i = 0; i < this._rows.length; ++i) {
|
|
this._rows[i].cur = this._rows[i][keyH] * kH;
|
|
}
|
|
} else {
|
|
distribute(this._h, this._rows);
|
|
}
|
|
this._positionElements();
|
|
},
|
|
"removeChild": function(child) {
|
|
Layout.fn.removeChild.call(this, child);
|
|
|
|
for (var i = 0; i < this._lay.length; ++i) {
|
|
for (var j = 0; j < this._lay[i].length; ++j) {
|
|
for (var k = 0; k < this._lay[i][j].length; ++k) {
|
|
if (child === this._lay[i][j][k].child) {
|
|
this._lay[i][j].splice(k, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this._cleanupLay();
|
|
this._recountLimits();
|
|
this.refreshLay();
|
|
},
|
|
"setSize": function(w, h) {
|
|
View.fn.setSize.call(this, w, h);
|
|
|
|
if (this._c.length) {
|
|
this.refreshLay();
|
|
}
|
|
}
|
|
});
|
|
|
|
function distribute (size, array) {
|
|
var i, portion;
|
|
var cMax = [];
|
|
for (i = 0; i < array.length; ++i) {
|
|
array[i].cur = array[i].min;
|
|
size -= array[i].min;
|
|
|
|
if (array[i].cur < array[i].max) {
|
|
cMax.push(array[i]);
|
|
}
|
|
}
|
|
cMax.sort(GridLayout._candidatesSortMax);
|
|
|
|
while (cMax.length && size) {
|
|
portion = size / cMax.length;
|
|
var last = cMax[cMax.length -1];
|
|
|
|
if (portion >= last.max) {
|
|
size -= last.max - last.cur;
|
|
last.cur = last.max;
|
|
cMax.pop();
|
|
} else {
|
|
for (i = 0; i < cMax.length; ++i) {
|
|
cMax[i].cur += portion;
|
|
}
|
|
size = 0;
|
|
}
|
|
}
|
|
|
|
if (size) {
|
|
portion = size / array.length;
|
|
for (i = 0; i < array.length; ++i) {
|
|
array.cur += portion;
|
|
}
|
|
}
|
|
}
|
|
|
|
GridLayout._candidatesSortMax = function(a, b) {
|
|
return b.max - a.max;
|
|
}
|
|
|
|
return GridLayout;
|
|
});
|
|
})();
|