1 jls.loader.provide('jls.gui.FlowLayout'); 2 3 jls.loader.require('jls.gui.Layout'); 4 5 /** 6 * @augments jls.gui.Layout 7 * @class This class represents a flow layout. The children can have an absolute or a relative position, 8 * these two layouts does not interfer, overlapping must be fix using z-index. 9 * <p>Child style position: <b>absolute</b>. 10 * The absolute position is defined using a combination of the following style options: left, right, top, bottom, width, height. 11 * If some options are conflicting each other then the last ones are ignored, 12 * the conflicting options and their priorities are: width, left, right and height, top, bottom.</p> 13 * <p>Child style position: <b>relative</b>. 14 * Children are positioned in a flow depending on the following style options: 15 * direction (ltr, rtl), textAlign (left, right, center, justify), verticalAlign (top, middle, bottom).</p> 16 */ 17 jls.gui.FlowLayout = jls.lang.Class.create(jls.gui.Layout, 18 { 19 onUpdate : function() { 20 /* 21 * update relative children then absolute 22 */ 23 // TODO use several width: px, %, weight 24 var cSize = this._element.getClientSize(); 25 var cWidth = cSize[0]; 26 var cHeight = cSize[1]; 27 //jls.lang.System.out.println('update(): [' + cWidth + ', ' + cHeight + ']'); 28 var cStyle = this._element.getStyle(); 29 var hGap = cStyle.getPropertyValue('hGap') || 0; 30 var vGap = cStyle.getPropertyValue('vGap') || 0; 31 var lines = []; 32 var lineWeight = 0; 33 var lineHeight = 0; 34 var lineWidth = 0; 35 var lineElements = []; 36 var globalHeight = 0; 37 var globalWidth = 0; 38 var childCount = this._element.getChildCount(); 39 // Special case for only one child without layout styles (left, top, width, height) 40 if (childCount == 1) { 41 var child = this._element.getChild(0); 42 var style = child.getStyle(); 43 if ((style.getPropertyValue('width') == null) && (style.getPropertyValue('height') == null) && 44 (style.getPropertyValue('left') == null) && (style.getPropertyValue('top') == null)) 45 { 46 jls.gui.FlowLayout.place(child, hGap, vGap, cWidth - hGap * 2, cHeight - vGap * 2); 47 return; 48 } 49 } 50 for (var i = 0; i < childCount; i++) { 51 var child = this._element.getChild(i); 52 var style = child.getStyle(); 53 var position = style.getPropertyValue('position'); 54 var rawWidth = style.getPropertyValue('width'); 55 var rawHeight = style.getPropertyValue('height'); 56 if (position == 'relative') { 57 var weight = jls.gui.FlowLayout.getWeight(rawWidth); 58 var width = weight > 0 ? 0 : jls.gui.FlowLayout.computeSize(rawWidth, cWidth); 59 var height = jls.gui.FlowLayout.computeSize(rawHeight, cHeight, childCount); 60 /* 61 * TODO Manage display:block styles 62 */ 63 if ((style.getPropertyValue('clear') == 'left') || 64 ((lineElements.length > 0) && (lineElements[lineElements.length - 1].element.getStyle().getPropertyValue('clear') == 'right')) || 65 (lineWidth + (lineElements.length > 0 ? hGap : 0) + width > cWidth)) 66 { 67 globalHeight += vGap; 68 // new line 69 lines.push({ 70 weight: lineWeight, 71 width: lineWidth, 72 height: lineHeight, 73 elements: lineElements 74 }); 75 // compute global 76 if (lineWidth > globalWidth) { 77 globalWidth = lineWidth; 78 } 79 globalHeight += lineHeight; 80 //jls.lang.System.out.println('lines[' + (lines.length - 1) + ']: ' + lineWidth + 'x' + lineHeight + ', ' + globalWidth + 'x' + globalHeight); 81 // reset 82 lineElements = []; 83 lineWeight = 0; 84 lineWidth = 0; 85 lineHeight = 0; 86 } 87 lineWidth += hGap; 88 lineElements.push({ 89 width: width, 90 height: height, 91 rawWidth: rawWidth, 92 rawHeight: rawHeight, 93 element: child 94 }); 95 lineWeight += weight; 96 lineWidth += width; 97 if (height > lineHeight) { 98 lineHeight = height; 99 } 100 //jls.lang.System.out.println('child[' + i + ']: ' + width + 'x' + height + ', ' + lineWidth + 'x' + lineHeight + ', ' + globalWidth + 'x' + globalHeight); 101 } else if (position == 'absolute') { 102 var width = jls.gui.FlowLayout.computeSize(rawWidth, cWidth); 103 var height = jls.gui.FlowLayout.computeSize(rawHeight, cHeight); 104 var x = 0; 105 var y = 0; 106 var w = 0; 107 var h = 0; 108 var left = jls.gui.FlowLayout.computeSize(style.getPropertyValue('left'), cWidth); 109 var top = jls.gui.FlowLayout.computeSize(style.getPropertyValue('top'), cHeight); 110 var right = jls.gui.FlowLayout.computeSize(style.getPropertyValue('right'), cWidth); 111 var bottom = jls.gui.FlowLayout.computeSize(style.getPropertyValue('bottom'), cHeight); 112 // compute horizontal 113 if (width != null) { 114 w = width; 115 } else if ((left != null) && (right != null)) { 116 w = cWidth - right - left; 117 } else { 118 w = 10; // TODO auto 119 } 120 if (left != null) { 121 x = left; 122 } else if (right != null) { 123 x = cWidth - right - w; 124 } else { 125 x = 0; // default 126 } 127 // compute vertical 128 if (height != null) { 129 h = height; 130 } else if ((top != null) && (bottom != null)) { 131 h = cHeight - bottom - top; 132 } else { 133 h = 10; // TODO auto 134 } 135 if (top != null) { 136 y = top; 137 } else if (right != null) { 138 y = cHeight - bottom - h; 139 } else { 140 y = 0; // default 141 } 142 jls.gui.FlowLayout.place(child, x, y, w, h); 143 } else { 144 throw new jls.lang.Exception('Invalid position: "' + position + '"'); 145 } 146 } 147 if (lineElements.length > 0) { 148 if (lines.length > 0) { 149 globalHeight += vGap; 150 } 151 // new line 152 lines.push({ 153 weight: lineWeight, 154 width: lineWidth, 155 height: lineHeight, 156 elements: lineElements 157 }); 158 // compute global 159 if (lineWidth > globalWidth) { 160 globalWidth = lineWidth; 161 } 162 globalHeight += lineHeight; 163 //jls.lang.System.out.println('lines[' + (lines.length - 1) + ']: ' + lineWidth + 'x' + lineHeight + ', ' + globalWidth + 'x' + globalHeight); 164 } 165 //jls.lang.System.out.println('update(): ' + cWidth + 'x' + cHeight + ', ' + globalWidth + 'x' + globalHeight); 166 var lineCount = lines.length; 167 if (lineCount > 0) { 168 var direction = cStyle.getPropertyValue('direction'); 169 var verticalPosition = cStyle.getPropertyValue('verticalPosition'); 170 var textAlign = cStyle.getPropertyValue('textAlign'); 171 var verticalAlign = cStyle.getPropertyValue('verticalAlign'); 172 var y = vGap; 173 if (verticalPosition == 'middle') { 174 y = Math.floor((cHeight - globalHeight) / 2); 175 } else if (verticalPosition == 'bottom') { 176 y = cHeight - globalHeight; 177 } 178 for (var i = 0; i < lineCount; i++) { 179 var line = lines[i]; 180 var x = hGap; 181 var lineWidth = line.weight > 0 ? (cWidth - hGap * 2) : line.width; 182 if (textAlign == 'center') { 183 x = Math.floor((cWidth - lineWidth) / 2); 184 } else if (textAlign == 'right') { 185 x = cWidth - lineWidth; 186 } 187 lineElements = line.elements; 188 if (direction == 'rtl') { 189 for (var j = lineElements.length - 1; j >= 0; j--) { 190 var e = lineElements[j]; 191 var w; 192 var h = e.height; 193 if (line.weight > 0) { 194 w = jls.gui.FlowLayout.computeSize(e.rawWidth, lineWidth - line.width, line.weight); 195 } else { 196 w = e.width; 197 } 198 jls.gui.FlowLayout.placeElement(verticalAlign, line.height, e.element, x, y, w, h); 199 x += w + hGap; 200 } 201 } else { 202 for (var j = 0; j < lineElements.length; j++) { 203 var e = lineElements[j]; 204 var w; 205 var h = e.height; 206 if ((line.weight > 0) && (e.width == 0)) { 207 w = jls.gui.FlowLayout.computeSize(e.rawWidth, lineWidth - line.width, line.weight); 208 //jls.lang.System.out.println('computeSize(' + e.rawWidth + ', ' + cWidth + ', ' + line.weight + ') => ' + w); 209 } else { 210 w = e.width; 211 } 212 jls.gui.FlowLayout.placeElement(verticalAlign, line.height, e.element, x, y, w, h); 213 x += w + hGap; 214 } 215 } 216 y += line.height + vGap; 217 } 218 } 219 } 220 }); 221 222 Object.extend(jls.gui.FlowLayout, 223 { 224 isWeight : function(value) { 225 return value.charAt(value.length - 1) == 'w'; 226 }, 227 getWeight : function(value) { 228 if ((typeof value != 'string') || (value.length < 2)) { 229 return 0; 230 } 231 value = value.match(/([0-9]+)(.*)/); 232 return value[2] == 'w' ? parseInt(value[1]) : 0; 233 /*var w = value[2] == 'w' ? parseInt(value[1]) : 0; 234 jls.lang.System.out.println('getWeight(' + value + ') => ' + w); 235 return w;*/ 236 }, 237 computeSize : function(value, size, totalWeight) { 238 if (value == null) { 239 return null; 240 } 241 if (typeof value == 'number') { 242 return value; 243 } 244 if ((typeof value != 'string') || (value.length == 0)) { 245 throw new jls.lang.Exception('Illegal type of value "' + (typeof value) + '"'); 246 } 247 value = value.match(/([0-9]+)(.*)/); 248 var numberValue = value[1]; 249 var typeValue = value[2] || 'px'; 250 switch (typeValue) { 251 case 'px': 252 return Math.round(numberValue); 253 case '%': 254 return Math.round(numberValue * size / 100); 255 case 'w': 256 if (typeof totalWeight == 'undefined') { 257 throw new jls.lang.Exception('The total weight is missing'); 258 } 259 //jls.lang.System.out.println('computeSize(' + value + ', ' + size + ', ' + totalWeight + ') => ' + Math.round(numberValue * size / totalWeight)); 260 return Math.round(numberValue * size / totalWeight); 261 default: 262 throw new jls.lang.Exception('Illegal size type "' + typeValue + '"'); 263 } 264 }, 265 placeElement : function(verticalAlign, lineHeight, child, x, y, w, h) { 266 if (verticalAlign == 'middle') { 267 y = y + Math.floor((lineHeight - h) / 2); 268 } else if (verticalAlign == 'bottom') { 269 y = y + lineHeight - h; 270 } 271 jls.gui.FlowLayout.place(child, x, y, w, h); 272 }, 273 place : function(child, x, y, w, h) { 274 //jls.lang.System.out.println('[' + x + ', ' + y + ', ' + w + ', ' + h + ']'); 275 //child.setLocation([x, y]); 276 child.setBounds([x, y, w, h]); 277 child.update(); 278 } 279 }); 280