Kirigami2

controls/templates/InlineMessage.qml
1/*
2 * SPDX-FileCopyrightText: 2018 Eike Hein <hein@kde.org>
3 * SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8import QtQuick
9import QtQuick.Controls as QQC2
10import QtQuick.Templates as T
11import org.kde.kirigami as Kirigami
12import org.kde.kirigami.templates.private as TP
13
14/**
15 * An inline message item with support for informational, positive,
16 * warning and error types, and with support for associated actions.
17 *
18 * InlineMessage can be used to give information to the user or
19 * interact with the user, without requiring the use of a dialog.
20 *
21 * The InlineMessage item is hidden by default. It also manages its
22 * height (and implicitHeight) during an animated reveal when shown.
23 * You should avoid setting height on an InlineMessage unless it is
24 * already visible.
25 *
26 * Optionally an icon can be set, defaulting to an icon appropriate
27 * to the message type otherwise.
28 *
29 * Optionally a close button can be shown.
30 *
31 * Actions are added from left to right. If more actions are set than
32 * can fit, an overflow menu is provided.
33 *
34 * Example:
35 * @code
36 * import org.kde.kirigami as Kirigami
37 *
38 * Kirigami.InlineMessage {
39 * type: Kirigami.MessageType.Error
40 *
41 * text: i18n("My error message")
42 *
43 * actions: [
44 * Kirigami.Action {
45 * icon.name: "list-add"
46 * text: i18n("Add")
47 * onTriggered: source => {
48 * // do stuff
49 * }
50 * },
51 * Kirigami.Action {
52 * icon.name: "edit"
53 * text: i18n("Edit")
54 * onTriggered: source => {
55 * // do stuff
56 * }
57 * }
58 * ]
59 * }
60 * @endcode
61 *
62 * @since 5.45
63 * @inherit QtQuick.Templates.Control
64 */
65T.Control {
66 id: root
67
68 visible: false
69
70 /**
71 * Defines a position for the message: whether it's to be used as an inline component inside the page,
72 * a page header, or a page footer.
73 */
74 enum Position {
75 Inline,
76 Header,
77 Footer
78 }
79
80 /**
81 * Adjust the look of the message based upon the position.
82 * If a message is positioned in the header area or in the footer area
83 * of a page, it might be desirable to not have borders but just a line
84 * separating it from the content area. In this case, use the Header or
85 * Footer position.
86 * Default is InlineMessage.Position.Inline
87 */
88 property int position: InlineMessage.Position.Inline
89
90 /**
91 * This signal is emitted when a link is hovered in the message text.
92 * @param The hovered link.
93 */
94 signal linkHovered(string link)
95
96 /**
97 * This signal is emitted when a link is clicked or tapped in the message text.
98 * @param The clicked or tapped link.
99 */
100 signal linkActivated(string link)
102 /**
103 * This property holds the link embedded in the message text that the user is hovering over.
104 */
105 readonly property alias hoveredLink: label.hoveredLink
106
107 /**
108 * This property holds the message type. One of Information, Positive, Warning or Error.
109 *
110 * The default is Kirigami.MessageType.Information.
111 */
112 property int type: Kirigami.MessageType.Information
113
114 /**
115 * This grouped property holds the description of an optional icon.
116 *
117 * * source: The source of the icon, a freedesktop-compatible icon name is recommended.
118 * * color: An optional tint color for the icon.
119 *
120 * If no custom icon is set, an icon appropriate to the message type
121 * is shown.
122 */
123 property TP.IconPropertiesGroup icon: TP.IconPropertiesGroup {}
124
125 /**
126 * This property holds the message text.
127 */
128 property string text
129
130 /**
131 * This property holds whether the close button is displayed.
132 *
133 * The default is false.
134 */
135 property bool showCloseButton: false
136
137 /**
138 * This property holds the list of actions to show. Actions are added from left to
139 * right. If more actions are set than can fit, an overflow menu is
140 * provided.
141 */
142 property list<T.Action> actions
143
144 /**
145 * This property holds whether the current message item is animating.
146 */
147 readonly property bool animating: _animating
148
149 property bool _animating: false
150
151 implicitHeight: visible ? (contentLayout.implicitHeight + topPadding + bottomPadding) : 0
152
153 padding: Kirigami.Units.smallSpacing
154
155 Accessible.role: Accessible.AlertMessage
156 Accessible.ignored: !visible
157
158 Behavior on implicitHeight {
159 enabled: !root.visible
160
161 SequentialAnimation {
162 PropertyAction { targets: root; property: "_animating"; value: true }
163 NumberAnimation { duration: Kirigami.Units.longDuration }
164 }
165 }
166
167 onVisibleChanged: {
168 if (!visible) {
169 contentLayout.opacity = 0;
170 }
171 }
172
173 opacity: visible ? 1 : 0
174
175 Behavior on opacity {
176 enabled: !root.visible
177
178 NumberAnimation { duration: Kirigami.Units.shortDuration }
179 }
180
181 onOpacityChanged: {
182 if (opacity === 0) {
183 contentLayout.opacity = 0;
184 } else if (opacity === 1) {
185 contentLayout.opacity = 1;
186 }
187 }
188
189 onImplicitHeightChanged: {
190 height = implicitHeight;
191 }
192
193 contentItem: Item {
194 id: contentLayout
195
196 // Used to defer opacity animation until we know if InlineMessage was
197 // initialized visible.
198 property bool complete: false
199
200 Behavior on opacity {
201 enabled: root.visible && contentLayout.complete
202
203 SequentialAnimation {
204 NumberAnimation { duration: Kirigami.Units.shortDuration * 2 }
205 PropertyAction { targets: root; property: "_animating"; value: false }
206 }
207 }
208
209 implicitHeight: {
210 if (atBottom) {
211 return label.implicitHeight + actionsLayout.implicitHeight + actionsLayout.anchors.topMargin
212 } else {
213 return Math.max(icon.implicitHeight, label.implicitHeight, closeButton.implicitHeight, actionsLayout.implicitHeight)
214 }
215 }
216
217 Accessible.ignored: true
218
219 readonly property real remainingWidth: width - (
220 icon.width
221 + label.anchors.leftMargin + label.implicitWidth + label.anchors.rightMargin
222 + (root.showCloseButton ? closeButton.width : 0)
223 )
224 readonly property bool multiline: remainingWidth <= 0 || atBottom
225
226 readonly property bool atBottom: (root.actions.length > 0) && (label.lineCount > 1 || actionsLayout.implicitWidth > remainingWidth)
227
228 Kirigami.Icon {
229 id: icon
230
231 width: Kirigami.Units.iconSizes.smallMedium
232 height: Kirigami.Units.iconSizes.smallMedium
233
234 anchors {
235 left: parent.left
236 leftMargin: Kirigami.Units.smallSpacing
237 topMargin: Kirigami.Units.smallSpacing
238 }
239
240 states: [
241 State {
242 name: "multi-line"
243 when: contentLayout.atBottom || label.height > icon.height * 1.7
244 AnchorChanges {
245 target: icon
246 anchors.top: icon.parent.top
247 anchors.verticalCenter: undefined
248 }
249 },
250 // States are evaluated in the order they are declared.
251 // This is a fallback state.
252 State {
253 name: "single-line"
254 when: true
255 AnchorChanges {
256 target: icon
257 anchors.top: undefined
258 anchors.verticalCenter: parent.verticalCenter
259 }
260 }
261 ]
262
263 source: {
264 if (root.icon.name) {
265 return root.icon.name;
266 } else if (root.icon.source) {
267 return root.icon.source;
268 }
269
270 switch (root.type) {
271 case Kirigami.MessageType.Positive:
272 return "emblem-success";
273 case Kirigami.MessageType.Warning:
274 return "emblem-warning";
275 case Kirigami.MessageType.Error:
276 return "emblem-error";
277 default:
278 return "emblem-information";
279 }
280 }
281
282 color: root.icon.color
283
284 Accessible.ignored: !root.visible
285 Accessible.name: {
286 switch (root.type) {
287 case Kirigami.MessageType.Positive:
288 return qsTr("Success");
289 case Kirigami.MessageType.Warning:
290 return qsTr("Warning");
291 case Kirigami.MessageType.Error:
292 return qsTr("Error");
293 default:
294 return qsTr("Note");
295 }
296 }
297 }
298
299 Kirigami.SelectableLabel {
300 id: label
301
302 anchors {
303 left: icon.right
304 leftMargin: Kirigami.Units.largeSpacing
305 right: root.showCloseButton ? closeButton.left : parent.right
306 rightMargin: root.showCloseButton ? Kirigami.Units.smallSpacing : 0
307 top: parent.top
308 }
309
310 color: Kirigami.Theme.textColor
311 wrapMode: Text.WordWrap
312
313 text: root.text
314
315 verticalAlignment: Text.AlignVCenter
316
317 // QTBUG-117667 TextEdit (super-type of SelectableLabel) needs
318 // very specific state-management trick so it doesn't get stuck.
319 // State names serve purely as a description.
320 states: [
321 State {
322 name: "multi-line"
323 when: contentLayout.multiline
324 AnchorChanges {
325 target: label
326 anchors.bottom: undefined
327 }
328 PropertyChanges {
329 target: label
330 height: label.implicitHeight
331 }
332 },
333 // States are evaluated in the order they are declared.
334 // This is a fallback state.
335 State {
336 name: "single-line"
337 when: true
338 AnchorChanges {
339 target: label
340 anchors.bottom: label.parent.bottom
341 }
342 }
343 ]
344
345 onLinkHovered: link => root.linkHovered(link)
346 onLinkActivated: link => root.linkActivated(link)
347
348 Accessible.ignored: !root.visible
349 }
350
351 Kirigami.ActionToolBar {
352 id: actionsLayout
353
354 flat: false
355 actions: root.actions
356 visible: root.actions.length > 0
357 Accessible.ignored: !visible || !root.visible
358 alignment: Qt.AlignRight
359
360 anchors {
361 left: parent.left
362 top: contentLayout.atBottom ? label.bottom : parent.top
363 topMargin: contentLayout.atBottom ? Kirigami.Units.largeSpacing : 0
364 right: (!contentLayout.atBottom && root.showCloseButton) ? closeButton.left : parent.right
365 rightMargin: !contentLayout.atBottom && root.showCloseButton ? Kirigami.Units.smallSpacing : 0
366 }
367 }
368
369 QQC2.ToolButton {
370 id: closeButton
371
372 visible: root.showCloseButton
373
374 anchors.right: parent.right
375
376 // Incompatible anchors need to be evaluated in a given order,
377 // which simple declarative bindings cannot assure
378 states: [
379 State {
380 name: "onTop"
381 when: contentLayout.atBottom
382 AnchorChanges {
383 target: closeButton
384 anchors.top: parent.top
385 anchors.verticalCenter: undefined
386 }
387 } ,
388 State {
389 name: "centered"
390 AnchorChanges {
391 target: closeButton
392 anchors.top: undefined
393 anchors.verticalCenter: parent.verticalCenter
394 }
395 }
396 ]
397
398 height: contentLayout.atBottom ? implicitHeight : implicitHeight
399
400 text: qsTr("Close")
401 display: QQC2.ToolButton.IconOnly
402 icon.name: "dialog-close"
403
404 onClicked: root.visible = false
405
406 Accessible.ignored: !root.visible
407 }
408
409 Component.onCompleted: complete = true
410 }
411}
Class for rendering an icon in UI.
Definition icon.h:35
KIOCORE_EXPORT CopyJob * link(const QList< QUrl > &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
QString name(StandardAction id)
QString label(StandardShortcut id)
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:56:52 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.