1 /** 2 * The Render Engine 3 * InputControl 4 * 5 * @fileoverview A single line text input control. 6 * 7 * @author: Brett Fattori (brettf@renderengine.com) 8 * 9 * @author: $Author: bfattori $ 10 * @version: $Revision: 1555 $ 11 * 12 * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com) 13 * 14 * Permission is hereby granted, free of charge, to any person obtaining a copy 15 * of this software and associated documentation files (the "Software"), to deal 16 * in the Software without restriction, including without limitation the rights 17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 * copies of the Software, and to permit persons to whom the Software is 19 * furnished to do so, subject to the following conditions: 20 * 21 * The above copyright notice and this permission notice shall be included in 22 * all copies or substantial portions of the Software. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 * THE SOFTWARE. 31 * 32 */ 33 34 // The class this file defines and its required classes 35 R.Engine.define({ 36 "class":"R.ui.TextInputControl", 37 "requires":[ 38 "R.ui.AbstractUIControl", 39 "R.components.input.Keyboard" 40 ] 41 }); 42 43 /** 44 * @class UI input control for the input of a single line of text. 45 * 46 * @constructor 47 * @param [size=10] {Number} The number of visible characters in the context. 48 * @param [maxLength=0] {Number} The maximum number of allowed characters. Zero for 49 * unlimited text length. 50 * @extends R.ui.AbstractUIControl 51 */ 52 R.ui.TextInputControl = function () { 53 return R.ui.AbstractUIControl.extend(/** @scope R.ui.TextInputControl.prototype */{ 54 55 text:"", 56 maxLength:0, 57 size:0, 58 styleClass:"", 59 password:false, 60 passwordChar:null, 61 passwordText:null, 62 blinkTime:0, 63 blink:false, 64 65 /** @private */ 66 constructor:function (size, maxLength, textRenderer) { 67 this.base("TextInput", textRenderer); 68 this.size = size || 10; 69 this.maxLength = maxLength || 0; 70 this.addClass("inputcontrol"); 71 this.password = false; 72 this.passwordChar = "*"; 73 this.passwordText = ""; 74 this.blinkTime = 0; 75 this.blink = false; 76 77 // We won't get events for keyboard unless we have the component 78 this.add(R.components.input.Keyboard.create("keyinput")); 79 80 // We want to add events to capture key presses 81 // when the control has focus 82 this.addEvents({ 83 "keydown":function (evt, which) { 84 if (this.hasFocus()) { 85 if (which == R.engine.Events.KEYCODE_BACKSPACE) { 86 if (this.text.length > 0) { 87 this.text = this.text.substring(0, this.text.length - 1); 88 if (this.password) { 89 this.passwordText = this.passwordText.substring(0, this.text.length - 1); 90 } 91 } 92 93 evt.stopPropagation(); 94 evt.preventDefault(); 95 } 96 97 this.getTextRenderer().setText(this.text); 98 this.triggerEvent("change"); 99 } 100 }, 101 "keypress":function (evt, which) { 102 if (this.hasFocus()) { 103 if (which != R.engine.Events.KEYCODE_ENTER && 104 which != R.engine.Events.KEYCODE_BACKSPACE) { 105 if (this.maxLength == 0 || this.text.length < this.maxLength) { 106 if (this.password) { 107 this.text += this.passwordChar; 108 this.passwordText += String.fromCharCode(which); 109 } else { 110 this.text += String.fromCharCode(which); 111 } 112 } 113 } 114 115 this.getTextRenderer().setText(this.text); 116 this.triggerEvent("change"); 117 evt.stopPropagation(); 118 evt.preventDefault(); 119 } 120 } 121 }); 122 }, 123 124 /** 125 * Destroy the text input control, releasing its event handlers. 126 */ 127 destroy:function () { 128 this.removeEvent("keydown"); 129 this.removeEvent("keypress"); 130 this.base(); 131 }, 132 133 /** 134 * Releases the object back into the object pool. See {@link R.engine.PooledObject#release} 135 * for more information. 136 */ 137 release:function () { 138 this.base(); 139 this.text = ""; 140 this.maxLength = 0; 141 this.size = 10; 142 this.styleClass = ""; 143 }, 144 145 /** 146 * Set a flag indicating this is a password field. 147 * @param state {Boolean} <code>true</code> to mask the characters 148 */ 149 setPassword:function (state) { 150 this.password = state; 151 }, 152 153 /** 154 * Set the number of characters to display in the input. 155 * @param size {Number} The number of characters to display 156 */ 157 setSize:function (size) { 158 this.size = size; 159 }, 160 161 /** 162 * Set the maximum length of input allowed. 163 * @param maxLength {Number} Maximum allowed input length 164 */ 165 setMaxLength:function (maxLength) { 166 this.maxLength = maxLength; 167 }, 168 169 /** 170 * @private 171 */ 172 mask:function (str) { 173 var m = ""; 174 for (var s = 0; s < str.length; s++) { 175 m += this.passwordChar; 176 } 177 return m; 178 }, 179 180 /** 181 * Set the value of the input control. 182 * @param text {String} Text 183 */ 184 setText:function (text) { 185 this.text = this.password ? this.mask(text) : text; 186 this.passwordText = this.password ? text : ""; 187 this.getTextRenderer().setText(this.text); 188 }, 189 190 /** 191 * Get the value of the input control. 192 * @return {String} 193 */ 194 getText:function () { 195 return this.password ? this.passwordText : this.text; 196 }, 197 198 /** 199 * Calculate and return the width of the control in pixels. 200 * @return {Number} 201 */ 202 calcWidth:function (str) { 203 var old = this.getTextRenderer().getText(); 204 if (str === undefined) { 205 str = ""; 206 for (var s = 0; s < this.size; s++) { 207 str += "W"; 208 } 209 } 210 this.getTextRenderer().setText(str); 211 var width = this.getTextRenderer().getBoundingBox().w; 212 this.getTextRenderer().setText(old); 213 return width; 214 }, 215 216 /** 217 * Calculate and return the height of the control in pixels. 218 * @return {Number} 219 */ 220 calcHeight:function () { 221 var str = "W", old = this.getTextRenderer().getText(); 222 this.getTextRenderer().setText(str); 223 var height = this.getTextRenderer().getBoundingBox().h; 224 this.getTextRenderer().setText(old); 225 return height; 226 }, 227 228 /** 229 * Draw the input caret. 230 * @param renderContext {R.rendercontexts.RenderContext2D} The render context where the control is 231 * drawn. 232 * @param worldTime {Number} The current world time, in milliseconds 233 * @param dt {Number} The time since the last frame was drawn by the engine, in milliseconds 234 */ 235 drawCaret:function (renderContext, worldTime, dt) { 236 if (this.hasFocus()) { 237 if (worldTime > this.blinkTime) { 238 this.blink = !this.blink; 239 this.blinkTime = worldTime + 500; 240 } 241 if (this.blink) { 242 var cPos = R.math.Point2D.create(this.calcWidth(this.text) + 4, 2), 243 cEnd = R.clone(cPos); 244 cEnd.y += this.calcHeight() - 4; 245 renderContext.setLineWidth(2); 246 renderContext.setLineStyle(this.getTextRenderer().getTextColor()); 247 renderContext.drawLine(cPos, cEnd); 248 cPos.destroy(); 249 cEnd.destroy(); 250 } 251 } 252 }, 253 254 /** 255 * Draw the input component within the 256 * @param renderContext {R.rendercontexts.RenderContext2D} The render context where the control is 257 * drawn. 258 * @param worldTime {Number} The current world time, in milliseconds 259 * @param dt {Number} The time since the last frame was drawn by the engine, in milliseconds 260 */ 261 drawControl:function (renderContext, worldTime, dt) { 262 // Draw the current input text. The text baseline is the bottom of the font, 263 // so we need to move that down by the height of the control (with some padding to look right) 264 renderContext.pushTransform(); 265 var baseline = R.math.Point2D.create(2, this.calcHeight() - 3); 266 renderContext.setPosition(baseline); 267 this.getTextRenderer().update(renderContext, worldTime, dt); 268 baseline.destroy(); 269 renderContext.popTransform(); 270 271 // Draw the caret 272 this.drawCaret(renderContext, worldTime, dt); 273 }, 274 275 /** 276 * Returns a bean which represents the read or read/write properties 277 * of the object. 278 * 279 * @return {Object} The properties object 280 */ 281 getProperties:function () { 282 var self = this; 283 var prop = this.base(self); 284 return $.extend(prop, { 285 "Size":[function () { 286 return self.size; 287 }, function (i) { 288 self.setSize(parseInt(i)); 289 }, true], 290 "MaxLength":[function () { 291 return self.maxLength; 292 }, function (i) { 293 self.setMaxLength(parseInt(i)); 294 }, true], 295 "IsPassword":[function () { 296 return self.password; 297 }, { 298 "toggle":true, 299 "fn":function (s) { 300 self.setPassword(s); 301 } 302 }, true], 303 "Text":[function () { 304 return self.getText(); 305 }, function (i) { 306 self.setText(i); 307 }, true] 308 }); 309 } 310 311 }, /** @scope R.ui.TextInputControl.prototype */{ 312 313 /** 314 * Get the class name of this object 315 * @return {String} The string "R.ui.TextInputControl" 316 */ 317 getClassName:function () { 318 return "R.ui.TextInputControl"; 319 }, 320 321 /** 322 * Get a properties object with values for the given object. 323 * @param obj {R.ui.TextInputControl} The text input control to query 324 * @param [defaults] {Object} Default values that don't need to be serialized unless 325 * they are different. 326 * @return {Object} 327 */ 328 serialize:function (obj, defaults) { 329 // Defaults for object properties which can be skipped if no different 330 defaults = defaults || []; 331 $.extend(defaults, { 332 "Text":"", 333 "MaxLength":0, 334 "IsPassword":false, 335 "Size":10 336 }); 337 return R.ui.AbstractUIControl.serialize(obj, defaults); 338 }, 339 340 /** 341 * Deserialize the object back into a text input control. 342 * @param obj {Object} The object to deserialize 343 * @param [clazz] {Class} The object class to populate 344 * @return {R.ui.TextInputControl} The object which was deserialized 345 */ 346 deserialize:function (obj, clazz) { 347 clazz = clazz || R.ui.TextInputControl.create(); 348 R.ui.AbstractUIControl.deserialize(obj, clazz); 349 return clazz; 350 } 351 }); 352 353 };