From c9057c565220fa7f5e264775fde4bf41c5eae308 Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Tue, 11 Aug 2009 23:45:56 +0200 Subject: [PATCH] Make URLs stylable and fix on-hover underline You can now use ::url in stylesheets to define how URLs should look like. Default is palette(link) which uses the system color for that purpose. Since this is applied after layouting, you shouldn't use attributes that change the font geometry. --- data/default.qss | 4 +++ src/qtui/chatitem.cpp | 59 ++++++++++++++++++++++++++++++++----- src/qtui/chatitem.h | 5 +++- src/uisupport/qssparser.cpp | 2 ++ src/uisupport/uistyle.cpp | 2 +- 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/data/default.qss b/data/default.qss index 22d24019..0371face 100644 --- a/data/default.qss +++ b/data/default.qss @@ -27,6 +27,10 @@ ChatLine::timestamp { foreground: #707c70; } +ChatLine::url { + foreground: palette(link); +} + Palette { marker-line: red; } diff --git a/src/qtui/chatitem.cpp b/src/qtui/chatitem.cpp index 1f7e9ac6..e2fa55b6 100644 --- a/src/qtui/chatitem.cpp +++ b/src/qtui/chatitem.cpp @@ -84,10 +84,14 @@ void ChatItem::initLayoutHelper(QTextLayout *layout, QTextOption::WrapMode wrapM layout->setTextOption(option); QList formatRanges - = QtUi::style()->toTextLayoutList(data(MessageModel::FormatRole).value(), layout->text().length(), data(ChatLineModel::MsgLabelRole).toUInt()); + = QtUi::style()->toTextLayoutList(formatList(), layout->text().length(), data(ChatLineModel::MsgLabelRole).toUInt()); layout->setAdditionalFormats(formatRanges); } +UiStyle::FormatList ChatItem::formatList() const { + return data(MessageModel::FormatRole).value(); +} + void ChatItem::doLayout(QTextLayout *layout) const { layout->beginLayout(); QTextLine line = layout->createLine(); @@ -118,7 +122,7 @@ void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QTextLayout layout; initLayout(&layout); - layout.draw(painter, QPointF(0,0), selectionFormats(), boundingRect()); + layout.draw(painter, QPointF(0,0), additionalFormats(), boundingRect()); // layout()->draw(painter, QPointF(0,0), formats, boundingRect()); @@ -150,6 +154,34 @@ void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, // painter->drawRect(_boundingRect.adjusted(0, 0, -1, -1)); } +void ChatItem::overlayFormat(UiStyle::FormatList &fmtList, int start, int end, quint32 overlayFmt) const { + for(int i = 0; i < fmtList.count(); i++) { + int fmtStart = fmtList.at(i).first; + int fmtEnd = (i < fmtList.count()-1 ? fmtList.at(i+1).first : data(MessageModel::DisplayRole).toString().length()); + + if(fmtEnd <= start) + continue; + if(fmtStart >= end) + break; + + // split the format if necessary + if(fmtStart < start) { + fmtList.insert(i, fmtList.at(i)); + fmtList[++i].first = start; + } + if(end < fmtEnd) { + fmtList.insert(i, fmtList.at(i)); + fmtList[i+1].first = end; + } + + fmtList[i].second |= overlayFmt; + } +} + +QVector ChatItem::additionalFormats() const { + return selectionFormats(); +} + QVector ChatItem::selectionFormats() const { if(!hasSelection()) return QVector(); @@ -163,14 +195,14 @@ QVector ChatItem::selectionFormats() const { end = qMax(_selectionStart, _selectionEnd); } - UiStyle::FormatList fmtList = data(MessageModel::FormatRole).value(); + UiStyle::FormatList fmtList = formatList(); - while(fmtList.count() >=2 && fmtList.at(1).first <= start) + while(fmtList.count() > 1 && fmtList.at(1).first <= start) fmtList.removeFirst(); fmtList.first().first = start; - while(fmtList.count() >= 2 && fmtList.last().first >= end) + while(fmtList.count() > 1 && fmtList.last().first >= end) fmtList.removeLast(); return QtUi::style()->toTextLayoutList(fmtList, end, UiStyle::Selected|data(ChatLineModel::MsgLabelRole).toUInt()).toVector(); @@ -331,7 +363,7 @@ void SenderChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *op QPixmap pixmap(layout.boundingRect().toRect().size()); pixmap.fill(Qt::transparent); QPainter pixPainter(&pixmap); - layout.draw(&pixPainter, QPointF(qMax(offset, (qreal)0), 0), selectionFormats()); + layout.draw(&pixPainter, QPointF(qMax(offset, (qreal)0), 0), additionalFormats()); pixPainter.end(); // Create alpha channel mask @@ -353,7 +385,7 @@ void SenderChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *op pixmap.setAlphaChannel(mask); painter->drawPixmap(0, 0, pixmap); } else { - layout.draw(painter, QPointF(0,0), selectionFormats(), boundingRect()); + layout.draw(painter, QPointF(0,0), additionalFormats(), boundingRect()); } } @@ -507,9 +539,20 @@ ContentsChatItem::Clickable ContentsChatItem::clickableAt(const QPointF &pos) co return Clickable(); } +UiStyle::FormatList ContentsChatItem::formatList() const { + UiStyle::FormatList fmtList = ChatItem::formatList(); + for(int i = 0; i < privateData()->clickables.count(); i++) { + Clickable click = privateData()->clickables.at(i); + if(click.type == Clickable::Url) { + overlayFormat(fmtList, click.start, click.start + click.length, UiStyle::Url); + } + } + return fmtList; +} + QVector ContentsChatItem::additionalFormats() const { + QVector fmt = ChatItem::additionalFormats(); // mark a clickable if hovered upon - QVector fmt; if(privateData()->currentClickable.isValid()) { Clickable click = privateData()->currentClickable; QTextLayout::FormatRange f; diff --git a/src/qtui/chatitem.h b/src/qtui/chatitem.h index e31ce819..9abd761b 100644 --- a/src/qtui/chatitem.h +++ b/src/qtui/chatitem.h @@ -52,6 +52,7 @@ public: doLayout(layout); } virtual void doLayout(QTextLayout *) const; + virtual UiStyle::FormatList formatList() const; virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); enum { Type = ChatScene::ChatItemType }; @@ -85,7 +86,8 @@ protected: void paintBackground(QPainter *); QVector selectionFormats() const; - virtual inline QVector additionalFormats() const { return QVector(); } + virtual QVector additionalFormats() const; + void overlayFormat(UiStyle::FormatList &fmtList, int start, int end, quint32 overlayFmt) const; inline qint16 selectionStart() const { return _selectionStart; } inline void setSelectionStart(qint16 start) { _selectionStart = start; } @@ -190,6 +192,7 @@ protected: doLayout(layout); } virtual void doLayout(QTextLayout *layout) const; + virtual UiStyle::FormatList formatList() const; private: struct Clickable; diff --git a/src/uisupport/qssparser.cpp b/src/uisupport/qssparser.cpp index d8af2dc6..1262be7e 100644 --- a/src/uisupport/qssparser.cpp +++ b/src/uisupport/qssparser.cpp @@ -186,6 +186,8 @@ quint64 QssParser::parseFormatType(const QString &decl) { fmtType |= UiStyle::Hostmask; else if(subElement == "modeflags") fmtType |= UiStyle::ModeFlags; + else if(subElement == "url") + fmtType |= UiStyle::Url; else { qWarning() << Q_FUNC_INFO << tr("Invalid subelement name in %1").arg(decl); return UiStyle::Invalid; diff --git a/src/uisupport/uistyle.cpp b/src/uisupport/uistyle.cpp index 12c38892..0ebdf061 100644 --- a/src/uisupport/uistyle.cpp +++ b/src/uisupport/uistyle.cpp @@ -354,7 +354,7 @@ void UiStyle::mergeFormat(QTextCharFormat &fmt, quint32 ftype, quint64 label) { // URL if(ftype & Url) - mergeSubElementFormat(fmt, ftype & Url, label); + mergeSubElementFormat(fmt, ftype & (Url | 0x000000ff), label); } // Merge a subelement format into an existing message format -- 2.20.1