Merge lp:~artmello/ubuntu-ui-extras/ubuntu-ui-extras-videoplayer-components into lp:ubuntu-ui-extras
- ubuntu-ui-extras-videoplayer-components
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~artmello/ubuntu-ui-extras/ubuntu-ui-extras-videoplayer-components |
Merge into: | lp:ubuntu-ui-extras |
Diff against target: |
1320 lines (+1258/-0) 11 files modified
modules/Ubuntu/Components/Extras/CMakeLists.txt (+1/-0) modules/Ubuntu/Components/Extras/MediaPlayer/AbstractPlayer.qml (+213/-0) modules/Ubuntu/Components/Extras/MediaPlayer/CMakeLists.txt (+2/-0) modules/Ubuntu/Components/Extras/MediaPlayer/ContentPopover.qml (+63/-0) modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionBar.qml (+83/-0) modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionsDrawer.qml (+124/-0) modules/Ubuntu/Components/Extras/MediaPlayer/PlayerPanel.qml (+111/-0) modules/Ubuntu/Components/Extras/MediaPlayer/TimeLine.qml (+98/-0) modules/Ubuntu/Components/Extras/MediaPlayer/VideoPlayerToolbar.qml (+128/-0) modules/Ubuntu/Components/Extras/VideoPlayer.qml (+434/-0) modules/Ubuntu/Components/Extras/qmldir (+1/-0) |
To merge this branch: | bzr merge lp:~artmello/ubuntu-ui-extras/ubuntu-ui-extras-videoplayer-components |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
system-apps-ci-bot | continuous-integration | Needs Fixing | |
PS Jenkins bot | continuous-integration | Approve | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+280450@code.launchpad.net |
Commit message
Add VideoPlayer Component
Description of the change
Add VideoPlayer Component
PS Jenkins bot (ps-jenkins) wrote : | # |
- 74. By Arthur Mello
-
Add property to set title
Add back action
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:74
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 75. By Arthur Mello
-
Fix video replay after ending
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:75
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 76. By Arthur Mello
-
Merge with trunk
- 77. By Arthur Mello
-
Make sure to stop player before closing the stream
- 78. By Arthur Mello
-
Move title bar to a loader so it will not show up during component loading
- 79. By Arthur Mello
-
Fix toolbars open/close
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:79
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 80. By Arthur Mello
-
Changes to botton bar:
- Change Share button for a Play/Pause one
- Add more vertical space between time labels and scrubber
- Add button anchored to the right to toggle fullscreen - 81. By Arthur Mello
-
Changes to titlebar
- Add share/save buttons by default
- Application can control if share/save buttons are visible
- Application can add it owns actions to the titlebar - 82. By Arthur Mello
-
Fix toolbar actions issue
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:82
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 83. By Arthur Mello
-
Make sure the dismissBars timer is not triggered twice
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:83
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 84. By Arthur Mello
-
Do not trigger the same action twice
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:84
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 85. By Arthur Mello
-
Fix Share/Save actions so the ContentPeerPicker can be called from inside the component
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:85
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 86. By Arthur Mello
-
Fix behavior when the Video ends
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:86
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 87. By Arthur Mello
-
Handle fullscreen propoty inside video player
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:87
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 88. By Arthur Mello
-
Fix typo
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:88
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 89. By Arthur Mello
-
Add elite to title bar text
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:89
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 90. By Arthur Mello
-
Merge with trunk
- 91. By Arthur Mello
-
Change component API following review
- 92. By Arthur Mello
-
Make fullscreen a rdonly property
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:91
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:92
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:92
https:/
Executed test runs:
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Unmerged revisions
- 92. By Arthur Mello
-
Make fullscreen a rdonly property
- 91. By Arthur Mello
-
Change component API following review
- 90. By Arthur Mello
-
Merge with trunk
- 89. By Arthur Mello
-
Add elite to title bar text
- 88. By Arthur Mello
-
Fix typo
- 87. By Arthur Mello
-
Handle fullscreen propoty inside video player
- 86. By Arthur Mello
-
Fix behavior when the Video ends
- 85. By Arthur Mello
-
Fix Share/Save actions so the ContentPeerPicker can be called from inside the component
- 84. By Arthur Mello
-
Do not trigger the same action twice
- 83. By Arthur Mello
-
Make sure the dismissBars timer is not triggered twice
Preview Diff
1 | === modified file 'modules/Ubuntu/Components/Extras/CMakeLists.txt' |
2 | --- modules/Ubuntu/Components/Extras/CMakeLists.txt 2014-12-09 11:14:16 +0000 |
3 | +++ modules/Ubuntu/Components/Extras/CMakeLists.txt 2016-03-09 10:31:35 +0000 |
4 | @@ -21,4 +21,5 @@ |
5 | |
6 | add_subdirectory(plugin) |
7 | add_subdirectory(Example) |
8 | +add_subdirectory(MediaPlayer) |
9 | add_subdirectory(PhotoEditor) |
10 | |
11 | === added directory 'modules/Ubuntu/Components/Extras/MediaPlayer' |
12 | === added file 'modules/Ubuntu/Components/Extras/MediaPlayer/AbstractPlayer.qml' |
13 | --- modules/Ubuntu/Components/Extras/MediaPlayer/AbstractPlayer.qml 1970-01-01 00:00:00 +0000 |
14 | +++ modules/Ubuntu/Components/Extras/MediaPlayer/AbstractPlayer.qml 2016-03-09 10:31:35 +0000 |
15 | @@ -0,0 +1,213 @@ |
16 | +/* |
17 | + * Copyright (C) 2013-2015 Canonical, Ltd. |
18 | + * |
19 | + * Authors: |
20 | + * Ugo Riboni <ugo.riboni@canonical.com> |
21 | + * Michał Sawicz <michal.sawicz@canonical.com> |
22 | + * Renato Araujo Oliveira Filho <renato@canonical.com> |
23 | + * Arthur Renato Mello <arthur.mello@canonical.com> |
24 | + * |
25 | + * This program is free software; you can redistribute it and/or modify |
26 | + * it under the terms of the GNU General Public License as published by |
27 | + * the Free Software Foundation; version 3. |
28 | + * |
29 | + * This program is distributed in the hope that it will be useful, |
30 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
31 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
32 | + * GNU General Public License for more details. |
33 | + * |
34 | + * You should have received a copy of the GNU General Public License |
35 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
36 | + */ |
37 | + |
38 | +import QtQuick 2.4 |
39 | +import QtMultimedia 5.0 |
40 | + |
41 | +Item { |
42 | + id: player |
43 | + |
44 | + property MediaPlayer mediaPlayer: _mediaPlayer |
45 | + property alias source: _mediaPlayer.source |
46 | + |
47 | + property int forwardSeekStep: Math.min(60000, _mediaPlayer.duration * 0.05) |
48 | + property int backwardSeekStep: Math.min(30000, _mediaPlayer.duration * 0.025) |
49 | + |
50 | + |
51 | + signal error(int errorCode, string errorString) |
52 | + |
53 | + state: "stopped" |
54 | + |
55 | + function stop() { |
56 | + state = "stopped" |
57 | + } |
58 | + |
59 | + function play() { |
60 | + state = "playing" |
61 | + } |
62 | + |
63 | + function pause() { |
64 | + state = "paused" |
65 | + } |
66 | + |
67 | + function togglePause() { |
68 | + if (playing) { |
69 | + pause() |
70 | + } else if (paused) { |
71 | + play() |
72 | + } |
73 | + } |
74 | + |
75 | + function seekForward() { |
76 | + return seek(forwardSeekStep) |
77 | + } |
78 | + |
79 | + function seekBackward() { |
80 | + return seek(-backwardSeekStep) |
81 | + } |
82 | + |
83 | + function seek(value) { |
84 | + if (_mediaPlayer.seekable) { |
85 | + if (state != "playing" && state != "paused") { |
86 | + state = "playing" |
87 | + state = "paused" |
88 | + } |
89 | + _mediaPlayer.seek(_mediaPlayer.position + value) |
90 | + } else { |
91 | + return false |
92 | + } |
93 | + return true |
94 | + } |
95 | + |
96 | + function fastForward() { |
97 | + if (_mediaPlayer.seekable) { |
98 | + if (state == "forwarding") { |
99 | + scrubbingTimer.step = Math.min(5 * forwardSeekStep, scrubbingTimer.step * 1.5) |
100 | + } else { |
101 | + state = "forwarding" |
102 | + } |
103 | + return true |
104 | + } else { |
105 | + return false |
106 | + } |
107 | + } |
108 | + |
109 | + function rewind() { |
110 | + if (_mediaPlayer.seekable) { |
111 | + if (state == "rewinding") { |
112 | + scrubbingTimer.step = Math.max(-5 * forwardSeekStep, scrubbingTimer.step * 1.5) |
113 | + } else { |
114 | + state = "rewinding" |
115 | + } |
116 | + return true |
117 | + } else { |
118 | + return false |
119 | + } |
120 | + } |
121 | + |
122 | + function hideVideoOutput() { |
123 | + _videoOutput.visible = false |
124 | + } |
125 | + |
126 | + function showVideoOutput() { |
127 | + _videoOutput.visible = true |
128 | + } |
129 | + |
130 | + Rectangle { |
131 | + id: bg |
132 | + anchors.fill: parent |
133 | + color: "#1B1B1B" |
134 | + opacity: 0.85 |
135 | + } |
136 | + |
137 | + VideoOutput { |
138 | + id: _videoOutput |
139 | + |
140 | + source: _mediaPlayer |
141 | + anchors.fill: parent |
142 | + smooth: true |
143 | + |
144 | + Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuad } } |
145 | + } |
146 | + |
147 | + MediaPlayer { |
148 | + id: _mediaPlayer |
149 | + |
150 | + onError: { |
151 | + console.error("AbstractPlayer: " + error + ":" + errorString) |
152 | + player.error(error, errorString) |
153 | + } |
154 | + |
155 | + onPlaybackStateChanged: { |
156 | + // Make sure that the app toggles the play/pause button when playbackStatus |
157 | + // changes from underneath it in media-hub/qtubuntu-media |
158 | + if (_mediaPlayer.playbackState == MediaPlayer.PausedState) { |
159 | + player.pause() |
160 | + } else if (_mediaPlayer.playbackState == MediaPlayer.PlayingState) { |
161 | + player.play() |
162 | + } |
163 | + } |
164 | + } |
165 | + |
166 | + Timer { |
167 | + id: scrubbingTimer |
168 | + interval: 500 |
169 | + repeat: true |
170 | + |
171 | + property int step |
172 | + |
173 | + onTriggered: |
174 | + if (_mediaPlayer.position + step < 0) { |
175 | + _mediaPlayer.seek(0) |
176 | + player.state = "playing" |
177 | + } else { |
178 | + _mediaPlayer.seek(_mediaPlayer.position + step) |
179 | + } |
180 | + } |
181 | + |
182 | + states: [ |
183 | + State { |
184 | + name: "stopped" |
185 | + StateChangeScript { script: _mediaPlayer.stop() } |
186 | + PropertyChanges { target: scrubbingTimer; running: false } |
187 | + }, |
188 | + |
189 | + State { |
190 | + name: "playing" |
191 | + PropertyChanges { target: _mediaPlayer; playbackRate: 1.0; muted: false } |
192 | + StateChangeScript { script: _mediaPlayer.play() } |
193 | + PropertyChanges { target: scrubbingTimer; running: false } |
194 | + }, |
195 | + |
196 | + State { |
197 | + name: "paused" |
198 | + StateChangeScript { script: _mediaPlayer.pause() } |
199 | + PropertyChanges { target: scrubbingTimer; running: false } |
200 | + }, |
201 | + |
202 | + State { |
203 | + name: "forwarding" |
204 | + PropertyChanges { target: _mediaPlayer; muted: true } |
205 | + StateChangeScript { script: _mediaPlayer.pause() } |
206 | + PropertyChanges { target: scrubbingTimer; running: true } |
207 | + }, |
208 | + |
209 | + State { |
210 | + name: "rewinding" |
211 | + PropertyChanges { target: _mediaPlayer; muted: true } |
212 | + StateChangeScript { script: _mediaPlayer.pause() } |
213 | + PropertyChanges { target: scrubbingTimer; running: true } |
214 | + } |
215 | + ] |
216 | + |
217 | + transitions: [ |
218 | + Transition { |
219 | + to: "forwarding" |
220 | + PropertyAction { target: scrubbingTimer; property: "step"; value: forwardSeekStep } |
221 | + }, |
222 | + |
223 | + Transition { |
224 | + to: "rewinding" |
225 | + PropertyAction { target: scrubbingTimer; property: "step"; value: -forwardSeekStep } |
226 | + } |
227 | + ] |
228 | +} |
229 | |
230 | === added file 'modules/Ubuntu/Components/Extras/MediaPlayer/CMakeLists.txt' |
231 | --- modules/Ubuntu/Components/Extras/MediaPlayer/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
232 | +++ modules/Ubuntu/Components/Extras/MediaPlayer/CMakeLists.txt 2016-03-09 10:31:35 +0000 |
233 | @@ -0,0 +1,2 @@ |
234 | +file(GLOB QML_FILES *.qml *.js) |
235 | +install(FILES ${QML_FILES} DESTINATION ${PLUGIN_DIR}/MediaPlayer) |
236 | |
237 | === added file 'modules/Ubuntu/Components/Extras/MediaPlayer/ContentPopover.qml' |
238 | --- modules/Ubuntu/Components/Extras/MediaPlayer/ContentPopover.qml 1970-01-01 00:00:00 +0000 |
239 | +++ modules/Ubuntu/Components/Extras/MediaPlayer/ContentPopover.qml 2016-03-09 10:31:35 +0000 |
240 | @@ -0,0 +1,63 @@ |
241 | +/* |
242 | + * Copyright 2014 Canonical Ltd. |
243 | + * |
244 | + * This program is free software; you can redistribute it and/or modify |
245 | + * it under the terms of the GNU General Public License as published by |
246 | + * the Free Software Foundation; version 3. |
247 | + * |
248 | + * This program is distributed in the hope that it will be useful, |
249 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
250 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
251 | + * GNU General Public License for more details. |
252 | + * |
253 | + * You should have received a copy of the GNU General Public License |
254 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
255 | + */ |
256 | + |
257 | +import QtQuick 2.4 |
258 | +import Ubuntu.Components 1.3 |
259 | +import Ubuntu.Components.Popups 1.3 |
260 | +import Ubuntu.Content 0.1 |
261 | + |
262 | +PopupBase { |
263 | + id: contentPopover |
264 | + |
265 | + property var transferContentType |
266 | + property var transferItems |
267 | + |
268 | + property alias transferContentHandler: contentPeerPicker.handler |
269 | + |
270 | + signal contentPeerSelected() |
271 | + |
272 | + fadingAnimation: UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } |
273 | + |
274 | + Component.onCompleted: { |
275 | + contentPeerPicker.peerSelected.connect(contentPeerSelected); |
276 | + } |
277 | + |
278 | + Rectangle { |
279 | + anchors.fill: parent |
280 | + color: theme.palette.normal.overlay |
281 | + } |
282 | + |
283 | + ContentPeerPicker { |
284 | + id: contentPeerPicker |
285 | + |
286 | + Component.onCompleted: { |
287 | + contentType = parent.transferContentType; |
288 | + } |
289 | + |
290 | + visible: true |
291 | + |
292 | + onPeerSelected: { |
293 | + var transfer = peer.request(); |
294 | + if (transfer.state === ContentTransfer.InProgress) { |
295 | + transfer.items = parent.transferItems; |
296 | + transfer.state = ContentTransfer.Charged; |
297 | + } |
298 | + PopupUtils.close(contentPopover); |
299 | + } |
300 | + onCancelPressed: PopupUtils.close(contentPopover); |
301 | + } |
302 | +} |
303 | + |
304 | |
305 | === added file 'modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionBar.qml' |
306 | --- modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionBar.qml 1970-01-01 00:00:00 +0000 |
307 | +++ modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionBar.qml 2016-03-09 10:31:35 +0000 |
308 | @@ -0,0 +1,83 @@ |
309 | +/* |
310 | + * Copyright (C) 2016 Canonical, Ltd. |
311 | + * |
312 | + * This program is free software; you can redistribute it and/or modify |
313 | + * it under the terms of the GNU General Public License as published by |
314 | + * the Free Software Foundation; version 3. |
315 | + * |
316 | + * This program is distributed in the hope that it will be useful, |
317 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
318 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
319 | + * GNU General Public License for more details. |
320 | + * |
321 | + * You should have received a copy of the GNU General Public License |
322 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
323 | + */ |
324 | + |
325 | +import QtQuick 2.4 |
326 | +import Ubuntu.Components 1.3 |
327 | + |
328 | +Item { |
329 | + id: playerActionBar |
330 | + |
331 | + property list<Action> actions |
332 | + |
333 | + signal drawerOpened() |
334 | + signal drawerClosed() |
335 | + |
336 | + function closeDrawer() { |
337 | + actionsDrawer.close() |
338 | + } |
339 | + |
340 | + width: units.gu(5) |
341 | + height: width |
342 | + |
343 | + AbstractButton { |
344 | + id: singleActionButton |
345 | + |
346 | + anchors.fill: parent |
347 | + visible: playerActionBar.actions.length == 1 |
348 | + action: playerActionBar.actions.length == 1 ? playerActionBar.actions[0] : null |
349 | + |
350 | + Icon { |
351 | + anchors.centerIn: parent |
352 | + width: units.gu(2.5) |
353 | + height: width |
354 | + |
355 | + color: "white" |
356 | + name: singleActionButton.action ? singleActionButton.action.iconName : "" |
357 | + opacity: singleActionButton.action ? (singleActionButton.action.enabled ? 1.0 : 0.5) : 1.0 |
358 | + } |
359 | + } |
360 | + |
361 | + AbstractButton { |
362 | + onClicked: { |
363 | + if (actionsDrawer.visible) { |
364 | + actionsDrawer.close() |
365 | + } else { |
366 | + actionsDrawer.open() |
367 | + } |
368 | + } |
369 | + |
370 | + anchors.fill: parent |
371 | + visible: playerActionBar.actions.length > 1 |
372 | + |
373 | + Icon { |
374 | + anchors.centerIn: parent |
375 | + width: units.gu(2.5) |
376 | + height: width |
377 | + |
378 | + color: "white" |
379 | + name: "contextual-menu" |
380 | + } |
381 | + } |
382 | + |
383 | + PlayerActionsDrawer { |
384 | + id: actionsDrawer |
385 | + |
386 | + onDrawerOpened: playerActionBar.drawerOpened() |
387 | + onDrawerClosed: playerActionBar.drawerClosed() |
388 | + |
389 | + actions: playerActionBar.actions |
390 | + } |
391 | +} |
392 | |
393 | === added file 'modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionsDrawer.qml' |
394 | --- modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionsDrawer.qml 1970-01-01 00:00:00 +0000 |
395 | +++ modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionsDrawer.qml 2016-03-09 10:31:35 +0000 |
396 | @@ -0,0 +1,124 @@ |
397 | +/* |
398 | + * Copyright (C) 2016 Canonical, Ltd. |
399 | + * |
400 | + * This program is free software; you can redistribute it and/or modify |
401 | + * it under the terms of the GNU General Public License as published by |
402 | + * the Free Software Foundation; version 3. |
403 | + * |
404 | + * This program is distributed in the hope that it will be useful, |
405 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
406 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
407 | + * GNU General Public License for more details. |
408 | + * |
409 | + * You should have received a copy of the GNU General Public License |
410 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
411 | + */ |
412 | + |
413 | +import QtQuick 2.4 |
414 | +import Ubuntu.Components 1.3 |
415 | + |
416 | +Item { |
417 | + id: actionsDrawer |
418 | + |
419 | + property list<Action> actions |
420 | + property bool opened: false |
421 | + |
422 | + signal drawerOpened() |
423 | + signal drawerClosed() |
424 | + |
425 | + function open() { |
426 | + opened = true; |
427 | + visible = true; |
428 | + actionsDrawer.drawerOpened() |
429 | + } |
430 | + |
431 | + function close() { |
432 | + opened = false; |
433 | + actionsDrawer.drawerClosed() |
434 | + } |
435 | + |
436 | + anchors { |
437 | + top: parent.bottom |
438 | + right: parent.right |
439 | + } |
440 | + |
441 | + width: units.gu(20) |
442 | + height: actionsColumn.height |
443 | + visible: false |
444 | + |
445 | + clip: actionsColumn.y != 0 |
446 | + |
447 | + Column { |
448 | + id: actionsColumn |
449 | + |
450 | + onYChanged: { |
451 | + if (y == -height) { |
452 | + actionsDrawer.visible = false; |
453 | + } |
454 | + } |
455 | + |
456 | + anchors { |
457 | + left: parent.left |
458 | + right: parent.right |
459 | + } |
460 | + |
461 | + y: actionsDrawer.opened ? 0 : -height |
462 | + Behavior on y { UbuntuNumberAnimation {} } |
463 | + |
464 | + Repeater { |
465 | + model: actionsDrawer.actions.length > 0 ? actionsDrawer.actions : 0 |
466 | + |
467 | + delegate: AbstractButton { |
468 | + id: actionButton |
469 | + |
470 | + anchors { |
471 | + left: actionsColumn.left |
472 | + right: actionsColumn.right |
473 | + } |
474 | + |
475 | + height: units.gu(6) |
476 | + enabled: action.enabled |
477 | + |
478 | + action: modelData |
479 | + onClicked: actionsDrawer.close() |
480 | + |
481 | + Rectangle { |
482 | + anchors.fill: parent |
483 | + color: actionButton.pressed ? Qt.rgba(1.0, 1.0, 1.0, 0.3) : Qt.rgba(0.0, 0.0, 0.0, 0.6) |
484 | + } |
485 | + |
486 | + Label { |
487 | + id: label |
488 | + |
489 | + anchors { |
490 | + left: icon.right |
491 | + right: parent.right |
492 | + leftMargin: units.gu(2) |
493 | + rightMargin: units.gu(2) |
494 | + verticalCenter: parent.verticalCenter |
495 | + } |
496 | + |
497 | + text: model.text |
498 | + elide: Text.ElideRight |
499 | + color: action.enabled ? theme.palette.normal.foregroundText : Qt.darker(theme.palette.normal.foregroundText, 2.0) |
500 | + } |
501 | + |
502 | + Icon { |
503 | + id: icon |
504 | + |
505 | + anchors { |
506 | + left: parent.left |
507 | + leftMargin: units.gu(2) |
508 | + verticalCenter: parent.verticalCenter |
509 | + } |
510 | + |
511 | + width: height |
512 | + height: label.paintedHeight |
513 | + |
514 | + color: label.color |
515 | + name: model.iconName |
516 | + } |
517 | + } |
518 | + } |
519 | + } |
520 | +} |
521 | |
522 | === added file 'modules/Ubuntu/Components/Extras/MediaPlayer/PlayerPanel.qml' |
523 | --- modules/Ubuntu/Components/Extras/MediaPlayer/PlayerPanel.qml 1970-01-01 00:00:00 +0000 |
524 | +++ modules/Ubuntu/Components/Extras/MediaPlayer/PlayerPanel.qml 2016-03-09 10:31:35 +0000 |
525 | @@ -0,0 +1,111 @@ |
526 | +/* |
527 | + * Copyright (C) 2015 Canonical, Ltd. |
528 | + * |
529 | + * Authors: |
530 | + * Arthur Renato Mello <arthur.mello@canonical.com> |
531 | + * |
532 | + * This program is free software; you can redistribute it and/or modify |
533 | + * it under the terms of the GNU General Public License as published by |
534 | + * the Free Software Foundation; version 3. |
535 | + * |
536 | + * This program is distributed in the hope that it will be useful, |
537 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
538 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
539 | + * GNU General Public License for more details. |
540 | + * |
541 | + * You should have received a copy of the GNU General Public License |
542 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
543 | + */ |
544 | + |
545 | +import QtQuick 2.4 |
546 | +import Ubuntu.Components 1.3 |
547 | + |
548 | +Panel { |
549 | + id: playerPanel |
550 | + |
551 | + property Item panelContents |
552 | + |
553 | + readonly property alias leadingActionBar: leading |
554 | + readonly property alias trailingActionBar: trailing |
555 | + |
556 | + signal panelDrawerOpened() |
557 | + signal panelDrawerClosed() |
558 | + |
559 | + Component.onCompleted: holder.updateContents() |
560 | + onPanelContentsChanged: holder.updateContents() |
561 | + |
562 | + onOpenedChanged: { |
563 | + if (!opened) { |
564 | + leading.closeDrawer() |
565 | + trailing.closeDrawer() |
566 | + } |
567 | + } |
568 | + |
569 | + Rectangle { |
570 | + anchors.fill: parent |
571 | + color: "#1B1B1B" |
572 | + opacity: 0.85 |
573 | + |
574 | + PlayerActionBar { |
575 | + id: leading |
576 | + |
577 | + onDrawerOpened: playerPanel.panelDrawerOpened() |
578 | + onDrawerClosed: playerPanel.panelDrawerClosed() |
579 | + |
580 | + anchors { |
581 | + top: parent.top |
582 | + bottom: parent.bottom |
583 | + left: parent.left |
584 | + leftMargin: units.gu(1) |
585 | + } |
586 | + |
587 | + visible: actions.length > 0 |
588 | + } |
589 | + |
590 | + Item { |
591 | + id: holder |
592 | + |
593 | + property Item previousContents: null |
594 | + property Item previousContentsParent: null |
595 | + |
596 | + anchors { |
597 | + top: parent.top |
598 | + bottom: parent.bottom |
599 | + left: leading.right |
600 | + right: trailing.left |
601 | + margins: units.gu(0.5) |
602 | + } |
603 | + |
604 | + function updateContents() { |
605 | + if (holder.previousContents) { |
606 | + holder.previousContents.parent = holder.previousContentsParent; |
607 | + } |
608 | + |
609 | + if (playerPanel.panelContents) { |
610 | + holder.previousContents = playerPanel.panelContents; |
611 | + holder.previousContentsParent = playerPanel.panelContents.parent; |
612 | + playerPanel.panelContents.parent = holder; |
613 | + } else { |
614 | + holder.previousContents = null; |
615 | + holder.previousContentsParent = null; |
616 | + } |
617 | + } |
618 | + } |
619 | + |
620 | + PlayerActionBar { |
621 | + id: trailing |
622 | + |
623 | + onDrawerOpened: playerPanel.panelDrawerOpened() |
624 | + onDrawerClosed: playerPanel.panelDrawerClosed() |
625 | + |
626 | + anchors { |
627 | + top: parent.top |
628 | + bottom: parent.bottom |
629 | + right: parent.right |
630 | + rightMargin: units.gu(1) |
631 | + } |
632 | + |
633 | + visible: actions.length > 0 |
634 | + } |
635 | + } |
636 | +} |
637 | |
638 | === added file 'modules/Ubuntu/Components/Extras/MediaPlayer/TimeLine.qml' |
639 | --- modules/Ubuntu/Components/Extras/MediaPlayer/TimeLine.qml 1970-01-01 00:00:00 +0000 |
640 | +++ modules/Ubuntu/Components/Extras/MediaPlayer/TimeLine.qml 2016-03-09 10:31:35 +0000 |
641 | @@ -0,0 +1,98 @@ |
642 | +/* |
643 | + * Copyright (C) 2013-2015 Canonical, Ltd. |
644 | + * |
645 | + * Authors: |
646 | + * Renato Araujo Oliveira Filho <renato@canonical.com> |
647 | + * Arthur Renato Mello <arthur.mello@canonical.com> |
648 | + * |
649 | + * This program is free software; you can redistribute it and/or modify |
650 | + * it under the terms of the GNU General Public License as published by |
651 | + * the Free Software Foundation; version 3. |
652 | + * |
653 | + * This program is distributed in the hope that it will be useful, |
654 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
655 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
656 | + * GNU General Public License for more details. |
657 | + * |
658 | + * You should have received a copy of the GNU General Public License |
659 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
660 | + */ |
661 | + |
662 | +import QtQuick 2.4 |
663 | +import Ubuntu.Components 1.3 |
664 | + |
665 | +Item { |
666 | + id: _sliderItem |
667 | + |
668 | + readonly property alias liveValue: _slider.value |
669 | + readonly property alias pressed: _slider.pressed |
670 | + |
671 | + property alias minimumValue: _slider.minimumValue |
672 | + property alias maximumValue: _slider.maximumValue |
673 | + |
674 | + property real videoPosition: -1 |
675 | + property string currentTime |
676 | + property string remainingTime |
677 | + property string durationTime |
678 | + |
679 | + onVideoPositionChanged: { |
680 | + if (!_slider.pressed) { |
681 | + _slider.value = _sliderItem.videoPosition |
682 | + } |
683 | + } |
684 | + |
685 | + Slider { |
686 | + id: _slider |
687 | + objectName: "TimeLine.Slider" |
688 | + |
689 | + function formatValue(v) { |
690 | + var hour = 0 |
691 | + var min = 0 |
692 | + var secs = 0 |
693 | + var timeValue = Math.floor(v) |
694 | + |
695 | + secs = timeValue % 60 |
696 | + timeValue = Math.floor(timeValue / 60) |
697 | + min = timeValue % 60 |
698 | + hour = Math.floor(timeValue / 60) |
699 | + |
700 | + if (secs < 10) secs = "0%1".arg(secs) |
701 | + if (min < 10) min = "0%1".arg(min) |
702 | + if (hour < 10) hour = "0%1".arg(hour) |
703 | + |
704 | + // TRANSLATORS: this refers to a duration/remaining time of the video, of which you can change the order. |
705 | + // %1 refers to hours, %2 refers to minutes and %3 refers to seconds. |
706 | + return i18n.tr("%1:%2:%3").arg(hour).arg(min).arg(secs) |
707 | + } |
708 | + |
709 | + anchors { |
710 | + left: parent.left |
711 | + right: parent.right |
712 | + } |
713 | + |
714 | + live: true |
715 | + |
716 | + onValueChanged: { |
717 | + if (value > 0) { |
718 | + _sliderItem.currentTime = formatValue(value) |
719 | + if (_slider.maximumValue > 0) { |
720 | + _sliderItem.remainingTime = formatValue(_slider.maximumValue - value) |
721 | + } else { |
722 | + // TRANSLATORS: this refers to an unknown duration. |
723 | + _sliderItem.remainingTime = i18n.tr("unknown") |
724 | + } |
725 | + } else { |
726 | + _sliderItem.currentTime = i18n.tr("0:00:00") |
727 | + } |
728 | + } |
729 | + |
730 | + onMaximumValueChanged: { |
731 | + if (_slider.maximumValue > 0) { |
732 | + _sliderItem.durationTime = _slider.formatValue(_slider.maximumValue) |
733 | + } else { |
734 | + // TRANSLATORS: this refers to an unknown duration. |
735 | + _sliderItem.remainingTime = i18n.tr("unknown") |
736 | + } |
737 | + } |
738 | + } |
739 | +} |
740 | |
741 | === added file 'modules/Ubuntu/Components/Extras/MediaPlayer/VideoPlayerToolbar.qml' |
742 | --- modules/Ubuntu/Components/Extras/MediaPlayer/VideoPlayerToolbar.qml 1970-01-01 00:00:00 +0000 |
743 | +++ modules/Ubuntu/Components/Extras/MediaPlayer/VideoPlayerToolbar.qml 2016-03-09 10:31:35 +0000 |
744 | @@ -0,0 +1,128 @@ |
745 | +/* |
746 | + * Copyright (C) 2015 Canonical, Ltd. |
747 | + * |
748 | + * Authors: |
749 | + * Arthur Renato Mello <arthur.mello@canonical.com> |
750 | + * |
751 | + * This program is free software; you can redistribute it and/or modify |
752 | + * it under the terms of the GNU General Public License as published by |
753 | + * the Free Software Foundation; version 3. |
754 | + * |
755 | + * This program is distributed in the hope that it will be useful, |
756 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
757 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
758 | + * GNU General Public License for more details. |
759 | + * |
760 | + * You should have received a copy of the GNU General Public License |
761 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
762 | + */ |
763 | + |
764 | +import QtQuick 2.4 |
765 | +import QtMultimedia 5.0 |
766 | +import Ubuntu.Components 1.3 |
767 | + |
768 | +PlayerPanel { |
769 | + id: _videoPlayerToolbar |
770 | + |
771 | + property var videoPlayerStatus |
772 | + property var videoPlayerState |
773 | + property real videoDuration: 1000 |
774 | + property real videoPosition: 0 |
775 | + |
776 | + readonly property bool isPlaying: _videoPlayerToolbar.videoPlayerState == "playing" |
777 | + readonly property bool isPaused: _videoPlayerToolbar.videoPlayerState == "paused" |
778 | + readonly property bool isEndOfMedia: _videoPlayerToolbar.videoPlayerStatus != MediaPlayer.EndOfMedia |
779 | + |
780 | + signal playRequested() |
781 | + signal pauseRequested() |
782 | + signal seekRequested(int time) |
783 | + signal fullscreenActionRequested() |
784 | + |
785 | + leadingActionBar.actions: [ |
786 | + Action { |
787 | + iconName: _videoPlayerToolbar.isPlaying ? "media-playback-pause" : "media-playback-start" |
788 | + text: _videoPlayerToolbar.isPlaying ? i18n.tr("Pause") : i18n.tr("Start") |
789 | + onTriggered: { |
790 | + if (_videoPlayerToolbar.isPlaying) { |
791 | + _videoPlayerToolbar.pauseRequested() |
792 | + } else { |
793 | + _videoPlayerToolbar.playRequested() |
794 | + } |
795 | + } |
796 | + } |
797 | + ] |
798 | + |
799 | + panelContents: Item { |
800 | + anchors.fill: parent |
801 | + |
802 | + TimeLine { |
803 | + id: _timeline |
804 | + |
805 | + property bool seeking: false |
806 | + |
807 | + anchors { |
808 | + top: parent.top |
809 | + left: parent.left |
810 | + right: parent.right |
811 | + } |
812 | + |
813 | + height: units.gu(4) |
814 | + |
815 | + minimumValue: 0 |
816 | + maximumValue: _videoPlayerToolbar.videoDuration |
817 | + videoPosition: _videoPlayerToolbar.videoPosition |
818 | + |
819 | + // Pause the video during the seek |
820 | + onPressedChanged: { |
821 | + if (!pressed && seeking) { |
822 | + if (_videoPlayerToolbar.isEndOfMedia && |
823 | + _videoPlayerToolbar.isPaused) { |
824 | + |
825 | + _videoPlayerToolbar.playRequested() |
826 | + } |
827 | + seeking = false |
828 | + } |
829 | + } |
830 | + |
831 | + // Live value is the real slider value. Ex: User dragging the slider |
832 | + onLiveValueChanged: { |
833 | + var changed = Math.abs(liveValue - videoPosition) |
834 | + if (changed > 1) { |
835 | + if (!seeking) { |
836 | + _videoPlayerToolbar.pauseRequested() |
837 | + seeking = true |
838 | + } |
839 | + _videoPlayerToolbar.seekRequested(liveValue * 1000) |
840 | + } |
841 | + } |
842 | + } |
843 | + |
844 | + Label { |
845 | + anchors { |
846 | + top: _timeline.bottom |
847 | + left: parent.left |
848 | + } |
849 | + |
850 | + text: _timeline.currentTime |
851 | + fontSize: "x-small" |
852 | + } |
853 | + |
854 | + Label { |
855 | + anchors { |
856 | + top: _timeline.bottom |
857 | + right: parent.right |
858 | + } |
859 | + |
860 | + text: _timeline.durationTime |
861 | + fontSize: "x-small" |
862 | + } |
863 | + } |
864 | + |
865 | + trailingActionBar.actions: [ |
866 | + Action { |
867 | + iconName: "view-fullscreen" |
868 | + text: i18n.tr("Toggle Fullscreen") |
869 | + onTriggered: _videoPlayerToolbar.fullscreenActionRequested() |
870 | + } |
871 | + ] |
872 | +} |
873 | |
874 | === added file 'modules/Ubuntu/Components/Extras/VideoPlayer.qml' |
875 | --- modules/Ubuntu/Components/Extras/VideoPlayer.qml 1970-01-01 00:00:00 +0000 |
876 | +++ modules/Ubuntu/Components/Extras/VideoPlayer.qml 2016-03-09 10:31:35 +0000 |
877 | @@ -0,0 +1,434 @@ |
878 | +/* |
879 | + * Copyright (C) 2013-2015 Canonical, Ltd. |
880 | + * |
881 | + * Authors: |
882 | + * Ugo Riboni <ugo.riboni@canonical.com> |
883 | + * Michał Sawicz <michal.sawicz@canonical.com> |
884 | + * Renato Araujo Oliveira Filho <renato@canonical.com> |
885 | + * Arthur Renato Mello <arthur.mello@canonical.com> |
886 | + * |
887 | + * This program is free software; you can redistribute it and/or modify |
888 | + * it under the terms of the GNU General Public License as published by |
889 | + * the Free Software Foundation; version 3. |
890 | + * |
891 | + * This program is distributed in the hope that it will be useful, |
892 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
893 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
894 | + * GNU General Public License for more details. |
895 | + * |
896 | + * You should have received a copy of the GNU General Public License |
897 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
898 | + */ |
899 | + |
900 | +import QtQuick 2.4 |
901 | +import QtMultimedia 5.0 |
902 | +import Ubuntu.Components 1.3 |
903 | +import Ubuntu.Components.Extras 0.2 |
904 | +import Ubuntu.Components.Popups 1.0 as Popups |
905 | +import Ubuntu.Content 0.1 |
906 | +import Ubuntu.Thumbnailer 0.1 |
907 | +import "MediaPlayer" |
908 | + |
909 | +AbstractPlayer { |
910 | + id: player |
911 | + objectName: "player" |
912 | + |
913 | + signal closeRequested() |
914 | + |
915 | + readonly property int seekStep: 1000 |
916 | + readonly property bool fullscreen: true |
917 | + |
918 | + property list<Action> titlebarActions |
919 | + property var errorDialog: null |
920 | + property string title: "" |
921 | + |
922 | + function playPause() { |
923 | + if (["paused", "playing"].indexOf(player.state) != -1) { |
924 | + player.togglePause(); |
925 | + } else { |
926 | + player.play(); |
927 | + } |
928 | + } |
929 | + |
930 | + function setTitlebarOpened(opened) { |
931 | + if (!_titlebarLoader.item) { |
932 | + return |
933 | + } |
934 | + |
935 | + if (opened && !_titlebarLoader.item.opened) { |
936 | + _titlebarLoader.item.open() |
937 | + } else if (!opened && _titlebarLoader.item.opened) { |
938 | + _titlebarLoader.item.close() |
939 | + } |
940 | + } |
941 | + |
942 | + function setToolbarOpened(opened) { |
943 | + if (!_toolbarLoader.item) { |
944 | + return |
945 | + } |
946 | + |
947 | + if (opened && !_toolbarLoader.item.opened) { |
948 | + _toolbarLoader.item.open() |
949 | + } else if (!opened && _toolbarLoader.item.opened) { |
950 | + _toolbarLoader.item.close() |
951 | + } |
952 | + } |
953 | + |
954 | + Component.onCompleted: { |
955 | + if ((state !== "playing") && (source != "")) { |
956 | + play() |
957 | + } |
958 | + } |
959 | + |
960 | + Connections { |
961 | + target: player.mediaPlayer |
962 | + onStatusChanged: { |
963 | + if (status == MediaPlayer.EndOfMedia) { |
964 | + player.fullscreen = false |
965 | + player.hideVideoOutput() |
966 | + player.pause() |
967 | + player.seek(0) |
968 | + player.stop() |
969 | + player.setTitlebarOpened(true) |
970 | + player.setToolbarOpened(true) |
971 | + } else { |
972 | + player.showVideoOutput() |
973 | + } |
974 | + } |
975 | + } |
976 | + |
977 | + MouseArea { |
978 | + id: _mouseArea |
979 | + objectName: "videoMouseArea" |
980 | + |
981 | + anchors.fill: parent |
982 | + |
983 | + onClicked: { |
984 | + if (!_titlebarLoader.item || !_toolbarLoader.item) { |
985 | + return |
986 | + } |
987 | + |
988 | + player.setTitlebarOpened(!_titlebarLoader.item.opened) |
989 | + player.setToolbarOpened(!_toolbarLoader.item.opened) |
990 | + } |
991 | + } |
992 | + |
993 | + Timer { |
994 | + id: _dismissBars |
995 | + |
996 | + running: false |
997 | + interval: 1000 |
998 | + repeat: false |
999 | + onTriggered: { |
1000 | + player.setTitlebarOpened(false) |
1001 | + player.setToolbarOpened(false) |
1002 | + } |
1003 | + } |
1004 | + |
1005 | + Image { |
1006 | + id: _videoThumbnail |
1007 | + |
1008 | + fillMode: Image.PreserveAspectCrop |
1009 | + smooth: true |
1010 | + source: "image://thumbnailer/" + player.source.toString().replace("file://", "") |
1011 | + |
1012 | + visible: player.mediaPlayer.status == MediaPlayer.EndOfMedia |
1013 | + opacity: visible ? 1.0 : 0.0 |
1014 | + Behavior on opacity { UbuntuNumberAnimation {} } |
1015 | + |
1016 | + asynchronous: true |
1017 | + height: parent.height |
1018 | + width: parent.width |
1019 | + cache: false |
1020 | + |
1021 | + sourceSize.width: parent.width |
1022 | + sourceSize.height: parent.height |
1023 | + |
1024 | + Icon { |
1025 | + width: units.gu(5) |
1026 | + height: units.gu(5) |
1027 | + anchors.centerIn: parent |
1028 | + name: "media-playback-start" |
1029 | + color: "white" |
1030 | + opacity: 0.8 |
1031 | + } |
1032 | + |
1033 | + MouseArea { |
1034 | + anchors.fill: parent |
1035 | + onClicked: { |
1036 | + player.fullscreen = true |
1037 | + player.play() |
1038 | + player.setTitlebarOpened(false) |
1039 | + player.setToolbarOpened(false) |
1040 | + } |
1041 | + } |
1042 | + } |
1043 | + |
1044 | + Loader { |
1045 | + id: _titlebarLoader |
1046 | + |
1047 | + anchors { |
1048 | + top: parent.top |
1049 | + left: parent.left |
1050 | + right: parent.right |
1051 | + } |
1052 | + |
1053 | + height: units.gu(6) |
1054 | + |
1055 | + active: player.mediaPlayer && player.mediaPlayer.duration > 0 |
1056 | + sourceComponent: _titlebarComponent |
1057 | + } |
1058 | + |
1059 | + Component { |
1060 | + id: _titlebarComponent |
1061 | + |
1062 | + PlayerPanel { |
1063 | + id: _titlebar |
1064 | + |
1065 | + onPanelDrawerOpened: _dismissBars.stop() |
1066 | + onPanelDrawerClosed: { |
1067 | + if (_titlebar.opened) { |
1068 | + _dismissBars.restart() |
1069 | + } |
1070 | + } |
1071 | + |
1072 | + Component.onCompleted: { |
1073 | + var newActions = []; |
1074 | + for (var i = 0; i < trailingActionBar.actions.length; i++) newActions.push(trailingActionBar.actions[i]); |
1075 | + for (var i = 0; i < player.titlebarActions.length; i++) newActions.push(player.titlebarActions[i]); |
1076 | + trailingActionBar.actions = newActions; |
1077 | + } |
1078 | + |
1079 | + Action { |
1080 | + text: i18n.tr("Edit") |
1081 | + iconName: "edit" |
1082 | + } |
1083 | + |
1084 | + align: Qt.AlignTop |
1085 | + |
1086 | + leadingActionBar.actions: [ |
1087 | + Action { |
1088 | + iconName: "back" |
1089 | + text: i18n.tr("Back") |
1090 | + onTriggered: { |
1091 | + player.stop() |
1092 | + player.closeRequested() |
1093 | + } |
1094 | + } |
1095 | + ] |
1096 | + |
1097 | + panelContents: Label { |
1098 | + anchors { |
1099 | + left: parent.left |
1100 | + right: parent.right |
1101 | + verticalCenter: parent.verticalCenter |
1102 | + } |
1103 | + |
1104 | + text: player.title != "" ? player.title : player.source |
1105 | + fontSize: "medium" |
1106 | + elide: Text.ElideRight |
1107 | + } |
1108 | + |
1109 | + trailingActionBar.actions: [ |
1110 | + Action { |
1111 | + iconName: "share" |
1112 | + text: i18n.tr("Share") |
1113 | + onTriggered: { |
1114 | + player.pause() |
1115 | + var dialog = PopupUtils.open(_sharePopoverComponent) |
1116 | + dialog.parent = player |
1117 | + } |
1118 | + }, |
1119 | + Action { |
1120 | + iconName: "save" |
1121 | + text: i18n.tr("Save") |
1122 | + onTriggered: { |
1123 | + player.pause() |
1124 | + var dialog = PopupUtils.open(_savePopoverComponent) |
1125 | + dialog.parent = player |
1126 | + } |
1127 | + } |
1128 | + ] |
1129 | + } |
1130 | + } |
1131 | + |
1132 | + Loader { |
1133 | + id: _toolbarLoader |
1134 | + |
1135 | + anchors { |
1136 | + left: parent.left |
1137 | + right: parent.right |
1138 | + bottom: parent.bottom |
1139 | + } |
1140 | + |
1141 | + height: units.gu(7) |
1142 | + |
1143 | + active: player.mediaPlayer && player.mediaPlayer.duration > 0 |
1144 | + sourceComponent: _toolbarComponent |
1145 | + } |
1146 | + |
1147 | + Component { |
1148 | + id: _toolbarComponent |
1149 | + |
1150 | + VideoPlayerToolbar { |
1151 | + id: _controls |
1152 | + objectName: "toolbar" |
1153 | + |
1154 | + onPanelDrawerOpened: _dismissBars.stop() |
1155 | + onPanelDrawerClosed: { |
1156 | + if (_controls.opened) { |
1157 | + _dismissBars.restart() |
1158 | + } |
1159 | + } |
1160 | + |
1161 | + onPlayRequested: { |
1162 | + player.play() |
1163 | + _dismissBars.restart() |
1164 | + } |
1165 | + onPauseRequested: player.pause() |
1166 | + onSeekRequested: player.mediaPlayer.seek(time) |
1167 | + onFullscreenActionRequested: player.fullscreen = !player.fullscreen |
1168 | + |
1169 | + anchors.fill: parent |
1170 | + |
1171 | + videoPlayerStatus: player.mediaPlayer.status |
1172 | + videoPlayerState: player.state |
1173 | + |
1174 | + videoDuration: player.mediaPlayer.duration / 1000 |
1175 | + videoPosition: player.mediaPlayer.position / 1000 |
1176 | + } |
1177 | + } |
1178 | + |
1179 | + Keys.onPressed: { |
1180 | + switch(event.key) { |
1181 | + case Qt.Key_Space: |
1182 | + player.playPause() |
1183 | + break; |
1184 | + case Qt.Key_Right: |
1185 | + case Qt.Key_Left: |
1186 | + { |
1187 | + var currentPos = (video ? video.position : 0) |
1188 | + var nextPos = currentPos |
1189 | + if (event.key == Qt.Key_Right) { |
1190 | + var maxPos = (video ? video.duration : 0) |
1191 | + nextPos += player.seekStep |
1192 | + if (nextPos > maxPos) { |
1193 | + nextPos = -1; |
1194 | + } |
1195 | + } else { |
1196 | + nextPos -= player.seekStep |
1197 | + if (nextPos < 0) { |
1198 | + nextPos = -1 |
1199 | + } |
1200 | + } |
1201 | + |
1202 | + if (nextPos != -1) { |
1203 | + player.mediaPlayer.seek(nextPos) |
1204 | + } |
1205 | + break; |
1206 | + } |
1207 | + case Qt.Key_F11: |
1208 | + player.fullscreen = !player.fullscreen |
1209 | + break; |
1210 | + case Qt.Key_Escape: |
1211 | + player.fullscreen = false |
1212 | + break; |
1213 | + default: |
1214 | + break; |
1215 | + } |
1216 | + } |
1217 | + |
1218 | + Component { |
1219 | + id: _sharePopoverComponent |
1220 | + |
1221 | + ContentPopover { |
1222 | + ContentItem { id: _contentItem } |
1223 | + |
1224 | + Component.onCompleted: { |
1225 | + _contentItem.url = player.source |
1226 | + transferItems = [contentItem] |
1227 | + } |
1228 | + |
1229 | + onVisibleChanged: { |
1230 | + player.setTitlebarOpened(true) |
1231 | + player.setToolbarOpened(true) |
1232 | + } |
1233 | + |
1234 | + transferContentType: ContentType.Videos |
1235 | + transferContentHandler: ContentHandler.Share |
1236 | + } |
1237 | + } |
1238 | + |
1239 | + Component { |
1240 | + id: _savePopoverComponent |
1241 | + |
1242 | + ContentPopover { |
1243 | + ContentItem { id: _contentItem } |
1244 | + |
1245 | + Component.onCompleted: { |
1246 | + _contentItem.url = player.source |
1247 | + transferItems = [_contentItem] |
1248 | + } |
1249 | + |
1250 | + onVisibleChanged: { |
1251 | + player.setTitlebarOpened(true) |
1252 | + player.setToolbarOpened(true) |
1253 | + } |
1254 | + |
1255 | + transferContentType: ContentType.Videos |
1256 | + transferContentHandler: ContentHandler.Destination |
1257 | + } |
1258 | + } |
1259 | + |
1260 | + Component { |
1261 | + id: _dialogPlayerError |
1262 | + |
1263 | + Popups.Dialog { |
1264 | + id: dialogue |
1265 | + objectName: "playError" |
1266 | + |
1267 | + property string errorString: "" |
1268 | + |
1269 | + title: i18n.tr("Error playing video") |
1270 | + text: errorString |
1271 | + |
1272 | + Button { |
1273 | + text: i18n.tr("Close") |
1274 | + onClicked: { |
1275 | + PopupUtils.close(dialogue) |
1276 | + if (player.state != MediaPlayer.PlayingState) { |
1277 | + console.debug("Warning: Quitting app due to fatal playback error.") |
1278 | + Qt.quit() |
1279 | + } |
1280 | + } |
1281 | + } |
1282 | + |
1283 | + Component.onDestruction: player.errorDialog = null |
1284 | + } |
1285 | + } |
1286 | + |
1287 | + onError: { |
1288 | + if (player.errorDialog !== null) { |
1289 | + return |
1290 | + } |
1291 | + |
1292 | + player.errorDialog = PopupUtils.open(_dialogPlayerError, null) |
1293 | + switch(errorCode) { |
1294 | + case 1: |
1295 | + player.errorDialog.errorString = i18n.tr("Fail to open the source video.") |
1296 | + break; |
1297 | + case 2: |
1298 | + player.errorDialog.errorString = i18n.tr("Video format not supported.") |
1299 | + break; |
1300 | + case 3: |
1301 | + player.errorDialog.errorString = i18n.tr("A network error occurred.") |
1302 | + break; |
1303 | + case 4: |
1304 | + player.errorDialog.errorString = i18n.tr("There are not the appropriate permissions to play a media resource.") |
1305 | + break; |
1306 | + case 5: |
1307 | + player.errorDialog.errorString = i18n.tr("Fail to connect with playback backend.") |
1308 | + break; |
1309 | + } |
1310 | + } |
1311 | +} |
1312 | |
1313 | === modified file 'modules/Ubuntu/Components/Extras/qmldir' |
1314 | --- modules/Ubuntu/Components/Extras/qmldir 2015-02-11 14:46:34 +0000 |
1315 | +++ modules/Ubuntu/Components/Extras/qmldir 2016-03-09 10:31:35 +0000 |
1316 | @@ -2,3 +2,4 @@ |
1317 | plugin ubuntu-ui-extras-plugin |
1318 | Example 0.2 Example.qml |
1319 | PhotoEditor 0.2 PhotoEditor.qml |
1320 | +VideoPlayer 0.2 VideoPlayer.qml |
PASSED: Continuous integration, rev:73 jenkins. qa.ubuntu. com/job/ ubuntu- ui-extras- ci/76/ jenkins. qa.ubuntu. com/job/ ubuntu- ui-extras- vivid-amd64- ci/37 jenkins. qa.ubuntu. com/job/ ubuntu- ui-extras- vivid-armhf- ci/37 jenkins. qa.ubuntu. com/job/ ubuntu- ui-extras- vivid-armhf- ci/37/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- ui-extras- ci/76/rebuild
http://