MyGUI
3.2.1
|
00001 /* 00002 * This source file is part of MyGUI. For the latest info, see http://mygui.info/ 00003 * Distributed under the MIT License 00004 * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) 00005 */ 00006 00007 #include "MyGUI_Precompiled.h" 00008 #include "MyGUI_TextView.h" 00009 00010 namespace MyGUI 00011 { 00012 00013 namespace 00014 { 00015 00016 template<typename T> 00017 void setMin(T& _var, const T& _newValue) 00018 { 00019 if (_newValue < _var) 00020 _var = _newValue; 00021 } 00022 00023 template<typename T> 00024 void setMax(T& _var, const T& _newValue) 00025 { 00026 if (_var < _newValue) 00027 _var = _newValue; 00028 } 00029 00030 } 00031 00032 class RollBackPoint 00033 { 00034 public: 00035 RollBackPoint() : 00036 position(0), 00037 count(0), 00038 width(0), 00039 rollback(false) 00040 { 00041 } 00042 00043 void set(size_t _position, UString::const_iterator& _space_point, size_t _count, float _width) 00044 { 00045 position = _position; 00046 space_point = _space_point; 00047 count = _count; 00048 width = _width; 00049 rollback = true; 00050 } 00051 00052 void clear() 00053 { 00054 rollback = false; 00055 } 00056 00057 bool empty() const 00058 { 00059 return !rollback; 00060 } 00061 00062 float getWidth() const 00063 { 00064 MYGUI_DEBUG_ASSERT(rollback, "rollback point not valid"); 00065 return width; 00066 } 00067 00068 size_t getCount() const 00069 { 00070 MYGUI_DEBUG_ASSERT(rollback, "rollback point not valid"); 00071 return count; 00072 } 00073 00074 size_t getPosition() const 00075 { 00076 MYGUI_DEBUG_ASSERT(rollback, "rollback point not valid"); 00077 return position; 00078 } 00079 00080 UString::const_iterator getTextIter() const 00081 { 00082 MYGUI_DEBUG_ASSERT(rollback, "rollback point not valid"); 00083 return space_point; 00084 } 00085 00086 private: 00087 size_t position; 00088 UString::const_iterator space_point; 00089 size_t count; 00090 float width; 00091 bool rollback; 00092 }; 00093 00094 TextView::TextView() : 00095 mLength(0), 00096 mFontHeight(0) 00097 { 00098 } 00099 00100 void TextView::update(const UString& _text, IFont* _font, int _height, Align _align, VertexColourType _format, int _maxWidth) 00101 { 00102 mFontHeight = _height; 00103 00104 // массив для быстрой конвертации цветов 00105 static const char convert_colour[64] = 00106 { 00107 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 00108 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 00109 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 00110 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0 00111 }; 00112 00113 mViewSize.clear(); 00114 00115 RollBackPoint roll_back; 00116 IntSize result; 00117 float width = 0.0f; 00118 size_t count = 0; 00119 mLength = 0; 00120 mLineInfo.clear(); 00121 LineInfo line_info; 00122 int font_height = _font->getDefaultHeight(); 00123 00124 UString::const_iterator end = _text.end(); 00125 UString::const_iterator index = _text.begin(); 00126 00127 /*if (index == end) 00128 return;*/ 00129 00130 result.height += _height; 00131 00132 for (; index != end; ++index) 00133 { 00134 Char character = *index; 00135 00136 // новая строка 00137 if (character == FontCodeType::CR 00138 || character == FontCodeType::NEL 00139 || character == FontCodeType::LF) 00140 { 00141 if (character == FontCodeType::CR) 00142 { 00143 UString::const_iterator peeki = index; 00144 ++peeki; 00145 if ((peeki != end) && (*peeki == FontCodeType::LF)) 00146 index = peeki; // skip both as one newline 00147 } 00148 00149 line_info.width = (int)ceil(width); 00150 line_info.count = count; 00151 mLength += line_info.count + 1; 00152 00153 result.height += _height; 00154 setMax(result.width, line_info.width); 00155 width = 0; 00156 count = 0; 00157 00158 mLineInfo.push_back(line_info); 00159 line_info.clear(); 00160 00161 // отменяем откат 00162 roll_back.clear(); 00163 00164 continue; 00165 } 00166 // тег 00167 else if (character == L'#') 00168 { 00169 // берем следующий символ 00170 ++ index; 00171 if (index == end) 00172 { 00173 --index; // это защита 00174 continue; 00175 } 00176 00177 character = *index; 00178 // если два подряд, то рисуем один шарп, если нет то меняем цвет 00179 if (character != L'#') 00180 { 00181 // парсим первый символ 00182 uint32 colour = convert_colour[(character - 48) & 0x3F]; 00183 00184 // и еще пять символов после шарпа 00185 for (char i = 0; i < 5; i++) 00186 { 00187 ++ index; 00188 if (index == end) 00189 { 00190 --index; // это защита 00191 continue; 00192 } 00193 colour <<= 4; 00194 colour += convert_colour[ ((*index) - 48) & 0x3F ]; 00195 } 00196 00197 // если нужно, то меняем красный и синий компоненты 00198 texture_utility::convertColour(colour, _format); 00199 00200 line_info.simbols.push_back( CharInfo(colour) ); 00201 00202 continue; 00203 } 00204 } 00205 00206 GlyphInfo* info = _font->getGlyphInfo(character); 00207 00208 if (info == nullptr) 00209 continue; 00210 00211 if (FontCodeType::Space == character) 00212 { 00213 roll_back.set(line_info.simbols.size(), index, count, width); 00214 } 00215 else if (FontCodeType::Tab == character) 00216 { 00217 roll_back.set(line_info.simbols.size(), index, count, width); 00218 } 00219 00220 float char_width = info->width; 00221 float char_height = info->height; 00222 float char_advance = info->advance; 00223 float char_bearingX = info->bearingX; 00224 float char_bearingY = info->bearingY; 00225 00226 if (_height != font_height) 00227 { 00228 float scale = (float)_height / font_height; 00229 00230 char_width *= scale; 00231 char_height *= scale; 00232 char_advance *= scale; 00233 char_bearingX *= scale; 00234 char_bearingY *= scale; 00235 } 00236 00237 float char_fullAdvance = char_bearingX + char_advance; 00238 00239 // перенос слов 00240 if (_maxWidth != -1 00241 && (width + char_fullAdvance) > _maxWidth 00242 && !roll_back.empty()) 00243 { 00244 // откатываем до последнего пробела 00245 width = roll_back.getWidth(); 00246 count = roll_back.getCount(); 00247 index = roll_back.getTextIter(); 00248 line_info.simbols.erase(line_info.simbols.begin() + roll_back.getPosition(), line_info.simbols.end()); 00249 00250 // запоминаем место отката, как полную строку 00251 line_info.width = (int)ceil(width); 00252 line_info.count = count; 00253 mLength += line_info.count + 1; 00254 00255 result.height += _height; 00256 setMax(result.width, line_info.width); 00257 width = 0; 00258 count = 0; 00259 00260 mLineInfo.push_back(line_info); 00261 line_info.clear(); 00262 00263 // отменяем откат 00264 roll_back.clear(); 00265 00266 continue; 00267 } 00268 00269 line_info.simbols.push_back(CharInfo(info->uvRect, char_width, char_height, char_advance, char_bearingX, char_bearingY)); 00270 width += char_fullAdvance; 00271 count ++; 00272 } 00273 00274 line_info.width = (int)ceil(width); 00275 line_info.count = count; 00276 mLength += line_info.count; 00277 00278 mLineInfo.push_back(line_info); 00279 00280 setMax(result.width, line_info.width); 00281 00282 // теперь выравниванием строки 00283 for (VectorLineInfo::iterator line = mLineInfo.begin(); line != mLineInfo.end(); ++line) 00284 { 00285 if (_align.isRight()) 00286 line->offset = result.width - line->width; 00287 else if (_align.isHCenter()) 00288 line->offset = (result.width - line->width) / 2; 00289 } 00290 00291 mViewSize = result; 00292 } 00293 00294 size_t TextView::getCursorPosition(const IntPoint& _value) 00295 { 00296 const int height = mFontHeight; 00297 size_t result = 0; 00298 int top = 0; 00299 00300 for (VectorLineInfo::const_iterator line = mLineInfo.begin(); line != mLineInfo.end(); ++line) 00301 { 00302 // это последняя строка 00303 bool lastline = !(line + 1 != mLineInfo.end()); 00304 00305 // наша строчка 00306 if (top + height > _value.top || lastline) 00307 { 00308 top += height; 00309 float left = (float)line->offset; 00310 int count = 0; 00311 00312 // ищем символ 00313 for (VectorCharInfo::const_iterator sim = line->simbols.begin(); sim != line->simbols.end(); ++sim) 00314 { 00315 if (sim->isColour()) 00316 continue; 00317 00318 float fullAdvance = sim->getAdvance() + sim->getBearingX(); 00319 if (left + fullAdvance / 2.0f > _value.left) 00320 { 00321 break; 00322 } 00323 left += fullAdvance; 00324 count ++; 00325 } 00326 00327 result += count; 00328 break; 00329 } 00330 00331 if (!lastline) 00332 { 00333 top += height; 00334 result += line->count + 1; 00335 } 00336 } 00337 00338 return result; 00339 } 00340 00341 IntPoint TextView::getCursorPoint(size_t _position) 00342 { 00343 setMin(_position, mLength); 00344 00345 size_t position = 0; 00346 int top = 0; 00347 float left = 0.0f; 00348 for (VectorLineInfo::const_iterator line = mLineInfo.begin(); line != mLineInfo.end(); ++line) 00349 { 00350 left = (float)line->offset; 00351 if (position + line->count >= _position) 00352 { 00353 for (VectorCharInfo::const_iterator sim = line->simbols.begin(); sim != line->simbols.end(); ++sim) 00354 { 00355 if (sim->isColour()) 00356 continue; 00357 00358 if (position == _position) 00359 break; 00360 00361 position ++; 00362 left += sim->getBearingX() + sim->getAdvance(); 00363 } 00364 break; 00365 } 00366 position += line->count + 1; 00367 top += mFontHeight; 00368 } 00369 00370 return IntPoint((int)left, top); 00371 } 00372 00373 const IntSize& TextView::getViewSize() const 00374 { 00375 return mViewSize; 00376 } 00377 00378 size_t TextView::getTextLength() const 00379 { 00380 return mLength; 00381 } 00382 00383 const VectorLineInfo& TextView::getData() const 00384 { 00385 return mLineInfo; 00386 } 00387 00388 } // namespace MyGUI