diff --git a/examples/swipenavigator/main.qml b/examples/swipenavigator/main.qml new file mode 100644 index 00000000..59db2315 --- /dev/null +++ b/examples/swipenavigator/main.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 +import org.kde.kirigami 2.13 as Kirigami + +Kirigami.ApplicationWindow { + Kirigami.SwipeNavigator { + anchors.fill: parent + Kirigami.Page { + icon.name: "globe" + title: "World Clocks" + } + Kirigami.Page { + icon.name: "clock" + title: "Alarms" + needsAttention: true + } + Kirigami.Page { + icon.name: "clock" + title: "Stopwatch" + } + Kirigami.Page { + icon.name: "clock" + title: "Timers" + progress: 0.5 + } + } +} diff --git a/kirigami.qrc b/kirigami.qrc index 7437e366..dd41a686 100644 --- a/kirigami.qrc +++ b/kirigami.qrc @@ -1,97 +1,104 @@ 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/ActionToolBar.qml src/controls/templates/InlineMessage.qml src/controls/InlineMessage.qml src/controls/ToolBarApplicationHeader.qml src/controls/ActionTextField.qml src/controls/SearchField.qml src/controls/PasswordField.qml src/controls/private/GlobalDrawerActionItem.qml src/controls/private/ContextDrawerActionItem.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/DefaultCardBackground.qml src/controls/private/BannerImage.qml src/controls/private/EdgeShadow.qml src/controls/private/ActionToolBarLayoutDetails.qml src/controls/private/globaltoolbar/AbstractPageHeader.qml src/controls/private/globaltoolbar/PageRowGlobalToolBarStyleGroup.qml src/controls/private/globaltoolbar/PageRowGlobalToolBarUI.qml src/controls/private/PrivateActionToolButton.qml src/controls/private/globaltoolbar/TitlesPageHeader.qml src/controls/private/globaltoolbar/ToolBarPageHeader.qml src/controls/private/globaltoolbar/BreadcrumbControl.qml src/controls/private/globaltoolbar/TabBarControl.qml src/controls/private/ActionsMenu.qml src/controls/private/ActionMenuItem.qml src/controls/private/CardsGridViewPrivate.qml src/controls/private/CardsListViewPrivate.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/private/BorderPropertiesGroup.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/AboutPage.qml src/controls/UrlButton.qml src/controls/LinkButton.qml src/controls/Label.qml src/controls/BasicListItem.qml src/controls/AbstractApplicationHeader.qml src/controls/FormLayout.qml src/controls/ListItemDragHandle.qml src/controls/ShadowedImage.qml src/controls/Avatar.qml + src/controls/swipenavigator/PrivateSwipeHighlight.qml + src/controls/swipenavigator/PrivateSwipeProgress.qml + src/controls/swipenavigator/PrivateSwipeStack.qml + src/controls/swipenavigator/PrivateSwipeTab.qml + src/controls/swipenavigator/PrivateSwipeTabBar.qml + src/controls/swipenavigator/PrivateSwipeTabBarLoader.qml + src/controls/swipenavigator/SwipeNavigator.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/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/PlaceholderMessage.qml diff --git a/src/controls/Page.qml b/src/controls/Page.qml index cd6ad6e5..a78518ff 100644 --- a/src/controls/Page.qml +++ b/src/controls/Page.qml @@ -1,405 +1,427 @@ /* * SPDX-FileCopyrightText: 2015 Marco Martin * * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.10 as Kirigami import "private" import QtQuick.Templates 2.1 as T2 import QtQuick.Controls 2.1 as QQC2 /** * Page is a container for all the app pages: everything pushed to the * ApplicationWindow stackView should be a Page instance (or a subclass, * such as ScrollablePage) * @see ScrollablePage * @inherit QtQuick.Templates.Page */ QQC2.Page { id: root /** * leftPadding: int * default contents padding at left */ leftPadding: Kirigami.Units.gridUnit /** * topPadding: int * default contents padding at top */ topPadding: Kirigami.Units.gridUnit /** * rightPadding: int * default contents padding at right */ rightPadding: Kirigami.Units.gridUnit /** * bottomPadding: int * default contents padding at bottom */ bottomPadding: actionButtons.item ? actionButtons.height : Kirigami.Units.gridUnit /** * flickable: Flickable * if the central element of the page is a Flickable * (ListView and Gridview as well) you can set it there. * normally, you wouldn't need to do that, but just use the * ScrollablePage element instead * @see ScrollablePage * Use this if your flickable has some non standard properties, such as not covering the whole Page */ property Flickable flickable /** * actions.contextualActions: list * Defines the contextual actions for the page: * an easy way to assign actions in the right sliding panel * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } * [...] * } * @endcode * * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.Page { * [...] * actions.contextualActions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * [...] * } * @endcode */ //TODO: remove property alias contextualActions: actionsGroup.contextualActions /** * actions.main: Action * An optional single action for the action button. * it can be a Kirigami.Action or a QAction * * Example usage: * * @code * import org.kde.kirigami 2.4 as Kirigami * Kirigami.Page { * actions.main: Kirigami.Action { * iconName: "edit" * onTriggered: { * // do stuff * } * } * } * @endcode */ //TODO: remove property alias mainAction: actionsGroup.main /** * actions.left: Action * An optional extra action at the left of the main action button. * it can be a Kirigami.Action or a QAction * * Example usage: * * @code * import org.kde.kirigami 2.4 as Kirigami * Kirigami.Page { * actions.left: Kirigami.Action { * iconName: "edit" * onTriggered: { * // do stuff * } * } * } * @endcode */ //TODO: remove property alias leftAction: actionsGroup.left /** * actions.right: Action * An optional extra action at the right of the main action button. * it can be a Kirigami.Action or a QAction * * Example usage: * * @code * import org.kde.kirigami 2.4 as Kirigami * Kirigami.Page { * actions.right: Kirigami.Action { * iconName: "edit" * onTriggered: { * // do stuff * } * } * } * @endcode */ //TODO: remove property alias rightAction: actionsGroup.right /** * Actions properties are grouped. * * @code * import org.kde.kirigami 2.4 as Kirigami * Kirigami.Page { * actions { * main: Kirigami.Action {...} * left: Kirigami.Action {...} * right: Kirigami.Action {...} * contextualActions: [ * Kirigami.Action {...}, * Kirigami.Action {...} * ] * } * } * @endcode */ readonly property alias actions: actionsGroup /** * contextualActionsAboutToShow: signal * Emitted when a visualization for the actions is about to be shown, * such as the toolbar menu or the contextDrawer * @since 2.7 */ signal contextualActionsAboutToShow /** * isCurrentPage: bool * * Specifies if it's the currently selected page in the window's pages row. * * @since 2.1 */ readonly property bool isCurrentPage: Kirigami.ColumnView.view ? Kirigami.ColumnView.index == Kirigami.ColumnView.view.currentIndex : true /** * overlay: Item * an item which stays on top of every other item in the page, * if you want to make sure some elements are completely in a * layer on top of the whole content, parent items to this one. * It's a "local" version of ApplicationWindow's overlay * @since 2.5 */ readonly property alias overlay: overlayItem + /** + * icon: variant + * + * The icon that represents this page. + */ + property ActionIconGroup icon: ActionIconGroup {} + + /** + * needsAttention: bool + * + * Whether this page needs user attention. + */ + property bool needsAttention + + /** + * progress: real + * + * Progress of a task this page is doing. Set to undefined to indicate + * that there are no ongoing tasks. + */ + property var progress: undefined + /** * titleDelegate: Component * The delegate which will be used to draw the page title. It can be customized to put any kind of Item in there. * @since 2.7 */ property Component titleDelegate: Kirigami.Heading { id: title level: 1 Layout.fillWidth: true Layout.maximumWidth: implicitWidth + 1 // The +1 is to make sure we do not trigger eliding at max width Layout.minimumWidth: 0 opacity: root.isCurrentPage ? 1 : 0.4 maximumLineCount: 1 elide: Text.ElideRight text: root.title } /** * emitted When the application requests a Back action * For instance a global "back" shortcut or the Android * Back button has been pressed. * The page can manage the back event by itself, * and if it set event.accepted = true, it will stop the main * application to manage the back event. */ signal backRequested(var event); // Look for sheets and cose them //FIXME: port Sheets to Popup? onBackRequested: { for(var i in root.resources) { var item = root.resources[i]; if (item.hasOwnProperty("close") && item.hasOwnProperty("sheetOpen") && item.sheetOpen) { item.close() event.accepted = true; return; } } } /** * globalToolBarItem: Item * The item used as global toolbar for the page * present only if we are in a PageRow as a page or as a layer, * and the style is either Titles or ToolBar * @since 2.5 */ readonly property Item globalToolBarItem: globalToolBar.item /** * The style for the automatically generated global toolbar: by default the Page toolbar is the one set globally in the PageRow in its globalToolBar.style property. * A single page can override the application toolbar style for itself. * It is discouraged to use this, except very specific exceptions, like a chat * application which can't have controls on the bottom except the text field. */ property int globalToolBarStyle: { if (globalToolBar.row && !globalToolBar.stack) { return globalToolBar.row.globalToolBar.actualStyle; } else if (globalToolBar.stack) { return Kirigami.Settings.isMobile ? Kirigami.ApplicationHeaderStyle.Titles : Kirigami.ApplicationHeaderStyle.ToolBar; } else { return Kirigami.ApplicationHeaderStyle.None; } } //NOTE: contentItem will be created if not existing (and contentChildren of Page would become its children) This with anchors enforces the geometry we want, where globalToolBar is a super-header, on top of header contentItem: Item { anchors { top: root.header ? root.header.bottom : (globalToolBar.visible ? globalToolBar.bottom : parent.top) topMargin: root.topPadding + root.spacing bottom: root.footer ? root.footer.top : parent.bottom bottomMargin: root.bottomPadding + root.spacing } } background: Rectangle { color: Kirigami.Theme.backgroundColor } implicitHeight: (header ? header.implicitHeight : 0) + (footer ? footer.implicitHeight : 0) + contentItem.implicitHeight + topPadding + bottomPadding implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding //FIXME: on material the shadow would bleed over clip: root.header != null; onHeaderChanged: { if (header) { header.anchors.top = Qt.binding(function() {return globalToolBar.visible ? globalToolBar.bottom : root.top}); } } Component.onCompleted: { headerChanged(); parentChanged(root.parent); } onParentChanged: { if (!parent) { return; } globalToolBar.stack = null; globalToolBar.row = null; if (root.Kirigami.ColumnView.view) { globalToolBar.row = root.Kirigami.ColumnView.view.__pageRow; } if (root.T2.StackView.view) { globalToolBar.stack = root.T2.StackView.view; globalToolBar.row = root.T2.StackView.view ? root.T2.StackView.view.parent : null; } if (globalToolBar.row) { root.globalToolBarStyleChanged.connect(globalToolBar.syncSource); globalToolBar.syncSource(); } } //in data in order for them to not be considered for contentItem, contentChildren, contentData data: [ PageActionPropertyGroup { id: actionsGroup }, Item { id: overlayItem parent: root z: 9997 anchors { fill: parent topMargin: globalToolBar.height } }, //global top toolbar if we are in a PageRow (in the row or as a layer) Loader { id: globalToolBar z: 9999 height: item ? item.implicitHeight : 0 anchors { left: parent.left right: parent.right top: parent.top } property Kirigami.PageRow row property T2.StackView stack visible: active active: (row || stack) && (root.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.ToolBar || root.globalToolBarStyle == Kirigami.ApplicationHeaderStyle.Titles) function syncSource() { if (row && active) { setSource(Qt.resolvedUrl(root.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.ToolBar ? "private/globaltoolbar/ToolBarPageHeader.qml" : "private/globaltoolbar/TitlesPageHeader.qml"), //TODO: find container reliably, remove assumption {"pageRow": Qt.binding(function() {return row}), "page": root, "current": Qt.binding(function() {return stack || row.currentIndex === root.Kirigami.ColumnView.level})}); } } }, //bottom action buttons Loader { id: actionButtons z: 9999 parent: root anchors { left: parent.left right: parent.right bottom: parent.bottom } //if the page doesn't inherit, assume it has custom colors we want to use them here too Kirigami.Theme.inherit: !root.Kirigami.Theme.inherit Kirigami.Theme.colorSet: Kirigami.Theme.Button //It should be T2.Page, Qt 5.7 doesn't like it property Item page: root height: item ? item.implicitHeight : 0 active: typeof applicationWindow !== "undefined" && (!globalToolBar.row || root.globalToolBarStyle !== Kirigami.ApplicationHeaderStyle.ToolBar) && root.actions && (root.actions.main || root.actions.left || root.actions.right || root.actions.contextualActions.length) && //Legacy (typeof applicationWindow === "undefined" || (!applicationWindow().header || applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") === -1) && (!applicationWindow().footer || applicationWindow().footer.toString().indexOf("ToolBarApplicationHeader") === -1)) source: Qt.resolvedUrl("./private/ActionButton.qml") } ] Layout.fillWidth: true } diff --git a/src/controls/swipenavigator/PrivateSwipeHighlight.qml b/src/controls/swipenavigator/PrivateSwipeHighlight.qml new file mode 100644 index 00000000..087ae4d0 --- /dev/null +++ b/src/controls/swipenavigator/PrivateSwipeHighlight.qml @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2020 Carson Black + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import org.kde.kirigami 2.12 as Kirigami + +Rectangle { + Accessible.ignored: true + + anchors { + bottom: Kirigami.Settings.isMobile ? undefined : parent.bottom + top: Kirigami.Settings.isMobile ? parent.top : undefined + left: parent.left + right: parent.right + } + + color: { + if (state == "highlighted") { + return Kirigami.Theme.activeTextColor + } else if (state == "requestingAttention") { + return Kirigami.Theme.negativeTextColor + } + return "transparent" + } + + // Unlike most things, we don't want to scale with the em grid, so we don't use a Unit. + height: 2 +} \ No newline at end of file diff --git a/src/controls/swipenavigator/PrivateSwipeProgress.qml b/src/controls/swipenavigator/PrivateSwipeProgress.qml new file mode 100644 index 00000000..d3aaf050 --- /dev/null +++ b/src/controls/swipenavigator/PrivateSwipeProgress.qml @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2020 Carson Black + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import org.kde.kirigami 2.12 as Kirigami + +Item { + id: __progressRoot + property var progress + + Rectangle { + Accessible.ignored: true + + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + } + width: parent.width * __progressRoot.progress + color: Kirigami.ColorUtils.adjustColor(Kirigami.Theme.positiveTextColor, {"alpha": 0.2*255}) + + Rectangle { + anchors { + bottom: Kirigami.Settings.isMobile ? undefined : parent.bottom + top: Kirigami.Settings.isMobile ? parent.top : undefined + left: parent.left + right: parent.right + } + + color: Kirigami.Theme.positiveTextColor + + // Unlike most things, we don't want to scale with the em grid, so we don't use a Unit. + height: 2 + } + } + + + Rectangle { + Accessible.ignored: true + + anchors { + top: parent.top + bottom: parent.bottom + right: parent.right + } + width: parent.width - (parent.width * __progressRoot.progress) + color: Kirigami.ColorUtils.adjustColor(Kirigami.Theme.textColor, {"alpha": 0.1*255}) + + Rectangle { + anchors { + bottom: Kirigami.Settings.isMobile ? undefined : parent.bottom + top: Kirigami.Settings.isMobile ? parent.top : undefined + left: parent.left + right: parent.right + } + + color: Kirigami.ColorUtils.adjustColor(Kirigami.Theme.textColor, {"alpha": 0.1*255}) + + // Unlike most things, we don't want to scale with the em grid, so we don't use a Unit. + height: 2 + } + } +} \ No newline at end of file diff --git a/src/controls/swipenavigator/PrivateSwipeStack.qml b/src/controls/swipenavigator/PrivateSwipeStack.qml new file mode 100644 index 00000000..76f8e9c2 --- /dev/null +++ b/src/controls/swipenavigator/PrivateSwipeStack.qml @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: 2020 Carson Black + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import org.kde.kirigami 2.12 as Kirigami + +StackView { + popEnter: Transition { + OpacityAnimator { + from: 0 + to: 1 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutCubic + } + } + popExit: Transition { + ParallelAnimation { + OpacityAnimator { + from: 1 + to: 0 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutCubic + } + YAnimator { + from: 0 + to: height/2 + duration: Kirigami.Units.longDuration + easing.type: Easing.InCubic + } + } + } + + pushEnter: Transition { + ParallelAnimation { + //NOTE: It's a PropertyAnimation instead of an Animator because with an animator the item will be visible for an instant before starting to fade + PropertyAnimation { + property: "opacity" + from: 0 + to: 1 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutCubic + } + YAnimator { + from: height/2 + to: 0 + duration: Kirigami.Units.longDuration + easing.type: Easing.OutCubic + } + } + } + + + pushExit: Transition { + OpacityAnimator { + from: 1 + to: 0 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutCubic + } + } + + replaceEnter: Transition { + ParallelAnimation { + OpacityAnimator { + from: 0 + to: 1 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutCubic + } + YAnimator { + from: height/2 + to: 0 + duration: Kirigami.Units.longDuration + easing.type: Easing.OutCubic + } + } + } + + replaceExit: Transition { + ParallelAnimation { + OpacityAnimator { + from: 1 + to: 0 + duration: Kirigami.Units.longDuration + easing.type: Easing.InCubic + } + YAnimator { + from: 0 + to: -height/2 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutCubic + } + } + } +} \ No newline at end of file diff --git a/src/controls/swipenavigator/PrivateSwipeTab.qml b/src/controls/swipenavigator/PrivateSwipeTab.qml new file mode 100644 index 00000000..3e0efc05 --- /dev/null +++ b/src/controls/swipenavigator/PrivateSwipeTab.qml @@ -0,0 +1,160 @@ +/* + * SPDX-FileCopyrightText: 2020 Carson Black + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import org.kde.kirigami 2.12 as Kirigami + +Rectangle { + id: tabRoot + property bool small: false + signal indexChanged(real xPos, real tabWidth) + + Keys.onPressed: { + if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) { + columnView.currentIndex = index + } + } + activeFocusOnTab: true + implicitHeight: swipeNavigatorRoot.big + ? small ? Kirigami.Units.gridUnit*5 : Kirigami.Units.gridUnit*4 + : small ? Kirigami.Units.gridUnit*3 : Kirigami.Units.gridUnit*2 + + onActiveFocusChanged: { + if (activeFocus) { + tabRoot.indexChanged(tabRoot.x, tabRoot.width) + } + } + Connections { + target: columnView + function onCurrentIndexChanged() { + if (index == columnView.currentIndex) { + tabRoot.indexChanged(tabRoot.x, tabRoot.width) + } + } + } + + Accessible.name: modelData.title + Accessible.description: { + if (!!modelData.progress) { + if (index == columnView.currentIndex) { + return i18nc("Accessibility text for a page tab. Keep the text as concise as possible and don't use a percent sign.", "Current page. Progress: %1 percent.", Math.round(modelData.progress*100)) + } else { + return i18nc("Accessibility text for a page tab. Keep the text as concise as possible.", "Navigate to %1. Progress: %1 percent.", modelData.title, Math.round(modelData.progress*100)) + } + } else { + if (index == columnView.currentIndex) { + return i18nc("Accessibility text for a page tab. Keep the text as concise as possible.", "Current page.") + } else if (modelData.needsAttention) { + return i18nc("Accessibility text for a page tab that's requesting the user's attention. Keep the text as concise as possible.", "Navigate to %1. Demanding attention.", modelData.title) + } else { + return i18nc("Accessibility text for a page tab that's requesting the user's attention. Keep the text as concise as possible.", "Navigate to %1.", modelData.title) + } + } + } + Accessible.role: Accessible.PageTab + Accessible.focusable: true + Accessible.onPressAction: columnView.currentIndex = index + + implicitWidth: small ? smallTitleRow.implicitWidth : largeTitleRow.implicitWidth + border { + width: activeFocus ? 2 : 0 + color: Kirigami.Theme.textColor + } + color: { + if (index == columnView.currentIndex) { + return Kirigami.ColorUtils.adjustColor(Kirigami.Theme.activeTextColor, {"alpha": 0.2*255}) + } else if (modelData.needsAttention) { + return Kirigami.ColorUtils.adjustColor(Kirigami.Theme.negativeTextColor, {"alpha": 0.2*255}) + } else { + return "transparent" + } + } + + PrivateSwipeHighlight { + states: [ + State { name: "highlighted"; when: index == columnView.currentIndex }, + State { name: "requestingAttention"; when: modelData.needsAttention } + ] + } + + PrivateSwipeProgress { + anchors.fill: parent + visible: modelData.progress != undefined + progress: modelData.progress + } + + RowLayout { + id: smallTitleRow + anchors.fill: parent + Accessible.ignored: true + visible: small + + ColumnLayout { + Layout.margins: Kirigami.Settings.isMobile ? Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing + Layout.alignment: Qt.AlignVCenter + + Kirigami.Icon { + visible: !!modelData.icon.name + source: modelData.icon.name + + Layout.preferredHeight: swipeNavigatorRoot.big ? Kirigami.Units.iconSizes.medium : Kirigami.Units.iconSizes.small + Layout.preferredWidth: Layout.preferredHeight + + Layout.alignment: (Qt.AlignHCenter | Qt.AlignBottom) + } + Kirigami.Heading { + level: swipeNavigatorRoot.big ? 2 : 5 + text: modelData.title + + Layout.fillWidth: true + Layout.alignment: (Qt.AlignLeft | Qt.AlignVCenter) + } + } + } + + RowLayout { + id: largeTitleRow + anchors.fill: parent + Accessible.ignored: true + visible: !small + + RowLayout { + Layout.margins: swipeNavigatorRoot.big ? Kirigami.Units.largeSpacing*2 : Kirigami.Units.largeSpacing + Layout.alignment: Qt.AlignVCenter + + Kirigami.Icon { + visible: !!modelData.icon.name + source: modelData.icon.name + + Layout.preferredHeight: swipeNavigatorRoot.big ? Kirigami.Units.iconSizes.medium : Kirigami.Units.iconSizes.small + Layout.preferredWidth: Layout.preferredHeight + + Layout.alignment: (Qt.AlignLeft | Qt.AlignVCenter) + } + Kirigami.Heading { + level: swipeNavigatorRoot.big ? 1 : 2 + text: modelData.title + + Layout.fillWidth: true + Layout.alignment: (Qt.AlignLeft | Qt.AlignVCenter) + } + } + } + + MouseArea { + id: mouse + anchors.fill: parent + Accessible.ignored: true + onClicked: { + columnView.currentIndex = index + } + } + + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter +} \ No newline at end of file diff --git a/src/controls/swipenavigator/PrivateSwipeTabBar.qml b/src/controls/swipenavigator/PrivateSwipeTabBar.qml new file mode 100644 index 00000000..bd05d9b0 --- /dev/null +++ b/src/controls/swipenavigator/PrivateSwipeTabBar.qml @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2020 Carson Black + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import org.kde.kirigami 2.12 as Kirigami + +RowLayout { + id: swipeTabBarRoot + spacing: 0 + signal indexChanged(real xPos, real tabWidth) + + property Item layouter: Item { + Row { + id: expandedLayouter + Repeater { + model: swipeNavigatorRoot.pages + delegate: PrivateSwipeTab { small: false } + } + } + } + + Repeater { + model: swipeNavigatorRoot.pages + delegate: PrivateSwipeTab { + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + small: expandedLayouter.width > swipeNavigatorRoot.width + onIndexChanged: swipeTabBarRoot.indexChanged(xPos, tabWidth) + } + } +} diff --git a/src/controls/swipenavigator/PrivateSwipeTabBarLoader.qml b/src/controls/swipenavigator/PrivateSwipeTabBarLoader.qml new file mode 100644 index 00000000..471d52d5 --- /dev/null +++ b/src/controls/swipenavigator/PrivateSwipeTabBarLoader.qml @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2020 Carson Black + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import org.kde.kirigami 2.12 as Kirigami + +Loader { + id: __loader + readonly property bool shouldScroll: shrunkLayouter.width > swipeNavigatorRoot.width + property Item layouter: Item { + Row { + id: shrunkLayouter + Repeater { + model: swipeNavigatorRoot.pages + delegate: PrivateSwipeTab { small: true } + } + } + } + Component { + id: nonScrollable + PrivateSwipeTabBar {} + } + Component { + id: scrollable + ScrollView { + id: view + ScrollBar.horizontal.visible: false + Timer { + interval: 5000 + running: true + repeat: true + } + PrivateSwipeTabBar { + id: bar + property real targetDestination + NumberAnimation { + id: scrollAni + target: view.ScrollBar.horizontal + property: "position" + to: bar.targetDestination + duration: Kirigami.Units.longDuration + easing.type: Easing.OutExpo + } + onIndexChanged: { + if (xPos > (bar.width)/2) { + bar.targetDestination = (1-view.ScrollBar.horizontal.size) * ((xPos+tabWidth) / bar.width) + scrollAni.restart() + } else { + bar.targetDestination = (1-view.ScrollBar.horizontal.size) * ((xPos) / bar.width) + scrollAni.restart() + } + } + } + } + } + sourceComponent: shouldScroll ? scrollable : nonScrollable +} \ No newline at end of file diff --git a/src/controls/swipenavigator/SwipeNavigator.qml b/src/controls/swipenavigator/SwipeNavigator.qml new file mode 100644 index 00000000..1170fbc8 --- /dev/null +++ b/src/controls/swipenavigator/SwipeNavigator.qml @@ -0,0 +1,185 @@ +/* + * SPDX-FileCopyrightText: 2020 Carson Black + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import org.kde.kirigami 2.13 as Kirigami + +/** + * SwipeNavigator is a control providing for lateral navigation. + * + * @include swipenavigator/main.qml + */ +GridLayout { + id: swipeNavigatorRoot + rowSpacing: 0 + columns: 1 + + /** + * pages: list + * + * A list of pages to swipe between. + */ + default property list pages + + /** + * layers: StackView + * + * The StackView holding the core item, allowing users of a SwipeNavigator + * in order to push pages on top of the SwipeNavigator. + */ + property alias layers: stackView + + /** + * big: bool + * + * Whether or not to present the SwipeNavigator in a larger format + * suitable for rendering on televisions. + */ + property bool big: false + + /** + * header: Item + * + * The item that will be displayed before the tabs. + */ + property Component header: Item {visible: false} + + /** + * footer: Item + * + * The item that will be displayed after the tabs. + */ + property Component footer: Item {visible: false} + + QtObject { + id: _gridManager + readonly property bool tall: (_header.width + __main.item.width + Math.abs(__main.offset) + _footer.width) > swipeNavigatorRoot.width + readonly property int rowOne: Kirigami.Settings.isMobile ? 1 : 0 + readonly property int rowTwo: Kirigami.Settings.isMobile ? 0 : 1 + readonly property int rowDirection: Kirigami.Settings.isMobile ? 1 : -1 + property Item item: Item { + states: [ + State { + name: "small" + when: !_gridManager.tall + }, + State { + name: "tall" + when: _gridManager.tall + } + ] + transitions: [ + Transition { + to: "tall" + ScriptAction { + script: { + // Let's take these out of the layout first... + _dummyOne.visible = false + _dummyTwo.visible = false + // Now we move the header and footer up + _header.Layout.row += _gridManager.rowDirection + _footer.Layout.row += _gridManager.rowDirection + // Now that the header and footer are out of the way, + // let's expand the tabs + __main.Layout.column-- + __main.Layout.columnSpan = 3 + } + } + }, + Transition { + to: "small" + ScriptAction { + script: { + // Let's move the tabs back to where they belong + __main.Layout.columnSpan = 1 + __main.Layout.column++ + // Move the header and footer down into the empty space + _header.Layout.row -= _gridManager.rowDirection + _footer.Layout.row -= _gridManager.rowDirection + // Now we can bring these guys back in + _dummyOne.visible = false + _dummyTwo.visible = false + } + } + } + ] + } + } + + ToolBar { + id: topToolBar + + padding: 0 + bottomPadding: 1 + position: Kirigami.Settings.isMobile ? ToolBar.Footer : ToolBar.Header + + Layout.row: Kirigami.Settings.isMobile ? 1 : 0 + + GridLayout { + id: _grid + + rowSpacing: 0 + columnSpacing: 0 + anchors.fill: parent + rows: 2 + columns: 3 + + // Row one + Item { id: _spacer; Layout.row: 0; Layout.column: 1; Layout.fillWidth: true } + Item { id: _dummyOne; Layout.row: 0; Layout.column: 0 } + Item { id: _dummyTwo; Layout.row: 0; Layout.column: 2 } + + // Row two + Loader { id: _header; sourceComponent: swipeNavigatorRoot.header; Layout.row: 1; Layout.column: 0 } + PrivateSwipeTabBarLoader { + id: __main + readonly property int offset: _header.width - _footer.width + readonly property int effectiveOffset: _gridManager.tall ? 0 : offset + Layout.rightMargin: effectiveOffset > 0 ? effectiveOffset : 0 + Layout.leftMargin: effectiveOffset < 0 ? -effectiveOffset : 0 + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + Layout.row: 1 + Layout.column: 1 + states: [ + State { + name: "shouldScroll" + when: __main.shouldScroll + PropertyChanges { target: __main; Layout.fillWidth: true } + } + ] + } + Loader { id: _footer; sourceComponent: swipeNavigatorRoot.footer; Layout.row: 1; Layout.column: 2 } + } + + Layout.fillWidth: true + + Accessible.role: Accessible.PageTabList + } + + StackView { + id: stackView + + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.row: Kirigami.Settings.isMobile ? 0 : 1 + + Kirigami.ColumnView { + id: columnView + columnResizeMode: Kirigami.ColumnView.SingleColumn + + contentChildren: swipeNavigatorRoot.pages + anchors.fill: parent + + Component.onCompleted: { + columnView.currentIndex = 0 + } + } + } +} diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp index 5753ef5d..acdefbc4 100644 --- a/src/kirigamiplugin.cpp +++ b/src/kirigamiplugin.cpp @@ -1,274 +1,275 @@ /* * SPDX-FileCopyrightText: 2009 Alan Alpert * SPDX-FileCopyrightText: 2010 Ménard Alexis * SPDX-FileCopyrightText: 2010 Marco Martin * * SPDX-License-Identifier: LGPL-2.0-or-later */ #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 "shadowedrectangle.h" #include "shadowedtexture.h" #include "colorutils.h" #include "pagerouter.h" #include "imagecolors.h" #include "avatar.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); } }; // we can't do this in the plugin object directly, as that can live in a different thread // and event filters are only allowed in the same thread as the filtered object class LanguageChangeEventFilter : public QObject { Q_OBJECT public: bool eventFilter(QObject *receiver, QEvent *event) override { if (event->type() == QEvent::LanguageChange && receiver == QCoreApplication::instance()) { emit languageChangeEvent(); } return QObject::eventFilter(receiver, event); } Q_SIGNALS: void languageChangeEvent(); }; KirigamiPlugin::KirigamiPlugin(QObject *parent) : QQmlExtensionPlugin(parent) { auto filter = new LanguageChangeEventFilter; filter->moveToThread(QCoreApplication::instance()->thread()); QCoreApplication::instance()->installEventFilter(filter); connect(filter, &LanguageChangeEventFilter::languageChangeEvent, this, &KirigamiPlugin::languageChangeEvent); } 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) { #if defined(Q_OS_ANDROID) && QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc")); #endif 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"); // 2.12 qmlRegisterType(uri, 2, 12, "ShadowedRectangle"); qmlRegisterType(uri, 2, 12, "ShadowedTexture"); qmlRegisterType(componentUrl(QStringLiteral("ShadowedImage.qml")), uri, 2, 12, "ShadowedImage"); qmlRegisterType(componentUrl(QStringLiteral("PlaceholderMessage.qml")), uri, 2, 12, "PlaceholderMessage"); qmlRegisterUncreatableType(uri, 2, 12, "BorderGroup", QStringLiteral("Used as grouped property")); qmlRegisterUncreatableType(uri, 2, 12, "ShadowGroup", QStringLiteral("Used as grouped property")); qmlRegisterSingletonType(uri, 2, 12, "ColorUtils", [] (QQmlEngine*, QJSEngine*) -> QObject* { return new ColorUtils; }); qmlRegisterUncreatableType(uri, 2, 12, "CornersGroup", QStringLiteral("Used as grouped property")); qmlRegisterType(uri, 2, 12, "PageRouter"); qmlRegisterType(uri, 2, 12, "PageRoute"); qmlRegisterUncreatableType(uri, 2, 12, "PageRouterAttached", QStringLiteral("PageRouterAttached cannot be created")); qmlRegisterType(componentUrl(QStringLiteral("RouterWindow.qml")), uri, 2, 12, "RouterWindow"); // 2.13 qmlRegisterType(uri, 2, 13, "ImageColors"); qmlRegisterSingletonType("org.kde.kirigami.private", 2, 13, "AvatarPrivate", [] (QQmlEngine*, QJSEngine*) -> QObject* { return new AvatarPrivate; }); qmlRegisterType(componentUrl(QStringLiteral("Avatar.qml")), uri, 2, 13, "Avatar"); + qmlRegisterType(componentUrl(QStringLiteral("swipenavigator/SwipeNavigator.qml")), uri, 2, 13, "SwipeNavigator"); qmlProtectModule(uri, 2); } void KirigamiPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(uri); connect(this, &KirigamiPlugin::languageChangeEvent, engine, &QQmlEngine::retranslate); } #include "kirigamiplugin.moc"