1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-15 00:32:47 -04:00

Compare commits

...

5 Commits

Author SHA1 Message Date
bbedward 6fe4cc98b9 popout: fix blurry text 2026-02-09 21:33:12 -05:00
ArijanJ b9bcfd8d2c Making the new media playback OSD more beautiful (#1638)
* feat: decouple track art downloads into new TrackArtService

* feat: beautify media playback osd with track art

* fix: bug when switching from art to no art
2026-02-09 21:13:23 -05:00
bbedward e3bd31bb52 clipboard: fix row layout overflow 2026-02-09 21:09:15 -05:00
purian23 0922e3e459 clipboard: Fix pinned entry logic
- Add keyboard nav to pinned entries
- Fix wrong copied selection upon Enter
2026-02-09 20:53:48 -05:00
purian23 a168b12bb2 dankbar: Fix widget context focus w/Autohide enabled 2026-02-09 19:42:27 -05:00
15 changed files with 481 additions and 143 deletions
+10 -3
View File
@@ -146,9 +146,16 @@ func handleCopyEntry(conn net.Conn, req models.Request, m *Manager) {
return
}
if err := m.TouchEntry(uint64(id)); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
if entry.Pinned {
if err := m.CreateHistoryEntryFromPinned(entry); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
} else {
if err := m.TouchEntry(uint64(id)); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
}
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "copied to clipboard"})
+92 -1
View File
@@ -388,6 +388,10 @@ func (m *Manager) deduplicateInTx(b *bolt.Bucket, hash uint64) error {
if extractHash(v) != hash {
continue
}
entry, err := decodeEntry(v)
if err == nil && entry.Pinned {
continue
}
if err := b.Delete(k); err != nil {
return err
}
@@ -842,6 +846,62 @@ func (m *Manager) TouchEntry(id uint64) error {
return nil
}
func (m *Manager) CreateHistoryEntryFromPinned(pinnedEntry *Entry) error {
if m.db == nil {
return fmt.Errorf("database not available")
}
// Create a new unpinned entry with the same data
newEntry := Entry{
Data: pinnedEntry.Data,
MimeType: pinnedEntry.MimeType,
Size: pinnedEntry.Size,
Timestamp: time.Now(),
IsImage: pinnedEntry.IsImage,
Preview: pinnedEntry.Preview,
Pinned: false,
}
if err := m.storeEntryWithoutDedup(newEntry); err != nil {
return err
}
m.updateState()
m.notifySubscribers()
return nil
}
func (m *Manager) storeEntryWithoutDedup(entry Entry) error {
if m.db == nil {
return fmt.Errorf("database not available")
}
entry.Hash = computeHash(entry.Data)
return m.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("clipboard"))
id, err := b.NextSequence()
if err != nil {
return err
}
entry.ID = id
encoded, err := encodeEntry(entry)
if err != nil {
return err
}
if err := b.Put(itob(id), encoded); err != nil {
return err
}
return m.trimLengthInTx(b)
})
}
func (m *Manager) ClearHistory() {
if m.db == nil {
return
@@ -1419,6 +1479,37 @@ func (m *Manager) PinEntry(id uint64) error {
return fmt.Errorf("database not available")
}
entryToPin, err := m.GetEntry(id)
if err != nil {
return err
}
var hashExists bool
if err := m.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("clipboard"))
if b == nil {
return nil
}
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
entry, err := decodeEntry(v)
if err != nil || !entry.Pinned {
continue
}
if entry.Hash == entryToPin.Hash {
hashExists = true
return nil
}
}
return nil
}); err != nil {
return err
}
if hashExists {
return nil
}
// Check pinned count
cfg := m.getConfig()
pinnedCount := 0
@@ -1443,7 +1534,7 @@ func (m *Manager) PinEntry(id uint64) error {
return fmt.Errorf("maximum pinned entries reached (%d)", cfg.MaxPinned)
}
err := m.db.Update(func(tx *bolt.Tx) error {
err = m.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("clipboard"))
v := b.Get(itob(id))
if v == nil {
@@ -164,6 +164,7 @@ Item {
}
visible: modal.activeTab === "saved"
currentIndex: clipboardContent.modal ? clipboardContent.modal.selectedIndex : 0
spacing: Theme.spacingXS
interactive: true
flickDeceleration: 1500
@@ -173,6 +174,26 @@ Item {
pressDelay: 0
flickableDirection: Flickable.VerticalFlick
function ensureVisible(index) {
if (index < 0 || index >= count) {
return;
}
const itemHeight = ClipboardConstants.itemHeight + spacing;
const itemY = index * itemHeight;
const itemBottom = itemY + itemHeight;
if (itemY < contentY) {
contentY = itemY;
} else if (itemBottom > contentY + height) {
contentY = itemBottom - height;
}
}
onCurrentIndexChanged: {
if (clipboardContent.modal?.keyboardNavigationActive && currentIndex >= 0) {
ensureVisible(currentIndex);
}
}
StyledText {
text: I18n.tr("No saved clipboard entries")
anchors.centerIn: parent
@@ -190,7 +211,7 @@ Item {
entry: modelData
entryIndex: index + 1
itemIndex: index
isSelected: false
isSelected: clipboardContent.modal?.keyboardNavigationActive && index === clipboardContent.modal.selectedIndex
modal: clipboardContent.modal
listView: savedListView
onCopyRequested: clipboardContent.modal.copyEntry(modelData)
+81 -73
View File
@@ -1,5 +1,6 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
@@ -19,6 +20,7 @@ Rectangle {
readonly property string entryType: modal ? modal.getEntryType(entry) : "text"
readonly property string entryPreview: modal ? modal.getEntryPreview(entry) : ""
readonly property bool hasPinnedDuplicate: !entry.pinned && ClipboardService.hashedPinnedEntry(entry.hash)
radius: Theme.cornerRadius
color: {
@@ -28,91 +30,37 @@ Rectangle {
return mouseArea.containsMouse ? Theme.primaryHoverLight : Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency);
}
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
anchors.rightMargin: Theme.spacingS
spacing: Theme.spacingL
Rectangle {
id: indexBadge
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
width: 24
height: 24
radius: 12
color: Theme.primarySelected
Rectangle {
width: 24
height: 24
radius: 12
color: Theme.primarySelected
anchors.verticalCenter: parent.verticalCenter
StyledText {
anchors.centerIn: parent
text: entryIndex.toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.primary
}
}
Row {
anchors.verticalCenter: parent.verticalCenter
width: parent.width - 110
spacing: Theme.spacingM
ClipboardThumbnail {
width: entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize
height: entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize
anchors.verticalCenter: parent.verticalCenter
entry: root.entry
entryType: root.entryType
modal: root.modal
listView: root.listView
itemIndex: root.itemIndex
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: parent.width - (entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize) - Theme.spacingM
spacing: Theme.spacingXS
StyledText {
text: {
switch (entryType) {
case "image":
return I18n.tr("Image") + " • " + entryPreview;
case "long_text":
return I18n.tr("Long Text");
default:
return I18n.tr("Text");
}
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
font.weight: Font.Medium
width: parent.width
elide: Text.ElideRight
}
StyledText {
text: entryPreview
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
width: parent.width
wrapMode: Text.WordWrap
maximumLineCount: entryType === "long_text" ? 3 : 1
elide: Text.ElideRight
}
}
StyledText {
anchors.centerIn: parent
text: entryIndex.toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.primary
}
}
Row {
id: actionButtons
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
DankActionButton {
iconName: "push_pin"
iconSize: Theme.iconSize - 6
iconColor: entry.pinned ? Theme.primary : Theme.surfaceText
backgroundColor: entry.pinned ? Theme.primarySelected : "transparent"
iconColor: (entry.pinned || hasPinnedDuplicate) ? Theme.primary : Theme.surfaceText
backgroundColor: (entry.pinned || hasPinnedDuplicate) ? Theme.primarySelected : "transparent"
onClicked: entry.pinned ? unpinRequested() : pinRequested()
}
@@ -124,6 +72,66 @@ Rectangle {
}
}
Item {
anchors.left: indexBadge.right
anchors.leftMargin: Theme.spacingM
anchors.right: actionButtons.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
height: contentColumn.implicitHeight
clip: true
ClipboardThumbnail {
id: thumbnail
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize
height: entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize
entry: root.entry
entryType: root.entryType
modal: root.modal
listView: root.listView
itemIndex: root.itemIndex
}
Column {
id: contentColumn
anchors.left: thumbnail.right
anchors.leftMargin: Theme.spacingM
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
StyledText {
text: {
switch (entryType) {
case "image":
return I18n.tr("Image") + " • " + entryPreview;
case "long_text":
return I18n.tr("Long Text");
default:
return I18n.tr("Text");
}
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
font.weight: Font.Medium
width: parent.width
elide: Text.ElideRight
}
StyledText {
text: entryPreview
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
width: parent.width
wrapMode: Text.WordWrap
maximumLineCount: entryType === "long_text" ? 3 : 1
elide: Text.ElideRight
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
@@ -18,6 +18,10 @@ DankModal {
}
property string activeTab: "recents"
onActiveTabChanged: {
ClipboardService.selectedIndex = 0;
ClipboardService.keyboardNavigationActive = false;
}
property bool showKeyboardHints: false
property Component clipboardContent
property int activeImageLoads: 0
@@ -13,15 +13,17 @@ QtObject {
}
function selectNext() {
if (!ClipboardService.clipboardEntries || ClipboardService.clipboardEntries.length === 0) {
const entries = modal.activeTab === "saved" ? ClipboardService.pinnedEntries : ClipboardService.unpinnedEntries;
if (!entries || entries.length === 0) {
return;
}
ClipboardService.keyboardNavigationActive = true;
ClipboardService.selectedIndex = Math.min(ClipboardService.selectedIndex + 1, ClipboardService.clipboardEntries.length - 1);
ClipboardService.selectedIndex = Math.min(ClipboardService.selectedIndex + 1, entries.length - 1);
}
function selectPrevious() {
if (!ClipboardService.clipboardEntries || ClipboardService.clipboardEntries.length === 0) {
const entries = modal.activeTab === "saved" ? ClipboardService.pinnedEntries : ClipboardService.unpinnedEntries;
if (!entries || entries.length === 0) {
return;
}
ClipboardService.keyboardNavigationActive = true;
@@ -29,19 +31,25 @@ QtObject {
}
function copySelected() {
if (!ClipboardService.clipboardEntries || ClipboardService.clipboardEntries.length === 0 || ClipboardService.selectedIndex < 0 || ClipboardService.selectedIndex >= ClipboardService.clipboardEntries.length) {
const entries = modal.activeTab === "saved" ? ClipboardService.pinnedEntries : ClipboardService.unpinnedEntries;
if (!entries || entries.length === 0 || ClipboardService.selectedIndex < 0 || ClipboardService.selectedIndex >= entries.length) {
return;
}
const selectedEntry = ClipboardService.clipboardEntries[ClipboardService.selectedIndex];
const selectedEntry = entries[ClipboardService.selectedIndex];
modal.copyEntry(selectedEntry);
}
function deleteSelected() {
if (!ClipboardService.clipboardEntries || ClipboardService.clipboardEntries.length === 0 || ClipboardService.selectedIndex < 0 || ClipboardService.selectedIndex >= ClipboardService.clipboardEntries.length) {
const entries = modal.activeTab === "saved" ? ClipboardService.pinnedEntries : ClipboardService.unpinnedEntries;
if (!entries || entries.length === 0 || ClipboardService.selectedIndex < 0 || ClipboardService.selectedIndex >= entries.length) {
return;
}
const selectedEntry = ClipboardService.clipboardEntries[ClipboardService.selectedIndex];
modal.deleteEntry(selectedEntry);
const selectedEntry = entries[ClipboardService.selectedIndex];
if (modal.activeTab === "saved") {
modal.deletePinnedEntry(selectedEntry);
} else {
modal.deleteEntry(selectedEntry);
}
}
function handleKey(event) {
@@ -37,10 +37,18 @@ PanelWindow {
desktopEntry = entry || null;
visible = true;
if (targetScreen) {
TrayMenuManager.registerMenu(targetScreen.name, root);
}
}
function close() {
visible = false;
if (root.screen) {
TrayMenuManager.unregisterMenu(root.screen.name);
}
}
screen: null
@@ -56,6 +64,19 @@ PanelWindow {
bottom: true
}
Component.onDestruction: {
if (root.screen) {
TrayMenuManager.unregisterMenu(root.screen.name);
}
}
Connections {
target: PopoutManager
function onPopoutOpening() {
root.close();
}
}
Rectangle {
id: menuContainer
@@ -111,10 +111,18 @@ BasePill {
edge = barEdge ?? "top";
visible = true;
if (contextMenuWindow.screen) {
TrayMenuManager.registerMenu(contextMenuWindow.screen.name, contextMenuWindow);
}
}
function closeMenu() {
visible = false;
if (contextMenuWindow.screen) {
TrayMenuManager.unregisterMenu(contextMenuWindow.screen.name);
}
}
screen: null
@@ -130,6 +138,19 @@ BasePill {
bottom: true
}
Component.onDestruction: {
if (contextMenuWindow.screen) {
TrayMenuManager.unregisterMenu(contextMenuWindow.screen.name);
}
}
Connections {
target: PopoutManager
function onPopoutOpening() {
contextMenuWindow.closeMenu();
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
@@ -161,10 +161,18 @@ BasePill {
edge = barEdge ?? "top";
visible = true;
if (contextMenuWindow.screen) {
TrayMenuManager.registerMenu(contextMenuWindow.screen.name, contextMenuWindow);
}
}
function closeMenu() {
visible = false;
if (contextMenuWindow.screen) {
TrayMenuManager.unregisterMenu(contextMenuWindow.screen.name);
}
}
screen: null
@@ -180,6 +188,19 @@ BasePill {
bottom: true
}
Component.onDestruction: {
if (contextMenuWindow.screen) {
TrayMenuManager.unregisterMenu(contextMenuWindow.screen.name);
}
}
Connections {
target: PopoutManager
function onPopoutOpening() {
contextMenuWindow.closeMenu();
}
}
MouseArea {
anchors.fill: parent
z: 0
@@ -855,12 +855,20 @@ Item {
edge = barEdge ?? "top";
isVisible = true;
visible = true;
if (screen) {
TrayMenuManager.registerMenu(screen.name, contextMenuWindow);
}
}
function close() {
isVisible = false;
visible = false;
windowContextMenuLoader.active = false;
if (screen) {
TrayMenuManager.unregisterMenu(screen.name);
}
}
implicitWidth: 100
@@ -879,6 +887,19 @@ Item {
bottom: true
}
Component.onDestruction: {
if (screen) {
TrayMenuManager.unregisterMenu(screen.name);
}
}
Connections {
target: PopoutManager
function onPopoutOpening() {
contextMenuWindow.close();
}
}
MouseArea {
anchors.fill: parent
onClicked: contextMenuWindow.close()
+4 -46
View File
@@ -62,8 +62,6 @@ Item {
readonly property real currentVolume: usePlayerVolume ? activePlayer.volume : (AudioService.sink?.audio?.volume ?? 0)
property bool isSwitching: false
property string _lastArtUrl: ""
property string _bgArtSource: ""
// Derived "no players" state: always correct, no timers.
readonly property int _playerCount: allPlayers ? allPlayers.length : 0
@@ -88,28 +86,7 @@ Item {
isSwitching = true;
_switchHold = true;
_switchHoldTimer.restart();
if (activePlayer.trackArtUrl)
loadArtwork(activePlayer.trackArtUrl);
}
property string activeTrackArtFile: ""
function loadArtwork(url) {
if (!url)
return;
if (url.startsWith("http://") || url.startsWith("https://")) {
const filename = "/tmp/.dankshell/trackart_" + Date.now() + ".jpg";
activeTrackArtFile = filename;
cleanupProcess.command = ["sh", "-c", "mkdir -p /tmp/.dankshell && find /tmp/.dankshell -name 'trackart_*' ! -name '" + filename.split('/').pop() + "' -delete"];
cleanupProcess.running = true;
imageDownloader.command = ["curl", "-L", "-s", "--user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36", "-o", filename, url];
imageDownloader.targetFile = filename;
imageDownloader.running = true;
return;
}
_bgArtSource = url;
TrackArtService.loadArtwork(activePlayer.trackArtUrl);
}
function maybeFinishSwitch() {
@@ -138,10 +115,7 @@ Item {
maybeFinishSwitch();
}
function onTrackArtUrlChanged() {
if (activePlayer?.trackArtUrl) {
_lastArtUrl = activePlayer.trackArtUrl;
loadArtwork(activePlayer.trackArtUrl);
}
TrackArtService.loadArtwork(activePlayer.trackArtUrl);
}
}
@@ -213,22 +187,6 @@ Item {
}
}
Process {
id: imageDownloader
running: false
property string targetFile: ""
onExited: exitCode => {
if (exitCode === 0 && targetFile)
_bgArtSource = "file://" + targetFile;
}
}
Process {
id: cleanupProcess
running: false
}
property bool isSeeking: false
Timer {
@@ -241,14 +199,14 @@ Item {
Item {
id: bgContainer
anchors.fill: parent
visible: _bgArtSource !== ""
visible: TrackArtService._bgArtSource !== ""
Image {
id: bgImage
anchors.centerIn: parent
width: Math.max(parent.width, parent.height) * 1.1
height: width
source: _bgArtSource
source: TrackArtService._bgArtSource
fillMode: Image.PreserveAspectCrop
asynchronous: true
cache: true
+94 -10
View File
@@ -1,4 +1,6 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Effects
import qs.Common
import qs.Services
import qs.Widgets
@@ -10,7 +12,7 @@ DankOSD {
readonly property bool useVertical: isVerticalLayout
readonly property var player: MprisController.activePlayer
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(280, Screen.width - Theme.spacingM * 2)
osdHeight: useVertical ? (Theme.iconSize * 2) : (40 + Theme.spacingS * 2)
autoHideInterval: 3000
enableMouseInteraction: true
@@ -44,12 +46,17 @@ DankOSD {
target: player
function handleUpdate() {
if (!root.player?.trackTitle) return;
if (!root.player?.trackTitle)
return;
if (SettingsData.osdMediaPlaybackEnabled) {
TrackArtService.loadArtwork(player.trackArtUrl);
root.show();
}
}
function onTrackArtUrlChanged() {
TrackArtService.loadArtwork(player.trackArtUrl);
}
function onIsPlayingChanged() {
handleUpdate();
}
@@ -79,6 +86,67 @@ DankOSD {
onClicked: root.hide()
}
Item {
id: bgContainer
anchors.fill: parent
visible: TrackArtService._bgArtSource !== ""
Image {
id: bgImage
anchors.centerIn: parent
width: Math.max(parent.width, parent.height)
height: width
source: TrackArtService._bgArtSource
fillMode: Image.PreserveAspectCrop
asynchronous: true
cache: true
visible: false
}
Item {
id: blurredBg
anchors.fill: parent
visible: false
MultiEffect {
anchors.centerIn: parent
width: bgImage.width
height: bgImage.height
source: bgImage
blurEnabled: true
blurMax: 64
blur: 0.3
saturation: -0.2
brightness: -0.25
}
}
Rectangle {
id: bgMask
anchors.fill: parent
radius: Theme.cornerRadius
visible: false
layer.enabled: true
}
MultiEffect {
anchors.fill: parent
source: blurredBg
maskEnabled: true
maskSource: bgMask
maskThresholdMin: 0.5
maskSpreadAtMin: 1.0
opacity: 0.7
}
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: Theme.surface
opacity: 0.3
}
}
Rectangle {
width: Theme.iconSize
height: Theme.iconSize
@@ -107,17 +175,33 @@ DankOSD {
}
}
StyledText {
id: textItem
Column {
x: parent.gap * 2 + Theme.iconSize
width: parent.width - Theme.iconSize - parent.gap * 3
anchors.verticalCenter: parent.verticalCenter
text: player ? `${player.trackTitle || I18n.tr("Unknown Title")} ${player.trackArtist || I18n.tr("Unknown Artist")}` : ""
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
wrapMode: Text.Wrap
maximumLineCount: 3
spacing: 3
StyledText {
id: topText
width: parent.width
text: player ? `${player.trackTitle || I18n.tr("Unknown Title")}` : ""
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
wrapMode: Text.NoWrap
elide: Text.ElideRight
}
StyledText {
id: bottomText
width: parent.width
text: player ? ((player.trackArtist || I18n.tr("Unknown Artist")) + (player.trackAlbum ? ` ${player.trackAlbum}` : "")) : ""
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Light
color: Theme.surfaceText
wrapMode: Text.NoWrap
elide: Text.ElideRight
}
}
}
}
+7
View File
@@ -248,6 +248,13 @@ Singleton {
return "text";
}
function hashedPinnedEntry(entryHash) {
if (!entryHash) {
return false;
}
return pinnedEntries.some(pinnedEntry => pinnedEntry.hash === entryHash);
}
Connections {
target: DMSService
enabled: root.refCount > 0
+64
View File
@@ -0,0 +1,64 @@
pragma Singleton
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import Quickshell.Io
import Quickshell.Services.Mpris
Singleton {
id: root
property string _lastArtUrl: ""
property string _bgArtSource: ""
property string activeTrackArtFile: ""
function loadArtwork(url) {
if (!url || url == "") {
_bgArtSource = "";
_lastArtUrl = "";
return;
}
if (url == _lastArtUrl)
return;
_lastArtUrl = url;
if (url.startsWith("http://") || url.startsWith("https://")) {
const filename = "/tmp/.dankshell/trackart_" + Date.now() + ".jpg";
activeTrackArtFile = filename;
cleanupProcess.command = ["sh", "-c", "mkdir -p /tmp/.dankshell && find /tmp/.dankshell -name 'trackart_*' ! -name '" + filename.split('/').pop() + "' -delete"];
cleanupProcess.running = true;
imageDownloader.command = ["curl", "-L", "-s", "--user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36", "-o", filename, url];
imageDownloader.targetFile = filename;
imageDownloader.running = true;
return;
}
// otherwise
_bgArtSource = url;
}
property MprisPlayer activePlayer: MprisController.activePlayer
onActivePlayerChanged: {
loadArtwork(activePlayer.trackArtUrl);
}
Process {
id: imageDownloader
running: false
property string targetFile: ""
onExited: exitCode => {
if (exitCode === 0 && targetFile)
_bgArtSource = "file://" + targetFile;
}
}
Process {
id: cleanupProcess
running: false
}
}
+3 -1
View File
@@ -180,7 +180,7 @@ Item {
readonly property real screenWidth: screen ? screen.width : 0
readonly property real screenHeight: screen ? screen.height : 0
readonly property real dpr: screen ? CompositorService.getScreenScale(screen) : 1
readonly property real dpr: screen ? screen.devicePixelRatio : 1
readonly property real shadowBuffer: 5
readonly property real alignedWidth: Theme.px(popupWidth, dpr)
@@ -442,6 +442,8 @@ Item {
readonly property int blurMax: 64
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
layer.smooth: false
layer.textureSize: Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr))
layer.effect: MultiEffect {
id: shadowFx