diff -r 8d320eadaf39 lib/irrlicht/include/IGUIListBox.h --- a/lib/irrlicht/include/IGUIListBox.h Thu Jan 08 05:15:43 2009 +0100 +++ b/lib/irrlicht/include/IGUIListBox.h Thu Jan 08 06:46:21 2009 +0100 @@ -166,7 +166,17 @@ //! Clear the selection flags for all items //! This works only in EGUI_LBS_MULTI_SELECTION mode - virtual void clearAllItemMultiSelections() = 0; + 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 8d320eadaf39 lib/irrlicht/source/Irrlicht/CGUIListBox.cpp --- a/lib/irrlicht/source/Irrlicht/CGUIListBox.cpp Thu Jan 08 05:15:43 2009 +0100 +++ b/lib/irrlicht/source/Irrlicht/CGUIListBox.cpp Thu Jan 08 06:46:22 2009 +0100 @@ -28,7 +28,8 @@ ScrollBar(0), Selecting(false), DrawBack(drawBack), MoveOverSelect(moveOverSelect), selectTime(0), AutoScroll(true), KeyBuffer(), LastKeyTime(0), HighlightWhenNotFocused(true) - , SelectionMode(EGUI_LBS_SINGLE_SELECTION) + , SelectionMode(EGUI_LBS_SINGLE_SELECTION) + , WordWrap(false), FontUsedToWrap(0) { #ifdef _DEBUG setDebugName("CGUIListBox"); @@ -66,7 +67,10 @@ ScrollBar->drop(); if (Font) - Font->drop(); + Font->drop(); + + if ( FontUsedToWrap ) + FontUsedToWrap->drop(); if (IconBank) IconBank->drop(); @@ -161,8 +165,20 @@ Font->grab(); } } + + if ( WordWrap ) + { + TotalItemHeight = 0; + for ( u32 i=0; i < Items.size(); ++i ) + { + TotalItemHeight += Items[i].WrappedText.size() * ItemHeight; + } + } + else + { + TotalItemHeight = ItemHeight * Items.size(); + } - TotalItemHeight = ItemHeight * Items.size(); ScrollBar->setMax(TotalItemHeight - AbsoluteRect.getHeight()); if ( TotalItemHeight <= AbsoluteRect.getHeight() ) @@ -426,9 +442,26 @@ s32 oldSelected = Selected; // find new selected item. - if (ItemHeight!=0) - Selected = ((ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + ScrollBar->getPos()) / ItemHeight; + if (ItemHeight!=0) + { + 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 && Selected < (s32)Items.size() @@ -487,7 +520,8 @@ { IGUIElement::updateAbsolutePosition(); - recalculateItemHeight(); + recalculateItemHeight(); + wrapAllText(); } @@ -496,7 +530,11 @@ { if (!IsVisible) return; - + + if ( FontUsedToWrap != Font && WordWrap ) + { + wrapAllText(); + } recalculateItemHeight(); // if the font changed IGUISkin* skin = Environment->getSkin(); @@ -536,7 +574,15 @@ 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) @@ -578,7 +624,20 @@ 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; } @@ -596,7 +655,10 @@ { ListItem i; i.text = text; - i.icon = icon; + i.icon = icon; + + if ( WordWrap ) + wrapText(i); Items.push_back(i); recalculateItemHeight(); @@ -622,7 +684,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) { @@ -691,7 +765,8 @@ out->addBool ("DrawBack", DrawBack); out->addBool ("MoveOverSelect", MoveOverSelect); out->addBool ("AutoScroll", AutoScroll); - out->addEnum ("SelectionMode", SelectionMode, GUIListBoxSelectionNames ); + out->addEnum ("SelectionMode", SelectionMode, GUIListBoxSelectionNames ); + out->addBool ("WordWrap", WordWrap); out->addInt("ItemCount", Items.size()); for (u32 i=0;igetAttributeAsBool("DrawBack"); MoveOverSelect = in->getAttributeAsBool("MoveOverSelect"); AutoScroll = in->getAttributeAsBool("AutoScroll"); - SelectionMode = (EGUI_LISTBOX_SELECTION) in->getAttributeAsEnumeration("SelectionMode", GUIListBoxSelectionNames ); + SelectionMode = (EGUI_LISTBOX_SELECTION) in->getAttributeAsEnumeration("SelectionMode", GUIListBoxSelectionNames ); + WordWrap = in->getAttributeAsBool("WordWrap"); IGUIListBox::deserializeAttributes(in,options); @@ -757,7 +833,12 @@ Items[i].OverrideColors[c].Color = in->getAttributeAsColor(label.c_str()); } } - } + } + + if ( WordWrap ) + { + wrapAllText(); + } } @@ -797,7 +878,10 @@ { ListItem i; i.text = text; - i.icon = icon; + i.icon = icon; + + if ( WordWrap ) + wrapText(i); Items.insert(i, index); recalculateItemHeight(); @@ -960,6 +1044,159 @@ for ( u32 i = 0; i < Items.size(); ++i ) 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 diff -r 8d320eadaf39 lib/irrlicht/source/Irrlicht/CGUIListBox.h --- a/lib/irrlicht/source/Irrlicht/CGUIListBox.h Thu Jan 08 05:15:43 2009 +0100 +++ b/lib/irrlicht/source/Irrlicht/CGUIListBox.h Thu Jan 08 06:46:22 2009 +0100 @@ -151,6 +151,17 @@ //! Clear the selection flags for all items //! 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: @@ -161,7 +172,8 @@ core::stringw text; s32 icon; - bool IsMultiSelected; + bool IsMultiSelected; + core::array< core::stringw > WrappedText; // A multicolor extension struct ListItemOverrideColor @@ -181,7 +193,23 @@ void recalculateItemWidth(s32 icon); // get labels used for serialization - bool getSerializationLabels(EGUI_LISTBOX_COLOR colorType, core::stringc & useColorLabel, core::stringc & colorLabel) const; + 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; @@ -199,7 +227,9 @@ core::stringw KeyBuffer; u32 LastKeyTime; bool HighlightWhenNotFocused; - EGUI_LISTBOX_SELECTION SelectionMode; + EGUI_LISTBOX_SELECTION SelectionMode; + bool WordWrap; + gui::IGUIFont* FontUsedToWrap; };