diff --git a/kirigami.qrc b/kirigami.qrc
--- a/kirigami.qrc
+++ b/kirigami.qrc
@@ -32,6 +32,7 @@
src/controls/private/DefaultCardBackground.qml
src/controls/private/BannerImage.qml
src/controls/private/EdgeShadow.qml
+ src/controls/private/ActionToolBarLayoutDetails.qml
src/controls/private/globaltoolbar/AbstractPageHeader.qml
src/controls/private/globaltoolbar/PageRowGlobalToolBarStyleGroup.qml
src/controls/private/globaltoolbar/PageRowGlobalToolBarUI.qml
diff --git a/kirigami.qrc.in b/kirigami.qrc.in
--- a/kirigami.qrc.in
+++ b/kirigami.qrc.in
@@ -33,6 +33,7 @@
@kirigami_QML_DIR@/src/controls/private/DefaultCardBackground.qml
@kirigami_QML_DIR@/src/controls/private/BannerImage.qml
@kirigami_QML_DIR@/src/controls/private/EdgeShadow.qml
+ @kirigami_QML_DIR@/src/controls/private/ActionToolBarLayoutDetails.qml
@kirigami_QML_DIR@/src/controls/private/globaltoolbar/AbstractPageHeader.qml
@kirigami_QML_DIR@/src/controls/private/globaltoolbar/BreadcrumbControl.qml
@kirigami_QML_DIR@/src/controls/private/globaltoolbar/PageRowGlobalToolBarStyleGroup.qml
diff --git a/src/controls/Action.qml b/src/controls/Action.qml
--- a/src/controls/Action.qml
+++ b/src/controls/Action.qml
@@ -29,6 +29,31 @@
Controls.Action {
id: root
+ /**
+ * Hints for implementations using Actions indicating preferences about how to display the action.
+ */
+ enum DisplayHint {
+ /**
+ * Only display an icon for this Action.
+ */
+ IconOnly = 1,
+ /**
+ * Try to keep the action visible even when space constrained.
+ * Mutually exclusive with AlwaysHide, KeepVisible has priority.
+ */
+ KeepVisible = 2,
+ /**
+ * If possible, hide the action in an overflow menu or similar location.
+ * Mutually exclusive with KeepVisible, KeepVisible has priority.
+ */
+ AlwaysHide = 4,
+ /**
+ * When this action has children, do not display any indicator (like a
+ * menu arrow) for this action.
+ */
+ HideChildIndicator = 8
+ }
+
/**
* visible: bool
* True (default) when the graphic representation of the action
@@ -86,6 +111,37 @@
property Controls.Action parent
+ /**
+ * displayHint: int
+ *
+ * A combination of values from the Action.DisplayHint enum. These are provided to implementations to indicate
+ * a preference for certain display styles.
+ *
+ * Note that these are only preferences, implementations may choose to disregard them.
+ *
+ * @since 2.12
+ */
+ property int displayHint: 0
+
+ /**
+ * Helper function to check if a certain display hint has been set.
+ *
+ * This function is mostly convenience to enforce the mutual exclusivity of KeepVisible and AlwaysHide.
+ *
+ * @param hint The display hint to check if it is set.
+ *
+ * @return true if the hint was set for this action, false if not.
+ *
+ * @since 2.12
+ */
+ function displayHintSet(hint) {
+ if (hint === Action.DisplayHint.AlwaysHide && (displayHint & Action.DisplayHint.KeepVisible)) {
+ return false;
+ }
+
+ return (displayHint & hint)
+ }
+
default property alias children: root.__children
property list __children
diff --git a/src/controls/ActionToolBar.qml b/src/controls/ActionToolBar.qml
--- a/src/controls/ActionToolBar.qml
+++ b/src/controls/ActionToolBar.qml
@@ -96,7 +96,7 @@
}
Repeater {
- model: placeholderLayout.visibleActions
+ model: root.actions
delegate: PrivateActionToolButton {
id: actionDelegate
@@ -107,8 +107,11 @@
// from creating useless spacing
Layout.rightMargin: Kirigami.Units.smallSpacing
+ visible: details.visibleActions.indexOf(modelData) != -1
+ && (modelData.visible === undefined || modelData.visible)
+
flat: root.flat && !modelData.icon.color.a
- display: root.display
+ display: details.iconOnlyActions.indexOf(modelData) != -1 ? Controls.Button.IconOnly : root.display
kirigamiAction: modelData
}
}
@@ -122,82 +125,36 @@
id: moreButton
Layout.alignment: Qt.AlignRight
-
- visible: hiddenActions.length > 0 || placeholderLayout.hiddenActions.length > 0
- showMenuArrow: false
+ visible: root.hiddenActions.length > 0 || details.hiddenActions.length > 0
kirigamiAction: Kirigami.Action {
icon.name: "overflow-menu"
+ displayHint: Kirigami.Action.DisplayHint.IconOnly | Kirigami.Action.DisplayHint.HideChildIndicator
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: placeholderLayout.visibleActions.indexOf(parentAction) == -1 &&
+ value: details.visibleActions.indexOf(parentAction) == -1 &&
(parentAction.visible === undefined || parentAction.visible)
}
}
menu.itemDelegate: ActionMenuItem {
- visible: placeholderLayout.visibleActions.indexOf(action) == -1 &&
+ visible: details.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
- }
+ ActionToolBarLayoutDetails {
+ id: details
+ anchors.fill: parent
+ actions: root.actions
+ rightPadding: moreButton.width + Kirigami.Units.smallSpacing
+ flat: root.flat
+ display: root.display
}
-
- onWidthChanged: Qt.callLater(placeholderLayout.updateVisibleActions)
}
diff --git a/src/controls/private/ActionToolBarLayoutDetails.qml b/src/controls/private/ActionToolBarLayoutDetails.qml
new file mode 100644
--- /dev/null
+++ b/src/controls/private/ActionToolBarLayoutDetails.qml
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2018 Marco Martin
+ * Copyright 2019 Arjen Hiemstra
+ *
+ * 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
+
+/**
+ * This fairly complex thing determines the layout of ActionToolBar, that is,
+ * which actions should be displayed in full width toolbutton form, which should
+ * be displayed in an icon-only reduced size and which should be placed in the
+ * overflow menu.
+ *
+ * It makes use of two fairly static layouts, one contains all actions in expanded
+ * full-size form, the other in reduced icon-only form. The items in these layouts
+ * determine if they should be visible based on their relative position and size
+ * and some properties of the action. The update function then goes through these
+ * items, adding the actions to the relevant lists, so they can be used by the
+ * ActionToolBar to determine which action should be visible in what state.
+ *
+ * The reason for using two separate layouts from ActionToolBar's main layout is
+ * so that we can use the actual geometry of the buttons to calculate the
+ * visibility, completely disregarding any other layouting quirks. We can then
+ * use that information so only the relevant things in the ActionToolBar are
+ * visible. This allows the main ActionToolBar to use normal layout features for
+ * things like the positioning of the visible actions.
+ */
+Item {
+ id: details
+
+ property var actions
+
+ property var visibleActions: []
+ property var hiddenActions: []
+ property var iconOnlyActions: []
+
+ property bool flat: false
+ property int display: Controls.Button.TextBesideIcon
+ property real spacing: Kirigami.Units.smallSpacing
+ property real leftPadding: 0
+ property real rightPadding: 0
+
+ property real iconOnlyWidth: 0
+ readonly property real iconLayoutWidth: width - rightPadding
+ readonly property real fullLayoutWidth: iconLayoutWidth - iconOnlyWidth
+
+ enabled: false
+ opacity: 0 // Cannot use visible: false because then relayout doesn't happen correctly
+
+ function update() {
+ var visible = []
+ var hidden = []
+ var iconOnly = []
+ var iconsWidth = 0
+
+ if (details.width >= fullSizePlaceholderLayout.width + details.rightPadding) {
+ visibleActions = Array.prototype.map.call(details.actions, function(i) { return i })
+ hiddenActions = []
+ iconOnlyActions = []
+ iconOnlyWidth = 0
+ return
+ }
+
+ for (var i = 0; i < root.actions.length; ++i) {
+ var item = fullSizePlaceholderRepeater.itemAt(i)
+ var iconOnlyItem = iconOnlyPlaceholderRepeater.itemAt(i)
+
+ if (item.actionVisible) {
+ visible.push(item.kirigamiAction)
+ } else if (iconOnlyItem.actionVisible) {
+ visible.push(item.kirigamiAction)
+ iconOnly.push(item.kirigamiAction)
+ iconsWidth += iconOnlyItem.width + details.spacing
+ } else {
+ hidden.push(item.kirigamiAction)
+ }
+ }
+
+ visibleActions = visible
+ hiddenActions = hidden
+ iconOnlyActions = iconOnly
+ iconOnlyWidth = iconsWidth
+ }
+
+ onWidthChanged: Qt.callLater(update)
+ Component.onCompleted: Qt.callLater(update)
+
+ RowLayout {
+ id: fullSizePlaceholderLayout
+ spacing: details.spacing
+
+ // This binding is here to take care of things like visibility changes
+ onWidthChanged: Qt.callLater(details.update)
+
+ Repeater {
+ id: fullSizePlaceholderRepeater
+
+ model: details.actions
+
+ PrivateActionToolButton {
+ flat: details.flat && !modelData.icon.color.a
+ display: details.display
+ visible: (modelData.visible === undefined || modelData.visible)
+ && (modelData.displayHint !== undefined && !modelData.displayHintSet(Kirigami.Action.DisplayHint.AlwaysHide))
+ kirigamiAction: modelData
+ property bool actionVisible: visible && (x + width < details.fullLayoutWidth)
+ }
+ }
+ }
+
+ RowLayout {
+ id: iconOnlyPlaceholderLayout
+ spacing: details.spacing
+
+ Repeater {
+ id: iconOnlyPlaceholderRepeater
+
+ model: details.actions
+
+ PrivateActionToolButton {
+ flat: details.flat && !modelData.icon.color.a
+ display: Controls.Button.IconOnly
+ visible: (modelData.visible === undefined || modelData.visible)
+ && (modelData.displayHint !== undefined && modelData.displayHintSet(Kirigami.Action.DisplayHint.KeepVisible))
+ kirigamiAction: modelData
+ property bool actionVisible: visible && (x + width < details.iconLayoutWidth)
+ }
+ }
+ }
+}
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
@@ -42,8 +42,10 @@
//TODO: replace with upstream action when we depend on Qt 5.10
//TODO: upstream action makes the style to re-draw the content, it'd be ideal except for the custom dropDown icon needed for actionsMenu
property Controls.Action kirigamiAction
- property bool showText: true
- property bool showMenuArrow: true
+ property bool showText: !(kirigamiAction && kirigamiAction.displayHint !== undefined
+ && kirigamiAction.displayHintSet(Action.DisplayHint.IconOnly))
+ property bool showMenuArrow: !(kirigamiAction && kirigamiAction.displayHint !== undefined
+ && kirigamiAction.displayHintSet(Action.DisplayHint.HideChildIndicator))
property alias menu: menu
//we need our own text delegate