Changeset View
Changeset View
Standalone View
Standalone View
applets/mediacontroller/contents/ui/main.qml
Show All 23 Lines | |||||
24 | import org.kde.plasma.plasmoid 2.0 | 24 | import org.kde.plasma.plasmoid 2.0 | ||
25 | import org.kde.plasma.core 2.0 as PlasmaCore | 25 | import org.kde.plasma.core 2.0 as PlasmaCore | ||
26 | import org.kde.plasma.components 2.0 as PlasmaComponents | 26 | import org.kde.plasma.components 2.0 as PlasmaComponents | ||
27 | import org.kde.plasma.extras 2.0 as PlasmaExtras | 27 | import org.kde.plasma.extras 2.0 as PlasmaExtras | ||
28 | 28 | | |||
29 | Item { | 29 | Item { | ||
30 | id: root | 30 | id: root | ||
31 | 31 | | |||
32 | property var currentMetadata: mpris2Source.currentData ? mpris2Source.currentData.Metadata : undefined | | |||
33 | property string track: { | | |||
34 | if (!currentMetadata) { | | |||
35 | return "" | | |||
36 | } | | |||
37 | var xesamTitle = currentMetadata["xesam:title"] | | |||
38 | if (xesamTitle) { | | |||
39 | return xesamTitle | | |||
40 | } | | |||
41 | // if no track title is given, print out the file name | | |||
42 | var xesamUrl = currentMetadata["xesam:url"] ? currentMetadata["xesam:url"].toString() : "" | | |||
43 | if (!xesamUrl) { | | |||
44 | return "" | | |||
45 | } | | |||
46 | var lastSlashPos = xesamUrl.lastIndexOf('/') | | |||
47 | if (lastSlashPos < 0) { | | |||
48 | return "" | | |||
49 | } | | |||
50 | var lastUrlPart = xesamUrl.substring(lastSlashPos + 1) | | |||
51 | return decodeURIComponent(lastUrlPart) | | |||
52 | } | | |||
53 | property string artist: { | | |||
54 | if (!currentMetadata) { | | |||
55 | return "" | | |||
56 | } | | |||
57 | var xesamArtist = currentMetadata["xesam:artist"] | | |||
58 | if (!xesamArtist) { | | |||
59 | return ""; | | |||
60 | } | | |||
61 | | ||||
62 | if (typeof xesamArtist == "string") { | | |||
63 | return xesamArtist | | |||
64 | } else { | | |||
65 | return xesamArtist.join(", ") | | |||
66 | } | | |||
67 | } | | |||
68 | property string albumArt: currentMetadata ? currentMetadata["mpris:artUrl"] || "" : "" | | |||
69 | | ||||
70 | readonly property string identity: !root.noPlayer ? mpris2Source.currentData.Identity || mpris2Source.current : "" | | |||
71 | | ||||
72 | property bool noPlayer: mpris2Source.sources.length <= 1 | | |||
73 | | ||||
74 | property var mprisSourcesModel: [] | | |||
75 | | ||||
76 | readonly property bool canControl: (!root.noPlayer && mpris2Source.currentData.CanControl) || false | | |||
77 | readonly property bool canGoPrevious: (canControl && mpris2Source.currentData.CanGoPrevious) || false | | |||
78 | readonly property bool canGoNext: (canControl && mpris2Source.currentData.CanGoNext) || false | | |||
79 | readonly property bool canPlay: (canControl && mpris2Source.currentData.CanPlay) || false | | |||
80 | readonly property bool canPause: (canControl && mpris2Source.currentData.CanPause) || false | | |||
81 | | ||||
82 | Plasmoid.switchWidth: units.gridUnit * 14 | 32 | Plasmoid.switchWidth: units.gridUnit * 14 | ||
83 | Plasmoid.switchHeight: units.gridUnit * 10 | 33 | Plasmoid.switchHeight: units.gridUnit * 10 | ||
84 | Plasmoid.icon: "media-playback-playing" | 34 | Plasmoid.icon: "media-playback-playing" | ||
85 | Plasmoid.toolTipMainText: i18n("No media playing") | 35 | Plasmoid.toolTipMainText: i18n("No media playing") | ||
86 | Plasmoid.toolTipSubText: identity | 36 | Plasmoid.toolTipSubText: Media.currentPlayer | ||
87 | Plasmoid.toolTipTextFormat: Text.PlainText | 37 | Plasmoid.toolTipTextFormat: Text.PlainText | ||
88 | Plasmoid.status: PlasmaCore.Types.PassiveStatus | 38 | Plasmoid.status: PlasmaCore.Types.PassiveStatus | ||
89 | 39 | | |||
90 | Plasmoid.onContextualActionsAboutToShow: { | 40 | Plasmoid.onContextualActionsAboutToShow: { | ||
91 | plasmoid.clearActions() | 41 | plasmoid.clearActions() | ||
92 | 42 | | |||
93 | if (root.noPlayer) { | 43 | if (Media.noPlayers) { | ||
94 | return | 44 | return | ||
95 | } | 45 | } | ||
96 | 46 | | |||
97 | if (mpris2Source.currentData.CanRaise) { | 47 | if (mpris2Source.currentData.CanRaise) { | ||
98 | var icon = mpris2Source.currentData["Desktop Icon Name"] || "" | 48 | var icon = mpris2Source.currentData["Desktop Icon Name"] || "" | ||
99 | plasmoid.setAction("open", i18nc("Open player window or bring it to the front if already open", "Open"), icon) | 49 | plasmoid.setAction("open", i18nc("Open player window or bring it to the front if already open", "Open"), icon) | ||
100 | } | 50 | } | ||
101 | 51 | | |||
102 | if (canControl) { | 52 | if (canControl) { | ||
103 | plasmoid.setAction("previous", i18nc("Play previous track", "Previous Track"), | 53 | plasmoid.setAction("previous", i18nc("Play previous track", "Previous Track"), | ||
104 | Qt.application.layoutDirection === Qt.RightToLeft ? "media-skip-forward" : "media-skip-backward"); | 54 | Qt.application.layoutDirection === Qt.RightToLeft ? "media-skip-forward" : "media-skip-backward") | ||
105 | plasmoid.action("previous").enabled = Qt.binding(function() { | 55 | plasmoid.action("previous").enabled = Qt.binding(function() { | ||
106 | return root.canGoPrevious | 56 | return Media.canGoPrevious | ||
107 | }) | 57 | }) | ||
108 | 58 | | |||
109 | // if CanPause, toggle the menu entry between Play & Pause, otherwise always use Play | 59 | // if CanPause, toggle the menu entry between Play & Pause, otherwise always use Play | ||
110 | if (root.state == "playing" && root.canPause) { | 60 | if (Media.state == "playing" && Media.canPause) { | ||
111 | plasmoid.setAction("pause", i18nc("Pause playback", "Pause"), "media-playback-pause") | 61 | plasmoid.setAction("pause", i18nc("Pause playback", "Pause"), "media-playback-pause") | ||
112 | plasmoid.action("pause").enabled = Qt.binding(function() { | 62 | plasmoid.action("pause").enabled = Qt.binding(function() { | ||
113 | return root.state === "playing" && root.canPause; | 63 | return Media.state === "playing" && Media.canPause | ||
114 | }); | 64 | }) | ||
115 | } else { | 65 | } else { | ||
116 | plasmoid.setAction("play", i18nc("Start playback", "Play"), "media-playback-start") | 66 | plasmoid.setAction("play", i18nc("Start playback", "Play"), "media-playback-start") | ||
117 | plasmoid.action("play").enabled = Qt.binding(function() { | 67 | plasmoid.action("play").enabled = Qt.binding(function() { | ||
118 | return root.state !== "playing" && root.canPlay; | 68 | return Media.state !== "playing" && Media.canPlay | ||
119 | }); | 69 | }) | ||
120 | } | 70 | } | ||
121 | 71 | | |||
122 | plasmoid.setAction("next", i18nc("Play next track", "Next Track"), | 72 | plasmoid.setAction("next", i18nc("Play next track", "Next Track"), | ||
123 | Qt.application.layoutDirection === Qt.RightToLeft ? "media-skip-backward" : "media-skip-forward") | 73 | Qt.application.layoutDirection === Qt.RightToLeft ? "media-skip-backward" : "media-skip-forward") | ||
124 | plasmoid.action("next").enabled = Qt.binding(function() { | 74 | plasmoid.action("next").enabled = Qt.binding(function() { | ||
125 | return root.canGoNext | 75 | return Media.canGoNext | ||
126 | }) | 76 | }) | ||
127 | 77 | | |||
128 | plasmoid.setAction("stop", i18nc("Stop playback", "Stop"), "media-playback-stop") | 78 | plasmoid.setAction("stop", i18nc("Stop playback", "Stop"), "media-playback-stop") | ||
129 | plasmoid.action("stop").enabled = Qt.binding(function() { | 79 | plasmoid.action("stop").enabled = Qt.binding(function() { | ||
130 | return root.state === "playing" || root.state === "paused"; | 80 | return Media.state === "playing" || Media.state === "paused" | ||
131 | }) | 81 | }) | ||
132 | } | 82 | } | ||
133 | 83 | | |||
134 | if (mpris2Source.currentData.CanQuit) { | 84 | if (mpris2Source.currentData.CanQuit) { | ||
135 | plasmoid.setActionSeparator("quitseparator"); | 85 | plasmoid.setActionSeparator("quitseparator") | ||
136 | plasmoid.setAction("quit", i18nc("Quit player", "Quit"), "application-exit") | 86 | plasmoid.setAction("quit", i18nc("Quit player", "Quit"), "application-exit") | ||
137 | } | 87 | } | ||
138 | } | 88 | } | ||
139 | 89 | | |||
140 | // HACK Some players like Amarok take quite a while to load the next track | 90 | // HACK Some players like Amarok take quite a while to load the next track | ||
141 | // this avoids having the plasmoid jump between popup and panel | 91 | // this avoids having the plasmoid jump between popup and panel | ||
142 | onStateChanged: { | 92 | onStateChanged: { | ||
143 | if (state != "") { | 93 | if (Media.state != "") { | ||
144 | plasmoid.status = PlasmaCore.Types.ActiveStatus | 94 | plasmoid.status = PlasmaCore.Types.ActiveStatus | ||
145 | } else { | 95 | } else { | ||
146 | updatePlasmoidStatusTimer.restart() | 96 | updatePlasmoidStatusTimer.restart() | ||
147 | } | 97 | } | ||
148 | } | 98 | } | ||
149 | 99 | | |||
150 | Timer { | 100 | Timer { | ||
151 | id: updatePlasmoidStatusTimer | 101 | id: updatePlasmoidStatusTimer | ||
152 | interval: 3000 | 102 | interval: 3000 | ||
153 | onTriggered: { | 103 | onTriggered: { | ||
154 | if (state != "") { | 104 | if (Media.state != "") { | ||
155 | plasmoid.status = PlasmaCore.Types.ActiveStatus | 105 | plasmoid.status = PlasmaCore.Types.ActiveStatus | ||
156 | } else { | 106 | } else { | ||
157 | plasmoid.status = PlasmaCore.Types.PassiveStatus | 107 | plasmoid.status = PlasmaCore.Types.PassiveStatus | ||
158 | } | 108 | } | ||
159 | } | 109 | } | ||
160 | } | 110 | } | ||
161 | 111 | | |||
162 | Plasmoid.fullRepresentation: ExpandedRepresentation {} | 112 | Plasmoid.fullRepresentation: ExpandedRepresentation {} | ||
163 | 113 | | |||
164 | Plasmoid.compactRepresentation: PlasmaCore.IconItem { | 114 | Plasmoid.compactRepresentation: PlasmaCore.IconItem { | ||
165 | source: root.state === "playing" ? "media-playback-playing" : | 115 | source: { | ||
166 | root.state === "paused" ? "media-playback-paused" : | 116 | if (Media.state === "playing") { | ||
167 | "media-playback-stopped" | 117 | return "media-playback-playing" | ||
118 | } else if (Media.state == "paused") { | ||||
119 | return "media-playback-paused" | ||||
120 | } else { | ||||
121 | return "media-playback-stopped" | ||||
122 | } | ||||
123 | } | ||||
168 | active: compactMouse.containsMouse | 124 | active: compactMouse.containsMouse | ||
169 | 125 | | |||
170 | MouseArea { | 126 | MouseArea { | ||
171 | id: compactMouse | 127 | id: compactMouse | ||
172 | anchors.fill: parent | 128 | anchors.fill: parent | ||
173 | hoverEnabled: true | 129 | hoverEnabled: true | ||
174 | acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton | 130 | acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton | ||
175 | 131 | | |||
176 | onWheel: { | 132 | onWheel: Media.adjustVolume((wheel.angleDelta.y / 120) * 0.03) | ||
177 | var service = mpris2Source.serviceForSource(mpris2Source.current) | | |||
178 | var operation = service.operationDescription("ChangeVolume") | | |||
179 | operation.delta = (wheel.angleDelta.y / 120) * 0.03 | | |||
180 | operation.showOSD = true | | |||
181 | service.startOperationCall(operation) | | |||
182 | } | | |||
183 | 133 | | |||
184 | onClicked: { | 134 | onClicked: { | ||
185 | switch (mouse.button) { | 135 | switch (mouse.button) { | ||
186 | case Qt.MiddleButton: | 136 | case Qt.MiddleButton: | ||
187 | root.togglePlaying() | 137 | Media.togglePlaying() | ||
188 | break | 138 | break | ||
189 | case Qt.BackButton: | 139 | case Qt.BackButton: | ||
190 | root.action_previous() | 140 | Media.perform(Media.previous) | ||
191 | break | 141 | break | ||
192 | case Qt.ForwardButton: | 142 | case Qt.ForwardButton: | ||
193 | root.action_next() | 143 | Media.perform(Media.next) | ||
194 | break | 144 | break | ||
195 | default: | 145 | default: | ||
196 | plasmoid.expanded = !plasmoid.expanded | 146 | plasmoid.expanded = !plasmoid.expanded | ||
197 | } | 147 | } | ||
198 | } | 148 | } | ||
199 | } | 149 | } | ||
200 | } | 150 | } | ||
201 | 151 | | |||
202 | PlasmaCore.DataSource { | | |||
203 | id: mpris2Source | | |||
204 | | ||||
205 | readonly property string multiplexSource: "@multiplex" | | |||
206 | property string current: multiplexSource | | |||
207 | | ||||
208 | readonly property var currentData: data[current] | | |||
209 | | ||||
210 | engine: "mpris2" | | |||
211 | connectedSources: sources | | |||
212 | | ||||
213 | onSourceAdded: { | | |||
214 | updateMprisSourcesModel() | | |||
215 | } | | |||
216 | | ||||
217 | onSourceRemoved: { | | |||
218 | // if player is closed, reset to multiplex source | | |||
219 | if (source === current) { | | |||
220 | current = multiplexSource | | |||
221 | } | | |||
222 | updateMprisSourcesModel() | | |||
223 | } | | |||
224 | } | | |||
225 | | ||||
226 | Component.onCompleted: { | | |||
227 | mpris2Source.serviceForSource("@multiplex").enableGlobalShortcuts() | | |||
228 | updateMprisSourcesModel() | | |||
229 | } | | |||
230 | | ||||
231 | function togglePlaying() { | | |||
232 | if (root.state === "playing") { | | |||
233 | if (root.canPause) { | | |||
234 | root.action_pause(); | | |||
235 | } | | |||
236 | } else { | | |||
237 | if (root.canPlay) { | | |||
238 | root.action_play(); | | |||
239 | } | | |||
240 | } | | |||
241 | } | | |||
242 | | ||||
243 | function action_open() { | | |||
broulik: You can't remove these, they are wired up to the `plasmoid.setAction` calls above. (Yes, I'd… | |||||
244 | serviceOp(mpris2Source.current, "Raise"); | | |||
245 | } | | |||
246 | function action_quit() { | | |||
247 | serviceOp(mpris2Source.current, "Quit"); | | |||
248 | } | | |||
249 | | ||||
250 | function action_play() { | | |||
251 | serviceOp(mpris2Source.current, "Play"); | | |||
252 | } | | |||
253 | | ||||
254 | function action_pause() { | | |||
255 | serviceOp(mpris2Source.current, "Pause"); | | |||
256 | } | | |||
257 | | ||||
258 | function action_playPause() { | | |||
259 | serviceOp(mpris2Source.current, "PlayPause"); | | |||
260 | } | | |||
261 | | ||||
262 | function action_previous() { | | |||
263 | serviceOp(mpris2Source.current, "Previous"); | | |||
264 | } | | |||
265 | | ||||
266 | function action_next() { | | |||
267 | serviceOp(mpris2Source.current, "Next"); | | |||
268 | } | | |||
269 | | ||||
270 | function action_stop() { | | |||
271 | serviceOp(mpris2Source.current, "Stop"); | | |||
272 | } | | |||
273 | | ||||
274 | function serviceOp(src, op) { | | |||
275 | var service = mpris2Source.serviceForSource(src); | | |||
276 | var operation = service.operationDescription(op); | | |||
277 | return service.startOperationCall(operation); | | |||
278 | } | | |||
279 | | ||||
280 | function updateMprisSourcesModel () { | | |||
281 | | ||||
282 | var model = [{ | | |||
283 | 'text': i18n("Choose player automatically"), | | |||
284 | 'icon': 'emblem-favorite', | | |||
285 | 'source': mpris2Source.multiplexSource | | |||
286 | }] | | |||
287 | | ||||
288 | var sources = mpris2Source.sources | | |||
289 | for (var i = 0, length = sources.length; i < length; ++i) { | | |||
290 | var source = sources[i] | | |||
291 | if (source === mpris2Source.multiplexSource) { | | |||
292 | continue | | |||
293 | } | | |||
294 | | ||||
295 | model.push({ | | |||
296 | 'text': mpris2Source.data[source]["Identity"], | | |||
297 | 'icon': mpris2Source.data[source]["Desktop Icon Name"] || mpris2Source.data[source]["Desktop Entry"] || source, | | |||
298 | 'source': source | | |||
299 | }); | | |||
300 | } | | |||
301 | | ||||
302 | root.mprisSourcesModel = model; | | |||
303 | } | | |||
304 | | ||||
305 | states: [ | 152 | states: [ | ||
306 | State { | 153 | State { | ||
307 | name: "playing" | 154 | name: "playing" | ||
308 | when: !root.noPlayer && mpris2Source.currentData.PlaybackStatus === "Playing" | 155 | when: Media.state == "playing" | ||
broulik: You can just set `state: Media.state` rather than `when` on every state | |||||
309 | 156 | | |||
310 | PropertyChanges { | 157 | PropertyChanges { | ||
311 | target: plasmoid | 158 | target: plasmoid | ||
312 | icon: albumArt ? albumArt : "media-playback-playing" | 159 | icon: Media.albumArt ? Media.albumArt : "media-playback-playing" | ||
We don't set album art on the icon anymore, cf D28917 broulik: We don't set album art on the icon anymore, cf D28917 | |||||
313 | toolTipMainText: track | 160 | toolTipMainText: Media.currentTrack | ||
314 | toolTipSubText: artist ? i18nc("by Artist (player name)", "by %1 (%2)", artist, identity) : identity | 161 | toolTipSubText: Media.currentArtist ? i18nc("by Artist (player name)", "by %1 (%2)", Media.currentArtist, Media.currentPlayer) : Media.currentPlayer | ||
315 | } | 162 | } | ||
316 | }, | 163 | }, | ||
317 | State { | 164 | State { | ||
318 | name: "paused" | 165 | name: "paused" | ||
319 | when: !root.noPlayer && mpris2Source.currentData.PlaybackStatus === "Paused" | 166 | when: Media.state == "paused" | ||
320 | 167 | | |||
321 | PropertyChanges { | 168 | PropertyChanges { | ||
322 | target: plasmoid | 169 | target: plasmoid | ||
323 | icon: albumArt ? albumArt : "media-playback-paused" | 170 | icon: Media.albumArt ? Media.albumArt : "media-playback-paused" | ||
324 | toolTipMainText: track | 171 | toolTipMainText: Media.currentTrack | ||
325 | toolTipSubText: artist ? i18nc("by Artist (paused, player name)", "by %1 (paused, %2)", artist, identity) : i18nc("Paused (player name)", "Paused (%1)", identity) | 172 | toolTipSubText: Media.currentArtist ? i18nc("by Artist (paused, player name)", "by %1 (paused, %2)", Media.currentArtist, Media.currentPlayer) : i18nc("Paused (player name)", "Paused (%1)", Media.currentPlayer) | ||
326 | } | 173 | } | ||
327 | } | 174 | } | ||
328 | ] | 175 | ] | ||
329 | } | 176 | } |
You can't remove these, they are wired up to the plasmoid.setAction calls above. (Yes, I'd like to have an API to pass a JS callback :p but this is how it works right now)