Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion components/filedialog/FileDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ LazyLoader {

readonly property bool selectionValid: {
const file = folderContents.currentItem?.modelData;
return (file && !file.isDir && (filters.includes("*") || filters.includes(file.suffix))) ?? false;
const suffix = file?.suffix.toLowerCase();
return (file && !file.isDir && (filters.includes("*") || filters.some(f => f.toLowerCase() === suffix))) ?? false;
}

function accepted(path: string): void {
Expand Down
53 changes: 53 additions & 0 deletions components/images/ProfileImage.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
pragma ComponentBehavior: Bound

import QtQuick
import Caelestia

Item {
id: root

property string path
property int _reloadToken
readonly property string _format: {
_reloadToken;
return CUtils.imageFormat(path);
}

readonly property int status: loader.item?.status ?? Image.Null // qmllint disable missing-property

function reload(): void {
_reloadToken++;
loader.active = false;
loader.active = true;
}

Loader {
id: loader

anchors.fill: parent
sourceComponent: root._format === "gif" ? animatedComponent : cachingComponent
}

Component {
id: animatedComponent

AnimatedImage {
anchors.fill: parent
fillMode: AnimatedImage.PreserveAspectCrop
asynchronous: true
cache: false
playing: true
source: Qt.resolvedUrl(root.path)
}
}

Component {
id: cachingComponent

CachingImage {
anchors.fill: parent
cache: false
path: root.path
}
}
}
12 changes: 11 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,23 @@
nixpkgs.lib.genAttrs nixpkgs.lib.platforms.linux (
system: fn nixpkgs.legacyPackages.${system}
);
sourceRev =
if self ? rev
then self.rev
else if self ? dirtyRev
then self.dirtyRev
else if self ? shortRev
then self.shortRev
else if self ? lastModifiedDate
then self.lastModifiedDate
else "unknown";
in {
formatter = forAllSystems (pkgs: pkgs.alejandra);

packages = forAllSystems (pkgs: rec {
caelestia-shell = pkgs.callPackage ./nix {
inherit (inputs) m3shapes;
rev = self.rev or self.dirtyRev;
rev = sourceRev;
stdenv = pkgs.clangStdenv;
quickshell = inputs.quickshell.packages.${pkgs.stdenv.hostPlatform.system}.default.override {
withX11 = false;
Expand Down
2 changes: 1 addition & 1 deletion modules/dashboard/Wrapper.qml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Item {
readonly property FileDialog facePicker: FileDialog {
title: qsTr("Select a profile picture")
filterLabel: qsTr("Image files")
filters: Images.validImageExtensions
filters: Images.validProfileImageExtensions
onAccepted: path => {
if (CUtils.copyFile(Qt.resolvedUrl(path), Qt.resolvedUrl(`${Paths.home}/.face`)))
Quickshell.execDetached(["notify-send", "-a", "caelestia-shell", "-u", "low", "-h", `STRING:image-path:${path}`, "Profile picture changed", `Profile picture changed to ${Paths.shortenHome(path)}`]);
Expand Down
10 changes: 9 additions & 1 deletion modules/dashboard/dash/User.qml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Item {
}
}

CachingImage {
ProfileImage {
id: pfp

anchors.fill: parent
Expand Down Expand Up @@ -129,6 +129,14 @@ Item {
}
}
}

Connections {
function onAccepted(): void {
Qt.callLater(pfp.reload);
}

target: root.facePicker
}
}

MaterialShape {
Expand Down
2 changes: 1 addition & 1 deletion modules/lock/center/ProfilePic.qml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Item {
visible: pfp.status !== Image.Ready
}

CachingImage {
ProfileImage {
id: pfp

anchors.fill: shape
Expand Down
14 changes: 14 additions & 0 deletions plugin/src/Caelestia/cutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <qdir.h>
#include <qfileinfo.h>
#include <qfuturewatcher.h>
#include <qimagereader.h>
#include <qloggingcategory.h>
#include <qqmlengine.h>

Expand Down Expand Up @@ -138,6 +139,19 @@ QString CUtils::toLocalFile(const QUrl& url) {
return url.toLocalFile();
}

QString CUtils::imageFormat(const QUrl& url) {
if (!url.isLocalFile()) {
qCWarning(lcCUtils) << "imageFormat: url" << url << "is not a local file";
return QString();
}

return imageFormat(url.toLocalFile());
}

QString CUtils::imageFormat(const QString& path) {
return QString::fromLatin1(QImageReader::imageFormat(path));
}

qreal CUtils::clamp(qreal value, qreal min, qreal max) {
return qBound(min, value, max);
}
Expand Down
2 changes: 2 additions & 0 deletions plugin/src/Caelestia/cutils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class CUtils : public QObject {
Q_INVOKABLE static bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true);
Q_INVOKABLE static bool deleteFile(const QUrl& path);
Q_INVOKABLE static QString toLocalFile(const QUrl& url);
Q_INVOKABLE static QString imageFormat(const QUrl& url);
Q_INVOKABLE static QString imageFormat(const QString& path);

Q_INVOKABLE static qreal clamp(qreal value, qreal min, qreal max);

Expand Down
10 changes: 9 additions & 1 deletion utils/Images.qml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ import Quickshell
Singleton {
readonly property list<string> validImageTypes: ["jpeg", "png", "webp", "tiff", "svg"]
readonly property list<string> validImageExtensions: ["jpg", "jpeg", "png", "webp", "tif", "tiff", "svg"]
readonly property list<string> validProfileImageTypes: validImageTypes.concat(["gif"])
readonly property list<string> validProfileImageExtensions: validImageExtensions.concat(["gif"])

function isValidImageByName(name: string): bool {
return validImageExtensions.some(t => name.endsWith(`.${t}`));
const lowerName = name.toLowerCase();
return validImageExtensions.some(t => lowerName.endsWith(`.${t}`));
}

function isValidProfileImageByName(name: string): bool {
const lowerName = name.toLowerCase();
return validProfileImageExtensions.some(t => lowerName.endsWith(`.${t}`));
}
}