1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-11-28 17:04:31 +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
{
typedef std::vector<uint8_t> Content;
typedef std::list<Content> Contents;
typedef Utf8Stream::Point Utf8Point;
typedef std::pair<Utf8Point, Utf8Point> Range;
typedef std::pair<Utf8Stream::Point, Utf8Stream::Point> Range;
struct StyleImpl : BookTypesetter::Style
{
@ -127,29 +124,28 @@ namespace MWGui
InteractiveId mInteractiveId;
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)
&& partal_match(tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId);
&& partialMatch(tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId);
}
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)
&& partal_match(tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId);
&& partialMatch(tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId);
}
bool partal_match(const MyGUI::Colour& tstHotColour, const MyGUI::Colour& tstActiveColour,
const MyGUI::Colour& tstNormalColour, intptr_t tstInteractiveId)
bool partialMatch(const MyGUI::Colour& tstHotColour, const MyGUI::Colour& tstActiveColour,
const MyGUI::Colour& tstNormalColour, InteractiveId tstInteractiveId) const
{
return (mHotColour == tstHotColour) && (mActiveColour == tstActiveColour)
&& (mNormalColour == tstNormalColour) && (mInteractiveId == tstInteractiveId);
}
};
typedef std::list<StyleImpl> Styles;
struct Run
{
StyleImpl* mStyle;
@ -158,19 +154,15 @@ namespace MWGui
int mPrintableChars;
};
typedef std::vector<Run> Runs;
struct Line
{
Runs mRuns;
std::vector<Run> mRuns;
MyGUI::IntRect mRect;
};
typedef std::vector<Line> Lines;
struct Section
{
Lines mLines;
std::vector<Line> mLines;
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.
typedef std::pair<int, int> Page;
typedef std::vector<Page> Pages;
Pages mPages;
std::vector<Page> mPages;
Sections mSections;
Contents mContents;
Styles mStyles;
std::list<Content> mContents;
std::list<StyleImpl> mStyles;
MyGUI::IntRect mRect;
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;
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())
return Range(Utf8Point(nullptr), Utf8Point(nullptr));
if (content.empty())
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(); }
@ -222,19 +212,19 @@ namespace MWGui
template <typename Visitor>
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;
for (Lines::const_iterator j = i->mLines.begin(); j != i->mLines.end(); ++j)
for (const Line& line : section.mLines)
{
if (top >= j->mRect.bottom || bottom <= j->mRect.top)
if (top >= line.mRect.bottom || bottom <= line.mRect.top)
continue;
for (Runs::const_iterator k = j->mRuns.begin(); k != j->mRuns.end(); ++k)
if (!font || k->mStyle->mFont == font)
visitor(*i, *j, *k);
for (const Run& run : line.mRuns)
{
if (!font || run.mStyle->mFont == font)
visitor(section, line, run);
}
}
}
}
@ -275,26 +265,26 @@ namespace MWGui
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;
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;
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;
return k->mStyle;
return run.mStyle;
}
}
}
@ -304,9 +294,9 @@ namespace MWGui
MyGUI::IFont* affectedFont(StyleImpl* style)
{
for (Styles::iterator i = mStyles.begin(); i != mStyles.end(); ++i)
if (&*i == style)
return i->mFont;
for (const StyleImpl& s : mStyles)
if (&s == style)
return s.mFont;
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 mPageHeight;
BookPtr mBook;
std::shared_ptr<TypesetBookImpl> mBook;
Section* mSection;
Line* mLine;
Run* mRun;
@ -347,7 +333,7 @@ namespace MWGui
std::vector<PartialText> mPartialWhitespace;
std::vector<PartialText> mPartialWord;
Book::Content const* mCurrentContent;
TypesetBookImpl::Content const* mCurrentContent;
Alignment mCurrentAlignment;
Typesetter(int width, int height)
@ -359,12 +345,12 @@ namespace MWGui
, mCurrentContent(nullptr)
, mCurrentAlignment(AlignLeft)
{
mBook = std::make_shared<Book>();
mBook = std::make_shared<TypesetBookImpl>();
}
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;
if (fontName.empty())
@ -375,9 +361,9 @@ namespace MWGui
if (useBookFont)
fullFontName = "Journalbook " + fullFontName;
for (Styles::iterator i = mBook->mStyles.begin(); i != mBook->mStyles.end(); ++i)
if (i->match(fullFontName, fontColour, fontColour, fontColour, 0))
return &*i;
for (StyleImpl& style : mBook->mStyles)
if (style.match(fullFontName, fontColour, fontColour, fontColour, 0))
return &style;
MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fullFontName);
if (!font)
@ -393,15 +379,15 @@ namespace MWGui
return &style;
}
Style* createHotStyle(Style* baseStyle, const Colour& normalColour, const Colour& hoverColour,
const Colour& activeColour, InteractiveId id, bool unique) override
Style* createHotStyle(Style* baseStyle, const MyGUI::Colour& normalColour, const MyGUI::Colour& hoverColour,
const MyGUI::Colour& activeColour, InteractiveId id, bool unique) override
{
StyleImpl* const baseStyleImpl = static_cast<StyleImpl*>(baseStyle);
if (!unique)
for (Styles::iterator i = mBook->mStyles.begin(); i != mBook->mStyles.end(); ++i)
if (i->match(baseStyleImpl->mFont, hoverColour, activeColour, normalColour, id))
return &*i;
for (StyleImpl& style : mBook->mStyles)
if (style.match(baseStyleImpl->mFont, hoverColour, activeColour, normalColour, id))
return &style;
StyleImpl& style = *mBook->mStyles.insert(mBook->mStyles.end(), StyleImpl());
@ -414,30 +400,30 @@ namespace MWGui
return &style;
}
void write(Style* style, Utf8Span text) override
void write(Style* style, std::string_view text) override
{
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();
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)
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();
mCurrentContent = reinterpret_cast<Content const*>(contentHandle);
mCurrentContent = contentHandle;
}
void write(Style* style, size_t begin, size_t end) override
@ -446,10 +432,10 @@ namespace MWGui
assert(end <= mCurrentContent->size());
assert(begin <= mCurrentContent->size());
const Utf8Point contentBegin = mCurrentContent->data() + begin;
const Utf8Point contentEnd = mCurrentContent->data() + end;
const Utf8Stream::Point contentBegin = mCurrentContent->data() + begin;
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
@ -486,7 +472,7 @@ namespace MWGui
mCurrentAlignment = sectionAlignment;
}
TypesetBook::Ptr complete() override
std::shared_ptr<TypesetBook> complete() override
{
int curPageStart = 0;
int curPageStop = 0;
@ -497,26 +483,26 @@ namespace MWGui
for (Sections::iterator i = mBook->mSections.begin(); i != mBook->mSections.end(); ++i, ++sa)
{
// 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;
switch (*sa)
{
default:
case AlignLeft:
j->mRect.left = 0;
line.mRect.left = 0;
break;
case AlignCenter:
j->mRect.left = excess / 2;
line.mRect.left = excess / 2;
break;
case AlignRight:
j->mRect.left = excess;
line.mRect.left = excess;
break;
}
j->mRect.right = j->mRect.left + width;
line.mRect.right = line.mRect.left + width;
}
if (curPageStop == curPageStart)
@ -541,7 +527,7 @@ namespace MWGui
// one.
assert(curPageStart != curPageStop);
mBook->mPages.push_back(Page(curPageStart, curPageStop));
mBook->mPages.emplace_back(curPageStart, curPageStop);
curPageStart = i->mRect.top;
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
// one.
mBook->mPages.push_back(Page(curPageStart, curPageStop));
mBook->mPages.emplace_back(curPageStart, curPageStop);
curPageStart = i->mRect.top;
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
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;
}
}
mBook->mPages.push_back(Page(curPageStart, splitPos));
mBook->mPages.emplace_back(curPageStart, splitPos);
curPageStart = splitPos;
curPageStop = splitPos;
@ -584,15 +570,13 @@ namespace MWGui
}
if (curPageStart != curPageStop)
mBook->mPages.push_back(Page(curPageStart, curPageStop));
mBook->mPages.emplace_back(curPageStart, curPageStop);
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())
{
if (ucsLineBreak(stream.peek()))
@ -651,10 +635,10 @@ namespace MWGui
int spaceWidth = 0;
int wordWidth = 0;
for (PartialTextConstIterator i = mPartialWhitespace.begin(); i != mPartialWhitespace.end(); ++i)
spaceWidth += i->mWidth;
for (PartialTextConstIterator i = mPartialWord.begin(); i != mPartialWord.end(); ++i)
wordWidth += i->mWidth;
for (const PartialText& partialText : mPartialWhitespace)
spaceWidth += partialText.mWidth;
for (const PartialText& partialText : mPartialWord)
wordWidth += partialText.mWidth;
int left = mLine ? mLine->mRect.right : 0;
@ -666,21 +650,23 @@ namespace MWGui
}
else
{
for (PartialTextConstIterator i = mPartialWhitespace.begin(); i != mPartialWhitespace.end(); ++i)
for (const PartialText& partialText : mPartialWhitespace)
{
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;
}
}
for (PartialTextConstIterator i = mPartialWord.begin(); i != mPartialWord.end(); ++i)
for (const PartialText& partialText : mPartialWord)
{
int top = mLine ? mLine->mRect.top : mBook->mRect.bottom;
const int numChars = static_cast<int>(i->mEnd - i->mBegin);
appendRun(i->mStyle, i->mBegin, i->mEnd, numChars, left + i->mWidth, top + fontHeight);
const int numChars = static_cast<int>(partialText.mEnd - partialText.mBegin);
appendRun(partialText.mStyle, partialText.mBegin, partialText.mEnd, numChars, left + partialText.mWidth,
top + fontHeight);
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);
}
@ -932,17 +918,12 @@ namespace MWGui
{
MYGUI_RTTI_DERIVED(PageDisplay)
protected:
typedef TypesetBookImpl::Section Section;
typedef TypesetBookImpl::Line Line;
typedef TypesetBookImpl::Run Run;
bool mIsPageReset;
size_t mPage;
struct TextFormat : ISubWidget
{
typedef MyGUI::IFont* Id;
Id mFont;
MyGUI::IFont* mFont;
int mCountVertex;
MyGUI::ITexture* mTexture;
MyGUI::RenderItem* mRenderItem;
@ -1017,16 +998,15 @@ namespace MWGui
}
public:
typedef TypesetBookImpl::StyleImpl Style;
typedef std::map<TextFormat::Id, std::unique_ptr<TextFormat>> ActiveTextFormats;
typedef std::map<MyGUI::IFont*, std::unique_ptr<TextFormat>> ActiveTextFormats;
int mViewTop;
int mViewBottom;
Style* mFocusItem;
TypesetBookImpl::StyleImpl* mFocusItem;
bool mItemActive;
MyGUI::MouseButton mLastDown;
std::function<void(intptr_t)> mLinkClicked;
std::function<void(TypesetBook::InteractiveId)> mLinkClicked;
std::shared_ptr<TypesetBookImpl> mBook;
@ -1072,7 +1052,7 @@ namespace MWGui
void onMouseMove(int left, int top)
{
Style* hit = nullptr;
TypesetBookImpl::StyleImpl* hit = nullptr;
if (auto pos = getAdjustedPos(left, top, true))
if (pos->top <= mViewBottom)
hit = mBook->hitTestWithMargin(pos->left, pos->top);
@ -1123,7 +1103,8 @@ namespace MWGui
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;
@ -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);
@ -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;
@ -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);
@ -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);
}
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
{

View file

@ -8,6 +8,7 @@
#include <cstdint>
#include <functional>
#include <memory>
#include <vector>
#include <components/settings/values.hpp>
@ -17,7 +18,7 @@ namespace MWGui
/// the book page widget.
struct TypesetBook
{
typedef std::shared_ptr<TypesetBook> Ptr;
using Content = std::vector<uint8_t>;
typedef intptr_t InteractiveId;
/// Returns the number of pages in the document.
@ -40,12 +41,6 @@ namespace MWGui
/// A factory class for creating a typeset book instance.
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;
enum Alignment
@ -62,16 +57,18 @@ namespace MWGui
struct Style;
/// 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.
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
/// existing style. The unique flag forces a new instance of this style
/// to be created even if an existing instance is present.
virtual Style* createHotStyle(Style* baseStyle, const Colour& normalColour, const Colour& hoverColour,
const Colour& activeColour, InteractiveId id, bool unique = true)
virtual Style* createHotStyle(Style* baseStyle, const MyGUI::Colour& normalColour,
const MyGUI::Colour& hoverColour, const MyGUI::Colour& activeColour, TypesetBook::InteractiveId id,
bool unique = true)
= 0;
/// Insert a line break into the document. Newline characters in the input
@ -89,22 +86,22 @@ namespace MWGui
virtual void setSectionAlignment(Alignment sectionAlignment) = 0;
// 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
/// 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.
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.
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
/// using the specified style.
virtual void write(Style* style, size_t begin, size_t end) = 0;
/// 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.
@ -112,11 +109,10 @@ namespace MWGui
{
MYGUI_RTTI_DERIVED(BookPage)
public:
typedef TypesetBook::InteractiveId InteractiveId;
typedef std::function<void(InteractiveId)> ClickCallback;
using ClickCallback = std::function<void(TypesetBook::InteractiveId)>;
/// 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.
virtual void adviseLinkClicked(ClickCallback callback) = 0;

View file

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

View file

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

View file

@ -12,10 +12,10 @@ namespace
{
struct AddContent
{
MWGui::BookTypesetter::Ptr mTypesetter;
std::shared_ptr<MWGui::BookTypesetter> mTypesetter;
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))
, mBodyStyle(bodyStyle)
{
@ -24,19 +24,19 @@ namespace
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)
{
}
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;
const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
if (topicId)
if (topic)
style = mTypesetter->createHotStyle(mBodyStyle, textColours.journalLink, textColours.journalLinkOver,
textColours.journalLinkPressed, topicId);
textColours.journalLinkPressed, MWGui::TypesetBook::InteractiveId(topic));
mTypesetter->write(style, begin, end);
}
@ -44,10 +44,10 @@ namespace
struct AddEntry
{
MWGui::BookTypesetter::Ptr mTypesetter;
std::shared_ptr<MWGui::BookTypesetter> mTypesetter;
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))
, mBodyStyle(bodyStyle)
{
@ -66,8 +66,8 @@ namespace
bool mAddHeader;
MWGui::BookTypesetter::Style* mHeaderStyle;
explicit AddJournalEntry(MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* bodyStyle,
MWGui::BookTypesetter::Style* headerStyle, bool addHeader)
explicit AddJournalEntry(std::shared_ptr<MWGui::BookTypesetter> typesetter,
MWGui::BookTypesetter::Style* bodyStyle, MWGui::BookTypesetter::Style* headerStyle, bool addHeader)
: AddEntry(std::move(typesetter), bodyStyle)
, mAddHeader(addHeader)
, mHeaderStyle(headerStyle)
@ -90,11 +90,12 @@ namespace
struct AddTopicEntry : AddEntry
{
intptr_t mContentId;
const MWGui::TypesetBook::Content* mContentId;
MWGui::BookTypesetter::Style* mHeaderStyle;
explicit AddTopicEntry(MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* bodyStyle,
MWGui::BookTypesetter::Style* headerStyle, intptr_t contentId)
explicit AddTopicEntry(std::shared_ptr<MWGui::BookTypesetter> typesetter,
MWGui::BookTypesetter::Style* bodyStyle, MWGui::BookTypesetter::Style* headerStyle,
const MWGui::TypesetBook::Content* contentId)
: AddEntry(std::move(typesetter), bodyStyle)
, mContentId(contentId)
, mHeaderStyle(headerStyle)
@ -117,12 +118,12 @@ namespace
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)
{
}
void operator()(MWGui::JournalViewModel::Utf8Span topicName)
void operator()(std::string_view topicName)
{
mTypesetter->write(mBodyStyle, topicName);
mTypesetter->sectionBreak();
@ -131,12 +132,12 @@ namespace
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)
{
}
void operator()(MWGui::JournalViewModel::Utf8Span topicName)
void operator()(std::string_view topicName)
{
mTypesetter->write(mBodyStyle, topicName);
mTypesetter->sectionBreak();
@ -147,15 +148,6 @@ namespace
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()
{
// 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;
}
typedef TypesetBook::Ptr book;
JournalBooks::JournalBooks(JournalViewModel::Ptr model, ToUTF8::FromType encoding)
JournalBooks::JournalBooks(std::shared_ptr<JournalViewModel> model, ToUTF8::FromType encoding)
: mModel(std::move(model))
, mEncoding(encoding)
, 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* 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->write(
body, to_utf8_span("You should have gone though the starting quest and got an initial quest."));
typesetter->write(body, "You should have gone though the starting quest and got an initial quest.");
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* body = typesetter->createStyle({}, MyGUI::Colour::Black);
@ -199,49 +188,50 @@ namespace MWGui
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* 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();
}
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* body = typesetter->createStyle({}, MyGUI::Colour::Black);
AddQuestName addName(typesetter, header);
addName(to_utf8_span(questName));
addName(questName);
mModel->visitJournalEntries(questName, AddJournalEntry(typesetter, body, header, false));
return typesetter->complete();
}
book JournalBooks::createTopicIndexBook()
std::shared_ptr<TypesetBook> JournalBooks::createTopicIndexBook()
{
bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251);
BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex();
std::shared_ptr<BookTypesetter> typesetter
= isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex();
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);
@ -260,12 +250,12 @@ namespace MWGui
const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
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)
typesetter->sectionBreak();
typesetter->write(style, to_utf8_span(buffer));
typesetter->write(style, buffer);
typesetter->lineBreak();
ch++;
@ -274,9 +264,9 @@ namespace MWGui
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);
@ -314,14 +304,14 @@ namespace MWGui
if (i % sectionBreak == 0)
typesetter->sectionBreak();
typesetter->write(style, to_utf8_span(buffer));
typesetter->write(style, buffer);
typesetter->lineBreak();
}
return typesetter;
}
BookTypesetter::Ptr JournalBooks::createTypesetter()
std::shared_ptr<BookTypesetter> JournalBooks::createTypesetter()
{
// TODO: determine page size from layout...
return BookTypesetter::create(240, 320);

View file

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

View file

@ -22,26 +22,15 @@ namespace MWGui
struct JournalViewModelImpl : JournalViewModel
{
typedef MWDialogue::KeywordSearch<intptr_t> KeywordSearchT;
using TopicSearch = MWDialogue::KeywordSearch<const MWDialogue::Topic*>;
mutable bool mKeywordSearchLoaded;
mutable KeywordSearchT mKeywordSearch;
mutable TopicSearch mKeywordSearch;
JournalViewModelImpl() { mKeywordSearchLoaded = false; }
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 unload() override
@ -57,7 +46,7 @@ namespace MWGui
MWBase::Journal* journal = MWBase::Environment::get().getJournal();
for (const auto& [_, topic] : journal->getTopics())
mKeywordSearch.seed(topic.getName(), intptr_t(&topic));
mKeywordSearch.seed(topic.getName(), &topic);
mKeywordSearchLoaded = true;
}
@ -88,10 +77,8 @@ namespace MWGui
mutable bool loaded;
mutable std::string utf8text;
typedef std::pair<size_t, size_t> Range;
// 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;
@ -126,7 +113,7 @@ namespace MWGui
utf8text.replace(posBegin, posEnd + 1 - posBegin, displayName);
intptr_t value = 0;
const MWDialogue::Topic* value = nullptr;
if (mModel->mKeywordSearch.containsKeyword(topicName, 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();
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();
mModel->ensureKeyWordSearchLoaded();
@ -154,25 +141,23 @@ namespace MWGui
&& MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())
{
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();
++it)
for (const auto& [range, topicId] : mHyperLinks)
{
intptr_t topicId = it->second;
if (formatted < it->first.first)
visitor(0, formatted, it->first.first);
visitor(topicId, it->first.first, it->first.second);
formatted = it->first.second;
if (formatted < range.first)
visitor(0, formatted, range.first);
visitor(topicId, range.first, range.second);
formatted = range.second;
}
if (formatted < utf8text.size())
visitor(0, formatted, utf8text.size());
}
else
{
std::vector<KeywordSearchT::Match> matches;
std::vector<TopicSearch::Match> matches;
mModel->mKeywordSearch.highlightKeywords(utf8text.begin(), utf8text.end(), matches);
std::string::const_iterator i = utf8text.begin();
for (const KeywordSearchT::Match& match : matches)
for (const TopicSearch::Match& match : matches)
{
if (i != match.mBeg)
visitor(0, i - utf8text.begin(), match.mBeg - utf8text.begin());
@ -231,7 +216,7 @@ namespace MWGui
std::string getText() const override { return mEntry->getText(); }
Utf8Span timestamp() const override
std::string_view timestamp() const override
{
if (timestamp_buffer.empty())
{
@ -246,7 +231,7 @@ namespace MWGui
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(toUtf8Span(topic.getName()));
visitor(topic.getName());
}
void visitTopicNamesStartingWith(
@ -324,19 +309,18 @@ namespace MWGui
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)
visitor(TopicEntryImpl(this, topic, entry));
}
};
JournalViewModel::Ptr JournalViewModel::create()
std::shared_ptr<JournalViewModel> JournalViewModel::create()
{
return std::make_shared<JournalViewModelImpl>();
}

View file

@ -8,6 +8,11 @@
#include <components/misc/utf8stream.hpp>
namespace MWDialogue
{
class Topic;
}
namespace MWGui
{
/// View-Model for the journal GUI
@ -18,13 +23,6 @@ namespace MWGui
/// game data store.
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.
struct Entry
{
@ -33,12 +31,12 @@ namespace MWGui
/// This function returns a borrowed reference to the body of the
/// journal entry. The returned reference becomes invalid when the
/// 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
/// and end of the span relative to the body, and a valid topic ID if
/// 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;
};
@ -48,7 +46,7 @@ namespace MWGui
{
/// Returns a pre-formatted span of UTF8 encoded text representing
/// 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;
};
@ -58,7 +56,7 @@ namespace MWGui
{
/// Returns a pre-formatted span of UTF8 encoded text representing
/// 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;
};
@ -78,20 +76,25 @@ namespace MWGui
/// walks over the journal entries related to all quests with the given name
/// If \a questName is empty, simply visits all journal entries
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
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
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
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
static Ptr create();
static std::shared_ptr<JournalViewModel> create();
virtual ~JournalViewModel() = default;
};

View file

@ -50,13 +50,11 @@ namespace
struct DisplayState
{
size_t mPage;
Book mBook;
std::shared_ptr<MWGui::TypesetBook> mBook;
};
typedef std::stack<DisplayState> DisplayStateStack;
DisplayStateStack mStates;
Book mTopicIndexBook;
std::stack<DisplayState> mStates;
std::shared_ptr<MWGui::TypesetBook> mTopicIndexBook;
bool mQuestMode;
bool mOptionsMode;
bool mTopicsMode;
@ -91,7 +89,7 @@ namespace
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)
, JournalWindow()
{
@ -123,7 +121,10 @@ namespace
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(RightBookPage)->adviseLinkClicked(std::move(callback));
@ -135,8 +136,9 @@ namespace
}
{
MWGui::BookPage::ClickCallback callback
= [this](MWGui::TypesetBook::InteractiveId index) { notifyIndexLinkClicked(index); };
MWGui::BookPage::ClickCallback callback = [this](MWGui::TypesetBook::InteractiveId index) {
notifyIndexLinkClicked(static_cast<Utf8Stream::UnicodeChar>(index));
};
getPage(LeftTopicIndex)->adviseLinkClicked(callback);
getPage(CenterTopicIndex)->adviseLinkClicked(callback);
@ -240,7 +242,7 @@ namespace
setBookMode();
Book journalBook;
std::shared_ptr<MWGui::TypesetBook> journalBook;
if (mModel->isEmpty())
journalBook = createEmptyJournalBook();
else
@ -268,8 +270,8 @@ namespace
{
mModel->unload();
getPage(LeftBookPage)->showPage(Book(), 0);
getPage(RightBookPage)->showPage(Book(), 0);
getPage(LeftBookPage)->showPage({}, 0);
getPage(RightBookPage)->showPage({}, 0);
while (!mStates.empty())
mStates.pop();
@ -315,7 +317,7 @@ namespace
// TODO: figure out how to make "options" page overlay book page
// 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 (mQuestMode)
@ -326,7 +328,7 @@ namespace
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void pushBook(Book& book)
void pushBook(std::shared_ptr<MWGui::TypesetBook>& book)
{
DisplayState bs;
bs.mPage = 0;
@ -336,7 +338,7 @@ namespace
updateCloseJournalButton();
}
void replaceBook(Book& book)
void replaceBook(std::shared_ptr<MWGui::TypesetBook>& book)
{
assert(!mStates.empty());
mStates.top().mBook = book;
@ -360,7 +362,7 @@ namespace
void updateShowingPages()
{
Book book;
std::shared_ptr<MWGui::TypesetBook> book;
size_t page;
size_t relPages;
@ -393,8 +395,16 @@ namespace
setVisible(PageOneNum, relPages > 0);
setVisible(PageTwoNum, relPages > 1);
getPage(LeftBookPage)->showPage((relPages > 0) ? book : Book(), page + 0);
getPage(RightBookPage)->showPage((relPages > 0) ? std::move(book) : Book(), page + 1);
if (relPages > 0)
{
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(PageTwoNum, page + 2);
@ -410,9 +420,9 @@ namespace
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)
replaceBook(topicBook);
@ -434,17 +444,14 @@ namespace
{
ESM::RefId topic = ESM::RefId::stringRefId(topicIdString);
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);
if (it != journal->getTopics().end())
topicId = intptr_t(&it->second);
notifyTopicClicked(topicId);
notifyTopicClicked(it->second);
}
void notifyQuestClicked(const std::string& name, int id)
{
Book book = createQuestBook(name);
std::shared_ptr<MWGui::TypesetBook> book = createQuestBook(name);
if (mStates.size() > 1)
replaceBook(book);
@ -508,7 +515,7 @@ namespace
}
}
void notifyIndexLinkClicked(MWGui::TypesetBook::InteractiveId index)
void notifyIndexLinkClicked(Utf8Stream::UnicodeChar index)
{
setVisible(LeftTopicIndex, false);
setVisible(CenterTopicIndex, false);
@ -664,7 +671,7 @@ namespace
if (!mStates.empty())
{
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())
{
@ -785,7 +792,8 @@ namespace
if (mSelectedIndex >= 27)
russianOffset++; // 27, not 28, because of skipping char 26
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
@ -945,7 +953,7 @@ namespace
// glue the implementation to the interface
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);
}