diff --git a/src/controls/SwipeListItem2.qml b/src/controls/SwipeListItem2.qml new file mode 100644 index 00000000..cee92eab --- /dev/null +++ b/src/controls/SwipeListItem2.qml @@ -0,0 +1,276 @@ +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" + +T2.SwipeDelegate { + id: root + + /** + * supportsMouseEvents: bool + * Holds if the item emits signals related to mouse interaction. + *TODO: remove + * The default value is false. + */ + property alias supportsMouseEvents: root.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: root.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: swipe.position != 0 + + /** + * 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 + + + LayoutMirroring.childrenInherit: true + + Loader { + id: overlayLoader + parent: root + z: contentItem ? contentItem.z + 1 : 0 + sourceComponent: handleComponent + anchors { + right: contentItem ? contentItem.right : undefined + top: parent.top + bottom: parent.bottom + rightMargin: -root.leftPadding + } + } + Component { + id: handleComponent + + MouseArea { + id: dragButton + anchors { + right: parent.right + } + implicitWidth: Kirigami.Units.iconSizes.smallMedium + + preventStealing: true + property real openPosition: (root.width - width - root.leftPadding - root.rightPadding)/root.width + onClicked: { + if (root.LayoutMirroring.enabled) { + if (root.swipe.position < 0.5) { + slideAnim.to = openPosition + } else { + slideAnim.to = 0 + } + } else { + if (root.swipe.position > -0.5) { + slideAnim.to = -openPosition + } else { + slideAnim.to = 0 + } + } + slideAnim.restart(); + } + onPositionChanged: { + var pos = mapToItem(root, mouse.x, mouse.y); + + if (root.LayoutMirroring.enabled) { + root.swipe.position = Math.max(0, Math.min(openPosition, (pos.x / root.width))) + } else { + root.swipe.position = Math.min(0, Math.max(-openPosition, (pos.x / root.width - 1))) + } + } + onReleased: { + if (root.LayoutMirroring.enabled) { + if (root.swipe.position > 0.5) { + slideAnim.to = openPosition + } else { + slideAnim.to = 0 + } + } else { + if (root.swipe.position < -0.5) { + slideAnim.to = -openPosition + } else { + slideAnim.to = 0 + } + } + slideAnim.restart(); + } + + Kirigami.Icon { + id: handleIcon + anchors.fill: parent + selected: root.checked || (root.pressed && !root.checked && !root.sectionDelegate) + source: (LayoutMirroring.enabled ? (root.background.x < root.background.width/2 ? "overflow-menu-right" : "overflow-menu-left") : (root.background.x < -root.background.width/2 ? "overflow-menu-right" : "overflow-menu-left")) + } + } + } + + property Component behindDelegate: Rectangle { + anchors.fill: parent + + color: Controls.SwipeDelegate.pressed ? Qt.darker(Kirigami.Theme.backgroundColor, 1.1) : Qt.darker(Kirigami.Theme.backgroundColor, 1.05) + + visible: root.swipe.position != 0 + Controls.SwipeDelegate.onPressedChanged: { + slideAnim.to = 0; + slideAnim.restart(); + } + + 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 ? root.background.x - width : (root.background.x + root.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 + } + + Repeater { + model: { + if (root.actions.length === 0) { + return null; + } else { + return root.actions[0].text !== undefined && + root.actions[0].trigger !== undefined ? + root.actions : + root.actions[0]; + } + } + delegate: Controls.ToolButton { + anchors.verticalCenter: parent.verticalCenter + 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: Units.toolTipDelay + Controls.ToolTip.timeout: 5000 + Controls.ToolTip.visible: listItem.visible && (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: Rectangle { + color: Controls.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato" + radius: 10 + } + swipe { + enabled: false + right: root.LayoutMirroring.enabled ? null : root.behindDelegate + left: root.LayoutMirroring.enabled ? root.behindDelegate : null + } + NumberAnimation { + id: slideAnim + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + target: root.swipe + property: "position" + from: root.swipe.position + } +} + diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp index 4dffe9b1..58c1425a 100644 --- a/src/kirigamiplugin.cpp +++ b/src/kirigamiplugin.cpp @@ -1,221 +1,223 @@ /* * 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 "columnview.h" #include "enums.h" #include "icon.h" #include "settings.h" #include "formlayoutattached.h" #include "mnemonicattached.h" #include "delegaterecycler.h" #include "pagepool.h" #include "scenepositionattached.h" #include "wheelhandler.h" #include #include #include #include #include #include #include "libkirigami/platformtheme.h" static QString s_selectedStyle; //Q_INIT_RESOURCE(kirigami); #ifdef KIRIGAMI_BUILD_TYPE_STATIC #include #endif class CopyHelperPrivate : public QObject { Q_OBJECT public: Q_INVOKABLE static void copyTextToClipboard(const QString& text) { qGuiApp->clipboard()->setText(text); } }; QUrl KirigamiPlugin::componentUrl(const QString &fileName) const { for (const QString &style : qAsConst(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(QLatin1String(uri) == QLatin1String("org.kde.kirigami")); const QString style = QQuickStyle::name(); if (QIcon::themeName().isEmpty() && !qEnvironmentVariableIsSet("XDG_CURRENT_DESKTOP")) { QIcon::setThemeSearchPaths({resolveFilePath(QStringLiteral(".")), QStringLiteral(":/icons")}); QIcon::setThemeName(QStringLiteral("breeze-internal")); } #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(QLatin1String("Material"))) { m_stylesFallbackChain.prepend(QStringLiteral("Material")); } #else // do we have an iOS specific style? if (!m_stylesFallbackChain.contains(QLatin1String("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(QStringLiteral("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 *e, QJSEngine*) -> QObject* { Settings *settings = Settings::self(); //singleton managed internally, qml should never delete it e->setObjectOwnership(settings, QQmlEngine::CppOwnership); settings->setStyle(s_selectedStyle); return settings; } ); qmlRegisterUncreatableType(uri, 2, 0, "ApplicationHeaderStyle", QStringLiteral("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"); 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", QStringLiteral("Cannot create objects of type Theme, use it as an attached property")); //2.3 qmlRegisterType(componentUrl(QStringLiteral("FormLayout.qml")), uri, 2, 3, "FormLayout"); qmlRegisterUncreatableType(uri, 2, 3, "FormData", QStringLiteral("Cannot create objects of type FormData, use it as an attached property")); qmlRegisterUncreatableType(uri, 2, 3, "MnemonicData", QStringLiteral("Cannot create objects of type MnemonicData, use it as an attached property")); //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", QStringLiteral("Cannot create objects of type MessageType")); qmlRegisterType(uri, 2, 4, "DelegateRecycler"); //2.5 qmlRegisterType(componentUrl(QStringLiteral("ListItemDragHandle.qml")), uri, 2, 5, "ListItemDragHandle"); qmlRegisterType(componentUrl(QStringLiteral("ActionToolBar.qml")), uri, 2, 5, "ActionToolBar"); qmlRegisterUncreatableType(uri, 2, 5, "ScenePosition", QStringLiteral("Cannot create objects of type ScenePosition, use it as an attached property")); //2.6 qmlRegisterType(componentUrl(QStringLiteral("AboutPage.qml")), uri, 2, 6, "AboutPage"); qmlRegisterType(componentUrl(QStringLiteral("LinkButton.qml")), uri, 2, 6, "LinkButton"); qmlRegisterType(componentUrl(QStringLiteral("UrlButton.qml")), uri, 2, 6, "UrlButton"); qmlRegisterSingletonType("org.kde.kirigami.private", 2, 6, "CopyHelperPrivate", [] (QQmlEngine*, QJSEngine*) -> QObject* { return new CopyHelperPrivate; }); //2.7 qmlRegisterType(uri, 2, 7, "ColumnView"); qmlRegisterType(componentUrl(QStringLiteral("ActionTextField.qml")), uri, 2, 7, "ActionTextField"); //2.8 qmlRegisterType(componentUrl(QStringLiteral("SearchField.qml")), uri, 2, 8, "SearchField"); qmlRegisterType(componentUrl(QStringLiteral("PasswordField.qml")), uri, 2, 8, "PasswordField"); //2.9 qmlRegisterType(uri, 2, 9, "WheelHandler"); qmlRegisterUncreatableType(uri, 2, 9, "WheelEvent", QStringLiteral("Cannot create objects of type WheelEvent.")); //2.10 qmlRegisterType(componentUrl(QStringLiteral("ListSectionHeader.qml")), uri, 2, 10, "ListSectionHeader"); // 2.11 qmlRegisterType(uri, 2, 11, "PagePool"); qmlRegisterType(componentUrl(QStringLiteral("PagePoolAction.qml")), uri, 2, 11, "PagePoolAction"); + //TODO: remove + qmlRegisterType(componentUrl(QStringLiteral("SwipeListItem2.qml")), uri, 2, 11, "SwipeListItem2"); qmlProtectModule(uri, 2); } #include "kirigamiplugin.moc"