diff -r f7c57859c998 lib/irrlicht/include/IGUIListBox.h --- a/lib/irrlicht/include/IGUIListBox.h Wed Jun 03 23:57:48 2009 +0200 +++ b/lib/irrlicht/include/IGUIListBox.h Thu Jun 04 00:07:36 2009 +0200 @@ -170,6 +170,15 @@ //! Clear the selection flags for all items //! This works only in EGUI_LBS_MULTI_SELECTION mode virtual void clearAllItemMultiSelections() = 0; + + //! Enables or disables word wrap. + /** \param enable: If set to true, words going over one line are + broken to the next line. */ + virtual void setWordWrap(bool enable) = 0; + + //! Checks if word wrap is enabled + //! \return true if word wrap is enabled, false otherwise + virtual bool isWordWrapEnabled() const = 0; }; diff -r f7c57859c998 lib/irrlicht/source/Irrlicht/CGUIListBox.cpp --- a/lib/irrlicht/source/Irrlicht/CGUIListBox.cpp Wed Jun 03 23:57:48 2009 +0200 +++ b/lib/irrlicht/source/Irrlicht/CGUIListBox.cpp Thu Jun 04 00:07:36 2009 +0200 @@ -29,6 +29,7 @@ ScrollBar(0), selectTime(0), LastKeyTime(0), Selecting(false), DrawBack(drawBack), MoveOverSelect(moveOverSelect), AutoScroll(true), HighlightWhenNotFocused(true) , SelectionMode(EGUI_LBS_SINGLE_SELECTION) + , WordWrap(false), FontUsedToWrap(0) { #ifdef _DEBUG setDebugName("CGUIListBox"); @@ -68,6 +69,9 @@ if (Font) Font->drop(); + if ( FontUsedToWrap ) + FontUsedToWrap->drop(); + if (IconBank) IconBank->drop(); } @@ -165,7 +169,19 @@ } } - TotalItemHeight = ItemHeight * Items.size(); + if ( WordWrap ) + { + TotalItemHeight = 0; + for ( u32 i=0; i < Items.size(); ++i ) + { + TotalItemHeight += Items[i].WrappedText.size() * ItemHeight; + } + } + else + { + TotalItemHeight = ItemHeight * Items.size(); + } + ScrollBar->setMax(TotalItemHeight - AbsoluteRect.getHeight()); ScrollBar->setSmallStep ( 1 ); ScrollBar->setLargeStep ( ItemHeight ); @@ -447,7 +463,24 @@ // find new selected item. if (ItemHeight!=0) - Selected = ((ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + ScrollBar->getPos()) / ItemHeight; + { + s32 selectedLine = ((ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + ScrollBar->getPos()) / ItemHeight; + + Selected = selectedLine; + if ( WordWrap ) + { + s32 lines = 0; + for ( u32 i=0; i < Items.size(); ++i) + { + lines += Items[i].WrappedText.size(); + if ( lines > selectedLine ) + { + Selected = i; + break; + } + } + } + } // set the multiselection flag. The other stuff like active selected can just be ignored. if ( EGUI_LBS_MULTI_SELECTION == SelectionMode @@ -509,6 +542,7 @@ IGUIElement::updateAbsolutePosition(); recalculateItemHeight(); + wrapAllText(); } @@ -517,6 +551,11 @@ { if (!IsVisible) return; + + if ( FontUsedToWrap != Font && WordWrap ) + { + wrapAllText(); + } recalculateItemHeight(); // if the font changed @@ -557,7 +596,14 @@ bool highlightSelected = (SelectionMode != EGUI_LBS_MULTI_SELECTION && i == Selected && hl) || (SelectionMode == EGUI_LBS_MULTI_SELECTION && Items[i].IsMultiSelected ); - frameRect.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y + ItemHeight; + if ( WordWrap ) + { + frameRect.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y + ItemHeight * Items[i].WrappedText.size(); + } + else + { + frameRect.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y + ItemHeight; + } if (frameRect.LowerRightCorner.Y >= AbsoluteRect.UpperLeftCorner.Y && frameRect.UpperLeftCorner.Y <= AbsoluteRect.LowerRightCorner.Y) @@ -599,7 +645,19 @@ else textCol = hasItemOverrideColor(i, EGUI_LBC_TEXT) ? getItemOverrideColor(i, EGUI_LBC_TEXT) : getItemDefaultColor(EGUI_LBC_TEXT); - Font->draw(Items[i].text.c_str(), textRect, textCol, false, true, &clientClip); + if ( WordWrap ) + { + for ( u32 w=0; w < Items[i].WrappedText.size(); ++w ) + { + textRect.LowerRightCorner.Y = textRect.UpperLeftCorner.Y + ItemHeight; + Font->draw(Items[i].WrappedText[w].c_str(), textRect, textCol, false, true, &clientClip); + textRect.UpperLeftCorner.Y = textRect.LowerRightCorner.Y; + } + } + else + { + Font->draw(Items[i].text.c_str(), textRect, textCol, false, true, &clientClip); + } textRect.UpperLeftCorner.X -= ItemsIconWidth+3; } @@ -619,6 +677,9 @@ i.text = text; i.icon = icon; + if ( WordWrap ) + wrapText(i); + Items.push_back(i); recalculateItemHeight(); recalculateItemWidth(icon); @@ -643,7 +704,19 @@ if (!AutoScroll) return; - const s32 selPos = (Selected == -1 ? TotalItemHeight : Selected * ItemHeight) - ScrollBar->getPos(); + // height from top of list if the complete list would be visible + s32 selectedItemHeight = Selected < 0 ? 0 : Selected * ItemHeight; + if ( WordWrap && Selected >= 0 ) + { + selectedItemHeight = 0; + for ( s32 i=0; igetPos(); + if (selPos < 0) { @@ -707,6 +780,7 @@ out->addBool ("MoveOverSelect", MoveOverSelect); out->addBool ("AutoScroll", AutoScroll); out->addEnum ("SelectionMode", SelectionMode, GUIListBoxSelectionNames ); + out->addBool ("WordWrap", WordWrap); out->addInt("ItemCount", Items.size()); for (u32 i=0;igetAttributeAsBool("MoveOverSelect"); AutoScroll = in->getAttributeAsBool("AutoScroll"); SelectionMode = (EGUI_LISTBOX_SELECTION) in->getAttributeAsEnumeration("SelectionMode", GUIListBoxSelectionNames ); + WordWrap = in->getAttributeAsBool("WordWrap"); IGUIListBox::deserializeAttributes(in,options); @@ -773,6 +848,11 @@ } } } + + if ( WordWrap ) + { + wrapAllText(); + } } @@ -813,6 +893,9 @@ ListItem i; i.text = text; i.icon = icon; + + if ( WordWrap ) + wrapText(i); Items.insert(i, index); recalculateItemHeight(); @@ -990,6 +1073,159 @@ Items[i].IsMultiSelected = false; } +// This breakText is already a rather general text-wrapping and does not access any members +// TODO: This can't handle text without whitespaces. +// TODO: this is mostly copied from CGUIEditBox::breakText it _should_ rather be an own class. For example CSplitTextLines. +void CGUIListBox::breakText( const core::stringw &origText, s32 maxWidth, gui::IGUIFont* font, bool wordWrap, bool multiLine, core::array &brokenText, core::array &brokenTextPositions) +{ + if ((!wordWrap && !multiLine) || !font || maxWidth <= 0) + { + brokenText.push_back(origText); + return; + } + + brokenText.clear(); // need to reallocate :/ + brokenTextPositions.set_used(0); + + core::stringw text(origText); // we need that currently because of windows break handling :( + core::stringw line; + core::stringw word; + core::stringw whitespace; + s32 lastLineStart = 0; + s32 size = origText.size(); + s32 length = 0; + wchar_t c; + + for (s32 i=0; igetDimension(whitespace.c_str()).Width; + s32 worldlgth = font->getDimension(word.c_str()).Width; + + if (wordWrap && length + worldlgth + whitelgth > maxWidth) + { + // break to next line + length = worldlgth; + brokenText.push_back(line); + brokenTextPositions.push_back(lastLineStart); + lastLineStart = i - (s32)word.size(); + line = word; + } + else + { + // add word to line + line += whitespace; + line += word; + length += whitelgth + worldlgth; + } + + word = L""; + whitespace = L""; + } + + whitespace += c; + + // compute line break + if (lineBreak) + { + line += whitespace; + line += word; + brokenText.push_back(line); + brokenTextPositions.push_back(lastLineStart); + lastLineStart = i+1; + line = L""; + word = L""; + whitespace = L""; + length = 0; + } + } + else + { + // yippee this is a word.. + word += c; + } + } + + line += whitespace; + line += word; + brokenText.push_back(line); + brokenTextPositions.push_back(lastLineStart); +} + +// create wrapped text for all lines +void CGUIListBox::wrapAllText() +{ + for ( s32 i = 0; i < (s32)Items.size(); ++i ) + { + wrapText(Items[i]); + } +} + +void CGUIListBox::wrapText( ListItem & item ) +{ + if ( FontUsedToWrap != Font ) + { + if ( FontUsedToWrap ) + FontUsedToWrap->drop(); + FontUsedToWrap = Font; + FontUsedToWrap->grab(); + } + + core::array brokenTextPositions; // we don't need it here, just as param to the function + + // the reduction by 7 was calculated by adding the pixels by which the width is reduced in draw + s32 maxWidth = AbsoluteRect.getWidth()-(ItemsIconWidth+7); + if (ScrollBar->isVisible()) + maxWidth -= Environment->getSkin()->getSize(EGDS_SCROLLBAR_SIZE); + + breakText( item.text, maxWidth, FontUsedToWrap, WordWrap, /*bool multiLine*/ true, item.WrappedText, brokenTextPositions); +} + +void CGUIListBox::setWordWrap(bool enable) +{ + if ( enable == WordWrap ) + return; + + WordWrap = enable; + if ( WordWrap ) + { + wrapAllText(); + } +} + +bool CGUIListBox::isWordWrapEnabled() const +{ + return WordWrap; +} + } // end namespace gui } // end namespace irr diff -r f7c57859c998 lib/irrlicht/source/Irrlicht/CGUIListBox.h --- a/lib/irrlicht/source/Irrlicht/CGUIListBox.h Wed Jun 03 23:57:48 2009 +0200 +++ b/lib/irrlicht/source/Irrlicht/CGUIListBox.h Thu Jun 04 00:07:36 2009 +0200 @@ -158,6 +158,15 @@ //! This works only in EGUI_LBS_MULTI_SELECTION mode virtual void clearAllItemMultiSelections(); + //! Enables or disables word wrap. + /** \param enable: If set to true, words going over one line are + broken to the next line. */ + virtual void setWordWrap(bool enable); + + //! Checks if word wrap is enabled + //! \return true if word wrap is enabled, false otherwise + virtual bool isWordWrapEnabled() const; + private: struct ListItem @@ -168,6 +177,7 @@ core::stringw text; s32 icon; bool IsMultiSelected; + core::array< core::stringw > WrappedText; // A multicolor extension struct ListItemOverrideColor @@ -189,6 +199,23 @@ // get labels used for serialization bool getSerializationLabels(EGUI_LISTBOX_COLOR colorType, core::stringc & useColorLabel, core::stringc & colorLabel) const; + // create wrapped text for all lines + void wrapAllText(); + + // wrap the text for the given item + void wrapText( ListItem & item); + + //! break Split text into several lines + //! \param origText Text which should be split + //! \param maxWidth Width in pixels available for text. + //! \param font Font used to print the text + //! \param wordWrap Wrap into several lines if one line is too long. Prefer wrapping on whitespace. + //! \param multiLine Handle linebreaks like '\n' and '\r' + //! \param brokenText return the text split in several lines + //! \param brokenTextPosition return the positions at which the original text was splitted + void breakText( const core::stringw &origText, s32 maxWidth, gui::IGUIFont* font, bool wordWrap, bool multiLine, core::array &brokenText, core::array &brokenTextPosition); + + core::array< ListItem > Items; s32 Selected; s32 ItemHeight; @@ -207,6 +234,8 @@ bool AutoScroll; bool HighlightWhenNotFocused; EGUI_LISTBOX_SELECTION SelectionMode; + bool WordWrap; + gui::IGUIFont* FontUsedToWrap; };