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