1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-02 03:28:28 -04:00

Compare commits

..

5 Commits

Author SHA1 Message Date
purian23 e791337ae9 fix(lint): drop unused constraints import 2026-06-27 22:35:33 -04:00
purian23 a8c15fcde9 feat(popouts): enhance hover functionality and introduce hover dismiss features 2026-06-27 22:21:03 -04:00
purian23 601d4104a3 feat(popouts): hover & settings cleanup 2026-06-27 01:59:45 -04:00
purian23 7979fb2b0e feat(popouts): complete initial hover implementation 2026-06-26 23:34:46 -04:00
purian23 06fa21118e feat(popouts): implement hover popout functionality 2026-06-26 23:34:46 -04:00
293 changed files with 3369 additions and 6789 deletions
-1
View File
@@ -24,7 +24,6 @@ jobs:
check-updates:
name: Check for updates
runs-on: ubuntu-latest
if: github.repository == 'AvengeMedia/DankMaterialShell'
outputs:
has_updates: ${{ steps.check.outputs.has_updates }}
-1
View File
@@ -24,7 +24,6 @@ jobs:
check-updates:
name: Check package/series updates
runs-on: ubuntu-latest
if: github.repository == 'AvengeMedia/DankMaterialShell'
outputs:
has_updates: ${{ steps.check.outputs.has_updates }}
-268
View File
@@ -1,268 +0,0 @@
name: Void Linux XBPS Repository
on:
schedule:
- cron: "0 2,5,14,17,20,23 * * *" # 9am, 12pm, 3pm, 6pm, 9pm, 12am EST (UTC times shown)
release:
types: [published]
workflow_dispatch:
inputs:
force_rebuild:
description: 'Force rebuilding packages even if they already exist in the repository'
type: boolean
required: false
default: false
build_git:
description: 'Build dms-git package'
type: boolean
required: false
default: true
build_dms:
description: 'Build stable dms package'
type: boolean
required: false
default: true
build_greeter:
description: 'Build stable dms-greeter package'
type: boolean
required: false
default: true
permissions:
contents: write
jobs:
build-and-deploy:
name: Build & Deploy XBPS packages
runs-on: ubuntu-latest
if: github.repository == 'AvengeMedia/DankMaterialShell'
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Checkout gh-pages branch
run: |
git clone --branch gh-pages https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git gh-pages-repo || {
echo "⚠️ gh-pages branch not found or empty, initializing a new one..."
mkdir gh-pages-repo
cd gh-pages-repo
git init
git checkout -b gh-pages
git remote add origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
cd ..
}
- name: Install XBPS Static Utilities
run: |
mkdir -p ${{ github.workspace }}/xbps
curl -s -L https://repo-default.voidlinux.org/static/xbps-static-latest.x86_64-musl.tar.xz | tar -xJ -C ${{ github.workspace }}/xbps
echo "${{ github.workspace }}/xbps/usr/bin" >> $GITHUB_PATH
- name: Clone void-packages
run: |
git clone --depth=1 https://github.com/void-linux/void-packages.git
- name: Inject templates
run: |
cp -R distro/void/srcpkgs/dms void-packages/srcpkgs/
cp -R distro/void/srcpkgs/dms-greeter void-packages/srcpkgs/
cp -R distro/void/srcpkgs/dms-git void-packages/srcpkgs/
- name: Enable unprivileged user namespaces (Ubuntu 24.04)
run: |
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
- name: Bootstrap xbps-src
run: |
cd void-packages
./xbps-src binary-bootstrap
- name: Configure signing keys and trust
run: |
# Write private key; extract and register the public key
echo "${{ secrets.XBPS_PRIVATE_KEY }}" > /tmp/xbps_privkey.pem
chmod 600 /tmp/xbps_privkey.pem
# Extract public key in PEM format
openssl rsa -in /tmp/xbps_privkey.pem -pubout -out /tmp/dms-key.pub
rm -f /tmp/xbps_privkey.pem
# Compute MD5 fingerprint in colon-separated hex format
FINGERPRINT=$(openssl rsa -pubin -in /tmp/dms-key.pub -outform DER 2>/dev/null | openssl dgst -md5 -c | tr '[:upper:]' '[:lower:]' | awk '{print $NF}')
# Format key in XML property list (plist) format as expected by xbps
mkdir -p /tmp/keys
cat <<EOF > "/tmp/keys/${FINGERPRINT}.plist"
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>public-key</key>
<data>$(base64 -w0 /tmp/dms-key.pub)</data>
<key>public-key-size</key>
<integer>4096</integer>
<key>signature-by</key>
<string>AvengeMedia</string>
</dict>
</plist>
EOF
# Copy keys to all host and chroot trust stores
sudo mkdir -p /var/db/xbps/keys
sudo cp "/tmp/keys/${FINGERPRINT}.plist" "/var/db/xbps/keys/${FINGERPRINT}.plist"
mkdir -p void-packages/masterdir/var/db/xbps/keys
cp "/tmp/keys/${FINGERPRINT}.plist" "void-packages/masterdir/var/db/xbps/keys/${FINGERPRINT}.plist"
mkdir -p void-packages/etc/xbps.d/keys
cp "/tmp/keys/${FINGERPRINT}.plist" "void-packages/etc/xbps.d/keys/${FINGERPRINT}.plist"
mkdir -p void-packages/common/repo-keys
cp "/tmp/keys/${FINGERPRINT}.plist" "void-packages/common/repo-keys/${FINGERPRINT}.plist"
rm -rf /tmp/keys /tmp/dms-key.pub
- name: Configure repositories
run: |
# Append the repository to repos-remote templates so xbps-src translates it automatically
echo "repository=https://avengemedia.github.io/DankLinux/current" >> void-packages/etc/xbps.d/repos-remote.conf
echo "repository=https://avengemedia.github.io/DankLinux/current" >> void-packages/etc/xbps.d/repos-remote-x86_64-multilib.conf
# Add any existing compiled packages to the build cache directory to avoid rebuilds
if [ -d "gh-pages-repo/current" ]; then
mkdir -p void-packages/hostdir/binpkgs
cp -L gh-pages-repo/current/*.xbps void-packages/hostdir/binpkgs/ 2>/dev/null || true
xbps-rindex -a void-packages/hostdir/binpkgs/*.xbps 2>/dev/null || true
fi
- name: Build packages
run: |
mkdir -p gh-pages-repo/current
BUILD_DMS="${{ github.event.inputs.build_dms || 'true' }}"
BUILD_GREETER="${{ github.event.inputs.build_greeter || 'true' }}"
BUILD_GIT="${{ github.event.inputs.build_git || 'true' }}"
FORCE_REBUILD="${{ github.event.inputs.force_rebuild || 'false' }}"
if [ "${{ github.event_name }}" = "schedule" ]; then
BUILD_DMS="false"
BUILD_GREETER="false"
BUILD_GIT="true"
fi
if [ "${{ github.event_name }}" = "release" ]; then
BUILD_DMS="true"
BUILD_GREETER="true"
BUILD_GIT="false"
fi
echo "=== Starting Builds ==="
echo "DMS stable build enabled: $BUILD_DMS"
echo "Greeter stable build enabled: $BUILD_GREETER"
echo "Git build enabled: $BUILD_GIT"
echo "Force rebuild: $FORCE_REBUILD"
cd void-packages
# 1. Build dms-git (development package)
if [ "$BUILD_GIT" = "true" ]; then
# Calculate dynamic git version (tag.commits.hash)
GIT_VER=$(git -C .. describe --tags --always | sed 's/^v//; s/-/./g')
echo "🔨 Preparing dms-git version $GIT_VER"
# Stage source tarball in the xbps-src source cache.
# --transform adds a top-level directory so xbps-src can extract
# into $wrksrc (create_wrksrc=yes handles the rest).
SRC_CACHE="hostdir/sources/dms-git-${GIT_VER}"
mkdir -p "$SRC_CACHE"
tar -czf "${SRC_CACHE}/dms-git-${GIT_VER}.tar.gz" \
--exclude=void-packages \
--exclude=gh-pages-repo \
--exclude=.git \
--exclude=danklinux \
-C .. .
CHECKSUM=$(sha256sum "${SRC_CACHE}/dms-git-${GIT_VER}.tar.gz" | cut -d' ' -f1)
# Dynamically patch template version, checksum, and distfiles
sed -i "s/^version=.*/version=${GIT_VER}/" srcpkgs/dms-git/template
sed -i "s/^checksum=.*/checksum=${CHECKSUM}/" srcpkgs/dms-git/template
sed -i "s|^distfiles=.*|distfiles=\"dms-git-${GIT_VER}.tar.gz\"|" srcpkgs/dms-git/template
EXPECTED_GIT_FILE="dms-git-${GIT_VER}_1.x86_64.xbps"
if [ -f "../gh-pages-repo/current/$EXPECTED_GIT_FILE" ] && [ "$FORCE_REBUILD" != "true" ]; then
echo "✅ $EXPECTED_GIT_FILE already exists, skipping build."
else
echo "🔨 Compiling dms-git..."
./xbps-src pkg dms-git
rm -f "../gh-pages-repo/current/${EXPECTED_GIT_FILE}"
cp -L hostdir/binpkgs/dms-git-*.xbps ../gh-pages-repo/current/
fi
fi
# 2. Build stable dms package
if [ "$BUILD_DMS" = "true" ]; then
STABLE_VER=$(grep -E '^version=' srcpkgs/dms/template | cut -d= -f2 | tr -d '"')
STABLE_REV=$(grep -E '^revision=' srcpkgs/dms/template | cut -d= -f2 | tr -d '"')
EXPECTED_DMS_FILE="dms-${STABLE_VER}_${STABLE_REV}.x86_64.xbps"
if [ -f "../gh-pages-repo/current/$EXPECTED_DMS_FILE" ] && [ "$FORCE_REBUILD" != "true" ]; then
echo "✅ $EXPECTED_DMS_FILE already exists, skipping build."
else
echo "🔨 Compiling dms ($STABLE_VER)..."
./xbps-src pkg dms
rm -f "../gh-pages-repo/current/${EXPECTED_DMS_FILE}"
cp -L hostdir/binpkgs/dms-${STABLE_VER}_${STABLE_REV}.x86_64.xbps ../gh-pages-repo/current/
fi
fi
# 3. Build stable dms-greeter package
if [ "$BUILD_GREETER" = "true" ]; then
GREETER_VER=$(grep -E '^version=' srcpkgs/dms-greeter/template | cut -d= -f2 | tr -d '"')
GREETER_REV=$(grep -E '^revision=' srcpkgs/dms-greeter/template | cut -d= -f2 | tr -d '"')
EXPECTED_GREETER_FILE="dms-greeter-${GREETER_VER}_${GREETER_REV}.x86_64.xbps"
if [ -f "../gh-pages-repo/current/$EXPECTED_GREETER_FILE" ] && [ "$FORCE_REBUILD" != "true" ]; then
echo "✅ $EXPECTED_GREETER_FILE already exists, skipping build."
else
echo "🔨 Compiling dms-greeter ($GREETER_VER)..."
./xbps-src pkg dms-greeter
rm -f "../gh-pages-repo/current/${EXPECTED_GREETER_FILE}"
cp -L hostdir/binpkgs/dms-greeter-${GREETER_VER}_${GREETER_REV}.x86_64.xbps ../gh-pages-repo/current/
fi
fi
- name: Index and sign repository
run: |
cd gh-pages-repo/current
# Clean up any stale or dangling signature files to prevent O_CREAT ENOENT errors
rm -f *.sig2 *.sig
# Guard: nothing to index if no .xbps files exist
if ! ls *.xbps 1>/dev/null 2>&1; then
echo "⚠️ No .xbps files found to index, skipping."
exit 0
fi
# Regenerate repo index
xbps-rindex -a $(pwd)/*.xbps
# Sign repository
echo "${{ secrets.XBPS_PRIVATE_KEY }}" > /tmp/xbps_privkey.pem
chmod 600 /tmp/xbps_privkey.pem
xbps-rindex --sign --signedby "AvengeMedia <AvengeMedia.US@gmail.com>" --privkey /tmp/xbps_privkey.pem $(pwd)
xbps-rindex --sign-pkg --privkey /tmp/xbps_privkey.pem $(pwd)/*.xbps
rm -f /tmp/xbps_privkey.pem
- name: Deploy to gh-pages branch
run: |
cd gh-pages-repo
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add current/
git diff --quiet && git diff --staged --quiet || (git commit -m "Update XBPS packages [skip ci]" && git push origin gh-pages)
-3
View File
@@ -110,9 +110,6 @@ bin/
# Core dumps
core.*
# prek-installed local git hooks (generated from .pre-commit-config.yaml)
.githooks/
# direnv
.envrc
.direnv/
-2
View File
@@ -37,11 +37,9 @@ var resolveIncludeCmd = &cobra.Command{
"cursor.lua",
"windowrules.lua",
"cursor.kdl",
"layout.kdl",
"outputs.kdl",
"binds.kdl",
"cursor.conf",
"layout.conf",
"outputs.conf",
"binds.conf",
}, cobra.ShellCompDirectiveNoFileComp
+10 -124
View File
@@ -32,14 +32,13 @@ var greeterCmd = &cobra.Command{
var (
greeterConfigSyncFn = greeter.SyncDMSConfigs
sharedAuthSyncFn = sharedpam.SyncAuthConfig
greeterIsNixOSFn = greeter.IsNixOS
)
var greeterInstallCmd = &cobra.Command{
Use: "install",
Short: "Install and configure DMS greeter",
Long: "Install greetd and configure it to use DMS as the greeter interface",
PreRunE: preRunGreeterMutation,
PreRunE: preRunPrivileged,
Run: func(cmd *cobra.Command, args []string) {
yes, _ := cmd.Flags().GetBool("yes")
term, _ := cmd.Flags().GetBool("terminal")
@@ -65,9 +64,6 @@ var greeterSyncCmd = &cobra.Command{
Short: "Sync DMS theme and settings with greeter",
Long: "Synchronize your current user's DMS theme, settings, and wallpaper configuration with the login greeter screen. Also updates a per-user cache slot at users/<username>/ for multi-account greeter theme preview.\n\nUse --profile on secondary accounts to sync only your own users/<username>/ slot without sudo or greetd changes.",
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := rejectNixOSGreeterMutation(cmd); err != nil {
return err
}
profile, _ := cmd.Flags().GetBool("profile")
if profile {
return nil
@@ -144,7 +140,7 @@ var greeterEnableCmd = &cobra.Command{
Use: "enable",
Short: "Enable DMS greeter in greetd config",
Long: "Configure greetd to use DMS as the greeter",
PreRunE: preRunGreeterMutation,
PreRunE: preRunPrivileged,
Run: func(cmd *cobra.Command, args []string) {
yes, _ := cmd.Flags().GetBool("yes")
term, _ := cmd.Flags().GetBool("terminal")
@@ -180,7 +176,7 @@ var greeterUninstallCmd = &cobra.Command{
Use: "uninstall",
Short: "Remove DMS greeter configuration and restore previous display manager",
Long: "Disable greetd, remove DMS managed configs, and restore the system to its pre-DMS-greeter state",
PreRunE: preRunGreeterMutation,
PreRunE: preRunPrivileged,
Run: func(cmd *cobra.Command, args []string) {
yes, _ := cmd.Flags().GetBool("yes")
term, _ := cmd.Flags().GetBool("terminal")
@@ -210,21 +206,6 @@ func init() {
greeterUninstallCmd.Flags().BoolP("terminal", "t", false, "Run in a new terminal (for entering sudo password)")
}
func rejectNixOSGreeterMutation(cmd *cobra.Command) error {
if !greeterIsNixOSFn() {
return nil
}
return fmt.Errorf("dms %s is disabled on NixOS because the greeter is managed declaratively\nConfigure the DMS greeter in your NixOS module, then apply the change with your normal nixos-rebuild workflow", normalizeCommandSpec(cmd.CommandPath()))
}
func preRunGreeterMutation(cmd *cobra.Command, args []string) error {
if err := rejectNixOSGreeterMutation(cmd); err != nil {
return err
}
return preRunPrivileged(cmd, args)
}
func syncGreeterConfigsAndAuth(dmsPath, compositor string, logFunc func(string), options sharedpam.SyncAuthOptions, beforeAuth func()) error {
if err := greeterConfigSyncFn(dmsPath, compositor, logFunc, ""); err != nil {
return err
@@ -1434,36 +1415,24 @@ func readDefaultSessionCommand(configPath string) string {
return ""
}
func explicitGreeterCacheDirFromCommand(command string) (string, bool) {
func extractGreeterCacheDirFromCommand(command string) string {
if command == "" {
return greeter.GreeterCacheDir
}
tokens := strings.Fields(command)
for i := 0; i < len(tokens); i++ {
token := strings.Trim(tokens[i], "\"")
if token == "--cache-dir" && i+1 < len(tokens) {
value := strings.Trim(tokens[i+1], "\"")
if value != "" {
return value, true
}
return strings.Trim(tokens[i+1], "\"")
}
if strings.HasPrefix(token, "--cache-dir=") {
value := strings.TrimPrefix(token, "--cache-dir=")
value = strings.Trim(value, "\"")
if value != "" {
return value, true
return value
}
}
}
return "", false
}
const nixOSGreeterStateDir = "/var/lib/dms-greeter"
func greeterStatusStateDir(command string, isNixOS bool) string {
if cacheDir, ok := explicitGreeterCacheDirFromCommand(command); ok {
return cacheDir
}
if isNixOS {
return nixOSGreeterStateDir
}
return greeter.GreeterCacheDir
}
@@ -1600,10 +1569,6 @@ func checkGreeterStatus() error {
fmt.Println("=== DMS Greeter Status ===")
fmt.Println()
if greeterIsNixOSFn() {
return checkNixOSGreeterStatus()
}
homeDir, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("failed to get user home directory: %w", err)
@@ -1667,7 +1632,7 @@ func checkGreeterStatus() error {
fmt.Println(" Run 'dms greeter sync' to set up group membership and permissions")
}
cacheDir := greeterStatusStateDir(configuredCommand, false)
cacheDir := extractGreeterCacheDirFromCommand(configuredCommand)
fmt.Println("\nGreeter Cache Directory:")
fmt.Printf(" Effective cache dir: %s\n", cacheDir)
if cacheDir != greeter.GreeterCacheDir {
@@ -1930,85 +1895,6 @@ func checkGreeterStatus() error {
return nil
}
func checkNixOSGreeterStatus() error {
const configPath = "/etc/greetd/config.toml"
configuredCommand := readDefaultSessionCommand(configPath)
allGood := true
fmt.Println("Greeter Configuration:")
switch {
case strings.Contains(configuredCommand, "dms-greeter"):
fmt.Println(" ✓ DMS greeter command found")
if wrapper := extractGreeterWrapperFromCommand(configuredCommand); wrapper != "" {
fmt.Printf(" Wrapper: %s\n", wrapper)
}
case configuredCommand != "":
fmt.Println(" ⚠ greetd default session does not reference dms-greeter")
allGood = false
default:
fmt.Printf(" No readable DMS command found in %s\n", configPath)
}
fmt.Println(" NixOS manages greeter configuration declaratively; apply changes through your NixOS module.")
stateDir := greeterStatusStateDir(configuredCommand, true)
fmt.Println("\nGreeter State Directory:")
fmt.Printf(" Effective state dir: %s\n", stateDir)
if stateDir == nixOSGreeterStateDir {
fmt.Println(" ✓ Using the NixOS module state path")
}
if stat, err := os.Stat(stateDir); err == nil && stat.IsDir() {
fmt.Printf(" ✓ %s exists\n", stateDir)
} else if os.IsNotExist(err) {
fmt.Printf(" ✗ %s not found\n", stateDir)
fmt.Println(" Rebuild your NixOS configuration after enabling the DMS greeter module.")
allGood = false
} else if err != nil {
fmt.Printf(" ✗ Could not inspect %s: %v\n", stateDir, err)
allGood = false
} else {
fmt.Printf(" ✗ %s is not a directory\n", stateDir)
allGood = false
}
fmt.Println("\nDeclarative Configuration Files:")
configFiles := []struct {
name string
path string
}{
{name: "Settings", path: filepath.Join(stateDir, "settings.json")},
{name: "Session state", path: filepath.Join(stateDir, "session.json")},
{name: "Color theme", path: filepath.Join(stateDir, "colors.json")},
}
for _, configFile := range configFiles {
if stat, err := os.Stat(configFile.path); err == nil && !stat.IsDir() {
fmt.Printf(" ✓ %s: %s\n", configFile.name, configFile.path)
} else if os.IsNotExist(err) {
fmt.Printf(" %s not present (optional; configure configHome/configFiles in the NixOS module)\n", configFile.name)
} else if err != nil {
fmt.Printf(" ⚠ %s could not be inspected: %v\n", configFile.name, err)
} else {
fmt.Printf(" ⚠ %s path is not a regular file: %s\n", configFile.name, configFile.path)
}
}
fmt.Println("\nGroup Membership:")
fmt.Println(" User group membership is managed by NixOS and is not required for declarative theme copies.")
fmt.Println("\nGreeter PAM Authentication:")
fmt.Println(" PAM is managed by NixOS modules.")
fmt.Println(" Configure fingerprint/U2F through security.pam.services.greetd.")
fmt.Println()
if allGood {
fmt.Println("✓ NixOS greeter state looks healthy and is managed declaratively.")
} else {
fmt.Println("⚠ Some issues detected. Update the DMS greeter module and rebuild NixOS; do not run 'dms greeter sync'.")
}
return nil
}
func recentAppArmorGreeterDenials(sampleLimit int) (int, []string, error) {
if sampleLimit <= 0 {
sampleLimit = 3
-56
View File
@@ -3,11 +3,9 @@ package main
import (
"errors"
"reflect"
"strings"
"testing"
sharedpam "github.com/AvengeMedia/DankMaterialShell/core/internal/pam"
"github.com/spf13/cobra"
)
func TestSyncGreeterConfigsAndAuthDelegatesSharedAuth(t *testing.T) {
@@ -87,57 +85,3 @@ func TestSyncGreeterConfigsAndAuthStopsOnConfigError(t *testing.T) {
t.Fatal("expected auth sync not to run after config sync failure")
}
}
func TestGreeterStatusStateDirUsesNixOSDefault(t *testing.T) {
if got := greeterStatusStateDir("", true); got != nixOSGreeterStateDir {
t.Fatalf("greeterStatusStateDir() = %q, want %q", got, nixOSGreeterStateDir)
}
}
func TestGreeterStatusStateDirHonorsExplicitOverrideOnNixOS(t *testing.T) {
command := "dms-greeter --cache-dir /srv/dms-greeter --command niri"
if got := greeterStatusStateDir(command, true); got != "/srv/dms-greeter" {
t.Fatalf("greeterStatusStateDir() = %q, want %q", got, "/srv/dms-greeter")
}
}
func TestRejectNixOSGreeterMutationBlocksImperativeCommands(t *testing.T) {
origGreeterIsNixOSFn := greeterIsNixOSFn
greeterIsNixOSFn = func() bool { return true }
t.Cleanup(func() {
greeterIsNixOSFn = origGreeterIsNixOSFn
})
for _, commandName := range []string{"install", "enable", "sync", "uninstall"} {
t.Run(commandName, func(t *testing.T) {
root := &cobra.Command{Use: "dms"}
greeterCommand := &cobra.Command{Use: "greeter"}
mutationCommand := &cobra.Command{Use: commandName}
root.AddCommand(greeterCommand)
greeterCommand.AddCommand(mutationCommand)
err := rejectNixOSGreeterMutation(mutationCommand)
if err == nil {
t.Fatalf("expected NixOS greeter %s to be rejected", commandName)
}
if !strings.Contains(err.Error(), "dms greeter "+commandName+" is disabled on NixOS") {
t.Fatalf("unexpected error: %v", err)
}
if strings.Contains(err.Error(), "/var/cache/dms-greeter") {
t.Fatalf("NixOS remediation should not recommend the non-NixOS cache path: %v", err)
}
})
}
}
func TestRejectNixOSGreeterMutationAllowsOtherDistros(t *testing.T) {
origGreeterIsNixOSFn := greeterIsNixOSFn
greeterIsNixOSFn = func() bool { return false }
t.Cleanup(func() {
greeterIsNixOSFn = origGreeterIsNixOSFn
})
if err := rejectNixOSGreeterMutation(&cobra.Command{Use: "sync"}); err != nil {
t.Fatalf("expected non-NixOS greeter command to be allowed, got %v", err)
}
}
-24
View File
@@ -1,24 +0,0 @@
package main
import (
"encoding/json"
"testing"
)
func TestDefaultImmutablePolicyAllowsSyncButBlocksEnable(t *testing.T) {
var policyFile cliPolicyFile
if err := json.Unmarshal(defaultCLIPolicyJSON, &policyFile); err != nil {
t.Fatalf("failed to parse embedded CLI policy: %v", err)
}
if policyFile.BlockedCommands == nil {
t.Fatal("embedded CLI policy has no blocked_commands")
}
blocked := normalizeBlockedCommands(*policyFile.BlockedCommands)
if !commandBlockedByPolicy("greeter enable", blocked) {
t.Fatal("expected greeter enable to remain blocked on immutable/image-based systems")
}
if commandBlockedByPolicy("greeter sync", blocked) {
t.Fatal("expected greeter sync to remain available on immutable/image-based systems")
}
}
@@ -13,7 +13,6 @@ hl.config({
input = {
kb_layout = "us",
numlock_by_default = true,
follow_mouse = 0,
touchpad = {
tap_to_click = true,
natural_scroll = true,
-10
View File
@@ -23,7 +23,6 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/tailscale"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/thememode"
serverThemes "github.com/AvengeMedia/DankMaterialShell/core/internal/server/themes"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wallpaper"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
)
@@ -57,15 +56,6 @@ func RouteRequest(conn net.Conn, req models.Request) {
return
}
if strings.HasPrefix(req.Method, "wallpaper.") {
if wallpaperManager == nil {
models.RespondError(conn, req.ID, "wallpaper manager not initialized")
return
}
wallpaper.HandleRequest(conn, req, wallpaperManager)
return
}
if strings.HasPrefix(req.Method, "loginctl.") {
if loginctlManager == nil {
models.RespondError(conn, req.ID, "loginctl manager not initialized")
+1 -66
View File
@@ -33,14 +33,13 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/tailscale"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/thememode"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/trayrecovery"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wallpaper"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlcontext"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
)
const APIVersion = 27
const APIVersion = 26
var CLIVersion = "dev"
@@ -74,7 +73,6 @@ var clipboardManager *clipboard.Manager
var dbusManager *serverDbus.Manager
var wlContext *wlcontext.SharedContext
var themeModeManager *thememode.Manager
var wallpaperManager *wallpaper.Manager
var trayRecoveryManager *trayrecovery.Manager
var locationManager *location.Manager
var sysUpdateManager *sysupdate.Manager
@@ -349,13 +347,6 @@ func InitializeThemeModeManager() error {
return nil
}
func InitializeWallpaperManager() error {
wallpaperManager = wallpaper.NewManager()
log.Info("Wallpaper rotation scheduler initialized")
return nil
}
func InitializeTrayRecoveryManager() error {
manager, err := trayrecovery.NewManager()
if err != nil {
@@ -472,10 +463,6 @@ func getCapabilities() Capabilities {
caps = append(caps, "theme.auto")
}
if wallpaperManager != nil {
caps = append(caps, "wallpaper")
}
if dbusManager != nil {
caps = append(caps, "dbus")
}
@@ -542,10 +529,6 @@ func getServerInfo() ServerInfo {
caps = append(caps, "theme.auto")
}
if wallpaperManager != nil {
caps = append(caps, "wallpaper")
}
if locationManager != nil {
caps = append(caps, "location")
}
@@ -858,38 +841,6 @@ func handleSubscribe(conn net.Conn, req models.Request) {
}()
}
if shouldSubscribe("wallpaper") && wallpaperManager != nil {
wg.Add(1)
wallpaperChan := wallpaperManager.Subscribe(clientID + "-wallpaper")
go func() {
defer wg.Done()
defer wallpaperManager.Unsubscribe(clientID + "-wallpaper")
initialState := wallpaperManager.GetState()
select {
case eventChan <- ServiceEvent{Service: "wallpaper", Data: initialState}:
case <-stopChan:
return
}
for {
select {
case state, ok := <-wallpaperChan:
if !ok {
return
}
select {
case eventChan <- ServiceEvent{Service: "wallpaper", Data: state}:
case <-stopChan:
return
}
case <-stopChan:
return
}
}
}()
}
if shouldSubscribe("bluetooth") && bluezManager != nil {
wg.Add(1)
bluezChan := bluezManager.Subscribe(clientID + "-bluetooth")
@@ -1335,9 +1286,6 @@ func cleanupManagers() {
if themeModeManager != nil {
themeModeManager.Close()
}
if wallpaperManager != nil {
wallpaperManager.Close()
}
if trayRecoveryManager != nil {
trayRecoveryManager.Close()
}
@@ -1630,19 +1578,6 @@ func Start(printDocs bool) error {
}()
}
if err := InitializeWallpaperManager(); err != nil {
log.Warnf("Wallpaper scheduler unavailable: %v", err)
} else {
notifyCapabilityChange()
go func() {
<-loginctlReady
if loginctlManager == nil {
return
}
wallpaperManager.WatchLoginctl(loginctlManager)
}()
}
go func() {
<-loginctlReady
if loginctlManager == nil {
@@ -1,84 +0,0 @@
package wallpaper
import (
"encoding/json"
"fmt"
"net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
)
func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
if manager == nil {
models.RespondError(conn, req.ID, "wallpaper manager not initialized")
return
}
switch req.Method {
case "wallpaper.getState":
handleGetState(conn, req, manager)
case "wallpaper.setConfig":
handleSetConfig(conn, req, manager)
case "wallpaper.trigger":
handleTrigger(conn, req, manager)
case "wallpaper.subscribe":
handleSubscribe(conn, req, manager)
default:
models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
}
}
func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
func handleSetConfig(conn net.Conn, req models.Request, manager *Manager) {
raw, ok := params.Any(req.Params, "config")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'config' parameter")
return
}
data, err := json.Marshal(raw)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
var config Config
if err := json.Unmarshal(data, &config); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
manager.SetConfig(config)
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "wallpaper schedule set"})
}
func handleTrigger(conn net.Conn, req models.Request, manager *Manager) {
manager.ResetSchedule(params.StringOpt(req.Params, "target", ""))
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "wallpaper schedule reset"})
}
func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
initialState := manager.GetState()
if err := json.NewEncoder(conn).Encode(models.Response[State]{
ID: req.ID,
Result: &initialState,
}); err != nil {
return
}
for state := range stateChan {
if err := json.NewEncoder(conn).Encode(models.Response[State]{
Result: &state,
}); err != nil {
return
}
}
}
-325
View File
@@ -1,325 +0,0 @@
package wallpaper
import (
"reflect"
"strconv"
"strings"
"sync"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
)
type activeSchedule struct {
cfg ScheduleConfig
nextFire time.Time
}
type Manager struct {
config Config
configMutex sync.RWMutex
state *State
stateMutex sync.RWMutex
subscribers syncmap.Map[string, chan State]
stopChan chan struct{}
updateTrigger chan struct{}
resetReq chan string
wg sync.WaitGroup
}
func NewManager() *Manager {
m := &Manager{
config: Config{
Global: ScheduleConfig{Mode: "interval", IntervalSec: 300, Time: "06:00"},
Monitors: map[string]ScheduleConfig{},
},
stopChan: make(chan struct{}),
updateTrigger: make(chan struct{}, 1),
resetReq: make(chan string, 8),
}
m.state = &State{Config: m.getConfig()}
m.wg.Add(1)
go m.schedulerLoop()
return m
}
func (m *Manager) GetState() State {
m.stateMutex.RLock()
defer m.stateMutex.RUnlock()
if m.state == nil {
return State{Config: m.getConfig()}
}
return *m.state
}
func (m *Manager) Subscribe(id string) chan State {
ch := make(chan State, 64)
m.subscribers.Store(id, ch)
return ch
}
func (m *Manager) Unsubscribe(id string) {
if val, ok := m.subscribers.LoadAndDelete(id); ok {
close(val)
}
}
func (m *Manager) SetConfig(config Config) {
if config.Monitors == nil {
config.Monitors = map[string]ScheduleConfig{}
}
m.configMutex.Lock()
if reflect.DeepEqual(m.config, config) {
m.configMutex.Unlock()
return
}
m.config = config
m.configMutex.Unlock()
m.TriggerUpdate()
}
func (m *Manager) ResetSchedule(target string) {
select {
case m.resetReq <- target:
default:
}
}
func (m *Manager) TriggerUpdate() {
select {
case m.updateTrigger <- struct{}{}:
default:
}
}
func (m *Manager) Close() {
select {
case <-m.stopChan:
return
default:
close(m.stopChan)
}
m.wg.Wait()
m.subscribers.Range(func(key string, ch chan State) bool {
close(ch)
m.subscribers.Delete(key)
return true
})
}
func (m *Manager) WatchLoginctl(lm *loginctl.Manager) {
ch := lm.Subscribe("wallpaper")
m.wg.Add(1)
go func() {
defer m.wg.Done()
defer lm.Unsubscribe("wallpaper")
for {
select {
case <-m.stopChan:
return
case state, ok := <-ch:
if !ok {
return
}
if state.PreparingForSleep {
continue
}
m.TriggerUpdate()
}
}
}()
}
func (m *Manager) schedulerLoop() {
defer m.wg.Done()
schedules := map[string]*activeSchedule{}
resets := map[string]bool{}
var seq uint64
var timer *time.Timer
for {
now := time.Now()
config := m.getConfig()
active := activeSchedules(config)
for key := range schedules {
if _, ok := active[key]; !ok {
delete(schedules, key)
}
}
for key, cfg := range active {
s, ok := schedules[key]
switch {
case !ok:
schedules[key] = &activeSchedule{cfg: cfg, nextFire: computeNext(now, cfg)}
case s.cfg != cfg || resets[key]:
s.cfg = cfg
s.nextFire = computeNext(now, cfg)
}
delete(resets, key)
}
var dueKeys []string
for key, s := range schedules {
if !s.nextFire.After(now) {
dueKeys = append(dueKeys, key)
s.nextFire = computeNext(now, s.cfg)
}
}
next, hasNext := soonest(schedules)
if len(dueKeys) == 0 {
m.setState(config, next, seq, "")
}
for _, key := range dueKeys {
seq++
m.setState(config, next, seq, key)
}
waitDur := 24 * time.Hour
if hasNext {
waitDur = time.Until(next)
if waitDur < time.Second {
waitDur = time.Second
}
}
if timer != nil {
timer.Stop()
}
timer = time.NewTimer(waitDur)
select {
case <-m.stopChan:
timer.Stop()
return
case <-m.updateTrigger:
timer.Stop()
case key := <-m.resetReq:
timer.Stop()
resets[key] = true
case <-timer.C:
}
}
}
func (m *Manager) setState(config Config, next time.Time, seq uint64, target string) {
newState := State{Config: config, NextRotation: next, CycleSeq: seq, Target: target}
m.stateMutex.Lock()
if m.state != nil && statesEqual(m.state, &newState) {
m.stateMutex.Unlock()
return
}
m.state = &newState
m.stateMutex.Unlock()
m.notifySubscribers()
}
func (m *Manager) notifySubscribers() {
state := m.GetState()
m.subscribers.Range(func(key string, ch chan State) bool {
select {
case ch <- state:
default:
}
return true
})
}
func (m *Manager) getConfig() Config {
m.configMutex.RLock()
defer m.configMutex.RUnlock()
return m.config
}
func activeSchedules(config Config) map[string]ScheduleConfig {
out := map[string]ScheduleConfig{}
if config.PerMonitor {
for name, cfg := range config.Monitors {
if cfg.Enabled {
out[name] = cfg
}
}
return out
}
if config.Global.Enabled {
out[""] = config.Global
}
return out
}
func computeNext(now time.Time, cfg ScheduleConfig) time.Time {
switch cfg.Mode {
case "time":
return nextDailyTime(now, cfg.Time)
default:
sec := cfg.IntervalSec
if sec < 1 {
sec = 1
}
return now.Add(time.Duration(sec) * time.Second)
}
}
func nextDailyTime(now time.Time, hhmm string) time.Time {
hour, minute, ok := parseHHMM(hhmm)
if !ok {
return now.Add(24 * time.Hour)
}
next := time.Date(now.Year(), now.Month(), now.Day(), hour, minute, 0, 0, now.Location())
if !next.After(now) {
next = next.Add(24 * time.Hour)
}
return next
}
func parseHHMM(hhmm string) (int, int, bool) {
parts := strings.Split(hhmm, ":")
if len(parts) != 2 {
return 0, 0, false
}
hour, err := strconv.Atoi(parts[0])
if err != nil || hour < 0 || hour > 23 {
return 0, 0, false
}
minute, err := strconv.Atoi(parts[1])
if err != nil || minute < 0 || minute > 59 {
return 0, 0, false
}
return hour, minute, true
}
func soonest(schedules map[string]*activeSchedule) (time.Time, bool) {
var best time.Time
found := false
for _, s := range schedules {
if !found || s.nextFire.Before(best) {
best = s.nextFire
found = true
}
}
return best, found
}
func statesEqual(a, b *State) bool {
switch {
case a == nil || b == nil:
return a == b
case a.CycleSeq != b.CycleSeq:
return false
case a.Target != b.Target:
return false
case !a.NextRotation.Equal(b.NextRotation):
return false
}
return reflect.DeepEqual(a.Config, b.Config)
}
@@ -1,106 +0,0 @@
package wallpaper
import (
"testing"
"time"
)
func TestParseHHMM(t *testing.T) {
cases := []struct {
in string
hour int
minute int
ok bool
}{
{"06:00", 6, 0, true},
{"23:59", 23, 59, true},
{"00:00", 0, 0, true},
{"24:00", 0, 0, false},
{"6:5", 6, 5, true},
{"bad", 0, 0, false},
{"12", 0, 0, false},
{"12:60", 0, 0, false},
}
for _, c := range cases {
hour, minute, ok := parseHHMM(c.in)
if ok != c.ok || (ok && (hour != c.hour || minute != c.minute)) {
t.Errorf("parseHHMM(%q) = (%d, %d, %v), want (%d, %d, %v)", c.in, hour, minute, ok, c.hour, c.minute, c.ok)
}
}
}
func TestComputeNextInterval(t *testing.T) {
now := time.Date(2026, 6, 30, 12, 0, 0, 0, time.UTC)
got := computeNext(now, ScheduleConfig{Mode: "interval", IntervalSec: 300})
if want := now.Add(300 * time.Second); !got.Equal(want) {
t.Errorf("interval next = %v, want %v", got, want)
}
clamped := computeNext(now, ScheduleConfig{Mode: "interval", IntervalSec: 0})
if want := now.Add(time.Second); !clamped.Equal(want) {
t.Errorf("interval clamp = %v, want %v", clamped, want)
}
}
func TestComputeNextTime(t *testing.T) {
now := time.Date(2026, 6, 30, 12, 0, 0, 0, time.UTC)
later := computeNext(now, ScheduleConfig{Mode: "time", Time: "18:00"})
if want := time.Date(2026, 6, 30, 18, 0, 0, 0, time.UTC); !later.Equal(want) {
t.Errorf("time next (today) = %v, want %v", later, want)
}
tomorrow := computeNext(now, ScheduleConfig{Mode: "time", Time: "06:00"})
if want := time.Date(2026, 7, 1, 6, 0, 0, 0, time.UTC); !tomorrow.Equal(want) {
t.Errorf("time next (tomorrow) = %v, want %v", tomorrow, want)
}
}
func TestActiveSchedules(t *testing.T) {
global := activeSchedules(Config{
Global: ScheduleConfig{Enabled: true, Mode: "interval", IntervalSec: 60},
})
if len(global) != 1 {
t.Fatalf("global active = %d, want 1", len(global))
}
if _, ok := global[""]; !ok {
t.Errorf("global active missing global key")
}
perMonitor := activeSchedules(Config{
PerMonitor: true,
Global: ScheduleConfig{Enabled: true},
Monitors: map[string]ScheduleConfig{
"DP-1": {Enabled: true, Mode: "interval", IntervalSec: 60},
"DP-2": {Enabled: false},
},
})
if len(perMonitor) != 1 {
t.Fatalf("per-monitor active = %d, want 1", len(perMonitor))
}
if _, ok := perMonitor["DP-1"]; !ok {
t.Errorf("per-monitor active missing DP-1")
}
}
func TestSchedulerEmitsCycle(t *testing.T) {
m := NewManager()
defer m.Close()
sub := m.Subscribe("test")
defer m.Unsubscribe("test")
m.SetConfig(Config{Global: ScheduleConfig{Enabled: true, Mode: "interval", IntervalSec: 1}})
deadline := time.After(3 * time.Second)
for {
select {
case state := <-sub:
if state.CycleSeq > 0 && state.Target == "" {
return
}
case <-deadline:
t.Fatal("scheduler did not emit a cycle event within 3s")
}
}
}
-23
View File
@@ -1,23 +0,0 @@
package wallpaper
import "time"
type ScheduleConfig struct {
Enabled bool `json:"enabled"`
Mode string `json:"mode"`
IntervalSec int `json:"intervalSec"`
Time string `json:"time"`
}
type Config struct {
PerMonitor bool `json:"perMonitor"`
Global ScheduleConfig `json:"global"`
Monitors map[string]ScheduleConfig `json:"monitors"`
}
type State struct {
Config Config `json:"config"`
NextRotation time.Time `json:"nextRotation"`
CycleSeq uint64 `json:"cycleSeq"`
Target string `json:"target"`
}
+6 -22
View File
@@ -74,9 +74,6 @@ func NewManager(display wlclient.WaylandDisplay, config Config) (*Manager, error
if config.Enabled {
m.post(func() {
if m.controlsInitialized {
return
}
log.Info("Gamma control enabled at startup")
gammaMgr := m.gammaControl.(*wlr_gamma_control.ZwlrGammaControlManagerV1)
m.availOutputsMu.RLock()
@@ -187,26 +184,13 @@ func (m *Manager) setupRegistry() error {
enabled := m.config.Enabled
m.configMutex.RUnlock()
if !enabled {
return
if enabled && m.controlsInitialized {
m.post(func() {
if err := m.addOutputControl(output); err != nil {
log.Warnf("Failed to add output control: %v", err)
}
})
}
m.post(func() {
if err := m.addOutputControl(output); err != nil {
log.Warnf("gamma: failed to add output control: %v", err)
return
}
if m.controlsInitialized {
return
}
// All outputs had been torn down (monitor sleep/disconnect),
// clearing controlsInitialized. Mark it ready again so the
// gamma_size event that follows control creation drives the
// reapply, instead of waiting for a manual toggle. No explicit
// apply here: applyGamma's dedup would suppress a no-change
// write anyway, and the new control isn't ready until gamma_size.
log.Info("gamma: output returned, re-establishing controls")
m.controlsInitialized = true
})
}
})
+1 -48
View File
@@ -10,8 +10,6 @@ let
inherit (lib) types;
cfg = config.programs.dank-material-shell.greeter;
cfgDms = config.programs.dank-material-shell;
cfgAutoLogin = config.services.displayManager.autoLogin;
sessionData = config.services.displayManager.sessionData;
inherit (config.services.greetd.settings.default_session) user;
@@ -50,38 +48,6 @@ let
} ${lib.optionalString cfg.logs.save "> ${cfg.logs.path} 2>&1"}
'';
autoLoginCommand =
pkgs.runCommand "dms-greeter-autologin-command"
{
nativeBuildInputs = [
pkgs.gnugrep
pkgs.coreutils
];
}
''
set -euo pipefail
session="${sessionData.autologinSession}"
desktops="${sessionData.desktops}"
for sessionFile in \
"$desktops/share/wayland-sessions/$session.desktop" \
"$desktops/share/xsessions/$session.desktop"
do
if [ -f "$sessionFile" ]; then
command="$(grep -m1 '^Exec=' "$sessionFile" | cut -d= -f2- || true)"
if [ -n "$command" ]; then
printf '%s\n' "$command" > "$out"
exit 0
fi
fi
done
echo "dms-greeter autologin: could not resolve Exec for session '$session'" >&2
exit 1
'';
jq = lib.getExe pkgs.jq;
in
{
@@ -189,13 +155,6 @@ in
dmsgreeter: user set for greetd default_session ${user} does not exist. Please create it before referencing it.
'';
}
{
assertion = cfgAutoLogin.enable -> sessionData.autologinSession != null;
message = ''
dms-greeter auto-login requires services.displayManager.defaultSession to be set,
or at least one session in services.displayManager.sessionPackages.
'';
}
];
# DMS currently relies on /etc/pam.d/login for lock screen password auth on NixOS.
# Declare security.pam.services.dankshell only if you want to override that runtime fallback.
@@ -206,13 +165,7 @@ in
# };
services.greetd = {
enable = lib.mkDefault true;
settings = {
default_session.command = lib.mkDefault (lib.getExe greeterScript);
initial_session = lib.mkIf (cfgAutoLogin.enable && (cfgAutoLogin.user != null)) {
inherit (cfgAutoLogin) user;
command = ''${lib.getExe pkgs.bash} -lc "${pkgs.systemd}/bin/systemd-cat $(<${autoLoginCommand})"'';
};
};
settings.default_session.command = lib.mkDefault (lib.getExe greeterScript);
};
fonts.packages = with pkgs; [
fira-code
-11
View File
@@ -16,14 +16,8 @@ pkgs.testers.runNixOSTest {
isSystemUser = true;
group = "greeter";
};
users.users.alice.isNormalUser = true;
services.greetd.settings.default_session.user = "greeter";
services.displayManager.autoLogin = {
enable = true;
user = "alice";
};
services.displayManager.defaultSession = "niri";
programs.niri.enable = true;
@@ -52,11 +46,6 @@ pkgs.testers.runNixOSTest {
greetd_config_path = config_match.group(1)
greetd_config = machine.succeed(f"cat {greetd_config_path}")
t.assertIn("dms-greeter", greetd_config)
t.assertIn("[initial_session]", greetd_config)
initial_session = greetd_config.split("[initial_session]", 1)[1]
t.assertIn('user = "alice"', initial_session)
t.assertIn("systemd-cat", initial_session)
script_match = re.search(r'command\s*=\s*"([^"]+/bin/dms-greeter)"', greetd_config)
if script_match is None:
+8 -33
View File
@@ -13,41 +13,16 @@ All build from source.
## Distribution
This is a DMS maintained repo for VoidLinux until these packages are officially merged upstream in the Void Linux repositories, you can install them from our self-hosted custom XBPS repositories served via GitHub Pages.
### Using the Self-Hosted Repositories
We serve both stable release and development packages directly from our repository branches.
#### 1. Add Repository Configurations
Create configuration files in `/etc/xbps.d/` pointing to our repositories (needed for both stable and git/nightly variants):
```sh
echo "repository=https://avengemedia.github.io/DankMaterialShell/current" | sudo tee /etc/xbps.d/dms.conf
echo "repository=https://avengemedia.github.io/DankLinux/current" | sudo tee /etc/xbps.d/danklinux.conf
```
#### 2. Install DMS
Synchronize repositories and install the package:
* For the **stable** variant:
```sh
sudo xbps-install -S dms
```
* For the **git/nightly** variant (this will conflict with and replace the stable package):
```sh
sudo xbps-install -S dms-git
```
*Note: On the first sync, `xbps-install` will output our signing key fingerprint and ask you to type `y` to trust and import it. Verify that the key matches our official signing fingerprint.*
These packages target the official
[`void-linux/void-packages`](https://github.com/void-linux/void-packages)
repository, so they install with a plain `xbps-install dms` and no extra setup.
Most dependencies (`quickshell`, `matugen`, `cava`, `niri`, `greetd`, …) are
already in Void; `dgop` and `danksearch` are packaged alongside in the
[danklinux repo](https://github.com/AvengeMedia/danklinux/tree/master/distro/void).
The templates here are the source of truth: copy each into a void-packages
checkout at `srcpkgs/<pkg>/template` to build or submit it.
checkout at `srcpkgs/<pkg>/template` to build or submit it. Only tagged releases
are packaged (no `-git`/nightly variant).
## Dependencies
-59
View File
@@ -1,59 +0,0 @@
# Template file for 'dms-git'
#
# DankMaterialShell git development release
#
# NOTE: This template is built exclusively by CI. The workflow stages a local
# source tarball and dynamically patches version, checksum, and distfiles
# before calling xbps-src. Do not attempt to build this template manually
# without first providing the source archive.
#
# The binary is built with the `distro_binary` build tag (drops the in-app
# self-update command).
pkgname=dms-git
version=1.4.6.git
revision=1
create_wrksrc=yes
build_style=go
build_wrksrc="core"
go_import_path="github.com/AvengeMedia/DankMaterialShell/core"
go_package="${go_import_path}/cmd/dms"
go_build_tags="distro_binary"
go_ldflags="-X main.Version=${version}"
short_desc="DankMaterialShell — Material 3 desktop shell for Wayland (git variant)"
maintainer="AvengeMedia <AvengeMedia.US@gmail.com>"
license="MIT"
homepage="https://danklinux.com"
# CI populates distfiles/checksum dynamically; these are placeholders.
distfiles="https://github.com/AvengeMedia/DankMaterialShell/archive/refs/tags/v${version}.tar.gz"
checksum=badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb
# Conflicts and provides
conflicts="dms"
provides="dms-${version}_${revision}"
# Optional feature deps are listed in distro/void/README.md.
depends="quickshell accountsservice dgop matugen dbus"
post_install() {
# QML shell tree (build_style=go already installed the dms binary)
vmkdir usr/share/quickshell/dms
vcopy "${wrksrc}/quickshell/*" usr/share/quickshell/dms
echo "${version}" > "${DESTDIR}/usr/share/quickshell/dms/VERSION"
# Desktop entry + icon
vinstall "${wrksrc}/assets/dms-open.desktop" 644 usr/share/applications
vinstall "${wrksrc}/assets/com.danklinux.dms.desktop" 644 usr/share/applications
vinstall "${wrksrc}/assets/danklogo.svg" 644 usr/share/icons/hicolor/scalable/apps
# Shell completions (generated by the built binary; skip when cross-building)
vmkdir usr/share/bash-completion/completions
vmkdir usr/share/zsh/site-functions
vmkdir usr/share/fish/vendor_completions.d
if [ -z "$CROSS_BUILD" ]; then
"${DESTDIR}/usr/bin/dms" completion bash > "${DESTDIR}/usr/share/bash-completion/completions/dms"
"${DESTDIR}/usr/bin/dms" completion zsh > "${DESTDIR}/usr/share/zsh/site-functions/_dms"
"${DESTDIR}/usr/bin/dms" completion fish > "${DESTDIR}/usr/share/fish/vendor_completions.d/dms.fish"
fi
vlicense "${wrksrc}/LICENSE"
}
+2 -2
View File
@@ -21,8 +21,8 @@ changelog="https://github.com/AvengeMedia/DankMaterialShell/releases"
distfiles="https://github.com/AvengeMedia/DankMaterialShell/archive/refs/tags/v${version}.tar.gz"
checksum=f54601e522c883fa9cce02bec070e4321e47389a1cf453e7ad0bb7379ad91b61
# Optional feature deps are listed in distro/void/README.md.
depends="quickshell accountsservice dgop matugen dbus"
# Optional feature deps (XBPS has no "recommends") are listed in distro/void/README.md.
depends="quickshell accountsservice dgop matugen"
post_install() {
# QML shell tree (build_style=go already installed the dms binary)
+1 -1
View File
@@ -55,7 +55,7 @@ Singleton {
readonly property bool isDirectionalEffect: isConnectedEffect || _effect === 1
readonly property bool isDepthEffect: _effect === 2
readonly property bool isConnectedEffect: (typeof FrameTransitionState !== "undefined") && FrameTransitionState.effectiveConnectedFrameModeActive
readonly property bool isConnectedEffect: (typeof SettingsData !== "undefined") && SettingsData.connectedFrameModeActive
readonly property real effectScaleCollapsed: _effectScaleCollapsed[_effect] !== undefined ? _effectScaleCollapsed[_effect] : 0.96
readonly property real effectAnimOffset: _effectAnimOffsets[_effect] !== undefined ? _effectAnimOffsets[_effect] : 16
+1 -21
View File
@@ -7,24 +7,6 @@ function dirname(path) {
return idx > 0 ? path.substring(0, idx) : ".";
}
function sectionHeaderFor(includeLine) {
const line = String(includeLine ?? "").trim();
if (line.startsWith("require"))
return "-- DMS Include Configs";
if (line.startsWith("source"))
return "# DMS Include Configs";
return "// DMS Include Configs";
}
function managedIncludePatternFor(includeLine) {
const line = String(includeLine ?? "").trim();
if (line.startsWith("require"))
return "require.*dms[.]";
if (line.startsWith("source"))
return "source.*dms/";
return "include.*dms/";
}
function buildRepairScript(options) {
const configFile = options.configFile;
const backupFile = options.backupFile;
@@ -49,9 +31,7 @@ function buildRepairScript(options) {
for (const include of includes) {
if (!include.grepPattern || !include.includeLine)
continue;
const sectionHeader = options.sectionHeader || sectionHeaderFor(include.includeLine);
const managedIncludePattern = managedIncludePatternFor(include.includeLine);
commands.push(`if ! grep -v '^[[:space:]]*\\(//\\|#\\|--\\)' ${shQuote(configFile)} 2>/dev/null | grep -q ${shQuote(include.grepPattern)}; then if grep -Fqx ${shQuote(sectionHeader)} ${shQuote(configFile)} 2>/dev/null || grep -v '^[[:space:]]*\\(//\\|#\\|--\\)' ${shQuote(configFile)} 2>/dev/null | grep -q ${shQuote(managedIncludePattern)}; then printf '%s\\n' ${shQuote(include.includeLine)} >> ${shQuote(configFile)}; elif [ -s ${shQuote(configFile)} ]; then printf '\\n%s\\n%s\\n' ${shQuote(sectionHeader)} ${shQuote(include.includeLine)} >> ${shQuote(configFile)}; else printf '%s\\n%s\\n' ${shQuote(sectionHeader)} ${shQuote(include.includeLine)} >> ${shQuote(configFile)}; fi; fi`);
commands.push(`if ! grep -v '^[[:space:]]*\\(//\\|#\\|--\\)' ${shQuote(configFile)} 2>/dev/null | grep -q ${shQuote(include.grepPattern)}; then echo '' >> ${shQuote(configFile)} && printf '%s\\n' ${shQuote(include.includeLine)} >> ${shQuote(configFile)}; fi`);
}
return commands.join("; ");
@@ -1,56 +0,0 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
Singleton {
id: root
property int revision: 0
property int appliedRevision: 0
readonly property bool ready: appliedRevision >= revision
// Latched: surfaces render the last compositor-acknowledged state until the atomic flip on ack
property bool effectiveFrameEnabled: false
property string effectiveFrameMode: "connected"
readonly property bool effectiveConnectedFrameModeActive: effectiveFrameEnabled && effectiveFrameMode === "connected"
signal transitionRequested(int revision)
function begin() {
revision++;
transitionRequested(revision);
return revision;
}
function acknowledge(requestRevision) {
if (requestRevision > appliedRevision)
appliedRevision = requestRevision;
}
function syncEffective() {
effectiveFrameEnabled = SettingsData.frameEnabled;
effectiveFrameMode = SettingsData.frameMode;
}
onReadyChanged: {
if (ready)
syncEffective();
}
// Tracks settings-load changes; live toggles begin() first (ready false) so the latch holds
Connections {
target: SettingsData
function onFrameEnabledChanged() {
if (root.ready)
root.syncEffective();
}
function onFrameModeChanged() {
if (root.ready)
root.syncEffective();
}
}
Component.onCompleted: syncEffective()
}
-10
View File
@@ -25,16 +25,6 @@ Singleton {
return CompositorService.useHyprlandFocusGrab && keyboardFocus(active, customFocus) === WlrKeyboardFocus.OnDemand;
}
function captureActiveToplevel() {
return ToplevelManager.activeToplevel;
}
function restoreToplevel(toplevel) {
if (toplevel)
Qt.callLater(() => toplevel.activate());
return null;
}
property list<var> barWindows: []
function registerBarWindow(window) {
+3 -99
View File
@@ -262,11 +262,7 @@ Singleton {
}
property bool frameEnabled: false
onFrameEnabledChanged: {
saveSettings();
if (!_loading)
updateFrameCompositorLayout();
}
onFrameEnabledChanged: saveSettings()
property real frameThickness: 16
onFrameThicknessChanged: saveSettings()
property int barInsetPaddingShared: -1
@@ -299,11 +295,7 @@ Singleton {
onFrameLauncherEdgeHoverChanged: saveSettings()
readonly property string frameModalEmergeSide: frameLauncherEmergeSide === "top" ? "bottom" : "top"
property string frameMode: "connected"
onFrameModeChanged: {
saveSettings();
if (!_loading && frameEnabled)
updateFrameCompositorLayout();
}
onFrameModeChanged: saveSettings()
property var connectedFrameBarStyleBackups: ({})
onConnectedFrameBarStyleBackupsChanged: saveSettings()
readonly property bool connectedFrameModeActive: frameEnabled && frameMode === "connected"
@@ -456,7 +448,6 @@ Singleton {
property string audioScrollMode: "volume"
property int audioWheelScrollAmount: 5
property bool audioDeviceScrollVolumeEnabled: false
property var mediaExcludePlayers: []
property bool clockCompactMode: false
property int focusedWindowSize: 1
property bool focusedWindowCompactMode: false
@@ -1016,17 +1007,6 @@ Singleton {
}
]
// Standalone bar xray is unsafe when windows can render beneath its surface
function _standaloneBarXrayAvailable(configs) {
const list = configs || [];
const activeBars = list.filter(c => c && c.enabled && (c.visible ?? true));
const gapsOverride = (typeof CompositorService !== "undefined" && CompositorService.isHyprland) ? hyprlandLayoutGapsOverride : niriLayoutGapsOverride;
const layoutGaps = gapsOverride >= 0 ? gapsOverride : Math.max(4, (list[0]?.spacing ?? 4));
return activeBars.every(c => !c.autoHide && !(c.useOverlayLayer ?? false) && (c.spacing ?? 4) + (c.bottomGap ?? 0) + layoutGaps >= 0);
}
readonly property bool standaloneBarXrayAvailable: _standaloneBarXrayAvailable(barConfigs)
property bool desktopClockEnabled: false
property string desktopClockStyle: "analog"
property real desktopClockTransparency: 0.8
@@ -1342,35 +1322,6 @@ Singleton {
return true;
}
function moveDesktopWidgetInstanceToGroup(instanceId, groupId, newIndexInGroup) {
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
const groups = desktopWidgetGroups || [];
const idx = instances.findIndex(inst => inst.id === instanceId);
if (idx === -1)
return false;
const [item] = instances.splice(idx, 1);
item.group = groupId || null;
const groupMatches = inst => {
if (!groupId)
return !inst.group || !groups.some(g => g.id === inst.group);
return inst.group === groupId;
};
const groupInstances = instances.filter(groupMatches);
const clamped = Math.max(0, Math.min(newIndexInGroup, groupInstances.length));
let targetGlobalIdx;
if (clamped >= groupInstances.length) {
const last = groupInstances[groupInstances.length - 1];
targetGlobalIdx = last ? instances.findIndex(inst => inst.id === last.id) + 1 : instances.length;
} else {
const targetInstance = groupInstances[clamped];
targetGlobalIdx = instances.findIndex(inst => inst.id === targetInstance.id);
}
instances.splice(targetGlobalIdx, 0, item);
desktopWidgetInstances = instances;
saveSettings();
return true;
}
function createDesktopWidgetGroup(name) {
const id = "dwg_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
const group = {
@@ -1471,17 +1422,6 @@ Singleton {
MangoService.generateLayoutConfig();
}
function updateFrameCompositorLayout() {
// Generate before begin() so compositor readiness is already pending at transitionRequested
if (typeof CompositorService !== "undefined") {
if (CompositorService.isNiri && typeof NiriService !== "undefined")
NiriService.generateNiriLayoutConfig(true);
if (CompositorService.isHyprland && typeof HyprlandService !== "undefined")
HyprlandService.generateLayoutConfig(true);
}
FrameTransitionState.begin();
}
function resolveIconTheme() {
if (iconThemePerMode && typeof SessionData !== "undefined" && SessionData.isLightMode)
return iconThemeLight;
@@ -2429,17 +2369,13 @@ Singleton {
if (index === -1)
return;
const positionChanged = updates.position !== undefined && configs[index].position !== updates.position;
const barXrayTargetWasAvailable = _standaloneBarXrayAvailable(configs);
if (updates.autoHide === false || updates.visible === false)
setBarIpcReveal(barId, false);
Object.assign(configs[index], updates);
const sanitizedConfigs = _sanitizeBarConfigsForConnectedFrame(configs).configs;
barConfigs = sanitizedConfigs;
barConfigs = _sanitizeBarConfigsForConnectedFrame(configs).configs;
updateBarConfigs();
if (!frameEnabled && _standaloneBarXrayAvailable(sanitizedConfigs) !== barXrayTargetWasAvailable)
updateCompositorLayout();
if (positionChanged) {
NotificationService.dismissAllPopups();
}
@@ -3126,32 +3062,6 @@ Singleton {
saveSettings();
}
function addMediaExcludePlayer(identity) {
if (identity === undefined || identity === null)
return;
var normalizedIdentity = identity.toString().trim().toLowerCase();
if (!normalizedIdentity)
return;
var list = mediaExcludePlayers ? mediaExcludePlayers.slice() : [];
var normalizedList = list.map(function (id) {
return id ? id.toString().trim().toLowerCase() : "";
});
if (normalizedList.indexOf(normalizedIdentity) >= 0)
return;
list.push(normalizedIdentity);
mediaExcludePlayers = list;
saveSettings();
}
function removeMediaExcludePlayer(index) {
var list = mediaExcludePlayers ? mediaExcludePlayers.slice() : [];
if (index < 0 || index >= list.length)
return;
list.splice(index, 1);
mediaExcludePlayers = list;
saveSettings();
}
property bool _pendingExpandNotificationRules: false
property int _pendingNotificationRuleIndex: -1
@@ -3572,9 +3482,6 @@ Singleton {
onLoaded: {
if (isGreeterMode)
return;
const wasLoaded = _hasLoaded;
const prevFrameEnabled = frameEnabled;
const prevFrameMode = frameMode;
_loading = true;
_hasUnsavedChanges = false;
try {
@@ -3609,9 +3516,6 @@ Singleton {
} finally {
_loading = false;
}
// External edits reload under _loading, which skips the per-property transition triggers
if (wasLoaded && !_parseError && (frameEnabled !== prevFrameEnabled || (frameEnabled && frameMode !== prevFrameMode)))
updateFrameCompositorLayout();
}
onLoadFailed: error => {
if (!isGreeterMode) {
-40
View File
@@ -17,8 +17,6 @@ const StockThemes = {
background: "#101418",
backgroundText: "#e0e2e8",
outline: "#8c9199",
surfaceContainerLowest: "#0c1014",
surfaceContainerLow: "#191c20",
surfaceContainer: "#1d2024",
surfaceContainerHigh: "#272a2f",
surfaceContainerHighest: "#32353a",
@@ -37,8 +35,6 @@ const StockThemes = {
background: "#141218",
backgroundText: "#e6e0e9",
outline: "#948f99",
surfaceContainerLowest: "#100e14",
surfaceContainerLow: "#1d1b20",
surfaceContainer: "#211f24",
surfaceContainerHigh: "#2b292f",
surfaceContainerHighest: "#36343a",
@@ -57,8 +53,6 @@ const StockThemes = {
background: "#10140f",
backgroundText: "#e0e4db",
outline: "#8c9388",
surfaceContainerLowest: "#0c100b",
surfaceContainerLow: "#191d17",
surfaceContainer: "#1d211b",
surfaceContainerHigh: "#272b25",
surfaceContainerHighest: "#323630",
@@ -77,8 +71,6 @@ const StockThemes = {
background: "#1a120e",
backgroundText: "#f0dfd8",
outline: "#a08d85",
surfaceContainerLowest: "#160e0a",
surfaceContainerLow: "#231a16",
surfaceContainer: "#271e1a",
surfaceContainerHigh: "#322824",
surfaceContainerHighest: "#3d332e",
@@ -97,8 +89,6 @@ const StockThemes = {
background: "#1a1110",
backgroundText: "#f1dedc",
outline: "#a08c89",
surfaceContainerLowest: "#160d0c",
surfaceContainerLow: "#231918",
surfaceContainer: "#271d1c",
surfaceContainerHigh: "#322826",
surfaceContainerHighest: "#3d3231",
@@ -117,8 +107,6 @@ const StockThemes = {
background: "#0e1416",
backgroundText: "#dee3e5",
outline: "#899295",
surfaceContainerLowest: "#0a1012",
surfaceContainerLow: "#171d1e",
surfaceContainer: "#1b2122",
surfaceContainerHigh: "#252b2c",
surfaceContainerHighest: "#303637",
@@ -137,8 +125,6 @@ const StockThemes = {
background: "#191112",
backgroundText: "#f0dee0",
outline: "#9f8c8e",
surfaceContainerLowest: "#150d0e",
surfaceContainerLow: "#22191a",
surfaceContainer: "#261d1e",
surfaceContainerHigh: "#312829",
surfaceContainerHighest: "#3c3233",
@@ -157,8 +143,6 @@ const StockThemes = {
background: "#17130b",
backgroundText: "#ebe1d4",
outline: "#998f80",
surfaceContainerLowest: "#130f07",
surfaceContainerLow: "#1f1b13",
surfaceContainer: "#231f17",
surfaceContainerHigh: "#2e2921",
surfaceContainerHighest: "#39342b",
@@ -177,8 +161,6 @@ const StockThemes = {
background: "#1a1110",
backgroundText: "#f1dedc",
outline: "#a08c8a",
surfaceContainerLowest: "#160d0c",
surfaceContainerLow: "#231918",
surfaceContainer: "#271d1c",
surfaceContainerHigh: "#322826",
surfaceContainerHighest: "#3d3231",
@@ -197,8 +179,6 @@ const StockThemes = {
background: "#131315",
backgroundText: "#e4e2e3",
outline: "#929092",
surfaceContainerLowest: "#252525",
surfaceContainerLow: "#303030",
surfaceContainer: "#353535",
surfaceContainerHigh: "#424242",
surfaceContainerHighest: "#505050",
@@ -223,8 +203,6 @@ const StockThemes = {
background: "#f7f9ff",
backgroundText: "#181c20",
outline: "#72777f",
surfaceContainerLowest: "#f9fbff",
surfaceContainerLow: "#eef0f6",
surfaceContainer: "#eceef4",
surfaceContainerHigh: "#e6e8ee",
surfaceContainerHighest: "#e0e2e8",
@@ -243,8 +221,6 @@ const StockThemes = {
background: "#fef7ff",
backgroundText: "#1d1b20",
outline: "#7a757f",
surfaceContainerLowest: "#fff9ff",
surfaceContainerLow: "#f4eef6",
surfaceContainer: "#f2ecf4",
surfaceContainerHigh: "#ece6ee",
surfaceContainerHighest: "#e6e0e9",
@@ -263,8 +239,6 @@ const StockThemes = {
background: "#f7fbf1",
backgroundText: "#191d17",
outline: "#72796f",
surfaceContainerLowest: "#f9fdf3",
surfaceContainerLow: "#eef1e8",
surfaceContainer: "#ecefe6",
surfaceContainerHigh: "#e6e9e0",
surfaceContainerHighest: "#e0e4db",
@@ -283,8 +257,6 @@ const StockThemes = {
background: "#fff8f6",
backgroundText: "#221a16",
outline: "#85736c",
surfaceContainerLowest: "#fffaf8",
surfaceContainerLow: "#feece5",
surfaceContainer: "#fceae3",
surfaceContainerHigh: "#f6e5de",
surfaceContainerHighest: "#f0dfd8",
@@ -303,8 +275,6 @@ const StockThemes = {
background: "#fff8f7",
backgroundText: "#231918",
outline: "#857370",
surfaceContainerLowest: "#fffaf9",
surfaceContainerLow: "#feece9",
surfaceContainer: "#fceae7",
surfaceContainerHigh: "#f7e4e1",
surfaceContainerHighest: "#f1dedc",
@@ -323,8 +293,6 @@ const StockThemes = {
background: "#f5fafc",
backgroundText: "#171d1e",
outline: "#6f797b",
surfaceContainerLowest: "#f7fcfe",
surfaceContainerLow: "#ebf1f2",
surfaceContainer: "#e9eff0",
surfaceContainerHigh: "#e3e9eb",
surfaceContainerHighest: "#dee3e5",
@@ -343,8 +311,6 @@ const StockThemes = {
background: "#fff8f7",
backgroundText: "#22191a",
outline: "#847375",
surfaceContainerLowest: "#fffaf9",
surfaceContainerLow: "#fdeced",
surfaceContainer: "#fbeaeb",
surfaceContainerHigh: "#f5e4e5",
surfaceContainerHighest: "#f0dee0",
@@ -363,8 +329,6 @@ const StockThemes = {
background: "#fff8f2",
backgroundText: "#1f1b13",
outline: "#7f7667",
surfaceContainerLowest: "#fffaf4",
surfaceContainerLow: "#f8eee1",
surfaceContainer: "#f6ecdf",
surfaceContainerHigh: "#f1e7d9",
surfaceContainerHighest: "#ebe1d4",
@@ -383,8 +347,6 @@ const StockThemes = {
background: "#fff8f7",
backgroundText: "#231918",
outline: "#857371",
surfaceContainerLowest: "#fffaf9",
surfaceContainerLow: "#feece9",
surfaceContainer: "#fceae7",
surfaceContainerHigh: "#f6e4e2",
surfaceContainerHighest: "#f1dedc",
@@ -403,8 +365,6 @@ const StockThemes = {
background: "#ffffff",
backgroundText: "#1a1a1a",
outline: "#757577",
surfaceContainerLowest: "#fafafb",
surfaceContainerLow: "#ededef",
surfaceContainer: "#e8e8ea",
surfaceContainerHigh: "#dcdcde",
surfaceContainerHighest: "#d0d0d2",
+46 -64
View File
@@ -463,8 +463,6 @@ Singleton {
"background": getMatugenColor("background", "#1a1c1e"),
"backgroundText": getMatugenColor("on_background", "#e3e8ef"),
"outline": getMatugenColor("outline", "#8e918f"),
"surfaceContainerLowest": getMatugenColor("surface_container_lowest", "#0e1013"),
"surfaceContainerLow": getMatugenColor("surface_container_low", "#181a1d"),
"surfaceContainer": getMatugenColor("surface_container", "#1e2023"),
"surfaceContainerHigh": getMatugenColor("surface_container_high", "#292b2f"),
"surfaceContainerHighest": getMatugenColor("surface_container_highest", "#343740"),
@@ -537,9 +535,7 @@ Singleton {
property color background: currentThemeData.background
property color backgroundText: currentThemeData.backgroundText
property color outline: currentThemeData.outline
property color outlineVariant: currentThemeData.outlineVariant || withAlpha(outline, 0.6)
property color surfaceContainerLowest: currentThemeData.surfaceContainerLowest || blend(surfaceContainer, surface, 1.2)
property color surfaceContainerLow: currentThemeData.surfaceContainerLow || blend(surface, surfaceContainer, 0.667)
property color outlineVariant: currentThemeData.outlineVariant || Qt.rgba(outline.r, outline.g, outline.b, 0.6)
property color surfaceContainer: currentThemeData.surfaceContainer
property color surfaceContainerHigh: currentThemeData.surfaceContainerHigh
property color surfaceContainerHighest: currentThemeData.surfaceContainerHighest || surfaceContainerHigh
@@ -550,9 +546,9 @@ Singleton {
property color onSurface: surfaceText
property color onSurfaceVariant: surfaceVariantText
property color onPrimary: primaryText
property color onSurface_12: withAlpha(onSurface, 0.12)
property color onSurface_38: withAlpha(onSurface, 0.38)
property color onSurfaceVariant_30: withAlpha(onSurfaceVariant, 0.30)
property color onSurface_12: Qt.rgba(onSurface.r, onSurface.g, onSurface.b, 0.12)
property color onSurface_38: Qt.rgba(onSurface.r, onSurface.g, onSurface.b, 0.38)
property color onSurfaceVariant_30: Qt.rgba(onSurfaceVariant.r, onSurfaceVariant.g, onSurfaceVariant.b, 0.30)
property color error: currentThemeData.error || "#F2B8B5"
property color warning: currentThemeData.warning || "#FF9800"
@@ -561,33 +557,32 @@ Singleton {
property color tempDanger: "#ff5555"
property color success: currentThemeData.success || "#4CAF50"
property color primaryHover: withAlpha(primary, 0.12)
property color primaryHoverLight: withAlpha(primary, transparentBlurLayers ? 0.12 : 0.08)
property color primaryPressed: withAlpha(primary, transparentBlurLayers ? 0.24 : 0.16)
property color primarySelected: withAlpha(primary, 0.3)
property color primaryBackground: withAlpha(primary, 0.04)
property color primaryHover: Qt.rgba(primary.r, primary.g, primary.b, 0.12)
property color primaryHoverLight: Qt.rgba(primary.r, primary.g, primary.b, transparentBlurLayers ? 0.12 : 0.08)
property color primaryPressed: Qt.rgba(primary.r, primary.g, primary.b, transparentBlurLayers ? 0.24 : 0.16)
property color primarySelected: Qt.rgba(primary.r, primary.g, primary.b, 0.3)
property color primaryBackground: Qt.rgba(primary.r, primary.g, primary.b, 0.04)
property color secondaryHover: withAlpha(secondary, 0.08)
property color secondaryHover: Qt.rgba(secondary.r, secondary.g, secondary.b, 0.08)
property color surfaceHover: withAlpha(surfaceVariant, 0.08)
property color surfacePressed: withAlpha(surfaceVariant, 0.12)
property color surfaceSelected: withAlpha(surfaceVariant, 0.15)
property color surfaceLight: withAlpha(surfaceVariant, transparentBlurLayers ? 0.3 : 0.1)
property color surfaceVariantAlpha: withAlpha(surfaceVariant, 0.2)
property color surfaceHover: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.08)
property color surfacePressed: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.12)
property color surfaceSelected: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.15)
property color surfaceLight: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, transparentBlurLayers ? 0.3 : 0.1)
property color surfaceVariantAlpha: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.2)
readonly property bool foregroundLayers: typeof SettingsData === "undefined" || (SettingsData.blurForegroundLayers ?? true)
readonly property bool blurForegroundLayers: BlurService.enabled && foregroundLayers
readonly property bool transparentBlurLayers: BlurService.enabled && !foregroundLayers
readonly property bool blurForegroundLayers: BlurService.enabled && (typeof SettingsData === "undefined" || (SettingsData.blurForegroundLayers ?? true))
readonly property bool transparentBlurLayers: BlurService.enabled && !blurForegroundLayers
readonly property color readableSurface: withAlpha(surfaceContainer, popupTransparency)
readonly property color readableSurfaceHigh: withAlpha(surfaceContainerHigh, popupTransparency)
readonly property color floatingSurface: foregroundLayers ? readableSurface : withAlpha(readableSurface, 0)
readonly property color floatingSurfaceHigh: foregroundLayers ? readableSurfaceHigh : withAlpha(readableSurfaceHigh, 0)
readonly property color floatingSurface: transparentBlurLayers ? "transparent" : readableSurface
readonly property color floatingSurfaceHigh: transparentBlurLayers ? "transparent" : readableSurfaceHigh
readonly property color nestedSurface: floatingSurfaceHigh
readonly property real blurLayerOutlineOpacity: Math.max(0, Math.min(1, typeof SettingsData === "undefined" ? 0.12 : (SettingsData.blurLayerOutlineOpacity ?? 0.12)))
readonly property real layerOutlineOpacity: blurLayerOutlineOpacity
readonly property int layerOutlineWidth: layerOutlineOpacity > 0 ? 1 : 0
property color surfaceTextHover: withAlpha(surfaceText, 0.08)
property color surfaceTextAlpha: withAlpha(surfaceText, 0.3)
readonly property real layerOutlineOpacity: BlurService.enabled ? blurLayerOutlineOpacity : 0.08
readonly property int layerOutlineWidth: BlurService.enabled && layerOutlineOpacity > 0 ? 1 : 0
property color surfaceTextHover: Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.08)
property color surfaceTextAlpha: Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.3)
function roleColor(mode) {
switch (mode) {
@@ -612,10 +607,6 @@ Singleton {
return surfaceVariant;
case "s":
return surface;
case "scll":
return surfaceContainerLowest;
case "scl":
return surfaceContainerLow;
case "sc":
return surfaceContainer;
case "sch":
@@ -628,23 +619,19 @@ Singleton {
case "err":
return error;
default:
return withAlpha(surface, 0);
return "transparent";
}
}
property color surfaceTextLight: withAlpha(surfaceText, 0.06)
property color surfaceTextSecondary: withAlpha(surfaceText, 0.6)
property color surfaceTextMedium: withAlpha(surfaceText, 0.7)
property color surfaceTextLight: Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.06)
property color surfaceTextMedium: Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.7)
property color outlineButton: withAlpha(outline, 0.5)
property color outlineLight: withAlpha(outline, Math.min(1, layerOutlineOpacity * 0.625))
property color outlineMedium: withAlpha(outline, layerOutlineOpacity)
property color outlineStrong: withAlpha(outline, Math.min(1, layerOutlineOpacity * 1.5))
property color outlineHeavy: withAlpha(outline, 0.2)
property color outlineButton: Qt.rgba(outline.r, outline.g, outline.b, 0.5)
property color outlineLight: Qt.rgba(outline.r, outline.g, outline.b, BlurService.enabled ? Math.min(1, layerOutlineOpacity * 0.625) : 0.05)
property color outlineMedium: Qt.rgba(outline.r, outline.g, outline.b, layerOutlineOpacity)
property color outlineStrong: Qt.rgba(outline.r, outline.g, outline.b, BlurService.enabled ? Math.min(1, layerOutlineOpacity * 1.5) : 0.12)
property color errorHover: withAlpha(error, 0.12)
property color errorPressed: withAlpha(error, 0.16)
property color errorSelected: withAlpha(error, 0.3)
property color warningHover: withAlpha(warning, 0.12)
property color errorHover: Qt.rgba(error.r, error.g, error.b, 0.12)
property color errorPressed: Qt.rgba(error.r, error.g, error.b, 0.16)
readonly property color ccTileActiveBg: {
switch (SettingsData.controlCenterTileColorMode) {
@@ -659,7 +646,7 @@ Singleton {
}
}
readonly property color ccTileInactiveBg: transparentBlurLayers ? withAlpha(surfaceContainerHigh, 0.16) : (foregroundLayers ? withAlpha(surfaceContainerHigh, BlurService.enabled ? Math.min(popupTransparency, 0.24) : popupTransparency) : withAlpha(surfaceContainer, 0))
readonly property color ccTileInactiveBg: transparentBlurLayers ? withAlpha(surfaceContainerHigh, 0.16) : (blurForegroundLayers ? withAlpha(surfaceContainerHigh, Math.min(popupTransparency, 0.24)) : withAlpha(surfaceContainer, popupTransparency))
readonly property color ccPillInactiveBg: transparentBlurLayers ? withAlpha(surfaceContainerHigh, 0.08) : nestedSurface
readonly property color ccPillInactiveHoverBg: transparentBlurLayers ? withAlpha(primary, 0.10) : primaryPressed
readonly property color ccSliderTrackColor: transparentBlurLayers ? surfaceText : surfaceContainerHigh
@@ -694,13 +681,13 @@ Singleton {
readonly property color ccTileRing: {
switch (SettingsData.controlCenterTileColorMode) {
case "primaryContainer":
return withAlpha(primary, 0.22);
return Qt.rgba(primary.r, primary.g, primary.b, 0.22);
case "secondary":
return withAlpha(surfaceText, 0.22);
return Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.22);
case "surfaceVariant":
return withAlpha(surfaceText, 0.22);
return Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.22);
default:
return withAlpha(primaryText, 0.22);
return Qt.rgba(primaryText.r, primaryText.g, primaryText.b, 0.22);
}
}
@@ -733,11 +720,11 @@ Singleton {
readonly property color buttonHover: {
switch (SettingsData.buttonColorMode) {
case "primaryContainer":
return withAlpha(primary, 0.12);
return Qt.rgba(primary.r, primary.g, primary.b, 0.12);
case "secondary":
return withAlpha(surfaceText, 0.12);
return Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.12);
case "surfaceVariant":
return withAlpha(surfaceText, 0.12);
return Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.12);
default:
return primaryHover;
}
@@ -746,11 +733,11 @@ Singleton {
readonly property color buttonPressed: {
switch (SettingsData.buttonColorMode) {
case "primaryContainer":
return withAlpha(primary, 0.16);
return Qt.rgba(primary.r, primary.g, primary.b, 0.16);
case "secondary":
return withAlpha(surfaceText, 0.16);
return Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.16);
case "surfaceVariant":
return withAlpha(surfaceText, 0.16);
return Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.16);
default:
return primaryPressed;
}
@@ -1076,12 +1063,12 @@ Singleton {
readonly property real connectedCornerRadius: {
if (typeof SettingsData === "undefined")
return 12;
return FrameTransitionState.effectiveConnectedFrameModeActive ? SettingsData.frameRounding : cornerRadius;
return SettingsData.connectedFrameModeActive ? SettingsData.frameRounding : cornerRadius;
}
readonly property color connectedSurfaceColor: {
if (typeof SettingsData === "undefined")
return withAlpha(surfaceContainer, popupTransparency);
return isConnectedEffect ? withAlpha(SettingsData.effectiveFrameColor, SettingsData.frameOpacity) : withAlpha(surfaceContainer, popupTransparency);
return isConnectedEffect ? Qt.rgba(SettingsData.effectiveFrameColor.r, SettingsData.effectiveFrameColor.g, SettingsData.effectiveFrameColor.b, SettingsData.frameOpacity) : withAlpha(surfaceContainer, popupTransparency);
}
readonly property real connectedSurfaceRadius: isConnectedEffect ? connectedCornerRadius : cornerRadius
readonly property bool connectedSurfaceBlurEnabled: (typeof SettingsData === "undefined") ? true : (!isConnectedEffect || SettingsData.frameBlurEnabled)
@@ -1250,7 +1237,6 @@ Singleton {
return typeof SettingsData !== "undefined" ? SettingsData.fontScale : 1.0;
}
property real spacingXXS: 2
property real spacingXS: 4
property real spacingS: 8
property real spacingM: 12
@@ -1482,7 +1468,7 @@ Singleton {
property string currentThemeName: currentTheme
function panelBackground() {
return withAlpha(surfaceContainer, panelTransparency);
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, panelTransparency);
}
property real notepadTransparency: SettingsData.notepadTransparencyOverride >= 0 ? SettingsData.notepadTransparencyOverride : popupTransparency
@@ -1992,8 +1978,6 @@ Singleton {
}
function withAlpha(c, a) {
if (!c || c.r === undefined)
return Qt.rgba(0, 0, 0, 0);
return Qt.rgba(c.r, c.g, c.b, a);
}
@@ -2004,8 +1988,6 @@ Singleton {
}
function blendAlpha(c, a) {
if (!c || c.r === undefined)
return Qt.rgba(0, 0, 0, 0);
return Qt.rgba(c.r, c.g, c.b, c.a * a);
}
@@ -184,7 +184,6 @@ var SPEC = {
audioScrollMode: { def: "volume" },
audioWheelScrollAmount: { def: 5 },
audioDeviceScrollVolumeEnabled: { def: false },
mediaExcludePlayers: { def: [] },
clockCompactMode: { def: false },
focusedWindowCompactMode: { def: false },
focusedWindowSize: { def: 1 },
+8 -34
View File
@@ -173,7 +173,6 @@ Item {
}
property bool barSurfacesLoaded: true
property int pendingFrameTransitionRevision: 0
function recreateBarSurfaces() {
log.info("Recreating bar surfaces, screens:", Quickshell.screens.length, Quickshell.screens.map(s => s.name).join(","));
@@ -182,23 +181,9 @@ Item {
barSurfaceReloadAction.schedule();
}
// Holds the bar rebuild until the compositor applies the layout, so the swap lands in one pass
function runPendingFrameTransition() {
if (pendingFrameTransitionRevision <= 0 || !CompositorService.frameCompositorLayoutReady)
return;
recreateBarSurfaces();
}
DeferredAction {
id: barSurfaceReloadAction
onTriggered: {
// Ack first so the latch flips and new bars build directly in the post-transition state
if (root.pendingFrameTransitionRevision > 0 && CompositorService.frameCompositorLayoutReady) {
FrameTransitionState.acknowledge(root.pendingFrameTransitionRevision);
root.pendingFrameTransitionRevision = 0;
}
root.barSurfacesLoaded = true;
}
onTriggered: root.barSurfacesLoaded = true
}
property string _barLayoutStateJson: {
@@ -227,23 +212,14 @@ Item {
}
}
Connections {
target: FrameTransitionState
function onTransitionRequested(revision) {
root.pendingFrameTransitionRevision = Math.max(root.pendingFrameTransitionRevision, revision);
root.runPendingFrameTransition();
}
}
Connections {
target: CompositorService
function onFrameCompositorLayoutReadyChanged() {
root.runPendingFrameTransition();
}
}
Connections {
target: SettingsData
function onFrameEnabledChanged() {
root.recreateBarSurfaces();
}
function onConnectedFrameModeActiveChanged() {
root.recreateBarSurfaces();
}
function onForceDankBarLayoutRefresh() {
root.recreateBarSurfaces();
}
@@ -258,7 +234,7 @@ Item {
}
Loader {
active: FrameTransitionState.effectiveFrameEnabled && SettingsData.frameLauncherEdgeHover
active: SettingsData.frameEnabled && SettingsData.frameLauncherEdgeHover
asynchronous: false
sourceComponent: FrameLauncherHoverZone {}
}
@@ -275,8 +251,6 @@ Item {
values: JSON.parse(root._barLayoutStateJson)
}
Component.onCompleted: BarWidgetService.dankBarRepeater = dankBarRepeater
property var hyprlandOverviewLoaderRef: hyprlandOverviewLoader
delegate: Loader {
+3 -3
View File
@@ -253,7 +253,7 @@ DankModal {
}
Row {
spacing: Theme.spacingXS
spacing: 4
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
@@ -264,7 +264,7 @@ DankModal {
iconName: "view_list"
iconSize: 20
iconColor: root.viewMode === "list" ? Theme.primary : Theme.surfaceText
backgroundColor: root.viewMode === "list" ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0)
backgroundColor: root.viewMode === "list" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
onClicked: {
root.viewMode = "list";
}
@@ -276,7 +276,7 @@ DankModal {
iconName: "grid_view"
iconSize: 20
iconColor: root.viewMode === "grid" ? Theme.primary : Theme.surfaceText
backgroundColor: root.viewMode === "grid" ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0)
backgroundColor: root.viewMode === "grid" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
onClicked: {
root.viewMode = "grid";
}
+2 -2
View File
@@ -224,7 +224,7 @@ DankModal {
Column {
anchors.centerIn: parent
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: I18n.tr("Passkey:")
@@ -256,7 +256,7 @@ DankModal {
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: cancelArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0)
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
border.color: Theme.surfaceVariantAlpha
border.width: 1
@@ -48,7 +48,7 @@ Rectangle {
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
width: parent.width - root.iconContainerSize - Theme.spacingS
StyledText {
@@ -24,8 +24,6 @@ FloatingWindow {
color: Theme.surfaceContainer
visible: false
onClosed: visible = false
FocusScope {
id: contentFocusScope
anchors.fill: parent
@@ -147,7 +147,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
iconName: "filter_list"
iconColor: modal.activeFilter !== "all" ? Theme.primary : Theme.surfaceText
backgroundColor: modal.activeFilter !== "all" ? Theme.primarySelected : Theme.withAlpha(Theme.primarySelected, 0)
backgroundColor: modal.activeFilter !== "all" ? Theme.primarySelected : "transparent"
tooltipText: I18n.tr("Filter by type", "Clipboard history type filter button tooltip")
onClicked: filterMenuLoader.item?.openDropdownMenu()
}
@@ -121,12 +121,12 @@ Item {
const hostX = host?.alignedX;
const hostY = host?.renderedAlignedY ?? host?.alignedY;
const globalPos = (!isNaN(hostX) && !isNaN(hostY)) ? ({
x: screenX + hostX + x,
y: screenY + hostY + y
}) : (parentHandler ? parentHandler.mapToGlobal(x, y) : ({
x: screenX + x,
y: screenY + y
}));
x: screenX + hostX + x,
y: screenY + hostY + y
}) : (parentHandler ? parentHandler.mapToGlobal(x, y) : ({
x: screenX + x,
y: screenY + y
}));
targetScreen = screenRef;
anchorX = globalPos.x - screenX + 4;
@@ -258,7 +258,7 @@ Item {
height: root.effectiveMenuHeight
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
radius: Theme.cornerRadius
border.color: BlurService.enabled ? BlurService.borderColor : Theme.outlineMedium
border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: BlurService.enabled ? BlurService.borderWidth : 1
opacity: root.openState ? 1 : 0
@@ -321,7 +321,7 @@ Item {
anchors.centerIn: parent
width: parent.width
height: 1
color: Theme.outlineHeavy
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
}
}
@@ -330,7 +330,7 @@ Item {
width: parent.width
height: parent.height
radius: Theme.cornerRadius
color: itemMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(BlurService.hoverColor(Theme.widgetBaseHoverColor), 0)
color: itemMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
Row {
anchors.left: parent.left
@@ -419,7 +419,7 @@ Item {
implicitWidth: saveMenuRow.implicitWidth + Theme.spacingS * 2
implicitHeight: saveMenuRow.implicitHeight + Theme.spacingS * 2
radius: Theme.cornerRadius
color: saveMenuSaveArea.containsMouse ? Theme.surfaceVariant : Theme.withAlpha(Theme.surfaceVariant, 0)
color: saveMenuSaveArea.containsMouse ? Theme.surfaceVariant : "transparent"
Row {
id: saveMenuRow
@@ -457,7 +457,7 @@ Item {
implicitWidth: saveMenuCloseRow.implicitWidth + Theme.spacingS * 2
implicitHeight: saveMenuCloseRow.implicitHeight + Theme.spacingS * 2
radius: Theme.cornerRadius
color: saveMenuCloseArea.containsMouse ? Theme.surfaceVariant : Theme.withAlpha(Theme.surfaceVariant, 0)
color: saveMenuCloseArea.containsMouse ? Theme.surfaceVariant : "transparent"
Row {
id: saveMenuCloseRow
@@ -495,7 +495,7 @@ Item {
implicitWidth: saveMenuPasteRow.implicitWidth + Theme.spacingS * 2
implicitHeight: saveMenuPasteRow.implicitHeight + Theme.spacingS * 2
radius: Theme.cornerRadius
color: saveMenuPasteArea.containsMouse ? Theme.surfaceVariant : Theme.withAlpha(Theme.surfaceVariant, 0)
color: saveMenuPasteArea.containsMouse ? Theme.surfaceVariant : "transparent"
opacity: modal.wtypeAvailable ? 1 : 0.5
Row {
@@ -110,7 +110,7 @@ Rectangle {
iconName: "push_pin"
iconSize: Theme.iconSize - 6
iconColor: (entry.pinned || hasPinnedDuplicate) ? Theme.primary : Theme.surfaceText
backgroundColor: (entry.pinned || hasPinnedDuplicate) ? Theme.primarySelected : Theme.withAlpha(Theme.primarySelected, 0)
backgroundColor: (entry.pinned || hasPinnedDuplicate) ? Theme.primarySelected : "transparent"
visible: root.showPinAction
onClicked: {
if (entry.pinned) {
@@ -228,9 +228,9 @@ Rectangle {
}
onClicked: {
if (SettingsData.clipboardClickToPaste) {
pasteRequested();
pasteRequested()
} else {
copyRequested();
copyRequested()
}
}
}
@@ -38,6 +38,7 @@ Item {
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
Row {
@@ -49,7 +50,7 @@ Item {
iconName: "push_pin"
iconSize: Theme.iconSize - 4
iconColor: header.activeTab === "saved" ? Theme.primary : Theme.surfaceText
backgroundColor: header.activeTab === "saved" ? Theme.primarySelected : Theme.withAlpha(Theme.primarySelected, 0)
backgroundColor: header.activeTab === "saved" ? Theme.primarySelected : "transparent"
visible: header.pinnedCount > 0 || header.activeTab === "saved"
tooltipText: header.activeTab === "saved" ? I18n.tr("Recent") : I18n.tr("Saved")
onClicked: tabChanged(header.activeTab === "saved" ? "recents" : "saved")
@@ -15,7 +15,7 @@ Rectangle {
height: ClipboardConstants.keyboardHintsHeight
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, 0.95)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
border.color: Theme.primary
border.width: 2
opacity: visible ? 1 : 0
@@ -24,7 +24,7 @@ Rectangle {
Column {
width: parent.width - Theme.spacingL * 2
anchors.centerIn: parent
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: keyboardHints.enterToPaste ? I18n.tr("↑/↓: Navigate • Enter: Paste • Ctrl+C: Copy • Del: Delete • Ctrl+E: Edit • Ctrl+S: Pin/Unpin • F10: Help", "Keyboard hints when enter-to-paste is enabled") : I18n.tr("↑/↓: Navigate • Enter/Ctrl+C: Copy • Del: Delete • Ctrl+E: Edit • Ctrl+S: Pin/Unpin • F10: Help")
+5 -5
View File
@@ -211,14 +211,14 @@ DankModal {
radius: Theme.cornerRadius
color: {
if (keyboardNavigation && selectedButton === 0) {
return Theme.primaryHover;
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
} else if (cancelButton.containsMouse) {
return Theme.surfacePressed;
} else {
return Theme.surfaceVariantAlpha;
}
}
border.color: (keyboardNavigation && selectedButton === 0) ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.color: (keyboardNavigation && selectedButton === 0) ? Theme.primary : "transparent"
border.width: (keyboardNavigation && selectedButton === 0) ? 1 : 0
StyledText {
@@ -249,14 +249,14 @@ DankModal {
color: {
const baseColor = confirmButtonColor;
if (keyboardNavigation && selectedButton === 1) {
return Theme.withAlpha(baseColor, 1);
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 1);
} else if (confirmButton.containsMouse) {
return Theme.withAlpha(baseColor, 0.9);
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9);
} else {
return baseColor;
}
}
border.color: (keyboardNavigation && selectedButton === 1) ? "white" : Qt.rgba(1, 1, 1, 0)
border.color: (keyboardNavigation && selectedButton === 1) ? "white" : "transparent"
border.width: (keyboardNavigation && selectedButton === 1) ? 1 : 0
StyledText {
+1 -4
View File
@@ -58,9 +58,6 @@ Item {
HyprlandFocusGrab {
windows: root.contentWindow ? [root.contentWindow] : []
active: KeyboardFocus.wantsGrab(root.shouldHaveFocus, root.customKeyboardFocus)
property var restoreToplevel: null
onActiveChanged: restoreToplevel = active ? KeyboardFocus.captureActiveToplevel() : KeyboardFocus.restoreToplevel(restoreToplevel)
}
readonly property var contentWindow: impl.item ? impl.item.contentWindow : null
readonly property var effectiveScreen: impl.item ? impl.item.effectiveScreen : null
@@ -93,7 +90,7 @@ Item {
impl.item.toggle();
}
readonly property var _desiredBackend: FrameTransitionState.effectiveConnectedFrameModeActive ? connectedComp : standaloneComp
readonly property var _desiredBackend: SettingsData.connectedFrameModeActive ? connectedComp : standaloneComp
property var _resolvedBackend: null
Component.onCompleted: _resolvedBackend = _desiredBackend
@@ -33,7 +33,7 @@ Item {
property string preferredConnectedBarSide: SettingsData.frameModalEmergeSide
readonly property bool frameConnectedMode: FrameTransitionState.effectiveFrameEnabled && Theme.isConnectedEffect && !!effectiveScreen && SettingsData.isScreenInPreferences(effectiveScreen, SettingsData.frameScreenPreferences)
readonly property bool frameConnectedMode: SettingsData.frameEnabled && Theme.isConnectedEffect && !!effectiveScreen && SettingsData.isScreenInPreferences(effectiveScreen, SettingsData.frameScreenPreferences)
readonly property string resolvedConnectedBarSide: frameConnectedMode ? preferredConnectedBarSide : ""
@@ -69,7 +69,7 @@ Item {
property real cornerRadius: Theme.cornerRadius
readonly property bool connectedSurfaceOverride: frameOwnsConnectedChrome
readonly property color effectiveBackgroundColor: connectedSurfaceOverride ? Theme.connectedSurfaceColor : backgroundColor
readonly property color effectiveBorderColor: connectedSurfaceOverride ? Theme.withAlpha(borderColor, 0) : borderColor
readonly property color effectiveBorderColor: connectedSurfaceOverride ? "transparent" : borderColor
readonly property real effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth
readonly property real effectiveCornerRadius: connectedSurfaceOverride ? Theme.connectedSurfaceRadius : cornerRadius
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
@@ -659,8 +659,8 @@ Item {
level: root.shadowLevel
fallbackOffset: root.shadowFallbackOffset
targetRadius: root.effectiveCornerRadius
targetColor: root.frameOwnsConnectedChrome ? Theme.withAlpha(root.effectiveBackgroundColor, 0) : root.effectiveBackgroundColor
borderColor: root.frameOwnsConnectedChrome ? Theme.withAlpha(root.effectiveBorderColor, 0) : root.effectiveBorderColor
targetColor: root.frameOwnsConnectedChrome ? "transparent" : root.effectiveBackgroundColor
borderColor: root.frameOwnsConnectedChrome ? "transparent" : root.effectiveBorderColor
borderWidth: root.frameOwnsConnectedChrome ? 0 : root.effectiveBorderWidth
shadowEnabled: !root.frameOwnsConnectedChrome && root.enableShadow && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
}
@@ -669,7 +669,7 @@ Item {
anchors.fill: parent
radius: root.effectiveCornerRadius
color: "transparent"
border.color: (root.connectedSurfaceOverride || root.frameOwnsConnectedChrome) ? Theme.withAlpha(BlurService.borderColor, 0) : BlurService.borderColor
border.color: (root.connectedSurfaceOverride || root.frameOwnsConnectedChrome) ? "transparent" : BlurService.borderColor
border.width: (root.connectedSurfaceOverride || root.frameOwnsConnectedChrome) ? 0 : BlurService.borderWidth
z: 100
}
@@ -53,7 +53,7 @@ Item {
readonly property alias contentWindow: contentWindow
readonly property alias clickCatcher: clickCatcher
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
readonly property bool useBackground: showBackground && !FrameTransitionState.effectiveFrameEnabled && SettingsData.modalDarkenBackground
readonly property bool useBackground: showBackground && !SettingsData.frameEnabled && SettingsData.modalDarkenBackground
readonly property bool useSingleWindow: CompositorService.isHyprland || useBackground
signal opened
+7 -7
View File
@@ -194,7 +194,7 @@ DankModal {
height: 40
radius: Theme.cornerRadius
color: Theme.surfaceVariantAlpha
border.color: textInput.activeFocus ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.color: textInput.activeFocus ? Theme.primary : "transparent"
border.width: textInput.activeFocus ? 1 : 0
TextInput {
@@ -216,7 +216,7 @@ DankModal {
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
font.pixelSize: Theme.fontSizeMedium
color: Theme.onSurface_38
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4)
text: root.inputPlaceholder
visible: textInput.text === "" && !textInput.activeFocus
}
@@ -238,14 +238,14 @@ DankModal {
radius: Theme.cornerRadius
color: {
if (root.keyboardNavigation && root.selectedButton === 0) {
return Theme.primaryHover;
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
} else if (cancelButton.containsMouse) {
return Theme.surfacePressed;
} else {
return Theme.surfaceVariantAlpha;
}
}
border.color: (root.keyboardNavigation && root.selectedButton === 0) ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.color: (root.keyboardNavigation && root.selectedButton === 0) ? Theme.primary : "transparent"
border.width: (root.keyboardNavigation && root.selectedButton === 0) ? 1 : 0
StyledText {
@@ -273,14 +273,14 @@ DankModal {
color: {
const baseColor = root.confirmButtonColor;
if (root.keyboardNavigation && root.selectedButton === 1) {
return Theme.withAlpha(baseColor, 1);
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 1);
} else if (confirmButton.containsMouse) {
return Theme.withAlpha(baseColor, 0.9);
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9);
} else {
return baseColor;
}
}
border.color: (root.keyboardNavigation && root.selectedButton === 1) ? "white" : Qt.rgba(1, 1, 1, 0)
border.color: (root.keyboardNavigation && root.selectedButton === 1) ? "white" : "transparent"
border.width: (root.keyboardNavigation && root.selectedButton === 1) ? 1 : 0
StyledText {
@@ -157,7 +157,7 @@ Rectangle {
width: actionContent.implicitWidth + Theme.spacingM * 2
height: actionsRow.height
radius: Theme.cornerRadius
color: index === root.selectedActionIndex ? Theme.primaryHover : actionArea.containsMouse ? Theme.surfaceHover : Theme.withAlpha(Theme.surfaceHover, 0)
color: index === root.selectedActionIndex ? Theme.primaryHover : actionArea.containsMouse ? Theme.surfaceHover : "transparent"
Row {
id: actionContent
@@ -43,7 +43,6 @@ Item {
signal queryChanged(string query)
signal viewModeChanged(string sectionId, string mode)
signal searchQueryRequested(string query)
signal sectionExpanded(string sectionId)
Ref {
service: AppSearchService
@@ -1875,9 +1874,6 @@ Item {
selectedFlatIndex = getFirstItemIndex();
}
updateSelectedItem();
if (!newCollapsed[sectionId])
sectionExpanded(sectionId);
}
function executeSelected() {
@@ -63,8 +63,8 @@ Item {
impl.item.toggleWithMode(mode);
}
readonly property bool useSpotlightBackend: !FrameTransitionState.effectiveConnectedFrameModeActive && SettingsData.launcherStyle === "spotlight"
readonly property var _desiredBackend: useSpotlightBackend ? spotlightComp : (FrameTransitionState.effectiveConnectedFrameModeActive ? connectedComp : standaloneComp)
readonly property bool useSpotlightBackend: !SettingsData.connectedFrameModeActive && SettingsData.launcherStyle === "spotlight"
readonly property var _desiredBackend: useSpotlightBackend ? spotlightComp : (SettingsData.connectedFrameModeActive ? connectedComp : standaloneComp)
property var _resolvedBackend: null
Component.onCompleted: _resolvedBackend = _desiredBackend
@@ -77,7 +77,7 @@ Item {
readonly property string preferredConnectedBarSide: SettingsData.frameLauncherEmergeSide
readonly property bool frameConnectedMode: FrameTransitionState.effectiveFrameEnabled && Theme.isConnectedEffect && !!effectiveScreen && SettingsData.isScreenInPreferences(effectiveScreen, SettingsData.frameScreenPreferences)
readonly property bool frameConnectedMode: SettingsData.frameEnabled && Theme.isConnectedEffect && !!effectiveScreen && SettingsData.isScreenInPreferences(effectiveScreen, SettingsData.frameScreenPreferences)
readonly property string resolvedConnectedBarSide: frameConnectedMode ? preferredConnectedBarSide : ""
@@ -168,7 +168,7 @@ Item {
}
}
readonly property int borderWidth: SettingsData.dankLauncherV2BorderEnabled ? SettingsData.dankLauncherV2BorderThickness : 0
readonly property color effectiveBorderColor: connectedSurfaceOverride ? Theme.withAlpha(borderColor, 0) : borderColor
readonly property color effectiveBorderColor: connectedSurfaceOverride ? "transparent" : borderColor
readonly property int effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
@@ -359,10 +359,8 @@ Item {
contentVisible = true;
spotlightContent.closeTransientUi?.();
const targetQuery = query || (SettingsData.rememberLastQuery ? (SessionData.launcherLastQuery || "") : "");
if (spotlightContent.searchField) {
spotlightContent.searchField.text = targetQuery;
spotlightContent.searchField.text = query;
}
if (spotlightContent.controller) {
var targetMode = mode || SessionData.getLauncherRestoreMode();
@@ -377,8 +375,8 @@ Item {
spotlightContent.controller.collapsedSections = {};
spotlightContent.controller.selectedFlatIndex = 0;
spotlightContent.controller.selectedItem = null;
if (targetQuery) {
spotlightContent.controller.setSearchQuery(targetQuery);
if (query) {
spotlightContent.controller.setSearchQuery(query);
} else {
spotlightContent.controller.searchQuery = "";
spotlightContent.controller.performSearch();
@@ -424,10 +422,8 @@ Item {
Qt.callLater(() => {
root.keyboardActive = true;
if (root.spotlightContent && root.spotlightContent.searchField) {
if (root.spotlightContent && root.spotlightContent.searchField)
root.spotlightContent.searchField.forceActiveFocus();
root.spotlightContent.searchField.selectAll();
}
});
});
}
@@ -534,9 +530,7 @@ Item {
HyprlandFocusGrab {
id: focusGrab
readonly property var contextMenuWindow: root.spotlightContent?.activeContextMenu?.contextWindow ?? null
readonly property bool contextMenuActive: root.spotlightContent?.activeContextMenu?.renderActive ?? false
windows: contextMenuActive && contextMenuWindow ? [contentWindow, contextMenuWindow] : [contentWindow]
windows: [contentWindow]
active: root.useHyprlandFocusGrab && root.spotlightOpen
onCleared: {
@@ -803,8 +797,8 @@ Item {
y: contentWrapper.y
level: root.shadowLevel
fallbackOffset: root.shadowFallbackOffset
targetColor: root.frameOwnsConnectedChrome ? Theme.withAlpha(root.backgroundColor, 0) : root.backgroundColor
borderColor: root.frameOwnsConnectedChrome ? Theme.withAlpha(root.effectiveBorderColor, 0) : root.effectiveBorderColor
targetColor: root.frameOwnsConnectedChrome ? "transparent" : root.backgroundColor
borderColor: root.frameOwnsConnectedChrome ? "transparent" : root.effectiveBorderColor
borderWidth: root.frameOwnsConnectedChrome ? 0 : root.effectiveBorderWidth
targetRadius: root.cornerRadius
shadowEnabled: !root.frameOwnsConnectedChrome && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
@@ -30,7 +30,7 @@ Item {
readonly property real screenWidth: effectiveScreen?.width ?? 1920
readonly property real screenHeight: effectiveScreen?.height ?? 1080
readonly property real dpr: effectiveScreen ? CompositorService.getScreenScale(effectiveScreen) : 1
readonly property bool useBackgroundDarken: !FrameTransitionState.effectiveFrameEnabled && SettingsData.modalDarkenBackground
readonly property bool useBackgroundDarken: !SettingsData.frameEnabled && SettingsData.modalDarkenBackground
readonly property bool usesOverlayLayer: useBackgroundDarken || SettingsData.launcherUseOverlayLayer || triggerUsesOverlayLayer
readonly property var effectiveLauncherLayer: LayerShell.fromEnv("DMS_MODAL_LAYER", root.usesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top, {
"allow": ["top", "overlay"],
@@ -155,7 +155,7 @@ Item {
}
if (spotlightContent.searchField) {
spotlightContent.searchField.forceActiveFocus();
spotlightContent.searchField.selectAll();
spotlightContent.searchField.cursorPosition = spotlightContent.searchField.text.length;
}
}
@@ -228,9 +228,7 @@ Item {
HyprlandFocusGrab {
id: focusGrab
readonly property var contextMenuWindow: root.spotlightContent?.activeContextMenu?.contextWindow ?? null
readonly property bool contextMenuActive: root.spotlightContent?.activeContextMenu?.renderActive ?? false
windows: contextMenuActive && contextMenuWindow ? [launcherWindow, contextMenuWindow] : [launcherWindow]
windows: [launcherWindow]
active: root.useHyprlandFocusGrab && root.keyboardActive
onCleared: {
if (spotlightOpen)
@@ -79,7 +79,7 @@ Item {
readonly property real windowHeight: alignedHeight + contentY + shadowPad
readonly property color backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
readonly property bool useBackgroundDarken: !FrameTransitionState.effectiveFrameEnabled && SettingsData.modalDarkenBackground
readonly property bool useBackgroundDarken: !SettingsData.frameEnabled && SettingsData.modalDarkenBackground
readonly property bool useSingleWindow: CompositorService.isHyprland || useBackgroundDarken
readonly property bool usesOverlayLayer: useBackgroundDarken || SettingsData.launcherUseOverlayLayer || triggerUsesOverlayLayer
readonly property var effectiveLauncherLayer: LayerShell.fromEnv("DMS_MODAL_LAYER", root.usesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top, {
@@ -139,7 +139,6 @@ Item {
if (spotlightContent.searchField) {
spotlightContent.searchField.text = targetQuery;
spotlightContent.searchField.selectAll();
}
if (spotlightContent.controller) {
var targetMode = mode || SessionData.getLauncherRestoreMode();
@@ -260,9 +259,7 @@ Item {
HyprlandFocusGrab {
id: focusGrab
readonly property var contextMenuWindow: root.spotlightContent?.activeContextMenu?.contextWindow ?? null
readonly property bool contextMenuActive: root.spotlightContent?.activeContextMenu?.renderActive ?? false
windows: contextMenuActive && contextMenuWindow ? [launcherWindow, contextMenuWindow] : [launcherWindow]
windows: [launcherWindow]
active: root.useHyprlandFocusGrab && root.keyboardActive
onCleared: {
@@ -36,7 +36,7 @@ Rectangle {
readonly property int computedIconSize: Math.min(48, Math.max(32, width * 0.45))
radius: Theme.cornerRadius
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryHoverLight, 0)
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : "transparent"
DankRipple {
id: rippleLayer
@@ -314,13 +314,9 @@ FocusScope {
}
Item {
id: contentHolder
anchors.fill: parent
visible: !editMode
readonly property bool inverted: (root.parentModal?.frameOwnsConnectedChrome ?? false) && (root.parentModal?.resolvedConnectedBarSide === "top")
readonly property bool _connectedArcAtHeader: inverted && !(root.parentModal?.launcherArcExtenderActive ?? false)
Item {
id: footerBar
readonly property bool _connectedBottomEmerge: (root.parentModal?.frameOwnsConnectedChrome ?? false) && (root.parentModal?.resolvedConnectedBarSide === "bottom")
@@ -329,10 +325,11 @@ FocusScope {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: root.parentModal?.borderWidth ?? 1
anchors.rightMargin: root.parentModal?.borderWidth ?? 1
y: contentHolder.inverted ? 0 : (parent.height - height - (_connectedBottomEmerge ? 0 : (root.parentModal?.borderWidth ?? 1)))
height: showFooter ? ((_connectedArcAtFooter || contentHolder._connectedArcAtHeader) ? 76 : 36) : 0
anchors.bottomMargin: _connectedBottomEmerge ? 0 : (root.parentModal?.borderWidth ?? 1)
height: showFooter ? (_connectedArcAtFooter ? 76 : 36) : 0
visible: showFooter
clip: true
@@ -351,7 +348,7 @@ FocusScope {
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
layoutDirection: I18n.isRtl ? Qt.RightToLeft : Qt.LeftToRight
spacing: Theme.spacingXXS
spacing: 2
Repeater {
model: [
@@ -384,7 +381,7 @@ FocusScope {
width: buttonContent.width + Theme.spacingM * 2
height: 28
radius: Theme.cornerRadius
color: controller.searchMode === modelData.id ? Theme.buttonBg : modeArea.containsMouse ? Theme.surfaceContainerHighest : Theme.withAlpha(Theme.surfaceContainerHighest, 0)
color: controller.searchMode === modelData.id ? Theme.buttonBg : modeArea.containsMouse ? Theme.surfaceContainerHighest : "transparent"
Row {
id: buttonContent
@@ -449,112 +446,105 @@ FocusScope {
}
}
Row {
id: searchRow
spacing: Theme.spacingS
Column {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: footerBar.top
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
y: contentHolder.inverted ? (parent.height - height - Theme.spacingM) : Theme.spacingM
Rectangle {
id: pluginBadge
visible: controller.activePluginName.length > 0
width: visible ? pluginBadgeContent.implicitWidth + Theme.spacingM : 0
height: searchField.height
radius: 16
color: Theme.primary
Row {
id: pluginBadgeContent
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
anchors.verticalCenter: parent.verticalCenter
name: "extension"
size: 14
color: Theme.primaryText
}
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: controller.activePluginName
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.primaryText
}
}
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
DankTextField {
id: searchField
width: parent.width - (pluginBadge.visible ? pluginBadge.width + Theme.spacingS : 0)
cornerRadius: Theme.cornerRadius
backgroundColor: root._launcherSearchFieldColor
normalBorderColor: root._launcherSearchBorderColor
focusedBorderColor: root._launcherSearchFocusedBorderColor
borderWidth: 1
focusedBorderWidth: 2
leftIconName: controller.activePluginId ? "extension" : controller.searchQuery.startsWith("/") ? "folder" : "search"
leftIconSize: Theme.iconSize
leftIconColor: Theme.surfaceVariantText
leftIconFocusedColor: Theme.primary
showClearButton: true
textColor: Theme.surfaceText
font.pixelSize: Theme.fontSizeLarge
enabled: root.parentModal ? (root.parentModal.spotlightOpen || root.parentModal.isClosing) : true
placeholderText: ""
ignoreUpDownKeys: true
ignoreTabKeys: true
keyForwardTargets: [root]
onTextChanged: {
controller.setSearchQuery(text);
if (actionPanel.expanded) {
actionPanel.hide();
}
}
Keys.onPressed: event => {
if (event.key === Qt.Key_Escape) {
if (root.parentModal) {
root.parentModal.hide();
}
event.accepted = true;
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter)) {
if (actionPanel.expanded && actionPanel.selectedActionIndex > 0) {
actionPanel.executeSelectedAction();
} else {
controller.executeSelected();
}
event.accepted = true;
}
}
}
}
Item {
id: contentStack
anchors.left: parent.left
anchors.right: parent.right
anchors.top: contentHolder.inverted ? footerBar.bottom : searchRow.bottom
anchors.bottom: contentHolder.inverted ? searchRow.top : footerBar.top
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
anchors.topMargin: contentHolder.inverted && !footerBar.showFooter ? Theme.spacingM : contentStack.gap
anchors.bottomMargin: contentHolder.inverted ? contentStack.gap : 0
readonly property real gap: Theme.spacingXS
anchors.topMargin: Theme.spacingM
spacing: Theme.spacingXS
clip: false
Row {
width: parent.width
spacing: Theme.spacingS
Rectangle {
id: pluginBadge
visible: controller.activePluginName.length > 0
width: visible ? pluginBadgeContent.implicitWidth + Theme.spacingM : 0
height: searchField.height
radius: 16
color: Theme.primary
Row {
id: pluginBadgeContent
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
anchors.verticalCenter: parent.verticalCenter
name: "extension"
size: 14
color: Theme.primaryText
}
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: controller.activePluginName
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.primaryText
}
}
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
DankTextField {
id: searchField
width: parent.width - (pluginBadge.visible ? pluginBadge.width + Theme.spacingS : 0)
cornerRadius: Theme.cornerRadius
backgroundColor: root._launcherSearchFieldColor
normalBorderColor: root._launcherSearchBorderColor
focusedBorderColor: root._launcherSearchFocusedBorderColor
borderWidth: 1
focusedBorderWidth: 2
leftIconName: controller.activePluginId ? "extension" : controller.searchQuery.startsWith("/") ? "folder" : "search"
leftIconSize: Theme.iconSize
leftIconColor: Theme.surfaceVariantText
leftIconFocusedColor: Theme.primary
showClearButton: true
textColor: Theme.surfaceText
font.pixelSize: Theme.fontSizeLarge
enabled: root.parentModal ? (root.parentModal.spotlightOpen || root.parentModal.isClosing) : true
placeholderText: ""
ignoreUpDownKeys: true
ignoreTabKeys: true
keyForwardTargets: [root]
onTextChanged: {
controller.setSearchQuery(text);
if (actionPanel.expanded) {
actionPanel.hide();
}
}
Keys.onPressed: event => {
if (event.key === Qt.Key_Escape) {
if (root.parentModal) {
root.parentModal.hide();
}
event.accepted = true;
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter)) {
if (actionPanel.expanded && actionPanel.selectedActionIndex > 0) {
actionPanel.executeSelectedAction();
} else {
controller.executeSelected();
}
event.accepted = true;
}
}
}
}
Row {
id: categoryRow
width: parent.width
@@ -562,8 +552,6 @@ FocusScope {
height: showPluginCategories ? 36 : 0
visible: showPluginCategories
spacing: Theme.spacingS
anchors.top: parent.top
anchors.topMargin: 0
clip: true
@@ -619,8 +607,6 @@ FocusScope {
width: parent.width
height: showFileFilters ? fileFilterContent.height : 0
visible: showFileFilters
anchors.top: parent.top
anchors.topMargin: 0
readonly property bool showFileFilters: controller.searchMode === "files"
@@ -639,7 +625,7 @@ FocusScope {
Row {
id: typeChips
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
visible: DSearchService.supportsTypeFilter
Repeater {
@@ -668,7 +654,7 @@ FocusScope {
width: chipContent.width + Theme.spacingM * 2
height: sortDropdown.height
radius: Theme.cornerRadius
color: controller.fileSearchType === modelData.id ? Theme.buttonBg : chipArea.containsMouse ? Theme.surfaceContainerHighest : Theme.withAlpha(Theme.surfaceContainerHighest, 0)
color: controller.fileSearchType === modelData.id ? Theme.buttonBg : chipArea.containsMouse ? Theme.surfaceContainerHighest : "transparent"
Row {
id: chipContent
@@ -760,12 +746,8 @@ FocusScope {
}
Item {
id: resultsSlot
width: parent.width
anchors.top: fileFilterRow.visible ? fileFilterRow.bottom : (categoryRow.visible ? categoryRow.bottom : parent.top)
anchors.topMargin: (fileFilterRow.visible || categoryRow.visible) ? contentStack.gap : 0
anchors.bottom: actionPanel.top
anchors.bottomMargin: actionPanel.height > 0 || !contentHolder.inverted ? contentStack.gap : 0
height: parent.height - searchField.height - categoryRow.height - fileFilterRow.height - actionPanel.height - Theme.spacingXS * ((categoryRow.visible ? 1 : 0) + (fileFilterRow.visible ? 1 : 0) + 2)
opacity: {
if (!root.parentModal)
return 1;
@@ -778,7 +760,6 @@ FocusScope {
id: resultsList
anchors.fill: parent
controller: root.controller
leadingSectionHeaderAtBottom: contentHolder.inverted
onItemRightClicked: (index, item, sceneX, sceneY) => {
if (item && contextMenu.hasContextMenuActions(item)) {
@@ -792,7 +773,6 @@ FocusScope {
ActionPanel {
id: actionPanel
width: parent.width
anchors.bottom: parent.bottom
selectedItem: controller.selectedItem
controller: controller
}
@@ -848,7 +828,7 @@ FocusScope {
width: 40
height: 40
radius: Theme.cornerRadius
color: backButtonArea.containsMouse ? Theme.surfaceHover : Theme.withAlpha(Theme.surfaceHover, 0)
color: backButtonArea.containsMouse ? Theme.surfaceHover : "transparent"
DankIcon {
anchors.centerIn: parent
@@ -878,7 +858,7 @@ FocusScope {
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: I18n.tr("Edit App")
@@ -915,7 +895,7 @@ FocusScope {
Column {
width: parent.width
spacing: Theme.spacingXS
spacing: 4
StyledText {
text: I18n.tr("Name")
@@ -935,7 +915,7 @@ FocusScope {
Column {
width: parent.width
spacing: Theme.spacingXS
spacing: 4
StyledText {
text: I18n.tr("Icon")
@@ -955,7 +935,7 @@ FocusScope {
Column {
width: parent.width
spacing: Theme.spacingXS
spacing: 4
StyledText {
text: I18n.tr("Description")
@@ -975,7 +955,7 @@ FocusScope {
Column {
width: parent.width
spacing: Theme.spacingXS
spacing: 4
StyledText {
text: I18n.tr("Environment Variables")
@@ -1001,7 +981,7 @@ FocusScope {
Column {
width: parent.width
spacing: Theme.spacingXS
spacing: 4
StyledText {
text: I18n.tr("Extra Arguments")
@@ -1080,7 +1060,7 @@ FocusScope {
width: 90
height: 40
radius: Theme.cornerRadius
color: saveButtonArea.containsMouse ? Theme.withAlpha(Theme.primary, 0.9) : Theme.primary
color: saveButtonArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.9) : Theme.primary
StyledText {
text: I18n.tr("Save")
@@ -25,7 +25,6 @@ Item {
property real anchorY: 0
property bool openState: false
property bool renderActive: false
readonly property alias contextWindow: menuWindow
readonly property bool blurActive: renderActive && openState && BlurService.enabled && Theme.connectedSurfaceBlurEnabled
readonly property real minMenuWidth: 180
@@ -447,17 +446,7 @@ Item {
WlrLayershell.namespace: "dms:launcher-context-menu"
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
// Hyprland steals the launcher's focus grab on exclusive focus; keep keys on the
// launcher window, which forwards them to the menu via handleKey().
WlrLayershell.keyboardFocus: {
if (PopoutManager.screenshotActive)
return WlrKeyboardFocus.None;
if (!root.renderActive)
return WlrKeyboardFocus.None;
if (CompositorService.useHyprlandFocusGrab)
return WlrKeyboardFocus.None;
return WlrKeyboardFocus.Exclusive;
}
WlrLayershell.keyboardFocus: PopoutManager.screenshotActive ? WlrKeyboardFocus.None : (root.renderActive ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None)
anchors {
top: true
@@ -518,7 +507,7 @@ Item {
height: root.effectiveMenuHeight
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
radius: Theme.cornerRadius
border.color: BlurService.enabled ? BlurService.borderColor : Theme.outlineMedium
border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: BlurService.enabled ? BlurService.borderWidth : 1
opacity: root.openState ? 1 : 0
@@ -596,7 +585,7 @@ Item {
anchors.centerIn: parent
width: parent.width
height: 1
color: Theme.outlineHeavy
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
}
}
@@ -607,9 +596,9 @@ Item {
radius: Theme.cornerRadius
color: {
if (root.keyboardNavigation && root.selectedMenuIndex === menuItemDelegate.itemIndex) {
return Theme.primaryPressed;
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2);
}
return itemMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(BlurService.hoverColor(Theme.widgetBaseHoverColor), 0);
return itemMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent";
}
Row {
@@ -37,7 +37,7 @@ Rectangle {
width: parent?.width ?? 200
height: 52
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryHoverLight, 0)
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : "transparent"
radius: Theme.cornerRadius
DankRipple {
@@ -170,7 +170,7 @@ Rectangle {
height: 28
radius: 14
anchors.verticalCenter: parent.verticalCenter
color: allModeToggleArea.containsMouse ? Theme.surfaceHover : Theme.withAlpha(Theme.surfaceHover, 0)
color: allModeToggleArea.containsMouse ? Theme.surfaceHover : "transparent"
property bool isAllowed: {
if (root.item?.type !== "plugin_browse")
@@ -11,11 +11,9 @@ Item {
property var controller: null
property int gridColumns: controller?.gridColumns ?? 4
property bool leadingSectionHeaderAtBottom: false
property var _visualRows: []
property var _flatIndexToRowMap: ({})
property var _cumulativeHeights: []
readonly property bool _bottomSectionHeaderActive: leadingSectionHeaderAtBottom && (controller?.sections?.length ?? 0) > 0
signal itemRightClicked(int index, var item, real mouseX, real mouseY)
@@ -30,17 +28,15 @@ Item {
var section = sections[s];
var sectionId = section.id;
if (!root._bottomSectionHeaderActive || s > 0) {
cumHeights.push(cumY);
rows.push({
_rowId: "h_" + sectionId,
type: "header",
section: section,
sectionId: sectionId,
height: 32
});
cumY += 32;
}
cumHeights.push(cumY);
rows.push({
_rowId: "h_" + sectionId,
type: "header",
section: section,
sectionId: sectionId,
height: 32
});
cumY += 32;
if (section.collapsed)
continue;
@@ -107,7 +103,6 @@ Item {
onGridColumnsChanged: Qt.callLater(_rebuildVisualModel)
onWidthChanged: Qt.callLater(_rebuildVisualModel)
onLeadingSectionHeaderAtBottomChanged: Qt.callLater(_rebuildVisualModel)
Connections {
target: root.controller
@@ -122,27 +117,12 @@ Item {
root._cumulativeHeights = [];
root._flatIndexToRowMap = {};
}
function onSectionExpanded(sectionId) {
Qt.callLater(() => {
root._rebuildVisualModel();
Qt.callLater(() => root.revealExpandedSection(sectionId));
});
}
}
function resetScroll() {
mainListView.contentY = mainListView.originY;
}
function revealExpandedSection(sectionId) {
for (var i = 0; i < _visualRows.length; i++) {
if (_visualRows[i].sectionId === sectionId) {
mainListView.positionViewAtIndex(i, ListView.Beginning);
return;
}
}
}
function ensureVisible(index) {
if (index < 0 || !controller?.flatModel || index >= controller.flatModel.length)
return;
@@ -214,7 +194,6 @@ Item {
id: listClip
anchors.fill: parent
anchors.topMargin: BlurService.enabled && stickyHeader.visible ? 32 : 0
anchors.bottomMargin: bottomSectionHeader.visible ? bottomSectionHeader.height : 0
clip: true
DankListView {
@@ -353,7 +332,6 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: bottomSectionHeader.visible ? bottomSectionHeader.height : 0
height: 24
z: 100
visible: {
@@ -399,7 +377,7 @@ Item {
height: 32
z: 101
color: Theme.floatingSurface
visible: !root._bottomSectionHeaderActive && stickyHeaderSection !== null
visible: stickyHeaderSection !== null
readonly property int versionTrigger: root.controller?.viewModeVersion ?? 0
@@ -450,30 +428,6 @@ Item {
}
}
SectionHeader {
id: bottomSectionHeader
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
z: 101
visible: root._bottomSectionHeaderActive
section: visible ? root.controller.sections[0] : null
controller: root.controller
viewMode: {
var vt = root.controller?.viewModeVersion ?? 0;
void (vt);
return root.controller?.getSectionViewMode(section?.id ?? "") ?? "list";
}
canChangeViewMode: {
var vt = root.controller?.viewModeVersion ?? 0;
void (vt);
return root.controller?.canChangeSectionViewMode(section?.id ?? "") ?? false;
}
canCollapse: root.controller?.canCollapseSection(section?.id ?? "") ?? false
isSticky: true
popupAbove: true
}
Item {
anchors.centerIn: parent
visible: (!root.controller?.sections || root.controller.sections.length === 0) && !root.controller?.isFileSearching
+2 -2
View File
@@ -50,7 +50,7 @@ Item {
id: listComponent
Column {
spacing: Theme.spacingXS
spacing: 4
width: contentLoader.width
Repeater {
@@ -83,7 +83,7 @@ Item {
Flow {
width: contentLoader.width
spacing: Theme.spacingXS
spacing: 4
Repeater {
model: ScriptModel {
@@ -15,14 +15,12 @@ Rectangle {
property bool canChangeViewMode: true
property bool canCollapse: true
property bool isSticky: false
property bool popupAbove: false
property Item popupAboveItem: null
signal viewModeToggled
width: parent?.width ?? 200
height: 32
color: isSticky ? Theme.withAlpha(Theme.surfaceHover, 0) : (hoverArea.containsMouse ? Theme.surfaceHover : Theme.withAlpha(Theme.surfaceHover, 0))
color: isSticky ? "transparent" : (hoverArea.containsMouse ? Theme.surfaceHover : "transparent")
radius: Theme.cornerRadius
MouseArea {
@@ -83,7 +81,7 @@ Rectangle {
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: chipArea.containsMouse || categoryPopup.visible ? Theme.surfaceContainerHigh : Theme.withAlpha(Theme.surfaceContainerHigh, 0)
color: chipArea.containsMouse || categoryPopup.visible ? Theme.surfaceContainerHigh : "transparent"
border.color: categoryPopup.visible ? Theme.primary : Theme.outlineMedium
border.width: categoryPopup.visible ? 2 : 1
}
@@ -124,10 +122,9 @@ Rectangle {
if (categoryPopup.visible) {
categoryPopup.close();
} else {
const chipPos = categoryChip.mapToItem(Overlay.overlay, 0, 0);
const abovePos = (root.popupAboveItem ?? categoryChip).mapToItem(Overlay.overlay, 0, 0);
categoryPopup.x = chipPos.x;
categoryPopup.y = root.popupAbove ? abovePos.y - categoryPopup.height - 4 : chipPos.y + categoryChip.height + 4;
const pos = categoryChip.mapToItem(Overlay.overlay, 0, 0);
categoryPopup.x = pos.x;
categoryPopup.y = pos.y + categoryChip.height + 4;
categoryPopup.open();
}
}
@@ -148,7 +145,7 @@ Rectangle {
contentItem: Rectangle {
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, 1)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 1)
border.color: Theme.primary
border.width: 2
@@ -169,7 +166,7 @@ Rectangle {
anchors.fill: parent
anchors.margins: Theme.spacingS
model: root.controller?.appCategories ?? []
spacing: Theme.spacingXXS
spacing: 2
clip: true
interactive: contentHeight > height
implicitHeight: contentHeight
@@ -182,7 +179,7 @@ Rectangle {
height: 32
radius: Theme.cornerRadius
readonly property bool isCurrent: categoryChip.currentCategory === modelData
color: isCurrent ? Theme.primaryHover : catArea.containsMouse ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryHoverLight, 0)
color: isCurrent ? Theme.primaryHover : catArea.containsMouse ? Theme.primaryHoverLight : "transparent"
Row {
anchors.left: parent.left
@@ -246,7 +243,7 @@ Rectangle {
Row {
id: viewModeRow
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
visible: root.canChangeViewMode && !root.section?.collapsed
Repeater {
@@ -272,7 +269,7 @@ Rectangle {
width: 20
height: 20
radius: 4
color: root.viewMode === modelData.mode ? Theme.primaryHover : modeArea.containsMouse ? Theme.surfaceHover : Theme.withAlpha(Theme.surfaceHover, 0)
color: root.viewMode === modelData.mode ? Theme.primaryHover : modeArea.containsMouse ? Theme.surfaceHover : "transparent"
DankIcon {
anchors.centerIn: parent
@@ -328,7 +325,7 @@ Rectangle {
anchors.fill: parent
anchors.rightMargin: rightContent.width + Theme.spacingS
cursorShape: root.canCollapse ? Qt.PointingHandCursor : Qt.ArrowCursor
enabled: root.canCollapse && !leftContent.hasAppCategories
enabled: root.canCollapse
onClicked: {
if (root.canCollapse && root.controller && root.section) {
root.controller.toggleSection(root.section.id);
@@ -76,7 +76,7 @@ Rectangle {
width: parent?.width ?? 200
height: 64
radius: Theme.cornerRadius
color: root.isSelected ? Theme.primaryPressed : root.isHovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryHoverLight, 0)
color: root.isSelected ? Theme.primaryPressed : root.isHovered ? Theme.primaryHoverLight : "transparent"
Behavior on color {
ColorAnimation {
@@ -150,7 +150,7 @@ Rectangle {
anchors.right: previewFrame.visible ? previewFrame.left : metaRow.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
StyledText {
id: nameText
@@ -225,7 +225,7 @@ Rectangle {
height: 28
radius: height / 2
anchors.verticalCenter: parent.verticalCenter
color: quickToggleArea.containsMouse ? Theme.surfaceHover : Theme.withAlpha(Theme.surfaceHover, 0)
color: quickToggleArea.containsMouse ? Theme.surfaceHover : "transparent"
readonly property bool isAllowed: {
if (root.item?.type !== "plugin_browse")
@@ -143,7 +143,7 @@ Item {
Column {
anchors.verticalCenter: parent.verticalCenter
width: Math.min(420, root.width - 88)
spacing: Theme.spacingXXS
spacing: 2
StyledText {
width: parent.width
@@ -19,7 +19,7 @@ Rectangle {
signal rightClicked(real mouseX, real mouseY)
radius: Theme.cornerRadius
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryPressed : Theme.withAlpha(Theme.primaryPressed, 0)
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryPressed : "transparent"
border.width: isSelected ? 2 : 0
border.color: Theme.primary
@@ -93,7 +93,7 @@ Rectangle {
Rectangle {
anchors.fill: parent
color: root.isHovered ? Theme.withAlpha(Theme.surfaceVariant, 0.2) : Theme.withAlpha(Theme.surfaceVariant, 0)
color: root.isHovered ? Theme.withAlpha(Theme.surfaceVariant, 0.2) : "transparent"
}
}
@@ -155,7 +155,7 @@ Rectangle {
width: root.hasScreencopy ? 28 : 40
height: root.hasScreencopy ? 28 : 16
radius: root.hasScreencopy ? 14 : 4
color: root.hasScreencopy ? Theme.surfaceContainer : Theme.withAlpha(Theme.surfaceContainer, 0)
color: root.hasScreencopy ? Theme.surfaceContainer : "transparent"
visible: attributionImage.status === Image.Ready
opacity: 0.95
@@ -129,7 +129,7 @@ DankModal {
width: Math.max(70, revertText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: revertArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0)
color: revertArea.containsMouse ? Theme.surfaceTextHover : "transparent"
border.color: Theme.surfaceVariantAlpha
border.width: 1
@@ -804,7 +804,7 @@ FocusScope {
anchors.topMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
visible: viewMode === "list"
spacing: Theme.spacingXXS
spacing: 2
model: folderModel
currentIndex: selectedIndex
onCurrentIndexChanged: {
@@ -136,9 +136,9 @@ StyledRect {
if (keyboardNavigationActive && delegateRoot.index === selectedIndex)
return Theme.surfacePressed;
return mouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : Theme.withAlpha(Theme.surfaceContainerHigh, 0);
return mouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : "transparent";
}
border.color: keyboardNavigationActive && delegateRoot.index === selectedIndex ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.color: keyboardNavigationActive && delegateRoot.index === selectedIndex ? Theme.primary : "transparent"
border.width: (keyboardNavigationActive && delegateRoot.index === selectedIndex) ? 2 : 0
Component.onCompleted: {
@@ -89,7 +89,7 @@ Popup {
contentItem: Rectangle {
color: Theme.floatingSurface
radius: Theme.cornerRadius
border.color: BlurService.enabled ? BlurService.borderColor : Theme.outlineMedium
border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: BlurService.enabled ? BlurService.borderWidth : 1
Column {
@@ -110,7 +110,7 @@ Popup {
if (!modelData.enabled || !area.containsMouse)
return "transparent";
if (modelData.dangerous)
return Theme.errorHover;
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12);
return BlurService.hoverColor(Theme.widgetBaseHoverColor);
}
@@ -139,9 +139,9 @@ StyledRect {
color: {
if (keyboardNavigationActive && listDelegateRoot.index === selectedIndex)
return Theme.surfacePressed;
return listMouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : Theme.withAlpha(Theme.surfaceContainerHigh, 0);
return listMouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : "transparent";
}
border.color: keyboardNavigationActive && listDelegateRoot.index === selectedIndex ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.color: keyboardNavigationActive && listDelegateRoot.index === selectedIndex ? Theme.primary : "transparent"
border.width: (keyboardNavigationActive && listDelegateRoot.index === selectedIndex) ? 2 : 0
Component.onCompleted: {
@@ -41,8 +41,6 @@ FloatingWindow {
color: Theme.surfaceContainer
visible: false
onClosed: close()
onVisibleChanged: {
if (visible) {
if (parentModal && "shouldHaveFocus" in parentModal) {
@@ -26,7 +26,7 @@ Row {
width: 32
height: 32
radius: Theme.cornerRadius
color: (backButtonMouseArea.containsMouse || (backButtonFocused && keyboardNavigationActive)) && currentPath !== homeDir ? Theme.surfaceVariant : Theme.withAlpha(Theme.surfaceVariant, 0)
color: (backButtonMouseArea.containsMouse || (backButtonFocused && keyboardNavigationActive)) && currentPath !== homeDir ? Theme.surfaceVariant : "transparent"
opacity: currentPath !== homeDir ? 1 : 0
anchors.verticalCenter: parent.verticalCenter
@@ -56,8 +56,8 @@ Row {
StyledRect {
anchors.fill: parent
radius: Theme.cornerRadius
color: pathEditMode ? Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) : Theme.withAlpha(Theme.surfaceContainer, 0)
border.color: pathEditMode ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
color: pathEditMode ? Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) : "transparent"
border.color: pathEditMode ? Theme.primary : "transparent"
border.width: pathEditMode ? 1 : 0
visible: !pathEditMode
@@ -16,7 +16,7 @@ StyledRect {
Column {
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: Theme.spacingXS
spacing: 4
StyledText {
text: I18n.tr("Quick Access")
@@ -34,7 +34,7 @@ StyledRect {
width: parent?.width ?? 0
height: 38
radius: Theme.cornerRadius
color: quickAccessMouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : (currentPath === modelData?.path ? Theme.surfacePressed : Theme.withAlpha(Theme.surfacePressed, 0))
color: quickAccessMouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : (currentPath === modelData?.path ? Theme.surfacePressed : "transparent")
Row {
anchors.fill: parent
@@ -60,7 +60,7 @@ StyledRect {
width: sortColumn?.width ?? 0
height: 32
radius: Theme.cornerRadius
color: sortMouseArea.containsMouse ? Theme.surfaceVariant : (sortBy === modelData?.value ? Theme.surfacePressed : Theme.withAlpha(Theme.surfacePressed, 0))
color: sortMouseArea.containsMouse ? Theme.surfaceVariant : (sortBy === modelData?.value ? Theme.surfacePressed : "transparent")
Row {
anchors.fill: parent
@@ -114,7 +114,7 @@ StyledRect {
width: sortColumn?.width ?? 0
height: 32
radius: Theme.cornerRadius
color: ascMouseArea.containsMouse ? Theme.surfaceVariant : (sortAscending ? Theme.surfacePressed : Theme.withAlpha(Theme.surfacePressed, 0))
color: ascMouseArea.containsMouse ? Theme.surfaceVariant : (sortAscending ? Theme.surfacePressed : "transparent")
Row {
anchors.fill: parent
@@ -152,7 +152,7 @@ StyledRect {
width: sortColumn?.width ?? 0
height: 32
radius: Theme.cornerRadius
color: descMouseArea.containsMouse ? Theme.surfaceVariant : (!sortAscending ? Theme.surfacePressed : Theme.withAlpha(Theme.surfacePressed, 0))
color: descMouseArea.containsMouse ? Theme.surfaceVariant : (!sortAscending ? Theme.surfacePressed : "transparent")
Row {
anchors.fill: parent
+1 -1
View File
@@ -13,7 +13,7 @@ Rectangle {
height: 200
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, 0.95)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
border.color: Theme.secondary
border.width: 2
opacity: showFileInfo ? 1 : 0
@@ -9,7 +9,7 @@ Rectangle {
height: 80
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, 0.95)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
border.color: Theme.primary
border.width: 2
opacity: showHints ? 1 : 0
@@ -20,7 +20,7 @@ Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: Theme.spacingS
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: I18n.tr("Tab/Shift+Tab: Nav • ←→↑↓: Grid Nav • Enter/Space: Select")
@@ -99,7 +99,7 @@ Item {
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: I18n.tr("You're All Set!", "greeter completion page title")
@@ -209,7 +209,7 @@ Item {
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: I18n.tr("System Check", "greeter doctor page title")
@@ -45,7 +45,7 @@ Rectangle {
Column {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: root.title
@@ -93,8 +93,6 @@ FloatingWindow {
color: Theme.surfaceContainer
visible: false
onClosed: visible = false
Process {
id: cheatsheetProcess
running: false
@@ -48,7 +48,7 @@ Rectangle {
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
width: parent.width - root.iconContainerSize - Theme.spacingM
StyledText {
@@ -18,7 +18,7 @@ Rectangle {
radius: Theme.cornerRadius
color: bgColor
border.width: selected ? 2 : 0
border.color: selected ? iconColor : Theme.withAlpha(iconColor, 0)
border.color: selected ? iconColor : "transparent"
scale: mouseArea.pressed ? 0.97 : 1
Behavior on scale {
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
@@ -147,7 +148,8 @@ Item {
iconName: "tune"
title: I18n.tr("Control Center", "greeter feature card title")
description: I18n.tr("Quick system toggles", "greeter feature card description")
onClicked: BarWidgetService.getBarWindowOnFocusedScreen()?.triggerControlCenter()
// This is doing an IPC since its just easier and lazier to access the bar ref
onClicked: Quickshell.execDetached(["dms", "ipc", "call", "control-center", "open"])
}
GreeterFeatureCard {
+6 -6
View File
@@ -361,7 +361,7 @@ DankModal {
Column {
Layout.fillWidth: true
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: I18n.tr("New Session")
@@ -414,7 +414,7 @@ DankModal {
width: parent.width
height: 64
radius: Theme.cornerRadius
color: muxModal.selectedIndex === index ? Theme.primaryContainer : (sessionMouse.containsMouse ? Theme.surfaceContainerHigh : Theme.withAlpha(Theme.surfaceContainerHigh, 0))
color: muxModal.selectedIndex === index ? Theme.primaryContainer : (sessionMouse.containsMouse ? Theme.surfaceContainerHigh : "transparent")
MouseArea {
id: sessionMouse
@@ -449,7 +449,7 @@ DankModal {
// Info
Column {
Layout.fillWidth: true
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: modelData.name
@@ -478,7 +478,7 @@ DankModal {
Layout.preferredHeight: 36
radius: 18
visible: MuxService.supportsRename
color: renameMouse.containsMouse ? Theme.surfaceContainerHighest : Theme.withAlpha(Theme.surfaceContainerHighest, 0)
color: renameMouse.containsMouse ? Theme.surfaceContainerHighest : "transparent"
DankIcon {
anchors.centerIn: parent
@@ -501,7 +501,7 @@ DankModal {
Layout.preferredWidth: 36
Layout.preferredHeight: 36
radius: 18
color: deleteMouse.containsMouse ? Theme.errorContainer : Theme.withAlpha(Theme.errorContainer, 0)
color: deleteMouse.containsMouse ? Theme.errorContainer : "transparent"
DankIcon {
anchors.centerIn: parent
@@ -601,7 +601,7 @@ DankModal {
delegate: Row {
required property var modelData
spacing: Theme.spacingXS
spacing: 4
Rectangle {
width: keyText.width + Theme.spacingS
+1 -1
View File
@@ -314,7 +314,7 @@ FocusScope {
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: cancelArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0)
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
border.color: Theme.surfaceVariantAlpha
border.width: 1
enabled: !root.isLoading
-2
View File
@@ -32,8 +32,6 @@ FloatingWindow {
color: Theme.surfaceContainer
visible: false
onClosed: hide()
onVisibleChanged: {
if (visible) {
Qt.callLater(focusContent);
+20 -20
View File
@@ -551,12 +551,12 @@ DankModal {
radius: Theme.cornerRadius
color: {
if (isSelected)
return Theme.primaryHover;
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
if (mouseArea.containsMouse)
return Theme.primaryHoverLight;
return Theme.surfaceHover;
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);
}
border.color: isSelected ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.color: isSelected ? Theme.primary : "transparent"
border.width: isSelected ? 2 : 0
Rectangle {
@@ -585,10 +585,10 @@ DankModal {
width: parent.width * root.holdProgress
color: {
if (gridButtonRect.modelData === "poweroff")
return Theme.errorSelected;
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3);
if (gridButtonRect.modelData === "reboot")
return Theme.withAlpha(Theme.warning, 0.3);
return Theme.primarySelected;
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.3);
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3);
}
}
}
@@ -626,13 +626,13 @@ DankModal {
width: 20
height: 16
radius: 4
color: Theme.onSurface_12
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.1)
anchors.horizontalCenter: parent.horizontalCenter
StyledText {
text: gridButtonRect.actionData.key
font.pixelSize: Theme.fontSizeSmall - 1
color: Theme.surfaceTextSecondary
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
font.weight: Font.Medium
anchors.centerIn: parent
}
@@ -688,12 +688,12 @@ DankModal {
radius: Theme.cornerRadius
color: {
if (isSelected)
return Theme.primaryHover;
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
if (listMouseArea.containsMouse)
return Theme.primaryHoverLight;
return Theme.surfaceHover;
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);
}
border.color: isSelected ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.color: isSelected ? Theme.primary : "transparent"
border.width: isSelected ? 2 : 0
Rectangle {
@@ -722,10 +722,10 @@ DankModal {
width: parent.width * root.holdProgress
color: {
if (listButtonRect.modelData === "poweroff")
return Theme.errorSelected;
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3);
if (listButtonRect.modelData === "reboot")
return Theme.withAlpha(Theme.warning, 0.3);
return Theme.primarySelected;
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.3);
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3);
}
}
}
@@ -770,7 +770,7 @@ DankModal {
width: 28
height: 20
radius: 4
color: Theme.onSurface_12
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.1)
anchors {
right: parent.right
rightMargin: Theme.spacingM
@@ -780,7 +780,7 @@ DankModal {
StyledText {
text: listButtonRect.actionData.key
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextSecondary
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
font.weight: Font.Medium
anchors.centerIn: parent
}
@@ -820,7 +820,7 @@ DankModal {
DankIcon {
name: root.showHoldHint ? "warning" : "touch_app"
size: Theme.fontSizeSmall
color: root.showHoldHint ? Theme.warning : Theme.surfaceTextSecondary
color: root.showHoldHint ? Theme.warning : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
anchors.verticalCenter: parent.verticalCenter
}
@@ -840,7 +840,7 @@ DankModal {
return I18n.tr("Hold to confirm (%1s)").arg(SettingsData.powerActionHoldDuration);
}
font.pixelSize: Theme.fontSizeSmall
color: root.showHoldHint ? Theme.warning : Theme.surfaceTextSecondary
color: root.showHoldHint ? Theme.warning : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
anchors.verticalCenter: parent.verticalCenter
}
}
+7 -7
View File
@@ -174,15 +174,15 @@ DankModal {
color: {
if (isActive)
return Theme.primaryPressed;
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16);
if (isSelected)
return Theme.primaryHoverLight;
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
if (mouseArea.containsMouse)
return Theme.surfacePressed;
return Theme.surfaceHover;
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12);
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.06);
}
border.color: isActive ? Theme.primary : (isSelected ? Theme.withAlpha(Theme.primary, 0.5) : Theme.withAlpha(Theme.primary, 0))
border.color: isActive ? Theme.primary : (isSelected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.5) : "transparent")
border.width: (isActive || isSelected) ? 2 : 0
// Shortcut Key Badge on Top-Right Corner
@@ -193,8 +193,8 @@ DankModal {
width: 20
height: 20
radius: 4
color: isActive ? Theme.primaryPressed : Theme.surfaceTextHover
border.color: isActive ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
color: isActive ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
border.color: isActive ? Theme.primary : "transparent"
border.width: isActive ? 1 : 0
StyledText {
+14 -19
View File
@@ -48,22 +48,19 @@ FloatingWindow {
log.warn("dgop is not available");
return;
}
if (!visible) {
show();
return;
}
const modalTitle = I18n.tr("System Monitor", "sysmon window title");
for (const toplevel of ToplevelManager.toplevels.values) {
if (toplevel.title !== "System Monitor" && toplevel.title !== modalTitle)
continue;
if (toplevel.activated) {
hide();
if (visible) {
const modalTitle = I18n.tr("System Monitor", "sysmon window title");
for (const toplevel of ToplevelManager.toplevels.values) {
if (toplevel.title !== "System Monitor" && toplevel.title !== modalTitle)
continue;
if (toplevel.activated) {
hide();
return;
}
toplevel.activate();
return;
}
toplevel.activate();
return;
}
hide();
show();
}
@@ -93,8 +90,6 @@ FloatingWindow {
color: Theme.surfaceContainer
visible: false
onClosed: hide()
onCurrentTabChanged: {
if (visible && currentTab === 0 && searchField.visible)
searchField.forceActiveFocus();
@@ -200,7 +195,7 @@ FloatingWindow {
width: 400
height: 200
radius: Theme.cornerRadius
color: Theme.errorHover
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1)
border.color: Theme.error
border.width: 2
visible: !DgopService.dgopAvailable
@@ -305,7 +300,7 @@ FloatingWindow {
spacing: Theme.spacingM
Row {
spacing: Theme.spacingXXS
spacing: 2
Repeater {
model: [
@@ -331,8 +326,8 @@ FloatingWindow {
width: tabRowContent.implicitWidth + Theme.spacingM * 2
height: Math.round(Theme.fontSizeMedium * 3.1)
radius: Theme.cornerRadius
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryHoverLight, 0))
border.color: currentTab === index ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
border.color: currentTab === index ? Theme.primary : "transparent"
border.width: currentTab === index ? 1 : 0
Row {
@@ -52,7 +52,7 @@ Rectangle {
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
spacing: 4
Rectangle {
width: 28
@@ -434,9 +434,7 @@ FocusScope {
visible: active
focus: active
sourceComponent: MediaPlayerTab {
parentModal: root.parentModal
}
sourceComponent: MediaPlayerTab {}
onActiveChanged: {
if (active && item)
+1 -4
View File
@@ -8,6 +8,7 @@ import qs.Widgets
FloatingWindow {
id: settingsModal
property bool disablePopupTransparency: true
property var profileBrowser: profileBrowserLoader.item
property var wallpaperBrowser: wallpaperBrowserLoader.item
@@ -92,8 +93,6 @@ FloatingWindow {
color: Theme.surfaceContainer
visible: false
onClosed: hide()
onIsCompactModeChanged: {
enableAnimations = false;
if (!isCompactMode) {
@@ -174,8 +173,6 @@ FloatingWindow {
FocusScope {
id: contentFocusScope
property bool disablePopupTransparency: true
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
@@ -711,7 +711,7 @@ Rectangle {
rightPadding: Theme.spacingS
bottomPadding: Theme.spacingL
topPadding: Theme.spacingM + 2
spacing: Theme.spacingXXS
spacing: 2
ProfileSection {
width: parent.width - parent.leftPadding - parent.rightPadding
@@ -804,7 +804,7 @@ Rectangle {
Column {
id: searchResultsColumn
width: parent.width - parent.leftPadding - parent.rightPadding
spacing: Theme.spacingXXS
spacing: 2
visible: root.searchActive
Item {
@@ -859,7 +859,7 @@ Rectangle {
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
StyledText {
text: resultDelegate.modelData.label
@@ -928,7 +928,7 @@ Rectangle {
width: parent.width - parent.leftPadding - parent.rightPadding
visible: !root.searchActive && root.isCategoryVisible(modelData)
spacing: Theme.spacingXXS
spacing: 2
Rectangle {
width: parent.width
@@ -1035,7 +1035,7 @@ Rectangle {
Column {
id: childrenColumn
width: parent.width
spacing: Theme.spacingXXS
spacing: 2
visible: categoryDelegate.modelData.children !== undefined && root.isCategoryExpanded(categoryDelegate.modelData.id)
clip: true
+2 -2
View File
@@ -141,7 +141,7 @@ DankModal {
Column {
width: parent.width - Theme.iconSize - 4 - chevron.width - Theme.spacingM * 2
spacing: Theme.spacingXXS
spacing: 2
anchors.verticalCenter: parent.verticalCenter
StyledText {
@@ -215,7 +215,7 @@ DankModal {
Column {
id: bodyCol
width: parent.width - Theme.iconSize - Theme.spacingM
spacing: Theme.spacingXS
spacing: 4
StyledText {
text: I18n.tr("No other active sessions on this seat")
+2 -2
View File
@@ -626,7 +626,7 @@ DankModal {
width: 20
height: 20
radius: 4
color: checked ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
color: checked ? Theme.primary : "transparent"
border.color: checked ? Theme.primary : Theme.outlineButton
border.width: 2
@@ -667,7 +667,7 @@ DankModal {
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: cancelArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0)
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
border.color: Theme.surfaceVariantAlpha
border.width: 1
+2 -4
View File
@@ -32,8 +32,6 @@ FloatingWindow {
color: Theme.surfaceContainer
visible: false
onClosed: hide()
function resetForm() {
nameInput.text = "";
appIdInput.text = "";
@@ -506,7 +504,7 @@ FloatingWindow {
width: 20
height: 20
radius: 4
color: parent.indeterminate ? Theme.surfaceVariant : (checked ? Theme.primary : Theme.withAlpha(Theme.primary, 0))
color: parent.indeterminate ? Theme.surfaceVariant : (checked ? Theme.primary : "transparent")
border.color: parent.indeterminate ? Theme.outlineButton : (checked ? Theme.primary : Theme.outlineButton)
border.width: 2
anchors.verticalCenter: parent.verticalCenter
@@ -1843,7 +1841,7 @@ FloatingWindow {
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: cancelArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0)
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
border.color: Theme.surfaceVariantAlpha
border.width: 1
+1 -3
View File
@@ -18,8 +18,6 @@ FloatingWindow {
color: Theme.surfaceContainer
visible: false
onClosed: hide()
function show(name) {
nameInput.text = name;
visible = true;
@@ -153,7 +151,7 @@ FloatingWindow {
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: cancelArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0)
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
border.color: Theme.surfaceVariantAlpha
border.width: 1
@@ -1,7 +1,6 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Item {
@@ -93,15 +92,6 @@ Item {
precision: root.needsSeconds ? SystemClock.Seconds : SystemClock.Minutes
}
Connections {
target: SessionService
function onSessionResumed() {
systemClock.enabled = false;
systemClock.enabled = true;
}
}
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
@@ -405,8 +405,8 @@ Item {
var c = root.accentColor;
var grad = ctx.createLinearGradient(0, 0, 0, height);
grad.addColorStop(0, Theme.withAlpha(c, 0.3));
grad.addColorStop(1, Theme.withAlpha(c, 0.05));
grad.addColorStop(0, Qt.rgba(c.r, c.g, c.b, 0.3));
grad.addColorStop(1, Qt.rgba(c.r, c.g, c.b, 0.05));
ctx.fillStyle = grad;
ctx.beginPath();
@@ -420,7 +420,7 @@ Item {
ctx.closePath();
ctx.fill();
ctx.strokeStyle = Theme.withAlpha(c, 0.6);
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.6);
ctx.lineWidth = 1.5;
ctx.beginPath();
for (var j = 0; j < hist.length; j++) {
@@ -431,7 +431,7 @@ Item {
ctx.stroke();
if (hist2 && hist2.length >= 2) {
ctx.strokeStyle = Theme.withAlpha(c, 0.3);
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.3);
ctx.lineWidth = 1;
ctx.beginPath();
for (var m = 0; m < hist2.length; m++) {
@@ -447,7 +447,7 @@ Item {
ColumnLayout {
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: Theme.spacingXXS
spacing: 2
RowLayout {
Layout.fillWidth: true
@@ -206,7 +206,7 @@ PluginComponent {
Rectangle {
height: 1
width: parent.width
color: Theme.outlineStrong
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
}
DankFlickable {
@@ -274,7 +274,7 @@ PluginComponent {
}
Column {
spacing: Theme.spacingXXS
spacing: 2
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
@@ -96,7 +96,7 @@ PluginComponent {
width: autoLabel.implicitWidth + Theme.spacingL * 2
height: 28
radius: 14
color: root.autoMode ? Theme.primaryPressed : (autoMouseArea.containsMouse ? Theme.surfaceLight : Theme.withAlpha(Theme.surfaceLight, 0))
color: root.autoMode ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : (autoMouseArea.containsMouse ? Theme.surfaceLight : "transparent")
border.color: root.autoMode ? Theme.primary : Theme.outlineMedium
border.width: root.autoMode ? 1 : Theme.layerOutlineWidth
@@ -170,7 +170,7 @@ PluginComponent {
radius: Theme.cornerRadius
color: {
if (isActive)
return Theme.primaryHover;
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
if (profileMouseArea.containsMouse)
return Theme.surfaceLight;
return Theme.floatingSurface;
@@ -234,7 +234,7 @@ PluginComponent {
verticalBarPill: Component {
Column {
spacing: Theme.spacingXXS
spacing: 2
DankIcon {
name: "monitor"
color: Theme.primary
@@ -333,7 +333,7 @@ PluginComponent {
width: peerListColumn.width
height: peerCardColumn.implicitHeight + Theme.spacingS * 2
radius: Theme.cornerRadius
color: modelData.hostname === (TailscaleService.selfNode ? TailscaleService.selfNode.hostname : "") ? Theme.primaryHoverLight : Theme.surfaceContainerHighest
color: modelData.hostname === (TailscaleService.selfNode ? TailscaleService.selfNode.hostname : "") ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
property bool isSelf: modelData.hostname === (TailscaleService.selfNode ? TailscaleService.selfNode.hostname : "")
property bool isExpanded: detailRoot.expandedHostname === modelData.hostname
@@ -344,7 +344,7 @@ PluginComponent {
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Theme.spacingS
spacing: Theme.spacingXXS
spacing: 2
RowLayout {
width: parent.width
@@ -419,7 +419,7 @@ PluginComponent {
Column {
visible: isExpanded
width: parent.width
spacing: Theme.spacingXXS
spacing: 2
topPadding: 4
RowLayout {
@@ -43,7 +43,7 @@ Rectangle {
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: mouseArea.containsMouse ? hoverTint(root.color) : Theme.withAlpha(hoverTint(root.color), 0)
color: mouseArea.containsMouse ? hoverTint(root.color) : "transparent"
opacity: mouseArea.containsMouse ? 0.08 : 0.0
Behavior on opacity {
@@ -74,7 +74,7 @@ Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
spacing: 2
Typography {
width: parent.width
@@ -13,7 +13,7 @@ Rectangle {
height: menuColumn.implicitHeight + Theme.spacingS * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainer
border.color: Theme.outlineStrong
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.16)
border.width: 1
MouseArea {
@@ -24,7 +24,7 @@ Rectangle {
id: menuColumn
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: Theme.spacingXXS
spacing: 2
DankToggle {
width: parent.width

Some files were not shown because too many files have changed in this diff Show More