Changeset View
Changeset View
Standalone View
Standalone View
applets/devicenotifier/package/contents/ui/DeviceItem.qml
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * Copyright 2011 Viranch Mehta <viranch.mehta@gmail.com> | 2 | * Copyright 2011 Viranch Mehta <viranch.mehta@gmail.com> | ||
3 | * Copyright 2012 Jacopo De Simoi <wilderkde@gmail.com> | 3 | * Copyright 2012 Jacopo De Simoi <wilderkde@gmail.com> | ||
4 | * Copyright 2016 Kai Uwe Broulik <kde@privat.broulik.de> | 4 | * Copyright 2016 Kai Uwe Broulik <kde@privat.broulik.de> | ||
5 | * Copyright 2020 Nate Graham <nate@kde.org> | ||||
5 | * | 6 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Library General Public License as | 8 | * it under the terms of the GNU Library General Public License as | ||
8 | * published by the Free Software Foundation; either version 2 or | 9 | * published by the Free Software Foundation; either version 2 or | ||
9 | * (at your option) any later version. | 10 | * (at your option) any later version. | ||
10 | * | 11 | * | ||
11 | * This program is distributed in the hope that it will be useful, | 12 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details | 15 | * GNU General Public License for more details | ||
15 | * | 16 | * | ||
16 | * You should have received a copy of the GNU Library General Public | 17 | * You should have received a copy of the GNU Library General Public | ||
17 | * License along with this program; if not, write to the | 18 | * License along with this program; if not, write to the | ||
18 | * Free Software Foundation, Inc., | 19 | * Free Software Foundation, Inc., | ||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
20 | */ | 21 | */ | ||
21 | 22 | | |||
22 | import QtQuick 2.0 | 23 | import QtQuick 2.0 | ||
23 | import QtQuick.Layouts 1.1 | 24 | import QtQuick.Controls 2.12 as QQC2 | ||
25 | import QtQml.Models 2.14 | ||||
24 | 26 | | |||
25 | import org.kde.plasma.core 2.0 as PlasmaCore | 27 | import org.kde.plasma.core 2.0 as PlasmaCore | ||
26 | import org.kde.plasma.components 2.0 as PlasmaComponents | 28 | import org.kde.plasma.extras 2.0 as PlasmaExtras | ||
29 | | ||||
27 | import org.kde.kquickcontrolsaddons 2.0 | 30 | import org.kde.kquickcontrolsaddons 2.0 | ||
28 | 31 | | |||
29 | MouseArea { | 32 | PlasmaExtras.ExpandableListItem { | ||
broulik: There's an `expandedDevice` property in devicenotifier.qml which is supposed to decide which… | |||||
30 | id: deviceItem | 33 | id: deviceItem | ||
31 | 34 | | |||
This is all over the place for me. All the free space jobs in the dataengine get the root size back. Not sure if a local issue or KIO? @dfaure broulik: This is all over the place for me. All the free space jobs in the dataengine get the root size… | |||||
Please give me a proper bug report in KIO terms :-) I don't see a bug in a TODO comment in some QML code ;) dfaure: Please give me a proper bug report in KIO terms :-)
What class do you use, what do you get as a… | |||||
32 | property string udi | 35 | property string udi | ||
33 | property alias icon: deviceIcon.source | 36 | readonly property int state: sdSource.data[udi] ? sdSource.data[udi].State : 0 | ||
34 | property alias deviceName: deviceLabel.text | 37 | readonly property int operationResult: (model["Operation result"]) | ||
35 | property string emblemIcon | | |||
36 | property int state | | |||
37 | | ||||
38 | property bool mounted | | |||
39 | property bool isRoot | | |||
40 | property bool expanded: devicenotifier.expandedDevice == udi | | |||
41 | property alias percentUsage: freeSpaceBar.value | | |||
42 | property string freeSpaceText | | |||
43 | | ||||
44 | signal actionTriggered | | |||
45 | | ||||
46 | property alias actionIcon: actionButton.iconName | | |||
47 | property alias actionToolTip: actionButton.tooltip | | |||
48 | property bool actionVisible | | |||
49 | 38 | | |||
39 | // don't show unmount actions for root and media players; instead, show an | ||||
40 | // "Open in file manager" button instead, so the user can do something useful | ||||
41 | readonly property bool unmountable: sdSource.data[udi]["File Path"] !== "/" | ||||
42 | && model["Device Types"].indexOf("Portable Media Player") == -1 | ||||
43 | readonly property bool isMounted: devicenotifier.isMounted(udi) | ||||
50 | readonly property bool hasMessage: statusSource.lastUdi == udi && statusSource.data[statusSource.last] ? true : false | 44 | readonly property bool hasMessage: statusSource.lastUdi == udi && statusSource.data[statusSource.last] ? true : false | ||
51 | readonly property var message: hasMessage ? statusSource.data[statusSource.last] || ({}) : ({}) | 45 | readonly property var message: hasMessage ? statusSource.data[statusSource.last] || ({}) : ({}) | ||
52 | 46 | | |||
53 | height: row.childrenRect.height + 2 * row.y | 47 | readonly property double freeSpace: sdSource.data[udi] && sdSource.data[udi]["Free Space"] ? sdSource.data[udi]["Free Space"] : -1.0 | ||
54 | hoverEnabled: true | 48 | readonly property double totalSpace: sdSource.data[udi] && sdSource.data[udi]["Size"] ? sdSource.data[udi]["Size"] : -1.0 | ||
55 | 49 | property bool freeSpaceKnown: freeSpace > 0 && totalSpace > 0 | |||
56 | onHasMessageChanged: { | 50 | | ||
57 | if (hasMessage) { | 51 | onOperationResultChanged: { | ||
58 | messageHighlight.highlight(this) | 52 | if (!popupIconTimer.running) { | ||
59 | } | 53 | if (operationResult == 1) { | ||
60 | } | 54 | devicenotifier.popupIcon = "dialog-ok" | ||
61 | 55 | popupIconTimer.restart() | |||
62 | onContainsMouseChanged: { | 56 | } else if (operationResult == 2) { | ||
63 | if (containsMouse) { | 57 | devicenotifier.popupIcon = "dialog-error" | ||
64 | devicenotifier.currentIndex = index | 58 | popupIconTimer.restart() | ||
65 | } | | |||
66 | | ||||
67 | // this is done to hide the highlight if the mouse moves out of the list view | | |||
68 | // and we are not hovering anything | | |||
69 | if (deviceItem.ListView.view.highlightItem) { | | |||
70 | deviceItem.ListView.view.highlightItem.opacity = (containsMouse ? 1 : 0) | | |||
71 | } | 59 | } | ||
72 | } | 60 | } | ||
73 | | ||||
74 | onClicked: { | | |||
75 | var data = hpSource.data[udi] | | |||
76 | if (!data) { | | |||
77 | return | | |||
78 | } | 61 | } | ||
79 | 62 | | |||
80 | var actions = data.actions | 63 | onHasMessageChanged: { | ||
81 | if (actions.length === 1) { | 64 | if (deviceItem.hasMessage) { | ||
82 | var service = hpSource.serviceForSource(udi) | 65 | messageHighlight.highlight(this) | ||
83 | var operation = service.operationDescription("invokeAction") | | |||
84 | operation.predicate = actions[0].predicate | | |||
85 | service.startOperationCall(operation) | | |||
86 | } else { | | |||
87 | devicenotifier.expandedDevice = (expanded ? "" : udi) | | |||
88 | } | 66 | } | ||
89 | } | 67 | } | ||
90 | 68 | | |||
91 | Connections { | 69 | Connections { | ||
92 | target: unmountAll | 70 | target: unmountAll | ||
93 | onClicked: { | 71 | onClicked: { | ||
94 | if (model["Removable"] && mounted) { | 72 | if (model["Removable"] && isMounted) { | ||
95 | actionTriggered(); | 73 | actionTriggered(); | ||
96 | } | 74 | } | ||
97 | } | 75 | } | ||
98 | } | 76 | } | ||
99 | 77 | | |||
100 | // this keeps the delegate around for 5 seconds after the device has been | 78 | // this keeps the delegate around for 5 seconds after the device has been | ||
101 | // removed in case there was a message, such as "you can now safely remove this" | 79 | // removed in case there was a message, such as "you can now safely remove this" | ||
102 | ListView.onRemove: { | 80 | ListView.onRemove: { | ||
103 | if (devicenotifier.expandedDevice == udi) { | 81 | deviceItem.isEnabled = false | ||
104 | devicenotifier.expandedDevice = "" | | |||
105 | } | | |||
106 | 82 | | |||
107 | if (deviceItem.hasMessage) { | 83 | if (deviceItem.hasMessage) { | ||
108 | ListView.delayRemove = true | 84 | ListView.delayRemove = true | ||
109 | keepDelegateTimer.restart() | 85 | keepDelegateTimer.restart() | ||
110 | 86 | | |||
111 | statusMessage.opacity = 1 // HACK seems the Column animation breaksf | | |||
112 | freeSpaceBar.visible = false | | |||
113 | actionButton.visible = false | | |||
114 | | ||||
115 | ++devicenotifier.pendingDelegateRemoval // QTBUG-50380 | 87 | ++devicenotifier.pendingDelegateRemoval // QTBUG-50380 | ||
116 | } | 88 | } | ||
89 | // Otherwise there are briefly multiple highlight effects | ||||
90 | devicenotifier.currentIndex = -1 | ||||
117 | } | 91 | } | ||
118 | 92 | | |||
119 | Timer { | 93 | Timer { | ||
120 | id: keepDelegateTimer | 94 | id: keepDelegateTimer | ||
121 | interval: 3000 // same interval as the auto hide / passive timer | 95 | interval: 5000 // same interval as the auto hide / passive timer | ||
122 | onTriggered: { | 96 | onTriggered: { | ||
123 | deviceItem.ListView.delayRemove = false | 97 | deviceItem.ListView.delayRemove = false | ||
124 | // otherwise the last message will show again when this device reappears | 98 | // otherwise the last message will show again when this device reappears | ||
125 | statusSource.clearMessage() | 99 | statusSource.clearMessage() | ||
126 | 100 | | |||
127 | --devicenotifier.pendingDelegateRemoval // QTBUG-50380 | 101 | --devicenotifier.pendingDelegateRemoval // QTBUG-50380 | ||
128 | } | 102 | } | ||
129 | } | 103 | } | ||
130 | 104 | | |||
131 | Timer { | 105 | Timer { | ||
132 | id: updateStorageSpaceTimer | 106 | id: updateStorageSpaceTimer | ||
133 | interval: 5000 | 107 | interval: 5000 | ||
134 | repeat: true | 108 | repeat: true | ||
135 | running: mounted && plasmoid.expanded | 109 | running: isMounted && plasmoid.expanded | ||
136 | triggeredOnStart: true // Update the storage space as soon as we open the plasmoid | 110 | triggeredOnStart: true // Update the storage space as soon as we open the plasmoid | ||
137 | onTriggered: { | 111 | onTriggered: { | ||
138 | var service = sdSource.serviceForSource(udi); | 112 | var service = sdSource.serviceForSource(udi); | ||
139 | var operation = service.operationDescription("updateFreespace"); | 113 | var operation = service.operationDescription("updateFreespace"); | ||
140 | service.startOperationCall(operation); | 114 | service.startOperationCall(operation); | ||
141 | } | 115 | } | ||
142 | } | 116 | } | ||
143 | 117 | | |||
144 | RowLayout { | 118 | Component { | ||
145 | id: row | 119 | id: deviceActionComponent | ||
146 | anchors.horizontalCenter: parent.horizontalCenter | 120 | QQC2.Action { } | ||
147 | y: units.smallSpacing | 121 | } | ||
148 | width: parent.width - 2 * units.smallSpacing | 122 | | ||
149 | spacing: units.smallSpacing | 123 | function actionTriggered() { | ||
150 | 124 | var service | |||
151 | // FIXME: Device item loses focus on mounting/unmounting it, | 125 | var operationName | ||
152 | // or specifically, when some UI element changes. | 126 | var operation | ||
153 | PlasmaCore.IconItem { | 127 | if (!deviceItem.unmountable) { | ||
154 | id: deviceIcon | 128 | service = hpSource.serviceForSource(udi); | ||
155 | Layout.alignment: Qt.AlignTop | 129 | operation = service.operationDescription('invokeAction'); | ||
156 | Layout.preferredWidth: units.iconSizes.medium | 130 | operation.predicate = "test-predicate-openinwindow.desktop"; | ||
157 | Layout.preferredHeight: width | 131 | } else { | ||
158 | enabled: deviceItem.state == 0 | 132 | var wasMounted = isMounted; | ||
159 | active: iconToolTip.containsMouse | 133 | service = sdSource.serviceForSource(udi); | ||
160 | 134 | operation = service.operationDescription(wasMounted ? "unmount" : "mount"); | |||
161 | PlasmaCore.IconItem { | 135 | } | ||
162 | id: deviceEmblem | 136 | service.startOperationCall(operation); | ||
163 | anchors { | 137 | if (wasMounted) { | ||
164 | left: parent.left | 138 | deviceItem.collapse(); | ||
165 | bottom: parent.bottom | 139 | } | ||
166 | } | 140 | } | ||
167 | width: units.iconSizes.small | 141 | | ||
168 | height: width | 142 | | ||
169 | source: { | 143 | icon: sdSource.data[udi] == undefined ? "" : sdSource.data[udi].Icon | ||
144 | | ||||
145 | iconEmblem: { | ||||
146 | if (sdSource.data[udi] != undefined) { | ||||
170 | if (deviceItem.hasMessage) { | 147 | if (deviceItem.hasMessage) { | ||
171 | if (deviceItem.message.solidError === 0) { | 148 | if (deviceItem.message.solidError === 0) { | ||
172 | return "emblem-information" | 149 | return "emblem-information" | ||
173 | } else { | 150 | } else { | ||
174 | return "emblem-error" | 151 | return "emblem-error" | ||
175 | } | 152 | } | ||
176 | } else if (deviceItem.state == 0) { | 153 | } else if (deviceItem.state == 0 && Emblems && Emblems[0]) { | ||
177 | return emblemIcon | 154 | return Emblems[0] | ||
178 | } else { | 155 | } else { | ||
179 | return "" | 156 | return "" | ||
180 | } | 157 | } | ||
181 | } | 158 | } | ||
159 | return "" | ||||
182 | } | 160 | } | ||
183 | 161 | | |||
184 | PlasmaCore.ToolTipArea { | 162 | title: sdSource.data[udi] == undefined ? "" : sdSource.data[udi].Description | ||
185 | id: iconToolTip | 163 | | ||
186 | anchors.fill: parent | 164 | subtitle: { | ||
187 | subText: { | 165 | if (deviceItem.hasMessage) { | ||
188 | if ((mounted || deviceItem.state != 0) && model["Available Content"] !== "Audio") { | 166 | return deviceItem.message.error | ||
189 | if (model["Removable"]) { | | |||
190 | return i18n("It is currently <b>not safe</b> to remove this device: applications may be accessing it. Click the eject button to safely remove this device.") | | |||
191 | } else { | | |||
192 | return i18n("This device is currently accessible.") | | |||
193 | } | | |||
194 | } else { | | |||
195 | if (model["Removable"]) { | | |||
196 | if (model["In Use"]) { | | |||
197 | return i18n("It is currently <b>not safe</b> to remove this device: applications may be accessing other volumes on this device. Click the eject button on these other volumes to safely remove this device."); | | |||
198 | } else { | | |||
199 | return i18n("It is currently safe to remove this device.") | | |||
200 | } | | |||
201 | } else { | | |||
202 | return i18n("This device is not currently accessible.") | | |||
203 | } | 167 | } | ||
168 | if (deviceItem.state == 0) { | ||||
169 | if (!hpSource.data[udi]) { | ||||
170 | return "" | ||||
204 | } | 171 | } | ||
172 | if (freeSpaceKnown) { | ||||
173 | var freeSpaceText = sdSource.data[udi]["Free Space Text"] | ||||
174 | var totalSpaceText = sdSource.data[udi]["Size Text"] | ||||
175 | return i18nc("@info:status Free disk space", "%1 free of %2", freeSpaceText, totalSpaceText) | ||||
205 | } | 176 | } | ||
177 | return "" | ||||
178 | } else if (deviceItem.state == 1) { | ||||
179 | return i18nc("Accessing is a less technical word for Mounting; translation should be short and mean \'Currently mounting this device\'", "Accessing...") | ||||
180 | } else { | ||||
181 | return i18nc("Removing is a less technical word for Unmounting; translation should be short and mean \'Currently unmounting this device\'", "Removing...") | ||||
206 | } | 182 | } | ||
207 | } | 183 | } | ||
208 | 184 | | |||
209 | Column { | 185 | subtitleCanWrap: true | ||
210 | Layout.fillWidth: true | | |||
211 | | ||||
212 | move: Transition { | | |||
213 | NumberAnimation { property: "y"; duration: units.longDuration; easing.type: Easing.InOutQuad } | | |||
214 | // ensure opacity values return to 1.0 if the add transition animation has been interrupted | | |||
215 | NumberAnimation { property: "opacity"; to: 1.0 } | | |||
216 | } | | |||
217 | 186 | | |||
218 | add: Transition { | 187 | // Color the subtitle red for disks with less than 5% free space | ||
219 | NumberAnimation { | 188 | subtitleColor: { | ||
220 | property: "opacity" | 189 | if (freeSpaceKnown) { | ||
221 | from: 0 | 190 | if (freeSpace / totalSpace <= 0.05) { | ||
222 | to: 1 | 191 | return theme.negativeTextColor | ||
223 | duration: units.longDuration | | |||
224 | easing.type: Easing.InOutQuad | | |||
225 | } | | |||
226 | } | 192 | } | ||
227 | | ||||
228 | PlasmaComponents.Label { | | |||
229 | id: deviceLabel | | |||
230 | width: parent.width | | |||
231 | height: undefined // reset PlasmaComponent.Label's strange default height | | |||
232 | elide: Text.ElideRight | | |||
233 | } | 193 | } | ||
234 | 194 | return theme.textColor | |||
235 | PlasmaComponents.ProgressBar { | | |||
236 | id: freeSpaceBar | | |||
237 | width: parent.width | | |||
238 | height: units.gridUnit // default is * 1.6 | | |||
239 | visible: deviceItem.state == 0 && mounted | | |||
240 | minimumValue: 0 | | |||
241 | maximumValue: 100 | | |||
242 | | ||||
243 | PlasmaCore.ToolTipArea { | | |||
244 | anchors.fill: parent | | |||
245 | subText: freeSpaceText != "" ? i18nc("@info:status Free disk space", "%1 free", freeSpaceText) : "" | | |||
246 | } | 195 | } | ||
247 | 196 | | |||
248 | // ProgressBar eats click events, so we'll forward them manually here... | 197 | defaultActionButtonAction: QQC2.Action { | ||
249 | // setting enabled to false will also make the ProgressBar *look* disabled | 198 | icon.name: { | ||
250 | MouseArea { | 199 | if (!deviceItem.unmountable) { | ||
251 | anchors.fill: parent | 200 | return "document-open-folder" | ||
252 | onClicked: deviceItem.clicked(mouse) | 201 | } else { | ||
202 | return isMounted ? "media-eject" : "media-mount" | ||||
253 | } | 203 | } | ||
254 | } | 204 | } | ||
255 | | ||||
256 | PlasmaComponents.Label { | | |||
257 | id: actionMessage | | |||
258 | width: parent.width | | |||
259 | height: undefined | | |||
260 | opacity: 0.6 | | |||
261 | font.pointSize: theme.smallestFont.pointSize | | |||
262 | visible: deviceItem.state != 0 || (!actionsList.visible && !deviceItem.hasMessage) | | |||
263 | text: { | 205 | text: { | ||
264 | if (deviceItem.state == 0) { | 206 | if (!deviceItem.unmountable) { | ||
265 | if (!hpSource.data[udi]) { | 207 | return i18n("Open in File Manager") | ||
266 | return "" | | |||
267 | } | | |||
268 | | ||||
269 | var actions = hpSource.data[udi].actions | | |||
270 | if (actions.length > 1) { | | |||
271 | return i18np("1 action for this device", "%1 actions for this device", actions.length); | | |||
272 | } else { | 208 | } else { | ||
273 | return actions[0].text | 209 | var types = model["Device Types"]; | ||
274 | } | 210 | if (!isMounted) { | ||
275 | } else if (deviceItem.state == 1) { | 211 | return i18n("Mount") | ||
276 | return i18nc("Accessing is a less technical word for Mounting; translation should be short and mean \'Currently mounting this device\'", "Accessing...") | 212 | } else if (types && types.indexOf("OpticalDisc") !== -1) { | ||
213 | return i18n("Eject") | ||||
277 | } else { | 214 | } else { | ||
278 | return i18nc("Removing is a less technical word for Unmounting; translation should be short and mean \'Currently unmounting this device\'", "Removing...") | 215 | return i18n("Safely remove") | ||
279 | } | 216 | } | ||
280 | } | 217 | } | ||
281 | } | 218 | } | ||
282 | 219 | onTriggered: actionTriggered() | |||
283 | PlasmaComponents.Label { | | |||
284 | id: statusMessage | | |||
285 | width: parent.width | | |||
286 | height: undefined | | |||
287 | font.pointSize: theme.smallestFont.pointSize | | |||
288 | text: deviceItem.hasMessage ? (deviceItem.message.error || "") : "" | | |||
289 | wrapMode: Text.WordWrap | | |||
290 | maximumLineCount: 10 | | |||
291 | elide: Text.ElideRight | | |||
292 | visible: deviceItem.hasMessage | | |||
293 | } | | |||
294 | | ||||
295 | Item { // spacer | | |||
296 | width: 1 | | |||
297 | height: units.smallSpacing | | |||
298 | visible: actionsList.visible | | |||
299 | } | | |||
300 | | ||||
301 | ListView { | | |||
302 | id: actionsList | | |||
303 | width: parent.width | | |||
304 | interactive: false | | |||
305 | model: hpSource.data[udi] ? hpSource.data[udi].actions : null | | |||
306 | height: deviceItem.expanded ? actionsList.contentHeight : 0 | | |||
307 | visible: height > 0 | | |||
308 | cacheBuffer: 50000 // create all items | | |||
309 | delegate: ActionItem { | | |||
310 | width: actionsList.width | | |||
311 | icon: modelData.icon | | |||
312 | label: modelData.text | | |||
313 | predicate: modelData.predicate | | |||
314 | } | | |||
315 | highlight: PlasmaComponents.Highlight {} | | |||
316 | highlightMoveDuration: 0 | | |||
317 | highlightResizeDuration: 0 | | |||
318 | } | | |||
319 | } | 220 | } | ||
320 | 221 | | |||
321 | Item { | 222 | isBusy: deviceItem.state != 0 | ||
322 | Layout.preferredWidth: units.iconSizes.medium | 223 | | ||
323 | Layout.fillHeight: true | 224 | // We need a JS array full of QQC2 actions; this Instantiator creates them | ||
324 | 225 | // from the actions list of the data source | |||
325 | PlasmaComponents.ToolButton { | 226 | Instantiator { | ||
326 | id: actionButton | 227 | model: hpSource.data[udi] ? hpSource.data[udi].actions : [] | ||
327 | visible: !busyIndicator.visible && deviceItem.actionVisible | 228 | delegate: QQC2.Action { | ||
328 | enabled: !isRoot | 229 | text: modelData.text | ||
329 | onClicked: actionTriggered() | 230 | icon.name: modelData.icon | ||
330 | y: mounted ? deviceLabel.height + (freeSpaceBar.height - height - units.smallSpacing) / 2 : (deviceLabel.height + actionMessage.height - height) / 2 | 231 | onTriggered: { | ||
331 | } | 232 | var service = hpSource.serviceForSource(udi); | ||
332 | 233 | var operation = service.operationDescription('invokeAction'); | |||
333 | PlasmaComponents.BusyIndicator { | 234 | operation.predicate = modelData.predicate; | ||
334 | id: busyIndicator | 235 | service.startOperationCall(operation); | ||
335 | width: parent.width | 236 | devicenotifier.currentIndex = -1; | ||
336 | height: width | | |||
337 | running: visible | | |||
338 | visible: deviceItem.state != 0 | | |||
339 | } | 237 | } | ||
340 | } | 238 | } | ||
239 | onObjectAdded: contextualActionsModel.push(object) | ||||
341 | } | 240 | } | ||
342 | } | 241 | } |
There's an expandedDevice property in devicenotifier.qml which is supposed to decide which device is expanded. You either want to wire up this properly with the new delegate or nuke it.
For example in DeviceItem do
However, it resets the expandedDevice right away for whatever reason. But this should maybe give you an idea. Alternatively, since expanded state is now in the delegate, you could just emit a signal when a source is added to have the device expand.