Skip to content

Commit a21a846

Browse files
committed
wallpaper: encode image URIs
fixes #1306
1 parent f5f21e7 commit a21a846

File tree

11 files changed

+132
-118
lines changed

11 files changed

+132
-118
lines changed

quickshell/Modals/FileBrowser/FileBrowserContent.qml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ FocusScope {
5252
signal fileSelected(string path)
5353
signal closeRequested
5454

55+
function encodeFileUrl(path) {
56+
if (!path)
57+
return "";
58+
return "file://" + path.split('/').map(s => encodeURIComponent(s)).join('/');
59+
}
60+
5561
function initialize() {
5662
loadSettings();
5763
currentPath = getLastPath();
@@ -188,7 +194,7 @@ FocusScope {
188194
function handleSaveFile(filePath) {
189195
var normalizedPath = filePath;
190196
if (!normalizedPath.startsWith("file://")) {
191-
normalizedPath = "file://" + filePath;
197+
normalizedPath = encodeFileUrl(filePath);
192198
}
193199

194200
var exists = false;
@@ -274,7 +280,7 @@ FocusScope {
274280
nameFilters: fileExtensions
275281
showFiles: true
276282
showDirs: true
277-
folder: currentPath ? "file://" + currentPath : "file://" + homeDir
283+
folder: encodeFileUrl(currentPath || homeDir)
278284
sortField: {
279285
switch (sortBy) {
280286
case "name":

quickshell/Modals/FileBrowser/FileBrowserGridDelegate.qml

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,89 +21,89 @@ StyledRect {
2121
signal itemSelected(int index, string path, string name, bool isDir)
2222

2323
function getFileExtension(fileName) {
24-
const parts = fileName.split('.')
24+
const parts = fileName.split('.');
2525
if (parts.length > 1) {
26-
return parts[parts.length - 1].toLowerCase()
26+
return parts[parts.length - 1].toLowerCase();
2727
}
28-
return ""
28+
return "";
2929
}
3030

3131
function determineFileType(fileName) {
32-
const ext = getFileExtension(fileName)
32+
const ext = getFileExtension(fileName);
3333

34-
const imageExts = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "ico"]
34+
const imageExts = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "ico"];
3535
if (imageExts.includes(ext)) {
36-
return "image"
36+
return "image";
3737
}
3838

39-
const videoExts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v"]
39+
const videoExts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v"];
4040
if (videoExts.includes(ext)) {
41-
return "video"
41+
return "video";
4242
}
4343

44-
const audioExts = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"]
44+
const audioExts = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"];
4545
if (audioExts.includes(ext)) {
46-
return "audio"
46+
return "audio";
4747
}
4848

49-
const codeExts = ["js", "ts", "jsx", "tsx", "py", "go", "rs", "c", "cpp", "h", "java", "kt", "swift", "rb", "php", "html", "css", "scss", "json", "xml", "yaml", "yml", "toml", "sh", "bash", "zsh", "fish", "qml", "vue", "svelte"]
49+
const codeExts = ["js", "ts", "jsx", "tsx", "py", "go", "rs", "c", "cpp", "h", "java", "kt", "swift", "rb", "php", "html", "css", "scss", "json", "xml", "yaml", "yml", "toml", "sh", "bash", "zsh", "fish", "qml", "vue", "svelte"];
5050
if (codeExts.includes(ext)) {
51-
return "code"
51+
return "code";
5252
}
5353

54-
const docExts = ["txt", "md", "pdf", "doc", "docx", "odt", "rtf"]
54+
const docExts = ["txt", "md", "pdf", "doc", "docx", "odt", "rtf"];
5555
if (docExts.includes(ext)) {
56-
return "document"
56+
return "document";
5757
}
5858

59-
const archiveExts = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar"]
59+
const archiveExts = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar"];
6060
if (archiveExts.includes(ext)) {
61-
return "archive"
61+
return "archive";
6262
}
6363

6464
if (!ext || fileName.indexOf('.') === -1) {
65-
return "binary"
65+
return "binary";
6666
}
6767

68-
return "file"
68+
return "file";
6969
}
7070

7171
function isImageFile(fileName) {
7272
if (!fileName) {
73-
return false
73+
return false;
7474
}
75-
return determineFileType(fileName) === "image"
75+
return determineFileType(fileName) === "image";
7676
}
7777

7878
function getIconForFile(fileName) {
79-
const lowerName = fileName.toLowerCase()
79+
const lowerName = fileName.toLowerCase();
8080
if (lowerName.startsWith("dockerfile")) {
81-
return "docker"
81+
return "docker";
8282
}
83-
const ext = fileName.split('.').pop()
84-
return ext || ""
83+
const ext = fileName.split('.').pop();
84+
return ext || "";
8585
}
8686

8787
width: weMode ? 245 : iconSizes[iconSizeIndex] + 16
8888
height: weMode ? 205 : iconSizes[iconSizeIndex] + 48
8989
radius: Theme.cornerRadius
9090
color: {
9191
if (keyboardNavigationActive && delegateRoot.index === selectedIndex)
92-
return Theme.surfacePressed
92+
return Theme.surfacePressed;
9393

94-
return mouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : "transparent"
94+
return mouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : "transparent";
9595
}
9696
border.color: keyboardNavigationActive && delegateRoot.index === selectedIndex ? Theme.primary : "transparent"
9797
border.width: (keyboardNavigationActive && delegateRoot.index === selectedIndex) ? 2 : 0
9898

9999
Component.onCompleted: {
100100
if (keyboardNavigationActive && delegateRoot.index === selectedIndex)
101-
itemSelected(delegateRoot.index, delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
101+
itemSelected(delegateRoot.index, delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir);
102102
}
103103

104104
onSelectedIndexChanged: {
105105
if (keyboardNavigationActive && selectedIndex === delegateRoot.index)
106-
itemSelected(delegateRoot.index, delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
106+
itemSelected(delegateRoot.index, delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir);
107107
}
108108

109109
Column {
@@ -121,19 +121,17 @@ StyledRect {
121121
anchors.margins: 2
122122
property var weExtensions: [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".tga"]
123123
property int weExtIndex: 0
124-
source: {
125-
if (weMode && delegateRoot.fileIsDir) {
126-
return "file://" + delegateRoot.filePath + "/preview" + weExtensions[weExtIndex]
127-
}
128-
return (!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) ? ("file://" + delegateRoot.filePath) : ""
124+
imagePath: {
125+
if (weMode && delegateRoot.fileIsDir)
126+
return delegateRoot.filePath + "/preview" + weExtensions[weExtIndex];
127+
return (!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) ? delegateRoot.filePath : "";
129128
}
130129
onStatusChanged: {
131130
if (weMode && delegateRoot.fileIsDir && status === Image.Error) {
132131
if (weExtIndex < weExtensions.length - 1) {
133-
weExtIndex++
134-
source = "file://" + delegateRoot.filePath + "/preview" + weExtensions[weExtIndex]
132+
weExtIndex++;
135133
} else {
136-
source = ""
134+
imagePath = "";
137135
}
138136
}
139137
}
@@ -198,7 +196,7 @@ StyledRect {
198196
hoverEnabled: true
199197
cursorShape: Qt.PointingHandCursor
200198
onClicked: {
201-
itemClicked(delegateRoot.index, delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
199+
itemClicked(delegateRoot.index, delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir);
202200
}
203201
}
204202
}

quickshell/Modals/FileBrowser/FileBrowserListDelegate.qml

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,97 +20,97 @@ StyledRect {
2020
signal itemSelected(int index, string path, string name, bool isDir)
2121

2222
function getFileExtension(fileName) {
23-
const parts = fileName.split('.')
23+
const parts = fileName.split('.');
2424
if (parts.length > 1) {
25-
return parts[parts.length - 1].toLowerCase()
25+
return parts[parts.length - 1].toLowerCase();
2626
}
27-
return ""
27+
return "";
2828
}
2929

3030
function determineFileType(fileName) {
31-
const ext = getFileExtension(fileName)
31+
const ext = getFileExtension(fileName);
3232

33-
const imageExts = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "ico"]
33+
const imageExts = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "ico"];
3434
if (imageExts.includes(ext)) {
35-
return "image"
35+
return "image";
3636
}
3737

38-
const videoExts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v"]
38+
const videoExts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v"];
3939
if (videoExts.includes(ext)) {
40-
return "video"
40+
return "video";
4141
}
4242

43-
const audioExts = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"]
43+
const audioExts = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"];
4444
if (audioExts.includes(ext)) {
45-
return "audio"
45+
return "audio";
4646
}
4747

48-
const codeExts = ["js", "ts", "jsx", "tsx", "py", "go", "rs", "c", "cpp", "h", "java", "kt", "swift", "rb", "php", "html", "css", "scss", "json", "xml", "yaml", "yml", "toml", "sh", "bash", "zsh", "fish", "qml", "vue", "svelte"]
48+
const codeExts = ["js", "ts", "jsx", "tsx", "py", "go", "rs", "c", "cpp", "h", "java", "kt", "swift", "rb", "php", "html", "css", "scss", "json", "xml", "yaml", "yml", "toml", "sh", "bash", "zsh", "fish", "qml", "vue", "svelte"];
4949
if (codeExts.includes(ext)) {
50-
return "code"
50+
return "code";
5151
}
5252

53-
const docExts = ["txt", "md", "pdf", "doc", "docx", "odt", "rtf"]
53+
const docExts = ["txt", "md", "pdf", "doc", "docx", "odt", "rtf"];
5454
if (docExts.includes(ext)) {
55-
return "document"
55+
return "document";
5656
}
5757

58-
const archiveExts = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar"]
58+
const archiveExts = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar"];
5959
if (archiveExts.includes(ext)) {
60-
return "archive"
60+
return "archive";
6161
}
6262

6363
if (!ext || fileName.indexOf('.') === -1) {
64-
return "binary"
64+
return "binary";
6565
}
6666

67-
return "file"
67+
return "file";
6868
}
6969

7070
function isImageFile(fileName) {
7171
if (!fileName) {
72-
return false
72+
return false;
7373
}
74-
return determineFileType(fileName) === "image"
74+
return determineFileType(fileName) === "image";
7575
}
7676

7777
function getIconForFile(fileName) {
78-
const lowerName = fileName.toLowerCase()
78+
const lowerName = fileName.toLowerCase();
7979
if (lowerName.startsWith("dockerfile")) {
80-
return "docker"
80+
return "docker";
8181
}
82-
const ext = fileName.split('.').pop()
83-
return ext || ""
82+
const ext = fileName.split('.').pop();
83+
return ext || "";
8484
}
8585

8686
function formatFileSize(size) {
8787
if (size < 1024)
88-
return size + " B"
88+
return size + " B";
8989
if (size < 1024 * 1024)
90-
return (size / 1024).toFixed(1) + " KB"
90+
return (size / 1024).toFixed(1) + " KB";
9191
if (size < 1024 * 1024 * 1024)
92-
return (size / (1024 * 1024)).toFixed(1) + " MB"
93-
return (size / (1024 * 1024 * 1024)).toFixed(1) + " GB"
92+
return (size / (1024 * 1024)).toFixed(1) + " MB";
93+
return (size / (1024 * 1024 * 1024)).toFixed(1) + " GB";
9494
}
9595

9696
height: 44
9797
radius: Theme.cornerRadius
9898
color: {
9999
if (keyboardNavigationActive && listDelegateRoot.index === selectedIndex)
100-
return Theme.surfacePressed
101-
return listMouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : "transparent"
100+
return Theme.surfacePressed;
101+
return listMouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) : "transparent";
102102
}
103103
border.color: keyboardNavigationActive && listDelegateRoot.index === selectedIndex ? Theme.primary : "transparent"
104104
border.width: (keyboardNavigationActive && listDelegateRoot.index === selectedIndex) ? 2 : 0
105105

106106
Component.onCompleted: {
107107
if (keyboardNavigationActive && listDelegateRoot.index === selectedIndex)
108-
itemSelected(listDelegateRoot.index, listDelegateRoot.filePath, listDelegateRoot.fileName, listDelegateRoot.fileIsDir)
108+
itemSelected(listDelegateRoot.index, listDelegateRoot.filePath, listDelegateRoot.fileName, listDelegateRoot.fileIsDir);
109109
}
110110

111111
onSelectedIndexChanged: {
112112
if (keyboardNavigationActive && selectedIndex === listDelegateRoot.index)
113-
itemSelected(listDelegateRoot.index, listDelegateRoot.filePath, listDelegateRoot.fileName, listDelegateRoot.fileIsDir)
113+
itemSelected(listDelegateRoot.index, listDelegateRoot.filePath, listDelegateRoot.fileName, listDelegateRoot.fileIsDir);
114114
}
115115

116116
Row {
@@ -127,7 +127,7 @@ StyledRect {
127127
CachingImage {
128128
id: listPreviewImage
129129
anchors.fill: parent
130-
source: (!listDelegateRoot.fileIsDir && isImageFile(listDelegateRoot.fileName)) ? ("file://" + listDelegateRoot.filePath) : ""
130+
imagePath: (!listDelegateRoot.fileIsDir && isImageFile(listDelegateRoot.fileName)) ? listDelegateRoot.filePath : ""
131131
fillMode: Image.PreserveAspectCrop
132132
maxCacheSize: 32
133133
visible: false
@@ -203,7 +203,7 @@ StyledRect {
203203
hoverEnabled: true
204204
cursorShape: Qt.PointingHandCursor
205205
onClicked: {
206-
itemClicked(listDelegateRoot.index, listDelegateRoot.filePath, listDelegateRoot.fileName, listDelegateRoot.fileIsDir)
206+
itemClicked(listDelegateRoot.index, listDelegateRoot.filePath, listDelegateRoot.fileName, listDelegateRoot.fileIsDir);
207207
}
208208
}
209209
}

quickshell/Modules/BlurredWallpaperBackground.qml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ Variants {
4040
id: root
4141
anchors.fill: parent
4242

43+
function encodeFileUrl(path) {
44+
if (!path)
45+
return "";
46+
return "file://" + path.split('/').map(s => encodeURIComponent(s)).join('/');
47+
}
48+
4349
property string source: SessionData.getMonitorWallpaper(modelData.name) || ""
4450
property bool isColorSource: source.startsWith("#")
4551

@@ -83,7 +89,7 @@ Variants {
8389
isInitialized = true;
8490
return;
8591
}
86-
const formattedSource = source.startsWith("file://") ? source : "file://" + source;
92+
const formattedSource = source.startsWith("file://") ? source : encodeFileUrl(source);
8793
setWallpaperImmediate(formattedSource);
8894
isInitialized = true;
8995
}
@@ -100,7 +106,7 @@ Variants {
100106
return;
101107
}
102108

103-
const formattedSource = source.startsWith("file://") ? source : "file://" + source;
109+
const formattedSource = source.startsWith("file://") ? source : encodeFileUrl(source);
104110

105111
if (!isInitialized || !currentWallpaper.source) {
106112
setWallpaperImmediate(formattedSource);

quickshell/Modules/DankDash/WallpaperTab.qml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import Qt.labs.folderlistmodel
22
import QtCore
33
import QtQuick
4-
import QtQuick.Controls
54
import QtQuick.Effects
65
import qs.Common
76
import qs.Modals.FileBrowser
@@ -311,7 +310,7 @@ Item {
311310
showFiles: true
312311
showDirs: false
313312
sortField: FolderListModel.Name
314-
folder: wallpaperDir ? "file://" + wallpaperDir : ""
313+
folder: wallpaperDir ? "file://" + wallpaperDir.split('/').map(s => encodeURIComponent(s)).join('/') : ""
315314
}
316315

317316
FileBrowserSurfaceModal {
@@ -401,7 +400,9 @@ Item {
401400
currentIndex = clampedIndex;
402401
positionViewAtIndex(clampedIndex, GridView.Contain);
403402
}
404-
Qt.callLater(() => { enableAnimation = true; });
403+
Qt.callLater(() => {
404+
enableAnimation = true;
405+
});
405406
}
406407

407408
Connections {

0 commit comments

Comments
 (0)