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      * f 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         this._super();
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, dimensions, alignment, fontName, fontSize);
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      * @param {Boolean} isCallParent
180      */
181     setString:function (text, isCallParent) {
182         if (isCallParent && isCallParent == true) {
183             this._super(text);
184             return;
185         }
186 
187         this._inputText = text || "";
188 
189         // if there is no input text, display placeholder instead
190         if (!this._inputText.length)
191             this._super(this._placeHolder);
192         else
193             this._super(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             this.setString(this._placeHolder, true);
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         var context = ctx || cc.renderContext;
226         if (this._delegate && this._delegate.onDraw(this))
227             return;
228 
229         if (this._inputText && this._inputText.length > 0) {
230             this._super(context);
231             return;
232         }
233 
234         // draw placeholder
235         var color = this.getColor();
236         this.setColor(this._ColorSpaceHolder);
237         this._super(context);
238         this.setColor(color);
239     },
240 
241     //////////////////////////////////////////////////////////////////////////
242     // CCIMEDelegate interface
243     //////////////////////////////////////////////////////////////////////////
244     /**
245      * Open keyboard and receive input text.
246      * @return {Boolean}
247      */
248     attachWithIME:function () {
249         return cc.IMEDispatcher.getInstance().attachDelegateWithIME(this);
250     },
251 
252     /**
253      * End text input  and close keyboard.
254      * @return {Boolean}
255      */
256     detachWithIME:function () {
257         return cc.IMEDispatcher.getInstance().detachDelegateWithIME(this);
258     },
259 
260     /**
261      * @return {Boolean}
262      */
263     canAttachWithIME:function () {
264         return (this._delegate) ? (!this._delegate.onTextFieldAttachWithIME(this)) : true;
265     },
266 
267     /**
268      * When the delegate detach with IME, this method call by CCIMEDispatcher.
269      */
270     didAttachWithIME:function () {
271     },
272 
273     /**
274      * @return {Boolean}
275      */
276     canDetachWithIME:function () {
277         return (this._delegate) ? (!this._delegate.onTextFieldDetachWithIME(this)) : true;
278     },
279 
280     /**
281      * When the delegate detach with IME, this method call by CCIMEDispatcher.
282      */
283     didDetachWithIME:function () {
284     },
285 
286     /**
287      *  Delete backward
288      */
289     deleteBackward:function () {
290         var strLen = this._inputText.length;
291         if (strLen == 0)
292             return;
293 
294         // get the delete byte number
295         var deleteLen = 1;    // default, erase 1 byte
296 
297         if (this._delegate && this._delegate.onTextFieldDeleteBackward(this, this._inputText[strLen - deleteLen], deleteLen)) {
298             // delegate don't want delete backward
299             return;
300         }
301 
302         // if delete all text, show space holder string
303         if (strLen <= deleteLen) {
304             this._inputText = "";
305             this._charCount = 0;
306             this.setString(this._placeHolder, true);
307             return;
308         }
309 
310         // set new input text
311         var sText = this._inputText.substring(0, strLen - deleteLen);
312         this.setString(sText, false);
313     },
314 
315     /**
316      *  Remove delegate
317      */
318     removeDelegate:function () {
319         cc.IMEDispatcher.getInstance().removeDelegate(this);
320     },
321 
322     /**
323      * @param {String} text
324      * @param {Number} len
325      */
326     insertText:function (text, len) {
327         var sInsert = text;
328 
329         // insert \n means input end
330         var pos = sInsert.indexOf('\n');
331         if (pos > -1) {
332             sInsert = sInsert.substring(0, pos);
333         }
334 
335         if (sInsert.length > 0) {
336             if (this._delegate && this._delegate.onTextFieldInsertText(this, sInsert, sInsert.length)) {
337                 // delegate doesn't want insert text
338                 return;
339             }
340 
341             var sText = this._inputText + sInsert;
342             this._charCount = sText.length;
343             this.setString(sText);
344         }
345 
346         if (pos == -1)
347             return;
348 
349         // '\n' has inserted,  let delegate process first
350         if (this._delegate && this._delegate.onTextFieldInsertText(this, "\n", 1))
351             return;
352 
353         // if delegate hasn't process, detach with ime as default
354         this.detachWithIME();
355     },
356     /**
357      * @return {String}
358      */
359     getContentText:function () {
360         return this._inputText;
361     },
362 
363     //////////////////////////////////////////////////////////////////////////
364     // keyboard show/hide notification
365     //////////////////////////////////////////////////////////////////////////
366     keyboardWillShow:function (info) {
367     },
368     keyboardDidShow:function (info) {
369     },
370     keyboardWillHide:function (info) {
371     },
372     keyboardDidHide:function (info) {
373     }
374 });
375 
376 /**
377  *  creates a cc.TextFieldTTF from a fontName, alignment, dimension and font size
378  * @param {String} placeholder
379  * @param {cc.Size} dimensions
380  * @param {Number} alignment
381  * @param {String} fontName
382  * @param {Number} fontSize
383  * @return {cc.TextFieldTTF|Null}
384  * @example
385  * //example
386  * // When five parameters
387  * var textField = cc.TextFieldTTF.create("<click here for input>", cc.size(100,50), cc.TEXT_ALIGNMENT_LEFT,"Arial", 32);
388  * // When three parameters
389  * var textField = cc.TextFieldTTF.create("<click here for input>", "Arial", 32);
390  */
391 cc.TextFieldTTF.create = function (placeholder, dimensions, alignment, fontName, fontSize) {
392     var ret;
393     switch (arguments.length) {
394         case 5:
395             ret = new cc.TextFieldTTF();
396             if (ret && ret.initWithPlaceHolder("", dimensions, alignment, fontName, fontSize)) {
397                 if (placeholder)
398                     ret.setPlaceHolder(placeholder);
399                 return ret;
400             }
401             return null;
402             break;
403         case 3:
404             ret = new cc.TextFieldTTF();
405             fontName = arguments[1];
406             fontSize = arguments[2];
407             if (ret && ret.initWithString(["", fontName, fontSize])) {
408                 if (placeholder)
409                     ret.setPlaceHolder(placeholder);
410                 return ret;
411             }
412             return null;
413             break;
414         default:
415             throw "Argument must be non-nil ";
416             break;
417     }
418 };
419 
420