blossom
Version:
Modern, Cross-Platform Application Framework
295 lines (266 loc) • 12.8 kB
JavaScript
// ==========================================================================
// Project: Blossom - Modern, Cross-Platform Application Framework
// Copyright: ©2012 Fohr Motion Picture Studios. All rights reserved.
// License: Licensed under the GPLv3 license (see BLOSSOM-LICENSE).
// ==========================================================================
/*globals sc_assert FAST_LAYOUT_FUNCTION BENCHMARK_LAYOUT_FUNCTION */
/*jslint evil:true */
sc_require('layers/layout_function');
if (FAST_LAYOUT_FUNCTION) {
console.log("Using FAST_LAYOUT_FUNCTION");
// This implementation generates optimized source code for each possible
// layout function (there are 9,216). It then uses new Function() to create
// the function with the optimized source code, and returns it. Each layout
// function is only generated once.
SC.GetLayoutFunction = function(hmode, vmode, hmax, vmax, layoutMode) {
sc_assert(hmode >= 0 && hmode < 16);
sc_assert(vmode >= 0 && vmode < 16);
sc_assert(typeof hmax === "boolean");
sc_assert(typeof vmax === "boolean");
sc_assert(layoutMode >= 0 && layoutMode < 9);
var shouldBenchmark = BENCHMARK_LAYOUT_FUNCTION;
var funcName = ['sc', hmode, vmode, hmax, vmax, layoutMode].join('_'),
func = SC.layoutFunctions[funcName],
src;
if (!func) {
src = [];
if (shouldBenchmark) {
src.push(['var benchKey = "', funcName, '";'].join(''));
src.push('SC.Benchmark.start(benchKey);');
}
// Note: layoutMode 5 (top, left) does not depends on pheight, and pwidth
// at all, and thus does not have any post-layout adjustments.
if (layoutMode === 5) {
src.push(' var x, y, width, height;');
// Modes 3 and 5 don't have x adjustment, but they do have y.
} else if (layoutMode === 3 || layoutMode === 7) {
src.push(' var minLayoutWidth,');
src.push(' minLayoutHeight, yAdjustment = 0,');
if (hmax) src.push(' maxLayoutWidth,');
if (vmax) src.push(' maxLayoutHeight,');
src.push(' x, y, width, height;');
// Modes 1 and 6 have x adjustment, but they don't have y.
} else if (layoutMode === 1 || layoutMode === 6) {
src.push(' var minLayoutWidth, xAdjustment = 0,');
src.push(' minLayoutHeight,');
if (hmax) src.push(' maxLayoutWidth,');
if (vmax) src.push(' maxLayoutHeight,');
src.push(' x, y, width, height;');
} else {
src.push(' var minLayoutWidth, xAdjustment = 0,');
src.push(' minLayoutHeight, yAdjustment = 0,');
if (hmax) src.push(' maxLayoutWidth,');
if (vmax) src.push(' maxLayoutHeight,');
src.push(' x, y, width, height;');
}
if (layoutMode !== 5) {
// Clamp pwidth and pheight to min/max layout values. This is a
// requirement for the layout process below: pwidth and pheight are
// assumed to lie within the correct range.
src.push('minLayoutWidth = layout[4]/*minLayoutWidth*/;');
if (hmax) src.push('maxLayoutWidth = layout[5]/*maxLayoutWidth*/;');
src.push('if (pwidth < minLayoutWidth) {');
if (layoutMode !== 3 && layoutMode !== 7) src.push(' xAdjustment = minLayoutWidth - pwidth;'); // a positive number
src.push(' pwidth = minLayoutWidth;');
if (hmax) {
src.push('} else if (pwidth > maxLayoutWidth) {');
if (layoutMode !== 3 && layoutMode !== 7) src.push(' xAdjustment = maxLayoutWidth - pwidth;'); // a negative number
src.push(' pwidth = maxLayoutWidth;');
}
src.push('}');
src.push('minLayoutHeight = layout[6]/*minLayoutHeight*/;');
if (vmax) src.push('maxLayoutHeight = layout[7]/*maxLayoutHeight*/;');
src.push('if (pheight < minLayoutHeight) {');
if (layoutMode !== 1 && layoutMode !== 6) src.push(' yAdjustment = minLayoutHeight - pheight;'); // a positive number
src.push(' pheight = minLayoutHeight;');
if (vmax) {
src.push('} else if (pheight > maxLayoutHeight) {');
if (layoutMode !== 1 && layoutMode !== 6) src.push(' yAdjustment = maxLayoutHeight - pheight;'); // a negative number
src.push(' pheight = maxLayoutHeight;');
}
src.push('}');
}
// Calculate x and width using layout and pwidth. Duplicate the switch
// statement, sorted by left value the first time (for x), right value
// the second time (for width). This keeps duplicate code at a minimum.
switch (hmode) {
case 0: // left, width
case 1: // left percentage, width
case 4: // right, width
case 5: // right percentage, width
case 8: // centerX, width
case 9: // centerX percentage, width
src.push('width = layout[1]/*width*/;');
break;
case 2: // left, width percentage
case 3: // left percentage, width percentage
case 6: // right, width percentage
case 7: // right percentage, width percentage
case 10: // centerX, width percentage
case 11: // centerX percentage, width percentage
src.push('width = pwidth * layout[1]/*width percentage*/;');
break;
// Each of the following cases require both the first and second
// value.
case 12: // left, right
src.push('width = pwidth - layout[0]/*left*/ - layout[1]/*right*/;');
break;
case 13: // left percentage, right
src.push('width = pwidth - (pwidth * layout[0]/*left percentage*/) - layout[1]/*right*/;');
break;
case 14: // left, right percentage
src.push('width = pwidth - layout[0]/*left*/ - (pwidth * layout[1]/*right percentage*/);');
break;
case 15: // left percentage, right percentage
src.push('width = pwidth - (pwidth * layout[0]/*left percentage*/) - (pwidth * layout[1]/*right percentage*/);');
break;
}
switch (hmode) {
case 0: // left, width
case 2: // left, width percentage
case 12: // left, right
case 14: // left, right percentage
src.push('x = layout[0]/*left*/;');
break;
case 1: // left percentage, width
case 3: // left percentage, width percentage
case 13: // left percentage, right
case 15: // left percentage, right percentage
src.push('x = pwidth * layout[0]/*left percentage*/;');
break;
case 4: // right, width
case 6: // right, width percentage
src.push('x = pwidth - layout[0]/*right*/ - width;');
break;
case 5: // right percentage, width
case 7: // right percentage, width percentage
src.push('x = pwidth - (pwidth * layout[0]/*right percentage*/) - width;');
break;
case 8: // centerX, width
case 10: // centerX, width percentage
src.push('x = (pwidth/2) + layout[0]/*centerX*/ - width/2;');
break;
case 9: // centerX percentage, width
case 11: // centerX percentage, width percentage
src.push('x = (pwidth/2) + (pwidth * layout[0]/*centerX percentage*/) - width/2;');
break;
}
// Calculate position.y, bounds.height using layout and pheight.
// Duplicate the switch statement, sorted by left value the first time
// (for position.y), right value the second time (for bounds.height).
// This keeps duplicate code at a minimum.
switch (vmode) {
case 0: // top, height
case 1: // top percentage, height
case 4: // bottom, height
case 5: // bottom prcentage, height
case 8: // centerY, height
case 9: // centerY percentage, height
src.push('height = layout[3]/*height*/;');
break;
case 2: // top, height percentage
case 3: // top percentage, height percentage
case 6: // bottom, height percentage
case 7: // bottom percentage, height percentage
case 10: // centerY, height percentage
case 11: // centerY percentage, height percentage
src.push('height = pheight * layout[3]/*height percentage*/;');
break;
// Each of the following cases require both the first and second value.
case 12: // top, bottom
src.push('height = pheight - layout[2]/*top*/ - layout[3]/*bottom*/;');
break;
case 13: // top percentage, bottom
src.push('height = pheight - (pheight * layout[2]/*top percentage*/) - layout[3]/*bottom*/;');
break;
case 14: // top, bottom percentage
src.push('height = pheight - layout[2]/*top*/ - (pheight * layout[3]/*bottom percentage*/);');
break;
case 15: // top percentage, bottom percentage
src.push('height = pheight - (pheight * layout[2]/*top percentage*/) - (pheight * layout[3]/*bottom percentage*/);');
break;
}
switch (vmode) {
case 0: // top, height
case 2: // top, height percentage
case 12: // top, bottom
case 14: // top, bottom percentage
src.push('y = layout[2]/*top*/;');
break;
case 1: // top percentage, height
case 3: // top percentage, height percentage
case 13: // top percentage, bottom
case 15: // top percentage, bottom percentage
src.push('y = pheight * layout[2]/*top percentage*/;');
break;
case 4: // bottom, height
case 6: // bottom, height percentage
src.push('y = pheight - layout[2]/*bottom*/ - height;');
break;
case 5: // bottom percentage, height
case 7: // bottom percentage, height percentage
src.push('y = pheight - (pheight * layout[2]/*bottom percentage*/) - height;');
break;
case 8: // centerY, height
case 10: // centerY, height percentage
src.push('y = (pheight/2) + layout[2]/*centerY*/ - height/2;');
break;
case 9: // centerY percentage, height
case 11: // centerY percentage, height percentage
src.push('y = (pheight/2) + (pheight * layout[2]/*centerY percentage*/) - height/2;');
break;
}
// All adjustments only affect x and y (position.x and position.y).
// Bounds are left unchanged.
if (layoutMode !== 5) {
switch (layoutMode) {
case 0: // align center
case 1: // align top
case 2: // align bottom
src.push('if (xAdjustment !== 0) x += xAdjustment/2;');
break;
// case 3: // align left
// case 5: // align top left
// case 7: // align bottom left
// // x is already correct
// break;
case 4: // align right
case 6: // align top right
case 8: // align bottom right
src.push('if (xAdjustment !== 0) x += xAdjustment;');
break;
}
switch (layoutMode) {
case 0: // align center
case 3: // align left
case 4: // align right
src.push('if (yAdjustment !== 0) y += yAdjustment/2;');
break;
// case 1: // align top
// case 5: // align top left
// case 6: // align top right
// // y is already correct
// break;
case 2: // align bottom
case 7: // align bottom left
case 8: // align bottom right
src.push('if (yAdjustment !== 0) y += yAdjustment;');
break;
}
}
// Update the position and bounds structs with the newly-computed
// values, offset to take into account anchorX and anchorY.
src.push('position.x = Math.floor(x + anchorX * width);');
src.push('position.y = Math.floor(y + anchorY * height);');
src.push('bounds.width = Math.ceil(width);');
src.push('bounds.height = Math.ceil(height);');
if (shouldBenchmark) {
src.push('SC.Benchmark.end(benchKey);');
}
src = src.join('\n ');
// if (funcName === "sc_12_12_false_false_5") console.log(src);
func = SC.layoutFunctions[funcName] = new Function("layout", "pwidth", "pheight", "anchorX", "anchorY", "position", "bounds", src);
}
return func;
};
} // FAST_LAYOUT_FUNCTION