diff options
Diffstat (limited to 'indra/llui')
-rw-r--r-- | indra/llui/llbadge.cpp | 12 | ||||
-rw-r--r-- | indra/llui/llfloater.cpp | 24 | ||||
-rw-r--r-- | indra/llui/llfolderview.cpp | 2 | ||||
-rw-r--r-- | indra/llui/llfolderviewitem.cpp | 138 | ||||
-rw-r--r-- | indra/llui/llfolderviewitem.h | 17 | ||||
-rw-r--r-- | indra/llui/llfolderviewmodel.h | 1 | ||||
-rw-r--r-- | indra/llui/lllineeditor.cpp | 60 | ||||
-rw-r--r-- | indra/llui/lllineeditor.h | 5 | ||||
-rw-r--r-- | indra/llui/llstatbar.cpp | 10 | ||||
-rw-r--r-- | indra/llui/lltextbase.cpp | 269 | ||||
-rw-r--r-- | indra/llui/lltextbase.h | 27 | ||||
-rw-r--r-- | indra/llui/lltexteditor.h | 2 | ||||
-rw-r--r-- | indra/llui/lltrans.cpp | 20 | ||||
-rw-r--r-- | indra/llui/lltrans.h | 18 | ||||
-rw-r--r-- | indra/llui/llurlentry.cpp | 36 | ||||
-rw-r--r-- | indra/llui/llurlentry.h | 4 | ||||
-rw-r--r-- | indra/llui/llview.cpp | 6 | ||||
-rw-r--r-- | indra/llui/llviewereventrecorder.cpp | 2 | ||||
-rw-r--r-- | indra/llui/llviewmodel.cpp | 10 | ||||
-rw-r--r-- | indra/llui/llviewmodel.h | 4 |
20 files changed, 474 insertions, 193 deletions
diff --git a/indra/llui/llbadge.cpp b/indra/llui/llbadge.cpp index c6654ee0aa..42b6f1f07b 100644 --- a/indra/llui/llbadge.cpp +++ b/indra/llui/llbadge.cpp @@ -204,13 +204,13 @@ void renderBadgeBackground(F32 centerX, F32 centerY, F32 width, F32 height, cons (F32)ll_round(x) + width, (F32)ll_round(y) + height); - LLVector3 vertices[4]; - vertices[0] = LLVector3(screen_rect.mRight, screen_rect.mTop, 1.0f); - vertices[1] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 1.0f); - vertices[2] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 1.0f); - vertices[3] = LLVector3(screen_rect.mRight, screen_rect.mBottom, 1.0f); + LLVector4a vertices[4]; + vertices[0].set(screen_rect.mLeft, screen_rect.mTop, 1.0f); + vertices[1].set(screen_rect.mRight, screen_rect.mTop, 1.0f); + vertices[2].set(screen_rect.mLeft, screen_rect.mBottom, 1.0f); + vertices[3].set(screen_rect.mRight, screen_rect.mBottom, 1.0f); - gGL.begin(LLRender::QUADS); + gGL.begin(LLRender::TRIANGLE_STRIP); { gGL.vertexBatchPreTransformed(vertices, 4); } diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 92fb4b75bf..4b904f09e0 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -2275,36 +2275,28 @@ void LLFloater::drawConeToOwner(F32 &context_cone_opacity, gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLGLEnable(GL_CULL_FACE); - gGL.begin(LLRender::QUADS); + gGL.begin(LLRender::TRIANGLE_STRIP); { gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop); - gGL.vertex2i(owner_rect.mRight, owner_rect.mTop); - gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); - gGL.vertex2i(local_rect.mRight, local_rect.mTop); - gGL.vertex2i(local_rect.mLeft, local_rect.mTop); - gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); gGL.vertex2i(local_rect.mLeft, local_rect.mTop); - gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); - gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom); - gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop); - + gGL.vertex2i(owner_rect.mRight, owner_rect.mTop); gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); - gGL.vertex2i(local_rect.mRight, local_rect.mBottom); gGL.vertex2i(local_rect.mRight, local_rect.mTop); gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); - gGL.vertex2i(owner_rect.mRight, owner_rect.mTop); gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom); - - gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); - gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); gGL.vertex2i(local_rect.mRight, local_rect.mBottom); gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); - gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom); gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom); + gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); + gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); + gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); + gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop); + gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); + gGL.vertex2i(local_rect.mLeft, local_rect.mTop); } gGL.end(); } diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index 388dc5b1ac..42a9e267d2 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -1649,7 +1649,7 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr { LLRect local_rect = item->getLocalRect(); S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); - S32 label_height = getLabelFontForStyle(mLabelStyle)->getLineHeight(); + S32 label_height = getLabelFont()->getLineHeight(); // when navigating with keyboard, only move top of opened folder on screen, otherwise show whole folder S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + item->getIconPad()) : local_rect.getHeight(); diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index e4f5664908..18bde344a0 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -48,7 +48,6 @@ static LLDefaultChildRegistry::Register<LLFolderViewItem> r("folder_view_item"); // statics std::map<U8, LLFontGL*> LLFolderViewItem::sFonts; // map of styles to fonts -bool LLFolderViewItem::sColorSetInitialized = false; LLUIColor LLFolderViewItem::sFgColor; LLUIColor LLFolderViewItem::sHighlightBgColor; LLUIColor LLFolderViewItem::sFlashBgColor; @@ -58,6 +57,10 @@ LLUIColor LLFolderViewItem::sFilterBGColor; LLUIColor LLFolderViewItem::sFilterTextColor; LLUIColor LLFolderViewItem::sSuffixColor; LLUIColor LLFolderViewItem::sSearchStatusColor; +S32 LLFolderViewItem::sTopPad = 0; +LLUIImagePtr LLFolderViewItem::sFolderArrowImg; +LLUIImagePtr LLFolderViewItem::sSelectionImg; +LLFontGL* LLFolderViewItem::sSuffixFont = nullptr; // only integers can be initialized in header const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f; @@ -83,15 +86,42 @@ LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style) return rtn; } + +const LLFontGL* LLFolderViewItem::getLabelFont() +{ + if (!pLabelFont) + { + pLabelFont = getLabelFontForStyle(mLabelStyle); + } + return pLabelFont; +} //static void LLFolderViewItem::initClass() { + const Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); + sTopPad = default_params.item_top_pad; + sFolderArrowImg = default_params.folder_arrow_image; + sSelectionImg = default_params.selection_image; + sSuffixFont = getLabelFontForStyle(LLFontGL::NORMAL); + + sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); + sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE); + sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); + sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); + sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); + sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); + sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); + sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); } //static void LLFolderViewItem::cleanupClass() { sFonts.clear(); + sFolderArrowImg = nullptr; + sSelectionImg = nullptr; + sSuffixFont = nullptr; } @@ -134,6 +164,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mIsItemCut(false), mCutGeneration(0), mLabelStyle( LLFontGL::NORMAL ), + pLabelFont(nullptr), mHasVisibleChildren(false), mLocalIndentation(p.folder_indentation), mIndentation(0), @@ -158,20 +189,6 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mMaxFolderItemOverlap(p.max_folder_item_overlap), mDoubleClickOverride(p.double_click_override) { - if (!sColorSetInitialized) - { - sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); - sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); - sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE); - sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); - sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); - sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); - sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); - sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); - sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); - sColorSetInitialized = true; - } - if (mViewModelItem) { mViewModelItem->setFolderViewItem(this); @@ -307,6 +324,7 @@ void LLFolderViewItem::refresh() LLFolderViewModelItem& vmi = *getViewModelItem(); mLabel = utf8str_to_wstring(vmi.getDisplayName()); + mLabelFontBuffer.reset(); setToolTip(vmi.getName()); // icons are slightly expensive to get, can be optimized // see LLInventoryIcon::getIcon() @@ -319,7 +337,9 @@ void LLFolderViewItem::refresh() // Very Expensive! // Can do a number of expensive checks, like checking active motions, wearables or friend list mLabelStyle = vmi.getLabelStyle(); + pLabelFont = nullptr; // refresh can be called from a coro, don't use getLabelFontForStyle, coro trips font list tread safety mLabelSuffix = utf8str_to_wstring(vmi.getLabelSuffix()); + mSuffixFontBuffer.reset(); } // Dirty the filter flag of the model from the view (CHUI-849) @@ -344,6 +364,7 @@ void LLFolderViewItem::refreshSuffix() // Very Expensive! // Can do a number of expensive checks, like checking active motions, wearables or friend list mLabelStyle = vmi->getLabelStyle(); + pLabelFont = nullptr; mLabelSuffix = utf8str_to_wstring(vmi->getLabelSuffix()); } @@ -736,19 +757,17 @@ bool LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, return handled; } -void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color) +void LLFolderViewItem::drawOpenFolderArrow() { //--------------------------------------------------------------------------------// // Draw open folder arrow // - const S32 TOP_PAD = default_params.item_top_pad; if (hasVisibleChildren() || !isFolderComplete()) { - LLUIImage* arrow_image = default_params.folder_arrow_image; gl_draw_scaled_rotated_image( - mIndentation, getRect().getHeight() - mArrowSize - mTextPad - TOP_PAD, - mArrowSize, mArrowSize, mControlLabelRotation, arrow_image->getImage(), fg_color); + mIndentation, getRect().getHeight() - mArrowSize - mTextPad - sTopPad, + mArrowSize, mArrowSize, mControlLabelRotation, sFolderArrowImg->getImage(), sFgColor); } } @@ -764,7 +783,7 @@ void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const L /*virtual*/ bool LLFolderViewItem::isFadeItem() { - LLClipboard& clipboard = LLClipboard::instance(); + static const LLClipboard& clipboard = LLClipboard::instance(); // Make it a 'simpleton'? if (mCutGeneration != clipboard.getGeneration()) { mCutGeneration = clipboard.getGeneration(); @@ -775,8 +794,9 @@ void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const L return mIsItemCut; } -void LLFolderViewItem::drawHighlight(const bool showContent, const bool hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, - const LLUIColor &focusOutlineColor, const LLUIColor &mouseOverColor) +void LLFolderViewItem::drawHighlight(bool showContent, bool hasKeyboardFocus, + const LLUIColor& selectColor, const LLUIColor& flashColor, + const LLUIColor& focusOutlineColor, const LLUIColor& mouseOverColor) { const S32 focus_top = getRect().getHeight(); const S32 focus_bottom = getRect().getHeight() - mItemHeight; @@ -784,7 +804,7 @@ void LLFolderViewItem::drawHighlight(const bool showContent, const bool hasKeybo const S32 FOCUS_LEFT = 1; // Determine which background color to use for highlighting - const LLUIColor& bgColor = (isFlashing() ? flashColor : selectColor); + const LLUIColor& bgColor = isFlashing() ? flashColor : selectColor; //--------------------------------------------------------------------------------// // Draw highlight for selected items @@ -792,7 +812,6 @@ void LLFolderViewItem::drawHighlight(const bool showContent, const bool hasKeybo // items if mShowSingleSelection is false. // if (isHighlightAllowed()) - { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); @@ -801,7 +820,7 @@ void LLFolderViewItem::drawHighlight(const bool showContent, const bool hasKeybo { LLColor4 bg_color = bgColor; // do time-based fade of extra objects - F32 fade_time = (getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.0f); + F32 fade_time = getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.f; if (getRoot() && getRoot()->getShowSingleSelection()) { // fading out @@ -890,7 +909,7 @@ void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y //--------------------------------------------------------------------------------// // Draw the actual label text // - font->render(mLabel, 0, x, y, color, + mLabelFontBuffer.render(font, mLabel, 0, x, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, /*use_ellipses*/true); } @@ -900,16 +919,14 @@ void LLFolderViewItem::draw() const bool show_context = (getRoot() ? getRoot()->getShowSelectionContext() : false); const bool filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : false); // If we have keyboard focus, draw selection filled - const Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - const S32 TOP_PAD = default_params.item_top_pad; - - const LLFontGL* font = getLabelFontForStyle(mLabelStyle); + const LLFontGL* font = getLabelFont(); + S32 line_height = font->getLineHeight(); getViewModelItem()->update(); - if(!mSingleFolderMode) + if (!mSingleFolderMode) { - drawOpenFolderArrow(default_params, sFgColor); + drawOpenFolderArrow(); } drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor); @@ -918,18 +935,19 @@ void LLFolderViewItem::draw() // Draw open icon // const S32 icon_x = mIndentation + mArrowSize + mTextPad; + const S32 rect_height = getRect().getHeight(); if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) // For open folders { - mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1); + mIconOpen->draw(icon_x, rect_height - mIconOpen->getHeight() - sTopPad + 1); } else if (mIcon) { - mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); + mIcon->draw(icon_x, rect_height - mIcon->getHeight() - sTopPad + 1); } if (mIconOverlay && getRoot()->showItemLinkOverlays()) { - mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); + mIconOverlay->draw(icon_x, rect_height - mIcon->getHeight() - sTopPad + 1); } //--------------------------------------------------------------------------------// @@ -940,26 +958,24 @@ void LLFolderViewItem::draw() return; } - auto filter_string_length = mViewModelItem->hasFilterStringMatch() ? static_cast<S32>(mViewModelItem->getFilterStringSize()) : 0; + S32 filter_string_length = mViewModelItem->hasFilterStringMatch() ? (S32)mViewModelItem->getFilterStringSize() : 0; F32 right_x = 0; - F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + F32 y = (F32)rect_height - line_height - (F32)mTextPad - (F32)sTopPad; F32 text_left = (F32)getLabelXPos(); LLWString combined_string = mLabel + mLabelSuffix; - const LLFontGL* suffix_font = getLabelFontForStyle(LLFontGL::NORMAL); S32 filter_offset = static_cast<S32>(mViewModelItem->getFilterStringOffset()); if (filter_string_length > 0) { - S32 bottom = getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD; - S32 top = getRect().getHeight() - TOP_PAD; - if(mLabelSuffix.empty() || (font == suffix_font)) + S32 bottom = rect_height - line_height - 3 - sTopPad; + S32 top = rect_height - sTopPad; + if(mLabelSuffix.empty() || (font == sSuffixFont)) { - S32 left = ll_round(text_left) + font->getWidth(combined_string.c_str(), 0, static_cast<S32>(mViewModelItem->getFilterStringOffset())) - 2; - S32 right = left + font->getWidth(combined_string.c_str(), static_cast<S32>(mViewModelItem->getFilterStringOffset()), filter_string_length) + 2; + S32 left = ll_round(text_left) + font->getWidth(combined_string.c_str(), 0, filter_offset) - 2; + S32 right = left + font->getWidth(combined_string.c_str(), filter_offset, filter_string_length) + 2; - LLUIImage* box_image = default_params.selection_image; - LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); + LLRect box_rect(left, top, right, bottom); + sSelectionImg->draw(box_rect, sFilterBGColor); } else { @@ -968,19 +984,17 @@ void LLFolderViewItem::draw() { S32 left = (S32)(ll_round(text_left) + font->getWidthF32(mLabel.c_str(), 0, llmin(filter_offset, (S32)mLabel.size()))) - 2; S32 right = left + (S32)font->getWidthF32(mLabel.c_str(), filter_offset, label_filter_length) + 2; - LLUIImage* box_image = default_params.selection_image; LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); + sSelectionImg->draw(box_rect, sFilterBGColor); } S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; if(suffix_filter_length > 0) { S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); - S32 left = (S32)(ll_round(text_left) + font->getWidthF32(mLabel.c_str(), 0, static_cast<S32>(mLabel.size())) + suffix_font->getWidthF32(mLabelSuffix.c_str(), 0, suffix_offset)) - 2; - S32 right = left + (S32)suffix_font->getWidthF32(mLabelSuffix.c_str(), suffix_offset, suffix_filter_length) + 2; - LLUIImage* box_image = default_params.selection_image; + S32 left = (S32)(ll_round(text_left) + font->getWidthF32(mLabel.c_str(), 0, static_cast<S32>(mLabel.size())) + sSuffixFont->getWidthF32(mLabelSuffix.c_str(), 0, suffix_offset)) - 2; + S32 right = left + (S32)sSuffixFont->getWidthF32(mLabelSuffix.c_str(), suffix_offset, suffix_filter_length) + 2; LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); + sSelectionImg->draw(box_rect, sFilterBGColor); } } } @@ -999,9 +1013,9 @@ void LLFolderViewItem::draw() // if (!mLabelSuffix.empty()) { - suffix_font->render( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : sSuffixColor.get(), - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x); + mSuffixFontBuffer.render(sSuffixFont, mLabelSuffix, 0, right_x, y, isFadeItem() ? color : sSuffixColor.get(), + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, S32_MAX, &right_x); } //--------------------------------------------------------------------------------// @@ -1009,10 +1023,10 @@ void LLFolderViewItem::draw() // if (filter_string_length > 0) { - if(mLabelSuffix.empty() || (font == suffix_font)) + if(mLabelSuffix.empty() || (font == sSuffixFont)) { F32 match_string_left = text_left + font->getWidthF32(combined_string.c_str(), 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string.c_str(), filter_offset, filter_string_length); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + F32 yy = (F32)rect_height - line_height - (F32)mTextPad - (F32)sTopPad; font->render(combined_string, filter_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, filter_string_length, S32_MAX, &right_x); @@ -1023,7 +1037,7 @@ void LLFolderViewItem::draw() if(label_filter_length > 0) { F32 match_string_left = text_left + font->getWidthF32(mLabel.c_str(), 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel.c_str(), filter_offset, label_filter_length); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + F32 yy = (F32)rect_height - line_height - (F32)mTextPad - (F32)sTopPad; font->render(mLabel, filter_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, label_filter_length, S32_MAX, &right_x); @@ -1033,9 +1047,9 @@ void LLFolderViewItem::draw() if(suffix_filter_length > 0) { S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); - F32 match_string_left = text_left + font->getWidthF32(mLabel.c_str(), 0, static_cast<S32>(mLabel.size())) + suffix_font->getWidthF32(mLabelSuffix.c_str(), 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix.c_str(), suffix_offset, suffix_filter_length); - F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; - suffix_font->render(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, + F32 match_string_left = text_left + font->getWidthF32(mLabel.c_str(), 0, static_cast<S32>(mLabel.size())) + sSuffixFont->getWidthF32(mLabelSuffix.c_str(), 0, suffix_offset + suffix_filter_length) - sSuffixFont->getWidthF32(mLabelSuffix.c_str(), suffix_offset, suffix_filter_length); + F32 yy = (F32)rect_height - sSuffixFont->getLineHeight() - (F32)mTextPad - (F32)sTopPad; + sSuffixFont->render(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, suffix_filter_length, S32_MAX, &right_x); } diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 60cdac3ab9..cc8a7d934c 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -29,6 +29,7 @@ #include "llflashtimer.h" #include "llview.h" #include "lluiimage.h" +#include "llfontvertexbuffer.h" class LLFolderView; class LLFolderViewModelItem; @@ -134,7 +135,6 @@ protected: LLUIColor mFontHighlightColor; // For now assuming all colors are the same in derived classes. - static bool sColorSetInitialized; static LLUIColor sFgColor; static LLUIColor sFgDisabledColor; static LLUIColor sHighlightBgColor; @@ -157,6 +157,7 @@ protected: virtual void setFlashState(bool) { } static LLFontGL* getLabelFontForStyle(U8 style); + const LLFontGL* getLabelFont(); bool mIsSelected; @@ -296,9 +297,9 @@ public: // virtual void handleDropped(); virtual void draw(); - void drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color); - void drawHighlight(const bool showContent, const bool hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor); - void drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x); + void drawOpenFolderArrow(); + void drawHighlight(bool showContent, bool hasKeyboardFocus, const LLUIColor& selectColor, const LLUIColor& flashColor, const LLUIColor& outlineColor, const LLUIColor& mouseOverColor); + void drawLabel(const LLFontGL* font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x); virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, @@ -307,6 +308,14 @@ public: private: static std::map<U8, LLFontGL*> sFonts; // map of styles to fonts + static S32 sTopPad; + static LLUIImagePtr sFolderArrowImg; + static LLUIImagePtr sSelectionImg; + static LLFontGL* sSuffixFont; + + LLFontVertexBuffer mLabelFontBuffer; + LLFontVertexBuffer mSuffixFontBuffer; + LLFontGL* pLabelFont{nullptr}; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index b8d6d89971..9372818ca5 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -292,6 +292,7 @@ public: dirtyFilter(); requestSort(); } + virtual void removeChild(LLFolderViewModelItem* child) { mChildren.remove(child); diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 60b6115b34..66b274c33f 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -436,6 +436,9 @@ void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit { mText.assign(utf8str_symbol_truncate(truncated_utf8, mMaxLengthChars)); } + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); if (all_selected) { @@ -617,6 +620,10 @@ void LLLineEditor::replaceWithSuggestion(U32 index) mText.insert(it->first, suggestion); setCursor(it->first + (S32)suggestion.length()); + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); + break; } } @@ -969,6 +976,10 @@ void LLLineEditor::removeChar() mText.erase(getCursor() - 1, 1); setCursor(getCursor() - 1); + + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); } else { @@ -992,6 +1003,10 @@ void LLLineEditor::addChar(const llwchar uni_char) return; mText.erase(getCursor(), 1); + + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); } S32 cur_bytes = static_cast<S32>(mText.getString().size()); @@ -1022,6 +1037,10 @@ void LLLineEditor::addChar(const llwchar uni_char) mText.insert(getCursor(), w_buf); setCursor(getCursor() + 1); + + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); } else { @@ -1186,6 +1205,10 @@ void LLLineEditor::deleteSelection() mText.erase(left_pos, selection_length); deselect(); setCursor(left_pos); + + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); } } @@ -1346,6 +1369,10 @@ void LLLineEditor::pasteHelper(bool is_primary) setCursor( getCursor() + (S32)clean_string.length() ); deselect(); + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); + // Validate new string and rollback the if needed. bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); if (need_to_rollback) @@ -1506,6 +1533,10 @@ bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) { mText.assign(*(--mCurrentHistoryLine)); setCursorToEnd(); + + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); } else { @@ -1523,6 +1554,10 @@ bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) { mText.assign( *(++mCurrentHistoryLine) ); setCursorToEnd(); + + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); } else { @@ -1897,7 +1932,8 @@ void LLLineEditor::draw() if( select_left > mScrollHPos ) { // unselected, left side - rendered_text = mGLFont->render( + rendered_text = mFontBufferPreSelection.render( + mGLFont, mText, mScrollHPos, rendered_pixels_right, text_bottom, text_color, @@ -1919,7 +1955,8 @@ void LLLineEditor::draw() gl_rect_2d(ll_round(rendered_pixels_right), cursor_top, ll_round(rendered_pixels_right)+width, cursor_bottom, color); LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); - rendered_text += mGLFont->render( + rendered_text += mFontBufferSelection.render( + mGLFont, mText, mScrollHPos + rendered_text, rendered_pixels_right, text_bottom, tmp_color, @@ -1934,7 +1971,8 @@ void LLLineEditor::draw() if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) ) { // unselected, right side - rendered_text += mGLFont->render( + rendered_text += mFontBufferPostSelection.render( + mGLFont, mText, mScrollHPos + rendered_text, rendered_pixels_right, text_bottom, text_color, @@ -1948,7 +1986,8 @@ void LLLineEditor::draw() } else { - rendered_text = mGLFont->render( + rendered_text = mFontBufferPreSelection.render( + mGLFont, mText, mScrollHPos, rendered_pixels_right, text_bottom, text_color, @@ -2108,7 +2147,8 @@ void LLLineEditor::draw() //to give indication that it is not text you typed in if (0 == mText.length() && (mReadOnly || mShowLabelFocused)) { - mGLFont->render(mLabel.getWString(), 0, + mFontBufferLabel.render(mGLFont, + mLabel.getWString(), 0, (F32)mTextLeftEdge, (F32)text_bottom, label_color, LLFontGL::LEFT, @@ -2133,7 +2173,8 @@ void LLLineEditor::draw() // draw label if no text provided if (0 == mText.length()) { - mGLFont->render(mLabel.getWString(), 0, + mFontBufferLabel.render(mGLFont, + mLabel.getWString(), 0, (F32)mTextLeftEdge, (F32)text_bottom, label_color, LLFontGL::LEFT, @@ -2404,12 +2445,16 @@ void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data) bool LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) { mText.setArg(key, text); + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); return true; } bool LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) { mLabel.setArg(key, text); + mFontBufferLabel.reset(); return true; } @@ -2508,6 +2553,9 @@ void LLLineEditor::updatePreedit(const LLWString &preedit_string, mPreeditOverwrittenWString.clear(); } mText.insert(insert_preedit_at, mPreeditWString); + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); mPreeditStandouts = preedit_standouts; diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index cdd22413e7..12fe800acb 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -40,6 +40,7 @@ #include "llframetimer.h" #include "lleditmenuhandler.h" +#include "llfontvertexbuffer.h" #include "llspellcheckmenuhandler.h" #include "lluictrl.h" #include "lluiimage.h" @@ -344,6 +345,10 @@ protected: LLViewBorder* mBorder; const LLFontGL* mGLFont; + LLFontVertexBuffer mFontBufferPreSelection; + LLFontVertexBuffer mFontBufferSelection; + LLFontVertexBuffer mFontBufferPostSelection; + LLFontVertexBuffer mFontBufferLabel; S32 mMaxLengthBytes; // Max length of the UTF8 string in bytes S32 mMaxLengthChars; // Maximum number of characters in the string S32 mCursorPos; // I-beam is just after the mCursorPos-th character. diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp index 2693243eb1..62c0401869 100644 --- a/indra/llui/llstatbar.cpp +++ b/indra/llui/llstatbar.cpp @@ -460,7 +460,7 @@ void LLStatBar::draw() max_value = 0.f; gGL.color4f(1.f, 0.f, 0.f, 1.f); - gGL.begin( LLRender::QUADS ); + gGL.begin(LLRender::TRIANGLES); const S32 max_frame = llmin(num_frames, num_values); U32 num_samples = 0; for (S32 i = 1; i <= max_frame; i++) @@ -498,6 +498,9 @@ void LLStatBar::draw() gGL.vertex2f((F32)bar_rect.mRight - offset, max); gGL.vertex2f((F32)bar_rect.mRight - offset, min); gGL.vertex2f((F32)bar_rect.mRight - offset - 1, min); + + gGL.vertex2f((F32)bar_rect.mRight - offset, max); + gGL.vertex2f((F32)bar_rect.mRight - offset - 1, min); gGL.vertex2f((F32)bar_rect.mRight - offset - 1, max); } else @@ -505,7 +508,10 @@ void LLStatBar::draw() gGL.vertex2f(min, (F32)bar_rect.mBottom + offset + 1); gGL.vertex2f(min, (F32)bar_rect.mBottom + offset); gGL.vertex2f(max, (F32)bar_rect.mBottom + offset); - gGL.vertex2f(max, (F32)bar_rect.mBottom + offset + 1 ); + + gGL.vertex2f(min, (F32)bar_rect.mBottom + offset + 1); + gGL.vertex2f(max, (F32)bar_rect.mBottom + offset); + gGL.vertex2f(max, (F32)bar_rect.mBottom + offset + 1); } } gGL.end(); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 0aebf7543c..cbbf83d679 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -948,7 +948,6 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length) { - beforeValueChange(); segment_set_t::iterator seg_iter = getSegIterContaining(pos); while(seg_iter != mSegments.end()) @@ -1325,6 +1324,7 @@ void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent) //virtual void LLTextBase::draw() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; // reflow if needed, on demand reflow(); @@ -2368,6 +2368,34 @@ S32 LLTextBase::removeFirstLine() return 0; } +// virtual +void LLTextBase::copyContents(const LLTextBase* source) +{ + llassert(source); + if (!source) + return; + + beforeValueChange(); + deselect(); + + mSegments.clear(); + for (const LLTextSegmentPtr& segp : source->mSegments) + { + mSegments.emplace(segp->clone(*this)); + } + + mLineInfoList.clear(); + for (const line_info& li : mLineInfoList) + { + mLineInfoList.push_back(line_info(li)); + } + + getViewModel()->setDisplay(source->getViewModel()->getDisplay()); + + onValueChange(0, getLength()); + needsReflow(); +} + void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params) { segment_vec_t segments; @@ -2586,6 +2614,11 @@ const LLWString& LLTextBase::getWText() const return getViewModel()->getDisplay(); } +S32 LLTextBase::getTextGeneration() const +{ + return getViewModel()->getDisplayGeneration(); +} + // If round is true, if the position is on the right half of a character, the cursor // will be put to its right. If round is false, the cursor will always be put to the // character's left. @@ -3228,6 +3261,24 @@ boost::signals2::connection LLTextBase::setIsObjectBlockedCallback(const is_bloc LLTextSegment::~LLTextSegment() {} +// static +LLStyleSP LLTextSegment::cloneStyle(LLTextBase& target, const LLStyle* source) +{ + // Take most params from target + LLStyle::Params params = target.getStyleParams(); + LLStyle* style = new LLStyle(params); + + // Take some params from source + style->setLinkHREF(source->getLinkHREF()); + if (source->isImage()) + { + style->setImage(source->getImage()->getName()); + } + + return style; +} + + bool LLTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; height = 0; return false; } bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const { @@ -3280,7 +3331,8 @@ LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 e : LLTextSegment(start, end), mStyle( style ), mToken(NULL), - mEditor(editor) + mEditor(editor), + mLastGeneration(-1) { mFontHeight = mStyle->getFont()->getLineHeight(); @@ -3294,7 +3346,8 @@ LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 e LLNormalTextSegment::LLNormalTextSegment( const LLUIColor& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) : LLTextSegment(start, end), mToken(NULL), - mEditor(editor) + mEditor(editor), + mLastGeneration(-1) { mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color)); @@ -3313,25 +3366,41 @@ F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selec { return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect); } + else + { + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); + } return draw_rect.mLeft; } // Draws a single text segment, reversing the color for selection if needed. F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect) { - F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha; - - const LLWString &text = getWText(); - F32 right_x = rect.mLeft; if (!mStyle->isVisible()) { return right_x; } - const LLFontGL* font = mStyle->getFont(); + F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha; + + const LLWString& text = getWText(); + S32 text_gen = mEditor.getTextGeneration(); + + if (text_gen != mLastGeneration) + { + mLastGeneration = text_gen; + + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); + } + const LLFontGL* font = mStyle->getFont(); LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % (alpha * mStyle->getAlpha()); + bool use_font_buffers = useFontBuffers(); if( selection_start > seg_start ) { @@ -3339,16 +3408,40 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 start = seg_start; S32 end = llmin( selection_start, seg_end ); S32 length = end - start; - font->render(text, start, - rect, - color, - LLFontGL::LEFT, mEditor.mTextVAlign, - LLFontGL::NORMAL, - mStyle->getShadowType(), - length, - &right_x, - mEditor.getUseEllipses(), - mEditor.getUseColor()); + if (use_font_buffers) + { + mFontBufferPreSelection.render( + font, + text, start, + rect, + color, + LLFontGL::LEFT, mEditor.mTextVAlign, + LLFontGL::NORMAL, + mStyle->getShadowType(), + length, + &right_x, + mEditor.getUseEllipses(), + mEditor.getUseColor()); + } + else + { + // Font buffer doesn't do well with changes and huge notecard with a bunch + // of segments will see a lot of buffer updates, so instead use derect + // rendering to cache. + // Todo: instead of mLastGeneration make buffer invalidation more fine grained + // like string hash of a given segment. + font->render( + text, start, + rect, + color, + LLFontGL::LEFT, mEditor.mTextVAlign, + LLFontGL::NORMAL, + mStyle->getShadowType(), + length, + &right_x, + mEditor.getUseEllipses(), + mEditor.getUseColor()); + } } rect.mLeft = right_x; @@ -3359,16 +3452,35 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 end = llmin( selection_end, seg_end ); S32 length = end - start; - font->render(text, start, - rect, - mStyle->getSelectedColor().get(), - LLFontGL::LEFT, mEditor.mTextVAlign, - LLFontGL::NORMAL, - LLFontGL::NO_SHADOW, - length, - &right_x, - mEditor.getUseEllipses(), - mEditor.getUseColor()); + if (use_font_buffers) + { + mFontBufferSelection.render( + font, + text, start, + rect, + mStyle->getSelectedColor().get(), + LLFontGL::LEFT, mEditor.mTextVAlign, + LLFontGL::NORMAL, + LLFontGL::NO_SHADOW, + length, + &right_x, + mEditor.getUseEllipses(), + mEditor.getUseColor()); + } + else + { + font->render( + text, start, + rect, + mStyle->getSelectedColor().get(), + LLFontGL::LEFT, mEditor.mTextVAlign, + LLFontGL::NORMAL, + LLFontGL::NO_SHADOW, + length, + &right_x, + mEditor.getUseEllipses(), + mEditor.getUseColor()); + } } rect.mLeft = right_x; if( selection_end < seg_end ) @@ -3377,16 +3489,36 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 start = llmax( selection_end, seg_start ); S32 end = seg_end; S32 length = end - start; - font->render(text, start, - rect, - color, - LLFontGL::LEFT, mEditor.mTextVAlign, - LLFontGL::NORMAL, - mStyle->getShadowType(), - length, - &right_x, - mEditor.getUseEllipses(), - mEditor.getUseColor()); + if (use_font_buffers) + { + mFontBufferPostSelection.render( + font, + text, start, + rect, + color, + LLFontGL::LEFT, mEditor.mTextVAlign, + LLFontGL::NORMAL, + mStyle->getShadowType(), + length, + &right_x, + mEditor.getUseEllipses(), + mEditor.getUseColor()); + } + else + { + font->render( + text, start, + rect, + color, + LLFontGL::LEFT, mEditor.mTextVAlign, + LLFontGL::NORMAL, + mStyle->getShadowType(), + length, + &right_x, + mEditor.getUseEllipses(), + mEditor.getUseColor()); + + } } return right_x; } @@ -3488,6 +3620,13 @@ void LLNormalTextSegment::setToolTip(const std::string& tooltip) mTooltip = tooltip; } +// virtual +LLTextSegmentPtr LLNormalTextSegment::clone(LLTextBase& target) const +{ + LLStyleConstSP sp(cloneStyle(target, mStyle)); + return new LLNormalTextSegment(sp, mStart, mEnd, target); +} + bool LLNormalTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { height = 0; @@ -3573,6 +3712,15 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin return num_chars; } +void LLNormalTextSegment::updateLayout(const class LLTextBase& editor) +{ + LLTextSegment::updateLayout(editor); + + mFontBufferPreSelection.reset(); + mFontBufferSelection.reset(); + mFontBufferPostSelection.reset(); +} + void LLNormalTextSegment::dump() const { LL_INFOS() << "Segment [" << @@ -3606,6 +3754,13 @@ LLLabelTextSegment::LLLabelTextSegment( const LLUIColor& color, S32 start, S32 e { } +// virtual +LLTextSegmentPtr LLLabelTextSegment::clone(LLTextBase& target) const +{ + LLStyleConstSP sp(cloneStyle(target, mStyle)); + return new LLLabelTextSegment(sp, mStart, mEnd, target); +} + /*virtual*/ const LLWString& LLLabelTextSegment::getWText() const { @@ -3630,6 +3785,13 @@ LLEmojiTextSegment::LLEmojiTextSegment(const LLUIColor& color, S32 start, S32 en { } +// virtual +LLTextSegmentPtr LLEmojiTextSegment::clone(LLTextBase& target) const +{ + LLStyleConstSP sp(cloneStyle(target, mStyle)); + return new LLEmojiTextSegment(sp, mStart, mEnd, target); +} + bool LLEmojiTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { if (mTooltip.empty()) @@ -3653,6 +3815,14 @@ LLOnHoverChangeableTextSegment::LLOnHoverChangeableTextSegment( LLStyleConstSP s mHoveredStyle(style), mNormalStyle(normal_style){} +// virtual +LLTextSegmentPtr LLOnHoverChangeableTextSegment::clone(LLTextBase& target) const +{ + LLStyleConstSP hsp(cloneStyle(target, mHoveredStyle)); + LLStyleConstSP nsp(cloneStyle(target, mNormalStyle)); + return new LLOnHoverChangeableTextSegment(hsp, nsp, mStart, mEnd, target); +} + /*virtual*/ F32 LLOnHoverChangeableTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { @@ -3692,6 +3862,13 @@ LLInlineViewSegment::~LLInlineViewSegment() mView->die(); } +// virtual +LLTextSegmentPtr LLInlineViewSegment::clone(LLTextBase& target) const +{ + llassert_always_msg(false, "NOT SUPPORTED"); + return nullptr; +} + bool LLInlineViewSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { if (first_char == 0 && num_chars == 0) @@ -3779,6 +3956,14 @@ LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLT LLLineBreakTextSegment::~LLLineBreakTextSegment() { } + +// virtual +LLTextSegmentPtr LLLineBreakTextSegment::clone(LLTextBase& target) const +{ + LLLineBreakTextSegment* copy = new LLLineBreakTextSegment(mStart); + copy->mFontHeight = mFontHeight; + return copy; +} bool LLLineBreakTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; @@ -3806,8 +3991,16 @@ LLImageTextSegment::~LLImageTextSegment() { } +// virtual +LLTextSegmentPtr LLImageTextSegment::clone(LLTextBase& target) const +{ + LLStyleConstSP sp(cloneStyle(target, mStyle)); + return new LLImageTextSegment(sp, mStart, target); +} + static const S32 IMAGE_HPAD = 3; +// virtual bool LLImageTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 00cf66b134..76d4e160af 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -30,6 +30,7 @@ #include "v4color.h" #include "lleditmenuhandler.h" +#include "llfontvertexbuffer.h" #include "llspellcheckmenuhandler.h" #include "llstyle.h" #include "llkeywords.h" @@ -44,6 +45,7 @@ class LLScrollContainer; class LLContextMenu; class LLUrlMatch; +class LLTextBase; /// /// A text segment is used to specify a subsection of a text string @@ -61,6 +63,9 @@ public: mEnd(end) {} virtual ~LLTextSegment(); + virtual LLTextSegmentPtr clone(LLTextBase& terget) const { return new LLTextSegment(mStart, mEnd); } + static LLStyleSP cloneStyle(LLTextBase& target, const LLStyle* source); + bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; virtual bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; @@ -127,10 +132,12 @@ public: LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); LLNormalTextSegment( const LLUIColor& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); virtual ~LLNormalTextSegment(); + /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; + /*virtual*/ void updateLayout(const class LLTextBase& editor); /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); /*virtual*/ bool canEdit() const { return true; } /*virtual*/ const LLUIColor& getColor() const { return mStyle->getColor(); } @@ -149,6 +156,7 @@ public: /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); protected: + virtual bool useFontBuffers() const { return true; } F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect); virtual const LLWString& getWText() const; @@ -161,6 +169,12 @@ protected: LLKeywordToken* mToken; std::string mTooltip; boost::signals2::connection mImageLoadedConnection; + + // font rendering + LLFontVertexBuffer mFontBufferPreSelection; + LLFontVertexBuffer mFontBufferSelection; + LLFontVertexBuffer mFontBufferPostSelection; + S32 mLastGeneration = -1; }; // This text segment is the same as LLNormalTextSegment, the only difference @@ -171,6 +185,7 @@ class LLLabelTextSegment : public LLNormalTextSegment public: LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); LLLabelTextSegment( const LLUIColor& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); + /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; protected: @@ -185,6 +200,7 @@ class LLEmojiTextSegment : public LLNormalTextSegment public: LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor); LLEmojiTextSegment(const LLUIColor& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); + /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const override; bool canEdit() const override { return false; } bool handleToolTip(S32 x, S32 y, MASK mask) override; @@ -195,6 +211,7 @@ class LLOnHoverChangeableTextSegment : public LLNormalTextSegment { public: LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor ); + /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); protected: @@ -209,6 +226,7 @@ class LLIndexSegment : public LLTextSegment { public: LLIndexSegment() : LLTextSegment(0, 0) {} + /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const { return new LLIndexSegment(); } }; class LLInlineViewSegment : public LLTextSegment @@ -226,6 +244,8 @@ public: LLInlineViewSegment(const Params& p, S32 start, S32 end); ~LLInlineViewSegment(); + /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; /*virtual*/ void updateLayout(const class LLTextBase& editor); @@ -250,6 +270,7 @@ public: LLLineBreakTextSegment(LLStyleConstSP style,S32 pos); LLLineBreakTextSegment(S32 pos); ~LLLineBreakTextSegment(); + /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); @@ -263,6 +284,8 @@ class LLImageTextSegment : public LLTextSegment public: LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor); ~LLImageTextSegment(); + /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 char_offset, S32 max_chars, S32 line_ind) const; F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); @@ -432,6 +455,7 @@ public: // wide-char versions void setWText(const LLWString& text); const LLWString& getWText() const; + S32 getTextGeneration() const; void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params()); @@ -475,7 +499,7 @@ public: LLRect getDocRectFromDocIndex(S32 pos) const; void setReadOnly(bool read_only) { mReadOnly = read_only; } - bool getReadOnly() { return mReadOnly; } + bool getReadOnly() const { return mReadOnly; } void setSkipLinkUnderline(bool skip_link_underline) { mSkipLinkUnderline = skip_link_underline; } bool getSkipLinkUnderline() { return mSkipLinkUnderline; } @@ -500,6 +524,7 @@ public: const LLFontGL* getFont() const override { return mFont; } + virtual void copyContents(const LLTextBase* source); virtual void appendLineBreakSegment(const LLStyle::Params& style_params); virtual void appendImageSegment(const LLStyle::Params& style_params); virtual void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 0b5acf19a1..e9e7070414 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -271,6 +271,8 @@ protected: virtual bool getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; virtual S32 getPreeditFontSize() const; virtual LLWString getPreeditString() const { return getWText(); } + + virtual bool useFontBuffers() const { return getReadOnly(); } // // Protected data // diff --git a/indra/llui/lltrans.cpp b/indra/llui/lltrans.cpp index 8410031653..0c6c5b64e4 100644 --- a/indra/llui/lltrans.cpp +++ b/indra/llui/lltrans.cpp @@ -28,12 +28,8 @@ #include "lltrans.h" -#include "llfasttimer.h" // for call count statistics #include "llxuiparser.h" #include "llsd.h" -#include "llxmlnode.h" - -#include <map> LLTrans::template_map_t LLTrans::sStringTemplates; LLTrans::template_map_t LLTrans::sDefaultStringTemplates; @@ -59,7 +55,7 @@ struct StringTable : public LLInitParam::Block<StringTable> }; //static -bool LLTrans::parseStrings(LLXMLNodePtr &root, const std::set<std::string>& default_args) +bool LLTrans::parseStrings(LLXMLNodePtr& root, const std::set<std::string>& default_args) { std::string xml_filename = "(strings file)"; if (!root->hasName("strings")) @@ -107,7 +103,7 @@ bool LLTrans::parseStrings(LLXMLNodePtr &root, const std::set<std::string>& defa //static -bool LLTrans::parseLanguageStrings(LLXMLNodePtr &root) +bool LLTrans::parseLanguageStrings(LLXMLNodePtr& root) { std::string xml_filename = "(language strings file)"; if (!root->hasName("strings")) @@ -138,10 +134,6 @@ bool LLTrans::parseLanguageStrings(LLXMLNodePtr &root) return true; } - - -static LLTrace::BlockTimerStatHandle FTM_GET_TRANS("Translate string"); - //static std::string LLTrans::getString(std::string_view xml_desc, const LLStringUtil::format_map_t& msg_args, bool def_string) { @@ -235,7 +227,7 @@ std::string LLTrans::getDefString(std::string_view xml_desc, const LLSD& msg_arg } //static -bool LLTrans::findString(std::string &result, std::string_view xml_desc, const LLStringUtil::format_map_t& msg_args) +bool LLTrans::findString(std::string& result, std::string_view xml_desc, const LLStringUtil::format_map_t& msg_args) { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -257,7 +249,7 @@ bool LLTrans::findString(std::string &result, std::string_view xml_desc, const L } //static -bool LLTrans::findString(std::string &result, std::string_view xml_desc, const LLSD& msg_args) +bool LLTrans::findString(std::string& result, std::string_view xml_desc, const LLSD& msg_args) { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -277,7 +269,7 @@ bool LLTrans::findString(std::string &result, std::string_view xml_desc, const L } //static -std::string LLTrans::getCountString(const std::string& language, const std::string& xml_desc, S32 count) +std::string LLTrans::getCountString(std::string_view language, std::string_view xml_desc, S32 count) { // Compute which string identifier to use const char* form = ""; @@ -337,7 +329,7 @@ std::string LLTrans::getCountString(const std::string& language, const std::stri args["[COUNT]"] = llformat("%d", count); // Look up "AgeYearsB" or "AgeWeeksC" including the "form" - std::string key = llformat("%s%s", xml_desc.c_str(), form); + std::string key = llformat("%s%s", xml_desc.data(), form); return getString(key, args); } diff --git a/indra/llui/lltrans.h b/indra/llui/lltrans.h index 3492ed0169..c5d01e6f8d 100644 --- a/indra/llui/lltrans.h +++ b/indra/llui/lltrans.h @@ -30,10 +30,8 @@ #include <map> #include <set> -#include "llpointer.h" #include "llstring.h" - -class LLXMLNode; +#include "llxmlnode.h" class LLSD; @@ -58,17 +56,17 @@ public: class LLTrans { public: - LLTrans(); + LLTrans() = default; /** * @brief Parses the xml root that holds the strings. Used once on startup -// *FIXME * @param xml_filename Filename to parse + * @param root xml root node to parse * @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE" * @returns true if the file was parsed successfully, true if something went wrong */ - static bool parseStrings(LLPointer<LLXMLNode> & root, const std::set<std::string>& default_args); + static bool parseStrings(LLXMLNodePtr& root, const std::set<std::string>& default_args); - static bool parseLanguageStrings(LLPointer<LLXMLNode> & root); + static bool parseLanguageStrings(LLXMLNodePtr& root); /** * @brief Returns a translated string @@ -80,14 +78,14 @@ public: static std::string getDefString(std::string_view xml_desc, const LLStringUtil::format_map_t& args); static std::string getString(std::string_view xml_desc, const LLSD& args, bool def_string = false); static std::string getDefString(std::string_view xml_desc, const LLSD& args); - static bool findString(std::string &result, std::string_view xml_desc, const LLStringUtil::format_map_t& args); - static bool findString(std::string &result, std::string_view xml_desc, const LLSD& args); + static bool findString(std::string& result, std::string_view xml_desc, const LLStringUtil::format_map_t& args); + static bool findString(std::string& result, std::string_view xml_desc, const LLSD& args); // Returns translated string with [COUNT] replaced with a number, following // special per-language logic for plural nouns. For example, some languages // may have different plurals for 0, 1, 2 and > 2. // See "AgeWeeksA", "AgeWeeksB", etc. in strings.xml for examples. - static std::string getCountString(const std::string& language, const std::string& xml_desc, S32 count); + static std::string getCountString(std::string_view language, std::string_view xml_desc, S32 count); /** * @brief Returns a translated string diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 79d2fcd049..3cc0c05ffa 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -600,15 +600,15 @@ void LLUrlEntryAgent::callObservers(const std::string &id, void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name) { - avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id); - if (it != mAvatarNameCacheConnections.end()) + auto range = mAvatarNameCacheConnections.equal_range(id); + for (avatar_name_cache_connection_map_t::iterator it = range.first; it != range.second; ++it) { if (it->second.connected()) { it->second.disconnect(); } - mAvatarNameCacheConnections.erase(it); } + mAvatarNameCacheConnections.erase(range.first, range.second); std::string label = av_name.getCompleteName(); @@ -695,16 +695,7 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa } else { - avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id); - if (it != mAvatarNameCacheConnections.end()) - { - if (it->second.connected()) - { - it->second.disconnect(); - } - mAvatarNameCacheConnections.erase(it); - } - mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); + mAvatarNameCacheConnections.emplace(agent_id, LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2))); addObserver(agent_id_string, url, cb); return LLTrans::getString("LoadingData"); @@ -770,17 +761,17 @@ LLUrlEntryAgentName::LLUrlEntryAgentName() {} void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, - const LLAvatarName& av_name) + const LLAvatarName& av_name) { - avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id); - if (it != mAvatarNameCacheConnections.end()) + auto range = mAvatarNameCacheConnections.equal_range(id); + for (avatar_name_cache_connection_map_t::iterator it = range.first; it != range.second; ++it) { if (it->second.connected()) { it->second.disconnect(); } - mAvatarNameCacheConnections.erase(it); } + mAvatarNameCacheConnections.erase(range.first, range.second); std::string label = getName(av_name); // received the agent name from the server - tell our observers @@ -815,16 +806,7 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab } else { - avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id); - if (it != mAvatarNameCacheConnections.end()) - { - if (it->second.connected()) - { - it->second.disconnect(); - } - mAvatarNameCacheConnections.erase(it); - } - mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); + mAvatarNameCacheConnections.emplace(agent_id, LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2))); addObserver(agent_id_string, url, cb); return LLTrans::getString("LoadingData"); diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 84ff278942..fffee88496 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -232,7 +232,7 @@ protected: private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - typedef std::map<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t; + typedef std::multimap<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t; avatar_name_cache_connection_map_t mAvatarNameCacheConnections; }; @@ -264,7 +264,7 @@ protected: private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - typedef std::map<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t; + typedef std::multimap<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t; avatar_name_cache_connection_map_t mAvatarNameCacheConnections; }; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 7d6c937b85..0206e46b57 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1370,8 +1370,10 @@ void LLView::drawDebugRect() y = rect_height - LINE_HEIGHT * (depth % lines + 1); - std::string debug_text = llformat("%s (%d x %d)", getName().c_str(), - debug_rect.getWidth(), debug_rect.getHeight()); + std::string debug_text = llformat("%s [%d, %d] + (%d x %d) = [%d, %d]", getName().c_str(), + debug_rect.mLeft, mParentView->getRect().getHeight() - debug_rect.mTop, + debug_rect.getWidth(), debug_rect.getHeight(), + debug_rect.mRight, mParentView->getRect().getHeight() - debug_rect.mBottom); LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW); } diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp index e5e0545dad..6d907d7e45 100644 --- a/indra/llui/llviewereventrecorder.cpp +++ b/indra/llui/llviewereventrecorder.cpp @@ -261,7 +261,7 @@ void LLViewerEventRecorder::logKeyUnicodeEvent(llwchar uni_char) { event.insert("event",LLSD("keyDown")); - LL_DEBUGS() << "[VITA] unicode key: " << uni_char << LL_ENDL; + LL_DEBUGS() << "[VITA] unicode key: " << (int)uni_char << LL_ENDL; LL_DEBUGS() << "[VITA] dumpxml " << LLSDXMLStreamer(event) << "\n" << LL_ENDL; diff --git a/indra/llui/llviewmodel.cpp b/indra/llui/llviewmodel.cpp index 93106b344f..35963c2b99 100644 --- a/indra/llui/llviewmodel.cpp +++ b/indra/llui/llviewmodel.cpp @@ -82,11 +82,20 @@ void LLTextViewModel::setValue(const LLSD& value) // approximate LLSD storage usage LLViewModel::setValue(value); mDisplay = utf8str_to_wstring(mStringValue = value.asString()); + mDisplayGeneration++; // mDisplay and mValue agree mUpdateFromDisplay = false; } +LLWString& LLTextViewModel::getEditableDisplay() +{ + mDirty = true; + mDisplayGeneration++; + mUpdateFromDisplay = true; + return mDisplay; +} + void LLTextViewModel::setDisplay(const LLWString& value) { // This is the strange way to alter the value. Normally we'd setValue() @@ -94,6 +103,7 @@ void LLTextViewModel::setDisplay(const LLWString& value) // value. But a text editor might want to edit the display string // directly, then convert back to UTF8 on commit. mDisplay = value; + mDisplayGeneration++; mDirty = true; // Don't immediately convert to UTF8 -- do it lazily -- we expect many // more setDisplay() calls than getValue() calls. Just flag that it needs diff --git a/indra/llui/llviewmodel.h b/indra/llui/llviewmodel.h index 6cf2200a81..16b0800ce2 100644 --- a/indra/llui/llviewmodel.h +++ b/indra/llui/llviewmodel.h @@ -105,7 +105,8 @@ public: // New functions /// Get the stored value in string form const LLWString& getDisplay() const { return mDisplay; } - LLWString& getEditableDisplay() { mDirty = true; mUpdateFromDisplay = true; return mDisplay; } + S32 getDisplayGeneration() const { return mDisplayGeneration; } + LLWString& getEditableDisplay(); /** * Set the display string directly (see LLTextEditor). What the user is @@ -120,6 +121,7 @@ private: /// To avoid converting every widget's stored value from LLSD to LLWString /// every frame, cache the converted value LLWString mDisplay; + S32 mDisplayGeneration = -1; /// As the user edits individual characters (setDisplay()), defer /// LLWString-to-UTF8 conversions until s/he's done. |