diff --git a/src/controls/AbstractApplicationHeader.qml b/src/controls/AbstractApplicationHeader.qml --- a/src/controls/AbstractApplicationHeader.qml +++ b/src/controls/AbstractApplicationHeader.qml @@ -18,6 +18,7 @@ */ import QtQuick 2.5 +import QtQuick.Templates 2.0 as T2 import org.kde.kirigami 2.5 import "private" @@ -51,9 +52,10 @@ anchors { right: parent.right left: parent.left - top: parent.bottom + bottom: root.position == T2.ToolBar.Footer ? parent.top : undefined + top: root.position == T2.ToolBar.Footer ? undefined : parent.bottom } - edge: Qt.TopEdge + edge: root.position == T2.ToolBar.Footer ? Qt.BottomEdge : Qt.TopEdge opacity: (!root.page.header || root.page.header.toString().indexOf("ToolBar") === -1) Behavior on opacity { OpacityAnimator { diff --git a/src/controls/Page.qml b/src/controls/Page.qml --- a/src/controls/Page.qml +++ b/src/controls/Page.qml @@ -19,13 +19,13 @@ import QtQuick 2.1 import QtQuick.Layouts 1.2 -import org.kde.kirigami 2.4 as Kirigami +import org.kde.kirigami 2.5 as Kirigami import "private" import QtQuick.Templates 2.0 as T2 /** * Page is a container for all the app pages: everything pushed to the - * ApplicationWindow stackView should be a Page instabnce (or a subclass, + * ApplicationWindow pageStackView should be a Page instabnce (or a subclass, * such as ScrollablePage) * @see ScrollablePage * @inherit QtQuick.Templates.Page @@ -55,7 +55,7 @@ * bottomPadding: int * default contents padding at bottom */ - bottomPadding: actionButtons.item ? actionButtons.height : Kirigami.Units.gridUnit + bottomPadding: globalFooter.item ? globalFooter.height : Kirigami.Units.gridUnit /** * flickable: Flickable @@ -203,15 +203,15 @@ /** * isCurrentPage: bool * - * Specifies if it's the currently selected page in the window's pages row. + * Specifies if it's the currently selected page in the window's pages pageRow. * * @since 2.1 */ - readonly property bool isCurrentPage: typeof applicationWindow === "undefined" || !globalToolBar.row + readonly property bool isCurrentPage: typeof applicationWindow === "undefined" || !globalHeader.pageRow ? true - : (globalToolBar.row.layers.depth > 1 - ? globalToolBar.row.layers.currentItem == root - : globalToolBar.row.currentItem == root) + : (globalHeader.pageRow.layers.depth > 1 + ? globalHeader.pageRow.layers.currentItem == root + : globalHeader.pageRow.currentItem == root) PageActionPropertyGroup { id: actionsGroup @@ -227,6 +227,12 @@ */ signal backRequested(var event); + /** + * + */ + property Component customGlobalToolBar + readonly property Item customGlobalToolBarItem: globalHeader.item + //NOTE: This exists just because control instances require it contentItem: Item { onChildrenChanged: { @@ -252,64 +258,103 @@ Component.onCompleted: { parentChanged(root.parent); } + //FIXME: this would need attached properties but would need a c++ PageRow onParentChanged: { if (!parent) { return; } - globalToolBar.stack = null; - globalToolBar.row = null; + globalHeader.pageStack = null; + globalHeader.pageRow = null; if (root.parent.hasOwnProperty("__pageRow")) { - globalToolBar.row = root.parent.__pageRow; + globalHeader.pageRow = root.parent.__pageRow; } if (root.T2.StackView.view) { - globalToolBar.stack = root.T2.StackView.view; - globalToolBar.row = root.T2.StackView.view.parent; + globalHeader.pageStack = root.T2.StackView.view; + globalHeader.pageRow = root.T2.StackView.view.parent; } - if (globalToolBar.row) { - globalToolBar.row.globalToolBar.actualStyleChanged.connect(globalToolBar.syncSource); - globalToolBar.syncSource(); + if (globalHeader.pageRow) { + globalHeader.pageRow.globalToolBar.actualStyleChanged.connect(globalHeader.syncToolBars); + globalHeader.syncToolBars(); } } - //global top toolbar if we are in a PageRow (in the row or as a layer) + //global top toolbar if we are in a PageRow (in the pageRow or as a layer) Loader { - id: globalToolBar + id: globalHeader z: 9999 parent: root.clip ? root.parent : root height: item ? item.implicitHeight : 0 anchors { left: parent ? root.left : undefined right: parent ? root.right : undefined bottom: parent ? root.top : undefined } - property Kirigami.PageRow row - property T2.StackView stack + property Kirigami.PageRow pageRow + property T2.StackView pageStack + property Loader customToolBarLoader: pageRow && pageRow.globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.ToolBar ? globalHeader : globalFooter - active: row && (row.globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.ToolBar || globalToolBar.row.globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.Titles) + active: pageRow && (pageRow.globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.ToolBar || globalHeader.pageRow.globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.Titles) - function syncSource() { - if (row && active) { - setSource(Qt.resolvedUrl(row.globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.ToolBar ? "private/ToolBarPageHeader.qml" : "private/TitlesPageHeader.qml"), + function syncToolBars() { + //load/reload the top toolbar + if (pageRow && active) { + var toolbarFile + if (pageRow.globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.ToolBar) { + if (root.customGlobalToolBar) { + toolbarFile = "private/AbstractPageHeader.qml"; + } else { + toolbarFile = "private/ToolBarPageHeader.qml"; + } + } else { + toolbarFile = "private/TitlesPageHeader.qml"; + } + globalHeader.setSource(Qt.resolvedUrl(toolbarFile), //TODO: find container reliably, remove assumption - {"pageRow": Qt.binding(function() {return row}), + {"pageRow": Qt.binding(function() {return pageRow}), "page": root, - "current": Qt.binding(function() {return stack || !root.parent ? true : row.currentIndex == root.parent.level})}); + "contentComponent": pageRow.globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.ToolBar ? customGlobalToolBar : null + }); } - } - Separator { - z: 999 - anchors.verticalCenter: globalToolBar.verticalCenter - height: globalToolBar.height * 0.6 - visible: globalToolBar.row && root.parent && globalToolBar.row.contentItem.contentX < root.parent.x - globalToolBar.row.globalToolBar.leftReservedSpace - Kirigami.Theme.textColor: globalToolBar.item ? globalToolBar.item.Kirigami.Theme.textColor : undefined + //load/reload the bottom toolbar + if (pageRow && globalFooter.active) { + var toolbarFile = root.customGlobalToolBar ? Qt.resolvedUrl("./private/AbstractPageHeader.qml") : Qt.resolvedUrl("./private/ActionButton.qml") + + globalFooter.setSource(Qt.resolvedUrl(toolbarFile), + //TODO: find container reliably, remove assumption + {"pageRow": Qt.binding(function() {return pageRow}), + "page": root, + "position": T2.ToolBar.Footer, + "contentComponent": customGlobalToolBar, + "leftPadding": Qt.binding(function() { + if (typeof applicationWindow == "undefined") { + return 0; + } + if (applicationWindow().globalDrawer && applicationWindow().globalDrawer.handleVisible) { + return Math.min(Math.max(0, applicationWindow().globalDrawer.handle.width - root.Kirigami.ScenePosition.x), + applicationWindow().globalDrawer.handle.width) + Kirigami.Units.smallSpacing; + } + return 0; + }), + "rightPadding": Qt.binding(function() { + if (typeof applicationWindow == "undefined") { + return 0; + } + if (applicationWindow().globalDrawer && applicationWindow().globalDrawer.handleVisible) { + return Math.min(Math.max(0, (root.Kirigami.ScenePosition.x + root.width) - (applicationWindow().width - applicationWindow().globalDrawer.handle.width)), + applicationWindow().globalDrawer.handle.width) + Kirigami.Units.smallSpacing; + } + return 0; + }), + }); + } } } //bottom action buttons Loader { - id: actionButtons + id: globalFooter z: 9999 parent: root anchors { @@ -319,13 +364,12 @@ } //It should be T2.Page, Qt 5.7 doesn't like it property Item page: root - height: item ? item.height : 0 - active: typeof applicationWindow !== "undefined" && (!globalToolBar.row || globalToolBar.row.globalToolBar.actualStyle != Kirigami.ApplicationHeaderStyle.ToolBar) && - //Legacy + height: item ? item.implicitHeight : 0 + active: typeof applicationWindow !== "undefined" && (!globalHeader.pageRow || globalHeader.pageRow.globalToolBar.actualStyle != Kirigami.ApplicationHeaderStyle.ToolBar) && + //Legacy, get eventually rid of it (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/PageRow.qml b/src/controls/PageRow.qml --- a/src/controls/PageRow.qml +++ b/src/controls/PageRow.qml @@ -652,7 +652,7 @@ page.anchors.top = container.top; page.anchors.right = container.right; page.anchors.bottom = container.bottom; - page.anchors.topMargin = Qt.binding(function() {return globalToolBarUI.height}); + page.anchors.topMargin = Qt.binding(function() {return page.customGlobalToolBarItem ? page.customGlobalToolBarItem.height : globalToolBarUI.height}); } else { pagesLogic.remove(level); } @@ -679,9 +679,24 @@ } Separator { + id: toolBarSeparator z: 999 + anchors.verticalCenter: globalToolBar.verticalCenter + y: pageSeparator.y/2 - height/2 + height: pageSeparator.y * 0.6 + visible: root.separatorVisible && mainView.contentX < container.x - globalToolBar.leftReservedSpace + Theme.textColor: globalToolBar.item ? globalToolBar.item.Theme.textColor : undefined + } + Separator { + id: pageSeparator + z: 999 + property int pageHeight: page ? page.height + 1 : 0 + property Item previousPageContainer: pagesLogic.get(container.level - 1) + height: previousPageContainer && previousPageContainer.page + ? Math.max(previousPageContainer.page.height, pageHeight) + : pageHeight anchors { - top: page ? page.top : parent.top + //top: page ? page.top : parent.top bottom: parent.bottom left: parent.left //ensure a sharp angle diff --git a/src/controls/private/AbstractPageHeader.qml b/src/controls/private/AbstractPageHeader.qml --- a/src/controls/private/AbstractPageHeader.qml +++ b/src/controls/private/AbstractPageHeader.qml @@ -26,20 +26,25 @@ id: root anchors.fill: parent property Item container - property bool current + property Component contentComponent + onContentComponentChanged: { + contentItem = contentComponent.createObject(root); + } minimumHeight: pageRow.globalToolBar.minimumHeight maximumHeight: pageRow.globalToolBar.maximumHeight - preferredHeight: pageRow.globalToolBar.preferredHeight + preferredHeight: Math.max(contentItem ? contentItem.implicitHeight : 0, pageRow.globalToolBar.preferredHeight); implicitHeight: page.y separatorVisible: pageRow.globalToolBar.separatorVisible + topPadding: Units.smallSpacing + bottomPadding: Units.smallSpacing leftPadding: Math.min(Qt.application.layoutDirection == Qt.LeftToRight ? Math.max(0, pageRow.ScenePosition.x - page.ScenePosition.x + pageRow.globalToolBar.leftReservedSpace) : Math.max(0, -pageRow.width + pageRow.ScenePosition.x + page.ScenePosition.x + page.width + pageRow.globalToolBar.leftReservedSpace), - root.width/2) + root.width/2) + Units.smallSpacing rightPadding: Qt.application.layoutDirection == Qt.LeftToRight ? Math.min(-pageRow.width - pageRow.ScenePosition.x + page.ScenePosition.x + page.width + pageRow.globalToolBar.rightReservedSpace) - : Math.max(0, pageRow.ScenePosition.x - page.ScenePosition.x + pageRow.globalToolBar.rightReservedSpace) + : Math.max(0, pageRow.ScenePosition.x - page.ScenePosition.x + pageRow.globalToolBar.rightReservedSpace) + Units.smallSpacing } diff --git a/src/controls/private/ActionButton.qml b/src/controls/private/ActionButton.qml --- a/src/controls/private/ActionButton.qml +++ b/src/controls/private/ActionButton.qml @@ -36,7 +36,7 @@ bottomMargin: root.page.footer ? root.page.footer.height : 0 } //smallSpacing for the shadow - height: button.height + Units.smallSpacing + implicitHeight: button.height + Units.smallSpacing clip: true readonly property Page page: root.parent.page diff --git a/src/controls/private/TitlesPageHeader.qml b/src/controls/private/TitlesPageHeader.qml --- a/src/controls/private/TitlesPageHeader.qml +++ b/src/controls/private/TitlesPageHeader.qml @@ -31,7 +31,7 @@ id: title anchors.fill: parent leftPadding: Units.largeSpacing - opacity: root.current ? 1 : 0.4 + opacity: root.page.isCurrentPage ? 1 : 0.4 maximumLineCount: 1 color: Theme.textColor elide: Text.ElideRight diff --git a/src/controls/private/ToolBarPageHeader.qml b/src/controls/private/ToolBarPageHeader.qml --- a/src/controls/private/ToolBarPageHeader.qml +++ b/src/controls/private/ToolBarPageHeader.qml @@ -30,90 +30,94 @@ implicitWidth: titleTextMetrics.width/2 + buttonTextMetrics.collapsedButtonsWidth Layout.minimumWidth: ctxActionsButton.width*4 - MouseArea { - anchors.fill: parent - onClicked: page.forceActiveFocus() - } - RowLayout { - id: titleLayout - anchors { - verticalCenter: parent.verticalCenter - left: parent.left - right: actionsLayout.left + Item { + anchors.fill:parent + implicitHeight: actionsLayout.height + MouseArea { + anchors.fill: parent + onClicked: page.forceActiveFocus() } + RowLayout { + id: titleLayout + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + right: actionsLayout.left + } - Heading { - id: title - Layout.fillWidth: true + Heading { + id: title + Layout.fillWidth: true - Layout.preferredWidth: implicitWidth - Layout.minimumWidth: Math.min(titleTextMetrics.width, root.width - buttonTextMetrics.requiredWidth) - leftPadding: Units.largeSpacing - opacity: root.current ? 1 : 0.4 - maximumLineCount: 1 - color: Theme.textColor - elide: Text.ElideRight - text: page ? page.title : "" + Layout.preferredWidth: implicitWidth + Layout.minimumWidth: Math.min(titleTextMetrics.width, root.width - buttonTextMetrics.requiredWidth) + leftPadding: Units.largeSpacing + opacity: root.page.isCurrentPage ? 1 : 0.4 + maximumLineCount: 1 + color: Theme.textColor + elide: Text.ElideRight + text: page ? page.title : "" + } } - } - TextMetrics { - id: titleTextMetrics - text: page ? page.title : "" - font: title.font - } - TextMetrics { - id: buttonTextMetrics - text: (page.actions.left ? page.actions.left.text : "") + (page.actions.main ? page.actions.main.text : "") + (page.actions.right ? page.actions.right.text : "") - readonly property int collapsedButtonsWidth: ctxActionsButton.width + (page.actions.left ? ctxActionsButton.width + Units.gridUnit : 0) + (page.actions.main ? ctxActionsButton.width + Units.gridUnit : 0) + (page.actions.right ? ctxActionsButton.width + Units.gridUnit : 0) - readonly property int requiredWidth: width + collapsedButtonsWidth - } - - RowLayout { - id: actionsLayout - anchors { - verticalCenter: parent.verticalCenter - right: ctxActionsButton.visible ? ctxActionsButton.left : parent.right + TextMetrics { + id: titleTextMetrics + text: page ? page.title : "" + font: title.font + } + TextMetrics { + id: buttonTextMetrics + text: (page.actions.left ? page.actions.left.text : "") + (page.actions.main ? page.actions.main.text : "") + (page.actions.right ? page.actions.right.text : "") + readonly property int collapsedButtonsWidth: ctxActionsButton.width + (page.actions.left ? ctxActionsButton.width + Units.gridUnit : 0) + (page.actions.main ? ctxActionsButton.width + Units.gridUnit : 0) + (page.actions.right ? ctxActionsButton.width + Units.gridUnit : 0) + readonly property int requiredWidth: width + collapsedButtonsWidth } - readonly property bool toobig: root.width - root.leftPadding - root.rightPadding - titleTextMetrics.width - Units.gridUnit < buttonTextMetrics.requiredWidth + RowLayout { + id: actionsLayout + anchors { + verticalCenter: parent.verticalCenter + right: ctxActionsButton.visible ? ctxActionsButton.left : parent.right + } - PrivateActionToolButton { - Layout.alignment: Qt.AlignVCenter - kirigamiAction: page && page.actions ? page.actions.left : null - showText: !parent.toobig - } - PrivateActionToolButton { - Layout.alignment: Qt.AlignVCenter - Layout.rightMargin: Units.smallSpacing - kirigamiAction: page && page.actions ? page.actions.main : null - showText: !parent.toobig - flat: false + readonly property bool toobig: root.width - root.leftPadding - root.rightPadding - titleTextMetrics.width - Units.gridUnit < buttonTextMetrics.requiredWidth + + PrivateActionToolButton { + Layout.alignment: Qt.AlignVCenter + kirigamiAction: page && page.actions ? page.actions.left : null + showText: !parent.toobig + } + PrivateActionToolButton { + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: Units.smallSpacing + kirigamiAction: page && page.actions ? page.actions.main : null + showText: !parent.toobig + flat: false + } + PrivateActionToolButton { + Layout.alignment: Qt.AlignVCenter + kirigamiAction: page && page.actions ? page.actions.right : null + showText: !parent.toobig + } } + PrivateActionToolButton { - Layout.alignment: Qt.AlignVCenter - kirigamiAction: page && page.actions ? page.actions.right : null - showText: !parent.toobig - } - } + id: ctxActionsButton + showMenuArrow: page.actions.contextualActions.length == 1 + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + rightMargin: Units.smallSpacing + } + Action { + id: overflowAction + icon.name: "overflow-menu" + tooltip: qsTr("More Actions") + visible: children.length > 0 + children: page && page.actions.contextualActions ? page.actions.contextualActions : null + } - PrivateActionToolButton { - id: ctxActionsButton - showMenuArrow: page.actions.contextualActions.length == 1 - anchors { - right: parent.right - verticalCenter: parent.verticalCenter - rightMargin: Units.smallSpacing + kirigamiAction: page && page.actions.contextualActions.length === 1 ? page.actions.contextualActions[0] : overflowAction } - Action { - id: overflowAction - icon.name: "overflow-menu" - tooltip: qsTr("More Actions") - visible: children.length > 0 - children: page && page.actions.contextualActions ? page.actions.contextualActions : null - } - - kirigamiAction: page && page.actions.contextualActions.length === 1 ? page.actions.contextualActions[0] : overflowAction } } diff --git a/src/controls/templates/AbstractApplicationHeader.qml b/src/controls/templates/AbstractApplicationHeader.qml --- a/src/controls/templates/AbstractApplicationHeader.qml +++ b/src/controls/templates/AbstractApplicationHeader.qml @@ -18,6 +18,7 @@ */ import QtQuick 2.5 +import QtQuick.Templates 2.0 as T import QtQuick.Layouts 1.2 import "private" import org.kde.kirigami 2.4 @@ -39,11 +40,11 @@ id: root z: 90 property int minimumHeight: 0 - property int preferredHeight: Units.gridUnit * 2 + property int preferredHeight: contentItem ? contentItem.implicitHeight : Units.gridUnit * 2 property int maximumHeight: Units.gridUnit * 3 property PageRow pageRow: __appWindow.pageStack property Page page: pageRow.currentItem - default property alias contentItem: mainItem.data + default property Item contentItem readonly property int paintedHeight: headerItem.y + headerItem.height - 1 LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true @@ -53,6 +54,7 @@ property int bottomPadding: 0 property bool separatorVisible: true + property int position: root.y <= 0 ? T.ToolBar.Header : T.ToolBar.Footer //FIXME: remove property QtObject __appWindow: applicationWindow(); @@ -75,13 +77,15 @@ background.anchors.fill = headerItem; } + onContentItemChanged: contentItem.parent = mainItem; + onMinimumHeightChanged: implicitHeight = preferredHeight; onPreferredHeightChanged: implicitHeight = preferredHeight; opacity: height > 0 ? 1 : 0 Behavior on implicitHeight { - enabled: root.page && root.page.flickable && !root.page.flickable.moving + enabled: false&& root.page && root.page.flickable && !root.page.flickable.moving NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad @@ -99,7 +103,8 @@ anchors { left: parent.left right: parent.right - bottom: parent.bottom + top: root.position == T.ToolBar.Footer ? parent.top : undefined + bottom: root.position == T.ToolBar.Footer ? undefined : parent.bottom } height: __appWindow.reachableMode && __appWindow.reachableModeEnabled ? root.maximumHeight : (root.minimumHeight > 0 ? Math.max(root.height, root.minimumHeight) : root.preferredHeight) @@ -109,11 +114,10 @@ target: root.page ? root.page.flickable : null property int oldContentY onContentYChanged: { - if (!Settings.isMobile || + //don't make footer toolbars scroll away for now + if (!Settings.isMobile || root.position == T.ToolBar.Footer || !__appWindow.controlsVisible || - !root.page || - root.page.flickable.atYBeginning || - root.page.flickable.atYEnd) { + !root.page) { return; //if moves but not dragging, just update oldContentY } else if (!root.page.flickable.dragging) { diff --git a/src/styles/org.kde.desktop/AbstractApplicationHeader.qml b/src/styles/org.kde.desktop/AbstractApplicationHeader.qml --- a/src/styles/org.kde.desktop/AbstractApplicationHeader.qml +++ b/src/styles/org.kde.desktop/AbstractApplicationHeader.qml @@ -18,6 +18,7 @@ */ import QtQuick 2.5 +import QtQuick.Templates 2.0 as T2 import org.kde.kirigami 2.4 import "../../templates" as T @@ -46,8 +47,8 @@ anchors { left: parent.left right: parent.right - bottom: root.y <= 0 ? parent.bottom : undefined - top: root.y <= 0 ? undefined : parent.top + bottom: root.position == T2.ToolBar.Footer ? undefined : parent.bottom + top: root.position == T2.ToolBar.Footer ? parent.top : undefined } } }