1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-02 03:28:28 -04:00

qs: large sweep of dead code removals

This commit is contained in:
bbedward
2026-07-26 23:22:22 -04:00
parent bab2078dfd
commit c67b185076
113 changed files with 1467 additions and 3254 deletions
+2 -25
View File
@@ -25,29 +25,6 @@ Item {
property bool effectActive: false
property bool useNextForEffect: false
function getFillMode(modeName) {
switch (modeName) {
case "Stretch":
return Image.Stretch;
case "Fit":
case "PreserveAspectFit":
return Image.PreserveAspectFit;
case "Fill":
case "PreserveAspectCrop":
return Image.PreserveAspectCrop;
case "Tile":
return Image.Tile;
case "TileVertically":
return Image.TileVertically;
case "TileHorizontally":
return Image.TileHorizontally;
case "Pad":
return Image.Pad;
default:
return Image.PreserveAspectCrop;
}
}
Component.onCompleted: {
if (initialSource && initialSource !== wallpaperSource && !(CompositorService.isNiri && SessionData.isSwitchingMode)) {
currentWallpaper.source = initialSource;
@@ -144,7 +121,7 @@ Item {
smooth: true
cache: true
sourceSize: root.blurTextureSize
fillMode: root.getFillMode(SessionData.getMonitorWallpaperFillMode(root.screenName))
fillMode: Theme.getFillMode(SessionData.getMonitorWallpaperFillMode(root.screenName))
onStatusChanged: {
if (status === Image.Error) {
@@ -166,7 +143,7 @@ Item {
smooth: true
cache: true
sourceSize: root.blurTextureSize
fillMode: root.getFillMode(SessionData.getMonitorWallpaperFillMode(root.screenName))
fillMode: Theme.getFillMode(SessionData.getMonitorWallpaperFillMode(root.screenName))
onStatusChanged: {
if (status === Image.Error) {
@@ -4,6 +4,7 @@ import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
import "../../Common/Format.js" as Format
Item {
id: root
@@ -188,16 +189,6 @@ Item {
return DgopService.availableGpus.find(g => g.pciId === selectedGpuPciId);
}
function formatBytes(bytes) {
if (bytes < 1024)
return bytes.toFixed(0) + "B";
if (bytes < 1024 * 1024)
return (bytes / 1024).toFixed(0) + "K";
if (bytes < 1024 * 1024 * 1024)
return (bytes / (1024 * 1024)).toFixed(1) + "M";
return (bytes / (1024 * 1024 * 1024)).toFixed(1) + "G";
}
function formatMemKB(kb) {
if (kb < 1024)
return kb.toFixed(0) + "K";
@@ -206,26 +197,18 @@ Item {
return (kb / (1024 * 1024)).toFixed(1) + "G";
}
function addToHistory(arr, val) {
var newArr = arr.slice();
newArr.push(val);
if (newArr.length > historySize)
newArr.shift();
return newArr;
}
function sampleData() {
if (showCpuGraph)
cpuHistory = addToHistory(cpuHistory, DgopService.cpuUsage);
cpuHistory = Format.addToHistory(cpuHistory, DgopService.cpuUsage, historySize);
if (showMemoryGraph)
memHistory = addToHistory(memHistory, DgopService.memoryUsage);
memHistory = Format.addToHistory(memHistory, DgopService.memoryUsage, historySize);
if (showNetworkGraph) {
netRxHistory = addToHistory(netRxHistory, DgopService.networkRxRate);
netTxHistory = addToHistory(netTxHistory, DgopService.networkTxRate);
netRxHistory = Format.addToHistory(netRxHistory, DgopService.networkRxRate, historySize);
netTxHistory = Format.addToHistory(netTxHistory, DgopService.networkTxRate, historySize);
}
if (showDisk) {
diskReadHistory = addToHistory(diskReadHistory, DgopService.diskReadRate);
diskWriteHistory = addToHistory(diskWriteHistory, DgopService.diskWriteRate);
diskReadHistory = Format.addToHistory(diskReadHistory, DgopService.diskReadRate, historySize);
diskWriteHistory = Format.addToHistory(diskWriteHistory, DgopService.diskWriteRate, historySize);
}
}
@@ -519,7 +502,7 @@ Item {
color: root.accentColor
}
StyledText {
text: root.formatBytes(DgopService.networkRxRate) + "/s"
text: Format.formatBytes(DgopService.networkRxRate) + "/s"
isMonospace: true
font.pixelSize: Theme.fontSizeMedium
color: root.textColor
@@ -533,7 +516,7 @@ Item {
color: root.dimColor
}
StyledText {
text: root.formatBytes(DgopService.networkTxRate) + "/s"
text: Format.formatBytes(DgopService.networkTxRate) + "/s"
isMonospace: true
font.pixelSize: Theme.fontSizeMedium
color: root.textColor
@@ -552,7 +535,7 @@ Item {
color: root.accentColor
}
StyledText {
text: root.formatBytes(DgopService.diskReadRate) + "/s"
text: Format.formatBytes(DgopService.diskReadRate) + "/s"
isMonospace: true
font.pixelSize: Theme.fontSizeMedium
color: root.textColor
@@ -566,7 +549,7 @@ Item {
color: root.dimColor
}
StyledText {
text: root.formatBytes(DgopService.diskWriteRate) + "/s"
text: Format.formatBytes(DgopService.diskWriteRate) + "/s"
isMonospace: true
font.pixelSize: Theme.fontSizeMedium
color: root.textColor
@@ -1,7 +1,7 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.ControlCenter.Details
import qs.Modules.Plugins
PluginComponent {
@@ -1,131 +0,0 @@
import QtQuick
import qs.Common
import qs.Widgets
Rectangle {
id: root
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property string iconName: ""
property string text: ""
property string secondaryText: ""
property bool isActive: false
property int widgetIndex: 0
property var widgetData: null
property bool editMode: false
signal clicked
width: parent ? parent.width : 200
height: 60
radius: {
if (Theme.cornerRadius === 0)
return 0;
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4;
}
readonly property color _tileBgActive: Theme.ccTileActiveBg
readonly property color _tileBgInactive: Theme.ccPillInactiveBg
readonly property color _tileRingActive: Theme.ccTileRing
color: isActive ? _tileBgActive : _tileBgInactive
border.color: isActive ? _tileRingActive : Theme.outlineMedium
border.width: isActive ? 1 : Theme.layerOutlineWidth
opacity: enabled ? 1.0 : 0.6
function hoverTint(base) {
const factor = 1.2;
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
}
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: mouseArea.containsMouse ? hoverTint(root.color) : Theme.withAlpha(hoverTint(root.color), 0)
opacity: mouseArea.containsMouse ? 0.08 : 0.0
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
}
}
}
Row {
anchors.fill: parent
anchors.leftMargin: Theme.spacingL + 2
anchors.rightMargin: Theme.spacingM
spacing: Theme.spacingM
DankIcon {
name: root.iconName
size: Theme.iconSize
color: isActive ? Theme.ccTileActiveText : Theme.ccTileInactiveIcon
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: parent.width - Theme.iconSize - parent.spacing
height: parent.height
Column {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
Typography {
width: parent.width
text: root.text
style: Typography.Style.Body
color: isActive ? Theme.ccTileActiveText : Theme.surfaceText
elide: Text.ElideRight
wrapMode: Text.NoWrap
horizontalAlignment: Text.AlignLeft
}
Typography {
width: parent.width
text: root.secondaryText
style: Typography.Style.Caption
color: isActive ? Theme.ccTileActiveText : Theme.surfaceVariantText
visible: text.length > 0
elide: Text.ElideRight
wrapMode: Text.NoWrap
horizontalAlignment: Text.AlignLeft
}
}
}
}
DankRipple {
id: ripple
cornerRadius: root.radius
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: root.enabled
onPressed: mouse => ripple.trigger(mouse.x, mouse.y)
onClicked: root.clicked()
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on radius {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
@@ -1,91 +0,0 @@
import QtQuick
import qs.Common
import qs.Modules.ControlCenter.Details
Item {
id: root
property string expandedSection: ""
property var expandedWidgetData: null
height: active ? 250 : 0
visible: active
readonly property bool active: expandedSection !== ""
Loader {
anchors.fill: parent
anchors.topMargin: Theme.spacingS
sourceComponent: {
if (!root.active)
return null;
if (expandedSection.startsWith("diskUsage_")) {
return diskUsageDetailComponent;
}
switch (expandedSection) {
case "wifi":
return networkDetailComponent;
case "bluetooth":
return bluetoothDetailComponent;
case "audioOutput":
return audioOutputDetailComponent;
case "audioInput":
return audioInputDetailComponent;
case "battery":
return batteryDetailComponent;
default:
return null;
}
}
}
Component {
id: networkDetailComponent
NetworkDetail {}
}
Component {
id: bluetoothDetailComponent
BluetoothDetail {}
}
Component {
id: audioOutputDetailComponent
AudioOutputDetail {}
}
Component {
id: audioInputDetailComponent
AudioInputDetail {}
}
Component {
id: batteryDetailComponent
BatteryDetail {}
}
Component {
id: diskUsageDetailComponent
DiskUsageDetail {
currentMountPath: root.expandedWidgetData?.mountPath || "/"
instanceId: root.expandedWidgetData?.instanceId || ""
onMountPathChanged: newMountPath => {
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
const widgets = SettingsData.controlCenterWidgets || [];
const newWidgets = widgets.map(w => {
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
const updatedWidget = Object.assign({}, w);
updatedWidget.mountPath = newMountPath;
return updatedWidget;
}
return w;
});
SettingsData.set("controlCenterWidgets", newWidgets);
}
}
}
}
}
@@ -1,55 +0,0 @@
import QtQuick
import qs.Common
import qs.Widgets
Rectangle {
id: root
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property string iconName: ""
property string text: ""
signal pressed
height: 34
radius: Theme.cornerRadius
color: mouseArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.surfaceVariant, 0.5)
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: root.iconName
size: Theme.fontSizeSmall
color: mouseArea.containsMouse ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Typography {
text: root.text
style: Typography.Style.Button
color: mouseArea.containsMouse ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
DankRipple {
id: ripple
cornerRadius: root.radius
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => {
ripple.trigger(mouse.x, mouse.y);
root.pressed();
}
}
}
@@ -4,6 +4,7 @@ import Quickshell.Services.Pipewire
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
Rectangle {
id: root
@@ -150,17 +151,9 @@ Rectangle {
property int maxPinnedInputs: 3
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v);
if (typeof value === "string" && value.length > 0)
return [value];
return [];
}
function getPinnedInputs() {
const pins = CacheData.audioInputDevicePins || {};
return normalizePinList(pins["preferredInput"]);
return QmlUtils.normalizePinList(pins["preferredInput"]);
}
Column {
@@ -316,7 +309,7 @@ Rectangle {
onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y)
onClicked: {
const pins = JSON.parse(JSON.stringify(CacheData.audioInputDevicePins || {}));
let pinnedList = audioContent.normalizePinList(pins["preferredInput"]);
let pinnedList = QmlUtils.normalizePinList(pins["preferredInput"]);
const pinIndex = pinnedList.indexOf(modelData.name);
if (pinIndex !== -1) {
@@ -4,6 +4,7 @@ import Quickshell.Services.Pipewire
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
Rectangle {
id: root
@@ -160,17 +161,9 @@ Rectangle {
property int maxPinnedOutputs: 3
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v);
if (typeof value === "string" && value.length > 0)
return [value];
return [];
}
function getPinnedOutputs() {
const pins = CacheData.audioOutputDevicePins || {};
return normalizePinList(pins["preferredOutput"]);
return QmlUtils.normalizePinList(pins["preferredOutput"]);
}
Column {
@@ -325,7 +318,7 @@ Rectangle {
onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y)
onClicked: {
const pins = JSON.parse(JSON.stringify(CacheData.audioOutputDevicePins || {}));
let pinnedList = audioContent.normalizePinList(pins["preferredOutput"]);
let pinnedList = QmlUtils.normalizePinList(pins["preferredOutput"]);
const pinIndex = pinnedList.indexOf(modelData.name);
if (pinIndex !== -1) {
@@ -5,6 +5,7 @@ import Quickshell.Bluetooth
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
Rectangle {
id: root
@@ -95,17 +96,9 @@ Rectangle {
BluetoothService.updateDeviceCodec(deviceAddress, codecName);
}
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v);
if (typeof value === "string" && value.length > 0)
return [value];
return [];
}
function getPinnedDevices() {
const pins = CacheData.bluetoothDevicePins || {};
return normalizePinList(pins["preferredDevice"]);
return QmlUtils.normalizePinList(pins["preferredDevice"]);
}
Row {
@@ -396,7 +389,7 @@ Rectangle {
cursorShape: Qt.PointingHandCursor
onClicked: {
const pins = JSON.parse(JSON.stringify(CacheData.bluetoothDevicePins || {}));
let pinnedList = root.normalizePinList(pins["preferredDevice"]);
let pinnedList = QmlUtils.normalizePinList(pins["preferredDevice"]);
const pinIndex = pinnedList.indexOf(pairedDelegate.modelData.address);
if (pinIndex !== -1) {
@@ -1,6 +1,7 @@
import QtQuick
import qs.Common
import qs.Widgets
import "../../../Common/Format.js" as Format
Rectangle {
id: root
@@ -23,33 +24,8 @@ Rectangle {
onTriggered: root.nowMs = Date.now()
}
function _pad2(n) {
return n < 10 ? "0" + n : "" + n;
}
function formatUntil(ts) {
if (!ts)
return "";
const d = new Date(ts);
const use24h = (typeof SettingsData !== "undefined") ? SettingsData.use24HourClock : true;
if (use24h)
return _pad2(d.getHours()) + ":" + _pad2(d.getMinutes());
const suffix = d.getHours() >= 12 ? "PM" : "AM";
const h12 = ((d.getHours() + 11) % 12) + 1;
return h12 + ":" + _pad2(d.getMinutes()) + " " + suffix;
}
function formatRemaining(ms) {
if (ms <= 0)
return "";
const totalMinutes = Math.ceil(ms / 60000);
if (totalMinutes < 60)
return I18n.tr("%1 min left").arg(totalMinutes);
const hours = Math.floor(totalMinutes / 60);
const mins = totalMinutes - hours * 60;
if (mins === 0)
return I18n.tr("%1 h left").arg(hours);
return I18n.tr("%1 h %2 m left").arg(hours).arg(mins);
return Format.formatRemaining(ms, "", I18n.tr("%1 min left"), I18n.tr("%1 h left"), I18n.tr("%1 h %2 m left"));
}
function minutesUntilTomorrowMorning() {
@@ -125,7 +101,7 @@ Rectangle {
if (SessionData.doNotDisturbUntil <= 0)
return I18n.tr("On indefinitely");
const remaining = Math.max(0, SessionData.doNotDisturbUntil - root.nowMs);
return root.formatRemaining(remaining) + " · " + I18n.tr("until %1").arg(root.formatUntil(SessionData.doNotDisturbUntil));
return root.formatRemaining(remaining) + " · " + I18n.tr("until %1").arg(Format.formatUntil(SessionData.doNotDisturbUntil, SettingsData.use24HourClock));
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
@@ -7,6 +7,7 @@ import qs.Services
import qs.Widgets
import qs.Modals
import qs.Modals.Common
import "../../../Common/QmlUtils.js" as QmlUtils
Rectangle {
id: root
@@ -66,17 +67,9 @@ Rectangle {
PopoutService.openSettingsWithTab("network_wifi");
}
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v);
if (typeof value === "string" && value.length > 0)
return [value];
return [];
}
function getPinnedNetworks() {
const pins = CacheData.wifiNetworkPins || {};
return normalizePinList(pins["preferredWifi"]);
return QmlUtils.normalizePinList(pins["preferredWifi"]);
}
property int currentPreferenceIndex: {
@@ -908,7 +901,7 @@ Rectangle {
onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y)
onClicked: {
const pins = JSON.parse(JSON.stringify(CacheData.wifiNetworkPins || {}));
let pinnedList = root.normalizePinList(pins["preferredWifi"]);
let pinnedList = QmlUtils.normalizePinList(pins["preferredWifi"]);
const pinIndex = pinnedList.indexOf(modelData.ssid);
if (pinIndex !== -1) {
@@ -0,0 +1,237 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Common
import qs.Modals.Common
import qs.Modals.FileBrowser
import qs.Services
import qs.Widgets
Rectangle {
id: root
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property var parentPopout: null
property string expandedUuid: ""
property int listHeight: 180
implicitHeight: 32 + 1 + listHeight + Theme.spacingS * 4 + Theme.spacingM * 2
radius: Theme.cornerRadius
color: Theme.nestedSurface
border.color: Theme.outlineMedium
border.width: Theme.layerOutlineWidth
FileBrowserSurfaceModal {
id: fileBrowser
browserTitle: I18n.tr("Import VPN")
browserIcon: "vpn_key"
browserType: "vpn"
fileExtensions: VPNService.getFileFilter()
parentPopout: root.parentPopout
onFileSelected: path => {
VPNService.importVpn(path.replace("file://", ""));
}
}
ConfirmModal {
id: deleteConfirm
}
Column {
id: contentColumn
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingS
RowLayout {
spacing: Theme.spacingS
width: parent.width
StyledText {
text: {
if (!DMSNetworkService.connected)
return I18n.tr("Active: None");
const names = DMSNetworkService.activeNames || [];
if (names.length <= 1)
return I18n.tr("Active: %1").arg(names[0] || "VPN");
return I18n.tr("Active: %1 +%2").arg(names[0]).arg(names.length - 1);
}
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
wrapMode: Text.NoWrap
Layout.fillWidth: true
}
Rectangle {
height: 28
radius: 14
color: importArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceLight
width: 90
Layout.alignment: Qt.AlignVCenter
opacity: VPNService.importing ? 0.5 : 1.0
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: VPNService.importing ? "sync" : "add"
size: Theme.fontSizeSmall
color: Theme.primary
}
StyledText {
text: I18n.tr("Import")
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
font.weight: Font.Medium
}
}
MouseArea {
id: importArea
anchors.fill: parent
hoverEnabled: true
cursorShape: VPNService.importing ? Qt.BusyCursor : Qt.PointingHandCursor
enabled: !VPNService.importing
onClicked: fileBrowser.open()
}
}
Rectangle {
height: 28
radius: 14
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
visible: DMSNetworkService.connected
width: 100
Layout.alignment: Qt.AlignVCenter
opacity: DMSNetworkService.isBusy ? 0.5 : 1.0
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: "link_off"
size: Theme.fontSizeSmall
color: Theme.surfaceText
}
StyledText {
text: I18n.tr("Disconnect")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
}
}
MouseArea {
id: discAllArea
anchors.fill: parent
hoverEnabled: true
cursorShape: DMSNetworkService.isBusy ? Qt.BusyCursor : Qt.PointingHandCursor
enabled: !DMSNetworkService.isBusy
onClicked: DMSNetworkService.disconnectAllActive()
}
}
DankActionButton {
Layout.alignment: Qt.AlignVCenter
iconName: "settings"
buttonSize: 28
iconSize: 16
iconColor: Theme.surfaceVariantText
onClicked: {
PopoutService.closeControlCenter();
PopoutService.openSettingsWithTab("network_vpn");
}
}
}
Rectangle {
height: 1
width: parent.width
color: Theme.outlineStrong
}
Item {
width: parent.width
height: root.listHeight
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
visible: DMSNetworkService.profiles.length === 0
DankIcon {
name: "vpn_key_off"
size: 36
color: Theme.surfaceVariantText
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: I18n.tr("No VPN profiles")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: I18n.tr("Click Import to add a .ovpn or .conf")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.horizontalCenter: parent.horizontalCenter
}
}
DankListView {
id: vpnListView
anchors.fill: parent
visible: DMSNetworkService.profiles.length > 0
spacing: Theme.spacingXS
cacheBuffer: 200
clip: true
model: ScriptModel {
values: DMSNetworkService.profiles
objectProp: "uuid"
}
delegate: VpnProfileDelegate {
required property var modelData
width: vpnListView.width
profile: modelData
isExpanded: root.expandedUuid === modelData.uuid
onToggleExpand: {
if (root.expandedUuid === modelData.uuid) {
root.expandedUuid = "";
return;
}
root.expandedUuid = modelData.uuid;
VPNService.getConfig(modelData.uuid);
}
onDeleteRequested: {
deleteConfirm.showWithOptions({
"title": I18n.tr("Delete VPN"),
"message": I18n.tr("Delete \"%1\"?").arg(modelData.name),
"confirmText": I18n.tr("Delete"),
"confirmColor": Theme.error,
"onConfirm": () => VPNService.deleteVpn(modelData.uuid)
});
}
}
}
}
Item {
width: 1
height: Theme.spacingS
}
}
}
@@ -0,0 +1,331 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
required property var profile
property bool isExpanded: false
readonly property bool isTransient: !!profile?.transient
readonly property bool canExpand: profile?.canExpand !== false
readonly property bool canDelete: profile?.canDelete !== false
signal toggleExpand
signal deleteRequested
readonly property bool isActive: DMSNetworkService.vpnStateForUuid(profile?.uuid) === "activated"
readonly property bool isConnecting: DMSNetworkService.isVpnConnectingUuid(profile?.uuid)
readonly property bool hasError: !isConnecting && DMSNetworkService.vpnError !== "" && DMSNetworkService.vpnErrorUuid === (profile?.uuid ?? "")
readonly property bool isHovered: rowArea.containsMouse || expandBtn.containsMouse || deleteBtn.containsMouse
readonly property var configData: (!isTransient && isExpanded) ? VPNService.editConfig : null
readonly property var configFields: buildConfigFields()
height: isExpanded ? 46 + expandedContent.height : 46
radius: Theme.cornerRadius
color: isHovered ? Theme.primaryHoverLight : (isActive ? Theme.primaryPressed : Theme.surfaceLight)
border.width: isActive ? 2 : 1
border.color: isActive ? Theme.primary : Theme.outlineLight
opacity: (DMSNetworkService.isBusy && !isConnecting) ? 0.5 : 1.0
clip: true
function buildConfigFields() {
if (!configData)
return [];
const fields = [];
const data = configData.data || {};
if (data.remote)
fields.push({
"key": "server",
"label": I18n.tr("Server"),
"value": data.remote
});
if (configData.username || data.username)
fields.push({
"key": "user",
"label": I18n.tr("Username"),
"value": configData.username || data.username
});
if (data.cipher)
fields.push({
"key": "cipher",
"label": I18n.tr("Cipher"),
"value": data.cipher
});
if (data.auth)
fields.push({
"key": "auth",
"label": I18n.tr("Auth"),
"value": data.auth
});
if (data["proto-tcp"] === "yes" || data["proto-tcp"] === "no")
fields.push({
"key": "proto",
"label": I18n.tr("Protocol"),
"value": data["proto-tcp"] === "yes" ? "TCP" : "UDP"
});
if (data["tunnel-mtu"])
fields.push({
"key": "mtu",
"label": I18n.tr("MTU"),
"value": data["tunnel-mtu"]
});
if (data["connection-type"])
fields.push({
"key": "conntype",
"label": I18n.tr("Auth Type"),
"value": data["connection-type"]
});
return fields;
}
Behavior on height {
NumberAnimation {
duration: 150
easing.type: Easing.OutQuad
}
}
MouseArea {
id: rowArea
anchors.fill: parent
hoverEnabled: true
cursorShape: DMSNetworkService.isBusy ? Qt.BusyCursor : Qt.PointingHandCursor
enabled: !DMSNetworkService.isBusy
onClicked: DMSNetworkService.toggle(profile.uuid)
}
Column {
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: Theme.spacingS
Row {
width: parent.width
height: 46 - Theme.spacingS * 2
spacing: Theme.spacingS
DankSpinner {
size: 18
strokeWidth: 2
color: Theme.warning
running: root.isConnecting
visible: root.isConnecting
anchors.verticalCenter: parent.verticalCenter
}
DankIcon {
visible: !root.isConnecting
name: isActive ? "vpn_lock" : (root.hasError ? "error" : "vpn_key_off")
size: 20
color: root.hasError ? Theme.error : (isActive ? Theme.primary : Theme.surfaceText)
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 1
anchors.verticalCenter: parent.verticalCenter
width: parent.width - 20 - ((canExpand ? 28 : 0) + (canDelete ? 28 : 0)) - Theme.spacingS * 4
StyledText {
text: profile?.name ?? ""
font.pixelSize: Theme.fontSizeMedium
color: isActive ? Theme.primary : Theme.surfaceText
elide: Text.ElideRight
wrapMode: Text.NoWrap
width: parent.width
horizontalAlignment: Text.AlignLeft
}
StyledText {
text: root.isConnecting ? I18n.tr("Connecting...") : (root.hasError ? DMSNetworkService.vpnError : VPNService.getVpnTypeFromProfile(profile))
font.pixelSize: Theme.fontSizeSmall
color: root.isConnecting ? Theme.warning : (root.hasError ? Theme.error : Theme.surfaceTextMedium)
wrapMode: Text.NoWrap
width: parent.width
elide: Text.ElideRight
horizontalAlignment: Text.AlignLeft
}
}
Item {
width: Theme.spacingXS
height: 1
}
Rectangle {
width: 28
height: 28
radius: 14
color: expandBtn.containsMouse ? Theme.surfacePressed : Theme.withAlpha(Theme.surfacePressed, 0)
anchors.verticalCenter: parent.verticalCenter
visible: canExpand
DankIcon {
anchors.centerIn: parent
name: isExpanded ? "expand_less" : "expand_more"
size: 18
color: Theme.surfaceText
}
MouseArea {
id: expandBtn
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.toggleExpand()
}
}
Rectangle {
width: 28
height: 28
radius: 14
color: deleteBtn.containsMouse ? Theme.errorHover : Theme.withAlpha(Theme.errorHover, 0)
anchors.verticalCenter: parent.verticalCenter
visible: canDelete
DankIcon {
anchors.centerIn: parent
name: "delete"
size: 18
color: deleteBtn.containsMouse ? Theme.error : Theme.surfaceVariantText
}
MouseArea {
id: deleteBtn
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.deleteRequested()
}
}
}
Column {
id: expandedContent
width: parent.width
spacing: Theme.spacingXS
visible: isExpanded
Rectangle {
width: parent.width
height: 1
color: Theme.outlineLight
}
Item {
width: parent.width
height: VPNService.configLoading ? 40 : 0
visible: VPNService.configLoading
DankSpinner {
anchors.centerIn: parent
size: 20
}
}
Flow {
width: parent.width
spacing: Theme.spacingXS
visible: !isTransient && !VPNService.configLoading && configData
Repeater {
model: configFields
delegate: Rectangle {
required property var modelData
width: fieldContent.width + Theme.spacingM * 2
height: 32
radius: Theme.cornerRadius - 2
color: Theme.surfaceLight
border.width: 1
border.color: Theme.outlineLight
Row {
id: fieldContent
anchors.centerIn: parent
spacing: Theme.spacingXS
StyledText {
text: modelData.label + ":"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: modelData.value
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
}
DankToggle {
width: parent.width
text: I18n.tr("Autoconnect")
checked: configData ? (configData.autoconnect || false) : false
visible: !VPNService.configLoading && configData !== null
onToggled: checked => {
VPNService.updateConfig(profile.uuid, {
autoconnect: checked
});
}
}
Column {
width: parent.width
spacing: Theme.spacingXS
visible: !isTransient && !VPNService.configLoading && profile?.type !== "wireguard"
StyledText {
text: root.hasError ? DMSNetworkService.vpnError : I18n.tr("Credentials")
font.pixelSize: Theme.fontSizeSmall
color: root.hasError ? Theme.error : Theme.surfaceVariantText
}
DankTextField {
id: usernameField
width: parent.width
placeholderText: I18n.tr("Username")
text: (configData && (configData.username || (configData.data && configData.data.username))) || ""
}
DankTextField {
id: passwordField
width: parent.width
placeholderText: I18n.tr("Password")
echoMode: TextInput.Password
showPasswordToggle: true
normalBorderColor: root.hasError ? Theme.error : Theme.outlineMedium
}
DankButton {
text: I18n.tr("Save credentials")
opacity: passwordField.text.length > 0 ? 1 : 0.5
onClicked: {
if (passwordField.text.length === 0)
return;
VPNService.setCredentials(profile.uuid, usernameField.text, passwordField.text, true);
passwordField.text = "";
}
}
}
Item {
width: 1
height: Theme.spacingXS
}
}
}
}
@@ -1,72 +0,0 @@
import QtQuick
import qs.Common
import qs.Widgets
Rectangle {
id: root
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property string iconName: ""
property color iconColor: Theme.surfaceText
property string labelText: ""
property real value: 0.0
property real maximumValue: 1.0
property real minimumValue: 0.0
signal sliderValueChanged(real value)
width: parent ? parent.width : 200
height: 60
radius: Theme.cornerRadius
color: Theme.nestedSurface
border.color: Theme.outlineMedium
border.width: Theme.layerOutlineWidth
opacity: enabled ? 1.0 : 0.6
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingM
anchors.right: sliderContainer.left
anchors.rightMargin: Theme.spacingS
spacing: Theme.spacingS
DankIcon {
name: root.iconName
size: Theme.iconSize
color: root.iconColor
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: root.labelText
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle {
id: sliderContainer
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: Theme.spacingM
width: 120
height: parent.height - Theme.spacingS * 2
DankSlider {
anchors.centerIn: parent
width: parent.width
enabled: root.enabled
minimum: Math.round(root.minimumValue * 100)
maximum: Math.round(root.maximumValue * 100)
value: Math.round(root.value * 100)
trackColor: Theme.ccSliderTrackColor
trackOpacity: Theme.ccSliderTrackOpacity
onSliderValueChanged: root.sliderValueChanged(newValue / 100.0)
}
}
}
@@ -25,11 +25,6 @@ Rectangle {
height: 60
radius: Theme.cornerRadius
function hoverTint(base) {
const factor = 1.2;
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
}
readonly property color _containerBg: Theme.ccPillInactiveBg
color: {
@@ -61,7 +56,7 @@ Rectangle {
radius: root.radius
z: 0
visible: false
color: hoverTint(_containerBg)
color: Theme.hoverTint(_containerBg)
opacity: 0.08
antialiasing: true
Behavior on opacity {
@@ -98,7 +93,7 @@ Rectangle {
Rectangle {
anchors.fill: parent
radius: _tileRadius
color: hoverTint(iconTile.color)
color: Theme.hoverTint(iconTile.color)
opacity: tileMouse.pressed ? 0.3 : (tileMouse.containsMouse ? 0.2 : 0.0)
visible: opacity > 0
antialiasing: true
@@ -1,23 +0,0 @@
import QtQuick
Rectangle {
id: root
property string title: ""
property Component content: null
property bool isVisible: true
property int contentHeight: 300
width: parent ? parent.width : 400
implicitHeight: isVisible ? contentHeight : 0
height: implicitHeight
color: "transparent"
clip: true
Loader {
id: contentLoader
anchors.fill: parent
sourceComponent: root.content
asynchronous: true
}
}
@@ -22,11 +22,6 @@ Rectangle {
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4;
}
function hoverTint(base) {
const factor = 1.2;
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
}
readonly property color _tileBgActive: Theme.ccTileActiveBg
readonly property color _tileBgInactive: Theme.ccPillInactiveBg
readonly property color _tileRingActive: Theme.ccTileRing
@@ -47,7 +42,7 @@ Rectangle {
Rectangle {
anchors.fill: parent
radius: parent.radius
color: hoverTint(root.color)
color: Theme.hoverTint(root.color)
opacity: mouseArea.pressed ? 0.3 : (mouseArea.containsMouse ? 0.2 : 0.0)
visible: opacity > 0
antialiasing: true
@@ -16,11 +16,6 @@ Rectangle {
height: 48
radius: Theme.cornerRadius === 0 ? 0 : Theme.cornerRadius
function hoverTint(base) {
const factor = 1.2;
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
}
color: Theme.primary
border.color: Theme.ccTileRing
border.width: 1
@@ -29,7 +24,7 @@ Rectangle {
Rectangle {
anchors.fill: parent
radius: parent.radius
color: hoverTint(root.color)
color: Theme.hoverTint(root.color)
opacity: mouseArea.pressed ? 0.3 : (mouseArea.containsMouse ? 0.2 : 0.0)
visible: opacity > 0
antialiasing: true
@@ -25,11 +25,6 @@ Rectangle {
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4;
}
function hoverTint(base) {
const factor = 1.2;
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
}
readonly property color _tileBgActive: Theme.ccTileActiveBg
readonly property color _tileBgInactive: Theme.ccPillInactiveBg
readonly property color _tileRingActive: Theme.ccTileRing
@@ -50,7 +45,7 @@ Rectangle {
Rectangle {
anchors.fill: parent
radius: parent.radius
color: hoverTint(root.color)
color: Theme.hoverTint(root.color)
opacity: mouseArea.pressed ? 0.3 : (mouseArea.containsMouse ? 0.2 : 0.0)
visible: opacity > 0
antialiasing: true
@@ -34,11 +34,6 @@ Rectangle {
height: 48
radius: Theme.cornerRadius + 4
function hoverTint(base) {
const factor = 1.2;
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
}
readonly property color _tileBg: Theme.ccPillInactiveBg
color: mouseArea.containsMouse ? Theme.ccPillInactiveHoverBg : _tileBg
@@ -50,7 +45,7 @@ Rectangle {
Rectangle {
anchors.fill: parent
radius: parent.radius
color: hoverTint(root.color)
color: Theme.hoverTint(root.color)
opacity: mouseArea.pressed ? 0.3 : (mouseArea.containsMouse ? 0.2 : 0.0)
visible: opacity > 0
antialiasing: true
@@ -20,11 +20,6 @@ Rectangle {
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4;
}
function hoverTint(base) {
const factor = 1.2;
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
}
readonly property color _tileBgActive: Theme.ccTileActiveBg
readonly property color _tileBgInactive: Theme.ccPillInactiveBg
readonly property color _tileRingActive: Theme.ccTileRing
@@ -45,7 +40,7 @@ Rectangle {
Rectangle {
anchors.fill: parent
radius: parent.radius
color: hoverTint(root.color)
color: Theme.hoverTint(root.color)
opacity: mouseArea.pressed ? 0.3 : (mouseArea.containsMouse ? 0.2 : 0.0)
visible: opacity > 0
antialiasing: true
@@ -39,17 +39,12 @@ Rectangle {
border.width: isActive ? 1 : Theme.layerOutlineWidth
opacity: enabled ? 1.0 : 0.6
function hoverTint(base) {
const factor = 1.2;
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
}
readonly property color _containerBg: Theme.ccPillInactiveBg
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: mouseArea.containsMouse ? hoverTint(_containerBg) : Theme.withAlpha(_containerBg, 0)
color: mouseArea.containsMouse ? Theme.hoverTint(_containerBg) : Theme.withAlpha(_containerBg, 0)
opacity: mouseArea.containsMouse ? 0.08 : 0.0
Behavior on opacity {
+1 -17
View File
@@ -274,22 +274,6 @@ Item {
return ws.num !== -1 ? ws.num : ws.name;
}
function escapeSwayWorkspaceName(name) {
return String(name ?? "").replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
}
function dispatchSwayWorkspace(ws) {
if (!ws)
return;
try {
if (ws.num !== undefined && ws.num !== -1) {
I3.dispatch(`workspace number ${ws.num}`);
} else if (ws.name) {
I3.dispatch(`workspace "${escapeSwayWorkspaceName(ws.name)}"`);
}
} catch (_) {}
}
function switchWorkspace(direction) {
const realWorkspaces = getRealWorkspaces();
if (realWorkspaces.length < 2) {
@@ -334,7 +318,7 @@ Item {
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0);
if (nextIndex !== validIndex) {
dispatchSwayWorkspace(realWorkspaces[nextIndex]);
CompositorService.dispatchSwayWorkspace(realWorkspaces[nextIndex]);
}
}
}
@@ -3,6 +3,7 @@ import QtQuick.Layouts
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.ControlCenter.Details
DankPopout {
id: root
@@ -3,22 +3,11 @@ import qs.Common
import qs.Modules.Plugins
import qs.Services
import qs.Widgets
import "../../../Common/Format.js" as Format
BasePill {
id: root
function formatNetworkSpeed(bytesPerSec) {
if (bytesPerSec < 1024) {
return bytesPerSec.toFixed(0) + " B/s";
} else if (bytesPerSec < 1024 * 1024) {
return (bytesPerSec / 1024).toFixed(1) + " KB/s";
} else if (bytesPerSec < 1024 * 1024 * 1024) {
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s";
} else {
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s";
}
}
Component.onCompleted: {
DgopService.addRef(["network"]);
}
@@ -97,7 +86,7 @@ BasePill {
}
StyledText {
text: DgopService.networkRxRate > 0 ? root.formatNetworkSpeed(DgopService.networkRxRate) : "0 B/s"
text: DgopService.networkRxRate > 0 ? Format.formatRate(DgopService.networkRxRate, 1) : "0 B/s"
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
color: Theme.widgetTextColor
anchors.verticalCenter: parent.verticalCenter
@@ -126,7 +115,7 @@ BasePill {
}
StyledText {
text: DgopService.networkTxRate > 0 ? root.formatNetworkSpeed(DgopService.networkTxRate) : "0 B/s"
text: DgopService.networkTxRate > 0 ? Format.formatRate(DgopService.networkTxRate, 1) : "0 B/s"
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
color: Theme.widgetTextColor
anchors.verticalCenter: parent.verticalCenter
@@ -228,22 +228,6 @@ Item {
return ws.num !== -1 ? ws.num : ws.name;
}
function escapeSwayWorkspaceName(name) {
return String(name ?? "").replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
}
function dispatchSwayWorkspace(ws) {
if (!ws)
return;
try {
if (ws.num !== undefined && ws.num !== -1) {
I3.dispatch(`workspace number ${ws.num}`);
} else if (ws.name) {
I3.dispatch(`workspace "${escapeSwayWorkspaceName(ws.name)}"`);
}
} catch (_) {}
}
function getSwayActiveWorkspace() {
if (!root.screenName || SettingsData.workspaceFollowFocus) {
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true);
@@ -711,7 +695,7 @@ Item {
case "sway":
case "scroll":
case "miracle":
dispatchSwayWorkspace(data);
CompositorService.dispatchSwayWorkspace(data);
break;
}
}
@@ -818,7 +802,7 @@ Item {
return;
}
dispatchSwayWorkspace(realWorkspaces[nextIndex]);
CompositorService.dispatchSwayWorkspace(realWorkspaces[nextIndex]);
}
}
@@ -1486,7 +1470,7 @@ Item {
} else if (root.isMango && modelData?.tag !== undefined) {
MangoService.switchToTag(root.screenName, modelData.tag);
} else if ((CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) && modelData?.num !== undefined) {
root.dispatchSwayWorkspace(modelData);
CompositorService.dispatchSwayWorkspace(modelData);
}
} else if (mouse.button === Qt.RightButton) {
if (CompositorService.isNiri) {
@@ -415,11 +415,24 @@ Item {
}
}
onWheel: wheelEvent => {
if (SettingsData.audioDeviceScrollVolumeEnabled && wheelEvent.x >= deviceMouseArea.width / 2) {
AudioService.handleNodeVolumeWheel(modelData, wheelEvent);
} else {
if (!SettingsData.audioDeviceScrollVolumeEnabled || wheelEvent.x < deviceMouseArea.width / 2) {
wheelEvent.accepted = false;
return;
}
if (!modelData?.audio)
return;
SessionData.suppressOSDTemporarily();
const delta = wheelEvent.angleDelta.y;
if (delta === 0)
return;
const current = Math.round(modelData.audio.volume * 100);
const maxVol = AudioService.getMaxVolumePercent(modelData);
const newVolume = delta > 0 ? Math.min(maxVol, current + AudioService.wheelVolumeStep) : Math.max(0, current - AudioService.wheelVolumeStep);
modelData.audio.muted = false;
modelData.audio.volume = newVolume / 100;
if (modelData === AudioService.sink)
AudioService.playVolumeChangeSoundIfEnabled();
wheelEvent.accepted = true;
}
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
@@ -451,6 +451,9 @@ Item {
height: width
anchors.centerIn: parent
activePlayer: root.activePlayer
artUrl: TrackArtService.resolvedArtUrl
accentColor: MediaAccentService.accent
cavaService: CavaService
}
}
@@ -522,6 +525,10 @@ Item {
height: 20
anchors.horizontalCenter: parent.horizontalCenter
activePlayer: root.activePlayer
stableLength: MprisController.activePlayerStableLength
accentColor: MediaAccentService.accent
accentTrackColor: MediaAccentService.accentTrack
accentSubtleColor: MediaAccentService.accentSubtle
isSeeking: root.isSeeking
onIsSeekingChanged: root.isSeeking = isSeeking
}
@@ -77,6 +77,9 @@ Card {
height: 80
anchors.centerIn: parent
activePlayer: root.activePlayer
artUrl: TrackArtService.resolvedArtUrl
accentColor: MediaAccentService.accent
cavaService: CavaService
albumSize: 76
animationScale: 1.05
}
@@ -114,6 +117,10 @@ Card {
height: 20
x: -2
activePlayer: root.activePlayer
stableLength: MprisController.activePlayerStableLength
accentColor: MediaAccentService.accent
accentTrackColor: MediaAccentService.accentTrack
accentSubtleColor: MediaAccentService.accentSubtle
isSeeking: root.isSeeking
onIsSeekingChanged: root.isSeeking = isSeeking
}
+4 -8
View File
@@ -114,10 +114,6 @@ Item {
readonly property real positionSpacing: barSpacing + effectiveDockBottomGap + effectiveDockMargin
readonly property real joinedEdgeMargin: dockGeometry.joinedEdgeMargin
readonly property real _dpr: (dock.screen && dock.screen.devicePixelRatio) ? dock.screen.devicePixelRatio : 1
function px(v) {
return Math.round(v * _dpr) / _dpr;
}
DockGeometry {
id: dockGeometry
@@ -397,8 +393,8 @@ Item {
property real animationHeadroom: Math.ceil(SettingsData.dockIconSize * 0.35)
readonly property real surfaceImplicitWidth: isVertical ? (px(dockGeometry.surfaceThickness + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
readonly property real surfaceImplicitHeight: !isVertical ? (px(dockGeometry.surfaceThickness + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
readonly property real surfaceImplicitWidth: isVertical ? (Theme.px(dockGeometry.surfaceThickness + SettingsData.dockIconSize * 0.3, _dpr) + animationHeadroom) : 0
readonly property real surfaceImplicitHeight: !isVertical ? (Theme.px(dockGeometry.surfaceThickness + SettingsData.dockIconSize * 0.3, _dpr) + animationHeadroom) : 0
readonly property real blurX: dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x + dockSlide.x
readonly property real blurY: dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y + dockSlide.y
@@ -585,11 +581,11 @@ Item {
// Keep the taller hit area regardless of the reveal state to prevent shrinking loop
return Math.min(Math.max(dockBackground.height + 64, 200), maxDockHeight);
}
return dock.reveal ? px(dockGeometry.motionThickness) : 1;
return dock.reveal ? Theme.px(dockGeometry.motionThickness, _dpr) : 1;
}
width: {
if (dock.isVertical) {
return dock.reveal ? px(dockGeometry.motionThickness) : 1;
return dock.reveal ? Theme.px(dockGeometry.motionThickness, _dpr) : 1;
}
// Keep the wider hit area regardless of the reveal state to prevent shrinking loop
return Math.min(dockBackground.width + 8 + dock.borderThickness, maxDockWidth);
+1 -5
View File
@@ -19,10 +19,6 @@ QtObject {
property real barSpacing: 0
property real dpr: 1
function px(value) {
return Math.round(value * dpr) / dpr;
}
readonly property bool frameExclusionActive: CompositorService.frameWindowVisibleForScreen(screen)
readonly property bool usesConnectedFrameChrome: CompositorService.usesConnectedFrameChromeForScreen(screen)
readonly property bool connectedBarActiveOnEdge: usesConnectedFrameChrome && !!screen && SettingsData.getActiveBarEdgesForScreen(screen).includes(edge)
@@ -56,6 +52,6 @@ QtObject {
// Frame/bar edge exclusions already reserve the edge itself, so the dock
// reservation covers only the dock body and user offset beyond that edge.
readonly property real reserveZone: px(bodyThickness + reserveOffset + effectiveMargin)
readonly property real reserveZone: Theme.px(bodyThickness + reserveOffset + effectiveMargin, dpr)
readonly property bool shouldReserveSpace: dockVisible && !autoHide && barSpacing <= 0
}
@@ -2,6 +2,7 @@ import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/Format.js" as Format
Rectangle {
id: root
@@ -22,36 +23,8 @@ Rectangle {
onTriggered: root.nowMs = Date.now()
}
function _pad2(n) {
return n < 10 ? "0" + n : "" + n;
}
function formatRemaining(ms) {
if (ms <= 0)
return I18n.tr("Off");
const totalMinutes = Math.ceil(ms / 60000);
if (totalMinutes < 60)
return I18n.tr("%1 min left").arg(totalMinutes);
const hours = Math.floor(totalMinutes / 60);
const mins = totalMinutes - hours * 60;
if (mins === 0)
return I18n.tr("%1 h left").arg(hours);
return I18n.tr("%1 h %2 m left").arg(hours).arg(mins);
}
function formatUntilTimestamp(ts) {
if (!ts)
return "";
const d = new Date(ts);
const hours = d.getHours();
const minutes = d.getMinutes();
const use24h = (typeof SettingsData !== "undefined") ? SettingsData.use24HourClock : true;
if (use24h) {
return _pad2(hours) + ":" + _pad2(minutes);
}
const suffix = hours >= 12 ? "PM" : "AM";
const h12 = ((hours + 11) % 12) + 1;
return h12 + ":" + _pad2(minutes) + " " + suffix;
return Format.formatRemaining(ms, I18n.tr("Off"), I18n.tr("%1 min left"), I18n.tr("%1 h left"), I18n.tr("%1 h %2 m left"));
}
function minutesUntilTomorrowMorning() {
@@ -149,7 +122,7 @@ Rectangle {
visible: root.currentlyActive
text: {
if (SessionData.doNotDisturbUntil > 0) {
return root.formatRemaining(root.currentRemainingMs) + " · " + I18n.tr("until %1").arg(root.formatUntilTimestamp(SessionData.doNotDisturbUntil));
return root.formatRemaining(root.currentRemainingMs) + " · " + I18n.tr("until %1").arg(Format.formatUntil(SessionData.doNotDisturbUntil, SettingsData.use24HourClock));
}
return I18n.tr("On indefinitely");
}
@@ -27,6 +27,36 @@ Rectangle {
readonly property real collapsedContentHeight: iconSize + cardPadding
readonly property real baseCardHeight: cardPadding * 2 + collapsedContentHeight
function formatHistoryTime(timestamp) {
NotificationService.timeUpdateTick;
NotificationService.clockFormatChanged;
const now = new Date();
const date = new Date(timestamp);
const diff = now.getTime() - timestamp;
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(minutes / 60);
if (hours < 1) {
if (minutes < 1)
return I18n.tr("now");
return I18n.tr("%1m ago").arg(minutes);
}
const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const itemDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
const daysDiff = Math.floor((nowDate - itemDate) / (1000 * 60 * 60 * 24));
const timeStr = SettingsData.use24HourClock ? date.toLocaleTimeString(Qt.locale(), "HH:mm") : date.toLocaleTimeString(Qt.locale(), "h:mm AP");
if (daysDiff === 0)
return timeStr;
try {
const localeName = (typeof I18n !== "undefined" && I18n.locale) ? I18n.locale().name : "en-US";
const weekday = date.toLocaleDateString(localeName, {
weekday: "long"
});
return weekday + ", " + timeStr;
} catch (e) {
return timeStr;
}
}
width: parent ? parent.width : 400
height: baseCardHeight + contentItem.extraHeight
radius: Theme.cornerRadius
@@ -219,7 +249,7 @@ Rectangle {
}
StyledText {
id: historyTimeText
text: NotificationService.formatHistoryTime(historyItem.timestamp)
text: root.formatHistoryTime(historyItem.timestamp)
color: Theme.surfaceTextMedium
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Normal
+3 -13
View File
@@ -2,6 +2,7 @@ import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import "../../Common/QmlUtils.js" as QmlUtils
Column {
id: root
@@ -18,7 +19,7 @@ Column {
property bool isInitialized: false
function loadValue() {
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings && settings.pluginService) {
const loadedValue = settings.loadValue(settingKey, defaultValue);
value = loadedValue;
@@ -33,23 +34,12 @@ Column {
onValueChanged: {
if (!isInitialized)
return;
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings) {
settings.saveValue(settingKey, value);
}
}
function findSettings() {
let item = parent;
while (item) {
if (item.saveValue !== undefined && item.loadValue !== undefined) {
return item;
}
item = item.parent;
}
return null;
}
StyledText {
text: root.label
font.pixelSize: Theme.fontSizeMedium
+3 -13
View File
@@ -1,6 +1,7 @@
import QtQuick
import qs.Common
import qs.Widgets
import "../../Common/QmlUtils.js" as QmlUtils
Column {
id: root
@@ -16,30 +17,19 @@ Column {
spacing: Theme.spacingM
Component.onCompleted: {
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings) {
items = settings.loadValue(settingKey, defaultValue);
}
}
onItemsChanged: {
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings) {
settings.saveValue(settingKey, items);
}
}
function findSettings() {
let item = parent;
while (item) {
if (item.saveValue !== undefined && item.loadValue !== undefined) {
return item;
}
item = item.parent;
}
return null;
}
function addItem(item) {
items = items.concat([item]);
}
@@ -1,6 +1,7 @@
import QtQuick
import qs.Common
import qs.Widgets
import "../../Common/QmlUtils.js" as QmlUtils
Column {
id: root
@@ -22,7 +23,7 @@ Column {
}
function loadValue() {
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings) {
isLoading = true;
items = settings.loadValue(settingKey, defaultValue);
@@ -34,23 +35,12 @@ Column {
if (isLoading) {
return;
}
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings) {
settings.saveValue(settingKey, items);
}
}
function findSettings() {
let item = parent;
while (item) {
if (item.saveValue !== undefined && item.loadValue !== undefined) {
return item;
}
item = item.parent;
}
return null;
}
function addItem(item) {
items = items.concat([item]);
}
@@ -1,43 +0,0 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
property string pluginId: ""
property var pluginInstance: null
property bool isCompoundPill: false
property bool isSmallToggle: false
readonly property bool hasDetail: pluginInstance?.ccDetailContent !== null
readonly property string iconName: pluginInstance?.ccWidgetIcon || "extension"
readonly property string primaryText: pluginInstance?.ccWidgetPrimaryText || "Plugin"
readonly property string secondaryText: pluginInstance?.ccWidgetSecondaryText || ""
readonly property bool isActive: pluginInstance?.ccWidgetIsActive || false
readonly property Component detailContent: pluginInstance?.ccDetailContent || null
readonly property real detailHeight: pluginInstance?.ccDetailHeight || 250
signal toggled
signal expanded
Component.onCompleted: {
if (pluginInstance) {
pluginInstance.ccWidgetToggled.connect(toggled);
pluginInstance.ccWidgetExpanded.connect(expanded);
}
}
function invokeToggle() {
if (pluginInstance) {
pluginInstance.ccWidgetToggled();
}
}
function invokeExpand() {
if (pluginInstance) {
pluginInstance.ccWidgetExpanded();
}
}
}
+20 -30
View File
@@ -1,6 +1,7 @@
import QtQuick
import qs.Common
import qs.Widgets
import "../../Common/QmlUtils.js" as QmlUtils
Column {
id: root
@@ -16,68 +17,57 @@ Column {
spacing: Theme.spacingS
function loadValue() {
const settings = findSettings()
const settings = QmlUtils.findSettings(root.parent);
if (settings && settings.pluginService) {
value = settings.loadValue(settingKey, defaultValue)
value = settings.loadValue(settingKey, defaultValue);
}
}
Component.onCompleted: {
loadValue()
loadValue();
}
readonly property var optionLabels: {
const labels = []
const labels = [];
for (let i = 0; i < options.length; i++) {
labels.push(options[i].label || options[i])
labels.push(options[i].label || options[i]);
}
return labels
return labels;
}
readonly property var valueToLabel: {
const map = {}
const map = {};
for (let i = 0; i < options.length; i++) {
const opt = options[i]
const opt = options[i];
if (typeof opt === 'object') {
map[opt.value] = opt.label
map[opt.value] = opt.label;
} else {
map[opt] = opt
map[opt] = opt;
}
}
return map
return map;
}
readonly property var labelToValue: {
const map = {}
const map = {};
for (let i = 0; i < options.length; i++) {
const opt = options[i]
const opt = options[i];
if (typeof opt === 'object') {
map[opt.label] = opt.value
map[opt.label] = opt.value;
} else {
map[opt] = opt
map[opt] = opt;
}
}
return map
return map;
}
onValueChanged: {
const settings = findSettings()
const settings = QmlUtils.findSettings(root.parent);
if (settings) {
settings.saveValue(settingKey, value)
settings.saveValue(settingKey, value);
}
}
function findSettings() {
let item = parent
while (item) {
if (item.saveValue !== undefined && item.loadValue !== undefined) {
return item
}
item = item.parent
}
return null
}
DankDropdown {
width: parent.width
text: root.label
@@ -85,7 +75,7 @@ Column {
currentValue: root.valueToLabel[root.value] || root.value
options: root.optionLabels
onValueChanged: newValue => {
root.value = root.labelToValue[newValue] || newValue
root.value = root.labelToValue[newValue] || newValue;
}
}
}
+3 -13
View File
@@ -1,6 +1,7 @@
import QtQuick
import qs.Common
import qs.Widgets
import "../../Common/QmlUtils.js" as QmlUtils
Column {
id: root
@@ -20,7 +21,7 @@ Column {
spacing: Theme.spacingS
function loadValue() {
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings && settings.pluginService) {
value = settings.loadValue(settingKey, defaultValue);
}
@@ -31,23 +32,12 @@ Column {
}
onValueChanged: {
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings) {
settings.saveValue(settingKey, value);
}
}
function findSettings() {
let item = parent;
while (item) {
if (item.saveValue !== undefined && item.loadValue !== undefined) {
return item;
}
item = item.parent;
}
return null;
}
StyledText {
text: root.label
font.pixelSize: Theme.fontSizeMedium
+3 -13
View File
@@ -1,6 +1,7 @@
import QtQuick
import qs.Common
import qs.Widgets
import "../../Common/QmlUtils.js" as QmlUtils
Column {
id: root
@@ -18,7 +19,7 @@ Column {
property bool isInitialized: false
function loadValue() {
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings && settings.pluginService) {
const loadedValue = settings.loadValue(settingKey, defaultValue);
if (textField.activeFocus && isInitialized)
@@ -39,22 +40,11 @@ Column {
if (textField.text === value)
return;
value = textField.text;
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings)
settings.saveValue(settingKey, value);
}
function findSettings() {
let item = parent;
while (item) {
if (item.saveValue !== undefined && item.loadValue !== undefined) {
return item;
}
item = item.parent;
}
return null;
}
StyledText {
text: root.label
font.pixelSize: Theme.fontSizeMedium
+3 -13
View File
@@ -1,6 +1,7 @@
import QtQuick
import qs.Common
import qs.Widgets
import "../../Common/QmlUtils.js" as QmlUtils
Row {
id: root
@@ -17,7 +18,7 @@ Row {
property bool isInitialized: false
function loadValue() {
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings && settings.pluginService) {
const loadedValue = settings.loadValue(settingKey, defaultValue);
value = loadedValue;
@@ -32,23 +33,12 @@ Row {
onValueChanged: {
if (!isInitialized)
return;
const settings = findSettings();
const settings = QmlUtils.findSettings(root.parent);
if (settings) {
settings.saveValue(settingKey, value);
}
}
function findSettings() {
let item = parent;
while (item) {
if (item.saveValue !== undefined && item.loadValue !== undefined) {
return item;
}
item = item.parent;
}
return null;
}
Column {
width: parent.width - toggle.width - Theme.spacingM
spacing: Theme.spacingXS
+3 -12
View File
@@ -3,20 +3,11 @@ import QtQuick.Layouts
import qs.Common
import qs.Services
import qs.Widgets
import "../../Common/Format.js" as Format
Item {
id: root
function formatSpeed(bytesPerSec) {
if (bytesPerSec < 1024)
return bytesPerSec.toFixed(0) + " B/s";
if (bytesPerSec < 1024 * 1024)
return (bytesPerSec / 1024).toFixed(1) + " KB/s";
if (bytesPerSec < 1024 * 1024 * 1024)
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s";
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(2) + " GB/s";
}
Component.onCompleted: {
DgopService.addRef(["disk", "diskmounts"]);
}
@@ -76,7 +67,7 @@ Item {
}
StyledText {
text: root.formatSpeed(DgopService.diskReadRate)
text: Format.formatRate(DgopService.diskReadRate)
font.pixelSize: Theme.fontSizeSmall
font.family: SettingsData.monoFontFamily
font.weight: Font.Bold
@@ -94,7 +85,7 @@ Item {
}
StyledText {
text: root.formatSpeed(DgopService.diskWriteRate)
text: Format.formatRate(DgopService.diskWriteRate)
font.pixelSize: Theme.fontSizeSmall
font.family: SettingsData.monoFontFamily
font.weight: Font.Bold
@@ -4,6 +4,7 @@ import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
import "../../Common/Format.js" as Format
Item {
id: root
@@ -17,31 +18,13 @@ Item {
property var diskReadHistory: []
property var diskWriteHistory: []
function formatBytes(bytes) {
if (bytes < 1024)
return bytes.toFixed(0) + " B/s";
if (bytes < 1024 * 1024)
return (bytes / 1024).toFixed(1) + " KB/s";
if (bytes < 1024 * 1024 * 1024)
return (bytes / (1024 * 1024)).toFixed(1) + " MB/s";
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB/s";
}
function addToHistory(arr, val) {
const newArr = arr.slice();
newArr.push(val);
if (newArr.length > historySize)
newArr.shift();
return newArr;
}
function sampleData() {
cpuHistory = addToHistory(cpuHistory, DgopService.cpuUsage);
memoryHistory = addToHistory(memoryHistory, DgopService.memoryUsage);
networkRxHistory = addToHistory(networkRxHistory, DgopService.networkRxRate);
networkTxHistory = addToHistory(networkTxHistory, DgopService.networkTxRate);
diskReadHistory = addToHistory(diskReadHistory, DgopService.diskReadRate);
diskWriteHistory = addToHistory(diskWriteHistory, DgopService.diskWriteRate);
cpuHistory = Format.addToHistory(cpuHistory, DgopService.cpuUsage, historySize);
memoryHistory = Format.addToHistory(memoryHistory, DgopService.memoryUsage, historySize);
networkRxHistory = Format.addToHistory(networkRxHistory, DgopService.networkRxRate, historySize);
networkTxHistory = Format.addToHistory(networkTxHistory, DgopService.networkTxRate, historySize);
diskReadHistory = Format.addToHistory(diskReadHistory, DgopService.diskReadRate, historySize);
diskWriteHistory = Format.addToHistory(diskWriteHistory, DgopService.diskWriteRate, historySize);
}
Component.onCompleted: {
@@ -111,8 +94,8 @@ Item {
Layout.fillHeight: true
title: I18n.tr("Network")
icon: "swap_horiz"
value: "↓ " + root.formatBytes(DgopService.networkRxRate)
subtitle: "↑ " + root.formatBytes(DgopService.networkTxRate)
value: "↓ " + Format.formatRate(DgopService.networkRxRate)
subtitle: "↑ " + Format.formatRate(DgopService.networkTxRate)
accentColor: Theme.info
history: root.networkRxHistory
history2: root.networkTxHistory
@@ -127,8 +110,8 @@ Item {
Layout.fillHeight: true
title: I18n.tr("Disk")
icon: "storage"
value: "R: " + root.formatBytes(DgopService.diskReadRate)
subtitle: "W: " + root.formatBytes(DgopService.diskWriteRate)
value: "R: " + Format.formatRate(DgopService.diskReadRate)
subtitle: "W: " + Format.formatRate(DgopService.diskWriteRate)
accentColor: Theme.warning
history: root.diskReadHistory
history2: root.diskWriteHistory
@@ -36,6 +36,36 @@ Item {
cachedProcesses = filteredProcesses;
}
function getProcessIcon(command) {
const cmd = command.toLowerCase();
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser") || cmd.includes("chromium"))
return "web";
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim"))
return "code";
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh"))
return "terminal";
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify"))
return "music_note";
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv"))
return "play_circle";
if (cmd.includes("systemd") || cmd.includes("elogind") || cmd.includes("kernel") || cmd.includes("kthread") || cmd.includes("kworker"))
return "settings";
return "memory";
}
function formatCpuUsage(cpu) {
return (cpu || 0).toFixed(1) + "%";
}
function formatMemoryUsage(memoryKB) {
const mem = memoryKB || 0;
if (mem < 1024)
return mem.toFixed(0) + " KB";
if (mem < 1024 * 1024)
return (mem / 1024).toFixed(1) + " MB";
return (mem / (1024 * 1024)).toFixed(1) + " GB";
}
readonly property var filteredProcesses: {
if (!DgopService.allProcesses || DgopService.allProcesses.length === 0)
return [];
@@ -521,7 +551,7 @@ Item {
spacing: Theme.spacingS
DankIcon {
name: DgopService.getProcessIcon(processItemRoot.processCmd)
name: root.getProcessIcon(processItemRoot.processCmd)
size: Theme.iconSize - 4
color: {
if (processItemRoot.processCpu > 80)
@@ -566,7 +596,7 @@ Item {
StyledText {
anchors.centerIn: parent
text: DgopService.formatCpuUsage(processItemRoot.processCpu)
text: root.formatCpuUsage(processItemRoot.processCpu)
font.pixelSize: Theme.fontSizeSmall
font.family: SettingsData.monoFontFamily
font.weight: Font.Bold
@@ -600,7 +630,7 @@ Item {
StyledText {
anchors.centerIn: parent
text: DgopService.formatMemoryUsage(processItemRoot.processMemKB)
text: root.formatMemoryUsage(processItemRoot.processMemKB)
font.pixelSize: Theme.fontSizeSmall
font.family: SettingsData.monoFontFamily
font.weight: Font.Bold
@@ -387,7 +387,6 @@ Singleton {
});
}
// Extract neutral per-output config from current live state
function extractOutputNeutralConfig(outputName, outputData, niriSettings, hyprlandSettings) {
const modeData = (outputData.modes && outputData.current_mode !== undefined) ? outputData.modes[outputData.current_mode] : null;
const modeStr = modeData ? modeData.width + "x" + modeData.height + "@" + (modeData.refresh_rate / 1000).toFixed(3) : null;
@@ -421,7 +420,6 @@ Singleton {
return cfg;
}
// Convert monitors.json config entry internal outputsData map
function profileKeyMatchesOutput(outputId, output, name) {
if (name === outputId || getOutputIdentifier(output, name) === outputId)
return true;
@@ -438,7 +436,6 @@ Singleton {
const cfgOutputs = configEntry.outputs || {};
for (const outputId in cfgOutputs) {
const cfg = cfgOutputs[outputId];
// Find matching live output to get modes list
let liveOutput = null;
for (const name in outputs) {
if (profileKeyMatchesOutput(outputId, outputs[name], name)) {
@@ -476,7 +473,6 @@ Singleton {
return result;
}
// Extract niri settings map from a neutral config entry.
function getNiriSettingsFromConfig(configEntry) {
const result = {};
for (const outputId in (configEntry.outputs || {})) {
@@ -490,7 +486,6 @@ Singleton {
return result;
}
// Extract hyprland settings map from neutral config entry
function getHyprlandSettingsFromConfig(configEntry) {
const result = {};
for (const outputId in (configEntry.outputs || {})) {
@@ -537,7 +532,6 @@ Singleton {
return true;
}
// Write compositor config from a neutral config entry and optionally reload
function applyConfigEntry(configEntry, configId, profileName, isManual) {
if (CompositorService.isHyprland && readOnly) {
if (isManual) {
@@ -580,8 +574,6 @@ Singleton {
});
}
// Profile management
function validateProfiles() {
log.info("Validating profiles against current outputs...");
readMonitorsJson(data => {
@@ -2072,7 +2064,6 @@ Singleton {
return pending !== undefined ? pending : originalValue;
}
// Returns true if the given output can currently be disabled.
// Prevents disabling all outputs and prevents disabling the only output
// in a single-display configuration.
function canDisableOutput() {
@@ -3,23 +3,11 @@ import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
import "../../Common/Format.js" as Format
Item {
id: root
function formatGammaTime(isoString) {
if (!isoString)
return "";
try {
const date = new Date(isoString);
if (isNaN(date.getTime()))
return "";
return date.toLocaleTimeString(Qt.locale(), "HH:mm");
} catch (e) {
return "";
}
}
DankFlickable {
anchors.fill: parent
clip: true
@@ -195,7 +183,7 @@ Item {
}
onTabClicked: index => {
DisplayService.setNightModeAutomationMode(index === 1 ? "location" : "time");
SessionData.setNightModeAutoMode(index === 1 ? "location" : "time");
currentIndex = index;
}
@@ -562,7 +550,7 @@ Item {
}
StyledText {
text: root.formatGammaTime(DisplayService.gammaSunriseTime)
text: Format.formatIsoTime(DisplayService.gammaSunriseTime)
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
@@ -598,7 +586,7 @@ Item {
}
StyledText {
text: root.formatGammaTime(DisplayService.gammaSunsetTime)
text: Format.formatIsoTime(DisplayService.gammaSunsetTime)
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
@@ -645,7 +633,7 @@ Item {
}
StyledText {
text: root.formatGammaTime(DisplayService.gammaNextTransition)
text: Format.formatIsoTime(DisplayService.gammaNextTransition)
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
File diff suppressed because it is too large Load Diff
@@ -240,7 +240,7 @@ Item {
required property var modelData
required property int index
readonly property bool isActive: DMSNetworkService.isActiveUuid(modelData.uuid)
readonly property bool isActive: DMSNetworkService.isActiveVpnUuid(modelData.uuid)
readonly property bool isTransient: !!modelData.transient
readonly property bool canExpand: modelData.canExpand !== false
readonly property bool canDelete: modelData.canDelete !== false
+3 -10
View File
@@ -9,6 +9,7 @@ import qs.Modules.Settings.Widgets
import qs.Modals.Common
import qs.Services
import qs.Widgets
import "../../Common/QmlUtils.js" as QmlUtils
Item {
id: networkWifiTab
@@ -46,22 +47,14 @@ Item {
property string expandedSavedWifiSsid: ""
property int maxPinnedWifiNetworks: 3
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v);
if (typeof value === "string" && value.length > 0)
return [value];
return [];
}
function getPinnedWifiNetworks() {
const pins = CacheData.wifiNetworkPins || {};
return normalizePinList(pins["preferredWifi"]);
return QmlUtils.normalizePinList(pins["preferredWifi"]);
}
function toggleWifiPin(ssid) {
const pins = JSON.parse(JSON.stringify(CacheData.wifiNetworkPins || {}));
let pinnedList = normalizePinList(pins["preferredWifi"]);
let pinnedList = QmlUtils.normalizePinList(pins["preferredWifi"]);
const pinIndex = pinnedList.indexOf(ssid);
if (pinIndex !== -1) {
@@ -1,5 +1,6 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
@@ -215,7 +216,7 @@ Item {
currentValue: (SettingsData.notificationSummaryFontSize || I18n.tr("Unset")).toString()
onValueChanged: value => {
SettingsData.set("notificationSummaryFontSize", Number(value === I18n.tr("Unset") ? 0 : value));
SettingsData.sendTestNotifications();
NotificationService.sendTestNotifications();
}
}
@@ -228,7 +229,7 @@ Item {
currentValue: (SettingsData.notificationBodyFontSize || I18n.tr("Unset")).toString()
onValueChanged: value => {
SettingsData.set("notificationBodyFontSize", Number(value === I18n.tr("Unset") ? 0 : value));
SettingsData.sendTestNotifications();
NotificationService.sendTestNotifications();
}
}
@@ -277,7 +278,7 @@ Item {
SettingsData.set("notificationPopupPosition", SettingsData.Position.Bottom);
break;
}
SettingsData.sendTestNotifications();
NotificationService.sendTestNotifications();
}
}
+2 -14
View File
@@ -8,6 +8,7 @@ import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
import "../../Common/ConfigIncludeResolve.js" as ConfigIncludeResolve
import "../../Common/Format.js" as Format
Item {
id: themeColorsTab
@@ -215,19 +216,6 @@ Item {
ToastService.showError(I18n.tr("Missing Environment Variables", "qt theme env error title"), I18n.tr("You need to set either:\nQT_QPA_PLATFORMTHEME=gtk3 OR\nQT_QPA_PLATFORMTHEME=qt6ct\nas environment variables, and then restart the shell.\n\nqt6ct requires qt6ct-kde to be installed.", "qt theme env error body"));
}
function formatThemeAutoTime(isoString) {
if (!isoString)
return "";
try {
const date = new Date(isoString);
if (isNaN(date.getTime()))
return "";
return date.toLocaleTimeString(Qt.locale(), "HH:mm");
} catch (e) {
return "";
}
}
function refreshMatugenSchemePreviews() {
if (!Theme.matugenAvailable)
return;
@@ -1566,7 +1554,7 @@ Item {
}
StyledText {
text: themeColorsTab.formatThemeAutoTime(SessionData.themeModeNextTransition)
text: Format.formatIsoTime(SessionData.themeModeNextTransition)
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
@@ -4,6 +4,7 @@ import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
Item {
id: root
@@ -20,16 +21,6 @@ Item {
readonly property bool isHighlighted: settingKey !== "" && SettingsSearchService.highlightSection === settingKey
function findParentFlickable() {
let p = root.parent;
while (p) {
if (p.hasOwnProperty("contentY") && p.hasOwnProperty("contentItem"))
return p;
p = p.parent;
}
return null;
}
Component.onCompleted: {
if (!settingKey)
return;
@@ -37,7 +28,7 @@ Item {
Qt.callLater(() => {
if (!root.parent)
return;
var flickable = findParentFlickable();
var flickable = QmlUtils.findParentFlickable(root.parent);
if (flickable)
SettingsSearchService.registerCard(key, root, flickable);
});
@@ -4,6 +4,7 @@ import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
StyledRect {
id: root
@@ -43,17 +44,6 @@ StyledRect {
readonly property bool hasHeader: root.title !== "" || root.iconName !== ""
property bool userToggledCollapse: false
function findParentFlickable() {
let p = root.parent;
while (p) {
if (p.hasOwnProperty("contentY") && p.hasOwnProperty("contentItem")) {
return p;
}
p = p.parent;
}
return null;
}
Component.onCompleted: {
if (!settingKey)
return;
@@ -61,7 +51,7 @@ StyledRect {
Qt.callLater(() => {
if (!root.parent)
return;
var flickable = findParentFlickable();
var flickable = QmlUtils.findParentFlickable(root.parent);
if (flickable)
SettingsSearchService.registerCard(key, root, flickable);
});
@@ -4,6 +4,7 @@ import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
DankDropdown {
id: root
@@ -35,16 +36,6 @@ DankDropdown {
}
}
function findParentFlickable() {
let p = root.parent;
while (p) {
if (p.hasOwnProperty("contentY") && p.hasOwnProperty("contentItem"))
return p;
p = p.parent;
}
return null;
}
Component.onCompleted: {
if (!settingKey)
return;
@@ -52,7 +43,7 @@ DankDropdown {
Qt.callLater(() => {
if (!root.parent)
return;
var flickable = findParentFlickable();
var flickable = QmlUtils.findParentFlickable(root.parent);
if (flickable)
SettingsSearchService.registerCard(key, root, flickable);
});
@@ -4,6 +4,7 @@ import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
StyledRect {
id: root
@@ -36,16 +37,6 @@ StyledRect {
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
function findParentFlickable() {
let p = root.parent;
while (p) {
if (p.hasOwnProperty("contentY") && p.hasOwnProperty("contentItem"))
return p;
p = p.parent;
}
return null;
}
Component.onCompleted: {
if (!settingKey)
return;
@@ -53,7 +44,7 @@ StyledRect {
Qt.callLater(() => {
if (!root.parent)
return;
const flickable = findParentFlickable();
const flickable = QmlUtils.findParentFlickable(root.parent);
if (flickable)
SettingsSearchService.registerCard(key, root, flickable);
});
@@ -4,6 +4,7 @@ import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
Item {
id: root
@@ -20,16 +21,6 @@ Item {
readonly property bool isHighlighted: settingKey !== "" && SettingsSearchService.highlightSection === settingKey
function findParentFlickable() {
let p = root.parent;
while (p) {
if (p.hasOwnProperty("contentY") && p.hasOwnProperty("contentItem"))
return p;
p = p.parent;
}
return null;
}
Component.onCompleted: {
if (!settingKey)
return;
@@ -37,7 +28,7 @@ Item {
Qt.callLater(() => {
if (!root.parent)
return;
var flickable = findParentFlickable();
var flickable = QmlUtils.findParentFlickable(root.parent);
if (flickable)
SettingsSearchService.registerCard(key, root, flickable);
});
@@ -4,6 +4,7 @@ import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
StyledRect {
id: root
@@ -30,16 +31,6 @@ StyledRect {
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
function findParentFlickable() {
let p = root.parent;
while (p) {
if (p.hasOwnProperty("contentY") && p.hasOwnProperty("contentItem"))
return p;
p = p.parent;
}
return null;
}
Component.onCompleted: {
if (!settingKey)
return;
@@ -47,7 +38,7 @@ StyledRect {
Qt.callLater(() => {
if (!root.parent)
return;
const flickable = findParentFlickable();
const flickable = QmlUtils.findParentFlickable(root.parent);
if (flickable)
SettingsSearchService.registerCard(key, root, flickable);
});
@@ -4,6 +4,7 @@ import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import "../../../Common/QmlUtils.js" as QmlUtils
DankToggle {
id: root
@@ -19,16 +20,6 @@ DankToggle {
width: parent?.width ?? 0
function findParentFlickable() {
let p = root.parent;
while (p) {
if (p.hasOwnProperty("contentY") && p.hasOwnProperty("contentItem"))
return p;
p = p.parent;
}
return null;
}
Component.onCompleted: {
if (!settingKey)
return;
@@ -36,7 +27,7 @@ DankToggle {
Qt.callLater(() => {
if (!root.parent)
return;
var flickable = findParentFlickable();
var flickable = QmlUtils.findParentFlickable(root.parent);
if (flickable)
SettingsSearchService.registerCard(key, root, flickable);
});
@@ -207,7 +207,7 @@ Item {
iconName: "label"
title: I18n.tr("Named Workspace Icons")
settingKey: "workspaceIcons"
visible: SettingsData.hasNamedWorkspaces()
visible: NiriService.hasNamedWorkspaces()
StyledText {
width: parent.width
@@ -218,7 +218,7 @@ Item {
}
Repeater {
model: SettingsData.getNamedWorkspaces()
model: NiriService.getNamedWorkspaces()
Rectangle {
width: parent.width
+3 -28
View File
@@ -114,7 +114,7 @@ Variants {
}
property real transitionProgress: 0
property real shaderFillMode: getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
property real shaderFillMode: Theme.getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
property vector4d fillColor: Qt.vector4d(0, 0, 0, 1)
property real edgeSmoothness: 0.1
@@ -321,31 +321,6 @@ Variants {
Qt.callLater(() => root.changeWallpaper(pending, true));
}
function getFillMode(modeName) {
switch (modeName) {
case "Scrolling":
return Image.PreserveAspectCrop;
case "Stretch":
return Image.Stretch;
case "Fit":
case "PreserveAspectFit":
return Image.PreserveAspectFit;
case "Fill":
case "PreserveAspectCrop":
return Image.PreserveAspectCrop;
case "Tile":
return Image.Tile;
case "TileVertically":
return Image.TileVertically;
case "TileHorizontally":
return Image.TileHorizontally;
case "Pad":
return Image.Pad;
default:
return Image.PreserveAspectCrop;
}
}
function updateWorkspaceData() {
if (!scrollingEnabled)
return;
@@ -740,7 +715,7 @@ Variants {
cache: true
sourceSize: Qt.size(root.textureWidth, root.textureHeight)
fillMode: root.getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
fillMode: Theme.getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
onStatusChanged: {
if (status === Image.Error) {
@@ -772,7 +747,7 @@ Variants {
cache: true
sourceSize: Qt.size(root.textureWidth, root.textureHeight)
fillMode: root.getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
fillMode: Theme.getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
onStatusChanged: {
if (status === Image.Error) {
@@ -218,13 +218,21 @@ Scope {
}
if (event.key === Qt.Key_Up) {
NiriService.moveWorkspaceUp();
NiriService.send({
"Action": {
"FocusWorkspaceUp": {}
}
});
event.accepted = true;
return;
}
if (event.key === Qt.Key_Down) {
NiriService.moveWorkspaceDown();
NiriService.send({
"Action": {
"FocusWorkspaceDown": {}
}
});
event.accepted = true;
return;
}