diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml index b26cb949..ea5fe9ef 100644 --- a/.github/workflows/check-format.yml +++ b/.github/workflows/check-format.yml @@ -34,6 +34,5 @@ jobs: - name: Check C++ format shell: fish {0} run: | - for file in (string match -v 'build/*' **.cpp **.hpp) - clang-format $file | diff -u $file - || exit 1 - end + find plugin extras -name '*.cpp' -o -name '*.hpp' \ + | xargs clang-format --dry-run --Werror diff --git a/config/Config.qml b/config/Config.qml index cdfd7123..997eaf1e 100644 --- a/config/Config.qml +++ b/config/Config.qml @@ -326,7 +326,7 @@ Singleton { const provider = { displayName: p.displayName, enabled: p.enabled, - iface: p.iface, + interface: p.interface, name: p.name }; if (p.connectCmd && p.connectCmd.length > 0) { diff --git a/modules/background/Background.qml b/modules/background/Background.qml index 95109e12..0f44b1a0 100644 --- a/modules/background/Background.qml +++ b/modules/background/Background.qml @@ -7,159 +7,154 @@ import qs.components.containers import qs.services import qs.config -Loader { - asynchronous: true - active: Config.background.enabled +Variants { + model: Config.background.enabled ? Screens.screens : [] - sourceComponent: Variants { - model: Screens.screens + StyledWindow { + id: win - StyledWindow { - id: win + required property ShellScreen modelData - required property ShellScreen modelData + screen: modelData + name: "background" + WlrLayershell.exclusionMode: ExclusionMode.Ignore + WlrLayershell.layer: Config.background.wallpaperEnabled ? WlrLayer.Background : WlrLayer.Bottom + color: Config.background.wallpaperEnabled ? "black" : "transparent" + surfaceFormat.opaque: false - screen: modelData - name: "background" - WlrLayershell.exclusionMode: ExclusionMode.Ignore - WlrLayershell.layer: Config.background.wallpaperEnabled ? WlrLayer.Background : WlrLayer.Bottom - color: Config.background.wallpaperEnabled ? "black" : "transparent" - surfaceFormat.opaque: false + anchors.top: true + anchors.bottom: true + anchors.left: true + anchors.right: true - anchors.top: true - anchors.bottom: true - anchors.left: true - anchors.right: true + Item { + id: behindClock - Item { - id: behindClock + anchors.fill: parent + + Loader { + id: wallpaper + + asynchronous: true anchors.fill: parent + active: Config.background.wallpaperEnabled - Loader { - id: wallpaper + sourceComponent: Wallpaper {} + } - asynchronous: true + Visualiser { + anchors.fill: parent + screen: win.modelData + wallpaper: wallpaper + } + } - anchors.fill: parent - active: Config.background.wallpaperEnabled + Loader { + id: clockLoader - sourceComponent: Wallpaper {} + asynchronous: true + active: Config.background.desktopClock.enabled + + anchors.margins: Appearance.padding.large * 2 + anchors.leftMargin: Appearance.padding.large * 2 + Config.bar.sizes.innerWidth + Math.max(Appearance.padding.smaller, Config.border.thickness) + + state: Config.background.desktopClock.position + states: [ + State { + name: "top-left" + + AnchorChanges { + target: clockLoader + anchors.top: parent.top + anchors.left: parent.left + } + }, + State { + name: "top-center" + + AnchorChanges { + target: clockLoader + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + } + }, + State { + name: "top-right" + + AnchorChanges { + target: clockLoader + anchors.top: parent.top + anchors.right: parent.right + } + }, + State { + name: "middle-left" + + AnchorChanges { + target: clockLoader + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + } + }, + State { + name: "middle-center" + + AnchorChanges { + target: clockLoader + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + }, + State { + name: "middle-right" + + AnchorChanges { + target: clockLoader + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + } + }, + State { + name: "bottom-left" + + AnchorChanges { + target: clockLoader + anchors.bottom: parent.bottom + anchors.left: parent.left + } + }, + State { + name: "bottom-center" + + AnchorChanges { + target: clockLoader + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + } + }, + State { + name: "bottom-right" + + AnchorChanges { + target: clockLoader + anchors.bottom: parent.bottom + anchors.right: parent.right + } } + ] - Visualiser { - anchors.fill: parent - screen: win.modelData - wallpaper: wallpaper + transitions: Transition { + AnchorAnimation { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial } } - Loader { - id: clockLoader - - asynchronous: true - active: Config.background.desktopClock.enabled - - anchors.margins: Appearance.padding.large * 2 - anchors.leftMargin: Appearance.padding.large * 2 + Config.bar.sizes.innerWidth + Math.max(Appearance.padding.smaller, Config.border.thickness) - - state: Config.background.desktopClock.position - states: [ - State { - name: "top-left" - - AnchorChanges { - target: clockLoader - anchors.top: parent.top - anchors.left: parent.left - } - }, - State { - name: "top-center" - - AnchorChanges { - target: clockLoader - anchors.top: parent.top - anchors.horizontalCenter: parent.horizontalCenter - } - }, - State { - name: "top-right" - - AnchorChanges { - target: clockLoader - anchors.top: parent.top - anchors.right: parent.right - } - }, - State { - name: "middle-left" - - AnchorChanges { - target: clockLoader - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - } - }, - State { - name: "middle-center" - - AnchorChanges { - target: clockLoader - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - } - }, - State { - name: "middle-right" - - AnchorChanges { - target: clockLoader - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - } - }, - State { - name: "bottom-left" - - AnchorChanges { - target: clockLoader - anchors.bottom: parent.bottom - anchors.left: parent.left - } - }, - State { - name: "bottom-center" - - AnchorChanges { - target: clockLoader - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - } - }, - State { - name: "bottom-right" - - AnchorChanges { - target: clockLoader - anchors.bottom: parent.bottom - anchors.right: parent.right - } - } - ] - - transitions: Transition { - AnchorAnimation { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - - sourceComponent: DesktopClock { - wallpaper: behindClock - absX: clockLoader.x - absY: clockLoader.y - } + sourceComponent: DesktopClock { + wallpaper: behindClock + absX: clockLoader.x + absY: clockLoader.y } } } diff --git a/modules/bar/popouts/Bluetooth.qml b/modules/bar/popouts/Bluetooth.qml index 2e87270a..027b3d41 100644 --- a/modules/bar/popouts/Bluetooth.qml +++ b/modules/bar/popouts/Bluetooth.qml @@ -15,6 +15,7 @@ ColumnLayout { required property PopoutState popouts + width: 300 spacing: Appearance.spacing.small StyledText { @@ -99,6 +100,13 @@ ColumnLayout { Layout.rightMargin: Appearance.spacing.small / 2 Layout.fillWidth: true text: device.modelData.name + elide: Text.ElideRight + } + + MaterialIcon { + visible: device.modelData.state === BluetoothDeviceState.Connected // qmllint disable unresolved-type + text: Icons.getBatteryIcon(device.modelData.batteryAvailable ? device.modelData.battery * 100 : -1) + color: device.modelData.battery < 0.2 ? Colours.palette.m3error : Colours.palette.m3onSurfaceVariant } StyledRect { @@ -141,6 +149,7 @@ ColumnLayout { } Loader { + visible: status === Loader.Ready asynchronous: true active: device.modelData.bonded sourceComponent: Item { diff --git a/modules/controlcenter/network/VpnDetails.qml b/modules/controlcenter/network/VpnDetails.qml index 4d749d8b..b02f4c49 100644 --- a/modules/controlcenter/network/VpnDetails.qml +++ b/modules/controlcenter/network/VpnDetails.qml @@ -72,6 +72,13 @@ DeviceDetails { newProvider.enabled = (i === index) ? false : (p.enabled !== false); } + if (p.connectCmd && p.connectCmd.length > 0) { + newProvider.connectCmd = p.connectCmd; + } + if (p.disconnectCmd && p.disconnectCmd.length > 0) { + newProvider.disconnectCmd = p.disconnectCmd; + } + providers.push(newProvider); } else { providers.push(p); @@ -504,21 +511,31 @@ DeviceDetails { const newProvider = { displayName: editVpnDialog.displayName || editVpnDialog.interfaceName, enabled: wasEnabled, - iface: editVpnDialog.interfaceName, - name: editVpnDialog.providerName, - connectCmd: hasCommands ? editVpnDialog.connectCmd.split(" ").filter(s => s.length > 0) : undefined, - disconnectCmd: hasCommands ? editVpnDialog.disconnectCmd.split(" ").filter(s => s.length > 0) : undefined + interface: editVpnDialog.interfaceName, + name: editVpnDialog.providerName }; - // Remove undefined properties - if (!hasCommands) { - delete newProvider.connectCmd; - delete newProvider.disconnectCmd; + if (hasCommands) { + newProvider.connectCmd = editVpnDialog.connectCmd.split(" ").filter(s => s.length > 0); + newProvider.disconnectCmd = editVpnDialog.disconnectCmd.split(" ").filter(s => s.length > 0); } providers.push(newProvider); } else { - providers.push(Config.utilities.vpn.provider[i]); + const p = Config.utilities.vpn.provider[i]; + const reconstructed = { + displayName: p.displayName, + enabled: p.enabled, + interface: p.interface, + name: p.name + }; + if (p.connectCmd && p.connectCmd.length > 0) { + reconstructed.connectCmd = p.connectCmd; + } + if (p.disconnectCmd && p.disconnectCmd.length > 0) { + reconstructed.disconnectCmd = p.disconnectCmd; + } + providers.push(reconstructed); } } diff --git a/modules/controlcenter/network/VpnList.qml b/modules/controlcenter/network/VpnList.qml index 6dabc2cf..8522f972 100644 --- a/modules/controlcenter/network/VpnList.qml +++ b/modules/controlcenter/network/VpnList.qml @@ -36,6 +36,12 @@ ColumnLayout { interface: p.interface, enabled: (i === targetIndex) }; + if (p.connectCmd && p.connectCmd.length > 0) { + newProvider.connectCmd = p.connectCmd; + } + if (p.disconnectCmd && p.disconnectCmd.length > 0) { + newProvider.disconnectCmd = p.disconnectCmd; + } providers.push(newProvider); } else { providers.push(p); @@ -226,6 +232,12 @@ ColumnLayout { interface: p.interface, enabled: (i === clickedIndex) }; + if (p.connectCmd && p.connectCmd.length > 0) { + newProvider.connectCmd = p.connectCmd; + } + if (p.disconnectCmd && p.disconnectCmd.length > 0) { + newProvider.disconnectCmd = p.disconnectCmd; + } providers.push(newProvider); } else { providers.push(p); @@ -265,7 +277,20 @@ ColumnLayout { const providers = []; for (let i = 0; i < Config.utilities.vpn.provider.length; i++) { if (i !== modelData.index) { - providers.push(Config.utilities.vpn.provider[i]); + const p = Config.utilities.vpn.provider[i]; + const reconstructed = { + name: p.name, + displayName: p.displayName, + interface: p.interface, + enabled: p.enabled + }; + if (p.connectCmd && p.connectCmd.length > 0) { + reconstructed.connectCmd = p.connectCmd; + } + if (p.disconnectCmd && p.disconnectCmd.length > 0) { + reconstructed.disconnectCmd = p.disconnectCmd; + } + providers.push(reconstructed); } } Config.utilities.vpn.provider = providers; diff --git a/modules/controlcenter/network/VpnSettings.qml b/modules/controlcenter/network/VpnSettings.qml index ae689050..80732fde 100644 --- a/modules/controlcenter/network/VpnSettings.qml +++ b/modules/controlcenter/network/VpnSettings.qml @@ -119,30 +119,62 @@ ColumnLayout { icon: modelData.isActive ? "arrow_downward" : "arrow_upward" visible: !modelData.isActive || Config.utilities.vpn.provider.length > 1 onClicked: { - if (modelData.isActive && index < Config.utilities.vpn.provider.length - 1) { + const providers = []; + for (let i = 0; i < Config.utilities.vpn.provider.length; i++) { + const p = Config.utilities.vpn.provider[i]; + const reconstructed = { + name: p.name, + displayName: p.displayName, + interface: p.interface, + enabled: p.enabled + }; + if (p.connectCmd && p.connectCmd.length > 0) { + reconstructed.connectCmd = p.connectCmd; + } + if (p.disconnectCmd && p.disconnectCmd.length > 0) { + reconstructed.disconnectCmd = p.disconnectCmd; + } + providers.push(reconstructed); + } + + if (modelData.isActive && index < providers.length - 1) { // Move down - const providers = [...Config.utilities.vpn.provider]; const temp = providers[index]; providers[index] = providers[index + 1]; providers[index + 1] = temp; - Config.utilities.vpn.provider = providers; - Config.save(); } else if (!modelData.isActive) { // Make active (move to top) - const providers = [...Config.utilities.vpn.provider]; const provider = providers.splice(index, 1)[0]; providers.unshift(provider); - Config.utilities.vpn.provider = providers; - Config.save(); } + + Config.utilities.vpn.provider = providers; + Config.save(); } } IconButton { icon: "delete" onClicked: { - const providers = [...Config.utilities.vpn.provider]; - providers.splice(index, 1); + const providers = []; + for (let i = 0; i < Config.utilities.vpn.provider.length; i++) { + if (i !== index) { + const p = Config.utilities.vpn.provider[i]; + const reconstructed = { + name: p.name, + displayName: p.displayName, + interface: p.interface, + enabled: p.enabled + }; + if (p.connectCmd && p.connectCmd.length > 0) { + reconstructed.connectCmd = p.connectCmd; + } + if (p.disconnectCmd && p.disconnectCmd.length > 0) { + reconstructed.disconnectCmd = p.disconnectCmd; + } + providers.push(reconstructed); + } + } Config.utilities.vpn.provider = providers; Config.save(); } diff --git a/services/LyricsService.qml b/services/LyricsService.qml index d41b93b4..a6b94f27 100644 --- a/services/LyricsService.qml +++ b/services/LyricsService.qml @@ -78,8 +78,11 @@ Singleton { function _doLoadLyrics() { const meta = getMetadata(); - if (!meta) + if (!meta) { + lyricsModel.clear(); + root.currentIndex = -1; return; + } loading = true; lyricsModel.clear(); diff --git a/services/NotifData.qml b/services/NotifData.qml index 3fde0145..d84183e5 100644 --- a/services/NotifData.qml +++ b/services/NotifData.qml @@ -77,7 +77,8 @@ QtObject { h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909); const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0); - const cache = Paths.notifimagecache + "/${hash}.png"; + Paths; // Screw you qmlls + const cache = `${Paths.notifimagecache}/${hash}.png`; CUtils.saveItem(this, Qt.resolvedUrl(cache), () => { notif.image = cache; notif.dummyImageLoader.active = false; diff --git a/services/VPN.qml b/services/VPN.qml index 31b68319..bc0addce 100644 --- a/services/VPN.qml +++ b/services/VPN.qml @@ -25,7 +25,7 @@ Singleton { } readonly property bool isCustomProvider: typeof providerInput === "object" readonly property string providerName: isCustomProvider ? (providerInput.name || "custom") : String(providerInput) - readonly property string interfaceName: isCustomProvider ? (providerInput.iface || "") : "" + readonly property string interfaceName: isCustomProvider ? (providerInput.interface || "") : "" readonly property var currentConfig: { const name = providerName; const iface = interfaceName; @@ -36,7 +36,7 @@ Singleton { return { connectCmd: custom.connectCmd || defaults.connectCmd, disconnectCmd: custom.disconnectCmd || defaults.disconnectCmd, - interface: custom.iface || defaults.interface, + interface: custom.interface || defaults.interface, displayName: custom.displayName || defaults.displayName }; } diff --git a/utils/Icons.qml b/utils/Icons.qml index 8a62f62d..aded0f4b 100644 --- a/utils/Icons.qml +++ b/utils/Icons.qml @@ -240,4 +240,24 @@ Singleton { } return icon; } + + function getBatteryIcon(charge: int): string { + if (charge > 0 && charge < 5) + return "battery_0_bar"; + if (charge >= 5 && charge < 20) + return "battery_1_bar"; + if (charge >= 20 && charge < 35) + return "battery_2_bar"; + if (charge >= 35 && charge < 50) + return "battery_3_bar"; + if (charge >= 50 && charge < 65) + return "battery_4_bar"; + if (charge >= 65 && charge < 80) + return "battery_5_bar"; + if (charge >= 80 && charge < 95) + return "battery_6_bar"; + if (charge >= 95) + return "battery_full"; + return "battery_alert"; + } }