summary refs log tree commit diff
path: root/src/ui/TextLabel.cpp
blob: 3568e15c5b00934368350fe7a6c8c61c9ebddc7f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "ui/TextLabel.h"

#include <QAbstractTextDocumentLayout>
#include <QDesktopServices>
#include <QEvent>
#include <QWheelEvent>

#include "Utils.h"

bool
ContextMenuFilter::eventFilter(QObject *obj, QEvent *event)
{
        if (event->type() == QEvent::MouseButtonPress) {
                emit contextMenuIsOpening();
                return true;
        }

        return QObject::eventFilter(obj, event);
}

TextLabel::TextLabel(QWidget *parent)
  : TextLabel(QString(), parent)
{}

TextLabel::TextLabel(const QString &text, QWidget *parent)
  : QTextBrowser(parent)
{
        document()->setDefaultStyleSheet(QString("a {color: %1; }").arg(utils::linkColor()));

        setText(text);
        setOpenExternalLinks(true);

        // Make it look and feel like an ordinary label.
        setReadOnly(true);
        setFrameStyle(QFrame::NoFrame);
        QPalette pal = palette();
        pal.setColor(QPalette::Base, Qt::transparent);
        setPalette(pal);

        // Wrap anywhere but prefer words, adjust minimum height on the fly.
        setLineWrapMode(QTextEdit::WidgetWidth);
        setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
        connect(document()->documentLayout(),
                &QAbstractTextDocumentLayout::documentSizeChanged,
                this,
                &TextLabel::adjustHeight);
        document()->setDocumentMargin(0);

        setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
        setFixedHeight(0);

        connect(this, &TextLabel::linkActivated, this, &TextLabel::handleLinkActivation);

        auto filter = new ContextMenuFilter(this);
        installEventFilter(filter);
        connect(filter, &ContextMenuFilter::contextMenuIsOpening, this, [this]() {
                contextMenuRequested_ = true;
        });
}

void
TextLabel::focusOutEvent(QFocusEvent *e)
{
        QTextBrowser::focusOutEvent(e);

        // We keep the selection available for the context menu.
        if (contextMenuRequested_) {
                contextMenuRequested_ = false;
                return;
        }

        QTextCursor cursor = textCursor();
        cursor.clearSelection();
        setTextCursor(cursor);
}

void
TextLabel::mousePressEvent(QMouseEvent *e)
{
        link_ = (e->button() & Qt::LeftButton) ? anchorAt(e->pos()) : QString();
        QTextBrowser::mousePressEvent(e);
}

void
TextLabel::mouseReleaseEvent(QMouseEvent *e)
{
        if (e->button() & Qt::LeftButton && !link_.isEmpty() && anchorAt(e->pos()) == link_) {
                emit linkActivated(link_);
                return;
        }

        QTextBrowser::mouseReleaseEvent(e);
}

void
TextLabel::wheelEvent(QWheelEvent *event)
{
        event->ignore();
}

void
TextLabel::handleLinkActivation(const QUrl &url)
{
        auto parts          = url.toString().split('/');
        auto defaultHandler = [](const QUrl &url) { QDesktopServices::openUrl(url); };

        if (url.host() != "matrix.to" || parts.isEmpty())
                return defaultHandler(url);

        try {
                using namespace mtx::identifiers;
                parse<User>(parts.last().toStdString());
        } catch (const std::exception &) {
                return defaultHandler(url);
        }

        emit userProfileTriggered(parts.last());
}