summary refs log tree commit diff
path: root/src/ui/TextLabel.cpp
blob: 340d3b8f1eb2d2d2ad2be69c5c87c6ead85b7cc7 (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());
}