Warning, /maui/mauikit/src/controls.6/SelectionBar.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 * Copyright 2018 Camilo Higuita <milo.h@aol.com> 0003 * 0004 * This program is free software; you can redistribute it and/or modify 0005 * it under the terms of the GNU Library General Public License as 0006 * published by the Free Software Foundation; either version 2, or 0007 * (at your option) any later version. 0008 * 0009 * This program is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 * GNU General Public License for more details 0013 * 0014 * You should have received a copy of the GNU Library General Public 0015 * License along with this program; if not, write to the 0016 * Free Software Foundation, Inc., 0017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0018 */ 0019 0020 import QtQuick 0021 import QtQuick.Controls 0022 import QtQuick.Layouts 0023 import QtQuick.Window 0024 0025 import org.mauikit.controls 1.3 as Maui 0026 0027 import Qt5Compat.GraphicalEffects 0028 0029 /** 0030 * @inherit QtQuick.Item 0031 * @brief A bar to group selected items and a list of actions to perform to such selection. 0032 * 0033 * <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> 0034 * 0035 * The list of actions is represented by a set of buttons in a horizontal row layout. 0036 * @see actions 0037 * 0038 * This control provides methods to append and query elements added to it. To add elements, it is necesary to map them, 0039 * so each item has an unique ID referred here as an URI. 0040 * @see append 0041 * 0042 * 0043 * @image html Misc/selectionbar.png 0044 * 0045 * @section notes Notes 0046 * 0047 * By default it is hidden when the selection is empty. 0048 * @see count 0049 * 0050 * The SelectionBar is usually placed as the `footer` element of a Page. 0051 * This control is styled as a floating bar, so it can be placed on top of its parent contents; due to this behavior it is a good idea to pair it with a MauiKit Page as its footer, and set the Page property `floatingFooter: true`. 0052 * 0053 * Most of the times this will be uses along with a browsing view, such as a ListBrowser or GridBrowser, which will list the items to be added or removed from the selection. 0054 * 0055 * Here we have three components that can be intertwined: Page, ListBrowser and the SelectionBar. 0056 * For the floating selection bar to not get on the way of the contents, the Page property `flickable` needs to be set to the flickable element of the browsing view, such as the ListBrowser::flickable. 0057 * @see ListBrowser::flickable 0058 * @see Page::flickable 0059 * 0060 * Below you will find a more complete example of the functionality of these three controls out together. 0061 * 0062 * @code 0063 * Maui.Page 0064 * { 0065 * anchors.fill: parent 0066 * 0067 * Maui.Controls.showCSD: true 0068 * 0069 * floatingFooter: true 0070 * flickable: _listBrowser.flickable //helps to keep the content from under the selection bar at the end. 0071 * 0072 * headBar.leftContent: Switch 0073 * { 0074 * text: "Single selection" 0075 * checked: _selectionBar.singleSelection 0076 * onToggled: _selectionBar.singleSelection = !_selectionBar.singleSelection 0077 * } 0078 * 0079 * Maui.ListBrowser 0080 * { 0081 * id: _listBrowser 0082 * 0083 * anchors.fill: parent 0084 * 0085 * model: 60 0086 * 0087 * delegate: Maui.ListBrowserDelegate 0088 * { 0089 * id: _delegate 0090 * 0091 * property string id : index // we need an unique ID for the selectio nbar 0092 * 0093 * width: ListView.view.width 0094 * 0095 * label1.text: "An example delegate." 0096 * label2.text: "The ID of this element is " + id 0097 * 0098 * iconSource: "folder" 0099 * 0100 * checkable: true 0101 * 0102 * Connections 0103 * { 0104 * target: _selectionBar 0105 * function onUriRemoved(uri) //watch when a uri is removed from the selection bar 0106 * { 0107 * if(uri == _delegate.id) 0108 * { 0109 * _delegate.checked = false 0110 * } 0111 * } 0112 * 0113 * function onUriAdded(uri) //watch when an uri is successfully added and mark the delegate as checked 0114 * { 0115 * if(uri == _delegate.id) 0116 * { 0117 * _delegate.checked = true 0118 * } 0119 * } 0120 * 0121 * function onCleared() //watch when the selection has been cleared and uncheck all the delegates 0122 * { 0123 * _delegate.checked = false 0124 * } 0125 * } 0126 * 0127 * onToggled: (state) => 0128 * { 0129 * if(state) 0130 * { 0131 * _selectionBar.append(_delegate.id, ({'title': "Testing"})) 0132 * }else 0133 * { 0134 * _selectionBar.removeAtUri(_delegate.id) 0135 * } 0136 * } // when the item is toggled, we mark it as checked and add it to the selection bar, otherwise we unchecked it and remove it from selection. 0137 * } 0138 * } 0139 * 0140 * footer: Maui.SelectionBar 0141 * { 0142 * id: _selectionBar 0143 * 0144 * anchors.horizontalCenter: parent.horizontalCenter 0145 * width: Math.min(parent.width-(Maui.Style.space.medium*2), implicitWidth) 0146 * maxListHeight: root.height - (Maui.Style.contentMargins*2) 0147 * 0148 * Action 0149 * { 0150 * icon.name: "love" 0151 * onTriggered: console.log(_selectionBar.getSelectedUrisString()) 0152 * } 0153 * 0154 * Action 0155 * { 0156 * icon.name: "folder" 0157 * onTriggered: console.log(_selectionBar.contains("0")) 0158 * } 0159 * 0160 * Action 0161 * { 0162 * icon.name: "list-add" 0163 * } 0164 * 0165 * onExitClicked: clear() 0166 * } 0167 * } 0168 * @endcode 0169 * 0170 * <a href="https://invent.kde.org/maui/mauikit/-/blob/qt6-2/examples/SelectionBar.qml">You can find a more complete example at this link.</a> 0171 * 0172 */ 0173 Item 0174 { 0175 id: control 0176 0177 implicitHeight: _layout.implicitHeight + Maui.Style.space.big 0178 implicitWidth: _layout.implicitWidth 0179 0180 /** 0181 * @brief Whether the bar is currently hidden. This is not the same as `visible`. 0182 * It is hidden when there is not items in the selection. 0183 */ 0184 readonly property bool hidden : count === 0 0185 0186 onHiddenChanged: 0187 { 0188 if(hidden && !singleSelection) 0189 { 0190 control.close() 0191 }else 0192 { 0193 control.open() 0194 } 0195 } 0196 0197 visible: false 0198 focus: true 0199 Keys.enabled: true 0200 0201 /** 0202 * @brief Default list of QQC2 Action; the actions are represented as buttons. 0203 * All of the actions listed in here will be visible, to hide some use the `hiddenActions` property. 0204 * @see hiddenActions 0205 */ 0206 default property list<Action> actions 0207 0208 /** 0209 * @brief List of action that won't be shown inside of the bar, but instead will always be hidden and listed in a dedicated overflow menu button. 0210 */ 0211 property list<Action> hiddenActions 0212 0213 /** 0214 * @brief The preferred display mode for the visible action buttons. 0215 * By default this is set to `ToolButton.TextBesideIcon`. 0216 */ 0217 property int display : ToolButton.TextBesideIcon 0218 0219 /** 0220 * @brief The list of items can be displayed in a popup. This property defines the maximum height the popup list. 0221 * By default this is set to `400`. 0222 */ 0223 property int maxListHeight : 400 0224 0225 /** 0226 * @brief By default the selection bar was designed to be floating and thus has rounded border corners. 0227 * This property allows to change the border radius. 0228 * By default this is set to `Style.radiusV`. 0229 */ 0230 property int radius: Maui.Style.radiusV 0231 0232 /** 0233 * @brief Whether the control only accepts a single item. 0234 * If single selection is set to true then only a single item can be appended, if another item is added then it replaces the previous one. 0235 * By default this is set to `false`. 0236 **/ 0237 property bool singleSelection: false 0238 0239 /** 0240 * @brief The array list of the URIs associated to the selected items. 0241 * @see items 0242 * @property var SelectionBar::uris 0243 */ 0244 readonly property alias uris: _private._uris 0245 0246 /** 0247 * @brief The array list of the items selected. 0248 * @property var SelectionBar::items 0249 */ 0250 readonly property alias items: _private._items 0251 0252 /** 0253 * @brief Total amount of items selected. 0254 * @property int SelectionBar::count 0255 */ 0256 readonly property alias count : _urisModel.count 0257 0258 /** 0259 * @brief Delegate to be used in popup list. 0260 * By default this is set to use a MauiKit ListBrowserDelegate. 0261 * The model use to feed the popup list is a QQC2 ListModel, populated by the `item` passed as the argument to the `append` method. 0262 * @see append 0263 */ 0264 property Component listDelegate: Maui.ListBrowserDelegate 0265 { 0266 id: delegate 0267 0268 width: ListView.view.width 0269 0270 Maui.Theme.backgroundColor: "transparent" 0271 Maui.Theme.textColor: control.Maui.Theme.textColor 0272 0273 iconVisible: false 0274 label1.text: model.uri 0275 0276 checkable: true 0277 checked: true 0278 onToggled: control.removeAtIndex(index) 0279 0280 onClicked: control.itemClicked(index) 0281 onPressAndHold: control.itemPressAndHold(index) 0282 } 0283 0284 /** 0285 * @brief Emitted when the selection is cleared either by the constrain of the single selection or by manually calling the clear method. 0286 */ 0287 signal cleared() 0288 0289 /** 0290 * @brief Emitted when close button is pressed or the Escape keyboard shortcut invoked. 0291 */ 0292 signal exitClicked() 0293 0294 /** 0295 * @brief Emitted when an item in the popup list view is clicked. 0296 * @paran index the index number of the item clicked. Use the `itemAt` function to get to the item. 0297 * @see itemAt 0298 */ 0299 signal itemClicked(int index) 0300 0301 /** 0302 * @brief Emitted when an item in the popup list view is pressed for a long time. 0303 * @paran index the index number of the item clicked. Use the `itemAt` function to get to the item. 0304 * @see itemAt 0305 */ 0306 signal itemPressAndHold(int index) 0307 0308 /** 0309 * @brief Emitted when an item is newly added to the selection. 0310 * @param item the item map passed to the `append` function. 0311 */ 0312 signal itemAdded(var item) 0313 0314 /** 0315 * @brief Emitted when an item has been removed from the selection. 0316 * @param item the item map passed to the `append` function. 0317 */ 0318 signal itemRemoved(var item) 0319 0320 /** 0321 * @brief Emitted when an item is newly added to the selection. This signal only sends the URI of the item. 0322 * @param uri the URI identifier 0323 */ 0324 signal uriAdded(string uri) 0325 0326 /** 0327 *@brief Emitted when an item has been removed from the selection. This signal only sends the URI of the item. 0328 * @param uri the URI identifier 0329 */ 0330 signal uriRemoved(string uri) 0331 0332 /** 0333 * @brief Emitted when an empty area of the selection bar has been clicked. 0334 * @param mouse the object with information of the event 0335 */ 0336 signal clicked(var mouse) 0337 0338 /** 0339 * @brief Emitted when an empty area of the selection bar has been right clicked. 0340 * @param mouse the object with information of the event 0341 */ 0342 signal rightClicked(var mouse) 0343 0344 /** 0345 * @brief Emitted when a group of URLs has been dropped onto the selection bar area. 0346 * @param uris the list of urls 0347 */ 0348 signal urisDropped(var uris) 0349 0350 QtObject 0351 { 0352 id: _private 0353 property var _uris : [] 0354 property var _items : [] 0355 } 0356 0357 ListModel 0358 { 0359 id: _urisModel 0360 } 0361 0362 Loader 0363 { 0364 id: _loader 0365 active: control.visible 0366 } 0367 0368 Component 0369 { 0370 id: _listContainerComponent 0371 0372 Popup 0373 { 0374 parent: control 0375 closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent 0376 modal: true 0377 height: Math.min(Math.min(400, control.maxListHeight), selectionList.contentHeight) + Maui.Style.space.big 0378 width: Math.min(600, control.Window.window.width*widthHint) 0379 0380 Maui.ListBrowser 0381 { 0382 id: selectionList 0383 0384 anchors.fill: parent 0385 model: _urisModel 0386 0387 delegate: control.listDelegate 0388 } 0389 } 0390 } 0391 0392 ParallelAnimation 0393 { 0394 id: _openAnimation 0395 NumberAnimation 0396 { 0397 target: _layout 0398 property: "y" 0399 from: _layout.height 0400 to: Maui.Style.space.big/2 0401 duration: Maui.Style.units.longDuration*1 0402 easing.type: Easing.OutBack 0403 0404 } 0405 0406 NumberAnimation 0407 { 0408 target: _layout 0409 property: "scale" 0410 from: 0.5 0411 to: 1 0412 duration: Maui.Style.units.longDuration*1 0413 easing.type: Easing.OutQuad 0414 } 0415 } 0416 0417 ParallelAnimation 0418 { 0419 id: _closeAnimation 0420 NumberAnimation 0421 { 0422 target: _layout 0423 property: "y" 0424 from: Maui.Style.space.big/2 0425 to: _layout.height 0426 duration: Maui.Style.units.longDuration*1 0427 easing.type: Easing.InBack 0428 0429 } 0430 0431 NumberAnimation 0432 { 0433 target: _layout 0434 property: "scale" 0435 from: 1 0436 to: 0.5 0437 duration: Maui.Style.units.longDuration*1 0438 easing.type: Easing.InQuad 0439 0440 } 0441 0442 onFinished: control.visible = false 0443 } 0444 0445 Maui.ToolBar 0446 { 0447 id: _layout 0448 width: control.width 0449 padding: Maui.Style.space.medium 0450 forceCenterMiddleContent: false 0451 position: ToolBar.Footer 0452 0453 leftContent: Maui.Chip 0454 { 0455 id: _counter 0456 visible: !control.singleSelection 0457 text: control.count 0458 font.pointSize: Maui.Style.fontSizes.big 0459 Maui.Theme.colorSet: control.Maui.Theme.colorSet 0460 Maui.Theme.backgroundColor: _loader.item && _loader.item.visible ? 0461 Maui.Theme.highlightColor : Qt.tint(control.Maui.Theme.textColor, Qt.rgba(control.Maui.Theme.backgroundColor.r, control.Maui.Theme.backgroundColor.g, control.Maui.Theme.backgroundColor.b, 0.9)) 0462 0463 0464 0465 Maui.Rectangle 0466 { 0467 opacity: 0.3 0468 anchors.fill: parent 0469 anchors.margins: 4 0470 visible: _counter.hovered 0471 color: "transparent" 0472 borderColor: "white" 0473 solidBorder: false 0474 } 0475 0476 MouseArea 0477 { 0478 id: _mouseArea 0479 anchors.fill: parent 0480 propagateComposedEvents: true 0481 property int startX 0482 property int startY 0483 Drag.active: drag.active 0484 Drag.hotSpot.x: 0 0485 Drag.hotSpot.y: 0 0486 Drag.dragType: Drag.Automatic 0487 Drag.supportedActions: Qt.CopyAction 0488 Drag.keys: ["text/plain","text/uri-list"] 0489 0490 onPressed: (mouse) => 0491 { 0492 if( mouse.source !== Qt.MouseEventSynthesizedByQt) 0493 { 0494 drag.target = _counter 0495 _counter.grabToImage(function(result) 0496 { 0497 _mouseArea.Drag.imageSource = result.url 0498 }) 0499 0500 _mouseArea.Drag.mimeData = { "text/uri-list": control.uris.join("\n")} 0501 0502 startX = _counter.x 0503 startY = _counter.y 0504 0505 }else mouse.accepted = false 0506 } 0507 0508 onReleased : 0509 { 0510 _counter.x = startX 0511 _counter.y = startY 0512 } 0513 0514 0515 onClicked: 0516 { 0517 if(!_loader.item) 0518 { 0519 _loader.sourceComponent = _listContainerComponent 0520 } 0521 _loader.item.open() 0522 console.log("Opening list") 0523 } 0524 0525 } 0526 } 0527 0528 Repeater 0529 { 0530 model: control.actions 0531 0532 ToolButton 0533 { 0534 action: modelData 0535 display: control.display 0536 ToolTip.delay: 1000 0537 ToolTip.timeout: 5000 0538 ToolTip.visible: hovered || pressed && action.text 0539 ToolTip.text: action.text 0540 } 0541 } 0542 0543 Maui.ToolButtonMenu 0544 { 0545 icon.name: "overflow-menu" 0546 visible: control.hiddenActions.length > 0 0547 Repeater 0548 { 0549 model: control.hiddenActions 0550 delegate: MenuItem{ action: modelData} 0551 } 0552 } 0553 0554 rightContent: Maui.CloseButton 0555 { 0556 onClicked: 0557 { 0558 control.exitClicked() 0559 } 0560 } 0561 0562 background: Rectangle 0563 { 0564 id: bg 0565 color: Maui.Theme.backgroundColor 0566 radius: control.radius 0567 0568 Behavior on color 0569 { 0570 Maui.ColorTransition {} 0571 } 0572 0573 MouseArea 0574 { 0575 anchors.fill: parent 0576 acceptedButtons: Qt.RightButton | Qt.LeftButton 0577 propagateComposedEvents: false 0578 preventStealing: true 0579 0580 onClicked: (mouse) => 0581 { 0582 if(!Maui.Handy.isMobile && mouse.button === Qt.RightButton) 0583 control.rightClicked(mouse) 0584 else 0585 control.clicked(mouse) 0586 } 0587 0588 onPressAndHold : (mouse) => 0589 { 0590 if(Maui.Handy.isMobile) 0591 control.rightClicked(mouse) 0592 } 0593 } 0594 0595 Maui.Rectangle 0596 { 0597 opacity: 0.2 0598 anchors.fill: parent 0599 anchors.margins: 4 0600 visible: _dropArea.containsDrag 0601 color: "transparent" 0602 borderColor: Maui.Theme.textColor 0603 solidBorder: false 0604 } 0605 0606 DropArea 0607 { 0608 id: _dropArea 0609 anchors.fill: parent 0610 onDropped: 0611 { 0612 control.urisDropped(drop.urls) 0613 } 0614 } 0615 0616 layer.enabled: true 0617 layer.effect: DropShadow 0618 { 0619 cached: true 0620 horizontalOffset: 0 0621 verticalOffset: 0 0622 radius: 8.0 0623 samples: 16 0624 color: "#80000000" 0625 smooth: true 0626 } 0627 } 0628 } 0629 0630 Keys.onEscapePressed: 0631 { 0632 control.exitClicked(); 0633 } 0634 0635 Keys.onBackPressed: 0636 { 0637 control.exitClicked(); 0638 event.accepted = true 0639 } 0640 0641 /** 0642 * @brief Removes all the items from the selection. 0643 * Triggers the `cleared` signal. 0644 * @see cleared 0645 */ 0646 function clear() 0647 { 0648 _private._uris = [] 0649 _private._items = [] 0650 _urisModel.clear() 0651 control.cleared() 0652 } 0653 0654 /** 0655 * @brief Returns an item at the given index 0656 * @param index the index number of the item. 0657 * @note Note that this is the index of the internal list on how items were added, and not the original index of the source list used to make the selection. 0658 * @return the requested item/map if it is found, otherwise null. 0659 */ 0660 function itemAt(index) 0661 { 0662 if(index < 0 || index > control.count) 0663 { 0664 return 0665 } 0666 return _urisModel.get(index) 0667 } 0668 0669 /** 0670 * @brief Remove a single item at the given index 0671 * @param index index of the item in the selection list 0672 */ 0673 function removeAtIndex(index) 0674 { 0675 if(index < 0) 0676 { 0677 return 0678 } 0679 0680 const item = _urisModel.get(index) 0681 const uri = item.uri 0682 0683 if(contains(uri)) 0684 { 0685 _private._uris.splice(index, 1) 0686 _private._items.splice(index, 1) 0687 _urisModel.remove(index) 0688 control.itemRemoved(item) 0689 control.uriRemoved(uri) 0690 } 0691 } 0692 0693 /** 0694 * @brief Removes an item from the selection at the given URI 0695 * @param uri the URI used to append the item 0696 */ 0697 function removeAtUri(uri) 0698 { 0699 removeAtIndex(indexOf(uri)) 0700 } 0701 0702 /** 0703 * @brief Returns the index of an item in the selection given its URI 0704 * @param uri the URI used to append the item 0705 * @return the index number of the found item, otherwise `-1` 0706 * 0707 */ 0708 function indexOf(uri) 0709 { 0710 return _private._uris.indexOf(uri) 0711 } 0712 0713 /** 0714 * @brief Appends a new item to the selection associated to the given URI 0715 * @param uri the URI to be associated with the item 0716 * @param item a map to be used to represent the item in the model. For example a valid item map would be: `({'title': "A title", 'icon': "love"})` 0717 */ 0718 function append(uri, item) 0719 { 0720 if(control.singleSelection) 0721 { 0722 clear() 0723 } 0724 0725 if(!contains(uri) || control.singleSelection) 0726 { 0727 _private._items.push(item) 0728 _private._uris.push(uri) 0729 0730 item.uri = uri 0731 _urisModel.append(item) 0732 control.itemAdded(item) 0733 control.uriAdded(uri) 0734 } 0735 } 0736 0737 /** 0738 * @brief Returns a single string with all the URIs separated by a comma. 0739 */ 0740 function getSelectedUrisString() 0741 { 0742 return String(""+_private._uris.join(",")) 0743 } 0744 0745 /** 0746 * @brief Returns whether the selection contains an item associated to the given URI. 0747 * @param uri the URI used to append the item 0748 */ 0749 function contains(uri) 0750 { 0751 return _private._uris.includes(uri) 0752 } 0753 0754 /** 0755 * @brief Forces to open the selection bar, if hidden. 0756 */ 0757 function open() 0758 { 0759 if(control.visible) 0760 { 0761 return; 0762 } 0763 0764 control.visible = true 0765 _openAnimation.start() 0766 } 0767 0768 /** 0769 * @brief Forces to close the selection bar, if visible. 0770 */ 0771 function close() 0772 { 0773 if(!control.visible) 0774 { 0775 return 0776 } 0777 _closeAnimation.start() 0778 } 0779 }