MauiKit Controls

ListBrowser.qml
1/*
2 * Copyright 2018 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 QtQuick.Controls
22
23import org.mauikit.controls as Maui
24
25/**
26 * @inherit QtQuick.Item
27 * @brief A browser view with a list layout.
28 * <a href="https://doc.qt.io/qt-6/qml-qtquick-controls-item.html">This controls inherits from QQC2 Item, to checkout its inherited properties refer to the Qt Docs.</a>
29 *
30 * This component might seem similar to QQC2 ListView - and it does uses it underneath - but this one includes a few more predefined elements, such as a placeholder element, pinch to zoom gestures, lasso selection support, and some predefined behaviour.
31 *
32 * @section structure Structure
33 * The browser has a dedicated placeholder element handled by MauiKit Holder, where a message can be set when the view is on a determined state the user should be warned about, such as if the view is empty, or not search results were found.
34 * @see Holder
35 *
36 * The lasso selection feature works with a mouse or a track-pad, and allows to select multiple items in the browser-view that are under the lasso rectangle area. A signal is emitted when the selection has been triggered - this is when the lasso rectangle is released - sending as an argument an array of numbers representing the indexes of the selected items.
37 * @see itemsSelected
38 *
39 * @note Consider using as the delegate elements the MauiKit ListBrowserDelegate.
40 *
41 * To position the delegates you can use the ListView attached properties, such as `ListView.view.width` to set the width of the delegate correctly.
42 *
43 * @image html Browsers/listbrowser.png
44 *
45 * @code
46 * Maui.ListBrowser
47 * {
48 * anchors.fill: parent
49 * model: 60
50 *
51 * enableLassoSelection: true
52 * onItemsSelected: (indexes) => console.log(indexes)
53 *
54 * delegate: Maui.ListBrowserDelegate
55 * {
56 * width: ListView.view.width
57 * label1.text: "An example delegate."
58 * label2.text: "Using the MauiKit ListBrowser."
59 *
60 * iconSource: "folder"
61 * }
62 * }
63 * @endcode
64 *
65 * <a href="https://invent.kde.org/maui/mauikit/-/blob/qt6-2/examples/ListBrowser.qml">You can find a more complete example at this link.</a>
66 */
67Item
68{
69 id: control
70 focus: true
71 clip: false
72
73 implicitHeight: contentHeight + topPadding + bottomPadding
74 implicitWidth: contentWidth + leftPadding + rightPadding
75
76 /**
77 * @brief The model to be used to populate the browsing view.
78 * @property var ListBrowser::model
79 */
80 property alias model : _listView.model
81
82 /**
83 * @brief The component to be used as the delegate.
84 * @note Consider using the MauiKit delegate controls, such as ListBrowserDelegate, ListDelegate or LabelDelegate.
85 * @property Component ListBrowser::delegate
86 */
87 property alias delegate : _listView.delegate
88
89 /**
90 * @brief The section group property to set the ListView sections.
91 * Refer to the Qt documentation on the ListView section.
92 * @property section ListBrowser::section
93 */
94 property alias section : _listView.section
95
96 /**
97 * @brief The position of the view contents on the Y axis.
98 * @property double ListBrowser::contentY
99 */
100 property alias contentY: _listView.contentY
101
102 /**
103 * @brief The position of the view contents on the X axis.
104 * @property double ListBrowser::contentY
105 */
106 property alias contentX: _listView.contentX
108 /**
109 * @brief The index number of the current element selected.
110 * @note To no break any binding, use the `setCurrentIndex` function.
111 * @property int ListBrowser::currentIndex
112 */
113 property alias currentIndex : _listView.currentIndex
114
115 /**
116 * @brief The current item selected.
117 * @property Item ListBrowser::currentItem
118 */
119 property alias currentItem : _listView.currentItem
120
121 /**
122 * @brief The total amount of elements listed in the view.
123 * @property int ListBrowser::count
124 */
125 property alias count : _listView.count
126
127 /**
128 * @brief The cache buffer.
129 * Refer to the QQC2 ListView for proper documentation.
130 * @property int ListBrowser::cacheBuffer
131 */
132 property alias cacheBuffer : _listView.cacheBuffer
133
134 /**
135 * @brief The orientation of the list view.
136 * By default this is set to `ListView.Vertical`.
137 * @property enum ListBrowser::orientation
138 */
139 property alias orientation: _listView.orientation
140
141 /**
142 * @brief How to snap the elements of the list view while scrolling.
143 * @note See Qt documentation.
144 * @property enum ListBrowser::snapMode
145 */
146 property alias snapMode: _listView.snapMode
147
148 /**
149 * @brief The spacing between the elements in the list view.
150 * By default this is set to `Style.defaultSpacing`
151 * @property int ListBrowser::spacing
152 */
153 property alias spacing: _listView.spacing
154
155 /**
156 * @brief An alias to access the QQC2 ListView.
157 * @property ListView ListBrowser::flickable
158 */
159 readonly property alias flickable : _listView
160
161 /**
162 * @brief An alias to access the QQC2 ScrollView.
163 * @property ScrollView ListBrowser::scrollView
164 */
165 readonly property alias scrollView : _scrollView
166
167 /**
168 * @brief The total height of all the elements listed in the view.
169 * @property int ListBrowser::contentHeight
170 */
171 property alias contentHeight : _listView.contentHeight
173 /**
174 * @brief The total width of all the elements.
175 * @property int ListBrowser::contentWidth
176 */
177 property alias contentWidth : _listView.contentWidth
179 /**
180 * @brief Whether the view is positioned at the end on the Y axis.
181 * Meant to be used if the view `orientation` has been set to vertical.
182 * @property bool ListBrowser::atYEnd
183 */
184 readonly property alias atYEnd : _listView.atYEnd
185
186 /**
187 * @brief Whether the view is positioned at the beginning on the Y axis.
188 * Meant to be used if the view `orientation` has been set to vertical.
189 * @property bool ListBrowser::atYBeginning
190 */
191 readonly property alias atYBeginning : _listView.atYBeginning
192
193 /**
194 * @brief The top padding.
195 * @see padding
196 * @property int ListBrowser::topPadding
197 */
198 property alias topPadding: _scrollView.topPadding
199
200 /**
201 * @brief The bottom padding.
202 * @see padding
203 * @property int ListBrowser::bottomPadding
204 */
205 property alias bottomPadding: _scrollView.bottomPadding
206
207 /**
208 * @brief The right padding.
209 * @see padding
210 * @property int ListBrowser::rightPadding
211 */
212 property alias rightPadding: _scrollView.rightPadding
213
214 /**
215 * @brief The left padding.
216 * @see padding
217 * @property int ListBrowser::leftPadding
218 */
219 property alias leftPadding: _scrollView.leftPadding
220
221 /**
222 * @brief The total padding all around the list view. The padding is added to the ScrollView.
223 * This is the same as setting `scrollView.padding`.
224 * @property int ListBrowser::padding
225 */
226 property alias padding: _scrollView.padding
227
228 /**
229 * @brief The policy of the vertical scroll bar from the scroll view.
230 * @see scrollView
231 * The default value of this is picked based on the Style property `Style.scrollBarPolicy`, unless the orientation of the view is set to horizontal, in which case this is set to 'ScrollBar.AlwaysOff`.
232 * Possible values are:
233 * - ScrollBar.AlwaysOff
234 * - ScrollBar.AlwaysOn
235 * - ScrollBar.AsNeeded
236 */
237 property int verticalScrollBarPolicy:
238 {
239 if(control.orientation === ListView.Horizontal)
240 return ScrollBar.AlwaysOff
241
242 switch(Maui.Style.scrollBarPolicy)
243 {
244 case Maui.Style.AlwaysOn: return ScrollBar.AlwaysOn;
245 case Maui.Style.AlwaysOff: return ScrollBar.AlwaysOff;
246 case Maui.Style.AsNeeded: return ScrollBar.AsNeeded;
247 case Maui.Style.AutoHide: return ScrollBar.AsNeeded;
248 }
249 }
250
251 /**
252 * @brief The policy of the horizontal scroll bar from the scroll view.
253 * @see scrollView
254 * The default value of this is picked based on the Style property `Style.scrollBarPolicy`, unless the orientation of the view is set to vertical, in which case this is set to 'ScrollBar.AlwaysOff`.
255 * Possible values are:
256 * - ScrollBar.AlwaysOff
257 * - ScrollBar.AlwaysOn
258 * - ScrollBar.AsNeeded
259 */
260 property int horizontalScrollBarPolicy:
261 {
262 if(control.orientation === ListView.Vertical)
263 return ScrollBar.AlwaysOff
265 switch(Maui.Style.scrollBarPolicy)
266 {
267 case Maui.Style.AlwaysOn: return ScrollBar.AlwaysOn;
268 case Maui.Style.AlwaysOff: return ScrollBar.AlwaysOff;
269 case Maui.Style.AsNeeded: return ScrollBar.AsNeeded;
270 case Maui.Style.AutoHide: return ScrollBar.AsNeeded;
271 }
272 }
273
274 /**
275 * @brief An alias to access the placeholder properties. This is handled by a MauiKit Holder.
276 * @see Holder::title
277 * @see Holder::body
278 *
279 * @property Holder ListBrowser::holder
280 */
281 property alias holder : _holder
282
283 /**
284 * @brief Whether to enable the lasso selection, to select multiple items.
285 * By default this is set to `false`.
286 * @see itemsSelected
287 */
288 property bool enableLassoSelection : false
290 /**
291 * @brief
292 */
293 property bool selectionMode: false
294
295 /**
296 * @brief An alias to the lasso rectangle.
297 * @property Rectangle ListBrowser::lassoRec
298 */
299 readonly property alias lassoRec : selectLayer
300
301 /**
302 * @brief The header section of the ListView element.
303 * @see flickable
304 * @property Component ListBrowser::header
305 */
306 property alias header : _listView.header
307
308 /**
309 * @brief The footer section of the ListView element
310 * @see flickable
311 * @property Component ListBrowser::footer
312 */
313 property alias footer : _listView.footer
314
315 /**
316 * @brief The actual width of the view-port. This is the actual width without any padding.
317 * @property int ListBrowser::availableWidth
318 */
319 readonly property alias availableWidth: _listView.width
320
321 /**
322 * @brief The actual height of the view-port. This is the actual height without any padding.
323 * @property int ListBrowser::availableHeight
324 */
325 readonly property alias availableHeight: _listView.height
326
327 /**
328 * @brief Emitted when the lasso selection has been released.
329 * @param indexes A array of index numbers is sent as the argument, representing the index value of the items under the lasso rectangle area.
330 */
331 signal itemsSelected(var indexes)
332
333 /**
334 * @brief Emitted when an empty space of the background area has been clicked.
335 * @param mouse Object with information about the click event.
336 */
337 signal areaClicked(var mouse)
338
339 /**
340 * @brief Emitted when an empty space of the area area background has been right clicked.
341 */
342 signal areaRightClicked()
343
344 /**
345 * @brief Emitted when a physical key from the device has been pressed.
346 * @param event The object with information about the event.
347 */
348 signal keyPress(var event)
349
350 Keys.enabled : true
351 Keys.forwardTo : _listView
352
354 {
355 id: _scrollView
356 anchors.fill: parent
357 clip: control.clip
358 focus: true
359 padding: Maui.Style.contentMargins
360 orientation: _listView.orientation
361
362 ScrollBar.horizontal.policy: control.horizontalScrollBarPolicy
363 ScrollBar.vertical.policy: control.verticalScrollBarPolicy
364
365 contentHeight: _listView.contentHeight
366 contentWidth: availableWidth
367
368 ListView
369 {
370 id: _listView
371 focus: true
372 clip: control.clip
373
374 property var selectedIndexes : []
375
376 spacing: Maui.Style.defaultSpacing
377
378 snapMode: ListView.NoSnap
379
380 displayMarginBeginning: Maui.Style.toolBarHeight * 4
381 displayMarginEnd: Maui.Style.toolBarHeight * 4
382
383 boundsBehavior: Flickable.StopAtBounds
384 boundsMovement: Flickable.StopAtBounds
385
386 interactive: Maui.Handy.hasTransientTouchInput
387 highlightFollowsCurrentItem: true
388 highlightMoveDuration: 0
389 highlightResizeDuration : 0
390
391 keyNavigationEnabled : true
392 keyNavigationWraps : true
393
394 Keys.onPressed: (event) => control.keyPress(event)
395
396 Maui.Holder
397 {
398 id: _holder
399 visible: false
400 anchors.fill : parent
401
402 anchors.topMargin: _listView.headerItem ? _listView.headerItem.height : 0
403 anchors.bottomMargin: _listView.footerItem ? _listView.footerItem.height : 0
404 }
405
406 Item
407 {
408 anchors.fill: parent
409 z: parent.z-1
410 clip: false
411
412 Loader
413 {
414 asynchronous: true
415 anchors.fill: parent
416 // active: !Maui.Handy.hasTransientTouchInput && !Maui.Handy.isMobile
417
418 sourceComponent: MouseArea
419 {
420 id: _mouseArea
421
422 propagateComposedEvents: true
423 preventStealing: true
424 scrollGestureEnabled: false
425 acceptedButtons: Qt.RightButton | Qt.LeftButton
426
427 onClicked: (mouse) =>
428 {
429 console.log("Area clicked")
430
431 control.areaClicked(mouse)
432 control.forceActiveFocus()
433
434 if(mouse.button === Qt.RightButton)
435 {
436 control.areaRightClicked()
437 mouse.accepted = true
438 return
439 }
440 }
441
442 onPositionChanged: (mouse) =>
443 {
444 if(_mouseArea.pressed && control.enableLassoSelection && selectLayer.visible)
445 {
446 if(mouseX >= selectLayer.newX)
447 {
448 selectLayer.width = (mouseX + 10) < (control.x + control.width) ? (mouseX - selectLayer.x) : selectLayer.width;
449 } else {
450 selectLayer.x = mouseX < control.x ? control.x : mouseX;
451 selectLayer.width = selectLayer.newX - selectLayer.x;
452 }
453
454 if(mouseY >= selectLayer.newY) {
455 selectLayer.height = (mouseY + 10) < (control.y + control.height) ? (mouseY - selectLayer.y) : selectLayer.height;
456 if(!_listView.atYEnd && mouseY > (control.y + control.height))
457 _listView.contentY += 10
458 } else {
459 selectLayer.y = mouseY < control.y ? control.y : mouseY;
460 selectLayer.height = selectLayer.newY - selectLayer.y;
461
462 if(!_listView.atYBeginning && selectLayer.y === 0)
463 _listView.contentY -= 10
464 }
465 }
466 }
467
468 onPressed: (mouse) =>
469 {
470 console.log("MOUSE EVENT SOURCE", mouse.source)
471 if (mouse.source === Qt.MouseEventNotSynthesized && control.enableLassoSelection && mouse.button === Qt.LeftButton && control.count > 0)
472 {
473 selectLayer.visible = true;
474 selectLayer.x = mouseX;
475 selectLayer.y = mouseY;
476 selectLayer.newX = mouseX;
477 selectLayer.newY = mouseY;
478 selectLayer.width = 0
479 selectLayer.height = 0;
480 }
481 }
482
483 onPressAndHold: (mouse) =>
484 {
485 if ( mouse.source !== Qt.MouseEventNotSynthesized && control.enableLassoSelection && !selectLayer.visible && !Maui.Handy.hasTransientTouchInput && !Maui.Handy.isAndroid)
486 {
487 selectLayer.visible = true;
488 selectLayer.x = mouseX;
489 selectLayer.y = mouseY;
490 selectLayer.newX = mouseX;
491 selectLayer.newY = mouseY;
492 selectLayer.width = 0
493 selectLayer.height = 0;
494 mouse.accepted = true
495 }else
496 {
497 mouse.accepted = false
498 }
499 }
500
501 onReleased: (mouse) =>
502 {
503 if(mouse.button !== Qt.LeftButton || !control.enableLassoSelection || !selectLayer.visible)
504 {
505 mouse.accepted = false
506 return;
507 }
508
509 if(selectLayer.y > _listView.contentHeight)
510 {
511 return selectLayer.reset();
512 }
513
514 var lassoIndexes = []
515 var limitY = mouse.y === lassoRec.y ? lassoRec.y+lassoRec.height : mouse.y
516 var y = lassoRec.y
517 for(y; y < limitY; y+=10)
518 {
519 const index = _listView.indexAt(_listView.width/2,y+_listView.contentY)
520 if(!lassoIndexes.includes(index) && index>-1 && index< _listView.count)
521 lassoIndexes.push(index)
522 }
523
524 control.itemsSelected(lassoIndexes)
525 console.log("INDEXES << " , lassoIndexes, lassoRec.y, limitY)
526 selectLayer.reset()
527 }
528 }
529 }
530 }
531
532 Maui.Rectangle
533 {
534 id: selectLayer
535 property int newX: 0
536 property int newY: 0
537 height: 0
538 width: 0
539 x: 0
540 y: 0
541 visible: false
542 color: Qt.rgba(control.Maui.Theme.highlightColor.r,control.Maui.Theme.highlightColor.g, control.Maui.Theme.highlightColor.b, 0.2)
543 opacity: 0.7
544
545 borderColor: control.Maui.Theme.highlightColor
546 borderWidth: 2
547 solidBorder: false
548
549 function reset()
550 {
551 selectLayer.x = 0;
552 selectLayer.y = 0;
553 selectLayer.newX = 0;
554 selectLayer.newY = 0;
555 selectLayer.visible = false;
556 selectLayer.width = 0;
557 selectLayer.height = 0;
558 }
559 }
560 }
561 }
562}
563
564
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Wed Nov 6 2024 12:04:21 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.