1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-11-28 14:34:41 +00:00

Reduce type conversions in books

This commit is contained in:
Evil Eye 2025-09-22 20:59:13 +02:00
parent 5308e22f6d
commit b15ae08a07
9 changed files with 303 additions and 342 deletions

View file

@ -113,10 +113,7 @@ namespace MWGui
struct TypesetBookImpl : TypesetBook struct TypesetBookImpl : TypesetBook
{ {
typedef std::vector<uint8_t> Content; typedef std::pair<Utf8Stream::Point, Utf8Stream::Point> Range;
typedef std::list<Content> Contents;
typedef Utf8Stream::Point Utf8Point;
typedef std::pair<Utf8Point, Utf8Point> Range;
struct StyleImpl : BookTypesetter::Style struct StyleImpl : BookTypesetter::Style
{ {
@ -127,29 +124,28 @@ namespace MWGui
InteractiveId mInteractiveId; InteractiveId mInteractiveId;
bool match(MyGUI::IFont* tstFont, const MyGUI::Colour& tstHotColour, const MyGUI::Colour& tstActiveColour, bool match(MyGUI::IFont* tstFont, const MyGUI::Colour& tstHotColour, const MyGUI::Colour& tstActiveColour,
const MyGUI::Colour& tstNormalColour, intptr_t tstInteractiveId) const MyGUI::Colour& tstNormalColour, InteractiveId tstInteractiveId) const
{ {
return (mFont == tstFont) return (mFont == tstFont)
&& partal_match(tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); && partialMatch(tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId);
} }
bool match(std::string_view tstFont, const MyGUI::Colour& tstHotColour, bool match(std::string_view tstFont, const MyGUI::Colour& tstHotColour,
const MyGUI::Colour& tstActiveColour, const MyGUI::Colour& tstNormalColour, intptr_t tstInteractiveId) const MyGUI::Colour& tstActiveColour, const MyGUI::Colour& tstNormalColour,
InteractiveId tstInteractiveId) const
{ {
return (mFont->getResourceName() == tstFont) return (mFont->getResourceName() == tstFont)
&& partal_match(tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); && partialMatch(tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId);
} }
bool partal_match(const MyGUI::Colour& tstHotColour, const MyGUI::Colour& tstActiveColour, bool partialMatch(const MyGUI::Colour& tstHotColour, const MyGUI::Colour& tstActiveColour,
const MyGUI::Colour& tstNormalColour, intptr_t tstInteractiveId) const MyGUI::Colour& tstNormalColour, InteractiveId tstInteractiveId) const
{ {
return (mHotColour == tstHotColour) && (mActiveColour == tstActiveColour) return (mHotColour == tstHotColour) && (mActiveColour == tstActiveColour)
&& (mNormalColour == tstNormalColour) && (mInteractiveId == tstInteractiveId); && (mNormalColour == tstNormalColour) && (mInteractiveId == tstInteractiveId);
} }
}; };
typedef std::list<StyleImpl> Styles;
struct Run struct Run
{ {
StyleImpl* mStyle; StyleImpl* mStyle;
@ -158,19 +154,15 @@ namespace MWGui
int mPrintableChars; int mPrintableChars;
}; };
typedef std::vector<Run> Runs;
struct Line struct Line
{ {
Runs mRuns; std::vector<Run> mRuns;
MyGUI::IntRect mRect; MyGUI::IntRect mRect;
}; };
typedef std::vector<Line> Lines;
struct Section struct Section
{ {
Lines mLines; std::vector<Line> mLines;
MyGUI::IntRect mRect; MyGUI::IntRect mRect;
}; };
@ -180,12 +172,10 @@ namespace MWGui
// A page is basically a "window" into a portion of the source text, similar to a ScrollView. // A page is basically a "window" into a portion of the source text, similar to a ScrollView.
typedef std::pair<int, int> Page; typedef std::pair<int, int> Page;
typedef std::vector<Page> Pages; std::vector<Page> mPages;
Pages mPages;
Sections mSections; Sections mSections;
Contents mContents; std::list<Content> mContents;
Styles mStyles; std::list<StyleImpl> mStyles;
MyGUI::IntRect mRect; MyGUI::IntRect mRect;
void setColour(size_t section, size_t line, size_t run, const MyGUI::Colour& colour) const override void setColour(size_t section, size_t line, size_t run, const MyGUI::Colour& colour) const override
@ -202,14 +192,14 @@ namespace MWGui
virtual ~TypesetBookImpl() = default; virtual ~TypesetBookImpl() = default;
Range addContent(const BookTypesetter::Utf8Span& text) Range addContent(std::string_view text)
{ {
Contents::iterator i = mContents.insert(mContents.end(), Content(text.first, text.second)); Content& content = mContents.emplace_back(text.begin(), text.end());
if (i->empty()) if (content.empty())
return Range(Utf8Point(nullptr), Utf8Point(nullptr)); return Range(nullptr, nullptr);
return Range(i->data(), i->data() + i->size()); return Range(content.data(), content.data() + content.size());
} }
size_t pageCount() const override { return mPages.size(); } size_t pageCount() const override { return mPages.size(); }
@ -222,19 +212,19 @@ namespace MWGui
template <typename Visitor> template <typename Visitor>
void visitRuns(int top, int bottom, MyGUI::IFont* font, Visitor const& visitor) const void visitRuns(int top, int bottom, MyGUI::IFont* font, Visitor const& visitor) const
{ {
for (Sections::const_iterator i = mSections.begin(); i != mSections.end(); ++i) for (const Section& section : mSections)
{ {
if (top >= mRect.bottom || bottom <= i->mRect.top) if (top >= mRect.bottom || bottom <= section.mRect.top)
continue; continue;
for (const Line& line : section.mLines)
for (Lines::const_iterator j = i->mLines.begin(); j != i->mLines.end(); ++j)
{ {
if (top >= j->mRect.bottom || bottom <= j->mRect.top) if (top >= line.mRect.bottom || bottom <= line.mRect.top)
continue; continue;
for (const Run& run : line.mRuns)
for (Runs::const_iterator k = j->mRuns.begin(); k != j->mRuns.end(); ++k) {
if (!font || k->mStyle->mFont == font) if (!font || run.mStyle->mFont == font)
visitor(*i, *j, *k); visitor(section, line, run);
}
} }
} }
} }
@ -275,26 +265,26 @@ namespace MWGui
StyleImpl* hitTest(int left, int top) const StyleImpl* hitTest(int left, int top) const
{ {
for (Sections::const_iterator i = mSections.begin(); i != mSections.end(); ++i) for (const Section& section : mSections)
{ {
if (top < i->mRect.top || top >= i->mRect.bottom) if (top < section.mRect.top || top >= section.mRect.bottom)
continue; continue;
int left1 = left - i->mRect.left; int left1 = left - section.mRect.left;
for (Lines::const_iterator j = i->mLines.begin(); j != i->mLines.end(); ++j) for (const Line& line : section.mLines)
{ {
if (top < j->mRect.top || top >= j->mRect.bottom) if (top < line.mRect.top || top >= line.mRect.bottom)
continue; continue;
int left2 = left1 - j->mRect.left; int left2 = left1 - line.mRect.left;
for (Runs::const_iterator k = j->mRuns.begin(); k != j->mRuns.end(); ++k) for (const Run& run : line.mRuns)
{ {
if (left2 < k->mLeft || left2 >= k->mRight) if (left2 < run.mLeft || left2 >= run.mRight)
continue; continue;
return k->mStyle; return run.mStyle;
} }
} }
} }
@ -304,9 +294,9 @@ namespace MWGui
MyGUI::IFont* affectedFont(StyleImpl* style) MyGUI::IFont* affectedFont(StyleImpl* style)
{ {
for (Styles::iterator i = mStyles.begin(); i != mStyles.end(); ++i) for (const StyleImpl& s : mStyles)
if (&*i == style) if (&s == style)
return i->mFont; return s.mFont;
return nullptr; return nullptr;
} }
@ -331,14 +321,10 @@ namespace MWGui
} }
}; };
typedef TypesetBookImpl Book;
typedef std::shared_ptr<Book> BookPtr;
typedef std::vector<PartialText>::const_iterator PartialTextConstIterator;
int mPageWidth; int mPageWidth;
int mPageHeight; int mPageHeight;
BookPtr mBook; std::shared_ptr<TypesetBookImpl> mBook;
Section* mSection; Section* mSection;
Line* mLine; Line* mLine;
Run* mRun; Run* mRun;
@ -347,7 +333,7 @@ namespace MWGui
std::vector<PartialText> mPartialWhitespace; std::vector<PartialText> mPartialWhitespace;
std::vector<PartialText> mPartialWord; std::vector<PartialText> mPartialWord;
Book::Content const* mCurrentContent; TypesetBookImpl::Content const* mCurrentContent;
Alignment mCurrentAlignment; Alignment mCurrentAlignment;
Typesetter(int width, int height) Typesetter(int width, int height)
@ -359,12 +345,12 @@ namespace MWGui
, mCurrentContent(nullptr) , mCurrentContent(nullptr)
, mCurrentAlignment(AlignLeft) , mCurrentAlignment(AlignLeft)
{ {
mBook = std::make_shared<Book>(); mBook = std::make_shared<TypesetBookImpl>();
} }
virtual ~Typesetter() = default; virtual ~Typesetter() = default;
Style* createStyle(const std::string& fontName, const Colour& fontColour, bool useBookFont) override Style* createStyle(const std::string& fontName, const MyGUI::Colour& fontColour, bool useBookFont) override
{ {
std::string fullFontName; std::string fullFontName;
if (fontName.empty()) if (fontName.empty())
@ -375,9 +361,9 @@ namespace MWGui
if (useBookFont) if (useBookFont)
fullFontName = "Journalbook " + fullFontName; fullFontName = "Journalbook " + fullFontName;
for (Styles::iterator i = mBook->mStyles.begin(); i != mBook->mStyles.end(); ++i) for (StyleImpl& style : mBook->mStyles)
if (i->match(fullFontName, fontColour, fontColour, fontColour, 0)) if (style.match(fullFontName, fontColour, fontColour, fontColour, 0))
return &*i; return &style;
MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fullFontName); MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fullFontName);
if (!font) if (!font)
@ -393,15 +379,15 @@ namespace MWGui
return &style; return &style;
} }
Style* createHotStyle(Style* baseStyle, const Colour& normalColour, const Colour& hoverColour, Style* createHotStyle(Style* baseStyle, const MyGUI::Colour& normalColour, const MyGUI::Colour& hoverColour,
const Colour& activeColour, InteractiveId id, bool unique) override const MyGUI::Colour& activeColour, InteractiveId id, bool unique) override
{ {
StyleImpl* const baseStyleImpl = static_cast<StyleImpl*>(baseStyle); StyleImpl* const baseStyleImpl = static_cast<StyleImpl*>(baseStyle);
if (!unique) if (!unique)
for (Styles::iterator i = mBook->mStyles.begin(); i != mBook->mStyles.end(); ++i) for (StyleImpl& style : mBook->mStyles)
if (i->match(baseStyleImpl->mFont, hoverColour, activeColour, normalColour, id)) if (style.match(baseStyleImpl->mFont, hoverColour, activeColour, normalColour, id))
return &*i; return &style;
StyleImpl& style = *mBook->mStyles.insert(mBook->mStyles.end(), StyleImpl()); StyleImpl& style = *mBook->mStyles.insert(mBook->mStyles.end(), StyleImpl());
@ -414,30 +400,30 @@ namespace MWGui
return &style; return &style;
} }
void write(Style* style, Utf8Span text) override void write(Style* style, std::string_view text) override
{ {
Range range = mBook->addContent(text); Range range = mBook->addContent(text);
writeImpl(static_cast<StyleImpl*>(style), range.first, range.second); writeImpl(static_cast<StyleImpl*>(style), Utf8Stream(range.first, range.second));
} }
intptr_t addContent(Utf8Span text, bool select) override const Content* addContent(std::string_view text, bool select) override
{ {
add_partial_text(); add_partial_text();
Contents::iterator i = mBook->mContents.insert(mBook->mContents.end(), Content(text.first, text.second)); Content& content = mBook->mContents.emplace_back(text.begin(), text.end());
if (select) if (select)
mCurrentContent = &(*i); mCurrentContent = &content;
return reinterpret_cast<intptr_t>(&(*i)); return &content;
} }
void selectContent(intptr_t contentHandle) override void selectContent(const Content* contentHandle) override
{ {
add_partial_text(); add_partial_text();
mCurrentContent = reinterpret_cast<Content const*>(contentHandle); mCurrentContent = contentHandle;
} }
void write(Style* style, size_t begin, size_t end) override void write(Style* style, size_t begin, size_t end) override
@ -446,10 +432,10 @@ namespace MWGui
assert(end <= mCurrentContent->size()); assert(end <= mCurrentContent->size());
assert(begin <= mCurrentContent->size()); assert(begin <= mCurrentContent->size());
const Utf8Point contentBegin = mCurrentContent->data() + begin; const Utf8Stream::Point contentBegin = mCurrentContent->data() + begin;
const Utf8Point contentEnd = mCurrentContent->data() + end; const Utf8Stream::Point contentEnd = mCurrentContent->data() + end;
writeImpl(static_cast<StyleImpl*>(style), contentBegin, contentEnd); writeImpl(static_cast<StyleImpl*>(style), Utf8Stream(contentBegin, contentEnd));
} }
void lineBreak(float margin) override void lineBreak(float margin) override
@ -486,7 +472,7 @@ namespace MWGui
mCurrentAlignment = sectionAlignment; mCurrentAlignment = sectionAlignment;
} }
TypesetBook::Ptr complete() override std::shared_ptr<TypesetBook> complete() override
{ {
int curPageStart = 0; int curPageStart = 0;
int curPageStop = 0; int curPageStop = 0;
@ -497,26 +483,26 @@ namespace MWGui
for (Sections::iterator i = mBook->mSections.begin(); i != mBook->mSections.end(); ++i, ++sa) for (Sections::iterator i = mBook->mSections.begin(); i != mBook->mSections.end(); ++i, ++sa)
{ {
// apply alignment to individual lines... // apply alignment to individual lines...
for (Lines::iterator j = i->mLines.begin(); j != i->mLines.end(); ++j) for (Line& line : i->mLines)
{ {
int width = j->mRect.width(); int width = line.mRect.width();
int excess = mPageWidth - width; int excess = mPageWidth - width;
switch (*sa) switch (*sa)
{ {
default: default:
case AlignLeft: case AlignLeft:
j->mRect.left = 0; line.mRect.left = 0;
break; break;
case AlignCenter: case AlignCenter:
j->mRect.left = excess / 2; line.mRect.left = excess / 2;
break; break;
case AlignRight: case AlignRight:
j->mRect.left = excess; line.mRect.left = excess;
break; break;
} }
j->mRect.right = j->mRect.left + width; line.mRect.right = line.mRect.left + width;
} }
if (curPageStop == curPageStart) if (curPageStop == curPageStart)
@ -541,7 +527,7 @@ namespace MWGui
// one. // one.
assert(curPageStart != curPageStop); assert(curPageStart != curPageStop);
mBook->mPages.push_back(Page(curPageStart, curPageStop)); mBook->mPages.emplace_back(curPageStart, curPageStop);
curPageStart = i->mRect.top; curPageStart = i->mRect.top;
curPageStop = i->mRect.bottom; curPageStop = i->mRect.bottom;
@ -553,7 +539,7 @@ namespace MWGui
{ {
// The section won't completely fit on the current page. Finish the current page and start a new // The section won't completely fit on the current page. Finish the current page and start a new
// one. // one.
mBook->mPages.push_back(Page(curPageStart, curPageStop)); mBook->mPages.emplace_back(curPageStart, curPageStop);
curPageStart = i->mRect.top; curPageStart = i->mRect.top;
curPageStop = i->mRect.bottom; curPageStop = i->mRect.bottom;
@ -564,16 +550,16 @@ namespace MWGui
{ {
// Adjust to the top of the first line that does not fit on the current page anymore // Adjust to the top of the first line that does not fit on the current page anymore
int splitPos = curPageStop; int splitPos = curPageStop;
for (Lines::iterator j = i->mLines.begin(); j != i->mLines.end(); ++j) for (const Line& line : i->mLines)
{ {
if (j->mRect.bottom > curPageStart + mPageHeight) if (line.mRect.bottom > curPageStart + mPageHeight)
{ {
splitPos = j->mRect.top; splitPos = line.mRect.top;
break; break;
} }
} }
mBook->mPages.push_back(Page(curPageStart, splitPos)); mBook->mPages.emplace_back(curPageStart, splitPos);
curPageStart = splitPos; curPageStart = splitPos;
curPageStop = splitPos; curPageStop = splitPos;
@ -584,15 +570,13 @@ namespace MWGui
} }
if (curPageStart != curPageStop) if (curPageStart != curPageStop)
mBook->mPages.push_back(Page(curPageStart, curPageStop)); mBook->mPages.emplace_back(curPageStart, curPageStop);
return mBook; return mBook;
} }
void writeImpl(StyleImpl* style, Utf8Stream::Point begin, Utf8Stream::Point end) void writeImpl(StyleImpl* style, Utf8Stream&& stream)
{ {
Utf8Stream stream(begin, end);
while (!stream.eof()) while (!stream.eof())
{ {
if (ucsLineBreak(stream.peek())) if (ucsLineBreak(stream.peek()))
@ -651,10 +635,10 @@ namespace MWGui
int spaceWidth = 0; int spaceWidth = 0;
int wordWidth = 0; int wordWidth = 0;
for (PartialTextConstIterator i = mPartialWhitespace.begin(); i != mPartialWhitespace.end(); ++i) for (const PartialText& partialText : mPartialWhitespace)
spaceWidth += i->mWidth; spaceWidth += partialText.mWidth;
for (PartialTextConstIterator i = mPartialWord.begin(); i != mPartialWord.end(); ++i) for (const PartialText& partialText : mPartialWord)
wordWidth += i->mWidth; wordWidth += partialText.mWidth;
int left = mLine ? mLine->mRect.right : 0; int left = mLine ? mLine->mRect.right : 0;
@ -666,21 +650,23 @@ namespace MWGui
} }
else else
{ {
for (PartialTextConstIterator i = mPartialWhitespace.begin(); i != mPartialWhitespace.end(); ++i) for (const PartialText& partialText : mPartialWhitespace)
{ {
int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; int top = mLine ? mLine->mRect.top : mBook->mRect.bottom;
appendRun(i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + fontHeight); appendRun(partialText.mStyle, partialText.mBegin, partialText.mEnd, 0, left + partialText.mWidth,
top + fontHeight);
left = mLine->mRect.right; left = mLine->mRect.right;
} }
} }
for (PartialTextConstIterator i = mPartialWord.begin(); i != mPartialWord.end(); ++i) for (const PartialText& partialText : mPartialWord)
{ {
int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; int top = mLine ? mLine->mRect.top : mBook->mRect.bottom;
const int numChars = static_cast<int>(i->mEnd - i->mBegin); const int numChars = static_cast<int>(partialText.mEnd - partialText.mBegin);
appendRun(i->mStyle, i->mBegin, i->mEnd, numChars, left + i->mWidth, top + fontHeight); appendRun(partialText.mStyle, partialText.mBegin, partialText.mEnd, numChars, left + partialText.mWidth,
top + fontHeight);
left = mLine->mRect.right; left = mLine->mRect.right;
} }
@ -747,7 +733,7 @@ namespace MWGui
} }
}; };
BookTypesetter::Ptr BookTypesetter::create(int pageWidth, int pageHeight) std::shared_ptr<BookTypesetter> BookTypesetter::create(int pageWidth, int pageHeight)
{ {
return std::make_shared<TypesetBookImpl::Typesetter>(pageWidth, pageHeight); return std::make_shared<TypesetBookImpl::Typesetter>(pageWidth, pageHeight);
} }
@ -932,17 +918,12 @@ namespace MWGui
{ {
MYGUI_RTTI_DERIVED(PageDisplay) MYGUI_RTTI_DERIVED(PageDisplay)
protected: protected:
typedef TypesetBookImpl::Section Section;
typedef TypesetBookImpl::Line Line;
typedef TypesetBookImpl::Run Run;
bool mIsPageReset; bool mIsPageReset;
size_t mPage; size_t mPage;
struct TextFormat : ISubWidget struct TextFormat : ISubWidget
{ {
typedef MyGUI::IFont* Id; MyGUI::IFont* mFont;
Id mFont;
int mCountVertex; int mCountVertex;
MyGUI::ITexture* mTexture; MyGUI::ITexture* mTexture;
MyGUI::RenderItem* mRenderItem; MyGUI::RenderItem* mRenderItem;
@ -1017,16 +998,15 @@ namespace MWGui
} }
public: public:
typedef TypesetBookImpl::StyleImpl Style; typedef std::map<MyGUI::IFont*, std::unique_ptr<TextFormat>> ActiveTextFormats;
typedef std::map<TextFormat::Id, std::unique_ptr<TextFormat>> ActiveTextFormats;
int mViewTop; int mViewTop;
int mViewBottom; int mViewBottom;
Style* mFocusItem; TypesetBookImpl::StyleImpl* mFocusItem;
bool mItemActive; bool mItemActive;
MyGUI::MouseButton mLastDown; MyGUI::MouseButton mLastDown;
std::function<void(intptr_t)> mLinkClicked; std::function<void(TypesetBook::InteractiveId)> mLinkClicked;
std::shared_ptr<TypesetBookImpl> mBook; std::shared_ptr<TypesetBookImpl> mBook;
@ -1072,7 +1052,7 @@ namespace MWGui
void onMouseMove(int left, int top) void onMouseMove(int left, int top)
{ {
Style* hit = nullptr; TypesetBookImpl::StyleImpl* hit = nullptr;
if (auto pos = getAdjustedPos(left, top, true)) if (auto pos = getAdjustedPos(left, top, true))
if (pos->top <= mViewBottom) if (pos->top <= mViewBottom)
hit = mBook->hitTestWithMargin(pos->left, pos->top); hit = mBook->hitTestWithMargin(pos->left, pos->top);
@ -1123,7 +1103,8 @@ namespace MWGui
if (pos && mLastDown == id) if (pos && mLastDown == id)
{ {
Style* item = pos->top <= mViewBottom ? mBook->hitTestWithMargin(pos->left, pos->top) : nullptr; TypesetBookImpl::StyleImpl* item
= pos->top <= mViewBottom ? mBook->hitTestWithMargin(pos->left, pos->top) : nullptr;
bool clicked = mFocusItem == item; bool clicked = mFocusItem == item;
@ -1138,7 +1119,7 @@ namespace MWGui
} }
} }
void showPage(TypesetBook::Ptr book, size_t newPage) void showPage(std::shared_ptr<TypesetBook> book, size_t newPage)
{ {
std::shared_ptr<TypesetBookImpl> newBook = std::dynamic_pointer_cast<TypesetBookImpl>(book); std::shared_ptr<TypesetBookImpl> newBook = std::dynamic_pointer_cast<TypesetBookImpl>(book);
@ -1212,7 +1193,8 @@ namespace MWGui
{ {
} }
void operator()(Section const& section, Line const& line, Run const& run) const void operator()(const TypesetBookImpl::Section& section, const TypesetBookImpl::Line& line,
const TypesetBookImpl::Run& run) const
{ {
MyGUI::IFont* const font = run.mStyle->mFont; MyGUI::IFont* const font = run.mStyle->mFont;
@ -1281,7 +1263,8 @@ namespace MWGui
{ {
} }
void operator()(Section const& section, Line const& line, Run const& run) const void operator()(const TypesetBookImpl::Section& section, const TypesetBookImpl::Line& line,
const TypesetBookImpl::Run& run) const
{ {
bool isActive = run.mStyle->mInteractiveId && (run.mStyle == mPageDisplay->mFocusItem); bool isActive = run.mStyle->mInteractiveId && (run.mStyle == mPageDisplay->mFocusItem);
@ -1374,14 +1357,20 @@ namespace MWGui
{ {
} }
void showPage(TypesetBook::Ptr book, size_t page) override { mPageDisplay->showPage(std::move(book), page); } void showPage(std::shared_ptr<TypesetBook> book, size_t page) override
{
mPageDisplay->showPage(std::move(book), page);
}
void adviseLinkClicked(std::function<void(InteractiveId)> linkClicked) override void adviseLinkClicked(std::function<void(TypesetBook::InteractiveId)> linkClicked) override
{ {
mPageDisplay->mLinkClicked = std::move(linkClicked); mPageDisplay->mLinkClicked = std::move(linkClicked);
} }
void unadviseLinkClicked() override { mPageDisplay->mLinkClicked = std::function<void(InteractiveId)>(); } void unadviseLinkClicked() override
{
mPageDisplay->mLinkClicked = std::function<void(TypesetBook::InteractiveId)>();
}
void setFocusItem(BookTypesetter::Style* itemStyle) override void setFocusItem(BookTypesetter::Style* itemStyle) override
{ {

View file

@ -8,6 +8,7 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <vector>
#include <components/settings/values.hpp> #include <components/settings/values.hpp>
@ -17,7 +18,7 @@ namespace MWGui
/// the book page widget. /// the book page widget.
struct TypesetBook struct TypesetBook
{ {
typedef std::shared_ptr<TypesetBook> Ptr; using Content = std::vector<uint8_t>;
typedef intptr_t InteractiveId; typedef intptr_t InteractiveId;
/// Returns the number of pages in the document. /// Returns the number of pages in the document.
@ -40,12 +41,6 @@ namespace MWGui
/// A factory class for creating a typeset book instance. /// A factory class for creating a typeset book instance.
struct BookTypesetter struct BookTypesetter
{ {
typedef std::shared_ptr<BookTypesetter> Ptr;
typedef TypesetBook::InteractiveId InteractiveId;
typedef MyGUI::Colour Colour;
typedef uint8_t const* Utf8Point;
typedef std::pair<Utf8Point, Utf8Point> Utf8Span;
virtual ~BookTypesetter() = default; virtual ~BookTypesetter() = default;
enum Alignment enum Alignment
@ -62,16 +57,18 @@ namespace MWGui
struct Style; struct Style;
/// A factory function for creating the default implementation of a book typesetter /// A factory function for creating the default implementation of a book typesetter
static Ptr create(int pageWidth, int pageHeight); static std::shared_ptr<BookTypesetter> create(int pageWidth, int pageHeight);
/// Create a simple text style consisting of a font and a text color. /// Create a simple text style consisting of a font and a text color.
virtual Style* createStyle(const std::string& fontName, const Colour& colour, bool useBookFont = true) = 0; virtual Style* createStyle(const std::string& fontName, const MyGUI::Colour& colour, bool useBookFont = true)
= 0;
/// Create a hyper-link style with a user-defined identifier based on an /// Create a hyper-link style with a user-defined identifier based on an
/// existing style. The unique flag forces a new instance of this style /// existing style. The unique flag forces a new instance of this style
/// to be created even if an existing instance is present. /// to be created even if an existing instance is present.
virtual Style* createHotStyle(Style* baseStyle, const Colour& normalColour, const Colour& hoverColour, virtual Style* createHotStyle(Style* baseStyle, const MyGUI::Colour& normalColour,
const Colour& activeColour, InteractiveId id, bool unique = true) const MyGUI::Colour& hoverColour, const MyGUI::Colour& activeColour, TypesetBook::InteractiveId id,
bool unique = true)
= 0; = 0;
/// Insert a line break into the document. Newline characters in the input /// Insert a line break into the document. Newline characters in the input
@ -89,22 +86,22 @@ namespace MWGui
virtual void setSectionAlignment(Alignment sectionAlignment) = 0; virtual void setSectionAlignment(Alignment sectionAlignment) = 0;
// Layout a block of text with the specified style into the document. // Layout a block of text with the specified style into the document.
virtual void write(Style* style, Utf8Span text) = 0; virtual void write(Style* style, std::string_view text) = 0;
/// Adds a content block to the document without laying it out. An /// Adds a content block to the document without laying it out. An
/// identifier is returned that can be used to refer to it. If select /// identifier is returned that can be used to refer to it. If select
/// is true, the block is activated to be references by future writes. /// is true, the block is activated to be references by future writes.
virtual intptr_t addContent(Utf8Span text, bool select = true) = 0; virtual const TypesetBook::Content* addContent(std::string_view text, bool select = true) = 0;
/// Select a previously created content block for future writes. /// Select a previously created content block for future writes.
virtual void selectContent(intptr_t contentHandle) = 0; virtual void selectContent(const TypesetBook::Content* contentHandle) = 0;
/// Layout a span of the selected content block into the document /// Layout a span of the selected content block into the document
/// using the specified style. /// using the specified style.
virtual void write(Style* style, size_t begin, size_t end) = 0; virtual void write(Style* style, size_t begin, size_t end) = 0;
/// Finalize the document layout, and return a pointer to it. /// Finalize the document layout, and return a pointer to it.
virtual TypesetBook::Ptr complete() = 0; virtual std::shared_ptr<TypesetBook> complete() = 0;
}; };
/// An interface to the BookPage widget. /// An interface to the BookPage widget.
@ -112,11 +109,10 @@ namespace MWGui
{ {
MYGUI_RTTI_DERIVED(BookPage) MYGUI_RTTI_DERIVED(BookPage)
public: public:
typedef TypesetBook::InteractiveId InteractiveId; using ClickCallback = std::function<void(TypesetBook::InteractiveId)>;
typedef std::function<void(InteractiveId)> ClickCallback;
/// Make the widget display the specified page from the specified book. /// Make the widget display the specified page from the specified book.
virtual void showPage(TypesetBook::Ptr book, size_t page) = 0; virtual void showPage(std::shared_ptr<TypesetBook> book, size_t page) = 0;
/// Set the callback for a clicking a hyper-link in the document. /// Set the callback for a clicking a hyper-link in the document.
virtual void adviseLinkClicked(ClickCallback callback) = 0; virtual void adviseLinkClicked(ClickCallback callback) = 0;

View file

@ -32,8 +32,6 @@
#include "bookpage.hpp" #include "bookpage.hpp"
#include "textcolours.hpp" #include "textcolours.hpp"
#include "journalbooks.hpp" // to_utf8_span
namespace MWGui namespace MWGui
{ {
void ResponseCallback::addResponse(std::string_view title, std::string_view text) void ResponseCallback::addResponse(std::string_view title, std::string_view text)
@ -208,7 +206,7 @@ namespace MWGui
mText = text; mText = text;
} }
void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, void Response::write(std::shared_ptr<BookTypesetter> typesetter, const TopicSearch& keywordSearch,
std::map<std::string, std::unique_ptr<Link>>& topicLinks) const std::map<std::string, std::unique_ptr<Link>>& topicLinks) const
{ {
typesetter->sectionBreak(mNeedMargin ? 9 : 0); typesetter->sectionBreak(mNeedMargin ? 9 : 0);
@ -218,12 +216,11 @@ namespace MWGui
{ {
const MyGUI::Colour& headerColour = windowManager->getTextColours().header; const MyGUI::Colour& headerColour = windowManager->getTextColours().header;
BookTypesetter::Style* title = typesetter->createStyle({}, headerColour, false); BookTypesetter::Style* title = typesetter->createStyle({}, headerColour, false);
typesetter->write(title, to_utf8_span(mTitle)); typesetter->write(title, mTitle);
typesetter->sectionBreak(); typesetter->sectionBreak();
} }
typedef std::pair<size_t, size_t> Range; std::map<std::pair<size_t, size_t>, const Link*> hyperLinks;
std::map<Range, intptr_t> hyperLinks;
// We need this copy for when @# hyperlinks are replaced // We need this copy for when @# hyperlinks are replaced
std::string text = mText; std::string text = mText;
@ -250,14 +247,13 @@ namespace MWGui
text.replace(posBegin, posEnd + 1 - posBegin, displayName); text.replace(posBegin, posEnd + 1 - posBegin, displayName);
if (topicLinks.find(topicName) != topicLinks.end()) if (topicLinks.find(topicName) != topicLinks.end())
hyperLinks[std::make_pair(posBegin, posBegin + displayName.size())] hyperLinks[std::make_pair(posBegin, posBegin + displayName.size())] = topicLinks[topicName].get();
= intptr_t(topicLinks[topicName].get());
} }
else else
break; break;
} }
typesetter->addContent(to_utf8_span(text)); typesetter->addContent(text);
if (hyperLinks.size() if (hyperLinks.size()
&& MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())
@ -266,48 +262,48 @@ namespace MWGui
BookTypesetter::Style* style = typesetter->createStyle({}, textColours.normal, false); BookTypesetter::Style* style = typesetter->createStyle({}, textColours.normal, false);
size_t formatted = 0; // points to the first character that is not laid out yet size_t formatted = 0; // points to the first character that is not laid out yet
for (auto& hyperLink : hyperLinks) for (const auto& [range, link] : hyperLinks)
{ {
intptr_t topicId = hyperLink.second; BookTypesetter::Style* hotStyle = typesetter->createHotStyle(style, textColours.link,
BookTypesetter::Style* hotStyle = typesetter->createHotStyle( textColours.linkOver, textColours.linkPressed, TypesetBook::InteractiveId(link));
style, textColours.link, textColours.linkOver, textColours.linkPressed, topicId); if (formatted < range.first)
if (formatted < hyperLink.first.first) typesetter->write(style, formatted, range.first);
typesetter->write(style, formatted, hyperLink.first.first); typesetter->write(hotStyle, range.first, range.second);
typesetter->write(hotStyle, hyperLink.first.first, hyperLink.first.second); formatted = range.second;
formatted = hyperLink.first.second;
} }
if (formatted < text.size()) if (formatted < text.size())
typesetter->write(style, formatted, text.size()); typesetter->write(style, formatted, text.size());
} }
else else
{ {
std::vector<KeywordSearchT::Match> matches; std::vector<TopicSearch::Match> matches;
keywordSearch->highlightKeywords(text.begin(), text.end(), matches); keywordSearch.highlightKeywords(text.begin(), text.end(), matches);
std::string::const_iterator i = text.begin(); std::string::const_iterator i = text.begin();
for (KeywordSearchT::Match& match : matches) for (TopicSearch::Match& match : matches)
{ {
if (i != match.mBeg) if (i != match.mBeg)
addTopicLink(typesetter, 0, i - text.begin(), match.mBeg - text.begin()); addTopicLink(typesetter, nullptr, i - text.begin(), match.mBeg - text.begin());
addTopicLink(typesetter, match.mValue, match.mBeg - text.begin(), match.mEnd - text.begin()); addTopicLink(typesetter, match.mValue, match.mBeg - text.begin(), match.mEnd - text.begin());
i = match.mEnd; i = match.mEnd;
} }
if (i != text.end()) if (i != text.end())
addTopicLink(std::move(typesetter), 0, i - text.begin(), text.size()); addTopicLink(std::move(typesetter), nullptr, i - text.begin(), text.size());
} }
} }
void Response::addTopicLink(BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const void Response::addTopicLink(
std::shared_ptr<BookTypesetter> typesetter, const MWGui::Topic* topic, size_t begin, size_t end) const
{ {
const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
BookTypesetter::Style* style = typesetter->createStyle({}, textColours.normal, false); BookTypesetter::Style* style = typesetter->createStyle({}, textColours.normal, false);
if (topicId) if (topic)
style = typesetter->createHotStyle( style = typesetter->createHotStyle(style, textColours.link, textColours.linkOver, textColours.linkPressed,
style, textColours.link, textColours.linkOver, textColours.linkPressed, topicId); TypesetBook::InteractiveId(topic));
typesetter->write(style, begin, end); typesetter->write(style, begin, end);
} }
@ -316,13 +312,13 @@ namespace MWGui
mText = text; mText = text;
} }
void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, void Message::write(std::shared_ptr<BookTypesetter> typesetter, const TopicSearch&,
std::map<std::string, std::unique_ptr<Link>>& topicLinks) const std::map<std::string, std::unique_ptr<Link>>&) const
{ {
const MyGUI::Colour& textColour = MWBase::Environment::get().getWindowManager()->getTextColours().notify; const MyGUI::Colour& textColour = MWBase::Environment::get().getWindowManager()->getTextColours().notify;
BookTypesetter::Style* title = typesetter->createStyle({}, textColour, false); BookTypesetter::Style* title = typesetter->createStyle({}, textColour, false);
typesetter->sectionBreak(9); typesetter->sectionBreak(9);
typesetter->write(title, to_utf8_span(mText)); typesetter->write(title, mText);
} }
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------
@ -661,7 +657,7 @@ namespace MWGui
mTopicsList->addItem(keyword, sVerticalPadding); mTopicsList->addItem(keyword, sVerticalPadding);
auto t = std::make_unique<Topic>(keyword); auto t = std::make_unique<Topic>(keyword);
mKeywordSearch.seed(topicId, intptr_t(t.get())); mKeywordSearch.seed(topicId, t.get());
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated); t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
mTopicLinks[topicId] = std::move(t); mTopicLinks[topicId] = std::move(t);
@ -689,10 +685,11 @@ namespace MWGui
mScrollBar->setVisible(true); mScrollBar->setVisible(true);
} }
BookTypesetter::Ptr typesetter = BookTypesetter::create(mHistory->getWidth(), std::numeric_limits<int>::max()); std::shared_ptr<BookTypesetter> typesetter
= BookTypesetter::create(mHistory->getWidth(), std::numeric_limits<int>::max());
for (const auto& text : mHistoryContents) for (const auto& text : mHistoryContents)
text->write(typesetter, &mKeywordSearch, mTopicLinks); text->write(typesetter, mKeywordSearch, mTopicLinks);
BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::White, false); BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::White, false);
@ -712,7 +709,7 @@ namespace MWGui
typesetter->lineBreak(); typesetter->lineBreak();
BookTypesetter::Style* questionStyle = typesetter->createHotStyle( BookTypesetter::Style* questionStyle = typesetter->createHotStyle(
body, textColours.answer, textColours.answerOver, textColours.answerPressed, interactiveId); body, textColours.answer, textColours.answerOver, textColours.answerPressed, interactiveId);
typesetter->write(questionStyle, to_utf8_span(choice.first)); typesetter->write(questionStyle, choice.first);
mChoiceStyles.push_back(questionStyle); mChoiceStyles.push_back(questionStyle);
} }
@ -731,10 +728,10 @@ namespace MWGui
BookTypesetter::Style* questionStyle = typesetter->createHotStyle( BookTypesetter::Style* questionStyle = typesetter->createHotStyle(
body, textColours.answer, textColours.answerOver, textColours.answerPressed, interactiveId); body, textColours.answer, textColours.answerOver, textColours.answerPressed, interactiveId);
typesetter->lineBreak(); typesetter->lineBreak();
typesetter->write(questionStyle, to_utf8_span(goodbye)); typesetter->write(questionStyle, goodbye);
} }
TypesetBook::Ptr book = typesetter->complete(); std::shared_ptr<TypesetBook> book = typesetter->complete();
mHistory->showPage(book, 0); mHistory->showPage(book, 0);
size_t viewHeight = mHistory->getParent()->getHeight(); size_t viewHeight = mHistory->getParent()->getHeight();
if (!scrollbar && book->getSize().second > viewHeight) if (!scrollbar && book->getSize().second > viewHeight)

View file

@ -79,14 +79,13 @@ namespace MWGui
struct Link struct Link
{ {
virtual ~Link() {} virtual ~Link() = default;
virtual void activated() = 0; virtual void activated() = 0;
}; };
struct Topic : Link struct Topic : Link
{ {
typedef MyGUI::delegates::MultiDelegate<const std::string&> EventHandle_TopicId; MyGUI::delegates::MultiDelegate<const std::string&> eventTopicActivated;
EventHandle_TopicId eventTopicActivated;
Topic(const std::string& id) Topic(const std::string& id)
: mTopicId(id) : mTopicId(id)
{ {
@ -97,8 +96,7 @@ namespace MWGui
struct Choice : Link struct Choice : Link
{ {
typedef MyGUI::delegates::MultiDelegate<int> EventHandle_ChoiceId; MyGUI::delegates::MultiDelegate<int> eventChoiceActivated;
EventHandle_ChoiceId eventChoiceActivated;
Choice(int id) Choice(int id)
: mChoiceId(id) : mChoiceId(id)
{ {
@ -109,27 +107,28 @@ namespace MWGui
struct Goodbye : Link struct Goodbye : Link
{ {
typedef MyGUI::delegates::MultiDelegate<> Event_Activated; MyGUI::delegates::MultiDelegate<> eventActivated;
Event_Activated eventActivated;
void activated() override; void activated() override;
}; };
typedef MWDialogue::KeywordSearch<intptr_t> KeywordSearchT; using TopicSearch = MWDialogue::KeywordSearch<const Topic*>;
struct DialogueText struct DialogueText
{ {
virtual ~DialogueText() = default; virtual ~DialogueText() = default;
virtual void write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, virtual void write(std::shared_ptr<BookTypesetter> typesetter, const TopicSearch& keywordSearch,
std::map<std::string, std::unique_ptr<Link>>& topicLinks) const = 0; std::map<std::string, std::unique_ptr<Link>>& topicLinks) const
= 0;
std::string mText; std::string mText;
}; };
struct Response : DialogueText struct Response : DialogueText
{ {
Response(std::string_view text, std::string_view title = {}, bool needMargin = true); Response(std::string_view text, std::string_view title = {}, bool needMargin = true);
void write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, void write(std::shared_ptr<BookTypesetter> typesetter, const TopicSearch& keywordSearch,
std::map<std::string, std::unique_ptr<Link>>& topicLinks) const override; std::map<std::string, std::unique_ptr<Link>>& topicLinks) const override;
void addTopicLink(BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const; void addTopicLink(
std::shared_ptr<BookTypesetter> typesetter, const Topic* topic, size_t begin, size_t end) const;
std::string mTitle; std::string mTitle;
bool mNeedMargin; bool mNeedMargin;
}; };
@ -137,7 +136,7 @@ namespace MWGui
struct Message : DialogueText struct Message : DialogueText
{ {
Message(std::string_view text); Message(std::string_view text);
void write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, void write(std::shared_ptr<BookTypesetter> typesetter, const TopicSearch& keywordSearch,
std::map<std::string, std::unique_ptr<Link>>& topicLinks) const override; std::map<std::string, std::unique_ptr<Link>>& topicLinks) const override;
}; };
@ -150,9 +149,6 @@ namespace MWGui
bool exit() override; bool exit() override;
// Events
typedef MyGUI::delegates::MultiDelegate<> EventHandle_Void;
void notifyLinkClicked(TypesetBook::InteractiveId link); void notifyLinkClicked(TypesetBook::InteractiveId link);
void setPtr(const MWWorld::Ptr& actor) override; void setPtr(const MWWorld::Ptr& actor) override;
@ -213,7 +209,7 @@ namespace MWGui
std::vector<std::unique_ptr<Link>> mDeleteLater; std::vector<std::unique_ptr<Link>> mDeleteLater;
KeywordSearchT mKeywordSearch; TopicSearch mKeywordSearch;
BookPage* mHistory; BookPage* mHistory;
Gui::MWList* mTopicsList; Gui::MWList* mTopicsList;

View file

@ -12,10 +12,10 @@ namespace
{ {
struct AddContent struct AddContent
{ {
MWGui::BookTypesetter::Ptr mTypesetter; std::shared_ptr<MWGui::BookTypesetter> mTypesetter;
MWGui::BookTypesetter::Style* mBodyStyle; MWGui::BookTypesetter::Style* mBodyStyle;
explicit AddContent(MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* bodyStyle) explicit AddContent(std::shared_ptr<MWGui::BookTypesetter> typesetter, MWGui::BookTypesetter::Style* bodyStyle)
: mTypesetter(std::move(typesetter)) : mTypesetter(std::move(typesetter))
, mBodyStyle(bodyStyle) , mBodyStyle(bodyStyle)
{ {
@ -24,19 +24,19 @@ namespace
struct AddSpan : AddContent struct AddSpan : AddContent
{ {
explicit AddSpan(MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* bodyStyle) explicit AddSpan(std::shared_ptr<MWGui::BookTypesetter> typesetter, MWGui::BookTypesetter::Style* bodyStyle)
: AddContent(std::move(typesetter), bodyStyle) : AddContent(std::move(typesetter), bodyStyle)
{ {
} }
void operator()(intptr_t topicId, size_t begin, size_t end) void operator()(const MWDialogue::Topic* topic, size_t begin, size_t end)
{ {
MWGui::BookTypesetter::Style* style = mBodyStyle; MWGui::BookTypesetter::Style* style = mBodyStyle;
const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
if (topicId) if (topic)
style = mTypesetter->createHotStyle(mBodyStyle, textColours.journalLink, textColours.journalLinkOver, style = mTypesetter->createHotStyle(mBodyStyle, textColours.journalLink, textColours.journalLinkOver,
textColours.journalLinkPressed, topicId); textColours.journalLinkPressed, MWGui::TypesetBook::InteractiveId(topic));
mTypesetter->write(style, begin, end); mTypesetter->write(style, begin, end);
} }
@ -44,10 +44,10 @@ namespace
struct AddEntry struct AddEntry
{ {
MWGui::BookTypesetter::Ptr mTypesetter; std::shared_ptr<MWGui::BookTypesetter> mTypesetter;
MWGui::BookTypesetter::Style* mBodyStyle; MWGui::BookTypesetter::Style* mBodyStyle;
AddEntry(MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* bodyStyle) AddEntry(std::shared_ptr<MWGui::BookTypesetter> typesetter, MWGui::BookTypesetter::Style* bodyStyle)
: mTypesetter(std::move(typesetter)) : mTypesetter(std::move(typesetter))
, mBodyStyle(bodyStyle) , mBodyStyle(bodyStyle)
{ {
@ -66,8 +66,8 @@ namespace
bool mAddHeader; bool mAddHeader;
MWGui::BookTypesetter::Style* mHeaderStyle; MWGui::BookTypesetter::Style* mHeaderStyle;
explicit AddJournalEntry(MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* bodyStyle, explicit AddJournalEntry(std::shared_ptr<MWGui::BookTypesetter> typesetter,
MWGui::BookTypesetter::Style* headerStyle, bool addHeader) MWGui::BookTypesetter::Style* bodyStyle, MWGui::BookTypesetter::Style* headerStyle, bool addHeader)
: AddEntry(std::move(typesetter), bodyStyle) : AddEntry(std::move(typesetter), bodyStyle)
, mAddHeader(addHeader) , mAddHeader(addHeader)
, mHeaderStyle(headerStyle) , mHeaderStyle(headerStyle)
@ -90,11 +90,12 @@ namespace
struct AddTopicEntry : AddEntry struct AddTopicEntry : AddEntry
{ {
intptr_t mContentId; const MWGui::TypesetBook::Content* mContentId;
MWGui::BookTypesetter::Style* mHeaderStyle; MWGui::BookTypesetter::Style* mHeaderStyle;
explicit AddTopicEntry(MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* bodyStyle, explicit AddTopicEntry(std::shared_ptr<MWGui::BookTypesetter> typesetter,
MWGui::BookTypesetter::Style* headerStyle, intptr_t contentId) MWGui::BookTypesetter::Style* bodyStyle, MWGui::BookTypesetter::Style* headerStyle,
const MWGui::TypesetBook::Content* contentId)
: AddEntry(std::move(typesetter), bodyStyle) : AddEntry(std::move(typesetter), bodyStyle)
, mContentId(contentId) , mContentId(contentId)
, mHeaderStyle(headerStyle) , mHeaderStyle(headerStyle)
@ -117,12 +118,12 @@ namespace
struct AddTopicName : AddContent struct AddTopicName : AddContent
{ {
AddTopicName(MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) AddTopicName(std::shared_ptr<MWGui::BookTypesetter> typesetter, MWGui::BookTypesetter::Style* style)
: AddContent(std::move(typesetter), style) : AddContent(std::move(typesetter), style)
{ {
} }
void operator()(MWGui::JournalViewModel::Utf8Span topicName) void operator()(std::string_view topicName)
{ {
mTypesetter->write(mBodyStyle, topicName); mTypesetter->write(mBodyStyle, topicName);
mTypesetter->sectionBreak(); mTypesetter->sectionBreak();
@ -131,12 +132,12 @@ namespace
struct AddQuestName : AddContent struct AddQuestName : AddContent
{ {
AddQuestName(MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) AddQuestName(std::shared_ptr<MWGui::BookTypesetter> typesetter, MWGui::BookTypesetter::Style* style)
: AddContent(std::move(typesetter), style) : AddContent(std::move(typesetter), style)
{ {
} }
void operator()(MWGui::JournalViewModel::Utf8Span topicName) void operator()(std::string_view topicName)
{ {
mTypesetter->write(mBodyStyle, topicName); mTypesetter->write(mBodyStyle, topicName);
mTypesetter->sectionBreak(); mTypesetter->sectionBreak();
@ -147,15 +148,6 @@ namespace
namespace MWGui namespace MWGui
{ {
MWGui::BookTypesetter::Utf8Span to_utf8_span(std::string_view text)
{
typedef MWGui::BookTypesetter::Utf8Point point;
point begin = reinterpret_cast<point>(text.data());
return MWGui::BookTypesetter::Utf8Span(begin, begin + text.length());
}
int getCyrillicIndexPageCount() int getCyrillicIndexPageCount()
{ {
// For small font size split alphabet to two columns (2x15 characers), for big font size split it to three // For small font size split alphabet to two columns (2x15 characers), for big font size split it to three
@ -163,33 +155,30 @@ namespace MWGui
return Settings::gui().mFontSize < 18 ? 2 : 3; return Settings::gui().mFontSize < 18 ? 2 : 3;
} }
typedef TypesetBook::Ptr book; JournalBooks::JournalBooks(std::shared_ptr<JournalViewModel> model, ToUTF8::FromType encoding)
JournalBooks::JournalBooks(JournalViewModel::Ptr model, ToUTF8::FromType encoding)
: mModel(std::move(model)) : mModel(std::move(model))
, mEncoding(encoding) , mEncoding(encoding)
, mIndexPagesCount(0) , mIndexPagesCount(0)
{ {
} }
book JournalBooks::createEmptyJournalBook() std::shared_ptr<TypesetBook> JournalBooks::createEmptyJournalBook()
{ {
BookTypesetter::Ptr typesetter = createTypesetter(); std::shared_ptr<BookTypesetter> typesetter = createTypesetter();
BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour); BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour);
BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black); BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black);
typesetter->write(header, to_utf8_span("You have no journal entries!")); typesetter->write(header, "You have no journal entries!");
typesetter->lineBreak(); typesetter->lineBreak();
typesetter->write( typesetter->write(body, "You should have gone though the starting quest and got an initial quest.");
body, to_utf8_span("You should have gone though the starting quest and got an initial quest."));
return typesetter->complete(); return typesetter->complete();
} }
book JournalBooks::createJournalBook() std::shared_ptr<TypesetBook> JournalBooks::createJournalBook()
{ {
BookTypesetter::Ptr typesetter = createTypesetter(); std::shared_ptr<BookTypesetter> typesetter = createTypesetter();
BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour); BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour);
BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black); BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black);
@ -199,49 +188,50 @@ namespace MWGui
return typesetter->complete(); return typesetter->complete();
} }
book JournalBooks::createTopicBook(uintptr_t topicId) std::shared_ptr<TypesetBook> JournalBooks::createTopicBook(const MWDialogue::Topic& topic)
{ {
BookTypesetter::Ptr typesetter = createTypesetter(); std::shared_ptr<BookTypesetter> typesetter = createTypesetter();
BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour); BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour);
BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black); BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black);
mModel->visitTopicName(topicId, AddTopicName(typesetter, header)); mModel->visitTopicName(topic, AddTopicName(typesetter, header));
intptr_t contentId = typesetter->addContent(to_utf8_span(", \"")); const TypesetBook::Content* contentId = typesetter->addContent(", \"");
mModel->visitTopicEntries(topicId, AddTopicEntry(typesetter, body, header, contentId)); mModel->visitTopicEntries(topic, AddTopicEntry(typesetter, body, header, contentId));
return typesetter->complete(); return typesetter->complete();
} }
book JournalBooks::createQuestBook(std::string_view questName) std::shared_ptr<TypesetBook> JournalBooks::createQuestBook(std::string_view questName)
{ {
BookTypesetter::Ptr typesetter = createTypesetter(); std::shared_ptr<BookTypesetter> typesetter = createTypesetter();
BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour); BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour);
BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black); BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black);
AddQuestName addName(typesetter, header); AddQuestName addName(typesetter, header);
addName(to_utf8_span(questName)); addName(questName);
mModel->visitJournalEntries(questName, AddJournalEntry(typesetter, body, header, false)); mModel->visitJournalEntries(questName, AddJournalEntry(typesetter, body, header, false));
return typesetter->complete(); return typesetter->complete();
} }
book JournalBooks::createTopicIndexBook() std::shared_ptr<TypesetBook> JournalBooks::createTopicIndexBook()
{ {
bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251);
BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex(); std::shared_ptr<BookTypesetter> typesetter
= isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex();
return typesetter->complete(); return typesetter->complete();
} }
BookTypesetter::Ptr JournalBooks::createLatinJournalIndex() std::shared_ptr<BookTypesetter> JournalBooks::createLatinJournalIndex()
{ {
BookTypesetter::Ptr typesetter = BookTypesetter::create(92, 260); std::shared_ptr<BookTypesetter> typesetter = BookTypesetter::create(92, 260);
typesetter->setSectionAlignment(BookTypesetter::AlignCenter); typesetter->setSectionAlignment(BookTypesetter::AlignCenter);
@ -260,12 +250,12 @@ namespace MWGui
const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
BookTypesetter::Style* style = typesetter->createHotStyle(body, textColours.journalTopic, BookTypesetter::Style* style = typesetter->createHotStyle(body, textColours.journalTopic,
textColours.journalTopicOver, textColours.journalTopicPressed, (Utf8Stream::UnicodeChar)ch); textColours.journalTopicOver, textColours.journalTopicPressed, Utf8Stream::UnicodeChar(ch));
if (i == 13) if (i == 13)
typesetter->sectionBreak(); typesetter->sectionBreak();
typesetter->write(style, to_utf8_span(buffer)); typesetter->write(style, buffer);
typesetter->lineBreak(); typesetter->lineBreak();
ch++; ch++;
@ -274,9 +264,9 @@ namespace MWGui
return typesetter; return typesetter;
} }
BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex() std::shared_ptr<BookTypesetter> JournalBooks::createCyrillicJournalIndex()
{ {
BookTypesetter::Ptr typesetter = BookTypesetter::create(92, 260); std::shared_ptr<BookTypesetter> typesetter = BookTypesetter::create(92, 260);
typesetter->setSectionAlignment(BookTypesetter::AlignCenter); typesetter->setSectionAlignment(BookTypesetter::AlignCenter);
@ -314,14 +304,14 @@ namespace MWGui
if (i % sectionBreak == 0) if (i % sectionBreak == 0)
typesetter->sectionBreak(); typesetter->sectionBreak();
typesetter->write(style, to_utf8_span(buffer)); typesetter->write(style, buffer);
typesetter->lineBreak(); typesetter->lineBreak();
} }
return typesetter; return typesetter;
} }
BookTypesetter::Ptr JournalBooks::createTypesetter() std::shared_ptr<BookTypesetter> JournalBooks::createTypesetter()
{ {
// TODO: determine page size from layout... // TODO: determine page size from layout...
return BookTypesetter::create(240, 320); return BookTypesetter::create(240, 320);

View file

@ -8,31 +8,29 @@
namespace MWGui namespace MWGui
{ {
MWGui::BookTypesetter::Utf8Span to_utf8_span(std::string_view text);
int getCyrillicIndexPageCount(); int getCyrillicIndexPageCount();
const MyGUI::Colour journalHeaderColour = MyGUI::Colour(0.60f, 0.00f, 0.00f); const MyGUI::Colour journalHeaderColour = MyGUI::Colour(0.60f, 0.00f, 0.00f);
struct JournalBooks struct JournalBooks
{ {
typedef TypesetBook::Ptr Book; std::shared_ptr<JournalViewModel> mModel;
JournalViewModel::Ptr mModel;
JournalBooks(JournalViewModel::Ptr model, ToUTF8::FromType encoding); JournalBooks(std::shared_ptr<JournalViewModel> model, ToUTF8::FromType encoding);
Book createEmptyJournalBook(); std::shared_ptr<TypesetBook> createEmptyJournalBook();
Book createJournalBook(); std::shared_ptr<TypesetBook> createJournalBook();
Book createTopicBook(uintptr_t topicId); std::shared_ptr<TypesetBook> createTopicBook(const MWDialogue::Topic& topic);
Book createQuestBook(std::string_view questName); std::shared_ptr<TypesetBook> createQuestBook(std::string_view questName);
Book createTopicIndexBook(); std::shared_ptr<TypesetBook> createTopicIndexBook();
ToUTF8::FromType mEncoding; ToUTF8::FromType mEncoding;
int mIndexPagesCount; int mIndexPagesCount;
private: private:
BookTypesetter::Ptr createTypesetter(); std::shared_ptr<BookTypesetter> createTypesetter();
BookTypesetter::Ptr createLatinJournalIndex(); std::shared_ptr<BookTypesetter> createLatinJournalIndex();
BookTypesetter::Ptr createCyrillicJournalIndex(); std::shared_ptr<BookTypesetter> createCyrillicJournalIndex();
}; };
} }

View file

@ -22,26 +22,15 @@ namespace MWGui
struct JournalViewModelImpl : JournalViewModel struct JournalViewModelImpl : JournalViewModel
{ {
typedef MWDialogue::KeywordSearch<intptr_t> KeywordSearchT; using TopicSearch = MWDialogue::KeywordSearch<const MWDialogue::Topic*>;
mutable bool mKeywordSearchLoaded; mutable bool mKeywordSearchLoaded;
mutable KeywordSearchT mKeywordSearch; mutable TopicSearch mKeywordSearch;
JournalViewModelImpl() { mKeywordSearchLoaded = false; } JournalViewModelImpl() { mKeywordSearchLoaded = false; }
virtual ~JournalViewModelImpl() = default; virtual ~JournalViewModelImpl() = default;
/// \todo replace this nasty BS
static Utf8Span toUtf8Span(std::string_view str)
{
if (str.empty())
return Utf8Span(Utf8Point(nullptr), Utf8Point(nullptr));
Utf8Point point = reinterpret_cast<Utf8Point>(str.data());
return Utf8Span(point, point + str.size());
}
void load() override {} void load() override {}
void unload() override void unload() override
@ -57,7 +46,7 @@ namespace MWGui
MWBase::Journal* journal = MWBase::Environment::get().getJournal(); MWBase::Journal* journal = MWBase::Environment::get().getJournal();
for (const auto& [_, topic] : journal->getTopics()) for (const auto& [_, topic] : journal->getTopics())
mKeywordSearch.seed(topic.getName(), intptr_t(&topic)); mKeywordSearch.seed(topic.getName(), &topic);
mKeywordSearchLoaded = true; mKeywordSearchLoaded = true;
} }
@ -88,10 +77,8 @@ namespace MWGui
mutable bool loaded; mutable bool loaded;
mutable std::string utf8text; mutable std::string utf8text;
typedef std::pair<size_t, size_t> Range;
// hyperlinks in @link# notation // hyperlinks in @link# notation
mutable std::map<Range, intptr_t> mHyperLinks; mutable std::map<std::pair<size_t, size_t>, const MWDialogue::Topic*> mHyperLinks;
virtual std::string getText() const = 0; virtual std::string getText() const = 0;
@ -126,7 +113,7 @@ namespace MWGui
utf8text.replace(posBegin, posEnd + 1 - posBegin, displayName); utf8text.replace(posBegin, posEnd + 1 - posBegin, displayName);
intptr_t value = 0; const MWDialogue::Topic* value = nullptr;
if (mModel->mKeywordSearch.containsKeyword(topicName, value)) if (mModel->mKeywordSearch.containsKeyword(topicName, value))
mHyperLinks[std::make_pair(posBegin, posBegin + displayName.size())] = value; mHyperLinks[std::make_pair(posBegin, posBegin + displayName.size())] = value;
} }
@ -138,14 +125,14 @@ namespace MWGui
} }
} }
Utf8Span body() const override std::string_view body() const override
{ {
ensureLoaded(); ensureLoaded();
return toUtf8Span(utf8text); return utf8text;
} }
void visitSpans(std::function<void(TopicId, size_t, size_t)> visitor) const override void visitSpans(std::function<void(const MWDialogue::Topic*, size_t, size_t)> visitor) const override
{ {
ensureLoaded(); ensureLoaded();
mModel->ensureKeyWordSearchLoaded(); mModel->ensureKeyWordSearchLoaded();
@ -154,25 +141,23 @@ namespace MWGui
&& MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())
{ {
size_t formatted = 0; // points to the first character that is not laid out yet size_t formatted = 0; // points to the first character that is not laid out yet
for (std::map<Range, intptr_t>::const_iterator it = mHyperLinks.begin(); it != mHyperLinks.end(); for (const auto& [range, topicId] : mHyperLinks)
++it)
{ {
intptr_t topicId = it->second; if (formatted < range.first)
if (formatted < it->first.first) visitor(0, formatted, range.first);
visitor(0, formatted, it->first.first); visitor(topicId, range.first, range.second);
visitor(topicId, it->first.first, it->first.second); formatted = range.second;
formatted = it->first.second;
} }
if (formatted < utf8text.size()) if (formatted < utf8text.size())
visitor(0, formatted, utf8text.size()); visitor(0, formatted, utf8text.size());
} }
else else
{ {
std::vector<KeywordSearchT::Match> matches; std::vector<TopicSearch::Match> matches;
mModel->mKeywordSearch.highlightKeywords(utf8text.begin(), utf8text.end(), matches); mModel->mKeywordSearch.highlightKeywords(utf8text.begin(), utf8text.end(), matches);
std::string::const_iterator i = utf8text.begin(); std::string::const_iterator i = utf8text.begin();
for (const KeywordSearchT::Match& match : matches) for (const TopicSearch::Match& match : matches)
{ {
if (i != match.mBeg) if (i != match.mBeg)
visitor(0, i - utf8text.begin(), match.mBeg - utf8text.begin()); visitor(0, i - utf8text.begin(), match.mBeg - utf8text.begin());
@ -231,7 +216,7 @@ namespace MWGui
std::string getText() const override { return mEntry->getText(); } std::string getText() const override { return mEntry->getText(); }
Utf8Span timestamp() const override std::string_view timestamp() const override
{ {
if (timestamp_buffer.empty()) if (timestamp_buffer.empty())
{ {
@ -246,7 +231,7 @@ namespace MWGui
timestamp_buffer = os.str(); timestamp_buffer = os.str();
} }
return toUtf8Span(timestamp_buffer); return timestamp_buffer;
} }
}; };
@ -288,10 +273,10 @@ namespace MWGui
} }
} }
void visitTopicName(TopicId topicId, std::function<void(Utf8Span)> visitor) const override void visitTopicName(
const MWDialogue::Topic& topic, std::function<void(std::string_view)> visitor) const override
{ {
MWDialogue::Topic const& topic = *reinterpret_cast<MWDialogue::Topic const*>(topicId); visitor(topic.getName());
visitor(toUtf8Span(topic.getName()));
} }
void visitTopicNamesStartingWith( void visitTopicNamesStartingWith(
@ -324,19 +309,18 @@ namespace MWGui
std::string getText() const override { return mEntry->getText(); } std::string getText() const override { return mEntry->getText(); }
Utf8Span source() const override { return toUtf8Span(mEntry->mActorName); } std::string_view source() const override { return mEntry->mActorName; }
}; };
void visitTopicEntries(TopicId topicId, std::function<void(TopicEntry const&)> visitor) const override void visitTopicEntries(
const MWDialogue::Topic& topic, std::function<void(TopicEntry const&)> visitor) const override
{ {
MWDialogue::Topic const& topic = *reinterpret_cast<MWDialogue::Topic const*>(topicId);
for (const MWDialogue::Entry& entry : topic) for (const MWDialogue::Entry& entry : topic)
visitor(TopicEntryImpl(this, topic, entry)); visitor(TopicEntryImpl(this, topic, entry));
} }
}; };
JournalViewModel::Ptr JournalViewModel::create() std::shared_ptr<JournalViewModel> JournalViewModel::create()
{ {
return std::make_shared<JournalViewModelImpl>(); return std::make_shared<JournalViewModelImpl>();
} }

View file

@ -8,6 +8,11 @@
#include <components/misc/utf8stream.hpp> #include <components/misc/utf8stream.hpp>
namespace MWDialogue
{
class Topic;
}
namespace MWGui namespace MWGui
{ {
/// View-Model for the journal GUI /// View-Model for the journal GUI
@ -18,13 +23,6 @@ namespace MWGui
/// game data store. /// game data store.
struct JournalViewModel struct JournalViewModel
{ {
typedef std::shared_ptr<JournalViewModel> Ptr;
typedef intptr_t QuestId;
typedef intptr_t TopicId;
typedef uint8_t const* Utf8Point;
typedef std::pair<Utf8Point, Utf8Point> Utf8Span;
/// The base interface for both journal entries and topics. /// The base interface for both journal entries and topics.
struct Entry struct Entry
{ {
@ -33,12 +31,12 @@ namespace MWGui
/// This function returns a borrowed reference to the body of the /// This function returns a borrowed reference to the body of the
/// journal entry. The returned reference becomes invalid when the /// journal entry. The returned reference becomes invalid when the
/// entry is destroyed. /// entry is destroyed.
virtual Utf8Span body() const = 0; virtual std::string_view body() const = 0;
/// Visits each subset of text in the body, delivering the beginning /// Visits each subset of text in the body, delivering the beginning
/// and end of the span relative to the body, and a valid topic ID if /// and end of the span relative to the body, and a valid topic ID if
/// the span represents a keyword, or zero if not. /// the span represents a keyword, or zero if not.
virtual void visitSpans(std::function<void(TopicId, size_t, size_t)> visitor) const = 0; virtual void visitSpans(std::function<void(const MWDialogue::Topic*, size_t, size_t)> visitor) const = 0;
virtual ~Entry() = default; virtual ~Entry() = default;
}; };
@ -48,7 +46,7 @@ namespace MWGui
{ {
/// Returns a pre-formatted span of UTF8 encoded text representing /// Returns a pre-formatted span of UTF8 encoded text representing
/// the name of the NPC this portion of dialog was heard from. /// the name of the NPC this portion of dialog was heard from.
virtual Utf8Span source() const = 0; virtual std::string_view source() const = 0;
virtual ~TopicEntry() = default; virtual ~TopicEntry() = default;
}; };
@ -58,7 +56,7 @@ namespace MWGui
{ {
/// Returns a pre-formatted span of UTF8 encoded text representing /// Returns a pre-formatted span of UTF8 encoded text representing
/// the in-game date this entry was added to the journal. /// the in-game date this entry was added to the journal.
virtual Utf8Span timestamp() const = 0; virtual std::string_view timestamp() const = 0;
virtual ~JournalEntry() = default; virtual ~JournalEntry() = default;
}; };
@ -78,20 +76,25 @@ namespace MWGui
/// walks over the journal entries related to all quests with the given name /// walks over the journal entries related to all quests with the given name
/// If \a questName is empty, simply visits all journal entries /// If \a questName is empty, simply visits all journal entries
virtual void visitJournalEntries( virtual void visitJournalEntries(
std::string_view questName, std::function<void(JournalEntry const&)> visitor) const = 0; std::string_view questName, std::function<void(JournalEntry const&)> visitor) const
= 0;
/// provides the name of the topic specified by its id /// provides the name of the topic specified by its id
virtual void visitTopicName(TopicId topicId, std::function<void(Utf8Span)> visitor) const = 0; virtual void visitTopicName(const MWDialogue::Topic& topic, std::function<void(std::string_view)> visitor) const
= 0;
/// walks over the topics whose names start with the character /// walks over the topics whose names start with the character
virtual void visitTopicNamesStartingWith( virtual void visitTopicNamesStartingWith(
Utf8Stream::UnicodeChar character, std::function<void(std::string_view)> visitor) const = 0; Utf8Stream::UnicodeChar character, std::function<void(std::string_view)> visitor) const
= 0;
/// walks over the topic entries for the topic specified by its identifier /// walks over the topic entries for the topic specified by its identifier
virtual void visitTopicEntries(TopicId topicId, std::function<void(TopicEntry const&)> visitor) const = 0; virtual void visitTopicEntries(
const MWDialogue::Topic& topic, std::function<void(TopicEntry const&)> visitor) const
= 0;
// create an instance of the default journal view model implementation // create an instance of the default journal view model implementation
static Ptr create(); static std::shared_ptr<JournalViewModel> create();
virtual ~JournalViewModel() = default; virtual ~JournalViewModel() = default;
}; };

View file

@ -50,13 +50,11 @@ namespace
struct DisplayState struct DisplayState
{ {
size_t mPage; size_t mPage;
Book mBook; std::shared_ptr<MWGui::TypesetBook> mBook;
}; };
typedef std::stack<DisplayState> DisplayStateStack; std::stack<DisplayState> mStates;
std::shared_ptr<MWGui::TypesetBook> mTopicIndexBook;
DisplayStateStack mStates;
Book mTopicIndexBook;
bool mQuestMode; bool mQuestMode;
bool mOptionsMode; bool mOptionsMode;
bool mTopicsMode; bool mTopicsMode;
@ -91,7 +89,7 @@ namespace
MWGui::BookPage* getPage(std::string_view name) { return getWidget<MWGui::BookPage>(name); } MWGui::BookPage* getPage(std::string_view name) { return getWidget<MWGui::BookPage>(name); }
JournalWindowImpl(MWGui::JournalViewModel::Ptr model, bool questList, ToUTF8::FromType encoding) JournalWindowImpl(std::shared_ptr<MWGui::JournalViewModel> model, bool questList, ToUTF8::FromType encoding)
: JournalBooks(std::move(model), encoding) : JournalBooks(std::move(model), encoding)
, JournalWindow() , JournalWindow()
{ {
@ -123,7 +121,10 @@ namespace
topicsList->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyTopicSelected); topicsList->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyTopicSelected);
{ {
MWGui::BookPage::ClickCallback callback = [this](intptr_t linkId) { notifyTopicClicked(linkId); }; MWGui::BookPage::ClickCallback callback = [this](MWGui::TypesetBook::InteractiveId linkId) {
const MWDialogue::Topic& topic = *reinterpret_cast<const MWDialogue::Topic*>(linkId);
notifyTopicClicked(topic);
};
getPage(LeftBookPage)->adviseLinkClicked(callback); getPage(LeftBookPage)->adviseLinkClicked(callback);
getPage(RightBookPage)->adviseLinkClicked(std::move(callback)); getPage(RightBookPage)->adviseLinkClicked(std::move(callback));
@ -135,8 +136,9 @@ namespace
} }
{ {
MWGui::BookPage::ClickCallback callback MWGui::BookPage::ClickCallback callback = [this](MWGui::TypesetBook::InteractiveId index) {
= [this](MWGui::TypesetBook::InteractiveId index) { notifyIndexLinkClicked(index); }; notifyIndexLinkClicked(static_cast<Utf8Stream::UnicodeChar>(index));
};
getPage(LeftTopicIndex)->adviseLinkClicked(callback); getPage(LeftTopicIndex)->adviseLinkClicked(callback);
getPage(CenterTopicIndex)->adviseLinkClicked(callback); getPage(CenterTopicIndex)->adviseLinkClicked(callback);
@ -240,7 +242,7 @@ namespace
setBookMode(); setBookMode();
Book journalBook; std::shared_ptr<MWGui::TypesetBook> journalBook;
if (mModel->isEmpty()) if (mModel->isEmpty())
journalBook = createEmptyJournalBook(); journalBook = createEmptyJournalBook();
else else
@ -268,8 +270,8 @@ namespace
{ {
mModel->unload(); mModel->unload();
getPage(LeftBookPage)->showPage(Book(), 0); getPage(LeftBookPage)->showPage({}, 0);
getPage(RightBookPage)->showPage(Book(), 0); getPage(RightBookPage)->showPage({}, 0);
while (!mStates.empty()) while (!mStates.empty())
mStates.pop(); mStates.pop();
@ -315,7 +317,7 @@ namespace
// TODO: figure out how to make "options" page overlay book page // TODO: figure out how to make "options" page overlay book page
// correctly, so that text may show underneath // correctly, so that text may show underneath
getPage(RightBookPage)->showPage(Book(), 0); getPage(RightBookPage)->showPage({}, 0);
// If in quest mode, ensure the quest list is updated // If in quest mode, ensure the quest list is updated
if (mQuestMode) if (mQuestMode)
@ -326,7 +328,7 @@ namespace
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
} }
void pushBook(Book& book) void pushBook(std::shared_ptr<MWGui::TypesetBook>& book)
{ {
DisplayState bs; DisplayState bs;
bs.mPage = 0; bs.mPage = 0;
@ -336,7 +338,7 @@ namespace
updateCloseJournalButton(); updateCloseJournalButton();
} }
void replaceBook(Book& book) void replaceBook(std::shared_ptr<MWGui::TypesetBook>& book)
{ {
assert(!mStates.empty()); assert(!mStates.empty());
mStates.top().mBook = book; mStates.top().mBook = book;
@ -360,7 +362,7 @@ namespace
void updateShowingPages() void updateShowingPages()
{ {
Book book; std::shared_ptr<MWGui::TypesetBook> book;
size_t page; size_t page;
size_t relPages; size_t relPages;
@ -393,8 +395,16 @@ namespace
setVisible(PageOneNum, relPages > 0); setVisible(PageOneNum, relPages > 0);
setVisible(PageTwoNum, relPages > 1); setVisible(PageTwoNum, relPages > 1);
getPage(LeftBookPage)->showPage((relPages > 0) ? book : Book(), page + 0); if (relPages > 0)
getPage(RightBookPage)->showPage((relPages > 0) ? std::move(book) : Book(), page + 1); {
getPage(LeftBookPage)->showPage(book, page + 0);
getPage(RightBookPage)->showPage(std::move(book), page + 1);
}
else
{
getPage(LeftBookPage)->showPage({}, page + 0);
getPage(RightBookPage)->showPage({}, page + 1);
}
setText(PageOneNum, page + 1); setText(PageOneNum, page + 1);
setText(PageTwoNum, page + 2); setText(PageTwoNum, page + 2);
@ -410,9 +420,9 @@ namespace
notifyNextPage(sender); notifyNextPage(sender);
} }
void notifyTopicClicked(intptr_t linkId) void notifyTopicClicked(const MWDialogue::Topic& topic)
{ {
Book topicBook = createTopicBook(linkId); std::shared_ptr<MWGui::TypesetBook> topicBook = createTopicBook(topic);
if (mStates.size() > 1) if (mStates.size() > 1)
replaceBook(topicBook); replaceBook(topicBook);
@ -434,17 +444,14 @@ namespace
{ {
ESM::RefId topic = ESM::RefId::stringRefId(topicIdString); ESM::RefId topic = ESM::RefId::stringRefId(topicIdString);
const MWBase::Journal* journal = MWBase::Environment::get().getJournal(); const MWBase::Journal* journal = MWBase::Environment::get().getJournal();
intptr_t topicId = 0; /// \todo get rid of intptr ids
const auto it = journal->getTopics().find(topic); const auto it = journal->getTopics().find(topic);
if (it != journal->getTopics().end()) if (it != journal->getTopics().end())
topicId = intptr_t(&it->second); notifyTopicClicked(it->second);
notifyTopicClicked(topicId);
} }
void notifyQuestClicked(const std::string& name, int id) void notifyQuestClicked(const std::string& name, int id)
{ {
Book book = createQuestBook(name); std::shared_ptr<MWGui::TypesetBook> book = createQuestBook(name);
if (mStates.size() > 1) if (mStates.size() > 1)
replaceBook(book); replaceBook(book);
@ -508,7 +515,7 @@ namespace
} }
} }
void notifyIndexLinkClicked(MWGui::TypesetBook::InteractiveId index) void notifyIndexLinkClicked(Utf8Stream::UnicodeChar index)
{ {
setVisible(LeftTopicIndex, false); setVisible(LeftTopicIndex, false);
setVisible(CenterTopicIndex, false); setVisible(CenterTopicIndex, false);
@ -664,7 +671,7 @@ namespace
if (!mStates.empty()) if (!mStates.empty())
{ {
size_t& page = mStates.top().mPage; size_t& page = mStates.top().mPage;
Book book = mStates.top().mBook; std::shared_ptr<MWGui::TypesetBook> book = mStates.top().mBook;
if (page + 2 < book->pageCount()) if (page + 2 < book->pageCount())
{ {
@ -785,7 +792,8 @@ namespace
if (mSelectedIndex >= 27) if (mSelectedIndex >= 27)
russianOffset++; // 27, not 28, because of skipping char 26 russianOffset++; // 27, not 28, because of skipping char 26
bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251);
notifyIndexLinkClicked(isRussian ? mSelectedIndex + russianOffset : mSelectedIndex + 'A'); size_t ch = isRussian ? mSelectedIndex + russianOffset : mSelectedIndex + 'A';
notifyIndexLinkClicked(static_cast<Utf8Stream::UnicodeChar>(ch));
} }
} }
else if (arg.button == SDL_CONTROLLER_BUTTON_B) // B: Back else if (arg.button == SDL_CONTROLLER_BUTTON_B) // B: Back
@ -945,7 +953,7 @@ namespace
// glue the implementation to the interface // glue the implementation to the interface
std::unique_ptr<MWGui::JournalWindow> MWGui::JournalWindow::create( std::unique_ptr<MWGui::JournalWindow> MWGui::JournalWindow::create(
JournalViewModel::Ptr model, bool questList, ToUTF8::FromType encoding) std::shared_ptr<JournalViewModel> model, bool questList, ToUTF8::FromType encoding)
{ {
return std::make_unique<JournalWindowImpl>(model, questList, encoding); return std::make_unique<JournalWindowImpl>(model, questList, encoding);
} }