diff --git a/src/controls/templates/AbstractListItem.qml b/src/controls/templates/AbstractListItem.qml index 013fc7de..c9a93ee2 100644 --- a/src/controls/templates/AbstractListItem.qml +++ b/src/controls/templates/AbstractListItem.qml @@ -1,171 +1,171 @@ /* * 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.1 import QtQuick.Layouts 1.0 import org.kde.kirigami 2.4 //NOTE: This must stay at 2.0 until KF6 due to retrocompatibility of the "icon" property import QtQuick.Templates 2.0 as T2 import QtQuick.Templates 2.4 as QQC2 /** * An item delegate for the primitive ListView component. * * It's intended to make all listviews look coherent. * * @inherit QtQuick.Templates.ItemDelegate */ T2.ItemDelegate { id: listItem /** * supportsMouseEvents: bool * Holds if the item emits signals related to mouse interaction. *TODO: remove * The default value is false. */ property bool supportsMouseEvents: hoverEnabled /** * containsMouse: bool * True when the user hovers the mouse over the list item * NOTE: on mobile touch devices this will be true only when pressed is also true * TODO: remove? */ property alias containsMouse: listItem.hovered /** * alternatingBackground: bool * If true the background of the list items will be alternating between two * colors, helping readability with multiple column views. * Use it only when implementing a view which shows data visually in multiple columns * @ since 2.7 */ property bool alternatingBackground: false /** * 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 /** * textColor: color * Color for the text in the item * It is advised to leave the default value (Theme.textColor) * * 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 * It is advised to leave the default value (Theme.viewBackgroundColor) */ property color backgroundColor: "transparent" /** * alternateBackgroundColor: color * The background color to use if alternatingBackground is true. * It is advised to leave the default. * @since 2.7 */ property color alternateBackgroundColor: Theme.alternateBackgroundColor /** * 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 // Overrides action property of newer import versions which we can't use property QQC2.Action action activeFocusOnTab: ListView.view ? false : true text: action ? action.text : undefined checked: action ? action.checked : false checkable: action ? action.checkable : false onClicked: { if (ListView.view && typeof index !== "undefined") { ListView.view.currentIndex = index; } if (!action) { return; } action.trigger(); checked = Qt.binding(function() { return action.checked }); } //Theme.inherit: false //Theme.colorSet: Theme.View padding: Settings.tabletMode ? Units.largeSpacing : Units.smallSpacing - leftPadding: LayoutMirroring.enabled && internal.view && internal.view.T2.ScrollBar.vertical && internal.view.T2.ScrollBar.vertical.visible ? internal.view.T2.ScrollBar.vertical.width : padding*2 + leftPadding: padding*2 topPadding: padding - rightPadding: !LayoutMirroring.enabled && internal.view && internal.view.T2.ScrollBar.vertical && internal.view.T2.ScrollBar.vertical.visible ? internal.view.T2.ScrollBar.vertical.width : padding*2 + rightPadding: padding*2 bottomPadding: padding implicitWidth: contentItem ? contentItem.implicitWidth + leftPadding + rightPadding : Units.gridUnit * 12 implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding width: parent && parent.width > 0 ? parent.width : implicitWidth Layout.fillWidth: true opacity: enabled ? 1 : 0.6 height: visible ? implicitHeight : 0 hoverEnabled: true QtObject { id: internal property Flickable view: listItem.ListView.view || (listItem.parent ? listItem.parent.ListView.view : null) property bool indicateActiveFocus: listItem.pressed || Settings.tabletMode || listItem.activeFocus || (view ? view.activeFocus : false) } Accessible.role: Accessible.ListItem highlighted: focus && ListView.isCurrentItem && ListView.view && ListView.view.keyNavigationEnabled } diff --git a/src/controls/templates/SwipeListItem.qml b/src/controls/templates/SwipeListItem.qml index 662d8674..a5b667fa 100644 --- a/src/controls/templates/SwipeListItem.qml +++ b/src/controls/templates/SwipeListItem.qml @@ -1,499 +1,491 @@ /* * Copyright 2019 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.4 import QtQuick.Controls 2.4 as Controls import QtQuick.Templates 2.4 as T2 import org.kde.kirigami 2.11 as Kirigami import "../private" /** * 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 { * icon.name: "document-decrypt" * onTriggered: print("Action 1 clicked") * }, * Action { * icon.name: model.action2Icon * onTriggered: //do something * } * ] * } * * } * @endcode * */ T2.SwipeDelegate { id: listItem /** * 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 * KF6: remove */ property alias containsMouse: listItem.hovered /** * alternatingBackground: bool * If true the background of the list items will be alternating between two * colors, helping readability with multiple column views. * Use it only when implementing a view which shows data visually in multiple columns * @ since 2.7 */ property bool alternatingBackground: false /** * 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 /** * actionsVisible: bool * True if it's possible to see and access the item actions. * Actions should go completely out of the way for instance during * the editing of an item. * @since 2.5 */ readonly property bool actionsVisible: actionsLayout.hasVisibleActions /** * 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: Kirigami.Theme.textColor /** * backgroundColor: color * Color for the background of the item */ property color backgroundColor: Kirigami.Theme.backgroundColor /** * alternateBackgroundColor: color * The background color to use if alternatingBackground is true. * It is advised to leave the default. * @since 2.7 */ property color alternateBackgroundColor: Kirigami.Theme.alternateBackgroundColor /** * 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: Kirigami.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: Kirigami.Theme.highlightColor //TODO KF6 remove this super wrong thing default property alias _default: listItem.contentItem LayoutMirroring.childrenInherit: true hoverEnabled: true implicitWidth: contentItem ? contentItem.implicitWidth : Kirigami.Units.gridUnit * 12 width: parent ? parent.width : implicitWidth implicitHeight: Math.max(Kirigami.Units.gridUnit * 2, contentItem.implicitHeight) + topPadding + bottomPadding padding: Kirigami.Settings.tabletMode ? Kirigami.Units.largeSpacing : Kirigami.Units.smallSpacing leftPadding: padding * 2 - rightPadding: padding * 2 + (overlayLoader.visible ? overlayLoader.width : 0) + internal.calculateMargin() + rightPadding: padding * 2 + (overlayLoader.visible ? overlayLoader.width : 0) + Kirigami.Units.smallSpacing topPadding: padding bottomPadding: padding contentItem: Item {} QtObject { id: internal property Flickable view: listItem.ListView.view || (listItem.parent ? (listItem.parent.ListView.view || listItem.parent) : null) readonly property QtObject swipeFilterItem: (view && view.parent && view.parent.parent && view.parent.parent._swipeFilter) ? view.parent.parent._swipeFilter : null readonly property bool edgeEnabled: swipeFilterItem ? swipeFilterItem.currentItem === listItem || swipeFilterItem.currentItem === listItem.parent : false property bool indicateActiveFocus: listItem.pressed || Kirigami.Settings.tabletMode || listItem.activeFocus || (view ? view.activeFocus : false) // Search for scrollbar of the view or of the ScrollView property T2.ScrollBar verticalScrollBar: { if (!view) { return null; } return view.T2.ScrollBar.vertical || view.parent.T2.ScrollBar.vertical; } //install the SwipeItemEventFilter onViewChanged: { if (!Kirigami.Settings.tabletMode) { return; } if (internal.view && Kirigami.Settings.tabletMode && !internal.view.parent.parent._swipeFilter) { var component = Qt.createComponent(Qt.resolvedUrl("../private/SwipeItemEventFilter.qml")); internal.view.parent.parent._swipeFilter = component.createObject(internal.view.parent.parent); } } - - function calculateMargin() { - if (verticalScrollBar && verticalScrollBar.visible) { - return verticalScrollBar.width - } else { - return Kirigami.Units.smallSpacing - } - } } Connections { target: Kirigami.Settings onTabletModeChanged: { if (Kirigami.Settings.tabletMode) { 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(); slideAnim.to = 0; slideAnim.restart(); } } } } //BEGIN Items Loader { id: overlayLoader anchors { right: contentItem ? contentItem.right : undefined top: parent.top bottom: parent.bottom - rightMargin: -listItem.rightPadding + internal.calculateMargin() + rightMargin: -listItem.rightPadding + Kirigami.Units.smallSpacing } parent: listItem z: contentItem ? contentItem.z + 1 : 0 width: item ? item.implicitWidth : actionsLayout.implicitWidth active: Kirigami.Settings.tabletMode visible: listItem.actionsVisible && opacity > 0 sourceComponent: handleComponent opacity: Kirigami.Settings.tabletMode || listItem.hovered || !listItem.supportsMouseEvents ? 1 : 0 Behavior on opacity { OpacityAnimator { id: opacityAnim duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } } Component { id: handleComponent MouseArea { id: dragButton anchors { right: parent.right } implicitWidth: Kirigami.Units.iconSizes.smallMedium preventStealing: true readonly property real openPosition: (listItem.width - width - listItem.leftPadding * 2)/listItem.width property real startX: 0 property real lastPosition: 0 property bool openIntention onPressed: startX = mapToItem(listItem, 0, 0).x; onClicked: { if (Math.abs(mapToItem(listItem, 0, 0).x - startX) > Qt.styleHints.startDragDistance) { return; } if (listItem.LayoutMirroring.enabled) { if (listItem.swipe.position < 0.5) { slideAnim.to = openPosition } else { slideAnim.to = 0 } } else { if (listItem.swipe.position > -0.5) { slideAnim.to = -openPosition } else { slideAnim.to = 0 } } slideAnim.restart(); } onPositionChanged: { var pos = mapToItem(listItem, mouse.x, mouse.y); if (listItem.LayoutMirroring.enabled) { listItem.swipe.position = Math.max(0, Math.min(openPosition, (pos.x / listItem.width))); openIntention = listItem.swipe.position > lastPosition; } else { listItem.swipe.position = Math.min(0, Math.max(-openPosition, (pos.x / (listItem.width -listItem.rightPadding) - 1))); openIntention = listItem.swipe.position < lastPosition; } lastPosition = listItem.swipe.position; } onReleased: { if (listItem.LayoutMirroring.enabled) { if (openIntention) { slideAnim.to = openPosition } else { slideAnim.to = 0 } } else { if (openIntention) { slideAnim.to = -openPosition } else { slideAnim.to = 0 } } slideAnim.restart(); } Kirigami.Icon { id: handleIcon anchors.fill: parent selected: listItem.checked || (listItem.pressed && !listItem.checked && !listItem.sectionDelegate) source: (LayoutMirroring.enabled ? (listItem.background.x < listItem.background.width/2 ? "overflow-menu-right" : "overflow-menu-left") : (listItem.background.x < -listItem.background.width/2 ? "overflow-menu-right" : "overflow-menu-left")) } Connections { id: swipeFilterConnection target: internal.edgeEnabled ? internal.swipeFilterItem : null onPeekChanged: { if (!listItem.actionsVisible) { return; } if (listItem.LayoutMirroring.enabled) { listItem.swipe.position = Math.max(0, Math.min(dragButton.openPosition, internal.swipeFilterItem.peek)); dragButton.openIntention = listItem.swipe.position > dragButton.lastPosition; } else { listItem.swipe.position = Math.min(0, Math.max(-dragButton.openPosition, -internal.swipeFilterItem.peek)); dragButton.openIntention = listItem.swipe.position < dragButton.lastPosition; } dragButton.lastPosition = listItem.swipe.position; } onPressed: { if (internal.edgeEnabled) { dragButton.onPressed(mouse); } } onClicked: { if (Math.abs(listItem.background.x) < Units.gridUnit && internal.edgeEnabled) { dragButton.clicked(mouse); } } onReleased: { if (internal.edgeEnabled) { dragButton.released(mouse); } } onCurrentItemChanged: { if (!internal.edgeEnabled) { slideAnim.to = 0; slideAnim.restart(); } } } } } //TODO: expose in API? Component { id: actionsBackgroundDelegate MouseArea { anchors.fill: parent // Controls.SwipeDelegate.onPressedChanged is broken with touch onClicked: { slideAnim.to = 0; slideAnim.restart(); } Rectangle { anchors.fill: parent color: parent.pressed ? Qt.darker(Kirigami.Theme.backgroundColor, 1.1) : Qt.darker(Kirigami.Theme.backgroundColor, 1.05) } visible: listItem.swipe.position != 0 EdgeShadow { edge: Qt.TopEdge visible: background.x != 0 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) visible: background.x != 0 anchors { top: parent.top bottom: parent.bottom } } } } RowLayout { id: actionsLayout anchors { right: parent.right top: parent.top bottom: parent.bottom - rightMargin: Kirigami.Settings.tabletMode ? internal.calculateMargin() : 0 + rightMargin: Kirigami.Units.smallSpacing } visible: parent != listItem parent: Kirigami.Settings.tabletMode ? listItem.swipe.leftItem || listItem.swipe.rightItem || listItem : overlayLoader property bool hasVisibleActions: false function updateVisibleActions(definitelyVisible = false) { if (definitelyVisible) { hasVisibleActions = true; } else { var actionCount = listItem.actions.length; for (var i = 0; i < actionCount; i++) { // Assuming that visible is only false if it is explicitly false, and not just falsy if (listItem.actions[i].visible === false) { continue; } hasVisibleActions = true; break; } } } 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: Controls.ToolButton { icon.name: modelData.iconName !== "" ? modelData.iconName : "" icon.source: modelData.iconSource !== "" ? modelData.iconSource : "" enabled: (modelData && modelData.enabled !== undefined) ? modelData.enabled : true; visible: (modelData && modelData.visible !== undefined) ? modelData.visible : true; onVisibleChanged: actionsLayout.updateVisibleActions(visible); Component.onCompleted: actionsLayout.updateVisibleActions(visible); Component.onDestruction: actionsLayout.updateVisibleActions(visible); Controls.ToolTip.delay: Kirigami.Units.toolTipDelay Controls.ToolTip.timeout: 5000 Controls.ToolTip.visible: listItem.visible && (Kirigami.Settings.tabletMode ? pressed : hovered) && Controls.ToolTip.text.length > 0 Controls.ToolTip.text: modelData.tooltip || modelData.text onClicked: { if (modelData && modelData.trigger !== undefined) { modelData.trigger(); } slideAnim.to = 0; slideAnim.restart(); } } } } background: DefaultListItemBackground {} swipe { enabled: false right: listItem.LayoutMirroring.enabled || !Kirigami.Settings.tabletMode ? null : actionsBackgroundDelegate left: listItem.LayoutMirroring.enabled && Kirigami.Settings.tabletMode ? actionsBackgroundDelegate : null } NumberAnimation { id: slideAnim duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad target: listItem.swipe property: "position" from: listItem.swipe.position } //END Items } diff --git a/src/controls/templates/private/ScrollView.qml b/src/controls/templates/private/ScrollView.qml index 77314055..dcc78b94 100644 --- a/src/controls/templates/private/ScrollView.qml +++ b/src/controls/templates/private/ScrollView.qml @@ -1,143 +1,144 @@ /* * 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.7 import QtQuick.Controls 2.0 import org.kde.kirigami 2.9 as Kirigami MouseArea { id: root default property Item contentItem property Flickable flickableItem clip: true //TODO: horizontalScrollBarPolicy is completely noop just for compatibility right now property int horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff property int verticalScrollBarPolicy: Qt.ScrollBarAsNeeded readonly property Item verticalScrollBar: flickableItem.ScrollBar.vertical ? flickableItem.ScrollBar.vertical : null onVerticalScrollBarPolicyChanged: { if (flickableItem.ScrollBar.vertical) { flickableItem.ScrollBar.vertical.visible = verticalScrollBarPolicy != Qt.ScrollBarAlwaysOff; } scrollBarCreationTimer.restart(); } onHorizontalScrollBarPolicyChanged: { if (flickableItem.ScrollBar.horizontal) { flickableItem.ScrollBar.horizontal.visible = horizontalScrollBarPolicy != Qt.ScrollBarAlwaysOff; } scrollBarCreationTimer.restart(); } onContentItemChanged: { if (contentItem.hasOwnProperty("contentY")) { flickableItem = contentItem; if (typeof(flickableItem.keyNavigationEnabled) != "undefined") { flickableItem.keyNavigationEnabled = true; flickableItem.keyNavigationWraps = false; } contentItem.parent = flickableParent; } else { flickableItem = flickableComponent.createObject(flickableParent); contentItem.parent = flickableItem.contentItem; } flickableItem.interactive = true; flickableItem.anchors.fill = flickableParent; scrollBarCreationTimer.restart(); } Timer { id: scrollBarCreationTimer interval: 0 onTriggered: { //create or destroy the vertical scrollbar if ((!flickableItem.ScrollBar.vertical) && verticalScrollBarPolicy != Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.vertical = verticalScrollComponent.createObject(root); } else if (flickableItem.ScrollBar.vertical && verticalScrollBarPolicy == Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.vertical.destroy(); } //create or destroy the horizontal scrollbar if ((!flickableItem.ScrollBar.horizontal) && horizontalScrollBarPolicy != Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.horizontal = horizontalScrollComponent.createObject(root); } else if (flickableItem.ScrollBar.horizontal && horizontalScrollBarPolicy == Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.horizontal.destroy(); } } } Kirigami.WheelHandler { id: wheelHandler target: root.flickableItem } Item { id: flickableParent anchors { fill: parent + rightMargin: !Kirigami.Settings.tabletMode && flickableItem.ScrollBar.vertical && flickableItem.ScrollBar.vertical.visible ? flickableItem.ScrollBar.vertical.width : 0 } } Component { id: flickableComponent Flickable { anchors { fill: parent } contentWidth: root.contentItem ? root.contentItem.width : 0 contentHeight: root.contentItem ? root.contentItem.height : 0 } } Component { id: verticalScrollComponent ScrollBar { z: flickableParent.z + 1 visible: root.contentItem.visible && size < 1 interactive: !Kirigami.Settings.tabletMode //NOTE: use this instead of anchors as crashes on some Qt 5.8 checkouts height: parent.height - anchors.topMargin anchors { topMargin: parent.flickableItem.headerItem ? parent.flickableItem.headerItem.height : 0 right: parent.right top: parent.top } } } Component { id: horizontalScrollComponent ScrollBar { z: flickableParent.z + 1 visible: root.contentItem.visible && size < 1 interactive: !Kirigami.Settings.tabletMode //NOTE: use this instead of anchors as crashes on some Qt 5.8 checkouts height: parent.height - anchors.topMargin anchors { left: parent.left right: parent.right bottom: parent.bottom } } } }