From 4f89c3e77ae78e8334cca850cfa3e256235bdc5d Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sat, 20 Sep 2014 00:11:04 +0200 Subject: [PATCH] Rework book formatter (Fixes #1148) --- apps/openmw/mwgui/bookwindow.cpp | 61 ++-- apps/openmw/mwgui/bookwindow.hpp | 6 +- apps/openmw/mwgui/formatting.cpp | 478 +++++++++++------------------ apps/openmw/mwgui/formatting.hpp | 155 ++++++---- apps/openmw/mwgui/scrollwindow.cpp | 5 +- files/mygui/openmw_book.layout | 4 +- 6 files changed, 318 insertions(+), 391 deletions(-) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 372e81844..92e876708 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -70,11 +70,6 @@ namespace MWGui void BookWindow::clearPages() { - for (std::vector::iterator it=mPages.begin(); - it!=mPages.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } mPages.clear(); } @@ -89,25 +84,9 @@ namespace MWGui MWWorld::LiveCellRef *ref = mBook.get(); - BookTextParser parser; - std::vector results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height); - - int i=0; - for (std::vector::iterator it=results.begin(); - it!=results.end(); ++it) - { - MyGUI::Widget* parent; - if (i%2 == 0) - parent = mLeftPage; - else - parent = mRightPage; - - MyGUI::Widget* pageWidget = parent->createWidgetReal("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast(i)); - pageWidget->setNeedMouseFocus(false); - parser.parsePage(*it, pageWidget, mLeftPage->getSize().width); - mPages.push_back(pageWidget); - ++i; - } + Formatting::BookFormatter formatter; + mPages = formatter.markupToWidget(mLeftPage, ref->mBase->mText); + formatter.markupToWidget(mRightPage, ref->mBase->mText); updatePages(); @@ -164,19 +143,6 @@ namespace MWGui mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); - unsigned int i=0; - for (std::vector::iterator it = mPages.begin(); - it != mPages.end(); ++it) - { - if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) - (*it)->setVisible(true); - else - { - (*it)->setVisible(false); - } - ++i; - } - //If it is the last page, hide the button "Next Page" if ( (mCurrentPage+1)*2 == mPages.size() || (mCurrentPage+1)*2 == mPages.size() + 1) @@ -191,6 +157,27 @@ namespace MWGui } else { mPrevPageButton->setVisible(true); } + + if (mPages.empty()) + return; + + MyGUI::Widget * paper; + + paper = mLeftPage->getChildAt(0); + paper->setCoord(paper->getPosition().left, -mPages[mCurrentPage*2].first, + paper->getWidth(), mPages[mCurrentPage*2].second); + + paper = mRightPage->getChildAt(0); + if ((mCurrentPage+1)*2 <= mPages.size()) + { + paper->setCoord(paper->getPosition().left, -mPages[mCurrentPage*2+1].first, + paper->getWidth(), mPages[mCurrentPage*2+1].second); + paper->setVisible(true); + } + else + { + paper->setVisible(false); + } } void BookWindow::adjustButton (Gui::ImageButton* button) diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index e0f921dc1..ea3057a6f 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -34,17 +34,21 @@ namespace MWGui void adjustButton(Gui::ImageButton* button); private: + typedef std::pair Page; + typedef std::vector Pages; + Gui::ImageButton* mCloseButton; Gui::ImageButton* mTakeButton; Gui::ImageButton* mNextPageButton; Gui::ImageButton* mPrevPageButton; + MyGUI::TextBox* mLeftPageNumber; MyGUI::TextBox* mRightPageNumber; MyGUI::Widget* mLeftPage; MyGUI::Widget* mRightPage; unsigned int mCurrentPage; // 0 is first page - std::vector mPages; + Pages mPages; MWWorld::Ptr mBook; diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 42dfb1310..25091def9 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -11,68 +11,14 @@ #include #include #include -#include +#include #include +#include + namespace { - int convertFromHex(std::string hex) - { - int value = 0; - - int a = 0; - int b = hex.length() - 1; - for (; b >= 0; a++, b--) - { - if (hex[b] >= '0' && hex[b] <= '9') - { - value += (hex[b] - '0') * (1 << (a * 4)); - } - else - { - switch (hex[b]) - { - case 'A': - case 'a': - value += 10 * (1 << (a * 4)); - break; - - case 'B': - case 'b': - value += 11 * (1 << (a * 4)); - break; - - case 'C': - case 'c': - value += 12 * (1 << (a * 4)); - break; - - case 'D': - case 'd': - value += 13 * (1 << (a * 4)); - break; - - case 'E': - case 'e': - value += 14 * (1 << (a * 4)); - break; - - case 'F': - case 'f': - value += 15 * (1 << (a * 4)); - break; - - default: - throw std::runtime_error("invalid character in hex number"); - break; - } - } - } - - return value; - } - Ogre::UTFString::unicode_char unicodeCharFromChar(char ch) { std::string s; @@ -80,56 +26,56 @@ namespace Ogre::UTFString string(s); return string.getChar(0); } - - bool is_not_empty(const std::string& s) { - std::string temp = s; - boost::algorithm::trim(temp); - return !temp.empty(); - } } namespace MWGui { - - std::vector BookTextParser::split(std::string utf8Text, const int width, const int height) + namespace Formatting { - using Ogre::UTFString; - std::vector result; + Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text, const int pageWidth, const int pageHeight) + { + using Ogre::UTFString; - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); - boost::algorithm::replace_all(utf8Text, "\n", ""); - boost::algorithm::replace_all(utf8Text, "\r", ""); - boost::algorithm::replace_all(utf8Text, "
", "\n"); - boost::algorithm::replace_all(utf8Text, "

", "\n\n"); + boost::algorithm::replace_all(utf8Text, "\n", ""); + boost::algorithm::replace_all(utf8Text, "\r", ""); + boost::algorithm::replace_all(utf8Text, "
", "\n"); + boost::algorithm::replace_all(utf8Text, "

", "\n\n"); - UTFString text(utf8Text); - const int spacing = 48; + UTFString text(utf8Text); + UTFString plainText; - const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); - const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); - const UTFString::unicode_char SPACE = unicodeCharFromChar(' '); + const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); + const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); - while (!text.empty()) - { - // read in characters until we have exceeded the size, or run out of text - int currentWidth = 0; - int currentHeight = 0; + Paginator pag(pageWidth, pageHeight); - size_t currentWordStart = 0; - size_t index = 0; - + while (parent->getChildCount()) { - std::string texToTrim = text.asUTF8(); - boost::algorithm::trim( texToTrim ); - text = UTFString(texToTrim); + MyGUI::Gui::getInstance().destroyWidget(parent->getChildAt(0)); } - - - while (currentHeight <= height - spacing && index < text.size()) + + MyGUI::Widget * paper = parent->createWidget("Widget", MyGUI::IntCoord(0, 0, pag.getPageWidth(), pag.getPageHeight()), MyGUI::Align::Left | MyGUI::Align::Top); + paper->setNeedMouseFocus(false); + + bool ignoreNewlines = true; + for (size_t index = 0; index < text.size(); ++index) { const UTFString::unicode_char ch = text.getChar(index); + if (!plainText.empty() && (ch == LEFT_ANGLE || index == text.size() - 1)) + { + // if there's a newline at the end of the box caption, remove it + if (plainText[plainText.size()-1] == NEWLINE) + plainText.erase(plainText.end()-1); + + TextElement elem(paper, pag, mTextStyle, plainText.asUTF8()); + elem.paginate(); + + plainText.clear(); + } + if (ch == LEFT_ANGLE) { const size_t tagStart = index + 1; @@ -140,263 +86,203 @@ namespace MWGui if (boost::algorithm::starts_with(tag, "IMG")) { - const int h = mHeight; - parseImage(tag, false); - currentHeight += (mHeight - h); - currentWidth = 0; + ImageElement elem(paper, pag, mTextStyle, tag); + elem.paginate(); + + ignoreNewlines = false; } else if (boost::algorithm::starts_with(tag, "FONT")) { parseFont(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - } - currentWidth = 0; } else if (boost::algorithm::starts_with(tag, "DIV")) { parseDiv(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - currentWidth = 0; - } } + index = tagEnd; } - else if (ch == NEWLINE) - { - currentHeight += currentFontHeight(); - currentWidth = 0; - currentWordStart = index; - } - else if (ch == SPACE) - { - currentWidth += 3; // keep this in sync with the font's SpaceWidth property - currentWordStart = index; - } else { - currentWidth += widthForCharGlyph(ch); - } - - if (currentWidth > width) - { - currentHeight += currentFontHeight(); - currentWidth = 0; - // add size of the current word - UTFString word = text.substr(currentWordStart, index - currentWordStart); - for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it) - currentWidth += widthForCharGlyph(it.getCharacter()); + if (!ignoreNewlines || ch != NEWLINE) + { + plainText.push_back(ch); + ignoreNewlines = false; + } } - index += UTFString::_utf16_char_length(ch); } - const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) - ? currentWordStart : index; - result.push_back(text.substr(0, pageEnd).asUTF8()); - text.erase(0, pageEnd); - } - - std::vector nonEmptyPages; - boost::copy(result | boost::adaptors::filtered(is_not_empty), std::back_inserter(nonEmptyPages)); - return nonEmptyPages; - } - - float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const - { - std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName) - ->getGlyphInfo(unicodeChar)->width; - } - - float BookTextParser::currentFontHeight() const - { - std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); - } + // insert last page + pag << Paginator::Page(pag.getStartTop(), pag.getStartTop() + pag.getPageHeight()); - MyGUI::IntSize BookTextParser::parsePage(std::string text, MyGUI::Widget* parent, const int width) - { - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesBook(text, interpreterContext); + paper->setSize(paper->getWidth(), pag.getCurrentTop()); - mParent = parent; - mWidth = width; - mHeight = 0; + return pag.getPages(); + } - assert(mParent); - while (mParent->getChildCount()) + Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text) { - MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); + return markupToWidget(parent, utf8Text, parent->getWidth(), parent->getHeight()); } - // remove trailing " - if (text[text.size()-1] == '\"') - text.erase(text.size()-1); - - parseSubText(text); - return MyGUI::IntSize(mWidth, mHeight); - } - - MyGUI::IntSize BookTextParser::parseScroll(std::string text, MyGUI::Widget* parent, const int width) - { - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesBook(text, interpreterContext); - - mParent = parent; - mWidth = width; - mHeight = 0; - - assert(mParent); - while (mParent->getChildCount()) + void BookFormatter::parseDiv(std::string tag) { - MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); + if (tag.find("ALIGN=") == std::string::npos) + return; + + int align_start = tag.find("ALIGN=")+7; + std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); + if (align == "CENTER") + mTextStyle.mTextAlign = MyGUI::Align::HCenter; + else if (align == "LEFT") + mTextStyle.mTextAlign = MyGUI::Align::Left; } - boost::algorithm::replace_all(text, "
", "\n"); - boost::algorithm::replace_all(text, "

", "\n\n"); - boost::algorithm::trim_left(text); - - // remove trailing " - if (text[text.size()-1] == '\"') - text.erase(text.size()-1); - - parseSubText(text); - return MyGUI::IntSize(mWidth, mHeight); - } - + void BookFormatter::parseFont(std::string tag) + { + if (tag.find("COLOR=") != std::string::npos) + { + int color_start = tag.find("COLOR=")+7; - void BookTextParser::parseImage(std::string tag, bool createWidget) - { - int src_start = tag.find("SRC=")+5; + int color; + std::stringstream ss; + ss << tag.substr(color_start, tag.find('"', color_start)-color_start); + ss >> std::hex >> color; - int width_start = tag.find("WIDTH=")+7; - int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); + mTextStyle.mColour = MyGUI::Colour( + (color>>16 & 0xFF) / 255.f, + (color>>8 & 0xFF) / 255.f, + (color & 0xFF) / 255.f); + } + if (tag.find("FACE=") != std::string::npos) + { + int face_start = tag.find("FACE=")+6; + std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); - int height_start = tag.find("HEIGHT=")+8; - int height = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); + if (face != "Magic Cards") + mTextStyle.mFont = face; + } + if (tag.find("SIZE=") != std::string::npos) + { + /// \todo + } + } - if (createWidget) + /* GraphicElement */ + GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style) + : mParent(parent), mPaginator(pag), mStyle(style) { - MyGUI::ImageBox* box = mParent->createWidget ("ImageBox", - MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, - mParent->getName() + boost::lexical_cast(mParent->getChildCount())); - - std::string image = Misc::ResourceHelpers::correctBookartPath(tag.substr(src_start, tag.find('"', src_start)-src_start), width, height); - box->setImageTexture(image); - box->setProperty("NeedMouse", "false"); } - mWidth = std::max(mWidth, width); - mHeight += height; - } + void GraphicElement::paginate() + { + int newTop = mPaginator.getCurrentTop() + getHeight(); + while (newTop-mPaginator.getStartTop() > mPaginator.getPageHeight()) + { + int newStartTop = pageSplit(); + mPaginator << Paginator::Page(mPaginator.getStartTop(), newStartTop); + mPaginator.setStartTop(newStartTop); + } - void BookTextParser::parseDiv(std::string tag) - { - if (tag.find("ALIGN=") == std::string::npos) - return; - - int align_start = tag.find("ALIGN=")+7; - std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); - if (align == "CENTER") - mTextStyle.mTextAlign = MyGUI::Align::HCenter; - else if (align == "LEFT") - mTextStyle.mTextAlign = MyGUI::Align::Left; - } + mPaginator.modifyCurrentTop(getHeight()); + } - void BookTextParser::parseFont(std::string tag) - { - if (tag.find("COLOR=") != std::string::npos) + int GraphicElement::pageSplit() { - int color_start = tag.find("COLOR=")+7; - std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start); - - mTextStyle.mColour = MyGUI::Colour( - convertFromHex(color.substr(0, 2))/255.0, - convertFromHex(color.substr(2, 2))/255.0, - convertFromHex(color.substr(4, 2))/255.0); + return mPaginator.getStartTop() + mPaginator.getPageHeight(); } - if (tag.find("FACE=") != std::string::npos) + + int GraphicElement::currentFontHeight() const { - int face_start = tag.find("FACE=")+6; - std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); + std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); + } - if (face != "Magic Cards") - mTextStyle.mFont = face; + float GraphicElement::widthForCharGlyph(MyGUI::Char unicodeChar) const + { + std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName) + ->getGlyphInfo(unicodeChar)->width; } - if (tag.find("SIZE=") != std::string::npos) + + /* TextElement */ + TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & text) + : GraphicElement(parent, pag, style) { - /// \todo + MyGUI::EditBox* box = parent->createWidget("NormalText", + MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, + parent->getName() + boost::lexical_cast(parent->getChildCount())); + box->setProperty("Static", "true"); + box->setProperty("MultiLine", "true"); + box->setProperty("WordWrap", "true"); + box->setProperty("NeedMouse", "false"); + box->setMaxTextLength(text.size()); + box->setTextAlign(mStyle.mTextAlign); + box->setTextColour(mStyle.mColour); + box->setFontName(mStyle.mFont); + box->setCaption(MyGUI::TextIterator::toTagsString(text)); + box->setSize(box->getSize().width, box->getTextSize().height); + mEditBox = box; } - } - void BookTextParser::parseSubText(std::string text) - { - if (text[0] == '<') + int TextElement::getHeight() { - const size_t tagStart = 1; - const size_t tagEnd = text.find('>', tagStart); - if (tagEnd == std::string::npos) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - const std::string tag = text.substr(tagStart, tagEnd - tagStart); - - if (boost::algorithm::starts_with(tag, "IMG")) - parseImage(tag); - if (boost::algorithm::starts_with(tag, "FONT")) - parseFont(tag); - if (boost::algorithm::starts_with(tag, "DIV")) - parseDiv(tag); - - text.erase(0, tagEnd + 1); + return mEditBox->getTextSize().height; } - size_t tagStart = std::string::npos; - std::string realText; // real text, without tags - for (size_t i = 0; i= MYGUI_DEFINE_VERSION(3, 2, 2)) + const MyGUI::VectorLineInfo & lines = mEditBox->getSubWidgetText()->castType()->getLineInfo(); + for (unsigned int i = lastLine; i < lines.size(); ++i) { - if ((i + 1 < text.size()) && text[i+1] == '/') // ignore closing tags - { - while (c != '>') - { - if (i >= text.size()) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - ++i; - c = text[i]; - } - continue; - } + if (lines[i].width == 0) + ret += lineHeight; else - { - tagStart = i; break; - } } - else - realText += c; +#endif + return ret; } - MyGUI::EditBox* box = mParent->createWidget("NormalText", - MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, - mParent->getName() + boost::lexical_cast(mParent->getChildCount())); - box->setProperty("Static", "true"); - box->setProperty("MultiLine", "true"); - box->setProperty("WordWrap", "true"); - box->setProperty("NeedMouse", "false"); - box->setMaxTextLength(realText.size()); - box->setTextAlign(mTextStyle.mTextAlign); - box->setTextColour(mTextStyle.mColour); - box->setFontName(mTextStyle.mFont); - box->setCaption(MyGUI::TextIterator::toTagsString(realText)); - box->setSize(box->getSize().width, box->getTextSize().height); - mHeight += box->getTextSize().height; - - if (tagStart != std::string::npos) + /* ImageElement */ + ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & tag) + : GraphicElement(parent, pag, style), + mImageHeight(0) { - parseSubText(text.substr(tagStart, text.size())); + int src_start = tag.find("SRC=")+5; + std::string src = tag.substr(src_start, tag.find('"', src_start)-src_start); + + int width_start = tag.find("WIDTH=")+7; + int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); + + int height_start = tag.find("HEIGHT=")+8; + mImageHeight = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); + + mImageBox = parent->createWidget ("ImageBox", + MyGUI::IntCoord(0, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top, + parent->getName() + boost::lexical_cast(parent->getChildCount())); + + std::string image = Misc::ResourceHelpers::correctBookartPath(src, width, mImageHeight); + mImageBox->setImageTexture(image); + mImageBox->setProperty("NeedMouse", "false"); } - } + int ImageElement::getHeight() + { + return mImageHeight; + } + + int ImageElement::pageSplit() + { + return mPaginator.getCurrentTop(); + } + } } diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index a32d98fe5..b0a8bce0f 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -5,63 +5,112 @@ namespace MWGui { - struct TextStyle + namespace Formatting { - TextStyle() : - mColour(0,0,0) - , mFont("Default") - , mTextSize(16) - , mTextAlign(MyGUI::Align::Left | MyGUI::Align::Top) + struct TextStyle { - } + TextStyle() : + mColour(0,0,0) + , mFont("Default") + , mTextSize(16) + , mTextAlign(MyGUI::Align::Left | MyGUI::Align::Top) + { + } - MyGUI::Colour mColour; - std::string mFont; - int mTextSize; - MyGUI::Align mTextAlign; - }; + MyGUI::Colour mColour; + std::string mFont; + int mTextSize; + MyGUI::Align mTextAlign; + }; - /// \brief utilities for parsing book/scroll text as mygui widgets - class BookTextParser - { - public: - /** - * Parse markup as MyGUI widgets - * @param markup to parse - * @param parent for the created widgets - * @param maximum width - * @return size of the created widgets - */ - MyGUI::IntSize parsePage(std::string text, MyGUI::Widget* parent, const int width); - - /** - * Parse markup as MyGUI widgets - * @param markup to parse - * @param parent for the created widgets - * @param maximum width - * @return size of the created widgets - */ - MyGUI::IntSize parseScroll(std::string text, MyGUI::Widget* parent, const int width); - - /** - * Split the specified text into pieces that fit in the area specified by width and height parameters - */ - std::vector split(std::string text, const int width, const int height); - - protected: - float widthForCharGlyph(unsigned unicodeChar) const; - float currentFontHeight() const; - void parseSubText(std::string text); - - void parseImage(std::string tag, bool createWidget=true); - void parseDiv(std::string tag); - void parseFont(std::string tag); - private: - MyGUI::Widget* mParent; - int mWidth; // maximum width - int mHeight; // current height - TextStyle mTextStyle; - }; + class Paginator + { + public: + typedef std::pair Page; + typedef std::vector Pages; + + Paginator(int pageWidth, int pageHeight) + : mStartTop(0), mCurrentTop(0), + mPageWidth(pageWidth), mPageHeight(pageHeight) + { + } + + int getStartTop() const { return mStartTop; } + int getCurrentTop() const { return mCurrentTop; } + int getPageWidth() const { return mPageWidth; } + int getPageHeight() const { return mPageHeight; } + Pages getPages() const { return mPages; } + + void setStartTop(int top) { mStartTop = top; } + void setCurrentTop(int top) { mCurrentTop = top; } + void modifyStartTop(int mod) { mStartTop += mod; } + void modifyCurrentTop(int mod) { mCurrentTop += mod; } + + Paginator & operator<<(const Page & page) + { + mPages.push_back(page); + return *this; + } + + private: + int mStartTop, mCurrentTop; + int mPageWidth, mPageHeight; + Pages mPages; + }; + + /// \brief utilities for parsing book/scroll text as mygui widgets + class BookFormatter + { + public: + Paginator::Pages markupToWidget(MyGUI::Widget * parent, std::string utf8Text, const int pageWidth, const int pageHeight); + Paginator::Pages markupToWidget(MyGUI::Widget * parent, std::string utf8Text); + + protected: + void parseDiv(std::string tag); + void parseFont(std::string tag); + private: + TextStyle mTextStyle; + }; + + class GraphicElement + { + public: + GraphicElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style); + virtual int getHeight() = 0; + virtual void paginate(); + virtual int pageSplit(); + + protected: + int currentFontHeight() const; + float widthForCharGlyph(MyGUI::Char unicodeChar) const; + + MyGUI::Widget * mParent; + Paginator & mPaginator; + TextStyle mStyle; + }; + + class TextElement : public GraphicElement + { + public: + TextElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & text); + virtual int getHeight(); + virtual int pageSplit(); + private: + MyGUI::EditBox * mEditBox; + }; + + class ImageElement : public GraphicElement + { + public: + ImageElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & tag); + virtual int getHeight(); + virtual int pageSplit(); + + private: + int mImageHeight; + MyGUI::ImageBox * mImageBox; + }; + } } #endif diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 7530d7a9e..038d91ca9 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -54,8 +54,9 @@ namespace MWGui MWWorld::LiveCellRef *ref = mScroll.get(); - BookTextParser parser; - MyGUI::IntSize size = parser.parseScroll(ref->mBase->mText, mTextView, 390); + Formatting::BookFormatter formatter; + formatter.markupToWidget(mTextView, ref->mBase->mText, 390, mTextView->getHeight()); + MyGUI::IntSize size = mTextView->getChildAt(0)->getSize(); // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mTextView->setVisibleVScroll(false); diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index 9f30bf253..f91cd696e 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -41,8 +41,8 @@ - - + +