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

Compare commits

...

18 Commits

Author SHA1 Message Date
bbedward
3b2ad9d1bd running apps: fix scroll events being propagated
fixes #1724
2026-02-18 10:12:15 -05:00
bbedward
27b7474180 matugen: make v4 detection more resilient 2026-02-18 09:55:45 -05:00
bbedward
63948d728e process list: fix scaling with fonts
fixes #1721
2026-02-18 09:55:45 -05:00
purian23
d219d3b873 dankinstall: Fix Debian ARM64 detection 2026-02-18 09:41:36 -05:00
bbedward
93ab290bc1 matugen: detect emacs directory
fixes #1720
2026-02-18 09:23:13 -05:00
bbedward
7335c5d79a osd: optimize bindings 2026-02-18 09:04:39 -05:00
bbedward
242ead722a screenshot: adjust cursor CLI option to be more explicit 2026-02-17 22:28:19 -05:00
bbedward
8a6d9696a8 settings: workaround crash 2026-02-17 22:20:01 -05:00
purian23
896b7ea242 notifications: Tweak animation scale & settings 2026-02-17 22:05:19 -05:00
bbedward
0c7f4c7828 settings: guard internal writes from watcher 2026-02-17 22:03:36 -05:00
bbedward
3d35af2a87 cc: fix plugin reloading in bar position changes 2026-02-17 17:24:22 -05:00
bbedward
fed3c36f84 popout: anchor height changing popout surfaces to top and bottom 2026-02-17 17:18:07 -05:00
bbedward
414d81aa40 workspaces: fix named workspace icons 2026-02-17 16:02:13 -05:00
bbedward
d548803769 dankinstall: no_anim on dms layers 2026-02-17 15:32:08 -05:00
bbedward
1180258394 system updater: fix hide no update option 2026-02-17 13:53:17 -05:00
bbedward
48a566a24b launcher: fix kb navigation not always showing last delegate in view 2026-02-17 13:07:43 -05:00
bbedward
3bc5d1df81 doctor: add qt6-imageformats check 2026-02-17 12:58:09 -05:00
bbedward
c7222e2e86 bump version, codename, disable changelog 2026-02-17 12:02:36 -05:00
40 changed files with 626 additions and 526 deletions

View File

@@ -45,9 +45,9 @@ body:
- type: textarea - type: textarea
id: dms_doctor id: dms_doctor
attributes: attributes:
label: dms doctor -v label: dms doctor -vC
description: Output of `dms doctor -v` command description: Output of `dms doctor -vC` command
placeholder: Paste the output of `dms doctor -v` here placeholder: Paste the output of `dms doctor -vC` here
validations: validations:
required: true required: true
- type: textarea - type: textarea

View File

@@ -30,9 +30,9 @@ body:
- type: textarea - type: textarea
id: dms_doctor id: dms_doctor
attributes: attributes:
label: dms doctor -v label: dms doctor -vC
description: Output of `dms doctor -v` command description: Output of `dms doctor -vC` command
placeholder: Paste the output of `dms doctor -v` here placeholder: Paste the output of `dms doctor -vC` here
validations: validations:
required: false required: false
- type: textarea - type: textarea

View File

@@ -649,40 +649,73 @@ func checkI2CAvailability() checkResult {
return checkResult{catOptionalFeatures, "I2C/DDC", statusOK, fmt.Sprintf("%d monitor(s) detected", len(devices)), "External monitor brightness control", doctorDocsURL + "#optional-features"} return checkResult{catOptionalFeatures, "I2C/DDC", statusOK, fmt.Sprintf("%d monitor(s) detected", len(devices)), "External monitor brightness control", doctorDocsURL + "#optional-features"}
} }
func checkKImageFormats() checkResult { func checkImageFormatPlugins() []checkResult {
url := doctorDocsURL + "#optional-features" url := doctorDocsURL + "#optional-features"
desc := "Extra image format support (AVIF, HEIF, JXL)"
pluginDir := findQtPluginDir() pluginDir := findQtPluginDir()
if pluginDir == "" { if pluginDir == "" {
return checkResult{catOptionalFeatures, "kimageformats", statusInfo, "Cannot detect (qtpaths not found)", desc, url} return []checkResult{
} {catOptionalFeatures, "qt6-imageformats", statusInfo, "Cannot detect (plugin dir not found)", "WebP, TIFF, JP2 support", url},
{catOptionalFeatures, "kimageformats", statusInfo, "Cannot detect (plugin dir not found)", "AVIF, HEIF, JXL support", url},
imageFormatsDir := filepath.Join(pluginDir, "imageformats")
keyPlugins := []struct{ file, format string }{
{"kimg_avif.so", "AVIF"},
{"kimg_heif.so", "HEIF"},
{"kimg_jxl.so", "JXL"},
{"kimg_exr.so", "EXR"},
}
var found []string
for _, p := range keyPlugins {
if _, err := os.Stat(filepath.Join(imageFormatsDir, p.file)); err == nil {
found = append(found, p.format)
} }
} }
if len(found) == 0 { imageFormatsDir := filepath.Join(pluginDir, "imageformats")
return checkResult{catOptionalFeatures, "kimageformats", statusWarn, "Not installed", desc, url}
type pluginCheck struct {
name string
desc string
plugins []struct{ file, format string }
} }
details := "" checks := []pluginCheck{
if doctorVerbose { {
details = fmt.Sprintf("Formats: %s (%s)", strings.Join(found, ", "), imageFormatsDir) name: "qt6-imageformats",
desc: "WebP, TIFF, GIF, JP2 support",
plugins: []struct{ file, format string }{
{"libqwebp.so", "WebP"},
{"libqtiff.so", "TIFF"},
{"libqgif.so", "GIF"},
{"libqjp2.so", "JP2"},
{"libqicns.so", "ICNS"},
},
},
{
name: "kimageformats",
desc: "AVIF, HEIF, JXL support",
plugins: []struct{ file, format string }{
{"kimg_avif.so", "AVIF"},
{"kimg_heif.so", "HEIF"},
{"kimg_jxl.so", "JXL"},
{"kimg_exr.so", "EXR"},
},
},
} }
return checkResult{catOptionalFeatures, "kimageformats", statusOK, fmt.Sprintf("Installed (%d formats)", len(found)), details, url} var results []checkResult
for _, c := range checks {
var found []string
for _, p := range c.plugins {
if _, err := os.Stat(filepath.Join(imageFormatsDir, p.file)); err == nil {
found = append(found, p.format)
}
}
var result checkResult
switch {
case len(found) == 0:
result = checkResult{catOptionalFeatures, c.name, statusWarn, "Not installed", c.desc, url}
default:
details := ""
if doctorVerbose {
details = fmt.Sprintf("Formats: %s (%s)", strings.Join(found, ", "), imageFormatsDir)
}
result = checkResult{catOptionalFeatures, c.name, statusOK, fmt.Sprintf("Installed (%d formats)", len(found)), details, url}
}
results = append(results, result)
}
return results
} }
func findQtPluginDir() string { func findQtPluginDir() string {
@@ -773,7 +806,7 @@ func checkOptionalDependencies() []checkResult {
results = append(results, checkResult{catOptionalFeatures, "cups-pk-helper", cupsPkStatus, cupsPkMsg, "Printer management", optionalFeaturesURL}) results = append(results, checkResult{catOptionalFeatures, "cups-pk-helper", cupsPkStatus, cupsPkMsg, "Printer management", optionalFeaturesURL})
results = append(results, checkI2CAvailability()) results = append(results, checkI2CAvailability())
results = append(results, checkKImageFormats()) results = append(results, checkImageFormatPlugins()...)
terminals := []string{"ghostty", "kitty", "alacritty", "foot", "wezterm"} terminals := []string{"ghostty", "kitty", "alacritty", "foot", "wezterm"}
if idx := slices.IndexFunc(terminals, utils.CommandExists); idx >= 0 { if idx := slices.IndexFunc(terminals, utils.CommandExists); idx >= 0 {

View File

@@ -13,16 +13,16 @@ import (
) )
var ( var (
ssOutputName string ssOutputName string
ssIncludeCursor bool ssCursor string
ssFormat string ssFormat string
ssQuality int ssQuality int
ssOutputDir string ssOutputDir string
ssFilename string ssFilename string
ssNoClipboard bool ssNoClipboard bool
ssNoFile bool ssNoFile bool
ssNoNotify bool ssNoNotify bool
ssStdout bool ssStdout bool
) )
var screenshotCmd = &cobra.Command{ var screenshotCmd = &cobra.Command{
@@ -52,7 +52,7 @@ Examples:
dms screenshot last # Last region (pre-selected) dms screenshot last # Last region (pre-selected)
dms screenshot --no-clipboard # Save file only dms screenshot --no-clipboard # Save file only
dms screenshot --no-file # Clipboard only dms screenshot --no-file # Clipboard only
dms screenshot --cursor # Include cursor dms screenshot --cursor=on # Include cursor
dms screenshot -f jpg -q 85 # JPEG with quality 85`, dms screenshot -f jpg -q 85 # JPEG with quality 85`,
} }
@@ -111,7 +111,7 @@ var notifyActionCmd = &cobra.Command{
func init() { func init() {
screenshotCmd.PersistentFlags().StringVarP(&ssOutputName, "output", "o", "", "Output name for 'output' mode") screenshotCmd.PersistentFlags().StringVarP(&ssOutputName, "output", "o", "", "Output name for 'output' mode")
screenshotCmd.PersistentFlags().BoolVar(&ssIncludeCursor, "cursor", false, "Include cursor in screenshot") screenshotCmd.PersistentFlags().StringVar(&ssCursor, "cursor", "off", "Include cursor in screenshot (on/off)")
screenshotCmd.PersistentFlags().StringVarP(&ssFormat, "format", "f", "png", "Output format (png, jpg, ppm)") screenshotCmd.PersistentFlags().StringVarP(&ssFormat, "format", "f", "png", "Output format (png, jpg, ppm)")
screenshotCmd.PersistentFlags().IntVarP(&ssQuality, "quality", "q", 90, "JPEG quality (1-100)") screenshotCmd.PersistentFlags().IntVarP(&ssQuality, "quality", "q", 90, "JPEG quality (1-100)")
screenshotCmd.PersistentFlags().StringVarP(&ssOutputDir, "dir", "d", "", "Output directory") screenshotCmd.PersistentFlags().StringVarP(&ssOutputDir, "dir", "d", "", "Output directory")
@@ -136,7 +136,9 @@ func getScreenshotConfig(mode screenshot.Mode) screenshot.Config {
config := screenshot.DefaultConfig() config := screenshot.DefaultConfig()
config.Mode = mode config.Mode = mode
config.OutputName = ssOutputName config.OutputName = ssOutputName
config.IncludeCursor = ssIncludeCursor if strings.EqualFold(ssCursor, "on") {
config.Cursor = screenshot.CursorOn
}
config.Clipboard = !ssNoClipboard config.Clipboard = !ssNoClipboard
config.SaveFile = !ssNoFile config.SaveFile = !ssNoFile
config.Notify = !ssNoNotify config.Notify = !ssNoNotify

View File

@@ -111,6 +111,7 @@ windowrule = float on, match:class ^(zoom)$
# windowrule = float on, match:class ^(org.quickshell)$ # windowrule = float on, match:class ^(org.quickshell)$
layerrule = no_anim on, match:namespace ^(quickshell)$ layerrule = no_anim on, match:namespace ^(quickshell)$
layerrule = no_anim on, match:namespace ^dms:.*
source = ./dms/colors.conf source = ./dms/colors.conf
source = ./dms/outputs.conf source = ./dms/outputs.conf

View File

@@ -430,7 +430,7 @@ func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Packa
} }
// Add repository // Add repository
repoLine := fmt.Sprintf("deb [signed-by=%s, arch=%s] %s/ /", keyringPath, runtime.GOARCH, baseURL) repoLine := fmt.Sprintf("deb [signed-by=%s arch=%s] %s/ /", keyringPath, runtime.GOARCH, baseURL)
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages, Phase: PhaseSystemPackages,

View File

@@ -33,6 +33,7 @@ const (
TemplateKindTerminal TemplateKindTerminal
TemplateKindGTK TemplateKindGTK
TemplateKindVSCode TemplateKindVSCode
TemplateKindEmacs
) )
type TemplateDef struct { type TemplateDef struct {
@@ -65,7 +66,7 @@ var templateRegistry = []TemplateDef{
{ID: "dgop", Commands: []string{"dgop"}, ConfigFile: "dgop.toml"}, {ID: "dgop", Commands: []string{"dgop"}, ConfigFile: "dgop.toml"},
{ID: "kcolorscheme", ConfigFile: "kcolorscheme.toml", RunUnconditionally: true}, {ID: "kcolorscheme", ConfigFile: "kcolorscheme.toml", RunUnconditionally: true},
{ID: "vscode", Kind: TemplateKindVSCode}, {ID: "vscode", Kind: TemplateKindVSCode},
{ID: "emacs", Commands: []string{"emacs"}, ConfigFile: "emacs.toml"}, {ID: "emacs", Commands: []string{"emacs"}, ConfigFile: "emacs.toml", Kind: TemplateKindEmacs},
} }
func (c *ColorMode) GTKTheme() string { func (c *ColorMode) GTKTheme() string {
@@ -78,7 +79,8 @@ func (c *ColorMode) GTKTheme() string {
} }
var ( var (
matugenVersionOnce sync.Once matugenVersionMu sync.Mutex
matugenVersionOK bool
matugenSupportsCOE bool matugenSupportsCOE bool
matugenIsV4 bool matugenIsV4 bool
) )
@@ -334,6 +336,10 @@ output_path = '%s'
appendVSCodeConfig(cfgFile, "cursor", filepath.Join(homeDir, ".cursor/extensions"), opts.ShellDir) appendVSCodeConfig(cfgFile, "cursor", filepath.Join(homeDir, ".cursor/extensions"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "windsurf", filepath.Join(homeDir, ".windsurf/extensions"), opts.ShellDir) appendVSCodeConfig(cfgFile, "windsurf", filepath.Join(homeDir, ".windsurf/extensions"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "vscode-insiders", filepath.Join(homeDir, ".vscode-insiders/extensions"), opts.ShellDir) 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)
}
default: default:
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile) appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
} }
@@ -491,6 +497,9 @@ func substituteVars(content, shellDir string) string {
result = strings.ReplaceAll(result, "'CONFIG_DIR/", "'"+utils.XDGConfigHome()+"/") result = strings.ReplaceAll(result, "'CONFIG_DIR/", "'"+utils.XDGConfigHome()+"/")
result = strings.ReplaceAll(result, "'DATA_DIR/", "'"+utils.XDGDataHome()+"/") result = strings.ReplaceAll(result, "'DATA_DIR/", "'"+utils.XDGDataHome()+"/")
result = strings.ReplaceAll(result, "'CACHE_DIR/", "'"+utils.XDGCacheHome()+"/") result = strings.ReplaceAll(result, "'CACHE_DIR/", "'"+utils.XDGCacheHome()+"/")
if emacsDir := utils.EmacsConfigDir(); emacsDir != "" {
result = strings.ReplaceAll(result, "'EMACS_DIR/", "'"+emacsDir+"/")
}
return result return result
} }
@@ -511,79 +520,160 @@ func extractTOMLSection(content, startMarker, endMarker string) string {
return content[startIdx : startIdx+endIdx] return content[startIdx : startIdx+endIdx]
} }
func checkMatugenVersion() { type matugenFlags struct {
matugenVersionOnce.Do(func() { supportsCOE bool
cmd := exec.Command("matugen", "--version") isV4 bool
output, err := cmd.Output()
if err != nil {
return
}
versionStr := strings.TrimSpace(string(output))
versionStr = strings.TrimPrefix(versionStr, "matugen ")
parts := strings.Split(versionStr, ".")
if len(parts) < 2 {
return
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return
}
matugenSupportsCOE = major > 3 || (major == 3 && minor >= 1)
matugenIsV4 = major >= 4
if matugenSupportsCOE {
log.Infof("Matugen %s supports --continue-on-error", versionStr)
}
if matugenIsV4 {
log.Infof("Matugen %s: using v4 flags", versionStr)
}
})
} }
func runMatugen(args []string) error { func detectMatugenVersion() (matugenFlags, error) {
checkMatugenVersion() matugenVersionMu.Lock()
defer matugenVersionMu.Unlock()
if matugenVersionOK {
return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil
}
return detectMatugenVersionLocked()
}
func redetectMatugenVersion(old matugenFlags) (matugenFlags, bool) {
matugenVersionMu.Lock()
defer matugenVersionMu.Unlock()
matugenVersionOK = false
flags, err := detectMatugenVersionLocked()
if err != nil {
return old, false
}
changed := flags.supportsCOE != old.supportsCOE || flags.isV4 != old.isV4
return flags, changed
}
func detectMatugenVersionLocked() (matugenFlags, error) {
cmd := exec.Command("matugen", "--version")
output, err := cmd.Output()
if err != nil {
return matugenFlags{}, fmt.Errorf("failed to get matugen version: %w", err)
}
versionStr := strings.TrimSpace(string(output))
versionStr = strings.TrimPrefix(versionStr, "matugen ")
parts := strings.Split(versionStr, ".")
if len(parts) < 2 {
return matugenFlags{}, fmt.Errorf("unexpected matugen version format: %q", versionStr)
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return matugenFlags{}, fmt.Errorf("failed to parse matugen major version %q: %w", parts[0], err)
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return matugenFlags{}, fmt.Errorf("failed to parse matugen minor version %q: %w", parts[1], err)
}
matugenSupportsCOE = major > 3 || (major == 3 && minor >= 1)
matugenIsV4 = major >= 4
matugenVersionOK = true
if matugenSupportsCOE { if matugenSupportsCOE {
args = append([]string{"--continue-on-error"}, args...) log.Infof("Matugen %s supports --continue-on-error", versionStr)
} }
if matugenIsV4 { if matugenIsV4 {
log.Infof("Matugen %s: using v4 flags", versionStr)
}
return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil
}
func buildMatugenArgs(baseArgs []string, flags matugenFlags) []string {
args := make([]string, 0, len(baseArgs)+4)
if flags.supportsCOE {
args = append(args, "--continue-on-error")
}
args = append(args, baseArgs...)
if flags.isV4 {
args = append(args, "--source-color-index", "0") args = append(args, "--source-color-index", "0")
} }
return args
}
func runMatugen(baseArgs []string) error {
flags, err := detectMatugenVersion()
if err != nil {
return err
}
args := buildMatugenArgs(baseArgs, flags)
cmd := exec.Command("matugen", args...) cmd := exec.Command("matugen", args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
return cmd.Run() runErr := cmd.Run()
if runErr == nil {
return nil
}
log.Warnf("Matugen failed (v4=%v): %v", flags.isV4, runErr)
newFlags, changed := redetectMatugenVersion(flags)
if !changed {
return runErr
}
log.Warnf("Matugen version changed (v4: %v -> %v), retrying", flags.isV4, newFlags.isV4)
args = buildMatugenArgs(baseArgs, newFlags)
retryCmd := exec.Command("matugen", args...)
retryCmd.Stdout = os.Stdout
retryCmd.Stderr = os.Stderr
return retryCmd.Run()
} }
func runMatugenDryRun(opts *Options) (string, error) { func runMatugenDryRun(opts *Options) (string, error) {
checkMatugenVersion() flags, err := detectMatugenVersion()
var args []string
switch opts.Kind {
case "hex":
args = []string{"color", "hex", opts.Value}
default:
args = []string{opts.Kind, opts.Value}
}
args = append(args, "-m", "dark", "-t", opts.MatugenType, "--json", "hex", "--dry-run")
if matugenIsV4 {
args = append(args, "--source-color-index", "0", "--old-json-output")
}
cmd := exec.Command("matugen", args...)
output, err := cmd.Output()
if err != nil { if err != nil {
return "", err return "", err
} }
output, dryErr := execDryRun(opts, flags)
if dryErr == nil {
return output, nil
}
log.Warnf("Matugen dry-run failed (v4=%v): %v", flags.isV4, dryErr)
newFlags, changed := redetectMatugenVersion(flags)
if !changed {
return "", dryErr
}
log.Warnf("Matugen version changed (v4: %v -> %v), retrying dry-run", flags.isV4, newFlags.isV4)
return execDryRun(opts, newFlags)
}
func execDryRun(opts *Options, flags matugenFlags) (string, error) {
var baseArgs []string
switch opts.Kind {
case "hex":
baseArgs = []string{"color", "hex", opts.Value}
default:
baseArgs = []string{opts.Kind, opts.Value}
}
baseArgs = append(baseArgs, "-m", "dark", "-t", opts.MatugenType, "--json", "hex", "--dry-run")
if flags.isV4 {
baseArgs = append(baseArgs, "--source-color-index", "0", "--old-json-output")
}
cmd := exec.Command("matugen", baseArgs...)
var stderr strings.Builder
cmd.Stderr = &stderr
output, err := cmd.Output()
if err != nil {
if stderr.Len() > 0 {
return "", fmt.Errorf("matugen %v failed (v4=%v): %s", baseArgs, flags.isV4, strings.TrimSpace(stderr.String()))
}
return "", fmt.Errorf("matugen %v failed (v4=%v): %w", baseArgs, flags.isV4, err)
}
return strings.ReplaceAll(string(output), "\n", ""), nil return strings.ReplaceAll(string(output), "\n", ""), nil
} }
@@ -819,6 +909,8 @@ func CheckTemplates(checker utils.AppChecker) []TemplateCheck {
detected = true detected = true
case tmpl.Kind == TemplateKindVSCode: case tmpl.Kind == TemplateKindVSCode:
detected = checkVSCodeExtension(homeDir) detected = checkVSCodeExtension(homeDir)
case tmpl.Kind == TemplateKindEmacs:
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks) && utils.EmacsConfigDir() != ""
default: default:
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks) detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks)
} }

View File

@@ -108,7 +108,7 @@ func NewRegionSelector(s *Screenshoter) *RegionSelector {
screenshoter: s, screenshoter: s,
outputs: make(map[uint32]*WaylandOutput), outputs: make(map[uint32]*WaylandOutput),
preCapture: make(map[*WaylandOutput]*PreCapture), preCapture: make(map[*WaylandOutput]*PreCapture),
showCapturedCursor: true, showCapturedCursor: s.config.Cursor == CursorOn,
} }
} }

View File

@@ -453,10 +453,7 @@ func (s *Screenshoter) blitBuffer(dst, src *ShmBuffer, dstX, dstY int, yInverted
} }
func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult, error) { func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult, error) {
cursor := int32(0) cursor := int32(s.config.Cursor)
if s.config.IncludeCursor {
cursor = 1
}
frame, err := s.screencopy.CaptureOutput(cursor, output.wlOutput) frame, err := s.screencopy.CaptureOutput(cursor, output.wlOutput)
if err != nil { if err != nil {
@@ -624,10 +621,7 @@ func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Regio
} }
} }
cursor := int32(0) cursor := int32(s.config.Cursor)
if s.config.IncludeCursor {
cursor = 1
}
frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h) frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h)
if err != nil { if err != nil {

View File

@@ -19,6 +19,13 @@ const (
FormatPPM FormatPPM
) )
type CursorMode int
const (
CursorOff CursorMode = iota
CursorOn
)
type Region struct { type Region struct {
X int32 `json:"x"` X int32 `json:"x"`
Y int32 `json:"y"` Y int32 `json:"y"`
@@ -42,29 +49,29 @@ type Output struct {
} }
type Config struct { type Config struct {
Mode Mode Mode Mode
OutputName string OutputName string
IncludeCursor bool Cursor CursorMode
Format Format Format Format
Quality int Quality int
OutputDir string OutputDir string
Filename string Filename string
Clipboard bool Clipboard bool
SaveFile bool SaveFile bool
Notify bool Notify bool
Stdout bool Stdout bool
} }
func DefaultConfig() Config { func DefaultConfig() Config {
return Config{ return Config{
Mode: ModeRegion, Mode: ModeRegion,
IncludeCursor: false, Cursor: CursorOff,
Format: FormatPNG, Format: FormatPNG,
Quality: 90, Quality: 90,
OutputDir: "", OutputDir: "",
Filename: "", Filename: "",
Clipboard: true, Clipboard: true,
SaveFile: true, SaveFile: true,
Notify: true, Notify: true,
} }
} }

View File

@@ -38,6 +38,22 @@ func XDGConfigHome() string {
return filepath.Join(home, ".config") return filepath.Join(home, ".config")
} }
func EmacsConfigDir() string {
home, _ := os.UserHomeDir()
emacsD := filepath.Join(home, ".emacs.d")
if info, err := os.Stat(emacsD); err == nil && info.IsDir() {
return emacsD
}
xdgEmacs := filepath.Join(XDGConfigHome(), "emacs")
if info, err := os.Stat(xdgEmacs); err == nil && info.IsDir() {
return xdgEmacs
}
return ""
}
func ExpandPath(path string) (string, error) { func ExpandPath(path string) (string, error) {
expanded := os.ExpandEnv(path) expanded := os.ExpandEnv(path)
expanded = filepath.Clean(expanded) expanded = filepath.Clean(expanded)

View File

@@ -1 +1 @@
Saffron Bloom The Wolverine

View File

@@ -60,6 +60,7 @@ Singleton {
property bool _hasLoaded: false property bool _hasLoaded: false
property bool _isReadOnly: false property bool _isReadOnly: false
property bool _hasUnsavedChanges: false property bool _hasUnsavedChanges: false
property bool _selfWrite: false
property var _loadedSettingsSnapshot: null property var _loadedSettingsSnapshot: null
property var pluginSettings: ({}) property var pluginSettings: ({})
property var builtInPluginSettings: ({}) property var builtInPluginSettings: ({})
@@ -1243,6 +1244,7 @@ Singleton {
function saveSettings() { function saveSettings() {
if (_loading || _parseError || !_hasLoaded) if (_loading || _parseError || !_hasLoaded)
return; return;
_selfWrite = true;
settingsFile.setText(JSON.stringify(Store.toJson(root), null, 2)); settingsFile.setText(JSON.stringify(Store.toJson(root), null, 2));
if (_isReadOnly) if (_isReadOnly)
_checkSettingsWritable(); _checkSettingsWritable();
@@ -2589,7 +2591,13 @@ Singleton {
blockWrites: true blockWrites: true
atomicWrites: true atomicWrites: true
watchChanges: true watchChanges: true
onFileChanged: settingsFileReloadDebounce.restart() onFileChanged: {
if (_selfWrite) {
_selfWrite = false;
return;
}
settingsFileReloadDebounce.restart();
}
onLoaded: { onLoaded: {
if (isGreeterMode) if (isGreeterMode)
return; return;

View File

@@ -81,6 +81,12 @@ function calculateNextIndex(flatModel, selectedFlatIndex, sectionId, viewMode, g
return bounds.start + newPosInSection; return bounds.start + newPosInSection;
} }
var currentRow = Math.floor(posInSection / cols);
var lastRow = Math.floor((bounds.count - 1) / cols);
if (currentRow < lastRow) {
return bounds.start + bounds.count - 1;
}
var nextSection = findNextNonHeaderIndex(flatModel, bounds.end + 1); var nextSection = findNextNonHeaderIndex(flatModel, bounds.end + 1);
return nextSection !== -1 ? nextSection : selectedFlatIndex; return nextSection !== -1 ? nextSection : selectedFlatIndex;
} }

View File

@@ -130,24 +130,17 @@ Item {
if (!entry || entry.isHeader) if (!entry || entry.isHeader)
return; return;
var rowIndex = _flatIndexToRowMap[index]; var rowIndex = _flatIndexToRowMap[index];
if (rowIndex === undefined || rowIndex >= _cumulativeHeights.length) if (rowIndex === undefined)
return;
var row = _visualRows[rowIndex];
if (!row)
return; return;
var rowY = _cumulativeHeights[rowIndex]; mainListView.positionViewAtIndex(rowIndex, ListView.Contain);
var rowHeight = row.height;
var scrollY = mainListView.contentY - mainListView.originY;
var viewHeight = mainListView.height;
var headerH = stickyHeader.height;
if (rowY < scrollY + headerH) { if (stickyHeader.visible && rowIndex < _cumulativeHeights.length) {
mainListView.contentY = Math.max(mainListView.originY, rowY - headerH + mainListView.originY); var rowY = _cumulativeHeights[rowIndex];
return; var scrollY = mainListView.contentY - mainListView.originY;
} if (rowY < scrollY + stickyHeader.height) {
if (rowY + rowHeight > scrollY + viewHeight) { mainListView.contentY = Math.max(mainListView.originY, rowY - stickyHeader.height + mainListView.originY);
mainListView.contentY = rowY + rowHeight - viewHeight + mainListView.originY; }
} }
} }

View File

@@ -83,9 +83,9 @@ FloatingWindow {
objectName: "processListModal" objectName: "processListModal"
title: I18n.tr("System Monitor", "sysmon window title") title: I18n.tr("System Monitor", "sysmon window title")
minimumSize: Qt.size(750, 550) minimumSize: Qt.size(Math.min(Math.round(Theme.fontSizeMedium * 48), Screen.width), Math.min(Math.round(Theme.fontSizeMedium * 34), Screen.height))
implicitWidth: 1000 implicitWidth: Math.round(Theme.fontSizeMedium * 71)
implicitHeight: 720 implicitHeight: Math.round(Theme.fontSizeMedium * 51)
color: Theme.surfaceContainer color: Theme.surfaceContainer
visible: false visible: false
@@ -236,7 +236,7 @@ FloatingWindow {
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 48 Layout.preferredHeight: Math.round(Theme.fontSizeMedium * 3.4)
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@@ -293,10 +293,10 @@ FloatingWindow {
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 52 Layout.preferredHeight: Math.round(Theme.fontSizeMedium * 3.7)
Layout.leftMargin: Theme.spacingL Layout.leftMargin: Theme.spacingL
Layout.rightMargin: Theme.spacingL Layout.rightMargin: Theme.spacingL
spacing: Theme.spacingL spacing: Theme.spacingM
Row { Row {
spacing: 2 spacing: 2
@@ -322,14 +322,15 @@ FloatingWindow {
] ]
Rectangle { Rectangle {
width: 120 width: tabRowContent.implicitWidth + Theme.spacingM * 2
height: 44 height: Math.round(Theme.fontSizeMedium * 3.1)
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent") color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
border.color: currentTab === index ? Theme.primary : "transparent" border.color: currentTab === index ? Theme.primary : "transparent"
border.width: currentTab === index ? 1 : 0 border.width: currentTab === index ? 1 : 0
Row { Row {
id: tabRowContent
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS spacing: Theme.spacingXS
@@ -373,11 +374,13 @@ FloatingWindow {
DankButtonGroup { DankButtonGroup {
id: processFilterGroup id: processFilterGroup
Layout.minimumWidth: implicitWidth + 8
model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")] model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")]
currentIndex: 0 currentIndex: 0
checkEnabled: false checkEnabled: false
buttonHeight: 36 buttonHeight: Math.round(Theme.fontSizeSmall * 2.6)
minButtonWidth: 0
buttonPadding: Theme.spacingS
textSize: Theme.fontSizeSmall
visible: currentTab === 0 visible: currentTab === 0
onSelectionChanged: (index, selected) => { onSelectionChanged: (index, selected) => {
if (!selected) if (!selected)
@@ -400,9 +403,9 @@ FloatingWindow {
DankTextField { DankTextField {
id: searchField id: searchField
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: 250 Layout.maximumWidth: Math.round(Theme.fontSizeMedium * 18)
Layout.minimumWidth: 120 Layout.minimumWidth: Theme.fontSizeMedium * 4
Layout.preferredHeight: 40 Layout.preferredHeight: Math.round(Theme.fontSizeMedium * 2.8)
placeholderText: I18n.tr("Search processes...", "process search placeholder") placeholderText: I18n.tr("Search processes...", "process search placeholder")
leftIconName: "search" leftIconName: "search"
showClearButton: true showClearButton: true
@@ -470,7 +473,7 @@ FloatingWindow {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 32 Layout.preferredHeight: Math.round(Theme.fontSizeSmall * 2.7)
Layout.leftMargin: Theme.spacingL Layout.leftMargin: Theme.spacingL
Layout.rightMargin: Theme.spacingL Layout.rightMargin: Theme.spacingL
Layout.bottomMargin: Theme.spacingM Layout.bottomMargin: Theme.spacingM

View File

@@ -945,22 +945,31 @@ Column {
} }
} }
Component.onCompleted: { function tryCreatePluginInstance() {
Qt.callLater(() => { const pluginComponent = PluginService.pluginWidgetComponents[pluginId];
const pluginComponent = PluginService.pluginWidgetComponents[pluginId]; if (!pluginComponent)
if (pluginComponent) { return false;
const instance = pluginComponent.createObject(null, { try {
"pluginId": pluginId, const instance = pluginComponent.createObject(null, {
"pluginService": PluginService, "pluginId": pluginId,
"visible": false, "pluginService": PluginService,
"width": 0, "visible": false,
"height": 0 "width": 0,
}); "height": 0
if (instance) { });
pluginInstance = instance; if (instance) {
} pluginInstance = instance;
return true;
} }
}); } catch (e) {
console.warn("DragDropGrid: stale plugin component for", pluginId, "- reloading");
PluginService.reloadPlugin(pluginId);
}
return false;
}
Component.onCompleted: {
Qt.callLater(() => tryCreatePluginInstance());
} }
Connections { Connections {
@@ -970,6 +979,11 @@ Column {
pluginInstance.loadPluginData(); pluginInstance.loadPluginData();
} }
} }
function onPluginLoaded(loadedPluginId) {
if (loadedPluginId !== pluginId || pluginInstance)
return;
Qt.callLater(() => tryCreatePluginInstance());
}
} }
Component.onDestruction: { Component.onDestruction: {

View File

@@ -13,8 +13,8 @@ Row {
property Item popoutContent: null property Item popoutContent: null
signal addWidget(string widgetId) signal addWidget(string widgetId)
signal resetToDefault() signal resetToDefault
signal clearAll() signal clearAll
height: 48 height: 48
spacing: Theme.spacingS spacing: Theme.spacingS
@@ -28,7 +28,7 @@ Row {
y: parent ? Math.round((parent.height - height) / 2) : 0 y: parent ? Math.round((parent.height - height) / 2) : 0
width: 400 width: 400
height: 300 height: 300
modal: true modal: false
focus: true focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
@@ -133,7 +133,7 @@ Row {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.addWidget(modelData.id) root.addWidget(modelData.id);
} }
} }
} }

View File

@@ -12,6 +12,7 @@ DankPopout {
id: root id: root
layerNamespace: "dms:control-center" layerNamespace: "dms:control-center"
fullHeightSurface: true
property string expandedSection: "" property string expandedSection: ""
property var triggerScreen: null property var triggerScreen: null

View File

@@ -216,14 +216,18 @@ QtObject {
} }
const pluginComponent = PluginService.pluginWidgetComponents[plugin.id]; const pluginComponent = PluginService.pluginWidgetComponents[plugin.id];
if (!pluginComponent || typeof pluginComponent.createObject !== 'function') { if (!pluginComponent)
continue; continue;
}
const tempInstance = pluginComponent.createObject(null); let tempInstance;
if (!tempInstance) { try {
tempInstance = pluginComponent.createObject(null);
} catch (e) {
PluginService.reloadPlugin(plugin.id);
continue; continue;
} }
if (!tempInstance)
continue;
const hasCCWidget = tempInstance.ccWidgetIcon && tempInstance.ccWidgetIcon.length > 0; const hasCCWidget = tempInstance.ccWidgetIcon && tempInstance.ccWidgetIcon.length > 0;
tempInstance.destroy(); tempInstance.destroy();

View File

@@ -155,6 +155,7 @@ BasePill {
property real touchpadThreshold: 500 property real touchpadThreshold: 500
onWheel: function (wheelEvent) { onWheel: function (wheelEvent) {
wheelEvent.accepted = true;
const deltaY = wheelEvent.angleDelta.y; const deltaY = wheelEvent.angleDelta.y;
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0; const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;

View File

@@ -10,6 +10,45 @@ BasePill {
property bool isActive: false property bool isActive: false
readonly property bool hasUpdates: SystemUpdateService.updateCount > 0 readonly property bool hasUpdates: SystemUpdateService.updateCount > 0
readonly property bool isChecking: SystemUpdateService.isChecking readonly property bool isChecking: SystemUpdateService.isChecking
readonly property bool shouldHide: SettingsData.updaterHideWidget && !hasUpdates && !isChecking && !SystemUpdateService.hasError
opacity: shouldHide ? 0 : 1
states: [
State {
name: "hidden_horizontal"
when: root.shouldHide && !isVerticalOrientation
PropertyChanges {
target: root
width: 0
}
},
State {
name: "hidden_vertical"
when: root.shouldHide && isVerticalOrientation
PropertyChanges {
target: root
height: 0
}
}
]
transitions: [
Transition {
NumberAnimation {
properties: "width,height"
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
]
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Ref { Ref {
service: SystemUpdateService service: SystemUpdateService

View File

@@ -924,8 +924,13 @@ Item {
return loadedIsUrgent; return loadedIsUrgent;
return false; return false;
} }
property var loadedIconData: null readonly property var loadedIconData: {
property bool loadedHasIcon: false if (isPlaceholder) return null;
const name = modelData?.name;
if (!name) return null;
return SettingsData.getWorkspaceNameIcon(name);
}
readonly property bool loadedHasIcon: loadedIconData !== null
property var loadedIcons: [] property var loadedIcons: []
readonly property int stableIconCount: { readonly property int stableIconCount: {
@@ -986,8 +991,8 @@ Item {
readonly property real baseHeight: root.isVertical ? (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7) : (SettingsData.showWorkspaceApps ? Math.max(widgetHeight * 0.7, root.appIconSize + Theme.spacingXS * 2) : widgetHeight * 0.5) readonly property real baseHeight: root.isVertical ? (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7) : (SettingsData.showWorkspaceApps ? Math.max(widgetHeight * 0.7, root.appIconSize + Theme.spacingXS * 2) : widgetHeight * 0.5)
readonly property bool hasWorkspaceName: SettingsData.showWorkspaceName && modelData?.name && modelData.name !== "" readonly property bool hasWorkspaceName: SettingsData.showWorkspaceName && modelData?.name && modelData.name !== ""
readonly property bool workspaceNamesEnabled: SettingsData.showWorkspaceName && (CompositorService.isNiri || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) readonly property bool workspaceNamesEnabled: SettingsData.showWorkspaceName && (CompositorService.isNiri || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle)
readonly property real contentImplicitWidth: (hasWorkspaceName || loadedHasIcon) ? (appIconsLoader.item?.contentWidth ?? 0) : 0 readonly property real contentImplicitWidth: hasWorkspaceName ? (appIconsLoader.item?.contentWidth ?? 0) : 0
readonly property real contentImplicitHeight: (workspaceNamesEnabled || loadedHasIcon) ? (appIconsLoader.item?.contentHeight ?? 0) : 0 readonly property real contentImplicitHeight: workspaceNamesEnabled ? (appIconsLoader.item?.contentHeight ?? 0) : 0
readonly property real iconsExtraWidth: { readonly property real iconsExtraWidth: {
if (!root.isVertical && SettingsData.showWorkspaceApps && stableIconCount > 0) { if (!root.isVertical && SettingsData.showWorkspaceApps && stableIconCount > 0) {
@@ -1222,8 +1227,6 @@ Item {
onTriggered: { onTriggered: {
if (isPlaceholder) { if (isPlaceholder) {
delegateRoot.loadedWorkspaceData = null; delegateRoot.loadedWorkspaceData = null;
delegateRoot.loadedIconData = null;
delegateRoot.loadedHasIcon = false;
delegateRoot.loadedIcons = []; delegateRoot.loadedIcons = [];
delegateRoot.loadedIsUrgent = false; delegateRoot.loadedIsUrgent = false;
return; return;
@@ -1249,13 +1252,6 @@ Item {
delegateRoot.loadedIsUrgent = wsData?.urgent ?? false; delegateRoot.loadedIsUrgent = wsData?.urgent ?? false;
} }
var icData = null;
if (wsData?.name) {
icData = SettingsData.getWorkspaceNameIcon(wsData.name);
}
delegateRoot.loadedIconData = icData;
delegateRoot.loadedHasIcon = icData !== null;
if (SettingsData.showWorkspaceApps) { if (SettingsData.showWorkspaceApps) {
if (CompositorService.isDwl || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) { if (CompositorService.isDwl || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) {
delegateRoot.loadedIcons = root.getWorkspaceIcons(modelData); delegateRoot.loadedIcons = root.getWorkspaceIcons(modelData);
@@ -1420,7 +1416,7 @@ Item {
Item { Item {
visible: loadedHasIcon && loadedIconData?.type === "icon" visible: loadedHasIcon && loadedIconData?.type === "icon"
width: wsIcon.width + (isActive && loadedIcons.length > 0 ? 4 : 0) width: wsIcon.width
height: root.appIconSize height: root.appIconSize
DankIcon { DankIcon {
@@ -1435,7 +1431,7 @@ Item {
Item { Item {
visible: loadedHasIcon && loadedIconData?.type === "text" visible: loadedHasIcon && loadedIconData?.type === "text"
width: wsText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0) width: wsText.implicitWidth
height: root.appIconSize height: root.appIconSize
StyledText { StyledText {
@@ -1449,14 +1445,14 @@ Item {
} }
Item { Item {
visible: (SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon visible: ((SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon) || (loadedHasIcon && SettingsData.showWorkspaceName && hasWorkspaceName)
width: wsIndexText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0) width: wsIndexText.implicitWidth
height: root.appIconSize height: root.appIconSize
StyledText { StyledText {
id: wsIndexText id: wsIndexText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: root.getWorkspaceIndex(modelData, index) text: loadedHasIcon ? (modelData?.name ?? "") : root.getWorkspaceIndex(modelData, index)
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale) font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
@@ -1574,9 +1570,9 @@ Item {
} }
StyledText { StyledText {
visible: (SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon visible: ((SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon) || (loadedHasIcon && SettingsData.showWorkspaceName && hasWorkspaceName)
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: root.getWorkspaceIndex(modelData, index) text: loadedHasIcon ? (root.isVertical ? (modelData?.name ?? "").charAt(0) : (modelData?.name ?? "")) : root.getWorkspaceIndex(modelData, index)
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale) font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
@@ -1670,55 +1666,6 @@ Item {
} }
} }
// Loader for Custom Name Icon
Loader {
id: customIconLoader
anchors.fill: parent
active: !isPlaceholder && loadedHasIcon && loadedIconData.type === "icon" && !SettingsData.showWorkspaceApps
sourceComponent: Item {
DankIcon {
anchors.centerIn: parent
name: loadedIconData ? loadedIconData.value : "" // NULL CHECK
size: Theme.fontSizeSmall
color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : Theme.surfaceTextMedium
weight: isActive && !isPlaceholder ? 500 : 400
}
}
}
// Loader for Custom Name Text
Loader {
id: customTextLoader
anchors.fill: parent
active: !isPlaceholder && loadedHasIcon && loadedIconData.type === "text" && !SettingsData.showWorkspaceApps
sourceComponent: Item {
StyledText {
anchors.centerIn: parent
text: loadedIconData ? loadedIconData.value : "" // NULL CHECK
color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : Theme.surfaceTextMedium
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
}
}
}
// Loader for Workspace Index
Loader {
id: indexLoader
anchors.fill: parent
active: (SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon && !SettingsData.showWorkspaceApps
sourceComponent: Item {
StyledText {
anchors.centerIn: parent
text: {
return root.getWorkspaceIndex(modelData, index);
}
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
}
}
}
} }
Component.onCompleted: updateAllData() Component.onCompleted: updateAllData()

View File

@@ -278,7 +278,7 @@ Item {
Behavior on x { Behavior on x {
enabled: !swipeDragHandler.active && delegateRoot.__delegateInitialized enabled: !swipeDragHandler.active && delegateRoot.__delegateInitialized
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.notificationExitDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
@@ -286,7 +286,7 @@ Item {
Behavior on opacity { Behavior on opacity {
enabled: delegateRoot.__delegateInitialized enabled: delegateRoot.__delegateInitialized
NumberAnimation { NumberAnimation {
duration: delegateRoot.__delegateInitialized ? Theme.shortDuration : 0 duration: delegateRoot.__delegateInitialized ? Theme.notificationExitDuration : 0
} }
} }
} }

View File

@@ -257,7 +257,7 @@ DankListView {
target: delegateRoot target: delegateRoot
property: "swipeOffset" property: "swipeOffset"
to: 0 to: 0
duration: Theme.shortDuration duration: Theme.notificationExitDuration
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
onStopped: NotificationService.dismissGroup(delegateRoot.modelData?.key || "") onStopped: NotificationService.dismissGroup(delegateRoot.modelData?.key || "")
} }

View File

@@ -764,7 +764,7 @@ Rectangle {
target: expandedDelegateWrapper target: expandedDelegateWrapper
property: "swipeOffset" property: "swipeOffset"
to: expandedDelegateWrapper.swipeOffset > 0 ? expandedDelegateWrapper.width : -expandedDelegateWrapper.width to: expandedDelegateWrapper.swipeOffset > 0 ? expandedDelegateWrapper.width : -expandedDelegateWrapper.width
duration: Theme.shortDuration duration: Theme.notificationExitDuration
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
onStopped: NotificationService.dismissNotification(modelData) onStopped: NotificationService.dismissNotification(modelData)
} }

View File

@@ -7,6 +7,7 @@ DankPopout {
id: root id: root
layerNamespace: "dms:notification-center-popout" layerNamespace: "dms:notification-center-popout"
fullHeightSurface: true
property bool notificationHistoryVisible: false property bool notificationHistoryVisible: false
property var triggerScreen: null property var triggerScreen: null
@@ -34,9 +35,9 @@ DankPopout {
popupWidth: triggerScreen ? Math.min(500, Math.max(380, triggerScreen.width - 48)) : 400 popupWidth: triggerScreen ? Math.min(500, Math.max(380, triggerScreen.width - 48)) : 400
popupHeight: stablePopupHeight popupHeight: stablePopupHeight
positioning: "" positioning: ""
animationScaleCollapsed: 1.0 animationScaleCollapsed: 0.94
animationOffset: 0 animationOffset: 0
suspendShadowWhileResizing: true suspendShadowWhileResizing: false
screen: triggerScreen screen: triggerScreen
shouldBeVisible: notificationHistoryVisible shouldBeVisible: notificationHistoryVisible

View File

@@ -814,7 +814,7 @@ PanelWindow {
Behavior on swipeOffset { Behavior on swipeOffset {
enabled: !content.swipeActive && !content.swipeDismissing enabled: !content.swipeActive && !content.swipeDismissing
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.notificationExitDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
@@ -824,7 +824,7 @@ PanelWindow {
target: content target: content
property: "swipeOffset" property: "swipeOffset"
to: isTopCenter ? -content.height : isBottomCenter ? content.height : (SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom ? -content.width : content.width) to: isTopCenter ? -content.height : isBottomCenter ? content.height : (SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom ? -content.width : content.width)
duration: Theme.shortDuration duration: Theme.notificationExitDuration
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
onStopped: { onStopped: {
NotificationService.dismissNotification(notificationData); NotificationService.dismissNotification(notificationData);

View File

@@ -7,9 +7,10 @@ DankOSD {
id: root id: root
readonly property bool useVertical: isVerticalLayout readonly property bool useVertical: isVerticalLayout
property int targetBrightness: { property int _displayBrightness: 0
DisplayService.brightnessVersion;
return DisplayService.brightnessLevel; function _syncBrightness() {
_displayBrightness = DisplayService.brightnessLevel;
} }
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2) osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
@@ -20,9 +21,9 @@ DankOSD {
Connections { Connections {
target: DisplayService target: DisplayService
function onBrightnessChanged(showOsd) { function onBrightnessChanged(showOsd) {
if (showOsd && SettingsData.osdBrightnessEnabled) { root._syncBrightness();
if (showOsd && SettingsData.osdBrightnessEnabled)
root.show(); root.show();
}
} }
} }
@@ -53,13 +54,11 @@ DankOSD {
anchors.centerIn: parent anchors.centerIn: parent
name: { name: {
const deviceInfo = DisplayService.getCurrentDeviceInfo(); const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc") { if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc")
return "brightness_medium"; return "brightness_medium";
} else if (deviceInfo.name.includes("kbd")) { if (deviceInfo.name.includes("kbd"))
return "keyboard"; return "keyboard";
} else { return "lightbulb";
return "lightbulb";
}
} }
size: Theme.iconSize size: Theme.iconSize
color: Theme.primary color: Theme.primary
@@ -77,20 +76,16 @@ DankOSD {
const deviceInfo = DisplayService.getCurrentDeviceInfo(); const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo) if (!deviceInfo)
return 1; return 1;
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id); if (SessionData.getBrightnessExponential(deviceInfo.id))
if (isExponential) {
return 1; return 1;
}
return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0; return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0;
} }
maximum: { maximum: {
const deviceInfo = DisplayService.getCurrentDeviceInfo(); const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo) if (!deviceInfo)
return 100; return 100;
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id); if (SessionData.getBrightnessExponential(deviceInfo.id))
if (isExponential) {
return 100; return 100;
}
return deviceInfo.displayMax || 100; return deviceInfo.displayMax || 100;
} }
enabled: DisplayService.brightnessAvailable enabled: DisplayService.brightnessAvailable
@@ -99,28 +94,24 @@ DankOSD {
const deviceInfo = DisplayService.getCurrentDeviceInfo(); const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo) if (!deviceInfo)
return "%"; return "%";
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id); if (SessionData.getBrightnessExponential(deviceInfo.id))
if (isExponential) {
return "%"; return "%";
}
return deviceInfo.class === "ddc" ? "" : "%"; return deviceInfo.class === "ddc" ? "" : "%";
} }
thumbOutlineColor: Theme.surfaceContainer thumbOutlineColor: Theme.surfaceContainer
alwaysShowValue: SettingsData.osdAlwaysShowValue alwaysShowValue: SettingsData.osdAlwaysShowValue
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
if (DisplayService.brightnessAvailable) { if (!DisplayService.brightnessAvailable)
DisplayService.setBrightness(newValue, DisplayService.lastIpcDevice, true); return;
resetHideTimer(); DisplayService.setBrightness(newValue, DisplayService.lastIpcDevice, true);
} resetHideTimer();
} }
onContainsMouseChanged: { onContainsMouseChanged: setChildHovered(containsMouse)
setChildHovered(containsMouse);
}
Binding on value { Binding on value {
value: root.targetBrightness value: root._displayBrightness
when: !brightnessSlider.isDragging when: !brightnessSlider.isDragging
} }
} }
@@ -146,13 +137,11 @@ DankOSD {
anchors.centerIn: parent anchors.centerIn: parent
name: { name: {
const deviceInfo = DisplayService.getCurrentDeviceInfo(); const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc") { if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc")
return "brightness_medium"; return "brightness_medium";
} else if (deviceInfo.name.includes("kbd")) { if (deviceInfo.name.includes("kbd"))
return "keyboard"; return "keyboard";
} else { return "lightbulb";
return "lightbulb";
}
} }
size: Theme.iconSize size: Theme.iconSize
color: Theme.primary color: Theme.primary
@@ -170,7 +159,7 @@ DankOSD {
property int value: 50 property int value: 50
Binding on value { Binding on value {
value: root.targetBrightness value: root._displayBrightness
when: !vertSlider.dragging when: !vertSlider.dragging
} }
@@ -178,8 +167,7 @@ DankOSD {
const deviceInfo = DisplayService.getCurrentDeviceInfo(); const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo) if (!deviceInfo)
return 1; return 1;
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id); if (SessionData.getBrightnessExponential(deviceInfo.id))
if (isExponential)
return 1; return 1;
return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0; return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0;
} }
@@ -188,8 +176,7 @@ DankOSD {
const deviceInfo = DisplayService.getCurrentDeviceInfo(); const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo) if (!deviceInfo)
return 100; return 100;
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id); if (SessionData.getBrightnessExponential(deviceInfo.id))
if (isExponential)
return 100; return 100;
return deviceInfo.displayMax || 100; return deviceInfo.displayMax || 100;
} }
@@ -240,33 +227,25 @@ DankOSD {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: { onContainsMouseChanged: setChildHovered(containsMouse)
setChildHovered(containsMouse);
}
onPressed: mouse => { onPressed: mouse => {
vertSlider.dragging = true; vertSlider.dragging = true;
updateBrightness(mouse); updateBrightness(mouse);
} }
onReleased: { onReleased: vertSlider.dragging = false
vertSlider.dragging = false;
}
onPositionChanged: mouse => { onPositionChanged: mouse => {
if (pressed) { if (pressed)
updateBrightness(mouse); updateBrightness(mouse);
}
} }
onClicked: mouse => { onClicked: mouse => updateBrightness(mouse)
updateBrightness(mouse);
}
function updateBrightness(mouse) { function updateBrightness(mouse) {
if (!DisplayService.brightnessAvailable) { if (!DisplayService.brightnessAvailable)
return; return;
}
const ratio = 1.0 - (mouse.y / height); const ratio = 1.0 - (mouse.y / height);
const newValue = Math.round(vertSlider.minimum + ratio * (vertSlider.maximum - vertSlider.minimum)); const newValue = Math.round(vertSlider.minimum + ratio * (vertSlider.maximum - vertSlider.minimum));
vertSlider.value = newValue; vertSlider.value = newValue;

View File

@@ -8,13 +8,20 @@ DankOSD {
readonly property bool useVertical: isVerticalLayout readonly property bool useVertical: isVerticalLayout
readonly property var player: MprisController.activePlayer readonly property var player: MprisController.activePlayer
readonly property int currentVolume: player ? Math.min(100, Math.round(player.volume * 100)) : 0
readonly property bool volumeSupported: player?.volumeSupported ?? false readonly property bool volumeSupported: player?.volumeSupported ?? false
property bool _suppressNewPlayer: false property bool _suppressNewPlayer: false
property int _displayVolume: 0
function _syncVolume() {
if (!player)
return;
_displayVolume = Math.min(100, Math.round(player.volume * 100));
}
onPlayerChanged: { onPlayerChanged: {
_suppressNewPlayer = true; _suppressNewPlayer = true;
_suppressTimer.restart(); _suppressTimer.restart();
_syncVolume();
} }
Timer { Timer {
@@ -37,25 +44,25 @@ DankOSD {
} }
function toggleMute() { function toggleMute() {
if (player) { if (!player)
player.volume = player.volume > 0 ? 0 : 1; return;
} player.volume = player.volume > 0 ? 0 : 1;
} }
function setVolume(volumePercent) { function setVolume(volumePercent) {
if (player) { if (!player)
player.volume = volumePercent / 100; return;
resetHideTimer(); player.volume = volumePercent / 100;
} resetHideTimer();
} }
Connections { Connections {
target: player target: player
function onVolumeChanged() { function onVolumeChanged() {
if (SettingsData.osdMediaVolumeEnabled && volumeSupported && !_suppressNewPlayer) { root._syncVolume();
if (SettingsData.osdMediaVolumeEnabled && volumeSupported && !_suppressNewPlayer)
root.show(); root.show();
}
} }
} }
@@ -96,9 +103,7 @@ DankOSD {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: toggleMute() onClicked: toggleMute()
onContainsMouseChanged: { onContainsMouseChanged: setChildHovered(containsMouse || volumeSlider.containsMouse)
setChildHovered(containsMouse || volumeSlider.containsMouse);
}
} }
} }
@@ -115,29 +120,21 @@ DankOSD {
showValue: true showValue: true
unit: "%" unit: "%"
thumbOutlineColor: Theme.surfaceContainer thumbOutlineColor: Theme.surfaceContainer
valueOverride: currentVolume valueOverride: root._displayVolume
alwaysShowValue: SettingsData.osdAlwaysShowValue alwaysShowValue: SettingsData.osdAlwaysShowValue
Component.onCompleted: { Component.onCompleted: {
value = currentVolume; root._syncVolume();
value = root._displayVolume;
} }
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => setVolume(newValue)
setVolume(newValue);
}
onContainsMouseChanged: { onContainsMouseChanged: setChildHovered(containsMouse || muteButton.containsMouse)
setChildHovered(containsMouse || muteButton.containsMouse);
}
Connections { Binding on value {
target: player value: root._displayVolume
when: !volumeSlider.pressed
function onVolumeChanged() {
if (volumeSlider && !volumeSlider.pressed) {
volumeSlider.value = currentVolume;
}
}
} }
} }
} }
@@ -172,9 +169,7 @@ DankOSD {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: toggleMute() onClicked: toggleMute()
onContainsMouseChanged: { onContainsMouseChanged: setChildHovered(containsMouse || vertSliderArea.containsMouse)
setChildHovered(containsMouse || vertSliderArea.containsMouse);
}
} }
} }
@@ -186,7 +181,7 @@ DankOSD {
y: gap * 2 + Theme.iconSize y: gap * 2 + Theme.iconSize
property bool dragging: false property bool dragging: false
property int value: currentVolume property int value: root._displayVolume
Rectangle { Rectangle {
id: vertTrack id: vertTrack
@@ -231,28 +226,21 @@ DankOSD {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: { onContainsMouseChanged: setChildHovered(containsMouse || muteButtonVert.containsMouse)
setChildHovered(containsMouse || muteButtonVert.containsMouse);
}
onPressed: mouse => { onPressed: mouse => {
vertSlider.dragging = true; vertSlider.dragging = true;
updateVolume(mouse); updateVolume(mouse);
} }
onReleased: { onReleased: vertSlider.dragging = false
vertSlider.dragging = false;
}
onPositionChanged: mouse => { onPositionChanged: mouse => {
if (pressed) { if (pressed)
updateVolume(mouse); updateVolume(mouse);
}
} }
onClicked: mouse => { onClicked: mouse => updateVolume(mouse)
updateVolume(mouse);
}
function updateVolume(mouse) { function updateVolume(mouse) {
const ratio = 1.0 - (mouse.y / height); const ratio = 1.0 - (mouse.y / height);
@@ -260,16 +248,6 @@ DankOSD {
setVolume(volume); setVolume(volume);
} }
} }
Connections {
target: player
function onVolumeChanged() {
if (!vertSlider.dragging) {
vertSlider.value = currentVolume;
}
}
}
} }
StyledText { StyledText {
@@ -283,15 +261,4 @@ DankOSD {
} }
} }
} }
onOsdShown: {
if (player && contentLoader.item && contentLoader.item.item) {
if (!useVertical) {
const slider = contentLoader.item.item.children[0].children[1];
if (slider && slider.value !== undefined) {
slider.value = currentVolume;
}
}
}
}
} }

View File

@@ -7,6 +7,13 @@ DankOSD {
id: root id: root
readonly property bool useVertical: isVerticalLayout readonly property bool useVertical: isVerticalLayout
property int _displayVolume: 0
function _syncVolume() {
if (!AudioService.sink?.audio)
return;
_displayVolume = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
}
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2) osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
osdHeight: useVertical ? Math.min(260, Screen.height - Theme.spacingM * 2) : (40 + Theme.spacingS * 2) osdHeight: useVertical ? Math.min(260, Screen.height - Theme.spacingM * 2) : (40 + Theme.spacingS * 2)
@@ -14,18 +21,17 @@ DankOSD {
enableMouseInteraction: true enableMouseInteraction: true
Connections { Connections {
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null target: AudioService.sink?.audio ?? null
function onVolumeChanged() { function onVolumeChanged() {
if (SettingsData.osdVolumeEnabled) { root._syncVolume();
if (SettingsData.osdVolumeEnabled)
root.show(); root.show();
}
} }
function onMutedChanged() { function onMutedChanged() {
if (SettingsData.osdVolumeEnabled) { if (SettingsData.osdVolumeEnabled)
root.show(); root.show();
}
} }
} }
@@ -33,9 +39,9 @@ DankOSD {
target: AudioService target: AudioService
function onSinkChanged() { function onSinkChanged() {
if (root.shouldBeVisible && SettingsData.osdVolumeEnabled) { root._syncVolume();
if (root.shouldBeVisible && SettingsData.osdVolumeEnabled)
root.show(); root.show();
}
} }
} }
@@ -64,7 +70,7 @@ DankOSD {
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.muted ? "volume_off" : "volume_up" name: AudioService.sink?.audio?.muted ? "volume_off" : "volume_up"
size: Theme.iconSize size: Theme.iconSize
color: muteButton.containsMouse ? Theme.primary : Theme.surfaceText color: muteButton.containsMouse ? Theme.primary : Theme.surfaceText
} }
@@ -75,60 +81,45 @@ DankOSD {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: AudioService.toggleMute()
AudioService.toggleMute(); onContainsMouseChanged: setChildHovered(containsMouse || volumeSlider.containsMouse)
}
onContainsMouseChanged: {
setChildHovered(containsMouse || volumeSlider.containsMouse);
}
} }
} }
DankSlider { DankSlider {
id: volumeSlider id: volumeSlider
readonly property real actualVolumePercent: AudioService.sink && AudioService.sink.audio ? Math.round(AudioService.sink.audio.volume * 100) : 0
readonly property real displayPercent: actualVolumePercent
width: parent.width - Theme.iconSize - parent.gap * 3 width: parent.width - Theme.iconSize - parent.gap * 3
height: 40 height: 40
x: parent.gap * 2 + Theme.iconSize x: parent.gap * 2 + Theme.iconSize
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
minimum: 0 minimum: 0
maximum: AudioService.sinkMaxVolume maximum: AudioService.sinkMaxVolume
enabled: AudioService.sink && AudioService.sink.audio enabled: AudioService.sink?.audio
showValue: true showValue: true
unit: "%" unit: "%"
thumbOutlineColor: Theme.surfaceContainer thumbOutlineColor: Theme.surfaceContainer
valueOverride: displayPercent valueOverride: root._displayVolume
alwaysShowValue: SettingsData.osdAlwaysShowValue alwaysShowValue: SettingsData.osdAlwaysShowValue
Component.onCompleted: { Component.onCompleted: {
if (AudioService.sink && AudioService.sink.audio) { root._syncVolume();
value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100)); value = root._displayVolume;
}
} }
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
if (AudioService.sink && AudioService.sink.audio) { if (!AudioService.sink?.audio)
SessionData.suppressOSDTemporarily(); return;
AudioService.sink.audio.volume = newValue / 100; SessionData.suppressOSDTemporarily();
resetHideTimer(); AudioService.sink.audio.volume = newValue / 100;
} resetHideTimer();
} }
onContainsMouseChanged: { onContainsMouseChanged: setChildHovered(containsMouse || muteButton.containsMouse)
setChildHovered(containsMouse || muteButton.containsMouse);
}
Connections { Binding on value {
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null value: root._displayVolume
when: !volumeSlider.pressed
function onVolumeChanged() {
if (volumeSlider && !volumeSlider.pressed) {
volumeSlider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
}
}
} }
} }
} }
@@ -151,7 +142,7 @@ DankOSD {
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.muted ? "volume_off" : "volume_up" name: AudioService.sink?.audio?.muted ? "volume_off" : "volume_up"
size: Theme.iconSize size: Theme.iconSize
color: muteButtonVert.containsMouse ? Theme.primary : Theme.surfaceText color: muteButtonVert.containsMouse ? Theme.primary : Theme.surfaceText
} }
@@ -162,12 +153,8 @@ DankOSD {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: AudioService.toggleMute()
AudioService.toggleMute(); onContainsMouseChanged: setChildHovered(containsMouse || vertSliderArea.containsMouse)
}
onContainsMouseChanged: {
setChildHovered(containsMouse || vertSliderArea.containsMouse);
}
} }
} }
@@ -179,7 +166,7 @@ DankOSD {
y: gap * 2 + Theme.iconSize y: gap * 2 + Theme.iconSize
property bool dragging: false property bool dragging: false
property int value: AudioService.sink && AudioService.sink.audio ? Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100)) : 0 property int value: root._displayVolume
Rectangle { Rectangle {
id: vertTrack id: vertTrack
@@ -220,50 +207,35 @@ DankOSD {
id: vertSliderArea id: vertSliderArea
anchors.fill: parent anchors.fill: parent
anchors.margins: -12 anchors.margins: -12
enabled: AudioService.sink && AudioService.sink.audio enabled: AudioService.sink?.audio
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: { onContainsMouseChanged: setChildHovered(containsMouse || muteButtonVert.containsMouse)
setChildHovered(containsMouse || muteButtonVert.containsMouse);
}
onPressed: mouse => { onPressed: mouse => {
vertSlider.dragging = true; vertSlider.dragging = true;
updateVolume(mouse); updateVolume(mouse);
} }
onReleased: { onReleased: vertSlider.dragging = false
vertSlider.dragging = false;
}
onPositionChanged: mouse => { onPositionChanged: mouse => {
if (pressed) { if (pressed)
updateVolume(mouse); updateVolume(mouse);
}
} }
onClicked: mouse => { onClicked: mouse => updateVolume(mouse)
updateVolume(mouse);
}
function updateVolume(mouse) { function updateVolume(mouse) {
if (AudioService.sink && AudioService.sink.audio) { if (!AudioService.sink?.audio)
const maxVol = AudioService.sinkMaxVolume; return;
const ratio = 1.0 - (mouse.y / height); const maxVol = AudioService.sinkMaxVolume;
const volume = Math.max(0, Math.min(maxVol, Math.round(ratio * maxVol))); const ratio = 1.0 - (mouse.y / height);
SessionData.suppressOSDTemporarily(); const volume = Math.max(0, Math.min(maxVol, Math.round(ratio * maxVol)));
AudioService.sink.audio.volume = volume / 100; SessionData.suppressOSDTemporarily();
resetHideTimer(); AudioService.sink.audio.volume = volume / 100;
} resetHideTimer();
}
}
Connections {
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
function onVolumeChanged() {
vertSlider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
} }
} }
} }
@@ -279,15 +251,4 @@ DankOSD {
} }
} }
} }
onOsdShown: {
if (AudioService.sink && AudioService.sink.audio && contentLoader.item && contentLoader.item.item) {
if (!useVertical) {
const slider = contentLoader.item.item.children[0].children[1];
if (slider && slider.value !== undefined) {
slider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
}
}
}
}
} }

View File

@@ -18,36 +18,41 @@ Column {
property bool isInitialized: false property bool isInitialized: false
function loadValue() { function loadValue() {
const settings = findSettings() const settings = findSettings();
if (settings && settings.pluginService) { if (settings && settings.pluginService) {
const loadedValue = settings.loadValue(settingKey, defaultValue) const loadedValue = settings.loadValue(settingKey, defaultValue);
value = loadedValue if (textField.activeFocus && isInitialized)
textField.text = loadedValue return;
isInitialized = true value = loadedValue;
textField.text = loadedValue;
isInitialized = true;
} }
} }
Component.onCompleted: { Component.onCompleted: {
Qt.callLater(loadValue) Qt.callLater(loadValue);
} }
onValueChanged: { function commit() {
if (!isInitialized) return if (!isInitialized)
const settings = findSettings() return;
if (settings) { if (textField.text === value)
settings.saveValue(settingKey, value) return;
} value = textField.text;
const settings = findSettings();
if (settings)
settings.saveValue(settingKey, value);
} }
function findSettings() { function findSettings() {
let item = parent let item = parent;
while (item) { while (item) {
if (item.saveValue !== undefined && item.loadValue !== undefined) { if (item.saveValue !== undefined && item.loadValue !== undefined) {
return item return item;
} }
item = item.parent item = item.parent;
} }
return null return null;
} }
StyledText { StyledText {
@@ -70,16 +75,10 @@ Column {
id: textField id: textField
width: parent.width width: parent.width
placeholderText: root.placeholder placeholderText: root.placeholder
onTextEdited: { onEditingFinished: root.commit()
root.value = text
}
onEditingFinished: {
root.value = text
}
onActiveFocusChanged: { onActiveFocusChanged: {
if (!activeFocus) { if (!activeFocus)
root.value = text root.commit();
}
} }
} }
} }

View File

@@ -26,8 +26,8 @@ DankPopout {
open(); open();
} }
popupWidth: 650 popupWidth: Math.round(Theme.fontSizeMedium * 46)
popupHeight: 550 popupHeight: Math.round(Theme.fontSizeMedium * 39)
triggerWidth: 55 triggerWidth: 55
positioning: "" positioning: ""
screen: triggerScreen screen: triggerScreen
@@ -151,11 +151,13 @@ DankPopout {
DankButtonGroup { DankButtonGroup {
id: processFilterGroup id: processFilterGroup
Layout.minimumWidth: implicitWidth + 8 Layout.minimumWidth: implicitWidth
model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")] model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")]
currentIndex: 0 currentIndex: 0
checkEnabled: false checkEnabled: false
buttonHeight: Math.round(Theme.fontSizeMedium * 2.2) buttonHeight: Math.round(Theme.fontSizeSmall * 2.4)
minButtonWidth: 0
buttonPadding: Theme.spacingM
textSize: Theme.fontSizeSmall textSize: Theme.fontSizeSmall
onSelectionChanged: (index, selected) => { onSelectionChanged: (index, selected) => {
if (!selected) if (!selected)
@@ -177,7 +179,8 @@ DankPopout {
DankTextField { DankTextField {
id: searchField id: searchField
Layout.preferredWidth: Theme.fontSizeMedium * 14 Layout.fillWidth: true
Layout.minimumWidth: Theme.fontSizeMedium * 8
Layout.preferredHeight: Theme.fontSizeMedium * 2.5 Layout.preferredHeight: Theme.fontSizeMedium * 2.5
placeholderText: I18n.tr("Search...") placeholderText: I18n.tr("Search...")
leftIconName: "search" leftIconName: "search"

View File

@@ -375,8 +375,20 @@ FocusScope {
if (!plugin || !PluginService.isPluginLoaded(pluginId)) if (!plugin || !PluginService.isPluginLoaded(pluginId))
return; return;
var isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher")); var isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher"));
if (isLauncher) if (isLauncher) {
PluginService.reloadPlugin(pluginId); pluginReloadTimer.pendingPluginId = pluginId;
pluginReloadTimer.restart();
}
}
}
Timer {
id: pluginReloadTimer
property string pendingPluginId: ""
interval: 500
onTriggered: {
if (pendingPluginId)
PluginService.reloadPlugin(pendingPluginId);
} }
} }

View File

@@ -16,7 +16,7 @@ Item {
property var cachedCursorThemes: SettingsData.availableCursorThemes property var cachedCursorThemes: SettingsData.availableCursorThemes
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label) property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
property var installedRegistryThemes: [] property var installedRegistryThemes: []
property var templateDetection: ({}) property var templateDetection: []
property var cursorIncludeStatus: ({ property var cursorIncludeStatus: ({
"exists": false, "exists": false,
@@ -106,9 +106,10 @@ Item {
} }
function isTemplateDetected(templateId) { function isTemplateDetected(templateId) {
if (!templateDetection || Object.keys(templateDetection).length === 0) if (!templateDetection || templateDetection.length === 0)
return true; return true;
return templateDetection[templateId] !== false; var item = templateDetection.find(i => i.id === templateId);
return !item || item.detected !== false;
} }
function getTemplateDescription(templateId, baseDescription) { function getTemplateDescription(templateId, baseDescription) {
@@ -145,30 +146,17 @@ Item {
DMSService.listInstalledThemes(); DMSService.listInstalledThemes();
if (PopoutService.pendingThemeInstall) if (PopoutService.pendingThemeInstall)
Qt.callLater(() => showThemeBrowser()); Qt.callLater(() => showThemeBrowser());
templateCheckProcess.running = true; Proc.runCommand("template-check", ["dms", "matugen", "check"], (output, exitCode) => {
if (exitCode !== 0)
return;
try {
themeColorsTab.templateDetection = JSON.parse(output.trim());
} catch (e) {}
});
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl) if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl)
checkCursorIncludeStatus(); checkCursorIncludeStatus();
} }
Process {
id: templateCheckProcess
command: ["dms", "matugen", "check"]
running: false
stdout: StdioCollector {
onStreamFinished: {
try {
const results = JSON.parse(text);
const detection = {};
for (const item of results) {
detection[item.id] = item.detected;
}
themeColorsTab.templateDetection = detection;
} catch (e) {}
}
}
}
Connections { Connections {
target: DMSService target: DMSService
function onInstalledThemesReceived(themes) { function onInstalledThemesReceived(themes) {

View File

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

View File

@@ -1 +1 @@
v1.4.0 v1.5-beta

View File

@@ -28,6 +28,10 @@ PanelWindow {
function show() { function show() {
if (SessionData.suppressOSD) if (SessionData.suppressOSD)
return; return;
if (shouldBeVisible) {
hideTimer.restart();
return;
}
OSDManager.showOSD(root); OSDManager.showOSD(root);
closeTimer.stop(); closeTimer.stop();
shouldBeVisible = true; shouldBeVisible = true;
@@ -257,7 +261,7 @@ PanelWindow {
property real shadowSpreadPx: 0 property real shadowSpreadPx: 0
property real shadowBaseAlpha: 0.60 property real shadowBaseAlpha: 0.60
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha * osdContainer.opacity)) readonly property real effectiveShadowAlpha: shouldBeVisible ? Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) : 0
Rectangle { Rectangle {
id: background id: background

View File

@@ -30,7 +30,10 @@ Item {
property var customKeyboardFocus: null property var customKeyboardFocus: null
property bool backgroundInteractive: true property bool backgroundInteractive: true
property bool contentHandlesKeys: false property bool contentHandlesKeys: false
property bool fullHeightSurface: false
property bool _resizeActive: false property bool _resizeActive: false
property real _surfaceMarginLeft: 0
property real _surfaceW: 0
property real storedBarThickness: Theme.barHeight - 4 property real storedBarThickness: Theme.barHeight - 4
property real storedBarSpacing: 4 property real storedBarSpacing: 4
@@ -125,6 +128,10 @@ Item {
_lastOpenedScreen = screen; _lastOpenedScreen = screen;
shouldBeVisible = true; shouldBeVisible = true;
if (useBackgroundWindow) {
_surfaceMarginLeft = alignedX - shadowBuffer;
_surfaceW = alignedWidth + shadowBuffer * 2;
}
Qt.callLater(() => { Qt.callLater(() => {
if (shouldBeVisible && screen) { if (shouldBeVisible && screen) {
if (useBackgroundWindow) if (useBackgroundWindow)
@@ -360,20 +367,38 @@ Item {
return WlrKeyboardFocus.Exclusive; return WlrKeyboardFocus.Exclusive;
} }
readonly property bool _fullHeight: useBackgroundWindow && root.fullHeightSurface
anchors { anchors {
left: true left: true
top: true top: true
right: !useBackgroundWindow right: !useBackgroundWindow
bottom: !useBackgroundWindow bottom: _fullHeight || !useBackgroundWindow
} }
WlrLayershell.margins { WlrLayershell.margins {
left: useBackgroundWindow ? (root.alignedX - shadowBuffer) : 0 left: useBackgroundWindow ? root._surfaceMarginLeft : 0
top: useBackgroundWindow ? (root.alignedY - shadowBuffer) : 0 top: (useBackgroundWindow && !_fullHeight) ? (root.alignedY - shadowBuffer) : 0
} }
implicitWidth: useBackgroundWindow ? (root.alignedWidth + (shadowBuffer * 2)) : 0 implicitWidth: useBackgroundWindow ? root._surfaceW : 0
implicitHeight: useBackgroundWindow ? (root.alignedHeight + (shadowBuffer * 2)) : 0 implicitHeight: (useBackgroundWindow && !_fullHeight) ? (root.alignedHeight + shadowBuffer * 2) : 0
mask: (useBackgroundWindow && _fullHeight) ? contentInputMask : null
Region {
id: contentInputMask
item: contentMaskRect
}
Item {
id: contentMaskRect
visible: false
x: contentContainer.x - root.shadowBuffer
y: contentContainer.y - root.shadowBuffer
width: root.alignedWidth + root.shadowBuffer * 2
height: root.alignedHeight + root.shadowBuffer * 2
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@@ -393,7 +418,7 @@ Item {
Item { Item {
id: contentContainer id: contentContainer
x: useBackgroundWindow ? shadowBuffer : root.alignedX x: useBackgroundWindow ? shadowBuffer : root.alignedX
y: useBackgroundWindow ? shadowBuffer : root.alignedY y: (useBackgroundWindow && !contentWindow._fullHeight) ? shadowBuffer : root.alignedY
width: root.alignedWidth width: root.alignedWidth
height: root.alignedHeight height: root.alignedHeight

View File

@@ -1,3 +1,3 @@
[templates.dmsemacs] [templates.dmsemacs]
input_path = 'SHELL_DIR/matugen/templates/dank-emacs.el' input_path = 'SHELL_DIR/matugen/templates/dank-emacs.el'
output_path = "~/.emacs.d/themes/dank-emacs-theme.el" output_path = 'EMACS_DIR/themes/dank-emacs-theme.el'