Warning, /frameworks/kirigami/autotests/tst_actiontoolbar.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 import QtQuick
0008 import QtQuick.Controls
0009 import QtTest
0010 import org.kde.kirigami as Kirigami
0011 
0012 // TODO: Find a nicer way to handle this
0013 import "../src/controls/private" as KirigamiPrivate
0014 
0015 TestCase {
0016     id: testCase
0017     name: "ActionToolBarTest"
0018 
0019     width: 800
0020     height: 400
0021     visible: true
0022 
0023     when: windowShown
0024 
0025     // These buttons are required for getting the right metrics.
0026     // Since ActionToolBar bases all sizing on button sizes, we need to be able
0027     // to verify that layouting does the right thing.
0028     property ToolButton iconButton: KirigamiPrivate.PrivateActionToolButton {
0029         display: Button.IconOnly
0030         action: Kirigami.Action { icon.name: "document-new"; text: "Test Action" }
0031         font.pointSize: 10
0032     }
0033     property ToolButton textButton: KirigamiPrivate.PrivateActionToolButton {
0034         display: Button.TextOnly
0035         action: Kirigami.Action { icon.name: "document-new"; text: "Test Action" }
0036         font.pointSize: 10
0037     }
0038     property ToolButton textIconButton: KirigamiPrivate.PrivateActionToolButton {
0039         action: Kirigami.Action { icon.name: "document-new"; text: "Test Action" }
0040         font.pointSize: 10
0041     }
0042     property TextField textField: TextField { font.pointSize: 10 }
0043 
0044     Component {
0045         id: single;
0046         Kirigami.ActionToolBar {
0047             font.pointSize: 10
0048             actions: [
0049                 Kirigami.Action { icon.name: "document-new"; text: "Test Action" }
0050             ]
0051         }
0052     }
0053 
0054     Component {
0055         id: multiple
0056         Kirigami.ActionToolBar {
0057             font.pointSize: 10
0058             actions: [
0059                 Kirigami.Action { icon.name: "document-new"; text: "Test Action" },
0060                 Kirigami.Action { icon.name: "document-new"; text: "Test Action" },
0061                 Kirigami.Action { icon.name: "document-new"; text: "Test Action" }
0062             ]
0063         }
0064     }
0065 
0066     Component {
0067         id: iconOnly
0068         Kirigami.ActionToolBar {
0069             display: Button.IconOnly
0070             font.pointSize: 10
0071             actions: [
0072                 Kirigami.Action { icon.name: "document-new"; text: "Test Action" },
0073                 Kirigami.Action { icon.name: "document-new"; text: "Test Action" },
0074                 Kirigami.Action { icon.name: "document-new"; text: "Test Action" }
0075             ]
0076         }
0077     }
0078 
0079     Component {
0080         id: qtActions
0081         Kirigami.ActionToolBar {
0082             font.pointSize: 10
0083             actions: [
0084                 Action { icon.name: "document-new"; text: "Test Action" },
0085                 Action { icon.name: "document-new"; text: "Test Action" },
0086                 Action { icon.name: "document-new"; text: "Test Action" }
0087             ]
0088         }
0089     }
0090 
0091     Component {
0092         id: mixed
0093         Kirigami.ActionToolBar {
0094             font.pointSize: 10
0095             actions: [
0096                 Kirigami.Action { icon.name: "document-new"; text: "Test Action"; displayHint: Kirigami.DisplayHint.IconOnly },
0097                 Kirigami.Action { icon.name: "document-new"; text: "Test Action" },
0098                 Kirigami.Action { icon.name: "document-new"; text: "Test Action"; displayComponent: TextField { } },
0099                 Kirigami.Action { icon.name: "document-new"; text: "Test Action"; displayHint: Kirigami.DisplayHint.AlwaysHide },
0100                 Kirigami.Action { icon.name: "document-new"; text: "Test Action"; displayHint: Kirigami.DisplayHint.KeepVisible }
0101             ]
0102         }
0103     }
0104 
0105     function test_layout_data() {
0106         return [
0107             // One action
0108             // Full window width, should just display a toolbutton
0109             { tag: "single_full", component: single, width: testCase.width, expected: testCase.textIconButton.width },
0110             // Small width, should display the overflow button
0111             { tag: "single_min", component: single, width: 50, expected: testCase.iconButton.width },
0112             // Half window width, should display a single toolbutton
0113             { tag: "single_half", component: single, width: testCase.width / 2, expected: testCase.textIconButton.width },
0114             // Multiple actions
0115             // Full window width, should display as many buttons as there are actions
0116             { tag: "multi_full", component: multiple, width: testCase.width,
0117                 expected: testCase.textIconButton.width * 3 + Kirigami.Units.smallSpacing * 2 },
0118             // Small width, should display just the overflow button
0119             { tag: "multi_min", component: multiple, width: 50, expected: testCase.iconButton.width },
0120             // Half window width, should display one action and overflow button
0121             { tag: "multi_small", component: multiple,
0122                 width: testCase.textIconButton.width * 2 + testCase.iconButton.width + Kirigami.Units.smallSpacing * 3,
0123                 expected: testCase.textIconButton.width * 2 + testCase.iconButton.width + Kirigami.Units.smallSpacing * 2 },
0124             // Multiple actions, display set to icon only
0125             // Full window width, should display as many icon-only buttons as there are actions
0126             { tag: "icon_full", component: iconOnly, width: testCase.width,
0127                 expected: testCase.iconButton.width * 3 + Kirigami.Units.smallSpacing * 2 },
0128             // Small width, should display just the overflow button
0129             { tag: "icon_min", component: iconOnly, width: 50, expected: testCase.iconButton.width },
0130             // Quarter window width, should display one icon-only button and the overflow button
0131             { tag: "icon_small", component: iconOnly, width: testCase.iconButton.width * 4,
0132                 expected: testCase.iconButton.width * 3 + Kirigami.Units.smallSpacing * 2 },
0133             // QtQuick Controls actions
0134             // Full window width, should display as many buttons as there are actions
0135             { tag: "qt_full", component: qtActions, width: testCase.width,
0136                 expected: testCase.textIconButton.width * 3 + Kirigami.Units.smallSpacing * 2 },
0137             // Small width, should display just the overflow button
0138             { tag: "qt_min", component: qtActions, width: 50, expected: testCase.iconButton.width },
0139             // Half window width, should display one action and overflow button
0140             { tag: "qt_small", component: qtActions,
0141                 width: testCase.textIconButton.width * 2 + testCase.iconButton.width + Kirigami.Units.smallSpacing * 3,
0142                 expected: testCase.textIconButton.width * 2 + testCase.iconButton.width + Kirigami.Units.smallSpacing * 2 },
0143             // Mix of different display hints, displayComponent and normal actions.
0144             // Full window width, should display everything, but one action is collapsed to icon
0145             { tag: "mixed", component: mixed, width: testCase.width,
0146                 expected: testCase.textIconButton.width * 2 + testCase.iconButton.width * 2 + testCase.textField.width + Kirigami.Units.smallSpacing * 4 }
0147         ]
0148     }
0149 
0150     // Test layouting of ActionToolBar
0151     //
0152     // ActionToolBar has some pretty complex behaviour, which generally boils down to it trying
0153     // to fit as many visible actions as possible and placing the hidden ones in an overflow menu.
0154     // This test, along with the data above, verifies that that this behaviour is correct.
0155     function test_layout(data) {
0156         var toolbar = createTemporaryObject(data.component, testCase, {width: data.width})
0157 
0158         verify(toolbar)
0159         verify(waitForRendering(toolbar))
0160 
0161         while (toolbar.visibleWidth == 0) {
0162             // The toolbar creates its delegates asynchronously during "idle
0163             // time", this means we need to wait for a bit so the toolbar has
0164             // the time to do that. As long as it has not finished creation, the
0165             // toolbar will have a visibleWidth of 0, so we can use that to
0166             // determine when it is done.
0167             wait(50)
0168         }
0169 
0170         compare(toolbar.visibleWidth, data.expected)
0171     }
0172 
0173     Component {
0174         id: heightMode
0175 
0176         Kirigami.ActionToolBar {
0177             id: heightModeToolBar
0178             font.pointSize: 10
0179 
0180             property real customHeight: 50
0181 
0182             actions: [
0183                 Kirigami.Action {
0184                     displayComponent: Button {
0185                         objectName: "tall"
0186                         implicitHeight: heightModeToolBar.customHeight
0187                         implicitWidth: 50
0188                     }
0189                 },
0190                 Kirigami.Action {
0191                     displayComponent: Button {
0192                         objectName: "short"
0193                         implicitHeight: 25
0194                         implicitWidth: 50
0195                     }
0196                 }
0197             ]
0198         }
0199     }
0200 
0201     function getChild(toolbar, objectName) {
0202         let c = toolbar.children[0].children
0203         for (let i in c) {
0204             if (c[i].objectName === objectName) {
0205                 return c[i]
0206             }
0207         }
0208         return -1
0209     }
0210 
0211     function test_height() {
0212         var toolbar = createTemporaryObject(heightMode, testCase, {width: testCase.width})
0213 
0214         verify(toolbar)
0215         verify(waitForRendering(toolbar))
0216 
0217         while (toolbar.visibleWidth == 0) {
0218             // Same as above
0219             wait(50)
0220         }
0221 
0222         compare(toolbar.implicitHeight, 50)
0223         compare(toolbar.height, 50)
0224 
0225         toolbar.customHeight = 100
0226 
0227         // Changing the delegate height will trigger the internal layout to
0228         // relayout, which is done in polish. This is not signaled to the
0229         // parent toolbar, so we need to wait on the contentItem here.
0230         verify(isPolishScheduled(toolbar.contentItem))
0231         verify(waitForItemPolished(toolbar.contentItem))
0232 
0233         // Implicit height changes should propagate to the layout's height as
0234         // long as that doesn't have an explicit height set.
0235         compare(toolbar.implicitHeight, 100)
0236         compare(toolbar.height, 100)
0237 
0238         // This should be the default, but make sure to set it regardless
0239         toolbar.heightMode = Kirigami.ToolBarLayout.ConstrainIfLarger
0240         toolbar.height = 50
0241 
0242         // Find the actual child items so we can check their properties
0243         let t = getChild(toolbar, "tall");
0244         let s = getChild(toolbar, "short");
0245 
0246         // waitForItemPolished doesn't wait long enough and waitForRendering
0247         // waits too long, so just wait an arbitrary amount of time...
0248         wait(50)
0249 
0250         // ConstrainIfLarger should reduce the height of the first, but not touch the second
0251         compare(t.height, 50)
0252         compare(t.y, 0)
0253         compare(s.height, 25)
0254         compare(s.y, 13) // Should be centered and rounded
0255 
0256         // AlwaysCenter should not touch any item's height, only make sure they are centered
0257         toolbar.heightMode = Kirigami.ToolBarLayout.AlwaysCenter
0258 
0259         wait(50)
0260 
0261         compare(t.height, 100)
0262         compare(t.y, -25)
0263         compare(s.height, 25)
0264         compare(s.y, 13)
0265 
0266         // AlwaysFill should make sure each item has the same height as the toolbar
0267         toolbar.heightMode = Kirigami.ToolBarLayout.AlwaysFill
0268 
0269         wait(50)
0270 
0271         compare(t.height, 50)
0272         compare(t.y, 0)
0273         compare(s.height, 50)
0274         compare(s.y, 0)
0275 
0276         // Unconstraining the toolbar should reset its height to the maximum
0277         // implicit height and set any children to that same value as heightMode
0278         // is still AlwaysFill.
0279         toolbar.height = undefined
0280 
0281         wait(50)
0282 
0283         compare(toolbar.height, 100)
0284         compare(t.height, 100)
0285         compare(s.height, 100)
0286     }
0287 }