1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * Text field delegate
 29  * @class
 30  * @extends cc.Class
 31  */
 32 cc.TextFieldDelegate = cc.Class.extend(/** @lends cc.TextFieldDelegate# */{
 33     /**
 34      * If the sender doesn't want to attach with IME, return true;
 35      * @param {cc.TextFieldTTF} sender
 36      * @return {Boolean}
 37      */
 38     onTextFieldAttachWithIME:function (sender) {
 39         return false;
 40     },
 41 
 42     /**
 43      * If the sender doesn't want to detach with IME, return true;
 44      * @param {cc.TextFieldTTF} sender
 45      * @return {Boolean}
 46      */
 47     onTextFieldDetachWithIME:function (sender) {
 48         return false;
 49     },
 50 
 51     /**
 52      * If the sender doesn't want to insert the text, return true;
 53      * @param {cc.TextFieldTTF} sender
 54      * @param {String} text
 55      * @param {Number} len
 56      * @return {Boolean}
 57      */
 58     onTextFieldInsertText:function (sender, text, len) {
 59         return false
 60     },
 61 
 62     /**
 63      * If the sender doesn't want to delete the delText, return true;
 64      * @param {cc.TextFieldTTF} sender
 65      * @param {String} delText
 66      * @param {Number} len
 67      * @return {Boolean}
 68      */
 69     onTextFieldDeleteBackward:function (sender, delText, len) {
 70         return false;
 71     },
 72 
 73     /**
 74      * If doesn't want draw sender as default, return true.
 75      * @param {cc.TextFieldTTF} sender
 76      * @return {Boolean}
 77      */
 78     onDraw:function (sender) {
 79         return false;
 80     }
 81 });
 82 
 83 /**
 84  * A simple text input field with TTF font.
 85  * @class
 86  * @extends cc.LabelTTF
 87  *
 88  * @property {cc.Node}      delegate            - Delegate
 89  * @property {Number}       charCount           - <@readonly> Characators count
 90  * @property {String}       placeHolder         - Place holder for the field
 91  * @property {cc.Color}     colorSpaceHolder
 92  *
 93  * @param {String} placeholder
 94  * @param {cc.Size} dimensions
 95  * @param {Number} alignment
 96  * @param {String} fontName
 97  * @param {Number} fontSize
 98  *
 99  * @example
100  * //example
101  * // When five parameters
102  * var textField = new cc.TextFieldTTF("<click here for input>", cc.size(100,50), cc.TEXT_ALIGNMENT_LEFT,"Arial", 32);
103  * // When three parameters
104  * var textField = new cc.TextFieldTTF("<click here for input>", "Arial", 32);
105  */
106 cc.TextFieldTTF = cc.LabelTTF.extend(/** @lends cc.TextFieldTTF# */{
107 	delegate:null,
108 	colorSpaceHolder:null,
109 
110     _colorText: null,
111     _lens:null,
112     _inputText:"",
113     _placeHolder:"",
114     _charCount:0,
115     _className:"TextFieldTTF",
116 
117     /**
118      * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br />
119      * creates a cc.TextFieldTTF from a fontName, alignment, dimension and font size.
120      * @param {String} placeholder
121      * @param {cc.Size} dimensions
122      * @param {Number} alignment
123      * @param {String} fontName
124      * @param {Number} fontSize
125      */
126     ctor:function (placeholder, dimensions, alignment, fontName, fontSize) {
127         this.colorSpaceHolder = cc.color(127, 127, 127);
128         this._colorText = cc.color(255,255,255, 255);
129         cc.LabelTTF.prototype.ctor.call(this);
130 
131         if(fontSize !== undefined){
132             this.initWithPlaceHolder("", dimensions, alignment, fontName, fontSize);
133             if(placeholder)
134                 this.setPlaceHolder(placeholder);
135         }else if(fontName === undefined && alignment !== undefined){
136             this.initWithString("", arguments[1], arguments[2]);
137             if(placeholder)
138                 this.setPlaceHolder(placeholder);
139         }
140     },
141 
142     onEnter: function(){
143         cc.LabelTTF.prototype.onEnter.call(this);
144         cc.imeDispatcher.addDelegate(this);
145     },
146 
147     onExit: function(){
148         cc.LabelTTF.prototype.onExit.call(this);
149         cc.imeDispatcher.removeDelegate(this);
150     },
151 
152     /**
153      * Gets the delegate.
154      * @return {cc.Node}
155      */
156     getDelegate:function () {
157         return this.delegate;
158     },
159 
160     /**
161      * Set the delegate.
162      * @param {cc.Node} value
163      */
164     setDelegate:function (value) {
165         this.delegate = value;
166     },
167 
168     /**
169      * Gets the char count.
170      * @return {Number}
171      */
172     getCharCount:function () {
173         return this._charCount;
174     },
175 
176     /**
177      * Returns the color of space holder.
178      * @return {cc.Color}
179      */
180     getColorSpaceHolder:function () {
181         return cc.color(this.colorSpaceHolder);
182     },
183 
184     /**
185      * Sets the color of space holder.
186      * @param {cc.Color} value
187      */
188     setColorSpaceHolder:function (value) {
189         this.colorSpaceHolder.r = value.r;
190         this.colorSpaceHolder.g = value.g;
191         this.colorSpaceHolder.b = value.b;
192         this.colorSpaceHolder.a = cc.isUndefined(value.a) ? 255 : value.a;
193         if(!this._inputText.length)
194             this.setColor(this.colorSpaceHolder);
195     },
196 
197     /**
198      * Sets the color of cc.TextFieldTTF's text.
199      * @param {cc.Color} textColor
200      */
201     setTextColor:function(textColor){
202         this._colorText.r = textColor.r;
203         this._colorText.g = textColor.g;
204         this._colorText.b = textColor.b;
205         this._colorText.a = cc.isUndefined(textColor.a) ? 255 : textColor.a;
206         if(this._inputText.length)
207             this.setColor(this._colorText);
208     },
209 
210     /**
211      * Initializes the cc.TextFieldTTF with a font name, alignment, dimension and font size
212      * @param {String} placeholder
213      * @param {cc.Size} dimensions
214      * @param {Number} alignment
215      * @param {String} fontName
216      * @param {Number} fontSize
217      * @return {Boolean}
218      * @example
219      * //example
220      * var  textField = new cc.TextFieldTTF();
221      * // When five parameters
222      * textField.initWithPlaceHolder("<click here for input>", cc.size(100,50), cc.TEXT_ALIGNMENT_LEFT,"Arial", 32);
223      * // When three parameters
224      * textField.initWithPlaceHolder("<click here for input>", "Arial", 32);
225      */
226     initWithPlaceHolder:function (placeholder, dimensions, alignment, fontName, fontSize) {
227         switch (arguments.length) {
228             case 5:
229                 if (placeholder)
230                     this.setPlaceHolder(placeholder);
231                 return this.initWithString(this._placeHolder,fontName, fontSize, dimensions, alignment);
232                 break;
233             case 3:
234                 if (placeholder)
235                     this.setPlaceHolder(placeholder);
236                 return this.initWithString(this._placeHolder, arguments[1], arguments[2]);
237                 break;
238             default:
239                 throw new Error("Argument must be non-nil ");
240                 break;
241         }
242     },
243 
244     /**
245      * Input text property
246      * @param {String} text
247      */
248     setString:function (text) {
249         text = String(text);
250         this._inputText = text || "";
251 
252         // if there is no input text, display placeholder instead
253         if (!this._inputText.length){
254             cc.LabelTTF.prototype.setString.call(this, this._placeHolder);
255             this.setColor(this.colorSpaceHolder);
256         } else {
257             cc.LabelTTF.prototype.setString.call(this,this._inputText);
258             this.setColor(this._colorText);
259         }
260         if(cc._renderType === cc._RENDER_TYPE_CANVAS)
261             this._renderCmd._updateTexture();
262         this._charCount = this._inputText.length;
263     },
264 
265     /**
266      * Gets the string
267      * @return {String}
268      */
269     getString:function () {
270         return this._inputText;
271     },
272 
273     /**
274      * Set the place holder. <br />
275      * display this string if string equal "".
276      * @param {String} text
277      */
278     setPlaceHolder:function (text) {
279         this._placeHolder = text || "";
280         if (!this._inputText.length) {
281             cc.LabelTTF.prototype.setString.call(this,this._placeHolder);
282             this.setColor(this.colorSpaceHolder);
283         }
284     },
285 
286     /**
287      * Gets the place holder. <br />
288      * default display string.
289      * @return {String}
290      */
291     getPlaceHolder:function () {
292         return this._placeHolder;
293     },
294 
295     /**
296      * Render function using the canvas 2d context or WebGL context, internal usage only, please do not call this function.
297      * @param {CanvasRenderingContext2D | WebGLRenderingContext} ctx The render context
298      */
299     draw:function (ctx) {
300         //console.log("size",this._contentSize);
301         var context = ctx || cc._renderContext;
302         if (this.delegate && this.delegate.onDraw(this))
303             return;
304 
305         cc.LabelTTF.prototype.draw.call(this, context);
306     },
307 
308     /**
309      * Recursive method that visit its children and draw them.
310      * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx
311      */
312     visit: function(ctx){
313         this._super(ctx);
314     },
315 
316     //////////////////////////////////////////////////////////////////////////
317     // CCIMEDelegate interface
318     //////////////////////////////////////////////////////////////////////////
319     /**
320      * Open keyboard and receive input text.
321      * @return {Boolean}
322      */
323     attachWithIME:function () {
324         return cc.imeDispatcher.attachDelegateWithIME(this);
325     },
326 
327     /**
328      * End text input  and close keyboard.
329      * @return {Boolean}
330      */
331     detachWithIME:function () {
332         return cc.imeDispatcher.detachDelegateWithIME(this);
333     },
334 
335     /**
336      * Return whether to allow attach with IME.
337      * @return {Boolean}
338      */
339     canAttachWithIME:function () {
340         return (this.delegate) ? (!this.delegate.onTextFieldAttachWithIME(this)) : true;
341     },
342 
343     /**
344      * When the delegate detach with IME, this method call by CCIMEDispatcher.
345      */
346     didAttachWithIME:function () {
347     },
348 
349     /**
350      * Return whether to allow detach with IME.
351      * @return {Boolean}
352      */
353     canDetachWithIME:function () {
354         return (this.delegate) ? (!this.delegate.onTextFieldDetachWithIME(this)) : true;
355     },
356 
357     /**
358      * When the delegate detach with IME, this method call by CCIMEDispatcher.
359      */
360     didDetachWithIME:function () {
361     },
362 
363     /**
364      * Delete backward
365      */
366     deleteBackward:function () {
367         var strLen = this._inputText.length;
368         if (strLen === 0)
369             return;
370 
371         // get the delete byte number
372         var deleteLen = 1;    // default, erase 1 byte
373 
374         if (this.delegate && this.delegate.onTextFieldDeleteBackward(this, this._inputText[strLen - deleteLen], deleteLen)) {
375             // delegate don't want delete backward
376             return;
377         }
378 
379         // if delete all text, show space holder string
380         if (strLen <= deleteLen) {
381             this._inputText = "";
382             this._charCount = 0;
383             cc.LabelTTF.prototype.setString.call(this,this._placeHolder);
384             this.setColor(this.colorSpaceHolder);
385             return;
386         }
387 
388         // set new input text
389         this.string = this._inputText.substring(0, strLen - deleteLen);
390     },
391 
392     /**
393      *  Remove delegate
394      */
395     removeDelegate:function () {
396         cc.imeDispatcher.removeDelegate(this);
397     },
398 
399     _tipMessage: "please enter your word:",
400     /**
401      * Sets the input tip message to show on mobile browser.  (mobile Web only)
402      * @param {string} tipMessage
403      */
404     setTipMessage: function (tipMessage) {
405         if (tipMessage == null)
406             return;
407         this._tipMessage = tipMessage;
408     },
409 
410     /**
411      * Gets the input tip message to show on mobile browser.   (mobile Web only)
412      * @returns {string}
413      */
414     getTipMessage: function () {
415         return this._tipMessage;
416     },
417 
418     /**
419      * Append the text. <br />
420      * Input the character.
421      * @param {String} text
422      * @param {Number} len
423      */
424     insertText:function (text, len) {
425         var sInsert = text;
426 
427         // insert \n means input end
428         var pos = sInsert.indexOf('\n');
429         if (pos > -1) {
430             sInsert = sInsert.substring(0, pos);
431         }
432 
433         if (sInsert.length > 0) {
434             if (this.delegate && this.delegate.onTextFieldInsertText(this, sInsert, sInsert.length)) {
435                 // delegate doesn't want insert text
436                 return;
437             }
438 
439             var sText = this._inputText + sInsert;
440             this._charCount = sText.length;
441             this.string = sText;
442         }
443 
444         if (pos === -1)
445             return;
446 
447         // '\n' has inserted,  let delegate process first
448         if (this.delegate && this.delegate.onTextFieldInsertText(this, "\n", 1))
449             return;
450 
451         // if delegate hasn't process, detach with ime as default
452         this.detachWithIME();
453     },
454 
455     /**
456      * Gets the input text.
457      * @return {String}
458      */
459     getContentText:function () {
460         return this._inputText;
461     },
462 
463     //////////////////////////////////////////////////////////////////////////
464     // keyboard show/hide notification
465     //////////////////////////////////////////////////////////////////////////
466     keyboardWillShow:function (info) {
467     },
468     keyboardDidShow:function (info) {
469     },
470     keyboardWillHide:function (info) {
471     },
472     keyboardDidHide:function (info) {
473     }
474 });
475 
476 var _p = cc.TextFieldTTF.prototype;
477 
478 // Extended properties
479 /** @expose */
480 _p.charCount;
481 cc.defineGetterSetter(_p, "charCount", _p.getCharCount);
482 /** @expose */
483 _p.placeHolder;
484 cc.defineGetterSetter(_p, "placeHolder", _p.getPlaceHolder, _p.setPlaceHolder);
485 
486 /**
487  * Please use new TextFieldTTF instead. <br />
488  * Creates a cc.TextFieldTTF from a fontName, alignment, dimension and font size.
489  * @deprecated since v3.0 Please use new TextFieldTTF instead.
490  * @param {String} placeholder
491  * @param {cc.Size} dimensions
492  * @param {Number} alignment
493  * @param {String} fontName
494  * @param {Number} fontSize
495  * @return {cc.TextFieldTTF|Null}
496  */
497 cc.TextFieldTTF.create = function (placeholder, dimensions, alignment, fontName, fontSize) {
498     return new cc.TextFieldTTF(placeholder, dimensions, alignment, fontName, fontSize);
499 };
500 
501