1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 2024 Felix Ernst <felixernst@kde.org> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
6 | */ |
7 | |
8 | #include "kcontextualhelpbutton.h" |
9 | |
10 | #include <QLabel> |
11 | #include <QPointer> |
12 | #include <QTextDocumentFragment> |
13 | #include <QWidgetAction> |
14 | |
15 | class KContextualHelpButtonPrivate |
16 | { |
17 | public: |
18 | explicit KContextualHelpButtonPrivate(KContextualHelpButton *q, const QString &contextualHelpText, const QWidget *heightHintWidget); |
19 | |
20 | void setContextualHelpText(const QString &contextualHelpText); |
21 | |
22 | QString contextualHelpText() const; |
23 | |
24 | void setHeightHintWidget(const QWidget *heightHintWidget); |
25 | |
26 | const QWidget *heightHintWidget() const; |
27 | |
28 | /* |
29 | * A helper method called from KContextualHelpButton::sizeHint(). |
30 | * |
31 | * Returns the preferredSize based on m_heightHintWidget and fallbackSize. |
32 | * |
33 | * fallbackSize is used as the width. Also used as the height if there is no m_heightHintWidget. |
34 | */ |
35 | QSize preferredSize(const QSize &fallbackSize) const; |
36 | |
37 | private: |
38 | KContextualHelpButton *const q_ptr; |
39 | |
40 | /* The popup showing the contextualHelpText. */ |
41 | QLabel * = nullptr; |
42 | |
43 | QPointer<const QWidget> m_heightHintWidget; |
44 | }; |
45 | |
46 | KContextualHelpButton::KContextualHelpButton(const QString &contextualHelpText, const QWidget *heightHintWidget, QWidget *parent) |
47 | : QToolButton{parent} |
48 | , d_ptr(std::make_unique<KContextualHelpButtonPrivate>(args: this, args: contextualHelpText, args&: heightHintWidget)) |
49 | { |
50 | } |
51 | |
52 | KContextualHelpButton::KContextualHelpButton(QWidget *parent) |
53 | : KContextualHelpButton{QString{}, nullptr, parent} |
54 | { |
55 | } |
56 | |
57 | KContextualHelpButtonPrivate::KContextualHelpButtonPrivate(KContextualHelpButton *q, const QString &contextualHelpText, const QWidget *heightHintWidget) |
58 | : q_ptr{q} |
59 | , m_heightHintWidget{heightHintWidget} |
60 | { |
61 | q_ptr->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual" ))); |
62 | q_ptr->setAutoRaise(true); |
63 | q_ptr->setCursor(Qt::WhatsThisCursor); |
64 | // i18n: This text will only ever be read by assistive technology like screen readers for visually-impaired. It shows up nowhere on the visible user |
65 | // interface. Usually we start button labels with a verb, however the help text contained within this button will automatically be read by the screen |
66 | // reader without requiring additional user input. In this sense the label here is more of an announcement that the text read afterwards is help relevant |
67 | // to the current context. |
68 | q_ptr->setAccessibleName(QObject::tr(s: "Contextual Help" , c: "@action:button accessible name" )); |
69 | q_ptr->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis); // Makes sure clicking this button in whatsThisMode triggers it instead of doing nothing. |
70 | |
71 | auto = new QWidgetAction{q_ptr}; |
72 | q_ptr->addAction(action: popup); |
73 | q_ptr->setPopupMode(QToolButton::InstantPopup); |
74 | |
75 | m_popupLabel = new QLabel{q_ptr}; |
76 | m_popupLabel->setWordWrap(true); |
77 | m_popupLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); // Makes the label and links within it keyboard-accessible. |
78 | m_popupLabel->setOpenExternalLinks(true); |
79 | popup->setDefaultWidget(m_popupLabel); |
80 | |
81 | setContextualHelpText(contextualHelpText); |
82 | } |
83 | |
84 | KContextualHelpButton::~KContextualHelpButton() = default; |
85 | |
86 | void KContextualHelpButton::setContextualHelpText(const QString &contextualHelpText) |
87 | { |
88 | d_ptr->setContextualHelpText(contextualHelpText); |
89 | } |
90 | |
91 | void KContextualHelpButtonPrivate::setContextualHelpText(const QString &contextualHelpText) |
92 | { |
93 | if (contextualHelpText == q_ptr->toolTip() && contextualHelpText == m_popupLabel->text()) { |
94 | return; |
95 | } |
96 | q_ptr->setToolTip(contextualHelpText); |
97 | q_ptr->setAccessibleDescription(QTextDocumentFragment::fromHtml(html: contextualHelpText).toPlainText()); // Makes sure html tags aren't read out. |
98 | m_popupLabel->setText(contextualHelpText); |
99 | Q_EMIT q_ptr->contextualHelpTextChanged(newContextualHelpText: contextualHelpText); |
100 | } |
101 | |
102 | QString KContextualHelpButton::contextualHelpText() const |
103 | { |
104 | return d_ptr->contextualHelpText(); |
105 | } |
106 | |
107 | QString KContextualHelpButtonPrivate::contextualHelpText() const |
108 | { |
109 | return m_popupLabel->text(); |
110 | } |
111 | |
112 | void KContextualHelpButton::setHeightHintWidget(const QWidget *heightHintWidget) |
113 | { |
114 | d_ptr->setHeightHintWidget(heightHintWidget); |
115 | } |
116 | |
117 | void KContextualHelpButtonPrivate::setHeightHintWidget(const QWidget *heightHintWidget) |
118 | { |
119 | m_heightHintWidget = heightHintWidget; |
120 | } |
121 | |
122 | const QWidget *KContextualHelpButton::heightHintWidget() const |
123 | { |
124 | return d_ptr->heightHintWidget(); |
125 | } |
126 | |
127 | const QWidget *KContextualHelpButtonPrivate::heightHintWidget() const |
128 | { |
129 | return m_heightHintWidget; |
130 | } |
131 | |
132 | QSize KContextualHelpButton::sizeHint() const |
133 | { |
134 | return d_ptr->preferredSize(fallbackSize: QToolButton::sizeHint()); |
135 | } |
136 | |
137 | QSize KContextualHelpButtonPrivate::preferredSize(const QSize &fallbackSize) const |
138 | { |
139 | return {fallbackSize.width(), m_heightHintWidget ? m_heightHintWidget->sizeHint().height() : fallbackSize.height()}; |
140 | } |
141 | |
142 | #include "moc_kcontextualhelpbutton.cpp" |
143 | |