diff --git a/kirigami.qrc b/kirigami.qrc --- a/kirigami.qrc +++ b/kirigami.qrc @@ -13,6 +13,7 @@ src/controls/CardsLayout.qml src/controls/CardsListView.qml src/controls/CardsGridView.qml + src/controls/ActionToolBar.qml src/controls/templates/AbstractCard.qml src/controls/InlineMessage.qml src/controls/ToolBarApplicationHeader.qml diff --git a/src/controls/ActionToolBar.qml b/src/controls/ActionToolBar.qml new file mode 100644 --- /dev/null +++ b/src/controls/ActionToolBar.qml @@ -0,0 +1,151 @@ +/* + * 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.6 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.0 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 RowLayout + * @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 + + /** + * flat: bool + * Wether we want our buttons to have a flat appearance. Default: true + */ + property bool flat: true + + implicitHeight: actionsLayout.implicitHeight + + implicitWidth: { + var width = 0; + for (var i = 0; i < actionsLayout.children.length; ++i) { + if (actionsLayout.children[i].kirigamiAction && actionsLayout.children[i].kirigamiAction.visible) { + width += actionsLayout.children[i].implicitWidth + actionsLayout.spacing; + } + } + width += moreButton.width; + return width; + } + + RowLayout { + id: actionsLayout + anchors.fill: parent + //anchors.rightMargin: moreButton.width + + spacing: Kirigami.Units.smallSpacing + property var overflowSet: [] + + // 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; + } + + Repeater { + model: root.actions + delegate: PrivateActionToolButton { + id: actionDelegate + flat: root.flat + readonly property bool fits: { + var minX = 0; + for (var i = 0; i < index; ++i) { + if (actionsLayout.children[i].visible) { + minX += actionsLayout.children[i].implicitWidth + actionsLayout.spacing; + } + } + return minX + implicitWidth < actionsLayout.width - moreButton.width + } + + visible: modelData.visible && fits + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + Layout.minimumWidth: implicitWidth + kirigamiAction: modelData + onFitsChanged: updateOverflowSet() + function updateOverflowSet() { + var index = actionsLayout.findIndex(actionsLayout.overflowSet, function(act) { + return act == modelData}); + + if ((fits || !modelData.visible) && index > -1) { + actionsLayout.overflowSet.splice(index, 1); + } else if (!fits && modelData.visible && index == -1) { + actionsLayout.overflowSet.push(modelData); + } + actionsLayout.overflowSetChanged(); + } + Connections { + target: modelData + onVisibleChanged: actionDelegate.updateOverflowSet(); + } + Component.onCompleted: { + actionDelegate.updateOverflowSet(); + } + } + } + Controls.ToolButton { + id: moreButton + + Layout.alignment: Qt.AlignRight + Kirigami.Icon { + anchors.fill: parent + source: "overflow-menu" + anchors.margins: 4 + } + + //checkable: true + checked: menu.visible + visible: actionsLayout.overflowSet.length > 0; + onClicked: menu.visible ? menu.close() : menu.open() + + ActionsMenu { + id: menu + y: -height + x: -width + moreButton.width + actions: root.actions + submenuComponent: Component { + ActionsMenu {} + } + itemDelegate: ActionMenuItem { + visible: actionsLayout.findIndex(actionsLayout.overflowSet, function(act) {return act == ourAction}) > -1 && (ourAction.visible == undefined || ourAction.visible) + } + } + } + } +} diff --git a/src/controls/Card.qml b/src/controls/Card.qml --- a/src/controls/Card.qml +++ b/src/controls/Card.qml @@ -20,7 +20,7 @@ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls -import org.kde.kirigami 2.4 as Kirigami +import org.kde.kirigami 2.5 as Kirigami import "private" /** @@ -96,98 +96,9 @@ header.anchors.bottomMargin = Qt.binding(function() {return root.headerOrientation == Qt.Horizontal ? -root.bottomPadding : 0}); } - footer: RowLayout { - id: actionsLayout - spacing: Kirigami.Units.smallSpacing - property var overflowSet: [] - visible: root.footer == actionsLayout - - // 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; - } - - Repeater { - model: root.actions - delegate: PrivateActionToolButton { - id: actionDelegate - readonly property bool fits: { - var minX = 0; - for (var i = 0; i < index; ++i) { - if (actionsLayout.children[i].visible) { - minX += actionsLayout.children[i].implicitWidth + actionsLayout.spacing; - } - } - return minX + implicitWidth < root.width - root.leftPadding - root.rightPadding - moreButton.width; - } - visible: modelData.visible && fits - Layout.fillWidth: true - Layout.minimumWidth: implicitWidth - kirigamiAction: modelData - onFitsChanged: updateOverflowSet() - function updateOverflowSet() { - var index = actionsLayout.findIndex(actionsLayout.overflowSet, function(act){ - return act == modelData}); - - if ((fits || !modelData.visible) && index > -1) { - actionsLayout.overflowSet.splice(index, 1); - } else if (!fits && modelData.visible && index == -1) { - actionsLayout.overflowSet.push(modelData); - } - actionsLayout.overflowSetChanged(); - } - Connections { - target: modelData - onVisibleChanged: actionDelegate.updateOverflowSet(); - } - Component.onCompleted: { - actionDelegate.updateOverflowSet(); - } - } - } - Controls.ToolButton { - id: moreButton - - Kirigami.Icon { - anchors.fill: parent - source: "overflow-menu" - anchors.margins: 4 - } - Layout.alignment: Qt.AlignRight - //checkable: true - checked: menu.visible - visible: actionsLayout.overflowSet.length > 0; - onClicked: menu.visible ? menu.close() : menu.open() - - Controls.Menu { - id: menu - y: -height - x: -width + moreButton.width - - Repeater { - model: root.actions - delegate: BasicListItem { - text: modelData ? modelData.text : "" - icon: modelData.icon - checkable: modelData.checkable - checked: modelData.checked - onClicked: { - modelData.trigger(); - menu.visible = false; - } - separatorVisible: false - backgroundColor: "transparent" - visible: actionsLayout.findIndex(actionsLayout.overflowSet, function(act) { - return act == modelData}) > -1 && modelData.visible - enabled: modelData.enabled - } - } - } - } + footer: Kirigami.ActionToolBar { + id: actionsToolBar + actions: root.actions + visible: root.footer == actionsToolBar } } diff --git a/src/controls/InlineMessage.qml b/src/controls/InlineMessage.qml --- a/src/controls/InlineMessage.qml +++ b/src/controls/InlineMessage.qml @@ -23,7 +23,7 @@ import QtGraphicalEffects 1.0 import QtQuick.Layouts 1.0 import QtQuick.Controls 2.0 as Controls -import org.kde.kirigami 2.4 as Kirigami +import org.kde.kirigami 2.5 as Kirigami import "private" import "templates" as T @@ -246,13 +246,14 @@ } } - RowLayout { + Kirigami.ActionToolBar { id: actionsLayout + flat: false + actions: root.actions visible: root.actions.length Layout.alignment: Qt.AlignRight - Layout.fillWidth: true Layout.row: { if (messageTextMetrics.width + Kirigami.Units.smallSpacing > @@ -266,125 +267,12 @@ Layout.column: Layout.row ? 0 : 2 Layout.columnSpan: Layout.row ? (closeButton.visible ? 3 : 2) : 1 - property var overflowSet: [] - - spacing: Kirigami.Units.smallSpacing - - // 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; - } - TextMetrics { id: messageTextMetrics font: text.font text: text.text } - - Repeater { - model: root.actions - - //TODO: use a normal button when we can depend from Qt 5.10 - delegate: PrivateActionToolButton { - id: actionButton - - flat: false - kirigamiAction: modelData - visible: modelData.visible && fits - - Layout.alignment: Qt.AlignVCenter - Layout.minimumWidth: implicitWidth - - readonly property bool fits: { - var minX = 0; - - for (var i = 0; i < index; ++i) { - if (actionsLayout.children[i].visible) { - minX += actionsLayout.children[i].implicitWidth + actionsLayout.spacing; - } - } - - return minX + implicitWidth < contentLayout.width - moreButton.width; - } - - onFitsChanged: updateOverflowSet() - - function updateOverflowSet() { - var index = actionsLayout.findIndex(actionsLayout.overflowSet, function(act){ - return act == modelData}); - - if ((fits || !modelData.visible) && index > -1) { - actionsLayout.overflowSet.splice(index, 1); - } else if (!fits && modelData.visible && index == -1) { - actionsLayout.overflowSet.push(modelData); - } - - actionsLayout.overflowSetChanged(); - } - - Connections { - target: modelData - - onVisibleChanged: actionButton.updateOverflowSet(); - } - - Component.onCompleted: updateOverflowSet(); - } - } - - Controls.ToolButton { - id: moreButton - - visible: actionsLayout.overflowSet.length > 0 - - checked: menu.visible - - onClicked: menu.visible ? menu.close() : menu.open() - - Kirigami.Icon { - anchors.fill: parent - source: "overflow-menu" - anchors.margins: 4 - } - - Controls.Menu { - id: menu - y: -height - x: -width + moreButton.width - - Repeater { - model: root.actions - - delegate: BasicListItem { - enabled: modelData.enabled - - checkable: modelData.checkable - checked: modelData.checked - - text: modelData ? modelData.text : "" - icon: modelData.icon - - onClicked: { - modelData.trigger(); - menu.visible = false; - } - - separatorVisible: false - - backgroundColor: "transparent" - - visible: actionsLayout.findIndex(actionsLayout.overflowSet, function(act) { - return act == modelData}) > -1 && modelData.visible - } - } - } - } } Controls.ToolButton { diff --git a/src/controls/private/ActionMenuItemBase.qml b/src/controls/private/ActionMenuItemBase.qml --- a/src/controls/private/ActionMenuItemBase.qml +++ b/src/controls/private/ActionMenuItemBase.qml @@ -33,6 +33,4 @@ onTriggered: { ourAction.trigger() } - - readonly property var ourMenu: theMenu.submenuComponent ? theMenu.submenuComponent.createObject(menuItem, { actions: ourAction.children }) : null } diff --git a/src/controls/private/ActionsMenu.qml b/src/controls/private/ActionsMenu.qml --- a/src/controls/private/ActionsMenu.qml +++ b/src/controls/private/ActionsMenu.qml @@ -25,11 +25,9 @@ id: theMenu property alias actions: actionsInstantiator.model property Component submenuComponent + //renamed to work on both Qt 5.9 and 5.10 + property Component itemDelegate: Component {ActionMenuItem {}} - Component { - id: menuItemComponent - ActionMenuItem {} - } Instantiator { id: actionsInstantiator @@ -41,7 +39,7 @@ function create() { if (!action.children || action.children.length === 0) { - item = menuItemComponent.createObject(null, { ourAction: action }); + item = theMenu.itemDelegate.createObject(null, { ourAction: action }); theMenu.addItem(item) } else if (theMenu.submenuComponent) { item = theMenu.submenuComponent.createObject(null, { title: action.text, actions: action.children }); diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp --- a/src/kirigamiplugin.cpp +++ b/src/kirigamiplugin.cpp @@ -170,6 +170,7 @@ //2.5 qmlRegisterType(componentUrl(QStringLiteral("ListItemDragHandle.qml")), uri, 2, 5, "ListItemDragHandle"); + qmlRegisterType(componentUrl(QStringLiteral("ActionToolBar.qml")), uri, 2, 5, "ActionToolBar"); qmlProtectModule(uri, 2); }