Changeset View
Changeset View
Standalone View
Standalone View
src/controls/Card.qml
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright 2018 Marco Martin <mart@kde.org> | ||||
3 | * | ||||
4 | * This program is free software; you can redistribute it and/or modify | ||||
5 | * it under the terms of the GNU Library General Public License as | ||||
6 | * published by the Free Software Foundation; either version 2, or | ||||
7 | * (at your option) any later version. | ||||
8 | * | ||||
9 | * This program is distributed in the hope that it will be useful, | ||||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
12 | * GNU Library General Public License for more details | ||||
13 | * | ||||
14 | * You should have received a copy of the GNU Library General Public | ||||
15 | * License along with this program; if not, write to the | ||||
16 | * Free Software Foundation, Inc., | ||||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
18 | */ | ||||
19 | | ||||
20 | import QtQuick 2.6 | ||||
21 | import QtQuick.Layouts 1.2 | ||||
22 | import QtQuick.Controls 2.0 as Controls | ||||
23 | import org.kde.kirigami 2.3 as Kirigami | ||||
24 | import "private" | ||||
25 | | ||||
26 | /** | ||||
27 | * This is the standard layout of a Card. | ||||
28 | * It is recomended to use this class when the concept of Cards is needed | ||||
29 | * in the application. | ||||
30 | * This Card has default items as header and footer. The header is an | ||||
31 | * image that can contain an optional title and icon, accessible via the | ||||
32 | * banner grouped property. | ||||
33 | * The footer will show a series of toolbuttons (and eventual overflow menu) | ||||
34 | * represewnting the actions list accessible with the list property actions. | ||||
35 | * It is possible even tough is discouraged to override the footer: | ||||
36 | * in this case the actions property shouldn't be used. | ||||
37 | * | ||||
38 | * @inherits AbstractCard | ||||
39 | * @since 2.4 | ||||
40 | */ | ||||
41 | AbstractCard { | ||||
42 | id: root | ||||
43 | | ||||
44 | /** | ||||
45 | * actions: list<Action> | ||||
46 | * if the card should provide clickable actions, put them in this property, | ||||
47 | * they will be put in the footer as a list of ToolButtons plus an optional | ||||
48 | * overflow menu, when not all of them will fit in the available Card width. | ||||
49 | */ | ||||
50 | property list<QtObject> actions | ||||
51 | | ||||
52 | /** | ||||
53 | * banner: grouped | ||||
54 | * Gropuped property to control the banner image present in the header, it | ||||
55 | * has the following sub properties: | ||||
56 | * * url imageSource: the source for the image, it understands any url | ||||
57 | * valid for an Image component | ||||
58 | * * string title: the title for the banner, shown as contrasting | ||||
59 | * text over the image | ||||
60 | * * Qt.Alignment titleAlignment: the alignment of the title inside the image, | ||||
61 | * a combination of flags is supported | ||||
62 | * (default: Qt.AlignTop | Qt.AlignLeft) | ||||
63 | * * string iconSource: the optional icon to put in the banner: | ||||
64 | * it can be either a freedesktop-compatible icon name (recommended) | ||||
65 | * or any url supported by Image | ||||
66 | * * Image.FillMode fillMode: see the fillMode property of the Image component (default: Image.PreserveAspectCrop) | ||||
67 | */ | ||||
68 | readonly property alias banner: bannerGroup | ||||
69 | | ||||
70 | header: BannerImage { | ||||
71 | id: bannerImage | ||||
72 | BannerGroup { | ||||
73 | id: bannerGroup | ||||
74 | } | ||||
75 | anchors.leftMargin: -root.leftPadding | ||||
76 | anchors.topMargin: -root.topPadding | ||||
77 | anchors.rightMargin: root.headerOrientation == Qt.Vertical ? -root.rightPadding : 0 | ||||
78 | anchors.bottomMargin: root.headerOrientation == Qt.Horizontal ? -root.bottomPadding : 0 | ||||
79 | title: bannerGroup.title | ||||
80 | source: bannerGroup.imageSource | ||||
81 | titleAlignment: bannerGroup.titleAlignment | ||||
82 | titleIcon: bannerGroup.iconSource | ||||
83 | fillMode: bannerGroup.fillMode | ||||
84 | height: Layout.preferredHeight | ||||
85 | | ||||
86 | } | ||||
87 | | ||||
88 | onHeaderChanged: { | ||||
89 | if (!header) { | ||||
90 | return; | ||||
91 | } | ||||
92 | | ||||
93 | header.anchors.leftMargin = Qt.binding(function() {return -root.leftPadding}); | ||||
94 | header.anchors.topMargin = Qt.binding(function() {return -root.topPadding}); | ||||
95 | header.anchors.rightMargin = Qt.binding(function() {return root.headerOrientation == Qt.Vertical ? -root.rightPadding : 0}); | ||||
96 | header.anchors.bottomMargin = Qt.binding(function() {return root.headerOrientation == Qt.Horizontal ? -root.bottomPadding : 0}); | ||||
97 | } | ||||
98 | | ||||
99 | footer: RowLayout { | ||||
100 | id: actionsLayout | ||||
101 | spacing: Kirigami.Units.smallSpacing | ||||
102 | property var overflowSet: [] | ||||
103 | visible: root.footer == actionsLayout | ||||
104 | | ||||
105 | Repeater { | ||||
106 | model: root.actions | ||||
107 | delegate: PrivateActionToolButton { | ||||
108 | id: actionDelegate | ||||
109 | property bool fits: { | ||||
110 | var minX = 0; | ||||
111 | for (var i = 0; i < index; ++i) { | ||||
112 | if (actionsLayout.children[i].visible) { | ||||
113 | minX += actionsLayout.children[i].implicitWidth + actionsLayout.spacing; | ||||
114 | } | ||||
115 | } | ||||
116 | return minX + implicitWidth < root.width - root.leftPadding - root.rightPadding - moreButton.width; | ||||
117 | } | ||||
118 | visible: modelData.visible && fits | ||||
119 | Layout.fillWidth: true | ||||
120 | Layout.minimumWidth: implicitWidth | ||||
121 | kirigamiAction: modelData | ||||
122 | onFitsChanged: updateOverflowSet() | ||||
123 | function updateOverflowSet() { | ||||
124 | var index = actionsLayout.overflowSet.findIndex(function(act){ | ||||
125 | return act == modelData}); | ||||
126 | | ||||
127 | if ((fits || !modelData.visible) && index > -1) { | ||||
128 | actionsLayout.overflowSet.splice(index, 1); | ||||
129 | } else if (!fits && modelData.visible && index == -1) { | ||||
130 | actionsLayout.overflowSet.push(modelData); | ||||
131 | } | ||||
132 | actionsLayout.overflowSetChanged(); | ||||
133 | } | ||||
134 | Connections { | ||||
135 | target: modelData | ||||
136 | onVisibleChanged: actionDelegate.updateOverflowSet(); | ||||
137 | } | ||||
138 | Component.onCompleted: { | ||||
139 | actionDelegate.updateOverflowSet(); | ||||
140 | } | ||||
141 | } | ||||
142 | } | ||||
143 | Controls.ToolButton { | ||||
144 | id: moreButton | ||||
145 | | ||||
146 | Icon { | ||||
147 | anchors.fill: parent | ||||
148 | source: "overflow-menu" | ||||
149 | anchors.margins: 4 | ||||
150 | } | ||||
151 | Layout.alignment: Qt.AlignRight | ||||
152 | //checkable: true | ||||
153 | checked: menu.visible | ||||
154 | visible: actionsLayout.overflowSet.length > 0; | ||||
155 | onClicked: menu.visible ? menu.close() : menu.open() | ||||
156 | | ||||
157 | Controls.Menu { | ||||
158 | id: menu | ||||
159 | y: -height | ||||
160 | x: -width + moreButton.width | ||||
161 | | ||||
162 | Repeater { | ||||
163 | model: root.actions | ||||
164 | delegate: BasicListItem { | ||||
165 | text: modelData ? modelData.text : "" | ||||
166 | icon: modelData.icon | ||||
167 | checkable: modelData.checkable | ||||
168 | checked: modelData.checked | ||||
169 | onClicked: { | ||||
170 | modelData.trigger(); | ||||
171 | menu.visible = false; | ||||
172 | } | ||||
173 | separatorVisible: false | ||||
174 | backgroundColor: "transparent" | ||||
175 | visible: actionsLayout.overflowSet.findIndex(function(act) { | ||||
176 | return act == modelData}) > -1 && modelData.visible | ||||
177 | enabled: modelData.enabled | ||||
178 | } | ||||
179 | } | ||||
180 | } | ||||
181 | } | ||||
182 | } | ||||
183 | } |