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