mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-02 02:22:06 -04:00
Compare commits
9 Commits
0fef4d515e
...
177a4c4095
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
177a4c4095 | ||
|
|
63df19ab78 | ||
|
|
54e0eb5979 | ||
|
|
185284d422 | ||
|
|
ce240405d9 | ||
|
|
58b700ed0d | ||
|
|
d436fa4920 | ||
|
|
d58486193e | ||
|
|
e9404eb9b6 |
@@ -1249,7 +1249,17 @@ func extractGreeterWrapperFromCommand(command string) string {
|
||||
if len(tokens) == 0 {
|
||||
return ""
|
||||
}
|
||||
return strings.Trim(tokens[0], "\"")
|
||||
wrapper := strings.Trim(tokens[0], "\"")
|
||||
if wrapper == "" {
|
||||
return ""
|
||||
}
|
||||
if len(tokens) > 1 {
|
||||
next := strings.Trim(tokens[1], "\"")
|
||||
if next != "" && (filepath.Base(wrapper) == "bash" || filepath.Base(wrapper) == "sh") && strings.Contains(filepath.Base(next), "dms-greeter") {
|
||||
return fmt.Sprintf("%s (script: %s)", wrapper, next)
|
||||
}
|
||||
}
|
||||
return wrapper
|
||||
}
|
||||
|
||||
func extractGreeterPathOverrideFromCommand(command string) string {
|
||||
@@ -1335,6 +1345,26 @@ func packageInstallHint() string {
|
||||
}
|
||||
}
|
||||
|
||||
func systemPamManagerRemediationHint() string {
|
||||
osInfo, err := distros.GetOSInfo()
|
||||
if err != nil {
|
||||
return "Disable it in your PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login."
|
||||
}
|
||||
config, exists := distros.Registry[osInfo.Distribution.ID]
|
||||
if !exists {
|
||||
return "Disable it in your PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login."
|
||||
}
|
||||
|
||||
switch config.Family {
|
||||
case distros.FamilyFedora:
|
||||
return "Disable it in authselect to force password-only greeter login."
|
||||
case distros.FamilyDebian, distros.FamilyUbuntu:
|
||||
return "Disable it in pam-auth-update to force password-only greeter login."
|
||||
default:
|
||||
return "Disable it in your distro PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login."
|
||||
}
|
||||
}
|
||||
|
||||
func isPackageOnlyGreeterDistro() bool {
|
||||
osInfo, err := distros.GetOSInfo()
|
||||
if err != nil {
|
||||
@@ -1568,22 +1598,56 @@ func checkGreeterStatus() error {
|
||||
fmt.Println(" ⚠ Legacy unmanaged DMS PAM lines detected. Run 'dms greeter sync' to normalize.")
|
||||
allGood = false
|
||||
}
|
||||
enableFprintToggle, enableU2fToggle := false, false
|
||||
if enableFprint, enableU2f, settingsErr := greeter.ReadGreeterAuthToggles(homeDir); settingsErr == nil {
|
||||
enableFprintToggle = enableFprint
|
||||
enableU2fToggle = enableU2f
|
||||
} else {
|
||||
fmt.Printf(" ℹ Could not read greeter auth toggles from settings: %v\n", settingsErr)
|
||||
}
|
||||
|
||||
includedFprintFile := greeter.DetectIncludedPamModule(string(pamData), "pam_fprintd.so")
|
||||
showIncludedFprintNotice := false
|
||||
if includedFprintFile != "" {
|
||||
if enableFprint, _, settingsErr := greeter.ReadGreeterAuthToggles(homeDir); settingsErr == nil && enableFprint {
|
||||
showIncludedFprintNotice = greeter.FingerprintAuthAvailableForCurrentUser()
|
||||
includedU2fFile := greeter.DetectIncludedPamModule(string(pamData), "pam_u2f.so")
|
||||
fprintAvailableForCurrentUser := greeter.FingerprintAuthAvailableForCurrentUser()
|
||||
|
||||
if managedFprint && includedFprintFile != "" {
|
||||
fmt.Printf(" ⚠ pam_fprintd found in both DMS managed block and %s.\n", includedFprintFile)
|
||||
fmt.Println(" Double fingerprint auth detected — run 'dms greeter sync' to resolve.")
|
||||
allGood = false
|
||||
}
|
||||
if managedU2f && includedU2fFile != "" {
|
||||
fmt.Printf(" ⚠ pam_u2f found in both DMS managed block and %s.\n", includedU2fFile)
|
||||
fmt.Println(" Double security-key auth detected — run 'dms greeter sync' to resolve.")
|
||||
allGood = false
|
||||
}
|
||||
|
||||
if includedFprintFile != "" && !managedFprint {
|
||||
if enableFprintToggle {
|
||||
fmt.Printf(" ℹ Fingerprint auth is enabled via included %s.\n", includedFprintFile)
|
||||
if fprintAvailableForCurrentUser {
|
||||
fmt.Println(" DMS toggle is enabled, and effective auth is coming from the included PAM stack.")
|
||||
} else {
|
||||
fmt.Println(" No enrolled fingerprints detected for the current user; password auth remains the effective path.")
|
||||
}
|
||||
} else {
|
||||
if fprintAvailableForCurrentUser {
|
||||
fmt.Printf(" ℹ Fingerprint auth is active via included %s while DMS fingerprint toggle is off.\n", includedFprintFile)
|
||||
fmt.Println(" Password login will work but may be delayed while the fingerprint module runs first.")
|
||||
fmt.Printf(" To eliminate the delay, %s\n", systemPamManagerRemediationHint())
|
||||
} else {
|
||||
fmt.Printf(" ℹ pam_fprintd is present via included %s, but no enrolled fingerprints were detected for user %s.\n", includedFprintFile, currentUser.Username)
|
||||
fmt.Println(" Password auth remains the effective login path.")
|
||||
}
|
||||
}
|
||||
}
|
||||
if managedFprint {
|
||||
if includedFprintFile != "" {
|
||||
fmt.Printf(" ⚠ pam_fprintd found in both DMS managed block and %s.\n", includedFprintFile)
|
||||
fmt.Println(" Double fingerprint auth detected — run 'dms greeter sync' to resolve.")
|
||||
allGood = false
|
||||
if includedU2fFile != "" && !managedU2f {
|
||||
if enableU2fToggle {
|
||||
fmt.Printf(" ℹ Security-key auth is enabled via included %s.\n", includedU2fFile)
|
||||
fmt.Println(" DMS toggle is enabled, but effective auth is coming from the included PAM stack.")
|
||||
} else {
|
||||
fmt.Printf(" ⚠ Security-key auth is active via included %s while DMS security-key toggle is off.\n", includedU2fFile)
|
||||
fmt.Printf(" %s\n", systemPamManagerRemediationHint())
|
||||
}
|
||||
} else if includedFprintFile != "" && showIncludedFprintNotice {
|
||||
fmt.Printf(" ℹ Fingerprint auth is enabled via included %s.\n", includedFprintFile)
|
||||
fmt.Println(" The DMS toggle only controls the managed block; disable fingerprint in authselect/pam-auth-update for password-only greeter login.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1424,9 +1424,30 @@ func FingerprintAuthAvailableForCurrentUser() bool {
|
||||
return FingerprintAuthAvailableForUser(username)
|
||||
}
|
||||
|
||||
func pamManagerHintForCurrentDistro() string {
|
||||
osInfo, err := distros.GetOSInfo()
|
||||
if err != nil {
|
||||
return "Disable it in your PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login."
|
||||
}
|
||||
config, exists := distros.Registry[osInfo.Distribution.ID]
|
||||
if !exists {
|
||||
return "Disable it in your PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login."
|
||||
}
|
||||
|
||||
switch config.Family {
|
||||
case distros.FamilyFedora:
|
||||
return "Disable it in authselect to force password-only greeter login."
|
||||
case distros.FamilyDebian, distros.FamilyUbuntu:
|
||||
return "Disable it in pam-auth-update to force password-only greeter login."
|
||||
default:
|
||||
return "Disable it in your distro PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login."
|
||||
}
|
||||
}
|
||||
|
||||
func syncGreeterPamConfig(homeDir string, logFunc func(string), sudoPassword string, forceAuth bool) error {
|
||||
var wantFprint, wantU2f bool
|
||||
fprintToggleEnabled := forceAuth
|
||||
u2fToggleEnabled := forceAuth
|
||||
if forceAuth {
|
||||
wantFprint = pamModuleExists("pam_fprintd.so")
|
||||
wantU2f = pamModuleExists("pam_u2f.so")
|
||||
@@ -1436,6 +1457,7 @@ func syncGreeterPamConfig(homeDir string, logFunc func(string), sudoPassword str
|
||||
return err
|
||||
}
|
||||
fprintToggleEnabled = settings.GreeterEnableFprint
|
||||
u2fToggleEnabled = settings.GreeterEnableU2f
|
||||
fprintModule := pamModuleExists("pam_fprintd.so")
|
||||
u2fModule := pamModuleExists("pam_u2f.so")
|
||||
wantFprint = settings.GreeterEnableFprint && fprintModule
|
||||
@@ -1464,14 +1486,43 @@ func syncGreeterPamConfig(homeDir string, logFunc func(string), sudoPassword str
|
||||
content, _ = stripLegacyGreeterPamLines(content)
|
||||
|
||||
includedFprintFile := DetectIncludedPamModule(content, "pam_fprintd.so")
|
||||
includedU2fFile := DetectIncludedPamModule(content, "pam_u2f.so")
|
||||
fprintAvailableForCurrentUser := FingerprintAuthAvailableForCurrentUser()
|
||||
if wantFprint && includedFprintFile != "" {
|
||||
logFunc("⚠ pam_fprintd already present in included " + includedFprintFile + " (managed by authselect/pam-auth-update). Skipping DMS fprint block to avoid double-fingerprint auth.")
|
||||
wantFprint = false
|
||||
}
|
||||
showIncludedFprintNotice := fprintToggleEnabled && FingerprintAuthAvailableForCurrentUser()
|
||||
if !wantFprint && includedFprintFile != "" && showIncludedFprintNotice {
|
||||
logFunc("ℹ Fingerprint auth is still enabled via included " + includedFprintFile + ".")
|
||||
logFunc(" Disable fingerprint in your system PAM manager (authselect/pam-auth-update) to force password-only greeter login.")
|
||||
if wantU2f && includedU2fFile != "" {
|
||||
logFunc("⚠ pam_u2f already present in included " + includedU2fFile + " (managed by authselect/pam-auth-update). Skipping DMS U2F block to avoid double security-key auth.")
|
||||
wantU2f = false
|
||||
}
|
||||
if !wantFprint && includedFprintFile != "" {
|
||||
if fprintToggleEnabled {
|
||||
logFunc("ℹ Fingerprint auth is still enabled via included " + includedFprintFile + ".")
|
||||
if fprintAvailableForCurrentUser {
|
||||
logFunc(" DMS toggle is enabled, and effective auth is provided by the included PAM stack.")
|
||||
} else {
|
||||
logFunc(" No enrolled fingerprints detected for the current user; password auth remains the effective path.")
|
||||
}
|
||||
} else {
|
||||
if fprintAvailableForCurrentUser {
|
||||
logFunc("ℹ Fingerprint auth is active via included " + includedFprintFile + " while DMS fingerprint toggle is off.")
|
||||
logFunc(" Password login will work but may be delayed while the fingerprint module runs first.")
|
||||
logFunc(" To eliminate the delay, " + pamManagerHintForCurrentDistro())
|
||||
} else {
|
||||
logFunc("ℹ pam_fprintd is present via included " + includedFprintFile + ", but no enrolled fingerprints were detected for the current user.")
|
||||
logFunc(" Password auth remains the effective login path.")
|
||||
}
|
||||
}
|
||||
}
|
||||
if !wantU2f && includedU2fFile != "" {
|
||||
if u2fToggleEnabled {
|
||||
logFunc("ℹ Security-key auth is still enabled via included " + includedU2fFile + ".")
|
||||
logFunc(" DMS toggle is enabled, but effective auth is provided by the included PAM stack.")
|
||||
} else {
|
||||
logFunc("⚠ Security-key auth is active via included " + includedU2fFile + " while DMS security-key toggle is off.")
|
||||
logFunc(" " + pamManagerHintForCurrentDistro())
|
||||
}
|
||||
}
|
||||
|
||||
if wantFprint || wantU2f {
|
||||
|
||||
@@ -71,6 +71,7 @@ var templateRegistry = []TemplateDef{
|
||||
{ID: "kcolorscheme", ConfigFile: "kcolorscheme.toml", RunUnconditionally: true},
|
||||
{ID: "vscode", Kind: TemplateKindVSCode},
|
||||
{ID: "emacs", Commands: []string{"emacs"}, ConfigFile: "emacs.toml", Kind: TemplateKindEmacs},
|
||||
{ID: "zed", Commands: []string{"zed"}, ConfigFile: "zed.toml"},
|
||||
}
|
||||
|
||||
func (c *ColorMode) GTKTheme() string {
|
||||
|
||||
@@ -474,11 +474,13 @@ Singleton {
|
||||
property bool matugenTemplateKcolorscheme: true
|
||||
property bool matugenTemplateVscode: true
|
||||
property bool matugenTemplateEmacs: true
|
||||
property bool matugenTemplateZed: true
|
||||
|
||||
property bool showDock: false
|
||||
property bool dockAutoHide: false
|
||||
property bool dockSmartAutoHide: false
|
||||
property bool dockGroupByApp: false
|
||||
property bool dockRestoreSpecialWorkspaceOnClick: false
|
||||
property bool dockOpenOnOverview: false
|
||||
property int dockPosition: SettingsData.Position.Bottom
|
||||
property real dockSpacing: 4
|
||||
@@ -549,6 +551,7 @@ Singleton {
|
||||
property bool notificationHistorySaveNormal: true
|
||||
property bool notificationHistorySaveCritical: true
|
||||
property var notificationRules: []
|
||||
property bool notificationFocusedMonitor: false
|
||||
|
||||
property bool osdAlwaysShowValue: false
|
||||
property int osdPosition: SettingsData.Position.BottomCenter
|
||||
|
||||
@@ -1551,7 +1551,7 @@ Singleton {
|
||||
if (typeof SettingsData !== "undefined") {
|
||||
const skipTemplates = [];
|
||||
if (!SettingsData.runDmsMatugenTemplates) {
|
||||
skipTemplates.push("gtk", "nvim", "niri", "qt5ct", "qt6ct", "firefox", "pywalfox", "zenbrowser", "vesktop", "equibop", "ghostty", "kitty", "foot", "alacritty", "wezterm", "dgop", "kcolorscheme", "vscode", "emacs");
|
||||
skipTemplates.push("gtk", "nvim", "niri", "qt5ct", "qt6ct", "firefox", "pywalfox", "zenbrowser", "vesktop", "equibop", "ghostty", "kitty", "foot", "alacritty", "wezterm", "dgop", "kcolorscheme", "vscode", "emacs", "zed");
|
||||
} else {
|
||||
if (!SettingsData.matugenTemplateGtk)
|
||||
skipTemplates.push("gtk");
|
||||
@@ -1595,6 +1595,8 @@ Singleton {
|
||||
skipTemplates.push("vscode");
|
||||
if (!SettingsData.matugenTemplateEmacs)
|
||||
skipTemplates.push("emacs");
|
||||
if (!SettingsData.matugenTemplateZed)
|
||||
skipTemplates.push("zed");
|
||||
}
|
||||
if (skipTemplates.length > 0) {
|
||||
args.push("--skip-templates", skipTemplates.join(","));
|
||||
|
||||
@@ -289,11 +289,13 @@ var SPEC = {
|
||||
matugenTemplateKcolorscheme: { def: true },
|
||||
matugenTemplateVscode: { def: true },
|
||||
matugenTemplateEmacs: { def: true },
|
||||
matugenTemplateZed: { def: true },
|
||||
|
||||
showDock: { def: false },
|
||||
dockAutoHide: { def: false },
|
||||
dockSmartAutoHide: { def: false },
|
||||
dockGroupByApp: { def: false },
|
||||
dockRestoreSpecialWorkspaceOnClick: { def: false },
|
||||
dockOpenOnOverview: { def: false },
|
||||
dockPosition: { def: 1 },
|
||||
dockSpacing: { def: 4 },
|
||||
@@ -363,6 +365,7 @@ var SPEC = {
|
||||
notificationHistorySaveNormal: { def: true },
|
||||
notificationHistorySaveCritical: { def: true },
|
||||
notificationRules: { def: [] },
|
||||
notificationFocusedMonitor: { def: false },
|
||||
|
||||
osdAlwaysShowValue: { def: false },
|
||||
osdPosition: { def: 5 },
|
||||
|
||||
@@ -313,7 +313,7 @@ Item {
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: SettingsData.getFilteredScreens("notifications")
|
||||
model: SettingsData.notificationFocusedMonitor ? Quickshell.screens : SettingsData.getFilteredScreens("notifications")
|
||||
|
||||
delegate: NotificationPopupManager {
|
||||
modelData: item
|
||||
|
||||
@@ -390,10 +390,11 @@ BasePill {
|
||||
anchors.top: parent.top
|
||||
}
|
||||
|
||||
StyledText {
|
||||
NumericText {
|
||||
id: audioPercentV
|
||||
visible: root.showAudioPercent
|
||||
text: Math.round((AudioService.sink?.audio?.volume ?? 0) * 100) + "%"
|
||||
reserveText: "100%"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||
color: Theme.widgetTextColor
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
@@ -416,10 +417,11 @@ BasePill {
|
||||
anchors.top: parent.top
|
||||
}
|
||||
|
||||
StyledText {
|
||||
NumericText {
|
||||
id: micPercentV
|
||||
visible: root.showMicPercent
|
||||
text: Math.round((AudioService.source?.audio?.volume ?? 0) * 100) + "%"
|
||||
reserveText: "100%"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||
color: Theme.widgetTextColor
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
@@ -442,10 +444,11 @@ BasePill {
|
||||
anchors.top: parent.top
|
||||
}
|
||||
|
||||
StyledText {
|
||||
NumericText {
|
||||
id: brightnessPercentV
|
||||
visible: root.showBrightnessPercent
|
||||
text: Math.round(getBrightness() * 100) + "%"
|
||||
reserveText: "100%"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||
color: Theme.widgetTextColor
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
@@ -536,7 +539,8 @@ BasePill {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: audioIcon.implicitWidth + (root.showAudioPercent ? audioPercent.implicitWidth : 0) + 4
|
||||
width: audioIcon.implicitWidth + (root.showAudioPercent ? audioPercent.reservedWidth : 0) + 4
|
||||
implicitWidth: width
|
||||
height: root.widgetThickness - root.horizontalPadding * 2
|
||||
color: "transparent"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -552,20 +556,23 @@ BasePill {
|
||||
anchors.leftMargin: 2
|
||||
}
|
||||
|
||||
StyledText {
|
||||
NumericText {
|
||||
id: audioPercent
|
||||
visible: root.showAudioPercent
|
||||
text: Math.round((AudioService.sink?.audio?.volume ?? 0) * 100) + "%"
|
||||
reserveText: "100%"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||
color: Theme.widgetTextColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: audioIcon.right
|
||||
anchors.leftMargin: 2
|
||||
width: reservedWidth
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: micIcon.implicitWidth + (root.showMicPercent ? micPercent.implicitWidth : 0) + 4
|
||||
width: micIcon.implicitWidth + (root.showMicPercent ? micPercent.reservedWidth : 0) + 4
|
||||
implicitWidth: width
|
||||
height: root.widgetThickness - root.horizontalPadding * 2
|
||||
color: "transparent"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -581,20 +588,22 @@ BasePill {
|
||||
anchors.leftMargin: 2
|
||||
}
|
||||
|
||||
StyledText {
|
||||
NumericText {
|
||||
id: micPercent
|
||||
visible: root.showMicPercent
|
||||
text: Math.round((AudioService.source?.audio?.volume ?? 0) * 100) + "%"
|
||||
reserveText: "100%"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||
color: Theme.widgetTextColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: micIcon.right
|
||||
anchors.leftMargin: 2
|
||||
width: reservedWidth
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: brightnessIcon.implicitWidth + (root.showBrightnessPercent ? brightnessPercent.implicitWidth : 0) + 4
|
||||
width: brightnessIcon.implicitWidth + (root.showBrightnessPercent ? brightnessPercent.reservedWidth : 0) + 4
|
||||
height: root.widgetThickness - root.horizontalPadding * 2
|
||||
color: "transparent"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -610,15 +619,17 @@ BasePill {
|
||||
anchors.leftMargin: 2
|
||||
}
|
||||
|
||||
StyledText {
|
||||
NumericText {
|
||||
id: brightnessPercent
|
||||
visible: root.showBrightnessPercent
|
||||
text: Math.round(getBrightness() * 100) + "%"
|
||||
reserveText: "100%"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||
color: Theme.widgetTextColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: brightnessIcon.right
|
||||
anchors.leftMargin: 2
|
||||
width: reservedWidth
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,16 +211,17 @@ BasePill {
|
||||
text: {
|
||||
const title = activeWindow && activeWindow.title ? activeWindow.title : "";
|
||||
const appName = appText.text;
|
||||
|
||||
if (compactMode && title === appName) {
|
||||
return title;
|
||||
}
|
||||
|
||||
if (!title || !appName) {
|
||||
return title;
|
||||
}
|
||||
|
||||
if (title.endsWith(" - " + appName)) {
|
||||
return title.substring(0, title.length - (" - " + appName).length);
|
||||
}
|
||||
|
||||
if (title.endsWith(appName)) {
|
||||
return title.substring(0, title.length - appName.length).replace(/ - $/, "");
|
||||
return title.substring(0, title.length - appName.length).replace(/ (-|—) $/, "");
|
||||
}
|
||||
|
||||
return title;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Services.SystemTray
|
||||
@@ -162,6 +161,23 @@ BasePill {
|
||||
return 0;
|
||||
}
|
||||
|
||||
readonly property string autoBarShadowDirection: {
|
||||
const edge = root.axis?.edge;
|
||||
switch (edge) {
|
||||
case "top":
|
||||
return "top";
|
||||
case "bottom":
|
||||
return "bottom";
|
||||
case "left":
|
||||
return "left";
|
||||
case "right":
|
||||
return "right";
|
||||
default:
|
||||
return "bottom";
|
||||
}
|
||||
}
|
||||
readonly property string effectiveShadowDirection: Theme.elevationLightDirection === "autoBar" ? autoBarShadowDirection : Theme.elevationLightDirection
|
||||
|
||||
property bool menuOpen: false
|
||||
property var currentTrayMenu: null
|
||||
|
||||
@@ -940,13 +956,6 @@ BasePill {
|
||||
}
|
||||
})(), overflowMenu.dpr)
|
||||
|
||||
readonly property var elev: Theme.elevationLevel2
|
||||
property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 8
|
||||
property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0
|
||||
property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.25
|
||||
readonly property real popupSurfaceAlpha: Theme.popupTransparency
|
||||
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
|
||||
|
||||
opacity: root.menuOpen ? 1 : 0
|
||||
scale: root.menuOpen ? 1 : 0.85
|
||||
|
||||
@@ -967,19 +976,14 @@ BasePill {
|
||||
ElevationShadow {
|
||||
id: bgShadowLayer
|
||||
anchors.fill: parent
|
||||
level: menuContainer.elev
|
||||
fallbackOffset: 4
|
||||
shadowBlurPx: menuContainer.shadowBlurPx
|
||||
shadowSpreadPx: menuContainer.shadowSpreadPx
|
||||
shadowColor: {
|
||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
||||
return Theme.withAlpha(baseColor, menuContainer.effectiveShadowAlpha);
|
||||
}
|
||||
level: Theme.elevationLevel3
|
||||
direction: root.effectiveShadowDirection
|
||||
fallbackOffset: 6
|
||||
targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
targetRadius: Theme.cornerRadius
|
||||
sourceRect.antialiasing: true
|
||||
sourceRect.smooth: true
|
||||
shadowEnabled: Theme.elevationEnabled
|
||||
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled
|
||||
layer.smooth: true
|
||||
layer.textureSize: Qt.size(Math.round(width * overflowMenu.dpr * 2), Math.round(height * overflowMenu.dpr * 2))
|
||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
||||
@@ -1402,13 +1406,6 @@ BasePill {
|
||||
}
|
||||
})(), menuWindow.dpr)
|
||||
|
||||
readonly property var elev: Theme.elevationLevel2
|
||||
property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 8
|
||||
property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0
|
||||
property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.25
|
||||
readonly property real popupSurfaceAlpha: Theme.popupTransparency
|
||||
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
|
||||
|
||||
opacity: menuRoot.showMenu ? 1 : 0
|
||||
scale: menuRoot.showMenu ? 1 : 0.85
|
||||
|
||||
@@ -1429,18 +1426,13 @@ BasePill {
|
||||
ElevationShadow {
|
||||
id: menuBgShadowLayer
|
||||
anchors.fill: parent
|
||||
level: menuContainer.elev
|
||||
fallbackOffset: 4
|
||||
shadowBlurPx: menuContainer.shadowBlurPx
|
||||
shadowSpreadPx: menuContainer.shadowSpreadPx
|
||||
shadowColor: {
|
||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
||||
return Theme.withAlpha(baseColor, menuContainer.effectiveShadowAlpha);
|
||||
}
|
||||
level: Theme.elevationLevel3
|
||||
direction: root.effectiveShadowDirection
|
||||
fallbackOffset: 6
|
||||
targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
targetRadius: Theme.cornerRadius
|
||||
sourceRect.antialiasing: true
|
||||
shadowEnabled: Theme.elevationEnabled
|
||||
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled
|
||||
layer.smooth: true
|
||||
layer.textureSize: Qt.size(Math.round(width * menuWindow.dpr), Math.round(height * menuWindow.dpr))
|
||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
@@ -133,6 +134,40 @@ Item {
|
||||
function getGroupedToplevels() {
|
||||
return appData?.allWindows?.map(w => w.toplevel).filter(t => t !== null) || [];
|
||||
}
|
||||
|
||||
function getHyprToplevelForWayland(waylandToplevel) {
|
||||
if (!waylandToplevel || !CompositorService.isHyprland || !Hyprland.toplevels)
|
||||
return null;
|
||||
const hyprToplevels = Array.from(Hyprland.toplevels.values);
|
||||
for (let i = 0; i < hyprToplevels.length; i++) {
|
||||
if (hyprToplevels[i].wayland === waylandToplevel)
|
||||
return hyprToplevels[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getSpecialWorkspaceName(waylandToplevel) {
|
||||
const hyprToplevel = getHyprToplevelForWayland(waylandToplevel);
|
||||
if (!hyprToplevel)
|
||||
return "";
|
||||
const wsName = String(hyprToplevel.lastIpcObject?.workspace?.name || hyprToplevel.workspace?.name || "");
|
||||
if (!wsName.startsWith("special:"))
|
||||
return "";
|
||||
return wsName.slice("special:".length);
|
||||
}
|
||||
|
||||
function restoreSpecialWorkspaceWindow(waylandToplevel) {
|
||||
if (!SettingsData.dockRestoreSpecialWorkspaceOnClick || !CompositorService.isHyprland || !waylandToplevel)
|
||||
return false;
|
||||
|
||||
const specialName = getSpecialWorkspaceName(waylandToplevel);
|
||||
if (!specialName)
|
||||
return false;
|
||||
|
||||
Hyprland.dispatch("togglespecialworkspace " + specialName);
|
||||
Qt.callLater(() => waylandToplevel.activate());
|
||||
return true;
|
||||
}
|
||||
onIsHoveredChanged: {
|
||||
if (mouseArea.pressed || dragging)
|
||||
return;
|
||||
@@ -276,8 +311,11 @@ Item {
|
||||
break;
|
||||
case "window":
|
||||
const windowToplevel = getToplevelObject();
|
||||
if (windowToplevel)
|
||||
if (windowToplevel) {
|
||||
if (restoreSpecialWorkspaceWindow(windowToplevel))
|
||||
return;
|
||||
windowToplevel.activate();
|
||||
}
|
||||
break;
|
||||
case "grouped":
|
||||
if (appData.windowCount === 0) {
|
||||
@@ -300,8 +338,11 @@ Item {
|
||||
SessionService.launchDesktopEntry(groupedEntry);
|
||||
} else if (appData.windowCount === 1) {
|
||||
const groupedToplevel = getToplevelObject();
|
||||
if (groupedToplevel)
|
||||
if (groupedToplevel) {
|
||||
if (restoreSpecialWorkspaceWindow(groupedToplevel))
|
||||
return;
|
||||
groupedToplevel.activate();
|
||||
}
|
||||
} else if (contextMenu) {
|
||||
const shouldHidePin = appData.appId === "org.quickshell";
|
||||
contextMenu.showForButton(root, appData, root.height + 25, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps);
|
||||
|
||||
@@ -35,8 +35,8 @@ Item {
|
||||
property bool pendingPasswordResponse: false
|
||||
property bool passwordSubmitRequested: false
|
||||
property bool cancelingExternalAuthForPassword: false
|
||||
property int defaultAuthTimeoutMs: 12000
|
||||
property int externalAuthTimeoutMs: 45000
|
||||
property int defaultAuthTimeoutMs: 10000
|
||||
property int externalAuthTimeoutMs: 36000
|
||||
property int memoryFlushDelayMs: 120
|
||||
property string pendingLaunchCommand: ""
|
||||
property var pendingLaunchEnv: []
|
||||
@@ -50,9 +50,12 @@ Item {
|
||||
property string faillockConfigText: ""
|
||||
property bool greeterWallpaperOverrideExists: false
|
||||
property string externalAuthAutoStartedForUser: ""
|
||||
property int passwordSessionTransitionRetryCount: 0
|
||||
property int maxPasswordSessionTransitionRetries: 2
|
||||
readonly property bool greeterPamHasFprint: pamModuleEnabled(greetdPamText, "pam_fprintd") || (greetdPamText.includes("system-auth") && pamModuleEnabled(systemAuthPamText, "pam_fprintd")) || (greetdPamText.includes("common-auth") && pamModuleEnabled(commonAuthPamText, "pam_fprintd")) || (greetdPamText.includes("password-auth") && pamModuleEnabled(passwordAuthPamText, "pam_fprintd"))
|
||||
readonly property bool greeterPamHasU2f: pamModuleEnabled(greetdPamText, "pam_u2f") || (greetdPamText.includes("system-auth") && pamModuleEnabled(systemAuthPamText, "pam_u2f")) || (greetdPamText.includes("common-auth") && pamModuleEnabled(commonAuthPamText, "pam_u2f")) || (greetdPamText.includes("password-auth") && pamModuleEnabled(passwordAuthPamText, "pam_u2f"))
|
||||
readonly property bool greeterExternalAuthAvailable: (greeterPamHasFprint && GreetdSettings.greeterEnableFprint) || (greeterPamHasU2f && GreetdSettings.greeterEnableU2f)
|
||||
readonly property bool greeterPamHasExternalAuth: greeterPamHasFprint || greeterPamHasU2f
|
||||
|
||||
function initWeatherService() {
|
||||
if (weatherInitialized)
|
||||
@@ -208,6 +211,13 @@ Item {
|
||||
authFeedbackMessage = "";
|
||||
}
|
||||
|
||||
function resetPasswordSessionTransition(clearSubmitRequest) {
|
||||
cancelingExternalAuthForPassword = false;
|
||||
passwordSessionTransitionRetryCount = 0;
|
||||
if (clearSubmitRequest)
|
||||
passwordSubmitRequested = false;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GreetdSettings
|
||||
function onSettingsLoadedChanged() {
|
||||
@@ -348,8 +358,7 @@ Item {
|
||||
PortalService.getGreeterUserProfileImage(user);
|
||||
GreeterState.passwordBuffer = "";
|
||||
pendingPasswordResponse = false;
|
||||
passwordSubmitRequested = false;
|
||||
cancelingExternalAuthForPassword = false;
|
||||
resetPasswordSessionTransition(true);
|
||||
maybeAutoStartExternalAuth();
|
||||
}
|
||||
|
||||
@@ -357,8 +366,7 @@ Item {
|
||||
if (!GreeterState.passwordBuffer || GreeterState.passwordBuffer.length === 0)
|
||||
return false;
|
||||
pendingPasswordResponse = false;
|
||||
passwordSubmitRequested = false;
|
||||
cancelingExternalAuthForPassword = false;
|
||||
resetPasswordSessionTransition(true);
|
||||
awaitingExternalAuth = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.restart();
|
||||
@@ -369,9 +377,24 @@ Item {
|
||||
}
|
||||
|
||||
function requestPasswordSessionTransition() {
|
||||
if (!GreeterState.passwordBuffer || GreeterState.passwordBuffer.length === 0)
|
||||
return;
|
||||
if (cancelingExternalAuthForPassword)
|
||||
return;
|
||||
if (passwordSessionTransitionRetryCount >= maxPasswordSessionTransitionRetries) {
|
||||
pendingPasswordResponse = false;
|
||||
awaitingExternalAuth = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.stop();
|
||||
resetPasswordSessionTransition(true);
|
||||
GreeterState.pamState = "error";
|
||||
authFeedbackMessage = currentAuthMessage();
|
||||
placeholderDelay.restart();
|
||||
Greetd.cancelSession();
|
||||
return;
|
||||
}
|
||||
cancelingExternalAuthForPassword = true;
|
||||
passwordSessionTransitionRetryCount = passwordSessionTransitionRetryCount + 1;
|
||||
awaitingExternalAuth = false;
|
||||
pendingPasswordResponse = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
@@ -388,9 +411,7 @@ Item {
|
||||
if (Greetd.state !== GreetdState.Inactive) {
|
||||
if (pendingPasswordResponse && hasPasswordBuffer)
|
||||
submitBufferedPassword();
|
||||
else if (awaitingExternalAuth && hasPasswordBuffer) {
|
||||
passwordSubmitRequested = true;
|
||||
} else if (hasPasswordBuffer)
|
||||
else if (hasPasswordBuffer)
|
||||
passwordSubmitRequested = true;
|
||||
return;
|
||||
}
|
||||
@@ -404,7 +425,10 @@ Item {
|
||||
pendingPasswordResponse = false;
|
||||
passwordSubmitRequested = hasPasswordBuffer;
|
||||
awaitingExternalAuth = !hasPasswordBuffer && root.greeterExternalAuthAvailable;
|
||||
authTimeout.interval = awaitingExternalAuth ? externalAuthTimeoutMs : defaultAuthTimeoutMs;
|
||||
// Included PAM stacks (system-auth/common-auth/password-auth) may still run
|
||||
// biometric/U2F modules before password even when DMS toggles are off.
|
||||
const waitingOnPamExternalBeforePassword = hasPasswordBuffer && root.greeterPamHasExternalAuth;
|
||||
authTimeout.interval = (awaitingExternalAuth || waitingOnPamExternalBeforePassword) ? externalAuthTimeoutMs : defaultAuthTimeoutMs;
|
||||
authTimeout.restart();
|
||||
Greetd.createSession(GreeterState.username);
|
||||
}
|
||||
@@ -1559,18 +1583,30 @@ Item {
|
||||
function onAuthMessage(message, error, responseRequired, echoResponse) {
|
||||
if (responseRequired) {
|
||||
cancelingExternalAuthForPassword = false;
|
||||
passwordSessionTransitionRetryCount = 0;
|
||||
awaitingExternalAuth = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.restart();
|
||||
pendingPasswordResponse = true;
|
||||
const hasPasswordBuffer = GreeterState.passwordBuffer && GreeterState.passwordBuffer.length > 0;
|
||||
if (!passwordSubmitRequested && hasPasswordBuffer)
|
||||
passwordSubmitRequested = true;
|
||||
if (passwordSubmitRequested && !root.submitBufferedPassword())
|
||||
passwordSubmitRequested = false;
|
||||
if (passwordSubmitRequested || hasPasswordBuffer) {
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.restart();
|
||||
} else {
|
||||
authTimeout.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
pendingPasswordResponse = false;
|
||||
const externalPrompt = root.isExternalAuthPrompt(message, responseRequired);
|
||||
if (!passwordSubmitRequested)
|
||||
awaitingExternalAuth = root.isExternalAuthPrompt(message, responseRequired);
|
||||
authTimeout.interval = awaitingExternalAuth ? externalAuthTimeoutMs : defaultAuthTimeoutMs;
|
||||
awaitingExternalAuth = root.greeterExternalAuthAvailable && externalPrompt;
|
||||
if (awaitingExternalAuth || (passwordSubmitRequested && externalPrompt && root.greeterPamHasExternalAuth))
|
||||
authTimeout.interval = externalAuthTimeoutMs;
|
||||
else
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.restart();
|
||||
Greetd.respond("");
|
||||
}
|
||||
@@ -1587,15 +1623,14 @@ Item {
|
||||
Qt.callLater(root.startAuthSession);
|
||||
return;
|
||||
}
|
||||
passwordSubmitRequested = false;
|
||||
resetPasswordSessionTransition(true);
|
||||
}
|
||||
}
|
||||
|
||||
function onReadyToLaunch() {
|
||||
awaitingExternalAuth = false;
|
||||
pendingPasswordResponse = false;
|
||||
passwordSubmitRequested = false;
|
||||
cancelingExternalAuthForPassword = false;
|
||||
resetPasswordSessionTransition(true);
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.stop();
|
||||
passwordFailureCount = 0;
|
||||
@@ -1629,8 +1664,7 @@ Item {
|
||||
function onAuthFailure(message) {
|
||||
awaitingExternalAuth = false;
|
||||
pendingPasswordResponse = false;
|
||||
passwordSubmitRequested = false;
|
||||
cancelingExternalAuthForPassword = false;
|
||||
resetPasswordSessionTransition(true);
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.stop();
|
||||
launchTimeout.stop();
|
||||
@@ -1651,8 +1685,7 @@ Item {
|
||||
function onError(error) {
|
||||
awaitingExternalAuth = false;
|
||||
pendingPasswordResponse = false;
|
||||
passwordSubmitRequested = false;
|
||||
cancelingExternalAuthForPassword = false;
|
||||
resetPasswordSessionTransition(true);
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.stop();
|
||||
launchTimeout.stop();
|
||||
@@ -1688,8 +1721,7 @@ Item {
|
||||
return;
|
||||
awaitingExternalAuth = false;
|
||||
pendingPasswordResponse = false;
|
||||
passwordSubmitRequested = false;
|
||||
cancelingExternalAuthForPassword = false;
|
||||
resetPasswordSessionTransition(true);
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
GreeterState.pamState = "error";
|
||||
authFeedbackMessage = currentAuthMessage();
|
||||
@@ -1707,8 +1739,7 @@ Item {
|
||||
if (!GreeterState.unlocking)
|
||||
return;
|
||||
pendingPasswordResponse = false;
|
||||
passwordSubmitRequested = false;
|
||||
cancelingExternalAuthForPassword = false;
|
||||
resetPasswordSessionTransition(true);
|
||||
GreeterState.unlocking = false;
|
||||
GreeterState.pamState = "error";
|
||||
authFeedbackMessage = currentAuthMessage();
|
||||
|
||||
@@ -90,6 +90,13 @@ Scope {
|
||||
printErrors: false
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: loginConfigWatcher
|
||||
|
||||
path: "/etc/pam.d/login"
|
||||
printErrors: false
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: u2fConfigWatcher
|
||||
|
||||
@@ -101,7 +108,7 @@ Scope {
|
||||
id: passwd
|
||||
|
||||
config: dankshellConfigWatcher.loaded ? "dankshell" : "login"
|
||||
configDirectory: dankshellConfigWatcher.loaded ? "/etc/pam.d" : Quickshell.shellDir + "/assets/pam"
|
||||
configDirectory: dankshellConfigWatcher.loaded || loginConfigWatcher.loaded ? "/etc/pam.d" : Quickshell.shellDir + "/assets/pam"
|
||||
|
||||
onMessageChanged: {
|
||||
if (message.startsWith("The account is locked"))
|
||||
|
||||
@@ -108,6 +108,13 @@ QtObject {
|
||||
return p && p.status !== Component.Null && !p._isDestroying && p.hasValidData;
|
||||
}
|
||||
|
||||
function _isFocusedScreen() {
|
||||
if (!SettingsData.notificationFocusedMonitor)
|
||||
return true;
|
||||
const focused = CompositorService.getFocusedScreen();
|
||||
return focused && manager.modelData && focused.name === manager.modelData.name;
|
||||
}
|
||||
|
||||
function _sync(newWrappers) {
|
||||
for (const p of popupWindows.slice()) {
|
||||
if (!_isValidWindow(p) || p.exiting)
|
||||
@@ -118,7 +125,7 @@ QtObject {
|
||||
}
|
||||
}
|
||||
for (const w of newWrappers) {
|
||||
if (w && !_hasWindowFor(w))
|
||||
if (w && !_hasWindowFor(w) && _isFocusedScreen())
|
||||
_insertAtTop(w);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +417,15 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
width: parent.width
|
||||
text: I18n.tr("Focused monitor only")
|
||||
description: I18n.tr("Show notifications only on the currently focused monitor")
|
||||
visible: parent.componentId === "notifications"
|
||||
checked: SettingsData.notificationFocusedMonitor
|
||||
onToggled: checked => SettingsData.set("notificationFocusedMonitor", checked)
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
width: parent.width
|
||||
text: I18n.tr("Show on Last Display")
|
||||
|
||||
@@ -160,6 +160,16 @@ Item {
|
||||
onToggled: checked => SettingsData.set("dockGroupByApp", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "dockRestoreSpecialWorkspaceOnClick"
|
||||
tags: ["dock", "hyprland", "special", "workspace", "restore"]
|
||||
text: I18n.tr("Restore Special Workspace Windows")
|
||||
description: I18n.tr("When clicking a dock window in a Hyprland special workspace, bring that special workspace back before focusing the window")
|
||||
checked: SettingsData.dockRestoreSpecialWorkspaceOnClick
|
||||
visible: CompositorService.isHyprland
|
||||
onToggled: checked => SettingsData.set("dockRestoreSpecialWorkspaceOnClick", checked)
|
||||
}
|
||||
|
||||
SettingsButtonGroupRow {
|
||||
settingKey: "dockIndicatorStyle"
|
||||
tags: ["dock", "indicator", "style", "circle", "line"]
|
||||
|
||||
@@ -288,6 +288,15 @@ Item {
|
||||
onToggled: checked => SettingsData.set("notificationPopupPrivacyMode", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "notificationFocusedMonitor"
|
||||
tags: ["notification", "popup", "focused", "monitor", "display", "screen", "active"]
|
||||
text: I18n.tr("Focused Monitor Only")
|
||||
description: I18n.tr("Show notification popups only on the currently focused monitor")
|
||||
checked: SettingsData.notificationFocusedMonitor
|
||||
onToggled: checked => SettingsData.set("notificationFocusedMonitor", checked)
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: notificationAnimationColumn.implicitHeight + Theme.spacingM * 2
|
||||
|
||||
@@ -2638,6 +2638,18 @@ Item {
|
||||
checked: SettingsData.matugenTemplateEmacs
|
||||
onToggled: checked => SettingsData.set("matugenTemplateEmacs", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
tab: "theme"
|
||||
tags: ["matugen", "zed", "template"]
|
||||
settingKey: "matugenTemplateZed"
|
||||
text: "Zed"
|
||||
description: getTemplateDescription("zed", "")
|
||||
descriptionColor: getTemplateDescriptionColor("zed")
|
||||
visible: SettingsData.runDmsMatugenTemplates
|
||||
checked: SettingsData.matugenTemplateZed
|
||||
onToggled: checked => SettingsData.set("matugenTemplateZed", checked)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
||||
@@ -239,7 +239,7 @@ Item {
|
||||
StyledRect {
|
||||
id: valueTooltip
|
||||
|
||||
width: tooltipText.contentWidth + Theme.spacingS * 2
|
||||
width: tooltipText.reservedWidth + Theme.spacingS * 2
|
||||
height: tooltipText.contentHeight + Theme.spacingXS * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainer
|
||||
@@ -251,10 +251,22 @@ Item {
|
||||
visible: slider.alwaysShowValue ? slider.showValue : ((sliderMouseArea.containsMouse && slider.showValue) || (slider.isDragging && slider.showValue))
|
||||
opacity: visible ? 1 : 0
|
||||
|
||||
StyledText {
|
||||
NumericText {
|
||||
id: tooltipText
|
||||
|
||||
text: (slider.valueOverride >= 0 ? Math.round(slider.valueOverride) : slider.value) + slider.unit
|
||||
reserveText: {
|
||||
let widest = "";
|
||||
const samples = [slider.minimum, slider.maximum];
|
||||
if (slider.valueOverride >= 0)
|
||||
samples.push(slider.valueOverride);
|
||||
for (let i = 0; i < samples.length; i++) {
|
||||
const candidate = Math.round(samples[i]) + slider.unit;
|
||||
if (candidate.length > widest.length)
|
||||
widest = candidate;
|
||||
}
|
||||
return widest;
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
|
||||
22
quickshell/Widgets/NumericText.qml
Normal file
22
quickshell/Widgets/NumericText.qml
Normal file
@@ -0,0 +1,22 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
StyledText {
|
||||
id: root
|
||||
|
||||
property string reserveText: ""
|
||||
readonly property real reservedWidth: reserveText !== "" ? Math.max(contentWidth, reserveMetrics.width) : contentWidth
|
||||
|
||||
isMonospace: true
|
||||
wrapMode: Text.NoWrap
|
||||
|
||||
StyledTextMetrics {
|
||||
id: reserveMetrics
|
||||
isMonospace: root.isMonospace
|
||||
font.pixelSize: root.font.pixelSize
|
||||
font.family: root.font.family
|
||||
font.weight: root.font.weight
|
||||
font.hintingPreference: root.font.hintingPreference
|
||||
text: root.reserveText
|
||||
}
|
||||
}
|
||||
3
quickshell/matugen/configs/zed.toml
Normal file
3
quickshell/matugen/configs/zed.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[templates.dmszed]
|
||||
input_path = "SHELL_DIR/matugen/templates/dank-zed.json"
|
||||
output_path = "CONFIG_DIR/zed/themes/dank-zed-theme.json"
|
||||
1523
quickshell/matugen/templates/dank-zed.json
Normal file
1523
quickshell/matugen/templates/dank-zed.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,8 @@ LANGUAGES = {
|
||||
"hu": "hu.json",
|
||||
"fa": "fa.json",
|
||||
"fr": "fr.json",
|
||||
"nl": "nl.json"
|
||||
"nl": "nl.json",
|
||||
"ru": "ru.json"
|
||||
}
|
||||
|
||||
def error(msg):
|
||||
|
||||
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
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
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
6949
quickshell/translations/poexports/ru.json
Normal file
6949
quickshell/translations/poexports/ru.json
Normal file
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
File diff suppressed because it is too large
Load Diff
@@ -2571,6 +2571,23 @@
|
||||
"theme"
|
||||
]
|
||||
},
|
||||
{
|
||||
"section": "matugenTemplateZed",
|
||||
"label": "Zed",
|
||||
"tabIndex": 10,
|
||||
"category": "Theme & Colors",
|
||||
"keywords": [
|
||||
"appearance",
|
||||
"colors",
|
||||
"look",
|
||||
"matugen",
|
||||
"scheme",
|
||||
"style",
|
||||
"template",
|
||||
"theme",
|
||||
"zed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"section": "matugenTemplateFirefox",
|
||||
"label": "Firefox",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user