UIRichText.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. /****************************************************************************
  2. Copyright (c) 2011-2012 cocos2d-x.org
  3. Copyright (c) 2013-2014 Chukong Technologies Inc.
  4. http://www.cocos2d-x.org
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11. The above copyright notice and this permission notice shall be included in
  12. all copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. THE SOFTWARE.
  20. ****************************************************************************/
  21. /**
  22. * ccui.RichElement is the base class of RichElementText, RichElementImage etc. It has type, tag, color and opacity attributes.
  23. * @class
  24. * @extends ccui.Class
  25. */
  26. ccui.RichElement = ccui.Class.extend(/** @lends ccui.RichElement# */{
  27. _type: 0,
  28. _tag: 0,
  29. _color: null,
  30. _opacity:0,
  31. /**
  32. * Constructor of ccui.RichElement
  33. */
  34. ctor: function () {
  35. this._type = 0;
  36. this._tag = 0;
  37. this._color = cc.color(255, 255, 255, 255);
  38. },
  39. /**
  40. * Initializes a richElement.
  41. * @param {Number} tag
  42. * @param {cc.Color} color
  43. * @param {Number} opacity
  44. */
  45. init: function (tag, color, opacity) {
  46. this._tag = tag;
  47. this._color.r = color.r;
  48. this._color.g = color.g;
  49. this._color.b = color.b;
  50. this._opacity = opacity;
  51. if(opacity === undefined)
  52. this._color.a = color.a;
  53. else
  54. this._color.a = opacity;
  55. }
  56. });
  57. /**
  58. * The text element for RichText, it has text, fontName, fontSize attributes.
  59. * @class
  60. * @extends ccui.RichElement
  61. */
  62. ccui.RichElementText = ccui.RichElement.extend(/** @lends ccui.RichElementText# */{
  63. _text: "",
  64. _fontName: "",
  65. _fontSize: 0,
  66. /**
  67. * Constructor of ccui.RichElementText
  68. * @param {Number} tag
  69. * @param {cc.Color} color
  70. * @param {Number} opacity
  71. * @param {String} text
  72. * @param {String} fontName
  73. * @param {Number} fontSize
  74. */
  75. ctor: function (tag, color, opacity, text, fontName, fontSize) {
  76. ccui.RichElement.prototype.ctor.call(this);
  77. this._type = ccui.RichElement.TEXT;
  78. this._text = "";
  79. this._fontName = "";
  80. this._fontSize = 0;
  81. fontSize && this.init(tag, color, opacity, text, fontName, fontSize);
  82. },
  83. /**
  84. * Initializes a ccui.RichElementText.
  85. * @param {Number} tag
  86. * @param {cc.Color} color
  87. * @param {Number} opacity
  88. * @param {String} text
  89. * @param {String} fontName
  90. * @param {Number} fontSize
  91. * @override
  92. */
  93. init: function (tag, color, opacity, text, fontName, fontSize) {
  94. ccui.RichElement.prototype.init.call(this, tag, color, opacity);
  95. this._text = text;
  96. this._fontName = fontName;
  97. this._fontSize = fontSize;
  98. }
  99. });
  100. /**
  101. * Create a richElementText
  102. * @deprecated since v3.0, please use new ccui.RichElementText() instead.
  103. * @param {Number} tag
  104. * @param {cc.Color} color
  105. * @param {Number} opacity
  106. * @param {String} text
  107. * @param {String} fontName
  108. * @param {Number} fontSize
  109. * @returns {ccui.RichElementText}
  110. */
  111. ccui.RichElementText.create = function (tag, color, opacity, text, fontName, fontSize) {
  112. return new ccui.RichElementText(tag, color, opacity, text, fontName, fontSize);
  113. };
  114. /**
  115. * The image element for RichText, it has filePath, textureRect, textureType attributes.
  116. * @class
  117. * @extends ccui.RichElement
  118. */
  119. ccui.RichElementImage = ccui.RichElement.extend(/** @lends ccui.RichElementImage# */{
  120. _filePath: "",
  121. _textureRect: null,
  122. _textureType: 0,
  123. /**
  124. * Constructor of ccui.RichElementImage
  125. * @param {Number} tag
  126. * @param {cc.Color} color
  127. * @param {Number} opacity
  128. * @param {String} filePath
  129. */
  130. ctor: function (tag, color, opacity, filePath) {
  131. ccui.RichElement.prototype.ctor.call(this);
  132. this._type = ccui.RichElement.IMAGE;
  133. this._filePath = "";
  134. this._textureRect = cc.rect(0, 0, 0, 0);
  135. this._textureType = 0;
  136. filePath && this.init(tag, color, opacity, filePath);
  137. },
  138. /**
  139. * Initializes a ccui.RichElementImage
  140. * @param {Number} tag
  141. * @param {cc.Color} color
  142. * @param {Number} opacity
  143. * @param {String} filePath
  144. * @override
  145. */
  146. init: function (tag, color, opacity, filePath) {
  147. ccui.RichElement.prototype.init.call(this, tag, color, opacity);
  148. this._filePath = filePath;
  149. }
  150. });
  151. /**
  152. * Create a richElementImage
  153. * @deprecated since v3.0, please use new ccui.RichElementImage() instead.
  154. * @param {Number} tag
  155. * @param {cc.Color} color
  156. * @param {Number} opacity
  157. * @param {String} filePath
  158. * @returns {ccui.RichElementImage}
  159. */
  160. ccui.RichElementImage.create = function (tag, color, opacity, filePath) {
  161. return new ccui.RichElementImage(tag, color, opacity, filePath);
  162. };
  163. /**
  164. * The custom node element for RichText.
  165. * @class
  166. * @extends ccui.RichElement
  167. */
  168. ccui.RichElementCustomNode = ccui.RichElement.extend(/** @lends ccui.RichElementCustomNode# */{
  169. _customNode: null,
  170. /**
  171. * Constructor of ccui.RichElementCustomNode
  172. * @param {Number} tag
  173. * @param {cc.Color} color
  174. * @param {Number} opacity
  175. * @param {cc.Node} customNode
  176. */
  177. ctor: function (tag, color, opacity, customNode) {
  178. ccui.RichElement.prototype.ctor.call(this);
  179. this._type = ccui.RichElement.CUSTOM;
  180. this._customNode = null;
  181. customNode && this.init(tag, color, opacity, customNode);
  182. },
  183. /**
  184. * Initializes a ccui.RichElementCustomNode
  185. * @param {Number} tag
  186. * @param {cc.Color} color
  187. * @param {Number} opacity
  188. * @param {cc.Node} customNode
  189. * @override
  190. */
  191. init: function (tag, color, opacity, customNode) {
  192. ccui.RichElement.prototype.init.call(this, tag, color, opacity);
  193. this._customNode = customNode;
  194. }
  195. });
  196. /**
  197. * Create a richElementCustomNode
  198. * @deprecated since v3.0, please use new ccui.RichElementCustomNode() instead.
  199. * @param {Number} tag
  200. * @param {Number} color
  201. * @param {Number} opacity
  202. * @param {cc.Node} customNode
  203. * @returns {ccui.RichElementCustomNode}
  204. */
  205. ccui.RichElementCustomNode.create = function (tag, color, opacity, customNode) {
  206. return new ccui.RichElementCustomNode(tag, color, opacity, customNode);
  207. };
  208. /**
  209. * The rich text control of Cocos UI. It receives text, image, and custom node as its children to display.
  210. * @class
  211. * @extends ccui.Widget
  212. */
  213. ccui.RichText = ccui.Widget.extend(/** @lends ccui.RichText# */{
  214. _formatTextDirty: false,
  215. _richElements: null,
  216. _elementRenders: null,
  217. _leftSpaceWidth: 0,
  218. _verticalSpace: 0,
  219. _elementRenderersContainer: null,
  220. /**
  221. * create a rich text
  222. * Constructor of ccui.RichText. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
  223. * @example
  224. * var uiRichText = new ccui.RichTex();
  225. */
  226. ctor: function () {
  227. ccui.Widget.prototype.ctor.call(this);
  228. this._formatTextDirty = false;
  229. this._richElements = [];
  230. this._elementRenders = [];
  231. this._leftSpaceWidth = 0;
  232. this._verticalSpace = 0;
  233. },
  234. _initRenderer: function () {
  235. this._elementRenderersContainer = new cc.Node();
  236. this._elementRenderersContainer.setAnchorPoint(0.5, 0.5);
  237. this.addProtectedChild(this._elementRenderersContainer, 0, -1);
  238. },
  239. /**
  240. * Insert a element
  241. * @param {ccui.RichElement} element
  242. * @param {Number} index
  243. */
  244. insertElement: function (element, index) {
  245. this._richElements.splice(index, 0, element);
  246. this._formatTextDirty = true;
  247. },
  248. /**
  249. * Push a element
  250. * @param {ccui.RichElement} element
  251. */
  252. pushBackElement: function (element) {
  253. this._richElements.push(element);
  254. this._formatTextDirty = true;
  255. },
  256. /**
  257. * Remove element
  258. * @param {ccui.RichElement} element
  259. */
  260. removeElement: function (element) {
  261. if (cc.isNumber(element))
  262. this._richElements.splice(element, 1);
  263. else
  264. cc.arrayRemoveObject(this._richElements, element);
  265. this._formatTextDirty = true;
  266. },
  267. /**
  268. * Formats the richText's content.
  269. */
  270. formatText: function () {
  271. if (this._formatTextDirty) {
  272. this._elementRenderersContainer.removeAllChildren();
  273. this._elementRenders.length = 0;
  274. var i, element, locRichElements = this._richElements;
  275. if (this._ignoreSize) {
  276. this._addNewLine();
  277. for (i = 0; i < locRichElements.length; i++) {
  278. element = locRichElements[i];
  279. var elementRenderer = null;
  280. switch (element._type) {
  281. case ccui.RichElement.TEXT:
  282. //todo: There may be ambiguous
  283. elementRenderer = new cc.LabelTTF(element._text, element._fontName, element._fontSize);
  284. break;
  285. case ccui.RichElement.IMAGE:
  286. elementRenderer = cc.Sprite.create(element._filePath);
  287. break;
  288. case ccui.RichElement.CUSTOM:
  289. elementRenderer = element._customNode;
  290. break;
  291. default:
  292. break;
  293. }
  294. elementRenderer.setColor(element._color);
  295. elementRenderer.setOpacity(element._color.a);
  296. this._pushToContainer(elementRenderer);
  297. }
  298. } else {
  299. this._addNewLine();
  300. for (i = 0; i < locRichElements.length; i++) {
  301. element = locRichElements[i];
  302. switch (element._type) {
  303. case ccui.RichElement.TEXT:
  304. this._handleTextRenderer(element._text, element._fontName, element._fontSize, element._color);
  305. break;
  306. case ccui.RichElement.IMAGE:
  307. this._handleImageRenderer(element._filePath, element._color, element._color.a);
  308. break;
  309. case ccui.RichElement.CUSTOM:
  310. this._handleCustomRenderer(element._customNode);
  311. break;
  312. default:
  313. break;
  314. }
  315. }
  316. }
  317. this.formatRenderers();
  318. this._formatTextDirty = false;
  319. }
  320. },
  321. _handleTextRenderer: function (text, fontName, fontSize, color) {
  322. var textRenderer = new cc.LabelTTF(text, fontName, fontSize);
  323. var textRendererWidth = textRenderer.getContentSize().width;
  324. this._leftSpaceWidth -= textRendererWidth;
  325. if (this._leftSpaceWidth < 0) {
  326. var overstepPercent = (-this._leftSpaceWidth) / textRendererWidth;
  327. var curText = text;
  328. var stringLength = curText.length;
  329. var leftLength = stringLength * (1 - overstepPercent);
  330. var leftWords = curText.substr(0, leftLength);
  331. var cutWords = curText.substr(leftLength, curText.length - 1);
  332. if (leftLength > 0) {
  333. var leftRenderer = new cc.LabelTTF(leftWords.substr(0, leftLength), fontName, fontSize);
  334. leftRenderer.setColor(color);
  335. leftRenderer.setOpacity(color.a);
  336. this._pushToContainer(leftRenderer);
  337. }
  338. this._addNewLine();
  339. this._handleTextRenderer(cutWords, fontName, fontSize, color);
  340. } else {
  341. textRenderer.setColor(color);
  342. textRenderer.setOpacity(color.a);
  343. this._pushToContainer(textRenderer);
  344. }
  345. },
  346. _handleImageRenderer: function (filePath, color, opacity) {
  347. var imageRenderer = cc.Sprite.create(filePath);
  348. this._handleCustomRenderer(imageRenderer);
  349. },
  350. _handleCustomRenderer: function (renderer) {
  351. var imgSize = renderer.getContentSize();
  352. this._leftSpaceWidth -= imgSize.width;
  353. if (this._leftSpaceWidth < 0) {
  354. this._addNewLine();
  355. this._pushToContainer(renderer);
  356. this._leftSpaceWidth -= imgSize.width;
  357. } else
  358. this._pushToContainer(renderer);
  359. },
  360. _addNewLine: function () {
  361. this._leftSpaceWidth = this._customSize.width;
  362. this._elementRenders.push([]);
  363. },
  364. /**
  365. * Formats richText's renderer.
  366. */
  367. formatRenderers: function () {
  368. var newContentSizeHeight = 0, locRenderersContainer = this._elementRenderersContainer;
  369. var locElementRenders = this._elementRenders;
  370. var i, j, row, nextPosX, l;
  371. if (this._ignoreSize) {
  372. var newContentSizeWidth = 0;
  373. row = locElementRenders[0];
  374. nextPosX = 0;
  375. for (j = 0; j < row.length; j++) {
  376. l = row[j];
  377. l.setAnchorPoint(cc.p(0, 0));
  378. l.setPosition(cc.p(nextPosX, 0));
  379. locRenderersContainer.addChild(l, 1, j);
  380. var iSize = l.getContentSize();
  381. newContentSizeWidth += iSize.width;
  382. newContentSizeHeight = Math.max(newContentSizeHeight, iSize.height);
  383. nextPosX += iSize.width;
  384. }
  385. locRenderersContainer.setContentSize(newContentSizeWidth, newContentSizeHeight);
  386. } else {
  387. var maxHeights = [];
  388. for (i = 0; i < locElementRenders.length; i++) {
  389. row = locElementRenders[i];
  390. var maxHeight = 0;
  391. for (j = 0; j < row.length; j++) {
  392. l = row[j];
  393. maxHeight = Math.max(l.getContentSize().height, maxHeight);
  394. }
  395. maxHeights[i] = maxHeight;
  396. newContentSizeHeight += maxHeights[i];
  397. }
  398. var nextPosY = this._customSize.height;
  399. for (i = 0; i < locElementRenders.length; i++) {
  400. row = locElementRenders[i];
  401. nextPosX = 0;
  402. nextPosY -= (maxHeights[i] + this._verticalSpace);
  403. for (j = 0; j < row.length; j++) {
  404. l = row[j];
  405. l.setAnchorPoint(cc.p(0, 0));
  406. l.setPosition(cc.p(nextPosX, nextPosY));
  407. locRenderersContainer.addChild(l, 1);
  408. nextPosX += l.getContentSize().width;
  409. }
  410. }
  411. locRenderersContainer.setContentSize(this._contentSize);
  412. }
  413. var length = locElementRenders.length;
  414. for (i = 0; i<length; i++){
  415. locElementRenders[i].length = 0;
  416. }
  417. this._elementRenders.length = 0;
  418. this.setContentSize(this._ignoreSize?this.getVirtualRendererSize():this._customSize);
  419. this._updateContentSizeWithTextureSize(this._contentSize);
  420. locRenderersContainer.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5);
  421. },
  422. _pushToContainer: function (renderer) {
  423. if (this._elementRenders.length <= 0)
  424. return;
  425. this._elementRenders[this._elementRenders.length - 1].push(renderer);
  426. },
  427. /**
  428. * Calls formatText before calls parent class' visit.
  429. * @override
  430. * @param ctx
  431. */
  432. visit: function (ctx) {
  433. if (this._enabled) {
  434. this.formatText();
  435. ccui.Widget.prototype.visit.call(this, ctx);
  436. }
  437. },
  438. /**
  439. * Sets vertical space
  440. * @param {Number} space
  441. */
  442. setVerticalSpace: function (space) {
  443. this._verticalSpace = space;
  444. },
  445. /**
  446. * Sets anchor point
  447. * @override
  448. * @param {cc.Point} pt
  449. */
  450. setAnchorPoint: function (pt) {
  451. ccui.Widget.prototype.setAnchorPoint.call(this, pt);
  452. this._elementRenderersContainer.setAnchorPoint(pt);
  453. },
  454. _setAnchorX: function (x) {
  455. ccui.Widget.prototype._setAnchorX.call(this, x);
  456. this._elementRenderersContainer._setAnchorX(x);
  457. },
  458. _setAnchorY: function (y) {
  459. ccui.Widget.prototype._setAnchorY.call(this, y);
  460. this._elementRenderersContainer._setAnchorY(y);
  461. },
  462. /**
  463. * Returns the renderer container's content size.
  464. * @override
  465. * @returns {cc.Size}
  466. */
  467. getVirtualRendererSize: function(){
  468. return this._elementRenderersContainer.getContentSize();
  469. },
  470. /**
  471. * Ignore the richText's custom size, If ignore is true that richText will ignore it's custom size, use renderer's content size, false otherwise.
  472. * @param {Boolean} ignore
  473. * @override
  474. */
  475. ignoreContentAdaptWithSize: function (ignore) {
  476. if (this._ignoreSize != ignore) {
  477. this._formatTextDirty = true;
  478. ccui.Widget.prototype.ignoreContentAdaptWithSize.call(this, ignore);
  479. }
  480. },
  481. /**
  482. * Gets the content size of ccui.RichText
  483. * @override
  484. * @return {cc.Size}
  485. */
  486. getContentSize: function(){
  487. this.formatText();
  488. return cc.Node.prototype.getContentSize.call(this);
  489. },
  490. _getWidth: function() {
  491. this.formatText();
  492. return cc.Node.prototype._getWidth.call(this);
  493. },
  494. _getHeight: function() {
  495. this.formatText();
  496. return cc.Node.prototype._getHeight.call(this);
  497. },
  498. /**
  499. * Returns the class name of ccui.RichText.
  500. * @returns {string}
  501. */
  502. getDescription: function(){
  503. return "RichText";
  504. }
  505. });
  506. /**
  507. * create a rich text
  508. * @deprecated since v3.0, please use new ccui.RichText() instead.
  509. * @returns {RichText}
  510. * @example
  511. * var uiRichText = ccui.RichTex.create();
  512. */
  513. ccui.RichText.create = function(){
  514. return new ccui.RichText();
  515. };
  516. // Constants
  517. //Rich element type
  518. /**
  519. * The text type of rich element.
  520. * @constant
  521. * @type {number}
  522. */
  523. ccui.RichElement.TEXT = 0;
  524. /**
  525. * The image type of rich element.
  526. * @constant
  527. * @type {number}
  528. */
  529. ccui.RichElement.IMAGE = 1;
  530. /**
  531. * The custom type of rich element.
  532. * @constant
  533. * @type {number}
  534. */
  535. ccui.RichElement.CUSTOM = 2;