Rework book formatter (Fixes #1148)

deque
MiroslavR 10 years ago
parent 727a83d09a
commit 4f89c3e77a

@ -70,11 +70,6 @@ namespace MWGui
void BookWindow::clearPages() void BookWindow::clearPages()
{ {
for (std::vector<MyGUI::Widget*>::iterator it=mPages.begin();
it!=mPages.end(); ++it)
{
MyGUI::Gui::getInstance().destroyWidget(*it);
}
mPages.clear(); mPages.clear();
} }
@ -89,25 +84,9 @@ namespace MWGui
MWWorld::LiveCellRef<ESM::Book> *ref = mBook.get<ESM::Book>(); MWWorld::LiveCellRef<ESM::Book> *ref = mBook.get<ESM::Book>();
BookTextParser parser; Formatting::BookFormatter formatter;
std::vector<std::string> results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height); mPages = formatter.markupToWidget(mLeftPage, ref->mBase->mText);
formatter.markupToWidget(mRightPage, ref->mBase->mText);
int i=0;
for (std::vector<std::string>::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::Widget>("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast<std::string>(i));
pageWidget->setNeedMouseFocus(false);
parser.parsePage(*it, pageWidget, mLeftPage->getSize().width);
mPages.push_back(pageWidget);
++i;
}
updatePages(); updatePages();
@ -164,19 +143,6 @@ namespace MWGui
mLeftPageNumber->setCaption( boost::lexical_cast<std::string>(mCurrentPage*2 + 1) ); mLeftPageNumber->setCaption( boost::lexical_cast<std::string>(mCurrentPage*2 + 1) );
mRightPageNumber->setCaption( boost::lexical_cast<std::string>(mCurrentPage*2 + 2) ); mRightPageNumber->setCaption( boost::lexical_cast<std::string>(mCurrentPage*2 + 2) );
unsigned int i=0;
for (std::vector<MyGUI::Widget*>::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 it is the last page, hide the button "Next Page"
if ( (mCurrentPage+1)*2 == mPages.size() if ( (mCurrentPage+1)*2 == mPages.size()
|| (mCurrentPage+1)*2 == mPages.size() + 1) || (mCurrentPage+1)*2 == mPages.size() + 1)
@ -191,6 +157,27 @@ namespace MWGui
} else { } else {
mPrevPageButton->setVisible(true); 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) void BookWindow::adjustButton (Gui::ImageButton* button)

@ -34,17 +34,21 @@ namespace MWGui
void adjustButton(Gui::ImageButton* button); void adjustButton(Gui::ImageButton* button);
private: private:
typedef std::pair<int, int> Page;
typedef std::vector<Page> Pages;
Gui::ImageButton* mCloseButton; Gui::ImageButton* mCloseButton;
Gui::ImageButton* mTakeButton; Gui::ImageButton* mTakeButton;
Gui::ImageButton* mNextPageButton; Gui::ImageButton* mNextPageButton;
Gui::ImageButton* mPrevPageButton; Gui::ImageButton* mPrevPageButton;
MyGUI::TextBox* mLeftPageNumber; MyGUI::TextBox* mLeftPageNumber;
MyGUI::TextBox* mRightPageNumber; MyGUI::TextBox* mRightPageNumber;
MyGUI::Widget* mLeftPage; MyGUI::Widget* mLeftPage;
MyGUI::Widget* mRightPage; MyGUI::Widget* mRightPage;
unsigned int mCurrentPage; // 0 is first page unsigned int mCurrentPage; // 0 is first page
std::vector<MyGUI::Widget*> mPages; Pages mPages;
MWWorld::Ptr mBook; MWWorld::Ptr mBook;

@ -11,68 +11,14 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/range/adaptor/filtered.hpp> #include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/copy.hpp> #include <boost/range/algorithm/copy.hpp>
#include <OgreUTFString.h>
#include <OgreUTFString.h>
#include <OgreResourceGroupManager.h> #include <OgreResourceGroupManager.h>
#include <MyGUI_EditText.h>
namespace 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) Ogre::UTFString::unicode_char unicodeCharFromChar(char ch)
{ {
std::string s; std::string s;
@ -80,56 +26,56 @@ namespace
Ogre::UTFString string(s); Ogre::UTFString string(s);
return string.getChar(0); 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 namespace MWGui
{ {
namespace Formatting
std::vector<std::string> BookTextParser::split(std::string utf8Text, const int width, const int height)
{ {
using Ogre::UTFString; Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text, const int pageWidth, const int pageHeight)
std::vector<std::string> result; {
using Ogre::UTFString;
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext);
boost::algorithm::replace_all(utf8Text, "\n", ""); boost::algorithm::replace_all(utf8Text, "\n", "");
boost::algorithm::replace_all(utf8Text, "\r", ""); boost::algorithm::replace_all(utf8Text, "\r", "");
boost::algorithm::replace_all(utf8Text, "<BR>", "\n"); boost::algorithm::replace_all(utf8Text, "<BR>", "\n");
boost::algorithm::replace_all(utf8Text, "<P>", "\n\n"); boost::algorithm::replace_all(utf8Text, "<P>", "\n\n");
UTFString text(utf8Text); UTFString text(utf8Text);
const int spacing = 48; UTFString plainText;
const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<');
const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n');
const UTFString::unicode_char SPACE = unicodeCharFromChar(' ');
while (!text.empty()) Paginator pag(pageWidth, pageHeight);
{
// read in characters until we have exceeded the size, or run out of text
int currentWidth = 0;
int currentHeight = 0;
size_t currentWordStart = 0; while (parent->getChildCount())
size_t index = 0;
{ {
std::string texToTrim = text.asUTF8(); MyGUI::Gui::getInstance().destroyWidget(parent->getChildAt(0));
boost::algorithm::trim( texToTrim );
text = UTFString(texToTrim);
} }
MyGUI::Widget * paper = parent->createWidget<MyGUI::Widget>("Widget", MyGUI::IntCoord(0, 0, pag.getPageWidth(), pag.getPageHeight()), MyGUI::Align::Left | MyGUI::Align::Top);
while (currentHeight <= height - spacing && index < text.size()) paper->setNeedMouseFocus(false);
bool ignoreNewlines = true;
for (size_t index = 0; index < text.size(); ++index)
{ {
const UTFString::unicode_char ch = text.getChar(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) if (ch == LEFT_ANGLE)
{ {
const size_t tagStart = index + 1; const size_t tagStart = index + 1;
@ -140,263 +86,203 @@ namespace MWGui
if (boost::algorithm::starts_with(tag, "IMG")) if (boost::algorithm::starts_with(tag, "IMG"))
{ {
const int h = mHeight; ImageElement elem(paper, pag, mTextStyle, tag);
parseImage(tag, false); elem.paginate();
currentHeight += (mHeight - h);
currentWidth = 0; ignoreNewlines = false;
} }
else if (boost::algorithm::starts_with(tag, "FONT")) else if (boost::algorithm::starts_with(tag, "FONT"))
{ {
parseFont(tag); parseFont(tag);
if (currentWidth != 0) {
currentHeight += currentFontHeight();
}
currentWidth = 0;
} }
else if (boost::algorithm::starts_with(tag, "DIV")) else if (boost::algorithm::starts_with(tag, "DIV"))
{ {
parseDiv(tag); parseDiv(tag);
if (currentWidth != 0) {
currentHeight += currentFontHeight();
currentWidth = 0;
}
} }
index = tagEnd; 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 else
{ {
currentWidth += widthForCharGlyph(ch); if (!ignoreNewlines || ch != NEWLINE)
} {
plainText.push_back(ch);
if (currentWidth > width) ignoreNewlines = false;
{ }
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());
} }
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()); // insert last page
text.erase(0, pageEnd); pag << Paginator::Page(pag.getStartTop(), pag.getStartTop() + pag.getPageHeight());
}
std::vector<std::string> 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();
}
MyGUI::IntSize BookTextParser::parsePage(std::string text, MyGUI::Widget* parent, const int width) paper->setSize(paper->getWidth(), pag.getCurrentTop());
{
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
text = Interpreter::fixDefinesBook(text, interpreterContext);
mParent = parent; return pag.getPages();
mWidth = width; }
mHeight = 0;
assert(mParent); Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text)
while (mParent->getChildCount())
{ {
MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); return markupToWidget(parent, utf8Text, parent->getWidth(), parent->getHeight());
} }
// remove trailing " void BookFormatter::parseDiv(std::string tag)
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())
{ {
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, "<BR>", "\n"); void BookFormatter::parseFont(std::string tag)
boost::algorithm::replace_all(text, "<P>", "\n\n"); {
boost::algorithm::trim_left(text); if (tag.find("COLOR=") != std::string::npos)
{
// remove trailing " int color_start = tag.find("COLOR=")+7;
if (text[text.size()-1] == '\"')
text.erase(text.size()-1);
parseSubText(text);
return MyGUI::IntSize(mWidth, mHeight);
}
void BookTextParser::parseImage(std::string tag, bool createWidget) int color;
{ std::stringstream ss;
int src_start = tag.find("SRC=")+5; ss << tag.substr(color_start, tag.find('"', color_start)-color_start);
ss >> std::hex >> color;
int width_start = tag.find("WIDTH=")+7; mTextStyle.mColour = MyGUI::Colour(
int width = boost::lexical_cast<int>(tag.substr(width_start, tag.find('"', width_start)-width_start)); (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; if (face != "Magic Cards")
int height = boost::lexical_cast<int>(tag.substr(height_start, tag.find('"', height_start)-height_start)); 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<MyGUI::ImageBox> ("ImageBox",
MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top,
mParent->getName() + boost::lexical_cast<std::string>(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); void GraphicElement::paginate()
mHeight += height; {
} 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) mPaginator.modifyCurrentTop(getHeight());
{ }
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;
}
void BookTextParser::parseFont(std::string tag) int GraphicElement::pageSplit()
{
if (tag.find("COLOR=") != std::string::npos)
{ {
int color_start = tag.find("COLOR=")+7; return mPaginator.getStartTop() + mPaginator.getPageHeight();
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);
} }
if (tag.find("FACE=") != std::string::npos)
int GraphicElement::currentFontHeight() const
{ {
int face_start = tag.find("FACE=")+6; std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont);
std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
}
if (face != "Magic Cards") float GraphicElement::widthForCharGlyph(MyGUI::Char unicodeChar) const
mTextStyle.mFont = face; {
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<MyGUI::EditBox>("NormalText",
MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top,
parent->getName() + boost::lexical_cast<std::string>(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) int TextElement::getHeight()
{
if (text[0] == '<')
{ {
const size_t tagStart = 1; return mEditBox->getTextSize().height;
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);
} }
size_t tagStart = std::string::npos; int TextElement::pageSplit()
std::string realText; // real text, without tags
for (size_t i = 0; i<text.size(); ++i)
{ {
char c = text[i]; // split lines
if (c == '<') const int lineHeight = currentFontHeight();
unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop()) / lineHeight;
int ret = mPaginator.getCurrentTop() + lastLine * lineHeight;
// first empty lines that would go to the next page should be ignored
// unfortunately, getLineInfo method won't be available until 3.2.2
#if (MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3, 2, 2))
const MyGUI::VectorLineInfo & lines = mEditBox->getSubWidgetText()->castType<MyGUI::EditText>()->getLineInfo();
for (unsigned int i = lastLine; i < lines.size(); ++i)
{ {
if ((i + 1 < text.size()) && text[i+1] == '/') // ignore closing tags if (lines[i].width == 0)
{ ret += lineHeight;
while (c != '>')
{
if (i >= text.size())
throw std::runtime_error("BookTextParser Error: Tag is not terminated");
++i;
c = text[i];
}
continue;
}
else else
{
tagStart = i;
break; break;
}
} }
else #endif
realText += c; return ret;
} }
MyGUI::EditBox* box = mParent->createWidget<MyGUI::EditBox>("NormalText", /* ImageElement */
MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & tag)
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount())); : GraphicElement(parent, pag, style),
box->setProperty("Static", "true"); mImageHeight(0)
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)
{ {
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<int>(tag.substr(width_start, tag.find('"', width_start)-width_start));
int height_start = tag.find("HEIGHT=")+8;
mImageHeight = boost::lexical_cast<int>(tag.substr(height_start, tag.find('"', height_start)-height_start));
mImageBox = parent->createWidget<MyGUI::ImageBox> ("ImageBox",
MyGUI::IntCoord(0, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top,
parent->getName() + boost::lexical_cast<std::string>(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();
}
}
} }

@ -5,63 +5,112 @@
namespace MWGui namespace MWGui
{ {
struct TextStyle namespace Formatting
{ {
TextStyle() : struct TextStyle
mColour(0,0,0)
, mFont("Default")
, mTextSize(16)
, mTextAlign(MyGUI::Align::Left | MyGUI::Align::Top)
{ {
} TextStyle() :
mColour(0,0,0)
, mFont("Default")
, mTextSize(16)
, mTextAlign(MyGUI::Align::Left | MyGUI::Align::Top)
{
}
MyGUI::Colour mColour; MyGUI::Colour mColour;
std::string mFont; std::string mFont;
int mTextSize; int mTextSize;
MyGUI::Align mTextAlign; MyGUI::Align mTextAlign;
}; };
/// \brief utilities for parsing book/scroll text as mygui widgets class Paginator
class BookTextParser {
{ public:
public: typedef std::pair<int, int> Page;
/** typedef std::vector<Page> Pages;
* Parse markup as MyGUI widgets
* @param markup to parse Paginator(int pageWidth, int pageHeight)
* @param parent for the created widgets : mStartTop(0), mCurrentTop(0),
* @param maximum width mPageWidth(pageWidth), mPageHeight(pageHeight)
* @return size of the created widgets {
*/ }
MyGUI::IntSize parsePage(std::string text, MyGUI::Widget* parent, const int width);
int getStartTop() const { return mStartTop; }
/** int getCurrentTop() const { return mCurrentTop; }
* Parse markup as MyGUI widgets int getPageWidth() const { return mPageWidth; }
* @param markup to parse int getPageHeight() const { return mPageHeight; }
* @param parent for the created widgets Pages getPages() const { return mPages; }
* @param maximum width
* @return size of the created widgets void setStartTop(int top) { mStartTop = top; }
*/ void setCurrentTop(int top) { mCurrentTop = top; }
MyGUI::IntSize parseScroll(std::string text, MyGUI::Widget* parent, const int width); void modifyStartTop(int mod) { mStartTop += mod; }
void modifyCurrentTop(int mod) { mCurrentTop += mod; }
/**
* Split the specified text into pieces that fit in the area specified by width and height parameters Paginator & operator<<(const Page & page)
*/ {
std::vector<std::string> split(std::string text, const int width, const int height); mPages.push_back(page);
return *this;
protected: }
float widthForCharGlyph(unsigned unicodeChar) const;
float currentFontHeight() const; private:
void parseSubText(std::string text); int mStartTop, mCurrentTop;
int mPageWidth, mPageHeight;
void parseImage(std::string tag, bool createWidget=true); Pages mPages;
void parseDiv(std::string tag); };
void parseFont(std::string tag);
private: /// \brief utilities for parsing book/scroll text as mygui widgets
MyGUI::Widget* mParent; class BookFormatter
int mWidth; // maximum width {
int mHeight; // current height public:
TextStyle mTextStyle; 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 #endif

@ -54,8 +54,9 @@ namespace MWGui
MWWorld::LiveCellRef<ESM::Book> *ref = mScroll.get<ESM::Book>(); MWWorld::LiveCellRef<ESM::Book> *ref = mScroll.get<ESM::Book>();
BookTextParser parser; Formatting::BookFormatter formatter;
MyGUI::IntSize size = parser.parseScroll(ref->mBase->mText, mTextView, 390); 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 // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
mTextView->setVisibleVScroll(false); mTextView->setVisibleVScroll(false);

@ -41,8 +41,8 @@
<Property key="TextColour" value="0 0 0"/> <Property key="TextColour" value="0 0 0"/>
</Widget> </Widget>
<Widget type="Widget" skin="" position="30 22 240 300" name="LeftPage"/> <Widget type="Widget" skin="" position="30 15 240 324" name="LeftPage"/>
<Widget type="Widget" skin="" position="300 22 240 300" name="RightPage"/> <Widget type="Widget" skin="" position="300 15 240 324" name="RightPage"/>
</Widget> </Widget>
</Widget> </Widget>
</Widget> </Widget>

Loading…
Cancel
Save