diff --git a/src/controls/ActionToolBar.qml b/src/controls/ActionToolBar.qml index e446231d..09184877 100644 --- a/src/controls/ActionToolBar.qml +++ b/src/controls/ActionToolBar.qml @@ -1,189 +1,206 @@ /* * 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.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.4 as Controls import org.kde.kirigami 2.5 as Kirigami import "private" /** * This is a simple toolbar built out of a list of actions * each action is represented by a ToolButton, those that won't fit * the size will go in a menu under a button with the overflow ... icon * * @inherits Item * @since 2.5 */ Item { id: root /** * actions: list * if the card should provide clickable actions, put them in this property, * they will be put in the footer as a list of ToolButtons plus an optional * overflow menu, when not all of them will fit in the available Card width. */ property list actions /** * actions: hiddenActions * This list of actions is for those you always want in the menu, even if there * is enough space. * @since 2.6 */ property list hiddenActions /** * flat: bool * Wether we want our buttons to have a flat appearance. Default: true */ property bool flat: true /** * display: enum * This controls the label position regarding the icon, is the same value to control individual Button components, * permitted values are: * * Button.IconOnly * * Button.TextOnly * * Button.TextBesideIcon * * Button.TextUnderIcon */ property int display: Controls.Button.TextBesideIcon + property int alignment: Qt.AlignLeft + /** * position enum * This property holds the position of the toolbar. * if this ActionToolBar is the contentItem of a QQC2 Toolbar, the position is binded to the ToolBar's position * * permitted values are: * *ToolBar.Header: The toolbar is at the top, as a window or page header. * *ToolBar.Footer: The toolbar is at the bottom, as a window or page footer. */ property int position: parent && parent.hasOwnProperty("position") ? parent.position : Controls.ToolBar.Header implicitHeight: actionsLayout.implicitHeight implicitWidth: actionsLayout.implicitWidth Layout.minimumWidth: moreButton.implicitWidth + Layout.maximumWidth: placeholderLayout.implicitWidth + moreButton.width RowLayout { id: actionsLayout anchors.fill: parent - //anchors.rightMargin: moreButton.width - - spacing: Kirigami.Units.smallSpacing - property var overflowSet: [] + spacing: 0 - // TODO use Array.findIndex once we depend on Qt 5.9 - function findIndex(array, cb) { - for (var i = 0, length = array.length; i < length; ++i) { - if (cb(array[i])) { - return i; - } - } - return -1; + Item { + Layout.fillWidth: root.alignment == Qt.AlignRight || root.alignment == Qt.AlignHCenter || root.alignment == Qt.AlignCenter; + Layout.fillHeight: true } - function isActionVisible(action) { - var index = actionsLayout.findIndex(actionsLayout.overflowSet, function(act){return act === action}); - if (index === -1) { - index = actionsLayout.findIndex(root.hiddenActions, function(act){return act === action}); - if (index === -1) { - return true - } - } - return false - } + Repeater { + model: placeholderLayout.visibleActions - RowLayout { - Layout.minimumWidth: 0 - Layout.fillHeight: true - Repeater { - model: root.actions - delegate: PrivateActionToolButton { - id: actionDelegate - flat: root.flat - opacity: x + width <= parent.width - enabled: opacity > 0 && modelData.enabled - - display: root.display - visible: !modelData.hasOwnProperty("visible") || modelData.visible - Layout.fillWidth: false - Layout.alignment: Qt.AlignVCenter - Layout.minimumWidth: implicitWidth - kirigamiAction: modelData - onOpacityChanged: updateOverflowSet() - function updateOverflowSet() { - var index = actionsLayout.findIndex(actionsLayout.overflowSet, function(act) { - return act === modelData}); - - if ((opacity > 0 || (modelData.hasOwnProperty("visible") || !modelData.visible)) && index > -1) { - actionsLayout.overflowSet.splice(index, 1); - } else if (opacity === 0 && (!modelData.hasOwnProperty("visible") || modelData.visible) && index === -1) { - actionsLayout.overflowSet.push(modelData); - } - actionsLayout.overflowSetChanged(); - } - Connections { - target: modelData - ignoreUnknownSignals: !modelData.hasOwnProperty("visible") - onVisibleChanged: actionDelegate.updateOverflowSet(); - } - Component.onCompleted: { - actionDelegate.updateOverflowSet(); - } - } + delegate: PrivateActionToolButton { + id: actionDelegate + + Layout.alignment: Qt.AlignVCenter + Layout.minimumWidth: implicitWidth + // Use rightMargin instead of spacing on the layout to prevent spacer items + // from creating useless spacing + Layout.rightMargin: Kirigami.Units.smallSpacing + + flat: root.flat && !modelData.icon.color.a + display: root.display + kirigamiAction: modelData } } Item { - Layout.fillWidth: true - visible: root.Layout.fillWidth + Layout.fillWidth: root.alignment == Qt.AlignLeft || root.alignment == Qt.AlignHCenter || root.alignment == Qt.AlignCenter; + Layout.fillHeight: true } PrivateActionToolButton { id: moreButton Layout.alignment: Qt.AlignRight - visible: hiddenActions.length > 0 || actionsLayout.overflowSet.length > 0 + visible: hiddenActions.length > 0 || placeholderLayout.hiddenActions.length > 0 showMenuArrow: false kirigamiAction: Kirigami.Action { icon.name: "overflow-menu" children: Array.prototype.map.call(root.actions, function (i) { return i }).concat(Array.prototype.map.call(hiddenActions, function (i) { return i })) } menu.submenuComponent: ActionsMenu { Binding { target: parentItem property: "visible" - value: !actionsLayout.isActionVisible(parentAction) && (parentAction.visible === undefined || parentAction.visible) + value: placeholderLayout.visibleActions.indexOf(parentAction) == -1 && + (parentAction.visible === undefined || parentAction.visible) } } menu.itemDelegate: ActionMenuItem { - visible: !actionsLayout.isActionVisible(action) && (action.visible === undefined || action.visible) + visible: placeholderLayout.visibleActions.indexOf(action) == -1 && + (action.visible === undefined || action.visible) + } + } + } + + RowLayout { + id: placeholderLayout + enabled: false + opacity: 0 // Cannot use visible: false because then relayout doesn't happen correctly + spacing: Kirigami.Units.smallSpacing + + property var visibleActions: [] + property var hiddenActions: [] + property real layoutWidth: root.width - moreButton.width - Kirigami.Units.smallSpacing + + Repeater { + id: placeholderRepeater + model: root.actions + + PrivateActionToolButton { + flat: root.flat && !modelData.icon.color.a + display: root.display + visible: modelData.visible === undefined || modelData.visible + kirigamiAction: modelData + + property bool actionVisible: x + width < placeholderLayout.layoutWidth + } + } + + Component.onCompleted: Qt.callLater(updateVisibleActions) + onWidthChanged: Qt.callLater(updateVisibleActions) + + function updateVisibleActions() { + var visible = [] + var hidden = [] + + if (root.width >= placeholderLayout.width + moreButton.width + Kirigami.Units.smallSpacing) { + visibleActions = Array.prototype.map.call(root.actions, function(item) { return item }) + hiddenActions = [] + return } + + for (var i = 0; i < root.actions.length; ++i) { + var item = placeholderRepeater.itemAt(i) + if (item.actionVisible) { + visible.push(item.kirigamiAction) + } else { + hidden.push(item.kirigamiAction) + + } + } + + visibleActions = visible + hiddenActions = hidden } } + + onWidthChanged: Qt.callLater(placeholderLayout.updateVisibleActions) } diff --git a/src/controls/private/globaltoolbar/ToolBarPageHeader.qml b/src/controls/private/globaltoolbar/ToolBarPageHeader.qml index 76146720..b60c1fb1 100644 --- a/src/controls/private/globaltoolbar/ToolBarPageHeader.qml +++ b/src/controls/private/globaltoolbar/ToolBarPageHeader.qml @@ -1,124 +1,101 @@ /* * 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 +import org.kde.kirigami 2.5 import "../" as Private AbstractPageHeader { id: root - implicitWidth: titleLoader.implicitWidth.width/2 + buttonTextMetrics.collapsedButtonsWidth - Layout.minimumWidth: ctxActionsButton.width*4 + implicitWidth: titleLoader.implcitWidth + toolBar.implicitWidth + Units.smallSpacing * 3 - Layout.preferredHeight: Math.max(titleLoader.implicitHeight, actionsLayout.implicitHeight) + Units.smallSpacing * 2 + Layout.preferredHeight: Math.max(titleLoader.implicitHeight, toolBar.implicitHeight) + Units.smallSpacing * 2 MouseArea { anchors.fill: parent onClicked: page.forceActiveFocus() } Loader { id: titleLoader anchors { verticalCenter: parent.verticalCenter left: parent.left - right: parent.right - leftMargin: Units.largeSpacing + leftMargin: Units.smallSpacing rightMargin: Units.smallSpacing } visible: pageRow.globalToolBar.toolbarActionAlignment == Qt.AlignRight && width > item.Layout.minimumWidth sourceComponent: page ? page.titleDelegate : null - - Layout.preferredWidth: item - ? (item.Layout.preferredWidth > 0 ? item.Layout.preferredWidth : item.implicitWidth) - : 0 } + ActionToolBar { + id: toolBar - 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 { + left: titleLoader.right + leftMargin: Units.smallSpacing + right: parent.right + rightMargin: Units.smallSpacing verticalCenter: parent.verticalCenter - left: parent.left - right: ctxActionsButton.visible ? ctxActionsButton.left : parent.right } - readonly property bool toobig: root.width - root.leftPadding - root.rightPadding - titleLoader.implicitWidth - Units.gridUnit < buttonTextMetrics.requiredWidth - - Item { - Layout.fillWidth: true - visible: pageRow.globalToolBar.toolbarActionAlignment != Qt.AlignLeft - } - Private.PrivateActionToolButton { - Layout.alignment: Qt.AlignVCenter - kirigamiAction: page && page.actions ? page.actions.left : null - showText: !parent.toobig - } - Private.PrivateActionToolButton { - Layout.alignment: Qt.AlignVCenter - Layout.rightMargin: Units.smallSpacing - kirigamiAction: page && page.actions ? page.actions.main : null - showText: !parent.toobig - } - Private.PrivateActionToolButton { - Layout.alignment: Qt.AlignVCenter - kirigamiAction: page && page.actions ? page.actions.right : null - showText: !parent.toobig - } - Item { - Layout.fillWidth: true - visible: pageRow.globalToolBar.toolbarActionAlignment != Qt.AlignRight + alignment: pageRow.globalToolBar.toolbarActionAlignment + display: buttonTextMetrics.toobig ? Controls.Button.IconOnly : Controls.Button.TextBesideIcon + + actions: { + var result = [] + + if (page) { + if (page.actions.main) { + result.push(page.actions.main) + } + if (page.actions.left) { + result.push(page.actions.left) + } + if (page.actions.right) { + result.push(page.actions.right) + } + if (page.actions.contextualActions.length > 0 && !buttonTextMetrics.toobig) { + result = result.concat(Array.prototype.map.call(page.actions.contextualActions, function(item) { return item })) + } + } + + return result } + + hiddenActions: page && buttonTextMetrics.toobig ? page.actions.contextualActions : [] } - Private.PrivateActionToolButton { - id: ctxActionsButton - showMenuArrow: page.actions.contextualActions.length == 1 - onMenuAboutToShow: page.contextualActionsAboutToShow(); - anchors { - right: parent.right - verticalCenter: parent.verticalCenter - rightMargin: Units.smallSpacing - } - Action { - id: overflowAction - icon.name: "overflow-menu" - tooltip: qsTr("More Actions") - visible: visibleChildren.length > 0 - children: page && page.actions.contextualActions ? page.actions.contextualActions : null - } - kirigamiAction: page && page.actions.contextualActions.length === 1 ? page.actions.contextualActions[0] : overflowAction + 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: toolBar.Layout.minimumWidth + (page.actions.left ? toolBar.Layout.minimumWidth + Units.gridUnit : 0) + (page.actions.main ? toolBar.Layout.minimumWidth + Units.gridUnit : 0) + (page.actions.right ? toolBar.Layout.minimumWidth + Units.gridUnit : 0) + readonly property int requiredWidth: width + collapsedButtonsWidth + readonly property bool toobig: root.width - root.leftPadding - root.rightPadding - titleLoader.implicitWidth - Units.gridUnit < buttonTextMetrics.requiredWidth } }