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:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user