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/**
16 * The private class of KContextualHelpButton used for the PIMPL idiom.
17 * \internal
18 */
19class KContextualHelpButtonPrivate
20{
21public:
22 /** @see KContextualHelpButton::KContextualHelpButton() */
23 explicit KContextualHelpButtonPrivate(KContextualHelpButton *q, const QString &contextualHelpText, const QWidget *heightHintWidget);
24
25 /** @see KContextualHelButton::setContextualHelpText() */
26 void setContextualHelpText(const QString &contextualHelpText);
27
28 /** @see KContextualHelButton::contextualHelpText() */
29 QString contextualHelpText() const;
30
31 /** @see KContextualHelpButton::setHeightHintWidget() */
32 void setHeightHintWidget(const QWidget *heightHintWidget);
33
34 /** @see KContextualHelpButton::setHeightHintWidget() */
35 const QWidget *heightHintWidget() const;
36
37 /**
38 * A helper method called from KContextualHelpButton::sizeHint().
39 *
40 * @returns the preferredSize based on m_heightHintWidget and fallbackSize.
41 * @param fallbackSize Used as the width. Also used as the height if there is no m_heightHintWidget.
42 *
43 * @see QWidget::sizeHint()
44 * @see KContextualHelpButton::setHeightHintWidget()
45 */
46 QSize preferredSize(const QSize &fallbackSize) const;
47
48private:
49 KContextualHelpButton *const q_ptr;
50
51 /** The popup showing the contextualHelpText. */
52 QLabel *m_popupLabel = nullptr;
53
54 /** @see KContextualHelpButton::setHeightHintWidget() */
55 QPointer<const QWidget> m_heightHintWidget;
56};
57
58KContextualHelpButton::KContextualHelpButton(const QString &contextualHelpText, const QWidget *heightHintWidget, QWidget *parent)
59 : QToolButton{parent}
60 , d_ptr(std::make_unique<KContextualHelpButtonPrivate>(args: this, args: contextualHelpText, args&: heightHintWidget))
61{
62}
63
64KContextualHelpButton::KContextualHelpButton(QWidget *parent)
65 : KContextualHelpButton{QString{}, nullptr, parent}
66{
67}
68
69KContextualHelpButtonPrivate::KContextualHelpButtonPrivate(KContextualHelpButton *q, const QString &contextualHelpText, const QWidget *heightHintWidget)
70 : q_ptr{q}
71 , m_heightHintWidget{heightHintWidget}
72{
73 q_ptr->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual")));
74 q_ptr->setAutoRaise(true);
75 q_ptr->setCursor(Qt::WhatsThisCursor);
76 // 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
77 // interface. Usually we start button labels with a verb, however the help text contained within this button will automatically be read by the screen
78 // 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
79 // to the current context.
80 q_ptr->setAccessibleName(QObject::tr(s: "Contextual Help", c: "@action:button accessible name"));
81 q_ptr->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis); // Makes sure clicking this button in whatsThisMode triggers it instead of doing nothing.
82
83 auto popup = new QWidgetAction{q_ptr};
84 q_ptr->addAction(action: popup);
85 q_ptr->setPopupMode(QToolButton::InstantPopup);
86
87 m_popupLabel = new QLabel{q_ptr};
88 m_popupLabel->setWordWrap(true);
89 m_popupLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); // Makes the label and links within it keyboard-accessible.
90 m_popupLabel->setOpenExternalLinks(true);
91 popup->setDefaultWidget(m_popupLabel);
92
93 setContextualHelpText(contextualHelpText);
94}
95
96KContextualHelpButton::~KContextualHelpButton() = default;
97
98void KContextualHelpButton::setContextualHelpText(const QString &contextualHelpText)
99{
100 d_ptr->setContextualHelpText(contextualHelpText);
101}
102
103void KContextualHelpButtonPrivate::setContextualHelpText(const QString &contextualHelpText)
104{
105 if (contextualHelpText == q_ptr->toolTip() && contextualHelpText == m_popupLabel->text()) {
106 return;
107 }
108 q_ptr->setToolTip(contextualHelpText);
109 q_ptr->setAccessibleDescription(QTextDocumentFragment::fromHtml(html: contextualHelpText).toPlainText()); // Makes sure html tags aren't read out.
110 m_popupLabel->setText(contextualHelpText);
111 Q_EMIT q_ptr->contextualHelpTextChanged(newContextualHelpText: contextualHelpText);
112}
113
114QString KContextualHelpButton::contextualHelpText() const
115{
116 return d_ptr->contextualHelpText();
117}
118
119QString KContextualHelpButtonPrivate::contextualHelpText() const
120{
121 return m_popupLabel->text();
122}
123
124void KContextualHelpButton::setHeightHintWidget(const QWidget *heightHintWidget)
125{
126 d_ptr->setHeightHintWidget(heightHintWidget);
127}
128
129void KContextualHelpButtonPrivate::setHeightHintWidget(const QWidget *heightHintWidget)
130{
131 m_heightHintWidget = heightHintWidget;
132}
133
134const QWidget *KContextualHelpButton::heightHintWidget() const
135{
136 return d_ptr->heightHintWidget();
137}
138
139const QWidget *KContextualHelpButtonPrivate::heightHintWidget() const
140{
141 return m_heightHintWidget;
142}
143
144QSize KContextualHelpButton::sizeHint() const
145{
146 return d_ptr->preferredSize(fallbackSize: QToolButton::sizeHint());
147}
148
149QSize KContextualHelpButtonPrivate::preferredSize(const QSize &fallbackSize) const
150{
151 return {fallbackSize.width(), m_heightHintWidget ? m_heightHintWidget->sizeHint().height() : fallbackSize.height()};
152}
153

source code of kwidgetsaddons/src/kcontextualhelpbutton.cpp