diff --git a/examples/gallerydata/contents/ui/DesktopExampleApp.qml b/examples/gallerydata/contents/ui/DesktopExampleApp.qml --- a/examples/gallerydata/contents/ui/DesktopExampleApp.qml +++ b/examples/gallerydata/contents/ui/DesktopExampleApp.qml @@ -26,50 +26,82 @@ Kirigami.ApplicationWindow { id: root - header: Kirigami.ToolBarApplicationHeader {} - globalDrawer: Kirigami.GlobalDrawer { title: "Widget gallery" titleIcon: "applications-graphics" bannerImageSource: "banner.jpg" actions: [ Kirigami.Action { - text: "Submenu 1" + text: "Top Bar Style" iconName: "view-list-icons" Kirigami.Action { - text: "Action 1" - onTriggered: showPassiveNotification(text + " clicked") + text: "Auto" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.Auto + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.Auto + } + Kirigami.Action { + text: "Breadcrumb" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.Breadcrumb + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.Breadcrumb } Kirigami.Action { - text: "Action 2" - onTriggered: showPassiveNotification(text + " clicked") + text: "TabBar" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.TabBar + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.TabBar } Kirigami.Action { - text: "Action 3" - onTriggered: showPassiveNotification(text + " clicked") + text: "Titles" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.Titles + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.Titles + } + Kirigami.Action { + text: "ToolBar" + visible: !Kirigami.Settings.isMobile + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.ToolBar + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.ToolBar + } + Kirigami.Action { + text: "None" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.None + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.None } }, Kirigami.Action { - text: "Submenu 2" + text: "Top Bar Sizing" iconName: "folder-sync" + visible: Kirigami.Settings.isMobile + Kirigami.Action { + text: "Slide Away" + onTriggered: { + root.pageStack.globalToolBar.minimumHeight = 0; + root.pageStack.globalToolBar.preferredHeight = 42; + } + checked: root.pageStack.globalToolBar.minimumHeight == 0 + } Kirigami.Action { - text: "Action 4" - onTriggered: showPassiveNotification(text + " clicked") + text: "Fixed" + onTriggered: { + root.pageStack.globalToolBar.minimumHeight = 42; + root.pageStack.globalToolBar.preferredHeight = 42; + } + checked: root.pageStack.globalToolBar.minimumHeight == 42 } Kirigami.Action { - text: "Action 5" - onTriggered: showPassiveNotification(text + " clicked") + text: "Resizing" + onTriggered: { + root.pageStack.globalToolBar.minimumHeight = 20; + root.pageStack.globalToolBar.preferredHeight = 52; + } + checked: root.pageStack.globalToolBar.minimumHeight == 20 } }, Kirigami.Action { - text: "Checkable" + text: "Modal Drawer" iconName: "go-next" checkable: true - checked: false - onTriggered: { - showPassiveNotification("Action checked: " + checked) - } + checked: globalDrawer.modal + onCheckedChanged: globalDrawer.modal = checked }, Kirigami.Action { text: "Open A Page" diff --git a/examples/gallerydata/contents/ui/ExampleApp.qml b/examples/gallerydata/contents/ui/ExampleApp.qml --- a/examples/gallerydata/contents/ui/ExampleApp.qml +++ b/examples/gallerydata/contents/ui/ExampleApp.qml @@ -26,40 +26,74 @@ Kirigami.ApplicationWindow { id: root - header: Kirigami.ApplicationHeader {} - globalDrawer: Kirigami.GlobalDrawer { title: "Widget gallery" titleIcon: "applications-graphics" bannerImageSource: "banner.jpg" actions: [ Kirigami.Action { - text: "Submenu 1" + text: "Top Bar Style" iconName: "view-list-icons" Kirigami.Action { - text: "Action 1" - onTriggered: showPassiveNotification(text + " clicked") + text: "Auto" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.Auto + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.Auto + } + Kirigami.Action { + text: "Breadcrumb" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.Breadcrumb + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.Breadcrumb + } + Kirigami.Action { + text: "TabBar" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.TabBar + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.TabBar } Kirigami.Action { - text: "Action 2" - onTriggered: showPassiveNotification(text + " clicked") + text: "Titles" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.Titles + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.Titles } Kirigami.Action { - text: "Action 3" - onTriggered: showPassiveNotification(text + " clicked") + text: "ToolBar" + visible: !Kirigami.Settings.isMobile + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.ToolBar + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.ToolBar + } + Kirigami.Action { + text: "None" + onTriggered: root.pageStack.globalToolBar.style = Kirigami.ApplicationHeaderStyle.None + checked: root.pageStack.globalToolBar.style == Kirigami.ApplicationHeaderStyle.None } }, Kirigami.Action { - text: "Submenu 2" + text: "Top Bar Sizing" iconName: "folder-sync" + visible: Kirigami.Settings.isMobile + Kirigami.Action { + text: "Slide Away" + onTriggered: { + root.pageStack.globalToolBar.minimumHeight = 0; + root.pageStack.globalToolBar.preferredHeight = 42; + } + checked: root.pageStack.globalToolBar.minimumHeight == 0 + } Kirigami.Action { - text: "Action 4" - onTriggered: showPassiveNotification(text + " clicked") + text: "Fixed" + onTriggered: { + root.pageStack.globalToolBar.minimumHeight = 42; + root.pageStack.globalToolBar.preferredHeight = 42; + } + checked: root.pageStack.globalToolBar.minimumHeight == 42 } Kirigami.Action { - text: "Action 5" - onTriggered: showPassiveNotification(text + " clicked") + text: "Resizing" + onTriggered: { + root.pageStack.globalToolBar.minimumHeight = 20; + root.pageStack.globalToolBar.preferredHeight = 52; + } + checked: root.pageStack.globalToolBar.minimumHeight == 20 } }, Kirigami.Action { diff --git a/examples/gallerydata/contents/ui/gallery/LayersGallery.qml b/examples/gallerydata/contents/ui/gallery/LayersGallery.qml --- a/examples/gallerydata/contents/ui/gallery/LayersGallery.qml +++ b/examples/gallerydata/contents/ui/gallery/LayersGallery.qml @@ -27,7 +27,7 @@ Layout.fillWidth: true //implicitWidth: Units.gridUnit * (Math.floor(Math.random() * 35) + 8) - title: "Multiple Columns" + title: "Layers" actions { main: Action { diff --git a/examples/simpleexamples/simpleChatApp.qml b/examples/simpleexamples/simpleChatApp.qml --- a/examples/simpleexamples/simpleChatApp.qml +++ b/examples/simpleexamples/simpleChatApp.qml @@ -25,7 +25,7 @@ Kirigami.ApplicationWindow { id: root - header: Kirigami.ToolBarApplicationHeader {} + //header: Kirigami.ToolBarApplicationHeader {} //FIXME: perhaps the default logic for going widescreen should be refined upstream wideScreen: width > columnWidth * 3 property int columnWidth: Kirigami.Units.gridUnit * 13 @@ -56,7 +56,7 @@ contextDrawer: Kirigami.OverlayDrawer { id: contextDrawer //they can depend on the page like that or be defined directly here - edge: Qt.RightEdge + edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge modal: !root.wideScreen onModalChanged: drawerOpen = !modal handleVisible: applicationWindow == undefined ? false : applicationWindow().controlsVisible diff --git a/kirigami.qrc b/kirigami.qrc --- a/kirigami.qrc +++ b/kirigami.qrc @@ -28,6 +28,12 @@ src/controls/private/BannerImage.qml src/controls/private/BannerGroup.qml src/controls/private/EdgeShadow.qml + src/controls/private/AbstractPageHeader.qml + src/controls/private/PageRowGlobalToolBarStyleGroup.qml + src/controls/private/PageRowGlobalToolBarUI.qml + src/controls/private/PrivateActionToolButton.qml + src/controls/private/TitlesPageHeader.qml + src/controls/private/ToolBarPageHeader.qml src/controls/Separator.qml src/controls/OverlayDrawer.qml src/controls/OverlaySheet.qml diff --git a/src/controls/AbstractApplicationHeader.qml b/src/controls/AbstractApplicationHeader.qml --- a/src/controls/AbstractApplicationHeader.qml +++ b/src/controls/AbstractApplicationHeader.qml @@ -18,7 +18,7 @@ */ import QtQuick 2.5 -import org.kde.kirigami 2.4 +import org.kde.kirigami 2.5 import "private" import "templates" as T @@ -38,16 +38,28 @@ T.AbstractApplicationHeader { id: root + Theme.inherit: false + Theme.textColor: root.parent.Theme.highlightedTextColor + Theme.backgroundColor: root.parent.Theme.highlightColor + Theme.highlightColor: root.parent.Theme.backgroundColor + background: Rectangle { - color: Theme.highlightColor + color: Theme.backgroundColor EdgeShadow { id: shadow - edge: Qt.TopEdge anchors { right: parent.right left: parent.left top: parent.bottom } + edge: Qt.TopEdge + opacity: (!root.page.header || root.page.header.toString().indexOf("ToolBar") === -1) + Behavior on opacity { + OpacityAnimator { + duration: Units.longDuration + easing.type: Easing.InOutQuad + } + } } } } diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml --- a/src/controls/ApplicationWindow.qml +++ b/src/controls/ApplicationWindow.qml @@ -114,6 +114,7 @@ PageRow { id: __pageStack + globalToolBar.style: Kirigami.ApplicationHeaderStyle.Auto anchors { fill: parent //HACK: workaround a bug in android iOS keyboard management diff --git a/src/controls/OverlayDrawer.qml b/src/controls/OverlayDrawer.qml --- a/src/controls/OverlayDrawer.qml +++ b/src/controls/OverlayDrawer.qml @@ -66,7 +66,15 @@ width: height height: Units.iconSizes.smallMedium source: { - switch(root.edge) { + var edge = root.edge; + if (Qt.application.layoutDirection == Qt.RightToLeft) { + if (edge == Qt.LeftEdge) { + edge = Qt.RightEdge; + } else { + edge = Qt.LeftEdge; + } + } + switch(edge) { case Qt.LeftEdge: return Qt.resolvedUrl("templates/private/MenuIcon.qml"); case Qt.RightEdge: { diff --git a/src/controls/Page.qml b/src/controls/Page.qml --- a/src/controls/Page.qml +++ b/src/controls/Page.qml @@ -207,11 +207,11 @@ * * @since 2.1 */ - readonly property bool isCurrentPage: typeof applicationWindow === "undefined" || !applicationWindow().pageStack + readonly property bool isCurrentPage: typeof applicationWindow === "undefined" || !globalToolBar.row ? true - : (applicationWindow().pageStack.layers.depth > 1 - ? applicationWindow().pageStack.layers.currentItem == root - : applicationWindow().pageStack.currentItem == root) + : (globalToolBar.row.layers.depth > 1 + ? globalToolBar.row.layers.currentItem == root + : globalToolBar.row.currentItem == root) PageActionPropertyGroup { id: actionsGroup @@ -246,8 +246,68 @@ } } - //on material the shadow would bleed over - clip: header !== undefined + //FIXME: on material the shadow would bleed over + clip: root.header != null; + + Component.onCompleted: { + parentChanged(root.parent); + } + onParentChanged: { + if (!parent) { + return; + } + globalToolBar.stack = null; + globalToolBar.row = null; + + if (root.parent.hasOwnProperty("__pageRow")) { + globalToolBar.row = root.parent.__pageRow; + } + if (root.T2.StackView.view) { + globalToolBar.stack = root.T2.StackView.view; + globalToolBar.row = root.T2.StackView.view.parent; + } + if (globalToolBar.row) { + globalToolBar.row.globalToolBar.actualStyleChanged.connect(globalToolBar.syncSource); + globalToolBar.syncSource(); + } + } + + //global top toolbar if we are in a PageRow (in the row or as a layer) + Loader { + id: globalToolBar + 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 + + active: row && (row.globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.ToolBar || globalToolBar.row.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"), + //TODO: find container reliably, remove assumption + {"pageRow": Qt.binding(function() {return row}), + "page": root, + "current": Qt.binding(function() {return stack || !root.parent ? true : row.currentIndex == root.parent.level})}); + } + } + + 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 + } + } + + //bottom action buttons Loader { id: actionButtons z: 9999 @@ -260,9 +320,12 @@ //It should be T2.Page, Qt 5.7 doesn't like it property Item page: root height: item ? item.height : 0 - source: typeof applicationWindow !== "undefined" && ((applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") === 0) || - (applicationWindow().footer && applicationWindow().footer.visible && applicationWindow().footer.toString().indexOf("ToolBarApplicationHeader") === 0)) - ? "" : Qt.resolvedUrl("./private/ActionButton.qml") + active: typeof applicationWindow !== "undefined" && (!globalToolBar.row || globalToolBar.row.globalToolBar.actualStyle != Kirigami.ApplicationHeaderStyle.ToolBar) && + //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/PageRow.qml b/src/controls/PageRow.qml --- a/src/controls/PageRow.qml +++ b/src/controls/PageRow.qml @@ -23,6 +23,8 @@ import QtQuick.Templates 2.0 as T import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.4 +import "private" as Private +import "templates" as KT /** * PageRow implements a row-based navigation model, which can be used @@ -98,6 +100,29 @@ * @since 5.38 */ property bool separatorVisible: true + + /** + * globalToolBar: grouped property + * Controls the appearance of an optional global toolbar for the whole PageRow. + * It's a grouped property comprised of the following properties: + * * style: (Kirigami.ApplicationHeaderStyle) can have the following values: + * ** Auto: depending on application formfactor, it can behave automatically like other values, such as a Breadcrumb on mobile and ToolBar on desktop + * ** Breadcrumb: it will show a breadcrumb of all the page titles in the stack, for easy navigation + * ** Titles: each page will only have its own tile on top + * ** TabBar: the global toolbar will look like a TabBar to select the pages + * ** ToolBar: each page will have the title on top together buttons and menus to represent all of the page actions: not available on Mobile systems. + * ** None: no global toolbar will be shown + * + * * actualStyle: this will represent the actual style of the toolbar: it can be different from style in the case style is Auto + * * showNavigationButtons: if true, forward and backward navigation buttons will be shown on the left of the toolbar + * * minimumHeight: (int) minimum height of the header, which will be resized when scrolling, only in Mobile mode (default: preferredHeight, sliding but no scaling) + property int preferredHeight: (int) the height the toolbar will usually have (default: 42) + property int maximumHeight: (int) The height the toolbar will have in mobile mode when the app is in reachable mode (default: preferredHeight * 1.5) + * * leftReservedSpace: (int, readonly) how many pixels are reserved at the left of the page toolBar (for navigation buttons or drawer handle) + property int rightReservedSpace: (int, readonly) how many pixels are reserved at the right of the page toolbar (drawer handle) + * @since 5.48 + */ + readonly property alias globalToolBar: globalToolBar //END PROPERTIES //BEGIN FUNCTIONS @@ -348,10 +373,23 @@ script: mainView.flick(100, 0) } } + + Private.PageRowGlobalToolBarStyleGroup { + id: globalToolBar + readonly property int leftReservedSpace: globalToolBarUI.item ? globalToolBarUI.item.leftReservedSpace : 0 + readonly property int rightReservedSpace: globalToolBarUI.item ? globalToolBarUI.item.rightReservedSpace : 0 + readonly property int height: globalToolBarUI.height + readonly property Item leftHandleAnchor: globalToolBarUI.item ? globalToolBarUI.item.leftHandleAnchor : null + readonly property Item rightHandleAnchor: globalToolBarUI.item ? globalToolBarUI.item.rightHandleAnchor : null + } + QQC2.StackView { id: layersStack z: 99 - anchors.fill: parent + anchors { + fill: parent + topMargin: depth > 1 && globalToolBarUI.visible ? globalToolBarUI.height: 0 + } initialItem: mainView function clear () { //don't let it kill the main page row @@ -415,6 +453,20 @@ } } + Loader { + id: globalToolBarUI + anchors { + left: parent.left + top: parent.top + right: parent.right + } + z: 100 + active: globalToolBar.actualStyle != ApplicationHeaderStyle.None + visible: active + height: active ? implicitHeight : 0 + source: Qt.resolvedUrl("private/PageRowGlobalToolBarUI.qml"); + } + ListView { id: mainView boundsBehavior: Flickable.StopAtBounds @@ -435,6 +487,7 @@ currentItem.page.forceActiveFocus(); } } + model: ObjectModel { id: pagesLogic readonly property var componentCache: new Array() @@ -569,16 +622,23 @@ readonly property int hint: page && page.implicitWidth ? page.implicitWidth : root.defaultColumnWidth readonly property int roundedHint: Math.floor(root.width/hint) > 0 ? root.width/Math.floor(root.width/hint) : root.width + property T.Control __pageRow: root + + property Item footer property Item page - property Item owner onPageChanged: { if (page) { owner = page.parent; page.parent = container; - page.anchors.fill = container; + page.anchors.left = container.left; + page.anchors.top = container.top; + page.anchors.right = container.right; + page.anchors.bottom = container.bottom; + page.anchors.topMargin = Qt.binding(function() {return globalToolBarUI.height}); } } + property Item owner drag.filterChildren: true onClicked: { switch (mouse.button) { @@ -602,11 +662,13 @@ Separator { z: 999 anchors { - top: parent.top + top: page ? page.top : parent.top bottom: parent.bottom left: parent.left + //ensure a sharp angle + topMargin: -width } - visible: root.separatorVisible && container.level > 0 + visible: root.separatorVisible && mainView.contentX < container.x } states: [ State { diff --git a/src/controls/ToolBarApplicationHeader.qml b/src/controls/ToolBarApplicationHeader.qml --- a/src/controls/ToolBarApplicationHeader.qml +++ b/src/controls/ToolBarApplicationHeader.qml @@ -39,6 +39,7 @@ //FIXME: needs a property difinition to have its own type in qml property string _internal: "" + Component.onCompleted: print("Warning: ToolbarApplicationHeader is deprecated, remove and use the automatic internal toolbar instead.") pageDelegate: Item { id: delegateItem readonly property bool current: __appWindow.pageStack.currentIndex == index diff --git a/src/controls/private/AbstractPageHeader.qml b/src/controls/private/AbstractPageHeader.qml new file mode 100644 --- /dev/null +++ b/src/controls/private/AbstractPageHeader.qml @@ -0,0 +1,42 @@ +/* + * Copyright 2018 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.5 +import QtQuick.Controls 2.0 as Controls +import QtQuick.Layouts 1.2 +import org.kde.kirigami 2.4 + +AbstractApplicationHeader { + anchors.fill: parent + property Item container + property bool current + + minimumHeight: pageRow.globalToolBar.minimumHeight + maximumHeight: pageRow.globalToolBar.maximumHeight + preferredHeight: pageRow.globalToolBar.preferredHeight + implicitHeight: page.y + + leftPadding: Qt.application.layoutDirection == Qt.LeftToRight + ? Math.max(0, pageRow.contentItem.contentX - mapToItem(pageRow.contentItem.contentItem, 0, 0).x + pageRow.globalToolBar.leftReservedSpace) + : Math.max(0, mapToItem(pageRow.contentItem.contentItem, width, 0).x - (pageRow.contentItem.contentX + pageRow.width) + pageRow.globalToolBar.leftReservedSpace) + + rightPadding: Qt.application.layoutDirection == Qt.LeftToRight + ? Math.min(pageRow.globalToolBar.rightReservedSpace, Math.max(0, mapToItem(pageRow.contentItem.contentItem, width, 0).x - (pageRow.contentItem.contentX + pageRow.width) + pageRow.globalToolBar.rightReservedSpace)) + : Math.max(0, pageRow.contentItem.contentX - mapToItem(pageRow.contentItem.contentItem, 0, 0).x + pageRow.globalToolBar.rightReservedSpace) +} diff --git a/src/controls/private/PageRowGlobalToolBarStyleGroup.qml b/src/controls/private/PageRowGlobalToolBarStyleGroup.qml new file mode 100644 --- /dev/null +++ b/src/controls/private/PageRowGlobalToolBarStyleGroup.qml @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.1 +import org.kde.kirigami 2.4 as Kirigami + +QtObject { + id: globalToolBar + property int style: Kirigami.ApplicationHeaderStyle.None + readonly property int actualStyle: { + if (style == Kirigami.ApplicationHeaderStyle.Auto) { + //Legacy: if ApplicationHeader or ToolbarApplicationHeader are in the header or footer, disable the toolbar here + if (typeof applicationWindow !== "undefined" && applicationWindow().header && applicationWindow().header.toString().indexOf("ApplicationHeader") !== -1) { + return Kirigami.ApplicationHeaderStyle.None + } + + //non legacy logic + return (Kirigami.Settings.isMobile + ? (root.wideMode ? Kirigami.ApplicationHeaderStyle.Titles : Kirigami.ApplicationHeaderStyle.Breadcrumb) + : Kirigami.ApplicationHeaderStyle.ToolBar) + } else { + //forbid ToolBar on mobile systems + return Kirigami.Settings.isMobile && style == Kirigami.ApplicationHeaderStyle.ToolBar ? Kirigami.ApplicationHeaderStyle.Breadcrumb : style; + } + } + + property bool showNavigationButtons: style != Kirigami.ApplicationHeaderStyle.TabBar && (!Kirigami.Settings.isMobile || Qt.platform.os == "ios") + + property int minimumHeight: 0 + property int preferredHeight: 42 + property int maximumHeight: preferredHeight +} diff --git a/src/controls/private/PageRowGlobalToolBarUI.qml b/src/controls/private/PageRowGlobalToolBarUI.qml new file mode 100644 --- /dev/null +++ b/src/controls/private/PageRowGlobalToolBarUI.qml @@ -0,0 +1,96 @@ +/* + * Copyright 2018 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.2 +import org.kde.kirigami 2.4 as Kirigami +import "../templates/private" as TemplatesPrivate + + +Kirigami.AbstractApplicationHeader { + id: header + readonly property int leftReservedSpace: buttonsLayout.visible && buttonsLayout.visibleChildren.length > 1 ? buttonsLayout.width : 0 + readonly property int rightReservedSpace: rightHandleAnchor.visible ? backButton.background.implicitHeight : 0 + + readonly property alias leftHandleAnchor: leftHandleAnchor + readonly property alias rightHandleAnchor: rightHandleAnchor + + height: visible ? implicitHeight : 0 + minimumHeight: globalToolBar.minimumHeight + preferredHeight: globalToolBar.preferredHeight + maximumHeight: globalToolBar.maximumHeight + + RowLayout { + anchors.fill: parent + spacing: 0 + RowLayout { + id: buttonsLayout + + visible: globalToolBar.showNavigationButtons && globalToolBar.actualStyle != Kirigami.ApplicationHeaderStyle.None + + Item { + id: leftHandleAnchor + visible: typeof applicationWindow() !== "undefined" && applicationWindow().globalDrawer.handleVisible && applicationWindow().globalDrawer.handle.handleAnchor == leftHandleAnchor + Layout.preferredWidth: backButton.background.implicitHeight + Layout.preferredHeight: backButton.background.implicitHeight + } + TemplatesPrivate.BackButton { + id: backButton + Layout.leftMargin: leftHandleAnchor.visible ? 0 : Kirigami.Units.smallSpacing + Layout.preferredWidth: background.implicitHeight + Layout.preferredHeight: background.implicitHeight + } + TemplatesPrivate.ForwardButton { + Layout.preferredWidth: background.implicitHeight + Layout.preferredHeight: background.implicitHeight + } + Kirigami.Separator { + Layout.preferredHeight: parent.parent.height * 0.6 + //FIXME: hacky + opacity: buttonsLayout.visibleChildren.length > 1 + } + } + Loader { + id: breadcrumbLoader + Layout.fillWidth: true + Layout.fillHeight: true + + active: globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.TabBar || globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.Breadcrumb + + //TODO: different implementation? + sourceComponent: Kirigami.ApplicationHeader { + minimumHeight: height + preferredHeight: height + maximumHeight: height + backButtonEnabled: false + anchors.fill:parent + background.visible: false + headerStyle: globalToolBar.style + } + } + Item { + id: rightHandleAnchor + visible: typeof applicationWindow() !== "undefined" && applicationWindow().contextDrawer && applicationWindow().contextDrawer.handleVisible && applicationWindow().contextDrawer.handle.handleAnchor == rightHandleAnchor + Layout.preferredWidth: backButton.background.implicitHeight + Layout.preferredHeight: backButton.background.implicitHeight + } + } + background.visible: breadcrumbLoader.active +} + diff --git a/src/controls/private/PrivateActionToolButton.qml b/src/controls/private/PrivateActionToolButton.qml --- a/src/controls/private/PrivateActionToolButton.qml +++ b/src/controls/private/PrivateActionToolButton.qml @@ -25,15 +25,16 @@ Controls.ToolButton { id: control - implicitWidth: showText && ( kirigamiAction ? kirigamiAction.text.length > 0 : text.length > 0) ? Math.max(background.implicitWidth, control.background.contentWidth) : implicitHeight + implicitWidth: showText && ( kirigamiAction ? kirigamiAction.text.length > 0 : text.length > 0) ? Math.max(layout.implicitWidth + Units.largeSpacing*2, background.implicitWidth) : implicitHeight implicitHeight: background.implicitHeight Theme.colorSet: Theme.Button - Theme.inherit: false + Theme.inherit: kirigamiAction && kirigamiAction.icon.color.a === 0 Theme.backgroundColor: kirigamiAction && kirigamiAction.icon.color.a ? kirigamiAction.icon.color : undefined - Theme.textColor: kirigamiAction && kirigamiAction.icon.color.a ? Theme.highlightedTextColor : undefined + Theme.textColor: kirigamiAction && !flat && kirigamiAction.icon.color.a ? Theme.highlightedTextColor : undefined hoverEnabled: true + flat: true //TODO: replace with upstream action when we depend on Qt 5.10 property Action kirigamiAction property bool showText: true @@ -72,11 +73,11 @@ contentItem: MouseArea { hoverEnabled: true onPressed: mouse.accepted = false - Theme.colorSet: checked || (!control.flat && control.kirigamiAction && control.kirigamiAction.icon.color.a) ? Theme.Selection : Theme.Button - Theme.inherit: false + Theme.colorSet: checked && (!control.flat && control.kirigamiAction && control.kirigamiAction.icon.color.a) ? Theme.Selection : Theme.Button + Theme.inherit: control.kirigamiAction && Theme.colorSet != Theme.Selection && control.kirigamiAction.icon.color.a == 0 RowLayout { id: layout - onImplicitWidthChanged: control.background.contentWidth = implicitWidth + 16 + anchors.centerIn: parent Icon { id: mainIcon diff --git a/src/controls/private/TitlesPageHeader.qml b/src/controls/private/TitlesPageHeader.qml new file mode 100644 --- /dev/null +++ b/src/controls/private/TitlesPageHeader.qml @@ -0,0 +1,47 @@ +/* + * Copyright 2018 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.5 +import QtQuick.Controls 2.0 as Controls +import QtQuick.Layouts 1.2 +import org.kde.kirigami 2.4 + + + +AbstractPageHeader { + id: root + + Heading { + id: title + anchors.fill: parent + leftPadding: Units.largeSpacing + opacity: root.current ? 1 : 0.4 + maximumLineCount: 1 + color: Theme.textColor + elide: Text.ElideRight + font.pointSize: -1 + font.pixelSize: Math.max(1, height*0.7) + text: page ? page.title : "" + MouseArea { + anchors.fill: parent + onClicked: page.forceActiveFocus() + } + } +} + diff --git a/src/controls/private/ToolBarPageHeader.qml b/src/controls/private/ToolBarPageHeader.qml new file mode 100644 --- /dev/null +++ b/src/controls/private/ToolBarPageHeader.qml @@ -0,0 +1,119 @@ +/* + * Copyright 2018 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.5 +import QtQuick.Controls 2.0 as Controls +import QtQuick.Layouts 1.2 +import org.kde.kirigami 2.4 + + + +AbstractPageHeader { + id: root + + 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 + } + + 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 : "" + } + } + + 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 + } + + 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 { + 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 + } + + 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 @@ -41,19 +41,25 @@ property int minimumHeight: 0 property int preferredHeight: 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 readonly property int paintedHeight: headerItem.y + headerItem.height - 1 LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true + property int leftPadding: 0 + property int topPadding: 0 + property int rightPadding: 0 + property int bottomPadding: 0 //FIXME: remove property QtObject __appWindow: applicationWindow(); anchors { left: parent.left right: parent.right } - height: preferredHeight + implicitHeight: preferredHeight /** * background: Item @@ -68,25 +74,22 @@ background.anchors.fill = headerItem; } + onMinimumHeightChanged: implicitHeight = preferredHeight; + onPreferredHeightChanged: implicitHeight = preferredHeight; + opacity: height > 0 ? 1 : 0 - Behavior on opacity { - OpacityAnimator { - duration: Units.longDuration - easing.type: Easing.InOutQuad - } - } - Behavior on height { - enabled: __appWindow.pageStack.currentItem && __appWindow.pageStack.currentItem.flickable && !__appWindow.pageStack.currentItem.flickable.moving + Behavior on implicitHeight { + enabled: root.page && root.page.flickable && !root.page.flickable.moving NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } Connections { target: __appWindow - onControlsVisibleChanged: root.height = __appWindow.controlsVisible ? root.preferredHeight : 0; + onControlsVisibleChanged: root.implicitHeight = __appWindow.controlsVisible ? root.preferredHeight : 0; } Item { @@ -98,73 +101,77 @@ bottom: parent.bottom } - height: __appWindow.reachableMode && __appWindow.reachableModeEnabled ? root.maximumHeight : root.preferredHeight + height: __appWindow.reachableMode && __appWindow.reachableModeEnabled ? root.maximumHeight : (root.minimumHeight > 0 ? Math.max(root.height, root.minimumHeight) : root.preferredHeight) Connections { id: headerSlideConnection - target: __appWindow.pageStack.currentItem ? __appWindow.pageStack.currentItem.flickable : null + target: root.page ? root.page.flickable : null property int oldContentY onContentYChanged: { if (!Settings.isMobile || !__appWindow.controlsVisible || - !__appWindow.pageStack.currentItem || - __appWindow.pageStack.currentItem.flickable.atYBeginning || - __appWindow.pageStack.currentItem.flickable.atYEnd) { + !root.page || + root.page.flickable.atYBeginning || + root.page.flickable.atYEnd) { return; //if moves but not dragging, just update oldContentY - } else if (!__appWindow.pageStack.currentItem.flickable.dragging) { - oldContentY = __appWindow.pageStack.currentItem.flickable.contentY; + } else if (!root.page.flickable.dragging) { + oldContentY = root.page.flickable.contentY; return; - } - + } if (__appWindow.wideScreen || !Settings.isMobile) { - root.height = root.preferredHeight; + root.implicitHeight = root.preferredHeight; } else { var oldHeight = root.height; - root.height = Math.max(root.minimumHeight, + root.implicitHeight = Math.max(root.minimumHeight, Math.min(root.preferredHeight, - root.height + oldContentY - __appWindow.pageStack.currentItem.flickable.contentY)); - + root.height + oldContentY - root.page.flickable.contentY)); + //if the height is changed, use that to simulate scroll if (oldHeight != height) { - __appWindow.pageStack.currentItem.flickable.contentY = oldContentY; + root.page.flickable.contentY = oldContentY; } else { - oldContentY = __appWindow.pageStack.currentItem.flickable.contentY; + oldContentY = root.page.flickable.contentY; } } } onMovementEnded: { if (__appWindow.wideScreen || !Settings.isMobile) { return; } - if (root.height > (root.preferredHeight - root.minimumHeight)/2 ) { - root.height = root.preferredHeight; + if (root.height > root.minimumHeight + (root.preferredHeight - root.minimumHeight)/2 ) { + root.implicitHeight = root.preferredHeight; } else { - root.height = root.minimumHeight; + root.implicitHeight = root.minimumHeight; } } } Connections { - target: __appWindow.pageStack + target: pageRow onCurrentItemChanged: { - if (!__appWindow.pageStack.currentItem) { + if (!root.page) { return; } - if (__appWindow.pageStack.currentItem.flickable) { - headerSlideConnection.oldContentY = __appWindow.pageStack.currentItem.flickable.contentY; + if (root.page.flickable) { + headerSlideConnection.oldContentY = root.page.flickable.contentY; } else { headerSlideConnection.oldContentY = 0; } - root.height = root.preferredHeight; + root.implicitHeight = root.preferredHeight; } } Item { id: mainItem + clip: childrenRect.width > width anchors { fill: parent + leftMargin: root.leftPadding + topMargin: root.topPadding + rightMargin: root.rightPadding + bottomMargin: root.bottomPadding } } } diff --git a/src/controls/templates/ApplicationHeader.qml b/src/controls/templates/ApplicationHeader.qml --- a/src/controls/templates/ApplicationHeader.qml +++ b/src/controls/templates/ApplicationHeader.qml @@ -95,7 +95,8 @@ color: header.background && header.background.color && header.background.color == Theme.highlightColor ? Theme.highlightedTextColor : Theme.textColor elide: Text.ElideRight text: page ? page.title : "" - font.pointSize: Math.max(1, titleList.height / (1.6 * Units.devicePixelRatio)) + font.pointSize: -1 + font.pixelSize: Math.max(1, titleList.height * 0.7) verticalAlignment: Text.AlignVCenter wrapMode: Text.NoWrap Rectangle { @@ -206,10 +207,10 @@ } } Repeater { - model: __appWindow.pageStack.layers.depth -1 + model: pageRow.layers.depth -1 delegate: Loader { sourceComponent: header.pageDelegate - readonly property Page page: __appWindow.pageStack.layers.get(modelData+1) + readonly property Page page: pageRow.layers.get(modelData+1) readonly property bool current: true; Component.onCompleted: stack.push(this) Component.onDestruction: stack.pop() @@ -233,7 +234,7 @@ Flickable { id: titleList - readonly property bool wideMode: typeof __appWindow.pageStack.wideMode !== "undefined" ? __appWindow.pageStack.wideMode : __appWindow.wideMode + readonly property bool wideMode: header.headerStyle != ApplicationHeaderStyle.Breadcrumb && typeof pageRow.wideMode !== "undefined" ? pageRow.wideMode : __appWindow.wideMode property int internalHeaderStyle: header.headerStyle == ApplicationHeaderStyle.Auto ? (titleList.wideMode ? ApplicationHeaderStyle.Titles : ApplicationHeaderStyle.Breadcrumb) : header.headerStyle //if scrolling the titlebar should scroll also the pages and vice versa property bool scrollingLocked: (header.headerStyle == ApplicationHeaderStyle.Titles || titleList.wideMode) @@ -251,7 +252,7 @@ contentWidth: contentItem.width contentHeight: height - readonly property int currentIndex: __appWindow.pageStack && __appWindow.pageStack.currentIndex !== undefined ? __appWindow.pageStack.currentIndex : 0 + readonly property int currentIndex: pageRow && pageRow.currentIndex !== undefined ? pageRow.currentIndex : 0 readonly property int count: mainRepeater.count function gotoIndex(idx) { @@ -280,18 +281,18 @@ id: contentXSyncTimer interval: 0 onTriggered: { - titleList.contentX = __appWindow.pageStack.contentItem.contentX - __appWindow.pageStack.contentItem.originX + titleList.originX; + titleList.contentX = pageRow.contentItem.contentX - pageRow.contentItem.originX + titleList.originX; } } onCountChanged: contentXSyncTimer.restart(); onCurrentIndexChanged: gotoIndex(currentIndex); onModelChanged: gotoIndex(currentIndex); onContentWidthChanged: gotoIndex(currentIndex); onContentXChanged: { - if (movingHorizontally && !titleList.scrollMutex && titleList.scrollingLocked && !__appWindow.pageStack.contentItem.moving) { + if (movingHorizontally && !titleList.scrollMutex && titleList.scrollingLocked && !pageRow.contentItem.moving) { titleList.scrollMutex = true; - __appWindow.pageStack.contentItem.contentX = titleList.contentX - titleList.originX + __appWindow.pageStack.contentItem.originX; + pageRow.contentItem.contentX = titleList.contentX - titleList.originX + pageRow.contentItem.originX; titleList.scrollMutex = false; } } @@ -301,14 +302,14 @@ onMovementEnded: { if (titleList.scrollingLocked) { //this will trigger snap as well - __appWindow.pageStack.contentItem.flick(0,0); + pageRow.contentItem.flick(0,0); } } onFlickEnded: movementEnded(); NumberAnimation { id: scrollTopAnimation - target: __appWindow.pageStack.currentItem && __appWindow.pageStack.currentItem.flickable ? __appWindow.pageStack.currentItem.flickable : null + target: pageRow.currentItem && pageRow.currentItem.flickable ? pageRow.currentItem.flickable : null property: "contentY" to: 0 duration: Units.longDuration @@ -320,7 +321,7 @@ spacing: 0 Repeater { id: mainRepeater - model: __appWindow.pageStack.depth + model: pageRow.depth delegate: MouseArea { id: delegate readonly property int currentIndex: index @@ -330,26 +331,26 @@ width: { //more columns shown? if (titleList.scrollingLocked && delegateLoader.page) { - return delegateLoader.page.width - (index == 0 ? navButtons.width : 0) - (index == __appWindow.pageStack.depth-1 ? stack.anchors.rightMargin : 0); + return delegateLoader.page.width - (index == 0 ? navButtons.width : 0) - (index == pageRow.depth-1 ? stack.anchors.rightMargin : 0); } else { return Math.min(titleList.width, delegateLoader.implicitWidth + Units.smallSpacing); } } height: titleList.height onClicked: { - if (__appWindow.pageStack.currentIndex == modelData) { + if (pageRow.currentIndex == modelData) { //scroll up if current otherwise make current - if (!__appWindow.pageStack.currentItem.flickable) { + if (!pageRow.currentItem.flickable) { return; } - if (__appWindow.pageStack.currentItem.flickable.contentY > -__appWindow.header.height) { - scrollTopAnimation.to = -__appWindow.pageStack.currentItem.flickable.topMargin; + if (pageRow.currentItem.flickable.contentY > -__appWindow.header.height) { + scrollTopAnimation.to = -pageRow.currentItem.flickable.topMargin; scrollTopAnimation.running = true; } } else { - __appWindow.pageStack.currentIndex = modelData; + pageRow.currentIndex = modelData; } } @@ -366,22 +367,22 @@ sourceComponent: header.pageDelegate - readonly property Page page: __appWindow.pageStack.get(modelData) + readonly property Page page: pageRow.get(modelData) //NOTE: why not use ListViewCurrentIndex? because listview itself resets //currentIndex in some situations (since here we are using an int as a model, //even more often) so the property binding gets broken - readonly property bool current: __appWindow.pageStack.currentIndex == index + readonly property bool current: pageRow.currentIndex == index readonly property int index: parent.currentIndex readonly property var modelData: parent.currentModelData } } } } Connections { - target: titleList.scrollingLocked ? __appWindow.pageStack.contentItem : null + target: titleList.scrollingLocked ? pageRow.contentItem : null onContentXChanged: { if (!titleList.dragging && !titleList.movingHorizontally && !titleList.scrollMutex) { - titleList.contentX = __appWindow.pageStack.contentItem.contentX - __appWindow.pageStack.contentItem.originX + titleList.originX; + titleList.contentX = pageRow.contentItem.contentX - pageRow.contentItem.originX + titleList.originX; } } } diff --git a/src/controls/templates/OverlayDrawer.qml b/src/controls/templates/OverlayDrawer.qml --- a/src/controls/templates/OverlayDrawer.qml +++ b/src/controls/templates/OverlayDrawer.qml @@ -105,14 +105,22 @@ id: drawerHandle z: root.modal ? applicationWindow().overlay.z + (root.position > 0 ? +1 : -1) : root.background.parent.z + 1 preventStealing: true - hoverEnabled: desktopMode + hoverEnabled: handleAnchor parent: applicationWindow().overlay.parent + property Item handleAnchor: (!Settings.isMobile && applicationWindow().pageStack && applicationWindow().pageStack.globalToolBar && applicationWindow().pageStack.globalToolBar.actualStyle != ApplicationHeaderStyle.None) + ? (root.edge == Qt.LeftEdge + ? applicationWindow().pageStack.globalToolBar.leftHandleAnchor + : applicationWindow().pageStack.globalToolBar.rightHandleAnchor) + : (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") !== -1 ? applicationWindow().header : null) + //FIXME: temporary solution, we need a scenepostracker item + onHandleAnchorChanged: { + handleAnchor.parent.yChanged.connect(handleAnchor.yChanged); + } property int startX property int mappedStartX - property bool desktopMode: applicationWindow() && applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") !== -1 - enabled: root.handleVisible && root.modal + enabled: root.handleVisible onPressed: { root.peeking = true; @@ -150,19 +158,17 @@ x: { switch(root.edge) { case Qt.LeftEdge: - return root.background.width * root.position; + return root.background.width * root.position + Units.smallSpacing; case Qt.RightEdge: - return drawerHandle.parent.width - (root.background.width * root.position) - width; + return drawerHandle.parent.width - (root.background.width * root.position) - width - Units.smallSpacing; default: return 0; } } + y: handleAnchor && anchors.bottom ? handleAnchor.parent.mapToItem(root.contentItem, 0, handleAnchor.y).y : 0 anchors { - top: drawerHandle.desktopMode ? parent.top : undefined - - bottom: drawerHandle.desktopMode ? undefined : parent.bottom - + bottom: drawerHandle.handleAnchor ? undefined : parent.bottom bottomMargin: { if (!applicationWindow()) { return; @@ -207,8 +213,8 @@ } visible: root.enabled && (root.edge == Qt.LeftEdge || root.edge == Qt.RightEdge) - width: Units.iconSizes.medium + Units.smallSpacing*2 - height: width + width: handleAnchor ? handleAnchor.width : Units.iconSizes.medium + Units.smallSpacing*2 + height: handleAnchor ? handleAnchor.height : width opacity: root.handleVisible ? 1 : 0 Behavior on opacity { NumberAnimation { diff --git a/src/controls/templates/private/BackButton.qml b/src/controls/templates/private/BackButton.qml --- a/src/controls/templates/private/BackButton.qml +++ b/src/controls/templates/private/BackButton.qml @@ -26,8 +26,8 @@ Controls.ToolButton { id: button - enabled: !Settings.isMobile && (__appWindow.pageStack.currentIndex > 0 || applicationWindow().pageStack.contentItem.contentX > 0) - visible: applicationWindow().pageStack.contentItem.contentWidth > applicationWindow().pageStack.width + enabled: applicationWindow().pageStack.layers.depth > 1 ||applicationWindow().pageStack.currentIndex > 0 || applicationWindow().pageStack.contentItem.contentX > 0 + visible: applicationWindow().pageStack.layers.depth > 1 || applicationWindow().pageStack.contentItem.contentWidth > applicationWindow().pageStack.width width: height height: parent.height @@ -43,7 +43,6 @@ width: Math.min(parent.width, Units.iconSizes.smallMedium) height: width opacity: parent.enabled ? 1 : 0.6 - selected: header.background && header.background.color && header.background.color == Theme.highlightColor source: (LayoutMirroring.enabled ? "go-previous-symbolic-rtl" : "go-previous-symbolic") } Controls.ToolTip { diff --git a/src/controls/templates/private/ForwardButton.qml b/src/controls/templates/private/ForwardButton.qml --- a/src/controls/templates/private/ForwardButton.qml +++ b/src/controls/templates/private/ForwardButton.qml @@ -27,9 +27,8 @@ id: button property Flickable headerFlickable - //visible: headerFlickable.internalHeaderStyle == ApplicationHeaderStyle.Titles && !applicationWindow().pageStack.contentItem.atXEnd && applicationWindow().pageStack.layers.depth < 2 - enabled: __appWindow.pageStack.currentIndex < __appWindow.pageStack.depth-1 || !applicationWindow().pageStack.contentItem.atXEnd - visible: applicationWindow().pageStack.contentItem.contentWidth > applicationWindow().pageStack.width + enabled: applicationWindow().pageStack.currentIndex < applicationWindow().pageStack.depth-1 || !applicationWindow().pageStack.contentItem.atXEnd + visible: applicationWindow().pageStack.layers.depth == 1 && applicationWindow().pageStack.contentItem.contentWidth > applicationWindow().pageStack.width width: height height: parent.height @@ -40,7 +39,6 @@ width: Math.min(parent.width, Units.iconSizes.smallMedium) height: width opacity: parent.enabled ? 1 : 0.6 - selected: header.background && header.background.color && header.background.color == Theme.highlightColor source: (LayoutMirroring.enabled ? "go-next-symbolic-rtl" : "go-next-symbolic") } Controls.ToolTip { diff --git a/src/enums.h b/src/enums.h --- a/src/enums.h +++ b/src/enums.h @@ -32,7 +32,9 @@ Auto = 0, Breadcrumb, Titles, - TabBar + TabBar, + ToolBar, ///@since 5.48 + None ///@since 5.48 }; }; 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 @@ -37,9 +37,12 @@ T.AbstractApplicationHeader { id: root + Theme.inherit: false + background: Rectangle { color: Theme.backgroundColor Separator { + visible: (!root.page.header || root.page.header.toString().indexOf("ToolBar") === -1) anchors { left: parent.left right: parent.right diff --git a/src/styles/org.kde.desktop/OverlayDrawer.qml b/src/styles/org.kde.desktop/OverlayDrawer.qml --- a/src/styles/org.kde.desktop/OverlayDrawer.qml +++ b/src/styles/org.kde.desktop/OverlayDrawer.qml @@ -44,7 +44,7 @@ anchors.fill: parent DropShadow { - visible: !parent.parent.desktopMode || root.handle.pressed || (root.modal && root.position > 0) + visible: !parent.parent.handleAnchor || root.handle.pressed || (root.modal && root.position > 0) anchors.fill: handleGraphics horizontalOffset: 0 verticalOffset: Units.devicePixelRatio @@ -57,16 +57,24 @@ id: handleGraphics anchors.centerIn: parent color: root.handle.pressed ? Theme.highlightColor : Theme.backgroundColor - width: Units.iconSizes.smallMedium + Units.smallSpacing * 2 + width: Units.iconSizes.medium height: width radius: Units.devicePixelRatio*2 - border.color: parent.parent.desktopMode && parent.parent.containsMouse ? Theme.highlightColor : "transparent" + border.color: parent.parent.handleAnchor && parent.parent.containsMouse ? Theme.hoverColor : "transparent" Loader { anchors.centerIn: parent width: height height: Units.iconSizes.smallMedium source: { - switch(root.edge) { + var edge = root.edge; + if (Qt.application.layoutDirection == Qt.RightToLeft) { + if (edge == Qt.LeftEdge) { + edge = Qt.RightEdge; + } else { + edge = Qt.LeftEdge; + } + } + switch(edge) { case Qt.LeftEdge: return Qt.resolvedUrl("../../templates/private/MenuIcon.qml"); case Qt.RightEdge: { @@ -114,7 +122,7 @@ //default to a sidebar in desktop mode modal: true drawerOpen: !modal - closePolicy: modal ? Popup.CloseOnEscape | Popup.CloseOnPressOutside : Popup.NoAutoClose + closePolicy: modal ? Popup.CloseOnEscape | Popup.CloseOnReleaseOutside : Popup.NoAutoClose handleVisible: (modal || !drawerOpen) && (typeof(applicationWindow)===typeof(Function) && applicationWindow() ? applicationWindow().controlsVisible : true) onPositionChanged: {