volume tabs
This commit is contained in:
@@ -56,7 +56,7 @@ Scope {
|
|||||||
height: bar.screen.height - backgroundRect.implicitHeight
|
height: bar.screen.height - backgroundRect.implicitHeight
|
||||||
intersection: Intersection.Xor
|
intersection: Intersection.Xor
|
||||||
|
|
||||||
regions: popoutRegions.instances
|
regions: panels.popouts.hasCurrent ? None : popoutRegions.instances
|
||||||
}
|
}
|
||||||
|
|
||||||
Variants {
|
Variants {
|
||||||
|
|||||||
@@ -74,6 +74,13 @@ Singleton {
|
|||||||
Pipewire.preferredDefaultAudioSource = newSource;
|
Pipewire.preferredDefaultAudioSource = newSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setAppAudioVolume(appStream: PwNode, newVolume: real): void {
|
||||||
|
if ( appStream?.ready && appStream?.audio ) {
|
||||||
|
appStream.audio.muted = false;
|
||||||
|
appStream.audio.volume = Math.max(0, Math.min(100, newVolume));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onSinkChanged: {
|
onSinkChanged: {
|
||||||
if (!sink?.ready)
|
if (!sink?.ready)
|
||||||
return;
|
return;
|
||||||
@@ -100,4 +107,110 @@ Singleton {
|
|||||||
PwObjectTracker {
|
PwObjectTracker {
|
||||||
objects: [...root.sinks, ...root.sources]
|
objects: [...root.sinks, ...root.sources]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PwNodeLinkTracker {
|
||||||
|
id: sinkLinkTracker
|
||||||
|
node: root.sink
|
||||||
|
}
|
||||||
|
|
||||||
|
PwObjectTracker {
|
||||||
|
objects: root.appStreams
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var appStreams: {
|
||||||
|
var defaultSink = root.sink;
|
||||||
|
var defaultSinkId = defaultSink.id;
|
||||||
|
var connectedStreamIds = {};
|
||||||
|
var connectedStreams = [];
|
||||||
|
|
||||||
|
if ( !sinkLinkTracker.linkGroups ) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var linkGroupsCount = 0;
|
||||||
|
if (sinkLinkTracker.linkGroups.length !== undefined) {
|
||||||
|
linkGroupsCount = sinkLinkTracker.linkGroups.length;
|
||||||
|
} else if (sinkLinkTracker.linkGroups.count !== undefined) {
|
||||||
|
linkGroupsCount = sinkLinkTracker.linkGroups.count;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( linkGroupsCount === 0 ) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var intermediateNodeIds = {};
|
||||||
|
var nodesToCheck = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < linkGroupsCount; i++) {
|
||||||
|
var linkGroup;
|
||||||
|
if (sinkLinkTracker.linkGroups.get) {
|
||||||
|
linkGroup = sinkLinkTracker.linkGroups.get(i);
|
||||||
|
} else {
|
||||||
|
linkGroup = sinkLinkTracker.linkGroups[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!linkGroup || !linkGroup.source) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceNode = linkGroup.source;
|
||||||
|
|
||||||
|
if (sourceNode.isStream && sourceNode.audio) {
|
||||||
|
if (!connectedStreamIds[sourceNode.id]) {
|
||||||
|
connectedStreamIds[sourceNode.id] = true;
|
||||||
|
connectedStreams.push(sourceNode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
intermediateNodeIds[sourceNode.id] = true;
|
||||||
|
nodesToCheck.push(sourceNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodesToCheck.length > 0 || connectedStreams.length === 0) {
|
||||||
|
try {
|
||||||
|
var allNodes = [];
|
||||||
|
if (Pipewire.nodes) {
|
||||||
|
if (Pipewire.nodes.count !== undefined) {
|
||||||
|
var nodeCount = Pipewire.nodes.count;
|
||||||
|
for (var n = 0; n < nodeCount; n++) {
|
||||||
|
var node;
|
||||||
|
if (Pipewire.nodes.get) {
|
||||||
|
node = Pipewire.nodes.get(n);
|
||||||
|
} else {
|
||||||
|
node = Pipewire.nodes[n];
|
||||||
|
}
|
||||||
|
if (node)
|
||||||
|
allNodes.push(node);
|
||||||
|
}
|
||||||
|
} else if (Pipewire.nodes.values) {
|
||||||
|
allNodes = Pipewire.nodes.values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var j = 0; j < allNodes.length; j++) {
|
||||||
|
var node = allNodes[j];
|
||||||
|
if (!node || !node.isStream || !node.audio) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamId = node.id;
|
||||||
|
if (connectedStreamIds[streamId]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(intermediateNodeIds).length > 0) {
|
||||||
|
connectedStreamIds[streamId] = true;
|
||||||
|
connectedStreams.push(node);
|
||||||
|
} else if (connectedStreams.length === 0) {
|
||||||
|
connectedStreamIds[streamId] = true;
|
||||||
|
connectedStreams.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e)
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
return connectedStreams;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Networking
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property list<NetworkDevice> devices: Networking.devices
|
||||||
|
property NetworkDevice activeDevice: devices.find(d => d.connected)
|
||||||
|
}
|
||||||
+199
-100
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Services.Pipewire
|
import Quickshell.Services.Pipewire
|
||||||
|
import Quickshell.Widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
@@ -17,21 +18,211 @@ Item {
|
|||||||
|
|
||||||
required property var wrapper
|
required property var wrapper
|
||||||
|
|
||||||
ButtonGroup {
|
|
||||||
id: sinks
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
|
||||||
id: sources
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: layout
|
id: layout
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
implicitWidth: stack.currentItem ? stack.currentItem.childrenRect.height : 0
|
||||||
spacing: 12
|
spacing: 12
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: tabBar
|
||||||
|
spacing: 6
|
||||||
|
Layout.fillWidth: true
|
||||||
|
property int tabHeight: 36
|
||||||
|
|
||||||
|
CustomClippingRect {
|
||||||
|
radius: 6
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: tabBar.tabHeight
|
||||||
|
|
||||||
|
color: stack.currentIndex === 0 ? DynamicColors.tPalette.m3primaryContainer : "transparent"
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
stack.currentIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
text: qsTr("Volumes")
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomClippingRect {
|
||||||
|
radius: 6
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: tabBar.tabHeight
|
||||||
|
|
||||||
|
color: stack.currentIndex === 1 ? DynamicColors.tPalette.m3primaryContainer : "transparent"
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
stack.currentIndex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
text: qsTr("Devices")
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackLayout {
|
||||||
|
id: stack
|
||||||
|
Layout.fillWidth: true
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
|
VolumesTab {}
|
||||||
|
DevicesTab {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component VolumesTab: ColumnLayout {
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
text: qsTr("Output Volume (%1)")
|
||||||
|
.arg(Audio.muted
|
||||||
|
? qsTr("Muted")
|
||||||
|
: `${Math.round(Audio.volume * 100)}%`)
|
||||||
|
font.weight: 500
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomMouseArea {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 10
|
||||||
|
|
||||||
|
CustomSlider {
|
||||||
|
anchors.fill: parent
|
||||||
|
value: Audio.volume
|
||||||
|
onMoved: Audio.setVolume(value)
|
||||||
|
|
||||||
|
Behavior on value { Anim {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.topMargin: 10
|
||||||
|
text: qsTr("Input Volume (%1)")
|
||||||
|
.arg(Audio.sourceMuted
|
||||||
|
? qsTr("Muted")
|
||||||
|
: `${Math.round(Audio.sourceVolume * 100)}%`)
|
||||||
|
font.weight: 500
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomMouseArea {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 10
|
||||||
|
|
||||||
|
CustomSlider {
|
||||||
|
anchors.fill: parent
|
||||||
|
value: Audio.sourceVolume
|
||||||
|
onMoved: Audio.setSourceVolume(value)
|
||||||
|
|
||||||
|
Behavior on value { Anim {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Audio.appStreams
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: appBox
|
||||||
|
|
||||||
|
Layout.topMargin: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 42
|
||||||
|
visible: !isCaptureStream
|
||||||
|
required property PwNode modelData
|
||||||
|
|
||||||
|
PwObjectTracker {
|
||||||
|
objects: appBox.modelData ? [appBox.modelData] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property bool isCaptureStream: {
|
||||||
|
if (!modelData || !modelData.properties)
|
||||||
|
return false;
|
||||||
|
const props = modelData.properties;
|
||||||
|
// Exclude capture streams - check for stream.capture.sink property
|
||||||
|
if (props["stream.capture.sink"] !== undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const mediaClass = props["media.class"] || "";
|
||||||
|
// Exclude Stream/Input (capture) but allow Stream/Output (playback)
|
||||||
|
if (mediaClass.includes("Capture") || mediaClass === "Stream/Input" || mediaClass === "Stream/Input/Audio") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const mediaRole = props["media.role"] || "";
|
||||||
|
if (mediaRole === "Capture") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: layoutVolume
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
property string iconPath: Quickshell.iconPath(DesktopEntries.byId(appBox.modelData.name).icon)
|
||||||
|
source: iconPath !== "" ? iconPath : Quickshell.iconPath("application-x-executable")
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
implicitSize: 42
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: metrics
|
||||||
|
text: appBox.modelData.properties["media.name"]
|
||||||
|
elide: Text.ElideRight
|
||||||
|
elideWidth: root.width - 50
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
text: metrics.elidedText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomMouseArea {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||||
|
Layout.bottomMargin: 5
|
||||||
|
implicitHeight: 10
|
||||||
|
CustomSlider {
|
||||||
|
anchors.fill: parent
|
||||||
|
value: appBox.modelData.audio.volume
|
||||||
|
onMoved: {
|
||||||
|
Audio.setAppAudioVolume(appBox.modelData, value)
|
||||||
|
console.log(layoutVolume.implicitHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component DevicesTab: ColumnLayout {
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
ButtonGroup { id: sinks }
|
||||||
|
ButtonGroup { id: sources }
|
||||||
|
|
||||||
CustomText {
|
CustomText {
|
||||||
text: qsTr("Output device")
|
text: qsTr("Output device")
|
||||||
font.weight: 500
|
font.weight: 500
|
||||||
@@ -41,8 +232,6 @@ Item {
|
|||||||
model: Audio.sinks
|
model: Audio.sinks
|
||||||
|
|
||||||
CustomRadioButton {
|
CustomRadioButton {
|
||||||
id: control
|
|
||||||
|
|
||||||
required property PwNode modelData
|
required property PwNode modelData
|
||||||
|
|
||||||
ButtonGroup.group: sinks
|
ButtonGroup.group: sinks
|
||||||
@@ -70,95 +259,5 @@ Item {
|
|||||||
text: modelData.description
|
text: modelData.description
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomText {
|
|
||||||
Layout.topMargin: 10
|
|
||||||
Layout.bottomMargin: -7 / 2
|
|
||||||
text: qsTr("Output Volume (%1)").arg(Audio.muted ? qsTr("Muted") : `${Math.round(Audio.volume * 100)}%`)
|
|
||||||
font.weight: 500
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomMouseArea {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
implicitHeight: 10
|
|
||||||
|
|
||||||
CustomSlider {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
implicitHeight: parent.implicitHeight
|
|
||||||
|
|
||||||
value: Audio.volume
|
|
||||||
onMoved: Audio.setVolume(value)
|
|
||||||
|
|
||||||
Behavior on value {
|
|
||||||
Anim {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
Layout.topMargin: 10
|
|
||||||
Layout.bottomMargin: -7 / 2
|
|
||||||
text: qsTr("Input Volume (%1)").arg(Audio.sourceMuted ? qsTr("Muted") : `${Math.round(Audio.sourceVolume * 100)}%`)
|
|
||||||
font.weight: 500
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomMouseArea {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
implicitHeight: 10
|
|
||||||
|
|
||||||
CustomSlider {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
implicitHeight: parent.implicitHeight
|
|
||||||
|
|
||||||
value: Audio.sourceVolume
|
|
||||||
onMoved: Audio.setSourceVolume(value)
|
|
||||||
|
|
||||||
Behavior on value {
|
|
||||||
Anim {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
Layout.topMargin: 12
|
|
||||||
visible: true
|
|
||||||
|
|
||||||
implicitWidth: expandBtn.implicitWidth + 10 * 2
|
|
||||||
implicitHeight: expandBtn.implicitHeight + 5
|
|
||||||
|
|
||||||
radius: 4
|
|
||||||
color: DynamicColors.palette.m3primaryContainer
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
color: DynamicColors.palette.m3onPrimaryContainer
|
|
||||||
|
|
||||||
function onClicked(): void {
|
|
||||||
Quickshell.execDetached(["app2unit", "--", "hyprpwcenter"]);
|
|
||||||
root.wrapper.hasCurrent = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: expandBtn
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 7
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
Layout.leftMargin: 7
|
|
||||||
text: qsTr("Open settings")
|
|
||||||
color: DynamicColors.palette.m3onPrimaryContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
Layout.topMargin: 2
|
|
||||||
text: "chevron_right"
|
|
||||||
color: DynamicColors.palette.m3onPrimaryContainer
|
|
||||||
font.pointSize: 18
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ RowLayout {
|
|||||||
delegate: WrappedLoader {
|
delegate: WrappedLoader {
|
||||||
sourceComponent: TrayWidget {
|
sourceComponent: TrayWidget {
|
||||||
bar: root.bar
|
bar: root.bar
|
||||||
|
popouts: root.popouts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,35 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Networking
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var wrapper
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Network.devices
|
||||||
|
|
||||||
|
CustomRadioButton {
|
||||||
|
id: network
|
||||||
|
visible: modelData.name !== "lo"
|
||||||
|
|
||||||
|
required property NetworkDevice modelData
|
||||||
|
|
||||||
|
checked: Network.activeDevice?.name === modelData.name
|
||||||
|
onClicked:
|
||||||
|
text: modelData.description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,21 @@ Item {
|
|||||||
|
|
||||||
required property SystemTrayItem item
|
required property SystemTrayItem item
|
||||||
required property PanelWindow bar
|
required property PanelWindow bar
|
||||||
|
required property int ind
|
||||||
|
required property Wrapper popouts
|
||||||
property bool hasLoaded: false
|
property bool hasLoaded: false
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
if ( mouse.button === Qt.LeftButton ) {
|
||||||
root.item.activate();
|
root.item.activate();
|
||||||
|
} else if ( mouse.button === Qt.RightButton ) {
|
||||||
|
root.popouts.currentName = `traymenu${ root.ind }`;
|
||||||
|
root.popouts.currentCenter = Qt.binding( () => root.mapToItem( root.bar, root.implicitWidth / 2, 0 ).x );
|
||||||
|
root.popouts.hasCurrent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Row {
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
required property PanelWindow bar
|
required property PanelWindow bar
|
||||||
|
required property Wrapper popouts
|
||||||
readonly property alias items: repeater
|
readonly property alias items: repeater
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
@@ -22,6 +23,9 @@ Row {
|
|||||||
TrayItem {
|
TrayItem {
|
||||||
id: trayItem
|
id: trayItem
|
||||||
required property SystemTrayItem modelData
|
required property SystemTrayItem modelData
|
||||||
|
required property int index
|
||||||
|
ind: index
|
||||||
|
popouts: root.popouts
|
||||||
implicitHeight: 34
|
implicitHeight: 34
|
||||||
implicitWidth: 28
|
implicitWidth: 28
|
||||||
item: modelData
|
item: modelData
|
||||||
|
|||||||
Reference in New Issue
Block a user