mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-02 03:28:28 -04:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| accdd0972c | |||
| 00c28f69fd | |||
| 2e0337b68c | |||
| 4975960ea4 | |||
| 0381a112b3 | |||
| ed5e2a22c0 | |||
| 847718459c | |||
| 95b120b475 | |||
| 4dd2f1d5be | |||
| bf5faf725b | |||
| e5eff919b8 | |||
| 606115748a | |||
| ad936bfdc9 | |||
| 110396cb2a | |||
| 7b08e61863 | |||
| 876ead665b |
@@ -44,6 +44,7 @@ type TemplateDef struct {
|
||||
ID string
|
||||
Commands []string
|
||||
Flatpaks []string
|
||||
ConfigDirs []string
|
||||
ConfigFile string
|
||||
Kind TemplateKind
|
||||
RunUnconditionally bool
|
||||
@@ -60,9 +61,9 @@ var templateRegistry = []TemplateDef{
|
||||
{ID: "firefox", Commands: []string{"firefox"}, ConfigFile: "firefox.toml"},
|
||||
{ID: "pywalfox", Commands: []string{"pywalfox"}, ConfigFile: "pywalfox.toml"},
|
||||
{ID: "zenbrowser", Commands: []string{"zen", "zen-browser", "zen-beta", "zen-twilight"}, Flatpaks: []string{"app.zen_browser.zen"}, ConfigFile: "zenbrowser.toml"},
|
||||
{ID: "vesktop", Commands: []string{"vesktop"}, Flatpaks: []string{"dev.vencord.Vesktop"}, ConfigFile: "vesktop.toml"},
|
||||
{ID: "vencord", Commands: []string{"discord", "Discord", "discord-canary", "DiscordCanary"}, Flatpaks: []string{"com.discordapp.Discord", "com.discordapp.DiscordCanary"}, ConfigFile: "vencord.toml"},
|
||||
{ID: "equibop", Commands: []string{"equibop"}, ConfigFile: "equibop.toml"},
|
||||
{ID: "vesktop", Commands: []string{"vesktop"}, Flatpaks: []string{"dev.vencord.Vesktop"}, ConfigDirs: []string{"vesktop"}, ConfigFile: "vesktop.toml"},
|
||||
{ID: "vencord", Commands: []string{"discord", "Discord", "discord-canary", "DiscordCanary"}, Flatpaks: []string{"com.discordapp.Discord", "com.discordapp.DiscordCanary"}, ConfigDirs: []string{"Vencord"}, ConfigFile: "vencord.toml"},
|
||||
{ID: "equibop", Commands: []string{"equibop"}, ConfigDirs: []string{"equibop"}, ConfigFile: "equibop.toml"},
|
||||
{ID: "ghostty", Commands: []string{"ghostty"}, ConfigFile: "ghostty.toml", Kind: TemplateKindTerminal},
|
||||
{ID: "kitty", Commands: []string{"kitty"}, ConfigFile: "kitty.toml", Kind: TemplateKindTerminal},
|
||||
{ID: "foot", Commands: []string{"foot"}, ConfigFile: "foot.toml", Kind: TemplateKindTerminal},
|
||||
@@ -459,9 +460,9 @@ output_path = '%s'
|
||||
case TemplateKindGTK:
|
||||
switch opts.Mode {
|
||||
case ColorModeLight:
|
||||
appendConfig(opts, cfgFile, nil, nil, "gtk3-light.toml")
|
||||
appendConfig(opts, cfgFile, nil, nil, nil, "gtk3-light.toml")
|
||||
default:
|
||||
appendConfig(opts, cfgFile, nil, nil, "gtk3-dark.toml")
|
||||
appendConfig(opts, cfgFile, nil, nil, nil, "gtk3-dark.toml")
|
||||
}
|
||||
case TemplateKindTerminal:
|
||||
appendTerminalConfig(opts, cfgFile, tmpDir, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
|
||||
@@ -474,10 +475,10 @@ output_path = '%s'
|
||||
appendVSCodeConfig(cfgFile, "vscode-insiders", filepath.Join(homeDir, ".vscode-insiders/extensions"), opts.ShellDir)
|
||||
case TemplateKindEmacs:
|
||||
if utils.EmacsConfigDir() != "" {
|
||||
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
|
||||
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigDirs, tmpl.ConfigFile)
|
||||
}
|
||||
default:
|
||||
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
|
||||
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigDirs, tmpl.ConfigFile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,13 +513,14 @@ func appendConfig(
|
||||
cfgFile *os.File,
|
||||
checkCmd []string,
|
||||
checkFlatpaks []string,
|
||||
checkConfigDirs []string,
|
||||
fileName string,
|
||||
) {
|
||||
configPath := filepath.Join(opts.ShellDir, "matugen", "configs", fileName)
|
||||
if _, err := os.Stat(configPath); err != nil {
|
||||
return
|
||||
}
|
||||
if !appExists(opts.AppChecker, checkCmd, checkFlatpaks) {
|
||||
if !appExists(opts.AppChecker, checkCmd, checkFlatpaks) && !configDirExists(checkConfigDirs) {
|
||||
return
|
||||
}
|
||||
data, err := os.ReadFile(configPath)
|
||||
@@ -599,6 +601,20 @@ func templateSessionActive(tmpl TemplateDef) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func configDirExists(names []string) bool {
|
||||
configHome := utils.XDGConfigHome()
|
||||
if configHome == "" {
|
||||
return false
|
||||
}
|
||||
for _, name := range names {
|
||||
info, err := os.Stat(filepath.Join(configHome, name))
|
||||
if err == nil && info.IsDir() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func appExists(checker utils.AppChecker, checkCmd []string, checkFlatpaks []string) bool {
|
||||
// Both nil is treated as "skip check" / unconditionally run
|
||||
if checkCmd == nil && checkFlatpaks == nil {
|
||||
@@ -1096,7 +1112,7 @@ func CheckTemplates(checker utils.AppChecker) []TemplateCheck {
|
||||
case tmpl.Kind == TemplateKindEmacs:
|
||||
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks) && utils.EmacsConfigDir() != ""
|
||||
default:
|
||||
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks) && templateSessionActive(tmpl)
|
||||
detected = (appExists(checker, tmpl.Commands, tmpl.Flatpaks) || configDirExists(tmpl.ConfigDirs)) && templateSessionActive(tmpl)
|
||||
}
|
||||
|
||||
checks = append(checks, TemplateCheck{ID: tmpl.ID, Detected: detected})
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestAppendConfigBinaryExists(t *testing.T) {
|
||||
|
||||
opts := &Options{ShellDir: shellDir, AppChecker: mockChecker}
|
||||
|
||||
appendConfig(opts, cfgFile, []string{"sh"}, nil, "test.toml")
|
||||
appendConfig(opts, cfgFile, []string{"sh"}, nil, nil, "test.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
@@ -82,7 +82,7 @@ func TestAppendConfigBinaryDoesNotExist(t *testing.T) {
|
||||
|
||||
opts := &Options{ShellDir: shellDir, AppChecker: mockChecker}
|
||||
|
||||
appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{}, "test.toml")
|
||||
appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{}, nil, "test.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
@@ -122,7 +122,7 @@ func TestAppendConfigFlatpakExists(t *testing.T) {
|
||||
|
||||
opts := &Options{ShellDir: shellDir, AppChecker: mockChecker}
|
||||
|
||||
appendConfig(opts, cfgFile, nil, []string{"app.zen_browser.zen"}, "test.toml")
|
||||
appendConfig(opts, cfgFile, nil, []string{"app.zen_browser.zen"}, nil, "test.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
@@ -163,7 +163,7 @@ func TestAppendConfigFlatpakDoesNotExist(t *testing.T) {
|
||||
|
||||
opts := &Options{ShellDir: shellDir, AppChecker: mockChecker}
|
||||
|
||||
appendConfig(opts, cfgFile, []string{}, []string{"com.nonexistent.flatpak"}, "test.toml")
|
||||
appendConfig(opts, cfgFile, []string{}, []string{"com.nonexistent.flatpak"}, nil, "test.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
@@ -203,7 +203,7 @@ func TestAppendConfigBothExist(t *testing.T) {
|
||||
|
||||
opts := &Options{ShellDir: shellDir, AppChecker: mockChecker}
|
||||
|
||||
appendConfig(opts, cfgFile, []string{"sh"}, []string{"app.zen_browser.zen"}, "test.toml")
|
||||
appendConfig(opts, cfgFile, []string{"sh"}, []string{"app.zen_browser.zen"}, nil, "test.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
@@ -244,7 +244,7 @@ func TestAppendConfigNeitherExists(t *testing.T) {
|
||||
|
||||
opts := &Options{ShellDir: shellDir, AppChecker: mockChecker}
|
||||
|
||||
appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{"com.nonexistent.flatpak"}, "test.toml")
|
||||
appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{"com.nonexistent.flatpak"}, nil, "test.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
@@ -281,7 +281,7 @@ func TestAppendConfigNoChecks(t *testing.T) {
|
||||
|
||||
opts := &Options{ShellDir: shellDir}
|
||||
|
||||
appendConfig(opts, cfgFile, nil, nil, "test.toml")
|
||||
appendConfig(opts, cfgFile, nil, nil, nil, "test.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
@@ -312,7 +312,7 @@ func TestAppendConfigFileDoesNotExist(t *testing.T) {
|
||||
|
||||
opts := &Options{ShellDir: shellDir}
|
||||
|
||||
appendConfig(opts, cfgFile, nil, nil, "nonexistent.toml")
|
||||
appendConfig(opts, cfgFile, nil, nil, nil, "nonexistent.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
@@ -487,3 +487,90 @@ func TestBuildMergedConfigSkipsMangowcWithoutActiveSession(t *testing.T) {
|
||||
}
|
||||
assert.NotContains(t, string(output), "[templates.dmsmango]")
|
||||
}
|
||||
|
||||
func TestAppendConfigConfigDirExists(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
shellDir := filepath.Join(tempDir, "shell")
|
||||
configsDir := filepath.Join(shellDir, "matugen", "configs")
|
||||
if err := os.MkdirAll(configsDir, 0o755); err != nil {
|
||||
t.Fatalf("failed to create configs dir: %v", err)
|
||||
}
|
||||
|
||||
testConfig := "vencord config content"
|
||||
if err := os.WriteFile(filepath.Join(configsDir, "vencord.toml"), []byte(testConfig), 0o644); err != nil {
|
||||
t.Fatalf("failed to write config: %v", err)
|
||||
}
|
||||
|
||||
configHome := filepath.Join(tempDir, "config")
|
||||
if err := os.MkdirAll(filepath.Join(configHome, "Vencord"), 0o755); err != nil {
|
||||
t.Fatalf("failed to create Vencord config dir: %v", err)
|
||||
}
|
||||
t.Setenv("XDG_CONFIG_HOME", configHome)
|
||||
|
||||
outFile := filepath.Join(tempDir, "output.toml")
|
||||
cfgFile, err := os.Create(outFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer cfgFile.Close()
|
||||
|
||||
mockChecker := mocks_utils.NewMockAppChecker(t)
|
||||
mockChecker.EXPECT().AnyCommandExists("nonexistent-binary-12345").Return(false)
|
||||
mockChecker.EXPECT().AnyFlatpakExists("com.nonexistent.flatpak").Return(false)
|
||||
|
||||
opts := &Options{ShellDir: shellDir, AppChecker: mockChecker}
|
||||
|
||||
appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{"com.nonexistent.flatpak"}, []string{"Vencord"}, "vencord.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read output: %v", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, testConfig+"\n", string(output))
|
||||
}
|
||||
|
||||
func TestAppendConfigConfigDirDoesNotExist(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
shellDir := filepath.Join(tempDir, "shell")
|
||||
configsDir := filepath.Join(shellDir, "matugen", "configs")
|
||||
if err := os.MkdirAll(configsDir, 0o755); err != nil {
|
||||
t.Fatalf("failed to create configs dir: %v", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath.Join(configsDir, "vencord.toml"), []byte("vencord config content"), 0o644); err != nil {
|
||||
t.Fatalf("failed to write config: %v", err)
|
||||
}
|
||||
|
||||
configHome := filepath.Join(tempDir, "config")
|
||||
if err := os.MkdirAll(configHome, 0o755); err != nil {
|
||||
t.Fatalf("failed to create config home: %v", err)
|
||||
}
|
||||
t.Setenv("XDG_CONFIG_HOME", configHome)
|
||||
|
||||
outFile := filepath.Join(tempDir, "output.toml")
|
||||
cfgFile, err := os.Create(outFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer cfgFile.Close()
|
||||
|
||||
mockChecker := mocks_utils.NewMockAppChecker(t)
|
||||
mockChecker.EXPECT().AnyCommandExists("nonexistent-binary-12345").Return(false)
|
||||
mockChecker.EXPECT().AnyFlatpakExists("com.nonexistent.flatpak").Return(false)
|
||||
|
||||
opts := &Options{ShellDir: shellDir, AppChecker: mockChecker}
|
||||
|
||||
appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{"com.nonexistent.flatpak"}, []string{"Vencord"}, "vencord.toml")
|
||||
|
||||
cfgFile.Close()
|
||||
output, err := os.ReadFile(outFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read output: %v", err)
|
||||
}
|
||||
|
||||
assert.Empty(t, string(output))
|
||||
}
|
||||
|
||||
@@ -65,16 +65,35 @@ Singleton {
|
||||
|
||||
function _openPopout(popout) {
|
||||
if (popout.dashVisible !== undefined) {
|
||||
if (popout.dashVisible && !popout.shouldBeVisible && !popout.isClosing)
|
||||
let flagStayedTrue = popout.dashVisible === true;
|
||||
if (popout.dashVisible && !popout.shouldBeVisible && !popout.isClosing) {
|
||||
popout.dashVisible = false;
|
||||
flagStayedTrue = false;
|
||||
}
|
||||
popout.dashVisible = true;
|
||||
// Flag already true (e.g. retargeting to another monitor) won't re-fire
|
||||
// the change handler, so drive the surface open explicitly.
|
||||
if (flagStayedTrue)
|
||||
_ensureSurfaceOpen(popout);
|
||||
return;
|
||||
}
|
||||
if (popout.notificationHistoryVisible !== undefined) {
|
||||
const flagStayedTrue = popout.notificationHistoryVisible === true;
|
||||
popout.notificationHistoryVisible = true;
|
||||
if (flagStayedTrue)
|
||||
_ensureSurfaceOpen(popout);
|
||||
return;
|
||||
}
|
||||
popout.open();
|
||||
_ensureSurfaceOpen(popout);
|
||||
}
|
||||
|
||||
function _ensureSurfaceOpen(popout) {
|
||||
if (typeof popout.present === "function") {
|
||||
popout.present();
|
||||
return;
|
||||
}
|
||||
if (typeof popout.open === "function")
|
||||
popout.open();
|
||||
}
|
||||
|
||||
function _closePopout(popout) {
|
||||
|
||||
@@ -139,6 +139,14 @@ Item {
|
||||
return;
|
||||
fadeDpmsWindowLoader.loadedWindow.cancelFade();
|
||||
}
|
||||
|
||||
function onMonitorsOffChanged() {
|
||||
if (IdleService.monitorsOff)
|
||||
return;
|
||||
if (!fadeDpmsWindowLoader.loadedWindow)
|
||||
return;
|
||||
fadeDpmsWindowLoader.loadedWindow.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ DankModal {
|
||||
property real holdProgress: 0
|
||||
property bool showHoldHint: false
|
||||
property bool holdFromKeyboard: false
|
||||
property string pendingKeyAction: ""
|
||||
property string committedAction: ""
|
||||
|
||||
readonly property bool needsConfirmation: SettingsData.powerActionConfirm
|
||||
readonly property int holdDurationMs: SettingsData.powerActionHoldDuration * 1000
|
||||
@@ -39,10 +39,16 @@ DankModal {
|
||||
return action !== "lock" && action !== "restart";
|
||||
}
|
||||
|
||||
function actionWakesOnKeyRelease(action) {
|
||||
return action === "suspend" || action === "hibernate";
|
||||
}
|
||||
|
||||
function startHold(action, actionIndex) {
|
||||
if (committedAction !== "")
|
||||
return;
|
||||
if (!needsConfirmation || !actionNeedsConfirm(action)) {
|
||||
if (holdFromKeyboard) {
|
||||
pendingKeyAction = action;
|
||||
if (holdFromKeyboard && actionWakesOnKeyRelease(action)) {
|
||||
commitAction(action, actionIndex);
|
||||
return;
|
||||
}
|
||||
executeAction(action);
|
||||
@@ -56,7 +62,6 @@ DankModal {
|
||||
}
|
||||
|
||||
function cancelHold() {
|
||||
pendingKeyAction = "";
|
||||
if (holdAction === "")
|
||||
return;
|
||||
const wasHolding = holdProgress > 0;
|
||||
@@ -76,8 +81,8 @@ DankModal {
|
||||
return;
|
||||
}
|
||||
holdTimer.stop();
|
||||
if (holdFromKeyboard) {
|
||||
pendingKeyAction = holdAction;
|
||||
if (holdFromKeyboard && actionWakesOnKeyRelease(holdAction)) {
|
||||
commitAction(holdAction, holdActionIndex);
|
||||
return;
|
||||
}
|
||||
const action = holdAction;
|
||||
@@ -87,6 +92,25 @@ DankModal {
|
||||
executeAction(action);
|
||||
}
|
||||
|
||||
function commitAction(action, actionIndex) {
|
||||
holdTimer.stop();
|
||||
holdAction = "";
|
||||
holdActionIndex = actionIndex;
|
||||
committedAction = action;
|
||||
commitFallbackTimer.restart();
|
||||
}
|
||||
|
||||
function executeCommittedAction() {
|
||||
if (committedAction === "")
|
||||
return;
|
||||
commitFallbackTimer.stop();
|
||||
const action = committedAction;
|
||||
committedAction = "";
|
||||
holdActionIndex = -1;
|
||||
holdProgress = 0;
|
||||
executeAction(action);
|
||||
}
|
||||
|
||||
signal switchUserRequested
|
||||
|
||||
function executeAction(action) {
|
||||
@@ -128,6 +152,12 @@ DankModal {
|
||||
onTriggered: root.showHoldHint = false
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: commitFallbackTimer
|
||||
interval: 5000
|
||||
onTriggered: root.executeCommittedAction()
|
||||
}
|
||||
|
||||
function openCentered() {
|
||||
parentBounds = Qt.rect(0, 0, 0, 0);
|
||||
parentScreen = null;
|
||||
@@ -286,7 +316,8 @@ DankModal {
|
||||
holdProgress = 0;
|
||||
showHoldHint = false;
|
||||
holdFromKeyboard = false;
|
||||
pendingKeyAction = "";
|
||||
committedAction = "";
|
||||
commitFallbackTimer.stop();
|
||||
updateVisibleActions();
|
||||
const defaultIndex = getDefaultActionIndex();
|
||||
selectedIndex = defaultIndex;
|
||||
@@ -309,6 +340,10 @@ DankModal {
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
if (committedAction !== "") {
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
holdFromKeyboard = true;
|
||||
if (SettingsData.powerMenuGridLayout) {
|
||||
handleGridNavigation(event, true);
|
||||
@@ -321,6 +356,11 @@ DankModal {
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
if (committedAction !== "") {
|
||||
event.accepted = true;
|
||||
executeCommittedAction();
|
||||
return;
|
||||
}
|
||||
if (SettingsData.powerMenuGridLayout) {
|
||||
handleGridNavigation(event, false);
|
||||
} else {
|
||||
@@ -331,16 +371,7 @@ DankModal {
|
||||
function handleListNavigation(event, isPressed) {
|
||||
if (!isPressed) {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_R || event.key === Qt.Key_X || event.key === Qt.Key_L || event.key === Qt.Key_S || event.key === Qt.Key_H || event.key === Qt.Key_D || (event.key === Qt.Key_P && !(event.modifiers & Qt.ControlModifier))) {
|
||||
if (pendingKeyAction !== "") {
|
||||
const action = pendingKeyAction;
|
||||
pendingKeyAction = "";
|
||||
holdAction = "";
|
||||
holdActionIndex = -1;
|
||||
holdProgress = 0;
|
||||
executeAction(action);
|
||||
} else {
|
||||
cancelHold();
|
||||
}
|
||||
cancelHold();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
@@ -434,16 +465,7 @@ DankModal {
|
||||
function handleGridNavigation(event, isPressed) {
|
||||
if (!isPressed) {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_R || event.key === Qt.Key_X || event.key === Qt.Key_L || event.key === Qt.Key_S || event.key === Qt.Key_H || event.key === Qt.Key_D || (event.key === Qt.Key_P && !(event.modifiers & Qt.ControlModifier))) {
|
||||
if (pendingKeyAction !== "") {
|
||||
const action = pendingKeyAction;
|
||||
pendingKeyAction = "";
|
||||
holdAction = "";
|
||||
holdActionIndex = -1;
|
||||
holdProgress = 0;
|
||||
executeAction(action);
|
||||
} else {
|
||||
cancelHold();
|
||||
}
|
||||
cancelHold();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
@@ -812,6 +834,7 @@ DankModal {
|
||||
|
||||
Row {
|
||||
id: hintRow
|
||||
readonly property bool selectedNeedsHold: root.actionNeedsConfirm(root.getActionAtIndex(root.selectedIndex))
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Theme.spacingS
|
||||
@@ -826,7 +849,13 @@ DankModal {
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: root.showHoldHint ? "warning" : "touch_app"
|
||||
name: {
|
||||
if (root.showHoldHint)
|
||||
return "warning";
|
||||
if (!hintRow.selectedNeedsHold)
|
||||
return "bolt";
|
||||
return "touch_app";
|
||||
}
|
||||
size: Theme.fontSizeSmall
|
||||
color: root.showHoldHint ? Theme.warning : Theme.surfaceTextSecondary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -836,8 +865,12 @@ DankModal {
|
||||
readonly property real totalMs: SettingsData.powerActionHoldDuration * 1000
|
||||
readonly property int remainingMs: Math.ceil(totalMs * (1 - root.holdProgress))
|
||||
text: {
|
||||
if (root.committedAction !== "")
|
||||
return I18n.tr("Release to confirm");
|
||||
if (root.showHoldHint)
|
||||
return I18n.tr("Hold longer to confirm");
|
||||
if (!hintRow.selectedNeedsHold)
|
||||
return I18n.tr("Activates immediately");
|
||||
if (root.holdProgress > 0) {
|
||||
if (totalMs < 1000)
|
||||
return I18n.tr("Hold to confirm (%1 ms)").arg(remainingMs);
|
||||
|
||||
@@ -77,6 +77,12 @@ DankPopout {
|
||||
|
||||
readonly property color _containerBg: Theme.nestedSurface
|
||||
|
||||
// Defer open one tick so screen-change geometry settles before the surface
|
||||
// maps; a synchronous open churns the surface and loses the blur on a switch.
|
||||
function present() {
|
||||
Qt.callLater(open);
|
||||
}
|
||||
|
||||
function openWithSection(section) {
|
||||
StateUtils.openWithSection(root, section);
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ BasePill {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: mouse => root.triggerRipple(this, mouse.x, mouse.y)
|
||||
onClicked: function (mouse) {
|
||||
onPressed: function (mouse) {
|
||||
root.triggerRipple(this, mouse.x, mouse.y);
|
||||
switch (mouse.button) {
|
||||
case Qt.RightButton:
|
||||
openContextMenu();
|
||||
|
||||
@@ -1250,13 +1250,8 @@ BasePill {
|
||||
|
||||
if (root.isVerticalOrientation) {
|
||||
const edge = root.axis?.edge;
|
||||
if (edge === "left") {
|
||||
const targetX = overflowMenu.anchorPos.x;
|
||||
return Math.max(left, Math.min(right, targetX));
|
||||
} else {
|
||||
const targetX = overflowMenu.anchorPos.x - alignedWidth;
|
||||
return Math.max(left, Math.min(right, targetX));
|
||||
}
|
||||
const targetX = edge === "left" ? overflowMenu.anchorPos.x : overflowMenu.anchorPos.x - alignedWidth;
|
||||
return Math.max(10, Math.min(overflowMenu.width - alignedWidth - 10, targetX));
|
||||
} else {
|
||||
const want = overflowMenu.anchorPos.x - alignedWidth / 2;
|
||||
return Math.max(left, Math.min(right, want));
|
||||
@@ -1271,13 +1266,8 @@ BasePill {
|
||||
const want = overflowMenu.anchorPos.y - alignedHeight / 2;
|
||||
return Math.max(top, Math.min(bottom, want));
|
||||
} else {
|
||||
if (root.isAtBottom) {
|
||||
const targetY = overflowMenu.anchorPos.y - alignedHeight;
|
||||
return Math.max(top, Math.min(bottom, targetY));
|
||||
} else {
|
||||
const targetY = overflowMenu.anchorPos.y;
|
||||
return Math.max(top, Math.min(bottom, targetY));
|
||||
}
|
||||
const targetY = root.isAtBottom ? overflowMenu.anchorPos.y - alignedHeight : overflowMenu.anchorPos.y;
|
||||
return Math.max(10, Math.min(overflowMenu.height - alignedHeight - 10, targetY));
|
||||
}
|
||||
})(), overflowMenu.dpr)
|
||||
|
||||
@@ -1770,13 +1760,8 @@ BasePill {
|
||||
|
||||
if (menuRoot.isVertical) {
|
||||
const edge = menuRoot.axis?.edge;
|
||||
if (edge === "left") {
|
||||
const targetX = menuWindow.anchorPos.x;
|
||||
return Math.max(left, Math.min(right, targetX));
|
||||
} else {
|
||||
const targetX = menuWindow.anchorPos.x - alignedWidth;
|
||||
return Math.max(left, Math.min(right, targetX));
|
||||
}
|
||||
const targetX = edge === "left" ? menuWindow.anchorPos.x : menuWindow.anchorPos.x - alignedWidth;
|
||||
return Math.max(10, Math.min(menuWindow.width - alignedWidth - 10, targetX));
|
||||
} else {
|
||||
const want = menuWindow.anchorPos.x - alignedWidth / 2;
|
||||
return Math.max(left, Math.min(right, want));
|
||||
@@ -1791,13 +1776,8 @@ BasePill {
|
||||
const want = menuWindow.anchorPos.y - alignedHeight / 2;
|
||||
return Math.max(top, Math.min(bottom, want));
|
||||
} else {
|
||||
if (menuRoot.isAtBottom) {
|
||||
const targetY = menuWindow.anchorPos.y - alignedHeight;
|
||||
return Math.max(top, Math.min(bottom, targetY));
|
||||
} else {
|
||||
const targetY = menuWindow.anchorPos.y;
|
||||
return Math.max(top, Math.min(bottom, targetY));
|
||||
}
|
||||
const targetY = menuRoot.isAtBottom ? menuWindow.anchorPos.y - alignedHeight : menuWindow.anchorPos.y;
|
||||
return Math.max(10, Math.min(menuWindow.height - alignedHeight - 10, targetY));
|
||||
}
|
||||
})(), menuWindow.dpr)
|
||||
|
||||
|
||||
@@ -1079,7 +1079,7 @@ Rectangle {
|
||||
|
||||
Text {
|
||||
text: I18n.tr("Add a task...", "placeholder in the new-task input field")
|
||||
color: Theme.onSurface_38
|
||||
color: Theme.outlineButton
|
||||
visible: taskInput.text.length === 0
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
@@ -19,6 +18,7 @@ Card {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
visible: !WeatherService.weather.available
|
||||
z: 1
|
||||
|
||||
DankSpinner {
|
||||
size: 24
|
||||
@@ -42,9 +42,9 @@ Card {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Button {
|
||||
DankButton {
|
||||
text: I18n.tr("Refresh")
|
||||
flat: true
|
||||
buttonHeight: 32
|
||||
visible: !WeatherService.weather.loading
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onClicked: WeatherService.forceRefresh()
|
||||
|
||||
@@ -141,52 +141,27 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
DankActionButton {
|
||||
id: refreshButtonTwo
|
||||
name: "refresh"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.withAlpha(Theme.surfaceText, 0.4)
|
||||
anchors.top: parent.top
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
smoothTransform: isRefreshing
|
||||
|
||||
property bool isRefreshing: false
|
||||
iconName: isRefreshing ? "" : "refresh"
|
||||
iconColor: Theme.withAlpha(Theme.surfaceText, 0.4)
|
||||
tooltipText: I18n.tr("Refresh Weather")
|
||||
tooltipSide: "left"
|
||||
enabled: !isRefreshing
|
||||
|
||||
MouseArea {
|
||||
id: refreshButtonMouseAreaTwo
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: parent.enabled ? Qt.PointingHandCursor : Qt.ForbiddenCursor
|
||||
enabled: parent.enabled
|
||||
property bool isRefreshing: false
|
||||
|
||||
Timer {
|
||||
id: hoverDelayTwo
|
||||
interval: 300
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
refreshButtonTooltipTwo.show(I18n.tr("Refresh Weather"), refreshButtonTwo, 0, 0, "left");
|
||||
}
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
hoverDelayTwo.restart();
|
||||
}
|
||||
|
||||
onExited: {
|
||||
hoverDelayTwo.stop();
|
||||
refreshButtonTooltipTwo.hide();
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
refreshButtonTwo.isRefreshing = true;
|
||||
WeatherService.forceRefresh();
|
||||
refreshTimerTwo.restart();
|
||||
}
|
||||
onClicked: {
|
||||
isRefreshing = true;
|
||||
WeatherService.forceRefresh();
|
||||
refreshTimerTwo.restart();
|
||||
}
|
||||
|
||||
DankTooltipV2 {
|
||||
id: refreshButtonTooltipTwo
|
||||
DankSpinner {
|
||||
anchors.centerIn: parent
|
||||
size: refreshButtonTwo.iconSize
|
||||
visible: refreshButtonTwo.isRefreshing
|
||||
}
|
||||
|
||||
Timer {
|
||||
@@ -194,14 +169,6 @@ Item {
|
||||
interval: 2000
|
||||
onTriggered: refreshButtonTwo.isRefreshing = false
|
||||
}
|
||||
|
||||
RotationAnimator on rotation {
|
||||
running: refreshButtonTwo.isRefreshing
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1000
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -864,52 +831,28 @@ Item {
|
||||
onClicked: SessionData.setWeatherHourlyDetailed(!SessionData.weatherHourlyDetailed)
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
DankActionButton {
|
||||
id: refreshButton
|
||||
name: "refresh"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.withAlpha(Theme.surfaceText, 0.4)
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
smoothTransform: isRefreshing
|
||||
|
||||
property bool isRefreshing: false
|
||||
iconName: isRefreshing ? "" : "refresh"
|
||||
iconColor: Theme.withAlpha(Theme.surfaceText, 0.4)
|
||||
tooltipText: I18n.tr("Refresh Weather")
|
||||
tooltipSide: "left"
|
||||
enabled: !isRefreshing
|
||||
|
||||
MouseArea {
|
||||
id: refreshButtonMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: parent.enabled ? Qt.PointingHandCursor : Qt.ForbiddenCursor
|
||||
enabled: parent.enabled
|
||||
property bool isRefreshing: false
|
||||
|
||||
Timer {
|
||||
id: hoverDelay
|
||||
interval: 300
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
refreshButtonTooltip.show(I18n.tr("Refresh Weather"), refreshButton, 0, 0, "left");
|
||||
}
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
hoverDelay.restart();
|
||||
}
|
||||
|
||||
onExited: {
|
||||
hoverDelay.stop();
|
||||
refreshButtonTooltip.hide();
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
refreshButton.isRefreshing = true;
|
||||
WeatherService.forceRefresh();
|
||||
refreshTimer.restart();
|
||||
}
|
||||
onClicked: {
|
||||
isRefreshing = true;
|
||||
WeatherService.forceRefresh();
|
||||
refreshTimer.restart();
|
||||
}
|
||||
|
||||
DankTooltipV2 {
|
||||
id: refreshButtonTooltip
|
||||
DankSpinner {
|
||||
anchors.centerIn: parent
|
||||
size: refreshButton.iconSize
|
||||
visible: refreshButton.isRefreshing
|
||||
}
|
||||
|
||||
Timer {
|
||||
@@ -917,14 +860,6 @@ Item {
|
||||
interval: 2000
|
||||
onTriggered: refreshButton.isRefreshing = false
|
||||
}
|
||||
|
||||
RotationAnimator on rotation {
|
||||
running: refreshButton.isRefreshing
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1000
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
@@ -65,10 +66,23 @@ PanelWindow {
|
||||
}
|
||||
|
||||
function cancelFade() {
|
||||
dismiss();
|
||||
fadeCancelled();
|
||||
}
|
||||
|
||||
function dismiss() {
|
||||
fadeSeq.stop();
|
||||
fadeOverlay.opacity = 0.0;
|
||||
active = false;
|
||||
fadeCancelled();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: IdleService
|
||||
function onIsShellLockedChanged() {
|
||||
if (IdleService.isShellLocked)
|
||||
return;
|
||||
root.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
||||
@@ -10,6 +10,8 @@ import qs.Services
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
readonly property var log: Log.scoped("Lock")
|
||||
|
||||
property string sharedPasswordBuffer: ""
|
||||
property bool shouldLock: false
|
||||
|
||||
@@ -21,26 +23,13 @@ Scope {
|
||||
|
||||
onShouldLockChanged: {
|
||||
IdleService.isShellLocked = shouldLock;
|
||||
if (shouldLock && lockPowerOffArmed) {
|
||||
lockStateCheck.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: lockStateCheck
|
||||
interval: 100
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (sessionLock.locked && lockPowerOffArmed) {
|
||||
pendingLock = false;
|
||||
lockPowerOffArmed = false;
|
||||
beginPowerOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool lockInitiatedLocally: false
|
||||
property bool pendingLock: false
|
||||
readonly property int maxLockRetries: 3
|
||||
property int lockRetryAttempts: 0
|
||||
property bool lockRetryPending: false
|
||||
property bool lockPowerOffArmed: false
|
||||
property bool lockWakeAllowed: false
|
||||
property bool customLockerSpawned: false
|
||||
@@ -116,6 +105,24 @@ Scope {
|
||||
return true;
|
||||
}
|
||||
|
||||
function resetLockRetry() {
|
||||
lockRetryAttempts = 0;
|
||||
lockRetryPending = false;
|
||||
lockRetryTimer.stop();
|
||||
}
|
||||
|
||||
function handleLockLost() {
|
||||
if (lockRetryAttempts >= maxLockRetries) {
|
||||
log.error("Compositor refused session lock", maxLockRetries, "times - resetting lock state");
|
||||
forceReset();
|
||||
return;
|
||||
}
|
||||
lockRetryAttempts++;
|
||||
lockRetryPending = true;
|
||||
lockRetryTimer.restart();
|
||||
log.warn("Session lock lost while lock requested - retry", lockRetryAttempts, "/", maxLockRetries);
|
||||
}
|
||||
|
||||
function lock() {
|
||||
if (SettingsData.customPowerActionLock?.length > 0) {
|
||||
spawnCustomLocker();
|
||||
@@ -124,6 +131,7 @@ Scope {
|
||||
if (shouldLock || pendingLock)
|
||||
return;
|
||||
|
||||
resetLockRetry();
|
||||
lockInitiatedLocally = true;
|
||||
lockPowerOffArmed = powerOffOnLock;
|
||||
|
||||
@@ -139,7 +147,7 @@ Scope {
|
||||
|
||||
function lockAndOutputsOff() {
|
||||
IdleService.lockPowerOffRequested = true;
|
||||
if (sessionLock.locked) {
|
||||
if (sessionLock.secure) {
|
||||
beginPowerOff();
|
||||
return;
|
||||
}
|
||||
@@ -150,6 +158,7 @@ Scope {
|
||||
function unlock() {
|
||||
if (!shouldLock)
|
||||
return;
|
||||
resetLockRetry();
|
||||
lockInitiatedLocally = false;
|
||||
notifyLoginctl(false);
|
||||
shouldLock = false;
|
||||
@@ -160,6 +169,7 @@ Scope {
|
||||
pendingLock = false;
|
||||
shouldLock = false;
|
||||
customLockerSpawned = false;
|
||||
resetLockRetry();
|
||||
resetPowerOffFade();
|
||||
IdleService.lockPowerOffRequested = false;
|
||||
}
|
||||
@@ -198,6 +208,12 @@ Scope {
|
||||
shouldLock = false;
|
||||
}
|
||||
|
||||
function onSessionResumed() {
|
||||
if (!shouldLock || sessionLock.locked)
|
||||
return;
|
||||
resumeRelockTimer.restart();
|
||||
}
|
||||
|
||||
function onLoginctlStateChanged() {
|
||||
if (SessionService.active && pendingLock) {
|
||||
pendingLock = false;
|
||||
@@ -239,7 +255,7 @@ Scope {
|
||||
WlSessionLock {
|
||||
id: sessionLock
|
||||
|
||||
locked: shouldLock
|
||||
locked: shouldLock && !lockRetryPending
|
||||
|
||||
WlSessionLockSurface {
|
||||
id: lockSurface
|
||||
@@ -312,16 +328,23 @@ Scope {
|
||||
Connections {
|
||||
target: sessionLock
|
||||
|
||||
function onLockedChanged() {
|
||||
notifyLockedHint(sessionLock.locked);
|
||||
if (sessionLock.locked) {
|
||||
pendingLock = false;
|
||||
if (lockPowerOffArmed && powerOffOnLock)
|
||||
beginPowerOff();
|
||||
lockPowerOffArmed = false;
|
||||
function onSecureChanged() {
|
||||
notifyLockedHint(sessionLock.secure);
|
||||
if (!sessionLock.secure)
|
||||
return;
|
||||
}
|
||||
IdleService.dismissFadeToLock();
|
||||
lockRetryAttempts = 0;
|
||||
pendingLock = false;
|
||||
if (lockPowerOffArmed && powerOffOnLock)
|
||||
beginPowerOff();
|
||||
lockPowerOffArmed = false;
|
||||
}
|
||||
|
||||
function onLockedChanged() {
|
||||
if (sessionLock.locked)
|
||||
return;
|
||||
if (shouldLock && (IdleService.monitorsOff || lockPowerOffArmed || IdleService.lockPowerOffRequested))
|
||||
return;
|
||||
lockWakeAllowed = false;
|
||||
resetPowerOffFade();
|
||||
if (IdleService.monitorsOff && powerOffOnLock) {
|
||||
@@ -329,6 +352,8 @@ Scope {
|
||||
CompositorService.powerOnMonitors();
|
||||
}
|
||||
IdleService.lockPowerOffRequested = false;
|
||||
if (shouldLock && !lockRetryPending)
|
||||
handleLockLost();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,13 +385,15 @@ Scope {
|
||||
}
|
||||
|
||||
function isLocked(): bool {
|
||||
return sessionLock.locked;
|
||||
return sessionLock.secure;
|
||||
}
|
||||
|
||||
function status(): string {
|
||||
return JSON.stringify({
|
||||
shouldLock: root.shouldLock,
|
||||
sessionLockLocked: sessionLock.locked,
|
||||
sessionLockSecure: sessionLock.secure,
|
||||
lockRetryAttempts: root.lockRetryAttempts,
|
||||
lockInitiatedLocally: root.lockInitiatedLocally,
|
||||
pendingLock: root.pendingLock,
|
||||
loginctlLocked: SessionService.locked,
|
||||
@@ -395,6 +422,29 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: lockRetryTimer
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: root.lockRetryPending = false
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: resumeRelockTimer
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (!root.shouldLock || sessionLock.locked)
|
||||
return;
|
||||
if (IdleService.monitorsOff || root.lockPowerOffArmed || IdleService.lockPowerOffRequested)
|
||||
return;
|
||||
root.log.warn("Session lock dead after resume - re-locking");
|
||||
root.resetLockRetry();
|
||||
root.lockRetryPending = true;
|
||||
root.lockRetryPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: dpmsReapplyTimer
|
||||
interval: 100
|
||||
@@ -407,7 +457,7 @@ Scope {
|
||||
interval: 200
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (!sessionLock.locked)
|
||||
if (!sessionLock.secure)
|
||||
return;
|
||||
if (!powerOffOnLock)
|
||||
return;
|
||||
@@ -426,7 +476,7 @@ Scope {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: sessionLock.locked
|
||||
enabled: sessionLock.secure
|
||||
hoverEnabled: enabled
|
||||
onPressed: lockWakeDebounce.restart()
|
||||
onPositionChanged: lockWakeDebounce.restart()
|
||||
@@ -435,10 +485,10 @@ Scope {
|
||||
|
||||
FocusScope {
|
||||
anchors.fill: parent
|
||||
focus: sessionLock.locked
|
||||
focus: sessionLock.secure
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (!sessionLock.locked)
|
||||
if (!sessionLock.secure)
|
||||
return;
|
||||
lockWakeDebounce.restart();
|
||||
}
|
||||
|
||||
@@ -61,6 +61,11 @@ DankPopout {
|
||||
notificationHistoryVisible = !notificationHistoryVisible;
|
||||
}
|
||||
|
||||
// Re-open without toggling the flag (used when retargeting to another monitor).
|
||||
function present() {
|
||||
openSized();
|
||||
}
|
||||
|
||||
function openSized() {
|
||||
if (!notificationHistoryVisible)
|
||||
return;
|
||||
|
||||
@@ -24,6 +24,8 @@ Item {
|
||||
property string matugenPreviewRequestKey: ""
|
||||
property var installedRegistryThemes: []
|
||||
property var templateDetection: []
|
||||
readonly property var neovimDarkBaseThemes: ["aquarium", "ashes", "aylin", "ayu_dark", "bearded-arc", "carbonfox", "catppuccin", "chadracula", "chadracula-evondev", "chadtain", "chocolate", "darcula-dark", "dark_horizon", "decay", "default-dark", "doomchad", "eldritch", "embark", "everblush", "everforest", "falcon", "flexoki", "flouromachine", "gatekeeper", "github_dark", "gruvbox", "gruvchad", "hiberbee", "horizon", "jabuti", "jellybeans", "kanagawa", "kanagawa-dragon", "material-darker", "material-deep-ocean", "melange", "midnight_breeze", "mito-laser", "monekai", "monochrome", "mountain", "neofusion", "nightfox", "nightlamp", "nightowl", "nord", "obsidian-ember", "oceanic-next", "onedark", "onenord", "oxocarbon", "palenight", "pastelDark", "pastelbeans", "penumbra_dark", "poimandres", "radium", "rosepine", "rxyhn", "scaryforest", "seoul256_dark", "solarized_dark", "solarized_osaka", "starlight", "sweetpastel", "tokyodark", "tokyonight", "tomorrow_night", "tundra", "vesper", "vscode_dark", "wombat", "yoru", "zenburn"]
|
||||
readonly property var neovimLightBaseThemes: ["ayu_light", "blossom_light", "catppuccin-latte", "default-light", "everforest_light", "flex-light", "flexoki-light", "github_light", "gruvbox_light", "material-lighter", "nano-light", "oceanic-light", "one_light", "onenord_light", "penumbra_light", "rosepine-dawn", "seoul256_light", "solarized_light", "sunrise_breeze", "vscode_light"]
|
||||
readonly property var matugenSchemeColorMap: {
|
||||
const map = {};
|
||||
const mode = SessionData.isLightMode ? "light" : "dark";
|
||||
@@ -2666,7 +2668,7 @@ Item {
|
||||
description: "Base to derive dark theme from"
|
||||
visible: neovimThemeToggle.visible && neovimThemeToggle.checked
|
||||
currentValue: SettingsData.matugenTemplateNeovimSettings?.dark?.baseTheme ?? "github_dark"
|
||||
options: ["aquarium", "ashes", "aylin", "ayu_dark", "bearded-arc", "carbonfox", "catppuccin", "chadracula", "chadracula-evondev", "chadtain", "chocolate", "darcula-dark", "dark_horizon", "decay", "default-dark", "doomchad", "eldritch", "embark", "everblush", "everforest", "falcon", "flexoki", "flouromachine", "gatekeeper", "github_dark", "gruvbox", "gruvchad", "hiberbee", "horizon", "jabuti", "jellybeans", "kanagawa", "kanagawa-dragon", "material-darker", "material-deep-ocean", "melange", "midnight_breeze", "mito-laser", "monekai", "monochrome", "mountain", "neofusion", "nightfox", "nightlamp", "nightowl", "nord", "obsidian-ember", "oceanic-next", "onedark", "onenord", "oxocarbon", "palenight", "pastelDark", "pastelbeans", "penumbra_dark", "poimandres", "radium", "rosepine", "rxyhn", "scaryforest", "seoul256_dark", "solarized_dark", "solarized_osaka", "starlight", "sweetpastel", "tokyodark", "tokyonight", "tomorrow_night", "tundra", "vesper", "vscode_dark", "wombat", "yoru", "zenburn"]
|
||||
options: themeColorsTab.neovimDarkBaseThemes.concat(themeColorsTab.neovimLightBaseThemes)
|
||||
enableFuzzySearch: true
|
||||
onValueChanged: value => {
|
||||
const settings = SettingsData.matugenTemplateNeovimSettings;
|
||||
@@ -2683,7 +2685,7 @@ Item {
|
||||
description: "Base to derive light theme from"
|
||||
visible: neovimThemeToggle.visible && neovimThemeToggle.checked
|
||||
currentValue: SettingsData.matugenTemplateNeovimSettings?.light?.baseTheme ?? "github_light"
|
||||
options: ["ayu_light", "blossom_light", "catppuccin-latte", "default-light", "everforest_light", "flex-light", "flexoki-light", "github_light", "gruvbox_light", "material-lighter", "nano-light", "oceanic-light", "one_light", "onenord_light", "penumbra_light", "rosepine-dawn", "seoul256_light", "solarized_light", "sunrise_breeze", "vscode_light"]
|
||||
options: themeColorsTab.neovimLightBaseThemes.concat(themeColorsTab.neovimDarkBaseThemes)
|
||||
enableFuzzySearch: true
|
||||
onValueChanged: value => {
|
||||
const settings = SettingsData.matugenTemplateNeovimSettings;
|
||||
|
||||
@@ -1199,16 +1199,18 @@ Item {
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
DankFilterChips {
|
||||
id: transitionGroup
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
selectionMode: "multi"
|
||||
model: SessionData.availableWallpaperTransitions.filter(t => t !== "none")
|
||||
initialSelection: SessionData.includedTransitions
|
||||
currentSelection: SessionData.includedTransitions
|
||||
multiSelect: true
|
||||
model: SessionData.availableWallpaperTransitions.filter(t => t !== "none").map(t => ({
|
||||
"value": t,
|
||||
"label": t.replace(/\b\w/g, c => c.toUpperCase())
|
||||
}))
|
||||
selectedValues: SessionData.includedTransitions
|
||||
|
||||
onSelectionChanged: (index, selected) => {
|
||||
const transition = model[index];
|
||||
onSelectionToggled: (index, selected) => {
|
||||
const transition = model[index].value;
|
||||
let newIncluded = SessionData.includedTransitions.slice();
|
||||
|
||||
if (selected && !newIncluded.includes(transition)) {
|
||||
|
||||
@@ -847,7 +847,7 @@ EOFCONFIG
|
||||
|
||||
const maxVol = root.sinkMaxVolume;
|
||||
const clampedVolume = Math.max(0, Math.min(maxVol, percentage));
|
||||
Quickshell.execDetached(["wpctl", "set-volume", "-l", String(maxVol / 100), "@DEFAULT_AUDIO_SINK@", String(clampedVolume / 100)]);
|
||||
root.sink.audio.volume = clampedVolume / 100;
|
||||
return `Volume set to ${clampedVolume}%`;
|
||||
}
|
||||
|
||||
@@ -857,14 +857,16 @@ EOFCONFIG
|
||||
}
|
||||
|
||||
function adjustDefaultSinkVolume(step, direction) {
|
||||
const audio = root.sink.audio;
|
||||
const maxVol = root.sinkMaxVolume;
|
||||
const stepValue = outputVolumeStep(step);
|
||||
const currentVolume = Math.round(root.sink.audio.volume * 100);
|
||||
const currentVolume = Math.round(audio.volume * 100);
|
||||
const newVolume = Math.max(0, Math.min(maxVol, currentVolume + direction * stepValue));
|
||||
const suffix = direction > 0 ? "+" : "-";
|
||||
|
||||
Quickshell.execDetached(["wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "0"]);
|
||||
Quickshell.execDetached(["wpctl", "set-volume", "-l", String(maxVol / 100), "@DEFAULT_AUDIO_SINK@", `${stepValue}%${suffix}`]);
|
||||
if (audio.muted)
|
||||
audio.muted = false;
|
||||
|
||||
audio.volume = newVolume / 100;
|
||||
return newVolume;
|
||||
}
|
||||
|
||||
|
||||
@@ -511,7 +511,7 @@ Singleton {
|
||||
return MangoService.filterCurrentDisplay(toplevels, screenName);
|
||||
if (isHyprland)
|
||||
return filterHyprlandCurrentDisplaySafe(toplevels, screenName);
|
||||
return toplevels;
|
||||
return toplevels.filter(t => _toplevelOnScreen(t, screenName));
|
||||
}
|
||||
|
||||
function _screenName(screenOrName) {
|
||||
|
||||
@@ -80,6 +80,8 @@ Singleton {
|
||||
respectInhibitors: root.respectInhibitors
|
||||
enabled: false
|
||||
onIsIdleChanged: {
|
||||
if (!enabled)
|
||||
return;
|
||||
if (isIdle) {
|
||||
if (SettingsData.fadeToDpmsEnabled) {
|
||||
root.fadeToDpmsRequested();
|
||||
@@ -101,6 +103,8 @@ Singleton {
|
||||
respectInhibitors: root.respectInhibitors
|
||||
enabled: false
|
||||
onIsIdleChanged: {
|
||||
if (!enabled)
|
||||
return;
|
||||
if (isIdle) {
|
||||
root.requestMonitorOff();
|
||||
} else {
|
||||
@@ -115,6 +119,8 @@ Singleton {
|
||||
respectInhibitors: root.respectInhibitors
|
||||
enabled: false
|
||||
onIsIdleChanged: {
|
||||
if (!enabled)
|
||||
return;
|
||||
if (isIdle) {
|
||||
if (SettingsData.fadeToLockEnabled) {
|
||||
root.fadeToLockRequested();
|
||||
@@ -135,6 +141,8 @@ Singleton {
|
||||
respectInhibitors: root.respectInhibitors
|
||||
enabled: false
|
||||
onIsIdleChanged: {
|
||||
if (!enabled)
|
||||
return;
|
||||
if (isIdle)
|
||||
root.requestSuspend();
|
||||
}
|
||||
@@ -149,6 +157,8 @@ Singleton {
|
||||
respectInhibitors: false
|
||||
enabled: root.enabled && root.isShellLocked && root.monitorsOff && (SettingsData.lockScreenPowerOffMonitorsOnLock || root.lockPowerOffRequested)
|
||||
onIsIdleChanged: {
|
||||
if (!enabled)
|
||||
return;
|
||||
if (!isIdle && root.monitorsOff)
|
||||
root.requestMonitorOn();
|
||||
}
|
||||
|
||||
@@ -467,7 +467,7 @@ Singleton {
|
||||
if (now - root._lastWatchedReloadAt < 700)
|
||||
return;
|
||||
root._lastWatchedReloadAt = now;
|
||||
root.reloadConfig(true, false);
|
||||
root.reloadConfig(false, false);
|
||||
}
|
||||
|
||||
function reloadConfig(showToast, suppressWatch) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import Quickshell.Services.Polkit
|
||||
|
||||
PolkitAgent {}
|
||||
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Polkit
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
@@ -12,10 +11,13 @@ Singleton {
|
||||
readonly property bool disablePolkitIntegration: Quickshell.env("DMS_DISABLE_POLKIT") === "1"
|
||||
|
||||
readonly property bool polkitAvailable: !disablePolkitIntegration
|
||||
readonly property alias agent: polkitAgentInstance
|
||||
readonly property var agent: polkitAgentLoader.item
|
||||
|
||||
PolkitAgent {
|
||||
id: polkitAgentInstance
|
||||
Loader {
|
||||
id: polkitAgentLoader
|
||||
active: root.polkitAvailable
|
||||
asynchronous: false
|
||||
source: "PolkitAgentInstance.qml"
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
|
||||
@@ -7,12 +7,15 @@ Flow {
|
||||
|
||||
property var model: []
|
||||
property int currentIndex: 0
|
||||
property bool multiSelect: false
|
||||
property var selectedValues: []
|
||||
property int chipHeight: 32
|
||||
property int chipPadding: Theme.spacingM
|
||||
property bool showCheck: true
|
||||
property bool showCounts: true
|
||||
|
||||
signal selectionChanged(int index)
|
||||
signal selectionToggled(int index, bool selected)
|
||||
|
||||
spacing: Theme.spacingS
|
||||
width: parent ? parent.width : 400
|
||||
@@ -25,7 +28,8 @@ Flow {
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
property bool selected: index === root.currentIndex
|
||||
property var value: typeof modelData === "string" ? modelData : (modelData.value !== undefined ? modelData.value : (modelData.label || ""))
|
||||
property bool selected: root.multiSelect ? root.selectedValues.includes(value) : (index === root.currentIndex)
|
||||
property bool hovered: mouseArea.containsMouse
|
||||
property bool pressed: mouseArea.pressed
|
||||
property string label: typeof modelData === "string" ? modelData : (modelData.label || "")
|
||||
@@ -99,6 +103,10 @@ Flow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: mouse => chipRipple.trigger(mouse.x, mouse.y)
|
||||
onClicked: {
|
||||
if (root.multiSelect) {
|
||||
root.selectionToggled(chip.index, !chip.selected);
|
||||
return;
|
||||
}
|
||||
root.currentIndex = chip.index;
|
||||
root.selectionChanged(chip.index);
|
||||
}
|
||||
|
||||
@@ -79,6 +79,9 @@ Item {
|
||||
"rightBar": 0
|
||||
})
|
||||
property var screen: null
|
||||
// Output the surface binds to; retargeted only in open() while hidden. A live
|
||||
// screen change on a mapped surface loses the popup blur on niri.
|
||||
property var surfaceScreen: null
|
||||
readonly property bool frameGapStandaloneActive: CompositorService.frameConfiguredForScreen(screen) && !CompositorService.usesConnectedFrameChromeForScreen(screen)
|
||||
readonly property bool fluidStandaloneActive: Theme.isDirectionalEffect
|
||||
readonly property bool backgroundDismissWindowRequired: backgroundInteractive
|
||||
@@ -133,8 +136,6 @@ Item {
|
||||
signal popoutClosed
|
||||
signal backgroundClicked
|
||||
|
||||
property var _lastOpenedScreen: null
|
||||
|
||||
property int effectiveBarPosition: 0
|
||||
property real effectiveBarBottomGap: 0
|
||||
readonly property string autoBarShadowDirection: {
|
||||
@@ -338,14 +339,14 @@ Item {
|
||||
_frozenMaskWidth = maskWidth;
|
||||
_frozenMaskHeight = maskHeight;
|
||||
|
||||
const screenChanged = _lastOpenedScreen !== null && _lastOpenedScreen !== screen;
|
||||
const screenChanged = surfaceScreen !== null && surfaceScreen !== screen;
|
||||
if (screenChanged) {
|
||||
// Hide on this tick so Qt actually tears down the wl_surface; the show
|
||||
// gets deferred below so the unmap is processed before the remap.
|
||||
// Unmap before retargeting so the surface is destroyed and recreated
|
||||
// on the new output rather than live-migrated.
|
||||
contentWindow.visible = false;
|
||||
backgroundWindow.visible = false;
|
||||
}
|
||||
_lastOpenedScreen = screen;
|
||||
surfaceScreen = screen;
|
||||
|
||||
if (contentContainer && !shouldBeVisible) {
|
||||
// Snap morph closed only on a fresh open; on screen-change re-open we stay at 1
|
||||
@@ -559,7 +560,7 @@ Item {
|
||||
|
||||
PanelWindow {
|
||||
id: backgroundWindow
|
||||
screen: root.screen
|
||||
screen: root.surfaceScreen
|
||||
visible: false
|
||||
color: "transparent"
|
||||
// Skip buffer updates when there's nothing to render. Briefly flipped
|
||||
@@ -625,7 +626,7 @@ Item {
|
||||
|
||||
PanelWindow {
|
||||
id: contentWindow
|
||||
screen: root.screen
|
||||
screen: root.surfaceScreen
|
||||
visible: false
|
||||
color: "transparent"
|
||||
readonly property bool closeVisualActive: root.shouldBeVisible || root.isClosing
|
||||
|
||||
@@ -131,7 +131,6 @@
|
||||
@define-color content_view_bg @view_bg_color;
|
||||
@define-color text_view_bg @view_bg_color;
|
||||
* {
|
||||
padding: 0;
|
||||
-GtkToolButton-icon-spacing: 4;
|
||||
-GtkTextView-error-underline-color: @error_bg_color;
|
||||
-GtkScrolledWindow-scrollbar-spacing: 0;
|
||||
|
||||
@@ -44,6 +44,10 @@ if mode ~= nil then
|
||||
end
|
||||
end
|
||||
|
||||
if settings.terminalsAlwaysDark then
|
||||
vim.o.background = "dark"
|
||||
end
|
||||
|
||||
local current_file_path = debug.getinfo(1, "S").source:sub(2)
|
||||
local theme_base = deepGet(settings, { "matugenTemplateNeovimSettings", vim.o.background, "baseTheme" })
|
||||
or ("github_" .. vim.o.background)
|
||||
|
||||
Reference in New Issue
Block a user