1 jls.loader.provide('jls.gui.Element'); 2 3 jls.loader.require('jls.gui.Style'); 4 jls.loader.require('jls.gui.FlowLayout'); 5 6 jls.gui.Element = jls.lang.Class.create( /** @lends jls.gui.Element.prototype */ 7 { 8 /** 9 * Creates an element. 10 * 11 * @param {Object} [parameters] The element parameter. 12 * @param {Object} [parameters.attributes] The element attributes. 13 * @param {Object} [parameters.style] The element style. 14 * @param {Array} [parameters.children] The element children. 15 * @param {jls.gui.Element} [parent] The parent. 16 * @constructs 17 * @class This class represents a graphical element. 18 */ 19 initialize : function(parameters, parent) { 20 parameters = parameters || {}; 21 this._children = []; 22 this._parent = parent || null; 23 this._eventsHandlers = {}; 24 // Default parameters 25 this._title = undefined; 26 this._id = undefined; 27 // Create style 28 this._style = new jls.gui.Style(parameters.style); 29 // Register style handler 30 this.getStyle().setHandler(this.onStyleChange.bind(this)); 31 if (this._parent) { 32 this._style.setParent(this._parent.getStyle()); 33 } 34 // Create default layout 35 this._layout = this.createDefaultLayout(); 36 // Set bounds 37 this._x = this._style.getPropertyValue('left'); 38 this._y = this._style.getPropertyValue('top'); 39 this._w = this._style.getPropertyValue('width'); 40 this._h = this._style.getPropertyValue('height'); 41 // Set the attributes 42 // TODO is this the right place? 43 if (parameters.attributes) { 44 this.setAttributes(parameters.attributes); 45 } 46 // Add this element to its parent 47 // TODO is this the right place? 48 if (this._parent != null) { 49 this._parent.addChild(this); 50 } 51 // Create the concrete element 52 this.onCreate(); 53 // Activate the style properties 54 this._style.activate(); 55 // Add the children 56 if (Object.isArray(parameters.children)) { 57 this.addChildren(parameters.children); 58 } 59 if (this._layout != null) { 60 this._layout.onCreate(); 61 } 62 }, 63 onCreate : jls.lang.Class.emptyFunction, 64 getParent : function() { 65 return this._parent; 66 }, 67 /* 68 * Attributes 69 */ 70 /** 71 * Sets an attribute. 72 * 73 * @param {String} key The attribute key. 74 * @param {String} value The attribute value. 75 */ 76 setAttribute : function(key, value) { 77 /* look for a setter method in this */ 78 var setter = key.camelize('set'); 79 jls.logger.trace('setter: "' + setter + '"'); 80 if (Object.isFunction(this[setter])) { 81 this[setter](value); 82 return this; 83 } 84 this._setAttribute(key, value); 85 return this; 86 }, 87 _setAttribute : jls.lang.Class.emptyFunction, 88 /** 89 * Returns an attribute. 90 * 91 * @param {String} key The attribute key. 92 * @returns {String} The attribute value. 93 */ 94 getAttribute : function(key) { 95 jls.logger.trace('getAttribute(' + key + ')'); 96 /* look for a getter method in this */ 97 var getter = key.camelize('get'); 98 jls.logger.trace('getter: "' + getter + '"'); 99 if (Object.isFunction(this[getter])) { 100 return this[getter](); 101 } 102 return this._getAttribute(key); 103 }, 104 _getAttribute : jls.lang.Class.emptyFunction, 105 /** 106 * Sets some attributes. 107 * 108 * @param {Object} attributes The attributes to set. 109 */ 110 setAttributes : function(attributes) { 111 for ( var k in attributes) { 112 this.setAttribute(k, attributes[k]); 113 } 114 return this; 115 }, 116 /** 117 * Returns the element style. 118 * 119 * @returns {jls.gui.Style} The element style. 120 */ 121 getStyle : function() { 122 return this._style; 123 }, 124 // onStyleChange(key, oldValue, newValue) 125 onStyleChange : jls.lang.Class.emptyFunction, 126 /** 127 * Returns the layout associated with this element. 128 * 129 * @returns {jls.gui.Layout} The element layout or null if there is no layout. 130 */ 131 getLayout : function() { 132 return this._layout; 133 }, 134 createDefaultLayout : function() { 135 return null; 136 }, 137 /** 138 * Sets the layout for this element. 139 * 140 * @param {jls.gui.Layout} layout The layout to associate to this element. 141 */ 142 setLayout : function(layout) { 143 if (layout != null) { 144 if (typeof layout == 'string') { 145 var layoutClass = jls.loader.require(layout); 146 layout = new layoutClass(this); 147 } else if (layout instanceof jls.gui.Layout) { 148 layout.setElement(this); 149 } else { 150 throw new jls.lang.Exception('Invalid layout'); 151 } 152 } 153 this._layout = layout; 154 return this; 155 }, 156 /* 157 * Element hierarchy 158 */ 159 /** 160 * Adds a child. 161 * 162 * @param {Object} child The child to add. 163 */ 164 addChild : function(child) { 165 if (child instanceof jls.gui.Element) { 166 jls.logger.trace('addChild for jls.gui.Element'); 167 // TODO if the child already have a parent remove it 168 child._parent = this; 169 this._children.push(child); 170 } else if ((typeof child == "object") && ('classname' in child)) { 171 jls.logger.trace('addChild for object'); 172 child = jls.lang.Class.newInstance(child, this); 173 } else { 174 throw new jls.lang.Exception('don\'t know how to add "' + (typeof child) + '" child type'); 175 } 176 if (this._layout != null) { 177 this._layout.onAddChild(child); 178 } 179 return child; 180 }, 181 /** 182 * Adds children. 183 * 184 * @param {Object} children The children to add. 185 */ 186 addChildren : function(children) { 187 for ( var i = 0; i < children.length; i++) { 188 this.addChild(children[i]); 189 } 190 return this; 191 }, 192 /** 193 * Removes a child. 194 * 195 * @param {Object} child The child to remove. 196 */ 197 removeChild : function(child) { 198 var index = 0; 199 if (typeof child == 'number') { 200 index = child; 201 } else { 202 for (index = this._children.length - 1; (index >= 0) && (this._children[index] !== child); index--); 203 if (index < 0) { 204 throw new jls.lang.Exception('Child not found'); 205 } 206 } 207 var removed = this._children[index]; 208 this._children.splice(index, 1); 209 //jls.logger.warn('removeChild(' + index + '))'); 210 if (removed && (removed instanceof jls.gui.Element)) { 211 removed._parent = null; 212 removed.onDestroy(); 213 } 214 if (this._layout != null) { 215 this._layout.onRemoveChild(child); 216 } 217 return removed; 218 }, 219 /** 220 * Removes all children. 221 * 222 */ 223 removeChildren : function() { 224 for ( var i = this._children.length - 1; i >= 0; i--) { 225 this.removeChild(i); 226 } 227 }, 228 /** 229 * Returns all children. 230 * 231 */ 232 getChildren : function() { 233 return this._children; 234 }, 235 getChildCount : function() { 236 return this._children.length; 237 }, 238 /** 239 * Gets a child. 240 * 241 * @param {Number} index The child index. 242 * @returns {Object} The child. 243 */ 244 getChild : function(index) { 245 return this._children[index]; 246 }, 247 /** 248 * Destroys this element. 249 * 250 */ 251 destroy : function() { 252 if (this.getParent() != null) { 253 this.getParent().removeChild(this); 254 } else { 255 this.onDestroy(); 256 } 257 return this; 258 }, 259 onDestroy : jls.lang.Class.emptyFunction, 260 /* 261 * Event 262 */ 263 dispatch : function(event) { 264 if (event.type in this._eventsHandlers) { 265 var handlers = this._eventsHandlers[event.type]; 266 for (var i = handlers.length - 1; i >= 0; i--) { 267 handlers[i](event); 268 } 269 } 270 return this; 271 }, 272 onStartObserving: jls.lang.Class.emptyFunction, 273 onStopObserving: jls.lang.Class.emptyFunction, 274 /** 275 * Register an event handler. 276 * 277 * @param {String} type The event type to observe. 278 * @param {Function} handler The handler to register. 279 * @returns {Function} The handler. 280 */ 281 observe: function(type, handler) { 282 if (Object.keys(this._eventsHandlers).length == 0) { 283 this.onStartObserving(); 284 } 285 if (! (type in this._eventsHandlers)) { 286 this._eventsHandlers[type] = []; 287 } 288 this._eventsHandlers[type].push(handler); 289 return handler; 290 }, 291 /** 292 * Unregister an event handler. 293 * 294 * @param {String} type The event type to unregister. 295 * @param {Function} handler The handler to unregister. 296 * @returns {Array} The removed handlers. 297 */ 298 unobserve: function(type, handler) { 299 var removed = []; 300 if (type in this._eventsHandlers) { 301 var handlers = this._eventsHandlers[type]; 302 for (var i = handlers.length - 1; i >= 0; i--) { 303 if (handler == handlers[i]) { 304 removed.push(handlers[i]); 305 handlers.splice(i, 1); 306 } 307 } 308 if (handlers.length == 0) { 309 delete this._eventsHandlers[type]; 310 } 311 } 312 if (Object.keys(this._eventsHandlers).length == 0) { 313 this.onStopObserving(); 314 } 315 return removed; 316 }, 317 /* 318 * Update 319 */ 320 // TODO doLayout ? 321 update : function() { 322 if (this._layout != null) { 323 this._layout.onUpdate(); 324 } 325 }, 326 /* 327 * Default attributes 328 */ 329 getX : function() { 330 return this._x; 331 }, 332 getY : function() { 333 return this._y; 334 }, 335 getW : function() { 336 return this._w; 337 }, 338 getH : function() { 339 return this._h; 340 }, 341 getBounds : function() { 342 return [this._x, this._y, this._w, this._h]; 343 }, 344 setBounds : function(rect) { 345 this._x = rect[0]; 346 this._y = rect[1]; 347 this._w = rect[2]; 348 this._h = rect[3]; 349 return this; 350 }, 351 getLocation : function() { 352 return [this._x, this._y]; 353 }, 354 setLocation : function(point) { 355 this._x = point[0]; 356 this._y = point[1]; 357 return this; 358 }, 359 getSize : function() { 360 return [this._w, this._h]; 361 }, 362 setSize : function(dim) { 363 this._w = dim[0]; 364 this._h = dim[1]; 365 return this; 366 }, 367 getClientOffset : function() { 368 return [0, 0]; 369 }, 370 /*getClientBounds : function() { 371 return this.getBounds(); 372 },*/ 373 getClientSize : function() { 374 return this.getSize(); 375 }, 376 getTitle : function() { 377 return this._title; 378 }, 379 setTitle : function(title) { 380 this._title = title; 381 return this; 382 } 383 }); 384 385 Object.extend(jls.gui.Element, /** @lends jls.gui.Element */ 386 { 387 /** 388 * Calls a function synchronously in the GUI message thread. 389 * 390 * @param {Function} fn The function to call. 391 * @returns {Object} The function result if any. 392 * @function 393 */ 394 invokeAndWait : jls.win32.Window.invokeAndWait, 395 /** 396 * Calls a function asynchronously in the GUI message thread. 397 * 398 * @param {Function} fn The handler to unregister. 399 * @function 400 */ 401 invokeLater : jls.win32.Window.invokeLater 402 }); 403