MauiKit Controls

SwipeView.qml
1/*
2 * Copyright 2020 Camilo Higuita <milo.h@aol.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License as
6 * published by the Free Software Foundation; either version 2, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20import QtQuick
21import QtQml
22
23import QtQuick.Controls
24import QtQuick.Layouts
25
26import org.mauikit.controls as Maui
27
28import "private" as Private
29
30/**
31 *
32 * @since org.mauikit.controls 1.2
33 *
34 * @brief Views switcher component.
35 *
36 * This controls inherits from MauiKit Page, to checkout its inherited properties refer to docs.
37 *
38 * The AppViews control presents a set of children items as views - into an horizontal swipe view, that does not jump around when resizing the application window and that takes care of different gestures and keyboard shortcuts for switching/navigating between the views.
39 *
40 * This component takes care of creating the button view-ports in its page header.
41 *
42 * Each child element represents a view - and each one should have the Controls metadata attached properties to give a title and icon to the view - so that it can be used as the text and icon for the view-port buttons. Some of the supported attached properties to be used are:
43 * - Controls.title
44 * - Controls.iconName
45 * - Controls.badgeText
46 *
47 * The badge item is handled by a MauiKit Badge control.
48 * @see Badge
49 *
50 * @see Controls
51 *
52 * @image html AppViews/viewports.png "The view ports as buttons in the header - the title is expanded for the current view on a wide mode, but compacted in a narrow space"
53 *
54 * @code
55 * AppViews
56 * {
57 * id: _page
58 * anchors.fill: parent
59 * Controls.showCSD: true
60 * headBar.forceCenterMiddleContent: true
61 *
62 * Rectangle
63 * {
64 * Controls.title: "View1"
65 * Controls.iconName: "love"
66 *
67 * color: "blue"
68 * }
69 *
70 * Rectangle
71 * {
72 * Controls.title: "View2"
73 * Controls.iconName: "folder"
74 * Controls.badgeText: "30"
75 * color: "pink"
76 * }
77 *
78 * Rectangle
79 * {
80 * Controls.title: "View3"
81 * Controls.iconName: "tag"
82 *
83 * color: "blue"
84 * }
85 * }
86 * @endcode
87 *
88 * @section notes Notes
89 *
90 * @subsection positioning Positioning & Behaviours
91 * There is not need to set the size or position of the child elements aka "views" - this component will take care of positioning them in the order they have been declared.
92 *
93 * If a child-item or a "view" is hidden via the visible property, then it is also hidden for the view port buttons.
94 *
95 * By default this component is not interactive with touch gestures, in order to not steal focus from other horizontal flickable elements - however you can enable it manually.
96 * @see interactive
97 *
98 * @subsection performance Performance
99 * Ideally do not add too many views, that are loaded simultaneously, since it will affect the application startup time. Instead you can load the views dinamically by using a Loader or the friend control AppViewLoader, which will take care of much of the loading task.
100 * @see AppViewLoader
101 *
102 * Besides taking longer to load - too many views - will also make the header bar too busy with the view-port buttons. This can also be tweaked by setting the maximum number of views visible - the other ones will be tucked away into an overflow menu.
103 * @see maxViews
104 *
105 * @subsection inheritance Inheritance & Shortcuts
106 * This component inherits for the MauiKit Page control, so you can customize it by using the same properties that can be applied to a Page, such as moving the header to the bottom, adding extra toolbars, enabling the pull-back behaviours, etc.
107 * @see Page
108 *
109 * The first four [4] views can be navigated by using keyboard shortcuts: [Ctrl + 1] [Ctrl + 2] [Ctrl + 3] [Ctrl + 4]
110 *
111 * <a href="https://invent.kde.org/maui/mauikit/-/blob/qt6-2/examples/AppViews.qml">You can find a more complete example at this link.</a>
112 * @inherit org::mauikit::controls::Page
113 */
114Maui.Page
115{
116 id: control
117
118 /**
119 * @brief All the child items declared will become part of the views. For each child element to be visible in the view port buttons, you need to use the Controls metadata attached properties.
120 * @see Controls
121 * The content layout is handled by a swipe view.
122 */
123 default property alias content: _swipeView.contentData
124
125 /**
126 * @brief The index number of the current view.
127 * @property int AppViews::currentIndex
128 */
129 property alias currentIndex : _swipeView.currentIndex
130
131 /**
132 * @brief The current item in the view.
133 * @property Item AppViews::currentItem
134 */
135 property alias currentItem : _swipeView.currentItem
136
137 /**
138 * @brief The total amount of items/views.
139 * @property int AppViews::count
140 */
141 property alias count : _swipeView.count
142
143 /**
144 * @brief Sets the views to be interactive by using touch gestures to flick between them.
145 * @property bool AppViews::interactive
146 */
147 property alias interactive : _swipeView.interactive
148
149 focus: true
150
151 /**
152 * @brief Maximum number of views to be shown in the view port buttons at the header bar.
153 * The rest of views buttons will be collapsed into a menu button.
154 * By default the maximum number is set to 4.
155 */
156 property int maxViews : 4
157
158 headBar.forceCenterMiddleContent: !isWide
159 headBar.middleContent: Loader
161 asynchronous: true
162 Layout.alignment: Qt.AlignCenter
163
164 sourceComponent: Private.ActionGroup
165 {
166 id: _actionGroup
167 currentIndex : _swipeView.currentIndex
168 display: ToolButton.TextUnderIcon
169 Binding on currentIndex
170 {
171 value: _swipeView.currentIndex
172 restoreMode: Binding.RestoreValue
173 }
174
175 onCurrentIndexChanged:
176 {
177 _swipeView.currentIndex = currentIndex
178 // _actionGroup.currentIndex = control.currentIndex
179 }
180
181 Component.onCompleted:
182 {
183 for(var i in _swipeView.contentChildren)
184 {
185 const obj = _swipeView.contentChildren[i]
186
187 if(obj.Maui.Controls.title || obj.Maui.Controls.iconName)
188 {
189 if(_actionGroup.items.length < control.maxViews)
190 {
191 _actionGroup.items.push(obj)
192 }else
193 {
194 _actionGroup.hiddenItems.push(obj)
195 }
196 }
197 }
198 }
199
200 onItemVisibilityChanged: (index, visible) =>
201 {
202 console.log("VISIBLE CHILDREN CHANGED", index, visible)
203 if(index === control.currentIndex && !visible && control.visible)
204 {
205 const nextIndex = control.currentIndex+1 === control.count ? 0 : control.currentIndex+1
206 const item = _swipeView.contentChildren[nextIndex]
207 if(item && item.visible)
208 control.goNext()
209 }
210 }
211 }
212 }
213
215 {
216 id:_swipeView
217 anchors.fill: parent
218 interactive: false
219
220 onCurrentItemChanged:
221 {
222 currentItem.forceActiveFocus()
223 _listView.positionViewAtIndex(control.currentIndex , ListView.SnapPosition)
224 history.push(_swipeView.currentIndex)
225 }
226
227 Keys.onBackPressed:
228 {
229 control.goBack()
230 }
231
233 {
234 sequence: StandardKey.Back
235 onActivated: control.goBack()
236 }
237
238 background: null
239 padding: 0
240
241 contentItem: ListView
242 {
243 id: _listView
244 model: _swipeView.contentModel
245 interactive: _swipeView.interactive
246 currentIndex: _swipeView.currentIndex
247 spacing: _swipeView.spacing
248 orientation: _swipeView.orientation
249 snapMode: ListView.SnapOneItem
250 boundsBehavior: Flickable.StopAtBounds
251 clip: _swipeView.clip
252
253 preferredHighlightBegin: 0
254 preferredHighlightEnd: width
255
256 highlightRangeMode: ListView.StrictlyEnforceRange
257 highlightMoveDuration: 0
258 highlightFollowsCurrentItem: true
259 highlightResizeDuration: 0
260 highlightMoveVelocity: -1
261 highlightResizeVelocity: -1
262
263 maximumFlickVelocity: 4 * (_swipeView.orientation === Qt.Horizontal ? width : height)
264
265 property int lastPos: 0
266
267 onCurrentIndexChanged:
268 {
269 _listView.lastPos = _listView.contentX
270 }
271 }
272
273 Keys.enabled: true
274 // Keys.forwardTo:_listView
275 Keys.onPressed: (event) =>
276 {
277 if((event.key == Qt.Key_1) && (event.modifiers & Qt.ControlModifier))
278 {
279 if(_swipeView.count > -1 )
280 {
281 _swipeView.currentIndex = 0
282 }
283 }
284
285 if((event.key == Qt.Key_2) && (event.modifiers & Qt.ControlModifier))
286 {
287 if(_swipeView.count > 0 )
288 {
289 _swipeView.currentIndex = 1
290 }
291 }
292
293 if((event.key == Qt.Key_3) && (event.modifiers & Qt.ControlModifier))
294 {
295 if(_swipeView.count > 1 )
296 {
297 _swipeView.currentIndex = 2
298 }
299 }
300
301 if((event.key == Qt.Key_4) && (event.modifiers & Qt.ControlModifier))
302 {
303 if(_swipeView.count > 2 )
304 {
305 _swipeView.currentIndex = 3
306 }
307 }
308 }
309 }
310
311
312
313 /**
314 * @brief The private object that handles the history workflow
315 * @warning This is a private property and can not be accesed
316 */
317 property QtObject history : QtObject
318 {
319 property var historyIndexes : []
320
321 function pop()
322 {
323 historyIndexes.pop()
324 return historyIndexes.pop()
325 }
326
327 function push(index)
328 {
329 historyIndexes.push(index)
330 }
331
332 function indexes()
333 {
334 return historyIndexes
335 }
336 }
337
338 /**
339 * @brief A quick function to request the control to go back to the previously visited view.
340 * A history of visited views is kept, and invoking this method will pop the history one by one .
341 */
342 function goBack()
343 {
344 _swipeView.setCurrentIndex(history.pop())
345 }
346
347 /**
348 * @brief Jumps to the next view. If it reaches the last one, then it wraps to the first view.
349 */
350 function goNext()
351 {
352 if(control.currentIndex+1 < control.count)
353 _listView.incrementCurrentIndex()
354 else
355 _listView.decrementCurrentIndex()
356 }
357
358 /**
359 * @brief Jumps to the previous view
360 */
361 function goPrevious()
362 {
363 _listView.decrementCurrentIndex()
364 }
365}
Views switcher component.
alias content
All the child items declared will become part of the views.
int maxViews
Maximum number of views to be shown in the view port buttons at the header bar.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri May 2 2025 11:57:11 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.