mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
feat: Wallpapers built into the Media Hub
- Thanks @TaylanTatli for the inspiration
This commit is contained in:
82
.github/workflows/poeditor-export.yml
vendored
82
.github/workflows/poeditor-export.yml
vendored
@@ -17,91 +17,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
|
|
||||||
- name: Install jq
|
- name: Install jq
|
||||||
run: sudo apt-get update && sudo apt-get install -y jq
|
run: sudo apt-get update && sudo apt-get install -y jq
|
||||||
|
|
||||||
- name: Extract source strings from codebase
|
|
||||||
env:
|
|
||||||
API_TOKEN: ${{ secrets.POEDITOR_API_TOKEN }}
|
|
||||||
PROJECT_ID: ${{ secrets.POEDITOR_PROJECT_ID }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
echo "::group::Extracting strings from QML files"
|
|
||||||
python3 translations/extract_translations.py
|
|
||||||
echo "::endgroup::"
|
|
||||||
|
|
||||||
echo "::group::Checking for changes in en.json"
|
|
||||||
if [[ -f "translations/en.json" ]]; then
|
|
||||||
jq -S . "translations/en.json" > /tmp/en_new.json
|
|
||||||
if [[ -f "translations/en.json.orig" ]]; then
|
|
||||||
jq -S . "translations/en.json.orig" > /tmp/en_old.json
|
|
||||||
else
|
|
||||||
git show HEAD:translations/en.json > /tmp/en_old.json 2>/dev/null || echo "[]" > /tmp/en_old.json
|
|
||||||
jq -S . /tmp/en_old.json > /tmp/en_old.json.tmp && mv /tmp/en_old.json.tmp /tmp/en_old.json
|
|
||||||
fi
|
|
||||||
|
|
||||||
if diff -q /tmp/en_new.json /tmp/en_old.json >/dev/null 2>&1; then
|
|
||||||
echo "No changes in source strings"
|
|
||||||
echo "source_changed=false" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "Detected changes in source strings"
|
|
||||||
echo "source_changed=true" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
echo "::group::Uploading source strings to POEditor"
|
|
||||||
RESP=$(curl -sS -X POST https://api.poeditor.com/v2/projects/upload \
|
|
||||||
-F api_token="$API_TOKEN" \
|
|
||||||
-F id="$PROJECT_ID" \
|
|
||||||
-F updating="terms" \
|
|
||||||
-F file=@"translations/en.json")
|
|
||||||
|
|
||||||
STATUS=$(echo "$RESP" | jq -r '.response.status')
|
|
||||||
if [[ "$STATUS" != "success" ]]; then
|
|
||||||
echo "::warning::POEditor upload failed: $RESP"
|
|
||||||
else
|
|
||||||
TERMS_ADDED=$(echo "$RESP" | jq -r '.result.terms.added // 0')
|
|
||||||
TERMS_UPDATED=$(echo "$RESP" | jq -r '.result.terms.updated // 0')
|
|
||||||
TERMS_DELETED=$(echo "$RESP" | jq -r '.result.terms.deleted // 0')
|
|
||||||
echo "Terms added: $TERMS_ADDED, updated: $TERMS_UPDATED, deleted: $TERMS_DELETED"
|
|
||||||
fi
|
|
||||||
echo "::endgroup::"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "::warning::translations/en.json not found"
|
|
||||||
echo "source_changed=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
echo "::endgroup::"
|
|
||||||
id: extract
|
|
||||||
|
|
||||||
- name: Commit and push source strings
|
|
||||||
if: steps.extract.outputs.source_changed == 'true'
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
|
|
||||||
git add translations/en.json translations/template.json
|
|
||||||
git commit -m "i18n: update source strings from codebase"
|
|
||||||
|
|
||||||
for attempt in 1 2 3; do
|
|
||||||
if git push; then
|
|
||||||
echo "Successfully pushed source string updates"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Push attempt $attempt failed, pulling and retrying..."
|
|
||||||
git pull --rebase
|
|
||||||
sleep $((attempt*2))
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Failed to push after retries" >&2
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Export and update translations from POEditor
|
- name: Export and update translations from POEditor
|
||||||
env:
|
env:
|
||||||
API_TOKEN: ${{ secrets.POEDITOR_API_TOKEN }}
|
API_TOKEN: ${{ secrets.POEDITOR_API_TOKEN }}
|
||||||
|
|||||||
@@ -207,9 +207,6 @@ Singleton {
|
|||||||
property bool dankBarAutoHide: false
|
property bool dankBarAutoHide: false
|
||||||
property bool dankBarOpenOnOverview: false
|
property bool dankBarOpenOnOverview: false
|
||||||
property bool dankBarVisible: true
|
property bool dankBarVisible: true
|
||||||
property int overviewRows: 2
|
|
||||||
property int overviewColumns: 5
|
|
||||||
property real overviewScale: 0.16
|
|
||||||
property real dankBarSpacing: 4
|
property real dankBarSpacing: 4
|
||||||
property real dankBarBottomGap: 0
|
property real dankBarBottomGap: 0
|
||||||
property real dankBarInnerPadding: 4
|
property real dankBarInnerPadding: 4
|
||||||
|
|||||||
@@ -441,10 +441,7 @@ Singleton {
|
|||||||
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode)
|
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode)
|
||||||
SessionData.setLightMode(light)
|
SessionData.setLightMode(light)
|
||||||
if (!isGreeterMode) {
|
if (!isGreeterMode) {
|
||||||
// Skip with matugen becuase, our script runner will do it.
|
PortalService.setLightMode(light)
|
||||||
if (!matugenAvailable) {
|
|
||||||
PortalService.setLightMode(light)
|
|
||||||
}
|
|
||||||
generateSystemThemesFromCurrentTheme()
|
generateSystemThemesFromCurrentTheme()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -722,16 +719,15 @@ Singleton {
|
|||||||
|
|
||||||
Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`])
|
Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`])
|
||||||
workerRunning = true
|
workerRunning = true
|
||||||
const syncModeWithPortal = (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal) ? "true" : "false"
|
|
||||||
if (rawWallpaperPath.startsWith("we:")) {
|
if (rawWallpaperPath.startsWith("we:")) {
|
||||||
console.log("Theme: Starting matugen worker (WE wallpaper)")
|
console.log("Theme: Starting matugen worker (WE wallpaper)")
|
||||||
systemThemeGenerator.command = [
|
systemThemeGenerator.command = [
|
||||||
"sh", "-c",
|
"sh", "-c",
|
||||||
`sleep 1 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' '${configDir}' '${syncModeWithPortal}' --run`
|
`sleep 1 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' '${configDir}' --run`
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
console.log("Theme: Starting matugen worker")
|
console.log("Theme: Starting matugen worker")
|
||||||
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, configDir, syncModeWithPortal, "--run"]
|
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, configDir, "--run"]
|
||||||
}
|
}
|
||||||
systemThemeGenerator.running = true
|
systemThemeGenerator.running = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,29 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
import Quickshell.Services.Greetd
|
import Quickshell.Services.Greetd
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modules.Greetd
|
import qs.Modules.Greetd
|
||||||
|
|
||||||
Scope {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
GreeterSurface {}
|
WlSessionLock {
|
||||||
|
id: sessionLock
|
||||||
|
locked: false
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
Qt.callLater(() => { locked = true })
|
||||||
|
}
|
||||||
|
|
||||||
|
onLockedChanged: {
|
||||||
|
if (!locked) {
|
||||||
|
console.log("Greetd session unlocked, exiting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GreeterSurface {
|
||||||
|
lock: sessionLock
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
DMSShell.qml
31
DMSShell.qml
@@ -22,7 +22,6 @@ import qs.Modules.ProcessList
|
|||||||
import qs.Modules.Settings
|
import qs.Modules.Settings
|
||||||
import qs.Modules.DankBar
|
import qs.Modules.DankBar
|
||||||
import qs.Modules.DankBar.Popouts
|
import qs.Modules.DankBar.Popouts
|
||||||
import qs.Modules.HyprWorkspaces
|
|
||||||
import qs.Modules.Plugins
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -64,11 +63,8 @@ Item {
|
|||||||
|
|
||||||
property var currentPosition: SettingsData.dankBarPosition
|
property var currentPosition: SettingsData.dankBarPosition
|
||||||
property bool initialized: false
|
property bool initialized: false
|
||||||
property var hyprlandOverviewLoaderRef: hyprlandOverviewLoader
|
|
||||||
|
|
||||||
sourceComponent: DankBar {
|
sourceComponent: DankBar {
|
||||||
hyprlandOverviewLoader: dankBarLoader.hyprlandOverviewLoaderRef
|
|
||||||
|
|
||||||
onColorPickerRequested: {
|
onColorPickerRequested: {
|
||||||
if (colorPickerModal.shouldBeVisible) {
|
if (colorPickerModal.shouldBeVisible) {
|
||||||
colorPickerModal.close()
|
colorPickerModal.close()
|
||||||
@@ -197,19 +193,17 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WifiPasswordModal {
|
LazyLoader {
|
||||||
id: wifiPasswordModal
|
id: wifiPasswordModalLoader
|
||||||
|
|
||||||
Component.onCompleted: {
|
active: false
|
||||||
PopoutService.wifiPasswordModal = wifiPasswordModal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
WifiPasswordModal {
|
||||||
target: NetworkService
|
id: wifiPasswordModal
|
||||||
|
|
||||||
function onCredentialsNeeded(token, ssid, setting, fields, hints, reason) {
|
Component.onCompleted: {
|
||||||
wifiPasswordModal.showFromPrompt(token, ssid, setting, fields, hints, reason)
|
PopoutService.wifiPasswordModal = wifiPasswordModal
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,7 +496,6 @@ Item {
|
|||||||
notepadSlideoutVariants: notepadSlideoutVariants
|
notepadSlideoutVariants: notepadSlideoutVariants
|
||||||
hyprKeybindsModalLoader: hyprKeybindsModalLoader
|
hyprKeybindsModalLoader: hyprKeybindsModalLoader
|
||||||
dankBarLoader: dankBarLoader
|
dankBarLoader: dankBarLoader
|
||||||
hyprlandOverviewLoader: hyprlandOverviewLoader
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Variants {
|
Variants {
|
||||||
@@ -545,12 +538,4 @@ Item {
|
|||||||
modelData: item
|
modelData: item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyLoader {
|
|
||||||
id: hyprlandOverviewLoader
|
|
||||||
active: CompositorService.isHyprland
|
|
||||||
component: HyprlandOverview {
|
|
||||||
id: hyprlandOverview
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ Item {
|
|||||||
required property var notepadSlideoutVariants
|
required property var notepadSlideoutVariants
|
||||||
required property var hyprKeybindsModalLoader
|
required property var hyprKeybindsModalLoader
|
||||||
required property var dankBarLoader
|
required property var dankBarLoader
|
||||||
required property var hyprlandOverviewLoader
|
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open() {
|
function open() {
|
||||||
@@ -348,30 +347,6 @@ Item {
|
|||||||
return "HYPR_KEYBINDS_TOGGLE_FAILED"
|
return "HYPR_KEYBINDS_TOGGLE_FAILED"
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleOverview(): string {
|
|
||||||
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
|
||||||
return "HYPR_NOT_AVAILABLE"
|
|
||||||
}
|
|
||||||
root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen
|
|
||||||
return root.hyprlandOverviewLoader.item.overviewOpen ? "OVERVIEW_OPEN_SUCCESS" : "OVERVIEW_CLOSE_SUCCESS"
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeOverview(): string {
|
|
||||||
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
|
||||||
return "HYPR_NOT_AVAILABLE"
|
|
||||||
}
|
|
||||||
root.hyprlandOverviewLoader.item.overviewOpen = false
|
|
||||||
return "OVERVIEW_CLOSE_SUCCESS"
|
|
||||||
}
|
|
||||||
|
|
||||||
function openOverview(): string {
|
|
||||||
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
|
||||||
return "HYPR_NOT_AVAILABLE"
|
|
||||||
}
|
|
||||||
root.hyprlandOverviewLoader.item.overviewOpen = true
|
|
||||||
return "OVERVIEW_OPEN_SUCCESS"
|
|
||||||
}
|
|
||||||
|
|
||||||
target: "hypr"
|
target: "hypr"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,6 @@ import qs.Widgets
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: resultsContainer
|
id: resultsContainer
|
||||||
|
|
||||||
// DEVELOPER NOTE: This component renders the Spotlight launcher (accessed via Mod+Space).
|
|
||||||
// Changes to launcher behavior, especially item rendering, filtering, or model structure,
|
|
||||||
// likely require corresponding updates in Modules/AppDrawer/AppLauncher.qml and vice versa.
|
|
||||||
|
|
||||||
property var appLauncher: null
|
property var appLauncher: null
|
||||||
property var contextMenu: null
|
property var contextMenu: null
|
||||||
|
|
||||||
@@ -94,32 +90,19 @@ Rectangle {
|
|||||||
width: resultsList.iconSize
|
width: resultsList.iconSize
|
||||||
height: resultsList.iconSize
|
height: resultsList.iconSize
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: model.icon !== undefined && model.icon !== ""
|
|
||||||
|
|
||||||
property string iconValue: model.icon || ""
|
|
||||||
property bool isMaterial: iconValue.indexOf("material:") === 0
|
|
||||||
property string materialName: isMaterial ? iconValue.substring(9) : ""
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: parent.materialName
|
|
||||||
size: resultsList.iconSize
|
|
||||||
color: Theme.surfaceText
|
|
||||||
visible: parent.isMaterial
|
|
||||||
}
|
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
id: listIconImg
|
id: listIconImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
source: Quickshell.iconPath(model.icon, true)
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: !parent.isMaterial && status === Image.Ready
|
visible: status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !parent.isMaterial && !listIconImg.visible
|
visible: !listIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 1
|
||||||
@@ -137,7 +120,7 @@ Rectangle {
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: (model.icon !== undefined && model.icon !== "") ? (parent.width - resultsList.iconSize - Theme.spacingL) : parent.width
|
width: parent.width - resultsList.iconSize - Theme.spacingL
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -272,33 +255,20 @@ Rectangle {
|
|||||||
width: iconSize
|
width: iconSize
|
||||||
height: iconSize
|
height: iconSize
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
visible: model.icon !== undefined && model.icon !== ""
|
|
||||||
|
|
||||||
property string iconValue: model.icon || ""
|
|
||||||
property bool isMaterial: iconValue.indexOf("material:") === 0
|
|
||||||
property string materialName: isMaterial ? iconValue.substring(9) : ""
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: parent.materialName
|
|
||||||
size: parent.iconSize
|
|
||||||
color: Theme.surfaceText
|
|
||||||
visible: parent.isMaterial
|
|
||||||
}
|
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
id: gridIconImg
|
id: gridIconImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
source: Quickshell.iconPath(model.icon, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: !parent.isMaterial && status === Image.Ready
|
visible: status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !parent.isMaterial && !gridIconImg.visible
|
visible: !gridIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|||||||
@@ -15,23 +15,12 @@ DankModal {
|
|||||||
property string wifiAnonymousIdentityInput: ""
|
property string wifiAnonymousIdentityInput: ""
|
||||||
property string wifiDomainInput: ""
|
property string wifiDomainInput: ""
|
||||||
|
|
||||||
property bool isPromptMode: false
|
|
||||||
property string promptToken: ""
|
|
||||||
property string promptReason: ""
|
|
||||||
property var promptFields: []
|
|
||||||
property string promptSetting: ""
|
|
||||||
|
|
||||||
function show(ssid) {
|
function show(ssid) {
|
||||||
wifiPasswordSSID = ssid
|
wifiPasswordSSID = ssid
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
wifiAnonymousIdentityInput = ""
|
wifiAnonymousIdentityInput = ""
|
||||||
wifiDomainInput = ""
|
wifiDomainInput = ""
|
||||||
isPromptMode = false
|
|
||||||
promptToken = ""
|
|
||||||
promptReason = ""
|
|
||||||
promptFields = []
|
|
||||||
promptSetting = ""
|
|
||||||
|
|
||||||
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid)
|
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid)
|
||||||
requiresEnterprise = network?.enterprise || false
|
requiresEnterprise = network?.enterprise || false
|
||||||
@@ -48,41 +37,6 @@ DankModal {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function showFromPrompt(token, ssid, setting, fields, hints, reason) {
|
|
||||||
wifiPasswordSSID = ssid
|
|
||||||
isPromptMode = true
|
|
||||||
promptToken = token
|
|
||||||
promptReason = reason
|
|
||||||
promptFields = fields || []
|
|
||||||
promptSetting = setting || "802-11-wireless-security"
|
|
||||||
|
|
||||||
requiresEnterprise = setting === "802-1x"
|
|
||||||
|
|
||||||
if (reason === "wrong-password") {
|
|
||||||
wifiPasswordInput = ""
|
|
||||||
wifiUsernameInput = ""
|
|
||||||
} else {
|
|
||||||
wifiPasswordInput = ""
|
|
||||||
wifiUsernameInput = ""
|
|
||||||
wifiAnonymousIdentityInput = ""
|
|
||||||
wifiDomainInput = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
open()
|
|
||||||
Qt.callLater(() => {
|
|
||||||
if (contentLoader.item) {
|
|
||||||
if (reason === "wrong-password" && contentLoader.item.passwordInput) {
|
|
||||||
contentLoader.item.passwordInput.text = ""
|
|
||||||
contentLoader.item.passwordInput.forceActiveFocus()
|
|
||||||
} else if (requiresEnterprise && contentLoader.item.usernameInput) {
|
|
||||||
contentLoader.item.usernameInput.forceActiveFocus()
|
|
||||||
} else if (contentLoader.item.passwordInput) {
|
|
||||||
contentLoader.item.passwordInput.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
width: 420
|
width: 420
|
||||||
height: requiresEnterprise ? 430 : 230
|
height: requiresEnterprise ? 430 : 230
|
||||||
@@ -106,9 +60,6 @@ DankModal {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
if (isPromptMode) {
|
|
||||||
NetworkService.cancelCredentials(promptToken)
|
|
||||||
}
|
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
@@ -139,9 +90,6 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
if (isPromptMode) {
|
|
||||||
NetworkService.cancelCredentials(promptToken)
|
|
||||||
}
|
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
@@ -169,28 +117,12 @@ DankModal {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
StyledText {
|
||||||
|
text: requiresEnterprise ? I18n.tr("Enter credentials for ") + wifiPasswordSSID : I18n.tr("Enter password for ") + wifiPasswordSSID
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingXS
|
elide: Text.ElideRight
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
const prefix = requiresEnterprise ? I18n.tr("Enter credentials for ") : I18n.tr("Enter password for ")
|
|
||||||
return prefix + wifiPasswordSSID
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceTextMedium
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
visible: isPromptMode && promptReason === "wrong-password"
|
|
||||||
text: I18n.tr("Incorrect password")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.error
|
|
||||||
width: parent.width
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,9 +131,6 @@ DankModal {
|
|||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 4
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (isPromptMode) {
|
|
||||||
NetworkService.cancelCredentials(promptToken)
|
|
||||||
}
|
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
@@ -279,26 +208,14 @@ DankModal {
|
|||||||
wifiPasswordInput = text
|
wifiPasswordInput = text
|
||||||
}
|
}
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
if (isPromptMode) {
|
const username = requiresEnterprise ? usernameInput.text : ""
|
||||||
const secrets = {}
|
NetworkService.connectToWifi(
|
||||||
if (promptSetting === "802-11-wireless-security") {
|
wifiPasswordSSID,
|
||||||
secrets["psk"] = passwordInput.text
|
passwordInput.text,
|
||||||
} else if (promptSetting === "802-1x") {
|
username,
|
||||||
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
wifiAnonymousIdentityInput,
|
||||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
wifiDomainInput
|
||||||
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput
|
)
|
||||||
}
|
|
||||||
NetworkService.submitCredentials(promptToken, secrets, true)
|
|
||||||
} else {
|
|
||||||
const username = requiresEnterprise ? usernameInput.text : ""
|
|
||||||
NetworkService.connectToWifi(
|
|
||||||
wifiPasswordSSID,
|
|
||||||
passwordInput.text,
|
|
||||||
username,
|
|
||||||
wifiAnonymousIdentityInput,
|
|
||||||
wifiDomainInput
|
|
||||||
)
|
|
||||||
}
|
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
@@ -478,9 +395,6 @@ DankModal {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (isPromptMode) {
|
|
||||||
NetworkService.cancelCredentials(promptToken)
|
|
||||||
}
|
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
@@ -516,26 +430,14 @@ DankModal {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (isPromptMode) {
|
const username = requiresEnterprise ? usernameInput.text : ""
|
||||||
const secrets = {}
|
NetworkService.connectToWifi(
|
||||||
if (promptSetting === "802-11-wireless-security") {
|
wifiPasswordSSID,
|
||||||
secrets["psk"] = passwordInput.text
|
passwordInput.text,
|
||||||
} else if (promptSetting === "802-1x") {
|
username,
|
||||||
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
wifiAnonymousIdentityInput,
|
||||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
wifiDomainInput
|
||||||
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput
|
)
|
||||||
}
|
|
||||||
NetworkService.submitCredentials(promptToken, secrets, true)
|
|
||||||
} else {
|
|
||||||
const username = requiresEnterprise ? usernameInput.text : ""
|
|
||||||
NetworkService.connectToWifi(
|
|
||||||
wifiPasswordSSID,
|
|
||||||
passwordInput.text,
|
|
||||||
username,
|
|
||||||
wifiAnonymousIdentityInput,
|
|
||||||
wifiDomainInput
|
|
||||||
)
|
|
||||||
}
|
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
|
|||||||
@@ -404,29 +404,16 @@ DankPopout {
|
|||||||
width: appList.iconSize
|
width: appList.iconSize
|
||||||
height: appList.iconSize
|
height: appList.iconSize
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: model.icon !== undefined && model.icon !== ""
|
|
||||||
|
|
||||||
property string iconValue: model.icon || ""
|
|
||||||
property bool isMaterial: iconValue.indexOf("material:") === 0
|
|
||||||
property string materialName: isMaterial ? iconValue.substring(9) : ""
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: parent.materialName
|
|
||||||
size: appList.iconSize - Theme.spacingM
|
|
||||||
color: Theme.surfaceText
|
|
||||||
visible: parent.isMaterial
|
|
||||||
}
|
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
id: listIconImg
|
id: listIconImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingXS
|
anchors.margins: Theme.spacingXS
|
||||||
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
source: Quickshell.iconPath(model.icon, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: !parent.isMaterial && status === Image.Ready
|
visible: status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -434,7 +421,7 @@ DankPopout {
|
|||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.bottomMargin: Theme.spacingM
|
anchors.bottomMargin: Theme.spacingM
|
||||||
visible: !parent.isMaterial && listIconImg.status !== Image.Ready
|
visible: !listIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 0
|
border.width: 0
|
||||||
@@ -448,12 +435,11 @@ DankPopout {
|
|||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: (model.icon !== undefined && model.icon !== "") ? (parent.width - appList.iconSize - Theme.spacingL) : parent.width
|
width: parent.width - appList.iconSize - Theme.spacingL
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -527,7 +513,6 @@ DankPopout {
|
|||||||
property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns
|
property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns
|
||||||
property int baseCellHeight: baseCellWidth + 20
|
property int baseCellHeight: baseCellWidth + 20
|
||||||
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
|
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
|
||||||
|
|
||||||
property int remainingSpace: width - (actualColumns * cellWidth)
|
property int remainingSpace: width - (actualColumns * cellWidth)
|
||||||
|
|
||||||
signal keyboardNavigationReset
|
signal keyboardNavigationReset
|
||||||
@@ -593,19 +578,6 @@ DankPopout {
|
|||||||
width: iconSize
|
width: iconSize
|
||||||
height: iconSize
|
height: iconSize
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
visible: model.icon !== undefined && model.icon !== ""
|
|
||||||
|
|
||||||
property string iconValue: model.icon || ""
|
|
||||||
property bool isMaterial: iconValue.indexOf("material:") === 0
|
|
||||||
property string materialName: isMaterial ? iconValue.substring(9) : ""
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: parent.materialName
|
|
||||||
size: parent.iconSize - Theme.spacingL
|
|
||||||
color: Theme.surfaceText
|
|
||||||
visible: parent.isMaterial
|
|
||||||
}
|
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
id: gridIconImg
|
id: gridIconImg
|
||||||
@@ -614,10 +586,10 @@ DankPopout {
|
|||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.bottomMargin: Theme.spacingS
|
anchors.bottomMargin: Theme.spacingS
|
||||||
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
source: Quickshell.iconPath(model.icon, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: !parent.isMaterial && status === Image.Ready
|
visible: status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -625,7 +597,7 @@ DankPopout {
|
|||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.bottomMargin: Theme.spacingS
|
anchors.bottomMargin: Theme.spacingS
|
||||||
visible: !parent.isMaterial && gridIconImg.status !== Image.Ready
|
visible: !gridIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 0
|
border.width: 0
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ import qs.Widgets
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// DEVELOPER NOTE: This component manages the AppDrawer launcher (accessed via DankBar icon).
|
|
||||||
// Changes to launcher behavior, especially item rendering, filtering, or model structure,
|
|
||||||
// likely require corresponding updates in Modals/Spotlight/SpotlightResults.qml and vice versa.
|
|
||||||
|
|
||||||
property string searchQuery: ""
|
property string searchQuery: ""
|
||||||
property string selectedCategory: I18n.tr("All")
|
property string selectedCategory: I18n.tr("All")
|
||||||
property string viewMode: "list" // "list" or "grid"
|
property string viewMode: "list" // "list" or "grid"
|
||||||
@@ -167,7 +163,7 @@ Item {
|
|||||||
filteredModel.append({
|
filteredModel.append({
|
||||||
"name": app.name || "",
|
"name": app.name || "",
|
||||||
"exec": app.execString || app.exec || app.action || "",
|
"exec": app.execString || app.exec || app.action || "",
|
||||||
"icon": app.icon !== undefined ? app.icon : (isPluginItem ? "" : "application-x-executable"),
|
"icon": app.icon || "application-x-executable",
|
||||||
"comment": app.comment || "",
|
"comment": app.comment || "",
|
||||||
"categories": app.categories || [],
|
"categories": app.categories || [],
|
||||||
"isPlugin": isPluginItem,
|
"isPlugin": isPluginItem,
|
||||||
|
|||||||
@@ -509,11 +509,7 @@ Rectangle {
|
|||||||
onClicked: function(event) {
|
onClicked: function(event) {
|
||||||
if (modelData.ssid !== NetworkService.currentWifiSSID) {
|
if (modelData.ssid !== NetworkService.currentWifiSSID) {
|
||||||
if (modelData.secured && !modelData.saved) {
|
if (modelData.secured && !modelData.saved) {
|
||||||
if (DMSService.apiVersion >= 7) {
|
wifiPasswordModal.show(modelData.ssid)
|
||||||
NetworkService.connectToWifi(modelData.ssid)
|
|
||||||
} else if (PopoutService.wifiPasswordModal) {
|
|
||||||
PopoutService.wifiPasswordModal.show(modelData.ssid)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
NetworkService.connectToWifi(modelData.ssid)
|
NetworkService.connectToWifi(modelData.ssid)
|
||||||
}
|
}
|
||||||
@@ -567,11 +563,7 @@ Rectangle {
|
|||||||
NetworkService.disconnectWifi()
|
NetworkService.disconnectWifi()
|
||||||
} else {
|
} else {
|
||||||
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
||||||
if (DMSService.apiVersion >= 7) {
|
wifiPasswordModal.show(networkContextMenu.currentSSID)
|
||||||
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
|
||||||
} else if (PopoutService.wifiPasswordModal) {
|
|
||||||
PopoutService.wifiPasswordModal.show(networkContextMenu.currentSSID)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
||||||
}
|
}
|
||||||
@@ -626,6 +618,10 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WifiPasswordModal {
|
||||||
|
id: wifiPasswordModal
|
||||||
|
}
|
||||||
|
|
||||||
NetworkInfoModal {
|
NetworkInfoModal {
|
||||||
id: networkInfoModal
|
id: networkInfoModal
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ Item {
|
|||||||
signal colorPickerRequested
|
signal colorPickerRequested
|
||||||
|
|
||||||
property alias barVariants: barVariants
|
property alias barVariants: barVariants
|
||||||
property var hyprlandOverviewLoader: null
|
|
||||||
|
|
||||||
function triggerControlCenterOnFocusedScreen() {
|
function triggerControlCenterOnFocusedScreen() {
|
||||||
let focusedScreenName = ""
|
let focusedScreenName = ""
|
||||||
@@ -49,6 +48,30 @@ Item {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function triggerWallpaperBrowserOnFocusedScreen() {
|
||||||
|
let focusedScreenName = ""
|
||||||
|
if (CompositorService.isHyprland && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor) {
|
||||||
|
focusedScreenName = Hyprland.focusedWorkspace.monitor.name
|
||||||
|
} else if (CompositorService.isNiri && NiriService.currentOutput) {
|
||||||
|
focusedScreenName = NiriService.currentOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!focusedScreenName && barVariants.instances.length > 0) {
|
||||||
|
const firstBar = barVariants.instances[0]
|
||||||
|
firstBar.triggerWallpaperBrowser()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < barVariants.instances.length; i++) {
|
||||||
|
const barInstance = barVariants.instances[i]
|
||||||
|
if (barInstance.modelData && barInstance.modelData.name === focusedScreenName) {
|
||||||
|
barInstance.triggerWallpaperBrowser()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
Variants {
|
Variants {
|
||||||
id: barVariants
|
id: barVariants
|
||||||
model: SettingsData.getFilteredScreens("dankBar")
|
model: SettingsData.getFilteredScreens("dankBar")
|
||||||
@@ -57,6 +80,7 @@ Item {
|
|||||||
id: barWindow
|
id: barWindow
|
||||||
|
|
||||||
property var controlCenterButtonRef: null
|
property var controlCenterButtonRef: null
|
||||||
|
property var clockButtonRef: null
|
||||||
|
|
||||||
function triggerControlCenter() {
|
function triggerControlCenter() {
|
||||||
controlCenterLoader.active = true
|
controlCenterLoader.active = true
|
||||||
@@ -79,6 +103,27 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function triggerWallpaperBrowser() {
|
||||||
|
dankDashPopoutLoader.active = true
|
||||||
|
if (!dankDashPopoutLoader.item) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clockButtonRef && dankDashPopoutLoader.item.setTriggerPosition) {
|
||||||
|
const globalPos = clockButtonRef.mapToGlobal(0, 0)
|
||||||
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, clockButtonRef.width)
|
||||||
|
const section = clockButtonRef.section || "center"
|
||||||
|
dankDashPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen)
|
||||||
|
} else {
|
||||||
|
dankDashPopoutLoader.item.triggerScreen = barWindow.screen
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dankDashPopoutLoader.item.dashVisible) {
|
||||||
|
dankDashPopoutLoader.item.currentTabIndex = 2
|
||||||
|
}
|
||||||
|
dankDashPopoutLoader.item.dashVisible = !dankDashPopoutLoader.item.dashVisible
|
||||||
|
}
|
||||||
|
|
||||||
readonly property var dBarLayer: {
|
readonly property var dBarLayer: {
|
||||||
switch (Quickshell.env("DMS_DANKBAR_LAYER")) {
|
switch (Quickshell.env("DMS_DANKBAR_LAYER")) {
|
||||||
case "bottom":
|
case "bottom":
|
||||||
@@ -773,7 +818,6 @@ Item {
|
|||||||
section: topBarContent.getWidgetSection(parent)
|
section: topBarContent.getWidgetSection(parent)
|
||||||
popupTarget: appDrawerLoader.item
|
popupTarget: appDrawerLoader.item
|
||||||
parentScreen: barWindow.screen
|
parentScreen: barWindow.screen
|
||||||
hyprlandOverviewLoader: root.hyprlandOverviewLoader
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
appDrawerLoader.active = true
|
appDrawerLoader.active = true
|
||||||
appDrawerLoader.item?.toggle()
|
appDrawerLoader.item?.toggle()
|
||||||
@@ -787,7 +831,6 @@ Item {
|
|||||||
WorkspaceSwitcher {
|
WorkspaceSwitcher {
|
||||||
screenName: barWindow.screenName
|
screenName: barWindow.screenName
|
||||||
widgetHeight: barWindow.widgetThickness
|
widgetHeight: barWindow.widgetThickness
|
||||||
hyprlandOverviewLoader: root.hyprlandOverviewLoader
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,6 +868,17 @@ Item {
|
|||||||
return dankDashPopoutLoader.item
|
return dankDashPopoutLoader.item
|
||||||
}
|
}
|
||||||
parentScreen: barWindow.screen
|
parentScreen: barWindow.screen
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
barWindow.clockButtonRef = this
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
if (barWindow.clockButtonRef === this) {
|
||||||
|
barWindow.clockButtonRef = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onClockClicked: {
|
onClockClicked: {
|
||||||
dankDashPopoutLoader.active = true
|
dankDashPopoutLoader.active = true
|
||||||
if (dankDashPopoutLoader.item) {
|
if (dankDashPopoutLoader.item) {
|
||||||
@@ -874,7 +928,7 @@ Item {
|
|||||||
dankDashPopoutLoader.active = true
|
dankDashPopoutLoader.active = true
|
||||||
if (dankDashPopoutLoader.item) {
|
if (dankDashPopoutLoader.item) {
|
||||||
dankDashPopoutLoader.item.dashVisible = !dankDashPopoutLoader.item.dashVisible
|
dankDashPopoutLoader.item.dashVisible = !dankDashPopoutLoader.item.dashVisible
|
||||||
dankDashPopoutLoader.item.currentTabIndex = 2
|
dankDashPopoutLoader.item.currentTabIndex = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1208,4 +1262,15 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "dankdash"
|
||||||
|
|
||||||
|
function wallpaper(): string {
|
||||||
|
if (root.triggerWallpaperBrowserOnFocusedScreen()) {
|
||||||
|
return "SUCCESS: Toggled wallpaper browser"
|
||||||
|
}
|
||||||
|
return "ERROR: Failed to toggle wallpaper browser"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,20 +74,14 @@ Rectangle {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
||||||
if (!Hyprland.toplevels) return false
|
const activeHyprToplevel = hyprlandToplevels.find(t => t.wayland === activeWindow)
|
||||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
|
||||||
const activeHyprToplevel = hyprlandToplevels.find(t => t?.wayland === activeWindow)
|
|
||||||
|
|
||||||
if (!activeHyprToplevel || !activeHyprToplevel.workspace) {
|
if (!activeHyprToplevel || !activeHyprToplevel.workspace) {
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return activeHyprToplevel.workspace.id === Hyprland.focusedWorkspace.id
|
|
||||||
} catch (e) {
|
|
||||||
console.error("FocusedApp: hasWindowsOnCurrentWorkspace error:", e)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return activeHyprToplevel.workspace.id === Hyprland.focusedWorkspace.id
|
||||||
}
|
}
|
||||||
|
|
||||||
return activeWindow && activeWindow.title
|
return activeWindow && activeWindow.title
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ Item {
|
|||||||
property var parentScreen: null
|
property var parentScreen: null
|
||||||
property real widgetThickness: 30
|
property real widgetThickness: 30
|
||||||
property real barThickness: 48
|
property real barThickness: 48
|
||||||
property var hyprlandOverviewLoader: null
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
@@ -36,8 +35,6 @@ Item {
|
|||||||
if (mouse.button === Qt.RightButton) {
|
if (mouse.button === Qt.RightButton) {
|
||||||
if (CompositorService.isNiri) {
|
if (CompositorService.isNiri) {
|
||||||
NiriService.toggleOverview()
|
NiriService.toggleOverview()
|
||||||
} else if (CompositorService.isHyprland && root.hyprlandOverviewLoader?.item) {
|
|
||||||
root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,28 +30,22 @@ Rectangle {
|
|||||||
if (!SettingsData.runningAppsGroupByApp) {
|
if (!SettingsData.runningAppsGroupByApp) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
try {
|
const appGroups = new Map();
|
||||||
const appGroups = new Map();
|
sortedToplevels.forEach((toplevel, index) => {
|
||||||
sortedToplevels.forEach((toplevel, index) => {
|
const appId = toplevel.appId || "unknown";
|
||||||
if (!toplevel) return;
|
if (!appGroups.has(appId)) {
|
||||||
const appId = toplevel?.appId || "unknown";
|
appGroups.set(appId, {
|
||||||
if (!appGroups.has(appId)) {
|
appId: appId,
|
||||||
appGroups.set(appId, {
|
windows: []
|
||||||
appId: appId,
|
|
||||||
windows: []
|
|
||||||
});
|
|
||||||
}
|
|
||||||
appGroups.get(appId).windows.push({
|
|
||||||
toplevel: toplevel,
|
|
||||||
windowId: index,
|
|
||||||
windowTitle: toplevel?.title || "(Unnamed)"
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
appGroups.get(appId).windows.push({
|
||||||
|
toplevel: toplevel,
|
||||||
|
windowId: index,
|
||||||
|
windowTitle: toplevel.title || "(Unnamed)"
|
||||||
});
|
});
|
||||||
return Array.from(appGroups.values());
|
});
|
||||||
} catch (e) {
|
return Array.from(appGroups.values());
|
||||||
console.error("RunningApps: groupedWindows error:", e);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
readonly property int windowCount: SettingsData.runningAppsGroupByApp ? groupedWindows.length : sortedToplevels.length
|
readonly property int windowCount: SettingsData.runningAppsGroupByApp ? groupedWindows.length : sortedToplevels.length
|
||||||
readonly property int calculatedSize: {
|
readonly property int calculatedSize: {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ Rectangle {
|
|||||||
property string screenName: ""
|
property string screenName: ""
|
||||||
property real widgetHeight: 30
|
property real widgetHeight: 30
|
||||||
property real barThickness: 48
|
property real barThickness: 48
|
||||||
property var hyprlandOverviewLoader: null
|
|
||||||
readonly property var sortedToplevels: {
|
readonly property var sortedToplevels: {
|
||||||
return CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, parentScreen?.name);
|
return CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, parentScreen?.name);
|
||||||
}
|
}
|
||||||
@@ -245,17 +244,11 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.NoButton
|
||||||
|
|
||||||
property real scrollAccumulator: 0
|
property real scrollAccumulator: 0
|
||||||
property real touchpadThreshold: 500
|
property real touchpadThreshold: 500
|
||||||
|
|
||||||
onClicked: mouse => {
|
|
||||||
if (mouse.button === Qt.RightButton && CompositorService.isHyprland && root.hyprlandOverviewLoader?.item) {
|
|
||||||
root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onWheel: wheel => {
|
onWheel: wheel => {
|
||||||
const deltaY = wheel.angleDelta.y
|
const deltaY = wheel.angleDelta.y
|
||||||
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0
|
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ DankPopout {
|
|||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
property int currentTabIndex: 0
|
property int currentTabIndex: 0
|
||||||
|
|
||||||
|
keyboardFocusMode: WlrKeyboardFocus.Exclusive
|
||||||
|
|
||||||
function setTriggerPosition(x, y, width, section, screen) {
|
function setTriggerPosition(x, y, width, section, screen) {
|
||||||
triggerSection = section
|
triggerSection = section
|
||||||
triggerScreen = screen
|
triggerScreen = screen
|
||||||
@@ -43,15 +45,49 @@ DankPopout {
|
|||||||
shouldBeVisible: dashVisible
|
shouldBeVisible: dashVisible
|
||||||
visible: shouldBeVisible
|
visible: shouldBeVisible
|
||||||
|
|
||||||
|
property bool __focusArmed: false
|
||||||
|
property bool __contentReady: false
|
||||||
|
|
||||||
|
function __tryFocusOnce() {
|
||||||
|
if (!__focusArmed) return
|
||||||
|
const win = root.window
|
||||||
|
if (!win || !win.visible) return
|
||||||
|
if (!contentLoader.item) return
|
||||||
|
|
||||||
|
if (win.requestActivate) win.requestActivate()
|
||||||
|
contentLoader.item.forceActiveFocus(Qt.TabFocusReason)
|
||||||
|
|
||||||
|
if (contentLoader.item.activeFocus)
|
||||||
|
__focusArmed = false
|
||||||
|
}
|
||||||
|
|
||||||
onDashVisibleChanged: {
|
onDashVisibleChanged: {
|
||||||
if (dashVisible) {
|
if (dashVisible) {
|
||||||
|
__focusArmed = true
|
||||||
|
__contentReady = !!contentLoader.item
|
||||||
open()
|
open()
|
||||||
|
__tryFocusOnce()
|
||||||
} else {
|
} else {
|
||||||
|
__focusArmed = false
|
||||||
|
__contentReady = false
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: contentLoader
|
||||||
|
function onLoaded() {
|
||||||
|
__contentReady = true
|
||||||
|
if (__focusArmed) __tryFocusOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.window ? root.window : null
|
||||||
|
enabled: !!root.window
|
||||||
|
function onVisibleChanged() { if (__focusArmed) __tryFocusOnce() }
|
||||||
|
}
|
||||||
|
|
||||||
onBackgroundClicked: {
|
onBackgroundClicked: {
|
||||||
dashVisible = false
|
dashVisible = false
|
||||||
}
|
}
|
||||||
@@ -67,18 +103,12 @@ DankPopout {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (root.shouldBeVisible) {
|
if (root.shouldBeVisible) {
|
||||||
forceActiveFocus()
|
mainContainer.forceActiveFocus()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: function(event) {
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
|
||||||
root.dashVisible = false
|
|
||||||
event.accepted = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
target: root
|
||||||
function onShouldBeVisibleChanged() {
|
function onShouldBeVisibleChanged() {
|
||||||
if (root.shouldBeVisible) {
|
if (root.shouldBeVisible) {
|
||||||
Qt.callLater(function() {
|
Qt.callLater(function() {
|
||||||
@@ -86,7 +116,52 @@ DankPopout {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target: root
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: function(event) {
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
root.dashVisible = false
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_Tab && !(event.modifiers & Qt.ShiftModifier)) {
|
||||||
|
let nextIndex = root.currentTabIndex + 1
|
||||||
|
while (nextIndex < tabBar.model.length && tabBar.model[nextIndex] && tabBar.model[nextIndex].isAction) {
|
||||||
|
nextIndex++
|
||||||
|
}
|
||||||
|
if (nextIndex >= tabBar.model.length) {
|
||||||
|
nextIndex = 0
|
||||||
|
}
|
||||||
|
root.currentTabIndex = nextIndex
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) {
|
||||||
|
let prevIndex = root.currentTabIndex - 1
|
||||||
|
while (prevIndex >= 0 && tabBar.model[prevIndex] && tabBar.model[prevIndex].isAction) {
|
||||||
|
prevIndex--
|
||||||
|
}
|
||||||
|
if (prevIndex < 0) {
|
||||||
|
prevIndex = tabBar.model.length - 1
|
||||||
|
while (prevIndex >= 0 && tabBar.model[prevIndex] && tabBar.model[prevIndex].isAction) {
|
||||||
|
prevIndex--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prevIndex >= 0) {
|
||||||
|
root.currentTabIndex = prevIndex
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.currentTabIndex === 2 && wallpaperTab.handleKeyEvent) {
|
||||||
|
if (wallpaperTab.handleKeyEvent(event)) {
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -128,11 +203,23 @@ DankPopout {
|
|||||||
currentIndex: root.currentTabIndex
|
currentIndex: root.currentTabIndex
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
equalWidthTabs: true
|
equalWidthTabs: true
|
||||||
|
enableArrowNavigation: false
|
||||||
|
focus: false
|
||||||
|
activeFocusOnTab: false
|
||||||
|
nextFocusTarget: {
|
||||||
|
const item = pages.currentItem
|
||||||
|
if (!item)
|
||||||
|
return null
|
||||||
|
if (item.focusTarget)
|
||||||
|
return item.focusTarget
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
model: {
|
model: {
|
||||||
let tabs = [
|
let tabs = [
|
||||||
{ icon: "dashboard", text: I18n.tr("Overview") },
|
{ icon: "dashboard", text: I18n.tr("Overview") },
|
||||||
{ icon: "music_note", text: I18n.tr("Media") }
|
{ icon: "music_note", text: I18n.tr("Media") },
|
||||||
|
{ icon: "wallpaper", text: I18n.tr("Wallpapers") }
|
||||||
]
|
]
|
||||||
|
|
||||||
if (SettingsData.weatherEnabled) {
|
if (SettingsData.weatherEnabled) {
|
||||||
@@ -148,7 +235,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onActionTriggered: function(index) {
|
onActionTriggered: function(index) {
|
||||||
let settingsIndex = SettingsData.weatherEnabled ? 3 : 2
|
let settingsIndex = SettingsData.weatherEnabled ? 4 : 3
|
||||||
if (index === settingsIndex) {
|
if (index === settingsIndex) {
|
||||||
dashVisible = false
|
dashVisible = false
|
||||||
settingsModal.show()
|
settingsModal.show()
|
||||||
@@ -168,7 +255,8 @@ DankPopout {
|
|||||||
implicitHeight: {
|
implicitHeight: {
|
||||||
if (currentIndex === 0) return overviewTab.implicitHeight
|
if (currentIndex === 0) return overviewTab.implicitHeight
|
||||||
if (currentIndex === 1) return mediaTab.implicitHeight
|
if (currentIndex === 1) return mediaTab.implicitHeight
|
||||||
if (SettingsData.weatherEnabled && currentIndex === 2) return weatherTab.implicitHeight
|
if (currentIndex === 2) return wallpaperTab.implicitHeight
|
||||||
|
if (SettingsData.weatherEnabled && currentIndex === 3) return weatherTab.implicitHeight
|
||||||
return overviewTab.implicitHeight
|
return overviewTab.implicitHeight
|
||||||
}
|
}
|
||||||
currentIndex: root.currentTabIndex
|
currentIndex: root.currentTabIndex
|
||||||
@@ -178,8 +266,8 @@ DankPopout {
|
|||||||
|
|
||||||
onSwitchToWeatherTab: {
|
onSwitchToWeatherTab: {
|
||||||
if (SettingsData.weatherEnabled) {
|
if (SettingsData.weatherEnabled) {
|
||||||
tabBar.currentIndex = 2
|
tabBar.currentIndex = 3
|
||||||
tabBar.tabClicked(2)
|
tabBar.tabClicked(3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,9 +281,16 @@ DankPopout {
|
|||||||
id: mediaTab
|
id: mediaTab
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WallpaperTab {
|
||||||
|
id: wallpaperTab
|
||||||
|
active: root.currentTabIndex === 2
|
||||||
|
tabBarItem: tabBar
|
||||||
|
keyForwardTarget: mainContainer
|
||||||
|
}
|
||||||
|
|
||||||
WeatherTab {
|
WeatherTab {
|
||||||
id: weatherTab
|
id: weatherTab
|
||||||
visible: SettingsData.weatherEnabled && root.currentTabIndex === 2
|
visible: SettingsData.weatherEnabled && root.currentTabIndex === 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
523
Modules/DankDash/WallpaperTab.qml
Normal file
523
Modules/DankDash/WallpaperTab.qml
Normal file
@@ -0,0 +1,523 @@
|
|||||||
|
import Qt.labs.folderlistmodel
|
||||||
|
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
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
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 bool active: false
|
||||||
|
property Item focusTarget: wallpaperGrid
|
||||||
|
property Item tabBarItem: null
|
||||||
|
property int gridIndex: 0
|
||||||
|
property Item keyForwardTarget: null
|
||||||
|
property int lastPage: 0
|
||||||
|
property bool enableAnimation: false
|
||||||
|
|
||||||
|
signal requestTabChange(int newIndex)
|
||||||
|
|
||||||
|
onCurrentPageChanged: {
|
||||||
|
if (currentPage !== lastPage) {
|
||||||
|
enableAnimation = false
|
||||||
|
lastPage = currentPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible && active) {
|
||||||
|
setInitialSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
loadWallpapers()
|
||||||
|
if (visible && active) {
|
||||||
|
setInitialSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active && visible) {
|
||||||
|
setInitialSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyEvent(event) {
|
||||||
|
const columns = 4
|
||||||
|
const rows = 4
|
||||||
|
const currentRow = Math.floor(gridIndex / columns)
|
||||||
|
const currentCol = gridIndex % columns
|
||||||
|
const visibleCount = wallpaperGrid.model.length
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
gridIndex = 0
|
||||||
|
currentPage++
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
currentPage--
|
||||||
|
gridIndex = Math.min(itemsPerPage - 1, wallpaperList.length - currentPage * itemsPerPage - 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
|
||||||
|
gridIndex = currentCol
|
||||||
|
currentPage++
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
currentPage--
|
||||||
|
const prevPageCount = Math.min(itemsPerPage, wallpaperList.length - currentPage * itemsPerPage)
|
||||||
|
const prevPageRows = Math.ceil(prevPageCount / columns)
|
||||||
|
gridIndex = (prevPageRows - 1) * columns + currentCol
|
||||||
|
gridIndex = Math.min(gridIndex, prevPageCount - 1)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_PageUp && currentPage > 0) {
|
||||||
|
gridIndex = 0
|
||||||
|
currentPage--
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_PageDown && currentPage < totalPages - 1) {
|
||||||
|
gridIndex = 0
|
||||||
|
currentPage++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_Home && event.modifiers & Qt.ControlModifier) {
|
||||||
|
gridIndex = 0
|
||||||
|
currentPage = 0
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_End && event.modifiers & Qt.ControlModifier) {
|
||||||
|
gridIndex = 0
|
||||||
|
currentPage = totalPages - 1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInitialSelection() {
|
||||||
|
if (!SessionData.wallpaperPath) {
|
||||||
|
gridIndex = 0
|
||||||
|
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
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gridIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
onWallpaperListChanged: {
|
||||||
|
if (visible && active) {
|
||||||
|
setInitialSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadWallpapers() {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/'))
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWallpaperList() {
|
||||||
|
if (!wallpaperFolderModel || wallpaperFolderModel.count === 0) {
|
||||||
|
wallpaperList = []
|
||||||
|
currentPage = 0
|
||||||
|
gridIndex = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build list from FolderListModel
|
||||||
|
const files = []
|
||||||
|
for (let i = 0; i < wallpaperFolderModel.count; i++) {
|
||||||
|
const filePath = wallpaperFolderModel.get(i, "filePath")
|
||||||
|
if (filePath) {
|
||||||
|
// Remove file:// prefix if present
|
||||||
|
const cleanPath = filePath.toString().replace(/^file:\/\//, '')
|
||||||
|
files.push(cleanPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SessionData
|
||||||
|
function onWallpaperPathChanged() {
|
||||||
|
loadWallpapers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FolderListModel {
|
||||||
|
id: wallpaperFolderModel
|
||||||
|
|
||||||
|
showDirsFirst: false
|
||||||
|
showDotAndDotDot: false
|
||||||
|
showHidden: false
|
||||||
|
nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||||
|
showFiles: true
|
||||||
|
showDirs: false
|
||||||
|
sortField: FolderListModel.Name
|
||||||
|
folder: wallpaperDir ? "file://" + wallpaperDir : ""
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === FolderListModel.Ready) {
|
||||||
|
updateWallpaperList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCountChanged: {
|
||||||
|
if (status === FolderListModel.Ready) {
|
||||||
|
updateWallpaperList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: wallpaperBrowserLoader
|
||||||
|
active: false
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: FileBrowserModal {
|
||||||
|
Component.onCompleted: {
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
browserTitle: "Select Wallpaper Directory"
|
||||||
|
browserIcon: "folder_open"
|
||||||
|
browserType: "wallpaper"
|
||||||
|
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
|
||||||
|
CacheData.wallpaperLastPath = dirPath
|
||||||
|
CacheData.saveCache()
|
||||||
|
}
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
onDialogClosed: {
|
||||||
|
Qt.callLater(() => wallpaperBrowserLoader.active = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - 50
|
||||||
|
|
||||||
|
GridView {
|
||||||
|
id: wallpaperGrid
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - Theme.spacingS
|
||||||
|
height: parent.height - Theme.spacingS
|
||||||
|
cellWidth: width / 4
|
||||||
|
cellHeight: height / 4
|
||||||
|
clip: true
|
||||||
|
enabled: root.active
|
||||||
|
interactive: root.active
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
keyNavigationEnabled: false
|
||||||
|
activeFocusOnTab: false
|
||||||
|
highlightFollowsCurrentItem: true
|
||||||
|
highlightMoveDuration: enableAnimation ? Theme.shortDuration : 0
|
||||||
|
focus: false
|
||||||
|
|
||||||
|
highlight: Item {
|
||||||
|
z: 1000
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingXS
|
||||||
|
color: "transparent"
|
||||||
|
border.width: 3
|
||||||
|
border.color: Theme.primary
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model: {
|
||||||
|
const startIndex = currentPage * itemsPerPage
|
||||||
|
const endIndex = Math.min(startIndex + itemsPerPage, wallpaperList.length)
|
||||||
|
return wallpaperList.slice(startIndex, endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
onModelChanged: {
|
||||||
|
const clampedIndex = model.length > 0 ? Math.min(Math.max(0, gridIndex), model.length - 1) : 0
|
||||||
|
if (gridIndex !== clampedIndex) {
|
||||||
|
gridIndex = clampedIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCountChanged: {
|
||||||
|
if (count > 0) {
|
||||||
|
const clampedIndex = Math.min(gridIndex, count - 1)
|
||||||
|
currentIndex = clampedIndex
|
||||||
|
positionViewAtIndex(clampedIndex, GridView.Contain)
|
||||||
|
}
|
||||||
|
enableAnimation = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onGridIndexChanged() {
|
||||||
|
if (enableAnimation && wallpaperGrid.count > 0) {
|
||||||
|
wallpaperGrid.currentIndex = gridIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
width: wallpaperGrid.cellWidth
|
||||||
|
height: wallpaperGrid.cellHeight
|
||||||
|
|
||||||
|
property string wallpaperPath: modelData || ""
|
||||||
|
property bool isSelected: SessionData.wallpaperPath === modelData
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: wallpaperCard
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingXS
|
||||||
|
color: Theme.surfaceContainerHighest
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: isSelected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15) : "transparent"
|
||||||
|
radius: parent.radius
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: thumbnailImage
|
||||||
|
anchors.fill: parent
|
||||||
|
source: modelData ? `file://${modelData}` : ""
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
asynchronous: true
|
||||||
|
cache: true
|
||||||
|
smooth: true
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
maskEnabled: true
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1.0
|
||||||
|
maskSource: ShaderEffectSource {
|
||||||
|
sourceItem: Rectangle {
|
||||||
|
width: thumbnailImage.width
|
||||||
|
height: thumbnailImage.height
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
running: thumbnailImage.status === Image.Loading
|
||||||
|
visible: running
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
cornerRadius: parent.radius
|
||||||
|
stateColor: Theme.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: wallpaperMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
gridIndex = index
|
||||||
|
if (modelData) {
|
||||||
|
SessionData.setWallpaper(modelData)
|
||||||
|
}
|
||||||
|
// Don't steal focus - let mainContainer keep it for keyboard nav
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: wallpaperList.length === 0
|
||||||
|
text: "No wallpapers found\n\nClick the folder icon below to browse"
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: Theme.outline
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
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
|
||||||
|
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: wallpaperList.length > 0 ? `${wallpaperList.length} 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++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: browseButton
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
iconName: "folder_open"
|
||||||
|
iconSize: 20
|
||||||
|
buttonSize: 32
|
||||||
|
opacity: 0.7
|
||||||
|
onClicked: wallpaperBrowserLoader.active = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import Quickshell
|
|||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Services.Greetd
|
import Quickshell.Services.Greetd
|
||||||
import Quickshell.Services.Pam
|
import Quickshell.Services.Pam
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
@@ -15,6 +16,8 @@ import qs.Modules.Lock
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
required property var sessionLock
|
||||||
|
|
||||||
readonly property string xdgDataDirs: Quickshell.env("XDG_DATA_DIRS")
|
readonly property string xdgDataDirs: Quickshell.env("XDG_DATA_DIRS")
|
||||||
property string screenName: ""
|
property string screenName: ""
|
||||||
property string randomFact: ""
|
property string randomFact: ""
|
||||||
@@ -114,6 +117,21 @@ Item {
|
|||||||
onTriggered: updateHyprlandLayout()
|
onTriggered: updateHyprlandLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ! This was for development and testing, just leaving so people can see how I did it.
|
||||||
|
// Timer {
|
||||||
|
// id: autoUnlockTimer
|
||||||
|
// interval: 10000
|
||||||
|
// running: true
|
||||||
|
// onTriggered: {
|
||||||
|
// root.sessionLock.locked = false
|
||||||
|
// GreeterState.unlocking = true
|
||||||
|
// const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex]
|
||||||
|
// if (sessionCmd) {
|
||||||
|
// GreetdMemory.setLastSessionId(sessionCmd.split(" ")[0])
|
||||||
|
// Greetd.launch(sessionCmd.split(" "), [], true)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: GreetdMemory
|
target: GreetdMemory
|
||||||
@@ -655,11 +673,180 @@ Item {
|
|||||||
height: 24
|
height: 24
|
||||||
color: Qt.rgba(255, 255, 255, 0.2)
|
color: Qt.rgba(255, 255, 255, 0.2)
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: {
|
visible: MprisController.activePlayer
|
||||||
const keyboardVisible = (CompositorService.isNiri && NiriService.keyboardLayoutNames.length > 1) ||
|
}
|
||||||
(CompositorService.isHyprland && hyprlandLayoutCount > 1)
|
|
||||||
return keyboardVisible && WeatherService.weather.available
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: MprisController.activePlayer
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 20
|
||||||
|
height: Theme.iconSize
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: MprisController.activePlayer?.playbackState === MprisPlaybackState.Playing
|
||||||
|
|
||||||
|
sourceComponent: Component {
|
||||||
|
Ref {
|
||||||
|
service: CavaService
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
running: !CavaService.cavaAvailable && MprisController.activePlayer?.playbackState === MprisPlaybackState.Playing
|
||||||
|
interval: 256
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
CavaService.values = [Math.random() * 40 + 10, Math.random() * 60 + 20, Math.random() * 50 + 15, Math.random() * 35 + 20, Math.random() * 45 + 15, Math.random() * 55 + 25]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1.5
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: 6
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 2
|
||||||
|
height: {
|
||||||
|
if (MprisController.activePlayer?.playbackState === MprisPlaybackState.Playing && CavaService.values.length > index) {
|
||||||
|
const rawLevel = CavaService.values[index] || 0
|
||||||
|
const scaledLevel = Math.sqrt(Math.min(Math.max(rawLevel, 0), 100) / 100) * 100
|
||||||
|
const maxHeight = Theme.iconSize - 2
|
||||||
|
const minHeight = 3
|
||||||
|
return minHeight + (scaledLevel / 100) * (maxHeight - minHeight)
|
||||||
|
}
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
radius: 1.5
|
||||||
|
color: "white"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Anims.durShort
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Anims.standardDecel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const player = MprisController.activePlayer
|
||||||
|
if (!player?.trackTitle)
|
||||||
|
return ""
|
||||||
|
const title = player.trackTitle
|
||||||
|
const artist = player.trackArtist || ""
|
||||||
|
return artist ? title + " • " + artist : title
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: "white"
|
||||||
|
opacity: 0.9
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: Math.min(implicitWidth, 400)
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 20
|
||||||
|
height: 20
|
||||||
|
radius: 10
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: prevArea.containsMouse ? Qt.rgba(255, 255, 255, 0.2) : "transparent"
|
||||||
|
visible: MprisController.activePlayer
|
||||||
|
opacity: (MprisController.activePlayer?.canGoPrevious ?? false) ? 1 : 0.3
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "skip_previous"
|
||||||
|
size: 12
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: prevArea
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: MprisController.activePlayer?.canGoPrevious ?? false
|
||||||
|
hoverEnabled: enabled
|
||||||
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
onClicked: MprisController.activePlayer?.previous()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
radius: 12
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: MprisController.activePlayer?.playbackState === MprisPlaybackState.Playing ? Qt.rgba(255, 255, 255, 0.9) : Qt.rgba(255, 255, 255, 0.2)
|
||||||
|
visible: MprisController.activePlayer
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: MprisController.activePlayer?.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow"
|
||||||
|
size: 14
|
||||||
|
color: MprisController.activePlayer?.playbackState === MprisPlaybackState.Playing ? "black" : "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: MprisController.activePlayer
|
||||||
|
hoverEnabled: enabled
|
||||||
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
onClicked: MprisController.activePlayer?.togglePlaying()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 20
|
||||||
|
height: 20
|
||||||
|
radius: 10
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: nextArea.containsMouse ? Qt.rgba(255, 255, 255, 0.2) : "transparent"
|
||||||
|
visible: MprisController.activePlayer
|
||||||
|
opacity: (MprisController.activePlayer?.canGoNext ?? false) ? 1 : 0.3
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "skip_next"
|
||||||
|
size: 12
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: nextArea
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: MprisController.activePlayer?.canGoNext ?? false
|
||||||
|
hoverEnabled: enabled
|
||||||
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
onClicked: MprisController.activePlayer?.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 1
|
||||||
|
height: 24
|
||||||
|
color: Qt.rgba(255, 255, 255, 0.2)
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: MprisController.activePlayer && WeatherService.weather.available
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -1060,6 +1247,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onReadyToLaunch() {
|
function onReadyToLaunch() {
|
||||||
|
root.sessionLock.locked = false
|
||||||
GreeterState.unlocking = true
|
GreeterState.unlocking = true
|
||||||
const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex]
|
const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex]
|
||||||
if (sessionCmd) {
|
if (sessionCmd) {
|
||||||
|
|||||||
@@ -1,34 +1,18 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import Quickshell.Services.Greetd
|
import Quickshell.Services.Greetd
|
||||||
import qs.Common
|
|
||||||
|
|
||||||
Variants {
|
WlSessionLockSurface {
|
||||||
model: Quickshell.screens
|
id: root
|
||||||
|
|
||||||
PanelWindow {
|
required property WlSessionLock lock
|
||||||
id: root
|
|
||||||
|
|
||||||
property var modelData
|
color: "transparent"
|
||||||
|
|
||||||
screen: modelData
|
GreeterContent {
|
||||||
anchors {
|
anchors.fill: parent
|
||||||
left: true
|
screenName: root.screen?.name ?? ""
|
||||||
right: true
|
sessionLock: root.lock
|
||||||
top: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
exclusionMode: ExclusionMode.Normal
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
GreeterContent {
|
|
||||||
anchors.fill: parent
|
|
||||||
screenName: root.screen?.name ?? ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,284 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
|
|
||||||
Scope {
|
|
||||||
id: overviewScope
|
|
||||||
|
|
||||||
property bool overviewOpen: false
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: hyprlandLoader
|
|
||||||
active: overviewScope.overviewOpen
|
|
||||||
asynchronous: false
|
|
||||||
|
|
||||||
sourceComponent: Variants {
|
|
||||||
id: overviewVariants
|
|
||||||
model: Quickshell.screens
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: root
|
|
||||||
required property var modelData
|
|
||||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
|
|
||||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
|
|
||||||
|
|
||||||
screen: modelData
|
|
||||||
visible: overviewScope.overviewOpen
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
WlrLayershell.namespace: "quickshell:overview"
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
WlrLayershell.exclusiveZone: -1
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
id: grab
|
|
||||||
windows: [root]
|
|
||||||
active: false
|
|
||||||
property bool hasBeenActivated: false
|
|
||||||
onActiveChanged: {
|
|
||||||
if (active) {
|
|
||||||
hasBeenActivated = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onCleared: () => {
|
|
||||||
if (hasBeenActivated && overviewScope.overviewOpen) {
|
|
||||||
overviewScope.overviewOpen = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: overviewScope
|
|
||||||
function onOverviewOpenChanged() {
|
|
||||||
if (overviewScope.overviewOpen) {
|
|
||||||
grab.hasBeenActivated = false
|
|
||||||
delayedGrabTimer.start()
|
|
||||||
} else {
|
|
||||||
delayedGrabTimer.stop()
|
|
||||||
grab.active = false
|
|
||||||
grab.hasBeenActivated = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onMonitorIsFocusedChanged() {
|
|
||||||
if (overviewScope.overviewOpen && root.monitorIsFocused && !grab.active) {
|
|
||||||
grab.hasBeenActivated = false
|
|
||||||
grab.active = true
|
|
||||||
} else if (overviewScope.overviewOpen && !root.monitorIsFocused && grab.active) {
|
|
||||||
grab.active = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: delayedGrabTimer
|
|
||||||
interval: 150
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
if (overviewScope.overviewOpen && root.monitorIsFocused) {
|
|
||||||
grab.active = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: closeTimer
|
|
||||||
interval: Theme.expressiveDurations.expressiveDefaultSpatial + 120
|
|
||||||
onTriggered: {
|
|
||||||
root.visible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
anchors.fill: parent
|
|
||||||
color: "black"
|
|
||||||
opacity: overviewScope.overviewOpen ? 0.5 : 0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: mouse => {
|
|
||||||
const localPos = mapToItem(contentContainer, mouse.x, mouse.y)
|
|
||||||
if (localPos.x < 0 || localPos.x > contentContainer.width || localPos.y < 0 || localPos.y > contentContainer.height) {
|
|
||||||
overviewScope.overviewOpen = false
|
|
||||||
closeTimer.restart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: contentContainer
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: 100
|
|
||||||
width: childrenRect.width
|
|
||||||
height: childrenRect.height
|
|
||||||
|
|
||||||
opacity: overviewScope.overviewOpen ? 1 : 0
|
|
||||||
transform: [scaleTransform, motionTransform]
|
|
||||||
|
|
||||||
Scale {
|
|
||||||
id: scaleTransform
|
|
||||||
origin.x: contentContainer.width / 2
|
|
||||||
origin.y: contentContainer.height / 2
|
|
||||||
xScale: overviewScope.overviewOpen ? 1 : 0.96
|
|
||||||
yScale: overviewScope.overviewOpen ? 1 : 0.96
|
|
||||||
|
|
||||||
Behavior on xScale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on yScale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Translate {
|
|
||||||
id: motionTransform
|
|
||||||
x: 0
|
|
||||||
y: overviewScope.overviewOpen ? 0 : Theme.spacingL
|
|
||||||
|
|
||||||
Behavior on y {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: overviewLoader
|
|
||||||
active: overviewScope.overviewOpen
|
|
||||||
asynchronous: false
|
|
||||||
|
|
||||||
sourceComponent: OverviewWidget {
|
|
||||||
panelWindow: root
|
|
||||||
overviewOpen: overviewScope.overviewOpen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FocusScope {
|
|
||||||
id: focusScope
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: overviewScope.overviewOpen
|
|
||||||
focus: overviewScope.overviewOpen && root.monitorIsFocused
|
|
||||||
|
|
||||||
Keys.onEscapePressed: event => {
|
|
||||||
if (!root.monitorIsFocused) return
|
|
||||||
overviewScope.overviewOpen = false
|
|
||||||
closeTimer.restart()
|
|
||||||
event.accepted = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: event => {
|
|
||||||
if (!root.monitorIsFocused) return
|
|
||||||
|
|
||||||
if (event.key === Qt.Key_Left || event.key === Qt.Key_Right) {
|
|
||||||
if (!overviewLoader.item) return
|
|
||||||
|
|
||||||
const thisMonitorWorkspaceIds = overviewLoader.item.thisMonitorWorkspaceIds
|
|
||||||
if (thisMonitorWorkspaceIds.length === 0) return
|
|
||||||
|
|
||||||
const currentId = root.monitor.activeWorkspace?.id ?? thisMonitorWorkspaceIds[0]
|
|
||||||
const currentIndex = thisMonitorWorkspaceIds.indexOf(currentId)
|
|
||||||
|
|
||||||
let targetIndex
|
|
||||||
if (event.key === Qt.Key_Left) {
|
|
||||||
targetIndex = currentIndex - 1
|
|
||||||
if (targetIndex < 0) targetIndex = thisMonitorWorkspaceIds.length - 1
|
|
||||||
} else {
|
|
||||||
targetIndex = currentIndex + 1
|
|
||||||
if (targetIndex >= thisMonitorWorkspaceIds.length) targetIndex = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetId = thisMonitorWorkspaceIds[targetIndex]
|
|
||||||
Hyprland.dispatch("workspace " + targetId)
|
|
||||||
event.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible && overviewScope.overviewOpen && root.monitorIsFocused) {
|
|
||||||
Qt.callLater(() => focusScope.forceActiveFocus())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onMonitorIsFocusedChanged() {
|
|
||||||
if (root.monitorIsFocused && overviewScope.overviewOpen) {
|
|
||||||
Qt.callLater(() => focusScope.forceActiveFocus())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible && overviewScope.overviewOpen) {
|
|
||||||
Qt.callLater(() => focusScope.forceActiveFocus())
|
|
||||||
} else if (!visible) {
|
|
||||||
grab.active = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: overviewScope
|
|
||||||
function onOverviewOpenChanged() {
|
|
||||||
if (overviewScope.overviewOpen) {
|
|
||||||
closeTimer.stop()
|
|
||||||
root.visible = true
|
|
||||||
Qt.callLater(() => focusScope.forceActiveFocus())
|
|
||||||
} else {
|
|
||||||
closeTimer.restart()
|
|
||||||
grab.active = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,428 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Effects
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
required property var panelWindow
|
|
||||||
required property bool overviewOpen
|
|
||||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
|
|
||||||
readonly property int workspacesShown: SettingsData.overviewRows * SettingsData.overviewColumns
|
|
||||||
|
|
||||||
readonly property var allWorkspaces: Hyprland.workspaces?.values || []
|
|
||||||
readonly property var allWorkspaceIds: {
|
|
||||||
const workspaces = allWorkspaces
|
|
||||||
if (!workspaces || workspaces.length === 0) return []
|
|
||||||
try {
|
|
||||||
const ids = workspaces.map(ws => ws?.id).filter(id => id !== null && id !== undefined)
|
|
||||||
return ids.sort((a, b) => a - b)
|
|
||||||
} catch (e) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property var thisMonitorWorkspaceIds: {
|
|
||||||
const workspaces = allWorkspaces
|
|
||||||
const mon = monitor
|
|
||||||
if (!workspaces || workspaces.length === 0 || !mon) return []
|
|
||||||
try {
|
|
||||||
const filtered = workspaces.filter(ws => ws?.monitor?.name === mon.name)
|
|
||||||
return filtered.map(ws => ws?.id).filter(id => id !== null && id !== undefined).sort((a, b) => a - b)
|
|
||||||
} catch (e) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property var displayedWorkspaceIds: {
|
|
||||||
if (!allWorkspaceIds || allWorkspaceIds.length === 0) {
|
|
||||||
const result = []
|
|
||||||
for (let i = 1; i <= workspacesShown; i++) {
|
|
||||||
result.push(i)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const maxExisting = Math.max(...allWorkspaceIds)
|
|
||||||
const totalNeeded = Math.max(workspacesShown, allWorkspaceIds.length)
|
|
||||||
const result = []
|
|
||||||
|
|
||||||
for (let i = 1; i <= maxExisting; i++) {
|
|
||||||
result.push(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
let nextId = maxExisting + 1
|
|
||||||
while (result.length < totalNeeded) {
|
|
||||||
result.push(nextId)
|
|
||||||
nextId++
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
} catch (e) {
|
|
||||||
const result = []
|
|
||||||
for (let i = 1; i <= workspacesShown; i++) {
|
|
||||||
result.push(i)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property int minWorkspaceId: displayedWorkspaceIds.length > 0 ? displayedWorkspaceIds[0] : 1
|
|
||||||
readonly property int maxWorkspaceId: displayedWorkspaceIds.length > 0 ? displayedWorkspaceIds[displayedWorkspaceIds.length - 1] : workspacesShown
|
|
||||||
readonly property int displayWorkspaceCount: displayedWorkspaceIds.length
|
|
||||||
|
|
||||||
function getWorkspaceMonitorName(workspaceId) {
|
|
||||||
if (!allWorkspaces || !workspaceId) return ""
|
|
||||||
try {
|
|
||||||
const ws = allWorkspaces.find(w => w?.id === workspaceId)
|
|
||||||
return ws?.monitor?.name ?? ""
|
|
||||||
} catch (e) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function workspaceHasWindows(workspaceId) {
|
|
||||||
if (!workspaceId) return false
|
|
||||||
try {
|
|
||||||
const workspace = allWorkspaces.find(ws => ws?.id === workspaceId)
|
|
||||||
if (!workspace) return false
|
|
||||||
const toplevels = workspace?.toplevels?.values || []
|
|
||||||
return toplevels.length > 0
|
|
||||||
} catch (e) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool monitorIsFocused: monitor?.focused ?? false
|
|
||||||
property real scale: SettingsData.overviewScale
|
|
||||||
property color activeBorderColor: Theme.primary
|
|
||||||
|
|
||||||
property real workspaceImplicitWidth: ((monitor.width / monitor.scale) * root.scale)
|
|
||||||
property real workspaceImplicitHeight: ((monitor.height / monitor.scale) * root.scale)
|
|
||||||
|
|
||||||
property int workspaceZ: 0
|
|
||||||
property int windowZ: 1
|
|
||||||
property int monitorLabelZ: 2
|
|
||||||
property int windowDraggingZ: 99999
|
|
||||||
property real workspaceSpacing: 5
|
|
||||||
|
|
||||||
property int draggingFromWorkspace: -1
|
|
||||||
property int draggingTargetWorkspace: -1
|
|
||||||
|
|
||||||
implicitWidth: overviewBackground.implicitWidth + Theme.spacingL * 2
|
|
||||||
implicitHeight: overviewBackground.implicitHeight + Theme.spacingL * 2
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
Hyprland.refreshToplevels()
|
|
||||||
Hyprland.refreshWorkspaces()
|
|
||||||
Hyprland.refreshMonitors()
|
|
||||||
}
|
|
||||||
|
|
||||||
onOverviewOpenChanged: {
|
|
||||||
if (overviewOpen) {
|
|
||||||
Hyprland.refreshToplevels()
|
|
||||||
Hyprland.refreshWorkspaces()
|
|
||||||
Hyprland.refreshMonitors()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: overviewBackground
|
|
||||||
property real padding: 10
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingL
|
|
||||||
|
|
||||||
implicitWidth: workspaceColumnLayout.implicitWidth + padding * 2
|
|
||||||
implicitHeight: workspaceColumnLayout.implicitHeight + padding * 2
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.surfaceContainer
|
|
||||||
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: MultiEffect {
|
|
||||||
shadowEnabled: true
|
|
||||||
shadowBlur: 0.5
|
|
||||||
shadowHorizontalOffset: 0
|
|
||||||
shadowVerticalOffset: 4
|
|
||||||
shadowColor: Theme.shadowStrong
|
|
||||||
shadowOpacity: 1
|
|
||||||
blurMax: 32
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: workspaceColumnLayout
|
|
||||||
|
|
||||||
z: root.workspaceZ
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: workspaceSpacing
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: SettingsData.overviewRows
|
|
||||||
delegate: RowLayout {
|
|
||||||
id: row
|
|
||||||
property int rowIndex: index
|
|
||||||
spacing: workspaceSpacing
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: SettingsData.overviewColumns
|
|
||||||
Rectangle {
|
|
||||||
id: workspace
|
|
||||||
property int colIndex: index
|
|
||||||
property int workspaceIndex: rowIndex * SettingsData.overviewColumns + colIndex
|
|
||||||
property int workspaceValue: (root.displayedWorkspaceIds && workspaceIndex < root.displayedWorkspaceIds.length) ? root.displayedWorkspaceIds[workspaceIndex] : -1
|
|
||||||
property bool workspaceExists: (root.allWorkspaceIds && workspaceValue > 0) ? root.allWorkspaceIds.includes(workspaceValue) : false
|
|
||||||
property var workspaceObj: (workspaceExists && Hyprland.workspaces?.values) ? Hyprland.workspaces.values.find(ws => ws?.id === workspaceValue) : null
|
|
||||||
property bool isActive: workspaceObj?.active ?? false
|
|
||||||
property bool isOnThisMonitor: (workspaceObj && root.monitor) ? (workspaceObj.monitor?.name === root.monitor.name) : true
|
|
||||||
property bool hasWindows: (workspaceValue > 0) ? root.workspaceHasWindows(workspaceValue) : false
|
|
||||||
property string workspaceMonitorName: (workspaceValue > 0) ? root.getWorkspaceMonitorName(workspaceValue) : ""
|
|
||||||
property color defaultWorkspaceColor: workspaceExists ? Theme.surfaceContainer : Theme.withAlpha(Theme.surfaceContainer, 0.3)
|
|
||||||
property color hoveredWorkspaceColor: Qt.lighter(defaultWorkspaceColor, 1.1)
|
|
||||||
property color hoveredBorderColor: Theme.surfaceVariant
|
|
||||||
property bool hoveredWhileDragging: false
|
|
||||||
property bool shouldShowActiveIndicator: isActive && isOnThisMonitor && hasWindows
|
|
||||||
|
|
||||||
visible: workspaceValue !== -1
|
|
||||||
|
|
||||||
implicitWidth: root.workspaceImplicitWidth
|
|
||||||
implicitHeight: root.workspaceImplicitHeight
|
|
||||||
color: hoveredWhileDragging ? hoveredWorkspaceColor : defaultWorkspaceColor
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
border.width: 2
|
|
||||||
border.color: hoveredWhileDragging ? hoveredBorderColor : (shouldShowActiveIndicator ? root.activeBorderColor : "transparent")
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: workspaceValue
|
|
||||||
font.pixelSize: Theme.fontSizeXLarge * 6
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
color: Theme.withAlpha(Theme.surfaceText, workspaceExists ? 0.2 : 0.1)
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: workspaceArea
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onClicked: {
|
|
||||||
if (root.draggingTargetWorkspace === -1) {
|
|
||||||
root.overviewOpen = false
|
|
||||||
Hyprland.dispatch(`workspace ${workspaceValue}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DropArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onEntered: {
|
|
||||||
root.draggingTargetWorkspace = workspaceValue
|
|
||||||
if (root.draggingFromWorkspace == root.draggingTargetWorkspace) return
|
|
||||||
hoveredWhileDragging = true
|
|
||||||
}
|
|
||||||
onExited: {
|
|
||||||
hoveredWhileDragging = false
|
|
||||||
if (root.draggingTargetWorkspace == workspaceValue) root.draggingTargetWorkspace = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: windowSpace
|
|
||||||
anchors.centerIn: parent
|
|
||||||
implicitWidth: workspaceColumnLayout.implicitWidth
|
|
||||||
implicitHeight: workspaceColumnLayout.implicitHeight
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: {
|
|
||||||
const workspaces = root.allWorkspaces
|
|
||||||
const minId = root.minWorkspaceId
|
|
||||||
const maxId = root.maxWorkspaceId
|
|
||||||
|
|
||||||
if (!workspaces || workspaces.length === 0) return []
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = []
|
|
||||||
for (const workspace of workspaces) {
|
|
||||||
const wsId = workspace?.id ?? -1
|
|
||||||
if (wsId >= minId && wsId <= maxId) {
|
|
||||||
const toplevels = workspace?.toplevels?.values || []
|
|
||||||
for (const toplevel of toplevels) {
|
|
||||||
result.push(toplevel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
} catch (e) {
|
|
||||||
console.error("OverviewWidget filter error:", e)
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delegate: OverviewWindow {
|
|
||||||
id: window
|
|
||||||
required property var modelData
|
|
||||||
|
|
||||||
overviewOpen: root.overviewOpen
|
|
||||||
readonly property int windowWorkspaceId: modelData?.workspace?.id ?? -1
|
|
||||||
|
|
||||||
function getWorkspaceIndex() {
|
|
||||||
if (!root.displayedWorkspaceIds || root.displayedWorkspaceIds.length === 0) return 0
|
|
||||||
if (!windowWorkspaceId || windowWorkspaceId < 0) return 0
|
|
||||||
try {
|
|
||||||
for (let i = 0; i < root.displayedWorkspaceIds.length; i++) {
|
|
||||||
if (root.displayedWorkspaceIds[i] === windowWorkspaceId) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
} catch (e) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property int workspaceIndex: getWorkspaceIndex()
|
|
||||||
readonly property int workspaceColIndex: workspaceIndex % SettingsData.overviewColumns
|
|
||||||
readonly property int workspaceRowIndex: Math.floor(workspaceIndex / SettingsData.overviewColumns)
|
|
||||||
|
|
||||||
toplevel: modelData
|
|
||||||
scale: root.scale
|
|
||||||
availableWorkspaceWidth: root.workspaceImplicitWidth
|
|
||||||
availableWorkspaceHeight: root.workspaceImplicitHeight
|
|
||||||
widgetMonitorId: root.monitor.id
|
|
||||||
|
|
||||||
xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex
|
|
||||||
yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex
|
|
||||||
|
|
||||||
z: atInitPosition ? root.windowZ : root.windowDraggingZ
|
|
||||||
property bool atInitPosition: (initX == x && initY == y)
|
|
||||||
|
|
||||||
Drag.hotSpot.x: width / 2
|
|
||||||
Drag.hotSpot.y: height / 2
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: dragArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
onEntered: window.hovered = true
|
|
||||||
onExited: window.hovered = false
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
|
|
||||||
drag.target: parent
|
|
||||||
|
|
||||||
onPressed: (mouse) => {
|
|
||||||
root.draggingFromWorkspace = windowData?.workspace.id
|
|
||||||
window.pressed = true
|
|
||||||
window.Drag.active = true
|
|
||||||
window.Drag.source = window
|
|
||||||
window.Drag.hotSpot.x = mouse.x
|
|
||||||
window.Drag.hotSpot.y = mouse.y
|
|
||||||
}
|
|
||||||
|
|
||||||
onReleased: {
|
|
||||||
const targetWorkspace = root.draggingTargetWorkspace
|
|
||||||
window.pressed = false
|
|
||||||
window.Drag.active = false
|
|
||||||
root.draggingFromWorkspace = -1
|
|
||||||
root.draggingTargetWorkspace = -1
|
|
||||||
|
|
||||||
if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) {
|
|
||||||
Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace},address:${windowData?.address}`)
|
|
||||||
Qt.callLater(() => {
|
|
||||||
Hyprland.refreshToplevels()
|
|
||||||
Hyprland.refreshWorkspaces()
|
|
||||||
Qt.callLater(() => {
|
|
||||||
window.x = window.initX
|
|
||||||
window.y = window.initY
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
window.x = window.initX
|
|
||||||
window.y = window.initY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: (event) => {
|
|
||||||
if (!windowData) return
|
|
||||||
|
|
||||||
if (event.button === Qt.LeftButton) {
|
|
||||||
root.overviewOpen = false
|
|
||||||
Hyprland.dispatch(`focuswindow address:${windowData.address}`)
|
|
||||||
event.accepted = true
|
|
||||||
} else if (event.button === Qt.MiddleButton) {
|
|
||||||
Hyprland.dispatch(`closewindow address:${windowData.address}`)
|
|
||||||
event.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: monitorLabelSpace
|
|
||||||
anchors.centerIn: parent
|
|
||||||
implicitWidth: workspaceColumnLayout.implicitWidth
|
|
||||||
implicitHeight: workspaceColumnLayout.implicitHeight
|
|
||||||
z: root.monitorLabelZ
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: SettingsData.overviewRows
|
|
||||||
delegate: Item {
|
|
||||||
id: labelRow
|
|
||||||
property int rowIndex: index
|
|
||||||
y: (root.workspaceImplicitHeight + workspaceSpacing) * rowIndex
|
|
||||||
width: parent.width
|
|
||||||
height: root.workspaceImplicitHeight
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: SettingsData.overviewColumns
|
|
||||||
delegate: Item {
|
|
||||||
id: labelItem
|
|
||||||
property int colIndex: index
|
|
||||||
property int workspaceIndex: labelRow.rowIndex * SettingsData.overviewColumns + colIndex
|
|
||||||
property int workspaceValue: (root.displayedWorkspaceIds && workspaceIndex < root.displayedWorkspaceIds.length) ? root.displayedWorkspaceIds[workspaceIndex] : -1
|
|
||||||
property bool workspaceExists: (root.allWorkspaceIds && workspaceValue > 0) ? root.allWorkspaceIds.includes(workspaceValue) : false
|
|
||||||
property string workspaceMonitorName: (workspaceValue > 0) ? root.getWorkspaceMonitorName(workspaceValue) : ""
|
|
||||||
|
|
||||||
x: (root.workspaceImplicitWidth + workspaceSpacing) * colIndex
|
|
||||||
width: root.workspaceImplicitWidth
|
|
||||||
height: root.workspaceImplicitHeight
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.margins: Theme.spacingS
|
|
||||||
width: monitorNameText.contentWidth + Theme.spacingS * 2
|
|
||||||
height: monitorNameText.contentHeight + Theme.spacingXS * 2
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.surface
|
|
||||||
visible: labelItem.workspaceExists && labelItem.workspaceMonitorName !== ""
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: monitorNameText
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: labelItem.workspaceMonitorName
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Effects
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import qs.Common
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
property var toplevel
|
|
||||||
property var scale
|
|
||||||
required property bool overviewOpen
|
|
||||||
property var availableWorkspaceWidth
|
|
||||||
property var availableWorkspaceHeight
|
|
||||||
property bool restrictToWorkspace: true
|
|
||||||
|
|
||||||
readonly property var windowData: toplevel?.lastIpcObject || null
|
|
||||||
readonly property var monitorObj: toplevel?.monitor
|
|
||||||
readonly property var monitorData: monitorObj?.lastIpcObject || null
|
|
||||||
|
|
||||||
property real initX: Math.max(((windowData?.at?.[0] ?? 0) - (monitorData?.x ?? 0) - (monitorData?.reserved?.[0] ?? 0)) * root.scale, 0) + xOffset
|
|
||||||
property real initY: Math.max(((windowData?.at?.[1] ?? 0) - (monitorData?.y ?? 0) - (monitorData?.reserved?.[1] ?? 0)) * root.scale, 0) + yOffset
|
|
||||||
property real xOffset: 0
|
|
||||||
property real yOffset: 0
|
|
||||||
property int widgetMonitorId: 0
|
|
||||||
|
|
||||||
property var targetWindowWidth: (windowData?.size?.[0] ?? 100) * scale
|
|
||||||
property var targetWindowHeight: (windowData?.size?.[1] ?? 100) * scale
|
|
||||||
property bool hovered: false
|
|
||||||
property bool pressed: false
|
|
||||||
|
|
||||||
property var iconToWindowRatio: 0.25
|
|
||||||
property var iconToWindowRatioCompact: 0.45
|
|
||||||
property var entry: DesktopEntries.heuristicLookup(windowData?.class)
|
|
||||||
property var iconPath: Quickshell.iconPath(entry?.icon ?? windowData?.class ?? "application-x-executable", "image-missing")
|
|
||||||
property bool compactMode: Theme.fontSizeSmall * 4 > targetWindowHeight || Theme.fontSizeSmall * 4 > targetWindowWidth
|
|
||||||
|
|
||||||
x: initX
|
|
||||||
y: initY
|
|
||||||
width: Math.min((windowData?.size?.[0] ?? 100) * root.scale, availableWorkspaceWidth)
|
|
||||||
height: Math.min((windowData?.size?.[1] ?? 100) * root.scale, availableWorkspaceHeight)
|
|
||||||
opacity: (monitorObj?.id ?? -1) == widgetMonitorId ? 1 : 0.4
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: maskRect
|
|
||||||
width: root.width
|
|
||||||
height: root.height
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
visible: false
|
|
||||||
layer.enabled: true
|
|
||||||
}
|
|
||||||
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: MultiEffect {
|
|
||||||
maskEnabled: true
|
|
||||||
maskSource: maskRect
|
|
||||||
maskSpreadAtMin: 1
|
|
||||||
maskThresholdMin: 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on x {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on y {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ScreencopyView {
|
|
||||||
id: windowPreview
|
|
||||||
anchors.fill: parent
|
|
||||||
captureSource: root.overviewOpen ? root.toplevel?.wayland : null
|
|
||||||
live: true
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: pressed ? Theme.withAlpha(Theme.surfaceContainerHigh, 0.5) :
|
|
||||||
hovered ? Theme.withAlpha(Theme.surfaceVariant, 0.3) :
|
|
||||||
Theme.withAlpha(Theme.surfaceContainer, 0.1)
|
|
||||||
border.color: Theme.withAlpha(Theme.outline, 0.3)
|
|
||||||
border.width: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
spacing: Theme.fontSizeSmall * 0.5
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: windowIcon
|
|
||||||
property var iconSize: {
|
|
||||||
return Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / (root.monitorData?.scale ?? 1)
|
|
||||||
}
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
source: root.iconPath
|
|
||||||
width: iconSize
|
|
||||||
height: iconSize
|
|
||||||
sourceSize: Qt.size(iconSize, iconSize)
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -537,7 +537,7 @@ Rectangle {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: clearText
|
id: clearText
|
||||||
text: I18n.tr("Dismiss")
|
text: I18n.tr("Clear")
|
||||||
color: parent.isHovered ? Theme.primary : Theme.surfaceVariantText
|
color: parent.isHovered ? Theme.primary : Theme.surfaceVariantText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -630,7 +630,7 @@ Rectangle {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: clearText
|
id: clearText
|
||||||
text: I18n.tr("Dismiss")
|
text: I18n.tr("Clear")
|
||||||
color: clearButton.isHovered ? Theme.primary : Theme.surfaceVariantText
|
color: clearButton.isHovered ? Theme.primary : Theme.surfaceVariantText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: I18n.tr("Clear")
|
text: I18n.tr("Clear All")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: clearArea.containsMouse ? Theme.primary : Theme.surfaceText
|
color: clearArea.containsMouse ? Theme.primary : Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ PanelWindow {
|
|||||||
property bool exiting: false
|
property bool exiting: false
|
||||||
property bool _isDestroying: false
|
property bool _isDestroying: false
|
||||||
property bool _finalized: false
|
property bool _finalized: false
|
||||||
readonly property string clearText: I18n.tr("Dismiss")
|
readonly property string clearText: I18n.tr("Clear")
|
||||||
|
|
||||||
signal entered
|
signal entered
|
||||||
signal exitFinished
|
signal exitFinished
|
||||||
@@ -512,7 +512,7 @@ PanelWindow {
|
|||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
z: -1
|
z: -1
|
||||||
onEntered: {
|
onEntered: {
|
||||||
@@ -523,20 +523,9 @@ PanelWindow {
|
|||||||
if (notificationData && notificationData.popup && notificationData.timer)
|
if (notificationData && notificationData.popup && notificationData.timer)
|
||||||
notificationData.timer.restart()
|
notificationData.timer.restart()
|
||||||
}
|
}
|
||||||
onClicked: (mouse) => {
|
onClicked: {
|
||||||
if (!notificationData || win.exiting)
|
if (notificationData && !win.exiting)
|
||||||
return
|
notificationData.popup = false
|
||||||
|
|
||||||
if (mouse.button === Qt.RightButton) {
|
|
||||||
NotificationService.dismissNotification(notificationData)
|
|
||||||
} else if (mouse.button === Qt.LeftButton) {
|
|
||||||
if (notificationData.actions && notificationData.actions.length > 0) {
|
|
||||||
notificationData.actions[0].invoke()
|
|
||||||
NotificationService.dismissNotification(notificationData)
|
|
||||||
} else {
|
|
||||||
notificationData.popup = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,35 +26,35 @@ Item {
|
|||||||
const baseItems = [
|
const baseItems = [
|
||||||
{
|
{
|
||||||
name: "Test Item 1",
|
name: "Test Item 1",
|
||||||
icon: "material:lightbulb",
|
icon: "lightbulb",
|
||||||
comment: "This is a test item that shows a toast notification",
|
comment: "This is a test item that shows a toast notification",
|
||||||
action: "toast:Test Item 1 executed!",
|
action: "toast:Test Item 1 executed!",
|
||||||
categories: ["LauncherExample"]
|
categories: ["LauncherExample"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test Item 2",
|
name: "Test Item 2",
|
||||||
icon: "material:star",
|
icon: "star",
|
||||||
comment: "Another test item with different action",
|
comment: "Another test item with different action",
|
||||||
action: "toast:Test Item 2 clicked!",
|
action: "toast:Test Item 2 clicked!",
|
||||||
categories: ["LauncherExample"]
|
categories: ["LauncherExample"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test Item 3",
|
name: "Test Item 3",
|
||||||
icon: "material:favorite",
|
icon: "favorite",
|
||||||
comment: "Third test item for demonstration",
|
comment: "Third test item for demonstration",
|
||||||
action: "toast:Test Item 3 activated!",
|
action: "toast:Test Item 3 activated!",
|
||||||
categories: ["LauncherExample"]
|
categories: ["LauncherExample"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Example Copy Action",
|
name: "Example Copy Action",
|
||||||
icon: "material:content_copy",
|
icon: "content_copy",
|
||||||
comment: "Demonstrates copying text to clipboard",
|
comment: "Demonstrates copying text to clipboard",
|
||||||
action: "copy:This text was copied by the launcher plugin!",
|
action: "copy:This text was copied by the launcher plugin!",
|
||||||
categories: ["LauncherExample"]
|
categories: ["LauncherExample"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Example Script Action",
|
name: "Example Script Action",
|
||||||
icon: "material:terminal",
|
icon: "terminal",
|
||||||
comment: "Demonstrates running a simple command",
|
comment: "Demonstrates running a simple command",
|
||||||
action: "script:echo 'Hello from launcher plugin!'",
|
action: "script:echo 'Hello from launcher plugin!'",
|
||||||
categories: ["LauncherExample"]
|
categories: ["LauncherExample"]
|
||||||
|
|||||||
@@ -88,41 +88,13 @@ function executeItem(item): void
|
|||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
name: "Item Name", // Display name
|
name: "Item Name", // Display name
|
||||||
icon: "icon_name", // Icon (optional, see Icon Types below)
|
icon: "icon_name", // Material icon
|
||||||
comment: "Description", // Subtitle text
|
comment: "Description", // Subtitle text
|
||||||
action: "type:data", // Action to execute
|
action: "type:data", // Action to execute
|
||||||
categories: ["PluginName"] // Category array
|
categories: ["PluginName"] // Category array
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Icon Types**:
|
|
||||||
|
|
||||||
The `icon` field supports three formats:
|
|
||||||
|
|
||||||
1. **Material Design Icons** - Use `material:` prefix:
|
|
||||||
```javascript
|
|
||||||
icon: "material:lightbulb" // Material Symbols Rounded font
|
|
||||||
```
|
|
||||||
Examples: `material:star`, `material:favorite`, `material:settings`
|
|
||||||
|
|
||||||
2. **Desktop Theme Icons** - Use icon name directly:
|
|
||||||
```javascript
|
|
||||||
icon: "firefox" // Uses system icon theme
|
|
||||||
```
|
|
||||||
Examples: `firefox`, `chrome`, `folder`, `text-editor`
|
|
||||||
|
|
||||||
3. **No Icon** - Omit the `icon` field entirely:
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
name: "😀 Grinning Face",
|
|
||||||
// No icon field
|
|
||||||
comment: "Copy emoji",
|
|
||||||
action: "copy:😀",
|
|
||||||
categories: ["MyPlugin"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Perfect for emoji pickers or text-only items where the icon area should be hidden
|
|
||||||
|
|
||||||
**Action Format**: `type:data` where:
|
**Action Format**: `type:data` where:
|
||||||
- `type` - Action handler (toast, copy, script, etc.)
|
- `type` - Action handler (toast, copy, script, etc.)
|
||||||
- `data` - Action-specific data
|
- `data` - Action-specific data
|
||||||
|
|||||||
@@ -1221,51 +1221,11 @@ Item {
|
|||||||
Each item returned by `getItems()` must include:
|
Each item returned by `getItems()` must include:
|
||||||
|
|
||||||
- `name` (string): Display name shown in launcher
|
- `name` (string): Display name shown in launcher
|
||||||
- `icon` (string, optional): Icon specification (see Icon Types below)
|
- `icon` (string): Material Design icon name
|
||||||
- `comment` (string): Description/subtitle text
|
- `comment` (string): Description/subtitle text
|
||||||
- `action` (string): Action identifier in `type:data` format
|
- `action` (string): Action identifier in `type:data` format
|
||||||
- `categories` (array): Array containing your plugin name
|
- `categories` (array): Array containing your plugin name
|
||||||
|
|
||||||
### Icon Types
|
|
||||||
|
|
||||||
The `icon` field supports three formats:
|
|
||||||
|
|
||||||
**1. Material Design Icons** - Use the `material:` prefix:
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
name: "My Item",
|
|
||||||
icon: "material:lightbulb", // Material Symbols Rounded font
|
|
||||||
comment: "Uses Material Design icon",
|
|
||||||
action: "toast:Hello!",
|
|
||||||
categories: ["MyPlugin"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Available icons: Any icon from Material Symbols font (e.g., `lightbulb`, `star`, `favorite`, `settings`, `terminal`, `translate`, `sentiment_satisfied`)
|
|
||||||
|
|
||||||
**2. Desktop Theme Icons** - Use icon name directly:
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
name: "Firefox",
|
|
||||||
icon: "firefox", // Uses system icon theme
|
|
||||||
comment: "Launches Firefox browser",
|
|
||||||
action: "exec:firefox",
|
|
||||||
categories: ["MyPlugin"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Uses the user's installed icon theme. Common examples: `firefox`, `chrome`, `folder`, `text-editor`
|
|
||||||
|
|
||||||
**3. No Icon** - Omit the `icon` field entirely:
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
name: "😀 Grinning Face",
|
|
||||||
// No icon field - emoji/unicode in name displays without icon area
|
|
||||||
comment: "Copy emoji to clipboard",
|
|
||||||
action: "copy:😀",
|
|
||||||
categories: ["MyPlugin"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
When `icon` is omitted, the launcher hides the icon area and displays only the text, giving full width to the item name. Perfect for emoji pickers or text-only items.
|
|
||||||
|
|
||||||
### Trigger System
|
### Trigger System
|
||||||
|
|
||||||
Triggers control when your plugin's items appear in the launcher:
|
Triggers control when your plugin's items appear in the launcher:
|
||||||
|
|||||||
49
README.md
49
README.md
@@ -258,7 +258,7 @@ There are a lot of possible configurations that you can enable/disable in the fl
|
|||||||
|
|
||||||
#### Other Distributions - via manual installation
|
#### Other Distributions - via manual installation
|
||||||
|
|
||||||
#### 1. Install Quickshell (Varies by Distribution)
|
**1. Install Quickshell (Varies by Distribution)**
|
||||||
```bash
|
```bash
|
||||||
# Arch
|
# Arch
|
||||||
paru -S quickshell-git
|
paru -S quickshell-git
|
||||||
@@ -267,61 +267,43 @@ sudo dnf copr enable avengemedia/danklinux && sudo dnf install quickshell-git
|
|||||||
# ! TODO - document other distros
|
# ! TODO - document other distros
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Install fonts
|
**2. Install fonts**
|
||||||
*Inter Variable* and *Fira Code* are not strictly required, but they are the default fonts of dms.
|
*Inter Variable* and *Fira Code* are not strictly required, but they are the default fonts of dms.
|
||||||
|
|
||||||
#### 2.1 Install Material Symbols
|
**2.1 Install Material Symbols**
|
||||||
```bash
|
```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
|
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
|
**2.2 Install Inter Variable**
|
||||||
```bash
|
```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
|
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)
|
**2.3 Install Fira Code (monospace font)**
|
||||||
```bash
|
```bash
|
||||||
sudo curl -L "https://github.com/tonsky/FiraCode/releases/latest/download/FiraCode-Regular.ttf" -o /usr/share/fonts/FiraCode-Regular.ttf
|
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
|
**2.4 Refresh font cache**
|
||||||
```bash
|
```bash
|
||||||
fc-cache -fv
|
fc-cache -fv
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. Install the shell
|
**3. Install the shell**
|
||||||
|
|
||||||
#### 3.1. Clone latest QML
|
|
||||||
|
|
||||||
|
**3.1. Clone latest master**
|
||||||
```bash
|
```bash
|
||||||
mkdir ~/.config/quickshell && git clone https://github.com/AvengeMedia/DankMaterialShell.git ~/.config/quickshell/dms
|
mkdir ~/.config/quickshell && git clone https://github.com/AvengeMedia/DankMaterialShell.git ~/.config/quickshell/dms
|
||||||
```
|
```
|
||||||
|
|
||||||
**FOR Stable Version, Checkout the latest tag**
|
**3.2. Install latest dms CLI**
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/.config/quickshell/dms
|
|
||||||
# You'll have to re-run this, to update to the latest version.
|
|
||||||
git checkout $(git describe --tags --abbrev=0)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.2. Install latest dms CLI
|
|
||||||
|
|
||||||
```bash
|
```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"
|
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"
|
||||||
```
|
```
|
||||||
**Note:** this is the latest *stable* dms CLI. If you are using QML/master (not pinned to a tag), then you may periodically be missing features, etc.
|
|
||||||
|
|
||||||
If preferred, you can build the dms-cli yourself (requires GO 1.24+)
|
**4. Optional Features (system monitoring, clipboard history, brightness controls, etc.)**
|
||||||
|
|
||||||
```bash
|
**4.1 Core optional dependencies**
|
||||||
git clone https://github.com/AvengeMedia/danklinux.git && cd danklinux
|
|
||||||
make && sudo make install
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 4. Optional Features (system monitoring, clipboard history, brightness controls, etc.)
|
|
||||||
|
|
||||||
#### 4.1 Core optional dependencies
|
|
||||||
```bash
|
```bash
|
||||||
# Arch Linux
|
# Arch Linux
|
||||||
sudo pacman -S cava wl-clipboard cliphist brightnessctl qt6-multimedia
|
sudo pacman -S cava wl-clipboard cliphist brightnessctl qt6-multimedia
|
||||||
@@ -335,7 +317,7 @@ Note: by enabling and installing the avengemedia/dms copr above, these core depe
|
|||||||
|
|
||||||
*Other distros will just need to find sources for the above packages*
|
*Other distros will just need to find sources for the above packages*
|
||||||
|
|
||||||
#### 4.2 - dgop manual installation
|
**4.2 - dgop manual installation**
|
||||||
|
|
||||||
`dgop` is available via AUR and a nix flake, other distributions can install it manually.
|
`dgop` is available via AUR and a nix flake, other distributions can install it manually.
|
||||||
|
|
||||||
@@ -411,6 +393,9 @@ binds {
|
|||||||
Mod+C hotkey-overlay-title="Control Center" {
|
Mod+C hotkey-overlay-title="Control Center" {
|
||||||
spawn "dms" "ipc" "call" "control-center" "toggle";
|
spawn "dms" "ipc" "call" "control-center" "toggle";
|
||||||
}
|
}
|
||||||
|
Mod+Y hotkey-overlay-title="Browse Wallpapers" {
|
||||||
|
spawn "dms" "ipc" "call" "dankdash" "wallpaper";
|
||||||
|
}
|
||||||
XF86AudioRaiseVolume allow-when-locked=true {
|
XF86AudioRaiseVolume allow-when-locked=true {
|
||||||
spawn "dms" "ipc" "call" "audio" "increment" "3";
|
spawn "dms" "ipc" "call" "audio" "increment" "3";
|
||||||
}
|
}
|
||||||
@@ -478,8 +463,8 @@ bind = SUPER, comma, exec, dms ipc call settings toggle
|
|||||||
bind = SUPER, P, exec, dms ipc call notepad toggle
|
bind = SUPER, P, exec, dms ipc call notepad toggle
|
||||||
bind = SUPERALT, L, exec, dms ipc call lock lock
|
bind = SUPERALT, L, exec, dms ipc call lock lock
|
||||||
bind = SUPER, X, exec, dms ipc call powermenu toggle
|
bind = SUPER, X, exec, dms ipc call powermenu toggle
|
||||||
bind = SUPER, C, exec, dms ipc call control-center toggle
|
bind = SUPER, C, exec, dms ipc call control-center toggle
|
||||||
bind = SUPER, TAB, exec, dms ipc call hypr toggleOverview
|
bind = SUPER, Y, exec, dms ipc call dankdash wallpaper
|
||||||
|
|
||||||
# Audio controls (function keys)
|
# Audio controls (function keys)
|
||||||
bindl = , XF86AudioRaiseVolume, exec, dms ipc call audio increment 3
|
bindl = , XF86AudioRaiseVolume, exec, dms ipc call audio increment 3
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -16,300 +17,67 @@ Singleton {
|
|||||||
|
|
||||||
readonly property string hyprlandSignature: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
|
readonly property string hyprlandSignature: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
|
||||||
readonly property string niriSocket: Quickshell.env("NIRI_SOCKET")
|
readonly property string niriSocket: Quickshell.env("NIRI_SOCKET")
|
||||||
|
|
||||||
property bool useNiriSorting: isNiri && NiriService
|
property bool useNiriSorting: isNiri && NiriService
|
||||||
|
|
||||||
property var sortedToplevels: sortedToplevelsCache
|
property var sortedToplevels: {
|
||||||
property var sortedToplevelsCache: []
|
if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values) {
|
||||||
|
|
||||||
property bool _sortScheduled: false
|
|
||||||
property bool _refreshScheduled: false
|
|
||||||
property bool _hasRefreshedOnce: false
|
|
||||||
|
|
||||||
property var _coordCache: ({})
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: refreshTimer
|
|
||||||
interval: 40
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
try {
|
|
||||||
Hyprland.refreshToplevels()
|
|
||||||
} catch(e) {}
|
|
||||||
_refreshScheduled = false
|
|
||||||
_hasRefreshedOnce = true
|
|
||||||
scheduleSort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function scheduleSort() {
|
|
||||||
if (_sortScheduled) return
|
|
||||||
_sortScheduled = true
|
|
||||||
Qt.callLater(function() {
|
|
||||||
_sortScheduled = false
|
|
||||||
sortedToplevelsCache = computeSortedToplevels()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function scheduleRefresh() {
|
|
||||||
if (!isHyprland) return
|
|
||||||
if (_refreshScheduled) return
|
|
||||||
_refreshScheduled = true
|
|
||||||
refreshTimer.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: ToplevelManager.toplevels
|
|
||||||
function onValuesChanged() { root.scheduleSort() }
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: Hyprland.toplevels
|
|
||||||
function onValuesChanged() {
|
|
||||||
root._hasRefreshedOnce = false
|
|
||||||
root.scheduleSort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: Hyprland.workspaces
|
|
||||||
function onValuesChanged() { root.scheduleSort() }
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: Hyprland
|
|
||||||
function onFocusedWorkspaceChanged() { root.scheduleSort() }
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
detectCompositor()
|
|
||||||
scheduleSort()
|
|
||||||
Qt.callLater(() => NiriService.generateNiriLayoutConfig())
|
|
||||||
}
|
|
||||||
|
|
||||||
function computeSortedToplevels() {
|
|
||||||
if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values)
|
|
||||||
return []
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
if (useNiriSorting)
|
if (useNiriSorting) {
|
||||||
return NiriService.sortToplevels(ToplevelManager.toplevels.values)
|
return NiriService.sortToplevels(ToplevelManager.toplevels.values)
|
||||||
|
|
||||||
if (isHyprland)
|
|
||||||
return sortHyprlandToplevelsSafe()
|
|
||||||
|
|
||||||
return Array.from(ToplevelManager.toplevels.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
function _get(o, path, fallback) {
|
|
||||||
try {
|
|
||||||
let v = o
|
|
||||||
for (let i = 0; i < path.length; i++) {
|
|
||||||
if (v === null || v === undefined) return fallback
|
|
||||||
v = v[path[i]]
|
|
||||||
}
|
|
||||||
return (v === undefined || v === null) ? fallback : v
|
|
||||||
} catch (e) { return fallback }
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortHyprlandToplevelsSafe() {
|
|
||||||
if (!Hyprland.toplevels || !Hyprland.toplevels.values) return []
|
|
||||||
if (_refreshScheduled) return sortedToplevelsCache
|
|
||||||
|
|
||||||
const items = Array.from(Hyprland.toplevels.values)
|
|
||||||
|
|
||||||
function _get(o, path, fb) {
|
|
||||||
try {
|
|
||||||
let v = o
|
|
||||||
for (let k of path) { if (v == null) return fb; v = v[k] }
|
|
||||||
return (v == null) ? fb : v
|
|
||||||
} catch(e) { return fb }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let snap = []
|
if (isHyprland) {
|
||||||
let missingAnyPosition = false
|
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
||||||
let hasNewWindow = false
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
const t = items[i]
|
|
||||||
if (!t) continue
|
|
||||||
|
|
||||||
const addr = t.address || ""
|
const sortedHyprland = hyprlandToplevels.sort((a, b) => {
|
||||||
const li = t.lastIpcObject || null
|
if (a.monitor && b.monitor) {
|
||||||
|
const monitorCompare = a.monitor.name.localeCompare(b.monitor.name)
|
||||||
|
if (monitorCompare !== 0) {
|
||||||
|
return monitorCompare
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const monName = _get(li, ["monitor"], null) ?? _get(t, ["monitor", "name"], "")
|
if (a.workspace && b.workspace) {
|
||||||
const monX = _get(t, ["monitor", "x"], Number.MAX_SAFE_INTEGER)
|
const workspaceCompare = a.workspace.id - b.workspace.id
|
||||||
const monY = _get(t, ["monitor", "y"], Number.MAX_SAFE_INTEGER)
|
if (workspaceCompare !== 0) {
|
||||||
|
return workspaceCompare
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const wsId = _get(li, ["workspace", "id"], null) ?? _get(t, ["workspace", "id"], Number.MAX_SAFE_INTEGER)
|
if (a.lastIpcObject && b.lastIpcObject && a.lastIpcObject.at && b.lastIpcObject.at) {
|
||||||
|
const aX = a.lastIpcObject.at[0]
|
||||||
|
const bX = b.lastIpcObject.at[0]
|
||||||
|
const aY = a.lastIpcObject.at[1]
|
||||||
|
const bY = b.lastIpcObject.at[1]
|
||||||
|
|
||||||
const at = _get(li, ["at"], null)
|
const xCompare = aX - bX
|
||||||
let atX = (at !== null && at !== undefined && typeof at[0] === "number") ? at[0] : NaN
|
if (Math.abs(xCompare) > 10) {
|
||||||
let atY = (at !== null && at !== undefined && typeof at[1] === "number") ? at[1] : NaN
|
return xCompare
|
||||||
|
}
|
||||||
|
return aY - bY
|
||||||
|
}
|
||||||
|
|
||||||
if (!(atX === atX) || !(atY === atY)) {
|
if (a.lastIpcObject && !b.lastIpcObject) {
|
||||||
const cached = _coordCache[addr]
|
return -1
|
||||||
if (cached) {
|
}
|
||||||
atX = cached.x
|
if (!a.lastIpcObject && b.lastIpcObject) {
|
||||||
atY = cached.y
|
return 1
|
||||||
} else {
|
}
|
||||||
if (addr) hasNewWindow = true
|
|
||||||
missingAnyPosition = true
|
|
||||||
atX = 1e9
|
|
||||||
atY = 1e9
|
|
||||||
}
|
|
||||||
} else if (addr) {
|
|
||||||
_coordCache[addr] = { x: atX, y: atY }
|
|
||||||
}
|
|
||||||
|
|
||||||
const relX = Number.isFinite(monX) ? (atX - monX) : atX
|
if (a.title && b.title) {
|
||||||
const relY = Number.isFinite(monY) ? (atY - monY) : atY
|
return a.title.localeCompare(b.title)
|
||||||
|
}
|
||||||
|
|
||||||
snap.push({
|
return 0
|
||||||
monKey: String(monName),
|
})
|
||||||
monOrderX: Number.isFinite(monX) ? monX : Number.MAX_SAFE_INTEGER,
|
|
||||||
monOrderY: Number.isFinite(monY) ? monY : Number.MAX_SAFE_INTEGER,
|
return sortedHyprland.map(hyprToplevel => hyprToplevel.wayland).filter(wayland => wayland !== null)
|
||||||
wsId: (typeof wsId === "number") ? wsId : Number.MAX_SAFE_INTEGER,
|
|
||||||
x: relX,
|
|
||||||
y: relY,
|
|
||||||
title: t.title || "",
|
|
||||||
address: addr,
|
|
||||||
wayland: t.wayland
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missingAnyPosition && hasNewWindow) {
|
return ToplevelManager.toplevels.values
|
||||||
_hasRefreshedOnce = false
|
|
||||||
scheduleRefresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
const groups = new Map()
|
|
||||||
for (const it of snap) {
|
|
||||||
const key = it.monKey + "::" + it.wsId
|
|
||||||
if (!groups.has(key)) groups.set(key, [])
|
|
||||||
groups.get(key).push(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
let groupList = []
|
|
||||||
for (const [key, arr] of groups) {
|
|
||||||
const repr = arr[0]
|
|
||||||
groupList.push({
|
|
||||||
key,
|
|
||||||
monKey: repr.monKey,
|
|
||||||
monOrderX: repr.monOrderX,
|
|
||||||
monOrderY: repr.monOrderY,
|
|
||||||
wsId: repr.wsId,
|
|
||||||
items: arr
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
groupList.sort((a, b) => {
|
|
||||||
if (a.monOrderX !== b.monOrderX) return a.monOrderX - b.monOrderX
|
|
||||||
if (a.monOrderY !== b.monOrderY) return a.monOrderY - b.monOrderY
|
|
||||||
if (a.monKey !== b.monKey) return a.monKey.localeCompare(b.monKey)
|
|
||||||
if (a.wsId !== b.wsId) return a.wsId - b.wsId
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
|
|
||||||
const COLUMN_THRESHOLD = 48
|
|
||||||
const JITTER_Y = 6
|
|
||||||
|
|
||||||
let ordered = []
|
|
||||||
for (const g of groupList) {
|
|
||||||
const arr = g.items
|
|
||||||
|
|
||||||
const xs = arr.map(it => it.x).filter(x => Number.isFinite(x)).sort((a, b) => a - b)
|
|
||||||
let colCenters = []
|
|
||||||
if (xs.length > 0) {
|
|
||||||
for (const x of xs) {
|
|
||||||
if (colCenters.length === 0) {
|
|
||||||
colCenters.push(x)
|
|
||||||
} else {
|
|
||||||
const last = colCenters[colCenters.length - 1]
|
|
||||||
if (x - last >= COLUMN_THRESHOLD) {
|
|
||||||
colCenters.push(x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
colCenters = [0]
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const it of arr) {
|
|
||||||
let bestCol = 0
|
|
||||||
let bestDist = Number.POSITIVE_INFINITY
|
|
||||||
for (let ci = 0; ci < colCenters.length; ci++) {
|
|
||||||
const d = Math.abs(it.x - colCenters[ci])
|
|
||||||
if (d < bestDist) {
|
|
||||||
bestDist = d
|
|
||||||
bestCol = ci
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it._col = bestCol
|
|
||||||
}
|
|
||||||
|
|
||||||
arr.sort((a, b) => {
|
|
||||||
if (a._col !== b._col) return a._col - b._col
|
|
||||||
|
|
||||||
const dy = a.y - b.y
|
|
||||||
if (Math.abs(dy) > JITTER_Y) return dy
|
|
||||||
|
|
||||||
if (a.title !== b.title) return a.title.localeCompare(b.title)
|
|
||||||
if (a.address !== b.address) return a.address.localeCompare(b.address)
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
|
|
||||||
ordered.push.apply(ordered, arr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ordered.map(x => x.wayland).filter(w => w !== null && w !== undefined)
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterCurrentWorkspace(toplevels, screen) {
|
|
||||||
if (useNiriSorting) return NiriService.filterCurrentWorkspace(toplevels, screen)
|
|
||||||
if (isHyprland) return filterHyprlandCurrentWorkspaceSafe(toplevels, screen)
|
|
||||||
return toplevels
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterHyprlandCurrentWorkspaceSafe(toplevels, screenName) {
|
|
||||||
if (!toplevels || toplevels.length === 0 || !Hyprland.toplevels) return toplevels
|
|
||||||
|
|
||||||
let currentWorkspaceId = null
|
|
||||||
try {
|
|
||||||
const hy = Array.from(Hyprland.toplevels.values)
|
|
||||||
for (const t of hy) {
|
|
||||||
const mon = _get(t, ["monitor", "name"], "")
|
|
||||||
const wsId = _get(t, ["workspace", "id"], null)
|
|
||||||
const active = !!_get(t, ["activated"], false)
|
|
||||||
if (mon === screenName && wsId !== null) {
|
|
||||||
if (active) { currentWorkspaceId = wsId; break }
|
|
||||||
if (currentWorkspaceId === null) currentWorkspaceId = wsId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentWorkspaceId === null && Hyprland.workspaces) {
|
|
||||||
const wss = Array.from(Hyprland.workspaces.values)
|
|
||||||
const focusedId = _get(Hyprland, ["focusedWorkspace", "id"], null)
|
|
||||||
for (const ws of wss) {
|
|
||||||
const monName = _get(ws, ["monitor"], "")
|
|
||||||
const wsId = _get(ws, ["id"], null)
|
|
||||||
if (monName === screenName && wsId !== null) {
|
|
||||||
if (focusedId !== null && wsId === focusedId) { currentWorkspaceId = wsId; break }
|
|
||||||
if (currentWorkspaceId === null) currentWorkspaceId = wsId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("CompositorService: workspace snapshot failed:", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentWorkspaceId === null) return toplevels
|
|
||||||
|
|
||||||
// Map wayland → wsId snapshot
|
|
||||||
let map = new Map()
|
|
||||||
try {
|
|
||||||
const hy = Array.from(Hyprland.toplevels.values)
|
|
||||||
for (const t of hy) {
|
|
||||||
const wsId = _get(t, ["workspace", "id"], null)
|
|
||||||
if (t && t.wayland && wsId !== null) map.set(t.wayland, wsId)
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
return toplevels.filter(w => map.get(w) === currentWorkspaceId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
@@ -323,30 +91,86 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterCurrentWorkspace(toplevels, screen) {
|
||||||
|
if (useNiriSorting) {
|
||||||
|
return NiriService.filterCurrentWorkspace(toplevels, screen)
|
||||||
|
}
|
||||||
|
if (isHyprland) {
|
||||||
|
return filterHyprlandCurrentWorkspace(toplevels, screen)
|
||||||
|
}
|
||||||
|
return toplevels
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterHyprlandCurrentWorkspace(toplevels, screenName) {
|
||||||
|
if (!toplevels || toplevels.length === 0 || !Hyprland.toplevels) {
|
||||||
|
return toplevels
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentWorkspaceId = null
|
||||||
|
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
||||||
|
|
||||||
|
for (const hyprToplevel of hyprlandToplevels) {
|
||||||
|
if (hyprToplevel.monitor && hyprToplevel.monitor.name === screenName && hyprToplevel.workspace) {
|
||||||
|
if (hyprToplevel.activated) {
|
||||||
|
currentWorkspaceId = hyprToplevel.workspace.id
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (currentWorkspaceId === null) {
|
||||||
|
currentWorkspaceId = hyprToplevel.workspace.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentWorkspaceId === null && Hyprland.workspaces) {
|
||||||
|
const workspaces = Array.from(Hyprland.workspaces.values)
|
||||||
|
for (const workspace of workspaces) {
|
||||||
|
if (workspace.monitor && workspace.monitor === screenName) {
|
||||||
|
if (Hyprland.focusedWorkspace && workspace.id === Hyprland.focusedWorkspace.id) {
|
||||||
|
currentWorkspaceId = workspace.id
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (currentWorkspaceId === null) {
|
||||||
|
currentWorkspaceId = workspace.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentWorkspaceId === null) {
|
||||||
|
return toplevels
|
||||||
|
}
|
||||||
|
|
||||||
|
return toplevels.filter(toplevel => {
|
||||||
|
for (const hyprToplevel of hyprlandToplevels) {
|
||||||
|
if (hyprToplevel.wayland === toplevel) {
|
||||||
|
return hyprToplevel.workspace && hyprToplevel.workspace.id === currentWorkspaceId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function detectCompositor() {
|
function detectCompositor() {
|
||||||
if (hyprlandSignature && hyprlandSignature.length > 0) {
|
if (hyprlandSignature && hyprlandSignature.length > 0) {
|
||||||
isHyprland = true
|
isHyprland = true
|
||||||
isNiri = false
|
isNiri = false
|
||||||
compositor = "hyprland"
|
compositor = "hyprland"
|
||||||
console.log("CompositorService: Detected Hyprland")
|
console.log("CompositorService: Detected Hyprland")
|
||||||
try {
|
|
||||||
Hyprland.refreshToplevels()
|
|
||||||
} catch(e) {}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (niriSocket && niriSocket.length > 0) {
|
if (niriSocket && niriSocket.length > 0) {
|
||||||
Proc.runCommand("niriSocketCheck", ["test", "-S", niriSocket], (output, exitCode) => {
|
Proc.runCommand("niriSocketCheck", ["test", "-S", root.niriSocket], (output, exitCode) => {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
isNiri = true
|
root.isNiri = true
|
||||||
isHyprland = false
|
root.isHyprland = false
|
||||||
compositor = "niri"
|
root.compositor = "niri"
|
||||||
console.log("CompositorService: Detected Niri with socket:", niriSocket)
|
console.log("CompositorService: Detected Niri with socket:", root.niriSocket)
|
||||||
NiriService.generateNiriBinds()
|
NiriService.generateNiriBinds()
|
||||||
} else {
|
} else {
|
||||||
isHyprland = false
|
root.isHyprland = false
|
||||||
isNiri = true
|
root.isNiri = true
|
||||||
compositor = "niri"
|
root.compositor = "niri"
|
||||||
console.warn("CompositorService: Niri socket check failed, defaulting to Niri anyway")
|
console.warn("CompositorService: Niri socket check failed, defaulting to Niri anyway")
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0)
|
||||||
@@ -359,14 +183,22 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function powerOffMonitors() {
|
function powerOffMonitors() {
|
||||||
if (isNiri) return NiriService.powerOffMonitors()
|
if (isNiri) {
|
||||||
if (isHyprland) return Hyprland.dispatch("dpms off")
|
return NiriService.powerOffMonitors()
|
||||||
|
}
|
||||||
|
if (isHyprland) {
|
||||||
|
return Hyprland.dispatch("dpms off")
|
||||||
|
}
|
||||||
console.warn("CompositorService: Cannot power off monitors, unknown compositor")
|
console.warn("CompositorService: Cannot power off monitors, unknown compositor")
|
||||||
}
|
}
|
||||||
|
|
||||||
function powerOnMonitors() {
|
function powerOnMonitors() {
|
||||||
if (isNiri) return NiriService.powerOnMonitors()
|
if (isNiri) {
|
||||||
if (isHyprland) return Hyprland.dispatch("dpms on")
|
return NiriService.powerOnMonitors()
|
||||||
|
}
|
||||||
|
if (isHyprland) {
|
||||||
|
return Hyprland.dispatch("dpms on")
|
||||||
|
}
|
||||||
console.warn("CompositorService: Cannot power on monitors, unknown compositor")
|
console.warn("CompositorService: Cannot power on monitors, unknown compositor")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,6 @@ Singleton {
|
|||||||
signal loginctlStateUpdate(var data)
|
signal loginctlStateUpdate(var data)
|
||||||
signal loginctlEvent(var event)
|
signal loginctlEvent(var event)
|
||||||
signal capabilitiesReceived()
|
signal capabilitiesReceived()
|
||||||
signal credentialsRequest(var data)
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (socketPath && socketPath.length > 0) {
|
if (socketPath && socketPath.length > 0) {
|
||||||
@@ -262,8 +261,6 @@ Singleton {
|
|||||||
capabilitiesReceived()
|
capabilitiesReceived()
|
||||||
} else if (service === "network") {
|
} else if (service === "network") {
|
||||||
networkStateUpdate(data)
|
networkStateUpdate(data)
|
||||||
} else if (service === "network.credentials") {
|
|
||||||
credentialsRequest(data)
|
|
||||||
} else if (service === "loginctl") {
|
} else if (service === "loginctl") {
|
||||||
if (data.event) {
|
if (data.event) {
|
||||||
loginctlEvent(data)
|
loginctlEvent(data)
|
||||||
|
|||||||
@@ -79,21 +79,8 @@ Singleton {
|
|||||||
property int refCount: 0
|
property int refCount: 0
|
||||||
property bool stateInitialized: false
|
property bool stateInitialized: false
|
||||||
|
|
||||||
property string credentialsToken: ""
|
|
||||||
property string credentialsSSID: ""
|
|
||||||
property string credentialsSetting: ""
|
|
||||||
property var credentialsFields: []
|
|
||||||
property var credentialsHints: []
|
|
||||||
property string credentialsReason: ""
|
|
||||||
property bool credentialsRequested: false
|
|
||||||
|
|
||||||
property string pendingConnectionSSID: ""
|
|
||||||
property var pendingConnectionStartTime: 0
|
|
||||||
property bool wasConnecting: false
|
|
||||||
|
|
||||||
signal networksUpdated
|
signal networksUpdated
|
||||||
signal connectionChanged
|
signal connectionChanged
|
||||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason)
|
|
||||||
|
|
||||||
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
||||||
|
|
||||||
@@ -133,10 +120,6 @@ Singleton {
|
|||||||
function onCapabilitiesChanged() {
|
function onCapabilitiesChanged() {
|
||||||
checkDMSCapabilities()
|
checkDMSCapabilities()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCredentialsRequest(data) {
|
|
||||||
handleCredentialsRequest(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDMSCapabilities() {
|
function checkDMSCapabilities() {
|
||||||
@@ -160,18 +143,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCredentialsRequest(data) {
|
|
||||||
credentialsToken = data.token || ""
|
|
||||||
credentialsSSID = data.ssid || ""
|
|
||||||
credentialsSetting = data.setting || "802-11-wireless-security"
|
|
||||||
credentialsFields = data.fields || ["psk"]
|
|
||||||
credentialsHints = data.hints || []
|
|
||||||
credentialsReason = data.reason || "Credentials required"
|
|
||||||
credentialsRequested = true
|
|
||||||
|
|
||||||
credentialsNeeded(credentialsToken, credentialsSSID, credentialsSetting, credentialsFields, credentialsHints, credentialsReason)
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRef() {
|
function addRef() {
|
||||||
refCount++
|
refCount++
|
||||||
if (refCount === 1 && networkAvailable) {
|
if (refCount === 1 && networkAvailable) {
|
||||||
@@ -206,9 +177,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateState(state) {
|
function updateState(state) {
|
||||||
const previousConnecting = isConnecting
|
|
||||||
const previousConnectingSSID = connectingSSID
|
|
||||||
|
|
||||||
networkStatus = state.networkStatus || "disconnected"
|
networkStatus = state.networkStatus || "disconnected"
|
||||||
primaryConnection = state.primaryConnection || ""
|
primaryConnection = state.primaryConnection || ""
|
||||||
|
|
||||||
@@ -257,45 +225,6 @@ Singleton {
|
|||||||
connectionError = state.lastError || ""
|
connectionError = state.lastError || ""
|
||||||
lastConnectionError = state.lastError || ""
|
lastConnectionError = state.lastError || ""
|
||||||
|
|
||||||
if (pendingConnectionSSID) {
|
|
||||||
if (wifiConnected && currentWifiSSID === pendingConnectionSSID && wifiIP) {
|
|
||||||
if (DMSService.verboseLogs) {
|
|
||||||
const elapsed = Date.now() - pendingConnectionStartTime
|
|
||||||
console.log("NetworkManagerService: Successfully connected to", pendingConnectionSSID, "in", elapsed, "ms")
|
|
||||||
}
|
|
||||||
ToastService.showInfo(`Connected to ${pendingConnectionSSID}`)
|
|
||||||
|
|
||||||
if (userPreference === "wifi" || userPreference === "auto") {
|
|
||||||
setConnectionPriority("wifi")
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingConnectionSSID = ""
|
|
||||||
connectionStatus = "connected"
|
|
||||||
} else if (previousConnecting && !isConnecting && !wifiConnected) {
|
|
||||||
const elapsed = Date.now() - pendingConnectionStartTime
|
|
||||||
|
|
||||||
if (elapsed < 5000) {
|
|
||||||
if (DMSService.verboseLogs) {
|
|
||||||
console.log("NetworkManagerService: Quick connection failure, likely authentication error")
|
|
||||||
}
|
|
||||||
connectionStatus = "invalid_password"
|
|
||||||
} else {
|
|
||||||
if (DMSService.verboseLogs) {
|
|
||||||
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) {
|
|
||||||
ToastService.showError(I18n.tr("Failed to connect to ") + pendingConnectionSSID)
|
|
||||||
}
|
|
||||||
connectionStatus = "failed"
|
|
||||||
pendingConnectionSSID = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wasConnecting = isConnecting
|
|
||||||
|
|
||||||
connectionChanged()
|
connectionChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,11 +242,11 @@ Singleton {
|
|||||||
connectionError = response.error
|
connectionError = response.error
|
||||||
lastConnectionError = response.error
|
lastConnectionError = response.error
|
||||||
connectionStatus = "failed"
|
connectionStatus = "failed"
|
||||||
ToastService.showError(I18n.tr("Failed to activate configuration"))
|
ToastService.showError(`Failed to activate configuration`)
|
||||||
} else {
|
} else {
|
||||||
connectionError = ""
|
connectionError = ""
|
||||||
connectionStatus = "connected"
|
connectionStatus = "connected"
|
||||||
ToastService.showInfo(I18n.tr("Configuration activated"))
|
ToastService.showInfo(`Configuration activated`)
|
||||||
}
|
}
|
||||||
|
|
||||||
isConnecting = false
|
isConnecting = false
|
||||||
@@ -351,47 +280,42 @@ Singleton {
|
|||||||
function connectToWifi(ssid, password = "", username = "", anonymousIdentity = "", domainSuffixMatch = "") {
|
function connectToWifi(ssid, password = "", username = "", anonymousIdentity = "", domainSuffixMatch = "") {
|
||||||
if (!networkAvailable || isConnecting) return
|
if (!networkAvailable || isConnecting) return
|
||||||
|
|
||||||
pendingConnectionSSID = ssid
|
connectingSSID = ssid
|
||||||
pendingConnectionStartTime = Date.now()
|
|
||||||
connectionError = ""
|
connectionError = ""
|
||||||
connectionStatus = "connecting"
|
connectionStatus = "connecting"
|
||||||
credentialsRequested = false
|
|
||||||
|
|
||||||
const params = { ssid: ssid }
|
const params = { ssid: ssid }
|
||||||
|
if (password) params.password = password
|
||||||
if (DMSService.apiVersion >= 7) {
|
if (username) params.username = username
|
||||||
if (password || username) {
|
if (anonymousIdentity) params.anonymousIdentity = anonymousIdentity
|
||||||
params.password = password
|
if (domainSuffixMatch) params.domainSuffixMatch = domainSuffixMatch
|
||||||
if (username) params.username = username
|
|
||||||
if (anonymousIdentity) params.anonymousIdentity = anonymousIdentity
|
|
||||||
if (domainSuffixMatch) params.domainSuffixMatch = domainSuffixMatch
|
|
||||||
params.interactive = false
|
|
||||||
} else {
|
|
||||||
params.interactive = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (password) params.password = password
|
|
||||||
if (username) params.username = username
|
|
||||||
if (anonymousIdentity) params.anonymousIdentity = anonymousIdentity
|
|
||||||
if (domainSuffixMatch) params.domainSuffixMatch = domainSuffixMatch
|
|
||||||
}
|
|
||||||
|
|
||||||
DMSService.sendRequest("network.wifi.connect", params, response => {
|
DMSService.sendRequest("network.wifi.connect", params, response => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
if (DMSService.verboseLogs) {
|
|
||||||
console.log("NetworkManagerService: Connection request failed:", response.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
connectionError = response.error
|
connectionError = response.error
|
||||||
lastConnectionError = response.error
|
lastConnectionError = response.error
|
||||||
pendingConnectionSSID = ""
|
connectionStatus = response.error.includes("password") || response.error.includes("authentication")
|
||||||
connectionStatus = "failed"
|
? "invalid_password"
|
||||||
ToastService.showError(I18n.tr("Failed to start connection to ") + ssid)
|
: "failed"
|
||||||
|
|
||||||
|
if (connectionStatus === "invalid_password") {
|
||||||
|
passwordDialogShouldReopen = true
|
||||||
|
ToastService.showError(`Invalid password for ${ssid}`)
|
||||||
|
} else {
|
||||||
|
ToastService.showError(`Failed to connect to ${ssid}`)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (DMSService.verboseLogs) {
|
connectionError = ""
|
||||||
console.log("NetworkManagerService: Connection request sent for", ssid)
|
connectionStatus = "connected"
|
||||||
|
ToastService.showInfo(`Connected to ${ssid}`)
|
||||||
|
|
||||||
|
if (userPreference === "wifi" || userPreference === "auto") {
|
||||||
|
setConnectionPriority("wifi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isConnecting = false
|
||||||
|
connectingSSID = ""
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,60 +324,15 @@ Singleton {
|
|||||||
|
|
||||||
DMSService.sendRequest("network.wifi.disconnect", null, response => {
|
DMSService.sendRequest("network.wifi.disconnect", null, response => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
ToastService.showError(I18n.tr("Failed to disconnect WiFi"))
|
ToastService.showError("Failed to disconnect WiFi")
|
||||||
} else {
|
} else {
|
||||||
ToastService.showInfo(I18n.tr("Disconnected from WiFi"))
|
ToastService.showInfo("Disconnected from WiFi")
|
||||||
currentWifiSSID = ""
|
currentWifiSSID = ""
|
||||||
connectionStatus = ""
|
connectionStatus = ""
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitCredentials(token, secrets, save) {
|
|
||||||
if (!networkAvailable || DMSService.apiVersion < 7) return
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
token: token,
|
|
||||||
secrets: secrets,
|
|
||||||
save: save || false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DMSService.verboseLogs) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelCredentials(token) {
|
|
||||||
if (!networkAvailable || DMSService.apiVersion < 7) return
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
token: token,
|
|
||||||
cancel: true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DMSService.verboseLogs) {
|
|
||||||
console.log("NetworkManagerService: Cancelling credentials for token", token)
|
|
||||||
}
|
|
||||||
|
|
||||||
credentialsRequested = false
|
|
||||||
pendingConnectionSSID = ""
|
|
||||||
connectionStatus = "cancelled"
|
|
||||||
|
|
||||||
DMSService.sendRequest("network.credentials.submit", params, response => {
|
|
||||||
if (response.error) {
|
|
||||||
console.warn("NetworkManagerService: Failed to cancel credentials:", response.error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function forgetWifiNetwork(ssid) {
|
function forgetWifiNetwork(ssid) {
|
||||||
if (!networkAvailable) return
|
if (!networkAvailable) return
|
||||||
|
|
||||||
@@ -462,7 +341,7 @@ Singleton {
|
|||||||
if (response.error) {
|
if (response.error) {
|
||||||
console.warn("Failed to forget network:", response.error)
|
console.warn("Failed to forget network:", response.error)
|
||||||
} else {
|
} else {
|
||||||
ToastService.showInfo(I18n.tr("Forgot network ") + ssid)
|
ToastService.showInfo(`Forgot network ${ssid}`)
|
||||||
|
|
||||||
savedConnections = savedConnections.filter(s => s.ssid !== ssid)
|
savedConnections = savedConnections.filter(s => s.ssid !== ssid)
|
||||||
savedWifiNetworks = savedWifiNetworks.filter(s => s.ssid !== ssid)
|
savedWifiNetworks = savedWifiNetworks.filter(s => s.ssid !== ssid)
|
||||||
@@ -495,7 +374,7 @@ Singleton {
|
|||||||
console.warn("Failed to toggle WiFi:", response.error)
|
console.warn("Failed to toggle WiFi:", response.error)
|
||||||
} else if (response.result) {
|
} else if (response.result) {
|
||||||
wifiEnabled = response.result.enabled
|
wifiEnabled = response.result.enabled
|
||||||
ToastService.showInfo(wifiEnabled ? I18n.tr("WiFi enabled") : I18n.tr("WiFi disabled"))
|
ToastService.showInfo(wifiEnabled ? "WiFi enabled" : "WiFi disabled")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -505,9 +384,9 @@ Singleton {
|
|||||||
|
|
||||||
DMSService.sendRequest("network.wifi.enable", null, response => {
|
DMSService.sendRequest("network.wifi.enable", null, response => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
ToastService.showError(I18n.tr("Failed to enable WiFi"))
|
ToastService.showError("Failed to enable WiFi")
|
||||||
} else {
|
} else {
|
||||||
ToastService.showInfo(I18n.tr("WiFi enabled"))
|
ToastService.showInfo("WiFi enabled")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,17 +69,8 @@ Singleton {
|
|||||||
|
|
||||||
property bool subscriptionConnected: activeService?.subscriptionConnected ?? false
|
property bool subscriptionConnected: activeService?.subscriptionConnected ?? false
|
||||||
|
|
||||||
property string credentialsToken: activeService?.credentialsToken ?? ""
|
|
||||||
property string credentialsSSID: activeService?.credentialsSSID ?? ""
|
|
||||||
property string credentialsSetting: activeService?.credentialsSetting ?? ""
|
|
||||||
property var credentialsFields: activeService?.credentialsFields ?? []
|
|
||||||
property var credentialsHints: activeService?.credentialsHints ?? []
|
|
||||||
property string credentialsReason: activeService?.credentialsReason ?? ""
|
|
||||||
property bool credentialsRequested: activeService?.credentialsRequested ?? false
|
|
||||||
|
|
||||||
signal networksUpdated
|
signal networksUpdated
|
||||||
signal connectionChanged
|
signal connectionChanged
|
||||||
signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason)
|
|
||||||
|
|
||||||
property bool usingLegacy: false
|
property bool usingLegacy: false
|
||||||
property var activeService: null
|
property var activeService: null
|
||||||
@@ -131,9 +122,6 @@ Singleton {
|
|||||||
if (activeService.connectionChanged) {
|
if (activeService.connectionChanged) {
|
||||||
activeService.connectionChanged.connect(root.connectionChanged)
|
activeService.connectionChanged.connect(root.connectionChanged)
|
||||||
}
|
}
|
||||||
if (activeService.credentialsNeeded) {
|
|
||||||
activeService.credentialsNeeded.connect(root.credentialsNeeded)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,16 +258,4 @@ Singleton {
|
|||||||
activeService.connectToSpecificWiredConfig(uuid)
|
activeService.connectToSpecificWiredConfig(uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitCredentials(token, secrets, save) {
|
|
||||||
if (activeService && activeService.submitCredentials) {
|
|
||||||
activeService.submitCredentials(token, secrets, save)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelCredentials(token) {
|
|
||||||
if (activeService && activeService.cancelCredentials) {
|
|
||||||
activeService.cancelCredentials(token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ binds {
|
|||||||
spawn "dms" "ipc" "call" "notifications" "toggle";
|
spawn "dms" "ipc" "call" "notifications" "toggle";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mod+Y hotkey-overlay-title="Browse Wallpapers" {
|
||||||
|
spawn "dms" "ipc" "call" "dankdash" "wallpaper";
|
||||||
|
}
|
||||||
|
|
||||||
Mod+Shift+N hotkey-overlay-title="Notepad" {
|
Mod+Shift+N hotkey-overlay-title="Notepad" {
|
||||||
spawn "dms" "ipc" "call" "notepad" "toggle";
|
spawn "dms" "ipc" "call" "notepad" "toggle";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ PanelWindow {
|
|||||||
property list<real> animationEnterCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
property list<real> animationEnterCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
||||||
property list<real> animationExitCurve: Theme.expressiveCurves.emphasized
|
property list<real> animationExitCurve: Theme.expressiveCurves.emphasized
|
||||||
property bool shouldBeVisible: false
|
property bool shouldBeVisible: false
|
||||||
|
property int keyboardFocusMode: WlrKeyboardFocus.OnDemand
|
||||||
|
|
||||||
signal opened
|
signal opened
|
||||||
signal popoutClosed
|
signal popoutClosed
|
||||||
@@ -63,7 +64,7 @@ PanelWindow {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
WlrLayershell.layer: WlrLayershell.Top
|
WlrLayershell.layer: WlrLayershell.Top
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: shouldBeVisible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: shouldBeVisible ? keyboardFocusMode : WlrKeyboardFocus.None
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import QtQuick
|
|||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Item {
|
FocusScope {
|
||||||
id: tabBar
|
id: tabBar
|
||||||
|
|
||||||
property alias model: tabRepeater.model
|
property alias model: tabRepeater.model
|
||||||
@@ -11,12 +11,101 @@ Item {
|
|||||||
property int tabHeight: 56
|
property int tabHeight: 56
|
||||||
property bool showIcons: true
|
property bool showIcons: true
|
||||||
property bool equalWidthTabs: true
|
property bool equalWidthTabs: true
|
||||||
|
property bool enableArrowNavigation: true
|
||||||
|
property Item nextFocusTarget: null
|
||||||
|
property Item previousFocusTarget: null
|
||||||
|
|
||||||
signal tabClicked(int index)
|
signal tabClicked(int index)
|
||||||
signal actionTriggered(int index)
|
signal actionTriggered(int index)
|
||||||
|
|
||||||
|
focus: false
|
||||||
|
activeFocusOnTab: true
|
||||||
height: tabHeight
|
height: tabHeight
|
||||||
|
|
||||||
|
KeyNavigation.tab: nextFocusTarget
|
||||||
|
KeyNavigation.down: nextFocusTarget
|
||||||
|
KeyNavigation.backtab: previousFocusTarget
|
||||||
|
KeyNavigation.up: previousFocusTarget
|
||||||
|
|
||||||
|
Keys.onPressed: (event) => {
|
||||||
|
if (!tabBar.activeFocus || tabRepeater.count === 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
function findSelectableIndex(startIndex, step) {
|
||||||
|
let idx = startIndex
|
||||||
|
for (let i = 0; i < tabRepeater.count; i++) {
|
||||||
|
idx = (idx + step + tabRepeater.count) % tabRepeater.count
|
||||||
|
const item = tabRepeater.itemAt(idx)
|
||||||
|
if (item && !item.isAction)
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToIndex = (nextIndex) => {
|
||||||
|
if (nextIndex >= 0 && nextIndex !== tabBar.currentIndex) {
|
||||||
|
tabBar.currentIndex = nextIndex
|
||||||
|
tabBar.tabClicked(nextIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveTarget = (item) => {
|
||||||
|
if (!item)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (item.focusTarget)
|
||||||
|
return resolveTarget(item.focusTarget)
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
const focusItem = (item) => {
|
||||||
|
const target = resolveTarget(item)
|
||||||
|
if (!target)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (target.requestFocus) {
|
||||||
|
Qt.callLater(() => target.requestFocus())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.forceActiveFocus) {
|
||||||
|
Qt.callLater(() => target.forceActiveFocus())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_Right && tabBar.enableArrowNavigation) {
|
||||||
|
const baseIndex = (tabBar.currentIndex >= 0 && tabBar.currentIndex < tabRepeater.count) ? tabBar.currentIndex : -1
|
||||||
|
const nextIndex = findSelectableIndex(baseIndex, 1)
|
||||||
|
if (nextIndex >= 0) {
|
||||||
|
goToIndex(nextIndex)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
} else if (event.key === Qt.Key_Left && tabBar.enableArrowNavigation) {
|
||||||
|
const baseIndex = (tabBar.currentIndex >= 0 && tabBar.currentIndex < tabRepeater.count) ? tabBar.currentIndex : 0
|
||||||
|
const nextIndex = findSelectableIndex(baseIndex, -1)
|
||||||
|
if (nextIndex >= 0) {
|
||||||
|
goToIndex(nextIndex)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
} else if (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier)) {
|
||||||
|
if (focusItem(tabBar.previousFocusTarget)) {
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
} else if (event.key === Qt.Key_Tab || event.key === Qt.Key_Down) {
|
||||||
|
if (focusItem(tabBar.nextFocusTarget)) {
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
} else if (event.key === Qt.Key_Up) {
|
||||||
|
if (focusItem(tabBar.previousFocusTarget)) {
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: tabRow
|
id: tabRow
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -77,7 +166,6 @@ Item {
|
|||||||
if (tabItem.isAction) {
|
if (tabItem.isAction) {
|
||||||
tabBar.actionTriggered(index)
|
tabBar.actionTriggered(index)
|
||||||
} else {
|
} else {
|
||||||
tabBar.currentIndex = index
|
|
||||||
tabBar.tabClicked(index)
|
tabBar.tabClicked(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
docs/IPC.md
38
docs/IPC.md
@@ -501,6 +501,13 @@ Dashboard popup control with tab selection for overview, media, and weather info
|
|||||||
- Parameters: `tab` - Optional tab to open when showing: "" (default), "overview", "media", or "weather"
|
- Parameters: `tab` - Optional tab to open when showing: "" (default), "overview", "media", or "weather"
|
||||||
- Returns: Success/failure message
|
- Returns: Success/failure message
|
||||||
|
|
||||||
|
### Target: `dankdash`
|
||||||
|
DankDash wallpaper browser control.
|
||||||
|
|
||||||
|
**Functions:**
|
||||||
|
- `wallpaper` - Toggle DankDash popup on focused screen with wallpaper tab selected
|
||||||
|
- Returns: Success/failure message
|
||||||
|
|
||||||
### Target: `file`
|
### Target: `file`
|
||||||
File browser controls for selecting wallpapers and profile images.
|
File browser controls for selecting wallpapers and profile images.
|
||||||
|
|
||||||
@@ -512,7 +519,7 @@ File browser controls for selecting wallpapers and profile images.
|
|||||||
- Both browsers support common image formats (jpg, jpeg, png, bmp, gif, webp)
|
- Both browsers support common image formats (jpg, jpeg, png, bmp, gif, webp)
|
||||||
|
|
||||||
### Target: `hypr`
|
### Target: `hypr`
|
||||||
Hyprland-specific controls including keybinds cheatsheet and workspace overview (Hyprland only).
|
Hyprland keybinds cheatsheet modal control (Hyprland only).
|
||||||
|
|
||||||
**Functions:**
|
**Functions:**
|
||||||
- `openBinds` - Show Hyprland keybinds cheatsheet modal
|
- `openBinds` - Show Hyprland keybinds cheatsheet modal
|
||||||
@@ -524,31 +531,13 @@ Hyprland-specific controls including keybinds cheatsheet and workspace overview
|
|||||||
- `toggleBinds` - Toggle Hyprland keybinds cheatsheet modal visibility
|
- `toggleBinds` - Toggle Hyprland keybinds cheatsheet modal visibility
|
||||||
- Returns: Success/failure message
|
- Returns: Success/failure message
|
||||||
- Note: Returns "HYPR_NOT_AVAILABLE" if not running Hyprland
|
- Note: Returns "HYPR_NOT_AVAILABLE" if not running Hyprland
|
||||||
- `openOverview` - Show Hyprland workspace overview
|
|
||||||
- Returns: "OVERVIEW_OPEN_SUCCESS" or "HYPR_NOT_AVAILABLE"
|
|
||||||
- Displays all workspaces across all monitors with live window previews
|
|
||||||
- Allows drag-and-drop window movement between workspaces and monitors
|
|
||||||
- `closeOverview` - Hide Hyprland workspace overview
|
|
||||||
- Returns: "OVERVIEW_CLOSE_SUCCESS" or "HYPR_NOT_AVAILABLE"
|
|
||||||
- `toggleOverview` - Toggle Hyprland workspace overview visibility
|
|
||||||
- Returns: "OVERVIEW_OPEN_SUCCESS", "OVERVIEW_CLOSE_SUCCESS", or "HYPR_NOT_AVAILABLE"
|
|
||||||
|
|
||||||
**Keybinds Cheatsheet Description:**
|
**Description:**
|
||||||
Displays an auto-categorized cheatsheet of all Hyprland keybinds parsed from `~/.config/hypr`. Keybinds are organized into three columns:
|
Displays an auto-categorized cheatsheet of all Hyprland keybinds parsed from `~/.config/hypr`. Keybinds are organized into three columns:
|
||||||
- **Window / Monitor** - Window and monitor management keybinds (sorted by dispatcher)
|
- **Window / Monitor** - Window and monitor management keybinds (sorted by dispatcher)
|
||||||
- **Workspace** - Workspace switching and management (sorted by dispatcher)
|
- **Workspace** - Workspace switching and management (sorted by dispatcher)
|
||||||
- **Execute** - Application launchers and commands (sorted by keybind)
|
- **Execute** - Application launchers and commands (sorted by keybind)
|
||||||
|
|
||||||
**Workspace Overview Description:**
|
|
||||||
Displays a live overview of all workspaces across all monitors with window previews:
|
|
||||||
- **Multi-monitor support** - Shows workspaces from all connected monitors with monitor name labels
|
|
||||||
- **Live window previews** - Real-time screen capture of all windows on each workspace
|
|
||||||
- **Drag-and-drop** - Move windows between workspaces and monitors by dragging
|
|
||||||
- **Keyboard navigation** - Use Left/Right arrow keys to switch between workspaces on current monitor
|
|
||||||
- **Visual indicators** - Active workspace highlighted when it contains windows
|
|
||||||
- **Click to switch** - Click any workspace to switch to it
|
|
||||||
- **Click outside or press Escape** - Close the overview
|
|
||||||
|
|
||||||
### Modal Examples
|
### Modal Examples
|
||||||
```bash
|
```bash
|
||||||
# Open application launcher
|
# Open application launcher
|
||||||
@@ -581,6 +570,9 @@ dms ipc call dash open overview
|
|||||||
dms ipc call dash toggle media
|
dms ipc call dash toggle media
|
||||||
dms ipc call dash open weather
|
dms ipc call dash open weather
|
||||||
|
|
||||||
|
# Open wallpaper browser
|
||||||
|
dms ipc call dankdash wallpaper
|
||||||
|
|
||||||
# Open file browsers
|
# Open file browsers
|
||||||
dms ipc call file browse wallpaper
|
dms ipc call file browse wallpaper
|
||||||
dms ipc call file browse profile
|
dms ipc call file browse profile
|
||||||
@@ -588,11 +580,6 @@ dms ipc call file browse profile
|
|||||||
# Show Hyprland keybinds cheatsheet (Hyprland only)
|
# Show Hyprland keybinds cheatsheet (Hyprland only)
|
||||||
dms ipc call hypr toggleBinds
|
dms ipc call hypr toggleBinds
|
||||||
dms ipc call hypr openBinds
|
dms ipc call hypr openBinds
|
||||||
|
|
||||||
# Show Hyprland workspace overview (Hyprland only)
|
|
||||||
dms ipc call hypr toggleOverview
|
|
||||||
dms ipc call hypr openOverview
|
|
||||||
dms ipc call hypr closeOverview
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Common Usage Patterns
|
## Common Usage Patterns
|
||||||
@@ -620,7 +607,6 @@ bind = SUPER, V, exec, qs -c dms ipc call clipboard toggle
|
|||||||
bind = SUPER, P, exec, qs -c dms ipc call notepad toggle
|
bind = SUPER, P, exec, qs -c dms ipc call notepad toggle
|
||||||
bind = SUPER, X, exec, qs -c dms ipc call powermenu toggle
|
bind = SUPER, X, exec, qs -c dms ipc call powermenu toggle
|
||||||
bind = SUPER, slash, exec, qs -c dms ipc call hypr toggleBinds
|
bind = SUPER, slash, exec, qs -c dms ipc call hypr toggleBinds
|
||||||
bind = SUPER, Tab, exec, qs -c dms ipc call hypr toggleOverview
|
|
||||||
bind = , XF86AudioRaiseVolume, exec, qs -c dms ipc call audio increment 3
|
bind = , XF86AudioRaiseVolume, exec, qs -c dms ipc call audio increment 3
|
||||||
bind = , XF86MonBrightnessUp, exec, qs -c dms ipc call brightness increment 5 ""
|
bind = , XF86MonBrightnessUp, exec, qs -c dms ipc call brightness increment 5 ""
|
||||||
```
|
```
|
||||||
|
|||||||
12
nix/niri.nix
12
nix/niri.nix
@@ -59,11 +59,6 @@ in {
|
|||||||
allow-when-locked = true;
|
allow-when-locked = true;
|
||||||
action = dms-ipc "audio" "micmute";
|
action = dms-ipc "audio" "micmute";
|
||||||
};
|
};
|
||||||
"Mod+Alt+N" = {
|
|
||||||
allow-when-locked = true;
|
|
||||||
action = dms-ipc "night" "toggle";
|
|
||||||
hotkey-overlay.title = "Toggle Night Mode";
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
// lib.attrsets.optionalAttrs cfg.enableSystemMonitoring {
|
// lib.attrsets.optionalAttrs cfg.enableSystemMonitoring {
|
||||||
"Mod+M" = {
|
"Mod+M" = {
|
||||||
@@ -86,6 +81,13 @@ in {
|
|||||||
allow-when-locked = true;
|
allow-when-locked = true;
|
||||||
action = dms-ipc "brightness" "decrement" "5" "";
|
action = dms-ipc "brightness" "decrement" "5" "";
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
// lib.attrsets.optionalAttrs cfg.enableNightMode {
|
||||||
|
"Mod+Alt+N" = {
|
||||||
|
allow-when-locked = true;
|
||||||
|
action = dms-ipc "night" "toggle";
|
||||||
|
hotkey-overlay.title = "Toggle Night Mode";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if [ $# -lt 5 ]; then
|
if [ $# -lt 4 ]; then
|
||||||
echo "Usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR SYNC_MODE_WITH_PORTAL --run" >&2
|
echo "Usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR --run" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
STATE_DIR="$1"
|
STATE_DIR="$1"
|
||||||
SHELL_DIR="$2"
|
SHELL_DIR="$2"
|
||||||
CONFIG_DIR="$3"
|
CONFIG_DIR="$3"
|
||||||
SYNC_MODE_WITH_PORTAL="$4"
|
|
||||||
|
|
||||||
if [ ! -d "$STATE_DIR" ]; then
|
if [ ! -d "$STATE_DIR" ]; then
|
||||||
echo "Error: STATE_DIR '$STATE_DIR' does not exist" >&2
|
echo "Error: STATE_DIR '$STATE_DIR' does not exist" >&2
|
||||||
@@ -26,10 +25,10 @@ if [ ! -d "$CONFIG_DIR" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
shift 4
|
shift 3 # Remove STATE_DIR, SHELL_DIR, and CONFIG_DIR from arguments
|
||||||
|
|
||||||
if [[ "${1:-}" != "--run" ]]; then
|
if [[ "${1:-}" != "--run" ]]; then
|
||||||
echo "usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR SYNC_MODE_WITH_PORTAL --run" >&2
|
echo "usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR --run" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -63,27 +62,6 @@ key_of() {
|
|||||||
echo "${kind}|${value}|${mode}|${icon}|${matugen_type}|${surface_base}|${run_user_templates}" | sha256sum | cut -d' ' -f1
|
echo "${kind}|${value}|${mode}|${icon}|${matugen_type}|${surface_base}|${run_user_templates}" | sha256sum | cut -d' ' -f1
|
||||||
}
|
}
|
||||||
|
|
||||||
set_system_color_scheme() {
|
|
||||||
local mode="$1"
|
|
||||||
|
|
||||||
if [[ "$SYNC_MODE_WITH_PORTAL" != "true" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
local target_scheme
|
|
||||||
if [[ "$mode" == "light" ]]; then
|
|
||||||
target_scheme="default"
|
|
||||||
else
|
|
||||||
target_scheme="prefer-dark"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v gsettings >/dev/null 2>&1; then
|
|
||||||
gsettings set org.gnome.desktop.interface color-scheme "$target_scheme" >/dev/null 2>&1 || true
|
|
||||||
elif command -v dconf >/dev/null 2>&1; then
|
|
||||||
dconf write /org/gnome/desktop/interface/color-scheme "'$target_scheme'" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
build_once() {
|
build_once() {
|
||||||
local json="$1"
|
local json="$1"
|
||||||
local kind value mode icon matugen_type surface_base run_user_templates
|
local kind value mode icon matugen_type surface_base run_user_templates
|
||||||
@@ -259,7 +237,7 @@ EOF
|
|||||||
rm -f "$TMP_CONTENT_CFG"
|
rm -f "$TMP_CONTENT_CFG"
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|
||||||
echo "$JSON" | grep -q '"primary"' || { echo "matugen JSON missing primary" >&2; set_system_color_scheme "$mode"; return 2; }
|
echo "$JSON" | grep -q '"primary"' || { echo "matugen JSON missing primary" >&2; return 2; }
|
||||||
printf "%s" "$JSON" > "$LAST_JSON"
|
printf "%s" "$JSON" > "$LAST_JSON"
|
||||||
|
|
||||||
GTK_CSS="$CONFIG_DIR/gtk-3.0/gtk.css"
|
GTK_CSS="$CONFIG_DIR/gtk-3.0/gtk.css"
|
||||||
@@ -311,8 +289,6 @@ EOF
|
|||||||
mv "$TMP" "$CONFIG_DIR/kitty/dank-theme.conf"
|
mv "$TMP" "$CONFIG_DIR/kitty/dank-theme.conf"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set_system_color_scheme "$mode"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if command -v pywalfox >/dev/null 2>&1 && [[ -f "$HOME/.cache/wal/colors.json" ]]; then
|
if command -v pywalfox >/dev/null 2>&1 && [[ -f "$HOME/.cache/wal/colors.json" ]]; then
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
{
|
{
|
||||||
"term": "About",
|
"term": "About",
|
||||||
"context": "About",
|
"context": "About",
|
||||||
"reference": "Modules/Settings/AboutTab.qml:251, Modals/Settings/SettingsSidebar.qml:44",
|
"reference": "Modals/Settings/SettingsSidebar.qml:44, Modules/Settings/AboutTab.qml:251",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
{
|
{
|
||||||
"term": "All",
|
"term": "All",
|
||||||
"context": "All",
|
"context": "All",
|
||||||
"reference": "Services/AppSearchService.qml:217, Services/AppSearchService.qml:233, Modules/AppDrawer/AppDrawerPopout.qml:45, Modules/AppDrawer/CategorySelector.qml:11, Modules/AppDrawer/AppLauncher.qml:16, Modules/AppDrawer/AppLauncher.qml:27, Modules/AppDrawer/AppLauncher.qml:28, Modules/AppDrawer/AppLauncher.qml:45, Modules/AppDrawer/AppLauncher.qml:46, Modules/AppDrawer/AppLauncher.qml:80, Modals/Spotlight/SpotlightModal.qml:59",
|
"reference": "Services/AppSearchService.qml:217, Services/AppSearchService.qml:233, Modals/Spotlight/SpotlightModal.qml:59, Modules/AppDrawer/CategorySelector.qml:11, Modules/AppDrawer/AppLauncher.qml:12, Modules/AppDrawer/AppLauncher.qml:23, Modules/AppDrawer/AppLauncher.qml:24, Modules/AppDrawer/AppLauncher.qml:41, Modules/AppDrawer/AppLauncher.qml:42, Modules/AppDrawer/AppLauncher.qml:76, Modules/AppDrawer/AppDrawerPopout.qml:45",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
{
|
{
|
||||||
"term": "All displays",
|
"term": "All displays",
|
||||||
"context": "All displays",
|
"context": "All displays",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:670",
|
"reference": "Modules/Settings/DisplaysTab.qml:666",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Anonymous Identity (optional)",
|
"term": "Anonymous Identity (optional)",
|
||||||
"context": "Anonymous Identity (optional)",
|
"context": "Anonymous Identity (optional)",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:365",
|
"reference": "Modals/WifiPasswordModal.qml:282",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -299,6 +299,12 @@
|
|||||||
"reference": "Modules/Settings/DockTab.qml:128",
|
"reference": "Modules/Settings/DockTab.qml:128",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Auto-location",
|
||||||
|
"context": "Auto-location",
|
||||||
|
"reference": "Modules/Settings/DisplaysTab.qml:364",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Auto-saving...",
|
"term": "Auto-saving...",
|
||||||
"context": "Auto-saving...",
|
"context": "Auto-saving...",
|
||||||
@@ -308,7 +314,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Automatic Control",
|
"term": "Automatic Control",
|
||||||
"context": "Automatic Control",
|
"context": "Automatic Control",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:164",
|
"reference": "Modules/Settings/DisplaysTab.qml:162",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -329,12 +335,6 @@
|
|||||||
"reference": "Modules/Settings/PersonalizationTab.qml:647",
|
"reference": "Modules/Settings/PersonalizationTab.qml:647",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Automatically detect location based on IP address",
|
|
||||||
"context": "Automatically detect location based on IP address",
|
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:369",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Automatically determine your location using your IP address",
|
"term": "Automatically determine your location using your IP address",
|
||||||
"context": "Automatically determine your location using your IP address",
|
"context": "Automatically determine your location using your IP address",
|
||||||
@@ -380,7 +380,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Available Screens (",
|
"term": "Available Screens (",
|
||||||
"context": "Available Screens (",
|
"context": "Available Screens (",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:513",
|
"reference": "Modules/Settings/DisplaysTab.qml:509",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -524,7 +524,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Cancel",
|
"term": "Cancel",
|
||||||
"context": "Cancel",
|
"context": "Cancel",
|
||||||
"reference": "Modals/DankColorPickerModal.qml:510, Modals/WifiPasswordModal.qml:468, Modules/Settings/PluginsTab.qml:1220, Modals/FileBrowser/FileBrowserModal.qml:952",
|
"reference": "Modals/DankColorPickerModal.qml:510, Modals/WifiPasswordModal.qml:385, Modals/FileBrowser/FileBrowserModal.qml:952, Modules/Settings/PluginsTab.qml:1220",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -578,13 +578,13 @@
|
|||||||
{
|
{
|
||||||
"term": "Clear",
|
"term": "Clear",
|
||||||
"context": "Clear",
|
"context": "Clear",
|
||||||
"reference": "Modules/Notifications/Center/NotificationHeader.qml:102",
|
"reference": "Modules/Notifications/Popup/NotificationPopup.qml:24, Modules/Notifications/Center/NotificationCard.qml:540, Modules/Notifications/Center/NotificationCard.qml:633",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Clear All",
|
"term": "Clear All",
|
||||||
"context": "Clear All",
|
"context": "Clear All",
|
||||||
"reference": "Modals/Clipboard/ClipboardHistoryModal.qml:157",
|
"reference": "Modals/Clipboard/ClipboardHistoryModal.qml:157, Modules/Notifications/Center/NotificationHeader.qml:102",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -620,7 +620,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Close",
|
"term": "Close",
|
||||||
"context": "Close",
|
"context": "Close",
|
||||||
"reference": "Modals/NetworkInfoModal.qml:129, Modals/NetworkWiredInfoModal.qml:129, Modules/SystemUpdatePopout.qml:333, Modules/DankBar/Widgets/RunningApps.qml:717",
|
"reference": "Modules/SystemUpdatePopout.qml:333, Modals/NetworkWiredInfoModal.qml:129, Modals/NetworkInfoModal.qml:129, Modules/DankBar/Widgets/RunningApps.qml:711",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -638,7 +638,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Color temperature for night mode",
|
"term": "Color temperature for night mode",
|
||||||
"context": "Color temperature for night mode",
|
"context": "Color temperature for night mode",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:145",
|
"reference": "Modules/Settings/DisplaysTab.qml:143",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -701,12 +701,6 @@
|
|||||||
"reference": "Modules/Settings/AboutTab.qml:361",
|
"reference": "Modules/Settings/AboutTab.qml:361",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Configuration activated",
|
|
||||||
"context": "Configuration activated",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:320",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Configure icons for named workspaces. Icons take priority over numbers when both are enabled.",
|
"term": "Configure icons for named workspaces. Icons take priority over numbers when both are enabled.",
|
||||||
"context": "Configure icons for named workspaces. Icons take priority over numbers when both are enabled.",
|
"context": "Configure icons for named workspaces. Icons take priority over numbers when both are enabled.",
|
||||||
@@ -716,31 +710,25 @@
|
|||||||
{
|
{
|
||||||
"term": "Configure which displays show shell components",
|
"term": "Configure which displays show shell components",
|
||||||
"context": "Configure which displays show shell components",
|
"context": "Configure which displays show shell components",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:497",
|
"reference": "Modules/Settings/DisplaysTab.qml:493",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Connect",
|
"term": "Connect",
|
||||||
"context": "Connect",
|
"context": "Connect",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:505",
|
"reference": "Modals/WifiPasswordModal.qml:419",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Connect to Wi-Fi",
|
"term": "Connect to Wi-Fi",
|
||||||
"context": "Connect to Wi-Fi",
|
"context": "Connect to Wi-Fi",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:166",
|
"reference": "Modals/WifiPasswordModal.qml:114",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Connected Displays",
|
"term": "Connected Displays",
|
||||||
"context": "Connected Displays",
|
"context": "Connected Displays",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:490",
|
"reference": "Modules/Settings/DisplaysTab.qml:486",
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "Connection failed. Check password and try again.",
|
|
||||||
"context": "Connection failed. Check password and try again.",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:287",
|
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -884,7 +872,7 @@
|
|||||||
{
|
{
|
||||||
"term": "DMS out of date",
|
"term": "DMS out of date",
|
||||||
"context": "DMS out of date",
|
"context": "DMS out of date",
|
||||||
"reference": "Services/DMSService.qml:235",
|
"reference": "Services/DMSService.qml:234",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -983,12 +971,6 @@
|
|||||||
"reference": "Modules/ControlCenter/BuiltinPlugins/VpnWidget.qml:90, Modules/DankBar/Popouts/VpnPopout.qml:213",
|
"reference": "Modules/ControlCenter/BuiltinPlugins/VpnWidget.qml:90, Modules/DankBar/Popouts/VpnPopout.qml:213",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Disconnected from WiFi",
|
|
||||||
"context": "Disconnected from WiFi",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:405",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Disk",
|
"term": "Disk",
|
||||||
"context": "Disk",
|
"context": "Disk",
|
||||||
@@ -1001,12 +983,6 @@
|
|||||||
"reference": "Modules/Settings/DankBarTab.qml:87",
|
"reference": "Modules/Settings/DankBarTab.qml:87",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Dismiss",
|
|
||||||
"context": "Dismiss",
|
|
||||||
"reference": "Modules/Notifications/Popup/NotificationPopup.qml:24, Modules/Notifications/Center/NotificationCard.qml:540, Modules/Notifications/Center/NotificationCard.qml:633",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen",
|
"term": "Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen",
|
||||||
"context": "Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen",
|
"context": "Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen",
|
||||||
@@ -1058,7 +1034,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Do Not Disturb",
|
"term": "Do Not Disturb",
|
||||||
"context": "Do Not Disturb",
|
"context": "Do Not Disturb",
|
||||||
"reference": "Modules/Notifications/Center/NotificationSettings.qml:131, Modules/Notifications/Center/NotificationHeader.qml:41",
|
"reference": "Modules/Notifications/Center/NotificationHeader.qml:41, Modules/Notifications/Center/NotificationSettings.qml:131",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1082,7 +1058,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Domain (optional)",
|
"term": "Domain (optional)",
|
||||||
"context": "Domain (optional)",
|
"context": "Domain (optional)",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:397",
|
"reference": "Modals/WifiPasswordModal.qml:314",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1160,13 +1136,13 @@
|
|||||||
{
|
{
|
||||||
"term": "End",
|
"term": "End",
|
||||||
"context": "End",
|
"context": "End",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:318",
|
"reference": "Modules/Settings/DisplaysTab.qml:315",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Enter credentials for ",
|
"term": "Enter credentials for ",
|
||||||
"context": "Enter credentials for ",
|
"context": "Enter credentials for ",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:178",
|
"reference": "Modals/WifiPasswordModal.qml:121",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1190,7 +1166,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Enter password for ",
|
"term": "Enter password for ",
|
||||||
"context": "Enter password for ",
|
"context": "Enter password for ",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:178",
|
"reference": "Modals/WifiPasswordModal.qml:121",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1211,30 +1187,6 @@
|
|||||||
"reference": "Modals/FileBrowser/FileInfo.qml:200",
|
"reference": "Modals/FileBrowser/FileInfo.qml:200",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Failed to activate configuration",
|
|
||||||
"context": "Failed to activate configuration",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:316",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "Failed to connect to ",
|
|
||||||
"context": "Failed to connect to ",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:289",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "Failed to disconnect WiFi",
|
|
||||||
"context": "Failed to disconnect WiFi",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:403",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "Failed to enable WiFi",
|
|
||||||
"context": "Failed to enable WiFi",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:508",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Failed to set profile image",
|
"term": "Failed to set profile image",
|
||||||
"context": "Failed to set profile image",
|
"context": "Failed to set profile image",
|
||||||
@@ -1247,12 +1199,6 @@
|
|||||||
"reference": "Services/PortalService.qml:159",
|
"reference": "Services/PortalService.qml:159",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Failed to start connection to ",
|
|
||||||
"context": "Failed to start connection to ",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:389",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Feels Like",
|
"term": "Feels Like",
|
||||||
"context": "Feels Like",
|
"context": "Feels Like",
|
||||||
@@ -1292,7 +1238,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Font Family",
|
"term": "Font Family",
|
||||||
"context": "Font Family",
|
"context": "Font Family",
|
||||||
"reference": "Modules/Settings/ThemeColorsTab.qml:944, Modules/Notepad/NotepadSettings.qml:220",
|
"reference": "Modules/Notepad/NotepadSettings.qml:220, Modules/Settings/ThemeColorsTab.qml:944",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1334,13 +1280,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Forget Network",
|
"term": "Forget Network",
|
||||||
"context": "Forget Network",
|
"context": "Forget Network",
|
||||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:606",
|
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:598",
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "Forgot network ",
|
|
||||||
"context": "Forgot network ",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:465",
|
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1392,9 +1332,9 @@
|
|||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Gamma control not available. Requires DMS API v6+.",
|
"term": "Geoclue service not running - cannot auto-detect location",
|
||||||
"context": "Gamma control not available. Requires DMS API v6+.",
|
"context": "Geoclue service not running - cannot auto-detect location",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:119",
|
"reference": "Modules/Settings/DisplaysTab.qml:365",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1484,7 +1424,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Hour",
|
"term": "Hour",
|
||||||
"context": "Hour",
|
"context": "Hour",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:255",
|
"reference": "Modules/Settings/DisplaysTab.qml:252",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1547,12 +1487,6 @@
|
|||||||
"reference": "Modules/Settings/PersonalizationTab.qml:923",
|
"reference": "Modules/Settings/PersonalizationTab.qml:923",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Incorrect password",
|
|
||||||
"context": "Incorrect password",
|
|
||||||
"reference": "Modals/WifiPasswordModal.qml:189",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Individual Batteries",
|
"term": "Individual Batteries",
|
||||||
"context": "Individual Batteries",
|
"context": "Individual Batteries",
|
||||||
@@ -1640,13 +1574,13 @@
|
|||||||
{
|
{
|
||||||
"term": "Latitude",
|
"term": "Latitude",
|
||||||
"context": "Latitude",
|
"context": "Latitude",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:402, Modules/Settings/TimeWeatherTab.qml:665",
|
"reference": "Modules/Settings/DisplaysTab.qml:398, Modules/Settings/TimeWeatherTab.qml:665",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Launch",
|
"term": "Launch",
|
||||||
"context": "Launch",
|
"context": "Launch",
|
||||||
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:923, Modals/Spotlight/SpotlightContextMenu.qml:251",
|
"reference": "Modals/Spotlight/SpotlightContextMenu.qml:251, Modules/AppDrawer/AppDrawerPopout.qml:895",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1658,7 +1592,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Launch on dGPU",
|
"term": "Launch on dGPU",
|
||||||
"context": "Launch on dGPU",
|
"context": "Launch on dGPU",
|
||||||
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:983, Modules/Dock/DockContextMenu.qml:417, Modals/Spotlight/SpotlightContextMenu.qml:312",
|
"reference": "Modals/Spotlight/SpotlightContextMenu.qml:312, Modules/AppDrawer/AppDrawerPopout.qml:955, Modules/Dock/DockContextMenu.qml:417",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1748,7 +1682,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Longitude",
|
"term": "Longitude",
|
||||||
"context": "Longitude",
|
"context": "Longitude",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:425, Modules/Settings/TimeWeatherTab.qml:716",
|
"reference": "Modules/Settings/DisplaysTab.qml:421, Modules/Settings/TimeWeatherTab.qml:716",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1766,7 +1700,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Manual Coordinates",
|
"term": "Manual Coordinates",
|
||||||
"context": "Manual Coordinates",
|
"context": "Manual Coordinates",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:390",
|
"reference": "Modules/Settings/DisplaysTab.qml:386",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1790,7 +1724,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Matugen Palette",
|
"term": "Matugen Palette",
|
||||||
"context": "Matugen Palette",
|
"context": "Matugen Palette",
|
||||||
"reference": "Modules/Settings/PersonalizationTab.qml:1276, Modules/Settings/ThemeColorsTab.qml:629",
|
"reference": "Modules/Settings/ThemeColorsTab.qml:629, Modules/Settings/PersonalizationTab.qml:1276",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1856,7 +1790,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Minute",
|
"term": "Minute",
|
||||||
"context": "Minute",
|
"context": "Minute",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:263",
|
"reference": "Modules/Settings/DisplaysTab.qml:260",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1928,13 +1862,13 @@
|
|||||||
{
|
{
|
||||||
"term": "Network Info",
|
"term": "Network Info",
|
||||||
"context": "Network Info",
|
"context": "Network Info",
|
||||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:340, Modules/ControlCenter/Details/NetworkDetail.qml:583",
|
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:340, Modules/ControlCenter/Details/NetworkDetail.qml:575",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Network Information",
|
"term": "Network Information",
|
||||||
"context": "Network Information",
|
"context": "Network Information",
|
||||||
"reference": "Modals/NetworkInfoModal.qml:59, Modals/NetworkWiredInfoModal.qml:59",
|
"reference": "Modals/NetworkWiredInfoModal.qml:59, Modals/NetworkInfoModal.qml:59",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2054,7 +1988,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Notepad",
|
"term": "Notepad",
|
||||||
"context": "Notepad",
|
"context": "Notepad",
|
||||||
"reference": "DMSShell.qml:413, Modules/Settings/DankBarTab.qml:175",
|
"reference": "DMSShell.qml:407, Modules/Settings/DankBarTab.qml:175",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2090,7 +2024,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Notification Popups",
|
"term": "Notification Popups",
|
||||||
"context": "Notification Popups",
|
"context": "Notification Popups",
|
||||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:585, Modules/Settings/DisplaysTab.qml:24",
|
"reference": "Modules/Settings/DisplaysTab.qml:24, Modules/Settings/WidgetTweaksTab.qml:585",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2144,7 +2078,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Only adjust gamma based on time or location rules.",
|
"term": "Only adjust gamma based on time or location rules.",
|
||||||
"context": "Only adjust gamma based on time or location rules.",
|
"context": "Only adjust gamma based on time or location rules.",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:165",
|
"reference": "Modules/Settings/DisplaysTab.qml:163",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2192,7 +2126,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Password",
|
"term": "Password",
|
||||||
"context": "Password",
|
"context": "Password",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:274",
|
"reference": "Modals/WifiPasswordModal.qml:203",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2234,7 +2168,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Pin to Dock",
|
"term": "Pin to Dock",
|
||||||
"context": "Pin to Dock",
|
"context": "Pin to Dock",
|
||||||
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:786, Modules/Dock/DockContextMenu.qml:370, Modals/Spotlight/SpotlightContextMenu.qml:110, Modals/Spotlight/SpotlightContextMenu.qml:113",
|
"reference": "Modals/Spotlight/SpotlightContextMenu.qml:110, Modals/Spotlight/SpotlightContextMenu.qml:113, Modules/AppDrawer/AppDrawerPopout.qml:758, Modules/Dock/DockContextMenu.qml:370",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2510,7 +2444,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Save",
|
"term": "Save",
|
||||||
"context": "Save",
|
"context": "Save",
|
||||||
"reference": "Modules/Notepad/Notepad.qml:480, Modules/Notepad/NotepadTextEditor.qml:511, Modals/FileBrowser/FileBrowserModal.qml:818",
|
"reference": "Modals/FileBrowser/FileBrowserModal.qml:818, Modules/Notepad/NotepadTextEditor.qml:511, Modules/Notepad/Notepad.qml:480",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2636,7 +2570,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Select the palette algorithm used for wallpaper-based colors",
|
"term": "Select the palette algorithm used for wallpaper-based colors",
|
||||||
"context": "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/ThemeColorsTab.qml:630, Modules/Settings/PersonalizationTab.qml:1277",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2672,7 +2606,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Settings",
|
"term": "Settings",
|
||||||
"context": "Settings",
|
"context": "Settings",
|
||||||
"reference": "Services/AppSearchService.qml:176, Modules/DankDash/DankDashPopout.qml:142, Modals/Settings/SettingsModal.qml:165",
|
"reference": "Services/AppSearchService.qml:176, Modals/Settings/SettingsModal.qml:165, Modules/DankDash/DankDashPopout.qml:142",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2720,13 +2654,13 @@
|
|||||||
{
|
{
|
||||||
"term": "Show on all connected displays",
|
"term": "Show on all connected displays",
|
||||||
"context": "Show on all connected displays",
|
"context": "Show on all connected displays",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:671",
|
"reference": "Modules/Settings/DisplaysTab.qml:667",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Show on screens:",
|
"term": "Show on screens:",
|
||||||
"context": "Show on screens:",
|
"context": "Show on screens:",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:655",
|
"reference": "Modules/Settings/DisplaysTab.qml:651",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2744,7 +2678,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Show password",
|
"term": "Show password",
|
||||||
"context": "Show password",
|
"context": "Show password",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:440",
|
"reference": "Modals/WifiPasswordModal.qml:357",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2834,7 +2768,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Start",
|
"term": "Start",
|
||||||
"context": "Start",
|
"context": "Start",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:275",
|
"reference": "Modules/Settings/DisplaysTab.qml:272",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2882,7 +2816,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Switch User",
|
"term": "Switch User",
|
||||||
"context": "Switch User",
|
"context": "Switch User",
|
||||||
"reference": "Modules/Greetd/GreeterContent.qml:549",
|
"reference": "Modules/Greetd/GreeterContent.qml:567",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2996,7 +2930,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Temperature",
|
"term": "Temperature",
|
||||||
"context": "Temperature",
|
"context": "Temperature",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:144",
|
"reference": "Modules/Settings/DisplaysTab.qml:142",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3074,7 +3008,7 @@
|
|||||||
{
|
{
|
||||||
"term": "To update, run the following command:",
|
"term": "To update, run the following command:",
|
||||||
"context": "To update, run the following command:",
|
"context": "To update, run the following command:",
|
||||||
"reference": "Services/DMSService.qml:236",
|
"reference": "Services/DMSService.qml:235",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3134,7 +3068,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Unpin from Dock",
|
"term": "Unpin from Dock",
|
||||||
"context": "Unpin from Dock",
|
"context": "Unpin from Dock",
|
||||||
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:786, Modules/Dock/DockContextMenu.qml:370, Modals/Spotlight/SpotlightContextMenu.qml:113",
|
"reference": "Modals/Spotlight/SpotlightContextMenu.qml:113, Modules/AppDrawer/AppDrawerPopout.qml:758, Modules/Dock/DockContextMenu.qml:370",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3191,12 +3125,6 @@
|
|||||||
"reference": "Modules/Settings/TimeWeatherTab.qml:545",
|
"reference": "Modules/Settings/TimeWeatherTab.qml:545",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Use IP Location",
|
|
||||||
"context": "Use IP Location",
|
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:368",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Use Monospace Font",
|
"term": "Use Monospace Font",
|
||||||
"context": "Use Monospace Font",
|
"context": "Use Monospace Font",
|
||||||
@@ -3215,6 +3143,12 @@
|
|||||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:194",
|
"reference": "Modules/Settings/WidgetTweaksTab.qml:194",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Use automatic location detection (geoclue2)",
|
||||||
|
"context": "Use automatic location detection (geoclue2)",
|
||||||
|
"reference": "Modules/Settings/DisplaysTab.qml:365",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Use custom command for update your system",
|
"term": "Use custom command for update your system",
|
||||||
"context": "Use custom command for update your system",
|
"context": "Use custom command for update your system",
|
||||||
@@ -3254,13 +3188,13 @@
|
|||||||
{
|
{
|
||||||
"term": "Username",
|
"term": "Username",
|
||||||
"context": "Username",
|
"context": "Username",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:237",
|
"reference": "Modals/WifiPasswordModal.qml:166",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Uses sunrise/sunset times to automatically adjust night mode based on your location.",
|
"term": "Uses sunrise/sunset times to automatically adjust night mode based on your location.",
|
||||||
"context": "Uses sunrise/sunset times to automatically adjust night mode based on your location.",
|
"context": "Uses sunrise/sunset times to automatically adjust night mode based on your location.",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:446",
|
"reference": "Modules/Settings/DisplaysTab.qml:442",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3353,18 +3287,6 @@
|
|||||||
"reference": "Modules/Settings/LauncherTab.qml:517",
|
"reference": "Modules/Settings/LauncherTab.qml:517",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "WiFi disabled",
|
|
||||||
"context": "WiFi disabled",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:498",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "WiFi enabled",
|
|
||||||
"context": "WiFi enabled",
|
|
||||||
"reference": "Services/NetworkManagerService.qml:498, Services/NetworkManagerService.qml:510",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "WiFi is off",
|
"term": "WiFi is off",
|
||||||
"context": "WiFi is off",
|
"context": "WiFi is off",
|
||||||
|
|||||||
@@ -167,9 +167,6 @@
|
|||||||
"Automatically cycle through wallpapers in the same folder": {
|
"Automatically cycle through wallpapers in the same folder": {
|
||||||
"Automatically cycle through wallpapers in the same folder": "同じフォルダ内の壁紙を自動的に切り替える"
|
"Automatically cycle through wallpapers in the same folder": "同じフォルダ内の壁紙を自動的に切り替える"
|
||||||
},
|
},
|
||||||
"Automatically detect location based on IP address": {
|
|
||||||
"Automatically detect location based on IP address": "IPアドレスに基づいて位置を自動的に検出"
|
|
||||||
},
|
|
||||||
"Automatically determine your location using your IP address": {
|
"Automatically determine your location using your IP address": {
|
||||||
"Automatically determine your location using your IP address": "IPアドレスを使用して現在地を自動的に特定します"
|
"Automatically determine your location using your IP address": "IPアドレスを使用して現在地を自動的に特定します"
|
||||||
},
|
},
|
||||||
@@ -353,9 +350,6 @@
|
|||||||
"Compositor:": {
|
"Compositor:": {
|
||||||
"Compositor:": "コンポジター:"
|
"Compositor:": "コンポジター:"
|
||||||
},
|
},
|
||||||
"Configuration activated": {
|
|
||||||
"Configuration activated": ""
|
|
||||||
},
|
|
||||||
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": {
|
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": {
|
||||||
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": "名前付きワークスペースのアイコンを設定します。両方が有効になっている場合、アイコンが数字よりも優先されます。"
|
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": "名前付きワークスペースのアイコンを設定します。両方が有効になっている場合、アイコンが数字よりも優先されます。"
|
||||||
},
|
},
|
||||||
@@ -371,9 +365,6 @@
|
|||||||
"Connected Displays": {
|
"Connected Displays": {
|
||||||
"Connected Displays": "接続されたディスプレイ"
|
"Connected Displays": "接続されたディスプレイ"
|
||||||
},
|
},
|
||||||
"Connection failed. Check password and try again.": {
|
|
||||||
"Connection failed. Check password and try again.": ""
|
|
||||||
},
|
|
||||||
"Contrast": {
|
"Contrast": {
|
||||||
"Contrast": "コントラスト"
|
"Contrast": "コントラスト"
|
||||||
},
|
},
|
||||||
@@ -494,18 +485,12 @@
|
|||||||
"Disconnect": {
|
"Disconnect": {
|
||||||
"Disconnect": "切断"
|
"Disconnect": "切断"
|
||||||
},
|
},
|
||||||
"Disconnected from WiFi": {
|
|
||||||
"Disconnected from WiFi": ""
|
|
||||||
},
|
|
||||||
"Disk": {
|
"Disk": {
|
||||||
"Disk": "ディスク"
|
"Disk": "ディスク"
|
||||||
},
|
},
|
||||||
"Disk Usage": {
|
"Disk Usage": {
|
||||||
"Disk Usage": "ディスク使用率"
|
"Disk Usage": "ディスク使用率"
|
||||||
},
|
},
|
||||||
"Dismiss": {
|
|
||||||
"Dismiss": ""
|
|
||||||
},
|
|
||||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
||||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "画面の上、下、左、右の端に配置できる、ピン留めされた実行中のアプリケーションを含むドックを表示します"
|
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "画面の上、下、左、右の端に配置できる、ピン留めされた実行中のアプリケーションを含むドックを表示します"
|
||||||
},
|
},
|
||||||
@@ -608,27 +593,6 @@
|
|||||||
"F1/I: Toggle • F10: Help": {
|
"F1/I: Toggle • F10: Help": {
|
||||||
"F1/I: Toggle • F10: Help": "F1/I: 切り替え • F10: ヘルプ"
|
"F1/I: Toggle • F10: Help": "F1/I: 切り替え • F10: ヘルプ"
|
||||||
},
|
},
|
||||||
"Failed to activate configuration": {
|
|
||||||
"Failed to activate configuration": ""
|
|
||||||
},
|
|
||||||
"Failed to connect to ": {
|
|
||||||
"Failed to connect to ": ""
|
|
||||||
},
|
|
||||||
"Failed to disconnect WiFi": {
|
|
||||||
"Failed to disconnect WiFi": ""
|
|
||||||
},
|
|
||||||
"Failed to enable WiFi": {
|
|
||||||
"Failed to enable WiFi": ""
|
|
||||||
},
|
|
||||||
"Failed to set profile image": {
|
|
||||||
"Failed to set profile image": "プロフィール画像の設定に失敗しました"
|
|
||||||
},
|
|
||||||
"Failed to set profile image: ": {
|
|
||||||
"Failed to set profile image: ": "プロフィール画像の設定に失敗しました: "
|
|
||||||
},
|
|
||||||
"Failed to start connection to ": {
|
|
||||||
"Failed to start connection to ": ""
|
|
||||||
},
|
|
||||||
"Feels Like": {
|
"Feels Like": {
|
||||||
"Feels Like": "どうやら"
|
"Feels Like": "どうやら"
|
||||||
},
|
},
|
||||||
@@ -671,9 +635,6 @@
|
|||||||
"Forget Network": {
|
"Forget Network": {
|
||||||
"Forget Network": "ネットワークを忘れる"
|
"Forget Network": "ネットワークを忘れる"
|
||||||
},
|
},
|
||||||
"Forgot network ": {
|
|
||||||
"Forgot network ": ""
|
|
||||||
},
|
|
||||||
"Format Legend": {
|
"Format Legend": {
|
||||||
"Format Legend": "フォーマット凡例"
|
"Format Legend": "フォーマット凡例"
|
||||||
},
|
},
|
||||||
@@ -698,9 +659,6 @@
|
|||||||
"Gamma Control": {
|
"Gamma Control": {
|
||||||
"Gamma Control": "ガンマ設定"
|
"Gamma Control": "ガンマ設定"
|
||||||
},
|
},
|
||||||
"Gamma control not available. Requires DMS API v6+.": {
|
|
||||||
"Gamma control not available. Requires DMS API v6+.": "ガンマ制御は使用できません。DMS API v6+ が必要です。"
|
|
||||||
},
|
|
||||||
"Geoclue service not running - cannot auto-detect location": {
|
"Geoclue service not running - cannot auto-detect location": {
|
||||||
"Geoclue service not running - cannot auto-detect location": "ジオクルー サービスが実行されていない - 位置を自動検出できません"
|
"Geoclue service not running - cannot auto-detect location": "ジオクルー サービスが実行されていない - 位置を自動検出できません"
|
||||||
},
|
},
|
||||||
@@ -779,9 +737,6 @@
|
|||||||
"Include Transitions": {
|
"Include Transitions": {
|
||||||
"Include Transitions": "トランジションを含める"
|
"Include Transitions": "トランジションを含める"
|
||||||
},
|
},
|
||||||
"Incorrect password": {
|
|
||||||
"Incorrect password": ""
|
|
||||||
},
|
|
||||||
"Individual Batteries": {
|
"Individual Batteries": {
|
||||||
"Individual Batteries": "バッテリーごと"
|
"Individual Batteries": "バッテリーごと"
|
||||||
},
|
},
|
||||||
@@ -1115,9 +1070,6 @@
|
|||||||
"Percentage": {
|
"Percentage": {
|
||||||
"Percentage": "百分率"
|
"Percentage": "百分率"
|
||||||
},
|
},
|
||||||
"Permission denied to set profile image.": {
|
|
||||||
"Permission denied to set profile image.": "プロフィール画像の設定権限が拒否されました。"
|
|
||||||
},
|
|
||||||
"Personalization": {
|
"Personalization": {
|
||||||
"Personalization": "パーソナライゼーション"
|
"Personalization": "パーソナライゼーション"
|
||||||
},
|
},
|
||||||
@@ -1196,12 +1148,6 @@
|
|||||||
"Process": {
|
"Process": {
|
||||||
"Process": "プロセス"
|
"Process": "プロセス"
|
||||||
},
|
},
|
||||||
"Profile Image Error": {
|
|
||||||
"Profile Image Error": "プロフィール画像エラー"
|
|
||||||
},
|
|
||||||
"Profile image is too large. Please use a smaller image.": {
|
|
||||||
"Profile image is too large. Please use a smaller image.": "プロフィール画像が大きすぎます。より小さい画像を使用してください。"
|
|
||||||
},
|
|
||||||
"QML, JavaScript, Go": {
|
"QML, JavaScript, Go": {
|
||||||
"QML, JavaScript, Go": "QML, JavaScript, Go"
|
"QML, JavaScript, Go": "QML, JavaScript, Go"
|
||||||
},
|
},
|
||||||
@@ -1328,9 +1274,6 @@
|
|||||||
"Select which transitions to include in randomization": {
|
"Select which transitions to include in randomization": {
|
||||||
"Select which transitions to include in randomization": "含めたいトランジションをランダム化に選択"
|
"Select which transitions to include in randomization": "含めたいトランジションをランダム化に選択"
|
||||||
},
|
},
|
||||||
"Selected image file not found.": {
|
|
||||||
"Selected image file not found.": "選択した画像ファイルが見つかりませんでした。"
|
|
||||||
},
|
|
||||||
"Separator": {
|
"Separator": {
|
||||||
"Separator": "区切り"
|
"Separator": "区切り"
|
||||||
},
|
},
|
||||||
@@ -1601,9 +1544,6 @@
|
|||||||
"Use Fahrenheit instead of Celsius for temperature": {
|
"Use Fahrenheit instead of Celsius for temperature": {
|
||||||
"Use Fahrenheit instead of Celsius for temperature": "温度は摂氏ではなく華氏を使用"
|
"Use Fahrenheit instead of Celsius for temperature": "温度は摂氏ではなく華氏を使用"
|
||||||
},
|
},
|
||||||
"Use IP Location": {
|
|
||||||
"Use IP Location": "IP ロケーションの使用"
|
|
||||||
},
|
|
||||||
"Use Monospace Font": {
|
"Use Monospace Font": {
|
||||||
"Use Monospace Font": "等幅フォントを使用"
|
"Use Monospace Font": "等幅フォントを使用"
|
||||||
},
|
},
|
||||||
@@ -1685,12 +1625,6 @@
|
|||||||
"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.": {
|
||||||
"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.": "有効にすると、アプリはアルファベット順に並べ替えられます。無効にすると、アプリは使用頻度で並べ替えられます。"
|
||||||
},
|
},
|
||||||
"WiFi disabled": {
|
|
||||||
"WiFi disabled": ""
|
|
||||||
},
|
|
||||||
"WiFi enabled": {
|
|
||||||
"WiFi enabled": ""
|
|
||||||
},
|
|
||||||
"WiFi is off": {
|
"WiFi is off": {
|
||||||
"WiFi is off": "Wi-Fiはオフ中"
|
"WiFi is off": "Wi-Fiはオフ中"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -167,9 +167,6 @@
|
|||||||
"Automatically cycle through wallpapers in the same folder": {
|
"Automatically cycle through wallpapers in the same folder": {
|
||||||
"Automatically cycle through wallpapers in the same folder": "Circular automaticamente dentre os papéis de parede na mesma pasta"
|
"Automatically cycle through wallpapers in the same folder": "Circular automaticamente dentre os papéis de parede na mesma pasta"
|
||||||
},
|
},
|
||||||
"Automatically detect location based on IP address": {
|
|
||||||
"Automatically detect location based on IP address": ""
|
|
||||||
},
|
|
||||||
"Automatically determine your location using your IP address": {
|
"Automatically determine your location using your IP address": {
|
||||||
"Automatically determine your location using your IP address": "Detectar automaticamente a sua localização usando o seu endereço de IP"
|
"Automatically determine your location using your IP address": "Detectar automaticamente a sua localização usando o seu endereço de IP"
|
||||||
},
|
},
|
||||||
@@ -353,9 +350,6 @@
|
|||||||
"Compositor:": {
|
"Compositor:": {
|
||||||
"Compositor:": "Compositor:"
|
"Compositor:": "Compositor:"
|
||||||
},
|
},
|
||||||
"Configuration activated": {
|
|
||||||
"Configuration activated": ""
|
|
||||||
},
|
|
||||||
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": {
|
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": {
|
||||||
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": "Configurações de ícones para área de trabalho nomeadas. Ícones tem prioridade sobre números, isso quando ambos estão habilitados. "
|
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": "Configurações de ícones para área de trabalho nomeadas. Ícones tem prioridade sobre números, isso quando ambos estão habilitados. "
|
||||||
},
|
},
|
||||||
@@ -371,9 +365,6 @@
|
|||||||
"Connected Displays": {
|
"Connected Displays": {
|
||||||
"Connected Displays": "Telas Conectadas"
|
"Connected Displays": "Telas Conectadas"
|
||||||
},
|
},
|
||||||
"Connection failed. Check password and try again.": {
|
|
||||||
"Connection failed. Check password and try again.": ""
|
|
||||||
},
|
|
||||||
"Contrast": {
|
"Contrast": {
|
||||||
"Contrast": "Contraste"
|
"Contrast": "Contraste"
|
||||||
},
|
},
|
||||||
@@ -494,18 +485,12 @@
|
|||||||
"Disconnect": {
|
"Disconnect": {
|
||||||
"Disconnect": "Desconectar"
|
"Disconnect": "Desconectar"
|
||||||
},
|
},
|
||||||
"Disconnected from WiFi": {
|
|
||||||
"Disconnected from WiFi": ""
|
|
||||||
},
|
|
||||||
"Disk": {
|
"Disk": {
|
||||||
"Disk": "Disco"
|
"Disk": "Disco"
|
||||||
},
|
},
|
||||||
"Disk Usage": {
|
"Disk Usage": {
|
||||||
"Disk Usage": "Uso de Disco"
|
"Disk Usage": "Uso de Disco"
|
||||||
},
|
},
|
||||||
"Dismiss": {
|
|
||||||
"Dismiss": ""
|
|
||||||
},
|
|
||||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
||||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "Exibir um dock com aplicativos que estão sendo utilizados, e que pode ser posicionada no superior, inferior, esquerda ou direita dos cantos da tela"
|
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "Exibir um dock com aplicativos que estão sendo utilizados, e que pode ser posicionada no superior, inferior, esquerda ou direita dos cantos da tela"
|
||||||
},
|
},
|
||||||
@@ -608,27 +593,6 @@
|
|||||||
"F1/I: Toggle • F10: Help": {
|
"F1/I: Toggle • F10: Help": {
|
||||||
"F1/I: Toggle • F10: Help": "F1/I: Ativar • F10: Ajuda"
|
"F1/I: Toggle • F10: Help": "F1/I: Ativar • F10: Ajuda"
|
||||||
},
|
},
|
||||||
"Failed to activate configuration": {
|
|
||||||
"Failed to activate configuration": ""
|
|
||||||
},
|
|
||||||
"Failed to connect to ": {
|
|
||||||
"Failed to connect to ": ""
|
|
||||||
},
|
|
||||||
"Failed to disconnect WiFi": {
|
|
||||||
"Failed to disconnect WiFi": ""
|
|
||||||
},
|
|
||||||
"Failed to enable WiFi": {
|
|
||||||
"Failed to enable WiFi": ""
|
|
||||||
},
|
|
||||||
"Failed to set profile image": {
|
|
||||||
"Failed to set profile image": ""
|
|
||||||
},
|
|
||||||
"Failed to set profile image: ": {
|
|
||||||
"Failed to set profile image: ": ""
|
|
||||||
},
|
|
||||||
"Failed to start connection to ": {
|
|
||||||
"Failed to start connection to ": ""
|
|
||||||
},
|
|
||||||
"Feels Like": {
|
"Feels Like": {
|
||||||
"Feels Like": "Sensação Térmica"
|
"Feels Like": "Sensação Térmica"
|
||||||
},
|
},
|
||||||
@@ -671,9 +635,6 @@
|
|||||||
"Forget Network": {
|
"Forget Network": {
|
||||||
"Forget Network": "Esquecer Internet"
|
"Forget Network": "Esquecer Internet"
|
||||||
},
|
},
|
||||||
"Forgot network ": {
|
|
||||||
"Forgot network ": ""
|
|
||||||
},
|
|
||||||
"Format Legend": {
|
"Format Legend": {
|
||||||
"Format Legend": "Formatar Legenda"
|
"Format Legend": "Formatar Legenda"
|
||||||
},
|
},
|
||||||
@@ -698,9 +659,6 @@
|
|||||||
"Gamma Control": {
|
"Gamma Control": {
|
||||||
"Gamma Control": "Controle de Gama"
|
"Gamma Control": "Controle de Gama"
|
||||||
},
|
},
|
||||||
"Gamma control not available. Requires DMS API v6+.": {
|
|
||||||
"Gamma control not available. Requires DMS API v6+.": ""
|
|
||||||
},
|
|
||||||
"Geoclue service not running - cannot auto-detect location": {
|
"Geoclue service not running - cannot auto-detect location": {
|
||||||
"Geoclue service not running - cannot auto-detect location": ""
|
"Geoclue service not running - cannot auto-detect location": ""
|
||||||
},
|
},
|
||||||
@@ -779,9 +737,6 @@
|
|||||||
"Include Transitions": {
|
"Include Transitions": {
|
||||||
"Include Transitions": "Incluir Transições"
|
"Include Transitions": "Incluir Transições"
|
||||||
},
|
},
|
||||||
"Incorrect password": {
|
|
||||||
"Incorrect password": ""
|
|
||||||
},
|
|
||||||
"Individual Batteries": {
|
"Individual Batteries": {
|
||||||
"Individual Batteries": "Baterias Individuais"
|
"Individual Batteries": "Baterias Individuais"
|
||||||
},
|
},
|
||||||
@@ -1115,9 +1070,6 @@
|
|||||||
"Percentage": {
|
"Percentage": {
|
||||||
"Percentage": "Porcetagem"
|
"Percentage": "Porcetagem"
|
||||||
},
|
},
|
||||||
"Permission denied to set profile image.": {
|
|
||||||
"Permission denied to set profile image.": ""
|
|
||||||
},
|
|
||||||
"Personalization": {
|
"Personalization": {
|
||||||
"Personalization": "Personalização"
|
"Personalization": "Personalização"
|
||||||
},
|
},
|
||||||
@@ -1196,12 +1148,6 @@
|
|||||||
"Process": {
|
"Process": {
|
||||||
"Process": "Processo"
|
"Process": "Processo"
|
||||||
},
|
},
|
||||||
"Profile Image Error": {
|
|
||||||
"Profile Image Error": ""
|
|
||||||
},
|
|
||||||
"Profile image is too large. Please use a smaller image.": {
|
|
||||||
"Profile image is too large. Please use a smaller image.": ""
|
|
||||||
},
|
|
||||||
"QML, JavaScript, Go": {
|
"QML, JavaScript, Go": {
|
||||||
"QML, JavaScript, Go": "QML, JavaScript, Go"
|
"QML, JavaScript, Go": "QML, JavaScript, Go"
|
||||||
},
|
},
|
||||||
@@ -1328,9 +1274,6 @@
|
|||||||
"Select which transitions to include in randomization": {
|
"Select which transitions to include in randomization": {
|
||||||
"Select which transitions to include in randomization": "Selecionar quais transições incluir na randomização"
|
"Select which transitions to include in randomization": "Selecionar quais transições incluir na randomização"
|
||||||
},
|
},
|
||||||
"Selected image file not found.": {
|
|
||||||
"Selected image file not found.": ""
|
|
||||||
},
|
|
||||||
"Separator": {
|
"Separator": {
|
||||||
"Separator": "Separador"
|
"Separator": "Separador"
|
||||||
},
|
},
|
||||||
@@ -1601,9 +1544,6 @@
|
|||||||
"Use Fahrenheit instead of Celsius for temperature": {
|
"Use Fahrenheit instead of Celsius for temperature": {
|
||||||
"Use Fahrenheit instead of Celsius for temperature": "Usar Fahrenheit em vez de Celsius para temperatura"
|
"Use Fahrenheit instead of Celsius for temperature": "Usar Fahrenheit em vez de Celsius para temperatura"
|
||||||
},
|
},
|
||||||
"Use IP Location": {
|
|
||||||
"Use IP Location": ""
|
|
||||||
},
|
|
||||||
"Use Monospace Font": {
|
"Use Monospace Font": {
|
||||||
"Use Monospace Font": "Usar Fonte Monoespaçada"
|
"Use Monospace Font": "Usar Fonte Monoespaçada"
|
||||||
},
|
},
|
||||||
@@ -1685,12 +1625,6 @@
|
|||||||
"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.": {
|
||||||
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": "Quando ativado, apps são ordenados alfabeticamente. Quando desativado, apps são ordenados por frequência de uso"
|
"When enabled, apps are sorted alphabetically. When disabled, apps are sorted by usage frequency.": "Quando ativado, apps são ordenados alfabeticamente. Quando desativado, apps são ordenados por frequência de uso"
|
||||||
},
|
},
|
||||||
"WiFi disabled": {
|
|
||||||
"WiFi disabled": ""
|
|
||||||
},
|
|
||||||
"WiFi enabled": {
|
|
||||||
"WiFi enabled": ""
|
|
||||||
},
|
|
||||||
"WiFi is off": {
|
"WiFi is off": {
|
||||||
"WiFi is off": "WiFi desligado"
|
"WiFi is off": "WiFi desligado"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -167,9 +167,6 @@
|
|||||||
"Automatically cycle through wallpapers in the same folder": {
|
"Automatically cycle through wallpapers in the same folder": {
|
||||||
"Automatically cycle through wallpapers in the same folder": "自动轮换文件夹中的壁纸"
|
"Automatically cycle through wallpapers in the same folder": "自动轮换文件夹中的壁纸"
|
||||||
},
|
},
|
||||||
"Automatically detect location based on IP address": {
|
|
||||||
"Automatically detect location based on IP address": "根据 IP 地址自动检测位置"
|
|
||||||
},
|
|
||||||
"Automatically determine your location using your IP address": {
|
"Automatically determine your location using your IP address": {
|
||||||
"Automatically determine your location using your IP address": "通过 IP 地址自动检测您的位置"
|
"Automatically determine your location using your IP address": "通过 IP 地址自动检测您的位置"
|
||||||
},
|
},
|
||||||
@@ -353,9 +350,6 @@
|
|||||||
"Compositor:": {
|
"Compositor:": {
|
||||||
"Compositor:": "合成器:"
|
"Compositor:": "合成器:"
|
||||||
},
|
},
|
||||||
"Configuration activated": {
|
|
||||||
"Configuration activated": ""
|
|
||||||
},
|
|
||||||
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": {
|
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": {
|
||||||
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": "为已命名工作区配置图标。当数字和图标同时启用时,图标优先生效。"
|
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": "为已命名工作区配置图标。当数字和图标同时启用时,图标优先生效。"
|
||||||
},
|
},
|
||||||
@@ -371,9 +365,6 @@
|
|||||||
"Connected Displays": {
|
"Connected Displays": {
|
||||||
"Connected Displays": "已连接显示器"
|
"Connected Displays": "已连接显示器"
|
||||||
},
|
},
|
||||||
"Connection failed. Check password and try again.": {
|
|
||||||
"Connection failed. Check password and try again.": ""
|
|
||||||
},
|
|
||||||
"Contrast": {
|
"Contrast": {
|
||||||
"Contrast": "对比度"
|
"Contrast": "对比度"
|
||||||
},
|
},
|
||||||
@@ -494,18 +485,12 @@
|
|||||||
"Disconnect": {
|
"Disconnect": {
|
||||||
"Disconnect": "断开连接"
|
"Disconnect": "断开连接"
|
||||||
},
|
},
|
||||||
"Disconnected from WiFi": {
|
|
||||||
"Disconnected from WiFi": ""
|
|
||||||
},
|
|
||||||
"Disk": {
|
"Disk": {
|
||||||
"Disk": "磁盘"
|
"Disk": "磁盘"
|
||||||
},
|
},
|
||||||
"Disk Usage": {
|
"Disk Usage": {
|
||||||
"Disk Usage": "磁盘占用"
|
"Disk Usage": "磁盘占用"
|
||||||
},
|
},
|
||||||
"Dismiss": {
|
|
||||||
"Dismiss": ""
|
|
||||||
},
|
|
||||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
||||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "显示一个包含固定和运行中应用的程序坞,可放置在屏幕四边任意位置"
|
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "显示一个包含固定和运行中应用的程序坞,可放置在屏幕四边任意位置"
|
||||||
},
|
},
|
||||||
@@ -608,27 +593,6 @@
|
|||||||
"F1/I: Toggle • F10: Help": {
|
"F1/I: Toggle • F10: Help": {
|
||||||
"F1/I: Toggle • F10: Help": "F1/I: 切换 • F10: 帮助"
|
"F1/I: Toggle • F10: Help": "F1/I: 切换 • F10: 帮助"
|
||||||
},
|
},
|
||||||
"Failed to activate configuration": {
|
|
||||||
"Failed to activate configuration": ""
|
|
||||||
},
|
|
||||||
"Failed to connect to ": {
|
|
||||||
"Failed to connect to ": ""
|
|
||||||
},
|
|
||||||
"Failed to disconnect WiFi": {
|
|
||||||
"Failed to disconnect WiFi": ""
|
|
||||||
},
|
|
||||||
"Failed to enable WiFi": {
|
|
||||||
"Failed to enable WiFi": ""
|
|
||||||
},
|
|
||||||
"Failed to set profile image": {
|
|
||||||
"Failed to set profile image": "无法设置个人资料图片"
|
|
||||||
},
|
|
||||||
"Failed to set profile image: ": {
|
|
||||||
"Failed to set profile image: ": "无法设置个人资料图片:"
|
|
||||||
},
|
|
||||||
"Failed to start connection to ": {
|
|
||||||
"Failed to start connection to ": ""
|
|
||||||
},
|
|
||||||
"Feels Like": {
|
"Feels Like": {
|
||||||
"Feels Like": "体感温度"
|
"Feels Like": "体感温度"
|
||||||
},
|
},
|
||||||
@@ -671,9 +635,6 @@
|
|||||||
"Forget Network": {
|
"Forget Network": {
|
||||||
"Forget Network": "取消保存"
|
"Forget Network": "取消保存"
|
||||||
},
|
},
|
||||||
"Forgot network ": {
|
|
||||||
"Forgot network ": ""
|
|
||||||
},
|
|
||||||
"Format Legend": {
|
"Format Legend": {
|
||||||
"Format Legend": "参考格式"
|
"Format Legend": "参考格式"
|
||||||
},
|
},
|
||||||
@@ -698,9 +659,6 @@
|
|||||||
"Gamma Control": {
|
"Gamma Control": {
|
||||||
"Gamma Control": "伽马控制"
|
"Gamma Control": "伽马控制"
|
||||||
},
|
},
|
||||||
"Gamma control not available. Requires DMS API v6+.": {
|
|
||||||
"Gamma control not available. Requires DMS API v6+.": "伽玛控制不可用。需要 DMS API v6+。"
|
|
||||||
},
|
|
||||||
"Geoclue service not running - cannot auto-detect location": {
|
"Geoclue service not running - cannot auto-detect location": {
|
||||||
"Geoclue service not running - cannot auto-detect location": "Geoclue 服务未运行 - 无法自动检测位置"
|
"Geoclue service not running - cannot auto-detect location": "Geoclue 服务未运行 - 无法自动检测位置"
|
||||||
},
|
},
|
||||||
@@ -779,9 +737,6 @@
|
|||||||
"Include Transitions": {
|
"Include Transitions": {
|
||||||
"Include Transitions": "包含过渡效果"
|
"Include Transitions": "包含过渡效果"
|
||||||
},
|
},
|
||||||
"Incorrect password": {
|
|
||||||
"Incorrect password": ""
|
|
||||||
},
|
|
||||||
"Individual Batteries": {
|
"Individual Batteries": {
|
||||||
"Individual Batteries": "分别显示电池"
|
"Individual Batteries": "分别显示电池"
|
||||||
},
|
},
|
||||||
@@ -1115,9 +1070,6 @@
|
|||||||
"Percentage": {
|
"Percentage": {
|
||||||
"Percentage": "占用率"
|
"Percentage": "占用率"
|
||||||
},
|
},
|
||||||
"Permission denied to set profile image.": {
|
|
||||||
"Permission denied to set profile image.": "因权限问题,无法设置个人资料图片。"
|
|
||||||
},
|
|
||||||
"Personalization": {
|
"Personalization": {
|
||||||
"Personalization": "个性化"
|
"Personalization": "个性化"
|
||||||
},
|
},
|
||||||
@@ -1196,12 +1148,6 @@
|
|||||||
"Process": {
|
"Process": {
|
||||||
"Process": "进程"
|
"Process": "进程"
|
||||||
},
|
},
|
||||||
"Profile Image Error": {
|
|
||||||
"Profile Image Error": "个人资料图片错误"
|
|
||||||
},
|
|
||||||
"Profile image is too large. Please use a smaller image.": {
|
|
||||||
"Profile image is too large. Please use a smaller image.": "个人资料图片太大。请选择更小的图片。"
|
|
||||||
},
|
|
||||||
"QML, JavaScript, Go": {
|
"QML, JavaScript, Go": {
|
||||||
"QML, JavaScript, Go": "QML, JavaScript, Go"
|
"QML, JavaScript, Go": "QML, JavaScript, Go"
|
||||||
},
|
},
|
||||||
@@ -1328,9 +1274,6 @@
|
|||||||
"Select which transitions to include in randomization": {
|
"Select which transitions to include in randomization": {
|
||||||
"Select which transitions to include in randomization": "选择在随机轮换中使用的过渡效果"
|
"Select which transitions to include in randomization": "选择在随机轮换中使用的过渡效果"
|
||||||
},
|
},
|
||||||
"Selected image file not found.": {
|
|
||||||
"Selected image file not found.": "找不到选定的图片文件。"
|
|
||||||
},
|
|
||||||
"Separator": {
|
"Separator": {
|
||||||
"Separator": "分隔符"
|
"Separator": "分隔符"
|
||||||
},
|
},
|
||||||
@@ -1601,9 +1544,6 @@
|
|||||||
"Use Fahrenheit instead of Celsius for temperature": {
|
"Use Fahrenheit instead of Celsius for temperature": {
|
||||||
"Use Fahrenheit instead of Celsius for temperature": "使用华氏度显示温度"
|
"Use Fahrenheit instead of Celsius for temperature": "使用华氏度显示温度"
|
||||||
},
|
},
|
||||||
"Use IP Location": {
|
|
||||||
"Use IP Location": "使用IP所在位置"
|
|
||||||
},
|
|
||||||
"Use Monospace Font": {
|
"Use Monospace Font": {
|
||||||
"Use Monospace Font": "使用等宽字体"
|
"Use Monospace Font": "使用等宽字体"
|
||||||
},
|
},
|
||||||
@@ -1685,12 +1625,6 @@
|
|||||||
"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.": {
|
||||||
"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.": "启用后,应用按字母顺序排序;禁用则按使用频率排序"
|
||||||
},
|
},
|
||||||
"WiFi disabled": {
|
|
||||||
"WiFi disabled": ""
|
|
||||||
},
|
|
||||||
"WiFi enabled": {
|
|
||||||
"WiFi enabled": ""
|
|
||||||
},
|
|
||||||
"WiFi is off": {
|
"WiFi is off": {
|
||||||
"WiFi is off": "Wi-Fi 已关闭"
|
"WiFi is off": "Wi-Fi 已关闭"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -349,6 +349,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Auto-location",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Auto-saving...",
|
"term": "Auto-saving...",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -384,13 +391,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Automatically detect location based on IP address",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Automatically determine your location using your IP address",
|
"term": "Automatically determine your location using your IP address",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -818,13 +818,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Configuration activated",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Configure icons for named workspaces. Icons take priority over numbers when both are enabled.",
|
"term": "Configure icons for named workspaces. Icons take priority over numbers when both are enabled.",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -860,13 +853,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Connection failed. Check password and try again.",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Contrast",
|
"term": "Contrast",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1147,13 +1133,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Disconnected from WiFi",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Disk",
|
"term": "Disk",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1168,13 +1147,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Dismiss",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen",
|
"term": "Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1413,34 +1385,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Failed to activate configuration",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "Failed to connect to ",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "Failed to disconnect WiFi",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "Failed to enable WiFi",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Failed to set profile image",
|
"term": "Failed to set profile image",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1455,13 +1399,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Failed to start connection to ",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Feels Like",
|
"term": "Feels Like",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1560,13 +1497,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Forgot network ",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Format Legend",
|
"term": "Format Legend",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1624,7 +1554,7 @@
|
|||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Gamma control not available. Requires DMS API v6+.",
|
"term": "Geoclue service not running - cannot auto-detect location",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
"context": "",
|
"context": "",
|
||||||
"reference": "",
|
"reference": "",
|
||||||
@@ -1805,13 +1735,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Incorrect password",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Individual Batteries",
|
"term": "Individual Batteries",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -3723,13 +3646,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Use IP Location",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Use Monospace Font",
|
"term": "Use Monospace Font",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -3751,6 +3667,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Use automatic location detection (geoclue2)",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Use custom command for update your system",
|
"term": "Use custom command for update your system",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -3912,20 +3835,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "WiFi disabled",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term": "WiFi enabled",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "WiFi is off",
|
"term": "WiFi is off",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
|
|||||||
5884
wall.patch
Normal file
5884
wall.patch
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user