1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-15 08:42:47 -04:00

Compare commits

...

9 Commits

Author SHA1 Message Date
purian23 7f15227de1 Reduce dups & add workflow hotfix 2025-11-24 13:58:22 -05:00
purian23 bb45240665 Further optimize OBS build scripts 2025-11-24 13:10:16 -05:00
bbedward 29f84aeab5 dankbar: fix monitoring widgets with no background option
fixes #806
2025-11-24 12:26:29 -05:00
bbedward 5a52edcad8 ws: add option for occupied only 2025-11-24 12:03:34 -05:00
bbedward b078e23aa1 settings: fix scrollable area in window 2025-11-24 11:56:10 -05:00
bbedward 7fa87125b5 audio: optimize visualizations 2025-11-24 11:37:24 -05:00
bbedward f618df46d8 audio: optimize non-cava fallback 2025-11-24 11:08:03 -05:00
bbedward ee03853901 idle: add fade to lock option
fixes #694
fixes #805
2025-11-24 10:59:36 -05:00
bbedward 6c4a9bcfb8 modals: restore Top layer as default
- Cut a mask in the background window
- restores virt kb compat
2025-11-24 09:38:03 -05:00
39 changed files with 2410 additions and 2003 deletions
+115 -3
View File
@@ -18,9 +18,117 @@ on:
- cron: '0 */3 * * *' # Every 3 hours for dms-git builds
jobs:
update-obs:
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 latest commit hash from master branch
LATEST_COMMIT=$(git rev-parse origin/master 2>/dev/null || git rev-parse master 2>/dev/null || echo "")
if [[ -z "$LATEST_COMMIT" ]]; then
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "Could not determine git commit, proceeding with update"
else
# 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
# Check tarball age - if older than 3 hours, update needed
if [[ -f "dms-git-source.tar.gz" ]]; then
TARBALL_MTIME=$(stat -c%Y "dms-git-source.tar.gz" 2>/dev/null || echo "0")
CURRENT_TIME=$(date +%s)
AGE_SECONDS=$((CURRENT_TIME - TARBALL_MTIME))
AGE_HOURS=$((AGE_SECONDS / 3600))
# If tarball is older than 3 hours, check for new commits
if [[ $AGE_HOURS -ge 3 ]]; then
# Check if there are new commits in the last 3 hours
cd "${{ github.workspace }}"
NEW_COMMITS=$(git log --since="3 hours ago" --oneline origin/master 2>/dev/null | wc -l)
if [[ $NEW_COMMITS -gt 0 ]]; then
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 New commits detected in last 3 hours, update needed"
else
echo "has_updates=false" >> $GITHUB_OUTPUT
echo "📋 No new commits in last 3 hours, skipping update"
fi
else
echo "has_updates=false" >> $GITHUB_OUTPUT
echo "📋 Recent upload exists (< 3 hours), skipping update"
fi
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 No existing tarball in OBS, update needed"
fi
cd "${{ github.workspace }}"
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 First upload to OBS, update needed"
fi
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
if: |
github.event_name == 'workflow_dispatch' ||
needs.check-updates.outputs.has_updates == 'true'
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -36,13 +144,13 @@ jobs:
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Triggered by tag: $VERSION"
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "packages=dms-git" >> $GITHUB_OUTPUT
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=all" >> $GITHUB_OUTPUT
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
fi
- name: Update version in packaging files
@@ -85,6 +193,7 @@ jobs:
- name: Upload to OBS
env:
FORCE_REBUILD: ${{ github.event_name == 'workflow_dispatch' && 'true' || '' }}
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
run: |
PACKAGES="${{ steps.packages.outputs.packages }}"
@@ -111,4 +220,7 @@ jobs:
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
+7
View File
@@ -39,6 +39,9 @@ override_dh_auto_build:
elif [ -f dms-source.tar.gz ]; then \
tar -xzf dms-source.tar.gz; \
fi; \
if [ ! -d DankMaterialShell-$(UPSTREAM_VERSION) ] && [ -d DankMaterialShell-0.6.2 ]; then \
mv DankMaterialShell-0.6.2 DankMaterialShell-$(UPSTREAM_VERSION); \
fi; \
fi
@@ -46,6 +49,10 @@ override_dh_auto_install:
install -Dm755 dms debian/dms/usr/bin/dms
mkdir -p debian/dms/usr/share/quickshell/dms debian/dms/usr/lib/systemd/user
# Handle directory name mismatch again for install step if needed
if [ ! -d DankMaterialShell-$(UPSTREAM_VERSION) ] && [ -d DankMaterialShell-0.6.2 ]; then \
mv DankMaterialShell-0.6.2 DankMaterialShell-$(UPSTREAM_VERSION); \
fi
if [ -d DankMaterialShell-$(UPSTREAM_VERSION) ]; then \
cp -r DankMaterialShell-$(UPSTREAM_VERSION)/quickshell/* debian/dms/usr/share/quickshell/dms/; \
install -Dm644 DankMaterialShell-$(UPSTREAM_VERSION)/quickshell/assets/systemd/dms.service debian/dms/usr/lib/systemd/user/dms.service; \
+364 -229
View File
@@ -37,8 +37,6 @@ done
OBS_BASE_PROJECT="home:AvengeMedia"
OBS_BASE="$HOME/.cache/osc-checkouts"
# Available packages
AVAILABLE_PACKAGES=(dms dms-git)
if [[ -z "$PACKAGE" ]]; then
@@ -65,11 +63,9 @@ if [[ -z "$MESSAGE" ]]; then
MESSAGE="Update packaging"
fi
# Get repo root (2 levels up from distro/scripts/)
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$REPO_ROOT"
# Ensure we're in repo root
if [[ ! -d "distro/debian" ]]; then
echo "Error: Run this script from the repository root"
exit 1
@@ -143,7 +139,6 @@ esac
OBS_PROJECT="${OBS_BASE_PROJECT}:${PROJECT}"
echo "==> Target: $OBS_PROJECT / $PACKAGE"
echo "==> Message: $MESSAGE"
if [[ "$UPLOAD_DEBIAN" == true && "$UPLOAD_OPENSUSE" == true ]]; then
echo "==> Distributions: Debian + OpenSUSE"
elif [[ "$UPLOAD_DEBIAN" == true ]]; then
@@ -152,10 +147,8 @@ elif [[ "$UPLOAD_OPENSUSE" == true ]]; then
echo "==> Distribution: OpenSUSE only"
fi
# Create .obs directory if it doesn't exist
mkdir -p "$OBS_BASE"
# Check out package if not already present
if [[ ! -d "$OBS_BASE/$OBS_PROJECT/$PACKAGE" ]]; then
echo "Checking out $OBS_PROJECT/$PACKAGE..."
cd "$OBS_BASE"
@@ -167,29 +160,35 @@ WORK_DIR="$OBS_BASE/$OBS_PROJECT/$PACKAGE"
echo "==> Preparing $PACKAGE for OBS upload"
# Clean working directory (keep osc metadata)
find "$WORK_DIR" -maxdepth 1 -type f \( -name "*.tar.gz" -o -name "*.spec" -o -name "_service" -o -name "*.dsc" \) -delete 2>/dev/null || true
find "$WORK_DIR" -maxdepth 1 -type f \( -name "*.tar.gz" -o -name "*.tar.xz" -o -name "*.tar.bz2" -o -name "*.tar" -o -name "*.spec" -o -name "_service" -o -name "*.dsc" \) -delete 2>/dev/null || true
if [[ -f "distro/debian/$PACKAGE/_service" ]]; then
echo " - Copying _service (for binary downloads)"
cp "distro/debian/$PACKAGE/_service" "$WORK_DIR/"
fi
# Copy OpenSUSE spec if it exists and handle auto-increment
CHANGELOG_VERSION=""
if [[ -d "distro/debian/$PACKAGE/debian" ]]; then
CHANGELOG_VERSION=$(grep -m1 "^$PACKAGE" "distro/debian/$PACKAGE/debian/changelog" 2>/dev/null | sed 's/.*(\([^)]*\)).*/\1/' || echo "")
if [[ -n "$CHANGELOG_VERSION" ]] && [[ "$CHANGELOG_VERSION" == *"-"* ]]; then
SOURCE_FORMAT_CHECK=$(cat "distro/debian/$PACKAGE/debian/source/format" 2>/dev/null || echo "3.0 (quilt)")
if [[ "$SOURCE_FORMAT_CHECK" == *"native"* ]]; then
CHANGELOG_VERSION=$(echo "$CHANGELOG_VERSION" | sed 's/-[0-9]*$//')
fi
fi
fi
if [[ "$UPLOAD_OPENSUSE" == true ]] && [[ -f "distro/opensuse/$PACKAGE.spec" ]]; then
echo " - Copying $PACKAGE.spec for OpenSUSE"
cp "distro/opensuse/$PACKAGE.spec" "$WORK_DIR/"
# Auto-increment Release if same Version is being rebuilt
if [[ -f "$WORK_DIR/.osc/$PACKAGE.spec" ]]; then
NEW_VERSION=$(grep "^Version:" "$WORK_DIR/$PACKAGE.spec" | awk '{print $2}' | head -1)
NEW_RELEASE=$(grep "^Release:" "$WORK_DIR/$PACKAGE.spec" | sed 's/^Release:[[:space:]]*//' | sed 's/%{?dist}//' | head -1)
OLD_VERSION=$(grep "^Version:" "$WORK_DIR/.osc/$PACKAGE.spec" | awk '{print $2}' | head -1)
OLD_RELEASE=$(grep "^Release:" "$WORK_DIR/.osc/$PACKAGE.spec" | sed 's/^Release:[[:space:]]*//' | sed 's/%{?dist}//' | head -1)
if [[ "$NEW_VERSION" == "$OLD_VERSION" ]]; then
# Same version - increment release number
if [[ "$OLD_RELEASE" =~ ^([0-9]+) ]]; then
BASE_RELEASE="${BASH_REMATCH[1]}"
NEXT_RELEASE=$((BASE_RELEASE + 1))
@@ -206,38 +205,33 @@ elif [[ "$UPLOAD_OPENSUSE" == true ]]; then
echo " - Warning: OpenSUSE spec file not found, skipping OpenSUSE upload"
fi
# Handle OpenSUSE-only uploads (create tarball without Debian processing)
if [[ "$UPLOAD_OPENSUSE" == true ]] && [[ "$UPLOAD_DEBIAN" == false ]] && [[ -f "distro/opensuse/$PACKAGE.spec" ]]; then
echo " - OpenSUSE-only upload: creating source tarball"
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
# Check _service file to determine how to get source
if [[ -f "distro/debian/$PACKAGE/_service" ]]; then
# Check for tar_scm (git source)
if grep -q "tar_scm" "distro/debian/$PACKAGE/_service"; then
GIT_URL=$(grep -A 5 'name="tar_scm"' "distro/debian/$PACKAGE/_service" | grep "url" | sed 's/.*<param name="url">\(.*\)<\/param>.*/\1/')
GIT_REVISION=$(grep -A 5 'name="tar_scm"' "distro/debian/$PACKAGE/_service" | grep "revision" | sed 's/.*<param name="revision">\(.*\)<\/param>.*/\1/')
if [[ -f "distro/debian/$PACKAGE/_service" ]] && grep -q "tar_scm" "distro/debian/$PACKAGE/_service"; then
GIT_URL=$(grep -A 5 'name="tar_scm"' "distro/debian/$PACKAGE/_service" | grep "url" | sed 's/.*<param name="url">\(.*\)<\/param>.*/\1/')
GIT_REVISION=$(grep -A 5 'name="tar_scm"' "distro/debian/$PACKAGE/_service" | grep "revision" | sed 's/.*<param name="revision">\(.*\)<\/param>.*/\1/')
if [[ -n "$GIT_URL" ]]; then
echo " Cloning git source from: $GIT_URL (revision: ${GIT_REVISION:-master})"
SOURCE_DIR="$TEMP_DIR/dms-git-source"
if git clone --depth 1 --branch "${GIT_REVISION:-master}" "$GIT_URL" "$SOURCE_DIR" 2>/dev/null || \
git clone --depth 1 "$GIT_URL" "$SOURCE_DIR" 2>/dev/null; then
cd "$SOURCE_DIR"
if [[ -n "$GIT_REVISION" ]]; then
git checkout "$GIT_REVISION" 2>/dev/null || true
fi
SOURCE_DIR=$(pwd)
cd "$REPO_ROOT"
if [[ -n "$GIT_URL" ]]; then
echo " Cloning git source from: $GIT_URL (revision: ${GIT_REVISION:-master})"
SOURCE_DIR="$TEMP_DIR/dms-git-source"
if git clone --depth 1 --branch "${GIT_REVISION:-master}" "$GIT_URL" "$SOURCE_DIR" 2>/dev/null || \
git clone --depth 1 "$GIT_URL" "$SOURCE_DIR" 2>/dev/null; then
cd "$SOURCE_DIR"
if [[ -n "$GIT_REVISION" ]]; then
git checkout "$GIT_REVISION" 2>/dev/null || true
fi
rm -rf .git
SOURCE_DIR=$(pwd)
cd "$REPO_ROOT"
fi
fi
fi
if [[ -n "$SOURCE_DIR" && -d "$SOURCE_DIR" ]]; then
# Extract Source0 from spec file
SOURCE0=$(grep "^Source0:" "distro/opensuse/$PACKAGE.spec" | awk '{print $2}' | head -1)
if [[ -n "$SOURCE0" ]]; then
@@ -273,32 +267,33 @@ fi
# Generate .dsc file and handle source format (for Debian only)
if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; then
# Get version from changelog
CHANGELOG_VERSION=$(grep -m1 "^$PACKAGE" distro/debian/$PACKAGE/debian/changelog 2>/dev/null | sed 's/.*(\([^)]*\)).*/\1/' || echo "0.1.11")
# Use CHANGELOG_VERSION already set above, or get it if not set
if [[ -z "$CHANGELOG_VERSION" ]]; then
CHANGELOG_VERSION=$(grep -m1 "^$PACKAGE" distro/debian/$PACKAGE/debian/changelog 2>/dev/null | sed 's/.*(\([^)]*\)).*/\1/' || echo "0.1.11")
fi
# Determine source format
SOURCE_FORMAT=$(cat "distro/debian/$PACKAGE/debian/source/format" 2>/dev/null || echo "3.0 (quilt)")
# Handle native format (3.0 native)
# For native format, remove any Debian revision (-N) from version
# Native format cannot have revisions, so strip them if present
if [[ "$SOURCE_FORMAT" == *"native"* ]] && [[ "$CHANGELOG_VERSION" == *"-"* ]]; then
# Remove Debian revision (everything from - onwards)
CHANGELOG_VERSION=$(echo "$CHANGELOG_VERSION" | sed 's/-[0-9]*$//')
echo " Warning: Removed Debian revision from version for native format: $CHANGELOG_VERSION"
fi
if [[ "$SOURCE_FORMAT" == *"native"* ]]; then
echo " - Native format detected: creating combined tarball"
VERSION="$CHANGELOG_VERSION"
# Create temp directory for building combined tarball
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
# Determine tarball name for native format (use version without revision)
COMBINED_TARBALL="${PACKAGE}_${VERSION}.tar.gz"
SOURCE_DIR=""
# Check _service file to determine how to get source
if [[ -f "distro/debian/$PACKAGE/_service" ]]; then
# Check for tar_scm first (git source) - this takes priority for git packages
if grep -q "tar_scm" "distro/debian/$PACKAGE/_service"; then
# For dms-git, use tar_scm to get git source
GIT_URL=$(grep -A 5 'name="tar_scm"' "distro/debian/$PACKAGE/_service" | grep "url" | sed 's/.*<param name="url">\(.*\)<\/param>.*/\1/')
GIT_REVISION=$(grep -A 5 'name="tar_scm"' "distro/debian/$PACKAGE/_service" | grep "revision" | sed 's/.*<param name="revision">\(.*\)<\/param>.*/\1/')
@@ -311,6 +306,7 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
if [[ -n "$GIT_REVISION" ]]; then
git checkout "$GIT_REVISION" 2>/dev/null || true
fi
rm -rf .git
SOURCE_DIR=$(pwd)
cd "$REPO_ROOT"
else
@@ -319,16 +315,10 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
fi
fi
elif grep -q "download_url" "distro/debian/$PACKAGE/_service" && [[ "$PACKAGE" != "dms-git" ]]; then
# Extract download_url for source (skip binary downloads)
# Look for download_url with "source" in path or .tar.gz/.tar.xz archives
# Skip binaries (distropkg, standalone .gz files, etc.)
# Extract all paths from download_url services
ALL_PATHS=$(grep -A 5 '<service name="download_url">' "distro/debian/$PACKAGE/_service" | \
grep '<param name="path">' | \
sed 's/.*<param name="path">\(.*\)<\/param>.*/\1/')
# Find source path (has "source" or ends with .tar.gz/.tar.xz, but not distropkg)
SOURCE_PATH=""
for path in $ALL_PATHS; do
if echo "$path" | grep -qE "(source|archive|\.tar\.(gz|xz|bz2))" && \
@@ -338,7 +328,6 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
fi
done
# If no source found, try first path that ends with .tar.gz/.tar.xz
if [[ -z "$SOURCE_PATH" ]]; then
for path in $ALL_PATHS; do
if echo "$path" | grep -qE "\.tar\.(gz|xz|bz2)$"; then
@@ -349,7 +338,6 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
fi
if [[ -n "$SOURCE_PATH" ]]; then
# Extract the service block containing this path
SOURCE_BLOCK=$(awk -v target="$SOURCE_PATH" '
/<service name="download_url">/ { in_block=1; block="" }
in_block { block=block"\n"$0 }
@@ -371,17 +359,16 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
SOURCE_URL="${URL_PROTOCOL}://${URL_HOST}${URL_PATH}"
echo " Downloading source from: $SOURCE_URL"
if wget -q -O "$TEMP_DIR/source-archive" "$SOURCE_URL"; then
if wget -q -O "$TEMP_DIR/source-archive" "$SOURCE_URL" 2>/dev/null || \
curl -L -f -s -o "$TEMP_DIR/source-archive" "$SOURCE_URL" 2>/dev/null; then
cd "$TEMP_DIR"
if [[ "$SOURCE_URL" == *.tar.xz ]]; then
tar -xJf source-archive
elif [[ "$SOURCE_URL" == *.tar.gz ]] || [[ "$SOURCE_URL" == *.tgz ]]; then
tar -xzf source-archive
fi
# GitHub archives extract to DankMaterialShell-VERSION/ or similar
SOURCE_DIR=$(find . -maxdepth 1 -type d -name "DankMaterialShell-*" | head -1)
if [[ -z "$SOURCE_DIR" ]]; then
# Try to find any extracted directory
SOURCE_DIR=$(find . -maxdepth 1 -type d ! -name "." | head -1)
fi
if [[ -z "$SOURCE_DIR" || ! -d "$SOURCE_DIR" ]]; then
@@ -391,11 +378,11 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
cd "$REPO_ROOT"
exit 1
fi
# Convert to absolute path
SOURCE_DIR=$(cd "$SOURCE_DIR" && pwd)
cd "$REPO_ROOT"
else
echo "Error: Failed to download source from $SOURCE_URL"
echo "Tried both wget and curl. Please check the URL and network connectivity."
exit 1
fi
fi
@@ -417,216 +404,120 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
# Create OpenSUSE-compatible source tarballs BEFORE adding debian/ directory
# (OpenSUSE doesn't need debian/ directory)
if [[ "$UPLOAD_OPENSUSE" == true ]] && [[ -f "distro/opensuse/$PACKAGE.spec" ]]; then
# If SOURCE_DIR is not set (OpenSUSE-only upload), detect source now
if [[ -z "$SOURCE_DIR" || ! -d "$SOURCE_DIR" ]]; then
echo " - Detecting source for OpenSUSE-only upload"
if [[ -z "$TEMP_DIR" ]]; then
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
fi
# Check _service file to determine how to get source
if [[ -f "distro/debian/$PACKAGE/_service" ]]; then
# Check for tar_scm first (git source) - this takes priority for git packages
if grep -q "tar_scm" "distro/debian/$PACKAGE/_service"; then
# For dms-git, use tar_scm to get git source
GIT_URL=$(grep -A 5 'name="tar_scm"' "distro/debian/$PACKAGE/_service" | grep "url" | sed 's/.*<param name="url">\(.*\)<\/param>.*/\1/')
GIT_REVISION=$(grep -A 5 'name="tar_scm"' "distro/debian/$PACKAGE/_service" | grep "revision" | sed 's/.*<param name="revision">\(.*\)<\/param>.*/\1/')
if [[ -n "$GIT_URL" ]]; then
echo " Cloning git source from: $GIT_URL (revision: ${GIT_REVISION:-master})"
SOURCE_DIR="$TEMP_DIR/dms-git-source"
if git clone --depth 1 --branch "${GIT_REVISION:-master}" "$GIT_URL" "$SOURCE_DIR" 2>/dev/null || \
git clone --depth 1 "$GIT_URL" "$SOURCE_DIR" 2>/dev/null; then
cd "$SOURCE_DIR"
if [[ -n "$GIT_REVISION" ]]; then
git checkout "$GIT_REVISION" 2>/dev/null || true
fi
SOURCE_DIR=$(pwd)
cd "$REPO_ROOT"
else
echo "Error: Failed to clone git repository"
exit 1
fi
fi
elif grep -q "download_url" "distro/debian/$PACKAGE/_service" && [[ "$PACKAGE" != "dms-git" ]]; then
# Extract download_url for source (skip binary downloads)
ALL_PATHS=$(grep -A 5 '<service name="download_url">' "distro/debian/$PACKAGE/_service" | \
grep '<param name="path">' | \
sed 's/.*<param name="path">\(.*\)<\/param>.*/\1/')
# Find source path (has "source" or ends with .tar.gz/.tar.xz, but not distropkg)
SOURCE_PATH=""
for path in $ALL_PATHS; do
if echo "$path" | grep -qE "(source|archive|\.tar\.(gz|xz|bz2))" && \
! echo "$path" | grep -qE "(distropkg|binary)"; then
SOURCE_PATH="$path"
break
fi
done
# If no source found, try first path that ends with .tar.gz/.tar.xz
if [[ -z "$SOURCE_PATH" ]]; then
for path in $ALL_PATHS; do
if echo "$path" | grep -qE "\.tar\.(gz|xz|bz2)$"; then
SOURCE_PATH="$path"
break
fi
done
fi
if [[ -n "$SOURCE_PATH" ]]; then
# Extract the service block containing this path
SOURCE_BLOCK=$(awk -v target="$SOURCE_PATH" '
/<service name="download_url">/ { in_block=1; block="" }
in_block { block=block"\n"$0 }
/<\/service>/ {
if (in_block && block ~ target) {
print block
exit
}
in_block=0
}
' "distro/debian/$PACKAGE/_service")
URL_PROTOCOL=$(echo "$SOURCE_BLOCK" | grep "protocol" | sed 's/.*<param name="protocol">\(.*\)<\/param>.*/\1/' | head -1)
URL_HOST=$(echo "$SOURCE_BLOCK" | grep "host" | sed 's/.*<param name="host">\(.*\)<\/param>.*/\1/' | head -1)
URL_PATH="$SOURCE_PATH"
fi
if [[ -n "$URL_PROTOCOL" && -n "$URL_HOST" && -n "$URL_PATH" ]]; then
SOURCE_URL="${URL_PROTOCOL}://${URL_HOST}${URL_PATH}"
echo " Downloading source from: $SOURCE_URL"
if wget -q -O "$TEMP_DIR/source-archive" "$SOURCE_URL"; then
cd "$TEMP_DIR"
if [[ "$SOURCE_URL" == *.tar.xz ]]; then
tar -xJf source-archive
elif [[ "$SOURCE_URL" == *.tar.gz ]] || [[ "$SOURCE_URL" == *.tgz ]]; then
tar -xzf source-archive
fi
# GitHub archives extract to DankMaterialShell-VERSION/ or similar
SOURCE_DIR=$(find . -maxdepth 1 -type d -name "DankMaterialShell-*" | head -1)
if [[ -z "$SOURCE_DIR" ]]; then
# Try to find any extracted directory
SOURCE_DIR=$(find . -maxdepth 1 -type d ! -name "." | head -1)
fi
if [[ -z "$SOURCE_DIR" || ! -d "$SOURCE_DIR" ]]; then
echo "Error: Failed to extract source archive or find source directory"
echo "Contents of $TEMP_DIR:"
ls -la "$TEMP_DIR"
cd "$REPO_ROOT"
exit 1
fi
# Convert to absolute path
SOURCE_DIR=$(cd "$SOURCE_DIR" && pwd)
cd "$REPO_ROOT"
else
echo "Error: Failed to download source from $SOURCE_URL"
exit 1
fi
fi
fi
fi
if [[ -z "$SOURCE_DIR" || ! -d "$SOURCE_DIR" ]]; then
echo "Error: Could not determine or obtain source for $PACKAGE (OpenSUSE-only upload)"
echo "SOURCE_DIR: $SOURCE_DIR"
if [[ -d "$TEMP_DIR" ]]; then
echo "Contents of temp directory:"
ls -la "$TEMP_DIR"
fi
exit 1
fi
echo " Found source directory: $SOURCE_DIR"
fi
echo " - Creating OpenSUSE-compatible source tarballs"
# Extract Source0 from spec file
SOURCE0=$(grep "^Source0:" "distro/opensuse/$PACKAGE.spec" | awk '{print $2}' | head -1); if [[ -z "$SOURCE0" && "$PACKAGE" == "dms-git" ]]; then SOURCE0="dms-git-source.tar.gz"; fi
SOURCE0=$(grep "^Source0:" "distro/opensuse/$PACKAGE.spec" | awk '{print $2}' | head -1)
if [[ -z "$SOURCE0" && "$PACKAGE" == "dms-git" ]]; then
SOURCE0="dms-git-source.tar.gz"
fi
if [[ -n "$SOURCE0" ]]; then
# Create a separate temporary directory for OpenSUSE tarball creation to avoid conflicts
OBS_TARBALL_DIR=$(mktemp -d -t obs-tarball-XXXXXX)
cd "$OBS_TARBALL_DIR"
case "$PACKAGE" in
dms)
# dms spec expects DankMaterialShell-%{version} directory (from %setup -q -n DankMaterialShell-%{version})
# Extract version from spec file
DMS_VERSION=$(grep "^Version:" "$REPO_ROOT/distro/opensuse/$PACKAGE.spec" | sed 's/^Version:[[:space:]]*//' | head -1)
if [[ -n "$CHANGELOG_VERSION" ]]; then
DMS_VERSION="$CHANGELOG_VERSION"
else
DMS_VERSION=$(grep "^Version:" "$REPO_ROOT/distro/opensuse/$PACKAGE.spec" | sed 's/^Version:[[:space:]]*//' | head -1)
fi
EXPECTED_DIR="DankMaterialShell-${DMS_VERSION}"
echo " Creating $SOURCE0 (directory: $EXPECTED_DIR)"
cp -r "$SOURCE_DIR" "$EXPECTED_DIR"
if [[ "$SOURCE0" == *.tar.xz ]]; then
tar -cJf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -cJf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
elif [[ "$SOURCE0" == *.tar.bz2 ]]; then
tar -cjf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -cjf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
else
tar -czf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
fi
rm -rf "$EXPECTED_DIR"
echo " Created $SOURCE0 ($(stat -c%s "$WORK_DIR/$SOURCE0" 2>/dev/null || echo 0) bytes)"
;;
dms-git)
# dms-git spec expects dms-git-source directory (from %setup -q -n dms-git-source)
EXPECTED_DIR="dms-git-source"
echo " Creating $SOURCE0 (directory: $EXPECTED_DIR)"
cp -r "$SOURCE_DIR" "$EXPECTED_DIR"
if [[ "$SOURCE0" == *.tar.xz ]]; then
tar -cJf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -cJf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
elif [[ "$SOURCE0" == *.tar.bz2 ]]; then
tar -cjf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -cjf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
else
tar -czf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
fi
rm -rf "$EXPECTED_DIR"
echo " Created $SOURCE0 ($(stat -c%s "$WORK_DIR/$SOURCE0" 2>/dev/null || echo 0) bytes)"
;;
*)
# Generic handling
DIR_NAME=$(basename "$SOURCE_DIR")
echo " Creating $SOURCE0 (directory: $DIR_NAME)"
cp -r "$SOURCE_DIR" "$DIR_NAME"
if [[ "$SOURCE0" == *.tar.xz ]]; then
tar -cJf "$WORK_DIR/$SOURCE0" "$DIR_NAME"
tar --sort=name --mtime='2000-01-01 00:00:00' -cJf "$WORK_DIR/$SOURCE0" "$DIR_NAME"
elif [[ "$SOURCE0" == *.tar.bz2 ]]; then
tar -cjf "$WORK_DIR/$SOURCE0" "$DIR_NAME"
tar --sort=name --mtime='2000-01-01 00:00:00' -cjf "$WORK_DIR/$SOURCE0" "$DIR_NAME"
else
tar -czf "$WORK_DIR/$SOURCE0" "$DIR_NAME"
tar --sort=name --mtime='2000-01-01 00:00:00' -czf "$WORK_DIR/$SOURCE0" "$DIR_NAME"
fi
rm -rf "$DIR_NAME"
echo " Created $SOURCE0 ($(stat -c%s "$WORK_DIR/$SOURCE0" 2>/dev/null || echo 0) bytes)"
;;
esac
# Clean up the tarball work directory
cd "$REPO_ROOT"
rm -rf "$OBS_TARBALL_DIR"
echo " - OpenSUSE source tarballs created"
fi
# Copy spec file
cp "distro/opensuse/$PACKAGE.spec" "$WORK_DIR/"
fi
# Copy debian/ directory into source (for Debian builds only)
if [[ "$UPLOAD_DEBIAN" == true ]]; then
echo " Copying debian/ directory into source"
cp -r "distro/debian/$PACKAGE/debian" "$SOURCE_DIR/"
# Create combined tarball
# For dms, rename directory to match what debian/rules expects
# debian/rules uses UPSTREAM_VERSION which is the full version from changelog
if [[ "$PACKAGE" == "dms" ]]; then
CHANGELOG_IN_SOURCE="$SOURCE_DIR/debian/changelog"
if [[ -f "$CHANGELOG_IN_SOURCE" ]]; then
ACTUAL_VERSION=$(grep -m1 "^$PACKAGE" "$CHANGELOG_IN_SOURCE" 2>/dev/null | sed 's/.*(\([^)]*\)).*/\1/' || echo "$VERSION")
CURRENT_DIR=$(basename "$SOURCE_DIR")
EXPECTED_DIR="DankMaterialShell-${ACTUAL_VERSION}"
if [[ "$CURRENT_DIR" != "$EXPECTED_DIR" ]]; then
echo " Renaming directory from $CURRENT_DIR to $EXPECTED_DIR to match debian/rules"
cd "$(dirname "$SOURCE_DIR")"
mv "$CURRENT_DIR" "$EXPECTED_DIR"
SOURCE_DIR="$(pwd)/$EXPECTED_DIR"
cd "$REPO_ROOT"
fi
fi
fi
rm -f "$WORK_DIR/$COMBINED_TARBALL"
echo " Creating combined tarball: $COMBINED_TARBALL"
cd "$(dirname "$SOURCE_DIR")"
TARBALL_BASE=$(basename "$SOURCE_DIR")
tar -czf "$WORK_DIR/$COMBINED_TARBALL" "$TARBALL_BASE"
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/$COMBINED_TARBALL" "$TARBALL_BASE"
cd "$REPO_ROOT"
# Generate .dsc file for native format
if [[ "$PACKAGE" == "dms" ]]; then
TARBALL_DIR=$(tar -tzf "$WORK_DIR/$COMBINED_TARBALL" 2>/dev/null | head -1 | cut -d'/' -f1)
EXPECTED_TARBALL_DIR="DankMaterialShell-${VERSION}"
if [[ "$TARBALL_DIR" != "$EXPECTED_TARBALL_DIR" ]]; then
echo " Warning: Tarball directory name mismatch: $TARBALL_DIR != $EXPECTED_TARBALL_DIR"
echo " This may cause build failures. Recreating tarball..."
cd "$(dirname "$SOURCE_DIR")"
rm -f "$WORK_DIR/$COMBINED_TARBALL"
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/$COMBINED_TARBALL" "$TARBALL_BASE"
cd "$REPO_ROOT"
fi
fi
TARBALL_SIZE=$(stat -c%s "$WORK_DIR/$COMBINED_TARBALL" 2>/dev/null || stat -f%z "$WORK_DIR/$COMBINED_TARBALL" 2>/dev/null)
TARBALL_MD5=$(md5sum "$WORK_DIR/$COMBINED_TARBALL" | cut -d' ' -f1)
# Extract Build-Depends from control file
BUILD_DEPS="debhelper-compat (= 13)"
if [[ -f "distro/debian/$PACKAGE/debian/control" ]]; then
CONTROL_DEPS=$(sed -n '/^Build-Depends:/,/^[A-Z]/p' "distro/debian/$PACKAGE/debian/control" | \
@@ -654,9 +545,7 @@ EOF
echo " - Generated $PACKAGE.dsc for native format"
fi
else
# Quilt format (legacy) - for Debian only
if [[ "$UPLOAD_DEBIAN" == true ]]; then
# For quilt format, version can have revision
if [[ "$CHANGELOG_VERSION" == *"-"* ]]; then
VERSION="$CHANGELOG_VERSION"
else
@@ -683,13 +572,246 @@ EOF
fi
fi
# Change to working directory and commit
cd "$WORK_DIR"
echo "==> Updating working copy"
if ! osc up; then
echo "Error: Failed to update working copy"
exit 1
fi
# Only auto-increment on manual runs (REBUILD_RELEASE set or not in CI), not automated workflows
OLD_DSC_FILE=""
if [[ -f "$WORK_DIR/$PACKAGE.dsc" ]]; then
OLD_DSC_FILE="$WORK_DIR/$PACKAGE.dsc"
elif [[ -f "$WORK_DIR/.osc/sources/$PACKAGE.dsc" ]]; then
OLD_DSC_FILE="$WORK_DIR/.osc/sources/$PACKAGE.dsc"
fi
if [[ "$UPLOAD_DEBIAN" == true ]] && [[ "$SOURCE_FORMAT" == *"native"* ]] && [[ -n "$OLD_DSC_FILE" ]]; then
OLD_DSC_VERSION=$(grep "^Version:" "$OLD_DSC_FILE" 2>/dev/null | awk '{print $2}' | head -1)
IS_MANUAL=false
if [[ -n "${REBUILD_RELEASE:-}" ]]; then
IS_MANUAL=true
echo "==> Manual rebuild detected (REBUILD_RELEASE=$REBUILD_RELEASE)"
elif [[ -n "${FORCE_REBUILD:-}" ]] && [[ "${FORCE_REBUILD}" == "true" ]]; then
IS_MANUAL=true
echo "==> Manual workflow trigger detected (FORCE_REBUILD=true)"
elif [[ -z "${GITHUB_ACTIONS:-}" ]] && [[ -z "${CI:-}" ]]; then
IS_MANUAL=true
echo "==> Local/manual run detected (not in CI)"
fi
if [[ -n "$OLD_DSC_VERSION" ]] && [[ "$OLD_DSC_VERSION" == "$CHANGELOG_VERSION" ]] && [[ "$IS_MANUAL" == true ]]; then
echo "==> Detected rebuild of same version $CHANGELOG_VERSION, incrementing version"
if [[ "$CHANGELOG_VERSION" =~ ^([0-9.]+)\+git$ ]]; then
BASE_VERSION="${BASH_REMATCH[1]}"
NEW_VERSION="${BASE_VERSION}+git1"
echo " Incrementing git number: $CHANGELOG_VERSION -> $NEW_VERSION"
elif [[ "$CHANGELOG_VERSION" =~ ^([0-9.]+)\+git([0-9]+)$ ]]; then
BASE_VERSION="${BASH_REMATCH[1]}"
GIT_NUM="${BASH_REMATCH[2]}"
NEW_GIT_NUM=$((GIT_NUM + 1))
NEW_VERSION="${BASE_VERSION}+git${NEW_GIT_NUM}"
echo " Incrementing git number: $CHANGELOG_VERSION -> $NEW_VERSION"
elif [[ "$CHANGELOG_VERSION" =~ ^([0-9.]+)ppa([0-9]+)$ ]]; then
BASE_VERSION="${BASH_REMATCH[1]}"
PPA_NUM="${BASH_REMATCH[2]}"
NEW_PPA_NUM=$((PPA_NUM + 1))
NEW_VERSION="${BASE_VERSION}ppa${NEW_PPA_NUM}"
echo " Incrementing PPA number: $CHANGELOG_VERSION -> $NEW_VERSION"
elif [[ "$CHANGELOG_VERSION" =~ ^([0-9.]+)\+git([0-9]+)(\.[a-f0-9]+)?(ppa([0-9]+))?$ ]]; then
BASE_VERSION="${BASH_REMATCH[1]}"
GIT_NUM="${BASH_REMATCH[2]}"
GIT_HASH="${BASH_REMATCH[3]}"
PPA_NUM="${BASH_REMATCH[5]}"
if [[ -n "$PPA_NUM" ]]; then
NEW_PPA_NUM=$((PPA_NUM + 1))
NEW_VERSION="${BASE_VERSION}+git${GIT_NUM}${GIT_HASH}ppa${NEW_PPA_NUM}"
echo " Incrementing PPA number: $CHANGELOG_VERSION -> $NEW_VERSION"
else
NEW_VERSION="${BASE_VERSION}+git${GIT_NUM}${GIT_HASH}ppa1"
echo " Adding PPA number: $CHANGELOG_VERSION -> $NEW_VERSION"
fi
elif [[ "$CHANGELOG_VERSION" =~ ^([0-9.]+)(-([0-9]+))?$ ]]; then
BASE_VERSION="${BASH_REMATCH[1]}"
NEW_VERSION="${BASE_VERSION}ppa1"
echo " Warning: Native format cannot have Debian revision, converting to PPA format: $CHANGELOG_VERSION -> $NEW_VERSION"
else
NEW_VERSION="${CHANGELOG_VERSION}ppa1"
echo " Warning: Could not parse version format, appending ppa1: $CHANGELOG_VERSION -> $NEW_VERSION"
fi
if [[ -z "$SOURCE_DIR" ]] || [[ ! -d "$SOURCE_DIR" ]] || [[ ! -d "$SOURCE_DIR/debian" ]]; then
echo " Error: Source directory with debian/ not found for version increment"
exit 1
fi
SOURCE_CHANGELOG="$SOURCE_DIR/debian/changelog"
if [[ ! -f "$SOURCE_CHANGELOG" ]]; then
echo " Error: Changelog not found in source directory: $SOURCE_CHANGELOG"
exit 1
fi
REPO_CHANGELOG="$REPO_ROOT/distro/debian/$PACKAGE/debian/changelog"
TEMP_CHANGELOG=$(mktemp)
{
echo "$PACKAGE ($NEW_VERSION) unstable; urgency=medium"
echo ""
echo " * Rebuild to fix repository metadata issues"
echo ""
echo " -- Avenge Media <AvengeMedia.US@gmail.com> $(date -R)"
echo ""
if [[ -f "$REPO_CHANGELOG" ]]; then
OLD_ENTRY_START=$(grep -n "^$PACKAGE (" "$REPO_CHANGELOG" | sed -n '2p' | cut -d: -f1)
if [[ -n "$OLD_ENTRY_START" ]]; then
tail -n +$OLD_ENTRY_START "$REPO_CHANGELOG"
fi
fi
} > "$TEMP_CHANGELOG"
cp "$TEMP_CHANGELOG" "$SOURCE_CHANGELOG"
rm -f "$TEMP_CHANGELOG"
CHANGELOG_VERSION="$NEW_VERSION"
VERSION="$NEW_VERSION"
COMBINED_TARBALL="${PACKAGE}_${VERSION}.tar.gz"
for old_tarball in "${PACKAGE}"_*.tar.gz; do
if [[ -f "$old_tarball" ]] && [[ "$old_tarball" != "${PACKAGE}_${NEW_VERSION}.tar.gz" ]]; then
echo " Removing old tarball from OBS: $old_tarball"
osc rm -f "$old_tarball" 2>/dev/null || rm -f "$old_tarball"
fi
done
if [[ "$PACKAGE" == "dms" ]] && [[ -f "$WORK_DIR/dms-source.tar.gz" ]]; then
echo " Recreating dms-source.tar.gz with new directory name for incremented version"
EXPECTED_SOURCE_DIR="DankMaterialShell-${NEW_VERSION}"
TEMP_SOURCE_DIR=$(mktemp -d)
cd "$TEMP_SOURCE_DIR"
tar -xzf "$WORK_DIR/dms-source.tar.gz" 2>/dev/null || tar -xJf "$WORK_DIR/dms-source.tar.gz" 2>/dev/null || tar -xjf "$WORK_DIR/dms-source.tar.gz" 2>/dev/null
EXTRACTED=$(find . -maxdepth 1 -type d -name "DankMaterialShell-*" | head -1)
if [[ -n "$EXTRACTED" ]] && [[ "$EXTRACTED" != "./$EXPECTED_SOURCE_DIR" ]]; then
echo " Renaming $EXTRACTED to $EXPECTED_SOURCE_DIR"
mv "$EXTRACTED" "$EXPECTED_SOURCE_DIR"
rm -f "$WORK_DIR/dms-source.tar.gz"
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/dms-source.tar.gz" "$EXPECTED_SOURCE_DIR"
ROOT_DIR=$(tar -tf "$WORK_DIR/dms-source.tar.gz" | head -1 | cut -d/ -f1)
if [[ "$ROOT_DIR" != "$EXPECTED_SOURCE_DIR" ]]; then
echo " Error: Recreated tarball has wrong root directory: $ROOT_DIR (expected $EXPECTED_SOURCE_DIR)"
exit 1
fi
fi
cd "$REPO_ROOT"
rm -rf "$TEMP_SOURCE_DIR"
fi
echo " Recreating tarball with new version: $COMBINED_TARBALL"
if [[ -n "$SOURCE_DIR" ]] && [[ -d "$SOURCE_DIR" ]] && [[ -d "$SOURCE_DIR/debian" ]]; then
if [[ "$PACKAGE" == "dms" ]]; then
cd "$(dirname "$SOURCE_DIR")"
CURRENT_DIR=$(basename "$SOURCE_DIR")
EXPECTED_DIR="DankMaterialShell-${NEW_VERSION}"
if [[ "$CURRENT_DIR" != "$EXPECTED_DIR" ]]; then
echo " Renaming directory from $CURRENT_DIR to $EXPECTED_DIR to match debian/rules"
if [[ -d "$CURRENT_DIR" ]]; then
mv "$CURRENT_DIR" "$EXPECTED_DIR"
SOURCE_DIR="$(pwd)/$EXPECTED_DIR"
else
echo " Warning: Source directory $CURRENT_DIR not found, extracting from existing tarball"
OLD_TARBALL=$(ls "${PACKAGE}"_*.tar.gz 2>/dev/null | head -1)
if [[ -f "$OLD_TARBALL" ]]; then
EXTRACT_DIR=$(mktemp -d)
cd "$EXTRACT_DIR"
tar -xzf "$WORK_DIR/$OLD_TARBALL"
EXTRACTED_DIR=$(find . -maxdepth 1 -type d -name "DankMaterialShell-*" | head -1)
if [[ -n "$EXTRACTED_DIR" ]] && [[ "$EXTRACTED_DIR" != "./$EXPECTED_DIR" ]]; then
mv "$EXTRACTED_DIR" "$EXPECTED_DIR"
if [[ -f "$EXPECTED_DIR/debian/changelog" ]]; then
ACTUAL_VER=$(grep -m1 "^$PACKAGE" "$EXPECTED_DIR/debian/changelog" 2>/dev/null | sed 's/.*(\([^)]*\)).*/\1/')
if [[ "$ACTUAL_VER" != "$NEW_VERSION" ]]; then
echo " Updating changelog version in extracted directory"
REPO_CHANGELOG="$REPO_ROOT/distro/debian/$PACKAGE/debian/changelog"
TEMP_CHANGELOG=$(mktemp)
{
echo "$PACKAGE ($NEW_VERSION) unstable; urgency=medium"
echo ""
echo " * Rebuild to fix repository metadata issues"
echo ""
echo " -- Avenge Media <AvengeMedia.US@gmail.com> $(date -R)"
echo ""
if [[ -f "$REPO_CHANGELOG" ]]; then
OLD_ENTRY_START=$(grep -n "^$PACKAGE (" "$REPO_CHANGELOG" | sed -n '2p' | cut -d: -f1)
if [[ -n "$OLD_ENTRY_START" ]]; then
tail -n +$OLD_ENTRY_START "$REPO_CHANGELOG"
fi
fi
} > "$TEMP_CHANGELOG"
cp "$TEMP_CHANGELOG" "$EXPECTED_DIR/debian/changelog"
rm -f "$TEMP_CHANGELOG"
fi
fi
SOURCE_DIR="$(pwd)/$EXPECTED_DIR"
cd "$REPO_ROOT"
else
echo " Error: Could not extract or find source directory"
rm -rf "$EXTRACT_DIR"
exit 1
fi
else
echo " Error: No existing tarball found to extract"
exit 1
fi
fi
fi
fi
cd "$(dirname "$SOURCE_DIR")"
TARBALL_BASE=$(basename "$SOURCE_DIR")
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/$COMBINED_TARBALL" "$TARBALL_BASE"
cd "$WORK_DIR"
TARBALL_SIZE=$(stat -c%s "$WORK_DIR/$COMBINED_TARBALL" 2>/dev/null || stat -f%z "$WORK_DIR/$COMBINED_TARBALL" 2>/dev/null)
TARBALL_MD5=$(md5sum "$WORK_DIR/$COMBINED_TARBALL" | cut -d' ' -f1)
BUILD_DEPS="debhelper-compat (= 13)"
if [[ -f "distro/debian/$PACKAGE/debian/control" ]]; then
CONTROL_DEPS=$(sed -n '/^Build-Depends:/,/^[A-Z]/p' "distro/debian/$PACKAGE/debian/control" | \
sed '/^Build-Depends:/s/^Build-Depends: *//' | \
sed '/^[A-Z]/d' | \
tr '\n' ' ' | \
sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/[[:space:]]\+/ /g')
if [[ -n "$CONTROL_DEPS" && "$CONTROL_DEPS" != "" ]]; then
BUILD_DEPS="$CONTROL_DEPS"
fi
fi
cat > "$WORK_DIR/$PACKAGE.dsc" << EOF
Format: 3.0 (native)
Source: $PACKAGE
Binary: $PACKAGE
Architecture: any
Version: $VERSION
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
Build-Depends: $BUILD_DEPS
Files:
$TARBALL_MD5 $TARBALL_SIZE $COMBINED_TARBALL
EOF
echo " - Updated changelog and recreated tarball with version $NEW_VERSION"
else
echo " Error: Source directory not found, cannot recreate tarball"
exit 1
fi
fi
fi
find . -maxdepth 1 -type f \( -name "*.dsc" -o -name "*.spec" \) -exec grep -l "^<<<<<<< " {} \; 2>/dev/null | while read -r conflicted_file; do
echo " Removing conflicted text file: $conflicted_file"
rm -f "$conflicted_file"
done
echo "==> Staging changes"
# List files to be uploaded
echo "Files to upload:"
# Only list files relevant to the selected upload type
if [[ "$UPLOAD_DEBIAN" == true ]] && [[ "$UPLOAD_OPENSUSE" == true ]]; then
ls -lh *.tar.gz *.tar.xz *.tar *.spec *.dsc _service 2>/dev/null | awk '{print " " $9 " (" $5 ")"}'
elif [[ "$UPLOAD_DEBIAN" == true ]]; then
@@ -699,35 +821,48 @@ elif [[ "$UPLOAD_OPENSUSE" == true ]]; then
fi
echo ""
osc addremove
osc addremove 2>&1 | grep -v "Git SCM package" || true
echo "==> Committing to OBS"
osc commit -m "$MESSAGE"
SOURCE_TARBALL="${PACKAGE}-source.tar.gz"
if [[ -f "$SOURCE_TARBALL" ]]; then
echo "==> Ensuring $SOURCE_TARBALL is tracked by OBS"
osc add "$SOURCE_TARBALL" 2>&1 | grep -v "already added\|already tracked\|Git SCM package" || true
elif [[ -f "$WORK_DIR/$SOURCE_TARBALL" ]]; then
echo "==> Copying $SOURCE_TARBALL from WORK_DIR and adding to OBS"
cp "$WORK_DIR/$SOURCE_TARBALL" "$SOURCE_TARBALL"
osc add "$SOURCE_TARBALL" 2>&1 | grep -v "already added\|already tracked\|Git SCM package" || true
fi
ADDREMOVE_EXIT=${PIPESTATUS[0]}
if [[ $ADDREMOVE_EXIT -ne 0 ]] && [[ $ADDREMOVE_EXIT -ne 1 ]]; then
echo "Warning: osc addremove returned exit code $ADDREMOVE_EXIT"
fi
if osc status | grep -q '^C'; then
echo "==> Resolving conflicts"
osc status | grep '^C' | awk '{print $2}' | xargs -r osc resolved
fi
if ! osc status 2>/dev/null | grep -qE '^[MAD]|^[?]'; then
echo "==> No changes to commit (package already up to date)"
else
echo "==> Committing to OBS"
set +e
osc commit -m "$MESSAGE" 2>&1 | grep -v "Git SCM package" | grep -v "apiurl\|project\|_ObsPrj\|_manifest\|git-obs"
COMMIT_EXIT=${PIPESTATUS[0]}
set -e
if [[ $COMMIT_EXIT -ne 0 ]]; then
echo "Error: Upload failed with exit code $COMMIT_EXIT"
exit 1
fi
fi
echo "==> Checking build status"
osc results
echo ""
echo "Upload complete! Monitor builds with:"
echo " cd $WORK_DIR && osc results"
echo " cd $WORK_DIR && osc buildlog <repo> <arch>"
echo ""
# Don't cleanup - keep checkout for status checking
echo ""
echo "Upload complete! Build status:"
echo "Upload complete!"
cd "$WORK_DIR"
osc results 2>&1 | head -10
cd "$REPO_ROOT"
echo ""
echo "To check detailed status:"
echo " cd $WORK_DIR && osc results"
echo " cd $WORK_DIR && osc remotebuildlog $OBS_PROJECT $PACKAGE Debian_13 x86_64"
echo ""
echo "NOTE: Checkout kept at $WORK_DIR for status checking"
echo ""
echo "✅ Upload complete!"
echo ""
echo "Check build status with:"
echo " ./distro/scripts/obs-status.sh $PACKAGE"
+3
View File
@@ -159,6 +159,7 @@ Singleton {
property bool showWorkspaceApps: false
property int maxWorkspaceIcons: 3
property bool workspacesPerMonitor: true
property bool showOccupiedWorkspacesOnly: false
property bool dwlShowAllTags: false
property var workspaceNameIcons: ({})
property bool waveProgressEnabled: true
@@ -243,6 +244,8 @@ Singleton {
property bool lockBeforeSuspend: false
property bool preventIdleForMedia: false
property bool loginctlLockIntegration: true
property bool fadeToLockEnabled: false
property int fadeToLockGracePeriod: 5
property string launchPrefix: ""
property var brightnessDevicePins: ({})
property var wifiNetworkPins: ({})
@@ -74,6 +74,7 @@ var SPEC = {
showWorkspaceApps: { def: false },
maxWorkspaceIcons: { def: 3 },
workspacesPerMonitor: { def: true },
showOccupiedWorkspacesOnly: { def: false },
dwlShowAllTags: { def: false },
workspaceNameIcons: { def: {} },
waveProgressEnabled: { def: true },
@@ -145,6 +146,8 @@ var SPEC = {
lockBeforeSuspend: { def: false },
preventIdleForMedia: { def: false },
loginctlLockIntegration: { def: true },
fadeToLockEnabled: { def: false },
fadeToLockGracePeriod: { def: 5 },
launchPrefix: { def: "" },
brightnessDevicePins: { def: {} },
wifiNetworkPins: { def: {} },
+40
View File
@@ -63,6 +63,46 @@ Item {
id: lock
}
Variants {
model: Quickshell.screens
delegate: Loader {
id: fadeWindowLoader
required property var modelData
active: SettingsData.fadeToLockEnabled
asynchronous: false
sourceComponent: FadeToLockWindow {
screen: fadeWindowLoader.modelData
onFadeCompleted: {
IdleService.lockRequested();
}
onFadeCancelled: {
console.log("Fade to lock cancelled by user on screen:", fadeWindowLoader.modelData.name);
}
}
Connections {
target: IdleService
enabled: fadeWindowLoader.item !== null
function onFadeToLockRequested() {
if (fadeWindowLoader.item) {
fadeWindowLoader.item.startFade();
}
}
function onCancelFadeToLock() {
if (fadeWindowLoader.item) {
fadeWindowLoader.item.cancelFade();
}
}
}
}
}
Repeater {
id: dankBarRepeater
model: ScriptModel {
+17 -8
View File
@@ -49,10 +49,7 @@ Item {
signal dialogClosed
signal backgroundClicked
readonly property bool useBackgroundWindow: {
const layerOverride = Quickshell.env("DMS_MODAL_LAYER");
return !layerOverride || layerOverride === "overlay";
}
readonly property bool useBackgroundWindow: true
function open() {
ModalManager.openModal(root);
@@ -146,6 +143,16 @@ Item {
bottom: true
}
mask: Region {
item: Rectangle {
x: root.alignedX
y: root.alignedY
width: root.shouldBeVisible ? root.alignedWidth : 0
height: root.shouldBeVisible ? root.alignedHeight : 0
}
intersection: Intersection.Xor
}
MouseArea {
anchors.fill: parent
enabled: root.closeOnBackgroundClick && root.shouldBeVisible
@@ -186,13 +193,15 @@ Item {
WlrLayershell.layer: {
switch (Quickshell.env("DMS_MODAL_LAYER")) {
case "bottom":
return WlrLayershell.Bottom;
case "top":
console.error("DankModal: 'bottom' layer is not valid for modals. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "background":
return WlrLayershell.Background;
default:
console.error("DankModal: 'background' layer is not valid for modals. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "overlay":
return WlrLayershell.Overlay;
default:
return WlrLayershell.Top;
}
}
WlrLayershell.exclusiveZone: -1
+177 -142
View File
@@ -10,12 +10,13 @@ Item {
anchors.fill: parent
anchors.topMargin: Theme.spacingL
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
StyledRect {
@@ -76,10 +77,10 @@ Item {
checked: SessionService.loginctlAvailable && SettingsData.loginctlLockIntegration
enabled: SessionService.loginctlAvailable
onToggled: checked => {
if (SessionService.loginctlAvailable) {
SettingsData.set("loginctlLockIntegration", checked)
}
}
if (SessionService.loginctlAvailable) {
SettingsData.set("loginctlLockIntegration", checked);
}
}
}
DankToggle {
@@ -160,6 +161,40 @@ Item {
onToggled: checked => SettingsData.set("preventIdleForMedia", checked)
}
DankToggle {
width: parent.width
text: I18n.tr("Fade to lock screen")
description: I18n.tr("Gradually fade the screen before locking with a configurable grace period")
checked: SettingsData.fadeToLockEnabled
onToggled: checked => SettingsData.set("fadeToLockEnabled", checked)
}
DankDropdown {
id: fadeGracePeriodDropdown
property var periodOptions: ["1 second", "2 seconds", "3 seconds", "4 seconds", "5 seconds", "10 seconds", "15 seconds", "20 seconds", "30 seconds"]
property var periodValues: [1, 2, 3, 4, 5, 10, 15, 20, 30]
width: parent.width
addHorizontalPadding: true
text: I18n.tr("Fade grace period")
options: periodOptions
visible: SettingsData.fadeToLockEnabled
enabled: SettingsData.fadeToLockEnabled
Component.onCompleted: {
const currentPeriod = SettingsData.fadeToLockGracePeriod;
const index = periodValues.indexOf(currentPeriod);
currentValue = index >= 0 ? periodOptions[index] : "5 seconds";
}
onValueChanged: value => {
const index = periodOptions.indexOf(value);
if (index >= 0) {
SettingsData.set("fadeToLockGracePeriod", periodValues[index]);
}
}
}
DankDropdown {
id: lockDropdown
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"]
@@ -172,29 +207,29 @@ Item {
Connections {
target: powerCategory
function onCurrentIndexChanged() {
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acLockTimeout : SettingsData.batteryLockTimeout
const index = lockDropdown.timeoutValues.indexOf(currentTimeout)
lockDropdown.currentValue = index >= 0 ? lockDropdown.timeoutOptions[index] : "Never"
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acLockTimeout : SettingsData.batteryLockTimeout;
const index = lockDropdown.timeoutValues.indexOf(currentTimeout);
lockDropdown.currentValue = index >= 0 ? lockDropdown.timeoutOptions[index] : "Never";
}
}
Component.onCompleted: {
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acLockTimeout : SettingsData.batteryLockTimeout
const index = timeoutValues.indexOf(currentTimeout)
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acLockTimeout : SettingsData.batteryLockTimeout;
const index = timeoutValues.indexOf(currentTimeout);
currentValue = index >= 0 ? timeoutOptions[index] : "Never";
}
onValueChanged: value => {
const index = timeoutOptions.indexOf(value)
if (index >= 0) {
const timeout = timeoutValues[index]
if (powerCategory.currentIndex === 0) {
SettingsData.set("acLockTimeout", timeout)
} else {
SettingsData.set("batteryLockTimeout", timeout)
}
}
}
const index = timeoutOptions.indexOf(value);
if (index >= 0) {
const timeout = timeoutValues[index];
if (powerCategory.currentIndex === 0) {
SettingsData.set("acLockTimeout", timeout);
} else {
SettingsData.set("batteryLockTimeout", timeout);
}
}
}
}
DankDropdown {
@@ -209,29 +244,29 @@ Item {
Connections {
target: powerCategory
function onCurrentIndexChanged() {
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acMonitorTimeout : SettingsData.batteryMonitorTimeout
const index = monitorDropdown.timeoutValues.indexOf(currentTimeout)
monitorDropdown.currentValue = index >= 0 ? monitorDropdown.timeoutOptions[index] : "Never"
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acMonitorTimeout : SettingsData.batteryMonitorTimeout;
const index = monitorDropdown.timeoutValues.indexOf(currentTimeout);
monitorDropdown.currentValue = index >= 0 ? monitorDropdown.timeoutOptions[index] : "Never";
}
}
Component.onCompleted: {
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acMonitorTimeout : SettingsData.batteryMonitorTimeout
const index = timeoutValues.indexOf(currentTimeout)
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acMonitorTimeout : SettingsData.batteryMonitorTimeout;
const index = timeoutValues.indexOf(currentTimeout);
currentValue = index >= 0 ? timeoutOptions[index] : "Never";
}
onValueChanged: value => {
const index = timeoutOptions.indexOf(value)
if (index >= 0) {
const timeout = timeoutValues[index]
if (powerCategory.currentIndex === 0) {
SettingsData.set("acMonitorTimeout", timeout)
} else {
SettingsData.set("batteryMonitorTimeout", timeout)
}
}
}
const index = timeoutOptions.indexOf(value);
if (index >= 0) {
const timeout = timeoutValues[index];
if (powerCategory.currentIndex === 0) {
SettingsData.set("acMonitorTimeout", timeout);
} else {
SettingsData.set("batteryMonitorTimeout", timeout);
}
}
}
}
DankDropdown {
@@ -246,29 +281,29 @@ Item {
Connections {
target: powerCategory
function onCurrentIndexChanged() {
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acSuspendTimeout : SettingsData.batterySuspendTimeout
const index = suspendDropdown.timeoutValues.indexOf(currentTimeout)
suspendDropdown.currentValue = index >= 0 ? suspendDropdown.timeoutOptions[index] : "Never"
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acSuspendTimeout : SettingsData.batterySuspendTimeout;
const index = suspendDropdown.timeoutValues.indexOf(currentTimeout);
suspendDropdown.currentValue = index >= 0 ? suspendDropdown.timeoutOptions[index] : "Never";
}
}
Component.onCompleted: {
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acSuspendTimeout : SettingsData.batterySuspendTimeout
const index = timeoutValues.indexOf(currentTimeout)
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acSuspendTimeout : SettingsData.batterySuspendTimeout;
const index = timeoutValues.indexOf(currentTimeout);
currentValue = index >= 0 ? timeoutOptions[index] : "Never";
}
onValueChanged: value => {
const index = timeoutOptions.indexOf(value)
if (index >= 0) {
const timeout = timeoutValues[index]
if (powerCategory.currentIndex === 0) {
SettingsData.set("acSuspendTimeout", timeout)
} else {
SettingsData.set("batterySuspendTimeout", timeout)
}
}
}
const index = timeoutOptions.indexOf(value);
if (index >= 0) {
const timeout = timeoutValues[index];
if (powerCategory.currentIndex === 0) {
SettingsData.set("acSuspendTimeout", timeout);
} else {
SettingsData.set("batterySuspendTimeout", timeout);
}
}
}
}
Column {
@@ -293,25 +328,25 @@ Item {
Connections {
target: powerCategory
function onCurrentIndexChanged() {
const behavior = powerCategory.currentIndex === 0 ? SettingsData.acSuspendBehavior : SettingsData.batterySuspendBehavior
suspendBehaviorSelector.currentIndex = behavior
const behavior = powerCategory.currentIndex === 0 ? SettingsData.acSuspendBehavior : SettingsData.batterySuspendBehavior;
suspendBehaviorSelector.currentIndex = behavior;
}
}
Component.onCompleted: {
const behavior = powerCategory.currentIndex === 0 ? SettingsData.acSuspendBehavior : SettingsData.batterySuspendBehavior
currentIndex = behavior
const behavior = powerCategory.currentIndex === 0 ? SettingsData.acSuspendBehavior : SettingsData.batterySuspendBehavior;
currentIndex = behavior;
}
onSelectionChanged: (index, selected) => {
if (selected) {
if (powerCategory.currentIndex === 0) {
SettingsData.set("acSuspendBehavior", index)
} else {
SettingsData.set("batterySuspendBehavior", index)
}
}
}
if (selected) {
if (powerCategory.currentIndex === 0) {
SettingsData.set("acSuspendBehavior", index);
} else {
SettingsData.set("batterySuspendBehavior", index);
}
}
}
}
}
@@ -384,17 +419,17 @@ Item {
property var actionValues: ["reboot", "logout", "poweroff", "lock", "suspend", "restart", "hibernate"]
Component.onCompleted: {
const currentAction = SettingsData.powerMenuDefaultAction || "logout"
const index = actionValues.indexOf(currentAction)
currentValue = index >= 0 ? options[index] : "Log Out"
const currentAction = SettingsData.powerMenuDefaultAction || "logout";
const index = actionValues.indexOf(currentAction);
currentValue = index >= 0 ? options[index] : "Log Out";
}
onValueChanged: value => {
const index = options.indexOf(value)
if (index >= 0) {
SettingsData.set("powerMenuDefaultAction", actionValues[index])
}
}
const index = options.indexOf(value);
if (index >= 0) {
SettingsData.set("powerMenuDefaultAction", actionValues[index]);
}
}
}
Column {
@@ -406,14 +441,14 @@ Item {
text: I18n.tr("Show Reboot")
checked: SettingsData.powerMenuActions.includes("reboot")
onToggled: checked => {
let actions = [...SettingsData.powerMenuActions]
if (checked && !actions.includes("reboot")) {
actions.push("reboot")
} else if (!checked) {
actions = actions.filter(a => a !== "reboot")
}
SettingsData.set("powerMenuActions", actions)
}
let actions = [...SettingsData.powerMenuActions];
if (checked && !actions.includes("reboot")) {
actions.push("reboot");
} else if (!checked) {
actions = actions.filter(a => a !== "reboot");
}
SettingsData.set("powerMenuActions", actions);
}
}
DankToggle {
@@ -421,14 +456,14 @@ Item {
text: I18n.tr("Show Log Out")
checked: SettingsData.powerMenuActions.includes("logout")
onToggled: checked => {
let actions = [...SettingsData.powerMenuActions]
if (checked && !actions.includes("logout")) {
actions.push("logout")
} else if (!checked) {
actions = actions.filter(a => a !== "logout")
}
SettingsData.set("powerMenuActions", actions)
}
let actions = [...SettingsData.powerMenuActions];
if (checked && !actions.includes("logout")) {
actions.push("logout");
} else if (!checked) {
actions = actions.filter(a => a !== "logout");
}
SettingsData.set("powerMenuActions", actions);
}
}
DankToggle {
@@ -436,14 +471,14 @@ Item {
text: I18n.tr("Show Power Off")
checked: SettingsData.powerMenuActions.includes("poweroff")
onToggled: checked => {
let actions = [...SettingsData.powerMenuActions]
if (checked && !actions.includes("poweroff")) {
actions.push("poweroff")
} else if (!checked) {
actions = actions.filter(a => a !== "poweroff")
}
SettingsData.set("powerMenuActions", actions)
}
let actions = [...SettingsData.powerMenuActions];
if (checked && !actions.includes("poweroff")) {
actions.push("poweroff");
} else if (!checked) {
actions = actions.filter(a => a !== "poweroff");
}
SettingsData.set("powerMenuActions", actions);
}
}
DankToggle {
@@ -451,14 +486,14 @@ Item {
text: I18n.tr("Show Lock")
checked: SettingsData.powerMenuActions.includes("lock")
onToggled: checked => {
let actions = [...SettingsData.powerMenuActions]
if (checked && !actions.includes("lock")) {
actions.push("lock")
} else if (!checked) {
actions = actions.filter(a => a !== "lock")
}
SettingsData.set("powerMenuActions", actions)
}
let actions = [...SettingsData.powerMenuActions];
if (checked && !actions.includes("lock")) {
actions.push("lock");
} else if (!checked) {
actions = actions.filter(a => a !== "lock");
}
SettingsData.set("powerMenuActions", actions);
}
}
DankToggle {
@@ -466,14 +501,14 @@ Item {
text: I18n.tr("Show Suspend")
checked: SettingsData.powerMenuActions.includes("suspend")
onToggled: checked => {
let actions = [...SettingsData.powerMenuActions]
if (checked && !actions.includes("suspend")) {
actions.push("suspend")
} else if (!checked) {
actions = actions.filter(a => a !== "suspend")
}
SettingsData.set("powerMenuActions", actions)
}
let actions = [...SettingsData.powerMenuActions];
if (checked && !actions.includes("suspend")) {
actions.push("suspend");
} else if (!checked) {
actions = actions.filter(a => a !== "suspend");
}
SettingsData.set("powerMenuActions", actions);
}
}
DankToggle {
@@ -482,14 +517,14 @@ Item {
description: I18n.tr("Restart the DankMaterialShell")
checked: SettingsData.powerMenuActions.includes("restart")
onToggled: checked => {
let actions = [...SettingsData.powerMenuActions]
if (checked && !actions.includes("restart")) {
actions.push("restart")
} else if (!checked) {
actions = actions.filter(a => a !== "restart")
}
SettingsData.set("powerMenuActions", actions)
}
let actions = [...SettingsData.powerMenuActions];
if (checked && !actions.includes("restart")) {
actions.push("restart");
} else if (!checked) {
actions = actions.filter(a => a !== "restart");
}
SettingsData.set("powerMenuActions", actions);
}
}
DankToggle {
@@ -499,14 +534,14 @@ Item {
checked: SettingsData.powerMenuActions.includes("hibernate")
visible: SessionService.hibernateSupported
onToggled: checked => {
let actions = [...SettingsData.powerMenuActions]
if (checked && !actions.includes("hibernate")) {
actions.push("hibernate")
} else if (!checked) {
actions = actions.filter(a => a !== "hibernate")
}
SettingsData.set("powerMenuActions", actions)
}
let actions = [...SettingsData.powerMenuActions];
if (checked && !actions.includes("hibernate")) {
actions.push("hibernate");
} else if (!checked) {
actions = actions.filter(a => a !== "hibernate");
}
SettingsData.set("powerMenuActions", actions);
}
}
}
}
@@ -612,12 +647,12 @@ Item {
Component.onCompleted: {
if (SettingsData.customPowerActionLock) {
text = SettingsData.customPowerActionLock
text = SettingsData.customPowerActionLock;
}
}
onTextEdited: {
SettingsData.set("customPowerActionLock", text.trim())
SettingsData.set("customPowerActionLock", text.trim());
}
}
}
@@ -644,12 +679,12 @@ Item {
Component.onCompleted: {
if (SettingsData.customPowerActionLogout) {
text = SettingsData.customPowerActionLogout
text = SettingsData.customPowerActionLogout;
}
}
onTextEdited: {
SettingsData.set("customPowerActionLogout", text.trim())
SettingsData.set("customPowerActionLogout", text.trim());
}
}
}
@@ -676,12 +711,12 @@ Item {
Component.onCompleted: {
if (SettingsData.customPowerActionSuspend) {
text = SettingsData.customPowerActionSuspend
text = SettingsData.customPowerActionSuspend;
}
}
onTextEdited: {
SettingsData.set("customPowerActionSuspend", text.trim())
SettingsData.set("customPowerActionSuspend", text.trim());
}
}
}
@@ -708,12 +743,12 @@ Item {
Component.onCompleted: {
if (SettingsData.customPowerActionHibernate) {
text = SettingsData.customPowerActionHibernate
text = SettingsData.customPowerActionHibernate;
}
}
onTextEdited: {
SettingsData.set("customPowerActionHibernate", text.trim())
SettingsData.set("customPowerActionHibernate", text.trim());
}
}
}
@@ -740,12 +775,12 @@ Item {
Component.onCompleted: {
if (SettingsData.customPowerActionReboot) {
text = SettingsData.customPowerActionReboot
text = SettingsData.customPowerActionReboot;
}
}
onTextEdited: {
SettingsData.set("customPowerActionReboot", text.trim())
SettingsData.set("customPowerActionReboot", text.trim());
}
}
}
@@ -772,12 +807,12 @@ Item {
Component.onCompleted: {
if (SettingsData.customPowerActionPowerOff) {
text = SettingsData.customPowerActionPowerOff
text = SettingsData.customPowerActionPowerOff;
}
}
onTextEdited: {
SettingsData.set("customPowerActionPowerOff", text.trim())
SettingsData.set("customPowerActionPowerOff", text.trim());
}
}
}
@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Effects
import qs.Common
import qs.Services
import qs.Widgets
@@ -81,7 +80,6 @@ Rectangle {
}
}
}
}
Rectangle {
@@ -105,11 +103,8 @@ Rectangle {
return PortalService.setProfileImage("");
}
}
}
}
}
MouseArea {
@@ -121,7 +116,6 @@ Rectangle {
propagateComposedEvents: true
acceptedButtons: Qt.NoButton
}
}
Column {
@@ -145,9 +139,6 @@ Rectangle {
elide: Text.ElideRight
width: parent.width
}
}
}
}
+20 -43
View File
@@ -13,7 +13,7 @@ FocusScope {
Rectangle {
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: 0
anchors.rightMargin: (parentModal && parentModal.isCompactMode) ? Theme.spacingS : (32 + Theme.spacingS)
anchors.bottomMargin: 0
anchors.topMargin: 0
color: "transparent"
@@ -30,15 +30,13 @@ FocusScope {
PersonalizationTab {
parentModal: root.parentModal
}
}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -49,15 +47,13 @@ FocusScope {
visible: active
focus: active
sourceComponent: TimeWeatherTab {
}
sourceComponent: TimeWeatherTab {}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -74,10 +70,9 @@ FocusScope {
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -88,15 +83,13 @@ FocusScope {
visible: active
focus: active
sourceComponent: WidgetTweaksTab {
}
sourceComponent: WidgetTweaksTab {}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -108,17 +101,14 @@ FocusScope {
focus: active
sourceComponent: Component {
DockTab {
}
DockTab {}
}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -129,15 +119,13 @@ FocusScope {
visible: active
focus: active
sourceComponent: DisplaysTab {
}
sourceComponent: DisplaysTab {}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -148,15 +136,13 @@ FocusScope {
visible: active
focus: active
sourceComponent: LauncherTab {
}
sourceComponent: LauncherTab {}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -167,15 +153,13 @@ FocusScope {
visible: active
focus: active
sourceComponent: ThemeColorsTab {
}
sourceComponent: ThemeColorsTab {}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -186,15 +170,13 @@ FocusScope {
visible: active
focus: active
sourceComponent: PowerSettings {
}
sourceComponent: PowerSettings {}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -211,10 +193,9 @@ FocusScope {
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
@@ -225,17 +206,13 @@ FocusScope {
visible: active
focus: active
sourceComponent: AboutTab {
}
sourceComponent: AboutTab {}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus())
Qt.callLater(() => item.forceActiveFocus());
}
}
}
}
}
+1 -3
View File
@@ -234,9 +234,7 @@ FloatingWindow {
SettingsContent {
id: content
width: Math.min(550, parent.width - Theme.spacingL * 2)
height: parent.height
anchors.horizontalCenter: parent.horizontalCenter
anchors.fill: parent
parentModal: settingsModal
currentIndex: settingsModal.currentTabIndex
}
@@ -20,19 +20,50 @@ Item {
Ref {
service: CavaService
}
}
}
readonly property real maxBarHeight: Theme.iconSize - 2
readonly property real minBarHeight: 3
readonly property real heightRange: maxBarHeight - minBarHeight
property var barHeights: [minBarHeight, minBarHeight, minBarHeight, minBarHeight, minBarHeight, minBarHeight]
Timer {
id: fallbackTimer
running: !CavaService.cavaAvailable && isPlaying
interval: 256
interval: 500
repeat: true
onTriggered: {
CavaService.values = [Math.random() * 40 + 10, Math.random() * 60 + 20, Math.random() * 50 + 15, Math.random() * 35 + 20, Math.random() * 45 + 15, Math.random() * 55 + 25];
CavaService.values = [Math.random() * 20 + 5, Math.random() * 25 + 8, Math.random() * 22 + 6, Math.random() * 20 + 5, Math.random() * 22 + 6, Math.random() * 25 + 8];
}
}
Connections {
target: CavaService
function onValuesChanged() {
if (!root.isPlaying) {
root.barHeights = [root.minBarHeight, root.minBarHeight, root.minBarHeight, root.minBarHeight, root.minBarHeight, root.minBarHeight];
return;
}
const newHeights = [];
for (let i = 0; i < 6; i++) {
if (CavaService.values.length <= i) {
newHeights.push(root.minBarHeight);
continue;
}
const rawLevel = CavaService.values[i];
if (rawLevel <= 0) {
newHeights.push(root.minBarHeight);
} else if (rawLevel >= 100) {
newHeights.push(root.maxBarHeight);
} else {
newHeights.push(root.minBarHeight + Math.sqrt(rawLevel * 0.01) * root.heightRange);
}
}
root.barHeights = newHeights;
}
}
@@ -45,33 +76,19 @@ Item {
Rectangle {
width: 2
height: {
if (root.isPlaying && CavaService.values.length > index) {
const rawLevel = CavaService.values[index] || 0;
const scaledLevel = Math.sqrt(Math.min(Math.max(rawLevel, 0), 100) / 100) * 100;
const maxHeight = Theme.iconSize - 2;
const minHeight = 3;
return minHeight + (scaledLevel / 100) * (maxHeight - minHeight);
}
return 3;
}
height: root.barHeights[index]
radius: 1.5
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
Behavior on height {
enabled: root.isPlaying && !CavaService.cavaAvailable
NumberAnimation {
duration: Anims.durShort
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.standardDecel
duration: 100
easing.type: Easing.Linear
}
}
}
}
}
}
@@ -25,8 +25,8 @@ BasePill {
content: Component {
Item {
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : cpuContentRoot.implicitWidth
implicitHeight: root.isVerticalOrientation ? cpuColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : cpuContent.implicitWidth
implicitHeight: root.isVerticalOrientation ? cpuColumn.implicitHeight : cpuContent.implicitHeight
Column {
id: cpuColumn
@@ -65,79 +65,72 @@ BasePill {
}
}
Item {
id: cpuContentRoot
Row {
id: cpuContent
visible: !root.isVerticalOrientation
anchors.centerIn: parent
spacing: Theme.spacingXS
implicitWidth: cpuRow.implicitWidth
implicitHeight: cpuRow.implicitHeight
Row {
id: cpuRow
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
id: cpuIcon
name: "memory"
size: Theme.barIconSize(root.barThickness)
color: {
if (DgopService.cpuUsage > 80) {
return Theme.tempDanger;
}
if (DgopService.cpuUsage > 60) {
return Theme.tempWarning;
}
return Theme.widgetIconColor;
DankIcon {
id: cpuIcon
name: "memory"
size: Theme.barIconSize(root.barThickness)
color: {
if (DgopService.cpuUsage > 80) {
return Theme.tempDanger;
}
implicitWidth: size
implicitHeight: size
width: size
height: size
if (DgopService.cpuUsage > 60) {
return Theme.tempWarning;
}
return Theme.widgetIconColor;
}
Item {
id: textBox
implicitWidth: size
implicitHeight: size
width: size
height: size
}
implicitWidth: root.minimumWidth ? Math.max(cpuBaseline.width, cpuText.paintedWidth) : cpuText.paintedWidth
implicitHeight: cpuText.implicitHeight
Item {
id: textBox
width: implicitWidth
height: implicitHeight
implicitWidth: root.minimumWidth ? Math.max(cpuBaseline.width, cpuText.paintedWidth) : cpuText.paintedWidth
implicitHeight: cpuText.implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
width: implicitWidth
height: implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
StyledTextMetrics {
id: cpuBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
text: "88%"
}
StyledText {
id: cpuText
text: {
const v = DgopService.cpuUsage;
if (v === undefined || v === null || v === 0) {
return "--%";
}
return v.toFixed(0) + "%";
}
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
color: Theme.widgetTextColor
StyledTextMetrics {
id: cpuBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
text: "88%"
}
StyledText {
id: cpuText
text: {
const v = DgopService.cpuUsage;
if (v === undefined || v === null || v === 0) {
return "--%";
}
return v.toFixed(0) + "%";
}
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
color: Theme.widgetTextColor
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideNone
}
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideNone
}
}
}
@@ -25,8 +25,8 @@ BasePill {
content: Component {
Item {
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : cpuTempContentRoot.implicitWidth
implicitHeight: root.isVerticalOrientation ? cpuTempColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : cpuTempRow.implicitWidth
implicitHeight: root.isVerticalOrientation ? cpuTempColumn.implicitHeight : cpuTempRow.implicitHeight
Column {
id: cpuTempColumn
@@ -65,79 +65,72 @@ BasePill {
}
}
Item {
id: cpuTempContentRoot
Row {
id: cpuTempRow
visible: !root.isVerticalOrientation
anchors.centerIn: parent
spacing: Theme.spacingXS
implicitWidth: cpuTempRow.implicitWidth
implicitHeight: cpuTempRow.implicitHeight
Row {
id: cpuTempRow
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
id: cpuTempIcon
name: "device_thermostat"
size: Theme.barIconSize(root.barThickness)
color: {
if (DgopService.cpuTemperature > 85) {
return Theme.tempDanger;
}
if (DgopService.cpuTemperature > 69) {
return Theme.tempWarning;
}
return Theme.widgetIconColor;
DankIcon {
id: cpuTempIcon
name: "device_thermostat"
size: Theme.barIconSize(root.barThickness)
color: {
if (DgopService.cpuTemperature > 85) {
return Theme.tempDanger;
}
implicitWidth: size
implicitHeight: size
width: size
height: size
if (DgopService.cpuTemperature > 69) {
return Theme.tempWarning;
}
return Theme.widgetIconColor;
}
Item {
id: textBox
implicitWidth: size
implicitHeight: size
width: size
height: size
}
implicitWidth: root.minimumWidth ? Math.max(tempBaseline.width, cpuTempText.paintedWidth) : cpuTempText.paintedWidth
implicitHeight: cpuTempText.implicitHeight
Item {
id: textBox
width: implicitWidth
height: implicitHeight
implicitWidth: root.minimumWidth ? Math.max(tempBaseline.width, cpuTempText.paintedWidth) : cpuTempText.paintedWidth
implicitHeight: cpuTempText.implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
width: implicitWidth
height: implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
StyledTextMetrics {
id: tempBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
text: "88°"
}
StyledText {
id: cpuTempText
text: {
if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) {
return "--°";
}
return Math.round(DgopService.cpuTemperature) + "°";
}
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
color: Theme.widgetTextColor
StyledTextMetrics {
id: tempBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
text: "88°"
}
StyledText {
id: cpuTempText
text: {
if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) {
return "--°";
}
return Math.round(DgopService.cpuTemperature) + "°";
}
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
color: Theme.widgetTextColor
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideNone
}
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideNone
}
}
}
@@ -93,8 +93,8 @@ BasePill {
content: Component {
Item {
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : gpuTempContentRoot.implicitWidth
implicitHeight: root.isVerticalOrientation ? gpuTempColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : gpuTempRow.implicitWidth
implicitHeight: root.isVerticalOrientation ? gpuTempColumn.implicitHeight : gpuTempRow.implicitHeight
Column {
id: gpuTempColumn
@@ -133,79 +133,72 @@ BasePill {
}
}
Item {
id: gpuTempContentRoot
Row {
id: gpuTempRow
visible: !root.isVerticalOrientation
anchors.centerIn: parent
spacing: Theme.spacingXS
implicitWidth: gpuTempRow.implicitWidth
implicitHeight: gpuTempRow.implicitHeight
Row {
id: gpuTempRow
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
id: gpuTempIcon
name: "auto_awesome_mosaic"
size: Theme.barIconSize(root.barThickness)
color: {
if (root.displayTemp > 80) {
return Theme.tempDanger;
}
if (root.displayTemp > 65) {
return Theme.tempWarning;
}
return Theme.widgetIconColor;
DankIcon {
id: gpuTempIcon
name: "auto_awesome_mosaic"
size: Theme.barIconSize(root.barThickness)
color: {
if (root.displayTemp > 80) {
return Theme.tempDanger;
}
implicitWidth: size
implicitHeight: size
width: size
height: size
if (root.displayTemp > 65) {
return Theme.tempWarning;
}
return Theme.widgetIconColor;
}
Item {
id: textBox
implicitWidth: size
implicitHeight: size
width: size
height: size
}
implicitWidth: root.minimumWidth ? Math.max(gpuTempBaseline.width, gpuTempText.paintedWidth) : gpuTempText.paintedWidth
implicitHeight: gpuTempText.implicitHeight
Item {
id: textBox
width: implicitWidth
height: implicitHeight
implicitWidth: root.minimumWidth ? Math.max(gpuTempBaseline.width, gpuTempText.paintedWidth) : gpuTempText.paintedWidth
implicitHeight: gpuTempText.implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
width: implicitWidth
height: implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
StyledTextMetrics {
id: gpuTempBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
text: "88°"
}
StyledText {
id: gpuTempText
text: {
if (root.displayTemp === undefined || root.displayTemp === null || root.displayTemp === 0) {
return "--°";
}
return Math.round(root.displayTemp) + "°";
}
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
color: Theme.widgetTextColor
StyledTextMetrics {
id: gpuTempBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
text: "88°"
}
StyledText {
id: gpuTempText
text: {
if (root.displayTemp === undefined || root.displayTemp === null || root.displayTemp === 0) {
return "--°";
}
return Math.round(root.displayTemp) + "°";
}
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
color: Theme.widgetTextColor
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideNone
}
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideNone
}
}
}
+38 -13
View File
@@ -101,9 +101,24 @@ BasePill {
anchors.centerIn: parent
spacing: Theme.spacingXS
AudioVisualization {
Item {
width: 20
height: 20
anchors.horizontalCenter: parent.horizontalCenter
AudioVisualization {
anchors.fill: parent
visible: CavaService.cavaAvailable
}
DankIcon {
anchors.fill: parent
name: "music_note"
size: 20
color: Theme.primary
visible: !CavaService.cavaAvailable
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
@@ -165,28 +180,38 @@ BasePill {
id: mediaInfo
spacing: Theme.spacingXS
AudioVisualization {
Item {
width: 20
height: 20
anchors.verticalCenter: parent.verticalCenter
AudioVisualization {
anchors.fill: parent
visible: CavaService.cavaAvailable
}
DankIcon {
anchors.fill: parent
name: "music_note"
size: 20
color: Theme.primary
visible: !CavaService.cavaAvailable
}
}
Rectangle {
id: textContainer
readonly property string cachedIdentity: activePlayer ? (activePlayer.identity || "") : ""
readonly property string lowerIdentity: cachedIdentity.toLowerCase()
readonly property bool isWebMedia: lowerIdentity.includes("firefox") || lowerIdentity.includes("chrome") || lowerIdentity.includes("chromium") || lowerIdentity.includes("edge") || lowerIdentity.includes("safari")
property string displayText: {
if (!activePlayer || !activePlayer.trackTitle) {
return "";
}
let identity = activePlayer.identity || "";
let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium") || identity.toLowerCase().includes("edge") || identity.toLowerCase().includes("safari");
let title = "";
let subtitle = "";
if (isWebMedia && activePlayer.trackTitle) {
title = activePlayer.trackTitle;
subtitle = activePlayer.trackArtist || identity;
} else {
title = activePlayer.trackTitle || "Unknown Track";
subtitle = activePlayer.trackArtist || "";
}
const title = isWebMedia ? activePlayer.trackTitle : (activePlayer.trackTitle || "Unknown Track");
const subtitle = isWebMedia ? (activePlayer.trackArtist || cachedIdentity) : (activePlayer.trackArtist || "");
return subtitle.length > 0 ? title + " • " + subtitle : title;
}
@@ -27,8 +27,8 @@ BasePill {
content: Component {
Item {
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : ramContentRoot.implicitWidth
implicitHeight: root.isVerticalOrientation ? ramColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : ramContent.implicitWidth
implicitHeight: root.isVerticalOrientation ? ramColumn.implicitHeight : ramContent.implicitHeight
Column {
id: ramColumn
@@ -75,93 +75,86 @@ BasePill {
}
}
Item {
id: ramContentRoot
Row {
id: ramContent
visible: !root.isVerticalOrientation
anchors.centerIn: parent
spacing: Theme.spacingXS
implicitWidth: ramRow.implicitWidth
implicitHeight: ramRow.implicitHeight
Row {
id: ramRow
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
id: ramIcon
name: "developer_board"
size: Theme.barIconSize(root.barThickness)
color: {
if (DgopService.memoryUsage > 90) {
return Theme.tempDanger;
}
if (DgopService.memoryUsage > 75) {
return Theme.tempWarning;
}
return Theme.widgetIconColor;
DankIcon {
id: ramIcon
name: "developer_board"
size: Theme.barIconSize(root.barThickness)
color: {
if (DgopService.memoryUsage > 90) {
return Theme.tempDanger;
}
implicitWidth: size
implicitHeight: size
width: size
height: size
if (DgopService.memoryUsage > 75) {
return Theme.tempWarning;
}
return Theme.widgetIconColor;
}
Item {
id: textBox
implicitWidth: size
implicitHeight: size
width: size
height: size
}
implicitWidth: root.minimumWidth ? Math.max(ramBaseline.width, ramText.paintedWidth) : ramText.paintedWidth
implicitHeight: ramText.implicitHeight
Item {
id: textBox
width: implicitWidth
height: implicitHeight
implicitWidth: root.minimumWidth ? Math.max(ramBaseline.width, ramText.paintedWidth) : ramText.paintedWidth
implicitHeight: ramText.implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
width: implicitWidth
height: implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
StyledTextMetrics {
id: ramBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
text: {
if (!root.showSwap) {
return "88%";
}
if (root.swapUsage < 10) {
return "88% · 0%";
}
return "88% · 88%";
StyledTextMetrics {
id: ramBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
text: {
if (!root.showSwap) {
return "88%";
}
}
StyledText {
id: ramText
text: {
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
return "--%";
}
let ramText = DgopService.memoryUsage.toFixed(0) + "%";
if (root.showSwap && DgopService.totalSwapKB > 0) {
return ramText + " · " + root.swapUsage.toFixed(0) + "%";
}
return ramText;
if (root.swapUsage < 10) {
return "88% · 0%";
}
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
color: Theme.widgetTextColor
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideNone
wrapMode: Text.NoWrap
return "88% · 88%";
}
}
StyledText {
id: ramText
text: {
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
return "--%";
}
let ramText = DgopService.memoryUsage.toFixed(0) + "%";
if (root.showSwap && DgopService.totalSwapKB > 0) {
return ramText + " · " + root.swapUsage.toFixed(0) + "%";
}
return ramText;
}
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
color: Theme.widgetTextColor
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideNone
wrapMode: Text.NoWrap
}
}
}
}
@@ -115,34 +115,48 @@ Item {
function getHyprlandWorkspaces() {
const workspaces = Hyprland.workspaces?.values || [];
if (workspaces.length === 0)
if (workspaces.length === 0) {
return [
{
id: 1,
name: "1"
}
];
const filtered = workspaces.filter(ws => ws.id > -1);
if (filtered.length === 0)
return [
{
id: 1,
name: "1"
}
];
if (!root.screenName || !SettingsData.workspacesPerMonitor) {
return filtered.slice().sort((a, b) => a.id - b.id);
}
const monitorWorkspaces = filtered.filter(ws => ws.monitor?.name === root.screenName);
return monitorWorkspaces.length > 0 ? monitorWorkspaces.sort((a, b) => a.id - b.id) : [
{
id: 1,
name: "1"
}
];
let filtered = workspaces.filter(ws => ws.id > -1);
if (filtered.length === 0) {
return [
{
id: 1,
name: "1"
}
];
}
if (!root.screenName || !SettingsData.workspacesPerMonitor) {
filtered = filtered.slice().sort((a, b) => a.id - b.id);
} else {
const monitorWorkspaces = filtered.filter(ws => ws.monitor?.name === root.screenName);
filtered = monitorWorkspaces.length > 0 ? monitorWorkspaces.sort((a, b) => a.id - b.id) : [
{
id: 1,
name: "1"
}
];
}
if (!SettingsData.showOccupiedWorkspacesOnly) {
return filtered;
}
const hyprlandToplevels = Array.from(Hyprland.toplevels?.values || []);
const activeWsId = root.currentWorkspace;
return filtered.filter(ws => {
if (ws.id === activeWsId)
return true;
return hyprlandToplevels.some(tl => tl.workspace?.id === ws.id);
});
}
function getHyprlandActiveWorkspace() {
@@ -287,12 +301,26 @@ Item {
return [1, 2];
}
let workspaces;
if (!root.screenName || !SettingsData.workspacesPerMonitor) {
return NiriService.getCurrentOutputWorkspaceNumbers();
workspaces = NiriService.getCurrentOutputWorkspaceNumbers();
} else {
const displayWorkspaces = NiriService.allWorkspaces.filter(ws => ws.output === root.screenName).map(ws => ws.idx + 1);
workspaces = displayWorkspaces.length > 0 ? displayWorkspaces : [1, 2];
}
const displayWorkspaces = NiriService.allWorkspaces.filter(ws => ws.output === root.screenName).map(ws => ws.idx + 1);
return displayWorkspaces.length > 0 ? displayWorkspaces : [1, 2];
if (!SettingsData.showOccupiedWorkspacesOnly) {
return workspaces;
}
return workspaces.filter(wsNum => {
const workspace = NiriService.allWorkspaces.find(w => w.idx + 1 === wsNum && w.output === root.screenName);
if (!workspace)
return false;
if (workspace.is_active)
return true;
return NiriService.windows?.some(win => win.workspace_id === workspace.id) ?? false;
});
}
function getNiriActiveWorkspace() {
@@ -0,0 +1,102 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Common
PanelWindow {
id: root
property bool active: false
signal fadeCompleted
signal fadeCancelled
visible: active
color: "transparent"
WlrLayershell.namespace: "dms:fade-to-lock"
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: active ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
anchors {
left: true
right: true
top: true
bottom: true
}
Rectangle {
id: fadeOverlay
anchors.fill: parent
color: "black"
opacity: 0
onOpacityChanged: {
if (opacity >= 0.99 && root.active) {
root.fadeCompleted();
}
}
}
SequentialAnimation {
id: fadeSeq
running: false
NumberAnimation {
target: fadeOverlay
property: "opacity"
from: 0.0
to: 1.0
duration: SettingsData.fadeToLockGracePeriod * 1000
easing.type: Easing.OutCubic
}
}
function startFade() {
if (!SettingsData.fadeToLockEnabled)
return;
active = true;
fadeOverlay.opacity = 0.0;
fadeSeq.stop();
fadeSeq.start();
}
function cancelFade() {
fadeSeq.stop();
fadeOverlay.opacity = 0.0;
active = false;
fadeCancelled();
}
MouseArea {
anchors.fill: parent
enabled: root.active
onClicked: root.cancelFade()
onPressed: root.cancelFade()
}
FocusScope {
anchors.fill: parent
focus: root.active
Keys.onPressed: event => {
root.cancelFade();
event.accepted = true;
}
}
Component.onCompleted: {
if (active) {
forceActiveFocus();
}
}
onActiveChanged: {
if (active) {
forceActiveFocus();
}
}
}
+28 -28
View File
@@ -15,23 +15,23 @@ Scope {
property bool processingExternalEvent: false
Component.onCompleted: {
IdleService.lockComponent = this
IdleService.lockComponent = this;
}
function lock() {
if (SettingsData.customPowerActionLock && SettingsData.customPowerActionLock.length > 0) {
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLock])
return
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLock]);
return;
}
if (!processingExternalEvent && SettingsData.loginctlLockIntegration && DMSService.isConnected) {
DMSService.lockSession(response => {
if (response.error) {
console.warn("Lock: Failed to call loginctl.lock:", response.error)
shouldLock = true
console.warn("Lock: Failed to call loginctl.lock:", response.error);
shouldLock = true;
}
})
});
} else {
shouldLock = true
shouldLock = true;
}
}
@@ -39,32 +39,32 @@ Scope {
if (!processingExternalEvent && SettingsData.loginctlLockIntegration && DMSService.isConnected) {
DMSService.unlockSession(response => {
if (response.error) {
console.warn("Lock: Failed to call loginctl.unlock:", response.error)
shouldLock = false
console.warn("Lock: Failed to call loginctl.unlock:", response.error);
shouldLock = false;
}
})
});
} else {
shouldLock = false
shouldLock = false;
}
}
function activate() {
lock()
lock();
}
Connections {
target: SessionService
function onSessionLocked() {
processingExternalEvent = true
shouldLock = true
processingExternalEvent = false
processingExternalEvent = true;
shouldLock = true;
processingExternalEvent = false;
}
function onSessionUnlocked() {
processingExternalEvent = true
shouldLock = false
processingExternalEvent = false
processingExternalEvent = true;
shouldLock = false;
processingExternalEvent = false;
}
}
@@ -72,7 +72,7 @@ Scope {
target: IdleService
function onLockRequested() {
lock()
lock();
}
}
@@ -93,11 +93,11 @@ Scope {
screenName: lockSurface.screen?.name ?? ""
isLocked: shouldLock
onUnlockRequested: {
root.unlock()
root.unlock();
}
onPasswordChanged: newPassword => {
root.sharedPasswordBuffer = newPassword
}
root.sharedPasswordBuffer = newPassword;
}
}
}
}
@@ -113,21 +113,21 @@ Scope {
if (!root.processingExternalEvent && SettingsData.loginctlLockIntegration && DMSService.isConnected) {
DMSService.lockSession(response => {
if (response.error) {
console.warn("Lock: Failed to call loginctl.lock:", response.error)
root.shouldLock = true
console.warn("Lock: Failed to call loginctl.lock:", response.error);
root.shouldLock = true;
}
})
});
} else {
root.shouldLock = true
root.shouldLock = true;
}
}
function demo() {
demoWindow.showDemo()
demoWindow.showDemo();
}
function isLocked(): bool {
return sessionLock.locked
return sessionLock.locked;
}
}
}
+162 -154
View File
@@ -1,6 +1,5 @@
pragma ComponentBehavior: Bound
import QtCore
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
@@ -30,52 +29,60 @@ Item {
signal unlockRequested
function pickRandomFact() {
randomFact = Facts.getRandomFact()
randomFact = Facts.getRandomFact();
}
Component.onCompleted: {
if (demoMode) {
pickRandomFact()
pickRandomFact();
}
WeatherService.addRef()
UserInfoService.refreshUserInfo()
WeatherService.addRef();
UserInfoService.refreshUserInfo();
if (CompositorService.isHyprland) {
updateHyprlandLayout()
hyprlandLayoutUpdateTimer.start()
updateHyprlandLayout();
hyprlandLayoutUpdateTimer.start();
}
lockerReadyArmed = true
lockerReadyArmed = true;
}
onDemoModeChanged: {
if (demoMode) {
pickRandomFact()
pickRandomFact();
}
}
Component.onDestruction: {
WeatherService.removeRef()
WeatherService.removeRef();
if (CompositorService.isHyprland) {
hyprlandLayoutUpdateTimer.stop()
hyprlandLayoutUpdateTimer.stop();
}
}
function sendLockerReadyOnce() {
if (lockerReadySent) return;
if (root.unlocking) return;
if (lockerReadySent)
return;
if (root.unlocking)
return;
lockerReadySent = true;
if (SessionService.loginctlAvailable && DMSService.apiVersion >= 2) {
DMSService.sendRequest("loginctl.lockerReady", null, resp => {
if (resp?.error) console.warn("lockerReady failed:", resp.error)
else console.log("lockerReady sent (afterAnimating/afterRendering)");
if (resp?.error)
console.warn("lockerReady failed:", resp.error);
else
console.log("lockerReady sent (afterAnimating/afterRendering)");
});
}
}
function maybeSend() {
if (!lockerReadyArmed) return;
if (root.unlocking) return;
if (!root.visible || root.opacity <= 0) return;
if (!lockerReadyArmed)
return;
if (root.unlocking)
return;
if (!root.visible || root.opacity <= 0)
return;
Qt.callLater(() => {
if (root.visible && root.opacity > 0 && !root.unlocking)
sendLockerReadyOnce();
@@ -86,8 +93,12 @@ Item {
target: root.Window.window
enabled: target !== null
function onAfterAnimating() { maybeSend(); }
function onAfterRendering() { maybeSend(); }
function onAfterAnimating() {
maybeSend();
}
function onAfterRendering() {
maybeSend();
}
}
onVisibleChanged: maybeSend()
@@ -95,7 +106,7 @@ Item {
function updateHyprlandLayout() {
if (CompositorService.isHyprland) {
hyprlandLayoutProcess.running = true
hyprlandLayoutProcess.running = true;
}
}
@@ -106,27 +117,27 @@ Item {
stdout: StdioCollector {
onStreamFinished: {
try {
const data = JSON.parse(text)
const mainKeyboard = data.keyboards.find(kb => kb.main === true)
hyprlandKeyboard = mainKeyboard.name
const data = JSON.parse(text);
const mainKeyboard = data.keyboards.find(kb => kb.main === true);
hyprlandKeyboard = mainKeyboard.name;
if (mainKeyboard && mainKeyboard.active_keymap) {
const parts = mainKeyboard.active_keymap.split(" ")
const parts = mainKeyboard.active_keymap.split(" ");
if (parts.length > 0) {
hyprlandCurrentLayout = parts[0].substring(0, 2).toUpperCase()
hyprlandCurrentLayout = parts[0].substring(0, 2).toUpperCase();
} else {
hyprlandCurrentLayout = mainKeyboard.active_keymap.substring(0, 2).toUpperCase()
hyprlandCurrentLayout = mainKeyboard.active_keymap.substring(0, 2).toUpperCase();
}
} else {
hyprlandCurrentLayout = ""
hyprlandCurrentLayout = "";
}
if (mainKeyboard && mainKeyboard.layout_names) {
hyprlandLayoutCount = mainKeyboard.layout_names.length
hyprlandLayoutCount = mainKeyboard.layout_names.length;
} else {
hyprlandLayoutCount = 0
hyprlandLayoutCount = 0;
}
} catch (e) {
hyprlandCurrentLayout = ""
hyprlandLayoutCount = 0
hyprlandCurrentLayout = "";
hyprlandLayoutCount = 0;
}
}
}
@@ -143,8 +154,8 @@ Item {
Loader {
anchors.fill: parent
active: {
var currentWallpaper = SessionData.getMonitorWallpaper(screenName)
return !currentWallpaper || (currentWallpaper && currentWallpaper.startsWith("#"))
var currentWallpaper = SessionData.getMonitorWallpaper(screenName);
return !currentWallpaper || (currentWallpaper && currentWallpaper.startsWith("#"));
}
asynchronous: true
@@ -158,8 +169,8 @@ Item {
anchors.fill: parent
source: {
var currentWallpaper = SessionData.getMonitorWallpaper(screenName)
return (currentWallpaper && !currentWallpaper.startsWith("#")) ? currentWallpaper : ""
var currentWallpaper = SessionData.getMonitorWallpaper(screenName);
return (currentWallpaper && !currentWallpaper.startsWith("#")) ? currentWallpaper : "";
}
fillMode: Theme.getFillMode(SettingsData.wallpaperFillMode)
smooth: true
@@ -213,8 +224,8 @@ Item {
spacing: 0
property string fullTimeStr: {
const format = SettingsData.getEffectiveTimeFormat()
return systemClock.date.toLocaleTimeString(Qt.locale(), format)
const format = SettingsData.getEffectiveTimeFormat();
return systemClock.date.toLocaleTimeString(Qt.locale(), format);
}
property var timeParts: fullTimeStr.split(':')
property string hours: timeParts[0] || ""
@@ -222,8 +233,8 @@ Item {
property string secondsWithAmPm: timeParts.length > 2 ? timeParts[2] : ""
property string seconds: secondsWithAmPm.replace(/\s*(AM|PM|am|pm)$/i, '')
property string ampm: {
const match = fullTimeStr.match(/\s*(AM|PM|am|pm)$/i)
return match ? match[0].trim() : ""
const match = fullTimeStr.match(/\s*(AM|PM|am|pm)$/i);
return match ? match[0].trim() : "";
}
property bool hasSeconds: timeParts.length > 2
@@ -322,9 +333,9 @@ Item {
anchors.verticalCenterOffset: -25
text: {
if (SettingsData.lockDateFormat && SettingsData.lockDateFormat.length > 0) {
return systemClock.date.toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat)
return systemClock.date.toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat);
}
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat)
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat);
}
font.pixelSize: Theme.fontSizeXLarge
color: "white"
@@ -347,14 +358,14 @@ Item {
Layout.preferredHeight: 60
imageSource: {
if (PortalService.profileImage === "") {
return ""
return "";
}
if (PortalService.profileImage.startsWith("/")) {
return "file://" + PortalService.profileImage
return "file://" + PortalService.profileImage;
}
return PortalService.profileImage
return PortalService.profileImage;
}
fallbackIcon: "person"
}
@@ -414,20 +425,20 @@ Item {
anchors.fill: parent
anchors.leftMargin: lockIconContainer.width + Theme.spacingM * 2
anchors.rightMargin: {
let margin = Theme.spacingM
let margin = Theme.spacingM;
if (loadingSpinner.visible) {
margin += loadingSpinner.width
margin += loadingSpinner.width;
}
if (enterButton.visible) {
margin += enterButton.width + 2
margin += enterButton.width + 2;
}
if (virtualKeyboardButton.visible) {
margin += virtualKeyboardButton.width
margin += virtualKeyboardButton.width;
}
if (revealButton.visible) {
margin += revealButton.width
margin += revealButton.width;
}
return margin
return margin;
}
opacity: 0
focus: true
@@ -436,36 +447,36 @@ Item {
echoMode: parent.showPassword ? TextInput.Normal : TextInput.Password
onTextChanged: {
if (!demoMode) {
root.passwordBuffer = text
root.passwordBuffer = text;
}
}
onAccepted: {
if (!demoMode && !pam.passwd.active) {
console.log("Enter pressed, starting PAM authentication")
pam.passwd.start()
console.log("Enter pressed, starting PAM authentication");
pam.passwd.start();
}
}
Keys.onPressed: event => {
if (demoMode) {
return
}
if (demoMode) {
return;
}
if (pam.passwd.active) {
console.log("PAM is active, ignoring input")
event.accepted = true
return
}
}
if (pam.passwd.active) {
console.log("PAM is active, ignoring input");
event.accepted = true;
return;
}
}
Component.onCompleted: {
if (!demoMode) {
forceActiveFocus()
forceActiveFocus();
}
}
onVisibleChanged: {
if (visible && !demoMode) {
forceActiveFocus()
forceActiveFocus();
}
}
@@ -473,9 +484,9 @@ Item {
if (!activeFocus && !demoMode && visible && passwordField && !powerMenu.isVisible) {
Qt.callLater(() => {
if (passwordField && passwordField.forceActiveFocus) {
passwordField.forceActiveFocus()
passwordField.forceActiveFocus();
}
})
});
}
}
@@ -483,9 +494,9 @@ Item {
if (enabled && !demoMode && visible && passwordField && !powerMenu.isVisible) {
Qt.callLater(() => {
if (passwordField && passwordField.forceActiveFocus) {
passwordField.forceActiveFocus()
passwordField.forceActiveFocus();
}
})
});
}
}
}
@@ -506,15 +517,15 @@ Item {
anchors.verticalCenter: parent.verticalCenter
text: {
if (demoMode) {
return ""
return "";
}
if (root.unlocking) {
return "Unlocking..."
return "Unlocking...";
}
if (pam.passwd.active) {
return "Authenticating..."
return "Authenticating...";
}
return "Password..."
return "Password...";
}
color: root.unlocking ? Theme.primary : (pam.passwd.active ? Theme.primary : Theme.outline)
font.pixelSize: Theme.fontSizeMedium
@@ -543,12 +554,12 @@ Item {
anchors.verticalCenter: parent.verticalCenter
text: {
if (demoMode) {
return "••••••••"
return "••••••••";
}
if (parent.showPassword) {
return root.passwordBuffer
return root.passwordBuffer;
}
return "•".repeat(root.passwordBuffer.length)
return "•".repeat(root.passwordBuffer.length);
}
color: Theme.surfaceText
font.pixelSize: parent.showPassword ? Theme.fontSizeMedium : Theme.fontSizeLarge
@@ -589,9 +600,9 @@ Item {
enabled: visible
onClicked: {
if (keyboardController.isKeyboardActive) {
keyboardController.hide()
keyboardController.hide();
} else {
keyboardController.show()
keyboardController.show();
}
}
}
@@ -690,8 +701,8 @@ Item {
enabled: !demoMode
onClicked: {
if (!demoMode) {
console.log("Enter button clicked, starting PAM authentication")
pam.passwd.start()
console.log("Enter button clicked, starting PAM authentication");
pam.passwd.start();
}
}
@@ -717,15 +728,15 @@ Item {
Layout.preferredHeight: 20
text: {
if (root.pamState === "error") {
return "Authentication error - try again"
return "Authentication error - try again";
}
if (root.pamState === "max") {
return "Too many attempts - locked out"
return "Too many attempts - locked out";
}
if (root.pamState === "fail") {
return "Incorrect password - try again"
return "Incorrect password - try again";
}
return ""
return "";
}
color: Theme.error
font.pixelSize: Theme.fontSizeSmall
@@ -793,11 +804,11 @@ Item {
anchors.verticalCenter: parent.verticalCenter
visible: {
if (CompositorService.isNiri) {
return NiriService.keyboardLayoutNames.length > 1
return NiriService.keyboardLayoutNames.length > 1;
} else if (CompositorService.isHyprland) {
return hyprlandLayoutCount > 1
return hyprlandLayoutCount > 1;
}
return false
return false;
}
Row {
@@ -823,17 +834,18 @@ Item {
StyledText {
text: {
if (CompositorService.isNiri) {
const layout = NiriService.getCurrentKeyboardLayoutName()
if (!layout) return ""
const parts = layout.split(" ")
const layout = NiriService.getCurrentKeyboardLayoutName();
if (!layout)
return "";
const parts = layout.split(" ");
if (parts.length > 0) {
return parts[0].substring(0, 2).toUpperCase()
return parts[0].substring(0, 2).toUpperCase();
}
return layout.substring(0, 2).toUpperCase()
return layout.substring(0, 2).toUpperCase();
} else if (CompositorService.isHyprland) {
return hyprlandCurrentLayout
return hyprlandCurrentLayout;
}
return ""
return "";
}
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Light
@@ -851,15 +863,10 @@ Item {
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
if (CompositorService.isNiri) {
NiriService.cycleKeyboardLayout()
NiriService.cycleKeyboardLayout();
} else if (CompositorService.isHyprland) {
Quickshell.execDetached([
"hyprctl",
"switchxkblayout",
hyprlandKeyboard,
"next"
])
updateHyprlandLayout()
Quickshell.execDetached(["hyprctl", "switchxkblayout", hyprlandKeyboard, "next"]);
updateHyprlandLayout();
}
}
}
@@ -898,7 +905,7 @@ Item {
interval: 256
repeat: true
onTriggered: {
CavaService.values = [Math.random() * 40 + 10, Math.random() * 60 + 20, Math.random() * 50 + 15, Math.random() * 35 + 20, Math.random() * 45 + 15, Math.random() * 55 + 25]
CavaService.values = [Math.random() * 40 + 10, Math.random() * 60 + 20, Math.random() * 50 + 15, Math.random() * 35 + 20, Math.random() * 45 + 15, Math.random() * 55 + 25];
}
}
@@ -914,13 +921,13 @@ Item {
width: 2
height: {
if (MprisController.activePlayer?.playbackState === MprisPlaybackState.Playing && CavaService.values.length > index) {
const rawLevel = CavaService.values[index] || 0
const scaledLevel = Math.sqrt(Math.min(Math.max(rawLevel, 0), 100) / 100) * 100
const maxHeight = Theme.iconSize - 2
const minHeight = 3
return minHeight + (scaledLevel / 100) * (maxHeight - minHeight)
const rawLevel = CavaService.values[index] || 0;
const scaledLevel = Math.sqrt(Math.min(Math.max(rawLevel, 0), 100) / 100) * 100;
const maxHeight = Theme.iconSize - 2;
const minHeight = 3;
return minHeight + (scaledLevel / 100) * (maxHeight - minHeight);
}
return 3
return 3;
}
radius: 1.5
color: "white"
@@ -940,11 +947,12 @@ Item {
StyledText {
text: {
const player = MprisController.activePlayer
if (!player?.trackTitle) return ""
const title = player.trackTitle
const artist = player.trackArtist || ""
return artist ? title + " • " + artist : title
const player = MprisController.activePlayer;
if (!player?.trackTitle)
return "";
const title = player.trackTitle;
const artist = player.trackArtist || "";
return artist ? title + " • " + artist : title;
}
font.pixelSize: Theme.fontSizeLarge
color: "white"
@@ -1099,15 +1107,15 @@ Item {
DankIcon {
name: {
if (!AudioService.sink?.audio) {
return "volume_up"
return "volume_up";
}
if (AudioService.sink.audio.muted || AudioService.sink.audio.volume === 0) {
return "volume_off"
return "volume_off";
}
if (AudioService.sink.audio.volume * 100 < 33) {
return "volume_down"
return "volume_down";
}
return "volume_up"
return "volume_up";
}
size: Theme.iconSize - 2
color: (AudioService.sink && AudioService.sink.audio && (AudioService.sink.audio.muted || AudioService.sink.audio.volume === 0)) ? Qt.rgba(255, 255, 255, 0.5) : "white"
@@ -1133,95 +1141,95 @@ Item {
name: {
if (BatteryService.isCharging) {
if (BatteryService.batteryLevel >= 90) {
return "battery_charging_full"
return "battery_charging_full";
}
if (BatteryService.batteryLevel >= 80) {
return "battery_charging_90"
return "battery_charging_90";
}
if (BatteryService.batteryLevel >= 60) {
return "battery_charging_80"
return "battery_charging_80";
}
if (BatteryService.batteryLevel >= 50) {
return "battery_charging_60"
return "battery_charging_60";
}
if (BatteryService.batteryLevel >= 30) {
return "battery_charging_50"
return "battery_charging_50";
}
if (BatteryService.batteryLevel >= 20) {
return "battery_charging_30"
return "battery_charging_30";
}
return "battery_charging_20"
return "battery_charging_20";
}
if (BatteryService.isPluggedIn) {
if (BatteryService.batteryLevel >= 90) {
return "battery_charging_full"
return "battery_charging_full";
}
if (BatteryService.batteryLevel >= 80) {
return "battery_charging_90"
return "battery_charging_90";
}
if (BatteryService.batteryLevel >= 60) {
return "battery_charging_80"
return "battery_charging_80";
}
if (BatteryService.batteryLevel >= 50) {
return "battery_charging_60"
return "battery_charging_60";
}
if (BatteryService.batteryLevel >= 30) {
return "battery_charging_50"
return "battery_charging_50";
}
if (BatteryService.batteryLevel >= 20) {
return "battery_charging_30"
return "battery_charging_30";
}
return "battery_charging_20"
return "battery_charging_20";
}
if (BatteryService.batteryLevel >= 95) {
return "battery_full"
return "battery_full";
}
if (BatteryService.batteryLevel >= 85) {
return "battery_6_bar"
return "battery_6_bar";
}
if (BatteryService.batteryLevel >= 70) {
return "battery_5_bar"
return "battery_5_bar";
}
if (BatteryService.batteryLevel >= 55) {
return "battery_4_bar"
return "battery_4_bar";
}
if (BatteryService.batteryLevel >= 40) {
return "battery_3_bar"
return "battery_3_bar";
}
if (BatteryService.batteryLevel >= 25) {
return "battery_2_bar"
return "battery_2_bar";
}
return "battery_1_bar"
return "battery_1_bar";
}
size: Theme.iconSize
color: {
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
return Theme.error
return Theme.error;
}
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
return Theme.primary
return Theme.primary;
}
return "white"
return "white";
}
anchors.verticalCenter: parent.verticalCenter
}
@@ -1246,9 +1254,9 @@ Item {
buttonSize: 40
onClicked: {
if (demoMode) {
console.log("Demo: Power Menu")
console.log("Demo: Power Menu");
} else {
powerMenu.show()
powerMenu.show();
}
}
}
@@ -1272,18 +1280,18 @@ Item {
id: pam
lockSecured: !demoMode
onUnlockRequested: {
root.unlocking = true
lockerReadyArmed = false
passwordField.text = ""
root.passwordBuffer = ""
root.unlockRequested()
root.unlocking = true;
lockerReadyArmed = false;
passwordField.text = "";
root.passwordBuffer = "";
root.unlockRequested();
}
onStateChanged: {
root.pamState = state
root.pamState = state;
if (state !== "") {
placeholderDelay.restart()
passwordField.text = ""
root.passwordBuffer = ""
placeholderDelay.restart();
passwordField.text = "";
root.passwordBuffer = "";
}
}
}
@@ -1312,7 +1320,7 @@ Item {
showLogout: true
onClosed: {
if (!demoMode && passwordField && passwordField.forceActiveFocus) {
Qt.callLater(() => passwordField.forceActiveFocus())
Qt.callLater(() => passwordField.forceActiveFocus());
}
}
}
+3 -5
View File
@@ -1,9 +1,7 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Common
Rectangle {
id: root
@@ -14,7 +12,7 @@ Rectangle {
required property bool isLocked
signal passwordChanged(string newPassword)
signal unlockRequested()
signal unlockRequested
color: "transparent"
@@ -28,14 +26,14 @@ Rectangle {
onUnlockRequested: root.unlockRequested()
onPasswordBufferChanged: {
if (root.sharedPasswordBuffer !== passwordBuffer) {
root.passwordChanged(passwordBuffer)
root.passwordChanged(passwordBuffer);
}
}
}
onIsLockedChanged: {
if (!isLocked) {
lockContent.unlocking = false
lockContent.unlocking = false;
}
}
}
+83 -78
View File
@@ -1,7 +1,5 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
@@ -16,50 +14,70 @@ Item {
property bool isLabwc: CompositorService.isLabwc
property string compositorName: {
if (isHyprland) return "hyprland"
if (isSway) return "sway"
if (isDwl) return "mangowc"
if (isLabwc) return "labwc"
return "niri"
if (isHyprland)
return "hyprland";
if (isSway)
return "sway";
if (isDwl)
return "mangowc";
if (isLabwc)
return "labwc";
return "niri";
}
property string compositorLogo: {
if (isHyprland) return "/assets/hyprland.svg"
if (isSway) return "/assets/sway.svg"
if (isDwl) return "/assets/mango.png"
if (isLabwc) return "/assets/labwc.png"
return "/assets/niri.svg"
if (isHyprland)
return "/assets/hyprland.svg";
if (isSway)
return "/assets/sway.svg";
if (isDwl)
return "/assets/mango.png";
if (isLabwc)
return "/assets/labwc.png";
return "/assets/niri.svg";
}
property string compositorUrl: {
if (isHyprland) return "https://hypr.land"
if (isSway) return "https://swaywm.org"
if (isDwl) return "https://github.com/DreamMaoMao/mangowc"
if (isLabwc) return "https://labwc.github.io/"
return "https://github.com/YaLTeR/niri"
if (isHyprland)
return "https://hypr.land";
if (isSway)
return "https://swaywm.org";
if (isDwl)
return "https://github.com/DreamMaoMao/mangowc";
if (isLabwc)
return "https://labwc.github.io/";
return "https://github.com/YaLTeR/niri";
}
property string compositorTooltip: {
if (isHyprland) return "Hyprland Website"
if (isSway) return "Sway Website"
if (isDwl) return "mangowc GitHub"
if (isLabwc) return "LabWC Website"
return "niri GitHub"
if (isHyprland)
return "Hyprland Website";
if (isSway)
return "Sway Website";
if (isDwl)
return "mangowc GitHub";
if (isLabwc)
return "LabWC Website";
return "niri GitHub";
}
property string dmsDiscordUrl: "https://discord.gg/ppWTpKmPgT"
property string dmsDiscordTooltip: "niri/dms Discord"
property string compositorDiscordUrl: {
if (isHyprland) return "https://discord.com/invite/hQ9XvMUjjr"
if (isDwl) return "https://discord.gg/CPjbDxesh5"
return ""
if (isHyprland)
return "https://discord.com/invite/hQ9XvMUjjr";
if (isDwl)
return "https://discord.gg/CPjbDxesh5";
return "";
}
property string compositorDiscordTooltip: {
if (isHyprland) return "Hyprland Discord Server"
if (isDwl) return "mangowc Discord Server"
return ""
if (isHyprland)
return "Hyprland Discord Server";
if (isDwl)
return "mangowc Discord Server";
return "";
}
property string redditUrl: "https://reddit.com/r/niri"
@@ -76,13 +94,14 @@ Item {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
// ASCII Art Header
@@ -91,8 +110,7 @@ Item {
height: asciiSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -157,20 +175,20 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
height: 24
width: {
let baseWidth = compositorButton.width + dmsDiscordButton.width + Theme.spacingM
let baseWidth = compositorButton.width + dmsDiscordButton.width + Theme.spacingM;
if (showMatrix) {
baseWidth += matrixButton.width + 4
baseWidth += matrixButton.width + 4;
}
if (showIrc) {
baseWidth += ircButton.width + Theme.spacingM
baseWidth += ircButton.width + Theme.spacingM;
}
if (showCompositorDiscord) {
baseWidth += compositorDiscordButton.width + Theme.spacingM
baseWidth += compositorDiscordButton.width + Theme.spacingM;
}
if (showReddit) {
baseWidth += redditButton.width + Theme.spacingM
baseWidth += redditButton.width + Theme.spacingM;
}
return baseWidth
return baseWidth;
}
Item {
@@ -186,10 +204,7 @@ Item {
Image {
anchors.fill: parent
source: Qt.resolvedUrl(".").toString().replace(
"file://", "").replace(
"/Modules/Settings/",
"") + compositorLogo
source: Qt.resolvedUrl(".").toString().replace("file://", "").replace("/Modules/Settings/", "") + compositorLogo
sourceSize: Qt.size(24, 24)
smooth: true
fillMode: Image.PreserveAspectFit
@@ -217,10 +232,7 @@ Item {
Image {
anchors.fill: parent
source: Qt.resolvedUrl(".").toString().replace(
"file://", "").replace(
"/Modules/Settings/",
"") + "/assets/matrix-logo-white.svg"
source: Qt.resolvedUrl(".").toString().replace("file://", "").replace("/Modules/Settings/", "") + "/assets/matrix-logo-white.svg"
sourceSize: Qt.size(28, 18)
smooth: true
fillMode: Image.PreserveAspectFit
@@ -238,8 +250,7 @@ Item {
hoverEnabled: true
onEntered: parent.hovered = true
onExited: parent.hovered = false
onClicked: Qt.openUrlExternally(
"https://matrix.to/#/#niri:matrix.org")
onClicked: Qt.openUrlExternally("https://matrix.to/#/#niri:matrix.org")
}
}
@@ -276,9 +287,11 @@ Item {
width: 20
height: 20
x: {
if (showMatrix) return matrixButton.x + matrixButton.width + Theme.spacingM
if (showIrc) return ircButton.x + ircButton.width + Theme.spacingM
return compositorButton.x + compositorButton.width + Theme.spacingM
if (showMatrix)
return matrixButton.x + matrixButton.width + Theme.spacingM;
if (showIrc)
return ircButton.x + ircButton.width + Theme.spacingM;
return compositorButton.x + compositorButton.width + Theme.spacingM;
}
anchors.verticalCenter: parent.verticalCenter
@@ -287,10 +300,7 @@ Item {
Image {
anchors.fill: parent
source: Qt.resolvedUrl(".").toString().replace(
"file://", "").replace(
"/Modules/Settings/",
"") + "/assets/discord.svg"
source: Qt.resolvedUrl(".").toString().replace("file://", "").replace("/Modules/Settings/", "") + "/assets/discord.svg"
sourceSize: Qt.size(20, 20)
smooth: true
fillMode: Image.PreserveAspectFit
@@ -319,10 +329,7 @@ Item {
Image {
anchors.fill: parent
source: Qt.resolvedUrl(".").toString().replace(
"file://", "").replace(
"/Modules/Settings/",
"") + "/assets/discord.svg"
source: Qt.resolvedUrl(".").toString().replace("file://", "").replace("/Modules/Settings/", "") + "/assets/discord.svg"
sourceSize: Qt.size(20, 20)
smooth: true
fillMode: Image.PreserveAspectFit
@@ -351,10 +358,7 @@ Item {
Image {
anchors.fill: parent
source: Qt.resolvedUrl(".").toString().replace(
"file://", "").replace(
"/Modules/Settings/",
"") + "/assets/reddit.svg"
source: Qt.resolvedUrl(".").toString().replace("file://", "").replace("/Modules/Settings/", "") + "/assets/reddit.svg"
sourceSize: Qt.size(20, 20)
smooth: true
fillMode: Image.PreserveAspectFit
@@ -373,15 +377,13 @@ Item {
}
}
// Project Information
StyledRect {
width: parent.width
height: projectSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -438,8 +440,7 @@ Item {
height: techSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -600,8 +601,7 @@ Item {
height: supportSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Row {
@@ -648,7 +648,6 @@ Item {
}
}
}
}
}
@@ -659,13 +658,19 @@ Item {
z: 1000
property var hoveredButton: {
if (compositorButton.hovered) return compositorButton
if (matrixButton.visible && matrixButton.hovered) return matrixButton
if (ircButton.visible && ircButton.hovered) return ircButton
if (dmsDiscordButton.hovered) return dmsDiscordButton
if (compositorDiscordButton.visible && compositorDiscordButton.hovered) return compositorDiscordButton
if (redditButton.visible && redditButton.hovered) return redditButton
return null
if (compositorButton.hovered)
return compositorButton;
if (matrixButton.visible && matrixButton.hovered)
return matrixButton;
if (ircButton.visible && ircButton.hovered)
return ircButton;
if (dmsDiscordButton.hovered)
return dmsDiscordButton;
if (compositorDiscordButton.visible && compositorDiscordButton.hovered)
return compositorDiscordButton;
if (redditButton.visible && redditButton.hovered)
return redditButton;
return null;
}
property string tooltipText: hoveredButton ? hoveredButton.tooltipText : ""
+3 -2
View File
@@ -1075,12 +1075,13 @@ Item {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
StyledRect {
+147 -155
View File
@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Common
@@ -10,81 +9,88 @@ Item {
id: displaysTab
function getBarComponentsFromSettings() {
const bars = SettingsData.barConfigs || []
const bars = SettingsData.barConfigs || [];
return bars.map(bar => ({
"id": "bar:" + bar.id,
"name": bar.name || "Bar",
"description": I18n.tr("Individual bar configuration"),
"icon": "toolbar",
"barId": bar.id
}))
"id": "bar:" + bar.id,
"name": bar.name || "Bar",
"description": I18n.tr("Individual bar configuration"),
"icon": "toolbar",
"barId": bar.id
}));
}
property var variantComponents: getVariantComponentsList()
function getVariantComponentsList() {
return [
...getBarComponentsFromSettings(),
return [...getBarComponentsFromSettings(),
{
"id": "dock",
"name": I18n.tr("Application Dock"),
"description": I18n.tr("Bottom dock for pinned and running applications"),
"icon": "dock"
}, {
},
{
"id": "notifications",
"name": I18n.tr("Notification Popups"),
"description": I18n.tr("Notification toast popups"),
"icon": "notifications"
}, {
},
{
"id": "wallpaper",
"name": I18n.tr("Wallpaper"),
"description": I18n.tr("Desktop background images"),
"icon": "wallpaper"
}, {
},
{
"id": "osd",
"name": I18n.tr("On-Screen Displays"),
"description": I18n.tr("Volume, brightness, and other system OSDs"),
"icon": "picture_in_picture"
}, {
},
{
"id": "toast",
"name": I18n.tr("Toast Messages"),
"description": I18n.tr("System toast notifications"),
"icon": "campaign"
}, {
},
{
"id": "notepad",
"name": I18n.tr("Notepad Slideout"),
"description": I18n.tr("Quick note-taking slideout panel"),
"icon": "sticky_note_2"
}, {
},
{
"id": "systemTray",
"name": I18n.tr("System Tray"),
"description": I18n.tr("System tray icons"),
"icon": "notifications"
}
]
];
}
Connections {
target: SettingsData
function onBarConfigsChanged() {
variantComponents = getVariantComponentsList()
variantComponents = getVariantComponentsList();
}
}
function getScreenPreferences(componentId) {
if (componentId.startsWith("bar:")) {
const barId = componentId.substring(4)
const barConfig = SettingsData.getBarConfig(barId)
return barConfig?.screenPreferences || ["all"]
const barId = componentId.substring(4);
const barConfig = SettingsData.getBarConfig(barId);
return barConfig?.screenPreferences || ["all"];
}
return SettingsData.screenPreferences && SettingsData.screenPreferences[componentId] || ["all"];
}
function setScreenPreferences(componentId, screenNames) {
if (componentId.startsWith("bar:")) {
const barId = componentId.substring(4)
SettingsData.updateBarConfig(barId, { screenPreferences: screenNames })
return
const barId = componentId.substring(4);
SettingsData.updateBarConfig(barId, {
screenPreferences: screenNames
});
return;
}
var prefs = SettingsData.screenPreferences || {};
var newPrefs = Object.assign({}, prefs);
@@ -94,18 +100,20 @@ Item {
function getShowOnLastDisplay(componentId) {
if (componentId.startsWith("bar:")) {
const barId = componentId.substring(4)
const barConfig = SettingsData.getBarConfig(barId)
return barConfig?.showOnLastDisplay ?? true
const barId = componentId.substring(4);
const barConfig = SettingsData.getBarConfig(barId);
return barConfig?.showOnLastDisplay ?? true;
}
return SettingsData.showOnLastDisplay && SettingsData.showOnLastDisplay[componentId] || false;
}
function setShowOnLastDisplay(componentId, enabled) {
if (componentId.startsWith("bar:")) {
const barId = componentId.substring(4)
SettingsData.updateBarConfig(barId, { showOnLastDisplay: enabled })
return
const barId = componentId.substring(4);
SettingsData.updateBarConfig(barId, {
showOnLastDisplay: enabled
});
return;
}
var prefs = SettingsData.showOnLastDisplay || {};
var newPrefs = Object.assign({}, prefs);
@@ -116,13 +124,14 @@ Item {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
StyledRect {
@@ -169,12 +178,12 @@ Item {
checked: DisplayService.nightModeEnabled
enabled: DisplayService.gammaControlAvailable
onToggled: checked => {
DisplayService.toggleNightMode()
}
DisplayService.toggleNightMode();
}
Connections {
function onNightModeEnabledChanged() {
nightModeToggle.checked = DisplayService.nightModeEnabled
nightModeToggle.checked = DisplayService.nightModeEnabled;
}
target: DisplayService
@@ -194,19 +203,19 @@ Item {
description: I18n.tr("Color temperature for night mode")
currentValue: SessionData.nightModeTemperature + "K"
options: {
var temps = []
var temps = [];
for (var i = 2500; i <= 6000; i += 500) {
temps.push(i + "K")
temps.push(i + "K");
}
return temps
return temps;
}
onValueChanged: value => {
var temp = parseInt(value.replace("K", ""))
SessionData.setNightModeTemperature(temp)
if (SessionData.nightModeHighTemperature < temp) {
SessionData.setNightModeHighTemperature(temp)
}
}
var temp = parseInt(value.replace("K", ""));
SessionData.setNightModeTemperature(temp);
if (SessionData.nightModeHighTemperature < temp) {
SessionData.setNightModeHighTemperature(temp);
}
}
}
DankDropdown {
@@ -215,19 +224,19 @@ Item {
description: I18n.tr("Color temperature for day time")
currentValue: SessionData.nightModeHighTemperature + "K"
options: {
var temps = []
var minTemp = SessionData.nightModeTemperature
var temps = [];
var minTemp = SessionData.nightModeTemperature;
for (var i = Math.max(2500, minTemp); i <= 10000; i += 500) {
temps.push(i + "K")
temps.push(i + "K");
}
return temps
return temps;
}
onValueChanged: value => {
var temp = parseInt(value.replace("K", ""))
if (temp >= SessionData.nightModeTemperature) {
SessionData.setNightModeHighTemperature(temp)
}
}
var temp = parseInt(value.replace("K", ""));
if (temp >= SessionData.nightModeTemperature) {
SessionData.setNightModeHighTemperature(temp);
}
}
}
}
@@ -239,18 +248,18 @@ Item {
checked: SessionData.nightModeAutoEnabled
visible: DisplayService.gammaControlAvailable
onToggled: checked => {
if (checked && !DisplayService.nightModeEnabled) {
DisplayService.toggleNightMode()
} else if (!checked && DisplayService.nightModeEnabled) {
DisplayService.toggleNightMode()
}
SessionData.setNightModeAutoEnabled(checked)
}
if (checked && !DisplayService.nightModeEnabled) {
DisplayService.toggleNightMode();
} else if (!checked && DisplayService.nightModeEnabled) {
DisplayService.toggleNightMode();
}
SessionData.setNightModeAutoEnabled(checked);
}
Connections {
target: SessionData
function onNightModeAutoEnabledChanged() {
automaticToggle.checked = SessionData.nightModeAutoEnabled
automaticToggle.checked = SessionData.nightModeAutoEnabled;
}
}
}
@@ -264,7 +273,7 @@ Item {
Connections {
target: SessionData
function onNightModeAutoEnabledChanged() {
automaticSettings.visible = SessionData.nightModeAutoEnabled
automaticSettings.visible = SessionData.nightModeAutoEnabled;
}
}
@@ -277,29 +286,32 @@ Item {
width: 200
height: 45
anchors.horizontalCenter: parent.horizontalCenter
model: [{
model: [
{
"text": "Time",
"icon": "access_time"
}, {
},
{
"text": "Location",
"icon": "place"
}]
}
]
Component.onCompleted: {
currentIndex = SessionData.nightModeAutoMode === "location" ? 1 : 0
Qt.callLater(updateIndicator)
currentIndex = SessionData.nightModeAutoMode === "location" ? 1 : 0;
Qt.callLater(updateIndicator);
}
onTabClicked: index => {
DisplayService.setNightModeAutomationMode(index === 1 ? "location" : "time")
currentIndex = index
}
DisplayService.setNightModeAutomationMode(index === 1 ? "location" : "time");
currentIndex = index;
}
Connections {
target: SessionData
function onNightModeAutoModeChanged() {
modeTabBarNight.currentIndex = SessionData.nightModeAutoMode === "location" ? 1 : 0
Qt.callLater(modeTabBarNight.updateIndicator)
modeTabBarNight.currentIndex = SessionData.nightModeAutoMode === "location" ? 1 : 0;
Qt.callLater(modeTabBarNight.updateIndicator);
}
}
}
@@ -356,30 +368,30 @@ Item {
dropdownWidth: 70
currentValue: SessionData.nightModeStartHour.toString()
options: {
var hours = []
var hours = [];
for (var i = 0; i < 24; i++) {
hours.push(i.toString())
hours.push(i.toString());
}
return hours
return hours;
}
onValueChanged: value => {
SessionData.setNightModeStartHour(parseInt(value))
}
SessionData.setNightModeStartHour(parseInt(value));
}
}
DankDropdown {
dropdownWidth: 70
currentValue: SessionData.nightModeStartMinute.toString().padStart(2, '0')
options: {
var minutes = []
var minutes = [];
for (var i = 0; i < 60; i += 5) {
minutes.push(i.toString().padStart(2, '0'))
minutes.push(i.toString().padStart(2, '0'));
}
return minutes
return minutes;
}
onValueChanged: value => {
SessionData.setNightModeStartMinute(parseInt(value))
}
SessionData.setNightModeStartMinute(parseInt(value));
}
}
}
@@ -399,30 +411,30 @@ Item {
dropdownWidth: 70
currentValue: SessionData.nightModeEndHour.toString()
options: {
var hours = []
var hours = [];
for (var i = 0; i < 24; i++) {
hours.push(i.toString())
hours.push(i.toString());
}
return hours
return hours;
}
onValueChanged: value => {
SessionData.setNightModeEndHour(parseInt(value))
}
SessionData.setNightModeEndHour(parseInt(value));
}
}
DankDropdown {
dropdownWidth: 70
currentValue: SessionData.nightModeEndMinute.toString().padStart(2, '0')
options: {
var minutes = []
var minutes = [];
for (var i = 0; i < 60; i += 5) {
minutes.push(i.toString().padStart(2, '0'))
minutes.push(i.toString().padStart(2, '0'));
}
return minutes
return minutes;
}
onValueChanged: value => {
SessionData.setNightModeEndMinute(parseInt(value))
}
SessionData.setNightModeEndMinute(parseInt(value));
}
}
}
}
@@ -441,13 +453,13 @@ Item {
description: I18n.tr("Automatically detect location based on IP address")
checked: SessionData.nightModeUseIPLocation || false
onToggled: checked => {
SessionData.setNightModeUseIPLocation(checked)
SessionData.setNightModeUseIPLocation(checked);
}
Connections {
target: SessionData
function onNightModeUseIPLocationChanged() {
ipLocationToggle.checked = SessionData.nightModeUseIPLocation
ipLocationToggle.checked = SessionData.nightModeUseIPLocation;
}
}
}
@@ -482,9 +494,9 @@ Item {
text: SessionData.latitude.toString()
placeholderText: "0.0"
onEditingFinished: {
const lat = parseFloat(text)
const lat = parseFloat(text);
if (!isNaN(lat) && lat >= -90 && lat <= 90 && lat !== SessionData.latitude) {
SessionData.setLatitude(lat)
SessionData.setLatitude(lat);
}
}
}
@@ -505,9 +517,9 @@ Item {
text: SessionData.longitude.toString()
placeholderText: "0.0"
onEditingFinished: {
const lon = parseFloat(text)
const lon = parseFloat(text);
if (!isNaN(lon) && lon >= -180 && lon <= 180 && lon !== SessionData.longitude) {
SessionData.setLongitude(lon)
SessionData.setLongitude(lon);
}
}
}
@@ -572,9 +584,7 @@ Item {
wrapMode: Text.WordWrap
width: parent.width
}
}
}
Column {
@@ -618,15 +628,16 @@ Item {
model: [I18n.tr("Name"), I18n.tr("Model")]
currentIndex: SettingsData.displayNameMode === "model" ? 1 : 0
onSelectionChanged: (index, selected) => {
if (!selected) return
SettingsData.displayNameMode = index === 1 ? "model" : "system"
SettingsData.saveSettings()
if (!selected)
return;
SettingsData.displayNameMode = index === 1 ? "model" : "system";
SettingsData.saveSettings();
}
Connections {
target: SettingsData
function onDisplayNameModeChanged() {
displayModeGroup.currentIndex = SettingsData.displayNameMode === "model" ? 1 : 0
displayModeGroup.currentIndex = SettingsData.displayNameMode === "model" ? 1 : 0;
}
}
}
@@ -691,21 +702,13 @@ Item {
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
}
}
}
}
}
}
}
Column {
@@ -760,9 +763,7 @@ Item {
wrapMode: Text.WordWrap
width: parent.width
}
}
}
Column {
@@ -787,17 +788,17 @@ Item {
text: I18n.tr("All displays")
description: I18n.tr("Show on all connected displays")
checked: {
var prefs = displaysTab.getScreenPreferences(parent.componentId)
return prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all")
var prefs = displaysTab.getScreenPreferences(parent.componentId);
return prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all");
}
onToggled: (checked) => {
onToggled: checked => {
if (checked) {
displaysTab.setScreenPreferences(parent.componentId, ["all"])
displaysTab.setScreenPreferences(parent.componentId, ["all"]);
} else {
displaysTab.setScreenPreferences(parent.componentId, [])
const cid = parent.componentId
displaysTab.setScreenPreferences(parent.componentId, []);
const cid = parent.componentId;
if (["dankBar", "dock", "notifications", "osd", "toast"].includes(cid) || cid.startsWith("bar:")) {
displaysTab.setShowOnLastDisplay(cid, true)
displaysTab.setShowOnLastDisplay(cid, true);
}
}
}
@@ -809,14 +810,14 @@ Item {
description: I18n.tr("Always show when there's only one connected display")
checked: displaysTab.getShowOnLastDisplay(parent.componentId)
visible: {
const prefs = displaysTab.getScreenPreferences(parent.componentId)
const isAll = prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all")
const cid = parent.componentId
const isRelevantComponent = ["dankBar", "dock", "notifications", "osd", "toast", "notepad", "systemTray"].includes(cid) || cid.startsWith("bar:")
return !isAll && isRelevantComponent
const prefs = displaysTab.getScreenPreferences(parent.componentId);
const isAll = prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all");
const cid = parent.componentId;
const isRelevantComponent = ["dankBar", "dock", "notifications", "osd", "toast", "notepad", "systemTray"].includes(cid) || cid.startsWith("bar:");
return !isAll && isRelevantComponent;
}
onToggled: (checked) => {
displaysTab.setShowOnLastDisplay(parent.componentId, checked)
onToggled: checked => {
displaysTab.setShowOnLastDisplay(parent.componentId, checked);
}
}
@@ -826,8 +827,8 @@ Item {
color: Theme.outline
opacity: 0.2
visible: {
var prefs = displaysTab.getScreenPreferences(parent.componentId)
return !prefs.includes("all") && !(typeof prefs[0] === "string" && prefs[0] === "all")
var prefs = displaysTab.getScreenPreferences(parent.componentId);
return !prefs.includes("all") && !(typeof prefs[0] === "string" && prefs[0] === "all");
}
}
@@ -835,8 +836,8 @@ Item {
width: parent.width
spacing: Theme.spacingXS
visible: {
var prefs = displaysTab.getScreenPreferences(parent.componentId)
return !prefs.includes("all") && !(typeof prefs[0] === "string" && prefs[0] === "all")
var prefs = displaysTab.getScreenPreferences(parent.componentId);
return !prefs.includes("all") && !(typeof prefs[0] === "string" && prefs[0] === "all");
}
Repeater {
@@ -850,50 +851,41 @@ Item {
text: SettingsData.getScreenDisplayName(screenData)
description: screenData.width + "×" + screenData.height + " • " + (SettingsData.displayNameMode === "system" ? (screenData.model || "Unknown Model") : screenData.name)
checked: {
var prefs = displaysTab.getScreenPreferences(componentId)
if (typeof prefs[0] === "string" && prefs[0] === "all") return false
return SettingsData.isScreenInPreferences(screenData, prefs)
var prefs = displaysTab.getScreenPreferences(componentId);
if (typeof prefs[0] === "string" && prefs[0] === "all")
return false;
return SettingsData.isScreenInPreferences(screenData, prefs);
}
onToggled: (checked) => {
var currentPrefs = displaysTab.getScreenPreferences(componentId)
onToggled: checked => {
var currentPrefs = displaysTab.getScreenPreferences(componentId);
if (typeof currentPrefs[0] === "string" && currentPrefs[0] === "all") {
currentPrefs = []
currentPrefs = [];
}
var newPrefs = currentPrefs.filter(pref => {
if (typeof pref === "string") return false
return pref.name !== screenData.name || pref.model !== screenData.model
})
if (typeof pref === "string")
return false;
return pref.name !== screenData.name || pref.model !== screenData.model;
});
if (checked) {
newPrefs.push({
name: screenData.name,
model: screenData.model || ""
})
});
}
displaysTab.setScreenPreferences(componentId, newPrefs)
displaysTab.setScreenPreferences(componentId, newPrefs);
}
}
}
}
}
}
}
}
}
}
}
}
}
}
+60 -62
View File
@@ -1,6 +1,4 @@
import QtQuick
import QtQuick.Controls
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
@@ -11,12 +9,13 @@ Item {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
// Dock Position
@@ -25,8 +24,7 @@ Item {
height: dockPositionSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -67,20 +65,33 @@ Item {
model: ["Top", "Bottom", "Left", "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
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) {
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
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;
}
}
}
@@ -95,8 +106,7 @@ Item {
height: dockVisibilitySection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -118,8 +128,7 @@ Item {
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- enableToggle.width - Theme.spacingM
width: parent.width - Theme.iconSize - Theme.spacingM - enableToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
@@ -145,8 +154,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.showDock
onToggled: checked => {
SettingsData.setShowDock(checked)
}
SettingsData.setShowDock(checked);
}
}
}
@@ -172,8 +181,7 @@ Item {
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- autoHideToggle.width - Theme.spacingM
width: parent.width - Theme.iconSize - Theme.spacingM - autoHideToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
@@ -199,8 +207,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.dockAutoHide
onToggled: checked => {
SettingsData.set("dockAutoHide", checked)
}
SettingsData.set("dockAutoHide", checked);
}
}
Behavior on opacity {
@@ -232,8 +240,7 @@ Item {
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- overviewToggle.width - Theme.spacingM
width: parent.width - Theme.iconSize - Theme.spacingM - overviewToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
@@ -259,8 +266,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.dockOpenOnOverview
onToggled: checked => {
SettingsData.set("dockOpenOnOverview", checked)
}
SettingsData.set("dockOpenOnOverview", checked);
}
}
}
}
@@ -272,8 +279,7 @@ Item {
height: groupByAppSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -295,8 +301,7 @@ Item {
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- groupByAppToggle.width - Theme.spacingM
width: parent.width - Theme.iconSize - Theme.spacingM - groupByAppToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
@@ -322,8 +327,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.dockGroupByApp
onToggled: checked => {
SettingsData.set("dockGroupByApp", checked)
}
SettingsData.set("dockGroupByApp", checked);
}
}
}
}
@@ -335,8 +340,7 @@ Item {
height: indicatorStyleSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -378,7 +382,7 @@ Item {
currentIndex: SettingsData.dockIndicatorStyle === "circle" ? 0 : 1
onSelectionChanged: (index, selected) => {
if (selected) {
SettingsData.set("dockIndicatorStyle", index === 0 ? "circle" : "line")
SettingsData.set("dockIndicatorStyle", index === 0 ? "circle" : "line");
}
}
}
@@ -392,8 +396,7 @@ Item {
height: iconSizeSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -437,7 +440,7 @@ Item {
iconColor: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
onClicked: {
SettingsData.set("dockIconSize", 48)
SettingsData.set("dockIconSize", 48);
}
}
}
@@ -453,8 +456,8 @@ Item {
wheelEnabled: false
thumbOutlineColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
onSliderValueChanged: newValue => {
SettingsData.set("dockIconSize", newValue)
}
SettingsData.set("dockIconSize", newValue);
}
}
}
}
@@ -465,8 +468,7 @@ Item {
height: dockSpacingSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -533,7 +535,7 @@ Item {
iconColor: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
onClicked: {
SettingsData.set("dockSpacing", 8)
SettingsData.set("dockSpacing", 8);
}
}
@@ -554,9 +556,8 @@ Item {
wheelEnabled: false
thumbOutlineColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
onSliderValueChanged: newValue => {
SettingsData.set("dockSpacing",
newValue)
}
SettingsData.set("dockSpacing", newValue);
}
}
}
@@ -597,7 +598,7 @@ Item {
iconColor: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
onClicked: {
SettingsData.set("dockBottomGap", 0)
SettingsData.set("dockBottomGap", 0);
}
}
@@ -618,9 +619,8 @@ Item {
wheelEnabled: false
thumbOutlineColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
onSliderValueChanged: newValue => {
SettingsData.set("dockBottomGap",
newValue)
}
SettingsData.set("dockBottomGap", newValue);
}
}
}
@@ -661,7 +661,7 @@ Item {
iconColor: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
onClicked: {
SettingsData.set("dockMargin", 0)
SettingsData.set("dockMargin", 0);
}
}
@@ -682,8 +682,8 @@ Item {
wheelEnabled: false
thumbOutlineColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
onSliderValueChanged: newValue => {
SettingsData.set("dockMargin", newValue)
}
SettingsData.set("dockMargin", newValue);
}
}
}
}
@@ -695,8 +695,7 @@ Item {
height: transparencySection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -740,7 +739,7 @@ Item {
iconColor: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
onClicked: {
SettingsData.set("dockTransparency", 0.85)
SettingsData.set("dockTransparency", 0.85);
}
}
}
@@ -756,9 +755,8 @@ Item {
wheelEnabled: false
thumbOutlineColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
onSliderValueChanged: newValue => {
SettingsData.set("dockTransparency",
newValue / 100)
}
SettingsData.set("dockTransparency", newValue / 100);
}
}
}
}
+114 -140
View File
@@ -1,9 +1,5 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Widgets
import qs.Common
import qs.Modals
import qs.Modals.FileBrowser
import qs.Services
import qs.Widgets
@@ -18,19 +14,20 @@ Item {
browserType: "generic"
filterExtensions: ["*.svg", "*.png", "*.jpg", "*.jpeg", "*.webp"]
onFileSelected: path => {
SettingsData.set("launcherLogoCustomPath", path.replace("file://", ""))
SettingsData.set("launcherLogoCustomPath", path.replace("file://", ""));
}
}
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
StyledRect {
@@ -38,8 +35,7 @@ Item {
height: launcherLogoSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -85,41 +81,47 @@ Item {
id: logoModeGroup
anchors.horizontalCenter: parent.horizontalCenter
model: {
const modes = [I18n.tr("Apps Icon"), I18n.tr("OS Logo"), I18n.tr("Dank")]
const modes = [I18n.tr("Apps Icon"), I18n.tr("OS Logo"), I18n.tr("Dank")];
if (CompositorService.isNiri) {
modes.push("niri")
modes.push("niri");
} else if (CompositorService.isHyprland) {
modes.push("Hyprland")
modes.push("Hyprland");
} else if (CompositorService.isDwl) {
modes.push("mango")
modes.push("mango");
} else if (CompositorService.isSway) {
modes.push("Sway")
modes.push("Sway");
} else {
modes.push(I18n.tr("Compositor"))
modes.push(I18n.tr("Compositor"));
}
modes.push(I18n.tr("Custom"))
return modes
modes.push(I18n.tr("Custom"));
return modes;
}
currentIndex: {
if (SettingsData.launcherLogoMode === "apps") return 0
if (SettingsData.launcherLogoMode === "os") return 1
if (SettingsData.launcherLogoMode === "dank") return 2
if (SettingsData.launcherLogoMode === "compositor") return 3
if (SettingsData.launcherLogoMode === "custom") return 4
return 0
if (SettingsData.launcherLogoMode === "apps")
return 0;
if (SettingsData.launcherLogoMode === "os")
return 1;
if (SettingsData.launcherLogoMode === "dank")
return 2;
if (SettingsData.launcherLogoMode === "compositor")
return 3;
if (SettingsData.launcherLogoMode === "custom")
return 4;
return 0;
}
onSelectionChanged: (index, selected) => {
if (!selected) return
if (!selected)
return;
if (index === 0) {
SettingsData.set("launcherLogoMode", "apps")
SettingsData.set("launcherLogoMode", "apps");
} else if (index === 1) {
SettingsData.set("launcherLogoMode", "os")
SettingsData.set("launcherLogoMode", "os");
} else if (index === 2) {
SettingsData.set("launcherLogoMode", "dank")
SettingsData.set("launcherLogoMode", "dank");
} else if (index === 3) {
SettingsData.set("launcherLogoMode", "compositor")
SettingsData.set("launcherLogoMode", "compositor");
} else if (index === 4) {
SettingsData.set("launcherLogoMode", "custom")
SettingsData.set("launcherLogoMode", "custom");
}
}
}
@@ -200,25 +202,29 @@ Item {
id: colorModeGroup
model: [I18n.tr("Default"), I18n.tr("Primary"), I18n.tr("Surface"), I18n.tr("Custom")]
currentIndex: {
const override = SettingsData.launcherLogoColorOverride
if (override === "") return 0
if (override === "primary") return 1
if (override === "surface") return 2
return 3
const override = SettingsData.launcherLogoColorOverride;
if (override === "")
return 0;
if (override === "primary")
return 1;
if (override === "surface")
return 2;
return 3;
}
onSelectionChanged: (index, selected) => {
if (!selected) return
if (!selected)
return;
if (index === 0) {
SettingsData.set("launcherLogoColorOverride", "")
SettingsData.set("launcherLogoColorOverride", "");
} else if (index === 1) {
SettingsData.set("launcherLogoColorOverride", "primary")
SettingsData.set("launcherLogoColorOverride", "primary");
} else if (index === 2) {
SettingsData.set("launcherLogoColorOverride", "surface")
SettingsData.set("launcherLogoColorOverride", "surface");
} else if (index === 3) {
const currentOverride = SettingsData.launcherLogoColorOverride
const isPreset = currentOverride === "" || currentOverride === "primary" || currentOverride === "surface"
const currentOverride = SettingsData.launcherLogoColorOverride;
const isPreset = currentOverride === "" || currentOverride === "primary" || currentOverride === "surface";
if (isPreset) {
SettingsData.set("launcherLogoColorOverride", "#ffffff")
SettingsData.set("launcherLogoColorOverride", "#ffffff");
}
}
}
@@ -226,18 +232,18 @@ Item {
Rectangle {
visible: {
const override = SettingsData.launcherLogoColorOverride
return override !== "" && override !== "primary" && override !== "surface"
const override = SettingsData.launcherLogoColorOverride;
return override !== "" && override !== "primary" && override !== "surface";
}
width: 36
height: 36
radius: 18
color: {
const override = SettingsData.launcherLogoColorOverride
const override = SettingsData.launcherLogoColorOverride;
if (override !== "" && override !== "primary" && override !== "surface") {
return override
return override;
}
return "#ffffff"
return "#ffffff";
}
border.color: Theme.outline
border.width: 1
@@ -248,12 +254,12 @@ Item {
cursorShape: Qt.PointingHandCursor
onClicked: {
if (PopoutService.colorPickerModal) {
PopoutService.colorPickerModal.selectedColor = SettingsData.launcherLogoColorOverride
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Launcher Logo Color")
PopoutService.colorPickerModal.onColorSelectedCallback = function(selectedColor) {
SettingsData.set("launcherLogoColorOverride", selectedColor)
}
PopoutService.colorPickerModal.show()
PopoutService.colorPickerModal.selectedColor = SettingsData.launcherLogoColorOverride;
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Launcher Logo Color");
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
SettingsData.set("launcherLogoColorOverride", selectedColor);
};
PopoutService.colorPickerModal.show();
}
}
}
@@ -290,7 +296,7 @@ Item {
thumbOutlineColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
anchors.horizontalCenter: parent.horizontalCenter
onSliderValueChanged: newValue => {
SettingsData.set("launcherLogoSizeOffset", newValue)
SettingsData.set("launcherLogoSizeOffset", newValue);
}
}
}
@@ -300,8 +306,8 @@ Item {
width: parent.width
height: customControlsFlow.height
visible: {
const override = SettingsData.launcherLogoColorOverride
return override !== "" && override !== "primary" && override !== "surface"
const override = SettingsData.launcherLogoColorOverride;
return override !== "" && override !== "primary" && override !== "surface";
}
opacity: visible ? 1 : 0
@@ -341,7 +347,7 @@ Item {
thumbOutlineColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
anchors.horizontalCenter: parent.horizontalCenter
onSliderValueChanged: newValue => {
SettingsData.set("launcherLogoBrightness", newValue / 100)
SettingsData.set("launcherLogoBrightness", newValue / 100);
}
}
}
@@ -370,7 +376,7 @@ Item {
thumbOutlineColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
anchors.horizontalCenter: parent.horizontalCenter
onSliderValueChanged: newValue => {
SettingsData.set("launcherLogoContrast", newValue / 100)
SettingsData.set("launcherLogoContrast", newValue / 100);
}
}
}
@@ -393,7 +399,7 @@ Item {
checked: SettingsData.launcherLogoColorInvertOnMode
anchors.horizontalCenter: parent.horizontalCenter
onToggled: checked => {
SettingsData.set("launcherLogoColorInvertOnMode", checked)
SettingsData.set("launcherLogoColorInvertOnMode", checked);
}
}
}
@@ -408,8 +414,7 @@ Item {
height: launchPrefixSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -452,7 +457,7 @@ Item {
text: SettingsData.launchPrefix
placeholderText: I18n.tr("Enter launch prefix (e.g., 'uwsm-app')")
onTextEdited: {
SettingsData.set("launchPrefix", text)
SettingsData.set("launchPrefix", text);
}
}
}
@@ -463,8 +468,7 @@ Item {
height: sortingSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -494,9 +498,7 @@ Item {
}
Item {
width: parent.width - parent.children[0].width
- parent.children[1].width
- sortToggle.width - Theme.spacingM * 3
width: parent.width - parent.children[0].width - parent.children[1].width - sortToggle.width - Theme.spacingM * 3
height: 1
}
@@ -508,7 +510,7 @@ Item {
checked: SettingsData.sortAppsAlphabetically
anchors.verticalCenter: parent.verticalCenter
onToggled: checked => {
SettingsData.set("sortAppsAlphabetically", checked)
SettingsData.set("sortAppsAlphabetically", checked);
}
}
}
@@ -528,8 +530,7 @@ Item {
height: gridColumnsSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -584,7 +585,7 @@ Item {
thumbOutlineColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
anchors.horizontalCenter: parent.horizontalCenter
onSliderValueChanged: newValue => {
SettingsData.set("appLauncherGridColumns", newValue)
SettingsData.set("appLauncherGridColumns", newValue);
}
}
}
@@ -596,8 +597,7 @@ Item {
height: niriOverviewSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
visible: CompositorService.isNiri
@@ -628,9 +628,7 @@ Item {
}
Item {
width: parent.width - parent.children[0].width
- parent.children[1].width
- niriOverviewToggle.width - Theme.spacingM * 3
width: parent.width - parent.children[0].width - parent.children[1].width - niriOverviewToggle.width - Theme.spacingM * 3
height: 1
}
@@ -642,7 +640,7 @@ Item {
checked: SettingsData.spotlightCloseNiriOverview
anchors.verticalCenter: parent.verticalCenter
onToggled: checked => {
SettingsData.set("spotlightCloseNiriOverview", checked)
SettingsData.set("spotlightCloseNiriOverview", checked);
}
}
}
@@ -662,36 +660,33 @@ Item {
height: recentlyUsedSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
id: recentlyUsedSection
property var rankedAppsModel: {
var apps = []
for (var appId in (AppUsageHistoryData.appUsageRanking
|| {})) {
var appData = (AppUsageHistoryData.appUsageRanking
|| {})[appId]
var apps = [];
for (var appId in (AppUsageHistoryData.appUsageRanking || {})) {
var appData = (AppUsageHistoryData.appUsageRanking || {})[appId];
apps.push({
"id": appId,
"name": appData.name,
"exec": appData.exec,
"icon": appData.icon,
"comment": appData.comment,
"usageCount": appData.usageCount,
"lastUsed": appData.lastUsed
})
"id": appId,
"name": appData.name,
"exec": appData.exec,
"icon": appData.icon,
"comment": appData.comment,
"usageCount": appData.usageCount,
"lastUsed": appData.lastUsed
});
}
apps.sort(function (a, b) {
if (a.usageCount !== b.usageCount)
return b.usageCount - a.usageCount
return b.usageCount - a.usageCount;
return a.name.localeCompare(b.name)
})
return apps.slice(0, 20)
return a.name.localeCompare(b.name);
});
return apps.slice(0, 20);
}
anchors.fill: parent
@@ -718,9 +713,7 @@ Item {
}
Item {
width: parent.width - parent.children[0].width
- parent.children[1].width
- clearAllButton.width - Theme.spacingM * 3
width: parent.width - parent.children[0].width - parent.children[1].width - clearAllButton.width - Theme.spacingM * 3
height: 1
}
@@ -732,8 +725,8 @@ Item {
iconColor: Theme.error
anchors.verticalCenter: parent.verticalCenter
onClicked: {
AppUsageHistoryData.appUsageRanking = {}
AppUsageHistoryData.saveSettings()
AppUsageHistoryData.appUsageRanking = {};
AppUsageHistoryData.saveSettings();
}
}
}
@@ -759,12 +752,8 @@ Item {
width: rankedAppsList.width
height: 48
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r,
Theme.surfaceContainer.g,
Theme.surfaceContainer.b, 0.3)
border.color: Qt.rgba(Theme.outline.r,
Theme.outline.g,
Theme.outline.b, 0.1)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.3)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
border.width: 0
Row {
@@ -792,7 +781,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
onStatusChanged: {
if (status === Image.Error)
source = "image://icon/application-x-executable"
source = "image://icon/application-x-executable";
}
}
@@ -801,8 +790,7 @@ Item {
spacing: 2
StyledText {
text: modelData.name
|| "Unknown App"
text: modelData.name || "Unknown App"
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
@@ -811,37 +799,27 @@ Item {
StyledText {
text: {
if (!modelData.lastUsed)
return "Never used"
return "Never used";
var date = new Date(modelData.lastUsed)
var now = new Date()
var diffMs = now - date
var diffMins = Math.floor(
diffMs / (1000 * 60))
var diffHours = Math.floor(
diffMs / (1000 * 60 * 60))
var diffDays = Math.floor(
diffMs / (1000 * 60 * 60 * 24))
var date = new Date(modelData.lastUsed);
var now = new Date();
var diffMs = now - date;
var diffMins = Math.floor(diffMs / (1000 * 60));
var diffHours = Math.floor(diffMs / (1000 * 60 * 60));
var diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
if (diffMins < 1)
return I18n.tr("Last launched just now")
return I18n.tr("Last launched just now");
if (diffMins < 60)
return I18n.tr("Last launched %1 minute%2 ago")
.arg(diffMins)
.arg(diffMins === 1 ? "" : "s")
return I18n.tr("Last launched %1 minute%2 ago").arg(diffMins).arg(diffMins === 1 ? "" : "s");
if (diffHours < 24)
return I18n.tr("Last launched %1 hour%2 ago")
.arg(diffHours)
.arg(diffHours === 1 ? "" : "s")
return I18n.tr("Last launched %1 hour%2 ago").arg(diffHours).arg(diffHours === 1 ? "" : "s");
if (diffDays < 7)
return I18n.tr("Last launched %1 day%2 ago")
.arg(diffDays)
.arg(diffDays === 1 ? "" : "s")
return I18n.tr("Last launched %1 day%2 ago").arg(diffDays).arg(diffDays === 1 ? "" : "s");
return I18n.tr("Last launched %1")
.arg(date.toLocaleDateString())
return I18n.tr("Last launched %1").arg(date.toLocaleDateString());
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
@@ -858,13 +836,10 @@ Item {
iconSize: 16
iconColor: Theme.error
onClicked: {
var currentRanking = Object.assign(
{},
AppUsageHistoryData.appUsageRanking
|| {})
delete currentRanking[modelData.id]
AppUsageHistoryData.appUsageRanking = currentRanking
AppUsageHistoryData.saveSettings()
var currentRanking = Object.assign({}, AppUsageHistoryData.appUsageRanking || {});
delete currentRanking[modelData.id];
AppUsageHistoryData.appUsageRanking = currentRanking;
AppUsageHistoryData.saveSettings();
}
}
}
@@ -872,8 +847,7 @@ Item {
StyledText {
width: parent.width
text: recentlyUsedSection.rankedAppsModel.length
=== 0 ? "No apps have been launched yet." : ""
text: recentlyUsedSection.rankedAppsModel.length === 0 ? "No apps have been launched yet." : ""
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter
@@ -66,13 +66,14 @@ Item {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
// Wallpaper Section
+58 -63
View File
@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Services
import qs.Widgets
@@ -15,10 +14,10 @@ StyledRect {
property string pluginId: pluginData ? pluginData.id : ""
property string pluginDirectoryName: {
if (pluginData && pluginData.pluginDirectory) {
var path = pluginData.pluginDirectory
return path.substring(path.lastIndexOf('/') + 1)
var path = pluginData.pluginDirectory;
return path.substring(path.lastIndexOf('/') + 1);
}
return pluginId
return pluginId;
}
property string pluginName: pluginData ? (pluginData.name || pluginData.id) : ""
property string pluginVersion: pluginData ? (pluginData.version || "1.0.0") : ""
@@ -44,7 +43,7 @@ StyledRect {
cursorShape: root.hasSettings ? Qt.PointingHandCursor : Qt.ArrowCursor
enabled: root.hasSettings
onClicked: {
root.expandedPluginId = root.expandedPluginId === root.pluginId ? "" : root.pluginId
root.expandedPluginId = root.expandedPluginId === root.pluginId ? "" : root.pluginId;
}
}
@@ -127,32 +126,32 @@ StyledRect {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
const currentPluginName = root.pluginName
const currentPluginId = root.pluginId
const currentPluginName = root.pluginName;
const currentPluginId = root.pluginId;
DMSService.update(currentPluginName, response => {
if (response.error) {
ToastService.showError("Update failed: " + response.error)
ToastService.showError("Update failed: " + response.error);
} else {
ToastService.showInfo("Plugin updated: " + currentPluginName)
PluginService.forceRescanPlugin(currentPluginId)
ToastService.showInfo("Plugin updated: " + currentPluginName);
PluginService.forceRescanPlugin(currentPluginId);
if (DMSService.apiVersion >= 8) {
DMSService.listInstalled()
DMSService.listInstalled();
}
}
})
});
}
onEntered: {
tooltipLoader.active = true
tooltipLoader.active = true;
if (tooltipLoader.item) {
const p = mapToItem(null, width / 2, 0)
tooltipLoader.item.show(I18n.tr("Update Plugin"), p.x, p.y - 40, null)
const p = mapToItem(null, width / 2, 0);
tooltipLoader.item.show(I18n.tr("Update Plugin"), p.x, p.y - 40, null);
}
}
onExited: {
if (tooltipLoader.item) {
tooltipLoader.item.hide()
tooltipLoader.item.hide();
}
tooltipLoader.active = false
tooltipLoader.active = false;
}
}
}
@@ -177,31 +176,31 @@ StyledRect {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
const currentPluginName = root.pluginName
const currentPluginName = root.pluginName;
DMSService.uninstall(currentPluginName, response => {
if (response.error) {
ToastService.showError("Uninstall failed: " + response.error)
ToastService.showError("Uninstall failed: " + response.error);
} else {
ToastService.showInfo("Plugin uninstalled: " + currentPluginName)
PluginService.scanPlugins()
ToastService.showInfo("Plugin uninstalled: " + currentPluginName);
PluginService.scanPlugins();
if (root.isExpanded) {
root.expandedPluginId = ""
root.expandedPluginId = "";
}
}
})
});
}
onEntered: {
tooltipLoader.active = true
tooltipLoader.active = true;
if (tooltipLoader.item) {
const p = mapToItem(null, width / 2, 0)
tooltipLoader.item.show(I18n.tr("Uninstall Plugin"), p.x, p.y - 40, null)
const p = mapToItem(null, width / 2, 0);
tooltipLoader.item.show(I18n.tr("Uninstall Plugin"), p.x, p.y - 40, null);
}
}
onExited: {
if (tooltipLoader.item) {
tooltipLoader.item.hide()
tooltipLoader.item.hide();
}
tooltipLoader.active = false
tooltipLoader.active = false;
}
}
}
@@ -226,28 +225,28 @@ StyledRect {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
const currentPluginId = root.pluginId
const currentPluginName = root.pluginName
root.isReloading = true
const currentPluginId = root.pluginId;
const currentPluginName = root.pluginName;
root.isReloading = true;
if (PluginService.reloadPlugin(currentPluginId)) {
ToastService.showInfo("Plugin reloaded: " + currentPluginName)
ToastService.showInfo("Plugin reloaded: " + currentPluginName);
} else {
ToastService.showError("Failed to reload plugin: " + currentPluginName)
root.isReloading = false
ToastService.showError("Failed to reload plugin: " + currentPluginName);
root.isReloading = false;
}
}
onEntered: {
tooltipLoader.active = true
tooltipLoader.active = true;
if (tooltipLoader.item) {
const p = mapToItem(null, width / 2, 0)
tooltipLoader.item.show(I18n.tr("Reload Plugin"), p.x, p.y - 40, null)
const p = mapToItem(null, width / 2, 0);
tooltipLoader.item.show(I18n.tr("Reload Plugin"), p.x, p.y - 40, null);
}
}
onExited: {
if (tooltipLoader.item) {
tooltipLoader.item.hide()
tooltipLoader.item.hide();
}
tooltipLoader.active = false
tooltipLoader.active = false;
}
}
}
@@ -257,25 +256,25 @@ StyledRect {
anchors.verticalCenter: parent.verticalCenter
checked: PluginService.isPluginLoaded(root.pluginId)
onToggled: isChecked => {
const currentPluginId = root.pluginId
const currentPluginName = root.pluginName
const currentPluginId = root.pluginId;
const currentPluginName = root.pluginName;
if (isChecked) {
if (PluginService.enablePlugin(currentPluginId)) {
ToastService.showInfo("Plugin enabled: " + currentPluginName)
ToastService.showInfo("Plugin enabled: " + currentPluginName);
} else {
ToastService.showError("Failed to enable plugin: " + currentPluginName)
checked = false
ToastService.showError("Failed to enable plugin: " + currentPluginName);
checked = false;
}
} else {
if (PluginService.disablePlugin(currentPluginId)) {
ToastService.showInfo("Plugin disabled: " + currentPluginName)
ToastService.showInfo("Plugin disabled: " + currentPluginName);
if (root.isExpanded) {
root.expandedPluginId = ""
root.expandedPluginId = "";
}
} else {
ToastService.showError("Failed to disable plugin: " + currentPluginName)
checked = true
ToastService.showError("Failed to disable plugin: " + currentPluginName);
checked = true;
}
}
}
@@ -330,7 +329,7 @@ StyledRect {
focus: root.isExpanded && root.hasSettings
Keys.onPressed: event => {
event.accepted = true
event.accepted = true;
}
Rectangle {
@@ -350,38 +349,34 @@ StyledRect {
source: {
if (active && root.pluginSettingsPath) {
var path = root.pluginSettingsPath
var path = root.pluginSettingsPath;
if (!path.startsWith("file://")) {
path = "file://" + path
path = "file://" + path;
}
return path
return path;
}
return ""
return "";
}
onLoaded: {
if (item && typeof PluginService !== "undefined") {
item.pluginService = PluginService
item.pluginService = PluginService;
}
if (item && typeof PopoutService !== "undefined" && "popoutService" in item) {
item.popoutService = PopoutService
item.popoutService = PopoutService;
}
if (item) {
Qt.callLater(() => {
settingsContainer.focus = true
item.forceActiveFocus()
})
settingsContainer.focus = true;
item.forceActiveFocus();
});
}
}
}
StyledText {
anchors.centerIn: parent
text: !PluginService.isPluginLoaded(root.pluginId) ?
"Enable plugin to access settings" :
(settingsLoader.status === Loader.Error ?
"Failed to load settings" :
"No configurable settings")
text: !PluginService.isPluginLoaded(root.pluginId) ? "Enable plugin to access settings" : (settingsLoader.status === Loader.Error ? "Failed to load settings" : "No configurable settings")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
visible: root.isExpanded && (!settingsLoader.active || settingsLoader.status === Loader.Error)
+40 -41
View File
@@ -1,8 +1,5 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Common
import qs.Modals.Common
import qs.Services
import qs.Widgets
@@ -20,13 +17,14 @@ FocusScope {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
StyledRect {
@@ -126,7 +124,7 @@ FocusScope {
iconName: "store"
enabled: DMSService.dmsAvailable
onClicked: {
pluginBrowserModal.show()
pluginBrowserModal.show();
}
}
@@ -134,12 +132,12 @@ FocusScope {
text: I18n.tr("Scan")
iconName: "refresh"
onClicked: {
pluginsTab.isRefreshingPlugins = true
PluginService.scanPlugins()
pluginsTab.isRefreshingPlugins = true;
PluginService.scanPlugins();
if (DMSService.dmsAvailable) {
DMSService.listInstalled()
DMSService.listInstalled();
}
pluginsTab.refreshPluginList()
pluginsTab.refreshPluginList();
}
}
@@ -147,8 +145,8 @@ FocusScope {
text: I18n.tr("Create Dir")
iconName: "create_new_folder"
onClicked: {
PluginService.createPluginDirectory()
ToastService.showInfo("Created plugin directory: " + PluginService.pluginDirectory)
PluginService.createPluginDirectory();
ToastService.showInfo("Created plugin directory: " + PluginService.pluginDirectory);
}
}
}
@@ -226,15 +224,16 @@ FocusScope {
pluginData: modelData
expandedPluginId: pluginsTab.expandedPluginId
hasUpdate: {
if (DMSService.apiVersion < 8) return false
return pluginsTab.installedPluginsData[pluginId] || pluginsTab.installedPluginsData[pluginName] || false
if (DMSService.apiVersion < 8)
return false;
return pluginsTab.installedPluginsData[pluginId] || pluginsTab.installedPluginsData[pluginName] || false;
}
isReloading: pluginsTab.isReloading
onExpandedPluginIdChanged: {
pluginsTab.expandedPluginId = expandedPluginId
pluginsTab.expandedPluginId = expandedPluginId;
}
onIsReloadingChanged: {
pluginsTab.isReloading = isReloading
pluginsTab.isReloading = isReloading;
}
}
}
@@ -255,69 +254,69 @@ FocusScope {
function refreshPluginList() {
Qt.callLater(() => {
var plugins = PluginService.getAvailablePlugins()
pluginRepeater.model = null
pluginRepeater.model = plugins
pluginsTab.isRefreshingPlugins = false
})
var plugins = PluginService.getAvailablePlugins();
pluginRepeater.model = null;
pluginRepeater.model = plugins;
pluginsTab.isRefreshingPlugins = false;
});
}
Connections {
target: PluginService
function onPluginLoaded() {
refreshPluginList()
refreshPluginList();
if (isReloading) {
isReloading = false
isReloading = false;
}
}
function onPluginUnloaded() {
refreshPluginList()
refreshPluginList();
if (!isReloading && pluginsTab.expandedPluginId !== "" && !PluginService.isPluginLoaded(pluginsTab.expandedPluginId)) {
pluginsTab.expandedPluginId = ""
pluginsTab.expandedPluginId = "";
}
}
function onPluginListUpdated() {
if (DMSService.apiVersion >= 8) {
DMSService.listInstalled()
DMSService.listInstalled();
}
refreshPluginList()
refreshPluginList();
}
}
Connections {
target: DMSService
function onPluginsListReceived(plugins) {
pluginBrowserModal.isLoading = false
pluginBrowserModal.allPlugins = plugins
pluginBrowserModal.updateFilteredPlugins()
pluginBrowserModal.isLoading = false;
pluginBrowserModal.allPlugins = plugins;
pluginBrowserModal.updateFilteredPlugins();
}
function onInstalledPluginsReceived(plugins) {
var pluginMap = {}
var pluginMap = {};
for (var i = 0; i < plugins.length; i++) {
var plugin = plugins[i]
var hasUpdate = plugin.hasUpdate || false
var plugin = plugins[i];
var hasUpdate = plugin.hasUpdate || false;
if (plugin.id) {
pluginMap[plugin.id] = hasUpdate
pluginMap[plugin.id] = hasUpdate;
}
if (plugin.name) {
pluginMap[plugin.name] = hasUpdate
pluginMap[plugin.name] = hasUpdate;
}
}
installedPluginsData = pluginMap
Qt.callLater(refreshPluginList)
installedPluginsData = pluginMap;
Qt.callLater(refreshPluginList);
}
function onOperationSuccess(message) {
ToastService.showInfo(message)
ToastService.showInfo(message);
}
function onOperationError(error) {
ToastService.showError(error)
ToastService.showError(error);
}
}
Component.onCompleted: {
pluginBrowserModal.parentModal = pluginsTab.parentModal
pluginBrowserModal.parentModal = pluginsTab.parentModal;
if (DMSService.dmsAvailable && DMSService.apiVersion >= 8) {
DMSService.listInstalled()
DMSService.listInstalled();
}
}
@@ -16,7 +16,7 @@ Column {
spacing: expanded ? Theme.spacingM : 0
Component.onCompleted: {
if (!collapsible)
expanded = true
expanded = true;
}
MouseArea {
@@ -26,15 +26,12 @@ Column {
hoverEnabled: collapsible
onClicked: {
if (collapsible)
expanded = !expanded
expanded = !expanded;
}
Rectangle {
anchors.fill: parent
color: parent.containsMouse ? Qt.rgba(Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.08) : "transparent"
color: parent.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
radius: Theme.radiusS
}
@@ -59,13 +59,14 @@ Item {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
// Theme Color
+255 -256
View File
@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts
import qs.Common
@@ -12,13 +11,14 @@ Item {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
StyledRect {
@@ -26,8 +26,7 @@ Item {
height: timeSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -49,8 +48,7 @@ Item {
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- toggle.width - Theme.spacingM
width: parent.width - Theme.iconSize - Theme.spacingM - toggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
@@ -76,9 +74,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.use24HourClock
onToggled: checked => {
return SettingsData.set("use24HourClock",
checked)
}
return SettingsData.set("use24HourClock", checked);
}
}
}
}
@@ -89,8 +86,7 @@ Item {
height: timeSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -112,8 +108,7 @@ Item {
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- toggle.width - Theme.spacingM
width: parent.width - Theme.iconSize - Theme.spacingM - toggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
@@ -139,9 +134,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.showSeconds
onToggled: checked => {
return SettingsData.set("showSeconds",
checked)
}
return SettingsData.set("showSeconds", checked);
}
}
}
}
@@ -152,8 +146,7 @@ Item {
height: dateSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -189,60 +182,67 @@ Item {
description: "Preview: " + (SettingsData.clockDateFormat ? new Date().toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat) : new Date().toLocaleDateString(Qt.locale(), "ddd d"))
currentValue: {
if (!SettingsData.clockDateFormat || SettingsData.clockDateFormat.length === 0) {
return "System Default"
return "System Default";
}
const presets = [{
"format": "ddd d",
"label": "Day Date"
}, {
"format": "ddd MMM d",
"label": "Day Month Date"
}, {
"format": "MMM d",
"label": "Month Date"
}, {
"format": "M/d",
"label": "Numeric (M/D)"
}, {
"format": "d/M",
"label": "Numeric (D/M)"
}, {
"format": "ddd d MMM yyyy",
"label": "Full with Year"
}, {
"format": "yyyy-MM-dd",
"label": "ISO Date"
}, {
"format": "dddd, MMMM d",
"label": "Full Day & Month"
}]
const presets = [
{
"format": "ddd d",
"label": "Day Date"
},
{
"format": "ddd MMM d",
"label": "Day Month Date"
},
{
"format": "MMM d",
"label": "Month Date"
},
{
"format": "M/d",
"label": "Numeric (M/D)"
},
{
"format": "d/M",
"label": "Numeric (D/M)"
},
{
"format": "ddd d MMM yyyy",
"label": "Full with Year"
},
{
"format": "yyyy-MM-dd",
"label": "ISO Date"
},
{
"format": "dddd, MMMM d",
"label": "Full Day & Month"
}
];
const match = presets.find(p => {
return p.format
=== SettingsData.clockDateFormat
})
return match ? match.label: I18n.tr("Custom: ") + SettingsData.clockDateFormat
return p.format === SettingsData.clockDateFormat;
});
return match ? match.label : I18n.tr("Custom: ") + SettingsData.clockDateFormat;
}
options: ["System Default", "Day Date", "Day Month Date", "Month Date", "Numeric (M/D)", "Numeric (D/M)", "Full with Year", "ISO Date", "Full Day & Month", "Custom..."]
onValueChanged: value => {
const formatMap = {
"System Default": "",
"Day Date": "ddd d",
"Day Month Date": "ddd MMM d",
"Month Date": "MMM d",
"Numeric (M/D)": "M/d",
"Numeric (D/M)": "d/M",
"Full with Year": "ddd d MMM yyyy",
"ISO Date": "yyyy-MM-dd",
"Full Day & Month": "dddd, MMMM d"
}
if (value === "Custom...") {
customFormatInput.visible = true
} else {
customFormatInput.visible = false
SettingsData.set("clockDateFormat",
formatMap[value])
}
}
const formatMap = {
"System Default": "",
"Day Date": "ddd d",
"Day Month Date": "ddd MMM d",
"Month Date": "MMM d",
"Numeric (M/D)": "M/d",
"Numeric (D/M)": "d/M",
"Full with Year": "ddd d MMM yyyy",
"ISO Date": "yyyy-MM-dd",
"Full Day & Month": "dddd, MMMM d"
};
if (value === "Custom...") {
customFormatInput.visible = true;
} else {
customFormatInput.visible = false;
SettingsData.set("clockDateFormat", formatMap[value]);
}
}
}
DankDropdown {
@@ -251,60 +251,67 @@ Item {
description: "Preview: " + (SettingsData.lockDateFormat ? new Date().toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat) : new Date().toLocaleDateString(Qt.locale(), Locale.LongFormat))
currentValue: {
if (!SettingsData.lockDateFormat || SettingsData.lockDateFormat.length === 0) {
return "System Default"
return "System Default";
}
const presets = [{
"format": "ddd d",
"label": "Day Date"
}, {
"format": "ddd MMM d",
"label": "Day Month Date"
}, {
"format": "MMM d",
"label": "Month Date"
}, {
"format": "M/d",
"label": "Numeric (M/D)"
}, {
"format": "d/M",
"label": "Numeric (D/M)"
}, {
"format": "ddd d MMM yyyy",
"label": "Full with Year"
}, {
"format": "yyyy-MM-dd",
"label": "ISO Date"
}, {
"format": "dddd, MMMM d",
"label": "Full Day & Month"
}]
const presets = [
{
"format": "ddd d",
"label": "Day Date"
},
{
"format": "ddd MMM d",
"label": "Day Month Date"
},
{
"format": "MMM d",
"label": "Month Date"
},
{
"format": "M/d",
"label": "Numeric (M/D)"
},
{
"format": "d/M",
"label": "Numeric (D/M)"
},
{
"format": "ddd d MMM yyyy",
"label": "Full with Year"
},
{
"format": "yyyy-MM-dd",
"label": "ISO Date"
},
{
"format": "dddd, MMMM d",
"label": "Full Day & Month"
}
];
const match = presets.find(p => {
return p.format
=== SettingsData.lockDateFormat
})
return match ? match.label: I18n.tr("Custom: ") + SettingsData.lockDateFormat
return p.format === SettingsData.lockDateFormat;
});
return match ? match.label : I18n.tr("Custom: ") + SettingsData.lockDateFormat;
}
options: ["System Default", "Day Date", "Day Month Date", "Month Date", "Numeric (M/D)", "Numeric (D/M)", "Full with Year", "ISO Date", "Full Day & Month", "Custom..."]
onValueChanged: value => {
const formatMap = {
"System Default": "",
"Day Date": "ddd d",
"Day Month Date": "ddd MMM d",
"Month Date": "MMM d",
"Numeric (M/D)": "M/d",
"Numeric (D/M)": "d/M",
"Full with Year": "ddd d MMM yyyy",
"ISO Date": "yyyy-MM-dd",
"Full Day & Month": "dddd, MMMM d"
}
if (value === "Custom...") {
customLockFormatInput.visible = true
} else {
customLockFormatInput.visible = false
SettingsData.set("lockDateFormat",
formatMap[value])
}
}
const formatMap = {
"System Default": "",
"Day Date": "ddd d",
"Day Month Date": "ddd MMM d",
"Month Date": "MMM d",
"Numeric (M/D)": "M/d",
"Numeric (D/M)": "d/M",
"Full with Year": "ddd d MMM yyyy",
"ISO Date": "yyyy-MM-dd",
"Full Day & Month": "dddd, MMMM d"
};
if (value === "Custom...") {
customLockFormatInput.visible = true;
} else {
customLockFormatInput.visible = false;
SettingsData.set("lockDateFormat", formatMap[value]);
}
}
}
DankTextField {
@@ -316,7 +323,7 @@ Item {
text: SettingsData.clockDateFormat
onTextChanged: {
if (visible && text)
SettingsData.set("clockDateFormat", text)
SettingsData.set("clockDateFormat", text);
}
}
@@ -329,7 +336,7 @@ Item {
text: SettingsData.lockDateFormat
onTextChanged: {
if (visible && text)
SettingsData.set("lockDateFormat", text)
SettingsData.set("lockDateFormat", text);
}
}
@@ -338,8 +345,7 @@ Item {
height: formatHelp.implicitHeight + Theme.spacingM * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.1)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
border.width: 0
Column {
@@ -440,8 +446,7 @@ Item {
height: enableWeatherSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -463,8 +468,7 @@ Item {
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- enableToggle.width - Theme.spacingM
width: parent.width - Theme.iconSize - Theme.spacingM - enableToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
@@ -490,9 +494,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.weatherEnabled
onToggled: checked => {
return SettingsData.set("weatherEnabled",
checked)
}
return SettingsData.set("weatherEnabled", checked);
}
}
}
}
@@ -503,8 +506,7 @@ Item {
height: temperatureSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
visible: SettingsData.weatherEnabled
opacity: visible ? 1 : 0
@@ -528,8 +530,7 @@ Item {
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- temperatureToggle.width - Theme.spacingM
width: parent.width - Theme.iconSize - Theme.spacingM - temperatureToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
@@ -555,9 +556,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.useFahrenheit
onToggled: checked => {
return SettingsData.set("useFahrenheit",
checked)
}
return SettingsData.set("useFahrenheit", checked);
}
}
}
}
@@ -575,8 +575,7 @@ Item {
height: locationSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
visible: SettingsData.weatherEnabled
opacity: visible ? 1 : 0
@@ -600,8 +599,7 @@ Item {
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- autoLocationToggle.width - Theme.spacingM
width: parent.width - Theme.iconSize - Theme.spacingM - autoLocationToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
@@ -627,9 +625,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.useAutoLocation
onToggled: checked => {
return SettingsData.set("useAutoLocation",
checked)
}
return SettingsData.set("useAutoLocation", checked);
}
}
}
@@ -653,113 +650,113 @@ Item {
}
Row {
width: parent.width
spacing: Theme.spacingM
width: parent.width
spacing: Theme.spacingM
Column {
width: (parent.width - Theme.spacingM) / 2
spacing: Theme.spacingXS
Column {
width: (parent.width - Theme.spacingM) / 2
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Latitude")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
DankTextField {
id: latitudeInput
width: parent.width
height: 48
placeholderText: "40.7128"
backgroundColor: Theme.surfaceVariant
normalBorderColor: Theme.primarySelected
focusedBorderColor: Theme.primary
keyNavigationTab: longitudeInput
Component.onCompleted: {
if (SettingsData.weatherCoordinates) {
const coords = SettingsData.weatherCoordinates.split(',')
if (coords.length > 0) {
text = coords[0].trim()
}
}
}
Connections {
target: SettingsData
function onWeatherCoordinatesChanged() {
if (SettingsData.weatherCoordinates) {
const coords = SettingsData.weatherCoordinates.split(',')
if (coords.length > 0) {
latitudeInput.text = coords[0].trim()
}
}
}
}
onTextEdited: {
if (text && longitudeInput.text) {
const coords = text + "," + longitudeInput.text
SettingsData.weatherCoordinates = coords
SettingsData.saveSettings()
}
}
}
StyledText {
text: I18n.tr("Latitude")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
Column {
width: (parent.width - Theme.spacingM) / 2
spacing: Theme.spacingXS
DankTextField {
id: latitudeInput
width: parent.width
height: 48
placeholderText: "40.7128"
backgroundColor: Theme.surfaceVariant
normalBorderColor: Theme.primarySelected
focusedBorderColor: Theme.primary
keyNavigationTab: longitudeInput
StyledText {
text: I18n.tr("Longitude")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
Component.onCompleted: {
if (SettingsData.weatherCoordinates) {
const coords = SettingsData.weatherCoordinates.split(',');
if (coords.length > 0) {
text = coords[0].trim();
}
}
}
DankTextField {
id: longitudeInput
width: parent.width
height: 48
placeholderText: "-74.0060"
backgroundColor: Theme.surfaceVariant
normalBorderColor: Theme.primarySelected
focusedBorderColor: Theme.primary
keyNavigationTab: locationSearchInput
keyNavigationBacktab: latitudeInput
Component.onCompleted: {
Connections {
target: SettingsData
function onWeatherCoordinatesChanged() {
if (SettingsData.weatherCoordinates) {
const coords = SettingsData.weatherCoordinates.split(',')
if (coords.length > 1) {
text = coords[1].trim()
const coords = SettingsData.weatherCoordinates.split(',');
if (coords.length > 0) {
latitudeInput.text = coords[0].trim();
}
}
}
}
Connections {
target: SettingsData
function onWeatherCoordinatesChanged() {
if (SettingsData.weatherCoordinates) {
const coords = SettingsData.weatherCoordinates.split(',')
if (coords.length > 1) {
longitudeInput.text = coords[1].trim()
}
}
}
}
onTextEdited: {
if (text && latitudeInput.text) {
const coords = latitudeInput.text + "," + text
SettingsData.weatherCoordinates = coords
SettingsData.saveSettings()
}
onTextEdited: {
if (text && longitudeInput.text) {
const coords = text + "," + longitudeInput.text;
SettingsData.weatherCoordinates = coords;
SettingsData.saveSettings();
}
}
}
}
Column {
width: (parent.width - Theme.spacingM) / 2
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Longitude")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
DankTextField {
id: longitudeInput
width: parent.width
height: 48
placeholderText: "-74.0060"
backgroundColor: Theme.surfaceVariant
normalBorderColor: Theme.primarySelected
focusedBorderColor: Theme.primary
keyNavigationTab: locationSearchInput
keyNavigationBacktab: latitudeInput
Component.onCompleted: {
if (SettingsData.weatherCoordinates) {
const coords = SettingsData.weatherCoordinates.split(',');
if (coords.length > 1) {
text = coords[1].trim();
}
}
}
Connections {
target: SettingsData
function onWeatherCoordinatesChanged() {
if (SettingsData.weatherCoordinates) {
const coords = SettingsData.weatherCoordinates.split(',');
if (coords.length > 1) {
longitudeInput.text = coords[1].trim();
}
}
}
}
onTextEdited: {
if (text && latitudeInput.text) {
const coords = latitudeInput.text + "," + text;
SettingsData.weatherCoordinates = coords;
SettingsData.saveSettings();
}
}
}
}
}
Column {
width: parent.width
spacing: Theme.spacingXS
@@ -778,14 +775,14 @@ Item {
placeholderText: I18n.tr("New York, NY")
keyNavigationBacktab: longitudeInput
onLocationSelected: (displayName, coordinates) => {
SettingsData.setWeatherLocation(displayName, coordinates)
SettingsData.setWeatherLocation(displayName, coordinates);
const coords = coordinates.split(',')
if (coords.length >= 2) {
latitudeInput.text = coords[0].trim()
longitudeInput.text = coords[1].trim()
}
}
const coords = coordinates.split(',');
if (coords.length >= 2) {
latitudeInput.text = coords[0].trim();
longitudeInput.text = coords[1].trim();
}
}
}
}
}
@@ -804,8 +801,7 @@ Item {
height: weatherDisplaySection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
visible: SettingsData.weatherEnabled
opacity: visible ? 1 : 0
@@ -882,9 +878,9 @@ Item {
hoverEnabled: true
cursorShape: parent.enabled ? Qt.PointingHandCursor : Qt.ForbiddenCursor
onClicked: {
refreshButton.isRefreshing = true
WeatherService.forceRefresh()
refreshTimer.restart()
refreshButton.isRefreshing = true;
WeatherService.forceRefresh();
refreshTimer.restart();
}
enabled: parent.enabled
}
@@ -965,7 +961,7 @@ Item {
cursorShape: Qt.PointingHandCursor
onClicked: {
if (WeatherService.weather.available) {
SettingsData.set("useFahrenheit", !SettingsData.useFahrenheit)
SettingsData.set("useFahrenheit", !SettingsData.useFahrenheit);
}
}
enabled: WeatherService.weather.available
@@ -1185,14 +1181,16 @@ Item {
StyledText {
text: {
if (!WeatherService.weather.wind) return "--"
const windKmh = parseFloat(WeatherService.weather.wind)
if (isNaN(windKmh)) return WeatherService.weather.wind
if (!WeatherService.weather.wind)
return "--";
const windKmh = parseFloat(WeatherService.weather.wind);
if (isNaN(windKmh))
return WeatherService.weather.wind;
if (SettingsData.useFahrenheit) {
const windMph = Math.round(windKmh * 0.621371)
return windMph + " mph"
const windMph = Math.round(windKmh * 0.621371);
return windMph + " mph";
}
return WeatherService.weather.wind
return WeatherService.weather.wind;
}
font.pixelSize: Theme.fontSizeSmall + 1
color: Theme.surfaceText
@@ -1241,13 +1239,14 @@ Item {
StyledText {
text: {
if (!WeatherService.weather.pressure) return "--"
const pressureHpa = WeatherService.weather.pressure
if (!WeatherService.weather.pressure)
return "--";
const pressureHpa = WeatherService.weather.pressure;
if (SettingsData.useFahrenheit) {
const pressureInHg = (pressureHpa * 0.02953).toFixed(2)
return pressureInHg + " inHg"
const pressureInHg = (pressureHpa * 0.02953).toFixed(2);
return pressureInHg + " inHg";
}
return pressureHpa + " hPa"
return pressureHpa + " hPa";
}
font.pixelSize: Theme.fontSizeSmall + 1
color: Theme.surfaceText
@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Modals.Common
import qs.Widgets
+100 -113
View File
@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Services
import qs.Widgets
@@ -10,12 +9,13 @@ Item {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
width: parent.width
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
StyledRect {
@@ -23,8 +23,7 @@ Item {
height: workspaceSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -60,9 +59,8 @@ Item {
description: I18n.tr("Show workspace index numbers in the top bar workspace switcher")
checked: SettingsData.showWorkspaceIndex
onToggled: checked => {
return SettingsData.set("showWorkspaceIndex",
checked)
}
return SettingsData.set("showWorkspaceIndex", checked);
}
}
DankToggle {
width: parent.width
@@ -70,9 +68,8 @@ Item {
description: I18n.tr("Always show a minimum of 3 workspaces, even if fewer are available")
checked: SettingsData.showWorkspacePadding
onToggled: checked => {
return SettingsData.set("showWorkspacePadding",
checked)
}
return SettingsData.set("showWorkspacePadding", checked);
}
}
DankToggle {
@@ -82,12 +79,11 @@ Item {
checked: SettingsData.showWorkspaceApps
visible: CompositorService.isNiri || CompositorService.isHyprland
onToggled: checked => {
return SettingsData.set("showWorkspaceApps",
checked)
}
return SettingsData.set("showWorkspaceApps", checked);
}
}
Row {
Row {
width: parent.width - Theme.spacingL
spacing: Theme.spacingL
visible: SettingsData.showWorkspaceApps
@@ -116,7 +112,7 @@ Item {
topPadding: Theme.spacingXS
bottomPadding: Theme.spacingXS
onEditingFinished: {
SettingsData.set("maxWorkspaceIcons", parseInt(text, 10))
SettingsData.set("maxWorkspaceIcons", parseInt(text, 10));
}
}
}
@@ -139,6 +135,17 @@ Item {
}
}
DankToggle {
width: parent.width
text: I18n.tr("Show Occupied Workspaces Only")
description: I18n.tr("Display only workspaces that contain windows")
checked: SettingsData.showOccupiedWorkspacesOnly
visible: CompositorService.isNiri || CompositorService.isHyprland
onToggled: checked => {
return SettingsData.set("showOccupiedWorkspacesOnly", checked);
}
}
DankToggle {
width: parent.width
text: I18n.tr("Show All Tags")
@@ -157,8 +164,7 @@ Item {
height: mediaSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -290,8 +296,8 @@ Item {
MouseArea {
anchors.fill: parent
onPressed: mouse => {
updaterCustomCommand.forceActiveFocus()
mouse.accepted = false
updaterCustomCommand.forceActiveFocus();
mouse.accepted = false;
}
}
}
@@ -337,8 +343,8 @@ Item {
MouseArea {
anchors.fill: parent
onPressed: mouse => {
updaterTerminalCustomClass.forceActiveFocus()
mouse.accepted = false
updaterTerminalCustomClass.forceActiveFocus();
mouse.accepted = false;
}
}
}
@@ -352,8 +358,7 @@ Item {
height: runningAppsSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
visible: CompositorService.isNiri || CompositorService.isHyprland
@@ -390,9 +395,8 @@ Item {
description: I18n.tr("Show only apps running in current workspace")
checked: SettingsData.runningAppsCurrentWorkspace
onToggled: checked => {
return SettingsData.set("runningAppsCurrentWorkspace",
checked)
}
return SettingsData.set("runningAppsCurrentWorkspace", checked);
}
}
}
}
@@ -402,8 +406,7 @@ Item {
height: workspaceIconsSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
visible: SettingsData.hasNamedWorkspaces()
@@ -449,12 +452,8 @@ Item {
width: parent.width
height: workspaceIconRow.implicitHeight + Theme.spacingM
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r,
Theme.surfaceContainer.g,
Theme.surfaceContainer.b, 0.5)
border.color: Qt.rgba(Theme.outline.r,
Theme.outline.g,
Theme.outline.b, 0.3)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
border.width: 0
Row {
@@ -482,35 +481,28 @@ Item {
anchors.verticalCenter: parent.verticalCenter
Component.onCompleted: {
var iconData = SettingsData.getWorkspaceNameIcon(
modelData)
var iconData = SettingsData.getWorkspaceNameIcon(modelData);
if (iconData) {
setIcon(iconData.value,
iconData.type)
setIcon(iconData.value, iconData.type);
}
}
onIconSelected: (iconName, iconType) => {
SettingsData.setWorkspaceNameIcon(
modelData, {
"type": iconType,
"value": iconName
})
setIcon(iconName,
iconType)
}
SettingsData.setWorkspaceNameIcon(modelData, {
"type": iconType,
"value": iconName
});
setIcon(iconName, iconType);
}
Connections {
target: SettingsData
function onWorkspaceIconsUpdated() {
var iconData = SettingsData.getWorkspaceNameIcon(
modelData)
var iconData = SettingsData.getWorkspaceNameIcon(modelData);
if (iconData) {
iconPicker.setIcon(
iconData.value,
iconData.type)
iconPicker.setIcon(iconData.value, iconData.type);
} else {
iconPicker.setIcon("", "icon")
iconPicker.setIcon("", "icon");
}
}
}
@@ -539,8 +531,7 @@ Item {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
SettingsData.removeWorkspaceNameIcon(
modelData)
SettingsData.removeWorkspaceNameIcon(modelData);
}
}
}
@@ -560,8 +551,7 @@ Item {
height: notificationSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -603,41 +593,41 @@ Item {
description: I18n.tr("Choose where notification popups appear on screen")
currentValue: {
if (SettingsData.notificationPopupPosition === -1) {
return "Top Center"
return "Top Center";
}
switch (SettingsData.notificationPopupPosition) {
case SettingsData.Position.Top:
return "Top Right"
return "Top Right";
case SettingsData.Position.Bottom:
return "Bottom Left"
return "Bottom Left";
case SettingsData.Position.Left:
return "Top Left"
return "Top Left";
case SettingsData.Position.Right:
return "Bottom Right"
return "Bottom Right";
default:
return "Top Right"
return "Top Right";
}
}
options: ["Top Right", "Top Left", "Top Center", "Bottom Right", "Bottom Left"]
onValueChanged: value => {
switch (value) {
case "Top Right":
SettingsData.set("notificationPopupPosition", SettingsData.Position.Top)
break
SettingsData.set("notificationPopupPosition", SettingsData.Position.Top);
break;
case "Top Left":
SettingsData.set("notificationPopupPosition", SettingsData.Position.Left)
break
SettingsData.set("notificationPopupPosition", SettingsData.Position.Left);
break;
case "Top Center":
SettingsData.set("notificationPopupPosition", -1)
break
SettingsData.set("notificationPopupPosition", -1);
break;
case "Bottom Right":
SettingsData.set("notificationPopupPosition", SettingsData.Position.Right)
break
SettingsData.set("notificationPopupPosition", SettingsData.Position.Right);
break;
case "Bottom Left":
SettingsData.set("notificationPopupPosition", SettingsData.Position.Bottom)
break
SettingsData.set("notificationPopupPosition", SettingsData.Position.Bottom);
break;
}
SettingsData.sendTestNotifications()
SettingsData.sendTestNotifications();
}
}
}
@@ -649,8 +639,7 @@ Item {
height: osdRow.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Row {
@@ -694,8 +683,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.osdAlwaysShowValue
onToggleCompleted: checked => {
SettingsData.set("osdAlwaysShowValue", checked)
}
SettingsData.set("osdAlwaysShowValue", checked);
}
}
}
}
@@ -705,8 +694,7 @@ Item {
height: osdSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
@@ -749,52 +737,52 @@ Item {
currentValue: {
switch (SettingsData.osdPosition) {
case SettingsData.Position.Top:
return "Top Right"
return "Top Right";
case SettingsData.Position.Left:
return "Top Left"
return "Top Left";
case SettingsData.Position.TopCenter:
return "Top Center"
return "Top Center";
case SettingsData.Position.Right:
return "Bottom Right"
return "Bottom Right";
case SettingsData.Position.Bottom:
return "Bottom Left"
return "Bottom Left";
case SettingsData.Position.BottomCenter:
return "Bottom Center"
return "Bottom Center";
case SettingsData.Position.LeftCenter:
return "Left Center"
return "Left Center";
case SettingsData.Position.RightCenter:
return "Right Center"
return "Right Center";
default:
return "Bottom Center"
return "Bottom Center";
}
}
options: ["Top Right", "Top Left", "Top Center", "Bottom Right", "Bottom Left", "Bottom Center", "Left Center", "Right Center"]
onValueChanged: value => {
switch (value) {
case "Top Right":
SettingsData.set("osdPosition", SettingsData.Position.Top)
break
SettingsData.set("osdPosition", SettingsData.Position.Top);
break;
case "Top Left":
SettingsData.set("osdPosition", SettingsData.Position.Left)
break
SettingsData.set("osdPosition", SettingsData.Position.Left);
break;
case "Top Center":
SettingsData.set("osdPosition", SettingsData.Position.TopCenter)
break
SettingsData.set("osdPosition", SettingsData.Position.TopCenter);
break;
case "Bottom Right":
SettingsData.set("osdPosition", SettingsData.Position.Right)
break
SettingsData.set("osdPosition", SettingsData.Position.Right);
break;
case "Bottom Left":
SettingsData.set("osdPosition", SettingsData.Position.Bottom)
break
SettingsData.set("osdPosition", SettingsData.Position.Bottom);
break;
case "Bottom Center":
SettingsData.set("osdPosition", SettingsData.Position.BottomCenter)
break
SettingsData.set("osdPosition", SettingsData.Position.BottomCenter);
break;
case "Left Center":
SettingsData.set("osdPosition", SettingsData.Position.LeftCenter)
break
SettingsData.set("osdPosition", SettingsData.Position.LeftCenter);
break;
case "Right Center":
SettingsData.set("osdPosition", SettingsData.Position.RightCenter)
break
SettingsData.set("osdPosition", SettingsData.Position.RightCenter);
break;
}
}
}
@@ -806,18 +794,17 @@ Item {
description: I18n.tr("Show on-screen display when volume changes")
checked: SettingsData.osdVolumeEnabled
onToggled: checked => {
return SettingsData.set("osdVolumeEnabled", checked)
return SettingsData.set("osdVolumeEnabled", checked);
}
}
DankToggle {
width: parent.width
text: I18n.tr("Media Volume OSD")
description: I18n.tr("Show on-screen display when media player volume changes")
checked: SettingsData.osdMediaVolumeEnabled
onToggled: checked => {
return SettingsData.set("osdMediaVolumeEnabled", checked)
return SettingsData.set("osdMediaVolumeEnabled", checked);
}
}
@@ -827,7 +814,7 @@ Item {
description: I18n.tr("Show on-screen display when brightness changes")
checked: SettingsData.osdBrightnessEnabled
onToggled: checked => {
return SettingsData.set("osdBrightnessEnabled", checked)
return SettingsData.set("osdBrightnessEnabled", checked);
}
}
@@ -837,7 +824,7 @@ Item {
description: I18n.tr("Show on-screen display when idle inhibitor state changes")
checked: SettingsData.osdIdleInhibitorEnabled
onToggled: checked => {
return SettingsData.set("osdIdleInhibitorEnabled", checked)
return SettingsData.set("osdIdleInhibitorEnabled", checked);
}
}
@@ -847,7 +834,7 @@ Item {
description: I18n.tr("Show on-screen display when microphone is muted/unmuted")
checked: SettingsData.osdMicMuteEnabled
onToggled: checked => {
return SettingsData.set("osdMicMuteEnabled", checked)
return SettingsData.set("osdMicMuteEnabled", checked);
}
}
@@ -857,7 +844,7 @@ Item {
description: I18n.tr("Show on-screen display when caps lock state changes")
checked: SettingsData.osdCapsLockEnabled
onToggled: checked => {
return SettingsData.set("osdCapsLockEnabled", checked)
return SettingsData.set("osdCapsLockEnabled", checked);
}
}
@@ -867,7 +854,7 @@ Item {
description: I18n.tr("Show on-screen display when power profile changes")
checked: SettingsData.osdPowerProfileEnabled
onToggled: checked => {
return SettingsData.set("osdPowerProfileEnabled", checked)
return SettingsData.set("osdPowerProfileEnabled", checked);
}
}
}
+8 -12
View File
@@ -1,5 +1,4 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
@@ -19,12 +18,12 @@ Singleton {
command: ["which", "cava"]
running: false
onExited: exitCode => {
root.cavaAvailable = exitCode === 0
root.cavaAvailable = exitCode === 0;
}
}
Component.onCompleted: {
cavaCheck.running = true
cavaCheck.running = true;
}
Process {
@@ -35,21 +34,18 @@ Singleton {
onRunningChanged: {
if (!running) {
root.values = Array(6).fill(0)
root.values = Array(6).fill(0);
}
}
stdout: SplitParser {
splitMarker: "\n"
onRead: data => {
if (root.refCount > 0 && data.trim()) {
let points = data.split(";").map(p => {
return parseInt(p.trim(), 10)
}).filter(p => {
return !isNaN(p)
})
if (points.length >= 6) {
root.values = points.slice(0, 6)
if (root.refCount > 0 && data.length > 0) {
const parts = data.split(";");
if (parts.length >= 6) {
const points = [parseInt(parts[0], 10), parseInt(parts[1], 10), parseInt(parts[2], 10), parseInt(parts[3], 10), parseInt(parts[4], 10), parseInt(parts[5], 10)];
root.values = points;
}
}
}
+69 -58
View File
@@ -4,26 +4,24 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Wayland
import Quickshell.Services.Mpris
import qs.Common
import qs.Services
Singleton {
id: root
readonly property bool idleMonitorAvailable: {
try {
return typeof IdleMonitor !== "undefined"
return typeof IdleMonitor !== "undefined";
} catch (e) {
return false
return false;
}
}
readonly property bool idleInhibitorAvailable: {
try {
return typeof IdleInhibitor !== "undefined"
return typeof IdleInhibitor !== "undefined";
} catch (e) {
return false
return false;
}
}
@@ -44,32 +42,37 @@ Singleton {
onSuspendTimeoutChanged: _rearmIdleMonitors()
function _rearmIdleMonitors() {
_enableGate = false
Qt.callLater(() => { _enableGate = true })
_enableGate = false;
Qt.callLater(() => {
_enableGate = true;
});
}
signal lockRequested()
signal requestMonitorOff()
signal requestMonitorOn()
signal requestSuspend()
signal lockRequested
signal fadeToLockRequested
signal cancelFadeToLock
signal requestMonitorOff
signal requestMonitorOn
signal requestSuspend
property var monitorOffMonitor: null
property var lockMonitor: null
property var suspendMonitor: null
property var mediaInhibitor: null
property var lockComponent: null
function wake() {
requestMonitorOn()
requestMonitorOn();
}
function createMediaInhibitor() {
if (!idleInhibitorAvailable) {
return
return;
}
if (mediaInhibitor) {
mediaInhibitor.destroy()
mediaInhibitor = null
mediaInhibitor.destroy();
mediaInhibitor = null;
}
const inhibitorString = `
@@ -79,23 +82,23 @@ Singleton {
IdleInhibitor {
active: false
}
`
`;
mediaInhibitor = Qt.createQmlObject(inhibitorString, root, "IdleService.MediaInhibitor")
mediaInhibitor.active = Qt.binding(() => root.mediaPlaying)
mediaInhibitor = Qt.createQmlObject(inhibitorString, root, "IdleService.MediaInhibitor");
mediaInhibitor.active = Qt.binding(() => root.mediaPlaying);
}
function destroyMediaInhibitor() {
if (mediaInhibitor) {
mediaInhibitor.destroy()
mediaInhibitor = null
mediaInhibitor.destroy();
mediaInhibitor = null;
}
}
function createIdleMonitors() {
if (!idleMonitorAvailable) {
console.info("IdleService: IdleMonitor not available, skipping creation")
return
console.info("IdleService: IdleMonitor not available, skipping creation");
return;
}
try {
@@ -108,60 +111,68 @@ Singleton {
respectInhibitors: true
timeout: 0
}
`
`;
monitorOffMonitor = Qt.createQmlObject(qmlString, root, "IdleService.MonitorOffMonitor")
monitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.monitorTimeout > 0)
monitorOffMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors)
monitorOffMonitor.timeout = Qt.binding(() => root.monitorTimeout)
monitorOffMonitor.isIdleChanged.connect(function() {
monitorOffMonitor = Qt.createQmlObject(qmlString, root, "IdleService.MonitorOffMonitor");
monitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.monitorTimeout > 0);
monitorOffMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
monitorOffMonitor.timeout = Qt.binding(() => root.monitorTimeout);
monitorOffMonitor.isIdleChanged.connect(function () {
if (monitorOffMonitor.isIdle) {
root.requestMonitorOff()
root.requestMonitorOff();
} else {
root.requestMonitorOn()
root.requestMonitorOn();
}
})
});
lockMonitor = Qt.createQmlObject(qmlString, root, "IdleService.LockMonitor")
lockMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.lockTimeout > 0)
lockMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors)
lockMonitor.timeout = Qt.binding(() => root.lockTimeout)
lockMonitor.isIdleChanged.connect(function() {
lockMonitor = Qt.createQmlObject(qmlString, root, "IdleService.LockMonitor");
lockMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.lockTimeout > 0);
lockMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
lockMonitor.timeout = Qt.binding(() => root.lockTimeout);
lockMonitor.isIdleChanged.connect(function () {
if (lockMonitor.isIdle) {
root.lockRequested()
if (SettingsData.fadeToLockEnabled) {
root.fadeToLockRequested();
} else {
root.lockRequested();
}
} else {
if (SettingsData.fadeToLockEnabled) {
root.cancelFadeToLock();
}
}
})
});
suspendMonitor = Qt.createQmlObject(qmlString, root, "IdleService.SuspendMonitor")
suspendMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.suspendTimeout > 0)
suspendMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors)
suspendMonitor.timeout = Qt.binding(() => root.suspendTimeout)
suspendMonitor.isIdleChanged.connect(function() {
suspendMonitor = Qt.createQmlObject(qmlString, root, "IdleService.SuspendMonitor");
suspendMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.suspendTimeout > 0);
suspendMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
suspendMonitor.timeout = Qt.binding(() => root.suspendTimeout);
suspendMonitor.isIdleChanged.connect(function () {
if (suspendMonitor.isIdle) {
root.requestSuspend()
root.requestSuspend();
}
})
});
if (SettingsData.preventIdleForMedia) {
createMediaInhibitor()
createMediaInhibitor();
}
} catch (e) {
console.warn("IdleService: Error creating IdleMonitors:", e)
console.warn("IdleService: Error creating IdleMonitors:", e);
}
}
Connections {
target: root
function onRequestMonitorOff() {
CompositorService.powerOffMonitors()
CompositorService.powerOffMonitors();
}
function onRequestMonitorOn() {
CompositorService.powerOnMonitors()
CompositorService.powerOnMonitors();
}
function onRequestSuspend() {
SessionService.suspendWithBehavior(root.suspendBehavior)
SessionService.suspendWithBehavior(root.suspendBehavior);
}
}
@@ -169,7 +180,7 @@ Singleton {
target: SessionService
function onPrepareForSleep() {
if (SettingsData.lockBeforeSuspend) {
root.lockRequested()
root.lockRequested();
}
}
}
@@ -178,19 +189,19 @@ Singleton {
target: SettingsData
function onPreventIdleForMediaChanged() {
if (SettingsData.preventIdleForMedia) {
createMediaInhibitor()
createMediaInhibitor();
} else {
destroyMediaInhibitor()
destroyMediaInhibitor();
}
}
}
Component.onCompleted: {
if (!idleMonitorAvailable) {
console.warn("IdleService: IdleMonitor not available - power management disabled. This requires a newer version of Quickshell.")
console.warn("IdleService: IdleMonitor not available - power management disabled. This requires a newer version of Quickshell.");
} else {
console.info("IdleService: Initialized with idle monitoring support")
createIdleMonitors()
console.info("IdleService: Initialized with idle monitoring support");
createIdleMonitors();
}
}
}
}
+52 -59
View File
@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Shapes
import Quickshell.Services.Mpris
import qs.Common
@@ -18,7 +17,7 @@ Item {
onArtUrlChanged: {
if (artUrl && albumArt.status !== Image.Error) {
lastValidArtUrl = artUrl
lastValidArtUrl = artUrl;
}
}
@@ -36,7 +35,7 @@ Item {
width: parent.width * 1.1
height: parent.height * 1.1
anchors.centerIn: parent
visible: activePlayer?.playbackState === MprisPlaybackState.Playing && showAnimation
visible: CavaService.cavaAvailable && activePlayer?.playbackState === MprisPlaybackState.Playing && showAnimation
asynchronous: false
antialiasing: true
preferredRendererType: Shape.CurveRenderer
@@ -50,19 +49,21 @@ Item {
property var audioLevels: {
if (!CavaService.cavaAvailable || CavaService.values.length === 0) {
return [0.5, 0.3, 0.7, 0.4, 0.6, 0.5, 0.8, 0.2, 0.9, 0.6]
return [0.5, 0.3, 0.7, 0.4, 0.6, 0.5, 0.8, 0.2, 0.9, 0.6];
}
return CavaService.values
return CavaService.values;
}
property var smoothedLevels: [0.5, 0.3, 0.7, 0.4, 0.6, 0.5, 0.8, 0.2, 0.9, 0.6]
property var cubics: []
onAudioLevelsChanged: updatePath()
FrameAnimation {
running: morphingBlob.visible
onTriggered: morphingBlob.updatePath()
Connections {
target: CavaService
function onValuesChanged() {
if (morphingBlob.visible) {
morphingBlob.updatePath();
}
}
}
Component {
@@ -71,69 +72,61 @@ Item {
}
Component.onCompleted: {
shapePath.pathElements.push(Qt.createQmlObject(
'import QtQuick; import QtQuick.Shapes; PathMove {}', shapePath
))
shapePath.pathElements.push(Qt.createQmlObject('import QtQuick; import QtQuick.Shapes; PathMove {}', shapePath));
for (let i = 0; i < segments; i++) {
const seg = cubicSegment.createObject(shapePath)
shapePath.pathElements.push(seg)
cubics.push(seg)
const seg = cubicSegment.createObject(shapePath);
shapePath.pathElements.push(seg);
cubics.push(seg);
}
updatePath()
}
function expSmooth(prev, next, alpha) {
return prev + alpha * (next - prev)
updatePath();
}
function updatePath() {
if (cubics.length === 0) return
if (cubics.length === 0)
return;
for (let i = 0; i < Math.min(smoothedLevels.length, audioLevels.length); i++) {
smoothedLevels[i] = expSmooth(smoothedLevels[i], audioLevels[i], 0.35)
const alpha = 0.35;
const minLen = Math.min(smoothedLevels.length, audioLevels.length);
for (let i = 0; i < minLen; i++) {
smoothedLevels[i] += alpha * (audioLevels[i] - smoothedLevels[i]);
}
const points = []
const angleStep = 2 * Math.PI / segments;
const tension3 = 0.16666667;
const startMove = shapePath.pathElements[0];
const points = new Array(segments);
for (let i = 0; i < segments; i++) {
const angle = (i / segments) * 2 * Math.PI
const audioIndex = i % Math.min(smoothedLevels.length, 10)
const rawLevel = smoothedLevels[audioIndex] || 0
const scaledLevel = Math.sqrt(Math.min(Math.max(rawLevel, 0), 100) / 100) * 100
const normalizedLevel = scaledLevel / 100
const audioLevel = Math.max(0.15, normalizedLevel) * 0.5
const radius = baseRadius * (1.0 + audioLevel)
const x = centerX + Math.cos(angle) * radius
const y = centerY + Math.sin(angle) * radius
points.push({x: x, y: y})
const angle = i * angleStep;
const audioIndex = i % 10;
const rawLevel = smoothedLevels[audioIndex] || 0;
const clampedLevel = rawLevel < 0 ? 0 : (rawLevel > 100 ? 100 : rawLevel);
const audioLevel = Math.max(0.15, Math.sqrt(clampedLevel * 0.01)) * 0.5;
const radius = baseRadius * (1.0 + audioLevel);
points[i] = {
x: centerX + Math.cos(angle) * radius,
y: centerY + Math.sin(angle) * radius
};
}
const startMove = shapePath.pathElements[0]
startMove.x = points[0].x
startMove.y = points[0].y
startMove.x = points[0].x;
startMove.y = points[0].y;
const tension = 0.5
for (let i = 0; i < segments; i++) {
const p0 = points[(i - 1 + segments) % segments]
const p1 = points[i]
const p2 = points[(i + 1) % segments]
const p3 = points[(i + 2) % segments]
const p0 = points[(i + segments - 1) % segments];
const p1 = points[i];
const p2 = points[(i + 1) % segments];
const p3 = points[(i + 2) % segments];
const c1x = p1.x + (p2.x - p0.x) * tension / 3
const c1y = p1.y + (p2.y - p0.y) * tension / 3
const c2x = p2.x - (p3.x - p1.x) * tension / 3
const c2y = p2.y - (p3.y - p1.y) * tension / 3
const seg = cubics[i]
seg.control1X = c1x
seg.control1Y = c1y
seg.control2X = c2x
seg.control2Y = c2y
seg.x = p2.x
seg.y = p2.y
const seg = cubics[i];
seg.control1X = p1.x + (p2.x - p0.x) * tension3;
seg.control1Y = p1.y + (p2.y - p0.y) * tension3;
seg.control2X = p2.x - (p3.x - p1.x) * tension3;
seg.control2Y = p2.y - (p3.y - p1.y) * tension3;
seg.x = p2.x;
seg.y = p2.y;
}
}
@@ -161,8 +154,8 @@ Item {
onImageSourceChanged: {
if (imageSource && imageStatus !== Image.Error) {
lastValidArtUrl = imageSource
lastValidArtUrl = imageSource;
}
}
}
}
}
+7 -8
View File
@@ -91,10 +91,7 @@ Item {
setBarContext(pos, bottomGap);
}
readonly property bool useBackgroundWindow: {
const layerOverride = Quickshell.env("DMS_POPOUT_LAYER");
return !layerOverride || layerOverride === "overlay";
}
readonly property bool useBackgroundWindow: true
function open() {
if (!screen)
@@ -258,13 +255,15 @@ Item {
WlrLayershell.layer: {
switch (Quickshell.env("DMS_POPOUT_LAYER")) {
case "bottom":
return WlrLayershell.Bottom;
case "top":
console.warn("DankPopout: 'bottom' layer is not valid for popouts. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "background":
return WlrLayershell.Background;
default:
console.warn("DankPopout: 'background' layer is not valid for popouts. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "overlay":
return WlrLayershell.Overlay;
default:
return WlrLayershell.Top;
}
}
WlrLayershell.exclusiveZone: -1