mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-25 05:52:50 -05:00
Compare commits
21 Commits
master
...
e022c04519
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e022c04519 | ||
|
|
f534384e5e | ||
|
|
a25cdb43d5 | ||
|
|
4e9b4ca400 | ||
|
|
5bab1c98b1 | ||
|
|
2284bb002f | ||
|
|
b0611d6104 | ||
|
|
27965862d6 | ||
|
|
e74a901e05 | ||
|
|
77794deb2c | ||
|
|
1c10746e50 | ||
|
|
8ecb7282b9 | ||
|
|
9b3fa804ab | ||
|
|
b2ad31a27e | ||
|
|
db17e4cb14 | ||
|
|
1b7dcf56a8 | ||
|
|
502bb88e92 | ||
|
|
b76d0ce97d | ||
|
|
fa66d330cf | ||
|
|
157eab2d07 | ||
|
|
f50ad2dc22 |
@@ -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) {
|
||||
|
||||
@@ -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"},
|
||||
}
|
||||
|
||||
20
core/internal/utils/dbus.go
Normal file
20
core/internal/utils/dbus.go
Normal 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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
14
flake.nix
14
flake.nix
@@ -61,11 +61,13 @@
|
||||
(builtins.substring 6 2 longDate)
|
||||
];
|
||||
version =
|
||||
pkgs.lib.removePrefix "v" (pkgs.lib.trim (builtins.readFile ./quickshell/VERSION))
|
||||
+ "+date="
|
||||
+ mkDate (self.lastModifiedDate or "19700101")
|
||||
+ "_"
|
||||
+ (self.shortRev or "dirty");
|
||||
let
|
||||
rawVersion = pkgs.lib.removePrefix "v" (pkgs.lib.trim (builtins.readFile ./quickshell/VERSION));
|
||||
cleanVersion = builtins.replaceStrings [ " " ] [ "" ] rawVersion;
|
||||
dateSuffix = "+date=" + mkDate (self.lastModifiedDate or "19700101");
|
||||
revSuffix = "_" + (self.shortRev or "dirty");
|
||||
in
|
||||
"${cleanVersion}${dateSuffix}${revSuffix}";
|
||||
in
|
||||
{
|
||||
dms-shell = pkgs.buildGoModule (
|
||||
@@ -83,7 +85,7 @@
|
||||
ldflags = [
|
||||
"-s"
|
||||
"-w"
|
||||
"-X main.Version=${version}"
|
||||
"-X 'main.Version=${version}'"
|
||||
];
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -203,6 +203,8 @@ Item {
|
||||
|
||||
Component.onCompleted: {
|
||||
dockRecreateDebounce.start();
|
||||
// Force PolkitService singleton to initialize
|
||||
PolkitService.polkitAvailable;
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -315,19 +317,44 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
WifiPasswordModal {
|
||||
id: wifiPasswordModal
|
||||
LazyLoader {
|
||||
id: wifiPasswordModalLoader
|
||||
active: false
|
||||
|
||||
Component.onCompleted: {
|
||||
PopoutService.wifiPasswordModal = wifiPasswordModal;
|
||||
PopoutService.wifiPasswordModalLoader = wifiPasswordModalLoader;
|
||||
}
|
||||
|
||||
WifiPasswordModal {
|
||||
id: wifiPasswordModalItem
|
||||
|
||||
Component.onCompleted: {
|
||||
PopoutService.wifiPasswordModal = wifiPasswordModalItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PolkitAuthModal {
|
||||
id: polkitAuthModal
|
||||
LazyLoader {
|
||||
id: polkitAuthModalLoader
|
||||
active: false
|
||||
|
||||
Component.onCompleted: {
|
||||
PopoutService.polkitAuthModal = polkitAuthModal;
|
||||
PolkitAuthModal {
|
||||
id: polkitAuthModal
|
||||
|
||||
Component.onCompleted: {
|
||||
PopoutService.polkitAuthModal = polkitAuthModal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: PolkitService.agent
|
||||
enabled: PolkitService.polkitAvailable
|
||||
|
||||
function onAuthenticationRequestStarted() {
|
||||
polkitAuthModalLoader.active = true;
|
||||
if (polkitAuthModalLoader.item)
|
||||
polkitAuthModalLoader.item.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,17 +376,21 @@ Item {
|
||||
const now = Date.now();
|
||||
const timeSinceLastPrompt = now - lastCredentialsTime;
|
||||
|
||||
if (wifiPasswordModal.visible && timeSinceLastPrompt < 1000) {
|
||||
wifiPasswordModalLoader.active = true;
|
||||
if (!wifiPasswordModalLoader.item)
|
||||
return;
|
||||
|
||||
if (wifiPasswordModalLoader.item.visible && timeSinceLastPrompt < 1000) {
|
||||
NetworkService.cancelCredentials(lastCredentialsToken);
|
||||
lastCredentialsToken = token;
|
||||
lastCredentialsTime = now;
|
||||
wifiPasswordModal.showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService, fieldsInfo);
|
||||
wifiPasswordModalLoader.item.showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService, fieldsInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
lastCredentialsToken = token;
|
||||
lastCredentialsTime = now;
|
||||
wifiPasswordModal.showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService, fieldsInfo);
|
||||
wifiPasswordModalLoader.item.showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService, fieldsInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,17 +473,15 @@ Item {
|
||||
PopoutService.settingsModalLoader = settingsModalLoader;
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
if (active && item) {
|
||||
PopoutService.settingsModal = item;
|
||||
PopoutService._onSettingsModalLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
SettingsModal {
|
||||
id: settingsModal
|
||||
property bool wasShown: false
|
||||
|
||||
Component.onCompleted: {
|
||||
PopoutService.settingsModal = settingsModal;
|
||||
PopoutService._onSettingsModalLoaded();
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
wasShown = true;
|
||||
|
||||
@@ -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;
|
||||
@@ -797,11 +800,9 @@ Item {
|
||||
const modal = PopoutService.settingsModal;
|
||||
if (modal) {
|
||||
if (type === "wallpaper") {
|
||||
modal.wallpaperBrowser.allowStacking = false;
|
||||
modal.wallpaperBrowser.open();
|
||||
modal.openWallpaperBrowser(false);
|
||||
} else if (type === "profile") {
|
||||
modal.profileBrowser.allowStacking = false;
|
||||
modal.profileBrowser.open();
|
||||
modal.openProfileBrowser(false);
|
||||
}
|
||||
} else {
|
||||
PopoutService.openSettings();
|
||||
|
||||
@@ -128,7 +128,7 @@ FloatingWindow {
|
||||
iconName: "open_in_new"
|
||||
backgroundColor: Theme.surfaceContainerHighest
|
||||
textColor: Theme.surfaceText
|
||||
onClicked: Qt.openUrlExternally("https://danklinux.com/blog/v1.2-release")
|
||||
onClicked: Qt.openUrlExternally("https://danklinux.com/blog/v1-2-release")
|
||||
}
|
||||
|
||||
DankButton {
|
||||
|
||||
@@ -74,9 +74,7 @@ Rectangle {
|
||||
if (root.parentModal) {
|
||||
root.parentModal.allowFocusOverride = true;
|
||||
root.parentModal.shouldHaveFocus = false;
|
||||
if (root.parentModal.profileBrowser) {
|
||||
root.parentModal.profileBrowser.open();
|
||||
}
|
||||
root.parentModal.openProfileBrowser();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,26 @@ import qs.Widgets
|
||||
FloatingWindow {
|
||||
id: settingsModal
|
||||
|
||||
property alias profileBrowser: profileBrowser
|
||||
property alias wallpaperBrowser: wallpaperBrowser
|
||||
property var profileBrowser: profileBrowserLoader.item
|
||||
property var wallpaperBrowser: wallpaperBrowserLoader.item
|
||||
|
||||
function openProfileBrowser(allowStacking) {
|
||||
profileBrowserLoader.active = true;
|
||||
if (!profileBrowserLoader.item)
|
||||
return;
|
||||
if (allowStacking !== undefined)
|
||||
profileBrowserLoader.item.allowStacking = allowStacking;
|
||||
profileBrowserLoader.item.open();
|
||||
}
|
||||
|
||||
function openWallpaperBrowser(allowStacking) {
|
||||
wallpaperBrowserLoader.active = true;
|
||||
if (!wallpaperBrowserLoader.item)
|
||||
return;
|
||||
if (allowStacking !== undefined)
|
||||
wallpaperBrowserLoader.item.allowStacking = allowStacking;
|
||||
wallpaperBrowserLoader.item.open();
|
||||
}
|
||||
property alias sidebar: sidebar
|
||||
property int currentTabIndex: 0
|
||||
property bool shouldHaveFocus: visible
|
||||
@@ -96,41 +114,51 @@ FloatingWindow {
|
||||
}
|
||||
}
|
||||
|
||||
FileBrowserModal {
|
||||
id: profileBrowser
|
||||
LazyLoader {
|
||||
id: profileBrowserLoader
|
||||
active: false
|
||||
|
||||
allowStacking: true
|
||||
parentModal: settingsModal
|
||||
browserTitle: I18n.tr("Select Profile Image", "profile image file browser title")
|
||||
browserIcon: "person"
|
||||
browserType: "profile"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
PortalService.setProfileImage(path);
|
||||
close();
|
||||
}
|
||||
onDialogClosed: () => {
|
||||
allowStacking = true;
|
||||
FileBrowserModal {
|
||||
id: profileBrowserItem
|
||||
|
||||
allowStacking: true
|
||||
parentModal: settingsModal
|
||||
browserTitle: I18n.tr("Select Profile Image", "profile image file browser title")
|
||||
browserIcon: "person"
|
||||
browserType: "profile"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
PortalService.setProfileImage(path);
|
||||
close();
|
||||
}
|
||||
onDialogClosed: () => {
|
||||
allowStacking = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileBrowserModal {
|
||||
id: wallpaperBrowser
|
||||
LazyLoader {
|
||||
id: wallpaperBrowserLoader
|
||||
active: false
|
||||
|
||||
allowStacking: true
|
||||
parentModal: settingsModal
|
||||
browserTitle: I18n.tr("Select Wallpaper", "wallpaper file browser title")
|
||||
browserIcon: "wallpaper"
|
||||
browserType: "wallpaper"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
SessionData.setWallpaper(path);
|
||||
close();
|
||||
}
|
||||
onDialogClosed: () => {
|
||||
allowStacking = true;
|
||||
FileBrowserModal {
|
||||
id: wallpaperBrowserItem
|
||||
|
||||
allowStacking: true
|
||||
parentModal: settingsModal
|
||||
browserTitle: I18n.tr("Select Wallpaper", "wallpaper file browser title")
|
||||
browserIcon: "wallpaper"
|
||||
browserType: "wallpaper"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
SessionData.setWallpaper(path);
|
||||
close();
|
||||
}
|
||||
onDialogClosed: () => {
|
||||
allowStacking = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -687,8 +714,8 @@ Rectangle {
|
||||
if (modelData.secured && !modelData.saved) {
|
||||
if (DMSService.apiVersion >= 7) {
|
||||
NetworkService.connectToWifi(modelData.ssid);
|
||||
} else if (PopoutService.wifiPasswordModal) {
|
||||
PopoutService.wifiPasswordModal.show(modelData.ssid);
|
||||
} else {
|
||||
PopoutService.showWifiPasswordModal(modelData.ssid);
|
||||
}
|
||||
} else {
|
||||
NetworkService.connectToWifi(modelData.ssid);
|
||||
@@ -749,8 +776,8 @@ Rectangle {
|
||||
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
||||
if (DMSService.apiVersion >= 7) {
|
||||
NetworkService.connectToWifi(networkContextMenu.currentSSID);
|
||||
} else if (PopoutService.wifiPasswordModal) {
|
||||
PopoutService.wifiPasswordModal.show(networkContextMenu.currentSSID);
|
||||
} else {
|
||||
PopoutService.showWifiPasswordModal(networkContextMenu.currentSSID);
|
||||
}
|
||||
} else {
|
||||
NetworkService.connectToWifi(networkContextMenu.currentSSID);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 "?";
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,18 +22,38 @@ Item {
|
||||
readonly property var allInstances: SettingsData.desktopWidgetInstances || []
|
||||
readonly property var allGroups: SettingsData.desktopWidgetGroups || []
|
||||
|
||||
DesktopWidgetBrowser {
|
||||
id: widgetBrowser
|
||||
parentModal: root.parentModal
|
||||
onWidgetAdded: widgetType => {
|
||||
ToastService.showInfo(I18n.tr("Widget added"));
|
||||
function showWidgetBrowser() {
|
||||
widgetBrowserLoader.active = true;
|
||||
if (widgetBrowserLoader.item)
|
||||
widgetBrowserLoader.item.show();
|
||||
}
|
||||
|
||||
function showDesktopPluginBrowser() {
|
||||
desktopPluginBrowserLoader.active = true;
|
||||
if (desktopPluginBrowserLoader.item)
|
||||
desktopPluginBrowserLoader.item.show();
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: widgetBrowserLoader
|
||||
active: false
|
||||
|
||||
DesktopWidgetBrowser {
|
||||
parentModal: root.parentModal
|
||||
onWidgetAdded: widgetType => {
|
||||
ToastService.showInfo(I18n.tr("Widget added"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PluginBrowser {
|
||||
id: desktopPluginBrowser
|
||||
parentModal: root.parentModal
|
||||
typeFilter: "desktop-widget"
|
||||
LazyLoader {
|
||||
id: desktopPluginBrowserLoader
|
||||
active: false
|
||||
|
||||
PluginBrowser {
|
||||
parentModal: root.parentModal
|
||||
typeFilter: "desktop-widget"
|
||||
}
|
||||
}
|
||||
|
||||
DankFlickable {
|
||||
@@ -74,13 +94,13 @@ Item {
|
||||
DankButton {
|
||||
text: I18n.tr("Add Widget")
|
||||
iconName: "add"
|
||||
onClicked: widgetBrowser.show()
|
||||
onClicked: root.showWidgetBrowser()
|
||||
}
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Browse Plugins")
|
||||
iconName: "store"
|
||||
onClicked: desktopPluginBrowser.show()
|
||||
onClicked: root.showDesktopPluginBrowser()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Modals.Common
|
||||
import qs.Modals.FileBrowser
|
||||
@@ -14,6 +15,7 @@ Item {
|
||||
property string expandedVpnUuid: ""
|
||||
property string expandedWifiSsid: ""
|
||||
property string expandedEthDevice: ""
|
||||
property int maxPinnedWifiNetworks: 3
|
||||
|
||||
Component.onCompleted: {
|
||||
NetworkService.addRef();
|
||||
@@ -23,15 +25,59 @@ Item {
|
||||
NetworkService.removeRef();
|
||||
}
|
||||
|
||||
FileBrowserModal {
|
||||
id: vpnFileBrowser
|
||||
browserTitle: I18n.tr("Import VPN")
|
||||
browserIcon: "vpn_key"
|
||||
browserType: "vpn"
|
||||
fileExtensions: VPNService.getFileFilter()
|
||||
function openVpnFileBrowser() {
|
||||
vpnFileBrowserLoader.active = true;
|
||||
if (vpnFileBrowserLoader.item)
|
||||
vpnFileBrowserLoader.item.open();
|
||||
}
|
||||
|
||||
onFileSelected: path => {
|
||||
VPNService.importVpn(path.replace("file://", ""));
|
||||
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
|
||||
|
||||
FileBrowserModal {
|
||||
browserTitle: I18n.tr("Import VPN")
|
||||
browserIcon: "vpn_key"
|
||||
browserType: "vpn"
|
||||
fileExtensions: VPNService.getFileFilter()
|
||||
|
||||
onFileSelected: path => {
|
||||
VPNService.importVpn(path.replace("file://", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,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)
|
||||
@@ -1038,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
|
||||
@@ -1213,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1520,7 +1564,7 @@ Item {
|
||||
hoverEnabled: true
|
||||
cursorShape: VPNService.importing ? Qt.BusyCursor : Qt.PointingHandCursor
|
||||
enabled: !VPNService.importing
|
||||
onClicked: vpnFileBrowser.open()
|
||||
onClicked: networkTab.openVpnFileBrowser()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
@@ -209,7 +210,7 @@ FocusScope {
|
||||
iconName: "store"
|
||||
enabled: DMSService.dmsAvailable
|
||||
onClicked: {
|
||||
pluginBrowser.show();
|
||||
showPluginBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,9 +383,11 @@ FocusScope {
|
||||
Connections {
|
||||
target: DMSService
|
||||
function onPluginsListReceived(plugins) {
|
||||
pluginBrowser.isLoading = false;
|
||||
pluginBrowser.allPlugins = plugins;
|
||||
pluginBrowser.updateFilteredPlugins();
|
||||
if (!pluginBrowserLoader.item)
|
||||
return;
|
||||
pluginBrowserLoader.item.isLoading = false;
|
||||
pluginBrowserLoader.item.allPlugins = plugins;
|
||||
pluginBrowserLoader.item.updateFilteredPlugins();
|
||||
}
|
||||
function onInstalledPluginsReceived(plugins) {
|
||||
var pluginMap = {};
|
||||
@@ -410,22 +413,36 @@ FocusScope {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
pluginBrowser.parentModal = pluginsTab.parentModal;
|
||||
if (DMSService.dmsAvailable && DMSService.apiVersion >= 8)
|
||||
DMSService.listInstalled();
|
||||
if (PopoutService.pendingPluginInstall)
|
||||
Qt.callLater(() => pluginBrowser.show());
|
||||
Qt.callLater(showPluginBrowser);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: PopoutService
|
||||
function onPendingPluginInstallChanged() {
|
||||
if (PopoutService.pendingPluginInstall)
|
||||
pluginBrowser.show();
|
||||
showPluginBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
PluginBrowser {
|
||||
id: pluginBrowser
|
||||
LazyLoader {
|
||||
id: pluginBrowserLoader
|
||||
active: false
|
||||
|
||||
PluginBrowser {
|
||||
id: pluginBrowserItem
|
||||
|
||||
Component.onCompleted: {
|
||||
pluginBrowserItem.parentModal = pluginsTab.parentModal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showPluginBrowser() {
|
||||
pluginBrowserLoader.active = true;
|
||||
if (pluginBrowserLoader.item)
|
||||
pluginBrowserLoader.item.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ Item {
|
||||
if (DMSService.dmsAvailable)
|
||||
DMSService.listInstalledThemes();
|
||||
if (PopoutService.pendingThemeInstall)
|
||||
Qt.callLater(() => themeBrowser.show());
|
||||
Qt.callLater(() => showThemeBrowser());
|
||||
templateCheckProcess.running = true;
|
||||
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl)
|
||||
checkCursorIncludeStatus();
|
||||
@@ -169,7 +169,7 @@ Item {
|
||||
target: PopoutService
|
||||
function onPendingThemeInstallChanged() {
|
||||
if (PopoutService.pendingThemeInstall)
|
||||
themeBrowser.show();
|
||||
showThemeBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -939,7 +939,7 @@ Item {
|
||||
text: I18n.tr("Browse Themes", "browse themes button")
|
||||
iconName: "store"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onClicked: themeBrowser.show()
|
||||
onClicked: showThemeBrowser()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2041,7 +2041,18 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
ThemeBrowser {
|
||||
id: themeBrowser
|
||||
LazyLoader {
|
||||
id: themeBrowserLoader
|
||||
active: false
|
||||
|
||||
ThemeBrowser {
|
||||
id: themeBrowserItem
|
||||
}
|
||||
}
|
||||
|
||||
function showThemeBrowser() {
|
||||
themeBrowserLoader.active = true;
|
||||
if (themeBrowserLoader.item)
|
||||
themeBrowserLoader.item.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ Item {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: mainWallpaperBrowser.open()
|
||||
onClicked: root.openMainWallpaperBrowser()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,7 +476,7 @@ Item {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: lightWallpaperBrowser.open()
|
||||
onClicked: root.openLightWallpaperBrowser()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,7 +660,7 @@ Item {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: darkWallpaperBrowser.open()
|
||||
onClicked: root.openDarkWallpaperBrowser()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1242,53 +1242,83 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
FileBrowserModal {
|
||||
id: mainWallpaperBrowser
|
||||
parentModal: root.parentModal
|
||||
browserTitle: I18n.tr("Select Wallpaper", "wallpaper file browser title")
|
||||
browserIcon: "wallpaper"
|
||||
browserType: "wallpaper"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
if (SessionData.perMonitorWallpaper) {
|
||||
SessionData.setMonitorWallpaper(selectedMonitorName, path);
|
||||
} else {
|
||||
SessionData.setWallpaper(path);
|
||||
function openMainWallpaperBrowser() {
|
||||
mainWallpaperBrowserLoader.active = true;
|
||||
if (mainWallpaperBrowserLoader.item)
|
||||
mainWallpaperBrowserLoader.item.open();
|
||||
}
|
||||
|
||||
function openLightWallpaperBrowser() {
|
||||
lightWallpaperBrowserLoader.active = true;
|
||||
if (lightWallpaperBrowserLoader.item)
|
||||
lightWallpaperBrowserLoader.item.open();
|
||||
}
|
||||
|
||||
function openDarkWallpaperBrowser() {
|
||||
darkWallpaperBrowserLoader.active = true;
|
||||
if (darkWallpaperBrowserLoader.item)
|
||||
darkWallpaperBrowserLoader.item.open();
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: mainWallpaperBrowserLoader
|
||||
active: false
|
||||
|
||||
FileBrowserModal {
|
||||
parentModal: root.parentModal
|
||||
browserTitle: I18n.tr("Select Wallpaper", "wallpaper file browser title")
|
||||
browserIcon: "wallpaper"
|
||||
browserType: "wallpaper"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
if (SessionData.perMonitorWallpaper) {
|
||||
SessionData.setMonitorWallpaper(selectedMonitorName, path);
|
||||
} else {
|
||||
SessionData.setWallpaper(path);
|
||||
}
|
||||
close();
|
||||
}
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
FileBrowserModal {
|
||||
id: lightWallpaperBrowser
|
||||
parentModal: root.parentModal
|
||||
browserTitle: I18n.tr("Select Wallpaper", "light mode wallpaper file browser title")
|
||||
browserIcon: "light_mode"
|
||||
browserType: "wallpaper"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
SessionData.wallpaperPathLight = path;
|
||||
SessionData.syncWallpaperForCurrentMode();
|
||||
SessionData.saveSettings();
|
||||
close();
|
||||
LazyLoader {
|
||||
id: lightWallpaperBrowserLoader
|
||||
active: false
|
||||
|
||||
FileBrowserModal {
|
||||
parentModal: root.parentModal
|
||||
browserTitle: I18n.tr("Select Wallpaper", "light mode wallpaper file browser title")
|
||||
browserIcon: "light_mode"
|
||||
browserType: "wallpaper"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
SessionData.wallpaperPathLight = path;
|
||||
SessionData.syncWallpaperForCurrentMode();
|
||||
SessionData.saveSettings();
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileBrowserModal {
|
||||
id: darkWallpaperBrowser
|
||||
parentModal: root.parentModal
|
||||
browserTitle: I18n.tr("Select Wallpaper", "dark mode wallpaper file browser title")
|
||||
browserIcon: "dark_mode"
|
||||
browserType: "wallpaper"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
SessionData.wallpaperPathDark = path;
|
||||
SessionData.syncWallpaperForCurrentMode();
|
||||
SessionData.saveSettings();
|
||||
close();
|
||||
LazyLoader {
|
||||
id: darkWallpaperBrowserLoader
|
||||
active: false
|
||||
|
||||
FileBrowserModal {
|
||||
parentModal: root.parentModal
|
||||
browserTitle: I18n.tr("Select Wallpaper", "dark mode wallpaper file browser title")
|
||||
browserIcon: "dark_mode"
|
||||
browserType: "wallpaper"
|
||||
showHiddenFiles: true
|
||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||
onFileSelected: path => {
|
||||
SessionData.wallpaperPathDark = path;
|
||||
SessionData.syncWallpaperForCurrentMode();
|
||||
SessionData.saveSettings();
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
@@ -916,14 +917,28 @@ Item {
|
||||
});
|
||||
}
|
||||
|
||||
WidgetSelectionPopup {
|
||||
id: widgetSelectionPopup
|
||||
parentModal: widgetsTab.parentModal
|
||||
onWidgetSelected: (widgetId, targetSection) => {
|
||||
widgetsTab.addWidgetToSection(widgetId, targetSection);
|
||||
LazyLoader {
|
||||
id: widgetSelectionPopupLoader
|
||||
active: false
|
||||
|
||||
WidgetSelectionPopup {
|
||||
id: widgetSelectionPopupItem
|
||||
parentModal: widgetsTab.parentModal
|
||||
onWidgetSelected: (widgetId, targetSection) => {
|
||||
widgetsTab.addWidgetToSection(widgetId, targetSection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showWidgetSelectionPopup(sectionId) {
|
||||
widgetSelectionPopupLoader.active = true;
|
||||
if (!widgetSelectionPopupLoader.item)
|
||||
return;
|
||||
widgetSelectionPopupLoader.item.targetSection = sectionId;
|
||||
widgetSelectionPopupLoader.item.allWidgets = widgetsTab.getWidgetsForPopup();
|
||||
widgetSelectionPopupLoader.item.show();
|
||||
}
|
||||
|
||||
DankFlickable {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
@@ -1113,9 +1128,7 @@ Item {
|
||||
widgetsTab.handleItemOrderChanged(sectionId, newOrder);
|
||||
}
|
||||
onAddWidget: sectionId => {
|
||||
widgetSelectionPopup.targetSection = sectionId;
|
||||
widgetSelectionPopup.allWidgets = widgetsTab.getWidgetsForPopup();
|
||||
widgetSelectionPopup.show();
|
||||
showWidgetSelectionPopup(sectionId);
|
||||
}
|
||||
onRemoveWidget: (sectionId, index) => {
|
||||
widgetsTab.removeWidgetFromSection(sectionId, index);
|
||||
@@ -1170,9 +1183,7 @@ Item {
|
||||
widgetsTab.handleItemOrderChanged(sectionId, newOrder);
|
||||
}
|
||||
onAddWidget: sectionId => {
|
||||
widgetSelectionPopup.targetSection = sectionId;
|
||||
widgetSelectionPopup.allWidgets = widgetsTab.getWidgetsForPopup();
|
||||
widgetSelectionPopup.show();
|
||||
showWidgetSelectionPopup(sectionId);
|
||||
}
|
||||
onRemoveWidget: (sectionId, index) => {
|
||||
widgetsTab.removeWidgetFromSection(sectionId, index);
|
||||
@@ -1227,9 +1238,7 @@ Item {
|
||||
widgetsTab.handleItemOrderChanged(sectionId, newOrder);
|
||||
}
|
||||
onAddWidget: sectionId => {
|
||||
widgetSelectionPopup.targetSection = sectionId;
|
||||
widgetSelectionPopup.allWidgets = widgetsTab.getWidgetsForPopup();
|
||||
widgetSelectionPopup.show();
|
||||
showWidgetSelectionPopup(sectionId);
|
||||
}
|
||||
onRemoveWidget: (sectionId, index) => {
|
||||
widgetsTab.removeWidgetFromSection(sectionId, index);
|
||||
|
||||
@@ -27,7 +27,9 @@ Singleton {
|
||||
property var colorPickerModal: null
|
||||
property var notificationModal: null
|
||||
property var wifiPasswordModal: null
|
||||
property var wifiPasswordModalLoader: null
|
||||
property var polkitAuthModal: null
|
||||
property var polkitAuthModalLoader: null
|
||||
property var bluetoothPairingModal: null
|
||||
property var networkInfoModal: null
|
||||
|
||||
@@ -416,11 +418,17 @@ Singleton {
|
||||
}
|
||||
|
||||
function showWifiPasswordModal(ssid) {
|
||||
wifiPasswordModal?.show(ssid);
|
||||
if (wifiPasswordModalLoader)
|
||||
wifiPasswordModalLoader.active = true;
|
||||
if (wifiPasswordModal)
|
||||
wifiPasswordModal.show(ssid);
|
||||
}
|
||||
|
||||
function showHiddenNetworkModal() {
|
||||
wifiPasswordModal?.showHidden();
|
||||
if (wifiPasswordModalLoader)
|
||||
wifiPasswordModalLoader.active = true;
|
||||
if (wifiPasswordModal)
|
||||
wifiPasswordModal.showHidden();
|
||||
}
|
||||
|
||||
function hideWifiPasswordModal() {
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.2.0
|
||||
v1.2.3
|
||||
|
||||
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
@@ -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": {
|
||||
|
||||
@@ -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)": "HDR(EDID)"
|
||||
@@ -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": "使用触发前缀以激活"
|
||||
|
||||
Reference in New Issue
Block a user