/*! @file @author Albert Semenov @date 09/2009 @module */ /* This file is part of MyGUI. MyGUI is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. MyGUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with MyGUI. If not, see . */ #include "MyGUI_Precompiled.h" #include "MyGUI_EditText.h" #include "MyGUI_RenderItem.h" #include "MyGUI_FontManager.h" #include "MyGUI_RenderManager.h" #include "MyGUI_LanguageManager.h" #include "MyGUI_TextIterator.h" #include "MyGUI_IRenderTarget.h" #include "MyGUI_FontData.h" #include "MyGUI_CommonStateInfo.h" namespace MyGUI { const size_t VERTEX_IN_QUAD = 6; const size_t SIMPLETEXT_COUNT_VERTEX = 32 * VERTEX_IN_QUAD; MYGUI_FORCEINLINE void DrawQuad( MyGUI::Vertex*& _buff, float v_left, float v_top, float v_rignt, float v_bottom, float v_z, MyGUI::uint32 _colour, float t_left, float t_top, float t_right, float t_bottom, size_t& _count) { _buff[0].x = v_left; _buff[0].y = v_top; _buff[0].z = v_z; _buff[0].colour = _colour; _buff[0].u = t_left; _buff[0].v = t_top; _buff[1].x = v_left; _buff[1].y = v_bottom; _buff[1].z = v_z; _buff[1].colour = _colour; _buff[1].u = t_left; _buff[1].v = t_bottom; _buff[2].x = v_rignt; _buff[2].y = v_top; _buff[2].z = v_z; _buff[2].colour = _colour; _buff[2].u = t_right; _buff[2].v = t_top; _buff[3].x = v_rignt; _buff[3].y = v_top; _buff[3].z = v_z; _buff[3].colour = _colour; _buff[3].u = t_right; _buff[3].v = t_top; _buff[4].x = v_left; _buff[4].y = v_bottom; _buff[4].z = v_z; _buff[4].colour = _colour; _buff[4].u = t_left; _buff[4].v = t_bottom; _buff[5].x = v_rignt; _buff[5].y = v_bottom; _buff[5].z = v_z; _buff[5].colour = _colour; _buff[5].u = t_right; _buff[5].v = t_bottom; _buff += VERTEX_IN_QUAD; _count += VERTEX_IN_QUAD; } EditText::EditText() : ISubWidgetText(), mEmptyView(false), mCurrentColour(0x00FFFFFF), mInverseColour(0x00000000), mCurrentAlpha(0xFF000000), mTextOutDate(false), mTextAlign(Align::Default), mColour(Colour::White), mAlpha(ALPHA_MAX), mFont(nullptr), mTexture(nullptr), mFontHeight(0), mBackgroundNormal(true), mStartSelect(0), mEndSelect(0), mCursorPosition(0), mVisibleCursor(false), mInvertSelect(true), mNode(nullptr), mRenderItem(nullptr), mCountVertex(SIMPLETEXT_COUNT_VERTEX), mIsAddCursorWidth(true), mShiftText(false), mWordWrap(false), mOldWidth(0) { mVertexFormat = RenderManager::getInstance().getVertexFormat(); mCurrentColour = texture_utility::toColourARGB(mColour); texture_utility::convertColour(mCurrentColour, mVertexFormat); mCurrentColour = (mCurrentColour & 0x00FFFFFF) | mCurrentAlpha; mInverseColour = mCurrentColour ^ 0x00FFFFFF; } EditText::~EditText() { } void EditText::setVisible(bool _visible) { if (mVisible == _visible) return; mVisible = _visible; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } void EditText::_correctView() { if (nullptr != mNode) mNode->outOfDate(mRenderItem); } void EditText::_setAlign(const IntCoord& _oldcoord, bool _update) { _setAlign(_oldcoord.size(), _update); } void EditText::_setAlign(const IntSize& _oldsize, bool _update) { if (mWordWrap) { // передается старая координата всегда int width = mCroppedParent->getWidth(); if (mOldWidth != width) { mOldWidth = width; mTextOutDate = true; } } // необходимо разобраться bool need_update = true;//_update; // первоначальное выравнивание if (mAlign.isHStretch()) { // растягиваем mCoord.width = mCoord.width + (mCroppedParent->getWidth() - _oldsize.width); need_update = true; mIsMargin = true; // при изменении размеров все пересчитывать } else if (mAlign.isRight()) { // двигаем по правому краю mCoord.left = mCoord.left + (mCroppedParent->getWidth() - _oldsize.width); need_update = true; } else if (mAlign.isHCenter()) { // выравнивание по горизонтали без растяжения mCoord.left = (mCroppedParent->getWidth() - mCoord.width) / 2; need_update = true; } if (mAlign.isVStretch()) { // растягиваем mCoord.height = mCoord.height + (mCroppedParent->getHeight() - _oldsize.height); need_update = true; mIsMargin = true; // при изменении размеров все пересчитывать } else if (mAlign.isBottom()) { // двигаем по нижнему краю mCoord.top = mCoord.top + (mCroppedParent->getHeight() - _oldsize.height); need_update = true; } else if (mAlign.isVCenter()) { // выравнивание по вертикали без растяжения mCoord.top = (mCroppedParent->getHeight() - mCoord.height) / 2; need_update = true; } if (need_update) { mCurrentCoord = mCoord; _updateView(); } } void EditText::_updateView() { bool margin = _checkMargin(); mEmptyView = ((0 >= _getViewWidth()) || (0 >= _getViewHeight())); mCurrentCoord.left = mCoord.left + mMargin.left; mCurrentCoord.top = mCoord.top + mMargin.top; // вьюпорт стал битым if (margin) { // проверка на полный выход за границу if (_checkOutside()) { // запоминаем текущее состояние mIsMargin = margin; // обновить перед выходом if (nullptr != mNode) mNode->outOfDate(mRenderItem); return; } } // мы обрезаны или были обрезаны if ((mIsMargin) || (margin)) { mCurrentCoord.width = _getViewWidth(); mCurrentCoord.height = _getViewHeight(); } // запоминаем текущее состояние mIsMargin = margin; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } void EditText::setCaption(const UString& _value) { mCaption = _value; mTextOutDate = true; // если вершин не хватит, делаем реалок, с учетом выделения * 2 и курсора size_t need = (mCaption.size() * 2 + 2) * VERTEX_IN_QUAD; if (mCountVertex < need) { mCountVertex = need + SIMPLETEXT_COUNT_VERTEX; if (nullptr != mRenderItem) mRenderItem->reallockDrawItem(this, mCountVertex); } if (nullptr != mNode) mNode->outOfDate(mRenderItem); } const UString& EditText::getCaption() { return mCaption; } void EditText::setTextColour(const Colour& _value) { if (mColour == _value) return; mColour = _value; mCurrentColour = texture_utility::toColourARGB(mColour); texture_utility::convertColour(mCurrentColour, mVertexFormat); mCurrentColour = (mCurrentColour & 0x00FFFFFF) | mCurrentAlpha; mInverseColour = mCurrentColour ^ 0x00FFFFFF; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } const Colour& EditText::getTextColour() { return mColour; } void EditText::setAlpha(float _value) { if (mAlpha == _value) return; mAlpha = _value; mCurrentAlpha = ((uint8)(mAlpha*255) << 24); mCurrentColour = (mCurrentColour & 0x00FFFFFF) | mCurrentAlpha; mInverseColour = mCurrentColour ^ 0x00FFFFFF; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } float EditText::getAlpha() { return mAlpha; } void EditText::setFontName(const std::string& _value) { mTexture = 0; mFont = FontManager::getInstance().getByName(_value); if (mFont != nullptr) { mTexture = mFont->getTextureFont(); // если надо, устанавливаем дефолтный размер шрифта if (mFont->getDefaultHeight() != 0) { mFontHeight = mFont->getDefaultHeight(); } } mTextOutDate = true; // если мы были приаттаченны, то удаляем себя if (nullptr != mRenderItem) { mRenderItem->removeDrawItem(this); mRenderItem = nullptr; } // если есть текстура, то приаттачиваемся if (nullptr != mTexture && nullptr != mNode) { mRenderItem = mNode->addToRenderItem(mTexture, this); mRenderItem->addDrawItem(this, mCountVertex); } if (nullptr != mNode) mNode->outOfDate(mRenderItem); } const std::string& EditText::getFontName() { return mFont->getResourceName(); } void EditText::setFontHeight(int _value) { mFontHeight = _value; mTextOutDate = true; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } int EditText::getFontHeight() { return mFontHeight; } void EditText::createDrawItem(ITexture* _texture, ILayerNode * _node) { mNode = _node; // если уже есть текстура, то атачимся, актуально для смены леера if (nullptr != mTexture) { MYGUI_ASSERT(!mRenderItem, "mRenderItem must be nullptr"); mRenderItem = mNode->addToRenderItem(mTexture, this); mRenderItem->addDrawItem(this, mCountVertex); } } void EditText::destroyDrawItem() { if (nullptr != mRenderItem) { mRenderItem->removeDrawItem(this); mRenderItem = nullptr; } mNode = nullptr; } size_t EditText::getTextSelectionStart() { return mStartSelect; } size_t EditText::getTextSelectionEnd() { return mEndSelect; } void EditText::setTextSelection(size_t _start, size_t _end) { mStartSelect=_start; mEndSelect=_end; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } bool EditText::getSelectBackground() { return mBackgroundNormal; } void EditText::setSelectBackground(bool _normal) { if (mBackgroundNormal == _normal) return; mBackgroundNormal = _normal; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } bool EditText::isVisibleCursor() { return mVisibleCursor; } void EditText::setVisibleCursor(bool _value) { if (mVisibleCursor == _value) return; mVisibleCursor = _value; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } size_t EditText::getCursorPosition() { return mCursorPosition; } void EditText::setCursorPosition(size_t _index) { if (mCursorPosition == _index) return; mCursorPosition = _index; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } void EditText::setTextAlign(Align _value) { mTextAlign = _value; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } Align EditText::getTextAlign() { return mTextAlign; } IntSize EditText::getTextSize() { // если нуно обновить, или изменились пропорции экрана if (mTextOutDate) updateRawData(); IntSize size = mTextView.getViewSize(); // плюс размер курсора if (mIsAddCursorWidth) size.width += 2; return size; } void EditText::setViewOffset(const IntPoint& _point) { mViewOffset = _point; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } IntPoint EditText::getViewOffset() { return mViewOffset; } size_t EditText::getCursorPosition(const IntPoint& _point) { if (nullptr == mFont) return 0; if (mTextOutDate) updateRawData(); IntPoint point = _point; point -= mCroppedParent->getAbsolutePosition(); point += mViewOffset; point -= mCoord.point(); return mTextView.getCursorPosition(point); } IntCoord EditText::getCursorCoord(size_t _position) { if (nullptr == mFont) return IntCoord(); if (mTextOutDate) updateRawData(); IntPoint point = mTextView.getCursorPoint(_position); point += mCroppedParent->getAbsolutePosition(); point -= mViewOffset; point += mCoord.point(); return IntCoord(point.left, point.top, 2, mFontHeight); } void EditText::setShiftText(bool _value) { if (mShiftText == _value) return; mShiftText = _value; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } void EditText::setWordWrap(bool _value) { mWordWrap = _value; mTextOutDate = true; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } void EditText::updateRawData() { if (nullptr == mFont) return; // сбрасывам флаги mTextOutDate = false; int width = -1; if (mWordWrap) { width = mCoord.width; // обрезать слова нужно по шарине, которую мы реально используем if (mIsAddCursorWidth) width -= 2; } mTextView.update(mCaption, mFont, mFontHeight, mTextAlign, mVertexFormat, width); } void EditText::setStateData(IStateInfo* _data) { EditTextStateInfo* data = _data->castType(); if (data->getColour() != Colour::Zero) setTextColour(data->getColour()); setShiftText(data->getShift()); } void EditText::doRender() { if (nullptr == mFont) return; if (!mVisible || mEmptyView) return; bool _update = mRenderItem->getCurrentUpdate(); if (_update) mTextOutDate = true; if (mTextOutDate) updateRawData(); Vertex* _vertex = mRenderItem->getCurrentVertextBuffer(); const RenderTargetInfo& info = mRenderItem->getRenderTarget()->getInfo(); // колличество отрисованных вершин size_t vertex_count = 0; // текущие цвета uint32 colour_current = mCurrentColour; uint32 colour = mCurrentColour; uint32 colour_inverse = mInverseColour; GlyphInfo* back_glyph = mFont->getGlyphInfo(mBackgroundNormal ? FontCodeType::Selected : FontCodeType::SelectedBack); float vertex_z = info.maximumDepth; const VectorLineInfo& data = mTextView.getData(); int left = - mViewOffset.left + mCoord.left; int top = - mViewOffset.top + mCoord.top; int width = 0; const int height = mFontHeight; size_t index = 0; for (VectorLineInfo::const_iterator line=data.begin(); line!=data.end(); ++line) { left = line->offset - mViewOffset.left + mCoord.left; for (VectorCharInfo::const_iterator sim=line->simbols.begin(); sim!=line->simbols.end(); ++sim) { if (sim->isColour()) { colour = sim->getColour() | (colour & 0xFF000000); colour_inverse = colour ^ 0x00FFFFFF; continue; } // смещение текстуры для фона bool select = !((index >= mEndSelect) || (index < mStartSelect)); uint32 back_colour = 0; // выделение символа if (!select || !mInvertSelect) { colour_current = colour; back_colour = colour | 0x00FFFFFF; } else { colour_current = colour_inverse; back_colour = colour_inverse; } bool draw = true; // текущие размеры MyGUI::FloatRect texture_rect = sim->getUVRect(); width = sim->getWidth(); // конечные размеры int result_left = left; int result_top = top; int result_right = left + width; int result_bottom = top + height; float texture_width = texture_rect.right - texture_rect.left; float texture_height = texture_rect.bottom - texture_rect.top; // символ залазиет влево if (left < mCurrentCoord.left) { // символ вообще не виден if (left + width <= mCurrentCoord.left) { draw = false; } // символ обрезан else { result_left = mCurrentCoord.left; texture_rect.left += (texture_width * (float)(result_left - left) / (float)width); } } // символ залазиет вправо if (left + width > mCurrentCoord.right()) { // символ вообще не виден if (left >= mCurrentCoord.right()) { draw = false; } // символ обрезан else { result_right = mCurrentCoord.right(); texture_rect.right -= (texture_width * (float)((left + width) - result_right) / (float)width); } } // символ залазиет вверх if (top < mCurrentCoord.top) { // символ вообще не виден if (top + height <= mCurrentCoord.top) { draw = false; } // символ обрезан else { result_top = mCurrentCoord.top; texture_rect.top += (texture_height * (float)(result_top - top) / (float)height); } } // символ залазиет вниз if (top + height > mCurrentCoord.bottom()) { // символ вообще не виден if (top >= mCurrentCoord.bottom()) { draw = false; } // символ обрезан else { result_bottom = mCurrentCoord.bottom(); texture_rect.bottom -= (texture_height * (float)((top + height) - result_bottom) / (float)height); } } if (draw) { int pix_left = mCroppedParent->getAbsoluteLeft() - info.leftOffset + result_left; int pix_top = mCroppedParent->getAbsoluteTop() - info.topOffset + (mShiftText ? 1 : 0) + result_top; float real_left = ((info.pixScaleX * (float)(pix_left) + info.hOffset) * 2) - 1; float real_top = - (((info.pixScaleY * (float)(pix_top) + info.vOffset) * 2) - 1); float real_right = ((info.pixScaleX * (float)(pix_left + result_right - result_left) + info.hOffset) * 2) - 1; float real_bottom = - (((info.pixScaleY * (float)(pix_top + result_bottom - result_top) + info.vOffset) * 2) - 1); // если нужно рисуем выделение if (select) { const FloatRect& background_current = back_glyph->uvRect; DrawQuad(_vertex, real_left, real_top, real_right, real_bottom, vertex_z, back_colour, background_current.left, background_current.top, background_current.left, background_current.top, vertex_count); } DrawQuad(_vertex, real_left, real_top, real_right, real_bottom, vertex_z, colour_current, texture_rect.left, texture_rect.top, texture_rect.right, texture_rect.bottom, vertex_count); } left += width; index++; } top += height; index++; } if (mVisibleCursor) { MyGUI::IntPoint point = mTextView.getCursorPoint(mCursorPosition); point -= mViewOffset; point += mCoord.point(); bool draw = true; GlyphInfo* cursor_glyph = mFont->getGlyphInfo(FontCodeType::Cursor); MyGUI::FloatRect texture_rect = cursor_glyph->uvRect; left = point.left; top = point.top; width = 2;//cursor_glyph->width; // конечные размеры int result_left = left; int result_top = top; int result_width = width; int result_height = height; // символ залазиет влево if (left < mCurrentCoord.left) { // символ вообще не виден if (left + width <= mCurrentCoord.left) { draw = false; } // символ обрезан else { result_left = mCurrentCoord.left; result_width = width - (mCurrentCoord.left - left); float texture_width = texture_rect.right - texture_rect.left; texture_rect.left = texture_rect.right - (texture_width * (float)result_width / (float)width); } } // символ залазиет вправо if (left + width > mCurrentCoord.right()) { // символ вообще не виден if (left >= mCurrentCoord.right()) { draw = false; } // символ обрезан else { result_width = mCurrentCoord.right() - left; float texture_width = texture_rect.right - texture_rect.left; texture_rect.right = texture_rect.left + (texture_width * (float)result_width / (float)width); } } // символ залазиет вверх if (top < mCurrentCoord.top) { // символ вообще не виден if (top + height <= mCurrentCoord.top) { draw = false; } // символ обрезан else { result_top = mCurrentCoord.top; result_height = height - (mCurrentCoord.top - top); float texture_height = texture_rect.bottom - texture_rect.top; texture_rect.top = texture_rect.bottom - (texture_height * (float)result_height / (float)height); } } // символ залазиет вниз if (top + height > mCurrentCoord.bottom()) { // символ вообще не виден if (top >= mCurrentCoord.bottom()) { draw = false; } // символ обрезан else { result_height = mCurrentCoord.bottom() - top; float texture_height = texture_rect.bottom - texture_rect.top; texture_rect.bottom = texture_rect.top + (texture_height * (float)result_height / (float)height); } } if (draw) { int pix_left = mCroppedParent->getAbsoluteLeft() - info.leftOffset + result_left; int pix_top = mCroppedParent->getAbsoluteTop() - info.topOffset + (mShiftText ? 1 : 0) + result_top; float real_left = ((info.pixScaleX * (float)(pix_left) + info.hOffset) * 2) - 1; float real_top = - (((info.pixScaleY * (float)(pix_top) + info.vOffset) * 2) - 1); float real_right = ((info.pixScaleX * (float)(pix_left + result_width) + info.hOffset) * 2) - 1; float real_bottom = - (((info.pixScaleY * (float)(pix_top + result_height) + info.vOffset) * 2) - 1); DrawQuad(_vertex, real_left, real_top, real_right, real_bottom, vertex_z, colour_current | 0x00FFFFFF, texture_rect.left, texture_rect.top, texture_rect.right, texture_rect.bottom, vertex_count); } } // колличество реально отрисованных вершин mRenderItem->setLastVertexCount(vertex_count); } void EditText::setInvertSelected(bool _value) { if (mInvertSelect == _value) return; mInvertSelect = _value; if (nullptr != mNode) mNode->outOfDate(mRenderItem); } } // namespace MyGUI