1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3 
  4  http://www.cocos2d-x.org
  5 
  6  Permission is hereby granted, free of charge, to any person obtaining a copy
  7  of this software and associated documentation files (the "Software"), to deal
  8  in the Software without restriction, including without limitation the rights
  9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  copies of the Software, and to permit persons to whom the Software is
 11  furnished to do so, subject to the following conditions:
 12 
 13  The above copyright notice and this permission notice shall be included in
 14  all copies or substantial portions of the Software.
 15 
 16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22  THE SOFTWARE.
 23  ****************************************************************************/
 24 ccs.TextFiledEventType = {
 25     attach_with_me: 0,
 26     detach_with_ime: 1,
 27     insert_text: 2,
 28     delete_backward: 3
 29 };
 30 
 31 ccs.TEXTFIELDRENDERERZ = -1;
 32 /**
 33  * Base class for ccs.UICCTextField
 34  * @class
 35  * @extends cc.TextFieldTTF
 36  */
 37 ccs.UICCTextField = cc.TextFieldTTF.extend({
 38     _maxLengthEnabled: false,
 39     _maxLength: 0,
 40     _passwordEnabled: false,
 41     _passwordStyleText: "",
 42     _attachWithIME: false,
 43     _detachWithIME: false,
 44     _insertText: false,
 45     _deleteBackward: false,
 46     ctor: function () {
 47         cc.TextFieldTTF.prototype.ctor.call(this);
 48         this._maxLengthEnabled = false;
 49         this._maxLength = 0;
 50         this._passwordEnabled = false;
 51         this._passwordStyleText = "*";
 52         this._attachWithIME = false;
 53         this._detachWithIME = false;
 54         this._insertText = false;
 55         this._deleteBackward = false;
 56     },
 57     init:function(){
 58         if(ccs.Widget.prototype.init.call(this)){
 59             this.setTouchEnabled(true);
 60             return true;
 61         }
 62         return false;
 63     },
 64     onEnter: function () {
 65         cc.TextFieldTTF.prototype.onEnter.call(this);
 66         cc.TextFieldTTF.prototype.setDelegate.call(this,this);
 67     },
 68 
 69     //CCTextFieldDelegate
 70 
 71     onTextFieldAttachWithIME: function (sender) {
 72         this.setAttachWithIME(true);
 73         return false;
 74     },
 75 
 76     onTextFieldInsertText: function (sender, text, len) {
 77         if (len == 1 && text == "\n") {
 78             return false;
 79         }
 80         this.setInsertText(true);
 81         if (this._maxLengthEnabled) {
 82             if (cc.TextFieldTTF.prototype.getCharCount.call(this) >= this._maxLength) {
 83                 return true;
 84             }
 85         }
 86 
 87         return false;
 88     },
 89 
 90     onTextFieldDeleteBackward: function (sender, delText, nLen) {
 91         this.setDeleteBackward(true);
 92         return false;
 93     },
 94 
 95     onTextFieldDetachWithIME: function (sender) {
 96         this.setDetachWithIME(true);
 97         return false;
 98     },
 99 
100     insertText: function (text, len) {
101         var str_text = text;
102         var locString = cc.TextFieldTTF.prototype.getString.call(this);
103         var str_len = locString.length;
104         var multiple, header;
105         if (text != "\n") {
106             if (this._maxLengthEnabled) {
107                 multiple = 1;
108                 header = text.charCodeAt(0);
109                 if (header < 0 || header > 127) {
110                     multiple = 3;
111                 }
112 
113                 if (str_len + len > this._maxLength * multiple) {
114                     str_text = str_text.substr(0, this._maxLength * multiple);
115                     len = this._maxLength * multiple;
116                 }
117             }
118         }
119         cc.TextFieldTTF.prototype.insertText.call(this,str_text, len);
120 
121         // password
122         if (this._passwordEnabled) {
123             if (cc.TextFieldTTF.prototype.getCharCount.call(this) > 0) {
124                 this.setPasswordText(this._inputText);
125             }
126         }
127     },
128 
129     deleteBackward: function () {
130         cc.TextFieldTTF.prototype.deleteBackward.call(this);
131 
132         if (cc.TextFieldTTF.prototype.getCharCount.call(this) > 0) {
133             // password
134             if (this._passwordEnabled) {
135                 this.setPasswordText(this._inputText);
136             }
137         }
138     },
139 
140     openIME: function () {
141         cc.TextFieldTTF.prototype.attachWithIME.call(this);
142     },
143 
144     closeIME: function () {
145         cc.TextFieldTTF.prototype.detachWithIME.call(this);
146     },
147     onDraw:function (sender) {
148         return false;
149     },
150     setMaxLengthEnabled: function (enable) {
151         this._maxLengthEnabled = enable;
152     },
153 
154     isMaxLengthEnabled: function () {
155         return this._maxLengthEnabled;
156     },
157 
158     setMaxLength: function (length) {
159         this._maxLength = length;
160     },
161 
162     getMaxLength: function () {
163         return this._maxLength;
164     },
165 
166     getCharCount: function () {
167         return cc.TextFieldTTF.prototype.getCharCount.call(this);
168     },
169 
170     setPasswordEnabled: function (enable) {
171         this._passwordEnabled = enable;
172     },
173 
174     isPasswordEnabled: function () {
175         return this._passwordEnabled;
176     },
177 
178     setPasswordStyleText: function (styleText) {
179         if (styleText.length > 1) {
180             return;
181         }
182         var header = styleText.charCodeAt(0);
183         if (header < 33 || header > 126) {
184             return;
185         }
186         this._passwordStyleText = styleText;
187     },
188 
189     setPasswordText: function (text) {
190         var tempStr = "";
191         for (var i = 0; i < text.length; ++i) {
192             tempStr += this._passwordStyleText;
193         }
194         cc.LabelTTF.prototype.setString.call(this, tempStr);
195     },
196 
197     setAttachWithIME: function (attach) {
198         this._attachWithIME = attach;
199     },
200 
201     getAttachWithIME: function () {
202         return this._attachWithIME;
203     },
204 
205     setDetachWithIME: function (detach) {
206         this._detachWithIME = detach;
207     },
208 
209     getDetachWithIME: function () {
210         return this._detachWithIME;
211     },
212 
213     setInsertText: function (insert) {
214         this._insertText = insert;
215     },
216 
217     getInsertText: function () {
218         return this._insertText;
219     },
220 
221     setDeleteBackward: function (deleteBackward) {
222         this._deleteBackward = deleteBackward;
223     },
224 
225     getDeleteBackward: function () {
226         return this._deleteBackward;
227     }
228 });
229 
230 ccs.UICCTextField.create = function (placeholder, fontName, fontSize) {
231     var ret = new ccs.UICCTextField();
232     if (ret && ret.initWithString("", fontName, fontSize)) {
233         if (placeholder) {
234             ret.setPlaceHolder(placeholder);
235         }
236         return ret;
237     }
238     return null;
239 };
240 
241 /**
242  * Base class for ccs.TextField
243  * @class
244  * @extends ccs.Widget
245  */
246 ccs.TextField = ccs.Widget.extend(/** @lends ccs.TextField# */{
247     _textFieldRender: null,
248     _touchWidth: 0,
249     _touchHeight: 0,
250     _useTouchArea: false,
251     _textFieldEventListener: null,
252     _textFieldEventSelector: null,
253     _attachWithIMEListener: null,
254     _detachWithIMEListener: null,
255     _insertTextListener: null,
256     _deleteBackwardListener: null,
257     _attachWithIMESelector: null,
258     _detachWithIMESelector: null,
259     _insertTextSelector: null,
260     _deleteBackwardSelector: null,
261     _passwordStyleText:"",
262     ctor: function () {
263         ccs.Widget.prototype.ctor.call(this);
264         this._textFieldRender = null;
265         this._touchWidth = 0;
266         this._touchHeight = 0;
267         this._useTouchArea = false;
268 
269         this._textFieldEventListener = null;
270         this._textFieldEventSelector = null;
271         this._attachWithIMEListener = null;
272         this._detachWithIMEListener = null;
273         this._insertTextListener = null;
274         this._deleteBackwardListener = null;
275         this._attachWithIMESelector = null;
276         this._detachWithIMESelector = null;
277         this._insertTextSelector = null;
278         this._deleteBackwardSelector = null;
279     },
280 
281     onEnter:function(){
282         ccs.Widget.prototype.onEnter.call(this);
283         this.setUpdateEnabled(true);
284     },
285 
286     initRenderer: function () {
287         this._textFieldRender = ccs.UICCTextField.create("input words here", "Thonburi", 20);
288         cc.Node.prototype.addChild.call(this, this._textFieldRender, ccs.TEXTFIELDRENDERERZ, -1);
289 
290     },
291 
292     /**
293      * Set touch size.
294      * @param {cc.Size} size
295      */
296     setTouchSize: function (size) {
297         this._useTouchArea = true;
298         this._touchWidth = size.width;
299         this._touchHeight = size.height;
300     },
301 
302     /**
303      * Get touch size.
304      * @returns {cc.Size}
305      */
306     getTouchSize:function(){
307         return cc.size(this._touchWidth,this._touchHeight);
308     },
309 
310     /**
311      *  Changes the string value of textField.
312      * @param {String} text
313      */
314     setText: function (text) {
315         if (!text) {
316             return;
317         }
318         text = String(text);
319         if (this.isMaxLengthEnabled()) {
320             text = text.substr(0, this.getMaxLength());
321         }
322         if (this.isPasswordEnabled()) {
323             this._textFieldRender.setPasswordText(text);
324             this._textFieldRender.insertText(text, text.length);
325         }
326         else {
327             this._textFieldRender.setString(text);
328         }
329         this._textFieldRender.setString(text);
330         this.textfieldRendererScaleChangedWithSize();
331     },
332 
333     /**
334      * @param {String} value
335      */
336     setPlaceHolder: function (value) {
337         this._textFieldRender.setPlaceHolder(value);
338         this.textfieldRendererScaleChangedWithSize();
339     },
340 
341     /**
342      * @returns {String}
343      */
344     getPlaceHolder:function(){
345         return this._textFieldRender.getPlaceHolder();
346     },
347 
348     /**
349      * @param {cc.Size} size
350      */
351     setFontSize: function (size) {
352         this._textFieldRender.setFontSize(size);
353         this.textfieldRendererScaleChangedWithSize();
354     },
355 
356     /**
357      * @returns {Number}
358      */
359     getFontSize:function(){
360         return this._textFieldRender.getFontSize();
361     },
362 
363     /**
364      * @param {String} name
365      */
366     setFontName: function (name) {
367         this._textFieldRender.setFontName(name);
368         this.textfieldRendererScaleChangedWithSize();
369     },
370 
371     /**
372      * @returns {String}
373      */
374     getFontName:function(){
375         return this._textFieldRender.getFontName();
376     },
377 
378     /**
379      * detach with IME
380      */
381     didNotSelectSelf: function () {
382         this._textFieldRender.detachWithIME();
383     },
384 
385     /**
386      * get textField string value
387      * @returns {String}
388      */
389     getStringValue: function () {
390         return this._textFieldRender.getString();
391     },
392 
393     /**
394      * touch began
395      * @param {cc.Point} touchPoint
396      */
397     onTouchBegan: function (touchPoint) {
398         var pass = ccs.Widget.prototype.onTouchBegan.call(this, touchPoint);
399         return pass;
400     },
401 
402     /**
403      * touch ended
404      * @param touchPoint
405      */
406     onTouchEnded: function (touchPoint) {
407         ccs.Widget.prototype.onTouchEnded.call(this, touchPoint);
408         this._textFieldRender.attachWithIME();
409     },
410 
411     /**
412      * @param {Boolean} enable
413      */
414     setMaxLengthEnabled: function (enable) {
415         this._textFieldRender.setMaxLengthEnabled(enable);
416     },
417 
418     /**
419      * @returns {Boolean}
420      */
421     isMaxLengthEnabled: function () {
422         return this._textFieldRender.isMaxLengthEnabled();
423     },
424 
425     /**
426      * @param {number} length
427      */
428     setMaxLength: function (length) {
429         this._textFieldRender.setMaxLength(length);
430     },
431 
432     /**
433      * @returns {number} length
434      */
435     getMaxLength: function () {
436         return this._textFieldRender.getMaxLength();
437     },
438 
439     /**
440      * @param {Boolean} enable
441      */
442     setPasswordEnabled: function (enable) {
443         this._textFieldRender.setPasswordEnabled(enable);
444     },
445 
446     /**
447      * @returns {Boolean}
448      */
449     isPasswordEnabled: function () {
450         return this._textFieldRender.isPasswordEnabled();
451     },
452 
453     /**
454      * @param {String} styleText
455      */
456     setPasswordStyleText: function (styleText) {
457         this._textFieldRender.setPasswordStyleText(styleText);
458         this._passwordStyleText = styleText;
459     },
460 
461     /**
462      * @returns {String}
463      */
464     getPasswordStyleText:function(){
465         return this._passwordStyleText;
466     },
467 
468     update: function (dt) {
469         if (this.getAttachWithIME()) {
470             this.attachWithIMEEvent();
471             this.setAttachWithIME(false);
472         }
473         if (this.getDetachWithIME()) {
474             this.detachWithIMEEvent();
475             this.setDetachWithIME(false);
476         }
477         if (this.getInsertText()) {
478             this.insertTextEvent();
479             this.setInsertText(false);
480 
481             this.textfieldRendererScaleChangedWithSize();
482         }
483         if (this.getDeleteBackward()) {
484             this.deleteBackwardEvent();
485             this.setDeleteBackward(false);
486         }
487     },
488 
489     /**
490      * get whether attach with IME.
491      * @returns {Boolean}
492      */
493     getAttachWithIME: function () {
494         return this._textFieldRender.getAttachWithIME();
495     },
496 
497     /**
498      * set attach with IME.
499      * @param {Boolean} attach
500      */
501     setAttachWithIME: function (attach) {
502         this._textFieldRender.setAttachWithIME(attach);
503     },
504 
505     /**
506      * get whether eetach with IME.
507      * @returns {Boolean}
508      */
509     getDetachWithIME: function () {
510         return this._textFieldRender.getDetachWithIME();
511     },
512 
513     /**
514      * set detach with IME.
515      * @param {Boolean} detach
516      */
517     setDetachWithIME: function (detach) {
518         this._textFieldRender.setDetachWithIME(detach);
519     },
520 
521     /**
522      * get insertText
523      * @returns {String}
524      */
525     getInsertText: function () {
526         return this._textFieldRender.getInsertText();
527     },
528 
529     /**
530      * set insertText
531      * @param {String} insertText
532      */
533     setInsertText: function (insertText) {
534         this._textFieldRender.setInsertText(insertText);
535     },
536 
537     /**
538      * @returns {Boolean}
539      */
540     getDeleteBackward: function () {
541         return this._textFieldRender.getDeleteBackward();
542     },
543 
544     /**
545      * @param {Boolean} deleteBackward
546      */
547     setDeleteBackward: function (deleteBackward) {
548         this._textFieldRender.setDeleteBackward(deleteBackward);
549     },
550 
551     attachWithIMEEvent: function () {
552         if (this._textFieldEventListener && this._textFieldEventSelector) {
553             this._textFieldEventSelector.call(this._textFieldEventListener, this, ccs.TextFiledEventType.attach_with_me);
554         }
555     },
556 
557     detachWithIMEEvent: function () {
558         if (this._textFieldEventListener && this._textFieldEventSelector) {
559             this._textFieldEventSelector.call(this._textFieldEventListener, this, ccs.TextFiledEventType.detach_with_ime);
560         }
561     },
562 
563     insertTextEvent: function () {
564         if (this._textFieldEventListener && this._textFieldEventSelector) {
565             this._textFieldEventSelector.call(this._textFieldEventListener, this, ccs.TextFiledEventType.insert_text);
566         }
567     },
568 
569     deleteBackwardEvent: function () {
570         if (this._textFieldEventListener && this._textFieldEventSelector) {
571             this._textFieldEventSelector.call(this._textFieldEventListener, this, ccs.TextFiledEventType.delete_backward);
572         }
573     },
574 
575     /**
576      * add event listener
577      * @param {Function} selector
578      * @param {Object} target
579      */
580     addEventListenerTextField: function (selector, target) {
581         this._textFieldEventSelector = selector;
582         this._textFieldEventListener = target;
583     },
584 
585     /**
586      * check hit
587      * @param {cc.Point} pt
588      * @returns {boolean}
589      */
590     hitTest: function (pt) {
591         var nsp = this.convertToNodeSpace(pt);
592         var locSize = this._textFieldRender.getContentSize();
593         var bb = cc.rect(-locSize.width * this._anchorPoint.x, -locSize.height * this._anchorPoint.y, locSize.width, locSize.height);
594         return (nsp.x >= bb.x && nsp.x <= bb.x + bb.width && nsp.y >= bb.y && nsp.y <= bb.y + bb.height);
595     },
596 
597     /**
598      * override "setAnchorPoint" of widget.
599      * @param {cc.Point|Number} point The anchor point of UILabelBMFont or The anchor point.x of UILabelBMFont.
600      * @param {Number} [y] The anchor point.y of UILabelBMFont.
601      */
602     setAnchorPoint: function (point, y) {
603         if(y === undefined){
604 	        ccs.Widget.prototype.setAnchorPoint.call(this, point);
605 	        this._textFieldRender.setAnchorPoint(point);
606         } else {
607 	        ccs.Widget.prototype.setAnchorPoint.call(this, point, y);
608 	        this._textFieldRender.setAnchorPoint(point, y);
609         }
610     },
611 
612     onSizeChanged: function () {
613         ccs.Widget.prototype.onSizeChanged.call(this);
614         this.textfieldRendererScaleChangedWithSize();
615     },
616 
617     textfieldRendererScaleChangedWithSize: function () {
618         if (this._ignoreSize) {
619             this._textFieldRender.setScale(1.0);
620             var rendererSize = this.getContentSize();
621             this._size.width = rendererSize.width;
622             this._size.height = rendererSize.height;
623         }
624         else {
625             var textureSize = this.getContentSize();
626             if (textureSize.width <= 0.0 || textureSize.height <= 0.0) {
627                 this._textFieldRender.setScale(1.0);
628                 return;
629             }
630             var scaleX = this._size.width / textureSize.width;
631             var scaleY = this._size.height / textureSize.height;
632             this._textFieldRender.setScaleX(scaleX);
633             this._textFieldRender.setScaleY(scaleY);
634         }
635     },
636 
637     /**
638      * override "getContentSize" method of widget.
639      * @returns {cc.Size}
640      */
641     getContentSize: function () {
642         return this._textFieldRender.getContentSize();
643     },
644 
645     /**
646      * override "getContentSize" method of widget.
647      * @returns {cc.Node}
648      */
649     getVirtualRenderer: function () {
650         return this._textFieldRender;
651     },
652 
653     updateTextureColor: function () {
654         this.updateColorToRenderer(this._textFieldRender);
655     },
656 
657     updateTextureOpacity: function () {
658         this.updateOpacityToRenderer(this._textFieldRender);
659     },
660 
661     updateTextureRGBA: function () {
662         this.updateRGBAToRenderer(this._textFieldRender);
663     },
664     
665     /**
666      * Returns the "class name" of widget.
667      * @returns {string}
668      */
669     getDescription: function () {
670         return "TextField";
671     },
672 
673     attachWithIME: function () {
674         this._textFieldRender.attachWithIME();
675     },
676 
677     createCloneInstance: function () {
678         return ccs.TextField.create();
679     },
680 
681     copySpecialProperties: function (textField) {
682         this.setText(textField._textFieldRender.getString());
683         this.setPlaceHolder(textField.getStringValue());
684         this.setFontSize(textField._textFieldRender.getFontSize());
685         this.setFontName(textField._textFieldRender.getFontName());
686         this.setMaxLengthEnabled(textField.isMaxLengthEnabled());
687         this.setMaxLength(textField.getMaxLength());
688         this.setPasswordEnabled(textField.isPasswordEnabled());
689         this.setPasswordStyleText(textField._passwordStyleText);
690         this.setAttachWithIME(textField.getAttachWithIME());
691         this.setDetachWithIME(textField.getDetachWithIME());
692         this.setInsertText(textField.getInsertText());
693         this.setDeleteBackward(textField.getDeleteBackward());
694     }
695 });
696 /**
697  * allocates and initializes a UITextField.
698  * @constructs
699  * @return {ccs.TextField}
700  * @example
701  * // example
702  * var uiTextField = ccs.TextField.create();
703  */
704 ccs.TextField.create = function () {
705     var uiTextField = new ccs.TextField();
706     if (uiTextField && uiTextField.init()) {
707         return uiTextField;
708     }
709     return null;
710 };