1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga 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 cc.TextFieldTTF = cc.LabelTTF.extend(/** @lends cc.TextFieldTTF# */{
 94 	delegate:null,
 95 	colorSpaceHolder:null,
 96 
 97     _lens:null,
 98     _inputText:"",
 99     _placeHolder:"",
100     _charCount:0,
101     _className:"TextFieldTTF",
102 
103     ctor:function () {
104         this.colorSpaceHolder = cc.color(127, 127, 127);
105         cc.imeDispatcher.addDelegate(this);
106         cc.LabelTTF.prototype.ctor.call(this);
107     },
108 
109     /**
110      * @return {cc.Node}
111      */
112     getDelegate:function () {
113         return this.delegate;
114     },
115 
116     /**
117      * @param {cc.Node} value
118      */
119     setDelegate:function (value) {
120         this.delegate = value;
121     },
122 
123     /**
124      * @return {Number}
125      */
126     getCharCount:function () {
127         return this._charCount;
128     },
129 
130     /**
131      * @return {cc.Color}
132      */
133     getColorSpaceHolder:function () {
134         return this.colorSpaceHolder;
135     },
136 
137     /**
138      * @param {cc.Color} value
139      */
140     setColorSpaceHolder:function (value) {
141         this.colorSpaceHolder = value;
142     },
143     /**
144      * Initializes the cc.TextFieldTTF with a font name, alignment, dimension and font size
145      * @param {String} placeholder
146      * @param {cc.Size} dimensions
147      * @param {Number} alignment
148      * @param {String} fontName
149      * @param {Number} fontSize
150      * @return {Boolean}
151      * @example
152      * //example
153      * var  textField = new cc.TextFieldTTF();
154      * // When five parameters
155      * textField.initWithPlaceHolder("<click here for input>", cc.size(100,50), cc.TEXT_ALIGNMENT_LEFT,"Arial", 32);
156      * // When three parameters
157      * textField.initWithPlaceHolder("<click here for input>", "Arial", 32);
158      */
159     initWithPlaceHolder:function (placeholder, dimensions, alignment, fontName, fontSize) {
160         switch (arguments.length) {
161             case 5:
162                 if (placeholder) {
163                     this._placeHolder = placeholder;
164                 }
165                 return this.initWithString(this._placeHolder,fontName, fontSize, dimensions, alignment);
166                 break;
167             case 3:
168                 if (placeholder) {
169                     this._placeHolder = placeholder;
170                 }
171                 fontName = arguments[1];
172                 fontSize = arguments[2];
173                 return this.initWithString(this._placeHolder, fontName, fontSize);
174                 break;
175             default:
176                 throw "Argument must be non-nil ";
177                 break;
178         }
179     },
180 
181     /**
182      * Input text property
183      * @param {String} text
184      */
185     setString:function (text) {
186         text = String(text);
187         this._inputText = text || "";
188 
189         // if there is no input text, display placeholder instead
190         if (!this._inputText.length)
191             cc.LabelTTF.prototype.setString.call(this, this._placeHolder);
192         else
193             cc.LabelTTF.prototype.setString.call(this,this._inputText);
194         this._charCount = this._inputText.length;
195     },
196 
197     /**
198      * @return {String}
199      */
200     getString:function () {
201         return this._inputText;
202     },
203 
204     /**
205      * @param {String} text
206      */
207     setPlaceHolder:function (text) {
208         this._placeHolder = text || "";
209         if (!this._inputText.length) {
210             cc.LabelTTF.prototype.setString.call(this,this._placeHolder);
211         }
212     },
213 
214     /**
215      * @return {String}
216      */
217     getPlaceHolder:function () {
218         return this._placeHolder;
219     },
220 
221     /**
222      * @param {CanvasContext} ctx
223      */
224     draw:function (ctx) {
225         //console.log("size",this._contentSize);
226         var context = ctx || cc._renderContext;
227         if (this.delegate && this.delegate.onDraw(this))
228             return;
229 
230         if (this._inputText && this._inputText.length > 0) {
231             cc.LabelTTF.prototype.draw.call(this, context);
232             return;
233         }
234 
235         // draw placeholder
236         var color = this.color;
237         this.color = this.colorSpaceHolder;
238         if(cc._renderType === cc._RENDER_TYPE_CANVAS)
239             this._updateTexture();
240         cc.LabelTTF.prototype.draw.call(this, context);
241         this.color = color;
242     },
243 
244     //////////////////////////////////////////////////////////////////////////
245     // CCIMEDelegate interface
246     //////////////////////////////////////////////////////////////////////////
247     /**
248      * Open keyboard and receive input text.
249      * @return {Boolean}
250      */
251     attachWithIME:function () {
252         return cc.imeDispatcher.attachDelegateWithIME(this);
253     },
254 
255     /**
256      * End text input  and close keyboard.
257      * @return {Boolean}
258      */
259     detachWithIME:function () {
260         return cc.imeDispatcher.detachDelegateWithIME(this);
261     },
262 
263     /**
264      * @return {Boolean}
265      */
266     canAttachWithIME:function () {
267         return (this.delegate) ? (!this.delegate.onTextFieldAttachWithIME(this)) : true;
268     },
269 
270     /**
271      * When the delegate detach with IME, this method call by CCIMEDispatcher.
272      */
273     didAttachWithIME:function () {
274     },
275 
276     /**
277      * @return {Boolean}
278      */
279     canDetachWithIME:function () {
280         return (this.delegate) ? (!this.delegate.onTextFieldDetachWithIME(this)) : true;
281     },
282 
283     /**
284      * When the delegate detach with IME, this method call by CCIMEDispatcher.
285      */
286     didDetachWithIME:function () {
287     },
288 
289     /**
290      *  Delete backward
291      */
292     deleteBackward:function () {
293         var strLen = this._inputText.length;
294         if (strLen == 0)
295             return;
296 
297         // get the delete byte number
298         var deleteLen = 1;    // default, erase 1 byte
299 
300         if (this.delegate && this.delegate.onTextFieldDeleteBackward(this, this._inputText[strLen - deleteLen], deleteLen)) {
301             // delegate don't want delete backward
302             return;
303         }
304 
305         // if delete all text, show space holder string
306         if (strLen <= deleteLen) {
307             this._inputText = "";
308             this._charCount = 0;
309             cc.LabelTTF.prototype.setString.call(this,this._placeHolder);
310             return;
311         }
312 
313         // set new input text
314         var sText = this._inputText.substring(0, strLen - deleteLen);
315         this.string = sText;
316     },
317 
318     /**
319      *  Remove delegate
320      */
321     removeDelegate:function () {
322         cc.imeDispatcher.removeDelegate(this);
323     },
324 
325     /**
326      * @param {String} text
327      * @param {Number} len
328      */
329     insertText:function (text, len) {
330         var sInsert = text;
331 
332         // insert \n means input end
333         var pos = sInsert.indexOf('\n');
334         if (pos > -1) {
335             sInsert = sInsert.substring(0, pos);
336         }
337 
338         if (sInsert.length > 0) {
339             if (this.delegate && this.delegate.onTextFieldInsertText(this, sInsert, sInsert.length)) {
340                 // delegate doesn't want insert text
341                 return;
342             }
343 
344             var sText = this._inputText + sInsert;
345             this._charCount = sText.length;
346             this.string = sText;
347         }
348 
349         if (pos == -1)
350             return;
351 
352         // '\n' has inserted,  let delegate process first
353         if (this.delegate && this.delegate.onTextFieldInsertText(this, "\n", 1))
354             return;
355 
356         // if delegate hasn't process, detach with ime as default
357         this.detachWithIME();
358     },
359     /**
360      * @return {String}
361      */
362     getContentText:function () {
363         return this._inputText;
364     },
365 
366     //////////////////////////////////////////////////////////////////////////
367     // keyboard show/hide notification
368     //////////////////////////////////////////////////////////////////////////
369     keyboardWillShow:function (info) {
370     },
371     keyboardDidShow:function (info) {
372     },
373     keyboardWillHide:function (info) {
374     },
375     keyboardDidHide:function (info) {
376     }
377 });
378 
379 window._p = cc.TextFieldTTF.prototype;
380 
381 // Extended properties
382 /** @expose */
383 _p.charCount;
384 cc.defineGetterSetter(_p, "charCount", _p.getCharCount);
385 /** @expose */
386 _p.placeHolder;
387 cc.defineGetterSetter(_p, "placeHolder", _p.getPlaceHolder, _p.setPlaceHolder);
388 delete window._p;
389 
390 /**
391  *  creates a cc.TextFieldTTF from a fontName, alignment, dimension and font size
392  * @param {String} placeholder
393  * @param {cc.Size} dimensions
394  * @param {Number} alignment
395  * @param {String} fontName
396  * @param {Number} fontSize
397  * @return {cc.TextFieldTTF|Null}
398  * @example
399  * //example
400  * // When five parameters
401  * var textField = cc.TextFieldTTF.create("<click here for input>", cc.size(100,50), cc.TEXT_ALIGNMENT_LEFT,"Arial", 32);
402  * // When three parameters
403  * var textField = cc.TextFieldTTF.create("<click here for input>", "Arial", 32);
404  */
405 cc.TextFieldTTF.create = function (placeholder, dimensions, alignment, fontName, fontSize) {
406     var ret;
407     switch (arguments.length) {
408         case 5:
409             ret = new cc.TextFieldTTF();
410             if (ret && ret.initWithPlaceHolder("", dimensions, alignment, fontName, fontSize)) {
411                 if (placeholder)
412                     ret.placeHolder = placeholder;
413                 return ret;
414             }
415             return null;
416             break;
417         case 3:
418             ret = new cc.TextFieldTTF();
419             fontName = arguments[1];
420             fontSize = arguments[2];
421             if (ret && ret.initWithString("", fontName, fontSize)) {
422                 if (placeholder)
423                     ret.placeHolder = placeholder;
424                 return ret;
425             }
426             return null;
427             break;
428         default:
429             throw "Argument must be non-nil ";
430             break;
431     }
432 };
433 
434