mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-13 07:42:46 -04:00
Compare commits
47 Commits
v0.6.1
...
c49a875ec2
| Author | SHA1 | Date | |
|---|---|---|---|
| c49a875ec2 | |||
| 2a002304b9 | |||
| d9522818ae | |||
| 800588e121 | |||
| 991c31ebdb | |||
| 48f77e1691 | |||
| 42de6fd074 | |||
| 62845b470c | |||
| fd20986cf8 | |||
| 61369cde9e | |||
| 644384ce8b | |||
| 97c11a2482 | |||
| 1e7e1c2d78 | |||
| 1c7201fb04 | |||
| 61ec0c697a | |||
| 4b5fce1bfc | |||
| 6cc6e7c8e9 | |||
| 89298fce30 | |||
| a3a27e07fa | |||
| 4f32376f22 | |||
| 58bf189941 | |||
| bcfa508da5 | |||
| c0ae3ef58b | |||
| 1e70d7b4c3 | |||
| f8dc6ad2bc | |||
| e22482988f | |||
| 4eb896629d | |||
| b310e66275 | |||
| b39da1bea7 | |||
| fa575d0574 | |||
| dfe2f3771b | |||
| 46caeb0445 | |||
| 59cc9c7006 | |||
| 12e91534eb | |||
| d9da88ceb5 | |||
| 2dbfec0307 | |||
| 09cf8c9641 | |||
| f1bed4d6a3 | |||
| 2ed6c33c83 | |||
| 7ad532ed17 | |||
| 92fe8c5b14 | |||
| 8e95572589 | |||
| 62da862a66 | |||
| 993e34f548 | |||
| e39465aece | |||
| 8fd616b680 | |||
| cc054b27de |
@@ -386,6 +386,68 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
trigger-obs-update:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: release
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- 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: Update OBS packages
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
cd distro
|
||||||
|
bash scripts/obs-upload.sh dms "Update to $VERSION"
|
||||||
|
|
||||||
|
trigger-ppa-update:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: release
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
debhelper \
|
||||||
|
devscripts \
|
||||||
|
dput \
|
||||||
|
lftp \
|
||||||
|
build-essential \
|
||||||
|
fakeroot \
|
||||||
|
dpkg-dev
|
||||||
|
|
||||||
|
- name: Configure GPG
|
||||||
|
env:
|
||||||
|
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
echo "$GPG_KEY" | gpg --import
|
||||||
|
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | awk '{print $2}' | cut -d'/' -f2)
|
||||||
|
echo "DEBSIGN_KEYID=$GPG_KEY_ID" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Upload to PPA
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
cd distro/ubuntu/ppa
|
||||||
|
bash create-and-upload.sh ../dms dms questing
|
||||||
|
|
||||||
copr-build:
|
copr-build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: release
|
needs: release
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: DMS Copr Stable Release (Manual)
|
name: DMS Copr Stable Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
name: Update OBS Packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
package:
|
||||||
|
description: 'Package to update (dms, dms-git, or all)'
|
||||||
|
required: false
|
||||||
|
default: 'all'
|
||||||
|
rebuild_release:
|
||||||
|
description: 'Release number for rebuilds (e.g., 2, 3, 4 to increment spec Release)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
schedule:
|
||||||
|
- cron: '0 */3 * * *' # Every 3 hours for dms-git builds
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-obs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Determine packages to update
|
||||||
|
id: packages
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
|
||||||
|
echo "packages=dms" >> $GITHUB_OUTPUT
|
||||||
|
VERSION="${GITHUB_REF#refs/tags/}"
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "Triggered by tag: $VERSION"
|
||||||
|
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||||
|
echo "packages=dms-git" >> $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
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Update version in packaging files
|
||||||
|
if: steps.packages.outputs.version != ''
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.packages.outputs.version }}"
|
||||||
|
VERSION_NO_V="${VERSION#v}"
|
||||||
|
echo "Updating packaging to version $VERSION_NO_V"
|
||||||
|
|
||||||
|
# Update openSUSE spec files
|
||||||
|
sed -i "s/^Version:.*/Version: $VERSION_NO_V/" distro/opensuse/*.spec
|
||||||
|
|
||||||
|
# Update Debian _service files
|
||||||
|
for service in distro/debian/*/_service; do
|
||||||
|
if [[ -f "$service" ]]; then
|
||||||
|
sed -i "s|<param name=\"revision\">v[0-9.]*</param>|<param name=\"revision\">$VERSION</param>|" "$service"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add distro/
|
||||||
|
git commit -m "chore: update packaging to $VERSION" || echo "No changes to commit"
|
||||||
|
|
||||||
|
- 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: Upload to OBS
|
||||||
|
env:
|
||||||
|
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
|
||||||
|
run: |
|
||||||
|
PACKAGES="${{ steps.packages.outputs.packages }}"
|
||||||
|
MESSAGE="Automated update from GitHub Actions"
|
||||||
|
|
||||||
|
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||||
|
MESSAGE="Update to ${{ steps.packages.outputs.version }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd distro
|
||||||
|
|
||||||
|
if [[ "$PACKAGES" == "all" ]]; then
|
||||||
|
bash scripts/obs-upload.sh dms "$MESSAGE"
|
||||||
|
bash scripts/obs-upload.sh dms-git "Automated git update"
|
||||||
|
else
|
||||||
|
bash scripts/obs-upload.sh "$PACKAGES" "$MESSAGE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Summary
|
||||||
|
run: |
|
||||||
|
echo "### OBS Package Update Complete" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||||
|
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
echo "- **Project**: https://build.opensuse.org/project/show/home:AvengeMedia" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
name: Update PPA Packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
package:
|
||||||
|
description: 'Package to upload (dms, dms-git, or all)'
|
||||||
|
required: false
|
||||||
|
default: 'dms-git'
|
||||||
|
rebuild_release:
|
||||||
|
description: 'Release number for rebuilds (e.g., 2, 3, 4 for ppa2, ppa3, ppa4)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
schedule:
|
||||||
|
- cron: '0 */3 * * *' # Every 3 hours for dms-git builds
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
upload-ppa:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
debhelper \
|
||||||
|
devscripts \
|
||||||
|
dput \
|
||||||
|
lftp \
|
||||||
|
build-essential \
|
||||||
|
fakeroot \
|
||||||
|
dpkg-dev
|
||||||
|
|
||||||
|
- name: Configure GPG
|
||||||
|
env:
|
||||||
|
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
echo "$GPG_KEY" | gpg --import
|
||||||
|
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | awk '{print $2}' | cut -d'/' -f2)
|
||||||
|
echo "DEBSIGN_KEYID=$GPG_KEY_ID" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Determine packages to upload
|
||||||
|
id: packages
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||||
|
echo "packages=dms-git" >> $GITHUB_OUTPUT
|
||||||
|
echo "Triggered by schedule: uploading 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=dms-git" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload to PPA
|
||||||
|
env:
|
||||||
|
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
|
||||||
|
run: |
|
||||||
|
PACKAGES="${{ steps.packages.outputs.packages }}"
|
||||||
|
|
||||||
|
cd distro/ubuntu/ppa
|
||||||
|
|
||||||
|
if [[ "$PACKAGES" == "all" ]]; then
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Uploading dms to PPA..."
|
||||||
|
if [ -n "$REBUILD_RELEASE" ]; then
|
||||||
|
echo "🔄 Using rebuild release number: ppa$REBUILD_RELEASE"
|
||||||
|
fi
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
bash create-and-upload.sh "../dms" dms questing
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Uploading dms-git to PPA..."
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
bash create-and-upload.sh "../dms-git" dms-git questing
|
||||||
|
else
|
||||||
|
PPA_NAME="$PACKAGES"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Uploading $PACKAGES to PPA..."
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
bash create-and-upload.sh "../$PACKAGES" "$PPA_NAME" questing
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Summary
|
||||||
|
run: |
|
||||||
|
echo "### PPA Package Upload Complete" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
PACKAGES="${{ steps.packages.outputs.packages }}"
|
||||||
|
if [[ "$PACKAGES" == "all" ]]; then
|
||||||
|
echo "- **PPA dms**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **PPA dms-git**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [[ "$PACKAGES" == "dms" ]]; then
|
||||||
|
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [[ "$PACKAGES" == "dms-git" ]]; then
|
||||||
|
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||||
|
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Builds will appear once Launchpad processes the uploads." >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -136,3 +136,9 @@ go.work.sum
|
|||||||
# .vscode/
|
# .vscode/
|
||||||
|
|
||||||
bin/
|
bin/
|
||||||
|
|
||||||
|
# Extracted source trees in Ubuntu package directories
|
||||||
|
distro/ubuntu/*/dms-git-repo/
|
||||||
|
distro/ubuntu/*/DankMaterialShell-*/
|
||||||
|
distro/ubuntu/danklinux/*/dsearch-*/
|
||||||
|
distro/ubuntu/danklinux/*/dgop-*/
|
||||||
|
|||||||
+29
-15
@@ -2,28 +2,42 @@
|
|||||||
|
|
||||||
Contributions are welcome and encouraged.
|
Contributions are welcome and encouraged.
|
||||||
|
|
||||||
## Formatting
|
To contribute fork this repository, make your changes, and open a pull request.
|
||||||
|
|
||||||
The preferred tool for formatting files is [qmlfmt](https://github.com/jesperhh/qmlfmt) (also available on aur as qmlfmt-git). It actually kinda sucks, but `qmlformat` doesn't work with null safe operators and ternarys and pragma statements and a bunch of other things that are supported.
|
## VSCode Setup
|
||||||
|
|
||||||
We need some consistent style, so this at least gives the same formatter that Qt Creator uses.
|
This is a monorepo, the easiest thing to do is to open an editor in either `quickshell`, `core`, or both depending on which part of the project you are working on.
|
||||||
|
|
||||||
You can configure it to format on save in vscode by configuring the "custom local formatters" extension then adding this to settings json.
|
### QML (`quickshell` directory)
|
||||||
|
|
||||||
|
1. Install the [QML Extension](https://doc.qt.io/vscodeext/)
|
||||||
|
2. Configure `ctrl+shift+p` -> user preferences (json) with qmlls path
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"customLocalFormatters.formatters": [
|
{
|
||||||
{
|
"qt-qml.doNotAskForQmllsDownload": true,
|
||||||
"command": "sh -c \"qmlfmt -t 4 -i 4 -b 250 | sed 's/pragma ComponentBehavior$/pragma ComponentBehavior: Bound/g'\"",
|
"qt-qml.qmlls.customExePath": "/usr/lib/qt6/bin/qmlls"
|
||||||
"languages": ["qml"]
|
}
|
||||||
}
|
|
||||||
],
|
|
||||||
"[qml]": {
|
|
||||||
"editor.defaultFormatter": "jkillian.custom-local-formatters",
|
|
||||||
"editor.formatOnSave": true
|
|
||||||
},
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Sometimes it just breaks code though. Like turning `"_\""` into `"_""`, so you may not want to do formatOnSave.
|
3. Create empty `.qmlls.ini` file in `quickshell/` directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd quickshell
|
||||||
|
touch .qmlls.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Restart dms to generate the `.qmlls.ini` file
|
||||||
|
|
||||||
|
5. Make your changes, test, and open a pull request.
|
||||||
|
|
||||||
|
### GO (`core` directory)
|
||||||
|
|
||||||
|
1. Install the [Go Extension](https://code.visualstudio.com/docs/languages/go)
|
||||||
|
2. Ensure code is formatted with `make fmt`
|
||||||
|
3. Add appropriate test coverage and ensure tests pass with `make test`
|
||||||
|
4. Run `go mod tidy`
|
||||||
|
5. Open pull request
|
||||||
|
|
||||||
## Pull request
|
## Pull request
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ func getRuntimeDir() string {
|
|||||||
return os.TempDir()
|
return os.TempDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasSystemdRun() bool {
|
||||||
|
_, err := exec.LookPath("systemd-run")
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
func getPIDFilePath() string {
|
func getPIDFilePath() string {
|
||||||
return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", os.Getpid()))
|
return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", os.Getpid()))
|
||||||
}
|
}
|
||||||
@@ -165,6 +170,10 @@ func runShellInteractive(session bool) {
|
|||||||
cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules)
|
cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isSessionManaged && hasSystemdRun() {
|
||||||
|
cmd.Env = append(cmd.Env, "DMS_DEFAULT_LAUNCH_PREFIX=systemd-run --user --scope")
|
||||||
|
}
|
||||||
|
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" {
|
if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" {
|
||||||
if !strings.HasPrefix(configPath, homeDir) {
|
if !strings.HasPrefix(configPath, homeDir) {
|
||||||
@@ -387,6 +396,10 @@ func runShellDaemon(session bool) {
|
|||||||
cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules)
|
cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isSessionManaged && hasSystemdRun() {
|
||||||
|
cmd.Env = append(cmd.Env, "DMS_DEFAULT_LAUNCH_PREFIX=systemd-run --user --scope")
|
||||||
|
}
|
||||||
|
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" {
|
if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" {
|
||||||
if !strings.HasPrefix(configPath, homeDir) {
|
if !strings.HasPrefix(configPath, homeDir) {
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ windowrulev2 = noborder, class:^(kitty)$
|
|||||||
windowrulev2 = float, class:^(firefox)$, title:^(Picture-in-Picture)$
|
windowrulev2 = float, class:^(firefox)$, title:^(Picture-in-Picture)$
|
||||||
windowrulev2 = float, class:^(zoom)$
|
windowrulev2 = float, class:^(zoom)$
|
||||||
|
|
||||||
|
# DMS windows floating by default
|
||||||
|
windowrulev2 = float, class:^(org.quickshell)$
|
||||||
windowrulev2 = opacity 0.9 0.9, floating:0, focus:0
|
windowrulev2 = opacity 0.9 0.9, floating:0, focus:0
|
||||||
|
|
||||||
layerrule = noanim, ^(quickshell)$
|
layerrule = noanim, ^(quickshell)$
|
||||||
|
|||||||
@@ -218,6 +218,11 @@ window-rule {
|
|||||||
geometry-corner-radius 12
|
geometry-corner-radius 12
|
||||||
clip-to-geometry true
|
clip-to-geometry true
|
||||||
}
|
}
|
||||||
|
// Open dms windows as floating by default
|
||||||
|
window-rule {
|
||||||
|
match app-id=r#"org.quickshell$"#
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
binds {
|
binds {
|
||||||
// === System & Overview ===
|
// === System & Overview ===
|
||||||
Mod+D { spawn "niri" "msg" "action" "toggle-overview"; }
|
Mod+D { spawn "niri" "msg" "action" "toggle-overview"; }
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ func init() {
|
|||||||
Register("fedora-asahi-remix", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
|
Register("fedora-asahi-remix", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||||
return NewFedoraDistribution(config, logChan)
|
return NewFedoraDistribution(config, logChan)
|
||||||
})
|
})
|
||||||
|
|
||||||
Register("bluefin", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
|
Register("bluefin", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||||
return NewFedoraDistribution(config, logChan)
|
return NewFedoraDistribution(config, logChan)
|
||||||
})
|
})
|
||||||
|
Register("ultramarine", "#00078b", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||||
|
return NewFedoraDistribution(config, logChan)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type FedoraDistribution struct {
|
type FedoraDistribution struct {
|
||||||
@@ -506,6 +508,14 @@ func (f *FedoraDistribution) installDNFPackages(ctx context.Context, packages []
|
|||||||
f.log(fmt.Sprintf("Installing DNF packages: %s", strings.Join(packages, ", ")))
|
f.log(fmt.Sprintf("Installing DNF packages: %s", strings.Join(packages, ", ")))
|
||||||
|
|
||||||
args := []string{"dnf", "install", "-y"}
|
args := []string{"dnf", "install", "-y"}
|
||||||
|
|
||||||
|
for _, pkg := range packages {
|
||||||
|
if pkg == "niri" || pkg == "niri-git" {
|
||||||
|
args = append(args, "--setopt=install_weak_deps=False")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
args = append(args, packages...)
|
args = append(args, packages...)
|
||||||
|
|
||||||
progressChan <- InstallProgressMsg{
|
progressChan <- InstallProgressMsg{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
|
||||||
@@ -125,8 +126,9 @@ func (a *SecretAgent) GetSecrets(
|
|||||||
connType, displayName, vpnSvc := readConnTypeAndName(conn)
|
connType, displayName, vpnSvc := readConnTypeAndName(conn)
|
||||||
ssid := readSSID(conn)
|
ssid := readSSID(conn)
|
||||||
fields := fieldsNeeded(settingName, hints)
|
fields := fieldsNeeded(settingName, hints)
|
||||||
|
vpnPasswordFlags := readVPNPasswordFlags(conn, settingName)
|
||||||
|
|
||||||
log.Infof("[SecretAgent] connType=%s, name=%s, vpnSvc=%s, fields=%v, flags=%d", connType, displayName, vpnSvc, fields, flags)
|
log.Infof("[SecretAgent] connType=%s, name=%s, vpnSvc=%s, fields=%v, flags=%d, vpnPasswordFlags=%d", connType, displayName, vpnSvc, fields, flags, vpnPasswordFlags)
|
||||||
|
|
||||||
if a.backend != nil {
|
if a.backend != nil {
|
||||||
a.backend.stateMutex.RLock()
|
a.backend.stateMutex.RLock()
|
||||||
@@ -163,57 +165,70 @@ func (a *SecretAgent) GetSecrets(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
// For VPN connections with no hints, we can't provide a proper UI.
|
|
||||||
// Defer to other agents (like nm-applet or VPN-specific auth dialogs)
|
|
||||||
// that can handle the VPN type properly (e.g., OpenConnect with SAML, etc.)
|
|
||||||
if settingName == "vpn" {
|
if settingName == "vpn" {
|
||||||
log.Infof("[SecretAgent] VPN with empty hints - deferring to other agents for %s", vpnSvc)
|
if a.backend != nil {
|
||||||
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
|
a.backend.stateMutex.RLock()
|
||||||
}
|
isConnectingVPN := a.backend.state.IsConnectingVPN
|
||||||
|
a.backend.stateMutex.RUnlock()
|
||||||
|
|
||||||
const (
|
if !isConnectingVPN {
|
||||||
NM_SETTING_SECRET_FLAG_NONE = 0
|
log.Infof("[SecretAgent] VPN with empty hints - deferring to other agents for %s", vpnSvc)
|
||||||
NM_SETTING_SECRET_FLAG_AGENT_OWNED = 1
|
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
|
||||||
NM_SETTING_SECRET_FLAG_NOT_SAVED = 2
|
}
|
||||||
NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
var passwordFlags uint32 = 0xFFFF
|
log.Infof("[SecretAgent] VPN with empty hints but we're connecting - prompting for password")
|
||||||
switch settingName {
|
fields = []string{"password"}
|
||||||
case "802-11-wireless-security":
|
} else {
|
||||||
if wifiSecSettings, ok := conn["802-11-wireless-security"]; ok {
|
log.Infof("[SecretAgent] VPN with empty hints - deferring to other agents for %s", vpnSvc)
|
||||||
if flagsVariant, ok := wifiSecSettings["psk-flags"]; ok {
|
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
|
||||||
if pwdFlags, ok := flagsVariant.Value().(uint32); ok {
|
|
||||||
passwordFlags = pwdFlags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "802-1x":
|
|
||||||
if dot1xSettings, ok := conn["802-1x"]; ok {
|
|
||||||
if flagsVariant, ok := dot1xSettings["password-flags"]; ok {
|
|
||||||
if pwdFlags, ok := flagsVariant.Value().(uint32); ok {
|
|
||||||
passwordFlags = pwdFlags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if passwordFlags == 0xFFFF {
|
if len(fields) == 0 {
|
||||||
log.Warnf("[SecretAgent] Could not determine password-flags for empty hints - returning NoSecrets error")
|
const (
|
||||||
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
|
NM_SETTING_SECRET_FLAG_NONE = 0
|
||||||
} else if passwordFlags&NM_SETTING_SECRET_FLAG_NOT_REQUIRED != 0 {
|
NM_SETTING_SECRET_FLAG_AGENT_OWNED = 1
|
||||||
log.Infof("[SecretAgent] Secrets not required (flags=%d)", passwordFlags)
|
NM_SETTING_SECRET_FLAG_NOT_SAVED = 2
|
||||||
out := nmSettingMap{}
|
NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 4
|
||||||
out[settingName] = nmVariantMap{}
|
)
|
||||||
return out, nil
|
|
||||||
} else if passwordFlags&NM_SETTING_SECRET_FLAG_AGENT_OWNED != 0 {
|
var passwordFlags uint32 = 0xFFFF
|
||||||
log.Warnf("[SecretAgent] Secrets are agent-owned but we don't store secrets (flags=%d) - returning NoSecrets error", passwordFlags)
|
switch settingName {
|
||||||
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
|
case "802-11-wireless-security":
|
||||||
} else {
|
if wifiSecSettings, ok := conn["802-11-wireless-security"]; ok {
|
||||||
log.Infof("[SecretAgent] No secrets needed, using system stored secrets (flags=%d)", passwordFlags)
|
if flagsVariant, ok := wifiSecSettings["psk-flags"]; ok {
|
||||||
out := nmSettingMap{}
|
if pwdFlags, ok := flagsVariant.Value().(uint32); ok {
|
||||||
out[settingName] = nmVariantMap{}
|
passwordFlags = pwdFlags
|
||||||
return out, nil
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "802-1x":
|
||||||
|
if dot1xSettings, ok := conn["802-1x"]; ok {
|
||||||
|
if flagsVariant, ok := dot1xSettings["password-flags"]; ok {
|
||||||
|
if pwdFlags, ok := flagsVariant.Value().(uint32); ok {
|
||||||
|
passwordFlags = pwdFlags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if passwordFlags == 0xFFFF {
|
||||||
|
log.Warnf("[SecretAgent] Could not determine password-flags for empty hints - returning NoSecrets error")
|
||||||
|
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
|
||||||
|
} else if passwordFlags&NM_SETTING_SECRET_FLAG_NOT_REQUIRED != 0 {
|
||||||
|
log.Infof("[SecretAgent] Secrets not required (flags=%d)", passwordFlags)
|
||||||
|
out := nmSettingMap{}
|
||||||
|
out[settingName] = nmVariantMap{}
|
||||||
|
return out, nil
|
||||||
|
} else if passwordFlags&NM_SETTING_SECRET_FLAG_AGENT_OWNED != 0 {
|
||||||
|
log.Warnf("[SecretAgent] Secrets are agent-owned but we don't store secrets (flags=%d) - returning NoSecrets error", passwordFlags)
|
||||||
|
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
|
||||||
|
} else {
|
||||||
|
log.Infof("[SecretAgent] No secrets needed, using system stored secrets (flags=%d)", passwordFlags)
|
||||||
|
out := nmSettingMap{}
|
||||||
|
out[settingName] = nmVariantMap{}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,13 +358,11 @@ func (a *SecretAgent) GetSecrets(
|
|||||||
// Update settings based on type
|
// Update settings based on type
|
||||||
switch settingName {
|
switch settingName {
|
||||||
case "vpn":
|
case "vpn":
|
||||||
// Set password-flags=0 and add secrets to vpn section
|
|
||||||
vpn, ok := existingSettings["vpn"]
|
vpn, ok := existingSettings["vpn"]
|
||||||
if !ok {
|
if !ok {
|
||||||
vpn = make(map[string]dbus.Variant)
|
vpn = make(map[string]dbus.Variant)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get existing data map (vpn.data is string->string)
|
|
||||||
var data map[string]string
|
var data map[string]string
|
||||||
if dataVariant, ok := vpn["data"]; ok {
|
if dataVariant, ok := vpn["data"]; ok {
|
||||||
if dm, ok := dataVariant.Value().(map[string]string); ok {
|
if dm, ok := dataVariant.Value().(map[string]string); ok {
|
||||||
@@ -364,11 +377,9 @@ func (a *SecretAgent) GetSecrets(
|
|||||||
data = make(map[string]string)
|
data = make(map[string]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update password-flags to 0 (system-stored)
|
|
||||||
data["password-flags"] = "0"
|
data["password-flags"] = "0"
|
||||||
vpn["data"] = dbus.MakeVariant(data)
|
vpn["data"] = dbus.MakeVariant(data)
|
||||||
|
|
||||||
// Add secrets (vpn.secrets is string->string)
|
|
||||||
secs := make(map[string]string)
|
secs := make(map[string]string)
|
||||||
for k, v := range reply.Secrets {
|
for k, v := range reply.Secrets {
|
||||||
secs[k] = v
|
secs[k] = v
|
||||||
@@ -379,14 +390,12 @@ func (a *SecretAgent) GetSecrets(
|
|||||||
log.Infof("[SecretAgent] Updated VPN settings: password-flags=0, secrets with %d fields", len(secs))
|
log.Infof("[SecretAgent] Updated VPN settings: password-flags=0, secrets with %d fields", len(secs))
|
||||||
|
|
||||||
case "802-11-wireless-security":
|
case "802-11-wireless-security":
|
||||||
// Set psk-flags=0 for WiFi
|
|
||||||
wifiSec, ok := existingSettings["802-11-wireless-security"]
|
wifiSec, ok := existingSettings["802-11-wireless-security"]
|
||||||
if !ok {
|
if !ok {
|
||||||
wifiSec = make(map[string]dbus.Variant)
|
wifiSec = make(map[string]dbus.Variant)
|
||||||
}
|
}
|
||||||
wifiSec["psk-flags"] = dbus.MakeVariant(uint32(0))
|
wifiSec["psk-flags"] = dbus.MakeVariant(uint32(0))
|
||||||
|
|
||||||
// Add PSK secret
|
|
||||||
if psk, ok := reply.Secrets["psk"]; ok {
|
if psk, ok := reply.Secrets["psk"]; ok {
|
||||||
wifiSec["psk"] = dbus.MakeVariant(psk)
|
wifiSec["psk"] = dbus.MakeVariant(psk)
|
||||||
log.Infof("[SecretAgent] Updated WiFi settings: psk-flags=0")
|
log.Infof("[SecretAgent] Updated WiFi settings: psk-flags=0")
|
||||||
@@ -394,14 +403,12 @@ func (a *SecretAgent) GetSecrets(
|
|||||||
settings["802-11-wireless-security"] = wifiSec
|
settings["802-11-wireless-security"] = wifiSec
|
||||||
|
|
||||||
case "802-1x":
|
case "802-1x":
|
||||||
// Set password-flags=0 for 802.1x
|
|
||||||
dot1x, ok := existingSettings["802-1x"]
|
dot1x, ok := existingSettings["802-1x"]
|
||||||
if !ok {
|
if !ok {
|
||||||
dot1x = make(map[string]dbus.Variant)
|
dot1x = make(map[string]dbus.Variant)
|
||||||
}
|
}
|
||||||
dot1x["password-flags"] = dbus.MakeVariant(uint32(0))
|
dot1x["password-flags"] = dbus.MakeVariant(uint32(0))
|
||||||
|
|
||||||
// Add password secret
|
|
||||||
if password, ok := reply.Secrets["password"]; ok {
|
if password, ok := reply.Secrets["password"]; ok {
|
||||||
dot1x["password"] = dbus.MakeVariant(password)
|
dot1x["password"] = dbus.MakeVariant(password)
|
||||||
log.Infof("[SecretAgent] Updated 802.1x settings: password-flags=0")
|
log.Infof("[SecretAgent] Updated 802.1x settings: password-flags=0")
|
||||||
@@ -507,6 +514,39 @@ func fieldsNeeded(setting string, hints []string) []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readVPNPasswordFlags(conn map[string]nmVariantMap, settingName string) uint32 {
|
||||||
|
if settingName != "vpn" {
|
||||||
|
return 0xFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
vpnSettings, ok := conn["vpn"]
|
||||||
|
if !ok {
|
||||||
|
return 0xFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
dataVariant, ok := vpnSettings["data"]
|
||||||
|
if !ok {
|
||||||
|
return 0xFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
dataMap, ok := dataVariant.Value().(map[string]string)
|
||||||
|
if !ok {
|
||||||
|
return 0xFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
flagsStr, ok := dataMap["password-flags"]
|
||||||
|
if !ok {
|
||||||
|
return 0xFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
flags64, err := strconv.ParseUint(flagsStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0xFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint32(flags64)
|
||||||
|
}
|
||||||
|
|
||||||
func reasonFromFlags(flags uint32) string {
|
func reasonFromFlags(flags uint32) string {
|
||||||
const (
|
const (
|
||||||
NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0
|
NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ func (b *NetworkManagerBackend) ConnectVPN(uuidOrName string, singleActive bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
nm := b.nmConn.(gonetworkmanager.NetworkManager)
|
nm := b.nmConn.(gonetworkmanager.NetworkManager)
|
||||||
activeConn, err := nm.ActivateConnection(targetConn, nil, nil)
|
_, err = nm.ActivateConnection(targetConn, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.stateMutex.Lock()
|
b.stateMutex.Lock()
|
||||||
b.state.IsConnectingVPN = false
|
b.state.IsConnectingVPN = false
|
||||||
@@ -249,20 +249,6 @@ func (b *NetworkManagerBackend) ConnectVPN(uuidOrName string, singleActive bool)
|
|||||||
return fmt.Errorf("failed to activate VPN: %w", err)
|
return fmt.Errorf("failed to activate VPN: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if activeConn != nil {
|
|
||||||
state, _ := activeConn.GetPropertyState()
|
|
||||||
if state == 2 {
|
|
||||||
b.stateMutex.Lock()
|
|
||||||
b.state.IsConnectingVPN = false
|
|
||||||
b.state.ConnectingVPNUUID = ""
|
|
||||||
b.stateMutex.Unlock()
|
|
||||||
b.ListActiveVPN()
|
|
||||||
if b.onStateChange != nil {
|
|
||||||
b.onStateChange()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -607,41 +607,6 @@ func (m *Manager) transitionWorker() {
|
|||||||
|
|
||||||
if finalTarget == targetTemp {
|
if finalTarget == targetTemp {
|
||||||
log.Debugf("Transition complete: now at %dK", targetTemp)
|
log.Debugf("Transition complete: now at %dK", targetTemp)
|
||||||
|
|
||||||
m.configMutex.RLock()
|
|
||||||
enabled := m.config.Enabled
|
|
||||||
identityTemp := m.config.HighTemp
|
|
||||||
m.configMutex.RUnlock()
|
|
||||||
|
|
||||||
if !enabled && targetTemp == identityTemp && m.controlsInitialized {
|
|
||||||
m.post(func() {
|
|
||||||
log.Info("Destroying gamma controls after transition to identity")
|
|
||||||
m.outputs.Range(func(id uint32, out *outputState) bool {
|
|
||||||
if out.gammaControl != nil {
|
|
||||||
control := out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1)
|
|
||||||
control.Destroy()
|
|
||||||
log.Debugf("Destroyed gamma control for output %d", id)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
m.outputs.Range(func(key uint32, value *outputState) bool {
|
|
||||||
m.outputs.Delete(key)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
m.controlsInitialized = false
|
|
||||||
|
|
||||||
m.transitionMutex.Lock()
|
|
||||||
m.currentTemp = identityTemp
|
|
||||||
m.targetTemp = identityTemp
|
|
||||||
m.transitionMutex.Unlock()
|
|
||||||
|
|
||||||
if _, err := m.display.Sync(); err != nil {
|
|
||||||
log.Warnf("Failed to sync Wayland display after destroying controls: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("All gamma controls destroyed")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1262,46 +1227,33 @@ func (m *Manager) SetEnabled(enabled bool) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if m.controlsInitialized {
|
if m.controlsInitialized {
|
||||||
m.configMutex.RLock()
|
m.post(func() {
|
||||||
identityTemp := m.config.HighTemp
|
log.Info("Disabling gamma, destroying controls immediately")
|
||||||
m.configMutex.RUnlock()
|
m.outputs.Range(func(id uint32, out *outputState) bool {
|
||||||
|
if out.gammaControl != nil {
|
||||||
m.transitionMutex.RLock()
|
control := out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1)
|
||||||
currentTemp := m.currentTemp
|
control.Destroy()
|
||||||
m.transitionMutex.RUnlock()
|
log.Debugf("Destroyed gamma control for output %d", id)
|
||||||
|
|
||||||
if currentTemp == identityTemp {
|
|
||||||
m.post(func() {
|
|
||||||
log.Infof("Already at %dK, destroying gamma controls immediately", identityTemp)
|
|
||||||
m.outputs.Range(func(id uint32, out *outputState) bool {
|
|
||||||
if out.gammaControl != nil {
|
|
||||||
control := out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1)
|
|
||||||
control.Destroy()
|
|
||||||
log.Debugf("Destroyed gamma control for output %d", id)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
m.outputs.Range(func(key uint32, value *outputState) bool {
|
|
||||||
m.outputs.Delete(key)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
m.controlsInitialized = false
|
|
||||||
|
|
||||||
m.transitionMutex.Lock()
|
|
||||||
m.currentTemp = identityTemp
|
|
||||||
m.targetTemp = identityTemp
|
|
||||||
m.transitionMutex.Unlock()
|
|
||||||
|
|
||||||
if _, err := m.display.Sync(); err != nil {
|
|
||||||
log.Warnf("Failed to sync Wayland display after destroying controls: %v", err)
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
log.Info("All gamma controls destroyed")
|
|
||||||
})
|
})
|
||||||
} else {
|
m.outputs.Range(func(key uint32, value *outputState) bool {
|
||||||
log.Infof("Disabling: transitioning to %dK before destroying controls", identityTemp)
|
m.outputs.Delete(key)
|
||||||
m.startTransition(identityTemp)
|
return true
|
||||||
}
|
})
|
||||||
|
m.controlsInitialized = false
|
||||||
|
|
||||||
|
m.configMutex.RLock()
|
||||||
|
identityTemp := m.config.HighTemp
|
||||||
|
m.configMutex.RUnlock()
|
||||||
|
|
||||||
|
m.transitionMutex.Lock()
|
||||||
|
m.currentTemp = identityTemp
|
||||||
|
m.targetTemp = identityTemp
|
||||||
|
m.transitionMutex.Unlock()
|
||||||
|
|
||||||
|
log.Info("All gamma controls destroyed")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<services>
|
||||||
|
<!-- Pull full git repository for master branch -->
|
||||||
|
<service name="tar_scm" mode="disabled">
|
||||||
|
<param name="scm">git</param>
|
||||||
|
<param name="url">https://github.com/AvengeMedia/DankMaterialShell.git</param>
|
||||||
|
<param name="revision">master</param>
|
||||||
|
<param name="filename">dms-git-source</param>
|
||||||
|
</service>
|
||||||
|
<service name="recompress" mode="disabled">
|
||||||
|
<param name="file">*.tar</param>
|
||||||
|
<param name="compression">gz</param>
|
||||||
|
</service>
|
||||||
|
<!-- Download pre-built binaries (fallback for Debian 13 with Go 1.22) -->
|
||||||
|
<service name="download_url">
|
||||||
|
<param name="protocol">https</param>
|
||||||
|
<param name="host">github.com</param>
|
||||||
|
<param name="path">/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-amd64.gz</param>
|
||||||
|
</service>
|
||||||
|
<service name="download_url">
|
||||||
|
<param name="protocol">https</param>
|
||||||
|
<param name="host">github.com</param>
|
||||||
|
<param name="path">/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-arm64.gz</param>
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
dms-git (0.6.2+git) nightly; urgency=medium
|
||||||
|
|
||||||
|
* Build dms binary from source for true git version strings
|
||||||
|
* Match Fedora COPR git build behavior
|
||||||
|
* Now shows proper git version (e.g., v0.6.2-11-g12e91534)
|
||||||
|
* Add golang-go and make as build dependencies
|
||||||
|
|
||||||
|
-- Avenge Media <AvengeMedia.US@gmail.com> Fri, 22 Nov 2025 00:00:00 -0500
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
Source: dms-git
|
||||||
|
Section: x11
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||||
|
Build-Depends: debhelper-compat (= 13)
|
||||||
|
Standards-Version: 4.6.2
|
||||||
|
Homepage: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||||
|
|
||||||
|
Package: dms-git
|
||||||
|
Architecture: amd64 arm64
|
||||||
|
Depends: ${misc:Depends},
|
||||||
|
quickshell-git | quickshell,
|
||||||
|
accountsservice,
|
||||||
|
cava,
|
||||||
|
cliphist,
|
||||||
|
danksearch,
|
||||||
|
dgop,
|
||||||
|
matugen,
|
||||||
|
qml6-module-qtcore,
|
||||||
|
qml6-module-qtmultimedia,
|
||||||
|
qml6-module-qtqml,
|
||||||
|
qml6-module-qtquick,
|
||||||
|
qml6-module-qtquick-controls,
|
||||||
|
qml6-module-qtquick-dialogs,
|
||||||
|
qml6-module-qtquick-effects,
|
||||||
|
qml6-module-qtquick-layouts,
|
||||||
|
qml6-module-qtquick-templates,
|
||||||
|
qml6-module-qtquick-window,
|
||||||
|
qt6ct,
|
||||||
|
wl-clipboard
|
||||||
|
Provides: dms
|
||||||
|
Conflicts: dms
|
||||||
|
Replaces: dms
|
||||||
|
Description: DankMaterialShell - Modern Wayland Desktop Shell (git nightly)
|
||||||
|
DMS (DankMaterialShell) is a feature-rich desktop shell built on
|
||||||
|
Quickshell, providing a modern and customizable user interface for
|
||||||
|
Wayland compositors like niri, hyprland, and sway.
|
||||||
|
.
|
||||||
|
This is the nightly/git version built from the latest master branch.
|
||||||
|
.
|
||||||
|
Features include:
|
||||||
|
- Material Design inspired UI
|
||||||
|
- Customizable themes and appearance
|
||||||
|
- Built-in application launcher
|
||||||
|
- System tray and notifications
|
||||||
|
- Network and Bluetooth management
|
||||||
|
- Audio controls
|
||||||
|
- Systemd integration
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: dms
|
||||||
|
Upstream-Contact: Avenge Media LLC <AvengeMedia.US@gmail.com>
|
||||||
|
Source: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2025 Avenge Media LLC
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
.
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dms-git_0.6.0+git2061.5ddea836ppa1_source.buildinfo x11 optional
|
||||||
Executable
+54
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
DEB_VERSION := $(shell dpkg-parsechangelog -S Version)
|
||||||
|
UPSTREAM_VERSION := $(shell echo $(DEB_VERSION) | sed 's/-[^-]*$$//')
|
||||||
|
DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
if [ "$(DEB_HOST_ARCH)" = "amd64" ]; then \
|
||||||
|
if [ -f dms-distropkg-amd64.gz ]; then \
|
||||||
|
gunzip -c dms-distropkg-amd64.gz > dms; \
|
||||||
|
elif [ -f ../SOURCES/dms-distropkg-amd64.gz ]; then \
|
||||||
|
gunzip -c ../SOURCES/dms-distropkg-amd64.gz > dms; \
|
||||||
|
else \
|
||||||
|
echo "ERROR: dms-distropkg-amd64.gz not found!" && exit 1; \
|
||||||
|
fi \
|
||||||
|
elif [ "$(DEB_HOST_ARCH)" = "arm64" ]; then \
|
||||||
|
if [ -f dms-distropkg-arm64.gz ]; then \
|
||||||
|
gunzip -c dms-distropkg-arm64.gz > dms; \
|
||||||
|
elif [ -f ../SOURCES/dms-distropkg-arm64.gz ]; then \
|
||||||
|
gunzip -c ../SOURCES/dms-distropkg-arm64.gz > dms; \
|
||||||
|
else \
|
||||||
|
echo "ERROR: dms-distropkg-arm64.gz not found!" && exit 1; \
|
||||||
|
fi \
|
||||||
|
else \
|
||||||
|
echo "Unsupported architecture: $(DEB_HOST_ARCH)" && exit 1; \
|
||||||
|
fi
|
||||||
|
chmod +x dms
|
||||||
|
|
||||||
|
override_dh_auto_install:
|
||||||
|
install -Dm755 dms debian/dms-git/usr/bin/dms
|
||||||
|
|
||||||
|
mkdir -p debian/dms-git/usr/share/quickshell/dms debian/dms-git/usr/lib/systemd/user
|
||||||
|
if [ -d quickshell ]; then \
|
||||||
|
cp -r quickshell/* debian/dms-git/usr/share/quickshell/dms/; \
|
||||||
|
install -Dm644 quickshell/assets/systemd/dms.service debian/dms-git/usr/lib/systemd/user/dms.service; \
|
||||||
|
elif [ -d dms-git-source/quickshell ]; then \
|
||||||
|
cp -r dms-git-source/quickshell/* debian/dms-git/usr/share/quickshell/dms/; \
|
||||||
|
install -Dm644 dms-git-source/quickshell/assets/systemd/dms.service debian/dms-git/usr/lib/systemd/user/dms.service; \
|
||||||
|
else \
|
||||||
|
echo "ERROR: quickshell directory not found (checked root and dms-git-source/)!" && \
|
||||||
|
echo "Contents of current directory:" && ls -la && \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf debian/dms-git/usr/share/quickshell/dms/core \
|
||||||
|
debian/dms-git/usr/share/quickshell/dms/distro
|
||||||
|
|
||||||
|
override_dh_auto_clean:
|
||||||
|
rm -f dms
|
||||||
|
[ ! -d dms-git-source ] || rm -rf dms-git-source
|
||||||
|
dh_auto_clean
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dms-distropkg-amd64.gz
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Include files that are normally excluded by .gitignore
|
||||||
|
# These are needed for the build process on Launchpad
|
||||||
|
tar-ignore = !dms-distropkg-amd64.gz
|
||||||
|
tar-ignore = !dms-git-repo
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<services>
|
||||||
|
<!-- Download source tarball from GitHub releases -->
|
||||||
|
<service name="download_url">
|
||||||
|
<param name="protocol">https</param>
|
||||||
|
<param name="host">github.com</param>
|
||||||
|
<param name="path">/AvengeMedia/DankMaterialShell/archive/refs/tags/v0.6.2.tar.gz</param>
|
||||||
|
<param name="filename">dms-source.tar.gz</param>
|
||||||
|
</service>
|
||||||
|
<!-- Download amd64 binary -->
|
||||||
|
<service name="download_url">
|
||||||
|
<param name="protocol">https</param>
|
||||||
|
<param name="host">github.com</param>
|
||||||
|
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v0.6.2/dms-distropkg-amd64.gz</param>
|
||||||
|
</service>
|
||||||
|
<!-- Download arm64 binary -->
|
||||||
|
<service name="download_url">
|
||||||
|
<param name="protocol">https</param>
|
||||||
|
<param name="host">github.com</param>
|
||||||
|
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v0.6.2/dms-distropkg-arm64.gz</param>
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
dms (0.6.2) stable; urgency=medium
|
||||||
|
|
||||||
|
* Update to v0.6.2 release
|
||||||
|
* Fix binary download paths for OBS builds
|
||||||
|
* Native format: removed revisions
|
||||||
|
|
||||||
|
-- Avenge Media <AvengeMedia.US@gmail.com> Tue, 19 Nov 2025 10:00:00 -0500
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
Source: dms
|
||||||
|
Section: x11
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||||
|
Build-Depends: debhelper-compat (= 13)
|
||||||
|
Standards-Version: 4.6.2
|
||||||
|
Homepage: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||||
|
|
||||||
|
Package: dms
|
||||||
|
Architecture: amd64
|
||||||
|
Depends: ${misc:Depends},
|
||||||
|
quickshell-git | quickshell,
|
||||||
|
accountsservice,
|
||||||
|
cava,
|
||||||
|
cliphist,
|
||||||
|
danksearch,
|
||||||
|
dgop,
|
||||||
|
matugen,
|
||||||
|
qml6-module-qtcore,
|
||||||
|
qml6-module-qtmultimedia,
|
||||||
|
qml6-module-qtqml,
|
||||||
|
qml6-module-qtquick,
|
||||||
|
qml6-module-qtquick-controls,
|
||||||
|
qml6-module-qtquick-dialogs,
|
||||||
|
qml6-module-qtquick-effects,
|
||||||
|
qml6-module-qtquick-layouts,
|
||||||
|
qml6-module-qtquick-templates,
|
||||||
|
qml6-module-qtquick-window,
|
||||||
|
qt6ct,
|
||||||
|
wl-clipboard
|
||||||
|
Conflicts: dms-git
|
||||||
|
Replaces: dms-git
|
||||||
|
Description: DankMaterialShell - Modern Wayland Desktop Shell
|
||||||
|
DMS (DankMaterialShell) is a feature-rich desktop shell built on
|
||||||
|
Quickshell, providing a modern and customizable user interface for
|
||||||
|
Wayland compositors like niri, hyprland, and sway.
|
||||||
|
.
|
||||||
|
Features include:
|
||||||
|
- Material Design inspired UI
|
||||||
|
- Customizable themes and appearance
|
||||||
|
- Built-in application launcher
|
||||||
|
- System tray and notifications
|
||||||
|
- Network and Bluetooth management
|
||||||
|
- Audio controls
|
||||||
|
- Systemd integration
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: dms
|
||||||
|
Upstream-Contact: Avenge Media LLC <AvengeMedia.US@gmail.com>
|
||||||
|
Source: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2025 Avenge Media LLC
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
.
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dms_0.6.0ppa2_source.buildinfo x11 optional
|
||||||
Executable
+64
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
DEB_VERSION := $(shell dpkg-parsechangelog -S Version)
|
||||||
|
UPSTREAM_VERSION := $(shell echo $(DEB_VERSION) | sed 's/-[^-]*$$//')
|
||||||
|
DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
if [ "$(DEB_HOST_ARCH)" = "amd64" ]; then \
|
||||||
|
if [ -f dms-distropkg-amd64.gz ]; then \
|
||||||
|
gunzip -c dms-distropkg-amd64.gz > dms; \
|
||||||
|
elif [ -f ../SOURCES/dms-distropkg-amd64.gz ]; then \
|
||||||
|
gunzip -c ../SOURCES/dms-distropkg-amd64.gz > dms; \
|
||||||
|
elif [ -f ../../SOURCES/dms-distropkg-amd64.gz ]; then \
|
||||||
|
gunzip -c ../../SOURCES/dms-distropkg-amd64.gz > dms; \
|
||||||
|
else \
|
||||||
|
echo "ERROR: dms-distropkg-amd64.gz not found!" && exit 1; \
|
||||||
|
fi \
|
||||||
|
elif [ "$(DEB_HOST_ARCH)" = "arm64" ]; then \
|
||||||
|
if [ -f dms-distropkg-arm64.gz ]; then \
|
||||||
|
gunzip -c dms-distropkg-arm64.gz > dms; \
|
||||||
|
elif [ -f ../SOURCES/dms-distropkg-arm64.gz ]; then \
|
||||||
|
gunzip -c ../SOURCES/dms-distropkg-arm64.gz > dms; \
|
||||||
|
elif [ -f ../../SOURCES/dms-distropkg-arm64.gz ]; then \
|
||||||
|
gunzip -c ../../SOURCES/dms-distropkg-arm64.gz > dms; \
|
||||||
|
else \
|
||||||
|
echo "ERROR: dms-distropkg-arm64.gz not found!" && exit 1; \
|
||||||
|
fi \
|
||||||
|
else \
|
||||||
|
echo "Unsupported architecture: $(DEB_HOST_ARCH)" && exit 1; \
|
||||||
|
fi
|
||||||
|
chmod +x dms
|
||||||
|
|
||||||
|
if [ ! -d DankMaterialShell-$(UPSTREAM_VERSION) ]; then \
|
||||||
|
if [ -f ../SOURCES/dms-source.tar.gz ]; then \
|
||||||
|
tar -xzf ../SOURCES/dms-source.tar.gz; \
|
||||||
|
elif [ -f dms-source.tar.gz ]; then \
|
||||||
|
tar -xzf dms-source.tar.gz; \
|
||||||
|
fi; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
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; \
|
||||||
|
else \
|
||||||
|
echo "ERROR: DankMaterialShell-$(UPSTREAM_VERSION) directory not found!" && \
|
||||||
|
echo "Contents of current directory:" && ls -la && \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf debian/dms/usr/share/quickshell/dms/core \
|
||||||
|
debian/dms/usr/share/quickshell/dms/distro
|
||||||
|
|
||||||
|
override_dh_auto_clean:
|
||||||
|
rm -f dms
|
||||||
|
rm -rf DankMaterialShell-$(UPSTREAM_VERSION)
|
||||||
|
dh_auto_clean
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
dms-distropkg-amd64.gz
|
||||||
|
dms-source.tar.gz
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Include files that are normally excluded by .gitignore
|
||||||
|
# These are needed for the build process on Launchpad
|
||||||
|
tar-ignore = !dms-distropkg-amd64.gz
|
||||||
|
tar-ignore = !dms-source.tar.gz
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<services>
|
||||||
|
<!-- Pull full git repository for master branch (QML code) -->
|
||||||
|
<service name="tar_scm">
|
||||||
|
<param name="scm">git</param>
|
||||||
|
<param name="url">https://github.com/AvengeMedia/DankMaterialShell.git</param>
|
||||||
|
<param name="revision">master</param>
|
||||||
|
<param name="filename">dms-git-source</param>
|
||||||
|
</service>
|
||||||
|
<service name="recompress">
|
||||||
|
<param name="file">*.tar</param>
|
||||||
|
<param name="compression">gz</param>
|
||||||
|
</service>
|
||||||
|
<!-- Download pre-built binaries -->
|
||||||
|
<service name="download_url">
|
||||||
|
<param name="protocol">https</param>
|
||||||
|
<param name="host">github.com</param>
|
||||||
|
<param name="path">/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-amd64.gz</param>
|
||||||
|
</service>
|
||||||
|
<service name="download_url">
|
||||||
|
<param name="protocol">https</param>
|
||||||
|
<param name="host">github.com</param>
|
||||||
|
<param name="path">/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-arm64.gz</param>
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
%global debug_package %{nil}
|
||||||
|
|
||||||
|
Name: dms-git
|
||||||
|
Version: 0.6.2+git
|
||||||
|
Release: 5%{?dist}
|
||||||
|
Epoch: 1
|
||||||
|
Summary: DankMaterialShell - Material 3 inspired shell (git nightly)
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Source0: dms-git-source.tar.gz
|
||||||
|
Source1: dms-distropkg-amd64.gz
|
||||||
|
Source2: dms-distropkg-arm64.gz
|
||||||
|
|
||||||
|
BuildRequires: gzip
|
||||||
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
|
Requires: (quickshell-git or quickshell)
|
||||||
|
Requires: accountsservice
|
||||||
|
Requires: dgop
|
||||||
|
|
||||||
|
Recommends: cava
|
||||||
|
Recommends: cliphist
|
||||||
|
Recommends: danksearch
|
||||||
|
Recommends: matugen
|
||||||
|
Recommends: quickshell-git
|
||||||
|
Recommends: wl-clipboard
|
||||||
|
|
||||||
|
Recommends: NetworkManager
|
||||||
|
Recommends: qt6-qtmultimedia
|
||||||
|
Suggests: qt6ct
|
||||||
|
|
||||||
|
Provides: dms
|
||||||
|
Conflicts: dms
|
||||||
|
Obsoletes: dms
|
||||||
|
|
||||||
|
%description
|
||||||
|
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
||||||
|
and optimized for niri, Hyprland, Sway, and other wlroots compositors.
|
||||||
|
|
||||||
|
This git version tracks the master branch and includes the latest features
|
||||||
|
and fixes. Includes pre-built dms CLI binary and QML shell files.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q -n dms-git-source
|
||||||
|
|
||||||
|
%ifarch x86_64
|
||||||
|
gunzip -c %{SOURCE1} > dms
|
||||||
|
%endif
|
||||||
|
%ifarch aarch64
|
||||||
|
gunzip -c %{SOURCE2} > dms
|
||||||
|
%endif
|
||||||
|
chmod +x dms
|
||||||
|
|
||||||
|
%build
|
||||||
|
|
||||||
|
%install
|
||||||
|
install -Dm755 dms %{buildroot}%{_bindir}/dms
|
||||||
|
|
||||||
|
install -d %{buildroot}%{_datadir}/bash-completion/completions
|
||||||
|
install -d %{buildroot}%{_datadir}/zsh/site-functions
|
||||||
|
install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
||||||
|
./dms completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||||
|
./dms completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||||
|
./dms completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||||
|
|
||||||
|
install -Dm644 quickshell/assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||||
|
|
||||||
|
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||||
|
cp -r quickshell/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||||
|
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||||
|
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/core
|
||||||
|
|
||||||
|
%posttrans
|
||||||
|
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||||
|
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||||
|
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" -ge 2 ]; then
|
||||||
|
pkill -USR1 -x dms >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
%files
|
||||||
|
%license LICENSE
|
||||||
|
%doc CONTRIBUTING.md
|
||||||
|
%doc quickshell/README.md
|
||||||
|
%{_bindir}/dms
|
||||||
|
%dir %{_datadir}/fish
|
||||||
|
%dir %{_datadir}/fish/vendor_completions.d
|
||||||
|
%{_datadir}/fish/vendor_completions.d/dms.fish
|
||||||
|
%dir %{_datadir}/zsh
|
||||||
|
%dir %{_datadir}/zsh/site-functions
|
||||||
|
%{_datadir}/zsh/site-functions/_dms
|
||||||
|
%{_datadir}/bash-completion/completions/dms
|
||||||
|
%dir %{_datadir}/quickshell
|
||||||
|
%{_datadir}/quickshell/dms/
|
||||||
|
%{_userunitdir}/dms.service
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* Fri Nov 22 2025 AvengeMedia <maintainer@avengemedia.com> - 0.6.2+git-5
|
||||||
|
- Git nightly build from master branch
|
||||||
|
- Multi-arch support (x86_64, aarch64)
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# Spec for DMS for OpenSUSE/OBS
|
||||||
|
|
||||||
|
%global debug_package %{nil}
|
||||||
|
|
||||||
|
Name: dms
|
||||||
|
Version: 0.6.2
|
||||||
|
Release: 1%{?dist}
|
||||||
|
Summary: DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Source0: dms-source.tar.gz
|
||||||
|
Source1: dms-distropkg-amd64.gz
|
||||||
|
Source2: dms-distropkg-arm64.gz
|
||||||
|
|
||||||
|
BuildRequires: gzip
|
||||||
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
|
# Core requirements
|
||||||
|
Requires: (quickshell-git or quickshell)
|
||||||
|
Requires: accountsservice
|
||||||
|
Requires: dgop
|
||||||
|
|
||||||
|
# Core utilities (Highly recommended for DMS functionality)
|
||||||
|
Recommends: cava
|
||||||
|
Recommends: cliphist
|
||||||
|
Recommends: danksearch
|
||||||
|
Recommends: matugen
|
||||||
|
Recommends: NetworkManager
|
||||||
|
Recommends: qt6-qtmultimedia
|
||||||
|
Recommends: wl-clipboard
|
||||||
|
Suggests: qt6ct
|
||||||
|
|
||||||
|
%description
|
||||||
|
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
||||||
|
and optimized for niri, Hyprland, Sway, and other wlroots compositors. Features
|
||||||
|
notifications, app launcher, wallpaper customization, and plugin system.
|
||||||
|
|
||||||
|
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
|
||||||
|
process monitoring, notification center, clipboard history, dock, control center,
|
||||||
|
lock screen, and comprehensive plugin system.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q -n DankMaterialShell-%{version}
|
||||||
|
|
||||||
|
%ifarch x86_64
|
||||||
|
gunzip -c %{SOURCE1} > dms
|
||||||
|
%endif
|
||||||
|
%ifarch aarch64
|
||||||
|
gunzip -c %{SOURCE2} > dms
|
||||||
|
%endif
|
||||||
|
chmod +x dms
|
||||||
|
|
||||||
|
%build
|
||||||
|
|
||||||
|
%install
|
||||||
|
install -Dm755 dms %{buildroot}%{_bindir}/dms
|
||||||
|
|
||||||
|
install -d %{buildroot}%{_datadir}/bash-completion/completions
|
||||||
|
install -d %{buildroot}%{_datadir}/zsh/site-functions
|
||||||
|
install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
||||||
|
./dms completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||||
|
./dms completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||||
|
./dms completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||||
|
|
||||||
|
install -Dm644 quickshell/assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||||
|
|
||||||
|
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||||
|
cp -r quickshell/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||||
|
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||||
|
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/core
|
||||||
|
|
||||||
|
%posttrans
|
||||||
|
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||||
|
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||||
|
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||||
|
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" -ge 2 ]; then
|
||||||
|
pkill -USR1 -x dms >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
%files
|
||||||
|
%license LICENSE
|
||||||
|
%doc CONTRIBUTING.md
|
||||||
|
%doc quickshell/README.md
|
||||||
|
%{_bindir}/dms
|
||||||
|
%dir %{_datadir}/fish
|
||||||
|
%dir %{_datadir}/fish/vendor_completions.d
|
||||||
|
%{_datadir}/fish/vendor_completions.d/dms.fish
|
||||||
|
%dir %{_datadir}/zsh
|
||||||
|
%dir %{_datadir}/zsh/site-functions
|
||||||
|
%{_datadir}/zsh/site-functions/_dms
|
||||||
|
%{_datadir}/bash-completion/completions/dms
|
||||||
|
%dir %{_datadir}/quickshell
|
||||||
|
%{_datadir}/quickshell/dms/
|
||||||
|
%{_userunitdir}/dms.service
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* Fri Nov 22 2025 AvengeMedia <maintainer@avengemedia.com> - 0.6.2-1
|
||||||
|
- Stable release build with pre-built binaries
|
||||||
|
- Multi-arch support (x86_64, aarch64)
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Unified OBS status checker for dms packages
|
||||||
|
# Checks all platforms (Debian, OpenSUSE) and architectures (x86_64, aarch64)
|
||||||
|
# Only pulls logs if build failed
|
||||||
|
# Usage: ./distro/scripts/obs-status.sh [package-name]
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# ./distro/scripts/obs-status.sh # Check all packages
|
||||||
|
# ./distro/scripts/obs-status.sh dms # Check specific package
|
||||||
|
|
||||||
|
OBS_BASE_PROJECT="home:AvengeMedia"
|
||||||
|
OBS_BASE="$HOME/.cache/osc-checkouts"
|
||||||
|
|
||||||
|
ALL_PACKAGES=(dms dms-git)
|
||||||
|
|
||||||
|
REPOS=("Debian_13" "openSUSE_Tumbleweed" "16.0")
|
||||||
|
ARCHES=("x86_64" "aarch64")
|
||||||
|
|
||||||
|
if [[ -n "$1" ]]; then
|
||||||
|
PACKAGES=("$1")
|
||||||
|
else
|
||||||
|
PACKAGES=("${ALL_PACKAGES[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$OBS_BASE"
|
||||||
|
|
||||||
|
for pkg in "${PACKAGES[@]}"; do
|
||||||
|
case "$pkg" in
|
||||||
|
dms)
|
||||||
|
PROJECT="$OBS_BASE_PROJECT:dms"
|
||||||
|
;;
|
||||||
|
dms-git)
|
||||||
|
PROJECT="$OBS_BASE_PROJECT:dms-git"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Error: Unknown package '$pkg'"
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo "=== $pkg ==="
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
# Checkout if needed
|
||||||
|
if [[ ! -d "$PROJECT/$pkg" ]]; then
|
||||||
|
osc co "$PROJECT/$pkg" 2>&1 | tail -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$PROJECT/$pkg"
|
||||||
|
|
||||||
|
ALL_RESULTS=$(osc results 2>&1)
|
||||||
|
|
||||||
|
# Check each repository and architecture
|
||||||
|
FAILED_BUILDS=()
|
||||||
|
for repo in "${REPOS[@]}"; do
|
||||||
|
for arch in "${ARCHES[@]}"; do
|
||||||
|
STATUS=$(echo "$ALL_RESULTS" | grep "$repo.*$arch" | awk '{print $NF}' | head -1)
|
||||||
|
|
||||||
|
if [[ -n "$STATUS" ]]; then
|
||||||
|
# Color code status
|
||||||
|
case "$STATUS" in
|
||||||
|
succeeded)
|
||||||
|
COLOR="\033[0;32m" # Green
|
||||||
|
SYMBOL="✅"
|
||||||
|
;;
|
||||||
|
failed)
|
||||||
|
COLOR="\033[0;31m" # Red
|
||||||
|
SYMBOL="❌"
|
||||||
|
FAILED_BUILDS+=("$repo $arch")
|
||||||
|
;;
|
||||||
|
unresolvable)
|
||||||
|
COLOR="\033[0;33m" # Yellow
|
||||||
|
SYMBOL="⚠️"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COLOR="\033[0;37m" # White
|
||||||
|
SYMBOL="⏳"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
echo -e " $SYMBOL $repo $arch: ${COLOR}$STATUS\033[0m"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Pull logs for failed builds
|
||||||
|
if [[ ${#FAILED_BUILDS[@]} -gt 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo " 📋 Fetching logs for failed builds..."
|
||||||
|
for build in "${FAILED_BUILDS[@]}"; do
|
||||||
|
read -r repo arch <<< "$build"
|
||||||
|
echo ""
|
||||||
|
echo " ────────────────────────────────────────────"
|
||||||
|
echo " Build log: $repo $arch"
|
||||||
|
echo " ────────────────────────────────────────────"
|
||||||
|
osc remotebuildlog "$PROJECT" "$pkg" "$repo" "$arch" 2>&1 | tail -100
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
cd - > /dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Status check complete!"
|
||||||
|
|
||||||
@@ -0,0 +1,733 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Unified OBS upload script for dms packages
|
||||||
|
# Handles Debian and OpenSUSE builds for both x86_64 and aarch64
|
||||||
|
# Usage: ./distro/scripts/obs-upload.sh [distro] <package-name> [commit-message]
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# ./distro/scripts/obs-upload.sh dms "Update to v0.6.2"
|
||||||
|
# ./distro/scripts/obs-upload.sh debian dms
|
||||||
|
# ./distro/scripts/obs-upload.sh opensuse dms-git
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
UPLOAD_DEBIAN=true
|
||||||
|
UPLOAD_OPENSUSE=true
|
||||||
|
PACKAGE=""
|
||||||
|
MESSAGE=""
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
debian)
|
||||||
|
UPLOAD_DEBIAN=true
|
||||||
|
UPLOAD_OPENSUSE=false
|
||||||
|
;;
|
||||||
|
opensuse)
|
||||||
|
UPLOAD_DEBIAN=false
|
||||||
|
UPLOAD_OPENSUSE=true
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ -z "$PACKAGE" ]]; then
|
||||||
|
PACKAGE="$arg"
|
||||||
|
elif [[ -z "$MESSAGE" ]]; then
|
||||||
|
MESSAGE="$arg"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
OBS_BASE_PROJECT="home:AvengeMedia"
|
||||||
|
OBS_BASE="$HOME/.cache/osc-checkouts"
|
||||||
|
|
||||||
|
# Available packages
|
||||||
|
AVAILABLE_PACKAGES=(dms dms-git)
|
||||||
|
|
||||||
|
if [[ -z "$PACKAGE" ]]; then
|
||||||
|
echo "Available packages:"
|
||||||
|
echo ""
|
||||||
|
echo " 1. dms - Stable DMS"
|
||||||
|
echo " 2. dms-git - Nightly DMS"
|
||||||
|
echo " a. all"
|
||||||
|
echo ""
|
||||||
|
read -p "Select package (1-${#AVAILABLE_PACKAGES[@]}, a): " selection
|
||||||
|
|
||||||
|
if [[ "$selection" == "a" ]] || [[ "$selection" == "all" ]]; then
|
||||||
|
PACKAGE="all"
|
||||||
|
elif [[ "$selection" =~ ^[0-9]+$ ]] && [[ "$selection" -ge 1 ]] && [[ "$selection" -le ${#AVAILABLE_PACKAGES[@]} ]]; then
|
||||||
|
PACKAGE="${AVAILABLE_PACKAGES[$((selection-1))]}"
|
||||||
|
else
|
||||||
|
echo "Error: Invalid selection"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle "all" option
|
||||||
|
if [[ "$PACKAGE" == "all" ]]; then
|
||||||
|
echo "==> Uploading all packages"
|
||||||
|
DISTRO_ARG=""
|
||||||
|
if [[ "$UPLOAD_DEBIAN" == true && "$UPLOAD_OPENSUSE" == false ]]; then
|
||||||
|
DISTRO_ARG="debian"
|
||||||
|
elif [[ "$UPLOAD_DEBIAN" == false && "$UPLOAD_OPENSUSE" == true ]]; then
|
||||||
|
DISTRO_ARG="opensuse"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
FAILED=()
|
||||||
|
for pkg in "${AVAILABLE_PACKAGES[@]}"; do
|
||||||
|
if [[ -d "distro/debian/$pkg" ]]; then
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Uploading $pkg..."
|
||||||
|
echo "=========================================="
|
||||||
|
if [[ -n "$DISTRO_ARG" ]]; then
|
||||||
|
if bash "$0" "$DISTRO_ARG" "$pkg" "$MESSAGE"; then
|
||||||
|
echo "✅ $pkg uploaded successfully"
|
||||||
|
else
|
||||||
|
echo "❌ $pkg failed to upload"
|
||||||
|
FAILED+=("$pkg")
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if bash "$0" "$pkg" "$MESSAGE"; then
|
||||||
|
echo "✅ $pkg uploaded successfully"
|
||||||
|
else
|
||||||
|
echo "❌ $pkg failed to upload"
|
||||||
|
FAILED+=("$pkg")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo "⚠️ Skipping $pkg (not found in distro/debian/)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#FAILED[@]} -eq 0 ]]; then
|
||||||
|
echo "✅ All packages uploaded successfully!"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "❌ Some packages failed: ${FAILED[*]}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if package exists
|
||||||
|
if [[ ! -d "distro/debian/$PACKAGE" ]]; then
|
||||||
|
echo "Error: Package '$PACKAGE' not found in distro/debian/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$PACKAGE" in
|
||||||
|
dms)
|
||||||
|
PROJECT="dms"
|
||||||
|
;;
|
||||||
|
dms-git)
|
||||||
|
PROJECT="dms-git"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Error: Unknown package '$PACKAGE'"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
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
|
||||||
|
echo "==> Distribution: Debian only"
|
||||||
|
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"
|
||||||
|
osc co "$OBS_PROJECT/$PACKAGE"
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
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))
|
||||||
|
echo " - Detected rebuild of same version $NEW_VERSION (release $OLD_RELEASE -> $NEXT_RELEASE)"
|
||||||
|
sed -i "s/^Release:[[:space:]]*${NEW_RELEASE}%{?dist}/Release: ${NEXT_RELEASE}%{?dist}/" "$WORK_DIR/$PACKAGE.spec"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " - New version detected: $OLD_VERSION -> $NEW_VERSION (keeping release $NEW_RELEASE)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " - First upload to OBS (no previous spec found)"
|
||||||
|
fi
|
||||||
|
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 [[ -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"
|
||||||
|
fi
|
||||||
|
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
|
||||||
|
OBS_TARBALL_DIR=$(mktemp -d -t obs-tarball-XXXXXX)
|
||||||
|
cd "$OBS_TARBALL_DIR"
|
||||||
|
|
||||||
|
case "$PACKAGE" in
|
||||||
|
dms)
|
||||||
|
DMS_VERSION=$(grep "^Version:" "$REPO_ROOT/distro/opensuse/$PACKAGE.spec" | sed 's/^Version:[[:space:]]*//' | head -1)
|
||||||
|
EXPECTED_DIR="DankMaterialShell-${DMS_VERSION}"
|
||||||
|
;;
|
||||||
|
dms-git)
|
||||||
|
EXPECTED_DIR="dms-git-source"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
EXPECTED_DIR=$(basename "$SOURCE_DIR")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo " Creating $SOURCE0 (directory: $EXPECTED_DIR)"
|
||||||
|
cp -r "$SOURCE_DIR" "$EXPECTED_DIR"
|
||||||
|
tar -czf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
|
||||||
|
rm -rf "$EXPECTED_DIR"
|
||||||
|
echo " Created $SOURCE0 ($(stat -c%s "$WORK_DIR/$SOURCE0" 2>/dev/null || echo 0) bytes)"
|
||||||
|
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
rm -rf "$OBS_TARBALL_DIR"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " - Warning: Could not obtain source for OpenSUSE tarball"
|
||||||
|
fi
|
||||||
|
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")
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
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/')
|
||||||
|
|
||||||
|
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)
|
||||||
|
# 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))" && \
|
||||||
|
! 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"
|
||||||
|
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"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
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)
|
||||||
|
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"
|
||||||
|
elif [[ "$SOURCE0" == *.tar.bz2 ]]; then
|
||||||
|
tar -cjf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
|
||||||
|
else
|
||||||
|
tar -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"
|
||||||
|
elif [[ "$SOURCE0" == *.tar.bz2 ]]; then
|
||||||
|
tar -cjf "$WORK_DIR/$SOURCE0" "$EXPECTED_DIR"
|
||||||
|
else
|
||||||
|
tar -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"
|
||||||
|
elif [[ "$SOURCE0" == *.tar.bz2 ]]; then
|
||||||
|
tar -cjf "$WORK_DIR/$SOURCE0" "$DIR_NAME"
|
||||||
|
else
|
||||||
|
tar -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
|
||||||
|
echo " Creating combined tarball: $COMBINED_TARBALL"
|
||||||
|
cd "$(dirname "$SOURCE_DIR")"
|
||||||
|
TARBALL_BASE=$(basename "$SOURCE_DIR")
|
||||||
|
tar -czf "$WORK_DIR/$COMBINED_TARBALL" "$TARBALL_BASE"
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
|
||||||
|
# Generate .dsc file for native format
|
||||||
|
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" | \
|
||||||
|
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 " - 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
|
||||||
|
VERSION="${CHANGELOG_VERSION}-1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " - Quilt format detected: creating debian.tar.gz"
|
||||||
|
tar -czf "$WORK_DIR/debian.tar.gz" -C "distro/debian/$PACKAGE" debian/
|
||||||
|
|
||||||
|
echo " - Generating $PACKAGE.dsc for quilt format"
|
||||||
|
cat > "$WORK_DIR/$PACKAGE.dsc" << EOF
|
||||||
|
Format: 3.0 (quilt)
|
||||||
|
Source: $PACKAGE
|
||||||
|
Binary: $PACKAGE
|
||||||
|
Architecture: any
|
||||||
|
Version: $VERSION
|
||||||
|
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||||
|
Build-Depends: debhelper-compat (= 13), wget, gzip
|
||||||
|
DEBTRANSFORM-TAR: debian.tar.gz
|
||||||
|
Files:
|
||||||
|
00000000000000000000000000000000 1 debian.tar.gz
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Change to working directory and commit
|
||||||
|
cd "$WORK_DIR"
|
||||||
|
|
||||||
|
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
|
||||||
|
ls -lh *.tar.gz *.dsc _service 2>/dev/null | awk '{print " " $9 " (" $5 ")"}'
|
||||||
|
elif [[ "$UPLOAD_OPENSUSE" == true ]]; then
|
||||||
|
ls -lh *.tar.gz *.tar.xz *.tar *.spec _service 2>/dev/null | awk '{print " " $9 " (" $5 ")"}'
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
osc addremove
|
||||||
|
|
||||||
|
echo "==> Committing to OBS"
|
||||||
|
osc commit -m "$MESSAGE"
|
||||||
|
|
||||||
|
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:"
|
||||||
|
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"
|
||||||
Executable
+169
@@ -0,0 +1,169 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Manual testing script for DMS packaging
|
||||||
|
# Tests OBS (Debian/openSUSE) and PPA (Ubuntu) workflows
|
||||||
|
# Usage: ./distro/test-packaging.sh [obs|ppa|all]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
DISTRO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$DISTRO_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||||
|
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
|
||||||
|
TEST_MODE="${1:-all}"
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "DMS Packaging Test Suite"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 1: OBS Upload (Debian + openSUSE)
|
||||||
|
if [[ "$TEST_MODE" == "obs" ]] || [[ "$TEST_MODE" == "all" ]]; then
|
||||||
|
echo "═══════════════════════════════════════════════════════════════════"
|
||||||
|
echo "TEST 1: OBS Upload (Debian + openSUSE)"
|
||||||
|
echo "═══════════════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
OBS_SCRIPT="$SCRIPT_DIR/obs-upload.sh"
|
||||||
|
|
||||||
|
if [[ ! -f "$OBS_SCRIPT" ]]; then
|
||||||
|
error "OBS script not found: $OBS_SCRIPT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "OBS script location: $OBS_SCRIPT"
|
||||||
|
info "Available packages: dms, dms-git"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
warn "This will upload to OBS (home:AvengeMedia)"
|
||||||
|
read -p "Continue with OBS test? [y/N] " -n 1 -r
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
info "Select package to test:"
|
||||||
|
echo " 1. dms (stable)"
|
||||||
|
echo " 2. dms-git (nightly)"
|
||||||
|
echo " 3. all (both packages)"
|
||||||
|
read -p "Choice [1]: " -n 1 -r PKG_CHOICE
|
||||||
|
echo
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
PKG_CHOICE="${PKG_CHOICE:-1}"
|
||||||
|
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
|
||||||
|
case "$PKG_CHOICE" in
|
||||||
|
1)
|
||||||
|
info "Testing OBS upload for 'dms' package..."
|
||||||
|
bash "$OBS_SCRIPT" dms "Test packaging update"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
info "Testing OBS upload for 'dms-git' package..."
|
||||||
|
bash "$OBS_SCRIPT" dms-git "Test packaging update"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
info "Testing OBS upload for all packages..."
|
||||||
|
bash "$OBS_SCRIPT" all "Test packaging update"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Invalid choice"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
success "OBS test completed"
|
||||||
|
echo ""
|
||||||
|
info "Check build status: https://build.opensuse.org/project/monitor/home:AvengeMedia"
|
||||||
|
else
|
||||||
|
warn "OBS test skipped"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 2: PPA Upload (Ubuntu)
|
||||||
|
if [[ "$TEST_MODE" == "ppa" ]] || [[ "$TEST_MODE" == "all" ]]; then
|
||||||
|
echo "═══════════════════════════════════════════════════════════════════"
|
||||||
|
echo "TEST 2: PPA Upload (Ubuntu)"
|
||||||
|
echo "═══════════════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
PPA_SCRIPT="$DISTRO_DIR/ubuntu/ppa/create-and-upload.sh"
|
||||||
|
|
||||||
|
if [[ ! -f "$PPA_SCRIPT" ]]; then
|
||||||
|
error "PPA script not found: $PPA_SCRIPT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "PPA script location: $PPA_SCRIPT"
|
||||||
|
info "Available PPAs: dms, dms-git"
|
||||||
|
info "Ubuntu series: questing (25.10)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
warn "This will upload to Launchpad PPA (ppa:avengemedia/dms)"
|
||||||
|
read -p "Continue with PPA test? [y/N] " -n 1 -r
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
info "Select package to test:"
|
||||||
|
echo " 1. dms (stable)"
|
||||||
|
echo " 2. dms-git (nightly)"
|
||||||
|
read -p "Choice [1]: " -n 1 -r PKG_CHOICE
|
||||||
|
echo
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
PKG_CHOICE="${PKG_CHOICE:-1}"
|
||||||
|
|
||||||
|
case "$PKG_CHOICE" in
|
||||||
|
1)
|
||||||
|
info "Testing PPA upload for 'dms' package..."
|
||||||
|
DMS_PKG="$DISTRO_DIR/ubuntu/dms"
|
||||||
|
PPA_NAME="dms"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
info "Testing PPA upload for 'dms-git' package..."
|
||||||
|
DMS_PKG="$DISTRO_DIR/ubuntu/dms-git"
|
||||||
|
PPA_NAME="dms-git"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Invalid choice"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ ! -d "$DMS_PKG" ]]; then
|
||||||
|
error "DMS package directory not found: $DMS_PKG"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
bash "$PPA_SCRIPT" "$DMS_PKG" "$PPA_NAME" questing
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
success "PPA test completed"
|
||||||
|
echo ""
|
||||||
|
info "Check build status: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages"
|
||||||
|
else
|
||||||
|
warn "PPA test skipped"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Testing Summary"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
danksearch (0.0.7ppa3) questing; urgency=medium
|
||||||
|
|
||||||
|
* Rebuild for packaging fixes (ppa3)
|
||||||
|
|
||||||
|
-- Avenge Media <AvengeMedia.US@gmail.com> Fri, 21 Nov 2025 14:19:58 -0500
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
Source: danksearch
|
||||||
|
Section: utils
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||||
|
Build-Depends: debhelper-compat (= 13)
|
||||||
|
Standards-Version: 4.6.2
|
||||||
|
Homepage: https://github.com/AvengeMedia/danksearch
|
||||||
|
Vcs-Browser: https://github.com/AvengeMedia/danksearch
|
||||||
|
Vcs-Git: https://github.com/AvengeMedia/danksearch.git
|
||||||
|
|
||||||
|
Package: danksearch
|
||||||
|
Architecture: amd64 arm64
|
||||||
|
Depends: ${misc:Depends}
|
||||||
|
Description: Fast file search utility for DMS
|
||||||
|
DankSearch is a fast file search utility designed for DankMaterialShell.
|
||||||
|
It provides efficient file and content search capabilities with minimal
|
||||||
|
dependencies. This package contains the pre-built binary from the official
|
||||||
|
GitHub release.
|
||||||
|
.
|
||||||
|
Features include:
|
||||||
|
- Fast file searching
|
||||||
|
- Lightweight and efficient
|
||||||
|
- Designed for DMS integration
|
||||||
|
- Minimal resource usage
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: danksearch
|
||||||
|
Upstream-Contact: Avenge Media LLC <AvengeMedia.US@gmail.com>
|
||||||
|
Source: https://github.com/AvengeMedia/danksearch
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2025 Avenge Media LLC
|
||||||
|
License: GPL-3.0-only
|
||||||
|
|
||||||
|
License: GPL-3.0-only
|
||||||
|
This package is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License version 3 as
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
.
|
||||||
|
This package is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
|
.
|
||||||
|
On Debian systems, the complete text of the GNU General
|
||||||
|
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
danksearch_0.0.7ppa3_source.buildinfo utils optional
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
export DH_VERBOSE = 1
|
||||||
|
|
||||||
|
# Detect architecture for selecting correct binary
|
||||||
|
DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
|
||||||
|
|
||||||
|
# Map Debian arch to binary filename
|
||||||
|
ifeq ($(DEB_HOST_ARCH),amd64)
|
||||||
|
BINARY_FILE := dsearch-amd64
|
||||||
|
else ifeq ($(DEB_HOST_ARCH),arm64)
|
||||||
|
BINARY_FILE := dsearch-arm64
|
||||||
|
else
|
||||||
|
$(error Unsupported architecture: $(DEB_HOST_ARCH))
|
||||||
|
endif
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
# Binary is already included in source package (native format)
|
||||||
|
# Downloaded by build-source.sh before upload
|
||||||
|
# Just verify it exists and is executable
|
||||||
|
test -f $(BINARY_FILE) || (echo "ERROR: $(BINARY_FILE) not found!" && exit 1)
|
||||||
|
chmod +x $(BINARY_FILE)
|
||||||
|
|
||||||
|
override_dh_auto_install:
|
||||||
|
# Install binary as danksearch
|
||||||
|
install -Dm755 $(BINARY_FILE) debian/danksearch/usr/bin/danksearch
|
||||||
|
|
||||||
|
override_dh_auto_clean:
|
||||||
|
# Don't delete binaries - they're part of the source package (native format)
|
||||||
|
dh_auto_clean
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,9 @@
|
|||||||
|
dgop (0.1.11ppa2) questing; urgency=medium
|
||||||
|
|
||||||
|
* Rebuild for Questing (25.10) - Ubuntu 25.10+ only
|
||||||
|
* Stateless CPU/GPU monitoring tool
|
||||||
|
* Support for NVIDIA and AMD GPUs
|
||||||
|
* JSON output for integration
|
||||||
|
* Pre-built binary package for amd64 and arm64
|
||||||
|
|
||||||
|
-- Avenge Media <AvengeMedia.US@gmail.com> Sun, 16 Nov 2025 22:50:00 -0500
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
Source: dgop
|
||||||
|
Section: utils
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||||
|
Build-Depends: debhelper-compat (= 13),
|
||||||
|
wget,
|
||||||
|
gzip
|
||||||
|
Standards-Version: 4.6.2
|
||||||
|
Homepage: https://github.com/AvengeMedia/dgop
|
||||||
|
Vcs-Browser: https://github.com/AvengeMedia/dgop
|
||||||
|
Vcs-Git: https://github.com/AvengeMedia/dgop.git
|
||||||
|
|
||||||
|
Package: dgop
|
||||||
|
Architecture: amd64 arm64
|
||||||
|
Depends: ${misc:Depends}
|
||||||
|
Description: Stateless CPU/GPU monitor for DankMaterialShell
|
||||||
|
DGOP is a stateless system monitoring tool that provides CPU, GPU,
|
||||||
|
memory, and network statistics. Designed for integration with
|
||||||
|
DankMaterialShell but can be used standalone.
|
||||||
|
.
|
||||||
|
Features:
|
||||||
|
- CPU usage monitoring
|
||||||
|
- GPU usage and temperature (NVIDIA, AMD)
|
||||||
|
- Memory and swap statistics
|
||||||
|
- Network traffic monitoring
|
||||||
|
- Zero-state design (no background processes)
|
||||||
|
- JSON output for easy integration
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: dgop
|
||||||
|
Upstream-Contact: Avenge Media LLC <AvengeMedia.US@gmail.com>
|
||||||
|
Source: https://github.com/AvengeMedia/dgop
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2025 Avenge Media LLC
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
.
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dgop_0.1.11ppa2_source.buildinfo utils optional
|
||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
export DH_VERBOSE = 1
|
||||||
|
|
||||||
|
# Extract version from debian/changelog
|
||||||
|
DEB_VERSION := $(shell dpkg-parsechangelog -S Version)
|
||||||
|
# Get upstream version (strip -1ppa1 suffix)
|
||||||
|
UPSTREAM_VERSION := $(shell echo $(DEB_VERSION) | sed 's/-[^-]*$$//')
|
||||||
|
|
||||||
|
# Detect architecture for downloading correct binary
|
||||||
|
DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
|
||||||
|
|
||||||
|
# Map Debian arch to GitHub release arch names
|
||||||
|
ifeq ($(DEB_HOST_ARCH),amd64)
|
||||||
|
GITHUB_ARCH := amd64
|
||||||
|
else ifeq ($(DEB_HOST_ARCH),arm64)
|
||||||
|
GITHUB_ARCH := arm64
|
||||||
|
else
|
||||||
|
$(error Unsupported architecture: $(DEB_HOST_ARCH))
|
||||||
|
endif
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
# Binary is already included in source package (native format)
|
||||||
|
# Just verify it exists and is executable
|
||||||
|
test -f dgop || (echo "ERROR: dgop binary not found!" && exit 1)
|
||||||
|
chmod +x dgop
|
||||||
|
|
||||||
|
override_dh_auto_install:
|
||||||
|
# Install binary
|
||||||
|
install -Dm755 dgop debian/dgop/usr/bin/dgop
|
||||||
|
|
||||||
|
override_dh_auto_clean:
|
||||||
|
# Don't delete dgop binary - it's part of the source package (native format)
|
||||||
|
rm -f dgop.gz
|
||||||
|
dh_auto_clean
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
dms-git (0.6.2+git2094.6cc6e7c8ppa1) questing; urgency=medium
|
||||||
|
|
||||||
|
* Git snapshot (commit 2094: 6cc6e7c8)
|
||||||
|
|
||||||
|
-- Avenge Media <AvengeMedia.US@gmail.com> Sun, 23 Nov 2025 00:43:28 -0500
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
Source: dms-git
|
||||||
|
Section: x11
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||||
|
Build-Depends: debhelper-compat (= 13)
|
||||||
|
Standards-Version: 4.6.2
|
||||||
|
Homepage: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||||
|
|
||||||
|
Package: dms-git
|
||||||
|
Architecture: amd64
|
||||||
|
Depends: ${misc:Depends},
|
||||||
|
quickshell-git | quickshell,
|
||||||
|
accountsservice,
|
||||||
|
cava,
|
||||||
|
cliphist,
|
||||||
|
danksearch,
|
||||||
|
dgop,
|
||||||
|
matugen,
|
||||||
|
qml6-module-qtcore,
|
||||||
|
qml6-module-qtmultimedia,
|
||||||
|
qml6-module-qtqml,
|
||||||
|
qml6-module-qtquick,
|
||||||
|
qml6-module-qtquick-controls,
|
||||||
|
qml6-module-qtquick-dialogs,
|
||||||
|
qml6-module-qtquick-effects,
|
||||||
|
qml6-module-qtquick-layouts,
|
||||||
|
qml6-module-qtquick-templates,
|
||||||
|
qml6-module-qtquick-window,
|
||||||
|
qt6ct,
|
||||||
|
wl-clipboard
|
||||||
|
Provides: dms
|
||||||
|
Conflicts: dms
|
||||||
|
Replaces: dms
|
||||||
|
Description: DankMaterialShell - Modern Wayland Desktop Shell (git nightly)
|
||||||
|
DMS (DankMaterialShell) is a feature-rich desktop shell built on
|
||||||
|
Quickshell, providing a modern and customizable user interface for
|
||||||
|
Wayland compositors like niri, hyprland, and sway.
|
||||||
|
.
|
||||||
|
This is the nightly/git version built from the latest master branch.
|
||||||
|
.
|
||||||
|
Features include:
|
||||||
|
- Material Design inspired UI
|
||||||
|
- Customizable themes and appearance
|
||||||
|
- Built-in application launcher
|
||||||
|
- System tray and notifications
|
||||||
|
- Network and Bluetooth management
|
||||||
|
- Audio controls
|
||||||
|
- Systemd integration
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: dms
|
||||||
|
Upstream-Contact: Avenge Media LLC <AvengeMedia.US@gmail.com>
|
||||||
|
Source: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2025 Avenge Media LLC
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
.
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dms-git_0.6.2+git2094.6cc6e7c8ppa1_source.buildinfo x11 optional
|
||||||
Executable
+45
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
export DH_VERBOSE = 1
|
||||||
|
|
||||||
|
# Get git commit date for version
|
||||||
|
GIT_DATE := $(shell date +%Y%m%d)
|
||||||
|
GIT_COMMIT := HEAD
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
# Git source is already included in source package (cloned by build-source.sh)
|
||||||
|
# Launchpad build environment has no internet access
|
||||||
|
test -d dms-git-repo || (echo "ERROR: dms-git-repo directory not found!" && exit 1)
|
||||||
|
test -f dms-distropkg-amd64.gz || (echo "ERROR: dms-distropkg-amd64.gz not found!" && exit 1)
|
||||||
|
|
||||||
|
# Extract pre-built binary from latest release
|
||||||
|
# Note: For git versions, we use the latest release binary
|
||||||
|
# The QML files come from git master
|
||||||
|
gunzip -c dms-distropkg-amd64.gz > dms
|
||||||
|
chmod +x dms
|
||||||
|
|
||||||
|
override_dh_auto_install:
|
||||||
|
# Install binary
|
||||||
|
install -Dm755 dms debian/dms-git/usr/bin/dms
|
||||||
|
|
||||||
|
# Install QML files from git clone
|
||||||
|
mkdir -p debian/dms-git/usr/share/quickshell/dms
|
||||||
|
cp -r dms-git-repo/* debian/dms-git/usr/share/quickshell/dms/
|
||||||
|
|
||||||
|
# Remove unnecessary directories
|
||||||
|
rm -rf debian/dms-git/usr/share/quickshell/dms/core
|
||||||
|
rm -rf debian/dms-git/usr/share/quickshell/dms/distro
|
||||||
|
|
||||||
|
# Install systemd user service
|
||||||
|
install -Dm644 dms-git-repo/quickshell/assets/systemd/dms.service \
|
||||||
|
debian/dms-git/usr/lib/systemd/user/dms.service
|
||||||
|
|
||||||
|
override_dh_auto_clean:
|
||||||
|
# Don't delete dms-git-repo directory - it's part of the source package (native format)
|
||||||
|
# Clean up build artifacts (but keep dms-distropkg-amd64.gz for Launchpad)
|
||||||
|
rm -f dms
|
||||||
|
# Don't remove dms-distropkg-amd64.gz - it needs to be included in the source package for Launchpad builds
|
||||||
|
dh_auto_clean
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dms-distropkg-amd64.gz
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Include files that are normally excluded by .gitignore
|
||||||
|
# These are needed for the build process on Launchpad (which has no internet access)
|
||||||
|
tar-ignore = !dms-distropkg-amd64.gz
|
||||||
|
tar-ignore = !dms-git-repo
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
dms (0.6.2ppa3) questing; urgency=medium
|
||||||
|
|
||||||
|
* Rebuild for packaging fixes (ppa3)
|
||||||
|
|
||||||
|
-- Avenge Media <AvengeMedia.US@gmail.com> Sun, 23 Nov 2025 00:40:41 -0500
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
Source: dms
|
||||||
|
Section: x11
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||||
|
Build-Depends: debhelper-compat (= 13)
|
||||||
|
Standards-Version: 4.6.2
|
||||||
|
Homepage: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||||
|
|
||||||
|
Package: dms
|
||||||
|
Architecture: amd64
|
||||||
|
Depends: ${misc:Depends},
|
||||||
|
quickshell-git | quickshell,
|
||||||
|
accountsservice,
|
||||||
|
cava,
|
||||||
|
cliphist,
|
||||||
|
danksearch,
|
||||||
|
dgop,
|
||||||
|
matugen,
|
||||||
|
qml6-module-qtcore,
|
||||||
|
qml6-module-qtmultimedia,
|
||||||
|
qml6-module-qtqml,
|
||||||
|
qml6-module-qtquick,
|
||||||
|
qml6-module-qtquick-controls,
|
||||||
|
qml6-module-qtquick-dialogs,
|
||||||
|
qml6-module-qtquick-effects,
|
||||||
|
qml6-module-qtquick-layouts,
|
||||||
|
qml6-module-qtquick-templates,
|
||||||
|
qml6-module-qtquick-window,
|
||||||
|
qt6ct,
|
||||||
|
wl-clipboard
|
||||||
|
Conflicts: dms-git
|
||||||
|
Replaces: dms-git
|
||||||
|
Description: DankMaterialShell - Modern Wayland Desktop Shell
|
||||||
|
DMS (DankMaterialShell) is a feature-rich desktop shell built on
|
||||||
|
Quickshell, providing a modern and customizable user interface for
|
||||||
|
Wayland compositors like niri, hyprland, and sway.
|
||||||
|
.
|
||||||
|
Features include:
|
||||||
|
- Material Design inspired UI
|
||||||
|
- Customizable themes and appearance
|
||||||
|
- Built-in application launcher
|
||||||
|
- System tray and notifications
|
||||||
|
- Network and Bluetooth management
|
||||||
|
- Audio controls
|
||||||
|
- Systemd integration
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: dms
|
||||||
|
Upstream-Contact: Avenge Media LLC <AvengeMedia.US@gmail.com>
|
||||||
|
Source: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2025 Avenge Media LLC
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
.
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dms_0.6.2ppa3_source.buildinfo x11 optional
|
||||||
Executable
+63
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
export DH_VERBOSE = 1
|
||||||
|
|
||||||
|
# Extract version from debian/changelog
|
||||||
|
DEB_VERSION := $(shell dpkg-parsechangelog -S Version)
|
||||||
|
# Get upstream version (strip -1ppa1 suffix)
|
||||||
|
UPSTREAM_VERSION := $(shell echo $(DEB_VERSION) | sed 's/-[^-]*$$//')
|
||||||
|
# Strip ppa suffix and handle git versions
|
||||||
|
# Examples: 0.5.2ppa9 -> 0.5.2, 0.5.2+git20251116 -> 0.5.2
|
||||||
|
BASE_VERSION := $(shell echo $(UPSTREAM_VERSION) | sed 's/ppa[0-9]*$$//' | sed 's/+git.*//')
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
# All files are included in source package (downloaded by build-source.sh)
|
||||||
|
# Launchpad build environment has no internet access
|
||||||
|
test -f dms-distropkg-amd64.gz || (echo "ERROR: dms-distropkg-amd64.gz not found!" && exit 1)
|
||||||
|
test -f dms-source.tar.gz || (echo "ERROR: dms-source.tar.gz not found!" && exit 1)
|
||||||
|
|
||||||
|
# Extract pre-built binary
|
||||||
|
gunzip -c dms-distropkg-amd64.gz > dms
|
||||||
|
chmod +x dms
|
||||||
|
|
||||||
|
# Extract source tarball for QML files
|
||||||
|
tar -xzf dms-source.tar.gz
|
||||||
|
# Find the extracted directory (it might have various names)
|
||||||
|
# and create a symlink to expected name for consistent install
|
||||||
|
SOURCE_DIR=$$(find . -maxdepth 1 -type d -name "DankMaterialShell*" | head -n1); \
|
||||||
|
if [ -n "$$SOURCE_DIR" ]; then \
|
||||||
|
ln -sf $$SOURCE_DIR DankMaterialShell-$(BASE_VERSION); \
|
||||||
|
fi
|
||||||
|
|
||||||
|
override_dh_auto_install:
|
||||||
|
# Install binary
|
||||||
|
install -Dm755 dms debian/dms/usr/bin/dms
|
||||||
|
|
||||||
|
# Install QML files from source tarball
|
||||||
|
mkdir -p debian/dms/usr/share/quickshell/dms
|
||||||
|
cp -r DankMaterialShell-$(BASE_VERSION)/* debian/dms/usr/share/quickshell/dms/
|
||||||
|
|
||||||
|
# Remove unnecessary directories
|
||||||
|
rm -rf debian/dms/usr/share/quickshell/dms/core
|
||||||
|
rm -rf debian/dms/usr/share/quickshell/dms/distro
|
||||||
|
|
||||||
|
# Install systemd user service
|
||||||
|
install -Dm644 DankMaterialShell-$(BASE_VERSION)/quickshell/assets/systemd/dms.service \
|
||||||
|
debian/dms/usr/lib/systemd/user/dms.service
|
||||||
|
|
||||||
|
# Generate and install shell completions (if applicable)
|
||||||
|
# Uncomment if dms supports completion generation
|
||||||
|
# ./dms completion bash > dms.bash
|
||||||
|
# ./dms completion zsh > dms.zsh
|
||||||
|
# install -Dm644 dms.bash debian/dms/usr/share/bash-completion/completions/dms
|
||||||
|
# install -Dm644 dms.zsh debian/dms/usr/share/zsh/vendor-completions/_dms
|
||||||
|
|
||||||
|
override_dh_auto_clean:
|
||||||
|
rm -f dms
|
||||||
|
rm -rf DankMaterialShell-*
|
||||||
|
# Don't remove dms-distropkg-amd64.gz and dms-source.tar.gz
|
||||||
|
# They need to be included in the source package for Launchpad builds
|
||||||
|
dh_auto_clean
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
dms-distropkg-amd64.gz
|
||||||
|
dms-source.tar.gz
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Include files that are normally excluded by .gitignore
|
||||||
|
# These are needed for the build process on Launchpad (which has no internet access)
|
||||||
|
tar-ignore = !dms-distropkg-amd64.gz
|
||||||
|
tar-ignore = !dms-source.tar.gz
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# dput configuration for AvengeMedia DMS PPAs
|
||||||
|
# Copy this to ~/.dput.cf (or merge with existing ~/.dput.cf)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# dput ppa:avengemedia/dms ../package_version_source.changes
|
||||||
|
# dput ppa:avengemedia/dms-git ../package_version_source.changes
|
||||||
|
|
||||||
|
# Stable DMS PPA - for release versions
|
||||||
|
[ppa:avengemedia/dms]
|
||||||
|
fqdn = ppa.launchpad.net
|
||||||
|
method = ftp
|
||||||
|
incoming = ~avengemedia/ubuntu/dms/
|
||||||
|
login = anonymous
|
||||||
|
allow_unsigned_uploads = 0
|
||||||
|
|
||||||
|
# Nightly/Git DMS PPA - for development builds
|
||||||
|
[ppa:avengemedia/dms-git]
|
||||||
|
fqdn = ppa.launchpad.net
|
||||||
|
method = ftp
|
||||||
|
incoming = ~avengemedia/ubuntu/dms-git/
|
||||||
|
login = anonymous
|
||||||
|
allow_unsigned_uploads = 0
|
||||||
|
|
||||||
|
# Alternative: Use HTTPS instead of FTP (more reliable through firewalls)
|
||||||
|
# Uncomment these if FTP doesn't work:
|
||||||
|
#
|
||||||
|
# [ppa:avengemedia/dms-https]
|
||||||
|
# fqdn = ppa.launchpad.net
|
||||||
|
# method = https
|
||||||
|
# incoming = ~avengemedia/ubuntu/dms/
|
||||||
|
# login = anonymous
|
||||||
|
# allow_unsigned_uploads = 0
|
||||||
|
#
|
||||||
|
# [ppa:avengemedia/dms-git-https]
|
||||||
|
# fqdn = ppa.launchpad.net
|
||||||
|
# method = https
|
||||||
|
# incoming = ~avengemedia/ubuntu/dms-git/
|
||||||
|
# login = anonymous
|
||||||
|
# allow_unsigned_uploads = 0
|
||||||
|
|
||||||
|
# Notes:
|
||||||
|
# - allow_unsigned_uploads = 0 enforces GPG signing (required by Launchpad)
|
||||||
|
# - anonymous login is standard for PPA uploads
|
||||||
|
# - The incoming path must match your Launchpad username and PPA name
|
||||||
Executable
+246
@@ -0,0 +1,246 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Build and upload PPA package with automatic cleanup
|
||||||
|
# Usage: ./create-and-upload.sh <package-dir> <ppa-name> [ubuntu-series] [--keep-builds]
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# ./create-and-upload.sh ../dms dms questing
|
||||||
|
# ./create-and-upload.sh ../danklinux/dgop danklinux questing --keep-builds
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||||
|
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
KEEP_BUILDS=false
|
||||||
|
ARGS=()
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [ "$arg" = "--keep-builds" ]; then
|
||||||
|
KEEP_BUILDS=true
|
||||||
|
else
|
||||||
|
ARGS+=("$arg")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${#ARGS[@]} -lt 2 ]; then
|
||||||
|
error "Usage: $0 <package-dir> <ppa-name> [ubuntu-series] [--keep-builds]"
|
||||||
|
echo
|
||||||
|
echo "Arguments:"
|
||||||
|
echo " package-dir : Path to package directory (e.g., ../dms, ../danklinux/dgop)"
|
||||||
|
echo " ppa-name : PPA name (danklinux, dms, dms-git)"
|
||||||
|
echo " ubuntu-series : Ubuntu series (optional, default: questing)"
|
||||||
|
echo " Supported: questing (25.10) and newer only"
|
||||||
|
echo " Note: Requires Qt 6.6+ (quickshell requirement)"
|
||||||
|
echo " --keep-builds : Keep build artifacts after upload (optional)"
|
||||||
|
echo
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 ../dms dms questing"
|
||||||
|
echo " $0 ../danklinux/dgop danklinux questing --keep-builds"
|
||||||
|
echo " $0 ../dms-git dms-git # Defaults to questing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PACKAGE_DIR="${ARGS[0]}"
|
||||||
|
PPA_NAME="${ARGS[1]}"
|
||||||
|
UBUNTU_SERIES="${ARGS[2]:-questing}"
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
BUILD_SCRIPT="$SCRIPT_DIR/create-source.sh"
|
||||||
|
UPLOAD_SCRIPT="$SCRIPT_DIR/upload-ppa.sh"
|
||||||
|
|
||||||
|
# Validate scripts exist
|
||||||
|
if [ ! -f "$BUILD_SCRIPT" ]; then
|
||||||
|
error "Build script not found: $BUILD_SCRIPT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get absolute path
|
||||||
|
PACKAGE_DIR=$(cd "$PACKAGE_DIR" && pwd)
|
||||||
|
PACKAGE_NAME=$(basename "$PACKAGE_DIR")
|
||||||
|
PARENT_DIR=$(dirname "$PACKAGE_DIR")
|
||||||
|
|
||||||
|
info "Building and uploading: $PACKAGE_NAME"
|
||||||
|
info "Package directory: $PACKAGE_DIR"
|
||||||
|
info "PPA: ppa:avengemedia/$PPA_NAME"
|
||||||
|
info "Ubuntu series: $UBUNTU_SERIES"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Step 1: Build source package
|
||||||
|
info "Step 1: Building source package..."
|
||||||
|
if ! "$BUILD_SCRIPT" "$PACKAGE_DIR" "$UBUNTU_SERIES"; then
|
||||||
|
error "Build failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find the changes file
|
||||||
|
CHANGES_FILE=$(find "$PARENT_DIR" -maxdepth 1 -name "${PACKAGE_NAME}_*_source.changes" -type f | sort -V | tail -1)
|
||||||
|
|
||||||
|
if [ -z "$CHANGES_FILE" ]; then
|
||||||
|
error "Changes file not found in $PARENT_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Found changes file: $CHANGES_FILE"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Step 2: Upload to PPA
|
||||||
|
info "Step 2: Uploading to PPA..."
|
||||||
|
|
||||||
|
# Check if using lftp (for all PPAs) or dput
|
||||||
|
if [ "$PPA_NAME" = "danklinux" ] || [ "$PPA_NAME" = "dms" ] || [ "$PPA_NAME" = "dms-git" ]; then
|
||||||
|
warn "Using lftp for upload"
|
||||||
|
|
||||||
|
# Extract version from changes file
|
||||||
|
VERSION=$(grep "^Version:" "$CHANGES_FILE" | awk '{print $2}')
|
||||||
|
SOURCE_NAME=$(grep "^Source:" "$CHANGES_FILE" | awk '{print $2}')
|
||||||
|
|
||||||
|
# Find all files to upload
|
||||||
|
BUILD_DIR=$(dirname "$CHANGES_FILE")
|
||||||
|
CHANGES_BASENAME=$(basename "$CHANGES_FILE")
|
||||||
|
DSC_FILE="${CHANGES_BASENAME/_source.changes/.dsc}"
|
||||||
|
TARBALL="${CHANGES_BASENAME/_source.changes/.tar.xz}"
|
||||||
|
BUILDINFO="${CHANGES_BASENAME/_source.changes/_source.buildinfo}"
|
||||||
|
|
||||||
|
# Check all files exist
|
||||||
|
MISSING_FILES=()
|
||||||
|
[ ! -f "$BUILD_DIR/$DSC_FILE" ] && MISSING_FILES+=("$DSC_FILE")
|
||||||
|
[ ! -f "$BUILD_DIR/$TARBALL" ] && MISSING_FILES+=("$TARBALL")
|
||||||
|
[ ! -f "$BUILD_DIR/$BUILDINFO" ] && MISSING_FILES+=("$BUILDINFO")
|
||||||
|
|
||||||
|
if [ ${#MISSING_FILES[@]} -gt 0 ]; then
|
||||||
|
error "Missing required files:"
|
||||||
|
for file in "${MISSING_FILES[@]}"; do
|
||||||
|
error " - $file"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Uploading files:"
|
||||||
|
info " - $CHANGES_BASENAME"
|
||||||
|
info " - $DSC_FILE"
|
||||||
|
info " - $TARBALL"
|
||||||
|
info " - $BUILDINFO"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# lftp build dir change
|
||||||
|
LFTP_SCRIPT=$(mktemp)
|
||||||
|
cat > "$LFTP_SCRIPT" <<EOF
|
||||||
|
cd ~avengemedia/ubuntu/$PPA_NAME/
|
||||||
|
lcd $BUILD_DIR
|
||||||
|
mput $CHANGES_BASENAME
|
||||||
|
mput $DSC_FILE
|
||||||
|
mput $TARBALL
|
||||||
|
mput $BUILDINFO
|
||||||
|
bye
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if lftp -d ftp://anonymous:@ppa.launchpad.net < "$LFTP_SCRIPT"; then
|
||||||
|
success "Upload successful!"
|
||||||
|
rm -f "$LFTP_SCRIPT"
|
||||||
|
else
|
||||||
|
error "Upload failed!"
|
||||||
|
rm -f "$LFTP_SCRIPT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Use dput for other PPAs
|
||||||
|
if [ ! -f "$UPLOAD_SCRIPT" ]; then
|
||||||
|
error "Upload script not found: $UPLOAD_SCRIPT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Auto-confirm upload (pipe 'y' to the confirmation prompt)
|
||||||
|
if ! echo "y" | "$UPLOAD_SCRIPT" "$CHANGES_FILE" "$PPA_NAME"; then
|
||||||
|
error "Upload failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
success "Package uploaded successfully!"
|
||||||
|
info "Monitor build progress at:"
|
||||||
|
echo " https://launchpad.net/~avengemedia/+archive/ubuntu/$PPA_NAME/+packages"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Step 3: Cleanup (unless --keep-builds is specified)
|
||||||
|
if [ "$KEEP_BUILDS" = "false" ]; then
|
||||||
|
info "Step 3: Cleaning up build artifacts..."
|
||||||
|
|
||||||
|
# Find all build artifacts in parent directory
|
||||||
|
ARTIFACTS=(
|
||||||
|
"${PACKAGE_NAME}_*.dsc"
|
||||||
|
"${PACKAGE_NAME}_*.tar.xz"
|
||||||
|
"${PACKAGE_NAME}_*.tar.gz"
|
||||||
|
"${PACKAGE_NAME}_*_source.changes"
|
||||||
|
"${PACKAGE_NAME}_*_source.buildinfo"
|
||||||
|
"${PACKAGE_NAME}_*_source.build"
|
||||||
|
)
|
||||||
|
|
||||||
|
REMOVED=0
|
||||||
|
for pattern in "${ARTIFACTS[@]}"; do
|
||||||
|
for file in "$PARENT_DIR"/$pattern; do
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
rm -f "$file"
|
||||||
|
REMOVED=$((REMOVED + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Clean up downloaded binaries in package directory
|
||||||
|
case "$PACKAGE_NAME" in
|
||||||
|
danksearch)
|
||||||
|
if [ -f "$PACKAGE_DIR/dsearch-amd64" ]; then
|
||||||
|
rm -f "$PACKAGE_DIR/dsearch-amd64"
|
||||||
|
REMOVED=$((REMOVED + 1))
|
||||||
|
fi
|
||||||
|
if [ -f "$PACKAGE_DIR/dsearch-arm64" ]; then
|
||||||
|
rm -f "$PACKAGE_DIR/dsearch-arm64"
|
||||||
|
REMOVED=$((REMOVED + 1))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
dms)
|
||||||
|
# Remove downloaded binaries and source
|
||||||
|
if [ -f "$PACKAGE_DIR/dms-distropkg-amd64.gz" ]; then
|
||||||
|
rm -f "$PACKAGE_DIR/dms-distropkg-amd64.gz"
|
||||||
|
REMOVED=$((REMOVED + 1))
|
||||||
|
fi
|
||||||
|
if [ -f "$PACKAGE_DIR/dms-source.tar.gz" ]; then
|
||||||
|
rm -f "$PACKAGE_DIR/dms-source.tar.gz"
|
||||||
|
REMOVED=$((REMOVED + 1))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
dms-git)
|
||||||
|
# Remove downloaded binary
|
||||||
|
if [ -f "$PACKAGE_DIR/dms-distropkg-amd64.gz" ]; then
|
||||||
|
rm -f "$PACKAGE_DIR/dms-distropkg-amd64.gz"
|
||||||
|
REMOVED=$((REMOVED + 1))
|
||||||
|
fi
|
||||||
|
# Remove git source directory
|
||||||
|
if [ -d "$PACKAGE_DIR/dms-git-repo" ]; then
|
||||||
|
rm -rf "$PACKAGE_DIR/dms-git-repo"
|
||||||
|
REMOVED=$((REMOVED + 1))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ $REMOVED -gt 0 ]; then
|
||||||
|
success "Removed $REMOVED build artifact(s)"
|
||||||
|
else
|
||||||
|
info "No build artifacts to clean up"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
info "Keeping build artifacts (--keep-builds specified)"
|
||||||
|
info "Build artifacts in: $PARENT_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
success "Done!"
|
||||||
|
|
||||||
Executable
+566
@@ -0,0 +1,566 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Generic source package builder for DMS PPA packages
|
||||||
|
# Usage: ./create-source.sh <package-dir> [ubuntu-series]
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# ./create-source.sh ../dms questing
|
||||||
|
# ./create-source.sh ../dms-git questing
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||||
|
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
error "Usage: $0 <package-dir> [ubuntu-series]"
|
||||||
|
echo
|
||||||
|
echo "Arguments:"
|
||||||
|
echo " package-dir : Path to package directory (e.g., ../dms)"
|
||||||
|
echo " ubuntu-series : Ubuntu series (optional, default: noble)"
|
||||||
|
echo " Options: noble, jammy, oracular, mantic"
|
||||||
|
echo
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 ../dms questing"
|
||||||
|
echo " $0 ../dms-git questing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PACKAGE_DIR="$1"
|
||||||
|
UBUNTU_SERIES="${2:-noble}"
|
||||||
|
|
||||||
|
# Validate package directory
|
||||||
|
if [ ! -d "$PACKAGE_DIR" ]; then
|
||||||
|
error "Package directory not found: $PACKAGE_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$PACKAGE_DIR/debian" ]; then
|
||||||
|
error "No debian/ directory found in $PACKAGE_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get absolute path
|
||||||
|
PACKAGE_DIR=$(cd "$PACKAGE_DIR" && pwd)
|
||||||
|
PACKAGE_NAME=$(basename "$PACKAGE_DIR")
|
||||||
|
|
||||||
|
info "Building source package for: $PACKAGE_NAME"
|
||||||
|
info "Package directory: $PACKAGE_DIR"
|
||||||
|
info "Target Ubuntu series: $UBUNTU_SERIES"
|
||||||
|
|
||||||
|
# Check for required files
|
||||||
|
REQUIRED_FILES=(
|
||||||
|
"debian/control"
|
||||||
|
"debian/rules"
|
||||||
|
"debian/changelog"
|
||||||
|
"debian/copyright"
|
||||||
|
"debian/source/format"
|
||||||
|
)
|
||||||
|
|
||||||
|
for file in "${REQUIRED_FILES[@]}"; do
|
||||||
|
if [ ! -f "$PACKAGE_DIR/$file" ]; then
|
||||||
|
error "Required file missing: $file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Verify GPG key is set up
|
||||||
|
info "Checking GPG key setup..."
|
||||||
|
if ! gpg --list-secret-keys &> /dev/null; then
|
||||||
|
error "No GPG secret keys found. Please set up GPG first!"
|
||||||
|
error "See GPG_SETUP.md for instructions"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "GPG key found"
|
||||||
|
|
||||||
|
# Check if debuild is installed
|
||||||
|
if ! command -v debuild &> /dev/null; then
|
||||||
|
error "debuild not found. Install devscripts:"
|
||||||
|
error " sudo dnf install devscripts"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract package info from changelog
|
||||||
|
cd "$PACKAGE_DIR"
|
||||||
|
CHANGELOG_VERSION=$(dpkg-parsechangelog -S Version)
|
||||||
|
SOURCE_NAME=$(dpkg-parsechangelog -S Source)
|
||||||
|
|
||||||
|
info "Source package: $SOURCE_NAME"
|
||||||
|
info "Version: $CHANGELOG_VERSION"
|
||||||
|
|
||||||
|
# Check if version targets correct Ubuntu series
|
||||||
|
CHANGELOG_SERIES=$(dpkg-parsechangelog -S Distribution)
|
||||||
|
if [ "$CHANGELOG_SERIES" != "$UBUNTU_SERIES" ] && [ "$CHANGELOG_SERIES" != "UNRELEASED" ]; then
|
||||||
|
warn "Changelog targets '$CHANGELOG_SERIES' but building for '$UBUNTU_SERIES'"
|
||||||
|
warn "Consider updating changelog with: dch -r '' -D $UBUNTU_SERIES"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect package type and update version automatically
|
||||||
|
cd "$PACKAGE_DIR"
|
||||||
|
|
||||||
|
# Function to get latest tag from GitHub
|
||||||
|
get_latest_tag() {
|
||||||
|
local repo="$1"
|
||||||
|
# Try GitHub API first (faster)
|
||||||
|
if command -v curl &> /dev/null; then
|
||||||
|
LATEST_TAG=$(curl -s "https://api.github.com/repos/$repo/releases/latest" 2>/dev/null | grep '"tag_name":' | sed 's/.*"tag_name": "\(.*\)".*/\1/' | head -1)
|
||||||
|
if [ -n "$LATEST_TAG" ]; then
|
||||||
|
echo "$LATEST_TAG" | sed 's/^v//'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Fallback: clone and get latest tag
|
||||||
|
TEMP_REPO=$(mktemp -d)
|
||||||
|
if git clone --depth=1 --quiet "https://github.com/$repo.git" "$TEMP_REPO" 2>/dev/null; then
|
||||||
|
LATEST_TAG=$(cd "$TEMP_REPO" && git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "")
|
||||||
|
rm -rf "$TEMP_REPO"
|
||||||
|
echo "$LATEST_TAG"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect if package is git-based
|
||||||
|
IS_GIT_PACKAGE=false
|
||||||
|
GIT_REPO=""
|
||||||
|
SOURCE_DIR=""
|
||||||
|
|
||||||
|
# Check package name for -git suffix
|
||||||
|
if [[ "$PACKAGE_NAME" == *"-git" ]]; then
|
||||||
|
IS_GIT_PACKAGE=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check rules file for git clone patterns and extract repo
|
||||||
|
if grep -q "git clone" debian/rules 2>/dev/null; then
|
||||||
|
IS_GIT_PACKAGE=true
|
||||||
|
# Extract GitHub repo URL from rules
|
||||||
|
GIT_URL=$(grep -o "git clone.*https://github.com/[^/]*/[^/]*\.git" debian/rules 2>/dev/null | head -1 | sed 's/.*github\.com\///' | sed 's/\.git.*//' || echo "")
|
||||||
|
if [ -n "$GIT_URL" ]; then
|
||||||
|
GIT_REPO="$GIT_URL"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Special handling for known packages
|
||||||
|
case "$PACKAGE_NAME" in
|
||||||
|
dms-git)
|
||||||
|
IS_GIT_PACKAGE=true
|
||||||
|
GIT_REPO="AvengeMedia/DankMaterialShell"
|
||||||
|
SOURCE_DIR="dms-git-repo"
|
||||||
|
;;
|
||||||
|
dms)
|
||||||
|
GIT_REPO="AvengeMedia/DankMaterialShell"
|
||||||
|
info "Downloading pre-built binaries and source for dms..."
|
||||||
|
# Get version from changelog (remove ppa suffix for both quilt and native formats)
|
||||||
|
# Native: 0.5.2ppa1 -> 0.5.2, Quilt: 0.5.2-1ppa1 -> 0.5.2
|
||||||
|
VERSION=$(dpkg-parsechangelog -S Version | sed 's/-[^-]*$//' | sed 's/ppa[0-9]*$//')
|
||||||
|
|
||||||
|
# Download amd64 binary (will be included in source package)
|
||||||
|
if [ ! -f "dms-distropkg-amd64.gz" ]; then
|
||||||
|
info "Downloading dms binary for amd64..."
|
||||||
|
if wget -O dms-distropkg-amd64.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/download/v${VERSION}/dms-distropkg-amd64.gz"; then
|
||||||
|
success "amd64 binary downloaded"
|
||||||
|
else
|
||||||
|
error "Failed to download dms-distropkg-amd64.gz"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Download source tarball for QML files
|
||||||
|
if [ ! -f "dms-source.tar.gz" ]; then
|
||||||
|
info "Downloading dms source for QML files..."
|
||||||
|
if wget -O dms-source.tar.gz "https://github.com/AvengeMedia/DankMaterialShell/archive/refs/tags/v${VERSION}.tar.gz"; then
|
||||||
|
success "source tarball downloaded"
|
||||||
|
else
|
||||||
|
error "Failed to download dms-source.tar.gz"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
danksearch)
|
||||||
|
# danksearch uses pre-built binary from releases, like dgop
|
||||||
|
GIT_REPO="AvengeMedia/danksearch"
|
||||||
|
;;
|
||||||
|
dgop)
|
||||||
|
# dgop uses pre-built binary from releases
|
||||||
|
GIT_REPO="AvengeMedia/dgop"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Handle git packages
|
||||||
|
if [ "$IS_GIT_PACKAGE" = true ] && [ -n "$GIT_REPO" ]; then
|
||||||
|
info "Detected git package: $PACKAGE_NAME"
|
||||||
|
|
||||||
|
# Determine source directory name
|
||||||
|
if [ -z "$SOURCE_DIR" ]; then
|
||||||
|
# Default: use package name without -git suffix + -source or -repo
|
||||||
|
BASE_NAME=$(echo "$PACKAGE_NAME" | sed 's/-git$//')
|
||||||
|
if [ -d "${BASE_NAME}-source" ] 2>/dev/null; then
|
||||||
|
SOURCE_DIR="${BASE_NAME}-source"
|
||||||
|
elif [ -d "${BASE_NAME}-repo" ] 2>/dev/null; then
|
||||||
|
SOURCE_DIR="${BASE_NAME}-repo"
|
||||||
|
elif [ -d "$BASE_NAME" ] 2>/dev/null; then
|
||||||
|
SOURCE_DIR="$BASE_NAME"
|
||||||
|
else
|
||||||
|
SOURCE_DIR="${BASE_NAME}-source"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Always clone fresh source to get latest commit info
|
||||||
|
info "Cloning $GIT_REPO from GitHub (getting latest commit info)..."
|
||||||
|
TEMP_CLONE=$(mktemp -d)
|
||||||
|
if git clone "https://github.com/$GIT_REPO.git" "$TEMP_CLONE"; then
|
||||||
|
# Get git commit info from fresh clone
|
||||||
|
GIT_COMMIT_HASH=$(cd "$TEMP_CLONE" && git rev-parse --short HEAD)
|
||||||
|
GIT_COMMIT_COUNT=$(cd "$TEMP_CLONE" && git rev-list --count HEAD)
|
||||||
|
|
||||||
|
# Get upstream version from latest git tag (e.g., 0.2.1)
|
||||||
|
# Sort all tags by version and get the latest one (not just the one reachable from HEAD)
|
||||||
|
UPSTREAM_VERSION=$(cd "$TEMP_CLONE" && git tag -l "v*" | sed 's/^v//' | sort -V | tail -1)
|
||||||
|
if [ -z "$UPSTREAM_VERSION" ]; then
|
||||||
|
# Fallback: try without v prefix
|
||||||
|
UPSTREAM_VERSION=$(cd "$TEMP_CLONE" && git tag -l | grep -E '^[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -1)
|
||||||
|
fi
|
||||||
|
if [ -z "$UPSTREAM_VERSION" ]; then
|
||||||
|
# Last resort: use git describe
|
||||||
|
UPSTREAM_VERSION=$(cd "$TEMP_CLONE" && git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.0.1")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify we got valid commit info
|
||||||
|
if [ -z "$GIT_COMMIT_COUNT" ] || [ "$GIT_COMMIT_COUNT" = "0" ]; then
|
||||||
|
error "Failed to get commit count from $GIT_REPO"
|
||||||
|
rm -rf "$TEMP_CLONE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$GIT_COMMIT_HASH" ]; then
|
||||||
|
error "Failed to get commit hash from $GIT_REPO"
|
||||||
|
rm -rf "$TEMP_CLONE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "Got commit info: $GIT_COMMIT_COUNT ($GIT_COMMIT_HASH), upstream: $UPSTREAM_VERSION"
|
||||||
|
|
||||||
|
# Update changelog with git commit info
|
||||||
|
info "Updating changelog with git commit info..."
|
||||||
|
# Format: 0.2.1+git705.fdbb86appa1
|
||||||
|
# Check if we're rebuilding the same commit (increment PPA number if so)
|
||||||
|
BASE_VERSION="${UPSTREAM_VERSION}+git${GIT_COMMIT_COUNT}.${GIT_COMMIT_HASH}"
|
||||||
|
CURRENT_VERSION=$(dpkg-parsechangelog -S Version 2>/dev/null || echo "")
|
||||||
|
PPA_NUM=1
|
||||||
|
|
||||||
|
# If current version matches the base version, increment PPA number
|
||||||
|
# Escape special regex characters in BASE_VERSION for pattern matching
|
||||||
|
ESCAPED_BASE=$(echo "$BASE_VERSION" | sed 's/\./\\./g' | sed 's/+/\\+/g')
|
||||||
|
if [[ "$CURRENT_VERSION" =~ ^${ESCAPED_BASE}ppa([0-9]+)$ ]]; then
|
||||||
|
PPA_NUM=$((BASH_REMATCH[1] + 1))
|
||||||
|
info "Detected rebuild of same commit (current: $CURRENT_VERSION), incrementing PPA number to $PPA_NUM"
|
||||||
|
else
|
||||||
|
info "New commit or first build, using PPA number $PPA_NUM"
|
||||||
|
fi
|
||||||
|
|
||||||
|
NEW_VERSION="${BASE_VERSION}ppa${PPA_NUM}"
|
||||||
|
|
||||||
|
# Use sed to update changelog (non-interactive, faster)
|
||||||
|
# Get current changelog content - find the next package header line (starts with package name)
|
||||||
|
# Skip the first entry entirely by finding the second occurrence of the package name at start of line
|
||||||
|
OLD_ENTRY_START=$(grep -n "^${SOURCE_NAME} (" debian/changelog | sed -n '2p' | cut -d: -f1)
|
||||||
|
if [ -n "$OLD_ENTRY_START" ]; then
|
||||||
|
# Found second entry, use everything from there
|
||||||
|
CHANGELOG_CONTENT=$(tail -n +$OLD_ENTRY_START debian/changelog)
|
||||||
|
else
|
||||||
|
# No second entry found, changelog will only have new entry
|
||||||
|
CHANGELOG_CONTENT=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create new changelog entry with proper format
|
||||||
|
CHANGELOG_ENTRY="${SOURCE_NAME} (${NEW_VERSION}) ${UBUNTU_SERIES}; urgency=medium
|
||||||
|
|
||||||
|
* Git snapshot (commit ${GIT_COMMIT_COUNT}: ${GIT_COMMIT_HASH})
|
||||||
|
|
||||||
|
-- Avenge Media <AvengeMedia.US@gmail.com> $(date -R)"
|
||||||
|
|
||||||
|
# Write new changelog (new entry, blank line, then old entries)
|
||||||
|
echo "$CHANGELOG_ENTRY" > debian/changelog
|
||||||
|
if [ -n "$CHANGELOG_CONTENT" ]; then
|
||||||
|
echo "" >> debian/changelog
|
||||||
|
echo "$CHANGELOG_CONTENT" >> debian/changelog
|
||||||
|
fi
|
||||||
|
success "Version updated to $NEW_VERSION"
|
||||||
|
|
||||||
|
# Now clone to source directory (without .git for inclusion in package)
|
||||||
|
rm -rf "$SOURCE_DIR"
|
||||||
|
cp -r "$TEMP_CLONE" "$SOURCE_DIR"
|
||||||
|
rm -rf "$SOURCE_DIR/.git"
|
||||||
|
rm -rf "$TEMP_CLONE"
|
||||||
|
|
||||||
|
# Vendor Rust dependencies for packages that need it
|
||||||
|
if false; then
|
||||||
|
# No current packages need Rust vendoring
|
||||||
|
if [ -f "$SOURCE_DIR/Cargo.toml" ]; then
|
||||||
|
info "Vendoring Rust dependencies (Launchpad has no internet access)..."
|
||||||
|
cd "$SOURCE_DIR"
|
||||||
|
|
||||||
|
# Clean up any existing vendor directory and .orig files
|
||||||
|
# (prevents cargo from including .orig files in checksums)
|
||||||
|
rm -rf vendor .cargo
|
||||||
|
find . -type f -name "*.orig" -exec rm -f {} + || true
|
||||||
|
|
||||||
|
# Download all dependencies (crates.io + git repos) to vendor/
|
||||||
|
# cargo vendor outputs the config to stderr, capture it
|
||||||
|
mkdir -p .cargo
|
||||||
|
cargo vendor 2>&1 | awk '
|
||||||
|
/^\[source\.crates-io\]/ { printing=1 }
|
||||||
|
printing { print }
|
||||||
|
/^directory = "vendor"$/ { exit }
|
||||||
|
' > .cargo/config.toml
|
||||||
|
|
||||||
|
# Verify vendor directory was created
|
||||||
|
if [ ! -d "vendor" ]; then
|
||||||
|
error "Failed to vendor dependencies"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify config was created
|
||||||
|
if [ ! -s .cargo/config.toml ]; then
|
||||||
|
error "Failed to create cargo config"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CRITICAL: Remove ALL .orig files from vendor directory
|
||||||
|
# These break cargo checksums when dh_clean tries to use them
|
||||||
|
info "Cleaning .orig files from vendor directory..."
|
||||||
|
find vendor -type f -name "*.orig" -exec rm -fv {} + || true
|
||||||
|
find vendor -type f -name "*.rej" -exec rm -fv {} + || true
|
||||||
|
|
||||||
|
# Verify no .orig files remain
|
||||||
|
ORIG_COUNT=$(find vendor -type f -name "*.orig" | wc -l)
|
||||||
|
if [ "$ORIG_COUNT" -gt 0 ]; then
|
||||||
|
warn "Found $ORIG_COUNT .orig files still in vendor directory"
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "Rust dependencies vendored (including git dependencies)"
|
||||||
|
cd "$PACKAGE_DIR"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Download pre-built binary for dms-git
|
||||||
|
# dms-git uses latest release binary with git master QML files
|
||||||
|
if [ "$PACKAGE_NAME" = "dms-git" ]; then
|
||||||
|
info "Downloading latest release binary for dms-git..."
|
||||||
|
if [ ! -f "dms-distropkg-amd64.gz" ]; then
|
||||||
|
if wget -O dms-distropkg-amd64.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-amd64.gz"; then
|
||||||
|
success "Latest release binary downloaded"
|
||||||
|
else
|
||||||
|
error "Failed to download dms-distropkg-amd64.gz"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
info "Release binary already downloaded"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "Source prepared for packaging"
|
||||||
|
else
|
||||||
|
error "Failed to clone $GIT_REPO"
|
||||||
|
rm -rf "$TEMP_CLONE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Handle stable packages - get latest tag
|
||||||
|
elif [ -n "$GIT_REPO" ]; then
|
||||||
|
info "Detected stable package: $PACKAGE_NAME"
|
||||||
|
info "Fetching latest tag from $GIT_REPO..."
|
||||||
|
|
||||||
|
LATEST_TAG=$(get_latest_tag "$GIT_REPO")
|
||||||
|
if [ -n "$LATEST_TAG" ]; then
|
||||||
|
# Check source format - native packages can't use dashes
|
||||||
|
SOURCE_FORMAT=$(cat debian/source/format 2>/dev/null | head -1 || echo "3.0 (quilt)")
|
||||||
|
|
||||||
|
# Get current version to check if we need to increment PPA number
|
||||||
|
CURRENT_VERSION=$(dpkg-parsechangelog -S Version 2>/dev/null || echo "")
|
||||||
|
PPA_NUM=1
|
||||||
|
|
||||||
|
if [[ "$SOURCE_FORMAT" == *"native"* ]]; then
|
||||||
|
# Native format: 0.2.1ppa1 (no dash, no revision)
|
||||||
|
BASE_VERSION="${LATEST_TAG}"
|
||||||
|
# Check if we're rebuilding the same version (increment PPA number if so)
|
||||||
|
if [[ "$CURRENT_VERSION" =~ ^${LATEST_TAG}ppa([0-9]+)$ ]]; then
|
||||||
|
PPA_NUM=$((BASH_REMATCH[1] + 1))
|
||||||
|
info "Detected rebuild of same version (current: $CURRENT_VERSION), incrementing PPA number to $PPA_NUM"
|
||||||
|
else
|
||||||
|
info "New version or first build, using PPA number $PPA_NUM"
|
||||||
|
fi
|
||||||
|
NEW_VERSION="${BASE_VERSION}ppa${PPA_NUM}"
|
||||||
|
else
|
||||||
|
# Quilt format: 0.2.1-1ppa1 (with revision)
|
||||||
|
BASE_VERSION="${LATEST_TAG}-1"
|
||||||
|
# Check if we're rebuilding the same version (increment PPA number if so)
|
||||||
|
ESCAPED_BASE=$(echo "$BASE_VERSION" | sed 's/\./\\./g' | sed 's/-/\\-/g')
|
||||||
|
if [[ "$CURRENT_VERSION" =~ ^${ESCAPED_BASE}ppa([0-9]+)$ ]]; then
|
||||||
|
PPA_NUM=$((BASH_REMATCH[1] + 1))
|
||||||
|
info "Detected rebuild of same version (current: $CURRENT_VERSION), incrementing PPA number to $PPA_NUM"
|
||||||
|
else
|
||||||
|
info "New version or first build, using PPA number $PPA_NUM"
|
||||||
|
fi
|
||||||
|
NEW_VERSION="${BASE_VERSION}ppa${PPA_NUM}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if version needs updating (either new version or PPA number changed)
|
||||||
|
if [ "$CURRENT_VERSION" != "$NEW_VERSION" ]; then
|
||||||
|
if [ "$PPA_NUM" -gt 1 ]; then
|
||||||
|
info "Updating changelog for rebuild (PPA number incremented to $PPA_NUM)"
|
||||||
|
else
|
||||||
|
info "Updating changelog to latest tag: $LATEST_TAG"
|
||||||
|
fi
|
||||||
|
# Use sed to update changelog (non-interactive)
|
||||||
|
# Get current changelog content - find the next package header line
|
||||||
|
OLD_ENTRY_START=$(grep -n "^${SOURCE_NAME} (" debian/changelog | sed -n '2p' | cut -d: -f1)
|
||||||
|
if [ -n "$OLD_ENTRY_START" ]; then
|
||||||
|
CHANGELOG_CONTENT=$(tail -n +$OLD_ENTRY_START debian/changelog)
|
||||||
|
else
|
||||||
|
CHANGELOG_CONTENT=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create appropriate changelog message
|
||||||
|
if [ "$PPA_NUM" -gt 1 ]; then
|
||||||
|
CHANGELOG_MSG="Rebuild for packaging fixes (ppa${PPA_NUM})"
|
||||||
|
else
|
||||||
|
CHANGELOG_MSG="Upstream release ${LATEST_TAG}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
CHANGELOG_ENTRY="${SOURCE_NAME} (${NEW_VERSION}) ${UBUNTU_SERIES}; urgency=medium
|
||||||
|
|
||||||
|
* ${CHANGELOG_MSG}
|
||||||
|
|
||||||
|
-- Avenge Media <AvengeMedia.US@gmail.com> $(date -R)"
|
||||||
|
echo "$CHANGELOG_ENTRY" > debian/changelog
|
||||||
|
if [ -n "$CHANGELOG_CONTENT" ]; then
|
||||||
|
echo "" >> debian/changelog
|
||||||
|
echo "$CHANGELOG_CONTENT" >> debian/changelog
|
||||||
|
fi
|
||||||
|
success "Version updated to $NEW_VERSION"
|
||||||
|
else
|
||||||
|
info "Version already at latest tag: $LATEST_TAG"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not determine latest tag for $GIT_REPO, using existing version"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle packages that need pre-built binaries downloaded
|
||||||
|
cd "$PACKAGE_DIR"
|
||||||
|
case "$PACKAGE_NAME" in
|
||||||
|
danksearch)
|
||||||
|
info "Downloading pre-built binaries for danksearch..."
|
||||||
|
# Get version from changelog (remove ppa suffix for both quilt and native formats)
|
||||||
|
# Native: 0.5.2ppa1 -> 0.5.2, Quilt: 0.5.2-1ppa1 -> 0.5.2
|
||||||
|
VERSION=$(dpkg-parsechangelog -S Version | sed 's/-[^-]*$//' | sed 's/ppa[0-9]*$//')
|
||||||
|
|
||||||
|
# Download both amd64 and arm64 binaries (will be included in source package)
|
||||||
|
# Launchpad can't download during build, so we include both architectures
|
||||||
|
if [ ! -f "dsearch-amd64" ]; then
|
||||||
|
info "Downloading dsearch binary for amd64..."
|
||||||
|
if wget -O dsearch-amd64.gz "https://github.com/AvengeMedia/danksearch/releases/download/v${VERSION}/dsearch-linux-amd64.gz"; then
|
||||||
|
gunzip dsearch-amd64.gz
|
||||||
|
chmod +x dsearch-amd64
|
||||||
|
success "amd64 binary downloaded"
|
||||||
|
else
|
||||||
|
error "Failed to download dsearch-amd64.gz"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "dsearch-arm64" ]; then
|
||||||
|
info "Downloading dsearch binary for arm64..."
|
||||||
|
if wget -O dsearch-arm64.gz "https://github.com/AvengeMedia/danksearch/releases/download/v${VERSION}/dsearch-linux-arm64.gz"; then
|
||||||
|
gunzip dsearch-arm64.gz
|
||||||
|
chmod +x dsearch-arm64
|
||||||
|
success "arm64 binary downloaded"
|
||||||
|
else
|
||||||
|
error "Failed to download dsearch-arm64.gz"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
dgop)
|
||||||
|
# dgop binary should already be committed in the repo
|
||||||
|
if [ ! -f "dgop" ]; then
|
||||||
|
warn "dgop binary not found - should be committed to repo"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cd - > /dev/null
|
||||||
|
|
||||||
|
# Check if this version already exists on PPA (only in CI environment)
|
||||||
|
if command -v rmadison >/dev/null 2>&1; then
|
||||||
|
info "Checking if version already exists on PPA..."
|
||||||
|
PPA_VERSION_CHECK=$(rmadison -u ppa:avengemedia/dms "$PACKAGE_NAME" 2>/dev/null | grep "$VERSION" || true)
|
||||||
|
if [ -n "$PPA_VERSION_CHECK" ]; then
|
||||||
|
warn "Version $VERSION already exists on PPA:"
|
||||||
|
echo "$PPA_VERSION_CHECK"
|
||||||
|
echo
|
||||||
|
warn "Skipping upload to avoid duplicate. If this is a rebuild, increment the ppa number."
|
||||||
|
cd "$PACKAGE_DIR"
|
||||||
|
# Still clean up extracted sources
|
||||||
|
case "$PACKAGE_NAME" in
|
||||||
|
dms-git)
|
||||||
|
rm -rf DankMaterialShell-*
|
||||||
|
success "Cleaned up DankMaterialShell-*/ directory"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build source package
|
||||||
|
info "Building source package..."
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Determine if we need to include orig tarball (-sa) or just debian changes (-sd)
|
||||||
|
# Check if .orig.tar.xz already exists in parent directory (previous build)
|
||||||
|
ORIG_TARBALL="${PACKAGE_NAME}_${VERSION%.ppa*}.orig.tar.xz"
|
||||||
|
if [ -f "../$ORIG_TARBALL" ]; then
|
||||||
|
info "Found existing orig tarball, using -sd (debian changes only)"
|
||||||
|
DEBUILD_SOURCE_FLAG="-sd"
|
||||||
|
else
|
||||||
|
info "No existing orig tarball found, using -sa (include original source)"
|
||||||
|
DEBUILD_SOURCE_FLAG="-sa"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use -S for source only, -sa/-sd for source inclusion
|
||||||
|
# -d skips dependency checking (we're building on Fedora, not Ubuntu)
|
||||||
|
# Pipe yes to automatically answer prompts (e.g., "continue anyway?")
|
||||||
|
if yes | DEBIAN_FRONTEND=noninteractive debuild -S $DEBUILD_SOURCE_FLAG -d; then
|
||||||
|
echo
|
||||||
|
success "Source package built successfully!"
|
||||||
|
|
||||||
|
# List generated files
|
||||||
|
info "Generated files in $(dirname "$PACKAGE_DIR"):"
|
||||||
|
ls -lh "$(dirname "$PACKAGE_DIR")"/${SOURCE_NAME}_${CHANGELOG_VERSION}* 2>/dev/null || true
|
||||||
|
|
||||||
|
# Show what to do next
|
||||||
|
echo
|
||||||
|
info "Next steps:"
|
||||||
|
echo " 1. Review the source package:"
|
||||||
|
echo " cd $(dirname "$PACKAGE_DIR")"
|
||||||
|
echo " ls -lh ${SOURCE_NAME}_${CHANGELOG_VERSION}*"
|
||||||
|
echo
|
||||||
|
echo " 2. Upload to PPA (stable):"
|
||||||
|
echo " dput ppa:avengemedia/dms ${SOURCE_NAME}_${CHANGELOG_VERSION}_source.changes"
|
||||||
|
echo
|
||||||
|
echo " 3. Or upload to PPA (nightly):"
|
||||||
|
echo " dput ppa:avengemedia/dms-git ${SOURCE_NAME}_${CHANGELOG_VERSION}_source.changes"
|
||||||
|
echo
|
||||||
|
echo " 4. Or use the upload script:"
|
||||||
|
echo " ./upload-ppa.sh $(dirname "$PACKAGE_DIR")/${SOURCE_NAME}_${CHANGELOG_VERSION}_source.changes dms"
|
||||||
|
|
||||||
|
else
|
||||||
|
error "Source package build failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Executable
+179
@@ -0,0 +1,179 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Generic PPA uploader for DMS packages
|
||||||
|
# Usage: ./upload-ppa.sh <changes-file> <ppa-name>
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# ./upload-ppa.sh ../dms_0.5.2ppa1_source.changes dms
|
||||||
|
# ./upload-ppa.sh ../dms_0.5.2+git705.fdbb86appa1_source.changes dms-git
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||||
|
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
error "Usage: $0 <changes-file> <ppa-name>"
|
||||||
|
echo
|
||||||
|
echo "Arguments:"
|
||||||
|
echo " changes-file : Path to .changes file (e.g., ../dms_0.5.2ppa1_source.changes)"
|
||||||
|
echo " ppa-name : PPA to upload to (dms or dms-git)"
|
||||||
|
echo
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 ../dms_0.5.2ppa1_source.changes dms"
|
||||||
|
echo " $0 ../dms_0.5.2+git705.fdbb86appa1_source.changes dms-git"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CHANGES_FILE="$1"
|
||||||
|
PPA_NAME="$2"
|
||||||
|
|
||||||
|
# Validate changes file
|
||||||
|
if [ ! -f "$CHANGES_FILE" ]; then
|
||||||
|
error "Changes file not found: $CHANGES_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! "$CHANGES_FILE" =~ \.changes$ ]]; then
|
||||||
|
error "File must be a .changes file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate PPA name
|
||||||
|
if [ "$PPA_NAME" != "dms" ] && [ "$PPA_NAME" != "dms-git" ] && [ "$PPA_NAME" != "danklinux" ]; then
|
||||||
|
error "PPA name must be 'dms', 'dms-git', or 'danklinux'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get absolute path
|
||||||
|
CHANGES_FILE=$(realpath "$CHANGES_FILE")
|
||||||
|
|
||||||
|
info "Uploading to PPA: ppa:avengemedia/$PPA_NAME"
|
||||||
|
info "Changes file: $CHANGES_FILE"
|
||||||
|
|
||||||
|
# Check if dput or lftp is installed
|
||||||
|
UPLOAD_METHOD=""
|
||||||
|
if command -v dput &> /dev/null; then
|
||||||
|
UPLOAD_METHOD="dput"
|
||||||
|
elif command -v lftp &> /dev/null; then
|
||||||
|
UPLOAD_METHOD="lftp"
|
||||||
|
warn "dput not found, using lftp as fallback"
|
||||||
|
else
|
||||||
|
error "Neither dput nor lftp found. Install one with:"
|
||||||
|
error " sudo dnf install dput-ng # Preferred but broken on Fedora"
|
||||||
|
error " sudo dnf install lftp # Alternative upload method"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if ~/.dput.cf exists
|
||||||
|
if [ ! -f "$HOME/.dput.cf" ]; then
|
||||||
|
error "~/.dput.cf not found!"
|
||||||
|
echo
|
||||||
|
info "Create it from template:"
|
||||||
|
echo " cp $(dirname "$0")/../dput.cf.template ~/.dput.cf"
|
||||||
|
echo
|
||||||
|
info "Or create it manually with:"
|
||||||
|
cat <<'EOF'
|
||||||
|
[ppa:avengemedia/dms]
|
||||||
|
fqdn = ppa.launchpad.net
|
||||||
|
method = ftp
|
||||||
|
incoming = ~avengemedia/ubuntu/dms/
|
||||||
|
login = anonymous
|
||||||
|
allow_unsigned_uploads = 0
|
||||||
|
|
||||||
|
[ppa:avengemedia/dms-git]
|
||||||
|
fqdn = ppa.launchpad.net
|
||||||
|
method = ftp
|
||||||
|
incoming = ~avengemedia/ubuntu/dms-git/
|
||||||
|
login = anonymous
|
||||||
|
allow_unsigned_uploads = 0
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if PPA is configured in dput.cf
|
||||||
|
if ! grep -q "^\[ppa:avengemedia/$PPA_NAME\]" "$HOME/.dput.cf"; then
|
||||||
|
error "PPA 'ppa:avengemedia/$PPA_NAME' not found in ~/.dput.cf"
|
||||||
|
echo
|
||||||
|
info "Add this to ~/.dput.cf:"
|
||||||
|
cat <<EOF
|
||||||
|
[ppa:avengemedia/$PPA_NAME]
|
||||||
|
fqdn = ppa.launchpad.net
|
||||||
|
method = ftp
|
||||||
|
incoming = ~avengemedia/ubuntu/$PPA_NAME/
|
||||||
|
login = anonymous
|
||||||
|
allow_unsigned_uploads = 0
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract package info from changes file
|
||||||
|
PACKAGE_NAME=$(grep "^Source:" "$CHANGES_FILE" | awk '{print $2}')
|
||||||
|
VERSION=$(grep "^Version:" "$CHANGES_FILE" | awk '{print $2}')
|
||||||
|
|
||||||
|
info "Package: $PACKAGE_NAME"
|
||||||
|
info "Version: $VERSION"
|
||||||
|
|
||||||
|
# Show files that will be uploaded
|
||||||
|
echo
|
||||||
|
info "Files to be uploaded:"
|
||||||
|
grep "^ [a-f0-9]" "$CHANGES_FILE" | awk '{print " - " $5}' || true
|
||||||
|
|
||||||
|
# Verify GPG signature
|
||||||
|
info "Verifying GPG signature..."
|
||||||
|
if gpg --verify "$CHANGES_FILE" 2>/dev/null; then
|
||||||
|
success "GPG signature valid"
|
||||||
|
else
|
||||||
|
error "GPG signature verification failed!"
|
||||||
|
error "The .changes file must be signed with your GPG key"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ask for confirmation
|
||||||
|
echo
|
||||||
|
warn "About to upload to: ppa:avengemedia/$PPA_NAME"
|
||||||
|
read -p "Continue? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
info "Upload cancelled"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Upload to PPA
|
||||||
|
info "Uploading to Launchpad..."
|
||||||
|
echo
|
||||||
|
|
||||||
|
if dput "ppa:avengemedia/$PPA_NAME" "$CHANGES_FILE"; then
|
||||||
|
echo
|
||||||
|
success "Upload successful!"
|
||||||
|
echo
|
||||||
|
info "Monitor build progress at:"
|
||||||
|
echo " https://launchpad.net/~avengemedia/+archive/ubuntu/$PPA_NAME/+packages"
|
||||||
|
echo
|
||||||
|
info "Builds typically take 5-30 minutes depending on:"
|
||||||
|
echo " - Build queue length"
|
||||||
|
echo " - Package complexity"
|
||||||
|
echo " - Number of target Ubuntu series"
|
||||||
|
echo
|
||||||
|
info "Once built, users can install with:"
|
||||||
|
echo " sudo add-apt-repository ppa:avengemedia/$PPA_NAME"
|
||||||
|
echo " sudo apt update"
|
||||||
|
echo " sudo apt install $PACKAGE_NAME"
|
||||||
|
|
||||||
|
else
|
||||||
|
error "Upload failed!"
|
||||||
|
echo
|
||||||
|
info "Common issues:"
|
||||||
|
echo " - GPG key not verified on Launchpad (check https://launchpad.net/~/+editpgpkeys)"
|
||||||
|
echo " - Version already uploaded (must increment version number)"
|
||||||
|
echo " - Network/firewall blocking FTP (try HTTPS method in dput.cf)"
|
||||||
|
echo " - Email in changelog doesn't match GPG key email"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -9,155 +9,167 @@ Singleton {
|
|||||||
property var currentPopoutsByScreen: ({})
|
property var currentPopoutsByScreen: ({})
|
||||||
property var currentPopoutTriggers: ({})
|
property var currentPopoutTriggers: ({})
|
||||||
|
|
||||||
function showPopout(popout) {
|
signal popoutOpening
|
||||||
if (!popout || !popout.screen) return
|
signal popoutChanged
|
||||||
|
|
||||||
const screenName = popout.screen.name
|
function showPopout(popout) {
|
||||||
|
if (!popout || !popout.screen)
|
||||||
|
return;
|
||||||
|
popoutOpening();
|
||||||
|
|
||||||
|
const screenName = popout.screen.name;
|
||||||
|
|
||||||
for (const otherScreenName in currentPopoutsByScreen) {
|
for (const otherScreenName in currentPopoutsByScreen) {
|
||||||
const otherPopout = currentPopoutsByScreen[otherScreenName]
|
const otherPopout = currentPopoutsByScreen[otherScreenName];
|
||||||
if (!otherPopout || otherPopout === popout) continue
|
if (!otherPopout || otherPopout === popout)
|
||||||
|
continue;
|
||||||
if (otherPopout.dashVisible !== undefined) {
|
if (otherPopout.dashVisible !== undefined) {
|
||||||
otherPopout.dashVisible = false
|
otherPopout.dashVisible = false;
|
||||||
} else if (otherPopout.notificationHistoryVisible !== undefined) {
|
} else if (otherPopout.notificationHistoryVisible !== undefined) {
|
||||||
otherPopout.notificationHistoryVisible = false
|
otherPopout.notificationHistoryVisible = false;
|
||||||
} else {
|
} else {
|
||||||
otherPopout.close()
|
otherPopout.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPopoutsByScreen[screenName] = popout
|
currentPopoutsByScreen[screenName] = popout;
|
||||||
ModalManager.closeAllModalsExcept(null)
|
popoutChanged();
|
||||||
TrayMenuManager.closeAllMenus()
|
ModalManager.closeAllModalsExcept(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hidePopout(popout) {
|
function hidePopout(popout) {
|
||||||
if (!popout || !popout.screen) return
|
if (!popout || !popout.screen)
|
||||||
|
return;
|
||||||
const screenName = popout.screen.name
|
const screenName = popout.screen.name;
|
||||||
if (currentPopoutsByScreen[screenName] === popout) {
|
if (currentPopoutsByScreen[screenName] === popout) {
|
||||||
currentPopoutsByScreen[screenName] = null
|
currentPopoutsByScreen[screenName] = null;
|
||||||
currentPopoutTriggers[screenName] = null
|
currentPopoutTriggers[screenName] = null;
|
||||||
|
popoutChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeAllPopouts() {
|
function closeAllPopouts() {
|
||||||
for (const screenName in currentPopoutsByScreen) {
|
for (const screenName in currentPopoutsByScreen) {
|
||||||
const popout = currentPopoutsByScreen[screenName]
|
const popout = currentPopoutsByScreen[screenName];
|
||||||
if (!popout) continue
|
if (!popout)
|
||||||
|
continue;
|
||||||
if (popout.dashVisible !== undefined) {
|
if (popout.dashVisible !== undefined) {
|
||||||
popout.dashVisible = false
|
popout.dashVisible = false;
|
||||||
} else if (popout.notificationHistoryVisible !== undefined) {
|
} else if (popout.notificationHistoryVisible !== undefined) {
|
||||||
popout.notificationHistoryVisible = false
|
popout.notificationHistoryVisible = false;
|
||||||
} else {
|
} else {
|
||||||
popout.close()
|
popout.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentPopoutsByScreen = {}
|
currentPopoutsByScreen = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActivePopout(screen) {
|
function getActivePopout(screen) {
|
||||||
if (!screen) return null
|
if (!screen)
|
||||||
return currentPopoutsByScreen[screen.name] || null
|
return null;
|
||||||
|
return currentPopoutsByScreen[screen.name] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestPopout(popout, tabIndex, triggerSource) {
|
function requestPopout(popout, tabIndex, triggerSource) {
|
||||||
if (!popout || !popout.screen) return
|
if (!popout || !popout.screen)
|
||||||
|
return;
|
||||||
|
const screenName = popout.screen.name;
|
||||||
|
const currentPopout = currentPopoutsByScreen[screenName];
|
||||||
|
const triggerId = triggerSource !== undefined ? triggerSource : tabIndex;
|
||||||
|
|
||||||
const screenName = popout.screen.name
|
const willOpen = !(currentPopout === popout && popout.shouldBeVisible && triggerId !== undefined && currentPopoutTriggers[screenName] === triggerId);
|
||||||
const currentPopout = currentPopoutsByScreen[screenName]
|
if (willOpen) {
|
||||||
const triggerId = triggerSource !== undefined ? triggerSource : tabIndex
|
popoutOpening();
|
||||||
|
}
|
||||||
|
|
||||||
let justClosedSamePopout = false
|
let justClosedSamePopout = false;
|
||||||
for (const otherScreenName in currentPopoutsByScreen) {
|
for (const otherScreenName in currentPopoutsByScreen) {
|
||||||
if (otherScreenName === screenName) continue
|
if (otherScreenName === screenName)
|
||||||
const otherPopout = currentPopoutsByScreen[otherScreenName]
|
continue;
|
||||||
if (!otherPopout) continue
|
const otherPopout = currentPopoutsByScreen[otherScreenName];
|
||||||
|
if (!otherPopout)
|
||||||
|
continue;
|
||||||
if (otherPopout === popout) {
|
if (otherPopout === popout) {
|
||||||
justClosedSamePopout = true
|
justClosedSamePopout = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otherPopout.dashVisible !== undefined) {
|
if (otherPopout.dashVisible !== undefined) {
|
||||||
otherPopout.dashVisible = false
|
otherPopout.dashVisible = false;
|
||||||
} else if (otherPopout.notificationHistoryVisible !== undefined) {
|
} else if (otherPopout.notificationHistoryVisible !== undefined) {
|
||||||
otherPopout.notificationHistoryVisible = false
|
otherPopout.notificationHistoryVisible = false;
|
||||||
} else {
|
} else {
|
||||||
otherPopout.close()
|
otherPopout.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPopout && currentPopout !== popout) {
|
if (currentPopout && currentPopout !== popout) {
|
||||||
if (currentPopout.dashVisible !== undefined) {
|
if (currentPopout.dashVisible !== undefined) {
|
||||||
currentPopout.dashVisible = false
|
currentPopout.dashVisible = false;
|
||||||
} else if (currentPopout.notificationHistoryVisible !== undefined) {
|
} else if (currentPopout.notificationHistoryVisible !== undefined) {
|
||||||
currentPopout.notificationHistoryVisible = false
|
currentPopout.notificationHistoryVisible = false;
|
||||||
} else {
|
} else {
|
||||||
currentPopout.close()
|
currentPopout.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPopout === popout && popout.shouldBeVisible) {
|
if (currentPopout === popout && popout.shouldBeVisible) {
|
||||||
if (triggerId !== undefined && currentPopoutTriggers[screenName] === triggerId) {
|
if (triggerId !== undefined && currentPopoutTriggers[screenName] === triggerId) {
|
||||||
if (popout.dashVisible !== undefined) {
|
if (popout.dashVisible !== undefined) {
|
||||||
popout.dashVisible = false
|
popout.dashVisible = false;
|
||||||
} else if (popout.notificationHistoryVisible !== undefined) {
|
} else if (popout.notificationHistoryVisible !== undefined) {
|
||||||
popout.notificationHistoryVisible = false
|
popout.notificationHistoryVisible = false;
|
||||||
} else {
|
} else {
|
||||||
popout.close()
|
popout.close();
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (triggerId === undefined) {
|
if (triggerId === undefined) {
|
||||||
if (popout.dashVisible !== undefined) {
|
if (popout.dashVisible !== undefined) {
|
||||||
popout.dashVisible = false
|
popout.dashVisible = false;
|
||||||
} else if (popout.notificationHistoryVisible !== undefined) {
|
} else if (popout.notificationHistoryVisible !== undefined) {
|
||||||
popout.notificationHistoryVisible = false
|
popout.notificationHistoryVisible = false;
|
||||||
} else {
|
} else {
|
||||||
popout.close()
|
popout.close();
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tabIndex !== undefined && popout.currentTabIndex !== undefined) {
|
if (tabIndex !== undefined && popout.currentTabIndex !== undefined) {
|
||||||
popout.currentTabIndex = tabIndex
|
popout.currentTabIndex = tabIndex;
|
||||||
}
|
}
|
||||||
currentPopoutTriggers[screenName] = triggerId
|
currentPopoutTriggers[screenName] = triggerId;
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPopoutTriggers[screenName] = triggerId
|
currentPopoutTriggers[screenName] = triggerId;
|
||||||
currentPopoutsByScreen[screenName] = popout
|
currentPopoutsByScreen[screenName] = popout;
|
||||||
|
popoutChanged();
|
||||||
|
|
||||||
if (tabIndex !== undefined && popout.currentTabIndex !== undefined) {
|
if (tabIndex !== undefined && popout.currentTabIndex !== undefined) {
|
||||||
popout.currentTabIndex = tabIndex
|
popout.currentTabIndex = tabIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPopout !== popout) {
|
if (currentPopout !== popout) {
|
||||||
ModalManager.closeAllModalsExcept(null)
|
ModalManager.closeAllModalsExcept(null);
|
||||||
}
|
}
|
||||||
TrayMenuManager.closeAllMenus()
|
|
||||||
|
|
||||||
if (justClosedSamePopout) {
|
if (justClosedSamePopout) {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (popout.dashVisible !== undefined) {
|
if (popout.dashVisible !== undefined) {
|
||||||
popout.dashVisible = true
|
popout.dashVisible = true;
|
||||||
} else if (popout.notificationHistoryVisible !== undefined) {
|
} else if (popout.notificationHistoryVisible !== undefined) {
|
||||||
popout.notificationHistoryVisible = true
|
popout.notificationHistoryVisible = true;
|
||||||
} else {
|
} else {
|
||||||
popout.open()
|
popout.open();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
if (popout.dashVisible !== undefined) {
|
if (popout.dashVisible !== undefined) {
|
||||||
popout.dashVisible = true
|
popout.dashVisible = true;
|
||||||
} else if (popout.notificationHistoryVisible !== undefined) {
|
} else if (popout.notificationHistoryVisible !== undefined) {
|
||||||
popout.notificationHistoryVisible = true
|
popout.notificationHistoryVisible = true;
|
||||||
} else {
|
} else {
|
||||||
popout.open()
|
popout.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,13 +67,21 @@ Singleton {
|
|||||||
})
|
})
|
||||||
|
|
||||||
out.streamFinished.connect(function() {
|
out.streamFinished.connect(function() {
|
||||||
capturedOut = out.text || ""
|
try {
|
||||||
|
capturedOut = out.text || ""
|
||||||
|
} catch (e) {
|
||||||
|
capturedOut = ""
|
||||||
|
}
|
||||||
outSeen = true
|
outSeen = true
|
||||||
maybeComplete()
|
maybeComplete()
|
||||||
})
|
})
|
||||||
|
|
||||||
err.streamFinished.connect(function() {
|
err.streamFinished.connect(function() {
|
||||||
capturedErr = err.text || ""
|
try {
|
||||||
|
capturedErr = err.text || ""
|
||||||
|
} catch (e) {
|
||||||
|
capturedErr = ""
|
||||||
|
}
|
||||||
errSeen = true
|
errSeen = true
|
||||||
maybeComplete()
|
maybeComplete()
|
||||||
})
|
})
|
||||||
@@ -88,8 +96,14 @@ Singleton {
|
|||||||
function maybeComplete() {
|
function maybeComplete() {
|
||||||
if (!exitSeen || !outSeen || !errSeen) return
|
if (!exitSeen || !outSeen || !errSeen) return
|
||||||
timeoutTimer.stop()
|
timeoutTimer.stop()
|
||||||
if (typeof entry.callback === "function") {
|
if (entry && entry.callback && typeof entry.callback === "function") {
|
||||||
try { entry.callback(capturedOut, exitCodeValue) } catch (e) { console.warn("runCommand callback error:", e) }
|
try {
|
||||||
|
const safeOutput = capturedOut !== null && capturedOut !== undefined ? capturedOut : ""
|
||||||
|
const safeExitCode = exitCodeValue !== null && exitCodeValue !== undefined ? exitCodeValue : -1
|
||||||
|
entry.callback(safeOutput, safeExitCode)
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("runCommand callback error for command:", entry.command, "Error:", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try { proc.destroy() } catch (_) {}
|
try { proc.destroy() } catch (_) {}
|
||||||
try { timeoutTimer.destroy() } catch (_) {}
|
try { timeoutTimer.destroy() } catch (_) {}
|
||||||
|
|||||||
+661
-379
File diff suppressed because it is too large
Load Diff
@@ -2,16 +2,16 @@
|
|||||||
// Separated from Theme.qml to keep that file clean
|
// Separated from Theme.qml to keep that file clean
|
||||||
|
|
||||||
const CatppuccinMocha = {
|
const CatppuccinMocha = {
|
||||||
surface: "#313244",
|
surface: "#181825",
|
||||||
surfaceText: "#cdd6f4",
|
surfaceText: "#cdd6f4",
|
||||||
surfaceVariant: "#313244",
|
surfaceVariant: "#1e1e2e",
|
||||||
surfaceVariantText: "#a6adc8",
|
surfaceVariantText: "#a6adc8",
|
||||||
background: "#1e1e2e",
|
background: "#181825",
|
||||||
backgroundText: "#cdd6f4",
|
backgroundText: "#cdd6f4",
|
||||||
outline: "#6c7086",
|
outline: "#6c7086",
|
||||||
surfaceContainer: "#45475a",
|
surfaceContainer: "#1e1e2e",
|
||||||
surfaceContainerHigh: "#585b70",
|
surfaceContainerHigh: "#313244",
|
||||||
surfaceContainerHighest: "#6c7086"
|
surfaceContainerHighest: "#45475a"
|
||||||
}
|
}
|
||||||
|
|
||||||
const CatppuccinLatte = {
|
const CatppuccinLatte = {
|
||||||
|
|||||||
+424
-407
File diff suppressed because it is too large
Load Diff
@@ -6,26 +6,30 @@ import QtQuick
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var activeTrayBars: ({})
|
property var activeTrayMenus: ({})
|
||||||
|
|
||||||
function register(screenName, trayBar) {
|
function registerMenu(screenName, menu) {
|
||||||
if (!screenName || !trayBar) return
|
if (!screenName || !menu) return
|
||||||
activeTrayBars[screenName] = trayBar
|
const newMenus = Object.assign({}, activeTrayMenus)
|
||||||
|
newMenus[screenName] = menu
|
||||||
|
activeTrayMenus = newMenus
|
||||||
}
|
}
|
||||||
|
|
||||||
function unregister(screenName) {
|
function unregisterMenu(screenName) {
|
||||||
if (!screenName) return
|
if (!screenName) return
|
||||||
delete activeTrayBars[screenName]
|
const newMenus = Object.assign({}, activeTrayMenus)
|
||||||
|
delete newMenus[screenName]
|
||||||
|
activeTrayMenus = newMenus
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeAllMenus() {
|
function closeAllMenus() {
|
||||||
for (const screenName in activeTrayBars) {
|
for (const screenName in activeTrayMenus) {
|
||||||
const trayBar = activeTrayBars[screenName]
|
const menu = activeTrayMenus[screenName]
|
||||||
if (!trayBar) continue
|
if (!menu) continue
|
||||||
|
if (typeof menu.close === "function") {
|
||||||
trayBar.menuOpen = false
|
menu.close()
|
||||||
if (trayBar.currentTrayMenu) {
|
} else if (menu.showMenu !== undefined) {
|
||||||
trayBar.currentTrayMenu.showMenu = false
|
menu.showMenu = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ var SPEC = {
|
|||||||
runUserMatugenTemplates: { def: true, onChange: "regenSystemThemes" },
|
runUserMatugenTemplates: { def: true, onChange: "regenSystemThemes" },
|
||||||
matugenTargetMonitor: { def: "", onChange: "regenSystemThemes" },
|
matugenTargetMonitor: { def: "", onChange: "regenSystemThemes" },
|
||||||
|
|
||||||
dankBarTransparency: { def: 1.0, coerce: percentToUnit, migrate: ["topBarTransparency"] },
|
|
||||||
dankBarWidgetTransparency: { def: 1.0, coerce: percentToUnit, migrate: ["topBarWidgetTransparency"] },
|
|
||||||
popupTransparency: { def: 1.0, coerce: percentToUnit },
|
popupTransparency: { def: 1.0, coerce: percentToUnit },
|
||||||
dockTransparency: { def: 1.0, coerce: percentToUnit },
|
dockTransparency: { def: 1.0, coerce: percentToUnit },
|
||||||
|
|
||||||
widgetBackgroundColor: { def: "sch" },
|
widgetBackgroundColor: { def: "sch" },
|
||||||
|
widgetColorMode: { def: "default" },
|
||||||
cornerRadius: { def: 12, onChange: "updateNiriLayout" },
|
cornerRadius: { def: 12, onChange: "updateNiriLayout" },
|
||||||
|
|
||||||
use24HourClock: { def: true },
|
use24HourClock: { def: true },
|
||||||
@@ -88,15 +87,11 @@ var SPEC = {
|
|||||||
lockDateFormat: { def: "" },
|
lockDateFormat: { def: "" },
|
||||||
mediaSize: { def: 1 },
|
mediaSize: { def: 1 },
|
||||||
|
|
||||||
dankBarLeftWidgets: { def: ["launcherButton", "workspaceSwitcher", "focusedWindow"], migrate: ["topBarLeftWidgets"] },
|
|
||||||
dankBarCenterWidgets: { def: ["music", "clock", "weather"], migrate: ["topBarCenterWidgets"] },
|
|
||||||
dankBarRightWidgets: { def: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"], migrate: ["topBarRightWidgets"] },
|
|
||||||
dankBarWidgetOrder: { def: [] },
|
|
||||||
|
|
||||||
appLauncherViewMode: { def: "list" },
|
appLauncherViewMode: { def: "list" },
|
||||||
spotlightModalViewMode: { def: "list" },
|
spotlightModalViewMode: { def: "list" },
|
||||||
sortAppsAlphabetically: { def: false },
|
sortAppsAlphabetically: { def: false },
|
||||||
appLauncherGridColumns: { def: 4 },
|
appLauncherGridColumns: { def: 4 },
|
||||||
|
spotlightCloseNiriOverview: { def: true },
|
||||||
|
|
||||||
weatherLocation: { def: "New York, NY" },
|
weatherLocation: { def: "New York, NY" },
|
||||||
weatherCoordinates: { def: "40.7128,-74.0060" },
|
weatherCoordinates: { def: "40.7128,-74.0060" },
|
||||||
@@ -125,7 +120,6 @@ var SPEC = {
|
|||||||
monoFontFamily: { def: "Fira Code" },
|
monoFontFamily: { def: "Fira Code" },
|
||||||
fontWeight: { def: 400 },
|
fontWeight: { def: 400 },
|
||||||
fontScale: { def: 1.0 },
|
fontScale: { def: 1.0 },
|
||||||
dankBarFontScale: { def: 1.0 },
|
|
||||||
|
|
||||||
notepadUseMonospace: { def: true },
|
notepadUseMonospace: { def: true },
|
||||||
notepadFontFamily: { def: "" },
|
notepadFontFamily: { def: "" },
|
||||||
@@ -175,31 +169,9 @@ var SPEC = {
|
|||||||
dockIndicatorStyle: { def: "circle" },
|
dockIndicatorStyle: { def: "circle" },
|
||||||
|
|
||||||
notificationOverlayEnabled: { def: false },
|
notificationOverlayEnabled: { def: false },
|
||||||
dankBarAutoHide: { def: false, migrate: ["topBarAutoHide"] },
|
|
||||||
dankBarAutoHideDelay: { def: 250 },
|
|
||||||
dankBarOpenOnOverview: { def: false, migrate: ["topBarOpenOnOverview"] },
|
|
||||||
dankBarVisible: { def: true, migrate: ["topBarVisible"] },
|
|
||||||
overviewRows: { def: 2, persist: false },
|
overviewRows: { def: 2, persist: false },
|
||||||
overviewColumns: { def: 5, persist: false },
|
overviewColumns: { def: 5, persist: false },
|
||||||
overviewScale: { def: 0.16, persist: false },
|
overviewScale: { def: 0.16, persist: false },
|
||||||
dankBarSpacing: { def: 4, migrate: ["topBarSpacing"], onChange: "updateNiriLayout" },
|
|
||||||
dankBarBottomGap: { def: 0, migrate: ["topBarBottomGap"] },
|
|
||||||
dankBarInnerPadding: { def: 4, migrate: ["topBarInnerPadding"] },
|
|
||||||
dankBarPosition: { def: 0, migrate: ["dankBarAtBottom", "topBarAtBottom"] },
|
|
||||||
dankBarIsVertical: { def: false, persist: false },
|
|
||||||
|
|
||||||
dankBarSquareCorners: { def: false, migrate: ["topBarSquareCorners"] },
|
|
||||||
dankBarNoBackground: { def: false, migrate: ["topBarNoBackground"] },
|
|
||||||
dankBarGothCornersEnabled: { def: false, migrate: ["topBarGothCornersEnabled"] },
|
|
||||||
dankBarGothCornerRadiusOverride: { def: false },
|
|
||||||
dankBarGothCornerRadiusValue: { def: 12 },
|
|
||||||
dankBarBorderEnabled: { def: false },
|
|
||||||
dankBarBorderColor: { def: "surfaceText" },
|
|
||||||
dankBarBorderOpacity: { def: 1.0 },
|
|
||||||
dankBarBorderThickness: { def: 1 },
|
|
||||||
|
|
||||||
popupGapsAuto: { def: true },
|
|
||||||
popupGapsManual: { def: 4 },
|
|
||||||
|
|
||||||
modalDarkenBackground: { def: true },
|
modalDarkenBackground: { def: true },
|
||||||
|
|
||||||
@@ -217,6 +189,7 @@ var SPEC = {
|
|||||||
osdAlwaysShowValue: { def: false },
|
osdAlwaysShowValue: { def: false },
|
||||||
osdPosition: { def: 5 },
|
osdPosition: { def: 5 },
|
||||||
osdVolumeEnabled: { def: true },
|
osdVolumeEnabled: { def: true },
|
||||||
|
osdMediaVolumeEnabled : { def: true },
|
||||||
osdBrightnessEnabled: { def: true },
|
osdBrightnessEnabled: { def: true },
|
||||||
osdIdleInhibitorEnabled: { def: true },
|
osdIdleInhibitorEnabled: { def: true },
|
||||||
osdMicMuteEnabled: { def: true },
|
osdMicMuteEnabled: { def: true },
|
||||||
@@ -240,7 +213,40 @@ var SPEC = {
|
|||||||
|
|
||||||
displayNameMode: { def: "system" },
|
displayNameMode: { def: "system" },
|
||||||
screenPreferences: { def: {} },
|
screenPreferences: { def: {} },
|
||||||
showOnLastDisplay: { def: {} }
|
showOnLastDisplay: { def: {} },
|
||||||
|
|
||||||
|
barConfigs: { def: [{
|
||||||
|
id: "default",
|
||||||
|
name: "Main Bar",
|
||||||
|
enabled: true,
|
||||||
|
position: 0,
|
||||||
|
screenPreferences: ["all"],
|
||||||
|
showOnLastDisplay: true,
|
||||||
|
leftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"],
|
||||||
|
centerWidgets: ["music", "clock", "weather"],
|
||||||
|
rightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
|
||||||
|
spacing: 4,
|
||||||
|
innerPadding: 4,
|
||||||
|
bottomGap: 0,
|
||||||
|
transparency: 1.0,
|
||||||
|
widgetTransparency: 1.0,
|
||||||
|
squareCorners: false,
|
||||||
|
noBackground: false,
|
||||||
|
gothCornersEnabled: false,
|
||||||
|
gothCornerRadiusOverride: false,
|
||||||
|
gothCornerRadiusValue: 12,
|
||||||
|
borderEnabled: false,
|
||||||
|
borderColor: "surfaceText",
|
||||||
|
borderOpacity: 1.0,
|
||||||
|
borderThickness: 1,
|
||||||
|
fontScale: 1.0,
|
||||||
|
autoHide: false,
|
||||||
|
autoHideDelay: 250,
|
||||||
|
openOnOverview: false,
|
||||||
|
visible: true,
|
||||||
|
popupGapsAuto: true,
|
||||||
|
popupGapsManual: 4
|
||||||
|
}], onChange: "updateBarConfigs" }
|
||||||
};
|
};
|
||||||
|
|
||||||
function getValidKeys() {
|
function getValidKeys() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
function parse(root, jsonObj) {
|
function parse(root, jsonObj) {
|
||||||
var SPEC = SpecModule.SPEC;
|
var SPEC = SpecModule.SPEC;
|
||||||
for (var k in SPEC) {
|
for (var k in SPEC) {
|
||||||
|
if (k === "pluginSettings") continue;
|
||||||
var spec = SPEC[k];
|
var spec = SPEC[k];
|
||||||
root[k] = spec.def;
|
root[k] = spec.def;
|
||||||
}
|
}
|
||||||
@@ -13,6 +14,7 @@ function parse(root, jsonObj) {
|
|||||||
|
|
||||||
for (var k in jsonObj) {
|
for (var k in jsonObj) {
|
||||||
if (!SPEC[k]) continue;
|
if (!SPEC[k]) continue;
|
||||||
|
if (k === "pluginSettings") continue;
|
||||||
var raw = jsonObj[k];
|
var raw = jsonObj[k];
|
||||||
var spec = SPEC[k];
|
var spec = SPEC[k];
|
||||||
var coerce = spec.coerce;
|
var coerce = spec.coerce;
|
||||||
@@ -25,72 +27,93 @@ function toJson(root) {
|
|||||||
var out = {};
|
var out = {};
|
||||||
for (var k in SPEC) {
|
for (var k in SPEC) {
|
||||||
if (SPEC[k].persist === false) continue;
|
if (SPEC[k].persist === false) continue;
|
||||||
|
if (k === "pluginSettings") continue;
|
||||||
out[k] = root[k];
|
out[k] = root[k];
|
||||||
}
|
}
|
||||||
out.configVersion = root.settingsConfigVersion;
|
out.configVersion = root.settingsConfigVersion;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
function migrate(root, jsonObj) {
|
function migrateToVersion(obj, targetVersion) {
|
||||||
var SPEC = SpecModule.SPEC;
|
if (!obj) return null;
|
||||||
if (!jsonObj) return;
|
|
||||||
|
|
||||||
if (jsonObj.themeIndex !== undefined || jsonObj.themeIsDynamic !== undefined) {
|
var settings = JSON.parse(JSON.stringify(obj));
|
||||||
var themeNames = ["blue", "deepBlue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral"];
|
var currentVersion = settings.configVersion || 0;
|
||||||
if (jsonObj.themeIsDynamic) {
|
|
||||||
root.currentThemeName = "dynamic";
|
if (currentVersion >= targetVersion) {
|
||||||
} else if (jsonObj.themeIndex >= 0 && jsonObj.themeIndex < themeNames.length) {
|
return null;
|
||||||
root.currentThemeName = themeNames[jsonObj.themeIndex];
|
|
||||||
}
|
|
||||||
console.info("Auto-migrated theme from index", jsonObj.themeIndex, "to", root.currentThemeName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((jsonObj.dankBarWidgetOrder && jsonObj.dankBarWidgetOrder.length > 0) ||
|
if (currentVersion < 2) {
|
||||||
(jsonObj.topBarWidgetOrder && jsonObj.topBarWidgetOrder.length > 0)) {
|
console.info("Migrating settings from version", currentVersion, "to version 2");
|
||||||
if (jsonObj.dankBarLeftWidgets === undefined && jsonObj.dankBarCenterWidgets === undefined && jsonObj.dankBarRightWidgets === undefined) {
|
|
||||||
var widgetOrder = jsonObj.dankBarWidgetOrder || jsonObj.topBarWidgetOrder;
|
|
||||||
root.dankBarLeftWidgets = widgetOrder.filter(function(w) { return ["launcherButton", "workspaceSwitcher", "focusedWindow"].indexOf(w) >= 0; });
|
|
||||||
root.dankBarCenterWidgets = widgetOrder.filter(function(w) { return ["clock", "music", "weather"].indexOf(w) >= 0; });
|
|
||||||
root.dankBarRightWidgets = widgetOrder.filter(function(w) { return ["systemTray", "clipboard", "systemResources", "notificationButton", "battery", "controlCenterButton"].indexOf(w) >= 0; });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsonObj.useOSLogo !== undefined) {
|
if (settings.barConfigs === undefined) {
|
||||||
root.launcherLogoMode = jsonObj.useOSLogo ? "os" : "apps";
|
var position = 0;
|
||||||
root.launcherLogoColorOverride = jsonObj.osLogoColorOverride !== undefined ? jsonObj.osLogoColorOverride : "";
|
if (settings.dankBarAtBottom !== undefined || settings.topBarAtBottom !== undefined) {
|
||||||
root.launcherLogoBrightness = jsonObj.osLogoBrightness !== undefined ? jsonObj.osLogoBrightness : 0.5;
|
var atBottom = settings.dankBarAtBottom !== undefined ? settings.dankBarAtBottom : settings.topBarAtBottom;
|
||||||
root.launcherLogoContrast = jsonObj.osLogoContrast !== undefined ? jsonObj.osLogoContrast : 1;
|
position = atBottom ? 1 : 0;
|
||||||
}
|
} else if (settings.dankBarPosition !== undefined) {
|
||||||
|
position = settings.dankBarPosition;
|
||||||
if (jsonObj.mediaCompactMode !== undefined && jsonObj.mediaSize === undefined) {
|
|
||||||
root.mediaSize = jsonObj.mediaCompactMode ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var k in SPEC) {
|
|
||||||
var spec = SPEC[k];
|
|
||||||
if (!spec.migrate) continue;
|
|
||||||
for (var i = 0; i < spec.migrate.length; i++) {
|
|
||||||
var oldKey = spec.migrate[i];
|
|
||||||
if (jsonObj[oldKey] !== undefined && jsonObj[k] === undefined) {
|
|
||||||
var raw = jsonObj[oldKey];
|
|
||||||
var coerce = spec.coerce;
|
|
||||||
root[k] = coerce ? (coerce(raw) !== undefined ? coerce(raw) : root[k]) : raw;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultConfig = {
|
||||||
|
id: "default",
|
||||||
|
name: "Main Bar",
|
||||||
|
enabled: true,
|
||||||
|
position: position,
|
||||||
|
screenPreferences: ["all"],
|
||||||
|
showOnLastDisplay: true,
|
||||||
|
leftWidgets: settings.dankBarLeftWidgets || ["launcherButton", "workspaceSwitcher", "focusedWindow"],
|
||||||
|
centerWidgets: settings.dankBarCenterWidgets || ["music", "clock", "weather"],
|
||||||
|
rightWidgets: settings.dankBarRightWidgets || ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
|
||||||
|
spacing: settings.dankBarSpacing !== undefined ? settings.dankBarSpacing : 4,
|
||||||
|
innerPadding: settings.dankBarInnerPadding !== undefined ? settings.dankBarInnerPadding : 4,
|
||||||
|
bottomGap: settings.dankBarBottomGap !== undefined ? settings.dankBarBottomGap : 0,
|
||||||
|
transparency: settings.dankBarTransparency !== undefined ? settings.dankBarTransparency : 1.0,
|
||||||
|
widgetTransparency: settings.dankBarWidgetTransparency !== undefined ? settings.dankBarWidgetTransparency : 1.0,
|
||||||
|
squareCorners: settings.dankBarSquareCorners !== undefined ? settings.dankBarSquareCorners : false,
|
||||||
|
noBackground: settings.dankBarNoBackground !== undefined ? settings.dankBarNoBackground : false,
|
||||||
|
gothCornersEnabled: settings.dankBarGothCornersEnabled !== undefined ? settings.dankBarGothCornersEnabled : false,
|
||||||
|
gothCornerRadiusOverride: settings.dankBarGothCornerRadiusOverride !== undefined ? settings.dankBarGothCornerRadiusOverride : false,
|
||||||
|
gothCornerRadiusValue: settings.dankBarGothCornerRadiusValue !== undefined ? settings.dankBarGothCornerRadiusValue : 12,
|
||||||
|
borderEnabled: settings.dankBarBorderEnabled !== undefined ? settings.dankBarBorderEnabled : false,
|
||||||
|
borderColor: settings.dankBarBorderColor || "surfaceText",
|
||||||
|
borderOpacity: settings.dankBarBorderOpacity !== undefined ? settings.dankBarBorderOpacity : 1.0,
|
||||||
|
borderThickness: settings.dankBarBorderThickness !== undefined ? settings.dankBarBorderThickness : 1,
|
||||||
|
fontScale: settings.dankBarFontScale !== undefined ? settings.dankBarFontScale : 1.0,
|
||||||
|
autoHide: settings.dankBarAutoHide !== undefined ? settings.dankBarAutoHide : false,
|
||||||
|
autoHideDelay: settings.dankBarAutoHideDelay !== undefined ? settings.dankBarAutoHideDelay : 250,
|
||||||
|
openOnOverview: settings.dankBarOpenOnOverview !== undefined ? settings.dankBarOpenOnOverview : false,
|
||||||
|
visible: settings.dankBarVisible !== undefined ? settings.dankBarVisible : true,
|
||||||
|
popupGapsAuto: settings.popupGapsAuto !== undefined ? settings.popupGapsAuto : true,
|
||||||
|
popupGapsManual: settings.popupGapsManual !== undefined ? settings.popupGapsManual : 4
|
||||||
|
};
|
||||||
|
|
||||||
|
settings.barConfigs = [defaultConfig];
|
||||||
|
|
||||||
|
var legacyKeys = [
|
||||||
|
"dankBarLeftWidgets", "dankBarCenterWidgets", "dankBarRightWidgets",
|
||||||
|
"dankBarWidgetOrder", "dankBarAutoHide", "dankBarAutoHideDelay",
|
||||||
|
"dankBarOpenOnOverview", "dankBarVisible", "dankBarSpacing",
|
||||||
|
"dankBarBottomGap", "dankBarInnerPadding", "dankBarPosition",
|
||||||
|
"dankBarSquareCorners", "dankBarNoBackground", "dankBarGothCornersEnabled",
|
||||||
|
"dankBarGothCornerRadiusOverride", "dankBarGothCornerRadiusValue",
|
||||||
|
"dankBarBorderEnabled", "dankBarBorderColor", "dankBarBorderOpacity",
|
||||||
|
"dankBarBorderThickness", "popupGapsAuto", "popupGapsManual",
|
||||||
|
"dankBarAtBottom", "topBarAtBottom", "dankBarTransparency", "dankBarWidgetTransparency"
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i = 0; i < legacyKeys.length; i++) {
|
||||||
|
delete settings[legacyKeys[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info("Migrated single bar settings to barConfigs");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings.configVersion = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jsonObj.dankBarAtBottom !== undefined || jsonObj.topBarAtBottom !== undefined) {
|
return settings;
|
||||||
var atBottom = jsonObj.dankBarAtBottom !== undefined ? jsonObj.dankBarAtBottom : jsonObj.topBarAtBottom;
|
|
||||||
root.dankBarPosition = atBottom ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsonObj.pluginSettings !== undefined) {
|
|
||||||
root.pluginSettings = jsonObj.pluginSettings;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanup(fileText) {
|
function cleanup(fileText) {
|
||||||
@@ -104,7 +127,6 @@ function cleanup(fileText) {
|
|||||||
|
|
||||||
for (var key in settings) {
|
for (var key in settings) {
|
||||||
if (validKeys.indexOf(key) < 0) {
|
if (validKeys.indexOf(key) < 0) {
|
||||||
console.log("SettingsData: Removing unused key:", key);
|
|
||||||
delete settings[key];
|
delete settings[key];
|
||||||
needsSave = true;
|
needsSave = true;
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-29
@@ -22,7 +22,7 @@ import qs.Modules.ProcessList
|
|||||||
import qs.Modules.Settings
|
import qs.Modules.Settings
|
||||||
import qs.Modules.DankBar
|
import qs.Modules.DankBar
|
||||||
import qs.Modules.DankBar.Popouts
|
import qs.Modules.DankBar.Popouts
|
||||||
import qs.Modules.HyprWorkspaces
|
import qs.Modules.WorkspaceOverlays
|
||||||
import qs.Modules.Plugins
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -66,38 +66,32 @@ Item {
|
|||||||
id: lock
|
id: lock
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Repeater {
|
||||||
id: dankBarLoader
|
id: dankBarRepeater
|
||||||
asynchronous: false
|
model: ScriptModel {
|
||||||
|
values: SettingsData.barConfigs
|
||||||
|
}
|
||||||
|
|
||||||
property var currentPosition: SettingsData.dankBarPosition
|
|
||||||
property bool initialized: false
|
|
||||||
property var hyprlandOverviewLoaderRef: hyprlandOverviewLoader
|
property var hyprlandOverviewLoaderRef: hyprlandOverviewLoader
|
||||||
|
|
||||||
sourceComponent: DankBar {
|
delegate: Loader {
|
||||||
hyprlandOverviewLoader: dankBarLoader.hyprlandOverviewLoaderRef
|
id: barLoader
|
||||||
|
active: modelData.enabled
|
||||||
|
asynchronous: false
|
||||||
|
|
||||||
onColorPickerRequested: {
|
sourceComponent: DankBar {
|
||||||
if (colorPickerModal.shouldBeVisible) {
|
barConfig: modelData
|
||||||
colorPickerModal.close()
|
hyprlandOverviewLoader: dankBarRepeater.hyprlandOverviewLoaderRef
|
||||||
} else {
|
|
||||||
colorPickerModal.show()
|
onColorPickerRequested: {
|
||||||
|
if (colorPickerModal.shouldBeVisible) {
|
||||||
|
colorPickerModal.close()
|
||||||
|
} else {
|
||||||
|
colorPickerModal.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
initialized = true
|
|
||||||
}
|
|
||||||
|
|
||||||
onCurrentPositionChanged: {
|
|
||||||
if (!initialized)
|
|
||||||
return
|
|
||||||
|
|
||||||
const component = sourceComponent
|
|
||||||
sourceComponent = null
|
|
||||||
sourceComponent = component
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
@@ -126,7 +120,6 @@ Item {
|
|||||||
if (!initialized)
|
if (!initialized)
|
||||||
return
|
return
|
||||||
|
|
||||||
console.log("DEBUG: Dock position changed to:", currentPosition, "- recreating dock")
|
|
||||||
const comp = sourceComponent
|
const comp = sourceComponent
|
||||||
sourceComponent = null
|
sourceComponent = null
|
||||||
sourceComponent = comp
|
sourceComponent = comp
|
||||||
@@ -137,7 +130,7 @@ Item {
|
|||||||
id: dankDashPopoutLoader
|
id: dankDashPopoutLoader
|
||||||
|
|
||||||
active: false
|
active: false
|
||||||
asynchronous: true
|
asynchronous: false
|
||||||
|
|
||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
DankDashPopout {
|
DankDashPopout {
|
||||||
@@ -513,8 +506,9 @@ Item {
|
|||||||
dankDashPopoutLoader: dankDashPopoutLoader
|
dankDashPopoutLoader: dankDashPopoutLoader
|
||||||
notepadSlideoutVariants: notepadSlideoutVariants
|
notepadSlideoutVariants: notepadSlideoutVariants
|
||||||
hyprKeybindsModalLoader: hyprKeybindsModalLoader
|
hyprKeybindsModalLoader: hyprKeybindsModalLoader
|
||||||
dankBarLoader: dankBarLoader
|
dankBarRepeater: dankBarRepeater
|
||||||
hyprlandOverviewLoader: hyprlandOverviewLoader
|
hyprlandOverviewLoader: hyprlandOverviewLoader
|
||||||
|
settingsModal: settingsModal
|
||||||
}
|
}
|
||||||
|
|
||||||
Variants {
|
Variants {
|
||||||
@@ -534,6 +528,14 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: SettingsData.getFilteredScreens("osd")
|
||||||
|
|
||||||
|
delegate: MediaVolumeOSD {
|
||||||
|
modelData: item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Variants {
|
Variants {
|
||||||
model: SettingsData.getFilteredScreens("osd")
|
model: SettingsData.getFilteredScreens("osd")
|
||||||
|
|
||||||
@@ -581,4 +583,12 @@ Item {
|
|||||||
id: hyprlandOverview
|
id: hyprlandOverview
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: niriOverviewOverlayLoader
|
||||||
|
active: CompositorService.isNiri
|
||||||
|
component: NiriOverviewOverlay {
|
||||||
|
id: niriOverviewOverlay
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+253
-155
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
@@ -14,36 +13,44 @@ Item {
|
|||||||
required property var dankDashPopoutLoader
|
required property var dankDashPopoutLoader
|
||||||
required property var notepadSlideoutVariants
|
required property var notepadSlideoutVariants
|
||||||
required property var hyprKeybindsModalLoader
|
required property var hyprKeybindsModalLoader
|
||||||
required property var dankBarLoader
|
required property var dankBarRepeater
|
||||||
required property var hyprlandOverviewLoader
|
required property var hyprlandOverviewLoader
|
||||||
|
required property var settingsModal
|
||||||
|
|
||||||
|
function getFirstBar() {
|
||||||
|
if (!root.dankBarRepeater || root.dankBarRepeater.count === 0)
|
||||||
|
return null;
|
||||||
|
const firstLoader = root.dankBarRepeater.itemAt(0);
|
||||||
|
return firstLoader ? firstLoader.item : null;
|
||||||
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open() {
|
function open() {
|
||||||
root.powerMenuModalLoader.active = true
|
root.powerMenuModalLoader.active = true;
|
||||||
if (root.powerMenuModalLoader.item)
|
if (root.powerMenuModalLoader.item)
|
||||||
root.powerMenuModalLoader.item.openCentered()
|
root.powerMenuModalLoader.item.openCentered();
|
||||||
|
|
||||||
return "POWERMENU_OPEN_SUCCESS"
|
return "POWERMENU_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
if (root.powerMenuModalLoader.item)
|
if (root.powerMenuModalLoader.item)
|
||||||
root.powerMenuModalLoader.item.close()
|
root.powerMenuModalLoader.item.close();
|
||||||
|
|
||||||
return "POWERMENU_CLOSE_SUCCESS"
|
return "POWERMENU_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
root.powerMenuModalLoader.active = true
|
root.powerMenuModalLoader.active = true;
|
||||||
if (root.powerMenuModalLoader.item) {
|
if (root.powerMenuModalLoader.item) {
|
||||||
if (root.powerMenuModalLoader.item.shouldBeVisible) {
|
if (root.powerMenuModalLoader.item.shouldBeVisible) {
|
||||||
root.powerMenuModalLoader.item.close()
|
root.powerMenuModalLoader.item.close();
|
||||||
} else {
|
} else {
|
||||||
root.powerMenuModalLoader.item.openCentered()
|
root.powerMenuModalLoader.item.openCentered();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "POWERMENU_TOGGLE_SUCCESS"
|
return "POWERMENU_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "powermenu"
|
target: "powermenu"
|
||||||
@@ -51,26 +58,26 @@ Item {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open(): string {
|
function open(): string {
|
||||||
root.processListModalLoader.active = true
|
root.processListModalLoader.active = true;
|
||||||
if (root.processListModalLoader.item)
|
if (root.processListModalLoader.item)
|
||||||
root.processListModalLoader.item.show()
|
root.processListModalLoader.item.show();
|
||||||
|
|
||||||
return "PROCESSLIST_OPEN_SUCCESS"
|
return "PROCESSLIST_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): string {
|
function close(): string {
|
||||||
if (root.processListModalLoader.item)
|
if (root.processListModalLoader.item)
|
||||||
root.processListModalLoader.item.hide()
|
root.processListModalLoader.item.hide();
|
||||||
|
|
||||||
return "PROCESSLIST_CLOSE_SUCCESS"
|
return "PROCESSLIST_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
root.processListModalLoader.active = true
|
root.processListModalLoader.active = true;
|
||||||
if (root.processListModalLoader.item)
|
if (root.processListModalLoader.item)
|
||||||
root.processListModalLoader.item.toggle()
|
root.processListModalLoader.item.toggle();
|
||||||
|
|
||||||
return "PROCESSLIST_TOGGLE_SUCCESS"
|
return "PROCESSLIST_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "processlist"
|
target: "processlist"
|
||||||
@@ -78,27 +85,33 @@ Item {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open(): string {
|
function open(): string {
|
||||||
if (root.dankBarLoader.item) {
|
const bar = root.getFirstBar();
|
||||||
root.dankBarLoader.item.triggerControlCenterOnFocusedScreen()
|
if (bar) {
|
||||||
return "CONTROL_CENTER_OPEN_SUCCESS"
|
bar.triggerControlCenterOnFocusedScreen();
|
||||||
|
return "CONTROL_CENTER_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
return "CONTROL_CENTER_OPEN_FAILED"
|
return "CONTROL_CENTER_OPEN_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): string {
|
function hide(): string {
|
||||||
if (root.controlCenterLoader.item) {
|
if (root.controlCenterLoader.item && root.controlCenterLoader.item.shouldBeVisible) {
|
||||||
root.controlCenterLoader.item.close()
|
root.controlCenterLoader.item.close();
|
||||||
return "CONTROL_CENTER_CLOSE_SUCCESS"
|
return "CONTROL_CENTER_HIDE_SUCCESS";
|
||||||
}
|
}
|
||||||
return "CONTROL_CENTER_CLOSE_FAILED"
|
return "CONTROL_CENTER_HIDE_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
if (root.dankBarLoader.item) {
|
const bar = root.getFirstBar();
|
||||||
root.dankBarLoader.item.triggerControlCenterOnFocusedScreen()
|
if (bar) {
|
||||||
return "CONTROL_CENTER_TOGGLE_SUCCESS"
|
bar.triggerControlCenterOnFocusedScreen();
|
||||||
|
return "CONTROL_CENTER_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
return "CONTROL_CENTER_TOGGLE_FAILED"
|
return "CONTROL_CENTER_TOGGLE_FAILED";
|
||||||
|
}
|
||||||
|
|
||||||
|
function status(): string {
|
||||||
|
return (root.controlCenterLoader.item && root.controlCenterLoader.item.shouldBeVisible) ? "visible" : "hidden";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "control-center"
|
target: "control-center"
|
||||||
@@ -106,55 +119,56 @@ Item {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open(tab: string): string {
|
function open(tab: string): string {
|
||||||
root.dankDashPopoutLoader.active = true
|
root.dankDashPopoutLoader.active = true;
|
||||||
if (root.dankDashPopoutLoader.item) {
|
if (root.dankDashPopoutLoader.item) {
|
||||||
switch (tab.toLowerCase()) {
|
switch (tab.toLowerCase()) {
|
||||||
case "media":
|
case "media":
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = 1
|
root.dankDashPopoutLoader.item.currentTabIndex = 1;
|
||||||
break
|
break;
|
||||||
case "weather":
|
case "weather":
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 2 : 0
|
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 2 : 0;
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = 0
|
root.dankDashPopoutLoader.item.currentTabIndex = 0;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
root.dankDashPopoutLoader.item.setTriggerPosition(Screen.width / 2, Theme.barHeight + Theme.spacingS, 100, "center", Screen)
|
root.dankDashPopoutLoader.item.setTriggerPosition(Screen.width / 2, Theme.barHeight + Theme.spacingS, 100, "center", Screen);
|
||||||
root.dankDashPopoutLoader.item.dashVisible = true
|
root.dankDashPopoutLoader.item.dashVisible = true;
|
||||||
return "DASH_OPEN_SUCCESS"
|
return "DASH_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
return "DASH_OPEN_FAILED"
|
return "DASH_OPEN_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): string {
|
function close(): string {
|
||||||
if (root.dankDashPopoutLoader.item) {
|
if (root.dankDashPopoutLoader.item) {
|
||||||
root.dankDashPopoutLoader.item.dashVisible = false
|
root.dankDashPopoutLoader.item.dashVisible = false;
|
||||||
return "DASH_CLOSE_SUCCESS"
|
return "DASH_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
return "DASH_CLOSE_FAILED"
|
return "DASH_CLOSE_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle(tab: string): string {
|
function toggle(tab: string): string {
|
||||||
if (root.dankBarLoader.item && root.dankBarLoader.item.triggerWallpaperBrowserOnFocusedScreen()) {
|
const bar = root.getFirstBar();
|
||||||
|
if (bar && bar.triggerWallpaperBrowserOnFocusedScreen()) {
|
||||||
if (root.dankDashPopoutLoader.item) {
|
if (root.dankDashPopoutLoader.item) {
|
||||||
switch (tab.toLowerCase()) {
|
switch (tab.toLowerCase()) {
|
||||||
case "media":
|
case "media":
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = 1
|
root.dankDashPopoutLoader.item.currentTabIndex = 1;
|
||||||
break
|
break;
|
||||||
case "wallpaper":
|
case "wallpaper":
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = 2
|
root.dankDashPopoutLoader.item.currentTabIndex = 2;
|
||||||
break
|
break;
|
||||||
case "weather":
|
case "weather":
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 3 : 0
|
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 3 : 0;
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = 0
|
root.dankDashPopoutLoader.item.currentTabIndex = 0;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "DASH_TOGGLE_SUCCESS"
|
return "DASH_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
return "DASH_TOGGLE_FAILED"
|
return "DASH_TOGGLE_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "dash"
|
target: "dash"
|
||||||
@@ -163,68 +177,68 @@ Item {
|
|||||||
IpcHandler {
|
IpcHandler {
|
||||||
function getFocusedScreenName() {
|
function getFocusedScreenName() {
|
||||||
if (CompositorService.isHyprland && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor) {
|
if (CompositorService.isHyprland && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor) {
|
||||||
return Hyprland.focusedWorkspace.monitor.name
|
return Hyprland.focusedWorkspace.monitor.name;
|
||||||
}
|
}
|
||||||
if (CompositorService.isNiri && NiriService.currentOutput) {
|
if (CompositorService.isNiri && NiriService.currentOutput) {
|
||||||
return NiriService.currentOutput
|
return NiriService.currentOutput;
|
||||||
}
|
}
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActiveNotepadInstance() {
|
function getActiveNotepadInstance() {
|
||||||
if (root.notepadSlideoutVariants.instances.length === 0) {
|
if (root.notepadSlideoutVariants.instances.length === 0) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.notepadSlideoutVariants.instances.length === 1) {
|
if (root.notepadSlideoutVariants.instances.length === 1) {
|
||||||
return root.notepadSlideoutVariants.instances[0]
|
return root.notepadSlideoutVariants.instances[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
var focusedScreen = getFocusedScreenName()
|
var focusedScreen = getFocusedScreenName();
|
||||||
if (focusedScreen && root.notepadSlideoutVariants.instances.length > 0) {
|
if (focusedScreen && root.notepadSlideoutVariants.instances.length > 0) {
|
||||||
for (var i = 0; i < root.notepadSlideoutVariants.instances.length; i++) {
|
for (var i = 0; i < root.notepadSlideoutVariants.instances.length; i++) {
|
||||||
var slideout = root.notepadSlideoutVariants.instances[i]
|
var slideout = root.notepadSlideoutVariants.instances[i];
|
||||||
if (slideout.modelData && slideout.modelData.name === focusedScreen) {
|
if (slideout.modelData && slideout.modelData.name === focusedScreen) {
|
||||||
return slideout
|
return slideout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < root.notepadSlideoutVariants.instances.length; i++) {
|
for (var i = 0; i < root.notepadSlideoutVariants.instances.length; i++) {
|
||||||
var slideout = root.notepadSlideoutVariants.instances[i]
|
var slideout = root.notepadSlideoutVariants.instances[i];
|
||||||
if (slideout.isVisible) {
|
if (slideout.isVisible) {
|
||||||
return slideout
|
return slideout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return root.notepadSlideoutVariants.instances[0]
|
return root.notepadSlideoutVariants.instances[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function open(): string {
|
function open(): string {
|
||||||
var instance = getActiveNotepadInstance()
|
var instance = getActiveNotepadInstance();
|
||||||
if (instance) {
|
if (instance) {
|
||||||
instance.show()
|
instance.show();
|
||||||
return "NOTEPAD_OPEN_SUCCESS"
|
return "NOTEPAD_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
return "NOTEPAD_OPEN_FAILED"
|
return "NOTEPAD_OPEN_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): string {
|
function close(): string {
|
||||||
var instance = getActiveNotepadInstance()
|
var instance = getActiveNotepadInstance();
|
||||||
if (instance) {
|
if (instance) {
|
||||||
instance.hide()
|
instance.hide();
|
||||||
return "NOTEPAD_CLOSE_SUCCESS"
|
return "NOTEPAD_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
return "NOTEPAD_CLOSE_FAILED"
|
return "NOTEPAD_CLOSE_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
var instance = getActiveNotepadInstance()
|
var instance = getActiveNotepadInstance();
|
||||||
if (instance) {
|
if (instance) {
|
||||||
instance.toggle()
|
instance.toggle();
|
||||||
return "NOTEPAD_TOGGLE_SUCCESS"
|
return "NOTEPAD_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
return "NOTEPAD_TOGGLE_FAILED"
|
return "NOTEPAD_TOGGLE_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "notepad"
|
target: "notepad"
|
||||||
@@ -232,31 +246,31 @@ Item {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
SessionService.toggleIdleInhibit()
|
SessionService.toggleIdleInhibit();
|
||||||
return SessionService.idleInhibited ? "Idle inhibit enabled" : "Idle inhibit disabled"
|
return SessionService.idleInhibited ? "Idle inhibit enabled" : "Idle inhibit disabled";
|
||||||
}
|
}
|
||||||
|
|
||||||
function enable(): string {
|
function enable(): string {
|
||||||
SessionService.enableIdleInhibit()
|
SessionService.enableIdleInhibit();
|
||||||
return "Idle inhibit enabled"
|
return "Idle inhibit enabled";
|
||||||
}
|
}
|
||||||
|
|
||||||
function disable(): string {
|
function disable(): string {
|
||||||
SessionService.disableIdleInhibit()
|
SessionService.disableIdleInhibit();
|
||||||
return "Idle inhibit disabled"
|
return "Idle inhibit disabled";
|
||||||
}
|
}
|
||||||
|
|
||||||
function status(): string {
|
function status(): string {
|
||||||
return SessionService.idleInhibited ? "Idle inhibit is enabled" : "Idle inhibit is disabled"
|
return SessionService.idleInhibited ? "Idle inhibit is enabled" : "Idle inhibit is disabled";
|
||||||
}
|
}
|
||||||
|
|
||||||
function reason(newReason: string): string {
|
function reason(newReason: string): string {
|
||||||
if (!newReason) {
|
if (!newReason) {
|
||||||
return `Current reason: ${SessionService.inhibitReason}`
|
return `Current reason: ${SessionService.inhibitReason}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionService.setInhibitReason(newReason)
|
SessionService.setInhibitReason(newReason);
|
||||||
return `Inhibit reason set to: ${newReason}`
|
return `Inhibit reason set to: ${newReason}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "inhibit"
|
target: "inhibit"
|
||||||
@@ -264,42 +278,42 @@ Item {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function list(): string {
|
function list(): string {
|
||||||
return MprisController.availablePlayers.map(p => p.identity).join("\n")
|
return MprisController.availablePlayers.map(p => p.identity).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function play(): void {
|
function play(): void {
|
||||||
if (MprisController.activePlayer && MprisController.activePlayer.canPlay) {
|
if (MprisController.activePlayer && MprisController.activePlayer.canPlay) {
|
||||||
MprisController.activePlayer.play()
|
MprisController.activePlayer.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pause(): void {
|
function pause(): void {
|
||||||
if (MprisController.activePlayer && MprisController.activePlayer.canPause) {
|
if (MprisController.activePlayer && MprisController.activePlayer.canPause) {
|
||||||
MprisController.activePlayer.pause()
|
MprisController.activePlayer.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function playPause(): void {
|
function playPause(): void {
|
||||||
if (MprisController.activePlayer && MprisController.activePlayer.canTogglePlaying) {
|
if (MprisController.activePlayer && MprisController.activePlayer.canTogglePlaying) {
|
||||||
MprisController.activePlayer.togglePlaying()
|
MprisController.activePlayer.togglePlaying();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function previous(): void {
|
function previous(): void {
|
||||||
if (MprisController.activePlayer && MprisController.activePlayer.canGoPrevious) {
|
if (MprisController.activePlayer && MprisController.activePlayer.canGoPrevious) {
|
||||||
MprisController.activePlayer.previous()
|
MprisController.activePlayer.previous();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function next(): void {
|
function next(): void {
|
||||||
if (MprisController.activePlayer && MprisController.activePlayer.canGoNext) {
|
if (MprisController.activePlayer && MprisController.activePlayer.canGoNext) {
|
||||||
MprisController.activePlayer.next()
|
MprisController.activePlayer.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function stop(): void {
|
function stop(): void {
|
||||||
if (MprisController.activePlayer) {
|
if (MprisController.activePlayer) {
|
||||||
MprisController.activePlayer.stop()
|
MprisController.activePlayer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,78 +323,78 @@ Item {
|
|||||||
IpcHandler {
|
IpcHandler {
|
||||||
function toggle(provider: string): string {
|
function toggle(provider: string): string {
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
return "ERROR: No provider specified"
|
return "ERROR: No provider specified";
|
||||||
}
|
}
|
||||||
|
|
||||||
KeybindsService.loadProvider(provider)
|
KeybindsService.loadProvider(provider);
|
||||||
root.hyprKeybindsModalLoader.active = true
|
root.hyprKeybindsModalLoader.active = true;
|
||||||
|
|
||||||
if (root.hyprKeybindsModalLoader.item) {
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
if (root.hyprKeybindsModalLoader.item.shouldBeVisible) {
|
if (root.hyprKeybindsModalLoader.item.shouldBeVisible) {
|
||||||
root.hyprKeybindsModalLoader.item.close()
|
root.hyprKeybindsModalLoader.item.close();
|
||||||
} else {
|
} else {
|
||||||
root.hyprKeybindsModalLoader.item.open()
|
root.hyprKeybindsModalLoader.item.open();
|
||||||
}
|
}
|
||||||
return `KEYBINDS_TOGGLE_SUCCESS: ${provider}`
|
return `KEYBINDS_TOGGLE_SUCCESS: ${provider}`;
|
||||||
}
|
}
|
||||||
return `KEYBINDS_TOGGLE_FAILED: ${provider}`
|
return `KEYBINDS_TOGGLE_FAILED: ${provider}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleWithPath(provider: string, path: string): string {
|
function toggleWithPath(provider: string, path: string): string {
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
return "ERROR: No provider specified"
|
return "ERROR: No provider specified";
|
||||||
}
|
}
|
||||||
|
|
||||||
KeybindsService.loadProviderWithPath(provider, path)
|
KeybindsService.loadProviderWithPath(provider, path);
|
||||||
root.hyprKeybindsModalLoader.active = true
|
root.hyprKeybindsModalLoader.active = true;
|
||||||
|
|
||||||
if (root.hyprKeybindsModalLoader.item) {
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
if (root.hyprKeybindsModalLoader.item.shouldBeVisible) {
|
if (root.hyprKeybindsModalLoader.item.shouldBeVisible) {
|
||||||
root.hyprKeybindsModalLoader.item.close()
|
root.hyprKeybindsModalLoader.item.close();
|
||||||
} else {
|
} else {
|
||||||
root.hyprKeybindsModalLoader.item.open()
|
root.hyprKeybindsModalLoader.item.open();
|
||||||
}
|
}
|
||||||
return `KEYBINDS_TOGGLE_SUCCESS: ${provider} (${path})`
|
return `KEYBINDS_TOGGLE_SUCCESS: ${provider} (${path})`;
|
||||||
}
|
}
|
||||||
return `KEYBINDS_TOGGLE_FAILED: ${provider}`
|
return `KEYBINDS_TOGGLE_FAILED: ${provider}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function open(provider: string): string {
|
function open(provider: string): string {
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
return "ERROR: No provider specified"
|
return "ERROR: No provider specified";
|
||||||
}
|
}
|
||||||
|
|
||||||
KeybindsService.loadProvider(provider)
|
KeybindsService.loadProvider(provider);
|
||||||
root.hyprKeybindsModalLoader.active = true
|
root.hyprKeybindsModalLoader.active = true;
|
||||||
|
|
||||||
if (root.hyprKeybindsModalLoader.item) {
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
root.hyprKeybindsModalLoader.item.open()
|
root.hyprKeybindsModalLoader.item.open();
|
||||||
return `KEYBINDS_OPEN_SUCCESS: ${provider}`
|
return `KEYBINDS_OPEN_SUCCESS: ${provider}`;
|
||||||
}
|
}
|
||||||
return `KEYBINDS_OPEN_FAILED: ${provider}`
|
return `KEYBINDS_OPEN_FAILED: ${provider}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openWithPath(provider: string, path: string): string {
|
function openWithPath(provider: string, path: string): string {
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
return "ERROR: No provider specified"
|
return "ERROR: No provider specified";
|
||||||
}
|
}
|
||||||
|
|
||||||
KeybindsService.loadProviderWithPath(provider, path)
|
KeybindsService.loadProviderWithPath(provider, path);
|
||||||
root.hyprKeybindsModalLoader.active = true
|
root.hyprKeybindsModalLoader.active = true;
|
||||||
|
|
||||||
if (root.hyprKeybindsModalLoader.item) {
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
root.hyprKeybindsModalLoader.item.open()
|
root.hyprKeybindsModalLoader.item.open();
|
||||||
return `KEYBINDS_OPEN_SUCCESS: ${provider} (${path})`
|
return `KEYBINDS_OPEN_SUCCESS: ${provider} (${path})`;
|
||||||
}
|
}
|
||||||
return `KEYBINDS_OPEN_FAILED: ${provider}`
|
return `KEYBINDS_OPEN_FAILED: ${provider}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): string {
|
function close(): string {
|
||||||
if (root.hyprKeybindsModalLoader.item) {
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
root.hyprKeybindsModalLoader.item.close()
|
root.hyprKeybindsModalLoader.item.close();
|
||||||
return "KEYBINDS_CLOSE_SUCCESS"
|
return "KEYBINDS_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
return "KEYBINDS_CLOSE_FAILED"
|
return "KEYBINDS_CLOSE_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "keybinds"
|
target: "keybinds"
|
||||||
@@ -389,67 +403,67 @@ Item {
|
|||||||
IpcHandler {
|
IpcHandler {
|
||||||
function openBinds(): string {
|
function openBinds(): string {
|
||||||
if (!CompositorService.isHyprland) {
|
if (!CompositorService.isHyprland) {
|
||||||
return "HYPR_NOT_AVAILABLE"
|
return "HYPR_NOT_AVAILABLE";
|
||||||
}
|
}
|
||||||
KeybindsService.loadProvider("hyprland")
|
KeybindsService.loadProvider("hyprland");
|
||||||
root.hyprKeybindsModalLoader.active = true
|
root.hyprKeybindsModalLoader.active = true;
|
||||||
if (root.hyprKeybindsModalLoader.item) {
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
root.hyprKeybindsModalLoader.item.open()
|
root.hyprKeybindsModalLoader.item.open();
|
||||||
return "HYPR_KEYBINDS_OPEN_SUCCESS"
|
return "HYPR_KEYBINDS_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
return "HYPR_KEYBINDS_OPEN_FAILED"
|
return "HYPR_KEYBINDS_OPEN_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeBinds(): string {
|
function closeBinds(): string {
|
||||||
if (!CompositorService.isHyprland) {
|
if (!CompositorService.isHyprland) {
|
||||||
return "HYPR_NOT_AVAILABLE"
|
return "HYPR_NOT_AVAILABLE";
|
||||||
}
|
}
|
||||||
if (root.hyprKeybindsModalLoader.item) {
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
root.hyprKeybindsModalLoader.item.close()
|
root.hyprKeybindsModalLoader.item.close();
|
||||||
return "HYPR_KEYBINDS_CLOSE_SUCCESS"
|
return "HYPR_KEYBINDS_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
return "HYPR_KEYBINDS_CLOSE_FAILED"
|
return "HYPR_KEYBINDS_CLOSE_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleBinds(): string {
|
function toggleBinds(): string {
|
||||||
if (!CompositorService.isHyprland) {
|
if (!CompositorService.isHyprland) {
|
||||||
return "HYPR_NOT_AVAILABLE"
|
return "HYPR_NOT_AVAILABLE";
|
||||||
}
|
}
|
||||||
KeybindsService.loadProvider("hyprland")
|
KeybindsService.loadProvider("hyprland");
|
||||||
root.hyprKeybindsModalLoader.active = true
|
root.hyprKeybindsModalLoader.active = true;
|
||||||
if (root.hyprKeybindsModalLoader.item) {
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
if (root.hyprKeybindsModalLoader.item.shouldBeVisible) {
|
if (root.hyprKeybindsModalLoader.item.shouldBeVisible) {
|
||||||
root.hyprKeybindsModalLoader.item.close()
|
root.hyprKeybindsModalLoader.item.close();
|
||||||
} else {
|
} else {
|
||||||
root.hyprKeybindsModalLoader.item.open()
|
root.hyprKeybindsModalLoader.item.open();
|
||||||
}
|
}
|
||||||
return "HYPR_KEYBINDS_TOGGLE_SUCCESS"
|
return "HYPR_KEYBINDS_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
return "HYPR_KEYBINDS_TOGGLE_FAILED"
|
return "HYPR_KEYBINDS_TOGGLE_FAILED";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleOverview(): string {
|
function toggleOverview(): string {
|
||||||
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
||||||
return "HYPR_NOT_AVAILABLE"
|
return "HYPR_NOT_AVAILABLE";
|
||||||
}
|
}
|
||||||
root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen
|
root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen;
|
||||||
return root.hyprlandOverviewLoader.item.overviewOpen ? "OVERVIEW_OPEN_SUCCESS" : "OVERVIEW_CLOSE_SUCCESS"
|
return root.hyprlandOverviewLoader.item.overviewOpen ? "OVERVIEW_OPEN_SUCCESS" : "OVERVIEW_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeOverview(): string {
|
function closeOverview(): string {
|
||||||
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
||||||
return "HYPR_NOT_AVAILABLE"
|
return "HYPR_NOT_AVAILABLE";
|
||||||
}
|
}
|
||||||
root.hyprlandOverviewLoader.item.overviewOpen = false
|
root.hyprlandOverviewLoader.item.overviewOpen = false;
|
||||||
return "OVERVIEW_CLOSE_SUCCESS"
|
return "OVERVIEW_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function openOverview(): string {
|
function openOverview(): string {
|
||||||
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
||||||
return "HYPR_NOT_AVAILABLE"
|
return "HYPR_NOT_AVAILABLE";
|
||||||
}
|
}
|
||||||
root.hyprlandOverviewLoader.item.overviewOpen = true
|
root.hyprlandOverviewLoader.item.overviewOpen = true;
|
||||||
return "OVERVIEW_OPEN_SUCCESS"
|
return "OVERVIEW_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "hypr"
|
target: "hypr"
|
||||||
@@ -457,12 +471,96 @@ Item {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function wallpaper(): string {
|
function wallpaper(): string {
|
||||||
if (root.dankBarLoader.item && root.dankBarLoader.item.triggerWallpaperBrowserOnFocusedScreen()) {
|
const bar = root.getFirstBar();
|
||||||
return "SUCCESS: Toggled wallpaper browser"
|
if (bar && bar.triggerWallpaperBrowserOnFocusedScreen()) {
|
||||||
|
return "SUCCESS: Toggled wallpaper browser";
|
||||||
}
|
}
|
||||||
return "ERROR: Failed to toggle wallpaper browser"
|
return "ERROR: Failed to toggle wallpaper browser";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "dankdash"
|
target: "dankdash"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function reveal(index: int): string {
|
||||||
|
const idx = index - 1;
|
||||||
|
if (idx < 0 || idx >= SettingsData.barConfigs.length) {
|
||||||
|
return `BAR_${index}_NOT_FOUND`;
|
||||||
|
}
|
||||||
|
const bar = SettingsData.barConfigs[idx];
|
||||||
|
SettingsData.updateBarConfig(bar.id, {
|
||||||
|
visible: true
|
||||||
|
});
|
||||||
|
return `BAR_${index}_SHOW_SUCCESS`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide(index: int): string {
|
||||||
|
const idx = index - 1;
|
||||||
|
if (idx < 0 || idx >= SettingsData.barConfigs.length) {
|
||||||
|
return `BAR_${index}_NOT_FOUND`;
|
||||||
|
}
|
||||||
|
const bar = SettingsData.barConfigs[idx];
|
||||||
|
SettingsData.updateBarConfig(bar.id, {
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
return `BAR_${index}_HIDE_SUCCESS`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(index: int): string {
|
||||||
|
const idx = index - 1;
|
||||||
|
if (idx < 0 || idx >= SettingsData.barConfigs.length) {
|
||||||
|
return `BAR_${index}_NOT_FOUND`;
|
||||||
|
}
|
||||||
|
const bar = SettingsData.barConfigs[idx];
|
||||||
|
const newVisible = !(bar.visible ?? true);
|
||||||
|
SettingsData.updateBarConfig(bar.id, {
|
||||||
|
visible: newVisible
|
||||||
|
});
|
||||||
|
return newVisible ? `BAR_${index}_SHOW_SUCCESS` : `BAR_${index}_HIDE_SUCCESS`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function status(index: int): string {
|
||||||
|
const idx = index - 1;
|
||||||
|
if (idx < 0 || idx >= SettingsData.barConfigs.length) {
|
||||||
|
return `BAR_${index}_NOT_FOUND`;
|
||||||
|
}
|
||||||
|
const bar = SettingsData.barConfigs[idx];
|
||||||
|
return (bar.visible ?? true) ? "visible" : "hidden";
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function open(): string {
|
||||||
|
root.settingsModal.show();
|
||||||
|
return "SETTINGS_OPEN_SUCCESS";
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): string {
|
||||||
|
root.settingsModal.hide();
|
||||||
|
return "SETTINGS_CLOSE_SUCCESS";
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): string {
|
||||||
|
root.settingsModal.toggle();
|
||||||
|
return "SETTINGS_TOGGLE_SUCCESS";
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "settings"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function browse(type: string) {
|
||||||
|
if (type === "wallpaper") {
|
||||||
|
root.settingsModal.wallpaperBrowser.allowStacking = false;
|
||||||
|
root.settingsModal.wallpaperBrowser.open();
|
||||||
|
} else if (type === "profile") {
|
||||||
|
root.settingsModal.profileBrowser.allowStacking = false;
|
||||||
|
root.settingsModal.profileBrowser.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "file"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -9,6 +10,11 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:bluetooth-pairing"
|
layerNamespace: "dms:bluetooth-pairing"
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: [root]
|
||||||
|
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||||
|
}
|
||||||
|
|
||||||
property string deviceName: ""
|
property string deviceName: ""
|
||||||
property string deviceAddress: ""
|
property string deviceAddress: ""
|
||||||
property string requestType: ""
|
property string requestType: ""
|
||||||
@@ -18,38 +24,38 @@ DankModal {
|
|||||||
property string passkeyInput: ""
|
property string passkeyInput: ""
|
||||||
|
|
||||||
function show(pairingData) {
|
function show(pairingData) {
|
||||||
console.log("BluetoothPairingModal.show() called:", JSON.stringify(pairingData))
|
console.log("BluetoothPairingModal.show() called:", JSON.stringify(pairingData));
|
||||||
token = pairingData.token || ""
|
token = pairingData.token || "";
|
||||||
deviceName = pairingData.deviceName || ""
|
deviceName = pairingData.deviceName || "";
|
||||||
deviceAddress = pairingData.deviceAddr || ""
|
deviceAddress = pairingData.deviceAddr || "";
|
||||||
requestType = pairingData.requestType || ""
|
requestType = pairingData.requestType || "";
|
||||||
passkey = pairingData.passkey || 0
|
passkey = pairingData.passkey || 0;
|
||||||
pinInput = ""
|
pinInput = "";
|
||||||
passkeyInput = ""
|
passkeyInput = "";
|
||||||
|
|
||||||
console.log("BluetoothPairingModal: Calling open()")
|
console.log("BluetoothPairingModal: Calling open()");
|
||||||
open()
|
open();
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item) {
|
if (contentLoader.item) {
|
||||||
if (requestType === "pin" && contentLoader.item.pinInputField) {
|
if (requestType === "pin" && contentLoader.item.pinInputField) {
|
||||||
contentLoader.item.pinInputField.forceActiveFocus()
|
contentLoader.item.pinInputField.forceActiveFocus();
|
||||||
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
|
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
|
||||||
contentLoader.item.passkeyInputField.forceActiveFocus()
|
contentLoader.item.passkeyInputField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
keepPopoutsOpen: true
|
keepPopoutsOpen: true
|
||||||
width: 420
|
modalWidth: 420
|
||||||
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240
|
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240
|
||||||
|
|
||||||
onShouldBeVisibleChanged: () => {
|
onShouldBeVisibleChanged: () => {
|
||||||
if (!shouldBeVisible) {
|
if (!shouldBeVisible) {
|
||||||
pinInput = ""
|
pinInput = "";
|
||||||
passkeyInput = ""
|
passkeyInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,22 +63,22 @@ DankModal {
|
|||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item) {
|
if (contentLoader.item) {
|
||||||
if (requestType === "pin" && contentLoader.item.pinInputField) {
|
if (requestType === "pin" && contentLoader.item.pinInputField) {
|
||||||
contentLoader.item.pinInputField.forceActiveFocus()
|
contentLoader.item.pinInputField.forceActiveFocus();
|
||||||
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
|
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
|
||||||
contentLoader.item.passkeyInputField.forceActiveFocus()
|
contentLoader.item.passkeyInputField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
if (token) {
|
if (token) {
|
||||||
DMSService.bluetoothCancelPairing(token)
|
DMSService.bluetoothCancelPairing(token);
|
||||||
}
|
}
|
||||||
close()
|
close();
|
||||||
token = ""
|
token = "";
|
||||||
pinInput = ""
|
pinInput = "";
|
||||||
passkeyInput = ""
|
passkeyInput = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
@@ -88,13 +94,13 @@ DankModal {
|
|||||||
|
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
if (token) {
|
if (token) {
|
||||||
DMSService.bluetoothCancelPairing(token)
|
DMSService.bluetoothCancelPairing(token);
|
||||||
}
|
}
|
||||||
close()
|
close();
|
||||||
token = ""
|
token = "";
|
||||||
pinInput = ""
|
pinInput = "";
|
||||||
passkeyInput = ""
|
passkeyInput = "";
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -122,19 +128,19 @@ DankModal {
|
|||||||
text: {
|
text: {
|
||||||
switch (requestType) {
|
switch (requestType) {
|
||||||
case "confirm":
|
case "confirm":
|
||||||
return I18n.tr("Confirm passkey for ") + deviceName
|
return I18n.tr("Confirm passkey for ") + deviceName;
|
||||||
case "display-passkey":
|
case "display-passkey":
|
||||||
return I18n.tr("Enter this passkey on ") + deviceName
|
return I18n.tr("Enter this passkey on ") + deviceName;
|
||||||
case "authorize":
|
case "authorize":
|
||||||
return I18n.tr("Authorize pairing with ") + deviceName
|
return I18n.tr("Authorize pairing with ") + deviceName;
|
||||||
case "pin":
|
case "pin":
|
||||||
return I18n.tr("Enter PIN for ") + deviceName
|
return I18n.tr("Enter PIN for ") + deviceName;
|
||||||
case "passkey":
|
case "passkey":
|
||||||
return I18n.tr("Enter passkey for ") + deviceName
|
return I18n.tr("Enter passkey for ") + deviceName;
|
||||||
default:
|
default:
|
||||||
if (requestType.startsWith("authorize-service"))
|
if (requestType.startsWith("authorize-service"))
|
||||||
return I18n.tr("Authorize service for ") + deviceName
|
return I18n.tr("Authorize service for ") + deviceName;
|
||||||
return deviceName
|
return deviceName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -156,7 +162,7 @@ DankModal {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
pinInputField.forceActiveFocus()
|
pinInputField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,10 +177,10 @@ DankModal {
|
|||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
enabled: root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
pinInput = text
|
pinInput = text;
|
||||||
}
|
}
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
submitPairing()
|
submitPairing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +197,7 @@ DankModal {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
passkeyInputField.forceActiveFocus()
|
passkeyInputField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,10 +212,10 @@ DankModal {
|
|||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
enabled: root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
passkeyInput = text
|
passkeyInput = text;
|
||||||
}
|
}
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
submitPairing()
|
submitPairing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,12 +283,12 @@ DankModal {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (token) {
|
if (token) {
|
||||||
DMSService.bluetoothCancelPairing(token)
|
DMSService.bluetoothCancelPairing(token);
|
||||||
}
|
}
|
||||||
close()
|
close();
|
||||||
token = ""
|
token = "";
|
||||||
pinInput = ""
|
pinInput = "";
|
||||||
passkeyInput = ""
|
passkeyInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,10 +300,10 @@ DankModal {
|
|||||||
color: pairArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
color: pairArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
enabled: {
|
enabled: {
|
||||||
if (requestType === "pin")
|
if (requestType === "pin")
|
||||||
return pinInput.length > 0
|
return pinInput.length > 0;
|
||||||
if (requestType === "passkey")
|
if (requestType === "passkey")
|
||||||
return passkeyInput.length === 6
|
return passkeyInput.length === 6;
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
opacity: enabled ? 1 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
@@ -309,13 +315,13 @@ DankModal {
|
|||||||
switch (requestType) {
|
switch (requestType) {
|
||||||
case "confirm":
|
case "confirm":
|
||||||
case "display-passkey":
|
case "display-passkey":
|
||||||
return I18n.tr("Confirm")
|
return I18n.tr("Confirm");
|
||||||
case "authorize":
|
case "authorize":
|
||||||
return I18n.tr("Authorize")
|
return I18n.tr("Authorize");
|
||||||
default:
|
default:
|
||||||
if (requestType.startsWith("authorize-service"))
|
if (requestType.startsWith("authorize-service"))
|
||||||
return I18n.tr("Authorize")
|
return I18n.tr("Authorize");
|
||||||
return I18n.tr("Pair")
|
return I18n.tr("Pair");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -331,7 +337,7 @@ DankModal {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
submitPairing()
|
submitPairing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,48 +362,48 @@ DankModal {
|
|||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (token) {
|
if (token) {
|
||||||
DMSService.bluetoothCancelPairing(token)
|
DMSService.bluetoothCancelPairing(token);
|
||||||
}
|
}
|
||||||
close()
|
close();
|
||||||
token = ""
|
token = "";
|
||||||
pinInput = ""
|
pinInput = "";
|
||||||
passkeyInput = ""
|
passkeyInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitPairing() {
|
function submitPairing() {
|
||||||
const secrets = {}
|
const secrets = {};
|
||||||
|
|
||||||
switch (requestType) {
|
switch (requestType) {
|
||||||
case "pin":
|
case "pin":
|
||||||
secrets["pin"] = pinInput
|
secrets["pin"] = pinInput;
|
||||||
break
|
break;
|
||||||
case "passkey":
|
case "passkey":
|
||||||
secrets["passkey"] = passkeyInput
|
secrets["passkey"] = passkeyInput;
|
||||||
break
|
break;
|
||||||
case "confirm":
|
case "confirm":
|
||||||
case "display-passkey":
|
case "display-passkey":
|
||||||
case "authorize":
|
case "authorize":
|
||||||
secrets["decision"] = "yes"
|
secrets["decision"] = "yes";
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
if (requestType.startsWith("authorize-service")) {
|
if (requestType.startsWith("authorize-service")) {
|
||||||
secrets["decision"] = "yes"
|
secrets["decision"] = "yes";
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
DMSService.bluetoothSubmitPairing(token, secrets, true, response => {
|
DMSService.bluetoothSubmitPairing(token, secrets, true, response => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
ToastService.showError(I18n.tr("Pairing failed"), response.error)
|
ToastService.showError(I18n.tr("Pairing failed"), response.error);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
close()
|
close();
|
||||||
token = ""
|
token = "";
|
||||||
pinInput = ""
|
pinInput = "";
|
||||||
passkeyInput = ""
|
passkeyInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
DankModal {
|
DankModal {
|
||||||
id: clipboardHistoryModal
|
id: clipboardHistoryModal
|
||||||
|
|
||||||
layerNamespace: "dms:clipboard"
|
layerNamespace: "dms:clipboard"
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: [clipboardHistoryModal]
|
||||||
|
active: CompositorService.isHyprland && clipboardHistoryModal.shouldHaveFocus
|
||||||
|
}
|
||||||
|
|
||||||
property int totalCount: 0
|
property int totalCount: 0
|
||||||
property var clipboardEntries: []
|
property var clipboardEntries: []
|
||||||
property string searchText: ""
|
property string searchText: ""
|
||||||
@@ -25,119 +29,119 @@ DankModal {
|
|||||||
readonly property int maxConcurrentLoads: 3
|
readonly property int maxConcurrentLoads: 3
|
||||||
|
|
||||||
function updateFilteredModel() {
|
function updateFilteredModel() {
|
||||||
filteredClipboardModel.clear()
|
filteredClipboardModel.clear();
|
||||||
for (var i = 0; i < clipboardModel.count; i++) {
|
for (var i = 0; i < clipboardModel.count; i++) {
|
||||||
const entry = clipboardModel.get(i).entry
|
const entry = clipboardModel.get(i).entry;
|
||||||
if (searchText.trim().length === 0) {
|
if (searchText.trim().length === 0) {
|
||||||
filteredClipboardModel.append({
|
filteredClipboardModel.append({
|
||||||
"entry": entry
|
"entry": entry
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
const content = getEntryPreview(entry).toLowerCase()
|
const content = getEntryPreview(entry).toLowerCase();
|
||||||
if (content.includes(searchText.toLowerCase())) {
|
if (content.includes(searchText.toLowerCase())) {
|
||||||
filteredClipboardModel.append({
|
filteredClipboardModel.append({
|
||||||
"entry": entry
|
"entry": entry
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clipboardHistoryModal.totalCount = filteredClipboardModel.count
|
clipboardHistoryModal.totalCount = filteredClipboardModel.count;
|
||||||
if (filteredClipboardModel.count === 0) {
|
if (filteredClipboardModel.count === 0) {
|
||||||
keyboardNavigationActive = false
|
keyboardNavigationActive = false;
|
||||||
selectedIndex = 0
|
selectedIndex = 0;
|
||||||
} else if (selectedIndex >= filteredClipboardModel.count) {
|
} else if (selectedIndex >= filteredClipboardModel.count) {
|
||||||
selectedIndex = filteredClipboardModel.count - 1
|
selectedIndex = filteredClipboardModel.count - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
hide()
|
hide();
|
||||||
} else {
|
} else {
|
||||||
show()
|
show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
open()
|
open();
|
||||||
clipboardHistoryModal.searchText = ""
|
clipboardHistoryModal.searchText = "";
|
||||||
clipboardHistoryModal.activeImageLoads = 0
|
clipboardHistoryModal.activeImageLoads = 0;
|
||||||
clipboardHistoryModal.shouldHaveFocus = true
|
clipboardHistoryModal.shouldHaveFocus = true;
|
||||||
refreshClipboard()
|
refreshClipboard();
|
||||||
keyboardController.reset()
|
keyboardController.reset();
|
||||||
|
|
||||||
Qt.callLater(function () {
|
Qt.callLater(function () {
|
||||||
if (contentLoader.item && contentLoader.item.searchField) {
|
if (contentLoader.item && contentLoader.item.searchField) {
|
||||||
contentLoader.item.searchField.text = ""
|
contentLoader.item.searchField.text = "";
|
||||||
contentLoader.item.searchField.forceActiveFocus()
|
contentLoader.item.searchField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
close()
|
close();
|
||||||
clipboardHistoryModal.searchText = ""
|
clipboardHistoryModal.searchText = "";
|
||||||
clipboardHistoryModal.activeImageLoads = 0
|
clipboardHistoryModal.activeImageLoads = 0;
|
||||||
updateFilteredModel()
|
updateFilteredModel();
|
||||||
keyboardController.reset()
|
keyboardController.reset();
|
||||||
cleanupTempFiles()
|
cleanupTempFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupTempFiles() {
|
function cleanupTempFiles() {
|
||||||
Quickshell.execDetached(["sh", "-c", "rm -f /tmp/clipboard_*.png"])
|
Quickshell.execDetached(["sh", "-c", "rm -f /tmp/clipboard_*.png"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshClipboard() {
|
function refreshClipboard() {
|
||||||
clipboardProcesses.refresh()
|
clipboardProcesses.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyEntry(entry) {
|
function copyEntry(entry) {
|
||||||
const entryId = entry.split('\t')[0]
|
const entryId = entry.split('\t')[0];
|
||||||
Quickshell.execDetached(["sh", "-c", `cliphist decode ${entryId} | wl-copy`])
|
Quickshell.execDetached(["sh", "-c", `cliphist decode ${entryId} | wl-copy`]);
|
||||||
ToastService.showInfo(I18n.tr("Copied to clipboard"))
|
ToastService.showInfo(I18n.tr("Copied to clipboard"));
|
||||||
hide()
|
hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteEntry(entry) {
|
function deleteEntry(entry) {
|
||||||
clipboardProcesses.deleteEntry(entry)
|
clipboardProcesses.deleteEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearAll() {
|
function clearAll() {
|
||||||
clipboardProcesses.clearAll()
|
clipboardProcesses.clearAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEntryPreview(entry) {
|
function getEntryPreview(entry) {
|
||||||
let content = entry.replace(/^\s*\d+\s+/, "")
|
let content = entry.replace(/^\s*\d+\s+/, "");
|
||||||
if (content.includes("image/") || content.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(content)) {
|
if (content.includes("image/") || content.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(content)) {
|
||||||
const dimensionMatch = content.match(/(\d+)x(\d+)/)
|
const dimensionMatch = content.match(/(\d+)x(\d+)/);
|
||||||
if (dimensionMatch) {
|
if (dimensionMatch) {
|
||||||
return `Image ${dimensionMatch[1]}×${dimensionMatch[2]}`
|
return `Image ${dimensionMatch[1]}×${dimensionMatch[2]}`;
|
||||||
}
|
}
|
||||||
const typeMatch = content.match(/\b(png|jpg|jpeg|gif|bmp|webp)\b/i)
|
const typeMatch = content.match(/\b(png|jpg|jpeg|gif|bmp|webp)\b/i);
|
||||||
if (typeMatch) {
|
if (typeMatch) {
|
||||||
return `Image (${typeMatch[1].toUpperCase()})`
|
return `Image (${typeMatch[1].toUpperCase()})`;
|
||||||
}
|
}
|
||||||
return "Image"
|
return "Image";
|
||||||
}
|
}
|
||||||
if (content.length > ClipboardConstants.previewLength) {
|
if (content.length > ClipboardConstants.previewLength) {
|
||||||
return content.substring(0, ClipboardConstants.previewLength) + "..."
|
return content.substring(0, ClipboardConstants.previewLength) + "...";
|
||||||
}
|
}
|
||||||
return content
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEntryType(entry) {
|
function getEntryType(entry) {
|
||||||
if (entry.includes("image/") || entry.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(entry) || /\b(png|jpg|jpeg|gif|bmp|webp)\b/i.test(entry)) {
|
if (entry.includes("image/") || entry.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(entry) || /\b(png|jpg|jpeg|gif|bmp|webp)\b/i.test(entry)) {
|
||||||
return "image"
|
return "image";
|
||||||
}
|
}
|
||||||
if (entry.length > ClipboardConstants.longTextThreshold) {
|
if (entry.length > ClipboardConstants.longTextThreshold) {
|
||||||
return "long_text"
|
return "long_text";
|
||||||
}
|
}
|
||||||
return "text"
|
return "text";
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: false
|
visible: false
|
||||||
width: ClipboardConstants.modalWidth
|
modalWidth: ClipboardConstants.modalWidth
|
||||||
height: ClipboardConstants.modalHeight
|
modalHeight: ClipboardConstants.modalHeight
|
||||||
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
cornerRadius: Theme.cornerRadius
|
cornerRadius: Theme.cornerRadius
|
||||||
borderColor: Theme.outlineMedium
|
borderColor: Theme.outlineMedium
|
||||||
@@ -145,7 +149,7 @@ DankModal {
|
|||||||
enableShadow: true
|
enableShadow: true
|
||||||
onBackgroundClicked: hide()
|
onBackgroundClicked: hide()
|
||||||
modalFocusScope.Keys.onPressed: function (event) {
|
modalFocusScope.Keys.onPressed: function (event) {
|
||||||
keyboardController.handleKey(event)
|
keyboardController.handleKey(event);
|
||||||
}
|
}
|
||||||
content: clipboardContent
|
content: clipboardContent
|
||||||
|
|
||||||
@@ -160,12 +164,12 @@ DankModal {
|
|||||||
confirmButtonColor: Theme.primary
|
confirmButtonColor: Theme.primary
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
clipboardHistoryModal.shouldHaveFocus = false
|
clipboardHistoryModal.shouldHaveFocus = false;
|
||||||
} else if (clipboardHistoryModal.shouldBeVisible) {
|
} else if (clipboardHistoryModal.shouldBeVisible) {
|
||||||
clipboardHistoryModal.shouldHaveFocus = true
|
clipboardHistoryModal.shouldHaveFocus = true;
|
||||||
clipboardHistoryModal.modalFocusScope.forceActiveFocus()
|
clipboardHistoryModal.modalFocusScope.forceActiveFocus();
|
||||||
if (clipboardHistoryModal.contentLoader.item && clipboardHistoryModal.contentLoader.item.searchField) {
|
if (clipboardHistoryModal.contentLoader.item && clipboardHistoryModal.contentLoader.item.searchField) {
|
||||||
clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus()
|
clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,18 +196,18 @@ DankModal {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open(): string {
|
function open(): string {
|
||||||
clipboardHistoryModal.show()
|
clipboardHistoryModal.show();
|
||||||
return "CLIPBOARD_OPEN_SUCCESS"
|
return "CLIPBOARD_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): string {
|
function close(): string {
|
||||||
clipboardHistoryModal.hide()
|
clipboardHistoryModal.hide();
|
||||||
return "CLIPBOARD_CLOSE_SUCCESS"
|
return "CLIPBOARD_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
clipboardHistoryModal.toggle()
|
clipboardHistoryModal.toggle();
|
||||||
return "CLIPBOARD_TOGGLE_SUCCESS"
|
return "CLIPBOARD_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "clipboard"
|
target: "clipboard"
|
||||||
|
|||||||
@@ -19,141 +19,141 @@ DankModal {
|
|||||||
property bool keyboardNavigation: false
|
property bool keyboardNavigation: false
|
||||||
|
|
||||||
function show(title, message, onConfirmCallback, onCancelCallback) {
|
function show(title, message, onConfirmCallback, onCancelCallback) {
|
||||||
confirmTitle = title || ""
|
confirmTitle = title || "";
|
||||||
confirmMessage = message || ""
|
confirmMessage = message || "";
|
||||||
confirmButtonText = "Confirm"
|
confirmButtonText = "Confirm";
|
||||||
cancelButtonText = "Cancel"
|
cancelButtonText = "Cancel";
|
||||||
confirmButtonColor = Theme.primary
|
confirmButtonColor = Theme.primary;
|
||||||
onConfirm = onConfirmCallback || (() => {})
|
onConfirm = onConfirmCallback || (() => {});
|
||||||
onCancel = onCancelCallback || (() => {})
|
onCancel = onCancelCallback || (() => {});
|
||||||
selectedButton = -1
|
selectedButton = -1;
|
||||||
keyboardNavigation = false
|
keyboardNavigation = false;
|
||||||
open()
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWithOptions(options) {
|
function showWithOptions(options) {
|
||||||
confirmTitle = options.title || ""
|
confirmTitle = options.title || "";
|
||||||
confirmMessage = options.message || ""
|
confirmMessage = options.message || "";
|
||||||
confirmButtonText = options.confirmText || "Confirm"
|
confirmButtonText = options.confirmText || "Confirm";
|
||||||
cancelButtonText = options.cancelText || "Cancel"
|
cancelButtonText = options.cancelText || "Cancel";
|
||||||
confirmButtonColor = options.confirmColor || Theme.primary
|
confirmButtonColor = options.confirmColor || Theme.primary;
|
||||||
onConfirm = options.onConfirm || (() => {})
|
onConfirm = options.onConfirm || (() => {});
|
||||||
onCancel = options.onCancel || (() => {})
|
onCancel = options.onCancel || (() => {});
|
||||||
selectedButton = -1
|
selectedButton = -1;
|
||||||
keyboardNavigation = false
|
keyboardNavigation = false;
|
||||||
open()
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectButton() {
|
function selectButton() {
|
||||||
close()
|
close();
|
||||||
if (selectedButton === 0) {
|
if (selectedButton === 0) {
|
||||||
if (onCancel) {
|
if (onCancel) {
|
||||||
onCancel()
|
onCancel();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (onConfirm) {
|
if (onConfirm) {
|
||||||
onConfirm()
|
onConfirm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
width: 350
|
modalWidth: 350
|
||||||
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 160
|
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 160
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
shouldHaveFocus: true
|
shouldHaveFocus: true
|
||||||
onBackgroundClicked: {
|
onBackgroundClicked: {
|
||||||
close()
|
close();
|
||||||
if (onCancel) {
|
if (onCancel) {
|
||||||
onCancel()
|
onCancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onOpened: {
|
onOpened: {
|
||||||
Qt.callLater(function () {
|
Qt.callLater(function () {
|
||||||
modalFocusScope.forceActiveFocus()
|
modalFocusScope.forceActiveFocus();
|
||||||
modalFocusScope.focus = true
|
modalFocusScope.focus = true;
|
||||||
shouldHaveFocus = true
|
shouldHaveFocus = true;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
modalFocusScope.Keys.onPressed: function (event) {
|
modalFocusScope.Keys.onPressed: function (event) {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case Qt.Key_Escape:
|
case Qt.Key_Escape:
|
||||||
close()
|
close();
|
||||||
if (onCancel) {
|
if (onCancel) {
|
||||||
onCancel()
|
onCancel();
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_Left:
|
case Qt.Key_Left:
|
||||||
case Qt.Key_Up:
|
case Qt.Key_Up:
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true;
|
||||||
selectedButton = 0
|
selectedButton = 0;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_Right:
|
case Qt.Key_Right:
|
||||||
case Qt.Key_Down:
|
case Qt.Key_Down:
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true;
|
||||||
selectedButton = 1
|
selectedButton = 1;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_N:
|
case Qt.Key_N:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true;
|
||||||
selectedButton = (selectedButton + 1) % 2
|
selectedButton = (selectedButton + 1) % 2;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_P:
|
case Qt.Key_P:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true;
|
||||||
selectedButton = selectedButton === -1 ? 1 : (selectedButton - 1 + 2) % 2
|
selectedButton = selectedButton === -1 ? 1 : (selectedButton - 1 + 2) % 2;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_J:
|
case Qt.Key_J:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true;
|
||||||
selectedButton = 1
|
selectedButton = 1;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_K:
|
case Qt.Key_K:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true;
|
||||||
selectedButton = 0
|
selectedButton = 0;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_H:
|
case Qt.Key_H:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true;
|
||||||
selectedButton = 0
|
selectedButton = 0;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_L:
|
case Qt.Key_L:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true;
|
||||||
selectedButton = 1
|
selectedButton = 1;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_Tab:
|
case Qt.Key_Tab:
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true;
|
||||||
selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2
|
selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_Return:
|
case Qt.Key_Return:
|
||||||
case Qt.Key_Enter:
|
case Qt.Key_Enter:
|
||||||
if (selectedButton !== -1) {
|
if (selectedButton !== -1) {
|
||||||
selectButton()
|
selectButton();
|
||||||
} else {
|
} else {
|
||||||
selectedButton = 1
|
selectedButton = 1;
|
||||||
selectButton()
|
selectButton();
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,11 +210,11 @@ DankModal {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
if (keyboardNavigation && selectedButton === 0) {
|
if (keyboardNavigation && selectedButton === 0) {
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
||||||
} else if (cancelButton.containsMouse) {
|
} else if (cancelButton.containsMouse) {
|
||||||
return Theme.surfacePressed
|
return Theme.surfacePressed;
|
||||||
} else {
|
} else {
|
||||||
return Theme.surfaceVariantAlpha
|
return Theme.surfaceVariantAlpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
border.color: (keyboardNavigation && selectedButton === 0) ? Theme.primary : "transparent"
|
border.color: (keyboardNavigation && selectedButton === 0) ? Theme.primary : "transparent"
|
||||||
@@ -235,8 +235,8 @@ DankModal {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
selectedButton = 0
|
selectedButton = 0;
|
||||||
selectButton()
|
selectButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,13 +246,13 @@ DankModal {
|
|||||||
height: 40
|
height: 40
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
const baseColor = confirmButtonColor
|
const baseColor = confirmButtonColor;
|
||||||
if (keyboardNavigation && selectedButton === 1) {
|
if (keyboardNavigation && selectedButton === 1) {
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 1)
|
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 1);
|
||||||
} else if (confirmButton.containsMouse) {
|
} else if (confirmButton.containsMouse) {
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9)
|
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9);
|
||||||
} else {
|
} else {
|
||||||
return baseColor
|
return baseColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
border.color: (keyboardNavigation && selectedButton === 1) ? "white" : "transparent"
|
border.color: (keyboardNavigation && selectedButton === 1) ? "white" : "transparent"
|
||||||
@@ -273,8 +273,8 @@ DankModal {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
selectedButton = 1
|
selectedButton = 1;
|
||||||
selectButton()
|
selectButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
PanelWindow {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string layerNamespace: "dms:modal"
|
property string layerNamespace: "dms:modal"
|
||||||
WlrLayershell.namespace: layerNamespace
|
|
||||||
|
|
||||||
property alias content: contentLoader.sourceComponent
|
property alias content: contentLoader.sourceComponent
|
||||||
property alias contentLoader: contentLoader
|
property alias contentLoader: contentLoader
|
||||||
property Item directContent: null
|
property Item directContent: null
|
||||||
property real width: 400
|
property real modalWidth: 400
|
||||||
property real height: 300
|
property real modalHeight: 300
|
||||||
readonly property real screenWidth: screen ? screen.width : 1920
|
property var targetScreen
|
||||||
readonly property real screenHeight: screen ? screen.height : 1080
|
readonly property var effectiveScreen: targetScreen || contentWindow.screen
|
||||||
readonly property real dpr: CompositorService.getScreenScale(screen)
|
readonly property real screenWidth: effectiveScreen?.width
|
||||||
|
readonly property real screenHeight: effectiveScreen?.height
|
||||||
|
readonly property real dpr: effectiveScreen ? CompositorService.getScreenScale(effectiveScreen) : 1
|
||||||
property bool showBackground: true
|
property bool showBackground: true
|
||||||
property real backgroundOpacity: 0.5
|
property real backgroundOpacity: 0.5
|
||||||
property string positioning: "center"
|
property string positioning: "center"
|
||||||
@@ -44,295 +43,340 @@ PanelWindow {
|
|||||||
property bool allowStacking: false
|
property bool allowStacking: false
|
||||||
property bool keepContentLoaded: false
|
property bool keepContentLoaded: false
|
||||||
property bool keepPopoutsOpen: false
|
property bool keepPopoutsOpen: false
|
||||||
|
property var customKeyboardFocus: null
|
||||||
|
|
||||||
signal opened
|
signal opened
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
signal backgroundClicked
|
signal backgroundClicked
|
||||||
|
|
||||||
|
readonly property bool useBackgroundWindow: {
|
||||||
|
const layerOverride = Quickshell.env("DMS_MODAL_LAYER");
|
||||||
|
return !layerOverride || layerOverride === "overlay";
|
||||||
|
}
|
||||||
|
|
||||||
function open() {
|
function open() {
|
||||||
ModalManager.openModal(root)
|
ModalManager.openModal(root);
|
||||||
closeTimer.stop()
|
closeTimer.stop();
|
||||||
shouldBeVisible = true
|
shouldBeVisible = true;
|
||||||
visible = true
|
if (useBackgroundWindow)
|
||||||
shouldHaveFocus = false
|
backgroundWindow.visible = true;
|
||||||
|
contentWindow.visible = true;
|
||||||
|
shouldHaveFocus = false;
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
shouldHaveFocus = Qt.binding(() => shouldBeVisible)
|
shouldHaveFocus = Qt.binding(() => shouldBeVisible);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
shouldBeVisible = false
|
shouldBeVisible = false;
|
||||||
shouldHaveFocus = false
|
shouldHaveFocus = false;
|
||||||
closeTimer.restart()
|
closeTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (shouldBeVisible) {
|
shouldBeVisible ? close() : open();
|
||||||
close()
|
|
||||||
} else {
|
|
||||||
open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visible: shouldBeVisible
|
|
||||||
color: "transparent"
|
|
||||||
WlrLayershell.layer: {
|
|
||||||
switch (Quickshell.env("DMS_MODAL_LAYER")) {
|
|
||||||
case "bottom":
|
|
||||||
return WlrLayershell.Bottom
|
|
||||||
case "overlay":
|
|
||||||
return WlrLayershell.Overlay
|
|
||||||
case "background":
|
|
||||||
return WlrLayershell.Background
|
|
||||||
default:
|
|
||||||
return WlrLayershell.Top
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WlrLayershell.exclusiveZone: -1
|
|
||||||
WlrLayershell.keyboardFocus: {
|
|
||||||
if (!shouldHaveFocus) return WlrKeyboardFocus.None
|
|
||||||
if (CompositorService.isHyprland) return WlrKeyboardFocus.OnDemand
|
|
||||||
return WlrKeyboardFocus.Exclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [root]
|
|
||||||
active: CompositorService.isHyprland && shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (root.visible) {
|
|
||||||
opened()
|
|
||||||
} else {
|
|
||||||
if (Qt.inputMethod) {
|
|
||||||
Qt.inputMethod.hide()
|
|
||||||
Qt.inputMethod.reset()
|
|
||||||
}
|
|
||||||
dialogClosed()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
target: ModalManager
|
||||||
function onCloseAllModalsExcept(excludedModal) {
|
function onCloseAllModalsExcept(excludedModal) {
|
||||||
if (excludedModal !== root && !allowStacking && shouldBeVisible) {
|
if (excludedModal !== root && !allowStacking && shouldBeVisible) {
|
||||||
close()
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target: ModalManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: closeTimer
|
id: closeTimer
|
||||||
|
|
||||||
interval: animationDuration + 120
|
interval: animationDuration + 120
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
visible = false
|
if (!shouldBeVisible) {
|
||||||
}
|
contentWindow.visible = false;
|
||||||
}
|
if (useBackgroundWindow)
|
||||||
|
backgroundWindow.visible = false;
|
||||||
anchors {
|
dialogClosed();
|
||||||
top: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: root.closeOnBackgroundClick && root.shouldBeVisible
|
|
||||||
onClicked: mouse => {
|
|
||||||
const localPos = mapToItem(contentContainer, mouse.x, mouse.y)
|
|
||||||
if (localPos.x < 0 || localPos.x > contentContainer.width || localPos.y < 0 || localPos.y > contentContainer.height) {
|
|
||||||
root.backgroundClicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
color: "black"
|
|
||||||
opacity: root.showBackground && SettingsData.modalDarkenBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
|
||||||
visible: root.showBackground && SettingsData.modalDarkenBackground
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: root.animationDuration
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
readonly property real shadowBuffer: 5
|
||||||
id: modalContainer
|
readonly property real alignedWidth: Theme.px(modalWidth, dpr)
|
||||||
|
readonly property real alignedHeight: Theme.px(modalHeight, dpr)
|
||||||
|
|
||||||
width: Theme.px(root.width, dpr)
|
readonly property real alignedX: Theme.snap((() => {
|
||||||
height: Theme.px(root.height, dpr)
|
switch (positioning) {
|
||||||
x: {
|
case "center":
|
||||||
if (positioning === "center") {
|
return (screenWidth - alignedWidth) / 2;
|
||||||
return Theme.snap((root.screenWidth - width) / 2, dpr)
|
case "top-right":
|
||||||
} else if (positioning === "top-right") {
|
return Math.max(Theme.spacingL, screenWidth - alignedWidth - Theme.spacingL);
|
||||||
return Theme.px(Math.max(Theme.spacingL, root.screenWidth - width - Theme.spacingL), dpr)
|
case "custom":
|
||||||
} else if (positioning === "custom") {
|
return customPosition.x;
|
||||||
return Theme.snap(root.customPosition.x, dpr)
|
default:
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0
|
})(), dpr)
|
||||||
}
|
|
||||||
y: {
|
readonly property real alignedY: Theme.snap((() => {
|
||||||
if (positioning === "center") {
|
switch (positioning) {
|
||||||
return Theme.snap((root.screenHeight - height) / 2, dpr)
|
case "center":
|
||||||
} else if (positioning === "top-right") {
|
return (screenHeight - alignedHeight) / 2;
|
||||||
return Theme.px(Theme.barHeight + Theme.spacingXS, dpr)
|
case "top-right":
|
||||||
} else if (positioning === "custom") {
|
return Theme.barHeight + Theme.spacingXS;
|
||||||
return Theme.snap(root.customPosition.y, dpr)
|
case "custom":
|
||||||
|
return customPosition.y;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0
|
})(), dpr)
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: backgroundWindow
|
||||||
|
visible: false
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
WlrLayershell.namespace: root.layerNamespace + ":background"
|
||||||
|
WlrLayershell.layer: WlrLayershell.Top
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property bool slide: root.animationType === "slide"
|
MouseArea {
|
||||||
readonly property real offsetX: slide ? 15 : 0
|
anchors.fill: parent
|
||||||
readonly property real offsetY: slide ? -30 : root.animationOffset
|
enabled: root.closeOnBackgroundClick && root.shouldBeVisible
|
||||||
|
onClicked: mouse => {
|
||||||
|
const clickX = mouse.x;
|
||||||
|
const clickY = mouse.y;
|
||||||
|
const outsideContent = clickX < root.alignedX || clickX > root.alignedX + root.alignedWidth || clickY < root.alignedY || clickY > root.alignedY + root.alignedHeight;
|
||||||
|
|
||||||
property real animX: 0
|
if (!outsideContent)
|
||||||
property real animY: 0
|
return;
|
||||||
property real scaleValue: root.animationScaleCollapsed
|
root.backgroundClicked();
|
||||||
|
|
||||||
onOffsetXChanged: animX = Theme.snap(root.shouldBeVisible ? 0 : offsetX, root.dpr)
|
|
||||||
onOffsetYChanged: animY = Theme.snap(root.shouldBeVisible ? 0 : offsetY, root.dpr)
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onShouldBeVisibleChanged() {
|
|
||||||
modalContainer.animX = Theme.snap(root.shouldBeVisible ? 0 : modalContainer.offsetX, root.dpr)
|
|
||||||
modalContainer.animY = Theme.snap(root.shouldBeVisible ? 0 : modalContainer.offsetY, root.dpr)
|
|
||||||
modalContainer.scaleValue = root.shouldBeVisible ? 1.0 : root.animationScaleCollapsed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on animX {
|
Rectangle {
|
||||||
NumberAnimation {
|
id: background
|
||||||
duration: root.animationDuration
|
anchors.fill: parent
|
||||||
easing.type: Easing.BezierSpline
|
color: "black"
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
opacity: root.showBackground && SettingsData.modalDarkenBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
||||||
|
visible: root.showBackground && SettingsData.modalDarkenBackground
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animationDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on animY {
|
PanelWindow {
|
||||||
NumberAnimation {
|
id: contentWindow
|
||||||
duration: root.animationDuration
|
visible: false
|
||||||
easing.type: Easing.BezierSpline
|
color: "transparent"
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
|
||||||
|
WlrLayershell.namespace: root.layerNamespace
|
||||||
|
WlrLayershell.layer: {
|
||||||
|
switch (Quickshell.env("DMS_MODAL_LAYER")) {
|
||||||
|
case "bottom":
|
||||||
|
return WlrLayershell.Bottom;
|
||||||
|
case "top":
|
||||||
|
return WlrLayershell.Top;
|
||||||
|
case "background":
|
||||||
|
return WlrLayershell.Background;
|
||||||
|
default:
|
||||||
|
return WlrLayershell.Overlay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: {
|
||||||
|
if (customKeyboardFocus !== null)
|
||||||
|
return customKeyboardFocus;
|
||||||
|
if (!shouldHaveFocus)
|
||||||
|
return WlrKeyboardFocus.None;
|
||||||
|
if (CompositorService.isHyprland)
|
||||||
|
return WlrKeyboardFocus.OnDemand;
|
||||||
|
return WlrKeyboardFocus.Exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on scaleValue {
|
anchors {
|
||||||
NumberAnimation {
|
left: true
|
||||||
duration: root.animationDuration
|
top: true
|
||||||
easing.type: Easing.BezierSpline
|
}
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
|
||||||
|
WlrLayershell.margins {
|
||||||
|
left: Math.max(0, Theme.snap(root.alignedX - shadowBuffer, dpr))
|
||||||
|
top: Math.max(0, Theme.snap(root.alignedY - shadowBuffer, dpr))
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: root.alignedWidth + (shadowBuffer * 2)
|
||||||
|
implicitHeight: root.alignedHeight + (shadowBuffer * 2)
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
opened();
|
||||||
|
} else {
|
||||||
|
if (Qt.inputMethod) {
|
||||||
|
Qt.inputMethod.hide();
|
||||||
|
Qt.inputMethod.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: contentContainer
|
id: modalContainer
|
||||||
|
x: shadowBuffer
|
||||||
|
y: shadowBuffer
|
||||||
|
width: root.alignedWidth
|
||||||
|
height: root.alignedHeight
|
||||||
|
|
||||||
anchors.centerIn: parent
|
readonly property bool slide: root.animationType === "slide"
|
||||||
width: parent.width
|
readonly property real offsetX: slide ? 15 : 0
|
||||||
height: parent.height
|
readonly property real offsetY: slide ? -30 : root.animationOffset
|
||||||
clip: false
|
|
||||||
layer.enabled: true
|
|
||||||
layer.smooth: false
|
|
||||||
layer.textureSize: Qt.size(width * root.dpr, height * root.dpr)
|
|
||||||
opacity: root.shouldBeVisible ? 1 : 0
|
|
||||||
scale: modalContainer.scaleValue
|
|
||||||
x: Theme.snap(modalContainer.animX + (parent.width - width) * (1 - modalContainer.scaleValue) * 0.5, root.dpr)
|
|
||||||
y: Theme.snap(modalContainer.animY + (parent.height - height) * (1 - modalContainer.scaleValue) * 0.5, root.dpr)
|
|
||||||
|
|
||||||
Behavior on opacity {
|
property real animX: 0
|
||||||
|
property real animY: 0
|
||||||
|
property real scaleValue: root.animationScaleCollapsed
|
||||||
|
|
||||||
|
onOffsetXChanged: animX = Theme.snap(root.shouldBeVisible ? 0 : offsetX, root.dpr)
|
||||||
|
onOffsetYChanged: animY = Theme.snap(root.shouldBeVisible ? 0 : offsetY, root.dpr)
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onShouldBeVisibleChanged() {
|
||||||
|
modalContainer.animX = Theme.snap(root.shouldBeVisible ? 0 : modalContainer.offsetX, root.dpr);
|
||||||
|
modalContainer.animY = Theme.snap(root.shouldBeVisible ? 0 : modalContainer.offsetY, root.dpr);
|
||||||
|
modalContainer.scaleValue = root.shouldBeVisible ? 1.0 : root.animationScaleCollapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on animX {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: animationDuration
|
duration: root.animationDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankRectangle {
|
Behavior on animY {
|
||||||
anchors.fill: parent
|
NumberAnimation {
|
||||||
color: root.backgroundColor
|
duration: root.animationDuration
|
||||||
borderColor: root.borderColor
|
easing.type: Easing.BezierSpline
|
||||||
borderWidth: root.borderWidth
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
radius: root.cornerRadius
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FocusScope {
|
Behavior on scaleValue {
|
||||||
anchors.fill: parent
|
NumberAnimation {
|
||||||
focus: root.shouldBeVisible
|
duration: root.animationDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentContainer
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
clip: false
|
clip: false
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: directContentWrapper
|
id: animatedContent
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: root.directContent !== null
|
|
||||||
focus: true
|
|
||||||
clip: false
|
clip: false
|
||||||
|
opacity: root.shouldBeVisible ? 1 : 0
|
||||||
|
scale: modalContainer.scaleValue
|
||||||
|
x: Theme.snap(modalContainer.animX, root.dpr) + (parent.width - width) * (1 - modalContainer.scaleValue) * 0.5
|
||||||
|
y: Theme.snap(modalContainer.animY, root.dpr) + (parent.height - height) * (1 - modalContainer.scaleValue) * 0.5
|
||||||
|
|
||||||
Component.onCompleted: {
|
Behavior on opacity {
|
||||||
if (root.directContent) {
|
NumberAnimation {
|
||||||
root.directContent.parent = directContentWrapper
|
duration: animationDuration
|
||||||
root.directContent.anchors.fill = directContentWrapper
|
easing.type: Easing.BezierSpline
|
||||||
Qt.callLater(() => root.directContent.forceActiveFocus())
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
DankRectangle {
|
||||||
function onDirectContentChanged() {
|
anchors.fill: parent
|
||||||
if (root.directContent) {
|
color: root.backgroundColor
|
||||||
root.directContent.parent = directContentWrapper
|
borderColor: root.borderColor
|
||||||
root.directContent.anchors.fill = directContentWrapper
|
borderWidth: root.borderWidth
|
||||||
Qt.callLater(() => root.directContent.forceActiveFocus())
|
radius: root.cornerRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: root.shouldBeVisible
|
||||||
|
clip: false
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: directContentWrapper
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: root.directContent !== null
|
||||||
|
focus: true
|
||||||
|
clip: false
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (root.directContent) {
|
||||||
|
root.directContent.parent = directContentWrapper;
|
||||||
|
root.directContent.anchors.fill = directContentWrapper;
|
||||||
|
Qt.callLater(() => root.directContent.forceActiveFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onDirectContentChanged() {
|
||||||
|
if (root.directContent) {
|
||||||
|
root.directContent.parent = directContentWrapper;
|
||||||
|
root.directContent.anchors.fill = directContentWrapper;
|
||||||
|
Qt.callLater(() => root.directContent.forceActiveFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target: root
|
Loader {
|
||||||
}
|
id: contentLoader
|
||||||
}
|
anchors.fill: parent
|
||||||
|
active: root.directContent === null && (root.keepContentLoaded || root.shouldBeVisible || contentWindow.visible)
|
||||||
|
asynchronous: false
|
||||||
|
focus: true
|
||||||
|
clip: false
|
||||||
|
visible: root.directContent === null
|
||||||
|
|
||||||
Loader {
|
onLoaded: {
|
||||||
id: contentLoader
|
if (item) {
|
||||||
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
anchors.fill: parent
|
}
|
||||||
active: root.directContent === null && (root.keepContentLoaded || root.shouldBeVisible || root.visible)
|
}
|
||||||
asynchronous: false
|
|
||||||
focus: true
|
|
||||||
clip: false
|
|
||||||
visible: root.directContent === null
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
if (item) {
|
|
||||||
Qt.callLater(() => item.forceActiveFocus())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: focusScope
|
id: focusScope
|
||||||
|
objectName: "modalFocusScope"
|
||||||
objectName: "modalFocusScope"
|
anchors.fill: parent
|
||||||
anchors.fill: parent
|
visible: root.shouldBeVisible || contentWindow.visible
|
||||||
visible: root.shouldBeVisible || root.visible
|
focus: root.shouldBeVisible
|
||||||
focus: root.shouldBeVisible
|
Keys.onEscapePressed: event => {
|
||||||
Keys.onEscapePressed: event => {
|
if (root.closeOnEscapeKey && shouldHaveFocus) {
|
||||||
if (root.closeOnEscapeKey && shouldHaveFocus) {
|
root.close();
|
||||||
root.close()
|
event.accepted = true;
|
||||||
event.accepted = true
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -12,7 +11,12 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:color-picker"
|
layerNamespace: "dms:color-picker"
|
||||||
|
|
||||||
property string pickerTitle: "Choose Color"
|
HyprlandFocusGrab {
|
||||||
|
windows: [root]
|
||||||
|
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||||
|
}
|
||||||
|
|
||||||
|
property string pickerTitle: I18n.tr("Choose Color")
|
||||||
property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
|
property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
|
||||||
property var onColorSelectedCallback: null
|
property var onColorSelectedCallback: null
|
||||||
|
|
||||||
@@ -26,87 +30,79 @@ DankModal {
|
|||||||
property real gradientX: 0
|
property real gradientX: 0
|
||||||
property real gradientY: 0
|
property real gradientY: 0
|
||||||
|
|
||||||
readonly property var standardColors: [
|
readonly property var standardColors: ["#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#d32f2f", "#c2185b", "#7b1fa2", "#512da8", "#303f9f", "#1976d2", "#0288d1", "#0097a7", "#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19", "#c62828", "#ad1457", "#6a1b9a", "#4527a0", "#283593", "#1565c0", "#0277bd", "#00838f", "#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315", "#ffffff", "#9e9e9e", "#212121"]
|
||||||
"#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4",
|
|
||||||
"#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722",
|
|
||||||
"#d32f2f", "#c2185b", "#7b1fa2", "#512da8", "#303f9f", "#1976d2", "#0288d1", "#0097a7",
|
|
||||||
"#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19",
|
|
||||||
"#c62828", "#ad1457", "#6a1b9a", "#4527a0", "#283593", "#1565c0", "#0277bd", "#00838f",
|
|
||||||
"#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315",
|
|
||||||
"#ffffff", "#9e9e9e", "#212121"
|
|
||||||
]
|
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
currentColor = selectedColor
|
currentColor = selectedColor;
|
||||||
updateFromColor(currentColor)
|
updateFromColor(currentColor);
|
||||||
open()
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
onColorSelectedCallback = null
|
onColorSelectedCallback = null;
|
||||||
close()
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideInstant() {
|
function hideInstant() {
|
||||||
onColorSelectedCallback = null
|
onColorSelectedCallback = null;
|
||||||
shouldBeVisible = false
|
shouldBeVisible = false;
|
||||||
visible = false
|
visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onColorSelected: (color) => {
|
onColorSelected: color => {
|
||||||
if (onColorSelectedCallback) {
|
if (onColorSelectedCallback) {
|
||||||
onColorSelectedCallback(color)
|
onColorSelectedCallback(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyColorToClipboard(colorValue) {
|
function copyColorToClipboard(colorValue) {
|
||||||
Quickshell.execDetached(["sh", "-c", `echo -n "${colorValue}" | wl-copy`])
|
Quickshell.execDetached(["sh", "-c", `echo -n "${colorValue}" | wl-copy`]);
|
||||||
ToastService.showInfo(`Color ${colorValue} copied`)
|
ToastService.showInfo(`Color ${colorValue} copied`);
|
||||||
SessionData.addRecentColor(currentColor)
|
SessionData.addRecentColor(currentColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFromColor(color) {
|
function updateFromColor(color) {
|
||||||
hue = color.hsvHue
|
hue = color.hsvHue;
|
||||||
saturation = color.hsvSaturation
|
saturation = color.hsvSaturation;
|
||||||
value = color.hsvValue
|
value = color.hsvValue;
|
||||||
alpha = color.a
|
alpha = color.a;
|
||||||
gradientX = saturation
|
gradientX = saturation;
|
||||||
gradientY = 1 - value
|
gradientY = 1 - value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateColor() {
|
function updateColor() {
|
||||||
currentColor = Qt.hsva(hue, saturation, value, alpha)
|
currentColor = Qt.hsva(hue, saturation, value, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateColorFromGradient(x, y) {
|
function updateColorFromGradient(x, y) {
|
||||||
saturation = Math.max(0, Math.min(1, x))
|
saturation = Math.max(0, Math.min(1, x));
|
||||||
value = Math.max(0, Math.min(1, 1 - y))
|
value = Math.max(0, Math.min(1, 1 - y));
|
||||||
updateColor()
|
updateColor();
|
||||||
selectedColor = currentColor
|
selectedColor = currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickColorFromScreen() {
|
function pickColorFromScreen() {
|
||||||
hideInstant()
|
hideInstant();
|
||||||
Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (output, errorCode) => {
|
Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (output, errorCode) => {
|
||||||
if (errorCode !== 0) {
|
if (errorCode !== 0) {
|
||||||
console.warn("hyprpicker exited with code:", errorCode)
|
console.warn("hyprpicker exited with code:", errorCode);
|
||||||
root.show()
|
root.show();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const colorStr = output.trim()
|
const colorStr = output.trim();
|
||||||
if (colorStr.length >= 7 && colorStr.startsWith('#')) {
|
if (colorStr.length >= 7 && colorStr.startsWith('#')) {
|
||||||
const pickedColor = Qt.color(colorStr)
|
const pickedColor = Qt.color(colorStr);
|
||||||
root.selectedColor = pickedColor
|
root.selectedColor = pickedColor;
|
||||||
root.currentColor = pickedColor
|
root.currentColor = pickedColor;
|
||||||
root.updateFromColor(pickedColor)
|
root.updateFromColor(pickedColor);
|
||||||
copyColorToClipboard(colorStr)
|
copyColorToClipboard(colorStr);
|
||||||
root.show()
|
root.show();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
width: 680
|
modalWidth: 680
|
||||||
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 680
|
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 680
|
||||||
backgroundColor: Theme.surfaceContainer
|
backgroundColor: Theme.surfaceContainer
|
||||||
cornerRadius: Theme.cornerRadius
|
cornerRadius: Theme.cornerRadius
|
||||||
borderColor: Theme.outlineMedium
|
borderColor: Theme.outlineMedium
|
||||||
@@ -127,8 +123,8 @@ DankModal {
|
|||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
root.hide()
|
root.hide();
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -166,7 +162,7 @@ DankModal {
|
|||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 4
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
root.pickColorFromScreen()
|
root.pickColorFromScreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +171,7 @@ DankModal {
|
|||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 4
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
root.hide()
|
root.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,8 +197,14 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
gradient: Gradient {
|
gradient: Gradient {
|
||||||
orientation: Gradient.Horizontal
|
orientation: Gradient.Horizontal
|
||||||
GradientStop { position: 0.0; color: "#ffffff" }
|
GradientStop {
|
||||||
GradientStop { position: 1.0; color: "transparent" }
|
position: 0.0
|
||||||
|
color: "#ffffff"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1.0
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,8 +212,14 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
gradient: Gradient {
|
gradient: Gradient {
|
||||||
orientation: Gradient.Vertical
|
orientation: Gradient.Vertical
|
||||||
GradientStop { position: 0.0; color: "transparent" }
|
GradientStop {
|
||||||
GradientStop { position: 1.0; color: "#000000" }
|
position: 0.0
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1.0
|
||||||
|
color: "#000000"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,19 +250,19 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.CrossCursor
|
cursorShape: Qt.CrossCursor
|
||||||
onPressed: mouse => {
|
onPressed: mouse => {
|
||||||
const x = Math.max(0, Math.min(1, mouse.x / width))
|
const x = Math.max(0, Math.min(1, mouse.x / width));
|
||||||
const y = Math.max(0, Math.min(1, mouse.y / height))
|
const y = Math.max(0, Math.min(1, mouse.y / height));
|
||||||
root.gradientX = x
|
root.gradientX = x;
|
||||||
root.gradientY = y
|
root.gradientY = y;
|
||||||
root.updateColorFromGradient(x, y)
|
root.updateColorFromGradient(x, y);
|
||||||
}
|
}
|
||||||
onPositionChanged: mouse => {
|
onPositionChanged: mouse => {
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
const x = Math.max(0, Math.min(1, mouse.x / width))
|
const x = Math.max(0, Math.min(1, mouse.x / width));
|
||||||
const y = Math.max(0, Math.min(1, mouse.y / height))
|
const y = Math.max(0, Math.min(1, mouse.y / height));
|
||||||
root.gradientX = x
|
root.gradientX = x;
|
||||||
root.gradientY = y
|
root.gradientY = y;
|
||||||
root.updateColorFromGradient(x, y)
|
root.updateColorFromGradient(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,13 +278,34 @@ DankModal {
|
|||||||
|
|
||||||
gradient: Gradient {
|
gradient: Gradient {
|
||||||
orientation: Gradient.Vertical
|
orientation: Gradient.Vertical
|
||||||
GradientStop { position: 0.00; color: "#ff0000" }
|
GradientStop {
|
||||||
GradientStop { position: 0.17; color: "#ffff00" }
|
position: 0.00
|
||||||
GradientStop { position: 0.33; color: "#00ff00" }
|
color: "#ff0000"
|
||||||
GradientStop { position: 0.50; color: "#00ffff" }
|
}
|
||||||
GradientStop { position: 0.67; color: "#0000ff" }
|
GradientStop {
|
||||||
GradientStop { position: 0.83; color: "#ff00ff" }
|
position: 0.17
|
||||||
GradientStop { position: 1.00; color: "#ff0000" }
|
color: "#ffff00"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.33
|
||||||
|
color: "#00ff00"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.50
|
||||||
|
color: "#00ffff"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.67
|
||||||
|
color: "#0000ff"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.83
|
||||||
|
color: "#ff00ff"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1.00
|
||||||
|
color: "#ff0000"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -293,17 +322,17 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.SizeVerCursor
|
cursorShape: Qt.SizeVerCursor
|
||||||
onPressed: mouse => {
|
onPressed: mouse => {
|
||||||
const h = Math.max(0, Math.min(1, mouse.y / height))
|
const h = Math.max(0, Math.min(1, mouse.y / height));
|
||||||
root.hue = h
|
root.hue = h;
|
||||||
root.updateColor()
|
root.updateColor();
|
||||||
root.selectedColor = root.currentColor
|
root.selectedColor = root.currentColor;
|
||||||
}
|
}
|
||||||
onPositionChanged: mouse => {
|
onPositionChanged: mouse => {
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
const h = Math.max(0, Math.min(1, mouse.y / height))
|
const h = Math.max(0, Math.min(1, mouse.y / height));
|
||||||
root.hue = h
|
root.hue = h;
|
||||||
root.updateColor()
|
root.updateColor();
|
||||||
root.selectedColor = root.currentColor
|
root.selectedColor = root.currentColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,10 +371,10 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
const pickedColor = Qt.color(modelData)
|
const pickedColor = Qt.color(modelData);
|
||||||
root.selectedColor = pickedColor
|
root.selectedColor = pickedColor;
|
||||||
root.currentColor = pickedColor
|
root.currentColor = pickedColor;
|
||||||
root.updateFromColor(pickedColor)
|
root.updateFromColor(pickedColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,9 +416,9 @@ DankModal {
|
|||||||
|
|
||||||
color: {
|
color: {
|
||||||
if (index < SessionData.recentColors.length) {
|
if (index < SessionData.recentColors.length) {
|
||||||
return SessionData.recentColors[index]
|
return SessionData.recentColors[index];
|
||||||
}
|
}
|
||||||
return Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
return Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency);
|
||||||
}
|
}
|
||||||
|
|
||||||
opacity: index < SessionData.recentColors.length ? 1.0 : 0.3
|
opacity: index < SessionData.recentColors.length ? 1.0 : 0.3
|
||||||
@@ -400,10 +429,10 @@ DankModal {
|
|||||||
enabled: index < SessionData.recentColors.length
|
enabled: index < SessionData.recentColors.length
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (index < SessionData.recentColors.length) {
|
if (index < SessionData.recentColors.length) {
|
||||||
const pickedColor = SessionData.recentColors[index]
|
const pickedColor = SessionData.recentColors[index];
|
||||||
root.selectedColor = pickedColor
|
root.selectedColor = pickedColor;
|
||||||
root.currentColor = pickedColor
|
root.currentColor = pickedColor;
|
||||||
root.updateFromColor(pickedColor)
|
root.updateFromColor(pickedColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -429,10 +458,10 @@ DankModal {
|
|||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: 100
|
||||||
showValue: false
|
showValue: false
|
||||||
onSliderValueChanged: (newValue) => {
|
onSliderValueChanged: newValue => {
|
||||||
root.alpha = newValue / 100
|
root.alpha = newValue / 100;
|
||||||
root.updateColor()
|
root.updateColor();
|
||||||
root.selectedColor = root.currentColor
|
root.selectedColor = root.currentColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -479,9 +508,10 @@ DankModal {
|
|||||||
text: root.currentColor.toString()
|
text: root.currentColor.toString()
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
textColor: {
|
textColor: {
|
||||||
if (text.length === 0) return Theme.surfaceText
|
if (text.length === 0)
|
||||||
const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/
|
return Theme.surfaceText;
|
||||||
return hexPattern.test(text) ? Theme.surfaceText : Theme.error
|
const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/;
|
||||||
|
return hexPattern.test(text) ? Theme.surfaceText : Theme.error;
|
||||||
}
|
}
|
||||||
placeholderText: "#000000"
|
placeholderText: "#000000"
|
||||||
backgroundColor: Theme.surfaceHover
|
backgroundColor: Theme.surfaceHover
|
||||||
@@ -490,13 +520,14 @@ DankModal {
|
|||||||
topPadding: Theme.spacingS
|
topPadding: Theme.spacingS
|
||||||
bottomPadding: Theme.spacingS
|
bottomPadding: Theme.spacingS
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/
|
const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/;
|
||||||
if (!hexPattern.test(text)) return
|
if (!hexPattern.test(text))
|
||||||
const color = Qt.color(text)
|
return;
|
||||||
|
const color = Qt.color(text);
|
||||||
if (color) {
|
if (color) {
|
||||||
root.selectedColor = color
|
root.selectedColor = color;
|
||||||
root.currentColor = color
|
root.currentColor = color;
|
||||||
root.updateFromColor(color)
|
root.updateFromColor(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -508,7 +539,7 @@ DankModal {
|
|||||||
buttonSize: 36
|
buttonSize: 36
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
root.copyColorToClipboard(hexInput.text)
|
root.copyColorToClipboard(hexInput.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -540,14 +571,14 @@ DankModal {
|
|||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: {
|
text: {
|
||||||
const r = Math.round(root.currentColor.r * 255)
|
const r = Math.round(root.currentColor.r * 255);
|
||||||
const g = Math.round(root.currentColor.g * 255)
|
const g = Math.round(root.currentColor.g * 255);
|
||||||
const b = Math.round(root.currentColor.b * 255)
|
const b = Math.round(root.currentColor.b * 255);
|
||||||
if (root.alpha < 1) {
|
if (root.alpha < 1) {
|
||||||
const a = Math.round(root.alpha * 255)
|
const a = Math.round(root.alpha * 255);
|
||||||
return `${r}, ${g}, ${b}, ${a}`
|
return `${r}, ${g}, ${b}, ${a}`;
|
||||||
}
|
}
|
||||||
return `${r}, ${g}, ${b}`
|
return `${r}, ${g}, ${b}`;
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -561,18 +592,18 @@ DankModal {
|
|||||||
buttonSize: 36
|
buttonSize: 36
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
const r = Math.round(root.currentColor.r * 255)
|
const r = Math.round(root.currentColor.r * 255);
|
||||||
const g = Math.round(root.currentColor.g * 255)
|
const g = Math.round(root.currentColor.g * 255);
|
||||||
const b = Math.round(root.currentColor.b * 255)
|
const b = Math.round(root.currentColor.b * 255);
|
||||||
let rgbString
|
let rgbString;
|
||||||
if (root.alpha < 1) {
|
if (root.alpha < 1) {
|
||||||
const a = Math.round(root.alpha * 255)
|
const a = Math.round(root.alpha * 255);
|
||||||
rgbString = `rgba(${r}, ${g}, ${b}, ${a})`
|
rgbString = `rgba(${r}, ${g}, ${b}, ${a})`;
|
||||||
} else {
|
} else {
|
||||||
rgbString = `rgb(${r}, ${g}, ${b})`
|
rgbString = `rgb(${r}, ${g}, ${b})`;
|
||||||
}
|
}
|
||||||
Quickshell.execDetached(["sh", "-c", `echo -n "${rgbString}" | wl-copy`])
|
Quickshell.execDetached(["sh", "-c", `echo -n "${rgbString}" | wl-copy`]);
|
||||||
ToastService.showInfo(`${rgbString} copied`)
|
ToastService.showInfo(`${rgbString} copied`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -604,14 +635,14 @@ DankModal {
|
|||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: {
|
text: {
|
||||||
const h = Math.round(root.hue * 360)
|
const h = Math.round(root.hue * 360);
|
||||||
const s = Math.round(root.saturation * 100)
|
const s = Math.round(root.saturation * 100);
|
||||||
const v = Math.round(root.value * 100)
|
const v = Math.round(root.value * 100);
|
||||||
if (root.alpha < 1) {
|
if (root.alpha < 1) {
|
||||||
const a = Math.round(root.alpha * 100)
|
const a = Math.round(root.alpha * 100);
|
||||||
return `${h}°, ${s}%, ${v}%, ${a}%`
|
return `${h}°, ${s}%, ${v}%, ${a}%`;
|
||||||
}
|
}
|
||||||
return `${h}°, ${s}%, ${v}%`
|
return `${h}°, ${s}%, ${v}%`;
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -625,18 +656,18 @@ DankModal {
|
|||||||
buttonSize: 36
|
buttonSize: 36
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
const h = Math.round(root.hue * 360)
|
const h = Math.round(root.hue * 360);
|
||||||
const s = Math.round(root.saturation * 100)
|
const s = Math.round(root.saturation * 100);
|
||||||
const v = Math.round(root.value * 100)
|
const v = Math.round(root.value * 100);
|
||||||
let hsvString
|
let hsvString;
|
||||||
if (root.alpha < 1) {
|
if (root.alpha < 1) {
|
||||||
const a = Math.round(root.alpha * 100)
|
const a = Math.round(root.alpha * 100);
|
||||||
hsvString = `${h}, ${s}, ${v}, ${a}`
|
hsvString = `${h}, ${s}, ${v}, ${a}`;
|
||||||
} else {
|
} else {
|
||||||
hsvString = `${h}, ${s}, ${v}`
|
hsvString = `${h}, ${s}, ${v}`;
|
||||||
}
|
}
|
||||||
Quickshell.execDetached(["sh", "-c", `echo -n "${hsvString}" | wl-copy`])
|
Quickshell.execDetached(["sh", "-c", `echo -n "${hsvString}" | wl-copy`]);
|
||||||
ToastService.showInfo(`HSV ${hsvString} copied`)
|
ToastService.showInfo(`HSV ${hsvString} copied`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -652,9 +683,9 @@ DankModal {
|
|||||||
textColor: Theme.background
|
textColor: Theme.background
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
onClicked: {
|
onClicked: {
|
||||||
SessionData.addRecentColor(root.currentColor)
|
SessionData.addRecentColor(root.currentColor);
|
||||||
root.colorSelected(root.currentColor)
|
root.colorSelected(root.currentColor);
|
||||||
root.hide()
|
root.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
DankModal {
|
DankModal {
|
||||||
@@ -15,8 +14,8 @@ DankModal {
|
|||||||
|
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
width: 420
|
modalWidth: 420
|
||||||
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 200
|
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 200
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: countdownTimer
|
id: countdownTimer
|
||||||
@@ -24,20 +23,20 @@ DankModal {
|
|||||||
repeat: true
|
repeat: true
|
||||||
running: root.shouldBeVisible
|
running: root.shouldBeVisible
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
countdown--
|
countdown--;
|
||||||
if (countdown <= 0) {
|
if (countdown <= 0) {
|
||||||
revert()
|
revert();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
countdown = 15
|
countdown = 15;
|
||||||
countdownTimer.start()
|
countdownTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
onClosed: {
|
onClosed: {
|
||||||
countdownTimer.stop()
|
countdownTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
onBackgroundClicked: revert
|
onBackgroundClicked: revert
|
||||||
@@ -51,13 +50,13 @@ DankModal {
|
|||||||
implicitHeight: mainColumn.implicitHeight
|
implicitHeight: mainColumn.implicitHeight
|
||||||
|
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
revert()
|
revert();
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onReturnPressed: event => {
|
Keys.onReturnPressed: event => {
|
||||||
confirm()
|
confirm();
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -235,12 +234,12 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function confirm() {
|
function confirm() {
|
||||||
displaysTab.confirmChanges()
|
displaysTab.confirmChanges();
|
||||||
close()
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function revert() {
|
function revert() {
|
||||||
displaysTab.revertChanges()
|
displaysTab.revertChanges();
|
||||||
close()
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -10,34 +9,36 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:network-info"
|
layerNamespace: "dms:network-info"
|
||||||
|
|
||||||
|
keepPopoutsOpen: true
|
||||||
|
|
||||||
property bool networkInfoModalVisible: false
|
property bool networkInfoModalVisible: false
|
||||||
property string networkSSID: ""
|
property string networkSSID: ""
|
||||||
property var networkData: null
|
property var networkData: null
|
||||||
|
|
||||||
function showNetworkInfo(ssid, data) {
|
function showNetworkInfo(ssid, data) {
|
||||||
networkSSID = ssid
|
networkSSID = ssid;
|
||||||
networkData = data
|
networkData = data;
|
||||||
networkInfoModalVisible = true
|
networkInfoModalVisible = true;
|
||||||
open()
|
open();
|
||||||
NetworkService.fetchNetworkInfo(ssid)
|
NetworkService.fetchNetworkInfo(ssid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideDialog() {
|
function hideDialog() {
|
||||||
networkInfoModalVisible = false
|
networkInfoModalVisible = false;
|
||||||
close()
|
close();
|
||||||
networkSSID = ""
|
networkSSID = "";
|
||||||
networkData = null
|
networkData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: networkInfoModalVisible
|
visible: networkInfoModalVisible
|
||||||
width: 600
|
modalWidth: 600
|
||||||
height: 500
|
modalHeight: 500
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
onBackgroundClicked: hideDialog()
|
onBackgroundClicked: hideDialog()
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
networkSSID = ""
|
networkSSID = "";
|
||||||
networkData = null
|
networkData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +72,6 @@ DankModal {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
@@ -80,7 +80,6 @@ DankModal {
|
|||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: root.hideDialog()
|
onClicked: root.hideDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -109,7 +108,6 @@ DankModal {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -148,17 +146,10 @@ DankModal {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -10,34 +9,36 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:network-info-wired"
|
layerNamespace: "dms:network-info-wired"
|
||||||
|
|
||||||
|
keepPopoutsOpen: true
|
||||||
|
|
||||||
property bool networkWiredInfoModalVisible: false
|
property bool networkWiredInfoModalVisible: false
|
||||||
property string networkID: ""
|
property string networkID: ""
|
||||||
property var networkData: null
|
property var networkData: null
|
||||||
|
|
||||||
function showNetworkInfo(id, data) {
|
function showNetworkInfo(id, data) {
|
||||||
networkID = id
|
networkID = id;
|
||||||
networkData = data
|
networkData = data;
|
||||||
networkWiredInfoModalVisible = true
|
networkWiredInfoModalVisible = true;
|
||||||
open()
|
open();
|
||||||
NetworkService.fetchWiredNetworkInfo(data.uuid)
|
NetworkService.fetchWiredNetworkInfo(data.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideDialog() {
|
function hideDialog() {
|
||||||
networkWiredInfoModalVisible = false
|
networkWiredInfoModalVisible = false;
|
||||||
close()
|
close();
|
||||||
networkID = ""
|
networkID = "";
|
||||||
networkData = null
|
networkData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: networkWiredInfoModalVisible
|
visible: networkWiredInfoModalVisible
|
||||||
width: 600
|
modalWidth: 600
|
||||||
height: 500
|
modalHeight: 500
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
onBackgroundClicked: hideDialog()
|
onBackgroundClicked: hideDialog()
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
networkID = ""
|
networkID = "";
|
||||||
networkData = null
|
networkData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +72,6 @@ DankModal {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
@@ -80,7 +80,6 @@ DankModal {
|
|||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: root.hideDialog()
|
onClicked: root.hideDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -109,7 +108,6 @@ DankModal {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -148,17 +146,10 @@ DankModal {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,72 +1,77 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Modules.Notifications.Center
|
import qs.Modules.Notifications.Center
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
DankModal {
|
DankModal {
|
||||||
id: notificationModal
|
id: notificationModal
|
||||||
|
|
||||||
layerNamespace: "dms:notification-center-modal"
|
layerNamespace: "dms:notification-center-modal"
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: [notificationModal]
|
||||||
|
active: CompositorService.isHyprland && notificationModal.shouldHaveFocus
|
||||||
|
}
|
||||||
|
|
||||||
property bool notificationModalOpen: false
|
property bool notificationModalOpen: false
|
||||||
property var notificationListRef: null
|
property var notificationListRef: null
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
notificationModalOpen = true
|
notificationModalOpen = true;
|
||||||
NotificationService.onOverlayOpen()
|
NotificationService.onOverlayOpen();
|
||||||
open()
|
open();
|
||||||
modalKeyboardController.reset()
|
modalKeyboardController.reset();
|
||||||
if (modalKeyboardController && notificationListRef) {
|
if (modalKeyboardController && notificationListRef) {
|
||||||
modalKeyboardController.listView = notificationListRef
|
modalKeyboardController.listView = notificationListRef;
|
||||||
modalKeyboardController.rebuildFlatNavigation()
|
modalKeyboardController.rebuildFlatNavigation();
|
||||||
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
modalKeyboardController.keyboardNavigationActive = true
|
modalKeyboardController.keyboardNavigationActive = true;
|
||||||
modalKeyboardController.selectedFlatIndex = 0
|
modalKeyboardController.selectedFlatIndex = 0;
|
||||||
modalKeyboardController.updateSelectedIdFromIndex()
|
modalKeyboardController.updateSelectedIdFromIndex();
|
||||||
if (notificationListRef) {
|
if (notificationListRef) {
|
||||||
notificationListRef.keyboardActive = true
|
notificationListRef.keyboardActive = true;
|
||||||
notificationListRef.currentIndex = 0
|
notificationListRef.currentIndex = 0;
|
||||||
}
|
}
|
||||||
modalKeyboardController.selectionVersion++
|
modalKeyboardController.selectionVersion++;
|
||||||
modalKeyboardController.ensureVisible()
|
modalKeyboardController.ensureVisible();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
notificationModalOpen = false
|
notificationModalOpen = false;
|
||||||
NotificationService.onOverlayClose()
|
NotificationService.onOverlayClose();
|
||||||
close()
|
close();
|
||||||
modalKeyboardController.reset()
|
modalKeyboardController.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
hide()
|
hide();
|
||||||
} else {
|
} else {
|
||||||
show()
|
show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
width: 500
|
modalWidth: 500
|
||||||
height: 700
|
modalHeight: 700
|
||||||
visible: false
|
visible: false
|
||||||
onBackgroundClicked: hide()
|
onBackgroundClicked: hide()
|
||||||
onOpened: () => {
|
onOpened: () => {
|
||||||
Qt.callLater(() => modalFocusScope.forceActiveFocus());
|
Qt.callLater(() => modalFocusScope.forceActiveFocus());
|
||||||
}
|
}
|
||||||
onShouldBeVisibleChanged: (shouldBeVisible) => {
|
onShouldBeVisibleChanged: shouldBeVisible => {
|
||||||
if (!shouldBeVisible) {
|
if (!shouldBeVisible) {
|
||||||
notificationModalOpen = false
|
notificationModalOpen = false;
|
||||||
modalKeyboardController.reset()
|
modalKeyboardController.reset();
|
||||||
NotificationService.onOverlayClose()
|
NotificationService.onOverlayClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modalFocusScope.Keys.onPressed: (event) => modalKeyboardController.handleKey(event)
|
modalFocusScope.Keys.onPressed: event => modalKeyboardController.handleKey(event)
|
||||||
|
|
||||||
NotificationKeyboardController {
|
NotificationKeyboardController {
|
||||||
id: modalKeyboardController
|
id: modalKeyboardController
|
||||||
@@ -125,14 +130,13 @@ DankModal {
|
|||||||
height: parent.height - y
|
height: parent.height - y
|
||||||
keyboardController: modalKeyboardController
|
keyboardController: modalKeyboardController
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
notificationModal.notificationListRef = notificationList
|
notificationModal.notificationListRef = notificationList;
|
||||||
if (modalKeyboardController) {
|
if (modalKeyboardController) {
|
||||||
modalKeyboardController.listView = notificationList
|
modalKeyboardController.listView = notificationList;
|
||||||
modalKeyboardController.rebuildFlatNavigation()
|
modalKeyboardController.rebuildFlatNavigation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationKeyboardHints {
|
NotificationKeyboardHints {
|
||||||
@@ -144,9 +148,6 @@ DankModal {
|
|||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
showHints: modalKeyboardController.showKeyboardHints
|
showHints: modalKeyboardController.showKeyboardHints
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -9,33 +10,38 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:polkit"
|
layerNamespace: "dms:polkit"
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: [root]
|
||||||
|
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||||
|
}
|
||||||
|
|
||||||
property string passwordInput: ""
|
property string passwordInput: ""
|
||||||
property var currentFlow: PolkitService.agent?.flow
|
property var currentFlow: PolkitService.agent?.flow
|
||||||
property bool isLoading: false
|
property bool isLoading: false
|
||||||
property real minHeight: 240
|
property real minHeight: 240
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
passwordInput = ""
|
passwordInput = "";
|
||||||
isLoading = false
|
isLoading = false;
|
||||||
open()
|
open();
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item && contentLoader.item.passwordField) {
|
if (contentLoader.item && contentLoader.item.passwordField) {
|
||||||
contentLoader.item.passwordField.forceActiveFocus()
|
contentLoader.item.passwordField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
width: 420
|
modalWidth: 420
|
||||||
height: Math.max(minHeight, contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240)
|
modalHeight: Math.max(minHeight, contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240)
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: contentLoader.item
|
target: contentLoader.item
|
||||||
function onImplicitHeightChanged() {
|
function onImplicitHeightChanged() {
|
||||||
if (shouldBeVisible && contentLoader.item) {
|
if (shouldBeVisible && contentLoader.item) {
|
||||||
const newHeight = contentLoader.item.implicitHeight + Theme.spacingM * 2
|
const newHeight = contentLoader.item.implicitHeight + Theme.spacingM * 2;
|
||||||
if (newHeight > minHeight) {
|
if (newHeight > minHeight) {
|
||||||
minHeight = newHeight
|
minHeight = newHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,19 +50,19 @@ DankModal {
|
|||||||
onOpened: {
|
onOpened: {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item && contentLoader.item.passwordField) {
|
if (contentLoader.item && contentLoader.item.passwordField) {
|
||||||
contentLoader.item.passwordField.forceActiveFocus()
|
contentLoader.item.passwordField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClosed: {
|
onDialogClosed: {
|
||||||
passwordInput = ""
|
passwordInput = "";
|
||||||
isLoading = false
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
if (currentFlow && !isLoading) {
|
if (currentFlow && !isLoading) {
|
||||||
currentFlow.cancelAuthenticationRequest()
|
currentFlow.cancelAuthenticationRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,12 +71,12 @@ DankModal {
|
|||||||
enabled: PolkitService.polkitAvailable
|
enabled: PolkitService.polkitAvailable
|
||||||
|
|
||||||
function onAuthenticationRequestStarted() {
|
function onAuthenticationRequestStarted() {
|
||||||
show()
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onIsActiveChanged() {
|
function onIsActiveChanged() {
|
||||||
if (!(PolkitService.agent?.isActive ?? false)) {
|
if (!(PolkitService.agent?.isActive ?? false)) {
|
||||||
close()
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,24 +87,24 @@ DankModal {
|
|||||||
|
|
||||||
function onIsResponseRequiredChanged() {
|
function onIsResponseRequiredChanged() {
|
||||||
if (currentFlow.isResponseRequired) {
|
if (currentFlow.isResponseRequired) {
|
||||||
isLoading = false
|
isLoading = false;
|
||||||
passwordInput = ""
|
passwordInput = "";
|
||||||
if (contentLoader.item && contentLoader.item.passwordField) {
|
if (contentLoader.item && contentLoader.item.passwordField) {
|
||||||
contentLoader.item.passwordField.forceActiveFocus()
|
contentLoader.item.passwordField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAuthenticationSucceeded() {
|
function onAuthenticationSucceeded() {
|
||||||
close()
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAuthenticationFailed() {
|
function onAuthenticationFailed() {
|
||||||
isLoading = false
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAuthenticationRequestCancelled() {
|
function onAuthenticationRequestCancelled() {
|
||||||
close()
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,9 +120,9 @@ DankModal {
|
|||||||
|
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
if (currentFlow && !isLoading) {
|
if (currentFlow && !isLoading) {
|
||||||
currentFlow.cancelAuthenticationRequest()
|
currentFlow.cancelAuthenticationRequest();
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -171,7 +177,7 @@ DankModal {
|
|||||||
opacity: enabled ? 1 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (currentFlow) {
|
if (currentFlow) {
|
||||||
currentFlow.cancelAuthenticationRequest()
|
currentFlow.cancelAuthenticationRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +214,7 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: !isLoading
|
enabled: !isLoading
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
passwordField.forceActiveFocus()
|
passwordField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,13 +230,13 @@ DankModal {
|
|||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
enabled: !isLoading
|
enabled: !isLoading
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
passwordInput = text
|
passwordInput = text;
|
||||||
}
|
}
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
if (passwordInput.length > 0 && currentFlow && !isLoading) {
|
if (passwordInput.length > 0 && currentFlow && !isLoading) {
|
||||||
isLoading = true
|
isLoading = true;
|
||||||
currentFlow.submit(passwordInput)
|
currentFlow.submit(passwordInput);
|
||||||
passwordInput = ""
|
passwordInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,7 +309,7 @@ DankModal {
|
|||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (currentFlow) {
|
if (currentFlow) {
|
||||||
currentFlow.cancelAuthenticationRequest()
|
currentFlow.cancelAuthenticationRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,9 +342,9 @@ DankModal {
|
|||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (currentFlow && !isLoading) {
|
if (currentFlow && !isLoading) {
|
||||||
isLoading = true
|
isLoading = true;
|
||||||
currentFlow.submit(passwordInput)
|
currentFlow.submit(passwordInput);
|
||||||
passwordInput = ""
|
passwordInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -10,6 +11,11 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:power-menu"
|
layerNamespace: "dms:power-menu"
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: [root]
|
||||||
|
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||||
|
}
|
||||||
|
|
||||||
property int selectedIndex: 0
|
property int selectedIndex: 0
|
||||||
property int selectedRow: 0
|
property int selectedRow: 0
|
||||||
property int selectedCol: 0
|
property int selectedCol: 0
|
||||||
@@ -23,64 +29,64 @@ DankModal {
|
|||||||
signal lockRequested
|
signal lockRequested
|
||||||
|
|
||||||
function openCentered() {
|
function openCentered() {
|
||||||
parentBounds = Qt.rect(0, 0, 0, 0)
|
parentBounds = Qt.rect(0, 0, 0, 0);
|
||||||
parentScreen = null
|
parentScreen = null;
|
||||||
backgroundOpacity = 0.5
|
backgroundOpacity = 0.5;
|
||||||
open()
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openFromControlCenter(bounds, targetScreen) {
|
function openFromControlCenter(bounds, targetScreen) {
|
||||||
parentBounds = bounds
|
parentBounds = bounds;
|
||||||
parentScreen = targetScreen
|
parentScreen = targetScreen;
|
||||||
backgroundOpacity = 0
|
backgroundOpacity = 0;
|
||||||
keepPopoutsOpen = true
|
keepPopoutsOpen = true;
|
||||||
open()
|
open();
|
||||||
keepPopoutsOpen = false
|
keepPopoutsOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateVisibleActions() {
|
function updateVisibleActions() {
|
||||||
const allActions = SettingsData.powerMenuActions || ["reboot", "logout", "poweroff", "lock", "suspend", "restart"]
|
const allActions = SettingsData.powerMenuActions || ["reboot", "logout", "poweroff", "lock", "suspend", "restart"];
|
||||||
visibleActions = allActions.filter(action => {
|
visibleActions = allActions.filter(action => {
|
||||||
if (action === "hibernate" && !SessionService.hibernateSupported)
|
if (action === "hibernate" && !SessionService.hibernateSupported)
|
||||||
return false
|
return false;
|
||||||
return true
|
return true;
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!SettingsData.powerMenuGridLayout) return
|
if (!SettingsData.powerMenuGridLayout)
|
||||||
|
return;
|
||||||
const count = visibleActions.length
|
const count = visibleActions.length;
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
gridColumns = 1
|
gridColumns = 1;
|
||||||
gridRows = 1
|
gridRows = 1;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count <= 3) {
|
if (count <= 3) {
|
||||||
gridColumns = 1
|
gridColumns = 1;
|
||||||
gridRows = count
|
gridRows = count;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count === 4) {
|
if (count === 4) {
|
||||||
gridColumns = 2
|
gridColumns = 2;
|
||||||
gridRows = 2
|
gridRows = 2;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gridColumns = 3
|
gridColumns = 3;
|
||||||
gridRows = Math.ceil(count / 3)
|
gridRows = Math.ceil(count / 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultActionIndex() {
|
function getDefaultActionIndex() {
|
||||||
const defaultAction = SettingsData.powerMenuDefaultAction || "logout"
|
const defaultAction = SettingsData.powerMenuDefaultAction || "logout";
|
||||||
const index = visibleActions.indexOf(defaultAction)
|
const index = visibleActions.indexOf(defaultAction);
|
||||||
return index >= 0 ? index : 0
|
return index >= 0 ? index : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActionAtIndex(index) {
|
function getActionAtIndex(index) {
|
||||||
if (index < 0 || index >= visibleActions.length)
|
if (index < 0 || index >= visibleActions.length)
|
||||||
return ""
|
return "";
|
||||||
return visibleActions[index]
|
return visibleActions[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActionData(action) {
|
function getActionData(action) {
|
||||||
@@ -90,64 +96,64 @@ DankModal {
|
|||||||
"icon": "restart_alt",
|
"icon": "restart_alt",
|
||||||
"label": I18n.tr("Reboot"),
|
"label": I18n.tr("Reboot"),
|
||||||
"key": "R"
|
"key": "R"
|
||||||
}
|
};
|
||||||
case "logout":
|
case "logout":
|
||||||
return {
|
return {
|
||||||
"icon": "logout",
|
"icon": "logout",
|
||||||
"label": I18n.tr("Log Out"),
|
"label": I18n.tr("Log Out"),
|
||||||
"key": "X"
|
"key": "X"
|
||||||
}
|
};
|
||||||
case "poweroff":
|
case "poweroff":
|
||||||
return {
|
return {
|
||||||
"icon": "power_settings_new",
|
"icon": "power_settings_new",
|
||||||
"label": I18n.tr("Power Off"),
|
"label": I18n.tr("Power Off"),
|
||||||
"key": "P"
|
"key": "P"
|
||||||
}
|
};
|
||||||
case "lock":
|
case "lock":
|
||||||
return {
|
return {
|
||||||
"icon": "lock",
|
"icon": "lock",
|
||||||
"label": I18n.tr("Lock"),
|
"label": I18n.tr("Lock"),
|
||||||
"key": "L"
|
"key": "L"
|
||||||
}
|
};
|
||||||
case "suspend":
|
case "suspend":
|
||||||
return {
|
return {
|
||||||
"icon": "bedtime",
|
"icon": "bedtime",
|
||||||
"label": I18n.tr("Suspend"),
|
"label": I18n.tr("Suspend"),
|
||||||
"key": "S"
|
"key": "S"
|
||||||
}
|
};
|
||||||
case "hibernate":
|
case "hibernate":
|
||||||
return {
|
return {
|
||||||
"icon": "ac_unit",
|
"icon": "ac_unit",
|
||||||
"label": I18n.tr("Hibernate"),
|
"label": I18n.tr("Hibernate"),
|
||||||
"key": "H"
|
"key": "H"
|
||||||
}
|
};
|
||||||
case "restart":
|
case "restart":
|
||||||
return {
|
return {
|
||||||
"icon": "refresh",
|
"icon": "refresh",
|
||||||
"label": I18n.tr("Restart DMS"),
|
"label": I18n.tr("Restart DMS"),
|
||||||
"key": "D"
|
"key": "D"
|
||||||
}
|
};
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
"icon": "help",
|
"icon": "help",
|
||||||
"label": action,
|
"label": action,
|
||||||
"key": "?"
|
"key": "?"
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectOption(action) {
|
function selectOption(action) {
|
||||||
if (action === "lock") {
|
if (action === "lock") {
|
||||||
close()
|
close();
|
||||||
lockRequested()
|
lockRequested();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (action === "restart") {
|
if (action === "restart") {
|
||||||
close()
|
close();
|
||||||
Quickshell.execDetached(["dms", "restart"])
|
Quickshell.execDetached(["dms", "restart"]);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
close()
|
close();
|
||||||
const actions = {
|
const actions = {
|
||||||
"logout": {
|
"logout": {
|
||||||
"title": I18n.tr("Log Out"),
|
"title": I18n.tr("Log Out"),
|
||||||
@@ -169,227 +175,223 @@ DankModal {
|
|||||||
"title": I18n.tr("Power Off"),
|
"title": I18n.tr("Power Off"),
|
||||||
"message": I18n.tr("Are you sure you want to power off the system?")
|
"message": I18n.tr("Are you sure you want to power off the system?")
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
const selected = actions[action]
|
const selected = actions[action];
|
||||||
if (selected) {
|
if (selected) {
|
||||||
root.powerActionRequested(action, selected.title, selected.message)
|
root.powerActionRequested(action, selected.title, selected.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
width: SettingsData.powerMenuGridLayout
|
modalWidth: SettingsData.powerMenuGridLayout ? Math.min(550, gridColumns * 180 + Theme.spacingS * (gridColumns - 1) + Theme.spacingL * 2) : 400
|
||||||
? Math.min(550, gridColumns * 180 + Theme.spacingS * (gridColumns - 1) + Theme.spacingL * 2)
|
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight : 300
|
||||||
: 400
|
|
||||||
height: contentLoader.item ? contentLoader.item.implicitHeight : 300
|
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
screen: parentScreen
|
targetScreen: parentScreen
|
||||||
positioning: parentBounds.width > 0 ? "custom" : "center"
|
positioning: parentBounds.width > 0 ? "custom" : "center"
|
||||||
customPosition: {
|
customPosition: {
|
||||||
if (parentBounds.width > 0) {
|
if (parentBounds.width > 0) {
|
||||||
const effectiveBarThickness = Math.max(26 + SettingsData.dankBarInnerPadding * 0.6 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
|
const effectiveBarThickness = Math.max(26 + (SettingsData.barConfigs[0]?.innerPadding ?? 4) * 0.6 + (SettingsData.barConfigs[0]?.innerPadding ?? 4) + 4, Theme.barHeight - 4 - (8 - (SettingsData.barConfigs[0]?.innerPadding ?? 4)));
|
||||||
const barExclusionZone = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
|
const barExclusionZone = effectiveBarThickness + (SettingsData.barConfigs[0]?.spacing ?? 4) + (SettingsData.barConfigs[0]?.bottomGap ?? 0);
|
||||||
const screenW = parentScreen?.width ?? 1920
|
const screenW = parentScreen?.width ?? 1920;
|
||||||
const screenH = parentScreen?.height ?? 1080
|
const screenH = parentScreen?.height ?? 1080;
|
||||||
const margin = Theme.spacingL
|
const margin = Theme.spacingL;
|
||||||
|
|
||||||
let targetX = parentBounds.x + (parentBounds.width - width) / 2
|
let targetX = parentBounds.x + (parentBounds.width - modalWidth) / 2;
|
||||||
let targetY = parentBounds.y + (parentBounds.height - height) / 2
|
let targetY = parentBounds.y + (parentBounds.height - modalHeight) / 2;
|
||||||
|
|
||||||
const minY = SettingsData.dankBarPosition === SettingsData.Position.Top ? barExclusionZone + margin : margin
|
const minY = (SettingsData.barConfigs[0]?.position ?? SettingsData.Position.Top) === SettingsData.Position.Top ? barExclusionZone + margin : margin;
|
||||||
const maxY = SettingsData.dankBarPosition === SettingsData.Position.Bottom ? screenH - height - barExclusionZone - margin : screenH - height - margin
|
const maxY = (SettingsData.barConfigs[0]?.position ?? SettingsData.Position.Top) === SettingsData.Position.Bottom ? screenH - modalHeight - barExclusionZone - margin : screenH - modalHeight - margin;
|
||||||
|
|
||||||
targetY = Math.max(minY, Math.min(maxY, targetY))
|
targetY = Math.max(minY, Math.min(maxY, targetY));
|
||||||
|
|
||||||
return Qt.point(targetX, targetY)
|
return Qt.point(targetX, targetY);
|
||||||
}
|
}
|
||||||
return Qt.point(0, 0)
|
return Qt.point(0, 0);
|
||||||
}
|
}
|
||||||
onBackgroundClicked: () => close()
|
onBackgroundClicked: () => close()
|
||||||
onOpened: () => {
|
onOpened: () => {
|
||||||
updateVisibleActions()
|
updateVisibleActions();
|
||||||
const defaultIndex = getDefaultActionIndex()
|
const defaultIndex = getDefaultActionIndex();
|
||||||
if (SettingsData.powerMenuGridLayout) {
|
if (SettingsData.powerMenuGridLayout) {
|
||||||
selectedRow = Math.floor(defaultIndex / gridColumns)
|
selectedRow = Math.floor(defaultIndex / gridColumns);
|
||||||
selectedCol = defaultIndex % gridColumns
|
selectedCol = defaultIndex % gridColumns;
|
||||||
selectedIndex = defaultIndex
|
selectedIndex = defaultIndex;
|
||||||
} else {
|
} else {
|
||||||
selectedIndex = defaultIndex
|
selectedIndex = defaultIndex;
|
||||||
}
|
}
|
||||||
Qt.callLater(() => modalFocusScope.forceActiveFocus())
|
Qt.callLater(() => modalFocusScope.forceActiveFocus());
|
||||||
}
|
}
|
||||||
Component.onCompleted: updateVisibleActions()
|
Component.onCompleted: updateVisibleActions()
|
||||||
modalFocusScope.Keys.onPressed: event => {
|
modalFocusScope.Keys.onPressed: event => {
|
||||||
if (SettingsData.powerMenuGridLayout) {
|
if (SettingsData.powerMenuGridLayout) {
|
||||||
handleGridNavigation(event)
|
handleGridNavigation(event);
|
||||||
} else {
|
} else {
|
||||||
handleListNavigation(event)
|
handleListNavigation(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleListNavigation(event) {
|
function handleListNavigation(event) {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case Qt.Key_Up:
|
case Qt.Key_Up:
|
||||||
case Qt.Key_Backtab:
|
case Qt.Key_Backtab:
|
||||||
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length
|
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_Down:
|
case Qt.Key_Down:
|
||||||
case Qt.Key_Tab:
|
case Qt.Key_Tab:
|
||||||
selectedIndex = (selectedIndex + 1) % visibleActions.length
|
selectedIndex = (selectedIndex + 1) % visibleActions.length;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_Return:
|
case Qt.Key_Return:
|
||||||
case Qt.Key_Enter:
|
case Qt.Key_Enter:
|
||||||
selectOption(getActionAtIndex(selectedIndex))
|
selectOption(getActionAtIndex(selectedIndex));
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_N:
|
case Qt.Key_N:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
selectedIndex = (selectedIndex + 1) % visibleActions.length
|
selectedIndex = (selectedIndex + 1) % visibleActions.length;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_P:
|
case Qt.Key_P:
|
||||||
if (!(event.modifiers & Qt.ControlModifier)) {
|
if (!(event.modifiers & Qt.ControlModifier)) {
|
||||||
selectOption("poweroff")
|
selectOption("poweroff");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
} else {
|
} else {
|
||||||
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length
|
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_J:
|
case Qt.Key_J:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
selectedIndex = (selectedIndex + 1) % visibleActions.length
|
selectedIndex = (selectedIndex + 1) % visibleActions.length;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_K:
|
case Qt.Key_K:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length
|
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_R:
|
case Qt.Key_R:
|
||||||
selectOption("reboot")
|
selectOption("reboot");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_X:
|
case Qt.Key_X:
|
||||||
selectOption("logout")
|
selectOption("logout");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_L:
|
case Qt.Key_L:
|
||||||
selectOption("lock")
|
selectOption("lock");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_S:
|
case Qt.Key_S:
|
||||||
selectOption("suspend")
|
selectOption("suspend");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_H:
|
case Qt.Key_H:
|
||||||
selectOption("hibernate")
|
selectOption("hibernate");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_D:
|
case Qt.Key_D:
|
||||||
selectOption("restart")
|
selectOption("restart");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleGridNavigation(event) {
|
function handleGridNavigation(event) {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case Qt.Key_Left:
|
case Qt.Key_Left:
|
||||||
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns
|
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns;
|
||||||
selectedIndex = selectedRow * gridColumns + selectedCol
|
selectedIndex = selectedRow * gridColumns + selectedCol;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_Right:
|
case Qt.Key_Right:
|
||||||
selectedCol = (selectedCol + 1) % gridColumns
|
selectedCol = (selectedCol + 1) % gridColumns;
|
||||||
selectedIndex = selectedRow * gridColumns + selectedCol
|
selectedIndex = selectedRow * gridColumns + selectedCol;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_Up:
|
case Qt.Key_Up:
|
||||||
case Qt.Key_Backtab:
|
case Qt.Key_Backtab:
|
||||||
selectedRow = (selectedRow - 1 + gridRows) % gridRows
|
selectedRow = (selectedRow - 1 + gridRows) % gridRows;
|
||||||
selectedIndex = selectedRow * gridColumns + selectedCol
|
selectedIndex = selectedRow * gridColumns + selectedCol;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_Down:
|
case Qt.Key_Down:
|
||||||
case Qt.Key_Tab:
|
case Qt.Key_Tab:
|
||||||
selectedRow = (selectedRow + 1) % gridRows
|
selectedRow = (selectedRow + 1) % gridRows;
|
||||||
selectedIndex = selectedRow * gridColumns + selectedCol
|
selectedIndex = selectedRow * gridColumns + selectedCol;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_Return:
|
case Qt.Key_Return:
|
||||||
case Qt.Key_Enter:
|
case Qt.Key_Enter:
|
||||||
selectOption(getActionAtIndex(selectedIndex))
|
selectOption(getActionAtIndex(selectedIndex));
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_N:
|
case Qt.Key_N:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
selectedCol = (selectedCol + 1) % gridColumns
|
selectedCol = (selectedCol + 1) % gridColumns;
|
||||||
selectedIndex = selectedRow * gridColumns + selectedCol
|
selectedIndex = selectedRow * gridColumns + selectedCol;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_P:
|
case Qt.Key_P:
|
||||||
if (!(event.modifiers & Qt.ControlModifier)) {
|
if (!(event.modifiers & Qt.ControlModifier)) {
|
||||||
selectOption("poweroff")
|
selectOption("poweroff");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
} else {
|
} else {
|
||||||
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns
|
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns;
|
||||||
selectedIndex = selectedRow * gridColumns + selectedCol
|
selectedIndex = selectedRow * gridColumns + selectedCol;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_J:
|
case Qt.Key_J:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
selectedRow = (selectedRow + 1) % gridRows
|
selectedRow = (selectedRow + 1) % gridRows;
|
||||||
selectedIndex = selectedRow * gridColumns + selectedCol
|
selectedIndex = selectedRow * gridColumns + selectedCol;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_K:
|
case Qt.Key_K:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
selectedRow = (selectedRow - 1 + gridRows) % gridRows
|
selectedRow = (selectedRow - 1 + gridRows) % gridRows;
|
||||||
selectedIndex = selectedRow * gridColumns + selectedCol
|
selectedIndex = selectedRow * gridColumns + selectedCol;
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case Qt.Key_R:
|
case Qt.Key_R:
|
||||||
selectOption("reboot")
|
selectOption("reboot");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_X:
|
case Qt.Key_X:
|
||||||
selectOption("logout")
|
selectOption("logout");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_L:
|
case Qt.Key_L:
|
||||||
selectOption("lock")
|
selectOption("lock");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_S:
|
case Qt.Key_S:
|
||||||
selectOption("suspend")
|
selectOption("suspend");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_H:
|
case Qt.Key_H:
|
||||||
selectOption("hibernate")
|
selectOption("hibernate");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
case Qt.Key_D:
|
case Qt.Key_D:
|
||||||
selectOption("restart")
|
selectOption("restart");
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
implicitHeight: SettingsData.powerMenuGridLayout
|
implicitHeight: SettingsData.powerMenuGridLayout ? buttonGrid.implicitHeight + Theme.spacingL * 2 : buttonColumn.implicitHeight + Theme.spacingL * 2
|
||||||
? buttonGrid.implicitHeight + Theme.spacingL * 2
|
|
||||||
: buttonColumn.implicitHeight + Theme.spacingL * 2
|
|
||||||
|
|
||||||
Grid {
|
Grid {
|
||||||
id: buttonGrid
|
id: buttonGrid
|
||||||
@@ -410,15 +412,15 @@ DankModal {
|
|||||||
readonly property bool isSelected: root.selectedIndex === index
|
readonly property bool isSelected: root.selectedIndex === index
|
||||||
readonly property bool showWarning: modelData === "reboot" || modelData === "poweroff"
|
readonly property bool showWarning: modelData === "reboot" || modelData === "poweroff"
|
||||||
|
|
||||||
width: (root.width - Theme.spacingL * 2 - Theme.spacingS * (root.gridColumns - 1)) / root.gridColumns
|
width: (root.modalWidth - Theme.spacingL * 2 - Theme.spacingS * (root.gridColumns - 1)) / root.gridColumns
|
||||||
height: 100
|
height: 100
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
||||||
if (mouseArea.containsMouse)
|
if (mouseArea.containsMouse)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
|
||||||
}
|
}
|
||||||
border.color: isSelected ? Theme.primary : "transparent"
|
border.color: isSelected ? Theme.primary : "transparent"
|
||||||
border.width: isSelected ? 2 : 0
|
border.width: isSelected ? 2 : 0
|
||||||
@@ -432,9 +434,9 @@ DankModal {
|
|||||||
size: Theme.iconSize + 8
|
size: Theme.iconSize + 8
|
||||||
color: {
|
color: {
|
||||||
if (parent.parent.showWarning && mouseArea.containsMouse) {
|
if (parent.parent.showWarning && mouseArea.containsMouse) {
|
||||||
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning
|
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||||
}
|
}
|
||||||
return Theme.surfaceText
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
@@ -444,9 +446,9 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: {
|
color: {
|
||||||
if (parent.parent.showWarning && mouseArea.containsMouse) {
|
if (parent.parent.showWarning && mouseArea.containsMouse) {
|
||||||
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning
|
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||||
}
|
}
|
||||||
return Theme.surfaceText
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -475,9 +477,9 @@ DankModal {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.selectedRow = Math.floor(index / root.gridColumns)
|
root.selectedRow = Math.floor(index / root.gridColumns);
|
||||||
root.selectedCol = index % root.gridColumns
|
root.selectedCol = index % root.gridColumns;
|
||||||
root.selectOption(modelData)
|
root.selectOption(modelData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -512,10 +514,10 @@ DankModal {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
||||||
if (listMouseArea.containsMouse)
|
if (listMouseArea.containsMouse)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
|
||||||
}
|
}
|
||||||
border.color: isSelected ? Theme.primary : "transparent"
|
border.color: isSelected ? Theme.primary : "transparent"
|
||||||
border.width: isSelected ? 2 : 0
|
border.width: isSelected ? 2 : 0
|
||||||
@@ -535,9 +537,9 @@ DankModal {
|
|||||||
size: Theme.iconSize + 4
|
size: Theme.iconSize + 4
|
||||||
color: {
|
color: {
|
||||||
if (parent.parent.showWarning && listMouseArea.containsMouse) {
|
if (parent.parent.showWarning && listMouseArea.containsMouse) {
|
||||||
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning
|
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||||
}
|
}
|
||||||
return Theme.surfaceText
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
@@ -547,9 +549,9 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: {
|
color: {
|
||||||
if (parent.parent.showWarning && listMouseArea.containsMouse) {
|
if (parent.parent.showWarning && listMouseArea.containsMouse) {
|
||||||
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning
|
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||||
}
|
}
|
||||||
return Theme.surfaceText
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -582,8 +584,8 @@ DankModal {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.selectedIndex = index
|
root.selectedIndex = index;
|
||||||
root.selectOption(modelData)
|
root.selectOption(modelData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,62 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
|
||||||
import qs.Modules.ProcessList
|
import qs.Modules.ProcessList
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
DankModal {
|
FloatingWindow {
|
||||||
id: processListModal
|
id: processListModal
|
||||||
|
|
||||||
layerNamespace: "dms:process-list-modal"
|
|
||||||
|
|
||||||
property int currentTab: 0
|
property int currentTab: 0
|
||||||
property var tabNames: ["Processes", "Performance", "System"]
|
property var tabNames: ["Processes", "Performance", "System"]
|
||||||
|
property bool shouldHaveFocus: visible
|
||||||
|
property alias shouldBeVisible: processListModal.visible
|
||||||
|
|
||||||
|
signal closingModal
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
if (!DgopService.dgopAvailable) {
|
if (!DgopService.dgopAvailable) {
|
||||||
console.warn("ProcessListModal: dgop is not available");
|
console.warn("ProcessListModal: dgop is not available");
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
open();
|
visible = true;
|
||||||
UserInfoService.getUptime();
|
UserInfoService.getUptime();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
close();
|
visible = false;
|
||||||
if (processContextMenu.visible) {
|
if (processContextMenu.visible) {
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (!DgopService.dgopAvailable) {
|
if (!DgopService.dgopAvailable) {
|
||||||
console.warn("ProcessListModal: dgop is not available");
|
console.warn("ProcessListModal: dgop is not available");
|
||||||
return ;
|
return;
|
||||||
}
|
|
||||||
if (shouldBeVisible) {
|
|
||||||
hide();
|
|
||||||
} else {
|
|
||||||
show();
|
|
||||||
}
|
}
|
||||||
|
visible = !visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
width: 900
|
objectName: "processListModal"
|
||||||
height: 680
|
title: I18n.tr("System Monitor", "sysmon window title")
|
||||||
|
implicitWidth: 900
|
||||||
|
implicitHeight: 680
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
visible: false
|
visible: false
|
||||||
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
|
||||||
cornerRadius: Theme.cornerRadius
|
onVisibleChanged: {
|
||||||
enableShadow: true
|
if (!visible) {
|
||||||
onBackgroundClicked: () => {
|
closingModal();
|
||||||
return hide();
|
} else {
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (contentFocusScope) {
|
||||||
|
contentFocusScope.forceActiveFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
@@ -59,300 +65,277 @@ DankModal {
|
|||||||
ProcessesTab {
|
ProcessesTab {
|
||||||
contextMenu: processContextMenu
|
contextMenu: processContextMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: performanceTabComponent
|
id: performanceTabComponent
|
||||||
|
|
||||||
PerformanceTab {
|
PerformanceTab {}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: systemTabComponent
|
id: systemTabComponent
|
||||||
|
|
||||||
SystemTab {
|
SystemTab {}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessContextMenu {
|
ProcessContextMenu {
|
||||||
id: processContextMenu
|
id: processContextMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
FocusScope {
|
||||||
Item {
|
id: contentFocusScope
|
||||||
anchors.fill: parent
|
|
||||||
focus: true
|
anchors.fill: parent
|
||||||
Keys.onPressed: (event) => {
|
focus: true
|
||||||
if (event.key === Qt.Key_Escape) {
|
|
||||||
processListModal.hide();
|
Keys.onPressed: event => {
|
||||||
event.accepted = true;
|
if (event.key === Qt.Key_Escape) {
|
||||||
} else if (event.key === Qt.Key_1) {
|
hide();
|
||||||
currentTab = 0;
|
event.accepted = true;
|
||||||
event.accepted = true;
|
return;
|
||||||
} else if (event.key === Qt.Key_2) {
|
|
||||||
currentTab = 1;
|
|
||||||
event.accepted = true;
|
|
||||||
} else if (event.key === Qt.Key_3) {
|
|
||||||
currentTab = 2;
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show error message when dgop is not available
|
switch (event.key) {
|
||||||
Rectangle {
|
case Qt.Key_1:
|
||||||
anchors.centerIn: parent
|
currentTab = 0;
|
||||||
width: 400
|
event.accepted = true;
|
||||||
height: 200
|
return;
|
||||||
radius: Theme.cornerRadius
|
case Qt.Key_2:
|
||||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1)
|
currentTab = 1;
|
||||||
border.color: Theme.error
|
event.accepted = true;
|
||||||
border.width: 2
|
return;
|
||||||
visible: !DgopService.dgopAvailable
|
case Qt.Key_3:
|
||||||
|
currentTab = 2;
|
||||||
Column {
|
event.accepted = true;
|
||||||
anchors.centerIn: parent
|
return;
|
||||||
spacing: Theme.spacingL
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "error"
|
|
||||||
size: 48
|
|
||||||
color: Theme.error
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("System Monitor Unavailable")
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.error
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature.")
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingL
|
|
||||||
spacing: Theme.spacingL
|
|
||||||
visible: DgopService.dgopAvailable
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 40
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("System Monitor")
|
|
||||||
font.pixelSize: Theme.fontSizeLarge + 4
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
DankActionButton {
|
|
||||||
circular: false
|
|
||||||
iconName: "close"
|
|
||||||
iconSize: Theme.iconSize - 4
|
|
||||||
iconColor: Theme.surfaceText
|
|
||||||
onClicked: () => {
|
|
||||||
return processListModal.hide();
|
|
||||||
}
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 52
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
border.color: Theme.outlineLight
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 4
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: tabNames
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - (tabNames.length - 1) * 2) / tabNames.length
|
|
||||||
height: 44
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
|
|
||||||
border.color: currentTab === index ? Theme.primary : "transparent"
|
|
||||||
border.width: currentTab === index ? 1 : 0
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: {
|
|
||||||
const tabIcons = ["list_alt", "analytics", "settings"];
|
|
||||||
return tabIcons[index] || "tab";
|
|
||||||
}
|
|
||||||
size: Theme.iconSize - 2
|
|
||||||
color: currentTab === index ? Theme.primary : Theme.surfaceText
|
|
||||||
opacity: currentTab === index ? 1 : 0.7
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: currentTab === index ? Theme.primary : Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.verticalCenterOffset: -1
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: tabMouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: () => {
|
|
||||||
currentTab = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
border.color: Theme.outlineLight
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: processesTab
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingS
|
|
||||||
active: processListModal.visible && currentTab === 0
|
|
||||||
visible: currentTab === 0
|
|
||||||
opacity: currentTab === 0 ? 1 : 0
|
|
||||||
sourceComponent: processesTabComponent
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: performanceTab
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingS
|
|
||||||
active: processListModal.visible && currentTab === 1
|
|
||||||
visible: currentTab === 1
|
|
||||||
opacity: currentTab === 1 ? 1 : 0
|
|
||||||
sourceComponent: performanceTabComponent
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: systemTab
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingS
|
|
||||||
active: processListModal.visible && currentTab === 2
|
|
||||||
visible: currentTab === 2
|
|
||||||
opacity: currentTab === 2 ? 1 : 0
|
|
||||||
sourceComponent: systemTabComponent
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 400
|
||||||
|
height: 200
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1)
|
||||||
|
border.color: Theme.error
|
||||||
|
border.width: 2
|
||||||
|
visible: !DgopService.dgopAvailable
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "error"
|
||||||
|
size: 48
|
||||||
|
color: Theme.error
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("System Monitor Unavailable")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.error
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature.")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
visible: DgopService.dgopAvailable
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 40
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("System Monitor")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge + 4
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
circular: false
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: () => {
|
||||||
|
processListModal.hide();
|
||||||
|
}
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 52
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.color: Theme.outlineLight
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 4
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: tabNames
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: (parent.width - (tabNames.length - 1) * 2) / tabNames.length
|
||||||
|
height: 44
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
|
||||||
|
border.color: currentTab === index ? Theme.primary : "transparent"
|
||||||
|
border.width: currentTab === index ? 1 : 0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: {
|
||||||
|
const tabIcons = ["list_alt", "analytics", "settings"];
|
||||||
|
return tabIcons[index] || "tab";
|
||||||
|
}
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: currentTab === index ? Theme.primary : Theme.surfaceText
|
||||||
|
opacity: currentTab === index ? 1 : 0.7
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: currentTab === index ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.verticalCenterOffset: -1
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: tabMouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: () => {
|
||||||
|
currentTab = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on border.color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
|
border.color: Theme.outlineLight
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: processesTab
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
active: processListModal.visible && currentTab === 0
|
||||||
|
visible: currentTab === 0
|
||||||
|
opacity: currentTab === 0 ? 1 : 0
|
||||||
|
sourceComponent: processesTabComponent
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: performanceTab
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
active: processListModal.visible && currentTab === 1
|
||||||
|
visible: currentTab === 1
|
||||||
|
opacity: currentTab === 1 ? 1 : 0
|
||||||
|
sourceComponent: performanceTabComponent
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: systemTab
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
active: processListModal.visible && currentTab === 2
|
||||||
|
visible: currentTab === 2
|
||||||
|
opacity: currentTab === 2 ? 1 : 0
|
||||||
|
sourceComponent: systemTabComponent
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: lockScreenSection.implicitHeight + Theme.spacingL * 2
|
height: lockScreenSection.implicitHeight + Theme.spacingL * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
color: Theme.surfaceContainerHigh
|
||||||
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
|
border.width: 0
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: timeoutSection.implicitHeight + Theme.spacingL * 2
|
height: timeoutSection.implicitHeight + Theme.spacingL * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
color: Theme.surfaceContainerHigh
|
||||||
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
|
border.width: 0
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: powerMenuCustomSection.implicitHeight + Theme.spacingL * 2
|
height: powerMenuCustomSection.implicitHeight + Theme.spacingL * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
color: Theme.surfaceContainerHigh
|
||||||
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
|
border.width: 0
|
||||||
|
|
||||||
@@ -516,7 +516,7 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: powerCommandConfirmSection.implicitHeight + Theme.spacingL * 2
|
height: powerCommandConfirmSection.implicitHeight + Theme.spacingL * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
color: Theme.surfaceContainerHigh
|
||||||
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
|
border.width: 0
|
||||||
|
|
||||||
@@ -560,7 +560,7 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: powerCommandCustomization.implicitHeight + Theme.spacingL * 2
|
height: powerCommandCustomization.implicitHeight + Theme.spacingL * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
color: Theme.surfaceContainerHigh
|
||||||
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
|
border.width: 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,142 +1,87 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
import Quickshell
|
||||||
import Quickshell.Io
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
|
||||||
import qs.Modals.FileBrowser
|
import qs.Modals.FileBrowser
|
||||||
import qs.Modules.Settings
|
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
DankModal {
|
FloatingWindow {
|
||||||
id: settingsModal
|
id: settingsModal
|
||||||
|
|
||||||
layerNamespace: "dms:settings"
|
|
||||||
|
|
||||||
property Component settingsContent
|
|
||||||
property alias profileBrowser: profileBrowser
|
property alias profileBrowser: profileBrowser
|
||||||
|
property alias wallpaperBrowser: wallpaperBrowser
|
||||||
property int currentTabIndex: 0
|
property int currentTabIndex: 0
|
||||||
|
property bool shouldHaveFocus: visible
|
||||||
|
property bool allowFocusOverride: false
|
||||||
|
property alias shouldBeVisible: settingsModal.visible
|
||||||
|
property bool isCompactMode: width < 700
|
||||||
|
property bool menuVisible: !isCompactMode
|
||||||
|
property bool enableAnimations: true
|
||||||
|
|
||||||
signal closingModal()
|
signal closingModal
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
open();
|
visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
close();
|
visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (shouldBeVisible) {
|
visible = !visible;
|
||||||
hide();
|
}
|
||||||
} else {
|
|
||||||
show();
|
function toggleMenu() {
|
||||||
}
|
enableAnimations = true;
|
||||||
|
menuVisible = !menuVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
objectName: "settingsModal"
|
objectName: "settingsModal"
|
||||||
width: Math.min(800, screenWidth * 0.9)
|
title: I18n.tr("Settings", "settings window title")
|
||||||
height: Math.min(800, screenHeight * 0.85)
|
implicitWidth: 800
|
||||||
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
implicitHeight: 800
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
visible: false
|
visible: false
|
||||||
onBackgroundClicked: () => {
|
|
||||||
return hide();
|
onIsCompactModeChanged: {
|
||||||
}
|
enableAnimations = false;
|
||||||
content: settingsContent
|
if (!isCompactMode) {
|
||||||
onOpened: () => {
|
menuVisible = true;
|
||||||
|
}
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
modalFocusScope.forceActiveFocus()
|
enableAnimations = true;
|
||||||
if (contentLoader.item) {
|
});
|
||||||
contentLoader.item.forceActiveFocus()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible && shouldBeVisible) {
|
if (!visible) {
|
||||||
|
closingModal();
|
||||||
|
} else {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
modalFocusScope.forceActiveFocus()
|
if (contentFocusScope) {
|
||||||
if (contentLoader.item) {
|
contentFocusScope.forceActiveFocus();
|
||||||
contentLoader.item.forceActiveFocus()
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modalFocusScope.Keys.onPressed: event => {
|
|
||||||
const tabCount = 11
|
|
||||||
if (event.key === Qt.Key_Down) {
|
|
||||||
currentTabIndex = (currentTabIndex + 1) % tabCount
|
|
||||||
event.accepted = true
|
|
||||||
} else if (event.key === Qt.Key_Up) {
|
|
||||||
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount
|
|
||||||
event.accepted = true
|
|
||||||
} else if (event.key === Qt.Key_Tab && !event.modifiers) {
|
|
||||||
currentTabIndex = (currentTabIndex + 1) % tabCount
|
|
||||||
event.accepted = true
|
|
||||||
} else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && event.modifiers & Qt.ShiftModifier)) {
|
|
||||||
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount
|
|
||||||
event.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHandler {
|
|
||||||
function open(): string {
|
|
||||||
settingsModal.show();
|
|
||||||
return "SETTINGS_OPEN_SUCCESS";
|
|
||||||
}
|
|
||||||
|
|
||||||
function close(): string {
|
|
||||||
settingsModal.hide();
|
|
||||||
return "SETTINGS_CLOSE_SUCCESS";
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle(): string {
|
|
||||||
settingsModal.toggle();
|
|
||||||
return "SETTINGS_TOGGLE_SUCCESS";
|
|
||||||
}
|
|
||||||
|
|
||||||
target: "settings"
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHandler {
|
|
||||||
function browse(type: string) {
|
|
||||||
if (type === "wallpaper") {
|
|
||||||
wallpaperBrowser.allowStacking = false;
|
|
||||||
wallpaperBrowser.open();
|
|
||||||
} else if (type === "profile") {
|
|
||||||
profileBrowser.allowStacking = false;
|
|
||||||
profileBrowser.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
target: "file"
|
|
||||||
}
|
|
||||||
|
|
||||||
FileBrowserModal {
|
FileBrowserModal {
|
||||||
id: profileBrowser
|
id: profileBrowser
|
||||||
|
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
parentModal: settingsModal
|
parentModal: settingsModal
|
||||||
browserTitle: "Select Profile Image"
|
browserTitle: I18n.tr("Select Profile Image", "profile image file browser title")
|
||||||
browserIcon: "person"
|
browserIcon: "person"
|
||||||
browserType: "profile"
|
browserType: "profile"
|
||||||
showHiddenFiles: true
|
showHiddenFiles: true
|
||||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||||
onFileSelected: (path) => {
|
onFileSelected: path => {
|
||||||
PortalService.setProfileImage(path);
|
PortalService.setProfileImage(path);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
onDialogClosed: () => {
|
onDialogClosed: () => {
|
||||||
allowStacking = true;
|
allowStacking = true;
|
||||||
if (settingsModal.shouldBeVisible) {
|
|
||||||
Qt.callLater(() => {
|
|
||||||
settingsModal.modalFocusScope.forceActiveFocus()
|
|
||||||
if (settingsModal.contentLoader.item) {
|
|
||||||
settingsModal.contentLoader.item.forceActiveFocus()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,116 +90,166 @@ DankModal {
|
|||||||
|
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
parentModal: settingsModal
|
parentModal: settingsModal
|
||||||
browserTitle: "Select Wallpaper"
|
browserTitle: I18n.tr("Select Wallpaper", "wallpaper file browser title")
|
||||||
browserIcon: "wallpaper"
|
browserIcon: "wallpaper"
|
||||||
browserType: "wallpaper"
|
browserType: "wallpaper"
|
||||||
showHiddenFiles: true
|
showHiddenFiles: true
|
||||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||||
onFileSelected: (path) => {
|
onFileSelected: path => {
|
||||||
SessionData.setWallpaper(path);
|
SessionData.setWallpaper(path);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
onDialogClosed: () => {
|
onDialogClosed: () => {
|
||||||
allowStacking = true;
|
allowStacking = true;
|
||||||
if (settingsModal.shouldBeVisible) {
|
|
||||||
Qt.callLater(() => {
|
|
||||||
settingsModal.modalFocusScope.forceActiveFocus()
|
|
||||||
if (settingsModal.contentLoader.item) {
|
|
||||||
settingsModal.contentLoader.item.forceActiveFocus()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsContent: Component {
|
FocusScope {
|
||||||
Item {
|
id: contentFocusScope
|
||||||
id: rootScope
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Keys.onEscapePressed: event => {
|
anchors.fill: parent
|
||||||
settingsModal.hide()
|
focus: true
|
||||||
event.accepted = true
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
const tabCount = 11;
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
hide();
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (event.key === Qt.Key_Down) {
|
||||||
|
currentTabIndex = (currentTabIndex + 1) % tabCount;
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === Qt.Key_Up) {
|
||||||
|
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount;
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === Qt.Key_Tab && !event.modifiers) {
|
||||||
|
currentTabIndex = (currentTabIndex + 1) % tabCount;
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && event.modifiers & Qt.ShiftModifier)) {
|
||||||
|
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount;
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: Theme.spacingL
|
spacing: 0
|
||||||
anchors.rightMargin: Theme.spacingL
|
|
||||||
anchors.topMargin: Theme.spacingM
|
|
||||||
anchors.bottomMargin: Theme.spacingL
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 35
|
height: 48
|
||||||
|
z: 10
|
||||||
Row {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "settings"
|
|
||||||
size: Theme.iconSize
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Settings")
|
|
||||||
font.pixelSize: Theme.fontSizeXLarge
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Medium
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DankActionButton {
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
circular: false
|
|
||||||
iconName: "close"
|
|
||||||
iconSize: Theme.iconSize - 4
|
|
||||||
iconColor: Theme.surfaceText
|
|
||||||
onClicked: () => {
|
|
||||||
return settingsModal.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Theme.surfaceContainer
|
||||||
|
opacity: 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
anchors.left: parent.left
|
||||||
height: parent.height - 35
|
anchors.leftMargin: Theme.spacingL
|
||||||
spacing: 0
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
SettingsSidebar {
|
DankActionButton {
|
||||||
id: sidebar
|
visible: settingsModal.isCompactMode
|
||||||
|
circular: false
|
||||||
parentModal: settingsModal
|
iconName: "menu"
|
||||||
currentIndex: settingsModal.currentTabIndex
|
iconSize: Theme.iconSize - 4
|
||||||
onCurrentIndexChanged: {
|
iconColor: Theme.surfaceText
|
||||||
settingsModal.currentTabIndex = currentIndex
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: () => {
|
||||||
|
settingsModal.toggleMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "settings"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Settings")
|
||||||
|
font.pixelSize: Theme.fontSizeXLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Theme.spacingM
|
||||||
|
circular: false
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: () => {
|
||||||
|
settingsModal.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - 48
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
SettingsSidebar {
|
||||||
|
id: sidebar
|
||||||
|
|
||||||
|
x: 0
|
||||||
|
width: settingsModal.isCompactMode ? parent.width : 270
|
||||||
|
visible: settingsModal.isCompactMode ? settingsModal.menuVisible : true
|
||||||
|
parentModal: settingsModal
|
||||||
|
currentIndex: settingsModal.currentTabIndex
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
settingsModal.currentTabIndex = currentIndex;
|
||||||
|
if (settingsModal.isCompactMode) {
|
||||||
|
settingsModal.enableAnimations = true;
|
||||||
|
settingsModal.menuVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
x: settingsModal.isCompactMode ? (settingsModal.menuVisible ? parent.width : 0) : sidebar.width
|
||||||
|
width: settingsModal.isCompactMode ? parent.width : parent.width - sidebar.width
|
||||||
|
height: parent.height
|
||||||
|
clip: true
|
||||||
|
|
||||||
SettingsContent {
|
SettingsContent {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
width: parent.width - sidebar.width
|
width: Math.min(550, parent.width - Theme.spacingL * 2)
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
parentModal: settingsModal
|
parentModal: settingsModal
|
||||||
currentIndex: settingsModal.currentTabIndex
|
currentIndex: settingsModal.currentTabIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
enabled: settingsModal.enableAnimations
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,47 +10,59 @@ Rectangle {
|
|||||||
|
|
||||||
property int currentIndex: 0
|
property int currentIndex: 0
|
||||||
property var parentModal: null
|
property var parentModal: null
|
||||||
readonly property var sidebarItems: [{
|
readonly property var sidebarItems: [
|
||||||
"text": I18n.tr("Personalization"),
|
{
|
||||||
"icon": "person"
|
"text": I18n.tr("Personalization"),
|
||||||
}, {
|
"icon": "person"
|
||||||
"text": I18n.tr("Time & Weather"),
|
},
|
||||||
"icon": "schedule"
|
{
|
||||||
}, {
|
"text": I18n.tr("Time & Weather"),
|
||||||
"text": I18n.tr("Dank Bar"),
|
"icon": "schedule"
|
||||||
"icon": "toolbar"
|
},
|
||||||
}, {
|
{
|
||||||
"text": I18n.tr("Widgets"),
|
"text": I18n.tr("Dank Bar"),
|
||||||
"icon": "widgets"
|
"icon": "toolbar"
|
||||||
}, {
|
},
|
||||||
"text": I18n.tr("Dock"),
|
{
|
||||||
"icon": "dock_to_bottom"
|
"text": I18n.tr("Widgets"),
|
||||||
}, {
|
"icon": "widgets"
|
||||||
"text": I18n.tr("Displays"),
|
},
|
||||||
"icon": "monitor"
|
{
|
||||||
}, {
|
"text": I18n.tr("Dock"),
|
||||||
"text": I18n.tr("Launcher"),
|
"icon": "dock_to_bottom"
|
||||||
"icon": "apps"
|
},
|
||||||
}, {
|
{
|
||||||
"text": I18n.tr("Theme & Colors"),
|
"text": I18n.tr("Displays"),
|
||||||
"icon": "palette"
|
"icon": "monitor"
|
||||||
}, {
|
},
|
||||||
"text": I18n.tr("Power & Security"),
|
{
|
||||||
"icon": "power"
|
"text": I18n.tr("Launcher"),
|
||||||
}, {
|
"icon": "apps"
|
||||||
"text": I18n.tr("Plugins"),
|
},
|
||||||
"icon": "extension"
|
{
|
||||||
}, {
|
"text": I18n.tr("Theme & Colors"),
|
||||||
"text": I18n.tr("About"),
|
"icon": "palette"
|
||||||
"icon": "info"
|
},
|
||||||
}]
|
{
|
||||||
|
"text": I18n.tr("Power & Security"),
|
||||||
|
"icon": "power"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": I18n.tr("Plugins"),
|
||||||
|
"icon": "extension"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": I18n.tr("About"),
|
||||||
|
"icon": "info"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
function navigateNext() {
|
function navigateNext() {
|
||||||
currentIndex = (currentIndex + 1) % sidebarItems.length
|
currentIndex = (currentIndex + 1) % sidebarItems.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigatePrevious() {
|
function navigatePrevious() {
|
||||||
currentIndex = (currentIndex - 1 + sidebarItems.length) % sidebarItems.length
|
currentIndex = (currentIndex - 1 + sidebarItems.length) % sidebarItems.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
width: 270
|
width: 270
|
||||||
@@ -61,31 +73,32 @@ Rectangle {
|
|||||||
DankFlickable {
|
DankFlickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
clip: true
|
clip: true
|
||||||
contentHeight: sidebarColumn.implicitHeight
|
contentHeight: sidebarColumn.height
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: sidebarColumn
|
id: sidebarColumn
|
||||||
|
|
||||||
anchors.fill: parent
|
width: parent.width
|
||||||
anchors.leftMargin: Theme.spacingS
|
leftPadding: Theme.spacingS
|
||||||
anchors.rightMargin: Theme.spacingS
|
rightPadding: Theme.spacingS
|
||||||
anchors.bottomMargin: Theme.spacingS
|
bottomPadding: Theme.spacingL
|
||||||
anchors.topMargin: Theme.spacingM + 2
|
topPadding: Theme.spacingM + 2
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
ProfileSection {
|
ProfileSection {
|
||||||
|
width: parent.width - parent.leftPadding - parent.rightPadding
|
||||||
parentModal: sidebarContainer.parentModal
|
parentModal: sidebarContainer.parentModal
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width - Theme.spacingS * 2
|
width: parent.width - parent.leftPadding - parent.rightPadding
|
||||||
height: 1
|
height: 1
|
||||||
color: Theme.outline
|
color: Theme.outline
|
||||||
opacity: 0.2
|
opacity: 0.2
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width - parent.leftPadding - parent.rightPadding
|
||||||
height: Theme.spacingL
|
height: Theme.spacingL
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +113,7 @@ Rectangle {
|
|||||||
|
|
||||||
property bool isActive: sidebarContainer.currentIndex === index
|
property bool isActive: sidebarContainer.currentIndex === index
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width - parent.leftPadding - parent.rightPadding
|
||||||
height: 44
|
height: 44
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: isActive ? Theme.primary : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
|
color: isActive ? Theme.primary : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||||
@@ -125,7 +138,6 @@ Rectangle {
|
|||||||
font.weight: parent.parent.isActive ? Font.Medium : Font.Normal
|
font.weight: parent.parent.isActive ? Font.Medium : Font.Normal
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -144,15 +156,9 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,10 @@ Item {
|
|||||||
onAppLaunched: () => {
|
onAppLaunched: () => {
|
||||||
if (parentModal)
|
if (parentModal)
|
||||||
parentModal.hide()
|
parentModal.hide()
|
||||||
|
|
||||||
|
if (SettingsData.spotlightCloseNiriOverview && NiriService.inOverview) {
|
||||||
|
NiriService.toggleOverview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onViewModeSelected: mode => {
|
onViewModeSelected: mode => {
|
||||||
SettingsData.set("spotlightModalViewMode", mode)
|
SettingsData.set("spotlightModalViewMode", mode)
|
||||||
@@ -170,6 +174,10 @@ Item {
|
|||||||
onFileOpened: () => {
|
onFileOpened: () => {
|
||||||
if (parentModal)
|
if (parentModal)
|
||||||
parentModal.hide()
|
parentModal.hide()
|
||||||
|
|
||||||
|
if (SettingsData.spotlightCloseNiriOverview && NiriService.inOverview) {
|
||||||
|
NiriService.toggleOverview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,88 +1,92 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import Quickshell.Hyprland
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Widgets
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Modules.AppDrawer
|
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
DankModal {
|
DankModal {
|
||||||
id: spotlightModal
|
id: spotlightModal
|
||||||
|
|
||||||
layerNamespace: "dms:spotlight"
|
layerNamespace: "dms:spotlight"
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: [spotlightModal]
|
||||||
|
active: CompositorService.isHyprland && spotlightModal.shouldHaveFocus
|
||||||
|
}
|
||||||
|
|
||||||
property bool spotlightOpen: false
|
property bool spotlightOpen: false
|
||||||
property alias spotlightContent: spotlightContentInstance
|
property alias spotlightContent: spotlightContentInstance
|
||||||
|
property bool openedFromOverview: false
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
spotlightOpen = true
|
openedFromOverview = false;
|
||||||
open()
|
spotlightOpen = true;
|
||||||
|
open();
|
||||||
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (spotlightContent && spotlightContent.searchField) {
|
if (spotlightContent && spotlightContent.searchField) {
|
||||||
spotlightContent.searchField.forceActiveFocus()
|
spotlightContent.searchField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWithQuery(query) {
|
function showWithQuery(query) {
|
||||||
if (spotlightContent) {
|
if (spotlightContent) {
|
||||||
if (spotlightContent.appLauncher) {
|
if (spotlightContent.appLauncher) {
|
||||||
spotlightContent.appLauncher.searchQuery = query
|
spotlightContent.appLauncher.searchQuery = query;
|
||||||
}
|
}
|
||||||
if (spotlightContent.searchField) {
|
if (spotlightContent.searchField) {
|
||||||
spotlightContent.searchField.text = query
|
spotlightContent.searchField.text = query;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spotlightOpen = true
|
spotlightOpen = true;
|
||||||
open()
|
open();
|
||||||
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (spotlightContent && spotlightContent.searchField) {
|
if (spotlightContent && spotlightContent.searchField) {
|
||||||
spotlightContent.searchField.forceActiveFocus()
|
spotlightContent.searchField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
spotlightOpen = false
|
openedFromOverview = false;
|
||||||
close()
|
spotlightOpen = false;
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDialogClosed: {
|
onDialogClosed: {
|
||||||
if (spotlightContent) {
|
if (spotlightContent) {
|
||||||
if (spotlightContent.appLauncher) {
|
if (spotlightContent.appLauncher) {
|
||||||
spotlightContent.appLauncher.searchQuery = ""
|
spotlightContent.appLauncher.searchQuery = "";
|
||||||
spotlightContent.appLauncher.selectedIndex = 0
|
spotlightContent.appLauncher.selectedIndex = 0;
|
||||||
spotlightContent.appLauncher.setCategory(I18n.tr("All"))
|
spotlightContent.appLauncher.setCategory(I18n.tr("All"));
|
||||||
}
|
}
|
||||||
if (spotlightContent.fileSearchController) {
|
if (spotlightContent.fileSearchController) {
|
||||||
spotlightContent.fileSearchController.reset()
|
spotlightContent.fileSearchController.reset();
|
||||||
}
|
}
|
||||||
if (spotlightContent.resetScroll) {
|
if (spotlightContent.resetScroll) {
|
||||||
spotlightContent.resetScroll()
|
spotlightContent.resetScroll();
|
||||||
}
|
}
|
||||||
if (spotlightContent.searchField) {
|
if (spotlightContent.searchField) {
|
||||||
spotlightContent.searchField.text = ""
|
spotlightContent.searchField.text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (spotlightOpen) {
|
if (spotlightOpen) {
|
||||||
hide()
|
hide();
|
||||||
} else {
|
} else {
|
||||||
show()
|
show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeVisible: spotlightOpen
|
shouldBeVisible: spotlightOpen
|
||||||
width: 500
|
modalWidth: 500
|
||||||
height: 600
|
modalHeight: 600
|
||||||
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
cornerRadius: Theme.cornerRadius
|
cornerRadius: Theme.cornerRadius
|
||||||
borderColor: Theme.outlineMedium
|
borderColor: Theme.outlineMedium
|
||||||
@@ -90,25 +94,25 @@ DankModal {
|
|||||||
enableShadow: true
|
enableShadow: true
|
||||||
keepContentLoaded: true
|
keepContentLoaded: true
|
||||||
onVisibleChanged: () => {
|
onVisibleChanged: () => {
|
||||||
if (visible && !spotlightOpen) {
|
if (visible && !spotlightOpen) {
|
||||||
show()
|
show();
|
||||||
}
|
}
|
||||||
if (visible && spotlightContent) {
|
if (visible && spotlightContent) {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (spotlightContent.searchField) {
|
if (spotlightContent.searchField) {
|
||||||
spotlightContent.searchField.forceActiveFocus()
|
spotlightContent.searchField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
return hide()
|
return hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCloseAllModalsExcept(excludedModal) {
|
function onCloseAllModalsExcept(excludedModal) {
|
||||||
if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) {
|
if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) {
|
||||||
spotlightOpen = false
|
spotlightOpen = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,32 +121,32 @@ DankModal {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open(): string {
|
function open(): string {
|
||||||
spotlightModal.show()
|
spotlightModal.show();
|
||||||
return "SPOTLIGHT_OPEN_SUCCESS"
|
return "SPOTLIGHT_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): string {
|
function close(): string {
|
||||||
spotlightModal.hide()
|
spotlightModal.hide();
|
||||||
return "SPOTLIGHT_CLOSE_SUCCESS"
|
return "SPOTLIGHT_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
spotlightModal.toggle()
|
spotlightModal.toggle();
|
||||||
return "SPOTLIGHT_TOGGLE_SUCCESS"
|
return "SPOTLIGHT_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function openQuery(query: string): string {
|
function openQuery(query: string): string {
|
||||||
spotlightModal.showWithQuery(query)
|
spotlightModal.showWithQuery(query);
|
||||||
return "SPOTLIGHT_OPEN_QUERY_SUCCESS"
|
return "SPOTLIGHT_OPEN_QUERY_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleQuery(query: string): string {
|
function toggleQuery(query: string): string {
|
||||||
if (spotlightModal.spotlightOpen) {
|
if (spotlightModal.spotlightOpen) {
|
||||||
spotlightModal.hide()
|
spotlightModal.hide();
|
||||||
} else {
|
} else {
|
||||||
spotlightModal.showWithQuery(query)
|
spotlightModal.showWithQuery(query);
|
||||||
}
|
}
|
||||||
return "SPOTLIGHT_TOGGLE_QUERY_SUCCESS"
|
return "SPOTLIGHT_TOGGLE_QUERY_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
target: "spotlight"
|
target: "spotlight"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -8,6 +9,12 @@ DankModal {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
layerNamespace: "dms:wifi-password"
|
layerNamespace: "dms:wifi-password"
|
||||||
|
keepPopoutsOpen: true
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: [root]
|
||||||
|
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||||
|
}
|
||||||
|
|
||||||
property string wifiPasswordSSID: ""
|
property string wifiPasswordSSID: ""
|
||||||
property string wifiPasswordInput: ""
|
property string wifiPasswordInput: ""
|
||||||
@@ -29,118 +36,124 @@ DankModal {
|
|||||||
property string connectionType: ""
|
property string connectionType: ""
|
||||||
|
|
||||||
function show(ssid) {
|
function show(ssid) {
|
||||||
wifiPasswordSSID = ssid
|
wifiPasswordSSID = ssid;
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = "";
|
||||||
wifiAnonymousIdentityInput = ""
|
wifiAnonymousIdentityInput = "";
|
||||||
wifiDomainInput = ""
|
wifiDomainInput = "";
|
||||||
isPromptMode = false
|
isPromptMode = false;
|
||||||
promptToken = ""
|
promptToken = "";
|
||||||
promptReason = ""
|
promptReason = "";
|
||||||
promptFields = []
|
promptFields = [];
|
||||||
promptSetting = ""
|
promptSetting = "";
|
||||||
isVpnPrompt = false
|
isVpnPrompt = false;
|
||||||
connectionName = ""
|
connectionName = "";
|
||||||
vpnServiceType = ""
|
vpnServiceType = "";
|
||||||
connectionType = ""
|
connectionType = "";
|
||||||
|
|
||||||
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid)
|
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid);
|
||||||
requiresEnterprise = network?.enterprise || false
|
requiresEnterprise = network?.enterprise || false;
|
||||||
|
|
||||||
open()
|
open();
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item) {
|
if (contentLoader.item) {
|
||||||
if (requiresEnterprise && contentLoader.item.usernameInput) {
|
if (requiresEnterprise && contentLoader.item.usernameInput) {
|
||||||
contentLoader.item.usernameInput.forceActiveFocus()
|
contentLoader.item.usernameInput.forceActiveFocus();
|
||||||
} else if (contentLoader.item.passwordInput) {
|
} else if (contentLoader.item.passwordInput) {
|
||||||
contentLoader.item.passwordInput.forceActiveFocus()
|
contentLoader.item.passwordInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService) {
|
function showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService) {
|
||||||
isPromptMode = true
|
isPromptMode = true;
|
||||||
promptToken = token
|
promptToken = token;
|
||||||
promptReason = reason
|
promptReason = reason;
|
||||||
promptFields = fields || []
|
promptFields = fields || [];
|
||||||
promptSetting = setting || "802-11-wireless-security"
|
promptSetting = setting || "802-11-wireless-security";
|
||||||
connectionType = connType || "802-11-wireless"
|
connectionType = connType || "802-11-wireless";
|
||||||
connectionName = connName || ssid || ""
|
connectionName = connName || ssid || "";
|
||||||
vpnServiceType = vpnService || ""
|
vpnServiceType = vpnService || "";
|
||||||
|
|
||||||
isVpnPrompt = (connectionType === "vpn" || connectionType === "wireguard")
|
isVpnPrompt = (connectionType === "vpn" || connectionType === "wireguard");
|
||||||
wifiPasswordSSID = isVpnPrompt ? connectionName : ssid
|
wifiPasswordSSID = isVpnPrompt ? connectionName : ssid;
|
||||||
|
|
||||||
requiresEnterprise = setting === "802-1x"
|
requiresEnterprise = setting === "802-1x";
|
||||||
|
|
||||||
if (reason === "wrong-password") {
|
if (reason === "wrong-password") {
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = "";
|
||||||
} else {
|
} else {
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = "";
|
||||||
wifiAnonymousIdentityInput = ""
|
wifiAnonymousIdentityInput = "";
|
||||||
wifiDomainInput = ""
|
wifiDomainInput = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
open()
|
open();
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item) {
|
if (contentLoader.item) {
|
||||||
if (reason === "wrong-password" && contentLoader.item.passwordInput) {
|
if (reason === "wrong-password" && contentLoader.item.passwordInput) {
|
||||||
contentLoader.item.passwordInput.text = ""
|
contentLoader.item.passwordInput.text = "";
|
||||||
contentLoader.item.passwordInput.forceActiveFocus()
|
contentLoader.item.passwordInput.forceActiveFocus();
|
||||||
} else if (requiresEnterprise && contentLoader.item.usernameInput) {
|
} else if (requiresEnterprise && contentLoader.item.usernameInput) {
|
||||||
contentLoader.item.usernameInput.forceActiveFocus()
|
contentLoader.item.usernameInput.forceActiveFocus();
|
||||||
} else if (contentLoader.item.passwordInput) {
|
} else if (contentLoader.item.passwordInput) {
|
||||||
contentLoader.item.passwordInput.forceActiveFocus()
|
contentLoader.item.passwordInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
width: 420
|
modalWidth: 420
|
||||||
height: requiresEnterprise ? 430 : 230
|
modalHeight: {
|
||||||
|
if (requiresEnterprise)
|
||||||
|
return 430;
|
||||||
|
if (isVpnPrompt)
|
||||||
|
return 260;
|
||||||
|
return 230;
|
||||||
|
}
|
||||||
onShouldBeVisibleChanged: () => {
|
onShouldBeVisibleChanged: () => {
|
||||||
if (!shouldBeVisible) {
|
if (!shouldBeVisible) {
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = "";
|
||||||
wifiAnonymousIdentityInput = ""
|
wifiAnonymousIdentityInput = "";
|
||||||
wifiDomainInput = ""
|
wifiDomainInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onOpened: {
|
onOpened: {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item) {
|
if (contentLoader.item) {
|
||||||
if (requiresEnterprise && contentLoader.item.usernameInput) {
|
if (requiresEnterprise && contentLoader.item.usernameInput) {
|
||||||
contentLoader.item.usernameInput.forceActiveFocus()
|
contentLoader.item.usernameInput.forceActiveFocus();
|
||||||
} else if (contentLoader.item.passwordInput) {
|
} else if (contentLoader.item.passwordInput) {
|
||||||
contentLoader.item.passwordInput.forceActiveFocus()
|
contentLoader.item.passwordInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
if (isPromptMode) {
|
if (isPromptMode) {
|
||||||
NetworkService.cancelCredentials(promptToken)
|
NetworkService.cancelCredentials(promptToken);
|
||||||
}
|
}
|
||||||
close()
|
close();
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = "";
|
||||||
wifiAnonymousIdentityInput = ""
|
wifiAnonymousIdentityInput = "";
|
||||||
wifiDomainInput = ""
|
wifiDomainInput = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: NetworkService
|
target: NetworkService
|
||||||
|
|
||||||
function onPasswordDialogShouldReopenChanged() {
|
function onPasswordDialogShouldReopenChanged() {
|
||||||
if (NetworkService.passwordDialogShouldReopen && NetworkService.connectingSSID !== "") {
|
if (NetworkService.passwordDialogShouldReopen && NetworkService.connectingSSID !== "") {
|
||||||
wifiPasswordSSID = NetworkService.connectingSSID
|
wifiPasswordSSID = NetworkService.connectingSSID;
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
open()
|
open();
|
||||||
NetworkService.passwordDialogShouldReopen = false
|
NetworkService.passwordDialogShouldReopen = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,16 +168,16 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
if (isPromptMode) {
|
if (isPromptMode) {
|
||||||
NetworkService.cancelCredentials(promptToken)
|
NetworkService.cancelCredentials(promptToken);
|
||||||
}
|
}
|
||||||
close()
|
close();
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = "";
|
||||||
wifiAnonymousIdentityInput = ""
|
wifiAnonymousIdentityInput = "";
|
||||||
wifiDomainInput = ""
|
wifiDomainInput = "";
|
||||||
event.accepted = true
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -181,9 +194,9 @@ DankModal {
|
|||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (isVpnPrompt) {
|
if (isVpnPrompt) {
|
||||||
return I18n.tr("Connect to VPN")
|
return I18n.tr("Connect to VPN");
|
||||||
}
|
}
|
||||||
return I18n.tr("Connect to Wi-Fi")
|
return I18n.tr("Connect to Wi-Fi");
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -197,10 +210,10 @@ DankModal {
|
|||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (isVpnPrompt) {
|
if (isVpnPrompt) {
|
||||||
return I18n.tr("Enter password for ") + wifiPasswordSSID
|
return I18n.tr("Enter password for ") + wifiPasswordSSID;
|
||||||
}
|
}
|
||||||
const prefix = requiresEnterprise ? I18n.tr("Enter credentials for ") : I18n.tr("Enter password for ")
|
const prefix = requiresEnterprise ? I18n.tr("Enter credentials for ") : I18n.tr("Enter password for ");
|
||||||
return prefix + wifiPasswordSSID
|
return prefix + wifiPasswordSSID;
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
@@ -223,15 +236,15 @@ DankModal {
|
|||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 4
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (isPromptMode) {
|
if (isPromptMode) {
|
||||||
NetworkService.cancelCredentials(promptToken)
|
NetworkService.cancelCredentials(promptToken);
|
||||||
}
|
}
|
||||||
close()
|
close();
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = "";
|
||||||
wifiAnonymousIdentityInput = ""
|
wifiAnonymousIdentityInput = "";
|
||||||
wifiDomainInput = ""
|
wifiDomainInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,8 +260,8 @@ DankModal {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
usernameInput.forceActiveFocus()
|
usernameInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankTextField {
|
DankTextField {
|
||||||
@@ -262,13 +275,13 @@ DankModal {
|
|||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
enabled: root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
wifiUsernameInput = text
|
wifiUsernameInput = text;
|
||||||
}
|
}
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
if (passwordInput) {
|
if (passwordInput) {
|
||||||
passwordInput.forceActiveFocus()
|
passwordInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,8 +296,8 @@ DankModal {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
passwordInput.forceActiveFocus()
|
passwordInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankTextField {
|
DankTextField {
|
||||||
@@ -300,43 +313,42 @@ DankModal {
|
|||||||
focus: !requiresEnterprise
|
focus: !requiresEnterprise
|
||||||
enabled: root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
wifiPasswordInput = text
|
wifiPasswordInput = text;
|
||||||
}
|
}
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
if (isPromptMode) {
|
if (isPromptMode) {
|
||||||
const secrets = {}
|
const secrets = {};
|
||||||
if (isVpnPrompt) {
|
if (isVpnPrompt) {
|
||||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
if (passwordInput.text)
|
||||||
} else if (promptSetting === "802-11-wireless-security") {
|
secrets["password"] = passwordInput.text;
|
||||||
secrets["psk"] = passwordInput.text
|
} else if (promptSetting === "802-11-wireless-security") {
|
||||||
} else if (promptSetting === "802-1x") {
|
secrets["psk"] = passwordInput.text;
|
||||||
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
} else if (promptSetting === "802-1x") {
|
||||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
if (usernameInput.text)
|
||||||
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput
|
secrets["identity"] = usernameInput.text;
|
||||||
}
|
if (passwordInput.text)
|
||||||
NetworkService.submitCredentials(promptToken, secrets, true)
|
secrets["password"] = passwordInput.text;
|
||||||
} else {
|
if (wifiAnonymousIdentityInput)
|
||||||
const username = requiresEnterprise ? usernameInput.text : ""
|
secrets["anonymous-identity"] = wifiAnonymousIdentityInput;
|
||||||
NetworkService.connectToWifi(
|
}
|
||||||
wifiPasswordSSID,
|
NetworkService.submitCredentials(promptToken, secrets, savePasswordCheckbox.checked);
|
||||||
passwordInput.text,
|
} else {
|
||||||
username,
|
const username = requiresEnterprise ? usernameInput.text : "";
|
||||||
wifiAnonymousIdentityInput,
|
NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text, username, wifiAnonymousIdentityInput, wifiDomainInput);
|
||||||
wifiDomainInput
|
}
|
||||||
)
|
close();
|
||||||
}
|
wifiPasswordInput = "";
|
||||||
close()
|
wifiUsernameInput = "";
|
||||||
wifiPasswordInput = ""
|
wifiAnonymousIdentityInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiDomainInput = "";
|
||||||
wifiAnonymousIdentityInput = ""
|
passwordInput.text = "";
|
||||||
wifiDomainInput = ""
|
if (requiresEnterprise)
|
||||||
passwordInput.text = ""
|
usernameInput.text = "";
|
||||||
if (requiresEnterprise) usernameInput.text = ""
|
}
|
||||||
}
|
|
||||||
Component.onCompleted: () => {
|
Component.onCompleted: () => {
|
||||||
if (root.shouldBeVisible && !requiresEnterprise)
|
if (root.shouldBeVisible && !requiresEnterprise)
|
||||||
focusDelayTimer.start()
|
focusDelayTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: focusDelayTimer
|
id: focusDelayTimer
|
||||||
@@ -344,14 +356,14 @@ DankModal {
|
|||||||
interval: 100
|
interval: 100
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: () => {
|
onTriggered: () => {
|
||||||
if (root.shouldBeVisible) {
|
if (root.shouldBeVisible) {
|
||||||
if (requiresEnterprise && usernameInput) {
|
if (requiresEnterprise && usernameInput) {
|
||||||
usernameInput.forceActiveFocus()
|
usernameInput.forceActiveFocus();
|
||||||
} else {
|
} else {
|
||||||
passwordInput.forceActiveFocus()
|
passwordInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -359,7 +371,7 @@ DankModal {
|
|||||||
|
|
||||||
function onShouldBeVisibleChanged() {
|
function onShouldBeVisibleChanged() {
|
||||||
if (root.shouldBeVisible)
|
if (root.shouldBeVisible)
|
||||||
focusDelayTimer.start()
|
focusDelayTimer.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,8 +389,8 @@ DankModal {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
anonInput.forceActiveFocus()
|
anonInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankTextField {
|
DankTextField {
|
||||||
@@ -392,8 +404,8 @@ DankModal {
|
|||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
enabled: root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
wifiAnonymousIdentityInput = text
|
wifiAnonymousIdentityInput = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,8 +421,8 @@ DankModal {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
domainMatchInput.forceActiveFocus()
|
domainMatchInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankTextField {
|
DankTextField {
|
||||||
@@ -424,49 +436,96 @@ DankModal {
|
|||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
enabled: root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
wifiDomainInput = text
|
wifiDomainInput = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Column {
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
Rectangle {
|
Row {
|
||||||
id: showPasswordCheckbox
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
property bool checked: false
|
Rectangle {
|
||||||
|
id: showPasswordCheckbox
|
||||||
|
|
||||||
width: 20
|
property bool checked: false
|
||||||
height: 20
|
|
||||||
radius: 4
|
|
||||||
color: checked ? Theme.primary : "transparent"
|
|
||||||
border.color: checked ? Theme.primary : Theme.outlineButton
|
|
||||||
border.width: 2
|
|
||||||
|
|
||||||
DankIcon {
|
width: 20
|
||||||
anchors.centerIn: parent
|
height: 20
|
||||||
name: "check"
|
radius: 4
|
||||||
size: 12
|
color: checked ? Theme.primary : "transparent"
|
||||||
color: Theme.background
|
border.color: checked ? Theme.primary : Theme.outlineButton
|
||||||
visible: parent.checked
|
border.width: 2
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "check"
|
||||||
|
size: 12
|
||||||
|
color: Theme.background
|
||||||
|
visible: parent.checked
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: () => {
|
||||||
|
showPasswordCheckbox.checked = !showPasswordCheckbox.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
StyledText {
|
||||||
anchors.fill: parent
|
text: I18n.tr("Show password")
|
||||||
hoverEnabled: true
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
cursorShape: Qt.PointingHandCursor
|
color: Theme.surfaceText
|
||||||
onClicked: () => {
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
showPasswordCheckbox.checked = !showPasswordCheckbox.checked
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
Row {
|
||||||
text: I18n.tr("Show password")
|
spacing: Theme.spacingS
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
visible: isVpnPrompt
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
Rectangle {
|
||||||
|
id: savePasswordCheckbox
|
||||||
|
|
||||||
|
property bool checked: false
|
||||||
|
|
||||||
|
width: 20
|
||||||
|
height: 20
|
||||||
|
radius: 4
|
||||||
|
color: checked ? Theme.primary : "transparent"
|
||||||
|
border.color: checked ? Theme.primary : Theme.outlineButton
|
||||||
|
border.width: 2
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "check"
|
||||||
|
size: 12
|
||||||
|
color: Theme.background
|
||||||
|
visible: parent.checked
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: () => {
|
||||||
|
savePasswordCheckbox.checked = !savePasswordCheckbox.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Save password")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,15 +563,15 @@ DankModal {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (isPromptMode) {
|
if (isPromptMode) {
|
||||||
NetworkService.cancelCredentials(promptToken)
|
NetworkService.cancelCredentials(promptToken);
|
||||||
}
|
}
|
||||||
close()
|
close();
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = "";
|
||||||
wifiAnonymousIdentityInput = ""
|
wifiAnonymousIdentityInput = "";
|
||||||
wifiDomainInput = ""
|
wifiDomainInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,9 +582,9 @@ DankModal {
|
|||||||
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
enabled: {
|
enabled: {
|
||||||
if (isVpnPrompt) {
|
if (isVpnPrompt) {
|
||||||
return passwordInput.text.length > 0
|
return passwordInput.text.length > 0;
|
||||||
}
|
}
|
||||||
return requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0
|
return requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0;
|
||||||
}
|
}
|
||||||
opacity: enabled ? 1 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
@@ -547,36 +606,35 @@ DankModal {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (isPromptMode) {
|
if (isPromptMode) {
|
||||||
const secrets = {}
|
const secrets = {};
|
||||||
if (isVpnPrompt) {
|
if (isVpnPrompt) {
|
||||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
if (passwordInput.text)
|
||||||
} else if (promptSetting === "802-11-wireless-security") {
|
secrets["password"] = passwordInput.text;
|
||||||
secrets["psk"] = passwordInput.text
|
} else if (promptSetting === "802-11-wireless-security") {
|
||||||
} else if (promptSetting === "802-1x") {
|
secrets["psk"] = passwordInput.text;
|
||||||
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
} else if (promptSetting === "802-1x") {
|
||||||
if (passwordInput.text) secrets["password"] = passwordInput.text
|
if (usernameInput.text)
|
||||||
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput
|
secrets["identity"] = usernameInput.text;
|
||||||
}
|
if (passwordInput.text)
|
||||||
NetworkService.submitCredentials(promptToken, secrets, true)
|
secrets["password"] = passwordInput.text;
|
||||||
} else {
|
if (wifiAnonymousIdentityInput)
|
||||||
const username = requiresEnterprise ? usernameInput.text : ""
|
secrets["anonymous-identity"] = wifiAnonymousIdentityInput;
|
||||||
NetworkService.connectToWifi(
|
}
|
||||||
wifiPasswordSSID,
|
NetworkService.submitCredentials(promptToken, secrets, savePasswordCheckbox.checked);
|
||||||
passwordInput.text,
|
} else {
|
||||||
username,
|
const username = requiresEnterprise ? usernameInput.text : "";
|
||||||
wifiAnonymousIdentityInput,
|
NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text, username, wifiAnonymousIdentityInput, wifiDomainInput);
|
||||||
wifiDomainInput
|
}
|
||||||
)
|
close();
|
||||||
}
|
wifiPasswordInput = "";
|
||||||
close()
|
wifiUsernameInput = "";
|
||||||
wifiPasswordInput = ""
|
wifiAnonymousIdentityInput = "";
|
||||||
wifiUsernameInput = ""
|
wifiDomainInput = "";
|
||||||
wifiAnonymousIdentityInput = ""
|
passwordInput.text = "";
|
||||||
wifiDomainInput = ""
|
if (requiresEnterprise)
|
||||||
passwordInput.text = ""
|
usernameInput.text = "";
|
||||||
if (requiresEnterprise) usernameInput.text = ""
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user