1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

Compare commits

..

24 Commits

Author SHA1 Message Date
Bailey
aab4b6765d nix: Support specifying systemd target (#1385) 2026-01-16 02:01:51 -03:00
bbedward
3539aca1f7 cc: wrap icons in fixed size containers
maybe #1376
2026-01-15 23:05:21 -05:00
bbedward
81fbe9eaba controlcenter: fix visibility condition of no icons
fixes #1377
2026-01-15 23:00:43 -05:00
purian23
f9dc6de485 Fix fedora version format 2026-01-15 23:00:08 -05:00
bbedward
012022d370 plugins: fix plugin confirm third part repo window 2026-01-15 22:55:11 -05:00
purian23
993216e157 distro: Update Fedora dynamic versioning 2026-01-15 22:30:20 -05:00
purian23
c992f2b582 feat: Allow more pinned services in Control Center/Settings 2026-01-15 21:51:17 -05:00
purian23
3243adebca core: Update ghostty on dankinstall 2026-01-15 21:26:31 -05:00
Abhinav Chalise
baccef57d4 fix volume osd sliding ui update for vertical layout (#1382) 2026-01-15 21:10:43 -05:00
bbedward
a823095372 widgets: add fallback for steam apps 2026-01-15 21:07:57 -05:00
Lucas
172a743de4 doctor: use dbus for checking on services (#1384)
* doctor: use dbus for checking on services

* doctor: show docs URL for failed checks

* core: remove unused function
2026-01-15 20:59:47 -05:00
Ivan Molodetskikh
623eec3689 Add screencast indicator for niri (#1361)
* niri: Handle new Cast events

* bar: Add screen sharing indicator

Configurable like other icons; on by default.

* lockscreen: Add screen sharing indicator
2026-01-15 00:55:21 -05:00
bbedward
53a033fe35 dankdash: fix weather open IPC
fixes #1367
2026-01-14 22:29:29 -05:00
bbedward
c490ee24f4 matugen: fix nvim ID in skipTemplates 2026-01-14 22:27:07 -05:00
bbedward
cc1e49294e i18n: update terms 2026-01-14 22:22:27 -05:00
purian23
e6fa46ae26 dankdash: Center Media Art & Controls 2026-01-14 18:03:16 -05:00
purian23
35fe774a1b Update OBS Choice selection 2026-01-13 17:51:55 -05:00
purian23
1e6a0f9423 Update OBS DMS Stable workflow 2026-01-13 17:31:01 -05:00
bbedward
cc1877aadb modals: fix wifi passowrd, polkit, and VPN import 2026-01-13 17:21:16 -05:00
bbedward
f1eb1fa9ba settings: fix child windows on newer quickshell-git 2026-01-13 16:57:23 -05:00
Lucas
bdd01e335d settings: fix modal not opening on latest quickshell (#1357) 2026-01-13 16:41:54 -05:00
Lucas
4b7baf82cd nix: escape version string (#1353) 2026-01-13 11:24:51 -05:00
purian23
15c88ce1d2 quickshell: Despace Versioning 2026-01-13 11:07:12 -05:00
bbedward
8891c388d0 bump to v1.4-unstable 2026-01-13 08:40:56 -05:00
39 changed files with 1852 additions and 1410 deletions

View File

@@ -4,13 +4,14 @@ on:
workflow_dispatch:
inputs:
package:
description: "Package to update (dms, dms-git, or all)"
required: false
default: "all"
tag_version:
description: "Specific tag version for dms stable (e.g., v1.0.2). Leave empty to auto-detect latest release."
required: false
default: ""
description: "Package to update"
required: true
type: choice
options:
- dms
- dms-git
- all
default: "dms"
rebuild_release:
description: "Release number for rebuilds (e.g., 2, 3, 4 to increment spec Release)"
required: false
@@ -56,8 +57,9 @@ jobs:
}
# Helper function to check dms stable tag
# Sets LATEST_TAG variable in parent scope if update needed
check_dms_stable() {
local LATEST_TAG=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | grep '"tag_name"' | sed 's/.*"tag_name": "v\?\([^"]*\)".*/\1/' || echo "")
LATEST_TAG=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | grep '"tag_name"' | sed 's/.*"tag_name": "\([^"]*\)".*/\1/' || echo "")
local OBS_SPEC=$(curl -s -u "$OBS_USERNAME:$OBS_PASSWORD" "https://api.opensuse.org/source/home:AvengeMedia:dms/dms/dms.spec" 2>/dev/null || echo "")
local OBS_VERSION=$(echo "$OBS_SPEC" | grep "^Version:" | awk '{print $2}' | xargs || echo "")
@@ -73,8 +75,8 @@ jobs:
# Main logic
REBUILD="${{ github.event.inputs.rebuild_release }}"
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
# Tag push - always update stable package
if [[ "${{ github.ref }}" =~ ^refs/tags/ ]]; then
# Tag selected or pushed - always update stable package
echo "packages=dms" >> $GITHUB_OUTPUT
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
@@ -104,7 +106,12 @@ jobs:
# Check each package and build list of those needing updates
PACKAGES_TO_UPDATE=()
check_dms_git && PACKAGES_TO_UPDATE+=("dms-git")
check_dms_stable && PACKAGES_TO_UPDATE+=("dms")
if check_dms_stable; then
PACKAGES_TO_UPDATE+=("dms")
if [[ -n "$LATEST_TAG" ]]; then
echo "version=$LATEST_TAG" >> $GITHUB_OUTPUT
fi
fi
if [[ ${#PACKAGES_TO_UPDATE[@]} -gt 0 ]]; then
echo "packages=${PACKAGES_TO_UPDATE[*]}" >> $GITHUB_OUTPUT
@@ -129,6 +136,9 @@ jobs:
if check_dms_stable; then
echo "packages=$PKG" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
if [[ -n "$LATEST_TAG" ]]; then
echo "version=$LATEST_TAG" >> $GITHUB_OUTPUT
fi
else
echo "packages=" >> $GITHUB_OUTPUT
echo "has_updates=false" >> $GITHUB_OUTPUT
@@ -161,12 +171,19 @@ jobs:
- name: Determine packages to update
id: packages
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
# Tag push event - use the pushed tag
# Check if GITHUB_REF points to a tag (works for both push events and workflow_dispatch with tag selected)
if [[ "${{ github.ref }}" =~ ^refs/tags/ ]]; then
# Tag selected or pushed - use the tag from GITHUB_REF
echo "packages=dms" >> $GITHUB_OUTPUT
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Triggered by tag: $VERSION"
echo "Using tag from GITHUB_REF: $VERSION"
# Check if check-updates already determined a version (from auto-detection)
elif [[ -n "${{ needs.check-updates.outputs.version }}" ]]; then
# Use version from check-updates job
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
echo "version=${{ needs.check-updates.outputs.version }}" >> $GITHUB_OUTPUT
echo "Using version from check-updates: ${{ needs.check-updates.outputs.version }}"
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
# Scheduled run - dms-git only
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
@@ -176,22 +193,28 @@ jobs:
# Determine version for dms stable
if [[ "${{ github.event.inputs.package }}" == "dms" ]]; then
# For explicit dms selection, require tag_version
if [[ -n "${{ github.event.inputs.tag_version }}" ]]; then
VERSION="${{ github.event.inputs.tag_version }}"
# Use github.ref if tag selected, otherwise auto-detect latest
if [[ "${{ github.ref }}" =~ ^refs/tags/ ]]; then
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Using specified tag: $VERSION"
echo "Using tag from GITHUB_REF: $VERSION"
else
echo "ERROR: tag_version is required when package=dms"
echo "Please specify a tag version (e.g., v1.0.2) or use package=all for auto-detection"
exit 1
# Auto-detect latest release for dms
LATEST_TAG=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | grep '"tag_name"' | sed 's/.*"tag_name": "\([^"]*\)".*/\1/' || echo "")
if [[ -n "$LATEST_TAG" ]]; then
echo "version=$LATEST_TAG" >> $GITHUB_OUTPUT
echo "Auto-detected latest release: $LATEST_TAG"
else
echo "ERROR: Could not auto-detect latest release"
exit 1
fi
fi
elif [[ "${{ github.event.inputs.package }}" == "all" ]]; then
# For "all", auto-detect if tag_version not specified
if [[ -n "${{ github.event.inputs.tag_version }}" ]]; then
VERSION="${{ github.event.inputs.tag_version }}"
# Use github.ref if tag selected, otherwise auto-detect latest
if [[ "${{ github.ref }}" =~ ^refs/tags/ ]]; then
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Using specified tag: $VERSION"
echo "Using tag from GITHUB_REF: $VERSION"
else
# Auto-detect latest release for "all"
LATEST_TAG=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | grep '"tag_name"' | sed 's/.*"tag_name": "\([^"]*\)".*/\1/' || echo "")
@@ -206,7 +229,7 @@ jobs:
fi
# Use filtered packages from check-updates when package="all" and no rebuild/tag specified
if [[ "${{ github.event.inputs.package }}" == "all" ]] && [[ -z "${{ github.event.inputs.rebuild_release }}" ]] && [[ -z "${{ github.event.inputs.tag_version }}" ]]; then
if [[ "${{ github.event.inputs.package }}" == "all" ]] && [[ -z "${{ github.event.inputs.rebuild_release }}" ]] && [[ ! "${{ github.ref }}" =~ ^refs/tags/ ]]; then
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
echo "Manual trigger: all (filtered to: ${{ needs.check-updates.outputs.packages }})"
else
@@ -215,6 +238,9 @@ jobs:
fi
else
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
if [[ -n "${{ needs.check-updates.outputs.version }}" ]]; then
echo "version=${{ needs.check-updates.outputs.version }}" >> $GITHUB_OUTPUT
fi
fi
- name: Update dms-git spec version

View File

@@ -477,7 +477,7 @@ func checkWindowManagers() []checkResult {
results = append(results, checkResult{
catCompositor, c.name, statusOK,
getVersionFromCommand(c.versionCmd, c.versionArg, c.versionRegex), details,
doctorDocsURL + "#compositor",
doctorDocsURL + "#compositor-checks",
})
}
@@ -486,7 +486,7 @@ func checkWindowManagers() []checkResult {
catCompositor, "Compositor", statusError,
"No supported Wayland compositor found",
"Install Hyprland, niri, Sway, River, or Wayfire",
doctorDocsURL + "#compositor",
doctorDocsURL + "#compositor-checks",
})
}
@@ -634,19 +634,14 @@ func checkI2CAvailability() checkResult {
return checkResult{catOptionalFeatures, "I2C/DDC", statusOK, fmt.Sprintf("%d monitor(s) detected", len(devices)), "External monitor brightness control", doctorDocsURL + "#optional-features"}
}
func detectNetworkBackend() string {
result, err := network.DetectNetworkStack()
if err != nil {
return ""
}
switch result.Backend {
func detectNetworkBackend(stackResult *network.DetectResult) string {
switch stackResult.Backend {
case network.BackendNetworkManager:
return "NetworkManager"
case network.BackendIwd:
return "iwd"
case network.BackendNetworkd:
if result.HasIwd {
if stackResult.HasIwd {
return "iwd + systemd-networkd"
}
return "systemd-networkd"
@@ -657,75 +652,73 @@ func detectNetworkBackend() string {
}
}
func getOptionalDBusStatus(busName string) (status, string) {
if utils.IsDBusServiceAvailable(busName) {
return statusOK, "Available"
} else {
return statusWarn, "Not available"
}
}
func checkOptionalDependencies() []checkResult {
var results []checkResult
if utils.IsServiceActive("accounts-daemon", false) {
results = append(results, checkResult{catOptionalFeatures, "accountsservice", statusOK, "Running", "User accounts", doctorDocsURL + "#optional-features"})
} else {
results = append(results, checkResult{catOptionalFeatures, "accountsservice", statusWarn, "Not running", "User accounts", doctorDocsURL + "#optional-features"})
}
optionalFeaturesURL := doctorDocsURL + "#optional-features"
if utils.IsServiceActive("power-profiles-daemon", false) {
results = append(results, checkResult{catOptionalFeatures, "power-profiles-daemon", statusOK, "Running", "Power profile management", doctorDocsURL + "#optional-features"})
} else {
results = append(results, checkResult{catOptionalFeatures, "power-profiles-daemon", statusInfo, "Not running", "Power profile management", doctorDocsURL + "#optional-features"})
}
accountsStatus, accountsMsg := getOptionalDBusStatus("org.freedesktop.Accounts")
results = append(results, checkResult{catOptionalFeatures, "accountsservice", accountsStatus, accountsMsg, "User accounts", optionalFeaturesURL})
ppdStatus, ppdMsg := getOptionalDBusStatus("org.freedesktop.UPower.PowerProfiles")
results = append(results, checkResult{catOptionalFeatures, "power-profiles-daemon", ppdStatus, ppdMsg, "Power profile management", optionalFeaturesURL})
logindStatus, logindMsg := getOptionalDBusStatus("org.freedesktop.login1")
results = append(results, checkResult{catOptionalFeatures, "logind", logindStatus, logindMsg, "Session management", optionalFeaturesURL})
results = append(results, checkI2CAvailability())
terminals := []string{"ghostty", "kitty", "alacritty", "foot", "wezterm"}
if idx := slices.IndexFunc(terminals, utils.CommandExists); idx >= 0 {
results = append(results, checkResult{catOptionalFeatures, "Terminal", statusOK, terminals[idx], "", doctorDocsURL + "#optional-features"})
results = append(results, checkResult{catOptionalFeatures, "Terminal", statusOK, terminals[idx], "", optionalFeaturesURL})
} else {
results = append(results, checkResult{catOptionalFeatures, "Terminal", statusWarn, "None found", "Install ghostty, kitty, or alacritty", doctorDocsURL + "#optional-features"})
results = append(results, checkResult{catOptionalFeatures, "Terminal", statusWarn, "None found", "Install ghostty, kitty, or alacritty", optionalFeaturesURL})
}
networkResult, err := network.DetectNetworkStack()
networkStatus, networkMessage, networkDetails := statusOK, "Not available", "Network management"
if err == nil && networkResult.Backend != network.BackendNone {
networkMessage = detectNetworkBackend(networkResult)
if doctorVerbose {
networkDetails = networkResult.ChosenReason
}
} else {
networkStatus = statusInfo
}
results = append(results, checkResult{catOptionalFeatures, "Network", networkStatus, networkMessage, networkDetails, optionalFeaturesURL})
deps := []struct {
name, cmd, altCmd, desc string
important bool
name, cmd, desc string
important bool
}{
{"matugen", "matugen", "", "Dynamic theming", true},
{"dgop", "dgop", "", "System monitoring", true},
{"cava", "cava", "", "Audio visualizer", true},
{"khal", "khal", "", "Calendar events", false},
{"Network", "nmcli", "iwctl", "Network management", false},
{"danksearch", "dsearch", "", "File search", false},
{"loginctl", "loginctl", "", "Session management", false},
{"fprintd", "fprintd-list", "", "Fingerprint auth", false},
{"matugen", "matugen", "Dynamic theming", true},
{"dgop", "dgop", "System monitoring", true},
{"cava", "cava", "Audio visualizer", true},
{"khal", "khal", "Calendar events", false},
{"danksearch", "dsearch", "File search", false},
{"fprintd", "fprintd-list", "Fingerprint auth", false},
}
for _, d := range deps {
found, foundCmd := utils.CommandExists(d.cmd), d.cmd
if !found && d.altCmd != "" && utils.CommandExists(d.altCmd) {
found, foundCmd = true, d.altCmd
}
found := utils.CommandExists(d.cmd)
switch {
case found:
message := "Installed"
details := d.desc
if d.name == "Network" {
result, err := network.DetectNetworkStack()
if err == nil && result.Backend != network.BackendNone {
message = detectNetworkBackend() + " (active)"
if doctorVerbose {
details = result.ChosenReason
}
} else {
switch foundCmd {
case "nmcli":
message = "NetworkManager (installed)"
case "iwctl":
message = "iwd (installed)"
}
}
}
results = append(results, checkResult{catOptionalFeatures, d.name, statusOK, message, details, doctorDocsURL + "#optional-features"})
results = append(results, checkResult{catOptionalFeatures, d.name, statusOK, "Installed", d.desc, optionalFeaturesURL})
case d.important:
results = append(results, checkResult{catOptionalFeatures, d.name, statusWarn, "Missing", d.desc, doctorDocsURL + "#optional-features"})
results = append(results, checkResult{catOptionalFeatures, d.name, statusWarn, "Missing", d.desc, optionalFeaturesURL})
default:
results = append(results, checkResult{catOptionalFeatures, d.name, statusInfo, "Not installed", d.desc, doctorDocsURL + "#optional-features"})
results = append(results, checkResult{catOptionalFeatures, d.name, statusInfo, "Not installed", d.desc, optionalFeaturesURL})
}
}
@@ -893,6 +886,10 @@ func printResultLine(r checkResult, styles tui.Styles) {
if doctorVerbose && r.details != "" {
fmt.Printf(" %s\n", styles.Subtle.Render("└─ "+r.details))
}
if (r.status == statusError || r.status == statusWarn) && r.url != "" {
fmt.Printf(" %s\n", styles.Subtle.Render("→ "+r.url))
}
}
func printSummary(results []checkResult, qsMissingFeatures bool) {

View File

@@ -108,7 +108,6 @@ func (o *OpenSUSEDistribution) GetPackageMappingWithVariants(wm deps.WindowManag
packages := map[string]PackageMapping{
// Standard zypper packages
"git": {Name: "git", Repository: RepoTypeSystem},
"ghostty": {Name: "ghostty", Repository: RepoTypeSystem},
"kitty": {Name: "kitty", Repository: RepoTypeSystem},
"alacritty": {Name: "alacritty", Repository: RepoTypeSystem},
"xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem},
@@ -117,6 +116,7 @@ func (o *OpenSUSEDistribution) GetPackageMappingWithVariants(wm deps.WindowManag
// DMS packages from OBS
"dms (DankMaterialShell)": o.getDmsMapping(variants["dms (DankMaterialShell)"]),
"quickshell": o.getQuickshellMapping(variants["quickshell"]),
"ghostty": {Name: "ghostty", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
"matugen": {Name: "matugen", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
"dgop": {Name: "dgop", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
}

View File

@@ -0,0 +1,20 @@
package utils
import (
"github.com/godbus/dbus/v5"
)
func IsDBusServiceAvailable(busName string) bool {
conn, err := dbus.ConnectSystemBus()
if err != nil {
return false
}
defer conn.Close()
obj := conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
var owned bool
if err := obj.Call("org.freedesktop.DBus.NameHasOwner", 0, busName).Store(&owned); err != nil {
return false
}
return owned
}

View File

@@ -2,7 +2,6 @@ package utils
import (
"os/exec"
"strings"
)
type AppChecker interface {
@@ -43,16 +42,3 @@ func AnyCommandExists(cmds ...string) bool {
}
return false
}
func IsServiceActive(name string, userService bool) bool {
if !CommandExists("systemctl") {
return false
}
args := []string{"is-active", name}
if userService {
args = []string{"--user", "is-active", name}
}
output, _ := exec.Command("systemctl", args...).Output()
return strings.EqualFold(strings.TrimSpace(string(output)), "active")
}

View File

@@ -73,6 +73,12 @@ in
default = hasPluginSettings;
description = ''Whether to manage plugin settings. Automatically enabled if any plugins have settings configured.'';
};
systemd.target = lib.mkOption {
type = lib.types.str;
default = config.wayland.systemd.target;
defaultText = lib.literalExpression "config.wayland.systemd.target";
description = "Systemd target to bind to.";
};
config = lib.mkIf cfg.enable {
@@ -84,8 +90,8 @@ in
systemd.user.services.dms = lib.mkIf cfg.systemd.enable {
Unit = {
Description = "DankMaterialShell";
PartOf = [ config.wayland.systemd.target ];
After = [ config.wayland.systemd.target ];
PartOf = [ cfg.systemd.target ];
After = [ cfg.systemd.target ];
};
Service = {
@@ -93,7 +99,7 @@ in
Restart = "on-failure";
};
Install.WantedBy = [ config.wayland.systemd.target ];
Install.WantedBy = [ cfg.systemd.target ];
};
xdg.stateFile."DankMaterialShell/session.json" = lib.mkIf (cfg.session != { }) {

View File

@@ -20,15 +20,19 @@ in
imports = [
(import ./options.nix args)
];
options.programs.dank-material-shell.systemd.target = lib.mkOption {
type = lib.types.str;
description = "Systemd target to bind to.";
default = "graphical-session.target";
};
config = lib.mkIf cfg.enable {
systemd.user.services.dms = lib.mkIf cfg.systemd.enable {
description = "DankMaterialShell";
path = lib.mkForce [ ];
partOf = [ "graphical-session.target" ];
after = [ "graphical-session.target" ];
wantedBy = [ "graphical-session.target" ];
partOf = [ cfg.systemd.target ];
after = [ cfg.systemd.target ];
wantedBy = [ cfg.systemd.target ];
restartIfChanged = cfg.systemd.restartIfChanged;
serviceConfig = {

View File

@@ -1 +1 @@
Spicy Miso
Saffron Bloom

View File

@@ -45,6 +45,10 @@ Singleton {
Quickshell.execDetached(["cp", strip(from), strip(to)]);
}
function isSteamApp(appId: string): bool {
return appId && /^steam_app_\d+$/.test(appId);
}
function moddedAppId(appId: string): string {
const subs = SettingsData.appIdSubstitutions || [];
for (let i = 0; i < subs.length; i++) {
@@ -60,6 +64,9 @@ Singleton {
}
}
}
const steamMatch = appId.match(/^steam_app_(\d+)$/);
if (steamMatch)
return `steam_icon_${steamMatch[1]}`;
return appId;
}

View File

@@ -145,6 +145,7 @@ Singleton {
property bool controlCenterShowMicPercent: true
property bool controlCenterShowBatteryIcon: false
property bool controlCenterShowPrinterIcon: false
property bool controlCenterShowScreenSharingIcon: true
property bool showPrivacyButton: true
property bool privacyShowMicIcon: false
property bool privacyShowCameraIcon: false

View File

@@ -904,7 +904,7 @@ Singleton {
if (typeof SettingsData !== "undefined") {
const skipTemplates = [];
if (!SettingsData.runDmsMatugenTemplates) {
skipTemplates.push("gtk", "neovim", "niri", "qt5ct", "qt6ct", "firefox", "pywalfox", "zenbrowser", "vesktop", "equibop", "ghostty", "kitty", "foot", "alacritty", "wezterm", "dgop", "kcolorscheme", "vscode");
skipTemplates.push("gtk", "nvim", "niri", "qt5ct", "qt6ct", "firefox", "pywalfox", "zenbrowser", "vesktop", "equibop", "ghostty", "kitty", "foot", "alacritty", "wezterm", "dgop", "kcolorscheme", "vscode");
} else {
if (!SettingsData.matugenTemplateGtk)
skipTemplates.push("gtk");

View File

@@ -28,7 +28,8 @@ Singleton {
showMicIcon: false,
showMicPercent: true,
showBatteryIcon: false,
showPrinterIcon: false
showPrinterIcon: false,
showScreenSharingIcon: true
};
leftModel.append(dummy);
centerModel.append(dummy);
@@ -84,6 +85,8 @@ Singleton {
item.showBatteryIcon = order[i].showBatteryIcon;
if (isObj && order[i].showPrinterIcon !== undefined)
item.showPrinterIcon = order[i].showPrinterIcon;
if (isObj && order[i].showScreenSharingIcon !== undefined)
item.showScreenSharingIcon = order[i].showScreenSharingIcon;
model.append(item);
}

View File

@@ -70,6 +70,7 @@ var SPEC = {
controlCenterShowMicPercent: { def: false },
controlCenterShowBatteryIcon: { def: false },
controlCenterShowPrinterIcon: { def: false },
controlCenterShowScreenSharingIcon: { def: true },
showPrivacyButton: { def: true },
privacyShowMicIcon: { def: false },

View File

@@ -132,8 +132,11 @@ Item {
case "media":
root.dankDashPopoutLoader.item.currentTabIndex = 1;
break;
case "wallpaper":
root.dankDashPopoutLoader.item.currentTabIndex = 2;
break;
case "weather":
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 2 : 0;
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 3 : 0;
break;
default:
root.dankDashPopoutLoader.item.currentTabIndex = 0;

View File

@@ -122,6 +122,21 @@ Rectangle {
contentHeight: audioColumn.height
clip: true
property int maxPinnedInputs: 3
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v)
if (typeof value === "string" && value.length > 0)
return [value]
return []
}
function getPinnedInputs() {
const pins = SettingsData.audioInputDevicePins || {}
return normalizePinList(pins["preferredInput"])
}
Column {
id: audioColumn
width: parent.width
@@ -133,16 +148,20 @@ Rectangle {
const nodes = Pipewire.nodes.values.filter(node => {
return node.audio && !node.isSink && !node.isStream;
});
const pins = SettingsData.audioInputDevicePins || {};
const pinnedName = pins["preferredInput"];
const pinnedList = audioContent.getPinnedInputs();
let sorted = [...nodes];
sorted.sort((a, b) => {
// Pinned device first
if (a.name === pinnedName && b.name !== pinnedName)
return -1;
if (b.name === pinnedName && a.name !== pinnedName)
return 1;
const aPinnedIndex = pinnedList.indexOf(a.name)
const bPinnedIndex = pinnedList.indexOf(b.name)
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
if (aPinnedIndex === -1)
return 1
if (bPinnedIndex === -1)
return -1
return aPinnedIndex - bPinnedIndex
}
// Then active device
if (a === AudioService.source && b !== AudioService.source)
return -1;
@@ -224,7 +243,7 @@ Rectangle {
height: 28
radius: height / 2
color: {
const isThisDevicePinned = (SettingsData.audioInputDevicePins || {})["preferredInput"] === modelData.name;
const isThisDevicePinned = audioContent.getPinnedInputs().includes(modelData.name);
return isThisDevicePinned ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.surfaceText, 0.05);
}
@@ -237,7 +256,7 @@ Rectangle {
name: "push_pin"
size: 16
color: {
const isThisDevicePinned = (SettingsData.audioInputDevicePins || {})["preferredInput"] === modelData.name;
const isThisDevicePinned = audioContent.getPinnedInputs().includes(modelData.name);
return isThisDevicePinned ? Theme.primary : Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
@@ -245,12 +264,12 @@ Rectangle {
StyledText {
text: {
const isThisDevicePinned = (SettingsData.audioInputDevicePins || {})["preferredInput"] === modelData.name;
const isThisDevicePinned = audioContent.getPinnedInputs().includes(modelData.name);
return isThisDevicePinned ? I18n.tr("Pinned") : I18n.tr("Pin");
}
font.pixelSize: Theme.fontSizeSmall
color: {
const isThisDevicePinned = (SettingsData.audioInputDevicePins || {})["preferredInput"] === modelData.name;
const isThisDevicePinned = audioContent.getPinnedInputs().includes(modelData.name);
return isThisDevicePinned ? Theme.primary : Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
@@ -261,16 +280,24 @@ Rectangle {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
const pins = JSON.parse(JSON.stringify(SettingsData.audioInputDevicePins || {}));
const isCurrentlyPinned = pins["preferredInput"] === modelData.name;
const pins = JSON.parse(JSON.stringify(SettingsData.audioInputDevicePins || {}))
let pinnedList = audioContent.normalizePinList(pins["preferredInput"])
const pinIndex = pinnedList.indexOf(modelData.name)
if (isCurrentlyPinned) {
delete pins["preferredInput"];
if (pinIndex !== -1) {
pinnedList.splice(pinIndex, 1)
} else {
pins["preferredInput"] = modelData.name;
pinnedList.unshift(modelData.name)
if (pinnedList.length > audioContent.maxPinnedInputs)
pinnedList = pinnedList.slice(0, audioContent.maxPinnedInputs)
}
SettingsData.set("audioInputDevicePins", pins);
if (pinnedList.length > 0)
pins["preferredInput"] = pinnedList
else
delete pins["preferredInput"]
SettingsData.set("audioInputDevicePins", pins)
}
}
}

View File

@@ -132,6 +132,21 @@ Rectangle {
contentHeight: audioColumn.height
clip: true
property int maxPinnedOutputs: 3
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v)
if (typeof value === "string" && value.length > 0)
return [value]
return []
}
function getPinnedOutputs() {
const pins = SettingsData.audioOutputDevicePins || {}
return normalizePinList(pins["preferredOutput"])
}
Column {
id: audioColumn
width: parent.width
@@ -143,16 +158,20 @@ Rectangle {
const nodes = Pipewire.nodes.values.filter(node => {
return node.audio && node.isSink && !node.isStream;
});
const pins = SettingsData.audioOutputDevicePins || {};
const pinnedName = pins["preferredOutput"];
const pinnedList = audioContent.getPinnedOutputs();
let sorted = [...nodes];
sorted.sort((a, b) => {
// Pinned device first
if (a.name === pinnedName && b.name !== pinnedName)
return -1;
if (b.name === pinnedName && a.name !== pinnedName)
return 1;
const aPinnedIndex = pinnedList.indexOf(a.name)
const bPinnedIndex = pinnedList.indexOf(b.name)
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
if (aPinnedIndex === -1)
return 1
if (bPinnedIndex === -1)
return -1
return aPinnedIndex - bPinnedIndex
}
// Then active device
if (a === AudioService.sink && b !== AudioService.sink)
return -1;
@@ -236,7 +255,7 @@ Rectangle {
height: 28
radius: height / 2
color: {
const isThisDevicePinned = (SettingsData.audioOutputDevicePins || {})["preferredOutput"] === modelData.name;
const isThisDevicePinned = audioContent.getPinnedOutputs().includes(modelData.name);
return isThisDevicePinned ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.surfaceText, 0.05);
}
@@ -249,7 +268,7 @@ Rectangle {
name: "push_pin"
size: 16
color: {
const isThisDevicePinned = (SettingsData.audioOutputDevicePins || {})["preferredOutput"] === modelData.name;
const isThisDevicePinned = audioContent.getPinnedOutputs().includes(modelData.name);
return isThisDevicePinned ? Theme.primary : Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
@@ -257,12 +276,12 @@ Rectangle {
StyledText {
text: {
const isThisDevicePinned = (SettingsData.audioOutputDevicePins || {})["preferredOutput"] === modelData.name;
const isThisDevicePinned = audioContent.getPinnedOutputs().includes(modelData.name);
return isThisDevicePinned ? I18n.tr("Pinned") : I18n.tr("Pin");
}
font.pixelSize: Theme.fontSizeSmall
color: {
const isThisDevicePinned = (SettingsData.audioOutputDevicePins || {})["preferredOutput"] === modelData.name;
const isThisDevicePinned = audioContent.getPinnedOutputs().includes(modelData.name);
return isThisDevicePinned ? Theme.primary : Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
@@ -273,16 +292,24 @@ Rectangle {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
const pins = JSON.parse(JSON.stringify(SettingsData.audioOutputDevicePins || {}));
const isCurrentlyPinned = pins["preferredOutput"] === modelData.name;
const pins = JSON.parse(JSON.stringify(SettingsData.audioOutputDevicePins || {}))
let pinnedList = audioContent.normalizePinList(pins["preferredOutput"])
const pinIndex = pinnedList.indexOf(modelData.name)
if (isCurrentlyPinned) {
delete pins["preferredOutput"];
if (pinIndex !== -1) {
pinnedList.splice(pinIndex, 1)
} else {
pins["preferredOutput"] = modelData.name;
pinnedList.unshift(modelData.name)
if (pinnedList.length > audioContent.maxPinnedOutputs)
pinnedList = pinnedList.slice(0, audioContent.maxPinnedOutputs)
}
SettingsData.set("audioOutputDevicePins", pins);
if (pinnedList.length > 0)
pins["preferredOutput"] = pinnedList
else
delete pins["preferredOutput"]
SettingsData.set("audioOutputDevicePins", pins)
}
}
}

View File

@@ -150,6 +150,21 @@ Rectangle {
contentHeight: bluetoothColumn.height
clip: true
property int maxPinnedDevices: 3
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v)
if (typeof value === "string" && value.length > 0)
return [value]
return []
}
function getPinnedDevices() {
const pins = SettingsData.bluetoothDevicePins || {}
return normalizePinList(pins["preferredDevice"])
}
Column {
id: bluetoothColumn
width: parent.width
@@ -162,14 +177,18 @@ Rectangle {
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
return []
const pins = SettingsData.bluetoothDevicePins || {}
const pinnedAddr = pins["preferredDevice"]
const pinnedList = bluetoothContent.getPinnedDevices()
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
devices.sort((a, b) => {
// Pinned device first
if (a.address === pinnedAddr && b.address !== pinnedAddr) return -1
if (b.address === pinnedAddr && a.address !== pinnedAddr) return 1
const aPinnedIndex = pinnedList.indexOf(a.address)
const bPinnedIndex = pinnedList.indexOf(b.address)
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
if (aPinnedIndex === -1) return 1
if (bPinnedIndex === -1) return -1
return aPinnedIndex - bPinnedIndex
}
// Then connected devices
if (a.connected && !b.connected) return -1
if (!a.connected && b.connected) return 1
@@ -302,7 +321,7 @@ Rectangle {
height: 28
radius: height / 2
color: {
const isThisDevicePinned = (SettingsData.bluetoothDevicePins || {})["preferredDevice"] === modelData.address
const isThisDevicePinned = bluetoothContent.getPinnedDevices().includes(modelData.address)
return isThisDevicePinned ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.surfaceText, 0.05)
}
@@ -315,7 +334,7 @@ Rectangle {
name: "push_pin"
size: 16
color: {
const isThisDevicePinned = (SettingsData.bluetoothDevicePins || {})["preferredDevice"] === modelData.address
const isThisDevicePinned = bluetoothContent.getPinnedDevices().includes(modelData.address)
return isThisDevicePinned ? Theme.primary : Theme.surfaceText
}
anchors.verticalCenter: parent.verticalCenter
@@ -323,12 +342,12 @@ Rectangle {
StyledText {
text: {
const isThisDevicePinned = (SettingsData.bluetoothDevicePins || {})["preferredDevice"] === modelData.address
const isThisDevicePinned = bluetoothContent.getPinnedDevices().includes(modelData.address)
return isThisDevicePinned ? I18n.tr("Pinned") : I18n.tr("Pin")
}
font.pixelSize: Theme.fontSizeSmall
color: {
const isThisDevicePinned = (SettingsData.bluetoothDevicePins || {})["preferredDevice"] === modelData.address
const isThisDevicePinned = bluetoothContent.getPinnedDevices().includes(modelData.address)
return isThisDevicePinned ? Theme.primary : Theme.surfaceText
}
anchors.verticalCenter: parent.verticalCenter
@@ -340,14 +359,22 @@ Rectangle {
cursorShape: Qt.PointingHandCursor
onClicked: {
const pins = JSON.parse(JSON.stringify(SettingsData.bluetoothDevicePins || {}))
const isCurrentlyPinned = pins["preferredDevice"] === modelData.address
let pinnedList = bluetoothContent.normalizePinList(pins["preferredDevice"])
const pinIndex = pinnedList.indexOf(modelData.address)
if (isCurrentlyPinned) {
delete pins["preferredDevice"]
if (pinIndex !== -1) {
pinnedList.splice(pinIndex, 1)
} else {
pins["preferredDevice"] = modelData.address
pinnedList.unshift(modelData.address)
if (pinnedList.length > bluetoothContent.maxPinnedDevices)
pinnedList = pinnedList.slice(0, bluetoothContent.maxPinnedDevices)
}
if (pinnedList.length > 0)
pins["preferredDevice"] = pinnedList
else
delete pins["preferredDevice"]
SettingsData.set("bluetoothDevicePins", pins)
}
}

View File

@@ -463,20 +463,39 @@ Rectangle {
contentHeight: wifiColumn.height
clip: true
property int maxPinnedNetworks: 3
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v)
if (typeof value === "string" && value.length > 0)
return [value]
return []
}
function getPinnedNetworks() {
const pins = SettingsData.wifiNetworkPins || {}
return normalizePinList(pins["preferredWifi"])
}
property var frozenNetworks: []
property bool menuOpen: false
property var sortedNetworks: {
const ssid = NetworkService.currentWifiSSID;
const networks = NetworkService.wifiNetworks;
const pins = SettingsData.wifiNetworkPins || {};
const pinnedSSID = pins["preferredWifi"];
const pinnedList = getPinnedNetworks()
let sorted = [...networks];
sorted.sort((a, b) => {
if (a.ssid === pinnedSSID && b.ssid !== pinnedSSID)
return -1;
if (b.ssid === pinnedSSID && a.ssid !== pinnedSSID)
return 1;
const aPinnedIndex = pinnedList.indexOf(a.ssid)
const bPinnedIndex = pinnedList.indexOf(b.ssid)
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
if (aPinnedIndex === -1)
return 1
if (bPinnedIndex === -1)
return -1
return aPinnedIndex - bPinnedIndex
}
if (a.ssid === ssid)
return -1;
if (b.ssid === ssid)
@@ -625,7 +644,7 @@ Rectangle {
height: 28
radius: height / 2
color: {
const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid;
const isThisNetworkPinned = wifiContent.getPinnedNetworks().includes(modelData.ssid);
return isThisNetworkPinned ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.surfaceText, 0.05);
}
@@ -638,7 +657,7 @@ Rectangle {
name: "push_pin"
size: 16
color: {
const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid;
const isThisNetworkPinned = wifiContent.getPinnedNetworks().includes(modelData.ssid);
return isThisNetworkPinned ? Theme.primary : Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
@@ -646,12 +665,12 @@ Rectangle {
StyledText {
text: {
const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid;
const isThisNetworkPinned = wifiContent.getPinnedNetworks().includes(modelData.ssid);
return isThisNetworkPinned ? I18n.tr("Pinned") : I18n.tr("Pin");
}
font.pixelSize: Theme.fontSizeSmall
color: {
const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid;
const isThisNetworkPinned = wifiContent.getPinnedNetworks().includes(modelData.ssid);
return isThisNetworkPinned ? Theme.primary : Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
@@ -662,16 +681,24 @@ Rectangle {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {}));
const isCurrentlyPinned = pins["preferredWifi"] === modelData.ssid;
const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {}))
let pinnedList = wifiContent.normalizePinList(pins["preferredWifi"])
const pinIndex = pinnedList.indexOf(modelData.ssid)
if (isCurrentlyPinned) {
delete pins["preferredWifi"];
if (pinIndex !== -1) {
pinnedList.splice(pinIndex, 1)
} else {
pins["preferredWifi"] = modelData.ssid;
pinnedList.unshift(modelData.ssid)
if (pinnedList.length > wifiContent.maxPinnedNetworks)
pinnedList = pinnedList.slice(0, wifiContent.maxPinnedNetworks)
}
SettingsData.set("wifiNetworkPins", pins);
if (pinnedList.length > 0)
pins["preferredWifi"] = pinnedList
else
delete pins["preferredWifi"]
SettingsData.set("wifiNetworkPins", pins)
}
}
}

View File

@@ -24,10 +24,12 @@ BasePill {
property bool showMicPercent: widgetData?.showMicPercent !== undefined ? widgetData.showMicPercent : SettingsData.controlCenterShowMicPercent
property bool showBatteryIcon: widgetData?.showBatteryIcon !== undefined ? widgetData.showBatteryIcon : SettingsData.controlCenterShowBatteryIcon
property bool showPrinterIcon: widgetData?.showPrinterIcon !== undefined ? widgetData.showPrinterIcon : SettingsData.controlCenterShowPrinterIcon
property bool showScreenSharingIcon: widgetData?.showScreenSharingIcon !== undefined ? widgetData.showScreenSharingIcon : SettingsData.controlCenterShowScreenSharingIcon
property real touchpadThreshold: 100
property real micAccumulator: 0
property real volumeAccumulator: 0
property real brightnessAccumulator: 0
readonly property real vIconSize: Theme.barIconSize(root.barThickness, -4)
Loader {
active: root.showPrinterIcon
@@ -213,7 +215,25 @@ BasePill {
}
function hasNoVisibleIcons() {
return !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon && !root.showVpnIcon && !root.showBrightnessIcon && !root.showMicIcon && !root.showBatteryIcon && !root.showPrinterIcon;
if (root.showScreenSharingIcon && NiriService.hasCasts)
return false;
if (root.showNetworkIcon && NetworkService.networkAvailable)
return false;
if (root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected)
return false;
if (root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled)
return false;
if (root.showAudioIcon)
return false;
if (root.showMicIcon)
return false;
if (root.showBrightnessIcon && DisplayService.brightnessAvailable && root.hasPinnedBrightnessDevice())
return false;
if (root.showBatteryIcon && BatteryService.batteryAvailable)
return false;
if (root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs())
return false;
return true;
}
content: Component {
@@ -227,45 +247,75 @@ BasePill {
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: root.getNetworkIconName()
size: Theme.barIconSize(root.barThickness, -4)
color: root.getNetworkIconColor()
Item {
width: root.vIconSize
height: root.vIconSize
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showScreenSharingIcon && NiriService.hasCasts
DankIcon {
name: "screen_record"
size: root.vIconSize
color: NiriService.hasActiveCast ? Theme.primary : Theme.surfaceText
anchors.centerIn: parent
}
}
Item {
width: root.vIconSize
height: root.vIconSize
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showNetworkIcon && NetworkService.networkAvailable
DankIcon {
name: root.getNetworkIconName()
size: root.vIconSize
color: root.getNetworkIconColor()
anchors.centerIn: parent
}
}
DankIcon {
name: "vpn_lock"
size: Theme.barIconSize(root.barThickness, -4)
color: NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText
Item {
width: root.vIconSize
height: root.vIconSize
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected
DankIcon {
name: "vpn_lock"
size: root.vIconSize
color: NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText
anchors.centerIn: parent
}
}
DankIcon {
name: "bluetooth"
size: Theme.barIconSize(root.barThickness, -4)
color: BluetoothService.connected ? Theme.primary : Theme.surfaceText
Item {
width: root.vIconSize
height: root.vIconSize
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
DankIcon {
name: "bluetooth"
size: root.vIconSize
color: BluetoothService.connected ? Theme.primary : Theme.surfaceText
anchors.centerIn: parent
}
}
Rectangle {
width: audioIconV.implicitWidth + 4
height: audioIconV.implicitHeight + (root.showAudioPercent ? audioPercentV.implicitHeight : 0) + 4
color: "transparent"
Item {
width: root.vIconSize
height: root.vIconSize + (root.showAudioPercent ? audioPercentV.implicitHeight + 2 : 0)
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showAudioIcon
DankIcon {
id: audioIconV
name: root.getVolumeIconName()
size: Theme.barIconSize(root.barThickness, -4)
size: root.vIconSize
color: Theme.widgetIconColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 2
}
StyledText {
@@ -292,21 +342,19 @@ BasePill {
}
}
Rectangle {
width: micIconV.implicitWidth + 4
height: micIconV.implicitHeight + (root.showAudioPercent ? micPercentV.implicitHeight : 0) + 4
color: "transparent"
Item {
width: root.vIconSize
height: root.vIconSize + (root.showMicPercent ? micPercentV.implicitHeight + 2 : 0)
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showMicIcon
DankIcon {
id: micIconV
name: root.getMicIconName()
size: Theme.barIconSize(root.barThickness, -4)
size: root.vIconSize
color: root.getMicIconColor()
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 2
}
StyledText {
@@ -333,21 +381,19 @@ BasePill {
}
}
Rectangle {
width: brightnessIconV.implicitWidth + 4
height: brightnessIconV.implicitHeight + (root.showBrightnessPercent ? brightnessPercentV.implicitHeight : 0) + 4
color: "transparent"
Item {
width: root.vIconSize
height: root.vIconSize + (root.showBrightnessPercent ? brightnessPercentV.implicitHeight + 2 : 0)
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showBrightnessIcon && DisplayService.brightnessAvailable && root.hasPinnedBrightnessDevice()
DankIcon {
id: brightnessIconV
name: root.getBrightnessIconName()
size: Theme.barIconSize(root.barThickness, -4)
size: root.vIconSize
color: Theme.widgetIconColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 2
}
StyledText {
@@ -371,28 +417,46 @@ BasePill {
}
}
DankIcon {
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
size: Theme.barIconSize(root.barThickness, -4)
color: root.getBatteryIconColor()
Item {
width: root.vIconSize
height: root.vIconSize
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showBatteryIcon && BatteryService.batteryAvailable
DankIcon {
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
size: root.vIconSize
color: root.getBatteryIconColor()
anchors.centerIn: parent
}
}
DankIcon {
name: "print"
size: Theme.barIconSize(root.barThickness, -4)
color: Theme.primary
Item {
width: root.vIconSize
height: root.vIconSize
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs()
DankIcon {
name: "print"
size: root.vIconSize
color: Theme.primary
anchors.centerIn: parent
}
}
DankIcon {
name: "settings"
size: Theme.barIconSize(root.barThickness, -4)
color: root.isActive ? Theme.primary : Theme.widgetIconColor
Item {
width: root.vIconSize
height: root.vIconSize
anchors.horizontalCenter: parent.horizontalCenter
visible: root.hasNoVisibleIcons()
DankIcon {
name: "settings"
size: root.vIconSize
color: root.isActive ? Theme.primary : Theme.widgetIconColor
anchors.centerIn: parent
}
}
}
@@ -402,6 +466,14 @@ BasePill {
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: "screen_record"
size: Theme.barIconSize(root.barThickness, -4)
color: NiriService.hasActiveCast ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
visible: root.showScreenSharingIcon && NiriService.hasCasts
}
DankIcon {
id: networkIcon
name: root.getNetworkIconName()

View File

@@ -155,9 +155,17 @@ BasePill {
}
}
DankIcon {
anchors.centerIn: parent
size: 18
name: "sports_esports"
color: Theme.widgetTextColor
visible: root.isVerticalOrientation && activeWindow && activeWindow.appId && appIcon.status !== Image.Ready && Paths.isSteamApp(activeWindow.appId)
}
Text {
anchors.centerIn: parent
visible: root.isVerticalOrientation && activeWindow && activeWindow.appId && appIcon.status !== Image.Ready
visible: root.isVerticalOrientation && activeWindow && activeWindow.appId && appIcon.status !== Image.Ready && !Paths.isSteamApp(activeWindow.appId)
text: {
if (!activeWindow || !activeWindow.appId)
return "?";

View File

@@ -393,9 +393,19 @@ Item {
}
}
DankIcon {
anchors.left: parent.left
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness)) / 2) : Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
size: Theme.barIconSize(root.barThickness)
name: "sports_esports"
color: Theme.widgetTextColor
visible: !iconImg.visible && Paths.isSteamApp(appId)
}
Text {
anchors.centerIn: parent
visible: !iconImg.visible
visible: !iconImg.visible && !Paths.isSteamApp(appId)
text: {
root._desktopEntriesUpdateTrigger;
if (!appId)
@@ -628,9 +638,19 @@ Item {
}
}
DankIcon {
anchors.left: parent.left
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness)) / 2) : Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
size: Theme.barIconSize(root.barThickness)
name: "sports_esports"
color: Theme.widgetTextColor
visible: !iconImg.visible && Paths.isSteamApp(appId)
}
Text {
anchors.centerIn: parent
visible: !iconImg.visible
visible: !iconImg.visible && !Paths.isSteamApp(appId)
text: {
root._desktopEntriesUpdateTrigger;
if (!appId)

View File

@@ -265,6 +265,7 @@ Item {
if (!byApp[key]) {
const isQuickshell = keyBase === "org.quickshell";
const isSteamApp = Paths.isSteamApp(keyBase);
const moddedId = Paths.moddedAppId(keyBase);
const desktopEntry = DesktopEntries.heuristicLookup(moddedId);
const icon = Paths.getAppIcon(keyBase, desktopEntry);
@@ -272,6 +273,7 @@ Item {
"type": "icon",
"icon": icon,
"isQuickshell": isQuickshell,
"isSteamApp": isSteamApp,
"active": !!((w.activated || w.is_focused) || (CompositorService.isNiri && w.is_focused)),
"count": 1,
"windowId": w.address || w.id,
@@ -1135,7 +1137,7 @@ Item {
anchors.fill: parent
source: modelData.icon
opacity: modelData.active ? 1.0 : rowAppMouseArea.containsMouse ? 0.8 : 0.6
visible: !modelData.isQuickshell
visible: !modelData.isQuickshell && !modelData.isSteamApp
}
IconImage {
@@ -1151,6 +1153,22 @@ Item {
}
}
IconImage {
anchors.fill: parent
source: modelData.icon
opacity: modelData.active ? 1.0 : rowAppMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isSteamApp && modelData.icon
}
DankIcon {
anchors.centerIn: parent
size: root.appIconSize
name: "sports_esports"
color: Theme.widgetTextColor
opacity: modelData.active ? 1.0 : rowAppMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isSteamApp && !modelData.icon
}
MouseArea {
id: rowAppMouseArea
anchors.fill: parent
@@ -1229,7 +1247,7 @@ Item {
anchors.fill: parent
source: modelData.icon
opacity: modelData.active ? 1.0 : colAppMouseArea.containsMouse ? 0.8 : 0.6
visible: !modelData.isQuickshell
visible: !modelData.isQuickshell && !modelData.isSteamApp
}
IconImage {
@@ -1245,6 +1263,22 @@ Item {
}
}
IconImage {
anchors.fill: parent
source: modelData.icon
opacity: modelData.active ? 1.0 : colAppMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isSteamApp && modelData.icon
}
DankIcon {
anchors.centerIn: parent
size: root.appIconSize
name: "sports_esports"
color: Theme.widgetTextColor
opacity: modelData.active ? 1.0 : colAppMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isSteamApp && !modelData.icon
}
MouseArea {
id: colAppMouseArea
anchors.fill: parent

View File

@@ -327,11 +327,12 @@ Item {
clip: false
visible: !_noneAvailable && (!showNoPlayerNow)
ColumnLayout {
x: 72
y: 20
width: 484
height: 370
spacing: Theme.spacingXS
anchors.top: parent.top
anchors.topMargin: 20
anchors.horizontalCenter: parent.horizontalCenter
Item {
width: parent.width

View File

@@ -403,7 +403,7 @@ Item {
width: actualIconSize
height: actualIconSize
anchors.centerIn: parent
visible: iconImg.status !== Image.Ready
visible: iconImg.status !== Image.Ready && appData && appData.appId && !Paths.isSteamApp(appData.appId)
color: Theme.surfaceLight
radius: Theme.cornerRadius
border.width: 1
@@ -425,6 +425,14 @@ Item {
}
}
DankIcon {
anchors.centerIn: parent
size: actualIconSize
name: "sports_esports"
color: Theme.surfaceText
visible: iconImg.status !== Image.Ready && appData && appData.appId && Paths.isSteamApp(appData.appId)
}
Loader {
anchors.horizontalCenter: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? undefined : parent.horizontalCenter
anchors.verticalCenter: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? parent.verticalCenter : undefined

View File

@@ -1406,6 +1406,14 @@ Item {
anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.networkAvailable || (BluetoothService.available && BluetoothService.enabled) || (AudioService.sink && AudioService.sink.audio)
DankIcon {
name: "screen_record"
size: Theme.iconSize - 2
color: NiriService.hasActiveCast ? "white" : Qt.rgba(255, 255, 255, 0.5)
anchors.verticalCenter: parent.verticalCenter
visible: NiriService.hasCasts
}
DankIcon {
name: {
if (NetworkService.wifiToggling)

View File

@@ -262,9 +262,7 @@ DankOSD {
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
function onVolumeChanged() {
if (!vertSlider.dragging) {
vertSlider.value = Math.min(100, Math.round(AudioService.sink.audio.volume * 100));
}
vertSlider.value = Math.min(100, Math.round(AudioService.sink.audio.volume * 100));
}
}
}

View File

@@ -177,10 +177,11 @@ Item {
StyledText {
text: {
if (!SystemUpdateService.shellVersion)
if (!SystemUpdateService.shellVersion && !DMSService.cliVersion)
return "dms";
let version = SystemUpdateService.shellVersion;
let version = SystemUpdateService.shellVersion || "";
let cliVersion = DMSService.cliVersion || "";
// Debian/Ubuntu/OpenSUSE git format: 1.0.3+git2264.c5c5ce84
let match = version.match(/^([\d.]+)\+git(\d+)\./);
@@ -191,7 +192,25 @@ Item {
// Fedora COPR git format: 0.0.git.2267.d430cae9
match = version.match(/^[\d.]+\.git\.(\d+)\./);
if (match) {
return `dms (git) v1.0.3-${match[1]}`;
function extractBaseVersion(value) {
if (!value)
return "";
let baseMatch = value.match(/(\d+\.\d+\.\d+)/);
if (baseMatch)
return baseMatch[1];
baseMatch = value.match(/(\d+\.\d+)/);
if (baseMatch)
return baseMatch[1];
return "";
}
let baseVersion = extractBaseVersion(cliVersion);
if (!baseVersion)
baseVersion = extractBaseVersion(SystemUpdateService.semverVersion);
if (baseVersion) {
return `dms (git) v${baseVersion}-${match[1]}`;
}
return `dms (git) v${match[1]}`;
}
// Stable release format: 1.0.3
@@ -200,6 +219,18 @@ Item {
return `dms v${match[1]}`;
}
if (!version && cliVersion) {
match = cliVersion.match(/^([\d.]+)\+git(\d+)\./);
if (match) {
return `dms (git) v${match[1]}-${match[2]}`;
}
match = cliVersion.match(/^([\d.]+)$/);
if (match) {
return `dms v${match[1]}`;
}
return `dms ${cliVersion}`;
}
return `dms ${version}`;
}
font.pixelSize: Theme.fontSizeXLarge

View File

@@ -15,6 +15,7 @@ Item {
property string expandedVpnUuid: ""
property string expandedWifiSsid: ""
property string expandedEthDevice: ""
property int maxPinnedWifiNetworks: 3
Component.onCompleted: {
NetworkService.addRef();
@@ -30,6 +31,40 @@ Item {
vpnFileBrowserLoader.item.open();
}
function normalizePinList(value) {
if (Array.isArray(value))
return value.filter(v => v)
if (typeof value === "string" && value.length > 0)
return [value]
return []
}
function getPinnedWifiNetworks() {
const pins = SettingsData.wifiNetworkPins || {}
return normalizePinList(pins["preferredWifi"])
}
function toggleWifiPin(ssid) {
const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {}))
let pinnedList = normalizePinList(pins["preferredWifi"])
const pinIndex = pinnedList.indexOf(ssid)
if (pinIndex !== -1) {
pinnedList.splice(pinIndex, 1)
} else {
pinnedList.unshift(ssid)
if (pinnedList.length > maxPinnedWifiNetworks)
pinnedList = pinnedList.slice(0, maxPinnedWifiNetworks)
}
if (pinnedList.length > 0)
pins["preferredWifi"] = pinnedList
else
delete pins["preferredWifi"]
SettingsData.set("wifiNetworkPins", pins)
}
LazyLoader {
id: vpnFileBrowserLoader
active: false
@@ -1025,15 +1060,19 @@ Item {
model: {
const ssid = NetworkService.currentWifiSSID;
const networks = NetworkService.wifiNetworks || [];
const pins = SettingsData.wifiNetworkPins || {};
const pinnedSSID = pins["preferredWifi"];
const pinnedList = networkTab.getPinnedWifiNetworks();
let sorted = [...networks];
sorted.sort((a, b) => {
if (a.ssid === pinnedSSID && b.ssid !== pinnedSSID)
return -1;
if (b.ssid === pinnedSSID && a.ssid !== pinnedSSID)
return 1;
const aPinnedIndex = pinnedList.indexOf(a.ssid)
const bPinnedIndex = pinnedList.indexOf(b.ssid)
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
if (aPinnedIndex === -1)
return 1
if (bPinnedIndex === -1)
return -1
return aPinnedIndex - bPinnedIndex
}
if (a.ssid === ssid)
return -1;
if (b.ssid === ssid)
@@ -1049,7 +1088,7 @@ Item {
required property int index
readonly property bool isConnected: modelData.ssid === NetworkService.currentWifiSSID
readonly property bool isPinned: (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid
readonly property bool isPinned: networkTab.getPinnedWifiNetworks().includes(modelData.ssid)
readonly property bool isExpanded: networkTab.expandedWifiSsid === modelData.ssid
width: parent.width
@@ -1224,13 +1263,7 @@ Item {
buttonSize: 28
iconColor: isPinned ? Theme.primary : Theme.surfaceVariantText
onClicked: {
const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {}));
if (isPinned) {
delete pins["preferredWifi"];
} else {
pins["preferredWifi"] = modelData.ssid;
}
SettingsData.set("wifiNetworkPins", pins);
networkTab.toggleWifiPin(modelData.ssid)
}
}

View File

@@ -270,7 +270,9 @@ FloatingWindow {
root.updateFilteredPlugins();
return;
}
thirdPartyConfirmModal.visible = true;
thirdPartyConfirmLoader.active = true;
if (thirdPartyConfirmLoader.item)
thirdPartyConfirmLoader.item.show();
}
}
@@ -668,119 +670,132 @@ FloatingWindow {
}
}
FloatingWindow {
id: thirdPartyConfirmModal
LazyLoader {
id: thirdPartyConfirmLoader
active: false
objectName: "thirdPartyConfirm"
title: I18n.tr("Third-Party Plugin Warning")
implicitWidth: 500
implicitHeight: 350
color: Theme.surfaceContainer
visible: false
FloatingWindow {
id: thirdPartyConfirmModal
FocusScope {
anchors.fill: parent
focus: true
Keys.onPressed: event => {
if (event.key === Qt.Key_Escape) {
thirdPartyConfirmModal.visible = false;
event.accepted = true;
}
function show() {
visible = true;
}
Column {
function hide() {
visible = false;
}
objectName: "thirdPartyConfirm"
title: I18n.tr("Third-Party Plugin Warning")
implicitWidth: 500
implicitHeight: 350
color: Theme.surfaceContainer
visible: false
FocusScope {
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingL
focus: true
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "warning"
size: Theme.iconSize
color: Theme.warning
anchors.verticalCenter: parent.verticalCenter
Keys.onPressed: event => {
if (event.key === Qt.Key_Escape) {
thirdPartyConfirmModal.hide();
event.accepted = true;
}
StyledText {
text: I18n.tr("Third-Party Plugin Warning")
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: parent.width - parent.spacing * 2 - Theme.iconSize - parent.children[1].implicitWidth - closeConfirmBtn.width
height: 1
}
DankActionButton {
id: closeConfirmBtn
iconName: "close"
iconSize: Theme.iconSize - 2
iconColor: Theme.outline
anchors.verticalCenter: parent.verticalCenter
onClicked: thirdPartyConfirmModal.visible = false
}
}
StyledText {
width: parent.width
text: I18n.tr("Third-party plugins are created by the community and are not officially supported by DankMaterialShell.\n\nThese plugins may pose security and privacy risks - install at your own risk.")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
wrapMode: Text.WordWrap
}
Column {
width: parent.width
spacing: Theme.spacingS
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingL
StyledText {
text: I18n.tr("• Plugins may contain bugs or security issues")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "warning"
size: Theme.iconSize
color: Theme.warning
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Third-Party Plugin Warning")
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: parent.width - parent.spacing * 2 - Theme.iconSize - parent.children[1].implicitWidth - closeConfirmBtn.width
height: 1
}
DankActionButton {
id: closeConfirmBtn
iconName: "close"
iconSize: Theme.iconSize - 2
iconColor: Theme.outline
anchors.verticalCenter: parent.verticalCenter
onClicked: thirdPartyConfirmModal.hide()
}
}
StyledText {
text: I18n.tr("• Review code before installation when possible")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
width: parent.width
text: I18n.tr("Third-party plugins are created by the community and are not officially supported by DankMaterialShell.\n\nThese plugins may pose security and privacy risks - install at your own risk.")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
wrapMode: Text.WordWrap
}
StyledText {
text: I18n.tr("• Install only from trusted sources")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
Column {
width: parent.width
spacing: Theme.spacingS
Item {
width: parent.width
height: parent.height - parent.spacing * 3 - y
}
StyledText {
text: I18n.tr("• Plugins may contain bugs or security issues")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
Row {
anchors.right: parent.right
spacing: Theme.spacingM
StyledText {
text: I18n.tr("• Review code before installation when possible")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
DankButton {
text: I18n.tr("Cancel")
iconName: "close"
onClicked: thirdPartyConfirmModal.visible = false
StyledText {
text: I18n.tr("• Install only from trusted sources")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
DankButton {
text: I18n.tr("I Understand")
iconName: "check"
onClicked: {
SessionData.setShowThirdPartyPlugins(true);
root.updateFilteredPlugins();
thirdPartyConfirmModal.visible = false;
Item {
width: parent.width
height: parent.height - parent.spacing * 3 - y
}
Row {
anchors.right: parent.right
spacing: Theme.spacingM
DankButton {
text: I18n.tr("Cancel")
iconName: "close"
onClicked: thirdPartyConfirmModal.hide()
}
DankButton {
text: I18n.tr("I Understand")
iconName: "check"
onClicked: {
SessionData.setShowThirdPartyPlugins(true);
root.updateFilteredPlugins();
thirdPartyConfirmModal.hide();
}
}
}
}

View File

@@ -383,6 +383,7 @@ Item {
widgetObj.showMicPercent = SettingsData.controlCenterShowMicPercent;
widgetObj.showBatteryIcon = SettingsData.controlCenterShowBatteryIcon;
widgetObj.showPrinterIcon = SettingsData.controlCenterShowPrinterIcon;
widgetObj.showScreenSharingIcon = SettingsData.controlCenterShowScreenSharingIcon;
}
if (widgetId === "diskUsage")
widgetObj.mountPath = "/";
@@ -443,6 +444,7 @@ Item {
newWidget.showMicPercent = widget.showMicPercent ?? SettingsData.controlCenterShowMicPercent;
newWidget.showBatteryIcon = widget.showBatteryIcon ?? SettingsData.controlCenterShowBatteryIcon;
newWidget.showPrinterIcon = widget.showPrinterIcon ?? SettingsData.controlCenterShowPrinterIcon;
newWidget.showScreenSharingIcon = widget.showScreenSharingIcon ?? SettingsData.controlCenterShowScreenSharingIcon;
}
widgets[i] = newWidget;
break;
@@ -499,6 +501,7 @@ Item {
newWidget.showMicPercent = widget.showMicPercent ?? SettingsData.controlCenterShowMicPercent;
newWidget.showBatteryIcon = widget.showBatteryIcon ?? SettingsData.controlCenterShowBatteryIcon;
newWidget.showPrinterIcon = widget.showPrinterIcon ?? SettingsData.controlCenterShowPrinterIcon;
newWidget.showScreenSharingIcon = widget.showScreenSharingIcon ?? SettingsData.controlCenterShowScreenSharingIcon;
}
widgets[widgetIndex] = newWidget;
setWidgetsForSection(sectionId, widgets);
@@ -577,6 +580,7 @@ Item {
newWidget.showMicPercent = widget.showMicPercent ?? SettingsData.controlCenterShowMicPercent;
newWidget.showBatteryIcon = widget.showBatteryIcon ?? SettingsData.controlCenterShowBatteryIcon;
newWidget.showPrinterIcon = widget.showPrinterIcon ?? SettingsData.controlCenterShowPrinterIcon;
newWidget.showScreenSharingIcon = widget.showScreenSharingIcon ?? SettingsData.controlCenterShowScreenSharingIcon;
}
widgets[widgetIndex] = newWidget;
setWidgetsForSection(sectionId, widgets);
@@ -608,7 +612,8 @@ Item {
"showMicIcon": widget.showMicIcon ?? SettingsData.controlCenterShowMicIcon,
"showMicPercent": widget.showMicPercent ?? SettingsData.controlCenterShowMicPercent,
"showBatteryIcon": widget.showBatteryIcon ?? SettingsData.controlCenterShowBatteryIcon,
"showPrinterIcon": widget.showPrinterIcon ?? SettingsData.controlCenterShowPrinterIcon
"showPrinterIcon": widget.showPrinterIcon ?? SettingsData.controlCenterShowPrinterIcon,
"showScreenSharingIcon": widget.showScreenSharingIcon ?? SettingsData.controlCenterShowScreenSharingIcon
};
newWidget[settingName] = value;
@@ -675,6 +680,7 @@ Item {
newWidget.showMicPercent = widget.showMicPercent ?? SettingsData.controlCenterShowMicPercent;
newWidget.showBatteryIcon = widget.showBatteryIcon ?? SettingsData.controlCenterShowBatteryIcon;
newWidget.showPrinterIcon = widget.showPrinterIcon ?? SettingsData.controlCenterShowPrinterIcon;
newWidget.showScreenSharingIcon = widget.showScreenSharingIcon ?? SettingsData.controlCenterShowScreenSharingIcon;
}
widgets[widgetIndex] = newWidget;
setWidgetsForSection(sectionId, widgets);
@@ -735,6 +741,7 @@ Item {
newWidget.showMicPercent = widget.showMicPercent ?? SettingsData.controlCenterShowMicPercent;
newWidget.showBatteryIcon = widget.showBatteryIcon ?? SettingsData.controlCenterShowBatteryIcon;
newWidget.showPrinterIcon = widget.showPrinterIcon ?? SettingsData.controlCenterShowPrinterIcon;
newWidget.showScreenSharingIcon = widget.showScreenSharingIcon ?? SettingsData.controlCenterShowScreenSharingIcon;
}
widgets[widgetIndex] = newWidget;
setWidgetsForSection(sectionId, widgets);
@@ -795,6 +802,7 @@ Item {
newWidget.showMicPercent = widget.showMicPercent ?? SettingsData.controlCenterShowMicPercent;
newWidget.showBatteryIcon = widget.showBatteryIcon ?? SettingsData.controlCenterShowBatteryIcon;
newWidget.showPrinterIcon = widget.showPrinterIcon ?? SettingsData.controlCenterShowPrinterIcon;
newWidget.showScreenSharingIcon = widget.showScreenSharingIcon ?? SettingsData.controlCenterShowScreenSharingIcon;
}
widgets[i] = newWidget;
widget = newWidget;
@@ -867,6 +875,8 @@ Item {
item.showBatteryIcon = widget.showBatteryIcon;
if (widget.showPrinterIcon !== undefined)
item.showPrinterIcon = widget.showPrinterIcon;
if (widget.showScreenSharingIcon !== undefined)
item.showScreenSharingIcon = widget.showScreenSharingIcon;
if (widget.minimumWidth !== undefined)
item.minimumWidth = widget.minimumWidth;
if (widget.showSwap !== undefined)

View File

@@ -875,6 +875,11 @@ Column {
icon: "print",
label: I18n.tr("Printer"),
setting: "showPrinterIcon"
},
{
icon: "screen_record",
label: I18n.tr("Screen Sharing"),
setting: "showScreenSharingIcon"
}
]
@@ -907,6 +912,8 @@ Column {
return wd?.showBatteryIcon ?? SettingsData.controlCenterShowBatteryIcon;
case "showPrinterIcon":
return wd?.showPrinterIcon ?? SettingsData.controlCenterShowPrinterIcon;
case "showScreenSharingIcon":
return wd?.showScreenSharingIcon ?? SettingsData.controlCenterShowScreenSharingIcon;
default:
return false;
}

View File

@@ -11,7 +11,7 @@ Singleton {
id: root
readonly property string currentVersion: "1.2"
readonly property bool changelogEnabled: true
readonly property bool changelogEnabled: false
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/DankMaterialShell"
readonly property string changelogMarkerPath: configDir + "/.changelog-" + currentVersion

View File

@@ -27,6 +27,10 @@ Singleton {
property bool inOverview: false
property var casts: []
property bool hasCasts: casts.length > 0
property bool hasActiveCast: casts.some(c => c.is_active)
property int currentKeyboardLayoutIndex: 0
property var keyboardLayoutNames: []
@@ -356,6 +360,15 @@ Singleton {
case 'ScreenshotCaptured':
handleScreenshotCaptured(event.ScreenshotCaptured);
break;
case 'CastsChanged':
handleCastsChanged(event.CastsChanged);
break;
case 'CastStartedOrChanged':
handleCastStartedOrChanged(event.CastStartedOrChanged);
break;
case 'CastStopped':
handleCastStopped(event.CastStopped);
break;
}
}
@@ -649,6 +662,28 @@ Singleton {
}
}
function handleCastsChanged(data) {
casts = data.casts || [];
}
function handleCastStartedOrChanged(data) {
if (!data.cast)
return;
const cast = data.cast;
const existingIndex = casts.findIndex(c => c.stream_id === cast.stream_id);
if (existingIndex >= 0) {
const updatedCasts = [...casts];
updatedCasts[existingIndex] = cast;
casts = updatedCasts;
} else {
casts = [...casts, cast];
}
}
function handleCastStopped(data) {
casts = casts.filter(c => c.stream_id !== data.stream_id);
}
function updateCurrentOutputWorkspaces() {
if (!currentOutput) {
currentOutputWorkspaces = allWorkspaces;

View File

@@ -1 +1 @@
v1.2.2
v1.4-unstable

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -240,7 +240,7 @@
"All displays": "Tutti gli schermi"
},
"Allow clicks to pass through the widget": {
"Allow clicks to pass through the widget": ""
"Allow clicks to pass through the widget": "Consenti il passaggio dei clic attraverso il widget"
},
"Alt+←/Backspace: Back • F1/I: File Info • F10: Help • Esc: Close": {
"Alt+←/Backspace: Back • F1/I: File Info • F10: Help • Esc: Close": "Alt+←/Backspace: Indietro • F1/I: File Info • F10: Aiuto • Esc: Chiudi"
@@ -279,7 +279,7 @@
"Anonymous Identity (optional)": "Identità anonima (facoltativa)"
},
"App ID Substitutions": {
"App ID Substitutions": ""
"App ID Substitutions": "Sostituzioni App ID"
},
"App Launcher": {
"App Launcher": "App Launcher"
@@ -729,7 +729,7 @@
"Click Import to add a .ovpn or .conf": "Clicca su Importa per aggiungere un file .ovpn o .conf"
},
"Click Through": {
"Click Through": ""
"Click Through": "Clic passanti"
},
"Click any shortcut to edit. Changes save to %1": {
"Click any shortcut to edit. Changes save to %1": "Clicca su qualsiasi scorciatoia per modificare. Le modifiche vengono salvate in %1"
@@ -1797,7 +1797,7 @@
"Grid Columns": "Colonne Griglia"
},
"Group": {
"Group": ""
"Group": "Gruppo"
},
"Group Workspace Apps": {
"Group Workspace Apps": "Raggruppa App per Spazio di Lavoro"
@@ -1809,13 +1809,13 @@
"Group multiple windows of the same app together with a window count indicator": "Raggruppa molteplici finestre della stessa app con un indicatore del numero di finestre"
},
"Group removed": {
"Group removed": ""
"Group removed": "Gruppo rimosso"
},
"Group repeated application icons in unfocused workspaces": {
"Group repeated application icons in unfocused workspaces": "Raggruppa le icone delle applicazioni duplicate negli spazi di lavoro non attivi"
},
"Groups": {
"Groups": ""
"Groups": "Gruppi"
},
"HDR (EDID)": {
"HDR (EDID)": "HDR (EDID)"
@@ -2214,7 +2214,7 @@
"Manual Show/Hide": "Mostra/Nascondi Manuale"
},
"Map window class names to icon names for proper icon display": {
"Map window class names to icon names for proper icon display": ""
"Map window class names to icon names for proper icon display": "Associa i nomi delle classi delle finestre ai nomi delle icone per una corretta visualizzazione"
},
"Margin": {
"Margin": "Margini"
@@ -2439,7 +2439,7 @@
"New York, NY": "New York, NY"
},
"New group name...": {
"New group name...": ""
"New group name...": "Nome del nuovo gruppo..."
},
"Next Transition": {
"Next Transition": "Prossima Transizione"
@@ -2673,7 +2673,7 @@
"Options": "Opzioni"
},
"Organize widgets into collapsible groups": {
"Organize widgets into collapsible groups": ""
"Organize widgets into collapsible groups": "Organizza i widget in gruppi comprimibili"
},
"Other": {
"Other": "Altro"
@@ -2748,7 +2748,7 @@
"Password": "Password"
},
"Pattern": {
"Pattern": ""
"Pattern": "Pattern"
},
"Pause": {
"Pause": "Pausa"
@@ -3012,7 +3012,7 @@
"Repeat": "Ripetizione"
},
"Replacement": {
"Replacement": ""
"Replacement": "Sostituzione"
},
"Report": {
"Report": "Riepilogo"
@@ -3648,7 +3648,7 @@
"Sync Mode with Portal": "Modalità Sync con Portale"
},
"Sync Position Across Screens": {
"Sync Position Across Screens": ""
"Sync Position Across Screens": "Sincronizza la posizione tra gli schermi"
},
"Sync dark mode with settings portals for system-wide theme hints": {
"Sync dark mode with settings portals for system-wide theme hints": "Sincronizza tema scuro con impostazioni di sistema"
@@ -3876,7 +3876,7 @@
"Unfocused Color": "Colore Inattivo"
},
"Ungrouped": {
"Ungrouped": ""
"Ungrouped": "Non Raggruppato"
},
"Uninstall Plugin": {
"Uninstall Plugin": "Disinstalla Plugin"
@@ -3978,13 +3978,13 @@
"Use light theme instead of dark theme": "Usa tema chiaro invece del tema scuro"
},
"Use smaller notification cards": {
"Use smaller notification cards": ""
"Use smaller notification cards": "Usa schede di notifica più piccole"
},
"Use sound theme from system settings": {
"Use sound theme from system settings": "Usa tema di suoni dalle impostazioni di sistema"
},
"Use the same position and size on all displays": {
"Use the same position and size on all displays": ""
"Use the same position and size on all displays": "Usa la stessa posizione e dimensione su tutti gli schermi"
},
"Use trigger prefix to activate": {
"Use trigger prefix to activate": "Usa il prefisso attivatore per attivare"
@@ -4553,7 +4553,7 @@
"No wallpaper selected": "Nessuno sfondo selezionato"
},
"notification center tab": {
"Current": "Attuale",
"Current": "Attuali",
"History": "Cronologia"
},
"notification history filter": {

View File

@@ -240,7 +240,7 @@
"All displays": "所有显示器"
},
"Allow clicks to pass through the widget": {
"Allow clicks to pass through the widget": ""
"Allow clicks to pass through the widget": "允许鼠标穿透部件"
},
"Alt+←/Backspace: Back • F1/I: File Info • F10: Help • Esc: Close": {
"Alt+←/Backspace: Back • F1/I: File Info • F10: Help • Esc: Close": "Alt+←/退格: 返回 • F1/I: 文件信息 • F10: 帮助 • Esc: 关闭"
@@ -279,7 +279,7 @@
"Anonymous Identity (optional)": "匿名身份(可选)"
},
"App ID Substitutions": {
"App ID Substitutions": ""
"App ID Substitutions": "应用ID替换"
},
"App Launcher": {
"App Launcher": "启动器"
@@ -729,7 +729,7 @@
"Click Import to add a .ovpn or .conf": "点击导入添加 .ovpn 或 .conf 文件"
},
"Click Through": {
"Click Through": ""
"Click Through": "鼠标穿透"
},
"Click any shortcut to edit. Changes save to %1": {
"Click any shortcut to edit. Changes save to %1": "点击任意快捷方式以编辑。更改将保存至%1。"
@@ -1797,7 +1797,7 @@
"Grid Columns": "网格列"
},
"Group": {
"Group": ""
"Group": "分组"
},
"Group Workspace Apps": {
"Group Workspace Apps": "分组工作区应用"
@@ -1809,13 +1809,13 @@
"Group multiple windows of the same app together with a window count indicator": "将同一应用的多个窗口合并显示,并标注窗口数量"
},
"Group removed": {
"Group removed": ""
"Group removed": "分组已移除"
},
"Group repeated application icons in unfocused workspaces": {
"Group repeated application icons in unfocused workspaces": "在不聚焦的工作区中将重复应用图标分组"
},
"Groups": {
"Groups": ""
"Groups": "分组"
},
"HDR (EDID)": {
"HDR (EDID)": "HDREDID"
@@ -2214,7 +2214,7 @@
"Manual Show/Hide": "手动显示/隐藏"
},
"Map window class names to icon names for proper icon display": {
"Map window class names to icon names for proper icon display": ""
"Map window class names to icon names for proper icon display": "将窗口类名称映射到图标名称以实现正确的图标显示"
},
"Margin": {
"Margin": "边距"
@@ -2439,7 +2439,7 @@
"New York, NY": "纽约,美国纽约州"
},
"New group name...": {
"New group name...": ""
"New group name...": "新分组名..."
},
"Next Transition": {
"Next Transition": "下一过渡"
@@ -2673,7 +2673,7 @@
"Options": "选项"
},
"Organize widgets into collapsible groups": {
"Organize widgets into collapsible groups": ""
"Organize widgets into collapsible groups": "将部件组织成可折叠分组"
},
"Other": {
"Other": "其他"
@@ -2748,7 +2748,7 @@
"Password": "密码"
},
"Pattern": {
"Pattern": ""
"Pattern": "模式"
},
"Pause": {
"Pause": "暂停"
@@ -3012,7 +3012,7 @@
"Repeat": "重复"
},
"Replacement": {
"Replacement": ""
"Replacement": "替换"
},
"Report": {
"Report": "报告"
@@ -3648,7 +3648,7 @@
"Sync Mode with Portal": "同步系统深色模式"
},
"Sync Position Across Screens": {
"Sync Position Across Screens": ""
"Sync Position Across Screens": "在显示器间同步位置"
},
"Sync dark mode with settings portals for system-wide theme hints": {
"Sync dark mode with settings portals for system-wide theme hints": "随系统设置开启深色模式,以适配全局主题"
@@ -3876,7 +3876,7 @@
"Unfocused Color": "未聚焦颜色"
},
"Ungrouped": {
"Ungrouped": ""
"Ungrouped": "已解除分组"
},
"Uninstall Plugin": {
"Uninstall Plugin": "卸载插件"
@@ -3978,13 +3978,13 @@
"Use light theme instead of dark theme": "使用浅色主题替代深色主题"
},
"Use smaller notification cards": {
"Use smaller notification cards": ""
"Use smaller notification cards": "使用更小的通知卡"
},
"Use sound theme from system settings": {
"Use sound theme from system settings": "使用系统设置中的声音主题"
},
"Use the same position and size on all displays": {
"Use the same position and size on all displays": ""
"Use the same position and size on all displays": "在所有显示器上使用同样的位置与大小"
},
"Use trigger prefix to activate": {
"Use trigger prefix to activate": "使用触发前缀以激活"