summaryrefslogtreecommitdiff
path: root/indra/llui/lltextbase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/lltextbase.cpp')
-rw-r--r--indra/llui/lltextbase.cpp374
1 files changed, 242 insertions, 132 deletions
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 8fc6f16702..38b35020b5 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -401,11 +401,11 @@ void LLTextBase::drawSelectionBackground()
++rect_it)
{
LLRect selection_rect = *rect_it;
- selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom);
- gl_rect_2d(selection_rect, selection_color);
- }
+ selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom);
+ gl_rect_2d(selection_rect, selection_color);
}
}
+}
void LLTextBase::drawCursor()
{
@@ -962,17 +962,18 @@ void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent)
{
if (width != getRect().getWidth() || height != getRect().getHeight())
{
- //EXT-4288
- //to keep consistance scrolling behaviour
- //when scrolling from top and from bottom...
- bool is_scrolled_to_end = (mScroller!=NULL) && scrolledToEnd();
+ bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false;
LLUICtrl::reshape( width, height, called_from_parent );
- if (is_scrolled_to_end)
+ if (mScroller && scrolled_to_bottom && mTrackEnd)
{
- deselect();
- endOfDoc();
+ // keep bottom of text buffer visible
+ // do this here as well as in reflow to handle case
+ // where shrinking from top, which causes buffer to temporarily
+ // not be scrolled to the bottom, since the scroll index
+ // specified the _top_ of the visible document region
+ mScroller->goToBottom();
}
// do this first after reshape, because other things depend on
@@ -1116,6 +1117,34 @@ void LLTextBase::reflow()
updateSegments();
+ if (mReflowIndex == S32_MAX)
+ {
+ return;
+ }
+
+ bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false;
+
+ LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
+ bool follow_selection = getLocalRect().overlaps(cursor_rect); // cursor is (potentially) visible
+
+ // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing
+ cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop;
+ cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom;
+
+ S32 first_line = getFirstVisibleLine();
+
+ // if scroll anchor not on first line, update it to first character of first line
+ if (!mLineInfoList.empty()
+ && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart
+ || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
+ {
+ mScrollIndex = mLineInfoList[first_line].mDocIndexStart;
+ }
+ LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex);
+ // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing
+ first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop;
+ first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom;
+
S32 reflow_count = 0;
while(mReflowIndex < S32_MAX)
{
@@ -1129,6 +1158,7 @@ void LLTextBase::reflow()
lldebugs << "Breaking out of reflow due to possible infinite loop in " << getName() << llendl;
break;
}
+
S32 start_index = mReflowIndex;
mReflowIndex = S32_MAX;
@@ -1136,25 +1166,6 @@ void LLTextBase::reflow()
// to force inlined widgets with follows set to shrink
mDocumentView->reshape(mVisibleTextRect.getWidth(), mDocumentView->getRect().getHeight());
- bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false;
-
- LLRect old_cursor_rect = getLocalRectFromDocIndex(mCursorPos);
- bool follow_selection = mVisibleTextRect.overlaps(old_cursor_rect); // cursor is visible
- old_cursor_rect.translate(-mVisibleTextRect.mLeft, -mVisibleTextRect.mBottom);
-
- S32 first_line = getFirstVisibleLine();
-
- // if scroll anchor not on first line, update it to first character of first line
- if (!mLineInfoList.empty()
- && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart
- || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
- {
- mScrollIndex = mLineInfoList[first_line].mDocIndexStart;
- }
- LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex);
- // subtract off effect of horizontal scrollbar from local position of first char
- first_char_rect.translate(-mVisibleTextRect.mLeft, -mVisibleTextRect.mBottom);
-
S32 cur_top = 0;
segment_set_t::iterator seg_iter = mSegments.begin();
@@ -1275,6 +1286,7 @@ void LLTextBase::reflow()
segmentp->updateLayout(*this);
}
+ }
// apply scroll constraints after reflowing text
if (!hasMouseCapture() && mScroller)
@@ -1288,20 +1300,29 @@ void LLTextBase::reflow()
{
// keep cursor in same vertical position on screen when selecting text
LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos);
+ LLRect old_cursor_rect = cursor_rect;
+ old_cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop;
+ old_cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom;
+
mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect);
}
else
{
// keep first line of text visible
LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex);
- mScroller->scrollToShowRect(new_first_char_rect, first_char_rect);
+
+ // pass in desired rect in the coordinate frame of the document viewport
+ LLRect old_first_char_rect = first_char_rect;
+ old_first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop;
+ old_first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom;
+
+ mScroller->scrollToShowRect(new_first_char_rect, old_first_char_rect);
}
}
// reset desired x cursor position
updateCursorXPos();
}
-}
LLRect LLTextBase::getTextBoundingRect()
{
@@ -1576,7 +1597,7 @@ static LLUIImagePtr image_from_icon_name(const std::string& icon_name)
}
}
-void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
+void LLTextBase::appendTextImpl(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
{
LLStyle::Params style_params(input_params);
style_params.fillFrom(getDefaultStyleParams());
@@ -1609,8 +1630,7 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
part = (S32)LLTextParser::MIDDLE;
}
std::string subtext=text.substr(0,start);
- appendAndHighlightText(subtext, prepend_newline, part, style_params);
- prepend_newline = false;
+ appendAndHighlightTextImpl(subtext, part, style_params);
}
// output an optional icon before the Url
@@ -1627,13 +1647,12 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
// Text will be replaced during rendering with the icon,
// but string cannot be empty or the segment won't be
// added (or drawn).
- appendAndHighlightText(" ", prepend_newline, part, icon_params);
- prepend_newline = false;
+ appendImageSegment(part, icon);
}
}
- // output the styled Url
- appendAndHighlightText(match.getLabel(), prepend_newline, part, link_params);
+ // output the styled Url
+ appendAndHighlightTextImpl(match.getLabel(), part, link_params);
// set the tooltip for the Url label
if (! match.getTooltip().empty())
@@ -1645,8 +1664,6 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
segment->setToolTip(match.getTooltip());
}
}
-
- prepend_newline = false;
// move on to the rest of the text after the Url
if (end < (S32)text.length())
@@ -1660,13 +1677,41 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
break;
}
}
- if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END;
- if (end < (S32)text.length()) appendAndHighlightText(text, prepend_newline, part, style_params);
+ if (part != (S32)LLTextParser::WHOLE)
+ part=(S32)LLTextParser::END;
+ if (end < (S32)text.length())
+ appendAndHighlightTextImpl(text, part, style_params);
}
else
{
- appendAndHighlightText(new_text, prepend_newline, part, style_params);
+ appendAndHighlightTextImpl(new_text, part, style_params);
+ }
+}
+
+void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
+{
+ if (new_text.empty())
+ return;
+
+ if(prepend_newline)
+ appendLineBreakSegment(input_params);
+ std::string::size_type start = 0;
+ std::string::size_type pos = new_text.find("\n",start);
+
+ while(pos!=-1)
+ {
+ if(pos!=start)
+ {
+ std::string str = std::string(new_text,start,pos-start);
+ appendTextImpl(str,input_params);
+ }
+ appendLineBreakSegment(input_params);
+ start = pos+1;
+ pos = new_text.find("\n",start);
}
+
+ std::string str = std::string(new_text,start,new_text.length()-start);
+ appendTextImpl(str,input_params);
}
void LLTextBase::needsReflow(S32 index)
@@ -1675,10 +1720,28 @@ void LLTextBase::needsReflow(S32 index)
mReflowIndex = llmin(mReflowIndex, index);
}
-void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& style_params)
+void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params)
{
- if (new_text.empty()) return;
+ segment_vec_t segments;
+ LLStyleConstSP sp(new LLStyle(style_params));
+ segments.push_back(new LLLineBreakTextSegment(sp, getLength()));
+
+ insertStringNoUndo(getLength(), utf8str_to_wstring("\n"), &segments);
+}
+
+void LLTextBase::appendImageSegment(S32 highlight_part, const LLStyle::Params& style_params)
+{
+ segment_vec_t segments;
+ LLStyleConstSP sp(new LLStyle(style_params));
+ segments.push_back(new LLImageTextSegment(sp, getLength(),*this));
+
+ insertStringNoUndo(getLength(), utf8str_to_wstring(" "), &segments);
+}
+
+
+void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params)
+{
// Save old state
S32 selection_start = mSelectionStart;
S32 selection_end = mSelectionEnd;
@@ -1691,13 +1754,11 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen
setCursorPos(old_length);
- LLTextParser* highlight = LLTextParser::getInstance();
-
- if (mParseHighlights && highlight)
+ if (mParseHighlights)
{
LLStyle::Params highlight_params(style_params);
- LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part);
+ LLSD pieces = LLTextParser::instance().parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part);
for (S32 i = 0; i < pieces.size(); i++)
{
LLSD color_llsd = pieces[i]["color"];
@@ -1706,14 +1767,8 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen
highlight_params.color = lcolor;
LLWString wide_text;
- if (prepend_newline && (i == 0 || pieces.size() <= 1 ))
- {
- wide_text = utf8str_to_wstring(std::string("\n") + pieces[i]["text"].asString());
- }
- else
- {
wide_text = utf8str_to_wstring(pieces[i]["text"].asString());
- }
+
S32 cur_length = getLength();
LLStyleConstSP sp(new LLStyle(highlight_params));
LLTextSegmentPtr segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this);
@@ -1725,17 +1780,7 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen
else
{
LLWString wide_text;
-
- // Add carriage return if not first line
- if (getLength() != 0
- && prepend_newline)
- {
- wide_text = utf8str_to_wstring(std::string("\n") + new_text);
- }
- else
- {
wide_text = utf8str_to_wstring(new_text);
- }
segment_vec_t segments;
S32 segment_start = old_length;
@@ -1763,11 +1808,32 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen
{
setCursorPos(cursor_pos);
}
+}
- //if( !allow_undo )
- //{
- // blockUndo();
- //}
+void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& style_params)
+{
+ if (new_text.empty()) return;
+
+ if(prepend_newline)
+ appendLineBreakSegment(style_params);
+
+ std::string::size_type start = 0;
+ std::string::size_type pos = new_text.find("\n",start);
+
+ while(pos!=-1)
+ {
+ if(pos!=start)
+ {
+ std::string str = std::string(new_text,start,pos-start);
+ appendAndHighlightTextImpl(str,highlight_part, style_params);
+ }
+ appendLineBreakSegment(style_params);
+ start = pos+1;
+ pos = new_text.find("\n",start);
+ }
+
+ std::string str = std::string(new_text,start,new_text.length()-start);
+ appendAndHighlightTextImpl(str,highlight_part, style_params);
}
@@ -1876,14 +1942,19 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,
S32 text_width, text_height;
bool newline = segmentp->getDimensions(line_seg_offset, segment_line_length, text_width, text_height);
+ if(newline)
+ {
+ pos = segment_line_start + segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round);
+ break;
+ }
+
// if we've reached a line of text *below* the mouse cursor, doc index is first character on that line
if (hit_past_end_of_line && local_y - mVisibleTextRect.mBottom + visible_region.mBottom > line_iter->mRect.mTop)
{
pos = segment_line_start;
break;
}
- if (local_x < start_x + text_width // cursor to left of right edge of text
- || newline) // or this line ends with a newline, set doc pos to newline char
+ if (local_x < start_x + text_width) // cursor to left of right edge of text
{
// Figure out which character we're nearest to.
S32 offset;
@@ -1907,13 +1978,13 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,
pos = segment_line_start + offset;
break;
}
- else if (hit_past_end_of_line && segmentp->getEnd() >= line_iter->mDocIndexEnd - 1)
+ else if (hit_past_end_of_line && segmentp->getEnd() > line_iter->mDocIndexEnd - 1)
{
+ // segment wraps to next line, so just set doc pos to the end of the line
// segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd)
pos = llmin(getLength(), line_iter->mDocIndexEnd);
break;
}
-
start_x += text_width;
}
@@ -1982,11 +2053,18 @@ LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const
LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const
{
+ LLRect content_window_rect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
+ if (mBorderVisible)
+ {
+ content_window_rect.stretch(-1);
+ }
+
LLRect local_rect;
+
if (mLineInfoList.empty())
{
// return default height rect in upper left
- local_rect = mVisibleTextRect;
+ local_rect = content_window_rect;
local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight());
return local_rect;
}
@@ -1997,8 +2075,8 @@ LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const
// compensate for scrolled, inset view of doc
LLRect scrolled_view_rect = getVisibleDocumentRect();
local_rect = doc_rect;
- local_rect.translate(mVisibleTextRect.mLeft - scrolled_view_rect.mLeft,
- mVisibleTextRect.mBottom - scrolled_view_rect.mBottom);
+ local_rect.translate(content_window_rect.mLeft - scrolled_view_rect.mLeft,
+ content_window_rect.mBottom - scrolled_view_rect.mBottom);
return local_rect;
}
@@ -2370,25 +2448,6 @@ F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selec
{
if( end - start > 0 )
{
- if ( mStyle->isImage() && (start >= 0) && (end <= mEnd - mStart))
- {
- // ...for images, only render the image, not the underlying text,
- // which is only a placeholder space
- LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha;
- LLUIImagePtr image = mStyle->getImage();
- S32 style_image_height = image->getHeight();
- S32 style_image_width = image->getWidth();
- // Text is drawn from the top of the draw_rect downward
- S32 text_center = draw_rect.mTop - (mFontHeight / 2);
- // Align image to center of text
- S32 image_bottom = text_center - (style_image_height / 2);
- image->draw(draw_rect.mLeft, image_bottom,
- style_image_width, style_image_height, color);
-
- const S32 IMAGE_HPAD = 3;
- return draw_rect.mLeft + style_image_width + IMAGE_HPAD;
- }
-
return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect);
}
return draw_rect.mLeft;
@@ -2401,11 +2460,6 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
const LLWString &text = mEditor.getWText();
- if ( text[seg_end-1] == '\n' )
- {
- --seg_end;
- }
-
F32 right_x = rect.mLeft;
if (!mStyle->isVisible())
{
@@ -2564,33 +2618,14 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt
{
height = 0;
width = 0;
- bool force_newline = false;
if (num_chars > 0)
{
height = mFontHeight;
const LLWString &text = mEditor.getWText();
// if last character is a newline, then return true, forcing line break
- llwchar last_char = text[mStart + first_char + num_chars - 1];
- if (last_char == '\n')
- {
- force_newline = true;
- // don't count newline in font width
- width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars - 1);
- }
- else
- {
width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars);
}
- }
-
- LLUIImagePtr image = mStyle->getImage();
- if( image.notNull())
- {
- width += image->getWidth();
- height = llmax(height, image->getHeight());
- }
-
- return force_newline;
+ return false;
}
S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const
@@ -2613,15 +2648,7 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin
num_pixels = llmax(0, num_pixels - image->getWidth());
}
- // search for newline and if found, truncate there
- S32 last_char = mStart + segment_offset;
- for (; last_char != mEnd; ++last_char)
- {
- if (text[last_char] == '\n')
- {
- break;
- }
- }
+ S32 last_char = mEnd;
// set max characters to length of segment, or to first newline
max_chars = llmin(max_chars, last_char - (mStart + segment_offset));
@@ -2649,8 +2676,7 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin
S32 last_char_in_run = mStart + segment_offset + num_chars;
// check length first to avoid indexing off end of string
if (last_char_in_run < mEnd
- && (last_char_in_run >= mEditor.getLength()
- || text[last_char_in_run] == '\n'))
+ && (last_char_in_run >= mEditor.getLength() ))
{
num_chars++;
}
@@ -2745,3 +2771,87 @@ void LLInlineViewSegment::linkToDocument(LLTextBase* editor)
{
editor->addDocumentChild(mView);
}
+
+LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1)
+{
+ mFontHeight = llceil(style->getFont()->getLineHeight());
+}
+LLLineBreakTextSegment::~LLLineBreakTextSegment()
+{
+}
+bool LLLineBreakTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
+{
+ width = 0;
+ height = mFontHeight;
+
+ return true;
+}
+S32 LLLineBreakTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
+{
+ return 1;
+}
+F32 LLLineBreakTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
+{
+ return draw_rect.mLeft;
+}
+
+LLImageTextSegment::LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor)
+ :LLTextSegment(pos,pos+1)
+ ,mStyle( style )
+ ,mEditor(editor)
+{
+}
+
+LLImageTextSegment::~LLImageTextSegment()
+{
+}
+
+static const S32 IMAGE_HPAD = 3;
+
+bool LLImageTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
+{
+ width = 0;
+ height = llceil(mStyle->getFont()->getLineHeight());;
+
+ LLUIImagePtr image = mStyle->getImage();
+ if( image.notNull())
+ {
+ width += image->getWidth() + IMAGE_HPAD;
+ height = llmax(height, image->getHeight() + IMAGE_HPAD );
+ }
+ return false;
+}
+
+S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
+{
+ LLUIImagePtr image = mStyle->getImage();
+ S32 image_width = image->getWidth();
+ if(num_pixels>image_width + IMAGE_HPAD)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
+{
+ if ( (start >= 0) && (end <= mEnd - mStart))
+ {
+ LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha;
+ LLUIImagePtr image = mStyle->getImage();
+ S32 style_image_height = image->getHeight();
+ S32 style_image_width = image->getWidth();
+ // Text is drawn from the top of the draw_rect downward
+
+ S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2);
+ // Align image to center of draw rect
+ S32 image_bottom = text_center - (style_image_height / 2);
+ image->draw(draw_rect.mLeft, image_bottom,
+ style_image_width, style_image_height, color);
+
+ const S32 IMAGE_HPAD = 3;
+ return draw_rect.mLeft + style_image_width + IMAGE_HPAD;
+ }
+ return 0.0;
+}
+