diff --git a/applets/kicker/package/contents/ui/DashboardRepresentation.qml b/applets/kicker/package/contents/ui/DashboardRepresentation.qml index c53aff13d..c5e90ee9e 100644 --- a/applets/kicker/package/contents/ui/DashboardRepresentation.qml +++ b/applets/kicker/package/contents/ui/DashboardRepresentation.qml @@ -1,1083 +1,1083 @@ /*************************************************************************** * Copyright (C) 2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.4 import QtGraphicalEffects 1.0 import org.kde.plasma.core 2.1 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 import org.kde.kwindowsystem 1.0 import org.kde.plasma.private.shell 2.0 import org.kde.plasma.private.kicker 0.1 as Kicker import "code/tools.js" as Tools /* TODO * Reverse middleRow layout + keyboard nav + filter list text alignment in rtl locales. * Keep cursor column when arrow'ing down past non-full trailing rows into a lower grid. * Make DND transitions cleaner by performing an item swap instead of index reinsertion. */ Kicker.DashboardWindow { id: root property bool smallScreen: ((Math.floor(width / units.iconSizes.huge) <= 22) || (Math.floor(height / units.iconSizes.huge) <= 14)) property int iconSize: smallScreen ? units.iconSizes.large : units.iconSizes.huge property int cellSize: iconSize + theme.mSize(theme.defaultFont).height + (2 * units.smallSpacing) + (2 * Math.max(highlightItemSvg.margins.top + highlightItemSvg.margins.bottom, highlightItemSvg.margins.left + highlightItemSvg.margins.right)) property int columns: Math.floor(((smallScreen ? 85 : 80)/100) * Math.ceil(width / cellSize)) property bool searching: (searchField.text != "") property var widgetExplorer: null keyEventProxy: searchField backgroundColor: Qt.rgba(0, 0, 0, 0.737) onKeyEscapePressed: { if (searching) { searchField.clear(); } else { root.toggle(); } } onVisibleChanged: { tabBar.activeTab = 0; reset(); if (visible) { preloadAllAppsTimer.restart(); } } onSearchingChanged: { if (!searching) { reset(); } else { filterList.currentIndex = -1; if (tabBar.activeTab == 1) { widgetExplorer.widgetsModel.filterQuery = ""; widgetExplorer.widgetsModel.filterType = ""; } } } function reset() { searchField.clear(); globalFavoritesGrid.currentIndex = -1; systemFavoritesGrid.currentIndex = -1; filterList.currentIndex = 0; funnelModel.sourceModel = rootModel.modelForRow(0); mainGrid.model = (tabBar.activeTab == 0) ? funnelModel : root.widgetExplorer.widgetsModel; mainGrid.currentIndex = -1; filterListScrollArea.focus = true; filterList.model = (tabBar.activeTab == 0) ? rootModel : root.widgetExplorer.filterModel; } function updateWidgetExplorer() { if (tabBar.activeTab == 1 /* Widgets */ || tabBar.hoveredTab == 1) { if (!root.widgetExplorer) { root.widgetExplorer = widgetExplorerComponent.createObject(root, { containment: containmentInterface.screenContainment(plasmoid) }); } } else if (root.widgetExplorer) { root.widgetExplorer.destroy(); root.widgetExplorer = null; } } mainItem: MouseArea { id: rootItem anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true Connections { target: kicker onReset: { if (!root.searching) { filterList.applyFilter(); if (tabBar.activeTab == 0) { funnelModel.reset(); } } } onDragSourceChanged: { if (!dragSource) { // FIXME TODO HACK: Reset all views post-DND to work around // mouse grab bug despite QQuickWindow::mouseGrabberItem==0x0. // Needs a more involved hunt through Qt Quick sources later since // it's not happening with near-identical code in the menu repr. rootModel.refresh(); } else if (tabBar.activeTab == 1) { root.toggle(); containmentInterface.ensureMutable(containmentInterface.screenContainment(plasmoid)); kwindowsystem.showingDesktop = true; } } } KWindowSystem { id: kwindowsystem } Component { id: widgetExplorerComponent WidgetExplorer { showSpecialFilters: false } } Connections { target: plasmoid onUserConfiguringChanged: { if (plasmoid.userConfiguring) { root.hide() } } } PlasmaComponents.Menu { id: contextMenu PlasmaComponents.MenuItem { action: plasmoid.action("configure") } } PlasmaExtras.Heading { id: dummyHeading visible: false width: 0 level: 1 } TextMetrics { id: headingMetrics font: dummyHeading.font } Kicker.FunnelModel { id: funnelModel onSourceModelChanged: { if (mainColumn.visible) { mainGrid.currentIndex = -1; mainGrid.forceLayout(); } } } Timer { id: preloadAllAppsTimer property bool done: false interval: 1000 repeat: false onTriggered: { if (done || root.searching) { return; } for (var i = 0; i < rootModel.count; ++i) { var model = rootModel.modelForRow(i); if (model.description === "KICKER_ALL_MODEL") { allAppsGrid.model = model; done = true; break; } } } function defer() { if (running && !done) { restart(); } } } Kicker.ContainmentInterface { id: containmentInterface } DashboardTabBar { id: tabBar y: 0 anchors.horizontalCenter: parent.horizontalCenter visible: (plasmoid.immutability !== PlasmaCore.Types.SystemImmutable) onActiveTabChanged: { root.updateWidgetExplorer(); root.reset(); } onHoveredTabChanged: root.updateWidgetExplorer() Keys.onDownPressed: { mainColumn.tryActivate(0, 0); } } TextEdit { id: searchField width: 0 height: 0 visible: false persistentSelection: true onTextChanged: { if (tabBar.activeTab == 0) { runnerModel.query = searchField.text; } else { root.widgetExplorer.widgetsModel.searchTerm = searchField.text; } } function clear() { text = ""; } onSelectionStartChanged: Qt.callLater(searchHeading.updateSelection) onSelectionEndChanged: Qt.callLater(searchHeading.updateSelection) } TextEdit { id: searchHeading anchors { horizontalCenter: parent.horizontalCenter } y: (middleRow.anchors.topMargin / 2) - (root.smallScreen ? (height/10) : 0) font.pointSize: dummyHeading.font.pointSize * 1.5 wrapMode: Text.NoWrap opacity: 1.0 selectByMouse: false cursorVisible: false color: "white" text: root.searching ? i18n("Searching for '%1'", searchField.text) : i18n("Type to search...") function updateSelection() { if (!searchField.selectedText) { return; } var delta = text.lastIndexOf(searchField.text, text.length - 2); searchHeading.select(searchField.selectionStart + delta, searchField.selectionEnd + delta); } } PlasmaComponents.ToolButton { id: cancelSearchButton anchors { left: searchHeading.right leftMargin: units.largeSpacing verticalCenter: searchHeading.verticalCenter } width: units.iconSizes.large height: width visible: (searchField.text != "") iconName: "dialog-close" flat: false onClicked: searchField.clear(); - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Tab) { event.accepted = true; if (runnerModel.count) { mainColumn.tryActivate(0, 0); } else { systemFavoritesGrid.tryActivate(0, 0); } } else if (event.key === Qt.Key_Backtab) { event.accepted = true; if (tabBar.visible) { tabBar.focus = true; } else if (globalFavoritesGrid.enabled) { globalFavoritesGrid.tryActivate(0, 0); } else { systemFavoritesGrid.tryActivate(0, 0); } } } } Row { id: middleRow anchors { top: parent.top topMargin: units.gridUnit * (smallScreen ? 8 : 10) bottom: parent.bottom bottomMargin: (units.gridUnit * 2) horizontalCenter: parent.horizontalCenter } width: (root.columns * root.cellSize) + (2 * spacing) spacing: units.gridUnit * 2 Item { id: favoritesColumn anchors { top: parent.top bottom: parent.bottom } width: (columns * root.cellSize) + units.gridUnit property int columns: 3 PlasmaExtras.Heading { id: favoritesColumnLabel enabled: (tabBar.activeTab == 0) anchors { top: parent.top } x: units.smallSpacing width: parent.width - x elide: Text.ElideRight wrapMode: Text.NoWrap color: "white" level: 1 text: i18n("Favorites") opacity: (enabled ? 1.0 : 0.3) Behavior on opacity { SmoothedAnimation { duration: units.longDuration; velocity: 0.01 } } } PlasmaCore.SvgItem { id: favoritesColumnLabelUnderline enabled: (tabBar.activeTab == 0) anchors { top: favoritesColumnLabel.bottom } width: parent.width - units.gridUnit height: lineSvg.horLineHeight svg: lineSvg elementId: "horizontal-line" opacity: (enabled ? 1.0 : 0.3) Behavior on opacity { SmoothedAnimation { duration: units.longDuration; velocity: 0.01 } } } ItemGridView { id: globalFavoritesGrid enabled: (tabBar.activeTab == 0) anchors { top: favoritesColumnLabelUnderline.bottom topMargin: units.largeSpacing } property int rows: (Math.floor((parent.height - favoritesColumnLabel.height - favoritesColumnLabelUnderline.height - units.largeSpacing) / root.cellSize) - systemFavoritesGrid.rows) width: parent.width height: rows * root.cellSize cellWidth: root.cellSize cellHeight: root.cellSize iconSize: root.iconSize model: globalFavorites dropEnabled: true usesPlasmaTheme: false opacity: (enabled ? 1.0 : 0.3) Behavior on opacity { SmoothedAnimation { duration: units.longDuration; velocity: 0.01 } } onCurrentIndexChanged: { preloadAllAppsTimer.defer(); } onKeyNavRight: { mainColumn.tryActivate(currentRow(), 0); } onKeyNavDown: { systemFavoritesGrid.tryActivate(0, currentCol()); } - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Tab) { event.accepted = true; if (tabBar.visible) { tabBar.focus = true; } else if (root.searching) { cancelSearchButton.focus = true; } else { mainColumn.tryActivate(0, 0); } } else if (event.key === Qt.Key_Backtab) { event.accepted = true; systemFavoritesGrid.tryActivate(0, 0); } } Binding { target: globalFavorites property: "iconSize" value: root.iconSize } } ItemGridView { id: systemFavoritesGrid anchors { top: globalFavoritesGrid.bottom } property int rows: Math.ceil(count / Math.floor(width / root.cellSize)) width: parent.width height: rows * root.cellSize cellWidth: root.cellSize cellHeight: root.cellSize iconSize: root.iconSize model: systemFavorites dropEnabled: true usesPlasmaTheme: true onCurrentIndexChanged: { preloadAllAppsTimer.defer(); } onKeyNavRight: { mainColumn.tryActivate(globalFavoritesGrid.rows + currentRow(), 0); } onKeyNavUp: { globalFavoritesGrid.tryActivate(globalFavoritesGrid.rows - 1, currentCol()); } - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Tab) { event.accepted = true; if (globalFavoritesGrid.enabled) { globalFavoritesGrid.tryActivate(0, 0); } else if (tabBar.visible) { tabBar.focus = true; } else if (root.searching && !runnerModel.count) { cancelSearchButton.focus = true; } else { mainColumn.tryActivate(0, 0); } } else if (event.key === Qt.Key_Backtab) { event.accepted = true; if (filterList.enabled) { filterList.forceActiveFocus(); } else if (root.searching && !runnerModel.count) { cancelSearchButton.focus = true; } else { mainColumn.tryActivate(0, 0); } } } } } Item { id: mainColumn anchors.top: parent.top width: (columns * root.cellSize) + units.gridUnit height: Math.floor(parent.height / root.cellSize) * root.cellSize + mainGridContainer.headerHeight property int columns: root.columns - favoritesColumn.columns - filterListColumn.columns property Item visibleGrid: mainGrid function tryActivate(row, col) { if (visibleGrid) { visibleGrid.tryActivate(row, col); } } Item { id: mainGridContainer anchors.fill: parent z: (opacity == 1.0) ? 1 : 0 enabled: (opacity == 1.0) ? 1 : 0 property int headerHeight: mainColumnLabel.height + mainColumnLabelUnderline.height + units.largeSpacing opacity: { if (tabBar.activeTab == 0 && root.searching) { return 0.0; } if (filterList.allApps) { return 0.0; } return 1.0; } onOpacityChanged: { if (opacity == 1.0) { mainColumn.visibleGrid = mainGrid; } } PlasmaExtras.Heading { id: mainColumnLabel anchors { top: parent.top } x: units.smallSpacing width: parent.width - x elide: Text.ElideRight wrapMode: Text.NoWrap opacity: 1.0 color: "white" level: 1 text: (tabBar.activeTab == 0) ? funnelModel.description : i18n("Widgets") } PlasmaCore.SvgItem { id: mainColumnLabelUnderline visible: mainGrid.count anchors { top: mainColumnLabel.bottom } width: parent.width - units.gridUnit height: lineSvg.horLineHeight svg: lineSvg elementId: "horizontal-line" } ItemGridView { id: mainGrid anchors { top: mainColumnLabelUnderline.bottom topMargin: units.largeSpacing } width: parent.width height: systemFavoritesGrid.y + systemFavoritesGrid.height - mainGridContainer.headerHeight cellWidth: (tabBar.activeTab == 0 ? root.cellSize : root.cellSize * 2) cellHeight: cellWidth iconSize: (tabBar.activeTab == 0 ? root.iconSize : cellWidth - (units.largeSpacing * 2)) model: funnelModel onCurrentIndexChanged: { preloadAllAppsTimer.defer(); } onKeyNavLeft: { if (tabBar.activeTab == 0) { var row = currentRow(); var target = row + 1 > globalFavoritesGrid.rows ? systemFavoritesGrid : globalFavoritesGrid; var targetRow = row + 1 > globalFavoritesGrid.rows ? row - globalFavoritesGrid.rows : row; target.tryActivate(targetRow, favoritesColumn.columns - 1); } } onKeyNavRight: { filterListScrollArea.focus = true; } onKeyNavUp: { if (tabBar.visible) { tabBar.focus = true; } } onItemActivated: { if (tabBar.activeTab == 1) { containmentInterface.ensureMutable(containmentInterface.screenContainment(plasmoid)); root.widgetExplorer.addApplet(currentItem.m.pluginName); root.toggle(); kwindowsystem.showingDesktop = true; } } } } ItemMultiGridView { id: allAppsGrid anchors { top: parent.top } z: (opacity == 1.0) ? 1 : 0 width: parent.width height: systemFavoritesGrid.y + systemFavoritesGrid.height enabled: (opacity == 1.0) ? 1 : 0 opacity: filterList.allApps ? 1.0 : 0.0 onOpacityChanged: { if (opacity == 1.0) { allAppsGrid.flickableItem.contentY = 0; mainColumn.visibleGrid = allAppsGrid; } } onKeyNavLeft: { var row = 0; for (var i = 0; i < subGridIndex; i++) { row += subGridAt(i).lastRow() + 2; // Header counts as one. } row += subGridAt(subGridIndex).currentRow(); var target = row + 1 > globalFavoritesGrid.rows ? systemFavoritesGrid : globalFavoritesGrid; var targetRow = row + 1 > globalFavoritesGrid.rows ? row - globalFavoritesGrid.rows : row; target.tryActivate(targetRow, favoritesColumn.columns - 1); } onKeyNavRight: { filterListScrollArea.focus = true; } } ItemMultiGridView { id: runnerGrid anchors { top: parent.top } z: (opacity == 1.0) ? 1 : 0 width: parent.width height: Math.min(implicitHeight, systemFavoritesGrid.y + systemFavoritesGrid.height) enabled: (opacity == 1.0) ? 1 : 0 model: runnerModel grabFocus: true opacity: (tabBar.activeTab == 0 && root.searching) ? 1.0 : 0.0 onOpacityChanged: { if (opacity == 1.0) { mainColumn.visibleGrid = runnerGrid; } } onKeyNavLeft: { var row = 0; for (var i = 0; i < subGridIndex; i++) { row += subGridAt(i).lastRow() + 2; // Header counts as one. } row += subGridAt(subGridIndex).currentRow(); var target = row + 1 > globalFavoritesGrid.rows ? systemFavoritesGrid : globalFavoritesGrid; var targetRow = row + 1 > globalFavoritesGrid.rows ? row - globalFavoritesGrid.rows : row; target.tryActivate(targetRow, favoritesColumn.columns - 1); } } - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Tab) { event.accepted = true; if (filterList.enabled) { filterList.forceActiveFocus(); } else { systemFavoritesGrid.tryActivate(0, 0); } } else if (event.key === Qt.Key_Backtab) { event.accepted = true; if (root.searching) { cancelSearchButton.focus = true; } else if (tabBar.visible) { tabBar.focus = true; } else if (globalFavoritesGrid.enabled) { globalFavoritesGrid.tryActivate(0, 0); } else { systemFavoritesGrid.tryActivate(0, 0); } } } } Item { id: filterListColumn anchors { top: parent.top topMargin: mainColumnLabelUnderline.y + mainColumnLabelUnderline.height + units.largeSpacing bottom: parent.bottom } width: columns * root.cellSize property int columns: 3 PlasmaExtras.ScrollArea { id: filterListScrollArea x: root.visible ? 0 : units.gridUnit Behavior on x { SmoothedAnimation { duration: units.longDuration; velocity: 0.01 } } width: parent.width height: mainGrid.height enabled: !root.searching property alias currentIndex: filterList.currentIndex opacity: root.visible ? (root.searching ? 0.30 : 1.0) : 0.3 Behavior on opacity { SmoothedAnimation { duration: units.longDuration; velocity: 0.01 } } verticalScrollBarPolicy: (opacity == 1.0) ? Qt.ScrollBarAsNeeded : Qt.ScrollBarAlwaysOff onEnabledChanged: { if (!enabled) { filterList.currentIndex = -1; } } onCurrentIndexChanged: { focus = (currentIndex != -1); } ListView { id: filterList focus: true property bool allApps: false property int eligibleWidth: width property int hItemMargins: Math.max(highlightItemSvg.margins.left + highlightItemSvg.margins.right, listItemSvg.margins.left + listItemSvg.margins.right) model: rootModel boundsBehavior: Flickable.StopAtBounds snapMode: ListView.SnapToItem spacing: 0 keyNavigationWraps: true delegate: MouseArea { id: item signal actionTriggered(string actionId, variant actionArgument) signal aboutToShowActionMenu(variant actionMenu) property var m: model property int textWidth: label.contentWidth property int mouseCol property bool hasActionList: ((model.favoriteId !== null) || (("hasActionList" in model) && (model.hasActionList === true))) property Item menu: actionMenu width: parent.width height: Math.ceil((label.paintedHeight + Math.max(highlightItemSvg.margins.top + highlightItemSvg.margins.bottom, listItemSvg.margins.top + listItemSvg.margins.bottom)) / 2) * 2 Accessible.role: Accessible.MenuItem Accessible.name: model.display acceptedButtons: Qt.LeftButton | Qt.RightButton hoverEnabled: true onContainsMouseChanged: { if (!containsMouse) { updateCurrentItemTimer.stop(); } } - onPositionChanged: { // Lazy menu implementation. + onPositionChanged: mouse => { // Lazy menu implementation. mouseCol = mouse.x; if (justOpenedTimer.running || ListView.view.currentIndex === 0 || index === ListView.view.currentIndex) { updateCurrentItem(); } else if ((index == ListView.view.currentIndex - 1) && mouse.y < (height - 6) || (index == ListView.view.currentIndex + 1) && mouse.y > 5) { if (mouse.x > ListView.view.eligibleWidth - 5) { updateCurrentItem(); } } else if (mouse.x > ListView.view.eligibleWidth) { updateCurrentItem(); } updateCurrentItemTimer.restart(); } - onPressed: { + onPressed: mouse => { if (mouse.buttons & Qt.RightButton) { if (hasActionList) { openActionMenu(item, mouse.x, mouse.y); } } } - onClicked: { + onClicked: mouse => { if (mouse.button == Qt.LeftButton) { updateCurrentItem(); } } onAboutToShowActionMenu: { var actionList = hasActionList ? model.actionList : []; Tools.fillActionMenu(i18n, actionMenu, actionList, ListView.view.model.favoritesModel, model.favoriteId); } onActionTriggered: { if (Tools.triggerAction(ListView.view.model, model.index, actionId, actionArgument) === true) { plasmoid.expanded = false; } } function openActionMenu(visualParent, x, y) { aboutToShowActionMenu(actionMenu); actionMenu.visualParent = visualParent; actionMenu.open(x, y); } function updateCurrentItem() { ListView.view.currentIndex = index; ListView.view.eligibleWidth = Math.min(width, mouseCol); } ActionMenu { id: actionMenu onActionClicked: { actionTriggered(actionId, actionArgument); } } Timer { id: updateCurrentItemTimer interval: 50 repeat: false onTriggered: parent.updateCurrentItem() } PlasmaExtras.Heading { id: label anchors { fill: parent leftMargin: highlightItemSvg.margins.left rightMargin: highlightItemSvg.margins.right } elide: Text.ElideRight wrapMode: Text.NoWrap opacity: 1.0 color: "white" level: 1 text: model.display } } highlight: PlasmaComponents.Highlight { anchors { top: filterList.currentItem ? filterList.currentItem.top : undefined left: filterList.currentItem ? filterList.currentItem.left : undefined bottom: filterList.currentItem ? filterList.currentItem.bottom : undefined } opacity: filterListScrollArea.focus ? 1.0 : 0.7 width: (highlightItemSvg.margins.left + filterList.currentItem.textWidth + highlightItemSvg.margins.right + units.smallSpacing) visible: filterList.currentItem } highlightFollowsCurrentItem: false highlightMoveDuration: 0 highlightResizeDuration: 0 onCurrentIndexChanged: applyFilter() onCountChanged: { var width = 0; for (var i = 0; i < rootModel.count; ++i) { headingMetrics.text = rootModel.labelForRow(i); if (headingMetrics.width > width) { width = headingMetrics.width; } } filterListColumn.columns = Math.ceil(width / root.cellSize); filterListScrollArea.width = width + hItemMargins + (units.gridUnit * 2); } function applyFilter() { if (!root.searching && currentIndex >= 0) { if (tabBar.activeTab == 1) { root.widgetExplorer.widgetsModel.filterQuery = currentItem.m.filterData; root.widgetExplorer.widgetsModel.filterType = currentItem.m.filterType; allApps = false; funnelModel.sourceModel = model; return; } if (preloadAllAppsTimer.running) { preloadAllAppsTimer.stop(); } var model = rootModel.modelForRow(currentIndex); if (model.description === "KICKER_ALL_MODEL") { allAppsGrid.model = model; allApps = true; funnelModel.sourceModel = null; preloadAllAppsTimer.done = true; } else { funnelModel.sourceModel = model; allApps = false; } } else { funnelModel.sourceModel = null; allApps = false; } } - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Left) { event.accepted = true; var currentRow = Math.max(0, Math.ceil(currentItem.y / mainGrid.cellHeight) - 1); mainColumn.tryActivate(currentRow, mainColumn.columns - 1); } else if (event.key === Qt.Key_Tab) { event.accepted = true; systemFavoritesGrid.tryActivate(0, 0); } else if (event.key === Qt.Key_Backtab) { event.accepted = true; mainColumn.tryActivate(0, 0); } } } } } } - onPressed: { + onPressed: mouse => { if (mouse.button == Qt.RightButton) { contextMenu.open(mouse.x, mouse.y); } } - onClicked: { + onClicked: mouse => { if (mouse.button == Qt.LeftButton) { root.toggle(); } } } } diff --git a/applets/kicker/package/contents/ui/DashboardTabBar.qml b/applets/kicker/package/contents/ui/DashboardTabBar.qml index 6d7a9b99f..28e61eace 100644 --- a/applets/kicker/package/contents/ui/DashboardTabBar.qml +++ b/applets/kicker/package/contents/ui/DashboardTabBar.qml @@ -1,93 +1,93 @@ /*************************************************************************** * Copyright (C) 2016 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.4 Row { id: tabBar property int activeTab: 0 property int hoveredTab: -1 Accessible.role: Accessible.PageTabList signal containsMouseChanged(int index, bool containsMouse) - onContainsMouseChanged: { + onContainsMouseChanged: (index, containsMouse) => { if (containsMouse) { hoveredTab = index; } else { hoveredTab = -1; } } DashboardTabButton { id: appsdocs index: 0 text: i18n("Apps & Docs") active: (tabBar.activeTab == 0) focus: parent.focus & active } DashboardTabButton { id: widgets index: 1 text: i18n("Widgets") active: (tabBar.activeTab == 1) focus: parent.focus & active } - Keys.onLeftPressed: { + Keys.onLeftPressed: event => { if (activeTab == 1) { activeTab = 0; } } - Keys.onRightPressed: { + Keys.onRightPressed: event => { if (activeTab == 0) { activeTab = 1; } } - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Tab) { event.accepted = true; if (searching) { cancelSearchButton.focus = true; } else { mainColumn.tryActivate(0, 0); } } else if (event.key === Qt.Key_Backtab) { event.accepted = true; if (globalFavoritesGrid.enabled) { globalFavoritesGrid.tryActivate(0, 0); } else { systemFavoritesGrid.tryActivate(0, 0); } } } } diff --git a/applets/kicker/package/contents/ui/ItemGridDelegate.qml b/applets/kicker/package/contents/ui/ItemGridDelegate.qml index f94fb27e6..64c58a33a 100644 --- a/applets/kicker/package/contents/ui/ItemGridDelegate.qml +++ b/applets/kicker/package/contents/ui/ItemGridDelegate.qml @@ -1,132 +1,132 @@ /*************************************************************************** * Copyright (C) 2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import "code/tools.js" as Tools Item { id: item width: GridView.view.cellWidth height: width property bool showLabel: true property int itemIndex: model.index property string favoriteId: model.favoriteId !== undefined ? model.favoriteId : "" property url url: model.url !== undefined ? model.url : "" property variant icon: model.decoration !== undefined ? model.decoration : "" property var m: model property bool hasActionList: ((model.favoriteId !== null) || (("hasActionList" in model) && (model.hasActionList === true))) Accessible.role: Accessible.MenuItem Accessible.name: model.display function openActionMenu(x, y) { var actionList = hasActionList ? model.actionList : []; Tools.fillActionMenu(i18n, actionMenu, actionList, GridView.view.model.favoritesModel, model.favoriteId); actionMenu.visualParent = item; actionMenu.open(x, y); } function actionTriggered(actionId, actionArgument) { var close = (Tools.triggerAction(GridView.view.model, model.index, actionId, actionArgument) === true); if (close) { root.toggle(); } } PlasmaCore.IconItem { id: icon y: item.showLabel ? (2 * highlightItemSvg.margins.top) : undefined anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: item.showLabel ? undefined : parent.verticalCenter width: iconSize height: width colorGroup: PlasmaCore.Theme.ComplementaryColorGroup animated: false usesPlasmaTheme: item.GridView.view.usesPlasmaTheme source: model.decoration } PlasmaComponents.Label { id: label visible: item.showLabel anchors { top: icon.bottom topMargin: units.smallSpacing left: parent.left leftMargin: highlightItemSvg.margins.left right: parent.right rightMargin: highlightItemSvg.margins.right } horizontalAlignment: Text.AlignHCenter maximumLineCount: 2 elide: Text.ElideRight wrapMode: Text.Wrap color: "white" // FIXME TODO: Respect theming? text: ("name" in model ? model.name : model.display) } PlasmaCore.ToolTipArea { id: toolTip property string text: model.display anchors.fill: parent active: root.visible && label.truncated mainItem: toolTipDelegate onContainsMouseChanged: item.GridView.view.itemContainsMouseChanged(containsMouse) } - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Menu && hasActionList) { event.accepted = true; openActionMenu(item); } else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) { event.accepted = true; if ("trigger" in GridView.view.model) { GridView.view.model.trigger(index, "", null); root.toggle(); } itemGrid.itemActivated(index, "", null); } } } diff --git a/applets/kicker/package/contents/ui/ItemGridView.qml b/applets/kicker/package/contents/ui/ItemGridView.qml index 82dd1ee12..6b7eb790a 100644 --- a/applets/kicker/package/contents/ui/ItemGridView.qml +++ b/applets/kicker/package/contents/ui/ItemGridView.qml @@ -1,470 +1,470 @@ /*************************************************************************** * Copyright (C) 2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.4 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 import org.kde.draganddrop 2.0 FocusScope { id: itemGrid signal keyNavLeft signal keyNavRight signal keyNavUp signal keyNavDown signal itemActivated(int index, string actionId, string argument) property bool dragEnabled: true property bool dropEnabled: false property bool showLabels: true property alias usesPlasmaTheme: gridView.usesPlasmaTheme property alias currentIndex: gridView.currentIndex property alias currentItem: gridView.currentItem property alias contentItem: gridView.contentItem property alias count: gridView.count property alias model: gridView.model property alias cellWidth: gridView.cellWidth property alias cellHeight: gridView.cellHeight property alias iconSize: gridView.iconSize property alias horizontalScrollBarPolicy: scrollArea.horizontalScrollBarPolicy property alias verticalScrollBarPolicy: scrollArea.verticalScrollBarPolicy onDropEnabledChanged: { if (!dropEnabled && "dropPlaceHolderIndex" in model) { model.dropPlaceHolderIndex = -1; } } onFocusChanged: { if (!focus && !root.keyEventProxy.activeFocus) { currentIndex = -1; } } function currentRow() { if (currentIndex == -1) { return -1; } return Math.floor(currentIndex / Math.floor(width / itemGrid.cellWidth)); } function currentCol() { if (currentIndex == -1) { return -1; } return currentIndex - (currentRow() * Math.floor(width / itemGrid.cellWidth)); } function lastRow() { var columns = Math.floor(width / itemGrid.cellWidth); return Math.ceil(count / columns) - 1; } function tryActivate(row, col) { if (count) { var columns = Math.floor(width / itemGrid.cellWidth); var rows = Math.ceil(count / columns); row = Math.min(row, rows - 1); col = Math.min(col, columns - 1); currentIndex = Math.min(row ? ((Math.max(1, row) * columns) + col) : col, count - 1); focus = true; } } function forceLayout() { gridView.forceLayout(); } ActionMenu { id: actionMenu onActionClicked: { visualParent.actionTriggered(actionId, actionArgument); } } DropArea { id: dropArea anchors.fill: parent onDragMove: { if (!itemGrid.dropEnabled || gridView.animating || !kicker.dragSource) { return; } var x = Math.max(0, event.x - (width % itemGrid.cellWidth)); var cPos = mapToItem(gridView.contentItem, x, event.y); var item = gridView.itemAt(cPos.x, cPos.y); if (item) { if (kicker.dragSource.parent === gridView.contentItem) { if (item !== kicker.dragSource) { item.GridView.view.model.moveRow(dragSource.itemIndex, item.itemIndex); } } else if (kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model && !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) { var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1); itemGrid.model.dropPlaceholderIndex = item.itemIndex; if (!hasPlaceholder) { gridView.currentIndex = (item.itemIndex - 1); } } } else if (kicker.dragSource.parent !== gridView.contentItem && kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model && !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) { var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1); itemGrid.model.dropPlaceholderIndex = hasPlaceholder ? itemGrid.model.count - 1 : itemGrid.model.count; if (!hasPlaceholder) { gridView.currentIndex = (itemGrid.model.count - 1); } } else { itemGrid.model.dropPlaceholderIndex = -1; gridView.currentIndex = -1; } } onDragLeave: { if ("dropPlaceholderIndex" in itemGrid.model) { itemGrid.model.dropPlaceholderIndex = -1; gridView.currentIndex = -1; } } onDrop: { if (kicker.dragSource && kicker.dragSource.parent !== gridView.contentItem && kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model) { itemGrid.model.addFavorite(kicker.dragSource.favoriteId, itemGrid.model.dropPlaceholderIndex); gridView.currentIndex = -1; } } Timer { id: resetAnimationDurationTimer interval: 120 repeat: false onTriggered: { gridView.animationDuration = interval - 20; } } PlasmaExtras.ScrollArea { id: scrollArea anchors.fill: parent focus: true horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff GridView { id: gridView signal itemContainsMouseChanged(bool containsMouse) property bool usesPlasmaTheme: false property int iconSize: units.iconSizes.huge property bool animating: false property int animationDuration: itemGrid.dropEnabled ? resetAnimationDurationTimer.interval : 0 focus: true currentIndex: -1 move: Transition { enabled: itemGrid.dropEnabled SequentialAnimation { PropertyAction { target: gridView; property: "animating"; value: true } NumberAnimation { duration: gridView.animationDuration properties: "x, y" easing.type: Easing.OutQuad } PropertyAction { target: gridView; property: "animating"; value: false } } } moveDisplaced: Transition { enabled: itemGrid.dropEnabled SequentialAnimation { PropertyAction { target: gridView; property: "animating"; value: true } NumberAnimation { duration: gridView.animationDuration properties: "x, y" easing.type: Easing.OutQuad } PropertyAction { target: gridView; property: "animating"; value: false } } } keyNavigationWraps: false boundsBehavior: Flickable.StopAtBounds delegate: ItemGridDelegate { showLabel: itemGrid.showLabels } highlight: Item { property bool isDropPlaceHolder: "dropPlaceholderIndex" in itemGrid.model && itemGrid.currentIndex === itemGrid.model.dropPlaceholderIndex PlasmaComponents.Highlight { visible: gridView.currentItem && !isDropPlaceHolder anchors.fill: parent } PlasmaCore.FrameSvgItem { visible: gridView.currentItem && isDropPlaceHolder anchors.fill: parent imagePath: "widgets/viewitem" prefix: "selected" opacity: 0.5 PlasmaCore.IconItem { anchors { right: parent.right rightMargin: parent.margins.right bottom: parent.bottom bottomMargin: parent.margins.bottom } width: units.iconSizes.smallMedium height: width source: "list-add" active: false } } } highlightFollowsCurrentItem: true highlightMoveDuration: 0 onCurrentIndexChanged: { if (currentIndex != -1) { hoverArea.hoverEnabled = false focus = true; } } onCountChanged: { animationDuration = 0; resetAnimationDurationTimer.start(); } onModelChanged: { currentIndex = -1; } Keys.onLeftPressed: { if (itemGrid.currentCol() !== 0) { event.accepted = true; moveCurrentIndexLeft(); } else { itemGrid.keyNavLeft(); } } Keys.onRightPressed: { var columns = Math.floor(width / cellWidth); if (itemGrid.currentCol() !== columns - 1 && currentIndex != count -1) { event.accepted = true; moveCurrentIndexRight(); } else { itemGrid.keyNavRight(); } } Keys.onUpPressed: { if (itemGrid.currentRow() !== 0) { event.accepted = true; moveCurrentIndexUp(); positionViewAtIndex(currentIndex, GridView.Contain); } else { itemGrid.keyNavUp(); } } Keys.onDownPressed: { if (itemGrid.currentRow() < itemGrid.lastRow()) { // Fix moveCurrentIndexDown()'s lack of proper spatial nav down // into partial columns. event.accepted = true; var columns = Math.floor(width / cellWidth); var newIndex = currentIndex + columns; currentIndex = Math.min(newIndex, count - 1); positionViewAtIndex(currentIndex, GridView.Contain); } else { itemGrid.keyNavDown(); } } onItemContainsMouseChanged: { if (!containsMouse) { if (!actionMenu.opened) { gridView.currentIndex = -1; } hoverArea.pressX = -1; hoverArea.pressY = -1; hoverArea.lastX = -1; hoverArea.lastY = -1; hoverArea.pressedItem = null; hoverArea.hoverEnabled = true; } } } } MouseArea { id: hoverArea anchors.fill: parent property int pressX: -1 property int pressY: -1 property int lastX: -1 property int lastY: -1 property Item pressedItem: null acceptedButtons: Qt.LeftButton | Qt.RightButton hoverEnabled: true function updatePositionProperties(x, y) { // Prevent hover event synthesis in QQuickWindow interfering // with keyboard navigation by ignoring repeated events with // identical coordinates. As the work done here would be re- // dundant in any case, these are safe to ignore. if (lastX === x && lastY === y) { return; } lastX = x; lastY = y; var cPos = mapToItem(gridView.contentItem, x, y); var item = gridView.itemAt(cPos.x, cPos.y); if (!item) { gridView.currentIndex = -1; pressedItem = null; } else { gridView.currentIndex = item.itemIndex; itemGrid.focus = (itemGrid.currentIndex != -1) } return item; } - onPressed: { + onPressed: mouse => { mouse.accepted = true; updatePositionProperties(mouse.x, mouse.y); pressX = mouse.x; pressY = mouse.y; if (mouse.button == Qt.RightButton) { if (gridView.currentItem) { if (gridView.currentItem.hasActionList) { var mapped = mapToItem(gridView.currentItem, mouse.x, mouse.y); gridView.currentItem.openActionMenu(mapped.x, mapped.y); } } else { var mapped = mapToItem(rootItem, mouse.x, mouse.y); contextMenu.open(mapped.x, mapped.y); } } else { pressedItem = gridView.currentItem; } } - onReleased: { + onReleased: mouse => { mouse.accepted = true; updatePositionProperties(mouse.x, mouse.y); if (gridView.currentItem && gridView.currentItem == pressedItem) { if ("trigger" in gridView.model) { gridView.model.trigger(pressedItem.itemIndex, "", null); root.toggle(); } itemGrid.itemActivated(pressedItem.itemIndex, "", null); } else if (!dragHelper.dragging && !pressedItem && mouse.button == Qt.LeftButton) { root.toggle(); } pressX = -1; pressY = -1; pressedItem = null; } - onPositionChanged: { + onPositionChanged: mouse => { var item = pressedItem? pressedItem : updatePositionProperties(mouse.x, mouse.y); if (gridView.currentIndex != -1) { if (itemGrid.dragEnabled && pressX != -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) { if ("pluginName" in item.m) { dragHelper.startDrag(kicker, item.url, item.icon, "text/x-plasmoidservicename", item.m.pluginName); } else { dragHelper.startDrag(kicker, item.url, item.icon); } kicker.dragSource = item; pressX = -1; pressY = -1; } } } } } } diff --git a/applets/kicker/package/contents/ui/ItemListDelegate.qml b/applets/kicker/package/contents/ui/ItemListDelegate.qml index a00c3182b..fbdd9f0c7 100644 --- a/applets/kicker/package/contents/ui/ItemListDelegate.qml +++ b/applets/kicker/package/contents/ui/ItemListDelegate.qml @@ -1,284 +1,284 @@ /*************************************************************************** * Copyright (C) 2013-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import "code/tools.js" as Tools Item { id: item height: isSeparator ? separatorHeight : itemHeight width: ListView.view.width enabled: !isSeparator signal actionTriggered(string actionId, variant actionArgument) signal aboutToShowActionMenu(variant actionMenu) readonly property real fullTextWidth: Math.ceil(icon.width + label.implicitWidth + arrow.width + row.anchors.leftMargin + row.anchors.rightMargin + row.actualSpacing) property bool isSeparator: (model.isSeparator === true) property bool hasChildren: (model.hasChildren === true) property bool hasActionList: ((model.favoriteId !== null) || (("hasActionList" in model) && (model.hasActionList === true))) property QtObject childDialog: null property Item menu: actionMenu Accessible.role: isSeparator ? Accessible.Separator: Accessible.MenuItem Accessible.name: label.text onHasChildrenChanged: { if (!hasChildren && ListView.view.currentItem === item) { ListView.view.currentIndex = -1; } } onAboutToShowActionMenu: { var actionList = item.hasActionList ? model.actionList : []; Tools.fillActionMenu(i18n, actionMenu, actionList, ListView.view.model.favoritesModel, model.favoriteId); } onActionTriggered: { if (Tools.triggerAction(ListView.view.model, model.index, actionId, actionArgument) === true) { plasmoid.expanded = false; } } function openActionMenu(visualParent, x, y) { aboutToShowActionMenu(actionMenu); actionMenu.visualParent = visualParent; actionMenu.open(x, y); } ActionMenu { id: actionMenu onActionClicked: { item.actionTriggered(actionId, actionArgument); } } MouseArea { id: mouseArea anchors { left: parent.left right: parent.right verticalCenter: parent.verticalCenter } height: parent.height property int mouseCol property bool pressed: false property int pressX: -1 property int pressY: -1 hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton - onPressed: { + onPressed: mouse => { if (mouse.buttons & Qt.RightButton) { if (item.hasActionList) { item.openActionMenu(mouseArea, mouse.x, mouse.y); } } else { pressed = true; pressX = mouse.x; pressY = mouse.y; } } - onReleased: { + onReleased: mouse => { if (pressed && !item.hasChildren) { item.ListView.view.model.trigger(index, "", null); plasmoid.expanded = false; } pressed = false; pressX = -1; pressY = -1; } - onPositionChanged: { + onPositionChanged: mouse => { if (pressX != -1 && model.url && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) { dragHelper.startDrag(kicker, model.url, model.decoration); pressed = false; pressX = -1; pressY = -1; return; } // FIXME: Correct escape angle calc for right screen edge. if (justOpenedTimer.running || !item.hasChildren) { item.ListView.view.currentIndex = index; } else { mouseCol = mouse.x; if (index === item.ListView.view.currentIndex) { updateCurrentItem(); } else if ((index == item.ListView.view.currentIndex - 1) && mouse.y < (itemHeight - 6) || (index == item.ListView.view.currentIndex + 1) && mouse.y > 5) { if ((item.childDialog != null && item.childDialog.facingLeft) ? mouse.x > item.ListView.view.eligibleWidth - 5 : mouse.x < item.ListView.view.eligibleWidth + 5) { updateCurrentItem(); } } else if ((item.childDialog != null && item.childDialog.facingLeft) ? mouse.x > item.ListView.view.eligibleWidth : mouse.x < item.ListView.view.eligibleWidth) { updateCurrentItem(); } updateCurrentItemTimer.start(); } } onContainsMouseChanged: { if (!containsMouse) { pressed = false; pressX = -1; pressY = -1; updateCurrentItemTimer.stop(); } } function updateCurrentItem() { item.ListView.view.currentIndex = index; item.ListView.view.eligibleWidth = Math.min(width, mouseCol); } Timer { id: updateCurrentItemTimer interval: 50 repeat: false onTriggered: parent.updateCurrentItem() } } Row { id: row anchors.left: parent.left anchors.leftMargin: highlightItemSvg.margins.left anchors.right: parent.right anchors.rightMargin: highlightItemSvg.margins.right height: parent.height spacing: units.smallSpacing * 2 readonly property real actualSpacing: ((icon.visible ? 1 : 0) * spacing) + ((arrow.visible ? 1 : 0) * spacing) LayoutMirroring.enabled: (Qt.application.layoutDirection == Qt.RightToLeft) PlasmaCore.IconItem { id: icon anchors.verticalCenter: parent.verticalCenter width: visible ? units.iconSizes.small : 0 height: width visible: iconsEnabled animated: false usesPlasmaTheme: false source: model.decoration } PlasmaComponents.Label { id: label enabled: !isParent || (isParent && item.hasChildren) anchors.verticalCenter: parent.verticalCenter width: parent.width - icon.width - arrow.width - parent.actualSpacing verticalAlignment: Text.AlignVCenter textFormat: Text.PlainText wrapMode: Text.NoWrap elide: Text.ElideRight text: model.display } PlasmaCore.SvgItem { id: arrow anchors.verticalCenter: parent.verticalCenter width: visible ? units.iconSizes.small : 0 height: width visible: item.hasChildren opacity: (item.ListView.view.currentIndex === index) ? 1.0 : 0.4 svg: arrows elementId: (Qt.application.layoutDirection == Qt.RightToLeft) ? "left-arrow" : "right-arrow" } } Component { id: separatorComponent PlasmaCore.SvgItem { width: parent.width height: lineSvg.horLineHeight svg: lineSvg elementId: "horizontal-line" } } Loader { id: separatorLoader anchors.left: parent.left anchors.leftMargin: highlightItemSvg.margins.left anchors.right: parent.right anchors.rightMargin: highlightItemSvg.margins.right anchors.verticalCenter: parent.verticalCenter active: item.isSeparator asynchronous: false sourceComponent: separatorComponent } Keys.onPressed: { if (event.key === Qt.Key_Menu && item.hasActionList) { event.accepted = true; item.openActionMenu(mouseArea); } else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return) && !item.hasChildren) { if (!item.hasChildren) { event.accepted = true; item.ListView.view.model.trigger(index, "", null); plasmoid.expanded = false; } } } } diff --git a/applets/kicker/package/contents/ui/ItemListView.qml b/applets/kicker/package/contents/ui/ItemListView.qml index 92ef30a46..05ef8f979 100644 --- a/applets/kicker/package/contents/ui/ItemListView.qml +++ b/applets/kicker/package/contents/ui/ItemListView.qml @@ -1,266 +1,266 @@ /*************************************************************************** * Copyright (C) 2013-2014 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 FocusScope { id: itemList property real minimumWidth: units.gridUnit * 14 property real maximumWidth: minimumWidth * 2 width: minimumWidth height: listView.contentHeight signal exited signal keyNavigationAtListEnd signal appendSearchText(string text) property Item focusParent: null property QtObject dialog: null property QtObject childDialog: null property bool iconsEnabled: false property int itemHeight: Math.ceil((Math.max(theme.mSize(theme.defaultFont).height, units.iconSizes.small) + Math.max(highlightItemSvg.margins.top + highlightItemSvg.margins.bottom, listItemSvg.margins.top + listItemSvg.margins.bottom)) / 2) * 2 property int separatorHeight: lineSvg.horLineHeight + (2 * units.smallSpacing) property alias currentIndex: listView.currentIndex property alias currentItem: listView.currentItem property alias keyNavigationWraps: listView.keyNavigationWraps property alias showChildDialogs: listView.showChildDialogs property alias model: listView.model property alias count: listView.count property alias containsMouse: listener.containsMouse property alias resetOnExitDelay: resetIndexTimer.interval onFocusParentChanged: { appendSearchText.connect(focusParent.appendSearchText); } Timer { id: dialogSpawnTimer property bool focusOnSpawn: false interval: 70 repeat: false onTriggered: { if (!plasmoid.expanded || model === undefined || currentIndex == -1) { return; } if (itemList.childDialog != null) { itemList.childDialog.delayedDestroy(); } // Gets reenabled after the dialog spawn causes a focus-in on the dialog window. plasmoid.hideOnWindowDeactivate = false; itemList.childDialog = itemListDialogComponent.createObject(itemList); itemList.childDialog.focusParent = itemList; itemList.childDialog.visualParent = listView.currentItem; itemList.childDialog.model = model.modelForRow(listView.currentIndex); itemList.childDialog.visible = true; windowSystem.forceActive(itemList.childDialog.mainItem); itemList.childDialog.mainItem.focus = true; if (focusOnSpawn) { itemList.childDialog.mainItem.showChildDialogs = false; itemList.childDialog.mainItem.currentIndex = 0; itemList.childDialog.mainItem.showChildDialogs = true; } } } Timer { id: resetIndexTimer interval: (dialog != null) ? 50 : 150 repeat: false onTriggered: { if (focus && (!itemList.childDialog || !itemList.childDialog.mainItem.containsMouse)) { currentIndex = -1; itemList.exited(); } } } MouseEventListener { id: listener anchors.fill: parent hoverEnabled: true onContainsMouseChanged: { listView.eligibleWidth = listView.width; if (containsMouse) { resetIndexTimer.stop(); itemList.forceActiveFocus(); } else if ((!itemList.childDialog || !dialog) && (!currentItem || !currentItem.menu.opened)) { resetIndexTimer.start(); } } PlasmaExtras.ScrollArea { anchors.fill: parent focus: true ListView { id: listView property bool showChildDialogs: true property int eligibleWidth: width currentIndex: -1 boundsBehavior: Flickable.StopAtBounds snapMode: ListView.SnapToItem spacing: 0 keyNavigationWraps: (dialog != null) delegate: ItemListDelegate { onFullTextWidthChanged: { if (fullTextWidth > itemList.width) itemList.width = Math.min(fullTextWidth, itemList.maximumWidth); } } highlight: PlasmaComponents.Highlight { visible: listView.currentItem && !listView.currentItem.isSeparator } highlightMoveDuration: 0 onCountChanged: { currentIndex = -1; } onCurrentIndexChanged: { if (currentIndex != -1) { if (itemList.childDialog) { if (currentItem && currentItem.hasChildren) { itemList.childDialog.mainItem.width = itemList.minimumWidth; itemList.childDialog.model = model.modelForRow(currentIndex); itemList.childDialog.visualParent = listView.currentItem; } else { itemList.childDialog.delayedDestroy(); } return; } if (currentItem == null || !currentItem.hasChildren || !plasmoid.expanded) { dialogSpawnTimer.stop(); return; } if (showChildDialogs) { dialogSpawnTimer.focusOnSpawn = false; dialogSpawnTimer.restart(); } } else if (itemList.childDialog != null) { itemList.childDialog.delayedDestroy(); itemList.childDialog = null; } } onCurrentItemChanged: { if (currentItem) { currentItem.menu.closed.connect(resetIndexTimer.restart); } } - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Up) { event.accepted = true; if (!keyNavigationWraps && currentIndex == 0) { itemList.keyNavigationAtListEnd(); return; } showChildDialogs = false; decrementCurrentIndex(); if (currentItem.isSeparator) { decrementCurrentIndex(); } showChildDialogs = true; } else if (event.key === Qt.Key_Down) { event.accepted = true; if (!keyNavigationWraps && currentIndex == count - 1) { itemList.keyNavigationAtListEnd(); return; } showChildDialogs = false; incrementCurrentIndex(); if (currentItem.isSeparator) { incrementCurrentIndex(); } showChildDialogs = true; } else if ((event.key === Qt.Key_Right || event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && itemList.childDialog != null) { windowSystem.forceActive(itemList.childDialog.mainItem); itemList.childDialog.mainItem.focus = true; itemList.childDialog.mainItem.currentIndex = 0; } else if ((event.key === Qt.Key_Right || event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && itemList.childDialog == null && currentItem != null && currentItem.hasChildren) { dialogSpawnTimer.focusOnSpawn = true; dialogSpawnTimer.restart(); } else if (event.key === Qt.Key_Left && dialog != null) { dialog.destroy(); } else if (event.key === Qt.Key_Escape) { plasmoid.expanded = false; } else if (event.key === Qt.Key_Tab) { //do nothing, and skip appending text } else if (event.text !== "") { appendSearchText(event.text); } } } } } Component.onCompleted: { windowSystem.monitorWindowFocus(itemList); if (dialog == null) { appendSearchText.connect(root.appendSearchText); } } } diff --git a/applets/kicker/package/contents/ui/MenuRepresentation.qml b/applets/kicker/package/contents/ui/MenuRepresentation.qml index 62a606962..c679ca42f 100644 --- a/applets/kicker/package/contents/ui/MenuRepresentation.qml +++ b/applets/kicker/package/contents/ui/MenuRepresentation.qml @@ -1,458 +1,458 @@ /*************************************************************************** * Copyright (C) 2013-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.2 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents FocusScope { id: root focus: true Layout.minimumWidth: (sideBar.width + (sideBar.width ? mainRow.spacing : 0) + Math.max(searchField.defaultWidth, runnerColumns.width)) Layout.maximumWidth: Math.max(mainRow.width, Layout.minimumWidth); // mainRow.width is constrained by rootList.maximumWidth Layout.minimumHeight: Math.max(((rootModel.count - rootModel.separatorCount) * rootList.itemHeight) + (rootModel.separatorCount * rootList.separatorHeight) + searchField.height + (2 * units.smallSpacing), sideBar.margins.top + sideBar.margins.bottom + favoriteApps.contentHeight + favoriteSystemActions.contentHeight + sidebarSeparator.height + (4 * units.smallSpacing)) Layout.maximumHeight: Layout.minimumHeight signal appendSearchText(string text) function reset() { plasmoid.hideOnWindowDeactivate = true; rootList.currentIndex = -1; searchField.text = ""; searchField.focus = true; } Row { id: mainRow height: parent.height spacing: units.smallSpacing LayoutMirroring.enabled: ((plasmoid.location === PlasmaCore.Types.RightEdge) || (Qt.application.layoutDirection == Qt.RightToLeft && plasmoid.location !== PlasmaCore.Types.LeftEdge)) PlasmaCore.FrameSvgItem { id: sideBar visible: width > 0 width: (globalFavorites && systemFavorites && (globalFavorites.count + systemFavorites.count) ? units.iconSizes.medium + margins.left + margins.right : 0) height: parent.height imagePath: "widgets/frame" prefix: "plain" SideBarSection { id: favoriteApps anchors.top: parent.top anchors.topMargin: sideBar.margins.top height: (sideBar.height - sideBar.margins.top - sideBar.margins.bottom - favoriteSystemActions.height - sidebarSeparator.height - (4 * units.smallSpacing)) model: globalFavorites states: [ State { name: "top" when: (plasmoid.location === PlasmaCore.Types.TopEdge) AnchorChanges { target: favoriteApps anchors.top: undefined anchors.bottom: parent.bottom } PropertyChanges { target: favoriteApps anchors.topMargin: undefined anchors.bottomMargin: sideBar.margins.bottom } }] Binding { target: globalFavorites property: "iconSize" value: units.iconSizes.medium } } PlasmaCore.SvgItem { id: sidebarSeparator anchors.bottom: favoriteSystemActions.top anchors.bottomMargin: (2 * units.smallSpacing) anchors.horizontalCenter: parent.horizontalCenter width: units.iconSizes.medium height: lineSvg.horLineHeight visible: (favoriteApps.model && favoriteApps.model.count && favoriteSystemActions.model && favoriteSystemActions.model.count) svg: lineSvg elementId: "horizontal-line" states: [ State { name: "top" when: (plasmoid.location === PlasmaCore.Types.TopEdge) AnchorChanges { target: sidebarSeparator anchors.top: favoriteSystemActions.bottom anchors.bottom: undefined } PropertyChanges { target: sidebarSeparator anchors.topMargin: (2 * units.smallSpacing) anchors.bottomMargin: undefined } }] } SideBarSection { id: favoriteSystemActions anchors.bottom: parent.bottom anchors.bottomMargin: sideBar.margins.bottom model: systemFavorites usesPlasmaTheme: true states: [ State { name: "top" when: (plasmoid.location === PlasmaCore.Types.TopEdge) AnchorChanges { target: favoriteSystemActions anchors.top: parent.top anchors.bottom: undefined } PropertyChanges { target: favoriteSystemActions anchors.topMargin: sideBar.margins.top anchors.bottomMargin: undefined } }] } } ItemListView { id: rootList anchors.top: parent.top minimumWidth: root.Layout.minimumWidth - sideBar.width - mainRow.spacing height: ((rootModel.count - rootModel.separatorCount) * itemHeight) + (rootModel.separatorCount * separatorHeight) visible: (searchField.text == "") iconsEnabled: plasmoid.configuration.showIconsRootLevel model: rootModel onKeyNavigationAtListEnd: { searchField.focus = true; } states: [ State { name: "top" when: (plasmoid.location === PlasmaCore.Types.TopEdge) AnchorChanges { target: rootList anchors.top: undefined anchors.bottom: parent.bottom } }] Component.onCompleted: { rootList.exited.connect(root.reset); } } Row { id: runnerColumns height: parent.height signal focusChanged() visible: (searchField.text != "" && runnerModel.count > 0) Repeater { id: runnerColumnsRepeater model: runnerModel delegate: RunnerResultsList { id: runnerMatches onKeyNavigationAtListEnd: { searchField.focus = true; } onContainsMouseChanged: { if (containsMouse) { runnerMatches.focus = true; } } onFocusChanged: { if (focus) { runnerColumns.focusChanged(); } } function focusChanged() { if (!runnerMatches.focus && runnerMatches.currentIndex != -1) { runnerMatches.currentIndex = -1; } } - Keys.onPressed: { + Keys.onPressed: event => { var target = null; if (event.key === Qt.Key_Right) { var targets = new Array(); for (var i = index + 1; i < runnerModel.count; ++i) { targets[targets.length] = i; } for (var i = 0; i < index; ++i) { targets[targets.length] = i; } for (var i = 0; i < targets.length; ++i) { if (runnerModel.modelForRow(targets[i]).count) { target = runnerColumnsRepeater.itemAt(targets[i]); break; } } } else if (event.key === Qt.Key_Left) { var targets = new Array(); for (var i = index - 1; i >= 0; --i) { targets[targets.length] = i; } for (var i = runnerModel.count - 1; i > index; --i) { targets[targets.length] = i; } for (var i = 0; i < targets.length; ++i) { if (runnerModel.modelForRow(targets[i]).count) { target = runnerColumnsRepeater.itemAt(targets[i]); break; } } } if (target) { event.accepted = true; currentIndex = -1; target.currentIndex = 0; target.focus = true; } } Component.onCompleted: { runnerColumns.focusChanged.connect(focusChanged); } Component.onDestruction: { runnerColumns.focusChanged.disconnect(focusChanged); } } } } } PlasmaComponents.TextField { id: searchField anchors.bottom: mainRow.bottom anchors.left: parent.left anchors.leftMargin: sideBar.width + (sideBar.width ? mainRow.spacing : 0) + units.smallSpacing readonly property real defaultWidth: units.gridUnit * 14 width: (runnerColumnsRepeater.count != 0 ? runnerColumnsRepeater.itemAt(0).width : (rootList.visible ? rootList.width : defaultWidth)) - units.smallSpacing focus: true placeholderText: i18n("Search...") clearButtonShown: true onTextChanged: { runnerModel.query = text; } onFocusChanged: { if (focus) { // FIXME: Cleanup arbitration between rootList/runnerCols here and in Keys. if (rootList.visible) { rootList.currentIndex = -1; } if (runnerColumns.visible) { runnerColumnsRepeater.itemAt(0).currentIndex = -1; } } } states: [ State { name: "top" when: plasmoid.location === PlasmaCore.Types.TopEdge AnchorChanges { target: searchField anchors.top: mainRow.top anchors.bottom: undefined anchors.left: parent.left anchors.right: undefined } PropertyChanges { target: searchField anchors.leftMargin: sideBar.width + mainRow.spacing + units.smallSpacing anchors.rightMargin: undefined } }, State { name: "right" when: (plasmoid.location === PlasmaCore.Types.RightEdge && Qt.application.layoutDirection == Qt.LeftToRight) || (plasmoid.location === PlasmaCore.Types.LeftEdge && Qt.application.layoutDirection == Qt.RightToLeft) AnchorChanges { target: searchField anchors.top: undefined anchors.bottom: mainRow.bottom anchors.left: undefined anchors.right: parent.right } PropertyChanges { target: searchField anchors.leftMargin: undefined anchors.rightMargin: sideBar.width + mainRow.spacing + units.smallSpacing } }] - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Up) { if (rootList.visible) { rootList.showChildDialogs = false; rootList.currentIndex = rootList.model.count - 1; rootList.forceActiveFocus(); rootList.showChildDialogs = true; } if (runnerColumns.visible) { for (var i = 0; i < runnerModel.count; ++i) { if (runnerModel.modelForRow(i).count) { var targetList = runnerColumnsRepeater.itemAt(i); targetList.currentIndex = runnerModel.modelForRow(i).count - 1; targetList.forceActiveFocus(); break; } } } } else if (event.key === Qt.Key_Down) { if (rootList.visible) { rootList.showChildDialogs = false; rootList.currentIndex = Math.min(1, rootList.count); rootList.forceActiveFocus(); rootList.showChildDialogs = true; } if (runnerColumns.visible) { for (var i = 0; i < runnerModel.count; ++i) { if (runnerModel.modelForRow(i).count) { var targetList = runnerColumnsRepeater.itemAt(i); targetList.currentIndex = Math.min(1, targetList.count); targetList.forceActiveFocus(); break; } } } } else if (event.key === Qt.Key_Left && cursorPosition == 0) { for (var i = runnerModel.count; i >= 0; --i) { if (runnerModel.modelForRow(i).count) { var targetList = runnerColumnsRepeater.itemAt(i); targetList.currentIndex = 0; targetList.forceActiveFocus(); break; } } } else if (event.key === Qt.Key_Right && cursorPosition == length) { for (var i = 1; i < runnerModel.count; ++i) { if (runnerModel.modelForRow(i).count) { var targetList = runnerColumnsRepeater.itemAt(i); targetList.currentIndex = 0; targetList.forceActiveFocus(); break; } } } else if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { if (runnerColumns.visible && runnerModel.modelForRow(0).count) { runnerModel.modelForRow(0).trigger(0, "", null); plasmoid.expanded = false; } } } function appendText(newText) { focus = true; text = text + newText; } } - Keys.onPressed: { + Keys.onPressed: event => { if (event.key === Qt.Key_Escape) { plasmoid.expanded = false; } } Component.onCompleted: { appendSearchText.connect(searchField.appendText); kicker.reset.connect(reset); windowSystem.hidden.connect(reset); } } diff --git a/applets/kicker/package/contents/ui/SideBarItem.qml b/applets/kicker/package/contents/ui/SideBarItem.qml index 95539dda5..420d49c09 100644 --- a/applets/kicker/package/contents/ui/SideBarItem.qml +++ b/applets/kicker/package/contents/ui/SideBarItem.qml @@ -1,156 +1,156 @@ /*************************************************************************** * Copyright (C) 2013-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.kquickcontrolsaddons 2.0 import "code/tools.js" as Tools Item { id: item width: root.width height: root.width signal actionTriggered(string actionId, variant actionArgument) signal aboutToShowActionMenu(variant actionMenu) property bool hasActionList: ((model.favoriteId !== null) || (("hasActionList" in model) && (model.hasActionList !== null))) property int itemIndex: model.index - onAboutToShowActionMenu: { + onAboutToShowActionMenu: actionMenu => { var actionList = (model.hasActionList !== null) ? model.actionList : []; Tools.fillActionMenu(i18n, actionMenu, actionList, repeater.model, model.favoriteId); } - onActionTriggered: { + onActionTriggered: (actionId, actionArgument) => { if (Tools.triggerAction(repeater.model, model.index, actionId, actionArgument) === true) { plasmoid.expanded = false; } } function openActionMenu(visualParent, x, y) { aboutToShowActionMenu(actionMenu); actionMenu.visualParent = visualParent; actionMenu.open(x, y); } ActionMenu { id: actionMenu onActionClicked: { actionTriggered(actionId, actionArgument); } } PlasmaCore.IconItem { anchors.fill: parent active: toolTip.containsMouse source: model.decoration usesPlasmaTheme: repeater.usesPlasmaTheme } MouseEventListener { id: listener anchors { fill: parent leftMargin: - sideBar.margins.left rightMargin: - sideBar.margins.right } enabled: (item.parent && !item.parent.animating) property bool pressed: false property int pressX: -1 property int pressY: -1 hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton onPressed: { if (mouse.buttons & Qt.RightButton) { if (item.hasActionList) { item.openActionMenu(item, mouse.x, mouse.y); } } else { pressed = true; pressX = mouse.x; pressY = mouse.y; } } onReleased: { if (pressed) { repeater.model.trigger(index, "", null); plasmoid.expanded = false; } pressed = false; pressX = -1; pressY = -1; } onContainsMouseChanged: { if (!containsMouse) { pressed = false; pressX = -1; pressY = -1; } } onPositionChanged: { if (pressX != -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) { kicker.dragSource = item; dragHelper.startDrag(kicker, model.url, model.icon); pressed = false; pressX = -1; pressY = -1; return; } } } PlasmaCore.ToolTipArea { id: toolTip property string text: model.display anchors { fill: parent leftMargin: - sideBar.margins.left rightMargin: - sideBar.margins.right } interactive: false location: (((plasmoid.location == PlasmaCore.Types.RightEdge) || (Qt.application.layoutDirection == Qt.RightToLeft)) ? PlasmaCore.Types.RightEdge : PlasmaCore.Types.LeftEdge) mainItem: toolTipDelegate } } diff --git a/applets/kicker/package/contents/ui/SideBarSection.qml b/applets/kicker/package/contents/ui/SideBarSection.qml index 31fac3ab9..ada5abc77 100644 --- a/applets/kicker/package/contents/ui/SideBarSection.qml +++ b/applets/kicker/package/contents/ui/SideBarSection.qml @@ -1,99 +1,99 @@ /*************************************************************************** * Copyright (C) 2013-2014 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.draganddrop 2.0 DropArea { id: root width: units.iconSizes.medium height: contentHeight anchors.horizontalCenter: parent.horizontalCenter property int contentHeight: model ? (model.count * units.iconSizes.medium) + ((model.count - 1) * flow.spacing) : 0 property alias model: repeater.model property alias usesPlasmaTheme: repeater.usesPlasmaTheme - onDragMove: { + onDragMove: event => { if (flow.animating) { return; } var above = flow.childAt(event.x, event.y); if (above && above !== kicker.dragSource && dragSource.parent == flow) { repeater.model.moveRow(dragSource.itemIndex, above.itemIndex); } } Flow { id: flow anchors.fill: parent property bool animating: false property int animationDuration: resetAnimationDurationTimer.interval move: Transition { SequentialAnimation { PropertyAction { target: flow; property: "animating"; value: true } NumberAnimation { duration: flow.animationDuration properties: "x, y" easing.type: Easing.OutQuad } PropertyAction { target: flow; property: "animating"; value: false } } } spacing: (2 * units.smallSpacing) Repeater { id: repeater property bool usesPlasmaTheme: false delegate: SideBarItem {} onCountChanged: { flow.animationDuration = 0; resetAnimationDurationTimer.start(); } } } Timer { id: resetAnimationDurationTimer interval: 150 repeat: false onTriggered: { flow.animationDuration = interval - 20; } } }