415 lines
14 KiB
QML
415 lines
14 KiB
QML
import "src"
|
|
import "src/Utils.js" as Utils
|
|
import qs.Commons
|
|
import qs.Widgets
|
|
import qs.Services.UI
|
|
import QtQuick
|
|
import QtMultimedia
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import Quickshell.Wayland
|
|
import Qt5Compat.GraphicalEffects
|
|
import Qt.labs.folderlistmodel
|
|
|
|
PanelWindow {
|
|
id: root
|
|
|
|
property int animationCardsDuration: pluginApi?.pluginSettings?.animation_cards_duration ?? pluginApi?.manifest?.metadata?.defaultSettings?.animation_cards_duration
|
|
property int animationWindowDuration: pluginApi?.pluginSettings?.animation_window_duration ?? pluginApi?.manifest?.metadata?.defaultSettings?.animation_window_duration
|
|
property bool animateWindow: pluginApi?.pluginSettings?.animate_window ?? pluginApi?.manifest?.metadata?.defaultSettings?.animate_window ?? true
|
|
property color backgroundColor: pluginApi?.pluginSettings?.background_color ?? pluginApi?.manifest?.metadata?.defaultSettings?.background_color
|
|
property real backgroundOpacity: pluginApi?.pluginSettings?.background_opacity ?? pluginApi?.manifest?.metadata?.defaultSettings?.background_opacity
|
|
property int cardHeight: pluginApi?.pluginSettings?.card_height ?? pluginApi?.manifest?.metadata?.defaultSettings?.card_height
|
|
property int cardRadius: Style.radiusM
|
|
property int cardSpacing: pluginApi?.pluginSettings?.card_spacing ?? pluginApi?.manifest?.metadata?.defaultSettings?.card_spacing
|
|
property int cardStripWidth: pluginApi?.pluginSettings?.card_strip_width ?? pluginApi?.manifest?.metadata?.defaultSettings?.card_strip_width
|
|
property int cardsShown: pluginApi?.pluginSettings?.cards_shown ?? pluginApi?.manifest?.metadata?.defaultSettings?.cards_shown
|
|
property real centerWidthRatio: pluginApi?.pluginSettings?.center_width_ratio ?? pluginApi?.manifest?.metadata?.defaultSettings?.center_width_ratio
|
|
property int currentIndex: cardDeck.currentIndex
|
|
property string currentCardColor: filteredFiles[currentIndex]?.filterColor ?? ""
|
|
property var imageFilter: pluginApi?.manifest?.metadata?.defaultSettings?.image_filter
|
|
property var videoFilter: pluginApi?.manifest?.metadata?.defaultSettings?.video_filter
|
|
property var filteredFiles: []
|
|
property var availableColors: []
|
|
property bool hideHelp: pluginApi?.pluginSettings?.hide_help ?? pluginApi?.manifest?.metadata?.defaultSettings?.hide_help ?? true
|
|
property bool hideTopBar: pluginApi?.pluginSettings?.hide_top_bar ?? pluginApi?.manifest?.metadata?.defaultSettings?.hide_top_bar ?? false
|
|
property bool livePreview: pluginApi?.pluginSettings?.live_preview ?? pluginApi?.manifest?.metadata?.defaultSettings?.live_preview
|
|
property bool loading: thumbnailService.loading
|
|
property int pendingProcesses: 0
|
|
property var pluginApi: null
|
|
property string selectedFilter: pluginApi?.pluginSettings?.selected_filter || pluginApi?.manifest?.metadata?.defaultSettings?.selected_filter
|
|
property string selectedColorFilter: ""
|
|
property real shearFactor: pluginApi?.pluginSettings?.shear_factor ?? pluginApi?.manifest?.metadata?.defaultSettings?.shear_factor
|
|
property int thumbnailRevision: thumbnailService.thumbnailRevision
|
|
property int topBarHeight: pluginApi?.pluginSettings?.top_bar_height ?? pluginApi?.manifest?.metadata?.defaultSettings?.top_bar_height
|
|
property int topBarRadius: Style.radiusM
|
|
|
|
signal quitRequested
|
|
|
|
function applyCurrentCard() {
|
|
var f = filteredFiles[cardDeck.currentIndex];
|
|
if (!f)
|
|
return;
|
|
|
|
applicant.command = ["bash", "-c", Utils.wallpaperCommand(f)];
|
|
applicant.running = true;
|
|
|
|
var wallpaperPath = f.isVideo ? f.thumbnail : f.filePath;
|
|
WallpaperService.changeWallpaper(wallpaperPath);
|
|
}
|
|
|
|
function applyFilterToFiles() {
|
|
var currentFile = filteredFiles[cardDeck.currentIndex]?.filePath ?? "";
|
|
|
|
var all = thumbnailService.files ?? [];
|
|
var result = all;
|
|
|
|
if (selectedFilter === "images")
|
|
result = result.filter(f => !f.isVideo);
|
|
else if (selectedFilter === "videos")
|
|
result = result.filter(f => f.isVideo);
|
|
|
|
var colors = {};
|
|
for (var c = 0; c < result.length; c++)
|
|
colors[result[c].filterColor] = true;
|
|
availableColors = Object.keys(colors);
|
|
|
|
if (selectedColorFilter !== "")
|
|
result = result.filter(f => f.filterColor === selectedColorFilter);
|
|
|
|
filteredFiles = result;
|
|
|
|
var newIdx = 0;
|
|
if (currentFile !== "") {
|
|
for (var i = 0; i < result.length; i++) {
|
|
if (result[i].filePath === currentFile) {
|
|
newIdx = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
cardDeck.jumpTo(newIdx);
|
|
}
|
|
|
|
function close() {
|
|
if (exitAnimation.running)
|
|
return;
|
|
if (root.animateWindow) {
|
|
exitAnimation.start();
|
|
} else {
|
|
root.quitRequested();
|
|
}
|
|
}
|
|
|
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
|
WlrLayershell.layer: WlrLayer.Overlay
|
|
aboveWindows: true
|
|
color: "transparent"
|
|
exclusionMode: "Ignore"
|
|
exclusiveZone: 0
|
|
implicitHeight: screen.height
|
|
implicitWidth: screen.width
|
|
screen: pluginApi.panelOpenScreen
|
|
|
|
onCurrentIndexChanged: {
|
|
if (root.livePreview)
|
|
applyCurrentCard();
|
|
}
|
|
|
|
onSelectedFilterChanged: applyFilterToFiles()
|
|
onSelectedColorFilterChanged: applyFilterToFiles()
|
|
onVisibleChanged: {
|
|
if (visible) {
|
|
if (root.animateWindow) {
|
|
enterAnimation.start();
|
|
} else {
|
|
content.opacity = 1;
|
|
content.scale = 1;
|
|
background.opacity = root.backgroundOpacity;
|
|
}
|
|
}
|
|
}
|
|
|
|
Process {
|
|
id: applicant
|
|
|
|
command: []
|
|
running: false
|
|
}
|
|
|
|
Connections {
|
|
function onFilesChanged() {
|
|
root.applyFilterToFiles();
|
|
}
|
|
|
|
target: thumbnailService
|
|
}
|
|
|
|
ThumbnailService {
|
|
id: thumbnailService
|
|
|
|
cacheDir: root.pluginApi?.Settings.cacheDir + "/wallcards"
|
|
imageFilter: root.imageFilter
|
|
videoFilter: root.videoFilter
|
|
wallpaperDir: root.pluginApi?.Settings.data.wallpaper.directory
|
|
}
|
|
|
|
ParallelAnimation {
|
|
id: enterAnimation
|
|
|
|
NumberAnimation {
|
|
duration: root.animationWindowDuration
|
|
easing.type: Easing.OutCubic
|
|
from: 0.85
|
|
property: "scale"
|
|
target: content
|
|
to: 1.0
|
|
}
|
|
NumberAnimation {
|
|
duration: root.animationWindowDuration
|
|
easing.type: Easing.OutCubic
|
|
from: 0
|
|
property: "opacity"
|
|
target: content
|
|
to: 1
|
|
}
|
|
NumberAnimation {
|
|
duration: root.animationWindowDuration
|
|
easing.type: Easing.OutCubic
|
|
from: 0
|
|
property: "opacity"
|
|
target: background
|
|
to: root.backgroundOpacity
|
|
}
|
|
}
|
|
|
|
ParallelAnimation {
|
|
id: exitAnimation
|
|
|
|
property int duration: root.animationWindowDuration * 0.4
|
|
|
|
onFinished: root.quitRequested()
|
|
|
|
NumberAnimation {
|
|
duration: exitAnimation.duration
|
|
easing.type: Easing.InCubic
|
|
property: "opacity"
|
|
target: content
|
|
to: 0
|
|
}
|
|
NumberAnimation {
|
|
duration: exitAnimation.duration
|
|
easing.type: Easing.InCubic
|
|
property: "scale"
|
|
target: content
|
|
to: 0.85
|
|
}
|
|
NumberAnimation {
|
|
duration: exitAnimation.duration
|
|
easing.type: Easing.InCubic
|
|
property: "opacity"
|
|
target: background
|
|
to: 0
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: background
|
|
|
|
anchors.fill: parent
|
|
color: root.backgroundColor
|
|
opacity: 0
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
|
|
onClicked: root.close()
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: content
|
|
|
|
anchors.fill: parent
|
|
focus: true
|
|
opacity: 0
|
|
scale: 0.85
|
|
|
|
Keys.onPressed: event => {
|
|
if (event.modifiers & Qt.ShiftModifier) {
|
|
if (event.key === Qt.Key_H) {
|
|
root.cardHeight = Math.max(root.cardHeight - 10, parent.height * 0.10);
|
|
event.accepted = true;
|
|
return;
|
|
} else if (event.key === Qt.Key_L) {
|
|
root.cardHeight = Math.min(root.cardHeight + 10, parent.height * 0.75);
|
|
event.accepted = true;
|
|
return;
|
|
} else if (event.key === Qt.Key_J) {
|
|
cardDeck.centerWidthRatio = Math.max(cardDeck.centerWidthRatio - 0.01, 0.2);
|
|
event.accepted = true;
|
|
return;
|
|
} else if (event.key === Qt.Key_K) {
|
|
cardDeck.centerWidthRatio = Math.min(cardDeck.centerWidthRatio + 0.01, 0.6);
|
|
event.accepted = true;
|
|
return;
|
|
} else if (event.key === Qt.Key_N) {
|
|
root.cardsShown = Math.max(root.cardsShown - 2, 5);
|
|
event.accepted = true;
|
|
return;
|
|
} else if (event.key === Qt.Key_P) {
|
|
root.cardsShown = Math.min(root.cardsShown + 2, 15);
|
|
event.accepted = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (event.modifiers & Qt.ControlModifier) {
|
|
if (event.key === Qt.Key_K) {
|
|
root.cardSpacing = Math.max(root.cardSpacing + 2, 0);
|
|
event.accepted = true;
|
|
return;
|
|
} else if (event.key === Qt.Key_J) {
|
|
root.cardSpacing = Math.max(root.cardSpacing - 2, 0);
|
|
event.accepted = true;
|
|
return;
|
|
} else if (event.key === Qt.Key_L) {
|
|
root.cardStripWidth = Math.min(root.cardStripWidth + 5, 300);
|
|
event.accepted = true;
|
|
return;
|
|
} else if (event.key === Qt.Key_H) {
|
|
root.cardStripWidth = Math.max(root.cardStripWidth - 5, 20);
|
|
event.accepted = true;
|
|
return;
|
|
} else if (event.key === Qt.Key_S) {
|
|
root.pluginApi.pluginSettings.card_height = root.cardHeight;
|
|
root.pluginApi.pluginSettings.center_width_ratio = cardDeck.centerWidthRatio;
|
|
root.pluginApi.pluginSettings.cards_shown = root.cardsShown;
|
|
root.pluginApi.pluginSettings.card_spacing = root.cardSpacing;
|
|
root.pluginApi.pluginSettings.card_strip_width = root.cardStripWidth;
|
|
root.pluginApi.pluginSettings.hide_top_bar = root.hideTopBar;
|
|
root.pluginApi.pluginSettings.live_preview = root.livePreview;
|
|
root.pluginApi.pluginSettings.selected_filter = root.selectedFilter;
|
|
root.pluginApi.pluginSettings.hide_help = root.hideHelp;
|
|
root.pluginApi.pluginSettings.animate_window = root.animateWindow;
|
|
root.pluginApi.saveSettings();
|
|
event.accepted = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
function handleKey(event, bindings) {
|
|
const action = bindings[event.key];
|
|
if (action) {
|
|
action();
|
|
event.accepted = true;
|
|
}
|
|
}
|
|
|
|
const bindings = {
|
|
[Qt.Key_Question]: () => sideBar.expanded = !sideBar.expanded,
|
|
[Qt.Key_Q]: () => root.close(),
|
|
[Qt.Key_Return]: () => {
|
|
root.applyCurrentCard();
|
|
root.close();
|
|
},
|
|
[Qt.Key_Space]: () => root.applyCurrentCard(),
|
|
[Qt.Key_Escape]: () => root.close(),
|
|
[Qt.Key_A]: () => root.selectedFilter = "all",
|
|
[Qt.Key_I]: () => root.selectedFilter = "images",
|
|
[Qt.Key_V]: () => root.selectedFilter = "videos",
|
|
[Qt.Key_T]: () => root.hideTopBar = !root.hideTopBar,
|
|
[Qt.Key_F]: () => {
|
|
if (root.selectedColorFilter !== "")
|
|
root.selectedColorFilter = "";
|
|
else
|
|
root.selectedColorFilter = root.currentCardColor;
|
|
},
|
|
[Qt.Key_R]: () => {
|
|
cardDeck.randomJump();
|
|
topBar.flashShuffle();
|
|
},
|
|
[Qt.Key_P]: () => root.livePreview = !root.livePreview,
|
|
[Qt.Key_K]: () => cardDeck.navigateTo(cardDeck.currentIndex + 1),
|
|
[Qt.Key_J]: () => cardDeck.navigateTo(cardDeck.currentIndex - 1),
|
|
[Qt.Key_L]: () => cardDeck.navigateTo(cardDeck.currentIndex + root.cardsShown - 2),
|
|
[Qt.Key_H]: () => cardDeck.navigateTo(cardDeck.currentIndex - root.cardsShown + 2)
|
|
};
|
|
|
|
const action = bindings[event.key];
|
|
if (action) {
|
|
action();
|
|
event.accepted = true;
|
|
}
|
|
}
|
|
|
|
LoadingBar {
|
|
id: loadingBar
|
|
|
|
anchors.centerIn: parent
|
|
pending: thumbnailService.pendingProcesses
|
|
total: thumbnailService.fileCount
|
|
}
|
|
|
|
TopBar {
|
|
id: topBar
|
|
|
|
anchors.bottom: cardDeck.top
|
|
anchors.bottomMargin: root.topBarHeight / 3
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.horizontalCenterOffset: shearFactor * -root.topBarHeight * 4 / 3
|
|
animationDuration: root.animationCardsDuration
|
|
availableColors: root.availableColors
|
|
colorOrder: thumbnailService.colorOrder
|
|
colorOrderColors: thumbnailService.colorOrderColors
|
|
currentCardColor: root.currentCardColor
|
|
currentIndex: cardDeck.currentIndex
|
|
filteredCount: root.filteredFiles.length
|
|
height: root.topBarHeight
|
|
livePreview: root.livePreview
|
|
pluginApi: root.pluginApi
|
|
radius: root.topBarRadius
|
|
selectedColorFilter: root.selectedColorFilter
|
|
selectedFilter: root.selectedFilter
|
|
shearFactor: root.shearFactor
|
|
visible: !loadingBar.visible && !root.hideTopBar
|
|
width: cardDeck.width
|
|
|
|
onColorFilterSelected: key => root.selectedColorFilter = key
|
|
onFilterSelected: key => root.selectedFilter = key
|
|
onLivePreviewToggled: root.livePreview = !root.livePreview
|
|
onShuffleRequested: cardDeck.randomJump()
|
|
}
|
|
|
|
SideBar {
|
|
id: sideBar
|
|
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: Style.marginL
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
hideHelp: root.hideHelp
|
|
visible: !loadingBar.visible
|
|
}
|
|
|
|
CardDeck {
|
|
id: cardDeck
|
|
|
|
anchors.centerIn: parent
|
|
animationDuration: root.animationCardsDuration
|
|
cardRadius: root.cardRadius
|
|
cardSpacing: root.cardSpacing
|
|
cardStripWidth: root.cardStripWidth
|
|
cardsShown: root.cardsShown
|
|
centerWidthRatio: root.centerWidthRatio
|
|
filteredCount: root.filteredFiles.length
|
|
filteredModel: root.filteredFiles
|
|
height: root.cardHeight
|
|
shearFactor: root.shearFactor
|
|
visible: !loadingBar.visible
|
|
|
|
onApplyRequested: root.applyCurrentCard()
|
|
}
|
|
}
|
|
}
|