mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-07 04:52:06 -04:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03a8e1e0d5 | ||
|
|
4d4d3c20a1 | ||
|
|
cef16d6bc9 | ||
|
|
aafaad1791 | ||
|
|
7906fdc2b0 | ||
|
|
397650ca52 | ||
|
|
826207006a | ||
|
|
58c2fcd31c | ||
|
|
b2a2b425ec | ||
|
|
942c9c9609 | ||
|
|
46d6e1cff3 | ||
|
|
a4137c57c1 | ||
|
|
1ad8b627f1 | ||
|
|
58a02ce290 | ||
|
|
8e1ad1a2be | ||
|
|
68cd7ab32c | ||
|
|
f649ce9a8e | ||
|
|
c4df242f07 | ||
|
|
26846c8d55 | ||
|
|
31b44a667c | ||
|
|
4f3b73ee21 | ||
|
|
4cfae91f02 | ||
|
|
8d947a6e95 | ||
|
|
1e84d4252c | ||
|
|
76072e1d4c | ||
|
|
6408dce4a9 | ||
|
|
0b2e1cca38 | ||
|
|
c1bfd8c0b7 | ||
|
|
90ffa5833b | ||
|
|
169c669286 | ||
|
|
f8350deafc | ||
|
|
0286a1b80b | ||
|
|
7c3e6c1f02 | ||
|
|
d2d72db3c9 | ||
|
|
f81f861408 | ||
|
|
af494543f5 | ||
|
|
db4de55338 | ||
|
|
37ecbbbbde | ||
|
|
d6a6d2a438 | ||
|
|
bf1c6eec74 | ||
|
|
0ddae80584 | ||
|
|
5c96c03bfa | ||
|
|
dfe36e47d8 | ||
|
|
63e1b75e57 | ||
|
|
29efdd8598 | ||
|
|
34d03cf11b | ||
|
|
c339389d44 | ||
|
|
af5f6eb656 | ||
|
|
a6d28e2553 | ||
|
|
6213267908 | ||
|
|
d084114149 | ||
|
|
f6d99eca0d | ||
|
|
722eb3289e | ||
|
|
b7f2bdcb2d | ||
|
|
11c20db6e6 | ||
|
|
8a4e3f8bb1 | ||
|
|
bc8fe97c13 | ||
|
|
47262155aa |
71
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
71
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -8,31 +8,31 @@ body:
|
|||||||
value: |
|
value: |
|
||||||
## DankMaterialShell Bug Report
|
## DankMaterialShell Bug Report
|
||||||
Limit your report to one issue per submission unless closely related
|
Limit your report to one issue per submission unless closely related
|
||||||
- type: checkboxes
|
- type: dropdown
|
||||||
id: compositor
|
id: compositor
|
||||||
attributes:
|
attributes:
|
||||||
label: Compositor
|
label: Compositor
|
||||||
options:
|
options:
|
||||||
- label: Niri
|
- Niri
|
||||||
- label: Hyprland
|
- Hyprland
|
||||||
- label: MangoWC (dwl)
|
- MangoWC (dwl)
|
||||||
- label: Sway
|
- Sway
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: checkboxes
|
- type: dropdown
|
||||||
id: distribution
|
id: distribution
|
||||||
attributes:
|
attributes:
|
||||||
label: Distribution
|
label: Distribution
|
||||||
options:
|
options:
|
||||||
- label: Arch Linux
|
- Arch Linux
|
||||||
- label: CachyOS
|
- CachyOS
|
||||||
- label: Fedora
|
- Fedora
|
||||||
- label: NixOS
|
- NixOS
|
||||||
- label: Debian
|
- Debian
|
||||||
- label: Ubuntu
|
- Ubuntu
|
||||||
- label: Gentoo
|
- Gentoo
|
||||||
- label: OpenSUSE
|
- OpenSUSE
|
||||||
- label: Other (specify below)
|
- Other (specify below)
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
@@ -42,12 +42,45 @@ body:
|
|||||||
placeholder: e.g., PikaOS, Void Linux, etc.
|
placeholder: e.g., PikaOS, Void Linux, etc.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: installation_method
|
||||||
|
attributes:
|
||||||
|
label: Select your Installation Method
|
||||||
|
options:
|
||||||
|
- DankInstaller
|
||||||
|
- Distro Packaging
|
||||||
|
- Source
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: original_installation_method_different
|
||||||
|
attributes:
|
||||||
|
label: Was your original Installation method different?
|
||||||
|
options:
|
||||||
|
- "Yes"
|
||||||
|
- No (specify below)
|
||||||
|
default: 0
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: original_installation_method_specify
|
||||||
|
attributes:
|
||||||
|
label: If no, specify
|
||||||
|
placeholder: e.g., Distro Packaging, then Source
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: dms_doctor
|
id: dms_doctor
|
||||||
attributes:
|
attributes:
|
||||||
label: dms doctor -v
|
label: dms doctor -vC
|
||||||
description: Output of `dms doctor -v` command
|
description: Output of `dms doctor -vC` command — paste between the lines below to keep it collapsed in the issue
|
||||||
placeholder: Paste the output of `dms doctor -v` here
|
placeholder: Paste the output of `dms doctor -vC` here
|
||||||
|
value: |
|
||||||
|
<details>
|
||||||
|
<summary>Click to expand</summary>
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@@ -69,7 +102,7 @@ body:
|
|||||||
- type: textarea
|
- type: textarea
|
||||||
id: steps_to_reproduce
|
id: steps_to_reproduce
|
||||||
attributes:
|
attributes:
|
||||||
label: Steps to Reproduce & Installation Method
|
label: Steps to Reproduce
|
||||||
description: Please provide detailed steps to reproduce the issue
|
description: Please provide detailed steps to reproduce the issue
|
||||||
placeholder: |
|
placeholder: |
|
||||||
1. ...
|
1. ...
|
||||||
|
|||||||
21
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
21
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -23,18 +23,25 @@ body:
|
|||||||
placeholder: Why is this feature important?
|
placeholder: Why is this feature important?
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: checkboxes
|
- type: dropdown
|
||||||
id: compositor
|
id: compositor
|
||||||
attributes:
|
attributes:
|
||||||
label: Compositor(s)
|
label: Compositor(s)
|
||||||
description: Is this feature specific to one or more compositors?
|
description: Is this feature specific to one or more compositors?
|
||||||
options:
|
options:
|
||||||
- label: All compositors
|
- All compositors
|
||||||
- label: Niri
|
- Niri
|
||||||
- label: Hyprland
|
- Hyprland
|
||||||
- label: MangoWC (dwl)
|
- MangoWC (dwl)
|
||||||
- label: Sway
|
- Sway
|
||||||
- label: Other (specify below)
|
- Other (specify below)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: compositor_other
|
||||||
|
attributes:
|
||||||
|
label: If Other, please specify
|
||||||
|
placeholder: e.g., Wayfire, Mutter, etc.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
79
.github/ISSUE_TEMPLATE/support_request.yml
vendored
79
.github/ISSUE_TEMPLATE/support_request.yml
vendored
@@ -7,32 +7,87 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
## DankMaterialShell Support Request
|
## DankMaterialShell Support Request
|
||||||
- type: checkboxes
|
- type: dropdown
|
||||||
id: compositor
|
id: compositor
|
||||||
attributes:
|
attributes:
|
||||||
label: Compositor
|
label: Compositor
|
||||||
options:
|
options:
|
||||||
- label: Niri
|
- Niri
|
||||||
- label: Hyprland
|
- Hyprland
|
||||||
- label: MangoWC (dwl)
|
- MangoWC (dwl)
|
||||||
- label: Sway
|
- Sway
|
||||||
- label: Other (specify below)
|
- Other (specify below)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: compositor_other
|
||||||
|
attributes:
|
||||||
|
label: If Other, please specify
|
||||||
|
placeholder: e.g., Wayfire, Mutter, etc.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: input
|
- type: dropdown
|
||||||
id: distribution
|
id: distribution
|
||||||
attributes:
|
attributes:
|
||||||
label: Distribution
|
label: Distribution
|
||||||
description: Which Linux distribution are you using? (e.g., Arch, Fedora, Debian, etc.)
|
options:
|
||||||
placeholder: Your Linux distribution
|
- Arch Linux
|
||||||
|
- CachyOS
|
||||||
|
- Fedora
|
||||||
|
- NixOS
|
||||||
|
- Debian
|
||||||
|
- Ubuntu
|
||||||
|
- Gentoo
|
||||||
|
- OpenSUSE
|
||||||
|
- Other (specify below)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: distribution_other
|
||||||
|
attributes:
|
||||||
|
label: If Other, please specify
|
||||||
|
placeholder: e.g., PikaOS, Void Linux, etc.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: installation_method
|
||||||
|
attributes:
|
||||||
|
label: Select your Installation Method
|
||||||
|
options:
|
||||||
|
- DankInstaller
|
||||||
|
- Distro Packaging
|
||||||
|
- Source
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: original_installation_method_different
|
||||||
|
attributes:
|
||||||
|
label: Was your original Installation method different?
|
||||||
|
options:
|
||||||
|
- "Yes"
|
||||||
|
- No (specify below)
|
||||||
|
default: 0
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: original_installation_method_specify
|
||||||
|
attributes:
|
||||||
|
label: If no, specify
|
||||||
|
placeholder: e.g., Distro Packaging, then Source
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: dms_doctor
|
id: dms_doctor
|
||||||
attributes:
|
attributes:
|
||||||
label: dms doctor -v
|
label: dms doctor -vC
|
||||||
description: Output of `dms doctor -v` command
|
description: Output of `dms doctor -vC` command — paste between the lines below to keep it collapsed in the issue
|
||||||
placeholder: Paste the output of `dms doctor -v` here
|
placeholder: Paste the output of `dms doctor -vC` here
|
||||||
|
value: |
|
||||||
|
<details>
|
||||||
|
<summary>Click to expand</summary>
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
383
.github/workflows/backup/run-obs.yml.bak
vendored
383
.github/workflows/backup/run-obs.yml.bak
vendored
@@ -1,383 +0,0 @@
|
|||||||
name: Update OBS Packages
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
package:
|
|
||||||
description: "Package to update (dms, dms-git, or all)"
|
|
||||||
required: false
|
|
||||||
default: "all"
|
|
||||||
force_upload:
|
|
||||||
description: "Force upload without version check"
|
|
||||||
required: false
|
|
||||||
default: "false"
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- "false"
|
|
||||||
- "true"
|
|
||||||
rebuild_release:
|
|
||||||
description: "Release number for rebuilds (e.g., 2, 3, 4 to increment spec Release)"
|
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
schedule:
|
|
||||||
- cron: "0 */3 * * *" # Every 3 hours for dms-git builds
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-updates:
|
|
||||||
name: Check for updates
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
has_updates: ${{ steps.check.outputs.has_updates }}
|
|
||||||
packages: ${{ steps.check.outputs.packages }}
|
|
||||||
version: ${{ steps.check.outputs.version }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Install OSC
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y osc
|
|
||||||
|
|
||||||
mkdir -p ~/.config/osc
|
|
||||||
cat > ~/.config/osc/oscrc << EOF
|
|
||||||
[general]
|
|
||||||
apiurl = https://api.opensuse.org
|
|
||||||
|
|
||||||
[https://api.opensuse.org]
|
|
||||||
user = ${{ secrets.OBS_USERNAME }}
|
|
||||||
pass = ${{ secrets.OBS_PASSWORD }}
|
|
||||||
EOF
|
|
||||||
chmod 600 ~/.config/osc/oscrc
|
|
||||||
|
|
||||||
- name: Check for updates
|
|
||||||
id: check
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
|
|
||||||
echo "packages=dms" >> $GITHUB_OUTPUT
|
|
||||||
VERSION="${GITHUB_REF#refs/tags/}"
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "Triggered by tag: $VERSION (always update)"
|
|
||||||
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
|
||||||
echo "packages=dms-git" >> $GITHUB_OUTPUT
|
|
||||||
echo "Checking if dms-git source has changed..."
|
|
||||||
|
|
||||||
# Get current commit hash (8 chars to match spec format)
|
|
||||||
CURRENT_COMMIT=$(git rev-parse --short=8 HEAD)
|
|
||||||
|
|
||||||
# Check OBS for last uploaded commit
|
|
||||||
OBS_BASE="$HOME/.cache/osc-checkouts"
|
|
||||||
mkdir -p "$OBS_BASE"
|
|
||||||
OBS_PROJECT="home:AvengeMedia:dms-git"
|
|
||||||
|
|
||||||
if [[ -d "$OBS_BASE/$OBS_PROJECT/dms-git" ]]; then
|
|
||||||
cd "$OBS_BASE/$OBS_PROJECT/dms-git"
|
|
||||||
osc up -q 2>/dev/null || true
|
|
||||||
|
|
||||||
# Extract commit hash from spec Version line & format like; 0.6.2+git2264.a679be68
|
|
||||||
if [[ -f "dms-git.spec" ]]; then
|
|
||||||
OBS_COMMIT=$(grep "^Version:" "dms-git.spec" | grep -oP '\.[a-f0-9]{8}' | tr -d '.' || echo "")
|
|
||||||
|
|
||||||
if [[ -n "$OBS_COMMIT" ]]; then
|
|
||||||
if [[ "$CURRENT_COMMIT" == "$OBS_COMMIT" ]]; then
|
|
||||||
echo "has_updates=false" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 Commit $CURRENT_COMMIT already uploaded to OBS, skipping"
|
|
||||||
else
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 New commit detected: $CURRENT_COMMIT (OBS has $OBS_COMMIT)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 Could not extract OBS commit, proceeding with update"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 No spec file in OBS, proceeding with update"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "${{ github.workspace }}"
|
|
||||||
else
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 First upload to OBS, update needed"
|
|
||||||
fi
|
|
||||||
elif [[ "${{ github.event.inputs.force_upload }}" == "true" ]]; then
|
|
||||||
PKG="${{ github.event.inputs.package }}"
|
|
||||||
if [[ -z "$PKG" || "$PKG" == "all" ]]; then
|
|
||||||
echo "packages=all" >> $GITHUB_OUTPUT
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "🚀 Force upload: all packages"
|
|
||||||
else
|
|
||||||
echo "packages=$PKG" >> $GITHUB_OUTPUT
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "🚀 Force upload: $PKG"
|
|
||||||
fi
|
|
||||||
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
|
||||||
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "Manual trigger: ${{ github.event.inputs.package }}"
|
|
||||||
else
|
|
||||||
echo "packages=all" >> $GITHUB_OUTPUT
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
update-obs:
|
|
||||||
name: Upload to OBS
|
|
||||||
needs: check-updates
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
if: |
|
|
||||||
github.event.inputs.force_upload == 'true' ||
|
|
||||||
github.event_name == 'workflow_dispatch' ||
|
|
||||||
needs.check-updates.outputs.has_updates == 'true'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Generate GitHub App Token
|
|
||||||
id: generate_token
|
|
||||||
uses: actions/create-github-app-token@v1
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.APP_ID }}
|
|
||||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
token: ${{ steps.generate_token.outputs.token }}
|
|
||||||
|
|
||||||
- name: Check if last commit was automated
|
|
||||||
id: check-loop
|
|
||||||
run: |
|
|
||||||
LAST_COMMIT_MSG=$(git log -1 --pretty=%B | head -1)
|
|
||||||
if [[ "$LAST_COMMIT_MSG" == "ci: Auto-update PPA packages"* ]] || [[ "$LAST_COMMIT_MSG" == "ci: Auto-update OBS packages"* ]]; then
|
|
||||||
echo "⏭️ Last commit was automated ($LAST_COMMIT_MSG), skipping to prevent infinite loop"
|
|
||||||
echo "skip=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "✅ Last commit was not automated, proceeding"
|
|
||||||
echo "skip=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Determine packages to update
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
id: packages
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
|
|
||||||
echo "packages=dms" >> $GITHUB_OUTPUT
|
|
||||||
VERSION="${GITHUB_REF#refs/tags/}"
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
echo "Triggered by tag: $VERSION"
|
|
||||||
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
|
||||||
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
|
||||||
echo "Triggered by schedule: updating git package"
|
|
||||||
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
|
||||||
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
|
|
||||||
echo "Manual trigger: ${{ github.event.inputs.package }}"
|
|
||||||
else
|
|
||||||
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Update dms-git spec version
|
|
||||||
if: steps.check-loop.outputs.skip != 'true' && (contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all')
|
|
||||||
run: |
|
|
||||||
# Get commit info for dms-git versioning
|
|
||||||
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
|
||||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
|
||||||
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.2")
|
|
||||||
|
|
||||||
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
|
|
||||||
echo "📦 Updating dms-git.spec to version: $NEW_VERSION"
|
|
||||||
|
|
||||||
# Update version in spec
|
|
||||||
sed -i "s/^Version:.*/Version: $NEW_VERSION/" distro/opensuse/dms-git.spec
|
|
||||||
|
|
||||||
# Add changelog entry
|
|
||||||
DATE_STR=$(date "+%a %b %d %Y")
|
|
||||||
CHANGELOG_ENTRY="* $DATE_STR Avenge Media <AvengeMedia.US@gmail.com> - ${NEW_VERSION}-1\n- Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)"
|
|
||||||
sed -i "/%changelog/a\\$CHANGELOG_ENTRY" distro/opensuse/dms-git.spec
|
|
||||||
|
|
||||||
- name: Update Debian dms-git changelog version
|
|
||||||
if: steps.check-loop.outputs.skip != 'true' && (contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all')
|
|
||||||
run: |
|
|
||||||
# Get commit info for dms-git versioning
|
|
||||||
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
|
||||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
|
||||||
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.2")
|
|
||||||
|
|
||||||
# Debian version format: 0.6.2+git2256.9162e314
|
|
||||||
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
|
|
||||||
echo "📦 Updating Debian dms-git changelog to version: $NEW_VERSION"
|
|
||||||
|
|
||||||
CHANGELOG_DATE=$(date -R)
|
|
||||||
|
|
||||||
CHANGELOG_FILE="distro/debian/dms-git/debian/changelog"
|
|
||||||
|
|
||||||
# Get current version from changelog
|
|
||||||
CURRENT_VERSION=$(head -1 "$CHANGELOG_FILE" | sed 's/.*(\([^)]*\)).*/\1/')
|
|
||||||
|
|
||||||
echo "Current Debian version: $CURRENT_VERSION"
|
|
||||||
echo "New version: $NEW_VERSION"
|
|
||||||
|
|
||||||
# Only update if version changed
|
|
||||||
if [ "$CURRENT_VERSION" != "$NEW_VERSION" ]; then
|
|
||||||
# Create new changelog entry at top
|
|
||||||
TEMP_CHANGELOG=$(mktemp)
|
|
||||||
|
|
||||||
cat > "$TEMP_CHANGELOG" << EOF
|
|
||||||
dms-git ($NEW_VERSION) nightly; urgency=medium
|
|
||||||
|
|
||||||
* Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)
|
|
||||||
|
|
||||||
-- Avenge Media <AvengeMedia.US@gmail.com> $CHANGELOG_DATE
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Prepend to existing changelog
|
|
||||||
cat "$CHANGELOG_FILE" >> "$TEMP_CHANGELOG"
|
|
||||||
mv "$TEMP_CHANGELOG" "$CHANGELOG_FILE"
|
|
||||||
|
|
||||||
echo "✓ Updated Debian changelog: $CURRENT_VERSION → $NEW_VERSION"
|
|
||||||
else
|
|
||||||
echo "✓ Debian changelog already at version $NEW_VERSION"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Update dms stable version
|
|
||||||
if: steps.check-loop.outputs.skip != 'true' && steps.packages.outputs.version != ''
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.packages.outputs.version }}"
|
|
||||||
VERSION_NO_V="${VERSION#v}"
|
|
||||||
echo "Updating packaging to version $VERSION_NO_V"
|
|
||||||
|
|
||||||
# Update openSUSE dms spec (stable only)
|
|
||||||
sed -i "s/^Version:.*/Version: $VERSION_NO_V/" distro/opensuse/dms.spec
|
|
||||||
|
|
||||||
# Update openSUSE spec changelog
|
|
||||||
DATE_STR=$(date "+%a %b %d %Y")
|
|
||||||
CHANGELOG_ENTRY="* $DATE_STR AvengeMedia <maintainer@avengemedia.com> - ${VERSION_NO_V}-1\\n- Update to stable $VERSION release\\n- Bug fixes and improvements"
|
|
||||||
sed -i "/%changelog/a\\$CHANGELOG_ENTRY\\n" distro/opensuse/dms.spec
|
|
||||||
|
|
||||||
# Update Debian _service files (both tar_scm and download_url formats)
|
|
||||||
for service in distro/debian/*/_service; do
|
|
||||||
if [[ -f "$service" ]]; then
|
|
||||||
# Update tar_scm revision parameter (for dms-git)
|
|
||||||
sed -i "s|<param name=\"revision\">v[0-9.]*</param>|<param name=\"revision\">$VERSION</param>|" "$service"
|
|
||||||
|
|
||||||
# Update download_url paths (for dms stable)
|
|
||||||
sed -i "s|/v[0-9.]\+/|/$VERSION/|g" "$service"
|
|
||||||
sed -i "s|/tags/v[0-9.]\+\.tar\.gz|/tags/$VERSION.tar.gz|g" "$service"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Update Debian changelog for dms stable
|
|
||||||
if [[ -f "distro/debian/dms/debian/changelog" ]]; then
|
|
||||||
CHANGELOG_DATE=$(date -R)
|
|
||||||
TEMP_CHANGELOG=$(mktemp)
|
|
||||||
|
|
||||||
cat > "$TEMP_CHANGELOG" << EOF
|
|
||||||
dms ($VERSION_NO_V) stable; urgency=medium
|
|
||||||
|
|
||||||
* Update to $VERSION stable release
|
|
||||||
* Bug fixes and improvements
|
|
||||||
|
|
||||||
-- Avenge Media <AvengeMedia.US@gmail.com> $CHANGELOG_DATE
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat "distro/debian/dms/debian/changelog" >> "$TEMP_CHANGELOG"
|
|
||||||
mv "$TEMP_CHANGELOG" "distro/debian/dms/debian/changelog"
|
|
||||||
|
|
||||||
echo "✓ Updated Debian changelog to $VERSION_NO_V"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Install Go
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: "1.24"
|
|
||||||
|
|
||||||
- name: Install OSC
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y osc
|
|
||||||
|
|
||||||
mkdir -p ~/.config/osc
|
|
||||||
cat > ~/.config/osc/oscrc << EOF
|
|
||||||
[general]
|
|
||||||
apiurl = https://api.opensuse.org
|
|
||||||
|
|
||||||
[https://api.opensuse.org]
|
|
||||||
user = ${{ secrets.OBS_USERNAME }}
|
|
||||||
pass = ${{ secrets.OBS_PASSWORD }}
|
|
||||||
EOF
|
|
||||||
chmod 600 ~/.config/osc/oscrc
|
|
||||||
|
|
||||||
- name: Upload to OBS
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
env:
|
|
||||||
FORCE_UPLOAD: ${{ github.event.inputs.force_upload }}
|
|
||||||
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
|
|
||||||
run: |
|
|
||||||
PACKAGES="${{ steps.packages.outputs.packages }}"
|
|
||||||
MESSAGE="Automated update from GitHub Actions"
|
|
||||||
|
|
||||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
|
||||||
MESSAGE="Update to ${{ steps.packages.outputs.version }}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$PACKAGES" == "all" ]]; then
|
|
||||||
bash distro/scripts/obs-upload.sh dms "$MESSAGE"
|
|
||||||
bash distro/scripts/obs-upload.sh dms-git "Automated git update"
|
|
||||||
else
|
|
||||||
bash distro/scripts/obs-upload.sh "$PACKAGES" "$MESSAGE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Get changed packages
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
id: changed-packages
|
|
||||||
run: |
|
|
||||||
# Check if there are any changes to commit
|
|
||||||
if git diff --exit-code distro/debian/ distro/opensuse/ >/dev/null 2>&1; then
|
|
||||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 No changelog or spec changes to commit"
|
|
||||||
else
|
|
||||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
||||||
# Get list of changed packages for commit message
|
|
||||||
CHANGED_DEB=$(git diff --name-only distro/debian/ 2>/dev/null | grep 'debian/changelog' | xargs dirname 2>/dev/null | xargs dirname 2>/dev/null | xargs basename 2>/dev/null | tr '\n' ', ' | sed 's/, $//' || echo "")
|
|
||||||
CHANGED_SUSE=$(git diff --name-only distro/opensuse/ 2>/dev/null | grep '\.spec$' | sed 's|distro/opensuse/||' | sed 's/\.spec$//' | tr '\n' ', ' | sed 's/, $//' || echo "")
|
|
||||||
|
|
||||||
PKGS=$(echo "$CHANGED_DEB,$CHANGED_SUSE" | tr ',' '\n' | grep -v '^$' | sort -u | tr '\n' ',' | sed 's/,$//')
|
|
||||||
echo "packages=$PKGS" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 Changed packages: $PKGS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Commit packaging changes
|
|
||||||
if: steps.check-loop.outputs.skip != 'true' && steps.changed-packages.outputs.has_changes == 'true'
|
|
||||||
run: |
|
|
||||||
git config user.name "dms-ci[bot]"
|
|
||||||
git config user.email "dms-ci[bot]@users.noreply.github.com"
|
|
||||||
git add distro/debian/*/debian/changelog distro/opensuse/*.spec
|
|
||||||
git commit -m "ci: Auto-update OBS packages [${{ steps.changed-packages.outputs.packages }}]" -m "🤖 Automated by GitHub Actions"
|
|
||||||
git pull --rebase origin master
|
|
||||||
git push
|
|
||||||
|
|
||||||
- name: Summary
|
|
||||||
run: |
|
|
||||||
echo "### OBS Package Update Complete" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
|
||||||
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
if [[ "${{ needs.check-updates.outputs.has_updates }}" == "false" ]]; then
|
|
||||||
echo "- **Status**: Skipped (no changes detected)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
echo "- **Project**: https://build.opensuse.org/project/show/home:AvengeMedia" >> $GITHUB_STEP_SUMMARY
|
|
||||||
298
.github/workflows/backup/run-ppa.yml.bak
vendored
298
.github/workflows/backup/run-ppa.yml.bak
vendored
@@ -1,298 +0,0 @@
|
|||||||
name: Update PPA Packages
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
package:
|
|
||||||
description: "Package to upload (dms, dms-git, dms-greeter, or all)"
|
|
||||||
required: false
|
|
||||||
default: "dms-git"
|
|
||||||
force_upload:
|
|
||||||
description: "Force upload without version check"
|
|
||||||
required: false
|
|
||||||
default: "false"
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- "false"
|
|
||||||
- "true"
|
|
||||||
rebuild_release:
|
|
||||||
description: "Release number for rebuilds (e.g., 2, 3, 4 for ppa2, ppa3, ppa4)"
|
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
schedule:
|
|
||||||
- cron: "0 */3 * * *" # Every 3 hours for dms-git builds
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-updates:
|
|
||||||
name: Check for updates
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
has_updates: ${{ steps.check.outputs.has_updates }}
|
|
||||||
packages: ${{ steps.check.outputs.packages }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Check for updates
|
|
||||||
id: check
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "schedule" ]]; then
|
|
||||||
echo "packages=dms-git" >> $GITHUB_OUTPUT
|
|
||||||
echo "Checking if dms-git source has changed..."
|
|
||||||
|
|
||||||
# Get current commit hash (8 chars to match changelog format)
|
|
||||||
CURRENT_COMMIT=$(git rev-parse --short=8 HEAD)
|
|
||||||
|
|
||||||
# Extract commit hash from changelog
|
|
||||||
# Format: dms-git (0.6.2+git2264.c5c5ce84) questing; urgency=medium
|
|
||||||
CHANGELOG_FILE="distro/ubuntu/dms-git/debian/changelog"
|
|
||||||
|
|
||||||
if [[ -f "$CHANGELOG_FILE" ]]; then
|
|
||||||
CHANGELOG_COMMIT=$(head -1 "$CHANGELOG_FILE" | grep -oP '\.[a-f0-9]{8}' | tr -d '.' || echo "")
|
|
||||||
|
|
||||||
if [[ -n "$CHANGELOG_COMMIT" ]]; then
|
|
||||||
if [[ "$CURRENT_COMMIT" == "$CHANGELOG_COMMIT" ]]; then
|
|
||||||
echo "has_updates=false" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 Commit $CURRENT_COMMIT already in changelog, skipping upload"
|
|
||||||
else
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 New commit detected: $CURRENT_COMMIT (changelog has $CHANGELOG_COMMIT)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 Could not extract commit from changelog, proceeding with upload"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 No changelog file found, proceeding with upload"
|
|
||||||
fi
|
|
||||||
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
|
||||||
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "Manual trigger: ${{ github.event.inputs.package }}"
|
|
||||||
else
|
|
||||||
echo "packages=dms-git" >> $GITHUB_OUTPUT
|
|
||||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
upload-ppa:
|
|
||||||
name: Upload to PPA
|
|
||||||
needs: check-updates
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
if: |
|
|
||||||
github.event.inputs.force_upload == 'true' ||
|
|
||||||
github.event_name == 'workflow_dispatch' ||
|
|
||||||
needs.check-updates.outputs.has_updates == 'true'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Generate GitHub App Token
|
|
||||||
id: generate_token
|
|
||||||
uses: actions/create-github-app-token@v1
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.APP_ID }}
|
|
||||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
token: ${{ steps.generate_token.outputs.token }}
|
|
||||||
|
|
||||||
- name: Check if last commit was automated
|
|
||||||
id: check-loop
|
|
||||||
run: |
|
|
||||||
LAST_COMMIT_MSG=$(git log -1 --pretty=%B | head -1)
|
|
||||||
if [[ "$LAST_COMMIT_MSG" == "ci: Auto-update PPA packages"* ]] || [[ "$LAST_COMMIT_MSG" == "ci: Auto-update OBS packages"* ]]; then
|
|
||||||
echo "⏭️ Last commit was automated ($LAST_COMMIT_MSG), skipping to prevent infinite loop"
|
|
||||||
echo "skip=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "✅ Last commit was not automated, proceeding"
|
|
||||||
echo "skip=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: "1.24"
|
|
||||||
cache: false
|
|
||||||
|
|
||||||
- name: Install build dependencies
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y \
|
|
||||||
debhelper \
|
|
||||||
devscripts \
|
|
||||||
dput \
|
|
||||||
lftp \
|
|
||||||
build-essential \
|
|
||||||
fakeroot \
|
|
||||||
dpkg-dev
|
|
||||||
|
|
||||||
- name: Configure GPG
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
env:
|
|
||||||
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
||||||
run: |
|
|
||||||
echo "$GPG_KEY" | gpg --import
|
|
||||||
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | awk '{print $2}' | cut -d'/' -f2)
|
|
||||||
echo "DEBSIGN_KEYID=$GPG_KEY_ID" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Determine packages to upload
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
id: packages
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.inputs.force_upload }}" == "true" ]]; then
|
|
||||||
PKG="${{ github.event.inputs.package }}"
|
|
||||||
if [[ -z "$PKG" || "$PKG" == "all" ]]; then
|
|
||||||
echo "packages=all" >> $GITHUB_OUTPUT
|
|
||||||
echo "🚀 Force upload: all packages"
|
|
||||||
else
|
|
||||||
echo "packages=$PKG" >> $GITHUB_OUTPUT
|
|
||||||
echo "🚀 Force upload: $PKG"
|
|
||||||
fi
|
|
||||||
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
|
||||||
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
|
||||||
echo "Triggered by schedule: uploading git package"
|
|
||||||
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
|
||||||
# Manual package selection should respect change detection
|
|
||||||
SELECTED_PKG="${{ github.event.inputs.package }}"
|
|
||||||
UPDATED_PKG="${{ needs.check-updates.outputs.packages }}"
|
|
||||||
|
|
||||||
# Check if manually selected package is in the updated list
|
|
||||||
if [[ "$UPDATED_PKG" == *"$SELECTED_PKG"* ]] || [[ "$SELECTED_PKG" == "all" ]]; then
|
|
||||||
echo "packages=$SELECTED_PKG" >> $GITHUB_OUTPUT
|
|
||||||
echo "📦 Manual selection (has updates): $SELECTED_PKG"
|
|
||||||
else
|
|
||||||
echo "packages=" >> $GITHUB_OUTPUT
|
|
||||||
echo "⚠️ Manual selection '$SELECTED_PKG' has no updates - skipping (use force_upload to override)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Upload to PPA
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
run: |
|
|
||||||
PACKAGES="${{ steps.packages.outputs.packages }}"
|
|
||||||
REBUILD_RELEASE="${{ github.event.inputs.rebuild_release }}"
|
|
||||||
|
|
||||||
if [[ -z "$PACKAGES" ]]; then
|
|
||||||
echo "No packages selected for upload. Skipping."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build command arguments
|
|
||||||
BUILD_ARGS=()
|
|
||||||
if [[ -n "$REBUILD_RELEASE" ]]; then
|
|
||||||
BUILD_ARGS+=("$REBUILD_RELEASE")
|
|
||||||
echo "✓ Using rebuild release number: ppa$REBUILD_RELEASE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$PACKAGES" == "all" ]]; then
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
echo "Uploading dms to PPA..."
|
|
||||||
if [ -n "$REBUILD_RELEASE" ]; then
|
|
||||||
echo "🔄 Using rebuild release number: ppa$REBUILD_RELEASE"
|
|
||||||
fi
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
bash distro/scripts/ppa-upload.sh dms dms questing "${BUILD_ARGS[@]}"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
echo "Uploading dms-git to PPA..."
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
bash distro/scripts/ppa-upload.sh dms-git dms-git questing "${BUILD_ARGS[@]}"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
echo "Uploading dms-greeter to PPA..."
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
bash distro/scripts/ppa-upload.sh dms-greeter danklinux questing "${BUILD_ARGS[@]}"
|
|
||||||
else
|
|
||||||
# Map package to PPA name
|
|
||||||
case "$PACKAGES" in
|
|
||||||
dms)
|
|
||||||
PPA_NAME="dms"
|
|
||||||
;;
|
|
||||||
dms-git)
|
|
||||||
PPA_NAME="dms-git"
|
|
||||||
;;
|
|
||||||
dms-greeter)
|
|
||||||
PPA_NAME="danklinux"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
PPA_NAME="$PACKAGES"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
echo "Uploading $PACKAGES to PPA..."
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
bash distro/scripts/ppa-upload.sh "$PACKAGES" "$PPA_NAME" questing "${BUILD_ARGS[@]}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Get changed packages
|
|
||||||
if: steps.check-loop.outputs.skip != 'true'
|
|
||||||
id: changed-packages
|
|
||||||
run: |
|
|
||||||
# Check if there are any changelog changes to commit
|
|
||||||
if git diff --exit-code distro/ubuntu/ >/dev/null 2>&1; then
|
|
||||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 No changelog changes to commit"
|
|
||||||
else
|
|
||||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
||||||
# Get list of changed packages for commit message (deduplicate)
|
|
||||||
CHANGED=$(git diff --name-only distro/ubuntu/ | grep 'debian/changelog' | sed 's|/debian/changelog||' | xargs -I{} basename {} | sort -u | tr '\n' ',' | sed 's/,$//')
|
|
||||||
echo "packages=$CHANGED" >> $GITHUB_OUTPUT
|
|
||||||
echo "📋 Changed packages: $CHANGED"
|
|
||||||
echo "📋 Debug - Changed files:"
|
|
||||||
git diff --name-only distro/ubuntu/ | grep 'debian/changelog' || echo "No changelog files found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Commit changelog changes
|
|
||||||
if: steps.check-loop.outputs.skip != 'true' && steps.changed-packages.outputs.has_changes == 'true'
|
|
||||||
run: |
|
|
||||||
git config user.name "dms-ci[bot]"
|
|
||||||
git config user.email "dms-ci[bot]@users.noreply.github.com"
|
|
||||||
git add distro/ubuntu/*/debian/changelog
|
|
||||||
git commit -m "ci: Auto-update PPA packages [${{ steps.changed-packages.outputs.packages }}]" -m "🤖 Automated by GitHub Actions"
|
|
||||||
git pull --rebase origin master
|
|
||||||
git push
|
|
||||||
|
|
||||||
- name: Summary
|
|
||||||
run: |
|
|
||||||
echo "### PPA Package Upload Complete" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
if [[ "${{ needs.check-updates.outputs.has_updates }}" == "false" ]]; then
|
|
||||||
echo "- **Status**: Skipped (no changes detected)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
PACKAGES="${{ steps.packages.outputs.packages }}"
|
|
||||||
if [[ "$PACKAGES" == "all" ]]; then
|
|
||||||
echo "- **PPA dms**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "- **PPA dms-git**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "- **PPA danklinux**: https://launchpad.net/~avengemedia/+archive/ubuntu/danklinux/+packages" >> $GITHUB_STEP_SUMMARY
|
|
||||||
elif [[ "$PACKAGES" == "dms" ]]; then
|
|
||||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
|
||||||
elif [[ "$PACKAGES" == "dms-git" ]]; then
|
|
||||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
|
|
||||||
elif [[ "$PACKAGES" == "dms-greeter" ]]; then
|
|
||||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/danklinux/+packages" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
|
||||||
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "Builds will appear once Launchpad processes the uploads." >> $GITHUB_STEP_SUMMARY
|
|
||||||
@@ -5,11 +5,13 @@ repos:
|
|||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
- repo: local
|
||||||
rev: v0.10.0.1
|
|
||||||
hooks:
|
hooks:
|
||||||
- id: shellcheck
|
- id: shellcheck
|
||||||
args: [-e, SC2164, -e, SC2001, -e, SC2012, -e, SC2317]
|
name: shellcheck
|
||||||
|
entry: shellcheck -e SC2164 -e SC2001 -e SC2012 -e SC2317
|
||||||
|
language: system
|
||||||
|
types: [shell]
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
- id: go-mod-tidy
|
- id: go-mod-tidy
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ nix develop
|
|||||||
|
|
||||||
This will provide:
|
This will provide:
|
||||||
|
|
||||||
- Go 1.24 toolchain (go, gopls, delve, go-tools) and GNU Make
|
- Go 1.25+ toolchain (go, gopls, delve, go-tools) and GNU Make
|
||||||
- Quickshell and required QML packages
|
- Quickshell and required QML packages
|
||||||
- Properly configured QML2_IMPORT_PATH
|
- Properly configured QML2_IMPORT_PATH
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ The on-screen preview displays the selected format. JSON output includes hex, RG
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Requires Go 1.24+
|
Requires Go 1.25+
|
||||||
|
|
||||||
**Development build:**
|
**Development build:**
|
||||||
|
|
||||||
|
|||||||
@@ -649,40 +649,73 @@ func checkI2CAvailability() checkResult {
|
|||||||
return checkResult{catOptionalFeatures, "I2C/DDC", statusOK, fmt.Sprintf("%d monitor(s) detected", len(devices)), "External monitor brightness control", doctorDocsURL + "#optional-features"}
|
return checkResult{catOptionalFeatures, "I2C/DDC", statusOK, fmt.Sprintf("%d monitor(s) detected", len(devices)), "External monitor brightness control", doctorDocsURL + "#optional-features"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkKImageFormats() checkResult {
|
func checkImageFormatPlugins() []checkResult {
|
||||||
url := doctorDocsURL + "#optional-features"
|
url := doctorDocsURL + "#optional-features"
|
||||||
desc := "Extra image format support (AVIF, HEIF, JXL)"
|
|
||||||
|
|
||||||
pluginDir := findQtPluginDir()
|
pluginDir := findQtPluginDir()
|
||||||
if pluginDir == "" {
|
if pluginDir == "" {
|
||||||
return checkResult{catOptionalFeatures, "kimageformats", statusInfo, "Cannot detect (qtpaths not found)", desc, url}
|
return []checkResult{
|
||||||
}
|
{catOptionalFeatures, "qt6-imageformats", statusInfo, "Cannot detect (plugin dir not found)", "WebP, TIFF, JP2 support", url},
|
||||||
|
{catOptionalFeatures, "kimageformats", statusInfo, "Cannot detect (plugin dir not found)", "AVIF, HEIF, JXL support", url},
|
||||||
imageFormatsDir := filepath.Join(pluginDir, "imageformats")
|
|
||||||
keyPlugins := []struct{ file, format string }{
|
|
||||||
{"kimg_avif.so", "AVIF"},
|
|
||||||
{"kimg_heif.so", "HEIF"},
|
|
||||||
{"kimg_jxl.so", "JXL"},
|
|
||||||
{"kimg_exr.so", "EXR"},
|
|
||||||
}
|
|
||||||
|
|
||||||
var found []string
|
|
||||||
for _, p := range keyPlugins {
|
|
||||||
if _, err := os.Stat(filepath.Join(imageFormatsDir, p.file)); err == nil {
|
|
||||||
found = append(found, p.format)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(found) == 0 {
|
imageFormatsDir := filepath.Join(pluginDir, "imageformats")
|
||||||
return checkResult{catOptionalFeatures, "kimageformats", statusWarn, "Not installed", desc, url}
|
|
||||||
|
type pluginCheck struct {
|
||||||
|
name string
|
||||||
|
desc string
|
||||||
|
plugins []struct{ file, format string }
|
||||||
}
|
}
|
||||||
|
|
||||||
details := ""
|
checks := []pluginCheck{
|
||||||
if doctorVerbose {
|
{
|
||||||
details = fmt.Sprintf("Formats: %s (%s)", strings.Join(found, ", "), imageFormatsDir)
|
name: "qt6-imageformats",
|
||||||
|
desc: "WebP, TIFF, GIF, JP2 support",
|
||||||
|
plugins: []struct{ file, format string }{
|
||||||
|
{"libqwebp.so", "WebP"},
|
||||||
|
{"libqtiff.so", "TIFF"},
|
||||||
|
{"libqgif.so", "GIF"},
|
||||||
|
{"libqjp2.so", "JP2"},
|
||||||
|
{"libqicns.so", "ICNS"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "kimageformats",
|
||||||
|
desc: "AVIF, HEIF, JXL support",
|
||||||
|
plugins: []struct{ file, format string }{
|
||||||
|
{"kimg_avif.so", "AVIF"},
|
||||||
|
{"kimg_heif.so", "HEIF"},
|
||||||
|
{"kimg_jxl.so", "JXL"},
|
||||||
|
{"kimg_exr.so", "EXR"},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkResult{catOptionalFeatures, "kimageformats", statusOK, fmt.Sprintf("Installed (%d formats)", len(found)), details, url}
|
var results []checkResult
|
||||||
|
for _, c := range checks {
|
||||||
|
var found []string
|
||||||
|
for _, p := range c.plugins {
|
||||||
|
if _, err := os.Stat(filepath.Join(imageFormatsDir, p.file)); err == nil {
|
||||||
|
found = append(found, p.format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result checkResult
|
||||||
|
switch {
|
||||||
|
case len(found) == 0:
|
||||||
|
result = checkResult{catOptionalFeatures, c.name, statusWarn, "Not installed", c.desc, url}
|
||||||
|
default:
|
||||||
|
details := ""
|
||||||
|
if doctorVerbose {
|
||||||
|
details = fmt.Sprintf("Formats: %s (%s)", strings.Join(found, ", "), imageFormatsDir)
|
||||||
|
}
|
||||||
|
result = checkResult{catOptionalFeatures, c.name, statusOK, fmt.Sprintf("Installed (%d formats)", len(found)), details, url}
|
||||||
|
}
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func findQtPluginDir() string {
|
func findQtPluginDir() string {
|
||||||
@@ -773,7 +806,7 @@ func checkOptionalDependencies() []checkResult {
|
|||||||
results = append(results, checkResult{catOptionalFeatures, "cups-pk-helper", cupsPkStatus, cupsPkMsg, "Printer management", optionalFeaturesURL})
|
results = append(results, checkResult{catOptionalFeatures, "cups-pk-helper", cupsPkStatus, cupsPkMsg, "Printer management", optionalFeaturesURL})
|
||||||
|
|
||||||
results = append(results, checkI2CAvailability())
|
results = append(results, checkI2CAvailability())
|
||||||
results = append(results, checkKImageFormats())
|
results = append(results, checkImageFormatPlugins()...)
|
||||||
|
|
||||||
terminals := []string{"ghostty", "kitty", "alacritty", "foot", "wezterm"}
|
terminals := []string{"ghostty", "kitty", "alacritty", "foot", "wezterm"}
|
||||||
if idx := slices.IndexFunc(terminals, utils.CommandExists); idx >= 0 {
|
if idx := slices.IndexFunc(terminals, utils.CommandExists); idx >= 0 {
|
||||||
|
|||||||
@@ -13,16 +13,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ssOutputName string
|
ssOutputName string
|
||||||
ssIncludeCursor bool
|
ssCursor string
|
||||||
ssFormat string
|
ssFormat string
|
||||||
ssQuality int
|
ssQuality int
|
||||||
ssOutputDir string
|
ssOutputDir string
|
||||||
ssFilename string
|
ssFilename string
|
||||||
ssNoClipboard bool
|
ssNoClipboard bool
|
||||||
ssNoFile bool
|
ssNoFile bool
|
||||||
ssNoNotify bool
|
ssNoNotify bool
|
||||||
ssStdout bool
|
ssStdout bool
|
||||||
)
|
)
|
||||||
|
|
||||||
var screenshotCmd = &cobra.Command{
|
var screenshotCmd = &cobra.Command{
|
||||||
@@ -52,7 +52,7 @@ Examples:
|
|||||||
dms screenshot last # Last region (pre-selected)
|
dms screenshot last # Last region (pre-selected)
|
||||||
dms screenshot --no-clipboard # Save file only
|
dms screenshot --no-clipboard # Save file only
|
||||||
dms screenshot --no-file # Clipboard only
|
dms screenshot --no-file # Clipboard only
|
||||||
dms screenshot --cursor # Include cursor
|
dms screenshot --cursor=on # Include cursor
|
||||||
dms screenshot -f jpg -q 85 # JPEG with quality 85`,
|
dms screenshot -f jpg -q 85 # JPEG with quality 85`,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ var notifyActionCmd = &cobra.Command{
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
screenshotCmd.PersistentFlags().StringVarP(&ssOutputName, "output", "o", "", "Output name for 'output' mode")
|
screenshotCmd.PersistentFlags().StringVarP(&ssOutputName, "output", "o", "", "Output name for 'output' mode")
|
||||||
screenshotCmd.PersistentFlags().BoolVar(&ssIncludeCursor, "cursor", false, "Include cursor in screenshot")
|
screenshotCmd.PersistentFlags().StringVar(&ssCursor, "cursor", "off", "Include cursor in screenshot (on/off)")
|
||||||
screenshotCmd.PersistentFlags().StringVarP(&ssFormat, "format", "f", "png", "Output format (png, jpg, ppm)")
|
screenshotCmd.PersistentFlags().StringVarP(&ssFormat, "format", "f", "png", "Output format (png, jpg, ppm)")
|
||||||
screenshotCmd.PersistentFlags().IntVarP(&ssQuality, "quality", "q", 90, "JPEG quality (1-100)")
|
screenshotCmd.PersistentFlags().IntVarP(&ssQuality, "quality", "q", 90, "JPEG quality (1-100)")
|
||||||
screenshotCmd.PersistentFlags().StringVarP(&ssOutputDir, "dir", "d", "", "Output directory")
|
screenshotCmd.PersistentFlags().StringVarP(&ssOutputDir, "dir", "d", "", "Output directory")
|
||||||
@@ -136,7 +136,9 @@ func getScreenshotConfig(mode screenshot.Mode) screenshot.Config {
|
|||||||
config := screenshot.DefaultConfig()
|
config := screenshot.DefaultConfig()
|
||||||
config.Mode = mode
|
config.Mode = mode
|
||||||
config.OutputName = ssOutputName
|
config.OutputName = ssOutputName
|
||||||
config.IncludeCursor = ssIncludeCursor
|
if strings.EqualFold(ssCursor, "on") {
|
||||||
|
config.Cursor = screenshot.CursorOn
|
||||||
|
}
|
||||||
config.Clipboard = !ssNoClipboard
|
config.Clipboard = !ssNoClipboard
|
||||||
config.SaveFile = !ssNoFile
|
config.SaveFile = !ssNoFile
|
||||||
config.Notify = !ssNoNotify
|
config.Notify = !ssNoNotify
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ windowrule = float on, match:class ^(blueman-manager)$
|
|||||||
windowrule = float on, match:class ^(org\.gnome\.Nautilus)$
|
windowrule = float on, match:class ^(org\.gnome\.Nautilus)$
|
||||||
windowrule = float on, match:class ^(xdg-desktop-portal)$
|
windowrule = float on, match:class ^(xdg-desktop-portal)$
|
||||||
|
|
||||||
windowrule = noinitialfocus on, match:class ^(steam)$, match:title ^(notificationtoasts)
|
windowrule = no_initial_focus on, match:class ^(steam)$, match:title ^(notificationtoasts)
|
||||||
windowrule = pin on, match:class ^(steam)$, match:title ^(notificationtoasts)
|
windowrule = pin on, match:class ^(steam)$, match:title ^(notificationtoasts)
|
||||||
|
|
||||||
windowrule = float on, match:class ^(firefox)$, match:title ^(Picture-in-Picture)$
|
windowrule = float on, match:class ^(firefox)$, match:title ^(Picture-in-Picture)$
|
||||||
@@ -111,6 +111,7 @@ windowrule = float on, match:class ^(zoom)$
|
|||||||
# windowrule = float on, match:class ^(org.quickshell)$
|
# windowrule = float on, match:class ^(org.quickshell)$
|
||||||
|
|
||||||
layerrule = no_anim on, match:namespace ^(quickshell)$
|
layerrule = no_anim on, match:namespace ^(quickshell)$
|
||||||
|
layerrule = no_anim on, match:namespace ^dms:.*
|
||||||
|
|
||||||
source = ./dms/colors.conf
|
source = ./dms/colors.conf
|
||||||
source = ./dms/outputs.conf
|
source = ./dms/outputs.conf
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ func init() {
|
|||||||
Register("cachyos", "#08A283", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
|
Register("cachyos", "#08A283", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||||
return NewArchDistribution(config, logChan)
|
return NewArchDistribution(config, logChan)
|
||||||
})
|
})
|
||||||
|
Register("catos", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||||
|
return NewArchDistribution(config, logChan)
|
||||||
|
})
|
||||||
Register("endeavouros", "#7F3FBF", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
|
Register("endeavouros", "#7F3FBF", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||||
return NewArchDistribution(config, logChan)
|
return NewArchDistribution(config, logChan)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Packa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add repository
|
// Add repository
|
||||||
repoLine := fmt.Sprintf("deb [signed-by=%s, arch=%s] %s/ /", keyringPath, runtime.GOARCH, baseURL)
|
repoLine := fmt.Sprintf("deb [signed-by=%s arch=%s] %s/ /", keyringPath, runtime.GOARCH, baseURL)
|
||||||
|
|
||||||
progressChan <- InstallProgressMsg{
|
progressChan <- InstallProgressMsg{
|
||||||
Phase: PhaseSystemPackages,
|
Phase: PhaseSystemPackages,
|
||||||
|
|||||||
@@ -341,6 +341,8 @@ func (n *NiriProvider) buildActionFromNode(bindNode *document.Node) string {
|
|||||||
val := arg.ValueString()
|
val := arg.ValueString()
|
||||||
if val == "" {
|
if val == "" {
|
||||||
parts = append(parts, `""`)
|
parts = append(parts, `""`)
|
||||||
|
} else if strings.ContainsAny(val, " \t") {
|
||||||
|
parts = append(parts, `"`+strings.ReplaceAll(val, `"`, `\"`)+`"`)
|
||||||
} else {
|
} else {
|
||||||
parts = append(parts, val)
|
parts = append(parts, val)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const (
|
|||||||
TemplateKindTerminal
|
TemplateKindTerminal
|
||||||
TemplateKindGTK
|
TemplateKindGTK
|
||||||
TemplateKindVSCode
|
TemplateKindVSCode
|
||||||
|
TemplateKindEmacs
|
||||||
)
|
)
|
||||||
|
|
||||||
type TemplateDef struct {
|
type TemplateDef struct {
|
||||||
@@ -65,7 +66,7 @@ var templateRegistry = []TemplateDef{
|
|||||||
{ID: "dgop", Commands: []string{"dgop"}, ConfigFile: "dgop.toml"},
|
{ID: "dgop", Commands: []string{"dgop"}, ConfigFile: "dgop.toml"},
|
||||||
{ID: "kcolorscheme", ConfigFile: "kcolorscheme.toml", RunUnconditionally: true},
|
{ID: "kcolorscheme", ConfigFile: "kcolorscheme.toml", RunUnconditionally: true},
|
||||||
{ID: "vscode", Kind: TemplateKindVSCode},
|
{ID: "vscode", Kind: TemplateKindVSCode},
|
||||||
{ID: "emacs", Commands: []string{"emacs"}, ConfigFile: "emacs.toml"},
|
{ID: "emacs", Commands: []string{"emacs"}, ConfigFile: "emacs.toml", Kind: TemplateKindEmacs},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ColorMode) GTKTheme() string {
|
func (c *ColorMode) GTKTheme() string {
|
||||||
@@ -78,7 +79,8 @@ func (c *ColorMode) GTKTheme() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
matugenVersionOnce sync.Once
|
matugenVersionMu sync.Mutex
|
||||||
|
matugenVersionOK bool
|
||||||
matugenSupportsCOE bool
|
matugenSupportsCOE bool
|
||||||
matugenIsV4 bool
|
matugenIsV4 bool
|
||||||
)
|
)
|
||||||
@@ -334,6 +336,10 @@ output_path = '%s'
|
|||||||
appendVSCodeConfig(cfgFile, "cursor", filepath.Join(homeDir, ".cursor/extensions"), opts.ShellDir)
|
appendVSCodeConfig(cfgFile, "cursor", filepath.Join(homeDir, ".cursor/extensions"), opts.ShellDir)
|
||||||
appendVSCodeConfig(cfgFile, "windsurf", filepath.Join(homeDir, ".windsurf/extensions"), opts.ShellDir)
|
appendVSCodeConfig(cfgFile, "windsurf", filepath.Join(homeDir, ".windsurf/extensions"), opts.ShellDir)
|
||||||
appendVSCodeConfig(cfgFile, "vscode-insiders", filepath.Join(homeDir, ".vscode-insiders/extensions"), opts.ShellDir)
|
appendVSCodeConfig(cfgFile, "vscode-insiders", filepath.Join(homeDir, ".vscode-insiders/extensions"), opts.ShellDir)
|
||||||
|
case TemplateKindEmacs:
|
||||||
|
if utils.EmacsConfigDir() != "" {
|
||||||
|
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
|
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
|
||||||
}
|
}
|
||||||
@@ -491,6 +497,9 @@ func substituteVars(content, shellDir string) string {
|
|||||||
result = strings.ReplaceAll(result, "'CONFIG_DIR/", "'"+utils.XDGConfigHome()+"/")
|
result = strings.ReplaceAll(result, "'CONFIG_DIR/", "'"+utils.XDGConfigHome()+"/")
|
||||||
result = strings.ReplaceAll(result, "'DATA_DIR/", "'"+utils.XDGDataHome()+"/")
|
result = strings.ReplaceAll(result, "'DATA_DIR/", "'"+utils.XDGDataHome()+"/")
|
||||||
result = strings.ReplaceAll(result, "'CACHE_DIR/", "'"+utils.XDGCacheHome()+"/")
|
result = strings.ReplaceAll(result, "'CACHE_DIR/", "'"+utils.XDGCacheHome()+"/")
|
||||||
|
if emacsDir := utils.EmacsConfigDir(); emacsDir != "" {
|
||||||
|
result = strings.ReplaceAll(result, "'EMACS_DIR/", "'"+emacsDir+"/")
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,79 +520,160 @@ func extractTOMLSection(content, startMarker, endMarker string) string {
|
|||||||
return content[startIdx : startIdx+endIdx]
|
return content[startIdx : startIdx+endIdx]
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkMatugenVersion() {
|
type matugenFlags struct {
|
||||||
matugenVersionOnce.Do(func() {
|
supportsCOE bool
|
||||||
cmd := exec.Command("matugen", "--version")
|
isV4 bool
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
versionStr := strings.TrimSpace(string(output))
|
|
||||||
versionStr = strings.TrimPrefix(versionStr, "matugen ")
|
|
||||||
|
|
||||||
parts := strings.Split(versionStr, ".")
|
|
||||||
if len(parts) < 2 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
major, err := strconv.Atoi(parts[0])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
minor, err := strconv.Atoi(parts[1])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
matugenSupportsCOE = major > 3 || (major == 3 && minor >= 1)
|
|
||||||
matugenIsV4 = major >= 4
|
|
||||||
if matugenSupportsCOE {
|
|
||||||
log.Infof("Matugen %s supports --continue-on-error", versionStr)
|
|
||||||
}
|
|
||||||
if matugenIsV4 {
|
|
||||||
log.Infof("Matugen %s: using v4 flags", versionStr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMatugen(args []string) error {
|
func detectMatugenVersion() (matugenFlags, error) {
|
||||||
checkMatugenVersion()
|
matugenVersionMu.Lock()
|
||||||
|
defer matugenVersionMu.Unlock()
|
||||||
|
|
||||||
|
if matugenVersionOK {
|
||||||
|
return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return detectMatugenVersionLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
func redetectMatugenVersion(old matugenFlags) (matugenFlags, bool) {
|
||||||
|
matugenVersionMu.Lock()
|
||||||
|
defer matugenVersionMu.Unlock()
|
||||||
|
|
||||||
|
matugenVersionOK = false
|
||||||
|
flags, err := detectMatugenVersionLocked()
|
||||||
|
if err != nil {
|
||||||
|
return old, false
|
||||||
|
}
|
||||||
|
changed := flags.supportsCOE != old.supportsCOE || flags.isV4 != old.isV4
|
||||||
|
return flags, changed
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectMatugenVersionLocked() (matugenFlags, error) {
|
||||||
|
cmd := exec.Command("matugen", "--version")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return matugenFlags{}, fmt.Errorf("failed to get matugen version: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
versionStr := strings.TrimSpace(string(output))
|
||||||
|
versionStr = strings.TrimPrefix(versionStr, "matugen ")
|
||||||
|
|
||||||
|
parts := strings.Split(versionStr, ".")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return matugenFlags{}, fmt.Errorf("unexpected matugen version format: %q", versionStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
major, err := strconv.Atoi(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
return matugenFlags{}, fmt.Errorf("failed to parse matugen major version %q: %w", parts[0], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
minor, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return matugenFlags{}, fmt.Errorf("failed to parse matugen minor version %q: %w", parts[1], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
matugenSupportsCOE = major > 3 || (major == 3 && minor >= 1)
|
||||||
|
matugenIsV4 = major >= 4
|
||||||
|
matugenVersionOK = true
|
||||||
|
|
||||||
if matugenSupportsCOE {
|
if matugenSupportsCOE {
|
||||||
args = append([]string{"--continue-on-error"}, args...)
|
log.Infof("Matugen %s supports --continue-on-error", versionStr)
|
||||||
}
|
}
|
||||||
if matugenIsV4 {
|
if matugenIsV4 {
|
||||||
|
log.Infof("Matugen %s: using v4 flags", versionStr)
|
||||||
|
}
|
||||||
|
return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildMatugenArgs(baseArgs []string, flags matugenFlags) []string {
|
||||||
|
args := make([]string, 0, len(baseArgs)+4)
|
||||||
|
if flags.supportsCOE {
|
||||||
|
args = append(args, "--continue-on-error")
|
||||||
|
}
|
||||||
|
args = append(args, baseArgs...)
|
||||||
|
if flags.isV4 {
|
||||||
args = append(args, "--source-color-index", "0")
|
args = append(args, "--source-color-index", "0")
|
||||||
}
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMatugen(baseArgs []string) error {
|
||||||
|
flags, err := detectMatugenVersion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
args := buildMatugenArgs(baseArgs, flags)
|
||||||
cmd := exec.Command("matugen", args...)
|
cmd := exec.Command("matugen", args...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
return cmd.Run()
|
runErr := cmd.Run()
|
||||||
|
if runErr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("Matugen failed (v4=%v): %v", flags.isV4, runErr)
|
||||||
|
|
||||||
|
newFlags, changed := redetectMatugenVersion(flags)
|
||||||
|
if !changed {
|
||||||
|
return runErr
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("Matugen version changed (v4: %v -> %v), retrying", flags.isV4, newFlags.isV4)
|
||||||
|
args = buildMatugenArgs(baseArgs, newFlags)
|
||||||
|
retryCmd := exec.Command("matugen", args...)
|
||||||
|
retryCmd.Stdout = os.Stdout
|
||||||
|
retryCmd.Stderr = os.Stderr
|
||||||
|
return retryCmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMatugenDryRun(opts *Options) (string, error) {
|
func runMatugenDryRun(opts *Options) (string, error) {
|
||||||
checkMatugenVersion()
|
flags, err := detectMatugenVersion()
|
||||||
|
|
||||||
var args []string
|
|
||||||
switch opts.Kind {
|
|
||||||
case "hex":
|
|
||||||
args = []string{"color", "hex", opts.Value}
|
|
||||||
default:
|
|
||||||
args = []string{opts.Kind, opts.Value}
|
|
||||||
}
|
|
||||||
args = append(args, "-m", "dark", "-t", opts.MatugenType, "--json", "hex", "--dry-run")
|
|
||||||
if matugenIsV4 {
|
|
||||||
args = append(args, "--source-color-index", "0", "--old-json-output")
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command("matugen", args...)
|
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output, dryErr := execDryRun(opts, flags)
|
||||||
|
if dryErr == nil {
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("Matugen dry-run failed (v4=%v): %v", flags.isV4, dryErr)
|
||||||
|
|
||||||
|
newFlags, changed := redetectMatugenVersion(flags)
|
||||||
|
if !changed {
|
||||||
|
return "", dryErr
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("Matugen version changed (v4: %v -> %v), retrying dry-run", flags.isV4, newFlags.isV4)
|
||||||
|
return execDryRun(opts, newFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func execDryRun(opts *Options, flags matugenFlags) (string, error) {
|
||||||
|
var baseArgs []string
|
||||||
|
switch opts.Kind {
|
||||||
|
case "hex":
|
||||||
|
baseArgs = []string{"color", "hex", opts.Value}
|
||||||
|
default:
|
||||||
|
baseArgs = []string{opts.Kind, opts.Value}
|
||||||
|
}
|
||||||
|
baseArgs = append(baseArgs, "-m", "dark", "-t", opts.MatugenType, "--json", "hex", "--dry-run")
|
||||||
|
if flags.isV4 {
|
||||||
|
baseArgs = append(baseArgs, "--source-color-index", "0", "--old-json-output")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("matugen", baseArgs...)
|
||||||
|
var stderr strings.Builder
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
if stderr.Len() > 0 {
|
||||||
|
return "", fmt.Errorf("matugen %v failed (v4=%v): %s", baseArgs, flags.isV4, strings.TrimSpace(stderr.String()))
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("matugen %v failed (v4=%v): %w", baseArgs, flags.isV4, err)
|
||||||
|
}
|
||||||
return strings.ReplaceAll(string(output), "\n", ""), nil
|
return strings.ReplaceAll(string(output), "\n", ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -819,6 +909,8 @@ func CheckTemplates(checker utils.AppChecker) []TemplateCheck {
|
|||||||
detected = true
|
detected = true
|
||||||
case tmpl.Kind == TemplateKindVSCode:
|
case tmpl.Kind == TemplateKindVSCode:
|
||||||
detected = checkVSCodeExtension(homeDir)
|
detected = checkVSCodeExtension(homeDir)
|
||||||
|
case tmpl.Kind == TemplateKindEmacs:
|
||||||
|
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks) && utils.EmacsConfigDir() != ""
|
||||||
default:
|
default:
|
||||||
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks)
|
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ func (i *ExtWorkspaceManagerV1) Dispatch(opcode uint32, fd int, data []byte) {
|
|||||||
l := 0
|
l := 0
|
||||||
objectID := client.Uint32(data[l : l+4])
|
objectID := client.Uint32(data[l : l+4])
|
||||||
proxy := i.Context().GetProxy(objectID)
|
proxy := i.Context().GetProxy(objectID)
|
||||||
if proxy != nil {
|
if proxy != nil && !proxy.IsZombie() {
|
||||||
e.WorkspaceGroup = proxy.(*ExtWorkspaceGroupHandleV1)
|
e.WorkspaceGroup = proxy.(*ExtWorkspaceGroupHandleV1)
|
||||||
} else {
|
} else {
|
||||||
groupHandle := &ExtWorkspaceGroupHandleV1{}
|
groupHandle := &ExtWorkspaceGroupHandleV1{}
|
||||||
@@ -278,7 +278,7 @@ func (i *ExtWorkspaceManagerV1) Dispatch(opcode uint32, fd int, data []byte) {
|
|||||||
l := 0
|
l := 0
|
||||||
objectID := client.Uint32(data[l : l+4])
|
objectID := client.Uint32(data[l : l+4])
|
||||||
proxy := i.Context().GetProxy(objectID)
|
proxy := i.Context().GetProxy(objectID)
|
||||||
if proxy != nil {
|
if proxy != nil && !proxy.IsZombie() {
|
||||||
e.Workspace = proxy.(*ExtWorkspaceHandleV1)
|
e.Workspace = proxy.(*ExtWorkspaceHandleV1)
|
||||||
} else {
|
} else {
|
||||||
wsHandle := &ExtWorkspaceHandleV1{}
|
wsHandle := &ExtWorkspaceHandleV1{}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func NewRegionSelector(s *Screenshoter) *RegionSelector {
|
|||||||
screenshoter: s,
|
screenshoter: s,
|
||||||
outputs: make(map[uint32]*WaylandOutput),
|
outputs: make(map[uint32]*WaylandOutput),
|
||||||
preCapture: make(map[*WaylandOutput]*PreCapture),
|
preCapture: make(map[*WaylandOutput]*PreCapture),
|
||||||
showCapturedCursor: true,
|
showCapturedCursor: s.config.Cursor == CursorOn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -453,10 +453,7 @@ func (s *Screenshoter) blitBuffer(dst, src *ShmBuffer, dstX, dstY int, yInverted
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult, error) {
|
func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult, error) {
|
||||||
cursor := int32(0)
|
cursor := int32(s.config.Cursor)
|
||||||
if s.config.IncludeCursor {
|
|
||||||
cursor = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
frame, err := s.screencopy.CaptureOutput(cursor, output.wlOutput)
|
frame, err := s.screencopy.CaptureOutput(cursor, output.wlOutput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -624,10 +621,7 @@ func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Regio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor := int32(0)
|
cursor := int32(s.config.Cursor)
|
||||||
if s.config.IncludeCursor {
|
|
||||||
cursor = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h)
|
frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ const (
|
|||||||
FormatPPM
|
FormatPPM
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CursorMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CursorOff CursorMode = iota
|
||||||
|
CursorOn
|
||||||
|
)
|
||||||
|
|
||||||
type Region struct {
|
type Region struct {
|
||||||
X int32 `json:"x"`
|
X int32 `json:"x"`
|
||||||
Y int32 `json:"y"`
|
Y int32 `json:"y"`
|
||||||
@@ -42,29 +49,29 @@ type Output struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Mode Mode
|
Mode Mode
|
||||||
OutputName string
|
OutputName string
|
||||||
IncludeCursor bool
|
Cursor CursorMode
|
||||||
Format Format
|
Format Format
|
||||||
Quality int
|
Quality int
|
||||||
OutputDir string
|
OutputDir string
|
||||||
Filename string
|
Filename string
|
||||||
Clipboard bool
|
Clipboard bool
|
||||||
SaveFile bool
|
SaveFile bool
|
||||||
Notify bool
|
Notify bool
|
||||||
Stdout bool
|
Stdout bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultConfig() Config {
|
func DefaultConfig() Config {
|
||||||
return Config{
|
return Config{
|
||||||
Mode: ModeRegion,
|
Mode: ModeRegion,
|
||||||
IncludeCursor: false,
|
Cursor: CursorOff,
|
||||||
Format: FormatPNG,
|
Format: FormatPNG,
|
||||||
Quality: 90,
|
Quality: 90,
|
||||||
OutputDir: "",
|
OutputDir: "",
|
||||||
Filename: "",
|
Filename: "",
|
||||||
Clipboard: true,
|
Clipboard: true,
|
||||||
SaveFile: true,
|
SaveFile: true,
|
||||||
Notify: true,
|
Notify: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,8 +232,15 @@ func (m *Manager) setupDataDeviceSync() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevOffer := m.currentOffer
|
||||||
m.currentOffer = offer
|
m.currentOffer = offer
|
||||||
|
|
||||||
|
if prevOffer != nil && prevOffer != offer {
|
||||||
|
m.offerMutex.Lock()
|
||||||
|
delete(m.offerMimeTypes, prevOffer)
|
||||||
|
m.offerMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
m.offerMutex.RLock()
|
m.offerMutex.RLock()
|
||||||
mimes := m.offerMimeTypes[offer]
|
mimes := m.offerMimeTypes[offer]
|
||||||
m.offerMutex.RUnlock()
|
m.offerMutex.RUnlock()
|
||||||
@@ -587,20 +594,26 @@ func (m *Manager) uriListPreview(data []byte) (string, bool) {
|
|||||||
uris = strings.Split(text, "\n")
|
uris = strings.Split(text, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(uris) > 1 {
|
||||||
|
return fmt.Sprintf("[[ %d files ]]", len(uris)), false
|
||||||
|
}
|
||||||
|
|
||||||
if len(uris) == 1 && strings.HasPrefix(uris[0], "file://") {
|
if len(uris) == 1 && strings.HasPrefix(uris[0], "file://") {
|
||||||
filePath := strings.TrimPrefix(uris[0], "file://")
|
filePath := strings.TrimPrefix(uris[0], "file://")
|
||||||
if info, err := os.Stat(filePath); err == nil && !info.IsDir() {
|
info, err := os.Stat(filePath)
|
||||||
|
if err != nil || info.IsDir() {
|
||||||
|
return m.textPreview(data), false
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := m.getConfig()
|
||||||
|
if info.Size() <= cfg.MaxEntrySize {
|
||||||
if imgData, err := os.ReadFile(filePath); err == nil {
|
if imgData, err := os.ReadFile(filePath); err == nil {
|
||||||
if config, imgFmt, err := image.DecodeConfig(bytes.NewReader(imgData)); err == nil {
|
if config, imgFmt, err := image.DecodeConfig(bytes.NewReader(imgData)); err == nil {
|
||||||
return fmt.Sprintf("[[ file %s %s %dx%d ]]", filepath.Base(filePath), imgFmt, config.Width, config.Height), true
|
return fmt.Sprintf("[[ file %s %s %dx%d ]]", filepath.Base(filePath), imgFmt, config.Width, config.Height), true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("[[ file %s ]]", filepath.Base(filePath)), false
|
|
||||||
}
|
}
|
||||||
}
|
return fmt.Sprintf("[[ file %s ]]", filepath.Base(filePath)), false
|
||||||
|
|
||||||
if len(uris) > 1 {
|
|
||||||
return fmt.Sprintf("[[ %d files ]]", len(uris)), false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.textPreview(data), false
|
return m.textPreview(data), false
|
||||||
@@ -623,6 +636,11 @@ func (m *Manager) tryReadImageFromURI(data []byte) ([]byte, string, bool) {
|
|||||||
return nil, "", false
|
return nil, "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg := m.getConfig()
|
||||||
|
if info.Size() > cfg.MaxEntrySize {
|
||||||
|
return nil, "", false
|
||||||
|
}
|
||||||
|
|
||||||
imgData, err := os.ReadFile(filePath)
|
imgData, err := os.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", false
|
return nil, "", false
|
||||||
|
|||||||
@@ -16,4 +16,8 @@ const (
|
|||||||
dbusScreensaverPath = "/ScreenSaver"
|
dbusScreensaverPath = "/ScreenSaver"
|
||||||
dbusScreensaverPath2 = "/org/freedesktop/ScreenSaver"
|
dbusScreensaverPath2 = "/org/freedesktop/ScreenSaver"
|
||||||
dbusScreensaverInterface = "org.freedesktop.ScreenSaver"
|
dbusScreensaverInterface = "org.freedesktop.ScreenSaver"
|
||||||
|
|
||||||
|
dbusGnomeScreensaverName = "org.gnome.ScreenSaver"
|
||||||
|
dbusGnomeScreensaverPath = "/org/gnome/ScreenSaver"
|
||||||
|
dbusGnomeScreensaverInterface = "org.gnome.ScreenSaver"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -191,6 +191,12 @@ func (m *Manager) Close() {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
m.screensaverSubscribers.Range(func(key string, ch chan ScreensaverState) bool {
|
||||||
|
close(ch)
|
||||||
|
m.screensaverSubscribers.Delete(key)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
if m.systemConn != nil {
|
if m.systemConn != nil {
|
||||||
m.systemConn.Close()
|
m.systemConn.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package freedesktop
|
package freedesktop
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@@ -15,45 +16,9 @@ type screensaverHandler struct {
|
|||||||
manager *Manager
|
manager *Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) initializeScreensaver() error {
|
func screensaverIntrospectIface(ifaceName string) introspect.Interface {
|
||||||
if m.sessionConn == nil {
|
return introspect.Interface{
|
||||||
m.stateMutex.Lock()
|
Name: ifaceName,
|
||||||
m.state.Screensaver.Available = false
|
|
||||||
m.stateMutex.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
reply, err := m.sessionConn.RequestName(dbusScreensaverName, dbus.NameFlagDoNotQueue)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("Failed to request screensaver name: %v", err)
|
|
||||||
m.stateMutex.Lock()
|
|
||||||
m.state.Screensaver.Available = false
|
|
||||||
m.stateMutex.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if reply != dbus.RequestNameReplyPrimaryOwner {
|
|
||||||
log.Warnf("Screensaver name already owned by another process")
|
|
||||||
m.stateMutex.Lock()
|
|
||||||
m.state.Screensaver.Available = false
|
|
||||||
m.stateMutex.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
handler := &screensaverHandler{manager: m}
|
|
||||||
|
|
||||||
if err := m.sessionConn.Export(handler, dbusScreensaverPath, dbusScreensaverInterface); err != nil {
|
|
||||||
log.Warnf("Failed to export screensaver on %s: %v", dbusScreensaverPath, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.sessionConn.Export(handler, dbusScreensaverPath2, dbusScreensaverInterface); err != nil {
|
|
||||||
log.Warnf("Failed to export screensaver on %s: %v", dbusScreensaverPath2, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
screensaverIface := introspect.Interface{
|
|
||||||
Name: dbusScreensaverInterface,
|
|
||||||
Methods: []introspect.Method{
|
Methods: []introspect.Method{
|
||||||
{
|
{
|
||||||
Name: "Inhibit",
|
Name: "Inhibit",
|
||||||
@@ -69,40 +34,106 @@ func (m *Manager) initializeScreensaver() error {
|
|||||||
{Name: "cookie", Type: "u", Direction: "in"},
|
{Name: "cookie", Type: "u", Direction: "in"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "GetActive",
|
||||||
|
Args: []introspect.Arg{
|
||||||
|
{Name: "active", Type: "b", Direction: "out"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "SetActive",
|
||||||
|
Args: []introspect.Arg{
|
||||||
|
{Name: "active", Type: "b", Direction: "in"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Lock",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
Signals: []introspect.Signal{
|
||||||
|
{
|
||||||
|
Name: "ActiveChanged",
|
||||||
|
Args: []introspect.Arg{
|
||||||
|
{Name: "new_value", Type: "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) initializeScreensaver() error {
|
||||||
|
if m.sessionConn == nil {
|
||||||
|
m.stateMutex.Lock()
|
||||||
|
m.state.Screensaver.Available = false
|
||||||
|
m.stateMutex.Unlock()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
introNode := &introspect.Node{
|
handler := &screensaverHandler{manager: m}
|
||||||
Name: dbusScreensaverPath,
|
|
||||||
Interfaces: []introspect.Interface{
|
|
||||||
introspect.IntrospectData,
|
|
||||||
screensaverIface,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := m.sessionConn.Export(introspect.NewIntrospectable(introNode), dbusScreensaverPath, "org.freedesktop.DBus.Introspectable"); err != nil {
|
|
||||||
log.Warnf("Failed to export introspectable on %s: %v", dbusScreensaverPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
introNode2 := &introspect.Node{
|
m.screensaverFreedesktopClaimed = m.claimScreensaverName(handler,
|
||||||
Name: dbusScreensaverPath2,
|
dbusScreensaverName, dbusScreensaverInterface, dbusScreensaverPath, dbusScreensaverPath2)
|
||||||
Interfaces: []introspect.Interface{
|
m.screensaverGnomeClaimed = m.claimScreensaverName(handler,
|
||||||
introspect.IntrospectData,
|
dbusGnomeScreensaverName, dbusGnomeScreensaverInterface, dbusGnomeScreensaverPath)
|
||||||
screensaverIface,
|
|
||||||
},
|
if !m.screensaverFreedesktopClaimed && !m.screensaverGnomeClaimed {
|
||||||
}
|
log.Warn("No screensaver interface could be claimed")
|
||||||
if err := m.sessionConn.Export(introspect.NewIntrospectable(introNode2), dbusScreensaverPath2, "org.freedesktop.DBus.Introspectable"); err != nil {
|
m.stateMutex.Lock()
|
||||||
log.Warnf("Failed to export introspectable on %s: %v", dbusScreensaverPath2, err)
|
m.state.Screensaver.Available = false
|
||||||
|
m.stateMutex.Unlock()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
go m.watchPeerDisconnects()
|
go m.watchPeerDisconnects()
|
||||||
|
|
||||||
m.stateMutex.Lock()
|
m.stateMutex.Lock()
|
||||||
m.state.Screensaver.Available = true
|
m.state.Screensaver.Available = true
|
||||||
|
m.state.Screensaver.Active = false
|
||||||
m.state.Screensaver.Inhibited = false
|
m.state.Screensaver.Inhibited = false
|
||||||
m.state.Screensaver.Inhibitors = []ScreensaverInhibitor{}
|
m.state.Screensaver.Inhibitors = []ScreensaverInhibitor{}
|
||||||
m.stateMutex.Unlock()
|
m.stateMutex.Unlock()
|
||||||
|
|
||||||
log.Info("Screensaver inhibit listener initialized")
|
log.Info("Screensaver listener initialized")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) claimScreensaverName(handler *screensaverHandler, name, iface string, paths ...dbus.ObjectPath) bool {
|
||||||
|
reply, err := m.sessionConn.RequestName(name, dbus.NameFlagDoNotQueue)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to request screensaver name %s: %v", name, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if reply != dbus.RequestNameReplyPrimaryOwner {
|
||||||
|
log.Warnf("Screensaver name %s already owned by another process", name)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err := m.exportScreensaverOnPaths(handler, iface, paths...); err != nil {
|
||||||
|
log.Warnf("Failed to export screensaver on %s: %v", name, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
log.Infof("Claimed %s on session bus", name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// exportScreensaverOnPaths exports the handler and introspection on the given
|
||||||
|
// paths under the specified interface name.
|
||||||
|
func (m *Manager) exportScreensaverOnPaths(handler *screensaverHandler, ifaceName string, paths ...dbus.ObjectPath) error {
|
||||||
|
iface := screensaverIntrospectIface(ifaceName)
|
||||||
|
for _, path := range paths {
|
||||||
|
if err := m.sessionConn.Export(handler, path, ifaceName); err != nil {
|
||||||
|
return fmt.Errorf("export handler on %s: %w", path, err)
|
||||||
|
}
|
||||||
|
node := &introspect.Node{
|
||||||
|
Name: string(path),
|
||||||
|
Interfaces: []introspect.Interface{
|
||||||
|
introspect.IntrospectData,
|
||||||
|
iface,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := m.sessionConn.Export(introspect.NewIntrospectable(node), path, "org.freedesktop.DBus.Introspectable"); err != nil {
|
||||||
|
log.Warnf("Failed to export introspectable on %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,3 +299,51 @@ func (m *Manager) NotifyScreensaverSubscribers() {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *screensaverHandler) GetActive() (bool, *dbus.Error) {
|
||||||
|
h.manager.stateMutex.RLock()
|
||||||
|
active := h.manager.state.Screensaver.Active
|
||||||
|
h.manager.stateMutex.RUnlock()
|
||||||
|
return active, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *screensaverHandler) SetActive(active bool) *dbus.Error {
|
||||||
|
h.manager.SetScreenLockActive(active)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *screensaverHandler) Lock() *dbus.Error {
|
||||||
|
h.manager.SetScreenLockActive(true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) SetScreenLockActive(active bool) {
|
||||||
|
m.stateMutex.Lock()
|
||||||
|
changed := m.state.Screensaver.Active != active
|
||||||
|
m.state.Screensaver.Active = active
|
||||||
|
m.stateMutex.Unlock()
|
||||||
|
|
||||||
|
if !changed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Screen lock active changed: %v", active)
|
||||||
|
defer m.NotifyScreensaverSubscribers()
|
||||||
|
|
||||||
|
if m.sessionConn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.screensaverFreedesktopClaimed {
|
||||||
|
if err := m.sessionConn.Emit(dbusScreensaverPath, dbusScreensaverInterface+".ActiveChanged", active); err != nil {
|
||||||
|
log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusScreensaverPath, err)
|
||||||
|
}
|
||||||
|
if err := m.sessionConn.Emit(dbusScreensaverPath2, dbusScreensaverInterface+".ActiveChanged", active); err != nil {
|
||||||
|
log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusScreensaverPath2, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.screensaverGnomeClaimed {
|
||||||
|
if err := m.sessionConn.Emit(dbusGnomeScreensaverPath, dbusGnomeScreensaverInterface+".ActiveChanged", active); err != nil {
|
||||||
|
log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusGnomeScreensaverPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
102
core/internal/server/freedesktop/screensaver_test.go
Normal file
102
core/internal/server/freedesktop/screensaver_test.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package freedesktop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetScreenLockActive_ChangesState(t *testing.T) {
|
||||||
|
manager := &Manager{
|
||||||
|
state: &FreedeskState{
|
||||||
|
Screensaver: ScreensaverState{Available: true},
|
||||||
|
},
|
||||||
|
stateMutex: sync.RWMutex{},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.False(t, manager.GetScreensaverState().Active)
|
||||||
|
|
||||||
|
manager.SetScreenLockActive(true)
|
||||||
|
assert.True(t, manager.GetScreensaverState().Active)
|
||||||
|
|
||||||
|
manager.SetScreenLockActive(false)
|
||||||
|
assert.False(t, manager.GetScreensaverState().Active)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetScreenLockActive_NoChangeNoDuplicate(t *testing.T) {
|
||||||
|
ch := make(chan ScreensaverState, 64)
|
||||||
|
manager := &Manager{
|
||||||
|
state: &FreedeskState{
|
||||||
|
Screensaver: ScreensaverState{Available: true, Active: false},
|
||||||
|
},
|
||||||
|
stateMutex: sync.RWMutex{},
|
||||||
|
}
|
||||||
|
manager.screensaverSubscribers.Store("test", ch)
|
||||||
|
defer manager.screensaverSubscribers.Delete("test")
|
||||||
|
|
||||||
|
// Setting to same value should not notify
|
||||||
|
manager.SetScreenLockActive(false)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
t.Fatal("should not have received notification for no-change")
|
||||||
|
case <-time.After(50 * time.Millisecond):
|
||||||
|
// Expected: no notification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetScreenLockActive_NotifiesSubscribers(t *testing.T) {
|
||||||
|
ch := make(chan ScreensaverState, 64)
|
||||||
|
manager := &Manager{
|
||||||
|
state: &FreedeskState{
|
||||||
|
Screensaver: ScreensaverState{Available: true, Active: false},
|
||||||
|
},
|
||||||
|
stateMutex: sync.RWMutex{},
|
||||||
|
}
|
||||||
|
manager.screensaverSubscribers.Store("test", ch)
|
||||||
|
defer manager.screensaverSubscribers.Delete("test")
|
||||||
|
|
||||||
|
manager.SetScreenLockActive(true)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case state := <-ch:
|
||||||
|
assert.True(t, state.Active)
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("timeout waiting for subscriber notification")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetScreenLockActive_NilSessionConn(t *testing.T) {
|
||||||
|
manager := &Manager{
|
||||||
|
state: &FreedeskState{
|
||||||
|
Screensaver: ScreensaverState{Available: true},
|
||||||
|
},
|
||||||
|
stateMutex: sync.RWMutex{},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NotPanics(t, func() {
|
||||||
|
manager.SetScreenLockActive(true)
|
||||||
|
})
|
||||||
|
assert.True(t, manager.GetScreensaverState().Active)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetActive_ReturnsCurrentState(t *testing.T) {
|
||||||
|
manager := &Manager{
|
||||||
|
state: &FreedeskState{
|
||||||
|
Screensaver: ScreensaverState{Available: true, Active: true},
|
||||||
|
},
|
||||||
|
stateMutex: sync.RWMutex{},
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := &screensaverHandler{manager: manager}
|
||||||
|
active, dbusErr := handler.GetActive()
|
||||||
|
assert.Nil(t, dbusErr)
|
||||||
|
assert.True(t, active)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScreensaverState_ActiveDefaultsFalse(t *testing.T) {
|
||||||
|
state := ScreensaverState{}
|
||||||
|
assert.False(t, state.Active)
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ type ScreensaverInhibitor struct {
|
|||||||
|
|
||||||
type ScreensaverState struct {
|
type ScreensaverState struct {
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
|
Active bool `json:"active"`
|
||||||
Inhibited bool `json:"inhibited"`
|
Inhibited bool `json:"inhibited"`
|
||||||
Inhibitors []ScreensaverInhibitor `json:"inhibitors"`
|
Inhibitors []ScreensaverInhibitor `json:"inhibitors"`
|
||||||
}
|
}
|
||||||
@@ -50,14 +51,16 @@ type FreedeskState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
state *FreedeskState
|
state *FreedeskState
|
||||||
stateMutex sync.RWMutex
|
stateMutex sync.RWMutex
|
||||||
systemConn *dbus.Conn
|
systemConn *dbus.Conn
|
||||||
sessionConn *dbus.Conn
|
sessionConn *dbus.Conn
|
||||||
accountsObj dbus.BusObject
|
accountsObj dbus.BusObject
|
||||||
settingsObj dbus.BusObject
|
settingsObj dbus.BusObject
|
||||||
currentUID uint64
|
currentUID uint64
|
||||||
subscribers syncmap.Map[string, chan FreedeskState]
|
subscribers syncmap.Map[string, chan FreedeskState]
|
||||||
screensaverSubscribers syncmap.Map[string, chan ScreensaverState]
|
screensaverSubscribers syncmap.Map[string, chan ScreensaverState]
|
||||||
screensaverCookieCounter uint32
|
screensaverCookieCounter uint32
|
||||||
|
screensaverFreedesktopClaimed bool
|
||||||
|
screensaverGnomeClaimed bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1516,7 +1516,11 @@ func Start(printDocs bool) error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
loginctlReady := make(chan struct{})
|
||||||
|
freedesktopReady := make(chan struct{})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer close(loginctlReady)
|
||||||
if err := InitializeLoginctlManager(); err != nil {
|
if err := InitializeLoginctlManager(); err != nil {
|
||||||
log.Warnf("Loginctl manager unavailable: %v", err)
|
log.Warnf("Loginctl manager unavailable: %v", err)
|
||||||
} else {
|
} else {
|
||||||
@@ -1525,6 +1529,7 @@ func Start(printDocs bool) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer close(freedesktopReady)
|
||||||
if err := InitializeFreedeskManager(); err != nil {
|
if err := InitializeFreedeskManager(); err != nil {
|
||||||
log.Warnf("Freedesktop manager unavailable: %v", err)
|
log.Warnf("Freedesktop manager unavailable: %v", err)
|
||||||
} else if freedesktopManager != nil {
|
} else if freedesktopManager != nil {
|
||||||
@@ -1533,6 +1538,31 @@ func Start(printDocs bool) error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Bridge loginctl lock state to the freedesktop/gnome screensaver
|
||||||
|
// ActiveChanged signal so apps like Bitwarden can detect screen lock.
|
||||||
|
go func() {
|
||||||
|
<-loginctlReady
|
||||||
|
<-freedesktopReady
|
||||||
|
|
||||||
|
if loginctlManager == nil || freedesktopManager == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := loginctlManager.Subscribe("dms-lock-bridge")
|
||||||
|
defer loginctlManager.Unsubscribe("dms-lock-bridge")
|
||||||
|
|
||||||
|
initial := loginctlManager.GetState()
|
||||||
|
lastLocked := initial.Locked
|
||||||
|
freedesktopManager.SetScreenLockActive(lastLocked)
|
||||||
|
|
||||||
|
for state := range ch {
|
||||||
|
if state.Locked != lastLocked {
|
||||||
|
lastLocked = state.Locked
|
||||||
|
freedesktopManager.SetScreenLockActive(lastLocked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if err := InitializeWaylandManager(); err != nil {
|
if err := InitializeWaylandManager(); err != nil {
|
||||||
log.Warnf("Wayland manager unavailable: %v", err)
|
log.Warnf("Wayland manager unavailable: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ func TestCleanupStaleSockets(t *testing.T) {
|
|||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
t.Setenv("XDG_RUNTIME_DIR", tempDir)
|
t.Setenv("XDG_RUNTIME_DIR", tempDir)
|
||||||
|
|
||||||
staleSocket := filepath.Join(tempDir, "danklinux-999999.sock")
|
staleSocket := filepath.Join(tempDir, "danklinux-4194305.sock")
|
||||||
err := os.WriteFile(staleSocket, []byte{}, 0o600)
|
err := os.WriteFile(staleSocket, []byte{}, 0o600)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,22 @@ func XDGConfigHome() string {
|
|||||||
return filepath.Join(home, ".config")
|
return filepath.Join(home, ".config")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EmacsConfigDir() string {
|
||||||
|
home, _ := os.UserHomeDir()
|
||||||
|
|
||||||
|
emacsD := filepath.Join(home, ".emacs.d")
|
||||||
|
if info, err := os.Stat(emacsD); err == nil && info.IsDir() {
|
||||||
|
return emacsD
|
||||||
|
}
|
||||||
|
|
||||||
|
xdgEmacs := filepath.Join(XDGConfigHome(), "emacs")
|
||||||
|
if info, err := os.Stat(xdgEmacs); err == nil && info.IsDir() {
|
||||||
|
return xdgEmacs
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func ExpandPath(path string) (string, error) {
|
func ExpandPath(path string) (string, error) {
|
||||||
expanded := os.ExpandEnv(path)
|
expanded := os.ExpandEnv(path)
|
||||||
expanded = filepath.Clean(expanded)
|
expanded = filepath.Clean(expanded)
|
||||||
|
|||||||
@@ -181,7 +181,7 @@
|
|||||||
buildInputs =
|
buildInputs =
|
||||||
with pkgs;
|
with pkgs;
|
||||||
[
|
[
|
||||||
go_1_24
|
go_1_25
|
||||||
gopls
|
gopls
|
||||||
delve
|
delve
|
||||||
go-tools
|
go-tools
|
||||||
@@ -189,6 +189,7 @@
|
|||||||
|
|
||||||
prek
|
prek
|
||||||
uv # for prek
|
uv # for prek
|
||||||
|
shellcheck
|
||||||
|
|
||||||
# Nix development tools
|
# Nix development tools
|
||||||
nixd
|
nixd
|
||||||
|
|||||||
@@ -142,6 +142,8 @@ Singleton {
|
|||||||
if (tabIndex !== undefined && popout.currentTabIndex !== undefined) {
|
if (tabIndex !== undefined && popout.currentTabIndex !== undefined) {
|
||||||
popout.currentTabIndex = tabIndex;
|
popout.currentTabIndex = tabIndex;
|
||||||
}
|
}
|
||||||
|
if (popout.updateSurfacePosition)
|
||||||
|
popout.updateSurfacePosition();
|
||||||
currentPopoutTriggers[screenName] = triggerId;
|
currentPopoutTriggers[screenName] = triggerId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ Singleton {
|
|||||||
property bool _hasLoaded: false
|
property bool _hasLoaded: false
|
||||||
property bool _isReadOnly: false
|
property bool _isReadOnly: false
|
||||||
property bool _hasUnsavedChanges: false
|
property bool _hasUnsavedChanges: false
|
||||||
|
property bool _selfWrite: false
|
||||||
property var _loadedSettingsSnapshot: null
|
property var _loadedSettingsSnapshot: null
|
||||||
property var pluginSettings: ({})
|
property var pluginSettings: ({})
|
||||||
property var builtInPluginSettings: ({})
|
property var builtInPluginSettings: ({})
|
||||||
@@ -1243,6 +1244,7 @@ Singleton {
|
|||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
if (_loading || _parseError || !_hasLoaded)
|
if (_loading || _parseError || !_hasLoaded)
|
||||||
return;
|
return;
|
||||||
|
_selfWrite = true;
|
||||||
settingsFile.setText(JSON.stringify(Store.toJson(root), null, 2));
|
settingsFile.setText(JSON.stringify(Store.toJson(root), null, 2));
|
||||||
if (_isReadOnly)
|
if (_isReadOnly)
|
||||||
_checkSettingsWritable();
|
_checkSettingsWritable();
|
||||||
@@ -2589,7 +2591,13 @@ Singleton {
|
|||||||
blockWrites: true
|
blockWrites: true
|
||||||
atomicWrites: true
|
atomicWrites: true
|
||||||
watchChanges: true
|
watchChanges: true
|
||||||
onFileChanged: settingsFileReloadDebounce.restart()
|
onFileChanged: {
|
||||||
|
if (_selfWrite) {
|
||||||
|
_selfWrite = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
settingsFileReloadDebounce.restart();
|
||||||
|
}
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
if (isGreeterMode)
|
if (isGreeterMode)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -859,6 +859,70 @@ Item {
|
|||||||
return success ? `WIDGET_TOGGLE_SUCCESS: ${widgetId}` : `WIDGET_TOGGLE_FAILED: ${widgetId}`;
|
return success ? `WIDGET_TOGGLE_SUCCESS: ${widgetId}` : `WIDGET_TOGGLE_FAILED: ${widgetId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openWith(widgetId: string, mode: string): string {
|
||||||
|
if (!widgetId)
|
||||||
|
return "ERROR: No widget ID specified";
|
||||||
|
if (!BarWidgetService.hasWidget(widgetId))
|
||||||
|
return `WIDGET_NOT_FOUND: ${widgetId}`;
|
||||||
|
|
||||||
|
const widget = BarWidgetService.getWidgetOnFocusedScreen(widgetId);
|
||||||
|
if (!widget)
|
||||||
|
return `WIDGET_NOT_AVAILABLE: ${widgetId}`;
|
||||||
|
if (typeof widget.openWithMode !== "function")
|
||||||
|
return `WIDGET_OPEN_WITH_NOT_SUPPORTED: ${widgetId}`;
|
||||||
|
|
||||||
|
widget.openWithMode(mode || "all");
|
||||||
|
return `WIDGET_OPEN_WITH_SUCCESS: ${widgetId} ${mode}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleWith(widgetId: string, mode: string): string {
|
||||||
|
if (!widgetId)
|
||||||
|
return "ERROR: No widget ID specified";
|
||||||
|
if (!BarWidgetService.hasWidget(widgetId))
|
||||||
|
return `WIDGET_NOT_FOUND: ${widgetId}`;
|
||||||
|
|
||||||
|
const widget = BarWidgetService.getWidgetOnFocusedScreen(widgetId);
|
||||||
|
if (!widget)
|
||||||
|
return `WIDGET_NOT_AVAILABLE: ${widgetId}`;
|
||||||
|
if (typeof widget.toggleWithMode !== "function")
|
||||||
|
return `WIDGET_TOGGLE_WITH_NOT_SUPPORTED: ${widgetId}`;
|
||||||
|
|
||||||
|
widget.toggleWithMode(mode || "all");
|
||||||
|
return `WIDGET_TOGGLE_WITH_SUCCESS: ${widgetId} ${mode}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openQuery(widgetId: string, query: string): string {
|
||||||
|
if (!widgetId)
|
||||||
|
return "ERROR: No widget ID specified";
|
||||||
|
if (!BarWidgetService.hasWidget(widgetId))
|
||||||
|
return `WIDGET_NOT_FOUND: ${widgetId}`;
|
||||||
|
|
||||||
|
const widget = BarWidgetService.getWidgetOnFocusedScreen(widgetId);
|
||||||
|
if (!widget)
|
||||||
|
return `WIDGET_NOT_AVAILABLE: ${widgetId}`;
|
||||||
|
if (typeof widget.openWithQuery !== "function")
|
||||||
|
return `WIDGET_OPEN_QUERY_NOT_SUPPORTED: ${widgetId}`;
|
||||||
|
|
||||||
|
widget.openWithQuery(query || "");
|
||||||
|
return `WIDGET_OPEN_QUERY_SUCCESS: ${widgetId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleQuery(widgetId: string, query: string): string {
|
||||||
|
if (!widgetId)
|
||||||
|
return "ERROR: No widget ID specified";
|
||||||
|
if (!BarWidgetService.hasWidget(widgetId))
|
||||||
|
return `WIDGET_NOT_FOUND: ${widgetId}`;
|
||||||
|
|
||||||
|
const widget = BarWidgetService.getWidgetOnFocusedScreen(widgetId);
|
||||||
|
if (!widget)
|
||||||
|
return `WIDGET_NOT_AVAILABLE: ${widgetId}`;
|
||||||
|
if (typeof widget.toggleWithQuery !== "function")
|
||||||
|
return `WIDGET_TOGGLE_QUERY_NOT_SUPPORTED: ${widgetId}`;
|
||||||
|
|
||||||
|
widget.toggleWithQuery(query || "");
|
||||||
|
return `WIDGET_TOGGLE_QUERY_SUCCESS: ${widgetId}`;
|
||||||
|
}
|
||||||
|
|
||||||
function list(): string {
|
function list(): string {
|
||||||
const widgets = BarWidgetService.getRegisteredWidgetIds();
|
const widgets = BarWidgetService.getRegisteredWidgetIds();
|
||||||
if (widgets.length === 0)
|
if (widgets.length === 0)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ FloatingWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
objectName: "changelogModal"
|
objectName: "changelogModal"
|
||||||
title: "What's New"
|
title: i18n("What's New")
|
||||||
minimumSize: Qt.size(modalWidth, modalHeight)
|
minimumSize: Qt.size(modalWidth, modalHeight)
|
||||||
maximumSize: Qt.size(modalWidth, modalHeight)
|
maximumSize: Qt.size(modalWidth, modalHeight)
|
||||||
color: Theme.surfaceContainer
|
color: Theme.surfaceContainer
|
||||||
@@ -81,7 +81,7 @@ FloatingWindow {
|
|||||||
onClicked: root.dismiss()
|
onClicked: root.dismiss()
|
||||||
|
|
||||||
DankTooltip {
|
DankTooltip {
|
||||||
text: "Close"
|
text: i18n("Close")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ FloatingWindow {
|
|||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
DankButton {
|
DankButton {
|
||||||
text: "Read Full Release Notes"
|
text: i18n("Read Full Release Notes")
|
||||||
iconName: "open_in_new"
|
iconName: "open_in_new"
|
||||||
backgroundColor: Theme.surfaceContainerHighest
|
backgroundColor: Theme.surfaceContainerHighest
|
||||||
textColor: Theme.surfaceText
|
textColor: Theme.surfaceText
|
||||||
@@ -133,7 +133,7 @@ FloatingWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DankButton {
|
DankButton {
|
||||||
text: "Got It"
|
text: i18n("Got It")
|
||||||
iconName: "check"
|
iconName: "check"
|
||||||
backgroundColor: Theme.primary
|
backgroundColor: Theme.primary
|
||||||
textColor: Theme.primaryText
|
textColor: Theme.primaryText
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Singleton {
|
|||||||
readonly property int popoutWidth: 550
|
readonly property int popoutWidth: 550
|
||||||
readonly property int popoutHeight: 500
|
readonly property int popoutHeight: 500
|
||||||
readonly property int itemHeight: 72
|
readonly property int itemHeight: 72
|
||||||
readonly property int thumbnailSize: 48
|
readonly property int thumbnailSize: 100
|
||||||
readonly property int retryInterval: 50
|
readonly property int retryInterval: 50
|
||||||
readonly property int viewportBuffer: 100
|
readonly property int viewportBuffer: 100
|
||||||
readonly property int extendedBuffer: 200
|
readonly property int extendedBuffer: 200
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ Rectangle {
|
|||||||
anchors.right: actionButtons.left
|
anchors.right: actionButtons.left
|
||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
height: contentColumn.implicitHeight
|
// height: contentColumn.implicitHeight
|
||||||
|
height: ClipboardConstants.itemHeight
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
ClipboardThumbnail {
|
ClipboardThumbnail {
|
||||||
@@ -92,7 +93,7 @@ Rectangle {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize
|
width: entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize
|
||||||
height: entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize
|
height: entryType === "image" ? ClipboardConstants.itemHeight - 4 : Theme.iconSize // 100 - 4 = 96, 96:72 = 4:3
|
||||||
entry: root.entry
|
entry: root.entry
|
||||||
entryType: root.entryType
|
entryType: root.entryType
|
||||||
modal: root.modal
|
modal: root.modal
|
||||||
|
|||||||
@@ -137,23 +137,23 @@ Item {
|
|||||||
anchors.margins: 2
|
anchors.margins: 2
|
||||||
source: thumbnailImage
|
source: thumbnailImage
|
||||||
maskEnabled: true
|
maskEnabled: true
|
||||||
maskSource: clipboardCircularMask
|
maskSource: clipboardRoundedRectangularMask
|
||||||
visible: entryType === "image" && thumbnailImage.status === Image.Ready && thumbnailImage.source != ""
|
visible: entryType === "image" && thumbnailImage.status === Image.Ready && thumbnailImage.source != ""
|
||||||
maskThresholdMin: 0.5
|
maskThresholdMin: 0.5
|
||||||
maskSpreadAtMin: 1
|
maskSpreadAtMin: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: clipboardCircularMask
|
id: clipboardRoundedRectangularMask
|
||||||
width: ClipboardConstants.thumbnailSize - 4
|
width: ClipboardConstants.thumbnailSize
|
||||||
height: ClipboardConstants.thumbnailSize - 4
|
height: ClipboardConstants.itemHeight - 4
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.smooth: true
|
layer.smooth: true
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: width / 2
|
radius: Theme.cornerRadius / 2 // Thumbnail corner radius is divided by 2 so it doesnt look weird on large corner radius (eg: 32px)
|
||||||
color: "black"
|
color: "black"
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,11 +56,6 @@ Rectangle {
|
|||||||
case "app":
|
case "app":
|
||||||
if (selectedItem?.isCore)
|
if (selectedItem?.isCore)
|
||||||
break;
|
break;
|
||||||
if (selectedItem?.actions) {
|
|
||||||
for (var i = 0; i < selectedItem.actions.length; i++) {
|
|
||||||
result.push(selectedItem.actions[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (SessionService.nvidiaCommand) {
|
if (SessionService.nvidiaCommand) {
|
||||||
result.push({
|
result.push({
|
||||||
name: I18n.tr("Launch on dGPU"),
|
name: I18n.tr("Launch on dGPU"),
|
||||||
@@ -68,6 +63,11 @@ Rectangle {
|
|||||||
action: "launch_dgpu"
|
action: "launch_dgpu"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (selectedItem?.actions) {
|
||||||
|
for (var i = 0; i < selectedItem.actions.length; i++) {
|
||||||
|
result.push(selectedItem.actions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -125,13 +125,6 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly property var sectionDefinitions: [
|
readonly property var sectionDefinitions: [
|
||||||
{
|
|
||||||
id: "calculator",
|
|
||||||
title: I18n.tr("Calculator"),
|
|
||||||
icon: "calculate",
|
|
||||||
priority: 0,
|
|
||||||
defaultViewMode: "list"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "favorites",
|
id: "favorites",
|
||||||
title: I18n.tr("Pinned"),
|
title: I18n.tr("Pinned"),
|
||||||
@@ -681,12 +674,6 @@ Item {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var calculatorResult = evaluateCalculator(searchQuery);
|
|
||||||
if (calculatorResult) {
|
|
||||||
calculatorResult._preScored = 12000;
|
|
||||||
allItems.push(calculatorResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
var apps = searchApps(searchQuery);
|
var apps = searchApps(searchQuery);
|
||||||
for (var i = 0; i < apps.length; i++) {
|
for (var i = 0; i < apps.length; i++) {
|
||||||
if (searchQuery)
|
if (searchQuery)
|
||||||
@@ -697,12 +684,15 @@ Item {
|
|||||||
if (searchMode === "all") {
|
if (searchMode === "all") {
|
||||||
if (searchQuery && searchQuery.length >= 2) {
|
if (searchQuery && searchQuery.length >= 2) {
|
||||||
_pluginPhasePending = true;
|
_pluginPhasePending = true;
|
||||||
_pluginPhaseForceFirst = shouldResetSelection;
|
_phase1Items = allItems.slice();
|
||||||
_phase1Items = allItems;
|
|
||||||
pluginPhaseTimer.restart();
|
pluginPhaseTimer.restart();
|
||||||
isSearching = true;
|
if (allItems.length === 0) {
|
||||||
searchCompleted();
|
_pluginPhaseForceFirst = shouldResetSelection;
|
||||||
return;
|
isSearching = true;
|
||||||
|
searchCompleted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pluginPhaseForceFirst = false;
|
||||||
} else if (!searchQuery) {
|
} else if (!searchQuery) {
|
||||||
var emptyTriggerOrdered = getEmptyTriggerPluginsOrdered();
|
var emptyTriggerOrdered = getEmptyTriggerPluginsOrdered();
|
||||||
for (var i = 0; i < emptyTriggerOrdered.length; i++) {
|
for (var i = 0; i < emptyTriggerOrdered.length; i++) {
|
||||||
@@ -931,13 +921,6 @@ Item {
|
|||||||
return Transform.transformFileResult(file, I18n.tr("Open"), I18n.tr("Open folder"), I18n.tr("Copy path"));
|
return Transform.transformFileResult(file, I18n.tr("Open"), I18n.tr("Open folder"), I18n.tr("Copy path"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function evaluateCalculator(query) {
|
|
||||||
var calc = Utils.evaluateCalculator(query);
|
|
||||||
if (!calc)
|
|
||||||
return null;
|
|
||||||
return Transform.createCalculatorItem(calc, query, I18n.tr("Copy"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function detectTrigger(query) {
|
function detectTrigger(query) {
|
||||||
if (!query || query.length === 0)
|
if (!query || query.length === 0)
|
||||||
return {
|
return {
|
||||||
@@ -1270,6 +1253,23 @@ Item {
|
|||||||
CacheData.saveLauncherCache(serializable);
|
CacheData.saveLauncherCache(serializable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _actionsFromDesktopEntry(appId) {
|
||||||
|
if (!appId)
|
||||||
|
return [];
|
||||||
|
var entry = DesktopEntries.heuristicLookup(appId);
|
||||||
|
if (!entry || !entry.actions || entry.actions.length === 0)
|
||||||
|
return [];
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < entry.actions.length; i++) {
|
||||||
|
result.push({
|
||||||
|
name: entry.actions[i].name,
|
||||||
|
icon: "play_arrow",
|
||||||
|
actionData: entry.actions[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function _loadDiskCache() {
|
function _loadDiskCache() {
|
||||||
var cached = CacheData.loadLauncherCache();
|
var cached = CacheData.loadLauncherCache();
|
||||||
if (!cached || !Array.isArray(cached) || cached.length === 0)
|
if (!cached || !Array.isArray(cached) || cached.length === 0)
|
||||||
@@ -1297,8 +1297,12 @@ Item {
|
|||||||
data: {
|
data: {
|
||||||
id: it.id
|
id: it.id
|
||||||
},
|
},
|
||||||
actions: [],
|
actions: _actionsFromDesktopEntry(it.id),
|
||||||
primaryAction: null,
|
primaryAction: it.type === "app" && !it.isCore ? {
|
||||||
|
name: I18n.tr("Launch"),
|
||||||
|
icon: "open_in_new",
|
||||||
|
action: "launch"
|
||||||
|
} : null,
|
||||||
_diskCached: true,
|
_diskCached: true,
|
||||||
_hName: "",
|
_hName: "",
|
||||||
_hSub: "",
|
_hSub: "",
|
||||||
@@ -1559,9 +1563,6 @@ Item {
|
|||||||
case "file":
|
case "file":
|
||||||
openFile(item.data?.path);
|
openFile(item.data?.path);
|
||||||
break;
|
break;
|
||||||
case "calculator":
|
|
||||||
copyToClipboard(item.name);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1616,25 +1617,41 @@ Item {
|
|||||||
itemExecuted();
|
itemExecuted();
|
||||||
}
|
}
|
||||||
|
|
||||||
function launchApp(app) {
|
function _resolveDesktopEntry(app) {
|
||||||
if (!app)
|
if (!app)
|
||||||
|
return null;
|
||||||
|
if (app.command)
|
||||||
|
return app;
|
||||||
|
var id = app.id || app.execString || app.exec || "";
|
||||||
|
if (!id)
|
||||||
|
return null;
|
||||||
|
return DesktopEntries.heuristicLookup(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function launchApp(app) {
|
||||||
|
var entry = _resolveDesktopEntry(app);
|
||||||
|
if (!entry)
|
||||||
return;
|
return;
|
||||||
SessionService.launchDesktopEntry(app);
|
SessionService.launchDesktopEntry(entry);
|
||||||
AppUsageHistoryData.addAppUsage(app);
|
AppUsageHistoryData.addAppUsage(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
function launchAppWithNvidia(app) {
|
function launchAppWithNvidia(app) {
|
||||||
if (!app)
|
var entry = _resolveDesktopEntry(app);
|
||||||
|
if (!entry)
|
||||||
return;
|
return;
|
||||||
SessionService.launchDesktopEntry(app, true);
|
SessionService.launchDesktopEntry(entry, true);
|
||||||
AppUsageHistoryData.addAppUsage(app);
|
AppUsageHistoryData.addAppUsage(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
function launchAppAction(actionItem) {
|
function launchAppAction(actionItem) {
|
||||||
if (!actionItem || !actionItem.parentApp || !actionItem.actionData)
|
if (!actionItem || !actionItem.actionData)
|
||||||
return;
|
return;
|
||||||
SessionService.launchDesktopAction(actionItem.parentApp, actionItem.actionData);
|
var entry = _resolveDesktopEntry(actionItem.parentApp);
|
||||||
AppUsageHistoryData.addAppUsage(actionItem.parentApp);
|
if (!entry)
|
||||||
|
return;
|
||||||
|
SessionService.launchDesktopAction(entry, actionItem.actionData);
|
||||||
|
AppUsageHistoryData.addAppUsage(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openFile(path) {
|
function openFile(path) {
|
||||||
|
|||||||
@@ -101,35 +101,6 @@ function detectIconType(iconName) {
|
|||||||
return "material";
|
return "material";
|
||||||
}
|
}
|
||||||
|
|
||||||
function evaluateCalculator(query) {
|
|
||||||
if (!query || query.length === 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var mathExpr = query.replace(/[^0-9+\-*/().%\s^]/g, "");
|
|
||||||
if (mathExpr.length < 2)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var hasMath = /[+\-*/^%]/.test(query) && /\d/.test(query);
|
|
||||||
if (!hasMath)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var sanitized = mathExpr.replace(/\^/g, "**");
|
|
||||||
var result = Function('"use strict"; return (' + sanitized + ')')();
|
|
||||||
|
|
||||||
if (typeof result === "number" && isFinite(result)) {
|
|
||||||
var displayResult = Number.isInteger(result) ? result.toString() : result.toFixed(6).replace(/\.?0+$/, "");
|
|
||||||
return {
|
|
||||||
expression: query,
|
|
||||||
result: result,
|
|
||||||
displayResult: displayResult
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (e) { }
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortPluginIdsByOrder(pluginIds, order) {
|
function sortPluginIdsByOrder(pluginIds, order) {
|
||||||
if (!order || order.length === 0)
|
if (!order || order.length === 0)
|
||||||
return pluginIds;
|
return pluginIds;
|
||||||
|
|||||||
@@ -190,32 +190,6 @@ function transformPluginItem(item, pluginId, selectLabel) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCalculatorItem(calc, query, copyLabel) {
|
|
||||||
return {
|
|
||||||
id: "calculator_result",
|
|
||||||
type: "calculator",
|
|
||||||
name: calc.displayResult,
|
|
||||||
subtitle: query + " =",
|
|
||||||
icon: "calculate",
|
|
||||||
iconType: "material",
|
|
||||||
section: "calculator",
|
|
||||||
data: {
|
|
||||||
expression: calc.expression,
|
|
||||||
result: calc.result
|
|
||||||
},
|
|
||||||
actions: [],
|
|
||||||
primaryAction: {
|
|
||||||
name: copyLabel,
|
|
||||||
icon: "content_copy",
|
|
||||||
action: "copy"
|
|
||||||
},
|
|
||||||
_hName: "",
|
|
||||||
_hSub: "",
|
|
||||||
_hRich: false,
|
|
||||||
_preScored: undefined
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPluginBrowseItem(pluginId, plugin, trigger, isBuiltIn, isAllowed, browseLabel, triggerLabel, noTriggerLabel) {
|
function createPluginBrowseItem(pluginId, plugin, trigger, isBuiltIn, isAllowed, browseLabel, triggerLabel, noTriggerLabel) {
|
||||||
var rawIcon = isBuiltIn ? (plugin.cornerIcon || "extension") : (plugin.icon || "extension");
|
var rawIcon = isBuiltIn ? (plugin.cornerIcon || "extension") : (plugin.icon || "extension");
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -470,9 +470,6 @@ FocusScope {
|
|||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
controller.setSearchQuery(text);
|
controller.setSearchQuery(text);
|
||||||
if (text.length === 0) {
|
|
||||||
controller.restorePreviousMode();
|
|
||||||
}
|
|
||||||
if (actionPanel.expanded) {
|
if (actionPanel.expanded) {
|
||||||
actionPanel.hide();
|
actionPanel.hide();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,12 @@ function calculateNextIndex(flatModel, selectedFlatIndex, sectionId, viewMode, g
|
|||||||
return bounds.start + newPosInSection;
|
return bounds.start + newPosInSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var currentRow = Math.floor(posInSection / cols);
|
||||||
|
var lastRow = Math.floor((bounds.count - 1) / cols);
|
||||||
|
if (currentRow < lastRow) {
|
||||||
|
return bounds.start + bounds.count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
var nextSection = findNextNonHeaderIndex(flatModel, bounds.end + 1);
|
var nextSection = findNextNonHeaderIndex(flatModel, bounds.end + 1);
|
||||||
return nextSection !== -1 ? nextSection : selectedFlatIndex;
|
return nextSection !== -1 ? nextSection : selectedFlatIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,8 +178,6 @@ Rectangle {
|
|||||||
if (!root.item)
|
if (!root.item)
|
||||||
return "";
|
return "";
|
||||||
switch (root.item.type) {
|
switch (root.item.type) {
|
||||||
case "calculator":
|
|
||||||
return I18n.tr("Calc");
|
|
||||||
case "plugin":
|
case "plugin":
|
||||||
return I18n.tr("Plugin");
|
return I18n.tr("Plugin");
|
||||||
case "file":
|
case "file":
|
||||||
|
|||||||
@@ -130,24 +130,17 @@ Item {
|
|||||||
if (!entry || entry.isHeader)
|
if (!entry || entry.isHeader)
|
||||||
return;
|
return;
|
||||||
var rowIndex = _flatIndexToRowMap[index];
|
var rowIndex = _flatIndexToRowMap[index];
|
||||||
if (rowIndex === undefined || rowIndex >= _cumulativeHeights.length)
|
if (rowIndex === undefined)
|
||||||
return;
|
|
||||||
var row = _visualRows[rowIndex];
|
|
||||||
if (!row)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var rowY = _cumulativeHeights[rowIndex];
|
mainListView.positionViewAtIndex(rowIndex, ListView.Contain);
|
||||||
var rowHeight = row.height;
|
|
||||||
var scrollY = mainListView.contentY - mainListView.originY;
|
|
||||||
var viewHeight = mainListView.height;
|
|
||||||
var headerH = stickyHeader.height;
|
|
||||||
|
|
||||||
if (rowY < scrollY + headerH) {
|
if (stickyHeader.visible && rowIndex < _cumulativeHeights.length) {
|
||||||
mainListView.contentY = Math.max(mainListView.originY, rowY - headerH + mainListView.originY);
|
var rowY = _cumulativeHeights[rowIndex];
|
||||||
return;
|
var scrollY = mainListView.contentY - mainListView.originY;
|
||||||
}
|
if (rowY < scrollY + stickyHeader.height) {
|
||||||
if (rowY + rowHeight > scrollY + viewHeight) {
|
mainListView.contentY = Math.max(mainListView.originY, rowY - stickyHeader.height + mainListView.originY);
|
||||||
mainListView.contentY = rowY + rowHeight - viewHeight + mainListView.originY;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -238,37 +238,37 @@ FocusScope {
|
|||||||
|
|
||||||
property var quickAccessLocations: [
|
property var quickAccessLocations: [
|
||||||
{
|
{
|
||||||
"name": "Home",
|
"name": I18n.tr("Home"),
|
||||||
"path": homeDir,
|
"path": homeDir,
|
||||||
"icon": "home"
|
"icon": "home"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Documents",
|
"name": I18n.tr("Documents"),
|
||||||
"path": docsDir,
|
"path": docsDir,
|
||||||
"icon": "description"
|
"icon": "description"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Downloads",
|
"name": I18n.tr("Downloads"),
|
||||||
"path": downloadDir,
|
"path": downloadDir,
|
||||||
"icon": "download"
|
"icon": "download"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Pictures",
|
"name": I18n.tr("Pictures"),
|
||||||
"path": picsDir,
|
"path": picsDir,
|
||||||
"icon": "image"
|
"icon": "image"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Music",
|
"name": I18n.tr("Music"),
|
||||||
"path": musicDir,
|
"path": musicDir,
|
||||||
"icon": "music_note"
|
"icon": "music_note"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Videos",
|
"name": I18n.tr("Videos"),
|
||||||
"path": videosDir,
|
"path": videosDir,
|
||||||
"icon": "movie"
|
"icon": "movie"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Desktop",
|
"name": I18n.tr("Desktop"),
|
||||||
"path": desktopDir,
|
"path": desktopDir,
|
||||||
"icon": "computer"
|
"icon": "computer"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ StyledRect {
|
|||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Quick Access"
|
text: I18n.tr("Quick Access")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -91,7 +91,12 @@ DankModal {
|
|||||||
id: searchField
|
id: searchField
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
leftIconName: "search"
|
leftIconName: "search"
|
||||||
|
keyForwardTargets: [root.modalFocusScope]
|
||||||
onTextEdited: searchDebounce.restart()
|
onTextEdited: searchDebounce.restart()
|
||||||
|
Keys.onEscapePressed: event => {
|
||||||
|
root.close();
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +123,7 @@ DankModal {
|
|||||||
|
|
||||||
function generateCategories(query) {
|
function generateCategories(query) {
|
||||||
const lowerQuery = query ? query.toLowerCase().trim() : "";
|
const lowerQuery = query ? query.toLowerCase().trim() : "";
|
||||||
|
const lowerQueryWords = query.split(/\s+/);
|
||||||
const processed = {};
|
const processed = {};
|
||||||
|
|
||||||
for (const cat in rawBinds) {
|
for (const cat in rawBinds) {
|
||||||
@@ -130,10 +136,25 @@ DankModal {
|
|||||||
const keyLower = bind.key.toLowerCase();
|
const keyLower = bind.key.toLowerCase();
|
||||||
const descLower = bind.desc.toLowerCase();
|
const descLower = bind.desc.toLowerCase();
|
||||||
const actionLower = bind.action.toLowerCase();
|
const actionLower = bind.action.toLowerCase();
|
||||||
if (!(lowerQuery.length === 0 || keyLower.includes(lowerQuery) || descLower.includes(lowerQuery) || catLower.includes(lowerQuery) || actionLower.includes(lowerQuery)))
|
|
||||||
continue;
|
|
||||||
if (bind.hideOnOverlay)
|
if (bind.hideOnOverlay)
|
||||||
continue;
|
continue;
|
||||||
|
let shouldContinue = false;
|
||||||
|
for (let j = 0; j < lowerQueryWords.length; j++) {
|
||||||
|
const word = lowerQueryWords[j];
|
||||||
|
if (!(
|
||||||
|
word.length === 0 ||
|
||||||
|
keyLower.includes(word) ||
|
||||||
|
descLower.includes(word) ||
|
||||||
|
catLower.includes(word) ||
|
||||||
|
actionLower.includes(word)
|
||||||
|
)) {
|
||||||
|
shouldContinue = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shouldContinue)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (bind.subcat) {
|
if (bind.subcat) {
|
||||||
hasSubcats = true;
|
hasSubcats = true;
|
||||||
|
|||||||
@@ -83,9 +83,9 @@ FloatingWindow {
|
|||||||
|
|
||||||
objectName: "processListModal"
|
objectName: "processListModal"
|
||||||
title: I18n.tr("System Monitor", "sysmon window title")
|
title: I18n.tr("System Monitor", "sysmon window title")
|
||||||
minimumSize: Qt.size(750, 550)
|
minimumSize: Qt.size(Math.min(Math.round(Theme.fontSizeMedium * 48), Screen.width), Math.min(Math.round(Theme.fontSizeMedium * 34), Screen.height))
|
||||||
implicitWidth: 1000
|
implicitWidth: Math.round(Theme.fontSizeMedium * 71)
|
||||||
implicitHeight: 720
|
implicitHeight: Math.round(Theme.fontSizeMedium * 51)
|
||||||
color: Theme.surfaceContainer
|
color: Theme.surfaceContainer
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ FloatingWindow {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 48
|
Layout.preferredHeight: Math.round(Theme.fontSizeMedium * 3.4)
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -293,10 +293,10 @@ FloatingWindow {
|
|||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 52
|
Layout.preferredHeight: Math.round(Theme.fontSizeMedium * 3.7)
|
||||||
Layout.leftMargin: Theme.spacingL
|
Layout.leftMargin: Theme.spacingL
|
||||||
Layout.rightMargin: Theme.spacingL
|
Layout.rightMargin: Theme.spacingL
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: 2
|
spacing: 2
|
||||||
@@ -322,14 +322,15 @@ FloatingWindow {
|
|||||||
]
|
]
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 120
|
width: tabRowContent.implicitWidth + Theme.spacingM * 2
|
||||||
height: 44
|
height: Math.round(Theme.fontSizeMedium * 3.1)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
|
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
|
||||||
border.color: currentTab === index ? Theme.primary : "transparent"
|
border.color: currentTab === index ? Theme.primary : "transparent"
|
||||||
border.width: currentTab === index ? 1 : 0
|
border.width: currentTab === index ? 1 : 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
id: tabRowContent
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
@@ -373,11 +374,13 @@ FloatingWindow {
|
|||||||
|
|
||||||
DankButtonGroup {
|
DankButtonGroup {
|
||||||
id: processFilterGroup
|
id: processFilterGroup
|
||||||
Layout.minimumWidth: implicitWidth + 8
|
|
||||||
model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")]
|
model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")]
|
||||||
currentIndex: 0
|
currentIndex: 0
|
||||||
checkEnabled: false
|
checkEnabled: false
|
||||||
buttonHeight: 36
|
buttonHeight: Math.round(Theme.fontSizeSmall * 2.6)
|
||||||
|
minButtonWidth: 0
|
||||||
|
buttonPadding: Theme.spacingS
|
||||||
|
textSize: Theme.fontSizeSmall
|
||||||
visible: currentTab === 0
|
visible: currentTab === 0
|
||||||
onSelectionChanged: (index, selected) => {
|
onSelectionChanged: (index, selected) => {
|
||||||
if (!selected)
|
if (!selected)
|
||||||
@@ -400,9 +403,9 @@ FloatingWindow {
|
|||||||
DankTextField {
|
DankTextField {
|
||||||
id: searchField
|
id: searchField
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: 250
|
Layout.maximumWidth: Math.round(Theme.fontSizeMedium * 18)
|
||||||
Layout.minimumWidth: 120
|
Layout.minimumWidth: Theme.fontSizeMedium * 4
|
||||||
Layout.preferredHeight: 40
|
Layout.preferredHeight: Math.round(Theme.fontSizeMedium * 2.8)
|
||||||
placeholderText: I18n.tr("Search processes...", "process search placeholder")
|
placeholderText: I18n.tr("Search processes...", "process search placeholder")
|
||||||
leftIconName: "search"
|
leftIconName: "search"
|
||||||
showClearButton: true
|
showClearButton: true
|
||||||
@@ -470,7 +473,7 @@ FloatingWindow {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 32
|
Layout.preferredHeight: Math.round(Theme.fontSizeSmall * 2.7)
|
||||||
Layout.leftMargin: Theme.spacingL
|
Layout.leftMargin: Theme.spacingL
|
||||||
Layout.rightMargin: Theme.spacingL
|
Layout.rightMargin: Theme.spacingL
|
||||||
Layout.bottomMargin: Theme.spacingM
|
Layout.bottomMargin: Theme.spacingM
|
||||||
|
|||||||
@@ -8,10 +8,39 @@ DankPopout {
|
|||||||
|
|
||||||
layerNamespace: "dms:app-launcher"
|
layerNamespace: "dms:app-launcher"
|
||||||
|
|
||||||
|
property string _pendingMode: ""
|
||||||
|
property string _pendingQuery: ""
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openWithMode(mode) {
|
||||||
|
_pendingMode = mode || "";
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleWithMode(mode) {
|
||||||
|
if (shouldBeVisible) {
|
||||||
|
close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openWithMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openWithQuery(query) {
|
||||||
|
_pendingQuery = query || "";
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleWithQuery(query) {
|
||||||
|
if (shouldBeVisible) {
|
||||||
|
close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openWithQuery(query);
|
||||||
|
}
|
||||||
|
|
||||||
popupWidth: 560
|
popupWidth: 560
|
||||||
popupHeight: 640
|
popupHeight: 640
|
||||||
triggerWidth: 40
|
triggerWidth: 40
|
||||||
@@ -30,15 +59,25 @@ DankPopout {
|
|||||||
var lc = contentLoader.item?.launcherContent;
|
var lc = contentLoader.item?.launcherContent;
|
||||||
if (!lc)
|
if (!lc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const query = _pendingQuery;
|
||||||
|
const mode = _pendingMode || "apps";
|
||||||
|
_pendingMode = "";
|
||||||
|
_pendingQuery = "";
|
||||||
|
|
||||||
if (lc.searchField) {
|
if (lc.searchField) {
|
||||||
lc.searchField.text = "";
|
lc.searchField.text = query;
|
||||||
lc.searchField.forceActiveFocus();
|
lc.searchField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
if (lc.controller) {
|
if (lc.controller) {
|
||||||
lc.controller.searchMode = "apps";
|
lc.controller.searchMode = mode;
|
||||||
lc.controller.pluginFilter = "";
|
lc.controller.pluginFilter = "";
|
||||||
lc.controller.searchQuery = "";
|
lc.controller.searchQuery = "";
|
||||||
lc.controller.performSearch();
|
if (query) {
|
||||||
|
lc.controller.setSearchQuery(query);
|
||||||
|
} else {
|
||||||
|
lc.controller.performSearch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lc.resetScroll?.();
|
lc.resetScroll?.();
|
||||||
lc.actionPanel?.hide();
|
lc.actionPanel?.hide();
|
||||||
|
|||||||
@@ -15,18 +15,22 @@ Item {
|
|||||||
property var pluginDetailInstance: null
|
property var pluginDetailInstance: null
|
||||||
property var widgetModel: null
|
property var widgetModel: null
|
||||||
property var collapseCallback: null
|
property var collapseCallback: null
|
||||||
|
property real maxAvailableHeight: 9999
|
||||||
|
|
||||||
function getDetailHeight(section) {
|
function getDetailHeight(section) {
|
||||||
const maxAvailable = parent ? parent.height - Theme.spacingS : 9999;
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case section === "wifi":
|
case section === "wifi":
|
||||||
case section === "bluetooth":
|
case section === "bluetooth":
|
||||||
case section === "builtin_vpn":
|
case section === "builtin_vpn":
|
||||||
return Math.min(350, maxAvailable);
|
return Math.min(350, maxAvailableHeight);
|
||||||
case section.startsWith("brightnessSlider_"):
|
case section.startsWith("brightnessSlider_"):
|
||||||
return Math.min(400, maxAvailable);
|
return Math.min(400, maxAvailableHeight);
|
||||||
|
case section.startsWith("plugin_"):
|
||||||
|
if (pluginDetailInstance?.ccDetailHeight)
|
||||||
|
return Math.min(pluginDetailInstance.ccDetailHeight, maxAvailableHeight);
|
||||||
|
return Math.min(250, maxAvailableHeight);
|
||||||
default:
|
default:
|
||||||
return Math.min(250, maxAvailable);
|
return Math.min(250, maxAvailableHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,26 @@ Column {
|
|||||||
|
|
||||||
spacing: editMode ? Theme.spacingL : Theme.spacingS
|
spacing: editMode ? Theme.spacingL : Theme.spacingS
|
||||||
|
|
||||||
|
property real maxPopoutHeight: 9999
|
||||||
property var currentRowWidgets: []
|
property var currentRowWidgets: []
|
||||||
property real currentRowWidth: 0
|
property real currentRowWidth: 0
|
||||||
property int expandedRowIndex: -1
|
property int expandedRowIndex: -1
|
||||||
property var colorPickerModal: null
|
property var colorPickerModal: null
|
||||||
|
|
||||||
|
readonly property real _maxDetailHeight: {
|
||||||
|
const rows = layoutResult.rows;
|
||||||
|
let totalRowHeight = 0;
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
const sliderOnly = rows[i].every(w => {
|
||||||
|
const id = w.id || "";
|
||||||
|
return id === "volumeSlider" || id === "brightnessSlider" || id === "inputVolumeSlider";
|
||||||
|
});
|
||||||
|
totalRowHeight += sliderOnly ? 36 : 60;
|
||||||
|
}
|
||||||
|
const rowSpacing = Math.max(0, rows.length - 1) * spacing;
|
||||||
|
return Math.max(100, maxPopoutHeight - totalRowHeight - rowSpacing);
|
||||||
|
}
|
||||||
|
|
||||||
function calculateRowsAndWidgets() {
|
function calculateRowsAndWidgets() {
|
||||||
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex);
|
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex);
|
||||||
}
|
}
|
||||||
@@ -163,6 +178,7 @@ Column {
|
|||||||
DetailHost {
|
DetailHost {
|
||||||
id: detailHost
|
id: detailHost
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
maxAvailableHeight: root._maxDetailHeight
|
||||||
height: active ? (getDetailHeight(root.expandedSection) + Theme.spacingS) : 0
|
height: active ? (getDetailHeight(root.expandedSection) + Theme.spacingS) : 0
|
||||||
property bool active: {
|
property bool active: {
|
||||||
if (root.expandedSection === "")
|
if (root.expandedSection === "")
|
||||||
@@ -945,22 +961,31 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
function tryCreatePluginInstance() {
|
||||||
Qt.callLater(() => {
|
const pluginComponent = PluginService.pluginWidgetComponents[pluginId];
|
||||||
const pluginComponent = PluginService.pluginWidgetComponents[pluginId];
|
if (!pluginComponent)
|
||||||
if (pluginComponent) {
|
return false;
|
||||||
const instance = pluginComponent.createObject(null, {
|
try {
|
||||||
"pluginId": pluginId,
|
const instance = pluginComponent.createObject(null, {
|
||||||
"pluginService": PluginService,
|
"pluginId": pluginId,
|
||||||
"visible": false,
|
"pluginService": PluginService,
|
||||||
"width": 0,
|
"visible": false,
|
||||||
"height": 0
|
"width": 0,
|
||||||
});
|
"height": 0
|
||||||
if (instance) {
|
});
|
||||||
pluginInstance = instance;
|
if (instance) {
|
||||||
}
|
pluginInstance = instance;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
});
|
} catch (e) {
|
||||||
|
console.warn("DragDropGrid: stale plugin component for", pluginId, "- reloading");
|
||||||
|
PluginService.reloadPlugin(pluginId);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
Qt.callLater(() => tryCreatePluginInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -970,6 +995,11 @@ Column {
|
|||||||
pluginInstance.loadPluginData();
|
pluginInstance.loadPluginData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function onPluginLoaded(loadedPluginId) {
|
||||||
|
if (loadedPluginId !== pluginId || pluginInstance)
|
||||||
|
return;
|
||||||
|
Qt.callLater(() => tryCreatePluginInstance());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ Row {
|
|||||||
property Item popoutContent: null
|
property Item popoutContent: null
|
||||||
|
|
||||||
signal addWidget(string widgetId)
|
signal addWidget(string widgetId)
|
||||||
signal resetToDefault()
|
signal resetToDefault
|
||||||
signal clearAll()
|
signal clearAll
|
||||||
|
|
||||||
height: 48
|
height: 48
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
@@ -28,7 +28,7 @@ Row {
|
|||||||
y: parent ? Math.round((parent.height - height) / 2) : 0
|
y: parent ? Math.round((parent.height - height) / 2) : 0
|
||||||
width: 400
|
width: 400
|
||||||
height: 300
|
height: 300
|
||||||
modal: true
|
modal: false
|
||||||
focus: true
|
focus: true
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ Row {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.addWidget(modelData.id)
|
root.addWidget(modelData.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ DankPopout {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
layerNamespace: "dms:control-center"
|
layerNamespace: "dms:control-center"
|
||||||
|
fullHeightSurface: true
|
||||||
|
|
||||||
property string expandedSection: ""
|
property string expandedSection: ""
|
||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
@@ -115,6 +116,7 @@ DankPopout {
|
|||||||
property alias bluetoothCodecSelector: bluetoothCodecSelector
|
property alias bluetoothCodecSelector: bluetoothCodecSelector
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
clip: true
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -165,6 +167,10 @@ DankPopout {
|
|||||||
id: widgetGrid
|
id: widgetGrid
|
||||||
width: parent.width
|
width: parent.width
|
||||||
editMode: root.editMode
|
editMode: root.editMode
|
||||||
|
maxPopoutHeight: {
|
||||||
|
const screenHeight = (root.triggerScreen?.height ?? 1080);
|
||||||
|
return screenHeight - 100 - Theme.spacingL - headerPane.height - Theme.spacingS;
|
||||||
|
}
|
||||||
expandedSection: root.expandedSection
|
expandedSection: root.expandedSection
|
||||||
expandedWidgetIndex: root.expandedWidgetIndex
|
expandedWidgetIndex: root.expandedWidgetIndex
|
||||||
expandedWidgetData: root.expandedWidgetData
|
expandedWidgetData: root.expandedWidgetData
|
||||||
|
|||||||
@@ -40,6 +40,24 @@ Rectangle {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: 1
|
||||||
|
width: parent.width - headerText.width - settingsButton.width
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: settingsButton
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
iconName: "settings"
|
||||||
|
buttonSize: 28
|
||||||
|
iconSize: 16
|
||||||
|
iconColor: Theme.surfaceVariantText
|
||||||
|
onClicked: {
|
||||||
|
PopoutService.closeControlCenter();
|
||||||
|
PopoutService.openSettingsWithTab("audio");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -151,8 +169,11 @@ Rectangle {
|
|||||||
Repeater {
|
Repeater {
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: {
|
values: {
|
||||||
|
const hidden = SessionData.hiddenInputDeviceNames ?? [];
|
||||||
const nodes = Pipewire.nodes.values.filter(node => {
|
const nodes = Pipewire.nodes.values.filter(node => {
|
||||||
return node.audio && !node.isSink && !node.isStream;
|
if (!node.audio || node.isSink || node.isStream)
|
||||||
|
return false;
|
||||||
|
return !hidden.includes(node.name);
|
||||||
});
|
});
|
||||||
const pinnedList = audioContent.getPinnedInputs();
|
const pinnedList = audioContent.getPinnedInputs();
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,24 @@ Rectangle {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: 1
|
||||||
|
width: parent.width - headerText.width - settingsButton.width
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: settingsButton
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
iconName: "settings"
|
||||||
|
buttonSize: 28
|
||||||
|
iconSize: 16
|
||||||
|
iconColor: Theme.surfaceVariantText
|
||||||
|
onClicked: {
|
||||||
|
PopoutService.closeControlCenter();
|
||||||
|
PopoutService.openSettingsWithTab("audio");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -161,8 +179,11 @@ Rectangle {
|
|||||||
Repeater {
|
Repeater {
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: {
|
values: {
|
||||||
|
const hidden = SessionData.hiddenOutputDeviceNames ?? [];
|
||||||
const nodes = Pipewire.nodes.values.filter(node => {
|
const nodes = Pipewire.nodes.values.filter(node => {
|
||||||
return node.audio && node.isSink && !node.isStream;
|
if (!node.audio || !node.isSink || node.isStream)
|
||||||
|
return false;
|
||||||
|
return !hidden.includes(node.name);
|
||||||
});
|
});
|
||||||
const pinnedList = audioContent.getPinnedOutputs();
|
const pinnedList = audioContent.getPinnedOutputs();
|
||||||
|
|
||||||
|
|||||||
@@ -216,14 +216,18 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pluginComponent = PluginService.pluginWidgetComponents[plugin.id];
|
const pluginComponent = PluginService.pluginWidgetComponents[plugin.id];
|
||||||
if (!pluginComponent || typeof pluginComponent.createObject !== 'function') {
|
if (!pluginComponent)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
const tempInstance = pluginComponent.createObject(null);
|
let tempInstance;
|
||||||
if (!tempInstance) {
|
try {
|
||||||
|
tempInstance = pluginComponent.createObject(null);
|
||||||
|
} catch (e) {
|
||||||
|
PluginService.reloadPlugin(plugin.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!tempInstance)
|
||||||
|
continue;
|
||||||
|
|
||||||
const hasCCWidget = tempInstance.ccWidgetIcon && tempInstance.ccWidgetIcon.length > 0;
|
const hasCCWidget = tempInstance.ccWidgetIcon && tempInstance.ccWidgetIcon.length > 0;
|
||||||
tempInstance.destroy();
|
tempInstance.destroy();
|
||||||
|
|||||||
@@ -642,24 +642,52 @@ Item {
|
|||||||
popoutTarget: appDrawerLoader.item
|
popoutTarget: appDrawerLoader.item
|
||||||
parentScreen: barWindow.screen
|
parentScreen: barWindow.screen
|
||||||
hyprlandOverviewLoader: barWindow ? barWindow.hyprlandOverviewLoader : null
|
hyprlandOverviewLoader: barWindow ? barWindow.hyprlandOverviewLoader : null
|
||||||
onClicked: {
|
|
||||||
|
function _preparePopout() {
|
||||||
appDrawerLoader.active = true;
|
appDrawerLoader.active = true;
|
||||||
// Use topBarContent.barConfig directly since widget barConfig binding doesn't work in Components
|
if (!appDrawerLoader.item)
|
||||||
|
return false;
|
||||||
const effectiveBarConfig = topBarContent.barConfig;
|
const effectiveBarConfig = topBarContent.barConfig;
|
||||||
// Calculate barPosition from axis.edge
|
|
||||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||||
if (appDrawerLoader.item && appDrawerLoader.item.setBarContext) {
|
if (appDrawerLoader.item.setBarContext)
|
||||||
appDrawerLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
appDrawerLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||||
}
|
if (appDrawerLoader.item.setTriggerPosition) {
|
||||||
if (appDrawerLoader.item && appDrawerLoader.item.setTriggerPosition) {
|
|
||||||
const globalPos = launcherButton.visualContent.mapToItem(null, 0, 0);
|
const globalPos = launcherButton.visualContent.mapToItem(null, 0, 0);
|
||||||
const currentScreen = barWindow.screen;
|
const currentScreen = barWindow.screen;
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barWindow.effectiveBarThickness, launcherButton.visualWidth, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barWindow.effectiveBarThickness, launcherButton.visualWidth, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||||
appDrawerLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, launcherButton.section, currentScreen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
appDrawerLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, launcherButton.section, currentScreen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||||
}
|
}
|
||||||
if (appDrawerLoader.item) {
|
return true;
|
||||||
PopoutManager.requestPopout(appDrawerLoader.item, undefined, "appDrawer");
|
}
|
||||||
}
|
|
||||||
|
function openWithMode(mode) {
|
||||||
|
if (!_preparePopout())
|
||||||
|
return;
|
||||||
|
appDrawerLoader.item.openWithMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleWithMode(mode) {
|
||||||
|
if (!_preparePopout())
|
||||||
|
return;
|
||||||
|
appDrawerLoader.item.toggleWithMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openWithQuery(query) {
|
||||||
|
if (!_preparePopout())
|
||||||
|
return;
|
||||||
|
appDrawerLoader.item.openWithQuery(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleWithQuery(query) {
|
||||||
|
if (!_preparePopout())
|
||||||
|
return;
|
||||||
|
appDrawerLoader.item.toggleWithQuery(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (!_preparePopout())
|
||||||
|
return;
|
||||||
|
PopoutManager.requestPopout(appDrawerLoader.item, undefined, "appDrawer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -783,7 +811,7 @@ Item {
|
|||||||
} else {
|
} else {
|
||||||
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
||||||
}
|
}
|
||||||
PopoutManager.requestPopout(dankDashPopoutLoader.item, 0, (effectiveBarConfig?.id ?? "default") + "-0");
|
PopoutManager.requestPopout(dankDashPopoutLoader.item, 0, (effectiveBarConfig?.id ?? "default") + "-" + section + "-0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -839,7 +867,7 @@ Item {
|
|||||||
} else {
|
} else {
|
||||||
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
||||||
}
|
}
|
||||||
PopoutManager.requestPopout(dankDashPopoutLoader.item, 1, (effectiveBarConfig?.id ?? "default") + "-1");
|
PopoutManager.requestPopout(dankDashPopoutLoader.item, 1, (effectiveBarConfig?.id ?? "default") + "-" + section + "-1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -898,7 +926,7 @@ Item {
|
|||||||
} else {
|
} else {
|
||||||
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
||||||
}
|
}
|
||||||
PopoutManager.requestPopout(dankDashPopoutLoader.item, 3, (effectiveBarConfig?.id ?? "default") + "-3");
|
PopoutManager.requestPopout(dankDashPopoutLoader.item, 3, (effectiveBarConfig?.id ?? "default") + "-" + section + "-3");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ PanelWindow {
|
|||||||
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
PopoutManager.requestPopout(dankDashPopoutLoader.item, 2, (barConfig?.id ?? "default") + "-2");
|
PopoutManager.requestPopout(dankDashPopoutLoader.item, 2, (barConfig?.id ?? "default") + "-" + section + "-2");
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property var dBarLayer: {
|
readonly property var dBarLayer: {
|
||||||
|
|||||||
@@ -99,13 +99,6 @@ BasePill {
|
|||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
StyledTextMetrics {
|
||||||
id: cpuBaseline
|
id: cpuBaseline
|
||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||||
|
|||||||
@@ -99,13 +99,6 @@ BasePill {
|
|||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
StyledTextMetrics {
|
||||||
id: tempBaseline
|
id: tempBaseline
|
||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||||
|
|||||||
@@ -193,13 +193,6 @@ BasePill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
width: Math.max(diskBaseline.width, paintedWidth)
|
width: Math.max(diskBaseline.width, paintedWidth)
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 120
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,13 +167,6 @@ BasePill {
|
|||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
StyledTextMetrics {
|
||||||
id: gpuTempBaseline
|
id: gpuTempBaseline
|
||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||||
|
|||||||
@@ -110,13 +110,6 @@ BasePill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
width: Math.max(rxBaseline.width, paintedWidth)
|
width: Math.max(rxBaseline.width, paintedWidth)
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 120
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,13 +139,6 @@ BasePill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
width: Math.max(txBaseline.width, paintedWidth)
|
width: Math.max(txBaseline.width, paintedWidth)
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 120
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,13 +109,6 @@ BasePill {
|
|||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
StyledTextMetrics {
|
||||||
id: ramBaseline
|
id: ramBaseline
|
||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||||
|
|||||||
@@ -138,15 +138,13 @@ BasePill {
|
|||||||
readonly property real iconCellSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) + 6
|
readonly property real iconCellSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) + 6
|
||||||
|
|
||||||
readonly property string focusedAppId: {
|
readonly property string focusedAppId: {
|
||||||
const toplevels = CompositorService.sortedToplevels;
|
if (!sortedToplevels || sortedToplevels.length === 0)
|
||||||
if (!toplevels)
|
|
||||||
return "";
|
return "";
|
||||||
let result = "";
|
for (let i = 0; i < sortedToplevels.length; i++) {
|
||||||
for (let i = 0; i < toplevels.length; i++) {
|
if (sortedToplevels[i].activated)
|
||||||
if (toplevels[i].activated)
|
return sortedToplevels[i].appId || "";
|
||||||
result = toplevels[i].appId || "";
|
|
||||||
}
|
}
|
||||||
return result;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: windowCount > 0
|
visible: windowCount > 0
|
||||||
@@ -155,6 +153,7 @@ BasePill {
|
|||||||
property real touchpadThreshold: 500
|
property real touchpadThreshold: 500
|
||||||
|
|
||||||
onWheel: function (wheelEvent) {
|
onWheel: function (wheelEvent) {
|
||||||
|
wheelEvent.accepted = true;
|
||||||
const deltaY = wheelEvent.angleDelta.y;
|
const deltaY = wheelEvent.angleDelta.y;
|
||||||
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;
|
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,44 @@ BasePill {
|
|||||||
property bool suppressShiftAnimation: false
|
property bool suppressShiftAnimation: false
|
||||||
readonly property bool hasHiddenItems: allTrayItems.length > mainBarItems.length
|
readonly property bool hasHiddenItems: allTrayItems.length > mainBarItems.length
|
||||||
visible: allTrayItems.length > 0
|
visible: allTrayItems.length > 0
|
||||||
|
opacity: allTrayItems.length > 0 ? 1 : 0
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "hidden_horizontal"
|
||||||
|
when: allTrayItems.length === 0 && !isVerticalOrientation
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
width: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hidden_vertical"
|
||||||
|
when: allTrayItems.length === 0 && isVerticalOrientation
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
height: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "width,height"
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
readonly property real trayItemSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) + 6
|
readonly property real trayItemSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) + 6
|
||||||
|
|
||||||
readonly property real minTooltipY: {
|
readonly property real minTooltipY: {
|
||||||
|
|||||||
@@ -10,6 +10,45 @@ BasePill {
|
|||||||
property bool isActive: false
|
property bool isActive: false
|
||||||
readonly property bool hasUpdates: SystemUpdateService.updateCount > 0
|
readonly property bool hasUpdates: SystemUpdateService.updateCount > 0
|
||||||
readonly property bool isChecking: SystemUpdateService.isChecking
|
readonly property bool isChecking: SystemUpdateService.isChecking
|
||||||
|
readonly property bool shouldHide: SettingsData.updaterHideWidget && !hasUpdates && !isChecking && !SystemUpdateService.hasError
|
||||||
|
|
||||||
|
opacity: shouldHide ? 0 : 1
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "hidden_horizontal"
|
||||||
|
when: root.shouldHide && !isVerticalOrientation
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
width: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hidden_vertical"
|
||||||
|
when: root.shouldHide && isVerticalOrientation
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
height: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "width,height"
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: SystemUpdateService
|
service: SystemUpdateService
|
||||||
|
|||||||
@@ -924,8 +924,13 @@ Item {
|
|||||||
return loadedIsUrgent;
|
return loadedIsUrgent;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
property var loadedIconData: null
|
readonly property var loadedIconData: {
|
||||||
property bool loadedHasIcon: false
|
if (isPlaceholder) return null;
|
||||||
|
const name = modelData?.name;
|
||||||
|
if (!name) return null;
|
||||||
|
return SettingsData.getWorkspaceNameIcon(name);
|
||||||
|
}
|
||||||
|
readonly property bool loadedHasIcon: loadedIconData !== null
|
||||||
property var loadedIcons: []
|
property var loadedIcons: []
|
||||||
|
|
||||||
readonly property int stableIconCount: {
|
readonly property int stableIconCount: {
|
||||||
@@ -986,8 +991,8 @@ Item {
|
|||||||
readonly property real baseHeight: root.isVertical ? (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7) : (SettingsData.showWorkspaceApps ? Math.max(widgetHeight * 0.7, root.appIconSize + Theme.spacingXS * 2) : widgetHeight * 0.5)
|
readonly property real baseHeight: root.isVertical ? (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7) : (SettingsData.showWorkspaceApps ? Math.max(widgetHeight * 0.7, root.appIconSize + Theme.spacingXS * 2) : widgetHeight * 0.5)
|
||||||
readonly property bool hasWorkspaceName: SettingsData.showWorkspaceName && modelData?.name && modelData.name !== ""
|
readonly property bool hasWorkspaceName: SettingsData.showWorkspaceName && modelData?.name && modelData.name !== ""
|
||||||
readonly property bool workspaceNamesEnabled: SettingsData.showWorkspaceName && (CompositorService.isNiri || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle)
|
readonly property bool workspaceNamesEnabled: SettingsData.showWorkspaceName && (CompositorService.isNiri || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle)
|
||||||
readonly property real contentImplicitWidth: (hasWorkspaceName || loadedHasIcon) ? (appIconsLoader.item?.contentWidth ?? 0) : 0
|
readonly property real contentImplicitWidth: hasWorkspaceName ? (appIconsLoader.item?.contentWidth ?? 0) : 0
|
||||||
readonly property real contentImplicitHeight: (workspaceNamesEnabled || loadedHasIcon) ? (appIconsLoader.item?.contentHeight ?? 0) : 0
|
readonly property real contentImplicitHeight: workspaceNamesEnabled ? (appIconsLoader.item?.contentHeight ?? 0) : 0
|
||||||
|
|
||||||
readonly property real iconsExtraWidth: {
|
readonly property real iconsExtraWidth: {
|
||||||
if (!root.isVertical && SettingsData.showWorkspaceApps && stableIconCount > 0) {
|
if (!root.isVertical && SettingsData.showWorkspaceApps && stableIconCount > 0) {
|
||||||
@@ -1222,8 +1227,6 @@ Item {
|
|||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (isPlaceholder) {
|
if (isPlaceholder) {
|
||||||
delegateRoot.loadedWorkspaceData = null;
|
delegateRoot.loadedWorkspaceData = null;
|
||||||
delegateRoot.loadedIconData = null;
|
|
||||||
delegateRoot.loadedHasIcon = false;
|
|
||||||
delegateRoot.loadedIcons = [];
|
delegateRoot.loadedIcons = [];
|
||||||
delegateRoot.loadedIsUrgent = false;
|
delegateRoot.loadedIsUrgent = false;
|
||||||
return;
|
return;
|
||||||
@@ -1249,13 +1252,6 @@ Item {
|
|||||||
delegateRoot.loadedIsUrgent = wsData?.urgent ?? false;
|
delegateRoot.loadedIsUrgent = wsData?.urgent ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var icData = null;
|
|
||||||
if (wsData?.name) {
|
|
||||||
icData = SettingsData.getWorkspaceNameIcon(wsData.name);
|
|
||||||
}
|
|
||||||
delegateRoot.loadedIconData = icData;
|
|
||||||
delegateRoot.loadedHasIcon = icData !== null;
|
|
||||||
|
|
||||||
if (SettingsData.showWorkspaceApps) {
|
if (SettingsData.showWorkspaceApps) {
|
||||||
if (CompositorService.isDwl || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) {
|
if (CompositorService.isDwl || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) {
|
||||||
delegateRoot.loadedIcons = root.getWorkspaceIcons(modelData);
|
delegateRoot.loadedIcons = root.getWorkspaceIcons(modelData);
|
||||||
@@ -1420,7 +1416,7 @@ Item {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: loadedHasIcon && loadedIconData?.type === "icon"
|
visible: loadedHasIcon && loadedIconData?.type === "icon"
|
||||||
width: wsIcon.width + (isActive && loadedIcons.length > 0 ? 4 : 0)
|
width: wsIcon.width
|
||||||
height: root.appIconSize
|
height: root.appIconSize
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
@@ -1435,7 +1431,7 @@ Item {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: loadedHasIcon && loadedIconData?.type === "text"
|
visible: loadedHasIcon && loadedIconData?.type === "text"
|
||||||
width: wsText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0)
|
width: wsText.implicitWidth
|
||||||
height: root.appIconSize
|
height: root.appIconSize
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -1449,14 +1445,14 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: (SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon
|
visible: ((SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon) || (loadedHasIcon && SettingsData.showWorkspaceName && hasWorkspaceName)
|
||||||
width: wsIndexText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0)
|
width: wsIndexText.implicitWidth
|
||||||
height: root.appIconSize
|
height: root.appIconSize
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: wsIndexText
|
id: wsIndexText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: root.getWorkspaceIndex(modelData, index)
|
text: loadedHasIcon ? (modelData?.name ?? "") : root.getWorkspaceIndex(modelData, index)
|
||||||
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
|
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
|
||||||
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
|
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
|
||||||
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
|
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
|
||||||
@@ -1574,9 +1570,9 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
visible: (SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon
|
visible: ((SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon) || (loadedHasIcon && SettingsData.showWorkspaceName && hasWorkspaceName)
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: root.getWorkspaceIndex(modelData, index)
|
text: loadedHasIcon ? (root.isVertical ? (modelData?.name ?? "").charAt(0) : (modelData?.name ?? "")) : root.getWorkspaceIndex(modelData, index)
|
||||||
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
|
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
|
||||||
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
|
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
|
||||||
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
|
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
|
||||||
@@ -1670,55 +1666,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loader for Custom Name Icon
|
|
||||||
Loader {
|
|
||||||
id: customIconLoader
|
|
||||||
anchors.fill: parent
|
|
||||||
active: !isPlaceholder && loadedHasIcon && loadedIconData.type === "icon" && !SettingsData.showWorkspaceApps
|
|
||||||
sourceComponent: Item {
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: loadedIconData ? loadedIconData.value : "" // NULL CHECK
|
|
||||||
size: Theme.fontSizeSmall
|
|
||||||
color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : Theme.surfaceTextMedium
|
|
||||||
weight: isActive && !isPlaceholder ? 500 : 400
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loader for Custom Name Text
|
|
||||||
Loader {
|
|
||||||
id: customTextLoader
|
|
||||||
anchors.fill: parent
|
|
||||||
active: !isPlaceholder && loadedHasIcon && loadedIconData.type === "text" && !SettingsData.showWorkspaceApps
|
|
||||||
sourceComponent: Item {
|
|
||||||
StyledText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: loadedIconData ? loadedIconData.value : "" // NULL CHECK
|
|
||||||
color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : Theme.surfaceTextMedium
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
|
|
||||||
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loader for Workspace Index
|
|
||||||
Loader {
|
|
||||||
id: indexLoader
|
|
||||||
anchors.fill: parent
|
|
||||||
active: (SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon && !SettingsData.showWorkspaceApps
|
|
||||||
sourceComponent: Item {
|
|
||||||
StyledText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: {
|
|
||||||
return root.getWorkspaceIndex(modelData, index);
|
|
||||||
}
|
|
||||||
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
|
|
||||||
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: updateAllData()
|
Component.onCompleted: updateAllData()
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ Card {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: UserInfoService.username || "brandon"
|
text: UserInfoService.username || I18n.tr("brandon")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -64,18 +64,18 @@ Card {
|
|||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (CompositorService.isNiri)
|
if (CompositorService.isNiri)
|
||||||
return "on niri";
|
return I18n.tr("on Niri");
|
||||||
if (CompositorService.isHyprland)
|
if (CompositorService.isHyprland)
|
||||||
return "on Hyprland";
|
return I18n.tr("on Hyprland");
|
||||||
// technically they might not be on mangowc, but its what we support in the docs
|
// technically they might not be on mangowc, but its what we support in the docs
|
||||||
if (CompositorService.isDwl)
|
if (CompositorService.isDwl)
|
||||||
return "on MangoWC";
|
return I18n.tr("on MangoWC");
|
||||||
if (CompositorService.isSway)
|
if (CompositorService.isSway)
|
||||||
return "on Sway";
|
return I18n.tr("on Sway");
|
||||||
if (CompositorService.isScroll)
|
if (CompositorService.isScroll)
|
||||||
return "on Scroll";
|
return I18n.tr("on Scroll");
|
||||||
if (CompositorService.isMiracle)
|
if (CompositorService.isMiracle)
|
||||||
return "on Miracle WM";
|
return I18n.tr("on Miracle WM");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -99,7 +99,7 @@ Card {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: DgopService.shortUptime || "up"
|
text: DgopService.shortUptime || I18n.tr("up")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|||||||
@@ -553,15 +553,7 @@ Variants {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, backgroundTransparency)
|
color: Theme.withAlpha(Theme.surfaceContainer, backgroundTransparency)
|
||||||
border.color: Theme.outlineMedium
|
|
||||||
border.width: 1
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: Qt.rgba(Theme.surfaceTint.r, Theme.surfaceTint.g, Theme.surfaceTint.b, 0.04)
|
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
width: Math.min(400, Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2))
|
width: Math.min(400, Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2))
|
||||||
height: Math.max(60, menuColumn.implicitHeight + Theme.spacingS * 2)
|
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
||||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
@@ -388,18 +388,31 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: pinArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: pinArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
StyledText {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: root.appData && root.appData.isPinned ? I18n.tr("Unpin from Dock") : I18n.tr("Pin to Dock")
|
spacing: Theme.spacingXS
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
DankIcon {
|
||||||
font.weight: Font.Normal
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
elide: Text.ElideRight
|
name: root.appData && root.appData.isPinned ? "keep_off" : "push_pin"
|
||||||
wrapMode: Text.NoWrap
|
size: 14
|
||||||
|
color: Theme.surfaceText
|
||||||
|
opacity: 0.7
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.appData && root.appData.isPinned ? I18n.tr("Unpin from Dock") : I18n.tr("Pin to Dock")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankRipple {
|
DankRipple {
|
||||||
@@ -448,18 +461,31 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: nvidiaArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: nvidiaArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
StyledText {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: I18n.tr("Launch on dGPU")
|
spacing: Theme.spacingXS
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
DankIcon {
|
||||||
font.weight: Font.Normal
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
elide: Text.ElideRight
|
name: "memory"
|
||||||
wrapMode: Text.NoWrap
|
size: 14
|
||||||
|
color: Theme.surfaceText
|
||||||
|
opacity: 0.7
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: I18n.tr("Launch on dGPU")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankRipple {
|
DankRipple {
|
||||||
@@ -490,23 +516,31 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: closeArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
color: closeArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
||||||
|
|
||||||
StyledText {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: {
|
spacing: Theme.spacingXS
|
||||||
if (root.appData && root.appData.type === "grouped") {
|
|
||||||
return "Close All Windows";
|
DankIcon {
|
||||||
}
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
return "Close Window";
|
name: "close"
|
||||||
|
size: 14
|
||||||
|
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
|
opacity: 0.7
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.appData && root.appData.type === "grouped" ? I18n.tr("Close All Windows") : I18n.tr("Close Window")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
|
|
||||||
font.weight: Font.Normal
|
|
||||||
elide: Text.ElideRight
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DankRipple {
|
DankRipple {
|
||||||
|
|||||||
@@ -1065,7 +1065,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Caps Lock is on"
|
text: I18n.tr("Caps Lock is on")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.error
|
color: Theme.error
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ Item {
|
|||||||
Behavior on x {
|
Behavior on x {
|
||||||
enabled: !swipeDragHandler.active && delegateRoot.__delegateInitialized
|
enabled: !swipeDragHandler.active && delegateRoot.__delegateInitialized
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.notificationExitDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -286,7 +286,7 @@ Item {
|
|||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
enabled: delegateRoot.__delegateInitialized
|
enabled: delegateRoot.__delegateInitialized
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: delegateRoot.__delegateInitialized ? Theme.shortDuration : 0
|
duration: delegateRoot.__delegateInitialized ? Theme.notificationExitDuration : 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ DankListView {
|
|||||||
target: delegateRoot
|
target: delegateRoot
|
||||||
property: "swipeOffset"
|
property: "swipeOffset"
|
||||||
to: 0
|
to: 0
|
||||||
duration: Theme.shortDuration
|
duration: Theme.notificationExitDuration
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
onStopped: NotificationService.dismissGroup(delegateRoot.modelData?.key || "")
|
onStopped: NotificationService.dismissGroup(delegateRoot.modelData?.key || "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -764,7 +764,7 @@ Rectangle {
|
|||||||
target: expandedDelegateWrapper
|
target: expandedDelegateWrapper
|
||||||
property: "swipeOffset"
|
property: "swipeOffset"
|
||||||
to: expandedDelegateWrapper.swipeOffset > 0 ? expandedDelegateWrapper.width : -expandedDelegateWrapper.width
|
to: expandedDelegateWrapper.swipeOffset > 0 ? expandedDelegateWrapper.width : -expandedDelegateWrapper.width
|
||||||
duration: Theme.shortDuration
|
duration: Theme.notificationExitDuration
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
onStopped: NotificationService.dismissNotification(modelData)
|
onStopped: NotificationService.dismissNotification(modelData)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ DankPopout {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
layerNamespace: "dms:notification-center-popout"
|
layerNamespace: "dms:notification-center-popout"
|
||||||
|
fullHeightSurface: true
|
||||||
|
|
||||||
property bool notificationHistoryVisible: false
|
property bool notificationHistoryVisible: false
|
||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
@@ -34,9 +35,9 @@ DankPopout {
|
|||||||
popupWidth: triggerScreen ? Math.min(500, Math.max(380, triggerScreen.width - 48)) : 400
|
popupWidth: triggerScreen ? Math.min(500, Math.max(380, triggerScreen.width - 48)) : 400
|
||||||
popupHeight: stablePopupHeight
|
popupHeight: stablePopupHeight
|
||||||
positioning: ""
|
positioning: ""
|
||||||
animationScaleCollapsed: 1.0
|
animationScaleCollapsed: 0.94
|
||||||
animationOffset: 0
|
animationOffset: 0
|
||||||
suspendShadowWhileResizing: true
|
suspendShadowWhileResizing: false
|
||||||
|
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
shouldBeVisible: notificationHistoryVisible
|
shouldBeVisible: notificationHistoryVisible
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Rectangle {
|
|||||||
spacing: 2
|
spacing: 2
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "↑/↓: Nav • Space: Expand • Enter: Action/Expand • E: Text"
|
text: I18n.tr("↑/↓: Nav • Space: Expand • Enter: Action/Expand • E: Text")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ PanelWindow {
|
|||||||
property bool _isDestroying: false
|
property bool _isDestroying: false
|
||||||
property bool _finalized: false
|
property bool _finalized: false
|
||||||
property real _lastReportedAlignedHeight: -1
|
property real _lastReportedAlignedHeight: -1
|
||||||
|
property real _storedTopMargin: 0
|
||||||
|
property real _storedBottomMargin: 0
|
||||||
readonly property string clearText: I18n.tr("Dismiss")
|
readonly property string clearText: I18n.tr("Dismiss")
|
||||||
property bool descriptionExpanded: false
|
property bool descriptionExpanded: false
|
||||||
readonly property bool hasExpandableBody: (notificationData?.htmlBody || "").replace(/<[^>]*>/g, "").trim().length > 0
|
readonly property bool hasExpandableBody: (notificationData?.htmlBody || "").replace(/<[^>]*>/g, "").trim().length > 0
|
||||||
@@ -146,6 +148,8 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
_lastReportedAlignedHeight = Theme.px(implicitHeight, dpr);
|
_lastReportedAlignedHeight = Theme.px(implicitHeight, dpr);
|
||||||
|
_storedTopMargin = getTopMargin();
|
||||||
|
_storedBottomMargin = getBottomMargin();
|
||||||
if (SettingsData.notificationPopupPrivacyMode)
|
if (SettingsData.notificationPopupPrivacyMode)
|
||||||
descriptionExpanded = false;
|
descriptionExpanded = false;
|
||||||
if (hasValidData) {
|
if (hasValidData) {
|
||||||
@@ -179,14 +183,30 @@ PanelWindow {
|
|||||||
property bool isBottomCenter: SettingsData.notificationPopupPosition === SettingsData.Position.BottomCenter
|
property bool isBottomCenter: SettingsData.notificationPopupPosition === SettingsData.Position.BottomCenter
|
||||||
property bool isCenterPosition: isTopCenter || isBottomCenter
|
property bool isCenterPosition: isTopCenter || isBottomCenter
|
||||||
|
|
||||||
anchors.top: isTopCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Left
|
anchors.top: true
|
||||||
anchors.bottom: isBottomCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom || SettingsData.notificationPopupPosition === SettingsData.Position.Right
|
anchors.bottom: true
|
||||||
anchors.left: SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
|
anchors.left: SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
|
||||||
anchors.right: SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Right
|
anchors.right: SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Right
|
||||||
|
|
||||||
|
mask: contentInputMask
|
||||||
|
|
||||||
|
Region {
|
||||||
|
id: contentInputMask
|
||||||
|
item: contentMaskRect
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentMaskRect
|
||||||
|
visible: false
|
||||||
|
x: content.x
|
||||||
|
y: content.y
|
||||||
|
width: alignedWidth
|
||||||
|
height: alignedHeight
|
||||||
|
}
|
||||||
|
|
||||||
margins {
|
margins {
|
||||||
top: getTopMargin()
|
top: _storedTopMargin
|
||||||
bottom: getBottomMargin()
|
bottom: _storedBottomMargin
|
||||||
left: getLeftMargin()
|
left: getLeftMargin()
|
||||||
right: getRightMargin()
|
right: getRightMargin()
|
||||||
}
|
}
|
||||||
@@ -263,7 +283,14 @@ PanelWindow {
|
|||||||
id: content
|
id: content
|
||||||
|
|
||||||
x: Theme.snap((win.width - alignedWidth) / 2, dpr)
|
x: Theme.snap((win.width - alignedWidth) / 2, dpr)
|
||||||
y: Theme.snap((win.height - alignedHeight) / 2, dpr)
|
y: {
|
||||||
|
const isTop = isTopCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Left;
|
||||||
|
if (isTop) {
|
||||||
|
return Theme.snap(screenY, dpr);
|
||||||
|
} else {
|
||||||
|
return Theme.snap(win.height - alignedHeight - screenY, dpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
width: alignedWidth
|
width: alignedWidth
|
||||||
height: alignedHeight
|
height: alignedHeight
|
||||||
visible: !win._finalized
|
visible: !win._finalized
|
||||||
@@ -311,7 +338,7 @@ PanelWindow {
|
|||||||
id: bgShadowLayer
|
id: bgShadowLayer
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.snap(4, win.dpr)
|
anchors.margins: Theme.snap(4, win.dpr)
|
||||||
layer.enabled: !win._isDestroying && win.screenValid && !implicitHeightAnim.running
|
layer.enabled: !win._isDestroying && win.screenValid
|
||||||
layer.smooth: false
|
layer.smooth: false
|
||||||
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
|
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
|
||||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
||||||
@@ -814,7 +841,7 @@ PanelWindow {
|
|||||||
Behavior on swipeOffset {
|
Behavior on swipeOffset {
|
||||||
enabled: !content.swipeActive && !content.swipeDismissing
|
enabled: !content.swipeActive && !content.swipeDismissing
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.notificationExitDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -824,7 +851,7 @@ PanelWindow {
|
|||||||
target: content
|
target: content
|
||||||
property: "swipeOffset"
|
property: "swipeOffset"
|
||||||
to: isTopCenter ? -content.height : isBottomCenter ? content.height : (SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom ? -content.width : content.width)
|
to: isTopCenter ? -content.height : isBottomCenter ? content.height : (SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom ? -content.width : content.width)
|
||||||
duration: Theme.shortDuration
|
duration: Theme.notificationExitDuration
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
onStopped: {
|
onStopped: {
|
||||||
NotificationService.dismissNotification(notificationData);
|
NotificationService.dismissNotification(notificationData);
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ DankOSD {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property bool useVertical: isVerticalLayout
|
readonly property bool useVertical: isVerticalLayout
|
||||||
property int targetBrightness: {
|
property int _displayBrightness: 0
|
||||||
DisplayService.brightnessVersion;
|
|
||||||
return DisplayService.brightnessLevel;
|
function _syncBrightness() {
|
||||||
|
_displayBrightness = DisplayService.brightnessLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
|
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
|
||||||
@@ -20,9 +21,9 @@ DankOSD {
|
|||||||
Connections {
|
Connections {
|
||||||
target: DisplayService
|
target: DisplayService
|
||||||
function onBrightnessChanged(showOsd) {
|
function onBrightnessChanged(showOsd) {
|
||||||
if (showOsd && SettingsData.osdBrightnessEnabled) {
|
root._syncBrightness();
|
||||||
|
if (showOsd && SettingsData.osdBrightnessEnabled)
|
||||||
root.show();
|
root.show();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,13 +54,11 @@ DankOSD {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: {
|
name: {
|
||||||
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
||||||
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc") {
|
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc")
|
||||||
return "brightness_medium";
|
return "brightness_medium";
|
||||||
} else if (deviceInfo.name.includes("kbd")) {
|
if (deviceInfo.name.includes("kbd"))
|
||||||
return "keyboard";
|
return "keyboard";
|
||||||
} else {
|
return "lightbulb";
|
||||||
return "lightbulb";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
size: Theme.iconSize
|
size: Theme.iconSize
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
@@ -77,20 +76,16 @@ DankOSD {
|
|||||||
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
||||||
if (!deviceInfo)
|
if (!deviceInfo)
|
||||||
return 1;
|
return 1;
|
||||||
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
|
if (SessionData.getBrightnessExponential(deviceInfo.id))
|
||||||
if (isExponential) {
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0;
|
return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0;
|
||||||
}
|
}
|
||||||
maximum: {
|
maximum: {
|
||||||
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
||||||
if (!deviceInfo)
|
if (!deviceInfo)
|
||||||
return 100;
|
return 100;
|
||||||
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
|
if (SessionData.getBrightnessExponential(deviceInfo.id))
|
||||||
if (isExponential) {
|
|
||||||
return 100;
|
return 100;
|
||||||
}
|
|
||||||
return deviceInfo.displayMax || 100;
|
return deviceInfo.displayMax || 100;
|
||||||
}
|
}
|
||||||
enabled: DisplayService.brightnessAvailable
|
enabled: DisplayService.brightnessAvailable
|
||||||
@@ -99,28 +94,24 @@ DankOSD {
|
|||||||
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
||||||
if (!deviceInfo)
|
if (!deviceInfo)
|
||||||
return "%";
|
return "%";
|
||||||
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
|
if (SessionData.getBrightnessExponential(deviceInfo.id))
|
||||||
if (isExponential) {
|
|
||||||
return "%";
|
return "%";
|
||||||
}
|
|
||||||
return deviceInfo.class === "ddc" ? "" : "%";
|
return deviceInfo.class === "ddc" ? "" : "%";
|
||||||
}
|
}
|
||||||
thumbOutlineColor: Theme.surfaceContainer
|
thumbOutlineColor: Theme.surfaceContainer
|
||||||
alwaysShowValue: SettingsData.osdAlwaysShowValue
|
alwaysShowValue: SettingsData.osdAlwaysShowValue
|
||||||
|
|
||||||
onSliderValueChanged: newValue => {
|
onSliderValueChanged: newValue => {
|
||||||
if (DisplayService.brightnessAvailable) {
|
if (!DisplayService.brightnessAvailable)
|
||||||
DisplayService.setBrightness(newValue, DisplayService.lastIpcDevice, true);
|
return;
|
||||||
resetHideTimer();
|
DisplayService.setBrightness(newValue, DisplayService.lastIpcDevice, true);
|
||||||
}
|
resetHideTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
onContainsMouseChanged: {
|
onContainsMouseChanged: setChildHovered(containsMouse)
|
||||||
setChildHovered(containsMouse);
|
|
||||||
}
|
|
||||||
|
|
||||||
Binding on value {
|
Binding on value {
|
||||||
value: root.targetBrightness
|
value: root._displayBrightness
|
||||||
when: !brightnessSlider.isDragging
|
when: !brightnessSlider.isDragging
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,13 +137,11 @@ DankOSD {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: {
|
name: {
|
||||||
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
||||||
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc") {
|
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc")
|
||||||
return "brightness_medium";
|
return "brightness_medium";
|
||||||
} else if (deviceInfo.name.includes("kbd")) {
|
if (deviceInfo.name.includes("kbd"))
|
||||||
return "keyboard";
|
return "keyboard";
|
||||||
} else {
|
return "lightbulb";
|
||||||
return "lightbulb";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
size: Theme.iconSize
|
size: Theme.iconSize
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
@@ -170,7 +159,7 @@ DankOSD {
|
|||||||
property int value: 50
|
property int value: 50
|
||||||
|
|
||||||
Binding on value {
|
Binding on value {
|
||||||
value: root.targetBrightness
|
value: root._displayBrightness
|
||||||
when: !vertSlider.dragging
|
when: !vertSlider.dragging
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,8 +167,7 @@ DankOSD {
|
|||||||
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
||||||
if (!deviceInfo)
|
if (!deviceInfo)
|
||||||
return 1;
|
return 1;
|
||||||
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
|
if (SessionData.getBrightnessExponential(deviceInfo.id))
|
||||||
if (isExponential)
|
|
||||||
return 1;
|
return 1;
|
||||||
return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0;
|
return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0;
|
||||||
}
|
}
|
||||||
@@ -188,8 +176,7 @@ DankOSD {
|
|||||||
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
const deviceInfo = DisplayService.getCurrentDeviceInfo();
|
||||||
if (!deviceInfo)
|
if (!deviceInfo)
|
||||||
return 100;
|
return 100;
|
||||||
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
|
if (SessionData.getBrightnessExponential(deviceInfo.id))
|
||||||
if (isExponential)
|
|
||||||
return 100;
|
return 100;
|
||||||
return deviceInfo.displayMax || 100;
|
return deviceInfo.displayMax || 100;
|
||||||
}
|
}
|
||||||
@@ -240,33 +227,25 @@ DankOSD {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onContainsMouseChanged: {
|
onContainsMouseChanged: setChildHovered(containsMouse)
|
||||||
setChildHovered(containsMouse);
|
|
||||||
}
|
|
||||||
|
|
||||||
onPressed: mouse => {
|
onPressed: mouse => {
|
||||||
vertSlider.dragging = true;
|
vertSlider.dragging = true;
|
||||||
updateBrightness(mouse);
|
updateBrightness(mouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
onReleased: vertSlider.dragging = false
|
||||||
vertSlider.dragging = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
onPositionChanged: mouse => {
|
onPositionChanged: mouse => {
|
||||||
if (pressed) {
|
if (pressed)
|
||||||
updateBrightness(mouse);
|
updateBrightness(mouse);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: mouse => {
|
onClicked: mouse => updateBrightness(mouse)
|
||||||
updateBrightness(mouse);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateBrightness(mouse) {
|
function updateBrightness(mouse) {
|
||||||
if (!DisplayService.brightnessAvailable) {
|
if (!DisplayService.brightnessAvailable)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
const ratio = 1.0 - (mouse.y / height);
|
const ratio = 1.0 - (mouse.y / height);
|
||||||
const newValue = Math.round(vertSlider.minimum + ratio * (vertSlider.maximum - vertSlider.minimum));
|
const newValue = Math.round(vertSlider.minimum + ratio * (vertSlider.maximum - vertSlider.minimum));
|
||||||
vertSlider.value = newValue;
|
vertSlider.value = newValue;
|
||||||
|
|||||||
@@ -8,13 +8,20 @@ DankOSD {
|
|||||||
|
|
||||||
readonly property bool useVertical: isVerticalLayout
|
readonly property bool useVertical: isVerticalLayout
|
||||||
readonly property var player: MprisController.activePlayer
|
readonly property var player: MprisController.activePlayer
|
||||||
readonly property int currentVolume: player ? Math.min(100, Math.round(player.volume * 100)) : 0
|
|
||||||
readonly property bool volumeSupported: player?.volumeSupported ?? false
|
readonly property bool volumeSupported: player?.volumeSupported ?? false
|
||||||
property bool _suppressNewPlayer: false
|
property bool _suppressNewPlayer: false
|
||||||
|
property int _displayVolume: 0
|
||||||
|
|
||||||
|
function _syncVolume() {
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
_displayVolume = Math.min(100, Math.round(player.volume * 100));
|
||||||
|
}
|
||||||
|
|
||||||
onPlayerChanged: {
|
onPlayerChanged: {
|
||||||
_suppressNewPlayer = true;
|
_suppressNewPlayer = true;
|
||||||
_suppressTimer.restart();
|
_suppressTimer.restart();
|
||||||
|
_syncVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
@@ -37,25 +44,25 @@ DankOSD {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toggleMute() {
|
function toggleMute() {
|
||||||
if (player) {
|
if (!player)
|
||||||
player.volume = player.volume > 0 ? 0 : 1;
|
return;
|
||||||
}
|
player.volume = player.volume > 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setVolume(volumePercent) {
|
function setVolume(volumePercent) {
|
||||||
if (player) {
|
if (!player)
|
||||||
player.volume = volumePercent / 100;
|
return;
|
||||||
resetHideTimer();
|
player.volume = volumePercent / 100;
|
||||||
}
|
resetHideTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: player
|
target: player
|
||||||
|
|
||||||
function onVolumeChanged() {
|
function onVolumeChanged() {
|
||||||
if (SettingsData.osdMediaVolumeEnabled && volumeSupported && !_suppressNewPlayer) {
|
root._syncVolume();
|
||||||
|
if (SettingsData.osdMediaVolumeEnabled && volumeSupported && !_suppressNewPlayer)
|
||||||
root.show();
|
root.show();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,9 +103,7 @@ DankOSD {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: toggleMute()
|
onClicked: toggleMute()
|
||||||
onContainsMouseChanged: {
|
onContainsMouseChanged: setChildHovered(containsMouse || volumeSlider.containsMouse)
|
||||||
setChildHovered(containsMouse || volumeSlider.containsMouse);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,29 +120,21 @@ DankOSD {
|
|||||||
showValue: true
|
showValue: true
|
||||||
unit: "%"
|
unit: "%"
|
||||||
thumbOutlineColor: Theme.surfaceContainer
|
thumbOutlineColor: Theme.surfaceContainer
|
||||||
valueOverride: currentVolume
|
valueOverride: root._displayVolume
|
||||||
alwaysShowValue: SettingsData.osdAlwaysShowValue
|
alwaysShowValue: SettingsData.osdAlwaysShowValue
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
value = currentVolume;
|
root._syncVolume();
|
||||||
|
value = root._displayVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSliderValueChanged: newValue => {
|
onSliderValueChanged: newValue => setVolume(newValue)
|
||||||
setVolume(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
onContainsMouseChanged: {
|
onContainsMouseChanged: setChildHovered(containsMouse || muteButton.containsMouse)
|
||||||
setChildHovered(containsMouse || muteButton.containsMouse);
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Binding on value {
|
||||||
target: player
|
value: root._displayVolume
|
||||||
|
when: !volumeSlider.pressed
|
||||||
function onVolumeChanged() {
|
|
||||||
if (volumeSlider && !volumeSlider.pressed) {
|
|
||||||
volumeSlider.value = currentVolume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,9 +169,7 @@ DankOSD {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: toggleMute()
|
onClicked: toggleMute()
|
||||||
onContainsMouseChanged: {
|
onContainsMouseChanged: setChildHovered(containsMouse || vertSliderArea.containsMouse)
|
||||||
setChildHovered(containsMouse || vertSliderArea.containsMouse);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +181,7 @@ DankOSD {
|
|||||||
y: gap * 2 + Theme.iconSize
|
y: gap * 2 + Theme.iconSize
|
||||||
|
|
||||||
property bool dragging: false
|
property bool dragging: false
|
||||||
property int value: currentVolume
|
property int value: root._displayVolume
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: vertTrack
|
id: vertTrack
|
||||||
@@ -231,28 +226,21 @@ DankOSD {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onContainsMouseChanged: {
|
onContainsMouseChanged: setChildHovered(containsMouse || muteButtonVert.containsMouse)
|
||||||
setChildHovered(containsMouse || muteButtonVert.containsMouse);
|
|
||||||
}
|
|
||||||
|
|
||||||
onPressed: mouse => {
|
onPressed: mouse => {
|
||||||
vertSlider.dragging = true;
|
vertSlider.dragging = true;
|
||||||
updateVolume(mouse);
|
updateVolume(mouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
onReleased: vertSlider.dragging = false
|
||||||
vertSlider.dragging = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
onPositionChanged: mouse => {
|
onPositionChanged: mouse => {
|
||||||
if (pressed) {
|
if (pressed)
|
||||||
updateVolume(mouse);
|
updateVolume(mouse);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: mouse => {
|
onClicked: mouse => updateVolume(mouse)
|
||||||
updateVolume(mouse);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateVolume(mouse) {
|
function updateVolume(mouse) {
|
||||||
const ratio = 1.0 - (mouse.y / height);
|
const ratio = 1.0 - (mouse.y / height);
|
||||||
@@ -260,16 +248,6 @@ DankOSD {
|
|||||||
setVolume(volume);
|
setVolume(volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: player
|
|
||||||
|
|
||||||
function onVolumeChanged() {
|
|
||||||
if (!vertSlider.dragging) {
|
|
||||||
vertSlider.value = currentVolume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -283,15 +261,4 @@ DankOSD {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onOsdShown: {
|
|
||||||
if (player && contentLoader.item && contentLoader.item.item) {
|
|
||||||
if (!useVertical) {
|
|
||||||
const slider = contentLoader.item.item.children[0].children[1];
|
|
||||||
if (slider && slider.value !== undefined) {
|
|
||||||
slider.value = currentVolume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ DankOSD {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property bool useVertical: isVerticalLayout
|
readonly property bool useVertical: isVerticalLayout
|
||||||
|
property int _displayVolume: 0
|
||||||
|
|
||||||
|
function _syncVolume() {
|
||||||
|
if (!AudioService.sink?.audio)
|
||||||
|
return;
|
||||||
|
_displayVolume = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
|
||||||
|
}
|
||||||
|
|
||||||
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
|
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
|
||||||
osdHeight: useVertical ? Math.min(260, Screen.height - Theme.spacingM * 2) : (40 + Theme.spacingS * 2)
|
osdHeight: useVertical ? Math.min(260, Screen.height - Theme.spacingM * 2) : (40 + Theme.spacingS * 2)
|
||||||
@@ -14,18 +21,17 @@ DankOSD {
|
|||||||
enableMouseInteraction: true
|
enableMouseInteraction: true
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
|
target: AudioService.sink?.audio ?? null
|
||||||
|
|
||||||
function onVolumeChanged() {
|
function onVolumeChanged() {
|
||||||
if (SettingsData.osdVolumeEnabled) {
|
root._syncVolume();
|
||||||
|
if (SettingsData.osdVolumeEnabled)
|
||||||
root.show();
|
root.show();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMutedChanged() {
|
function onMutedChanged() {
|
||||||
if (SettingsData.osdVolumeEnabled) {
|
if (SettingsData.osdVolumeEnabled)
|
||||||
root.show();
|
root.show();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,9 +39,9 @@ DankOSD {
|
|||||||
target: AudioService
|
target: AudioService
|
||||||
|
|
||||||
function onSinkChanged() {
|
function onSinkChanged() {
|
||||||
if (root.shouldBeVisible && SettingsData.osdVolumeEnabled) {
|
root._syncVolume();
|
||||||
|
if (root.shouldBeVisible && SettingsData.osdVolumeEnabled)
|
||||||
root.show();
|
root.show();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +70,7 @@ DankOSD {
|
|||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.muted ? "volume_off" : "volume_up"
|
name: AudioService.sink?.audio?.muted ? "volume_off" : "volume_up"
|
||||||
size: Theme.iconSize
|
size: Theme.iconSize
|
||||||
color: muteButton.containsMouse ? Theme.primary : Theme.surfaceText
|
color: muteButton.containsMouse ? Theme.primary : Theme.surfaceText
|
||||||
}
|
}
|
||||||
@@ -75,60 +81,45 @@ DankOSD {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: AudioService.toggleMute()
|
||||||
AudioService.toggleMute();
|
onContainsMouseChanged: setChildHovered(containsMouse || volumeSlider.containsMouse)
|
||||||
}
|
|
||||||
onContainsMouseChanged: {
|
|
||||||
setChildHovered(containsMouse || volumeSlider.containsMouse);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankSlider {
|
DankSlider {
|
||||||
id: volumeSlider
|
id: volumeSlider
|
||||||
|
|
||||||
readonly property real actualVolumePercent: AudioService.sink && AudioService.sink.audio ? Math.round(AudioService.sink.audio.volume * 100) : 0
|
|
||||||
readonly property real displayPercent: actualVolumePercent
|
|
||||||
|
|
||||||
width: parent.width - Theme.iconSize - parent.gap * 3
|
width: parent.width - Theme.iconSize - parent.gap * 3
|
||||||
height: 40
|
height: 40
|
||||||
x: parent.gap * 2 + Theme.iconSize
|
x: parent.gap * 2 + Theme.iconSize
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: AudioService.sinkMaxVolume
|
maximum: AudioService.sinkMaxVolume
|
||||||
enabled: AudioService.sink && AudioService.sink.audio
|
enabled: AudioService.sink?.audio
|
||||||
showValue: true
|
showValue: true
|
||||||
unit: "%"
|
unit: "%"
|
||||||
thumbOutlineColor: Theme.surfaceContainer
|
thumbOutlineColor: Theme.surfaceContainer
|
||||||
valueOverride: displayPercent
|
valueOverride: root._displayVolume
|
||||||
alwaysShowValue: SettingsData.osdAlwaysShowValue
|
alwaysShowValue: SettingsData.osdAlwaysShowValue
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
root._syncVolume();
|
||||||
value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
|
value = root._displayVolume;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSliderValueChanged: newValue => {
|
onSliderValueChanged: newValue => {
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
if (!AudioService.sink?.audio)
|
||||||
SessionData.suppressOSDTemporarily();
|
return;
|
||||||
AudioService.sink.audio.volume = newValue / 100;
|
SessionData.suppressOSDTemporarily();
|
||||||
resetHideTimer();
|
AudioService.sink.audio.volume = newValue / 100;
|
||||||
}
|
resetHideTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
onContainsMouseChanged: {
|
onContainsMouseChanged: setChildHovered(containsMouse || muteButton.containsMouse)
|
||||||
setChildHovered(containsMouse || muteButton.containsMouse);
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Binding on value {
|
||||||
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
|
value: root._displayVolume
|
||||||
|
when: !volumeSlider.pressed
|
||||||
function onVolumeChanged() {
|
|
||||||
if (volumeSlider && !volumeSlider.pressed) {
|
|
||||||
volumeSlider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +142,7 @@ DankOSD {
|
|||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.muted ? "volume_off" : "volume_up"
|
name: AudioService.sink?.audio?.muted ? "volume_off" : "volume_up"
|
||||||
size: Theme.iconSize
|
size: Theme.iconSize
|
||||||
color: muteButtonVert.containsMouse ? Theme.primary : Theme.surfaceText
|
color: muteButtonVert.containsMouse ? Theme.primary : Theme.surfaceText
|
||||||
}
|
}
|
||||||
@@ -162,12 +153,8 @@ DankOSD {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: AudioService.toggleMute()
|
||||||
AudioService.toggleMute();
|
onContainsMouseChanged: setChildHovered(containsMouse || vertSliderArea.containsMouse)
|
||||||
}
|
|
||||||
onContainsMouseChanged: {
|
|
||||||
setChildHovered(containsMouse || vertSliderArea.containsMouse);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +166,7 @@ DankOSD {
|
|||||||
y: gap * 2 + Theme.iconSize
|
y: gap * 2 + Theme.iconSize
|
||||||
|
|
||||||
property bool dragging: false
|
property bool dragging: false
|
||||||
property int value: AudioService.sink && AudioService.sink.audio ? Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100)) : 0
|
property int value: root._displayVolume
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: vertTrack
|
id: vertTrack
|
||||||
@@ -220,50 +207,35 @@ DankOSD {
|
|||||||
id: vertSliderArea
|
id: vertSliderArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: -12
|
anchors.margins: -12
|
||||||
enabled: AudioService.sink && AudioService.sink.audio
|
enabled: AudioService.sink?.audio
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onContainsMouseChanged: {
|
onContainsMouseChanged: setChildHovered(containsMouse || muteButtonVert.containsMouse)
|
||||||
setChildHovered(containsMouse || muteButtonVert.containsMouse);
|
|
||||||
}
|
|
||||||
|
|
||||||
onPressed: mouse => {
|
onPressed: mouse => {
|
||||||
vertSlider.dragging = true;
|
vertSlider.dragging = true;
|
||||||
updateVolume(mouse);
|
updateVolume(mouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
onReleased: vertSlider.dragging = false
|
||||||
vertSlider.dragging = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
onPositionChanged: mouse => {
|
onPositionChanged: mouse => {
|
||||||
if (pressed) {
|
if (pressed)
|
||||||
updateVolume(mouse);
|
updateVolume(mouse);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: mouse => {
|
onClicked: mouse => updateVolume(mouse)
|
||||||
updateVolume(mouse);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateVolume(mouse) {
|
function updateVolume(mouse) {
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
if (!AudioService.sink?.audio)
|
||||||
const maxVol = AudioService.sinkMaxVolume;
|
return;
|
||||||
const ratio = 1.0 - (mouse.y / height);
|
const maxVol = AudioService.sinkMaxVolume;
|
||||||
const volume = Math.max(0, Math.min(maxVol, Math.round(ratio * maxVol)));
|
const ratio = 1.0 - (mouse.y / height);
|
||||||
SessionData.suppressOSDTemporarily();
|
const volume = Math.max(0, Math.min(maxVol, Math.round(ratio * maxVol)));
|
||||||
AudioService.sink.audio.volume = volume / 100;
|
SessionData.suppressOSDTemporarily();
|
||||||
resetHideTimer();
|
AudioService.sink.audio.volume = volume / 100;
|
||||||
}
|
resetHideTimer();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
|
|
||||||
|
|
||||||
function onVolumeChanged() {
|
|
||||||
vertSlider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,15 +251,4 @@ DankOSD {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onOsdShown: {
|
|
||||||
if (AudioService.sink && AudioService.sink.audio && contentLoader.item && contentLoader.item.item) {
|
|
||||||
if (!useVertical) {
|
|
||||||
const slider = contentLoader.item.item.children[0].children[1];
|
|
||||||
if (slider && slider.value !== undefined) {
|
|
||||||
slider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,36 +18,41 @@ Column {
|
|||||||
property bool isInitialized: false
|
property bool isInitialized: false
|
||||||
|
|
||||||
function loadValue() {
|
function loadValue() {
|
||||||
const settings = findSettings()
|
const settings = findSettings();
|
||||||
if (settings && settings.pluginService) {
|
if (settings && settings.pluginService) {
|
||||||
const loadedValue = settings.loadValue(settingKey, defaultValue)
|
const loadedValue = settings.loadValue(settingKey, defaultValue);
|
||||||
value = loadedValue
|
if (textField.activeFocus && isInitialized)
|
||||||
textField.text = loadedValue
|
return;
|
||||||
isInitialized = true
|
value = loadedValue;
|
||||||
|
textField.text = loadedValue;
|
||||||
|
isInitialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Qt.callLater(loadValue)
|
Qt.callLater(loadValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
onValueChanged: {
|
function commit() {
|
||||||
if (!isInitialized) return
|
if (!isInitialized)
|
||||||
const settings = findSettings()
|
return;
|
||||||
if (settings) {
|
if (textField.text === value)
|
||||||
settings.saveValue(settingKey, value)
|
return;
|
||||||
}
|
value = textField.text;
|
||||||
|
const settings = findSettings();
|
||||||
|
if (settings)
|
||||||
|
settings.saveValue(settingKey, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findSettings() {
|
function findSettings() {
|
||||||
let item = parent
|
let item = parent;
|
||||||
while (item) {
|
while (item) {
|
||||||
if (item.saveValue !== undefined && item.loadValue !== undefined) {
|
if (item.saveValue !== undefined && item.loadValue !== undefined) {
|
||||||
return item
|
return item;
|
||||||
}
|
}
|
||||||
item = item.parent
|
item = item.parent;
|
||||||
}
|
}
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -70,16 +75,10 @@ Column {
|
|||||||
id: textField
|
id: textField
|
||||||
width: parent.width
|
width: parent.width
|
||||||
placeholderText: root.placeholder
|
placeholderText: root.placeholder
|
||||||
onTextEdited: {
|
onEditingFinished: root.commit()
|
||||||
root.value = text
|
|
||||||
}
|
|
||||||
onEditingFinished: {
|
|
||||||
root.value = text
|
|
||||||
}
|
|
||||||
onActiveFocusChanged: {
|
onActiveFocusChanged: {
|
||||||
if (!activeFocus) {
|
if (!activeFocus)
|
||||||
root.value = text
|
root.commit();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ DankPopout {
|
|||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
popupWidth: 650
|
popupWidth: Math.round(Theme.fontSizeMedium * 46)
|
||||||
popupHeight: 550
|
popupHeight: Math.round(Theme.fontSizeMedium * 39)
|
||||||
triggerWidth: 55
|
triggerWidth: 55
|
||||||
positioning: ""
|
positioning: ""
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
@@ -151,11 +151,13 @@ DankPopout {
|
|||||||
|
|
||||||
DankButtonGroup {
|
DankButtonGroup {
|
||||||
id: processFilterGroup
|
id: processFilterGroup
|
||||||
Layout.minimumWidth: implicitWidth + 8
|
Layout.minimumWidth: implicitWidth
|
||||||
model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")]
|
model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")]
|
||||||
currentIndex: 0
|
currentIndex: 0
|
||||||
checkEnabled: false
|
checkEnabled: false
|
||||||
buttonHeight: Math.round(Theme.fontSizeMedium * 2.2)
|
buttonHeight: Math.round(Theme.fontSizeSmall * 2.4)
|
||||||
|
minButtonWidth: 0
|
||||||
|
buttonPadding: Theme.spacingM
|
||||||
textSize: Theme.fontSizeSmall
|
textSize: Theme.fontSizeSmall
|
||||||
onSelectionChanged: (index, selected) => {
|
onSelectionChanged: (index, selected) => {
|
||||||
if (!selected)
|
if (!selected)
|
||||||
@@ -177,7 +179,8 @@ DankPopout {
|
|||||||
|
|
||||||
DankTextField {
|
DankTextField {
|
||||||
id: searchField
|
id: searchField
|
||||||
Layout.preferredWidth: Theme.fontSizeMedium * 14
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: Theme.fontSizeMedium * 8
|
||||||
Layout.preferredHeight: Theme.fontSizeMedium * 2.5
|
Layout.preferredHeight: Theme.fontSizeMedium * 2.5
|
||||||
placeholderText: I18n.tr("Search...")
|
placeholderText: I18n.tr("Search...")
|
||||||
leftIconName: "search"
|
leftIconName: "search"
|
||||||
|
|||||||
@@ -1061,7 +1061,7 @@ Singleton {
|
|||||||
|
|
||||||
function getHyprlandOutputIdentifier(output, outputName) {
|
function getHyprlandOutputIdentifier(output, outputName) {
|
||||||
if (SettingsData.displayNameMode === "model" && output?.make && output?.model)
|
if (SettingsData.displayNameMode === "model" && output?.make && output?.model)
|
||||||
return "desc:" + output.make + " " + output.model;
|
return "desc:" + output.make + " " + output.model + " " + (output?.serial || "Unknown");
|
||||||
return outputName;
|
return outputName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,56 +30,6 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
spacing: Theme.spacingXL
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
SettingsCard {
|
|
||||||
width: parent.width
|
|
||||||
iconName: "swap_vert"
|
|
||||||
title: I18n.tr("Position")
|
|
||||||
settingKey: "dockPosition"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: dockPositionButtonGroup.height
|
|
||||||
|
|
||||||
DankButtonGroup {
|
|
||||||
id: dockPositionButtonGroup
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
model: [I18n.tr("Top"), I18n.tr("Bottom"), I18n.tr("Left"), I18n.tr("Right")]
|
|
||||||
currentIndex: {
|
|
||||||
switch (SettingsData.dockPosition) {
|
|
||||||
case SettingsData.Position.Top:
|
|
||||||
return 0;
|
|
||||||
case SettingsData.Position.Bottom:
|
|
||||||
return 1;
|
|
||||||
case SettingsData.Position.Left:
|
|
||||||
return 2;
|
|
||||||
case SettingsData.Position.Right:
|
|
||||||
return 3;
|
|
||||||
default:
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onSelectionChanged: (index, selected) => {
|
|
||||||
if (!selected)
|
|
||||||
return;
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
SettingsData.setDockPosition(SettingsData.Position.Top);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
SettingsData.setDockPosition(SettingsData.Position.Bottom);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
SettingsData.setDockPosition(SettingsData.Position.Left);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
SettingsData.setDockPosition(SettingsData.Position.Right);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
iconName: "dock_to_bottom"
|
iconName: "dock_to_bottom"
|
||||||
@@ -136,6 +86,56 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "swap_vert"
|
||||||
|
title: I18n.tr("Position")
|
||||||
|
settingKey: "dockPosition"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: dockPositionButtonGroup.height
|
||||||
|
|
||||||
|
DankButtonGroup {
|
||||||
|
id: dockPositionButtonGroup
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
model: [I18n.tr("Top"), I18n.tr("Bottom"), I18n.tr("Left"), I18n.tr("Right")]
|
||||||
|
currentIndex: {
|
||||||
|
switch (SettingsData.dockPosition) {
|
||||||
|
case SettingsData.Position.Top:
|
||||||
|
return 0;
|
||||||
|
case SettingsData.Position.Bottom:
|
||||||
|
return 1;
|
||||||
|
case SettingsData.Position.Left:
|
||||||
|
return 2;
|
||||||
|
case SettingsData.Position.Right:
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSelectionChanged: (index, selected) => {
|
||||||
|
if (!selected)
|
||||||
|
return;
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
SettingsData.setDockPosition(SettingsData.Position.Top);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
SettingsData.setDockPosition(SettingsData.Position.Bottom);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
SettingsData.setDockPosition(SettingsData.Position.Left);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
SettingsData.setDockPosition(SettingsData.Position.Right);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
iconName: "apps"
|
iconName: "apps"
|
||||||
|
|||||||
@@ -375,8 +375,10 @@ FocusScope {
|
|||||||
if (!plugin || !PluginService.isPluginLoaded(pluginId))
|
if (!plugin || !PluginService.isPluginLoaded(pluginId))
|
||||||
return;
|
return;
|
||||||
var isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher"));
|
var isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher"));
|
||||||
if (isLauncher)
|
if (isLauncher) {
|
||||||
|
pluginsTab.isReloading = true;
|
||||||
PluginService.reloadPlugin(pluginId);
|
PluginService.reloadPlugin(pluginId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import qs.Modules.Settings.Widgets
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
readonly property var timeoutOptions: [I18n.tr("Never"), I18n.tr("1 minute"), I18n.tr("2 minutes"), I18n.tr("3 minutes"), I18n.tr("5 minutes"), I18n.tr("10 minutes"), I18n.tr("15 minutes"), I18n.tr("20 minutes"), I18n.tr("30 minutes"), I18n.tr("1 hour"), I18n.tr("1 hour 30 minutes"), I18n.tr("2 hours"), I18n.tr("3 hours")]
|
||||||
readonly property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
readonly property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
||||||
|
|
||||||
function getTimeoutIndex(timeout) {
|
function getTimeoutIndex(timeout) {
|
||||||
@@ -56,7 +56,7 @@ Item {
|
|||||||
id: powerCategory
|
id: powerCategory
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: BatteryService.batteryAvailable
|
visible: BatteryService.batteryAvailable
|
||||||
model: ["AC Power", "Battery"]
|
model: [I18n.tr("AC Power"), I18n.tr("Battery")]
|
||||||
currentIndex: 0
|
currentIndex: 0
|
||||||
selectionMode: "single"
|
selectionMode: "single"
|
||||||
checkEnabled: false
|
checkEnabled: false
|
||||||
@@ -100,7 +100,7 @@ Item {
|
|||||||
id: fadeGracePeriodDropdown
|
id: fadeGracePeriodDropdown
|
||||||
settingKey: "fadeToLockGracePeriod"
|
settingKey: "fadeToLockGracePeriod"
|
||||||
tags: ["fade", "grace", "period", "timeout", "lock"]
|
tags: ["fade", "grace", "period", "timeout", "lock"]
|
||||||
property var periodOptions: ["1 second", "2 seconds", "3 seconds", "4 seconds", "5 seconds", "10 seconds", "15 seconds", "20 seconds", "30 seconds"]
|
property var periodOptions: [I18n.tr("1 second"), I18n.tr("2 seconds"), I18n.tr("3 seconds"), I18n.tr("4 seconds"), I18n.tr("5 seconds"), I18n.tr("10 seconds"), I18n.tr("15 seconds"), I18n.tr("20 seconds"), I18n.tr("30 seconds")]
|
||||||
property var periodValues: [1, 2, 3, 4, 5, 10, 15, 20, 30]
|
property var periodValues: [1, 2, 3, 4, 5, 10, 15, 20, 30]
|
||||||
|
|
||||||
text: I18n.tr("Lock fade grace period")
|
text: I18n.tr("Lock fade grace period")
|
||||||
@@ -111,7 +111,7 @@ Item {
|
|||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
const currentPeriod = SettingsData.fadeToLockGracePeriod;
|
const currentPeriod = SettingsData.fadeToLockGracePeriod;
|
||||||
const index = periodValues.indexOf(currentPeriod);
|
const index = periodValues.indexOf(currentPeriod);
|
||||||
currentValue = index >= 0 ? periodOptions[index] : "5 seconds";
|
currentValue = index >= 0 ? periodOptions[index] : I18n.tr("5 seconds");
|
||||||
}
|
}
|
||||||
|
|
||||||
onValueChanged: value => {
|
onValueChanged: value => {
|
||||||
@@ -126,7 +126,7 @@ Item {
|
|||||||
id: fadeDpmsGracePeriodDropdown
|
id: fadeDpmsGracePeriodDropdown
|
||||||
settingKey: "fadeToDpmsGracePeriod"
|
settingKey: "fadeToDpmsGracePeriod"
|
||||||
tags: ["fade", "grace", "period", "timeout", "dpms", "monitor"]
|
tags: ["fade", "grace", "period", "timeout", "dpms", "monitor"]
|
||||||
property var periodOptions: ["1 second", "2 seconds", "3 seconds", "4 seconds", "5 seconds", "10 seconds", "15 seconds", "20 seconds", "30 seconds"]
|
property var periodOptions: [I18n.tr("1 second"), I18n.tr("2 seconds"), I18n.tr("3 seconds"), I18n.tr("4 seconds"), I18n.tr("5 seconds"), I18n.tr("10 seconds"), I18n.tr("15 seconds"), I18n.tr("20 seconds"), I18n.tr("30 seconds")]
|
||||||
property var periodValues: [1, 2, 3, 4, 5, 10, 15, 20, 30]
|
property var periodValues: [1, 2, 3, 4, 5, 10, 15, 20, 30]
|
||||||
|
|
||||||
text: I18n.tr("Monitor fade grace period")
|
text: I18n.tr("Monitor fade grace period")
|
||||||
@@ -137,7 +137,7 @@ Item {
|
|||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
const currentPeriod = SettingsData.fadeToDpmsGracePeriod;
|
const currentPeriod = SettingsData.fadeToDpmsGracePeriod;
|
||||||
const index = periodValues.indexOf(currentPeriod);
|
const index = periodValues.indexOf(currentPeriod);
|
||||||
currentValue = index >= 0 ? periodOptions[index] : "5 seconds";
|
currentValue = index >= 0 ? periodOptions[index] : I18n.tr("5 seconds");
|
||||||
}
|
}
|
||||||
|
|
||||||
onValueChanged: value => {
|
onValueChanged: value => {
|
||||||
@@ -308,7 +308,7 @@ Item {
|
|||||||
DankButtonGroup {
|
DankButtonGroup {
|
||||||
id: suspendBehaviorSelector
|
id: suspendBehaviorSelector
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
model: ["Suspend", "Hibernate", "Suspend then Hibernate"]
|
model: [I18n.tr("Suspend"), I18n.tr("Hibernate"), I18n.tr("Suspend then Hibernate")]
|
||||||
selectionMode: "single"
|
selectionMode: "single"
|
||||||
checkEnabled: false
|
checkEnabled: false
|
||||||
|
|
||||||
@@ -375,13 +375,13 @@ Item {
|
|||||||
settingKey: "powerMenuDefaultAction"
|
settingKey: "powerMenuDefaultAction"
|
||||||
tags: ["power", "menu", "default", "action", "reboot", "logout", "shutdown"]
|
tags: ["power", "menu", "default", "action", "reboot", "logout", "shutdown"]
|
||||||
text: I18n.tr("Default selected action")
|
text: I18n.tr("Default selected action")
|
||||||
options: ["Reboot", "Log Out", "Power Off", "Lock", "Suspend", "Restart DMS", "Hibernate"]
|
options: [I18n.tr("Reboot"), I18n.tr("Log Out"), I18n.tr("Power Off"), I18n.tr("Lock"), I18n.tr("Suspend"), I18n.tr("Restart DMS"), I18n.tr("Hibernate")]
|
||||||
property var actionValues: ["reboot", "logout", "poweroff", "lock", "suspend", "restart", "hibernate"]
|
property var actionValues: ["reboot", "logout", "poweroff", "lock", "suspend", "restart", "hibernate"]
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
const currentAction = SettingsData.powerMenuDefaultAction || "logout";
|
const currentAction = SettingsData.powerMenuDefaultAction || "logout";
|
||||||
const index = actionValues.indexOf(currentAction);
|
const index = actionValues.indexOf(currentAction);
|
||||||
currentValue = index >= 0 ? options[index] : "Log Out";
|
currentValue = index >= 0 ? options[index] : I18n.tr("Log Out");
|
||||||
}
|
}
|
||||||
|
|
||||||
onValueChanged: value => {
|
onValueChanged: value => {
|
||||||
@@ -479,7 +479,7 @@ Item {
|
|||||||
id: holdDurationDropdown
|
id: holdDurationDropdown
|
||||||
settingKey: "powerActionHoldDuration"
|
settingKey: "powerActionHoldDuration"
|
||||||
tags: ["power", "hold", "duration", "confirm", "time"]
|
tags: ["power", "hold", "duration", "confirm", "time"]
|
||||||
property var durationOptions: ["250 ms", "500 ms", "750 ms", "1 second", "2 seconds", "3 seconds", "5 seconds", "10 seconds"]
|
property var durationOptions: [I18n.tr("250 ms"), I18n.tr("500 ms"), I18n.tr("750 ms"), I18n.tr("1 second"), I18n.tr("2 seconds"), I18n.tr("3 seconds"), I18n.tr("5 seconds"), I18n.tr("10 seconds")]
|
||||||
property var durationValues: [0.25, 0.5, 0.75, 1, 2, 3, 5, 10]
|
property var durationValues: [0.25, 0.5, 0.75, 1, 2, 3, 5, 10]
|
||||||
|
|
||||||
text: I18n.tr("Hold Duration")
|
text: I18n.tr("Hold Duration")
|
||||||
@@ -489,7 +489,7 @@ Item {
|
|||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
const currentDuration = SettingsData.powerActionHoldDuration;
|
const currentDuration = SettingsData.powerActionHoldDuration;
|
||||||
const index = durationValues.indexOf(currentDuration);
|
const index = durationValues.indexOf(currentDuration);
|
||||||
currentValue = index >= 0 ? durationOptions[index] : "500 ms";
|
currentValue = index >= 0 ? durationOptions[index] : I18n.tr("500 ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
onValueChanged: value => {
|
onValueChanged: value => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Item {
|
|||||||
property var cachedCursorThemes: SettingsData.availableCursorThemes
|
property var cachedCursorThemes: SettingsData.availableCursorThemes
|
||||||
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
|
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
|
||||||
property var installedRegistryThemes: []
|
property var installedRegistryThemes: []
|
||||||
property var templateDetection: ({})
|
property var templateDetection: []
|
||||||
|
|
||||||
property var cursorIncludeStatus: ({
|
property var cursorIncludeStatus: ({
|
||||||
"exists": false,
|
"exists": false,
|
||||||
@@ -106,9 +106,10 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isTemplateDetected(templateId) {
|
function isTemplateDetected(templateId) {
|
||||||
if (!templateDetection || Object.keys(templateDetection).length === 0)
|
if (!templateDetection || templateDetection.length === 0)
|
||||||
return true;
|
return true;
|
||||||
return templateDetection[templateId] !== false;
|
var item = templateDetection.find(i => i.id === templateId);
|
||||||
|
return !item || item.detected !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTemplateDescription(templateId, baseDescription) {
|
function getTemplateDescription(templateId, baseDescription) {
|
||||||
@@ -145,30 +146,17 @@ Item {
|
|||||||
DMSService.listInstalledThemes();
|
DMSService.listInstalledThemes();
|
||||||
if (PopoutService.pendingThemeInstall)
|
if (PopoutService.pendingThemeInstall)
|
||||||
Qt.callLater(() => showThemeBrowser());
|
Qt.callLater(() => showThemeBrowser());
|
||||||
templateCheckProcess.running = true;
|
Proc.runCommand("template-check", ["dms", "matugen", "check"], (output, exitCode) => {
|
||||||
|
if (exitCode !== 0)
|
||||||
|
return;
|
||||||
|
try {
|
||||||
|
themeColorsTab.templateDetection = JSON.parse(output.trim());
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl)
|
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl)
|
||||||
checkCursorIncludeStatus();
|
checkCursorIncludeStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
|
||||||
id: templateCheckProcess
|
|
||||||
command: ["dms", "matugen", "check"]
|
|
||||||
running: false
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
try {
|
|
||||||
const results = JSON.parse(text);
|
|
||||||
const detection = {};
|
|
||||||
for (const item of results) {
|
|
||||||
detection[item.id] = item.detected;
|
|
||||||
}
|
|
||||||
themeColorsTab.templateDetection = detection;
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: DMSService
|
target: DMSService
|
||||||
function onInstalledThemesReceived(themes) {
|
function onInstalledThemesReceived(themes) {
|
||||||
@@ -1045,11 +1033,11 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
model: [
|
model: [
|
||||||
{
|
{
|
||||||
"text": "Time",
|
"text": I18n.tr("Time", "theme auto mode tab"),
|
||||||
"icon": "access_time"
|
"icon": "access_time"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Location",
|
"text": I18n.tr("Location", "theme auto mode tab"),
|
||||||
"icon": "place"
|
"icon": "place"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -2340,7 +2328,7 @@ Item {
|
|||||||
tags: ["matugen", "neovim", "terminal", "template"]
|
tags: ["matugen", "neovim", "terminal", "template"]
|
||||||
settingKey: "matugenTemplateNeovim"
|
settingKey: "matugenTemplateNeovim"
|
||||||
text: "neovim"
|
text: "neovim"
|
||||||
description: getTemplateDescription("nvim", "Requires lazy plugin manager")
|
description: getTemplateDescription("nvim", I18n.tr("Requires lazy plugin manager", "neovim template description"))
|
||||||
descriptionColor: getTemplateDescriptionColor("nvim")
|
descriptionColor: getTemplateDescriptionColor("nvim")
|
||||||
visible: SettingsData.runDmsMatugenTemplates
|
visible: SettingsData.runDmsMatugenTemplates
|
||||||
checked: SettingsData.matugenTemplateNeovim
|
checked: SettingsData.matugenTemplateNeovim
|
||||||
@@ -2469,7 +2457,7 @@ Item {
|
|||||||
onValueChanged: value => {
|
onValueChanged: value => {
|
||||||
SettingsData.setIconTheme(value);
|
SettingsData.setIconTheme(value);
|
||||||
if (Quickshell.env("QT_QPA_PLATFORMTHEME") != "gtk3" && Quickshell.env("QT_QPA_PLATFORMTHEME") != "qt6ct" && Quickshell.env("QT_QPA_PLATFORMTHEME_QT6") != "qt6ct") {
|
if (Quickshell.env("QT_QPA_PLATFORMTHEME") != "gtk3" && Quickshell.env("QT_QPA_PLATFORMTHEME") != "qt6ct" && Quickshell.env("QT_QPA_PLATFORMTHEME_QT6") != "qt6ct") {
|
||||||
ToastService.showError("Missing Environment Variables", "You need to set either:\nQT_QPA_PLATFORMTHEME=gtk3 OR\nQT_QPA_PLATFORMTHEME=qt6ct\nas environment variables, and then restart the shell.\n\nqt6ct requires qt6ct-kde to be installed.");
|
ToastService.showError(I18n.tr("Missing Environment Variables", "qt theme env error title"), I18n.tr("You need to set either:\nQT_QPA_PLATFORMTHEME=gtk3 OR\nQT_QPA_PLATFORMTHEME=qt6ct\nas environment variables, and then restart the shell.\n\nqt6ct requires qt6ct-kde to be installed.", "qt theme env error body"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ Item {
|
|||||||
if (!PopoutService.colorPickerModal)
|
if (!PopoutService.colorPickerModal)
|
||||||
return;
|
return;
|
||||||
PopoutService.colorPickerModal.selectedColor = root.currentWallpaper.startsWith("#") ? root.currentWallpaper : Theme.primary;
|
PopoutService.colorPickerModal.selectedColor = root.currentWallpaper.startsWith("#") ? root.currentWallpaper : Theme.primary;
|
||||||
PopoutService.colorPickerModal.pickerTitle = "Choose Wallpaper Color";
|
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Wallpaper Color", "wallpaper color picker title");
|
||||||
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
|
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
|
||||||
if (SessionData.perMonitorWallpaper) {
|
if (SessionData.perMonitorWallpaper) {
|
||||||
SessionData.setMonitorWallpaper(selectedMonitorName, selectedColor);
|
SessionData.setMonitorWallpaper(selectedMonitorName, selectedColor);
|
||||||
@@ -237,7 +237,7 @@ Item {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: root.currentWallpaper ? root.currentWallpaper.split('/').pop() : "No wallpaper selected"
|
text: root.currentWallpaper ? root.currentWallpaper.split('/').pop() : I18n.tr("No wallpaper selected")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
@@ -507,7 +507,7 @@ Item {
|
|||||||
return;
|
return;
|
||||||
var lightWallpaper = SessionData.wallpaperPathLight;
|
var lightWallpaper = SessionData.wallpaperPathLight;
|
||||||
PopoutService.colorPickerModal.selectedColor = lightWallpaper.startsWith("#") ? lightWallpaper : Theme.primary;
|
PopoutService.colorPickerModal.selectedColor = lightWallpaper.startsWith("#") ? lightWallpaper : Theme.primary;
|
||||||
PopoutService.colorPickerModal.pickerTitle = "Choose Light Mode Color";
|
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Light Mode Color", "light mode wallpaper color picker title");
|
||||||
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
|
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
|
||||||
SessionData.wallpaperPathLight = selectedColor;
|
SessionData.wallpaperPathLight = selectedColor;
|
||||||
SessionData.syncWallpaperForCurrentMode();
|
SessionData.syncWallpaperForCurrentMode();
|
||||||
@@ -558,7 +558,7 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
var lightWallpaper = SessionData.wallpaperPathLight;
|
var lightWallpaper = SessionData.wallpaperPathLight;
|
||||||
return lightWallpaper ? lightWallpaper.split('/').pop() : "Not set";
|
return lightWallpaper ? lightWallpaper.split('/').pop() : I18n.tr("Not set", "wallpaper not set label");
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
@@ -691,7 +691,7 @@ Item {
|
|||||||
return;
|
return;
|
||||||
var darkWallpaper = SessionData.wallpaperPathDark;
|
var darkWallpaper = SessionData.wallpaperPathDark;
|
||||||
PopoutService.colorPickerModal.selectedColor = darkWallpaper.startsWith("#") ? darkWallpaper : Theme.primary;
|
PopoutService.colorPickerModal.selectedColor = darkWallpaper.startsWith("#") ? darkWallpaper : Theme.primary;
|
||||||
PopoutService.colorPickerModal.pickerTitle = "Choose Dark Mode Color";
|
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Dark Mode Color", "dark mode wallpaper color picker title");
|
||||||
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
|
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
|
||||||
SessionData.wallpaperPathDark = selectedColor;
|
SessionData.wallpaperPathDark = selectedColor;
|
||||||
SessionData.syncWallpaperForCurrentMode();
|
SessionData.syncWallpaperForCurrentMode();
|
||||||
@@ -742,7 +742,7 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
var darkWallpaper = SessionData.wallpaperPathDark;
|
var darkWallpaper = SessionData.wallpaperPathDark;
|
||||||
return darkWallpaper ? darkWallpaper.split('/').pop() : "Not set";
|
return darkWallpaper ? darkWallpaper.split('/').pop() : I18n.tr("Not set", "wallpaper not set label");
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
@@ -813,7 +813,7 @@ Item {
|
|||||||
return SettingsData.getScreenDisplayName(screens[i]);
|
return SettingsData.getScreenDisplayName(screens[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "No monitors";
|
return I18n.tr("No monitors", "no monitors available label");
|
||||||
}
|
}
|
||||||
options: {
|
options: {
|
||||||
var screenNames = [];
|
var screenNames = [];
|
||||||
@@ -844,7 +844,7 @@ Item {
|
|||||||
currentValue: {
|
currentValue: {
|
||||||
var screens = Quickshell.screens;
|
var screens = Quickshell.screens;
|
||||||
if (!SettingsData.matugenTargetMonitor || SettingsData.matugenTargetMonitor === "") {
|
if (!SettingsData.matugenTargetMonitor || SettingsData.matugenTargetMonitor === "") {
|
||||||
return screens.length > 0 ? SettingsData.getScreenDisplayName(screens[0]) + " (Default)" : "No monitors";
|
return screens.length > 0 ? SettingsData.getScreenDisplayName(screens[0]) + " " + I18n.tr("(Default)", "default monitor label suffix") : I18n.tr("No monitors", "no monitors available label");
|
||||||
}
|
}
|
||||||
for (var i = 0; i < screens.length; i++) {
|
for (var i = 0; i < screens.length; i++) {
|
||||||
if (screens[i].name === SettingsData.matugenTargetMonitor) {
|
if (screens[i].name === SettingsData.matugenTargetMonitor) {
|
||||||
@@ -859,14 +859,14 @@ Item {
|
|||||||
for (var i = 0; i < screens.length; i++) {
|
for (var i = 0; i < screens.length; i++) {
|
||||||
var label = SettingsData.getScreenDisplayName(screens[i]);
|
var label = SettingsData.getScreenDisplayName(screens[i]);
|
||||||
if (i === 0 && (!SettingsData.matugenTargetMonitor || SettingsData.matugenTargetMonitor === "")) {
|
if (i === 0 && (!SettingsData.matugenTargetMonitor || SettingsData.matugenTargetMonitor === "")) {
|
||||||
label += " (Default)";
|
label += " " + I18n.tr("(Default)", "default monitor label suffix");
|
||||||
}
|
}
|
||||||
screenNames.push(label);
|
screenNames.push(label);
|
||||||
}
|
}
|
||||||
return screenNames;
|
return screenNames;
|
||||||
}
|
}
|
||||||
onValueChanged: value => {
|
onValueChanged: value => {
|
||||||
var cleanValue = value.replace(" (Default)", "");
|
var cleanValue = value.replace(" " + I18n.tr("(Default)", "default monitor label suffix"), "");
|
||||||
var screens = Quickshell.screens;
|
var screens = Quickshell.screens;
|
||||||
for (var i = 0; i < screens.length; i++) {
|
for (var i = 0; i < screens.length; i++) {
|
||||||
if (SettingsData.getScreenDisplayName(screens[i]) === cleanValue) {
|
if (SettingsData.getScreenDisplayName(screens[i]) === cleanValue) {
|
||||||
@@ -941,11 +941,11 @@ Item {
|
|||||||
height: 45
|
height: 45
|
||||||
model: [
|
model: [
|
||||||
{
|
{
|
||||||
"text": "Interval",
|
"text": I18n.tr("Interval", "wallpaper cycling mode tab"),
|
||||||
"icon": "schedule"
|
"icon": "schedule"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Time",
|
"text": I18n.tr("Time", "wallpaper cycling mode tab"),
|
||||||
"icon": "access_time"
|
"icon": "access_time"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -981,7 +981,7 @@ Item {
|
|||||||
|
|
||||||
SettingsDropdownRow {
|
SettingsDropdownRow {
|
||||||
id: intervalDropdown
|
id: intervalDropdown
|
||||||
property var intervalOptions: ["5 seconds", "10 seconds", "15 seconds", "20 seconds", "25 seconds", "30 seconds", "35 seconds", "40 seconds", "45 seconds", "50 seconds", "55 seconds", "1 minute", "5 minutes", "15 minutes", "30 minutes", "1 hour", "1.5 hours", "2 hours", "3 hours", "4 hours", "6 hours", "8 hours", "12 hours"]
|
property var intervalOptions: [I18n.tr("5 seconds", "wallpaper interval"), I18n.tr("10 seconds", "wallpaper interval"), I18n.tr("15 seconds", "wallpaper interval"), I18n.tr("20 seconds", "wallpaper interval"), I18n.tr("25 seconds", "wallpaper interval"), I18n.tr("30 seconds", "wallpaper interval"), I18n.tr("35 seconds", "wallpaper interval"), I18n.tr("40 seconds", "wallpaper interval"), I18n.tr("45 seconds", "wallpaper interval"), I18n.tr("50 seconds", "wallpaper interval"), I18n.tr("55 seconds", "wallpaper interval"), I18n.tr("1 minute", "wallpaper interval"), I18n.tr("5 minutes", "wallpaper interval"), I18n.tr("15 minutes", "wallpaper interval"), I18n.tr("30 minutes", "wallpaper interval"), I18n.tr("1 hour", "wallpaper interval"), I18n.tr("1 hour 30 minutes", "wallpaper interval"), I18n.tr("2 hours", "wallpaper interval"), I18n.tr("3 hours", "wallpaper interval"), I18n.tr("4 hours", "wallpaper interval"), I18n.tr("6 hours", "wallpaper interval"), I18n.tr("8 hours", "wallpaper interval"), I18n.tr("12 hours", "wallpaper interval")]
|
||||||
|
|
||||||
property var intervalValues: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 300, 900, 1800, 3600, 5400, 7200, 10800, 14400, 21600, 28800, 43200]
|
property var intervalValues: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 300, 900, 1800, 3600, 5400, 7200, 10800, 14400, 21600, 28800, 43200]
|
||||||
tab: "wallpaper"
|
tab: "wallpaper"
|
||||||
@@ -1005,7 +1005,7 @@ Item {
|
|||||||
currentSeconds = SessionData.wallpaperCyclingInterval;
|
currentSeconds = SessionData.wallpaperCyclingInterval;
|
||||||
}
|
}
|
||||||
const index = intervalValues.indexOf(currentSeconds);
|
const index = intervalValues.indexOf(currentSeconds);
|
||||||
return index >= 0 ? intervalOptions[index] : "5 minutes";
|
return index >= 0 ? intervalOptions[index] : I18n.tr("5 minutes", "wallpaper interval");
|
||||||
}
|
}
|
||||||
onValueChanged: value => {
|
onValueChanged: value => {
|
||||||
const index = intervalOptions.indexOf(value);
|
const index = intervalOptions.indexOf(value);
|
||||||
@@ -1029,7 +1029,7 @@ Item {
|
|||||||
currentSeconds = SessionData.wallpaperCyclingInterval;
|
currentSeconds = SessionData.wallpaperCyclingInterval;
|
||||||
}
|
}
|
||||||
const index = intervalDropdown.intervalValues.indexOf(currentSeconds);
|
const index = intervalDropdown.intervalValues.indexOf(currentSeconds);
|
||||||
intervalDropdown.currentValue = index >= 0 ? intervalDropdown.intervalOptions[index] : "5 minutes";
|
intervalDropdown.currentValue = index >= 0 ? intervalDropdown.intervalOptions[index] : I18n.tr("5 minutes", "wallpaper interval");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -813,7 +813,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 80
|
width: resetContentRow.implicitWidth + Theme.spacingM * 2
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: resetArea.containsMouse ? Theme.surfacePressed : Theme.surfaceVariant
|
color: resetArea.containsMouse ? Theme.surfacePressed : Theme.surfaceVariant
|
||||||
@@ -821,6 +821,7 @@ Item {
|
|||||||
border.width: 0
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
id: resetContentRow
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ Variants {
|
|||||||
Image {
|
Image {
|
||||||
id: nextWallpaper
|
id: nextWallpaper
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: true
|
visible: source !== ""
|
||||||
opacity: 0
|
opacity: 0
|
||||||
layer.enabled: false
|
layer.enabled: false
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
@@ -512,14 +512,18 @@ Variants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiEffect {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: effectLoader.active ? effectLoader.item : currentWallpaper
|
active: CompositorService.isNiri && SettingsData.blurWallpaperOnOverview && NiriService.inOverview && currentWallpaper.source !== ""
|
||||||
visible: CompositorService.isNiri && SettingsData.blurWallpaperOnOverview && NiriService.inOverview && currentWallpaper.source !== ""
|
|
||||||
blurEnabled: true
|
sourceComponent: MultiEffect {
|
||||||
blur: 0.8
|
anchors.fill: parent
|
||||||
blurMax: 75
|
source: effectLoader.active ? effectLoader.item : currentWallpaper
|
||||||
autoPaddingEnabled: false
|
blurEnabled: true
|
||||||
|
blur: 0.8
|
||||||
|
blurMax: 75
|
||||||
|
autoPaddingEnabled: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,9 +288,9 @@ Singleton {
|
|||||||
const cpu = data.cpu;
|
const cpu = data.cpu;
|
||||||
cpuSampleCount++;
|
cpuSampleCount++;
|
||||||
|
|
||||||
cpuUsage = cpu.usage || 0;
|
cpuUsage = Math.round((cpu.usage || 0) * 10) / 10;
|
||||||
cpuFrequency = cpu.frequency || 0;
|
cpuFrequency = Math.round(cpu.frequency || 0);
|
||||||
cpuTemperature = cpu.temperature || 0;
|
cpuTemperature = Math.round(cpu.temperature || 0);
|
||||||
cpuCores = cpu.count || 1;
|
cpuCores = cpu.count || 1;
|
||||||
cpuModel = cpu.model || "";
|
cpuModel = cpu.model || "";
|
||||||
perCoreCpuUsage = cpu.coreUsage || [];
|
perCoreCpuUsage = cpu.coreUsage || [];
|
||||||
@@ -308,11 +308,12 @@ Singleton {
|
|||||||
const freeKB = mem.free || 0;
|
const freeKB = mem.free || 0;
|
||||||
const usedKB = mem.used !== undefined ? mem.used : (totalKB - availableKB);
|
const usedKB = mem.used !== undefined ? mem.used : (totalKB - availableKB);
|
||||||
|
|
||||||
totalMemoryMB = totalKB / 1024;
|
totalMemoryMB = Math.round(totalKB / 1024);
|
||||||
availableMemoryMB = availableKB / 1024;
|
availableMemoryMB = Math.round(availableKB / 1024);
|
||||||
freeMemoryMB = freeKB / 1024;
|
freeMemoryMB = Math.round(freeKB / 1024);
|
||||||
usedMemoryMB = usedKB / 1024;
|
usedMemoryMB = Math.round(usedKB / 1024);
|
||||||
memoryUsage = mem.usedPercent !== undefined ? mem.usedPercent : (totalKB > 0 ? ((totalKB - availableKB) / totalKB) * 100 : 0);
|
const rawMemUsage = mem.usedPercent !== undefined ? mem.usedPercent : (totalKB > 0 ? ((totalKB - availableKB) / totalKB) * 100 : 0);
|
||||||
|
memoryUsage = Math.round(rawMemUsage * 10) / 10;
|
||||||
|
|
||||||
totalMemoryKB = totalKB;
|
totalMemoryKB = totalKB;
|
||||||
usedMemoryKB = usedKB;
|
usedMemoryKB = usedKB;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ Singleton {
|
|||||||
|
|
||||||
function getOutputIdentifier(output, outputName) {
|
function getOutputIdentifier(output, outputName) {
|
||||||
if (SettingsData.displayNameMode === "model" && output.make && output.model)
|
if (SettingsData.displayNameMode === "model" && output.make && output.model)
|
||||||
return "desc:" + output.make + " " + output.model;
|
return "desc:" + output.make + " " + output.model + " " + (output.serial || "Unknown");
|
||||||
return outputName;
|
return outputName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -963,48 +963,34 @@ Singleton {
|
|||||||
return enrichedToplevels;
|
return enrichedToplevels;
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterCurrentWorkspace(toplevels, screenName) {
|
function _matchAndEnrichToplevels(toplevels, niriWindows) {
|
||||||
let currentWorkspaceId = null;
|
|
||||||
|
|
||||||
for (var i = 0; i < allWorkspaces.length; i++) {
|
|
||||||
const ws = allWorkspaces[i];
|
|
||||||
if (ws.output === screenName && ws.is_active) {
|
|
||||||
currentWorkspaceId = ws.id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentWorkspaceId === null)
|
|
||||||
return toplevels;
|
|
||||||
|
|
||||||
const workspaceWindows = windows.filter(niriWindow => niriWindow.workspace_id === currentWorkspaceId);
|
|
||||||
const usedToplevels = new Set();
|
const usedToplevels = new Set();
|
||||||
const result = [];
|
const result = [];
|
||||||
|
|
||||||
for (const niriWindow of workspaceWindows) {
|
for (const niriWindow of niriWindows) {
|
||||||
let bestMatch = null;
|
let bestMatch = null;
|
||||||
let bestScore = -1;
|
let bestScore = -1;
|
||||||
|
|
||||||
for (const toplevel of toplevels) {
|
for (const toplevel of toplevels) {
|
||||||
if (usedToplevels.has(toplevel))
|
if (usedToplevels.has(toplevel))
|
||||||
continue;
|
continue;
|
||||||
if (toplevel.appId === niriWindow.app_id) {
|
if (toplevel.appId !== niriWindow.app_id)
|
||||||
let score = 1;
|
continue;
|
||||||
|
|
||||||
if (niriWindow.title && toplevel.title) {
|
let score = 1;
|
||||||
if (toplevel.title === niriWindow.title) {
|
if (niriWindow.title && toplevel.title) {
|
||||||
score = 3;
|
if (toplevel.title === niriWindow.title) {
|
||||||
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
|
score = 3;
|
||||||
score = 2;
|
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
|
||||||
}
|
score = 2;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (score > bestScore) {
|
if (score > bestScore) {
|
||||||
bestScore = score;
|
bestScore = score;
|
||||||
bestMatch = toplevel;
|
bestMatch = toplevel;
|
||||||
if (score === 3)
|
if (score === 3)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1025,17 +1011,15 @@ Singleton {
|
|||||||
return NiriService.focusWindow(niriWindow.id);
|
return NiriService.focusWindow(niriWindow.id);
|
||||||
},
|
},
|
||||||
"close": function () {
|
"close": function () {
|
||||||
if (bestMatch.close) {
|
if (bestMatch.close)
|
||||||
return bestMatch.close();
|
return bestMatch.close();
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let prop in bestMatch) {
|
for (let prop in bestMatch) {
|
||||||
if (!(prop in enrichedToplevel)) {
|
if (!(prop in enrichedToplevel))
|
||||||
enrichedToplevel[prop] = bestMatch[prop];
|
enrichedToplevel[prop] = bestMatch[prop];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push(enrichedToplevel);
|
result.push(enrichedToplevel);
|
||||||
@@ -1044,6 +1028,26 @@ Singleton {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterCurrentWorkspace(toplevels, screenName) {
|
||||||
|
let currentWorkspaceId = null;
|
||||||
|
|
||||||
|
for (var i = 0; i < allWorkspaces.length; i++) {
|
||||||
|
const ws = allWorkspaces[i];
|
||||||
|
if (ws.output === screenName && ws.is_active) {
|
||||||
|
currentWorkspaceId = ws.id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentWorkspaceId === null)
|
||||||
|
return toplevels;
|
||||||
|
|
||||||
|
if (toplevels.length > 0 && toplevels[0].niriWorkspaceId !== undefined)
|
||||||
|
return toplevels.filter(t => t.niriWorkspaceId === currentWorkspaceId);
|
||||||
|
|
||||||
|
return _matchAndEnrichToplevels(toplevels, windows.filter(nw => nw.workspace_id === currentWorkspaceId));
|
||||||
|
}
|
||||||
|
|
||||||
function filterCurrentDisplay(toplevels, screenName) {
|
function filterCurrentDisplay(toplevels, screenName) {
|
||||||
if (!toplevels || toplevels.length === 0 || !screenName)
|
if (!toplevels || toplevels.length === 0 || !screenName)
|
||||||
return toplevels;
|
return toplevels;
|
||||||
@@ -1058,71 +1062,10 @@ Singleton {
|
|||||||
if (outputWorkspaceIds.size === 0)
|
if (outputWorkspaceIds.size === 0)
|
||||||
return toplevels;
|
return toplevels;
|
||||||
|
|
||||||
const displayWindows = windows.filter(niriWindow => outputWorkspaceIds.has(niriWindow.workspace_id));
|
if (toplevels.length > 0 && toplevels[0].niriWorkspaceId !== undefined)
|
||||||
const usedToplevels = new Set();
|
return toplevels.filter(t => outputWorkspaceIds.has(t.niriWorkspaceId));
|
||||||
const result = [];
|
|
||||||
|
|
||||||
for (const niriWindow of displayWindows) {
|
return _matchAndEnrichToplevels(toplevels, windows.filter(nw => outputWorkspaceIds.has(nw.workspace_id)));
|
||||||
let bestMatch = null;
|
|
||||||
let bestScore = -1;
|
|
||||||
|
|
||||||
for (const toplevel of toplevels) {
|
|
||||||
if (usedToplevels.has(toplevel))
|
|
||||||
continue;
|
|
||||||
if (toplevel.appId === niriWindow.app_id) {
|
|
||||||
let score = 1;
|
|
||||||
|
|
||||||
if (niriWindow.title && toplevel.title) {
|
|
||||||
if (toplevel.title === niriWindow.title) {
|
|
||||||
score = 3;
|
|
||||||
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
|
|
||||||
score = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (score > bestScore) {
|
|
||||||
bestScore = score;
|
|
||||||
bestMatch = toplevel;
|
|
||||||
if (score === 3)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bestMatch)
|
|
||||||
continue;
|
|
||||||
usedToplevels.add(bestMatch);
|
|
||||||
|
|
||||||
const workspace = workspaces[niriWindow.workspace_id];
|
|
||||||
const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false;
|
|
||||||
|
|
||||||
const enrichedToplevel = {
|
|
||||||
"appId": bestMatch.appId,
|
|
||||||
"title": bestMatch.title,
|
|
||||||
"activated": isFocused,
|
|
||||||
"niriWindowId": niriWindow.id,
|
|
||||||
"niriWorkspaceId": niriWindow.workspace_id,
|
|
||||||
"activate": function () {
|
|
||||||
return NiriService.focusWindow(niriWindow.id);
|
|
||||||
},
|
|
||||||
"close": function () {
|
|
||||||
if (bestMatch.close) {
|
|
||||||
return bestMatch.close();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let prop in bestMatch) {
|
|
||||||
if (!(prop in enrichedToplevel)) {
|
|
||||||
enrichedToplevel[prop] = bestMatch[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push(enrichedToplevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateNiriLayoutConfig() {
|
function generateNiriLayoutConfig() {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ Singleton {
|
|||||||
property var registeredCards: ({})
|
property var registeredCards: ({})
|
||||||
property var settingsIndex: []
|
property var settingsIndex: []
|
||||||
property bool indexLoaded: false
|
property bool indexLoaded: false
|
||||||
|
property var _translatedCache: []
|
||||||
|
|
||||||
readonly property var conditionMap: ({
|
readonly property var conditionMap: ({
|
||||||
"isNiri": () => CompositorService.isNiri,
|
"isNiri": () => CompositorService.isNiri,
|
||||||
@@ -38,9 +39,11 @@ Singleton {
|
|||||||
try {
|
try {
|
||||||
root.settingsIndex = JSON.parse(text());
|
root.settingsIndex = JSON.parse(text());
|
||||||
root.indexLoaded = true;
|
root.indexLoaded = true;
|
||||||
|
root._rebuildTranslationCache();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("SettingsSearchService: Failed to parse index:", e);
|
console.warn("SettingsSearchService: Failed to parse index:", e);
|
||||||
root.settingsIndex = [];
|
root.settingsIndex = [];
|
||||||
|
root._translatedCache = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onLoadFailed: error => console.warn("SettingsSearchService: Failed to load index:", error)
|
onLoadFailed: error => console.warn("SettingsSearchService: Failed to load index:", error)
|
||||||
@@ -131,6 +134,27 @@ Singleton {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _rebuildTranslationCache() {
|
||||||
|
var cache = [];
|
||||||
|
for (var i = 0; i < settingsIndex.length; i++) {
|
||||||
|
var item = settingsIndex[i];
|
||||||
|
var t = translateItem(item);
|
||||||
|
cache.push({
|
||||||
|
section: t.section,
|
||||||
|
label: t.label,
|
||||||
|
tabIndex: t.tabIndex,
|
||||||
|
category: t.category,
|
||||||
|
keywords: t.keywords,
|
||||||
|
icon: t.icon,
|
||||||
|
description: t.description,
|
||||||
|
conditionKey: t.conditionKey,
|
||||||
|
labelLower: t.label.toLowerCase(),
|
||||||
|
categoryLower: t.category.toLowerCase()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_translatedCache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
function search(text) {
|
function search(text) {
|
||||||
query = text;
|
query = text;
|
||||||
if (!text) {
|
if (!text) {
|
||||||
@@ -138,18 +162,19 @@ Singleton {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryLower = text.toLowerCase().trim();
|
var queryLower = text.toLowerCase().trim();
|
||||||
const queryWords = queryLower.split(/\s+/).filter(w => w.length > 0);
|
var queryWords = queryLower.split(/\s+/).filter(w => w.length > 0);
|
||||||
const scored = [];
|
var scored = [];
|
||||||
|
var cache = _translatedCache;
|
||||||
|
|
||||||
for (const item of settingsIndex) {
|
for (var i = 0; i < cache.length; i++) {
|
||||||
if (!checkCondition(item))
|
var entry = cache[i];
|
||||||
|
if (!checkCondition(entry))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const translated = translateItem(item);
|
var labelLower = entry.labelLower;
|
||||||
const labelLower = translated.label.toLowerCase();
|
var categoryLower = entry.categoryLower;
|
||||||
const categoryLower = translated.category.toLowerCase();
|
var score = 0;
|
||||||
let score = 0;
|
|
||||||
|
|
||||||
if (labelLower === queryLower) {
|
if (labelLower === queryLower) {
|
||||||
score = 10000;
|
score = 10000;
|
||||||
@@ -162,24 +187,32 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (score === 0) {
|
if (score === 0) {
|
||||||
for (const keyword of item.keywords) {
|
var keywords = entry.keywords;
|
||||||
if (keyword.startsWith(queryLower)) {
|
for (var k = 0; k < keywords.length; k++) {
|
||||||
score = Math.max(score, 800);
|
if (keywords[k].startsWith(queryLower)) {
|
||||||
|
score = 800;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (keyword.includes(queryLower)) {
|
if (keywords[k].includes(queryLower) && score < 400) {
|
||||||
score = Math.max(score, 400);
|
score = 400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (score === 0 && queryWords.length > 1) {
|
if (score === 0 && queryWords.length > 1) {
|
||||||
let allMatch = true;
|
var allMatch = true;
|
||||||
for (const word of queryWords) {
|
for (var w = 0; w < queryWords.length; w++) {
|
||||||
const inLabel = labelLower.includes(word);
|
var word = queryWords[w];
|
||||||
const inKeywords = item.keywords.some(k => k.includes(word));
|
if (labelLower.includes(word))
|
||||||
const inCategory = categoryLower.includes(word);
|
continue;
|
||||||
if (!inLabel && !inKeywords && !inCategory) {
|
var inKeywords = false;
|
||||||
|
for (var k = 0; k < entry.keywords.length; k++) {
|
||||||
|
if (entry.keywords[k].includes(word)) {
|
||||||
|
inKeywords = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!inKeywords && !categoryLower.includes(word)) {
|
||||||
allMatch = false;
|
allMatch = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -190,7 +223,7 @@ Singleton {
|
|||||||
|
|
||||||
if (score > 0) {
|
if (score > 0) {
|
||||||
scored.push({
|
scored.push({
|
||||||
item: translated,
|
item: entry,
|
||||||
score: score
|
score: score
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,36 +119,36 @@ Singleton {
|
|||||||
|
|
||||||
function getWeatherCondition(code) {
|
function getWeatherCondition(code) {
|
||||||
const conditions = {
|
const conditions = {
|
||||||
"0": "Clear",
|
"0": I18n.tr("Clear Sky"),
|
||||||
"1": "Clear",
|
"1": I18n.tr("Clear Sky"),
|
||||||
"2": "Partly cloudy",
|
"2": I18n.tr("Partly Cloudy"),
|
||||||
"3": "Overcast",
|
"3": I18n.tr("Overcast"),
|
||||||
"45": "Fog",
|
"45": I18n.tr("Fog"),
|
||||||
"48": "Fog",
|
"48": I18n.tr("Fog"),
|
||||||
"51": "Drizzle",
|
"51": I18n.tr("Drizzle"),
|
||||||
"53": "Drizzle",
|
"53": I18n.tr("Drizzle"),
|
||||||
"55": "Drizzle",
|
"55": I18n.tr("Drizzle"),
|
||||||
"56": "Freezing drizzle",
|
"56": I18n.tr("Freezing Drizzle"),
|
||||||
"57": "Freezing drizzle",
|
"57": I18n.tr("Freezing Drizzle"),
|
||||||
"61": "Light rain",
|
"61": I18n.tr("Light Rain"),
|
||||||
"63": "Rain",
|
"63": I18n.tr("Rain"),
|
||||||
"65": "Heavy rain",
|
"65": I18n.tr("Heavy Rain"),
|
||||||
"66": "Light rain",
|
"66": I18n.tr("Light Rain"),
|
||||||
"67": "Heavy rain",
|
"67": I18n.tr("Heavy Rain"),
|
||||||
"71": "Light snow",
|
"71": I18n.tr("Light Snow"),
|
||||||
"73": "Snow",
|
"73": I18n.tr("Snow"),
|
||||||
"75": "Heavy snow",
|
"75": I18n.tr("Heavy Snow"),
|
||||||
"77": "Snow",
|
"77": I18n.tr("Snow"),
|
||||||
"80": "Light rain",
|
"80": I18n.tr("Light Rain"),
|
||||||
"81": "Rain",
|
"81": I18n.tr("Rain"),
|
||||||
"82": "Heavy rain",
|
"82": I18n.tr("Heavy Rain"),
|
||||||
"85": "Light snow showers",
|
"85": I18n.tr("Light Snow Showers"),
|
||||||
"86": "Heavy snow showers",
|
"86": I18n.tr("Heavy Snow Showers"),
|
||||||
"95": "Thunderstorm",
|
"95": I18n.tr("Thunderstorm"),
|
||||||
"96": "Thunderstorm with hail",
|
"96": I18n.tr("Thunderstorm with Hail"),
|
||||||
"99": "Thunderstorm with hail"
|
"99": I18n.tr("Thunderstorm with Hail")
|
||||||
};
|
};
|
||||||
return conditions[String(code)] || "Unknown";
|
return conditions[String(code)] || I18n.tr("Unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
property var moonPhaseNames: ["moon_new", "moon_waxing_crescent", "moon_first_quarter", "moon_waxing_gibbous", "moon_full", "moon_waning_gibbous", "moon_last_quarter", "moon_waning_crescent"]
|
property var moonPhaseNames: ["moon_new", "moon_waxing_crescent", "moon_first_quarter", "moon_waxing_gibbous", "moon_full", "moon_waning_gibbous", "moon_last_quarter", "moon_waning_crescent"]
|
||||||
@@ -647,8 +647,8 @@ Singleton {
|
|||||||
const address = data.address || {};
|
const address = data.address || {};
|
||||||
|
|
||||||
root.location = {
|
root.location = {
|
||||||
city: address.hamlet || address.city || address.town || address.village || "Unknown",
|
city: address.hamlet || address.city || address.town || address.village || I18n.tr("Unknown"),
|
||||||
country: address.country || "Unknown",
|
country: address.country || I18n.tr("Unknown"),
|
||||||
latitude: parseFloat(data.lat),
|
latitude: parseFloat(data.lat),
|
||||||
longitude: parseFloat(data.lon)
|
longitude: parseFloat(data.lon)
|
||||||
};
|
};
|
||||||
@@ -807,8 +807,8 @@ Singleton {
|
|||||||
"tempF": Math.round(tempF),
|
"tempF": Math.round(tempF),
|
||||||
"feelsLike": Math.round(feelsLikeC),
|
"feelsLike": Math.round(feelsLikeC),
|
||||||
"feelsLikeF": Math.round(feelsLikeF),
|
"feelsLikeF": Math.round(feelsLikeF),
|
||||||
"city": root.location?.city || "Unknown",
|
"city": root.location?.city || I18n.tr("Unknown"),
|
||||||
"country": root.location?.country || "Unknown",
|
"country": root.location?.country || I18n.tr("Unknown"),
|
||||||
"wCode": current.weather_code || 0,
|
"wCode": current.weather_code || 0,
|
||||||
"humidity": Math.round(current.relative_humidity_2m || 0),
|
"humidity": Math.round(current.relative_humidity_2m || 0),
|
||||||
"wind": Math.round(current.wind_speed_10m || 0),
|
"wind": Math.round(current.wind_speed_10m || 0),
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v1.4.0
|
v1.4.2
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ PanelWindow {
|
|||||||
function show() {
|
function show() {
|
||||||
if (SessionData.suppressOSD)
|
if (SessionData.suppressOSD)
|
||||||
return;
|
return;
|
||||||
|
if (shouldBeVisible) {
|
||||||
|
hideTimer.restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
OSDManager.showOSD(root);
|
OSDManager.showOSD(root);
|
||||||
closeTimer.stop();
|
closeTimer.stop();
|
||||||
shouldBeVisible = true;
|
shouldBeVisible = true;
|
||||||
@@ -257,7 +261,7 @@ PanelWindow {
|
|||||||
property real shadowSpreadPx: 0
|
property real shadowSpreadPx: 0
|
||||||
property real shadowBaseAlpha: 0.60
|
property real shadowBaseAlpha: 0.60
|
||||||
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
|
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
|
||||||
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha * osdContainer.opacity))
|
readonly property real effectiveShadowAlpha: shouldBeVisible ? Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) : 0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: background
|
id: background
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ Item {
|
|||||||
property var customKeyboardFocus: null
|
property var customKeyboardFocus: null
|
||||||
property bool backgroundInteractive: true
|
property bool backgroundInteractive: true
|
||||||
property bool contentHandlesKeys: false
|
property bool contentHandlesKeys: false
|
||||||
|
property bool fullHeightSurface: false
|
||||||
property bool _resizeActive: false
|
property bool _resizeActive: false
|
||||||
|
property real _surfaceMarginLeft: 0
|
||||||
|
property real _surfaceW: 0
|
||||||
|
|
||||||
property real storedBarThickness: Theme.barHeight - 4
|
property real storedBarThickness: Theme.barHeight - 4
|
||||||
property real storedBarSpacing: 4
|
property real storedBarSpacing: 4
|
||||||
@@ -106,6 +109,13 @@ Item {
|
|||||||
|
|
||||||
readonly property bool useBackgroundWindow: !CompositorService.isHyprland || CompositorService.useHyprlandFocusGrab
|
readonly property bool useBackgroundWindow: !CompositorService.isHyprland || CompositorService.useHyprlandFocusGrab
|
||||||
|
|
||||||
|
function updateSurfacePosition() {
|
||||||
|
if (useBackgroundWindow && shouldBeVisible) {
|
||||||
|
_surfaceMarginLeft = alignedX - shadowBuffer;
|
||||||
|
_surfaceW = alignedWidth + shadowBuffer * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function open() {
|
function open() {
|
||||||
if (!screen)
|
if (!screen)
|
||||||
return;
|
return;
|
||||||
@@ -125,6 +135,10 @@ Item {
|
|||||||
_lastOpenedScreen = screen;
|
_lastOpenedScreen = screen;
|
||||||
|
|
||||||
shouldBeVisible = true;
|
shouldBeVisible = true;
|
||||||
|
if (useBackgroundWindow) {
|
||||||
|
_surfaceMarginLeft = alignedX - shadowBuffer;
|
||||||
|
_surfaceW = alignedWidth + shadowBuffer * 2;
|
||||||
|
}
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (shouldBeVisible && screen) {
|
if (shouldBeVisible && screen) {
|
||||||
if (useBackgroundWindow)
|
if (useBackgroundWindow)
|
||||||
@@ -360,20 +374,38 @@ Item {
|
|||||||
return WlrKeyboardFocus.Exclusive;
|
return WlrKeyboardFocus.Exclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property bool _fullHeight: useBackgroundWindow && root.fullHeightSurface
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: true
|
left: true
|
||||||
top: true
|
top: true
|
||||||
right: !useBackgroundWindow
|
right: !useBackgroundWindow
|
||||||
bottom: !useBackgroundWindow
|
bottom: _fullHeight || !useBackgroundWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.margins {
|
WlrLayershell.margins {
|
||||||
left: useBackgroundWindow ? (root.alignedX - shadowBuffer) : 0
|
left: useBackgroundWindow ? root._surfaceMarginLeft : 0
|
||||||
top: useBackgroundWindow ? (root.alignedY - shadowBuffer) : 0
|
top: (useBackgroundWindow && !_fullHeight) ? (root.alignedY - shadowBuffer) : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitWidth: useBackgroundWindow ? (root.alignedWidth + (shadowBuffer * 2)) : 0
|
implicitWidth: useBackgroundWindow ? root._surfaceW : 0
|
||||||
implicitHeight: useBackgroundWindow ? (root.alignedHeight + (shadowBuffer * 2)) : 0
|
implicitHeight: (useBackgroundWindow && !_fullHeight) ? (root.alignedHeight + shadowBuffer * 2) : 0
|
||||||
|
|
||||||
|
mask: (useBackgroundWindow && _fullHeight) ? contentInputMask : null
|
||||||
|
|
||||||
|
Region {
|
||||||
|
id: contentInputMask
|
||||||
|
item: contentMaskRect
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentMaskRect
|
||||||
|
visible: false
|
||||||
|
x: contentContainer.x - root.shadowBuffer
|
||||||
|
y: contentContainer.y - root.shadowBuffer
|
||||||
|
width: root.alignedWidth + root.shadowBuffer * 2
|
||||||
|
height: root.alignedHeight + root.shadowBuffer * 2
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -393,7 +425,7 @@ Item {
|
|||||||
Item {
|
Item {
|
||||||
id: contentContainer
|
id: contentContainer
|
||||||
x: useBackgroundWindow ? shadowBuffer : root.alignedX
|
x: useBackgroundWindow ? shadowBuffer : root.alignedX
|
||||||
y: useBackgroundWindow ? shadowBuffer : root.alignedY
|
y: (useBackgroundWindow && !contentWindow._fullHeight) ? shadowBuffer : root.alignedY
|
||||||
width: root.alignedWidth
|
width: root.alignedWidth
|
||||||
height: root.alignedHeight
|
height: root.alignedHeight
|
||||||
|
|
||||||
@@ -444,6 +476,44 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: shadowSource
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "black"
|
||||||
|
visible: false
|
||||||
|
opacity: contentWrapper.opacity
|
||||||
|
scale: contentWrapper.scale
|
||||||
|
x: contentWrapper.x
|
||||||
|
y: contentWrapper.y
|
||||||
|
|
||||||
|
property real shadowBlurPx: 10
|
||||||
|
property real shadowSpreadPx: 0
|
||||||
|
property real shadowBaseAlpha: 0.60
|
||||||
|
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
|
||||||
|
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
|
||||||
|
readonly property int blurMax: 64
|
||||||
|
|
||||||
|
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive)
|
||||||
|
layer.smooth: false
|
||||||
|
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
id: shadowFx
|
||||||
|
autoPaddingEnabled: true
|
||||||
|
shadowEnabled: true
|
||||||
|
blurEnabled: false
|
||||||
|
maskEnabled: false
|
||||||
|
shadowBlur: Math.max(0, Math.min(1, shadowSource.shadowBlurPx / shadowSource.blurMax))
|
||||||
|
shadowScale: 1 + (2 * shadowSource.shadowSpreadPx) / Math.max(1, Math.min(shadowSource.width, shadowSource.height))
|
||||||
|
shadowColor: {
|
||||||
|
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
||||||
|
return Theme.withAlpha(baseColor, shadowSource.effectiveShadowAlpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: contentWrapper
|
id: contentWrapper
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -455,31 +525,10 @@ Item {
|
|||||||
x: Theme.snap(contentContainer.animX + (parent.width - width) * (1 - contentContainer.scaleValue) * 0.5, root.dpr)
|
x: Theme.snap(contentContainer.animX + (parent.width - width) * (1 - contentContainer.scaleValue) * 0.5, root.dpr)
|
||||||
y: Theme.snap(contentContainer.animY + (parent.height - height) * (1 - contentContainer.scaleValue) * 0.5, root.dpr)
|
y: Theme.snap(contentContainer.animY + (parent.height - height) * (1 - contentContainer.scaleValue) * 0.5, root.dpr)
|
||||||
|
|
||||||
property real shadowBlurPx: 10
|
layer.enabled: contentWrapper.opacity < 1
|
||||||
property real shadowSpreadPx: 0
|
|
||||||
property real shadowBaseAlpha: 0.60
|
|
||||||
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
|
|
||||||
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
|
|
||||||
readonly property int blurMax: 64
|
|
||||||
|
|
||||||
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive)
|
|
||||||
layer.smooth: false
|
layer.smooth: false
|
||||||
layer.textureSize: root.dpr > 1 ? Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr)) : Qt.size(0, 0)
|
layer.textureSize: root.dpr > 1 ? Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr)) : Qt.size(0, 0)
|
||||||
|
|
||||||
layer.effect: MultiEffect {
|
|
||||||
id: shadowFx
|
|
||||||
autoPaddingEnabled: true
|
|
||||||
shadowEnabled: true
|
|
||||||
blurEnabled: false
|
|
||||||
maskEnabled: false
|
|
||||||
shadowBlur: Math.max(0, Math.min(1, contentWrapper.shadowBlurPx / contentWrapper.blurMax))
|
|
||||||
shadowScale: 1 + (2 * contentWrapper.shadowSpreadPx) / Math.max(1, Math.min(contentWrapper.width, contentWrapper.height))
|
|
||||||
shadowColor: {
|
|
||||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
|
||||||
return Theme.withAlpha(baseColor, contentWrapper.effectiveShadowAlpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: animationDuration
|
duration: animationDuration
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
[templates.dmsemacs]
|
[templates.dmsemacs]
|
||||||
input_path = 'SHELL_DIR/matugen/templates/dank-emacs.el'
|
input_path = 'SHELL_DIR/matugen/templates/dank-emacs.el'
|
||||||
output_path = "~/.emacs.d/themes/dank-emacs-theme.el"
|
output_path = 'EMACS_DIR/themes/dank-emacs-theme.el'
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user