From 9da7effa3dd8bbacbc58d2be47a17d631e261c81 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Sun, 12 Sep 2010 20:49:11 +0300 Subject: Improve chat speed. Parse chat lines in BrowserBox once after changes. Reviewed-by: Thorbjorn --- src/gui/widgets/browserbox.cpp | 260 ++++++++++++----------------------------- src/gui/widgets/browserbox.h | 60 ++++++++-- src/gui/widgets/chattab.cpp | 1 + 3 files changed, 128 insertions(+), 193 deletions(-) diff --git a/src/gui/widgets/browserbox.cpp b/src/gui/widgets/browserbox.cpp index d43afed7..6ae4c588 100644 --- a/src/gui/widgets/browserbox.cpp +++ b/src/gui/widgets/browserbox.cpp @@ -2,6 +2,7 @@ * The Mana Client * Copyright (C) 2004-2009 The Mana World Development Team * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2009 Aethyra Development Team * * This file is part of The Mana Client. * @@ -21,6 +22,8 @@ #include "gui/widgets/browserbox.h" +#include "client.h" + #include "gui/theme.h" #include "gui/widgets/linkhandler.h" @@ -40,7 +43,9 @@ BrowserBox::BrowserBox(unsigned int mode, bool opaque): mMaxRows(0), mHeight(0), mWidth(0), - mYStart(0) + mYStart(0), + mUpdateTime(-1), + mAlwaysUpdate(true) { setFocusable(true); addMouseListener(this); @@ -74,13 +79,14 @@ void BrowserBox::addRow(const std::string &row) { std::string tmp = row; std::string newRow; - BROWSER_LINK bLink; std::string::size_type idx1, idx2, idx3; gcn::Font *font = getFont(); // Use links and user defined colors if (mUseLinksAndUserColors) { + BROWSER_LINK bLink; + // Check for links in format "@@link|Caption@@" idx1 = tmp.find("@@"); while (idx1 != std::string::npos) @@ -92,7 +98,7 @@ void BrowserBox::addRow(const std::string &row) break; bLink.link = tmp.substr(idx1 + 2, idx2 - (idx1 + 2)); bLink.caption = tmp.substr(idx2 + 1, idx3 - (idx2 + 1)); - bLink.y1 = mTextRows.size() * font->getHeight(); + bLink.y1 = static_cast(mTextRows.size()) * font->getHeight(); bLink.y2 = bLink.y1 + font->getHeight(); newRow += tmp.substr(0, idx1); @@ -150,7 +156,7 @@ void BrowserBox::addRow(const std::string &row) if (mMode == AUTO_SIZE) { std::string plain = newRow; - for (idx1 = plain.find("##"); idx1 != std::string::npos; idx1 = plain.find("##")) + while ((idx1 = plain.find("##")) != std::string::npos) plain.erase(idx1, 3); // Adjust the BrowserBox size @@ -179,11 +185,11 @@ void BrowserBox::addRow(const std::string &row) // Wraping between words (at blank spaces) if ((nextChar < row.size()) && (row.at(nextChar) == ' ')) { - int nextSpacePos = row.find(" ", (nextChar + 1)); + int nextSpacePos = static_cast( + row.find(" ", (nextChar + 1))); if (nextSpacePos <= 0) - { - nextSpacePos = row.size() - 1; - } + nextSpacePos = static_cast(row.size()) - 1; + int nextWordWidth = font->getWidth( row.substr(nextChar, (nextSpacePos - nextChar))); @@ -204,11 +210,12 @@ void BrowserBox::addRow(const std::string &row) } } - setHeight(font->getHeight() * (mTextRows.size() + y)); + setHeight(font->getHeight() * (static_cast( + mTextRows.size()) + y)); } else { - setHeight(font->getHeight() * mTextRows.size()); + setHeight(font->getHeight() * static_cast(mTextRows.size())); } updateHeight(); } @@ -225,7 +232,9 @@ void BrowserBox::clearRows() struct MouseOverLink { - MouseOverLink(int x, int y) : mX(x),mY(y) { } + MouseOverLink(int x, int y) : mX(x), mY(y) + { } + bool operator() (BROWSER_LINK &link) { return (mX >= link.x1 && mX < link.x2 && @@ -249,7 +258,8 @@ void BrowserBox::mouseMoved(gcn::MouseEvent &event) LinkIterator i = find_if(mLinks.begin(), mLinks.end(), MouseOverLink(event.getX(), event.getY())); - mSelectedLink = (i != mLinks.end()) ? (i - mLinks.begin()) : -1; + mSelectedLink = (i != mLinks.end()) + ? static_cast(i - mLinks.begin()) : -1; } void BrowserBox::draw(gcn::Graphics *graphics) @@ -261,10 +271,7 @@ void BrowserBox::draw(gcn::Graphics *graphics) mYStart = 0; if (getWidth() != mWidth) - { - mWidth = getWidth(); updateHeight(); - } if (mOpaque) { @@ -296,7 +303,28 @@ void BrowserBox::draw(gcn::Graphics *graphics) } } + gcn::Font *font = getFont(); + + for (LinePartIterator i = mLineParts.begin(); + i != mLineParts.end(); + i ++) + { + const LinePart &part = *i; + if (part.getY() + 50 < mYStart) + continue; + if (part.getY() > yEnd) + break; + graphics->setColor(part.getColor()); + font->drawString(graphics, part.getText(), part.getX(), part.getY()); + } + + return; +} + +int BrowserBox::calcHeight() +{ int x = 0, y = 0; + int wrappedLines = 0; int link = 0; gcn::Font *font = getFont(); @@ -305,27 +333,13 @@ void BrowserBox::draw(gcn::Graphics *graphics) char const *hyphen = "~"; int hyphenWidth = font->getWidth(hyphen); - graphics->setColor(Theme::getThemeColor(Theme::TEXT)); + gcn::Color selColor = Theme::getThemeColor(Theme::TEXT); const gcn::Color textColor = Theme::getThemeColor(Theme::TEXT); - TextRowsHeightIterator h = mTextRowsHeights.begin(); - for (TextRowIterator i = mTextRows.begin(); - i != mTextRows.end(); - i++, h++) - { - bool hidden = false; - if (y + 50 < mYStart) - { - y += *(h); - continue; - } - else if (y > yEnd) - { - break; - } + mLineParts.clear(); - gcn::Color selColor = textColor; - gcn::Color prevColor = selColor; + for (TextRowIterator i = mTextRows.begin(); i != mTextRows.end(); i++) + { const std::string row = *(i); bool wrapped = false; x = 0; @@ -333,19 +347,19 @@ void BrowserBox::draw(gcn::Graphics *graphics) // Check for separator lines if (row.find("---", 0) == 0) { - if (!hidden) + const int dashWidth = fontWidthMinus; + for (x = 0; x < getWidth(); x++) { - const int dashWidth = fontWidthMinus; - for (x = 0; x < getWidth(); x++) - { - font->drawString(graphics, "-", x, y); - x += dashWidth - 2; - } + mLineParts.push_back(LinePart(x, y, selColor, "-")); + x += dashWidth - 2; } + y += fontHeight; continue; } + gcn::Color prevColor = selColor; + // TODO: Check if we must take texture size limits into account here // TODO: Check if some of the O(n) calls can be removed for (std::string::size_type start = 0, end = std::string::npos; @@ -364,14 +378,14 @@ void BrowserBox::draw(gcn::Graphics *graphics) if (mUseLinksAndUserColors) end = row.find("##", start + 1); - if (!hidden - && (mUseLinksAndUserColors || - (!mUseLinksAndUserColors && (start == 0)))) + if (mUseLinksAndUserColors || + (!mUseLinksAndUserColors && (start == 0))) { // Check for color change in format "##x", x = [L,P,0..9] if (row.find("##", start) == start && row.size() > start + 2) { const char c = row.at(start + 2); + bool valid; const gcn::Color col = Theme::getThemeColor(c, valid); @@ -381,7 +395,6 @@ void BrowserBox::draw(gcn::Graphics *graphics) } else if (c == '<') { - link++; prevColor = selColor; selColor = col; } @@ -408,144 +421,12 @@ void BrowserBox::draw(gcn::Graphics *graphics) selColor = textColor; } } - start += 3; - - if (start == row.size()) - break; - } - graphics->setColor(selColor); - } - - std::string::size_type len = - end == std::string::npos ? end : end - start; - std::string part = row.substr(start, len); - - // Auto wrap mode - if (mMode == AUTO_WRAP && getWidth() > 0 - && font->getWidth(part) > 0 - && (x + font->getWidth(part) + 10) > getWidth()) - { - bool forced = false; - - /* FIXME: This code layout makes it easy to crash remote - clients by talking garbage. Forged long utf-8 characters - will cause either a buffer underflow in substr or an - infinite loop in the main loop. */ - do - { - if (!forced) - end = row.rfind(' ', end); - - // Check if we have to (stupidly) force-wrap - if (end == std::string::npos || end <= start) - { - forced = true; - end = row.size(); - x += hyphenWidth; // Account for the wrap-notifier - continue; - } - - // Skip to the start of the current character - while ((row[end] & 192) == 128) - end--; - end--; // And then to the last byte of the previous one - - part = row.substr(start, end - start + 1); - } - while (end > start && font->getWidth(part) > 0 - && (x + font->getWidth(part) + 10) > getWidth()); - - if (forced) - { - x -= hyphenWidth; // Remove the wrap-notifier accounting - if (y >= mYStart) - { - font->drawString(graphics, hyphen, - getWidth() - hyphenWidth, y); - } - end++; // Skip to the next character - } - else - { - end += 2; // Skip to after the space - } - - wrapped = true; - } - - font->drawString(graphics, part, x, y); - - if (mMode == AUTO_WRAP && font->getWidth(part) == 0) - break; - - x += font->getWidth(part); - } - y += fontHeight; - } - setHeight(mHeight); -} - -int BrowserBox::calcHeight() -{ - int x = 0, y = 0; - int wrappedLines = 0; - int link = 0; - gcn::Font *font = getFont(); - - int fontHeight = font->getHeight(); - int fontWidthMinus = font->getWidth("-"); - char const *hyphen = "~"; - int hyphenWidth = font->getWidth(hyphen); - - mTextRowsHeights.clear(); - - for (TextRowIterator i = mTextRows.begin(); i != mTextRows.end(); i++) - { - const std::string row = *(i); - bool wrapped = false; - int yStart = y; - x = 0; - - // Check for separator lines - if (row.find("---", 0) == 0) - { - const int dashWidth = fontWidthMinus; - for (x = 0; x < getWidth(); x++) - x += dashWidth - 2; - - y += fontHeight; - continue; - } - - // TODO: Check if we must take texture size limits into account here - // TODO: Check if some of the O(n) calls can be removed - for (std::string::size_type start = 0, end = std::string::npos; - start != std::string::npos; - start = end, end = std::string::npos) - { - // Wrapped line continuation shall be indented - if (wrapped) - { - y += fontHeight; - x = 15; - wrapped = false; - } - - // "Tokenize" the string at control sequences - if (mUseLinksAndUserColors) - end = row.find("##", start + 1); - - if (mUseLinksAndUserColors || - (!mUseLinksAndUserColors && (start == 0))) - { - // Check for color change in format "##x", x = [L,P,0..9] - if (row.find("##", start) == start && row.size() > start + 2) - { - const char c = row.at(start + 2); if (c == '<') { - const int size = font->getWidth(mLinks[link].caption) + 1; + const int size = + font->getWidth(mLinks[link].caption) + 1; + mLinks[link].x1 = x; mLinks[link].y1 = y; mLinks[link].x2 = mLinks[link].x1 + size; @@ -605,6 +486,8 @@ int BrowserBox::calcHeight() if (forced) { x -= hyphenWidth; // Remove the wrap-notifier accounting + mLineParts.push_back(LinePart(getWidth() - hyphenWidth, + y, selColor, hyphen)); end++; // Skip to the next character } else @@ -616,19 +499,30 @@ int BrowserBox::calcHeight() wrappedLines++; } + mLineParts.push_back(LinePart(x, y, selColor, part.c_str())); + if (mMode == AUTO_WRAP && font->getWidth(part) == 0) break; x += font->getWidth(part); } y += fontHeight; - mTextRowsHeights.push_back(y - yStart); } - return (mTextRows.size() + wrappedLines) * fontHeight; + return (static_cast(mTextRows.size()) + wrappedLines) * fontHeight; } void BrowserBox::updateHeight() { - mHeight = calcHeight(); - setHeight(mHeight); + if (mAlwaysUpdate || std::abs(mUpdateTime - tick_time) > 10 + || mTextRows.size() < 3) + { + mWidth = getWidth(); + mHeight = calcHeight(); + setHeight(mHeight); + mUpdateTime = tick_time; + } + else if (!mUpdateTime) + { + mUpdateTime = tick_time; + } } diff --git a/src/gui/widgets/browserbox.h b/src/gui/widgets/browserbox.h index b71f30d7..54a2a8cc 100644 --- a/src/gui/widgets/browserbox.h +++ b/src/gui/widgets/browserbox.h @@ -2,6 +2,7 @@ * The Mana Client * Copyright (C) 2004-2009 The Mana World Development Team * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2009 Aethyra Development Team * * This file is part of The Mana Client. * @@ -30,12 +31,39 @@ class LinkHandler; -struct BROWSER_LINK { +struct BROWSER_LINK +{ int x1, x2, y1, y2; /**< Where link is placed */ std::string link; std::string caption; }; +class LinePart +{ + public: + LinePart(int x, int y, gcn::Color color, std::string text) : + mX(x), mY(y), mColor(color), mText(text) + { + } + + int getX() const + { return mX; } + + int getY() const + { return mY; } + + const std::string &getText() const + { return mText; } + + const gcn::Color &getColor() const + { return mColor; } + + private: + int mX, mY; + gcn::Color mColor; + std::string mText; +}; + /** * A simple browser box able to handle links and forward events to the * parent conteiner. @@ -72,7 +100,7 @@ class BrowserBox : public gcn::Widget, /** * Sets the maximum numbers of rows in the browser box. 0 = no limit. */ - void setMaxRow(int max) {mMaxRows = max; }; + void setMaxRow(unsigned max) {mMaxRows = max; }; /** * Disable links & user defined colors to be used in chat input. @@ -111,8 +139,9 @@ class BrowserBox : public gcn::Widget, /** * BrowserBox modes. */ - enum { - AUTO_SIZE, + enum + { + AUTO_SIZE = 0, AUTO_WRAP /**< Maybe it needs a fix or to be redone. */ }; @@ -126,7 +155,8 @@ class BrowserBox : public gcn::Widget, * windows and widgets. So, I think it's better keep BrowserBox * opaque (white background) by default. */ - enum { + enum + { RED = 0xff0000, /**< Color 1 */ GREEN = 0x009000, /**< Color 2 */ BLUE = 0x0000ff, /**< Color 3 */ @@ -142,21 +172,29 @@ class BrowserBox : public gcn::Widget, * Highlight modes for links. * This can be used for a bitmask. */ - enum { + enum + { UNDERLINE = 1, BACKGROUND = 2 }; + typedef std::list TextRows; + + TextRows &getRows() + { return mTextRows; } + + void setAlwaysUpdate(bool n) + { mAlwaysUpdate = n; } + private: int calcHeight(); - typedef std::list TextRows; typedef TextRows::iterator TextRowIterator; TextRows mTextRows; - typedef std::list TextRowsHeights; - typedef TextRowsHeights::iterator TextRowsHeightIterator; - TextRowsHeights mTextRowsHeights; + typedef std::list LinePartList; + typedef LinePartList::iterator LinePartIterator; + LinePartList mLineParts; typedef std::vector Links; typedef Links::iterator LinkIterator; @@ -172,6 +210,8 @@ class BrowserBox : public gcn::Widget, int mHeight; int mWidth; int mYStart; + int mUpdateTime; + bool mAlwaysUpdate; }; #endif diff --git a/src/gui/widgets/chattab.cpp b/src/gui/widgets/chattab.cpp index 8bf57508..bbb2b8bb 100644 --- a/src/gui/widgets/chattab.cpp +++ b/src/gui/widgets/chattab.cpp @@ -53,6 +53,7 @@ ChatTab::ChatTab(const std::string &name) : Tab() mTextOutput->setOpaque(false); mTextOutput->setMaxRow((int) config.getValue("ChatLogLength", 0)); mTextOutput->setLinkHandler(chatWindow->mItemLinkHandler); + mTextOutput->setAlwaysUpdate(false); mScrollArea = new ScrollArea(mTextOutput); mScrollArea->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER, -- cgit v1.2.3-60-g2f50