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
24KateMessageWidget::KateMessageWidget(QWidget *parent, bool applyFadeEffect)
25 : QWidget(parent)
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()) {
96 break;
99 break;
101 m_messageWidget->setMessageType(KMessageWidget::Warning);
102 break;
104 m_messageWidget->setMessageType(KMessageWidget::Error);
105 break;
106 default:
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"
void removeAction(QAction *action)
void setCloseButtonVisible(bool visible)
QString text() const
void linkHovered(const QString &contents)
void addAction(QAction *action)
void setMessageType(KMessageWidget::MessageType type)
void setIcon(const QIcon &icon)
void setWordWrap(bool wordWrap)
void setPosition(Position position)
bool wordWrap() const
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.
bool isHideAnimationRunning() const
Returns true, if the hide animation is running, otherwise false.
void show()
Call to show and fade in the widget.
@ FadeEffect
fade in/out
@ GrowEffect
grow / shrink
void widgetHidden()
This signal is emitted when the hiding animation is finished.
bool isShowAnimationRunning() const
Returns true, if the how animation is running, otherwise false.
void hide()
Call to hide the widget.
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()
bool contains(const Key &key) const const
bool remove(const Key &key)
void getContentsMargins(int *left, int *top, int *right, int *bottom) const const
void setContentsMargins(const QMargins &margins)
qsizetype count() const const
iterator insert(const_iterator before, parameter_type value)
void removeAt(qsizetype i)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
bool disconnect(const QMetaObject::Connection &connection)
UniqueConnection
bool isActive() const const
void setSingleShot(bool singleShot)
void start()
void stop()
void timeout()
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
QList< QAction * > actions() const const
void adjustSize()
void ensurePolished() 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-2024 The KDE developers.
Generated on Fri May 17 2024 11:56:22 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.