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
15class KContextualHelpButtonPrivate
16{
17public:
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
37private:
38 KContextualHelpButton *const q_ptr;
39
40 /* The popup showing the contextualHelpText. */
41 QLabel *m_popupLabel = nullptr;
42
43 QPointer<const QWidget> m_heightHintWidget;
44};
45
46KContextualHelpButton::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
52KContextualHelpButton::KContextualHelpButton(QWidget *parent)
53 : KContextualHelpButton{QString{}, nullptr, parent}
54{
55}
56
57KContextualHelpButtonPrivate::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 popup = 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
84KContextualHelpButton::~KContextualHelpButton() = default;
85
86void KContextualHelpButton::setContextualHelpText(const QString &contextualHelpText)
87{
88 d_ptr->setContextualHelpText(contextualHelpText);
89}
90
91void 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
102QString KContextualHelpButton::contextualHelpText() const
103{
104 return d_ptr->contextualHelpText();
105}
106
107QString KContextualHelpButtonPrivate::contextualHelpText() const
108{
109 return m_popupLabel->text();
110}
111
112void KContextualHelpButton::setHeightHintWidget(const QWidget *heightHintWidget)
113{
114 d_ptr->setHeightHintWidget(heightHintWidget);
115}
116
117void KContextualHelpButtonPrivate::setHeightHintWidget(const QWidget *heightHintWidget)
118{
119 m_heightHintWidget = heightHintWidget;
120}
121
122const QWidget *KContextualHelpButton::heightHintWidget() const
123{
124 return d_ptr->heightHintWidget();
125}
126
127const QWidget *KContextualHelpButtonPrivate::heightHintWidget() const
128{
129 return m_heightHintWidget;
130}
131
132QSize KContextualHelpButton::sizeHint() const
133{
134 return d_ptr->preferredSize(fallbackSize: QToolButton::sizeHint());
135}
136
137QSize 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

source code of kwidgetsaddons/src/kcontextualhelpbutton.cpp