diff options
Diffstat (limited to 'indra/llui')
-rw-r--r-- | indra/llui/llaccordionctrl.cpp | 24 | ||||
-rw-r--r-- | indra/llui/llaccordionctrl.h | 1 | ||||
-rw-r--r-- | indra/llui/llaccordionctrltab.cpp | 29 | ||||
-rw-r--r-- | indra/llui/llaccordionctrltab.h | 7 | ||||
-rw-r--r-- | indra/llui/llbutton.cpp | 30 | ||||
-rw-r--r-- | indra/llui/llbutton.h | 7 | ||||
-rw-r--r-- | indra/llui/lltabcontainer.cpp | 8 | ||||
-rw-r--r-- | indra/llui/lltabcontainer.h | 5 | ||||
-rw-r--r-- | indra/llui/lltextbase.cpp | 282 | ||||
-rw-r--r-- | indra/llui/lltextbase.h | 34 | ||||
-rw-r--r-- | indra/llui/lltexteditor.cpp | 49 | ||||
-rw-r--r-- | indra/llui/lltexteditor.h | 1 | ||||
-rw-r--r-- | indra/llui/llurlentry.h | 2 | ||||
-rw-r--r-- | indra/llui/llurlmatch.cpp | 3 | ||||
-rw-r--r-- | indra/llui/llurlmatch.h | 7 | ||||
-rw-r--r-- | indra/llui/llurlregistry.cpp | 6 | ||||
-rw-r--r-- | indra/llui/tests/llurlmatch_test.cpp | 30 |
17 files changed, 394 insertions, 131 deletions
diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index 5d1d57cbb2..8e0245c451 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -65,6 +65,7 @@ LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params) , mFitParent(params.fit_parent) , mAutoScrolling( false ) , mAutoScrollRate( 0.f ) + , mSelectedTab( NULL ) { mSingleExpansion = params.single_expansion; if(mFitParent && !mSingleExpansion) @@ -76,6 +77,7 @@ LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params) LLAccordionCtrl::LLAccordionCtrl() : LLPanel() , mAutoScrolling( false ) , mAutoScrollRate( 0.f ) + , mSelectedTab( NULL ) { mSingleExpansion = false; mFitParent = false; @@ -689,6 +691,28 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info) } return 0; } + else if(str_action == "select_current") + { + for(size_t i=0;i<mAccordionTabs.size();++i) + { + // Set selection to the currently focused tab. + if(mAccordionTabs[i]->hasFocus()) + { + if (mAccordionTabs[i] != mSelectedTab) + { + if (mSelectedTab) + { + mSelectedTab->setSelected(false); + } + mSelectedTab = mAccordionTabs[i]; + mSelectedTab->setSelected(true); + } + + return 1; + } + } + return 0; + } } else if (info.has("scrollToShowRect")) { diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h index ab7d6548ca..a029201c90 100644 --- a/indra/llui/llaccordionctrl.h +++ b/indra/llui/llaccordionctrl.h @@ -130,6 +130,7 @@ private: bool mFitParent; bool mAutoScrolling; F32 mAutoScrollRate; + LLAccordionCtrlTab* mSelectedTab; }; diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index b09c108ec3..83e67980a3 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -76,6 +76,8 @@ public: std::string getTitle(); void setTitle(const std::string& title, const std::string& hl); + void setSelected(bool is_selected) { mIsSelected = is_selected; } + virtual void onMouseEnter(S32 x, S32 y, MASK mask); virtual void onMouseLeave(S32 x, S32 y, MASK mask); virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); @@ -103,6 +105,7 @@ private: LLUIColor mHeaderBGColor; bool mNeedsHighlight; + bool mIsSelected; LLFrameTimer mAutoOpenTimer; }; @@ -115,7 +118,8 @@ LLAccordionCtrlTab::LLAccordionCtrlTabHeader::LLAccordionCtrlTabHeader( const LLAccordionCtrlTabHeader::Params& p) : LLUICtrl(p) , mHeaderBGColor(p.header_bg_color()) -,mNeedsHighlight(false), +, mNeedsHighlight(false) +, mIsSelected(false), mImageCollapsed(p.header_collapse_img), mImageCollapsedPressed(p.header_collapse_img_pressed), mImageExpanded(p.header_expand_img), @@ -187,7 +191,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() // Only show green "focus" background image if the accordion is open, // because the user's mental model of focus is that it goes away after // the accordion is closed. - if (getParent()->hasFocus() + if (getParent()->hasFocus() || mIsSelected /*&& !(collapsible && !expanded)*/ // WHY?? ) { @@ -301,6 +305,7 @@ LLAccordionCtrlTab::Params::Params() ,header_image_focused("header_image_focused") ,header_text_color("header_text_color") ,fit_panel("fit_panel",true) + ,selection_enabled("selection_enabled", false) { mouse_opaque(false); } @@ -331,6 +336,11 @@ LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p) mHeader = LLUICtrlFactory::create<LLAccordionCtrlTabHeader>(headerParams); addChild(mHeader, 1); + if (p.selection_enabled) + { + LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLAccordionCtrlTab::selectOnFocusReceived, this)); + } + reshape(100, 200,FALSE); } @@ -498,6 +508,15 @@ boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus return boost::signals2::connection(); } +void LLAccordionCtrlTab::setSelected(bool is_selected) +{ + LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); + if (header) + { + header->setSelected(is_selected); + } +} + LLView* LLAccordionCtrlTab::findContainerView() { for(child_list_const_iter_t it = getChildList()->begin(); @@ -513,6 +532,11 @@ LLView* LLAccordionCtrlTab::findContainerView() return NULL; } +void LLAccordionCtrlTab::selectOnFocusReceived() +{ + if (getParent()) // A parent may not be set if tabs are added dynamically. + getParent()->notifyParent(LLSD().with("action", "select_current")); +} S32 LLAccordionCtrlTab::getHeaderHeight() { @@ -713,6 +737,7 @@ void LLAccordionCtrlTab::showAndFocusHeader() { LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); header->setFocus(true); + header->setSelected(true); LLRect screen_rc; LLRect selected_rc = header->getRect(); diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h index f5b7fd0af6..83a9024a74 100644 --- a/indra/llui/llaccordionctrltab.h +++ b/indra/llui/llaccordionctrltab.h @@ -88,6 +88,8 @@ public: Optional<bool> fit_panel; + Optional<bool> selection_enabled; + Optional<S32> padding_left; Optional<S32> padding_right; Optional<S32> padding_top; @@ -121,6 +123,8 @@ public: boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb); boost::signals2::connection setFocusLostCallback(const focus_signal_t::slot_type& cb); + void setSelected(bool is_selected); + bool getCollapsible() {return mCollapsible;}; void setCollapsible(bool collapsible) {mCollapsible = collapsible;}; @@ -199,6 +203,9 @@ protected: void drawChild(const LLRect& root_rect,LLView* child); LLView* findContainerView (); + + void selectOnFocusReceived(); + private: class LLAccordionCtrlTabHeader; diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 0255061b12..34f3049f2e 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -128,6 +128,7 @@ LLButton::LLButton(const LLButton::Params& p) mImageSelected(p.image_selected), mImageDisabled(p.image_disabled), mImageDisabledSelected(p.image_disabled_selected), + mImageFlash(p.image_flash), mImagePressed(p.image_pressed), mImagePressedSelected(p.image_pressed_selected), mImageHoverSelected(p.image_hover_selected), @@ -635,14 +636,24 @@ void LLButton::draw() if (mFlashing) { - LLColor4 flash_color = mFlashBgColor.get(); - use_glow_effect = TRUE; - glow_type = LLRender::BT_ALPHA; // blend the glow - - if (mNeedsHighlight) // highlighted AND flashing - glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity + // if button should flash and we have icon for flashing, use it as image for button + if(flash && mImageFlash) + { + // setting flash to false to avoid its further influence on glow + flash = false; + imagep = mImageFlash; + } + // else use usual flashing via flash_color else - glow_color = flash_color; + { + LLColor4 flash_color = mFlashBgColor.get(); + use_glow_effect = TRUE; + glow_type = LLRender::BT_ALPHA; // blend the glow + if (mNeedsHighlight) // highlighted AND flashing + glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity + else + glow_color = flash_color; + } } if (mNeedsHighlight && !imagep) @@ -1018,6 +1029,11 @@ void LLButton::setImageHoverUnselected(LLPointer<LLUIImage> image) mImageHoverUnselected = image; } +void LLButton::setImageFlash(LLPointer<LLUIImage> image) +{ + mImageFlash = image; +} + void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment, const LLColor4& color) { if (image_name.empty()) diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index a4d81ed6c3..9bd566d3c9 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -84,6 +84,7 @@ public: image_hover_unselected, image_disabled_selected, image_disabled, + image_flash, image_pressed, image_pressed_selected, image_overlay; @@ -246,6 +247,7 @@ public: void setImageHoverUnselected(LLPointer<LLUIImage> image); void setImageDisabled(LLPointer<LLUIImage> image); void setImageDisabledSelected(LLPointer<LLUIImage> image); + void setImageFlash(LLPointer<LLUIImage> image); void setImagePressed(LLPointer<LLUIImage> image); void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; } @@ -310,6 +312,11 @@ private: LLPointer<LLUIImage> mImagePressed; LLPointer<LLUIImage> mImagePressedSelected; + /* There are two ways an image can flash- by making changes in color according to flash_color attribute + or by changing icon from current to the one specified in image_flash. Second way is used only if + flash icon name is set in attributes(by default it isn't). First way is used otherwise. */ + LLPointer<LLUIImage> mImageFlash; + LLUIColor mHighlightColor; LLUIColor mFlashBgColor; diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 30fc7babae..986cfe75a1 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -197,10 +197,13 @@ static LLDefaultChildRegistry::Register<LLTabContainer> r2("tab_container"); LLTabContainer::TabParams::TabParams() : tab_top_image_unselected("tab_top_image_unselected"), tab_top_image_selected("tab_top_image_selected"), + tab_top_image_flash("tab_top_image_flash"), tab_bottom_image_unselected("tab_bottom_image_unselected"), tab_bottom_image_selected("tab_bottom_image_selected"), + tab_bottom_image_flash("tab_bottom_image_flash"), tab_left_image_unselected("tab_left_image_unselected"), - tab_left_image_selected("tab_left_image_selected") + tab_left_image_selected("tab_left_image_selected"), + tab_left_image_flash("tab_left_image_flash") {} LLTabContainer::Params::Params() @@ -879,16 +882,19 @@ void LLTabContainer::update_images(LLTabTuple* tuple, TabParams params, LLTabCon { tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_top_image_unselected)); tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_top_image_selected)); + tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_top_image_flash)); } else if (pos == LLTabContainer::BOTTOM) { tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_bottom_image_unselected)); tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_bottom_image_selected)); + tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_bottom_image_flash)); } else if (pos == LLTabContainer::LEFT) { tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_left_image_unselected)); tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_left_image_selected)); + tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_left_image_flash)); } } } diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 50ec2679f6..a2dc15aaf9 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -67,10 +67,13 @@ public: { Optional<LLUIImage*> tab_top_image_unselected, tab_top_image_selected, + tab_top_image_flash, tab_bottom_image_unselected, tab_bottom_image_selected, + tab_bottom_image_flash, tab_left_image_unselected, - tab_left_image_selected; + tab_left_image_selected, + tab_left_image_flash; TabParams(); }; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 78a6ab1eaa..ab9cd11769 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1562,7 +1562,7 @@ std::string LLTextBase::getText() const return getViewModel()->getValue().asString(); } -void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) +void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params) { LLStyle::Params style_params(input_params); style_params.fillFrom(getDefaultStyleParams()); @@ -1598,8 +1598,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 @@ -1613,19 +1612,18 @@ 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); - prepend_newline = false; + appendImageSegment(part, icon); } } // output the styled Url (unless we've been asked to suppress hyperlinking) if (match.isLinkDisabled()) { - appendAndHighlightText(match.getLabel(), prepend_newline, part, style_params); + appendAndHighlightTextImpl(match.getLabel(), part, style_params); } else { - appendAndHighlightText(match.getLabel(), prepend_newline, part, link_params); + appendAndHighlightTextImpl(match.getLabel(), part, link_params); // set the tooltip for the Url label if (! match.getTooltip().empty()) @@ -1638,8 +1636,6 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c } } } - prepend_newline = false; - // move on to the rest of the text after the Url if (end < (S32)text.length()) { @@ -1652,13 +1648,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) @@ -1667,10 +1691,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; @@ -1698,14 +1740,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()); - } + 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); @@ -1717,17 +1753,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); - } + wide_text = utf8str_to_wstring(new_text); segment_vec_t segments; S32 segment_start = old_length; @@ -1755,11 +1781,32 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen { setCursorPos(cursor_pos); } +} + +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); + } - //if( !allow_undo ) - //{ - // blockUndo(); - //} + std::string str = std::string(new_text,start,new_text.length()-start); + appendAndHighlightTextImpl(str,highlight_part, style_params); } @@ -1852,14 +1899,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; @@ -1883,13 +1935,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 start of next line (represented by mDocIndexEnd) - pos = llmin(getLength(), line_iter->mDocIndexEnd); + // 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; } @@ -2346,25 +2398,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; @@ -2377,11 +2410,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()) { @@ -2540,33 +2568,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()); + width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); } - - return force_newline; + return false; } S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const @@ -2589,15 +2598,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)); @@ -2625,8 +2626,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++; } @@ -2721,3 +2721,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; +} + diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 8ed0680df9..176308c61a 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -316,6 +316,13 @@ protected: void needsScroll() { mScrollNeeded = TRUE; } void replaceUrlLabel(const std::string &url, const std::string &label); + void appendLineBreakSegment(const LLStyle::Params& style_params); + void appendImageSegment(S32 highlight_part, const LLStyle::Params& style_params); + + void appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params = LLStyle::Params()); + void appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params); + + protected: // text segmentation and flow segment_set_t mSegments; @@ -507,5 +514,32 @@ private: bool mForceNewLine; }; +class LLLineBreakTextSegment : public LLTextSegment +{ +public: + + LLLineBreakTextSegment(LLStyleConstSP style,S32 pos); + ~LLLineBreakTextSegment(); + bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; + S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; + F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); + +private: + S32 mFontHeight; +}; + +class LLImageTextSegment : public LLTextSegment +{ +public: + LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor); + ~LLImageTextSegment(); + bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; + S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; + F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); + +private: + class LLTextBase& mEditor; + LLStyleConstSP mStyle; +}; #endif diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 4fd62045e8..c9474d66b7 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1083,6 +1083,28 @@ void LLTextEditor::addChar(llwchar wc) setCursorPos(mCursorPos + addChar( mCursorPos, wc )); } +void LLTextEditor::addLineBreakChar() +{ + if( !getEnabled() ) + { + return; + } + if( hasSelection() ) + { + deleteSelection(TRUE); + } + else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + removeChar(mCursorPos); + } + + LLStyleConstSP sp(new LLStyle(LLStyle::Params())); + LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos); + + S32 pos = execute(new TextCmdAddChar(mCursorPos, FALSE, '\n', segment)); + + setCursorPos(mCursorPos + pos); +} BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) @@ -1404,7 +1426,27 @@ void LLTextEditor::pasteHelper(bool is_primary) } // Insert the new text into the existing text. - setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE, LLTextSegmentPtr())); + + //paste text with linebreaks. + std::basic_string<llwchar>::size_type start = 0; + std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start); + + while(pos!=-1) + { + if(pos!=start) + { + std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,pos-start); + setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr())); + } + addLineBreakChar(); + + start = pos+1; + pos = clean_string.find('\n',start); + } + + std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,clean_string.length()-start); + setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr())); + deselect(); onKeyStroke(); @@ -2169,7 +2211,10 @@ void LLTextEditor::autoIndent() } // Insert that number of spaces on the new line - addChar( '\n' ); + + //appendLineBreakSegment(LLStyle::Params());//addChar( '\n' ); + addLineBreakChar(); + for( i = 0; i < space_count; i++ ) { addChar( ' ' ); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 9b3ab9414c..7b68974fd8 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -240,6 +240,7 @@ protected: // Undoable operations void addChar(llwchar c); // at mCursorPos S32 addChar(S32 pos, llwchar wc); + void addLineBreakChar(); S32 overwriteChar(S32 pos, llwchar wc); void removeChar(); S32 removeChar(S32 pos); diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 71f030677a..3c21fe8d61 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -94,6 +94,8 @@ public: /// is this a match for a URL that should not be hyperlinked? bool isLinkDisabled() const { return mDisabledLink; } + virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } + protected: std::string getIDStringFromUrl(const std::string &url) const; std::string escapeUrl(const std::string &url) const; diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp index 72a199c220..7c96665ce4 100644 --- a/indra/llui/llurlmatch.cpp +++ b/indra/llui/llurlmatch.cpp @@ -51,7 +51,7 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, const std::string &label, const std::string &tooltip, const std::string &icon, const LLUIColor& color, const std::string &menu, const std::string &location, - bool disabled_link) + bool disabled_link, const LLUUID& id) { mStart = start; mEnd = end; @@ -63,4 +63,5 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, mMenuName = menu; mLocation = location; mDisabledLink = disabled_link; + mID = id; } diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h index e86762548b..78dd2c528f 100644 --- a/indra/llui/llurlmatch.h +++ b/indra/llui/llurlmatch.h @@ -90,7 +90,10 @@ public: void setValues(U32 start, U32 end, const std::string &url, const std::string &label, const std::string &tooltip, const std::string &icon, const LLUIColor& color, const std::string &menu, - const std::string &location, bool disabled_link); + const std::string &location, bool disabled_link + , const LLUUID& id ); + + const LLUUID& getID() const { return mID;} private: U32 mStart; @@ -101,6 +104,8 @@ private: std::string mIcon; std::string mMenuName; std::string mLocation; + + LLUUID mID; LLUIColor mColor; bool mDisabledLink; }; diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 4341286eb4..1f86f72faa 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -183,7 +183,8 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL match_entry->getColor(), match_entry->getMenuName(), match_entry->getLocation(url), - match_entry->isLinkDisabled()); + match_entry->isLinkDisabled(), + match_entry->getID(url)); return true; } @@ -217,7 +218,8 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr match.getColor(), match.getMenuName(), match.getLocation(), - match.isLinkDisabled()); + match.isLinkDisabled(), + match.getID()); return true; } return false; diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index 24a32de268..06b850d233 100644 --- a/indra/llui/tests/llurlmatch_test.cpp +++ b/indra/llui/tests/llurlmatch_test.cpp @@ -54,7 +54,7 @@ namespace tut LLUrlMatch match; ensure("empty()", match.empty()); - match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "", false); + match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure("! empty()", ! match.empty()); } @@ -67,7 +67,7 @@ namespace tut LLUrlMatch match; ensure_equals("getStart() == 0", match.getStart(), 0); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getStart() == 10", match.getStart(), 10); } @@ -80,7 +80,7 @@ namespace tut LLUrlMatch match; ensure_equals("getEnd() == 0", match.getEnd(), 0); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getEnd() == 20", match.getEnd(), 20); } @@ -93,10 +93,10 @@ namespace tut LLUrlMatch match; ensure_equals("getUrl() == ''", match.getUrl(), ""); - match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getUrl() == '' (2)", match.getUrl(), ""); } @@ -109,10 +109,10 @@ namespace tut LLUrlMatch match; ensure_equals("getLabel() == ''", match.getLabel(), ""); - match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getLabel() == '' (2)", match.getLabel(), ""); } @@ -125,10 +125,10 @@ namespace tut LLUrlMatch match; ensure_equals("getTooltip() == ''", match.getTooltip(), ""); - match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getTooltip() == '' (2)", match.getTooltip(), ""); } @@ -141,10 +141,10 @@ namespace tut LLUrlMatch match; ensure_equals("getIcon() == ''", match.getIcon(), ""); - match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure_equals("getIcon() == '' (2)", match.getIcon(), ""); } @@ -157,10 +157,10 @@ namespace tut LLUrlMatch match; ensure("getMenuName() empty", match.getMenuName().empty()); - match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "", false); + match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "", false,LLUUID::null); ensure_equals("getMenuName() == \"xui_file.xml\"", match.getMenuName(), "xui_file.xml"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure("getMenuName() empty (2)", match.getMenuName().empty()); } @@ -173,10 +173,10 @@ namespace tut LLUrlMatch match; ensure("getLocation() empty", match.getLocation().empty()); - match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris", false); + match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris", false,LLUUID::null); ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); ensure("getLocation() empty (2)", match.getLocation().empty()); } } |