mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
Revert
This commit is contained in:
82
.github/workflows/poeditor-export.yml
vendored
82
.github/workflows/poeditor-export.yml
vendored
@@ -17,9 +17,91 @@ 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,6 +207,9 @@ 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,7 +441,10 @@ Singleton {
|
|||||||
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode)
|
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode)
|
||||||
SessionData.setLightMode(light)
|
SessionData.setLightMode(light)
|
||||||
if (!isGreeterMode) {
|
if (!isGreeterMode) {
|
||||||
PortalService.setLightMode(light)
|
// Skip with matugen becuase, our script runner will do it.
|
||||||
|
if (!matugenAvailable) {
|
||||||
|
PortalService.setLightMode(light)
|
||||||
|
}
|
||||||
generateSystemThemesFromCurrentTheme()
|
generateSystemThemesFromCurrentTheme()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -719,15 +722,16 @@ 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}' --run`
|
`sleep 1 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' '${configDir}' '${syncModeWithPortal}' --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, "--run"]
|
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, configDir, syncModeWithPortal, "--run"]
|
||||||
}
|
}
|
||||||
systemThemeGenerator.running = true
|
systemThemeGenerator.running = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,11 @@
|
|||||||
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
|
||||||
|
|
||||||
Item {
|
Scope {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
WlSessionLock {
|
GreeterSurface {}
|
||||||
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,6 +22,7 @@ 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
|
||||||
|
|
||||||
@@ -63,8 +64,11 @@ 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()
|
||||||
@@ -193,17 +197,19 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyLoader {
|
WifiPasswordModal {
|
||||||
id: wifiPasswordModalLoader
|
id: wifiPasswordModal
|
||||||
|
|
||||||
active: false
|
Component.onCompleted: {
|
||||||
|
PopoutService.wifiPasswordModal = wifiPasswordModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WifiPasswordModal {
|
Connections {
|
||||||
id: wifiPasswordModal
|
target: NetworkService
|
||||||
|
|
||||||
Component.onCompleted: {
|
function onCredentialsNeeded(token, ssid, setting, fields, hints, reason) {
|
||||||
PopoutService.wifiPasswordModal = wifiPasswordModal
|
wifiPasswordModal.showFromPrompt(token, ssid, setting, fields, hints, reason)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,6 +502,7 @@ Item {
|
|||||||
notepadSlideoutVariants: notepadSlideoutVariants
|
notepadSlideoutVariants: notepadSlideoutVariants
|
||||||
hyprKeybindsModalLoader: hyprKeybindsModalLoader
|
hyprKeybindsModalLoader: hyprKeybindsModalLoader
|
||||||
dankBarLoader: dankBarLoader
|
dankBarLoader: dankBarLoader
|
||||||
|
hyprlandOverviewLoader: hyprlandOverviewLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
Variants {
|
Variants {
|
||||||
@@ -538,4 +545,12 @@ Item {
|
|||||||
modelData: item
|
modelData: item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: hyprlandOverviewLoader
|
||||||
|
active: CompositorService.isHyprland
|
||||||
|
component: HyprlandOverview {
|
||||||
|
id: hyprlandOverview
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ 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() {
|
||||||
@@ -347,6 +348,30 @@ 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,6 +7,10 @@ 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
|
||||||
|
|
||||||
@@ -90,19 +94,32 @@ 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: Quickshell.iconPath(model.icon, true)
|
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: status === Image.Ready
|
visible: !parent.isMaterial && status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !listIconImg.visible
|
visible: !parent.isMaterial && !listIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 1
|
||||||
@@ -120,7 +137,7 @@ Rectangle {
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.width - resultsList.iconSize - Theme.spacingL
|
width: (model.icon !== undefined && model.icon !== "") ? (parent.width - resultsList.iconSize - Theme.spacingL) : parent.width
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -255,20 +272,33 @@ 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: Quickshell.iconPath(model.icon, true)
|
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: status === Image.Ready
|
visible: !parent.isMaterial && status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !gridIconImg.visible
|
visible: !parent.isMaterial && !gridIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|||||||
@@ -15,12 +15,23 @@ 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
|
||||||
@@ -37,6 +48,41 @@ 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
|
||||||
@@ -60,6 +106,9 @@ DankModal {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
|
if (isPromptMode) {
|
||||||
|
NetworkService.cancelCredentials(promptToken)
|
||||||
|
}
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
@@ -90,6 +139,9 @@ 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 = ""
|
||||||
@@ -117,12 +169,28 @@ DankModal {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
Column {
|
||||||
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
|
||||||
elide: Text.ElideRight
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +199,9 @@ 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 = ""
|
||||||
@@ -208,14 +279,26 @@ DankModal {
|
|||||||
wifiPasswordInput = text
|
wifiPasswordInput = text
|
||||||
}
|
}
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
const username = requiresEnterprise ? usernameInput.text : ""
|
if (isPromptMode) {
|
||||||
NetworkService.connectToWifi(
|
const secrets = {}
|
||||||
wifiPasswordSSID,
|
if (promptSetting === "802-11-wireless-security") {
|
||||||
passwordInput.text,
|
secrets["psk"] = passwordInput.text
|
||||||
username,
|
} else if (promptSetting === "802-1x") {
|
||||||
wifiAnonymousIdentityInput,
|
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
||||||
wifiDomainInput
|
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||||
)
|
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 = ""
|
||||||
@@ -395,6 +478,9 @@ DankModal {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
|
if (isPromptMode) {
|
||||||
|
NetworkService.cancelCredentials(promptToken)
|
||||||
|
}
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
@@ -430,14 +516,26 @@ DankModal {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
const username = requiresEnterprise ? usernameInput.text : ""
|
if (isPromptMode) {
|
||||||
NetworkService.connectToWifi(
|
const secrets = {}
|
||||||
wifiPasswordSSID,
|
if (promptSetting === "802-11-wireless-security") {
|
||||||
passwordInput.text,
|
secrets["psk"] = passwordInput.text
|
||||||
username,
|
} else if (promptSetting === "802-1x") {
|
||||||
wifiAnonymousIdentityInput,
|
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
||||||
wifiDomainInput
|
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||||
)
|
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,16 +404,29 @@ 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: Quickshell.iconPath(model.icon, true)
|
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: status === Image.Ready
|
visible: !parent.isMaterial && status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -421,7 +434,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: !listIconImg.visible
|
visible: !parent.isMaterial && listIconImg.status !== Image.Ready
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 0
|
border.width: 0
|
||||||
@@ -435,11 +448,12 @@ DankPopout {
|
|||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.width - appList.iconSize - Theme.spacingL
|
width: (model.icon !== undefined && model.icon !== "") ? (parent.width - appList.iconSize - Theme.spacingL) : parent.width
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -513,6 +527,7 @@ 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
|
||||||
@@ -578,6 +593,19 @@ 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
|
||||||
@@ -586,10 +614,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: Quickshell.iconPath(model.icon, true)
|
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: status === Image.Ready
|
visible: !parent.isMaterial && status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -597,7 +625,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: !gridIconImg.visible
|
visible: !parent.isMaterial && gridIconImg.status !== Image.Ready
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 0
|
border.width: 0
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ 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"
|
||||||
@@ -163,7 +167,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 || "application-x-executable",
|
"icon": app.icon !== undefined ? app.icon : (isPluginItem ? "" : "application-x-executable"),
|
||||||
"comment": app.comment || "",
|
"comment": app.comment || "",
|
||||||
"categories": app.categories || [],
|
"categories": app.categories || [],
|
||||||
"isPlugin": isPluginItem,
|
"isPlugin": isPluginItem,
|
||||||
|
|||||||
@@ -509,7 +509,11 @@ 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) {
|
||||||
wifiPasswordModal.show(modelData.ssid)
|
if (DMSService.apiVersion >= 7) {
|
||||||
|
NetworkService.connectToWifi(modelData.ssid)
|
||||||
|
} else if (PopoutService.wifiPasswordModal) {
|
||||||
|
PopoutService.wifiPasswordModal.show(modelData.ssid)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
NetworkService.connectToWifi(modelData.ssid)
|
NetworkService.connectToWifi(modelData.ssid)
|
||||||
}
|
}
|
||||||
@@ -563,7 +567,11 @@ Rectangle {
|
|||||||
NetworkService.disconnectWifi()
|
NetworkService.disconnectWifi()
|
||||||
} else {
|
} else {
|
||||||
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
||||||
wifiPasswordModal.show(networkContextMenu.currentSSID)
|
if (DMSService.apiVersion >= 7) {
|
||||||
|
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
||||||
|
} else if (PopoutService.wifiPasswordModal) {
|
||||||
|
PopoutService.wifiPasswordModal.show(networkContextMenu.currentSSID)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
||||||
}
|
}
|
||||||
@@ -618,10 +626,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WifiPasswordModal {
|
|
||||||
id: wifiPasswordModal
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkInfoModal {
|
NetworkInfoModal {
|
||||||
id: networkInfoModal
|
id: networkInfoModal
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ 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 = ""
|
||||||
@@ -48,30 +49,6 @@ 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")
|
||||||
@@ -80,7 +57,6 @@ 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
|
||||||
@@ -103,27 +79,6 @@ 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":
|
||||||
@@ -818,6 +773,7 @@ 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()
|
||||||
@@ -831,6 +787,7 @@ Item {
|
|||||||
WorkspaceSwitcher {
|
WorkspaceSwitcher {
|
||||||
screenName: barWindow.screenName
|
screenName: barWindow.screenName
|
||||||
widgetHeight: barWindow.widgetThickness
|
widgetHeight: barWindow.widgetThickness
|
||||||
|
hyprlandOverviewLoader: root.hyprlandOverviewLoader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -868,17 +825,6 @@ 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) {
|
||||||
@@ -928,7 +874,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 = 3
|
dankDashPopoutLoader.item.currentTabIndex = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1262,15 +1208,4 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
|
||||||
target: "dankdash"
|
|
||||||
|
|
||||||
function wallpaper(): string {
|
|
||||||
if (root.triggerWallpaperBrowserOnFocusedScreen()) {
|
|
||||||
return "SUCCESS: Toggled wallpaper browser"
|
|
||||||
}
|
|
||||||
return "ERROR: Failed to toggle wallpaper browser"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,14 +74,20 @@ Rectangle {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
try {
|
||||||
const activeHyprToplevel = hyprlandToplevels.find(t => t.wayland === activeWindow)
|
if (!Hyprland.toplevels) return false
|
||||||
|
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,6 +17,7 @@ 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()
|
||||||
@@ -35,6 +36,8 @@ 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,22 +30,28 @@ Rectangle {
|
|||||||
if (!SettingsData.runningAppsGroupByApp) {
|
if (!SettingsData.runningAppsGroupByApp) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const appGroups = new Map();
|
try {
|
||||||
sortedToplevels.forEach((toplevel, index) => {
|
const appGroups = new Map();
|
||||||
const appId = toplevel.appId || "unknown";
|
sortedToplevels.forEach((toplevel, index) => {
|
||||||
if (!appGroups.has(appId)) {
|
if (!toplevel) return;
|
||||||
appGroups.set(appId, {
|
const appId = toplevel?.appId || "unknown";
|
||||||
appId: appId,
|
if (!appGroups.has(appId)) {
|
||||||
windows: []
|
appGroups.set(appId, {
|
||||||
|
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());
|
||||||
return Array.from(appGroups.values());
|
} catch (e) {
|
||||||
|
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,6 +15,7 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -244,11 +245,17 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.RightButton
|
||||||
|
|
||||||
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,8 +16,6 @@ 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
|
||||||
@@ -45,49 +43,15 @@ 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
|
||||||
}
|
}
|
||||||
@@ -103,18 +67,7 @@ DankPopout {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (root.shouldBeVisible) {
|
if (root.shouldBeVisible) {
|
||||||
mainContainer.forceActiveFocus()
|
forceActiveFocus()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onShouldBeVisibleChanged() {
|
|
||||||
if (root.shouldBeVisible) {
|
|
||||||
Qt.callLater(function() {
|
|
||||||
mainContainer.forceActiveFocus()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,46 +75,18 @@ DankPopout {
|
|||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
root.dashVisible = false
|
root.dashVisible = false
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (event.key === Qt.Key_Tab && !(event.modifiers & Qt.ShiftModifier)) {
|
Connections {
|
||||||
let nextIndex = root.currentTabIndex + 1
|
function onShouldBeVisibleChanged() {
|
||||||
while (nextIndex < tabBar.model.length && tabBar.model[nextIndex] && tabBar.model[nextIndex].isAction) {
|
if (root.shouldBeVisible) {
|
||||||
nextIndex++
|
Qt.callLater(function() {
|
||||||
}
|
mainContainer.forceActiveFocus()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
target: root
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -203,23 +128,11 @@ 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) {
|
||||||
@@ -235,7 +148,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onActionTriggered: function(index) {
|
onActionTriggered: function(index) {
|
||||||
let settingsIndex = SettingsData.weatherEnabled ? 4 : 3
|
let settingsIndex = SettingsData.weatherEnabled ? 3 : 2
|
||||||
if (index === settingsIndex) {
|
if (index === settingsIndex) {
|
||||||
dashVisible = false
|
dashVisible = false
|
||||||
settingsModal.show()
|
settingsModal.show()
|
||||||
@@ -255,8 +168,7 @@ 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 (currentIndex === 2) return wallpaperTab.implicitHeight
|
if (SettingsData.weatherEnabled && currentIndex === 2) return weatherTab.implicitHeight
|
||||||
if (SettingsData.weatherEnabled && currentIndex === 3) return weatherTab.implicitHeight
|
|
||||||
return overviewTab.implicitHeight
|
return overviewTab.implicitHeight
|
||||||
}
|
}
|
||||||
currentIndex: root.currentTabIndex
|
currentIndex: root.currentTabIndex
|
||||||
@@ -266,8 +178,8 @@ DankPopout {
|
|||||||
|
|
||||||
onSwitchToWeatherTab: {
|
onSwitchToWeatherTab: {
|
||||||
if (SettingsData.weatherEnabled) {
|
if (SettingsData.weatherEnabled) {
|
||||||
tabBar.currentIndex = 3
|
tabBar.currentIndex = 2
|
||||||
tabBar.tabClicked(3)
|
tabBar.tabClicked(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,16 +193,9 @@ 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 === 3
|
visible: SettingsData.weatherEnabled && root.currentTabIndex === 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,523 +0,0 @@
|
|||||||
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,7 +7,6 @@ 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
|
||||||
@@ -16,8 +15,6 @@ 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: ""
|
||||||
@@ -117,21 +114,6 @@ 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
|
||||||
@@ -673,180 +655,11 @@ 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: MprisController.activePlayer
|
visible: {
|
||||||
}
|
const keyboardVisible = (CompositorService.isNiri && NiriService.keyboardLayoutNames.length > 1) ||
|
||||||
|
(CompositorService.isHyprland && hyprlandLayoutCount > 1)
|
||||||
Row {
|
return keyboardVisible && WeatherService.weather.available
|
||||||
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 {
|
||||||
@@ -1247,7 +1060,6 @@ 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,18 +1,34 @@
|
|||||||
|
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
|
||||||
|
|
||||||
WlSessionLockSurface {
|
Variants {
|
||||||
id: root
|
model: Quickshell.screens
|
||||||
|
|
||||||
required property WlSessionLock lock
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
color: "transparent"
|
property var modelData
|
||||||
|
|
||||||
GreeterContent {
|
screen: modelData
|
||||||
anchors.fill: parent
|
anchors {
|
||||||
screenName: root.screen?.name ?? ""
|
left: true
|
||||||
sessionLock: root.lock
|
right: true
|
||||||
|
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 ?? ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
284
Modules/HyprWorkspaces/HyprlandOverview.qml
Normal file
284
Modules/HyprWorkspaces/HyprlandOverview.qml
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
428
Modules/HyprWorkspaces/OverviewWidget.qml
Normal file
428
Modules/HyprWorkspaces/OverviewWidget.qml
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
140
Modules/HyprWorkspaces/OverviewWindow.qml
Normal file
140
Modules/HyprWorkspaces/OverviewWindow.qml
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
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("Clear")
|
text: I18n.tr("Dismiss")
|
||||||
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("Clear")
|
text: I18n.tr("Dismiss")
|
||||||
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 All")
|
text: I18n.tr("Clear")
|
||||||
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("Clear")
|
readonly property string clearText: I18n.tr("Dismiss")
|
||||||
|
|
||||||
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
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
z: -1
|
z: -1
|
||||||
onEntered: {
|
onEntered: {
|
||||||
@@ -523,9 +523,20 @@ PanelWindow {
|
|||||||
if (notificationData && notificationData.popup && notificationData.timer)
|
if (notificationData && notificationData.popup && notificationData.timer)
|
||||||
notificationData.timer.restart()
|
notificationData.timer.restart()
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: (mouse) => {
|
||||||
if (notificationData && !win.exiting)
|
if (!notificationData || win.exiting)
|
||||||
notificationData.popup = false
|
return
|
||||||
|
|
||||||
|
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: "lightbulb",
|
icon: "material: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: "star",
|
icon: "material: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: "favorite",
|
icon: "material: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: "content_copy",
|
icon: "material: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: "terminal",
|
icon: "material: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,13 +88,41 @@ function executeItem(item): void
|
|||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
name: "Item Name", // Display name
|
name: "Item Name", // Display name
|
||||||
icon: "icon_name", // Material icon
|
icon: "icon_name", // Icon (optional, see Icon Types below)
|
||||||
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,11 +1221,51 @@ 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): Material Design icon name
|
- `icon` (string, optional): Icon specification (see Icon Types below)
|
||||||
- `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,43 +267,61 @@ 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
|
||||||
```
|
```
|
||||||
|
|
||||||
**3.2. Install latest dms CLI**
|
**FOR Stable Version, Checkout the latest tag**
|
||||||
|
|
||||||
|
```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.
|
||||||
|
|
||||||
**4. Optional Features (system monitoring, clipboard history, brightness controls, etc.)**
|
If preferred, you can build the dms-cli yourself (requires GO 1.24+)
|
||||||
|
|
||||||
**4.1 Core optional dependencies**
|
```bash
|
||||||
|
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
|
||||||
@@ -317,7 +335,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.
|
||||||
|
|
||||||
@@ -393,9 +411,6 @@ 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";
|
||||||
}
|
}
|
||||||
@@ -463,8 +478,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, Y, exec, dms ipc call dankdash wallpaper
|
bind = SUPER, TAB, exec, dms ipc call hypr toggleOverview
|
||||||
|
|
||||||
# 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,5 +1,4 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -17,67 +16,300 @@ 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: {
|
property var sortedToplevels: sortedToplevelsCache
|
||||||
if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values) {
|
property var sortedToplevelsCache: []
|
||||||
|
|
||||||
|
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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHyprland) {
|
let snap = []
|
||||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
let missingAnyPosition = false
|
||||||
|
let hasNewWindow = false
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const t = items[i]
|
||||||
|
if (!t) continue
|
||||||
|
|
||||||
const sortedHyprland = hyprlandToplevels.sort((a, b) => {
|
const addr = t.address || ""
|
||||||
if (a.monitor && b.monitor) {
|
const li = t.lastIpcObject || null
|
||||||
const monitorCompare = a.monitor.name.localeCompare(b.monitor.name)
|
|
||||||
if (monitorCompare !== 0) {
|
|
||||||
return monitorCompare
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.workspace && b.workspace) {
|
const monName = _get(li, ["monitor"], null) ?? _get(t, ["monitor", "name"], "")
|
||||||
const workspaceCompare = a.workspace.id - b.workspace.id
|
const monX = _get(t, ["monitor", "x"], Number.MAX_SAFE_INTEGER)
|
||||||
if (workspaceCompare !== 0) {
|
const monY = _get(t, ["monitor", "y"], Number.MAX_SAFE_INTEGER)
|
||||||
return workspaceCompare
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.lastIpcObject && b.lastIpcObject && a.lastIpcObject.at && b.lastIpcObject.at) {
|
const wsId = _get(li, ["workspace", "id"], null) ?? _get(t, ["workspace", "id"], Number.MAX_SAFE_INTEGER)
|
||||||
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 xCompare = aX - bX
|
const at = _get(li, ["at"], null)
|
||||||
if (Math.abs(xCompare) > 10) {
|
let atX = (at !== null && at !== undefined && typeof at[0] === "number") ? at[0] : NaN
|
||||||
return xCompare
|
let atY = (at !== null && at !== undefined && typeof at[1] === "number") ? at[1] : NaN
|
||||||
}
|
|
||||||
return aY - bY
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.lastIpcObject && !b.lastIpcObject) {
|
if (!(atX === atX) || !(atY === atY)) {
|
||||||
return -1
|
const cached = _coordCache[addr]
|
||||||
}
|
if (cached) {
|
||||||
if (!a.lastIpcObject && b.lastIpcObject) {
|
atX = cached.x
|
||||||
return 1
|
atY = cached.y
|
||||||
}
|
} else {
|
||||||
|
if (addr) hasNewWindow = true
|
||||||
|
missingAnyPosition = true
|
||||||
|
atX = 1e9
|
||||||
|
atY = 1e9
|
||||||
|
}
|
||||||
|
} else if (addr) {
|
||||||
|
_coordCache[addr] = { x: atX, y: atY }
|
||||||
|
}
|
||||||
|
|
||||||
if (a.title && b.title) {
|
const relX = Number.isFinite(monX) ? (atX - monX) : atX
|
||||||
return a.title.localeCompare(b.title)
|
const relY = Number.isFinite(monY) ? (atY - monY) : atY
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
snap.push({
|
||||||
})
|
monKey: String(monName),
|
||||||
|
monOrderX: Number.isFinite(monX) ? monX : Number.MAX_SAFE_INTEGER,
|
||||||
return sortedHyprland.map(hyprToplevel => hyprToplevel.wayland).filter(wayland => wayland !== null)
|
monOrderY: Number.isFinite(monY) ? monY : Number.MAX_SAFE_INTEGER,
|
||||||
|
wsId: (typeof wsId === "number") ? wsId : Number.MAX_SAFE_INTEGER,
|
||||||
|
x: relX,
|
||||||
|
y: relY,
|
||||||
|
title: t.title || "",
|
||||||
|
address: addr,
|
||||||
|
wayland: t.wayland
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return ToplevelManager.toplevels.values
|
if (missingAnyPosition && hasNewWindow) {
|
||||||
|
_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 {
|
||||||
@@ -91,86 +323,30 @@ 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", root.niriSocket], (output, exitCode) => {
|
Proc.runCommand("niriSocketCheck", ["test", "-S", niriSocket], (output, exitCode) => {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
root.isNiri = true
|
isNiri = true
|
||||||
root.isHyprland = false
|
isHyprland = false
|
||||||
root.compositor = "niri"
|
compositor = "niri"
|
||||||
console.log("CompositorService: Detected Niri with socket:", root.niriSocket)
|
console.log("CompositorService: Detected Niri with socket:", niriSocket)
|
||||||
NiriService.generateNiriBinds()
|
NiriService.generateNiriBinds()
|
||||||
} else {
|
} else {
|
||||||
root.isHyprland = false
|
isHyprland = false
|
||||||
root.isNiri = true
|
isNiri = true
|
||||||
root.compositor = "niri"
|
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)
|
||||||
@@ -183,22 +359,14 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function powerOffMonitors() {
|
function powerOffMonitors() {
|
||||||
if (isNiri) {
|
if (isNiri) return NiriService.powerOffMonitors()
|
||||||
return NiriService.powerOffMonitors()
|
if (isHyprland) return Hyprland.dispatch("dpms off")
|
||||||
}
|
|
||||||
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) {
|
if (isNiri) return NiriService.powerOnMonitors()
|
||||||
return NiriService.powerOnMonitors()
|
if (isHyprland) return Hyprland.dispatch("dpms on")
|
||||||
}
|
|
||||||
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,6 +41,7 @@ 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) {
|
||||||
@@ -261,6 +262,8 @@ 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,8 +79,21 @@ 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")
|
||||||
|
|
||||||
@@ -120,6 +133,10 @@ Singleton {
|
|||||||
function onCapabilitiesChanged() {
|
function onCapabilitiesChanged() {
|
||||||
checkDMSCapabilities()
|
checkDMSCapabilities()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onCredentialsRequest(data) {
|
||||||
|
handleCredentialsRequest(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDMSCapabilities() {
|
function checkDMSCapabilities() {
|
||||||
@@ -143,6 +160,18 @@ 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) {
|
||||||
@@ -177,6 +206,9 @@ 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 || ""
|
||||||
|
|
||||||
@@ -225,6 +257,45 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,11 +313,11 @@ Singleton {
|
|||||||
connectionError = response.error
|
connectionError = response.error
|
||||||
lastConnectionError = response.error
|
lastConnectionError = response.error
|
||||||
connectionStatus = "failed"
|
connectionStatus = "failed"
|
||||||
ToastService.showError(`Failed to activate configuration`)
|
ToastService.showError(I18n.tr("Failed to activate configuration"))
|
||||||
} else {
|
} else {
|
||||||
connectionError = ""
|
connectionError = ""
|
||||||
connectionStatus = "connected"
|
connectionStatus = "connected"
|
||||||
ToastService.showInfo(`Configuration activated`)
|
ToastService.showInfo(I18n.tr("Configuration activated"))
|
||||||
}
|
}
|
||||||
|
|
||||||
isConnecting = false
|
isConnecting = false
|
||||||
@@ -280,42 +351,47 @@ Singleton {
|
|||||||
function connectToWifi(ssid, password = "", username = "", anonymousIdentity = "", domainSuffixMatch = "") {
|
function connectToWifi(ssid, password = "", username = "", anonymousIdentity = "", domainSuffixMatch = "") {
|
||||||
if (!networkAvailable || isConnecting) return
|
if (!networkAvailable || isConnecting) return
|
||||||
|
|
||||||
connectingSSID = ssid
|
pendingConnectionSSID = 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 (username) params.username = username
|
if (DMSService.apiVersion >= 7) {
|
||||||
if (anonymousIdentity) params.anonymousIdentity = anonymousIdentity
|
if (password || username) {
|
||||||
if (domainSuffixMatch) params.domainSuffixMatch = domainSuffixMatch
|
params.password = password
|
||||||
|
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
|
||||||
connectionStatus = response.error.includes("password") || response.error.includes("authentication")
|
pendingConnectionSSID = ""
|
||||||
? "invalid_password"
|
connectionStatus = "failed"
|
||||||
: "failed"
|
ToastService.showError(I18n.tr("Failed to start connection to ") + ssid)
|
||||||
|
|
||||||
if (connectionStatus === "invalid_password") {
|
|
||||||
passwordDialogShouldReopen = true
|
|
||||||
ToastService.showError(`Invalid password for ${ssid}`)
|
|
||||||
} else {
|
|
||||||
ToastService.showError(`Failed to connect to ${ssid}`)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
connectionError = ""
|
if (DMSService.verboseLogs) {
|
||||||
connectionStatus = "connected"
|
console.log("NetworkManagerService: Connection request sent for", ssid)
|
||||||
ToastService.showInfo(`Connected to ${ssid}`)
|
|
||||||
|
|
||||||
if (userPreference === "wifi" || userPreference === "auto") {
|
|
||||||
setConnectionPriority("wifi")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isConnecting = false
|
|
||||||
connectingSSID = ""
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,15 +400,60 @@ Singleton {
|
|||||||
|
|
||||||
DMSService.sendRequest("network.wifi.disconnect", null, response => {
|
DMSService.sendRequest("network.wifi.disconnect", null, response => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
ToastService.showError("Failed to disconnect WiFi")
|
ToastService.showError(I18n.tr("Failed to disconnect WiFi"))
|
||||||
} else {
|
} else {
|
||||||
ToastService.showInfo("Disconnected from WiFi")
|
ToastService.showInfo(I18n.tr("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
|
||||||
|
|
||||||
@@ -341,7 +462,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(`Forgot network ${ssid}`)
|
ToastService.showInfo(I18n.tr("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)
|
||||||
@@ -374,7 +495,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 ? "WiFi enabled" : "WiFi disabled")
|
ToastService.showInfo(wifiEnabled ? I18n.tr("WiFi enabled") : I18n.tr("WiFi disabled"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -384,9 +505,9 @@ Singleton {
|
|||||||
|
|
||||||
DMSService.sendRequest("network.wifi.enable", null, response => {
|
DMSService.sendRequest("network.wifi.enable", null, response => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
ToastService.showError("Failed to enable WiFi")
|
ToastService.showError(I18n.tr("Failed to enable WiFi"))
|
||||||
} else {
|
} else {
|
||||||
ToastService.showInfo("WiFi enabled")
|
ToastService.showInfo(I18n.tr("WiFi enabled"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,8 +69,17 @@ 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
|
||||||
@@ -122,6 +131,9 @@ 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,4 +270,16 @@ 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,10 +19,6 @@ 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,7 +25,6 @@ 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
|
||||||
@@ -64,7 +63,7 @@ PanelWindow {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
WlrLayershell.layer: WlrLayershell.Top
|
WlrLayershell.layer: WlrLayershell.Top
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: shouldBeVisible ? keyboardFocusMode : WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: shouldBeVisible ? WlrKeyboardFocus.OnDemand : 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
|
||||||
|
|
||||||
FocusScope {
|
Item {
|
||||||
id: tabBar
|
id: tabBar
|
||||||
|
|
||||||
property alias model: tabRepeater.model
|
property alias model: tabRepeater.model
|
||||||
@@ -11,101 +11,12 @@ FocusScope {
|
|||||||
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
|
||||||
@@ -166,6 +77,7 @@ FocusScope {
|
|||||||
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,13 +501,6 @@ 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.
|
||||||
|
|
||||||
@@ -519,7 +512,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 keybinds cheatsheet modal control (Hyprland only).
|
Hyprland-specific controls including keybinds cheatsheet and workspace overview (Hyprland only).
|
||||||
|
|
||||||
**Functions:**
|
**Functions:**
|
||||||
- `openBinds` - Show Hyprland keybinds cheatsheet modal
|
- `openBinds` - Show Hyprland keybinds cheatsheet modal
|
||||||
@@ -531,13 +524,31 @@ Hyprland keybinds cheatsheet modal control (Hyprland only).
|
|||||||
- `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"
|
||||||
|
|
||||||
**Description:**
|
**Keybinds Cheatsheet 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
|
||||||
@@ -570,9 +581,6 @@ 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
|
||||||
@@ -580,6 +588,11 @@ 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
|
||||||
@@ -607,6 +620,7 @@ 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,6 +59,11 @@ 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" = {
|
||||||
@@ -81,13 +86,6 @@ 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,14 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if [ $# -lt 4 ]; then
|
if [ $# -lt 5 ]; then
|
||||||
echo "Usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR --run" >&2
|
echo "Usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR SYNC_MODE_WITH_PORTAL --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
|
||||||
@@ -25,10 +26,10 @@ if [ ! -d "$CONFIG_DIR" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
shift 3 # Remove STATE_DIR, SHELL_DIR, and CONFIG_DIR from arguments
|
shift 4
|
||||||
|
|
||||||
if [[ "${1:-}" != "--run" ]]; then
|
if [[ "${1:-}" != "--run" ]]; then
|
||||||
echo "usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR --run" >&2
|
echo "usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR SYNC_MODE_WITH_PORTAL --run" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -62,6 +63,27 @@ 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
|
||||||
@@ -237,7 +259,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; return 2; }
|
echo "$JSON" | grep -q '"primary"' || { echo "matugen JSON missing primary" >&2; set_system_color_scheme "$mode"; 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"
|
||||||
@@ -289,6 +311,8 @@ 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": "Modals/Settings/SettingsSidebar.qml:44, Modules/Settings/AboutTab.qml:251",
|
"reference": "Modules/Settings/AboutTab.qml:251, Modals/Settings/SettingsSidebar.qml:44",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
{
|
{
|
||||||
"term": "All",
|
"term": "All",
|
||||||
"context": "All",
|
"context": "All",
|
||||||
"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",
|
"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",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
{
|
{
|
||||||
"term": "All displays",
|
"term": "All displays",
|
||||||
"context": "All displays",
|
"context": "All displays",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:666",
|
"reference": "Modules/Settings/DisplaysTab.qml:670",
|
||||||
"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:282",
|
"reference": "Modals/WifiPasswordModal.qml:365",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -299,12 +299,6 @@
|
|||||||
"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...",
|
||||||
@@ -314,7 +308,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Automatic Control",
|
"term": "Automatic Control",
|
||||||
"context": "Automatic Control",
|
"context": "Automatic Control",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:162",
|
"reference": "Modules/Settings/DisplaysTab.qml:164",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -335,6 +329,12 @@
|
|||||||
"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:509",
|
"reference": "Modules/Settings/DisplaysTab.qml:513",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -524,7 +524,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Cancel",
|
"term": "Cancel",
|
||||||
"context": "Cancel",
|
"context": "Cancel",
|
||||||
"reference": "Modals/DankColorPickerModal.qml:510, Modals/WifiPasswordModal.qml:385, Modals/FileBrowser/FileBrowserModal.qml:952, Modules/Settings/PluginsTab.qml:1220",
|
"reference": "Modals/DankColorPickerModal.qml:510, Modals/WifiPasswordModal.qml:468, Modules/Settings/PluginsTab.qml:1220, Modals/FileBrowser/FileBrowserModal.qml:952",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -578,13 +578,13 @@
|
|||||||
{
|
{
|
||||||
"term": "Clear",
|
"term": "Clear",
|
||||||
"context": "Clear",
|
"context": "Clear",
|
||||||
"reference": "Modules/Notifications/Popup/NotificationPopup.qml:24, Modules/Notifications/Center/NotificationCard.qml:540, Modules/Notifications/Center/NotificationCard.qml:633",
|
"reference": "Modules/Notifications/Center/NotificationHeader.qml:102",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Clear All",
|
"term": "Clear All",
|
||||||
"context": "Clear All",
|
"context": "Clear All",
|
||||||
"reference": "Modals/Clipboard/ClipboardHistoryModal.qml:157, Modules/Notifications/Center/NotificationHeader.qml:102",
|
"reference": "Modals/Clipboard/ClipboardHistoryModal.qml:157",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -620,7 +620,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Close",
|
"term": "Close",
|
||||||
"context": "Close",
|
"context": "Close",
|
||||||
"reference": "Modules/SystemUpdatePopout.qml:333, Modals/NetworkWiredInfoModal.qml:129, Modals/NetworkInfoModal.qml:129, Modules/DankBar/Widgets/RunningApps.qml:711",
|
"reference": "Modals/NetworkInfoModal.qml:129, Modals/NetworkWiredInfoModal.qml:129, Modules/SystemUpdatePopout.qml:333, Modules/DankBar/Widgets/RunningApps.qml:717",
|
||||||
"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:143",
|
"reference": "Modules/Settings/DisplaysTab.qml:145",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -701,6 +701,12 @@
|
|||||||
"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.",
|
||||||
@@ -710,25 +716,31 @@
|
|||||||
{
|
{
|
||||||
"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:493",
|
"reference": "Modules/Settings/DisplaysTab.qml:497",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Connect",
|
"term": "Connect",
|
||||||
"context": "Connect",
|
"context": "Connect",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:419",
|
"reference": "Modals/WifiPasswordModal.qml:505",
|
||||||
"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:114",
|
"reference": "Modals/WifiPasswordModal.qml:166",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Connected Displays",
|
"term": "Connected Displays",
|
||||||
"context": "Connected Displays",
|
"context": "Connected Displays",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:486",
|
"reference": "Modules/Settings/DisplaysTab.qml:490",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "Connection failed. Check password and try again.",
|
||||||
|
"context": "Connection failed. Check password and try again.",
|
||||||
|
"reference": "Services/NetworkManagerService.qml:287",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -872,7 +884,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:234",
|
"reference": "Services/DMSService.qml:235",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -971,6 +983,12 @@
|
|||||||
"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",
|
||||||
@@ -983,6 +1001,12 @@
|
|||||||
"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",
|
||||||
@@ -1034,7 +1058,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Do Not Disturb",
|
"term": "Do Not Disturb",
|
||||||
"context": "Do Not Disturb",
|
"context": "Do Not Disturb",
|
||||||
"reference": "Modules/Notifications/Center/NotificationHeader.qml:41, Modules/Notifications/Center/NotificationSettings.qml:131",
|
"reference": "Modules/Notifications/Center/NotificationSettings.qml:131, Modules/Notifications/Center/NotificationHeader.qml:41",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1058,7 +1082,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Domain (optional)",
|
"term": "Domain (optional)",
|
||||||
"context": "Domain (optional)",
|
"context": "Domain (optional)",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:314",
|
"reference": "Modals/WifiPasswordModal.qml:397",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1136,13 +1160,13 @@
|
|||||||
{
|
{
|
||||||
"term": "End",
|
"term": "End",
|
||||||
"context": "End",
|
"context": "End",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:315",
|
"reference": "Modules/Settings/DisplaysTab.qml:318",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Enter credentials for ",
|
"term": "Enter credentials for ",
|
||||||
"context": "Enter credentials for ",
|
"context": "Enter credentials for ",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:121",
|
"reference": "Modals/WifiPasswordModal.qml:178",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1166,7 +1190,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Enter password for ",
|
"term": "Enter password for ",
|
||||||
"context": "Enter password for ",
|
"context": "Enter password for ",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:121",
|
"reference": "Modals/WifiPasswordModal.qml:178",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1187,6 +1211,30 @@
|
|||||||
"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",
|
||||||
@@ -1199,6 +1247,12 @@
|
|||||||
"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",
|
||||||
@@ -1238,7 +1292,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Font Family",
|
"term": "Font Family",
|
||||||
"context": "Font Family",
|
"context": "Font Family",
|
||||||
"reference": "Modules/Notepad/NotepadSettings.qml:220, Modules/Settings/ThemeColorsTab.qml:944",
|
"reference": "Modules/Settings/ThemeColorsTab.qml:944, Modules/Notepad/NotepadSettings.qml:220",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1280,7 +1334,13 @@
|
|||||||
{
|
{
|
||||||
"term": "Forget Network",
|
"term": "Forget Network",
|
||||||
"context": "Forget Network",
|
"context": "Forget Network",
|
||||||
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:598",
|
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:606",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"term": "Forgot network ",
|
||||||
|
"context": "Forgot network ",
|
||||||
|
"reference": "Services/NetworkManagerService.qml:465",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1332,9 +1392,9 @@
|
|||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Geoclue service not running - cannot auto-detect location",
|
"term": "Gamma control not available. Requires DMS API v6+.",
|
||||||
"context": "Geoclue service not running - cannot auto-detect location",
|
"context": "Gamma control not available. Requires DMS API v6+.",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:365",
|
"reference": "Modules/Settings/DisplaysTab.qml:119",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1424,7 +1484,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Hour",
|
"term": "Hour",
|
||||||
"context": "Hour",
|
"context": "Hour",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:252",
|
"reference": "Modules/Settings/DisplaysTab.qml:255",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1487,6 +1547,12 @@
|
|||||||
"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",
|
||||||
@@ -1574,13 +1640,13 @@
|
|||||||
{
|
{
|
||||||
"term": "Latitude",
|
"term": "Latitude",
|
||||||
"context": "Latitude",
|
"context": "Latitude",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:398, Modules/Settings/TimeWeatherTab.qml:665",
|
"reference": "Modules/Settings/DisplaysTab.qml:402, Modules/Settings/TimeWeatherTab.qml:665",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Launch",
|
"term": "Launch",
|
||||||
"context": "Launch",
|
"context": "Launch",
|
||||||
"reference": "Modals/Spotlight/SpotlightContextMenu.qml:251, Modules/AppDrawer/AppDrawerPopout.qml:895",
|
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:923, Modals/Spotlight/SpotlightContextMenu.qml:251",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1592,7 +1658,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Launch on dGPU",
|
"term": "Launch on dGPU",
|
||||||
"context": "Launch on dGPU",
|
"context": "Launch on dGPU",
|
||||||
"reference": "Modals/Spotlight/SpotlightContextMenu.qml:312, Modules/AppDrawer/AppDrawerPopout.qml:955, Modules/Dock/DockContextMenu.qml:417",
|
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:983, Modules/Dock/DockContextMenu.qml:417, Modals/Spotlight/SpotlightContextMenu.qml:312",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1682,7 +1748,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Longitude",
|
"term": "Longitude",
|
||||||
"context": "Longitude",
|
"context": "Longitude",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:421, Modules/Settings/TimeWeatherTab.qml:716",
|
"reference": "Modules/Settings/DisplaysTab.qml:425, Modules/Settings/TimeWeatherTab.qml:716",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1700,7 +1766,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Manual Coordinates",
|
"term": "Manual Coordinates",
|
||||||
"context": "Manual Coordinates",
|
"context": "Manual Coordinates",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:386",
|
"reference": "Modules/Settings/DisplaysTab.qml:390",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1724,7 +1790,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Matugen Palette",
|
"term": "Matugen Palette",
|
||||||
"context": "Matugen Palette",
|
"context": "Matugen Palette",
|
||||||
"reference": "Modules/Settings/ThemeColorsTab.qml:629, Modules/Settings/PersonalizationTab.qml:1276",
|
"reference": "Modules/Settings/PersonalizationTab.qml:1276, Modules/Settings/ThemeColorsTab.qml:629",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1790,7 +1856,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Minute",
|
"term": "Minute",
|
||||||
"context": "Minute",
|
"context": "Minute",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:260",
|
"reference": "Modules/Settings/DisplaysTab.qml:263",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1862,13 +1928,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:575",
|
"reference": "Modules/ControlCenter/Details/NetworkDetail.qml:340, Modules/ControlCenter/Details/NetworkDetail.qml:583",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Network Information",
|
"term": "Network Information",
|
||||||
"context": "Network Information",
|
"context": "Network Information",
|
||||||
"reference": "Modals/NetworkWiredInfoModal.qml:59, Modals/NetworkInfoModal.qml:59",
|
"reference": "Modals/NetworkInfoModal.qml:59, Modals/NetworkWiredInfoModal.qml:59",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1988,7 +2054,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Notepad",
|
"term": "Notepad",
|
||||||
"context": "Notepad",
|
"context": "Notepad",
|
||||||
"reference": "DMSShell.qml:407, Modules/Settings/DankBarTab.qml:175",
|
"reference": "DMSShell.qml:413, Modules/Settings/DankBarTab.qml:175",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2024,7 +2090,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Notification Popups",
|
"term": "Notification Popups",
|
||||||
"context": "Notification Popups",
|
"context": "Notification Popups",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:24, Modules/Settings/WidgetTweaksTab.qml:585",
|
"reference": "Modules/Settings/WidgetTweaksTab.qml:585, Modules/Settings/DisplaysTab.qml:24",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2078,7 +2144,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:163",
|
"reference": "Modules/Settings/DisplaysTab.qml:165",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2126,7 +2192,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Password",
|
"term": "Password",
|
||||||
"context": "Password",
|
"context": "Password",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:203",
|
"reference": "Modals/WifiPasswordModal.qml:274",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2168,7 +2234,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Pin to Dock",
|
"term": "Pin to Dock",
|
||||||
"context": "Pin to Dock",
|
"context": "Pin to Dock",
|
||||||
"reference": "Modals/Spotlight/SpotlightContextMenu.qml:110, Modals/Spotlight/SpotlightContextMenu.qml:113, Modules/AppDrawer/AppDrawerPopout.qml:758, Modules/Dock/DockContextMenu.qml:370",
|
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:786, Modules/Dock/DockContextMenu.qml:370, Modals/Spotlight/SpotlightContextMenu.qml:110, Modals/Spotlight/SpotlightContextMenu.qml:113",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2444,7 +2510,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Save",
|
"term": "Save",
|
||||||
"context": "Save",
|
"context": "Save",
|
||||||
"reference": "Modals/FileBrowser/FileBrowserModal.qml:818, Modules/Notepad/NotepadTextEditor.qml:511, Modules/Notepad/Notepad.qml:480",
|
"reference": "Modules/Notepad/Notepad.qml:480, Modules/Notepad/NotepadTextEditor.qml:511, Modals/FileBrowser/FileBrowserModal.qml:818",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2570,7 +2636,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/ThemeColorsTab.qml:630, Modules/Settings/PersonalizationTab.qml:1277",
|
"reference": "Modules/Settings/PersonalizationTab.qml:1277, Modules/Settings/ThemeColorsTab.qml:630",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2606,7 +2672,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Settings",
|
"term": "Settings",
|
||||||
"context": "Settings",
|
"context": "Settings",
|
||||||
"reference": "Services/AppSearchService.qml:176, Modals/Settings/SettingsModal.qml:165, Modules/DankDash/DankDashPopout.qml:142",
|
"reference": "Services/AppSearchService.qml:176, Modules/DankDash/DankDashPopout.qml:142, Modals/Settings/SettingsModal.qml:165",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2654,13 +2720,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:667",
|
"reference": "Modules/Settings/DisplaysTab.qml:671",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Show on screens:",
|
"term": "Show on screens:",
|
||||||
"context": "Show on screens:",
|
"context": "Show on screens:",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:651",
|
"reference": "Modules/Settings/DisplaysTab.qml:655",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2678,7 +2744,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Show password",
|
"term": "Show password",
|
||||||
"context": "Show password",
|
"context": "Show password",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:357",
|
"reference": "Modals/WifiPasswordModal.qml:440",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2768,7 +2834,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Start",
|
"term": "Start",
|
||||||
"context": "Start",
|
"context": "Start",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:272",
|
"reference": "Modules/Settings/DisplaysTab.qml:275",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2816,7 +2882,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Switch User",
|
"term": "Switch User",
|
||||||
"context": "Switch User",
|
"context": "Switch User",
|
||||||
"reference": "Modules/Greetd/GreeterContent.qml:567",
|
"reference": "Modules/Greetd/GreeterContent.qml:549",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2930,7 +2996,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Temperature",
|
"term": "Temperature",
|
||||||
"context": "Temperature",
|
"context": "Temperature",
|
||||||
"reference": "Modules/Settings/DisplaysTab.qml:142",
|
"reference": "Modules/Settings/DisplaysTab.qml:144",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3008,7 +3074,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:235",
|
"reference": "Services/DMSService.qml:236",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3068,7 +3134,7 @@
|
|||||||
{
|
{
|
||||||
"term": "Unpin from Dock",
|
"term": "Unpin from Dock",
|
||||||
"context": "Unpin from Dock",
|
"context": "Unpin from Dock",
|
||||||
"reference": "Modals/Spotlight/SpotlightContextMenu.qml:113, Modules/AppDrawer/AppDrawerPopout.qml:758, Modules/Dock/DockContextMenu.qml:370",
|
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:786, Modules/Dock/DockContextMenu.qml:370, Modals/Spotlight/SpotlightContextMenu.qml:113",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3125,6 +3191,12 @@
|
|||||||
"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",
|
||||||
@@ -3143,12 +3215,6 @@
|
|||||||
"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",
|
||||||
@@ -3188,13 +3254,13 @@
|
|||||||
{
|
{
|
||||||
"term": "Username",
|
"term": "Username",
|
||||||
"context": "Username",
|
"context": "Username",
|
||||||
"reference": "Modals/WifiPasswordModal.qml:166",
|
"reference": "Modals/WifiPasswordModal.qml:237",
|
||||||
"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:442",
|
"reference": "Modules/Settings/DisplaysTab.qml:446",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3287,6 +3353,18 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -349,13 +349,6 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"term": "Auto-location",
|
|
||||||
"translation": "",
|
|
||||||
"context": "",
|
|
||||||
"reference": "",
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"term": "Auto-saving...",
|
"term": "Auto-saving...",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -391,6 +384,13 @@
|
|||||||
"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,6 +818,13 @@
|
|||||||
"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": "",
|
||||||
@@ -853,6 +860,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Connection failed. Check password and try again.",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Contrast",
|
"term": "Contrast",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1133,6 +1147,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Disconnected from WiFi",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Disk",
|
"term": "Disk",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1147,6 +1168,13 @@
|
|||||||
"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": "",
|
||||||
@@ -1385,6 +1413,34 @@
|
|||||||
"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": "",
|
||||||
@@ -1399,6 +1455,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Failed to start connection to ",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Feels Like",
|
"term": "Feels Like",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1497,6 +1560,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Forgot network ",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Format Legend",
|
"term": "Format Legend",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -1554,7 +1624,7 @@
|
|||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"term": "Geoclue service not running - cannot auto-detect location",
|
"term": "Gamma control not available. Requires DMS API v6+.",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
"context": "",
|
"context": "",
|
||||||
"reference": "",
|
"reference": "",
|
||||||
@@ -1735,6 +1805,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Incorrect password",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Individual Batteries",
|
"term": "Individual Batteries",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -3646,6 +3723,13 @@
|
|||||||
"reference": "",
|
"reference": "",
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"term": "Use IP Location",
|
||||||
|
"translation": "",
|
||||||
|
"context": "",
|
||||||
|
"reference": "",
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"term": "Use Monospace Font",
|
"term": "Use Monospace Font",
|
||||||
"translation": "",
|
"translation": "",
|
||||||
@@ -3667,13 +3751,6 @@
|
|||||||
"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": "",
|
||||||
@@ -3835,6 +3912,20 @@
|
|||||||
"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
5884
wall.patch
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user