KTextEditor

katemessagewidget.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Dominik Haumann <dhaumann@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "katemessagewidget.h"
8
9#include "katepartdebug.h"
10
11#include <KTextEditor/Message>
12
13#include <KMessageWidget>
14#include <kateanimation.h>
15
16#include <QEvent>
17#include <QShowEvent>
18#include <QTimer>
19#include <QToolTip>
20#include <QVBoxLayout>
21
22static const int s_defaultAutoHideTime = 6 * 1000;
23
26 , m_animation(nullptr)
27 , m_autoHideTimer(new QTimer(this))
28 , m_autoHideTime(-1)
29{
30 QVBoxLayout *l = new QVBoxLayout(this);
31 l->setContentsMargins(0, 0, 0, 0);
32
33 m_messageWidget = new KMessageWidget(this);
34 m_messageWidget->setCloseButtonVisible(false);
35
36 l->addWidget(m_messageWidget);
37
38 // tell the widget to always use the minimum size.
40
41 // by default, hide widgets
42 m_messageWidget->hide();
43 hide();
44
45 // create animation controller, and connect widgetHidden() to showNextMessage()
46 m_animation = new KateAnimation(m_messageWidget, applyFadeEffect ? KateAnimation::FadeEffect : KateAnimation::GrowEffect);
48
49 // setup autoHide timer details
50 m_autoHideTimer->setSingleShot(true);
51
53}
54
55void KateMessageWidget::setPosition(KateMessageWidget::Position position)
56{
57 switch (position) {
59 m_messageWidget->setPosition(KMessageWidget::Inline);
60 return;
62 m_messageWidget->setPosition(KMessageWidget::Header);
63 return;
65 m_messageWidget->setPosition(KMessageWidget::Footer);
66 return;
67 }
68}
69
71{
72 // at this point, we should not have a currently shown message
73 Q_ASSERT(m_currentMessage == nullptr);
74
75 // if not message to show, just stop
76 if (m_messageQueue.size() == 0) {
77 hide();
78 return;
79 }
80
81 // track current message
82 m_currentMessage = m_messageQueue[0];
83
84 // set text etc.
85 m_messageWidget->setText(m_currentMessage->text());
86 m_messageWidget->setIcon(m_currentMessage->icon());
87
88 // connect textChanged() and iconChanged(), so it's possible to change this on the fly
91
92 // the enums values do not necessarily match, hence translate with switch
93 switch (m_currentMessage->messageType()) {
95 m_messageWidget->setMessageType(KMessageWidget::Positive);
96 break;
98 m_messageWidget->setMessageType(KMessageWidget::Information);
99 break;
101 m_messageWidget->setMessageType(KMessageWidget::Warning);
102 break;
104 m_messageWidget->setMessageType(KMessageWidget::Error);
105 break;
106 default:
107 m_messageWidget->setMessageType(KMessageWidget::Information);
108 break;
109 }
110
111 // remove all actions from the message widget
112 const auto messageWidgetActions = m_messageWidget->actions();
113 for (QAction *a : messageWidgetActions) {
114 m_messageWidget->removeAction(a);
115 }
116
117 // add new actions to the message widget
118 const auto m_currentMessageActions = m_currentMessage->actions();
119 for (QAction *a : m_currentMessageActions) {
120 m_messageWidget->addAction(a);
121 }
122
123 // set word wrap of the message
124 setWordWrap(m_currentMessage);
125
126 // setup auto-hide timer, and start if requested
127 m_autoHideTime = m_currentMessage->autoHide();
128 m_autoHideTimer->stop();
129 if (m_autoHideTime >= 0) {
130 connect(m_autoHideTimer, &QTimer::timeout, m_currentMessage, &QObject::deleteLater, Qt::UniqueConnection);
131 if (m_currentMessage->autoHideMode() == KTextEditor::Message::Immediate) {
132 m_autoHideTimer->start(m_autoHideTime == 0 ? s_defaultAutoHideTime : m_autoHideTime);
133 }
134 }
135
136 // finally show
137 show();
138 m_animation->show();
139}
140
142{
143 // want word wrap anyway? -> ok
144 if (message->wordWrap()) {
145 m_messageWidget->setWordWrap(message->wordWrap());
146 return;
147 }
148
149 // word wrap not wanted, that's ok if a parent widget does not exist
150 if (!parentWidget()) {
151 m_messageWidget->setWordWrap(false);
152 return;
153 }
154
155 // word wrap not wanted -> enable word wrap if it breaks the layout otherwise
156 int margin = 0;
157 if (parentWidget()->layout()) {
158 // get left/right margin of the layout, since we need to subtract these
159 int leftMargin = 0;
160 int rightMargin = 0;
161 parentWidget()->layout()->getContentsMargins(&leftMargin, nullptr, &rightMargin, nullptr);
162 margin = leftMargin + rightMargin;
163 }
164
165 // if word wrap enabled, first disable it
166 if (m_messageWidget->wordWrap()) {
167 m_messageWidget->setWordWrap(false);
168 }
169
170 // make sure the widget's size is up-to-date in its hidden state
171 m_messageWidget->ensurePolished();
172 m_messageWidget->adjustSize();
173
174 // finally enable word wrap, if there is not enough free horizontal space
175 const int freeSpace = (parentWidget()->width() - margin) - m_messageWidget->width();
176 if (freeSpace < 0) {
177 // qCDebug(LOG_KTE) << "force word wrap to avoid breaking the layout" << freeSpace;
178 m_messageWidget->setWordWrap(true);
179 }
180}
181
182void KateMessageWidget::postMessage(KTextEditor::Message *message, QList<std::shared_ptr<QAction>> actions)
183{
184 Q_ASSERT(!m_messageHash.contains(message));
185 m_messageHash[message] = std::move(actions);
186
187 // insert message sorted after priority
188 int i = 0;
189 for (; i < m_messageQueue.count(); ++i) {
190 if (message->priority() > m_messageQueue[i]->priority()) {
191 break;
192 }
193 }
194
195 // queue message
196 m_messageQueue.insert(i, message);
197
198 // catch if the message gets deleted
200
201 if (i == 0 && !m_animation->isHideAnimationRunning()) {
202 // if message has higher priority than the one currently shown,
203 // then hide the current one and then show the new one.
204 if (m_currentMessage) {
205 // autoHide timer may be running for currently shown message, therefore
206 // simply disconnect autoHide timer to all timeout() receivers
207 disconnect(m_autoHideTimer, &QTimer::timeout, nullptr, nullptr);
208 m_autoHideTimer->stop();
209
210 // if there is a current message, the message queue must contain 2 messages
211 Q_ASSERT(m_messageQueue.size() > 1);
212 Q_ASSERT(m_currentMessage == m_messageQueue[1]);
213
214 // a bit unnice: disconnect textChanged() and iconChanged() signals of previously visible message
215 disconnect(m_currentMessage, &KTextEditor::Message::textChanged, m_messageWidget, &KMessageWidget::setText);
216 disconnect(m_currentMessage, &KTextEditor::Message::iconChanged, m_messageWidget, &KMessageWidget::setIcon);
217
218 m_currentMessage = nullptr;
219 m_animation->hide();
220 } else {
222 }
223 }
224}
225
227{
228 // last moment when message is valid, since KTE::Message is already in
229 // destructor we have to do the following:
230 // 1. remove message from m_messageQueue, so we don't care about it anymore
231 // 2. activate hide animation or show a new message()
232
233 // remove widget from m_messageQueue
234 int i = 0;
235 for (; i < m_messageQueue.count(); ++i) {
236 if (m_messageQueue[i] == message) {
237 break;
238 }
239 }
240
241 // the message must be in the list
242 Q_ASSERT(i < m_messageQueue.count());
243
244 // remove message
245 m_messageQueue.removeAt(i);
246
247 // remove message from hash -> release QActions
248 Q_ASSERT(m_messageHash.contains(message));
249 m_messageHash.remove(message);
250
251 // if deleted message is the current message, launch hide animation
252 if (message == m_currentMessage) {
253 m_currentMessage = nullptr;
254 m_animation->hide();
255 }
256}
257
259{
260 // message does not want autohide, or timer already running
261 if (!m_currentMessage // no message, nothing to do
262 || m_autoHideTime < 0 // message does not want auto-hide
263 || m_autoHideTimer->isActive() // auto-hide timer is already active
264 || m_animation->isHideAnimationRunning() // widget is in hide animation phase
265 || m_animation->isShowAnimationRunning() // widget is in show animation phase
266 ) {
267 return;
268 }
269
270 // safety checks: the message must still be valid
271 Q_ASSERT(m_messageQueue.size());
272 Q_ASSERT(m_currentMessage->autoHide() == m_autoHideTime);
273
274 // start autoHide timer as requested
275 m_autoHideTimer->start(m_autoHideTime == 0 ? s_defaultAutoHideTime : m_autoHideTime);
276}
277
279{
280 QToolTip::showText(QCursor::pos(), link, m_messageWidget);
281}
282
283QString KateMessageWidget::text() const
284{
285 return m_messageWidget->text();
286}
287
288#include "moc_katemessagewidget.cpp"
QString text() const
void linkHovered(const QString &contents)
void setIcon(const QIcon &icon)
void setPosition(Position position)
void setText(const QString &text)
This class holds a Message to display in Views.
Definition message.h:94
bool wordWrap() const
Check, whether word wrap is enabled or not.
void closed(KTextEditor::Message *message)
This signal is emitted before the message is deleted.
void textChanged(const QString &text)
This signal is emitted whenever setText() was called.
@ Error
error message type
Definition message.h:110
@ Information
information message type
Definition message.h:108
@ Positive
positive information message
Definition message.h:107
@ Warning
warning message type
Definition message.h:109
int priority() const
Returns the priority of the message.
@ Immediate
auto-hide is triggered as soon as the message is shown
Definition message.h:136
void iconChanged(const QIcon &icon)
This signal is emitted whenever setIcon() was called.
This class provides a fade in/out effect for KMessageWidgets.
@ FadeEffect
fade in/out
@ GrowEffect
grow / shrink
void widgetHidden()
This signal is emitted when the hiding animation is finished.
void setWordWrap(KTextEditor::Message *message)
Helper that enables word wrap to avoid breaking the layout.
Position
The position of the KateMessageWidget.
@ Header
Message positioned at the top of the view.
@ Footer
Message positioned at the bottom of the view.
void linkHovered(const QString &link)
User hovers on a link in the message widget.
void showNextMessage()
Show the next message in the queue.
void messageDestroyed(KTextEditor::Message *message)
catch when a message is deleted, then show next one, if applicable.
void startAutoHideTimer()
Start autoHide timer if requested.
void postMessage(KTextEditor::Message *message, QList< std::shared_ptr< QAction > > actions)
Post a new incoming message.
KateMessageWidget(QWidget *parent, bool applyFadeEffect=false)
Constructor.
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QPoint pos()
void getContentsMargins(int *left, int *top, int *right, int *bottom) const const
void setContentsMargins(const QMargins &margins)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
UniqueConnection
void timeout()
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
QWidget(QWidget *parent, Qt::WindowFlags f)
QList< QAction * > actions() const const
void hide()
QLayout * layout() const const
QWidget * parentWidget() const const
void show()
void setSizePolicy(QSizePolicy)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:55:24 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.