diff --git a/examples/gallerydata/contents/ui/gallery/ListViewGallery.qml b/examples/gallerydata/contents/ui/gallery/ListViewGallery.qml index 0148680c..b4438834 100644 --- a/examples/gallerydata/contents/ui/gallery/ListViewGallery.qml +++ b/examples/gallerydata/contents/ui/gallery/ListViewGallery.qml @@ -1,112 +1,145 @@ /* * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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 QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls -import org.kde.kirigami 2.4 as Kirigami +import org.kde.kirigami 2.5 as Kirigami Kirigami.ScrollablePage { id: page Layout.fillWidth: true title: "Long List view" actions { main: Kirigami.Action { iconName: sheet.sheetOpen ? "dialog-cancel" : "document-edit" text: "Main Action Text" checkable: true onCheckedChanged: sheet.sheetOpen = checked; } } //Close the drawer with the back button onBackRequested: { if (sheet.sheetOpen) { event.accepted = true; sheet.close(); } } supportsRefreshing: true onRefreshingChanged: { if (refreshing) { refreshRequestTimer.running = true; } else { showPassiveNotification("Example refreshing completed") } } background: Rectangle { color: Kirigami.Theme.backgroundColor } Kirigami.OverlaySheet { id: sheet onSheetOpenChanged: page.actions.main.checked = sheetOpen; parent: applicationWindow().overlay header: Kirigami.Heading { text: "Title" } footer: RowLayout { Controls.Label { text: "Footer:" } Controls.TextField { Layout.fillWidth: true Layout.alignment: Qt.AlignCenter } } ListView { model: 100 implicitWidth: Kirigami.Units.gridUnit * 30 delegate: Kirigami.BasicListItem { label: "Item in sheet" + modelData } } } - ListView { - Timer { - id: refreshRequestTimer - interval: 3000 - onTriggered: page.refreshing = false - } - model: 200 - delegate: Kirigami.SwipeListItem { + Component { + id: delegateComponent + Kirigami.SwipeListItem { id: listItem - contentItem: Controls.Label { - height: Math.max(implicitHeight, Kirigami.Units.iconSizes.smallMedium) - anchors.verticalCenter: parent.verticalCenter - text: "Item " + modelData - color: listItem.checked || (listItem.pressed && !listItem.checked && !listItem.sectionDelegate) ? listItem.activeTextColor : listItem.textColor + contentItem: RowLayout { + Kirigami.ListItemDragHandle { + listItem: listItem + listView: mainList + onMoveRequested: listModel.move(oldIndex, newIndex, 1) + } + + Controls.Label { + Layout.fillWidth: true + height: Math.max(implicitHeight, Kirigami.Units.iconSizes.smallMedium) + text: model.title + color: listItem.checked || (listItem.pressed && !listItem.checked && !listItem.sectionDelegate) ? listItem.activeTextColor : listItem.textColor + } } actions: [ Kirigami.Action { iconName: "document-decrypt" text: "Action 1" onTriggered: showPassiveNotification(model.text + " Action 1 clicked") }, Kirigami.Action { iconName: "mail-reply-sender" text: "Action 2" onTriggered: showPassiveNotification(model.text + " Action 2 clicked") }] } } + ListView { + id: mainList + Timer { + id: refreshRequestTimer + interval: 3000 + onTriggered: page.refreshing = false + } + model: ListModel { + id: listModel + + Component.onCompleted: { + for (var i = 0; i < 200; ++i) { + listModel.append({"title": "Item " + i, + "actions": [{text: "Action 1", icon: "document-decrypt"}, + {text: "Action 2", icon: "mail-reply-sender"}] + }) + } + } + } + moveDisplaced: Transition { + YAnimator { + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } + delegate: Kirigami.DelegateRecycler { + width: parent ? parent.width : implicitWidth + sourceComponent: delegateComponent + } + } } diff --git a/kirigami.qrc b/kirigami.qrc index ac0d0a26..0c5839e0 100644 --- a/kirigami.qrc +++ b/kirigami.qrc @@ -1,73 +1,74 @@ src/controls/AbstractApplicationWindow.qml src/controls/ContextDrawer.qml src/controls/Action.qml src/controls/Page.qml src/controls/PageRow.qml src/controls/AbstractListItem.qml src/controls/Theme.qml src/controls/AbstractCard.qml src/controls/templates/AbstractCard.qml src/controls/Card.qml src/controls/CardsLayout.qml src/controls/CardsListView.qml src/controls/CardsGridView.qml src/controls/templates/AbstractCard.qml src/controls/InlineMessage.qml src/controls/ToolBarApplicationHeader.qml src/controls/private/PrivateActionToolButton.qml src/controls/private/RefreshableScrollView.qml src/controls/private/SwipeItemEventFilter.qml src/controls/private/PageActionPropertyGroup.qml src/controls/private/ActionIconGroup.qml src/controls/private/CornerShadow.qml src/controls/private/ActionButton.qml src/controls/private/DefaultListItemBackground.qml src/controls/private/BannerImage.qml src/controls/private/BannerGroup.qml src/controls/private/EdgeShadow.qml src/controls/Separator.qml src/controls/OverlayDrawer.qml src/controls/OverlaySheet.qml src/controls/GlobalDrawer.qml src/controls/templates/AbstractListItem.qml src/controls/templates/private/MenuIcon.qml src/controls/templates/private/GenericDrawerIcon.qml src/controls/templates/private/PassiveNotification.qml src/controls/templates/private/ContextIcon.qml src/controls/templates/private/ScrollView.qml src/controls/templates/private/BackButton.qml src/controls/templates/private/IconPropertiesGroup.qml src/controls/templates/private/ForwardButton.qml src/controls/templates/OverlayDrawer.qml src/controls/templates/OverlaySheet.qml src/controls/templates/SwipeListItem.qml src/controls/templates/ApplicationHeader.qml src/controls/templates/AbstractApplicationHeader.qml src/controls/Units.qml src/controls/SwipeListItem.qml src/controls/ApplicationWindow.qml src/controls/AbstractApplicationItem.qml src/controls/ApplicationItem.qml src/controls/ApplicationHeader.qml src/controls/Heading.qml src/controls/ScrollablePage.qml src/controls/Label.qml src/controls/BasicListItem.qml src/controls/AbstractApplicationHeader.qml src/controls/FormLayout.qml + src/controls/ListItemDragHandle.qml src/styles/Material/AbstractListItem.qml src/styles/Material/Theme.qml src/styles/Material/SwipeListItem.qml src/styles/Material/Label.qml src/styles/org.kde.desktop/AbstractListItem.qml src/styles/org.kde.desktop/Theme.qml src/styles/org.kde.desktop/OverlayDrawer.qml src/styles/org.kde.desktop/Units.qml src/styles/org.kde.desktop/SwipeListItem.qml src/styles/org.kde.desktop/ApplicationWindow.qml src/styles/org.kde.desktop/AbstractApplicationHeader.qml src/controls/templates/FormLayout.qml diff --git a/src/controls/ListItemDragHandle.qml b/src/controls/ListItemDragHandle.qml new file mode 100644 index 00000000..ca3cd938 --- /dev/null +++ b/src/controls/ListItemDragHandle.qml @@ -0,0 +1,193 @@ +/* +* Copyright (C) 2018 by Marco Martin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License as +* published by the Free Software Foundation; either version 2, 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 Library General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. +*/ + +import QtQuick 2.6 +import QtQuick.Layouts 1.2 +import org.kde.kirigami 2.4 as Kirigami + +/** + * Implements a drag handle supposed to be in items in ListViews to reorder items + * The ListView must visualize a model which supports item reordering, + * such as ListModel.move() or QAbstractItemModel instances with moveRows() correctly implemented. + * In order for ListItemDragHandle to work correctly, the listItem that is being dragged + * should not directly be the delegate of the ListView, but a child of it. + * + * It is recomended to use DelagateRecycler as base delegate like the following code: + * @code + * ... + * Component { + * id: delegateComponent + * Kirigami.AbstractListItem { + * id: listItem + * contentItem: RowLayout { + * Kirigami.ListItemDragHandle { + * listItem: listItem + * listView: mainList + * onMoveRequested: listModel.move(oldIndex, newIndex, 1) + * } + * Controls.Label { + * text: model.label + * } + * } + * } + * } + * ListView { + * id: mainList + * + * model: ListModel { + * id: listModel + * ListItem { + * lablel: "Item 1" + * } + * ListItem { + * lablel: "Item 2" + * } + * ListItem { + * lablel: "Item 3" + * } + * } + * //this is optional to make list items animated when reordered + * moveDisplaced: Transition { + * YAnimator { + * duration: Kirigami.Units.longDuration + * easing.type: Easing.InOutQuad + * } + * } + * delegate: Kirigami.DelegateRecycler { + * width: mainList.width + * sourceComponent: delegateComponent + * } + * } + * ... + * @endcode + * + * @inherits MouseArea + * @since 2.5 + */ +MouseArea { + id: root + + /** + * listItem: Item + * The id of the delegate that we want to drag around, which *must* + * be a child of the actual ListView's delegate + */ + property Item listItem + + /** + * listView: Listview + * The id of the ListView the delegates belong to. + */ + property ListView listView + + /** + * Emitted when the drag handle wants to move the item in the model + * The following example does the move in the case a ListModel is used + * @code + * onMoveRequested: listModel.move(oldIndex, newIndex, 1) + * @endcode + * @param oldIndex the index the item is currently at + * @param newIndex the index we want to move the item to + */ + signal moveRequested(int oldIndex, int newIndex) + + hoverEnabled: !Kirigami.Settings.tabletMode + + drag { + target: listItem + axis: Drag.YAxis + minimumY: 0 + maximumY: listView.height - listItem.height + } + Kirigami.Icon { + id: internal + source: "handle-sort" + property int startY + property int mouseDownY + property Item originalParent + property int autoScrollThreshold: listItem.height * 3 + opacity: root.pressed || root.containsMouse ? 1 : 0.6 + + function arrangeItem() { + var newIndex = listView.indexAt(1, listView.contentItem.mapFromItem(listItem, 0, 0).y + internal.mouseDownY); + + if (Math.abs(listItem.y - internal.startY) > height && newIndex > -1 && newIndex != index) { + root.moveRequested(index, newIndex); + } + } + + anchors.fill: parent + } + preventStealing: true + implicitWidth: Kirigami.Units.iconSizes.smallMedium + implicitHeight: implicitWidth + + + onPressed: { + internal.originalParent = listItem.parent; + listItem.parent = listView; + listItem.y = internal.originalParent.mapToItem(listItem.parent, listItem.x, listItem.y).y; + internal.originalParent.z = 99; + internal.startY = listItem.y; + internal.mouseDownY = mouse.y; + } + + onPositionChanged: { + internal.arrangeItem(); + + scrollTimer.interval = 500 * Math.max(0.1, (1-Math.max(internal.autoScrollThreshold - listItem.y, listItem.y - listView.height + internal.autoScrollThreshold + listItem.height) / internal.autoScrollThreshold)); + scrollTimer.running = (listItem.y < internal.autoScrollThreshold || + listItem.y > listView.height - internal.autoScrollThreshold); + } + onReleased: { + listItem.y = internal.originalParent.mapFromItem(listItem, 0, 0).y; + listItem.parent = internal.originalParent; + dropAnimation.running = true; + scrollTimer.running = false; + } + onCanceled: released() + SequentialAnimation { + id: dropAnimation + YAnimator { + target: listItem + from: listItem.y + to: 0 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + PropertyAction { + target: listItem.parent + property: "z" + value: 0 + } + } + Timer { + id: scrollTimer + interval: 500 + repeat: true + onTriggered: { + if (listItem.y < internal.autoScrollThreshold) { + listView.contentY = Math.max(0, listView.contentY - Kirigami.Units.gridUnit) + } else { + listView.contentY = Math.min(listView.contentHeight - listView.height, listView.contentY + Kirigami.Units.gridUnit) + } + internal.arrangeItem(); + } + } +} diff --git a/src/controls/private/DefaultListItemBackground.qml b/src/controls/private/DefaultListItemBackground.qml index 7ec2a0af..09ae5299 100644 --- a/src/controls/private/DefaultListItemBackground.qml +++ b/src/controls/private/DefaultListItemBackground.qml @@ -1,59 +1,52 @@ /* * Copyright 2016 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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.1 import org.kde.kirigami 2.4 Rectangle { id: background color: listItem.checked || listItem.highlighted || (listItem.supportsMouseEvents && listItem.pressed && !listItem.checked && !listItem.sectionDelegate) ? listItem.activeBackgroundColor : listItem.backgroundColor visible: listItem.ListView.view ? listItem.ListView.view.highlight === null : true Rectangle { id: internal property bool indicateActiveFocus: listItem.pressed || Settings.tabletMode || listItem.activeFocus || (listItem.ListView.view ? listItem.ListView.view.activeFocus : false) anchors.fill: parent visible: !Settings.tabletMode && listItem.supportsMouseEvents color: listItem.activeBackgroundColor opacity: (listItem.hovered || listItem.highlighted) && !listItem.pressed ? (indicateActiveFocus ? 0.2 : 0.1 ) : 0 Behavior on opacity { NumberAnimation { duration: Units.longDuration } } } Behavior on color { ColorAnimation { duration: Units.longDuration } } - readonly property bool _firstElement: typeof(index) !== "undefined" && index == 0 + readonly property bool __separatorVisible: listItem.separatorVisible - on_FirstElementChanged: { - if (_firstElement) { + on__SeparatorVisibleChanged: { + if (__separatorVisible) { var newObject = Qt.createQmlObject('import QtQuick 2.0; import org.kde.kirigami 2.4; Separator {anchors {left: parent.left; right: parent.right; bottom: parent.top} visible: listItem.separatorVisible}', background); + newObject = Qt.createQmlObject('import QtQuick 2.0; import org.kde.kirigami 2.4; Separator {anchors {left: parent.left; right: parent.right; bottom: parent.bottom} visible: listItem.separatorVisible}', + background); } } - - Separator { - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - visible: listItem.separatorVisible - } } diff --git a/src/controls/templates/SwipeListItem.qml b/src/controls/templates/SwipeListItem.qml index 1b6031cb..5c0ce5dd 100644 --- a/src/controls/templates/SwipeListItem.qml +++ b/src/controls/templates/SwipeListItem.qml @@ -1,383 +1,394 @@ /* * Copyright 2010 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.4 import "../private" import QtQuick.Templates 2.0 as T2 /** * An item delegate Intended to support extra actions obtainable * by uncovering them by dragging away the item with the handle * This acts as a container for normal list items. * Any subclass of AbstractListItem can be assigned as the contentItem property. * @code * ListView { * model: myModel * delegate: SwipeListItem { * QQC2.Label { * text: model.text * } * actions: [ * Action { * iconName: "document-decrypt" * onTriggered: print("Action 1 clicked") * }, * Action { * iconName: model.action2Icon * onTriggered: //do something * } * ] * } * * } * @endcode * * @inherit QtQuick.Templates.ItemDelegate */ T2.ItemDelegate { id: listItem //BEGIN properties /** * supportsMouseEvents: bool * Holds if the item emits signals related to mouse interaction. *TODO: remove * The default value is false. */ property alias supportsMouseEvents: listItem.hoverEnabled /** * containsMouse: bool * True when the user hover the mouse over the list item * NOTE: on mobile touch devices this will be true only when pressed is also true */ property alias containsMouse: listItem.hovered /** * sectionDelegate: bool * If true the item will be a delegate for a section, so will look like a * "title" for the items under it. */ property bool sectionDelegate: false /** * separatorVisible: bool * True if the separator between items is visible * default: true */ property bool separatorVisible: true /** * actions: list * Defines the actions for the list item: at most 4 buttons will * contain the actions for the item, that can be revealed by * sliding away the list item. */ property list actions /** * textColor: color * Color for the text in the item * * Note: if custom text elements are inserted in an AbstractListItem, * their color property will have to be manually bound with this property */ property color textColor: Theme.textColor /** * backgroundColor: color * Color for the background of the item */ property color backgroundColor: Theme.backgroundColor /** * activeTextColor: color * Color for the text in the item when pressed or selected * It is advised to leave the default value (Theme.highlightedTextColor) * * Note: if custom text elements are inserted in an AbstractListItem, * their color property will have to be manually bound with this property */ property color activeTextColor: Theme.highlightedTextColor /** * activeBackgroundColor: color * Color for the background of the item when pressed or selected * It is advised to leave the default value (Theme.highlightColor) */ property color activeBackgroundColor: Theme.highlightColor default property alias _default: listItem.contentItem Theme.colorGroup: behindItem.indicateActiveFocus ? Theme.Active : Theme.Inactive hoverEnabled: true implicitWidth: contentItem ? contentItem.implicitWidth : Units.gridUnit * 12 width: parent ? parent.width : implicitWidth implicitHeight: contentItem.implicitHeight + Units.smallSpacing * 5 leftPadding: Units.smallSpacing * 2 + (LayoutMirroring.enabled ? (handleMouse.visible ? handleMouse.width : 0) + handleMouse.anchors.rightMargin : 0) topPadding: Units.smallSpacing * 2 rightPadding: Units.smallSpacing * 2 + (LayoutMirroring.enabled ? 0 : (handleMouse.visible ? handleMouse.width : 0) + handleMouse.anchors.rightMargin) bottomPadding: Units.smallSpacing * 2 //END properties Item { id: behindItem parent: listItem z: -1 //TODO: a global "open" state enabled: background.x !== 0 property bool indicateActiveFocus: listItem.pressed || Settings.tabletMode || listItem.activeFocus || (view ? view.activeFocus : false) - property Flickable view: listItem.ListView.view || listItem.parent.ListView.view + property Flickable view: listItem.ListView.view || (listItem.parent ? (listItem.parent.ListView.view || listItem.parent) : null) + onViewChanged: { + if (view && Settings.tabletMode && !behindItem.view.parent.parent._swipeFilter) { + var component = Qt.createComponent(Qt.resolvedUrl("../private/SwipeItemEventFilter.qml")); + behindItem.view.parent.parent._swipeFilter = component.createObject(behindItem.view.parent.parent); + print("SSS"+behindItem.view.parent.parent._swipeFilter+internal.swipeFilterItem+" "+(behindItem.view && behindItem.view.parent && behindItem.view.parent.parent)) + } + } + anchors { fill: parent } Rectangle { id: shadowHolder color: Qt.darker(Theme.backgroundColor, 1.05); anchors.fill: parent } EdgeShadow { edge: Qt.TopEdge anchors { right: parent.right left: parent.left top: parent.top } } EdgeShadow { edge: LayoutMirroring.enabled ? Qt.RightEdge : Qt.LeftEdge x: LayoutMirroring.enabled ? listItem.background.x - width : (listItem.background.x + listItem.background.width) anchors { top: parent.top bottom: parent.bottom } } MouseArea { anchors.fill: parent preventStealing: true enabled: background.x != 0 onClicked: { positionAnimation.from = background.x; positionAnimation.to = 0; positionAnimation.running = true; } } Row { id: actionsLayout z: 1 parent: Settings.tabletMode ? behindItem : listItem opacity: Settings.tabletMode ? 1 : (listItem.hovered ? 1 : 0) Behavior on opacity { OpacityAnimator { duration: Units.longDuration easing.type: Easing.InOutQuad } } anchors { right: parent.right verticalCenter: parent.verticalCenter rightMargin: LayoutMirroring.enabled ? listItem.leftPadding : listItem.rightPadding } height: Math.min( parent.height / 1.5, Units.iconSizes.medium) width: childrenRect.width property bool exclusive: false property Item checkedButton spacing: Units.largeSpacing Repeater { model: { if (listItem.actions.length == 0) { return null; } else { return listItem.actions[0].text !== undefined && listItem.actions[0].trigger !== undefined ? listItem.actions : listItem.actions[0]; } } delegate: Icon { height: actionsLayout.height width: height source: modelData.iconName enabled: (modelData && modelData.enabled !== undefined) ? modelData.enabled : true; visible: (modelData && modelData.visible !== undefined) ? modelData.visible : true; MouseArea { id: actionMouse anchors { fill: parent; margins: Settings.tabletMode ? -Units.smallSpacing : 0; } enabled: (modelData && modelData.enabled !== undefined) ? modelData.enabled : true; hoverEnabled: !Settings.tabletMode onClicked: { if (modelData && modelData.trigger !== undefined) { modelData.trigger(); } positionAnimation.from = background.x; positionAnimation.to = 0; positionAnimation.running = true; } Controls.ToolTip.delay: 1000 Controls.ToolTip.timeout: 5000 Controls.ToolTip.visible: (Settings.tabletMode ? actionMouse.pressed : actionMouse.containsMouse) && Controls.ToolTip.text.length > 0 Controls.ToolTip.text: modelData.tooltip || modelData.text } } } } } MouseArea { id: handleMouse parent: listItem.background visible: Settings.tabletMode z: 99 anchors { right: parent.right top: parent.top bottom: parent.bottom rightMargin: behindItem.view && behindItem.view.T2.ScrollBar && behindItem.view.T2.ScrollBar.vertical && behindItem.view.T2.ScrollBar.vertical.interactive ? behindItem.view.T2.ScrollBar.vertical.width : Units.smallSpacing } preventStealing: true width: height property var downTimestamp; property int startX property int startMouseX onClicked: { positionAnimation.from = background.x; if (listItem.background.x > -listItem.background.width/2) { positionAnimation.to = (LayoutMirroring.enabled ? -1 : +1) * (-listItem.width + height + handleMouse.anchors.rightMargin); } else { positionAnimation.to = 0; } positionAnimation.running = true; } onPressed: { downTimestamp = (new Date()).getTime(); startX = listItem.background.x; startMouseX = mouse.x; } onPositionChanged: { if (LayoutMirroring.enabled) { listItem.background.x = Math.max(0, Math.min(listItem.width - height, listItem.background.x - (startMouseX - mouse.x))); } else { listItem.background.x = Math.min(0, Math.max(-listItem.width + height, listItem.background.x - (startMouseX - mouse.x))); } } onReleased: { var speed = ((startX - listItem.background.x) / ((new Date()).getTime() - downTimestamp) * 1000); if (LayoutMirroring.enabled) { speed = -speed; } if (Math.abs(speed) < Units.gridUnit) { return; } if (speed > listItem.width/2) { positionAnimation.to = (LayoutMirroring.enabled ? -1 : +1) * (-listItem.width + height + handleMouse.anchors.rightMargin); } else { positionAnimation.to = 0; } positionAnimation.from = background.x; positionAnimation.running = true; } Icon { id: handleIcon anchors.verticalCenter: parent.verticalCenter selected: listItem.checked || (listItem.pressed && !listItem.checked && !listItem.sectionDelegate) width: Units.iconSizes.smallMedium height: width x: y source: (LayoutMirroring.enabled ? (listItem.background.x < listItem.background.width/2 ? "handle-right" : "handle-left") : (listItem.background.x < -listItem.background.width/2 ? "handle-right" : "handle-left")) } } NumberAnimation { id: positionAnimation property: "x" target: background duration: Units.longDuration easing.type: Easing.InOutQuad } //BEGIN signal handlers onContentItemChanged: { if (!contentItem) { return; } contentItem.parent = background; contentItem.anchors.top = background.top; contentItem.anchors.left = background.left; contentItem.anchors.right = background.right; contentItem.anchors.leftMargin = Qt.binding(function() {return listItem.leftPadding}); contentItem.anchors.rightMargin = Qt.binding(function() {return listItem.rightPadding}); contentItem.anchors.topMargin = Qt.binding(function() {return listItem.topPadding}); contentItem.z = 0; } Component.onCompleted: { //this will happen only once - if (Settings.tabletMode && !swipeFilterConnection.swipeFilterItem) { - var component = Qt.createComponent(Qt.resolvedUrl("../private/SwipeItemEventFilter.qml")); - behindItem.view.parent.parent._swipeFilter = component.createObject(behindItem.view.parent.parent); - } listItem.contentItemChanged(); } Connections { target: Settings onTabletModeChanged: { if (Settings.tabletMode) { - if (!swipeFilterConnection.swipeFilterItem) { + if (!internal.swipeFilterItem) { var component = Qt.createComponent(Qt.resolvedUrl("../private/SwipeItemEventFilter.qml")); listItem.ListView.view.parent.parent._swipeFilter = component.createObject(listItem.ListView.view.parent.parent); } } else { if (listItem.ListView.view.parent.parent._swipeFilter) { listItem.ListView.view.parent.parent._swipeFilter.destroy(); positionAnimation.to = 0; positionAnimation.from = background.x; positionAnimation.running = true; } } } } + QtObject { + id: internal + readonly property QtObject swipeFilterItem: (behindItem.view && behindItem.view.parent && behindItem.view.parent.parent) ? behindItem.view.parent.parent._swipeFilter : null + + readonly property bool edgeEnabled: swipeFilterItem ? swipeFilterItem.currentItem === listItem || swipeFilterItem.currentItem === listItem.parent : false + } + Connections { id: swipeFilterConnection - readonly property QtObject swipeFilterItem: (behindItem.view && behindItem.view && behindItem.view.parent && behindItem.view.parent.parent) ? behindItem.view.parent.parent._swipeFilter : null - readonly property bool enabled: swipeFilterItem ? swipeFilterItem.currentItem === listItem : false - target: enabled ? swipeFilterItem : null - onPeekChanged: listItem.background.x = -(listItem.background.width - listItem.background.height) * swipeFilterItem.peek + + target: internal.edgeEnabled ? internal.swipeFilterItem : null + onPeekChanged: listItem.background.x = -(listItem.background.width - listItem.background.height) * internal.swipeFilterItem.peek onCurrentItemChanged: { - if (!enabled) { + if (!internal.edgeEnabled) { positionAnimation.to = 0; positionAnimation.from = background.x; positionAnimation.running = true; } } } + //END signal handlers Accessible.role: Accessible.ListItem } diff --git a/src/delegaterecycler.cpp b/src/delegaterecycler.cpp index 074ec473..42dbe7e1 100644 --- a/src/delegaterecycler.cpp +++ b/src/delegaterecycler.cpp @@ -1,241 +1,268 @@ /* * Copyright 2011 Marco Martin * Copyright 2014 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library 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. */ #include "delegaterecycler.h" #include #include #include #include class DelegateCache { public: DelegateCache(); ~DelegateCache(); void ref(QQmlComponent *); void deref(QQmlComponent *); void insert(QQmlComponent *, QQuickItem *); QQuickItem *take(QQmlComponent *); private: static const int s_cacheSize = 40; QHash m_refs; QHash > m_unusedItems; }; Q_GLOBAL_STATIC(DelegateCache, s_delegateCache) DelegateCache::DelegateCache() { } DelegateCache::~DelegateCache() { for (auto item : m_unusedItems) { qDeleteAll(item); } } void DelegateCache::ref(QQmlComponent *component) { m_refs[component]++; } void DelegateCache::deref(QQmlComponent *component) { if (!m_refs.contains(component)) { return; } m_refs[component]--; if (m_refs[component] <= 0) { m_refs.remove(component); if (m_unusedItems.contains(component)) { qDeleteAll(m_unusedItems[component]); m_unusedItems.remove(component); } } } void DelegateCache::insert(QQmlComponent *component, QQuickItem *item) { if (m_unusedItems.contains(component) && m_unusedItems[component].length() >= s_cacheSize) { item->deleteLater(); return; } item->setParentItem(nullptr); m_unusedItems[component].append(item); } QQuickItem *DelegateCache::take(QQmlComponent *component) { if (m_unusedItems.contains(component) && !m_unusedItems[component].isEmpty()) { QQuickItem *item = m_unusedItems[component].first(); m_unusedItems[component].pop_front(); return item; } return nullptr; } DelegateRecycler::DelegateRecycler(QQuickItem *parent) : QQuickItem(parent) { } DelegateRecycler::~DelegateRecycler() { if (m_sourceComponent) { s_delegateCache->insert(m_sourceComponent, m_item); s_delegateCache->deref(m_sourceComponent); } } +void DelegateRecycler::syncIndex() +{ + QQmlContext *ctx = QQmlEngine::contextForObject(m_item)->parentContext(); + ctx->setContextProperty(QStringLiteral("index"), m_propertiesTracker->property("trackedIndex")); +} + +void DelegateRecycler::syncModel() +{ + QQmlContext *ctx = QQmlEngine::contextForObject(m_item)->parentContext(); + ctx->setContextProperty(QStringLiteral("model"), m_propertiesTracker->property("trackedModel")); +} + +void DelegateRecycler::syncModelData() +{ + QQmlContext *ctx = QQmlEngine::contextForObject(m_item)->parentContext(); + ctx->setContextProperty(QStringLiteral("modelData"), m_propertiesTracker->property("trackedModelData")); +} + QQmlComponent *DelegateRecycler::sourceComponent() const { return m_sourceComponent; } void DelegateRecycler::setSourceComponent(QQmlComponent *component) { if (component && component->parent() == this) { qWarning() << "Error: source components cannot be declared inside DelegateRecycler"; return; } if (m_sourceComponent == component) { return; } + + if (!m_propertiesTracker) { + QQmlComponent *propertiesTrackerComponent = new QQmlComponent(qmlEngine(this), this); + + propertiesTrackerComponent->setData(QByteArrayLiteral("import QtQuick 2.3\nQtObject{property int trackedIndex: index; property var trackedModel: typeof model != 'undefined' ? model : null; property var trackedModelData: typeof modelData != 'undefined' ? modelData : null}"), QUrl()); + m_propertiesTracker = propertiesTrackerComponent->create(QQmlEngine::contextForObject(this)); + + connect(m_propertiesTracker, SIGNAL(trackedIndexChanged()), this, SLOT(syncIndex())); + connect(m_propertiesTracker, SIGNAL(trackedModelChanged()), this, SLOT(syncModel())); + connect(m_propertiesTracker, SIGNAL(trackedModelDataChanged()), this, SLOT(syncModelData())); + } + if (m_sourceComponent) { if (m_item) { disconnect(m_item.data(), &QQuickItem::implicitWidthChanged, this, &DelegateRecycler::updateHints); disconnect(m_item.data(), &QQuickItem::implicitHeightChanged, this, &DelegateRecycler::updateHints); s_delegateCache->insert(component, m_item); } s_delegateCache->deref(component); } m_sourceComponent = component; s_delegateCache->ref(component); m_item = s_delegateCache->take(component); if (!m_item) { QQuickItem *candidate = parentItem(); QQmlContext *ctx = nullptr; while (candidate) { QQmlContext *parentCtx = QQmlEngine::contextForObject(candidate); if (parentCtx) { ctx = new QQmlContext(QQmlEngine::contextForObject(candidate), candidate); break; } else { candidate = candidate->parentItem(); } } - QQmlContext *myCtx = QQmlEngine::contextForObject(this); - ctx->setContextProperty(QStringLiteral("model"), myCtx->contextProperty(QStringLiteral("model"))); - ctx->setContextProperty(QStringLiteral("modelData"), myCtx->contextProperty(QStringLiteral("modelData"))); - ctx->setContextProperty(QStringLiteral("index"), myCtx->contextProperty(QStringLiteral("index"))); + ctx->setContextProperty(QStringLiteral("model"), m_propertiesTracker->property("trackedModel")); + ctx->setContextProperty(QStringLiteral("modelData"), m_propertiesTracker->property("trackedModelData")); + ctx->setContextProperty(QStringLiteral("index"), m_propertiesTracker->property("trackedIndex")); QObject * obj = component->create(ctx); m_item = qobject_cast(obj); if (!m_item) { obj->deleteLater(); } } else { - QQmlContext *myCtx = QQmlEngine::contextForObject(this); QQmlContext *ctx = QQmlEngine::contextForObject(m_item)->parentContext(); - QObject *model = myCtx->contextProperty(QStringLiteral("model")).value(); - ctx->setContextProperty(QStringLiteral("model"), QVariant::fromValue(model)); - ctx->setContextProperty(QStringLiteral("modelData"), myCtx->contextProperty(QStringLiteral("modelData"))); - ctx->setContextProperty(QStringLiteral("index"), myCtx->contextProperty(QStringLiteral("index"))); + ctx->setContextProperty(QStringLiteral("model"), m_propertiesTracker->property("trackedModel")); + ctx->setContextProperty(QStringLiteral("modelData"), m_propertiesTracker->property("trackedModelData")); + ctx->setContextProperty(QStringLiteral("index"), m_propertiesTracker->property("trackedIndex")); } if (m_item) { m_item->setParentItem(this); connect(m_item.data(), &QQuickItem::implicitWidthChanged, this, &DelegateRecycler::updateHints); connect(m_item.data(), &QQuickItem::implicitHeightChanged, this, &DelegateRecycler::updateHints); updateSize(true); } emit sourceComponentChanged(); } void DelegateRecycler::resetSourceComponent() { s_delegateCache->deref(m_sourceComponent); m_sourceComponent = nullptr; } void DelegateRecycler::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - if (m_item && newGeometry != oldGeometry) { + if (m_item && newGeometry.size() != oldGeometry.size()) { updateSize(true); } QQuickItem::geometryChanged(newGeometry, oldGeometry); } void DelegateRecycler::updateHints() { updateSize(false); } void DelegateRecycler::updateSize(bool parentResized) { if (!m_item) { return; } const bool needToUpdateWidth = parentResized && widthValid(); const bool needToUpdateHeight = parentResized && heightValid(); if (parentResized) { m_item->setPosition(QPoint(0,0)); } if (needToUpdateWidth && needToUpdateHeight) { m_item->setSize(QSizeF(width(), height())); } else if (needToUpdateWidth) { m_item->setWidth(width()); } else if (needToUpdateHeight) { m_item->setHeight(height()); } if (m_updatingSize) { return; } m_updatingSize = true; setImplicitSize(m_item->implicitWidth() >= 0 ? m_item->implicitWidth() : m_item->width(), m_item->implicitHeight() >= 0 ? m_item->implicitHeight() : m_item->height()); m_updatingSize = false; } #include diff --git a/src/delegaterecycler.h b/src/delegaterecycler.h index 832c309d..647ad677 100644 --- a/src/delegaterecycler.h +++ b/src/delegaterecycler.h @@ -1,81 +1,87 @@ /* * Copyright 2018 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library 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. */ #ifndef DELEGATERECYCLER_H #define DELEGATERECYCLER_H #include #include #include class DelegateCache; /** * This class may be used as a delegate of a ListView or a GridView in the case * the intended delegate is a bit heavy, with many objects inside. * This will ensure the delegate instances will be put back in a commoin pool after * destruction, so when scrolling a big list, the delegates from old delete items will * be taken from the pool and reused, minimizing the need of instantiating new objects * and deleting old ones. It ensures scrolling of lists with heavy delegates is * smoother and helps with memory fragmentations as well. * * NOTE: CardListView and CardGridView are already using this recycler, so do NOT use it * as a delegate for those 2 views. * Also, do NOT use this with a Repeater. * @since 2.4 */ class DelegateRecycler : public QQuickItem { Q_OBJECT /** * The Component the actual delegates will be built from. * Note: the component may not be a child of this object, therefore it can't be * declared inside the DelegateRecycler declaration. * The DelegateRecycler will not take ownership of the delegate Component, so it's up * to the caller to delete it (usually with the normal child/parent relationship) */ Q_PROPERTY(QQmlComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceComponentChanged) public: DelegateRecycler(QQuickItem *parent=0); ~DelegateRecycler(); QQmlComponent *sourceComponent() const; void setSourceComponent(QQmlComponent *component); void resetSourceComponent(); protected: void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; void updateHints(); void updateSize(bool parentResized); Q_SIGNALS: void sourceComponentChanged(); +private Q_SLOTS: + void syncIndex(); + void syncModel(); + void syncModelData(); + private: QPointer m_sourceComponent; QPointer m_item; + QObject *m_propertiesTracker = nullptr; bool m_updatingSize = false; }; #endif diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp index 89a96c91..914ddf4e 100644 --- a/src/kirigamiplugin.cpp +++ b/src/kirigamiplugin.cpp @@ -1,176 +1,178 @@ /* * Copyright 2009 by Alan Alpert * Copyright 2010 by Ménard Alexis * Copyright 2010 by Marco Martin * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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. */ #include "kirigamiplugin.h" #include "enums.h" #include "desktopicon.h" #include "settings.h" #include "formlayoutattached.h" #include "mnemonicattached.h" #include "delegaterecycler.h" #include #include #include #include #include "libkirigami/platformtheme.h" static QString s_selectedStyle; //Q_INIT_RESOURCE(kirigami); #ifdef KIRIGAMI_BUILD_TYPE_STATIC #include #endif QUrl KirigamiPlugin::componentUrl(const QString &fileName) const { foreach (const QString &style, m_stylesFallbackChain) { const QString candidate = QStringLiteral("styles/") + style + QLatin1Char('/') + fileName; if (QFile::exists(resolveFilePath(candidate))) { #ifdef KIRIGAMI_BUILD_TYPE_STATIC return QUrl(QStringLiteral("qrc:/org/kde/kirigami/styles/") + style + QLatin1Char('/') + fileName); #else return QUrl(resolveFileUrl(candidate)); #endif } } #ifdef KIRIGAMI_BUILD_TYPE_STATIC return QUrl(QStringLiteral("qrc:/org/kde/kirigami/") + fileName); #else return QUrl(resolveFileUrl(fileName)); #endif } void KirigamiPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.kirigami")); const QString style = QQuickStyle::name(); #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) //org.kde.desktop.plasma is a couple of files that fall back to desktop by purpose if ((style.isEmpty() || style == QStringLiteral("org.kde.desktop.plasma")) && QFile::exists(resolveFilePath(QStringLiteral("/styles/org.kde.desktop")))) { m_stylesFallbackChain.prepend(QStringLiteral("org.kde.desktop")); } #elif defined(Q_OS_ANDROID) if (!m_stylesFallbackChain.contains(QStringLiteral("Material"))) { m_stylesFallbackChain.prepend(QStringLiteral("Material")); } #else // do we have an iOS specific style? if (!m_stylesFallbackChain.contains(QStringLiteral("Material"))) { m_stylesFallbackChain.prepend(QStringLiteral("Material")); } #endif if (!style.isEmpty() && QFile::exists(resolveFilePath(QStringLiteral("/styles/") + style)) && !m_stylesFallbackChain.contains(style)) { m_stylesFallbackChain.prepend(style); //if we have plasma deps installed, use them for extra integration if (style == QStringLiteral("org.kde.desktop") && QFile::exists(resolveFilePath(QStringLiteral("/styles/org.kde.desktop.plasma")))) { m_stylesFallbackChain.prepend("org.kde.desktop.plasma"); } } else { #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) m_stylesFallbackChain.prepend(QStringLiteral("org.kde.desktop")); #endif } //At this point the fallback chain will be selected->org.kde.desktop->Fallback s_selectedStyle = m_stylesFallbackChain.first(); qmlRegisterSingletonType(uri, 2, 0, "Settings", [](QQmlEngine*, QJSEngine*) -> QObject* { Settings *settings = new Settings; settings->setStyle(s_selectedStyle); return settings; } ); qmlRegisterUncreatableType(uri, 2, 0, "ApplicationHeaderStyle", "Cannot create objects of type ApplicationHeaderStyle"); //old legacy retrocompatible Theme qmlRegisterSingletonType(componentUrl(QStringLiteral("Theme.qml")), uri, 2, 0, "Theme"); qmlRegisterSingletonType(componentUrl(QStringLiteral("Units.qml")), uri, 2, 0, "Units"); qmlRegisterType(componentUrl(QStringLiteral("Action.qml")), uri, 2, 0, "Action"); qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationHeader.qml")), uri, 2, 0, "AbstractApplicationHeader"); qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationWindow.qml")), uri, 2, 0, "AbstractApplicationWindow"); qmlRegisterType(componentUrl(QStringLiteral("AbstractListItem.qml")), uri, 2, 0, "AbstractListItem"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationHeader.qml")), uri, 2, 0, "ApplicationHeader"); qmlRegisterType(componentUrl(QStringLiteral("ToolBarApplicationHeader.qml")), uri, 2, 0, "ToolBarApplicationHeader"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationWindow.qml")), uri, 2, 0, "ApplicationWindow"); qmlRegisterType(componentUrl(QStringLiteral("BasicListItem.qml")), uri, 2, 0, "BasicListItem"); qmlRegisterType(componentUrl(QStringLiteral("OverlayDrawer.qml")), uri, 2, 0, "OverlayDrawer"); qmlRegisterType(componentUrl(QStringLiteral("ContextDrawer.qml")), uri, 2, 0, "ContextDrawer"); qmlRegisterType(componentUrl(QStringLiteral("GlobalDrawer.qml")), uri, 2, 0, "GlobalDrawer"); qmlRegisterType(componentUrl(QStringLiteral("Heading.qml")), uri, 2, 0, "Heading"); qmlRegisterType(componentUrl(QStringLiteral("Separator.qml")), uri, 2, 0, "Separator"); qmlRegisterType(componentUrl(QStringLiteral("PageRow.qml")), uri, 2, 0, "PageRow"); //we want a different implementation with Plasma style if (s_selectedStyle == QStringLiteral("Plasma")) { qmlRegisterType(componentUrl(QStringLiteral("Icon.qml")), uri, 2, 0, "Icon"); } else { DesktopIcon::s_internalIconPath = resolveFilePath(QStringLiteral("icons")); qmlRegisterType(uri, 2, 0, "Icon"); } qmlRegisterType(componentUrl(QStringLiteral("Label.qml")), uri, 2, 0, "Label"); //TODO: uncomment for 2.3 release //qmlRegisterTypeNotAvailable(uri, 2, 3, "Label", "Label type not supported anymore, use QtQuick.Controls.Label 2.0 instead"); qmlRegisterType(componentUrl(QStringLiteral("OverlaySheet.qml")), uri, 2, 0, "OverlaySheet"); qmlRegisterType(componentUrl(QStringLiteral("Page.qml")), uri, 2, 0, "Page"); qmlRegisterType(componentUrl(QStringLiteral("ScrollablePage.qml")), uri, 2, 0, "ScrollablePage"); qmlRegisterType(componentUrl(QStringLiteral("SplitDrawer.qml")), uri, 2, 0, "SplitDrawer"); qmlRegisterType(componentUrl(QStringLiteral("SwipeListItem.qml")), uri, 2, 0, "SwipeListItem"); //2.1 qmlRegisterType(componentUrl(QStringLiteral("AbstractItemViewHeader.qml")), uri, 2, 1, "AbstractItemViewHeader"); qmlRegisterType(componentUrl(QStringLiteral("ItemViewHeader.qml")), uri, 2, 1, "ItemViewHeader"); qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationItem.qml")), uri, 2, 1, "AbstractApplicationItem"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationItem.qml")), uri, 2, 1, "ApplicationItem"); //2.2 //Theme changed from a singleton to an attached property qmlRegisterUncreatableType(uri, 2, 2, "Theme", "Cannot create objects of type Theme, use it as an attached poperty"); //2.3 qmlRegisterType(componentUrl(QStringLiteral("FormLayout.qml")), uri, 2, 3, "FormLayout"); qmlRegisterUncreatableType(uri, 2, 3, "FormData", "Cannot create objects of type FormData, use it as an attached poperty"); qmlRegisterUncreatableType(uri, 2, 3, "MnemonicData", "Cannot create objects of type MnemonicData, use it as an attached poperty"); //2.4 qmlRegisterType(componentUrl(QStringLiteral("AbstractCard.qml")), uri, 2, 4, "AbstractCard"); qmlRegisterType(componentUrl(QStringLiteral("Card.qml")), uri, 2, 4, "Card"); qmlRegisterType(componentUrl(QStringLiteral("CardsListView.qml")), uri, 2, 4, "CardsListView"); qmlRegisterType(componentUrl(QStringLiteral("CardsGridView.qml")), uri, 2, 4, "CardsGridView"); qmlRegisterType(componentUrl(QStringLiteral("CardsLayout.qml")), uri, 2, 4, "CardsLayout"); qmlRegisterType(componentUrl(QStringLiteral("InlineMessage.qml")), uri, 2, 4, "InlineMessage"); qmlRegisterUncreatableType(uri, 2, 4, "MessageType", "Cannot create objects of type MessageType"); - qmlRegisterType(uri, 2, 4, "DelegateRecycler"); + //2.5 + qmlRegisterType(componentUrl(QStringLiteral("ListItemDragHandle.qml")), uri, 2, 5, "ListItemDragHandle"); + qmlProtectModule(uri, 2); } #include "moc_kirigamiplugin.cpp" diff --git a/src/qmldir b/src/qmldir index 40204396..9f423011 100644 --- a/src/qmldir +++ b/src/qmldir @@ -1,44 +1,46 @@ module org.kde.kirigami classname KirigamiPlugin depends QtQuick.Controls 1.4 depends QtQuick.Controls.Private 1.0 depends QtQuick.Controls 2.0 depends QtGraphicalEffects 1.0 designersupported typeinfo plugins.qmltypes singleton Theme 2.2 Theme.qml OverlaySheet 2.0 OverlaySheet.qml ApplicationItem 2.1 ApplicationItem.qml AbstractApplicationWindow 2.0 AbstractApplicationWindow.qml FormLayout 2.3 FormLayout.qml GlobalDrawer 2.0 GlobalDrawer.qml BasicListItem 2.0 BasicListItem.qml AbstractItemViewHeader 2.1 AbstractItemViewHeader.qml Heading 2.0 Heading.qml Action 2.0 Action.qml Separator 2.0 Separator.qml ItemViewHeader 2.1 ItemViewHeader.qml SwipeListItem 2.0 SwipeListItem.qml AbstractListItem 2.0 AbstractListItem.qml Icon 2.0 Icon.qml Units 2.0 Units.qml Page 2.0 Page.qml Label 2.0 Label.qml AbstractApplicationItem 2.1 AbstractApplicationItem.qml ToolBarApplicationHeader 2.0 ToolBarApplicationHeader.qml ScrollablePage 2.0 ScrollablePage.qml ApplicationHeader 2.0 ApplicationHeader.qml AbstractApplicationHeader 2.0 AbstractApplicationHeader.qml ContextDrawer 2.0 ContextDrawer.qml ApplicationWindow 2.0 ApplicationWindow.qml PageRow 2.0 PageRow.qml AbstractCard 2.4 AbstractCard.qml Card 2.4 Card.qml CardsLayout 2.4 CardsLayout.qml CardsListView 2.4 CardsListView.qml CardsGridView 2.4 CardsGridView.qml InlineMessage 2.4 InlineMessage.qml +ListItemDragHandle 2.5 ListItemDragHandle.qml + diff --git a/src/styles/Material/SwipeListItem.qml b/src/styles/Material/SwipeListItem.qml index 784bfc71..5ea1765a 100644 --- a/src/styles/Material/SwipeListItem.qml +++ b/src/styles/Material/SwipeListItem.qml @@ -1,92 +1,91 @@ /* * Copyright 2010 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ import QtQuick 2.5 import org.kde.kirigami 2.4 import "../../private" import "../../templates" as T /** * An item delegate Intended to support extra actions obtainable * by uncovering them by dragging away the item with the handle * This acts as a container for normal list items. * Any subclass of AbstractListItem can be assigned as the contentItem property. * @code * ListView { * model: myModel * delegate: SwipeListItem { * Label { * text: model.text * } * actions: [ * Action { * iconName: "document-decrypt" * onTriggered: print("Action 1 clicked") * }, * Action { * iconName: model.action2Icon * onTriggered: //do something * } * ] * } * * } * @endcode * * @inherit QtQuick.Item */ T.SwipeListItem { id: root onPressedChanged: { if (pressed) { clickAnim.running = true } } background: DefaultListItemBackground { - clip: true //TODO: this will have to reuse QQC2.1 Ripple Rectangle { id: ripple anchors.centerIn: parent width: parent.width height: parent.width radius: width color: Qt.rgba(1,1,1,0.3) scale: 0 opacity: 1 ParallelAnimation { id: clickAnim ScaleAnimator { target: ripple from: 0 to: 1 duration: Units.longDuration } OpacityAnimator { target: ripple from: 0 to: 1 duration: Units.longDuration } } } } implicitHeight: contentItem.implicitHeight + Units.smallSpacing * 6 }