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