diff --git a/lib/wController/controller.h b/lib/wController/controller.h index 7372a2d..f655401 100644 --- a/lib/wController/controller.h +++ b/lib/wController/controller.h @@ -16,10 +16,15 @@ #include #include +namespace U { + class Connector; +}; + namespace C { class Controller : public QObject { Q_OBJECT + friend class U::Connector; public: enum ModelType { string, diff --git a/lib/wServerUtils/connector.cpp b/lib/wServerUtils/connector.cpp index 724df6e..5fecd07 100644 --- a/lib/wServerUtils/connector.cpp +++ b/lib/wServerUtils/connector.cpp @@ -43,6 +43,9 @@ U::Connector::~Connector() ctrl->unregisterController(); } } + + disconnect(server, SIGNAL(newConnection(const W::Socket&)), this, SLOT(onNewConnection(const W::Socket&))); + disconnect(server, SIGNAL(closedConnection(const W::Socket&)), this, SLOT(onClosedConnection(const W::Socket&))); } void U::Connector::addIgnoredNode(const W::String& name) @@ -115,7 +118,7 @@ void U::Connector::onClosedConnection(const W::Socket& socket) std::multimap::const_iterator end = controllers.upper_bound(name); for (; beg != end; ++beg) { - beg->second->unsubscribe(); + beg->second->dropSubscribed(); beg->second->unregisterController(); } diff --git a/lib/wSocket/server.cpp b/lib/wSocket/server.cpp index 9b7f6ad..d9a5289 100644 --- a/lib/wSocket/server.cpp +++ b/lib/wSocket/server.cpp @@ -110,6 +110,10 @@ W::Socket * W::Server::createSocket(QWebSocket* socket) } Socket *wSocket = new Socket(name, socket, connectionId, this); + if (connections.find(connectionId) != connections.end()) { + throw new SocketIdDuplectation(connectionId); + } + connections[connectionId] = wSocket; connect(wSocket, SIGNAL(connected()), SLOT(onSocketConnected())); connect(wSocket, SIGNAL(disconnected()), SLOT(onSocketDisconnected())); diff --git a/lib/wSocket/server.h b/lib/wSocket/server.h index eeeb7b1..5c6bdc0 100644 --- a/lib/wSocket/server.h +++ b/lib/wSocket/server.h @@ -73,6 +73,16 @@ namespace W std::string getMessage() const{return "An attempt to access non existing socket";} }; + + class SocketIdDuplectation: + public Utils::Exception + { + public: + SocketIdDuplectation(uint64_t p_id):Exception(),id(p_id){} + uint64_t id; + + std::string getMessage() const{return "An attempt to create a socket with duplicating id == " + std::to_string(id);} + }; }; } diff --git a/lib/wSocket/socket.cpp b/lib/wSocket/socket.cpp index fa26a92..c227faf 100644 --- a/lib/wSocket/socket.cpp +++ b/lib/wSocket/socket.cpp @@ -106,8 +106,10 @@ void W::Socket::onSocketConnected() void W::Socket::onSocketDisconnected() { - state = disconnected_s; - emit disconnected(); + if (state != disconnected_s) { + state = disconnected_s; + emit disconnected(); + } } void W::Socket::onBinaryMessageReceived(const QByteArray& ba) diff --git a/libjs/utils/globalMethods.js b/libjs/utils/globalMethods.js index da47d1e..1a9a28f 100644 --- a/libjs/utils/globalMethods.js +++ b/libjs/utils/globalMethods.js @@ -1,71 +1,73 @@ "use strict"; -global.W = { - extend: function () { - var lTarget = arguments[0] || {}; - var lIndex = 1; - var lLength = arguments.length; - var lDeep = false; - var lOptions, lName, lSrc, lCopy, lCopyIsArray, lClone; - - // Handle a deep copy situation - if (typeof lTarget === "boolean") { - lDeep = lTarget; - lTarget = arguments[1] || {}; - // skip the boolean and the target - lIndex = 2; - } - - // Handle case when target is a string or something (possible in deep - // copy) - if (typeof lTarget !== "object" && typeof lTarget != "function") { - lTarget = {}; - } - - if (lLength === lIndex) { - lTarget = this; - --lIndex; - } - - for (; lIndex < lLength; lIndex++) { - // Only deal with non-null/undefined values - if ((lOptions = arguments[lIndex]) != undefined) { - // Extend the base object - for (lName in lOptions) { - lSrc = lTarget[lName]; - lCopy = lOptions[lName]; - - // Prevent never-ending loop - if (lTarget === lCopy) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if (lDeep && lCopy && (Object.isObject(lCopy) || (lCopyIsArray = Array.isArray(lCopy)))) { - if (lCopyIsArray) { - lCopyIsArray = false; - lClone = lSrc && Array.isArray(lSrc) ? lSrc : []; - - } else { - lClone = lSrc && Object.isObject(lSrc) ? lSrc : {}; - } - - // Never move original objects, clone them - lTarget[lName] = Object.extend(lDeep, lClone, lCopy); - - // Don't bring in undefined values +function extend () { + var lTarget = arguments[0] || {}; + var lIndex = 1; + var lLength = arguments.length; + var lDeep = false; + var lOptions, lName, lSrc, lCopy, lCopyIsArray, lClone; + + // Handle a deep copy situation + if (typeof lTarget === "boolean") { + lDeep = lTarget; + lTarget = arguments[1] || {}; + // skip the boolean and the target + lIndex = 2; + } + + // Handle case when target is a string or something (possible in deep + // copy) + if (typeof lTarget !== "object" && lTarget instanceof Function) { + lTarget = {}; + } + + if (lLength === lIndex) { + lTarget = this; + --lIndex; + } + + for (; lIndex < lLength; lIndex++) { + // Only deal with non-null/undefined values + if ((lOptions = arguments[lIndex]) != undefined) { + // Extend the base object + for (lName in lOptions) { + lSrc = lTarget[lName]; + lCopy = lOptions[lName]; + + // Prevent never-ending loop + if (lTarget === lCopy) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if (lDeep && lCopy && (typeof lCopy === "object" || (lCopyIsArray = lCopy instanceof Array))) { + if (lCopyIsArray) { + lCopyIsArray = false; + lClone = lSrc && lSrc instanceof Array ? lSrc : []; + } else { - if (lCopy !== undefined) { - lTarget[lName] = lCopy; - } + lClone = lSrc && typeof lCopy === "object" ? lSrc : {}; + } + + // Never move original objects, clone them + lTarget[lName] = extend(lDeep, lClone, lCopy); + + // Don't bring in undefined values + } else { + if (lCopy !== undefined) { + lTarget[lName] = lCopy; } } } } + } + + // Return the modified object + return lTarget; +} - // Return the modified object - return lTarget; - }, +global.W = { + extend: extend, touchToMouse: function (e) { e.preventDefault(); if (e.touches.length > 1 || (e.type == "touchend" && e.touches.length > 0)) diff --git a/lorgar/views/gridLayout.js b/lorgar/views/gridLayout.js index 712001c..adc9a89 100644 --- a/lorgar/views/gridLayout.js +++ b/lorgar/views/gridLayout.js @@ -19,13 +19,19 @@ W.extend(base, options); Layout.fn.constructor.call(this, controller, base); + this._rh = []; + this._rH = []; + this._rw = []; + this._rW = []; this._lay = [[[]]]; this._cols = [{}]; this._rows = [{}]; + this._sde = []; //size dependent elements }, "append": function(child, row, col, rowSpan, colSpan, aligment) { aligment = aligment || 5; this._addChild(child, aligment); + child.on("sizeDependencyChange", this._onChildSizeDependencyChange.bind(this, child)); rowSpan = rowSpan || 1; colSpan = colSpan || 1; @@ -44,10 +50,17 @@ } var obj = { child: child, + col: col, + row: row, colspan: colSpan, rowspan: rowSpan, a: aligment } + var item = this._c[this._c.length - 1]; + item.col = col; + item.row = row; + item.colspan = colSpan; + item.rowspan = rowSpan; for (i = row; i < tRow; ++i) { for (var j = col; j < tCol; ++j) { @@ -55,6 +68,17 @@ } } + if (child._o.sizeDependency !== Layout.SizeDependency.straight.independent) { + this._sde.push(obj); + if (this._o.sizeDependency === Layout.SizeDependency.straight.independent) { + this.setSizeDependency(child._o.sizeDependency) + } else { + if (this._o.sizeDependency !== child._o.sizeDependency) { + this.setSizeDependency(Layout.SizeDependency.straight.undefined) + } + } + } + this._recountLimits(); if (this._w !== undefined && this._h !== undefined) { this.refreshLay(); @@ -94,9 +118,14 @@ "clear": function() { Layout.fn.clear.call(this); + this._rh = []; + this._rH = []; + this._rw = []; + this._rW = []; this._lay = [[[]]]; this._cols = [{}]; this._rows = [{}]; + this._sde = []; }, "_onChildChangeLimits": function() { var notification = this._recountLimits(); @@ -176,6 +205,10 @@ } }, "_recountLimits": function() { + this._rh = []; + this._rH = []; + this._rw = []; + this._rW = []; this._cols = []; this._rows = []; var i, j; @@ -185,35 +218,38 @@ for (i = 0; i < this._lay.length; ++i) { while (!this._rows[i]) { - this._rows.push({}); + this._rows.push({ + count: 0, + min: 0, + max: Infinity + }); } for (j = 0; j < this._lay[i].length; ++j) { while (!this._cols[j]) { - this._cols.push({}); + this._cols.push({ + count: 0, + min: 0, + max: Infinity + }); } for (var k = 0; k < this._lay[i][j].length; ++k) { var e = this._lay[i][j][k]; + ++this._cols[j].count; + ++this._rows[i].count; 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; + var colMinW = this._cols[j].min; + var rowMinH = this._rows[i].min; + var colMaxW = this._cols[j].max; + var rowMaxH = this._rows[i].max; 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 @@ -223,9 +259,6 @@ 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 @@ -234,148 +267,159 @@ 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]); + multiLimits(this._cols, multiCols, true, this._rw, this._rW); + multiLimits(this._rows, multiRows, false, this._rh, this._rH); + + for (i = 0; i < this._sde.length; ++i) { + var dep = this._sde[i]; + var cols = []; + var rows = []; + var tdwMax = 0; + var tdwMin = 0; + var tdhMax = 0; + var tdhMin = 0; + for (var c = 0; c < dep.colspan; ++c) { + var col = this._cols[dep.col + c]; + tdwMax += col.max; + tdwMin += col.min; + if (col.min < col.max) { + cols.push(col); } } - - 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; + for (var r = 0; r < dep.rowspan; ++r) { + var row = this._rows[dep.row + r]; + tdhMax += row.max; + tdhMin += row.min; + if (row.min < row.max) { + rows.push(row); + } + } + var cMaxWidth, cMinWidth, cMaxHeight, cMinHeight; + var opts = dep.child._o; + var cmw = true; + var cMw = true; + var cmh = true; + var cMh = true; + while (cols.length || rows.length) { + switch (opts.sizeDependency) { + case Layout.SizeDependency.straight.straight: + if (cMh) { + cMaxWidth = dep.child.getMaxWidthForHeight(tdhMax); + cMh = false; } - } - 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 (cmh) { + cMinWidth = dep.child.getMinWidthForHeight(tdhMin); + cmh = false; } - } - - 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 (cMw) { + cMaxHeight = dep.child.getMaxHeightForWidth(tdwMax); + cMw = false; } - } - - 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 (cmw) { + cMinHeight = dep.child.getMinHeightForWidth(tdwMin); + cmw = false; } + break; + case Layout.SizeDependency.straight.reversed: + if (cMh) { + cMinWidth = dep.child.getMinWidthForHeight(tdhMax); + cMh = false; + } + if (cmh) { + cMaxWidth = dep.child.getMaxWidthForHeight(tdhMin); + cmh = false; + } + if (cMw) { + cMinHeight = dep.child.getMinHeightForWidth(tdwMax); + cMw = false; + } + if (cmw) { + cMaxHeight = dep.child.getMaxHeightForWidth(tdwMin); + cmw = false; + } + break; + } + var changed = false; + + if (!changed && cMinWidth > tdwMin) { + var wDiff = cMinWidth - tdwMin; + var change = wDiff - adjust(wDiff, cols, true); + if (change > 0) { + changed = true; + cmw = true; + tdwMin += change; } - - if (leftMax < 1) { - leftMax = 0; + } + if (!changed && cMinHeight > tdhMin) { + var hDiff = cMaxHeight - tdhMin; + var change = hDiff - adjust(hDiff, rows, true); + if (change > 0) { + changed = true; + cmh = true; + tdhMin += change; } } + if (!changed && cMaxWidth < tdwMax) { + var wDiff = tdwMax - cMaxWidth; + if (wDiff < Infinity) { + var change = wDiff - adjust(wDiff, cols, false); + if (change > 0) { + changed = true; + cMw = true; + tdwMax -= change; + } + } else { + constrain(cMaxWidth, cols, false); + changed = true; + cMw = true; + tdwMax = cMaxWidth; + } + } + if (!changed && cMaxHeight < tdhMax) { + var hDiff = tdhMax - cMaxHeight; + if (hDiff < Infinity) { + var change = hDiff - adjust(hDiff, rows, false); + if (change > 0) { + changed = true; + cMh = true; + tdhMax -= change; + } + } else { + constrain(cMaxHeight, rows, false); + changed = true; + cMh = true; + tdhMax = cMaxHeight; + } + } + if (!changed) { + break; + } } } - var minWidth = 0; - var minHeight = 0; - var maxWidth = 0; - var maxHeight = 0; + var tw = total(this._cols, this._rw, this._rW); + var th = total(this._rows, this._rh, this._rH); - 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; + this._storedCols = []; + this._storedRows = []; + if (this._o.sizeDependency !== Layout.SizeDependency.straight.independent) { + W.extend(true, this._storedCols, this._cols); + W.extend(true, this._storedRows, this._rows); } - return this._setLimits(minWidth, minHeight, maxWidth, maxHeight); + return this._setLimits(tw.min, th.min, tw.max, th.max); }, "refreshLay": function() { + if (this._o.sizeDependency !== Layout.SizeDependency.straight.independent) { + this._cols = []; + this._rows = []; + W.extend(true, this._cols, this._storedCols); + W.extend(true, this._rows, this._storedRows); + } + var totalMaxW = 0; var totalMaxH = 0; var totalMinW = 0; @@ -386,10 +430,6 @@ 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; @@ -406,7 +446,42 @@ this._cols[i].cur = this._cols[i][keyW] * kW; } } else { - distribute(this._w, this._cols); + distribute(this._w, this._cols, this._rw, this._rW); + } + + for (i = 0; i < this._sde.length; ++i) { + var dep = this._sde[i]; + var rows = []; + var tdw = 0; + var tdhMax = 0; + var tdhMin = 0; + for (var c = 0; c < dep.colspan; ++c) { + tdw += this._cols[dep.col + c].cur; + } + for (var r = 0; r < dep.rowspan; ++r) { + var row = this._rows[dep.row + r]; + tdhMax += row.max; + tdhMin += row.min; + if (row.min < row.max) { + rows.push(row); + } + } + + if (rows.length > 0) { + var cMaxHeight = dep.child.getMaxHeightForWidth(tdw); + var cMinHeight = dep.child.getMinHeightForWidth(tdw); + if (cMaxHeight < tdhMax) { + adjust(tdhMax - cMaxHeight, rows, false); + } + if (cMinHeight > tdhMin) { + adjust(cMinHeight - cMinHeight, rows, true); + } + } + } + + for (i = 0; i < this._rows.length; ++i) { + totalMaxH += this._rows[i].max; + totalMinH += this._rows[i].min; } if (this._h <= totalMinH || this._h >= totalMaxH) { @@ -424,26 +499,43 @@ this._rows[i].cur = this._rows[i][keyH] * kH; } } else { - distribute(this._h, this._rows); + distribute(this._h, this._rows, this._rh, this._rH); } this._positionElements(); }, "removeChild": function(child) { - Layout.fn.removeChild.call(this, child); + var obj = Layout.fn.removeChild.call(this, child); + var found = obj !== undefined; + var layObject; - 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); + if (found) { + child.off("sizeDependencyChange") //assuming a child is supposed to have only one parent that should be okay + var cs = obj.colspan; + var rs = obj.rowspan; + var row = obj.row; + var col = obj.col; + for (var i = 0; i < rs; ++ i) { + for (var j = 0; j < cs; ++j) { + var cell = this._lay[row + i][col + j]; + for (var k = 0; k < cell.length; ++k) { + if (cell[k].child === child) { + layObject = cell[k]; + cell.splice(k, 1); + } } } } + + var di = this._sde.indexOf(layObject); + if (di !== -1) { + this._sde.splice(di, 1); + this._recalculateSizeDependency(); //TODO need to recalculate the grid, but not sure about the order of triggers. + } + + this._cleanupLay(); + this._recountLimits(); + this.refreshLay(); } - - this._cleanupLay(); - this._recountLimits(); - this.refreshLay(); }, "setSize": function(w, h) { View.fn.setSize.call(this, w, h); @@ -451,11 +543,40 @@ if (this._c.length) { this.refreshLay(); } + }, + "_recalculateSizeDependency": function() { + if (this._sde.length === 0) { + this.setSizeDependency(Layout.SizeDependency.straight.independent); + } else { + var sd = this._sde[0]._o.sizeDependency; + for (var d = 0; d < this._sde.length; ++d) { + var csd = this._sde[d]._o.sizeDependency + if (sd !== csd) { + sd = Layout.SizeDependency.straight.undefined; + } + if (sd === Layout.SizeDependency.straight.undefined) { + break; + } + } + if (sd !== this._o.sizeDependency) { + this.setSizeDependency(sd); + } + } + }, + "_onChildSizeDependencyChange": function(child, d) { + if (d === Layout.SizeDependency.straight.independent) { + var index = this._getChildObjectIndex(child); + var obj = this._c[index]; //I assume it presents among children + + var di = this._sde.indexOf(obj); + this._sde.splice(di, 1); + } + this._recalculateSizeDependency(); //TODO need to recalculate the grid, but not sure about the order of triggers. } }); - function distribute (size, array) { - var i, portion; + function distribute (size, array, rm, rM) { + var i; var cMax = []; for (i = 0; i < array.length; ++i) { array[i].cur = array[i].min; @@ -465,32 +586,247 @@ cMax.push(array[i]); } } - cMax.sort(GridLayout._candidatesSortMax); + for (i = 0; i < rm.length && size > 1; ++i) { + var rule = rm[i]; + var trm = 0; + var mems = []; + for (var j = 0; j < rule.el.length; ++j) { + var mem = array[rule.el[j]]; + trm += mem.cur; + mems.push(mem); + } + var c = rule.val - trm; + if (c > 1) { + size -= c; + mems.sort(GridLayout._candidatesSortMax); + c -= deliver(mems, c); + if (c > 1) { + var p = c / rule.el.length; + for (j = 0; j < rule.el.length; ++j) { + array[rule.el[j]].cur += p; + } + } + } + } + + cMax.sort(GridLayout._candidatesSortMax); + size -= deliver(cMax, size); + + for (i = 0; i < rM.length; ++i) { + var rule = rm[i]; + var trm = 0; + var mems = []; + for (var j = 0; j < rule.el.length; ++j) { + var mem = array[rule.el[j]]; + trm += mem.cur; + mems.push(mem); + } + var c = trm - rule.val; + if (c > 1) { + size += c; + var p = c / mems.length; + for (j = 0; j < mems.length; ++j) { + mems.cur -= p; + } + } + } + + if (size) { + var portion = size / array.length; + for (i = 0; i < array.length; ++i) { + array.cur += portion; + } + } + } + + function deliver(cMax, size) { + var oSize = size; while (cMax.length && size) { - portion = size / cMax.length; + var portion = size / cMax.length; var last = cMax[cMax.length -1]; - if (portion >= last.max) { - size -= last.max - last.cur; + var avail = last.max - last.cur; + if (portion >= avail) { + size -= avail; last.cur = last.max; cMax.pop(); } else { - for (i = 0; i < cMax.length; ++i) { + for (var 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; + return oSize - size; + } + + function multiLimits(arr, elements, w, rm, rM) { + var mind = "minWidth"; + var maxd = "maxWidth"; + var spand = "colspan"; + if (!w) { + mind = "minHeight"; + maxd = "maxHeight"; + spand = "rowspan"; + } + for (var i = 0; i < elements.length; ++i) { + var e = elements[i].e; + var pos = elements[i].p; + var span = e[spand]; + var target = pos + span; + var minSize = 0; + var maxSize = 0; + var flexible = []; + var el = []; + for (var j = pos; j < target; ++j) { + minSize += arr[j].min; + maxSize += arr[j].max; + el.push(j); + if (arr[j].min < arr[j].max) { + flexible.push(arr[j]); + } + } + + var leftMin = e.child._o[mind] - minSize; + var leftMax = maxSize - e.child._o[maxd]; + var naMin = adjust(leftMin, flexible, true); + var naMax = adjust(leftMax, flexible, false); + if (naMin > 0) { + rm.push({ + el: el, + val: e.child._o[mind] + }); + } + if (naMax > 0) { + rM.push({ + el: el, + val: e.child._o[maxd] + }); } } } + function adjust(space, arr, isMin) { + while (space > 0 && arr.length > 0) { + var portion = space / arr.length; + + for (var j = 0; j < arr.length; ++j) { + var mem = arr[j]; + var lPortion = Math.min(mem.max - mem.min, portion); + if (isMin === true) { + mem.min += lPortion; + } else { + mem.max -= lPortion; + } + space -= lPortion; + if (mem.min === mem.max) { + arr.splice(j, 1); + break; + } + } + + if (space < 1) { + space = 0; + } + } + if (space < 0) { + space = 0; + } + return space; + } + + function constrain(space, arr, isMin) { + while (space > 0 && arr.length > 0) { + var portion = space / arr.length; + + for (var j = 0; j < arr.length; ++j) { + var mem = arr[j]; + var lPortion + if (isMin === true) { + lPortion = Math.min(mem.max, portion); + mem.min = lPortion; + } else { + lPortion = Math.max(mem.min, portion); + mem.max = lPortion; + } + space -= lPortion; + if (mem.min === mem.max) { + arr.splice(j, 1); + break; + } + } + + if (space < 1) { + space = 0; + } + } + } + + function total(arr, rm, rM) { + var min = 0; + var max = 0; + var am = []; + for (var i = 0; i < arr.length; ++i) { + var el = arr[i]; + if (el.count === 0) { + el.min = 0; + el.max = 0; + } else { + min += el.min; + el.max = Math.max(el.max, el.min); + max += el.max; + } + am.push({ + min: el.min, + max: el.max + }); + } + + min += shove(rm, am, true); + max -= shove(rM, am, false); + + return { + min: min, + max: Math.max(max, min) + } + } + + function shove(rules, arr, isMin) { + var dim = "max"; + if (isMin === true) { + dim = "min"; + } + var change = 0; + for (var i = 0; i < rules.length; ++i) { + var rule = rules[i]; + var val = rule.val; + var tr = 0; + for (var j = 0; j < rule.el.length; ++j) { + tr += arr[rule.el[j]][dim]; + } + var c; + if (isMin) { + c = val - tr; + } else { + c = tr - val; + } + if (c > 0) { + var p = c / rule.el.length; + for (j = 0; j < rule.el.length; ++j) { + if (isMin) { + arr[rule.el[j]][dim] += p; + } else { + arr[rule.el[j]][dim] -= p; //TODO a possibility of going under zero + } + } + change += c; + } + } + return change; + } + GridLayout._candidatesSortMax = function(a, b) { return b.max - a.max; } diff --git a/lorgar/views/image.js b/lorgar/views/image.js index 7f2dd2d..f28dea4 100644 --- a/lorgar/views/image.js +++ b/lorgar/views/image.js @@ -9,27 +9,96 @@ var View = require("views/view"); var Image = View.inherit({ - "className": "Image", - "constructor": function(controller, options) { - var base = {}; + className: "Image", + constructor: function(controller, options) { + var base = { + preserveAspectRatio: false, + aspectRatio: 1 // width / height + }; W.extend(base, options) + if (base.preserveAspectRatio) { + base.sizeDependency = View.SizeDependency.straight.straight; + if (base.maxWidth || base.maxHeight) { + var max = closestSize(base.maxWidth || Infinity, base.maxHeight || Infinity, base.aspectRatio); //TODO really not sure about that + base.maxWidth = max.w; + base.maxHeight = max.h; + } + if (base.minWidth || base.minHeight) { + var min = closestSize(base.minWidth || 0, base.minHeight || 0, base.aspectRatio); + base.minWidth = min.w; + base.minHeight = min.h; + } + } var el = document.createElement("img"); View.fn.constructor.call(this, controller, base, el); controller.needData(); }, - "destructor": function() { + destructor: function() { this._f.dontNeedData(); View.fn.destructor.call(this); }, - "_onData": function() { + _onData: function() { if (this._f.hasData()) { - this._e.src = "data:" + this._f.getMimeType() + ";base64," + this._f.data.base64(); + this._e.src = "data:" + this._f.getMimeType() + ";base64," + this._f.data.base64(); //TODO figure out if that could be done with URLObject + } + }, + _closestSize: function(w, h) { + if (this._o.preserveAspectRatio) { + return closestSize(w, h, this._o.aspectRatio); + } else { + return View.fn._closestSize.call(this, w, h); + } + }, + "getMinWidthForHeight": function(h) { + if (this._o.preserveAspectRatio) { + return this.constrainWidth(h * this._o.aspectRatio); + } else { + return this._o.minWidth; + } + }, + "getMaxWidthForHeight": function(h) { + if (this._o.preserveAspectRatio) { + return this.constrainWidth(h * this._o.aspectRatio); + } else { + return this._o.maxWidth; + } + }, + "getMinHeightForWidth": function(w) { + if (this._o.preserveAspectRatio) { + return this.constrainHeight(w / this._o.aspectRatio); + } else { + return this._o.minHeight; + } + }, + "getMaxHeightForWidth": function(w) { + if (this._o.preserveAspectRatio) { + return this.constrainHeight(w / this._o.aspectRatio); + } else { + return this._o.maxHeight; } } }); + function closestSize(w, h, ratio) { + var nh = w / ratio; + var nw = h * ratio; + var dh = Math.abs(h - nh); + var dw = Math.abs(w - nw); + if (dh > dw) { + return { + w: nw, + h: h + } + } else { + return { + w: w, + h: nh + } + } + } + return Image; }) })(); diff --git a/lorgar/views/layout.js b/lorgar/views/layout.js index f41c45c..901bc80 100644 --- a/lorgar/views/layout.js +++ b/lorgar/views/layout.js @@ -172,14 +172,26 @@ } }, "removeChild": function(child) { + var i = this._getChildObjectIndex(child); + if (i !== -1) { + var obj = this._c[i]; + this._removeChildByIndex(i); + return obj; + } + }, + "_getChildObjectIndex": function(child) { + var obj; var i; for (i = 0; i < this._c.length; ++i) { - if (this._c[i].c === child) { + obj = this._c[i]; + if (obj.c === child) { break; } } - if (i !== this._c.length) { - this._removeChildByIndex(i); + if (obj !== undefined) { + return i; + } else { + return -1; } }, "_removeChildByIndex": function(i) { diff --git a/lorgar/views/player.js b/lorgar/views/player.js index 4b42cb9..916cdd2 100644 --- a/lorgar/views/player.js +++ b/lorgar/views/player.js @@ -107,7 +107,9 @@ case ItemType.straight.picture: this._picture = new Image(ctrl, { maxWidth: 100, - maxHeight: 100 + maxHeight: 100, + preserveAspectRatio: true, + aspectRatio: 1 }); this.append(this._picture, 0, 0, 3, 1); break; diff --git a/lorgar/views/view.js b/lorgar/views/view.js index cc3e745..a3db2a7 100644 --- a/lorgar/views/view.js +++ b/lorgar/views/view.js @@ -5,11 +5,13 @@ var defineArray = []; defineArray.push("lib/utils/subscribable"); defineArray.push("views/helpers/draggable"); + defineArray.push("lib/utils/enum"); define(moduleName, defineArray, function view_module() { var counter = 0; var Subscribable = require("lib/utils/subscribable"); var Draggable = require("views/helpers/draggable"); + var Enum = require("lib/utils/enum"); var View = Subscribable.inherit({ "className": "View", @@ -22,7 +24,8 @@ minHeight: 0, maxWidth: Infinity, maxHeight: Infinity, - draggable: false + draggable: false, + sizeDependency: SizeDependency.straight.independent }; W.extend(base, options); @@ -86,6 +89,12 @@ this._onAddProperty(prop.k, prop.p); } }, + _closestSize: function(w, h) { + return { //this is for cases when dimensions depend on each other, must be implemented in child classes + w: w, //supposed to return closest to given size basing on dependency + h: h + } + }, "constrainHeight": function(h) { h = Math.max(h, this._o.minHeight); h = Math.min(h, this._o.maxHeight); @@ -203,33 +212,48 @@ return needToTell && this._events.changeLimits && this._events.changeLimits.length; //to see if someone actually going to listen that event, if not - return result }, "setMaxSize": function(w, h) { - this._o.maxWidth = w; - this._o.maxHeight = h; - - if (this._w !== undefined && this._h !== undefined) { - this.setSize(this._w, this._h); + if (this._o.maxHeight !== h || this._o.maxWidth !== w) { + this._o.maxWidth = w; + this._o.maxHeight = h; + + if (this._w !== undefined && this._h !== undefined) { + this.setSize(this._w, this._h); + } + + this.trigger("changeLimits", this); } - - this.trigger("changeLimits", this); }, "setMinSize": function(w, h) { - this._o.minWidth = w; - this._o.minHeight = h; - - if (this._w !== undefined && this._h !== undefined) { - this.setSize(this._w, this._h); + if (this._o.minHeight !== h || this._o.minWidth !== w) { + this._o.minWidth = w; + this._o.minHeight = h; + + if (this._w !== undefined && this._h !== undefined) { + this.setSize(this._w, this._h); + } + + this.trigger("changeLimits", this); } - - this.trigger("changeLimits", this); }, "setSize": function(w, h) { - this._w = this.constrainWidth(w); - this._h = this.constrainHeight(h); + var nw = this.constrainWidth(w); + var nh = this.constrainHeight(h); - this._e.style.width = this._w + "px"; - this._e.style.height = this._h + "px"; + if (this._o.sizeDependency !== SizeDependency.straight.independent) { + var obj = this._closestSize(nw, nh); + nw = obj.w; + nh = obj.h; + } - this.trigger("resize", this._w, this._h); + if (nw !== this._w || nh !== this._h) { + this._w = nw; + this._h = nh; + + this._e.style.width = this._w + "px"; + this._e.style.height = this._h + "px"; + + this.trigger("resize", this._w, this._h); + } }, "setTop": function(t) { this._y = t; @@ -237,6 +261,22 @@ }, "trySize": function(w, h) { return !(w < this._o.minWidth || h < this._o.minHeight || w > this._o.maxWidth || h > this._o.maxHeight) + }, + "setSizeDependency": function(d) { + this._o.sizeDependency = d; + this.trigger("sizeDependencyChange", d); + }, + "getMinWidthForHeight": function(h) { + return this._o.minWidth; + }, + "getMaxWidthForHeight": function(h) { + return this._o.maxWidth; + }, + "getMinHeightForWidth": function(w) { + return this._o.minHeight; + }, + "getMaxHeightForWidth": function(h) { + return this._o.maxHeight; } }); @@ -331,6 +371,13 @@ View: View }; + var SizeDependency = new Enum("SizeDependency"); + SizeDependency.add("independent"); + SizeDependency.add("straight"); + SizeDependency.add("reversed"); + SizeDependency.add("undefined"); + + View.SizeDependency = SizeDependency; return View; }); diff --git a/magnus/pages/album.js b/magnus/pages/album.js index 8aa916d..7ae7130 100644 --- a/magnus/pages/album.js +++ b/magnus/pages/album.js @@ -12,6 +12,7 @@ var VCController = require("../lib/wController/vocabulary"); var Address = require("../lib/wType/address"); var Uint64 = require("../lib/wType/uint64"); var Vocabulary = require("../lib/wType/vocabulary"); +var Boolean = require("../lib/wType/boolean"); var AlbumPage = TempPage.inherit({ "className": "AlbumPage", @@ -28,6 +29,8 @@ var AlbumPage = TempPage.inherit({ imageOptions.insert("maxWidth", new Uint64(200)); imageOptions.insert("minHeight", new Uint64(200)); imageOptions.insert("maxHeight", new Uint64(200)); + imageOptions.insert("aspectRatio", new Uint64(1)); + imageOptions.insert("preserveAspectRatio", new Boolean(true)); this.addItem(this._image, 0, 0, 2, 1, TempPage.Aligment.CenterCenter, new Uint64(TempPage.getModelTypeId(this._image)), imageOptions); var header = this._header = new String(this._address["+"](new Address(["header"])), ""); diff --git a/magnus/pages/song.js b/magnus/pages/song.js index 122b9a4..186102d 100644 --- a/magnus/pages/song.js +++ b/magnus/pages/song.js @@ -8,6 +8,8 @@ var VCController = require("../lib/wController/vocabulary"); var Address = require("../lib/wType/address"); var Uint64 = require("../lib/wType/uint64"); +var Boolean = require("../lib/wType/boolean"); +var Vocabulary = require("../lib/wType/vocabulary"); var SongPage = TempPage.inherit({ "className": "SongPage", @@ -30,8 +32,12 @@ var SongPage = TempPage.inherit({ artist.addProperty("fontFamily", "casualFont"); this.addItem(artist, 2, 1, 1, 1, TempPage.Aligment.CenterTop); + var io = new Vocabulary(); + io.insert("aspectRatio", new Uint64(1)); + io.insert("preserveAspectRatio", new Boolean(true)); + var image = this._image = new Image(this._address["+"](new Address(["image"])), new Uint64(0)); - this.addItem(image, 0, 0, 3, 1, TempPage.Aligment.CenterCenter); + this.addItem(image, 0, 0, 3, 1, TempPage.Aligment.CenterCenter, new Uint64(TempPage.getModelTypeId(image)), io); }, "destructor": function() { this._clearCtrls();