Text.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  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. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "Font.h"
  25. #include "Log.h"
  26. #include "Profiler.h"
  27. #include "ResourceCache.h"
  28. #include "Text.h"
  29. #include "Texture.h"
  30. #include "DebugNew.h"
  31. Text::Text(const std::string& text, const std::string& name) :
  32. UIElement(name),
  33. mFontSize(DEFAULT_FONT_SIZE),
  34. mMaxWidth(0),
  35. mText(text),
  36. mTextAlignment(HA_LEFT),
  37. mTextSpacing(1.0f)
  38. {
  39. }
  40. Text::~Text()
  41. {
  42. }
  43. XMLElement Text::loadParameters(XMLFile* file, const std::string& elementName, ResourceCache* cache)
  44. {
  45. XMLElement paramElem = UIElement::loadParameters(file, elementName, cache);
  46. if (paramElem.hasChildElement("font"))
  47. {
  48. XMLElement fontElem = paramElem.getChildElement("font");
  49. setFont(cache->getResource<Font>(fontElem.getString("name")), fontElem.getInt("size"));
  50. }
  51. return paramElem;
  52. }
  53. bool Text::setFont(Font* font, int size)
  54. {
  55. if (!font)
  56. {
  57. LOGERROR("Null font for Text");
  58. return false;
  59. }
  60. mFont = font;
  61. mFontSize = max(size, 1);
  62. // Catch exception if executed from script
  63. try
  64. {
  65. calculateTextSize();
  66. }
  67. catch (Exception& e)
  68. {
  69. mFont = 0;
  70. SAFE_RETHROW_RET(e, false);
  71. }
  72. return true;
  73. }
  74. void Text::setMaxWidth(int maxWidth)
  75. {
  76. mMaxWidth = max(maxWidth, 0);
  77. calculateTextSize();
  78. }
  79. void Text::setText(const std::string& text)
  80. {
  81. mText = text;
  82. calculateTextSize();
  83. }
  84. void Text::setTextAlignment(HorizontalAlignment align)
  85. {
  86. mTextAlignment = align;
  87. }
  88. void Text::setTextSpacing(float spacing)
  89. {
  90. mTextSpacing = max(spacing, 0.5f);
  91. }
  92. void Text::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
  93. {
  94. if (!mFont)
  95. return;
  96. const FontFace* face = mFont->getFace(mFontSize);
  97. UIBatch batch;
  98. batch.begin(&quads);
  99. batch.mBlendMode = BLEND_ALPHA;
  100. batch.mScissor = currentScissor;
  101. batch.mTexture = face->mTexture;
  102. unsigned rowIndex = 0;
  103. int x = getRowStartPosition(rowIndex);
  104. int y = 0;
  105. int rowHeight = (int)(mTextSpacing * face->mRowHeight);
  106. for (unsigned i = 0; i < mPrintText.length(); ++i)
  107. {
  108. unsigned char c = (unsigned char)mPrintText[i];
  109. if (c != '\n')
  110. {
  111. const FontGlyph& glyph = face->mGlyphs[face->mGlyphIndex[c]];
  112. if (c != ' ')
  113. batch.addQuad(*this, x + glyph.mOffsetX, y + glyph.mOffsetY, glyph.mWidth, glyph.mHeight, glyph.mX, glyph.mY);
  114. x += glyph.mAdvanceX;
  115. }
  116. else
  117. {
  118. rowIndex++;
  119. x = getRowStartPosition(rowIndex);
  120. y += rowHeight;
  121. }
  122. }
  123. UIBatch::addOrMerge(batch, batches);
  124. }
  125. int Text::getRowHeight() const
  126. {
  127. if (mFont)
  128. return mFont->getFace(mFontSize)->mRowHeight;
  129. else
  130. return 0;
  131. }
  132. void Text::calculateTextSize()
  133. {
  134. int width = 0;
  135. int height = 0;
  136. mRowWidths.clear();
  137. if (mFont)
  138. {
  139. const FontFace* face = mFont->getFace(mFontSize);
  140. int rowWidth = 0;
  141. int rowHeight = (int)(mTextSpacing * face->mRowHeight);
  142. // First see if the text must be split up
  143. if (!mMaxWidth)
  144. mPrintText = mText;
  145. else
  146. {
  147. unsigned nextBreak = 0;
  148. unsigned lineStart = 0;
  149. for (unsigned i = 0; i < mText.length(); ++i)
  150. {
  151. unsigned j;
  152. if (mText[i] != '\n')
  153. {
  154. bool ok = true;
  155. if (nextBreak <= i)
  156. {
  157. int futureRowWidth = rowWidth;
  158. for (j = i; j < mText.length(); ++j)
  159. {
  160. if ((mText[j] == ' ') || (mText[j] == '\n'))
  161. {
  162. nextBreak = j;
  163. break;
  164. }
  165. futureRowWidth += face->mGlyphs[face->mGlyphIndex[mText[j]]].mAdvanceX;
  166. if ((mText[j] == '-') && (futureRowWidth <= mMaxWidth))
  167. {
  168. nextBreak = j + 1;
  169. break;
  170. }
  171. if (futureRowWidth > mMaxWidth)
  172. {
  173. ok = false;
  174. break;
  175. }
  176. }
  177. }
  178. if (!ok)
  179. {
  180. // If did not find any breaks on the line, copy until j, or at least 1 char, to prevent infinite loop
  181. if (nextBreak == lineStart)
  182. {
  183. int copyLength = max(j - i, 1);
  184. mPrintText.append(mText.substr(i, copyLength));
  185. i += copyLength;
  186. }
  187. mPrintText += '\n';
  188. rowWidth = 0;
  189. nextBreak = lineStart = i;
  190. }
  191. if (i < mText.length())
  192. {
  193. // When copying a space, we may be over row width
  194. rowWidth += face->mGlyphs[face->mGlyphIndex[mText[i]]].mAdvanceX;
  195. if (rowWidth <= mMaxWidth)
  196. mPrintText += mText[i];
  197. }
  198. }
  199. else
  200. {
  201. mPrintText += '\n';
  202. rowWidth = 0;
  203. nextBreak = lineStart = i;
  204. }
  205. }
  206. }
  207. rowWidth = 0;
  208. for (unsigned i = 0; i < mPrintText.length(); ++i)
  209. {
  210. unsigned char c = (unsigned char)mPrintText[i];
  211. if (c != '\n')
  212. {
  213. const FontGlyph& glyph = face->mGlyphs[face->mGlyphIndex[c]];
  214. rowWidth += glyph.mAdvanceX;
  215. }
  216. else
  217. {
  218. width = max(width, rowWidth);
  219. height += rowHeight;
  220. mRowWidths.push_back(rowWidth);
  221. rowWidth = 0;
  222. }
  223. }
  224. if (rowWidth)
  225. {
  226. width = max(width, rowWidth);
  227. height += rowHeight;
  228. mRowWidths.push_back(rowWidth);
  229. }
  230. }
  231. setSize(width, height);
  232. }
  233. int Text::getRowStartPosition(unsigned rowIndex) const
  234. {
  235. int rowWidth = 0;
  236. if (rowIndex < mRowWidths.size())
  237. rowWidth = mRowWidths[rowIndex];
  238. switch (mTextAlignment)
  239. {
  240. default:
  241. return 0;
  242. case HA_CENTER:
  243. return (getSize().mX - rowWidth) / 2;
  244. case HA_RIGHT:
  245. return getSize().mX - rowWidth;
  246. }
  247. }