Changeset View
Changeset View
Standalone View
Standalone View
applet/contents/ui/main.qml
Show All 17 Lines | 1 | /* | |||
---|---|---|---|---|---|
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | 19 | */ | ||
20 | 20 | | |||
21 | import QtQuick 2.2 | 21 | import QtQuick 2.2 | ||
22 | import QtQuick.Layouts 1.0 | 22 | import QtQuick.Layouts 1.0 | ||
23 | 23 | | |||
24 | import org.kde.plasma.core 2.1 as PlasmaCore | 24 | import org.kde.plasma.core 2.1 as PlasmaCore | ||
25 | import org.kde.plasma.components 2.0 as PlasmaComponents | 25 | import org.kde.plasma.components 2.0 as PlasmaComponents | ||
26 | import org.kde.plasma.components 3.0 as PlasmaComponents3 | ||||
26 | import org.kde.plasma.extras 2.0 as PlasmaExtras | 27 | import org.kde.plasma.extras 2.0 as PlasmaExtras | ||
27 | import org.kde.plasma.plasmoid 2.0 | 28 | import org.kde.plasma.plasmoid 2.0 | ||
28 | 29 | | |||
29 | import org.kde.plasma.private.volume 0.1 | 30 | import org.kde.plasma.private.volume 0.1 | ||
30 | 31 | | |||
31 | import "../code/icon.js" as Icon | 32 | import "../code/icon.js" as Icon | ||
32 | 33 | | |||
33 | Item { | 34 | Item { | ||
Show All 24 Lines | 55 | Plasmoid.toolTipMainText: { | |||
58 | } | 59 | } | ||
59 | 60 | | |||
60 | if (sink.muted) { | 61 | if (sink.muted) { | ||
61 | return i18n("Audio Muted"); | 62 | return i18n("Audio Muted"); | ||
62 | } else { | 63 | } else { | ||
63 | return i18n("Volume at %1%", volumePercent(sink.volume)); | 64 | return i18n("Volume at %1%", volumePercent(sink.volume)); | ||
64 | } | 65 | } | ||
65 | } | 66 | } | ||
66 | Plasmoid.toolTipSubText: paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink) ? paSinkModel.preferredSink.description : "" | 67 | Plasmoid.toolTipSubText: { | ||
68 | if (paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink)) { | ||||
69 | var port = paSinkModel.preferredSink.ports[paSinkModel.preferredSink.activePortIndex]; | ||||
70 | if (port) { | ||||
71 | return port.description | ||||
72 | } | ||||
73 | return paSinkModel.preferredSink.name | ||||
74 | } | ||||
75 | return "" | ||||
76 | } | ||||
67 | 77 | | |||
68 | function isDummyOutput(output) { | 78 | function isDummyOutput(output) { | ||
69 | return output && output.name === dummyOutputName; | 79 | return output && output.name === dummyOutputName; | ||
70 | } | 80 | } | ||
71 | 81 | | |||
72 | function boundVolume(volume) { | 82 | function boundVolume(volume) { | ||
73 | return Math.max(PulseAudio.MinimalVolume, Math.min(volume, maxVolumeValue)); | 83 | return Math.max(PulseAudio.MinimalVolume, Math.min(volume, maxVolumeValue)); | ||
74 | } | 84 | } | ||
▲ Show 20 Lines • Show All 218 Lines • ▼ Show 20 Line(s) | |||||
293 | VolumeOSD { | 303 | VolumeOSD { | ||
294 | id: osd | 304 | id: osd | ||
295 | } | 305 | } | ||
296 | 306 | | |||
297 | VolumeFeedback { | 307 | VolumeFeedback { | ||
298 | id: feedback | 308 | id: feedback | ||
299 | } | 309 | } | ||
300 | 310 | | |||
311 | PlasmaCore.Svg { | ||||
312 | id: lineSvg | ||||
313 | imagePath: "widgets/line" | ||||
314 | } | ||||
315 | | ||||
301 | Plasmoid.fullRepresentation: ColumnLayout { | 316 | Plasmoid.fullRepresentation: ColumnLayout { | ||
302 | spacing: units.smallSpacing | 317 | spacing: units.smallSpacing | ||
ngraham: Revert this change | |||||
303 | Layout.preferredHeight: main.Layout.preferredHeight | 318 | Layout.preferredHeight: main.Layout.preferredHeight | ||
304 | Layout.preferredWidth: main.Layout.preferredWidth | 319 | Layout.preferredWidth: main.Layout.preferredWidth | ||
305 | 320 | | |||
306 | function beginMoveStream(type, stream) { | 321 | function beginMoveStream(type, stream) { | ||
307 | if (type == "sink") { | 322 | if (type == "sink") { | ||
308 | sourceView.visible = false; | 323 | sourceView.visible = false; | ||
309 | sourceViewHeader.visible = false; | | |||
310 | } else if (type == "source") { | 324 | } else if (type == "source") { | ||
311 | sinkView.visible = false; | 325 | sinkView.visible = false; | ||
312 | sinkViewHeader.visible = false; | | |||
313 | } | 326 | } | ||
314 | 327 | | |||
328 | devicesLine.visible = false; | ||||
315 | tabBar.currentTab = devicesTab; | 329 | tabBar.currentTab = devicesTab; | ||
316 | } | 330 | } | ||
317 | 331 | | |||
318 | function endMoveStream() { | 332 | function endMoveStream() { | ||
319 | tabBar.currentTab = streamsTab; | 333 | tabBar.currentTab = streamsTab; | ||
320 | 334 | | |||
321 | sourceView.visible = true; | 335 | sourceView.visible = true; | ||
322 | sourceViewHeader.visible = true; | 336 | devicesLine.visible = true; | ||
323 | sinkView.visible = true; | 337 | sinkView.visible = true; | ||
324 | sinkViewHeader.visible = true; | | |||
325 | } | 338 | } | ||
326 | 339 | | |||
327 | RowLayout { | 340 | RowLayout { | ||
328 | spacing: units.smallSpacing | 341 | spacing: units.smallSpacing | ||
329 | Layout.fillWidth: true | 342 | Layout.fillWidth: true | ||
330 | 343 | | |||
331 | PlasmaComponents.TabBar { | 344 | PlasmaComponents.TabBar { | ||
332 | id: tabBar | 345 | id: tabBar | ||
333 | Layout.fillWidth: true | 346 | Layout.fillWidth: true | ||
334 | activeFocusOnTab: true | 347 | activeFocusOnTab: true | ||
335 | 348 | | |||
336 | PlasmaComponents.TabButton { | 349 | PlasmaComponents.TabButton { | ||
337 | id: devicesTab | 350 | id: devicesTab | ||
338 | text: i18n("Devices") | 351 | text: i18n("Devices") | ||
339 | } | 352 | } | ||
340 | 353 | | |||
341 | PlasmaComponents.TabButton { | 354 | PlasmaComponents.TabButton { | ||
342 | id: streamsTab | 355 | id: streamsTab | ||
343 | text: i18n("Applications") | 356 | text: i18n("Applications") | ||
344 | } | 357 | } | ||
345 | } | 358 | } | ||
346 | | ||||
347 | PlasmaComponents.ToolButton { | | |||
348 | Layout.alignment: Qt.AlignBottom | | |||
349 | tooltip: plasmoid.action("configure").text | | |||
350 | iconName: "configure" | | |||
351 | Accessible.name: tooltip | | |||
352 | onClicked: { | | |||
353 | plasmoid.action("configure").trigger(); | | |||
354 | } | | |||
355 | } | | |||
356 | } | 359 | } | ||
357 | 360 | | |||
358 | PlasmaExtras.ScrollArea { | 361 | PlasmaExtras.ScrollArea { | ||
359 | id: scrollView; | 362 | id: scrollView; | ||
360 | 363 | | |||
361 | Layout.fillWidth: true | 364 | Layout.fillWidth: true | ||
362 | Layout.fillHeight: true | 365 | Layout.fillHeight: true | ||
363 | 366 | | |||
364 | horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff | 367 | horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff | ||
365 | flickableItem.boundsBehavior: Flickable.StopAtBounds; | 368 | flickableItem.boundsBehavior: Flickable.StopAtBounds; | ||
366 | 369 | | |||
367 | //our scroll isn't a list of delegates, all internal items are tab focussable, making this redundant | 370 | //our scroll isn't a list of delegates, all internal items are tab focussable, making this redundant | ||
368 | activeFocusOnTab: false | 371 | activeFocusOnTab: false | ||
369 | 372 | | |||
370 | Item { | 373 | Item { | ||
371 | width: streamsView.visible ? streamsView.width : devicesView.width | 374 | width: streamsView.visible ? streamsView.width : devicesView.width | ||
372 | height: streamsView.visible ? streamsView.height : devicesView.height | 375 | height: streamsView.visible ? streamsView.height : devicesView.height | ||
373 | 376 | | |||
374 | ColumnLayout { | 377 | ColumnLayout { | ||
375 | id: streamsView | 378 | id: streamsView | ||
379 | spacing: 0 | ||||
376 | visible: tabBar.currentTab == streamsTab | 380 | visible: tabBar.currentTab == streamsTab | ||
377 | readonly property bool simpleMode: (sinkInputView.count >= 1 && sourceOutputView.count == 0) || (sinkInputView.count == 0 && sourceOutputView.count >= 1) | 381 | readonly property bool simpleMode: (sinkInputView.count >= 1 && sourceOutputView.count == 0) || (sinkInputView.count == 0 && sourceOutputView.count >= 1) | ||
378 | property int maximumWidth: scrollView.viewport.width | 382 | property int maximumWidth: scrollView.viewport.width | ||
379 | width: maximumWidth | 383 | width: maximumWidth | ||
380 | Layout.maximumWidth: maximumWidth | 384 | Layout.maximumWidth: maximumWidth | ||
381 | 385 | | |||
382 | Header { | | |||
383 | Layout.fillWidth: true | | |||
384 | visible: sinkInputView.count > 0 && !streamsView.simpleMode | | |||
385 | text: i18n("Playback Streams") | | |||
386 | } | | |||
387 | ListView { | 386 | ListView { | ||
388 | id: sinkInputView | 387 | id: sinkInputView | ||
389 | 388 | | |||
390 | Layout.fillWidth: true | 389 | Layout.fillWidth: true | ||
391 | Layout.minimumHeight: contentHeight | 390 | Layout.minimumHeight: contentHeight | ||
392 | Layout.maximumHeight: contentHeight | 391 | Layout.maximumHeight: contentHeight | ||
393 | 392 | | |||
394 | model: PulseObjectFilterModel { | 393 | model: PulseObjectFilterModel { | ||
395 | filters: [ { role: "VirtualStream", value: false } ] | 394 | filters: [ { role: "VirtualStream", value: false } ] | ||
396 | sourceModel: SinkInputModel {} | 395 | sourceModel: SinkInputModel {} | ||
397 | } | 396 | } | ||
398 | boundsBehavior: Flickable.StopAtBounds; | 397 | boundsBehavior: Flickable.StopAtBounds; | ||
399 | delegate: StreamListItem { | 398 | delegate: StreamListItem { | ||
400 | type: "sink-input" | 399 | type: "sink-input" | ||
401 | draggable: sinkView.count > 1 | 400 | draggable: sinkView.count > 1 | ||
402 | onlyOne: streamsView.simpleMode | | |||
403 | } | 401 | } | ||
404 | } | 402 | } | ||
405 | 403 | | |||
406 | Header { | 404 | PlasmaCore.SvgItem { | ||
ngraham: I think there isn't a line here in the mockup, right? | |||||
407 | Layout.fillWidth: true | 405 | elementId: "horizontal-line" | ||
408 | visible: sourceOutputView.count > 0 && !streamsView.simpleMode | 406 | Layout.preferredWidth: scrollView.viewport.width - units.smallSpacing * 4 | ||
409 | text: i18n("Recording Streams") | 407 | Layout.preferredHeight: naturalSize.height | ||
408 | Layout.leftMargin: units.smallSpacing * 2 | ||||
409 | Layout.rightMargin: units.smallSpacing * 2 | ||||
410 | Layout.topMargin: units.smallSpacing | ||||
411 | svg: lineSvg | ||||
412 | visible: sinkInputView.model.count > 0 && sourceOutputView.model.count > 0 | ||||
This is the line that separates the playback and recording applications. gvgeo: This is the line that separates the playback and recording applications.
Just notice that… | |||||
410 | } | 413 | } | ||
414 | | ||||
411 | ListView { | 415 | ListView { | ||
412 | id: sourceOutputView | 416 | id: sourceOutputView | ||
413 | 417 | | |||
414 | Layout.fillWidth: true | 418 | Layout.fillWidth: true | ||
415 | Layout.minimumHeight: contentHeight | 419 | Layout.minimumHeight: contentHeight | ||
416 | Layout.maximumHeight: contentHeight | 420 | Layout.maximumHeight: contentHeight | ||
417 | 421 | | |||
418 | model: PulseObjectFilterModel { | 422 | model: PulseObjectFilterModel { | ||
419 | filters: [ { role: "VirtualStream", value: false } ] | 423 | filters: [ { role: "VirtualStream", value: false } ] | ||
420 | sourceModel: SourceOutputModel {} | 424 | sourceModel: SourceOutputModel {} | ||
421 | } | 425 | } | ||
422 | boundsBehavior: Flickable.StopAtBounds; | 426 | boundsBehavior: Flickable.StopAtBounds; | ||
423 | delegate: StreamListItem { | 427 | delegate: StreamListItem { | ||
424 | type: "source-input" | 428 | type: "source-input" | ||
425 | draggable: sourceView.count > 1 | 429 | draggable: sourceView.count > 1 | ||
426 | onlyOne: streamsView.simpleMode | | |||
427 | } | 430 | } | ||
428 | } | 431 | } | ||
429 | } | 432 | } | ||
430 | 433 | | |||
431 | ColumnLayout { | 434 | ColumnLayout { | ||
432 | id: devicesView | 435 | id: devicesView | ||
433 | visible: tabBar.currentTab == devicesTab | 436 | visible: tabBar.currentTab == devicesTab | ||
434 | readonly property bool simpleMode: sinkView.count == 1 && sourceView.count == 1 | 437 | readonly property bool simpleMode: sinkView.count == 1 && sourceView.count == 1 | ||
435 | property int maximumWidth: scrollView.viewport.width | 438 | property int maximumWidth: scrollView.viewport.width | ||
436 | width: maximumWidth | 439 | width: maximumWidth | ||
437 | Layout.maximumWidth: maximumWidth | 440 | Layout.maximumWidth: maximumWidth | ||
441 | spacing: 0 | ||||
438 | 442 | | |||
439 | Header { | | |||
440 | id: sinkViewHeader | | |||
441 | Layout.fillWidth: true | | |||
442 | visible: sinkView.count > 0 && !devicesView.simpleMode | | |||
443 | text: i18n("Playback Devices") | | |||
444 | } | | |||
445 | ListView { | 443 | ListView { | ||
446 | id: sinkView | 444 | id: sinkView | ||
447 | 445 | | |||
448 | Layout.fillWidth: true | 446 | Layout.fillWidth: true | ||
449 | Layout.minimumHeight: contentHeight | 447 | Layout.minimumHeight: contentHeight | ||
450 | Layout.maximumHeight: contentHeight | 448 | Layout.maximumHeight: contentHeight | ||
449 | spacing: 0 | ||||
451 | 450 | | |||
452 | model: PlasmaCore.SortFilterModel { | 451 | model: PlasmaCore.SortFilterModel { | ||
453 | sortRole: "SortByDefault" | 452 | sortRole: "SortByDefault" | ||
454 | sortOrder: Qt.DescendingOrder | 453 | sortOrder: Qt.DescendingOrder | ||
455 | sourceModel: paSinkModel | 454 | sourceModel: paSinkModel | ||
456 | 455 | | |||
457 | filterCallback: function (source_row, value) { | 456 | filterCallback: function (source_row, value) { | ||
458 | var idx = sourceModel.index(source_row, 0); | 457 | var idx = sourceModel.index(source_row, 0); | ||
459 | 458 | | |||
460 | if (sourceModel.data(idx, sourceModel.role("Name")) === dummyOutputName) { | 459 | if (sourceModel.data(idx, sourceModel.role("Name")) === dummyOutputName) { | ||
461 | return false; | 460 | return false; | ||
462 | } | 461 | } | ||
463 | 462 | | |||
464 | return true; | 463 | return true; | ||
465 | } | 464 | } | ||
466 | } | 465 | } | ||
467 | boundsBehavior: Flickable.StopAtBounds; | 466 | boundsBehavior: Flickable.StopAtBounds; | ||
468 | delegate: DeviceListItem { | 467 | delegate: DeviceListItem { | ||
469 | type: "sink" | 468 | type: "sink" | ||
470 | onlyOne: devicesView.simpleMode | | |||
471 | } | 469 | } | ||
472 | } | 470 | } | ||
473 | 471 | | |||
474 | Header { | 472 | PlasmaCore.SvgItem { | ||
ngraham: ditto | |||||
475 | id: sourceViewHeader | 473 | id: devicesLine | ||
476 | Layout.fillWidth: true | 474 | elementId: "horizontal-line" | ||
477 | visible: sourceView.count > 0 && !devicesView.simpleMode | 475 | Layout.preferredWidth: scrollView.viewport.width - units.smallSpacing * 4 | ||
478 | text: i18n("Recording Devices") | 476 | Layout.leftMargin: units.smallSpacing * 2 | ||
477 | Layout.rightMargin: Layout.leftMargin | ||||
478 | Layout.topMargin: units.smallSpacing | ||||
479 | svg: lineSvg | ||||
480 | visible: sinkView.model.count > 0 && sourceView.model.count > 0 && (sinkView.model.count > 1 || sourceView.model.count > 1) | ||||
479 | } | 481 | } | ||
482 | | ||||
480 | ListView { | 483 | ListView { | ||
481 | id: sourceView | 484 | id: sourceView | ||
482 | 485 | | |||
483 | Layout.fillWidth: true | 486 | Layout.fillWidth: true | ||
484 | Layout.minimumHeight: contentHeight | 487 | Layout.minimumHeight: contentHeight | ||
485 | Layout.maximumHeight: contentHeight | 488 | Layout.maximumHeight: contentHeight | ||
486 | 489 | | |||
487 | model: PulseObjectFilterModel { | 490 | model: PulseObjectFilterModel { | ||
488 | sortRole: "SortByDefault" | 491 | sortRole: "SortByDefault" | ||
489 | sortOrder: Qt.DescendingOrder | 492 | sortOrder: Qt.DescendingOrder | ||
490 | sourceModel: paSourceModel | 493 | sourceModel: paSourceModel | ||
491 | } | 494 | } | ||
492 | boundsBehavior: Flickable.StopAtBounds; | 495 | boundsBehavior: Flickable.StopAtBounds; | ||
493 | delegate: DeviceListItem { | 496 | delegate: DeviceListItem { | ||
494 | type: "source" | 497 | type: "source" | ||
495 | onlyOne: devicesView.simpleMode | | |||
496 | } | 498 | } | ||
497 | } | 499 | } | ||
498 | } | 500 | } | ||
499 | 501 | | |||
500 | PlasmaExtras.Heading { | 502 | PlasmaExtras.Heading { | ||
501 | level: 4 | 503 | level: 4 | ||
502 | opacity: 0.8 | 504 | opacity: 0.8 | ||
503 | width: parent.width | 505 | width: parent.width | ||
Show All 13 Lines | 514 | PlasmaExtras.Heading { | |||
517 | visible: devicesView.visible && !sinkView.count && !sourceView.count | 519 | visible: devicesView.visible && !sinkView.count && !sourceView.count | ||
518 | text: i18n("No output or input devices found") | 520 | text: i18n("No output or input devices found") | ||
519 | wrapMode: Text.WordWrap | 521 | wrapMode: Text.WordWrap | ||
520 | verticalAlignment: Text.AlignVCenter | 522 | verticalAlignment: Text.AlignVCenter | ||
521 | horizontalAlignment: Text.AlignHCenter | 523 | horizontalAlignment: Text.AlignHCenter | ||
522 | } | 524 | } | ||
523 | } | 525 | } | ||
524 | } | 526 | } | ||
527 | | ||||
528 | PlasmaCore.SvgItem { | ||||
you can give it negative side margins to get it to touch the sides of the pop-up, as in the mockup ngraham: you can give it negative side margins to get it to touch the sides of the pop-up, as in the… | |||||
529 | elementId: "horizontal-line" | ||||
530 | Layout.fillWidth: true | ||||
531 | Layout.topMargin: 0 - units.smallSpacing / 2 | ||||
532 | Layout.leftMargin: 0 - units.smallSpacing * 1.5 | ||||
533 | Layout.rightMargin: Layout.leftMargin | ||||
534 | svg: lineSvg | ||||
535 | } | ||||
536 | | ||||
537 | RowLayout { | ||||
538 | | ||||
539 | Item { | ||||
540 | Layout.fillWidth: true | ||||
541 | } | ||||
542 | | ||||
So this doesn't actually do anything yet? Obviously that needs to change before this can land. :) Also the checked state needs to be saved somewhere. ngraham: So this doesn't actually do anything yet? Obviously that needs to change before this can land. | |||||
543 | PlasmaComponents.ToolButton { | ||||
544 | tooltip: plasmoid.action("configure").text | ||||
545 | iconName: "configure" | ||||
ngraham: Don't use a separate Heading here; give the Checkbox a `text:` property | |||||
546 | Accessible.name: tooltip | ||||
547 | onClicked: plasmoid.action("configure").trigger() | ||||
548 | } | ||||
549 | } | ||||
525 | } | 550 | } | ||
526 | 551 | | |||
527 | Component.onCompleted: { | 552 | Component.onCompleted: { | ||
528 | MicrophoneIndicator.init(); | 553 | MicrophoneIndicator.init(); | ||
529 | } | 554 | } | ||
530 | } | 555 | } |
Revert this change