diff --git a/src/controls/swipenavigator/PrivateSwipeTab.qml b/src/controls/swipenavigator/PrivateSwipeTab.qml index 19be8eb1..275bb7d7 100644 --- a/src/controls/swipenavigator/PrivateSwipeTab.qml +++ b/src/controls/swipenavigator/PrivateSwipeTab.qml @@ -1,152 +1,153 @@ /* * 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) Keys.onPressed: { if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) { columnView.currentIndex = index } } activeFocusOnTab: true implicitHeight: small ? Kirigami.Units.gridUnit*3 : Kirigami.Units.gridUnit*2 Connections { target: columnView function onCurrentIndexChanged() { if (index == columnView.currentIndex) { - swipeTabBarRoot.indexChanged(tabRoot.x) + tabRoot.indexChanged(tabRoot.x) } } } 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.Units.largeSpacing Layout.alignment: Qt.AlignVCenter Kirigami.Icon { visible: !!modelData.icon.name source: modelData.icon.name Layout.preferredHeight: Kirigami.Units.iconSizes.small Layout.preferredWidth: Layout.preferredHeight Layout.alignment: (Qt.AlignHCenter | Qt.AlignBottom) } Kirigami.Heading { level: 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: Kirigami.Units.largeSpacing Layout.alignment: Qt.AlignVCenter Kirigami.Icon { visible: !!modelData.icon.name source: modelData.icon.name Layout.preferredHeight: Kirigami.Units.iconSizes.small Layout.preferredWidth: Layout.preferredHeight Layout.alignment: (Qt.AlignLeft | Qt.AlignVCenter) } Kirigami.Heading { level: 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 index 84f35156..7da60637 100644 --- a/src/controls/swipenavigator/PrivateSwipeTabBar.qml +++ b/src/controls/swipenavigator/PrivateSwipeTabBar.qml @@ -1,36 +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 - property bool small: false - spacing: 0 signal indexChanged(real xPos) 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) } } } diff --git a/src/controls/swipenavigator/PrivateSwipeTabBarLoader.qml b/src/controls/swipenavigator/PrivateSwipeTabBarLoader.qml new file mode 100644 index 00000000..3fa4922c --- /dev/null +++ b/src/controls/swipenavigator/PrivateSwipeTabBarLoader.qml @@ -0,0 +1,46 @@ +/* + * 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 { + ScrollBar.horizontal.visible: false + PrivateSwipeTabBar {} + } + } + sourceComponent: shouldScroll ? scrollable : nonScrollable + onItemChanged: { + item.parent = this.parent + item.Layout.fillWidth = true + item.Layout.fillHeight = true + item.Layout.alignment = Qt.AlignHCenter + item.Layout.row = Qt.binding(function() { return __loader.Layout.row }) + item.Layout.column = Qt.binding(function() { return __loader.Layout.column }) + item.Layout.columnSpan = Qt.binding(function() { return __loader.Layout.columnSpan }) + } +} \ No newline at end of file diff --git a/src/controls/swipenavigator/SwipeNavigator.qml b/src/controls/swipenavigator/SwipeNavigator.qml index fa67595c..06b789db 100644 --- a/src/controls/swipenavigator/SwipeNavigator.qml +++ b/src/controls/swipenavigator/SwipeNavigator.qml @@ -1,143 +1,144 @@ /* * 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 /** * type: int * * What type of SwipeNavigator this is. * * `Kirigami.SwipeNavigatorType.Top`: this SwipeNavigator presents its UI as a top bar. * * `Kirigami.SwipeNavigatorType.Left`: this SwipeNavigator presents its UI as a sidebar or drawer depending on available width. */ property int type: Kirigami.SwipeNavigatorType.Top /** * 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 /** * header: Item * * The item that will be displayed before the tabs. */ property Item header: Item {visible: false} onHeaderChanged: { header.parent = _grid header.Layout.alignment = Qt.AlignLeft | Qt.AlignVcenter } /** * footer: Item * * The item that will be displayed after the tabs. */ property Item footer: Item {visible: false} onFooterChanged: { footer.parent = _grid footer.Layout.alignment = Qt.AlignLeft | Qt.AlignVcenter } Item { visible: false id: layouter - property bool tall: (swipeNavigatorRoot.header.width + __main.width + swipeNavigatorRoot.footer.width) > swipeNavigatorRoot.width + property bool tall: (swipeNavigatorRoot.header.width + __main.item.width + swipeNavigatorRoot.footer.width) > swipeNavigatorRoot.width states: [ State { name: "small" when: !layouter.tall PropertyChanges { target: swipeNavigatorRoot.header; Layout.row: 0; Layout.column: 0} PropertyChanges { target: __main; Layout.row: 0; Layout.column: 1} PropertyChanges { target: swipeNavigatorRoot.footer; Layout.row: 0; Layout.column: 2} }, State { name: "tall" when: layouter.tall PropertyChanges { target: swipeNavigatorRoot.header; Layout.row: 0; Layout.column: 0} PropertyChanges { target: __main; Layout.row: 1; Layout.column: 0; Layout.columnSpan: 3} PropertyChanges { target: swipeNavigatorRoot.footer; Layout.row: 0; Layout.column: 2} PropertyChanges { target: _spacer; Layout.row: 0; Layout.column: 1; Layout.fillWidth: true; visible: true } } ] } 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 Item { id: _spacer; visible: false } - PrivateSwipeTabBar { + PrivateSwipeTabBarLoader { id: __main + visible: false Layout.fillWidth: true Layout.fillHeight: true Layout.alignment: Qt.AlignHCenter } } 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 } } } }