forked from Shinonome/dots-hyprland
wallpaper selector: quick places
This commit is contained in:
@@ -108,6 +108,10 @@ Rectangle {
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: dirEditButton.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer2
|
||||
}
|
||||
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Edit directory")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,15 @@ import Quickshell
|
||||
|
||||
Singleton {
|
||||
// XDG Dirs, with "file://"
|
||||
readonly property string home: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
|
||||
readonly property string config: StandardPaths.standardLocations(StandardPaths.ConfigLocation)[0]
|
||||
readonly property string state: StandardPaths.standardLocations(StandardPaths.StateLocation)[0]
|
||||
readonly property string cache: StandardPaths.standardLocations(StandardPaths.CacheLocation)[0]
|
||||
readonly property string genericCache: StandardPaths.standardLocations(StandardPaths.GenericCacheLocation)[0]
|
||||
readonly property string pictures: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
|
||||
readonly property string documents: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0]
|
||||
readonly property string downloads: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
|
||||
readonly property string pictures: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
|
||||
readonly property string videos: StandardPaths.standardLocations(StandardPaths.MoviesLocation)[0]
|
||||
|
||||
// Other dirs used by the shell, without "file://"
|
||||
property string assetsPath: Quickshell.shellPath("assets")
|
||||
|
||||
@@ -27,7 +27,10 @@ ListView {
|
||||
required property var modelData
|
||||
required property int index
|
||||
buttonText: index === 0 ? "/" : modelData
|
||||
toggled: index === directory.split("/").length - 1
|
||||
toggled: {
|
||||
if (directory.trim() === "/") return index === 0;
|
||||
return index === directory.split("/").length - 1
|
||||
}
|
||||
leftmost: index === 0
|
||||
rightmost: index === breadcrumbDirectory.split("/").length - 1
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property string path
|
||||
property bool isHovered: false
|
||||
|
||||
property alias color: background.color
|
||||
property alias radius: background.radius
|
||||
property alias padding: background.anchors.margins
|
||||
|
||||
signal activated()
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 8
|
||||
}
|
||||
radius: Appearance.rounding.normal
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: wallpaperItemColumnLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 6
|
||||
}
|
||||
spacing: 4
|
||||
|
||||
Item {
|
||||
id: wallpaperItemImageContainer
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: thumbnailImageLoader
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: thumbnailImageLoader
|
||||
anchors.fill: parent
|
||||
active: root.visible
|
||||
sourceComponent: Image {
|
||||
id: thumbnailImage
|
||||
source: {
|
||||
if (root.path.length == 0)
|
||||
return;
|
||||
const resolvedUrl = Qt.resolvedUrl(root.path);
|
||||
const md5Hash = Qt.md5(resolvedUrl);
|
||||
const cacheSize = "normal";
|
||||
const thumbnailPath = `${Directories.genericCache}/thumbnails/${cacheSize}/${md5Hash}.png`;
|
||||
return thumbnailPath;
|
||||
}
|
||||
asynchronous: true
|
||||
cache: false
|
||||
smooth: true
|
||||
mipmap: false
|
||||
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
clip: true
|
||||
sourceSize.width: wallpaperItemColumnLayout.width
|
||||
sourceSize.height: wallpaperItemColumnLayout.height - wallpaperItemColumnLayout.spacing - wallpaperItemName.height
|
||||
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: wallpaperItemImageContainer.width
|
||||
height: wallpaperItemImageContainer.height
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: wallpaperItemName
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 10
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
color: (index === grid.currentIndex || parent.isHovered) ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer0
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
text: FileUtils.fileNameForPath(root.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
for (let i = 0; i < grid.count; i++) {
|
||||
const item = grid.itemAtIndex(i);
|
||||
if (item && item !== parent) {
|
||||
item.isHovered = false;
|
||||
}
|
||||
}
|
||||
parent.isHovered = true;
|
||||
grid.currentIndex = index;
|
||||
}
|
||||
onExited: {
|
||||
parent.isHovered = false;
|
||||
}
|
||||
onClicked: root.activated()
|
||||
}
|
||||
}
|
||||
@@ -9,15 +9,11 @@ import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int columns: 4
|
||||
property real previewCellAspectRatio: 4 / 3
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
property var wallpapers: Wallpapers.wallpapers
|
||||
property string filterQuery: ""
|
||||
property bool useDarkMode: Appearance.m3colors.darkmode
|
||||
@@ -66,17 +62,8 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.fill: parent
|
||||
spacing: -Appearance.sizes.elevationMargin
|
||||
|
||||
Item { // The grid
|
||||
id: wallpaperGrid
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: wallpaperGridBackground.implicitWidth + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: wallpaperGridBackground.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: mainLayout.implicitHeight
|
||||
implicitWidth: mainLayout.implicitWidth
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: wallpaperGridBackground
|
||||
@@ -95,12 +82,87 @@ Item {
|
||||
|
||||
property int calculatedRows: Math.ceil(grid.count / grid.columns)
|
||||
|
||||
// implicitWidth: gridColumnLayout.implicitWidth
|
||||
// implicitHeight: gridColumnLayout.implicitHeight
|
||||
implicitWidth: gridColumnLayout.implicitWidth
|
||||
implicitHeight: gridColumnLayout.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: mainLayout
|
||||
anchors.fill: parent
|
||||
spacing: -4
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: 4
|
||||
implicitWidth: quickDirColumnLayout.implicitWidth
|
||||
implicitHeight: quickDirColumnLayout.implicitHeight
|
||||
color: Appearance.colors.colLayer1
|
||||
radius: wallpaperGridBackground.radius - Layout.margins
|
||||
|
||||
ColumnLayout {
|
||||
// The grid
|
||||
id: quickDirColumnLayout
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
Layout.margins: 12
|
||||
font {
|
||||
pixelSize: Appearance.font.pixelSize.normal
|
||||
weight: Font.Medium
|
||||
}
|
||||
text: Translation.tr("Pick a wallpaper")
|
||||
}
|
||||
ListView {
|
||||
// Quick dirs
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: 4
|
||||
implicitWidth: 140
|
||||
clip: true
|
||||
model: [
|
||||
{ icon: "home", name: "Home", path: Directories.home },
|
||||
{ icon: "folder", name: "Documents", path: Directories.documents },
|
||||
{ icon: "download", name: "Downloads", path: Directories.downloads },
|
||||
{ icon: "image", name: "Pictures", path: Directories.pictures },
|
||||
{ icon: "movie", name: "Videos", path: Directories.videos },
|
||||
{ icon: "", name: "---", path: "INTENTIONALLY_INVALID_DIR" },
|
||||
{ icon: "wallpaper", name: "Wallpapers", path: `${Directories.pictures}/Wallpapers` },
|
||||
{ icon: "favorite", name: "Homework", path: `${Directories.pictures}/homework` },
|
||||
]
|
||||
delegate: RippleButton {
|
||||
id: quickDirButton
|
||||
required property var modelData
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
onClicked: Wallpapers.setDirectory(quickDirButton.modelData.path)
|
||||
enabled: modelData.icon.length > 0
|
||||
toggled: Wallpapers.directory === FileUtils.trimFileProtocol(modelData.path)
|
||||
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||
|
||||
contentItem: RowLayout {
|
||||
MaterialSymbol {
|
||||
color: quickDirButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: quickDirButton.modelData.icon
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
color: quickDirButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1
|
||||
text: quickDirButton.modelData.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: gridColumnLayout
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
AddressBar {
|
||||
id: addressBar
|
||||
@@ -109,7 +171,7 @@ Item {
|
||||
Layout.fillHeight: false
|
||||
directory: Wallpapers.directory
|
||||
onNavigateToDirectory: path => {
|
||||
Wallpapers.setDirectory(path);
|
||||
Wallpapers.setDirectory(path.length == 0 ? "/" : path);
|
||||
}
|
||||
radius: wallpaperGridBackground.radius - Layout.margins
|
||||
}
|
||||
@@ -174,125 +236,18 @@ Item {
|
||||
}
|
||||
onModelChanged: currentIndex = 0
|
||||
|
||||
delegate: Item {
|
||||
id: wallpaperItem
|
||||
delegate: WallpaperDirectoryItem {
|
||||
required property var modelData
|
||||
required property int index
|
||||
visible: modelData.length > 0
|
||||
width: grid.cellWidth
|
||||
height: grid.cellHeight
|
||||
property bool isHovered: false
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 8
|
||||
}
|
||||
radius: Appearance.rounding.normal
|
||||
color: (index === grid.currentIndex || parent.isHovered) ? Appearance.colors.colPrimary : ColorUtils.transparentize(Appearance.colors.colPrimary)
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: wallpaperItemColumnLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 6
|
||||
}
|
||||
spacing: 4
|
||||
|
||||
Item {
|
||||
id: wallpaperItemImageContainer
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: thumbnailImageLoader
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: thumbnailImageLoader
|
||||
anchors.fill: parent
|
||||
active: wallpaperItem.visible
|
||||
sourceComponent: Image {
|
||||
id: thumbnailImage
|
||||
source: {
|
||||
if (wallpaperItem.modelData.length == 0)
|
||||
return;
|
||||
const resolvedUrl = Qt.resolvedUrl(wallpaperItem.modelData);
|
||||
const md5Hash = Qt.md5(resolvedUrl);
|
||||
const cacheSize = "normal";
|
||||
const thumbnailPath = `${Directories.genericCache}/thumbnails/${cacheSize}/${md5Hash}.png`;
|
||||
return thumbnailPath;
|
||||
}
|
||||
asynchronous: true
|
||||
cache: false
|
||||
smooth: true
|
||||
mipmap: false
|
||||
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
clip: true
|
||||
sourceSize.width: wallpaperItemColumnLayout.width
|
||||
sourceSize.height: wallpaperItemColumnLayout.height - wallpaperItemColumnLayout.spacing - wallpaperItemName.height
|
||||
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: wallpaperItemImageContainer.width
|
||||
height: wallpaperItemImageContainer.height
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: wallpaperItemName
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 10
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
color: (index === grid.currentIndex || parent.isHovered) ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer0
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
text: FileUtils.fileNameForPath(wallpaperItem.modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
for (let i = 0; i < grid.count; i++) {
|
||||
const item = grid.itemAtIndex(i);
|
||||
if (item && item !== parent) {
|
||||
item.isHovered = false;
|
||||
}
|
||||
}
|
||||
parent.isHovered = true;
|
||||
grid.currentIndex = index;
|
||||
}
|
||||
onExited: {
|
||||
parent.isHovered = false;
|
||||
}
|
||||
onClicked: {
|
||||
path: modelData
|
||||
color: (index === grid?.currentIndex || parent?.isHovered) ? Appearance.colors.colPrimary : ColorUtils.transparentize(Appearance.colors.colPrimary)
|
||||
onActivated: {
|
||||
Wallpapers.apply(path, root.useDarkMode);
|
||||
GlobalStates.wallpaperSelectorOpen = false;
|
||||
filterField.text = "";
|
||||
Wallpapers.apply(wallpaperItem.modelData, root.useDarkMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -340,7 +295,7 @@ Item {
|
||||
GlobalStates.wallpaperSelectorOpen = false;
|
||||
}
|
||||
contentItem: MaterialSymbol {
|
||||
text: "files"
|
||||
text: "open_in_new"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
}
|
||||
StyledToolTip {
|
||||
@@ -425,7 +380,6 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
|
||||
@@ -44,6 +44,7 @@ Singleton {
|
||||
property string nicePath: ""
|
||||
function setDirectoryIfValid(path) {
|
||||
validateDirProc.nicePath = FileUtils.trimFileProtocol(path).replace(/\/+$/, "")
|
||||
if (/^\/*$/.test(validateDirProc.nicePath)) validateDirProc.nicePath = "/";
|
||||
validateDirProc.exec(["test", "-d", nicePath])
|
||||
}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
@@ -61,7 +62,7 @@ Singleton {
|
||||
id: files
|
||||
folder: Qt.resolvedUrl(root.directory)
|
||||
nameFilters: root.extensions.map(ext => `*.${ext}`)
|
||||
showDirs: false
|
||||
showDirs: true
|
||||
showDotAndDotDot: false
|
||||
showOnlyReadable: true
|
||||
sortField: FolderListModel.Time
|
||||
|
||||
Reference in New Issue
Block a user