summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/IdleMonitors.qml51
-rw-r--r--modules/background/Background.qml1
-rw-r--r--modules/bar/components/Clock.qml1
-rw-r--r--modules/bar/components/StatusIcons.qml3
-rw-r--r--modules/bar/components/workspaces/OccupiedBg.qml11
-rw-r--r--modules/bar/popouts/Battery.qml1
-rw-r--r--modules/bar/popouts/Bluetooth.qml1
-rw-r--r--modules/bar/popouts/Content.qml7
-rw-r--r--modules/bar/popouts/KbLayout.qml28
-rw-r--r--modules/bar/popouts/Network.qml20
-rw-r--r--modules/bar/popouts/TrayMenu.qml4
-rw-r--r--modules/bar/popouts/Wrapper.qml6
-rw-r--r--modules/bar/popouts/kblayout/KbLayout.qml158
-rw-r--r--modules/bar/popouts/kblayout/KbLayoutModel.qml200
-rw-r--r--modules/controlcenter/ControlCenter.qml1
-rw-r--r--modules/controlcenter/NavRail.qml1
-rw-r--r--modules/controlcenter/Panes.qml3
-rw-r--r--modules/controlcenter/Session.qml4
-rw-r--r--modules/controlcenter/audio/AudioPane.qml145
-rw-r--r--modules/controlcenter/components/SplitPaneLayout.qml2
-rw-r--r--modules/controlcenter/components/SplitPaneWithDetails.qml1
-rw-r--r--modules/controlcenter/network/NetworkSettings.qml2
-rw-r--r--modules/controlcenter/network/NetworkingPane.qml30
-rw-r--r--modules/dashboard/Content.qml10
-rw-r--r--modules/dashboard/dash/DateTime.qml9
-rw-r--r--modules/drawers/Drawers.qml2
-rw-r--r--modules/launcher/services/Apps.qml2
-rw-r--r--modules/lock/Center.qml417
-rw-r--r--modules/lock/Content.qml27
-rw-r--r--modules/lock/InputField.qml149
-rw-r--r--modules/lock/Lock.qml55
-rw-r--r--modules/lock/LockSurface.qml231
-rw-r--r--modules/lock/Pam.qml193
-rw-r--r--modules/notifications/Notification.qml4
-rw-r--r--modules/osd/Content.qml1
-rw-r--r--modules/sidebar/NotifDock.qml1
-rw-r--r--modules/sidebar/NotifGroup.qml2
-rw-r--r--modules/utilities/Content.qml2
-rw-r--r--modules/utilities/cards/IdleInhibit.qml125
-rw-r--r--modules/utilities/cards/Toggles.qml2
-rw-r--r--modules/windowinfo/Buttons.qml1
-rw-r--r--modules/windowinfo/Preview.qml1
42 files changed, 562 insertions, 1353 deletions
diff --git a/modules/IdleMonitors.qml b/modules/IdleMonitors.qml
deleted file mode 100644
index b7ce058..0000000
--- a/modules/IdleMonitors.qml
+++ /dev/null
@@ -1,51 +0,0 @@
-pragma ComponentBehavior: Bound
-
-import "lock"
-import qs.config
-import qs.services
-import Caelestia.Internal
-import Quickshell
-import Quickshell.Wayland
-
-Scope {
- id: root
-
- required property Lock lock
- readonly property bool enabled: !Config.general.idle.inhibitWhenAudio || !Players.list.some(p => p.isPlaying)
-
- function handleIdleAction(action: var): void {
- if (!action)
- return;
-
- if (action === "lock")
- lock.lock.locked = true;
- else if (action === "unlock")
- lock.lock.locked = false;
- else if (typeof action === "string")
- Hypr.dispatch(action);
- else
- Quickshell.execDetached(action);
- }
-
- LogindManager {
- onAboutToSleep: {
- if (Config.general.idle.lockBeforeSleep)
- root.lock.lock.locked = true;
- }
- onLockRequested: root.lock.lock.locked = true
- onUnlockRequested: root.lock.lock.unlock()
- }
-
- Variants {
- model: Config.general.idle.timeouts
-
- IdleMonitor {
- required property var modelData
-
- enabled: root.enabled && (modelData.enabled ?? true)
- timeout: modelData.timeout
- respectInhibitors: modelData.respectInhibitors ?? true
- onIsIdleChanged: root.handleIdleAction(isIdle ? modelData.idleAction : modelData.returnAction)
- }
- }
-}
diff --git a/modules/background/Background.qml b/modules/background/Background.qml
index 1bc567b..0acce6b 100644
--- a/modules/background/Background.qml
+++ b/modules/background/Background.qml
@@ -10,7 +10,6 @@ import Quickshell.Wayland
import QtQuick
Loader {
- asynchronous: true
active: Config.background.enabled
sourceComponent: Variants {
diff --git a/modules/bar/components/Clock.qml b/modules/bar/components/Clock.qml
index be29c0b..801e93d 100644
--- a/modules/bar/components/Clock.qml
+++ b/modules/bar/components/Clock.qml
@@ -17,7 +17,6 @@ Column {
active: Config.bar.clock.showIcon
visible: active
- asynchronous: true
sourceComponent: MaterialIcon {
text: "calendar_month"
diff --git a/modules/bar/components/StatusIcons.qml b/modules/bar/components/StatusIcons.qml
index 2f52596..442bd2c 100644
--- a/modules/bar/components/StatusIcons.qml
+++ b/modules/bar/components/StatusIcons.qml
@@ -143,7 +143,7 @@ StyledRect {
// Network icon
WrappedLoader {
name: "network"
- active: Config.bar.status.showNetwork
+ active: Config.bar.status.showNetwork && (! Nmcli.activeEthernet || Config.bar.status.showWifi)
sourceComponent: MaterialIcon {
animate: true
@@ -265,7 +265,6 @@ StyledRect {
required property string name
Layout.alignment: Qt.AlignHCenter
- asynchronous: true
visible: active
}
}
diff --git a/modules/bar/components/workspaces/OccupiedBg.qml b/modules/bar/components/workspaces/OccupiedBg.qml
index 0364575..da6fa55 100644
--- a/modules/bar/components/workspaces/OccupiedBg.qml
+++ b/modules/bar/components/workspaces/OccupiedBg.qml
@@ -16,12 +16,15 @@ Item {
property list<var> pills: []
onOccupiedChanged: {
+ if (!occupied) return;
let count = 0;
const start = groupOffset;
const end = start + Config.bar.workspaces.shown;
for (const [ws, occ] of Object.entries(occupied)) {
if (ws > start && ws <= end && occ) {
- if (!occupied[ws - 1]) {
+ const isFirstInGroup = Number(ws) === start + 1;
+ const isLastInGroup = Number(ws) === end;
+ if (isFirstInGroup || !occupied[ws - 1]) {
if (pills[count])
pills[count].start = ws;
else
@@ -30,7 +33,7 @@ Item {
}));
count++;
}
- if (!occupied[ws + 1])
+ if ((isLastInGroup || !occupied[ws + 1]) && pills[count - 1])
pills[count - 1].end = ws;
}
}
@@ -48,8 +51,8 @@ Item {
required property var modelData
- readonly property Workspace start: root.workspaces.itemAt(getWsIdx(modelData.start)) ?? null
- readonly property Workspace end: root.workspaces.itemAt(getWsIdx(modelData.end)) ?? null
+ readonly property Workspace start: root.workspaces.count > 0 ? root.workspaces.itemAt(getWsIdx(modelData.start)) ?? null : null
+ readonly property Workspace end: root.workspaces.count > 0 ? root.workspaces.itemAt(getWsIdx(modelData.end)) ?? null : null
function getWsIdx(ws: int): int {
let i = ws - 1;
diff --git a/modules/bar/popouts/Battery.qml b/modules/bar/popouts/Battery.qml
index 35d32c5..ac975e1 100644
--- a/modules/bar/popouts/Battery.qml
+++ b/modules/bar/popouts/Battery.qml
@@ -40,7 +40,6 @@ Column {
anchors.horizontalCenter: parent.horizontalCenter
active: PowerProfiles.degradationReason !== PerformanceDegradationReason.None
- asynchronous: true
height: active ? (item?.implicitHeight ?? 0) : 0
diff --git a/modules/bar/popouts/Bluetooth.qml b/modules/bar/popouts/Bluetooth.qml
index 91ac560..676da82 100644
--- a/modules/bar/popouts/Bluetooth.qml
+++ b/modules/bar/popouts/Bluetooth.qml
@@ -142,7 +142,6 @@ ColumnLayout {
}
Loader {
- asynchronous: true
active: device.modelData.bonded
sourceComponent: Item {
implicitWidth: connectBtn.implicitWidth
diff --git a/modules/bar/popouts/Content.qml b/modules/bar/popouts/Content.qml
index da993fa..c9a7c5d 100644
--- a/modules/bar/popouts/Content.qml
+++ b/modules/bar/popouts/Content.qml
@@ -6,6 +6,8 @@ import Quickshell
import Quickshell.Services.SystemTray
import QtQuick
+import "./kblayout"
+
Item {
id: root
@@ -114,9 +116,12 @@ Item {
Popout {
name: "kblayout"
- sourceComponent: KbLayout {}
+ sourceComponent: KbLayout {
+ wrapper: root.wrapper
+ }
}
+
Popout {
name: "lockstatus"
sourceComponent: LockStatus {}
diff --git a/modules/bar/popouts/KbLayout.qml b/modules/bar/popouts/KbLayout.qml
deleted file mode 100644
index ace5af2..0000000
--- a/modules/bar/popouts/KbLayout.qml
+++ /dev/null
@@ -1,28 +0,0 @@
-import qs.components
-import qs.components.controls
-import qs.services
-import qs.config
-import Quickshell
-import QtQuick.Layouts
-
-ColumnLayout {
- id: root
-
- spacing: Appearance.spacing.normal
-
- StyledText {
- Layout.topMargin: Appearance.padding.normal
- Layout.rightMargin: Appearance.padding.normal
- text: qsTr("Keyboard layout: %1").arg(Hypr.kbLayoutFull)
- font.weight: 500
- }
-
- TextButton {
- Layout.bottomMargin: Appearance.padding.normal
- Layout.rightMargin: Appearance.padding.normal
- Layout.fillWidth: true
-
- text: qsTr("Switch layout")
- onClicked: Hypr.extras.message("switchxkblayout all next")
- }
-}
diff --git a/modules/bar/popouts/Network.qml b/modules/bar/popouts/Network.qml
index 0e99613..5b32e4a 100644
--- a/modules/bar/popouts/Network.qml
+++ b/modules/bar/popouts/Network.qml
@@ -131,17 +131,13 @@ ColumnLayout {
Nmcli.disconnectFromNetwork();
} else {
root.connectingToSsid = networkItem.modelData.ssid;
- NetworkConnection.handleConnect(
- networkItem.modelData,
- null,
- (network) => {
- // Password is required - show password dialog
- root.passwordNetwork = network;
- root.showPasswordDialog = true;
- root.wrapper.currentName = "wirelesspassword";
- }
- );
-
+ NetworkConnection.handleConnect(networkItem.modelData, null, network => {
+ // Password is required - show password dialog
+ root.passwordNetwork = network;
+ root.showPasswordDialog = true;
+ root.wrapper.currentName = "wirelesspassword";
+ });
+
// Clear connecting state if connection succeeds immediately (saved profile)
// This is handled by the onActiveChanged connection below
}
@@ -216,7 +212,7 @@ ColumnLayout {
anchors.centerIn: parent
strokeWidth: Appearance.padding.small / 2
bgColour: "transparent"
- implicitHeight: parent.implicitHeight - Appearance.padding.smaller * 2
+ implicitSize: parent.implicitHeight - Appearance.padding.smaller * 2
running: Nmcli.scanning
}
}
diff --git a/modules/bar/popouts/TrayMenu.qml b/modules/bar/popouts/TrayMenu.qml
index c87c0e1..9b743db 100644
--- a/modules/bar/popouts/TrayMenu.qml
+++ b/modules/bar/popouts/TrayMenu.qml
@@ -85,7 +85,6 @@ StackView {
anchors.right: parent.right
active: !item.modelData.isSeparator
- asynchronous: true
sourceComponent: Item {
implicitHeight: label.implicitHeight
@@ -118,7 +117,6 @@ StackView {
anchors.left: parent.left
active: item.modelData.icon !== ""
- asynchronous: true
sourceComponent: IconImage {
implicitSize: label.implicitHeight
@@ -155,7 +153,6 @@ StackView {
anchors.right: parent.right
active: item.modelData.hasChildren
- asynchronous: true
sourceComponent: MaterialIcon {
text: "chevron_right"
@@ -169,7 +166,6 @@ StackView {
Loader {
active: menu.isSubMenu
- asynchronous: true
sourceComponent: Item {
implicitWidth: back.implicitWidth
diff --git a/modules/bar/popouts/Wrapper.qml b/modules/bar/popouts/Wrapper.qml
index 5ef4f9d..fc74222 100644
--- a/modules/bar/popouts/Wrapper.qml
+++ b/modules/bar/popouts/Wrapper.qml
@@ -35,8 +35,8 @@ Item {
if (mode === "winfo") {
detachedMode = mode;
} else {
- detachedMode = "any";
queuedMode = mode;
+ detachedMode = "any";
}
focus = true;
}
@@ -101,7 +101,6 @@ Item {
id: content
shouldBeActive: root.hasCurrent && !root.detachedMode
- asynchronous: true
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
@@ -112,7 +111,6 @@ Item {
Comp {
shouldBeActive: root.detachedMode === "winfo"
- asynchronous: true
anchors.centerIn: parent
sourceComponent: WindowInfo {
@@ -123,7 +121,6 @@ Item {
Comp {
shouldBeActive: root.detachedMode === "any"
- asynchronous: true
anchors.centerIn: parent
sourceComponent: ControlCenter {
@@ -173,7 +170,6 @@ Item {
property bool shouldBeActive
- asynchronous: true
active: false
opacity: 0
diff --git a/modules/bar/popouts/kblayout/KbLayout.qml b/modules/bar/popouts/kblayout/KbLayout.qml
new file mode 100644
index 0000000..f612f58
--- /dev/null
+++ b/modules/bar/popouts/kblayout/KbLayout.qml
@@ -0,0 +1,158 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import qs.components
+import qs.components.controls
+import qs.services
+import qs.config
+import qs.utils
+
+import "."
+
+ColumnLayout {
+ id: root
+
+ required property Item wrapper
+
+ spacing: Appearance.spacing.small
+ width: Config.bar.sizes.kbLayoutWidth
+
+ KbLayoutModel { id: kb }
+
+ function refresh() { kb.refresh() }
+ Component.onCompleted: kb.start()
+
+ StyledText {
+ Layout.topMargin: Appearance.padding.normal
+ Layout.rightMargin: Appearance.padding.small
+ text: qsTr("Keyboard Layouts")
+ font.weight: 500
+ }
+
+ ListView {
+ id: list
+ model: kb.visibleModel
+
+ Layout.fillWidth: true
+ Layout.rightMargin: Appearance.padding.small
+ Layout.topMargin: Appearance.spacing.small
+
+ clip: true
+ interactive: true
+ implicitHeight: Math.min(contentHeight, 320)
+ visible: kb.visibleModel.count > 0
+ spacing: Appearance.spacing.small
+
+ add: Transition {
+ NumberAnimation { properties: "opacity"; from: 0; to: 1; duration: 140 }
+ NumberAnimation { properties: "y"; duration: 180; easing.type: Easing.OutCubic }
+ }
+ remove: Transition { NumberAnimation { properties: "opacity"; to: 0; duration: 100 } }
+ move: Transition { NumberAnimation { properties: "y"; duration: 180; easing.type: Easing.OutCubic } }
+ displaced: Transition { NumberAnimation { properties: "y"; duration: 180; easing.type: Easing.OutCubic } }
+
+ delegate: Item {
+ required property int layoutIndex
+ required property string label
+
+ width: list.width
+ height: Math.max(36, rowText.implicitHeight + Appearance.padding.small * 2)
+
+ readonly property bool isDisabled: layoutIndex > 3
+
+ StateLayer {
+ id: layer
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ implicitHeight: parent.height - 4
+
+ radius: Appearance.rounding.full
+ enabled: !isDisabled
+
+ function onClicked(): void {
+ if (!isDisabled)
+ kb.switchTo(layoutIndex);
+ }
+ }
+
+ StyledText {
+ id: rowText
+ anchors.verticalCenter: layer.verticalCenter
+ anchors.left: layer.left
+ anchors.right: layer.right
+ anchors.leftMargin: Appearance.padding.small
+ anchors.rightMargin: Appearance.padding.small
+ text: label
+ elide: Text.ElideRight
+ opacity: isDisabled ? 0.4 : 1.0
+ }
+
+ ToolTip.visible: isDisabled && layer.containsMouse
+ ToolTip.text: "XKB limitation: maximum 4 layouts allowed"
+ }
+ }
+
+ Rectangle {
+ visible: kb.activeLabel.length > 0
+ Layout.fillWidth: true
+ Layout.rightMargin: Appearance.padding.small
+ Layout.topMargin: Appearance.spacing.small
+
+ height: 1
+ color: Colours.palette.m3onSurfaceVariant
+ opacity: 0.35
+ }
+
+ RowLayout {
+ id: activeRow
+
+ visible: kb.activeLabel.length > 0
+ Layout.fillWidth: true
+ Layout.rightMargin: Appearance.padding.small
+ Layout.topMargin: Appearance.spacing.small
+ spacing: Appearance.spacing.small
+
+ opacity: 1
+ scale: 1
+
+ MaterialIcon {
+ text: "keyboard"
+ color: Colours.palette.m3primary
+ }
+
+ StyledText {
+ Layout.fillWidth: true
+ text: kb.activeLabel
+ elide: Text.ElideRight
+ font.weight: 500
+ color: Colours.palette.m3primary
+ }
+
+ Connections {
+ target: kb
+ function onActiveLabelChanged() {
+ if (!activeRow.visible)
+ return;
+ popIn.restart();
+ }
+ }
+
+ SequentialAnimation {
+ id: popIn
+ running: false
+
+ ParallelAnimation {
+ NumberAnimation { target: activeRow; property: "opacity"; to: 0.0; duration: 70 }
+ NumberAnimation { target: activeRow; property: "scale"; to: 0.92; duration: 70 }
+ }
+
+ ParallelAnimation {
+ NumberAnimation { target: activeRow; property: "opacity"; to: 1.0; duration: 160; easing.type: Easing.OutCubic }
+ NumberAnimation { target: activeRow; property: "scale"; to: 1.0; duration: 220; easing.type: Easing.OutBack }
+ }
+ }
+ }
+}
diff --git a/modules/bar/popouts/kblayout/KbLayoutModel.qml b/modules/bar/popouts/kblayout/KbLayoutModel.qml
new file mode 100644
index 0000000..41e45b3
--- /dev/null
+++ b/modules/bar/popouts/kblayout/KbLayoutModel.qml
@@ -0,0 +1,200 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+
+import Quickshell
+import Quickshell.Io
+
+import qs.config
+import Caelestia
+
+Item {
+ id: model
+ visible: false
+
+ ListModel { id: _visibleModel }
+ property alias visibleModel: _visibleModel
+
+ property string activeLabel: ""
+ property int activeIndex: -1
+
+ function start() {
+ _xkbXmlBase.running = true;
+ _getKbLayoutOpt.running = true;
+ }
+
+ function refresh() {
+ _notifiedLimit = false;
+ _getKbLayoutOpt.running = true;
+ }
+
+ function switchTo(idx) {
+ _switchProc.command = ["hyprctl", "switchxkblayout", "all", String(idx)];
+ _switchProc.running = true;
+ }
+
+ ListModel { id: _layoutsModel }
+
+ property var _xkbMap: ({})
+ property bool _notifiedLimit: false
+
+ Process {
+ id: _xkbXmlBase
+ command: ["xmllint", "--xpath", "//layout/configItem[name and description]", "/usr/share/X11/xkb/rules/base.xml"]
+ stdout: StdioCollector { onStreamFinished: _buildXmlMap(text) }
+ onRunningChanged: if (!running && (typeof exitCode !== "undefined") && exitCode !== 0) _xkbXmlEvdev.running = true
+ }
+
+ Process {
+ id: _xkbXmlEvdev
+ command: ["xmllint", "--xpath", "//layout/configItem[name and description]", "/usr/share/X11/xkb/rules/evdev.xml"]
+ stdout: StdioCollector { onStreamFinished: _buildXmlMap(text) }
+ }
+
+ function _buildXmlMap(xml) {
+ const map = {};
+
+ const re = /<name>\s*([^<]+?)\s*<\/name>[\s\S]*?<description>\s*([^<]+?)\s*<\/description>/g;
+
+ let m;
+ while ((m = re.exec(xml)) !== null) {
+ const code = (m[1] || "").trim();
+ const desc = (m[2] || "").trim();
+ if (!code || !desc) continue;
+ map[code] = _short(desc);
+ }
+
+ if (Object.keys(map).length === 0)
+ return;
+
+ _xkbMap = map;
+
+ if (_layoutsModel.count > 0) {
+ const tmp = [];
+ for (let i = 0; i < _layoutsModel.count; i++) {
+ const it = _layoutsModel.get(i);
+ tmp.push({ layoutIndex: it.layoutIndex, token: it.token, label: _pretty(it.token) });
+ }
+ _layoutsModel.clear();
+ tmp.forEach(t => _layoutsModel.append(t));
+ _fetchActiveLayouts.running = true;
+ }
+ }
+
+ function _short(desc) {
+ const m = desc.match(/^(.*)\((.*)\)$/);
+ if (!m) return desc;
+ const lang = m[1].trim();
+ const region = m[2].trim();
+ const code = (region.split(/[,\s-]/)[0] || region).slice(0, 2).toUpperCase();
+ return `${lang} (${code})`;
+ }
+
+ Process {
+ id: _getKbLayoutOpt
+ command: ["hyprctl", "-j", "getoption", "input:kb_layout"]
+ stdout: StdioCollector {
+ onStreamFinished: {
+ try {
+ const j = JSON.parse(text);
+ const raw = (j?.str || j?.value || "").toString().trim();
+ if (raw.length) {
+ _setLayouts(raw);
+ _fetchActiveLayouts.running = true;
+ return;
+ }
+ } catch (e) {}
+ _fetchLayoutsFromDevices.running = true;
+ }
+ }
+ }
+
+ Process {
+ id: _fetchLayoutsFromDevices
+ command: ["hyprctl", "-j", "devices"]
+ stdout: StdioCollector {
+ onStreamFinished: {
+ try {
+ const dev = JSON.parse(text);
+ const kb = dev?.keyboards?.find(k => k.main) || dev?.keyboards?.[0];
+ const raw = (kb?.layout || "").trim();
+ if (raw.length) _setLayouts(raw);
+ } catch (e) {}
+ _fetchActiveLayouts.running = true;
+ }
+ }
+ }
+
+ Process {
+ id: _fetchActiveLayouts
+ command: ["hyprctl", "-j", "devices"]
+ stdout: StdioCollector {
+ onStreamFinished: {
+ try {
+ const dev = JSON.parse(text);
+ const kb = dev?.keyboards?.find(k => k.main) || dev?.keyboards?.[0];
+ const idx = kb?.active_layout_index ?? -1;
+
+ activeIndex = idx >= 0 ? idx : -1;
+ activeLabel =
+ (idx >= 0 && idx < _layoutsModel.count)
+ ? _layoutsModel.get(idx).label
+ : "";
+ } catch (e) {
+ activeIndex = -1;
+ activeLabel = "";
+ }
+
+ _rebuildVisible();
+ }
+ }
+ }
+
+ Process {
+ id: _switchProc
+ onRunningChanged: if (!running) _fetchActiveLayouts.running = true
+ }
+
+ function _setLayouts(raw) {
+ const parts = raw.split(",").map(s => s.trim()).filter(Boolean);
+ _layoutsModel.clear();
+
+ const seen = new Set();
+ let idx = 0;
+
+ for (const p of parts) {
+ if (seen.has(p)) continue;
+ seen.add(p);
+ _layoutsModel.append({ layoutIndex: idx, token: p, label: _pretty(p) });
+ idx++;
+ }
+ }
+
+ function _rebuildVisible() {
+ _visibleModel.clear();
+
+ let arr = [];
+ for (let i = 0; i < _layoutsModel.count; i++)
+ arr.push(_layoutsModel.get(i));
+
+ arr = arr.filter(i => i.layoutIndex !== activeIndex);
+ arr.forEach(i => _visibleModel.append(i));
+
+ if (!Config.utilities.toasts.kbLimit)
+ return;
+
+ if (_layoutsModel.count > 4) {
+ Toaster.toast(
+ qsTr("Keyboard layout limit"),
+ qsTr("XKB supports only 4 layouts at a time"),
+ "warning"
+ );
+ }
+ }
+
+ function _pretty(token) {
+ const code = token.replace(/\(.*\)$/, "").trim();
+ if (_xkbMap[code]) return code.toUpperCase() + " - " + _xkbMap[code];
+ return code.toUpperCase() + " - " + code;
+ }
+}
diff --git a/modules/controlcenter/ControlCenter.qml b/modules/controlcenter/ControlCenter.qml
index fdb824e..043e7e1 100644
--- a/modules/controlcenter/ControlCenter.qml
+++ b/modules/controlcenter/ControlCenter.qml
@@ -42,7 +42,6 @@ Item {
Layout.fillWidth: true
Layout.columnSpan: 2
- asynchronous: true
active: root.floating
visible: active
diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml
index ef338b2..e61a741 100644
--- a/modules/controlcenter/NavRail.qml
+++ b/modules/controlcenter/NavRail.qml
@@ -43,7 +43,6 @@ Item {
Loader {
Layout.topMargin: Appearance.spacing.large
- asynchronous: true
active: !root.session.floating
visible: active
diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml
index 32ee708..03f74be 100644
--- a/modules/controlcenter/Panes.qml
+++ b/modules/controlcenter/Panes.qml
@@ -126,11 +126,10 @@ ClippingRectangle {
anchors.fill: parent
clip: false
- asynchronous: true
active: false
Component.onCompleted: {
- pane.updateActive();
+ Qt.callLater(pane.updateActive);
}
onActiveChanged: {
diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml
index 090ae9b..164e6cd 100644
--- a/modules/controlcenter/Session.qml
+++ b/modules/controlcenter/Session.qml
@@ -15,6 +15,6 @@ QtObject {
readonly property NetworkState network: NetworkState {}
readonly property EthernetState ethernet: EthernetState {}
- onActiveChanged: activeIndex = panes.indexOf(active)
- onActiveIndexChanged: active = panes[activeIndex]
+ onActiveChanged: activeIndex = Math.max(0, panes.indexOf(active))
+ onActiveIndexChanged: if (panes[activeIndex]) active = panes[activeIndex]
}
diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml
index 76122f9..e4a1a64 100644
--- a/modules/controlcenter/audio/AudioPane.qml
+++ b/modules/controlcenter/audio/AudioPane.qml
@@ -460,6 +460,151 @@ Item {
}
}
}
+
+ SectionHeader {
+ title: qsTr("Applications")
+ description: qsTr("Control volume for individual applications")
+ }
+
+ SectionContainer {
+ contentSpacing: Appearance.spacing.normal
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.small
+
+ Repeater {
+ model: Audio.streams
+ Layout.fillWidth: true
+
+ delegate: ColumnLayout {
+ required property var modelData
+ required property int index
+
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.smaller
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.normal
+
+ MaterialIcon {
+ text: "apps"
+ font.pointSize: Appearance.font.size.normal
+ fill: 0
+ }
+
+ StyledText {
+ Layout.fillWidth: true
+ elide: Text.ElideRight
+ maximumLineCount: 1
+ text: Audio.getStreamName(modelData)
+ font.pointSize: Appearance.font.size.normal
+ font.weight: 500
+ }
+
+ StyledInputField {
+ id: streamVolumeInput
+ Layout.preferredWidth: 70
+ validator: IntValidator { bottom: 0; top: 100 }
+ enabled: !Audio.getStreamMuted(modelData)
+
+ Component.onCompleted: {
+ text = Math.round(Audio.getStreamVolume(modelData) * 100).toString();
+ }
+
+ Connections {
+ target: modelData
+ function onAudioChanged() {
+ if (!streamVolumeInput.hasFocus && modelData?.audio) {
+ streamVolumeInput.text = Math.round(modelData.audio.volume * 100).toString();
+ }
+ }
+ }
+
+ onTextEdited: (text) => {
+ if (hasFocus) {
+ const val = parseInt(text);
+ if (!isNaN(val) && val >= 0 && val <= 100) {
+ Audio.setStreamVolume(modelData, val / 100);
+ }
+ }
+ }
+
+ onEditingFinished: {
+ const val = parseInt(text);
+ if (isNaN(val) || val < 0 || val > 100) {
+ text = Math.round(Audio.getStreamVolume(modelData) * 100).toString();
+ }
+ }
+ }
+
+ StyledText {
+ text: "%"
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.normal
+ opacity: Audio.getStreamMuted(modelData) ? 0.5 : 1
+ }
+
+ StyledRect {
+ implicitWidth: implicitHeight
+ implicitHeight: streamMuteIcon.implicitHeight + Appearance.padding.normal * 2
+
+ radius: Appearance.rounding.normal
+ color: Audio.getStreamMuted(modelData) ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
+
+ StateLayer {
+ function onClicked(): void {
+ Audio.setStreamMuted(modelData, !Audio.getStreamMuted(modelData));
+ }
+ }
+
+ MaterialIcon {
+ id: streamMuteIcon
+
+ anchors.centerIn: parent
+ text: Audio.getStreamMuted(modelData) ? "volume_off" : "volume_up"
+ color: Audio.getStreamMuted(modelData) ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
+ }
+ }
+ }
+
+ StyledSlider {
+ Layout.fillWidth: true
+ implicitHeight: Appearance.padding.normal * 3
+
+ value: Audio.getStreamVolume(modelData)
+ enabled: !Audio.getStreamMuted(modelData)
+ opacity: enabled ? 1 : 0.5
+ onMoved: {
+ Audio.setStreamVolume(modelData, value);
+ if (!streamVolumeInput.hasFocus) {
+ streamVolumeInput.text = Math.round(value * 100).toString();
+ }
+ }
+
+ Connections {
+ target: modelData
+ function onAudioChanged() {
+ if (modelData?.audio) {
+ value = modelData.audio.volume;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ StyledText {
+ Layout.fillWidth: true
+ visible: Audio.streams.length === 0
+ text: qsTr("No applications currently playing audio")
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+ }
}
}
}
diff --git a/modules/controlcenter/components/SplitPaneLayout.qml b/modules/controlcenter/components/SplitPaneLayout.qml
index 8b4f0d9..3609a74 100644
--- a/modules/controlcenter/components/SplitPaneLayout.qml
+++ b/modules/controlcenter/components/SplitPaneLayout.qml
@@ -49,7 +49,6 @@ RowLayout {
anchors.leftMargin: Appearance.padding.large
anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2
- asynchronous: true
sourceComponent: root.leftContent
Component.onCompleted: {
@@ -91,7 +90,6 @@ RowLayout {
anchors.fill: parent
anchors.margins: Appearance.padding.large * 2
- asynchronous: true
sourceComponent: root.rightContent
Component.onCompleted: {
diff --git a/modules/controlcenter/components/SplitPaneWithDetails.qml b/modules/controlcenter/components/SplitPaneWithDetails.qml
index e873923..5db5bdb 100644
--- a/modules/controlcenter/components/SplitPaneWithDetails.qml
+++ b/modules/controlcenter/components/SplitPaneWithDetails.qml
@@ -56,7 +56,6 @@ Item {
transformOrigin: Item.Center
clip: false
- asynchronous: true
sourceComponent: rightPaneItem.targetComponent
}
diff --git a/modules/controlcenter/network/NetworkSettings.qml b/modules/controlcenter/network/NetworkSettings.qml
index 22e07cb..81175fb 100644
--- a/modules/controlcenter/network/NetworkSettings.qml
+++ b/modules/controlcenter/network/NetworkSettings.qml
@@ -4,10 +4,12 @@ import ".."
import "../components"
import qs.components
import qs.components.controls
+import qs.components.containers
import qs.components.effects
import qs.services
import qs.config
import QtQuick
+import QtQuick.Controls
import QtQuick.Layouts
ColumnLayout {
diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml
index b430cce..9047174 100644
--- a/modules/controlcenter/network/NetworkingPane.qml
+++ b/modules/controlcenter/network/NetworkingPane.qml
@@ -153,9 +153,9 @@ Item {
rightContent: Component {
Item {
id: rightPaneItem
-
- property var ethernetPane: root.session.ethernet.active
- property var wirelessPane: root.session.network.active
+
+ property var ethernetPane: root.session && root.session.ethernet ? root.session.ethernet.active : null
+ property var wirelessPane: root.session && root.session.network ? root.session.network.active : null
property var pane: ethernetPane || wirelessPane
property string paneId: ethernetPane ? ("eth:" + (ethernetPane.interface || "")) : (wirelessPane ? ("wifi:" + (wirelessPane.ssid || wirelessPane.bssid || "")) : "settings")
property Component targetComponent: settingsComponent
@@ -173,28 +173,28 @@ Item {
}
Connections {
- target: root.session.ethernet
+ target: root.session && root.session.ethernet ? root.session.ethernet : null
+ enabled: target !== null
+
function onActiveChanged() {
- // Clear wireless when ethernet is selected
- if (root.session.ethernet.active && root.session.network.active) {
- root.session.network.active = null;
- return; // Let the network.onActiveChanged handle the update
+ // Clear others when ethernet is selected
+ if (root.session && root.session.ethernet && root.session.ethernet.active) {
+ if (root.session.network && root.session.network.active) root.session.network.active = null;
}
rightPaneItem.nextComponent = rightPaneItem.getComponentForPane();
- // paneId will automatically update via property binding
}
}
Connections {
- target: root.session.network
+ target: root.session && root.session.network ? root.session.network : null
+ enabled: target !== null
+
function onActiveChanged() {
- // Clear ethernet when wireless is selected
- if (root.session.network.active && root.session.ethernet.active) {
- root.session.ethernet.active = null;
- return; // Let the ethernet.onActiveChanged handle the update
+ // Clear others when wireless is selected
+ if (root.session && root.session.network && root.session.network.active) {
+ if (root.session.ethernet && root.session.ethernet.active) root.session.ethernet.active = null;
}
rightPaneItem.nextComponent = rightPaneItem.getComponentForPane();
- // paneId will automatically update via property binding
}
}
diff --git a/modules/dashboard/Content.qml b/modules/dashboard/Content.qml
index f5f0e46..79659c5 100644
--- a/modules/dashboard/Content.qml
+++ b/modules/dashboard/Content.qml
@@ -85,6 +85,7 @@ Item {
id: row
Pane {
+ index: 0
sourceComponent: Dash {
visibilities: root.visibilities
state: root.state
@@ -92,16 +93,19 @@ Item {
}
Pane {
+ index: 1
sourceComponent: Media {
visibilities: root.visibilities
}
}
Pane {
+ index: 2
sourceComponent: Performance {}
}
Pane {
+ index: 3
sourceComponent: Weather {}
}
}
@@ -127,9 +131,15 @@ Item {
}
component Pane: Loader {
+ id: pane
+
+ required property int index
+
Layout.alignment: Qt.AlignTop
Component.onCompleted: active = Qt.binding(() => {
+ // Always keep current tab loaded
+ if (pane.index === view.currentIndex) return true;
const vx = Math.floor(view.visibleArea.xPosition * view.contentWidth);
const vex = Math.floor(vx + view.visibleArea.widthRatio * view.contentWidth);
return (vx >= x && vx <= x + implicitWidth) || (vex >= x && vex <= x + implicitWidth);
diff --git a/modules/dashboard/dash/DateTime.qml b/modules/dashboard/dash/DateTime.qml
index bbef067..e740448 100644
--- a/modules/dashboard/dash/DateTime.qml
+++ b/modules/dashboard/dash/DateTime.qml
@@ -9,8 +9,6 @@ import QtQuick.Layouts
Item {
id: root
- readonly property list<string> timeComponents: Time.format(Config.services.useTwelveHourClock ? "hh:mm:A" : "hh:mm").split(":")
-
anchors.top: parent.top
anchors.bottom: parent.bottom
implicitWidth: Config.dashboard.sizes.dateTimeWidth
@@ -24,7 +22,7 @@ Item {
StyledText {
Layout.bottomMargin: -(font.pointSize * 0.4)
Layout.alignment: Qt.AlignHCenter
- text: root.timeComponents[0]
+ text: Time.hourStr
color: Colours.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge
font.family: Appearance.font.family.clock
@@ -42,7 +40,7 @@ Item {
StyledText {
Layout.topMargin: -(font.pointSize * 0.4)
Layout.alignment: Qt.AlignHCenter
- text: root.timeComponents[1]
+ text: Time.minuteStr
color: Colours.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge
font.family: Appearance.font.family.clock
@@ -52,12 +50,11 @@ Item {
Loader {
Layout.alignment: Qt.AlignHCenter
- asynchronous: true
active: Config.services.useTwelveHourClock
visible: active
sourceComponent: StyledText {
- text: root.timeComponents[2] ?? ""
+ text: Time.amPmStr
color: Colours.palette.m3primary
font.pointSize: Appearance.font.size.large
font.family: Appearance.font.family.clock
diff --git a/modules/drawers/Drawers.qml b/modules/drawers/Drawers.qml
index 9fc38bd..00f9596 100644
--- a/modules/drawers/Drawers.qml
+++ b/modules/drawers/Drawers.qml
@@ -47,7 +47,7 @@ Variants {
return 0;
const mon = Hypr.monitorFor(screen);
- if (mon?.lastIpcObject.specialWorkspace.name || mon?.activeWorkspace?.lastIpcObject.windows > 0)
+ if (mon?.lastIpcObject?.specialWorkspace?.name || mon?.activeWorkspace?.lastIpcObject?.windows > 0)
return 0;
const thresholds = [];
diff --git a/modules/launcher/services/Apps.qml b/modules/launcher/services/Apps.qml
index 79f3754..cb334fb 100644
--- a/modules/launcher/services/Apps.qml
+++ b/modules/launcher/services/Apps.qml
@@ -66,7 +66,7 @@ Searcher {
}
list: appDb.apps
- useFuzzy: Config.launcher.useFuzzy.apps
+ useFuzzy: Config.launcher.useFuzzy
AppDb {
id: appDb
diff --git a/modules/lock/Center.qml b/modules/lock/Center.qml
deleted file mode 100644
index 4e2215b..0000000
--- a/modules/lock/Center.qml
+++ /dev/null
@@ -1,417 +0,0 @@
-pragma ComponentBehavior: Bound
-
-import qs.components
-import qs.components.controls
-import qs.services
-import qs.config
-import qs.utils
-import QtQuick
-import QtQuick.Layouts
-
-ColumnLayout {
- id: root
-
- required property var lock
- readonly property list<string> timeComponents: Time.format(Config.services.useTwelveHourClock ? "hh:mm:A" : "hh:mm").split(":")
- readonly property real centerScale: Math.min(1, (lock.screen?.height ?? 1440) / 1440)
- readonly property int centerWidth: Config.lock.sizes.centerWidth * centerScale
-
- Layout.preferredWidth: centerWidth
- Layout.fillWidth: false
- Layout.fillHeight: true
-
- spacing: Appearance.spacing.large * 2
-
- RowLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: Appearance.spacing.small
-
- StyledText {
- Layout.alignment: Qt.AlignVCenter
- text: root.timeComponents[0]
- color: Colours.palette.m3secondary
- font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
- font.family: Appearance.font.family.clock
- font.bold: true
- }
-
- StyledText {
- Layout.alignment: Qt.AlignVCenter
- text: ":"
- color: Colours.palette.m3primary
- font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
- font.family: Appearance.font.family.clock
- font.bold: true
- }
-
- StyledText {
- Layout.alignment: Qt.AlignVCenter
- text: root.timeComponents[1]
- color: Colours.palette.m3secondary
- font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
- font.family: Appearance.font.family.clock
- font.bold: true
- }
-
- Loader {
- Layout.leftMargin: Appearance.spacing.small
- Layout.alignment: Qt.AlignVCenter
-
- asynchronous: true
- active: Config.services.useTwelveHourClock
- visible: active
-
- sourceComponent: StyledText {
- text: root.timeComponents[2] ?? ""
- color: Colours.palette.m3primary
- font.pointSize: Math.floor(Appearance.font.size.extraLarge * 2 * root.centerScale)
- font.family: Appearance.font.family.clock
- font.bold: true
- }
- }
- }
-
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- Layout.topMargin: -Appearance.padding.large * 2
-
- text: Time.format("dddd, d MMMM yyyy")
- color: Colours.palette.m3tertiary
- font.pointSize: Math.floor(Appearance.font.size.extraLarge * root.centerScale)
- font.family: Appearance.font.family.mono
- font.bold: true
- }
-
- StyledClippingRect {
- Layout.topMargin: Appearance.spacing.large * 2
- Layout.alignment: Qt.AlignHCenter
-
- implicitWidth: root.centerWidth / 2
- implicitHeight: root.centerWidth / 2
-
- color: Colours.tPalette.m3surfaceContainer
- radius: Appearance.rounding.full
-
- MaterialIcon {
- anchors.centerIn: parent
-
- text: "person"
- color: Colours.palette.m3onSurfaceVariant
- font.pointSize: Math.floor(root.centerWidth / 4)
- }
-
- Image {
- id: pfp
-
- anchors.fill: parent
- source: Paths.face
- }
- }
-
- StyledRect {
- Layout.alignment: Qt.AlignHCenter
-
- implicitWidth: root.centerWidth * 0.8
- implicitHeight: input.implicitHeight + Appearance.padding.small * 2
-
- color: Colours.tPalette.m3surfaceContainer
- radius: Appearance.rounding.full
-
- focus: true
- onActiveFocusChanged: {
- if (!activeFocus)
- forceActiveFocus();
- }
-
- Keys.onPressed: event => {
- if (root.lock.unlocking)
- return;
-
- if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return)
- inputField.placeholder.animate = false;
-
- root.lock.pam.handleKey(event);
- }
-
- StateLayer {
- hoverEnabled: false
- cursorShape: Qt.IBeamCursor
-
- function onClicked(): void {
- parent.forceActiveFocus();
- }
- }
-
- RowLayout {
- id: input
-
- anchors.fill: parent
- anchors.margins: Appearance.padding.small
- spacing: Appearance.spacing.normal
-
- Item {
- implicitWidth: implicitHeight
- implicitHeight: fprintIcon.implicitHeight + Appearance.padding.small * 2
-
- MaterialIcon {
- id: fprintIcon
-
- anchors.centerIn: parent
- animate: true
- text: {
- if (root.lock.pam.fprint.tries >= Config.lock.maxFprintTries)
- return "fingerprint_off";
- if (root.lock.pam.fprint.active)
- return "fingerprint";
- return "lock";
- }
- color: root.lock.pam.fprint.tries >= Config.lock.maxFprintTries ? Colours.palette.m3error : Colours.palette.m3onSurface
- opacity: root.lock.pam.passwd.active ? 0 : 1
-
- Behavior on opacity {
- Anim {}
- }
- }
-
- CircularIndicator {
- anchors.fill: parent
- running: root.lock.pam.passwd.active
- }
- }
-
- InputField {
- id: inputField
-
- pam: root.lock.pam
- }
-
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: enterIcon.implicitHeight + Appearance.padding.small * 2
-
- color: root.lock.pam.buffer ? Colours.palette.m3primary : Colours.layer(Colours.palette.m3surfaceContainerHigh, 2)
- radius: Appearance.rounding.full
-
- StateLayer {
- color: root.lock.pam.buffer ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
-
- function onClicked(): void {
- root.lock.pam.passwd.start();
- }
- }
-
- MaterialIcon {
- id: enterIcon
-
- anchors.centerIn: parent
- text: "arrow_forward"
- color: root.lock.pam.buffer ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
- font.weight: 500
- }
- }
- }
- }
-
- Item {
- Layout.fillWidth: true
- Layout.topMargin: -Appearance.spacing.large
-
- implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
-
- Behavior on implicitHeight {
- Anim {}
- }
-
- StyledText {
- id: stateMessage
-
- readonly property string msg: {
- if (Hypr.kbLayout !== Hypr.defaultKbLayout) {
- if (Hypr.capsLock && Hypr.numLock)
- return qsTr("Caps lock and Num lock are ON.\nKeyboard layout: %1").arg(Hypr.kbLayoutFull);
- if (Hypr.capsLock)
- return qsTr("Caps lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
- if (Hypr.numLock)
- return qsTr("Num lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
- return qsTr("Keyboard layout: %1").arg(Hypr.kbLayoutFull);
- }
-
- if (Hypr.capsLock && Hypr.numLock)
- return qsTr("Caps lock and Num lock are ON.");
- if (Hypr.capsLock)
- return qsTr("Caps lock is ON.");
- if (Hypr.numLock)
- return qsTr("Num lock is ON.");
-
- return "";
- }
-
- property bool shouldBeVisible
-
- onMsgChanged: {
- if (msg) {
- if (opacity > 0) {
- animate = true;
- text = msg;
- animate = false;
- } else {
- text = msg;
- }
- shouldBeVisible = true;
- } else {
- shouldBeVisible = false;
- }
- }
-
- anchors.left: parent.left
- anchors.right: parent.right
-
- scale: shouldBeVisible && !message.msg ? 1 : 0.7
- opacity: shouldBeVisible && !message.msg ? 1 : 0
- color: Colours.palette.m3onSurfaceVariant
- animateProp: "opacity"
-
- font.family: Appearance.font.family.mono
- horizontalAlignment: Qt.AlignHCenter
- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
- lineHeight: 1.2
-
- Behavior on scale {
- Anim {}
- }
-
- Behavior on opacity {
- Anim {}
- }
- }
-
- StyledText {
- id: message
-
- readonly property Pam pam: root.lock.pam
- readonly property string msg: {
- if (pam.fprintState === "error")
- return qsTr("FP ERROR: %1").arg(pam.fprint.message);
- if (pam.state === "error")
- return qsTr("PW ERROR: %1").arg(pam.passwd.message);
-
- if (pam.lockMessage)
- return pam.lockMessage;
-
- if (pam.state === "max" && pam.fprintState === "max")
- return qsTr("Maximum password and fingerprint attempts reached.");
- if (pam.state === "max") {
- if (pam.fprint.available)
- return qsTr("Maximum password attempts reached. Please use fingerprint.");
- return qsTr("Maximum password attempts reached.");
- }
- if (pam.fprintState === "max")
- return qsTr("Maximum fingerprint attempts reached. Please use password.");
-
- if (pam.state === "fail") {
- if (pam.fprint.available)
- return qsTr("Incorrect password. Please try again or use fingerprint.");
- return qsTr("Incorrect password. Please try again.");
- }
- if (pam.fprintState === "fail")
- return qsTr("Fingerprint not recognized (%1/%2). Please try again or use password.").arg(pam.fprint.tries).arg(Config.lock.maxFprintTries);
-
- return "";
- }
-
- anchors.left: parent.left
- anchors.right: parent.right
-
- scale: 0.7
- opacity: 0
- color: Colours.palette.m3error
-
- font.pointSize: Appearance.font.size.small
- font.family: Appearance.font.family.mono
- horizontalAlignment: Qt.AlignHCenter
- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
-
- onMsgChanged: {
- if (msg) {
- if (opacity > 0) {
- animate = true;
- text = msg;
- animate = false;
-
- exitAnim.stop();
- if (scale < 1)
- appearAnim.restart();
- else
- flashAnim.restart();
- } else {
- text = msg;
- exitAnim.stop();
- appearAnim.restart();
- }
- } else {
- appearAnim.stop();
- flashAnim.stop();
- exitAnim.start();
- }
- }
-
- Connections {
- target: root.lock.pam
-
- function onFlashMsg(): void {
- exitAnim.stop();
- if (message.scale < 1)
- appearAnim.restart();
- else
- flashAnim.restart();
- }
- }
-
- Anim {
- id: appearAnim
-
- target: message
- properties: "scale,opacity"
- to: 1
- onFinished: flashAnim.restart()
- }
-
- SequentialAnimation {
- id: flashAnim
-
- loops: 2
-
- FlashAnim {
- to: 0.3
- }
- FlashAnim {
- to: 1
- }
- }
-
- ParallelAnimation {
- id: exitAnim
-
- Anim {
- target: message
- property: "scale"
- to: 0.7
- duration: Appearance.anim.durations.large
- }
- Anim {
- target: message
- property: "opacity"
- to: 0
- duration: Appearance.anim.durations.large
- }
- }
- }
- }
-
- component FlashAnim: NumberAnimation {
- target: message
- property: "opacity"
- duration: Appearance.anim.durations.small
- easing.type: Easing.Linear
- }
-}
diff --git a/modules/lock/Content.qml b/modules/lock/Content.qml
deleted file mode 100644
index daa87ac..0000000
--- a/modules/lock/Content.qml
+++ /dev/null
@@ -1,27 +0,0 @@
-import qs.components
-import qs.services
-import qs.config
-import QtQuick
-import QtQuick.Layouts
-
-RowLayout {
- id: root
-
- required property var lock
-
- spacing: Appearance.spacing.large * 2
-
- ColumnLayout {
- Layout.fillWidth: true
- spacing: Appearance.spacing.normal
- }
-
- Center {
- lock: root.lock
- }
-
- ColumnLayout {
- Layout.fillWidth: true
- spacing: Appearance.spacing.normal
- }
-}
diff --git a/modules/lock/InputField.qml b/modules/lock/InputField.qml
deleted file mode 100644
index 1acc787..0000000
--- a/modules/lock/InputField.qml
+++ /dev/null
@@ -1,149 +0,0 @@
-pragma ComponentBehavior: Bound
-
-import qs.components
-import qs.services
-import qs.config
-import Quickshell
-import QtQuick
-import QtQuick.Layouts
-
-Item {
- id: root
-
- required property Pam pam
- readonly property alias placeholder: placeholder
- property string buffer
-
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- clip: true
-
- Connections {
- target: root.pam
-
- function onBufferChanged(): void {
- if (root.pam.buffer.length > root.buffer.length) {
- charList.bindImWidth();
- } else if (root.pam.buffer.length === 0) {
- charList.implicitWidth = charList.implicitWidth;
- placeholder.animate = true;
- }
-
- root.buffer = root.pam.buffer;
- }
- }
-
- StyledText {
- id: placeholder
-
- anchors.centerIn: parent
-
- text: {
- if (root.pam.passwd.active)
- return qsTr("Loading...");
- if (root.pam.state === "max")
- return qsTr("You have reached the maximum number of tries");
- return qsTr("Enter your password");
- }
-
- animate: true
- color: root.pam.passwd.active ? Colours.palette.m3secondary : Colours.palette.m3outline
- font.pointSize: Appearance.font.size.normal
- font.family: Appearance.font.family.mono
-
- opacity: root.buffer ? 0 : 1
-
- Behavior on opacity {
- Anim {}
- }
- }
-
- ListView {
- id: charList
-
- readonly property int fullWidth: count * (implicitHeight + spacing) - spacing
-
- function bindImWidth(): void {
- imWidthBehavior.enabled = false;
- implicitWidth = Qt.binding(() => fullWidth);
- imWidthBehavior.enabled = true;
- }
-
- anchors.centerIn: parent
- anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
-
- implicitWidth: fullWidth
- implicitHeight: Appearance.font.size.normal
-
- orientation: Qt.Horizontal
- spacing: Appearance.spacing.small / 2
- interactive: false
-
- model: ScriptModel {
- values: root.buffer.split("")
- }
-
- delegate: StyledRect {
- id: ch
-
- implicitWidth: implicitHeight
- implicitHeight: charList.implicitHeight
-
- color: Colours.palette.m3onSurface
- radius: Appearance.rounding.full
-
- opacity: 0
- scale: 0
- Component.onCompleted: {
- opacity = 1;
- scale = 1;
- }
- ListView.onRemove: removeAnim.start()
-
- SequentialAnimation {
- id: removeAnim
-
- PropertyAction {
- target: ch
- property: "ListView.delayRemove"
- value: true
- }
- ParallelAnimation {
- Anim {
- target: ch
- property: "opacity"
- to: 0
- }
- Anim {
- target: ch
- property: "scale"
- to: 0.5
- }
- }
- PropertyAction {
- target: ch
- property: "ListView.delayRemove"
- value: false
- }
- }
-
- Behavior on opacity {
- Anim {}
- }
-
- Behavior on scale {
- Anim {
- duration: Appearance.anim.durations.expressiveFastSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
- }
- }
- }
-
- Behavior on implicitWidth {
- id: imWidthBehavior
-
- Anim {}
- }
- }
-}
diff --git a/modules/lock/Lock.qml b/modules/lock/Lock.qml
deleted file mode 100644
index 6fd5277..0000000
--- a/modules/lock/Lock.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-pragma ComponentBehavior: Bound
-
-import qs.components.misc
-import Quickshell
-import Quickshell.Io
-import Quickshell.Wayland
-
-Scope {
- property alias lock: lock
-
- WlSessionLock {
- id: lock
-
- signal unlock
-
- LockSurface {
- lock: lock
- pam: pam
- }
- }
-
- Pam {
- id: pam
-
- lock: lock
- }
-
- CustomShortcut {
- name: "lock"
- description: "Lock the current session"
- onPressed: lock.locked = true
- }
-
- CustomShortcut {
- name: "unlock"
- description: "Unlock the current session"
- onPressed: lock.unlock()
- }
-
- IpcHandler {
- target: "lock"
-
- function lock(): void {
- lock.locked = true;
- }
-
- function unlock(): void {
- lock.unlock();
- }
-
- function isLocked(): bool {
- return lock.locked;
- }
- }
-}
diff --git a/modules/lock/LockSurface.qml b/modules/lock/LockSurface.qml
deleted file mode 100644
index 0c3ce69..0000000
--- a/modules/lock/LockSurface.qml
+++ /dev/null
@@ -1,231 +0,0 @@
-pragma ComponentBehavior: Bound
-
-import qs.components
-import qs.services
-import qs.config
-import qs.utils
-import Quickshell.Wayland
-import QtQuick
-import QtQuick.Effects
-
-WlSessionLockSurface {
- id: root
-
- required property WlSessionLock lock
- required property Pam pam
-
- readonly property alias unlocking: unlockAnim.running
-
- color: "transparent"
-
- Connections {
- target: root.lock
-
- function onUnlock(): void {
- unlockAnim.start();
- }
- }
-
- SequentialAnimation {
- id: unlockAnim
-
- ParallelAnimation {
- Anim {
- target: lockContent
- properties: "implicitWidth,implicitHeight"
- to: lockContent.size
- duration: Appearance.anim.durations.expressiveDefaultSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
- }
- Anim {
- target: lockBg
- property: "radius"
- to: lockContent.radius
- }
- Anim {
- target: content
- property: "scale"
- to: 0
- duration: Appearance.anim.durations.expressiveDefaultSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
- }
- Anim {
- target: content
- property: "opacity"
- to: 0
- duration: Appearance.anim.durations.small
- }
- Anim {
- target: lockIcon
- property: "opacity"
- to: 1
- duration: Appearance.anim.durations.large
- }
- Anim {
- target: background
- property: "opacity"
- to: 0
- duration: Appearance.anim.durations.large
- }
- SequentialAnimation {
- PauseAnimation {
- duration: Appearance.anim.durations.small
- }
- Anim {
- target: lockContent
- property: "opacity"
- to: 0
- }
- }
- }
- PropertyAction {
- target: root.lock
- property: "locked"
- value: false
- }
- }
-
- ParallelAnimation {
- id: initAnim
-
- running: true
-
- Anim {
- target: background
- property: "opacity"
- to: 1
- duration: Appearance.anim.durations.large
- }
- SequentialAnimation {
- ParallelAnimation {
- Anim {
- target: lockContent
- property: "scale"
- to: 1
- duration: Appearance.anim.durations.expressiveFastSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
- }
- Anim {
- target: lockContent
- property: "rotation"
- to: 360
- duration: Appearance.anim.durations.expressiveFastSpatial
- easing.bezierCurve: Appearance.anim.curves.standardAccel
- }
- }
- ParallelAnimation {
- Anim {
- target: lockIcon
- property: "rotation"
- to: 360
- easing.bezierCurve: Appearance.anim.curves.standardDecel
- }
- Anim {
- target: lockIcon
- property: "opacity"
- to: 0
- }
- Anim {
- target: content
- property: "opacity"
- to: 1
- }
- Anim {
- target: content
- property: "scale"
- to: 1
- duration: Appearance.anim.durations.expressiveDefaultSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
- }
- Anim {
- target: lockBg
- property: "radius"
- to: Appearance.rounding.large * 1.5
- }
- Anim {
- target: lockContent
- property: "implicitWidth"
- to: root.screen?.height * Config.lock.sizes.heightMult * Config.lock.sizes.ratio
- duration: Appearance.anim.durations.expressiveDefaultSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
- }
- Anim {
- target: lockContent
- property: "implicitHeight"
- to: root.screen?.height * Config.lock.sizes.heightMult
- duration: Appearance.anim.durations.expressiveDefaultSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
- }
- }
- }
- }
-
- Image {
- id: background
-
- anchors.fill: parent
- source: Paths.wallpaper ?? ""
- opacity: 1
-
- layer.enabled: true
- layer.effect: MultiEffect {
- autoPaddingEnabled: false
- blurEnabled: true
- blur: 1
- blurMax: 64
- blurMultiplier: 1
- }
- }
-
- Item {
- id: lockContent
-
- readonly property int size: lockIcon.implicitHeight + Appearance.padding.large * 4
- readonly property int radius: size / 4 * Appearance.rounding.scale
-
- anchors.centerIn: parent
- implicitWidth: size
- implicitHeight: size
-
- rotation: 180
- scale: 0
-
- StyledRect {
- id: lockBg
-
- anchors.fill: parent
- color: Colours.palette.m3surface
- radius: parent.radius
- opacity: Colours.transparency.enabled ? Colours.transparency.base : 1
-
- layer.enabled: true
- layer.effect: MultiEffect {
- shadowEnabled: true
- blurMax: 15
- shadowColor: Qt.alpha(Colours.palette.m3shadow, 0.7)
- }
- }
-
- MaterialIcon {
- id: lockIcon
-
- anchors.centerIn: parent
- text: "lock"
- font.pointSize: Appearance.font.size.extraLarge * 4
- font.bold: true
- rotation: 180
- }
-
- Content {
- id: content
-
- anchors.centerIn: parent
- width: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio - Appearance.padding.large * 2
- height: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult - Appearance.padding.large * 2
-
- lock: root
- opacity: 0
- scale: 0
- }
- }
-}
diff --git a/modules/lock/Pam.qml b/modules/lock/Pam.qml
deleted file mode 100644
index 0186c2f..0000000
--- a/modules/lock/Pam.qml
+++ /dev/null
@@ -1,193 +0,0 @@
-import qs.config
-import Quickshell
-import Quickshell.Io
-import Quickshell.Wayland
-import Quickshell.Services.Pam
-import QtQuick
-
-Scope {
- id: root
-
- required property WlSessionLock lock
-
- readonly property alias passwd: passwd
- readonly property alias fprint: fprint
- property string lockMessage
- property string state
- property string fprintState
- property string buffer
-
- signal flashMsg
-
- function handleKey(event: KeyEvent): void {
- if (passwd.active || state === "max")
- return;
-
- if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
- passwd.start();
- } else if (event.key === Qt.Key_Backspace) {
- if (event.modifiers & Qt.ControlModifier) {
- buffer = "";
- } else {
- buffer = buffer.slice(0, -1);
- }
- } else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
- // No illegal characters (you are insane if you use unicode in your password)
- buffer += event.text;
- }
- }
-
- PamContext {
- id: passwd
-
- config: "passwd"
- configDirectory: Quickshell.shellDir + "/assets/pam.d"
-
- onMessageChanged: {
- if (message.startsWith("The account is locked"))
- root.lockMessage = message;
- else if (root.lockMessage && message.endsWith(" left to unlock)"))
- root.lockMessage += "\n" + message;
- }
-
- onResponseRequiredChanged: {
- if (!responseRequired)
- return;
-
- respond(root.buffer);
- root.buffer = "";
- }
-
- onCompleted: res => {
- if (res === PamResult.Success)
- return root.lock.unlock();
-
- if (res === PamResult.Error)
- root.state = "error";
- else if (res === PamResult.MaxTries)
- root.state = "max";
- else if (res === PamResult.Failed)
- root.state = "fail";
-
- root.flashMsg();
- stateReset.restart();
- }
- }
-
- PamContext {
- id: fprint
-
- property bool available
- property int tries
- property int errorTries
-
- function checkAvail(): void {
- if (!available || !Config.lock.enableFprint || !root.lock.secure) {
- abort();
- return;
- }
-
- tries = 0;
- errorTries = 0;
- start();
- }
-
- config: "fprint"
- configDirectory: Quickshell.shellDir + "/assets/pam.d"
-
- onCompleted: res => {
- if (!available)
- return;
-
- if (res === PamResult.Success)
- return root.lock.unlock();
-
- if (res === PamResult.Error) {
- root.fprintState = "error";
- errorTries++;
- if (errorTries < 5) {
- abort();
- errorRetry.restart();
- }
- } else if (res === PamResult.MaxTries) {
- // Isn't actually the real max tries as pam only reports completed
- // when max tries is reached.
- tries++;
- if (tries < Config.lock.maxFprintTries) {
- // Restart if not actually real max tries
- root.fprintState = "fail";
- start();
- } else {
- root.fprintState = "max";
- abort();
- }
- }
-
- root.flashMsg();
- fprintStateReset.start();
- }
- }
-
- Process {
- id: availProc
-
- command: ["sh", "-c", "fprintd-list $USER"]
- onExited: code => {
- fprint.available = code === 0;
- fprint.checkAvail();
- }
- }
-
- Timer {
- id: errorRetry
-
- interval: 800
- onTriggered: fprint.start()
- }
-
- Timer {
- id: stateReset
-
- interval: 4000
- onTriggered: {
- if (root.state !== "max")
- root.state = "";
- }
- }
-
- Timer {
- id: fprintStateReset
-
- interval: 4000
- onTriggered: {
- root.fprintState = "";
- fprint.errorTries = 0;
- }
- }
-
- Connections {
- target: root.lock
-
- function onSecureChanged(): void {
- if (root.lock.secure) {
- availProc.running = true;
- root.buffer = "";
- root.state = "";
- root.fprintState = "";
- root.lockMessage = "";
- }
- }
-
- function onUnlock(): void {
- fprint.abort();
- }
- }
-
- Connections {
- target: Config.lock
-
- function onEnableFprintChanged(): void {
- fprint.checkAvail();
- }
- }
-}
diff --git a/modules/notifications/Notification.qml b/modules/notifications/Notification.qml
index b5376cf..8c2d3ec 100644
--- a/modules/notifications/Notification.qml
+++ b/modules/notifications/Notification.qml
@@ -108,7 +108,6 @@ StyledRect {
id: image
active: root.hasImage
- asynchronous: true
anchors.left: parent.left
anchors.top: parent.top
@@ -135,7 +134,6 @@ StyledRect {
id: appIcon
active: root.hasAppIcon || !root.hasImage
- asynchronous: true
anchors.horizontalCenter: root.hasImage ? undefined : image.horizontalCenter
anchors.verticalCenter: root.hasImage ? undefined : image.verticalCenter
@@ -152,7 +150,6 @@ StyledRect {
id: icon
active: root.hasAppIcon
- asynchronous: true
anchors.centerIn: parent
@@ -169,7 +166,6 @@ StyledRect {
Loader {
active: !root.hasAppIcon
- asynchronous: true
anchors.centerIn: parent
anchors.horizontalCenterOffset: -Appearance.font.size.large * 0.02
anchors.verticalCenterOffset: Appearance.font.size.large * 0.02
diff --git a/modules/osd/Content.qml b/modules/osd/Content.qml
index 619810f..622f2d7 100644
--- a/modules/osd/Content.qml
+++ b/modules/osd/Content.qml
@@ -112,7 +112,6 @@ Item {
Layout.preferredHeight: shouldBeActive ? Config.osd.sizes.sliderHeight : 0
opacity: shouldBeActive ? 1 : 0
active: opacity > 0
- asynchronous: true
visible: active
Behavior on Layout.preferredHeight {
diff --git a/modules/sidebar/NotifDock.qml b/modules/sidebar/NotifDock.qml
index b915ed9..d039d15 100644
--- a/modules/sidebar/NotifDock.qml
+++ b/modules/sidebar/NotifDock.qml
@@ -87,7 +87,6 @@ Item {
Loader {
anchors.centerIn: parent
- asynchronous: true
active: opacity > 0
opacity: root.notifCount > 0 ? 0 : 1
diff --git a/modules/sidebar/NotifGroup.qml b/modules/sidebar/NotifGroup.qml
index c230a5c..16aac33 100644
--- a/modules/sidebar/NotifGroup.qml
+++ b/modules/sidebar/NotifGroup.qml
@@ -109,7 +109,6 @@ StyledRect {
Loader {
anchors.centerIn: parent
- asynchronous: true
sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
}
}
@@ -117,7 +116,6 @@ StyledRect {
Loader {
anchors.right: parent.right
anchors.bottom: parent.bottom
- asynchronous: true
active: root.appIcon && root.image
sourceComponent: StyledRect {
diff --git a/modules/utilities/Content.qml b/modules/utilities/Content.qml
index f6d8d63..770a774 100644
--- a/modules/utilities/Content.qml
+++ b/modules/utilities/Content.qml
@@ -19,8 +19,6 @@ Item {
anchors.fill: parent
spacing: Appearance.spacing.normal
- IdleInhibit {}
-
Toggles {
visibilities: root.visibilities
popouts: root.popouts
diff --git a/modules/utilities/cards/IdleInhibit.qml b/modules/utilities/cards/IdleInhibit.qml
deleted file mode 100644
index 0344e3a..0000000
--- a/modules/utilities/cards/IdleInhibit.qml
+++ /dev/null
@@ -1,125 +0,0 @@
-import qs.components
-import qs.components.controls
-import qs.services
-import qs.config
-import QtQuick
-import QtQuick.Layouts
-
-StyledRect {
- id: root
-
- Layout.fillWidth: true
- implicitHeight: layout.implicitHeight + (IdleInhibitor.enabled ? activeChip.implicitHeight + activeChip.anchors.topMargin : 0) + Appearance.padding.large * 2
-
- radius: Appearance.rounding.normal
- color: Colours.tPalette.m3surfaceContainer
- clip: true
-
- RowLayout {
- id: layout
-
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.margins: Appearance.padding.large
- spacing: Appearance.spacing.normal
-
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: icon.implicitHeight + Appearance.padding.smaller * 2
-
- radius: Appearance.rounding.full
- color: IdleInhibitor.enabled ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
-
- MaterialIcon {
- id: icon
-
- anchors.centerIn: parent
- text: "coffee"
- color: IdleInhibitor.enabled ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
- font.pointSize: Appearance.font.size.large
- }
- }
-
- ColumnLayout {
- Layout.fillWidth: true
- spacing: 0
-
- StyledText {
- Layout.fillWidth: true
- text: qsTr("Keep Awake")
- font.pointSize: Appearance.font.size.normal
- elide: Text.ElideRight
- }
-
- StyledText {
- Layout.fillWidth: true
- text: IdleInhibitor.enabled ? qsTr("Preventing sleep mode") : qsTr("Normal power management")
- color: Colours.palette.m3onSurfaceVariant
- font.pointSize: Appearance.font.size.small
- elide: Text.ElideRight
- }
- }
-
- StyledSwitch {
- checked: IdleInhibitor.enabled
- onToggled: IdleInhibitor.enabled = checked
- }
- }
-
- Loader {
- id: activeChip
-
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.topMargin: Appearance.spacing.larger
- anchors.bottomMargin: IdleInhibitor.enabled ? Appearance.padding.large : -implicitHeight
- anchors.leftMargin: Appearance.padding.large
-
- opacity: IdleInhibitor.enabled ? 1 : 0
- scale: IdleInhibitor.enabled ? 1 : 0.5
-
- Component.onCompleted: active = Qt.binding(() => opacity > 0)
-
- sourceComponent: StyledRect {
- implicitWidth: activeText.implicitWidth + Appearance.padding.normal * 2
- implicitHeight: activeText.implicitHeight + Appearance.padding.small * 2
-
- radius: Appearance.rounding.full
- color: Colours.palette.m3primary
-
- StyledText {
- id: activeText
-
- anchors.centerIn: parent
- text: qsTr("Active since %1").arg(Qt.formatTime(IdleInhibitor.enabledSince, Config.services.useTwelveHourClock ? "hh:mm a" : "hh:mm"))
- color: Colours.palette.m3onPrimary
- font.pointSize: Math.round(Appearance.font.size.small * 0.9)
- }
- }
-
- Behavior on anchors.bottomMargin {
- Anim {
- duration: Appearance.anim.durations.expressiveDefaultSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
- }
- }
-
- Behavior on opacity {
- Anim {
- duration: Appearance.anim.durations.small
- }
- }
-
- Behavior on scale {
- Anim {}
- }
- }
-
- Behavior on implicitHeight {
- Anim {
- duration: Appearance.anim.durations.expressiveDefaultSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
- }
- }
-}
diff --git a/modules/utilities/cards/Toggles.qml b/modules/utilities/cards/Toggles.qml
index e62d3c7..558f2d5 100644
--- a/modules/utilities/cards/Toggles.qml
+++ b/modules/utilities/cards/Toggles.qml
@@ -82,7 +82,7 @@ StyledRect {
icon: "vpn_key"
checked: VPN.connected
enabled: !VPN.connecting
- visible: VPN.enabled
+ visible: Config.utilities.vpn.provider.some(p => typeof p === "object" ? (p.enabled === true) : false)
onClicked: VPN.toggle()
}
diff --git a/modules/windowinfo/Buttons.qml b/modules/windowinfo/Buttons.qml
index d7ef095..89acfe6 100644
--- a/modules/windowinfo/Buttons.qml
+++ b/modules/windowinfo/Buttons.qml
@@ -118,7 +118,6 @@ ColumnLayout {
Loader {
active: root.client?.lastIpcObject.floating
- asynchronous: true
Layout.fillWidth: active
Layout.leftMargin: active ? 0 : -parent.spacing
Layout.rightMargin: active ? 0 : -parent.spacing
diff --git a/modules/windowinfo/Preview.qml b/modules/windowinfo/Preview.qml
index d2c5682..4cc0aab 100644
--- a/modules/windowinfo/Preview.qml
+++ b/modules/windowinfo/Preview.qml
@@ -35,7 +35,6 @@ Item {
Loader {
anchors.centerIn: parent
active: !root.client
- asynchronous: true
sourceComponent: ColumnLayout {
spacing: 0