Improve color generation performance

Colors are generated asynchronously now and the TimelineItem is
updated when the color generation finishes.  This allows the UI
to stay responsive while new colors are being generated.
This commit is contained in:
redsky17 2019-02-08 01:58:00 +00:00
parent bb345a9a9f
commit 13663ad5f8
3 changed files with 53 additions and 27 deletions

View file

@ -399,7 +399,6 @@ utils::hashQString(const QString &input)
QString
utils::generateContrastingHexColor(const QString &input, const QString &background)
{
nhlog::ui()->debug("Background hex {}", background.toStdString());
const QColor backgroundCol(background);
const qreal backgroundLum = luminance(background);
@ -407,8 +406,6 @@ utils::generateContrastingHexColor(const QString &input, const QString &backgrou
auto hash = hashQString(input);
// create a hue value based on the hash of the input.
auto userHue = qAbs(hash % 360);
nhlog::ui()->debug(
"User Hue {} : {}", input.toStdString(), QString::number(userHue).toStdString());
// start with moderate saturation and lightness values.
auto sat = 220;
auto lightness = 125;
@ -430,7 +427,6 @@ utils::generateContrastingHexColor(const QString &input, const QString &backgrou
// saturation instead.
if (lightness == 242 || lightness == 13) {
qreal newSat = qBound(26.0, sat * 1.25, 242.0);
nhlog::ui()->info("newSat {}", QString::number(newSat).toStdString());
inputColor.setHsl(userHue, qFloor(newSat), lightness);
auto tmpLum = luminance(inputColor);
@ -477,11 +473,6 @@ utils::generateContrastingHexColor(const QString &input, const QString &backgrou
// get the hex value of the generated color.
auto colorHex = inputColor.name();
nhlog::ui()->debug("Hex Generated for {}: [hex: {}, contrast: {}, luminance: {}]",
input.toStdString(),
colorHex.toStdString(),
QString::number(contrast).toStdString(),
QString::number(lum).toStdString());
return colorHex;
}

View file

@ -14,6 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <functional>
#include <QContextMenuEvent>
#include <QDesktopServices>
@ -197,6 +198,12 @@ TimelineItem::init()
connect(markAsRead_, &QAction::triggered, this, &TimelineItem::sendReadReceipt);
connect(viewRawMessage_, &QAction::triggered, this, &TimelineItem::openRawMessageViewer);
colorGenerating_ = new QFutureWatcher<QString>(this);
connect(colorGenerating_,
&QFutureWatcher<QString>::finished,
this,
&TimelineItem::finishedGeneratingColor);
topLayout_ = new QHBoxLayout(this);
mainLayout_ = new QVBoxLayout;
messageLayout_ = new QHBoxLayout;
@ -557,6 +564,12 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
adjustMessageLayout();
}
TimelineItem::~TimelineItem()
{
colorGenerating_->cancel();
colorGenerating_->waitForFinished();
}
void
TimelineItem::markSent()
{
@ -607,16 +620,39 @@ TimelineItem::generateBody(const QString &body)
void
TimelineItem::refreshAuthorColor()
{
// Cancel and wait if we are already generating the color.
if (colorGenerating_->isRunning()) {
colorGenerating_->cancel();
colorGenerating_->waitForFinished();
}
if (userName_) {
// generate user's unique color.
std::function<QString()> generate = [this]() {
QString userColor = utils::generateContrastingHexColor(
userName_->toolTip(), backgroundColor().name());
return userColor;
};
QString userColor = Cache::userColor(userName_->toolTip());
// If the color is empty, then generate it asynchronously
if (userColor.isEmpty()) {
// This attempts to refresh this item since it's not drawn
// which allows us to get the background color accurately.
qApp->style()->polish(this);
// generate user's unique color.
auto backCol = backgroundColor().name();
userColor =
utils::generateContrastingHexColor(userName_->toolTip(), backCol);
colorGenerating_->setFuture(QtConcurrent::run(generate));
} else {
userName_->setStyleSheet("QLabel { color : " + userColor + "; }");
}
}
}
void
TimelineItem::finishedGeneratingColor()
{
nhlog::ui()->debug("finishedGeneratingColor for: {}", userName_->toolTip().toStdString());
QString userColor = colorGenerating_->result();
if (!userColor.isEmpty()) {
// another TimelineItem might have inserted in the meantime.
if (Cache::userColor(userName_->toolTip()).isEmpty()) {
Cache::insertUserColor(userName_->toolTip(), userColor);
}
userName_->setStyleSheet("QLabel { color : " + userColor + "; }");
@ -656,17 +692,9 @@ TimelineItem::generateUserName(const QString &user_id, const QString &displaynam
userName_->setAlignment(Qt::AlignLeft | Qt::AlignTop);
userName_->setFixedWidth(QFontMetrics(userName_->font()).width(userName_->text()));
// TimelineItem isn't displayed. This forces the QSS to get
// loaded.
QString userColor = Cache::userColor(user_id);
if (userColor.isEmpty()) {
qApp->style()->polish(this);
// generate user's unique color.
auto backCol = backgroundColor().name();
userColor = utils::generateContrastingHexColor(user_id, backCol);
Cache::insertUserColor(user_id, userColor);
}
userName_->setStyleSheet("QLabel { color : " + userColor + "; }");
// Set the user color asynchronously if it hasn't been generated yet,
// otherwise this will just set it.
refreshAuthorColor();
auto filter = new UserProfileFilter(user_id, userName_);
userName_->installEventFilter(filter);

View file

@ -26,6 +26,8 @@
#include <QSettings>
#include <QTimer>
#include <QtConcurrent>
#include "AvatarProvider.h"
#include "RoomInfoListItem.h"
#include "Utils.h"
@ -204,6 +206,8 @@ public:
const QString &room_id,
QWidget *parent);
~TimelineItem();
void setBackgroundColor(const QColor &color) { backgroundColor_ = color; }
QColor backgroundColor() const { return backgroundColor_; }
@ -229,6 +233,7 @@ signals:
public slots:
void refreshAuthorColor();
void finishedGeneratingColor();
protected:
void paintEvent(QPaintEvent *event) override;
@ -264,6 +269,8 @@ private:
//! has been acknowledged by the server.
bool isReceived_ = false;
QFutureWatcher<QString> *colorGenerating_;
QString replaceEmoji(const QString &body);
QString event_id_;
QString room_id_;