mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30dc63c801 | ||
|
|
8db7b8419a | ||
|
|
8c626b20e1 | ||
|
|
a8929c8046 | ||
|
|
f8e4b5e958 | ||
|
|
58cae24157 | ||
|
|
bb4f5f37cc | ||
|
|
237333941a | ||
|
|
e1406875aa | ||
|
|
6ab96898a3 | ||
|
|
7973b2f3da | ||
|
|
90284625af | ||
|
|
6b3442512a | ||
|
|
f9208136af | ||
|
|
9895fbde0d | ||
|
|
30370e4985 | ||
|
|
7a45f370b5 | ||
|
|
38068eeaac | ||
|
|
62df30ed6c | ||
|
|
7e75c9e510 | ||
|
|
42f9edf566 | ||
|
|
c72b6144a5 | ||
|
|
032777e32e | ||
|
|
607b5320fd | ||
|
|
959766b265 | ||
|
|
9774991b56 | ||
|
|
e1587995d0 | ||
|
|
08e6e22046 | ||
|
|
454d8bdc88 | ||
|
|
d0ae7431eb | ||
|
|
d88dc17b21 | ||
|
|
705f569571 | ||
|
|
abc7badfa9 | ||
|
|
e8c2469227 | ||
|
|
1e5e8cd246 | ||
|
|
adc81cfb95 | ||
|
|
e175fa64cb | ||
|
|
f9994d0e42 | ||
|
|
3e5d1c514a | ||
|
|
6310394034 | ||
|
|
f32596053b | ||
|
|
cf4a6969d3 | ||
|
|
0918412916 | ||
|
|
41b1718587 | ||
|
|
ca2acbc704 | ||
|
|
1abd3ef8b1 | ||
|
|
cedba3770c | ||
|
|
f733be1fd1 | ||
|
|
01e02232d7 | ||
|
|
771920b38b | ||
|
|
0f55bbc148 | ||
|
|
ab4e9646ad | ||
|
|
884b73599a | ||
|
|
492c0e7ef7 | ||
|
|
0865ae000b | ||
|
|
049c9b44e4 | ||
|
|
199edd3771 | ||
|
|
8806217d25 |
87
.github/workflows/copr-release.yml
vendored
87
.github/workflows/copr-release.yml
vendored
@@ -1,11 +1,10 @@
|
||||
name: DMS Copr Stable Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
release:
|
||||
types: [published]
|
||||
workflow_run:
|
||||
workflows: ["Create Release from DMS"]
|
||||
types: [completed]
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
@@ -16,7 +15,8 @@ on:
|
||||
jobs:
|
||||
build-and-upload:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
@@ -27,20 +27,14 @@ jobs:
|
||||
if [ -n "${{ github.event.inputs.version }}" ]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
echo "Using manual version: $VERSION"
|
||||
elif [ "${{ github.event_name }}" = "release" ]; then
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
echo "Using release version: $VERSION"
|
||||
elif [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||
VERSION="${{ github.ref_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
echo "Using tag version: $VERSION"
|
||||
elif [ "${{ github.event_name }}" = "workflow_run" ]; then
|
||||
VERSION=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||
echo "Using latest release version from workflow_run: $VERSION"
|
||||
else
|
||||
# Fallback to latest release
|
||||
VERSION=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||
VERSION=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||
echo "Using latest release version: $VERSION"
|
||||
fi
|
||||
|
||||
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "✅ Building DMS stable version: $VERSION"
|
||||
|
||||
@@ -94,11 +88,9 @@ jobs:
|
||||
BuildRequires: wget
|
||||
|
||||
Requires: (quickshell or quickshell-git)
|
||||
Requires: accountsservice
|
||||
Requires: dms-cli
|
||||
Requires: dgop
|
||||
Requires: fira-code-fonts
|
||||
Requires: material-symbols-fonts
|
||||
Requires: rsms-inter-fonts
|
||||
|
||||
Recommends: brightnessctl
|
||||
Recommends: cava
|
||||
@@ -179,18 +171,59 @@ jobs:
|
||||
install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms
|
||||
install -Dm755 %{_builddir}/dgop %{buildroot}%{_bindir}/dgop
|
||||
|
||||
install -dm755 %{buildroot}%{_sysconfdir}/xdg/quickshell/dms
|
||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.github
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/*.spec
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/*.spec
|
||||
|
||||
%posttrans
|
||||
# Clean up old installation path from previous versions (only if empty)
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||
# Remove directories only if empty (preserves any user-added files)
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Restart DMS for active users after upgrade
|
||||
if [ "$1" -ge 2 ]; then
|
||||
# Find all quickshell DMS processes (PID and username)
|
||||
while read pid cmd; do
|
||||
username=$(ps -o user= -p "$pid" 2>/dev/null)
|
||||
|
||||
[ "$username" = "root" ] && continue
|
||||
[ -z "$username" ] && continue
|
||||
|
||||
# Get user's UID and validate session
|
||||
user_uid=$(id -u "$username" 2>/dev/null)
|
||||
[ -z "$user_uid" ] && continue
|
||||
[ ! -d "/run/user/$user_uid" ] && continue
|
||||
|
||||
wayland_display=$(tr '\0' '\n' < /proc/$pid/environ 2>/dev/null | grep '^WAYLAND_DISPLAY=' | cut -d= -f2)
|
||||
[ -z "$wayland_display" ] && continue
|
||||
|
||||
echo "Restarting DMS for user: $username"
|
||||
|
||||
# Run as user with full Wayland session environment
|
||||
runuser -u "$username" -- /bin/sh -c "
|
||||
export XDG_RUNTIME_DIR=/run/user/$user_uid
|
||||
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$user_uid/bus
|
||||
export WAYLAND_DISPLAY=$wayland_display
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:\$PATH
|
||||
dms restart >/dev/null 2>&1
|
||||
" 2>/dev/null || true
|
||||
|
||||
break
|
||||
done < <(pgrep -a -f 'quickshell.*dms' 2>/dev/null)
|
||||
fi
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc README.md CONTRIBUTING.md
|
||||
%{_sysconfdir}/xdg/quickshell/dms/
|
||||
%{_datadir}/quickshell/dms/
|
||||
|
||||
%files -n dms-cli
|
||||
%{_bindir}/dms
|
||||
|
||||
1
.github/workflows/poeditor-export.yml
vendored
1
.github/workflows/poeditor-export.yml
vendored
@@ -113,6 +113,7 @@ jobs:
|
||||
"ja:translations/poexports/ja.json"
|
||||
"zh-Hans:translations/poexports/zh_CN.json"
|
||||
"pt-br:translations/poexports/pt.json"
|
||||
"tr:translations/poexports/tr.json"
|
||||
)
|
||||
|
||||
ANY_CHANGED=false
|
||||
|
||||
26
.github/workflows/release.yml
vendored
26
.github/workflows/release.yml
vendored
@@ -49,9 +49,9 @@ jobs:
|
||||
set -e
|
||||
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "")
|
||||
if [ -z "$PREVIOUS_TAG" ]; then
|
||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" | head -50)
|
||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" --author='^(?!github-actions\[bot\])' | head -50)
|
||||
else
|
||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${TAG}")
|
||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" --author='^(?!github-actions\[bot\])' "${PREVIOUS_TAG}..${TAG}")
|
||||
fi
|
||||
|
||||
cat > RELEASE_BODY.md << 'EOF'
|
||||
@@ -219,25 +219,3 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract plain version
|
||||
id: plain_version
|
||||
run: |
|
||||
set -e
|
||||
if [[ "$TAG" == v* ]]; then
|
||||
VERSION="${TAG#v}"
|
||||
else
|
||||
VERSION="$TAG"
|
||||
fi
|
||||
echo "plain=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Trigger Copr release workflow
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${{ steps.plain_version.outputs.plain }}"
|
||||
JSON_PAYLOAD=$(jq -n --arg version "$VERSION" '{inputs:{version:$version}}')
|
||||
curl -X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/actions/workflows/copr-release.yml/dispatches \
|
||||
-d "$JSON_PAYLOAD"
|
||||
|
||||
@@ -66,6 +66,8 @@ Singleton {
|
||||
property int animationSpeed: SettingsData.AnimationSpeed.Short
|
||||
property int customAnimationDuration: 500
|
||||
property string wallpaperFillMode: "Fill"
|
||||
property bool blurredWallpaperLayer: false
|
||||
property bool blurWallpaperOnOverview: false
|
||||
|
||||
property bool showLauncherButton: true
|
||||
property bool showWorkspaceSwitcher: true
|
||||
@@ -269,7 +271,6 @@ Singleton {
|
||||
Component.onCompleted: {
|
||||
if (!isGreeterMode) {
|
||||
loadSettings()
|
||||
fontCheckTimer.start()
|
||||
initializeListModels()
|
||||
fprintdDetectionProcess.running = true
|
||||
}
|
||||
@@ -497,6 +498,8 @@ Singleton {
|
||||
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({})
|
||||
showOnLastDisplay = settings.showOnLastDisplay !== undefined ? settings.showOnLastDisplay : ({})
|
||||
wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill"
|
||||
blurredWallpaperLayer = settings.blurredWallpaperLayer !== undefined ? settings.blurredWallpaperLayer : false
|
||||
blurWallpaperOnOverview = settings.blurWallpaperOnOverview !== undefined ? settings.blurWallpaperOnOverview : false
|
||||
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : SettingsData.AnimationSpeed.Short
|
||||
customAnimationDuration = settings.customAnimationDuration !== undefined ? settings.customAnimationDuration : 500
|
||||
acMonitorTimeout = settings.acMonitorTimeout !== undefined ? settings.acMonitorTimeout : 0
|
||||
@@ -663,6 +666,8 @@ Singleton {
|
||||
"widgetBackgroundColor": widgetBackgroundColor,
|
||||
"surfaceBase": surfaceBase,
|
||||
"wallpaperFillMode": wallpaperFillMode,
|
||||
"blurredWallpaperLayer": blurredWallpaperLayer,
|
||||
"blurWallpaperOnOverview": blurWallpaperOnOverview,
|
||||
"notificationTimeoutLow": notificationTimeoutLow,
|
||||
"notificationTimeoutNormal": notificationTimeoutNormal,
|
||||
"notificationTimeoutCritical": notificationTimeoutCritical,
|
||||
@@ -741,7 +746,7 @@ Singleton {
|
||||
"dankBarBorderOpacity", "dankBarBorderThickness", "popupGapsAuto", "popupGapsManual",
|
||||
"dankBarPosition", "lockScreenShowPowerActions", "enableFprint", "maxFprintTries",
|
||||
"hideBrightnessSlider", "widgetBackgroundColor", "surfaceBase", "wallpaperFillMode",
|
||||
"notificationTimeoutLow", "notificationTimeoutNormal", "notificationTimeoutCritical",
|
||||
"blurredWallpaperLayer", "blurWallpaperOnOverview", "notificationTimeoutLow", "notificationTimeoutNormal", "notificationTimeoutCritical",
|
||||
"notificationPopupPosition", "osdAlwaysShowValue", "powerActionConfirm",
|
||||
"customPowerActionLock", "customPowerActionLogout", "customPowerActionSuspend",
|
||||
"customPowerActionHibernate", "customPowerActionReboot", "customPowerActionPowerOff",
|
||||
@@ -1088,6 +1093,16 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setBlurredWallpaperLayer(enabled) {
|
||||
blurredWallpaperLayer = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setBlurWallpaperOnOverview(enabled) {
|
||||
blurWallpaperOnOverview = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setShowLauncherButton(enabled) {
|
||||
showLauncherButton = enabled
|
||||
saveSettings()
|
||||
@@ -1868,27 +1883,6 @@ Singleton {
|
||||
id: rightWidgetsModel
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: fontCheckTimer
|
||||
|
||||
interval: 3000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
var availableFonts = Qt.fontFamilies()
|
||||
var missingFonts = []
|
||||
if (fontFamily === defaultFontFamily && !availableFonts.includes(defaultFontFamily))
|
||||
missingFonts.push(defaultFontFamily)
|
||||
|
||||
if (monoFontFamily === defaultMonoFontFamily && !availableFonts.includes(defaultMonoFontFamily))
|
||||
missingFonts.push(defaultMonoFontFamily)
|
||||
|
||||
if (missingFonts.length > 0) {
|
||||
var message = "Missing fonts: " + missingFonts.join(", ") + ". Using system defaults."
|
||||
ToastService.showWarning(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Process testNotificationProcess
|
||||
|
||||
testNotificationProcess: Process {
|
||||
|
||||
12
DMSShell.qml
12
DMSShell.qml
@@ -52,6 +52,14 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: blurredWallpaperBackgroundLoader
|
||||
active: SettingsData.blurredWallpaperLayer
|
||||
asynchronous: false
|
||||
|
||||
sourceComponent: BlurredWallpaperBackground {}
|
||||
}
|
||||
|
||||
WallpaperBackground {}
|
||||
|
||||
Lock {
|
||||
@@ -208,8 +216,8 @@ Item {
|
||||
Connections {
|
||||
target: NetworkService
|
||||
|
||||
function onCredentialsNeeded(token, ssid, setting, fields, hints, reason) {
|
||||
wifiPasswordModal.showFromPrompt(token, ssid, setting, fields, hints, reason)
|
||||
function onCredentialsNeeded(token, ssid, setting, fields, hints, reason, connType, connName, vpnService) {
|
||||
wifiPasswordModal.showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ DankModal {
|
||||
property var promptFields: []
|
||||
property string promptSetting: ""
|
||||
|
||||
property bool isVpnPrompt: false
|
||||
property string connectionName: ""
|
||||
property string vpnServiceType: ""
|
||||
property string connectionType: ""
|
||||
|
||||
function show(ssid) {
|
||||
wifiPasswordSSID = ssid
|
||||
wifiPasswordInput = ""
|
||||
@@ -32,6 +37,10 @@ DankModal {
|
||||
promptReason = ""
|
||||
promptFields = []
|
||||
promptSetting = ""
|
||||
isVpnPrompt = false
|
||||
connectionName = ""
|
||||
vpnServiceType = ""
|
||||
connectionType = ""
|
||||
|
||||
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid)
|
||||
requiresEnterprise = network?.enterprise || false
|
||||
@@ -48,13 +57,18 @@ DankModal {
|
||||
})
|
||||
}
|
||||
|
||||
function showFromPrompt(token, ssid, setting, fields, hints, reason) {
|
||||
wifiPasswordSSID = ssid
|
||||
function showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService) {
|
||||
isPromptMode = true
|
||||
promptToken = token
|
||||
promptReason = reason
|
||||
promptFields = fields || []
|
||||
promptSetting = setting || "802-11-wireless-security"
|
||||
connectionType = connType || "802-11-wireless"
|
||||
connectionName = connName || ssid || ""
|
||||
vpnServiceType = vpnService || ""
|
||||
|
||||
isVpnPrompt = (connectionType === "vpn" || connectionType === "wireguard")
|
||||
wifiPasswordSSID = isVpnPrompt ? connectionName : ssid
|
||||
|
||||
requiresEnterprise = setting === "802-1x"
|
||||
|
||||
@@ -163,7 +177,12 @@ DankModal {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Connect to Wi-Fi")
|
||||
text: {
|
||||
if (isVpnPrompt) {
|
||||
return I18n.tr("Connect to VPN")
|
||||
}
|
||||
return I18n.tr("Connect to Wi-Fi")
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
@@ -175,6 +194,9 @@ DankModal {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (isVpnPrompt) {
|
||||
return I18n.tr("Enter password for ") + wifiPasswordSSID
|
||||
}
|
||||
const prefix = requiresEnterprise ? I18n.tr("Enter credentials for ") : I18n.tr("Enter password for ")
|
||||
return prefix + wifiPasswordSSID
|
||||
}
|
||||
@@ -218,7 +240,7 @@ DankModal {
|
||||
color: Theme.surfaceHover
|
||||
border.color: usernameInput.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||
border.width: usernameInput.activeFocus ? 2 : 1
|
||||
visible: requiresEnterprise
|
||||
visible: requiresEnterprise && !isVpnPrompt
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
@@ -271,7 +293,7 @@ DankModal {
|
||||
textColor: Theme.surfaceText
|
||||
text: wifiPasswordInput
|
||||
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
|
||||
placeholderText: requiresEnterprise ? I18n.tr("Password") : ""
|
||||
placeholderText: (requiresEnterprise && !isVpnPrompt) ? I18n.tr("Password") : ""
|
||||
backgroundColor: "transparent"
|
||||
focus: !requiresEnterprise
|
||||
enabled: root.shouldBeVisible
|
||||
@@ -281,7 +303,9 @@ DankModal {
|
||||
onAccepted: () => {
|
||||
if (isPromptMode) {
|
||||
const secrets = {}
|
||||
if (promptSetting === "802-11-wireless-security") {
|
||||
if (isVpnPrompt) {
|
||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||
} else if (promptSetting === "802-11-wireless-security") {
|
||||
secrets["psk"] = passwordInput.text
|
||||
} else if (promptSetting === "802-1x") {
|
||||
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
||||
@@ -340,7 +364,7 @@ DankModal {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: requiresEnterprise
|
||||
visible: requiresEnterprise && !isVpnPrompt
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
@@ -372,7 +396,7 @@ DankModal {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: requiresEnterprise
|
||||
visible: requiresEnterprise && !isVpnPrompt
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
@@ -495,7 +519,12 @@ DankModal {
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||
enabled: requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0
|
||||
enabled: {
|
||||
if (isVpnPrompt) {
|
||||
return passwordInput.text.length > 0
|
||||
}
|
||||
return requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0
|
||||
}
|
||||
opacity: enabled ? 1 : 0.5
|
||||
|
||||
StyledText {
|
||||
@@ -518,7 +547,9 @@ DankModal {
|
||||
onClicked: () => {
|
||||
if (isPromptMode) {
|
||||
const secrets = {}
|
||||
if (promptSetting === "802-11-wireless-security") {
|
||||
if (isVpnPrompt) {
|
||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||
} else if (promptSetting === "802-11-wireless-security") {
|
||||
secrets["psk"] = passwordInput.text
|
||||
} else if (promptSetting === "802-1x") {
|
||||
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
||||
|
||||
135
Modules/BlurredWallpaperBackground.qml
Normal file
135
Modules/BlurredWallpaperBackground.qml
Normal file
@@ -0,0 +1,135 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
import qs.Modules
|
||||
|
||||
Variants {
|
||||
model: {
|
||||
if (SessionData.isGreeterMode) {
|
||||
return Quickshell.screens
|
||||
}
|
||||
return SettingsData.getFilteredScreens("wallpaper")
|
||||
}
|
||||
|
||||
PanelWindow {
|
||||
id: blurWallpaperWindow
|
||||
|
||||
required property var modelData
|
||||
|
||||
screen: modelData
|
||||
|
||||
WlrLayershell.layer: WlrLayer.Background
|
||||
WlrLayershell.namespace: "dms:blurwallpaper"
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
anchors.top: true
|
||||
anchors.bottom: true
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
|
||||
color: "transparent"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
|
||||
property string source: SessionData.getMonitorWallpaper(modelData.name) || ""
|
||||
property bool isColorSource: source.startsWith("#")
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onIsLightModeChanged() {
|
||||
if (SessionData.perModeWallpaper) {
|
||||
var newSource = SessionData.getMonitorWallpaper(modelData.name) || ""
|
||||
if (newSource !== root.source) {
|
||||
root.source = newSource
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
WallpaperEngineProc {
|
||||
id: weProc
|
||||
monitor: modelData.name
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (source) {
|
||||
const formattedSource = source.startsWith("file://") ? source : "file://" + source
|
||||
wallpaperImage.source = formattedSource
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
weProc.stop()
|
||||
}
|
||||
|
||||
onSourceChanged: {
|
||||
const isWE = source.startsWith("we:")
|
||||
const isColor = source.startsWith("#")
|
||||
|
||||
if (isWE) {
|
||||
wallpaperImage.source = ""
|
||||
weProc.start(source.substring(3))
|
||||
} else {
|
||||
weProc.stop()
|
||||
if (!source) {
|
||||
wallpaperImage.source = ""
|
||||
} else if (isColor) {
|
||||
wallpaperImage.source = ""
|
||||
} else {
|
||||
wallpaperImage.source = source.startsWith("file://") ? source : "file://" + source
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: !root.source || root.isColorSource
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: DankBackdrop {
|
||||
screenName: modelData.name
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: wallpaperImage
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
asynchronous: true
|
||||
smooth: true
|
||||
cache: true
|
||||
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: wallpaperImage
|
||||
blurEnabled: true
|
||||
blur: 0.8
|
||||
blurMax: 48
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,26 +10,26 @@ PluginComponent {
|
||||
id: root
|
||||
|
||||
Ref {
|
||||
service: VpnService
|
||||
service: DMSNetworkService
|
||||
}
|
||||
|
||||
ccWidgetIcon: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
|
||||
ccWidgetIcon: DMSNetworkService.isBusy ? "sync" : (DMSNetworkService.connected ? "vpn_lock" : "vpn_key_off")
|
||||
ccWidgetPrimaryText: "VPN"
|
||||
ccWidgetSecondaryText: {
|
||||
if (!VpnService.connected)
|
||||
if (!DMSNetworkService.connected)
|
||||
return "Disconnected"
|
||||
const names = VpnService.activeNames || []
|
||||
const names = DMSNetworkService.activeNames || []
|
||||
if (names.length <= 1)
|
||||
return names[0] || "Connected"
|
||||
return names[0] + " +" + (names.length - 1)
|
||||
}
|
||||
ccWidgetIsActive: VpnService.connected
|
||||
ccWidgetIsActive: DMSNetworkService.connected
|
||||
|
||||
onCcWidgetToggled: {
|
||||
if (VpnService.connected) {
|
||||
VpnService.disconnectAllActive()
|
||||
} else if (VpnService.profiles.length > 0) {
|
||||
VpnService.connect(VpnService.profiles[0].uuid)
|
||||
if (DMSNetworkService.connected) {
|
||||
DMSNetworkService.disconnectAllActive()
|
||||
} else if (DMSNetworkService.profiles.length > 0) {
|
||||
DMSNetworkService.connect(DMSNetworkService.profiles[0].uuid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,9 +52,9 @@ PluginComponent {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (!VpnService.connected)
|
||||
if (!DMSNetworkService.connected)
|
||||
return "Active: None"
|
||||
const names = VpnService.activeNames || []
|
||||
const names = DMSNetworkService.activeNames || []
|
||||
if (names.length <= 1)
|
||||
return "Active: " + (names[0] || "VPN")
|
||||
return "Active: " + names[0] + " +" + (names.length - 1)
|
||||
@@ -72,7 +72,7 @@ PluginComponent {
|
||||
height: 28
|
||||
radius: 14
|
||||
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
||||
visible: VpnService.connected
|
||||
visible: DMSNetworkService.connected
|
||||
width: 110
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
|
||||
@@ -99,7 +99,7 @@ PluginComponent {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: VpnService.disconnectAllActive()
|
||||
onClicked: DMSNetworkService.disconnectAllActive()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ PluginComponent {
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: VpnService.profiles.length === 0 ? 120 : 0
|
||||
height: DMSNetworkService.profiles.length === 0 ? 120 : 0
|
||||
visible: height > 0
|
||||
|
||||
Column {
|
||||
@@ -154,7 +154,7 @@ PluginComponent {
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: VpnService.profiles
|
||||
model: DMSNetworkService.profiles
|
||||
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
@@ -162,9 +162,9 @@ PluginComponent {
|
||||
width: parent ? parent.width : 300
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (VpnService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||
border.width: VpnService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||
border.color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||
border.width: DMSNetworkService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||
border.color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
@@ -174,9 +174,9 @@ PluginComponent {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: VpnService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||
name: DMSNetworkService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||
size: Theme.iconSize - 4
|
||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ PluginComponent {
|
||||
StyledText {
|
||||
text: modelData.name
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -234,7 +234,7 @@ PluginComponent {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: VpnService.toggle(modelData.uuid)
|
||||
onClicked: DMSNetworkService.toggle(modelData.uuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +332,10 @@ Column {
|
||||
return "Select device"
|
||||
if (AudioService.sink.audio.muted)
|
||||
return "Muted"
|
||||
return Math.round(AudioService.sink.audio.volume * 100) + "%"
|
||||
const volume = AudioService.sink.audio.volume
|
||||
if (typeof volume !== "number" || isNaN(volume))
|
||||
return "0%"
|
||||
return Math.round(volume * 100) + "%"
|
||||
}
|
||||
case "audioInput":
|
||||
{
|
||||
@@ -340,7 +343,10 @@ Column {
|
||||
return "Select device"
|
||||
if (AudioService.source.audio.muted)
|
||||
return "Muted"
|
||||
return Math.round(AudioService.source.audio.volume * 100) + "%"
|
||||
const volume = AudioService.source.audio.volume
|
||||
if (typeof volume !== "number" || isNaN(volume))
|
||||
return "0%"
|
||||
return Math.round(volume * 100) + "%"
|
||||
}
|
||||
default:
|
||||
return widgetDef?.description || ""
|
||||
|
||||
@@ -34,6 +34,10 @@ Rectangle {
|
||||
return 1
|
||||
}
|
||||
|
||||
if (NetworkService.backend !== "networkmanager" || DMSService.apiVersion <= 10) {
|
||||
return 1
|
||||
}
|
||||
|
||||
const pref = NetworkService.userPreference
|
||||
const status = NetworkService.networkStatus
|
||||
let index = 1
|
||||
@@ -76,7 +80,7 @@ Rectangle {
|
||||
DankButtonGroup {
|
||||
id: preferenceControls
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: DMSService.apiVersion >= 5
|
||||
visible: NetworkService.backend === "networkmanager" && DMSService.apiVersion > 10
|
||||
|
||||
model: ["Ethernet", "WiFi"]
|
||||
currentIndex: currentPreferenceIndex
|
||||
@@ -196,7 +200,7 @@ Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Theme.spacingM
|
||||
anchors.topMargin: Theme.spacingM
|
||||
visible: currentPreferenceIndex === 0 && DMSService.apiVersion >= 5
|
||||
visible: currentPreferenceIndex === 0 && NetworkService.backend === "networkmanager" && DMSService.apiVersion > 10
|
||||
contentHeight: wiredColumn.height
|
||||
clip: true
|
||||
|
||||
|
||||
@@ -143,8 +143,8 @@ QtObject {
|
||||
"description": "VPN connections",
|
||||
"icon": "vpn_key",
|
||||
"type": "builtin_plugin",
|
||||
"enabled": VpnService.available,
|
||||
"warning": !VpnService.available ? "VPN not available" : undefined,
|
||||
"enabled": DMSNetworkService.available,
|
||||
"warning": !DMSNetworkService.available ? "VPN not available" : undefined,
|
||||
"isBuiltinPlugin": true
|
||||
}]
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
property var defaultSource: AudioService.source
|
||||
|
||||
iconName: {
|
||||
if (!defaultSource) return "mic_off"
|
||||
|
||||
let volume = defaultSource.audio.volume
|
||||
let muted = defaultSource.audio.muted
|
||||
|
||||
if (muted || volume === 0.0) return "mic_off"
|
||||
return "mic"
|
||||
}
|
||||
|
||||
isActive: defaultSource && !defaultSource.audio.muted
|
||||
|
||||
primaryText: {
|
||||
if (!defaultSource) {
|
||||
return "No input device"
|
||||
}
|
||||
return defaultSource.description || "Audio Input"
|
||||
}
|
||||
|
||||
secondaryText: {
|
||||
if (!defaultSource) {
|
||||
return "Select device"
|
||||
}
|
||||
if (defaultSource.audio.muted) {
|
||||
return "Muted"
|
||||
}
|
||||
return Math.round(defaultSource.audio.volume * 100) + "%"
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
if (defaultSource && defaultSource.audio) {
|
||||
defaultSource.audio.muted = !defaultSource.audio.muted
|
||||
}
|
||||
}
|
||||
|
||||
onWheelEvent: function (wheelEvent) {
|
||||
if (!defaultSource || !defaultSource.audio) return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
let currentVolume = defaultSource.audio.volume * 100
|
||||
let newVolume
|
||||
if (delta > 0)
|
||||
newVolume = Math.min(100, currentVolume + 5)
|
||||
else
|
||||
newVolume = Math.max(0, currentVolume - 5)
|
||||
defaultSource.audio.muted = false
|
||||
defaultSource.audio.volume = newVolume / 100
|
||||
wheelEvent.accepted = true
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
property var defaultSink: AudioService.sink
|
||||
|
||||
iconName: {
|
||||
if (!defaultSink) return "volume_off"
|
||||
|
||||
let volume = defaultSink.audio.volume
|
||||
let muted = defaultSink.audio.muted
|
||||
|
||||
if (muted || volume === 0.0) return "volume_off"
|
||||
if (volume <= 0.33) return "volume_down"
|
||||
if (volume <= 0.66) return "volume_up"
|
||||
return "volume_up"
|
||||
}
|
||||
|
||||
isActive: defaultSink && !defaultSink.audio.muted
|
||||
|
||||
primaryText: {
|
||||
if (!defaultSink) {
|
||||
return "No output device"
|
||||
}
|
||||
return defaultSink.description || "Audio Output"
|
||||
}
|
||||
|
||||
secondaryText: {
|
||||
if (!defaultSink) {
|
||||
return "Select device"
|
||||
}
|
||||
if (defaultSink.audio.muted) {
|
||||
return "Muted"
|
||||
}
|
||||
return Math.round(defaultSink.audio.volume * 100) + "%"
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
if (defaultSink && defaultSink.audio) {
|
||||
defaultSink.audio.muted = !defaultSink.audio.muted
|
||||
}
|
||||
}
|
||||
|
||||
onWheelEvent: function (wheelEvent) {
|
||||
if (!defaultSink || !defaultSink.audio) return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
let currentVolume = defaultSink.audio.volume * 100
|
||||
let newVolume
|
||||
if (delta > 0)
|
||||
newVolume = Math.min(100, currentVolume + 5)
|
||||
else
|
||||
newVolume = Math.max(0, currentVolume - 5)
|
||||
defaultSink.audio.muted = false
|
||||
defaultSink.audio.volume = newVolume / 100
|
||||
AudioService.volumeChanged()
|
||||
wheelEvent.accepted = true
|
||||
}
|
||||
}
|
||||
@@ -372,9 +372,6 @@ Item {
|
||||
}
|
||||
item.widthChanged.connect(() => layoutTimer.restart())
|
||||
item.heightChanged.connect(() => layoutTimer.restart())
|
||||
if (model.widgetId === "spacer") {
|
||||
item.spacerSize = Qt.binding(() => model.size || 20)
|
||||
}
|
||||
if (root.axis && "axis" in item) {
|
||||
item.axis = Qt.binding(() => root.axis)
|
||||
}
|
||||
|
||||
@@ -200,11 +200,6 @@ Item {
|
||||
property var nativeInhibitor: null
|
||||
|
||||
Component.onCompleted: {
|
||||
const fonts = Qt.fontFamilies()
|
||||
if (fonts.indexOf("Material Symbols Rounded") === -1) {
|
||||
ToastService.showError("Please install Material Symbols Rounded and Restart your Shell. See README.md for instructions")
|
||||
}
|
||||
|
||||
if (SettingsData.forceStatusBarLayoutRefresh) {
|
||||
SettingsData.forceStatusBarLayoutRefresh.connect(() => {
|
||||
Qt.callLater(() => {
|
||||
@@ -1239,13 +1234,19 @@ Item {
|
||||
Component {
|
||||
id: separatorComponent
|
||||
|
||||
Rectangle {
|
||||
width: barWindow.isVertical ? barWindow.widgetThickness * 0.67 : 1
|
||||
height: barWindow.isVertical ? 1 : barWindow.widgetThickness * 0.67
|
||||
Item {
|
||||
width: barWindow.isVertical ? parent.barThickness : 1
|
||||
height: barWindow.isVertical ? 1 : parent.barThickness
|
||||
implicitWidth: width
|
||||
implicitHeight: height
|
||||
color: Theme.outline
|
||||
opacity: 0.3
|
||||
|
||||
Rectangle {
|
||||
width: barWindow.isVertical ? parent.width * 0.6 : 1
|
||||
height: barWindow.isVertical ? 1 : parent.height * 0.6
|
||||
anchors.centerIn: parent
|
||||
color: Theme.outline
|
||||
opacity: 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ DankPopout {
|
||||
id: root
|
||||
|
||||
Ref {
|
||||
service: VpnService
|
||||
service: DMSNetworkService
|
||||
}
|
||||
|
||||
property var triggerScreen: null
|
||||
@@ -161,11 +161,11 @@ DankPopout {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (!VpnService.connected) {
|
||||
if (!DMSNetworkService.connected) {
|
||||
return "Active: None";
|
||||
}
|
||||
|
||||
const names = VpnService.activeNames || [];
|
||||
const names = DMSNetworkService.activeNames || [];
|
||||
if (names.length <= 1) {
|
||||
return "Active: " + (names[0] || "VPN");
|
||||
}
|
||||
@@ -193,7 +193,7 @@ DankPopout {
|
||||
height: 28
|
||||
radius: 14
|
||||
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
||||
visible: VpnService.connected
|
||||
visible: DMSNetworkService.connected
|
||||
width: 130
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
border.width: 0
|
||||
@@ -224,7 +224,7 @@ DankPopout {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: VpnService.disconnectAllActive()
|
||||
onClicked: DMSNetworkService.disconnectAllActive()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -251,7 +251,7 @@ DankPopout {
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: VpnService.profiles.length === 0 ? 120 : 0
|
||||
height: DMSNetworkService.profiles.length === 0 ? 120 : 0
|
||||
visible: height > 0
|
||||
|
||||
Column {
|
||||
@@ -284,7 +284,7 @@ DankPopout {
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: VpnService.profiles
|
||||
model: DMSNetworkService.profiles
|
||||
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
@@ -292,9 +292,9 @@ DankPopout {
|
||||
width: parent ? parent.width : 300
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (VpnService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||
border.width: VpnService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||
border.color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||
border.width: DMSNetworkService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||
border.color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
@@ -304,9 +304,9 @@ DankPopout {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: VpnService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||
name: DMSNetworkService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||
size: Theme.iconSize - 4
|
||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ DankPopout {
|
||||
StyledText {
|
||||
text: modelData.name
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -392,7 +392,7 @@ DankPopout {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: VpnService.toggle(modelData.uuid)
|
||||
onClicked: DMSNetworkService.toggle(modelData.uuid)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ BasePill {
|
||||
StyledText {
|
||||
text: BatteryService.batteryLevel.toString()
|
||||
font.pixelSize: Theme.barTextSize(battery.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: BatteryService.batteryAvailable
|
||||
@@ -87,7 +86,6 @@ BasePill {
|
||||
StyledText {
|
||||
text: `${BatteryService.batteryLevel}%`
|
||||
font.pixelSize: Theme.barTextSize(battery.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: BatteryService.batteryAvailable
|
||||
@@ -101,7 +99,6 @@ BasePill {
|
||||
y: -battery.topMargin
|
||||
width: battery.width + battery.leftMargin + battery.rightMargin
|
||||
height: battery.height + battery.topMargin + battery.bottomMargin
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -37,9 +37,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -54,9 +54,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,18 +68,18 @@ BasePill {
|
||||
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(0)
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(1)
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,18 +92,18 @@ BasePill {
|
||||
text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(0)
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(1)
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,9 +134,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Light
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -149,9 +149,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Light
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,9 +169,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Light
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -184,9 +184,9 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Light
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,28 +198,30 @@ BasePill {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
id: timeText
|
||||
text: {
|
||||
return systemClock?.date?.toLocaleTimeString(Qt.locale(), SettingsData.getEffectiveTimeFormat())
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.baseline: dateText.baseline
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: middleDot
|
||||
text: "•"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.outlineButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.baseline: dateText.baseline
|
||||
visible: !SettingsData.clockCompactMode
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: dateText
|
||||
text: {
|
||||
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0) {
|
||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat)
|
||||
}
|
||||
|
||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d")
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
@@ -241,7 +243,6 @@ BasePill {
|
||||
y: -root.topMargin
|
||||
width: root.width + root.leftMargin + root.rightMargin
|
||||
height: root.height + root.topMargin + root.bottomMargin
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
|
||||
|
||||
@@ -27,7 +27,6 @@ BasePill {
|
||||
MouseArea {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
root.colorPickerRequested()
|
||||
|
||||
@@ -86,7 +86,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
onWheel: function(wheelEvent) {
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
@@ -188,7 +187,6 @@ BasePill {
|
||||
id: audioWheelArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
onWheel: function(wheelEvent) {
|
||||
let delta = wheelEvent.angleDelta.y;
|
||||
@@ -232,7 +230,6 @@ BasePill {
|
||||
y: -root.topMargin
|
||||
width: root.width + root.leftMargin + root.rightMargin
|
||||
height: root.height + root.topMargin + root.bottomMargin
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -59,7 +59,6 @@ BasePill {
|
||||
return DgopService.cpuUsage.toFixed(0);
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -97,7 +96,6 @@ BasePill {
|
||||
return DgopService.cpuUsage.toFixed(0) + "%";
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -106,7 +104,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: cpuBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100%"
|
||||
}
|
||||
|
||||
@@ -125,7 +122,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -59,7 +59,6 @@ BasePill {
|
||||
return Math.round(DgopService.cpuTemperature).toString();
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -97,7 +96,6 @@ BasePill {
|
||||
return Math.round(DgopService.cpuTemperature) + "°";
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -106,7 +104,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: tempBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100°"
|
||||
}
|
||||
|
||||
@@ -125,7 +122,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -10,6 +10,7 @@ BasePill {
|
||||
|
||||
property var widgetData: null
|
||||
property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/"
|
||||
property bool isHovered: mouseArea.containsMouse
|
||||
|
||||
property var selectedMount: {
|
||||
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
|
||||
@@ -114,7 +115,6 @@ BasePill {
|
||||
return root.diskUsagePercent.toFixed(0)
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -149,7 +149,6 @@ BasePill {
|
||||
return root.selectedMount.mount
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -164,7 +163,6 @@ BasePill {
|
||||
return root.diskUsagePercent.toFixed(0) + "%"
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -173,7 +171,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: diskBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100%"
|
||||
}
|
||||
|
||||
@@ -197,6 +194,7 @@ BasePill {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: root.isVerticalOrientation
|
||||
|
||||
@@ -17,6 +17,7 @@ BasePill {
|
||||
readonly property int maxCompactWidth: 288
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
property var activeDesktopEntry: null
|
||||
property bool isHovered: mouseArea.containsMouse
|
||||
|
||||
Component.onCompleted: {
|
||||
updateDesktopEntry()
|
||||
@@ -98,7 +99,7 @@ BasePill {
|
||||
return compactMode ? Math.min(baseWidth, maxCompactWidth - root.horizontalPadding * 2) : Math.min(baseWidth, maxNormalWidth - root.horizontalPadding * 2)
|
||||
}
|
||||
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||
clip: true
|
||||
clip: false
|
||||
|
||||
IconImage {
|
||||
id: appIcon
|
||||
@@ -146,7 +147,6 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: 10
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -166,7 +166,6 @@ BasePill {
|
||||
return desktopEntry && desktopEntry.name ? desktopEntry.name : activeWindow.appId;
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
@@ -203,7 +202,6 @@ BasePill {
|
||||
return title;
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
|
||||
@@ -123,7 +123,6 @@ BasePill {
|
||||
return Math.round(root.displayTemp).toString();
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -161,7 +160,6 @@ BasePill {
|
||||
return Math.round(root.displayTemp) + "°";
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -170,7 +168,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: gpuTempBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100°"
|
||||
}
|
||||
|
||||
@@ -189,7 +186,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -26,7 +26,6 @@ BasePill {
|
||||
MouseArea {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
SessionService.toggleIdleInhibit()
|
||||
|
||||
@@ -42,7 +42,6 @@ BasePill {
|
||||
return root.currentLayout.substring(0, 2).toUpperCase()
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -67,7 +66,6 @@ BasePill {
|
||||
MouseArea {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (CompositorService.isNiri) {
|
||||
|
||||
@@ -84,7 +84,6 @@ BasePill {
|
||||
MouseArea {
|
||||
id: customMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.RightButton
|
||||
onPressed: function (mouse){
|
||||
|
||||
@@ -162,7 +162,7 @@ BasePill {
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: textWidth
|
||||
height: 20
|
||||
height: root.widgetThickness
|
||||
visible: SettingsData.mediaSize > 0
|
||||
clip: true
|
||||
color: "transparent"
|
||||
@@ -176,7 +176,6 @@ BasePill {
|
||||
text: textContainer.displayText
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
wrapMode: Text.NoWrap
|
||||
x: needsScrolling ? -scrollOffset : 0
|
||||
onTextChanged: {
|
||||
@@ -257,7 +256,6 @@ BasePill {
|
||||
id: prevArea
|
||||
anchors.fill: parent
|
||||
enabled: root.playerAvailable
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (activePlayer) {
|
||||
@@ -315,7 +313,6 @@ BasePill {
|
||||
id: nextArea
|
||||
anchors.fill: parent
|
||||
enabled: root.playerAvailable
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (activePlayer) {
|
||||
|
||||
@@ -54,7 +54,6 @@ BasePill {
|
||||
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.info
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -67,7 +66,6 @@ BasePill {
|
||||
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.error
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -99,7 +97,6 @@ BasePill {
|
||||
StyledText {
|
||||
text: DgopService.networkRxRate > 0 ? root.formatNetworkSpeed(DgopService.networkRxRate) : "0 B/s"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -109,7 +106,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: rxBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "88.8 MB/s"
|
||||
}
|
||||
|
||||
@@ -137,7 +133,6 @@ BasePill {
|
||||
StyledText {
|
||||
text: DgopService.networkTxRate > 0 ? root.formatNetworkSpeed(DgopService.networkTxRate) : "0 B/s"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -147,7 +142,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: txBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "88.8 MB/s"
|
||||
}
|
||||
|
||||
|
||||
@@ -47,19 +47,6 @@ BasePill {
|
||||
size: Theme.barIconSize(root.barThickness, -4)
|
||||
color: root.isActive ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
color: Theme.primary
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 4
|
||||
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 4
|
||||
visible: NotepadStorageService.tabs && NotepadStorageService.tabs.length > 0
|
||||
opacity: 0.8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ BasePill {
|
||||
return DgopService.memoryUsage.toFixed(0);
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -97,7 +96,6 @@ BasePill {
|
||||
return DgopService.memoryUsage.toFixed(0) + "%";
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -106,7 +104,6 @@ BasePill {
|
||||
StyledTextMetrics {
|
||||
id: ramBaseline
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
text: "100%"
|
||||
}
|
||||
|
||||
@@ -125,7 +122,6 @@ BasePill {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
|
||||
@@ -22,6 +22,7 @@ Item {
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
||||
property Item windowRoot: (Window.window ? Window.window.contentItem : null)
|
||||
property int _workspaceUpdateTrigger: 0
|
||||
property int _desktopEntriesUpdateTrigger: 0
|
||||
readonly property var sortedToplevels: {
|
||||
_workspaceUpdateTrigger
|
||||
const toplevels = CompositorService.sortedToplevels
|
||||
@@ -34,6 +35,13 @@ Item {
|
||||
}
|
||||
return toplevels
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopEntries
|
||||
function onApplicationsChanged() {
|
||||
_desktopEntriesUpdateTrigger++
|
||||
}
|
||||
}
|
||||
readonly property var groupedWindows: {
|
||||
if (!SettingsData.runningAppsGroupByApp) {
|
||||
return []
|
||||
@@ -247,6 +255,7 @@ Item {
|
||||
property var toplevelObject: toplevelData
|
||||
property int windowCount: isGrouped ? modelData.windows.length : 1
|
||||
property string tooltipText: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
let appName = "Unknown"
|
||||
if (appId) {
|
||||
const desktopEntry = DesktopEntries.heuristicLookup(appId)
|
||||
@@ -285,6 +294,7 @@ Item {
|
||||
width: 18
|
||||
height: 18
|
||||
source: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
const moddedId = Paths.moddedAppId(appId)
|
||||
if (moddedId.toLowerCase().includes("steam_app")) {
|
||||
return ""
|
||||
@@ -319,6 +329,7 @@ Item {
|
||||
return !iconImg.visible && !isSteamApp
|
||||
}
|
||||
text: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
if (!appId) {
|
||||
return "?"
|
||||
}
|
||||
@@ -332,7 +343,6 @@ Item {
|
||||
}
|
||||
font.pixelSize: 10
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -352,7 +362,6 @@ Item {
|
||||
text: windowCount > 9 ? "9+" : windowCount
|
||||
font.pixelSize: 9
|
||||
color: Theme.surface
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +376,6 @@ Item {
|
||||
text: windowTitle
|
||||
font.pixelSize: Theme.barTextSize(barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
@@ -476,6 +484,7 @@ Item {
|
||||
property var toplevelObject: toplevelData
|
||||
property int windowCount: isGrouped ? modelData.windows.length : 1
|
||||
property string tooltipText: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
let appName = "Unknown"
|
||||
if (appId) {
|
||||
const desktopEntry = DesktopEntries.heuristicLookup(appId)
|
||||
@@ -513,6 +522,7 @@ Item {
|
||||
width: 18
|
||||
height: 18
|
||||
source: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
const moddedId = Paths.moddedAppId(appId)
|
||||
if (moddedId.toLowerCase().includes("steam_app")) {
|
||||
return ""
|
||||
@@ -546,6 +556,7 @@ Item {
|
||||
return !iconImg.visible && !isSteamApp
|
||||
}
|
||||
text: {
|
||||
root._desktopEntriesUpdateTrigger
|
||||
if (!appId) {
|
||||
return "?"
|
||||
}
|
||||
@@ -559,7 +570,6 @@ Item {
|
||||
}
|
||||
font.pixelSize: 10
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -579,7 +589,6 @@ Item {
|
||||
text: windowCount > 9 ? "9+" : windowCount
|
||||
font.pixelSize: 9
|
||||
color: Theme.surface
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +602,6 @@ Item {
|
||||
text: windowTitle
|
||||
font.pixelSize: Theme.barTextSize(barThickness)
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
@@ -780,7 +788,6 @@ Item {
|
||||
text: I18n.tr("Close")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
||||
@@ -131,7 +131,6 @@ Item {
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: (mouse) => {
|
||||
if (!delegateRoot.trayItem) {
|
||||
@@ -218,7 +217,6 @@ Item {
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: (mouse) => {
|
||||
if (!delegateRoot.trayItem) {
|
||||
@@ -486,7 +484,6 @@ Item {
|
||||
MouseArea {
|
||||
id: backArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: menuRoot.goBack()
|
||||
}
|
||||
@@ -522,7 +519,6 @@ Item {
|
||||
id: itemArea
|
||||
anchors.fill: parent
|
||||
enabled: !menuEntry?.isSeparator && (menuEntry?.enabled !== false)
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
|
||||
@@ -112,7 +112,6 @@ BasePill {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: SystemUpdateService.updateCount.toString()
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
visible: root.hasUpdates && !root.isChecking
|
||||
}
|
||||
@@ -123,7 +122,6 @@ BasePill {
|
||||
MouseArea {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||
|
||||
@@ -9,10 +9,11 @@ BasePill {
|
||||
id: root
|
||||
|
||||
Ref {
|
||||
service: VpnService
|
||||
service: DMSNetworkService
|
||||
}
|
||||
|
||||
property var popoutTarget: null
|
||||
property bool isHovered: clickArea.containsMouse
|
||||
|
||||
signal toggleVpnPopup()
|
||||
|
||||
@@ -24,9 +25,9 @@ BasePill {
|
||||
DankIcon {
|
||||
id: icon
|
||||
|
||||
name: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
|
||||
name: DMSNetworkService.isBusy ? "sync" : (DMSNetworkService.connected ? "vpn_lock" : "vpn_key_off")
|
||||
size: Theme.barIconSize(root.barThickness, -4)
|
||||
color: VpnService.connected ? Theme.primary : Theme.surfaceText
|
||||
color: DMSNetworkService.connected ? Theme.primary : Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
@@ -59,10 +60,10 @@ BasePill {
|
||||
tooltipLoader.active = true
|
||||
if (tooltipLoader.item) {
|
||||
let tooltipText = ""
|
||||
if (!VpnService.connected) {
|
||||
if (!DMSNetworkService.connected) {
|
||||
tooltipText = "VPN Disconnected"
|
||||
} else {
|
||||
const names = VpnService.activeNames || []
|
||||
const names = DMSNetworkService.activeNames || []
|
||||
if (names.length <= 1) {
|
||||
tooltipText = "VPN Connected • " + (names[0] || "")
|
||||
} else {
|
||||
|
||||
@@ -224,12 +224,28 @@ Item {
|
||||
|
||||
const currentIndex = realWorkspaces.findIndex(ws => ws === root.currentWorkspace)
|
||||
const validIndex = currentIndex === -1 ? 0 : currentIndex
|
||||
const nextIndex = direction > 0 ? (validIndex + 1) % realWorkspaces.length : (validIndex - 1 + realWorkspaces.length) % realWorkspaces.length
|
||||
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
|
||||
|
||||
if (nextIndex === validIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
NiriService.switchToWorkspace(realWorkspaces[nextIndex] - 1)
|
||||
} else if (CompositorService.isHyprland) {
|
||||
const command = direction > 0 ? "workspace r+1" : "workspace r-1"
|
||||
Hyprland.dispatch(command)
|
||||
const realWorkspaces = getRealWorkspaces()
|
||||
if (realWorkspaces.length < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
const currentIndex = realWorkspaces.findIndex(ws => ws.id === root.currentWorkspace)
|
||||
const validIndex = currentIndex === -1 ? 0 : currentIndex
|
||||
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
|
||||
|
||||
if (nextIndex === validIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,7 +588,6 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
id: appMouseArea
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
enabled: isActive
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
@@ -641,7 +656,6 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
id: appMouseArea
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
enabled: isActive
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Qt.labs.folderlistmodel
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Modals.FileBrowser
|
||||
import qs.Services
|
||||
@@ -16,11 +16,10 @@ Item {
|
||||
implicitWidth: 700
|
||||
implicitHeight: 410
|
||||
|
||||
property var wallpaperList: []
|
||||
property string wallpaperDir: ""
|
||||
property int currentPage: 0
|
||||
property int itemsPerPage: 16
|
||||
property int totalPages: Math.max(1, Math.ceil(wallpaperList.length / itemsPerPage))
|
||||
property int totalPages: Math.max(1, Math.ceil(wallpaperFolderModel.count / itemsPerPage))
|
||||
property bool active: false
|
||||
property Item focusTarget: wallpaperGrid
|
||||
property Item tabBarItem: null
|
||||
@@ -28,6 +27,8 @@ Item {
|
||||
property Item keyForwardTarget: null
|
||||
property int lastPage: 0
|
||||
property bool enableAnimation: false
|
||||
property string homeDir: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
||||
property string selectedFileName: ""
|
||||
|
||||
signal requestTabChange(int newIndex)
|
||||
|
||||
@@ -36,6 +37,11 @@ Item {
|
||||
enableAnimation = false
|
||||
lastPage = currentPage
|
||||
}
|
||||
updateSelectedFileName()
|
||||
}
|
||||
|
||||
onGridIndexChanged: {
|
||||
updateSelectedFileName()
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
@@ -45,10 +51,7 @@ Item {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
loadWallpapers()
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
loadWallpaperDirectory()
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
@@ -59,16 +62,17 @@ Item {
|
||||
|
||||
function handleKeyEvent(event) {
|
||||
const columns = 4
|
||||
const rows = 4
|
||||
const currentRow = Math.floor(gridIndex / columns)
|
||||
const currentCol = gridIndex % columns
|
||||
const visibleCount = wallpaperGrid.model.length
|
||||
const visibleCount = Math.min(itemsPerPage, wallpaperFolderModel.count - currentPage * itemsPerPage)
|
||||
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
if (gridIndex >= 0) {
|
||||
const item = wallpaperGrid.currentItem
|
||||
if (item && item.wallpaperPath) {
|
||||
SessionData.setWallpaper(item.wallpaperPath)
|
||||
if (gridIndex >= 0 && gridIndex < visibleCount) {
|
||||
const absoluteIndex = currentPage * itemsPerPage + gridIndex
|
||||
if (absoluteIndex < wallpaperFolderModel.count) {
|
||||
const filePath = wallpaperFolderModel.get(absoluteIndex, "filePath")
|
||||
if (filePath) {
|
||||
SessionData.setWallpaper(filePath.toString().replace(/^file:\/\//, ''))
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -76,10 +80,8 @@ Item {
|
||||
|
||||
if (event.key === Qt.Key_Right) {
|
||||
if (gridIndex + 1 < visibleCount) {
|
||||
// Move right within current page
|
||||
gridIndex++
|
||||
} else if (gridIndex === visibleCount - 1 && currentPage < totalPages - 1) {
|
||||
// At last item in page, go to next page
|
||||
} else if (currentPage < totalPages - 1) {
|
||||
gridIndex = 0
|
||||
currentPage++
|
||||
}
|
||||
@@ -88,22 +90,19 @@ Item {
|
||||
|
||||
if (event.key === Qt.Key_Left) {
|
||||
if (gridIndex > 0) {
|
||||
// Move left within current page
|
||||
gridIndex--
|
||||
} else if (gridIndex === 0 && currentPage > 0) {
|
||||
// At first item in page, go to previous page (last item)
|
||||
} else if (currentPage > 0) {
|
||||
currentPage--
|
||||
gridIndex = Math.min(itemsPerPage - 1, wallpaperList.length - currentPage * itemsPerPage - 1)
|
||||
const prevPageCount = Math.min(itemsPerPage, wallpaperFolderModel.count - currentPage * itemsPerPage)
|
||||
gridIndex = prevPageCount - 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Down) {
|
||||
if (gridIndex + columns < visibleCount) {
|
||||
// Move down within current page
|
||||
gridIndex += columns
|
||||
} else if (gridIndex >= visibleCount - columns && currentPage < totalPages - 1) {
|
||||
// In last row, go to next page
|
||||
} else if (currentPage < totalPages - 1) {
|
||||
gridIndex = currentCol
|
||||
currentPage++
|
||||
}
|
||||
@@ -112,12 +111,10 @@ Item {
|
||||
|
||||
if (event.key === Qt.Key_Up) {
|
||||
if (gridIndex >= columns) {
|
||||
// Move up within current page
|
||||
gridIndex -= columns
|
||||
} else if (gridIndex < columns && currentPage > 0) {
|
||||
// In first row, go to previous page (last row)
|
||||
} else if (currentPage > 0) {
|
||||
currentPage--
|
||||
const prevPageCount = Math.min(itemsPerPage, wallpaperList.length - currentPage * itemsPerPage)
|
||||
const prevPageCount = Math.min(itemsPerPage, wallpaperFolderModel.count - currentPage * itemsPerPage)
|
||||
const prevPageRows = Math.ceil(prevPageCount / columns)
|
||||
gridIndex = (prevPageRows - 1) * columns + currentCol
|
||||
gridIndex = Math.min(gridIndex, prevPageCount - 1)
|
||||
@@ -144,8 +141,9 @@ Item {
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_End && event.modifiers & Qt.ControlModifier) {
|
||||
gridIndex = 0
|
||||
currentPage = totalPages - 1
|
||||
const lastPageCount = Math.min(itemsPerPage, wallpaperFolderModel.count - currentPage * itemsPerPage)
|
||||
gridIndex = Math.max(0, lastPageCount - 1)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -153,40 +151,38 @@ Item {
|
||||
}
|
||||
|
||||
function setInitialSelection() {
|
||||
if (!SessionData.wallpaperPath) {
|
||||
if (!SessionData.wallpaperPath || wallpaperFolderModel.count === 0) {
|
||||
gridIndex = 0
|
||||
updateSelectedFileName()
|
||||
Qt.callLater(() => { enableAnimation = true })
|
||||
return
|
||||
}
|
||||
|
||||
const startIndex = currentPage * itemsPerPage
|
||||
const endIndex = Math.min(startIndex + itemsPerPage, wallpaperList.length)
|
||||
const pageWallpapers = wallpaperList.slice(startIndex, endIndex)
|
||||
|
||||
for (let i = 0; i < pageWallpapers.length; i++) {
|
||||
if (pageWallpapers[i] === SessionData.wallpaperPath) {
|
||||
gridIndex = i
|
||||
for (let i = 0; i < wallpaperFolderModel.count; i++) {
|
||||
const filePath = wallpaperFolderModel.get(i, "filePath")
|
||||
if (filePath && filePath.toString().replace(/^file:\/\//, '') === SessionData.wallpaperPath) {
|
||||
const targetPage = Math.floor(i / itemsPerPage)
|
||||
const targetIndex = i % itemsPerPage
|
||||
currentPage = targetPage
|
||||
gridIndex = targetIndex
|
||||
updateSelectedFileName()
|
||||
Qt.callLater(() => { enableAnimation = true })
|
||||
return
|
||||
}
|
||||
}
|
||||
gridIndex = 0
|
||||
updateSelectedFileName()
|
||||
Qt.callLater(() => { enableAnimation = true })
|
||||
}
|
||||
|
||||
onWallpaperListChanged: {
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
}
|
||||
|
||||
function loadWallpapers() {
|
||||
function loadWallpaperDirectory() {
|
||||
const currentWallpaper = SessionData.wallpaperPath
|
||||
|
||||
// Try current wallpaper path / fallback to wallpaperLastPath
|
||||
|
||||
if (!currentWallpaper || currentWallpaper.startsWith("#") || currentWallpaper.startsWith("we:")) {
|
||||
if (CacheData.wallpaperLastPath && CacheData.wallpaperLastPath !== "") {
|
||||
wallpaperDir = CacheData.wallpaperLastPath
|
||||
} else {
|
||||
wallpaperDir = ""
|
||||
wallpaperList = []
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -194,44 +190,51 @@ Item {
|
||||
wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/'))
|
||||
}
|
||||
|
||||
function updateWallpaperList() {
|
||||
if (!wallpaperFolderModel || wallpaperFolderModel.count === 0) {
|
||||
wallpaperList = []
|
||||
currentPage = 0
|
||||
gridIndex = 0
|
||||
function updateSelectedFileName() {
|
||||
if (wallpaperFolderModel.count === 0) {
|
||||
selectedFileName = ""
|
||||
return
|
||||
}
|
||||
|
||||
// Build list from FolderListModel
|
||||
const files = []
|
||||
for (let i = 0; i < wallpaperFolderModel.count; i++) {
|
||||
const filePath = wallpaperFolderModel.get(i, "filePath")
|
||||
const absoluteIndex = currentPage * itemsPerPage + gridIndex
|
||||
if (absoluteIndex < wallpaperFolderModel.count) {
|
||||
const filePath = wallpaperFolderModel.get(absoluteIndex, "filePath")
|
||||
if (filePath) {
|
||||
// Remove file:// prefix if present
|
||||
const cleanPath = filePath.toString().replace(/^file:\/\//, '')
|
||||
files.push(cleanPath)
|
||||
const pathStr = filePath.toString().replace(/^file:\/\//, '')
|
||||
selectedFileName = pathStr.substring(pathStr.lastIndexOf('/') + 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
wallpaperList = files
|
||||
|
||||
const currentPath = SessionData.wallpaperPath
|
||||
const selectedIndex = currentPath ? wallpaperList.indexOf(currentPath) : -1
|
||||
|
||||
if (selectedIndex >= 0) {
|
||||
currentPage = Math.floor(selectedIndex / itemsPerPage)
|
||||
gridIndex = selectedIndex % itemsPerPage
|
||||
} else {
|
||||
const maxPage = Math.max(0, Math.ceil(files.length / itemsPerPage) - 1)
|
||||
currentPage = Math.min(Math.max(0, currentPage), maxPage)
|
||||
gridIndex = 0
|
||||
}
|
||||
selectedFileName = ""
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onWallpaperPathChanged() {
|
||||
loadWallpapers()
|
||||
loadWallpaperDirectory()
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: wallpaperFolderModel
|
||||
function onCountChanged() {
|
||||
if (wallpaperFolderModel.status === FolderListModel.Ready) {
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
updateSelectedFileName()
|
||||
}
|
||||
}
|
||||
function onStatusChanged() {
|
||||
if (wallpaperFolderModel.status === FolderListModel.Ready && wallpaperFolderModel.count > 0) {
|
||||
if (visible && active) {
|
||||
setInitialSelection()
|
||||
}
|
||||
updateSelectedFileName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,18 +249,6 @@ Item {
|
||||
showDirs: false
|
||||
sortField: FolderListModel.Name
|
||||
folder: wallpaperDir ? "file://" + wallpaperDir : ""
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === FolderListModel.Ready) {
|
||||
updateWallpaperList()
|
||||
}
|
||||
}
|
||||
|
||||
onCountChanged: {
|
||||
if (status === FolderListModel.Ready) {
|
||||
updateWallpaperList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
@@ -275,13 +266,11 @@ Item {
|
||||
showHiddenFiles: false
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
allowStacking: true
|
||||
|
||||
|
||||
onFileSelected: (path) => {
|
||||
// Set the selected wallpaper
|
||||
const cleanPath = path.replace(/^file:\/\//, '')
|
||||
SessionData.setWallpaper(cleanPath)
|
||||
|
||||
// Extract directory from the selected file and load all wallpapers
|
||||
|
||||
const dirPath = cleanPath.substring(0, cleanPath.lastIndexOf('/'))
|
||||
if (dirPath) {
|
||||
wallpaperDir = dirPath
|
||||
@@ -290,7 +279,7 @@ Item {
|
||||
}
|
||||
close()
|
||||
}
|
||||
|
||||
|
||||
onDialogClosed: {
|
||||
Qt.callLater(() => wallpaperBrowserLoader.active = false)
|
||||
}
|
||||
@@ -336,8 +325,15 @@ Item {
|
||||
|
||||
model: {
|
||||
const startIndex = currentPage * itemsPerPage
|
||||
const endIndex = Math.min(startIndex + itemsPerPage, wallpaperList.length)
|
||||
return wallpaperList.slice(startIndex, endIndex)
|
||||
const endIndex = Math.min(startIndex + itemsPerPage, wallpaperFolderModel.count)
|
||||
const items = []
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
const filePath = wallpaperFolderModel.get(i, "filePath")
|
||||
if (filePath) {
|
||||
items.push(filePath.toString().replace(/^file:\/\//, ''))
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
onModelChanged: {
|
||||
@@ -359,8 +355,11 @@ Item {
|
||||
Connections {
|
||||
target: root
|
||||
function onGridIndexChanged() {
|
||||
if (enableAnimation && wallpaperGrid.count > 0) {
|
||||
if (wallpaperGrid.count > 0) {
|
||||
wallpaperGrid.currentIndex = gridIndex
|
||||
if (!enableAnimation) {
|
||||
wallpaperGrid.positionViewAtIndex(gridIndex, GridView.Contain)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -440,7 +439,6 @@ Item {
|
||||
if (modelData) {
|
||||
SessionData.setWallpaper(modelData)
|
||||
}
|
||||
// Don't steal focus - let mainContainer keep it for keyboard nav
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,7 +447,7 @@ Item {
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
visible: wallpaperList.length === 0
|
||||
visible: wallpaperFolderModel.count === 0
|
||||
text: "No wallpapers found\n\nClick the folder icon below to browse"
|
||||
font.pixelSize: 14
|
||||
color: Theme.outline
|
||||
@@ -457,67 +455,84 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
Column {
|
||||
width: parent.width
|
||||
height: 50
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Item {
|
||||
width: (parent.width - controlsRow.width - browseButton.width - Theme.spacingS) / 2
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Row {
|
||||
id: controlsRow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
height: 32
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankActionButton {
|
||||
Item {
|
||||
width: (parent.width - controlsRow.width - browseButton.width - Theme.spacingS) / 2
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Row {
|
||||
id: controlsRow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "skip_previous"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
enabled: currentPage > 0
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onClicked: {
|
||||
if (currentPage > 0) {
|
||||
currentPage--
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankActionButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "skip_previous"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
enabled: currentPage > 0
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onClicked: {
|
||||
if (currentPage > 0) {
|
||||
currentPage--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: wallpaperFolderModel.count > 0 ? `${wallpaperFolderModel.count} wallpapers • ${currentPage + 1} / ${totalPages}` : "No wallpapers"
|
||||
font.pixelSize: 14
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.7
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "skip_next"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
enabled: currentPage < totalPages - 1
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onClicked: {
|
||||
if (currentPage < totalPages - 1) {
|
||||
currentPage++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
DankActionButton {
|
||||
id: browseButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: wallpaperList.length > 0 ? `${wallpaperList.length} wallpapers • ${currentPage + 1} / ${totalPages}` : "No wallpapers"
|
||||
font.pixelSize: 14
|
||||
color: Theme.surfaceText
|
||||
iconName: "folder_open"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
opacity: 0.7
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "skip_next"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
enabled: currentPage < totalPages - 1
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onClicked: {
|
||||
if (currentPage < totalPages - 1) {
|
||||
currentPage++
|
||||
}
|
||||
}
|
||||
onClicked: wallpaperBrowserLoader.active = true
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
id: browseButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "folder_open"
|
||||
iconSize: 20
|
||||
buttonSize: 32
|
||||
opacity: 0.7
|
||||
onClicked: wallpaperBrowserLoader.active = true
|
||||
StyledText {
|
||||
width: parent.width
|
||||
height: 18
|
||||
text: selectedFileName
|
||||
font.pixelSize: 12
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.5
|
||||
visible: selectedFileName !== ""
|
||||
elide: Text.ElideMiddle
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,10 +452,10 @@ Item {
|
||||
anchors.top: SettingsData.dockPosition === SettingsData.Position.Top ? parent.top : undefined
|
||||
anchors.left: SettingsData.dockPosition === SettingsData.Position.Left ? parent.left : undefined
|
||||
anchors.right: SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined
|
||||
anchors.bottomMargin: SettingsData.dockPosition === SettingsData.Position.Bottom ? -2 : 0
|
||||
anchors.topMargin: SettingsData.dockPosition === SettingsData.Position.Top ? -2 : 0
|
||||
anchors.leftMargin: SettingsData.dockPosition === SettingsData.Position.Left ? -2 : 0
|
||||
anchors.rightMargin: SettingsData.dockPosition === SettingsData.Position.Right ? -2 : 0
|
||||
anchors.bottomMargin: SettingsData.dockPosition === SettingsData.Position.Bottom ? -(SettingsData.dockSpacing / 2) : 0
|
||||
anchors.topMargin: SettingsData.dockPosition === SettingsData.Position.Top ? -(SettingsData.dockSpacing / 2) : 0
|
||||
anchors.leftMargin: SettingsData.dockPosition === SettingsData.Position.Left ? -(SettingsData.dockSpacing / 2) : 0
|
||||
anchors.rightMargin: SettingsData.dockPosition === SettingsData.Position.Right ? -(SettingsData.dockSpacing / 2) : 0
|
||||
|
||||
sourceComponent: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? columnIndicator : rowIndicator
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ Singleton {
|
||||
property string customThemeFile: ""
|
||||
property string matugenScheme: "scheme-tonal-spot"
|
||||
property bool use24HourClock: true
|
||||
property bool showSeconds: false
|
||||
property bool useFahrenheit: false
|
||||
property bool nightModeEnabled: false
|
||||
property string weatherLocation: "New York, NY"
|
||||
@@ -54,6 +55,7 @@ Singleton {
|
||||
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : ""
|
||||
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot"
|
||||
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true
|
||||
showSeconds = settings.showSeconds !== undefined ? settings.showSeconds : false
|
||||
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false
|
||||
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
|
||||
weatherLocation = settings.weatherLocation !== undefined ? settings.weatherLocation : "New York, NY"
|
||||
|
||||
@@ -186,7 +186,7 @@ Item {
|
||||
|
||||
SystemClock {
|
||||
id: systemClock
|
||||
precision: SystemClock.Minutes
|
||||
precision: SystemClock.Seconds
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -196,40 +196,136 @@ Item {
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -100
|
||||
width: 400
|
||||
width: parent.width
|
||||
height: 140
|
||||
|
||||
StyledText {
|
||||
Row {
|
||||
id: clockText
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
text: {
|
||||
const format = GreetdSettings.use24HourClock ? "HH:mm" : "h:mm AP"
|
||||
spacing: 0
|
||||
|
||||
property string fullTimeStr: {
|
||||
const format = GreetdSettings.use24HourClock
|
||||
? (GreetdSettings.showSeconds ? "HH:mm:ss" : "HH:mm")
|
||||
: (GreetdSettings.showSeconds ? "h:mm:ss AP" : "h:mm AP")
|
||||
return systemClock.date.toLocaleTimeString(Qt.locale(), format)
|
||||
}
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
lineHeight: 0.8
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: clockText.bottom
|
||||
anchors.topMargin: -20
|
||||
text: {
|
||||
if (GreetdSettings.lockDateFormat && GreetdSettings.lockDateFormat.length > 0) {
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), GreetdSettings.lockDateFormat)
|
||||
}
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat)
|
||||
property var timeParts: fullTimeStr.split(':')
|
||||
property string hours: timeParts[0] || ""
|
||||
property string minutes: timeParts[1] || ""
|
||||
property string secondsWithAmPm: timeParts.length > 2 ? timeParts[2] : ""
|
||||
property string seconds: secondsWithAmPm.replace(/\s*(AM|PM|am|pm)$/i, '')
|
||||
property string ampm: {
|
||||
const match = fullTimeStr.match(/\s*(AM|PM|am|pm)$/i)
|
||||
return match ? match[0].trim() : ""
|
||||
}
|
||||
property bool hasSeconds: timeParts.length > 2
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[1] : clockText.hours.length > 0 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: ":"
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 0 ? clockText.minutes[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 1 ? clockText.minutes[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.hasSeconds ? ":" : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 0 ? clockText.seconds[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 1 ? clockText.seconds[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 20
|
||||
text: " "
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.ampm
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: "white"
|
||||
opacity: 0.9
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -10
|
||||
text: {
|
||||
if (GreetdSettings.lockDateFormat && GreetdSettings.lockDateFormat.length > 0) {
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), GreetdSettings.lockDateFormat)
|
||||
}
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: "white"
|
||||
opacity: 0.9
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 80
|
||||
|
||||
@@ -68,6 +68,33 @@ if [[ -z "$COMPOSITOR" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
locate_dms_config() {
|
||||
local config_name="$1"
|
||||
local search_paths=()
|
||||
|
||||
local config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
search_paths+=("$config_home/quickshell/$config_name")
|
||||
|
||||
search_paths+=("/usr/share/quickshell/$config_name")
|
||||
|
||||
local config_dirs="${XDG_CONFIG_DIRS:-/etc/xdg}"
|
||||
IFS=':' read -ra dirs <<< "$config_dirs"
|
||||
for dir in "${dirs[@]}"; do
|
||||
if [[ -n "$dir" ]]; then
|
||||
search_paths+=("$dir/quickshell/$config_name")
|
||||
fi
|
||||
done
|
||||
|
||||
for path in "${search_paths[@]}"; do
|
||||
if [[ -f "$path/shell.qml" ]]; then
|
||||
echo "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
export XDG_SESSION_TYPE=wayland
|
||||
export QT_QPA_PLATFORM=wayland
|
||||
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
|
||||
@@ -81,7 +108,14 @@ QS_CMD="qs"
|
||||
if [[ "$DMS_PATH" == /* ]]; then
|
||||
QS_CMD="qs -p $DMS_PATH"
|
||||
else
|
||||
QS_CMD="qs -c $DMS_PATH"
|
||||
RESOLVED_PATH=$(locate_dms_config "$DMS_PATH")
|
||||
if [[ $? -eq 0 && -n "$RESOLVED_PATH" ]]; then
|
||||
echo "Located DMS config at: $RESOLVED_PATH" >&2
|
||||
QS_CMD="qs -p $RESOLVED_PATH"
|
||||
else
|
||||
echo "Error: Could not find DMS config '$DMS_PATH' (shell.qml) in any valid config path" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$COMPOSITOR" in
|
||||
|
||||
@@ -197,7 +197,7 @@ Item {
|
||||
SystemClock {
|
||||
id: systemClock
|
||||
|
||||
precision: SystemClock.Minutes
|
||||
precision: SystemClock.Seconds
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -207,39 +207,134 @@ Item {
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -100
|
||||
width: 400
|
||||
width: parent.width
|
||||
height: 140
|
||||
|
||||
StyledText {
|
||||
Row {
|
||||
id: clockText
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
text: {
|
||||
return systemClock.date.toLocaleTimeString(Qt.locale(), SettingsData.getEffectiveTimeFormat())
|
||||
}
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
lineHeight: 0.8
|
||||
}
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: clockText.bottom
|
||||
anchors.topMargin: -20
|
||||
text: {
|
||||
if (SettingsData.lockDateFormat && SettingsData.lockDateFormat.length > 0) {
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat)
|
||||
}
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat)
|
||||
property string fullTimeStr: {
|
||||
const format = SettingsData.getEffectiveTimeFormat()
|
||||
return systemClock.date.toLocaleTimeString(Qt.locale(), format)
|
||||
}
|
||||
property var timeParts: fullTimeStr.split(':')
|
||||
property string hours: timeParts[0] || ""
|
||||
property string minutes: timeParts[1] || ""
|
||||
property string secondsWithAmPm: timeParts.length > 2 ? timeParts[2] : ""
|
||||
property string seconds: secondsWithAmPm.replace(/\s*(AM|PM|am|pm)$/i, '')
|
||||
property string ampm: {
|
||||
const match = fullTimeStr.match(/\s*(AM|PM|am|pm)$/i)
|
||||
return match ? match[0].trim() : ""
|
||||
}
|
||||
property bool hasSeconds: timeParts.length > 2
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[1] : clockText.hours.length > 0 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: ":"
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 0 ? clockText.minutes[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 1 ? clockText.minutes[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.hasSeconds ? ":" : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 0 ? clockText.seconds[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 1 ? clockText.seconds[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 20
|
||||
text: " "
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.ampm
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: "white"
|
||||
opacity: 0.9
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -25
|
||||
text: {
|
||||
if (SettingsData.lockDateFormat && SettingsData.lockDateFormat.length > 0) {
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat)
|
||||
}
|
||||
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: "white"
|
||||
opacity: 0.9
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 50
|
||||
|
||||
@@ -48,7 +48,8 @@ Item {
|
||||
return "transparent"
|
||||
}
|
||||
|
||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
|
||||
const isHovered = mouseArea.containsMouse || (root.isHovered ?? false)
|
||||
const baseColor = isHovered ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,23 @@ Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Component.onCompleted: {
|
||||
property bool isInitialized: false
|
||||
|
||||
function loadValue() {
|
||||
const settings = findSettings()
|
||||
if (settings) {
|
||||
value = settings.loadValue(settingKey, defaultValue)
|
||||
if (settings && settings.pluginService) {
|
||||
const loadedValue = settings.loadValue(settingKey, defaultValue)
|
||||
value = loadedValue
|
||||
isInitialized = true
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
loadValue()
|
||||
}
|
||||
|
||||
onValueChanged: {
|
||||
if (!isInitialized) return
|
||||
const settings = findSettings()
|
||||
if (settings) {
|
||||
settings.saveValue(settingKey, value)
|
||||
|
||||
@@ -460,6 +460,58 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.outline
|
||||
opacity: 0.2
|
||||
visible: CompositorService.isNiri
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: CompositorService.isNiri
|
||||
|
||||
DankIcon {
|
||||
name: "blur_on"
|
||||
size: Theme.iconSize
|
||||
color: SettingsData.blurWallpaperOnOverview ? Theme.primary : Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width - Theme.iconSize - Theme.spacingM - blurOverviewToggle.width - Theme.spacingM
|
||||
spacing: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Blur on Overview")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Blur wallpaper when niri overview is open")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
id: blurOverviewToggle
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checked: SettingsData.blurWallpaperOnOverview
|
||||
onToggled: checked => {
|
||||
SettingsData.setBlurWallpaperOnOverview(checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Per-Mode Wallpaper Section - Full Width
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
@@ -959,6 +1011,67 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: blurLayerColumn.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
border.width: 0
|
||||
visible: CompositorService.isNiri
|
||||
|
||||
Column {
|
||||
id: blurLayerColumn
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "blur_on"
|
||||
size: Theme.iconSize
|
||||
color: SettingsData.blurredWallpaperLayer ? Theme.primary : Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width - Theme.iconSize - Theme.spacingM - blurLayerToggle.width - Theme.spacingM
|
||||
spacing: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Blur Layer")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
id: blurLayerToggle
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checked: SettingsData.blurredWallpaperLayer
|
||||
onToggled: checked => {
|
||||
SettingsData.setBlurredWallpaperLayer(checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: lightModeRow.implicitHeight + Theme.spacingL * 2
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
@@ -6,6 +7,7 @@ import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
import qs.Modules
|
||||
import qs.Services
|
||||
|
||||
Variants {
|
||||
model: {
|
||||
@@ -467,6 +469,15 @@ Variants {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: effectLoader.active ? effectLoader.item : (root.actualTransitionType === "none" ? currentWallpaper : null)
|
||||
visible: CompositorService.isNiri && SettingsData.blurWallpaperOnOverview && NiriService.inOverview
|
||||
blurEnabled: true
|
||||
blur: 0.8
|
||||
blurMax: 48
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
68
README.md
68
README.md
@@ -267,31 +267,9 @@ sudo dnf copr enable avengemedia/danklinux && sudo dnf install quickshell-git
|
||||
# ! TODO - document other distros
|
||||
```
|
||||
|
||||
#### 2. Install fonts
|
||||
*Inter Variable* and *Fira Code* are not strictly required, but they are the default fonts of dms.
|
||||
#### 2. Install the shell
|
||||
|
||||
#### 2.1 Install Material Symbols
|
||||
```bash
|
||||
sudo curl -L "https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsRounded%5BFILL%2CGRAD%2Copsz%2Cwght%5D.ttf" -o /usr/share/fonts/MaterialSymbolsRounded.ttf
|
||||
```
|
||||
#### 2.2 Install Inter Variable
|
||||
```bash
|
||||
sudo curl -L "https://github.com/rsms/inter/raw/refs/tags/v4.1/docs/font-files/InterVariable.ttf" -o /usr/share/fonts/InterVariable.ttf
|
||||
```
|
||||
|
||||
#### 2.3 Install Fira Code (monospace font)
|
||||
```bash
|
||||
sudo curl -L "https://github.com/tonsky/FiraCode/releases/latest/download/FiraCode-Regular.ttf" -o /usr/share/fonts/FiraCode-Regular.ttf
|
||||
```
|
||||
|
||||
#### 2.4 Refresh font cache
|
||||
```bash
|
||||
fc-cache -fv
|
||||
```
|
||||
|
||||
#### 3. Install the shell
|
||||
|
||||
#### 3.1. Clone latest QML
|
||||
#### 2.1. Clone latest QML
|
||||
|
||||
```bash
|
||||
mkdir ~/.config/quickshell && git clone https://github.com/AvengeMedia/DankMaterialShell.git ~/.config/quickshell/dms
|
||||
@@ -305,7 +283,7 @@ cd ~/.config/quickshell/dms
|
||||
git checkout $(git describe --tags --abbrev=0)
|
||||
```
|
||||
|
||||
#### 3.2. Install latest dms CLI
|
||||
#### 2.2. Install latest dms CLI
|
||||
|
||||
```bash
|
||||
sudo sh -c "curl -L https://github.com/AvengeMedia/danklinux/releases/latest/download/dms-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').gz | gunzip | tee /usr/local/bin/dms > /dev/null && chmod +x /usr/local/bin/dms"
|
||||
@@ -319,9 +297,9 @@ git clone https://github.com/AvengeMedia/danklinux.git && cd danklinux
|
||||
make && sudo make install
|
||||
```
|
||||
|
||||
#### 4. Optional Features (system monitoring, clipboard history, brightness controls, etc.)
|
||||
#### 3. Optional Features (system monitoring, clipboard history, brightness controls, etc.)
|
||||
|
||||
#### 4.1 Core optional dependencies
|
||||
#### 3.1 Core optional dependencies
|
||||
```bash
|
||||
# Arch Linux
|
||||
sudo pacman -S cava wl-clipboard cliphist brightnessctl qt6-multimedia accountsservice
|
||||
@@ -329,13 +307,13 @@ paru -S matugen-bin dgop
|
||||
|
||||
# Fedora
|
||||
sudo dnf install cava wl-clipboard brightnessctl qt6-qtmultimedia accountsservice
|
||||
sudo dnf copr enable avengemedia/danklinux && sudo dnf install cliphist ghostty hyprpicker material-symbols-fonts matugen
|
||||
sudo dnf copr enable avengemedia/danklinux && sudo dnf install cliphist ghostty hyprpicker matugen
|
||||
```
|
||||
Note: by enabling and installing the avengemedia/dms copr above, these core dependencies will automatically be available for use.
|
||||
|
||||
*Other distros will just need to find sources for the above packages*
|
||||
|
||||
#### 4.2 - dgop manual installation
|
||||
#### 3.2 - dgop manual installation
|
||||
|
||||
`dgop` is available via AUR and a nix flake, other distributions can install it manually.
|
||||
|
||||
@@ -439,6 +417,37 @@ binds {
|
||||
spawn "dms" "ipc" "call" "night" "toggle";
|
||||
}
|
||||
}
|
||||
|
||||
// You probably want this too
|
||||
debug {
|
||||
honor-xdg-activation-with-invalid-serial
|
||||
}
|
||||
```
|
||||
|
||||
#### niri - place wallpaper on overview (blurred or non-blurred)
|
||||
|
||||
Place the wallpaper on backdrop using a layer-rule for a contiguous surface.
|
||||
|
||||
```kdl
|
||||
layer-rule {
|
||||
match namespace="quickshell"
|
||||
place-within-backdrop true
|
||||
}
|
||||
```
|
||||
|
||||
If using "Blur Layer" option, you may want the blurred layer to appear on overview only, that can be done with some layer rules:
|
||||
|
||||
```kdl
|
||||
layer-rule {
|
||||
match namespace="dms:blurwallpaper"
|
||||
opacity 0.0
|
||||
}
|
||||
|
||||
layer-rule {
|
||||
match namespace="dms:blurwallpaper"
|
||||
place-within-backdrop true
|
||||
opacity 1.0
|
||||
}
|
||||
```
|
||||
|
||||
#### niri theming
|
||||
@@ -790,7 +799,6 @@ All settings are configurable in
|
||||
|
||||
**Common issues:**
|
||||
|
||||
- **Missing icons:** Verify Material Symbols font installation with `fc-list | grep Material`
|
||||
- **No dynamic theming:** Install matugen and enable in settings
|
||||
- **Qt apps not themed:** Configure qt5ct/qt6ct and set QT_QPA_PLATFORMTHEME
|
||||
- **Calendar not syncing:** Check vdirsyncer credentials and network connectivity
|
||||
|
||||
@@ -76,6 +76,10 @@ Singleton {
|
||||
target: Hyprland
|
||||
function onFocusedWorkspaceChanged() { root.scheduleSort() }
|
||||
}
|
||||
Connections {
|
||||
target: NiriService
|
||||
function onWindowsChanged() { root.scheduleSort() }
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
detectCompositor()
|
||||
|
||||
@@ -11,6 +11,7 @@ Singleton {
|
||||
id: root
|
||||
|
||||
property bool networkAvailable: false
|
||||
property string backend: ""
|
||||
|
||||
property string networkStatus: "disconnected"
|
||||
property string primaryConnection: ""
|
||||
@@ -68,6 +69,24 @@ Singleton {
|
||||
property string wifiPassword: ""
|
||||
property string forgetSSID: ""
|
||||
|
||||
property var vpnProfiles: []
|
||||
property var vpnActive: []
|
||||
property bool vpnAvailable: false
|
||||
property bool vpnIsBusy: false
|
||||
|
||||
property alias profiles: root.vpnProfiles
|
||||
property alias activeConnections: root.vpnActive
|
||||
property var activeUuids: vpnActive.map(v => v.uuid).filter(u => !!u)
|
||||
property var activeNames: vpnActive.map(v => v.name).filter(n => !!n)
|
||||
property string activeUuid: activeUuids.length > 0 ? activeUuids[0] : ""
|
||||
property string activeName: activeNames.length > 0 ? activeNames[0] : ""
|
||||
property string activeDevice: vpnActive.length > 0 ? (vpnActive[0].device || "") : ""
|
||||
property string activeState: vpnActive.length > 0 ? (vpnActive[0].state || "") : ""
|
||||
property bool vpnConnected: activeUuids.length > 0
|
||||
property alias available: root.vpnAvailable
|
||||
property alias isBusy: root.vpnIsBusy
|
||||
property alias connected: root.vpnConnected
|
||||
|
||||
property string networkInfoSSID: ""
|
||||
property string networkInfoDetails: ""
|
||||
property bool networkInfoLoading: false
|
||||
@@ -93,7 +112,7 @@ Singleton {
|
||||
|
||||
signal networksUpdated
|
||||
signal connectionChanged
|
||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason)
|
||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason, string connType, string connName, string vpnService)
|
||||
|
||||
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
||||
|
||||
@@ -109,7 +128,7 @@ Singleton {
|
||||
|
||||
function onNetworkStateUpdate(data) {
|
||||
const networksCount = data.wifiNetworks?.length ?? "null"
|
||||
console.log("NetworkManagerService: Subscription update received, networks:", networksCount)
|
||||
console.log("DMSNetworkService: Subscription update received, networks:", networksCount)
|
||||
updateState(data)
|
||||
}
|
||||
}
|
||||
@@ -148,8 +167,6 @@ Singleton {
|
||||
|
||||
networkAvailable = DMSService.capabilities.includes("network")
|
||||
|
||||
console.log("NetworkManagerService: Network available:", networkAvailable)
|
||||
|
||||
if (networkAvailable && !stateInitialized) {
|
||||
stateInitialized = true
|
||||
getState()
|
||||
@@ -165,7 +182,11 @@ Singleton {
|
||||
credentialsReason = data.reason || "Credentials required"
|
||||
credentialsRequested = true
|
||||
|
||||
credentialsNeeded(credentialsToken, credentialsSSID, credentialsSetting, credentialsFields, credentialsHints, credentialsReason)
|
||||
const connType = data.connType || ""
|
||||
const connName = data.name || data.connectionId || ""
|
||||
const vpnService = data.vpnService || ""
|
||||
|
||||
credentialsNeeded(credentialsToken, credentialsSSID, credentialsSetting, credentialsFields, credentialsHints, credentialsReason, connType, connName, vpnService)
|
||||
}
|
||||
|
||||
function addRef() {
|
||||
@@ -191,7 +212,6 @@ Singleton {
|
||||
if (response.result) {
|
||||
updateState(response.result)
|
||||
if (!initialStateFetched && response.result.wifiEnabled && (!response.result.wifiNetworks || response.result.wifiNetworks.length === 0)) {
|
||||
console.log("NetworkManagerService: Initial state has no networks, triggering scan")
|
||||
initialStateFetched = true
|
||||
Qt.callLater(() => scanWifi())
|
||||
}
|
||||
@@ -203,6 +223,8 @@ Singleton {
|
||||
const previousConnecting = isConnecting
|
||||
const previousConnectingSSID = connectingSSID
|
||||
|
||||
backend = state.backend || ""
|
||||
vpnAvailable = networkAvailable && backend === "networkmanager"
|
||||
networkStatus = state.networkStatus || "disconnected"
|
||||
primaryConnection = state.primaryConnection || ""
|
||||
|
||||
@@ -245,6 +267,12 @@ Singleton {
|
||||
networksUpdated()
|
||||
}
|
||||
|
||||
if (state.vpnProfiles) {
|
||||
vpnProfiles = state.vpnProfiles
|
||||
}
|
||||
|
||||
vpnActive = state.vpnActive || []
|
||||
|
||||
userPreference = state.preference || "auto"
|
||||
isConnecting = state.isConnecting || false
|
||||
connectingSSID = state.connectingSSID || ""
|
||||
@@ -254,7 +282,7 @@ Singleton {
|
||||
if (pendingConnectionSSID) {
|
||||
if (wifiConnected && currentWifiSSID === pendingConnectionSSID && wifiIP) {
|
||||
const elapsed = Date.now() - pendingConnectionStartTime
|
||||
console.info("NetworkManagerService: Successfully connected to", pendingConnectionSSID, "in", elapsed, "ms")
|
||||
console.info("DMSNetworkService: Successfully connected to", pendingConnectionSSID, "in", elapsed, "ms")
|
||||
ToastService.showInfo(`Connected to ${pendingConnectionSSID}`)
|
||||
|
||||
if (userPreference === "wifi" || userPreference === "auto") {
|
||||
@@ -264,16 +292,17 @@ Singleton {
|
||||
pendingConnectionSSID = ""
|
||||
connectionStatus = "connected"
|
||||
} else if (previousConnecting && !isConnecting && !wifiConnected) {
|
||||
const elapsed = Date.now() - pendingConnectionStartTime
|
||||
const isCancellationError = connectionError === "user-canceled"
|
||||
const isBadCredentials = connectionError === "bad-credentials"
|
||||
|
||||
if (elapsed < 5000) {
|
||||
console.log("NetworkManagerService: Quick connection failure, likely authentication error")
|
||||
if (isCancellationError) {
|
||||
connectionStatus = "cancelled"
|
||||
pendingConnectionSSID = ""
|
||||
} else if (isBadCredentials) {
|
||||
connectionStatus = "invalid_password"
|
||||
pendingConnectionSSID = ""
|
||||
} else {
|
||||
console.log("NetworkManagerService: Connection failed for", pendingConnectionSSID)
|
||||
if (connectionError === "connection-failed") {
|
||||
ToastService.showError(I18n.tr("Connection failed. Check password and try again."))
|
||||
} else if (connectionError) {
|
||||
if (connectionError) {
|
||||
ToastService.showError(I18n.tr("Failed to connect to ") + pendingConnectionSSID)
|
||||
}
|
||||
connectionStatus = "failed"
|
||||
@@ -315,14 +344,12 @@ Singleton {
|
||||
function scanWifi() {
|
||||
if (!networkAvailable || isScanning || !wifiEnabled) return
|
||||
|
||||
console.log("NetworkManagerService: Starting WiFi scan...")
|
||||
isScanning = true
|
||||
DMSService.sendRequest("network.wifi.scan", null, response => {
|
||||
isScanning = false
|
||||
if (response.error) {
|
||||
console.warn("NetworkManagerService: WiFi scan failed:", response.error)
|
||||
console.warn("DMSNetworkService: WiFi scan failed:", response.error)
|
||||
} else {
|
||||
console.info("NetworkManagerService: Scan completed")
|
||||
Qt.callLater(() => getState())
|
||||
}
|
||||
})
|
||||
@@ -362,15 +389,15 @@ Singleton {
|
||||
|
||||
DMSService.sendRequest("network.wifi.connect", params, response => {
|
||||
if (response.error) {
|
||||
console.log("NetworkManagerService: Connection request failed:", response.error)
|
||||
if (connectionStatus === "cancelled") {
|
||||
return
|
||||
}
|
||||
|
||||
connectionError = response.error
|
||||
lastConnectionError = response.error
|
||||
pendingConnectionSSID = ""
|
||||
connectionStatus = "failed"
|
||||
ToastService.showError(I18n.tr("Failed to start connection to ") + ssid)
|
||||
} else {
|
||||
console.log("NetworkManagerService: Connection request sent for", ssid)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -390,7 +417,12 @@ Singleton {
|
||||
}
|
||||
|
||||
function submitCredentials(token, secrets, save) {
|
||||
if (!networkAvailable || DMSService.apiVersion < 7) return
|
||||
console.log("submitCredentials: networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion)
|
||||
|
||||
if (!networkAvailable || DMSService.apiVersion < 7) {
|
||||
console.warn("submitCredentials: Aborting - networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion)
|
||||
return
|
||||
}
|
||||
|
||||
const params = {
|
||||
token: token,
|
||||
@@ -398,13 +430,11 @@ Singleton {
|
||||
save: save || false
|
||||
}
|
||||
|
||||
console.log("NetworkManagerService: Submitting credentials for token", token)
|
||||
|
||||
credentialsRequested = false
|
||||
|
||||
DMSService.sendRequest("network.credentials.submit", params, response => {
|
||||
if (response.error) {
|
||||
console.warn("NetworkManagerService: Failed to submit credentials:", response.error)
|
||||
console.warn("DMSNetworkService: Failed to submit credentials:", response.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -413,19 +443,16 @@ Singleton {
|
||||
if (!networkAvailable || DMSService.apiVersion < 7) return
|
||||
|
||||
const params = {
|
||||
token: token,
|
||||
cancel: true
|
||||
token: token
|
||||
}
|
||||
|
||||
console.log("NetworkManagerService: Cancelling credentials for token", token)
|
||||
|
||||
credentialsRequested = false
|
||||
pendingConnectionSSID = ""
|
||||
connectionStatus = "cancelled"
|
||||
|
||||
DMSService.sendRequest("network.credentials.submit", params, response => {
|
||||
DMSService.sendRequest("network.credentials.cancel", params, response => {
|
||||
if (response.error) {
|
||||
console.warn("NetworkManagerService: Failed to cancel credentials:", response.error)
|
||||
console.warn("DMSNetworkService: Failed to cancel credentials:", response.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -679,31 +706,125 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function refreshVpnProfiles() {
|
||||
if (!vpnAvailable) return
|
||||
|
||||
DMSService.sendRequest("network.vpn.profiles", null, response => {
|
||||
if (response.result) {
|
||||
vpnProfiles = response.result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function refreshVpnActive() {
|
||||
if (!vpnAvailable) return
|
||||
|
||||
DMSService.sendRequest("network.vpn.active", null, response => {
|
||||
if (response.result) {
|
||||
vpnActive = response.result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function connectVpn(uuidOrName, singleActive = false) {
|
||||
if (!vpnAvailable || vpnIsBusy) return
|
||||
|
||||
vpnIsBusy = true
|
||||
|
||||
const params = {
|
||||
uuidOrName: uuidOrName,
|
||||
singleActive: singleActive
|
||||
}
|
||||
|
||||
DMSService.sendRequest("network.vpn.connect", params, response => {
|
||||
vpnIsBusy = false
|
||||
|
||||
if (response.error) {
|
||||
ToastService.showError(I18n.tr("Failed to connect VPN"))
|
||||
} else {
|
||||
Qt.callLater(() => getState())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function connect(uuidOrName, singleActive = false) {
|
||||
connectVpn(uuidOrName, singleActive)
|
||||
}
|
||||
|
||||
function disconnectVpn(uuidOrName) {
|
||||
if (!vpnAvailable || vpnIsBusy) return
|
||||
|
||||
vpnIsBusy = true
|
||||
|
||||
const params = {
|
||||
uuidOrName: uuidOrName
|
||||
}
|
||||
|
||||
DMSService.sendRequest("network.vpn.disconnect", params, response => {
|
||||
vpnIsBusy = false
|
||||
|
||||
if (response.error) {
|
||||
ToastService.showError(I18n.tr("Failed to disconnect VPN"))
|
||||
} else {
|
||||
Qt.callLater(() => getState())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function disconnect(uuidOrName) {
|
||||
disconnectVpn(uuidOrName)
|
||||
}
|
||||
|
||||
function disconnectAllVpns() {
|
||||
if (!vpnAvailable || vpnIsBusy) return
|
||||
|
||||
vpnIsBusy = true
|
||||
|
||||
DMSService.sendRequest("network.vpn.disconnectAll", null, response => {
|
||||
vpnIsBusy = false
|
||||
|
||||
if (response.error) {
|
||||
ToastService.showError(I18n.tr("Failed to disconnect VPNs"))
|
||||
} else {
|
||||
Qt.callLater(() => getState())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function disconnectAllActive() {
|
||||
disconnectAllVpns()
|
||||
}
|
||||
|
||||
function toggleVpn(uuid) {
|
||||
if (uuid) {
|
||||
if (isActiveVpnUuid(uuid)) {
|
||||
disconnectVpn(uuid)
|
||||
} else {
|
||||
connectVpn(uuid)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (vpnProfiles.length > 0) {
|
||||
connectVpn(vpnProfiles[0].uuid)
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(uuid) {
|
||||
toggleVpn(uuid)
|
||||
}
|
||||
|
||||
function isActiveVpnUuid(uuid) {
|
||||
return activeUuids && activeUuids.indexOf(uuid) !== -1
|
||||
}
|
||||
|
||||
function isActiveUuid(uuid) {
|
||||
return isActiveVpnUuid(uuid)
|
||||
}
|
||||
|
||||
function refreshNetworkState() {
|
||||
if (networkAvailable) {
|
||||
getState()
|
||||
}
|
||||
}
|
||||
|
||||
function splitNmcliFields(line) {
|
||||
const parts = []
|
||||
let cur = ""
|
||||
let escape = false
|
||||
for (var i = 0; i < line.length; i++) {
|
||||
const ch = line[i]
|
||||
if (escape) {
|
||||
cur += ch
|
||||
escape = false
|
||||
} else if (ch === '\\') {
|
||||
escape = true
|
||||
} else if (ch === ':') {
|
||||
parts.push(cur)
|
||||
cur = ""
|
||||
} else {
|
||||
cur += ch
|
||||
}
|
||||
}
|
||||
parts.push(cur)
|
||||
return parts
|
||||
}
|
||||
}
|
||||
@@ -271,6 +271,7 @@ Singleton {
|
||||
|
||||
function sendRequest(method, params, callback) {
|
||||
if (!isConnected) {
|
||||
console.warn("DMSService.sendRequest: Not connected, method:", method)
|
||||
if (callback) {
|
||||
callback({
|
||||
"error": "not connected to DMS socket"
|
||||
@@ -294,6 +295,7 @@ Singleton {
|
||||
pendingRequests[id] = callback
|
||||
}
|
||||
|
||||
console.log("DMSService.sendRequest: Sending request id=" + id + " method=" + method)
|
||||
requestSocket.send(request)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ Singleton {
|
||||
id: root
|
||||
|
||||
property bool networkAvailable: activeService !== null
|
||||
property string backend: activeService?.backend ?? ""
|
||||
property string networkStatus: activeService?.networkStatus ?? "disconnected"
|
||||
property string primaryConnection: activeService?.primaryConnection ?? ""
|
||||
|
||||
@@ -79,7 +80,7 @@ Singleton {
|
||||
|
||||
signal networksUpdated
|
||||
signal connectionChanged
|
||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason)
|
||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason, string connType, string connName, string vpnService)
|
||||
|
||||
property bool usingLegacy: false
|
||||
property var activeService: null
|
||||
@@ -97,16 +98,16 @@ Singleton {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: NetworkManagerService
|
||||
target: DMSNetworkService
|
||||
|
||||
function onNetworkAvailableChanged() {
|
||||
if (!activeService && NetworkManagerService.networkAvailable) {
|
||||
console.info("NetworkService: Network capability detected, using NetworkManagerService")
|
||||
activeService = NetworkManagerService
|
||||
if (!activeService && DMSNetworkService.networkAvailable) {
|
||||
console.info("NetworkService: Network capability detected, using DMSNetworkService")
|
||||
activeService = DMSNetworkService
|
||||
usingLegacy = false
|
||||
console.info("NetworkService: Switched to NetworkManagerService, networkAvailable:", networkAvailable)
|
||||
console.info("NetworkService: Switched to DMSNetworkService, networkAvailable:", networkAvailable)
|
||||
connectSignals()
|
||||
} else if (!activeService && !NetworkManagerService.networkAvailable && socketPath && socketPath.length > 0) {
|
||||
} else if (!activeService && !DMSNetworkService.networkAvailable && socketPath && socketPath.length > 0) {
|
||||
console.info("NetworkService: Network capability not available in DMS, using LegacyNetworkService")
|
||||
useLegacyService()
|
||||
}
|
||||
|
||||
@@ -177,49 +177,47 @@ Singleton {
|
||||
}
|
||||
|
||||
function sortWindowsByLayout(windowList) {
|
||||
return [...windowList].sort((a, b) => {
|
||||
const aWorkspace = workspaces[a.workspace_id]
|
||||
const bWorkspace = workspaces[b.workspace_id]
|
||||
const enriched = windowList.map(w => {
|
||||
const ws = workspaces[w.workspace_id]
|
||||
if (!ws) {
|
||||
return {
|
||||
window: w,
|
||||
outputX: 999999,
|
||||
outputY: 999999,
|
||||
wsIdx: 999999,
|
||||
col: 999999,
|
||||
row: 999999
|
||||
}
|
||||
}
|
||||
|
||||
if (aWorkspace && bWorkspace) {
|
||||
const aOutput = aWorkspace.output
|
||||
const bOutput = bWorkspace.output
|
||||
const outputInfo = outputs[ws.output]
|
||||
const outputX = (outputInfo && outputInfo.logical) ? outputInfo.logical.x : 999999
|
||||
const outputY = (outputInfo && outputInfo.logical) ? outputInfo.logical.y : 999999
|
||||
|
||||
const aOutputInfo = outputs[aOutput]
|
||||
const bOutputInfo = outputs[bOutput]
|
||||
const pos = w.layout?.pos_in_scrolling_layout
|
||||
const col = (pos && pos.length >= 2) ? pos[0] : 999999
|
||||
const row = (pos && pos.length >= 2) ? pos[1] : 999999
|
||||
|
||||
if (aOutputInfo && bOutputInfo && aOutputInfo.logical && bOutputInfo.logical) {
|
||||
if (aOutputInfo.logical.x !== bOutputInfo.logical.x) {
|
||||
return aOutputInfo.logical.x - bOutputInfo.logical.x
|
||||
}
|
||||
if (aOutputInfo.logical.y !== bOutputInfo.logical.y) {
|
||||
return aOutputInfo.logical.y - bOutputInfo.logical.y
|
||||
}
|
||||
}
|
||||
return {
|
||||
window: w,
|
||||
outputX: outputX,
|
||||
outputY: outputY,
|
||||
wsIdx: ws.idx,
|
||||
col: col,
|
||||
row: row
|
||||
}
|
||||
})
|
||||
|
||||
if (aOutput === bOutput && aWorkspace.idx !== bWorkspace.idx) {
|
||||
return aWorkspace.idx - bWorkspace.idx
|
||||
}
|
||||
}
|
||||
enriched.sort((a, b) => {
|
||||
if (a.outputX !== b.outputX) return a.outputX - b.outputX
|
||||
if (a.outputY !== b.outputY) return a.outputY - b.outputY
|
||||
if (a.wsIdx !== b.wsIdx) return a.wsIdx - b.wsIdx
|
||||
if (a.col !== b.col) return a.col - b.col
|
||||
if (a.row !== b.row) return a.row - b.row
|
||||
return a.window.id - b.window.id
|
||||
})
|
||||
|
||||
if (a.workspace_id === b.workspace_id && a.layout && b.layout) {
|
||||
if (a.layout.pos_in_scrolling_layout && b.layout.pos_in_scrolling_layout) {
|
||||
const aPos = a.layout.pos_in_scrolling_layout
|
||||
const bPos = b.layout.pos_in_scrolling_layout
|
||||
|
||||
if (aPos.length > 1 && bPos.length > 1) {
|
||||
if (aPos[0] !== bPos[0]) {
|
||||
return aPos[0] - bPos[0]
|
||||
}
|
||||
if (aPos[1] !== bPos[1]) {
|
||||
return aPos[1] - bPos[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a.id - b.id
|
||||
})
|
||||
return enriched.map(e => e.window)
|
||||
}
|
||||
|
||||
function handleNiriEvent(event) {
|
||||
@@ -235,6 +233,9 @@ Singleton {
|
||||
case 'WorkspaceActiveWindowChanged':
|
||||
handleWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged)
|
||||
break
|
||||
case 'WindowFocusChanged':
|
||||
handleWindowFocusChanged(event.WindowFocusChanged)
|
||||
break
|
||||
case 'WindowsChanged':
|
||||
handleWindowsChanged(event.WindowsChanged)
|
||||
break
|
||||
@@ -269,14 +270,18 @@ Singleton {
|
||||
}
|
||||
|
||||
function handleWorkspacesChanged(data) {
|
||||
const workspaces = {}
|
||||
const newWorkspaces = {}
|
||||
|
||||
for (const ws of data.workspaces) {
|
||||
workspaces[ws.id] = ws
|
||||
const oldWs = root.workspaces[ws.id]
|
||||
newWorkspaces[ws.id] = ws
|
||||
if (oldWs && oldWs.active_window_id !== undefined) {
|
||||
newWorkspaces[ws.id].active_window_id = oldWs.active_window_id
|
||||
}
|
||||
}
|
||||
|
||||
root.workspaces = workspaces
|
||||
allWorkspaces = [...data.workspaces].sort((a, b) => a.idx - b.idx)
|
||||
root.workspaces = newWorkspaces
|
||||
allWorkspaces = Object.values(newWorkspaces).sort((a, b) => a.idx - b.idx)
|
||||
|
||||
focusedWorkspaceIndex = allWorkspaces.findIndex(w => w.is_focused)
|
||||
if (focusedWorkspaceIndex >= 0) {
|
||||
@@ -298,33 +303,101 @@ Singleton {
|
||||
}
|
||||
const output = ws.output
|
||||
|
||||
const updatedWorkspaces = {}
|
||||
|
||||
for (const id in root.workspaces) {
|
||||
const workspace = root.workspaces[id]
|
||||
const got_activated = workspace.id === data.id
|
||||
|
||||
const updatedWs = {}
|
||||
for (let prop in workspace) {
|
||||
updatedWs[prop] = workspace[prop]
|
||||
}
|
||||
|
||||
if (workspace.output === output) {
|
||||
workspace.is_active = got_activated
|
||||
updatedWs.is_active = got_activated
|
||||
}
|
||||
|
||||
if (data.focused) {
|
||||
workspace.is_focused = got_activated
|
||||
updatedWs.is_focused = got_activated
|
||||
}
|
||||
|
||||
updatedWorkspaces[id] = updatedWs
|
||||
}
|
||||
|
||||
root.workspaces = updatedWorkspaces
|
||||
|
||||
focusedWorkspaceId = data.id
|
||||
focusedWorkspaceIndex = allWorkspaces.findIndex(w => w.id === data.id)
|
||||
focusedWorkspaceIndex = Object.values(updatedWorkspaces).findIndex(w => w.id === data.id)
|
||||
|
||||
if (focusedWorkspaceIndex >= 0) {
|
||||
currentOutput = allWorkspaces[focusedWorkspaceIndex].output || ""
|
||||
const ws = Object.values(updatedWorkspaces)[focusedWorkspaceIndex]
|
||||
currentOutput = ws.output || ""
|
||||
}
|
||||
|
||||
allWorkspaces = Object.values(root.workspaces).sort((a, b) => a.idx - b.idx)
|
||||
allWorkspaces = Object.values(updatedWorkspaces).sort((a, b) => a.idx - b.idx)
|
||||
|
||||
updateCurrentOutputWorkspaces()
|
||||
workspacesChanged()
|
||||
}
|
||||
|
||||
function handleWindowFocusChanged(data) {
|
||||
const focusedWindowId = data.id
|
||||
|
||||
let focusedWindow = null
|
||||
const updatedWindows = []
|
||||
|
||||
for (var i = 0; i < windows.length; i++) {
|
||||
const w = windows[i]
|
||||
const updatedWindow = {}
|
||||
|
||||
for (let prop in w) {
|
||||
updatedWindow[prop] = w[prop]
|
||||
}
|
||||
|
||||
updatedWindow.is_focused = (w.id === focusedWindowId)
|
||||
if (updatedWindow.is_focused) {
|
||||
focusedWindow = updatedWindow
|
||||
}
|
||||
|
||||
updatedWindows.push(updatedWindow)
|
||||
}
|
||||
|
||||
windows = updatedWindows
|
||||
|
||||
if (focusedWindow) {
|
||||
const ws = root.workspaces[focusedWindow.workspace_id]
|
||||
if (ws && ws.active_window_id !== focusedWindowId) {
|
||||
const updatedWs = {}
|
||||
for (let prop in ws) {
|
||||
updatedWs[prop] = ws[prop]
|
||||
}
|
||||
updatedWs.active_window_id = focusedWindowId
|
||||
|
||||
const updatedWorkspaces = {}
|
||||
for (const id in root.workspaces) {
|
||||
updatedWorkspaces[id] = id === focusedWindow.workspace_id ? updatedWs : root.workspaces[id]
|
||||
}
|
||||
root.workspaces = updatedWorkspaces
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleWorkspaceActiveWindowChanged(data) {
|
||||
const ws = root.workspaces[data.workspace_id]
|
||||
if (ws) {
|
||||
const updatedWs = {}
|
||||
for (let prop in ws) {
|
||||
updatedWs[prop] = ws[prop]
|
||||
}
|
||||
updatedWs.active_window_id = data.active_window_id
|
||||
|
||||
const updatedWorkspaces = {}
|
||||
for (const id in root.workspaces) {
|
||||
updatedWorkspaces[id] = id === data.workspace_id ? updatedWs : root.workspaces[id]
|
||||
}
|
||||
root.workspaces = updatedWorkspaces
|
||||
}
|
||||
|
||||
const updatedWindows = []
|
||||
|
||||
for (var i = 0; i < windows.length; i++) {
|
||||
@@ -366,10 +439,9 @@ Singleton {
|
||||
const updatedWindows = [...windows]
|
||||
updatedWindows[existingIndex] = window
|
||||
windows = sortWindowsByLayout(updatedWindows)
|
||||
return
|
||||
} else {
|
||||
windows = sortWindowsByLayout([...windows, window])
|
||||
}
|
||||
|
||||
windows = sortWindowsByLayout([...windows, window])
|
||||
}
|
||||
|
||||
function handleWindowLayoutsChanged(data) {
|
||||
@@ -400,7 +472,6 @@ Singleton {
|
||||
return
|
||||
|
||||
windows = sortWindowsByLayout(updatedWindows)
|
||||
windowsChanged()
|
||||
}
|
||||
|
||||
function handleOutputsChanged(data) {
|
||||
@@ -452,12 +523,19 @@ Singleton {
|
||||
if (!ws)
|
||||
return
|
||||
|
||||
ws.is_urgent = data.urgent
|
||||
|
||||
const idx = allWorkspaces.findIndex(w => w.id === data.id)
|
||||
if (idx >= 0) {
|
||||
allWorkspaces[idx].is_urgent = data.urgent
|
||||
const updatedWs = {}
|
||||
for (let prop in ws) {
|
||||
updatedWs[prop] = ws[prop]
|
||||
}
|
||||
updatedWs.is_urgent = data.urgent
|
||||
|
||||
const updatedWorkspaces = {}
|
||||
for (const id in root.workspaces) {
|
||||
updatedWorkspaces[id] = id === data.id ? updatedWs : root.workspaces[id]
|
||||
}
|
||||
root.workspaces = updatedWorkspaces
|
||||
|
||||
allWorkspaces = Object.values(updatedWorkspaces).sort((a, b) => a.idx - b.idx)
|
||||
|
||||
windowUrgentChanged()
|
||||
}
|
||||
@@ -637,10 +715,13 @@ Singleton {
|
||||
|
||||
usedToplevels.add(bestMatch)
|
||||
|
||||
const workspace = workspaces[niriWindow.workspace_id]
|
||||
const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false
|
||||
|
||||
const enrichedToplevel = {
|
||||
"appId": bestMatch.appId,
|
||||
"title": bestMatch.title,
|
||||
"activated": niriWindow.is_focused ?? false,
|
||||
"activated": isFocused,
|
||||
"niriWindowId": niriWindow.id,
|
||||
"niriWorkspaceId": niriWindow.workspace_id,
|
||||
"activate": function () {
|
||||
@@ -723,10 +804,13 @@ Singleton {
|
||||
|
||||
usedToplevels.add(bestMatch)
|
||||
|
||||
const workspace = workspaces[niriWindow.workspace_id]
|
||||
const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false
|
||||
|
||||
const enrichedToplevel = {
|
||||
"appId": bestMatch.appId,
|
||||
"title": bestMatch.title,
|
||||
"activated": niriWindow.is_focused ?? false,
|
||||
"activated": isFocused,
|
||||
"niriWindowId": niriWindow.id,
|
||||
"niriWorkspaceId": niriWindow.workspace_id,
|
||||
"activate": function () {
|
||||
|
||||
@@ -263,4 +263,22 @@ Singleton {
|
||||
running: refCount > 0 && distributionSupported && (pkgManager || updChecker)
|
||||
onTriggered: checkForUpdates()
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "systemupdater"
|
||||
|
||||
function updatestatus(): string {
|
||||
if (root.isChecking) {
|
||||
return "ERROR: already checking"
|
||||
}
|
||||
if (!distributionSupported) {
|
||||
return "ERROR: distribution not supported"
|
||||
}
|
||||
if (!pkgManager && !updChecker) {
|
||||
return "ERROR: update checker not available"
|
||||
}
|
||||
root.checkForUpdates()
|
||||
return "SUCCESS: Now checking..."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
// Minimal VPN controller backed by NetworkManager (nmcli + D-Bus monitor)
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property int refCount: 0
|
||||
|
||||
onRefCountChanged: {
|
||||
console.log("VpnService: refCount changed to", refCount)
|
||||
if (refCount > 0 && !nmMonitor.running) {
|
||||
console.log("VpnService: Starting nmMonitor")
|
||||
nmMonitor.running = true
|
||||
refreshAll()
|
||||
} else if (refCount === 0 && nmMonitor.running) {
|
||||
console.log("VpnService: Stopping nmMonitor")
|
||||
nmMonitor.running = false
|
||||
}
|
||||
}
|
||||
|
||||
// State
|
||||
property bool available: true
|
||||
property bool isBusy: false
|
||||
property string errorMessage: ""
|
||||
|
||||
// Profiles discovered on the system
|
||||
// [{ name, uuid, type }]
|
||||
property var profiles: []
|
||||
|
||||
// Allow multiple active VPNs (set true to allow concurrent connections)
|
||||
// Default: allow multiple, to align with NetworkManager capability
|
||||
property bool singleActive: false
|
||||
|
||||
// Active VPN connections (may be multiple)
|
||||
// Full list and convenience projections
|
||||
property var activeConnections: [] // [{ name, uuid, device, state }]
|
||||
property var activeUuids: []
|
||||
property var activeNames: []
|
||||
// Back-compat single values (first active if present)
|
||||
property string activeUuid: activeUuids.length > 0 ? activeUuids[0] : ""
|
||||
property string activeName: activeNames.length > 0 ? activeNames[0] : ""
|
||||
property string activeDevice: activeConnections.length > 0 ? (activeConnections[0].device || "") : ""
|
||||
property string activeState: activeConnections.length > 0 ? (activeConnections[0].state || "") : ""
|
||||
property bool connected: activeUuids.length > 0
|
||||
|
||||
// Use implicit property notify signals (profilesChanged, activeUuidChanged, etc.)
|
||||
|
||||
function refreshAll() {
|
||||
listProfiles()
|
||||
refreshActive()
|
||||
}
|
||||
|
||||
// Monitor NetworkManager changes and refresh on activity
|
||||
Process {
|
||||
id: nmMonitor
|
||||
command: ["gdbus", "monitor", "--system", "--dest", "org.freedesktop.NetworkManager"]
|
||||
running: false
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: line => {
|
||||
if (line.includes("ActiveConnection") || line.includes("PropertiesChanged") || line.includes("StateChanged")) {
|
||||
refreshAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Query all VPN profiles
|
||||
function listProfiles() {
|
||||
getProfiles.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: getProfiles
|
||||
command: ["bash", "-lc", "nmcli -t -f NAME,UUID,TYPE connection show | while IFS=: read -r name uuid type; do case \"$type\" in vpn) svc=$(nmcli -g vpn.service-type connection show uuid \"$uuid\" 2>/dev/null); echo \"$name:$uuid:$type:$svc\" ;; wireguard) echo \"$name:$uuid:$type:\" ;; *) : ;; esac; done"]
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = text.trim().length ? text.trim().split('\n') : []
|
||||
const out = []
|
||||
for (const line of lines) {
|
||||
const parts = line.split(':')
|
||||
if (parts.length >= 3 && (parts[2] === "vpn" || parts[2] === "wireguard")) {
|
||||
const svc = parts.length >= 4 ? parts[3] : ""
|
||||
out.push({ name: parts[0], uuid: parts[1], type: parts[2], serviceType: svc })
|
||||
}
|
||||
}
|
||||
root.profiles = out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Query active VPN connection
|
||||
function refreshActive() {
|
||||
getActive.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: getActive
|
||||
command: ["nmcli", "-t", "-f", "NAME,UUID,TYPE,DEVICE,STATE", "connection", "show", "--active"]
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = text.trim().length ? text.trim().split('\n') : []
|
||||
let act = []
|
||||
for (const line of lines) {
|
||||
const parts = line.split(':')
|
||||
if (parts.length >= 5 && (parts[2] === "vpn" || parts[2] === "wireguard")) {
|
||||
act.push({ name: parts[0], uuid: parts[1], device: parts[3], state: parts[4] })
|
||||
}
|
||||
}
|
||||
root.activeConnections = act
|
||||
root.activeUuids = act.map(a => a.uuid).filter(u => !!u)
|
||||
root.activeNames = act.map(a => a.name).filter(n => !!n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isActiveUuid(uuid) {
|
||||
return root.activeUuids && root.activeUuids.indexOf(uuid) !== -1
|
||||
}
|
||||
|
||||
function _looksLikeUuid(s) {
|
||||
// Very loose check for UUID pattern
|
||||
return s && s.indexOf('-') !== -1 && s.length >= 8
|
||||
}
|
||||
|
||||
function connect(uuidOrName) {
|
||||
if (root.isBusy) return
|
||||
root.isBusy = true
|
||||
root.errorMessage = ""
|
||||
if (root.singleActive) {
|
||||
// Bring down all active VPNs, then bring up the requested one
|
||||
const isUuid = _looksLikeUuid(uuidOrName)
|
||||
const escaped = ('' + uuidOrName).replace(/'/g, "'\\''")
|
||||
const upCmd = isUuid ? `nmcli connection up uuid '${escaped}'` : `nmcli connection up id '${escaped}'`
|
||||
const script = `set -e\n` +
|
||||
`nmcli -t -f UUID,TYPE connection show --active | awk -F: '$2 ~ /^(vpn|wireguard)$/ {print $1}' | while read u; do [ -n \"$u\" ] && nmcli connection down uuid \"$u\" || true; done\n` +
|
||||
upCmd + `\n`
|
||||
vpnSwitch.command = ["bash", "-lc", script]
|
||||
vpnSwitch.running = true
|
||||
} else {
|
||||
if (_looksLikeUuid(uuidOrName)) {
|
||||
vpnUp.command = ["nmcli", "connection", "up", "uuid", uuidOrName]
|
||||
} else {
|
||||
vpnUp.command = ["nmcli", "connection", "up", "id", uuidOrName]
|
||||
}
|
||||
vpnUp.running = true
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect(uuidOrName) {
|
||||
if (root.isBusy) return
|
||||
root.isBusy = true
|
||||
root.errorMessage = ""
|
||||
if (_looksLikeUuid(uuidOrName)) {
|
||||
vpnDown.command = ["nmcli", "connection", "down", "uuid", uuidOrName]
|
||||
} else {
|
||||
vpnDown.command = ["nmcli", "connection", "down", "id", uuidOrName]
|
||||
}
|
||||
vpnDown.running = true
|
||||
}
|
||||
|
||||
function toggle(uuid) {
|
||||
if (uuid) {
|
||||
if (isActiveUuid(uuid)) disconnect(uuid)
|
||||
else connect(uuid)
|
||||
return
|
||||
}
|
||||
if (root.profiles.length > 0) {
|
||||
connect(root.profiles[0].uuid)
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: vpnUp
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.isBusy = false
|
||||
if (!text.toLowerCase().includes("successfully")) {
|
||||
root.errorMessage = text.trim()
|
||||
}
|
||||
refreshAll()
|
||||
}
|
||||
}
|
||||
onExited: exitCode => {
|
||||
root.isBusy = false
|
||||
if (exitCode !== 0 && root.errorMessage === "") {
|
||||
root.errorMessage = "Failed to connect VPN"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: vpnDown
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.isBusy = false
|
||||
if (!text.toLowerCase().includes("deactivated") && !text.toLowerCase().includes("successfully")) {
|
||||
root.errorMessage = text.trim()
|
||||
}
|
||||
refreshAll()
|
||||
}
|
||||
}
|
||||
onExited: exitCode => {
|
||||
root.isBusy = false
|
||||
if (exitCode !== 0 && root.errorMessage === "") {
|
||||
root.errorMessage = "Failed to disconnect VPN"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function disconnectAllActive() {
|
||||
if (root.isBusy) return
|
||||
root.isBusy = true
|
||||
const script = `nmcli -t -f UUID,TYPE connection show --active | awk -F: '$2 ~ /^(vpn|wireguard)$/ {print $1}' | while read u; do [ -n \"$u\" ] && nmcli connection down uuid \"$u\" || true; done`
|
||||
vpnSwitch.command = ["bash", "-lc", script]
|
||||
vpnSwitch.running = true
|
||||
}
|
||||
|
||||
// Sequenced down/up using a single shell for exclusive switch
|
||||
Process {
|
||||
id: vpnSwitch
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.isBusy = false
|
||||
refreshAll()
|
||||
}
|
||||
}
|
||||
onExited: exitCode => {
|
||||
root.isBusy = false
|
||||
if (exitCode !== 0 && root.errorMessage === "") {
|
||||
root.errorMessage = "Failed to switch VPN"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
StyledText {
|
||||
id: icon
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias name: icon.text
|
||||
property alias size: icon.font.pixelSize
|
||||
property alias color: icon.color
|
||||
@@ -11,46 +11,60 @@ StyledText {
|
||||
property real fill: filled ? 1.0 : 0.0
|
||||
property int grade: Theme.isLightMode ? 0 : -25
|
||||
property int weight: filled ? 500 : 400
|
||||
|
||||
|
||||
implicitWidth: icon.implicitWidth
|
||||
implicitHeight: icon.implicitHeight
|
||||
|
||||
signal rotationCompleted()
|
||||
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: weight
|
||||
color: Theme.surfaceText
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
renderType: Text.NativeRendering
|
||||
antialiasing: true
|
||||
font.variableAxes: {
|
||||
"FILL": fill.toFixed(1),
|
||||
"GRAD": grade,
|
||||
"opsz": 24,
|
||||
"wght": weight
|
||||
|
||||
FontLoader {
|
||||
id: materialSymbolsFont
|
||||
source: Qt.resolvedUrl("../assets/fonts/material-design-icons/variablefont/MaterialSymbolsRounded[FILL,GRAD,opsz,wght].ttf")
|
||||
}
|
||||
|
||||
|
||||
StyledText {
|
||||
id: icon
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
font.family: materialSymbolsFont.name
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: root.weight
|
||||
color: Theme.surfaceText
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
renderType: Text.NativeRendering
|
||||
antialiasing: true
|
||||
font.variableAxes: {
|
||||
"FILL": root.fill.toFixed(1),
|
||||
"GRAD": root.grade,
|
||||
"opsz": 24,
|
||||
"wght": root.weight
|
||||
}
|
||||
|
||||
Behavior on font.weight {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on fill {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on weight {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: rotationTimer
|
||||
interval: 16
|
||||
repeat: false
|
||||
onTriggered: icon.rotationCompleted()
|
||||
onTriggered: root.rotationCompleted()
|
||||
}
|
||||
|
||||
|
||||
onRotationChanged: {
|
||||
rotationTimer.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,22 @@ import qs.Services
|
||||
Text {
|
||||
property bool isMonospace: false
|
||||
|
||||
FontLoader {
|
||||
id: interFont
|
||||
source: Qt.resolvedUrl("../assets/fonts/inter/InterVariable.ttf")
|
||||
}
|
||||
|
||||
FontLoader {
|
||||
id: firaCodeFont
|
||||
source: Qt.resolvedUrl("../assets/fonts/nerd-fonts/FiraCodeNerdFont-Regular.ttf")
|
||||
}
|
||||
|
||||
readonly property string resolvedFontFamily: {
|
||||
const requestedFont = isMonospace ? SettingsData.monoFontFamily : SettingsData.fontFamily
|
||||
const defaultFont = isMonospace ? SettingsData.defaultMonoFontFamily : SettingsData.defaultFontFamily
|
||||
|
||||
if (requestedFont === defaultFont) {
|
||||
const availableFonts = Qt.fontFamilies()
|
||||
if (!availableFonts.includes(requestedFont)) {
|
||||
return isMonospace ? "Monospace" : "DejaVu Sans"
|
||||
}
|
||||
return isMonospace ? firaCodeFont.name : interFont.name
|
||||
}
|
||||
return requestedFont
|
||||
}
|
||||
|
||||
BIN
assets/fonts/inter/InterVariable.ttf
Normal file
BIN
assets/fonts/inter/InterVariable.ttf
Normal file
Binary file not shown.
92
assets/fonts/inter/LICENSE.txt
Normal file
92
assets/fonts/inter/LICENSE.txt
Normal file
@@ -0,0 +1,92 @@
|
||||
Copyright (c) 2016 The Inter Project Authors (https://github.com/rsms/inter)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION AND CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
164
assets/fonts/inter/README.md
Normal file
164
assets/fonts/inter/README.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Inter
|
||||
|
||||
Inter is a typeface carefully crafted & designed for computer screens.
|
||||
Inter features a tall x-height to aid in readability of mixed-case and lower-case text.
|
||||
Inter is a [variable font](https://rsms.me/inter/#variable) with
|
||||
several [OpenType features](https://rsms.me/inter/#features), like contextual alternates that adjusts punctuation depending on the shape of surrounding glyphs, slashed zero for when you need to disambiguate "0" from "o", tabular numbers, etc.
|
||||
|
||||
[**Download Inter font files…**](https://github.com/rsms/inter/releases/latest)
|
||||
|
||||
<br>
|
||||
|
||||
[](https://rsms.me/inter/)
|
||||
|
||||
|
||||
### Quick questions
|
||||
|
||||
- **Where can I get Inter?** [Here](https://github.com/rsms/inter/releases/latest)
|
||||
- **I think I found a bug. How can I let you know?** [Open an issue here](https://github.com/rsms/inter/issues/new?template=bug_report.md)
|
||||
- **I have a question. Where can I get help?** [Post in Discussions Q&A](https://github.com/rsms/inter/discussions/categories/q-a)
|
||||
- **Should I use Inter from Google Fonts?** No, unless you have no other choice.
|
||||
(outdated, no italics)
|
||||
- **Can I legally use Inter for my purpose?** Most likely _yes!_ Inter is free and open source.
|
||||
([Read the license](LICENSE.txt) for details.)
|
||||
|
||||
|
||||
## Using & installing Inter
|
||||
|
||||
[**Download the latest font files…**](https://github.com/rsms/inter/releases/latest)
|
||||
|
||||
Using Inter on a web page:
|
||||
|
||||
```html
|
||||
<link rel="preconnect" href="https://your-font-file-host/">
|
||||
<link rel="stylesheet" href="https://your-font-file-host/inter.css">
|
||||
```
|
||||
|
||||
```css
|
||||
:root { font-family: 'Inter', sans-serif; }
|
||||
@supports (font-variation-settings: normal) {
|
||||
:root { font-family: 'Inter var', sans-serif; }
|
||||
}
|
||||
```
|
||||
|
||||
For web pages, there's an official [CDN distribution](https://rsms.me/inter/inter.css) that you can use directly without having to host the font files yourself:
|
||||
|
||||
```html
|
||||
<link rel="preconnect" href="https://rsms.me/">
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||||
```
|
||||
|
||||
|
||||
### Alternate distributions
|
||||
|
||||
- [NPM `inter-ui`](https://www.npmjs.com/package/inter-ui)
|
||||
- [Homebrew `font-inter`](https://github.com/Homebrew/homebrew-cask-fonts)
|
||||
- [Ubuntu `fonts-inter`](https://packages.ubuntu.com/search?keywords=fonts-inter)
|
||||
- [List of Inter available on various Linux distributions…](https://repology.org/project/fonts:inter/versions)
|
||||
- [Google Fonts](https://fonts.google.com/specimen/Inter)
|
||||
|
||||
**Disclaimer:** Alternate distributions may not always be up-to-date.
|
||||
|
||||
|
||||
## Notable uses of Inter
|
||||
|
||||
- [ElementaryOS](https://elementary.io/)
|
||||
- [Figma](https://figma.com/)
|
||||
- [GitLab](https://gitlab.com/)
|
||||
- [Guggenheim museums](https://www.pentagram.com/work/guggenheim-3) ([case study](https://www.pentagram.com/work/guggenheim-3/))
|
||||
- [ISO (International Organization for Standardization)](https://www.iso.org/) ([case study](https://www.motherbird.com.au/projects/iso/))
|
||||
- [Minimalissimo magazine](https://minimalissimo.com/)
|
||||
- [Mozilla](https://mozilla.design/firefox/typography/)
|
||||
- [NASA](https://www.nasa.gov/specials/artemis-ii/)
|
||||
- [Pixar Presto](https://en.wikipedia.org/wiki/Presto_(animation_software))
|
||||
- [Switzerland, Canton of Zurich](https://www.zh.ch/)
|
||||
- [Unity](https://unity.com/)
|
||||
- [Zurich Airport](https://flughafen-zuerich.ch/)
|
||||
|
||||
|
||||
> **Have you made something nice with Inter?**<br>
|
||||
> [Please share in Show & Tell! →](https://github.com/rsms/inter/discussions/categories/show-and-tell)
|
||||
|
||||
|
||||
### Notable forks
|
||||
|
||||
- [Open Runde](https://github.com/lauridskern/open-runde) is a rounded variant of Inter
|
||||
- [Interalia](https://github.com/Shavian-info/interalia) extends Inter with Shavian characters
|
||||
- [Raveo](https://github.com/jakubfoglar/raveo) is a "warmer version" of Inter
|
||||
|
||||
|
||||
## Supporters & contributors
|
||||
|
||||
A wholehearted **Thank You** to everyone who supports the Inter project!
|
||||
|
||||
Special thanks to
|
||||
[@thundernixon](https://github.com/thundernixon) and
|
||||
[@KatjaSchimmel](https://github.com/KatjaSchimmel)
|
||||
who have put in significant effort into making Inter what it is through
|
||||
their contributions ♡
|
||||
|
||||
See [graphs/contributors](https://github.com/rsms/inter/graphs/contributors)
|
||||
for a complete list of all contributors.
|
||||
|
||||
|
||||
## Contributing to this project
|
||||
|
||||
For instructions on how to work with the source files and how to
|
||||
[compile & build font files](CONTRIBUTING.md#compiling-font-files),
|
||||
refer to [**CONTRIBUTING.md**](CONTRIBUTING.md).
|
||||
|
||||
Inter is licensed under the [SIL Open Font License](LICENSE.txt)
|
||||
|
||||
|
||||
## Creating derivative fonts
|
||||
|
||||
Inter is open source which means you can make your own versions with your own changes.
|
||||
However when doing so, please [**read LICENSE.txt carefully.**](LICENSE.txt) It is a standard **SIL Open Font License 1.1**:
|
||||
|
||||
> The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
> development of collaborative font projects, to support the font creation
|
||||
> efforts of academic and linguistic communities, and to provide a free and
|
||||
> open framework in which fonts may be shared and improved in partnership
|
||||
> with others.
|
||||
>
|
||||
> The OFL allows the licensed fonts to be used, studied, modified and
|
||||
> redistributed freely as long as they are not sold by themselves. The
|
||||
> fonts, including any derivative works, can be bundled, embedded,
|
||||
> redistributed and/or sold with any software provided that any reserved
|
||||
> names are not used by derivative works. The fonts and derivatives,
|
||||
> however, cannot be released under any other type of license. The
|
||||
> requirement for fonts to remain under this license does not apply
|
||||
> to any document created using the fonts or their derivatives.
|
||||
|
||||
While you are allowed to use Inter commercially, i.e. bundled with product or service which makes you money, you are NOT allowed to sell Inter itself or derivatives of Inter. If you would like to do so, please [reach out](https://github.com/rsms) and we can talk about it.
|
||||
|
||||
Inter a trademark of Rasmus Andersson (DBA: RSMS)
|
||||
|
||||
"Inter" is a Reserved Font Name by Rasmus Andersson
|
||||
([font vendor code RSMS.](https://learn.microsoft.com/en-us/typography/vendors/#r))
|
||||
|
||||
|
||||
## Design
|
||||
|
||||
_This section discusses some of the design choices made for Inter._
|
||||
|
||||
Inter can be classified as a geometric neo-grotesque, similar in style to Roboto, Apple San Francisco, Akkurat, Asap, Lucida Grande and more. Some trade-offs were made in order to make this typeface work really well at small sizes:
|
||||
|
||||
- Early versions of Inter was not suitable for very large sizes because of some small-scale glyph optimizations (like "pits" and "traps") that help rasterization at small sizes but stand out and interfere at large sizes. However today Inter works well at large sizes and a [Display subfamily](https://github.com/rsms/inter/releases/tag/display-beta-1) is in the works for really large "display" sizes.
|
||||
|
||||
- Rasterized at sizes below 12px, some stems—like the horizontal center of "E", "F", or vertical center of "m"—are drawn with two semi-opaque pixels instead of one solid. This is because we "prioritize" (optimize for) higher-density rasterizations. If we move these stems to an off-center position—so that they can be drawn sharply at e.g. 11px—text will be less legible at higher resolutions.
|
||||
|
||||
Inter is a [variable font](https://rsms.me/inter/#variable) and is in addition also distributed as a set of traditional distinct font files in the following styles:
|
||||
|
||||
| Roman (upright) name | Italic name | Weight
|
||||
| -------------------- | -------------------- | ------------
|
||||
| Thin | Thin Italic | 100
|
||||
| Extra Light | Extra Light Italic | 200
|
||||
| Light | Light Italic | 300
|
||||
| Regular | Italic | 400
|
||||
| Medium | Medium Italic | 500
|
||||
| Semi Bold | Semi Bold Italic | 600
|
||||
| Bold | Bold Italic | 700
|
||||
| Extra Bold | Extra Bold Italic | 800
|
||||
| Black | Black Italic | 900
|
||||
|
||||
202
assets/fonts/material-design-icons/LICENSE
Normal file
202
assets/fonts/material-design-icons/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
141
assets/fonts/material-design-icons/README.md
Normal file
141
assets/fonts/material-design-icons/README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
## Material Symbols / Material Icons
|
||||
|
||||
These are two different official icon sets from Google, using the same underlying designs. Material Symbols is the current set, introduced in April 2022, built on variable font technology. Material Icons is the classic set, but no longer updated. More details below.
|
||||
|
||||
The icons can be browsed in a more user-friendly way at https://fonts.google.com/icons. Use the popdown menu near top left to choose between the two sets; Material Symbols is the default.
|
||||
|
||||
The icons are designed under the [material design guidelines](https://material.io/guidelines/).
|
||||
|
||||
## Icon Requests
|
||||
|
||||
We’d love to support your icon needs! Please submit your request here on GitHub as an issue.
|
||||
|
||||
Please note that Google Fonts does not accept user submissions of finished icon designs! There are fairly strict guidelines for Material icons, plus Google has upstream source files from which this repo is generated. Therefore, Google does not accept pull requests for icon files (whether new icon suggestions, or fixes for existing icons). Concepts are appreciated—just don’t design SVGs and submit them via pull request.
|
||||
|
||||
However, users are perfectly welcome to point at outside files or images as examples—for the kind of thing they want, but they won’t just be taken “as is.” This works especially well if you have multiple examples for a single icon, to help us understand the “essence” of the idea.
|
||||
|
||||
> For example, there is a fairly universal conceptual logo/icon for “agender,” so if you were proposing Google add an agender icon in the Material style, either mentioning that, or pointing at https://www.google.com/search?q=agender+icon would be a helpful tip.
|
||||
|
||||
### Third-party logos
|
||||
|
||||
Currently, Google does not include 3rd-party logos among the Material Symbols or Material Icons due to legal reasons. Some 3rd-party logos that were included in the past have since been removed.
|
||||
|
||||
## npm Packages
|
||||
|
||||
Google does not currently maintain the npm package for this repo, past v3 (2016). However, user @marella is hosting the following. He tells us these are automatically updated and published using GitHub Actions. Note: Google does **not** monitor or vet these packages.
|
||||
|
||||
### [material-symbols](https://github.com/marella/material-symbols/tree/main/material-symbols#readme) [](https://www.npmjs.com/package/material-symbols) [](https://packagephobia.com/result?p=material-symbols)
|
||||
|
||||
- Only WOFF2 variable fonts and CSS for Material Symbols
|
||||
- Includes outlined, rounded, and sharp icons and all variations of fill, weight, grade, and optical size
|
||||
- Supports Sass
|
||||
|
||||
### [material-icons](https://github.com/marella/material-icons#readme) [](https://www.npmjs.com/package/material-icons) [](https://packagephobia.com/result?p=material-icons) [](https://www.npmjs.com/package/material-icons)
|
||||
|
||||
- Only WOFF2, WOFF fonts and CSS
|
||||
- Includes outlined, round, sharp and two-tone icons
|
||||
- Supports Sass
|
||||
|
||||
### [@material-design-icons/font](https://github.com/marella/material-design-icons/tree/main/font#readme) [](https://www.npmjs.com/package/@material-design-icons/font) [](https://packagephobia.com/result?p=@material-design-icons/font)
|
||||
|
||||
- Only WOFF2 fonts and CSS
|
||||
- Lighter version of `material-icons` package
|
||||
- Doesn't support [older browsers](https://caniuse.com/woff2) such as Internet Explorer because of dropping WOFF (v1)
|
||||
|
||||
### [@material-design-icons/svg](https://github.com/marella/material-design-icons/tree/main/svg#readme) [](https://www.npmjs.com/package/@material-design-icons/svg) [](https://packagephobia.com/result?p=@material-design-icons/svg)
|
||||
|
||||
- Only SVGs
|
||||
- Optimizes SVGs using SVGO
|
||||
|
||||
## Material Symbols
|
||||
|
||||
These newer icons can be browsed in a more user-friendly way at https://fonts.google.com/icons. Use the popdown menu near top left to choose between the two sets; Material Symbols is the default.
|
||||
|
||||
These icons were built/designed as variable fonts first (based on the 24 px designs from Material Icons). There are three separate Material Symbols variable fonts, which also have static icons available (but those do not have all the variations available, as that would be hundreds of styles):
|
||||
- Outlined
|
||||
- Rounded
|
||||
- Sharp
|
||||
- Note that although there is no separate Filled font, the Fill axis allows access to filled styles, in all three fonts. It can also be manipulated for an animated fill effect, to indicate user selection.
|
||||
|
||||
Each of the fonts has these design axes, which can be varied in CSS, or in many more modern design apps:
|
||||
- Optical Size (opsz) from 20 to 48 px. The default is 24.
|
||||
- Weight from 100 (Thin) to 700 (Bold). Regular is 400.
|
||||
- Grade from -50 to 200. The default is 0 (zero). -50 is suggested for reversed contrast (e.g. white icons on black background)
|
||||
- Fill from 0 to 100. The default is 0 (zero).
|
||||
|
||||
The following directories in this repo contain specifically Material Symbols (not Material Icons) content:
|
||||
- symbols
|
||||
- variablefont
|
||||
|
||||
What is currently _not_ available in Material Symbols?
|
||||
- only the 20 and 24 px versions are designed with perfect pixel-grid alignment
|
||||
- the only pre-made fonts are the variable fonts
|
||||
- there are no two-tone icons
|
||||
|
||||
## Material Icons
|
||||
|
||||
The icons can be browsed in a more user-friendly way at https://fonts.google.com/icons?icon.set=Material+Icons
|
||||
|
||||
These classic icons are available in five distinct styles:
|
||||
- Outlined
|
||||
- Filled (the font version is just called Material Icons, as this is the oldest style)
|
||||
- Rounded
|
||||
- Sharp
|
||||
- Two tone
|
||||
|
||||
The following directories in this repo contain specifically Material Icons (not Material Symbols) content:
|
||||
- android
|
||||
- font
|
||||
- ios
|
||||
- png
|
||||
- src
|
||||
|
||||
What is currently _not_ available in Material Icons?
|
||||
- variable fonts
|
||||
- weights other than Regular
|
||||
- grades other than Regular
|
||||
- a means to animate Fill transitions
|
||||
- new icons (since updates were halted in 2022)
|
||||
|
||||
## Material Icons update history
|
||||
|
||||
### 4.0.0 Update
|
||||
* 2020 Aug 31
|
||||
* Restructured repository, updated assets.
|
||||
|
||||
### 3.0.1 Update
|
||||
* 2016 Sep 01
|
||||
* Changed license in package.json.
|
||||
* Added missing device symbol sprites.
|
||||
|
||||
### 3.0.0 Update
|
||||
* 2016 Aug 25
|
||||
* License change to Apache 2.0!
|
||||
|
||||
### 2.0
|
||||
* 2016 May 28
|
||||
|
||||
## Getting Started
|
||||
|
||||
Read the [developer guide](https://developers.google.com/fonts/docs/material_icons) on how to use the material design icons in your project.
|
||||
|
||||
### Using a font
|
||||
|
||||
The `font` and `variablefont` folders contain pre-generated font files that can be included in a project. This is especially convenient for the web; however, it is generally better to link to the web font hosted on Google Fonts:
|
||||
|
||||
```html
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Icons"
|
||||
rel="stylesheet">
|
||||
```
|
||||
|
||||
```html
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined"
|
||||
rel="stylesheet">
|
||||
```
|
||||
Read more on [Material Symbols](https://developers.google.com/fonts/docs/material_symbols/) or [Material Icons](https://developers.google.com/fonts/docs/material_icons/) in the Google Fonts developer guide.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
We have made these icons available for you to incorporate into your products under the [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt). Feel free to remix and re-share these icons and documentation in your products.
|
||||
We'd love attribution in your app's *about* screen, but it's not required.
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Bold.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Light.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Light.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Medium.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Medium.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Regular.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Retina.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-Retina.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-SemiBold.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFont-SemiBold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Bold.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Light.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Light.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Medium.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Medium.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Regular.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Retina.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-Retina.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-SemiBold.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontMono-SemiBold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Bold.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Light.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Light.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Medium.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Medium.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Regular.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Retina.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-Retina.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-SemiBold.ttf
Normal file
BIN
assets/fonts/nerd-fonts/FiraCodeNerdFontPropo-SemiBold.ttf
Normal file
Binary file not shown.
93
assets/fonts/nerd-fonts/LICENSE
Normal file
93
assets/fonts/nerd-fonts/LICENSE
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
48
assets/fonts/nerd-fonts/README.md
Normal file
48
assets/fonts/nerd-fonts/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Nerd Fonts
|
||||
|
||||
This is an archived font from the Nerd Fonts release v3.4.0.
|
||||
|
||||
For more information see:
|
||||
* https://github.com/ryanoasis/nerd-fonts/
|
||||
* https://github.com/ryanoasis/nerd-fonts/releases/latest/
|
||||
|
||||
# Fira Code
|
||||
|
||||
**Fira Code** is a free monospaced font with programming ligatures.
|
||||
|
||||
For more information have a look at the upstream website: https://github.com/tonsky/FiraCode
|
||||
|
||||
Version: 6.2
|
||||
|
||||
## Which font?
|
||||
|
||||
### TL;DR
|
||||
|
||||
* Pick your font family:
|
||||
* If you are limited to monospaced fonts (because of your terminal, etc) then pick a font with `Nerd Font Mono` (or `NFM`).
|
||||
* If you want to have bigger icons (usually around 1.5 normal letters wide) pick a font without `Mono` i.e. `Nerd Font` (or `NF`). Most terminals support this, but ymmv.
|
||||
* If you work in a proportional context (GUI elements or edit a presentation etc) pick a font with `Nerd Font Propo` (or `NFP`).
|
||||
|
||||
### Ligatures
|
||||
|
||||
Ligatures are generally preserved in the patched fonts.
|
||||
Nerd Fonts `v2.0.0` had no ligatures in the `Nerd Font Mono` fonts, this has been dropped with `v2.1.0`.
|
||||
If you have a ligature-aware terminal and don't want ligatures you can (usually) disable them in the terminal settings.
|
||||
|
||||
### Explanation
|
||||
|
||||
Once you narrow down your font choice of family (`Droid Sans`, `Inconsolata`, etc) and style (`bold`, `italic`, etc) you have 2 main choices:
|
||||
|
||||
#### `Option 1: Download already patched font`
|
||||
|
||||
* For a stable version download a font package from the [release page](https://github.com/ryanoasis/nerd-fonts/releases)
|
||||
* Or download the development version from the folders here
|
||||
|
||||
#### `Option 2: Patch your own font`
|
||||
|
||||
* Patch your own variations with the various options provided by the font patcher (i.e. not include all symbols for smaller font size)
|
||||
|
||||
For more information see: [The FAQ](https://github.com/ryanoasis/nerd-fonts/wiki/FAQ-and-Troubleshooting#which-font)
|
||||
|
||||
[SIL-RFN]:http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web_fonts_and_RFNs#14cbfd4a
|
||||
|
||||
@@ -19,7 +19,6 @@ BuildRequires: rpkg
|
||||
|
||||
Requires: greetd
|
||||
Requires: (quickshell-git or quickshell)
|
||||
Requires: material-symbols-fonts
|
||||
Requires(post): /usr/sbin/useradd
|
||||
Requires(post): /usr/sbin/groupadd
|
||||
|
||||
@@ -46,9 +45,9 @@ authentication, and dynamic theming.
|
||||
# QML-based application
|
||||
|
||||
%install
|
||||
# Install greeter files to XDG config location
|
||||
install -dm755 %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter
|
||||
cp -r * %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/
|
||||
# Install greeter files to shared data location
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms-greeter
|
||||
cp -r * %{buildroot}%{_datadir}/quickshell/dms-greeter/
|
||||
|
||||
# Install launcher script
|
||||
install -Dm755 Modules/Greetd/assets/dms-greeter %{buildroot}%{_bindir}/dms-greeter
|
||||
@@ -123,7 +122,7 @@ echo "Creating symlinks to sync theme..."
|
||||
declare -A links=(
|
||||
["$HOME/.config/DankMaterialShell/settings.json"]="$CACHE_DIR/settings.json"
|
||||
["$HOME/.local/state/DankMaterialShell/session.json"]="$CACHE_DIR/session.json"
|
||||
["$HOME/.cache/quickshell/dankshell/dms-colors.json"]="$CACHE_DIR/colors.json"
|
||||
["$HOME/.cache/DankMaterialShell/dms-colors.json"]="$CACHE_DIR/colors.json"
|
||||
)
|
||||
|
||||
for source in "${!links[@]}"; do
|
||||
@@ -160,19 +159,28 @@ install -dm755 %{buildroot}%{_sharedstatedir}/greeter
|
||||
# Instead, we verify/fix it in %post if needed
|
||||
|
||||
# Remove build and development files
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/.git*
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/.gitignore
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/.github
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/*.spec
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/dms.spec
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms-greeter/dms-greeter.spec
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/.github
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/*.spec
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/dms.spec
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/dms-greeter.spec
|
||||
|
||||
%posttrans
|
||||
# Clean up old installation path from previous versions (only if empty)
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms-greeter" ]; then
|
||||
# Remove directories only if empty (preserves any user-added files)
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms-greeter" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc %{_docdir}/dms-greeter/README.md
|
||||
%{_bindir}/dms-greeter
|
||||
%{_bindir}/dms-greeter-sync
|
||||
%{_sysconfdir}/xdg/quickshell/dms-greeter/
|
||||
%{_datadir}/quickshell/dms-greeter/
|
||||
%dir %attr(0750,greeter,greeter) %{_localstatedir}/cache/dms-greeter
|
||||
%dir %attr(0755,greeter,greeter) %{_sharedstatedir}/greeter
|
||||
|
||||
@@ -200,9 +208,9 @@ if [ -x /usr/sbin/semanage ] && [ -x /usr/sbin/restorecon ]; then
|
||||
semanage fcontext -a -t cache_home_t '%{_localstatedir}/cache/dms-greeter(/.*)?' >/dev/null 2>&1 || true
|
||||
restorecon -R %{_localstatedir}/cache/dms-greeter >/dev/null 2>&1 || true
|
||||
|
||||
# Config directory
|
||||
semanage fcontext -a -t etc_t '%{_sysconfdir}/xdg/quickshell/dms-greeter(/.*)?' >/dev/null 2>&1 || true
|
||||
restorecon -R %{_sysconfdir}/xdg/quickshell/dms-greeter >/dev/null 2>&1 || true
|
||||
# Shared data directory
|
||||
semanage fcontext -a -t usr_t '%{_datadir}/quickshell/dms-greeter(/.*)?' >/dev/null 2>&1 || true
|
||||
restorecon -R %{_datadir}/quickshell/dms-greeter >/dev/null 2>&1 || true
|
||||
|
||||
# PAM configuration
|
||||
restorecon %{_sysconfdir}/pam.d/greetd >/dev/null 2>&1 || true
|
||||
@@ -300,17 +308,19 @@ fi
|
||||
|
||||
# Only show banner on initial install
|
||||
if [ "$1" -eq 1 ]; then
|
||||
cat << EOF
|
||||
cat << 'EOF'
|
||||
|
||||
===============================================================================
|
||||
DMS Greeter Installation Complete!
|
||||
===============================================================================
|
||||
=========================================================================
|
||||
DMS Greeter Installation Complete!
|
||||
=========================================================================
|
||||
|
||||
Status:
|
||||
- Greeter user: Created ✓
|
||||
- Greeter directories: /var/cache/dms-greeter, /var/lib/greeter ✓
|
||||
- SELinux contexts: Applied ✓
|
||||
- Greetd config: $CONFIG_STATUS
|
||||
✓ Greeter user: Created
|
||||
✓ Greeter directories: /var/cache/dms-greeter, /var/lib/greeter
|
||||
✓ SELinux contexts: Applied
|
||||
EOF
|
||||
echo " ✓ Greetd config: $CONFIG_STATUS"
|
||||
cat << 'EOF'
|
||||
|
||||
Next steps:
|
||||
|
||||
@@ -327,7 +337,7 @@ Next steps:
|
||||
|
||||
Ready to test? Reboot or run: sudo systemctl start greetd
|
||||
Documentation: /usr/share/doc/dms-greeter/README.md
|
||||
===============================================================================
|
||||
=========================================================================
|
||||
|
||||
EOF
|
||||
fi
|
||||
@@ -338,7 +348,7 @@ if [ "$1" -eq 0 ] && [ -x /usr/sbin/semanage ]; then
|
||||
semanage fcontext -d '%{_bindir}/dms-greeter' 2>/dev/null || true
|
||||
semanage fcontext -d '%{_sharedstatedir}/greeter(/.*)?' 2>/dev/null || true
|
||||
semanage fcontext -d '%{_localstatedir}/cache/dms-greeter(/.*)?' 2>/dev/null || true
|
||||
semanage fcontext -d '%{_sysconfdir}/xdg/quickshell/dms-greeter(/.*)?' 2>/dev/null || true
|
||||
semanage fcontext -d '%{_datadir}/quickshell/dms-greeter(/.*)?' 2>/dev/null || true
|
||||
fi
|
||||
|
||||
%changelog
|
||||
|
||||
61
dms.spec
61
dms.spec
@@ -27,11 +27,9 @@ BuildRequires: wget
|
||||
|
||||
# Core requirements
|
||||
Requires: (quickshell-git or quickshell)
|
||||
Requires: accountsservice
|
||||
Requires: dms-cli
|
||||
Requires: dgop
|
||||
Requires: fira-code-fonts
|
||||
Requires: material-symbols-fonts
|
||||
Requires: rsms-inter-fonts
|
||||
|
||||
# Core utilities (Highly recommended for DMS functionality)
|
||||
Recommends: brightnessctl
|
||||
@@ -128,20 +126,61 @@ install -Dm755 %{_builddir}/danklinux-master/bin/${DMS_BINARY} %{buildroot}%{_bi
|
||||
# Install dgop binary
|
||||
install -Dm755 %{_builddir}/dgop %{buildroot}%{_bindir}/dgop
|
||||
|
||||
# Install shell files to XDG config location
|
||||
install -dm755 %{buildroot}%{_sysconfdir}/xdg/quickshell/dms
|
||||
cp -r * %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/
|
||||
# Install shell files to shared data location
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||
cp -r * %{buildroot}%{_datadir}/quickshell/dms/
|
||||
|
||||
# Remove build files
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.github
|
||||
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/*.spec
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/*.spec
|
||||
|
||||
%posttrans
|
||||
# Clean up old installation path from previous versions (only if empty)
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||
# Remove directories only if empty (preserves any user-added files)
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Restart DMS for active users after upgrade
|
||||
if [ "$1" -ge 2 ]; then
|
||||
# Find all quickshell DMS processes (PID and username)
|
||||
while read pid cmd; do
|
||||
username=$(ps -o user= -p "$pid" 2>/dev/null)
|
||||
|
||||
[ "$username" = "root" ] && continue
|
||||
[ -z "$username" ] && continue
|
||||
|
||||
# Get user's UID and validate session
|
||||
user_uid=$(id -u "$username" 2>/dev/null)
|
||||
[ -z "$user_uid" ] && continue
|
||||
[ ! -d "/run/user/$user_uid" ] && continue
|
||||
|
||||
wayland_display=$(tr '\0' '\n' < /proc/$pid/environ 2>/dev/null | grep '^WAYLAND_DISPLAY=' | cut -d= -f2)
|
||||
[ -z "$wayland_display" ] && continue
|
||||
|
||||
echo "Restarting DMS for user: $username"
|
||||
|
||||
# Run as user with full Wayland session environment
|
||||
runuser -u "$username" -- /bin/sh -c "
|
||||
export XDG_RUNTIME_DIR=/run/user/$user_uid
|
||||
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$user_uid/bus
|
||||
export WAYLAND_DISPLAY=$wayland_display
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:\$PATH
|
||||
dms restart >/dev/null 2>&1
|
||||
" 2>/dev/null || true
|
||||
|
||||
break
|
||||
done < <(pgrep -a -f 'quickshell.*dms' 2>/dev/null)
|
||||
fi
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc README.md CONTRIBUTING.md
|
||||
%{_sysconfdir}/xdg/quickshell/dms/
|
||||
%{_datadir}/quickshell/dms/
|
||||
|
||||
%files -n dms-cli
|
||||
%{_bindir}/dms
|
||||
|
||||
15
docs/IPC.md
15
docs/IPC.md
@@ -407,6 +407,21 @@ dms ipc call bar hide
|
||||
dms ipc call bar status
|
||||
```
|
||||
|
||||
## Target: `systemupdater`
|
||||
|
||||
System updater external check request.
|
||||
|
||||
### Functions
|
||||
|
||||
**`updatestatus`**
|
||||
- Trigger a system update check
|
||||
- Returns: Success confirmation
|
||||
|
||||
### Examples
|
||||
```bash
|
||||
dms ipc call systemupdater updatestatus
|
||||
```
|
||||
|
||||
## Modal Controls
|
||||
|
||||
These targets control various modal windows and overlays.
|
||||
|
||||
12
nix/niri.nix
12
nix/niri.nix
@@ -90,9 +90,15 @@ in {
|
||||
})
|
||||
|
||||
(lib.mkIf cfg.niri.enableSpawn {
|
||||
spawn-at-startup = [
|
||||
{command = ["dms" "run"];}
|
||||
];
|
||||
spawn-at-startup =
|
||||
[
|
||||
{command = ["dms" "run"];}
|
||||
]
|
||||
++ lib.optionals cfg.enableClipboard [
|
||||
{
|
||||
command = ["wl-paste" "--watch" "cliphist" "store"];
|
||||
}
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
{
|
||||
"term": "24-hour format",
|
||||
"context": "24-hour format",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:887",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:939",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -152,13 +152,13 @@
|
||||
{
|
||||
"term": "Animation Speed",
|
||||
"context": "Animation Speed",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1047",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1160",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Anonymous Identity (optional)",
|
||||
"context": "Anonymous Identity (optional)",
|
||||
"reference": "Modals/WifiPasswordModal.qml:365",
|
||||
"reference": "Modals/WifiPasswordModal.qml:389",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -332,7 +332,7 @@
|
||||
{
|
||||
"term": "Automatic Cycling",
|
||||
"context": "Automatic Cycling",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:640",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:692",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -344,7 +344,7 @@
|
||||
{
|
||||
"term": "Automatically cycle through wallpapers in the same folder",
|
||||
"context": "Automatically cycle through wallpapers in the same folder",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:647",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:699",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -362,7 +362,7 @@
|
||||
{
|
||||
"term": "Automatically extract colors from wallpaper",
|
||||
"context": "Automatically extract colors from wallpaper",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1251",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1364",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -404,7 +404,7 @@
|
||||
{
|
||||
"term": "Back",
|
||||
"context": "Back",
|
||||
"reference": "Modules/DankBar/Widgets/SystemTrayBar.qml:479",
|
||||
"reference": "Modules/DankBar/Widgets/SystemTrayBar.qml:477",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -449,6 +449,24 @@
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:69",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Blur Layer",
|
||||
"context": "Blur Layer",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1047",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Blur on Overview",
|
||||
"context": "Blur on Overview",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:489",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Blur wallpaper when niri overview is open",
|
||||
"context": "Blur wallpaper when niri overview is open",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:496",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Border",
|
||||
"context": "Border",
|
||||
@@ -542,7 +560,7 @@
|
||||
{
|
||||
"term": "Cancel",
|
||||
"context": "Cancel",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:249, Modals/DankColorPickerModal.qml:510, Modals/WifiPasswordModal.qml:468, Modules/Settings/PluginsTab.qml:1296, Modals/FileBrowser/FileBrowserModal.qml:952",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:249, Modals/DankColorPickerModal.qml:510, Modals/WifiPasswordModal.qml:492, Modules/Settings/PluginsTab.qml:1296, Modals/FileBrowser/FileBrowserModal.qml:952",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -638,7 +656,7 @@
|
||||
{
|
||||
"term": "Close",
|
||||
"context": "Close",
|
||||
"reference": "Modals/NetworkInfoModal.qml:129, Modals/NetworkWiredInfoModal.qml:129, Modules/SystemUpdatePopout.qml:333, Modules/DankBar/Widgets/RunningApps.qml:780",
|
||||
"reference": "Modals/NetworkInfoModal.qml:129, Modals/NetworkWiredInfoModal.qml:129, Modules/SystemUpdatePopout.qml:333, Modules/DankBar/Widgets/RunningApps.qml:788",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -716,7 +734,7 @@
|
||||
{
|
||||
"term": "Configuration activated",
|
||||
"context": "Configuration activated",
|
||||
"reference": "Services/NetworkManagerService.qml:308",
|
||||
"reference": "Services/DMSNetworkService.qml:337",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -746,13 +764,19 @@
|
||||
{
|
||||
"term": "Connect",
|
||||
"context": "Connect",
|
||||
"reference": "Modals/WifiPasswordModal.qml:505",
|
||||
"reference": "Modals/WifiPasswordModal.qml:534",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Connect to VPN",
|
||||
"context": "Connect to VPN",
|
||||
"reference": "Modals/WifiPasswordModal.qml:182",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Connect to Wi-Fi",
|
||||
"context": "Connect to Wi-Fi",
|
||||
"reference": "Modals/WifiPasswordModal.qml:166",
|
||||
"reference": "Modals/WifiPasswordModal.qml:184",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -761,12 +785,6 @@
|
||||
"reference": "Modules/Settings/DisplaysTab.qml:501",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Connection failed. Check password and try again.",
|
||||
"context": "Connection failed. Check password and try again.",
|
||||
"reference": "Services/NetworkManagerService.qml:275",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Contrast",
|
||||
"context": "Contrast",
|
||||
@@ -896,7 +914,7 @@
|
||||
{
|
||||
"term": "DEMO MODE - Click anywhere to exit",
|
||||
"context": "DEMO MODE - Click anywhere to exit",
|
||||
"reference": "Modules/Lock/LockScreenContent.qml:654",
|
||||
"reference": "Modules/Lock/LockScreenContent.qml:749",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -920,7 +938,7 @@
|
||||
{
|
||||
"term": "Daily at:",
|
||||
"context": "Daily at:",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:812",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:864",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1010,7 +1028,7 @@
|
||||
{
|
||||
"term": "Disconnected from WiFi",
|
||||
"context": "Disconnected from WiFi",
|
||||
"reference": "Services/NetworkManagerService.qml:385",
|
||||
"reference": "Services/DMSNetworkService.qml:412",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1106,7 +1124,7 @@
|
||||
{
|
||||
"term": "Domain (optional)",
|
||||
"context": "Domain (optional)",
|
||||
"reference": "Modals/WifiPasswordModal.qml:397",
|
||||
"reference": "Modals/WifiPasswordModal.qml:421",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1124,13 +1142,13 @@
|
||||
{
|
||||
"term": "Duration",
|
||||
"context": "Duration",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1100",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1213",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Dynamic Theming",
|
||||
"context": "Dynamic Theming",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1244",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1357",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1160,7 +1178,7 @@
|
||||
{
|
||||
"term": "Enable System Sounds",
|
||||
"context": "Enable System Sounds",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1398",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1511",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1172,7 +1190,13 @@
|
||||
{
|
||||
"term": "Enable WiFi",
|
||||
"context": "Enable WiFi",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:173",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:177",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.",
|
||||
"context": "Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1054",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1214,7 +1238,7 @@
|
||||
{
|
||||
"term": "Enter credentials for ",
|
||||
"context": "Enter credentials for ",
|
||||
"reference": "Modals/WifiPasswordModal.qml:178",
|
||||
"reference": "Modals/WifiPasswordModal.qml:200",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1244,7 +1268,7 @@
|
||||
{
|
||||
"term": "Enter password for ",
|
||||
"context": "Enter password for ",
|
||||
"reference": "Modals/WifiPasswordModal.qml:178",
|
||||
"reference": "Modals/WifiPasswordModal.qml:198, Modals/WifiPasswordModal.qml:200",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1256,7 +1280,7 @@
|
||||
{
|
||||
"term": "Execute templates from ~/.config/matugen/config.toml",
|
||||
"context": "Execute templates from ~/.config/matugen/config.toml",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1335",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1448",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1268,25 +1292,43 @@
|
||||
{
|
||||
"term": "Failed to activate configuration",
|
||||
"context": "Failed to activate configuration",
|
||||
"reference": "Services/NetworkManagerService.qml:304",
|
||||
"reference": "Services/DMSNetworkService.qml:333",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to connect VPN",
|
||||
"context": "Failed to connect VPN",
|
||||
"reference": "Services/DMSNetworkService.qml:743",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to connect to ",
|
||||
"context": "Failed to connect to ",
|
||||
"reference": "Services/NetworkManagerService.qml:277",
|
||||
"reference": "Services/DMSNetworkService.qml:306",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to disconnect VPN",
|
||||
"context": "Failed to disconnect VPN",
|
||||
"reference": "Services/DMSNetworkService.qml:767",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to disconnect VPNs",
|
||||
"context": "Failed to disconnect VPNs",
|
||||
"reference": "Services/DMSNetworkService.qml:787",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to disconnect WiFi",
|
||||
"context": "Failed to disconnect WiFi",
|
||||
"reference": "Services/NetworkManagerService.qml:383",
|
||||
"reference": "Services/DMSNetworkService.qml:410",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to enable WiFi",
|
||||
"context": "Failed to enable WiFi",
|
||||
"reference": "Services/NetworkManagerService.qml:484",
|
||||
"reference": "Services/DMSNetworkService.qml:511",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1310,7 +1352,7 @@
|
||||
{
|
||||
"term": "Failed to start connection to ",
|
||||
"context": "Failed to start connection to ",
|
||||
"reference": "Services/NetworkManagerService.qml:371",
|
||||
"reference": "Services/DMSNetworkService.qml:400",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1394,13 +1436,13 @@
|
||||
{
|
||||
"term": "Forget Network",
|
||||
"context": "Forget Network",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:606",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:610",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Forgot network ",
|
||||
"context": "Forgot network ",
|
||||
"reference": "Services/NetworkManagerService.qml:441",
|
||||
"reference": "Services/DMSNetworkService.qml:468",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1544,7 +1586,7 @@
|
||||
{
|
||||
"term": "How often to change wallpaper",
|
||||
"context": "How often to change wallpaper",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:758",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:810",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1598,13 +1640,13 @@
|
||||
{
|
||||
"term": "Include Transitions",
|
||||
"context": "Include Transitions",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:923",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:975",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Incorrect password",
|
||||
"context": "Incorrect password",
|
||||
"reference": "Modals/WifiPasswordModal.qml:189",
|
||||
"reference": "Modals/WifiPasswordModal.qml:211",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1640,7 +1682,7 @@
|
||||
{
|
||||
"term": "Interval",
|
||||
"context": "Interval",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:757",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:809",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1742,7 +1784,7 @@
|
||||
{
|
||||
"term": "Light Mode",
|
||||
"context": "Light Mode",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:991",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1104",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1844,13 +1886,13 @@
|
||||
{
|
||||
"term": "Matugen Palette",
|
||||
"context": "Matugen Palette",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1276, Modules/Settings/ThemeColorsTab.qml:629",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1389, Modules/Settings/ThemeColorsTab.qml:629",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Matugen Settings",
|
||||
"context": "Matugen Settings",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1219",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1332",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1916,19 +1958,19 @@
|
||||
{
|
||||
"term": "Mode:",
|
||||
"context": "Mode:",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:689",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:741",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Monitor",
|
||||
"context": "Monitor",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:592",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:644",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Monitor Selection:",
|
||||
"context": "Monitor Selection:",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:583",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:635",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1982,7 +2024,7 @@
|
||||
{
|
||||
"term": "Network Info",
|
||||
"context": "Network Info",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:340, Modules/ControlCenter/Details/NetworkDetail.qml:583",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:344, Modules/ControlCenter/Details/NetworkDetail.qml:587",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1994,7 +2036,7 @@
|
||||
{
|
||||
"term": "Network Settings",
|
||||
"context": "Network Settings",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:64",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:68",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2018,7 +2060,7 @@
|
||||
{
|
||||
"term": "New Notification",
|
||||
"context": "New Notification",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1512",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1625",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2108,7 +2150,7 @@
|
||||
{
|
||||
"term": "Notepad",
|
||||
"context": "Notepad",
|
||||
"reference": "DMSShell.qml:413, Modules/Settings/DankBarTab.qml:175",
|
||||
"reference": "DMSShell.qml:421, Modules/Settings/DankBarTab.qml:175",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2270,19 +2312,19 @@
|
||||
{
|
||||
"term": "Password",
|
||||
"context": "Password",
|
||||
"reference": "Modals/WifiPasswordModal.qml:274",
|
||||
"reference": "Modals/WifiPasswordModal.qml:296",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Per-Mode Wallpapers",
|
||||
"context": "Per-Mode Wallpapers",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:494",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:546",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Per-Monitor Wallpapers",
|
||||
"context": "Per-Monitor Wallpapers",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:551",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:603",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2330,31 +2372,31 @@
|
||||
{
|
||||
"term": "Play sound when new notification arrives",
|
||||
"context": "Play sound when new notification arrives",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1518",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1631",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Play sound when power cable is connected",
|
||||
"context": "Play sound when power cable is connected",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1587",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1700",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Play sound when volume is adjusted",
|
||||
"context": "Play sound when volume is adjusted",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1552",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1665",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Play sounds for system events",
|
||||
"context": "Play sounds for system events",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1405",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1518",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Plugged In",
|
||||
"context": "Plugged In",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1581",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1694",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2576,7 +2618,7 @@
|
||||
{
|
||||
"term": "Run User Templates",
|
||||
"context": "Run User Templates",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1328",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1441",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2678,7 +2720,7 @@
|
||||
{
|
||||
"term": "Select a preset or drag the slider to customize",
|
||||
"context": "Select a preset or drag the slider to customize",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1181",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1294",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2702,7 +2744,7 @@
|
||||
{
|
||||
"term": "Select monitor to configure wallpaper",
|
||||
"context": "Select monitor to configure wallpaper",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:593",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:645",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2720,19 +2762,19 @@
|
||||
{
|
||||
"term": "Select system sound theme",
|
||||
"context": "Select system sound theme",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1476",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1589",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Select the palette algorithm used for wallpaper-based colors",
|
||||
"context": "Select the palette algorithm used for wallpaper-based colors",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1277, Modules/Settings/ThemeColorsTab.qml:630",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1390, Modules/Settings/ThemeColorsTab.qml:630",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Select which transitions to include in randomization",
|
||||
"context": "Select which transitions to include in randomization",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:930",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:982",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2750,13 +2792,13 @@
|
||||
{
|
||||
"term": "Set different wallpapers for each connected monitor",
|
||||
"context": "Set different wallpapers for each connected monitor",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:558",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:610",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Set different wallpapers for light and dark mode",
|
||||
"context": "Set different wallpapers for light and dark mode",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:501",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:553",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2840,7 +2882,7 @@
|
||||
{
|
||||
"term": "Show password",
|
||||
"context": "Show password",
|
||||
"reference": "Modals/WifiPasswordModal.qml:440",
|
||||
"reference": "Modals/WifiPasswordModal.qml:464",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2906,7 +2948,7 @@
|
||||
{
|
||||
"term": "Sound Theme",
|
||||
"context": "Sound Theme",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1475",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1588",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2984,7 +3026,7 @@
|
||||
{
|
||||
"term": "Switch User",
|
||||
"context": "Switch User",
|
||||
"reference": "Modules/Greetd/GreeterContent.qml:549",
|
||||
"reference": "Modules/Greetd/GreeterContent.qml:645",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3218,7 +3260,7 @@
|
||||
{
|
||||
"term": "Transition Effect",
|
||||
"context": "Transition Effect",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:904",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:956",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3314,7 +3356,7 @@
|
||||
{
|
||||
"term": "Use System Theme",
|
||||
"context": "Use System Theme",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1447",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1560",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3338,13 +3380,13 @@
|
||||
{
|
||||
"term": "Use light theme instead of dark theme",
|
||||
"context": "Use light theme instead of dark theme",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:998",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1111",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Use sound theme from system settings",
|
||||
"context": "Use sound theme from system settings",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1453",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1566",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3362,7 +3404,7 @@
|
||||
{
|
||||
"term": "Username",
|
||||
"context": "Username",
|
||||
"reference": "Modals/WifiPasswordModal.qml:237",
|
||||
"reference": "Modals/WifiPasswordModal.qml:259",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3416,13 +3458,13 @@
|
||||
{
|
||||
"term": "Visual effect used when wallpaper changes",
|
||||
"context": "Visual effect used when wallpaper changes",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:905",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:957",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Volume Changed",
|
||||
"context": "Volume Changed",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1546",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1659",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3476,19 +3518,19 @@
|
||||
{
|
||||
"term": "WiFi disabled",
|
||||
"context": "WiFi disabled",
|
||||
"reference": "Services/NetworkManagerService.qml:474",
|
||||
"reference": "Services/DMSNetworkService.qml:501",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "WiFi enabled",
|
||||
"context": "WiFi enabled",
|
||||
"reference": "Services/NetworkManagerService.qml:474, Services/NetworkManagerService.qml:486",
|
||||
"reference": "Services/DMSNetworkService.qml:501, Services/DMSNetworkService.qml:513",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "WiFi is off",
|
||||
"context": "WiFi is off",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:155",
|
||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:159",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3590,7 +3632,7 @@
|
||||
{
|
||||
"term": "matugen not detected - dynamic theming unavailable",
|
||||
"context": "matugen not detected - dynamic theming unavailable",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1355",
|
||||
"reference": "Modules/Settings/PersonalizationTab.qml:1468",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
|
||||
@@ -227,6 +227,15 @@
|
||||
"Bluetooth Settings": {
|
||||
"Bluetooth Settings": "Bluetooth設定"
|
||||
},
|
||||
"Blur Layer": {
|
||||
"Blur Layer": "ぼかしレイヤー"
|
||||
},
|
||||
"Blur on Overview": {
|
||||
"Blur on Overview": "概要でぼかす"
|
||||
},
|
||||
"Blur wallpaper when niri overview is open": {
|
||||
"Blur wallpaper when niri overview is open": "Niri概要が開いているときに壁紙をぼかす"
|
||||
},
|
||||
"Border": {
|
||||
"Border": "ボーダー"
|
||||
},
|
||||
@@ -380,6 +389,9 @@
|
||||
"Connect": {
|
||||
"Connect": "接続"
|
||||
},
|
||||
"Connect to VPN": {
|
||||
"Connect to VPN": ""
|
||||
},
|
||||
"Connect to Wi-Fi": {
|
||||
"Connect to Wi-Fi": "Wi-Fiに接続"
|
||||
},
|
||||
@@ -564,7 +576,7 @@
|
||||
"Domain (optional)": "ドメイン (オプション)"
|
||||
},
|
||||
"Donate on Ko-fi": {
|
||||
"Donate on Ko-fi": ""
|
||||
"Donate on Ko-fi": "Ko-fiで寄付"
|
||||
},
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": {
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "ウィジェットをドラッグしてセクション内で順序を変更できます。目のアイコンでウィジェットを表示/非表示に(スペースは維持)、Xで完全に削除できます。"
|
||||
@@ -596,6 +608,9 @@
|
||||
"Enable WiFi": {
|
||||
"Enable WiFi": "WiFiを有効にする"
|
||||
},
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": {
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": "コンポジターをターゲットに設定できるぼかしレイヤー(名前空間:dms:blurwallpaper)を有効にします。Niri を手動で設定する必要があります。"
|
||||
},
|
||||
"Enable fingerprint authentication": {
|
||||
"Enable fingerprint authentication": "指紋認証を有効に"
|
||||
},
|
||||
@@ -644,9 +659,18 @@
|
||||
"Failed to activate configuration": {
|
||||
"Failed to activate configuration": "設定が適用できませんでした"
|
||||
},
|
||||
"Failed to connect VPN": {
|
||||
"Failed to connect VPN": ""
|
||||
},
|
||||
"Failed to connect to ": {
|
||||
"Failed to connect to ": "接続ができませんでした "
|
||||
},
|
||||
"Failed to disconnect VPN": {
|
||||
"Failed to disconnect VPN": ""
|
||||
},
|
||||
"Failed to disconnect VPNs": {
|
||||
"Failed to disconnect VPNs": ""
|
||||
},
|
||||
"Failed to disconnect WiFi": {
|
||||
"Failed to disconnect WiFi": "WiFiの切断ができませんでした"
|
||||
},
|
||||
@@ -654,7 +678,7 @@
|
||||
"Failed to enable WiFi": "WiFiを有効化にできませんでした"
|
||||
},
|
||||
"Failed to remove device": {
|
||||
"Failed to remove device": ""
|
||||
"Failed to remove device": "デバイスの削除に失敗しました"
|
||||
},
|
||||
"Failed to set profile image": {
|
||||
"Failed to set profile image": "プロフィール画像の設定に失敗しました"
|
||||
@@ -1209,7 +1233,7 @@
|
||||
"Plugins": "プラグイン"
|
||||
},
|
||||
"Plugins:": {
|
||||
"Plugins:": ""
|
||||
"Plugins:": "プラグイン:"
|
||||
},
|
||||
"Popup Position": {
|
||||
"Popup Position": "ポップアップの位置"
|
||||
@@ -1299,7 +1323,7 @@
|
||||
"Reset": "リセット"
|
||||
},
|
||||
"Resources": {
|
||||
"Resources": ""
|
||||
"Resources": "リソース"
|
||||
},
|
||||
"Right": {
|
||||
"Right": "右"
|
||||
@@ -1497,7 +1521,7 @@
|
||||
"Storage & Disks": "ストレージとディスク"
|
||||
},
|
||||
"Support Development": {
|
||||
"Support Development": ""
|
||||
"Support Development": "開発をサポート"
|
||||
},
|
||||
"Surface": {
|
||||
"Surface": "表面"
|
||||
@@ -1758,7 +1782,7 @@
|
||||
"Weather Widget": "天気ウィジェット"
|
||||
},
|
||||
"Website:": {
|
||||
"Website:": ""
|
||||
"Website:": "ウェブサイト:"
|
||||
},
|
||||
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": {
|
||||
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": "有効にすると、アプリはアルファベット順に並べ替えられます。無効にすると、アプリは使用頻度で並べ替えられます。"
|
||||
|
||||
@@ -227,6 +227,15 @@
|
||||
"Bluetooth Settings": {
|
||||
"Bluetooth Settings": "Configurações do Bluetooth"
|
||||
},
|
||||
"Blur Layer": {
|
||||
"Blur Layer": ""
|
||||
},
|
||||
"Blur on Overview": {
|
||||
"Blur on Overview": ""
|
||||
},
|
||||
"Blur wallpaper when niri overview is open": {
|
||||
"Blur wallpaper when niri overview is open": ""
|
||||
},
|
||||
"Border": {
|
||||
"Border": "Borda"
|
||||
},
|
||||
@@ -380,6 +389,9 @@
|
||||
"Connect": {
|
||||
"Connect": "Conectar"
|
||||
},
|
||||
"Connect to VPN": {
|
||||
"Connect to VPN": ""
|
||||
},
|
||||
"Connect to Wi-Fi": {
|
||||
"Connect to Wi-Fi": "Conectar ao Wi-Fi"
|
||||
},
|
||||
@@ -596,6 +608,9 @@
|
||||
"Enable WiFi": {
|
||||
"Enable WiFi": "Ativar Wi-fi "
|
||||
},
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": {
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": ""
|
||||
},
|
||||
"Enable fingerprint authentication": {
|
||||
"Enable fingerprint authentication": "Habilitar autenticação por impressão digital"
|
||||
},
|
||||
@@ -644,9 +659,18 @@
|
||||
"Failed to activate configuration": {
|
||||
"Failed to activate configuration": ""
|
||||
},
|
||||
"Failed to connect VPN": {
|
||||
"Failed to connect VPN": ""
|
||||
},
|
||||
"Failed to connect to ": {
|
||||
"Failed to connect to ": ""
|
||||
},
|
||||
"Failed to disconnect VPN": {
|
||||
"Failed to disconnect VPN": ""
|
||||
},
|
||||
"Failed to disconnect VPNs": {
|
||||
"Failed to disconnect VPNs": ""
|
||||
},
|
||||
"Failed to disconnect WiFi": {
|
||||
"Failed to disconnect WiFi": ""
|
||||
},
|
||||
|
||||
1895
translations/poexports/tr.json
Normal file
1895
translations/poexports/tr.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -227,6 +227,15 @@
|
||||
"Bluetooth Settings": {
|
||||
"Bluetooth Settings": "蓝牙设置"
|
||||
},
|
||||
"Blur Layer": {
|
||||
"Blur Layer": "模糊层"
|
||||
},
|
||||
"Blur on Overview": {
|
||||
"Blur on Overview": "在概览中模糊"
|
||||
},
|
||||
"Blur wallpaper when niri overview is open": {
|
||||
"Blur wallpaper when niri overview is open": "当打开概览时模糊壁纸"
|
||||
},
|
||||
"Border": {
|
||||
"Border": "边框"
|
||||
},
|
||||
@@ -380,6 +389,9 @@
|
||||
"Connect": {
|
||||
"Connect": "连接"
|
||||
},
|
||||
"Connect to VPN": {
|
||||
"Connect to VPN": "连接到 VPN"
|
||||
},
|
||||
"Connect to Wi-Fi": {
|
||||
"Connect to Wi-Fi": "连接到Wi-Fi"
|
||||
},
|
||||
@@ -564,7 +576,7 @@
|
||||
"Domain (optional)": "域(可选)"
|
||||
},
|
||||
"Donate on Ko-fi": {
|
||||
"Donate on Ko-fi": ""
|
||||
"Donate on Ko-fi": "在Ko-fi上赞助"
|
||||
},
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": {
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "拖动组件以在各区块内重新排序。点击眼睛图标可隐藏/显示组件(保留间距),点击 X 可彻底移除。"
|
||||
@@ -596,6 +608,9 @@
|
||||
"Enable WiFi": {
|
||||
"Enable WiFi": "启用 Wi-Fi"
|
||||
},
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": {
|
||||
"Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.": "启用合成器可定位的模糊层(命名空间:dms:blurwallpaper),需要手动配置 niri。"
|
||||
},
|
||||
"Enable fingerprint authentication": {
|
||||
"Enable fingerprint authentication": "启用指纹认证"
|
||||
},
|
||||
@@ -644,9 +659,18 @@
|
||||
"Failed to activate configuration": {
|
||||
"Failed to activate configuration": "无法应用配置"
|
||||
},
|
||||
"Failed to connect VPN": {
|
||||
"Failed to connect VPN": "连接 VPN 失败"
|
||||
},
|
||||
"Failed to connect to ": {
|
||||
"Failed to connect to ": "无法连接至 "
|
||||
},
|
||||
"Failed to disconnect VPN": {
|
||||
"Failed to disconnect VPN": "断开 VPN 失败"
|
||||
},
|
||||
"Failed to disconnect VPNs": {
|
||||
"Failed to disconnect VPNs": "断开 VPNs 失败"
|
||||
},
|
||||
"Failed to disconnect WiFi": {
|
||||
"Failed to disconnect WiFi": "无法断开 Wi-Fi 连接"
|
||||
},
|
||||
@@ -654,7 +678,7 @@
|
||||
"Failed to enable WiFi": "无法启用 Wi-Fi"
|
||||
},
|
||||
"Failed to remove device": {
|
||||
"Failed to remove device": ""
|
||||
"Failed to remove device": "删除设备失败"
|
||||
},
|
||||
"Failed to set profile image": {
|
||||
"Failed to set profile image": "无法设置个人资料图片"
|
||||
@@ -1209,7 +1233,7 @@
|
||||
"Plugins": "插件"
|
||||
},
|
||||
"Plugins:": {
|
||||
"Plugins:": ""
|
||||
"Plugins:": "插件:"
|
||||
},
|
||||
"Popup Position": {
|
||||
"Popup Position": "弹出位置"
|
||||
@@ -1299,7 +1323,7 @@
|
||||
"Reset": "重置"
|
||||
},
|
||||
"Resources": {
|
||||
"Resources": ""
|
||||
"Resources": "资源"
|
||||
},
|
||||
"Right": {
|
||||
"Right": "右侧"
|
||||
@@ -1497,7 +1521,7 @@
|
||||
"Storage & Disks": "存储与磁盘"
|
||||
},
|
||||
"Support Development": {
|
||||
"Support Development": ""
|
||||
"Support Development": "支持开发"
|
||||
},
|
||||
"Surface": {
|
||||
"Surface": "表面色"
|
||||
@@ -1758,7 +1782,7 @@
|
||||
"Weather Widget": "天气小组件"
|
||||
},
|
||||
"Website:": {
|
||||
"Website:": ""
|
||||
"Website:": "网站:"
|
||||
},
|
||||
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": {
|
||||
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": "启用后,应用按字母顺序排序;禁用则按使用频率排序"
|
||||
|
||||
@@ -524,6 +524,27 @@
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Blur Layer",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Blur on Overview",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Blur wallpaper when niri overview is open",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Border",
|
||||
"translation": "",
|
||||
@@ -874,6 +895,13 @@
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Connect to VPN",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Connect to Wi-Fi",
|
||||
"translation": "",
|
||||
@@ -888,13 +916,6 @@
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Connection failed. Check password and try again.",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Contrast",
|
||||
"translation": "",
|
||||
@@ -1371,6 +1392,13 @@
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Enable fingerprint authentication",
|
||||
"translation": "",
|
||||
@@ -1483,6 +1511,13 @@
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to connect VPN",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to connect to ",
|
||||
"translation": "",
|
||||
@@ -1490,6 +1525,20 @@
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to disconnect VPN",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to disconnect VPNs",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Failed to disconnect WiFi",
|
||||
"translation": "",
|
||||
|
||||
Reference in New Issue
Block a user