/*SPDX-FileCopyrightText: 2013-2014 Eike Hein <hein@kde.org>SPDX-License-Identifier: GPL-2.0-or-later*/pragma ComponentBehavior: Boundimport QtQuickimport QtQuick.Layoutsimport org.kde.kirigami as Kirigamiimport org.kde.plasma.components as PlasmaComponents3import org.kde.plasma.extras as PlasmaExtrasimport org.kde.plasma.private.kicker as KickerPlasmaComponents3.ScrollView {id: itemListsignal exitedsignal keyNavigationAtListEndsignal navigateLeftRequestedsignal navigateRightRequestedsignal interactionConcludedproperty Item mainSearchField: nullproperty Kicker.SubMenu dialog: nullproperty Kicker.SubMenu childDialog: nullproperty bool iconsEnabled: falseproperty bool dynamicResize : trueproperty bool showDescriptionInTooltip: falseproperty int innerLeftMargin: 0property int innerRightMargin: PlasmaComponents3.ScrollBar.vertical.visible ? Kirigami.Units.smallSpacing : 0property alias currentIndex: listView.currentIndexproperty alias currentItem: listView.currentItemproperty alias keyNavigationWraps: listView.keyNavigationWrapsproperty alias model: listView.modelproperty alias count: listView.countproperty alias resetOnExitDelay: resetIndexTimer.intervalimplicitWidth: listView.implicitWidth + leftPadding + rightPaddingimplicitHeight: listView.contentHeight + topPadding + bottomPaddingLayout.minimumWidth: Kirigami.Units.gridUnit * 14Layout.maximumWidth: Math.round(Layout.minimumWidth * 1.5)Layout.maximumHeight: contentHeightPlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOfffunction resetDelegateSizing() { // only needed when submenus are reused, called from ItemListDialoglistView.maxDelegateImplicitWidth = 0}function subMenuForCurrentItem() {if (!kicker.expanded || !itemList.model || itemList.currentIndex === -1 || ActionMenu.opened) {return;}if (itemList && !(itemList.currentItem as ItemListDelegate).hasChildren) {clearChildDialog();} else if (!itemList.childDialog) {itemList.childDialog = itemListDialogComponent.createObject(itemList, {mainSearchField: mainSearchField,visualParent: listView.currentItem,model: model.modelForRow(listView.currentIndex),visible: Qt.binding(() => itemList.Window.window.visible),dialogMirrored: itemList.LayoutMirroring.enabled});itemList.childDialog.index = listView.currentIndex;(itemList.childDialog as ItemListDialog).interactionConcluded.connect(itemList.interactionConcluded)itemList.childDialog.mainItem.forceActiveFocus(Qt.TabFocusReason)} else {itemList.childDialog.model = model.modelForRow(itemList.currentIndex);itemList.childDialog.visualParent = listView.currentItem;itemList.childDialog.index = listView.currentIndex;}}function clearChildDialog() {if (childDialog) {childDialog.delayedDestroy()childDialog = null}}Keys.priority: Keys.AfterItemKeys.forwardTo: [itemList.mainSearchField]onHoveredChanged: {const parentItem = itemList.dialog?.visualParent as ItemListDelegateif (hovered && parentItem) {parentItem.ListView.view.currentIndex = parentItem.index}Qt.callLater( () =>{if (ActionMenu.opened) {return} else if (hovered || (listView.currentItem as ItemListDelegate).dragActive || (listView.currentItem as ItemListDelegate).containsMouse) {resetIndexTimer.stop();} else {resetIndexTimer.start();}})}ListView {id: listViewwidth: itemList.availableWidthimplicitHeight: contentHeightimplicitWidth: itemList.Layout.minimumWidthanchors {top: parent.topleft: parent.leftleftMargin: itemList.innerLeftMargin}property int maxDelegateImplicitWidth: 0 // used to set implicitWidthBinding on implicitWidth {value: listView.maxDelegateImplicitWidthwhen: listView.maxDelegateImplicitWidth > 0 && itemList.dynamicResize}currentIndex: -1focus: trueclip: height < contentHeight + topMargin + bottomMarginboundsBehavior: Flickable.StopAtBoundssnapMode: ListView.SnapToItemspacing: 0keyNavigationEnabled: falseBinding on cacheBuffer {when: itemList.dynamicResizevalue: 10000 // load more delegates, to get their width right}Accessible.name: itemList.Accessible.nameAccessible.role: Accessible.Listdelegate: ItemListDelegate {width: listView.width - itemList.innerRightMarginanchors.left: listView.contentItem.leftshowIcons: itemList.iconsEnabledshowDescriptionInTooltip: itemList.showDescriptionInTooltipdialogDefaultRight: !itemList.LayoutMirroring.enabledonInteractionConcluded: itemList.interactionConcluded()onOpenCategory: keyboardInitiated => {listView.currentIndex = indexlistView.openOrFocusSubmenu()if (keyboardInitiated) { itemList.childDialog.mainItem.currentIndex = 0; }}onContainsMouseChanged: {if (containsMouse && itemList.hoverEnabled && !isSeparator && !ActionMenu.opened) {listView.currentIndex = indexitemList.forceActiveFocus()dialogSpawnTimer.restart()} else if (listView.currentIndex === index) {dialogSpawnTimer.stop()}}onImplicitWidthChanged: {listView.maxDelegateImplicitWidth = Math.max(listView.maxDelegateImplicitWidth, implicitWidth)}}highlight: PlasmaExtras.Highlight {anchors.left: listView.contentItem.leftvisible: !(listView.currentItem as ItemListDelegate)?.isSeparatorpressed: !!((listView.currentItem as ItemListDelegate)?.iconAndLabelsShouldlookSelected)active: !!(listView.currentItem as ItemListDelegate)?.hovered}highlightMoveDuration: 0onCountChanged: {if (currentIndex == 0 && !itemList.mainSearchField.activeFocus) {currentItem?.forceActiveFocus();} else {currentIndex = -1;}}onCurrentIndexChanged: {if (currentIndex === itemList.childDialog?.index) {return;} else if (currentIndex === -1 || !(currentItem as ItemListDelegate)?.hasChildren || !kicker.expanded) {dialogSpawnTimer.stop();itemList.clearChildDialog();} else if (itemList.childDialog) {dialogSpawnTimer.restart();}}Connections {target: ActionMenufunction onClosed() {resetIndexTimer.restart()}}function openOrFocusSubmenu() : void {if (itemList.childDialog === null) {itemList.subMenuForCurrentItem();} else {itemList.childDialog.requestActivate()itemList.childDialog.mainItem.forceActiveFocus(Qt.TabFocusReason);}}function handleLeftRightArrow(event: KeyEvent) : void {let backArrowKey = (event.key === Qt.Key_Left && !itemList.LayoutMirroring.enabled) ||(event.key === Qt.Key_Right && itemList.LayoutMirroring.enabled)let forwardArrowKey = (event.key === Qt.Key_Right && !itemList.LayoutMirroring.enabled) ||(event.key === Qt.Key_Left && itemList.LayoutMirroring.enabled)if (backArrowKey) {if (itemList.dialog != null) {itemList.dialog.destroy();} else {itemList.navigateLeftRequested();}} else if (forwardArrowKey) {if (listView.currentItem !== null && (listView.currentItem as ItemListDelegate).hasChildren) {openOrFocusSubmenu()itemList.childDialog.mainItem.currentIndex = 0;} else {itemList.navigateRightRequested();}}}function handleUpDownArrow(event: KeyEvent) : void {let moveIndex = (event.key === Qt.Key_Up) ? listView.decrementCurrentIndex : listView.incrementCurrentIndexif (!listView.keyNavigationWraps && ((event.key === Qt.Key_Up && listView.currentIndex == 0) ||(event.key === Qt.Key_Down && listView.currentIndex == listView.count - 1))) {itemList.keyNavigationAtListEnd();} else {moveIndex();if (listView.currentItem !== null) {if ((listView.currentItem as ItemListDelegate).isSeparator) {moveIndex();}listView.currentItem.forceActiveFocus(Qt.TabFocusReason);}}}Keys.onLeftPressed: event => handleLeftRightArrow(event)Keys.onRightPressed: event => handleLeftRightArrow(event)Keys.onUpPressed: event => handleUpDownArrow(event)Keys.onDownPressed: event => handleUpDownArrow(event)Keys.onEscapePressed: itemList.interactionConcluded()Keys.onPressed: event => {if (event.key !== Qt.Key_Tab && event.text !== "") {itemList.mainSearchField?.forceActiveFocus(Qt.ShortcutFocusReason);}}Timer {id: dialogSpawnTimerinterval: 100repeat: falseonTriggered: itemList.subMenuForCurrentItem()}Timer {id: resetIndexTimerinterval: (itemList.dialog != null) ? 50 : 150repeat: falseonTriggered: {if (itemList.focus && !(itemList.childDialog?.mainItem as ItemListView)?.hovered) {itemList.currentIndex = -1;itemList.exited();}}}Connections {target: itemList.childDialog?.mainItem ?? nullfunction onHoveredChanged() {const childListView = itemList.childDialog?.mainItem as ItemListViewif (childListView.hovered) {resetIndexTimer.stop()}}}}}